Skip to content

Commit f9c9ced

Browse files
committed
Allow specifying authorized_keys directory.
This adds a "-D" option to dropbear which allow specifying the directory path where authorized_keys is located. This will allow, for example running dropbear for interop testing during OpenSSH regression tests without impacting the running user's authorization files.
1 parent 155639d commit f9c9ced

File tree

5 files changed

+75
-67
lines changed

5 files changed

+75
-67
lines changed

src/dbutil.c

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -637,30 +637,32 @@ int m_str_to_uint(const char* str, unsigned int *val) {
637637
}
638638
}
639639

640-
/* Returns malloced path. inpath beginning with '~/' expanded,
641-
otherwise returned as-is */
640+
/* Returns malloced path from inpath, possibly expanding '~/'
641+
into the specified home directory.*/
642+
char * expand_homedir_path_home(const char *inpath, const char *homedir) {
643+
if (strncmp(inpath, "~/", 2) == 0 && homedir) {
644+
size_t len = strlen(inpath)-2 + strlen(homedir) + 2;
645+
char *buf = m_malloc(len);
646+
snprintf(buf, len, "%s/%s", homedir, inpath+2);
647+
return buf;
648+
}
649+
/* Fallback */
650+
return m_strdup(inpath);
651+
}
652+
653+
/* Returns malloced path from inpath, possibly expanding '~/'
654+
into the current user's home directory.*/
642655
char * expand_homedir_path(const char *inpath) {
643656
struct passwd *pw = NULL;
644-
if (strncmp(inpath, "~/", 2) == 0) {
645-
char *homedir = getenv("HOME");
657+
char *homedir = getenv("HOME");
646658

647-
if (!homedir) {
648-
pw = getpwuid(getuid());
649-
if (pw) {
650-
homedir = pw->pw_dir;
651-
}
652-
}
653-
654-
if (homedir) {
655-
int len = strlen(inpath)-2 + strlen(homedir) + 2;
656-
char *buf = m_malloc(len);
657-
snprintf(buf, len, "%s/%s", homedir, inpath+2);
658-
return buf;
659+
if (!homedir) {
660+
pw = getpwuid(getuid());
661+
if (pw) {
662+
homedir = pw->pw_dir;
659663
}
660664
}
661-
662-
/* Fallback */
663-
return m_strdup(inpath);
665+
return expand_homedir_path_home(inpath, homedir);
664666
}
665667

666668
int constant_time_memcmp(const void* a, const void *b, size_t n)

src/dbutil.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ time_t monotonic_now(void);
9292
void gettime_wrapper(struct timespec *now);
9393

9494
char * expand_homedir_path(const char *inpath);
95+
char * expand_homedir_path_home(const char *inpath, const char *homedir);
9596

9697
void fsync_parent_dir(const char* fn);
9798

src/runopts.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ typedef struct svr_runopts {
127127
buffer * banner;
128128
char * pidfile;
129129

130+
char * authorized_keys_dir;
131+
130132
char * forced_command;
131133
char* interface;
132134

src/svr-authpubkey.c

Lines changed: 42 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
#define MIN_AUTHKEYS_LINE 10 /* "ssh-rsa AB" - short but doesn't matter */
7272
#define MAX_AUTHKEYS_LINE 4200 /* max length of a line in authkeys */
7373

74+
static char * authorized_keys_filepath(void);
7475
static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
7576
const unsigned char* keyblob, unsigned int keybloblen);
7677
static int checkpubkeyperms(void);
@@ -431,6 +432,24 @@ static int checkpubkey_line(buffer* line, int line_num, const char* filename,
431432
return ret;
432433
}
433434

435+
/* Returns the full path to the user's authorized_keys file in an
436+
* allocated string which caller must free. */
437+
static char *authorized_keys_filepath() {
438+
size_t len = 0;
439+
char *pathname = NULL, *dir = NULL;
440+
const char *filename = "authorized_keys";
441+
442+
dir = expand_homedir_path_home(svr_opts.authorized_keys_dir,
443+
ses.authstate.pw_dir);
444+
445+
/* allocate max required pathname storage,
446+
* = dir + "/" + "authorized_keys" + '\0' */;
447+
len = strlen(dir) + strlen(filename) + 2;
448+
pathname = m_malloc(len);
449+
snprintf(pathname, len, "%s/%s", dir, filename);
450+
m_free(dir);
451+
return pathname;
452+
}
434453

435454
/* Checks whether a specified publickey (and associated algorithm) is an
436455
* acceptable key for authentication */
@@ -442,7 +461,6 @@ static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
442461
char * filename = NULL;
443462
int ret = DROPBEAR_FAILURE;
444463
buffer * line = NULL;
445-
unsigned int len;
446464
int line_num;
447465
uid_t origuid;
448466
gid_t origgid;
@@ -464,13 +482,7 @@ static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
464482
} else {
465483
/* we don't need to check pw and pw_dir for validity, since
466484
* its been done in checkpubkeyperms. */
467-
len = strlen(ses.authstate.pw_dir);
468-
/* allocate max required pathname storage,
469-
* = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
470-
filename = m_malloc(len + 22);
471-
snprintf(filename, len + 22, "%s/.ssh/authorized_keys",
472-
ses.authstate.pw_dir);
473-
485+
filename = authorized_keys_filepath();
474486
authfile = fopen(filename, "r");
475487
if (!authfile) {
476488
TRACE(("checkpubkey: failed opening %s: %s", filename, strerror(errno)))
@@ -524,53 +536,35 @@ static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
524536

525537
/* Returns DROPBEAR_SUCCESS if file permissions for pubkeys are ok,
526538
* DROPBEAR_FAILURE otherwise.
527-
* Checks that the user's homedir, ~/.ssh, and
528-
* ~/.ssh/authorized_keys are all owned by either root or the user, and are
529-
* g-w, o-w */
539+
* Checks that the authorized_keys path permissions are all owned by either
540+
* root or the user, and are g-w, o-w.
541+
* When this path is inside the user's home dir it checks up to and including
542+
* the home dir, otherwise it checks every path component. */
530543
static int checkpubkeyperms() {
531-
532-
char* filename = NULL;
533-
int ret = DROPBEAR_FAILURE;
534-
unsigned int len;
544+
char *path = authorized_keys_filepath(), *sep = NULL;
545+
int ret = DROPBEAR_SUCCESS;
535546

536547
TRACE(("enter checkpubkeyperms"))
537548

538-
if (ses.authstate.pw_dir == NULL) {
539-
goto out;
540-
}
541-
542-
if ((len = strlen(ses.authstate.pw_dir)) == 0) {
543-
goto out;
544-
}
545-
546-
/* allocate max required pathname storage,
547-
* = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
548-
len += 22;
549-
filename = m_malloc(len);
550-
strlcpy(filename, ses.authstate.pw_dir, len);
551-
552-
/* check ~ */
553-
if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
554-
goto out;
555-
}
556-
557-
/* check ~/.ssh */
558-
strlcat(filename, "/.ssh", len);
559-
if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
560-
goto out;
561-
}
562-
563-
/* now check ~/.ssh/authorized_keys */
564-
strlcat(filename, "/authorized_keys", len);
565-
if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
566-
goto out;
549+
/* Walk back up path checking permissions, stopping at either homedir,
550+
* or root if the path is outside of the homedir. */
551+
while ((sep = strrchr(path, '/')) != NULL) {
552+
if (sep == path) { /* root directory */
553+
sep++;
554+
}
555+
*sep = '\0';
556+
if (checkfileperm(path) != DROPBEAR_SUCCESS) {
557+
TRACE(("checkpubkeyperms: bad perm on %s", path))
558+
ret = DROPBEAR_FAILURE;
559+
}
560+
if (strcmp(path, ses.authstate.pw_dir) == 0 || strcmp(path, "/") == 0) {
561+
break;
562+
}
567563
}
568564

569-
/* file looks ok, return success */
570-
ret = DROPBEAR_SUCCESS;
571-
565+
/* all looks ok, return success */
572566
out:
573-
m_free(filename);
567+
m_free(path);
574568

575569
TRACE(("leave checkpubkeyperms"))
576570
return ret;

src/svr-runopts.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ static void printhelp(const char * progname) {
6161
#if DROPBEAR_ED25519
6262
" - ed25519 %s\n"
6363
#endif
64+
#if DROPBEAR_SVR_PUBKEY_AUTH
65+
"-D Directory containing authorized_keys file\n"
66+
#endif
6467
#if DROPBEAR_DELAY_HOSTKEY
6568
"-R Create hostkeys as required\n"
6669
#endif
@@ -173,6 +176,7 @@ void svr_getopts(int argc, char ** argv) {
173176
svr_opts.hostkey = NULL;
174177
svr_opts.delay_hostkey = 0;
175178
svr_opts.pidfile = expand_homedir_path(DROPBEAR_PIDFILE);
179+
svr_opts.authorized_keys_dir = "~/.ssh";
176180
#if DROPBEAR_SVR_LOCALANYFWD
177181
svr_opts.nolocaltcp = 0;
178182
#endif
@@ -225,6 +229,11 @@ void svr_getopts(int argc, char ** argv) {
225229
case 'r':
226230
next = &keyfile;
227231
break;
232+
#ifdef DROPBEAR_SVR_PUBKEY_AUTH
233+
case 'D':
234+
next = &svr_opts.authorized_keys_dir;
235+
break;
236+
#endif
228237
case 'R':
229238
svr_opts.delay_hostkey = 1;
230239
break;

0 commit comments

Comments
 (0)