diff --git a/dropbear.8 b/dropbear.8 index be733726b..ec5aa4025 100644 --- a/dropbear.8 +++ b/dropbear.8 @@ -29,6 +29,9 @@ or automatically with the '-R' option. See "Host Key Files" below. .B \-R Generate hostkeys automatically. See "Host Key Files" below. .TP +.B \-U \fIauthorized_keys +Path to file containing authorized user keys inside the user home directory. Defaults to '.ssh/authorized_keys'. +.TP .B \-F Don't fork into background. .TP diff --git a/runopts.h b/runopts.h index cecdc228e..25490c983 100644 --- a/runopts.h +++ b/runopts.h @@ -116,6 +116,8 @@ typedef struct svr_runopts { char * forced_command; + char * authkeysfile; + } svr_runopts; extern svr_runopts svr_opts; diff --git a/svr-authpubkey.c b/svr-authpubkey.c index 90d0d2cc9..71b1601eb 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -64,6 +64,7 @@ #include "ssh.h" #include "packet.h" #include "algo.h" +#include "runopts.h" #if DROPBEAR_SVR_PUBKEY_AUTH @@ -72,7 +73,7 @@ static int checkpubkey(char* algo, unsigned int algolen, unsigned char* keyblob, unsigned int keybloblen); -static int checkpubkeyperms(void); +static int checkpubkeyperms(char *filename, char *base); static void send_msg_userauth_pk_ok(char* algo, unsigned int algolen, unsigned char* keyblob, unsigned int keybloblen); static int checkfileperm(char * filename); @@ -196,6 +197,7 @@ static int checkpubkey(char* algo, unsigned int algolen, FILE * authfile = NULL; char * filename = NULL; + char * relfilename = NULL; int ret = DROPBEAR_FAILURE; buffer * line = NULL; unsigned int len, pos; @@ -212,21 +214,23 @@ static int checkpubkey(char* algo, unsigned int algolen, goto out; } - /* check file permissions, also whether file exists */ - if (checkpubkeyperms() == DROPBEAR_FAILURE) { + relfilename = ( svr_opts.authkeysfile + ? svr_opts.authkeysfile + : ".ssh/authorized_keys" ); + if (relfilename[0] == '/') { /* name is absolute */ + filename = m_strdup(relfilename); + } + else { + len = strlen(ses.authstate.pw_dir) + strlen(relfilename) + 2; + filename = m_malloc(len); + snprintf(filename, len, "%s/%s", ses.authstate.pw_dir, relfilename); + } + + if (checkpubkeyperms(filename, ses.authstate.pw_dir) != DROPBEAR_SUCCESS) { TRACE(("bad authorized_keys permissions, or file doesn't exist")) goto out; } - /* we don't need to check pw and pw_dir for validity, since - * its been done in checkpubkeyperms. */ - len = strlen(ses.authstate.pw_dir); - /* allocate max required pathname storage, - * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */ - filename = m_malloc(len + 22); - snprintf(filename, len + 22, "%s/.ssh/authorized_keys", - ses.authstate.pw_dir); - /* open the file */ authfile = fopen(filename, "r"); if (authfile == NULL) { @@ -362,52 +366,47 @@ static int checkpubkey(char* algo, unsigned int algolen, /* Returns DROPBEAR_SUCCESS if file permissions for pubkeys are ok, * DROPBEAR_FAILURE otherwise. - * Checks that the user's homedir, ~/.ssh, and - * ~/.ssh/authorized_keys are all owned by either root or the user, and are + * Checks filename and its parent directories recursively until the + * base directory (usually ~/) or one of its ancestors (up to /) is + * reached. + * The files and directories must be all owned by root or the user, and be * g-w, o-w */ -static int checkpubkeyperms() { - - char* filename = NULL; +static int checkpubkeyperms(char *filename, char *base) { + char* path = NULL; int ret = DROPBEAR_FAILURE; unsigned int len; - TRACE(("enter checkpubkeyperms")) + TRACE(("enter checkpubkeyperms(%s, %s)", filename, base)) - if (ses.authstate.pw_dir == NULL) { + if ((base == NULL) || (base[0] != '/') || + (filename == NULL) || (filename[0] != '/')) { + /* both filename and base must be absolute paths */ goto out; } - if ((len = strlen(ses.authstate.pw_dir)) == 0) { - goto out; - } - - /* allocate max required pathname storage, - * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */ - filename = m_malloc(len + 22); - strncpy(filename, ses.authstate.pw_dir, len+1); - - /* check ~ */ - if (checkfileperm(filename) != DROPBEAR_SUCCESS) { - goto out; - } + len = strlen(filename); + path = m_strdup(filename); - /* check ~/.ssh */ - strncat(filename, "/.ssh", 5); /* strlen("/.ssh") == 5 */ - if (checkfileperm(filename) != DROPBEAR_SUCCESS) { - goto out; - } + while (checkfileperm(len ? path : "/") == DROPBEAR_SUCCESS) { + /* check if we are on base trail and if this is the + * case, return success */ + if ((strncmp(base, path, len) == 0) && + (!len || (base[len] == '\0') || (base[len] == '/'))) { + ret = DROPBEAR_SUCCESS; + break; + } - /* now check ~/.ssh/authorized_keys */ - strncat(filename, "/authorized_keys", 16); - if (checkfileperm(filename) != DROPBEAR_SUCCESS) { - goto out; + /* look for parent directory */ + while (--len) { + if (path[len] == '/') { + path[len] = '\0'; + break; + } + } } - /* file looks ok, return success */ - ret = DROPBEAR_SUCCESS; - out: - m_free(filename); + m_free(path); TRACE(("leave checkpubkeyperms")) return ret; @@ -433,7 +432,8 @@ static int checkfileperm(char * filename) { TRACE(("wrong ownership")) } /* check permissions - don't want group or others +w */ - if (filestat.st_mode & (S_IWGRP | S_IWOTH)) { + if ((filestat.st_mode & (S_IWGRP | S_IWOTH)) && + !(S_ISDIR(filestat.st_mode) && (filestat.st_mode & S_ISVTX))) { badperm = 1; TRACE(("wrong perms")) } diff --git a/svr-runopts.c b/svr-runopts.c index e6dc8a8fd..0c980cdd8 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -120,6 +120,7 @@ void svr_getopts(int argc, char ** argv) { char* keepalive_arg = NULL; char* idle_timeout_arg = NULL; char* keyfile = NULL; + char* authkeysfile = NULL; char c; @@ -137,6 +138,8 @@ void svr_getopts(int argc, char ** argv) { svr_opts.hostkey = NULL; svr_opts.delay_hostkey = 0; svr_opts.pidfile = DROPBEAR_PIDFILE; + svr_opts.authkeysfile = NULL; + #if DROPBEAR_SVR_LOCALTCPFWD svr_opts.nolocaltcp = 0; #endif @@ -253,6 +256,9 @@ void svr_getopts(int argc, char ** argv) { case 'u': /* backwards compatibility with old urandom option */ break; + case 'U': + next = &authkeysfile; + break; #if DEBUG_TRACE case 'v': debug_trace = 1; @@ -295,6 +301,10 @@ void svr_getopts(int argc, char ** argv) { addhostkey(keyfile); keyfile = NULL; } + if (authkeysfile) { + svr_opts.authkeysfile = m_strdup(authkeysfile); + authkeysfile = NULL; + } } }