upstream: permitlisten option for authorized_keys; ok markus@
OpenBSD-Commit-ID: 8650883018d7aa893173d703379e4456a222c672
diff --git a/auth-options.c b/auth-options.c
index ef57ebf..c55c3ed 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 <djm@mindrot.org>
*
@@ -283,6 +283,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);
}
@@ -304,10 +308,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";
@@ -410,48 +474,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;
@@ -554,7 +583,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;
@@ -577,6 +609,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);
@@ -669,7 +711,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;
}
@@ -805,7 +849,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 */
@@ -859,7 +905,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 */
diff --git a/auth-options.h b/auth-options.h
index bf59b30..0462983 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 <djm@mindrot.org>
@@ -55,6 +55,10 @@
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 573cd03..0424f1f 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 @@
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 @@
}
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 b75faf3..3c41490 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 <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@@ -160,7 +160,7 @@
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 @@
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 @@
{ "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_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 @@
*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 @@
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 @@
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 62acd89..450b94e 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 <ylo@cs.hut.fi>
@@ -183,8 +183,8 @@
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 @@
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 3a3fd84..e72fcb0 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 <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@@ -290,27 +290,43 @@
}
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 @@
/* 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) {