dropbear: 'rsakeyfile' -> 'keyfile' transition
[openwrt/staging/jow.git] / package / network / services / dropbear / files / dropbear.init
index 728767f0309e39d15cf3f5c2476c094e168cae13..18273e63d0e9085918d66a30cef5c3a9366aa943 100755 (executable)
 # Copyright (C) 2006-2010 OpenWrt.org
 # Copyright (C) 2006 Carlos Sobrinho
 
-START=50
+START=19
 STOP=50
 
 USE_PROCD=1
-
-NAME=dropbear
 PROG=/usr/sbin/dropbear
+NAME=dropbear
 PIDCOUNT=0
-EXTRA_COMMANDS="killclients"
-EXTRA_HELP="   killclients Kill ${NAME} processes except servers and yourself"
 
-dropbear_instance()
+extra_command "killclients" "Kill ${NAME} processes except servers and yourself"
+
+_dropbearkey()
 {
-       append_ports()
-       {
-               local ifname="$1"
-               local port="$2"
-
-               grep -qs "^ *$ifname:" /proc/net/dev || {
-                       procd_append_param command -p "$port"
-                       return
-               }
+       /usr/bin/dropbearkey "$@" 0<&- 1>&- 2>&-
+}
 
-               for addr in $(
-                       ifconfig "$ifname" | sed -ne '
-                               /addr: *fe[89ab][0-9a-f]:/d
-                               s/.* addr: *\([0-9a-f:\.]*\).*/\1/p
-                       '
-               ); do
-                       procd_append_param command -p "$addr:$port"
-               done
+# $1 - host key file name
+hk_verify()
+{
+       [ -f "$1" ] || return 1
+       [ -s "$1" ] || return 2
+       _dropbearkey -y -f "$1" || return 3
+       return 0
+}
+
+# $1 - hk_verify() return code
+hk_errmsg()
+{
+       case "$1" in
+       0) ;;
+       1) echo "file does not exist" ;;
+       2) echo "file has zero length" ;;
+       3) echo "file is not valid host key or not supported" ;;
+       *) echo "unknown error" ;;
+       esac
+}
+
+# $1 - config option
+# $2 - host key file name
+hk_config()
+{
+       local x m
+       hk_verify "$2"; x=$?
+       case "$x" in
+       0)      procd_append_param command -r "$2"
+               ;;
+       *)      m=$(hk_errmsg "$x")
+               logger -t "${NAME}" -p daemon.warn \
+                 "option '$1', value '$2': $m, skipping"
+               ;;
+       esac
+}
+
+# $1 - host key file name
+hk_config__keyfile()
+{
+       hk_config 'keyfile' "$1"
+}
+
+hk_generate_as_needed()
+{
+       local kdir kgen ktype tdir kcount tfile
+       kdir='/etc/dropbear'
+
+       kgen=''
+       for ktype in ed25519 ecdsa rsa; do
+               hk_verify "${kdir}/dropbear_${ktype}_host_key" && continue
+
+               kgen="${kgen} ${ktype}"
+       done
+
+       [ -z "${kgen}" ] && return
+
+       tdir=$(mktemp -d); chmod 0700 "${tdir}"
+
+       kcount=0
+       for ktype in ${kgen}; do
+               tfile="${tdir}/dropbear_${ktype}_host_key"
+
+               if ! _dropbearkey -t ${ktype} -f "${tfile}"; then
+                       # unsupported key type
+                       rm -f "${tfile}"
+                       continue
+               fi
+
+               kcount=$((kcount+1))
+       done
+
+       if [ ${kcount} -ne 0 ]; then
+               mkdir -p "${kdir}"; chmod 0700 "${kdir}"; chown root "${kdir}"
+               mv -f "${tdir}/"* "${kdir}/"
+       fi
+
+       rm -rf "${tdir}"
+}
+
+append_ports()
+{
+       local ipaddrs="$1"
+       local port="$2"
+
+       [ -z "$ipaddrs" ] && {
+               procd_append_param command -p "$port"
+               return
        }
 
+       for addr in $ipaddrs; do
+               procd_append_param command -p "$addr:$port"
+       done
+}
 
