1262566Sdes/* $OpenBSD: auth-options.c,v 1.62 2013/12/19 00:27:57 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"
2465668Skris#include "xmalloc.h"
2565668Skris#include "match.h"
2676259Sgreen#include "log.h"
2776259Sgreen#include "canohost.h"
28162852Sdes#include "buffer.h"
2976259Sgreen#include "channels.h"
3076259Sgreen#include "servconf.h"
3192555Sdes#include "misc.h"
32162852Sdes#include "key.h"
33215116Sdes#include "auth-options.h"
34162852Sdes#include "hostfile.h"
35162852Sdes#include "auth.h"
3665668Skris
3765668Skris/* Flags set authorized_keys flags */
3865668Skrisint no_port_forwarding_flag = 0;
3965668Skrisint no_agent_forwarding_flag = 0;
4065668Skrisint no_x11_forwarding_flag = 0;
4165668Skrisint no_pty_flag = 0;
42181111Sdesint no_user_rc = 0;
43204917Sdesint key_is_cert_authority = 0;
4465668Skris
4565668Skris/* "command=" option. */
4665668Skrischar *forced_command = NULL;
4765668Skris
4865668Skris/* "environment=" options. */
4965668Skrisstruct envstring *custom_environment = NULL;
5065668Skris
51157016Sdes/* "tunnel=" option. */
52157016Sdesint forced_tun_device = -1;
53157016Sdes
54215116Sdes/* "principals=" option. */
55215116Sdeschar *authorized_principals = NULL;
56215116Sdes
5776259Sgreenextern ServerOptions options;
5876259Sgreen
5969587Sgreenvoid
6069587Sgreenauth_clear_options(void)
6169587Sgreen{
6269587Sgreen	no_agent_forwarding_flag = 0;
6369587Sgreen	no_port_forwarding_flag = 0;
6469587Sgreen	no_pty_flag = 0;
6569587Sgreen	no_x11_forwarding_flag = 0;
66181111Sdes	no_user_rc = 0;
67204917Sdes	key_is_cert_authority = 0;
6869587Sgreen	while (custom_environment) {
6969587Sgreen		struct envstring *ce = custom_environment;
7069587Sgreen		custom_environment = ce->next;
71255767Sdes		free(ce->s);
72255767Sdes		free(ce);
7369587Sgreen	}
7469587Sgreen	if (forced_command) {
75255767Sdes		free(forced_command);
7669587Sgreen		forced_command = NULL;
7769587Sgreen	}
78215116Sdes	if (authorized_principals) {
79255767Sdes		free(authorized_principals);
80215116Sdes		authorized_principals = NULL;
81215116Sdes	}
82157016Sdes	forced_tun_device = -1;
8376259Sgreen	channel_clear_permitted_opens();
8469587Sgreen}
8569587Sgreen
8676259Sgreen/*
8776259Sgreen * return 1 if access is granted, 0 if not.
8876259Sgreen * side effect: sets key option flags
8976259Sgreen */
9065668Skrisint
9176259Sgreenauth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
9265668Skris{
9365668Skris	const char *cp;
9476259Sgreen	int i;
9569587Sgreen
9669587Sgreen	/* reset options */
9769587Sgreen	auth_clear_options();
9869587Sgreen
9976259Sgreen	if (!opts)
10076259Sgreen		return 1;
10176259Sgreen
10276259Sgreen	while (*opts && *opts != ' ' && *opts != '\t') {
103204917Sdes		cp = "cert-authority";
104204917Sdes		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
105204917Sdes			key_is_cert_authority = 1;
106204917Sdes			opts += strlen(cp);
107204917Sdes			goto next_option;
108204917Sdes		}
10965668Skris		cp = "no-port-forwarding";
11076259Sgreen		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
11198675Sdes			auth_debug_add("Port forwarding disabled.");
11265668Skris			no_port_forwarding_flag = 1;
11376259Sgreen			opts += strlen(cp);
11465668Skris			goto next_option;
11565668Skris		}
11665668Skris		cp = "no-agent-forwarding";
11776259Sgreen		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
11898675Sdes			auth_debug_add("Agent forwarding disabled.");
11965668Skris			no_agent_forwarding_flag = 1;
12076259Sgreen			opts += strlen(cp);
12165668Skris			goto next_option;
12265668Skris		}
12365668Skris		cp = "no-X11-forwarding";
12476259Sgreen		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
12598675Sdes			auth_debug_add("X11 forwarding disabled.");
12665668Skris			no_x11_forwarding_flag = 1;
12776259Sgreen			opts += strlen(cp);
12865668Skris			goto next_option;
12965668Skris		}
13065668Skris		cp = "no-pty";
13176259Sgreen		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
13298675Sdes			auth_debug_add("Pty allocation disabled.");
13365668Skris			no_pty_flag = 1;
13476259Sgreen			opts += strlen(cp);
13565668Skris			goto next_option;
13665668Skris		}
137181111Sdes		cp = "no-user-rc";
138181111Sdes		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
139181111Sdes			auth_debug_add("User rc file execution disabled.");
140181111Sdes			no_user_rc = 1;
141181111Sdes			opts += strlen(cp);
142181111Sdes			goto next_option;
143181111Sdes		}
14465668Skris		cp = "command=\"";
14576259Sgreen		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
14676259Sgreen			opts += strlen(cp);
147215116Sdes			if (forced_command != NULL)
148255767Sdes				free(forced_command);
14976259Sgreen			forced_command = xmalloc(strlen(opts) + 1);
15065668Skris			i = 0;
15176259Sgreen			while (*opts) {
15276259Sgreen				if (*opts == '"')
15365668Skris					break;
15476259Sgreen				if (*opts == '\\' && opts[1] == '"') {
15576259Sgreen					opts += 2;
15665668Skris					forced_command[i++] = '"';
15765668Skris					continue;
15865668Skris				}
15976259Sgreen				forced_command[i++] = *opts++;
16065668Skris			}
16176259Sgreen			if (!*opts) {
16265668Skris				debug("%.100s, line %lu: missing end quote",
16376259Sgreen				    file, linenum);
16498675Sdes				auth_debug_add("%.100s, line %lu: missing end quote",
16576259Sgreen				    file, linenum);
166255767Sdes				free(forced_command);
16776259Sgreen				forced_command = NULL;
16876259Sgreen				goto bad_option;
16965668Skris			}
170162852Sdes			forced_command[i] = '\0';
171221420Sdes			auth_debug_add("Forced command.");
17276259Sgreen			opts++;
17365668Skris			goto next_option;
17465668Skris		}
175215116Sdes		cp = "principals=\"";
176215116Sdes		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
177215116Sdes			opts += strlen(cp);
178215116Sdes			if (authorized_principals != NULL)
179255767Sdes				free(authorized_principals);
180215116Sdes			authorized_principals = xmalloc(strlen(opts) + 1);
181215116Sdes			i = 0;
182215116Sdes			while (*opts) {
183215116Sdes				if (*opts == '"')
184215116Sdes					break;
185215116Sdes				if (*opts == '\\' && opts[1] == '"') {
186215116Sdes					opts += 2;
187215116Sdes					authorized_principals[i++] = '"';
188215116Sdes					continue;
189215116Sdes				}
190215116Sdes				authorized_principals[i++] = *opts++;
191215116Sdes			}
192215116Sdes			if (!*opts) {
193215116Sdes				debug("%.100s, line %lu: missing end quote",
194215116Sdes				    file, linenum);
195215116Sdes				auth_debug_add("%.100s, line %lu: missing end quote",
196215116Sdes				    file, linenum);
197255767Sdes				free(authorized_principals);
198215116Sdes				authorized_principals = NULL;
199215116Sdes				goto bad_option;
200215116Sdes			}
201215116Sdes			authorized_principals[i] = '\0';
202215116Sdes			auth_debug_add("principals: %.900s",
203215116Sdes			    authorized_principals);
204215116Sdes			opts++;
205215116Sdes			goto next_option;
206215116Sdes		}
20765668Skris		cp = "environment=\"";
208106121Sdes		if (options.permit_user_env &&
209106121Sdes		    strncasecmp(opts, cp, strlen(cp)) == 0) {
21065668Skris			char *s;
21165668Skris			struct envstring *new_envstring;
21276259Sgreen
21376259Sgreen			opts += strlen(cp);
21476259Sgreen			s = xmalloc(strlen(opts) + 1);
21565668Skris			i = 0;
21676259Sgreen			while (*opts) {
21776259Sgreen				if (*opts == '"')
21865668Skris					break;
21976259Sgreen				if (*opts == '\\' && opts[1] == '"') {
22076259Sgreen					opts += 2;
22165668Skris					s[i++] = '"';
22265668Skris					continue;
22365668Skris				}
22476259Sgreen				s[i++] = *opts++;
22565668Skris			}
22676259Sgreen			if (!*opts) {
22765668Skris				debug("%.100s, line %lu: missing end quote",
22876259Sgreen				    file, linenum);
22998675Sdes				auth_debug_add("%.100s, line %lu: missing end quote",
23076259Sgreen				    file, linenum);
231255767Sdes				free(s);
23276259Sgreen				goto bad_option;
23365668Skris			}
234162852Sdes			s[i] = '\0';
23598675Sdes			auth_debug_add("Adding to environment: %.900s", s);
23665668Skris			debug("Adding to environment: %.900s", s);
23776259Sgreen			opts++;
238258343Sdes			new_envstring = xcalloc(1, sizeof(struct envstring));
23965668Skris			new_envstring->s = s;
24065668Skris			new_envstring->next = custom_environment;
24165668Skris			custom_environment = new_envstring;
24265668Skris			goto next_option;
24365668Skris		}
24465668Skris		cp = "from=\"";
24576259Sgreen		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
24676259Sgreen			const char *remote_ip = get_remote_ipaddr();
24776259Sgreen			const char *remote_host = get_canonical_hostname(
248124208Sdes			    options.use_dns);
24976259Sgreen			char *patterns = xmalloc(strlen(opts) + 1);
25076259Sgreen
25176259Sgreen			opts += strlen(cp);
25265668Skris			i = 0;
25376259Sgreen			while (*opts) {
25476259Sgreen				if (*opts == '"')
25565668Skris					break;
25676259Sgreen				if (*opts == '\\' && opts[1] == '"') {
25776259Sgreen					opts += 2;
25865668Skris					patterns[i++] = '"';
25965668Skris					continue;
26065668Skris				}
26176259Sgreen				patterns[i++] = *opts++;
26265668Skris			}
26376259Sgreen			if (!*opts) {
26465668Skris				debug("%.100s, line %lu: missing end quote",
26576259Sgreen				    file, linenum);
26698675Sdes				auth_debug_add("%.100s, line %lu: missing end quote",
26776259Sgreen				    file, linenum);
268255767Sdes				free(patterns);
26976259Sgreen				goto bad_option;
27065668Skris			}
271162852Sdes			patterns[i] = '\0';
27276259Sgreen			opts++;
273181111Sdes			switch (match_host_and_ip(remote_host, remote_ip,
274181111Sdes			    patterns)) {
275181111Sdes			case 1:
276255767Sdes				free(patterns);
277181111Sdes				/* Host name matches. */
278181111Sdes				goto next_option;
279181111Sdes			case -1:
280181111Sdes				debug("%.100s, line %lu: invalid criteria",
281181111Sdes				    file, linenum);
282181111Sdes				auth_debug_add("%.100s, line %lu: "
283181111Sdes				    "invalid criteria", file, linenum);
284181111Sdes				/* FALLTHROUGH */
285181111Sdes			case 0:
286255767Sdes				free(patterns);
287124208Sdes				logit("Authentication tried for %.100s with "
28876259Sgreen				    "correct key but not from a permitted "
28976259Sgreen				    "host (host=%.200s, ip=%.200s).",
29076259Sgreen				    pw->pw_name, remote_host, remote_ip);
29198675Sdes				auth_debug_add("Your host '%.200s' is not "
29276259Sgreen				    "permitted to use this key for login.",
29376259Sgreen				    remote_host);
294181111Sdes				break;
29565668Skris			}
296181111Sdes			/* deny access */
297181111Sdes			return 0;
29865668Skris		}
29976259Sgreen		cp = "permitopen=\"";
30076259Sgreen		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
301146998Sdes			char *host, *p;
302192595Sdes			int port;
30376259Sgreen			char *patterns = xmalloc(strlen(opts) + 1);
30476259Sgreen
30576259Sgreen			opts += strlen(cp);
30676259Sgreen			i = 0;
30776259Sgreen			while (*opts) {
30876259Sgreen				if (*opts == '"')
30976259Sgreen					break;
31076259Sgreen				if (*opts == '\\' && opts[1] == '"') {
31176259Sgreen					opts += 2;
31276259Sgreen					patterns[i++] = '"';
31376259Sgreen					continue;
31476259Sgreen				}
31576259Sgreen				patterns[i++] = *opts++;
31676259Sgreen			}
31776259Sgreen			if (!*opts) {
31876259Sgreen				debug("%.100s, line %lu: missing end quote",
31976259Sgreen				    file, linenum);
320146998Sdes				auth_debug_add("%.100s, line %lu: missing "
321146998Sdes				    "end quote", file, linenum);
322255767Sdes				free(patterns);
32376259Sgreen				goto bad_option;
32476259Sgreen			}
325162852Sdes			patterns[i] = '\0';
32676259Sgreen			opts++;
327146998Sdes			p = patterns;
328146998Sdes			host = hpdelim(&p);
329146998Sdes			if (host == NULL || strlen(host) >= NI_MAXHOST) {
330146998Sdes				debug("%.100s, line %lu: Bad permitopen "
331147001Sdes				    "specification <%.100s>", file, linenum,
332146998Sdes				    patterns);
33398675Sdes				auth_debug_add("%.100s, line %lu: "
334146998Sdes				    "Bad permitopen specification", file,
335146998Sdes				    linenum);
336255767Sdes				free(patterns);
33776259Sgreen				goto bad_option;
33876259Sgreen			}
339147001Sdes			host = cleanhostname(host);
340240075Sdes			if (p == NULL || (port = permitopen_port(p)) < 0) {
341146998Sdes				debug("%.100s, line %lu: Bad permitopen port "
342146998Sdes				    "<%.100s>", file, linenum, p ? p : "");
34398675Sdes				auth_debug_add("%.100s, line %lu: "
34492555Sdes				    "Bad permitopen port", file, linenum);
345255767Sdes				free(patterns);
34676259Sgreen				goto bad_option;
34776259Sgreen			}
348248619Sdes			if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0)
34992555Sdes				channel_add_permitted_opens(host, port);
350255767Sdes			free(patterns);
35176259Sgreen			goto next_option;
35276259Sgreen		}
353157016Sdes		cp = "tunnel=\"";
354157016Sdes		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
355157016Sdes			char *tun = NULL;
356157016Sdes			opts += strlen(cp);
357157016Sdes			tun = xmalloc(strlen(opts) + 1);
358157016Sdes			i = 0;
359157016Sdes			while (*opts) {
360157016Sdes				if (*opts == '"')
361157016Sdes					break;
362157016Sdes				tun[i++] = *opts++;
363157016Sdes			}
364157016Sdes			if (!*opts) {
365157016Sdes				debug("%.100s, line %lu: missing end quote",
366157016Sdes				    file, linenum);
367157016Sdes				auth_debug_add("%.100s, line %lu: missing end quote",
368157016Sdes				    file, linenum);
369255767Sdes				free(tun);
370157016Sdes				forced_tun_device = -1;
371157016Sdes				goto bad_option;
372157016Sdes			}
373162852Sdes			tun[i] = '\0';
374157016Sdes			forced_tun_device = a2tun(tun, NULL);
375255767Sdes			free(tun);
376157016Sdes			if (forced_tun_device == SSH_TUNID_ERR) {
377157016Sdes				debug("%.100s, line %lu: invalid tun device",
378157016Sdes				    file, linenum);
379157016Sdes				auth_debug_add("%.100s, line %lu: invalid tun device",
380157016Sdes				    file, linenum);
381157016Sdes				forced_tun_device = -1;
382157016Sdes				goto bad_option;
383157016Sdes			}
384157016Sdes			auth_debug_add("Forced tun device: %d", forced_tun_device);
385157016Sdes			opts++;
386157016Sdes			goto next_option;
387157016Sdes		}
38865668Skrisnext_option:
38965668Skris		/*
39065668Skris		 * Skip the comma, and move to the next option
39165668Skris		 * (or break out if there are no more).
39265668Skris		 */
39376259Sgreen		if (!*opts)
39465668Skris			fatal("Bugs in auth-options.c option processing.");
39576259Sgreen		if (*opts == ' ' || *opts == '\t')
39665668Skris			break;		/* End of options. */
39776259Sgreen		if (*opts != ',')
39865668Skris			goto bad_option;
39976259Sgreen		opts++;
40065668Skris		/* Process the next option. */
40165668Skris	}
40298675Sdes
40365668Skris	/* grant access */
40465668Skris	return 1;
40565668Skris
40665668Skrisbad_option:
407124208Sdes	logit("Bad options in %.100s file, line %lu: %.50s",
40876259Sgreen	    file, linenum, opts);
40998675Sdes	auth_debug_add("Bad options in %.100s file, line %lu: %.50s",
41076259Sgreen	    file, linenum, opts);
41198675Sdes
41265668Skris	/* deny access */
41365668Skris	return 0;
41465668Skris}
415204917Sdes
416215116Sdes#define OPTIONS_CRITICAL	1
417215116Sdes#define OPTIONS_EXTENSIONS	2
418215116Sdesstatic int
419215116Sdesparse_option_list(u_char *optblob, size_t optblob_len, struct passwd *pw,
420215116Sdes    u_int which, int crit,
421215116Sdes    int *cert_no_port_forwarding_flag,
422215116Sdes    int *cert_no_agent_forwarding_flag,
423215116Sdes    int *cert_no_x11_forwarding_flag,
424215116Sdes    int *cert_no_pty_flag,
425215116Sdes    int *cert_no_user_rc,
426215116Sdes    char **cert_forced_command,
427215116Sdes    int *cert_source_address_done)
428204917Sdes{
429215116Sdes	char *command, *allowed;
430215116Sdes	const char *remote_ip;
431255767Sdes	char *name = NULL;
432255767Sdes	u_char *data_blob = NULL;
433204917Sdes	u_int nlen, dlen, clen;
434204917Sdes	Buffer c, data;
435262566Sdes	int ret = -1, result, found;
436204917Sdes
437204917Sdes	buffer_init(&data);
438204917Sdes
439204917Sdes	/* Make copy to avoid altering original */
440204917Sdes	buffer_init(&c);
441215116Sdes	buffer_append(&c, optblob, optblob_len);
442204917Sdes
443204917Sdes	while (buffer_len(&c) > 0) {
444221420Sdes		if ((name = buffer_get_cstring_ret(&c, &nlen)) == NULL ||
445204917Sdes		    (data_blob = buffer_get_string_ret(&c, &dlen)) == NULL) {
446215116Sdes			error("Certificate options corrupt");
447204917Sdes			goto out;
448204917Sdes		}
449204917Sdes		buffer_append(&data, data_blob, dlen);
450215116Sdes		debug3("found certificate option \"%.100s\" len %u",
451204917Sdes		    name, dlen);
452215116Sdes		found = 0;
453215116Sdes		if ((which & OPTIONS_EXTENSIONS) != 0) {
454215116Sdes			if (strcmp(name, "permit-X11-forwarding") == 0) {
455215116Sdes				*cert_no_x11_forwarding_flag = 0;
456215116Sdes				found = 1;
457215116Sdes			} else if (strcmp(name,
458215116Sdes			    "permit-agent-forwarding") == 0) {
459215116Sdes				*cert_no_agent_forwarding_flag = 0;
460215116Sdes				found = 1;
461215116Sdes			} else if (strcmp(name,
462215116Sdes			    "permit-port-forwarding") == 0) {
463215116Sdes				*cert_no_port_forwarding_flag = 0;
464215116Sdes				found = 1;
465215116Sdes			} else if (strcmp(name, "permit-pty") == 0) {
466215116Sdes				*cert_no_pty_flag = 0;
467215116Sdes				found = 1;
468215116Sdes			} else if (strcmp(name, "permit-user-rc") == 0) {
469215116Sdes				*cert_no_user_rc = 0;
470215116Sdes				found = 1;
471204917Sdes			}
472215116Sdes		}
473215116Sdes		if (!found && (which & OPTIONS_CRITICAL) != 0) {
474215116Sdes			if (strcmp(name, "force-command") == 0) {
475221420Sdes				if ((command = buffer_get_cstring_ret(&data,
476215116Sdes				    &clen)) == NULL) {
477215116Sdes					error("Certificate constraint \"%s\" "
478215116Sdes					    "corrupt", name);
479215116Sdes					goto out;
480215116Sdes				}
481215116Sdes				if (*cert_forced_command != NULL) {
482215116Sdes					error("Certificate has multiple "
483215116Sdes					    "force-command options");
484255767Sdes					free(command);
485215116Sdes					goto out;
486215116Sdes				}
487215116Sdes				*cert_forced_command = command;
488215116Sdes				found = 1;
489204917Sdes			}
490215116Sdes			if (strcmp(name, "source-address") == 0) {
491221420Sdes				if ((allowed = buffer_get_cstring_ret(&data,
492215116Sdes				    &clen)) == NULL) {
493215116Sdes					error("Certificate constraint "
494215116Sdes					    "\"%s\" corrupt", name);
495215116Sdes					goto out;
496215116Sdes				}
497215116Sdes				if ((*cert_source_address_done)++) {
498215116Sdes					error("Certificate has multiple "
499215116Sdes					    "source-address options");
500255767Sdes					free(allowed);
501215116Sdes					goto out;
502215116Sdes				}
503215116Sdes				remote_ip = get_remote_ipaddr();
504262566Sdes				result = addr_match_cidr_list(remote_ip,
505262566Sdes				    allowed);
506262566Sdes				free(allowed);
507262566Sdes				switch (result) {
508215116Sdes				case 1:
509215116Sdes					/* accepted */
510215116Sdes					break;
511215116Sdes				case 0:
512215116Sdes					/* no match */
513215116Sdes					logit("Authentication tried for %.100s "
514215116Sdes					    "with valid certificate but not "
515215116Sdes					    "from a permitted host "
516215116Sdes					    "(ip=%.200s).", pw->pw_name,
517215116Sdes					    remote_ip);
518215116Sdes					auth_debug_add("Your address '%.200s' "
519215116Sdes					    "is not permitted to use this "
520215116Sdes					    "certificate for login.",
521215116Sdes					    remote_ip);
522215116Sdes					goto out;
523215116Sdes				case -1:
524262566Sdes				default:
525215116Sdes					error("Certificate source-address "
526215116Sdes					    "contents invalid");
527215116Sdes					goto out;
528215116Sdes				}
529215116Sdes				found = 1;
530204917Sdes			}
531215116Sdes		}
532215116Sdes
533215116Sdes		if (!found) {
534215116Sdes			if (crit) {
535215116Sdes				error("Certificate critical option \"%s\" "
536215116Sdes				    "is not supported", name);
537204917Sdes				goto out;
538215116Sdes			} else {
539215116Sdes				logit("Certificate extension \"%s\" "
540215116Sdes				    "is not supported", name);
541204917Sdes			}
542215116Sdes		} else if (buffer_len(&data) != 0) {
543215116Sdes			error("Certificate option \"%s\" corrupt "
544204917Sdes			    "(extra data)", name);
545204917Sdes			goto out;
546204917Sdes		}
547204917Sdes		buffer_clear(&data);
548255767Sdes		free(name);
549255767Sdes		free(data_blob);
550255767Sdes		name = NULL;
551255767Sdes		data_blob = NULL;
552204917Sdes	}
553215116Sdes	/* successfully parsed all options */
554204917Sdes	ret = 0;
555204917Sdes
556215116Sdes out:
557215116Sdes	if (ret != 0 &&
558215116Sdes	    cert_forced_command != NULL &&
559215116Sdes	    *cert_forced_command != NULL) {
560255767Sdes		free(*cert_forced_command);
561215116Sdes		*cert_forced_command = NULL;
562215116Sdes	}
563215116Sdes	if (name != NULL)
564255767Sdes		free(name);
565215116Sdes	if (data_blob != NULL)
566255767Sdes		free(data_blob);
567215116Sdes	buffer_free(&data);
568215116Sdes	buffer_free(&c);
569215116Sdes	return ret;
570215116Sdes}
571215116Sdes
572215116Sdes/*
573215116Sdes * Set options from critical certificate options. These supersede user key
574215116Sdes * options so this must be called after auth_parse_options().
575215116Sdes */
576215116Sdesint
577215116Sdesauth_cert_options(Key *k, struct passwd *pw)
578215116Sdes{
579215116Sdes	int cert_no_port_forwarding_flag = 1;
580215116Sdes	int cert_no_agent_forwarding_flag = 1;
581215116Sdes	int cert_no_x11_forwarding_flag = 1;
582215116Sdes	int cert_no_pty_flag = 1;
583215116Sdes	int cert_no_user_rc = 1;
584215116Sdes	char *cert_forced_command = NULL;
585215116Sdes	int cert_source_address_done = 0;
586215116Sdes
587215116Sdes	if (key_cert_is_legacy(k)) {
588215116Sdes		/* All options are in the one field for v00 certs */
589215116Sdes		if (parse_option_list(buffer_ptr(&k->cert->critical),
590215116Sdes		    buffer_len(&k->cert->critical), pw,
591215116Sdes		    OPTIONS_CRITICAL|OPTIONS_EXTENSIONS, 1,
592215116Sdes		    &cert_no_port_forwarding_flag,
593215116Sdes		    &cert_no_agent_forwarding_flag,
594215116Sdes		    &cert_no_x11_forwarding_flag,
595215116Sdes		    &cert_no_pty_flag,
596215116Sdes		    &cert_no_user_rc,
597215116Sdes		    &cert_forced_command,
598215116Sdes		    &cert_source_address_done) == -1)
599215116Sdes			return -1;
600215116Sdes	} else {
601215116Sdes		/* Separate options and extensions for v01 certs */
602215116Sdes		if (parse_option_list(buffer_ptr(&k->cert->critical),
603215116Sdes		    buffer_len(&k->cert->critical), pw,
604215116Sdes		    OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL,
605215116Sdes		    &cert_forced_command,
606215116Sdes		    &cert_source_address_done) == -1)
607215116Sdes			return -1;
608215116Sdes		if (parse_option_list(buffer_ptr(&k->cert->extensions),
609215116Sdes		    buffer_len(&k->cert->extensions), pw,
610215116Sdes		    OPTIONS_EXTENSIONS, 1,
611215116Sdes		    &cert_no_port_forwarding_flag,
612215116Sdes		    &cert_no_agent_forwarding_flag,
613215116Sdes		    &cert_no_x11_forwarding_flag,
614215116Sdes		    &cert_no_pty_flag,
615215116Sdes		    &cert_no_user_rc,
616215116Sdes		    NULL, NULL) == -1)
617215116Sdes			return -1;
618215116Sdes	}
619215116Sdes
620204917Sdes	no_port_forwarding_flag |= cert_no_port_forwarding_flag;
621204917Sdes	no_agent_forwarding_flag |= cert_no_agent_forwarding_flag;
622204917Sdes	no_x11_forwarding_flag |= cert_no_x11_forwarding_flag;
623204917Sdes	no_pty_flag |= cert_no_pty_flag;
624204917Sdes	no_user_rc |= cert_no_user_rc;
625204917Sdes	/* CA-specified forced command supersedes key option */
626204917Sdes	if (cert_forced_command != NULL) {
627204917Sdes		if (forced_command != NULL)
628255767Sdes			free(forced_command);
629204917Sdes		forced_command = cert_forced_command;
630204917Sdes	}
631215116Sdes	return 0;
632204917Sdes}
633204917Sdes
634