agent: Support ssh-agent extensions for environment variables.

* common/session-env.c (session_env_list_stdenvnames): Extend to allow
return all names as one string.
* agent/command-ssh.c (SSH_REQUEST_EXTENSION): New.
(SSH_RESPONSE_EXTENSION_FAILURE): New.
(request_specs): Add handler for the extension command.
(ssh_handler_extension): New.
--

The extension mechanism is specified in
https://tools.ietf.org/html/draft-miller-ssh-agent-04

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2021-01-25 10:35:06 +01:00
parent 60499d9894
commit 224e26cf7b
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
2 changed files with 120 additions and 4 deletions

View File

@ -70,6 +70,7 @@
#define SSH_REQUEST_LOCK 22
#define SSH_REQUEST_UNLOCK 23
#define SSH_REQUEST_ADD_ID_CONSTRAINED 25
#define SSH_REQUEST_EXTENSION 27
/* Options. */
#define SSH_OPT_CONSTRAIN_LIFETIME 1
@ -80,6 +81,7 @@
#define SSH_RESPONSE_FAILURE 5
#define SSH_RESPONSE_IDENTITIES_ANSWER 12
#define SSH_RESPONSE_SIGN_RESPONSE 14
#define SSH_RESPONSE_EXTENSION_FAILURE 28
/* Other constants. */
#define SSH_DSA_SIGNATURE_PADDING 20
@ -249,6 +251,9 @@ static gpg_error_t ssh_handler_lock (ctrl_t ctrl,
static gpg_error_t ssh_handler_unlock (ctrl_t ctrl,
estream_t request,
estream_t response);
static gpg_error_t ssh_handler_extension (ctrl_t ctrl,
estream_t request,
estream_t response);
static gpg_error_t ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis);
static gpg_error_t ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec,
@ -290,7 +295,8 @@ static const ssh_request_spec_t request_specs[] =
REQUEST_SPEC_DEFINE (REMOVE_IDENTITY, remove_identity, 0),
REQUEST_SPEC_DEFINE (REMOVE_ALL_IDENTITIES, remove_all_identities, 0),
REQUEST_SPEC_DEFINE (LOCK, lock, 0),
REQUEST_SPEC_DEFINE (UNLOCK, unlock, 0)
REQUEST_SPEC_DEFINE (UNLOCK, unlock, 0),
REQUEST_SPEC_DEFINE (EXTENSION, extension, 0)
#undef REQUEST_SPEC_DEFINE
};
@ -3304,6 +3310,84 @@ ssh_handler_unlock (ctrl_t ctrl, estream_t request, estream_t response)
return ret_err;
}
/* Handler for the "extension" command. */
static gpg_error_t
ssh_handler_extension (ctrl_t ctrl, estream_t request, estream_t response)
{
gpg_error_t ret_err;
gpg_error_t err;
char *exttype = NULL;
char *name = NULL;
char *value = NULL;
err = stream_read_cstring (request, &exttype);
if (err)
goto leave;
if (opt.verbose)
log_info ("ssh-agent extension '%s' received\n", exttype);
if (!strcmp (exttype, "ssh-env@gnupg.org"))
{
for (;;)
{
xfree (name); name = NULL;
err = stream_read_cstring (request, &name);
if (gpg_err_code (err) == GPG_ERR_EOF)
break; /* ready. */
if (err)
{
if (opt.verbose)
log_error ("error reading ssh-agent env name\n");
goto leave;
}
xfree (value); value = NULL;
err = stream_read_cstring (request, &value);
if (err)
{
if (opt.verbose)
log_error ("error reading ssh-agent env value\n");
goto leave;
}
if (opt.debug)
log_debug ("ssh-agent env '%s'='%s'\n", name, value);
err = session_env_setenv (ctrl->session_env, name,
*value? value : NULL);
if (err)
{
log_error ("error setting ssh-agent env value: %s\n",
gpg_strerror (err));
goto leave;
}
}
err = 0;
}
else if (!strcmp (exttype, "ssh-envnames@gnupg.org"))
{
ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS);
if (!ret_err)
ret_err = stream_write_cstring
(response, session_env_list_stdenvnames (NULL, NULL));
goto finalleave;
}
else
{
if (opt.verbose)
log_info ("ssh-agent extension '%s' not supported\n", exttype);
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
}
leave:
if (!err)
ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS);
else
ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
finalleave:
xfree (exttype);
xfree (name);
xfree (value);
return ret_err;
}
/* Return the request specification for the request identified by TYPE

View File

@ -98,13 +98,45 @@ static size_t lastallocatedarraysize;
/* Return the names of standard environment variables one after the
other. The caller needs to set the value at the address of
ITERATOR initially to 0 and then call this function until it returns
NULL. */
ITERATOR initially to 0 and then call this function until it
returns NULL. If ITERATOR is NULL, a single comma delimited string
with the names is returned; NULL is never returned in this case and
R_ASSNAME is ignored. */
const char *
session_env_list_stdenvnames (int *iterator, const char **r_assname)
{
int idx = *iterator;
int idx;
static char *commastring;
if (!iterator)
{
if (!commastring)
{
size_t len = 0;
char *p;
for (idx = 0; idx < DIM (stdenvnames); idx++)
len += strlen (stdenvnames[idx].name) + 1;
commastring = xtrymalloc (len);
if (!commastring)
{
log_error ("%s: error allocating string: %s\n", __func__,
gpg_strerror (gpg_error_from_syserror ()));
return "GPG_TTY,TERM,DISPLAY";
}
p = commastring;
for (idx = 0; idx < DIM (stdenvnames); idx++)
{
if (idx)
*p++ = ',';
p = stpcpy (p, stdenvnames[idx].name);
}
gpgrt_annotate_leaked_object (commastring);
}
return commastring;
}
idx = *iterator;
if (idx < 0 || idx >= DIM (stdenvnames))
return NULL;
*iterator = idx + 1;