add-authkeyfile-option.patch (9765B)
1 From 43f6d11e986666b0871b4e5b03171a4e7e8cb0e9 Mon Sep 17 00:00:00 2001 2 From: Daniel Barlow <dan@telent.net> 3 Date: Wed, 11 Mar 2026 18:13:32 +0000 4 Subject: [PATCH] add -U otion to set path to authorized_keys file 5 6 allow %u, %d, %n "format specifiers" in the name so that the user's 7 username/homedir/uid can be embedded into the path 8 --- 9 Makefile.in | 2 +- 10 manpages/dropbear.8 | 2 + 11 src/pathexpand.c | 149 +++++++++++++++++++++++++++++++++++++++++++ 12 src/runopts.h | 1 + 13 src/svr-authpubkey.c | 5 ++ 14 src/svr-runopts.c | 25 +++++--- 15 6 files changed, 175 insertions(+), 9 deletions(-) 16 create mode 100644 src/pathexpand.c 17 18 diff --git a/Makefile.in b/Makefile.in 19 index a26f0112..e5bb99a3 100644 20 --- a/Makefile.in 21 +++ b/Makefile.in 22 @@ -51,7 +51,7 @@ COMMONOBJS = $(patsubst %,$(OBJ_DIR)/%,$(_COMMONOBJS)) 23 _SVROBJS=svr-kex.o svr-auth.o sshpty.o \ 24 svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \ 25 svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\ 26 - svr-forward.o svr-tcpfwd.o svr-streamfwd.o svr-authpam.o 27 + svr-forward.o svr-tcpfwd.o svr-streamfwd.o svr-authpam.o pathexpand.o 28 SVROBJS = $(patsubst %,$(OBJ_DIR)/%,$(_SVROBJS)) 29 30 _CLIOBJS=cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \ 31 diff --git a/manpages/dropbear.8 b/manpages/dropbear.8 32 index fdfd2710..34bb3f38 100644 33 --- a/manpages/dropbear.8 34 +++ b/manpages/dropbear.8 35 @@ -35,6 +35,8 @@ below. 36 Generate hostkeys automatically. See 37 .Dq Host Key Files 38 below. 39 +.Fl U Ar Iauthorized_keys 40 +Absolute pathname to file containing authorized user keys. May contain the sequences %d, %n, %u which are expanded to the user's home directory, username and numeric uid respectively. Default '%d/.ssh/authorized_keys'. 41 .It Fl F 42 Don't fork into background. 43 .It Fl E 44 diff --git a/src/pathexpand.c b/src/pathexpand.c 45 new file mode 100644 46 index 00000000..07e69553 47 --- /dev/null 48 +++ b/src/pathexpand.c 49 @@ -0,0 +1,149 @@ 50 +#include <limits.h> 51 +#include <string.h> 52 +#include <stdio.h> 53 + 54 +#ifdef TEST_PATHEXPAND 55 +#include <stdlib.h> 56 + 57 +/* to run tests: 58 + gcc -Wall -o pathexpand -D TEST_PATHEXPAND=1 src/pathexpand.c && ./pathexpand 59 +*/ 60 + 61 +char * pathexpand(char *relfilename); 62 + 63 + 64 +#define m_malloc(c) malloc(c) 65 +#define m_strdup(c) strdup(c) 66 +#define TRACE(c) dropbear_trace1 c 67 +#include <stdarg.h> 68 + 69 +inline static void dropbear_trace1(const char* format, ...) { 70 + va_list param; 71 + 72 + va_start(param, format); 73 + /* vfprintf(stderr, format, param); */ 74 + /* fprintf(stderr, "\n"); */ 75 +} 76 + 77 + 78 +struct session { 79 + struct AuthState { 80 + char * pw_dir; 81 + char * pw_name; 82 + uid_t pw_uid; 83 + } authstate; 84 +}; 85 + 86 +struct session ses = { 87 + .authstate = { 88 + .pw_dir = "/home/dan", 89 + .pw_name = "dan", 90 + .pw_uid = 12345, 91 + } 92 +}; 93 + 94 +int exit_status = 0; 95 + 96 +int expect_expansion(char * input, char * expected) { 97 + char *actual = pathexpand(input); 98 + if(strcmp(actual, expected) != 0) { 99 + printf("expected %s for %s, got %s\n", expected, input, actual); 100 + exit_status++; 101 + } 102 + free(actual); 103 + return exit_status; 104 +} 105 + 106 +int main(int argc, char *argv[]) { 107 + for(int i = 1; i < argc; i++) { 108 + char *actual = pathexpand(argv[i]); 109 + printf("%s => %s\n", argv[i], pathexpand(argv[i])); 110 + free(actual); 111 + } 112 + 113 + /* a string without % is unaltered */ 114 + expect_expansion("hello", "hello"); 115 + 116 + /* discards single trailing % */ 117 + expect_expansion("hello%", "hello"); 118 + 119 + /* %% is transformed to % */ 120 + expect_expansion("hello%%", "hello%"); 121 + expect_expansion("hello%%goodbye", "hello%goodbye"); 122 + 123 + /* %u is transformed to uid */ 124 + expect_expansion("/run/user/%u/authorized_keys", "/run/user/12345/authorized_keys"); 125 + /* % sequences work when at start of string */ 126 + expect_expansion("%u/authorized_keys", "12345/authorized_keys"); 127 + 128 + /* %d expands to home directory */ 129 + expect_expansion("%d/.ssh", "/home/dan/.ssh"); 130 + 131 + /* %n expands to username */ 132 + expect_expansion("/tmp/%n/.ssh", "/tmp/dan/.ssh"); 133 + 134 + /* unrecognised specifiers are discarded */ 135 + expect_expansion("/hi/%q/.ssh", "/hi//.ssh"); 136 + 137 + 138 + expect_expansion("%d/.ssh/authorized_keys", "/home/dan/.ssh/authorized_keys"); 139 + exit(exit_status); 140 +} 141 + 142 +#else 143 +#include "session.h" 144 +#include "debug.h" 145 +#endif 146 +#define NUMLEN(c) strlen(#c) 147 + 148 +char * pathexpand(char *relfilename) 149 +{ 150 + char * filename; 151 + int len; 152 + 153 + len = strlen(relfilename); 154 + for(char *p = relfilename; p; p = strchr(p, '%')) { 155 + switch(*(p+1)) { 156 + case 'd': len += strlen(ses.authstate.pw_dir); break; 157 + case 'n': len += strlen(ses.authstate.pw_name); break; 158 + case 'u': len += NUMLEN(INT_MAX); break; 159 + } 160 + if(*(p+1) == '\0') break; 161 + p=p+2; 162 + } 163 + filename = m_malloc(len+1); 164 + filename[0] = '\0'; 165 + 166 + char *start = relfilename; 167 + char *out = filename; 168 + char *p = relfilename; 169 + do { 170 + p = strchr(start, '%'); 171 + 172 + if(!p) { 173 + strcat(out, start); 174 + break; 175 + } 176 + strncat(out, start, p - start); 177 + 178 + switch(*(p+1)) { 179 + case '\0': 180 + p++; break; 181 + case 'd': 182 + strcat(out, ses.authstate.pw_dir); break; 183 + case 'n': 184 + strcat(out, ses.authstate.pw_name); break; 185 + case 'u': 186 + snprintf(out + strlen(out), 187 + NUMLEN(INT_MAX), 188 + "%d", 189 + ses.authstate.pw_uid); 190 + break; 191 + case '%': 192 + strcat(out, "%"); break; 193 + } 194 + start = p+2; 195 + } 196 + while (*p); 197 + return filename; /* caller must free */ 198 +} 199 diff --git a/src/runopts.h b/src/runopts.h 200 index 166ad4a7..b6772faf 100644 201 --- a/src/runopts.h 202 +++ b/src/runopts.h 203 @@ -125,6 +125,7 @@ typedef struct svr_runopts { 204 char * pidfile; 205 206 char * authorized_keys_dir; 207 + char * authorized_keys_file; 208 209 char * forced_command; 210 char* interface; 211 diff --git a/src/svr-authpubkey.c b/src/svr-authpubkey.c 212 index 938b52da..8e25c238 100644 213 --- a/src/svr-authpubkey.c 214 +++ b/src/svr-authpubkey.c 215 @@ -76,6 +76,7 @@ 216 #define MAX_AUTHKEYS_LINE 3000 217 218 static char * authorized_keys_filepath(void); 219 +extern char * pathexpand(char *input); 220 static int checkpubkey(const char* keyalgo, unsigned int keyalgolen, 221 const unsigned char* keyblob, unsigned int keybloblen, 222 struct PubKeyOptions **ret_options); 223 @@ -465,6 +466,10 @@ static char *authorized_keys_filepath() { 224 char *pathname = NULL, *dir = NULL; 225 const char *filename = "authorized_keys"; 226 227 + if(svr_opts.authorized_keys_file) { 228 + return pathexpand(svr_opts.authorized_keys_file); 229 + }; 230 + 231 dir = expand_homedir_path_home(svr_opts.authorized_keys_dir, 232 ses.authstate.pw_dir); 233 234 diff --git a/src/svr-runopts.c b/src/svr-runopts.c 235 index 2baec6bd..c0c73ee6 100644 236 --- a/src/svr-runopts.c 237 +++ b/src/svr-runopts.c 238 @@ -1,19 +1,19 @@ 239 /* 240 * Dropbear - a SSH2 server 241 - * 242 + * 243 * Copyright (c) 2002,2003 Matt Johnston 244 * All rights reserved. 245 - * 246 + * 247 * Permission is hereby granted, free of charge, to any person obtaining a copy 248 * of this software and associated documentation files (the "Software"), to deal 249 * in the Software without restriction, including without limitation the rights 250 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 251 * copies of the Software, and to permit persons to whom the Software is 252 * furnished to do so, subject to the following conditions: 253 - * 254 + * 255 * The above copyright notice and this permission notice shall be included in 256 * all copies or substantial portions of the Software. 257 - * 258 + * 259 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 260 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 261 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 262 @@ -65,7 +65,7 @@ static void printhelp(const char * progname) { 263 "-D Directory containing authorized_keys file\n" 264 #endif 265 #if DROPBEAR_DELAY_HOSTKEY 266 - "-R Create hostkeys as required\n" 267 + "-R Create hostkeys as required\n" 268 #endif 269 "-F Don't fork into background\n" 270 "-e Pass on server process environment to child process\n" 271 @@ -157,6 +157,7 @@ void svr_getopts(int argc, char ** argv) { 272 char* reexec_fd_arg = NULL; 273 char* keyfile = NULL; 274 char *algo_print_arg = NULL; 275 + char* authkeysfile = NULL; 276 char c; 277 #if DROPBEAR_PLUGIN 278 char* pubkey_plugin = NULL; 279 @@ -184,6 +185,7 @@ void svr_getopts(int argc, char ** argv) { 280 svr_opts.delay_hostkey = 0; 281 svr_opts.pidfile = expand_homedir_path(DROPBEAR_PIDFILE); 282 svr_opts.authorized_keys_dir = "~/.ssh"; 283 + svr_opts.authorized_keys_file = NULL; 284 #if DROPBEAR_SVR_LOCALANYFWD 285 svr_opts.nolocaltcp = 0; 286 #endif 287 @@ -199,7 +201,7 @@ void svr_getopts(int argc, char ** argv) { 288 289 #ifndef DISABLE_ZLIB 290 opts.compression = 1; 291 -#endif 292 +#endif 293 294 /* not yet 295 opts.ipv4 = 1; 296 @@ -362,6 +364,9 @@ void svr_getopts(int argc, char ** argv) { 297 case 'u': 298 /* backwards compatibility with old urandom option */ 299 break; 300 + case 'U': 301 + next = &authkeysfile; 302 + break; 303 #if DROPBEAR_PLUGIN 304 case 'A': 305 next = &pubkey_plugin; 306 @@ -412,6 +417,10 @@ void svr_getopts(int argc, char ** argv) { 307 addhostkey(keyfile); 308 keyfile = NULL; 309 } 310 + if (authkeysfile) { 311 + svr_opts.authorized_keys_file = m_strdup(authkeysfile); 312 + authkeysfile = NULL; 313 + } 314 } 315 } 316 317 @@ -709,7 +718,7 @@ void load_all_hostkeys() { 318 - Otherwise no ecdsa keys will be advertised */ 319 320 /* check if any keys were loaded at startup */ 321 - loaded_any_ecdsa = 322 + loaded_any_ecdsa = 323 0 324 #if DROPBEAR_ECC_256 325 || svr_opts.hostkey->ecckey256 326 @@ -756,7 +765,7 @@ void load_all_hostkeys() { 327 #endif 328 #if DROPBEAR_SK_ECDSA 329 disablekey(DROPBEAR_SIGNATURE_SK_ECDSA_NISTP256); 330 -#endif 331 +#endif 332 #if DROPBEAR_SK_ED25519 333 disablekey(DROPBEAR_SIGNATURE_SK_ED25519); 334 #endif 335 -- 336 2.54.0 337