Annotation of src/usr.bin/ssh/auth-options.c, Revision 1.86
1.86 ! djm 1: /* $OpenBSD: auth-options.c,v 1.85 2019/06/27 18:03:37 deraadt Exp $ */
1.75 djm 2: /*
3: * Copyright (c) 2018 Damien Miller <djm@mindrot.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
1.3 deraadt 17:
1.36 stevesk 18: #include <sys/types.h>
1.42 djm 19: #include <sys/queue.h>
1.36 stevesk 20:
1.37 stevesk 21: #include <netdb.h>
1.36 stevesk 22: #include <pwd.h>
1.39 stevesk 23: #include <string.h>
1.40 deraadt 24: #include <stdio.h>
25: #include <stdarg.h>
1.75 djm 26: #include <ctype.h>
27: #include <limits.h>
1.1 markus 28:
29: #include "xmalloc.h"
1.65 markus 30: #include "ssherr.h"
1.11 markus 31: #include "log.h"
1.65 markus 32: #include "sshbuf.h"
1.64 millert 33: #include "misc.h"
1.65 markus 34: #include "sshkey.h"
1.76 djm 35: #include "match.h"
36: #include "ssh2.h"
1.50 djm 37: #include "auth-options.h"
1.75 djm 38:
39: /*
40: * Match flag 'opt' in *optsp, and if allow_negate is set then also match
41: * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0
42: * if negated option matches.
43: * If the option or negated option matches, then *optsp is updated to
44: * point to the first character after the option.
45: */
46: static int
47: opt_flag(const char *opt, int allow_negate, const char **optsp)
48: {
49: size_t opt_len = strlen(opt);
50: const char *opts = *optsp;
51: int negate = 0;
52:
53: if (allow_negate && strncasecmp(opts, "no-", 3) == 0) {
54: opts += 3;
55: negate = 1;
56: }
57: if (strncasecmp(opts, opt, opt_len) == 0) {
58: *optsp = opts + opt_len;
59: return negate ? 0 : 1;
60: }
61: return -1;
62: }
63:
64: static char *
65: opt_dequote(const char **sp, const char **errstrp)
66: {
67: const char *s = *sp;
68: char *ret;
69: size_t i;
70:
71: *errstrp = NULL;
72: if (*s != '"') {
73: *errstrp = "missing start quote";
74: return NULL;
75: }
76: s++;
77: if ((ret = malloc(strlen((s)) + 1)) == NULL) {
78: *errstrp = "memory allocation failed";
79: return NULL;
80: }
81: for (i = 0; *s != '\0' && *s != '"';) {
82: if (s[0] == '\\' && s[1] == '"')
83: s++;
84: ret[i++] = *s++;
85: }
86: if (*s == '\0') {
87: *errstrp = "missing end quote";
88: free(ret);
89: return NULL;
90: }
91: ret[i] = '\0';
92: s++;
93: *sp = s;
94: return ret;
95: }
96:
97: static int
98: opt_match(const char **opts, const char *term)
99: {
100: if (strncasecmp((*opts), term, strlen(term)) == 0 &&
101: (*opts)[strlen(term)] == '=') {
102: *opts += strlen(term) + 1;
103: return 1;
104: }
105: return 0;
106: }
107:
108: static int
109: dup_strings(char ***dstp, size_t *ndstp, char **src, size_t nsrc)
110: {
111: char **dst;
112: size_t i, j;
113:
114: *dstp = NULL;
115: *ndstp = 0;
116: if (nsrc == 0)
117: return 0;
118:
119: if ((dst = calloc(nsrc, sizeof(*src))) == NULL)
120: return -1;
121: for (i = 0; i < nsrc; i++) {
122: if ((dst[i] = strdup(src[i])) == NULL) {
123: for (j = 0; j < i; j++)
124: free(dst[j]);
125: free(dst);
126: return -1;
127: }
128: }
129: /* success */
130: *dstp = dst;
131: *ndstp = nsrc;
132: return 0;
133: }
134:
135: #define OPTIONS_CRITICAL 1
136: #define OPTIONS_EXTENSIONS 2
137: static int
138: cert_option_list(struct sshauthopt *opts, struct sshbuf *oblob,
139: u_int which, int crit)
140: {
141: char *command, *allowed;
142: char *name = NULL;
143: struct sshbuf *c = NULL, *data = NULL;
144: int r, ret = -1, found;
145:
146: if ((c = sshbuf_fromb(oblob)) == NULL) {
147: error("%s: sshbuf_fromb failed", __func__);
148: goto out;
149: }
150:
151: while (sshbuf_len(c) > 0) {
152: sshbuf_free(data);
153: data = NULL;
154: if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 ||
155: (r = sshbuf_froms(c, &data)) != 0) {
156: error("Unable to parse certificate options: %s",
157: ssh_err(r));
158: goto out;
159: }
160: debug3("found certificate option \"%.100s\" len %zu",
161: name, sshbuf_len(data));
162: found = 0;
163: if ((which & OPTIONS_EXTENSIONS) != 0) {
164: if (strcmp(name, "permit-X11-forwarding") == 0) {
165: opts->permit_x11_forwarding_flag = 1;
166: found = 1;
167: } else if (strcmp(name,
168: "permit-agent-forwarding") == 0) {
169: opts->permit_agent_forwarding_flag = 1;
170: found = 1;
171: } else if (strcmp(name,
172: "permit-port-forwarding") == 0) {
173: opts->permit_port_forwarding_flag = 1;
174: found = 1;
175: } else if (strcmp(name, "permit-pty") == 0) {
176: opts->permit_pty_flag = 1;
177: found = 1;
178: } else if (strcmp(name, "permit-user-rc") == 0) {
179: opts->permit_user_rc = 1;
180: found = 1;
181: }
182: }
183: if (!found && (which & OPTIONS_CRITICAL) != 0) {
184: if (strcmp(name, "force-command") == 0) {
185: if ((r = sshbuf_get_cstring(data, &command,
186: NULL)) != 0) {
187: error("Unable to parse \"%s\" "
188: "section: %s", name, ssh_err(r));
189: goto out;
190: }
191: if (opts->force_command != NULL) {
192: error("Certificate has multiple "
193: "force-command options");
194: free(command);
195: goto out;
196: }
197: opts->force_command = command;
198: found = 1;
199: }
200: if (strcmp(name, "source-address") == 0) {
201: if ((r = sshbuf_get_cstring(data, &allowed,
202: NULL)) != 0) {
203: error("Unable to parse \"%s\" "
204: "section: %s", name, ssh_err(r));
205: goto out;
206: }
207: if (opts->required_from_host_cert != NULL) {
208: error("Certificate has multiple "
209: "source-address options");
210: free(allowed);
211: goto out;
212: }
213: /* Check syntax */
214: if (addr_match_cidr_list(NULL, allowed) == -1) {
215: error("Certificate source-address "
216: "contents invalid");
217: goto out;
218: }
219: opts->required_from_host_cert = allowed;
220: found = 1;
221: }
222: }
223:
224: if (!found) {
225: if (crit) {
226: error("Certificate critical option \"%s\" "
227: "is not supported", name);
228: goto out;
229: } else {
230: logit("Certificate extension \"%s\" "
231: "is not supported", name);
232: }
233: } else if (sshbuf_len(data) != 0) {
234: error("Certificate option \"%s\" corrupt "
235: "(extra data)", name);
236: goto out;
237: }
238: free(name);
239: name = NULL;
240: }
241: /* successfully parsed all options */
242: ret = 0;
243:
244: out:
245: free(name);
246: sshbuf_free(data);
247: sshbuf_free(c);
248: return ret;
249: }
250:
251: struct sshauthopt *
252: sshauthopt_new(void)
253: {
254: struct sshauthopt *ret;
255:
256: if ((ret = calloc(1, sizeof(*ret))) == NULL)
257: return NULL;
258: ret->force_tun_device = -1;
259: return ret;
260: }
261:
262: void
263: sshauthopt_free(struct sshauthopt *opts)
264: {
265: size_t i;
266:
267: if (opts == NULL)
268: return;
269:
270: free(opts->cert_principals);
271: free(opts->force_command);
272: free(opts->required_from_host_cert);
273: free(opts->required_from_host_keys);
274:
275: for (i = 0; i < opts->nenv; i++)
276: free(opts->env[i]);
277: free(opts->env);
278:
279: for (i = 0; i < opts->npermitopen; i++)
280: free(opts->permitopen[i]);
281: free(opts->permitopen);
282:
1.80 djm 283: for (i = 0; i < opts->npermitlisten; i++)
284: free(opts->permitlisten[i]);
285: free(opts->permitlisten);
286:
1.75 djm 287: explicit_bzero(opts, sizeof(*opts));
288: free(opts);
289: }
290:
291: struct sshauthopt *
292: sshauthopt_new_with_keys_defaults(void)
293: {
294: struct sshauthopt *ret = NULL;
295:
296: if ((ret = sshauthopt_new()) == NULL)
297: return NULL;
298:
299: /* Defaults for authorized_keys flags */
300: ret->permit_port_forwarding_flag = 1;
301: ret->permit_agent_forwarding_flag = 1;
302: ret->permit_x11_forwarding_flag = 1;
303: ret->permit_pty_flag = 1;
304: ret->permit_user_rc = 1;
305: return ret;
306: }
307:
1.80 djm 308: /*
309: * Parse and record a permitopen/permitlisten directive.
310: * Return 0 on success. Return -1 on failure and sets *errstrp to error reason.
311: */
312: static int
1.83 djm 313: handle_permit(const char **optsp, int allow_bare_port,
314: char ***permitsp, size_t *npermitsp, const char **errstrp)
1.80 djm 315: {
316: char *opt, *tmp, *cp, *host, **permits = *permitsp;
317: size_t npermits = *npermitsp;
318: const char *errstr = "unknown error";
319:
1.86 ! djm 320: if (npermits > SSH_AUTHOPT_PERMIT_MAX) {
1.80 djm 321: *errstrp = "too many permission directives";
322: return -1;
323: }
1.82 djm 324: if ((opt = opt_dequote(optsp, &errstr)) == NULL) {
1.80 djm 325: return -1;
326: }
1.83 djm 327: if (allow_bare_port && strchr(opt, ':') == NULL) {
328: /*
329: * Allow a bare port number in permitlisten to indicate a
330: * listen_host wildcard.
331: */
1.85 deraadt 332: if (asprintf(&tmp, "*:%s", opt) == -1) {
1.83 djm 333: *errstrp = "memory allocation failed";
334: return -1;
335: }
336: free(opt);
337: opt = tmp;
338: }
1.80 djm 339: if ((tmp = strdup(opt)) == NULL) {
340: free(opt);
341: *errstrp = "memory allocation failed";
342: return -1;
343: }
344: cp = tmp;
345: /* validate syntax before recording it. */
346: host = hpdelim(&cp);
347: if (host == NULL || strlen(host) >= NI_MAXHOST) {
348: free(tmp);
349: free(opt);
350: *errstrp = "invalid permission hostname";
351: return -1;
352: }
353: /*
354: * don't want to use permitopen_port to avoid
355: * dependency on channels.[ch] here.
356: */
357: if (cp == NULL ||
358: (strcmp(cp, "*") != 0 && a2port(cp) <= 0)) {
359: free(tmp);
360: free(opt);
361: *errstrp = "invalid permission port";
362: return -1;
363: }
364: /* XXX - add streamlocal support */
365: free(tmp);
366: /* Record it */
367: if ((permits = recallocarray(permits, npermits, npermits + 1,
368: sizeof(*permits))) == NULL) {
369: free(opt);
370: /* NB. don't update *permitsp if alloc fails */
371: *errstrp = "memory allocation failed";
372: return -1;
373: }
374: permits[npermits++] = opt;
375: *permitsp = permits;
376: *npermitsp = npermits;
377: return 0;
378: }
379:
1.75 djm 380: struct sshauthopt *
381: sshauthopt_parse(const char *opts, const char **errstrp)
382: {
1.80 djm 383: char **oarray, *opt, *cp, *tmp;
1.75 djm 384: int r;
385: struct sshauthopt *ret = NULL;
386: const char *errstr = "unknown error";
1.77 djm 387: uint64_t valid_before;
1.75 djm 388:
389: if (errstrp != NULL)
390: *errstrp = NULL;
391: if ((ret = sshauthopt_new_with_keys_defaults()) == NULL)
392: goto alloc_fail;
393:
394: if (opts == NULL)
395: return ret;
396:
397: while (*opts && *opts != ' ' && *opts != '\t') {
398: /* flag options */
399: if ((r = opt_flag("restrict", 0, &opts)) != -1) {
400: ret->restricted = 1;
401: ret->permit_port_forwarding_flag = 0;
402: ret->permit_agent_forwarding_flag = 0;
403: ret->permit_x11_forwarding_flag = 0;
404: ret->permit_pty_flag = 0;
405: ret->permit_user_rc = 0;
406: } else if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
407: ret->cert_authority = r;
408: } else if ((r = opt_flag("port-forwarding", 1, &opts)) != -1) {
409: ret->permit_port_forwarding_flag = r == 1;
410: } else if ((r = opt_flag("agent-forwarding", 1, &opts)) != -1) {
411: ret->permit_agent_forwarding_flag = r == 1;
412: } else if ((r = opt_flag("x11-forwarding", 1, &opts)) != -1) {
413: ret->permit_x11_forwarding_flag = r == 1;
414: } else if ((r = opt_flag("pty", 1, &opts)) != -1) {
415: ret->permit_pty_flag = r == 1;
416: } else if ((r = opt_flag("user-rc", 1, &opts)) != -1) {
417: ret->permit_user_rc = r == 1;
418: } else if (opt_match(&opts, "command")) {
419: if (ret->force_command != NULL) {
420: errstr = "multiple \"command\" clauses";
421: goto fail;
422: }
423: ret->force_command = opt_dequote(&opts, &errstr);
424: if (ret->force_command == NULL)
425: goto fail;
426: } else if (opt_match(&opts, "principals")) {
427: if (ret->cert_principals != NULL) {
428: errstr = "multiple \"principals\" clauses";
429: goto fail;
430: }
431: ret->cert_principals = opt_dequote(&opts, &errstr);
432: if (ret->cert_principals == NULL)
433: goto fail;
434: } else if (opt_match(&opts, "from")) {
435: if (ret->required_from_host_keys != NULL) {
436: errstr = "multiple \"from\" clauses";
437: goto fail;
438: }
439: ret->required_from_host_keys = opt_dequote(&opts,
440: &errstr);
441: if (ret->required_from_host_keys == NULL)
442: goto fail;
1.78 djm 443: } else if (opt_match(&opts, "expiry-time")) {
1.77 djm 444: if ((opt = opt_dequote(&opts, &errstr)) == NULL)
445: goto fail;
446: if (parse_absolute_time(opt, &valid_before) != 0 ||
447: valid_before == 0) {
448: free(opt);
449: errstr = "invalid expires time";
450: goto fail;
451: }
452: free(opt);
453: if (ret->valid_before == 0 ||
454: valid_before < ret->valid_before)
455: ret->valid_before = valid_before;
1.75 djm 456: } else if (opt_match(&opts, "environment")) {
457: if (ret->nenv > INT_MAX) {
458: errstr = "too many environment strings";
459: goto fail;
460: }
461: if ((opt = opt_dequote(&opts, &errstr)) == NULL)
462: goto fail;
463: /* env name must be alphanumeric and followed by '=' */
464: if ((tmp = strchr(opt, '=')) == NULL) {
465: free(opt);
466: errstr = "invalid environment string";
467: goto fail;
468: }
1.84 djm 469: if ((cp = strdup(opt)) == NULL)
470: goto alloc_fail;
471: cp[tmp - opt] = '\0'; /* truncate at '=' */
472: if (!valid_env_name(cp)) {
473: free(cp);
474: free(opt);
475: errstr = "invalid environment string";
476: goto fail;
1.75 djm 477: }
1.84 djm 478: free(cp);
1.75 djm 479: /* Append it. */
480: oarray = ret->env;
481: if ((ret->env = recallocarray(ret->env, ret->nenv,
482: ret->nenv + 1, sizeof(*ret->env))) == NULL) {
483: free(opt);
484: ret->env = oarray; /* put it back for cleanup */
485: goto alloc_fail;
486: }
487: ret->env[ret->nenv++] = opt;
488: } else if (opt_match(&opts, "permitopen")) {
1.83 djm 489: if (handle_permit(&opts, 0, &ret->permitopen,
1.80 djm 490: &ret->npermitopen, &errstr) != 0)
1.75 djm 491: goto fail;
1.80 djm 492: } else if (opt_match(&opts, "permitlisten")) {
1.83 djm 493: if (handle_permit(&opts, 1, &ret->permitlisten,
1.80 djm 494: &ret->npermitlisten, &errstr) != 0)
1.75 djm 495: goto fail;
496: } else if (opt_match(&opts, "tunnel")) {
497: if ((opt = opt_dequote(&opts, &errstr)) == NULL)
498: goto fail;
499: ret->force_tun_device = a2tun(opt, NULL);
500: free(opt);
501: if (ret->force_tun_device == SSH_TUNID_ERR) {
502: errstr = "invalid tun device";
503: goto fail;
504: }
505: }
506: /*
507: * Skip the comma, and move to the next option
508: * (or break out if there are no more).
509: */
510: if (*opts == '\0' || *opts == ' ' || *opts == '\t')
511: break; /* End of options. */
512: /* Anything other than a comma is an unknown option */
513: if (*opts != ',') {
514: errstr = "unknown key option";
515: goto fail;
516: }
517: opts++;
518: if (*opts == '\0') {
519: errstr = "unexpected end-of-options";
520: goto fail;
521: }
522: }
523:
524: /* success */
525: if (errstrp != NULL)
526: *errstrp = NULL;
527: return ret;
528:
529: alloc_fail:
530: errstr = "memory allocation failed";
531: fail:
532: sshauthopt_free(ret);
533: if (errstrp != NULL)
534: *errstrp = errstr;
535: return NULL;
536: }
537:
538: struct sshauthopt *
539: sshauthopt_from_cert(struct sshkey *k)
540: {
541: struct sshauthopt *ret;
542:
543: if (k == NULL || !sshkey_type_is_cert(k->type) || k->cert == NULL ||
544: k->cert->type != SSH2_CERT_TYPE_USER)
545: return NULL;
546:
547: if ((ret = sshauthopt_new()) == NULL)
548: return NULL;
549:
550: /* Handle options and critical extensions separately */
551: if (cert_option_list(ret, k->cert->critical,
552: OPTIONS_CRITICAL, 1) == -1) {
553: sshauthopt_free(ret);
554: return NULL;
555: }
556: if (cert_option_list(ret, k->cert->extensions,
557: OPTIONS_EXTENSIONS, 0) == -1) {
558: sshauthopt_free(ret);
559: return NULL;
560: }
561: /* success */
562: return ret;
563: }
564:
565: /*
566: * Merges "additional" options to "primary" and returns the result.
567: * NB. Some options from primary have primacy.
568: */
569: struct sshauthopt *
570: sshauthopt_merge(const struct sshauthopt *primary,
571: const struct sshauthopt *additional, const char **errstrp)
572: {
573: struct sshauthopt *ret;
574: const char *errstr = "internal error";
575: const char *tmp;
576:
577: if (errstrp != NULL)
578: *errstrp = NULL;
579:
580: if ((ret = sshauthopt_new()) == NULL)
581: goto alloc_fail;
582:
583: /* cert_authority and cert_principals are cleared in result */
584:
585: /* Prefer access lists from primary. */
586: /* XXX err is both set and mismatch? */
587: tmp = primary->required_from_host_cert;
588: if (tmp == NULL)
589: tmp = additional->required_from_host_cert;
590: if (tmp != NULL && (ret->required_from_host_cert = strdup(tmp)) == NULL)
591: goto alloc_fail;
592: tmp = primary->required_from_host_keys;
593: if (tmp == NULL)
594: tmp = additional->required_from_host_keys;
595: if (tmp != NULL && (ret->required_from_host_keys = strdup(tmp)) == NULL)
596: goto alloc_fail;
597:
1.80 djm 598: /*
599: * force_tun_device, permitopen/permitlisten and environment all
600: * prefer the primary.
601: */
1.75 djm 602: ret->force_tun_device = primary->force_tun_device;
603: if (ret->force_tun_device == -1)
604: ret->force_tun_device = additional->force_tun_device;
605: if (primary->nenv > 0) {
606: if (dup_strings(&ret->env, &ret->nenv,
607: primary->env, primary->nenv) != 0)
608: goto alloc_fail;
609: } else if (additional->nenv) {
610: if (dup_strings(&ret->env, &ret->nenv,
611: additional->env, additional->nenv) != 0)
612: goto alloc_fail;
613: }
614: if (primary->npermitopen > 0) {
615: if (dup_strings(&ret->permitopen, &ret->npermitopen,
616: primary->permitopen, primary->npermitopen) != 0)
617: goto alloc_fail;
618: } else if (additional->npermitopen > 0) {
619: if (dup_strings(&ret->permitopen, &ret->npermitopen,
620: additional->permitopen, additional->npermitopen) != 0)
621: goto alloc_fail;
622: }
623:
1.80 djm 624: if (primary->npermitlisten > 0) {
625: if (dup_strings(&ret->permitlisten, &ret->npermitlisten,
626: primary->permitlisten, primary->npermitlisten) != 0)
627: goto alloc_fail;
628: } else if (additional->npermitlisten > 0) {
629: if (dup_strings(&ret->permitlisten, &ret->npermitlisten,
630: additional->permitlisten, additional->npermitlisten) != 0)
631: goto alloc_fail;
632: }
633:
1.75 djm 634: /* Flags are logical-AND (i.e. must be set in both for permission) */
635: #define OPTFLAG(x) ret->x = (primary->x == 1) && (additional->x == 1)
636: OPTFLAG(permit_port_forwarding_flag);
637: OPTFLAG(permit_agent_forwarding_flag);
638: OPTFLAG(permit_x11_forwarding_flag);
639: OPTFLAG(permit_pty_flag);
640: OPTFLAG(permit_user_rc);
641: #undef OPTFLAG
642:
1.77 djm 643: /* Earliest expiry time should win */
644: if (primary->valid_before != 0)
645: ret->valid_before = primary->valid_before;
646: if (additional->valid_before != 0 &&
647: additional->valid_before < ret->valid_before)
648: ret->valid_before = additional->valid_before;
649:
1.75 djm 650: /*
651: * When both multiple forced-command are specified, only
652: * proceed if they are identical, otherwise fail.
653: */
654: if (primary->force_command != NULL &&
655: additional->force_command != NULL) {
656: if (strcmp(primary->force_command,
657: additional->force_command) == 0) {
658: /* ok */
659: ret->force_command = strdup(primary->force_command);
660: if (ret->force_command == NULL)
661: goto alloc_fail;
662: } else {
663: errstr = "forced command options do not match";
664: goto fail;
665: }
666: } else if (primary->force_command != NULL) {
667: if ((ret->force_command = strdup(
668: primary->force_command)) == NULL)
669: goto alloc_fail;
670: } else if (additional->force_command != NULL) {
671: if ((ret->force_command = strdup(
672: additional->force_command)) == NULL)
673: goto alloc_fail;
674: }
675: /* success */
676: if (errstrp != NULL)
677: *errstrp = NULL;
678: return ret;
679:
680: alloc_fail:
681: errstr = "memory allocation failed";
682: fail:
683: if (errstrp != NULL)
684: *errstrp = errstr;
685: sshauthopt_free(ret);
686: return NULL;
687: }
688:
689: /*
690: * Copy options
691: */
692: struct sshauthopt *
693: sshauthopt_copy(const struct sshauthopt *orig)
694: {
695: struct sshauthopt *ret;
696:
697: if ((ret = sshauthopt_new()) == NULL)
698: return NULL;
699:
700: #define OPTSCALAR(x) ret->x = orig->x
701: OPTSCALAR(permit_port_forwarding_flag);
702: OPTSCALAR(permit_agent_forwarding_flag);
703: OPTSCALAR(permit_x11_forwarding_flag);
704: OPTSCALAR(permit_pty_flag);
705: OPTSCALAR(permit_user_rc);
706: OPTSCALAR(restricted);
707: OPTSCALAR(cert_authority);
708: OPTSCALAR(force_tun_device);
1.77 djm 709: OPTSCALAR(valid_before);
1.75 djm 710: #undef OPTSCALAR
711: #define OPTSTRING(x) \
712: do { \
713: if (orig->x != NULL && (ret->x = strdup(orig->x)) == NULL) { \
714: sshauthopt_free(ret); \
715: return NULL; \
716: } \
717: } while (0)
718: OPTSTRING(cert_principals);
719: OPTSTRING(force_command);
720: OPTSTRING(required_from_host_cert);
721: OPTSTRING(required_from_host_keys);
722: #undef OPTSTRING
723:
724: if (dup_strings(&ret->env, &ret->nenv, orig->env, orig->nenv) != 0 ||
725: dup_strings(&ret->permitopen, &ret->npermitopen,
1.80 djm 726: orig->permitopen, orig->npermitopen) != 0 ||
727: dup_strings(&ret->permitlisten, &ret->npermitlisten,
728: orig->permitlisten, orig->npermitlisten) != 0) {
1.75 djm 729: sshauthopt_free(ret);
730: return NULL;
731: }
732: return ret;
733: }
734:
735: static int
736: serialise_array(struct sshbuf *m, char **a, size_t n)
737: {
738: struct sshbuf *b;
739: size_t i;
740: int r;
741:
742: if (n > INT_MAX)
743: return SSH_ERR_INTERNAL_ERROR;
744:
745: if ((b = sshbuf_new()) == NULL) {
746: return SSH_ERR_ALLOC_FAIL;
747: }
748: for (i = 0; i < n; i++) {
749: if ((r = sshbuf_put_cstring(b, a[i])) != 0) {
750: sshbuf_free(b);
751: return r;
752: }
753: }
754: if ((r = sshbuf_put_u32(m, n)) != 0 ||
755: (r = sshbuf_put_stringb(m, b)) != 0) {
756: sshbuf_free(b);
757: return r;
758: }
759: /* success */
760: return 0;
761: }
762:
763: static int
764: deserialise_array(struct sshbuf *m, char ***ap, size_t *np)
765: {
766: char **a = NULL;
767: size_t i, n = 0;
768: struct sshbuf *b = NULL;
769: u_int tmp;
770: int r = SSH_ERR_INTERNAL_ERROR;
771:
772: if ((r = sshbuf_get_u32(m, &tmp)) != 0 ||
773: (r = sshbuf_froms(m, &b)) != 0)
774: goto out;
775: if (tmp > INT_MAX) {
776: r = SSH_ERR_INVALID_FORMAT;
777: goto out;
778: }
779: n = tmp;
780: if (n > 0 && (a = calloc(n, sizeof(*a))) == NULL) {
781: r = SSH_ERR_ALLOC_FAIL;
782: goto out;
783: }
784: for (i = 0; i < n; i++) {
785: if ((r = sshbuf_get_cstring(b, &a[i], NULL)) != 0)
786: goto out;
787: }
788: /* success */
789: r = 0;
790: *ap = a;
791: a = NULL;
792: *np = n;
793: n = 0;
794: out:
795: for (i = 0; i < n; i++)
796: free(a[i]);
797: free(a);
798: sshbuf_free(b);
799: return r;
800: }
801:
802: static int
803: serialise_nullable_string(struct sshbuf *m, const char *s)
804: {
805: int r;
806:
807: if ((r = sshbuf_put_u8(m, s == NULL)) != 0 ||
808: (r = sshbuf_put_cstring(m, s)) != 0)
809: return r;
810: return 0;
811: }
812:
813: static int
814: deserialise_nullable_string(struct sshbuf *m, char **sp)
815: {
816: int r;
817: u_char flag;
818:
819: *sp = NULL;
820: if ((r = sshbuf_get_u8(m, &flag)) != 0 ||
821: (r = sshbuf_get_cstring(m, flag ? NULL : sp, NULL)) != 0)
822: return r;
823: return 0;
824: }
825:
826: int
827: sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m,
828: int untrusted)
829: {
830: int r = SSH_ERR_INTERNAL_ERROR;
831:
1.77 djm 832: /* Flag and simple integer options */
1.75 djm 833: if ((r = sshbuf_put_u8(m, opts->permit_port_forwarding_flag)) != 0 ||
834: (r = sshbuf_put_u8(m, opts->permit_agent_forwarding_flag)) != 0 ||
835: (r = sshbuf_put_u8(m, opts->permit_x11_forwarding_flag)) != 0 ||
836: (r = sshbuf_put_u8(m, opts->permit_pty_flag)) != 0 ||
837: (r = sshbuf_put_u8(m, opts->permit_user_rc)) != 0 ||
838: (r = sshbuf_put_u8(m, opts->restricted)) != 0 ||
1.77 djm 839: (r = sshbuf_put_u8(m, opts->cert_authority)) != 0 ||
840: (r = sshbuf_put_u64(m, opts->valid_before)) != 0)
1.75 djm 841: return r;
842:
843: /* tunnel number can be negative to indicate "unset" */
844: if ((r = sshbuf_put_u8(m, opts->force_tun_device == -1)) != 0 ||
845: (r = sshbuf_put_u32(m, (opts->force_tun_device < 0) ?
846: 0 : (u_int)opts->force_tun_device)) != 0)
847: return r;
848:
849: /* String options; these may be NULL */
850: if ((r = serialise_nullable_string(m,
851: untrusted ? "yes" : opts->cert_principals)) != 0 ||
852: (r = serialise_nullable_string(m,
853: untrusted ? "true" : opts->force_command)) != 0 ||
854: (r = serialise_nullable_string(m,
855: untrusted ? NULL : opts->required_from_host_cert)) != 0 ||
856: (r = serialise_nullable_string(m,
857: untrusted ? NULL : opts->required_from_host_keys)) != 0)
858: return r;
859:
860: /* Array options */
861: if ((r = serialise_array(m, opts->env,
862: untrusted ? 0 : opts->nenv)) != 0 ||
863: (r = serialise_array(m, opts->permitopen,
1.82 djm 864: untrusted ? 0 : opts->npermitopen)) != 0 ||
1.80 djm 865: (r = serialise_array(m, opts->permitlisten,
866: untrusted ? 0 : opts->npermitlisten)) != 0)
1.75 djm 867: return r;
868:
869: /* success */
870: return 0;
871: }
872:
873: int
874: sshauthopt_deserialise(struct sshbuf *m, struct sshauthopt **optsp)
875: {
876: struct sshauthopt *opts = NULL;
877: int r = SSH_ERR_INTERNAL_ERROR;
878: u_char f;
879: u_int tmp;
880:
881: if ((opts = calloc(1, sizeof(*opts))) == NULL)
882: return SSH_ERR_ALLOC_FAIL;
883:
884: #define OPT_FLAG(x) \
885: do { \
886: if ((r = sshbuf_get_u8(m, &f)) != 0) \
887: goto out; \
888: opts->x = f; \
889: } while (0)
890: OPT_FLAG(permit_port_forwarding_flag);
891: OPT_FLAG(permit_agent_forwarding_flag);
892: OPT_FLAG(permit_x11_forwarding_flag);
893: OPT_FLAG(permit_pty_flag);
894: OPT_FLAG(permit_user_rc);
895: OPT_FLAG(restricted);
896: OPT_FLAG(cert_authority);
897: #undef OPT_FLAG
1.77 djm 898:
899: if ((r = sshbuf_get_u64(m, &opts->valid_before)) != 0)
900: goto out;
1.75 djm 901:
902: /* tunnel number can be negative to indicate "unset" */
903: if ((r = sshbuf_get_u8(m, &f)) != 0 ||
904: (r = sshbuf_get_u32(m, &tmp)) != 0)
905: goto out;
906: opts->force_tun_device = f ? -1 : (int)tmp;
907:
908: /* String options may be NULL */
909: if ((r = deserialise_nullable_string(m, &opts->cert_principals)) != 0 ||
910: (r = deserialise_nullable_string(m, &opts->force_command)) != 0 ||
911: (r = deserialise_nullable_string(m,
912: &opts->required_from_host_cert)) != 0 ||
913: (r = deserialise_nullable_string(m,
914: &opts->required_from_host_keys)) != 0)
915: goto out;
916:
917: /* Array options */
918: if ((r = deserialise_array(m, &opts->env, &opts->nenv)) != 0 ||
919: (r = deserialise_array(m,
1.80 djm 920: &opts->permitopen, &opts->npermitopen)) != 0 ||
921: (r = deserialise_array(m,
922: &opts->permitlisten, &opts->npermitlisten)) != 0)
1.75 djm 923: goto out;
924:
925: /* success */
926: r = 0;
927: *optsp = opts;
928: opts = NULL;
929: out:
930: sshauthopt_free(opts);
931: return r;
932: }