-       local section="$1"
+validate_section_dropbear()
+{
+       uci_load_validate dropbear dropbear "$1" "$2" \
+               'PasswordAuth:bool:1' \
+               'enable:bool:1' \
+               'Interface:string' \
+               'GatewayPorts:bool:0' \
+               'ForceCommand:string' \
+               'RootPasswordAuth:bool:1' \
+               'RootLogin:bool:1' \
+               'rsakeyfile:file' \
+               'keyfile:list(file)' \
+               'BannerFile:file' \
+               'Port:port:22' \
+               'SSHKeepAlive:uinteger:300' \
+               'IdleTimeout:uinteger:0' \
+               'MaxAuthTries:uinteger:3' \
+               'RecvWindowSize:uinteger:0' \
+               'mdns:bool:1'
+}
 
-       # check if section is enabled (default)
-       local enabled
-       config_get_bool enabled "${section}" enable 1
-       [ "${enabled}" -eq 0 ] && return 1
+dropbear_instance()
+{
+       local ipaddrs
 
-       # increase pid file count to handle multiple instances correctly
-       PIDCOUNT="$(( ${PIDCOUNT} + 1))"
+       [ "$2" = 0 ] || {
+               echo "validation failed"
+               return 1
+       }
+
+       [ -n "${Interface}" ] && {
+               [ -n "${BOOT}" ] && return 0
+
+               network_get_ipaddrs_all ipaddrs "${Interface}" || {
+                       echo "interface ${Interface} has no physdev or physdev has no suitable ip"
+                       return 1
+               }
+       }
 
+       [ "${enable}" = "0" ] && return 1
+       PIDCOUNT="$(( ${PIDCOUNT} + 1))"
        local pid_file="/var/run/${NAME}.${PIDCOUNT}.pid"
 
+       # Increase default receive window size to increase
+       # throughput on high latency links
+       if [ "${RecvWindowSize}" -eq "0" ]; then
+               RecvWindowSize="262144"
+       fi
+
        procd_open_instance
        procd_set_param command "$PROG" -F -P "$pid_file"
-
-       # prepare parameters (initialise with pid file)
-       local val
-
-       # A) password authentication
-       config_get_bool val "${section}" PasswordAuth 1
-       [ "${val}" -eq 0 ] && procd_append_param command -s
-
-       # B) listen interface and port
-       local port
-       local interface
-       config_get interface "${section}" Interface
-       [ -n "$interface" ] && network_get_device interface "$interface"
-       config_get port "${section}" Port 22
-       append_ports "$interface" "$port"
-       # C) banner file
-       config_get val "${section}" BannerFile
-       [ -f "${val}" ] && procd_append_param command -b "${val}"
-       # D) gatewayports
-       config_get_bool val "${section}" GatewayPorts 0
-       [ "${val}" -eq 1 ] && procd_append_param command -a
-       # E) root password authentication
-       config_get_bool val "${section}" RootPasswordAuth 1
-       [ "${val}" -eq 0 ] && procd_append_param command -g
-       # F) root login
-       config_get_bool val "${section}" RootLogin 1
-       [ "${val}" -eq 0 ] && procd_append_param command -w
-       # G) host keys
-       config_get val "${section}" rsakeyfile
-       [ -f "${val}" ] && procd_append_param command -r "${val}"
-       config_get val "${section}" dsskeyfile
-       [ -f "${val}" ] && procd_append_param command -d "${val}"
-
+       [ "${PasswordAuth}" -eq 0 ] && procd_append_param command -s
+       [ "${GatewayPorts}" -eq 1 ] && procd_append_param command -a
+       [ -n "${ForceCommand}" ] && procd_append_param command -c "${ForceCommand}"
+       [ "${RootPasswordAuth}" -eq 0 ] && procd_append_param command -g
+       [ "${RootLogin}" -eq 0 ] && procd_append_param command -w
+       config_list_foreach "$1" 'keyfile' hk_config__keyfile
+       if [ -n "${rsakeyfile}" ]; then
+               logger -s -t "${NAME}" -p daemon.crit \
+                 "Option 'rsakeyfile' is considered to be DEPRECATED and will be REMOVED in future releases, use 'keyfile' list instead"
+               sed -i.before-upgrade -E -e 's/option(\s+)rsakeyfile/list keyfile/' \
+                 "/etc/config/${NAME}"
+               logger -s -t "${NAME}" -p daemon.crit \
+                 "Auto-transition 'option rsakeyfile' => 'list keyfile' in /etc/config/${NAME} is done, please verify your configuration"
+               hk_config 'rsakeyfile' "${rsakeyfile}"
+       fi
+       [ -n "${BannerFile}" ] && procd_append_param command -b "${BannerFile}"
+       append_ports "${ipaddrs}" "${Port}"
+       [ "${IdleTimeout}" -ne 0 ] && procd_append_param command -I "${IdleTimeout}"
+       [ "${SSHKeepAlive}" -ne 0 ] && procd_append_param command -K "${SSHKeepAlive}"
+       [ "${MaxAuthTries}" -ne 0 ] && procd_append_param command -T "${MaxAuthTries}"
+       [ "${RecvWindowSize}" -gt 0 -a "${RecvWindowSize}" -le 1048576 ] && \
+               procd_append_param command -W "${RecvWindowSize}"
+       [ "${mdns}" -ne 0 ] && procd_add_mdns "ssh" "tcp" "$Port" "daemon=dropbear"
+       procd_set_param respawn
        procd_close_instance
 }
 
