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