"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Josh Rickmar <openbsd+lists@zettaport.com>
Subject:
Re: add signer_id option to got.conf(5)
To:
gameoftrees@openbsd.org
Date:
Tue, 5 Jul 2022 11:06:21 -0400

Download raw body.

Thread
On Tue, Jul 05, 2022 at 04:55:57PM +0200, Stefan Sperling wrote:
> On Tue, Jul 05, 2022 at 10:51:01AM -0400, Josh Rickmar wrote:
> > ok?
> 
> Needs a regress test :)
> 

same patch with a test

-----------------------------------------------
commit c1f3d53395d805bcf04e1302fc5344bc110386a2 (signer_id_conf)
from: Josh Rickmar <jrick@zettaport.com>
date: Tue Jul  5 15:04:58 2022 UTC
 
 add signer_id option to got.conf(5)
 
 Setting this option will cause 'got tag' to sign all created tags using
 the SSH key, unless overridden by the -s flag.
 
diff bd957eacd0877b6f82a7dea14c185f3eb1e5b41c c1f3d53395d805bcf04e1302fc5344bc110386a2
commit - bd957eacd0877b6f82a7dea14c185f3eb1e5b41c
commit + c1f3d53395d805bcf04e1302fc5344bc110386a2
blob - 423824ab575879d09fe670bf4a98d4735d474f45
blob + 77ee3290c2b299ba86499c5b196115d3f54e96b6
--- got/got.c
+++ got/got.c
@@ -704,6 +704,39 @@ get_revoked_signers(char **revoked_signers, struct got
 }
 
 static const struct got_error *