-keygen()
+load_interfaces()
 {
-       for keytype in rsa dss; do
-               # check for keys
-               key=dropbear/dropbear_${keytype}_host_key
-               [ -f /tmp/$key -o -s /etc/$key ] || {
-                       # generate missing keys
-                       mkdir -p /tmp/dropbear
-                       [ -x /usr/bin/dropbearkey ] && {
-                               /usr/bin/dropbearkey -t $keytype -f /tmp/$key 2>&- >&- && exec /etc/rc.common "$initscript" start
-                       } &
-               exit 0
-               }
-       done
+       config_get interface "$1" Interface
+       config_get enable "$1" enable 1
 
-       lock /tmp/.switch2jffs
-       mkdir -p /etc/dropbear
-       mv /tmp/dropbear/dropbear_* /etc/dropbear/
-       lock -u /tmp/.switch2jffs
-       chown root /etc/dropbear
-       chmod 0700 /etc/dropbear
+       [ "${enable}" = "1" ] && interfaces=" ${interface} ${interfaces}"
+}
+
+boot()
+{
+       BOOT=1
+       start "$@"
 }
 
 start_service()
 {
-       [ -s /etc/dropbear/dropbear_rsa_host_key -a \
-         -s /etc/dropbear/dropbear_dss_host_key ] || keygen
+       hk_generate_as_needed
 
        . /lib/functions.sh
        . /lib/functions/network.sh
 
        config_load "${NAME}"
-       config_foreach dropbear_instance dropbear
+       config_foreach validate_section_dropbear dropbear dropbear_instance
 }
 
 service_triggers()
 {
-       procd_add_reload_trigger "dropbear"
+       local interfaces
+
+       procd_add_config_trigger "config.change" "dropbear" /etc/init.d/dropbear reload
+
+       config_load "${NAME}"
+       config_foreach load_interfaces dropbear
+
+       [ -n "${interfaces}" ] && {
+               for n in $interfaces ; do
+                       procd_add_interface_trigger "interface.*" $n /etc/init.d/dropbear reload
+               done
+       }
+
+       procd_add_validation validate_section_dropbear
+}
+
+shutdown() {
+       # close all open connections
+       killall dropbear
 }
 
 killclients()
@@ -137,7 +249,7 @@ killclients()
        while [ "${pid}" -ne 0 ]
         do
                # get parent process id
-               pid=`cut -d ' ' -f 4 "/proc/${pid}/stat"`
+               pid=$(cut -d ' ' -f 4 "/proc/${pid}/stat")
                [ "${pid}" -eq 0 ] && break
 
                # check if client connection
@@ -148,14 +260,14 @@ killclients()
        done
 
        # get all server pids that should be ignored
-       for server in `cat /var/run/${NAME}.*.pid`
+       for server in $(cat /var/run/${NAME}.*.pid)
         do
                append ignore "${server}"
        done
 
        # get all running pids and kill client connections
        local skip
-       for pid in `pidof "${NAME}"`
+       for pid in $(pidof "${NAME}")
         do
                # check if correct program, otherwise process next pid
                grep -F -q -e "${PROG}" "/proc/${pid}/cmdline" || {
@@ -166,7 +278,7 @@ killclients()
                skip=0
                for server in ${ignore}
                 do
-                       if [ "${pid}" == "${server}" ]
+                       if [ "${pid}" = "${server}" ]
                         then
                                skip=1
                                break