diff options
-rw-r--r-- | Makefile.in | 2 | ||||
-rw-r--r-- | auth.h | 38 | ||||
-rw-r--r-- | debug.h | 5 | ||||
-rw-r--r-- | options.h | 5 | ||||
-rw-r--r-- | svr-agentfwd.c | 5 | ||||
-rw-r--r-- | svr-authpubkey.c | 109 | ||||
-rw-r--r-- | svr-authpubkeyoptions.c | 202 | ||||
-rw-r--r-- | svr-chansession.c | 22 | ||||
-rw-r--r-- | svr-main.c | 6 | ||||
-rw-r--r-- | svr-session.c | 3 | ||||
-rw-r--r-- | svr-tcpfwd.c | 5 | ||||
-rw-r--r-- | svr-x11fwd.c | 5 | ||||
-rw-r--r-- | tcpfwd.h | 1 |
13 files changed, 394 insertions, 14 deletions
diff --git a/Makefile.in b/Makefile.in index f5b111f..3e6c855 100644 --- a/Makefile.in +++ b/Makefile.in @@ -23,7 +23,7 @@ COMMONOBJS=dbutil.o buffer.o \ atomicio.o compat.o fake-rfc2553.o SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \ - svr-authpasswd.o svr-authpubkey.o svr-session.o svr-service.o \ + svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \ svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\ svr-tcpfwd.o svr-authpam.o @@ -26,6 +26,7 @@ #define _AUTH_H_ #include "includes.h" +#include "chansession.h" void svr_authinitialise(); void cli_authinitialise(); @@ -38,6 +39,25 @@ void svr_auth_password(); void svr_auth_pubkey(); void svr_auth_pam(); +#ifdef ENABLE_SVR_PUBKEY_OPTIONS +int svr_pubkey_allows_agentfwd(); +int svr_pubkey_allows_tcpfwd(); +int svr_pubkey_allows_x11fwd(); +int svr_pubkey_allows_pty(); +void svr_pubkey_set_forced_command(struct ChanSess *chansess); +void svr_pubkey_options_cleanup(); +int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filename); +#else +/* no option : success */ +#define svr_pubkey_allows_agentfwd() 1 +#define svr_pubkey_allows_tcpfwd() 1 +#define svr_pubkey_allows_x11fwd() 1 +#define svr_pubkey_allows_pty() 1 +static inline void svr_pubkey_set_forced_command(struct ChanSess *chansess) { } +static inline void svr_pubkey_options_cleanup() { } +#define svr_add_pubkey_options(x,y,z) DROPBEAR_SUCCESS +#endif + /* Client functions */ void recv_msg_userauth_failure(); void recv_msg_userauth_success(); @@ -97,6 +117,10 @@ struct AuthState { char *pw_shell; char *pw_name; char *pw_passwd; +#ifdef ENABLE_SVR_PUBKEY_OPTIONS + struct PubKeyOptions* pubkey_options; +#endif + }; struct SignKeyList; @@ -111,4 +135,18 @@ struct SignKeyList { }; +#ifdef ENABLE_SVR_PUBKEY_OPTIONS +struct PubKeyOptions; +struct PubKeyOptions { + /* Flags */ + int no_port_forwarding_flag; + int no_agent_forwarding_flag; + int no_x11_forwarding_flag; + int no_pty_flag; + /* "command=" option. */ + unsigned char * forced_command; + +}; +#endif + #endif /* _AUTH_H_ */ @@ -67,6 +67,11 @@ #define TRACE(X) #endif /*DEBUG_TRACE*/ +/* To debug with GDB it is easier to run with no forking of child processes. + You will need to pass "-F" as well. */ +/* #define DEBUG_NOFORK */ + + /* For testing as non-root on shadowed systems, include the crypt of a password * here. You can then log in as any user with this password. Ensure that you * make your own password, and are careful about using this. This will also @@ -144,6 +144,11 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */ #define ENABLE_SVR_PAM_AUTH /* requires ./configure --enable-pam */ #define ENABLE_SVR_PUBKEY_AUTH +/* Wether to ake public key options in authorized_keys file into account */ +#ifdef ENABLE_SVR_PUBKEY_AUTH +#define ENABLE_SVR_PUBKEY_OPTIONS +#endif + #define ENABLE_CLI_PASSWORD_AUTH #define ENABLE_CLI_PUBKEY_AUTH #define ENABLE_CLI_INTERACT_AUTH diff --git a/svr-agentfwd.c b/svr-agentfwd.c index 6946d1d..87a1078 100644 --- a/svr-agentfwd.c +++ b/svr-agentfwd.c @@ -39,6 +39,7 @@ #include "buffer.h" #include "random.h" #include "listener.h" +#include "auth.h" #define AGENTDIRPREFIX "/tmp/dropbear-" @@ -52,6 +53,10 @@ int agentreq(struct ChanSess * chansess) { int fd; + if (!svr_pubkey_allows_agentfwd()) { + return DROPBEAR_FAILURE; + } + if (chansess->agentlistener != NULL) { return DROPBEAR_FAILURE; } diff --git a/svr-authpubkey.c b/svr-authpubkey.c index 71477de..de72766 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -21,6 +21,37 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +/* + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This copyright and permission notice applies to the code parsing public keys + * options string which can also be found in OpenSSH auth2-pubkey.c file + * (user_key_allowed2). It has been adapted to work with buffers. + * + */ /* Process a pubkey auth request */ @@ -159,7 +190,9 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen, int ret = DROPBEAR_FAILURE; buffer * line = NULL; unsigned int len, pos; - + buffer * options_buf = NULL; + int line_num; + TRACE(("enter checkpubkey")) /* check that we can use the algo */ @@ -193,25 +226,82 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen, TRACE(("checkpubkey: opened authorized_keys OK")) line = buf_new(MAX_AUTHKEYS_LINE); + line_num = 0; /* iterate through the lines */ do { + /* new line : potentially new options */ + if (options_buf) { + buf_free(options_buf); + options_buf = NULL; + } if (buf_getline(line, authfile) == DROPBEAR_FAILURE) { /* EOF reached */ TRACE(("checkpubkey: authorized_keys EOF reached")) break; } + line_num++; if (line->len < MIN_AUTHKEYS_LINE) { TRACE(("checkpubkey: line too short")) continue; /* line is too short for it to be a valid key */ } - /* check the key type - this also stops us from using keys - * which have options with them */ + /* check the key type - will fail if there are options */ + TRACE(("a line!")) + if (strncmp(buf_getptr(line, algolen), algo, algolen) != 0) { - continue; + int is_comment = 0; + char *options_start = NULL; + int options_len = 0; + int escape, quoted; + + /* skip over any comments or leading whitespace */ + while (line->pos < line->len) { + const char c = buf_getbyte(line); + if (c == ' ' || c == '\t') { + continue; + } else if (c == '#') { + is_comment = 1; + break; + } + buf_incrpos(line, -1); + break; + } + if (is_comment) { + /* next line */ + continue; + } + + /* remember start of options */ + options_start = buf_getptr(line, 1); + quoted = 0; + escape = 0; + options_len = 0; + + /* figure out where the options are */ + while (line->pos < line->len) { + const char c = buf_getbyte(line); + if (!quoted && (c == ' ' || c == '\t')) { + break; + } + escape = (!escape && c == '\\'); + if (!escape && c == '"') { + quoted = !quoted; + } + options_len++; + } + options_buf = buf_new(options_len); + buf_putbytes(options_buf, options_start, options_len); + + /* compare the algorithm */ + if (line->pos + algolen > line->len) { + continue; + } + if (strncmp(buf_getptr(line, algolen), algo, algolen) != 0) { + continue; + } } buf_incrpos(line, algolen); @@ -232,6 +322,11 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen, TRACE(("checkpubkey: line pos = %d len = %d", line->pos, line->len)) ret = cmp_base64_key(keyblob, keybloblen, algo, algolen, line, NULL); + + if (ret == DROPBEAR_SUCCESS && options_buf) { + ret = svr_add_pubkey_options(options_buf, line_num, filename); + } + if (ret == DROPBEAR_SUCCESS) { break; } @@ -248,6 +343,9 @@ out: buf_free(line); } m_free(filename); + if (options_buf) { + buf_free(options_buf); + } TRACE(("leave checkpubkey: ret=%d", ret)) return ret; } @@ -343,5 +441,4 @@ static int checkfileperm(char * filename) { return DROPBEAR_SUCCESS; } - -#endif +#endif diff --git a/svr-authpubkeyoptions.c b/svr-authpubkeyoptions.c new file mode 100644 index 0000000..8a92d62 --- /dev/null +++ b/svr-authpubkeyoptions.c @@ -0,0 +1,202 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2008 Frederic Moulins + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * This copyright and permission notice applies to the code parsing public keys + * options string which can also be found in OpenSSH auth-options.c file + * (auth_parse_options). + * + */ + +/* Process pubkey options during a pubkey auth request */ +#include "includes.h" +#include "session.h" +#include "dbutil.h" +#include "signkey.h" +#include "auth.h" + +#ifdef ENABLE_SVR_PUBKEY_OPTIONS + +/* Returns 1 if pubkey allows agent forwarding, + * 0 otherwise */ +int svr_pubkey_allows_agentfwd() { + if (ses.authstate.pubkey_options + && ses.authstate.pubkey_options->no_agent_forwarding_flag) { + return 0; + } + return 1; +} + +/* Returns 1 if pubkey allows tcp forwarding, + * 0 otherwise */ +int svr_pubkey_allows_tcpfwd() { + if (ses.authstate.pubkey_options + && ses.authstate.pubkey_options->no_port_forwarding_flag) { + return 0; + } + return 1; +} + +/* Returns 1 if pubkey allows x11 forwarding, + * 0 otherwise */ +int svr_pubkey_allows_x11fwd() { + if (ses.authstate.pubkey_options + && ses.authstate.pubkey_options->no_x11_forwarding_flag) { + return 0; + } + return 1; +} + +/* Returns 1 if pubkey allows pty, 0 otherwise */ +int svr_pubkey_allows_pty() { + if (ses.authstate.pubkey_options + && ses.authstate.pubkey_options->no_pty_flag) { + return 0; + } + return 1; +} + +/* Set chansession command to the one forced by 'command' public key option */ +void svr_pubkey_set_forced_command(struct ChanSess *chansess) { + if (ses.authstate.pubkey_options) + chansess->cmd = ses.authstate.pubkey_options->forced_command; +} + +/* Free potential public key options */ +void svr_pubkey_options_cleanup() { + if (ses.authstate.pubkey_options) { + m_free(ses.authstate.pubkey_options); + ses.authstate.pubkey_options = NULL; + } +} + +/* helper for svr_add_pubkey_options. returns DROPBEAR_SUCCESS if the option is matched, + and increments the options_buf */ +static int match_option(buffer *options_buf, const char *opt_name) { + const int len = strlen(opt_name); + if (options_buf->len - options_buf->pos < len) { + return DROPBEAR_FAILURE; + } + if (strncasecmp(buf_getptr(options_buf, len), opt_name, len) == 0) { + buf_incrpos(options_buf, len); + return DROPBEAR_SUCCESS; + } + return DROPBEAR_FAILURE; +} + +/* Parse pubkey options and set ses.authstate.pubkey_options accordingly. + * Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */ +int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filename) { + int ret = DROPBEAR_FAILURE; + + TRACE(("enter addpubkeyoptions")) + + ses.authstate.pubkey_options = (struct PubKeyOptions*)m_malloc(sizeof( struct PubKeyOptions )); + memset(ses.authstate.pubkey_options, '\0', sizeof(*ses.authstate.pubkey_options)); + + buf_setpos(options_buf, 0); + while (options_buf->pos < options_buf->len) { + if (match_option(options_buf, "no-port-forwarding") == DROPBEAR_SUCCESS) { + dropbear_log(LOG_WARNING, "Port forwarding disabled."); + ses.authstate.pubkey_options->no_port_forwarding_flag = 1; + goto next_option; + } +#ifdef ENABLE_AGENTFWD + if (match_option(options_buf, "no-agent-forwarding") == DROPBEAR_SUCCESS) { + dropbear_log(LOG_WARNING, "Agent forwarding disabled."); + ses.authstate.pubkey_options->no_agent_forwarding_flag = 1; + goto next_option; + } +#endif +#ifdef ENABLE_X11FWD + if (match_option(options_buf, "no-X11-forwarding") == DROPBEAR_SUCCESS) { + dropbear_log(LOG_WARNING, "X11 forwarding disabled."); + ses.authstate.pubkey_options->no_x11_forwarding_flag = 1; + goto next_option; + } +#endif + if (match_option(options_buf, "no-pty") == DROPBEAR_SUCCESS) { + dropbear_log(LOG_WARNING, "Pty allocation disabled."); + ses.authstate.pubkey_options->no_pty_flag = 1; + goto next_option; + } + if (match_option(options_buf, "command=\"") == DROPBEAR_SUCCESS) { + int escaped = 0; + const unsigned char* command_start = buf_getptr(options_buf, 0); + while (options_buf->pos < options_buf->len) { + const char c = buf_getbyte(options_buf); + if (!escaped && c == '"') { + const int command_len = buf_getptr(options_buf, 0) - command_start; + ses.authstate.pubkey_options->forced_command = m_malloc(command_len); + memcpy(ses.authstate.pubkey_options->forced_command, + command_start, command_len-1); + ses.authstate.pubkey_options->forced_command[command_len-1] = '\0'; + dropbear_log(LOG_WARNING, "Forced command '%s'", + ses.authstate.pubkey_options->forced_command); + goto next_option; + } + escaped = (!escaped && c == '\\'); + } + dropbear_log(LOG_WARNING, "Badly formatted command= authorized_keys option"); + goto bad_option; + } + +next_option: + /* + * Skip the comma, and move to the next option + * (or break out if there are no more). + */ + if (options_buf->pos < options_buf->len + && buf_getbyte(options_buf) != ',') { + goto bad_option; + } + /* Process the next option. */ + } + /* parsed all options with no problem */ + ret = DROPBEAR_SUCCESS; + goto end; + +bad_option: + ret = DROPBEAR_FAILURE; + m_free(ses.authstate.pubkey_options); + ses.authstate.pubkey_options = NULL; + dropbear_log(LOG_WARNING, "Bad public key options at %s:%d", filename, line_num); + +end: + TRACE(("leave addpubkeyoptions")) + return ret; +} + +#endif diff --git a/svr-chansession.c b/svr-chansession.c index 060a235..9b2a412 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -37,6 +37,7 @@ #include "x11fwd.h" #include "agentfwd.h" #include "runopts.h" +#include "auth.h" /* Handles sessions (either shells or programs) requested by the client */ @@ -527,6 +528,12 @@ static int sessionpty(struct ChanSess * chansess) { struct passwd * pw = NULL; TRACE(("enter sessionpty")) + + if (!svr_pubkey_allows_pty()) { + TRACE(("leave sessionpty : pty forbidden by public key option")) + return DROPBEAR_FAILURE; + } + chansess->term = buf_getstring(ses.payload, &termlen); if (termlen > MAX_TERM_LEN) { /* TODO send disconnect ? */ @@ -582,14 +589,19 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, return DROPBEAR_FAILURE; } + /* take public key option 'command' into account */ + svr_pubkey_set_forced_command(chansess); + if (iscmd) { /* "exec" */ - chansess->cmd = buf_getstring(ses.payload, &cmdlen); + if (chansess->cmd == NULL) { + chansess->cmd = buf_getstring(ses.payload, &cmdlen); - if (cmdlen > MAX_CMD_LEN) { - m_free(chansess->cmd); - /* TODO - send error - too long ? */ - return DROPBEAR_FAILURE; + if (cmdlen > MAX_CMD_LEN) { + m_free(chansess->cmd); + /* TODO - send error - too long ? */ + return DROPBEAR_FAILURE; + } } if (issubsys) { #ifdef SFTPSERVER_PATH @@ -266,7 +266,11 @@ void main_noinetd() { goto out; } +#ifdef DEBUG_NOFORK + fork_ret = 0; +#else fork_ret = fork(); +#endif if (fork_ret < 0) { dropbear_log(LOG_WARNING, "error forking: %s", strerror(errno)); goto out; @@ -292,9 +296,11 @@ void main_noinetd() { addrstring = getaddrstring(&remoteaddr, 1); dropbear_log(LOG_INFO, "Child connection from %s", addrstring); +#ifndef DEBUG_NOFORK if (setsid() < 0) { dropbear_exit("setsid: %s", strerror(errno)); } +#endif /* make sure we close sockets */ for (i = 0; i < listensockcount; i++) { diff --git a/svr-session.c b/svr-session.c index 658bc6e..eaccfe5 100644 --- a/svr-session.c +++ b/svr-session.c @@ -144,6 +144,9 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) { _dropbear_log(LOG_INFO, fmtbuf, param); + /* free potential public key options */ + svr_pubkey_options_cleanup(); + /* must be after we've done with username etc */ common_session_cleanup(); diff --git a/svr-tcpfwd.c b/svr-tcpfwd.c index d4dca6b..a55361b 100644 --- a/svr-tcpfwd.c +++ b/svr-tcpfwd.c @@ -32,6 +32,7 @@ #include "packet.h" #include "listener.h" #include "runopts.h" +#include "auth.h" #ifdef ENABLE_SVR_REMOTETCPFWD @@ -72,7 +73,7 @@ void recv_msg_global_request_remotetcp() { TRACE(("enter recv_msg_global_request_remotetcp")) - if (svr_opts.noremotetcp) { + if (svr_opts.noremotetcp || !svr_pubkey_allows_tcpfwd()) { TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled")) goto out; } @@ -236,7 +237,7 @@ static int newtcpdirect(struct Channel * channel) { int len; int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; - if (svr_opts.nolocaltcp) { + if (svr_opts.nolocaltcp || !svr_pubkey_allows_tcpfwd()) { TRACE(("leave newtcpdirect: local tcp forwarding disabled")) goto out; } diff --git a/svr-x11fwd.c b/svr-x11fwd.c index cbc8a79..7618e3b 100644 --- a/svr-x11fwd.c +++ b/svr-x11fwd.c @@ -33,6 +33,7 @@ #include "channel.h" #include "packet.h" #include "buffer.h" +#include "auth.h" #define X11BASEPORT 6000 #define X11BINDBASE 6010 @@ -47,6 +48,10 @@ int x11req(struct ChanSess * chansess) { int fd; + if (!svr_pubkey_allows_x11fwd()) { + return DROPBEAR_FAILURE; + } + /* we already have an x11 connection */ if (chansess->x11listener != NULL) { return DROPBEAR_FAILURE; @@ -55,6 +55,7 @@ struct TCPFwdList { /* Server */ void recv_msg_global_request_remotetcp(); + extern const struct ChanType svr_chan_tcpdirect; /* Client */ |