• Home
  • History
  • Annotate
  • only in this directory
1323124Sdes/* $OpenBSD: auth-options.c,v 1.71 2016/03/07 19:02:43 djm Exp $ */
265668Skris/*
365668Skris * Author: Tatu Ylonen <ylo@cs.hut.fi>
465668Skris * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
565668Skris *                    All rights reserved
665668Skris * As far as I am concerned, the code I have written for this software
765668Skris * can be used freely for any purpose.  Any derived versions of this
865668Skris * software must be clearly marked as such, and if the derived work is
965668Skris * incompatible with the protocol description in the RFC file, it must be
1065668Skris * called by a name other than "ssh" or "Secure Shell".
1165668Skris */
1265668Skris
1365668Skris#include "includes.h"
1465668Skris
15162852Sdes#include <sys/types.h>
16162852Sdes
17162852Sdes#include <netdb.h>
18162852Sdes#include <pwd.h>
19162852Sdes#include <string.h>
20162852Sdes#include <stdio.h>
21162852Sdes#include <stdarg.h>
22162852Sdes
23181111Sdes#include "openbsd-compat/sys-queue.h"
24295367Sdes
25295367Sdes#include "key.h"	/* XXX for typedef */
26295367Sdes#include "buffer.h"	/* XXX for typedef */
2765668Skris#include "xmalloc.h"
2865668Skris#include "match.h"
29295367Sdes#include "ssherr.h"
3076259Sgreen#include "log.h"
3176259Sgreen#include "canohost.h"
32323124Sdes#include "packet.h"
33295367Sdes#include "sshbuf.h"
34295367Sdes#include "misc.h"
3576259Sgreen#include "channels.h"
3676259Sgreen#include "servconf.h"
37295367Sdes#include "sshkey.h"
38215116Sdes#include "auth-options.h"
39162852Sdes#include "hostfile.h"
40162852Sdes#include "auth.h"
4165668Skris
4265668Skris/* Flags set authorized_keys flags */
4365668Skrisint no_port_forwarding_flag = 0;
4465668Skrisint no_agent_forwarding_flag = 0;
4565668Skrisint no_x11_forwarding_flag = 0;
4665668Skrisint no_pty_flag = 0;
47181111Sdesint no_user_rc = 0;
48204917Sdesint key_is_cert_authority = 0;
4965668Skris
5065668Skris/* "command=" option. */
5165668Skrischar *forced_command = NULL;
5265668Skris
5365668Skris/* "environment=" options. */
5465668Skrisstruct envstring *custom_environment = NULL;
5565668Skris
56157016Sdes/* "tunnel=" option. */
57157016Sdesint forced_tun_device = -1;
58157016Sdes
59215116Sdes/* "principals=" option. */
60215116Sdeschar *authorized_principals = NULL;
61215116Sdes
6276259Sgreenextern ServerOptions options;
6376259Sgreen
6469587Sgreenvoid
6569587Sgreenauth_clear_options(void)
6669587Sgreen{
6769587Sgreen	no_agent_forwarding_flag = 0;
6869587Sgreen	no_port_forwarding_flag = 0;
6969587Sgreen	no_pty_flag = 0;
7069587Sgreen	no_x11_forwarding_flag = 0;
71181111Sdes	no_user_rc = 0;
72204917Sdes	key_is_cert_authority = 0;
7369587Sgreen	while (custom_environment) {
7469587Sgreen		struct envstring *ce = custom_environment;
7569587Sgreen		custom_environment = ce->next;
76255767Sdes		free(ce->s);
77255767Sdes		free(ce);
7869587Sgreen	}
79296781Sdes	free(forced_command);
80296781Sdes	forced_command = NULL;
81296781Sdes	free(authorized_principals);
82296781Sdes	authorized_principals = NULL;
83157016Sdes	forced_tun_device = -1;
8476259Sgreen	channel_clear_permitted_opens();
8569587Sgreen}
8669587Sgreen
8776259Sgreen/*
88296781Sdes * Match flag 'opt' in *optsp, and if allow_negate is set then also match
89296781Sdes * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0
90296781Sdes * if negated option matches.
91296781Sdes * If the option or negated option matches, then *optsp is updated to
92296781Sdes * point to the first character after the option and, if 'msg' is not NULL
93296781Sdes * then a message based on it added via auth_debug_add().
94296781Sdes */
95296781Sdesstatic int
96296781Sdesmatch_flag(const char *opt, int allow_negate, char **optsp, const char *msg)
97296781Sdes{
98296781Sdes	size_t opt_len = strlen(opt);
99296781Sdes	char *opts = *optsp;
100296781Sdes	int negate = 0;
101296781Sdes
102296781Sdes	if (allow_negate && strncasecmp(opts, "no-", 3) == 0) {
103296781Sdes		opts += 3;
104296781Sdes		negate = 1;
105296781Sdes	}
106296781Sdes	if (strncasecmp(opts, opt, opt_len) == 0) {
107296781Sdes		*optsp = opts + opt_len;
108296781Sdes		if (msg != NULL) {
109296781Sdes			auth_debug_add("%s %s.", msg,
110296781Sdes			    negate ? "disabled" : "enabled");
111296781Sdes		}
112296781Sdes		return negate ? 0 : 1;
113296781Sdes	}
114296781Sdes	return -1;
115296781Sdes}
116296781Sdes
117296781Sdes/*
11876259Sgreen * return 1 if access is granted, 0 if not.
11976259Sgreen * side effect: sets key option flags
12076259Sgreen */
12165668Skrisint
12276259Sgreenauth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
12365668Skris{
124323124Sdes	struct ssh *ssh = active_state;		/* XXX */
12565668Skris	const char *cp;
126296781Sdes	int i, r;
12769587Sgreen
12869587Sgreen	/* reset options */
12969587Sgreen	auth_clear_options();
13069587Sgreen
13176259Sgreen	if (!opts)
13276259Sgreen		return 1;
13376259Sgreen
13476259Sgreen	while (*opts && *opts != ' ' && *opts != '\t') {
135296781Sdes		if ((r = match_flag("cert-authority", 0, &opts, NULL)) != -1) {
136296781Sdes			key_is_cert_authority = r;
137204917Sdes			goto next_option;
138204917Sdes		}
139296781Sdes		if ((r = match_flag("restrict", 0, &opts, NULL)) != -1) {
140296781Sdes			auth_debug_add("Key is restricted.");
14165668Skris			no_port_forwarding_flag = 1;
142296781Sdes			no_agent_forwarding_flag = 1;
143296781Sdes			no_x11_forwarding_flag = 1;
144296781Sdes			no_pty_flag = 1;
145296781Sdes			no_user_rc = 1;
14665668Skris			goto next_option;
14765668Skris		}
148296781Sdes		if ((r = match_flag("port-forwarding", 1, &opts,
149296781Sdes		    "Port forwarding")) != -1) {
150296781Sdes			no_port_forwarding_flag = r != 1;
15165668Skris			goto next_option;
15265668Skris		}
153296781Sdes		if ((r = match_flag("agent-forwarding", 1, &opts,
154296781Sdes		    "Agent forwarding")) != -1) {
155296781Sdes			no_agent_forwarding_flag = r != 1;
15665668Skris			goto next_option;
15765668Skris		}
158296781Sdes		if ((r = match_flag("x11-forwarding", 1, &opts,
159296781Sdes		    "X11 forwarding")) != -1) {
160296781Sdes			no_x11_forwarding_flag = r != 1;
16165668Skris			goto next_option;
16265668Skris		}
163296781Sdes		if ((r = match_flag("pty", 1, &opts,
164296781Sdes		    "PTY allocation")) != -1) {
165296781Sdes			no_pty_flag = r != 1;
166181111Sdes			goto next_option;
167181111Sdes		}
168296781Sdes		if ((r = match_flag("user-rc", 1, &opts,
169296781Sdes		    "User rc execution")) != -1) {
170296781Sdes			no_user_rc = r != 1;
171296781Sdes			goto next_option;
172296781Sdes		}
17365668Skris		cp = "command=\"";
17476259Sgreen		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
17576259Sgreen			opts += strlen(cp);
176296781Sdes			free(forced_command);
17776259Sgreen			forced_command = xmalloc(strlen(opts) + 1);
17865668Skris			i = 0;
17976259Sgreen			while (*opts) {
18076259Sgreen				if (*opts == '"')
18165668Skris					break;
18276259Sgreen				if (*opts == '\\' && opts[1] == '"') {
18376259Sgreen					opts += 2;
18465668Skris					forced_command[i++] = '"';
18565668Skris					continue;
18665668Skris				}
18776259Sgreen				forced_command[i++] = *opts++;
18865668Skris			}
18976259Sgreen			if (!*opts) {
19065668Skris				debug("%.100s, line %lu: missing end quote",
19176259Sgreen				    file, linenum);
19298675Sdes				auth_debug_add("%.100s, line %lu: missing end quote",
19376259Sgreen				    file, linenum);
194255767Sdes				free(forced_command);
19576259Sgreen				forced_command = NULL;
19676259Sgreen				goto bad_option;
19765668Skris			}
198162852Sdes			forced_command[i] = '\0';
199221420Sdes			auth_debug_add("Forced command.");
20076259Sgreen			opts++;
20165668Skris			goto next_option;
20265668Skris		}
203215116Sdes		cp = "principals=\"";
204215116Sdes		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
205215116Sdes			opts += strlen(cp);
206296781Sdes			free(authorized_principals);
207215116Sdes			authorized_principals = xmalloc(strlen(opts) + 1);
208215116Sdes			i = 0;
209215116Sdes			while (*opts) {
210215116Sdes				if (*opts == '"')
211215116Sdes					break;
212215116Sdes				if (*opts == '\\' && opts[1] == '"') {
213215116Sdes					opts += 2;
214215116Sdes					authorized_principals[i++] = '"';
215215116Sdes					continue;
216215116Sdes				}
217215116Sdes				authorized_principals[i++] = *opts++;
218215116Sdes			}
219215116Sdes			if (!*opts) {
220215116Sdes				debug("%.100s, line %lu: missing end quote",
221215116Sdes				    file, linenum);
222215116Sdes				auth_debug_add("%.100s, line %lu: missing end quote",
223215116Sdes				    file, linenum);
224255767Sdes				free(authorized_principals);
225215116Sdes				authorized_principals = NULL;
226215116Sdes				goto bad_option;
227215116Sdes			}
228215116Sdes			authorized_principals[i] = '\0';
229215116Sdes			auth_debug_add("principals: %.900s",
230215116Sdes			    authorized_principals);
231215116Sdes			opts++;
232215116Sdes			goto next_option;
233215116Sdes		}
23465668Skris		cp = "environment=\"";
235295367Sdes		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
23665668Skris			char *s;
23765668Skris			struct envstring *new_envstring;
23876259Sgreen
23976259Sgreen			opts += strlen(cp);
24076259Sgreen			s = xmalloc(strlen(opts) + 1);
24165668Skris			i = 0;
24276259Sgreen			while (*opts) {
24376259Sgreen				if (*opts == '"')
24465668Skris					break;
24576259Sgreen				if (*opts == '\\' && opts[1] == '"') {
24676259Sgreen					opts += 2;
24765668Skris					s[i++] = '"';
24865668Skris					continue;
24965668Skris				}
25076259Sgreen				s[i++] = *opts++;
25165668Skris			}
25276259Sgreen			if (!*opts) {
25365668Skris				debug("%.100s, line %lu: missing end quote",
25476259Sgreen				    file, linenum);
25598675Sdes				auth_debug_add("%.100s, line %lu: missing end quote",
25676259Sgreen				    file, linenum);
257255767Sdes				free(s);
25876259Sgreen				goto bad_option;
25965668Skris			}
260162852Sdes			s[i] = '\0';
26176259Sgreen			opts++;
262295367Sdes			if (options.permit_user_env) {
263295367Sdes				auth_debug_add("Adding to environment: "
264295367Sdes				    "%.900s", s);
265295367Sdes				debug("Adding to environment: %.900s", s);
266295367Sdes				new_envstring = xcalloc(1,
267295367Sdes				    sizeof(*new_envstring));
268295367Sdes				new_envstring->s = s;
269295367Sdes				new_envstring->next = custom_environment;
270295367Sdes				custom_environment = new_envstring;
271295367Sdes				s = NULL;
272295367Sdes			}
273295367Sdes			free(s);
27465668Skris			goto next_option;
27565668Skris		}
27665668Skris		cp = "from=\"";
27776259Sgreen		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
278323124Sdes			const char *remote_ip = ssh_remote_ipaddr(ssh);
279323124Sdes			const char *remote_host = auth_get_canonical_hostname(
280323124Sdes			    ssh, options.use_dns);
28176259Sgreen			char *patterns = xmalloc(strlen(opts) + 1);
28276259Sgreen
28376259Sgreen			opts += strlen(cp);
28465668Skris			i = 0;
28576259Sgreen			while (*opts) {
28676259Sgreen				if (*opts == '"')
28765668Skris					break;
28876259Sgreen				if (*opts == '\\' && opts[1] == '"') {
28976259Sgreen					opts += 2;
29065668Skris					patterns[i++] = '"';
29165668Skris					continue;
29265668Skris				}
29376259Sgreen				patterns[i++] = *opts++;
29465668Skris			}
29576259Sgreen			if (!*opts) {
29665668Skris				debug("%.100s, line %lu: missing end quote",
29776259Sgreen				    file, linenum);
29898675Sdes				auth_debug_add("%.100s, line %lu: missing end quote",
29976259Sgreen				    file, linenum);
300255767Sdes				free(patterns);
30176259Sgreen				goto bad_option;
30265668Skris			}
303162852Sdes			patterns[i] = '\0';
30476259Sgreen			opts++;
305181111Sdes			switch (match_host_and_ip(remote_host, remote_ip,
306181111Sdes			    patterns)) {
307181111Sdes			case 1:
308255767Sdes				free(patterns);
309181111Sdes				/* Host name matches. */
310181111Sdes				goto next_option;
311181111Sdes			case -1:
312181111Sdes				debug("%.100s, line %lu: invalid criteria",
313181111Sdes				    file, linenum);
314181111Sdes				auth_debug_add("%.100s, line %lu: "
315181111Sdes				    "invalid criteria", file, linenum);
316181111Sdes				/* FALLTHROUGH */
317181111Sdes			case 0:
318255767Sdes				free(patterns);
319124208Sdes				logit("Authentication tried for %.100s with "
32076259Sgreen				    "correct key but not from a permitted "
32176259Sgreen				    "host (host=%.200s, ip=%.200s).",
32276259Sgreen				    pw->pw_name, remote_host, remote_ip);
32398675Sdes				auth_debug_add("Your host '%.200s' is not "
32476259Sgreen				    "permitted to use this key for login.",
32576259Sgreen				    remote_host);
326181111Sdes				break;
32765668Skris			}
328181111Sdes			/* deny access */
329181111Sdes			return 0;
33065668Skris		}
33176259Sgreen		cp = "permitopen=\"";
33276259Sgreen		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
333146998Sdes			char *host, *p;
334192595Sdes			int port;
33576259Sgreen			char *patterns = xmalloc(strlen(opts) + 1);
33676259Sgreen
33776259Sgreen			opts += strlen(cp);
33876259Sgreen			i = 0;
33976259Sgreen			while (*opts) {
34076259Sgreen				if (*opts == '"')
34176259Sgreen					break;
34276259Sgreen				if (*opts == '\\' && opts[1] == '"') {
34376259Sgreen					opts += 2;
34476259Sgreen					patterns[i++] = '"';
34576259Sgreen					continue;
34676259Sgreen				}
34776259Sgreen				patterns[i++] = *opts++;
34876259Sgreen			}
34976259Sgreen			if (!*opts) {
35076259Sgreen				debug("%.100s, line %lu: missing end quote",
35176259Sgreen				    file, linenum);
352146998Sdes				auth_debug_add("%.100s, line %lu: missing "
353146998Sdes				    "end quote", file, linenum);
354255767Sdes				free(patterns);
35576259Sgreen				goto bad_option;
35676259Sgreen			}
357162852Sdes			patterns[i] = '\0';
35876259Sgreen			opts++;
359146998Sdes			p = patterns;
360295367Sdes			/* XXX - add streamlocal support */
361146998Sdes			host = hpdelim(&p);
362146998Sdes			if (host == NULL || strlen(host) >= NI_MAXHOST) {
363146998Sdes				debug("%.100s, line %lu: Bad permitopen "
364147001Sdes				    "specification <%.100s>", file, linenum,
365146998Sdes				    patterns);
36698675Sdes				auth_debug_add("%.100s, line %lu: "
367146998Sdes				    "Bad permitopen specification", file,
368146998Sdes				    linenum);
369255767Sdes				free(patterns);
37076259Sgreen				goto bad_option;
37176259Sgreen			}
372147001Sdes			host = cleanhostname(host);
373240075Sdes			if (p == NULL || (port = permitopen_port(p)) < 0) {
374146998Sdes				debug("%.100s, line %lu: Bad permitopen port "
375146998Sdes				    "<%.100s>", file, linenum, p ? p : "");
37698675Sdes				auth_debug_add("%.100s, line %lu: "
37792555Sdes				    "Bad permitopen port", file, linenum);
378255767Sdes				free(patterns);
37976259Sgreen				goto bad_option;
38076259Sgreen			}
381248619Sdes			if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0)
38292555Sdes				channel_add_permitted_opens(host, port);
383255767Sdes			free(patterns);
38476259Sgreen			goto next_option;
38576259Sgreen		}
386157016Sdes		cp = "tunnel=\"";
387157016Sdes		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
388157016Sdes			char *tun = NULL;
389157016Sdes			opts += strlen(cp);
390157016Sdes			tun = xmalloc(strlen(opts) + 1);
391157016Sdes			i = 0;
392157016Sdes			while (*opts) {
393157016Sdes				if (*opts == '"')
394157016Sdes					break;
395157016Sdes				tun[i++] = *opts++;
396157016Sdes			}
397157016Sdes			if (!*opts) {
398157016Sdes				debug("%.100s, line %lu: missing end quote",
399157016Sdes				    file, linenum);
400157016Sdes				auth_debug_add("%.100s, line %lu: missing end quote",
401157016Sdes				    file, linenum);
402255767Sdes				free(tun);
403157016Sdes				forced_tun_device = -1;
404157016Sdes				goto bad_option;
405157016Sdes			}
406162852Sdes			tun[i] = '\0';
407157016Sdes			forced_tun_device = a2tun(tun, NULL);
408255767Sdes			free(tun);
409157016Sdes			if (forced_tun_device == SSH_TUNID_ERR) {
410157016Sdes				debug("%.100s, line %lu: invalid tun device",
411157016Sdes				    file, linenum);
412157016Sdes				auth_debug_add("%.100s, line %lu: invalid tun device",
413157016Sdes				    file, linenum);
414157016Sdes				forced_tun_device = -1;
415157016Sdes				goto bad_option;
416157016Sdes			}
417157016Sdes			auth_debug_add("Forced tun device: %d", forced_tun_device);
418157016Sdes			opts++;
419157016Sdes			goto next_option;
420157016Sdes		}
42165668Skrisnext_option:
42265668Skris		/*
42365668Skris		 * Skip the comma, and move to the next option
42465668Skris		 * (or break out if there are no more).
42565668Skris		 */
42676259Sgreen		if (!*opts)
42765668Skris			fatal("Bugs in auth-options.c option processing.");
42876259Sgreen		if (*opts == ' ' || *opts == '\t')
42965668Skris			break;		/* End of options. */
43076259Sgreen		if (*opts != ',')
43165668Skris			goto bad_option;
43276259Sgreen		opts++;
43365668Skris		/* Process the next option. */
43465668Skris	}
43598675Sdes
43665668Skris	/* grant access */
43765668Skris	return 1;
43865668Skris
43965668Skrisbad_option:
440124208Sdes	logit("Bad options in %.100s file, line %lu: %.50s",
44176259Sgreen	    file, linenum, opts);
44298675Sdes	auth_debug_add("Bad options in %.100s file, line %lu: %.50s",
44376259Sgreen	    file, linenum, opts);
44498675Sdes
44565668Skris	/* deny access */
44665668Skris	return 0;
44765668Skris}
448204917Sdes
449215116Sdes#define OPTIONS_CRITICAL	1
450215116Sdes#define OPTIONS_EXTENSIONS	2
451215116Sdesstatic int
452295367Sdesparse_option_list(struct sshbuf *oblob, struct passwd *pw,
453215116Sdes    u_int which, int crit,
454215116Sdes    int *cert_no_port_forwarding_flag,
455215116Sdes    int *cert_no_agent_forwarding_flag,
456215116Sdes    int *cert_no_x11_forwarding_flag,
457215116Sdes    int *cert_no_pty_flag,
458215116Sdes    int *cert_no_user_rc,
459215116Sdes    char **cert_forced_command,
460215116Sdes    int *cert_source_address_done)
461204917Sdes{
462323124Sdes	struct ssh *ssh = active_state;		/* XXX */
463215116Sdes	char *command, *allowed;
464215116Sdes	const char *remote_ip;
465255767Sdes	char *name = NULL;
466295367Sdes	struct sshbuf *c = NULL, *data = NULL;
467295367Sdes	int r, ret = -1, result, found;
468204917Sdes
469295367Sdes	if ((c = sshbuf_fromb(oblob)) == NULL) {
470295367Sdes		error("%s: sshbuf_fromb failed", __func__);
471295367Sdes		goto out;
472295367Sdes	}
473204917Sdes
474295367Sdes	while (sshbuf_len(c) > 0) {
475295367Sdes		sshbuf_free(data);
476295367Sdes		data = NULL;
477295367Sdes		if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 ||
478295367Sdes		    (r = sshbuf_froms(c, &data)) != 0) {
479295367Sdes			error("Unable to parse certificate options: %s",
480295367Sdes			    ssh_err(r));
481204917Sdes			goto out;
482204917Sdes		}
483295367Sdes		debug3("found certificate option \"%.100s\" len %zu",
484295367Sdes		    name, sshbuf_len(data));
485215116Sdes		found = 0;
486215116Sdes		if ((which & OPTIONS_EXTENSIONS) != 0) {
487215116Sdes			if (strcmp(name, "permit-X11-forwarding") == 0) {
488215116Sdes				*cert_no_x11_forwarding_flag = 0;
489215116Sdes				found = 1;
490215116Sdes			} else if (strcmp(name,
491215116Sdes			    "permit-agent-forwarding") == 0) {
492215116Sdes				*cert_no_agent_forwarding_flag = 0;
493215116Sdes				found = 1;
494215116Sdes			} else if (strcmp(name,
495215116Sdes			    "permit-port-forwarding") == 0) {
496215116Sdes				*cert_no_port_forwarding_flag = 0;
497215116Sdes				found = 1;
498215116Sdes			} else if (strcmp(name, "permit-pty") == 0) {
499215116Sdes				*cert_no_pty_flag = 0;
500215116Sdes				found = 1;
501215116Sdes			} else if (strcmp(name, "permit-user-rc") == 0) {
502215116Sdes				*cert_no_user_rc = 0;
503215116Sdes				found = 1;
504204917Sdes			}
505215116Sdes		}
506215116Sdes		if (!found && (which & OPTIONS_CRITICAL) != 0) {
507215116Sdes			if (strcmp(name, "force-command") == 0) {
508295367Sdes				if ((r = sshbuf_get_cstring(data, &command,
509295367Sdes				    NULL)) != 0) {
510295367Sdes					error("Unable to parse \"%s\" "
511295367Sdes					    "section: %s", name, ssh_err(r));
512215116Sdes					goto out;
513215116Sdes				}
514215116Sdes				if (*cert_forced_command != NULL) {
515215116Sdes					error("Certificate has multiple "
516215116Sdes					    "force-command options");
517255767Sdes					free(command);
518215116Sdes					goto out;
519215116Sdes				}
520215116Sdes				*cert_forced_command = command;
521215116Sdes				found = 1;
522204917Sdes			}
523215116Sdes			if (strcmp(name, "source-address") == 0) {
524295367Sdes				if ((r = sshbuf_get_cstring(data, &allowed,
525295367Sdes				    NULL)) != 0) {
526295367Sdes					error("Unable to parse \"%s\" "
527295367Sdes					    "section: %s", name, ssh_err(r));
528215116Sdes					goto out;
529215116Sdes				}
530215116Sdes				if ((*cert_source_address_done)++) {
531215116Sdes					error("Certificate has multiple "
532215116Sdes					    "source-address options");
533255767Sdes					free(allowed);
534215116Sdes					goto out;
535215116Sdes				}
536323124Sdes				remote_ip = ssh_remote_ipaddr(ssh);
537262566Sdes				result = addr_match_cidr_list(remote_ip,
538262566Sdes				    allowed);
539262566Sdes				free(allowed);
540262566Sdes				switch (result) {
541215116Sdes				case 1:
542215116Sdes					/* accepted */
543215116Sdes					break;
544215116Sdes				case 0:
545215116Sdes					/* no match */
546215116Sdes					logit("Authentication tried for %.100s "
547215116Sdes					    "with valid certificate but not "
548215116Sdes					    "from a permitted host "
549215116Sdes					    "(ip=%.200s).", pw->pw_name,
550215116Sdes					    remote_ip);
551215116Sdes					auth_debug_add("Your address '%.200s' "
552215116Sdes					    "is not permitted to use this "
553215116Sdes					    "certificate for login.",
554215116Sdes					    remote_ip);
555215116Sdes					goto out;
556215116Sdes				case -1:
557262566Sdes				default:
558215116Sdes					error("Certificate source-address "
559215116Sdes					    "contents invalid");
560215116Sdes					goto out;
561215116Sdes				}
562215116Sdes				found = 1;
563204917Sdes			}
564215116Sdes		}
565215116Sdes
566215116Sdes		if (!found) {
567215116Sdes			if (crit) {
568215116Sdes				error("Certificate critical option \"%s\" "
569215116Sdes				    "is not supported", name);
570204917Sdes				goto out;
571215116Sdes			} else {
572215116Sdes				logit("Certificate extension \"%s\" "
573215116Sdes				    "is not supported", name);
574204917Sdes			}
575295367Sdes		} else if (sshbuf_len(data) != 0) {
576215116Sdes			error("Certificate option \"%s\" corrupt "
577204917Sdes			    "(extra data)", name);
578204917Sdes			goto out;
579204917Sdes		}
580255767Sdes		free(name);
581255767Sdes		name = NULL;
582204917Sdes	}
583215116Sdes	/* successfully parsed all options */
584204917Sdes	ret = 0;
585204917Sdes
586215116Sdes out:
587215116Sdes	if (ret != 0 &&
588215116Sdes	    cert_forced_command != NULL &&
589215116Sdes	    *cert_forced_command != NULL) {
590255767Sdes		free(*cert_forced_command);
591215116Sdes		*cert_forced_command = NULL;
592215116Sdes	}
593296781Sdes	free(name);
594295367Sdes	sshbuf_free(data);
595295367Sdes	sshbuf_free(c);
596215116Sdes	return ret;
597215116Sdes}
598215116Sdes
599215116Sdes/*
600215116Sdes * Set options from critical certificate options. These supersede user key
601215116Sdes * options so this must be called after auth_parse_options().
602215116Sdes */
603215116Sdesint
604295367Sdesauth_cert_options(struct sshkey *k, struct passwd *pw)
605215116Sdes{
606215116Sdes	int cert_no_port_forwarding_flag = 1;
607215116Sdes	int cert_no_agent_forwarding_flag = 1;
608215116Sdes	int cert_no_x11_forwarding_flag = 1;
609215116Sdes	int cert_no_pty_flag = 1;
610215116Sdes	int cert_no_user_rc = 1;
611215116Sdes	char *cert_forced_command = NULL;
612215116Sdes	int cert_source_address_done = 0;
613215116Sdes
614295367Sdes	/* Separate options and extensions for v01 certs */
615295367Sdes	if (parse_option_list(k->cert->critical, pw,
616295367Sdes	    OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL,
617295367Sdes	    &cert_forced_command,
618295367Sdes	    &cert_source_address_done) == -1)
619295367Sdes		return -1;
620295367Sdes	if (parse_option_list(k->cert->extensions, pw,
621295367Sdes	    OPTIONS_EXTENSIONS, 0,
622295367Sdes	    &cert_no_port_forwarding_flag,
623295367Sdes	    &cert_no_agent_forwarding_flag,
624295367Sdes	    &cert_no_x11_forwarding_flag,
625295367Sdes	    &cert_no_pty_flag,
626295367Sdes	    &cert_no_user_rc,
627295367Sdes	    NULL, NULL) == -1)
628295367Sdes		return -1;
629215116Sdes
630204917Sdes	no_port_forwarding_flag |= cert_no_port_forwarding_flag;
631204917Sdes	no_agent_forwarding_flag |= cert_no_agent_forwarding_flag;
632204917Sdes	no_x11_forwarding_flag |= cert_no_x11_forwarding_flag;
633204917Sdes	no_pty_flag |= cert_no_pty_flag;
634204917Sdes	no_user_rc |= cert_no_user_rc;
635204917Sdes	/* CA-specified forced command supersedes key option */
636204917Sdes	if (cert_forced_command != NULL) {
637296781Sdes		free(forced_command);
638204917Sdes		forced_command = cert_forced_command;
639204917Sdes	}
640215116Sdes	return 0;
641204917Sdes}
642204917Sdes
643