=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/auth-options.c,v retrieving revision 1.79 retrieving revision 1.80 diff -u -r1.79 -r1.80 --- src/usr.bin/ssh/auth-options.c 2018/04/06 04:15:45 1.79 +++ src/usr.bin/ssh/auth-options.c 2018/06/06 18:23:32 1.80 @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-options.c,v 1.79 2018/04/06 04:15:45 djm Exp $ */ +/* $OpenBSD: auth-options.c,v 1.80 2018/06/06 18:23:32 djm Exp $ */ /* * Copyright (c) 2018 Damien Miller * @@ -280,6 +280,10 @@ free(opts->permitopen[i]); free(opts->permitopen); + for (i = 0; i < opts->npermitlisten; i++) + free(opts->permitlisten[i]); + free(opts->permitlisten); + explicit_bzero(opts, sizeof(*opts)); free(opts); } @@ -301,10 +305,70 @@ return ret; } +/* + * Parse and record a permitopen/permitlisten directive. + * Return 0 on success. Return -1 on failure and sets *errstrp to error reason. + */ +static int +handle_permit(const char *opts, char ***permitsp, size_t *npermitsp, + const char **errstrp) +{ + char *opt, *tmp, *cp, *host, **permits = *permitsp; + size_t npermits = *npermitsp; + const char *errstr = "unknown error"; + + if (npermits > INT_MAX) { + *errstrp = "too many permission directives"; + return -1; + } + if ((opt = opt_dequote(&opts, &errstr)) == NULL) { + return -1; + } + if ((tmp = strdup(opt)) == NULL) { + free(opt); + *errstrp = "memory allocation failed"; + return -1; + } + cp = tmp; + /* validate syntax before recording it. */ + host = hpdelim(&cp); + if (host == NULL || strlen(host) >= NI_MAXHOST) { + free(tmp); + free(opt); + *errstrp = "invalid permission hostname"; + return -1; + } + /* + * don't want to use permitopen_port to avoid + * dependency on channels.[ch] here. + */ + if (cp == NULL || + (strcmp(cp, "*") != 0 && a2port(cp) <= 0)) { + free(tmp); + free(opt); + *errstrp = "invalid permission port"; + return -1; + } + /* XXX - add streamlocal support */ + free(tmp); + /* Record it */ + if ((permits = recallocarray(permits, npermits, npermits + 1, + sizeof(*permits))) == NULL) { + free(opt); + /* NB. don't update *permitsp if alloc fails */ + *errstrp = "memory allocation failed"; + return -1; + } + permits[npermits++] = opt; + *permitsp = permits; + *npermitsp = npermits; + return 0; +} + struct sshauthopt * sshauthopt_parse(const char *opts, const char **errstrp) { - char **oarray, *opt, *cp, *tmp, *host; + char **oarray, *opt, *cp, *tmp; int r; struct sshauthopt *ret = NULL; const char *errstr = "unknown error"; @@ -407,48 +471,13 @@ } ret->env[ret->nenv++] = opt; } else if (opt_match(&opts, "permitopen")) { - if (ret->npermitopen > INT_MAX) { - errstr = "too many permitopens"; + if (handle_permit(opts, &ret->permitopen, + &ret->npermitopen, &errstr) != 0) goto fail; - } - if ((opt = opt_dequote(&opts, &errstr)) == NULL) + } else if (opt_match(&opts, "permitlisten")) { + if (handle_permit(opts, &ret->permitlisten, + &ret->npermitlisten, &errstr) != 0) goto fail; - if ((tmp = strdup(opt)) == NULL) { - free(opt); - goto alloc_fail; - } - cp = tmp; - /* validate syntax of permitopen before recording it. */ - host = hpdelim(&cp); - if (host == NULL || strlen(host) >= NI_MAXHOST) { - free(tmp); - free(opt); - errstr = "invalid permitopen hostname"; - goto fail; - } - /* - * don't want to use permitopen_port to avoid - * dependency on channels.[ch] here. - */ - if (cp == NULL || - (strcmp(cp, "*") != 0 && a2port(cp) <= 0)) { - free(tmp); - free(opt); - errstr = "invalid permitopen port"; - goto fail; - } - /* XXX - add streamlocal support */ - free(tmp); - /* Record it */ - oarray = ret->permitopen; - if ((ret->permitopen = recallocarray(ret->permitopen, - ret->npermitopen, ret->npermitopen + 1, - sizeof(*ret->permitopen))) == NULL) { - free(opt); - ret->permitopen = oarray; - goto alloc_fail; - } - ret->permitopen[ret->npermitopen++] = opt; } else if (opt_match(&opts, "tunnel")) { if ((opt = opt_dequote(&opts, &errstr)) == NULL) goto fail; @@ -551,7 +580,10 @@ if (tmp != NULL && (ret->required_from_host_keys = strdup(tmp)) == NULL) goto alloc_fail; - /* force_tun_device, permitopen and environment prefer the primary. */ + /* + * force_tun_device, permitopen/permitlisten and environment all + * prefer the primary. + */ ret->force_tun_device = primary->force_tun_device; if (ret->force_tun_device == -1) ret->force_tun_device = additional->force_tun_device; @@ -574,6 +606,16 @@ goto alloc_fail; } + if (primary->npermitlisten > 0) { + if (dup_strings(&ret->permitlisten, &ret->npermitlisten, + primary->permitlisten, primary->npermitlisten) != 0) + goto alloc_fail; + } else if (additional->npermitlisten > 0) { + if (dup_strings(&ret->permitlisten, &ret->npermitlisten, + additional->permitlisten, additional->npermitlisten) != 0) + goto alloc_fail; + } + /* Flags are logical-AND (i.e. must be set in both for permission) */ #define OPTFLAG(x) ret->x = (primary->x == 1) && (additional->x == 1) OPTFLAG(permit_port_forwarding_flag); @@ -666,7 +708,9 @@ if (dup_strings(&ret->env, &ret->nenv, orig->env, orig->nenv) != 0 || dup_strings(&ret->permitopen, &ret->npermitopen, - orig->permitopen, orig->npermitopen) != 0) { + orig->permitopen, orig->npermitopen) != 0 || + dup_strings(&ret->permitlisten, &ret->npermitlisten, + orig->permitlisten, orig->npermitlisten) != 0) { sshauthopt_free(ret); return NULL; } @@ -802,7 +846,9 @@ if ((r = serialise_array(m, opts->env, untrusted ? 0 : opts->nenv)) != 0 || (r = serialise_array(m, opts->permitopen, - untrusted ? 0 : opts->npermitopen)) != 0) + untrusted ? 0 : opts->npermitopen)) || + (r = serialise_array(m, opts->permitlisten, + untrusted ? 0 : opts->npermitlisten)) != 0) return r; /* success */ @@ -856,7 +902,9 @@ /* Array options */ if ((r = deserialise_array(m, &opts->env, &opts->nenv)) != 0 || (r = deserialise_array(m, - &opts->permitopen, &opts->npermitopen)) != 0) + &opts->permitopen, &opts->npermitopen)) != 0 || + (r = deserialise_array(m, + &opts->permitlisten, &opts->npermitlisten)) != 0) goto out; /* success */