xref: /netbsd-src/crypto/external/bsd/openssh/dist/auth-options.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: auth-options.c,v 1.7 2013/11/08 19:18:24 christos Exp $	*/
2 /* $OpenBSD: auth-options.c,v 1.59.2.1 2013/11/08 01:33:56 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.7 2013/11/08 19:18:24 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 		free(ce->s);
78 		free(ce);
79 	}
80 	if (forced_command) {
81 		free(forced_command);
82 		forced_command = NULL;
83 	}
84 	if (authorized_principals) {
85 		free(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 				free(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 				free(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 				free(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 				free(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 				free(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 = xcalloc(1, 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 				free(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 				free(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 				free(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 				free(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 				free(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 				free(patterns);
353 				goto bad_option;
354 			}
355 			if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0)
356 				channel_add_permitted_opens(host, port);
357 			free(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 				free(tun);
377 				forced_tun_device = -1;
378 				goto bad_option;
379 			}
380 			tun[i] = '\0';
381 			forced_tun_device = a2tun(tun, NULL);
382 			free(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 	char *name = NULL;
439 	u_char *data_blob = NULL;
440 	u_int nlen, dlen, clen;
441 	Buffer c, data;
442 	int ret = -1, found;
443 
444 	buffer_init(&data);
445 
446 	/* Make copy to avoid altering original */
447 	buffer_init(&c);
448 	buffer_append(&c, optblob, optblob_len);
449 
450 	while (buffer_len(&c) > 0) {
451 		if ((name = buffer_get_cstring_ret(&c, &nlen)) == NULL ||
452 		    (data_blob = buffer_get_string_ret(&c, &dlen)) == NULL) {
453 			error("Certificate options corrupt");
454 			goto out;
455 		}
456 		buffer_append(&data, data_blob, dlen);
457 		debug3("found certificate option \"%.100s\" len %u",
458 		    name, dlen);
459 		found = 0;
460 		if ((which & OPTIONS_EXTENSIONS) != 0) {
461 			if (strcmp(name, "permit-X11-forwarding") == 0) {
462 				*cert_no_x11_forwarding_flag = 0;
463 				found = 1;
464 			} else if (strcmp(name,
465 			    "permit-agent-forwarding") == 0) {
466 				*cert_no_agent_forwarding_flag = 0;
467 				found = 1;
468 			} else if (strcmp(name,
469 			    "permit-port-forwarding") == 0) {
470 				*cert_no_port_forwarding_flag = 0;
471 				found = 1;
472 			} else if (strcmp(name, "permit-pty") == 0) {
473 				*cert_no_pty_flag = 0;
474 				found = 1;
475 			} else if (strcmp(name, "permit-user-rc") == 0) {
476 				*cert_no_user_rc = 0;
477 				found = 1;
478 			}
479 		}
480 		if (!found && (which & OPTIONS_CRITICAL) != 0) {
481 			if (strcmp(name, "force-command") == 0) {
482 				if ((command = buffer_get_cstring_ret(&data,
483 				    &clen)) == NULL) {
484 					error("Certificate constraint \"%s\" "
485 					    "corrupt", name);
486 					goto out;
487 				}
488 				if (*cert_forced_command != NULL) {
489 					error("Certificate has multiple "
490 					    "force-command options");
491 					free(command);
492 					goto out;
493 				}
494 				*cert_forced_command = command;
495 				found = 1;
496 			}
497 			if (strcmp(name, "source-address") == 0) {
498 				if ((allowed = buffer_get_cstring_ret(&data,
499 				    &clen)) == NULL) {
500 					error("Certificate constraint "
501 					    "\"%s\" corrupt", name);
502 					goto out;
503 				}
504 				if ((*cert_source_address_done)++) {
505 					error("Certificate has multiple "
506 					    "source-address options");
507 					free(allowed);
508 					goto out;
509 				}
510 				remote_ip = get_remote_ipaddr();
511 				switch (addr_match_cidr_list(remote_ip,
512 				    allowed)) {
513 				case 1:
514 					/* accepted */
515 					free(allowed);
516 					break;
517 				case 0:
518 					/* no match */
519 					logit("Authentication tried for %.100s "
520 					    "with valid certificate but not "
521 					    "from a permitted host "
522 					    "(ip=%.200s).", pw->pw_name,
523 					    remote_ip);
524 					auth_debug_add("Your address '%.200s' "
525 					    "is not permitted to use this "
526 					    "certificate for login.",
527 					    remote_ip);
528 					free(allowed);
529 					goto out;
530 				case -1:
531 					error("Certificate source-address "
532 					    "contents invalid");
533 					free(allowed);
534 					goto out;
535 				}
536 				found = 1;
537 			}
538 		}
539 
540 		if (!found) {
541 			if (crit) {
542 				error("Certificate critical option \"%s\" "
543 				    "is not supported", name);
544 				goto out;
545 			} else {
546 				logit("Certificate extension \"%s\" "
547 				    "is not supported", name);
548 			}
549 		} else if (buffer_len(&data) != 0) {
550 			error("Certificate option \"%s\" corrupt "
551 			    "(extra data)", name);
552 			goto out;
553 		}
554 		buffer_clear(&data);
555 		free(name);
556 		free(data_blob);
557 		name = NULL;
558 		data_blob = NULL;
559 	}
560 	/* successfully parsed all options */
561 	ret = 0;
562 
563  out:
564 	if (ret != 0 &&
565 	    cert_forced_command != NULL &&
566 	    *cert_forced_command != NULL) {
567 		free(*cert_forced_command);
568 		*cert_forced_command = NULL;
569 	}
570 	if (name != NULL)
571 		free(name);
572 	if (data_blob != NULL)
573 		free(data_blob);
574 	buffer_free(&data);
575 	buffer_free(&c);
576 	return ret;
577 }
578 
579 /*
580  * Set options from critical certificate options. These supersede user key
581  * options so this must be called after auth_parse_options().
582  */
583 int
584 auth_cert_options(Key *k, struct passwd *pw)
585 {
586 	int cert_no_port_forwarding_flag = 1;
587 	int cert_no_agent_forwarding_flag = 1;
588 	int cert_no_x11_forwarding_flag = 1;
589 	int cert_no_pty_flag = 1;
590 	int cert_no_user_rc = 1;
591 	char *cert_forced_command = NULL;
592 	int cert_source_address_done = 0;
593 
594 	if (key_cert_is_legacy(k)) {
595 		/* All options are in the one field for v00 certs */
596 		if (parse_option_list(buffer_ptr(&k->cert->critical),
597 		    buffer_len(&k->cert->critical), pw,
598 		    OPTIONS_CRITICAL|OPTIONS_EXTENSIONS, 1,
599 		    &cert_no_port_forwarding_flag,
600 		    &cert_no_agent_forwarding_flag,
601 		    &cert_no_x11_forwarding_flag,
602 		    &cert_no_pty_flag,
603 		    &cert_no_user_rc,
604 		    &cert_forced_command,
605 		    &cert_source_address_done) == -1)
606 			return -1;
607 	} else {
608 		/* Separate options and extensions for v01 certs */
609 		if (parse_option_list(buffer_ptr(&k->cert->critical),
610 		    buffer_len(&k->cert->critical), pw,
611 		    OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL,
612 		    &cert_forced_command,
613 		    &cert_source_address_done) == -1)
614 			return -1;
615 		if (parse_option_list(buffer_ptr(&k->cert->extensions),
616 		    buffer_len(&k->cert->extensions), pw,
617 		    OPTIONS_EXTENSIONS, 1,
618 		    &cert_no_port_forwarding_flag,
619 		    &cert_no_agent_forwarding_flag,
620 		    &cert_no_x11_forwarding_flag,
621 		    &cert_no_pty_flag,
622 		    &cert_no_user_rc,
623 		    NULL, NULL) == -1)
624 			return -1;
625 	}
626 
627 	no_port_forwarding_flag |= cert_no_port_forwarding_flag;
628 	no_agent_forwarding_flag |= cert_no_agent_forwarding_flag;
629 	no_x11_forwarding_flag |= cert_no_x11_forwarding_flag;
630 	no_pty_flag |= cert_no_pty_flag;
631 	no_user_rc |= cert_no_user_rc;
632 	/* CA-specified forced command supersedes key option */
633 	if (cert_forced_command != NULL) {
634 		if (forced_command != NULL)
635 			free(forced_command);
636 		forced_command = cert_forced_command;
637 	}
638 	return 0;
639 }
640 
641