#!/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/stunnel.pid"
|
|
CONF_FILE="/var/etc/stunnel.conf"
|
|
BIN="/usr/bin/stunnel"
|
|
CONF_FILE_CREATED=
|
|
HAVE_ALT_CONF_FILE=
|
|
SERVICE_SECTION_FOUND=
|
|
|
|
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' \
|
|
;
|
|
}
|
|
|
|
validate_service_section() {
|
|
uci_load_validate stunnel service "$1" "$2" \
|
|
'enabled:bool:1' \
|
|
\
|
|
'setgid:or(string,uinteger)' \
|
|
'setuid:or(string,uinteger)' \
|
|
;
|
|
}
|
|
|
|
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)' \
|
|
'ciphersuites:list(string)' \
|
|
'client:bool' \
|
|
'config:list(string)' \
|
|
'connect:list(string)' \
|
|
'CRLfile:string' \
|
|
'CRLpath:string' \
|
|
'curves:list(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' \
|
|
'ticketKeySecret:string' \
|
|
'ticketMacSecret:string' \
|
|
'TIMEOUTbusy:uinteger' \
|
|
'TIMEOUTclose:uinteger' \
|
|
'TIMEOUTconnect:uinteger' \
|
|
'TIMEOUTidle:uinteger' \
|
|
'transparent:or("both","destination","none","source")' \
|
|
'verifyChain:bool' \
|
|
'verifyPeer:bool' \
|
|
;
|
|
}
|
|
|
|
validate_globals_section_service_options() {
|
|
validate_service_options globals "$@"
|
|
}
|
|
|
|
validate_service_section_service_options() {
|
|
validate_service_options service "$@"
|
|
}
|
|
|
|
print_options() {
|
|
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_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_lists_reduce() {
|
|
local _delim="$1"
|
|
local _opt
|
|
local _value
|
|
local _values
|
|
local _v
|
|
shift
|
|
for _opt in $*; do
|
|
_value=
|
|
eval "_values=\$$_opt"
|
|
for _v in $_values; do
|
|
_value=$_value$_delim$_v
|
|
done
|
|
_value=${_value#$_delim}
|
|
[ -z "$_value" ] || echo "$_opt = $_value" >> "$CONF_FILE"
|
|
done
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
print_global_options() {
|
|
print_options \
|
|
compression \
|
|
EGD \
|
|
engine \
|
|
engineCtrl \
|
|
log \
|
|
output \
|
|
RNDbytes \
|
|
RNDfile \
|
|
RNDoverwrite \
|
|
;
|
|
|
|
print_bool_options \
|
|
syslog \
|
|
;
|
|
|
|
print_lists_reduce , \
|
|
engineDefault \
|
|
;
|
|
}
|
|
|
|
print_service_options() {
|
|
[ "$2" = 0 ] || {
|
|
echo "validation failed"
|
|
return 1
|
|
}
|
|
|
|
print_options \
|
|
CAfile \
|
|
CApath \
|
|
cert \
|
|
CRLfile \
|
|
CRLpath \
|
|
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 \
|
|
ticketKeySecret \
|
|
ticketMacSecret \
|
|
TIMEOUTbusy \
|
|
TIMEOUTclose \
|
|
TIMEOUTconnect \
|
|
TIMEOUTidle \
|
|
transparent \
|
|
;
|
|
|
|
print_bool_options \
|
|
client \
|
|
delay \
|
|
OCSPaia \
|
|
OCSPnonce \
|
|
pty \
|
|
renegotiation \
|
|
requireCert \
|
|
reset \
|
|
retry \
|
|
verifyChain \
|
|
verifyPeer \
|
|
;
|
|
|
|
print_lists_map \
|
|
checkEmail \
|
|
checkHost \
|
|
checkIP \
|
|
config \
|
|
connect \
|
|
OCSPflag \
|
|
options \
|
|
sni \
|
|
socket \
|
|
;
|
|
|
|
print_lists_reduce : \
|
|
ciphers \
|
|
curves \
|
|
ciphersuites \
|
|
;
|
|
|
|
print_host_port \
|
|
protocolHost \
|
|
sessiond \
|
|
;
|
|
|
|
print_optional_host_port \
|
|
accept \
|
|
redirect \
|
|
;
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
global_defs() {
|
|
local pid_dir
|
|
|
|
[ "$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 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 hopefully knows what they are doing
|
|
SERVICE_SECTION_FOUND=1
|
|
CONF_FILE_CREATED=1
|
|
HAVE_ALT_CONF_FILE=1
|
|
return 0
|
|
}
|
|
|
|
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_open_validate
|
|
validate_globals_section
|
|
validate_globals_section_service_options
|
|
validate_service_section
|
|
validate_service_section_service_options
|
|
procd_close_validate
|
|
}
|
|
|
|
start_service() {
|
|
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!"
|
|
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
|
|
}
|