upstream: permitlisten option for authorized_keys; ok markus@

OpenBSD-Commit-ID: 8650883018d7aa893173d703379e4456a222c672
This commit is contained in:
djm@openbsd.org 2018-06-06 18:23:32 +00:00 коммит произвёл Damien Miller
Родитель 115063a664
Коммит 93c06ab6b7
6 изменённых файлов: 165 добавлений и 89 удалений

Просмотреть файл

@ -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 <djm@mindrot.org>
*
@ -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 */

Просмотреть файл

@ -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 <djm@mindrot.org>
@ -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).

15
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. */

Просмотреть файл

@ -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 <ylo@cs.hut.fi>, 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");
}

Просмотреть файл

@ -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 <ylo@cs.hut.fi>
@ -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);

Просмотреть файл

@ -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 <ylo@cs.hut.fi>, 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) {