From c0ce2a6a97af66881675916c504af9caed2f9c2e Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 8 Sep 2008 15:14:02 +0000 Subject: * Patch from Frédéric Moulins adding options to authorized_keys. Needs review. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --HG-- branch : pubkey-options extra : convert_revision : 26872f944d79ddacff1070aab32115a6d726392c --- Makefile.in | 2 +- auth.h | 38 +++++++++ options.h | 5 ++ svr-agentfwd.c | 5 ++ svr-authpubkey.c | 81 ++++++++++++++++-- svr-authpubkeyoptions.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++++ svr-chansession.c | 22 +++-- svr-session.c | 3 + svr-tcpfwd.c | 5 +- svr-x11fwd.c | 5 ++ tcpfwd.h | 1 + 11 files changed, 375 insertions(+), 15 deletions(-) create mode 100644 svr-authpubkeyoptions.c 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 diff --git a/auth.h b/auth.h index b8e55bb..d6a50a4 100644 --- a/auth.h +++ b/auth.h @@ -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(const char* opts); +#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) 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_ */ diff --git a/options.h b/options.h index 5385d30..7b74829 100644 --- a/options.h +++ b/options.h @@ -136,6 +136,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..0dc7383 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 */ @@ -158,8 +189,9 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen, char * filename = NULL; int ret = DROPBEAR_FAILURE; buffer * line = NULL; - unsigned int len, pos; - + unsigned int len, pos, quoted; + const char *options = NULL; + TRACE(("enter checkpubkey")) /* check that we can use the algo */ @@ -196,6 +228,8 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen, /* iterate through the lines */ do { + /* new line : potentially new options */ + options = NULL; if (buf_getline(line, authfile) == DROPBEAR_FAILURE) { /* EOF reached */ @@ -208,10 +242,39 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen, 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 */ if (strncmp(buf_getptr(line, algolen), algo, algolen) != 0) { - continue; + /* there may be options or a commented line */ + if ('#' == line->data[line->pos]) continue; + /* no comment, skip to next space character */ + len = 0; + pos = line->pos; + options = buf_getptr(line, 1); + quoted = 0; + while (line->data[pos] + && (quoted || (line->data[pos] != ' ' + && line->data[pos] != '\t' + && line->data[pos] != '\n' + && line->data[pos] != '\r'))) { + pos++; + if (line->data[pos] == '\\' + && line->data[pos+1] == '"') { + pos++; /* skip both */ + } else if (line->data[pos] == '"') + quoted = !quoted; + } /* line->data[pos] == ['\0'|' '|'\t'] */ + + /* skip line if there is nothing left */ + if (pos >= line->len) continue; + /* skip line if it begins with a space or tab character */ + if (pos == line->pos) continue; + /* set the position of the line after what we have read */ + buf_setpos(line, pos+1); + /* give a second chance to the algo */ + if (line->pos + algolen > line->len) continue; + if (strncmp(buf_getptr(line, algolen), algo, algolen) != 0) { + continue; + } } buf_incrpos(line, algolen); @@ -232,6 +295,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) { + ret = svr_add_pubkey_options(options); + } + if (ret == DROPBEAR_SUCCESS) { break; } @@ -343,5 +411,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..aca1c2d --- /dev/null +++ b/svr-authpubkeyoptions.c @@ -0,0 +1,223 @@ +/* + * 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 + * Copyright (c) 1995 Tatu Ylonen , 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; + } +} + +/* 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(const char* opts) { + const char *cp; + int i; + int ret = DROPBEAR_FAILURE; + + TRACE(("enter addpubkeyoptions")) + + if (!opts || *opts == ' ') { + /* no option, success */ + ret = DROPBEAR_SUCCESS; + goto end; + } + + ses.authstate.pubkey_options = (struct PubKeyOptions*)m_malloc(sizeof( struct PubKeyOptions )); + + while (*opts && *opts != ' ' && *opts != '\t') { + cp = "no-port-forwarding"; + if (strncasecmp(opts, cp, strlen(cp)) == 0) { + dropbear_log(LOG_WARNING, "Port forwarding disabled."); + ses.authstate.pubkey_options->no_port_forwarding_flag = 1; + opts += strlen(cp); + goto next_option; + } +#ifdef ENABLE_AGENTFWD + cp = "no-agent-forwarding"; + if (strncasecmp(opts, cp, strlen(cp)) == 0) { + dropbear_log(LOG_WARNING, "Agent forwarding disabled."); + ses.authstate.pubkey_options->no_agent_forwarding_flag = 1; + opts += strlen(cp); + goto next_option; + } +#endif +#ifdef ENABLE_X11FWD + cp = "no-X11-forwarding"; + if (strncasecmp(opts, cp, strlen(cp)) == 0) { + dropbear_log(LOG_WARNING, "X11 forwarding disabled."); + ses.authstate.pubkey_options->no_x11_forwarding_flag = 1; + opts += strlen(cp); + goto next_option; + } +#endif + cp = "no-pty"; + if (strncasecmp(opts, cp, strlen(cp)) == 0) { + dropbear_log(LOG_WARNING, "Pty allocation disabled."); + ses.authstate.pubkey_options->no_pty_flag = 1; + opts += strlen(cp); + goto next_option; + } + cp = "command=\""; + if (strncasecmp(opts, cp, strlen(cp)) == 0) { + opts += strlen(cp); + ses.authstate.pubkey_options->forced_command = (char*)m_malloc(strlen(opts) + 1); + i = 0; + while (*opts) { + if (*opts == '"') + break; + if (*opts == '\\' && opts[1] == '"') { + opts += 2; + ses.authstate.pubkey_options->forced_command[i++] = '"'; + continue; + } + ses.authstate.pubkey_options->forced_command[i++] = *opts++; + } + if (!*opts) { + dropbear_log(LOG_WARNING, + "Missing end quote in public key command option"); + m_free(ses.authstate.pubkey_options->forced_command); + ses.authstate.pubkey_options->forced_command = NULL; + goto bad_option; + } + ses.authstate.pubkey_options->forced_command[i] = '\0'; + if (strlen(ses.authstate.pubkey_options->forced_command) > MAX_CMD_LEN) { + dropbear_log(LOG_WARNING, + "Public key option command too long (>MAX_CMD_LEN)."); + m_free(ses.authstate.pubkey_options->forced_command); + ses.authstate.pubkey_options->forced_command = NULL; + goto bad_option; + } + dropbear_log(LOG_WARNING, "Forced command '%s'", + ses.authstate.pubkey_options->forced_command); + opts++; + goto next_option; + } + next_option: + /* + * Skip the comma, and move to the next option + * (or break out if there are no more). + */ + if (!*opts) { + TRACE(("Bugs in svr-chansession.c pubkey option processing.")) + } + if (*opts == ' ' || *opts == '\t') { + break; /* End of options. */ + } + if (*opts != ',') { + goto bad_option; + } + opts++; + /* 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 : '%.50s'", opts); + +end: + TRACE(("leave addpubkeyoptions")) + return ret; + +} + +#endif diff --git a/svr-chansession.c b/svr-chansession.c index 7cedb8e..66cbabb 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 diff --git a/svr-session.c b/svr-session.c index 5a8364a..21d366e 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; diff --git a/tcpfwd.h b/tcpfwd.h index 28af029..f50515c 100644 --- a/tcpfwd.h +++ b/tcpfwd.h @@ -55,6 +55,7 @@ struct TCPFwdList { /* Server */ void recv_msg_global_request_remotetcp(); + extern const struct ChanType svr_chan_tcpdirect; /* Client */ -- cgit v1.2.3 From 31fa5e605b6d42d670945c1821caf8b887623fa1 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 12 Sep 2008 17:23:56 +0000 Subject: - Rework pubkey options to be more careful about buffer lengths. Needs review. --HG-- branch : pubkey-options extra : convert_revision : 537a6ebebb46424b967ffe787f0f8560e5f447e8 --- auth.h | 4 +- debug.h | 5 +++ svr-authpubkey.c | 96 +++++++++++++++++++++++++++--------------- svr-authpubkeyoptions.c | 109 +++++++++++++++++++----------------------------- svr-main.c | 6 +++ 5 files changed, 120 insertions(+), 100 deletions(-) diff --git a/auth.h b/auth.h index d6a50a4..83a2f8e 100644 --- a/auth.h +++ b/auth.h @@ -46,7 +46,7 @@ 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(const char* opts); +int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filename); #else /* no option : success */ #define svr_pubkey_allows_agentfwd() 1 @@ -55,7 +55,7 @@ int svr_add_pubkey_options(const char* opts); #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) DROPBEAR_SUCCESS +#define svr_add_pubkey_options(x,y,z) DROPBEAR_SUCCESS #endif /* Client functions */ diff --git a/debug.h b/debug.h index 175f3fc..b8c2a57 100644 --- a/debug.h +++ b/debug.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 diff --git a/svr-authpubkey.c b/svr-authpubkey.c index 0dc7383..de72766 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -189,8 +189,9 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen, char * filename = NULL; int ret = DROPBEAR_FAILURE; buffer * line = NULL; - unsigned int len, pos, quoted; - const char *options = NULL; + unsigned int len, pos; + buffer * options_buf = NULL; + int line_num; TRACE(("enter checkpubkey")) @@ -225,17 +226,22 @@ 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 */ - options = NULL; + 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")) @@ -243,38 +249,59 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen, } /* check the key type - will fail if there are options */ + TRACE(("a line!")) + if (strncmp(buf_getptr(line, algolen), algo, algolen) != 0) { - /* there may be options or a commented line */ - if ('#' == line->data[line->pos]) continue; - /* no comment, skip to next space character */ - len = 0; - pos = line->pos; - options = buf_getptr(line, 1); + 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; - while (line->data[pos] - && (quoted || (line->data[pos] != ' ' - && line->data[pos] != '\t' - && line->data[pos] != '\n' - && line->data[pos] != '\r'))) { - pos++; - if (line->data[pos] == '\\' - && line->data[pos+1] == '"') { - pos++; /* skip both */ - } else if (line->data[pos] == '"') - quoted = !quoted; - } /* line->data[pos] == ['\0'|' '|'\t'] */ - - /* skip line if there is nothing left */ - if (pos >= line->len) continue; - /* skip line if it begins with a space or tab character */ - if (pos == line->pos) continue; - /* set the position of the line after what we have read */ - buf_setpos(line, pos+1); - /* give a second chance to the algo */ - if (line->pos + algolen > line->len) continue; + 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); @@ -296,8 +323,8 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen, ret = cmp_base64_key(keyblob, keybloblen, algo, algolen, line, NULL); - if (ret == DROPBEAR_SUCCESS) { - ret = svr_add_pubkey_options(options); + if (ret == DROPBEAR_SUCCESS && options_buf) { + ret = svr_add_pubkey_options(options_buf, line_num, filename); } if (ret == DROPBEAR_SUCCESS) { @@ -316,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; } diff --git a/svr-authpubkeyoptions.c b/svr-authpubkeyoptions.c index aca1c2d..8a92d62 100644 --- a/svr-authpubkeyoptions.c +++ b/svr-authpubkeyoptions.c @@ -102,106 +102,86 @@ void svr_pubkey_options_cleanup() { } } +/* 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(const char* opts) { - const char *cp; - int i; +int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filename) { int ret = DROPBEAR_FAILURE; TRACE(("enter addpubkeyoptions")) - if (!opts || *opts == ' ') { - /* no option, success */ - ret = DROPBEAR_SUCCESS; - goto end; - } - ses.authstate.pubkey_options = (struct PubKeyOptions*)m_malloc(sizeof( struct PubKeyOptions )); + memset(ses.authstate.pubkey_options, '\0', sizeof(*ses.authstate.pubkey_options)); - while (*opts && *opts != ' ' && *opts != '\t') { - cp = "no-port-forwarding"; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { + 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; - opts += strlen(cp); goto next_option; } #ifdef ENABLE_AGENTFWD - cp = "no-agent-forwarding"; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { + 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; - opts += strlen(cp); goto next_option; } #endif #ifdef ENABLE_X11FWD - cp = "no-X11-forwarding"; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { + 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; - opts += strlen(cp); goto next_option; } #endif - cp = "no-pty"; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { + if (match_option(options_buf, "no-pty") == DROPBEAR_SUCCESS) { dropbear_log(LOG_WARNING, "Pty allocation disabled."); ses.authstate.pubkey_options->no_pty_flag = 1; - opts += strlen(cp); goto next_option; } - cp = "command=\""; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { - opts += strlen(cp); - ses.authstate.pubkey_options->forced_command = (char*)m_malloc(strlen(opts) + 1); - i = 0; - while (*opts) { - if (*opts == '"') - break; - if (*opts == '\\' && opts[1] == '"') { - opts += 2; - ses.authstate.pubkey_options->forced_command[i++] = '"'; - continue; + 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; } - ses.authstate.pubkey_options->forced_command[i++] = *opts++; - } - if (!*opts) { - dropbear_log(LOG_WARNING, - "Missing end quote in public key command option"); - m_free(ses.authstate.pubkey_options->forced_command); - ses.authstate.pubkey_options->forced_command = NULL; - goto bad_option; - } - ses.authstate.pubkey_options->forced_command[i] = '\0'; - if (strlen(ses.authstate.pubkey_options->forced_command) > MAX_CMD_LEN) { - dropbear_log(LOG_WARNING, - "Public key option command too long (>MAX_CMD_LEN)."); - m_free(ses.authstate.pubkey_options->forced_command); - ses.authstate.pubkey_options->forced_command = NULL; - goto bad_option; + escaped = (!escaped && c == '\\'); } - dropbear_log(LOG_WARNING, "Forced command '%s'", - ses.authstate.pubkey_options->forced_command); - opts++; - goto next_option; + dropbear_log(LOG_WARNING, "Badly formatted command= authorized_keys option"); + goto bad_option; } - next_option: + +next_option: /* * Skip the comma, and move to the next option * (or break out if there are no more). */ - if (!*opts) { - TRACE(("Bugs in svr-chansession.c pubkey option processing.")) - } - if (*opts == ' ' || *opts == '\t') { - break; /* End of options. */ - } - if (*opts != ',') { + if (options_buf->pos < options_buf->len + && buf_getbyte(options_buf) != ',') { goto bad_option; } - opts++; /* Process the next option. */ } /* parsed all options with no problem */ @@ -212,12 +192,11 @@ bad_option: ret = DROPBEAR_FAILURE; m_free(ses.authstate.pubkey_options); ses.authstate.pubkey_options = NULL; - dropbear_log(LOG_WARNING, "Bad public key options : '%.50s'", opts); + 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-main.c b/svr-main.c index f7ce221..8d57084 100644 --- a/svr-main.c +++ b/svr-main.c @@ -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++) { -- cgit v1.2.3