+get_signer_id(char **signer_id, struct got_repository *repo,
+    struct got_worktree *worktree)
+{
+	const char *got_signer_id = NULL;
+	const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL;
+
+	*signer_id = NULL;
+
+	if (worktree)
+		worktree_conf = got_worktree_get_gotconfig(worktree);
+	repo_conf = got_repo_get_gotconfig(repo);
+
+	/*
+	 * Priority of potential author information sources, from most
+	 * significant to least significant:
+	 * 1) work tree's .got/got.conf file
+	 * 2) repository's got.conf file
+	 */
+
+	if (worktree_conf)
+		got_signer_id = got_gotconfig_get_signer_id(worktree_conf);
+	if (got_signer_id == NULL)
+		got_signer_id = got_gotconfig_get_signer_id(repo_conf);
+
+	if (got_signer_id) {
+		*signer_id = strdup(got_signer_id);
+		if (*signer_id == NULL)
+			return got_error_from_errno("strdup");
+	}
+	return NULL;
+}
+
+static const struct got_error *
 get_gitconfig_path(char **gitconfig_path)
 {
 	const char *homedir = getenv("HOME");
@@ -7281,9 +7314,9 @@ cmd_tag(int argc, char *argv[])
 	char *cwd = NULL, *repo_path = NULL, *commit_id_str = NULL;
 	char *gitconfig_path = NULL, *tagger = NULL;
 	char *allowed_signers = NULL, *revoked_signers = NULL;
+	char *signer_id = NULL;
 	const char *tag_name = NULL, *commit_id_arg = NULL, *tagmsg = NULL;
 	int ch, do_list = 0, verify_tags = 0, verbosity = 0;
-	const char *signer_id = NULL;
 	int *pack_fds = NULL;
 
 	while ((ch = getopt(argc, argv, "c:m:r:ls:Vv")) != -1) {
@@ -7296,16 +7329,22 @@ cmd_tag(int argc, char *argv[])
 			break;
 		case 'r':
 			repo_path = realpath(optarg, NULL);
-			if (repo_path == NULL)
-				return got_error_from_errno2("realpath",
+			if (repo_path == NULL) {
+				error = got_error_from_errno2("realpath",
 				    optarg);
+				goto done;
+			}
 			got_path_strip_trailing_slashes(repo_path);
 			break;
 		case 'l':
 			do_list = 1;
 			break;
 		case 's':
-			signer_id = optarg;
+			signer_id = strdup(optarg);
+			if (signer_id == NULL) {
+				error = got_error_from_errno("strdup");
+				goto done;
+			}
 			break;
 		case 'V':
 			verify_tags = 1;
@@ -7432,6 +7471,11 @@ cmd_tag(int argc, char *argv[])
 		error = get_author(&tagger, repo, worktree);
 		if (error)
 			goto done;
+		if (signer_id == NULL) {
+			error = get_signer_id(&signer_id, repo, worktree);
+			if (error)
+				goto done;
+		}
 		if (worktree) {
 			/* Release work tree lock. */
 			got_worktree_close(worktree);
@@ -7492,6 +7536,7 @@ done:
 	free(tagger);
 	free(allowed_signers);
 	free(revoked_signers);
+	free(signer_id);
 	return error;
 }
 
blob - 7b2e234dbad1c046f7c60882658a72fb41612294
blob + bf287b2c094e6c1f0225bc770acf44401dfd19b2
--- got/got.conf.5
+++ got/got.conf.5
@@ -55,6 +55,18 @@ Because
 may fail to parse commits without an email address in author data,
 .Xr got 1
 attempts to reject author information with a missing email address.
+.It Ic signer_id Pa signer-id
+Configure a
+.Ar signer-id
+to sign tag objects.
+This key will be used to sign all tag objects unless overridden by
+.Cm got tag Fl s Ar signer-id .
+.Pp
+For SSH-based signatures,
+.Ar signer-id
+is the path to a file which may refer to either a private SSH key,
+or a public SSH key with the private half available via
+.Xr ssh-agent 1 .
 .It Ic allowed_signers Pa path
 Configure a
 .Ar path
blob - 26e15d93b91bc42ee028fa8ecf60a8d1ac4dfdc9
blob + 856906fff4ebd284a8a00d8f3b215d57faaa4a96
--- include/got_gotconfig.h
+++ include/got_gotconfig.h
@@ -45,3 +45,11 @@ got_gotconfig_get_allowed_signers_file(const struct go
  */
 const char *
 got_gotconfig_get_revoked_signers_file(const struct got_gotconfig *);
+
+/*
+ * Obtain the signer identity used to sign tag objects
+ * Returns NULL if no configuration file is found or no revoked signers file
+ * is configured.
+ */
+const char *
+got_gotconfig_get_signer_id(const struct got_gotconfig *);
blob - 39337ed4d9cbe7dfa5939b3f4dcb38793ccddfbd
blob + 3bc97a1cade1f4c7316b0712a2bb752d0276ebbc
--- lib/got_lib_gotconfig.h
+++ lib/got_lib_gotconfig.h
@@ -22,6 +22,7 @@ struct got_gotconfig {
 	struct got_remote_repo *remotes;
 	char *allowed_signers_file;
 	char *revoked_signers_file;
+	char *signer_id;
 };
 
 const struct got_error *got_gotconfig_read(struct got_gotconfig **,
blob - dac4ab973b68243e262fd1ae6482fffb6dc2bc57
blob + 8b976a88e8422f5bba1012e0cdd2e3d205ed1a6a
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
@@ -174,6 +174,7 @@ enum got_imsg_type {
 	GOT_IMSG_GOTCONFIG_AUTHOR_REQUEST,
 	GOT_IMSG_GOTCONFIG_ALLOWEDSIGNERS_REQUEST,
 	GOT_IMSG_GOTCONFIG_REVOKEDSIGNERS_REQUEST,
+	GOT_IMSG_GOTCONFIG_SIGNERID_REQUEST,
 	GOT_IMSG_GOTCONFIG_REMOTES_REQUEST,
 	GOT_IMSG_GOTCONFIG_INT_VAL,
 	GOT_IMSG_GOTCONFIG_STR_VAL,
@@ -766,6 +767,8 @@ const struct got_error *got_privsep_send_gotconfig_all
     struct imsgbuf *);
 const struct got_error *got_privsep_send_gotconfig_revoked_signers_req(
     struct imsgbuf *);
+const struct got_error *got_privsep_send_gotconfig_signer_id_req(
+    struct imsgbuf *);
 const struct got_error *got_privsep_send_gotconfig_remotes_req(
     struct imsgbuf *);
 const struct got_error *got_privsep_recv_gotconfig_str(char **,
blob - 7fae8306f7aa444e25b71f0a95f8f151ec324a7f
blob + 8abcb57e5150fe88df32e79806a1dca197c5948f
--- lib/gotconfig.c
+++ lib/gotconfig.c
@@ -119,6 +119,14 @@ got_gotconfig_read(struct got_gotconfig **conf, const 
 	if (err)
 		goto done;
 
+	err = got_privsep_send_gotconfig_signer_id_req(ibuf);
+	if (err)
+		goto done;
+
+	err = got_privsep_recv_gotconfig_str(&(*conf)->signer_id, ibuf);
+	if (err)
+		goto done;
+
 	err = got_privsep_send_gotconfig_remotes_req(ibuf);
 	if (err)
 		goto done;
@@ -188,3 +196,9 @@ got_gotconfig_get_revoked_signers_file(const struct go
 {
 	return conf->revoked_signers_file;
 }
+
+const char *
+got_gotconfig_get_signer_id(const struct got_gotconfig *conf)
+{
+	return conf->signer_id;
+}
blob - 9a16d647b870eb31f58c9d131d1174c60e7d5eb1
blob + ee575dbf080d9b5e9f2a1019a20d281907f06c91
--- lib/privsep.c
+++ lib/privsep.c
@@ -2388,6 +2388,17 @@ got_privsep_send_gotconfig_revoked_signers_req(struct 
 }
 
 const struct got_error *
+got_privsep_send_gotconfig_signer_id_req(struct imsgbuf *ibuf)
+{
+	if (imsg_compose(ibuf,
+	    GOT_IMSG_GOTCONFIG_SIGNERID_REQUEST, 0, 0, -1, NULL, 0) == -1)
+		return got_error_from_errno("imsg_compose "
+		    "GOTCONFIG_SIGNERID_REQUEST");
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
 got_privsep_send_gotconfig_remotes_req(struct imsgbuf *ibuf)
 {
 	if (imsg_compose(ibuf,
blob - be0d93073a8d7779e487b6a2d12bad1e6c9721d4
blob + 45aaadb8bcd9513fe5075ceaa0fc416d03874885
--- libexec/got-read-gotconfig/got-read-gotconfig.c
+++ libexec/got-read-gotconfig/got-read-gotconfig.c
@@ -566,6 +566,14 @@ main(int argc, char *argv[])
 			    gotconfig->revoked_signers_file ?
 			        gotconfig->revoked_signers_file : "");
 			break;
+		case GOT_IMSG_GOTCONFIG_SIGNERID_REQUEST:
+			if (gotconfig == NULL) {
+				err = got_error(GOT_ERR_PRIVSEP_MSG);
+				break;
+			}
+			err = send_gotconfig_str(&ibuf,
+			    gotconfig->signer_id ? gotconfig->signer_id : "");
+			break;
 		case GOT_IMSG_GOTCONFIG_REMOTES_REQUEST:
 			if (gotconfig == NULL) {
 				err = got_error(GOT_ERR_PRIVSEP_MSG);
blob - 504e691250732f7b2baee47695fc1794127b2adb
blob + f055d6650038380fc13eda6741a0f4631132c03c
--- libexec/got-read-gotconfig/gotconfig.h
+++ libexec/got-read-gotconfig/gotconfig.h
@@ -69,6 +69,7 @@ struct gotconfig {
 	int nremotes;
 	char	*allowed_signers_file;
 	char	*revoked_signers_file;
+	char	*signer_id;
 };
 
 /*
blob - 6275b70b1ffab7e1c17bf5cc0aab761e56b04c66
blob + cf5be7e73168496c2b26cde6b459a1167864c846
--- libexec/got-read-gotconfig/parse.y
+++ libexec/got-read-gotconfig/parse.y
@@ -99,8 +99,8 @@ typedef struct {
 
 %token	ERROR
 %token	REMOTE REPOSITORY SERVER PORT PROTOCOL MIRROR_REFERENCES BRANCH
-%token	AUTHOR ALLOWED_SIGNERS REVOKED_SIGNERS FETCH_ALL_BRANCHES REFERENCE
-%token	FETCH SEND
+%token	AUTHOR ALLOWED_SIGNERS REVOKED_SIGNERS SIGNER_ID FETCH_ALL_BRANCHES
+%token	REFERENCE FETCH SEND
 %token	<v.string>	STRING
 %token	<v.number>	NUMBER
 %type	<v.number>	boolean portplain
@@ -116,6 +116,7 @@ grammar		: /* empty */
 		| grammar remote '\n'
 		| grammar allowed_signers '\n'
 		| grammar revoked_signers '\n'
+		| grammar signer_id '\n'
 		;
 boolean		: STRING {
 			if (strcasecmp($1, "true") == 0 ||
@@ -317,6 +318,10 @@ revoked_signers	: REVOKED_SIGNERS STRING {
 			gotconfig.revoked_signers_file = $2;
 		}
 		;
+signer_id	: SIGNER_ID STRING {
+			gotconfig.signer_id = $2;
+		}
+		;
 optnl		: '\n' optnl
 		| /* empty */
 		;
@@ -381,6 +386,7 @@ lookup(char *s)
 		{"revoked_signers",	REVOKED_SIGNERS},
 		{"send",		SEND},
 		{"server",		SERVER},
+		{"signer_id",		SIGNER_ID},
 	};
 	const struct keywords	*p;
 
@@ -808,6 +814,7 @@ gotconfig_free(struct gotconfig *conf)
 	free(conf->author);
 	free(conf->allowed_signers_file);
 	free(conf->revoked_signers_file);
+	free(conf->signer_id);
 	while (!TAILQ_EMPTY(&conf->remotes)) {
 		remote = TAILQ_FIRST(&conf->remotes);
 		TAILQ_REMOVE(&conf->remotes, remote, entry);
blob - 217af530810b91c6f6861398bf49b4c6f5403578
blob + 9fb2abf1b9d1f9a39caaece132ad5ba7e9fd8178
--- regress/cmdline/tag.sh
+++ regress/cmdline/tag.sh
@@ -262,6 +262,7 @@ test_tag_create_ssh_signed() {
 	local commit_id=`git_show_head $testroot/repo`
 	local tag=1.0.0
 	local tag2=2.0.0
+	local tag3=3.0.0
 
 	ssh-keygen -q -N '' -t ed25519 -f $testroot/id_ed25519
 	ret=$?
@@ -376,6 +377,17 @@ test_tag_create_ssh_signed() {
 	got tag -s $testroot/id_ed25519 -m 'test' -r $testroot/repo \
 		-c $commit_id $tag2 > $testroot/stdout
 
+	# Create another signed tag with key defined in got.conf(5)
+	echo "signer_id \"$testroot/id_ed25519\"" >> \
+		$testroot/repo/.git/got.conf
+	got tag -m 'test' -r $testroot/repo -c HEAD $tag3 > $testroot/stdout
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		echo "got tag command failed unexpectedly"
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
 	# got tag -V behaves like got tag -l, but with verification enabled.
 	got tag -l -r $testroot/repo > $testroot/stdout.list
 	got tag -V -r $testroot/repo > $testroot/stdout.verify
@@ -389,6 +401,10 @@ test_tag_create_ssh_signed() {
 	echo -n "+signature: $GOOD_SIG" >> $testroot/stdout.expected
 	ssh-keygen -l -f $testroot/id_ed25519.pub | cut -d' ' -f 2 \
 		>> $testroot/stdout.expected
+	echo "@@ -33,0 +36 @@" >> $testroot/stdout.expected
+	echo -n "+signature: $GOOD_SIG" >> $testroot/stdout.expected
+	ssh-keygen -l -f $testroot/id_ed25519.pub | cut -d' ' -f 2 \
+		>> $testroot/stdout.expected
 	cmp -s $testroot/stdout $testroot/stdout.expected
 	ret=$?
 	if [ $ret -ne 0 ]; then