Skip to content

Commit fd9e226

Browse files
FStelzergitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 29b3157 commit fd9e226

File tree

3 files changed

+77
-3
lines changed

3 files changed

+77
-3
lines changed

Documentation/config/gpg.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,9 @@ gpg.minTrustLevel::
3333
* `marginal`
3434
* `fully`
3535
* `ultimate`
36+
37+
gpg.ssh.defaultKeyCommand:
38+
This command that will be run when user.signingkey is not set and a ssh
39+
signature is requested. On successful exit a valid ssh public key is
40+
expected in the first line of its output. To automatically use the first
41+
available key from your ssh-agent set this to "ssh-add -L".

Documentation/config/user.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,6 @@ user.signingKey::
4040
key (e.g.: "ssh-rsa XXXXXX identifier") or a file which contains it and
4141
corresponds to the private key used for signing. The private key
4242
needs to be available via ssh-agent. Alternatively it can be set to
43-
a file containing a private key directly.
43+
a file containing a private key directly. If not set git will call
44+
gpg.ssh.defaultKeyCommand (e.g.: "ssh-add -L") and try to use the first
45+
key available.

gpg-interface.c

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
#include "gpg-interface.h"
77
#include "sigchain.h"
88
#include "tempfile.h"
9+
#include "alias.h"
910

1011
static char *configured_signing_key;
12+
static const char *ssh_default_key_command;
1113
static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED;
1214

1315
struct gpg_format {
@@ -21,6 +23,7 @@ struct gpg_format {
2123
size_t signature_size);
2224
int (*sign_buffer)(struct strbuf *buffer, struct strbuf *signature,
2325
const char *signing_key);
26+
const char *(*get_default_key)(void);
2427
};
2528

2629
static const char *openpgp_verify_args[] = {
@@ -56,6 +59,8 @@ static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,
5659
static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
5760
const char *signing_key);
5861

62+
static const char *get_default_ssh_signing_key(void);
63+
5964
static struct gpg_format gpg_format[] = {
6065
{
6166
.name = "openpgp",
@@ -64,6 +69,7 @@ static struct gpg_format gpg_format[] = {
6469
.sigs = openpgp_sigs,
6570
.verify_signed_buffer = verify_gpg_signed_buffer,
6671
.sign_buffer = sign_buffer_gpg,
72+
.get_default_key = NULL,
6773
},
6874
{
6975
.name = "x509",
@@ -72,14 +78,16 @@ static struct gpg_format gpg_format[] = {
7278
.sigs = x509_sigs,
7379
.verify_signed_buffer = verify_gpg_signed_buffer,
7480
.sign_buffer = sign_buffer_gpg,
81+
.get_default_key = NULL,
7582
},
7683
{
7784
.name = "ssh",
7885
.program = "ssh-keygen",
7986
.verify_args = ssh_verify_args,
8087
.sigs = ssh_sigs,
8188
.verify_signed_buffer = NULL, /* TODO */
82-
.sign_buffer = sign_buffer_ssh
89+
.sign_buffer = sign_buffer_ssh,
90+
.get_default_key = get_default_ssh_signing_key,
8391
},
8492
};
8593

@@ -453,6 +461,12 @@ int git_gpg_config(const char *var, const char *value, void *cb)
453461
return 0;
454462
}
455463

464+
if (!strcmp(var, "gpg.ssh.defaultkeycommand")) {
465+
if (!value)
466+
return config_error_nonbool(var);
467+
return git_config_string(&ssh_default_key_command, var, value);
468+
}
469+
456470
if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program"))
457471
fmtname = "openpgp";
458472

@@ -470,11 +484,63 @@ int git_gpg_config(const char *var, const char *value, void *cb)
470484
return 0;
471485
}
472486

487+
/* Returns the first public key from an ssh-agent to use for signing */
488+
static const char *get_default_ssh_signing_key(void)
489+
{
490+
struct child_process ssh_default_key = CHILD_PROCESS_INIT;
491+
int ret = -1;
492+
struct strbuf key_stdout = STRBUF_INIT, key_stderr = STRBUF_INIT;
493+
struct strbuf **keys;
494+
char *key_command = NULL;
495+
const char **argv;
496+
int n;
497+
char *default_key = NULL;
498+
499+
if (!ssh_default_key_command)
500+
die(_("either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured"));
501+
502+
key_command = xstrdup(ssh_default_key_command);
503+
n = split_cmdline(key_command, &argv);
504+
505+
if (n < 0)
506+
die("malformed build-time gpg.ssh.defaultKeyCommand: %s",
507+
split_cmdline_strerror(n));
508+
509+
strvec_pushv(&ssh_default_key.args, argv);
510+
ret = pipe_command(&ssh_default_key, NULL, 0, &key_stdout, 0,
511+
&key_stderr, 0);
512+
513+
if (!ret) {
514+
keys = strbuf_split_max(&key_stdout, '\n', 2);
515+
if (keys[0] && starts_with(keys[0]->buf, "ssh-")) {
516+
default_key = strbuf_detach(keys[0], NULL);
517+
} else {
518+
warning(_("gpg.ssh.defaultKeycommand succeeded but returned no keys: %s %s"),
519+
key_stderr.buf, key_stdout.buf);
520+
}
521+
522+
strbuf_list_free(keys);
523+
} else {
524+
warning(_("gpg.ssh.defaultKeyCommand failed: %s %s"),
525+
key_stderr.buf, key_stdout.buf);
526+
}
527+
528+
free(key_command);
529+
free(argv);
530+
strbuf_release(&key_stdout);
531+
532+
return default_key;
533+
}
534+
473535
const char *get_signing_key(void)
474536
{
475537
if (configured_signing_key)
476538
return configured_signing_key;
477-
return git_committer_info(IDENT_STRICT|IDENT_NO_DATE);
539+
if (use_format->get_default_key) {
540+
return use_format->get_default_key();
541+
}
542+
543+
return git_committer_info(IDENT_STRICT | IDENT_NO_DATE);
478544
}
479545

480546
int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)

0 commit comments

Comments
 (0)