mirror of
https://github.com/openwrt/packages.git
synced 2026-04-15 10:51:55 +00:00
When alt_config_file is set, global_defs() returns before creating the PID file directory. stunnel then fails to start because it cannot write its PID file to the nonexistent directory. Move the PID directory creation and ownership setup above the alt_config_file early return so it runs regardless of config mode. Fixes: openwrt/openwrt#28982 Signed-off-by: Joshua Klinesmith <joshuaklinesmith@gmail.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
411 lines
8.3 KiB
Bash
411 lines
8.3 KiB
Bash
#!/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","capwin","capwinctrl","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' \
|
|
'sessionResume:bool' \
|
|
'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 \
|
|
sessionResume \
|
|
;
|
|
|
|
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
|
|
|
|
pid_dir="$(dirname "$PID_FILE")"
|
|
mkdir -p "$pid_dir"
|
|
[ -z "$setuid" ] || chown "$setuid" "$pid_dir"
|
|
[ -z "$setgid" ] || chown ":$setgid" "$pid_dir"
|
|
|
|
# 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
|
|
}
|
|
|
|
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
|
|
}
|