• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
printhelp()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 
cli_getopts(int argc,char ** argv)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
loadidentityfile(const char * filename)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 */
parsehostname(char * orighostarg)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 */
addforward(char * origstr,struct TCPFwdList ** fwdlist)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