commit ff5e1f096349d36a0ba772c0d78a21b2ab54e060 from: Josh Rickmar via: Thomas Adam date: Wed Jul 06 22:30:00 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. ok stsp@ commit - 8fe0cd3a6f849b803a9113fa3a2e06fad9f5315b commit + ff5e1f096349d36a0ba772c0d78a21b2ab54e060 blob - 527aac5ec1564ad8bda90d4d954008c453b86bbe blob + 3f0c7462ca5f91c506d50f1069da4df4835c65ec --- got/got.c +++ got/got.c @@ -703,6 +703,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"); @@ -7280,9 +7313,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) { @@ -7295,16 +7328,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; @@ -7437,6 +7476,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); @@ -7497,6 +7541,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 - d7a3b44f1263da90512579341f553502dc6d4a4e blob + 4fe2f0ffd0ca86387452b6b7ff95282358bcd66d --- 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 - 4263124d516f9667576c82c1dff455733d247f0d blob + 15c59da31d1d53d2ceac7f3f8184fb211f1f8403 --- lib/gotconfig.c +++ lib/gotconfig.c @@ -118,6 +118,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; @@ -187,3 +195,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 - e5c1a77dd9cbf5a1fbb32633f6161301ed5e1fc5 blob + c0e2e9215b1e69757313a926d8ca0e0408abd03f --- lib/privsep.c +++ lib/privsep.c @@ -2387,6 +2387,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 - a3485e939c9e5dbb2d7836e31c2630ff6aeeafe8 blob + b9052f22d3c1f55bf22de427ccf3adbeb24ef97b --- libexec/got-read-gotconfig/got-read-gotconfig.c +++ libexec/got-read-gotconfig/got-read-gotconfig.c @@ -576,6 +576,14 @@ main(int argc, char *argv[]) err = send_gotconfig_str(&ibuf, 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) { 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 - 394b87aa67d4fb52430e635f9143f621eb0a0d9a blob + 30138458acdf75220bb9572368ec826118f37f6f --- libexec/got-read-gotconfig/parse.y +++ libexec/got-read-gotconfig/parse.y @@ -104,8 +104,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 @@ -121,6 +121,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 || @@ -322,6 +323,10 @@ revoked_signers : REVOKED_SIGNERS STRING { gotconfig.revoked_signers_file = $2; } ; +signer_id : SIGNER_ID STRING { + gotconfig.signer_id = $2; + } + ; optnl : '\n' optnl | /* empty */ ; @@ -386,6 +391,7 @@ lookup(char *s) {"revoked_signers", REVOKED_SIGNERS}, {"send", SEND}, {"server", SERVER}, + {"signer_id", SIGNER_ID}, }; const struct keywords *p; @@ -813,6 +819,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 - d32b03f5eccb5ae355f4b16e488a44068c680b01 blob + d7bf186e4decab4cc4922f2dd6d76908101102bf --- regress/cmdline/tag.sh +++ regress/cmdline/tag.sh @@ -277,6 +277,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=$? @@ -383,7 +384,47 @@ test_tag_create_ssh_signed() { ret=$? if [ $ret -ne 0 ]; then echo "got checkout command failed unexpectedly" + test_done "$testroot" "$ret" + return 1 + fi + + # Create another signed tag with a SHA1 commit ID + 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 + diff -U0 $testroot/stdout.list $testroot/stdout.verify | + sed -e '/^--- /d' -e '/^+++ /d' > $testroot/stdout + echo "@@ -5,0 +6 @@" > $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 + echo "@@ -19,0 +21 @@" >> $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 + 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 + diff -u $testroot/stdout.expected $testroot/stdout + fi test_done "$testroot" "$ret" }