commit - 6c77e0337609d941281f017b3e0229ae45a35b7a
commit + 4d5ee9564a9e46a1f634f619833c62f636cfbdc1
blob - 8dfd844f3da472b6ed040a62acaf85403cbc07ea
blob + 1b45b53a4efff9977dcd3c2e2e33c499adc94533
--- got/Makefile
+++ got/Makefile
diff_myers.c diff_output.c diff_output_plain.c \
diff_output_unidiff.c diff_output_edscript.c \
diff_patience.c send.c deltify.c pack_create.c dial.c \
- bloom.c murmurhash2.c ratelimit.c patch.c
+ bloom.c murmurhash2.c ratelimit.c patch.c sigs.c date.c
MAN = ${PROG}.1 got-worktree.5 git-repository.5 got.conf.5
blob - 955f25359f4f5564010ae23da14cd2ad5b1c4b05
blob + 6feb1c3bbdd91673897061cb3ccbd9c80799d101
--- got/got.c
+++ got/got.c
#include "got_gotconfig.h"
#include "got_dial.h"
#include "got_patch.h"
+#include "got_sigs.h"
+#include "got_date.h"
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
*author = NULL;
}
return err;
+}
+
+static const struct got_error *
+get_allowed_signers(char **allowed_signers, struct got_repository *repo,
+ struct got_worktree *worktree)
+{
+ const char *got_allowed_signers = NULL;
+ const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL;
+
+ *allowed_signers = 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_allowed_signers = got_gotconfig_get_allowed_signers_file(
+ worktree_conf);
+ if (got_allowed_signers == NULL)
+ got_allowed_signers = got_gotconfig_get_allowed_signers_file(
+ repo_conf);
+
+ if (got_allowed_signers) {
+ *allowed_signers = strdup(got_allowed_signers);
+ if (*allowed_signers == NULL)
+ return got_error_from_errno("strdup");
+ }
+ return NULL;
+}
+
+static const struct got_error *
+get_revoked_signers(char **revoked_signers, struct got_repository *repo,
+ struct got_worktree *worktree)
+{
+ const char *got_revoked_signers = NULL;
+ const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL;
+
+ *revoked_signers = 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_revoked_signers = got_gotconfig_get_revoked_signers_file(
+ worktree_conf);
+ if (got_revoked_signers == NULL)
+ got_revoked_signers = got_gotconfig_get_revoked_signers_file(
+ repo_conf);
+
+ if (got_revoked_signers) {
+ *revoked_signers = strdup(got_revoked_signers);
+ if (*revoked_signers == NULL)
+ return got_error_from_errno("strdup");
+ }
+ return NULL;
}
static const struct got_error *
{
fprintf(stderr,
"usage: %s tag [-c commit] [-r repository] [-l] "
- "[-m message] name\n", getprogname());
+ "[-m message] [-s signer_id] name\n",
+ getprogname());
exit(1);
}
}
static const struct got_error *
-list_tags(struct got_repository *repo, const char *tag_name)
+list_tags(struct got_repository *repo, const char *tag_name, int verify_tags,
+ const char *allowed_signers, const char *revoked_signers, int verbosity)
{
static const struct got_error *err = NULL;
struct got_reflist_head refs;
struct got_reflist_entry *re;
char *wanted_refname = NULL;
+ int bad_sigs = 0;
TAILQ_INIT(&refs);
const char *refname;
char *refstr, *tagmsg0, *tagmsg, *line, *id_str, *datestr;
char datebuf[26];
- const char *tagger;
+ const char *tagger, *ssh_sig = NULL;
+ char *sig_msg = NULL;
time_t tagger_time;
struct got_object_id *id;
struct got_tag_object *tag;
err = got_error_from_errno("got_ref_to_str");
break;
}
- printf("%stag %s %s\n", GOT_COMMIT_SEP_STR, refname, refstr);
- free(refstr);
err = got_ref_resolve(&id, repo, re->ref);
if (err)
if (err)
break;
}
+
+ if (verify_tags) {
+ ssh_sig = got_sigs_get_tagmsg_ssh_signature(
+ got_object_tag_get_message(tag));
+ if (ssh_sig && allowed_signers == NULL) {
+ err = got_error_msg(
+ GOT_ERR_VERIFY_TAG_SIGNATURE,
+ "SSH signature verification requires "
+ "setting allowed_signers in "
+ "got.conf(5)");
+ break;
+ }
+ }
+
+ printf("%stag %s %s\n", GOT_COMMIT_SEP_STR, refname, refstr);
+ free(refstr);
printf("from: %s\n", tagger);
datestr = get_datestr(&tagger_time, datebuf);
if (datestr)
}
}
free(id_str);
+
+ if (ssh_sig) {
+ err = got_sigs_verify_tag_ssh(&sig_msg, tag, ssh_sig,
+ allowed_signers, revoked_signers, verbosity);
+ if (err && err->code == GOT_ERR_BAD_TAG_SIGNATURE)
+ bad_sigs = 1;
+ else if (err)
+ break;
+ printf("signature: %s", sig_msg);
+ free(sig_msg);
+ sig_msg = NULL;
+ }
+
if (commit) {
err = got_object_commit_get_logmsg(&tagmsg0, commit);
if (err)
done:
got_ref_list_free(&refs);
free(wanted_refname);
+
+ if (err == NULL && bad_sigs)
+ err = got_error(GOT_ERR_BAD_TAG_SIGNATURE);
return err;
}
if (fd != -1 && close(fd) == -1 && err == NULL)
err = got_error_from_errno2("close", *tagmsg_path);
- /* Editor is done; we can now apply unveil(2) */
- if (err == NULL)
- err = apply_unveil(repo_path, 0, NULL);
if (err) {
free(*tagmsg);
*tagmsg = NULL;
static const struct got_error *
add_tag(struct got_repository *repo, const char *tagger,
- const char *tag_name, const char *commit_arg, const char *tagmsg_arg)
+ const char *tag_name, const char *commit_arg, const char *tagmsg_arg,
+ const char *key_file, int verbosity)
{
const struct got_error *err = NULL;
struct got_object_id *commit_id = NULL, *tag_id = NULL;
preserve_tagmsg = 1;
goto done;
}
+ /* Editor is done; we can now apply unveil(2) */
+ err = got_sigs_apply_unveil();
+ if (err)
+ goto done;
+ err = apply_unveil(got_repo_get_path(repo), 0, NULL);
+ if (err)
+ goto done;
}
err = got_object_tag_create(&tag_id, tag_name, commit_id,
- tagger, time(NULL), tagmsg ? tagmsg : tagmsg_arg, repo);
+ tagger, time(NULL), tagmsg ? tagmsg : tagmsg_arg, key_file, repo,
+ verbosity);
if (err) {
if (tagmsg_path)
preserve_tagmsg = 1;
struct got_worktree *worktree = NULL;
char *cwd = NULL, *repo_path = NULL, *commit_id_str = NULL;
char *gitconfig_path = NULL, *tagger = NULL;
+ char *allowed_signers = NULL, *revoked_signers = NULL;
const char *tag_name = NULL, *commit_id_arg = NULL, *tagmsg = NULL;
- int ch, do_list = 0;
+ 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:l")) != -1) {
+ while ((ch = getopt(argc, argv, "c:m:r:ls:Vv")) != -1) {
switch (ch) {
case 'c':
commit_id_arg = optarg;
case 'l':
do_list = 1;
break;
+ case 's':
+ signer_id = optarg;
+ break;
+ case 'V':
+ verify_tags = 1;
+ break;
+ case 'v':
+ if (verbosity < 0)
+ verbosity = 0;
+ else if (verbosity < 3)
+ verbosity++;
+ break;
default:
usage_tag();
/* NOTREACHED */
}
}
- if (do_list) {
+ if (do_list || verify_tags) {
+ error = got_repo_open(&repo, repo_path, NULL, pack_fds);
+ if (error != NULL)
+ goto done;
+ error = get_allowed_signers(&allowed_signers, repo, worktree);
+ if (error)
+ goto done;
+ error = get_revoked_signers(&revoked_signers, repo, worktree);
+ if (error)
+ goto done;
if (worktree) {
/* Release work tree lock. */
got_worktree_close(worktree);
worktree = NULL;
}
- error = got_repo_open(&repo, repo_path, NULL, pack_fds);
- if (error != NULL)
- goto done;
+ /*
+ * Remove "cpath" promise unless needed for signature tmpfile
+ * creation.
+ */
+ if (verify_tags)
+ got_sigs_apply_unveil();
+ else {
#ifndef PROFILE
- /* Remove "cpath" promise. */
- if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
- NULL) == -1)
- err(1, "pledge");
+ if (pledge("stdio rpath wpath flock proc exec sendfd "
+ "unveil", NULL) == -1)
+ err(1, "pledge");
#endif
+ }
error = apply_unveil(got_repo_get_path(repo), 1, NULL);
if (error)
goto done;
- error = list_tags(repo, tag_name);
+ error = list_tags(repo, tag_name, verify_tags, allowed_signers,
+ revoked_signers, verbosity);
} else {
error = get_gitconfig_path(&gitconfig_path);
if (error)
}
if (tagmsg) {
+ if (signer_id) {
+ error = got_sigs_apply_unveil();
+ if (error)
+ goto done;
+ }
error = apply_unveil(got_repo_get_path(repo), 0, NULL);
if (error)
goto done;
}
error = add_tag(repo, tagger, tag_name,
- commit_id_str ? commit_id_str : commit_id_arg, tagmsg);
+ commit_id_str ? commit_id_str : commit_id_arg, tagmsg,
+ signer_id, verbosity);
}
done:
if (repo) {
free(gitconfig_path);
free(commit_id_str);
free(tagger);
+ free(allowed_signers);
+ free(revoked_signers);
return error;
}
got_object_tree_close(tree);
return err;
}
-
-static void
-format_gmtoff(char *buf, size_t sz, time_t gmtoff)
-{
- long long h, m;
- char sign = '+';
- if (gmtoff < 0) {
- sign = '-';
- gmtoff = -gmtoff;
- }
-
- h = (long long)gmtoff / 3600;
- m = ((long long)gmtoff - h*3600) / 60;
- snprintf(buf, sz, "%c%02lld%02lld", sign, h, m);
-}
-
static const struct got_error *
cat_commit(struct got_object_id *id, struct got_repository *repo, FILE *outfile)
{
fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_PARENT, pid_str);
free(pid_str);
}
- format_gmtoff(gmtoff, sizeof(gmtoff),
+ got_date_format_gmtoff(gmtoff, sizeof(gmtoff),
got_object_commit_get_author_gmtoff(commit));
fprintf(outfile, "%s%s %lld %s\n", GOT_COMMIT_LABEL_AUTHOR,
got_object_commit_get_author(commit),
(long long)got_object_commit_get_author_time(commit),
gmtoff);
- format_gmtoff(gmtoff, sizeof(gmtoff),
+ got_date_format_gmtoff(gmtoff, sizeof(gmtoff),
got_object_commit_get_committer_gmtoff(commit));
fprintf(outfile, "%s%s %lld %s\n", GOT_COMMIT_LABEL_COMMITTER,
got_object_commit_get_author(commit),
fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TAG,
got_object_tag_get_name(tag));
- format_gmtoff(gmtoff, sizeof(gmtoff),
+ got_date_format_gmtoff(gmtoff, sizeof(gmtoff),
got_object_tag_get_tagger_gmtoff(tag));
fprintf(outfile, "%s%s %lld %s\n", GOT_TAG_LABEL_TAGGER,
got_object_tag_get_tagger(tag),
blob - 781133bbc9837ad999231c521ae9da3239c0232b
blob + bf9729a9216142455edfd253fb05cd98c0b4b1f1
--- gotadmin/Makefile
+++ gotadmin/Makefile
inflate.c lockfile.c object.c object_cache.c object_create.c \
object_idset.c object_parse.c opentemp.c pack.c pack_create.c \
path.c privsep.c reference.c repository.c repository_admin.c \
- worktree_open.c sha1.c bloom.c murmurhash2.c ratelimit.c
+ worktree_open.c sha1.c bloom.c murmurhash2.c ratelimit.c \
+ sigs.c buf.c date.c
MAN = ${PROG}.1
CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib
blob - aa54a17419d407bb09bbc7b00af392c74aa8801f
blob + 948b9b8fc5270878f0eb2aa61a579197663e4827
--- gotweb/Makefile
+++ gotweb/Makefile
diff_main.c diff_atomize_text.c diff_myers.c diff_output.c \
diff_output_plain.c diff_output_unidiff.c \
diff_output_edscript.c diff_patience.c \
- bloom.c murmurhash2.c
+ bloom.c murmurhash2.c sigs.c date.c
MAN = ${PROG}.conf.5 ${PROG}.8
CPPFLAGS += -I${.CURDIR}/../include -I${.CURDIR}/../lib -I${.CURDIR} \
blob - /dev/null
blob + b005c2c948e0b4b35147550b1b23fef240ddf8b4 (mode 644)
--- /dev/null
+++ include/got_date.h
+/*
+ * Copyright (c) 2022 Josh Rickmar <jrick@zettaport.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+void
+got_date_format_gmtoff(char *, size_t, time_t);
blob - 22a9264b9f8d0c0b20b48895dd8ea59708e61d48
blob + 4bfaed588e2d81e1b0578a939dd96553de6bc11b
--- include/got_error.h
+++ include/got_error.h
#define GOT_ERR_PATCH_FAILED 151
#define GOT_ERR_FILEIDX_DUP_ENTRY 152
#define GOT_ERR_PIN_PACK 153
+#define GOT_ERR_BAD_TAG_SIGNATURE 154
+#define GOT_ERR_VERIFY_TAG_SIGNATURE 155
struct got_error {
int code;
blob - 3dbe5d7d43cf45ec0e7997d43f266c3ce0c9fcbe
blob + 26e15d93b91bc42ee028fa8ecf60a8d1ac4dfdc9
--- include/got_gotconfig.h
+++ include/got_gotconfig.h
*/
void got_gotconfig_get_remotes(int *, const struct got_remote_repo **,
const struct got_gotconfig *);
+
+/*
+ * Obtain the filename of the allowed signers file.
+ * Returns NULL if no configuration file is found or no allowed signers file
+ * is configured.
+ */
+const char *
+got_gotconfig_get_allowed_signers_file(const struct got_gotconfig *);
+
+/*
+ * Obtain the filename of the revoked signers file.
+ * Returns NULL if no configuration file is found or no revoked signers file
+ * is configured.
+ */
+const char *
+got_gotconfig_get_revoked_signers_file(const struct got_gotconfig *);
blob - a8d0318ceaa7152627e8c8718ba039f8517bc3e4
blob + 1cd6f349912d3e03ebbdccfd4beeeb54663af7fb
--- include/got_object.h
+++ include/got_object.h
/* Create a new tag object in the repository. */
const struct got_error *got_object_tag_create(struct got_object_id **,
const char *, struct got_object_id *, const char *,
- time_t, const char *, struct got_repository *);
+ time_t, const char *, const char *, struct got_repository *, int verbosity);
blob - /dev/null
blob + 204a6265963d6dcbf4d6f3de13f4bdbafaafc6fa (mode 644)
--- /dev/null
+++ include/got_sigs.h
+/*
+ * Copyright (c) 2022 Josh Rickmar <jrick@zettaport.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+const struct got_error *
+got_sigs_apply_unveil(void);
+
+const struct got_error *
+got_sigs_sign_tag_ssh(pid_t *, int *, int *, const char *, int);
+
+const char *
+got_sigs_get_tagmsg_ssh_signature(const char *);
+
+const struct got_error *
+got_sigs_verify_tag_ssh(char **, struct got_tag_object *, const char *,
+ const char *, const char *, int);
blob - /dev/null
blob + 815b291ce868d18136ce8f45fa2f890b6f6c08f9 (mode 644)
--- /dev/null
+++ lib/date.c
+/*
+ * Copyright (c) 2022 Josh Rickmar <jrick@zettaport.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include "got_date.h"
+
+void
+got_date_format_gmtoff(char *buf, size_t sz, time_t gmtoff)
+{
+ long long h, m;
+ char sign = '+';
+
+ if (gmtoff < 0) {
+ sign = '-';
+ gmtoff = -gmtoff;
+ }
+
+ h = (long long)gmtoff / 3600;
+ m = ((long long)gmtoff - h*3600) / 60;
+ snprintf(buf, sz, "%c%02lld%02lld", sign, h, m);
+}
blob - 3ffd653ef429fab490d06ba6a953185254e7c117
blob + 3c092e61bab70845c184eb10f359eb6df3ee01ce
--- lib/error.c
+++ lib/error.c
{ GOT_ERR_PATCH_FAILED, "patch failed to apply" },
{ GOT_ERR_FILEIDX_DUP_ENTRY, "duplicate file index entry" },
{ GOT_ERR_PIN_PACK, "could not pin pack file" },
+ { GOT_ERR_BAD_TAG_SIGNATURE, "invalid tag signature" },
+ { GOT_ERR_VERIFY_TAG_SIGNATURE, "cannot verify signature" },
};
static struct got_custom_error {
blob - 5e02aa1efeff0dd226e617da410a4663d8376d9a
blob + 39337ed4d9cbe7dfa5939b3f4dcb38793ccddfbd
--- lib/got_lib_gotconfig.h
+++ lib/got_lib_gotconfig.h
char *author;
int nremotes;
struct got_remote_repo *remotes;
+ char *allowed_signers_file;
+ char *revoked_signers_file;
};
const struct got_error *got_gotconfig_read(struct got_gotconfig **,
blob - 6ffe646e98676cf9a0d19fe3ad27f3e63ab04fcc
blob + dac4ab973b68243e262fd1ae6482fffb6dc2bc57
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
/* Messages related to gotconfig files. */
GOT_IMSG_GOTCONFIG_PARSE_REQUEST,
GOT_IMSG_GOTCONFIG_AUTHOR_REQUEST,
+ GOT_IMSG_GOTCONFIG_ALLOWEDSIGNERS_REQUEST,
+ GOT_IMSG_GOTCONFIG_REVOKEDSIGNERS_REQUEST,
GOT_IMSG_GOTCONFIG_REMOTES_REQUEST,
GOT_IMSG_GOTCONFIG_INT_VAL,
GOT_IMSG_GOTCONFIG_STR_VAL,
const struct got_error *got_privsep_send_gotconfig_parse_req(struct imsgbuf *,
int);
const struct got_error *got_privsep_send_gotconfig_author_req(struct imsgbuf *);
+const struct got_error *got_privsep_send_gotconfig_allowed_signers_req(
+ struct imsgbuf *);
+const struct got_error *got_privsep_send_gotconfig_revoked_signers_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 - 5b602c9f5513aee64b98ca608535d5b85280ec42
blob + 7fae8306f7aa444e25b71f0a95f8f151ec324a7f
--- lib/gotconfig.c
+++ lib/gotconfig.c
if (err)
goto done;
+ err = got_privsep_send_gotconfig_allowed_signers_req(ibuf);
+ if (err)
+ goto done;
+
+ err = got_privsep_recv_gotconfig_str(&(*conf)->allowed_signers_file,
+ ibuf);
+ if (err)
+ goto done;
+
+ err = got_privsep_send_gotconfig_revoked_signers_req(ibuf);
+ if (err)
+ goto done;
+
+ err = got_privsep_recv_gotconfig_str(&(*conf)->revoked_signers_file,
+ ibuf);
+ if (err)
+ goto done;
+
err = got_privsep_send_gotconfig_remotes_req(ibuf);
if (err)
goto done;
*nremotes = conf->nremotes;
*remotes = conf->remotes;
}
+
+const char *
+got_gotconfig_get_allowed_signers_file(const struct got_gotconfig *conf)
+{
+ return conf->allowed_signers_file;
+}
+
+const char *
+got_gotconfig_get_revoked_signers_file(const struct got_gotconfig *conf)
+{
+ return conf->revoked_signers_file;
+}
blob - 5036de1b9a6b491a1fc7c0358a03dcd9574f6cf3
blob + 8f33d6ba0309e1d8f43ef3b0c35b661bd6045211
--- lib/object_create.c
+++ lib/object_create.c
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/queue.h>
+#include <sys/wait.h>
#include <ctype.h>
#include <errno.h>
#include "got_repository.h"
#include "got_opentemp.h"
#include "got_path.h"
+#include "got_sigs.h"
#include "got_lib_sha1.h"
#include "got_lib_deflate.h"
#include "got_lib_lockfile.h"
#include "got_lib_object_create.h"
+
+#include "buf.h"
#ifndef nitems
#define nitems(_a) (sizeof(_a) / sizeof((_a)[0]))
const struct got_error *
got_object_tag_create(struct got_object_id **id,
const char *tag_name, struct got_object_id *object_id, const char *tagger,
- time_t tagger_time, const char *tagmsg, struct got_repository *repo)
+ time_t tagger_time, const char *tagmsg, const char *key_file,
+ struct got_repository *repo, int verbosity)
{
const struct got_error *err = NULL;
SHA1_CTX sha1_ctx;
char *header = NULL;
char *tag_str = NULL, *tagger_str = NULL;
char *id_str = NULL, *obj_str = NULL, *type_str = NULL;
- size_t headerlen, len = 0, n;
+ size_t headerlen, len = 0, sig_len = 0, n;
FILE *tagfile = NULL;
off_t tagsize = 0;
char *msg0 = NULL, *msg;
const char *obj_type_str;
int obj_type;
+ BUF *buf = NULL;
*id = NULL;
while (isspace((unsigned char)msg[0]))
msg++;
- len = strlen(obj_str) + strlen(type_str) + strlen(tag_str) +
- strlen(tagger_str) + 1 + strlen(msg) + 1;
+ if (key_file) {
+ FILE *out;
+ pid_t pid;
+ size_t len;
+ int in_fd, out_fd;
+ int status;
+
+ err = buf_alloc(&buf, 0);
+ if (err)
+ goto done;
+
+ /* signed message */
+ err = buf_puts(&len, buf, obj_str);
+ if (err)
+ goto done;
+ err = buf_puts(&len, buf, type_str);
+ if (err)
+ goto done;
+ err = buf_puts(&len, buf, tag_str);
+ if (err)
+ goto done;
+ err = buf_puts(&len, buf, tagger_str);
+ if (err)
+ goto done;
+ err = buf_putc(buf, '\n');
+ if (err)
+ goto done;
+ err = buf_puts(&len, buf, msg);
+ if (err)
+ goto done;
+ err = buf_putc(buf, '\n');
+ if (err)
+ goto done;
+ err = got_sigs_sign_tag_ssh(&pid, &in_fd, &out_fd, key_file,
+ verbosity);
+ if (err)
+ goto done;
+ if (buf_write_fd(buf, in_fd) == -1) {
+ err = got_error_from_errno("write");
+ goto done;
+ }
+ if (close(in_fd) == -1) {
+ err = got_error_from_errno("close");
+ goto done;
+ }
+
+ if (waitpid(pid, &status, 0) == -1) {
+ err = got_error_from_errno("waitpid");
+ goto done;
+ }
+
+ out = fdopen(out_fd, "r");
+ if (out == NULL) {
+ err = got_error_from_errno("fdopen");
+ goto done;
+ }
+ buf_empty(buf);
+ err = buf_load(&buf, out);
+ if (err)
+ goto done;
+ sig_len = buf_len(buf) + 1;
+ err = buf_putc(buf, '\0');
+ if (err)
+ goto done;
+ if (close(out_fd) == -1) {
+ err = got_error_from_errno("close");
+ goto done;
+ }
+ }
+
+ len = strlen(obj_str) + strlen(type_str) + strlen(tag_str) +
+ strlen(tagger_str) + 1 + strlen(msg) + 1 + sig_len;
if (asprintf(&header, "%s %zd", GOT_OBJ_LABEL_TAG, len) == -1) {
err = got_error_from_errno("asprintf");
goto done;
}
tagsize += n;
+ if (key_file && buf_len(buf) > 0) {
+ len = buf_len(buf);
+ SHA1Update(&sha1_ctx, buf_get(buf), len);
+ n = fwrite(buf_get(buf), 1, len, tagfile);
+ if (n != len) {
+ err = got_ferror(tagfile, GOT_ERR_IO);
+ goto done;
+ }
+ tagsize += n;
+ }
+
*id = malloc(sizeof(**id));
if (*id == NULL) {
err = got_error_from_errno("malloc");
free(header);
free(obj_str);
free(tagger_str);
+ if (buf)
+ buf_release(buf);
if (tagfile && fclose(tagfile) == EOF && err == NULL)
err = got_error_from_errno("fclose");
if (err) {
blob - 5655e967b5d1a0320fe3e1ef6184b52da9adc4d8
blob + 9a16d647b870eb31f58c9d131d1174c60e7d5eb1
--- lib/privsep.c
+++ lib/privsep.c
GOT_IMSG_GOTCONFIG_AUTHOR_REQUEST, 0, 0, -1, NULL, 0) == -1)
return got_error_from_errno("imsg_compose "
"GOTCONFIG_AUTHOR_REQUEST");
+
+ return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_send_gotconfig_allowed_signers_req(struct imsgbuf *ibuf)
+{
+ if (imsg_compose(ibuf,
+ GOT_IMSG_GOTCONFIG_ALLOWEDSIGNERS_REQUEST, 0, 0, -1, NULL, 0) == -1)
+ return got_error_from_errno("imsg_compose "
+ "GOTCONFIG_ALLOWEDSIGNERS_REQUEST");
+
+ return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_send_gotconfig_revoked_signers_req(struct imsgbuf *ibuf)
+{
+ if (imsg_compose(ibuf,
+ GOT_IMSG_GOTCONFIG_REVOKEDSIGNERS_REQUEST, 0, 0, -1, NULL, 0) == -1)
+ return got_error_from_errno("imsg_compose "
+ "GOTCONFIG_REVOKEDSIGNERS_REQUEST");
return flush_imsg(ibuf);
}
blob - /dev/null
blob + 0b04e3f959ed7aab07534ee9bb5cb4a63e0dd93f (mode 644)
--- /dev/null
+++ lib/sigs.c
+/*
+ * Copyright (c) 2022 Josh Rickmar <jrick@zettaport.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/wait.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <err.h>
+#include <assert.h>
+#include <sha1.h>
+
+#include "got_error.h"
+#include "got_date.h"
+#include "got_object.h"
+#include "got_opentemp.h"
+
+#include "got_sigs.h"
+
+#include "buf.h"
+
+#ifndef MIN
+#define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
+#endif
+
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+#ifndef GOT_TAG_PATH_SSH_KEYGEN
+#define GOT_TAG_PATH_SSH_KEYGEN "/usr/bin/ssh-keygen"
+#endif
+
+#ifndef GOT_TAG_PATH_SIGNIFY
+#define GOT_TAG_PATH_SIGNIFY "/usr/bin/signify"
+#endif
+
+const struct got_error *
+got_sigs_apply_unveil()
+{
+ if (unveil(GOT_TAG_PATH_SSH_KEYGEN, "x") != 0) {
+ return got_error_from_errno2("unveil",
+ GOT_TAG_PATH_SSH_KEYGEN);
+ }
+ if (unveil(GOT_TAG_PATH_SIGNIFY, "x") != 0) {
+ return got_error_from_errno2("unveil",
+ GOT_TAG_PATH_SIGNIFY);
+ }
+
+ return NULL;
+}
+
+const struct got_error *
+got_sigs_sign_tag_ssh(pid_t *newpid, int *in_fd, int *out_fd,
+ const char* key_file, int verbosity)
+{
+ const struct got_error *error = NULL;
+ int pid, in_pfd[2], out_pfd[2];
+ const char* argv[11];
+ int i = 0, j;
+
+ *newpid = -1;
+ *in_fd = -1;
+ *out_fd = -1;
+
+ argv[i++] = GOT_TAG_PATH_SSH_KEYGEN;
+ argv[i++] = "-Y";
+ argv[i++] = "sign";
+ argv[i++] = "-f";
+ argv[i++] = key_file;
+ argv[i++] = "-n";
+ argv[i++] = "git";
+ if (verbosity <= 0) {
+ argv[i++] = "-q";
+ } else {
+ /* ssh(1) allows up to 3 "-v" options. */
+ for (j = 0; j < MIN(3, verbosity); j++)
+ argv[i++] = "-v";
+ }
+ argv[i++] = NULL;
+ assert(i <= nitems(argv));
+
+ if (pipe2(in_pfd, 0) == -1)
+ return got_error_from_errno("pipe2");
+ if (pipe2(out_pfd, 0) == -1)
+ return got_error_from_errno("pipe2");
+
+ pid = fork();
+ if (pid == -1) {
+ error = got_error_from_errno("fork");
+ close(in_pfd[0]);
+ close(in_pfd[1]);
+ close(out_pfd[0]);
+ close(out_pfd[1]);
+ return error;
+ } else if (pid == 0) {
+ if (close(in_pfd[1]) == -1)
+ err(1, "close");
+ if (close(out_pfd[1]) == -1)
+ err(1, "close");
+ if (dup2(in_pfd[0], 0) == -1)
+ err(1, "dup2");
+ if (dup2(out_pfd[0], 1) == -1)
+ err(1, "dup2");
+ if (execv(GOT_TAG_PATH_SSH_KEYGEN, (char **const)argv) == -1)
+ err(1, "execv");
+ abort(); /* not reached */
+ }
+ if (close(in_pfd[0]) == -1)
+ return got_error_from_errno("close");
+ if (close(out_pfd[0]) == -1)
+ return got_error_from_errno("close");
+ *newpid = pid;
+ *in_fd = in_pfd[1];
+ *out_fd = out_pfd[1];
+ return NULL;
+}
+
+static char *
+signer_identity(const char *tagger)
+{
+ char *lt, *gt;
+
+ lt = strstr(tagger, " <");
+ gt = strrchr(tagger, '>');
+ if (lt && gt && lt+1 < gt)
+ return strndup(lt+2, gt-lt-2);
+ return NULL;
+}
+
+static const char* BEGIN_SSH_SIG = "-----BEGIN SSH SIGNATURE-----\n";
+static const char* END_SSH_SIG = "-----END SSH SIGNATURE-----\n";
+
+const char *
+got_sigs_get_tagmsg_ssh_signature(const char *tagmsg)
+{
+ const char *s = tagmsg, *begin = NULL, *end = NULL;
+
+ while ((s = strstr(s, BEGIN_SSH_SIG)) != NULL) {
+ begin = s;
+ s += strlen(BEGIN_SSH_SIG);
+ }
+ if (begin)
+ end = strstr(begin+strlen(BEGIN_SSH_SIG), END_SSH_SIG);
+ if (end == NULL)
+ return NULL;
+ return (end[strlen(END_SSH_SIG)] == '\0') ? begin : NULL;
+}
+
+static const struct got_error *
+got_tag_write_signed_data(BUF *buf, struct got_tag_object *tag,
+ const char *start_sig)
+{
+ const struct got_error *err = NULL;
+ struct got_object_id *id;
+ char *id_str = NULL;
+ char *tagger = NULL;
+ const char *tagmsg;
+ char gmtoff[6];
+ size_t len;
+
+ id = got_object_tag_get_object_id(tag);
+ err = got_object_id_str(&id_str, id);
+ if (err)
+ goto done;
+
+ const char *type_label = NULL;
+ switch (got_object_tag_get_object_type(tag)) {
+ case GOT_OBJ_TYPE_BLOB:
+ type_label = GOT_OBJ_LABEL_BLOB;
+ break;
+ case GOT_OBJ_TYPE_TREE:
+ type_label = GOT_OBJ_LABEL_TREE;
+ break;
+ case GOT_OBJ_TYPE_COMMIT:
+ type_label = GOT_OBJ_LABEL_COMMIT;
+ break;
+ case GOT_OBJ_TYPE_TAG:
+ type_label = GOT_OBJ_LABEL_TAG;
+ break;
+ default:
+ break;
+ }
+ got_date_format_gmtoff(gmtoff, sizeof(gmtoff),
+ got_object_tag_get_tagger_gmtoff(tag));
+ if (asprintf(&tagger, "%s %lld %s", got_object_tag_get_tagger(tag),
+ got_object_tag_get_tagger_time(tag), gmtoff) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+
+ err = buf_puts(&len, buf, GOT_TAG_LABEL_OBJECT);
+ if (err)
+ goto done;
+ err = buf_puts(&len, buf, id_str);
+ if (err)
+ goto done;
+ err = buf_putc(buf, '\n');
+ if (err)
+ goto done;
+ err = buf_puts(&len, buf, GOT_TAG_LABEL_TYPE);
+ if (err)
+ goto done;
+ err = buf_puts(&len, buf, type_label);
+ if (err)
+ goto done;
+ err = buf_putc(buf, '\n');
+ if (err)
+ goto done;
+ err = buf_puts(&len, buf, GOT_TAG_LABEL_TAG);
+ if (err)
+ goto done;
+ err = buf_puts(&len, buf, got_object_tag_get_name(tag));
+ if (err)
+ goto done;
+ err = buf_putc(buf, '\n');
+ if (err)
+ goto done;
+ err = buf_puts(&len, buf, GOT_TAG_LABEL_TAGGER);
+ if (err)
+ goto done;
+ err = buf_puts(&len, buf, tagger);
+ if (err)
+ goto done;
+ err = buf_puts(&len, buf, "\n");
+ if (err)
+ goto done;
+ tagmsg = got_object_tag_get_message(tag);
+ err = buf_append(&len, buf, tagmsg, start_sig-tagmsg);
+ if (err)
+ goto done;
+
+done:
+ free(id_str);
+ free(tagger);
+ return err;
+}
+
+const struct got_error *
+got_sigs_verify_tag_ssh(char **msg, struct got_tag_object *tag,
+ const char *start_sig, const char* allowed_signers, const char* revoked,
+ int verbosity)
+{
+ const struct got_error *error = NULL;
+ const char* argv[17];
+ int pid, status, in_pfd[2], out_pfd[2];
+ char* parsed_identity = NULL;
+ const char *identity;
+ char* tmppath = NULL;
+ FILE *tmpsig, *out = NULL;
+ BUF *buf;
+ int i = 0, j;
+
+ *msg = NULL;
+
+ error = got_opentemp_named(&tmppath, &tmpsig,
+ GOT_TMPDIR_STR "/got-tagsig");
+ if (error)
+ goto done;
+
+ identity = got_object_tag_get_tagger(tag);
+ parsed_identity = signer_identity(identity);
+ if (parsed_identity != NULL)
+ identity = parsed_identity;
+
+ if (fputs(start_sig, tmpsig) == EOF) {
+ error = got_error_from_errno("fputs");
+ goto done;
+ }
+ if (fflush(tmpsig) == EOF) {
+ error = got_error_from_errno("fflush");
+ goto done;
+ }
+
+ error = buf_alloc(&buf, 0);
+ if (error)
+ goto done;
+ error = got_tag_write_signed_data(buf, tag, start_sig);
+ if (error)
+ goto done;
+
+ argv[i++] = GOT_TAG_PATH_SSH_KEYGEN;
+ argv[i++] = "-Y";
+ argv[i++] = "verify";
+ argv[i++] = "-f";
+ argv[i++] = allowed_signers;
+ argv[i++] = "-I";
+ argv[i++] = identity;
+ argv[i++] = "-n";
+ argv[i++] = "git";
+ argv[i++] = "-s";
+ argv[i++] = tmppath;
+ if (revoked) {
+ argv[i++] = "-r";
+ argv[i++] = revoked;
+ }
+ if (verbosity > 0) {
+ /* ssh(1) allows up to 3 "-v" options. */
+ for (j = 0; j < MIN(3, verbosity); j++)
+ argv[i++] = "-v";
+ }
+ argv[i++] = NULL;
+ assert(i <= nitems(argv));
+
+ if (pipe2(in_pfd, 0) == -1) {
+ error = got_error_from_errno("pipe2");
+ goto done;
+ }
+ if (pipe2(out_pfd, 0) == -1) {
+ error = got_error_from_errno("pipe2");
+ goto done;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ error = got_error_from_errno("fork");
+ close(in_pfd[0]);
+ close(in_pfd[1]);
+ close(out_pfd[0]);
+ close(out_pfd[1]);
+ return error;
+ } else if (pid == 0) {
+ if (close(in_pfd[1]) == -1)
+ err(1, "close");
+ if (close(out_pfd[1]) == -1)
+ err(1, "close");
+ if (dup2(in_pfd[0], 0) == -1)
+ err(1, "dup2");
+ if (dup2(out_pfd[0], 1) == -1)
+ err(1, "dup2");
+ if (execv(GOT_TAG_PATH_SSH_KEYGEN, (char **const)argv) == -1)
+ err(1, "execv");
+ abort(); /* not reached */
+ }
+ if (close(in_pfd[0]) == -1) {
+ error = got_error_from_errno("close");
+ goto done;
+ }
+ if (close(out_pfd[0]) == -1) {
+ error = got_error_from_errno("close");
+ goto done;
+ }
+ if (buf_write_fd(buf, in_pfd[1]) == -1) {
+ error = got_error_from_errno("write");
+ goto done;
+ }
+ if (close(in_pfd[1]) == -1) {
+ error = got_error_from_errno("close");
+ goto done;
+ }
+ if (waitpid(pid, &status, 0) == -1) {
+ error = got_error_from_errno("waitpid");
+ goto done;
+ }
+ if (!WIFEXITED(status)) {
+ error = got_error(GOT_ERR_BAD_TAG_SIGNATURE);
+ goto done;
+ }
+
+ out = fdopen(out_pfd[1], "r");
+ if (out == NULL) {
+ error = got_error_from_errno("fdopen");
+ goto done;
+ }
+ error = buf_load(&buf, out);
+ if (error)
+ goto done;
+ error = buf_putc(buf, '\0');
+ if (error)
+ goto done;
+ if (close(out_pfd[1]) == -1) {
+ error = got_error_from_errno("close");
+ goto done;
+ }
+ out = NULL;
+ *msg = buf_get(buf);
+ if (WEXITSTATUS(status) != 0)
+ error = got_error(GOT_ERR_BAD_TAG_SIGNATURE);
+
+done:
+ free(parsed_identity);
+ free(tmppath);
+ if (tmpsig && fclose(tmpsig) == EOF && error == NULL)
+ error = got_error_from_errno("fclose");
+ if (out && fclose(out) == EOF && error == NULL)
+ error = got_error_from_errno("fclose");
+ return error;
+}
blob - aa2c97552358174249a7361aba78c785626d6b7f
blob + be0d93073a8d7779e487b6a2d12bad1e6c9721d4
--- libexec/got-read-gotconfig/got-read-gotconfig.c
+++ libexec/got-read-gotconfig/got-read-gotconfig.c
}
err = send_gotconfig_str(&ibuf,
gotconfig->author ? gotconfig->author : "");
+ break;
+ case GOT_IMSG_GOTCONFIG_ALLOWEDSIGNERS_REQUEST:
+ if (gotconfig == NULL) {
+ err = got_error(GOT_ERR_PRIVSEP_MSG);
+ break;
+ }
+ err = send_gotconfig_str(&ibuf,
+ gotconfig->allowed_signers_file ?
+ gotconfig->allowed_signers_file : "");
+ break;
+ case GOT_IMSG_GOTCONFIG_REVOKEDSIGNERS_REQUEST:
+ if (gotconfig == NULL) {
+ err = got_error(GOT_ERR_PRIVSEP_MSG);
+ break;
+ }
+ err = send_gotconfig_str(&ibuf,
+ gotconfig->revoked_signers_file ?
+ gotconfig->revoked_signers_file : "");
break;
case GOT_IMSG_GOTCONFIG_REMOTES_REQUEST:
if (gotconfig == NULL) {
blob - 1ce499222101a45de399bd433825c767df869d91
blob + 504e691250732f7b2baee47695fc1794127b2adb
--- libexec/got-read-gotconfig/gotconfig.h
+++ libexec/got-read-gotconfig/gotconfig.h
/*
+ * Copyright (c) 2022 Josh Rickmar <jrick@zettaport.com>
* Copyright (c) 2020, 2021 Tracey Emery <tracey@openbsd.org>
* Copyright (c) 2020 Stefan Sperling <stsp@openbsd.org>
*
char *author;
struct gotconfig_remote_repo_list remotes;
int nremotes;
+ char *allowed_signers_file;
+ char *revoked_signers_file;
};
/*
blob - b9a0bd38cabe5d893cbbb04c482578a895a094ed
blob + 85fc623c3bd3ebda367919af6ac405ae817a88fc
--- libexec/got-read-gotconfig/parse.y
+++ libexec/got-read-gotconfig/parse.y
%token ERROR
%token REMOTE REPOSITORY SERVER PORT PROTOCOL MIRROR_REFERENCES BRANCH
-%token AUTHOR FETCH_ALL_BRANCHES REFERENCE FETCH SEND
+%token AUTHOR ALLOWED_SIGNERS REVOKED_SIGNERS FETCH_ALL_BRANCHES REFERENCE
+%token FETCH SEND
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.number> boolean portplain
| grammar '\n'
| grammar author '\n'
| grammar remote '\n'
+ | grammar allowed_signers '\n'
;
boolean : STRING {
if (strcasecmp($1, "true") == 0 ||
;
author : AUTHOR STRING {
gotconfig.author = $2;
+ }
+ ;
+allowed_signers : ALLOWED_SIGNERS STRING {
+ gotconfig.allowed_signers_file = $2;
+ }
+ ;
+revoked_signers : REVOKED_SIGNERS STRING {
+ gotconfig.revoked_signers_file = $2;
}
;
optnl : '\n' optnl
{
/* This has to be sorted always. */
static const struct keywords keywords[] = {
+ {"allowed_signers", ALLOWED_SIGNERS},
{"author", AUTHOR},
{"branch", BRANCH},
{"fetch", FETCH},
{"reference", REFERENCE},
{"remote", REMOTE},
{"repository", REPOSITORY},
+ {"revoked_signers", REVOKED_SIGNERS},
{"send", SEND},
{"server", SERVER},
};
struct gotconfig_remote_repo *remote;
free(conf->author);
+ free(conf->allowed_signers_file);
+ free(conf->revoked_signers_file);
while (!TAILQ_EMPTY(&conf->remotes)) {
remote = TAILQ_FIRST(&conf->remotes);
TAILQ_REMOVE(&conf->remotes, remote, entry);
blob - 53325e40ea937187e8814d7b18dd3a6a2f5c40f5
blob + b39af2be74c1e13b37e5bb89219e62eed8046e23
--- regress/cmdline/tag.sh
+++ regress/cmdline/tag.sh
fi
test_done "$testroot" "$ret"
}
+
+test_tag_create_ssh_signed() {
+ local testroot=`test_init tag_create`
+ local commit_id=`git_show_head $testroot/repo`
+ local tag=1.0.0
+ local tag2=2.0.0
+
+ ssh-keygen -q -N '' -t ed25519 -f $testroot/id_ed25519
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "ssh-keygen failed unexpectedly"
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ touch $testroot/allowed_signers
+ echo "allowed_signers \"$testroot/allowed_signers\"" > \
+ $testroot/repo/.git/got.conf
+
+ # Create a signed tag based on repository's HEAD reference
+ got tag -s $testroot/id_ed25519 -m 'test' -r $testroot/repo -c HEAD \
+ $tag > $testroot/stdout
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "got tag command failed unexpectedly"
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ tag_id=`got ref -r $testroot/repo -l \
+ | grep "^refs/tags/$tag" | tr -d ' ' | cut -d: -f2`
+ echo "Created tag $tag_id" > $testroot/stdout.expected
+ cmp -s $testroot/stdout $testroot/stdout.expected
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # Ensure validation fails when the key is not allowed
+ echo "signature: Could not verify signature." > \
+ $testroot/stdout.expected
+ VERIFY_STDOUT=$(got tag -r $testroot/repo -V $tag 2> $testroot/stderr)
+ ret=$?
+ echo "$VERIFY_STDOUT" | grep '^signature: ' > $testroot/stdout
+ if [ $ret -eq 0 ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "1"
+ return 1
+ fi
+ GOOD_SIG='Good "git" signature for flan_hacker@openbsd.org with ED25519 key SHA256:'
+
+ # Validate the signature with the key allowed
+ echo -n 'flan_hacker@openbsd.org ' > $testroot/allowed_signers
+ cat $testroot/id_ed25519.pub >> $testroot/allowed_signers
+ GOT_STDOUT=$(got tag -r $testroot/repo -V $tag 2> $testroot/stderr)
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "got tag command failed unexpectedly"
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ if ! echo "$GOT_STDOUT" | grep -q "^signature: $GOOD_SIG"; then
+ echo "got tag command failed to validate signature"
+ test_done "$testroot" "1"
+ return 1
+ fi
+
+ # Ensure that Git recognizes and verifies the tag Got has created
+ (cd $testroot/repo && git checkout -q $tag)
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "git checkout command failed unexpectedly"
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ (cd $testroot/repo && git config --local gpg.ssh.allowedSignersFile \
+ $testroot/allowed_signers)
+ GIT_STDERR=$(cd $testroot/repo && git tag -v $tag 2>&1 1>/dev/null)
+ if ! echo "$GIT_STDERR" | grep -q "^$GOOD_SIG"; then
+ echo "git tag command failed to validate signature"
+ test_done "$testroot" "1"
+ return 1
+ fi
+
+ # Ensure Got recognizes the new tag
+ got checkout -c $tag $testroot/repo $testroot/wt >/dev/null
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "got checkout command failed unexpectedly"
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # Create a tag based on implied worktree HEAD ref
+ (cd $testroot/wt && got tag -m 'test' $tag2 > $testroot/stdout)
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ tag_id2=`got ref -r $testroot/repo -l \
+ | grep "^refs/tags/$tag2" | tr -d ' ' | cut -d: -f2`
+ echo "Created tag $tag_id2" > $testroot/stdout.expected
+ cmp -s $testroot/stdout $testroot/stdout.expected
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ (cd $testroot/repo && git checkout -q $tag2)
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "git checkout command failed unexpectedly"
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # Attempt to create a tag pointing at a non-commit
+ local tree_id=`git_show_tree $testroot/repo`
+ (cd $testroot/wt && got tag -m 'test' -c $tree_id foobar \
+ 2> $testroot/stderr)
+ ret=$?
+ if [ $ret -eq 0 ]; then
+ echo "git tag command succeeded unexpectedly"
+ test_done "$testroot" "1"
+ return 1
+ fi
+
+ echo "got: commit $tree_id: object not found" \
+ > $testroot/stderr.expected
+ cmp -s $testroot/stderr $testroot/stderr.expected
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got ref -r $testroot/repo -l > $testroot/stdout
+ echo "HEAD: $commit_id" > $testroot/stdout.expected
+ echo -n "refs/got/worktree/base-" >> $testroot/stdout.expected
+ cat $testroot/wt/.got/uuid | tr -d '\n' >> $testroot/stdout.expected
+ echo ": $commit_id" >> $testroot/stdout.expected
+ echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected
+ echo "refs/tags/$tag: $tag_id" >> $testroot/stdout.expected
+ echo "refs/tags/$tag2: $tag_id2" >> $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"
+}
+
test_parseargs "$@"
run_test test_tag_create
run_test test_tag_list
run_test test_tag_list_lightweight
+run_test test_tag_create_ssh_signed
blob - 0215869fd1a3678fe92c416a609faf3e875f0a34
blob + f835a2398bf16bf81771722cceeaea81aaa9423b
--- regress/fetch/Makefile
+++ regress/fetch/Makefile
SRCS = error.c privsep.c reference.c sha1.c object.c object_parse.c path.c \
opentemp.c repository.c lockfile.c object_cache.c pack.c inflate.c \
deflate.c delta.c delta_cache.c object_idset.c object_create.c \
- fetch.c gotconfig.c dial.c fetch_test.c bloom.c murmurhash2.c
+ fetch.c gotconfig.c dial.c fetch_test.c bloom.c murmurhash2.c sigs.c \
+ buf.c date.c
CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib
LDADD = -lutil -lz -lm
blob - ba79d5e787ada9939dea4f62aae062cea501f845
blob + 7379d7e77fb9e190eb44211cdf722939696a6cbe
--- tog/Makefile
+++ tog/Makefile
gotconfig.c diff_main.c diff_atomize_text.c \
diff_myers.c diff_output.c diff_output_plain.c \
diff_output_unidiff.c diff_output_edscript.c \
- diff_patience.c bloom.c murmurhash2.c
+ diff_patience.c bloom.c murmurhash2.c sigs.c date.c
MAN = ${PROG}.1
CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib