You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

402 lines
8.0 KiB

  1. #!/bin/sh /etc/rc.common
  2. # Copyright (C) 2006-2008 OpenWrt.org
  3. # Copyright (C) 2019 Jeffery To
  4. START=90
  5. USE_PROCD=1
  6. PID_FILE="/var/run/stunnel/stunnel.pid"
  7. CONF_FILE="/var/etc/stunnel.conf"
  8. BIN="/usr/bin/stunnel"
  9. CONF_FILE_CREATED=
  10. HAVE_ALT_CONF_FILE=
  11. SERVICE_SECTION_FOUND=
  12. validate_globals_section() {
  13. uci_load_validate stunnel globals "$1" "$2" \
  14. 'alt_config_file:file' \
  15. \
  16. 'compression:or("deflate","zlib")' \
  17. 'EGD:string' \
  18. 'engine:string' \
  19. 'engineCtrl:string' \
  20. 'engineDefault:list(or("ALL","CIPHERS","DH","DIGESTS","DSA","ECDH","ECDSA","PKEY","PKEY_ASN1","PKEY_CRYPTO","RAND","RSA"))' \
  21. 'log:or("append","overwrite")' \
  22. 'output:string' \
  23. 'RNDbytes:uinteger' \
  24. 'RNDfile:string' \
  25. 'RNDoverwrite:bool' \
  26. 'setgid:or(string,uinteger)' \
  27. 'setuid:or(string,uinteger)' \
  28. 'syslog:bool' \
  29. ;
  30. }
  31. validate_service_section() {
  32. uci_load_validate stunnel service "$1" "$2" \
  33. 'enabled:bool:1' \
  34. \
  35. 'setgid:or(string,uinteger)' \
  36. 'setuid:or(string,uinteger)' \
  37. ;
  38. }
  39. validate_service_options() {
  40. uci_load_validate stunnel "$1" "$2" "$3" \
  41. 'accept_host:host' \
  42. 'accept_port:port' \
  43. 'CAfile:string' \
  44. 'CApath:string' \
  45. 'cert:string' \
  46. 'checkEmail:list(string)' \
  47. 'checkHost:list(host)' \
  48. 'checkIP:list(ipaddr)' \
  49. 'ciphers:list(string)' \
  50. 'client:bool' \
  51. 'config:list(string)' \
  52. 'connect:list(string)' \
  53. 'CRLfile:string' \
  54. 'CRLpath:string' \
  55. 'curve:string' \
  56. 'debug:or(range(0,7),string)' \
  57. 'delay:bool' \
  58. 'engineId:string' \
  59. 'engineNum:and(uinteger,min(1))' \
  60. 'exec:string' \
  61. 'execArgs:string' \
  62. 'failover:or("prio","rr")' \
  63. 'ident:string' \
  64. 'include:directory' \
  65. 'key:string' \
  66. 'local:host' \
  67. 'logId:or("process","sequential","thread","unique")' \
  68. 'OCSP:string' \
  69. 'OCSPaia:bool' \
  70. 'OCSPflag:list(or("NOCASIGN","NOCERTS","NOCHAIN","NOCHECKS","NODELEGATED","NOEXPLICIT","NOINTERN","NOSIGS","NOTIME","NOVERIFY","RESPID_KEY","TRUSTOTHER"))' \
  71. 'OCSPnonce:bool' \
  72. 'options:list(string) ' \
  73. 'protocol:or("cifs","connect","imap","nntp","pgsql","pop3","proxy","smtp","socks")' \
  74. 'protocolAuthentication:or("basic","login","ntlm","plain")' \
  75. 'protocolDomain:hostname' \
  76. 'protocolHost_host:host' \
  77. 'protocolHost_port:port' \
  78. 'protocolPassword:string' \
  79. 'protocolUsername:string' \
  80. 'PSKidentity:string' \
  81. 'PSKsecrets:string' \
  82. 'pty:bool' \
  83. 'redirect_host:host' \
  84. 'redirect_port:port' \
  85. 'renegotiation:bool' \
  86. 'requireCert:bool' \
  87. 'reset:bool' \
  88. 'retry:bool' \
  89. 'service:string' \
  90. 'sessionCacheSize:uinteger' \
  91. 'sessionCacheTimeout:uinteger' \
  92. 'sessiond_host:host' \
  93. 'sessiond_port:port' \
  94. 'sni:list(string)' \
  95. 'socket:list(string)' \
  96. 'sslVersion:or("all","SSLv2","SSLv3","TLSv1","TLSv1.1","TLSv1.2")' \
  97. 'stack:uinteger' \
  98. 'TIMEOUTbusy:uinteger' \
  99. 'TIMEOUTclose:uinteger' \
  100. 'TIMEOUTconnect:uinteger' \
  101. 'TIMEOUTidle:uinteger' \
  102. 'transparent:or("both","destination","none","source")' \
  103. 'verifyChain:bool' \
  104. 'verifyPeer:bool' \
  105. ;
  106. }
  107. validate_globals_section_service_options() {
  108. validate_service_options globals "$@"
  109. }
  110. validate_service_section_service_options() {
  111. validate_service_options service "$@"
  112. }
  113. print_options() {
  114. local _opt
  115. local _value
  116. for _opt in $*; do
  117. eval "_value=\$$_opt"
  118. [ -z "$_value" ] || echo "$_opt = $_value" >> "$CONF_FILE"
  119. done
  120. }
  121. print_bool_options() {
  122. local _opt
  123. local _bool
  124. local _value
  125. for _opt in $*; do
  126. eval "_bool=\$$_opt"
  127. [ -z "$_bool" ] || {
  128. _value=no
  129. [ "$_bool" != 1 ] || _value=yes
  130. echo "$_opt = $_value" >> "$CONF_FILE"
  131. }
  132. done
  133. }
  134. print_lists_map() {
  135. local _opt
  136. local _values
  137. local _value
  138. for _opt in $*; do
  139. eval "_values=\$$_opt"
  140. for _value in $_values; do
  141. echo "$_opt = $_value" >> "$CONF_FILE"
  142. done
  143. done
  144. }
  145. print_lists_reduce() {
  146. local _delim="$1"
  147. local _opt
  148. local _value
  149. local _values
  150. local _v
  151. shift
  152. for _opt in $*; do
  153. _value=
  154. eval "_values=\$$_opt"
  155. for _v in $_values; do
  156. _value=$_value$_delim$_v
  157. done
  158. _value=${_value#$_delim}
  159. [ -z "$_value" ] || echo "$_opt = $_value" >> "$CONF_FILE"
  160. done
  161. }
  162. print_host_port() {
  163. local _opt
  164. local _host
  165. local _port
  166. for _opt in $*; do
  167. eval "_host=\${${_opt}_host}"
  168. eval "_port=\${${_opt}_port}"
  169. [ -z "$_host" ] || [ -z "$_port" ] || echo "$_opt = $_host:$_port" >> "$CONF_FILE"
  170. done
  171. }
  172. print_optional_host_port() {
  173. local _opt
  174. local _host
  175. local _port
  176. local _value
  177. for _opt in $*; do
  178. eval "_host=\${${_opt}_host}"
  179. eval "_port=\${${_opt}_port}"
  180. [ -z "$_port" ] || {
  181. _value=$_port
  182. [ -z "$_host" ] || _value=$_host:$_port
  183. echo "$_opt = $_value" >> "$CONF_FILE"
  184. }
  185. done
  186. }
  187. print_global_options() {
  188. print_options \
  189. compression \
  190. EGD \
  191. engine \
  192. engineCtrl \
  193. log \
  194. output \
  195. RNDbytes \
  196. RNDfile \
  197. RNDoverwrite \
  198. ;
  199. print_bool_options \
  200. syslog \
  201. ;
  202. print_lists_reduce , \
  203. engineDefault \
  204. ;
  205. }
  206. print_service_options() {
  207. [ "$2" = 0 ] || {
  208. echo "validation failed"
  209. return 1
  210. }
  211. print_options \
  212. CAfile \
  213. CApath \
  214. cert \
  215. CRLfile \
  216. CRLpath \
  217. curve \
  218. debug \
  219. logId \
  220. engineId \
  221. engineNum \
  222. exec \
  223. execArgs \
  224. failover \
  225. ident \
  226. include \
  227. key \
  228. local \
  229. OCSP \
  230. protocol \
  231. protocolAuthentication \
  232. protocolDomain \
  233. protocolPassword \
  234. protocolUsername \
  235. PSKidentity \
  236. PSKsecrets \
  237. service \
  238. sessionCacheSize \
  239. sessionCacheTimeout \
  240. setgid \
  241. setuid \
  242. sslVersion \
  243. stack \
  244. TIMEOUTbusy \
  245. TIMEOUTclose \
  246. TIMEOUTconnect \
  247. TIMEOUTidle \
  248. transparent \
  249. ;
  250. print_bool_options \
  251. client \
  252. delay \
  253. OCSPaia \
  254. OCSPnonce \
  255. pty \
  256. renegotiation \
  257. requireCert \
  258. reset \
  259. retry \
  260. verifyChain \
  261. verifyPeer \
  262. ;
  263. print_lists_map \
  264. checkEmail \
  265. checkHost \
  266. checkIP \
  267. config \
  268. connect \
  269. OCSPflag \
  270. options \
  271. sni \
  272. socket \
  273. ;
  274. print_lists_reduce : \
  275. ciphers \
  276. ;
  277. print_host_port \
  278. protocolHost \
  279. sessiond \
  280. ;
  281. print_optional_host_port \
  282. accept \
  283. redirect \
  284. ;
  285. }
  286. create_conf_file() {
  287. [ -n "$CONF_FILE_CREATED" ] || {
  288. mkdir -p "$(dirname "$CONF_FILE")"
  289. echo "; STunnel configuration file generated by uci" > "$CONF_FILE"
  290. echo "; Written $(date +'%c')" >> "$CONF_FILE"
  291. echo >> "$CONF_FILE"
  292. echo "foreground = quiet" >> "$CONF_FILE"
  293. echo "pid = $PID_FILE" >> "$CONF_FILE"
  294. CONF_FILE_CREATED=1
  295. }
  296. }
  297. global_defs() {
  298. local pid_dir
  299. [ "$2" = 0 ] || {
  300. echo "validation failed"
  301. return 1
  302. }
  303. # If the first globals section has alt_config_file, don't process any more globals
  304. [ -z "$HAVE_ALT_CONF_FILE" ] || return 0
  305. # If "alt_config_file" specified in the first globals section, use that instead
  306. [ -z "$alt_config_file" ] || [ -n "$CONF_FILE_CREATED" ] || {
  307. # Symlink "alt_config_file" since it's a bit easier and safer
  308. ln -s "$alt_config_file" "$CONF_FILE"
  309. # Set section found to start service, user hopefully knows what they are doing
  310. SERVICE_SECTION_FOUND=1
  311. CONF_FILE_CREATED=1
  312. HAVE_ALT_CONF_FILE=1
  313. return 0
  314. }
  315. pid_dir="$(dirname "$PID_FILE")"
  316. mkdir -p "$pid_dir"
  317. [ -z "$setuid" ] || chown "$setuid" "$pid_dir"
  318. [ -z "$setgid" ] || chown ":$setgid" "$pid_dir"
  319. create_conf_file
  320. print_global_options
  321. validate_service_options globals "$1" print_service_options
  322. }
  323. service_section() {
  324. [ "$2" = 0 ] || {
  325. echo "validation failed"
  326. return 1
  327. }
  328. [ "$enabled" = 1 ] || return 0
  329. SERVICE_SECTION_FOUND=1
  330. echo >> "$CONF_FILE"
  331. echo "[$1]" >> "$CONF_FILE"
  332. validate_service_options service "$1" print_service_options
  333. }
  334. service_triggers() {
  335. procd_add_reload_trigger stunnel
  336. procd_open_validate
  337. validate_globals_section
  338. validate_globals_section_service_options
  339. validate_service_section
  340. validate_service_section_service_options
  341. procd_close_validate
  342. }
  343. start_service() {
  344. rm -f "$CONF_FILE"
  345. config_load stunnel
  346. config_foreach validate_globals_section globals global_defs
  347. [ -n "$HAVE_ALT_CONF_FILE" ] || {
  348. create_conf_file
  349. config_foreach validate_service_section service service_section
  350. }
  351. [ -n "$SERVICE_SECTION_FOUND" ] || {
  352. logger -t stunnel -p daemon.info "No uci service section enabled or found!"
  353. return 1
  354. }
  355. procd_open_instance
  356. procd_set_param command "$BIN"
  357. procd_append_param command "$CONF_FILE"
  358. procd_set_param respawn
  359. procd_set_param file "$CONF_FILE"
  360. procd_close_instance
  361. }