xref: /netbsd-src/crypto/external/bsd/openssh/dist/auth-options.c (revision 7788a0781fe6ff2cce37368b4578a7ade0850cb1)
1 /*	$NetBSD: auth-options.c,v 1.6 2013/03/29 16:19:44 christos Exp $	*/
2 /* $OpenBSD: auth-options.c,v 1.57 2012/12/02 20:46:11 djm Exp $ */
3 /*
4  * Author: Tatu Ylonen <ylo@cs.hut.fi>
5  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
6  *                    All rights reserved
7  * As far as I am concerned, the code I have written for this software
8  * can be used freely for any purpose.  Any derived versions of this
9  * software must be clearly marked as such, and if the derived work is
10  * incompatible with the protocol description in the RFC file, it must be
11  * called by a name other than "ssh" or "Secure Shell".
12  */
13 
14 #include "includes.h"
15 __RCSID("$NetBSD: auth-options.c,v 1.6 2013/03/29 16:19:44 christos Exp $");
16 #include <sys/types.h>
17 #include <sys/queue.h>
18 
19 #include <netdb.h>
20 #include <pwd.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <time.h>
25 
26 #include "xmalloc.h"
27 #include "match.h"
28 #include "log.h"
29 #include "canohost.h"
30 #include "buffer.h"
31 #include "channels.h"
32 #include "servconf.h"
33 #include "misc.h"
34 #include "key.h"
35 #include "auth-options.h"
36 #include "hostfile.h"
37 #include "auth.h"
38 #ifdef GSSAPI
39 #include "ssh-gss.h"
40 #endif
41 #include "monitor_wrap.h"
42 
43 /* Flags set authorized_keys flags */
44 int no_port_forwarding_flag = 0;
45 int no_agent_forwarding_flag = 0;
46 int no_x11_forwarding_flag = 0;
47 int no_pty_flag = 0;
48 int no_user_rc = 0;
49 int key_is_cert_authority = 0;
50 
51 /* "command=" option. */
52 char *forced_command = NULL;
53 
54 /* "environment=" options. */
55 struct envstring *custom_environment = NULL;
56 
57 /* "tunnel=" option. */
58 int forced_tun_device = -1;
59 
60 /* "principals=" option. */
61 char *authorized_principals = NULL;
62 
63 extern ServerOptions options;
64 
65 void
66 auth_clear_options(void)
67 {
68 	no_agent_forwarding_flag = 0;
69 	no_port_forwarding_flag = 0;
70 	no_pty_flag = 0;
71 	no_x11_forwarding_flag = 0;
72 	no_user_rc = 0;
73 	key_is_cert_authority = 0;
74 	while (custom_environment) {
75 		struct envstring *ce = custom_environment;
76 		custom_environment = ce->next;
77 		xfree(ce->s);
78 		xfree(ce);
79 	}
80 	if (forced_command) {
81 		xfree(forced_command);
82 		forced_command = NULL;
83 	}
84 	if (authorized_principals) {
85 		xfree(authorized_principals);
86 		authorized_principals = NULL;
87 	}
88 	forced_tun_device = -1;
89 	channel_clear_permitted_opens();
90 }
91 
92 /*
93  * return 1 if access is granted, 0 if not.
94  * side effect: sets key option flags
95  */
96 int
97 auth_parse_options(struct passwd *pw, const char *opts, const char *file,
98     u_long linenum)
99 {
100 	const char *cp;
101 	int i;
102 
103 	/* reset options */
104 	auth_clear_options();
105 
106 	if (!opts)
107 		return 1;
108 
109 	while (*opts && *opts != ' ' && *opts != '\t') {
110 		cp = "cert-authority";
111 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
112 			key_is_cert_authority = 1;
113 			opts += strlen(cp);
114 			goto next_option;
115 		}
116 		cp = "no-port-forwarding";
117 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
118 			auth_debug_add("Port forwarding disabled.");
119 			no_port_forwarding_flag = 1;
120 			opts += strlen(cp);
121 			goto next_option;
122 		}
123 		cp = "no-agent-forwarding";
124 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
125 			auth_debug_add("Agent forwarding disabled.");
126 			no_agent_forwarding_flag = 1;
127 			opts += strlen(cp);
128 			goto next_option;
129 		}
130 		cp = "no-X11-forwarding";
131 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
132 			auth_debug_add("X11 forwarding disabled.");
133 			no_x11_forwarding_flag = 1;
134 			opts += strlen(cp);
135 			goto next_option;
136 		}
137 		cp = "no-pty";
138 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
139 			auth_debug_add("Pty allocation disabled.");
140 			no_pty_flag = 1;
141 			opts += strlen(cp);
142 			goto next_option;
143 		}
144 		cp = "no-user-rc";
145 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
146 			auth_debug_add("User rc file execution disabled.");
147 			no_user_rc = 1;
148 			opts += strlen(cp);
149 			goto next_option;
150 		}
151 		cp = "command=\"";
152 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
153 			opts += strlen(cp);
154 			if (forced_command != NULL)
155 				xfree(forced_command);
156 			forced_command = xmalloc(strlen(opts) + 1);
157 			i = 0;
158 			while (*opts) {
159 				if (*opts == '"')
160 					break;
161 				if (*opts == '\\' && opts[1] == '"') {
162 					opts += 2;
163 					forced_command[i++] = '"';
164 					continue;
165 				}
166 				forced_command[i++] = *opts++;
167 			}
168 			if (!*opts) {
169 				debug("%.100s, line %lu: missing end quote",
170 				    file, linenum);
171 				auth_debug_add("%.100s, line %lu: missing end quote",
172 				    file, linenum);
173 				xfree(forced_command);
174 				forced_command = NULL;
175 				goto bad_option;
176 			}
177 			forced_command[i] = '\0';
178 			auth_debug_add("Forced command.");
179 			opts++;
180 			goto next_option;
181 		}
182 		cp = "principals=\"";
183 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
184 			opts += strlen(cp);
185 			if (authorized_principals != NULL)
186 				xfree(authorized_principals);
187 			authorized_principals = xmalloc(strlen(opts) + 1);
188 			i = 0;
189 			while (*opts) {
190 				if (*opts == '"')
191 					break;
192 				if (*opts == '\\' && opts[1] == '"') {
193 					opts += 2;
194 					authorized_principals[i++] = '"';
195 					continue;
196 				}
197 				authorized_principals[i++] = *opts++;
198 			}
199 			if (!*opts) {
200 				debug("%.100s, line %lu: missing end quote",
201 				    file, linenum);
202 				auth_debug_add("%.100s, line %lu: missing end quote",
203 				    file, linenum);
204 				xfree(authorized_principals);
205 				authorized_principals = NULL;
206 				goto bad_option;
207 			}
208 			authorized_principals[i] = '\0';
209 			auth_debug_add("principals: %.900s",
210 			    authorized_principals);
211 			opts++;
212 			goto next_option;
213 		}
214 		cp = "environment=\"";
215 		if (options.permit_user_env &&
216 		    strncasecmp(opts, cp, strlen(cp)) == 0) {
217 			char *s;
218 			struct envstring *new_envstring;
219 
220 			opts += strlen(cp);
221 			s = xmalloc(strlen(opts) + 1);
222 			i = 0;
223 			while (*opts) {
224 				if (*opts == '"')
225 					break;
226 				if (*opts == '\\' && opts[1] == '"') {
227 					opts += 2;
228 					s[i++] = '"';
229 					continue;
230 				}
231 				s[i++] = *opts++;
232 			}
233 			if (!*opts) {
234 				debug("%.100s, line %lu: missing end quote",
235 				    file, linenum);
236 				auth_debug_add("%.100s, line %lu: missing end quote",
237 				    file, linenum);
238 				xfree(s);
239 				goto bad_option;
240 			}
241 			s[i] = '\0';
242 			auth_debug_add("Adding to environment: %.900s", s);
243 			debug("Adding to environment: %.900s", s);
244 			opts++;
245 			new_envstring = xmalloc(sizeof(struct envstring));
246 			new_envstring->s = s;
247 			new_envstring->next = custom_environment;
248 			custom_environment = new_envstring;
249 			goto next_option;
250 		}
251 		cp = "from=\"";
252 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
253 			const char *remote_ip = get_remote_ipaddr();
254 			const char *remote_host = get_canonical_hostname(
255 			    options.use_dns);
256 			char *patterns = xmalloc(strlen(opts) + 1);
257 
258 			opts += strlen(cp);
259 			i = 0;
260 			while (*opts) {
261 				if (*opts == '"')
262 					break;
263 				if (*opts == '\\' && opts[1] == '"') {
264 					opts += 2;
265 					patterns[i++] = '"';
266 					continue;
267 				}
268 				patterns[i++] = *opts++;
269 			}
270 			if (!*opts) {
271 				debug("%.100s, line %lu: missing end quote",
272 				    file, linenum);
273 				auth_debug_add("%.100s, line %lu: missing end quote",
274 				    file, linenum);
275 				xfree(patterns);
276 				goto bad_option;
277 			}
278 			patterns[i] = '\0';
279 			opts++;
280 			switch (match_host_and_ip(remote_host, remote_ip,
281 			    patterns)) {
282 			case 1:
283 				xfree(patterns);
284 				/* Host name matches. */
285 				goto next_option;
286 			case -1:
287 				debug("%.100s, line %lu: invalid criteria",
288 				    file, linenum);
289 				auth_debug_add("%.100s, line %lu: "
290 				    "invalid criteria", file, linenum);
291 				/* FALLTHROUGH */
292 			case 0:
293 				xfree(patterns);
294 				logit("Authentication tried for %.100s with "
295 				    "correct key but not from a permitted "
296 				    "host (host=%.200s, ip=%.200s).",
297 				    pw->pw_name, remote_host, remote_ip);
298 				auth_debug_add("Your host '%.200s' is not "
299 				    "permitted to use this key for login.",
300 				    remote_host);
301 				break;
302 			}
303 			/* deny access */
304 			return 0;
305 		}
306 		cp = "permitopen=\"";
307 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
308 			char *host, *p;
309 			int port;
310 			char *patterns = xmalloc(strlen(opts) + 1);
311 
312 			opts += strlen(cp);
313 			i = 0;
314 			while (*opts) {
315 				if (*opts == '"')
316 					break;
317 				if (*opts == '\\' && opts[1] == '"') {
318 					opts += 2;
319 					patterns[i++] = '"';
320 					continue;
321 				}
322 				patterns[i++] = *opts++;
323 			}
324 			if (!*opts) {
325 				debug("%.100s, line %lu: missing end quote",
326 				    file, linenum);
327 				auth_debug_add("%.100s, line %lu: missing "
328 				    "end quote", file, linenum);
329 				xfree(patterns);
330 				goto bad_option;
331 			}
332 			patterns[i] = '\0';
333 			opts++;
334 			p = patterns;
335 			host = hpdelim(&p);
336 			if (host == NULL || strlen(host) >= NI_MAXHOST) {
337 				debug("%.100s, line %lu: Bad permitopen "
338 				    "specification <%.100s>", file, linenum,
339 				    patterns);
340 				auth_debug_add("%.100s, line %lu: "
341 				    "Bad permitopen specification", file,
342 				    linenum);
343 				xfree(patterns);
344 				goto bad_option;
345 			}
346 			host = cleanhostname(host);
347 			if (p == NULL || (port = permitopen_port(p)) < 0) {
348 				debug("%.100s, line %lu: Bad permitopen port "
349 				    "<%.100s>", file, linenum, p ? p : "");
350 				auth_debug_add("%.100s, line %lu: "
351 				    "Bad permitopen port", file, linenum);
352 				xfree(patterns);
353 				goto bad_option;
354 			}
355 			if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0)
356 				channel_add_permitted_opens(host, port);
357 			xfree(patterns);
358 			goto next_option;
359 		}
360 		cp = "tunnel=\"";
361 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
362 			char *tun = NULL;
363 			opts += strlen(cp);
364 			tun = xmalloc(strlen(opts) + 1);
365 			i = 0;
366 			while (*opts) {
367 				if (*opts == '"')
368 					break;
369 				tun[i++] = *opts++;
370 			}
371 			if (!*opts) {
372 				debug("%.100s, line %lu: missing end quote",
373 				    file, linenum);
374 				auth_debug_add("%.100s, line %lu: missing end quote",
375 				    file, linenum);
376 				xfree(tun);
377 				forced_tun_device = -1;
378 				goto bad_option;
379 			}
380 			tun[i] = '\0';
381 			forced_tun_device = a2tun(tun, NULL);
382 			xfree(tun);
383 			if (forced_tun_device == SSH_TUNID_ERR) {
384 				debug("%.100s, line %lu: invalid tun device",
385 				    file, linenum);
386 				auth_debug_add("%.100s, line %lu: invalid tun device",
387 				    file, linenum);
388 				forced_tun_device = -1;
389 				goto bad_option;
390 			}
391 			auth_debug_add("Forced tun device: %d", forced_tun_device);
392 			opts++;
393 			goto next_option;
394 		}
395 next_option:
396 		/*
397 		 * Skip the comma, and move to the next option
398 		 * (or break out if there are no more).
399 		 */
400 		if (!*opts)
401 			fatal("Bugs in auth-options.c option processing.");
402 		if (*opts == ' ' || *opts == '\t')
403 			break;		/* End of options. */
404 		if (*opts != ',')
405 			goto bad_option;
406 		opts++;
407 		/* Process the next option. */
408 	}
409 
410 	/* grant access */
411 	return 1;
412 
413 bad_option:
414 	logit("Bad options in %.100s file, line %lu: %.50s",
415 	    file, linenum, opts);
416 	auth_debug_add("Bad options in %.100s file, line %lu: %.50s",
417 	    file, linenum, opts);
418 
419 	/* deny access */
420 	return 0;
421 }
422 
423 #define OPTIONS_CRITICAL	1
424 #define OPTIONS_EXTENSIONS	2
425 static int
426 parse_option_list(u_char *optblob, size_t optblob_len, struct passwd *pw,
427     u_int which, int crit,
428     int *cert_no_port_forwarding_flag,
429     int *cert_no_agent_forwarding_flag,
430     int *cert_no_x11_forwarding_flag,
431     int *cert_no_pty_flag,
432     int *cert_no_user_rc,
433     char **cert_forced_command,
434     int *cert_source_address_done)
435 {
436 	char *command, *allowed;
437 	const char *remote_ip;
438 	u_char *name = NULL, *data_blob = NULL;
439 	u_int nlen, dlen, clen;
440 	Buffer c, data;
441 	int ret = -1, found;
442 
443 	buffer_init(&data);
444 
445 	/* Make copy to avoid altering original */
446 	buffer_init(&c);
447 	buffer_append(&c, optblob, optblob_len);
448 
449 	while (buffer_len(&c) > 0) {
450 		if ((name = buffer_get_cstring_ret(&c, &nlen)) == NULL ||
451 		    (data_blob = buffer_get_string_ret(&c, &dlen)) == NULL) {
452 			error("Certificate options corrupt");
453 			goto out;
454 		}
455 		buffer_append(&data, data_blob, dlen);
456 		debug3("found certificate option \"%.100s\" len %u",
457 		    name, dlen);
458 		found = 0;
459 		if ((which & OPTIONS_EXTENSIONS) != 0) {
460 			if (strcmp(name, "permit-X11-forwarding") == 0) {
461 				*cert_no_x11_forwarding_flag = 0;
462 				found = 1;
463 			} else if (strcmp(name,
464 			    "permit-agent-forwarding") == 0) {
465 				*cert_no_agent_forwarding_flag = 0;
466 				found = 1;
467 			} else if (strcmp(name,
468 			    "permit-port-forwarding") == 0) {
469 				*cert_no_port_forwarding_flag = 0;
470 				found = 1;
471 			} else if (strcmp(name, "permit-pty") == 0) {
472 				*cert_no_pty_flag = 0;
473 				found = 1;
474 			} else if (strcmp(name, "permit-user-rc") == 0) {
475 				*cert_no_user_rc = 0;
476 				found = 1;
477 			}
478 		}
479 		if (!found && (which & OPTIONS_CRITICAL) != 0) {
480 			if (strcmp(name, "force-command") == 0) {
481 				if ((command = buffer_get_cstring_ret(&data,
482 				    &clen)) == NULL) {
483 					error("Certificate constraint \"%s\" "
484 					    "corrupt", name);
485 					goto out;
486 				}
487 				if (*cert_forced_command != NULL) {
488 					error("Certificate has multiple "
489 					    "force-command options");
490 					xfree(command);
491 					goto out;
492 				}
493 				*cert_forced_command = command;
494 				found = 1;
495 			}
496 			if (strcmp(name, "source-address") == 0) {
497 				if ((allowed = buffer_get_cstring_ret(&data,
498 				    &clen)) == NULL) {
499 					error("Certificate constraint "
500 					    "\"%s\" corrupt", name);
501 					goto out;
502 				}
503 				if ((*cert_source_address_done)++) {
504 					error("Certificate has multiple "
505 					    "source-address options");
506 					xfree(allowed);
507 					goto out;
508 				}
509 				remote_ip = get_remote_ipaddr();
510 				switch (addr_match_cidr_list(remote_ip,
511 				    allowed)) {
512 				case 1:
513 					/* accepted */
514 					xfree(allowed);
515 					break;
516 				case 0:
517 					/* no match */
518 					logit("Authentication tried for %.100s "
519 					    "with valid certificate but not "
520 					    "from a permitted host "
521 					    "(ip=%.200s).", pw->pw_name,
522 					    remote_ip);
523 					auth_debug_add("Your address '%.200s' "
524 					    "is not permitted to use this "
525 					    "certificate for login.",
526 					    remote_ip);
527 					xfree(allowed);
528 					goto out;
529 				case -1:
530 					error("Certificate source-address "
531 					    "contents invalid");
532 					xfree(allowed);
533 					goto out;
534 				}
535 				found = 1;
536 			}
537 		}
538 
539 		if (!found) {
540 			if (crit) {
541 				error("Certificate critical option \"%s\" "
542 				    "is not supported", name);
543 				goto out;
544 			} else {
545 				logit("Certificate extension \"%s\" "
546 				    "is not supported", name);
547 			}
548 		} else if (buffer_len(&data) != 0) {
549 			error("Certificate option \"%s\" corrupt "
550 			    "(extra data)", name);
551 			goto out;
552 		}
553 		buffer_clear(&data);
554 		xfree(name);
555 		xfree(data_blob);
556 		name = data_blob = NULL;
557 	}
558 	/* successfully parsed all options */
559 	ret = 0;
560 
561  out:
562 	if (ret != 0 &&
563 	    cert_forced_command != NULL &&
564 	    *cert_forced_command != NULL) {
565 		xfree(*cert_forced_command);
566 		*cert_forced_command = NULL;
567 	}
568 	if (name != NULL)
569 		xfree(name);
570 	if (data_blob != NULL)
571 		xfree(data_blob);
572 	buffer_free(&data);
573 	buffer_free(&c);
574 	return ret;
575 }
576 
577 /*
578  * Set options from critical certificate options. These supersede user key
579  * options so this must be called after auth_parse_options().
580  */
581 int
582 auth_cert_options(Key *k, struct passwd *pw)
583 {
584 	int cert_no_port_forwarding_flag = 1;
585 	int cert_no_agent_forwarding_flag = 1;
586 	int cert_no_x11_forwarding_flag = 1;
587 	int cert_no_pty_flag = 1;
588 	int cert_no_user_rc = 1;
589 	char *cert_forced_command = NULL;
590 	int cert_source_address_done = 0;
591 
592 	if (key_cert_is_legacy(k)) {
593 		/* All options are in the one field for v00 certs */
594 		if (parse_option_list(buffer_ptr(&k->cert->critical),
595 		    buffer_len(&k->cert->critical), pw,
596 		    OPTIONS_CRITICAL|OPTIONS_EXTENSIONS, 1,
597 		    &cert_no_port_forwarding_flag,
598 		    &cert_no_agent_forwarding_flag,
599 		    &cert_no_x11_forwarding_flag,
600 		    &cert_no_pty_flag,
601 		    &cert_no_user_rc,
602 		    &cert_forced_command,
603 		    &cert_source_address_done) == -1)
604 			return -1;
605 	} else {
606 		/* Separate options and extensions for v01 certs */
607 		if (parse_option_list(buffer_ptr(&k->cert->critical),
608 		    buffer_len(&k->cert->critical), pw,
609 		    OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL,
610 		    &cert_forced_command,
611 		    &cert_source_address_done) == -1)
612 			return -1;
613 		if (parse_option_list(buffer_ptr(&k->cert->extensions),
614 		    buffer_len(&k->cert->extensions), pw,
615 		    OPTIONS_EXTENSIONS, 1,
616 		    &cert_no_port_forwarding_flag,
617 		    &cert_no_agent_forwarding_flag,
618 		    &cert_no_x11_forwarding_flag,
619 		    &cert_no_pty_flag,
620 		    &cert_no_user_rc,
621 		    NULL, NULL) == -1)
622 			return -1;
623 	}
624 
625 	no_port_forwarding_flag |= cert_no_port_forwarding_flag;
626 	no_agent_forwarding_flag |= cert_no_agent_forwarding_flag;
627 	no_x11_forwarding_flag |= cert_no_x11_forwarding_flag;
628 	no_pty_flag |= cert_no_pty_flag;
629 	no_user_rc |= cert_no_user_rc;
630 	/* CA-specified forced command supersedes key option */
631 	if (cert_forced_command != NULL) {
632 		if (forced_command != NULL)
633 			xfree(forced_command);
634 		forced_command = cert_forced_command;
635 	}
636 	return 0;
637 }
638 
639