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