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