Home | History | Annotate | Download | only in dropbear
      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