1 |
commit: f1d02fbf01683c42ddb0cdfbfe7815c5ff37e035 |
2 |
Author: Fabian Groffen <grobian <AT> gentoo <DOT> org> |
3 |
AuthorDate: Fri May 24 11:58:26 2019 +0000 |
4 |
Commit: Fabian Groffen <grobian <AT> gentoo <DOT> org> |
5 |
CommitDate: Fri May 24 11:58:26 2019 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=f1d02fbf |
7 |
|
8 |
qmanifest: allow GPG-signing top-level Manifest |
9 |
|
10 |
Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org> |
11 |
|
12 |
man/include/qmanifest-01-generation.include | 17 ++ |
13 |
man/include/qmanifest.optdesc.yaml | 8 + |
14 |
man/qmanifest.1 | 30 ++- |
15 |
qmanifest.c | 214 ++++++++++++++++----- |
16 |
tests/qmanifest/dotest | 16 +- |
17 |
tests/qmanifest/manifest04.good | 3 +- |
18 |
tests/qmanifest/manifest07.good | 11 +- |
19 |
.../1F0A2C7F1E80A6EEEA3B9C30068FB3349702B3A7.key | Bin 0 -> 1171 bytes |
20 |
.../E37F9F3C8E4A940C625EC65B7070255F4AAA55F9.key | Bin 0 -> 1155 bytes |
21 |
tests/qmanifest/root/.gnupg/pubring.kbx | Bin 0 -> 1435 bytes |
22 |
tests/qmanifest/root/.gnupg/random_seed | Bin 0 -> 600 bytes |
23 |
tests/qmanifest/root/.gnupg/trustdb.gpg | Bin 0 -> 1280 bytes |
24 |
12 files changed, 233 insertions(+), 66 deletions(-) |
25 |
|
26 |
diff --git a/man/include/qmanifest-01-generation.include b/man/include/qmanifest-01-generation.include |
27 |
new file mode 100644 |
28 |
index 0000000..5a24a02 |
29 |
--- /dev/null |
30 |
+++ b/man/include/qmanifest-01-generation.include |
31 |
@@ -0,0 +1,17 @@ |
32 |
+.SH "GENERATING A SIGNED TREE" |
33 |
+.PP |
34 |
+By default, \fBqmanifest\fR will not try to sign the top-level Manifest |
35 |
+when it generating thick Manifests. A tree as such isn't completely |
36 |
+valid (as it misses the final signature), but still correct. To sign |
37 |
+the top-level Manifest, the \fB-s\fR flag needs to be used to provide |
38 |
+the GPG keyid to sign with. The passphrase is requested by \fBgpg\fR(1) |
39 |
+itself, unless the \fB-p\fR flag is given, in which case \fBqmanifest\fR |
40 |
+attempts to read the passphrase from \fIstdin\fR and then pass that |
41 |
+passphrase onto \fBgpg\fR. This is useful for scenarios in which the |
42 |
+signing of a tree is scripted. |
43 |
+.PP |
44 |
+To generate a tree signed by GPG keyid \fI0x123567ABC\fR using |
45 |
+passphrase \fImypasswd\fR, one could use: |
46 |
+.nf\fI |
47 |
+ $ echo mypasswd | qmanifest -g -s 0x123567ABC -p /path/to/tree |
48 |
+.fi |
49 |
|
50 |
diff --git a/man/include/qmanifest.optdesc.yaml b/man/include/qmanifest.optdesc.yaml |
51 |
new file mode 100644 |
52 |
index 0000000..8bf1ce7 |
53 |
--- /dev/null |
54 |
+++ b/man/include/qmanifest.optdesc.yaml |
55 |
@@ -0,0 +1,8 @@ |
56 |
+signas: | |
57 |
+ Sign generated Manifest using GPG key. This key must exist in your |
58 |
+ keyring and be valid for signing. |
59 |
+passphrase: | |
60 |
+ Ask for GPG key password (instead of relying on gpg-agent). While |
61 |
+ this option is not very useful compared to gpg's ways of gathering a |
62 |
+ password, it is mainly intended for automated setups where the |
63 |
+ password is piped in using \fIstdin\fR. |
64 |
|
65 |
diff --git a/man/qmanifest.1 b/man/qmanifest.1 |
66 |
index e223122..15027f6 100644 |
67 |
--- a/man/qmanifest.1 |
68 |
+++ b/man/qmanifest.1 |
69 |
@@ -38,7 +38,17 @@ with the desired maximum amount of threads in use by \fIqmanifest\fR. |
70 |
.SH OPTIONS |
71 |
.TP |
72 |
\fB\-g\fR, \fB\-\-generate\fR |
73 |
-Generate thick Manifests and sign. |
74 |
+Generate thick Manifests. |
75 |
+.TP |
76 |
+\fB\-s\fR \fI<arg>\fR, \fB\-\-signas\fR \fI<arg>\fR |
77 |
+Sign generated Manifest using GPG key. This key must exist in your |
78 |
+keyring and be valid for signing. |
79 |
+.TP |
80 |
+\fB\-p\fR, \fB\-\-passphrase\fR |
81 |
+Ask for GPG key password (instead of relying on gpg-agent). While |
82 |
+this option is not very useful compared to gpg's ways of gathering a |
83 |
+password, it is mainly intended for automated setups where the |
84 |
+password is piped in using \fIstdin\fR. |
85 |
.TP |
86 |
\fB\-d\fR, \fB\-\-dir\fR |
87 |
Treat arguments as directories. |
88 |
@@ -63,7 +73,23 @@ Print this help and exit. |
89 |
.TP |
90 |
\fB\-V\fR, \fB\-\-version\fR |
91 |
Print version and exit. |
92 |
- |
93 |
+.SH "GENERATING A SIGNED TREE" |
94 |
+.PP |
95 |
+By default, \fBqmanifest\fR will not try to sign the top-level Manifest |
96 |
+when it generating thick Manifests. A tree as such isn't completely |
97 |
+valid (as it misses the final signature), but still correct. To sign |
98 |
+the top-level Manifest, the \fB-s\fR flag needs to be used to provide |
99 |
+the GPG keyid to sign with. The passphrase is requested by \fBgpg\fR(1) |
100 |
+itself, unless the \fB-p\fR flag is given, in which case \fBqmanifest\fR |
101 |
+attempts to read the passphrase from \fIstdin\fR and then pass that |
102 |
+passphrase onto \fBgpg\fR. This is useful for scenarios in which the |
103 |
+signing of a tree is scripted. |
104 |
+.PP |
105 |
+To generate a tree signed by GPG keyid \fI0x123567ABC\fR using |
106 |
+passphrase \fImypasswd\fR, one could use: |
107 |
+.nf\fI |
108 |
+ $ echo mypasswd | qmanifest -g -s 0x123567ABC -p /path/to/tree |
109 |
+.fi |
110 |
.SH "REPORTING BUGS" |
111 |
Please report bugs via http://bugs.gentoo.org/ |
112 |
.br |
113 |
|
114 |
diff --git a/qmanifest.c b/qmanifest.c |
115 |
index 88352fa..ed203a6 100644 |
116 |
--- a/qmanifest.c |
117 |
+++ b/qmanifest.c |
118 |
@@ -39,15 +39,19 @@ |
119 |
#include "eat_file.h" |
120 |
#include "hash.h" |
121 |
|
122 |
-#define QMANIFEST_FLAGS "gdo" COMMON_FLAGS |
123 |
+#define QMANIFEST_FLAGS "gs:pdo" COMMON_FLAGS |
124 |
static struct option const qmanifest_long_opts[] = { |
125 |
{"generate", no_argument, NULL, 'g'}, |
126 |
+ {"signas", a_argument, NULL, 's'}, |
127 |
+ {"passphrase", no_argument, NULL, 'p'}, |
128 |
{"dir", no_argument, NULL, 'd'}, |
129 |
{"overlay", no_argument, NULL, 'o'}, |
130 |
COMMON_LONG_OPTS |
131 |
}; |
132 |
static const char * const qmanifest_opts_help[] = { |
133 |
- "Generate thick Manifests and sign", |
134 |
+ "Generate thick Manifests", |
135 |
+ "Sign generated Manifest using GPG key", |
136 |
+ "Ask for GPG key password (instead of relying on gpg-agent)", |
137 |
"Treat arguments as directories", |
138 |
"Treat arguments as overlay names", |
139 |
COMMON_OPTS_HELP |
140 |
@@ -55,6 +59,8 @@ static const char * const qmanifest_opts_help[] = { |
141 |
#define qmanifest_usage(ret) usage(ret, QMANIFEST_FLAGS, qmanifest_long_opts, qmanifest_opts_help, NULL, lookup_applet_idx("qmanifest")) |
142 |
|
143 |
static int hashes = HASH_DEFAULT; |
144 |
+static char *gpg_sign_key = NULL; |
145 |
+static bool gpg_get_password = false; |
146 |
|
147 |
/* linked list structure to hold verification complaints */ |
148 |
typedef struct verify_msg { |
149 |
@@ -688,37 +694,124 @@ generate_dir(const char *dir, enum type_manifest mtype) |
150 |
} |
151 |
} |
152 |
|
153 |
+static gpgme_error_t |
154 |
+gpgme_pw_cb(void *opaque, const char *uid_hint, const char *pw_info, |
155 |
+ int last_was_bad, int fd) |
156 |
+{ |
157 |
+ char *pass = (char *)opaque; |
158 |
+ size_t passlen = strlen(pass); |
159 |
+ ssize_t ret; |
160 |
+ |
161 |
+ (void)uid_hint; |
162 |
+ (void)pw_info; |
163 |
+ (void)last_was_bad; |
164 |
+ |
165 |
+ do { |
166 |
+ ret = write(fd, pass, passlen); |
167 |
+ if (ret > 0) { |
168 |
+ pass += ret; |
169 |
+ passlen -= ret; |
170 |
+ } |
171 |
+ } while (passlen > 0 && ret > 0); |
172 |
+ |
173 |
+ return passlen == 0 ? GPG_ERR_NO_ERROR : gpgme_error_from_errno(errno); |
174 |
+} |
175 |
+ |
176 |
static const char * |
177 |
-process_dir_gen(const char *dir) |
178 |
+process_dir_gen(void) |
179 |
{ |
180 |
char path[_Q_PATH_MAX]; |
181 |
int newhashes; |
182 |
- int curdirfd; |
183 |
+ struct termios termio; |
184 |
+ char *gpg_pass; |
185 |
|
186 |
- snprintf(path, sizeof(path), "%s%s/metadata/layout.conf", portroot, dir); |
187 |
- if ((newhashes = parse_layout_conf(path)) != 0) { |
188 |
+ if ((newhashes = parse_layout_conf("metadata/layout.conf")) != 0) { |
189 |
hashes = newhashes; |
190 |
} else { |
191 |
return "generation must be done on a full tree"; |
192 |
} |
193 |
|
194 |
- if ((curdirfd = open(".", O_RDONLY)) < 0) { |
195 |
- fprintf(stderr, "cannot open current directory?!? %s\n", |
196 |
- strerror(errno)); |
197 |
- } |
198 |
- snprintf(path, sizeof(path), "%s%s", portroot, dir); |
199 |
- if (chdir(path) != 0) { |
200 |
- fprintf(stderr, "cannot chdir() to %s: %s\n", dir, strerror(errno)); |
201 |
- return "not a directory"; |
202 |
- } |
203 |
- |
204 |
if (generate_dir(".\0", GLOBAL_MANIFEST) == NULL) |
205 |
return "generation failed"; |
206 |
|
207 |
- /* return to where we were before we called this function */ |
208 |
- if (fchdir(curdirfd) != 0 && verbose > 1) |
209 |
- warn("could not move back to original directory"); |
210 |
- close(curdirfd); |
211 |
+ if (gpg_sign_key != NULL) { |
212 |
+ gpgme_ctx_t gctx; |
213 |
+ gpgme_error_t gerr; |
214 |
+ gpgme_key_t gkey; |
215 |
+ gpgme_data_t manifest; |
216 |
+ gpgme_data_t out; |
217 |
+ FILE *f; |
218 |
+ size_t dlen; |
219 |
+ |
220 |
+ gerr = gpgme_new(&gctx); |
221 |
+ if (gerr != GPG_ERR_NO_ERROR) |
222 |
+ return "GPG setup failed"; |
223 |
+ |
224 |
+ gerr = gpgme_get_key(gctx, gpg_sign_key, &gkey, 0); |
225 |
+ if (gerr != GPG_ERR_NO_ERROR) |
226 |
+ return "failed to get GPG key"; |
227 |
+ gerr = gpgme_signers_add(gctx, gkey); |
228 |
+ if (gerr != GPG_ERR_NO_ERROR) |
229 |
+ return "failed to add GPG key to sign list, is it a suitable key?"; |
230 |
+ |
231 |
+ gpg_pass = NULL; |
232 |
+ if (gpg_get_password) { |
233 |
+ if (isatty(fileno(stdin))) { |
234 |
+ /* disable terminal echo; the printing of what you type */ |
235 |
+ tcgetattr(fileno(stdin), &termio); |
236 |
+ termio.c_lflag &= ~ECHO; |
237 |
+ tcsetattr(fileno(stdin), TCSANOW, &termio); |
238 |
+ |
239 |
+ printf("Password for GPG-key %s: ", gpg_sign_key); |
240 |
+ } |
241 |
+ |
242 |
+ gpg_pass = fgets(path, sizeof(path), stdin); |
243 |
+ |
244 |
+ if (isatty(fileno(stdin))) { |
245 |
+ printf("\n"); |
246 |
+ /* restore echoing, for what it's worth */ |
247 |
+ termio.c_lflag |= ECHO; |
248 |
+ tcsetattr(fileno(stdin), TCSANOW, &termio); |
249 |
+ } |
250 |
+ |
251 |
+ if (gpg_pass == NULL || *gpg_pass == '\0') |
252 |
+ warn("no GPG password given, gpg might ask for it again"); |
253 |
+ /* continue for the case where gpg-agent holds the pass */ |
254 |
+ else { |
255 |
+ gpgme_set_pinentry_mode(gctx, GPGME_PINENTRY_MODE_LOOPBACK); |
256 |
+ gpgme_set_passphrase_cb(gctx, gpgme_pw_cb, gpg_pass); |
257 |
+ } |
258 |
+ } |
259 |
+ |
260 |
+ if ((f = fopen(str_manifest, "r+")) == NULL) |
261 |
+ return "could not open top-level Manifest file"; |
262 |
+ |
263 |
+ /* finally, sign the Manifest */ |
264 |
+ if (gpgme_data_new_from_stream(&manifest, f) != GPG_ERR_NO_ERROR) |
265 |
+ return "failed to create GPG data from Manifest"; |
266 |
+ |
267 |
+ if (gpgme_data_new(&out) != GPG_ERR_NO_ERROR) |
268 |
+ return "failed to create GPG output buffer"; |
269 |
+ |
270 |
+ gerr = gpgme_op_sign(gctx, manifest, out, GPGME_SIG_MODE_CLEAR); |
271 |
+ if (gerr != GPG_ERR_NO_ERROR) { |
272 |
+ warn("%s: %s", gpgme_strsource(gerr), gpgme_strerror(gerr)); |
273 |
+ return "failed to GPG sign Manifest"; |
274 |
+ } |
275 |
+ |
276 |
+ /* write back signed Manifest */ |
277 |
+ rewind(f); |
278 |
+ gpgme_data_seek(out, 0, SEEK_SET); |
279 |
+ do { |
280 |
+ dlen = gpgme_data_read(out, path, sizeof(path)); |
281 |
+ fwrite(path, dlen, 1, f); |
282 |
+ } while (dlen == sizeof(path)); |
283 |
+ fclose(f); |
284 |
+ |
285 |
+ gpgme_data_release(out); |
286 |
+ gpgme_data_release(manifest); |
287 |
+ gpgme_release(gctx); |
288 |
+ } |
289 |
|
290 |
return NULL; |
291 |
} |
292 |
@@ -854,13 +947,19 @@ verify_gpg_sig(const char *path, verify_msg **msgs) |
293 |
"used to verify the signature has been revoked"); |
294 |
break; |
295 |
case GPG_ERR_BAD_SIGNATURE: |
296 |
+ free(ret); |
297 |
+ ret = NULL; |
298 |
printf("the signature is invalid\n"); |
299 |
break; |
300 |
case GPG_ERR_NO_PUBKEY: |
301 |
+ free(ret); |
302 |
+ ret = NULL; |
303 |
printf("the signature could not be verified due to a " |
304 |
"missing key\n"); |
305 |
break; |
306 |
default: |
307 |
+ free(ret); |
308 |
+ ret = NULL; |
309 |
printf("there was some other error which prevented the " |
310 |
"signature verification\n"); |
311 |
break; |
312 |
@@ -1414,7 +1513,7 @@ format_line(const char *pfx, const char *msg, int twidth) |
313 |
} |
314 |
|
315 |
static const char * |
316 |
-process_dir_vrfy(const char *dir) |
317 |
+process_dir_vrfy(void) |
318 |
{ |
319 |
char buf[8192]; |
320 |
int newhashes; |
321 |
@@ -1422,7 +1521,6 @@ process_dir_vrfy(const char *dir) |
322 |
struct timeval startt; |
323 |
struct timeval finisht; |
324 |
double etime; |
325 |
- int curdirfd; |
326 |
char *timestamp; |
327 |
verify_msg topmsg; |
328 |
verify_msg *walk = &topmsg; |
329 |
@@ -1436,35 +1534,25 @@ process_dir_vrfy(const char *dir) |
330 |
|
331 |
gettimeofday(&startt, NULL); |
332 |
|
333 |
- snprintf(buf, sizeof(buf), "%s%s/metadata/layout.conf", portroot, dir); |
334 |
+ snprintf(buf, sizeof(buf), "metadata/layout.conf"); |
335 |
if ((newhashes = parse_layout_conf(buf)) != 0) { |
336 |
hashes = newhashes; |
337 |
} else { |
338 |
return "verification must be done on a full tree"; |
339 |
} |
340 |
|
341 |
- if ((curdirfd = open(".", O_RDONLY)) < 0) { |
342 |
- fprintf(stderr, "cannot open current directory?!? %s\n", |
343 |
- strerror(errno)); |
344 |
- } |
345 |
- snprintf(buf, sizeof(buf), "%s%s", portroot, dir); |
346 |
- if (chdir(buf) != 0) { |
347 |
- fprintf(stderr, "cannot chdir() to %s: %s\n", dir, strerror(errno)); |
348 |
- return "not a directory"; |
349 |
- } |
350 |
- |
351 |
if ((gs = verify_gpg_sig(str_manifest, &walk)) == NULL) { |
352 |
ret = "gpg signature invalid"; |
353 |
} else { |
354 |
fprintf(stdout, |
355 |
"%s%s%s signature made %s by\n" |
356 |
- "%s\n" |
357 |
+ " %s%s%s\n" |
358 |
"primary key fingerprint %s\n" |
359 |
"%4s subkey fingerprint %s\n", |
360 |
gs->isgood ? GREEN : RED, |
361 |
gs->isgood ? "good": "BAD", |
362 |
NORM, gs->timestamp, |
363 |
- gs->signer, |
364 |
+ DKBLUE, gs->signer, NORM, |
365 |
gs->pkfingerprint, |
366 |
gs->algo, gs->fingerprint); |
367 |
if (!gs->isgood) |
368 |
@@ -1575,11 +1663,6 @@ process_dir_vrfy(const char *dir) |
369 |
|
370 |
gettimeofday(&finisht, NULL); |
371 |
|
372 |
- /* return to where we were before we called this function */ |
373 |
- if (fchdir(curdirfd) != 0 && verbose > 1) |
374 |
- warn("could not move back to original directory"); |
375 |
- close(curdirfd); |
376 |
- |
377 |
etime = ((double)((finisht.tv_sec - startt.tv_sec) * 1000000 + |
378 |
finisht.tv_usec) - (double)startt.tv_usec) / 1000000.0; |
379 |
printf("checked %zd Manifests, %zd files, %zd failures in %.02fs\n", |
380 |
@@ -1591,15 +1674,17 @@ int |
381 |
qmanifest_main(int argc, char **argv) |
382 |
{ |
383 |
char *prog; |
384 |
- const char *(*runfunc)(const char *); |
385 |
+ const char *(*runfunc)(void); |
386 |
int ret; |
387 |
const char *rsn; |
388 |
bool isdir = false; |
389 |
bool isoverlay = false; |
390 |
char *overlay; |
391 |
char path[_Q_PATH_MAX]; |
392 |
+ char path2[_Q_PATH_MAX]; |
393 |
size_t n; |
394 |
int i; |
395 |
+ int curdirfd; |
396 |
|
397 |
if ((prog = strrchr(argv[0], '/')) == NULL) { |
398 |
prog = argv[0]; |
399 |
@@ -1620,6 +1705,8 @@ qmanifest_main(int argc, char **argv) |
400 |
switch (ret) { |
401 |
COMMON_GETOPTS_CASES(qmanifest) |
402 |
case 'g': runfunc = process_dir_gen; break; |
403 |
+ case 's': gpg_sign_key = optarg; break; |
404 |
+ case 'p': gpg_get_password = true; break; |
405 |
case 'd': isdir = true; break; |
406 |
case 'o': isoverlay = true; break; |
407 |
} |
408 |
@@ -1653,6 +1740,9 @@ qmanifest_main(int argc, char **argv) |
409 |
} |
410 |
} |
411 |
|
412 |
+ if ((curdirfd = open(".", O_RDONLY)) < 0) |
413 |
+ warn("cannot open current directory?!? %s\n", strerror(errno)); |
414 |
+ |
415 |
ret = EXIT_SUCCESS; |
416 |
argc -= optind; |
417 |
argv += optind; |
418 |
@@ -1679,20 +1769,27 @@ qmanifest_main(int argc, char **argv) |
419 |
if (isdir || (!isoverlay && overlay == NULL)) /* !isdir && !isoverlay */ |
420 |
overlay = argv[i]; |
421 |
|
422 |
- if (runfunc == process_dir_vrfy) |
423 |
- printf("verifying %s%s%s...\n", BOLD, overlay, NORM); |
424 |
- |
425 |
if (*overlay != '/') { |
426 |
if (portroot[1] == '\0') { |
427 |
/* resolve the path */ |
428 |
+ (void)fchdir(curdirfd); |
429 |
(void)realpath(overlay, path); |
430 |
} else { |
431 |
snprintf(path, sizeof(path), "./%s", overlay); |
432 |
} |
433 |
- overlay = path; |
434 |
} |
435 |
|
436 |
- rsn = runfunc(overlay); |
437 |
+ snprintf(path2, sizeof(path2), "%s%s", portroot, path); |
438 |
+ if (chdir(path2) != 0) { |
439 |
+ warn("cannot change directory to %s: %s", overlay, strerror(errno)); |
440 |
+ ret |= 1; |
441 |
+ continue; |
442 |
+ } |
443 |
+ |
444 |
+ if (runfunc == process_dir_vrfy) |
445 |
+ printf("verifying %s%s%s...\n", BOLD, overlay, NORM); |
446 |
+ |
447 |
+ rsn = runfunc(); |
448 |
if (rsn != NULL) { |
449 |
printf("%s%s%s\n", RED, rsn, NORM); |
450 |
ret |= 2; |
451 |
@@ -1700,15 +1797,28 @@ qmanifest_main(int argc, char **argv) |
452 |
} |
453 |
|
454 |
if (i == 0) { |
455 |
- if (runfunc == process_dir_vrfy) |
456 |
- printf("verifying %s%s%s...\n", BOLD, main_overlay, NORM); |
457 |
- rsn = runfunc(main_overlay); |
458 |
- if (rsn != NULL) { |
459 |
- printf("%s%s%s\n", RED, rsn, NORM); |
460 |
- ret |= 2; |
461 |
+ snprintf(path, sizeof(path), "%s%s", portroot, main_overlay); |
462 |
+ if (chdir(path) != 0) { |
463 |
+ warn("cannot change directory to %s: %s", |
464 |
+ main_overlay, strerror(errno)); |
465 |
+ ret |= 1; |
466 |
+ } else { |
467 |
+ if (runfunc == process_dir_vrfy) |
468 |
+ printf("verifying %s%s%s...\n", BOLD, main_overlay, NORM); |
469 |
+ |
470 |
+ rsn = runfunc(); |
471 |
+ if (rsn != NULL) { |
472 |
+ printf("%s%s%s\n", RED, rsn, NORM); |
473 |
+ ret |= 2; |
474 |
+ } |
475 |
} |
476 |
} |
477 |
|
478 |
+ /* return to where we were before we called this function */ |
479 |
+ if (fchdir(curdirfd) != 0 && verbose > 1) |
480 |
+ warn("could not move back to original directory"); |
481 |
+ close(curdirfd); |
482 |
+ |
483 |
return ret; |
484 |
} |
485 |
|
486 |
|
487 |
diff --git a/tests/qmanifest/dotest b/tests/qmanifest/dotest |
488 |
index 636a723..fb2aa22 100755 |
489 |
--- a/tests/qmanifest/dotest |
490 |
+++ b/tests/qmanifest/dotest |
491 |
@@ -36,7 +36,7 @@ test 02 2 "qmanifest not_a_tree" |
492 |
test 03 2 "qmanifest notatree" |
493 |
|
494 |
# dir test |
495 |
-test 04 2 "qmanifest -d not_a_tree" |
496 |
+test 04 1 "qmanifest -d not_a_tree" |
497 |
|
498 |
# overlay test |
499 |
test 05 1 "qmanifest -o notatree" |
500 |
@@ -44,11 +44,19 @@ test 05 1 "qmanifest -o notatree" |
501 |
# generate a valid tree |
502 |
rm -Rf testtree |
503 |
cp -r "${ROOT}/simpletree" testtree || echo try it anyway |
504 |
+# make it a fully valid tree |
505 |
+export HOME=${ROOT} # for gnupg home |
506 |
+rm testtree/my-cat/mypackage/unrecorded-file |
507 |
unset ROOT PORTAGE_CONFIGROOT |
508 |
-test 06 0 "qmanifest -g testtree" |
509 |
+SIGNAS=0x3D695C8C0F87966B62DC5AFCDCFABA8E07F52261 |
510 |
+KEYPASS=qmanifest |
511 |
+test 06 0 "echo ${KEYPASS} | qmanifest -g -s ${SIGNAS} -p testtree" |
512 |
|
513 |
-# validate the just generated tree (doesn't do GPG signing hence fails) |
514 |
-test 07 0 "qmanifest testtree | sed '/Manifest timestamp/d'" |
515 |
+# validate the just generated tree |
516 |
+test 07 0 "qmanifest testtree | sed -e '/Manifest timestamp/d' -e 's/made .* UTC by/made by/'" |
517 |
+ |
518 |
+# shut down agents and whatnot |
519 |
+gpgconf --kill all |
520 |
|
521 |
cleantmpdir |
522 |
|
523 |
|
524 |
diff --git a/tests/qmanifest/manifest04.good b/tests/qmanifest/manifest04.good |
525 |
index 17c6a1f..4831674 100644 |
526 |
--- a/tests/qmanifest/manifest04.good |
527 |
+++ b/tests/qmanifest/manifest04.good |
528 |
@@ -1,2 +1 @@ |
529 |
-verifying not_a_tree... |
530 |
-verification must be done on a full tree |
531 |
+manifest: cannot change directory to not_a_tree: No such file or directory |
532 |
|
533 |
diff --git a/tests/qmanifest/manifest07.good b/tests/qmanifest/manifest07.good |
534 |
index 67176c5..6347806 100644 |
535 |
--- a/tests/qmanifest/manifest07.good |
536 |
+++ b/tests/qmanifest/manifest07.good |
537 |
@@ -1,7 +1,6 @@ |
538 |
verifying testtree... |
539 |
-Manifest: |
540 |
-- failed to verify signature |
541 |
-my-cat/mypackage/Manifest: |
542 |
-- file not listed: unrecorded-file |
543 |
-checked 5 Manifests, 9 files, 1 failures |
544 |
-manifest verification failed |
545 |
+good signature made by |
546 |
+ Qmanifest Test Key |
547 |
+primary key fingerprint 3D69 5C8C 0F87 966B 62DC 5AFC DCFA BA8E 07F5 2261 |
548 |
+ RSA subkey fingerprint 3D69 5C8C 0F87 966B 62DC 5AFC DCFA BA8E 07F5 2261 |
549 |
+checked 5 Manifests, 9 files, 0 failures |
550 |
|
551 |
diff --git a/tests/qmanifest/root/.gnupg/private-keys-v1.d/1F0A2C7F1E80A6EEEA3B9C30068FB3349702B3A7.key b/tests/qmanifest/root/.gnupg/private-keys-v1.d/1F0A2C7F1E80A6EEEA3B9C30068FB3349702B3A7.key |
552 |
new file mode 100644 |
553 |
index 0000000..b4ed767 |
554 |
Binary files /dev/null and b/tests/qmanifest/root/.gnupg/private-keys-v1.d/1F0A2C7F1E80A6EEEA3B9C30068FB3349702B3A7.key differ |
555 |
|
556 |
diff --git a/tests/qmanifest/root/.gnupg/private-keys-v1.d/E37F9F3C8E4A940C625EC65B7070255F4AAA55F9.key b/tests/qmanifest/root/.gnupg/private-keys-v1.d/E37F9F3C8E4A940C625EC65B7070255F4AAA55F9.key |
557 |
new file mode 100644 |
558 |
index 0000000..4b07401 |
559 |
Binary files /dev/null and b/tests/qmanifest/root/.gnupg/private-keys-v1.d/E37F9F3C8E4A940C625EC65B7070255F4AAA55F9.key differ |
560 |
|
561 |
diff --git a/tests/qmanifest/root/.gnupg/pubring.kbx b/tests/qmanifest/root/.gnupg/pubring.kbx |
562 |
new file mode 100644 |
563 |
index 0000000..848dc93 |
564 |
Binary files /dev/null and b/tests/qmanifest/root/.gnupg/pubring.kbx differ |
565 |
|
566 |
diff --git a/tests/qmanifest/root/.gnupg/random_seed b/tests/qmanifest/root/.gnupg/random_seed |
567 |
new file mode 100644 |
568 |
index 0000000..d32d054 |
569 |
Binary files /dev/null and b/tests/qmanifest/root/.gnupg/random_seed differ |
570 |
|
571 |
diff --git a/tests/qmanifest/root/.gnupg/trustdb.gpg b/tests/qmanifest/root/.gnupg/trustdb.gpg |
572 |
new file mode 100644 |
573 |
index 0000000..78308c6 |
574 |
Binary files /dev/null and b/tests/qmanifest/root/.gnupg/trustdb.gpg differ |