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>maint
							parent
							
								
									29b315778e
								
							
						
					
					
						commit
						fd9e226776
					
				|  | @ -33,3 +33,9 @@ gpg.minTrustLevel:: | |||
| * `marginal` | ||||
| * `fully` | ||||
| * `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". | ||||
|  |  | |||
|  | @ -40,4 +40,6 @@ user.signingKey:: | |||
| 	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 | ||||
| 	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. | ||||
|  |  | |||
|  | @ -6,8 +6,10 @@ | |||
| #include "gpg-interface.h" | ||||
| #include "sigchain.h" | ||||
| #include "tempfile.h" | ||||
| #include "alias.h" | ||||
|  | ||||
| static char *configured_signing_key; | ||||
| static const char *ssh_default_key_command; | ||||
| static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED; | ||||
|  | ||||
| struct gpg_format { | ||||
|  | @ -21,6 +23,7 @@ struct gpg_format { | |||
| 				    size_t signature_size); | ||||
| 	int (*sign_buffer)(struct strbuf *buffer, struct strbuf *signature, | ||||
| 			   const char *signing_key); | ||||
| 	const char *(*get_default_key)(void); | ||||
| }; | ||||
|  | ||||
| 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, | ||||
| 			   const char *signing_key); | ||||
|  | ||||
| static const char *get_default_ssh_signing_key(void); | ||||
|  | ||||
| static struct gpg_format gpg_format[] = { | ||||
| 	{ | ||||
| 		.name = "openpgp", | ||||
|  | @ -64,6 +69,7 @@ static struct gpg_format gpg_format[] = { | |||
| 		.sigs = openpgp_sigs, | ||||
| 		.verify_signed_buffer = verify_gpg_signed_buffer, | ||||
| 		.sign_buffer = sign_buffer_gpg, | ||||
| 		.get_default_key = NULL, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.name = "x509", | ||||
|  | @ -72,6 +78,7 @@ static struct gpg_format gpg_format[] = { | |||
| 		.sigs = x509_sigs, | ||||
| 		.verify_signed_buffer = verify_gpg_signed_buffer, | ||||
| 		.sign_buffer = sign_buffer_gpg, | ||||
| 		.get_default_key = NULL, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.name = "ssh", | ||||
|  | @ -79,7 +86,8 @@ static struct gpg_format gpg_format[] = { | |||
| 		.verify_args = ssh_verify_args, | ||||
| 		.sigs = ssh_sigs, | ||||
| 		.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; | ||||
| 	} | ||||
|  | ||||
| 	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")) | ||||
| 		fmtname = "openpgp"; | ||||
|  | ||||
|  | @ -470,10 +484,62 @@ int git_gpg_config(const char *var, const char *value, void *cb) | |||
| 	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) | ||||
| { | ||||
| 	if (configured_signing_key) | ||||
| 		return configured_signing_key; | ||||
| 	if (use_format->get_default_key) { | ||||
| 		return use_format->get_default_key(); | ||||
| 	} | ||||
|  | ||||
| 	return git_committer_info(IDENT_STRICT | IDENT_NO_DATE); | ||||
| } | ||||
|  | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Fabian Stelzer
						Fabian Stelzer