xref: /freebsd/crypto/openssh/auth-options.c (revision ca86bcf2)
1ca86bcf2SDag-Erling Smørgrav /* $OpenBSD: auth-options.c,v 1.72 2016/11/30 02:57:40 djm Exp $ */
2b66f2d16SKris Kennaway /*
3b66f2d16SKris Kennaway  * Author: Tatu Ylonen <ylo@cs.hut.fi>
4b66f2d16SKris Kennaway  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5b66f2d16SKris Kennaway  *                    All rights reserved
6b66f2d16SKris Kennaway  * As far as I am concerned, the code I have written for this software
7b66f2d16SKris Kennaway  * can be used freely for any purpose.  Any derived versions of this
8b66f2d16SKris Kennaway  * software must be clearly marked as such, and if the derived work is
9b66f2d16SKris Kennaway  * incompatible with the protocol description in the RFC file, it must be
10b66f2d16SKris Kennaway  * called by a name other than "ssh" or "Secure Shell".
11b66f2d16SKris Kennaway  */
12b66f2d16SKris Kennaway 
13b66f2d16SKris Kennaway #include "includes.h"
14761efaa7SDag-Erling Smørgrav 
15761efaa7SDag-Erling Smørgrav #include <sys/types.h>
16761efaa7SDag-Erling Smørgrav 
17761efaa7SDag-Erling Smørgrav #include <netdb.h>
18761efaa7SDag-Erling Smørgrav #include <pwd.h>
19761efaa7SDag-Erling Smørgrav #include <string.h>
20761efaa7SDag-Erling Smørgrav #include <stdio.h>
21761efaa7SDag-Erling Smørgrav #include <stdarg.h>
22b66f2d16SKris Kennaway 
23d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h"
24bc5531deSDag-Erling Smørgrav 
25bc5531deSDag-Erling Smørgrav #include "key.h"	/* XXX for typedef */
26bc5531deSDag-Erling Smørgrav #include "buffer.h"	/* XXX for typedef */
27b66f2d16SKris Kennaway #include "xmalloc.h"
28b66f2d16SKris Kennaway #include "match.h"
29bc5531deSDag-Erling Smørgrav #include "ssherr.h"
301e8db6e2SBrian Feldman #include "log.h"
311e8db6e2SBrian Feldman #include "canohost.h"
32076ad2f8SDag-Erling Smørgrav #include "packet.h"
33bc5531deSDag-Erling Smørgrav #include "sshbuf.h"
34a0ee8cc6SDag-Erling Smørgrav #include "misc.h"
351e8db6e2SBrian Feldman #include "channels.h"
361e8db6e2SBrian Feldman #include "servconf.h"
37bc5531deSDag-Erling Smørgrav #include "sshkey.h"
38e2f6069cSDag-Erling Smørgrav #include "auth-options.h"
39761efaa7SDag-Erling Smørgrav #include "hostfile.h"
40545d5ecaSDag-Erling Smørgrav #include "auth.h"
41b66f2d16SKris Kennaway 
42b66f2d16SKris Kennaway /* Flags set authorized_keys flags */
43b66f2d16SKris Kennaway int no_port_forwarding_flag = 0;
44b66f2d16SKris Kennaway int no_agent_forwarding_flag = 0;
45b66f2d16SKris Kennaway int no_x11_forwarding_flag = 0;
46b66f2d16SKris Kennaway int no_pty_flag = 0;
47d4af9e69SDag-Erling Smørgrav int no_user_rc = 0;
48b15c8340SDag-Erling Smørgrav int key_is_cert_authority = 0;
49b66f2d16SKris Kennaway 
50b66f2d16SKris Kennaway /* "command=" option. */
51b66f2d16SKris Kennaway char *forced_command = NULL;
52b66f2d16SKris Kennaway 
53b66f2d16SKris Kennaway /* "environment=" options. */
54b66f2d16SKris Kennaway struct envstring *custom_environment = NULL;
55b66f2d16SKris Kennaway 
56021d409fSDag-Erling Smørgrav /* "tunnel=" option. */
57021d409fSDag-Erling Smørgrav int forced_tun_device = -1;
58021d409fSDag-Erling Smørgrav 
59e2f6069cSDag-Erling Smørgrav /* "principals=" option. */
60e2f6069cSDag-Erling Smørgrav char *authorized_principals = NULL;
61e2f6069cSDag-Erling Smørgrav 
621e8db6e2SBrian Feldman extern ServerOptions options;
631e8db6e2SBrian Feldman 
645b9b2fafSBrian Feldman void
655b9b2fafSBrian Feldman auth_clear_options(void)
665b9b2fafSBrian Feldman {
675b9b2fafSBrian Feldman 	no_agent_forwarding_flag = 0;
685b9b2fafSBrian Feldman 	no_port_forwarding_flag = 0;
695b9b2fafSBrian Feldman 	no_pty_flag = 0;
705b9b2fafSBrian Feldman 	no_x11_forwarding_flag = 0;
71d4af9e69SDag-Erling Smørgrav 	no_user_rc = 0;
72b15c8340SDag-Erling Smørgrav 	key_is_cert_authority = 0;
735b9b2fafSBrian Feldman 	while (custom_environment) {
745b9b2fafSBrian Feldman 		struct envstring *ce = custom_environment;
755b9b2fafSBrian Feldman 		custom_environment = ce->next;
76e4a9863fSDag-Erling Smørgrav 		free(ce->s);
77e4a9863fSDag-Erling Smørgrav 		free(ce);
785b9b2fafSBrian Feldman 	}
79e4a9863fSDag-Erling Smørgrav 	free(forced_command);
805b9b2fafSBrian Feldman 	forced_command = NULL;
81e4a9863fSDag-Erling Smørgrav 	free(authorized_principals);
82e2f6069cSDag-Erling Smørgrav 	authorized_principals = NULL;
83021d409fSDag-Erling Smørgrav 	forced_tun_device = -1;
841e8db6e2SBrian Feldman 	channel_clear_permitted_opens();
855b9b2fafSBrian Feldman }
865b9b2fafSBrian Feldman 
871e8db6e2SBrian Feldman /*
88acc1a9efSDag-Erling Smørgrav  * Match flag 'opt' in *optsp, and if allow_negate is set then also match
89acc1a9efSDag-Erling Smørgrav  * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0
90acc1a9efSDag-Erling Smørgrav  * if negated option matches.
91acc1a9efSDag-Erling Smørgrav  * If the option or negated option matches, then *optsp is updated to
92acc1a9efSDag-Erling Smørgrav  * point to the first character after the option and, if 'msg' is not NULL
93acc1a9efSDag-Erling Smørgrav  * then a message based on it added via auth_debug_add().
94acc1a9efSDag-Erling Smørgrav  */
95acc1a9efSDag-Erling Smørgrav static int
96acc1a9efSDag-Erling Smørgrav match_flag(const char *opt, int allow_negate, char **optsp, const char *msg)
97acc1a9efSDag-Erling Smørgrav {
98acc1a9efSDag-Erling Smørgrav 	size_t opt_len = strlen(opt);
99acc1a9efSDag-Erling Smørgrav 	char *opts = *optsp;
100acc1a9efSDag-Erling Smørgrav 	int negate = 0;
101acc1a9efSDag-Erling Smørgrav 
102acc1a9efSDag-Erling Smørgrav 	if (allow_negate && strncasecmp(opts, "no-", 3) == 0) {
103acc1a9efSDag-Erling Smørgrav 		opts += 3;
104acc1a9efSDag-Erling Smørgrav 		negate = 1;
105acc1a9efSDag-Erling Smørgrav 	}
106acc1a9efSDag-Erling Smørgrav 	if (strncasecmp(opts, opt, opt_len) == 0) {
107acc1a9efSDag-Erling Smørgrav 		*optsp = opts + opt_len;
108acc1a9efSDag-Erling Smørgrav 		if (msg != NULL) {
109acc1a9efSDag-Erling Smørgrav 			auth_debug_add("%s %s.", msg,
110acc1a9efSDag-Erling Smørgrav 			    negate ? "disabled" : "enabled");
111acc1a9efSDag-Erling Smørgrav 		}
112acc1a9efSDag-Erling Smørgrav 		return negate ? 0 : 1;
113acc1a9efSDag-Erling Smørgrav 	}
114acc1a9efSDag-Erling Smørgrav 	return -1;
115acc1a9efSDag-Erling Smørgrav }
116acc1a9efSDag-Erling Smørgrav 
117acc1a9efSDag-Erling Smørgrav /*
1181e8db6e2SBrian Feldman  * return 1 if access is granted, 0 if not.
1191e8db6e2SBrian Feldman  * side effect: sets key option flags
1201e8db6e2SBrian Feldman  */
121b66f2d16SKris Kennaway int
1221e8db6e2SBrian Feldman auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
123b66f2d16SKris Kennaway {
124076ad2f8SDag-Erling Smørgrav 	struct ssh *ssh = active_state;		/* XXX */
125b66f2d16SKris Kennaway 	const char *cp;
126acc1a9efSDag-Erling Smørgrav 	int i, r;
1275b9b2fafSBrian Feldman 
1285b9b2fafSBrian Feldman 	/* reset options */
1295b9b2fafSBrian Feldman 	auth_clear_options();
1305b9b2fafSBrian Feldman 
1311e8db6e2SBrian Feldman 	if (!opts)
1321e8db6e2SBrian Feldman 		return 1;
1331e8db6e2SBrian Feldman 
1341e8db6e2SBrian Feldman 	while (*opts && *opts != ' ' && *opts != '\t') {
135acc1a9efSDag-Erling Smørgrav 		if ((r = match_flag("cert-authority", 0, &opts, NULL)) != -1) {
136acc1a9efSDag-Erling Smørgrav 			key_is_cert_authority = r;
137b15c8340SDag-Erling Smørgrav 			goto next_option;
138b15c8340SDag-Erling Smørgrav 		}
139acc1a9efSDag-Erling Smørgrav 		if ((r = match_flag("restrict", 0, &opts, NULL)) != -1) {
140acc1a9efSDag-Erling Smørgrav 			auth_debug_add("Key is restricted.");
141b66f2d16SKris Kennaway 			no_port_forwarding_flag = 1;
142b66f2d16SKris Kennaway 			no_agent_forwarding_flag = 1;
143b66f2d16SKris Kennaway 			no_x11_forwarding_flag = 1;
144b66f2d16SKris Kennaway 			no_pty_flag = 1;
145acc1a9efSDag-Erling Smørgrav 			no_user_rc = 1;
146b66f2d16SKris Kennaway 			goto next_option;
147b66f2d16SKris Kennaway 		}
148acc1a9efSDag-Erling Smørgrav 		if ((r = match_flag("port-forwarding", 1, &opts,
149acc1a9efSDag-Erling Smørgrav 		    "Port forwarding")) != -1) {
150acc1a9efSDag-Erling Smørgrav 			no_port_forwarding_flag = r != 1;
151acc1a9efSDag-Erling Smørgrav 			goto next_option;
152acc1a9efSDag-Erling Smørgrav 		}
153acc1a9efSDag-Erling Smørgrav 		if ((r = match_flag("agent-forwarding", 1, &opts,
154acc1a9efSDag-Erling Smørgrav 		    "Agent forwarding")) != -1) {
155acc1a9efSDag-Erling Smørgrav 			no_agent_forwarding_flag = r != 1;
156acc1a9efSDag-Erling Smørgrav 			goto next_option;
157acc1a9efSDag-Erling Smørgrav 		}
158acc1a9efSDag-Erling Smørgrav 		if ((r = match_flag("x11-forwarding", 1, &opts,
159acc1a9efSDag-Erling Smørgrav 		    "X11 forwarding")) != -1) {
160acc1a9efSDag-Erling Smørgrav 			no_x11_forwarding_flag = r != 1;
161acc1a9efSDag-Erling Smørgrav 			goto next_option;
162acc1a9efSDag-Erling Smørgrav 		}
163acc1a9efSDag-Erling Smørgrav 		if ((r = match_flag("pty", 1, &opts,
164acc1a9efSDag-Erling Smørgrav 		    "PTY allocation")) != -1) {
165acc1a9efSDag-Erling Smørgrav 			no_pty_flag = r != 1;
166acc1a9efSDag-Erling Smørgrav 			goto next_option;
167acc1a9efSDag-Erling Smørgrav 		}
168acc1a9efSDag-Erling Smørgrav 		if ((r = match_flag("user-rc", 1, &opts,
169acc1a9efSDag-Erling Smørgrav 		    "User rc execution")) != -1) {
170acc1a9efSDag-Erling Smørgrav 			no_user_rc = r != 1;
171d4af9e69SDag-Erling Smørgrav 			goto next_option;
172d4af9e69SDag-Erling Smørgrav 		}
173b66f2d16SKris Kennaway 		cp = "command=\"";
1741e8db6e2SBrian Feldman 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1751e8db6e2SBrian Feldman 			opts += strlen(cp);
176e4a9863fSDag-Erling Smørgrav 			free(forced_command);
1771e8db6e2SBrian Feldman 			forced_command = xmalloc(strlen(opts) + 1);
178b66f2d16SKris Kennaway 			i = 0;
1791e8db6e2SBrian Feldman 			while (*opts) {
1801e8db6e2SBrian Feldman 				if (*opts == '"')
181b66f2d16SKris Kennaway 					break;
1821e8db6e2SBrian Feldman 				if (*opts == '\\' && opts[1] == '"') {
1831e8db6e2SBrian Feldman 					opts += 2;
184b66f2d16SKris Kennaway 					forced_command[i++] = '"';
185b66f2d16SKris Kennaway 					continue;
186b66f2d16SKris Kennaway 				}
1871e8db6e2SBrian Feldman 				forced_command[i++] = *opts++;
188b66f2d16SKris Kennaway 			}
1891e8db6e2SBrian Feldman 			if (!*opts) {
190b66f2d16SKris Kennaway 				debug("%.100s, line %lu: missing end quote",
1911e8db6e2SBrian Feldman 				    file, linenum);
192545d5ecaSDag-Erling Smørgrav 				auth_debug_add("%.100s, line %lu: missing end quote",
1931e8db6e2SBrian Feldman 				    file, linenum);
194e4a9863fSDag-Erling Smørgrav 				free(forced_command);
1951e8db6e2SBrian Feldman 				forced_command = NULL;
1961e8db6e2SBrian Feldman 				goto bad_option;
197b66f2d16SKris Kennaway 			}
198761efaa7SDag-Erling Smørgrav 			forced_command[i] = '\0';
1994a421b63SDag-Erling Smørgrav 			auth_debug_add("Forced command.");
2001e8db6e2SBrian Feldman 			opts++;
201b66f2d16SKris Kennaway 			goto next_option;
202b66f2d16SKris Kennaway 		}
203e2f6069cSDag-Erling Smørgrav 		cp = "principals=\"";
204e2f6069cSDag-Erling Smørgrav 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
205e2f6069cSDag-Erling Smørgrav 			opts += strlen(cp);
206e4a9863fSDag-Erling Smørgrav 			free(authorized_principals);
207e2f6069cSDag-Erling Smørgrav 			authorized_principals = xmalloc(strlen(opts) + 1);
208e2f6069cSDag-Erling Smørgrav 			i = 0;
209e2f6069cSDag-Erling Smørgrav 			while (*opts) {
210e2f6069cSDag-Erling Smørgrav 				if (*opts == '"')
211e2f6069cSDag-Erling Smørgrav 					break;
212e2f6069cSDag-Erling Smørgrav 				if (*opts == '\\' && opts[1] == '"') {
213e2f6069cSDag-Erling Smørgrav 					opts += 2;
214e2f6069cSDag-Erling Smørgrav 					authorized_principals[i++] = '"';
215e2f6069cSDag-Erling Smørgrav 					continue;
216e2f6069cSDag-Erling Smørgrav 				}
217e2f6069cSDag-Erling Smørgrav 				authorized_principals[i++] = *opts++;
218e2f6069cSDag-Erling Smørgrav 			}
219e2f6069cSDag-Erling Smørgrav 			if (!*opts) {
220e2f6069cSDag-Erling Smørgrav 				debug("%.100s, line %lu: missing end quote",
221e2f6069cSDag-Erling Smørgrav 				    file, linenum);
222e2f6069cSDag-Erling Smørgrav 				auth_debug_add("%.100s, line %lu: missing end quote",
223e2f6069cSDag-Erling Smørgrav 				    file, linenum);
224e4a9863fSDag-Erling Smørgrav 				free(authorized_principals);
225e2f6069cSDag-Erling Smørgrav 				authorized_principals = NULL;
226e2f6069cSDag-Erling Smørgrav 				goto bad_option;
227e2f6069cSDag-Erling Smørgrav 			}
228e2f6069cSDag-Erling Smørgrav 			authorized_principals[i] = '\0';
229e2f6069cSDag-Erling Smørgrav 			auth_debug_add("principals: %.900s",
230e2f6069cSDag-Erling Smørgrav 			    authorized_principals);
231e2f6069cSDag-Erling Smørgrav 			opts++;
232e2f6069cSDag-Erling Smørgrav 			goto next_option;
233e2f6069cSDag-Erling Smørgrav 		}
234b66f2d16SKris Kennaway 		cp = "environment=\"";
235557f75e5SDag-Erling Smørgrav 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
236b66f2d16SKris Kennaway 			char *s;
237b66f2d16SKris Kennaway 			struct envstring *new_envstring;
2381e8db6e2SBrian Feldman 
2391e8db6e2SBrian Feldman 			opts += strlen(cp);
2401e8db6e2SBrian Feldman 			s = xmalloc(strlen(opts) + 1);
241b66f2d16SKris Kennaway 			i = 0;
2421e8db6e2SBrian Feldman 			while (*opts) {
2431e8db6e2SBrian Feldman 				if (*opts == '"')
244b66f2d16SKris Kennaway 					break;
2451e8db6e2SBrian Feldman 				if (*opts == '\\' && opts[1] == '"') {
2461e8db6e2SBrian Feldman 					opts += 2;
247b66f2d16SKris Kennaway 					s[i++] = '"';
248b66f2d16SKris Kennaway 					continue;
249b66f2d16SKris Kennaway 				}
2501e8db6e2SBrian Feldman 				s[i++] = *opts++;
251b66f2d16SKris Kennaway 			}
2521e8db6e2SBrian Feldman 			if (!*opts) {
253b66f2d16SKris Kennaway 				debug("%.100s, line %lu: missing end quote",
2541e8db6e2SBrian Feldman 				    file, linenum);
255545d5ecaSDag-Erling Smørgrav 				auth_debug_add("%.100s, line %lu: missing end quote",
2561e8db6e2SBrian Feldman 				    file, linenum);
257e4a9863fSDag-Erling Smørgrav 				free(s);
2581e8db6e2SBrian Feldman 				goto bad_option;
259b66f2d16SKris Kennaway 			}
260761efaa7SDag-Erling Smørgrav 			s[i] = '\0';
2611e8db6e2SBrian Feldman 			opts++;
262557f75e5SDag-Erling Smørgrav 			if (options.permit_user_env) {
263557f75e5SDag-Erling Smørgrav 				auth_debug_add("Adding to environment: "
264557f75e5SDag-Erling Smørgrav 				    "%.900s", s);
265557f75e5SDag-Erling Smørgrav 				debug("Adding to environment: %.900s", s);
266557f75e5SDag-Erling Smørgrav 				new_envstring = xcalloc(1,
267557f75e5SDag-Erling Smørgrav 				    sizeof(*new_envstring));
268b66f2d16SKris Kennaway 				new_envstring->s = s;
269b66f2d16SKris Kennaway 				new_envstring->next = custom_environment;
270b66f2d16SKris Kennaway 				custom_environment = new_envstring;
271557f75e5SDag-Erling Smørgrav 				s = NULL;
272557f75e5SDag-Erling Smørgrav 			}
273557f75e5SDag-Erling Smørgrav 			free(s);
274b66f2d16SKris Kennaway 			goto next_option;
275b66f2d16SKris Kennaway 		}
276b66f2d16SKris Kennaway 		cp = "from=\"";
2771e8db6e2SBrian Feldman 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
278076ad2f8SDag-Erling Smørgrav 			const char *remote_ip = ssh_remote_ipaddr(ssh);
279076ad2f8SDag-Erling Smørgrav 			const char *remote_host = auth_get_canonical_hostname(
280076ad2f8SDag-Erling Smørgrav 			    ssh, options.use_dns);
2811e8db6e2SBrian Feldman 			char *patterns = xmalloc(strlen(opts) + 1);
2821e8db6e2SBrian Feldman 
2831e8db6e2SBrian Feldman 			opts += strlen(cp);
284b66f2d16SKris Kennaway 			i = 0;
2851e8db6e2SBrian Feldman 			while (*opts) {
2861e8db6e2SBrian Feldman 				if (*opts == '"')
287b66f2d16SKris Kennaway 					break;
2881e8db6e2SBrian Feldman 				if (*opts == '\\' && opts[1] == '"') {
2891e8db6e2SBrian Feldman 					opts += 2;
290b66f2d16SKris Kennaway 					patterns[i++] = '"';
291b66f2d16SKris Kennaway 					continue;
292b66f2d16SKris Kennaway 				}
2931e8db6e2SBrian Feldman 				patterns[i++] = *opts++;
294b66f2d16SKris Kennaway 			}
2951e8db6e2SBrian Feldman 			if (!*opts) {
296b66f2d16SKris Kennaway 				debug("%.100s, line %lu: missing end quote",
2971e8db6e2SBrian Feldman 				    file, linenum);
298545d5ecaSDag-Erling Smørgrav 				auth_debug_add("%.100s, line %lu: missing end quote",
2991e8db6e2SBrian Feldman 				    file, linenum);
300e4a9863fSDag-Erling Smørgrav 				free(patterns);
3011e8db6e2SBrian Feldman 				goto bad_option;
302b66f2d16SKris Kennaway 			}
303761efaa7SDag-Erling Smørgrav 			patterns[i] = '\0';
3041e8db6e2SBrian Feldman 			opts++;
305d4af9e69SDag-Erling Smørgrav 			switch (match_host_and_ip(remote_host, remote_ip,
306d4af9e69SDag-Erling Smørgrav 			    patterns)) {
307d4af9e69SDag-Erling Smørgrav 			case 1:
308e4a9863fSDag-Erling Smørgrav 				free(patterns);
309d4af9e69SDag-Erling Smørgrav 				/* Host name matches. */
310d4af9e69SDag-Erling Smørgrav 				goto next_option;
311d4af9e69SDag-Erling Smørgrav 			case -1:
312d4af9e69SDag-Erling Smørgrav 				debug("%.100s, line %lu: invalid criteria",
313d4af9e69SDag-Erling Smørgrav 				    file, linenum);
314d4af9e69SDag-Erling Smørgrav 				auth_debug_add("%.100s, line %lu: "
315d4af9e69SDag-Erling Smørgrav 				    "invalid criteria", file, linenum);
316d4af9e69SDag-Erling Smørgrav 				/* FALLTHROUGH */
317d4af9e69SDag-Erling Smørgrav 			case 0:
318e4a9863fSDag-Erling Smørgrav 				free(patterns);
319d95e11bfSDag-Erling Smørgrav 				logit("Authentication tried for %.100s with "
3201e8db6e2SBrian Feldman 				    "correct key but not from a permitted "
3211e8db6e2SBrian Feldman 				    "host (host=%.200s, ip=%.200s).",
3221e8db6e2SBrian Feldman 				    pw->pw_name, remote_host, remote_ip);
323545d5ecaSDag-Erling Smørgrav 				auth_debug_add("Your host '%.200s' is not "
3241e8db6e2SBrian Feldman 				    "permitted to use this key for login.",
3251e8db6e2SBrian Feldman 				    remote_host);
326d4af9e69SDag-Erling Smørgrav 				break;
327d4af9e69SDag-Erling Smørgrav 			}
328b66f2d16SKris Kennaway 			/* deny access */
329b66f2d16SKris Kennaway 			return 0;
330b66f2d16SKris Kennaway 		}
3311e8db6e2SBrian Feldman 		cp = "permitopen=\"";
3321e8db6e2SBrian Feldman 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
3335e8dbd04SDag-Erling Smørgrav 			char *host, *p;
334cce7d346SDag-Erling Smørgrav 			int port;
3351e8db6e2SBrian Feldman 			char *patterns = xmalloc(strlen(opts) + 1);
3361e8db6e2SBrian Feldman 
3371e8db6e2SBrian Feldman 			opts += strlen(cp);
3381e8db6e2SBrian Feldman 			i = 0;
3391e8db6e2SBrian Feldman 			while (*opts) {
3401e8db6e2SBrian Feldman 				if (*opts == '"')
3411e8db6e2SBrian Feldman 					break;
3421e8db6e2SBrian Feldman 				if (*opts == '\\' && opts[1] == '"') {
3431e8db6e2SBrian Feldman 					opts += 2;
3441e8db6e2SBrian Feldman 					patterns[i++] = '"';
3451e8db6e2SBrian Feldman 					continue;
3461e8db6e2SBrian Feldman 				}
3471e8db6e2SBrian Feldman 				patterns[i++] = *opts++;
3481e8db6e2SBrian Feldman 			}
3491e8db6e2SBrian Feldman 			if (!*opts) {
3501e8db6e2SBrian Feldman 				debug("%.100s, line %lu: missing end quote",
3511e8db6e2SBrian Feldman 				    file, linenum);
3525e8dbd04SDag-Erling Smørgrav 				auth_debug_add("%.100s, line %lu: missing "
3535e8dbd04SDag-Erling Smørgrav 				    "end quote", file, linenum);
354e4a9863fSDag-Erling Smørgrav 				free(patterns);
3551e8db6e2SBrian Feldman 				goto bad_option;
3561e8db6e2SBrian Feldman 			}
357761efaa7SDag-Erling Smørgrav 			patterns[i] = '\0';
3581e8db6e2SBrian Feldman 			opts++;
3595e8dbd04SDag-Erling Smørgrav 			p = patterns;
360a0ee8cc6SDag-Erling Smørgrav 			/* XXX - add streamlocal support */
3615e8dbd04SDag-Erling Smørgrav 			host = hpdelim(&p);
3625e8dbd04SDag-Erling Smørgrav 			if (host == NULL || strlen(host) >= NI_MAXHOST) {
3635e8dbd04SDag-Erling Smørgrav 				debug("%.100s, line %lu: Bad permitopen "
3645e8dbd04SDag-Erling Smørgrav 				    "specification <%.100s>", file, linenum,
3655e8dbd04SDag-Erling Smørgrav 				    patterns);
366545d5ecaSDag-Erling Smørgrav 				auth_debug_add("%.100s, line %lu: "
3675e8dbd04SDag-Erling Smørgrav 				    "Bad permitopen specification", file,
3685e8dbd04SDag-Erling Smørgrav 				    linenum);
369e4a9863fSDag-Erling Smørgrav 				free(patterns);
3701e8db6e2SBrian Feldman 				goto bad_option;
3711e8db6e2SBrian Feldman 			}
3725e8dbd04SDag-Erling Smørgrav 			host = cleanhostname(host);
373462c32cbSDag-Erling Smørgrav 			if (p == NULL || (port = permitopen_port(p)) < 0) {
3745e8dbd04SDag-Erling Smørgrav 				debug("%.100s, line %lu: Bad permitopen port "
3755e8dbd04SDag-Erling Smørgrav 				    "<%.100s>", file, linenum, p ? p : "");
376545d5ecaSDag-Erling Smørgrav 				auth_debug_add("%.100s, line %lu: "
377ae1f160dSDag-Erling Smørgrav 				    "Bad permitopen port", file, linenum);
378e4a9863fSDag-Erling Smørgrav 				free(patterns);
3791e8db6e2SBrian Feldman 				goto bad_option;
3801e8db6e2SBrian Feldman 			}
3816888a9beSDag-Erling Smørgrav 			if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0)
382ae1f160dSDag-Erling Smørgrav 				channel_add_permitted_opens(host, port);
383e4a9863fSDag-Erling Smørgrav 			free(patterns);
3841e8db6e2SBrian Feldman 			goto next_option;
3851e8db6e2SBrian Feldman 		}
386021d409fSDag-Erling Smørgrav 		cp = "tunnel=\"";
387021d409fSDag-Erling Smørgrav 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
388021d409fSDag-Erling Smørgrav 			char *tun = NULL;
389021d409fSDag-Erling Smørgrav 			opts += strlen(cp);
390021d409fSDag-Erling Smørgrav 			tun = xmalloc(strlen(opts) + 1);
391021d409fSDag-Erling Smørgrav 			i = 0;
392021d409fSDag-Erling Smørgrav 			while (*opts) {
393021d409fSDag-Erling Smørgrav 				if (*opts == '"')
394021d409fSDag-Erling Smørgrav 					break;
395021d409fSDag-Erling Smørgrav 				tun[i++] = *opts++;
396021d409fSDag-Erling Smørgrav 			}
397021d409fSDag-Erling Smørgrav 			if (!*opts) {
398021d409fSDag-Erling Smørgrav 				debug("%.100s, line %lu: missing end quote",
399021d409fSDag-Erling Smørgrav 				    file, linenum);
400021d409fSDag-Erling Smørgrav 				auth_debug_add("%.100s, line %lu: missing end quote",
401021d409fSDag-Erling Smørgrav 				    file, linenum);
402e4a9863fSDag-Erling Smørgrav 				free(tun);
403021d409fSDag-Erling Smørgrav 				forced_tun_device = -1;
404021d409fSDag-Erling Smørgrav 				goto bad_option;
405021d409fSDag-Erling Smørgrav 			}
406761efaa7SDag-Erling Smørgrav 			tun[i] = '\0';
407021d409fSDag-Erling Smørgrav 			forced_tun_device = a2tun(tun, NULL);
408e4a9863fSDag-Erling Smørgrav 			free(tun);
409021d409fSDag-Erling Smørgrav 			if (forced_tun_device == SSH_TUNID_ERR) {
410021d409fSDag-Erling Smørgrav 				debug("%.100s, line %lu: invalid tun device",
411021d409fSDag-Erling Smørgrav 				    file, linenum);
412021d409fSDag-Erling Smørgrav 				auth_debug_add("%.100s, line %lu: invalid tun device",
413021d409fSDag-Erling Smørgrav 				    file, linenum);
414021d409fSDag-Erling Smørgrav 				forced_tun_device = -1;
415021d409fSDag-Erling Smørgrav 				goto bad_option;
416021d409fSDag-Erling Smørgrav 			}
417021d409fSDag-Erling Smørgrav 			auth_debug_add("Forced tun device: %d", forced_tun_device);
418021d409fSDag-Erling Smørgrav 			opts++;
419021d409fSDag-Erling Smørgrav 			goto next_option;
420021d409fSDag-Erling Smørgrav 		}
421b66f2d16SKris Kennaway next_option:
422b66f2d16SKris Kennaway 		/*
423b66f2d16SKris Kennaway 		 * Skip the comma, and move to the next option
424b66f2d16SKris Kennaway 		 * (or break out if there are no more).
425b66f2d16SKris Kennaway 		 */
4261e8db6e2SBrian Feldman 		if (!*opts)
427b66f2d16SKris Kennaway 			fatal("Bugs in auth-options.c option processing.");
4281e8db6e2SBrian Feldman 		if (*opts == ' ' || *opts == '\t')
429b66f2d16SKris Kennaway 			break;		/* End of options. */
4301e8db6e2SBrian Feldman 		if (*opts != ',')
431b66f2d16SKris Kennaway 			goto bad_option;
4321e8db6e2SBrian Feldman 		opts++;
433b66f2d16SKris Kennaway 		/* Process the next option. */
434b66f2d16SKris Kennaway 	}
435545d5ecaSDag-Erling Smørgrav 
436b66f2d16SKris Kennaway 	/* grant access */
437b66f2d16SKris Kennaway 	return 1;
438b66f2d16SKris Kennaway 
439b66f2d16SKris Kennaway bad_option:
440d95e11bfSDag-Erling Smørgrav 	logit("Bad options in %.100s file, line %lu: %.50s",
4411e8db6e2SBrian Feldman 	    file, linenum, opts);
442545d5ecaSDag-Erling Smørgrav 	auth_debug_add("Bad options in %.100s file, line %lu: %.50s",
4431e8db6e2SBrian Feldman 	    file, linenum, opts);
444545d5ecaSDag-Erling Smørgrav 
445b66f2d16SKris Kennaway 	/* deny access */
446b66f2d16SKris Kennaway 	return 0;
447b66f2d16SKris Kennaway }
448b15c8340SDag-Erling Smørgrav 
449e2f6069cSDag-Erling Smørgrav #define OPTIONS_CRITICAL	1
450e2f6069cSDag-Erling Smørgrav #define OPTIONS_EXTENSIONS	2
451e2f6069cSDag-Erling Smørgrav static int
452bc5531deSDag-Erling Smørgrav parse_option_list(struct sshbuf *oblob, struct passwd *pw,
453e2f6069cSDag-Erling Smørgrav     u_int which, int crit,
454e2f6069cSDag-Erling Smørgrav     int *cert_no_port_forwarding_flag,
455e2f6069cSDag-Erling Smørgrav     int *cert_no_agent_forwarding_flag,
456e2f6069cSDag-Erling Smørgrav     int *cert_no_x11_forwarding_flag,
457e2f6069cSDag-Erling Smørgrav     int *cert_no_pty_flag,
458e2f6069cSDag-Erling Smørgrav     int *cert_no_user_rc,
459e2f6069cSDag-Erling Smørgrav     char **cert_forced_command,
460e2f6069cSDag-Erling Smørgrav     int *cert_source_address_done)
461b15c8340SDag-Erling Smørgrav {
462076ad2f8SDag-Erling Smørgrav 	struct ssh *ssh = active_state;		/* XXX */
463e2f6069cSDag-Erling Smørgrav 	char *command, *allowed;
464e2f6069cSDag-Erling Smørgrav 	const char *remote_ip;
465e4a9863fSDag-Erling Smørgrav 	char *name = NULL;
466bc5531deSDag-Erling Smørgrav 	struct sshbuf *c = NULL, *data = NULL;
467bc5531deSDag-Erling Smørgrav 	int r, ret = -1, result, found;
468b15c8340SDag-Erling Smørgrav 
469bc5531deSDag-Erling Smørgrav 	if ((c = sshbuf_fromb(oblob)) == NULL) {
470bc5531deSDag-Erling Smørgrav 		error("%s: sshbuf_fromb failed", __func__);
471b15c8340SDag-Erling Smørgrav 		goto out;
472b15c8340SDag-Erling Smørgrav 	}
473bc5531deSDag-Erling Smørgrav 
474bc5531deSDag-Erling Smørgrav 	while (sshbuf_len(c) > 0) {
475bc5531deSDag-Erling Smørgrav 		sshbuf_free(data);
476bc5531deSDag-Erling Smørgrav 		data = NULL;
477bc5531deSDag-Erling Smørgrav 		if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 ||
478bc5531deSDag-Erling Smørgrav 		    (r = sshbuf_froms(c, &data)) != 0) {
479bc5531deSDag-Erling Smørgrav 			error("Unable to parse certificate options: %s",
480bc5531deSDag-Erling Smørgrav 			    ssh_err(r));
481bc5531deSDag-Erling Smørgrav 			goto out;
482bc5531deSDag-Erling Smørgrav 		}
483bc5531deSDag-Erling Smørgrav 		debug3("found certificate option \"%.100s\" len %zu",
484bc5531deSDag-Erling Smørgrav 		    name, sshbuf_len(data));
485e2f6069cSDag-Erling Smørgrav 		found = 0;
486e2f6069cSDag-Erling Smørgrav 		if ((which & OPTIONS_EXTENSIONS) != 0) {
487e2f6069cSDag-Erling Smørgrav 			if (strcmp(name, "permit-X11-forwarding") == 0) {
488e2f6069cSDag-Erling Smørgrav 				*cert_no_x11_forwarding_flag = 0;
489e2f6069cSDag-Erling Smørgrav 				found = 1;
490e2f6069cSDag-Erling Smørgrav 			} else if (strcmp(name,
491e2f6069cSDag-Erling Smørgrav 			    "permit-agent-forwarding") == 0) {
492e2f6069cSDag-Erling Smørgrav 				*cert_no_agent_forwarding_flag = 0;
493e2f6069cSDag-Erling Smørgrav 				found = 1;
494e2f6069cSDag-Erling Smørgrav 			} else if (strcmp(name,
495e2f6069cSDag-Erling Smørgrav 			    "permit-port-forwarding") == 0) {
496e2f6069cSDag-Erling Smørgrav 				*cert_no_port_forwarding_flag = 0;
497e2f6069cSDag-Erling Smørgrav 				found = 1;
498e2f6069cSDag-Erling Smørgrav 			} else if (strcmp(name, "permit-pty") == 0) {
499e2f6069cSDag-Erling Smørgrav 				*cert_no_pty_flag = 0;
500e2f6069cSDag-Erling Smørgrav 				found = 1;
501e2f6069cSDag-Erling Smørgrav 			} else if (strcmp(name, "permit-user-rc") == 0) {
502e2f6069cSDag-Erling Smørgrav 				*cert_no_user_rc = 0;
503e2f6069cSDag-Erling Smørgrav 				found = 1;
504e2f6069cSDag-Erling Smørgrav 			}
505e2f6069cSDag-Erling Smørgrav 		}
506e2f6069cSDag-Erling Smørgrav 		if (!found && (which & OPTIONS_CRITICAL) != 0) {
507e2f6069cSDag-Erling Smørgrav 			if (strcmp(name, "force-command") == 0) {
508bc5531deSDag-Erling Smørgrav 				if ((r = sshbuf_get_cstring(data, &command,
509bc5531deSDag-Erling Smørgrav 				    NULL)) != 0) {
510bc5531deSDag-Erling Smørgrav 					error("Unable to parse \"%s\" "
511bc5531deSDag-Erling Smørgrav 					    "section: %s", name, ssh_err(r));
512b15c8340SDag-Erling Smørgrav 					goto out;
513b15c8340SDag-Erling Smørgrav 				}
514e2f6069cSDag-Erling Smørgrav 				if (*cert_forced_command != NULL) {
515b15c8340SDag-Erling Smørgrav 					error("Certificate has multiple "
516e2f6069cSDag-Erling Smørgrav 					    "force-command options");
517e4a9863fSDag-Erling Smørgrav 					free(command);
518b15c8340SDag-Erling Smørgrav 					goto out;
519b15c8340SDag-Erling Smørgrav 				}
520e2f6069cSDag-Erling Smørgrav 				*cert_forced_command = command;
521e2f6069cSDag-Erling Smørgrav 				found = 1;
522e2f6069cSDag-Erling Smørgrav 			}
523e2f6069cSDag-Erling Smørgrav 			if (strcmp(name, "source-address") == 0) {
524bc5531deSDag-Erling Smørgrav 				if ((r = sshbuf_get_cstring(data, &allowed,
525bc5531deSDag-Erling Smørgrav 				    NULL)) != 0) {
526bc5531deSDag-Erling Smørgrav 					error("Unable to parse \"%s\" "
527bc5531deSDag-Erling Smørgrav 					    "section: %s", name, ssh_err(r));
528b15c8340SDag-Erling Smørgrav 					goto out;
529b15c8340SDag-Erling Smørgrav 				}
530e2f6069cSDag-Erling Smørgrav 				if ((*cert_source_address_done)++) {
531b15c8340SDag-Erling Smørgrav 					error("Certificate has multiple "
532e2f6069cSDag-Erling Smørgrav 					    "source-address options");
533e4a9863fSDag-Erling Smørgrav 					free(allowed);
534b15c8340SDag-Erling Smørgrav 					goto out;
535b15c8340SDag-Erling Smørgrav 				}
536076ad2f8SDag-Erling Smørgrav 				remote_ip = ssh_remote_ipaddr(ssh);
537f7167e0eSDag-Erling Smørgrav 				result = addr_match_cidr_list(remote_ip,
538f7167e0eSDag-Erling Smørgrav 				    allowed);
539f7167e0eSDag-Erling Smørgrav 				free(allowed);
540f7167e0eSDag-Erling Smørgrav 				switch (result) {
541b15c8340SDag-Erling Smørgrav 				case 1:
542b15c8340SDag-Erling Smørgrav 					/* accepted */
543b15c8340SDag-Erling Smørgrav 					break;
544b15c8340SDag-Erling Smørgrav 				case 0:
545b15c8340SDag-Erling Smørgrav 					/* no match */
546e2f6069cSDag-Erling Smørgrav 					logit("Authentication tried for %.100s "
547e2f6069cSDag-Erling Smørgrav 					    "with valid certificate but not "
548e2f6069cSDag-Erling Smørgrav 					    "from a permitted host "
549e2f6069cSDag-Erling Smørgrav 					    "(ip=%.200s).", pw->pw_name,
550e2f6069cSDag-Erling Smørgrav 					    remote_ip);
551e2f6069cSDag-Erling Smørgrav 					auth_debug_add("Your address '%.200s' "
552e2f6069cSDag-Erling Smørgrav 					    "is not permitted to use this "
553e2f6069cSDag-Erling Smørgrav 					    "certificate for login.",
554e2f6069cSDag-Erling Smørgrav 					    remote_ip);
555b15c8340SDag-Erling Smørgrav 					goto out;
556b15c8340SDag-Erling Smørgrav 				case -1:
557f7167e0eSDag-Erling Smørgrav 				default:
558e2f6069cSDag-Erling Smørgrav 					error("Certificate source-address "
559e2f6069cSDag-Erling Smørgrav 					    "contents invalid");
560b15c8340SDag-Erling Smørgrav 					goto out;
561b15c8340SDag-Erling Smørgrav 				}
562e2f6069cSDag-Erling Smørgrav 				found = 1;
563e2f6069cSDag-Erling Smørgrav 			}
564b15c8340SDag-Erling Smørgrav 		}
565b15c8340SDag-Erling Smørgrav 
566e2f6069cSDag-Erling Smørgrav 		if (!found) {
567e2f6069cSDag-Erling Smørgrav 			if (crit) {
568e2f6069cSDag-Erling Smørgrav 				error("Certificate critical option \"%s\" "
569e2f6069cSDag-Erling Smørgrav 				    "is not supported", name);
570e2f6069cSDag-Erling Smørgrav 				goto out;
571e2f6069cSDag-Erling Smørgrav 			} else {
572e2f6069cSDag-Erling Smørgrav 				logit("Certificate extension \"%s\" "
573e2f6069cSDag-Erling Smørgrav 				    "is not supported", name);
574e2f6069cSDag-Erling Smørgrav 			}
575bc5531deSDag-Erling Smørgrav 		} else if (sshbuf_len(data) != 0) {
576e2f6069cSDag-Erling Smørgrav 			error("Certificate option \"%s\" corrupt "
577b15c8340SDag-Erling Smørgrav 			    "(extra data)", name);
578b15c8340SDag-Erling Smørgrav 			goto out;
579b15c8340SDag-Erling Smørgrav 		}
580e4a9863fSDag-Erling Smørgrav 		free(name);
581e4a9863fSDag-Erling Smørgrav 		name = NULL;
582b15c8340SDag-Erling Smørgrav 	}
583e2f6069cSDag-Erling Smørgrav 	/* successfully parsed all options */
584b15c8340SDag-Erling Smørgrav 	ret = 0;
585b15c8340SDag-Erling Smørgrav 
586e2f6069cSDag-Erling Smørgrav  out:
587e2f6069cSDag-Erling Smørgrav 	if (ret != 0 &&
588e2f6069cSDag-Erling Smørgrav 	    cert_forced_command != NULL &&
589e2f6069cSDag-Erling Smørgrav 	    *cert_forced_command != NULL) {
590e4a9863fSDag-Erling Smørgrav 		free(*cert_forced_command);
591e2f6069cSDag-Erling Smørgrav 		*cert_forced_command = NULL;
592e2f6069cSDag-Erling Smørgrav 	}
593e4a9863fSDag-Erling Smørgrav 	free(name);
594bc5531deSDag-Erling Smørgrav 	sshbuf_free(data);
595bc5531deSDag-Erling Smørgrav 	sshbuf_free(c);
596e2f6069cSDag-Erling Smørgrav 	return ret;
597e2f6069cSDag-Erling Smørgrav }
598e2f6069cSDag-Erling Smørgrav 
599e2f6069cSDag-Erling Smørgrav /*
600e2f6069cSDag-Erling Smørgrav  * Set options from critical certificate options. These supersede user key
601e2f6069cSDag-Erling Smørgrav  * options so this must be called after auth_parse_options().
602e2f6069cSDag-Erling Smørgrav  */
603e2f6069cSDag-Erling Smørgrav int
604ca86bcf2SDag-Erling Smørgrav auth_cert_options(struct sshkey *k, struct passwd *pw, const char **reason)
605e2f6069cSDag-Erling Smørgrav {
606e2f6069cSDag-Erling Smørgrav 	int cert_no_port_forwarding_flag = 1;
607e2f6069cSDag-Erling Smørgrav 	int cert_no_agent_forwarding_flag = 1;
608e2f6069cSDag-Erling Smørgrav 	int cert_no_x11_forwarding_flag = 1;
609e2f6069cSDag-Erling Smørgrav 	int cert_no_pty_flag = 1;
610e2f6069cSDag-Erling Smørgrav 	int cert_no_user_rc = 1;
611e2f6069cSDag-Erling Smørgrav 	char *cert_forced_command = NULL;
612e2f6069cSDag-Erling Smørgrav 	int cert_source_address_done = 0;
613e2f6069cSDag-Erling Smørgrav 
614ca86bcf2SDag-Erling Smørgrav 	*reason = "invalid certificate options";
615ca86bcf2SDag-Erling Smørgrav 
616e2f6069cSDag-Erling Smørgrav 	/* Separate options and extensions for v01 certs */
617bc5531deSDag-Erling Smørgrav 	if (parse_option_list(k->cert->critical, pw,
618e2f6069cSDag-Erling Smørgrav 	    OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL,
619e2f6069cSDag-Erling Smørgrav 	    &cert_forced_command,
620e2f6069cSDag-Erling Smørgrav 	    &cert_source_address_done) == -1)
621e2f6069cSDag-Erling Smørgrav 		return -1;
622bc5531deSDag-Erling Smørgrav 	if (parse_option_list(k->cert->extensions, pw,
623557f75e5SDag-Erling Smørgrav 	    OPTIONS_EXTENSIONS, 0,
624e2f6069cSDag-Erling Smørgrav 	    &cert_no_port_forwarding_flag,
625e2f6069cSDag-Erling Smørgrav 	    &cert_no_agent_forwarding_flag,
626e2f6069cSDag-Erling Smørgrav 	    &cert_no_x11_forwarding_flag,
627e2f6069cSDag-Erling Smørgrav 	    &cert_no_pty_flag,
628e2f6069cSDag-Erling Smørgrav 	    &cert_no_user_rc,
629e2f6069cSDag-Erling Smørgrav 	    NULL, NULL) == -1)
630e2f6069cSDag-Erling Smørgrav 		return -1;
631e2f6069cSDag-Erling Smørgrav 
632b15c8340SDag-Erling Smørgrav 	no_port_forwarding_flag |= cert_no_port_forwarding_flag;
633b15c8340SDag-Erling Smørgrav 	no_agent_forwarding_flag |= cert_no_agent_forwarding_flag;
634b15c8340SDag-Erling Smørgrav 	no_x11_forwarding_flag |= cert_no_x11_forwarding_flag;
635b15c8340SDag-Erling Smørgrav 	no_pty_flag |= cert_no_pty_flag;
636b15c8340SDag-Erling Smørgrav 	no_user_rc |= cert_no_user_rc;
637ca86bcf2SDag-Erling Smørgrav 	/*
638ca86bcf2SDag-Erling Smørgrav 	 * Only permit both CA and key option forced-command if they match.
639ca86bcf2SDag-Erling Smørgrav 	 * Otherwise refuse the certificate.
640ca86bcf2SDag-Erling Smørgrav 	 */
641ca86bcf2SDag-Erling Smørgrav 	if (cert_forced_command != NULL && forced_command != NULL) {
642ca86bcf2SDag-Erling Smørgrav 		if (strcmp(forced_command, cert_forced_command) == 0) {
643e4a9863fSDag-Erling Smørgrav 			free(forced_command);
644b15c8340SDag-Erling Smørgrav 			forced_command = cert_forced_command;
645ca86bcf2SDag-Erling Smørgrav 		} else {
646ca86bcf2SDag-Erling Smørgrav 			*reason = "certificate and key options forced command "
647ca86bcf2SDag-Erling Smørgrav 			    "do not match";
648ca86bcf2SDag-Erling Smørgrav 			free(cert_forced_command);
649ca86bcf2SDag-Erling Smørgrav 			return -1;
650b15c8340SDag-Erling Smørgrav 		}
651ca86bcf2SDag-Erling Smørgrav 	} else if (cert_forced_command != NULL)
652ca86bcf2SDag-Erling Smørgrav 		forced_command = cert_forced_command;
653ca86bcf2SDag-Erling Smørgrav 	/* success */
654ca86bcf2SDag-Erling Smørgrav 	*reason = NULL;
655e2f6069cSDag-Erling Smørgrav 	return 0;
656b15c8340SDag-Erling Smørgrav }
657b15c8340SDag-Erling Smørgrav 
658