public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-commits] proj/portage-utils:master commit in: tests/qmanifest/root/.gnupg/, tests/qmanifest/, /, man/, man/include/, ...
@ 2019-05-24 11:58 Fabian Groffen
  0 siblings, 0 replies; only message in thread
From: Fabian Groffen @ 2019-05-24 11:58 UTC (permalink / raw
  To: gentoo-commits

commit:     f1d02fbf01683c42ddb0cdfbfe7815c5ff37e035
Author:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Fri May 24 11:58:26 2019 +0000
Commit:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Fri May 24 11:58:26 2019 +0000
URL:        https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=f1d02fbf

qmanifest: allow GPG-signing top-level Manifest

Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>

 man/include/qmanifest-01-generation.include        |  17 ++
 man/include/qmanifest.optdesc.yaml                 |   8 +
 man/qmanifest.1                                    |  30 ++-
 qmanifest.c                                        | 214 ++++++++++++++++-----
 tests/qmanifest/dotest                             |  16 +-
 tests/qmanifest/manifest04.good                    |   3 +-
 tests/qmanifest/manifest07.good                    |  11 +-
 .../1F0A2C7F1E80A6EEEA3B9C30068FB3349702B3A7.key   | Bin 0 -> 1171 bytes
 .../E37F9F3C8E4A940C625EC65B7070255F4AAA55F9.key   | Bin 0 -> 1155 bytes
 tests/qmanifest/root/.gnupg/pubring.kbx            | Bin 0 -> 1435 bytes
 tests/qmanifest/root/.gnupg/random_seed            | Bin 0 -> 600 bytes
 tests/qmanifest/root/.gnupg/trustdb.gpg            | Bin 0 -> 1280 bytes
 12 files changed, 233 insertions(+), 66 deletions(-)

diff --git a/man/include/qmanifest-01-generation.include b/man/include/qmanifest-01-generation.include
new file mode 100644
index 0000000..5a24a02
--- /dev/null
+++ b/man/include/qmanifest-01-generation.include
@@ -0,0 +1,17 @@
+.SH "GENERATING A SIGNED TREE"
+.PP
+By default, \fBqmanifest\fR will not try to sign the top-level Manifest
+when it generating thick Manifests.  A tree as such isn't completely
+valid (as it misses the final signature), but still correct.  To sign
+the top-level Manifest, the \fB-s\fR flag needs to be used to provide
+the GPG keyid to sign with.  The passphrase is requested by \fBgpg\fR(1)
+itself, unless the \fB-p\fR flag is given, in which case \fBqmanifest\fR
+attempts to read the passphrase from \fIstdin\fR and then pass that
+passphrase onto \fBgpg\fR.  This is useful for scenarios in which the
+signing of a tree is scripted.
+.PP
+To generate a tree signed by GPG keyid \fI0x123567ABC\fR using
+passphrase \fImypasswd\fR, one could use:
+.nf\fI
+	$ echo mypasswd | qmanifest -g -s 0x123567ABC -p /path/to/tree
+.fi

diff --git a/man/include/qmanifest.optdesc.yaml b/man/include/qmanifest.optdesc.yaml
new file mode 100644
index 0000000..8bf1ce7
--- /dev/null
+++ b/man/include/qmanifest.optdesc.yaml
@@ -0,0 +1,8 @@
+signas: |
+    Sign generated Manifest using GPG key.  This key must exist in your
+    keyring and be valid for signing.
+passphrase: |
+    Ask for GPG key password (instead of relying on gpg-agent).  While
+    this option is not very useful compared to gpg's ways of gathering a
+    password, it is mainly intended for automated setups where the
+    password is piped in using \fIstdin\fR.

diff --git a/man/qmanifest.1 b/man/qmanifest.1
index e223122..15027f6 100644
--- a/man/qmanifest.1
+++ b/man/qmanifest.1
@@ -38,7 +38,17 @@ with the desired maximum amount of threads in use by \fIqmanifest\fR.
 .SH OPTIONS
 .TP
 \fB\-g\fR, \fB\-\-generate\fR
-Generate thick Manifests and sign.
+Generate thick Manifests.
+.TP
+\fB\-s\fR \fI<arg>\fR, \fB\-\-signas\fR \fI<arg>\fR
+Sign generated Manifest using GPG key.  This key must exist in your
+keyring and be valid for signing.
+.TP
+\fB\-p\fR, \fB\-\-passphrase\fR
+Ask for GPG key password (instead of relying on gpg-agent).  While
+this option is not very useful compared to gpg's ways of gathering a
+password, it is mainly intended for automated setups where the
+password is piped in using \fIstdin\fR.
 .TP
 \fB\-d\fR, \fB\-\-dir\fR
 Treat arguments as directories.
@@ -63,7 +73,23 @@ Print this help and exit.
 .TP
 \fB\-V\fR, \fB\-\-version\fR
 Print version and exit.
-
+.SH "GENERATING A SIGNED TREE"
+.PP
+By default, \fBqmanifest\fR will not try to sign the top-level Manifest
+when it generating thick Manifests.  A tree as such isn't completely
+valid (as it misses the final signature), but still correct.  To sign
+the top-level Manifest, the \fB-s\fR flag needs to be used to provide
+the GPG keyid to sign with.  The passphrase is requested by \fBgpg\fR(1)
+itself, unless the \fB-p\fR flag is given, in which case \fBqmanifest\fR
+attempts to read the passphrase from \fIstdin\fR and then pass that
+passphrase onto \fBgpg\fR.  This is useful for scenarios in which the
+signing of a tree is scripted.
+.PP
+To generate a tree signed by GPG keyid \fI0x123567ABC\fR using
+passphrase \fImypasswd\fR, one could use:
+.nf\fI
+	$ echo mypasswd | qmanifest -g -s 0x123567ABC -p /path/to/tree
+.fi
 .SH "REPORTING BUGS"
 Please report bugs via http://bugs.gentoo.org/
 .br

diff --git a/qmanifest.c b/qmanifest.c
index 88352fa..ed203a6 100644
--- a/qmanifest.c
+++ b/qmanifest.c
@@ -39,15 +39,19 @@
 #include "eat_file.h"
 #include "hash.h"
 
-#define QMANIFEST_FLAGS "gdo" COMMON_FLAGS
+#define QMANIFEST_FLAGS "gs:pdo" COMMON_FLAGS
 static struct option const qmanifest_long_opts[] = {
 	{"generate",   no_argument, NULL, 'g'},
+	{"signas",      a_argument, NULL, 's'},
+	{"passphrase", no_argument, NULL, 'p'},
 	{"dir",        no_argument, NULL, 'd'},
 	{"overlay",    no_argument, NULL, 'o'},
 	COMMON_LONG_OPTS
 };
 static const char * const qmanifest_opts_help[] = {
-	"Generate thick Manifests and sign",
+	"Generate thick Manifests",
+	"Sign generated Manifest using GPG key",
+	"Ask for GPG key password (instead of relying on gpg-agent)",
 	"Treat arguments as directories",
 	"Treat arguments as overlay names",
 	COMMON_OPTS_HELP
@@ -55,6 +59,8 @@ static const char * const qmanifest_opts_help[] = {
 #define qmanifest_usage(ret) usage(ret, QMANIFEST_FLAGS, qmanifest_long_opts, qmanifest_opts_help, NULL, lookup_applet_idx("qmanifest"))
 
 static int hashes = HASH_DEFAULT;
+static char *gpg_sign_key = NULL;
+static bool gpg_get_password = false;
 
 /* linked list structure to hold verification complaints */
 typedef struct verify_msg {
@@ -688,37 +694,124 @@ generate_dir(const char *dir, enum type_manifest mtype)
 	}
 }
 
+static gpgme_error_t
+gpgme_pw_cb(void *opaque, const char *uid_hint, const char *pw_info,
+		int last_was_bad, int fd)
+{
+	char *pass = (char *)opaque;
+	size_t passlen = strlen(pass);
+	ssize_t ret;
+
+	(void)uid_hint;
+	(void)pw_info;
+	(void)last_was_bad;
+
+	do {
+		ret = write(fd, pass, passlen);
+		if (ret > 0) {
+			pass += ret;
+			passlen -= ret;
+		}
+	} while (passlen > 0 && ret > 0);
+
+	return passlen == 0 ? GPG_ERR_NO_ERROR : gpgme_error_from_errno(errno);
+}
+
 static const char *
-process_dir_gen(const char *dir)
+process_dir_gen(void)
 {
 	char path[_Q_PATH_MAX];
 	int newhashes;
-	int curdirfd;
+	struct termios termio;
+	char *gpg_pass;
 
-	snprintf(path, sizeof(path), "%s%s/metadata/layout.conf", portroot, dir);
-	if ((newhashes = parse_layout_conf(path)) != 0) {
+	if ((newhashes = parse_layout_conf("metadata/layout.conf")) != 0) {
 		hashes = newhashes;
 	} else {
 		return "generation must be done on a full tree";
 	}
 
-	if ((curdirfd = open(".", O_RDONLY)) < 0) {
-		fprintf(stderr, "cannot open current directory?!? %s\n",
-				strerror(errno));
-	}
-	snprintf(path, sizeof(path), "%s%s", portroot, dir);
-	if (chdir(path) != 0) {
-		fprintf(stderr, "cannot chdir() to %s: %s\n", dir, strerror(errno));
-		return "not a directory";
-	}
-
 	if (generate_dir(".\0", GLOBAL_MANIFEST) == NULL)
 		return "generation failed";
 
-	/* return to where we were before we called this function */
-	if (fchdir(curdirfd) != 0 && verbose > 1)
-		warn("could not move back to original directory");
-	close(curdirfd);
+	if (gpg_sign_key != NULL) {
+		gpgme_ctx_t gctx;
+		gpgme_error_t gerr;
+		gpgme_key_t gkey;
+		gpgme_data_t manifest;
+		gpgme_data_t out;
+		FILE *f;
+		size_t dlen;
+
+		gerr = gpgme_new(&gctx);
+		if (gerr != GPG_ERR_NO_ERROR)
+			return "GPG setup failed";
+
+		gerr = gpgme_get_key(gctx, gpg_sign_key, &gkey, 0);
+		if (gerr != GPG_ERR_NO_ERROR)
+			return "failed to get GPG key";
+		gerr = gpgme_signers_add(gctx, gkey);
+		if (gerr != GPG_ERR_NO_ERROR)
+			return "failed to add GPG key to sign list, is it a suitable key?";
+
+		gpg_pass = NULL;
+		if (gpg_get_password) {
+			if (isatty(fileno(stdin))) {
+				/* disable terminal echo; the printing of what you type */
+				tcgetattr(fileno(stdin), &termio);
+				termio.c_lflag &= ~ECHO;
+				tcsetattr(fileno(stdin), TCSANOW, &termio);
+
+				printf("Password for GPG-key %s: ", gpg_sign_key);
+			}
+
+			gpg_pass = fgets(path, sizeof(path), stdin);
+
+			if (isatty(fileno(stdin))) {
+				printf("\n");
+				/* restore echoing, for what it's worth */
+				termio.c_lflag |= ECHO;
+				tcsetattr(fileno(stdin), TCSANOW, &termio);
+			}
+
+			if (gpg_pass == NULL || *gpg_pass == '\0')
+				warn("no GPG password given, gpg might ask for it again");
+				/* continue for the case where gpg-agent holds the pass */
+			else {
+				gpgme_set_pinentry_mode(gctx, GPGME_PINENTRY_MODE_LOOPBACK);
+				gpgme_set_passphrase_cb(gctx, gpgme_pw_cb, gpg_pass);
+			}
+		}
+
+		if ((f = fopen(str_manifest, "r+")) == NULL)
+			return "could not open top-level Manifest file";
+
+		/* finally, sign the Manifest */
+		if (gpgme_data_new_from_stream(&manifest, f) != GPG_ERR_NO_ERROR)
+			return "failed to create GPG data from Manifest";
+
+		if (gpgme_data_new(&out) != GPG_ERR_NO_ERROR)
+			return "failed to create GPG output buffer";
+
+		gerr = gpgme_op_sign(gctx, manifest, out, GPGME_SIG_MODE_CLEAR);
+		if (gerr != GPG_ERR_NO_ERROR) {
+			warn("%s: %s", gpgme_strsource(gerr), gpgme_strerror(gerr));
+			return "failed to GPG sign Manifest";
+		}
+
+		/* write back signed Manifest */
+		rewind(f);
+		gpgme_data_seek(out, 0, SEEK_SET);
+		do {
+			dlen = gpgme_data_read(out, path, sizeof(path));
+			fwrite(path, dlen, 1, f);
+		} while (dlen == sizeof(path));
+		fclose(f);
+
+		gpgme_data_release(out);
+		gpgme_data_release(manifest);
+		gpgme_release(gctx);
+	}
 
 	return NULL;
 }
@@ -854,13 +947,19 @@ verify_gpg_sig(const char *path, verify_msg **msgs)
 						"used to verify the signature has been revoked");
 				break;
 			case GPG_ERR_BAD_SIGNATURE:
+				free(ret);
+				ret = NULL;
 				printf("the signature is invalid\n");
 				break;
 			case GPG_ERR_NO_PUBKEY:
+				free(ret);
+				ret = NULL;
 				printf("the signature could not be verified due to a "
 						"missing key\n");
 				break;
 			default:
+				free(ret);
+				ret = NULL;
 				printf("there was some other error which prevented the "
 						"signature verification\n");
 				break;
@@ -1414,7 +1513,7 @@ format_line(const char *pfx, const char *msg, int twidth)
 }
 
 static const char *
-process_dir_vrfy(const char *dir)
+process_dir_vrfy(void)
 {
 	char buf[8192];
 	int newhashes;
@@ -1422,7 +1521,6 @@ process_dir_vrfy(const char *dir)
 	struct timeval startt;
 	struct timeval finisht;
 	double etime;
-	int curdirfd;
 	char *timestamp;
 	verify_msg topmsg;
 	verify_msg *walk = &topmsg;
@@ -1436,35 +1534,25 @@ process_dir_vrfy(const char *dir)
 
 	gettimeofday(&startt, NULL);
 
-	snprintf(buf, sizeof(buf), "%s%s/metadata/layout.conf", portroot,  dir);
+	snprintf(buf, sizeof(buf), "metadata/layout.conf");
 	if ((newhashes = parse_layout_conf(buf)) != 0) {
 		hashes = newhashes;
 	} else {
 		return "verification must be done on a full tree";
 	}
 
-	if ((curdirfd = open(".", O_RDONLY)) < 0) {
-		fprintf(stderr, "cannot open current directory?!? %s\n",
-				strerror(errno));
-	}
-	snprintf(buf, sizeof(buf), "%s%s", portroot, dir);
-	if (chdir(buf) != 0) {
-		fprintf(stderr, "cannot chdir() to %s: %s\n", dir, strerror(errno));
-		return "not a directory";
-	}
-
 	if ((gs = verify_gpg_sig(str_manifest, &walk)) == NULL) {
 		ret = "gpg signature invalid";
 	} else {
 		fprintf(stdout,
 				"%s%s%s signature made %s by\n"
-				"%s\n"
+				"  %s%s%s\n"
 				"primary key fingerprint %s\n"
 				"%4s subkey fingerprint %s\n",
 				gs->isgood ? GREEN : RED,
 				gs->isgood ? "good": "BAD",
 				NORM, gs->timestamp,
-				gs->signer,
+				DKBLUE, gs->signer, NORM,
 				gs->pkfingerprint,
 				gs->algo, gs->fingerprint);
 		if (!gs->isgood)
@@ -1575,11 +1663,6 @@ process_dir_vrfy(const char *dir)
 
 	gettimeofday(&finisht, NULL);
 
-	/* return to where we were before we called this function */
-	if (fchdir(curdirfd) != 0 && verbose > 1)
-		warn("could not move back to original directory");
-	close(curdirfd);
-
 	etime = ((double)((finisht.tv_sec - startt.tv_sec) * 1000000 +
 				finisht.tv_usec) - (double)startt.tv_usec) / 1000000.0;
 	printf("checked %zd Manifests, %zd files, %zd failures in %.02fs\n",
@@ -1591,15 +1674,17 @@ int
 qmanifest_main(int argc, char **argv)
 {
 	char *prog;
-	const char *(*runfunc)(const char *);
+	const char *(*runfunc)(void);
 	int ret;
 	const char *rsn;
 	bool isdir = false;
 	bool isoverlay = false;
 	char *overlay;
 	char path[_Q_PATH_MAX];
+	char path2[_Q_PATH_MAX];
 	size_t n;
 	int i;
+	int curdirfd;
 
 	if ((prog = strrchr(argv[0], '/')) == NULL) {
 		prog = argv[0];
@@ -1620,6 +1705,8 @@ qmanifest_main(int argc, char **argv)
 		switch (ret) {
 			COMMON_GETOPTS_CASES(qmanifest)
 			case 'g': runfunc = process_dir_gen;  break;
+			case 's': gpg_sign_key = optarg;      break;
+			case 'p': gpg_get_password = true;    break;
 			case 'd': isdir = true;               break;
 			case 'o': isoverlay = true;           break;
 		}
@@ -1653,6 +1740,9 @@ qmanifest_main(int argc, char **argv)
 		}
 	}
 
+	if ((curdirfd = open(".", O_RDONLY)) < 0)
+		warn("cannot open current directory?!? %s\n", strerror(errno));
+
 	ret = EXIT_SUCCESS;
 	argc -= optind;
 	argv += optind;
@@ -1679,20 +1769,27 @@ qmanifest_main(int argc, char **argv)
 		if (isdir || (!isoverlay && overlay == NULL)) /* !isdir && !isoverlay */
 			overlay = argv[i];
 
-		if (runfunc == process_dir_vrfy)
-			printf("verifying %s%s%s...\n", BOLD, overlay, NORM);
-
 		if (*overlay != '/') {
 			if (portroot[1] == '\0') {
 				/* resolve the path */
+				(void)fchdir(curdirfd);
 				(void)realpath(overlay, path);
 			} else {
 				snprintf(path, sizeof(path), "./%s", overlay);
 			}
-			overlay = path;
 		}
 
-		rsn = runfunc(overlay);
+		snprintf(path2, sizeof(path2), "%s%s", portroot, path);
+		if (chdir(path2) != 0) {
+			warn("cannot change directory to %s: %s", overlay, strerror(errno));
+			ret |= 1;
+			continue;
+		}
+
+		if (runfunc == process_dir_vrfy)
+			printf("verifying %s%s%s...\n", BOLD, overlay, NORM);
+
+		rsn = runfunc();
 		if (rsn != NULL) {
 			printf("%s%s%s\n", RED, rsn, NORM);
 			ret |= 2;
@@ -1700,15 +1797,28 @@ qmanifest_main(int argc, char **argv)
 	}
 
 	if (i == 0) {
-		if (runfunc == process_dir_vrfy)
-			printf("verifying %s%s%s...\n", BOLD, main_overlay, NORM);
-		rsn = runfunc(main_overlay);
-		if (rsn != NULL) {
-			printf("%s%s%s\n", RED, rsn, NORM);
-			ret |= 2;
+		snprintf(path, sizeof(path), "%s%s", portroot, main_overlay);
+		if (chdir(path) != 0) {
+			warn("cannot change directory to %s: %s",
+					main_overlay, strerror(errno));
+			ret |= 1;
+		} else {
+			if (runfunc == process_dir_vrfy)
+				printf("verifying %s%s%s...\n", BOLD, main_overlay, NORM);
+
+			rsn = runfunc();
+			if (rsn != NULL) {
+				printf("%s%s%s\n", RED, rsn, NORM);
+				ret |= 2;
+			}
 		}
 	}
 
+	/* return to where we were before we called this function */
+	if (fchdir(curdirfd) != 0 && verbose > 1)
+		warn("could not move back to original directory");
+	close(curdirfd);
+
 	return ret;
 }
 

diff --git a/tests/qmanifest/dotest b/tests/qmanifest/dotest
index 636a723..fb2aa22 100755
--- a/tests/qmanifest/dotest
+++ b/tests/qmanifest/dotest
@@ -36,7 +36,7 @@ test 02 2 "qmanifest not_a_tree"
 test 03 2 "qmanifest notatree"
 
 # dir test
-test 04 2 "qmanifest -d not_a_tree"
+test 04 1 "qmanifest -d not_a_tree"
 
 # overlay test
 test 05 1 "qmanifest -o notatree"
@@ -44,11 +44,19 @@ test 05 1 "qmanifest -o notatree"
 # generate a valid tree
 rm -Rf testtree
 cp -r "${ROOT}/simpletree" testtree || echo try it anyway
+# make it a fully valid tree
+export HOME=${ROOT}  # for gnupg home
+rm testtree/my-cat/mypackage/unrecorded-file
 unset ROOT PORTAGE_CONFIGROOT
-test 06 0 "qmanifest -g testtree"
+SIGNAS=0x3D695C8C0F87966B62DC5AFCDCFABA8E07F52261
+KEYPASS=qmanifest
+test 06 0 "echo ${KEYPASS} | qmanifest -g -s ${SIGNAS} -p testtree"
 
-# validate the just generated tree (doesn't do GPG signing hence fails)
-test 07 0 "qmanifest testtree | sed '/Manifest timestamp/d'"
+# validate the just generated tree
+test 07 0 "qmanifest testtree | sed -e '/Manifest timestamp/d' -e 's/made .* UTC by/made by/'"
+
+# shut down agents and whatnot
+gpgconf --kill all
 
 cleantmpdir
 

diff --git a/tests/qmanifest/manifest04.good b/tests/qmanifest/manifest04.good
index 17c6a1f..4831674 100644
--- a/tests/qmanifest/manifest04.good
+++ b/tests/qmanifest/manifest04.good
@@ -1,2 +1 @@
-verifying not_a_tree...
-verification must be done on a full tree
+manifest: cannot change directory to not_a_tree: No such file or directory

diff --git a/tests/qmanifest/manifest07.good b/tests/qmanifest/manifest07.good
index 67176c5..6347806 100644
--- a/tests/qmanifest/manifest07.good
+++ b/tests/qmanifest/manifest07.good
@@ -1,7 +1,6 @@
 verifying testtree...
-Manifest:
-- failed to verify signature
-my-cat/mypackage/Manifest:
-- file not listed: unrecorded-file
-checked 5 Manifests, 9 files, 1 failures
-manifest verification failed
+good signature made by
+  Qmanifest Test Key
+primary key fingerprint 3D69 5C8C 0F87 966B 62DC  5AFC DCFA BA8E 07F5 2261
+ RSA subkey fingerprint 3D69 5C8C 0F87 966B 62DC  5AFC DCFA BA8E 07F5 2261
+checked 5 Manifests, 9 files, 0 failures

diff --git a/tests/qmanifest/root/.gnupg/private-keys-v1.d/1F0A2C7F1E80A6EEEA3B9C30068FB3349702B3A7.key b/tests/qmanifest/root/.gnupg/private-keys-v1.d/1F0A2C7F1E80A6EEEA3B9C30068FB3349702B3A7.key
new file mode 100644
index 0000000..b4ed767
Binary files /dev/null and b/tests/qmanifest/root/.gnupg/private-keys-v1.d/1F0A2C7F1E80A6EEEA3B9C30068FB3349702B3A7.key differ

diff --git a/tests/qmanifest/root/.gnupg/private-keys-v1.d/E37F9F3C8E4A940C625EC65B7070255F4AAA55F9.key b/tests/qmanifest/root/.gnupg/private-keys-v1.d/E37F9F3C8E4A940C625EC65B7070255F4AAA55F9.key
new file mode 100644
index 0000000..4b07401
Binary files /dev/null and b/tests/qmanifest/root/.gnupg/private-keys-v1.d/E37F9F3C8E4A940C625EC65B7070255F4AAA55F9.key differ

diff --git a/tests/qmanifest/root/.gnupg/pubring.kbx b/tests/qmanifest/root/.gnupg/pubring.kbx
new file mode 100644
index 0000000..848dc93
Binary files /dev/null and b/tests/qmanifest/root/.gnupg/pubring.kbx differ

diff --git a/tests/qmanifest/root/.gnupg/random_seed b/tests/qmanifest/root/.gnupg/random_seed
new file mode 100644
index 0000000..d32d054
Binary files /dev/null and b/tests/qmanifest/root/.gnupg/random_seed differ

diff --git a/tests/qmanifest/root/.gnupg/trustdb.gpg b/tests/qmanifest/root/.gnupg/trustdb.gpg
new file mode 100644
index 0000000..78308c6
Binary files /dev/null and b/tests/qmanifest/root/.gnupg/trustdb.gpg differ


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2019-05-24 11:58 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-05-24 11:58 [gentoo-commits] proj/portage-utils:master commit in: tests/qmanifest/root/.gnupg/, tests/qmanifest/, /, man/, man/include/, Fabian Groffen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox