1*190cef3dSDag-Erling Smørgrav /* $OpenBSD: auth-options.c,v 1.83 2018/06/19 02:59:41 djm Exp $ */ 2b66f2d16SKris Kennaway /* 347dd1d1bSDag-Erling Smørgrav * Copyright (c) 2018 Damien Miller <djm@mindrot.org> 447dd1d1bSDag-Erling Smørgrav * 547dd1d1bSDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 647dd1d1bSDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 747dd1d1bSDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 847dd1d1bSDag-Erling Smørgrav * 947dd1d1bSDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1047dd1d1bSDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1147dd1d1bSDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1247dd1d1bSDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1347dd1d1bSDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1447dd1d1bSDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1547dd1d1bSDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16b66f2d16SKris Kennaway */ 17b66f2d16SKris Kennaway 18b66f2d16SKris Kennaway #include "includes.h" 19761efaa7SDag-Erling Smørgrav 20761efaa7SDag-Erling Smørgrav #include <sys/types.h> 21761efaa7SDag-Erling Smørgrav 22761efaa7SDag-Erling Smørgrav #include <netdb.h> 23761efaa7SDag-Erling Smørgrav #include <pwd.h> 24761efaa7SDag-Erling Smørgrav #include <string.h> 25761efaa7SDag-Erling Smørgrav #include <stdio.h> 26761efaa7SDag-Erling Smørgrav #include <stdarg.h> 2747dd1d1bSDag-Erling Smørgrav #include <ctype.h> 2847dd1d1bSDag-Erling Smørgrav #include <limits.h> 29b66f2d16SKris Kennaway 30d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h" 31bc5531deSDag-Erling Smørgrav 32b66f2d16SKris Kennaway #include "xmalloc.h" 33bc5531deSDag-Erling Smørgrav #include "ssherr.h" 341e8db6e2SBrian Feldman #include "log.h" 35bc5531deSDag-Erling Smørgrav #include "sshbuf.h" 36a0ee8cc6SDag-Erling Smørgrav #include "misc.h" 37bc5531deSDag-Erling Smørgrav #include "sshkey.h" 3847dd1d1bSDag-Erling Smørgrav #include "match.h" 3947dd1d1bSDag-Erling Smørgrav #include "ssh2.h" 40e2f6069cSDag-Erling Smørgrav #include "auth-options.h" 415b9b2fafSBrian Feldman 421e8db6e2SBrian Feldman /* 43acc1a9efSDag-Erling Smørgrav * Match flag 'opt' in *optsp, and if allow_negate is set then also match 44acc1a9efSDag-Erling Smørgrav * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0 45acc1a9efSDag-Erling Smørgrav * if negated option matches. 46acc1a9efSDag-Erling Smørgrav * If the option or negated option matches, then *optsp is updated to 4747dd1d1bSDag-Erling Smørgrav * point to the first character after the option. 48acc1a9efSDag-Erling Smørgrav */ 49acc1a9efSDag-Erling Smørgrav static int 5047dd1d1bSDag-Erling Smørgrav opt_flag(const char *opt, int allow_negate, const char **optsp) 51acc1a9efSDag-Erling Smørgrav { 52acc1a9efSDag-Erling Smørgrav size_t opt_len = strlen(opt); 5347dd1d1bSDag-Erling Smørgrav const char *opts = *optsp; 54acc1a9efSDag-Erling Smørgrav int negate = 0; 55acc1a9efSDag-Erling Smørgrav 56acc1a9efSDag-Erling Smørgrav if (allow_negate && strncasecmp(opts, "no-", 3) == 0) { 57acc1a9efSDag-Erling Smørgrav opts += 3; 58acc1a9efSDag-Erling Smørgrav negate = 1; 59acc1a9efSDag-Erling Smørgrav } 60acc1a9efSDag-Erling Smørgrav if (strncasecmp(opts, opt, opt_len) == 0) { 61acc1a9efSDag-Erling Smørgrav *optsp = opts + opt_len; 62acc1a9efSDag-Erling Smørgrav return negate ? 0 : 1; 63acc1a9efSDag-Erling Smørgrav } 64acc1a9efSDag-Erling Smørgrav return -1; 65acc1a9efSDag-Erling Smørgrav } 66acc1a9efSDag-Erling Smørgrav 6747dd1d1bSDag-Erling Smørgrav static char * 6847dd1d1bSDag-Erling Smørgrav opt_dequote(const char **sp, const char **errstrp) 69b66f2d16SKris Kennaway { 7047dd1d1bSDag-Erling Smørgrav const char *s = *sp; 7147dd1d1bSDag-Erling Smørgrav char *ret; 7247dd1d1bSDag-Erling Smørgrav size_t i; 735b9b2fafSBrian Feldman 7447dd1d1bSDag-Erling Smørgrav *errstrp = NULL; 7547dd1d1bSDag-Erling Smørgrav if (*s != '"') { 7647dd1d1bSDag-Erling Smørgrav *errstrp = "missing start quote"; 7747dd1d1bSDag-Erling Smørgrav return NULL; 7847dd1d1bSDag-Erling Smørgrav } 7947dd1d1bSDag-Erling Smørgrav s++; 8047dd1d1bSDag-Erling Smørgrav if ((ret = malloc(strlen((s)) + 1)) == NULL) { 8147dd1d1bSDag-Erling Smørgrav *errstrp = "memory allocation failed"; 8247dd1d1bSDag-Erling Smørgrav return NULL; 8347dd1d1bSDag-Erling Smørgrav } 8447dd1d1bSDag-Erling Smørgrav for (i = 0; *s != '\0' && *s != '"';) { 8547dd1d1bSDag-Erling Smørgrav if (s[0] == '\\' && s[1] == '"') 8647dd1d1bSDag-Erling Smørgrav s++; 8747dd1d1bSDag-Erling Smørgrav ret[i++] = *s++; 8847dd1d1bSDag-Erling Smørgrav } 8947dd1d1bSDag-Erling Smørgrav if (*s == '\0') { 9047dd1d1bSDag-Erling Smørgrav *errstrp = "missing end quote"; 9147dd1d1bSDag-Erling Smørgrav free(ret); 9247dd1d1bSDag-Erling Smørgrav return NULL; 9347dd1d1bSDag-Erling Smørgrav } 9447dd1d1bSDag-Erling Smørgrav ret[i] = '\0'; 9547dd1d1bSDag-Erling Smørgrav s++; 9647dd1d1bSDag-Erling Smørgrav *sp = s; 9747dd1d1bSDag-Erling Smørgrav return ret; 9847dd1d1bSDag-Erling Smørgrav } 995b9b2fafSBrian Feldman 10047dd1d1bSDag-Erling Smørgrav static int 10147dd1d1bSDag-Erling Smørgrav opt_match(const char **opts, const char *term) 10247dd1d1bSDag-Erling Smørgrav { 10347dd1d1bSDag-Erling Smørgrav if (strncasecmp((*opts), term, strlen(term)) == 0 && 10447dd1d1bSDag-Erling Smørgrav (*opts)[strlen(term)] == '=') { 10547dd1d1bSDag-Erling Smørgrav *opts += strlen(term) + 1; 1061e8db6e2SBrian Feldman return 1; 107b15c8340SDag-Erling Smørgrav } 108b66f2d16SKris Kennaway return 0; 109b66f2d16SKris Kennaway } 1101e8db6e2SBrian Feldman 11147dd1d1bSDag-Erling Smørgrav static int 11247dd1d1bSDag-Erling Smørgrav dup_strings(char ***dstp, size_t *ndstp, char **src, size_t nsrc) 11347dd1d1bSDag-Erling Smørgrav { 11447dd1d1bSDag-Erling Smørgrav char **dst; 11547dd1d1bSDag-Erling Smørgrav size_t i, j; 116545d5ecaSDag-Erling Smørgrav 11747dd1d1bSDag-Erling Smørgrav *dstp = NULL; 11847dd1d1bSDag-Erling Smørgrav *ndstp = 0; 11947dd1d1bSDag-Erling Smørgrav if (nsrc == 0) 12047dd1d1bSDag-Erling Smørgrav return 0; 121b66f2d16SKris Kennaway 12247dd1d1bSDag-Erling Smørgrav if ((dst = calloc(nsrc, sizeof(*src))) == NULL) 12347dd1d1bSDag-Erling Smørgrav return -1; 12447dd1d1bSDag-Erling Smørgrav for (i = 0; i < nsrc; i++) { 12547dd1d1bSDag-Erling Smørgrav if ((dst[i] = strdup(src[i])) == NULL) { 12647dd1d1bSDag-Erling Smørgrav for (j = 0; j < i; j++) 12747dd1d1bSDag-Erling Smørgrav free(dst[j]); 12847dd1d1bSDag-Erling Smørgrav free(dst); 12947dd1d1bSDag-Erling Smørgrav return -1; 13047dd1d1bSDag-Erling Smørgrav } 13147dd1d1bSDag-Erling Smørgrav } 13247dd1d1bSDag-Erling Smørgrav /* success */ 13347dd1d1bSDag-Erling Smørgrav *dstp = dst; 13447dd1d1bSDag-Erling Smørgrav *ndstp = nsrc; 135b66f2d16SKris Kennaway return 0; 136b66f2d16SKris Kennaway } 137b15c8340SDag-Erling Smørgrav 138e2f6069cSDag-Erling Smørgrav #define OPTIONS_CRITICAL 1 139e2f6069cSDag-Erling Smørgrav #define OPTIONS_EXTENSIONS 2 140e2f6069cSDag-Erling Smørgrav static int 14147dd1d1bSDag-Erling Smørgrav cert_option_list(struct sshauthopt *opts, struct sshbuf *oblob, 14247dd1d1bSDag-Erling Smørgrav u_int which, int crit) 143b15c8340SDag-Erling Smørgrav { 144e2f6069cSDag-Erling Smørgrav char *command, *allowed; 145e4a9863fSDag-Erling Smørgrav char *name = NULL; 146bc5531deSDag-Erling Smørgrav struct sshbuf *c = NULL, *data = NULL; 14747dd1d1bSDag-Erling Smørgrav int r, ret = -1, found; 148b15c8340SDag-Erling Smørgrav 149bc5531deSDag-Erling Smørgrav if ((c = sshbuf_fromb(oblob)) == NULL) { 150bc5531deSDag-Erling Smørgrav error("%s: sshbuf_fromb failed", __func__); 151b15c8340SDag-Erling Smørgrav goto out; 152b15c8340SDag-Erling Smørgrav } 153bc5531deSDag-Erling Smørgrav 154bc5531deSDag-Erling Smørgrav while (sshbuf_len(c) > 0) { 155bc5531deSDag-Erling Smørgrav sshbuf_free(data); 156bc5531deSDag-Erling Smørgrav data = NULL; 157bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 || 158bc5531deSDag-Erling Smørgrav (r = sshbuf_froms(c, &data)) != 0) { 159bc5531deSDag-Erling Smørgrav error("Unable to parse certificate options: %s", 160bc5531deSDag-Erling Smørgrav ssh_err(r)); 161bc5531deSDag-Erling Smørgrav goto out; 162bc5531deSDag-Erling Smørgrav } 163bc5531deSDag-Erling Smørgrav debug3("found certificate option \"%.100s\" len %zu", 164bc5531deSDag-Erling Smørgrav name, sshbuf_len(data)); 165e2f6069cSDag-Erling Smørgrav found = 0; 166e2f6069cSDag-Erling Smørgrav if ((which & OPTIONS_EXTENSIONS) != 0) { 167e2f6069cSDag-Erling Smørgrav if (strcmp(name, "permit-X11-forwarding") == 0) { 16847dd1d1bSDag-Erling Smørgrav opts->permit_x11_forwarding_flag = 1; 169e2f6069cSDag-Erling Smørgrav found = 1; 170e2f6069cSDag-Erling Smørgrav } else if (strcmp(name, 171e2f6069cSDag-Erling Smørgrav "permit-agent-forwarding") == 0) { 17247dd1d1bSDag-Erling Smørgrav opts->permit_agent_forwarding_flag = 1; 173e2f6069cSDag-Erling Smørgrav found = 1; 174e2f6069cSDag-Erling Smørgrav } else if (strcmp(name, 175e2f6069cSDag-Erling Smørgrav "permit-port-forwarding") == 0) { 17647dd1d1bSDag-Erling Smørgrav opts->permit_port_forwarding_flag = 1; 177e2f6069cSDag-Erling Smørgrav found = 1; 178e2f6069cSDag-Erling Smørgrav } else if (strcmp(name, "permit-pty") == 0) { 17947dd1d1bSDag-Erling Smørgrav opts->permit_pty_flag = 1; 180e2f6069cSDag-Erling Smørgrav found = 1; 181e2f6069cSDag-Erling Smørgrav } else if (strcmp(name, "permit-user-rc") == 0) { 18247dd1d1bSDag-Erling Smørgrav opts->permit_user_rc = 1; 183e2f6069cSDag-Erling Smørgrav found = 1; 184e2f6069cSDag-Erling Smørgrav } 185e2f6069cSDag-Erling Smørgrav } 186e2f6069cSDag-Erling Smørgrav if (!found && (which & OPTIONS_CRITICAL) != 0) { 187e2f6069cSDag-Erling Smørgrav if (strcmp(name, "force-command") == 0) { 188bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(data, &command, 189bc5531deSDag-Erling Smørgrav NULL)) != 0) { 190bc5531deSDag-Erling Smørgrav error("Unable to parse \"%s\" " 191bc5531deSDag-Erling Smørgrav "section: %s", name, ssh_err(r)); 192b15c8340SDag-Erling Smørgrav goto out; 193b15c8340SDag-Erling Smørgrav } 19447dd1d1bSDag-Erling Smørgrav if (opts->force_command != NULL) { 195b15c8340SDag-Erling Smørgrav error("Certificate has multiple " 196e2f6069cSDag-Erling Smørgrav "force-command options"); 197e4a9863fSDag-Erling Smørgrav free(command); 198b15c8340SDag-Erling Smørgrav goto out; 199b15c8340SDag-Erling Smørgrav } 20047dd1d1bSDag-Erling Smørgrav opts->force_command = command; 201e2f6069cSDag-Erling Smørgrav found = 1; 202e2f6069cSDag-Erling Smørgrav } 203e2f6069cSDag-Erling Smørgrav if (strcmp(name, "source-address") == 0) { 204bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(data, &allowed, 205bc5531deSDag-Erling Smørgrav NULL)) != 0) { 206bc5531deSDag-Erling Smørgrav error("Unable to parse \"%s\" " 207bc5531deSDag-Erling Smørgrav "section: %s", name, ssh_err(r)); 208b15c8340SDag-Erling Smørgrav goto out; 209b15c8340SDag-Erling Smørgrav } 21047dd1d1bSDag-Erling Smørgrav if (opts->required_from_host_cert != NULL) { 211b15c8340SDag-Erling Smørgrav error("Certificate has multiple " 212e2f6069cSDag-Erling Smørgrav "source-address options"); 213e4a9863fSDag-Erling Smørgrav free(allowed); 214b15c8340SDag-Erling Smørgrav goto out; 215b15c8340SDag-Erling Smørgrav } 21647dd1d1bSDag-Erling Smørgrav /* Check syntax */ 21747dd1d1bSDag-Erling Smørgrav if (addr_match_cidr_list(NULL, allowed) == -1) { 218e2f6069cSDag-Erling Smørgrav error("Certificate source-address " 219e2f6069cSDag-Erling Smørgrav "contents invalid"); 220b15c8340SDag-Erling Smørgrav goto out; 221b15c8340SDag-Erling Smørgrav } 22247dd1d1bSDag-Erling Smørgrav opts->required_from_host_cert = allowed; 223e2f6069cSDag-Erling Smørgrav found = 1; 224e2f6069cSDag-Erling Smørgrav } 225b15c8340SDag-Erling Smørgrav } 226b15c8340SDag-Erling Smørgrav 227e2f6069cSDag-Erling Smørgrav if (!found) { 228e2f6069cSDag-Erling Smørgrav if (crit) { 229e2f6069cSDag-Erling Smørgrav error("Certificate critical option \"%s\" " 230e2f6069cSDag-Erling Smørgrav "is not supported", name); 231e2f6069cSDag-Erling Smørgrav goto out; 232e2f6069cSDag-Erling Smørgrav } else { 233e2f6069cSDag-Erling Smørgrav logit("Certificate extension \"%s\" " 234e2f6069cSDag-Erling Smørgrav "is not supported", name); 235e2f6069cSDag-Erling Smørgrav } 236bc5531deSDag-Erling Smørgrav } else if (sshbuf_len(data) != 0) { 237e2f6069cSDag-Erling Smørgrav error("Certificate option \"%s\" corrupt " 238b15c8340SDag-Erling Smørgrav "(extra data)", name); 239b15c8340SDag-Erling Smørgrav goto out; 240b15c8340SDag-Erling Smørgrav } 241e4a9863fSDag-Erling Smørgrav free(name); 242e4a9863fSDag-Erling Smørgrav name = NULL; 243b15c8340SDag-Erling Smørgrav } 244e2f6069cSDag-Erling Smørgrav /* successfully parsed all options */ 245b15c8340SDag-Erling Smørgrav ret = 0; 246b15c8340SDag-Erling Smørgrav 247e2f6069cSDag-Erling Smørgrav out: 248e4a9863fSDag-Erling Smørgrav free(name); 249bc5531deSDag-Erling Smørgrav sshbuf_free(data); 250bc5531deSDag-Erling Smørgrav sshbuf_free(c); 251e2f6069cSDag-Erling Smørgrav return ret; 252e2f6069cSDag-Erling Smørgrav } 253e2f6069cSDag-Erling Smørgrav 25447dd1d1bSDag-Erling Smørgrav struct sshauthopt * 25547dd1d1bSDag-Erling Smørgrav sshauthopt_new(void) 256e2f6069cSDag-Erling Smørgrav { 25747dd1d1bSDag-Erling Smørgrav struct sshauthopt *ret; 258e2f6069cSDag-Erling Smørgrav 25947dd1d1bSDag-Erling Smørgrav if ((ret = calloc(1, sizeof(*ret))) == NULL) 26047dd1d1bSDag-Erling Smørgrav return NULL; 26147dd1d1bSDag-Erling Smørgrav ret->force_tun_device = -1; 26247dd1d1bSDag-Erling Smørgrav return ret; 263b15c8340SDag-Erling Smørgrav } 26447dd1d1bSDag-Erling Smørgrav 26547dd1d1bSDag-Erling Smørgrav void 26647dd1d1bSDag-Erling Smørgrav sshauthopt_free(struct sshauthopt *opts) 26747dd1d1bSDag-Erling Smørgrav { 26847dd1d1bSDag-Erling Smørgrav size_t i; 26947dd1d1bSDag-Erling Smørgrav 27047dd1d1bSDag-Erling Smørgrav if (opts == NULL) 27147dd1d1bSDag-Erling Smørgrav return; 27247dd1d1bSDag-Erling Smørgrav 27347dd1d1bSDag-Erling Smørgrav free(opts->cert_principals); 27447dd1d1bSDag-Erling Smørgrav free(opts->force_command); 27547dd1d1bSDag-Erling Smørgrav free(opts->required_from_host_cert); 27647dd1d1bSDag-Erling Smørgrav free(opts->required_from_host_keys); 27747dd1d1bSDag-Erling Smørgrav 27847dd1d1bSDag-Erling Smørgrav for (i = 0; i < opts->nenv; i++) 27947dd1d1bSDag-Erling Smørgrav free(opts->env[i]); 28047dd1d1bSDag-Erling Smørgrav free(opts->env); 28147dd1d1bSDag-Erling Smørgrav 28247dd1d1bSDag-Erling Smørgrav for (i = 0; i < opts->npermitopen; i++) 28347dd1d1bSDag-Erling Smørgrav free(opts->permitopen[i]); 28447dd1d1bSDag-Erling Smørgrav free(opts->permitopen); 28547dd1d1bSDag-Erling Smørgrav 286*190cef3dSDag-Erling Smørgrav for (i = 0; i < opts->npermitlisten; i++) 287*190cef3dSDag-Erling Smørgrav free(opts->permitlisten[i]); 288*190cef3dSDag-Erling Smørgrav free(opts->permitlisten); 289*190cef3dSDag-Erling Smørgrav 29047dd1d1bSDag-Erling Smørgrav explicit_bzero(opts, sizeof(*opts)); 29147dd1d1bSDag-Erling Smørgrav free(opts); 29247dd1d1bSDag-Erling Smørgrav } 29347dd1d1bSDag-Erling Smørgrav 29447dd1d1bSDag-Erling Smørgrav struct sshauthopt * 29547dd1d1bSDag-Erling Smørgrav sshauthopt_new_with_keys_defaults(void) 29647dd1d1bSDag-Erling Smørgrav { 29747dd1d1bSDag-Erling Smørgrav struct sshauthopt *ret = NULL; 29847dd1d1bSDag-Erling Smørgrav 29947dd1d1bSDag-Erling Smørgrav if ((ret = sshauthopt_new()) == NULL) 30047dd1d1bSDag-Erling Smørgrav return NULL; 30147dd1d1bSDag-Erling Smørgrav 30247dd1d1bSDag-Erling Smørgrav /* Defaults for authorized_keys flags */ 30347dd1d1bSDag-Erling Smørgrav ret->permit_port_forwarding_flag = 1; 30447dd1d1bSDag-Erling Smørgrav ret->permit_agent_forwarding_flag = 1; 30547dd1d1bSDag-Erling Smørgrav ret->permit_x11_forwarding_flag = 1; 30647dd1d1bSDag-Erling Smørgrav ret->permit_pty_flag = 1; 30747dd1d1bSDag-Erling Smørgrav ret->permit_user_rc = 1; 30847dd1d1bSDag-Erling Smørgrav return ret; 30947dd1d1bSDag-Erling Smørgrav } 31047dd1d1bSDag-Erling Smørgrav 311*190cef3dSDag-Erling Smørgrav /* 312*190cef3dSDag-Erling Smørgrav * Parse and record a permitopen/permitlisten directive. 313*190cef3dSDag-Erling Smørgrav * Return 0 on success. Return -1 on failure and sets *errstrp to error reason. 314*190cef3dSDag-Erling Smørgrav */ 315*190cef3dSDag-Erling Smørgrav static int 316*190cef3dSDag-Erling Smørgrav handle_permit(const char **optsp, int allow_bare_port, 317*190cef3dSDag-Erling Smørgrav char ***permitsp, size_t *npermitsp, const char **errstrp) 318*190cef3dSDag-Erling Smørgrav { 319*190cef3dSDag-Erling Smørgrav char *opt, *tmp, *cp, *host, **permits = *permitsp; 320*190cef3dSDag-Erling Smørgrav size_t npermits = *npermitsp; 321*190cef3dSDag-Erling Smørgrav const char *errstr = "unknown error"; 322*190cef3dSDag-Erling Smørgrav 323*190cef3dSDag-Erling Smørgrav if (npermits > INT_MAX) { 324*190cef3dSDag-Erling Smørgrav *errstrp = "too many permission directives"; 325*190cef3dSDag-Erling Smørgrav return -1; 326*190cef3dSDag-Erling Smørgrav } 327*190cef3dSDag-Erling Smørgrav if ((opt = opt_dequote(optsp, &errstr)) == NULL) { 328*190cef3dSDag-Erling Smørgrav return -1; 329*190cef3dSDag-Erling Smørgrav } 330*190cef3dSDag-Erling Smørgrav if (allow_bare_port && strchr(opt, ':') == NULL) { 331*190cef3dSDag-Erling Smørgrav /* 332*190cef3dSDag-Erling Smørgrav * Allow a bare port number in permitlisten to indicate a 333*190cef3dSDag-Erling Smørgrav * listen_host wildcard. 334*190cef3dSDag-Erling Smørgrav */ 335*190cef3dSDag-Erling Smørgrav if (asprintf(&tmp, "*:%s", opt) < 0) { 336*190cef3dSDag-Erling Smørgrav *errstrp = "memory allocation failed"; 337*190cef3dSDag-Erling Smørgrav return -1; 338*190cef3dSDag-Erling Smørgrav } 339*190cef3dSDag-Erling Smørgrav free(opt); 340*190cef3dSDag-Erling Smørgrav opt = tmp; 341*190cef3dSDag-Erling Smørgrav } 342*190cef3dSDag-Erling Smørgrav if ((tmp = strdup(opt)) == NULL) { 343*190cef3dSDag-Erling Smørgrav free(opt); 344*190cef3dSDag-Erling Smørgrav *errstrp = "memory allocation failed"; 345*190cef3dSDag-Erling Smørgrav return -1; 346*190cef3dSDag-Erling Smørgrav } 347*190cef3dSDag-Erling Smørgrav cp = tmp; 348*190cef3dSDag-Erling Smørgrav /* validate syntax before recording it. */ 349*190cef3dSDag-Erling Smørgrav host = hpdelim(&cp); 350*190cef3dSDag-Erling Smørgrav if (host == NULL || strlen(host) >= NI_MAXHOST) { 351*190cef3dSDag-Erling Smørgrav free(tmp); 352*190cef3dSDag-Erling Smørgrav free(opt); 353*190cef3dSDag-Erling Smørgrav *errstrp = "invalid permission hostname"; 354*190cef3dSDag-Erling Smørgrav return -1; 355*190cef3dSDag-Erling Smørgrav } 356*190cef3dSDag-Erling Smørgrav /* 357*190cef3dSDag-Erling Smørgrav * don't want to use permitopen_port to avoid 358*190cef3dSDag-Erling Smørgrav * dependency on channels.[ch] here. 359*190cef3dSDag-Erling Smørgrav */ 360*190cef3dSDag-Erling Smørgrav if (cp == NULL || 361*190cef3dSDag-Erling Smørgrav (strcmp(cp, "*") != 0 && a2port(cp) <= 0)) { 362*190cef3dSDag-Erling Smørgrav free(tmp); 363*190cef3dSDag-Erling Smørgrav free(opt); 364*190cef3dSDag-Erling Smørgrav *errstrp = "invalid permission port"; 365*190cef3dSDag-Erling Smørgrav return -1; 366*190cef3dSDag-Erling Smørgrav } 367*190cef3dSDag-Erling Smørgrav /* XXX - add streamlocal support */ 368*190cef3dSDag-Erling Smørgrav free(tmp); 369*190cef3dSDag-Erling Smørgrav /* Record it */ 370*190cef3dSDag-Erling Smørgrav if ((permits = recallocarray(permits, npermits, npermits + 1, 371*190cef3dSDag-Erling Smørgrav sizeof(*permits))) == NULL) { 372*190cef3dSDag-Erling Smørgrav free(opt); 373*190cef3dSDag-Erling Smørgrav /* NB. don't update *permitsp if alloc fails */ 374*190cef3dSDag-Erling Smørgrav *errstrp = "memory allocation failed"; 375*190cef3dSDag-Erling Smørgrav return -1; 376*190cef3dSDag-Erling Smørgrav } 377*190cef3dSDag-Erling Smørgrav permits[npermits++] = opt; 378*190cef3dSDag-Erling Smørgrav *permitsp = permits; 379*190cef3dSDag-Erling Smørgrav *npermitsp = npermits; 380*190cef3dSDag-Erling Smørgrav return 0; 381*190cef3dSDag-Erling Smørgrav } 382*190cef3dSDag-Erling Smørgrav 38347dd1d1bSDag-Erling Smørgrav struct sshauthopt * 38447dd1d1bSDag-Erling Smørgrav sshauthopt_parse(const char *opts, const char **errstrp) 38547dd1d1bSDag-Erling Smørgrav { 386*190cef3dSDag-Erling Smørgrav char **oarray, *opt, *cp, *tmp; 38747dd1d1bSDag-Erling Smørgrav int r; 38847dd1d1bSDag-Erling Smørgrav struct sshauthopt *ret = NULL; 38947dd1d1bSDag-Erling Smørgrav const char *errstr = "unknown error"; 39047dd1d1bSDag-Erling Smørgrav uint64_t valid_before; 39147dd1d1bSDag-Erling Smørgrav 39247dd1d1bSDag-Erling Smørgrav if (errstrp != NULL) 39347dd1d1bSDag-Erling Smørgrav *errstrp = NULL; 39447dd1d1bSDag-Erling Smørgrav if ((ret = sshauthopt_new_with_keys_defaults()) == NULL) 39547dd1d1bSDag-Erling Smørgrav goto alloc_fail; 39647dd1d1bSDag-Erling Smørgrav 39747dd1d1bSDag-Erling Smørgrav if (opts == NULL) 39847dd1d1bSDag-Erling Smørgrav return ret; 39947dd1d1bSDag-Erling Smørgrav 40047dd1d1bSDag-Erling Smørgrav while (*opts && *opts != ' ' && *opts != '\t') { 40147dd1d1bSDag-Erling Smørgrav /* flag options */ 40247dd1d1bSDag-Erling Smørgrav if ((r = opt_flag("restrict", 0, &opts)) != -1) { 40347dd1d1bSDag-Erling Smørgrav ret->restricted = 1; 40447dd1d1bSDag-Erling Smørgrav ret->permit_port_forwarding_flag = 0; 40547dd1d1bSDag-Erling Smørgrav ret->permit_agent_forwarding_flag = 0; 40647dd1d1bSDag-Erling Smørgrav ret->permit_x11_forwarding_flag = 0; 40747dd1d1bSDag-Erling Smørgrav ret->permit_pty_flag = 0; 40847dd1d1bSDag-Erling Smørgrav ret->permit_user_rc = 0; 40947dd1d1bSDag-Erling Smørgrav } else if ((r = opt_flag("cert-authority", 0, &opts)) != -1) { 41047dd1d1bSDag-Erling Smørgrav ret->cert_authority = r; 41147dd1d1bSDag-Erling Smørgrav } else if ((r = opt_flag("port-forwarding", 1, &opts)) != -1) { 41247dd1d1bSDag-Erling Smørgrav ret->permit_port_forwarding_flag = r == 1; 41347dd1d1bSDag-Erling Smørgrav } else if ((r = opt_flag("agent-forwarding", 1, &opts)) != -1) { 41447dd1d1bSDag-Erling Smørgrav ret->permit_agent_forwarding_flag = r == 1; 41547dd1d1bSDag-Erling Smørgrav } else if ((r = opt_flag("x11-forwarding", 1, &opts)) != -1) { 41647dd1d1bSDag-Erling Smørgrav ret->permit_x11_forwarding_flag = r == 1; 41747dd1d1bSDag-Erling Smørgrav } else if ((r = opt_flag("pty", 1, &opts)) != -1) { 41847dd1d1bSDag-Erling Smørgrav ret->permit_pty_flag = r == 1; 41947dd1d1bSDag-Erling Smørgrav } else if ((r = opt_flag("user-rc", 1, &opts)) != -1) { 42047dd1d1bSDag-Erling Smørgrav ret->permit_user_rc = r == 1; 42147dd1d1bSDag-Erling Smørgrav } else if (opt_match(&opts, "command")) { 42247dd1d1bSDag-Erling Smørgrav if (ret->force_command != NULL) { 42347dd1d1bSDag-Erling Smørgrav errstr = "multiple \"command\" clauses"; 42447dd1d1bSDag-Erling Smørgrav goto fail; 42547dd1d1bSDag-Erling Smørgrav } 42647dd1d1bSDag-Erling Smørgrav ret->force_command = opt_dequote(&opts, &errstr); 42747dd1d1bSDag-Erling Smørgrav if (ret->force_command == NULL) 42847dd1d1bSDag-Erling Smørgrav goto fail; 42947dd1d1bSDag-Erling Smørgrav } else if (opt_match(&opts, "principals")) { 43047dd1d1bSDag-Erling Smørgrav if (ret->cert_principals != NULL) { 43147dd1d1bSDag-Erling Smørgrav errstr = "multiple \"principals\" clauses"; 43247dd1d1bSDag-Erling Smørgrav goto fail; 43347dd1d1bSDag-Erling Smørgrav } 43447dd1d1bSDag-Erling Smørgrav ret->cert_principals = opt_dequote(&opts, &errstr); 43547dd1d1bSDag-Erling Smørgrav if (ret->cert_principals == NULL) 43647dd1d1bSDag-Erling Smørgrav goto fail; 43747dd1d1bSDag-Erling Smørgrav } else if (opt_match(&opts, "from")) { 43847dd1d1bSDag-Erling Smørgrav if (ret->required_from_host_keys != NULL) { 43947dd1d1bSDag-Erling Smørgrav errstr = "multiple \"from\" clauses"; 44047dd1d1bSDag-Erling Smørgrav goto fail; 44147dd1d1bSDag-Erling Smørgrav } 44247dd1d1bSDag-Erling Smørgrav ret->required_from_host_keys = opt_dequote(&opts, 44347dd1d1bSDag-Erling Smørgrav &errstr); 44447dd1d1bSDag-Erling Smørgrav if (ret->required_from_host_keys == NULL) 44547dd1d1bSDag-Erling Smørgrav goto fail; 44647dd1d1bSDag-Erling Smørgrav } else if (opt_match(&opts, "expiry-time")) { 44747dd1d1bSDag-Erling Smørgrav if ((opt = opt_dequote(&opts, &errstr)) == NULL) 44847dd1d1bSDag-Erling Smørgrav goto fail; 44947dd1d1bSDag-Erling Smørgrav if (parse_absolute_time(opt, &valid_before) != 0 || 45047dd1d1bSDag-Erling Smørgrav valid_before == 0) { 45147dd1d1bSDag-Erling Smørgrav free(opt); 45247dd1d1bSDag-Erling Smørgrav errstr = "invalid expires time"; 45347dd1d1bSDag-Erling Smørgrav goto fail; 45447dd1d1bSDag-Erling Smørgrav } 45547dd1d1bSDag-Erling Smørgrav free(opt); 45647dd1d1bSDag-Erling Smørgrav if (ret->valid_before == 0 || 45747dd1d1bSDag-Erling Smørgrav valid_before < ret->valid_before) 45847dd1d1bSDag-Erling Smørgrav ret->valid_before = valid_before; 45947dd1d1bSDag-Erling Smørgrav } else if (opt_match(&opts, "environment")) { 46047dd1d1bSDag-Erling Smørgrav if (ret->nenv > INT_MAX) { 46147dd1d1bSDag-Erling Smørgrav errstr = "too many environment strings"; 46247dd1d1bSDag-Erling Smørgrav goto fail; 46347dd1d1bSDag-Erling Smørgrav } 46447dd1d1bSDag-Erling Smørgrav if ((opt = opt_dequote(&opts, &errstr)) == NULL) 46547dd1d1bSDag-Erling Smørgrav goto fail; 46647dd1d1bSDag-Erling Smørgrav /* env name must be alphanumeric and followed by '=' */ 46747dd1d1bSDag-Erling Smørgrav if ((tmp = strchr(opt, '=')) == NULL) { 46847dd1d1bSDag-Erling Smørgrav free(opt); 46947dd1d1bSDag-Erling Smørgrav errstr = "invalid environment string"; 47047dd1d1bSDag-Erling Smørgrav goto fail; 47147dd1d1bSDag-Erling Smørgrav } 47247dd1d1bSDag-Erling Smørgrav for (cp = opt; cp < tmp; cp++) { 473*190cef3dSDag-Erling Smørgrav if (!isalnum((u_char)*cp) && *cp != '_') { 47447dd1d1bSDag-Erling Smørgrav free(opt); 47547dd1d1bSDag-Erling Smørgrav errstr = "invalid environment string"; 47647dd1d1bSDag-Erling Smørgrav goto fail; 47747dd1d1bSDag-Erling Smørgrav } 47847dd1d1bSDag-Erling Smørgrav } 47947dd1d1bSDag-Erling Smørgrav /* Append it. */ 48047dd1d1bSDag-Erling Smørgrav oarray = ret->env; 48147dd1d1bSDag-Erling Smørgrav if ((ret->env = recallocarray(ret->env, ret->nenv, 48247dd1d1bSDag-Erling Smørgrav ret->nenv + 1, sizeof(*ret->env))) == NULL) { 48347dd1d1bSDag-Erling Smørgrav free(opt); 48447dd1d1bSDag-Erling Smørgrav ret->env = oarray; /* put it back for cleanup */ 48547dd1d1bSDag-Erling Smørgrav goto alloc_fail; 48647dd1d1bSDag-Erling Smørgrav } 48747dd1d1bSDag-Erling Smørgrav ret->env[ret->nenv++] = opt; 48847dd1d1bSDag-Erling Smørgrav } else if (opt_match(&opts, "permitopen")) { 489*190cef3dSDag-Erling Smørgrav if (handle_permit(&opts, 0, &ret->permitopen, 490*190cef3dSDag-Erling Smørgrav &ret->npermitopen, &errstr) != 0) 49147dd1d1bSDag-Erling Smørgrav goto fail; 492*190cef3dSDag-Erling Smørgrav } else if (opt_match(&opts, "permitlisten")) { 493*190cef3dSDag-Erling Smørgrav if (handle_permit(&opts, 1, &ret->permitlisten, 494*190cef3dSDag-Erling Smørgrav &ret->npermitlisten, &errstr) != 0) 49547dd1d1bSDag-Erling Smørgrav goto fail; 49647dd1d1bSDag-Erling Smørgrav } else if (opt_match(&opts, "tunnel")) { 49747dd1d1bSDag-Erling Smørgrav if ((opt = opt_dequote(&opts, &errstr)) == NULL) 49847dd1d1bSDag-Erling Smørgrav goto fail; 49947dd1d1bSDag-Erling Smørgrav ret->force_tun_device = a2tun(opt, NULL); 50047dd1d1bSDag-Erling Smørgrav free(opt); 50147dd1d1bSDag-Erling Smørgrav if (ret->force_tun_device == SSH_TUNID_ERR) { 50247dd1d1bSDag-Erling Smørgrav errstr = "invalid tun device"; 50347dd1d1bSDag-Erling Smørgrav goto fail; 50447dd1d1bSDag-Erling Smørgrav } 50547dd1d1bSDag-Erling Smørgrav } 50647dd1d1bSDag-Erling Smørgrav /* 50747dd1d1bSDag-Erling Smørgrav * Skip the comma, and move to the next option 50847dd1d1bSDag-Erling Smørgrav * (or break out if there are no more). 50947dd1d1bSDag-Erling Smørgrav */ 51047dd1d1bSDag-Erling Smørgrav if (*opts == '\0' || *opts == ' ' || *opts == '\t') 51147dd1d1bSDag-Erling Smørgrav break; /* End of options. */ 51247dd1d1bSDag-Erling Smørgrav /* Anything other than a comma is an unknown option */ 51347dd1d1bSDag-Erling Smørgrav if (*opts != ',') { 51447dd1d1bSDag-Erling Smørgrav errstr = "unknown key option"; 51547dd1d1bSDag-Erling Smørgrav goto fail; 51647dd1d1bSDag-Erling Smørgrav } 51747dd1d1bSDag-Erling Smørgrav opts++; 51847dd1d1bSDag-Erling Smørgrav if (*opts == '\0') { 51947dd1d1bSDag-Erling Smørgrav errstr = "unexpected end-of-options"; 52047dd1d1bSDag-Erling Smørgrav goto fail; 52147dd1d1bSDag-Erling Smørgrav } 52247dd1d1bSDag-Erling Smørgrav } 52347dd1d1bSDag-Erling Smørgrav 524ca86bcf2SDag-Erling Smørgrav /* success */ 52547dd1d1bSDag-Erling Smørgrav if (errstrp != NULL) 52647dd1d1bSDag-Erling Smørgrav *errstrp = NULL; 52747dd1d1bSDag-Erling Smørgrav return ret; 52847dd1d1bSDag-Erling Smørgrav 52947dd1d1bSDag-Erling Smørgrav alloc_fail: 53047dd1d1bSDag-Erling Smørgrav errstr = "memory allocation failed"; 53147dd1d1bSDag-Erling Smørgrav fail: 53247dd1d1bSDag-Erling Smørgrav sshauthopt_free(ret); 53347dd1d1bSDag-Erling Smørgrav if (errstrp != NULL) 53447dd1d1bSDag-Erling Smørgrav *errstrp = errstr; 53547dd1d1bSDag-Erling Smørgrav return NULL; 53647dd1d1bSDag-Erling Smørgrav } 53747dd1d1bSDag-Erling Smørgrav 53847dd1d1bSDag-Erling Smørgrav struct sshauthopt * 53947dd1d1bSDag-Erling Smørgrav sshauthopt_from_cert(struct sshkey *k) 54047dd1d1bSDag-Erling Smørgrav { 54147dd1d1bSDag-Erling Smørgrav struct sshauthopt *ret; 54247dd1d1bSDag-Erling Smørgrav 54347dd1d1bSDag-Erling Smørgrav if (k == NULL || !sshkey_type_is_cert(k->type) || k->cert == NULL || 54447dd1d1bSDag-Erling Smørgrav k->cert->type != SSH2_CERT_TYPE_USER) 54547dd1d1bSDag-Erling Smørgrav return NULL; 54647dd1d1bSDag-Erling Smørgrav 54747dd1d1bSDag-Erling Smørgrav if ((ret = sshauthopt_new()) == NULL) 54847dd1d1bSDag-Erling Smørgrav return NULL; 54947dd1d1bSDag-Erling Smørgrav 55047dd1d1bSDag-Erling Smørgrav /* Handle options and critical extensions separately */ 55147dd1d1bSDag-Erling Smørgrav if (cert_option_list(ret, k->cert->critical, 55247dd1d1bSDag-Erling Smørgrav OPTIONS_CRITICAL, 1) == -1) { 55347dd1d1bSDag-Erling Smørgrav sshauthopt_free(ret); 55447dd1d1bSDag-Erling Smørgrav return NULL; 55547dd1d1bSDag-Erling Smørgrav } 55647dd1d1bSDag-Erling Smørgrav if (cert_option_list(ret, k->cert->extensions, 55747dd1d1bSDag-Erling Smørgrav OPTIONS_EXTENSIONS, 0) == -1) { 55847dd1d1bSDag-Erling Smørgrav sshauthopt_free(ret); 55947dd1d1bSDag-Erling Smørgrav return NULL; 56047dd1d1bSDag-Erling Smørgrav } 56147dd1d1bSDag-Erling Smørgrav /* success */ 56247dd1d1bSDag-Erling Smørgrav return ret; 56347dd1d1bSDag-Erling Smørgrav } 56447dd1d1bSDag-Erling Smørgrav 56547dd1d1bSDag-Erling Smørgrav /* 56647dd1d1bSDag-Erling Smørgrav * Merges "additional" options to "primary" and returns the result. 56747dd1d1bSDag-Erling Smørgrav * NB. Some options from primary have primacy. 56847dd1d1bSDag-Erling Smørgrav */ 56947dd1d1bSDag-Erling Smørgrav struct sshauthopt * 57047dd1d1bSDag-Erling Smørgrav sshauthopt_merge(const struct sshauthopt *primary, 57147dd1d1bSDag-Erling Smørgrav const struct sshauthopt *additional, const char **errstrp) 57247dd1d1bSDag-Erling Smørgrav { 57347dd1d1bSDag-Erling Smørgrav struct sshauthopt *ret; 57447dd1d1bSDag-Erling Smørgrav const char *errstr = "internal error"; 57547dd1d1bSDag-Erling Smørgrav const char *tmp; 57647dd1d1bSDag-Erling Smørgrav 57747dd1d1bSDag-Erling Smørgrav if (errstrp != NULL) 57847dd1d1bSDag-Erling Smørgrav *errstrp = NULL; 57947dd1d1bSDag-Erling Smørgrav 58047dd1d1bSDag-Erling Smørgrav if ((ret = sshauthopt_new()) == NULL) 58147dd1d1bSDag-Erling Smørgrav goto alloc_fail; 58247dd1d1bSDag-Erling Smørgrav 58347dd1d1bSDag-Erling Smørgrav /* cert_authority and cert_principals are cleared in result */ 58447dd1d1bSDag-Erling Smørgrav 58547dd1d1bSDag-Erling Smørgrav /* Prefer access lists from primary. */ 58647dd1d1bSDag-Erling Smørgrav /* XXX err is both set and mismatch? */ 58747dd1d1bSDag-Erling Smørgrav tmp = primary->required_from_host_cert; 58847dd1d1bSDag-Erling Smørgrav if (tmp == NULL) 58947dd1d1bSDag-Erling Smørgrav tmp = additional->required_from_host_cert; 59047dd1d1bSDag-Erling Smørgrav if (tmp != NULL && (ret->required_from_host_cert = strdup(tmp)) == NULL) 59147dd1d1bSDag-Erling Smørgrav goto alloc_fail; 59247dd1d1bSDag-Erling Smørgrav tmp = primary->required_from_host_keys; 59347dd1d1bSDag-Erling Smørgrav if (tmp == NULL) 59447dd1d1bSDag-Erling Smørgrav tmp = additional->required_from_host_keys; 59547dd1d1bSDag-Erling Smørgrav if (tmp != NULL && (ret->required_from_host_keys = strdup(tmp)) == NULL) 59647dd1d1bSDag-Erling Smørgrav goto alloc_fail; 59747dd1d1bSDag-Erling Smørgrav 598*190cef3dSDag-Erling Smørgrav /* 599*190cef3dSDag-Erling Smørgrav * force_tun_device, permitopen/permitlisten and environment all 600*190cef3dSDag-Erling Smørgrav * prefer the primary. 601*190cef3dSDag-Erling Smørgrav */ 60247dd1d1bSDag-Erling Smørgrav ret->force_tun_device = primary->force_tun_device; 60347dd1d1bSDag-Erling Smørgrav if (ret->force_tun_device == -1) 60447dd1d1bSDag-Erling Smørgrav ret->force_tun_device = additional->force_tun_device; 60547dd1d1bSDag-Erling Smørgrav if (primary->nenv > 0) { 60647dd1d1bSDag-Erling Smørgrav if (dup_strings(&ret->env, &ret->nenv, 60747dd1d1bSDag-Erling Smørgrav primary->env, primary->nenv) != 0) 60847dd1d1bSDag-Erling Smørgrav goto alloc_fail; 60947dd1d1bSDag-Erling Smørgrav } else if (additional->nenv) { 61047dd1d1bSDag-Erling Smørgrav if (dup_strings(&ret->env, &ret->nenv, 61147dd1d1bSDag-Erling Smørgrav additional->env, additional->nenv) != 0) 61247dd1d1bSDag-Erling Smørgrav goto alloc_fail; 61347dd1d1bSDag-Erling Smørgrav } 61447dd1d1bSDag-Erling Smørgrav if (primary->npermitopen > 0) { 61547dd1d1bSDag-Erling Smørgrav if (dup_strings(&ret->permitopen, &ret->npermitopen, 61647dd1d1bSDag-Erling Smørgrav primary->permitopen, primary->npermitopen) != 0) 61747dd1d1bSDag-Erling Smørgrav goto alloc_fail; 61847dd1d1bSDag-Erling Smørgrav } else if (additional->npermitopen > 0) { 61947dd1d1bSDag-Erling Smørgrav if (dup_strings(&ret->permitopen, &ret->npermitopen, 62047dd1d1bSDag-Erling Smørgrav additional->permitopen, additional->npermitopen) != 0) 62147dd1d1bSDag-Erling Smørgrav goto alloc_fail; 62247dd1d1bSDag-Erling Smørgrav } 62347dd1d1bSDag-Erling Smørgrav 624*190cef3dSDag-Erling Smørgrav if (primary->npermitlisten > 0) { 625*190cef3dSDag-Erling Smørgrav if (dup_strings(&ret->permitlisten, &ret->npermitlisten, 626*190cef3dSDag-Erling Smørgrav primary->permitlisten, primary->npermitlisten) != 0) 627*190cef3dSDag-Erling Smørgrav goto alloc_fail; 628*190cef3dSDag-Erling Smørgrav } else if (additional->npermitlisten > 0) { 629*190cef3dSDag-Erling Smørgrav if (dup_strings(&ret->permitlisten, &ret->npermitlisten, 630*190cef3dSDag-Erling Smørgrav additional->permitlisten, additional->npermitlisten) != 0) 631*190cef3dSDag-Erling Smørgrav goto alloc_fail; 632*190cef3dSDag-Erling Smørgrav } 633*190cef3dSDag-Erling Smørgrav 63447dd1d1bSDag-Erling Smørgrav /* Flags are logical-AND (i.e. must be set in both for permission) */ 63547dd1d1bSDag-Erling Smørgrav #define OPTFLAG(x) ret->x = (primary->x == 1) && (additional->x == 1) 63647dd1d1bSDag-Erling Smørgrav OPTFLAG(permit_port_forwarding_flag); 63747dd1d1bSDag-Erling Smørgrav OPTFLAG(permit_agent_forwarding_flag); 63847dd1d1bSDag-Erling Smørgrav OPTFLAG(permit_x11_forwarding_flag); 63947dd1d1bSDag-Erling Smørgrav OPTFLAG(permit_pty_flag); 64047dd1d1bSDag-Erling Smørgrav OPTFLAG(permit_user_rc); 64147dd1d1bSDag-Erling Smørgrav #undef OPTFLAG 64247dd1d1bSDag-Erling Smørgrav 64347dd1d1bSDag-Erling Smørgrav /* Earliest expiry time should win */ 64447dd1d1bSDag-Erling Smørgrav if (primary->valid_before != 0) 64547dd1d1bSDag-Erling Smørgrav ret->valid_before = primary->valid_before; 64647dd1d1bSDag-Erling Smørgrav if (additional->valid_before != 0 && 64747dd1d1bSDag-Erling Smørgrav additional->valid_before < ret->valid_before) 64847dd1d1bSDag-Erling Smørgrav ret->valid_before = additional->valid_before; 64947dd1d1bSDag-Erling Smørgrav 65047dd1d1bSDag-Erling Smørgrav /* 65147dd1d1bSDag-Erling Smørgrav * When both multiple forced-command are specified, only 65247dd1d1bSDag-Erling Smørgrav * proceed if they are identical, otherwise fail. 65347dd1d1bSDag-Erling Smørgrav */ 65447dd1d1bSDag-Erling Smørgrav if (primary->force_command != NULL && 65547dd1d1bSDag-Erling Smørgrav additional->force_command != NULL) { 65647dd1d1bSDag-Erling Smørgrav if (strcmp(primary->force_command, 65747dd1d1bSDag-Erling Smørgrav additional->force_command) == 0) { 65847dd1d1bSDag-Erling Smørgrav /* ok */ 65947dd1d1bSDag-Erling Smørgrav ret->force_command = strdup(primary->force_command); 66047dd1d1bSDag-Erling Smørgrav if (ret->force_command == NULL) 66147dd1d1bSDag-Erling Smørgrav goto alloc_fail; 66247dd1d1bSDag-Erling Smørgrav } else { 66347dd1d1bSDag-Erling Smørgrav errstr = "forced command options do not match"; 66447dd1d1bSDag-Erling Smørgrav goto fail; 66547dd1d1bSDag-Erling Smørgrav } 66647dd1d1bSDag-Erling Smørgrav } else if (primary->force_command != NULL) { 66747dd1d1bSDag-Erling Smørgrav if ((ret->force_command = strdup( 66847dd1d1bSDag-Erling Smørgrav primary->force_command)) == NULL) 66947dd1d1bSDag-Erling Smørgrav goto alloc_fail; 67047dd1d1bSDag-Erling Smørgrav } else if (additional->force_command != NULL) { 67147dd1d1bSDag-Erling Smørgrav if ((ret->force_command = strdup( 67247dd1d1bSDag-Erling Smørgrav additional->force_command)) == NULL) 67347dd1d1bSDag-Erling Smørgrav goto alloc_fail; 67447dd1d1bSDag-Erling Smørgrav } 67547dd1d1bSDag-Erling Smørgrav /* success */ 67647dd1d1bSDag-Erling Smørgrav if (errstrp != NULL) 67747dd1d1bSDag-Erling Smørgrav *errstrp = NULL; 67847dd1d1bSDag-Erling Smørgrav return ret; 67947dd1d1bSDag-Erling Smørgrav 68047dd1d1bSDag-Erling Smørgrav alloc_fail: 68147dd1d1bSDag-Erling Smørgrav errstr = "memory allocation failed"; 68247dd1d1bSDag-Erling Smørgrav fail: 68347dd1d1bSDag-Erling Smørgrav if (errstrp != NULL) 68447dd1d1bSDag-Erling Smørgrav *errstrp = errstr; 68547dd1d1bSDag-Erling Smørgrav sshauthopt_free(ret); 68647dd1d1bSDag-Erling Smørgrav return NULL; 68747dd1d1bSDag-Erling Smørgrav } 68847dd1d1bSDag-Erling Smørgrav 68947dd1d1bSDag-Erling Smørgrav /* 69047dd1d1bSDag-Erling Smørgrav * Copy options 69147dd1d1bSDag-Erling Smørgrav */ 69247dd1d1bSDag-Erling Smørgrav struct sshauthopt * 69347dd1d1bSDag-Erling Smørgrav sshauthopt_copy(const struct sshauthopt *orig) 69447dd1d1bSDag-Erling Smørgrav { 69547dd1d1bSDag-Erling Smørgrav struct sshauthopt *ret; 69647dd1d1bSDag-Erling Smørgrav 69747dd1d1bSDag-Erling Smørgrav if ((ret = sshauthopt_new()) == NULL) 69847dd1d1bSDag-Erling Smørgrav return NULL; 69947dd1d1bSDag-Erling Smørgrav 70047dd1d1bSDag-Erling Smørgrav #define OPTSCALAR(x) ret->x = orig->x 70147dd1d1bSDag-Erling Smørgrav OPTSCALAR(permit_port_forwarding_flag); 70247dd1d1bSDag-Erling Smørgrav OPTSCALAR(permit_agent_forwarding_flag); 70347dd1d1bSDag-Erling Smørgrav OPTSCALAR(permit_x11_forwarding_flag); 70447dd1d1bSDag-Erling Smørgrav OPTSCALAR(permit_pty_flag); 70547dd1d1bSDag-Erling Smørgrav OPTSCALAR(permit_user_rc); 70647dd1d1bSDag-Erling Smørgrav OPTSCALAR(restricted); 70747dd1d1bSDag-Erling Smørgrav OPTSCALAR(cert_authority); 70847dd1d1bSDag-Erling Smørgrav OPTSCALAR(force_tun_device); 70947dd1d1bSDag-Erling Smørgrav OPTSCALAR(valid_before); 71047dd1d1bSDag-Erling Smørgrav #undef OPTSCALAR 71147dd1d1bSDag-Erling Smørgrav #define OPTSTRING(x) \ 71247dd1d1bSDag-Erling Smørgrav do { \ 71347dd1d1bSDag-Erling Smørgrav if (orig->x != NULL && (ret->x = strdup(orig->x)) == NULL) { \ 71447dd1d1bSDag-Erling Smørgrav sshauthopt_free(ret); \ 71547dd1d1bSDag-Erling Smørgrav return NULL; \ 71647dd1d1bSDag-Erling Smørgrav } \ 71747dd1d1bSDag-Erling Smørgrav } while (0) 71847dd1d1bSDag-Erling Smørgrav OPTSTRING(cert_principals); 71947dd1d1bSDag-Erling Smørgrav OPTSTRING(force_command); 72047dd1d1bSDag-Erling Smørgrav OPTSTRING(required_from_host_cert); 72147dd1d1bSDag-Erling Smørgrav OPTSTRING(required_from_host_keys); 72247dd1d1bSDag-Erling Smørgrav #undef OPTSTRING 72347dd1d1bSDag-Erling Smørgrav 72447dd1d1bSDag-Erling Smørgrav if (dup_strings(&ret->env, &ret->nenv, orig->env, orig->nenv) != 0 || 72547dd1d1bSDag-Erling Smørgrav dup_strings(&ret->permitopen, &ret->npermitopen, 726*190cef3dSDag-Erling Smørgrav orig->permitopen, orig->npermitopen) != 0 || 727*190cef3dSDag-Erling Smørgrav dup_strings(&ret->permitlisten, &ret->npermitlisten, 728*190cef3dSDag-Erling Smørgrav orig->permitlisten, orig->npermitlisten) != 0) { 72947dd1d1bSDag-Erling Smørgrav sshauthopt_free(ret); 73047dd1d1bSDag-Erling Smørgrav return NULL; 73147dd1d1bSDag-Erling Smørgrav } 73247dd1d1bSDag-Erling Smørgrav return ret; 73347dd1d1bSDag-Erling Smørgrav } 73447dd1d1bSDag-Erling Smørgrav 73547dd1d1bSDag-Erling Smørgrav static int 73647dd1d1bSDag-Erling Smørgrav serialise_array(struct sshbuf *m, char **a, size_t n) 73747dd1d1bSDag-Erling Smørgrav { 73847dd1d1bSDag-Erling Smørgrav struct sshbuf *b; 73947dd1d1bSDag-Erling Smørgrav size_t i; 74047dd1d1bSDag-Erling Smørgrav int r; 74147dd1d1bSDag-Erling Smørgrav 74247dd1d1bSDag-Erling Smørgrav if (n > INT_MAX) 74347dd1d1bSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 74447dd1d1bSDag-Erling Smørgrav 74547dd1d1bSDag-Erling Smørgrav if ((b = sshbuf_new()) == NULL) { 74647dd1d1bSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 74747dd1d1bSDag-Erling Smørgrav } 74847dd1d1bSDag-Erling Smørgrav for (i = 0; i < n; i++) { 74947dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(b, a[i])) != 0) { 75047dd1d1bSDag-Erling Smørgrav sshbuf_free(b); 75147dd1d1bSDag-Erling Smørgrav return r; 75247dd1d1bSDag-Erling Smørgrav } 75347dd1d1bSDag-Erling Smørgrav } 75447dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, n)) != 0 || 75547dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_stringb(m, b)) != 0) { 75647dd1d1bSDag-Erling Smørgrav sshbuf_free(b); 75747dd1d1bSDag-Erling Smørgrav return r; 75847dd1d1bSDag-Erling Smørgrav } 75947dd1d1bSDag-Erling Smørgrav /* success */ 760e2f6069cSDag-Erling Smørgrav return 0; 761b15c8340SDag-Erling Smørgrav } 762b15c8340SDag-Erling Smørgrav 76347dd1d1bSDag-Erling Smørgrav static int 76447dd1d1bSDag-Erling Smørgrav deserialise_array(struct sshbuf *m, char ***ap, size_t *np) 76547dd1d1bSDag-Erling Smørgrav { 76647dd1d1bSDag-Erling Smørgrav char **a = NULL; 76747dd1d1bSDag-Erling Smørgrav size_t i, n = 0; 76847dd1d1bSDag-Erling Smørgrav struct sshbuf *b = NULL; 76947dd1d1bSDag-Erling Smørgrav u_int tmp; 77047dd1d1bSDag-Erling Smørgrav int r = SSH_ERR_INTERNAL_ERROR; 77147dd1d1bSDag-Erling Smørgrav 77247dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &tmp)) != 0 || 77347dd1d1bSDag-Erling Smørgrav (r = sshbuf_froms(m, &b)) != 0) 77447dd1d1bSDag-Erling Smørgrav goto out; 77547dd1d1bSDag-Erling Smørgrav if (tmp > INT_MAX) { 77647dd1d1bSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 77747dd1d1bSDag-Erling Smørgrav goto out; 77847dd1d1bSDag-Erling Smørgrav } 77947dd1d1bSDag-Erling Smørgrav n = tmp; 78047dd1d1bSDag-Erling Smørgrav if (n > 0 && (a = calloc(n, sizeof(*a))) == NULL) { 78147dd1d1bSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 78247dd1d1bSDag-Erling Smørgrav goto out; 78347dd1d1bSDag-Erling Smørgrav } 78447dd1d1bSDag-Erling Smørgrav for (i = 0; i < n; i++) { 78547dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(b, &a[i], NULL)) != 0) 78647dd1d1bSDag-Erling Smørgrav goto out; 78747dd1d1bSDag-Erling Smørgrav } 78847dd1d1bSDag-Erling Smørgrav /* success */ 78947dd1d1bSDag-Erling Smørgrav r = 0; 79047dd1d1bSDag-Erling Smørgrav *ap = a; 79147dd1d1bSDag-Erling Smørgrav a = NULL; 79247dd1d1bSDag-Erling Smørgrav *np = n; 79347dd1d1bSDag-Erling Smørgrav n = 0; 79447dd1d1bSDag-Erling Smørgrav out: 79547dd1d1bSDag-Erling Smørgrav for (i = 0; i < n; i++) 79647dd1d1bSDag-Erling Smørgrav free(a[i]); 79747dd1d1bSDag-Erling Smørgrav free(a); 79847dd1d1bSDag-Erling Smørgrav sshbuf_free(b); 79947dd1d1bSDag-Erling Smørgrav return r; 80047dd1d1bSDag-Erling Smørgrav } 80147dd1d1bSDag-Erling Smørgrav 80247dd1d1bSDag-Erling Smørgrav static int 80347dd1d1bSDag-Erling Smørgrav serialise_nullable_string(struct sshbuf *m, const char *s) 80447dd1d1bSDag-Erling Smørgrav { 80547dd1d1bSDag-Erling Smørgrav int r; 80647dd1d1bSDag-Erling Smørgrav 80747dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_put_u8(m, s == NULL)) != 0 || 80847dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_cstring(m, s)) != 0) 80947dd1d1bSDag-Erling Smørgrav return r; 81047dd1d1bSDag-Erling Smørgrav return 0; 81147dd1d1bSDag-Erling Smørgrav } 81247dd1d1bSDag-Erling Smørgrav 81347dd1d1bSDag-Erling Smørgrav static int 81447dd1d1bSDag-Erling Smørgrav deserialise_nullable_string(struct sshbuf *m, char **sp) 81547dd1d1bSDag-Erling Smørgrav { 81647dd1d1bSDag-Erling Smørgrav int r; 81747dd1d1bSDag-Erling Smørgrav u_char flag; 81847dd1d1bSDag-Erling Smørgrav 81947dd1d1bSDag-Erling Smørgrav *sp = NULL; 82047dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_get_u8(m, &flag)) != 0 || 82147dd1d1bSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, flag ? NULL : sp, NULL)) != 0) 82247dd1d1bSDag-Erling Smørgrav return r; 82347dd1d1bSDag-Erling Smørgrav return 0; 82447dd1d1bSDag-Erling Smørgrav } 82547dd1d1bSDag-Erling Smørgrav 82647dd1d1bSDag-Erling Smørgrav int 82747dd1d1bSDag-Erling Smørgrav sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m, 82847dd1d1bSDag-Erling Smørgrav int untrusted) 82947dd1d1bSDag-Erling Smørgrav { 83047dd1d1bSDag-Erling Smørgrav int r = SSH_ERR_INTERNAL_ERROR; 83147dd1d1bSDag-Erling Smørgrav 83247dd1d1bSDag-Erling Smørgrav /* Flag and simple integer options */ 83347dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_put_u8(m, opts->permit_port_forwarding_flag)) != 0 || 83447dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u8(m, opts->permit_agent_forwarding_flag)) != 0 || 83547dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u8(m, opts->permit_x11_forwarding_flag)) != 0 || 83647dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u8(m, opts->permit_pty_flag)) != 0 || 83747dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u8(m, opts->permit_user_rc)) != 0 || 83847dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u8(m, opts->restricted)) != 0 || 83947dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u8(m, opts->cert_authority)) != 0 || 84047dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u64(m, opts->valid_before)) != 0) 84147dd1d1bSDag-Erling Smørgrav return r; 84247dd1d1bSDag-Erling Smørgrav 84347dd1d1bSDag-Erling Smørgrav /* tunnel number can be negative to indicate "unset" */ 84447dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_put_u8(m, opts->force_tun_device == -1)) != 0 || 84547dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u32(m, (opts->force_tun_device < 0) ? 84647dd1d1bSDag-Erling Smørgrav 0 : (u_int)opts->force_tun_device)) != 0) 84747dd1d1bSDag-Erling Smørgrav return r; 84847dd1d1bSDag-Erling Smørgrav 84947dd1d1bSDag-Erling Smørgrav /* String options; these may be NULL */ 85047dd1d1bSDag-Erling Smørgrav if ((r = serialise_nullable_string(m, 85147dd1d1bSDag-Erling Smørgrav untrusted ? "yes" : opts->cert_principals)) != 0 || 85247dd1d1bSDag-Erling Smørgrav (r = serialise_nullable_string(m, 85347dd1d1bSDag-Erling Smørgrav untrusted ? "true" : opts->force_command)) != 0 || 85447dd1d1bSDag-Erling Smørgrav (r = serialise_nullable_string(m, 85547dd1d1bSDag-Erling Smørgrav untrusted ? NULL : opts->required_from_host_cert)) != 0 || 85647dd1d1bSDag-Erling Smørgrav (r = serialise_nullable_string(m, 85747dd1d1bSDag-Erling Smørgrav untrusted ? NULL : opts->required_from_host_keys)) != 0) 85847dd1d1bSDag-Erling Smørgrav return r; 85947dd1d1bSDag-Erling Smørgrav 86047dd1d1bSDag-Erling Smørgrav /* Array options */ 86147dd1d1bSDag-Erling Smørgrav if ((r = serialise_array(m, opts->env, 86247dd1d1bSDag-Erling Smørgrav untrusted ? 0 : opts->nenv)) != 0 || 86347dd1d1bSDag-Erling Smørgrav (r = serialise_array(m, opts->permitopen, 864*190cef3dSDag-Erling Smørgrav untrusted ? 0 : opts->npermitopen)) != 0 || 865*190cef3dSDag-Erling Smørgrav (r = serialise_array(m, opts->permitlisten, 866*190cef3dSDag-Erling Smørgrav untrusted ? 0 : opts->npermitlisten)) != 0) 86747dd1d1bSDag-Erling Smørgrav return r; 86847dd1d1bSDag-Erling Smørgrav 86947dd1d1bSDag-Erling Smørgrav /* success */ 87047dd1d1bSDag-Erling Smørgrav return 0; 87147dd1d1bSDag-Erling Smørgrav } 87247dd1d1bSDag-Erling Smørgrav 87347dd1d1bSDag-Erling Smørgrav int 87447dd1d1bSDag-Erling Smørgrav sshauthopt_deserialise(struct sshbuf *m, struct sshauthopt **optsp) 87547dd1d1bSDag-Erling Smørgrav { 87647dd1d1bSDag-Erling Smørgrav struct sshauthopt *opts = NULL; 87747dd1d1bSDag-Erling Smørgrav int r = SSH_ERR_INTERNAL_ERROR; 87847dd1d1bSDag-Erling Smørgrav u_char f; 87947dd1d1bSDag-Erling Smørgrav u_int tmp; 88047dd1d1bSDag-Erling Smørgrav 88147dd1d1bSDag-Erling Smørgrav if ((opts = calloc(1, sizeof(*opts))) == NULL) 88247dd1d1bSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 88347dd1d1bSDag-Erling Smørgrav 88447dd1d1bSDag-Erling Smørgrav #define OPT_FLAG(x) \ 88547dd1d1bSDag-Erling Smørgrav do { \ 88647dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_get_u8(m, &f)) != 0) \ 88747dd1d1bSDag-Erling Smørgrav goto out; \ 88847dd1d1bSDag-Erling Smørgrav opts->x = f; \ 88947dd1d1bSDag-Erling Smørgrav } while (0) 89047dd1d1bSDag-Erling Smørgrav OPT_FLAG(permit_port_forwarding_flag); 89147dd1d1bSDag-Erling Smørgrav OPT_FLAG(permit_agent_forwarding_flag); 89247dd1d1bSDag-Erling Smørgrav OPT_FLAG(permit_x11_forwarding_flag); 89347dd1d1bSDag-Erling Smørgrav OPT_FLAG(permit_pty_flag); 89447dd1d1bSDag-Erling Smørgrav OPT_FLAG(permit_user_rc); 89547dd1d1bSDag-Erling Smørgrav OPT_FLAG(restricted); 89647dd1d1bSDag-Erling Smørgrav OPT_FLAG(cert_authority); 89747dd1d1bSDag-Erling Smørgrav #undef OPT_FLAG 89847dd1d1bSDag-Erling Smørgrav 89947dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_get_u64(m, &opts->valid_before)) != 0) 90047dd1d1bSDag-Erling Smørgrav goto out; 90147dd1d1bSDag-Erling Smørgrav 90247dd1d1bSDag-Erling Smørgrav /* tunnel number can be negative to indicate "unset" */ 90347dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_get_u8(m, &f)) != 0 || 90447dd1d1bSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &tmp)) != 0) 90547dd1d1bSDag-Erling Smørgrav goto out; 90647dd1d1bSDag-Erling Smørgrav opts->force_tun_device = f ? -1 : (int)tmp; 90747dd1d1bSDag-Erling Smørgrav 90847dd1d1bSDag-Erling Smørgrav /* String options may be NULL */ 90947dd1d1bSDag-Erling Smørgrav if ((r = deserialise_nullable_string(m, &opts->cert_principals)) != 0 || 91047dd1d1bSDag-Erling Smørgrav (r = deserialise_nullable_string(m, &opts->force_command)) != 0 || 91147dd1d1bSDag-Erling Smørgrav (r = deserialise_nullable_string(m, 91247dd1d1bSDag-Erling Smørgrav &opts->required_from_host_cert)) != 0 || 91347dd1d1bSDag-Erling Smørgrav (r = deserialise_nullable_string(m, 91447dd1d1bSDag-Erling Smørgrav &opts->required_from_host_keys)) != 0) 91547dd1d1bSDag-Erling Smørgrav goto out; 91647dd1d1bSDag-Erling Smørgrav 91747dd1d1bSDag-Erling Smørgrav /* Array options */ 91847dd1d1bSDag-Erling Smørgrav if ((r = deserialise_array(m, &opts->env, &opts->nenv)) != 0 || 91947dd1d1bSDag-Erling Smørgrav (r = deserialise_array(m, 920*190cef3dSDag-Erling Smørgrav &opts->permitopen, &opts->npermitopen)) != 0 || 921*190cef3dSDag-Erling Smørgrav (r = deserialise_array(m, 922*190cef3dSDag-Erling Smørgrav &opts->permitlisten, &opts->npermitlisten)) != 0) 92347dd1d1bSDag-Erling Smørgrav goto out; 92447dd1d1bSDag-Erling Smørgrav 92547dd1d1bSDag-Erling Smørgrav /* success */ 92647dd1d1bSDag-Erling Smørgrav r = 0; 92747dd1d1bSDag-Erling Smørgrav *optsp = opts; 92847dd1d1bSDag-Erling Smørgrav opts = NULL; 92947dd1d1bSDag-Erling Smørgrav out: 93047dd1d1bSDag-Erling Smørgrav sshauthopt_free(opts); 93147dd1d1bSDag-Erling Smørgrav return r; 93247dd1d1bSDag-Erling Smørgrav } 933