1*47dd1d1bSDag-Erling Smørgrav /* $OpenBSD: auth-options.c,v 1.78 2018/03/14 05:35:40 djm Exp $ */ 2b66f2d16SKris Kennaway /* 3*47dd1d1bSDag-Erling Smørgrav * Copyright (c) 2018 Damien Miller <djm@mindrot.org> 4*47dd1d1bSDag-Erling Smørgrav * 5*47dd1d1bSDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 6*47dd1d1bSDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 7*47dd1d1bSDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 8*47dd1d1bSDag-Erling Smørgrav * 9*47dd1d1bSDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10*47dd1d1bSDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11*47dd1d1bSDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12*47dd1d1bSDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13*47dd1d1bSDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14*47dd1d1bSDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15*47dd1d1bSDag-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> 27*47dd1d1bSDag-Erling Smørgrav #include <ctype.h> 28*47dd1d1bSDag-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" 38*47dd1d1bSDag-Erling Smørgrav #include "match.h" 39*47dd1d1bSDag-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 47*47dd1d1bSDag-Erling Smørgrav * point to the first character after the option. 48acc1a9efSDag-Erling Smørgrav */ 49acc1a9efSDag-Erling Smørgrav static int 50*47dd1d1bSDag-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); 53*47dd1d1bSDag-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 67*47dd1d1bSDag-Erling Smørgrav static char * 68*47dd1d1bSDag-Erling Smørgrav opt_dequote(const char **sp, const char **errstrp) 69b66f2d16SKris Kennaway { 70*47dd1d1bSDag-Erling Smørgrav const char *s = *sp; 71*47dd1d1bSDag-Erling Smørgrav char *ret; 72*47dd1d1bSDag-Erling Smørgrav size_t i; 735b9b2fafSBrian Feldman 74*47dd1d1bSDag-Erling Smørgrav *errstrp = NULL; 75*47dd1d1bSDag-Erling Smørgrav if (*s != '"') { 76*47dd1d1bSDag-Erling Smørgrav *errstrp = "missing start quote"; 77*47dd1d1bSDag-Erling Smørgrav return NULL; 78*47dd1d1bSDag-Erling Smørgrav } 79*47dd1d1bSDag-Erling Smørgrav s++; 80*47dd1d1bSDag-Erling Smørgrav if ((ret = malloc(strlen((s)) + 1)) == NULL) { 81*47dd1d1bSDag-Erling Smørgrav *errstrp = "memory allocation failed"; 82*47dd1d1bSDag-Erling Smørgrav return NULL; 83*47dd1d1bSDag-Erling Smørgrav } 84*47dd1d1bSDag-Erling Smørgrav for (i = 0; *s != '\0' && *s != '"';) { 85*47dd1d1bSDag-Erling Smørgrav if (s[0] == '\\' && s[1] == '"') 86*47dd1d1bSDag-Erling Smørgrav s++; 87*47dd1d1bSDag-Erling Smørgrav ret[i++] = *s++; 88*47dd1d1bSDag-Erling Smørgrav } 89*47dd1d1bSDag-Erling Smørgrav if (*s == '\0') { 90*47dd1d1bSDag-Erling Smørgrav *errstrp = "missing end quote"; 91*47dd1d1bSDag-Erling Smørgrav free(ret); 92*47dd1d1bSDag-Erling Smørgrav return NULL; 93*47dd1d1bSDag-Erling Smørgrav } 94*47dd1d1bSDag-Erling Smørgrav ret[i] = '\0'; 95*47dd1d1bSDag-Erling Smørgrav s++; 96*47dd1d1bSDag-Erling Smørgrav *sp = s; 97*47dd1d1bSDag-Erling Smørgrav return ret; 98*47dd1d1bSDag-Erling Smørgrav } 995b9b2fafSBrian Feldman 100*47dd1d1bSDag-Erling Smørgrav static int 101*47dd1d1bSDag-Erling Smørgrav opt_match(const char **opts, const char *term) 102*47dd1d1bSDag-Erling Smørgrav { 103*47dd1d1bSDag-Erling Smørgrav if (strncasecmp((*opts), term, strlen(term)) == 0 && 104*47dd1d1bSDag-Erling Smørgrav (*opts)[strlen(term)] == '=') { 105*47dd1d1bSDag-Erling Smørgrav *opts += strlen(term) + 1; 1061e8db6e2SBrian Feldman return 1; 107b15c8340SDag-Erling Smørgrav } 108b66f2d16SKris Kennaway return 0; 109b66f2d16SKris Kennaway } 1101e8db6e2SBrian Feldman 111*47dd1d1bSDag-Erling Smørgrav static int 112*47dd1d1bSDag-Erling Smørgrav dup_strings(char ***dstp, size_t *ndstp, char **src, size_t nsrc) 113*47dd1d1bSDag-Erling Smørgrav { 114*47dd1d1bSDag-Erling Smørgrav char **dst; 115*47dd1d1bSDag-Erling Smørgrav size_t i, j; 116545d5ecaSDag-Erling Smørgrav 117*47dd1d1bSDag-Erling Smørgrav *dstp = NULL; 118*47dd1d1bSDag-Erling Smørgrav *ndstp = 0; 119*47dd1d1bSDag-Erling Smørgrav if (nsrc == 0) 120*47dd1d1bSDag-Erling Smørgrav return 0; 121b66f2d16SKris Kennaway 122*47dd1d1bSDag-Erling Smørgrav if ((dst = calloc(nsrc, sizeof(*src))) == NULL) 123*47dd1d1bSDag-Erling Smørgrav return -1; 124*47dd1d1bSDag-Erling Smørgrav for (i = 0; i < nsrc; i++) { 125*47dd1d1bSDag-Erling Smørgrav if ((dst[i] = strdup(src[i])) == NULL) { 126*47dd1d1bSDag-Erling Smørgrav for (j = 0; j < i; j++) 127*47dd1d1bSDag-Erling Smørgrav free(dst[j]); 128*47dd1d1bSDag-Erling Smørgrav free(dst); 129*47dd1d1bSDag-Erling Smørgrav return -1; 130*47dd1d1bSDag-Erling Smørgrav } 131*47dd1d1bSDag-Erling Smørgrav } 132*47dd1d1bSDag-Erling Smørgrav /* success */ 133*47dd1d1bSDag-Erling Smørgrav *dstp = dst; 134*47dd1d1bSDag-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 141*47dd1d1bSDag-Erling Smørgrav cert_option_list(struct sshauthopt *opts, struct sshbuf *oblob, 142*47dd1d1bSDag-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; 147*47dd1d1bSDag-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) { 168*47dd1d1bSDag-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) { 172*47dd1d1bSDag-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) { 176*47dd1d1bSDag-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) { 179*47dd1d1bSDag-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) { 182*47dd1d1bSDag-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 } 194*47dd1d1bSDag-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 } 200*47dd1d1bSDag-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 } 210*47dd1d1bSDag-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 } 216*47dd1d1bSDag-Erling Smørgrav /* Check syntax */ 217*47dd1d1bSDag-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 } 222*47dd1d1bSDag-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 254*47dd1d1bSDag-Erling Smørgrav struct sshauthopt * 255*47dd1d1bSDag-Erling Smørgrav sshauthopt_new(void) 256e2f6069cSDag-Erling Smørgrav { 257*47dd1d1bSDag-Erling Smørgrav struct sshauthopt *ret; 258e2f6069cSDag-Erling Smørgrav 259*47dd1d1bSDag-Erling Smørgrav if ((ret = calloc(1, sizeof(*ret))) == NULL) 260*47dd1d1bSDag-Erling Smørgrav return NULL; 261*47dd1d1bSDag-Erling Smørgrav ret->force_tun_device = -1; 262*47dd1d1bSDag-Erling Smørgrav return ret; 263b15c8340SDag-Erling Smørgrav } 264*47dd1d1bSDag-Erling Smørgrav 265*47dd1d1bSDag-Erling Smørgrav void 266*47dd1d1bSDag-Erling Smørgrav sshauthopt_free(struct sshauthopt *opts) 267*47dd1d1bSDag-Erling Smørgrav { 268*47dd1d1bSDag-Erling Smørgrav size_t i; 269*47dd1d1bSDag-Erling Smørgrav 270*47dd1d1bSDag-Erling Smørgrav if (opts == NULL) 271*47dd1d1bSDag-Erling Smørgrav return; 272*47dd1d1bSDag-Erling Smørgrav 273*47dd1d1bSDag-Erling Smørgrav free(opts->cert_principals); 274*47dd1d1bSDag-Erling Smørgrav free(opts->force_command); 275*47dd1d1bSDag-Erling Smørgrav free(opts->required_from_host_cert); 276*47dd1d1bSDag-Erling Smørgrav free(opts->required_from_host_keys); 277*47dd1d1bSDag-Erling Smørgrav 278*47dd1d1bSDag-Erling Smørgrav for (i = 0; i < opts->nenv; i++) 279*47dd1d1bSDag-Erling Smørgrav free(opts->env[i]); 280*47dd1d1bSDag-Erling Smørgrav free(opts->env); 281*47dd1d1bSDag-Erling Smørgrav 282*47dd1d1bSDag-Erling Smørgrav for (i = 0; i < opts->npermitopen; i++) 283*47dd1d1bSDag-Erling Smørgrav free(opts->permitopen[i]); 284*47dd1d1bSDag-Erling Smørgrav free(opts->permitopen); 285*47dd1d1bSDag-Erling Smørgrav 286*47dd1d1bSDag-Erling Smørgrav explicit_bzero(opts, sizeof(*opts)); 287*47dd1d1bSDag-Erling Smørgrav free(opts); 288*47dd1d1bSDag-Erling Smørgrav } 289*47dd1d1bSDag-Erling Smørgrav 290*47dd1d1bSDag-Erling Smørgrav struct sshauthopt * 291*47dd1d1bSDag-Erling Smørgrav sshauthopt_new_with_keys_defaults(void) 292*47dd1d1bSDag-Erling Smørgrav { 293*47dd1d1bSDag-Erling Smørgrav struct sshauthopt *ret = NULL; 294*47dd1d1bSDag-Erling Smørgrav 295*47dd1d1bSDag-Erling Smørgrav if ((ret = sshauthopt_new()) == NULL) 296*47dd1d1bSDag-Erling Smørgrav return NULL; 297*47dd1d1bSDag-Erling Smørgrav 298*47dd1d1bSDag-Erling Smørgrav /* Defaults for authorized_keys flags */ 299*47dd1d1bSDag-Erling Smørgrav ret->permit_port_forwarding_flag = 1; 300*47dd1d1bSDag-Erling Smørgrav ret->permit_agent_forwarding_flag = 1; 301*47dd1d1bSDag-Erling Smørgrav ret->permit_x11_forwarding_flag = 1; 302*47dd1d1bSDag-Erling Smørgrav ret->permit_pty_flag = 1; 303*47dd1d1bSDag-Erling Smørgrav ret->permit_user_rc = 1; 304*47dd1d1bSDag-Erling Smørgrav return ret; 305*47dd1d1bSDag-Erling Smørgrav } 306*47dd1d1bSDag-Erling Smørgrav 307*47dd1d1bSDag-Erling Smørgrav struct sshauthopt * 308*47dd1d1bSDag-Erling Smørgrav sshauthopt_parse(const char *opts, const char **errstrp) 309*47dd1d1bSDag-Erling Smørgrav { 310*47dd1d1bSDag-Erling Smørgrav char **oarray, *opt, *cp, *tmp, *host; 311*47dd1d1bSDag-Erling Smørgrav int r; 312*47dd1d1bSDag-Erling Smørgrav struct sshauthopt *ret = NULL; 313*47dd1d1bSDag-Erling Smørgrav const char *errstr = "unknown error"; 314*47dd1d1bSDag-Erling Smørgrav uint64_t valid_before; 315*47dd1d1bSDag-Erling Smørgrav 316*47dd1d1bSDag-Erling Smørgrav if (errstrp != NULL) 317*47dd1d1bSDag-Erling Smørgrav *errstrp = NULL; 318*47dd1d1bSDag-Erling Smørgrav if ((ret = sshauthopt_new_with_keys_defaults()) == NULL) 319*47dd1d1bSDag-Erling Smørgrav goto alloc_fail; 320*47dd1d1bSDag-Erling Smørgrav 321*47dd1d1bSDag-Erling Smørgrav if (opts == NULL) 322*47dd1d1bSDag-Erling Smørgrav return ret; 323*47dd1d1bSDag-Erling Smørgrav 324*47dd1d1bSDag-Erling Smørgrav while (*opts && *opts != ' ' && *opts != '\t') { 325*47dd1d1bSDag-Erling Smørgrav /* flag options */ 326*47dd1d1bSDag-Erling Smørgrav if ((r = opt_flag("restrict", 0, &opts)) != -1) { 327*47dd1d1bSDag-Erling Smørgrav ret->restricted = 1; 328*47dd1d1bSDag-Erling Smørgrav ret->permit_port_forwarding_flag = 0; 329*47dd1d1bSDag-Erling Smørgrav ret->permit_agent_forwarding_flag = 0; 330*47dd1d1bSDag-Erling Smørgrav ret->permit_x11_forwarding_flag = 0; 331*47dd1d1bSDag-Erling Smørgrav ret->permit_pty_flag = 0; 332*47dd1d1bSDag-Erling Smørgrav ret->permit_user_rc = 0; 333*47dd1d1bSDag-Erling Smørgrav } else if ((r = opt_flag("cert-authority", 0, &opts)) != -1) { 334*47dd1d1bSDag-Erling Smørgrav ret->cert_authority = r; 335*47dd1d1bSDag-Erling Smørgrav } else if ((r = opt_flag("port-forwarding", 1, &opts)) != -1) { 336*47dd1d1bSDag-Erling Smørgrav ret->permit_port_forwarding_flag = r == 1; 337*47dd1d1bSDag-Erling Smørgrav } else if ((r = opt_flag("agent-forwarding", 1, &opts)) != -1) { 338*47dd1d1bSDag-Erling Smørgrav ret->permit_agent_forwarding_flag = r == 1; 339*47dd1d1bSDag-Erling Smørgrav } else if ((r = opt_flag("x11-forwarding", 1, &opts)) != -1) { 340*47dd1d1bSDag-Erling Smørgrav ret->permit_x11_forwarding_flag = r == 1; 341*47dd1d1bSDag-Erling Smørgrav } else if ((r = opt_flag("pty", 1, &opts)) != -1) { 342*47dd1d1bSDag-Erling Smørgrav ret->permit_pty_flag = r == 1; 343*47dd1d1bSDag-Erling Smørgrav } else if ((r = opt_flag("user-rc", 1, &opts)) != -1) { 344*47dd1d1bSDag-Erling Smørgrav ret->permit_user_rc = r == 1; 345*47dd1d1bSDag-Erling Smørgrav } else if (opt_match(&opts, "command")) { 346*47dd1d1bSDag-Erling Smørgrav if (ret->force_command != NULL) { 347*47dd1d1bSDag-Erling Smørgrav errstr = "multiple \"command\" clauses"; 348*47dd1d1bSDag-Erling Smørgrav goto fail; 349*47dd1d1bSDag-Erling Smørgrav } 350*47dd1d1bSDag-Erling Smørgrav ret->force_command = opt_dequote(&opts, &errstr); 351*47dd1d1bSDag-Erling Smørgrav if (ret->force_command == NULL) 352*47dd1d1bSDag-Erling Smørgrav goto fail; 353*47dd1d1bSDag-Erling Smørgrav } else if (opt_match(&opts, "principals")) { 354*47dd1d1bSDag-Erling Smørgrav if (ret->cert_principals != NULL) { 355*47dd1d1bSDag-Erling Smørgrav errstr = "multiple \"principals\" clauses"; 356*47dd1d1bSDag-Erling Smørgrav goto fail; 357*47dd1d1bSDag-Erling Smørgrav } 358*47dd1d1bSDag-Erling Smørgrav ret->cert_principals = opt_dequote(&opts, &errstr); 359*47dd1d1bSDag-Erling Smørgrav if (ret->cert_principals == NULL) 360*47dd1d1bSDag-Erling Smørgrav goto fail; 361*47dd1d1bSDag-Erling Smørgrav } else if (opt_match(&opts, "from")) { 362*47dd1d1bSDag-Erling Smørgrav if (ret->required_from_host_keys != NULL) { 363*47dd1d1bSDag-Erling Smørgrav errstr = "multiple \"from\" clauses"; 364*47dd1d1bSDag-Erling Smørgrav goto fail; 365*47dd1d1bSDag-Erling Smørgrav } 366*47dd1d1bSDag-Erling Smørgrav ret->required_from_host_keys = opt_dequote(&opts, 367*47dd1d1bSDag-Erling Smørgrav &errstr); 368*47dd1d1bSDag-Erling Smørgrav if (ret->required_from_host_keys == NULL) 369*47dd1d1bSDag-Erling Smørgrav goto fail; 370*47dd1d1bSDag-Erling Smørgrav } else if (opt_match(&opts, "expiry-time")) { 371*47dd1d1bSDag-Erling Smørgrav if ((opt = opt_dequote(&opts, &errstr)) == NULL) 372*47dd1d1bSDag-Erling Smørgrav goto fail; 373*47dd1d1bSDag-Erling Smørgrav if (parse_absolute_time(opt, &valid_before) != 0 || 374*47dd1d1bSDag-Erling Smørgrav valid_before == 0) { 375*47dd1d1bSDag-Erling Smørgrav free(opt); 376*47dd1d1bSDag-Erling Smørgrav errstr = "invalid expires time"; 377*47dd1d1bSDag-Erling Smørgrav goto fail; 378*47dd1d1bSDag-Erling Smørgrav } 379*47dd1d1bSDag-Erling Smørgrav free(opt); 380*47dd1d1bSDag-Erling Smørgrav if (ret->valid_before == 0 || 381*47dd1d1bSDag-Erling Smørgrav valid_before < ret->valid_before) 382*47dd1d1bSDag-Erling Smørgrav ret->valid_before = valid_before; 383*47dd1d1bSDag-Erling Smørgrav } else if (opt_match(&opts, "environment")) { 384*47dd1d1bSDag-Erling Smørgrav if (ret->nenv > INT_MAX) { 385*47dd1d1bSDag-Erling Smørgrav errstr = "too many environment strings"; 386*47dd1d1bSDag-Erling Smørgrav goto fail; 387*47dd1d1bSDag-Erling Smørgrav } 388*47dd1d1bSDag-Erling Smørgrav if ((opt = opt_dequote(&opts, &errstr)) == NULL) 389*47dd1d1bSDag-Erling Smørgrav goto fail; 390*47dd1d1bSDag-Erling Smørgrav /* env name must be alphanumeric and followed by '=' */ 391*47dd1d1bSDag-Erling Smørgrav if ((tmp = strchr(opt, '=')) == NULL) { 392*47dd1d1bSDag-Erling Smørgrav free(opt); 393*47dd1d1bSDag-Erling Smørgrav errstr = "invalid environment string"; 394*47dd1d1bSDag-Erling Smørgrav goto fail; 395*47dd1d1bSDag-Erling Smørgrav } 396*47dd1d1bSDag-Erling Smørgrav for (cp = opt; cp < tmp; cp++) { 397*47dd1d1bSDag-Erling Smørgrav if (!isalnum((u_char)*cp)) { 398*47dd1d1bSDag-Erling Smørgrav free(opt); 399*47dd1d1bSDag-Erling Smørgrav errstr = "invalid environment string"; 400*47dd1d1bSDag-Erling Smørgrav goto fail; 401*47dd1d1bSDag-Erling Smørgrav } 402*47dd1d1bSDag-Erling Smørgrav } 403*47dd1d1bSDag-Erling Smørgrav /* Append it. */ 404*47dd1d1bSDag-Erling Smørgrav oarray = ret->env; 405*47dd1d1bSDag-Erling Smørgrav if ((ret->env = recallocarray(ret->env, ret->nenv, 406*47dd1d1bSDag-Erling Smørgrav ret->nenv + 1, sizeof(*ret->env))) == NULL) { 407*47dd1d1bSDag-Erling Smørgrav free(opt); 408*47dd1d1bSDag-Erling Smørgrav ret->env = oarray; /* put it back for cleanup */ 409*47dd1d1bSDag-Erling Smørgrav goto alloc_fail; 410*47dd1d1bSDag-Erling Smørgrav } 411*47dd1d1bSDag-Erling Smørgrav ret->env[ret->nenv++] = opt; 412*47dd1d1bSDag-Erling Smørgrav } else if (opt_match(&opts, "permitopen")) { 413*47dd1d1bSDag-Erling Smørgrav if (ret->npermitopen > INT_MAX) { 414*47dd1d1bSDag-Erling Smørgrav errstr = "too many permitopens"; 415*47dd1d1bSDag-Erling Smørgrav goto fail; 416*47dd1d1bSDag-Erling Smørgrav } 417*47dd1d1bSDag-Erling Smørgrav if ((opt = opt_dequote(&opts, &errstr)) == NULL) 418*47dd1d1bSDag-Erling Smørgrav goto fail; 419*47dd1d1bSDag-Erling Smørgrav if ((tmp = strdup(opt)) == NULL) { 420*47dd1d1bSDag-Erling Smørgrav free(opt); 421*47dd1d1bSDag-Erling Smørgrav goto alloc_fail; 422*47dd1d1bSDag-Erling Smørgrav } 423*47dd1d1bSDag-Erling Smørgrav cp = tmp; 424*47dd1d1bSDag-Erling Smørgrav /* validate syntax of permitopen before recording it. */ 425*47dd1d1bSDag-Erling Smørgrav host = hpdelim(&cp); 426*47dd1d1bSDag-Erling Smørgrav if (host == NULL || strlen(host) >= NI_MAXHOST) { 427*47dd1d1bSDag-Erling Smørgrav free(tmp); 428*47dd1d1bSDag-Erling Smørgrav free(opt); 429*47dd1d1bSDag-Erling Smørgrav errstr = "invalid permitopen hostname"; 430*47dd1d1bSDag-Erling Smørgrav goto fail; 431*47dd1d1bSDag-Erling Smørgrav } 432*47dd1d1bSDag-Erling Smørgrav /* 433*47dd1d1bSDag-Erling Smørgrav * don't want to use permitopen_port to avoid 434*47dd1d1bSDag-Erling Smørgrav * dependency on channels.[ch] here. 435*47dd1d1bSDag-Erling Smørgrav */ 436*47dd1d1bSDag-Erling Smørgrav if (cp == NULL || 437*47dd1d1bSDag-Erling Smørgrav (strcmp(cp, "*") != 0 && a2port(cp) <= 0)) { 438*47dd1d1bSDag-Erling Smørgrav free(tmp); 439*47dd1d1bSDag-Erling Smørgrav free(opt); 440*47dd1d1bSDag-Erling Smørgrav errstr = "invalid permitopen port"; 441*47dd1d1bSDag-Erling Smørgrav goto fail; 442*47dd1d1bSDag-Erling Smørgrav } 443*47dd1d1bSDag-Erling Smørgrav /* XXX - add streamlocal support */ 444*47dd1d1bSDag-Erling Smørgrav free(tmp); 445*47dd1d1bSDag-Erling Smørgrav /* Record it */ 446*47dd1d1bSDag-Erling Smørgrav oarray = ret->permitopen; 447*47dd1d1bSDag-Erling Smørgrav if ((ret->permitopen = recallocarray(ret->permitopen, 448*47dd1d1bSDag-Erling Smørgrav ret->npermitopen, ret->npermitopen + 1, 449*47dd1d1bSDag-Erling Smørgrav sizeof(*ret->permitopen))) == NULL) { 450*47dd1d1bSDag-Erling Smørgrav free(opt); 451*47dd1d1bSDag-Erling Smørgrav ret->permitopen = oarray; 452*47dd1d1bSDag-Erling Smørgrav goto alloc_fail; 453*47dd1d1bSDag-Erling Smørgrav } 454*47dd1d1bSDag-Erling Smørgrav ret->permitopen[ret->npermitopen++] = opt; 455*47dd1d1bSDag-Erling Smørgrav } else if (opt_match(&opts, "tunnel")) { 456*47dd1d1bSDag-Erling Smørgrav if ((opt = opt_dequote(&opts, &errstr)) == NULL) 457*47dd1d1bSDag-Erling Smørgrav goto fail; 458*47dd1d1bSDag-Erling Smørgrav ret->force_tun_device = a2tun(opt, NULL); 459*47dd1d1bSDag-Erling Smørgrav free(opt); 460*47dd1d1bSDag-Erling Smørgrav if (ret->force_tun_device == SSH_TUNID_ERR) { 461*47dd1d1bSDag-Erling Smørgrav errstr = "invalid tun device"; 462*47dd1d1bSDag-Erling Smørgrav goto fail; 463*47dd1d1bSDag-Erling Smørgrav } 464*47dd1d1bSDag-Erling Smørgrav } 465*47dd1d1bSDag-Erling Smørgrav /* 466*47dd1d1bSDag-Erling Smørgrav * Skip the comma, and move to the next option 467*47dd1d1bSDag-Erling Smørgrav * (or break out if there are no more). 468*47dd1d1bSDag-Erling Smørgrav */ 469*47dd1d1bSDag-Erling Smørgrav if (*opts == '\0' || *opts == ' ' || *opts == '\t') 470*47dd1d1bSDag-Erling Smørgrav break; /* End of options. */ 471*47dd1d1bSDag-Erling Smørgrav /* Anything other than a comma is an unknown option */ 472*47dd1d1bSDag-Erling Smørgrav if (*opts != ',') { 473*47dd1d1bSDag-Erling Smørgrav errstr = "unknown key option"; 474*47dd1d1bSDag-Erling Smørgrav goto fail; 475*47dd1d1bSDag-Erling Smørgrav } 476*47dd1d1bSDag-Erling Smørgrav opts++; 477*47dd1d1bSDag-Erling Smørgrav if (*opts == '\0') { 478*47dd1d1bSDag-Erling Smørgrav errstr = "unexpected end-of-options"; 479*47dd1d1bSDag-Erling Smørgrav goto fail; 480*47dd1d1bSDag-Erling Smørgrav } 481*47dd1d1bSDag-Erling Smørgrav } 482*47dd1d1bSDag-Erling Smørgrav 483ca86bcf2SDag-Erling Smørgrav /* success */ 484*47dd1d1bSDag-Erling Smørgrav if (errstrp != NULL) 485*47dd1d1bSDag-Erling Smørgrav *errstrp = NULL; 486*47dd1d1bSDag-Erling Smørgrav return ret; 487*47dd1d1bSDag-Erling Smørgrav 488*47dd1d1bSDag-Erling Smørgrav alloc_fail: 489*47dd1d1bSDag-Erling Smørgrav errstr = "memory allocation failed"; 490*47dd1d1bSDag-Erling Smørgrav fail: 491*47dd1d1bSDag-Erling Smørgrav sshauthopt_free(ret); 492*47dd1d1bSDag-Erling Smørgrav if (errstrp != NULL) 493*47dd1d1bSDag-Erling Smørgrav *errstrp = errstr; 494*47dd1d1bSDag-Erling Smørgrav return NULL; 495*47dd1d1bSDag-Erling Smørgrav } 496*47dd1d1bSDag-Erling Smørgrav 497*47dd1d1bSDag-Erling Smørgrav struct sshauthopt * 498*47dd1d1bSDag-Erling Smørgrav sshauthopt_from_cert(struct sshkey *k) 499*47dd1d1bSDag-Erling Smørgrav { 500*47dd1d1bSDag-Erling Smørgrav struct sshauthopt *ret; 501*47dd1d1bSDag-Erling Smørgrav 502*47dd1d1bSDag-Erling Smørgrav if (k == NULL || !sshkey_type_is_cert(k->type) || k->cert == NULL || 503*47dd1d1bSDag-Erling Smørgrav k->cert->type != SSH2_CERT_TYPE_USER) 504*47dd1d1bSDag-Erling Smørgrav return NULL; 505*47dd1d1bSDag-Erling Smørgrav 506*47dd1d1bSDag-Erling Smørgrav if ((ret = sshauthopt_new()) == NULL) 507*47dd1d1bSDag-Erling Smørgrav return NULL; 508*47dd1d1bSDag-Erling Smørgrav 509*47dd1d1bSDag-Erling Smørgrav /* Handle options and critical extensions separately */ 510*47dd1d1bSDag-Erling Smørgrav if (cert_option_list(ret, k->cert->critical, 511*47dd1d1bSDag-Erling Smørgrav OPTIONS_CRITICAL, 1) == -1) { 512*47dd1d1bSDag-Erling Smørgrav sshauthopt_free(ret); 513*47dd1d1bSDag-Erling Smørgrav return NULL; 514*47dd1d1bSDag-Erling Smørgrav } 515*47dd1d1bSDag-Erling Smørgrav if (cert_option_list(ret, k->cert->extensions, 516*47dd1d1bSDag-Erling Smørgrav OPTIONS_EXTENSIONS, 0) == -1) { 517*47dd1d1bSDag-Erling Smørgrav sshauthopt_free(ret); 518*47dd1d1bSDag-Erling Smørgrav return NULL; 519*47dd1d1bSDag-Erling Smørgrav } 520*47dd1d1bSDag-Erling Smørgrav /* success */ 521*47dd1d1bSDag-Erling Smørgrav return ret; 522*47dd1d1bSDag-Erling Smørgrav } 523*47dd1d1bSDag-Erling Smørgrav 524*47dd1d1bSDag-Erling Smørgrav /* 525*47dd1d1bSDag-Erling Smørgrav * Merges "additional" options to "primary" and returns the result. 526*47dd1d1bSDag-Erling Smørgrav * NB. Some options from primary have primacy. 527*47dd1d1bSDag-Erling Smørgrav */ 528*47dd1d1bSDag-Erling Smørgrav struct sshauthopt * 529*47dd1d1bSDag-Erling Smørgrav sshauthopt_merge(const struct sshauthopt *primary, 530*47dd1d1bSDag-Erling Smørgrav const struct sshauthopt *additional, const char **errstrp) 531*47dd1d1bSDag-Erling Smørgrav { 532*47dd1d1bSDag-Erling Smørgrav struct sshauthopt *ret; 533*47dd1d1bSDag-Erling Smørgrav const char *errstr = "internal error"; 534*47dd1d1bSDag-Erling Smørgrav const char *tmp; 535*47dd1d1bSDag-Erling Smørgrav 536*47dd1d1bSDag-Erling Smørgrav if (errstrp != NULL) 537*47dd1d1bSDag-Erling Smørgrav *errstrp = NULL; 538*47dd1d1bSDag-Erling Smørgrav 539*47dd1d1bSDag-Erling Smørgrav if ((ret = sshauthopt_new()) == NULL) 540*47dd1d1bSDag-Erling Smørgrav goto alloc_fail; 541*47dd1d1bSDag-Erling Smørgrav 542*47dd1d1bSDag-Erling Smørgrav /* cert_authority and cert_principals are cleared in result */ 543*47dd1d1bSDag-Erling Smørgrav 544*47dd1d1bSDag-Erling Smørgrav /* Prefer access lists from primary. */ 545*47dd1d1bSDag-Erling Smørgrav /* XXX err is both set and mismatch? */ 546*47dd1d1bSDag-Erling Smørgrav tmp = primary->required_from_host_cert; 547*47dd1d1bSDag-Erling Smørgrav if (tmp == NULL) 548*47dd1d1bSDag-Erling Smørgrav tmp = additional->required_from_host_cert; 549*47dd1d1bSDag-Erling Smørgrav if (tmp != NULL && (ret->required_from_host_cert = strdup(tmp)) == NULL) 550*47dd1d1bSDag-Erling Smørgrav goto alloc_fail; 551*47dd1d1bSDag-Erling Smørgrav tmp = primary->required_from_host_keys; 552*47dd1d1bSDag-Erling Smørgrav if (tmp == NULL) 553*47dd1d1bSDag-Erling Smørgrav tmp = additional->required_from_host_keys; 554*47dd1d1bSDag-Erling Smørgrav if (tmp != NULL && (ret->required_from_host_keys = strdup(tmp)) == NULL) 555*47dd1d1bSDag-Erling Smørgrav goto alloc_fail; 556*47dd1d1bSDag-Erling Smørgrav 557*47dd1d1bSDag-Erling Smørgrav /* force_tun_device, permitopen and environment prefer the primary. */ 558*47dd1d1bSDag-Erling Smørgrav ret->force_tun_device = primary->force_tun_device; 559*47dd1d1bSDag-Erling Smørgrav if (ret->force_tun_device == -1) 560*47dd1d1bSDag-Erling Smørgrav ret->force_tun_device = additional->force_tun_device; 561*47dd1d1bSDag-Erling Smørgrav if (primary->nenv > 0) { 562*47dd1d1bSDag-Erling Smørgrav if (dup_strings(&ret->env, &ret->nenv, 563*47dd1d1bSDag-Erling Smørgrav primary->env, primary->nenv) != 0) 564*47dd1d1bSDag-Erling Smørgrav goto alloc_fail; 565*47dd1d1bSDag-Erling Smørgrav } else if (additional->nenv) { 566*47dd1d1bSDag-Erling Smørgrav if (dup_strings(&ret->env, &ret->nenv, 567*47dd1d1bSDag-Erling Smørgrav additional->env, additional->nenv) != 0) 568*47dd1d1bSDag-Erling Smørgrav goto alloc_fail; 569*47dd1d1bSDag-Erling Smørgrav } 570*47dd1d1bSDag-Erling Smørgrav if (primary->npermitopen > 0) { 571*47dd1d1bSDag-Erling Smørgrav if (dup_strings(&ret->permitopen, &ret->npermitopen, 572*47dd1d1bSDag-Erling Smørgrav primary->permitopen, primary->npermitopen) != 0) 573*47dd1d1bSDag-Erling Smørgrav goto alloc_fail; 574*47dd1d1bSDag-Erling Smørgrav } else if (additional->npermitopen > 0) { 575*47dd1d1bSDag-Erling Smørgrav if (dup_strings(&ret->permitopen, &ret->npermitopen, 576*47dd1d1bSDag-Erling Smørgrav additional->permitopen, additional->npermitopen) != 0) 577*47dd1d1bSDag-Erling Smørgrav goto alloc_fail; 578*47dd1d1bSDag-Erling Smørgrav } 579*47dd1d1bSDag-Erling Smørgrav 580*47dd1d1bSDag-Erling Smørgrav /* Flags are logical-AND (i.e. must be set in both for permission) */ 581*47dd1d1bSDag-Erling Smørgrav #define OPTFLAG(x) ret->x = (primary->x == 1) && (additional->x == 1) 582*47dd1d1bSDag-Erling Smørgrav OPTFLAG(permit_port_forwarding_flag); 583*47dd1d1bSDag-Erling Smørgrav OPTFLAG(permit_agent_forwarding_flag); 584*47dd1d1bSDag-Erling Smørgrav OPTFLAG(permit_x11_forwarding_flag); 585*47dd1d1bSDag-Erling Smørgrav OPTFLAG(permit_pty_flag); 586*47dd1d1bSDag-Erling Smørgrav OPTFLAG(permit_user_rc); 587*47dd1d1bSDag-Erling Smørgrav #undef OPTFLAG 588*47dd1d1bSDag-Erling Smørgrav 589*47dd1d1bSDag-Erling Smørgrav /* Earliest expiry time should win */ 590*47dd1d1bSDag-Erling Smørgrav if (primary->valid_before != 0) 591*47dd1d1bSDag-Erling Smørgrav ret->valid_before = primary->valid_before; 592*47dd1d1bSDag-Erling Smørgrav if (additional->valid_before != 0 && 593*47dd1d1bSDag-Erling Smørgrav additional->valid_before < ret->valid_before) 594*47dd1d1bSDag-Erling Smørgrav ret->valid_before = additional->valid_before; 595*47dd1d1bSDag-Erling Smørgrav 596*47dd1d1bSDag-Erling Smørgrav /* 597*47dd1d1bSDag-Erling Smørgrav * When both multiple forced-command are specified, only 598*47dd1d1bSDag-Erling Smørgrav * proceed if they are identical, otherwise fail. 599*47dd1d1bSDag-Erling Smørgrav */ 600*47dd1d1bSDag-Erling Smørgrav if (primary->force_command != NULL && 601*47dd1d1bSDag-Erling Smørgrav additional->force_command != NULL) { 602*47dd1d1bSDag-Erling Smørgrav if (strcmp(primary->force_command, 603*47dd1d1bSDag-Erling Smørgrav additional->force_command) == 0) { 604*47dd1d1bSDag-Erling Smørgrav /* ok */ 605*47dd1d1bSDag-Erling Smørgrav ret->force_command = strdup(primary->force_command); 606*47dd1d1bSDag-Erling Smørgrav if (ret->force_command == NULL) 607*47dd1d1bSDag-Erling Smørgrav goto alloc_fail; 608*47dd1d1bSDag-Erling Smørgrav } else { 609*47dd1d1bSDag-Erling Smørgrav errstr = "forced command options do not match"; 610*47dd1d1bSDag-Erling Smørgrav goto fail; 611*47dd1d1bSDag-Erling Smørgrav } 612*47dd1d1bSDag-Erling Smørgrav } else if (primary->force_command != NULL) { 613*47dd1d1bSDag-Erling Smørgrav if ((ret->force_command = strdup( 614*47dd1d1bSDag-Erling Smørgrav primary->force_command)) == NULL) 615*47dd1d1bSDag-Erling Smørgrav goto alloc_fail; 616*47dd1d1bSDag-Erling Smørgrav } else if (additional->force_command != NULL) { 617*47dd1d1bSDag-Erling Smørgrav if ((ret->force_command = strdup( 618*47dd1d1bSDag-Erling Smørgrav additional->force_command)) == NULL) 619*47dd1d1bSDag-Erling Smørgrav goto alloc_fail; 620*47dd1d1bSDag-Erling Smørgrav } 621*47dd1d1bSDag-Erling Smørgrav /* success */ 622*47dd1d1bSDag-Erling Smørgrav if (errstrp != NULL) 623*47dd1d1bSDag-Erling Smørgrav *errstrp = NULL; 624*47dd1d1bSDag-Erling Smørgrav return ret; 625*47dd1d1bSDag-Erling Smørgrav 626*47dd1d1bSDag-Erling Smørgrav alloc_fail: 627*47dd1d1bSDag-Erling Smørgrav errstr = "memory allocation failed"; 628*47dd1d1bSDag-Erling Smørgrav fail: 629*47dd1d1bSDag-Erling Smørgrav if (errstrp != NULL) 630*47dd1d1bSDag-Erling Smørgrav *errstrp = errstr; 631*47dd1d1bSDag-Erling Smørgrav sshauthopt_free(ret); 632*47dd1d1bSDag-Erling Smørgrav return NULL; 633*47dd1d1bSDag-Erling Smørgrav } 634*47dd1d1bSDag-Erling Smørgrav 635*47dd1d1bSDag-Erling Smørgrav /* 636*47dd1d1bSDag-Erling Smørgrav * Copy options 637*47dd1d1bSDag-Erling Smørgrav */ 638*47dd1d1bSDag-Erling Smørgrav struct sshauthopt * 639*47dd1d1bSDag-Erling Smørgrav sshauthopt_copy(const struct sshauthopt *orig) 640*47dd1d1bSDag-Erling Smørgrav { 641*47dd1d1bSDag-Erling Smørgrav struct sshauthopt *ret; 642*47dd1d1bSDag-Erling Smørgrav 643*47dd1d1bSDag-Erling Smørgrav if ((ret = sshauthopt_new()) == NULL) 644*47dd1d1bSDag-Erling Smørgrav return NULL; 645*47dd1d1bSDag-Erling Smørgrav 646*47dd1d1bSDag-Erling Smørgrav #define OPTSCALAR(x) ret->x = orig->x 647*47dd1d1bSDag-Erling Smørgrav OPTSCALAR(permit_port_forwarding_flag); 648*47dd1d1bSDag-Erling Smørgrav OPTSCALAR(permit_agent_forwarding_flag); 649*47dd1d1bSDag-Erling Smørgrav OPTSCALAR(permit_x11_forwarding_flag); 650*47dd1d1bSDag-Erling Smørgrav OPTSCALAR(permit_pty_flag); 651*47dd1d1bSDag-Erling Smørgrav OPTSCALAR(permit_user_rc); 652*47dd1d1bSDag-Erling Smørgrav OPTSCALAR(restricted); 653*47dd1d1bSDag-Erling Smørgrav OPTSCALAR(cert_authority); 654*47dd1d1bSDag-Erling Smørgrav OPTSCALAR(force_tun_device); 655*47dd1d1bSDag-Erling Smørgrav OPTSCALAR(valid_before); 656*47dd1d1bSDag-Erling Smørgrav #undef OPTSCALAR 657*47dd1d1bSDag-Erling Smørgrav #define OPTSTRING(x) \ 658*47dd1d1bSDag-Erling Smørgrav do { \ 659*47dd1d1bSDag-Erling Smørgrav if (orig->x != NULL && (ret->x = strdup(orig->x)) == NULL) { \ 660*47dd1d1bSDag-Erling Smørgrav sshauthopt_free(ret); \ 661*47dd1d1bSDag-Erling Smørgrav return NULL; \ 662*47dd1d1bSDag-Erling Smørgrav } \ 663*47dd1d1bSDag-Erling Smørgrav } while (0) 664*47dd1d1bSDag-Erling Smørgrav OPTSTRING(cert_principals); 665*47dd1d1bSDag-Erling Smørgrav OPTSTRING(force_command); 666*47dd1d1bSDag-Erling Smørgrav OPTSTRING(required_from_host_cert); 667*47dd1d1bSDag-Erling Smørgrav OPTSTRING(required_from_host_keys); 668*47dd1d1bSDag-Erling Smørgrav #undef OPTSTRING 669*47dd1d1bSDag-Erling Smørgrav 670*47dd1d1bSDag-Erling Smørgrav if (dup_strings(&ret->env, &ret->nenv, orig->env, orig->nenv) != 0 || 671*47dd1d1bSDag-Erling Smørgrav dup_strings(&ret->permitopen, &ret->npermitopen, 672*47dd1d1bSDag-Erling Smørgrav orig->permitopen, orig->npermitopen) != 0) { 673*47dd1d1bSDag-Erling Smørgrav sshauthopt_free(ret); 674*47dd1d1bSDag-Erling Smørgrav return NULL; 675*47dd1d1bSDag-Erling Smørgrav } 676*47dd1d1bSDag-Erling Smørgrav return ret; 677*47dd1d1bSDag-Erling Smørgrav } 678*47dd1d1bSDag-Erling Smørgrav 679*47dd1d1bSDag-Erling Smørgrav static int 680*47dd1d1bSDag-Erling Smørgrav serialise_array(struct sshbuf *m, char **a, size_t n) 681*47dd1d1bSDag-Erling Smørgrav { 682*47dd1d1bSDag-Erling Smørgrav struct sshbuf *b; 683*47dd1d1bSDag-Erling Smørgrav size_t i; 684*47dd1d1bSDag-Erling Smørgrav int r; 685*47dd1d1bSDag-Erling Smørgrav 686*47dd1d1bSDag-Erling Smørgrav if (n > INT_MAX) 687*47dd1d1bSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 688*47dd1d1bSDag-Erling Smørgrav 689*47dd1d1bSDag-Erling Smørgrav if ((b = sshbuf_new()) == NULL) { 690*47dd1d1bSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 691*47dd1d1bSDag-Erling Smørgrav } 692*47dd1d1bSDag-Erling Smørgrav for (i = 0; i < n; i++) { 693*47dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(b, a[i])) != 0) { 694*47dd1d1bSDag-Erling Smørgrav sshbuf_free(b); 695*47dd1d1bSDag-Erling Smørgrav return r; 696*47dd1d1bSDag-Erling Smørgrav } 697*47dd1d1bSDag-Erling Smørgrav } 698*47dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_put_u32(m, n)) != 0 || 699*47dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_stringb(m, b)) != 0) { 700*47dd1d1bSDag-Erling Smørgrav sshbuf_free(b); 701*47dd1d1bSDag-Erling Smørgrav return r; 702*47dd1d1bSDag-Erling Smørgrav } 703*47dd1d1bSDag-Erling Smørgrav /* success */ 704e2f6069cSDag-Erling Smørgrav return 0; 705b15c8340SDag-Erling Smørgrav } 706b15c8340SDag-Erling Smørgrav 707*47dd1d1bSDag-Erling Smørgrav static int 708*47dd1d1bSDag-Erling Smørgrav deserialise_array(struct sshbuf *m, char ***ap, size_t *np) 709*47dd1d1bSDag-Erling Smørgrav { 710*47dd1d1bSDag-Erling Smørgrav char **a = NULL; 711*47dd1d1bSDag-Erling Smørgrav size_t i, n = 0; 712*47dd1d1bSDag-Erling Smørgrav struct sshbuf *b = NULL; 713*47dd1d1bSDag-Erling Smørgrav u_int tmp; 714*47dd1d1bSDag-Erling Smørgrav int r = SSH_ERR_INTERNAL_ERROR; 715*47dd1d1bSDag-Erling Smørgrav 716*47dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_get_u32(m, &tmp)) != 0 || 717*47dd1d1bSDag-Erling Smørgrav (r = sshbuf_froms(m, &b)) != 0) 718*47dd1d1bSDag-Erling Smørgrav goto out; 719*47dd1d1bSDag-Erling Smørgrav if (tmp > INT_MAX) { 720*47dd1d1bSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 721*47dd1d1bSDag-Erling Smørgrav goto out; 722*47dd1d1bSDag-Erling Smørgrav } 723*47dd1d1bSDag-Erling Smørgrav n = tmp; 724*47dd1d1bSDag-Erling Smørgrav if (n > 0 && (a = calloc(n, sizeof(*a))) == NULL) { 725*47dd1d1bSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 726*47dd1d1bSDag-Erling Smørgrav goto out; 727*47dd1d1bSDag-Erling Smørgrav } 728*47dd1d1bSDag-Erling Smørgrav for (i = 0; i < n; i++) { 729*47dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(b, &a[i], NULL)) != 0) 730*47dd1d1bSDag-Erling Smørgrav goto out; 731*47dd1d1bSDag-Erling Smørgrav } 732*47dd1d1bSDag-Erling Smørgrav /* success */ 733*47dd1d1bSDag-Erling Smørgrav r = 0; 734*47dd1d1bSDag-Erling Smørgrav *ap = a; 735*47dd1d1bSDag-Erling Smørgrav a = NULL; 736*47dd1d1bSDag-Erling Smørgrav *np = n; 737*47dd1d1bSDag-Erling Smørgrav n = 0; 738*47dd1d1bSDag-Erling Smørgrav out: 739*47dd1d1bSDag-Erling Smørgrav for (i = 0; i < n; i++) 740*47dd1d1bSDag-Erling Smørgrav free(a[i]); 741*47dd1d1bSDag-Erling Smørgrav free(a); 742*47dd1d1bSDag-Erling Smørgrav sshbuf_free(b); 743*47dd1d1bSDag-Erling Smørgrav return r; 744*47dd1d1bSDag-Erling Smørgrav } 745*47dd1d1bSDag-Erling Smørgrav 746*47dd1d1bSDag-Erling Smørgrav static int 747*47dd1d1bSDag-Erling Smørgrav serialise_nullable_string(struct sshbuf *m, const char *s) 748*47dd1d1bSDag-Erling Smørgrav { 749*47dd1d1bSDag-Erling Smørgrav int r; 750*47dd1d1bSDag-Erling Smørgrav 751*47dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_put_u8(m, s == NULL)) != 0 || 752*47dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_cstring(m, s)) != 0) 753*47dd1d1bSDag-Erling Smørgrav return r; 754*47dd1d1bSDag-Erling Smørgrav return 0; 755*47dd1d1bSDag-Erling Smørgrav } 756*47dd1d1bSDag-Erling Smørgrav 757*47dd1d1bSDag-Erling Smørgrav static int 758*47dd1d1bSDag-Erling Smørgrav deserialise_nullable_string(struct sshbuf *m, char **sp) 759*47dd1d1bSDag-Erling Smørgrav { 760*47dd1d1bSDag-Erling Smørgrav int r; 761*47dd1d1bSDag-Erling Smørgrav u_char flag; 762*47dd1d1bSDag-Erling Smørgrav 763*47dd1d1bSDag-Erling Smørgrav *sp = NULL; 764*47dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_get_u8(m, &flag)) != 0 || 765*47dd1d1bSDag-Erling Smørgrav (r = sshbuf_get_cstring(m, flag ? NULL : sp, NULL)) != 0) 766*47dd1d1bSDag-Erling Smørgrav return r; 767*47dd1d1bSDag-Erling Smørgrav return 0; 768*47dd1d1bSDag-Erling Smørgrav } 769*47dd1d1bSDag-Erling Smørgrav 770*47dd1d1bSDag-Erling Smørgrav int 771*47dd1d1bSDag-Erling Smørgrav sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m, 772*47dd1d1bSDag-Erling Smørgrav int untrusted) 773*47dd1d1bSDag-Erling Smørgrav { 774*47dd1d1bSDag-Erling Smørgrav int r = SSH_ERR_INTERNAL_ERROR; 775*47dd1d1bSDag-Erling Smørgrav 776*47dd1d1bSDag-Erling Smørgrav /* Flag and simple integer options */ 777*47dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_put_u8(m, opts->permit_port_forwarding_flag)) != 0 || 778*47dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u8(m, opts->permit_agent_forwarding_flag)) != 0 || 779*47dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u8(m, opts->permit_x11_forwarding_flag)) != 0 || 780*47dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u8(m, opts->permit_pty_flag)) != 0 || 781*47dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u8(m, opts->permit_user_rc)) != 0 || 782*47dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u8(m, opts->restricted)) != 0 || 783*47dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u8(m, opts->cert_authority)) != 0 || 784*47dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u64(m, opts->valid_before)) != 0) 785*47dd1d1bSDag-Erling Smørgrav return r; 786*47dd1d1bSDag-Erling Smørgrav 787*47dd1d1bSDag-Erling Smørgrav /* tunnel number can be negative to indicate "unset" */ 788*47dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_put_u8(m, opts->force_tun_device == -1)) != 0 || 789*47dd1d1bSDag-Erling Smørgrav (r = sshbuf_put_u32(m, (opts->force_tun_device < 0) ? 790*47dd1d1bSDag-Erling Smørgrav 0 : (u_int)opts->force_tun_device)) != 0) 791*47dd1d1bSDag-Erling Smørgrav return r; 792*47dd1d1bSDag-Erling Smørgrav 793*47dd1d1bSDag-Erling Smørgrav /* String options; these may be NULL */ 794*47dd1d1bSDag-Erling Smørgrav if ((r = serialise_nullable_string(m, 795*47dd1d1bSDag-Erling Smørgrav untrusted ? "yes" : opts->cert_principals)) != 0 || 796*47dd1d1bSDag-Erling Smørgrav (r = serialise_nullable_string(m, 797*47dd1d1bSDag-Erling Smørgrav untrusted ? "true" : opts->force_command)) != 0 || 798*47dd1d1bSDag-Erling Smørgrav (r = serialise_nullable_string(m, 799*47dd1d1bSDag-Erling Smørgrav untrusted ? NULL : opts->required_from_host_cert)) != 0 || 800*47dd1d1bSDag-Erling Smørgrav (r = serialise_nullable_string(m, 801*47dd1d1bSDag-Erling Smørgrav untrusted ? NULL : opts->required_from_host_keys)) != 0) 802*47dd1d1bSDag-Erling Smørgrav return r; 803*47dd1d1bSDag-Erling Smørgrav 804*47dd1d1bSDag-Erling Smørgrav /* Array options */ 805*47dd1d1bSDag-Erling Smørgrav if ((r = serialise_array(m, opts->env, 806*47dd1d1bSDag-Erling Smørgrav untrusted ? 0 : opts->nenv)) != 0 || 807*47dd1d1bSDag-Erling Smørgrav (r = serialise_array(m, opts->permitopen, 808*47dd1d1bSDag-Erling Smørgrav untrusted ? 0 : opts->npermitopen)) != 0) 809*47dd1d1bSDag-Erling Smørgrav return r; 810*47dd1d1bSDag-Erling Smørgrav 811*47dd1d1bSDag-Erling Smørgrav /* success */ 812*47dd1d1bSDag-Erling Smørgrav return 0; 813*47dd1d1bSDag-Erling Smørgrav } 814*47dd1d1bSDag-Erling Smørgrav 815*47dd1d1bSDag-Erling Smørgrav int 816*47dd1d1bSDag-Erling Smørgrav sshauthopt_deserialise(struct sshbuf *m, struct sshauthopt **optsp) 817*47dd1d1bSDag-Erling Smørgrav { 818*47dd1d1bSDag-Erling Smørgrav struct sshauthopt *opts = NULL; 819*47dd1d1bSDag-Erling Smørgrav int r = SSH_ERR_INTERNAL_ERROR; 820*47dd1d1bSDag-Erling Smørgrav u_char f; 821*47dd1d1bSDag-Erling Smørgrav u_int tmp; 822*47dd1d1bSDag-Erling Smørgrav 823*47dd1d1bSDag-Erling Smørgrav if ((opts = calloc(1, sizeof(*opts))) == NULL) 824*47dd1d1bSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 825*47dd1d1bSDag-Erling Smørgrav 826*47dd1d1bSDag-Erling Smørgrav #define OPT_FLAG(x) \ 827*47dd1d1bSDag-Erling Smørgrav do { \ 828*47dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_get_u8(m, &f)) != 0) \ 829*47dd1d1bSDag-Erling Smørgrav goto out; \ 830*47dd1d1bSDag-Erling Smørgrav opts->x = f; \ 831*47dd1d1bSDag-Erling Smørgrav } while (0) 832*47dd1d1bSDag-Erling Smørgrav OPT_FLAG(permit_port_forwarding_flag); 833*47dd1d1bSDag-Erling Smørgrav OPT_FLAG(permit_agent_forwarding_flag); 834*47dd1d1bSDag-Erling Smørgrav OPT_FLAG(permit_x11_forwarding_flag); 835*47dd1d1bSDag-Erling Smørgrav OPT_FLAG(permit_pty_flag); 836*47dd1d1bSDag-Erling Smørgrav OPT_FLAG(permit_user_rc); 837*47dd1d1bSDag-Erling Smørgrav OPT_FLAG(restricted); 838*47dd1d1bSDag-Erling Smørgrav OPT_FLAG(cert_authority); 839*47dd1d1bSDag-Erling Smørgrav #undef OPT_FLAG 840*47dd1d1bSDag-Erling Smørgrav 841*47dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_get_u64(m, &opts->valid_before)) != 0) 842*47dd1d1bSDag-Erling Smørgrav goto out; 843*47dd1d1bSDag-Erling Smørgrav 844*47dd1d1bSDag-Erling Smørgrav /* tunnel number can be negative to indicate "unset" */ 845*47dd1d1bSDag-Erling Smørgrav if ((r = sshbuf_get_u8(m, &f)) != 0 || 846*47dd1d1bSDag-Erling Smørgrav (r = sshbuf_get_u32(m, &tmp)) != 0) 847*47dd1d1bSDag-Erling Smørgrav goto out; 848*47dd1d1bSDag-Erling Smørgrav opts->force_tun_device = f ? -1 : (int)tmp; 849*47dd1d1bSDag-Erling Smørgrav 850*47dd1d1bSDag-Erling Smørgrav /* String options may be NULL */ 851*47dd1d1bSDag-Erling Smørgrav if ((r = deserialise_nullable_string(m, &opts->cert_principals)) != 0 || 852*47dd1d1bSDag-Erling Smørgrav (r = deserialise_nullable_string(m, &opts->force_command)) != 0 || 853*47dd1d1bSDag-Erling Smørgrav (r = deserialise_nullable_string(m, 854*47dd1d1bSDag-Erling Smørgrav &opts->required_from_host_cert)) != 0 || 855*47dd1d1bSDag-Erling Smørgrav (r = deserialise_nullable_string(m, 856*47dd1d1bSDag-Erling Smørgrav &opts->required_from_host_keys)) != 0) 857*47dd1d1bSDag-Erling Smørgrav goto out; 858*47dd1d1bSDag-Erling Smørgrav 859*47dd1d1bSDag-Erling Smørgrav /* Array options */ 860*47dd1d1bSDag-Erling Smørgrav if ((r = deserialise_array(m, &opts->env, &opts->nenv)) != 0 || 861*47dd1d1bSDag-Erling Smørgrav (r = deserialise_array(m, 862*47dd1d1bSDag-Erling Smørgrav &opts->permitopen, &opts->npermitopen)) != 0) 863*47dd1d1bSDag-Erling Smørgrav goto out; 864*47dd1d1bSDag-Erling Smørgrav 865*47dd1d1bSDag-Erling Smørgrav /* success */ 866*47dd1d1bSDag-Erling Smørgrav r = 0; 867*47dd1d1bSDag-Erling Smørgrav *optsp = opts; 868*47dd1d1bSDag-Erling Smørgrav opts = NULL; 869*47dd1d1bSDag-Erling Smørgrav out: 870*47dd1d1bSDag-Erling Smørgrav sshauthopt_free(opts); 871*47dd1d1bSDag-Erling Smørgrav return r; 872*47dd1d1bSDag-Erling Smørgrav } 873