1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-05-12 09:26:08 +02:00

ssh signing: retrieve a default key from ssh-agent

If user.signingkey is not set and a ssh signature is requested we call
gpg.ssh.defaultKeyCommand (typically "ssh-add -L") and use the first key we get

Signed-off-by: Fabian Stelzer <fs@gigacodes.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Fabian Stelzer 2021-09-10 20:07:37 +00:00 committed by Junio C Hamano
parent 29b315778e
commit fd9e226776
3 changed files with 77 additions and 3 deletions

View File

@ -33,3 +33,9 @@ gpg.minTrustLevel::
* `marginal` * `marginal`
* `fully` * `fully`
* `ultimate` * `ultimate`
gpg.ssh.defaultKeyCommand:
This command that will be run when user.signingkey is not set and a ssh
signature is requested. On successful exit a valid ssh public key is
expected in the first line of its output. To automatically use the first
available key from your ssh-agent set this to "ssh-add -L".

View File

@ -40,4 +40,6 @@ user.signingKey::
key (e.g.: "ssh-rsa XXXXXX identifier") or a file which contains it and key (e.g.: "ssh-rsa XXXXXX identifier") or a file which contains it and
corresponds to the private key used for signing. The private key corresponds to the private key used for signing. The private key
needs to be available via ssh-agent. Alternatively it can be set to needs to be available via ssh-agent. Alternatively it can be set to
a file containing a private key directly. a file containing a private key directly. If not set git will call
gpg.ssh.defaultKeyCommand (e.g.: "ssh-add -L") and try to use the first
key available.

View File

@ -6,8 +6,10 @@
#include "gpg-interface.h" #include "gpg-interface.h"
#include "sigchain.h" #include "sigchain.h"
#include "tempfile.h" #include "tempfile.h"
#include "alias.h"
static char *configured_signing_key; static char *configured_signing_key;
static const char *ssh_default_key_command;
static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED; static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED;
struct gpg_format { struct gpg_format {
@ -21,6 +23,7 @@ struct gpg_format {
size_t signature_size); size_t signature_size);
int (*sign_buffer)(struct strbuf *buffer, struct strbuf *signature, int (*sign_buffer)(struct strbuf *buffer, struct strbuf *signature,
const char *signing_key); const char *signing_key);
const char *(*get_default_key)(void);
}; };
static const char *openpgp_verify_args[] = { static const char *openpgp_verify_args[] = {
@ -56,6 +59,8 @@ static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,
static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature, static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
const char *signing_key); const char *signing_key);
static const char *get_default_ssh_signing_key(void);
static struct gpg_format gpg_format[] = { static struct gpg_format gpg_format[] = {
{ {
.name = "openpgp", .name = "openpgp",
@ -64,6 +69,7 @@ static struct gpg_format gpg_format[] = {
.sigs = openpgp_sigs, .sigs = openpgp_sigs,
.verify_signed_buffer = verify_gpg_signed_buffer, .verify_signed_buffer = verify_gpg_signed_buffer,
.sign_buffer = sign_buffer_gpg, .sign_buffer = sign_buffer_gpg,
.get_default_key = NULL,
}, },
{ {
.name = "x509", .name = "x509",
@ -72,6 +78,7 @@ static struct gpg_format gpg_format[] = {
.sigs = x509_sigs, .sigs = x509_sigs,
.verify_signed_buffer = verify_gpg_signed_buffer, .verify_signed_buffer = verify_gpg_signed_buffer,
.sign_buffer = sign_buffer_gpg, .sign_buffer = sign_buffer_gpg,
.get_default_key = NULL,
}, },
{ {
.name = "ssh", .name = "ssh",
@ -79,7 +86,8 @@ static struct gpg_format gpg_format[] = {
.verify_args = ssh_verify_args, .verify_args = ssh_verify_args,
.sigs = ssh_sigs, .sigs = ssh_sigs,
.verify_signed_buffer = NULL, /* TODO */ .verify_signed_buffer = NULL, /* TODO */
.sign_buffer = sign_buffer_ssh .sign_buffer = sign_buffer_ssh,
.get_default_key = get_default_ssh_signing_key,
}, },
}; };
@ -453,6 +461,12 @@ int git_gpg_config(const char *var, const char *value, void *cb)
return 0; return 0;
} }
if (!strcmp(var, "gpg.ssh.defaultkeycommand")) {
if (!value)
return config_error_nonbool(var);
return git_config_string(&ssh_default_key_command, var, value);
}
if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program")) if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program"))
fmtname = "openpgp"; fmtname = "openpgp";
@ -470,11 +484,63 @@ int git_gpg_config(const char *var, const char *value, void *cb)
return 0; return 0;
} }
/* Returns the first public key from an ssh-agent to use for signing */
static const char *get_default_ssh_signing_key(void)
{
struct child_process ssh_default_key = CHILD_PROCESS_INIT;
int ret = -1;
struct strbuf key_stdout = STRBUF_INIT, key_stderr = STRBUF_INIT;
struct strbuf **keys;
char *key_command = NULL;
const char **argv;
int n;
char *default_key = NULL;
if (!ssh_default_key_command)
die(_("either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured"));
key_command = xstrdup(ssh_default_key_command);
n = split_cmdline(key_command, &argv);
if (n < 0)
die("malformed build-time gpg.ssh.defaultKeyCommand: %s",
split_cmdline_strerror(n));
strvec_pushv(&ssh_default_key.args, argv);
ret = pipe_command(&ssh_default_key, NULL, 0, &key_stdout, 0,
&key_stderr, 0);
if (!ret) {
keys = strbuf_split_max(&key_stdout, '\n', 2);
if (keys[0] && starts_with(keys[0]->buf, "ssh-")) {
default_key = strbuf_detach(keys[0], NULL);
} else {
warning(_("gpg.ssh.defaultKeycommand succeeded but returned no keys: %s %s"),
key_stderr.buf, key_stdout.buf);
}
strbuf_list_free(keys);
} else {
warning(_("gpg.ssh.defaultKeyCommand failed: %s %s"),
key_stderr.buf, key_stdout.buf);
}
free(key_command);
free(argv);
strbuf_release(&key_stdout);
return default_key;
}
const char *get_signing_key(void) const char *get_signing_key(void)
{ {
if (configured_signing_key) if (configured_signing_key)
return configured_signing_key; return configured_signing_key;
return git_committer_info(IDENT_STRICT|IDENT_NO_DATE); if (use_format->get_default_key) {
return use_format->get_default_key();
}
return git_committer_info(IDENT_STRICT | IDENT_NO_DATE);
} }
int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key) int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)