|
|
@ -1,180 +1,402 @@ |
|
|
|
#!/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 |
|
|
|
} |