upstream: Make with config keywords support which

percent_expansions more consistent.  - %C is moved into its own function and
added to Match Exec.  - move the common (global) options into a macro.  This
is ugly but it's    the least-ugly way I could come up with.  - move
IdentityAgent and ForwardAgent percent expansion to before the    config dump
to make it regression-testable.  - document all of the above

ok jmc@ for man page bits, "makes things less terrible" djm@ for the rest.

OpenBSD-Commit-ID: 4b65664bd6d8ae2a9afaf1a2438ddd1b614b1d75
This commit is contained in:
dtucker@openbsd.org 2020-04-03 02:27:12 +00:00 committed by Damien Miller
parent 6ec7457171
commit ed833da176
4 changed files with 92 additions and 88 deletions

View File

@ -1,4 +1,4 @@
/* $OpenBSD: readconf.c,v 1.326 2020/02/06 22:46:31 djm Exp $ */ /* $OpenBSD: readconf.c,v 1.327 2020/04/03 02:27:12 dtucker Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -324,6 +324,24 @@ kex_default_pk_alg(void)
return kex_default_pk_alg_filtered; return kex_default_pk_alg_filtered;
} }
char *
ssh_connection_hash(const char *thishost, const char *host, const char *portstr,
const char *user)
{
struct ssh_digest_ctx *md;
u_char conn_hash[SSH_DIGEST_MAX_LENGTH];
if ((md = ssh_digest_start(SSH_DIGEST_SHA1)) == NULL ||
ssh_digest_update(md, thishost, strlen(thishost)) < 0 ||
ssh_digest_update(md, host, strlen(host)) < 0 ||
ssh_digest_update(md, portstr, strlen(portstr)) < 0 ||
ssh_digest_update(md, user, strlen(user)) < 0 ||
ssh_digest_final(md, conn_hash, sizeof(conn_hash)) < 0)
fatal("%s: mux digest failed", __func__);
ssh_digest_free(md);
return tohex(conn_hash, ssh_digest_bytes(SSH_DIGEST_SHA1));
}
/* /*
* Adds a local TCP/IP port forward to options. Never returns if there is an * Adds a local TCP/IP port forward to options. Never returns if there is an
* error. * error.
@ -646,6 +664,8 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
if (r == (negate ? 1 : 0)) if (r == (negate ? 1 : 0))
this_result = result = 0; this_result = result = 0;
} else if (strcasecmp(attrib, "exec") == 0) { } else if (strcasecmp(attrib, "exec") == 0) {
char *conn_hash_hex;
if (gethostname(thishost, sizeof(thishost)) == -1) if (gethostname(thishost, sizeof(thishost)) == -1)
fatal("gethostname: %s", strerror(errno)); fatal("gethostname: %s", strerror(errno));
strlcpy(shorthost, thishost, sizeof(shorthost)); strlcpy(shorthost, thishost, sizeof(shorthost));
@ -653,8 +673,11 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
snprintf(portstr, sizeof(portstr), "%d", port); snprintf(portstr, sizeof(portstr), "%d", port);
snprintf(uidstr, sizeof(uidstr), "%llu", snprintf(uidstr, sizeof(uidstr), "%llu",
(unsigned long long)pw->pw_uid); (unsigned long long)pw->pw_uid);
conn_hash_hex = ssh_connection_hash(thishost, host,
portstr, pw->pw_name);
cmd = percent_expand(arg, cmd = percent_expand(arg,
"C", conn_hash_hex,
"L", shorthost, "L", shorthost,
"d", pw->pw_dir, "d", pw->pw_dir,
"h", host, "h", host,
@ -665,6 +688,7 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
"u", pw->pw_name, "u", pw->pw_name,
"i", uidstr, "i", uidstr,
(char *)NULL); (char *)NULL);
free(conn_hash_hex);
if (result != 1) { if (result != 1) {
/* skip execution if prior predicate failed */ /* skip execution if prior predicate failed */
debug3("%.200s line %d: skipped exec " debug3("%.200s line %d: skipped exec "

View File

@ -1,4 +1,4 @@
/* $OpenBSD: readconf.h,v 1.132 2020/01/23 02:46:49 dtucker Exp $ */ /* $OpenBSD: readconf.h,v 1.133 2020/04/03 02:27:12 dtucker Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -200,6 +200,8 @@ typedef struct {
#define SSH_STRICT_HOSTKEY_ASK 3 #define SSH_STRICT_HOSTKEY_ASK 3
const char *kex_default_pk_alg(void); const char *kex_default_pk_alg(void);
char *ssh_connection_hash(const char *thishost, const char *host,
const char *portstr, const char *user);
void initialize_options(Options *); void initialize_options(Options *);
void fill_default_options(Options *); void fill_default_options(Options *);
void fill_default_options_for_canonicalization(Options *); void fill_default_options_for_canonicalization(Options *);

120
ssh.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh.c,v 1.521 2020/03/06 18:20:02 markus Exp $ */ /* $OpenBSD: ssh.c,v 1.522 2020/04/03 02:27:12 dtucker Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -86,7 +86,6 @@
#include "canohost.h" #include "canohost.h"
#include "compat.h" #include "compat.h"
#include "cipher.h" #include "cipher.h"
#include "digest.h"
#include "packet.h" #include "packet.h"
#include "sshbuf.h" #include "sshbuf.h"
#include "channels.h" #include "channels.h"
@ -177,6 +176,13 @@ char *forward_agent_sock_path = NULL;
/* Various strings used to to percent_expand() arguments */ /* Various strings used to to percent_expand() arguments */
static char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; static char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
static char uidstr[32], *host_arg, *conn_hash_hex; static char uidstr[32], *host_arg, *conn_hash_hex;
#define DEFAULT_CLIENT_PERCENT_EXPAND_ARGS \
"C", conn_hash_hex, \
"L", shorthost, \
"i", uidstr, \
"l", thishost, \
"n", host_arg, \
"p", portstr
/* socket address the host resolves to */ /* socket address the host resolves to */
struct sockaddr_storage hostaddr; struct sockaddr_storage hostaddr;
@ -601,8 +607,6 @@ main(int ac, char **av)
extern char *optarg; extern char *optarg;
struct Forward fwd; struct Forward fwd;
struct addrinfo *addrs = NULL; struct addrinfo *addrs = NULL;
struct ssh_digest_ctx *md;
u_char conn_hash[SSH_DIGEST_MAX_LENGTH];
size_t n, len; size_t n, len;
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
@ -1330,15 +1334,8 @@ main(int ac, char **av)
snprintf(uidstr, sizeof(uidstr), "%llu", snprintf(uidstr, sizeof(uidstr), "%llu",
(unsigned long long)pw->pw_uid); (unsigned long long)pw->pw_uid);
if ((md = ssh_digest_start(SSH_DIGEST_SHA1)) == NULL || conn_hash_hex = ssh_connection_hash(thishost, host, portstr,
ssh_digest_update(md, thishost, strlen(thishost)) < 0 || options.user);
ssh_digest_update(md, host, strlen(host)) < 0 ||
ssh_digest_update(md, portstr, strlen(portstr)) < 0 ||
ssh_digest_update(md, options.user, strlen(options.user)) < 0 ||
ssh_digest_final(md, conn_hash, sizeof(conn_hash)) < 0)
fatal("%s: mux digest failed", __func__);
ssh_digest_free(md);
conn_hash_hex = tohex(conn_hash, ssh_digest_bytes(SSH_DIGEST_SHA1));
/* /*
* Expand tokens in arguments. NB. LocalCommand is expanded later, * Expand tokens in arguments. NB. LocalCommand is expanded later,
@ -1349,14 +1346,9 @@ main(int ac, char **av)
debug3("expanding RemoteCommand: %s", options.remote_command); debug3("expanding RemoteCommand: %s", options.remote_command);
cp = options.remote_command; cp = options.remote_command;
options.remote_command = percent_expand(cp, options.remote_command = percent_expand(cp,
"C", conn_hash_hex, DEFAULT_CLIENT_PERCENT_EXPAND_ARGS,
"L", shorthost,
"d", pw->pw_dir, "d", pw->pw_dir,
"h", host, "h", host,
"i", uidstr,
"l", thishost,
"n", host_arg,
"p", portstr,
"r", options.user, "r", options.user,
"u", pw->pw_name, "u", pw->pw_name,
(char *)NULL); (char *)NULL);
@ -1371,20 +1363,44 @@ main(int ac, char **av)
cp = tilde_expand_filename(options.control_path, getuid()); cp = tilde_expand_filename(options.control_path, getuid());
free(options.control_path); free(options.control_path);
options.control_path = percent_expand(cp, options.control_path = percent_expand(cp,
"C", conn_hash_hex, DEFAULT_CLIENT_PERCENT_EXPAND_ARGS,
"L", shorthost, "d", pw->pw_dir,
"h", host, "h", host,
"i", uidstr,
"l", thishost,
"n", host_arg,
"p", portstr,
"r", options.user, "r", options.user,
"u", pw->pw_name, "u", pw->pw_name,
"i", uidstr,
(char *)NULL); (char *)NULL);
free(cp); free(cp);
} }
if (options.identity_agent != NULL) {
p = tilde_expand_filename(options.identity_agent, getuid());
cp = percent_expand(p,
DEFAULT_CLIENT_PERCENT_EXPAND_ARGS,
"d", pw->pw_dir,
"h", host,
"r", options.user,
"u", pw->pw_name,
(char *)NULL);
free(p);
free(options.identity_agent);
options.identity_agent = cp;
}
if (options.forward_agent_sock_path != NULL) {
p = tilde_expand_filename(options.forward_agent_sock_path,
getuid());
cp = percent_expand(p,
DEFAULT_CLIENT_PERCENT_EXPAND_ARGS,
"d", pw->pw_dir,
"h", host,
"r", options.user,
"u", pw->pw_name,
(char *)NULL);
free(p);
free(options.forward_agent_sock_path);
options.forward_agent_sock_path = cp;
}
if (config_test) { if (config_test) {
dump_client_config(&options, host); dump_client_config(&options, host);
exit(0); exit(0);
@ -1509,23 +1525,7 @@ main(int ac, char **av)
if (strcmp(options.identity_agent, "none") == 0) { if (strcmp(options.identity_agent, "none") == 0) {
unsetenv(SSH_AUTHSOCKET_ENV_NAME); unsetenv(SSH_AUTHSOCKET_ENV_NAME);
} else { } else {
p = tilde_expand_filename(options.identity_agent, cp = options.identity_agent;
getuid());
cp = percent_expand(p,
"d", pw->pw_dir,
"h", host,
"i", uidstr,
"l", thishost,
"r", options.user,
"u", pw->pw_name,
(char *)NULL);
free(p);
/*
* If identity_agent represents an environment variable
* then recheck that it is valid (since processing with
* percent_expand() may have changed it) and substitute
* its value.
*/
if (cp[0] == '$') { if (cp[0] == '$') {
if (!valid_env_name(cp + 1)) { if (!valid_env_name(cp + 1)) {
fatal("Invalid IdentityAgent " fatal("Invalid IdentityAgent "
@ -1539,22 +1539,10 @@ main(int ac, char **av)
/* identity_agent specifies a path directly */ /* identity_agent specifies a path directly */
setenv(SSH_AUTHSOCKET_ENV_NAME, cp, 1); setenv(SSH_AUTHSOCKET_ENV_NAME, cp, 1);
} }
free(cp);
} }
} }
if (options.forward_agent && (options.forward_agent_sock_path != NULL)) { if (options.forward_agent && options.forward_agent_sock_path != NULL) {
p = tilde_expand_filename(options.forward_agent_sock_path, getuid());
cp = percent_expand(p,
"d", pw->pw_dir,
"h", host,
"i", uidstr,
"l", thishost,
"r", options.user,
"u", pw->pw_name,
(char *)NULL);
free(p);
if (cp[0] == '$') { if (cp[0] == '$') {
if (!valid_env_name(cp + 1)) { if (!valid_env_name(cp + 1)) {
fatal("Invalid ForwardAgent environment variable name %s", cp); fatal("Invalid ForwardAgent environment variable name %s", cp);
@ -1979,14 +1967,9 @@ ssh_session2(struct ssh *ssh, struct passwd *pw)
debug3("expanding LocalCommand: %s", options.local_command); debug3("expanding LocalCommand: %s", options.local_command);
cp = options.local_command; cp = options.local_command;
options.local_command = percent_expand(cp, options.local_command = percent_expand(cp,
"C", conn_hash_hex, DEFAULT_CLIENT_PERCENT_EXPAND_ARGS,
"L", shorthost,
"d", pw->pw_dir, "d", pw->pw_dir,
"h", host, "h", host,
"i", uidstr,
"l", thishost,
"n", host_arg,
"p", portstr,
"r", options.user, "r", options.user,
"u", pw->pw_name, "u", pw->pw_name,
"T", tun_fwd_ifname == NULL ? "NONE" : tun_fwd_ifname, "T", tun_fwd_ifname == NULL ? "NONE" : tun_fwd_ifname,
@ -2143,9 +2126,13 @@ load_public_identity_files(struct passwd *pw)
continue; continue;
} }
cp = tilde_expand_filename(options.identity_files[i], getuid()); cp = tilde_expand_filename(options.identity_files[i], getuid());
filename = percent_expand(cp, "d", pw->pw_dir, filename = percent_expand(cp,
"u", pw->pw_name, "l", thishost, "h", host, DEFAULT_CLIENT_PERCENT_EXPAND_ARGS,
"r", options.user, (char *)NULL); "d", pw->pw_dir,
"h", host,
"r", options.user,
"u", pw->pw_name,
(char *)NULL);
free(cp); free(cp);
check_load(sshkey_load_public(filename, &public, NULL), check_load(sshkey_load_public(filename, &public, NULL),
filename, "pubkey"); filename, "pubkey");
@ -2195,10 +2182,9 @@ load_public_identity_files(struct passwd *pw)
cp = tilde_expand_filename(options.certificate_files[i], cp = tilde_expand_filename(options.certificate_files[i],
getuid()); getuid());
filename = percent_expand(cp, filename = percent_expand(cp,
DEFAULT_CLIENT_PERCENT_EXPAND_ARGS,
"d", pw->pw_dir, "d", pw->pw_dir,
"h", host, "h", host,
"i", uidstr,
"l", thishost,
"r", options.user, "r", options.user,
"u", pw->pw_name, "u", pw->pw_name,
(char *)NULL); (char *)NULL);

View File

@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\" .\"
.\" $OpenBSD: ssh_config.5,v 1.322 2020/02/07 03:54:44 dtucker Exp $ .\" $OpenBSD: ssh_config.5,v 1.323 2020/04/03 02:27:12 dtucker Exp $
.Dd $Mdocdate: February 7 2020 $ .Dd $Mdocdate: April 3 2020 $
.Dt SSH_CONFIG 5 .Dt SSH_CONFIG 5
.Os .Os
.Sh NAME .Sh NAME
@ -1845,31 +1845,23 @@ otherwise.
The local username. The local username.
.El .El
.Pp .Pp
.Cm Match exec .Cm Match exec ,
accepts the tokens %%, %h, %i, %L, %l, %n, %p, %r, and %u. .Cm CertificateFile ,
.Pp .Cm ControlPath ,
.Cm CertificateFile .Cm IdentityAgent ,
accepts the tokens %%, %d, %h, %i, %l, %r, and %u. .Cm IdentityFile ,
.Pp and
.Cm ControlPath .Cm RemoteCommand
accepts the tokens %%, %C, %h, %i, %L, %l, %n, %p, %r, and %u. accept the tokens %%, %C, %d, %h, %i, %L, %l, %n, %p, %r, and %u.
.Pp .Pp
.Cm Hostname .Cm Hostname
accepts the tokens %% and %h. accepts the tokens %% and %h.
.Pp .Pp
.Cm IdentityAgent
and
.Cm IdentityFile
accept the tokens %%, %d, %h, %i, %l, %r, and %u.
.Pp
.Cm LocalCommand .Cm LocalCommand
accepts the tokens %%, %C, %d, %h, %i, %l, %n, %p, %r, %T, and %u. accepts all tokens.
.Pp .Pp
.Cm ProxyCommand .Cm ProxyCommand
accepts the tokens %%, %h, %n, %p, and %r. accepts the tokens %%, %h, %n, %p, and %r.
.Pp
.Cm RemoteCommand
accepts the tokens %%, %C, %d, %h, %i, %l, %n, %p, %r, and %u.
.Sh FILES .Sh FILES
.Bl -tag -width Ds .Bl -tag -width Ds
.It Pa ~/.ssh/config .It Pa ~/.ssh/config