diff --git a/auth-options.c b/auth-options.c index ef57ebf43..c55c3ed06 100644 --- a/auth-options.c +++ b/auth-options.c @@ -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 * @@ -283,6 +283,10 @@ sshauthopt_free(struct sshauthopt *opts) 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); } @@ -304,10 +308,70 @@ sshauthopt_new_with_keys_defaults(void) 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"; @@ -410,48 +474,13 @@ sshauthopt_parse(const char *opts, const char **errstrp) } 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; @@ -554,7 +583,10 @@ sshauthopt_merge(const struct sshauthopt *primary, 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; @@ -577,6 +609,16 @@ sshauthopt_merge(const struct sshauthopt *primary, 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); @@ -669,7 +711,9 @@ sshauthopt_copy(const struct sshauthopt *orig) 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; } @@ -805,7 +849,9 @@ sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m, 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 */ @@ -859,7 +905,9 @@ sshauthopt_deserialise(struct sshbuf *m, struct sshauthopt **optsp) /* 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 */ diff --git a/auth-options.h b/auth-options.h index bf59b30be..0462983b5 100644 --- a/auth-options.h +++ b/auth-options.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-options.h,v 1.26 2018/03/12 00:52:01 djm Exp $ */ +/* $OpenBSD: auth-options.h,v 1.27 2018/06/06 18:23:32 djm Exp $ */ /* * Copyright (c) 2018 Damien Miller @@ -55,6 +55,10 @@ struct sshauthopt { size_t npermitopen; char **permitopen; + /* Permitted listens (remote forwarding) */ + size_t npermitlisten; + char **permitlisten; + /* * Permitted host/addresses (comma-separated) * Caller must check source address matches both lists (if present). diff --git a/auth.c b/auth.c index 573cd03b0..0424f1f79 100644 --- a/auth.c +++ b/auth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.129 2018/06/01 03:33:53 djm Exp $ */ +/* $OpenBSD: auth.c,v 1.130 2018/06/06 18:23:32 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -1005,17 +1005,20 @@ auth_log_authopts(const char *loc, const struct sshauthopt *opts, int do_remote) int do_env = options.permit_user_env && opts->nenv > 0; int do_permitopen = opts->npermitopen > 0 && (options.allow_tcp_forwarding & FORWARD_LOCAL) != 0; + int do_permitlisten = opts->npermitlisten > 0 && + (options.allow_tcp_forwarding & FORWARD_REMOTE) != 0; size_t i; char msg[1024], buf[64]; snprintf(buf, sizeof(buf), "%d", opts->force_tun_device); /* Try to keep this alphabetically sorted */ - snprintf(msg, sizeof(msg), "key options:%s%s%s%s%s%s%s%s%s%s%s%s", + snprintf(msg, sizeof(msg), "key options:%s%s%s%s%s%s%s%s%s%s%s%s%s", opts->permit_agent_forwarding_flag ? " agent-forwarding" : "", opts->force_command == NULL ? "" : " command", do_env ? " environment" : "", opts->valid_before == 0 ? "" : "expires", do_permitopen ? " permitopen" : "", + do_permitlisten ? " permitlisten" : "", opts->permit_port_forwarding_flag ? " port-forwarding" : "", opts->cert_principals == NULL ? "" : " principals", opts->permit_pty_flag ? " pty" : "", @@ -1049,12 +1052,18 @@ auth_log_authopts(const char *loc, const struct sshauthopt *opts, int do_remote) } if (opts->force_command != NULL) debug("%s: forced command: \"%s\"", loc, opts->force_command); - if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) { + if (do_permitopen) { for (i = 0; i < opts->npermitopen; i++) { debug("%s: permitted open: %s", loc, opts->permitopen[i]); } } + if (do_permitlisten) { + for (i = 0; i < opts->npermitlisten; i++) { + debug("%s: permitted listen: %s", + loc, opts->permitlisten[i]); + } + } } /* Activate a new set of key/cert options; merging with what is there. */ diff --git a/servconf.c b/servconf.c index b75faf3f8..3c41490b3 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.329 2018/06/06 18:22:41 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.330 2018/06/06 18:23:32 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -160,7 +160,7 @@ initialize_server_options(ServerOptions *options) options->num_accept_env = 0; options->permit_tun = -1; options->permitted_opens = NULL; - options->permitted_remote_opens = NULL; + options->permitted_listens = NULL; options->adm_forced_command = NULL; options->chroot_directory = NULL; options->authorized_keys_command = NULL; @@ -463,7 +463,7 @@ typedef enum { sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, sAcceptEnv, sPermitTunnel, - sMatch, sPermitOpen, sPermitRemoteOpen, sForceCommand, sChrootDirectory, + sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, sUsePrivilegeSeparation, sAllowAgentForwarding, sHostCertificate, sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, @@ -598,7 +598,7 @@ static struct { { "permituserrc", sPermitUserRC, SSHCFG_ALL }, { "match", sMatch, SSHCFG_ALL }, { "permitopen", sPermitOpen, SSHCFG_ALL }, - { "permitremoteopen", sPermitRemoteOpen, SSHCFG_ALL }, + { "permitlisten", sPermitListen, SSHCFG_ALL }, { "forcecommand", sForceCommand, SSHCFG_ALL }, { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL }, @@ -878,9 +878,9 @@ process_permitopen(struct ssh *ssh, ServerOptions *options) { process_permitopen_list(ssh, sPermitOpen, options->permitted_opens, options->num_permitted_opens); - process_permitopen_list(ssh, sPermitRemoteOpen, - options->permitted_remote_opens, - options->num_permitted_remote_opens); + process_permitopen_list(ssh, sPermitListen, + options->permitted_listens, + options->num_permitted_listens); } struct connection_info * @@ -1831,11 +1831,11 @@ process_server_config_line(ServerOptions *options, char *line, *activep = value; break; - case sPermitRemoteOpen: + case sPermitListen: case sPermitOpen: - if (opcode == sPermitRemoteOpen) { - uintptr = &options->num_permitted_remote_opens; - chararrayptr = &options->permitted_remote_opens; + if (opcode == sPermitListen) { + uintptr = &options->num_permitted_listens; + chararrayptr = &options->permitted_listens; } else { uintptr = &options->num_permitted_opens; chararrayptr = &options->permitted_opens; @@ -1857,7 +1857,7 @@ process_server_config_line(ServerOptions *options, char *line, for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) { arg2 = xstrdup(arg); p = hpdelim(&arg); - /* XXX support bare port number for PermitRemoteOpen */ + /* XXX support bare port number for PermitListen */ if (p == NULL) { fatal("%s line %d: missing host in %s", filename, linenum, @@ -2596,12 +2596,12 @@ dump_config(ServerOptions *o) printf(" %s", o->permitted_opens[i]); } printf("\n"); - printf("permitremoteopen"); - if (o->num_permitted_remote_opens == 0) + printf("permitlisten"); + if (o->num_permitted_listens == 0) printf(" any"); else { - for (i = 0; i < o->num_permitted_remote_opens; i++) - printf(" %s", o->permitted_remote_opens[i]); + for (i = 0; i < o->num_permitted_listens; i++) + printf(" %s", o->permitted_listens[i]); } printf("\n"); } diff --git a/servconf.h b/servconf.h index 62acd8938..450b94ec4 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.132 2018/06/06 18:22:41 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.133 2018/06/06 18:23:32 djm Exp $ */ /* * Author: Tatu Ylonen @@ -183,8 +183,8 @@ typedef struct { char **permitted_opens; /* May also be one of PERMITOPEN_* */ u_int num_permitted_opens; - char **permitted_remote_opens; /* May also be one of PERMITOPEN_* */ - u_int num_permitted_remote_opens; + char **permitted_listens; /* May also be one of PERMITOPEN_* */ + u_int num_permitted_listens; char *chroot_directory; char *revoked_keys_file; @@ -248,8 +248,7 @@ struct connection_info { M_CP_STRARRAYOPT(accept_env, num_accept_env); \ M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \ M_CP_STRARRAYOPT(permitted_opens, num_permitted_opens); \ - M_CP_STRARRAYOPT(permitted_remote_opens, \ - num_permitted_remote_opens); \ + M_CP_STRARRAYOPT(permitted_listens, num_permitted_listens); \ } while (0) struct connection_info *get_connection_info(int, int); diff --git a/session.c b/session.c index 3a3fd841a..e72fcb0a8 100644 --- a/session.c +++ b/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.296 2018/06/06 18:22:41 djm Exp $ */ +/* $OpenBSD: session.c,v 1.297 2018/06/06 18:23:32 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -290,27 +290,43 @@ prepare_auth_info_file(struct passwd *pw, struct sshbuf *info) } static void -set_permitopen_from_authopts(struct ssh *ssh, const struct sshauthopt *opts) +set_fwdpermit_from_authopts(struct ssh *ssh, const struct sshauthopt *opts) { char *tmp, *cp, *host; int port; size_t i; - if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) - return; - channel_clear_permission(ssh, FORWARD_USER, FORWARD_LOCAL); - for (i = 0; i < auth_opts->npermitopen; i++) { - tmp = cp = xstrdup(auth_opts->permitopen[i]); - /* This shouldn't fail as it has already been checked */ - if ((host = hpdelim(&cp)) == NULL) - fatal("%s: internal error: hpdelim", __func__); - host = cleanhostname(host); - if (cp == NULL || (port = permitopen_port(cp)) < 0) - fatal("%s: internal error: permitopen port", - __func__); - channel_add_permission(ssh, FORWARD_USER, FORWARD_LOCAL, - host, port); - free(tmp); + if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) { + channel_clear_permission(ssh, FORWARD_USER, FORWARD_LOCAL); + for (i = 0; i < auth_opts->npermitopen; i++) { + tmp = cp = xstrdup(auth_opts->permitopen[i]); + /* This shouldn't fail as it has already been checked */ + if ((host = hpdelim(&cp)) == NULL) + fatal("%s: internal error: hpdelim", __func__); + host = cleanhostname(host); + if (cp == NULL || (port = permitopen_port(cp)) < 0) + fatal("%s: internal error: permitopen port", + __func__); + channel_add_permission(ssh, + FORWARD_USER, FORWARD_LOCAL, host, port); + free(tmp); + } + } + if ((options.allow_tcp_forwarding & FORWARD_REMOTE) != 0) { + channel_clear_permission(ssh, FORWARD_USER, FORWARD_REMOTE); + for (i = 0; i < auth_opts->npermitlisten; i++) { + tmp = cp = xstrdup(auth_opts->permitlisten[i]); + /* This shouldn't fail as it has already been checked */ + if ((host = hpdelim(&cp)) == NULL) + fatal("%s: internal error: hpdelim", __func__); + host = cleanhostname(host); + if (cp == NULL || (port = permitopen_port(cp)) < 0) + fatal("%s: internal error: permitlisten port", + __func__); + channel_add_permission(ssh, + FORWARD_USER, FORWARD_REMOTE, host, port); + free(tmp); + } } } @@ -323,7 +339,7 @@ do_authenticated(struct ssh *ssh, Authctxt *authctxt) /* setup the channel layer */ /* XXX - streamlocal? */ - set_permitopen_from_authopts(ssh, auth_opts); + set_fwdpermit_from_authopts(ssh, auth_opts); if (!auth_opts->permit_port_forwarding_flag || options.disable_forwarding) {