Annotation of src/usr.bin/ssh/auth-options.c, Revision 1.75
1.75 ! djm 1: /* $OpenBSD: auth-options.c,v 1.74 2017/09/12 06:32:07 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: */
1.75 ! djm 12: /*
! 13: * Copyright (c) 2018 Damien Miller <djm@mindrot.org>
! 14: *
! 15: * Permission to use, copy, modify, and distribute this software for any
! 16: * purpose with or without fee is hereby granted, provided that the above
! 17: * copyright notice and this permission notice appear in all copies.
! 18: *
! 19: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 20: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 21: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 22: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 23: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 24: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 25: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 26: */
1.3 deraadt 27:
1.36 stevesk 28: #include <sys/types.h>
1.42 djm 29: #include <sys/queue.h>
1.36 stevesk 30:
1.37 stevesk 31: #include <netdb.h>
1.36 stevesk 32: #include <pwd.h>
1.39 stevesk 33: #include <string.h>
1.40 deraadt 34: #include <stdio.h>
35: #include <stdarg.h>
1.75 ! djm 36: #include <ctype.h>
! 37: #include <limits.h>
1.1 markus 38:
1.65 markus 39: #include "key.h" /* XXX for typedef */
40: #include "buffer.h" /* XXX for typedef */
1.1 markus 41: #include "xmalloc.h"
42: #include "match.h"
1.65 markus 43: #include "ssherr.h"
1.75 ! djm 44: #include "ssh2.h"
1.11 markus 45: #include "log.h"
46: #include "canohost.h"
1.71 djm 47: #include "packet.h"
1.65 markus 48: #include "sshbuf.h"
1.64 millert 49: #include "misc.h"
1.18 markus 50: #include "channels.h"
1.12 markus 51: #include "servconf.h"
1.65 markus 52: #include "sshkey.h"
1.50 djm 53: #include "auth-options.h"
1.40 deraadt 54: #include "hostfile.h"
55: #include "auth.h"
1.1 markus 56:
57: /* Flags set authorized_keys flags */
58: int no_port_forwarding_flag = 0;
59: int no_agent_forwarding_flag = 0;
60: int no_x11_forwarding_flag = 0;
61: int no_pty_flag = 0;
1.41 djm 62: int no_user_rc = 0;
1.45 djm 63: int key_is_cert_authority = 0;
1.1 markus 64:
65: /* "command=" option. */
66: char *forced_command = NULL;
67:
68: /* "environment=" options. */
69: struct envstring *custom_environment = NULL;
70:
1.32 reyk 71: /* "tunnel=" option. */
72: int forced_tun_device = -1;
73:
1.51 djm 74: /* "principals=" option. */
75: char *authorized_principals = NULL;
76:
1.12 markus 77: extern ServerOptions options;
78:
1.74 djm 79: /* XXX refactor to be stateless */
80:
1.22 provos 81: void
1.5 markus 82: auth_clear_options(void)
83: {
1.74 djm 84: struct ssh *ssh = active_state; /* XXX */
85:
1.5 markus 86: no_agent_forwarding_flag = 0;
87: no_port_forwarding_flag = 0;
88: no_pty_flag = 0;
89: no_x11_forwarding_flag = 0;
1.41 djm 90: no_user_rc = 0;
1.45 djm 91: key_is_cert_authority = 0;
1.5 markus 92: while (custom_environment) {
93: struct envstring *ce = custom_environment;
94: custom_environment = ce->next;
1.58 djm 95: free(ce->s);
96: free(ce);
1.5 markus 97: }
1.70 mmcc 98: free(forced_command);
99: forced_command = NULL;
100: free(authorized_principals);
101: authorized_principals = NULL;
1.32 reyk 102: forced_tun_device = -1;
1.74 djm 103: channel_clear_permitted_opens(ssh);
1.5 markus 104: }
105:
1.10 markus 106: /*
1.69 djm 107: * Match flag 'opt' in *optsp, and if allow_negate is set then also match
108: * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0
109: * if negated option matches.
110: * If the option or negated option matches, then *optsp is updated to
111: * point to the first character after the option and, if 'msg' is not NULL
112: * then a message based on it added via auth_debug_add().
113: */
114: static int
115: match_flag(const char *opt, int allow_negate, char **optsp, const char *msg)
116: {
117: size_t opt_len = strlen(opt);
118: char *opts = *optsp;
119: int negate = 0;
120:
121: if (allow_negate && strncasecmp(opts, "no-", 3) == 0) {
122: opts += 3;
123: negate = 1;
124: }
125: if (strncasecmp(opts, opt, opt_len) == 0) {
126: *optsp = opts + opt_len;
127: if (msg != NULL) {
128: auth_debug_add("%s %s.", msg,
129: negate ? "disabled" : "enabled");
130: }
131: return negate ? 0 : 1;
132: }
133: return -1;
134: }
135:
136: /*
1.10 markus 137: * return 1 if access is granted, 0 if not.
138: * side effect: sets key option flags
1.74 djm 139: * XXX remove side effects; fill structure instead.
1.10 markus 140: */
1.1 markus 141: int
1.73 markus 142: auth_parse_options(struct passwd *pw, char *opts, const char *file,
143: u_long linenum)
1.1 markus 144: {
1.71 djm 145: struct ssh *ssh = active_state; /* XXX */
1.1 markus 146: const char *cp;
1.69 djm 147: int i, r;
1.5 markus 148:
149: /* reset options */
150: auth_clear_options();
1.13 markus 151:
152: if (!opts)
153: return 1;
1.5 markus 154:
1.12 markus 155: while (*opts && *opts != ' ' && *opts != '\t') {
1.69 djm 156: if ((r = match_flag("cert-authority", 0, &opts, NULL)) != -1) {
157: key_is_cert_authority = r;
1.45 djm 158: goto next_option;
159: }
1.69 djm 160: if ((r = match_flag("restrict", 0, &opts, NULL)) != -1) {
161: auth_debug_add("Key is restricted.");
1.1 markus 162: no_port_forwarding_flag = 1;
1.69 djm 163: no_agent_forwarding_flag = 1;
164: no_x11_forwarding_flag = 1;
165: no_pty_flag = 1;
166: no_user_rc = 1;
167: goto next_option;
168: }
169: if ((r = match_flag("port-forwarding", 1, &opts,
170: "Port forwarding")) != -1) {
171: no_port_forwarding_flag = r != 1;
1.1 markus 172: goto next_option;
173: }
1.69 djm 174: if ((r = match_flag("agent-forwarding", 1, &opts,
175: "Agent forwarding")) != -1) {
176: no_agent_forwarding_flag = r != 1;
1.1 markus 177: goto next_option;
178: }
1.69 djm 179: if ((r = match_flag("x11-forwarding", 1, &opts,
180: "X11 forwarding")) != -1) {
181: no_x11_forwarding_flag = r != 1;
1.1 markus 182: goto next_option;
183: }
1.69 djm 184: if ((r = match_flag("pty", 1, &opts,
185: "PTY allocation")) != -1) {
186: no_pty_flag = r != 1;
1.41 djm 187: goto next_option;
188: }
1.69 djm 189: if ((r = match_flag("user-rc", 1, &opts,
190: "User rc execution")) != -1) {
191: no_user_rc = r != 1;
1.1 markus 192: goto next_option;
193: }
194: cp = "command=\"";
1.12 markus 195: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
196: opts += strlen(cp);
1.70 mmcc 197: free(forced_command);
1.12 markus 198: forced_command = xmalloc(strlen(opts) + 1);
1.1 markus 199: i = 0;
1.12 markus 200: while (*opts) {
201: if (*opts == '"')
1.1 markus 202: break;
1.12 markus 203: if (*opts == '\\' && opts[1] == '"') {
204: opts += 2;
1.1 markus 205: forced_command[i++] = '"';
206: continue;
207: }
1.12 markus 208: forced_command[i++] = *opts++;
1.1 markus 209: }
1.12 markus 210: if (!*opts) {
1.1 markus 211: debug("%.100s, line %lu: missing end quote",
1.10 markus 212: file, linenum);
1.24 markus 213: auth_debug_add("%.100s, line %lu: missing end quote",
1.10 markus 214: file, linenum);
1.58 djm 215: free(forced_command);
1.14 markus 216: forced_command = NULL;
217: goto bad_option;
1.1 markus 218: }
1.38 dtucker 219: forced_command[i] = '\0';
1.54 djm 220: auth_debug_add("Forced command.");
1.51 djm 221: opts++;
222: goto next_option;
223: }
224: cp = "principals=\"";
225: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
226: opts += strlen(cp);
1.70 mmcc 227: free(authorized_principals);
1.51 djm 228: authorized_principals = xmalloc(strlen(opts) + 1);
229: i = 0;
230: while (*opts) {
231: if (*opts == '"')
232: break;
233: if (*opts == '\\' && opts[1] == '"') {
234: opts += 2;
235: authorized_principals[i++] = '"';
236: continue;
237: }
238: authorized_principals[i++] = *opts++;
239: }
240: if (!*opts) {
241: debug("%.100s, line %lu: missing end quote",
242: file, linenum);
243: auth_debug_add("%.100s, line %lu: missing end quote",
244: file, linenum);
1.58 djm 245: free(authorized_principals);
1.51 djm 246: authorized_principals = NULL;
247: goto bad_option;
248: }
249: authorized_principals[i] = '\0';
250: auth_debug_add("principals: %.900s",
251: authorized_principals);
1.12 markus 252: opts++;
1.1 markus 253: goto next_option;
254: }
255: cp = "environment=\"";
1.67 djm 256: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.1 markus 257: char *s;
258: struct envstring *new_envstring;
1.15 markus 259:
1.12 markus 260: opts += strlen(cp);
261: s = xmalloc(strlen(opts) + 1);
1.1 markus 262: i = 0;
1.12 markus 263: while (*opts) {
264: if (*opts == '"')
1.1 markus 265: break;
1.12 markus 266: if (*opts == '\\' && opts[1] == '"') {
267: opts += 2;
1.1 markus 268: s[i++] = '"';
269: continue;
270: }
1.12 markus 271: s[i++] = *opts++;
1.1 markus 272: }
1.12 markus 273: if (!*opts) {
1.1 markus 274: debug("%.100s, line %lu: missing end quote",
1.10 markus 275: file, linenum);
1.24 markus 276: auth_debug_add("%.100s, line %lu: missing end quote",
1.10 markus 277: file, linenum);
1.58 djm 278: free(s);
1.14 markus 279: goto bad_option;
1.1 markus 280: }
1.38 dtucker 281: s[i] = '\0';
1.12 markus 282: opts++;
1.67 djm 283: if (options.permit_user_env) {
284: auth_debug_add("Adding to environment: "
285: "%.900s", s);
286: debug("Adding to environment: %.900s", s);
287: new_envstring = xcalloc(1,
288: sizeof(*new_envstring));
289: new_envstring->s = s;
290: new_envstring->next = custom_environment;
291: custom_environment = new_envstring;
292: s = NULL;
293: }
294: free(s);
1.1 markus 295: goto next_option;
296: }
297: cp = "from=\"";
1.12 markus 298: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.71 djm 299: const char *remote_ip = ssh_remote_ipaddr(ssh);
300: const char *remote_host = auth_get_canonical_hostname(
301: ssh, options.use_dns);
1.12 markus 302: char *patterns = xmalloc(strlen(opts) + 1);
1.15 markus 303:
1.12 markus 304: opts += strlen(cp);
1.1 markus 305: i = 0;
1.12 markus 306: while (*opts) {
307: if (*opts == '"')
1.1 markus 308: break;
1.12 markus 309: if (*opts == '\\' && opts[1] == '"') {
310: opts += 2;
1.1 markus 311: patterns[i++] = '"';
312: continue;
313: }
1.12 markus 314: patterns[i++] = *opts++;
1.1 markus 315: }
1.12 markus 316: if (!*opts) {
1.1 markus 317: debug("%.100s, line %lu: missing end quote",
1.10 markus 318: file, linenum);
1.24 markus 319: auth_debug_add("%.100s, line %lu: missing end quote",
1.10 markus 320: file, linenum);
1.58 djm 321: free(patterns);
1.14 markus 322: goto bad_option;
1.1 markus 323: }
1.38 dtucker 324: patterns[i] = '\0';
1.12 markus 325: opts++;
1.43 djm 326: switch (match_host_and_ip(remote_host, remote_ip,
327: patterns)) {
328: case 1:
1.58 djm 329: free(patterns);
1.43 djm 330: /* Host name matches. */
331: goto next_option;
332: case -1:
333: debug("%.100s, line %lu: invalid criteria",
334: file, linenum);
335: auth_debug_add("%.100s, line %lu: "
336: "invalid criteria", file, linenum);
337: /* FALLTHROUGH */
338: case 0:
1.58 djm 339: free(patterns);
1.27 itojun 340: logit("Authentication tried for %.100s with "
1.12 markus 341: "correct key but not from a permitted "
342: "host (host=%.200s, ip=%.200s).",
343: pw->pw_name, remote_host, remote_ip);
1.24 markus 344: auth_debug_add("Your host '%.200s' is not "
1.12 markus 345: "permitted to use this key for login.",
346: remote_host);
1.43 djm 347: break;
1.1 markus 348: }
1.43 djm 349: /* deny access */
350: return 0;
1.15 markus 351: }
352: cp = "permitopen=\"";
353: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.29 djm 354: char *host, *p;
1.44 djm 355: int port;
1.15 markus 356: char *patterns = xmalloc(strlen(opts) + 1);
357:
358: opts += strlen(cp);
359: i = 0;
360: while (*opts) {
361: if (*opts == '"')
362: break;
363: if (*opts == '\\' && opts[1] == '"') {
364: opts += 2;
365: patterns[i++] = '"';
366: continue;
367: }
368: patterns[i++] = *opts++;
369: }
370: if (!*opts) {
371: debug("%.100s, line %lu: missing end quote",
372: file, linenum);
1.29 djm 373: auth_debug_add("%.100s, line %lu: missing "
374: "end quote", file, linenum);
1.58 djm 375: free(patterns);
1.15 markus 376: goto bad_option;
377: }
1.38 dtucker 378: patterns[i] = '\0';
1.15 markus 379: opts++;
1.29 djm 380: p = patterns;
1.64 millert 381: /* XXX - add streamlocal support */
1.29 djm 382: host = hpdelim(&p);
383: if (host == NULL || strlen(host) >= NI_MAXHOST) {
384: debug("%.100s, line %lu: Bad permitopen "
1.31 deraadt 385: "specification <%.100s>", file, linenum,
1.29 djm 386: patterns);
1.24 markus 387: auth_debug_add("%.100s, line %lu: "
1.29 djm 388: "Bad permitopen specification", file,
389: linenum);
1.58 djm 390: free(patterns);
1.15 markus 391: goto bad_option;
392: }
1.30 deraadt 393: host = cleanhostname(host);
1.55 dtucker 394: if (p == NULL || (port = permitopen_port(p)) < 0) {
1.29 djm 395: debug("%.100s, line %lu: Bad permitopen port "
396: "<%.100s>", file, linenum, p ? p : "");
1.24 markus 397: auth_debug_add("%.100s, line %lu: "
1.20 stevesk 398: "Bad permitopen port", file, linenum);
1.58 djm 399: free(patterns);
1.15 markus 400: goto bad_option;
401: }
1.57 djm 402: if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0)
1.74 djm 403: channel_add_permitted_opens(ssh, host, port);
1.58 djm 404: free(patterns);
1.32 reyk 405: goto next_option;
406: }
407: cp = "tunnel=\"";
408: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
409: char *tun = NULL;
410: opts += strlen(cp);
411: tun = xmalloc(strlen(opts) + 1);
412: i = 0;
413: while (*opts) {
414: if (*opts == '"')
415: break;
416: tun[i++] = *opts++;
417: }
418: if (!*opts) {
419: debug("%.100s, line %lu: missing end quote",
420: file, linenum);
421: auth_debug_add("%.100s, line %lu: missing end quote",
422: file, linenum);
1.58 djm 423: free(tun);
1.32 reyk 424: forced_tun_device = -1;
425: goto bad_option;
426: }
1.38 dtucker 427: tun[i] = '\0';
1.32 reyk 428: forced_tun_device = a2tun(tun, NULL);
1.58 djm 429: free(tun);
1.33 reyk 430: if (forced_tun_device == SSH_TUNID_ERR) {
1.32 reyk 431: debug("%.100s, line %lu: invalid tun device",
432: file, linenum);
433: auth_debug_add("%.100s, line %lu: invalid tun device",
434: file, linenum);
435: forced_tun_device = -1;
436: goto bad_option;
437: }
438: auth_debug_add("Forced tun device: %d", forced_tun_device);
439: opts++;
1.1 markus 440: goto next_option;
441: }
442: next_option:
443: /*
444: * Skip the comma, and move to the next option
445: * (or break out if there are no more).
446: */
1.12 markus 447: if (!*opts)
1.1 markus 448: fatal("Bugs in auth-options.c option processing.");
1.12 markus 449: if (*opts == ' ' || *opts == '\t')
1.1 markus 450: break; /* End of options. */
1.12 markus 451: if (*opts != ',')
1.1 markus 452: goto bad_option;
1.12 markus 453: opts++;
1.1 markus 454: /* Process the next option. */
455: }
1.22 provos 456:
1.1 markus 457: /* grant access */
458: return 1;
459:
460: bad_option:
1.27 itojun 461: logit("Bad options in %.100s file, line %lu: %.50s",
1.12 markus 462: file, linenum, opts);
1.24 markus 463: auth_debug_add("Bad options in %.100s file, line %lu: %.50s",
1.12 markus 464: file, linenum, opts);
1.22 provos 465:
1.1 markus 466: /* deny access */
467: return 0;
468: }
1.45 djm 469:
1.52 djm 470: #define OPTIONS_CRITICAL 1
471: #define OPTIONS_EXTENSIONS 2
472: static int
1.65 markus 473: parse_option_list(struct sshbuf *oblob, struct passwd *pw,
1.52 djm 474: u_int which, int crit,
475: int *cert_no_port_forwarding_flag,
476: int *cert_no_agent_forwarding_flag,
477: int *cert_no_x11_forwarding_flag,
478: int *cert_no_pty_flag,
479: int *cert_no_user_rc,
480: char **cert_forced_command,
481: int *cert_source_address_done)
1.45 djm 482: {
1.71 djm 483: struct ssh *ssh = active_state; /* XXX */
1.52 djm 484: char *command, *allowed;
485: const char *remote_ip;
1.59 djm 486: char *name = NULL;
1.65 markus 487: struct sshbuf *c = NULL, *data = NULL;
488: int r, ret = -1, result, found;
489:
490: if ((c = sshbuf_fromb(oblob)) == NULL) {
491: error("%s: sshbuf_fromb failed", __func__);
492: goto out;
493: }
494:
495: while (sshbuf_len(c) > 0) {
496: sshbuf_free(data);
497: data = NULL;
498: if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 ||
499: (r = sshbuf_froms(c, &data)) != 0) {
500: error("Unable to parse certificate options: %s",
501: ssh_err(r));
1.45 djm 502: goto out;
503: }
1.65 markus 504: debug3("found certificate option \"%.100s\" len %zu",
505: name, sshbuf_len(data));
1.52 djm 506: found = 0;
507: if ((which & OPTIONS_EXTENSIONS) != 0) {
508: if (strcmp(name, "permit-X11-forwarding") == 0) {
509: *cert_no_x11_forwarding_flag = 0;
510: found = 1;
511: } else if (strcmp(name,
512: "permit-agent-forwarding") == 0) {
513: *cert_no_agent_forwarding_flag = 0;
514: found = 1;
515: } else if (strcmp(name,
516: "permit-port-forwarding") == 0) {
517: *cert_no_port_forwarding_flag = 0;
518: found = 1;
519: } else if (strcmp(name, "permit-pty") == 0) {
520: *cert_no_pty_flag = 0;
521: found = 1;
522: } else if (strcmp(name, "permit-user-rc") == 0) {
523: *cert_no_user_rc = 0;
524: found = 1;
525: }
526: }
527: if (!found && (which & OPTIONS_CRITICAL) != 0) {
528: if (strcmp(name, "force-command") == 0) {
1.65 markus 529: if ((r = sshbuf_get_cstring(data, &command,
530: NULL)) != 0) {
531: error("Unable to parse \"%s\" "
532: "section: %s", name, ssh_err(r));
1.52 djm 533: goto out;
534: }
535: if (*cert_forced_command != NULL) {
536: error("Certificate has multiple "
537: "force-command options");
1.58 djm 538: free(command);
1.52 djm 539: goto out;
540: }
541: *cert_forced_command = command;
542: found = 1;
1.45 djm 543: }
1.52 djm 544: if (strcmp(name, "source-address") == 0) {
1.65 markus 545: if ((r = sshbuf_get_cstring(data, &allowed,
546: NULL)) != 0) {
547: error("Unable to parse \"%s\" "
548: "section: %s", name, ssh_err(r));
1.52 djm 549: goto out;
550: }
551: if ((*cert_source_address_done)++) {
552: error("Certificate has multiple "
553: "source-address options");
1.58 djm 554: free(allowed);
1.52 djm 555: goto out;
556: }
1.71 djm 557: remote_ip = ssh_remote_ipaddr(ssh);
1.62 djm 558: result = addr_match_cidr_list(remote_ip,
559: allowed);
560: free(allowed);
561: switch (result) {
1.52 djm 562: case 1:
563: /* accepted */
564: break;
565: case 0:
566: /* no match */
567: logit("Authentication tried for %.100s "
568: "with valid certificate but not "
569: "from a permitted host "
570: "(ip=%.200s).", pw->pw_name,
571: remote_ip);
572: auth_debug_add("Your address '%.200s' "
573: "is not permitted to use this "
574: "certificate for login.",
575: remote_ip);
576: goto out;
577: case -1:
1.62 djm 578: default:
1.52 djm 579: error("Certificate source-address "
580: "contents invalid");
581: goto out;
582: }
583: found = 1;
1.46 djm 584: }
1.52 djm 585: }
586:
587: if (!found) {
588: if (crit) {
589: error("Certificate critical option \"%s\" "
590: "is not supported", name);
1.45 djm 591: goto out;
1.52 djm 592: } else {
593: logit("Certificate extension \"%s\" "
594: "is not supported", name);
1.45 djm 595: }
1.65 markus 596: } else if (sshbuf_len(data) != 0) {
1.52 djm 597: error("Certificate option \"%s\" corrupt "
1.45 djm 598: "(extra data)", name);
599: goto out;
600: }
1.58 djm 601: free(name);
1.59 djm 602: name = NULL;
1.45 djm 603: }
1.50 djm 604: /* successfully parsed all options */
1.45 djm 605: ret = 0;
606:
1.52 djm 607: out:
608: if (ret != 0 &&
609: cert_forced_command != NULL &&
610: *cert_forced_command != NULL) {
1.58 djm 611: free(*cert_forced_command);
1.52 djm 612: *cert_forced_command = NULL;
613: }
1.70 mmcc 614: free(name);
1.65 markus 615: sshbuf_free(data);
616: sshbuf_free(c);
1.52 djm 617: return ret;
618: }
619:
620: /*
621: * Set options from critical certificate options. These supersede user key
622: * options so this must be called after auth_parse_options().
623: */
624: int
1.72 djm 625: auth_cert_options(struct sshkey *k, struct passwd *pw, const char **reason)
1.52 djm 626: {
627: int cert_no_port_forwarding_flag = 1;
628: int cert_no_agent_forwarding_flag = 1;
629: int cert_no_x11_forwarding_flag = 1;
630: int cert_no_pty_flag = 1;
631: int cert_no_user_rc = 1;
632: char *cert_forced_command = NULL;
633: int cert_source_address_done = 0;
634:
1.72 djm 635: *reason = "invalid certificate options";
636:
1.68 djm 637: /* Separate options and extensions for v01 certs */
638: if (parse_option_list(k->cert->critical, pw,
639: OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL,
640: &cert_forced_command,
641: &cert_source_address_done) == -1)
642: return -1;
643: if (parse_option_list(k->cert->extensions, pw,
644: OPTIONS_EXTENSIONS, 0,
645: &cert_no_port_forwarding_flag,
646: &cert_no_agent_forwarding_flag,
647: &cert_no_x11_forwarding_flag,
648: &cert_no_pty_flag,
649: &cert_no_user_rc,
650: NULL, NULL) == -1)
651: return -1;
1.52 djm 652:
1.45 djm 653: no_port_forwarding_flag |= cert_no_port_forwarding_flag;
654: no_agent_forwarding_flag |= cert_no_agent_forwarding_flag;
655: no_x11_forwarding_flag |= cert_no_x11_forwarding_flag;
656: no_pty_flag |= cert_no_pty_flag;
657: no_user_rc |= cert_no_user_rc;
1.72 djm 658: /*
659: * Only permit both CA and key option forced-command if they match.
660: * Otherwise refuse the certificate.
661: */
662: if (cert_forced_command != NULL && forced_command != NULL) {
663: if (strcmp(forced_command, cert_forced_command) == 0) {
664: free(forced_command);
665: forced_command = cert_forced_command;
666: } else {
667: *reason = "certificate and key options forced command "
668: "do not match";
669: free(cert_forced_command);
670: return -1;
671: }
672: } else if (cert_forced_command != NULL)
1.45 djm 673: forced_command = cert_forced_command;
1.72 djm 674: /* success */
675: *reason = NULL;
1.52 djm 676: return 0;
1.45 djm 677: }
678:
1.75 ! djm 679: /*
! 680: * authorized_keys options processing.
! 681: */
! 682:
! 683: /*
! 684: * Match flag 'opt' in *optsp, and if allow_negate is set then also match
! 685: * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0
! 686: * if negated option matches.
! 687: * If the option or negated option matches, then *optsp is updated to
! 688: * point to the first character after the option.
! 689: */
! 690: static int
! 691: opt_flag(const char *opt, int allow_negate, const char **optsp)
! 692: {
! 693: size_t opt_len = strlen(opt);
! 694: const char *opts = *optsp;
! 695: int negate = 0;
! 696:
! 697: if (allow_negate && strncasecmp(opts, "no-", 3) == 0) {
! 698: opts += 3;
! 699: negate = 1;
! 700: }
! 701: if (strncasecmp(opts, opt, opt_len) == 0) {
! 702: *optsp = opts + opt_len;
! 703: return negate ? 0 : 1;
! 704: }
! 705: return -1;
! 706: }
! 707:
! 708: static char *
! 709: opt_dequote(const char **sp, const char **errstrp)
! 710: {
! 711: const char *s = *sp;
! 712: char *ret;
! 713: size_t i;
! 714:
! 715: *errstrp = NULL;
! 716: if (*s != '"') {
! 717: *errstrp = "missing start quote";
! 718: return NULL;
! 719: }
! 720: s++;
! 721: if ((ret = malloc(strlen((s)) + 1)) == NULL) {
! 722: *errstrp = "memory allocation failed";
! 723: return NULL;
! 724: }
! 725: for (i = 0; *s != '\0' && *s != '"';) {
! 726: if (s[0] == '\\' && s[1] == '"')
! 727: s++;
! 728: ret[i++] = *s++;
! 729: }
! 730: if (*s == '\0') {
! 731: *errstrp = "missing end quote";
! 732: free(ret);
! 733: return NULL;
! 734: }
! 735: ret[i] = '\0';
! 736: s++;
! 737: *sp = s;
! 738: return ret;
! 739: }
! 740:
! 741: static int
! 742: opt_match(const char **opts, const char *term)
! 743: {
! 744: if (strncasecmp((*opts), term, strlen(term)) == 0 &&
! 745: (*opts)[strlen(term)] == '=') {
! 746: *opts += strlen(term) + 1;
! 747: return 1;
! 748: }
! 749: return 0;
! 750: }
! 751:
! 752: static int
! 753: dup_strings(char ***dstp, size_t *ndstp, char **src, size_t nsrc)
! 754: {
! 755: char **dst;
! 756: size_t i, j;
! 757:
! 758: *dstp = NULL;
! 759: *ndstp = 0;
! 760: if (nsrc == 0)
! 761: return 0;
! 762:
! 763: if ((dst = calloc(nsrc, sizeof(*src))) == NULL)
! 764: return -1;
! 765: for (i = 0; i < nsrc; i++) {
! 766: if ((dst[i] = strdup(src[i])) == NULL) {
! 767: for (j = 0; j < i; j++)
! 768: free(dst[j]);
! 769: free(dst);
! 770: return -1;
! 771: }
! 772: }
! 773: /* success */
! 774: *dstp = dst;
! 775: *ndstp = nsrc;
! 776: return 0;
! 777: }
! 778:
! 779: #define OPTIONS_CRITICAL 1
! 780: #define OPTIONS_EXTENSIONS 2
! 781: static int
! 782: cert_option_list(struct sshauthopt *opts, struct sshbuf *oblob,
! 783: u_int which, int crit)
! 784: {
! 785: char *command, *allowed;
! 786: char *name = NULL;
! 787: struct sshbuf *c = NULL, *data = NULL;
! 788: int r, ret = -1, found;
! 789:
! 790: if ((c = sshbuf_fromb(oblob)) == NULL) {
! 791: error("%s: sshbuf_fromb failed", __func__);
! 792: goto out;
! 793: }
! 794:
! 795: while (sshbuf_len(c) > 0) {
! 796: sshbuf_free(data);
! 797: data = NULL;
! 798: if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 ||
! 799: (r = sshbuf_froms(c, &data)) != 0) {
! 800: error("Unable to parse certificate options: %s",
! 801: ssh_err(r));
! 802: goto out;
! 803: }
! 804: debug3("found certificate option \"%.100s\" len %zu",
! 805: name, sshbuf_len(data));
! 806: found = 0;
! 807: if ((which & OPTIONS_EXTENSIONS) != 0) {
! 808: if (strcmp(name, "permit-X11-forwarding") == 0) {
! 809: opts->permit_x11_forwarding_flag = 1;
! 810: found = 1;
! 811: } else if (strcmp(name,
! 812: "permit-agent-forwarding") == 0) {
! 813: opts->permit_agent_forwarding_flag = 1;
! 814: found = 1;
! 815: } else if (strcmp(name,
! 816: "permit-port-forwarding") == 0) {
! 817: opts->permit_port_forwarding_flag = 1;
! 818: found = 1;
! 819: } else if (strcmp(name, "permit-pty") == 0) {
! 820: opts->permit_pty_flag = 1;
! 821: found = 1;
! 822: } else if (strcmp(name, "permit-user-rc") == 0) {
! 823: opts->permit_user_rc = 1;
! 824: found = 1;
! 825: }
! 826: }
! 827: if (!found && (which & OPTIONS_CRITICAL) != 0) {
! 828: if (strcmp(name, "force-command") == 0) {
! 829: if ((r = sshbuf_get_cstring(data, &command,
! 830: NULL)) != 0) {
! 831: error("Unable to parse \"%s\" "
! 832: "section: %s", name, ssh_err(r));
! 833: goto out;
! 834: }
! 835: if (opts->force_command != NULL) {
! 836: error("Certificate has multiple "
! 837: "force-command options");
! 838: free(command);
! 839: goto out;
! 840: }
! 841: opts->force_command = command;
! 842: found = 1;
! 843: }
! 844: if (strcmp(name, "source-address") == 0) {
! 845: if ((r = sshbuf_get_cstring(data, &allowed,
! 846: NULL)) != 0) {
! 847: error("Unable to parse \"%s\" "
! 848: "section: %s", name, ssh_err(r));
! 849: goto out;
! 850: }
! 851: if (opts->required_from_host_cert != NULL) {
! 852: error("Certificate has multiple "
! 853: "source-address options");
! 854: free(allowed);
! 855: goto out;
! 856: }
! 857: /* Check syntax */
! 858: if (addr_match_cidr_list(NULL, allowed) == -1) {
! 859: error("Certificate source-address "
! 860: "contents invalid");
! 861: goto out;
! 862: }
! 863: opts->required_from_host_cert = allowed;
! 864: found = 1;
! 865: }
! 866: }
! 867:
! 868: if (!found) {
! 869: if (crit) {
! 870: error("Certificate critical option \"%s\" "
! 871: "is not supported", name);
! 872: goto out;
! 873: } else {
! 874: logit("Certificate extension \"%s\" "
! 875: "is not supported", name);
! 876: }
! 877: } else if (sshbuf_len(data) != 0) {
! 878: error("Certificate option \"%s\" corrupt "
! 879: "(extra data)", name);
! 880: goto out;
! 881: }
! 882: free(name);
! 883: name = NULL;
! 884: }
! 885: /* successfully parsed all options */
! 886: ret = 0;
! 887:
! 888: out:
! 889: free(name);
! 890: sshbuf_free(data);
! 891: sshbuf_free(c);
! 892: return ret;
! 893: }
! 894:
! 895: struct sshauthopt *
! 896: sshauthopt_new(void)
! 897: {
! 898: struct sshauthopt *ret;
! 899:
! 900: if ((ret = calloc(1, sizeof(*ret))) == NULL)
! 901: return NULL;
! 902: ret->force_tun_device = -1;
! 903: return ret;
! 904: }
! 905:
! 906: void
! 907: sshauthopt_free(struct sshauthopt *opts)
! 908: {
! 909: size_t i;
! 910:
! 911: if (opts == NULL)
! 912: return;
! 913:
! 914: free(opts->cert_principals);
! 915: free(opts->force_command);
! 916: free(opts->required_from_host_cert);
! 917: free(opts->required_from_host_keys);
! 918:
! 919: for (i = 0; i < opts->nenv; i++)
! 920: free(opts->env[i]);
! 921: free(opts->env);
! 922:
! 923: for (i = 0; i < opts->npermitopen; i++)
! 924: free(opts->permitopen[i]);
! 925: free(opts->permitopen);
! 926:
! 927: explicit_bzero(opts, sizeof(*opts));
! 928: free(opts);
! 929: }
! 930:
! 931: struct sshauthopt *
! 932: sshauthopt_new_with_keys_defaults(void)
! 933: {
! 934: struct sshauthopt *ret = NULL;
! 935:
! 936: if ((ret = sshauthopt_new()) == NULL)
! 937: return NULL;
! 938:
! 939: /* Defaults for authorized_keys flags */
! 940: ret->permit_port_forwarding_flag = 1;
! 941: ret->permit_agent_forwarding_flag = 1;
! 942: ret->permit_x11_forwarding_flag = 1;
! 943: ret->permit_pty_flag = 1;
! 944: ret->permit_user_rc = 1;
! 945: return ret;
! 946: }
! 947:
! 948: struct sshauthopt *
! 949: sshauthopt_parse(const char *opts, const char **errstrp)
! 950: {
! 951: char **oarray, *opt, *cp, *tmp, *host;
! 952: int r;
! 953: struct sshauthopt *ret = NULL;
! 954: const char *errstr = "unknown error";
! 955:
! 956: if (errstrp != NULL)
! 957: *errstrp = NULL;
! 958: if ((ret = sshauthopt_new_with_keys_defaults()) == NULL)
! 959: goto alloc_fail;
! 960:
! 961: if (opts == NULL)
! 962: return ret;
! 963:
! 964: while (*opts && *opts != ' ' && *opts != '\t') {
! 965: /* flag options */
! 966: if ((r = opt_flag("restrict", 0, &opts)) != -1) {
! 967: ret->restricted = 1;
! 968: ret->permit_port_forwarding_flag = 0;
! 969: ret->permit_agent_forwarding_flag = 0;
! 970: ret->permit_x11_forwarding_flag = 0;
! 971: ret->permit_pty_flag = 0;
! 972: ret->permit_user_rc = 0;
! 973: } else if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
! 974: ret->cert_authority = r;
! 975: } else if ((r = opt_flag("port-forwarding", 1, &opts)) != -1) {
! 976: ret->permit_port_forwarding_flag = r == 1;
! 977: } else if ((r = opt_flag("agent-forwarding", 1, &opts)) != -1) {
! 978: ret->permit_agent_forwarding_flag = r == 1;
! 979: } else if ((r = opt_flag("x11-forwarding", 1, &opts)) != -1) {
! 980: ret->permit_x11_forwarding_flag = r == 1;
! 981: } else if ((r = opt_flag("pty", 1, &opts)) != -1) {
! 982: ret->permit_pty_flag = r == 1;
! 983: } else if ((r = opt_flag("user-rc", 1, &opts)) != -1) {
! 984: ret->permit_user_rc = r == 1;
! 985: } else if (opt_match(&opts, "command")) {
! 986: if (ret->force_command != NULL) {
! 987: errstr = "multiple \"command\" clauses";
! 988: goto fail;
! 989: }
! 990: ret->force_command = opt_dequote(&opts, &errstr);
! 991: if (ret->force_command == NULL)
! 992: goto fail;
! 993: } else if (opt_match(&opts, "principals")) {
! 994: if (ret->cert_principals != NULL) {
! 995: errstr = "multiple \"principals\" clauses";
! 996: goto fail;
! 997: }
! 998: ret->cert_principals = opt_dequote(&opts, &errstr);
! 999: if (ret->cert_principals == NULL)
! 1000: goto fail;
! 1001: } else if (opt_match(&opts, "from")) {
! 1002: if (ret->required_from_host_keys != NULL) {
! 1003: errstr = "multiple \"from\" clauses";
! 1004: goto fail;
! 1005: }
! 1006: ret->required_from_host_keys = opt_dequote(&opts,
! 1007: &errstr);
! 1008: if (ret->required_from_host_keys == NULL)
! 1009: goto fail;
! 1010: } else if (opt_match(&opts, "environment")) {
! 1011: if (ret->nenv > INT_MAX) {
! 1012: errstr = "too many environment strings";
! 1013: goto fail;
! 1014: }
! 1015: if ((opt = opt_dequote(&opts, &errstr)) == NULL)
! 1016: goto fail;
! 1017: /* env name must be alphanumeric and followed by '=' */
! 1018: if ((tmp = strchr(opt, '=')) == NULL) {
! 1019: free(opt);
! 1020: errstr = "invalid environment string";
! 1021: goto fail;
! 1022: }
! 1023: for (cp = opt; cp < tmp; cp++) {
! 1024: if (!isalnum((u_char)*cp)) {
! 1025: free(opt);
! 1026: errstr = "invalid environment string";
! 1027: goto fail;
! 1028: }
! 1029: }
! 1030: /* Append it. */
! 1031: oarray = ret->env;
! 1032: if ((ret->env = recallocarray(ret->env, ret->nenv,
! 1033: ret->nenv + 1, sizeof(*ret->env))) == NULL) {
! 1034: free(opt);
! 1035: ret->env = oarray; /* put it back for cleanup */
! 1036: goto alloc_fail;
! 1037: }
! 1038: ret->env[ret->nenv++] = opt;
! 1039: } else if (opt_match(&opts, "permitopen")) {
! 1040: if (ret->npermitopen > INT_MAX) {
! 1041: errstr = "too many permitopens";
! 1042: goto fail;
! 1043: }
! 1044: if ((opt = opt_dequote(&opts, &errstr)) == NULL)
! 1045: goto fail;
! 1046: if ((tmp = strdup(opt)) == NULL) {
! 1047: free(opt);
! 1048: goto alloc_fail;
! 1049: }
! 1050: cp = tmp;
! 1051: /* validate syntax of permitopen before recording it. */
! 1052: host = hpdelim(&cp);
! 1053: if (host == NULL || strlen(host) >= NI_MAXHOST) {
! 1054: free(tmp);
! 1055: free(opt);
! 1056: errstr = "invalid permitopen hostname";
! 1057: goto fail;
! 1058: }
! 1059: /*
! 1060: * don't want to use permitopen_port to avoid
! 1061: * dependency on channels.[ch] here.
! 1062: */
! 1063: if (cp == NULL ||
! 1064: (strcmp(cp, "*") != 0 && a2port(cp) <= 0)) {
! 1065: free(tmp);
! 1066: free(opt);
! 1067: errstr = "invalid permitopen port";
! 1068: goto fail;
! 1069: }
! 1070: /* XXX - add streamlocal support */
! 1071: free(tmp);
! 1072: /* Record it */
! 1073: oarray = ret->permitopen;
! 1074: if ((ret->permitopen = recallocarray(ret->permitopen,
! 1075: ret->npermitopen, ret->npermitopen + 1,
! 1076: sizeof(*ret->permitopen))) == NULL) {
! 1077: free(opt);
! 1078: ret->permitopen = oarray;
! 1079: goto alloc_fail;
! 1080: }
! 1081: ret->permitopen[ret->npermitopen++] = opt;
! 1082: } else if (opt_match(&opts, "tunnel")) {
! 1083: if ((opt = opt_dequote(&opts, &errstr)) == NULL)
! 1084: goto fail;
! 1085: ret->force_tun_device = a2tun(opt, NULL);
! 1086: free(opt);
! 1087: if (ret->force_tun_device == SSH_TUNID_ERR) {
! 1088: errstr = "invalid tun device";
! 1089: goto fail;
! 1090: }
! 1091: }
! 1092: /*
! 1093: * Skip the comma, and move to the next option
! 1094: * (or break out if there are no more).
! 1095: */
! 1096: if (*opts == '\0' || *opts == ' ' || *opts == '\t')
! 1097: break; /* End of options. */
! 1098: /* Anything other than a comma is an unknown option */
! 1099: if (*opts != ',') {
! 1100: errstr = "unknown key option";
! 1101: goto fail;
! 1102: }
! 1103: opts++;
! 1104: if (*opts == '\0') {
! 1105: errstr = "unexpected end-of-options";
! 1106: goto fail;
! 1107: }
! 1108: }
! 1109:
! 1110: /* success */
! 1111: if (errstrp != NULL)
! 1112: *errstrp = NULL;
! 1113: return ret;
! 1114:
! 1115: alloc_fail:
! 1116: errstr = "memory allocation failed";
! 1117: fail:
! 1118: sshauthopt_free(ret);
! 1119: if (errstrp != NULL)
! 1120: *errstrp = errstr;
! 1121: return NULL;
! 1122: }
! 1123:
! 1124: struct sshauthopt *
! 1125: sshauthopt_from_cert(struct sshkey *k)
! 1126: {
! 1127: struct sshauthopt *ret;
! 1128:
! 1129: if (k == NULL || !sshkey_type_is_cert(k->type) || k->cert == NULL ||
! 1130: k->cert->type != SSH2_CERT_TYPE_USER)
! 1131: return NULL;
! 1132:
! 1133: if ((ret = sshauthopt_new()) == NULL)
! 1134: return NULL;
! 1135:
! 1136: /* Handle options and critical extensions separately */
! 1137: if (cert_option_list(ret, k->cert->critical,
! 1138: OPTIONS_CRITICAL, 1) == -1) {
! 1139: sshauthopt_free(ret);
! 1140: return NULL;
! 1141: }
! 1142: if (cert_option_list(ret, k->cert->extensions,
! 1143: OPTIONS_EXTENSIONS, 0) == -1) {
! 1144: sshauthopt_free(ret);
! 1145: return NULL;
! 1146: }
! 1147: /* success */
! 1148: return ret;
! 1149: }
! 1150:
! 1151: /*
! 1152: * Merges "additional" options to "primary" and returns the result.
! 1153: * NB. Some options from primary have primacy.
! 1154: */
! 1155: struct sshauthopt *
! 1156: sshauthopt_merge(const struct sshauthopt *primary,
! 1157: const struct sshauthopt *additional, const char **errstrp)
! 1158: {
! 1159: struct sshauthopt *ret;
! 1160: const char *errstr = "internal error";
! 1161: const char *tmp;
! 1162:
! 1163: if (errstrp != NULL)
! 1164: *errstrp = NULL;
! 1165:
! 1166: if ((ret = sshauthopt_new()) == NULL)
! 1167: goto alloc_fail;
! 1168:
! 1169: /* cert_authority and cert_principals are cleared in result */
! 1170:
! 1171: /* Prefer access lists from primary. */
! 1172: /* XXX err is both set and mismatch? */
! 1173: tmp = primary->required_from_host_cert;
! 1174: if (tmp == NULL)
! 1175: tmp = additional->required_from_host_cert;
! 1176: if (tmp != NULL && (ret->required_from_host_cert = strdup(tmp)) == NULL)
! 1177: goto alloc_fail;
! 1178: tmp = primary->required_from_host_keys;
! 1179: if (tmp == NULL)
! 1180: tmp = additional->required_from_host_keys;
! 1181: if (tmp != NULL && (ret->required_from_host_keys = strdup(tmp)) == NULL)
! 1182: goto alloc_fail;
! 1183:
! 1184: /* force_tun_device, permitopen and environment prefer the primary. */
! 1185: ret->force_tun_device = primary->force_tun_device;
! 1186: if (ret->force_tun_device == -1)
! 1187: ret->force_tun_device = additional->force_tun_device;
! 1188: if (primary->nenv > 0) {
! 1189: if (dup_strings(&ret->env, &ret->nenv,
! 1190: primary->env, primary->nenv) != 0)
! 1191: goto alloc_fail;
! 1192: } else if (additional->nenv) {
! 1193: if (dup_strings(&ret->env, &ret->nenv,
! 1194: additional->env, additional->nenv) != 0)
! 1195: goto alloc_fail;
! 1196: }
! 1197: if (primary->npermitopen > 0) {
! 1198: if (dup_strings(&ret->permitopen, &ret->npermitopen,
! 1199: primary->permitopen, primary->npermitopen) != 0)
! 1200: goto alloc_fail;
! 1201: } else if (additional->npermitopen > 0) {
! 1202: if (dup_strings(&ret->permitopen, &ret->npermitopen,
! 1203: additional->permitopen, additional->npermitopen) != 0)
! 1204: goto alloc_fail;
! 1205: }
! 1206:
! 1207: /* Flags are logical-AND (i.e. must be set in both for permission) */
! 1208: #define OPTFLAG(x) ret->x = (primary->x == 1) && (additional->x == 1)
! 1209: OPTFLAG(permit_port_forwarding_flag);
! 1210: OPTFLAG(permit_agent_forwarding_flag);
! 1211: OPTFLAG(permit_x11_forwarding_flag);
! 1212: OPTFLAG(permit_pty_flag);
! 1213: OPTFLAG(permit_user_rc);
! 1214: #undef OPTFLAG
! 1215:
! 1216: /*
! 1217: * When both multiple forced-command are specified, only
! 1218: * proceed if they are identical, otherwise fail.
! 1219: */
! 1220: if (primary->force_command != NULL &&
! 1221: additional->force_command != NULL) {
! 1222: if (strcmp(primary->force_command,
! 1223: additional->force_command) == 0) {
! 1224: /* ok */
! 1225: ret->force_command = strdup(primary->force_command);
! 1226: if (ret->force_command == NULL)
! 1227: goto alloc_fail;
! 1228: } else {
! 1229: errstr = "forced command options do not match";
! 1230: goto fail;
! 1231: }
! 1232: } else if (primary->force_command != NULL) {
! 1233: if ((ret->force_command = strdup(
! 1234: primary->force_command)) == NULL)
! 1235: goto alloc_fail;
! 1236: } else if (additional->force_command != NULL) {
! 1237: if ((ret->force_command = strdup(
! 1238: additional->force_command)) == NULL)
! 1239: goto alloc_fail;
! 1240: }
! 1241: /* success */
! 1242: if (errstrp != NULL)
! 1243: *errstrp = NULL;
! 1244: return ret;
! 1245:
! 1246: alloc_fail:
! 1247: errstr = "memory allocation failed";
! 1248: fail:
! 1249: if (errstrp != NULL)
! 1250: *errstrp = errstr;
! 1251: sshauthopt_free(ret);
! 1252: return NULL;
! 1253: }
! 1254:
! 1255: /*
! 1256: * Copy options
! 1257: */
! 1258: struct sshauthopt *
! 1259: sshauthopt_copy(const struct sshauthopt *orig)
! 1260: {
! 1261: struct sshauthopt *ret;
! 1262:
! 1263: if ((ret = sshauthopt_new()) == NULL)
! 1264: return NULL;
! 1265:
! 1266: #define OPTSCALAR(x) ret->x = orig->x
! 1267: OPTSCALAR(permit_port_forwarding_flag);
! 1268: OPTSCALAR(permit_agent_forwarding_flag);
! 1269: OPTSCALAR(permit_x11_forwarding_flag);
! 1270: OPTSCALAR(permit_pty_flag);
! 1271: OPTSCALAR(permit_user_rc);
! 1272: OPTSCALAR(restricted);
! 1273: OPTSCALAR(cert_authority);
! 1274: OPTSCALAR(force_tun_device);
! 1275: #undef OPTSCALAR
! 1276: #define OPTSTRING(x) \
! 1277: do { \
! 1278: if (orig->x != NULL && (ret->x = strdup(orig->x)) == NULL) { \
! 1279: sshauthopt_free(ret); \
! 1280: return NULL; \
! 1281: } \
! 1282: } while (0)
! 1283: OPTSTRING(cert_principals);
! 1284: OPTSTRING(force_command);
! 1285: OPTSTRING(required_from_host_cert);
! 1286: OPTSTRING(required_from_host_keys);
! 1287: #undef OPTSTRING
! 1288:
! 1289: if (dup_strings(&ret->env, &ret->nenv, orig->env, orig->nenv) != 0 ||
! 1290: dup_strings(&ret->permitopen, &ret->npermitopen,
! 1291: orig->permitopen, orig->npermitopen) != 0) {
! 1292: sshauthopt_free(ret);
! 1293: return NULL;
! 1294: }
! 1295: return ret;
! 1296: }
! 1297:
! 1298: static int
! 1299: serialise_array(struct sshbuf *m, char **a, size_t n)
! 1300: {
! 1301: struct sshbuf *b;
! 1302: size_t i;
! 1303: int r;
! 1304:
! 1305: if (n > INT_MAX)
! 1306: return SSH_ERR_INTERNAL_ERROR;
! 1307:
! 1308: if ((b = sshbuf_new()) == NULL) {
! 1309: return SSH_ERR_ALLOC_FAIL;
! 1310: }
! 1311: for (i = 0; i < n; i++) {
! 1312: if ((r = sshbuf_put_cstring(b, a[i])) != 0) {
! 1313: sshbuf_free(b);
! 1314: return r;
! 1315: }
! 1316: }
! 1317: if ((r = sshbuf_put_u32(m, n)) != 0 ||
! 1318: (r = sshbuf_put_stringb(m, b)) != 0) {
! 1319: sshbuf_free(b);
! 1320: return r;
! 1321: }
! 1322: /* success */
! 1323: return 0;
! 1324: }
! 1325:
! 1326: static int
! 1327: deserialise_array(struct sshbuf *m, char ***ap, size_t *np)
! 1328: {
! 1329: char **a = NULL;
! 1330: size_t i, n = 0;
! 1331: struct sshbuf *b = NULL;
! 1332: u_int tmp;
! 1333: int r = SSH_ERR_INTERNAL_ERROR;
! 1334:
! 1335: if ((r = sshbuf_get_u32(m, &tmp)) != 0 ||
! 1336: (r = sshbuf_froms(m, &b)) != 0)
! 1337: goto out;
! 1338: if (tmp > INT_MAX) {
! 1339: r = SSH_ERR_INVALID_FORMAT;
! 1340: goto out;
! 1341: }
! 1342: n = tmp;
! 1343: if (n > 0 && (a = calloc(n, sizeof(*a))) == NULL) {
! 1344: r = SSH_ERR_ALLOC_FAIL;
! 1345: goto out;
! 1346: }
! 1347: for (i = 0; i < n; i++) {
! 1348: if ((r = sshbuf_get_cstring(b, &a[i], NULL)) != 0)
! 1349: goto out;
! 1350: }
! 1351: /* success */
! 1352: r = 0;
! 1353: *ap = a;
! 1354: a = NULL;
! 1355: *np = n;
! 1356: n = 0;
! 1357: out:
! 1358: for (i = 0; i < n; i++)
! 1359: free(a[i]);
! 1360: free(a);
! 1361: sshbuf_free(b);
! 1362: return r;
! 1363: }
! 1364:
! 1365: static int
! 1366: serialise_nullable_string(struct sshbuf *m, const char *s)
! 1367: {
! 1368: int r;
! 1369:
! 1370: if ((r = sshbuf_put_u8(m, s == NULL)) != 0 ||
! 1371: (r = sshbuf_put_cstring(m, s)) != 0)
! 1372: return r;
! 1373: return 0;
! 1374: }
! 1375:
! 1376: static int
! 1377: deserialise_nullable_string(struct sshbuf *m, char **sp)
! 1378: {
! 1379: int r;
! 1380: u_char flag;
! 1381:
! 1382: *sp = NULL;
! 1383: if ((r = sshbuf_get_u8(m, &flag)) != 0 ||
! 1384: (r = sshbuf_get_cstring(m, flag ? NULL : sp, NULL)) != 0)
! 1385: return r;
! 1386: return 0;
! 1387: }
! 1388:
! 1389: int
! 1390: sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m,
! 1391: int untrusted)
! 1392: {
! 1393: int r = SSH_ERR_INTERNAL_ERROR;
! 1394:
! 1395: /* Flag options */
! 1396: if ((r = sshbuf_put_u8(m, opts->permit_port_forwarding_flag)) != 0 ||
! 1397: (r = sshbuf_put_u8(m, opts->permit_agent_forwarding_flag)) != 0 ||
! 1398: (r = sshbuf_put_u8(m, opts->permit_x11_forwarding_flag)) != 0 ||
! 1399: (r = sshbuf_put_u8(m, opts->permit_pty_flag)) != 0 ||
! 1400: (r = sshbuf_put_u8(m, opts->permit_user_rc)) != 0 ||
! 1401: (r = sshbuf_put_u8(m, opts->restricted)) != 0 ||
! 1402: (r = sshbuf_put_u8(m, opts->cert_authority)) != 0)
! 1403: return r;
! 1404:
! 1405: /* tunnel number can be negative to indicate "unset" */
! 1406: if ((r = sshbuf_put_u8(m, opts->force_tun_device == -1)) != 0 ||
! 1407: (r = sshbuf_put_u32(m, (opts->force_tun_device < 0) ?
! 1408: 0 : (u_int)opts->force_tun_device)) != 0)
! 1409: return r;
! 1410:
! 1411: /* String options; these may be NULL */
! 1412: if ((r = serialise_nullable_string(m,
! 1413: untrusted ? "yes" : opts->cert_principals)) != 0 ||
! 1414: (r = serialise_nullable_string(m,
! 1415: untrusted ? "true" : opts->force_command)) != 0 ||
! 1416: (r = serialise_nullable_string(m,
! 1417: untrusted ? NULL : opts->required_from_host_cert)) != 0 ||
! 1418: (r = serialise_nullable_string(m,
! 1419: untrusted ? NULL : opts->required_from_host_keys)) != 0)
! 1420: return r;
! 1421:
! 1422: /* Array options */
! 1423: if ((r = serialise_array(m, opts->env,
! 1424: untrusted ? 0 : opts->nenv)) != 0 ||
! 1425: (r = serialise_array(m, opts->permitopen,
! 1426: untrusted ? 0 : opts->npermitopen)) != 0)
! 1427: return r;
! 1428:
! 1429: /* success */
! 1430: return 0;
! 1431: }
! 1432:
! 1433: int
! 1434: sshauthopt_deserialise(struct sshbuf *m, struct sshauthopt **optsp)
! 1435: {
! 1436: struct sshauthopt *opts = NULL;
! 1437: int r = SSH_ERR_INTERNAL_ERROR;
! 1438: u_char f;
! 1439: u_int tmp;
! 1440:
! 1441: if ((opts = calloc(1, sizeof(*opts))) == NULL)
! 1442: return SSH_ERR_ALLOC_FAIL;
! 1443:
! 1444: #define OPT_FLAG(x) \
! 1445: do { \
! 1446: if ((r = sshbuf_get_u8(m, &f)) != 0) \
! 1447: goto out; \
! 1448: opts->x = f; \
! 1449: } while (0)
! 1450: OPT_FLAG(permit_port_forwarding_flag);
! 1451: OPT_FLAG(permit_agent_forwarding_flag);
! 1452: OPT_FLAG(permit_x11_forwarding_flag);
! 1453: OPT_FLAG(permit_pty_flag);
! 1454: OPT_FLAG(permit_user_rc);
! 1455: OPT_FLAG(restricted);
! 1456: OPT_FLAG(cert_authority);
! 1457: #undef OPT_FLAG
! 1458:
! 1459: /* tunnel number can be negative to indicate "unset" */
! 1460: if ((r = sshbuf_get_u8(m, &f)) != 0 ||
! 1461: (r = sshbuf_get_u32(m, &tmp)) != 0)
! 1462: goto out;
! 1463: opts->force_tun_device = f ? -1 : (int)tmp;
! 1464:
! 1465: /* String options may be NULL */
! 1466: if ((r = deserialise_nullable_string(m, &opts->cert_principals)) != 0 ||
! 1467: (r = deserialise_nullable_string(m, &opts->force_command)) != 0 ||
! 1468: (r = deserialise_nullable_string(m,
! 1469: &opts->required_from_host_cert)) != 0 ||
! 1470: (r = deserialise_nullable_string(m,
! 1471: &opts->required_from_host_keys)) != 0)
! 1472: goto out;
! 1473:
! 1474: /* Array options */
! 1475: if ((r = deserialise_array(m, &opts->env, &opts->nenv)) != 0 ||
! 1476: (r = deserialise_array(m,
! 1477: &opts->permitopen, &opts->npermitopen)) != 0)
! 1478: goto out;
! 1479:
! 1480: /* success */
! 1481: r = 0;
! 1482: *optsp = opts;
! 1483: opts = NULL;
! 1484: out:
! 1485: sshauthopt_free(opts);
! 1486: return r;
! 1487: }