dropbear: rewrite init script startup logic to handle both host key files
[openwrt/openwrt.git] / package / network / services / dropbear / files / dropbear.init
index 19aab3653bf7ba356c6ed8abe9c3157b37f1e08b..173ab0928528b09598b28553f2ac31040122623e 100755 (executable)
@@ -2,7 +2,7 @@
 # Copyright (C) 2006-2010 OpenWrt.org
 # Copyright (C) 2006 Carlos Sobrinho
 
-START=50
+START=19
 STOP=50
 
 USE_PROCD=1
@@ -12,29 +12,109 @@ PIDCOUNT=0
 EXTRA_COMMANDS="killclients"
 EXTRA_HELP="   killclients Kill ${NAME} processes except servers and yourself"
 
+_dropbearkey()
+{
+       /usr/bin/dropbearkey "$@" 0<&- 1>&- 2>&-
+}
+
+# $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 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 ifname="$1"
+       local ipaddrs="$1"
        local port="$2"
 
-       grep -qs "^ *$ifname:" /proc/net/dev || {
+       [ -z "$ipaddrs" ] && {
                procd_append_param command -p "$port"
                return
        }
 
-       for addr in $(
-               ifconfig "$ifname" | sed -ne '
-                       /addr: *fe[89ab][0-9a-f]:/d
-                       s/.* addr: *\([0-9a-f:\.]*\).*/\1/p
-               '
-       ); do
+       for addr in $ipaddrs; do
                procd_append_param command -p "$addr:$port"
        done
 }
 
 validate_section_dropbear()
 {
-       uci_validate_section dropbear dropbear "${1}" \
+       uci_load_validate dropbear dropbear "$1" "$2" \
                'PasswordAuth:bool:1' \
                'enable:bool:1' \
                'Interface:string' \
@@ -42,25 +122,34 @@ validate_section_dropbear()
                'RootPasswordAuth:bool:1' \
                'RootLogin:bool:1' \
                'rsakeyfile:file' \
-               'dsskeyfile:file' \
+               'keyfile:list(file)' \
                'BannerFile:file' \
-               'Port:list(port):22' \
+               'Port:port:22' \
                'SSHKeepAlive:uinteger:300' \
-               'IdleTimeout:uinteger:0'
-       return $?
+               'IdleTimeout:uinteger:0' \
+               'MaxAuthTries:uinteger:3' \
+               'RecvWindowSize:uinteger:0' \
+               'mdns:bool:1'
 }
 
 dropbear_instance()
 {
-       local PasswordAuth enable Interface GatewayPorts \
-               RootPasswordAuth RootLogin rsakeyfile \
-               dsskeyfile BannerFile Port
+       local ipaddrs
 
-       validate_section_dropbear "${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"
@@ -71,57 +160,73 @@ dropbear_instance()
        [ "${GatewayPorts}" -eq 1 ] && procd_append_param command -a
        [ "${RootPasswordAuth}" -eq 0 ] && procd_append_param command -g
        [ "${RootLogin}" -eq 0 ] && procd_append_param command -w
-       [ -n "${rsakeyfile}" ] && procd_append_param command -r "${rsakeyfile}"
-       [ -n "${dsskeyfile}" ] && procd_append_param command -d "${dsskeyfile}"
+       if [ -n "${rsakeyfile}" ]; then
+               logger -t ${NAME} -p daemon.warn \
+                       "option 'rsakeyfile' is considered to be deprecated and" \
+                       "will be removed in future releases, use 'keyfile' instead"
+               hk_config 'rsakeyfile' "${rsakeyfile}"
+       fi
+       config_list_foreach "$1" "keyfile" hk_config__keyfile
        [ -n "${BannerFile}" ] && procd_append_param command -b "${BannerFile}"
-       [ -n "${Interface}" ] && network_get_device Interface "${Interface}"
-       append_ports "${Interface}" "${Port}"
+       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()
 {
        local ignore=''
@@ -162,7 +267,7 @@ killclients()
                skip=0
                for server in ${ignore}
                 do
-                       if [ "${pid}" == "${server}" ]
+                       if [ "${pid}" = "${server}" ]
                         then
                                skip=1
                                break