Page MenuHome GnuPG

GnuPG support for ssh (envvars)

Authored By
werner
Jan 25 2021, 10:49 AM
Size
14 KB
Subscribers
None

GnuPG support for ssh (envvars)

From 3a9a092530396d66d91059efd3737f9dae5051be Mon Sep 17 00:00:00 2001
From: Werner Koch <wk@gnupg.org>
Date: Mon, 25 Jan 2021 10:30:04 +0100
Subject: [PATCH] Allow sending envrionment variables to the agent.
* authfd.c (put_one_env, ssh_send_agent_env): New.
* authfd.h (SSH_AGENTC_EXTENSION): New.
(SSH_AGENT_EXTENSION_FAILURE): New.
* readconf.h (Options): Add fields no_more_agent_env, num_agent_env,
agent_env.
* readconf.c (OpCodes, keywords): Add oAgentEnv and "agentenv".
(free_options): Move macro FREE_ARRAY to outer scope. Free agent_env.
(process_config_line_depth): Parse oAgentEnv.
(initialize_options): Reset AgentEnv stuff.
(dump_client_config): Dump AgentEnv.
* sshconnect.c (maybe_add_key_to_agent): Call ssh_send_agent_env.
* sshconnect2.c (authentication_socket): New.
(pubkey_prepare): Call ssh_send_agent_env via new func.
--
This features comes handy with recent GnuPG versions and avoids the
long standing use of
gpg-connect-agent updatestartuptty /bye
to tell gpg-agent that ssh is now used on the current X-server or tty.
The ssh-agent mechanism is described in
https://tools.ietf.org/html/draft-miller-ssh-agent-04
---
authfd.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++
authfd.h | 3 ++
readconf.c | 71 +++++++++++++++++++++++++++++++++++------
readconf.h | 4 +++
ssh-add.c | 4 +++
ssh-keygen.c | 2 ++
ssh_config | 3 ++
ssh_config.5 | 17 ++++++++++
sshconnect.c | 6 ++++
sshconnect2.c | 25 ++++++++++++++-
10 files changed, 213 insertions(+), 10 deletions(-)
diff --git a/authfd.c b/authfd.c
index 189ebb39..b425f86f 100644
--- a/authfd.c
+++ b/authfd.c
@@ -189,6 +189,94 @@ ssh_close_authentication_socket(int sock)
close(sock);
}
+
+/* Helper for ssh_send_agent_env. */
+static int
+put_one_env (struct sshbuf *msg, const char *name)
+{
+ int r;
+ const char *val;
+
+ val = getenv (name);
+ if ((r = sshbuf_put_cstring(msg, name)) == 0) {
+ if (val)
+ r = sshbuf_put_cstring(msg, val);
+ else
+ r = sshbuf_put_string(msg, "", 1);
+ }
+ return r;
+}
+
+/* Send configured envvars to the agent. Returns an error code. */
+int
+ssh_send_agent_env (int sock, char **env, int num_env)
+{
+ struct sshbuf *msg;
+ int i, r;
+ u_char type;
+ char *p, *ptr;
+ char *namelist = NULL;
+
+ if (num_env < 1)
+ return 0; /* None configured. */
+
+ if ((msg = sshbuf_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+
+ if (num_env == 1 && !strcmp (env[0], "auto")) {
+ /* If only one variable named "auto" is defined, query
+ * the supported variables from the agent. */
+ if ((r = sshbuf_put_u8(msg, SSH_AGENTC_EXTENSION)) != 0 ||
+ (r = sshbuf_put_cstring(msg,"ssh-envnames@gnupg.org"))!= 0)
+ goto out;
+ if ((r = ssh_request_reply(sock, msg, msg)) != 0)
+ goto out;
+ if ((r = sshbuf_get_u8(msg, &type)) != 0)
+ goto out;
+ if (type == SSH_AGENT_EXTENSION_FAILURE)
+ r = SSH_ERR_AGENT_FAILURE;
+ else
+ r = decode_reply(type);
+ if (r)
+ goto out;
+ if ((r = sshbuf_get_cstring(msg, &namelist, NULL)) != 0)
+ goto out;
+ sshbuf_reset (msg);
+ }
+
+ if ((r = sshbuf_put_u8(msg, SSH_AGENTC_EXTENSION)) != 0 ||
+ (r = sshbuf_put_cstring(msg, "ssh-env@gnupg.org")) != 0)
+ goto out;
+
+ if (namelist != NULL) {
+ ptr = namelist;
+ while ((p = strsep (&ptr, ","))) {
+ if ((r = put_one_env (msg, p)))
+ goto out;
+ }
+ }
+ else {
+ for (i = 0; i < num_env; i++) {
+ if ((r = put_one_env (msg, env[i])))
+ goto out;
+ }
+ }
+
+ if ((r = ssh_request_reply(sock, msg, msg)) != 0)
+ goto out;
+ if ((r = sshbuf_get_u8(msg, &type)) != 0)
+ goto out;
+ if (type == SSH_AGENT_EXTENSION_FAILURE)
+ r = SSH_ERR_AGENT_FAILURE;
+ else
+ r = decode_reply(type);
+ out:
+ sshbuf_free(msg);
+ free (namelist);
+ return r;
+
+}
+
/* Lock/unlock agent */
int
ssh_lock_agent(int sock, int lock, const char *password)
diff --git a/authfd.h b/authfd.h
index 4fbf82f8..20cc108b 100644
--- a/authfd.h
+++ b/authfd.h
@@ -27,6 +27,7 @@ int ssh_get_authentication_socket(int *fdp);
int ssh_get_authentication_socket_path(const char *authsocket, int *fdp);
void ssh_close_authentication_socket(int sock);
+int ssh_send_agent_env (int sock, char **env, int num_env);
int ssh_lock_agent(int sock, int lock, const char *password);
int ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp);
void ssh_free_identitylist(struct ssh_identitylist *idl);
@@ -75,6 +76,8 @@ int ssh_agent_sign(int sock, const struct sshkey *key,
#define SSH_AGENTC_ADD_RSA_ID_CONSTRAINED 24
#define SSH2_AGENTC_ADD_ID_CONSTRAINED 25
#define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26
+#define SSH_AGENTC_EXTENSION 27
+#define SSH_AGENT_EXTENSION_FAILURE 28
#define SSH_AGENT_CONSTRAIN_LIFETIME 1
#define SSH_AGENT_CONSTRAIN_CONFIRM 2
diff --git a/readconf.c b/readconf.c
index c7df93de..50a79e17 100644
--- a/readconf.c
+++ b/readconf.c
@@ -172,7 +172,7 @@ typedef enum {
oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes,
oPubkeyAcceptedAlgorithms, oCASignatureAlgorithms, oProxyJump,
- oSecurityKeyProvider, oKnownHostsCommand,
+ oSecurityKeyProvider, oKnownHostsCommand, oAgentEnv,
oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
} OpCodes;
@@ -313,11 +313,22 @@ static struct {
{ "proxyjump", oProxyJump },
{ "securitykeyprovider", oSecurityKeyProvider },
{ "knownhostscommand", oKnownHostsCommand },
+ { "agentenv", oAgentEnv },
{ NULL, oBadOption }
};
+/* Free the array A which holds N items and is indexed with a
+ * variable of TYPE. */
+#define FREE_ARRAY(type, n, a) \
+ do { \
+ type _i; \
+ for (_i = 0; _i < (n); _i++) \
+ free((a)[_i]); \
+ } while (0)
+
+
const char *
kex_default_pk_alg(void)
{
@@ -766,6 +777,7 @@ rm_env(Options *options, const char *arg, const char *filename, int linenum)
}
}
+
/*
* Returns the number of the token pointed to by cp or oBadOption.
*/
@@ -2001,6 +2013,49 @@ parse_pubkey_algos:
*charptr = xstrdup(arg);
break;
+ case oAgentEnv:
+ while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
+ if (strchr(arg, '=') != NULL) {
+ error("%s line %d: Invalid environment name.",
+ filename, linenum);
+ return -1;
+ }
+ if (!*activep || options->no_more_agent_env)
+ continue;
+ if ((*arg == '-' || *arg == '#') && arg[1]) {
+ error("%s line %d: Invalid environment name.",
+ filename, linenum);
+ return -1;
+ }
+ if (*arg == '-') {
+ /* Remove all names */
+ if (options->num_agent_env) {
+ FREE_ARRAY(int, options->num_agent_env,
+ options->agent_env);
+ free(options->agent_env);
+ options->agent_env = NULL;
+ options->num_agent_env = 0;
+ }
+ continue;
+ }
+ if (*arg == '#') {
+ options->no_more_agent_env = 1;
+ continue;
+ }
+ if (options->num_agent_env >= INT_MAX) {
+ error("%s line %d: too many agent env.",
+ filename, linenum);
+ return -1;
+ }
+ options->agent_env = xrecallocarray(
+ options->agent_env, options->num_agent_env,
+ options->num_agent_env + 1,
+ sizeof(*options->agent_env));
+ options->agent_env[options->num_agent_env++] =
+ xstrdup(arg);
+ }
+ break;
+
case oDeprecated:
debug("%s line %d: Deprecated option \"%s\"",
filename, linenum, keyword);
@@ -2193,6 +2248,9 @@ initialize_options(Options * options)
options->num_send_env = 0;
options->setenv = NULL;
options->num_setenv = 0;
+ options->agent_env = NULL;
+ options->num_agent_env = 0;
+ options->no_more_agent_env = 0;
options->control_path = NULL;
options->control_master = -1;
options->control_persist = -1;
@@ -2496,13 +2554,6 @@ free_options(Options *o)
if (o == NULL)
return;
-#define FREE_ARRAY(type, n, a) \
- do { \
- type _i; \
- for (_i = 0; _i < (n); _i++) \
- free((a)[_i]); \
- } while (0)
-
free(o->forward_agent_sock_path);
free(o->xauth_location);
FREE_ARRAY(u_int, o->num_log_verbose, o->log_verbose);
@@ -2551,6 +2602,8 @@ free_options(Options *o)
free(o->send_env);
FREE_ARRAY(int, o->num_setenv, o->setenv);
free(o->setenv);
+ FREE_ARRAY(int, o->num_agent_env, o->agent_env);
+ free(o->agent_env);
free(o->control_path);
free(o->local_command);
free(o->remote_command);
@@ -2567,7 +2620,6 @@ free_options(Options *o)
free(o->jump_extra);
free(o->ignored_unknown);
explicit_bzero(o, sizeof(*o));
-#undef FREE_ARRAY
}
struct fwdarg {
@@ -3120,6 +3172,7 @@ dump_client_config(Options *o, const char *host)
dump_cfg_strarray_oneline(oUserKnownHostsFile, o->num_user_hostfiles, o->user_hostfiles);
dump_cfg_strarray(oSendEnv, o->num_send_env, o->send_env);
dump_cfg_strarray(oSetEnv, o->num_setenv, o->setenv);
+ dump_cfg_strarray(oAgentEnv, o->num_agent_env, o->agent_env);
dump_cfg_strarray_oneline(oLogVerbose,
o->num_log_verbose, o->log_verbose);
diff --git a/readconf.h b/readconf.h
index 4ee730b9..e0b5f370 100644
--- a/readconf.h
+++ b/readconf.h
@@ -126,6 +126,10 @@ typedef struct {
char **send_env;
int num_setenv;
char **setenv;
+ int no_more_agent_env;
+ int num_agent_env;
+ char **agent_env;
+
char *control_path;
int control_master;
diff --git a/ssh-add.c b/ssh-add.c
index 7edb9f9a..c6a9b2ce 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -780,6 +780,10 @@ main(int argc, char **argv)
}
log_init(__progname, log_level, log_facility, 1);
+ /* FIXME: Read the AgentEnv option from ssh_config and call
+ * ssh_send_agent_env (int sock, char **env, int num_env)
+ */
+
if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1)
fatal("Invalid combination of actions");
else if (xflag) {
diff --git a/ssh-keygen.c b/ssh-keygen.c
index cfb5f115..331ad7f6 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -2622,6 +2622,8 @@ sig_sign(const char *keypath, const char *sig_namespace, int argc, char **argv)
goto done;
}
+ /* FIXME: Handle AgentEnv */
+
if ((r = ssh_get_authentication_socket(&agent_fd)) != 0)
debug_r(r, "Couldn't get agent socket");
else {
diff --git a/ssh_config b/ssh_config
index 842ea866..05c5539b 100644
--- a/ssh_config
+++ b/ssh_config
@@ -44,3 +44,6 @@
# ProxyCommand ssh -q -W %h:%p gateway.example.com
# RekeyLimit 1G 1h
# UserKnownHostsFile ~/.ssh/known_hosts.d/%k
+
+# For use with GnuPG's ssh-agent implementation use
+# AgentEnv auto
diff --git a/ssh_config.5 b/ssh_config.5
index 96d6f658..fd33e773 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -270,6 +270,23 @@ Valid arguments are
(use IPv4 only), or
.Cm inet6
(use IPv6 only).
+.It Cm AgentEnv
+Specifies what variables from the local
+.Xr environ 7
+should be sent to a running ssh-agent(1).
+The agent may use these environment variables at its own discretion.
+Note that patterns for the variable names are not supported. To empty
+the list of previously set
+.Cm AgentEnv
+variable names the special name
+.Pa -
+may be used. To ignore all further set names use the special name
+.Pa # .
+To ask the agent for a list of names to send use
+.Pa auto
+as the first and only item.
+.Pp
+The default is not to send any environment variables to the agent.
.It Cm BatchMode
If set to
.Cm yes ,
diff --git a/sshconnect.c b/sshconnect.c
index 616ee37e..adce5d80 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -1718,6 +1718,12 @@ maybe_add_key_to_agent(const char *authfile, struct sshkey *private,
}
if (sshkey_is_sk(private))
skprovider = options.sk_provider;
+ if ((r = ssh_send_agent_env (auth_sock, options.agent_env,
+ options.num_agent_env)) != 0) {
+ debug("agent does not support AgentEnv: (%d)", r);
+ close(auth_sock);
+ return;
+ }
if ((r = ssh_add_identity_constrained(auth_sock, private,
comment == NULL ? authfile : comment,
options.add_keys_to_agent_lifespan,
diff --git a/sshconnect2.c b/sshconnect2.c
index de89b761..39302829 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1623,6 +1623,29 @@ key_type_allowed_by_config(struct sshkey *key)
}
+/* Helper for pubkey_prepare. */
+static int
+authentication_socket(int *fdp)
+{
+ int r;
+ int sock;
+
+ if ((r = ssh_get_authentication_socket(&sock)) == 0) {
+ if ((r = ssh_send_agent_env (sock, options.agent_env,
+ options.num_agent_env)) != 0) {
+ debug("agent does not support AgentEnv: (%d)", r);
+ close(sock);
+ *fdp = -1;
+ }
+ }
+
+ if (!r) {
+ *fdp = sock;
+ }
+
+ return r;
+}
+
/*
* try keys in the following order:
* 1. certificates listed in the config file
@@ -1694,7 +1717,7 @@ pubkey_prepare(Authctxt *authctxt)
TAILQ_INSERT_TAIL(preferred, id, next);
}
/* list of keys supported by the agent */
- if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) {
+ if ((r = authentication_socket(&agent_fd)) != 0) {
if (r != SSH_ERR_AGENT_NOT_PRESENT)
debug_fr(r, "ssh_get_authentication_socket");
} else if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) {
--
2.20.1

File Metadata

Mime Type
text/x-diff
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1353009

Event Timeline