Annotation of src/usr.bin/ssh/auth-options.c, Revision 1.83
1.83 ! djm 1: /* $OpenBSD: auth-options.c,v 1.82 2018/06/07 09:26:42 djm 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:
320: if (npermits > INT_MAX) {
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: */
! 332: if (asprintf(&tmp, "*:%s", opt) < 0) {
! 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: }
469: for (cp = opt; cp < tmp; cp++) {
1.79 djm 470: if (!isalnum((u_char)*cp) && *cp != '_') {
1.75 djm 471: free(opt);
472: errstr = "invalid environment string";
473: goto fail;
474: }
475: }
476: /* Append it. */
477: oarray = ret->env;
478: if ((ret->env = recallocarray(ret->env, ret->nenv,
479: ret->nenv + 1, sizeof(*ret->env))) == NULL) {
480: free(opt);
481: ret->env = oarray; /* put it back for cleanup */
482: goto alloc_fail;
483: }
484: ret->env[ret->nenv++] = opt;
485: } else if (opt_match(&opts, "permitopen")) {
1.83 ! djm 486: if (handle_permit(&opts, 0, &ret->permitopen,
1.80 djm 487: &ret->npermitopen, &errstr) != 0)
1.75 djm 488: goto fail;
1.80 djm 489: } else if (opt_match(&opts, "permitlisten")) {
1.83 ! djm 490: if (handle_permit(&opts, 1, &ret->permitlisten,
1.80 djm 491: &ret->npermitlisten, &errstr) != 0)
1.75 djm 492: goto fail;
493: } else if (opt_match(&opts, "tunnel")) {
494: if ((opt = opt_dequote(&opts, &errstr)) == NULL)
495: goto fail;
496: ret->force_tun_device = a2tun(opt, NULL);
497: free(opt);
498: if (ret->force_tun_device == SSH_TUNID_ERR) {
499: errstr = "invalid tun device";
500: goto fail;
501: }
502: }
503: /*
504: * Skip the comma, and move to the next option
505: * (or break out if there are no more).
506: */
507: if (*opts == '\0' || *opts == ' ' || *opts == '\t')
508: break; /* End of options. */
509: /* Anything other than a comma is an unknown option */
510: if (*opts != ',') {
511: errstr = "unknown key option";
512: goto fail;
513: }
514: opts++;
515: if (*opts == '\0') {
516: errstr = "unexpected end-of-options";
517: goto fail;
518: }
519: }
520:
521: /* success */
522: if (errstrp != NULL)
523: *errstrp = NULL;
524: return ret;
525:
526: alloc_fail:
527: errstr = "memory allocation failed";
528: fail:
529: sshauthopt_free(ret);
530: if (errstrp != NULL)
531: *errstrp = errstr;
532: return NULL;
533: }
534:
535: struct sshauthopt *
536: sshauthopt_from_cert(struct sshkey *k)
537: {
538: struct sshauthopt *ret;
539:
540: if (k == NULL || !sshkey_type_is_cert(k->type) || k->cert == NULL ||
541: k->cert->type != SSH2_CERT_TYPE_USER)
542: return NULL;
543:
544: if ((ret = sshauthopt_new()) == NULL)
545: return NULL;
546:
547: /* Handle options and critical extensions separately */
548: if (cert_option_list(ret, k->cert->critical,
549: OPTIONS_CRITICAL, 1) == -1) {
550: sshauthopt_free(ret);
551: return NULL;
552: }
553: if (cert_option_list(ret, k->cert->extensions,
554: OPTIONS_EXTENSIONS, 0) == -1) {
555: sshauthopt_free(ret);
556: return NULL;
557: }
558: /* success */
559: return ret;
560: }
561:
562: /*
563: * Merges "additional" options to "primary" and returns the result.
564: * NB. Some options from primary have primacy.
565: */
566: struct sshauthopt *
567: sshauthopt_merge(const struct sshauthopt *primary,
568: const struct sshauthopt *additional, const char **errstrp)
569: {
570: struct sshauthopt *ret;
571: const char *errstr = "internal error";
572: const char *tmp;
573:
574: if (errstrp != NULL)
575: *errstrp = NULL;
576:
577: if ((ret = sshauthopt_new()) == NULL)
578: goto alloc_fail;
579:
580: /* cert_authority and cert_principals are cleared in result */
581:
582: /* Prefer access lists from primary. */
583: /* XXX err is both set and mismatch? */
584: tmp = primary->required_from_host_cert;
585: if (tmp == NULL)
586: tmp = additional->required_from_host_cert;
587: if (tmp != NULL && (ret->required_from_host_cert = strdup(tmp)) == NULL)
588: goto alloc_fail;
589: tmp = primary->required_from_host_keys;
590: if (tmp == NULL)
591: tmp = additional->required_from_host_keys;
592: if (tmp != NULL && (ret->required_from_host_keys = strdup(tmp)) == NULL)
593: goto alloc_fail;
594:
1.80 djm 595: /*
596: * force_tun_device, permitopen/permitlisten and environment all
597: * prefer the primary.
598: */
1.75 djm 599: ret->force_tun_device = primary->force_tun_device;
600: if (ret->force_tun_device == -1)
601: ret->force_tun_device = additional->force_tun_device;
602: if (primary->nenv > 0) {
603: if (dup_strings(&ret->env, &ret->nenv,
604: primary->env, primary->nenv) != 0)
605: goto alloc_fail;
606: } else if (additional->nenv) {
607: if (dup_strings(&ret->env, &ret->nenv,
608: additional->env, additional->nenv) != 0)
609: goto alloc_fail;
610: }
611: if (primary->npermitopen > 0) {
612: if (dup_strings(&ret->permitopen, &ret->npermitopen,
613: primary->permitopen, primary->npermitopen) != 0)
614: goto alloc_fail;
615: } else if (additional->npermitopen > 0) {
616: if (dup_strings(&ret->permitopen, &ret->npermitopen,
617: additional->permitopen, additional->npermitopen) != 0)
618: goto alloc_fail;
619: }
620:
1.80 djm 621: if (primary->npermitlisten > 0) {
622: if (dup_strings(&ret->permitlisten, &ret->npermitlisten,
623: primary->permitlisten, primary->npermitlisten) != 0)
624: goto alloc_fail;
625: } else if (additional->npermitlisten > 0) {
626: if (dup_strings(&ret->permitlisten, &ret->npermitlisten,
627: additional->permitlisten, additional->npermitlisten) != 0)
628: goto alloc_fail;
629: }
630:
1.75 djm 631: /* Flags are logical-AND (i.e. must be set in both for permission) */
632: #define OPTFLAG(x) ret->x = (primary->x == 1) && (additional->x == 1)
633: OPTFLAG(permit_port_forwarding_flag);
634: OPTFLAG(permit_agent_forwarding_flag);
635: OPTFLAG(permit_x11_forwarding_flag);
636: OPTFLAG(permit_pty_flag);
637: OPTFLAG(permit_user_rc);
638: #undef OPTFLAG
639:
1.77 djm 640: /* Earliest expiry time should win */
641: if (primary->valid_before != 0)
642: ret->valid_before = primary->valid_before;
643: if (additional->valid_before != 0 &&
644: additional->valid_before < ret->valid_before)
645: ret->valid_before = additional->valid_before;
646:
1.75 djm 647: /*
648: * When both multiple forced-command are specified, only
649: * proceed if they are identical, otherwise fail.
650: */
651: if (primary->force_command != NULL &&
652: additional->force_command != NULL) {
653: if (strcmp(primary->force_command,
654: additional->force_command) == 0) {
655: /* ok */
656: ret->force_command = strdup(primary->force_command);
657: if (ret->force_command == NULL)
658: goto alloc_fail;
659: } else {
660: errstr = "forced command options do not match";
661: goto fail;
662: }
663: } else if (primary->force_command != NULL) {
664: if ((ret->force_command = strdup(
665: primary->force_command)) == NULL)
666: goto alloc_fail;
667: } else if (additional->force_command != NULL) {
668: if ((ret->force_command = strdup(
669: additional->force_command)) == NULL)
670: goto alloc_fail;
671: }
672: /* success */
673: if (errstrp != NULL)
674: *errstrp = NULL;
675: return ret;
676:
677: alloc_fail:
678: errstr = "memory allocation failed";
679: fail:
680: if (errstrp != NULL)
681: *errstrp = errstr;
682: sshauthopt_free(ret);
683: return NULL;
684: }
685:
686: /*
687: * Copy options
688: */
689: struct sshauthopt *
690: sshauthopt_copy(const struct sshauthopt *orig)
691: {
692: struct sshauthopt *ret;
693:
694: if ((ret = sshauthopt_new()) == NULL)
695: return NULL;
696:
697: #define OPTSCALAR(x) ret->x = orig->x
698: OPTSCALAR(permit_port_forwarding_flag);
699: OPTSCALAR(permit_agent_forwarding_flag);
700: OPTSCALAR(permit_x11_forwarding_flag);
701: OPTSCALAR(permit_pty_flag);
702: OPTSCALAR(permit_user_rc);
703: OPTSCALAR(restricted);
704: OPTSCALAR(cert_authority);
705: OPTSCALAR(force_tun_device);
1.77 djm 706: OPTSCALAR(valid_before);
1.75 djm 707: #undef OPTSCALAR
708: #define OPTSTRING(x) \
709: do { \
710: if (orig->x != NULL && (ret->x = strdup(orig->x)) == NULL) { \
711: sshauthopt_free(ret); \
712: return NULL; \
713: } \
714: } while (0)
715: OPTSTRING(cert_principals);
716: OPTSTRING(force_command);
717: OPTSTRING(required_from_host_cert);
718: OPTSTRING(required_from_host_keys);
719: #undef OPTSTRING
720:
721: if (dup_strings(&ret->env, &ret->nenv, orig->env, orig->nenv) != 0 ||
722: dup_strings(&ret->permitopen, &ret->npermitopen,
1.80 djm 723: orig->permitopen, orig->npermitopen) != 0 ||
724: dup_strings(&ret->permitlisten, &ret->npermitlisten,
725: orig->permitlisten, orig->npermitlisten) != 0) {
1.75 djm 726: sshauthopt_free(ret);
727: return NULL;
728: }
729: return ret;
730: }
731:
732: static int
733: serialise_array(struct sshbuf *m, char **a, size_t n)
734: {
735: struct sshbuf *b;
736: size_t i;
737: int r;
738:
739: if (n > INT_MAX)
740: return SSH_ERR_INTERNAL_ERROR;
741:
742: if ((b = sshbuf_new()) == NULL) {
743: return SSH_ERR_ALLOC_FAIL;
744: }
745: for (i = 0; i < n; i++) {
746: if ((r = sshbuf_put_cstring(b, a[i])) != 0) {
747: sshbuf_free(b);
748: return r;
749: }
750: }
751: if ((r = sshbuf_put_u32(m, n)) != 0 ||
752: (r = sshbuf_put_stringb(m, b)) != 0) {
753: sshbuf_free(b);
754: return r;
755: }
756: /* success */
757: return 0;
758: }
759:
760: static int
761: deserialise_array(struct sshbuf *m, char ***ap, size_t *np)
762: {
763: char **a = NULL;
764: size_t i, n = 0;
765: struct sshbuf *b = NULL;
766: u_int tmp;
767: int r = SSH_ERR_INTERNAL_ERROR;
768:
769: if ((r = sshbuf_get_u32(m, &tmp)) != 0 ||
770: (r = sshbuf_froms(m, &b)) != 0)
771: goto out;
772: if (tmp > INT_MAX) {
773: r = SSH_ERR_INVALID_FORMAT;
774: goto out;
775: }
776: n = tmp;
777: if (n > 0 && (a = calloc(n, sizeof(*a))) == NULL) {
778: r = SSH_ERR_ALLOC_FAIL;
779: goto out;
780: }
781: for (i = 0; i < n; i++) {
782: if ((r = sshbuf_get_cstring(b, &a[i], NULL)) != 0)
783: goto out;
784: }
785: /* success */
786: r = 0;
787: *ap = a;
788: a = NULL;
789: *np = n;
790: n = 0;
791: out:
792: for (i = 0; i < n; i++)
793: free(a[i]);
794: free(a);
795: sshbuf_free(b);
796: return r;
797: }
798:
799: static int
800: serialise_nullable_string(struct sshbuf *m, const char *s)
801: {
802: int r;
803:
804: if ((r = sshbuf_put_u8(m, s == NULL)) != 0 ||
805: (r = sshbuf_put_cstring(m, s)) != 0)
806: return r;
807: return 0;
808: }
809:
810: static int
811: deserialise_nullable_string(struct sshbuf *m, char **sp)
812: {
813: int r;
814: u_char flag;
815:
816: *sp = NULL;
817: if ((r = sshbuf_get_u8(m, &flag)) != 0 ||
818: (r = sshbuf_get_cstring(m, flag ? NULL : sp, NULL)) != 0)
819: return r;
820: return 0;
821: }
822:
823: int
824: sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m,
825: int untrusted)
826: {
827: int r = SSH_ERR_INTERNAL_ERROR;
828:
1.77 djm 829: /* Flag and simple integer options */
1.75 djm 830: if ((r = sshbuf_put_u8(m, opts->permit_port_forwarding_flag)) != 0 ||
831: (r = sshbuf_put_u8(m, opts->permit_agent_forwarding_flag)) != 0 ||
832: (r = sshbuf_put_u8(m, opts->permit_x11_forwarding_flag)) != 0 ||
833: (r = sshbuf_put_u8(m, opts->permit_pty_flag)) != 0 ||
834: (r = sshbuf_put_u8(m, opts->permit_user_rc)) != 0 ||
835: (r = sshbuf_put_u8(m, opts->restricted)) != 0 ||
1.77 djm 836: (r = sshbuf_put_u8(m, opts->cert_authority)) != 0 ||
837: (r = sshbuf_put_u64(m, opts->valid_before)) != 0)
1.75 djm 838: return r;
839:
840: /* tunnel number can be negative to indicate "unset" */
841: if ((r = sshbuf_put_u8(m, opts->force_tun_device == -1)) != 0 ||
842: (r = sshbuf_put_u32(m, (opts->force_tun_device < 0) ?
843: 0 : (u_int)opts->force_tun_device)) != 0)
844: return r;
845:
846: /* String options; these may be NULL */
847: if ((r = serialise_nullable_string(m,
848: untrusted ? "yes" : opts->cert_principals)) != 0 ||
849: (r = serialise_nullable_string(m,
850: untrusted ? "true" : opts->force_command)) != 0 ||
851: (r = serialise_nullable_string(m,
852: untrusted ? NULL : opts->required_from_host_cert)) != 0 ||
853: (r = serialise_nullable_string(m,
854: untrusted ? NULL : opts->required_from_host_keys)) != 0)
855: return r;
856:
857: /* Array options */
858: if ((r = serialise_array(m, opts->env,
859: untrusted ? 0 : opts->nenv)) != 0 ||
860: (r = serialise_array(m, opts->permitopen,
1.82 djm 861: untrusted ? 0 : opts->npermitopen)) != 0 ||
1.80 djm 862: (r = serialise_array(m, opts->permitlisten,
863: untrusted ? 0 : opts->npermitlisten)) != 0)
1.75 djm 864: return r;
865:
866: /* success */
867: return 0;
868: }
869:
870: int
871: sshauthopt_deserialise(struct sshbuf *m, struct sshauthopt **optsp)
872: {
873: struct sshauthopt *opts = NULL;
874: int r = SSH_ERR_INTERNAL_ERROR;
875: u_char f;
876: u_int tmp;
877:
878: if ((opts = calloc(1, sizeof(*opts))) == NULL)
879: return SSH_ERR_ALLOC_FAIL;
880:
881: #define OPT_FLAG(x) \
882: do { \
883: if ((r = sshbuf_get_u8(m, &f)) != 0) \
884: goto out; \
885: opts->x = f; \
886: } while (0)
887: OPT_FLAG(permit_port_forwarding_flag);
888: OPT_FLAG(permit_agent_forwarding_flag);
889: OPT_FLAG(permit_x11_forwarding_flag);
890: OPT_FLAG(permit_pty_flag);
891: OPT_FLAG(permit_user_rc);
892: OPT_FLAG(restricted);
893: OPT_FLAG(cert_authority);
894: #undef OPT_FLAG
1.77 djm 895:
896: if ((r = sshbuf_get_u64(m, &opts->valid_before)) != 0)
897: goto out;
1.75 djm 898:
899: /* tunnel number can be negative to indicate "unset" */
900: if ((r = sshbuf_get_u8(m, &f)) != 0 ||
901: (r = sshbuf_get_u32(m, &tmp)) != 0)
902: goto out;
903: opts->force_tun_device = f ? -1 : (int)tmp;
904:
905: /* String options may be NULL */
906: if ((r = deserialise_nullable_string(m, &opts->cert_principals)) != 0 ||
907: (r = deserialise_nullable_string(m, &opts->force_command)) != 0 ||
908: (r = deserialise_nullable_string(m,
909: &opts->required_from_host_cert)) != 0 ||
910: (r = deserialise_nullable_string(m,
911: &opts->required_from_host_keys)) != 0)
912: goto out;
913:
914: /* Array options */
915: if ((r = deserialise_array(m, &opts->env, &opts->nenv)) != 0 ||
916: (r = deserialise_array(m,
1.80 djm 917: &opts->permitopen, &opts->npermitopen)) != 0 ||
918: (r = deserialise_array(m,
919: &opts->permitlisten, &opts->npermitlisten)) != 0)
1.75 djm 920: goto out;
921:
922: /* success */
923: r = 0;
924: *optsp = opts;
925: opts = NULL;
926: out:
927: sshauthopt_free(opts);
928: return r;
929: }