Annotation of src/usr.bin/ssh/auth-options.c, Revision 1.82
1.82 ! djm 1: /* $OpenBSD: auth-options.c,v 1.81 2018/06/07 04:31:51 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.82 ! djm 313: handle_permit(const char **optsp, char ***permitsp, size_t *npermitsp,
1.80 djm 314: const char **errstrp)
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: }
327: if ((tmp = strdup(opt)) == NULL) {
328: free(opt);
329: *errstrp = "memory allocation failed";
330: return -1;
331: }
332: cp = tmp;
333: /* validate syntax before recording it. */
334: host = hpdelim(&cp);
335: if (host == NULL || strlen(host) >= NI_MAXHOST) {
336: free(tmp);
337: free(opt);
338: *errstrp = "invalid permission hostname";
339: return -1;
340: }
341: /*
342: * don't want to use permitopen_port to avoid
343: * dependency on channels.[ch] here.
344: */
345: if (cp == NULL ||
346: (strcmp(cp, "*") != 0 && a2port(cp) <= 0)) {
347: free(tmp);
348: free(opt);
349: *errstrp = "invalid permission port";
350: return -1;
351: }
352: /* XXX - add streamlocal support */
353: free(tmp);
354: /* Record it */
355: if ((permits = recallocarray(permits, npermits, npermits + 1,
356: sizeof(*permits))) == NULL) {
357: free(opt);
358: /* NB. don't update *permitsp if alloc fails */
359: *errstrp = "memory allocation failed";
360: return -1;
361: }
362: permits[npermits++] = opt;
363: *permitsp = permits;
364: *npermitsp = npermits;
365: return 0;
366: }
367:
1.75 djm 368: struct sshauthopt *
369: sshauthopt_parse(const char *opts, const char **errstrp)
370: {
1.80 djm 371: char **oarray, *opt, *cp, *tmp;
1.75 djm 372: int r;
373: struct sshauthopt *ret = NULL;
374: const char *errstr = "unknown error";
1.77 djm 375: uint64_t valid_before;
1.75 djm 376:
377: if (errstrp != NULL)
378: *errstrp = NULL;
379: if ((ret = sshauthopt_new_with_keys_defaults()) == NULL)
380: goto alloc_fail;
381:
382: if (opts == NULL)
383: return ret;
384:
385: while (*opts && *opts != ' ' && *opts != '\t') {
386: /* flag options */
387: if ((r = opt_flag("restrict", 0, &opts)) != -1) {
388: ret->restricted = 1;
389: ret->permit_port_forwarding_flag = 0;
390: ret->permit_agent_forwarding_flag = 0;
391: ret->permit_x11_forwarding_flag = 0;
392: ret->permit_pty_flag = 0;
393: ret->permit_user_rc = 0;
394: } else if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
395: ret->cert_authority = r;
396: } else if ((r = opt_flag("port-forwarding", 1, &opts)) != -1) {
397: ret->permit_port_forwarding_flag = r == 1;
398: } else if ((r = opt_flag("agent-forwarding", 1, &opts)) != -1) {
399: ret->permit_agent_forwarding_flag = r == 1;
400: } else if ((r = opt_flag("x11-forwarding", 1, &opts)) != -1) {
401: ret->permit_x11_forwarding_flag = r == 1;
402: } else if ((r = opt_flag("pty", 1, &opts)) != -1) {
403: ret->permit_pty_flag = r == 1;
404: } else if ((r = opt_flag("user-rc", 1, &opts)) != -1) {
405: ret->permit_user_rc = r == 1;
406: } else if (opt_match(&opts, "command")) {
407: if (ret->force_command != NULL) {
408: errstr = "multiple \"command\" clauses";
409: goto fail;
410: }
411: ret->force_command = opt_dequote(&opts, &errstr);
412: if (ret->force_command == NULL)
413: goto fail;
414: } else if (opt_match(&opts, "principals")) {
415: if (ret->cert_principals != NULL) {
416: errstr = "multiple \"principals\" clauses";
417: goto fail;
418: }
419: ret->cert_principals = opt_dequote(&opts, &errstr);
420: if (ret->cert_principals == NULL)
421: goto fail;
422: } else if (opt_match(&opts, "from")) {
423: if (ret->required_from_host_keys != NULL) {
424: errstr = "multiple \"from\" clauses";
425: goto fail;
426: }
427: ret->required_from_host_keys = opt_dequote(&opts,
428: &errstr);
429: if (ret->required_from_host_keys == NULL)
430: goto fail;
1.78 djm 431: } else if (opt_match(&opts, "expiry-time")) {
1.77 djm 432: if ((opt = opt_dequote(&opts, &errstr)) == NULL)
433: goto fail;
434: if (parse_absolute_time(opt, &valid_before) != 0 ||
435: valid_before == 0) {
436: free(opt);
437: errstr = "invalid expires time";
438: goto fail;
439: }
440: free(opt);
441: if (ret->valid_before == 0 ||
442: valid_before < ret->valid_before)
443: ret->valid_before = valid_before;
1.75 djm 444: } else if (opt_match(&opts, "environment")) {
445: if (ret->nenv > INT_MAX) {
446: errstr = "too many environment strings";
447: goto fail;
448: }
449: if ((opt = opt_dequote(&opts, &errstr)) == NULL)
450: goto fail;
451: /* env name must be alphanumeric and followed by '=' */
452: if ((tmp = strchr(opt, '=')) == NULL) {
453: free(opt);
454: errstr = "invalid environment string";
455: goto fail;
456: }
457: for (cp = opt; cp < tmp; cp++) {
1.79 djm 458: if (!isalnum((u_char)*cp) && *cp != '_') {
1.75 djm 459: free(opt);
460: errstr = "invalid environment string";
461: goto fail;
462: }
463: }
464: /* Append it. */
465: oarray = ret->env;
466: if ((ret->env = recallocarray(ret->env, ret->nenv,
467: ret->nenv + 1, sizeof(*ret->env))) == NULL) {
468: free(opt);
469: ret->env = oarray; /* put it back for cleanup */
470: goto alloc_fail;
471: }
472: ret->env[ret->nenv++] = opt;
473: } else if (opt_match(&opts, "permitopen")) {
1.81 djm 474: if (handle_permit(&opts, &ret->permitopen,
1.80 djm 475: &ret->npermitopen, &errstr) != 0)
1.75 djm 476: goto fail;
1.80 djm 477: } else if (opt_match(&opts, "permitlisten")) {
1.81 djm 478: if (handle_permit(&opts, &ret->permitlisten,
1.80 djm 479: &ret->npermitlisten, &errstr) != 0)
1.75 djm 480: goto fail;
481: } else if (opt_match(&opts, "tunnel")) {
482: if ((opt = opt_dequote(&opts, &errstr)) == NULL)
483: goto fail;
484: ret->force_tun_device = a2tun(opt, NULL);
485: free(opt);
486: if (ret->force_tun_device == SSH_TUNID_ERR) {
487: errstr = "invalid tun device";
488: goto fail;
489: }
490: }
491: /*
492: * Skip the comma, and move to the next option
493: * (or break out if there are no more).
494: */
495: if (*opts == '\0' || *opts == ' ' || *opts == '\t')
496: break; /* End of options. */
497: /* Anything other than a comma is an unknown option */
498: if (*opts != ',') {
499: errstr = "unknown key option";
500: goto fail;
501: }
502: opts++;
503: if (*opts == '\0') {
504: errstr = "unexpected end-of-options";
505: goto fail;
506: }
507: }
508:
509: /* success */
510: if (errstrp != NULL)
511: *errstrp = NULL;
512: return ret;
513:
514: alloc_fail:
515: errstr = "memory allocation failed";
516: fail:
517: sshauthopt_free(ret);
518: if (errstrp != NULL)
519: *errstrp = errstr;
520: return NULL;
521: }
522:
523: struct sshauthopt *
524: sshauthopt_from_cert(struct sshkey *k)
525: {
526: struct sshauthopt *ret;
527:
528: if (k == NULL || !sshkey_type_is_cert(k->type) || k->cert == NULL ||
529: k->cert->type != SSH2_CERT_TYPE_USER)
530: return NULL;
531:
532: if ((ret = sshauthopt_new()) == NULL)
533: return NULL;
534:
535: /* Handle options and critical extensions separately */
536: if (cert_option_list(ret, k->cert->critical,
537: OPTIONS_CRITICAL, 1) == -1) {
538: sshauthopt_free(ret);
539: return NULL;
540: }
541: if (cert_option_list(ret, k->cert->extensions,
542: OPTIONS_EXTENSIONS, 0) == -1) {
543: sshauthopt_free(ret);
544: return NULL;
545: }
546: /* success */
547: return ret;
548: }
549:
550: /*
551: * Merges "additional" options to "primary" and returns the result.
552: * NB. Some options from primary have primacy.
553: */
554: struct sshauthopt *
555: sshauthopt_merge(const struct sshauthopt *primary,
556: const struct sshauthopt *additional, const char **errstrp)
557: {
558: struct sshauthopt *ret;
559: const char *errstr = "internal error";
560: const char *tmp;
561:
562: if (errstrp != NULL)
563: *errstrp = NULL;
564:
565: if ((ret = sshauthopt_new()) == NULL)
566: goto alloc_fail;
567:
568: /* cert_authority and cert_principals are cleared in result */
569:
570: /* Prefer access lists from primary. */
571: /* XXX err is both set and mismatch? */
572: tmp = primary->required_from_host_cert;
573: if (tmp == NULL)
574: tmp = additional->required_from_host_cert;
575: if (tmp != NULL && (ret->required_from_host_cert = strdup(tmp)) == NULL)
576: goto alloc_fail;
577: tmp = primary->required_from_host_keys;
578: if (tmp == NULL)
579: tmp = additional->required_from_host_keys;
580: if (tmp != NULL && (ret->required_from_host_keys = strdup(tmp)) == NULL)
581: goto alloc_fail;
582:
1.80 djm 583: /*
584: * force_tun_device, permitopen/permitlisten and environment all
585: * prefer the primary.
586: */
1.75 djm 587: ret->force_tun_device = primary->force_tun_device;
588: if (ret->force_tun_device == -1)
589: ret->force_tun_device = additional->force_tun_device;
590: if (primary->nenv > 0) {
591: if (dup_strings(&ret->env, &ret->nenv,
592: primary->env, primary->nenv) != 0)
593: goto alloc_fail;
594: } else if (additional->nenv) {
595: if (dup_strings(&ret->env, &ret->nenv,
596: additional->env, additional->nenv) != 0)
597: goto alloc_fail;
598: }
599: if (primary->npermitopen > 0) {
600: if (dup_strings(&ret->permitopen, &ret->npermitopen,
601: primary->permitopen, primary->npermitopen) != 0)
602: goto alloc_fail;
603: } else if (additional->npermitopen > 0) {
604: if (dup_strings(&ret->permitopen, &ret->npermitopen,
605: additional->permitopen, additional->npermitopen) != 0)
606: goto alloc_fail;
607: }
608:
1.80 djm 609: if (primary->npermitlisten > 0) {
610: if (dup_strings(&ret->permitlisten, &ret->npermitlisten,
611: primary->permitlisten, primary->npermitlisten) != 0)
612: goto alloc_fail;
613: } else if (additional->npermitlisten > 0) {
614: if (dup_strings(&ret->permitlisten, &ret->npermitlisten,
615: additional->permitlisten, additional->npermitlisten) != 0)
616: goto alloc_fail;
617: }
618:
1.75 djm 619: /* Flags are logical-AND (i.e. must be set in both for permission) */
620: #define OPTFLAG(x) ret->x = (primary->x == 1) && (additional->x == 1)
621: OPTFLAG(permit_port_forwarding_flag);
622: OPTFLAG(permit_agent_forwarding_flag);
623: OPTFLAG(permit_x11_forwarding_flag);
624: OPTFLAG(permit_pty_flag);
625: OPTFLAG(permit_user_rc);
626: #undef OPTFLAG
627:
1.77 djm 628: /* Earliest expiry time should win */
629: if (primary->valid_before != 0)
630: ret->valid_before = primary->valid_before;
631: if (additional->valid_before != 0 &&
632: additional->valid_before < ret->valid_before)
633: ret->valid_before = additional->valid_before;
634:
1.75 djm 635: /*
636: * When both multiple forced-command are specified, only
637: * proceed if they are identical, otherwise fail.
638: */
639: if (primary->force_command != NULL &&
640: additional->force_command != NULL) {
641: if (strcmp(primary->force_command,
642: additional->force_command) == 0) {
643: /* ok */
644: ret->force_command = strdup(primary->force_command);
645: if (ret->force_command == NULL)
646: goto alloc_fail;
647: } else {
648: errstr = "forced command options do not match";
649: goto fail;
650: }
651: } else if (primary->force_command != NULL) {
652: if ((ret->force_command = strdup(
653: primary->force_command)) == NULL)
654: goto alloc_fail;
655: } else if (additional->force_command != NULL) {
656: if ((ret->force_command = strdup(
657: additional->force_command)) == NULL)
658: goto alloc_fail;
659: }
660: /* success */
661: if (errstrp != NULL)
662: *errstrp = NULL;
663: return ret;
664:
665: alloc_fail:
666: errstr = "memory allocation failed";
667: fail:
668: if (errstrp != NULL)
669: *errstrp = errstr;
670: sshauthopt_free(ret);
671: return NULL;
672: }
673:
674: /*
675: * Copy options
676: */
677: struct sshauthopt *
678: sshauthopt_copy(const struct sshauthopt *orig)
679: {
680: struct sshauthopt *ret;
681:
682: if ((ret = sshauthopt_new()) == NULL)
683: return NULL;
684:
685: #define OPTSCALAR(x) ret->x = orig->x
686: OPTSCALAR(permit_port_forwarding_flag);
687: OPTSCALAR(permit_agent_forwarding_flag);
688: OPTSCALAR(permit_x11_forwarding_flag);
689: OPTSCALAR(permit_pty_flag);
690: OPTSCALAR(permit_user_rc);
691: OPTSCALAR(restricted);
692: OPTSCALAR(cert_authority);
693: OPTSCALAR(force_tun_device);
1.77 djm 694: OPTSCALAR(valid_before);
1.75 djm 695: #undef OPTSCALAR
696: #define OPTSTRING(x) \
697: do { \
698: if (orig->x != NULL && (ret->x = strdup(orig->x)) == NULL) { \
699: sshauthopt_free(ret); \
700: return NULL; \
701: } \
702: } while (0)
703: OPTSTRING(cert_principals);
704: OPTSTRING(force_command);
705: OPTSTRING(required_from_host_cert);
706: OPTSTRING(required_from_host_keys);
707: #undef OPTSTRING
708:
709: if (dup_strings(&ret->env, &ret->nenv, orig->env, orig->nenv) != 0 ||
710: dup_strings(&ret->permitopen, &ret->npermitopen,
1.80 djm 711: orig->permitopen, orig->npermitopen) != 0 ||
712: dup_strings(&ret->permitlisten, &ret->npermitlisten,
713: orig->permitlisten, orig->npermitlisten) != 0) {
1.75 djm 714: sshauthopt_free(ret);
715: return NULL;
716: }
717: return ret;
718: }
719:
720: static int
721: serialise_array(struct sshbuf *m, char **a, size_t n)
722: {
723: struct sshbuf *b;
724: size_t i;
725: int r;
726:
727: if (n > INT_MAX)
728: return SSH_ERR_INTERNAL_ERROR;
729:
730: if ((b = sshbuf_new()) == NULL) {
731: return SSH_ERR_ALLOC_FAIL;
732: }
733: for (i = 0; i < n; i++) {
734: if ((r = sshbuf_put_cstring(b, a[i])) != 0) {
735: sshbuf_free(b);
736: return r;
737: }
738: }
739: if ((r = sshbuf_put_u32(m, n)) != 0 ||
740: (r = sshbuf_put_stringb(m, b)) != 0) {
741: sshbuf_free(b);
742: return r;
743: }
744: /* success */
745: return 0;
746: }
747:
748: static int
749: deserialise_array(struct sshbuf *m, char ***ap, size_t *np)
750: {
751: char **a = NULL;
752: size_t i, n = 0;
753: struct sshbuf *b = NULL;
754: u_int tmp;
755: int r = SSH_ERR_INTERNAL_ERROR;
756:
757: if ((r = sshbuf_get_u32(m, &tmp)) != 0 ||
758: (r = sshbuf_froms(m, &b)) != 0)
759: goto out;
760: if (tmp > INT_MAX) {
761: r = SSH_ERR_INVALID_FORMAT;
762: goto out;
763: }
764: n = tmp;
765: if (n > 0 && (a = calloc(n, sizeof(*a))) == NULL) {
766: r = SSH_ERR_ALLOC_FAIL;
767: goto out;
768: }
769: for (i = 0; i < n; i++) {
770: if ((r = sshbuf_get_cstring(b, &a[i], NULL)) != 0)
771: goto out;
772: }
773: /* success */
774: r = 0;
775: *ap = a;
776: a = NULL;
777: *np = n;
778: n = 0;
779: out:
780: for (i = 0; i < n; i++)
781: free(a[i]);
782: free(a);
783: sshbuf_free(b);
784: return r;
785: }
786:
787: static int
788: serialise_nullable_string(struct sshbuf *m, const char *s)
789: {
790: int r;
791:
792: if ((r = sshbuf_put_u8(m, s == NULL)) != 0 ||
793: (r = sshbuf_put_cstring(m, s)) != 0)
794: return r;
795: return 0;
796: }
797:
798: static int
799: deserialise_nullable_string(struct sshbuf *m, char **sp)
800: {
801: int r;
802: u_char flag;
803:
804: *sp = NULL;
805: if ((r = sshbuf_get_u8(m, &flag)) != 0 ||
806: (r = sshbuf_get_cstring(m, flag ? NULL : sp, NULL)) != 0)
807: return r;
808: return 0;
809: }
810:
811: int
812: sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m,
813: int untrusted)
814: {
815: int r = SSH_ERR_INTERNAL_ERROR;
816:
1.77 djm 817: /* Flag and simple integer options */
1.75 djm 818: if ((r = sshbuf_put_u8(m, opts->permit_port_forwarding_flag)) != 0 ||
819: (r = sshbuf_put_u8(m, opts->permit_agent_forwarding_flag)) != 0 ||
820: (r = sshbuf_put_u8(m, opts->permit_x11_forwarding_flag)) != 0 ||
821: (r = sshbuf_put_u8(m, opts->permit_pty_flag)) != 0 ||
822: (r = sshbuf_put_u8(m, opts->permit_user_rc)) != 0 ||
823: (r = sshbuf_put_u8(m, opts->restricted)) != 0 ||
1.77 djm 824: (r = sshbuf_put_u8(m, opts->cert_authority)) != 0 ||
825: (r = sshbuf_put_u64(m, opts->valid_before)) != 0)
1.75 djm 826: return r;
827:
828: /* tunnel number can be negative to indicate "unset" */
829: if ((r = sshbuf_put_u8(m, opts->force_tun_device == -1)) != 0 ||
830: (r = sshbuf_put_u32(m, (opts->force_tun_device < 0) ?
831: 0 : (u_int)opts->force_tun_device)) != 0)
832: return r;
833:
834: /* String options; these may be NULL */
835: if ((r = serialise_nullable_string(m,
836: untrusted ? "yes" : opts->cert_principals)) != 0 ||
837: (r = serialise_nullable_string(m,
838: untrusted ? "true" : opts->force_command)) != 0 ||
839: (r = serialise_nullable_string(m,
840: untrusted ? NULL : opts->required_from_host_cert)) != 0 ||
841: (r = serialise_nullable_string(m,
842: untrusted ? NULL : opts->required_from_host_keys)) != 0)
843: return r;
844:
845: /* Array options */
846: if ((r = serialise_array(m, opts->env,
847: untrusted ? 0 : opts->nenv)) != 0 ||
848: (r = serialise_array(m, opts->permitopen,
1.82 ! djm 849: untrusted ? 0 : opts->npermitopen)) != 0 ||
1.80 djm 850: (r = serialise_array(m, opts->permitlisten,
851: untrusted ? 0 : opts->npermitlisten)) != 0)
1.75 djm 852: return r;
853:
854: /* success */
855: return 0;
856: }
857:
858: int
859: sshauthopt_deserialise(struct sshbuf *m, struct sshauthopt **optsp)
860: {
861: struct sshauthopt *opts = NULL;
862: int r = SSH_ERR_INTERNAL_ERROR;
863: u_char f;
864: u_int tmp;
865:
866: if ((opts = calloc(1, sizeof(*opts))) == NULL)
867: return SSH_ERR_ALLOC_FAIL;
868:
869: #define OPT_FLAG(x) \
870: do { \
871: if ((r = sshbuf_get_u8(m, &f)) != 0) \
872: goto out; \
873: opts->x = f; \
874: } while (0)
875: OPT_FLAG(permit_port_forwarding_flag);
876: OPT_FLAG(permit_agent_forwarding_flag);
877: OPT_FLAG(permit_x11_forwarding_flag);
878: OPT_FLAG(permit_pty_flag);
879: OPT_FLAG(permit_user_rc);
880: OPT_FLAG(restricted);
881: OPT_FLAG(cert_authority);
882: #undef OPT_FLAG
1.77 djm 883:
884: if ((r = sshbuf_get_u64(m, &opts->valid_before)) != 0)
885: goto out;
1.75 djm 886:
887: /* tunnel number can be negative to indicate "unset" */
888: if ((r = sshbuf_get_u8(m, &f)) != 0 ||
889: (r = sshbuf_get_u32(m, &tmp)) != 0)
890: goto out;
891: opts->force_tun_device = f ? -1 : (int)tmp;
892:
893: /* String options may be NULL */
894: if ((r = deserialise_nullable_string(m, &opts->cert_principals)) != 0 ||
895: (r = deserialise_nullable_string(m, &opts->force_command)) != 0 ||
896: (r = deserialise_nullable_string(m,
897: &opts->required_from_host_cert)) != 0 ||
898: (r = deserialise_nullable_string(m,
899: &opts->required_from_host_keys)) != 0)
900: goto out;
901:
902: /* Array options */
903: if ((r = deserialise_array(m, &opts->env, &opts->nenv)) != 0 ||
904: (r = deserialise_array(m,
1.80 djm 905: &opts->permitopen, &opts->npermitopen)) != 0 ||
906: (r = deserialise_array(m,
907: &opts->permitlisten, &opts->npermitlisten)) != 0)
1.75 djm 908: goto out;
909:
910: /* success */
911: r = 0;
912: *optsp = opts;
913: opts = NULL;
914: out:
915: sshauthopt_free(opts);
916: return r;
917: }