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>

Fabian Stelzer committed Sep 10, 2021 at 20:07 UTC fd9e226776d1874af36b6b02fb2002b917af42fa
3 files changed +77 -3
Documentation/config/gpg.txt
+6
@@ -33,3 +33,9 @@ gpg.minTrustLevel::
33 * `marginal`
34 * `fully`
35 * `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
+3 -1
@@ -40,4 +40,6 @@ user.signingKey::
40 key (e.g.: "ssh-rsa XXXXXX identifier") or a file which contains it and
41 corresponds to the private key used for signing. The private key
42 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
+68 -2
@@ -6,8 +6,10 @@
6 #include "gpg-interface.h"
7 #include "sigchain.h"
8 #include "tempfile.h"
9 +#include "alias.h"
10
11 static char *configured_signing_key;
12 +static const char *ssh_default_key_command;
13 static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED;
14
15 struct gpg_format {
@@ -21,6 +23,7 @@ struct gpg_format {
23 size_t signature_size);
24 int (*sign_buffer)(struct strbuf *buffer, struct strbuf *signature,
25 const char *signing_key);
26 + const char *(*get_default_key)(void);
27 };
28
29 static const char *openpgp_verify_args[] = {
@@ -56,6 +59,8 @@ static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,
59 static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
60 const char *signing_key);
61
62 +static const char *get_default_ssh_signing_key(void);
63 +
64 static struct gpg_format gpg_format[] = {
65 {
66 .name = "openpgp",
@@ -64,6 +69,7 @@ static struct gpg_format gpg_format[] = {
69 .sigs = openpgp_sigs,
70 .verify_signed_buffer = verify_gpg_signed_buffer,
71 .sign_buffer = sign_buffer_gpg,
72 + .get_default_key = NULL,
73 },
74 {
75 .name = "x509",
@@ -72,6 +78,7 @@ static struct gpg_format gpg_format[] = {
78 .sigs = x509_sigs,
79 .verify_signed_buffer = verify_gpg_signed_buffer,
80 .sign_buffer = sign_buffer_gpg,
81 + .get_default_key = NULL,
82 },
83 {
84 .name = "ssh",
@@ -79,7 +86,8 @@ static struct gpg_format gpg_format[] = {
86 .verify_args = ssh_verify_args,
87 .sigs = ssh_sigs,
88 .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,
91 },
92 };
93
@@ -453,6 +461,12 @@ int git_gpg_config(const char *var, const char *value, void *cb)
461 return 0;
462 }
463
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 +
470 if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program"))
471 fmtname = "openpgp";
472
@@ -470,11 +484,63 @@ int git_gpg_config(const char *var, const char *value, void *cb)
484 return 0;
485 }
486
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 +
535 const char *get_signing_key(void)
536 {
537 if (configured_signing_key)
538 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);
544 }
545
546 int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)