1 /* 2 * Dropbear - a SSH2 server 3 * 4 * Copyright (c) 2002,2003 Matt Johnston 5 * All rights reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a copy 8 * of this software and associated documentation files (the "Software"), to deal 9 * in the Software without restriction, including without limitation the rights 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 * copies of the Software, and to permit persons to whom the Software is 12 * furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in 15 * all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. */ 24 25 #include "includes.h" 26 #include "runopts.h" 27 #include "signkey.h" 28 #include "buffer.h" 29 #include "dbutil.h" 30 #include "algo.h" 31 #include "tcpfwd.h" 32 33 cli_runopts cli_opts; /* GLOBAL */ 34 35 static void printhelp(); 36 static void parsehostname(char* userhostarg); 37 #ifdef ENABLE_CLI_PUBKEY_AUTH 38 static void loadidentityfile(const char* filename); 39 #endif 40 #ifdef ENABLE_CLI_ANYTCPFWD 41 static void addforward(char* str, struct TCPFwdList** fwdlist); 42 #endif 43 44 static void printhelp() { 45 46 fprintf(stderr, "Dropbear client v%s\n" 47 "Usage: %s [options] [user@]host [command]\n" 48 "Options are:\n" 49 "-p <remoteport>\n" 50 "-l <username>\n" 51 "-t Allocate a pty\n" 52 "-T Don't allocate a pty\n" 53 "-N Don't run a remote command\n" 54 "-f Run in background after auth\n" 55 "-y Always accept remote host key if unknown\n" 56 #ifdef ENABLE_CLI_PUBKEY_AUTH 57 "-i <identityfile> (multiple allowed)\n" 58 #endif 59 #ifdef ENABLE_CLI_LOCALTCPFWD 60 "-L <listenport:remotehost:remoteport> Local port forwarding\n" 61 "-g Allow remote hosts to connect to forwarded ports\n" 62 #endif 63 #ifdef ENABLE_CLI_REMOTETCPFWD 64 "-R <listenport:remotehost:remoteport> Remote port forwarding\n" 65 #endif 66 #ifdef DEBUG_TRACE 67 "-v verbose\n" 68 #endif 69 ,DROPBEAR_VERSION, cli_opts.progname); 70 } 71 72 void cli_getopts(int argc, char ** argv) { 73 74 unsigned int i, j; 75 char ** next = 0; 76 unsigned int cmdlen; 77 #ifdef ENABLE_CLI_PUBKEY_AUTH 78 int nextiskey = 0; /* A flag if the next argument is a keyfile */ 79 #endif 80 #ifdef ENABLE_CLI_LOCALTCPFWD 81 int nextislocal = 0; 82 #endif 83 #ifdef ENABLE_CLI_REMOTETCPFWD 84 int nextisremote = 0; 85 #endif 86 char* dummy = NULL; /* Not used for anything real */ 87 88 /* see printhelp() for options */ 89 cli_opts.progname = argv[0]; 90 cli_opts.remotehost = NULL; 91 cli_opts.remoteport = NULL; 92 cli_opts.username = NULL; 93 cli_opts.cmd = NULL; 94 cli_opts.no_cmd = 0; 95 cli_opts.backgrounded = 0; 96 cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */ 97 cli_opts.always_accept_key = 0; 98 #ifdef ENABLE_CLI_PUBKEY_AUTH 99 cli_opts.privkeys = NULL; 100 #endif 101 #ifdef ENABLE_CLI_LOCALTCPFWD 102 cli_opts.localfwds = NULL; 103 opts.listen_fwd_all = 0; 104 #endif 105 #ifdef ENABLE_CLI_REMOTETCPFWD 106 cli_opts.remotefwds = NULL; 107 #endif 108 /* not yet 109 opts.ipv4 = 1; 110 opts.ipv6 = 1; 111 */ 112 113 /* Iterate all the arguments */ 114 for (i = 1; i < (unsigned int)argc; i++) { 115 #ifdef ENABLE_CLI_PUBKEY_AUTH 116 if (nextiskey) { 117 /* Load a hostkey since the previous argument was "-i" */ 118 loadidentityfile(argv[i]); 119 nextiskey = 0; 120 continue; 121 } 122 #endif 123 #ifdef ENABLE_CLI_REMOTETCPFWD 124 if (nextisremote) { 125 TRACE(("nextisremote true")) 126 addforward(argv[i], &cli_opts.remotefwds); 127 nextisremote = 0; 128 continue; 129 } 130 #endif 131 #ifdef ENABLE_CLI_LOCALTCPFWD 132 if (nextislocal) { 133 TRACE(("nextislocal true")) 134 addforward(argv[i], &cli_opts.localfwds); 135 nextislocal = 0; 136 continue; 137 } 138 #endif 139 if (next) { 140 /* The previous flag set a value to assign */ 141 *next = argv[i]; 142 if (*next == NULL) { 143 dropbear_exit("Invalid null argument"); 144 } 145 next = NULL; 146 continue; 147 } 148 149 if (argv[i][0] == '-') { 150 /* A flag *waves* */ 151 152 switch (argv[i][1]) { 153 case 'y': /* always accept the remote hostkey */ 154 cli_opts.always_accept_key = 1; 155 break; 156 case 'p': /* remoteport */ 157 next = &cli_opts.remoteport; 158 break; 159 #ifdef ENABLE_CLI_PUBKEY_AUTH 160 case 'i': /* an identityfile */ 161 /* Keep scp happy when it changes "-i file" to "-ifile" */ 162 if (strlen(argv[i]) > 2) { 163 loadidentityfile(&argv[i][2]); 164 } else { 165 nextiskey = 1; 166 } 167 break; 168 #endif 169 case 't': /* we want a pty */ 170 cli_opts.wantpty = 1; 171 break; 172 case 'T': /* don't want a pty */ 173 cli_opts.wantpty = 0; 174 break; 175 case 'N': 176 cli_opts.no_cmd = 1; 177 break; 178 case 'f': 179 cli_opts.backgrounded = 1; 180 break; 181 #ifdef ENABLE_CLI_LOCALTCPFWD 182 case 'L': 183 nextislocal = 1; 184 break; 185 case 'g': 186 opts.listen_fwd_all = 1; 187 break; 188 #endif 189 #ifdef ENABLE_CLI_REMOTETCPFWD 190 case 'R': 191 nextisremote = 1; 192 break; 193 #endif 194 case 'l': 195 next = &cli_opts.username; 196 break; 197 case 'h': 198 printhelp(); 199 exit(EXIT_SUCCESS); 200 break; 201 #ifdef DEBUG_TRACE 202 case 'v': 203 debug_trace = 1; 204 break; 205 #endif 206 case 'F': 207 case 'e': 208 case 'c': 209 case 'm': 210 case 'D': 211 #ifndef ENABLE_CLI_REMOTETCPFWD 212 case 'R': 213 #endif 214 #ifndef ENABLE_CLI_LOCALTCPFWD 215 case 'L': 216 #endif 217 case 'o': 218 case 'b': 219 next = &dummy; 220 default: 221 fprintf(stderr, 222 "WARNING: Ignoring unknown argument '%s'\n", argv[i]); 223 break; 224 } /* Switch */ 225 226 /* Now we handle args where they might be "-luser" (no spaces)*/ 227 if (next && strlen(argv[i]) > 2) { 228 *next = &argv[i][2]; 229 next = NULL; 230 } 231 232 continue; /* next argument */ 233 234 } else { 235 TRACE(("non-flag arg: '%s'", argv[i])) 236 237 /* Either the hostname or commands */ 238 239 if (cli_opts.remotehost == NULL) { 240 241 parsehostname(argv[i]); 242 243 } else { 244 245 /* this is part of the commands to send - after this we 246 * don't parse any more options, and flags are sent as the 247 * command */ 248 cmdlen = 0; 249 for (j = i; j < (unsigned int)argc; j++) { 250 cmdlen += strlen(argv[j]) + 1; /* +1 for spaces */ 251 } 252 /* Allocate the space */ 253 cli_opts.cmd = (char*)m_malloc(cmdlen); 254 cli_opts.cmd[0] = '\0'; 255 256 /* Append all the bits */ 257 for (j = i; j < (unsigned int)argc; j++) { 258 strlcat(cli_opts.cmd, argv[j], cmdlen); 259 strlcat(cli_opts.cmd, " ", cmdlen); 260 } 261 /* It'll be null-terminated here */ 262 263 /* We've eaten all the options and flags */ 264 break; 265 } 266 } 267 } 268 269 if (cli_opts.remotehost == NULL) { 270 printhelp(); 271 exit(EXIT_FAILURE); 272 } 273 274 if (cli_opts.remoteport == NULL) { 275 cli_opts.remoteport = "22"; 276 } 277 278 /* If not explicitly specified with -t or -T, we don't want a pty if 279 * there's a command, but we do otherwise */ 280 if (cli_opts.wantpty == 9) { 281 if (cli_opts.cmd == NULL) { 282 cli_opts.wantpty = 1; 283 } else { 284 cli_opts.wantpty = 0; 285 } 286 } 287 288 if (cli_opts.backgrounded && cli_opts.cmd == NULL 289 && cli_opts.no_cmd == 0) { 290 dropbear_exit("command required for -f"); 291 } 292 } 293 294 #ifdef ENABLE_CLI_PUBKEY_AUTH 295 static void loadidentityfile(const char* filename) { 296 297 struct SignKeyList * nextkey; 298 sign_key *key; 299 int keytype; 300 301 key = new_sign_key(); 302 keytype = DROPBEAR_SIGNKEY_ANY; 303 if ( readhostkey(filename, key, &keytype) != DROPBEAR_SUCCESS ) { 304 305 fprintf(stderr, "Failed loading keyfile '%s'\n", filename); 306 sign_key_free(key); 307 308 } else { 309 310 nextkey = (struct SignKeyList*)m_malloc(sizeof(struct SignKeyList)); 311 nextkey->key = key; 312 nextkey->next = cli_opts.privkeys; 313 nextkey->type = keytype; 314 cli_opts.privkeys = nextkey; 315 } 316 } 317 #endif 318 319 320 /* Parses a [user@]hostname argument. userhostarg is the argv[i] corresponding 321 * - note that it will be modified */ 322 static void parsehostname(char* orighostarg) { 323 324 uid_t uid; 325 struct passwd *pw = NULL; 326 char *userhostarg = NULL; 327 328 /* We probably don't want to be editing argvs */ 329 userhostarg = m_strdup(orighostarg); 330 331 cli_opts.remotehost = strchr(userhostarg, '@'); 332 if (cli_opts.remotehost == NULL) { 333 /* no username portion, the cli-auth.c code can figure the 334 * local user's name */ 335 cli_opts.remotehost = userhostarg; 336 } else { 337 cli_opts.remotehost[0] = '\0'; /* Split the user/host */ 338 cli_opts.remotehost++; 339 cli_opts.username = userhostarg; 340 } 341 342 if (cli_opts.username == NULL) { 343 uid = getuid(); 344 345 pw = getpwuid(uid); 346 if (pw == NULL || pw->pw_name == NULL) { 347 dropbear_exit("Unknown own user"); 348 } 349 350 cli_opts.username = m_strdup(pw->pw_name); 351 } 352 353 if (cli_opts.remotehost[0] == '\0') { 354 dropbear_exit("Bad hostname"); 355 } 356 } 357 358 #ifdef ENABLE_CLI_ANYTCPFWD 359 /* Turn a "listenport:remoteaddr:remoteport" string into into a forwarding 360 * set, and add it to the forwarding list */ 361 static void addforward(char* origstr, struct TCPFwdList** fwdlist) { 362 363 char * listenport = NULL; 364 char * connectport = NULL; 365 char * connectaddr = NULL; 366 struct TCPFwdList* newfwd = NULL; 367 char * str = NULL; 368 369 TRACE(("enter addforward")) 370 371 /* We need to split the original argument up. This var 372 is never free()d. */ 373 str = m_strdup(origstr); 374 375 listenport = str; 376 377 connectaddr = strchr(str, ':'); 378 if (connectaddr == NULL) { 379 TRACE(("connectaddr == NULL")) 380 goto fail; 381 } 382 *connectaddr = '\0'; 383 connectaddr++; 384 385 connectport = strchr(connectaddr, ':'); 386 if (connectport == NULL) { 387 TRACE(("connectport == NULL")) 388 goto fail; 389 } 390 *connectport = '\0'; 391 connectport++; 392 393 newfwd = (struct TCPFwdList*)m_malloc(sizeof(struct TCPFwdList)); 394 395 /* Now we check the ports - note that the port ints are unsigned, 396 * the check later only checks for >= MAX_PORT */ 397 newfwd->listenport = strtol(listenport, NULL, 10); 398 if (errno != 0) { 399 TRACE(("bad listenport strtol")) 400 goto fail; 401 } 402 403 newfwd->connectport = strtol(connectport, NULL, 10); 404 if (errno != 0) { 405 TRACE(("bad connectport strtol")) 406 goto fail; 407 } 408 409 newfwd->connectaddr = connectaddr; 410 411 if (newfwd->listenport > 65535) { 412 TRACE(("listenport > 65535")) 413 goto badport; 414 } 415 416 if (newfwd->connectport > 65535) { 417 TRACE(("connectport > 65535")) 418 goto badport; 419 } 420 421 newfwd->next = *fwdlist; 422 *fwdlist = newfwd; 423 424 TRACE(("leave addforward: done")) 425 return; 426 427 fail: 428 dropbear_exit("Bad TCP forward '%s'", origstr); 429 430 badport: 431 dropbear_exit("Bad TCP port in '%s'", origstr); 432 } 433 #endif 434