stunnel: Update init script 8070/head
authorJeffery To <jeffery.to@gmail.com>
Tue, 29 Jan 2019 13:21:20 +0000 (21:21 +0800)
committerJeffery To <jeffery.to@gmail.com>
Tue, 29 Jan 2019 13:49:23 +0000 (21:49 +0800)
The reworked init script:
* Loads and validates options using uci_validate_section() (through
  uci_load_validate())
* Allows service options be specified in the globals section
* Hard-codes less global options (debug, syslog), as their default
  values already work
* Adds support for almost all options (up to the current package
  version, 5.49)
* Moves the pid file into a subdirectory (/var/run/stunnel) so that it
  can be created successfully when setuid is used

Certain options are omitted:
* chroot - requires more setup than the init script can manage
* fips, libwrap - disabled at compile-time
* iconActive, iconError, iconIdle, taskbar - gui/win32 only
* verify - obsolete, verifyChain and/or verifyPeer should be used
  instead

Signed-off-by: Jeffery To <jeffery.to@gmail.com>
net/stunnel/Makefile
net/stunnel/files/stunnel.init
net/stunnel/files/stunnel.uci

index e05c1d582700c26f8cbc18638c82f94570a2b49b..a9736c1a2cd0e72cdb712ab4b46462a5bff3f882 100644 (file)
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=stunnel
 PKG_VERSION:=5.49
-PKG_RELEASE:=1
+PKG_RELEASE:=2
 
 PKG_LICENSE:=GPL-2.0+
 PKG_MAINTAINER:=Florian Eckert <fe@dev.tdt.de>
index a1772e1593af6c27c5d59bf680c04b18ef3d22f9..0a2dfa41690d3c995598d760a257683e63cf3b80 100644 (file)
 #!/bin/sh /etc/rc.common
 # Copyright (C) 2006-2008 OpenWrt.org
+# Copyright (C) 2019 Jeffery To
 
 START=90
 USE_PROCD=1
 
-PID_FILE="/var/run/stunnel.pid"
-CONF_FILE="/tmp/stunnel.conf"
+PID_FILE="/var/run/stunnel/stunnel.pid"
+CONF_FILE="/var/etc/stunnel.conf"
 BIN="/usr/bin/stunnel"
-SERVICE_SECTION_FOUND=0
+CONF_FILE_CREATED=
+HAVE_ALT_CONF_FILE=
+SERVICE_SECTION_FOUND=
 
