Skip to content

Commit

Permalink
Allow specifying authorized_keys directory.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
daztucker committed Dec 3, 2024
1 parent 155639d commit f9c9ced
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 67 deletions.
40 changes: 21 additions & 19 deletions src/dbutil.c
Original file line number Diff line number Diff line change
Expand Up @@ -637,30 +637,32 @@ int m_str_to_uint(const char* str, unsigned int *val) {
}
}

/* Returns malloced path. inpath beginning with '~/' expanded,
otherwise returned as-is */
/* Returns malloced path from inpath, possibly expanding '~/'
into the specified home directory.*/
char * expand_homedir_path_home(const char *inpath, const char *homedir) {
if (strncmp(inpath, "~/", 2) == 0 && homedir) {
size_t len = strlen(inpath)-2 + strlen(homedir) + 2;
char *buf = m_malloc(len);
snprintf(buf, len, "%s/%s", homedir, inpath+2);
return buf;
}
/* Fallback */
return m_strdup(inpath);
}

/* Returns malloced path from inpath, possibly expanding '~/'
into the current user's home directory.*/
char * expand_homedir_path(const char *inpath) {
struct passwd *pw = NULL;
if (strncmp(inpath, "~/", 2) == 0) {
char *homedir = getenv("HOME");
char *homedir = getenv("HOME");

if (!homedir) {
pw = getpwuid(getuid());
if (pw) {
homedir = pw->pw_dir;
}
}

if (homedir) {
int len = strlen(inpath)-2 + strlen(homedir) + 2;
char *buf = m_malloc(len);
snprintf(buf, len, "%s/%s", homedir, inpath+2);
return buf;
if (!homedir) {
pw = getpwuid(getuid());
if (pw) {
homedir = pw->pw_dir;
}
}

/* Fallback */
return m_strdup(inpath);
return expand_homedir_path_home(inpath, homedir);
}

int constant_time_memcmp(const void* a, const void *b, size_t n)
Expand Down
1 change: 1 addition & 0 deletions src/dbutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ time_t monotonic_now(void);
void gettime_wrapper(struct timespec *now);

char * expand_homedir_path(const char *inpath);
char * expand_homedir_path_home(const char *inpath, const char *homedir);

void fsync_parent_dir(const char* fn);

Expand Down
2 changes: 2 additions & 0 deletions src/runopts.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ typedef struct svr_runopts {
buffer * banner;
char * pidfile;

char * authorized_keys_dir;

char * forced_command;
char* interface;

Expand Down
90 changes: 42 additions & 48 deletions src/svr-authpubkey.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
#define MIN_AUTHKEYS_LINE 10 /* "ssh-rsa AB" - short but doesn't matter */
#define MAX_AUTHKEYS_LINE 4200 /* max length of a line in authkeys */

static char * authorized_keys_filepath(void);
static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
const unsigned char* keyblob, unsigned int keybloblen);
static int checkpubkeyperms(void);
Expand Down Expand Up @@ -431,6 +432,24 @@ static int checkpubkey_line(buffer* line, int line_num, const char* filename,
return ret;
}

/* Returns the full path to the user's authorized_keys file in an
* allocated string which caller must free. */
static char *authorized_keys_filepath() {
size_t len = 0;
char *pathname = NULL, *dir = NULL;
const char *filename = "authorized_keys";

dir = expand_homedir_path_home(svr_opts.authorized_keys_dir,
ses.authstate.pw_dir);

/* allocate max required pathname storage,
* = dir + "/" + "authorized_keys" + '\0' */;
len = strlen(dir) + strlen(filename) + 2;
pathname = m_malloc(len);
snprintf(pathname, len, "%s/%s", dir, filename);
m_free(dir);
return pathname;
}

/* Checks whether a specified publickey (and associated algorithm) is an
* acceptable key for authentication */
Expand All @@ -442,7 +461,6 @@ static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
char * filename = NULL;
int ret = DROPBEAR_FAILURE;
buffer * line = NULL;
unsigned int len;
int line_num;
uid_t origuid;
gid_t origgid;
Expand All @@ -464,13 +482,7 @@ static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
} else {
/* 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);

filename = authorized_keys_filepath();
authfile = fopen(filename, "r");
if (!authfile) {
TRACE(("checkpubkey: failed opening %s: %s", filename, strerror(errno)))
Expand Down Expand Up @@ -524,53 +536,35 @@ static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,

/* 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
* g-w, o-w */
* Checks that the authorized_keys path permissions are all owned by either
* root or the user, and are g-w, o-w.
* When this path is inside the user's home dir it checks up to and including
* the home dir, otherwise it checks every path component. */
static int checkpubkeyperms() {

char* filename = NULL;
int ret = DROPBEAR_FAILURE;
unsigned int len;
char *path = authorized_keys_filepath(), *sep = NULL;
int ret = DROPBEAR_SUCCESS;

TRACE(("enter checkpubkeyperms"))

if (ses.authstate.pw_dir == NULL) {
goto out;
}

if ((len = strlen(ses.authstate.pw_dir)) == 0) {
goto out;
}

/* allocate max required pathname storage,
* = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
len += 22;
filename = m_malloc(len);
strlcpy(filename, ses.authstate.pw_dir, len);

/* check ~ */
if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
goto out;
}

/* check ~/.ssh */
strlcat(filename, "/.ssh", len);
if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
goto out;
}

/* now check ~/.ssh/authorized_keys */
strlcat(filename, "/authorized_keys", len);
if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
goto out;
/* Walk back up path checking permissions, stopping at either homedir,
* or root if the path is outside of the homedir. */
while ((sep = strrchr(path, '/')) != NULL) {
if (sep == path) { /* root directory */
sep++;
}
*sep = '\0';
if (checkfileperm(path) != DROPBEAR_SUCCESS) {
TRACE(("checkpubkeyperms: bad perm on %s", path))
ret = DROPBEAR_FAILURE;
}
if (strcmp(path, ses.authstate.pw_dir) == 0 || strcmp(path, "/") == 0) {
break;
}
}

/* file looks ok, return success */
ret = DROPBEAR_SUCCESS;

/* all looks ok, return success */
out:
m_free(filename);
m_free(path);

TRACE(("leave checkpubkeyperms"))
return ret;
Expand Down
9 changes: 9 additions & 0 deletions src/svr-runopts.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ static void printhelp(const char * progname) {
#if DROPBEAR_ED25519
" - ed25519 %s\n"
#endif
#if DROPBEAR_SVR_PUBKEY_AUTH
"-D Directory containing authorized_keys file\n"
#endif
#if DROPBEAR_DELAY_HOSTKEY
"-R Create hostkeys as required\n"
#endif
Expand Down Expand Up @@ -173,6 +176,7 @@ void svr_getopts(int argc, char ** argv) {
svr_opts.hostkey = NULL;
svr_opts.delay_hostkey = 0;
svr_opts.pidfile = expand_homedir_path(DROPBEAR_PIDFILE);
svr_opts.authorized_keys_dir = "~/.ssh";
#if DROPBEAR_SVR_LOCALANYFWD
svr_opts.nolocaltcp = 0;
#endif
Expand Down Expand Up @@ -225,6 +229,11 @@ void svr_getopts(int argc, char ** argv) {
case 'r':
next = &keyfile;
break;
#ifdef DROPBEAR_SVR_PUBKEY_AUTH
case 'D':
next = &svr_opts.authorized_keys_dir;
break;
#endif
case 'R':
svr_opts.delay_hostkey = 1;
break;
Expand Down

0 comments on commit f9c9ced

Please sign in to comment.