From: Josh Rickmar Subject: Re: add signer_id option to got.conf(5) To: gameoftrees@openbsd.org Date: Tue, 5 Jul 2022 11:06:21 -0400 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 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 STRING %token NUMBER %type 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