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_LOCK 22
#define SSH_REQUEST_UNLOCK 23 #define SSH_REQUEST_UNLOCK 23
#define SSH_REQUEST_ADD_ID_CONSTRAINED 25 #define SSH_REQUEST_ADD_ID_CONSTRAINED 25
#define SSH_REQUEST_EXTENSION 27
/* Options. */ /* Options. */
#define SSH_OPT_CONSTRAIN_LIFETIME 1 #define SSH_OPT_CONSTRAIN_LIFETIME 1
@ -80,6 +81,7 @@
#define SSH_RESPONSE_FAILURE 5 #define SSH_RESPONSE_FAILURE 5
#define SSH_RESPONSE_IDENTITIES_ANSWER 12 #define SSH_RESPONSE_IDENTITIES_ANSWER 12
#define SSH_RESPONSE_SIGN_RESPONSE 14 #define SSH_RESPONSE_SIGN_RESPONSE 14
#define SSH_RESPONSE_EXTENSION_FAILURE 28
/* Other constants. */ /* Other constants. */
#define SSH_DSA_SIGNATURE_PADDING 20 #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, static gpg_error_t ssh_handler_unlock (ctrl_t ctrl,
estream_t request, estream_t request,
estream_t response); 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_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis);
static gpg_error_t ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec, 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_IDENTITY, remove_identity, 0),
REQUEST_SPEC_DEFINE (REMOVE_ALL_IDENTITIES, remove_all_identities, 0), REQUEST_SPEC_DEFINE (REMOVE_ALL_IDENTITIES, remove_all_identities, 0),
REQUEST_SPEC_DEFINE (LOCK, lock, 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 #undef REQUEST_SPEC_DEFINE
}; };
@ -3304,6 +3310,84 @@ ssh_handler_unlock (ctrl_t ctrl, estream_t request, estream_t response)
return ret_err; 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 /* 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 /* Return the names of standard environment variables one after the
other. The caller needs to set the value at the address of other. The caller needs to set the value at the address of
ITERATOR initially to 0 and then call this function until it returns ITERATOR initially to 0 and then call this function until it
NULL. */ 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 * const char *
session_env_list_stdenvnames (int *iterator, const char **r_assname) 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)) if (idx < 0 || idx >= DIM (stdenvnames))
return NULL; return NULL;
*iterator = idx + 1; *iterator = idx + 1;