-global_defs() {
-       local debug compression
+validate_globals_section() {
+       uci_load_validate stunnel globals "$1" "$2" \
+               'alt_config_file:file' \
+               \
+               'compression:or("deflate","zlib")' \
+               'EGD:string' \
+               'engine:string' \
+               'engineCtrl:string' \
+               'engineDefault:list(or("ALL","CIPHERS","DH","DIGESTS","DSA","ECDH","ECDSA","PKEY","PKEY_ASN1","PKEY_CRYPTO","RAND","RSA"))' \
+               'log:or("append","overwrite")' \
+               'output:string' \
+               'RNDbytes:uinteger' \
+               'RNDfile:string' \
+               'RNDoverwrite:bool' \
+               'setgid:or(string,uinteger)' \
+               'setuid:or(string,uinteger)' \
+               'syslog:bool' \
+               ;
+}
 
-       config_get alt_config_file 'globals' alt_config_file
-       [ -z "$alt_config_file" ] || return 0
+validate_service_section() {
+       uci_load_validate stunnel service "$1" "$2" \
+               'enabled:bool:1' \
+               \
+               'setgid:or(string,uinteger)' \
+               'setuid:or(string,uinteger)' \
+               ;
+}
 
-       # Set default settings
-       printf "foreground = yes\n" >> "$CONF_FILE"
-       printf "pid = %s\n" "$PID_FILE" >> "$CONF_FILE"
-       printf "syslog = yes\n" >> "$CONF_FILE"
+validate_service_options() {
+       uci_load_validate stunnel "$1" "$2" "$3" \
+               'accept_host:host' \
+               'accept_port:port' \
+               'CAfile:string' \
+               'CApath:string' \
+               'cert:string' \
+               'checkEmail:list(string)' \
+               'checkHost:list(host)' \
+               'checkIP:list(ipaddr)' \
+               'ciphers:list(string)' \
+               'client:bool' \
+               'config:list(string)' \
+               'connect:list(string)' \
+               'CRLfile:string' \
+               'CRLpath:string' \
+               'curve:string' \
+               'debug:or(range(0,7),string)' \
+               'delay:bool' \
+               'engineId:string' \
+               'engineNum:and(uinteger,min(1))' \
+               'exec:string' \
+               'execArgs:string' \
+               'failover:or("prio","rr")' \
+               'ident:string' \
+               'include:directory' \
+               'key:string' \
+               'local:host' \
+               'logId:or("process","sequential","thread","unique")' \
+               'OCSP:string' \
+               'OCSPaia:bool' \
+               'OCSPflag:list(or("NOCASIGN","NOCERTS","NOCHAIN","NOCHECKS","NODELEGATED","NOEXPLICIT","NOINTERN","NOSIGS","NOTIME","NOVERIFY","RESPID_KEY","TRUSTOTHER"))' \
+               'OCSPnonce:bool' \
+               'options:list(string)   ' \
+               'protocol:or("cifs","connect","imap","nntp","pgsql","pop3","proxy","smtp","socks")' \
+               'protocolAuthentication:or("basic","login","ntlm","plain")' \
+               'protocolDomain:hostname' \
+               'protocolHost_host:host' \
+               'protocolHost_port:port' \
+               'protocolPassword:string' \
+               'protocolUsername:string' \
+               'PSKidentity:string' \
+               'PSKsecrets:string' \
+               'pty:bool' \
+               'redirect_host:host' \
+               'redirect_port:port' \
+               'renegotiation:bool' \
+               'requireCert:bool' \
+               'reset:bool' \
+               'retry:bool' \
+               'service:string' \
+               'sessionCacheSize:uinteger' \
+               'sessionCacheTimeout:uinteger' \
+               'sessiond_host:host' \
+               'sessiond_port:port' \
+               'sni:list(string)' \
+               'socket:list(string)' \
+               'sslVersion:or("all","SSLv2","SSLv3","TLSv1","TLSv1.1","TLSv1.2")' \
+               'stack:uinteger' \
+               'TIMEOUTbusy:uinteger' \
+               'TIMEOUTclose:uinteger' \
+               'TIMEOUTconnect:uinteger' \
+               'TIMEOUTidle:uinteger' \
+               'transparent:or("both","destination","none","source")' \
+               'verifyChain:bool' \
+               'verifyPeer:bool' \
+               ;
+}
 
-       config_get debug 'globals' debug '5'
-       printf "debug = %s\n" "$debug" >> "$CONF_FILE"
+validate_globals_section_service_options() {
+       validate_service_options globals "$@"
+}
 
-       config_get compression 'globals' compression
-       [ -z "$compression" ] || printf "compression = %s\n" "$compression" >> "$CONF_FILE"
+validate_service_section_service_options() {
+       validate_service_options service "$@"
 }
 
 print_options() {
-       local config=$1
-       shift
-       for opt in "$@"; do
-               local $opt
-               local value
-               local is_boolean=0
-
-               if [ "${opt:0:5}" == "bool_" ]; then
-                       opt="${opt:5}"
-                       is_boolean=1
-               fi
-
-               config_get "value" "$config" "$opt"
-               [ -z "$value" ] || {
-                       if [ "$value" = '1' ] && [ "$is_boolean" -eq "1" ]; then
-                               value="yes"
-                       elif [ "$value" = '0' ] && [ "$is_boolean" -eq "1" ] ; then
-                               value="no"
-                       fi
-                       printf "%s = %s\n" "$opt" "$value" >> "$CONF_FILE"
+       local _opt
+       local _value
+       for _opt in $*; do
+               eval "_value=\$$_opt"
+               [ -z "$_value" ] || echo "$_opt = $_value" >> "$CONF_FILE"
+       done
+}
+
+print_bool_options() {
+       local _opt
+       local _bool
+       local _value
+       for _opt in $*; do
+               eval "_bool=\$$_opt"
+               [ -z "$_bool" ] || {
+                       _value=no
+                       [ "$_bool" != 1 ] || _value=yes
+                       echo "$_opt = $_value" >> "$CONF_FILE"
                }
        done
 }
 
-print_list() {
-       local config=$1
-       shift
-       for opt in "$@"; do
-               local $opt
-               local elements
-               config_get "elements" "$config" "$opt"
-               for element in $elements; do
-                       printf "%s = %s\n" "$opt" "$element" >> "$CONF_FILE"
+print_lists_map() {
+       local _opt
+       local _values
+       local _value
+       for _opt in $*; do
+               eval "_values=\$$_opt"
+               for _value in $_values; do
+                       echo "$_opt = $_value" >> "$CONF_FILE"
                done
        done
 }
 
-print_list_colon() {
-       local config=$1
-       local value
+print_lists_reduce() {
+       local _delim="$1"
+       local _opt
+       local _value
+       local _values
+       local _v
        shift
-       for opt in "$@"; do
-               local $opt
-               local elements
-               config_get "elements" "$config" "$opt"
-               for element in $elements; do
-                       value="${value}:${element}"
+       for _opt in $*; do
+               _value=
+               eval "_values=\$$_opt"
+               for _v in $_values; do
+                       _value=$_value$_delim$_v
                done
-               printf "%s = %s\n" "$opt" "${value#*:}" >> "$CONF_FILE"
+               _value=${_value#$_delim}
+               [ -z "$_value" ] || echo "$_opt = $_value" >> "$CONF_FILE"
        done
 }
 
-service_section() {
-       local cfg="$1"
-       local accept_host accept_port enabled
+print_host_port() {
+       local _opt
+       local _host
+       local _port
+       for _opt in $*; do
+               eval "_host=\${${_opt}_host}"
+               eval "_port=\${${_opt}_port}"
+               [ -z "$_host" ] || [ -z "$_port" ] || echo "$_opt = $_host:$_port" >> "$CONF_FILE"
+       done
+}
 
-       config_get_bool enabled "$cfg" 'enabled' '1'
-       [ ${enabled} -gt 0 ] || return 0
+print_optional_host_port() {
+       local _opt
+       local _host
+       local _port
+       local _value
+       for _opt in $*; do
+               eval "_host=\${${_opt}_host}"
+               eval "_port=\${${_opt}_port}"
+               [ -z "$_port" ] || {
+                       _value=$_port
+                       [ -z "$_host" ] || _value=$_host:$_port
+                       echo "$_opt = $_value" >> "$CONF_FILE"
+               }
+       done
+}
 
-       SERVICE_SECTION_FOUND=1
-       printf "\n" >> "$CONF_FILE"
-       printf "[%s]\n" "$cfg" >> "$CONF_FILE"
+print_global_options() {
+       print_options \
+               compression \
+               EGD \
+               engine \
+               engineCtrl \
+               log \
+               output \
+               RNDbytes \
+               RNDfile \
+               RNDoverwrite \
+               ;
+
+       print_bool_options \
+               syslog \
+               ;
 
-       config_get accept_host "$cfg" accept_host 'localhost'
-       config_get accept_port "$cfg" accept_port
-       printf "accept = %s:%s\n" "$accept_host" "$accept_port" >> "$CONF_FILE"
+       print_lists_reduce , \
+               engineDefault \
+               ;
+}
+
+print_service_options() {
+       [ "$2" = 0 ] || {
+               echo "validation failed"
+               return 1
+       }
 
-       print_options "$cfg" CApath \
+       print_options \
                CAfile \
+               CApath \
                cert \
-               CRLpath \
                CRLfile \
+               CRLpath \
                curve \
-               logId \
                debug \
+               logId \
                engineId \
                engineNum \
+               exec \
+               execArgs \
                failover \
                ident \
+               include \
                key \
                local \
+               OCSP \
+               protocol \
+               protocolAuthentication \
+               protocolDomain \
+               protocolPassword \
+               protocolUsername \
                PSKidentity \
                PSKsecrets \
+               service \
+               sessionCacheSize \
+               sessionCacheTimeout \
+               setgid \
+               setuid \
                sslVersion \
+               stack \
                TIMEOUTbusy \
                TIMEOUTclose \
                TIMEOUTconnect \
                TIMEOUTidle \
-               bool_delay \
-               bool_libwrap \
-               bool_reset \
-               bool_requireCert \
-               bool_verifyChain \
-               bool_verifyPeer \
-               bool_client
-
-       print_list "$cfg" checkEmail \
+               transparent \
+               ;
+
+       print_bool_options \
+               client \
+               delay \
+               OCSPaia \
+               OCSPnonce \
+               pty \
+               renegotiation \
+               requireCert \
+               reset \
+               retry \
+               verifyChain \
+               verifyPeer \
+               ;
+
+       print_lists_map \
+               checkEmail \
                checkHost \
                checkIP \
+               config \
                connect \
-               options
+               OCSPflag \
+               options \
+               sni \
+               socket \
+               ;
 
-       print_list_colon "$cfg" ciphers
-}
+       print_lists_reduce : \
+               ciphers \
+               ;
 
-process_config() {
-       local alt_config_file
+       print_host_port \
+               protocolHost \
+               sessiond \
+               ;
 
-       rm -f "$CONF_FILE"
+       print_optional_host_port \
+               accept \
+               redirect \
+               ;
+}
 
-       # First line
-       printf "; STunnel configuration file generated by uci\n" > "$CONF_FILE"
-       printf "; Written %s\n\n" "$(date +'%c')" >> "$CONF_FILE"
+create_conf_file() {
+       [ -n "$CONF_FILE_CREATED" ] || {
+               mkdir -p "$(dirname "$CONF_FILE")"
+               echo "; STunnel configuration file generated by uci" > "$CONF_FILE"
+               echo "; Written $(date +'%c')" >> "$CONF_FILE"
+               echo >> "$CONF_FILE"
+               echo "foreground = quiet" >> "$CONF_FILE"
+               echo "pid = $PID_FILE" >> "$CONF_FILE"
+               CONF_FILE_CREATED=1
+       }
+}
 
-       [ -f /etc/config/stunnel ] || return 0
+global_defs() {
+       local pid_dir
 
-       config_load stunnel
-       global_defs
+       [ "$2" = 0 ] || {
+               echo "validation failed"
+               return 1
+       }
+
+       # If the first globals section has alt_config_file, don't process any more globals
+       [ -z "$HAVE_ALT_CONF_FILE" ] || return 0
 
-       # If "alt_config_file" specified, use that instead
-       [ -n "$alt_config_file" ] && [ -f "$alt_config_file" ] && {
-               rm -f "$CONF_FILE"
+       # If "alt_config_file" specified in the first globals section, use that instead
+       [ -z "$alt_config_file" ] || [ -n "$CONF_FILE_CREATED" ] || {
                # Symlink "alt_config_file" since it's a bit easier and safer
                ln -s "$alt_config_file" "$CONF_FILE"
-               # Set section found to start service user hopfully knows what you does
+               # Set section found to start service, user hopefully knows what they are doing
                SERVICE_SECTION_FOUND=1
+               CONF_FILE_CREATED=1
+               HAVE_ALT_CONF_FILE=1
                return 0
        }
 
-       config_foreach service_section service
+       pid_dir="$(dirname "$PID_FILE")"
+       mkdir -p "$pid_dir"
+       [ -z "$setuid" ] || chown "$setuid" "$pid_dir"
+       [ -z "$setgid" ] || chown ":$setgid" "$pid_dir"
+
+       create_conf_file
+       print_global_options
+       validate_service_options globals "$1" print_service_options
+}
+
+service_section() {
+       [ "$2" = 0 ] || {
+               echo "validation failed"
+               return 1
+       }
+
+       [ "$enabled" = 1 ] || return 0
+
+       SERVICE_SECTION_FOUND=1
+       echo >> "$CONF_FILE"
+       echo "[$1]" >> "$CONF_FILE"
+
+       validate_service_options service "$1" print_service_options
 }
 
 service_triggers() {
-       procd_add_reload_trigger "stunnel"
+       procd_add_reload_trigger stunnel
+
+       procd_open_validate
+       validate_globals_section
+       validate_globals_section_service_options
+       validate_service_section
+       validate_service_section_service_options
+       procd_close_validate
 }
 
 start_service() {
-       process_config
-
-       if [ "$SERVICE_SECTION_FOUND" = 1 ]; then
-               procd_open_instance
-               procd_set_param command "$BIN"
-               procd_append_param command "$CONF_FILE"
-               procd_set_param respawn
-               procd_set_param file "$CONF_FILE"
-               procd_close_instance
-       else
+       rm -f "$CONF_FILE"
+       config_load stunnel
+
+       config_foreach validate_globals_section globals global_defs
+
+       [ -n "$HAVE_ALT_CONF_FILE" ] || {
+               create_conf_file
+               config_foreach validate_service_section service service_section
+       }
+
+       [ -n "$SERVICE_SECTION_FOUND" ] || {
                logger -t stunnel -p daemon.info "No uci service section enabled or found!"
-       fi
+               return 1
+       }
+
+       procd_open_instance
+       procd_set_param command "$BIN"
+       procd_append_param command "$CONF_FILE"
+       procd_set_param respawn
+       procd_set_param file "$CONF_FILE"
+       procd_close_instance
 }
index 6fad1c6c7e9b76658f12648906763c6116a5862e..89633ab99ad9cb905a8a058ad97cbc10d549792b 100644 (file)
@@ -1,8 +1,10 @@
 config globals 'globals'
-       option alt_config_file '/etc/stunnel/stunnel.conf'
-       option debug '5'
+       #option alt_config_file '/etc/stunnel/stunnel.conf'
+       option setuid 'nobody'
+       option setgid 'nogroup'
 
 config service 'dummy'
+       option enabled '1'
        option client '1'
        option accept_host 'localhost'
        option accept_port '6000'