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