diff options
-rw-r--r-- | Makefile.in | 2 | ||||
-rw-r--r-- | auth.h | 38 | ||||
-rw-r--r-- | options.h | 5 | ||||
-rw-r--r-- | svr-agentfwd.c | 5 | ||||
-rw-r--r-- | svr-authpubkey.c | 81 | ||||
-rw-r--r-- | svr-authpubkeyoptions.c | 223 | ||||
-rw-r--r-- | svr-chansession.c | 22 | ||||
-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 |
11 files changed, 375 insertions, 15 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(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_ */ @@ -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 <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; + } +} + +/* 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; @@ -55,6 +55,7 @@ struct TCPFwdList { /* Server */ void recv_msg_global_request_remotetcp(); + extern const struct ChanType svr_chan_tcpdirect; /* Client */ |