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 );
7475static int checkpubkey (const char * keyalgo , unsigned int keyalgolen ,
7576 const unsigned char * keyblob , unsigned int keybloblen );
7677static 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. */
530543static 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 */
572566out :
573- m_free (filename );
567+ m_free (path );
574568
575569 TRACE (("leave checkpubkeyperms" ))
576570 return ret ;
0 commit comments