Splint fiddles.
[platform/upstream/rpm.git] / lib / rpmchecksig.c
1 /** \ingroup rpmcli
2  * \file lib/rpmchecksig.c
3  * Verify the signature of a package.
4  */
5
6 #include "system.h"
7
8 #include "rpmio_internal.h"
9 #include <rpmcli.h>
10
11 #include "rpmdb.h"
12
13 #include "rpmts.h"
14
15 #include "rpmlead.h"
16 #include "signature.h"
17 #include "misc.h"       /* XXX for makeTempFile() */
18 #include "debug.h"
19
20 /*@access FD_t @*/              /* XXX stealing digests */
21 /*@access pgpDig @*/
22 /*@access pgpDigParams @*/
23
24 /*@unchecked@*/
25 static int _print_pkts = 0;
26
27 /**
28  */
29 /*@-boundsread@*/
30 static int manageFile(/*@out@*/ FD_t *fdp,
31                 /*@null@*/ /*@out@*/ const char **fnp,
32                 int flags, /*@unused@*/ int rc)
33         /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
34         /*@modifies *fdp, *fnp, rpmGlobalMacroContext,
35                 fileSystem, internalState @*/
36 {
37     const char *fn;
38     FD_t fd;
39
40     if (fdp == NULL)    /* programmer error */
41         return 1;
42
43 /*@-boundswrite@*/
44     /* close and reset *fdp to NULL */
45     if (*fdp && (fnp == NULL || *fnp == NULL)) {
46         (void) Fclose(*fdp);
47         *fdp = NULL;
48         return 0;
49     }
50
51     /* open a file and set *fdp */
52     if (*fdp == NULL && fnp != NULL && *fnp != NULL) {
53         fd = Fopen(*fnp, ((flags & O_WRONLY) ? "w.ufdio" : "r.ufdio"));
54         if (fd == NULL || Ferror(fd)) {
55             rpmError(RPMERR_OPEN, _("%s: open failed: %s\n"), *fnp,
56                 Fstrerror(fd));
57             return 1;
58         }
59         *fdp = fd;
60         return 0;
61     }
62
63     /* open a temp file */
64     if (*fdp == NULL && (fnp == NULL || *fnp == NULL)) {
65         fn = NULL;
66         if (makeTempFile(NULL, (fnp ? &fn : NULL), &fd)) {
67             rpmError(RPMERR_MAKETEMP, _("makeTempFile failed\n"));
68             return 1;
69         }
70         if (fnp != NULL)
71             *fnp = fn;
72         *fdp = fdLink(fd, "manageFile return");
73         fd = fdFree(fd, "manageFile return");
74         return 0;
75     }
76 /*@=boundswrite@*/
77
78     /* no operation */
79     if (*fdp != NULL && fnp != NULL && *fnp != NULL)
80         return 0;
81
82     /* XXX never reached */
83     return 1;
84 }
85 /*@=boundsread@*/
86
87 /**
88  * Copy header+payload, calculating digest(s) on the fly.
89  */
90 /*@-boundsread@*/
91 static int copyFile(FD_t *sfdp, const char **sfnp,
92                 FD_t *tfdp, const char **tfnp)
93         /*@globals rpmGlobalMacroContext,
94                 fileSystem, internalState @*/
95         /*@modifies *sfdp, *sfnp, *tfdp, *tfnp, rpmGlobalMacroContext,
96                 fileSystem, internalState @*/
97 {
98     unsigned char buf[BUFSIZ];
99     ssize_t count;
100     int rc = 1;
101
102     if (manageFile(sfdp, sfnp, O_RDONLY, 0))
103         goto exit;
104     if (manageFile(tfdp, tfnp, O_WRONLY|O_CREAT|O_TRUNC, 0))
105         goto exit;
106
107     while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), *sfdp)) > 0)
108     {
109         if (Fwrite(buf, sizeof(buf[0]), count, *tfdp) != count) {
110             rpmError(RPMERR_FWRITE, _("%s: Fwrite failed: %s\n"), *tfnp,
111                 Fstrerror(*tfdp));
112             goto exit;
113         }
114     }
115     if (count < 0) {
116         rpmError(RPMERR_FREAD, _("%s: Fread failed: %s\n"), *sfnp, Fstrerror(*sfdp));
117         goto exit;
118     }
119
120     rc = 0;
121
122 exit:
123     if (*sfdp)  (void) manageFile(sfdp, NULL, 0, rc);
124     if (*tfdp)  (void) manageFile(tfdp, NULL, 0, rc);
125     return rc;
126 }
127 /*@=boundsread@*/
128
129 /**
130  * Retrieve signer fingerprint from an OpenPGP signature tag.
131  * @param sig           signature header
132  * @param sigtag        signature tag
133  * @retval signid       signer fingerprint
134  * @return              0 on success
135  */
136 static int getSignid(Header sig, int sigtag, unsigned char * signid)
137         /*@globals fileSystem, internalState @*/
138         /*@modifies *signid, fileSystem, internalState @*/
139 {
140     void * pkt = NULL;
141     int_32 pkttyp = 0;
142     int_32 pktlen = 0;
143     int rc = 1;
144
145     if (headerGetEntry(sig, sigtag, &pkttyp, &pkt, &pktlen) && pkt != NULL) {
146         pgpDig dig = pgpNewDig();
147
148         if (!pgpPrtPkts(pkt, pktlen, dig, 0)) {
149 /*@-bounds@*/
150             memcpy(signid, dig->signature.signid, sizeof(dig->signature.signid));
151 /*@=bounds@*/
152             rc = 0;
153         }
154      
155         dig = pgpFreeDig(dig);
156     }
157     pkt = headerFreeData(pkt, pkttyp);
158     return rc;
159 }
160
161 /** \ingroup rpmcli
162  * Create/modify elements in signature header.
163  * @param ts            transaction set
164  * @param qva           mode flags and parameters
165  * @param argv          array of package file names (NULL terminated)
166  * @return              0 on success
167  */
168 static int rpmReSign(/*@unused@*/ rpmts ts,
169                 QVA_t qva, const char ** argv)
170         /*@globals rpmGlobalMacroContext,
171                 fileSystem, internalState @*/
172         /*@modifies rpmGlobalMacroContext,
173                 fileSystem, internalState @*/
174 {
175     FD_t fd = NULL;
176     FD_t ofd = NULL;
177     struct rpmlead lead, *l = &lead;
178     int_32 sigtag;
179     const char *rpm, *trpm;
180     const char *sigtarget = NULL;
181     char tmprpm[1024+1];
182     Header sigh = NULL;
183     const char * msg;
184     void * uh = NULL;
185     int_32 uht, uhc;
186     int res = EXIT_FAILURE;
187     rpmRC rc;
188     int xx;
189     
190     tmprpm[0] = '\0';
191     /*@-branchstate@*/
192 /*@-boundsread@*/
193     if (argv)
194     while ((rpm = *argv++) != NULL)
195 /*@=boundsread@*/
196     {
197
198         fprintf(stdout, "%s:\n", rpm);
199
200         if (manageFile(&fd, &rpm, O_RDONLY, 0))
201             goto exit;
202
203 /*@-boundswrite@*/
204         memset(l, 0, sizeof(*l));
205 /*@=boundswrite@*/
206         rc = readLead(fd, l);
207         if (rc != RPMRC_OK) {
208             rpmError(RPMERR_READLEAD, _("%s: not an rpm package\n"), rpm);
209             goto exit;
210         }
211         switch (l->major) {
212         case 1:
213             rpmError(RPMERR_BADSIGTYPE, _("%s: Can't sign v1 packaging\n"), rpm);
214             goto exit;
215             /*@notreached@*/ /*@switchbreak@*/ break;
216         case 2:
217             rpmError(RPMERR_BADSIGTYPE, _("%s: Can't re-sign v2 packaging\n"), rpm);
218             goto exit;
219             /*@notreached@*/ /*@switchbreak@*/ break;
220         default:
221             /*@switchbreak@*/ break;
222         }
223
224         msg = NULL;
225         rc = rpmReadSignature(fd, &sigh, l->signature_type, &msg);
226         switch (rc) {
227         default:
228             rpmError(RPMERR_SIGGEN, _("%s: rpmReadSignature failed: %s"), rpm,
229                         (msg && *msg ? msg : "\n"));
230             msg = _free(msg);
231             goto exit;
232             /*@notreached@*/ /*@switchbreak@*/ break;
233         case RPMRC_OK:
234             if (sigh == NULL) {
235                 rpmError(RPMERR_SIGGEN, _("%s: No signature available\n"), rpm);
236                 goto exit;
237             }
238             /*@switchbreak@*/ break;
239         }
240         msg = _free(msg);
241
242         /* Write the header and archive to a temp file */
243         /* ASSERT: ofd == NULL && sigtarget == NULL */
244         if (copyFile(&fd, &rpm, &ofd, &sigtarget))
245             goto exit;
246         /* Both fd and ofd are now closed. sigtarget contains tempfile name. */
247         /* ASSERT: fd == NULL && ofd == NULL */
248
249         /* Dump the immutable region (if present). */
250         if (headerGetEntry(sigh, RPMTAG_HEADERSIGNATURES, &uht, &uh, &uhc)) {
251             HeaderIterator hi;
252             int_32 tag, type, count;
253             hPTR_t ptr;
254             Header oh;
255             Header nh;
256
257             nh = headerNew();
258             if (nh == NULL) {
259                 uh = headerFreeData(uh, uht);
260                 goto exit;
261             }
262
263             oh = headerCopyLoad(uh);
264             for (hi = headerInitIterator(oh);
265                 headerNextIterator(hi, &tag, &type, &ptr, &count);
266                 ptr = headerFreeData(ptr, type))
267             {
268                 if (ptr)
269                     xx = headerAddEntry(nh, tag, type, ptr, count);
270             }
271             hi = headerFreeIterator(hi);
272             oh = headerFree(oh);
273
274             sigh = headerFree(sigh);
275             sigh = headerLink(nh);
276             nh = headerFree(nh);
277         }
278
279         /* Eliminate broken digest values. */
280         xx = headerRemoveEntry(sigh, RPMSIGTAG_LEMD5_1);
281         xx = headerRemoveEntry(sigh, RPMSIGTAG_LEMD5_2);
282         xx = headerRemoveEntry(sigh, RPMSIGTAG_BADSHA1_1);
283         xx = headerRemoveEntry(sigh, RPMSIGTAG_BADSHA1_2);
284
285         /* Toss and recalculate header+payload size and digests. */
286         xx = headerRemoveEntry(sigh, RPMSIGTAG_SIZE);
287         xx = rpmAddSignature(sigh, sigtarget, RPMSIGTAG_SIZE, qva->passPhrase);
288         xx = headerRemoveEntry(sigh, RPMSIGTAG_MD5);
289         xx = rpmAddSignature(sigh, sigtarget, RPMSIGTAG_MD5, qva->passPhrase);
290         xx = headerRemoveEntry(sigh, RPMSIGTAG_SHA1);
291         xx = rpmAddSignature(sigh, sigtarget, RPMSIGTAG_SHA1, qva->passPhrase);
292
293         /* If gpg/pgp is configured, replace the signature. */
294         if ((sigtag = rpmLookupSignatureType(RPMLOOKUPSIG_QUERY)) > 0) {
295             unsigned char oldsignid[8], newsignid[8];
296
297             /* Grab the old signature fingerprint (if any) */
298             memset(oldsignid, 0, sizeof(oldsignid));
299             xx = getSignid(sigh, sigtag, oldsignid);
300
301             switch (sigtag) {
302             case RPMSIGTAG_GPG:
303                 xx = headerRemoveEntry(sigh, RPMSIGTAG_DSA);
304                 /*@fallthrough@*/
305             case RPMSIGTAG_PGP5:
306             case RPMSIGTAG_PGP:
307                 xx = headerRemoveEntry(sigh, RPMSIGTAG_RSA);
308                 /*@switchbreak@*/ break;
309             }
310
311             xx = headerRemoveEntry(sigh, sigtag);
312             xx = rpmAddSignature(sigh, sigtarget, sigtag, qva->passPhrase);
313
314             /* If package was previously signed, check for same signer. */
315             memset(newsignid, 0, sizeof(newsignid));
316             if (memcmp(oldsignid, newsignid, sizeof(oldsignid))) {
317
318                 /* Grab the new signature fingerprint */
319                 xx = getSignid(sigh, sigtag, newsignid);
320
321                 /* If same signer, skip resigning the package. */
322                 if (!memcmp(oldsignid, newsignid, sizeof(oldsignid))) {
323
324                     rpmMessage(RPMMESS_WARNING,
325                         _("%s: was already signed by key ID %s, skipping\n"),
326                         rpm, pgpHexStr(newsignid+4, sizeof(newsignid)-4));
327
328                     /* Clean up intermediate target */
329                     xx = unlink(sigtarget);
330                     sigtarget = _free(sigtarget);
331                     continue;
332                 }
333             }
334
335         }
336
337         /* Reallocate the signature into one contiguous region. */
338         sigh = headerReload(sigh, RPMTAG_HEADERSIGNATURES);
339         if (sigh == NULL)       /* XXX can't happen */
340             goto exit;
341
342         /* Write the lead/signature of the output rpm */
343 /*@-boundswrite@*/
344         strcpy(tmprpm, rpm);
345         strcat(tmprpm, ".XXXXXX");
346 /*@=boundswrite@*/
347         (void) mktemp(tmprpm);
348         trpm = tmprpm;
349
350         if (manageFile(&ofd, &trpm, O_WRONLY|O_CREAT|O_TRUNC, 0))
351             goto exit;
352
353         l->signature_type = RPMSIGTYPE_HEADERSIG;
354         rc = writeLead(ofd, l);
355         if (rc != RPMRC_OK) {
356             rpmError(RPMERR_WRITELEAD, _("%s: writeLead failed: %s\n"), trpm,
357                 Fstrerror(ofd));
358             goto exit;
359         }
360
361         if (rpmWriteSignature(ofd, sigh)) {
362             rpmError(RPMERR_SIGGEN, _("%s: rpmWriteSignature failed: %s\n"), trpm,
363                 Fstrerror(ofd));
364             goto exit;
365         }
366
367         /* Append the header and archive from the temp file */
368         /* ASSERT: fd == NULL && ofd != NULL */
369         if (copyFile(&fd, &sigtarget, &ofd, &trpm))
370             goto exit;
371         /* Both fd and ofd are now closed. */
372         /* ASSERT: fd == NULL && ofd == NULL */
373
374         /* Move final target into place. */
375         xx = unlink(rpm);
376         xx = rename(trpm, rpm);
377         tmprpm[0] = '\0';
378
379         /* Clean up intermediate target */
380         xx = unlink(sigtarget);
381         sigtarget = _free(sigtarget);
382     }
383     /*@=branchstate@*/
384
385     res = 0;
386
387 exit:
388     if (fd)     (void) manageFile(&fd, NULL, 0, res);
389     if (ofd)    (void) manageFile(&ofd, NULL, 0, res);
390
391     sigh = rpmFreeSignature(sigh);
392
393     if (sigtarget) {
394         xx = unlink(sigtarget);
395         sigtarget = _free(sigtarget);
396     }
397     if (tmprpm[0] != '\0') {
398         xx = unlink(tmprpm);
399         tmprpm[0] = '\0';
400     }
401
402     return res;
403 }
404
405 int rpmcliImportPubkey(const rpmts ts, const unsigned char * pkt, ssize_t pktlen)
406 {
407     const char * afmt = "%{pubkeys:armor}";
408     const char * group = "Public Keys";
409     const char * license = "pubkey";
410     const char * buildhost = "localhost";
411     int_32 pflags = (RPMSENSE_KEYRING|RPMSENSE_EQUAL);
412     int_32 zero = 0;
413     pgpDig dig = NULL;
414     pgpDigParams pubp = NULL;
415     const char * d = NULL;
416     const char * enc = NULL;
417     const char * n = NULL;
418     const char * u = NULL;
419     const char * v = NULL;
420     const char * r = NULL;
421     const char * evr = NULL;
422     Header h = NULL;
423     int rc = 1;         /* assume failure */
424     char * t;
425     int xx;
426
427     if (pkt == NULL || pktlen <= 0)
428         return -1;
429     if (rpmtsOpenDB(ts, (O_RDWR|O_CREAT)))
430         return -1;
431
432     if ((enc = b64encode(pkt, pktlen)) == NULL)
433         goto exit;
434
435     dig = pgpNewDig();
436
437     /* Build header elements. */
438     (void) pgpPrtPkts(pkt, pktlen, dig, 0);
439     pubp = &dig->pubkey;
440
441 /*@-boundswrite@*/
442     v = t = xmalloc(16+1);
443     t = stpcpy(t, pgpHexStr(pubp->signid, sizeof(pubp->signid)));
444
445     r = t = xmalloc(8+1);
446     t = stpcpy(t, pgpHexStr(pubp->time, sizeof(pubp->time)));
447
448     n = t = xmalloc(sizeof("gpg()")+8);
449     t = stpcpy( stpcpy( stpcpy(t, "gpg("), v+8), ")");
450
451     /*@-nullpass@*/ /* FIX: pubp->userid may be NULL */
452     u = t = xmalloc(sizeof("gpg()")+strlen(pubp->userid));
453     t = stpcpy( stpcpy( stpcpy(t, "gpg("), pubp->userid), ")");
454     /*@=nullpass@*/
455
456     evr = t = xmalloc(sizeof("4X:-")+strlen(v)+strlen(r));
457     t = stpcpy(t, (pubp->version == 4 ? "4:" : "3:"));
458     t = stpcpy( stpcpy( stpcpy(t, v), "-"), r);
459 /*@=boundswrite@*/
460
461     /* Check for pre-existing header. */
462
463     /* Build pubkey header. */
464     h = headerNew();
465
466     xx = headerAddOrAppendEntry(h, RPMTAG_PUBKEYS,
467                         RPM_STRING_ARRAY_TYPE, &enc, 1);
468
469     d = headerSprintf(h, afmt, rpmTagTable, rpmHeaderFormats, NULL);
470     if (d == NULL)
471         goto exit;
472
473     xx = headerAddEntry(h, RPMTAG_NAME, RPM_STRING_TYPE, "gpg-pubkey", 1);
474     xx = headerAddEntry(h, RPMTAG_VERSION, RPM_STRING_TYPE, v+8, 1);
475     xx = headerAddEntry(h, RPMTAG_RELEASE, RPM_STRING_TYPE, r, 1);
476     xx = headerAddEntry(h, RPMTAG_DESCRIPTION, RPM_STRING_TYPE, d, 1);
477     xx = headerAddEntry(h, RPMTAG_GROUP, RPM_STRING_TYPE, group, 1);
478     xx = headerAddEntry(h, RPMTAG_LICENSE, RPM_STRING_TYPE, license, 1);
479     xx = headerAddEntry(h, RPMTAG_SUMMARY, RPM_STRING_TYPE, u, 1);
480
481     xx = headerAddEntry(h, RPMTAG_SIZE, RPM_INT32_TYPE, &zero, 1);
482
483     xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME,
484                         RPM_STRING_ARRAY_TYPE, &u, 1);
485     xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION,
486                         RPM_STRING_ARRAY_TYPE, &evr, 1);
487     xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS,
488                         RPM_INT32_TYPE, &pflags, 1);
489
490     xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME,
491                         RPM_STRING_ARRAY_TYPE, &n, 1);
492     xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION,
493                         RPM_STRING_ARRAY_TYPE, &evr, 1);
494     xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS,
495                         RPM_INT32_TYPE, &pflags, 1);
496
497     xx = headerAddEntry(h, RPMTAG_RPMVERSION, RPM_STRING_TYPE, RPMVERSION, 1);
498
499     /* XXX W2DO: tag value inheirited from parent? */
500     xx = headerAddEntry(h, RPMTAG_BUILDHOST, RPM_STRING_TYPE, buildhost, 1);
501     {   int_32 tid = rpmtsGetTid(ts);
502         xx = headerAddEntry(h, RPMTAG_INSTALLTIME, RPM_INT32_TYPE, &tid, 1);
503         /* XXX W2DO: tag value inheirited from parent? */
504         xx = headerAddEntry(h, RPMTAG_BUILDTIME, RPM_INT32_TYPE, &tid, 1);
505     }
506
507 #ifdef  NOTYET
508     /* XXX W2DO: tag value inheirited from parent? */
509     xx = headerAddEntry(h, RPMTAG_SOURCERPM, RPM_STRING_TYPE, fn, 1);
510 #endif
511
512     /* Add header to database. */
513     rc = rpmdbAdd(rpmtsGetRdb(ts), rpmtsGetTid(ts), h, NULL, NULL);
514     if (xx != 0)
515         goto exit;
516
517 exit:
518     /* Clean up. */
519     h = headerFree(h);
520     dig = pgpFreeDig(dig);
521     n = _free(n);
522     u = _free(u);
523     v = _free(v);
524     r = _free(r);
525     evr = _free(evr);
526     enc = _free(enc);
527     d = _free(d);
528     
529     return rc;
530 }
531
532 /** \ingroup rpmcli
533  * Import public key(s).
534  * @todo Implicit --update policy for gpg-pubkey headers.
535  * @param ts            transaction set
536  * @param qva           mode flags and parameters
537  * @param argv          array of pubkey file names (NULL terminated)
538  * @return              0 on success
539  */
540 static int rpmcliImportPubkeys(const rpmts ts,
541                 /*@unused@*/ QVA_t qva,
542                 /*@null@*/ const char ** argv)
543         /*@globals RPMVERSION, rpmGlobalMacroContext,
544                 fileSystem, internalState @*/
545         /*@modifies ts, rpmGlobalMacroContext,
546                 fileSystem, internalState @*/
547 {
548     const char * fn;
549     const unsigned char * pkt = NULL;
550     ssize_t pktlen = 0;
551     int res = 0;
552     int rc;
553
554     if (argv == NULL) return res;
555
556     /*@-branchstate@*/
557 /*@-boundsread@*/
558     while ((fn = *argv++) != NULL) {
559 /*@=boundsread@*/
560
561 rpmtsClean(ts);
562         pkt = _free(pkt);
563
564         /* Read pgp packet. */
565         if ((rc = pgpReadPkts(fn, &pkt, &pktlen)) <= 0) {
566             rpmError(RPMERR_IMPORT, _("%s: import read failed.\n"), fn);
567             res++;
568             continue;
569         }
570         if (rc != PGPARMOR_PUBKEY) {
571             rpmError(RPMERR_IMPORT, _("%s: not an armored public key.\n"), fn);
572             res++;
573             continue;
574         }
575
576         /* Import pubkey packet(s). */
577         if ((rc = rpmcliImportPubkey(ts, pkt, pktlen)) != 0) {
578             rpmError(RPMERR_IMPORT, _("%s: import failed.\n"), fn);
579             res++;
580             continue;
581         }
582
583     }
584     /*@=branchstate@*/
585     
586 rpmtsClean(ts);
587     pkt = _free(pkt);
588     return res;
589 }
590
591 /*@unchecked@*/
592 static unsigned char header_magic[8] = {
593         0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
594 };
595
596 /**
597  * @todo If the GPG key was known available, the md5 digest could be skipped.
598  */
599 static int readFile(FD_t fd, const char * fn, pgpDig dig)
600         /*@globals fileSystem, internalState @*/
601         /*@modifies fd, *dig, fileSystem, internalState @*/
602 {
603     unsigned char buf[4*BUFSIZ];
604     ssize_t count;
605     int rc = 1;
606     int i;
607
608     dig->nbytes = 0;
609
610     /* Read the header from the package. */
611     {   Header h = headerRead(fd, HEADER_MAGIC_YES);
612         if (h == NULL) {
613             rpmError(RPMERR_FREAD, _("%s: headerRead failed\n"), fn);
614             goto exit;
615         }
616
617         dig->nbytes += headerSizeof(h, HEADER_MAGIC_YES);
618
619         if (headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) {
620             void * uh;
621             int_32 uht, uhc;
622         
623             if (!headerGetEntry(h, RPMTAG_HEADERIMMUTABLE, &uht, &uh, &uhc)
624             ||   uh == NULL)
625             {
626                 h = headerFree(h);
627                 rpmError(RPMERR_FREAD, _("%s: headerGetEntry failed\n"), fn);
628                 goto exit;
629             }
630             dig->hdrsha1ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE);
631             (void) rpmDigestUpdate(dig->hdrsha1ctx, header_magic, sizeof(header_magic));
632             (void) rpmDigestUpdate(dig->hdrsha1ctx, uh, uhc);
633             uh = headerFreeData(uh, uht);
634         }
635         h = headerFree(h);
636     }
637
638     /* Read the payload from the package. */
639     while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0)
640         dig->nbytes += count;
641     if (count < 0) {
642         rpmError(RPMERR_FREAD, _("%s: Fread failed: %s\n"), fn, Fstrerror(fd));
643         goto exit;
644     }
645
646     /* XXX Steal the digest-in-progress from the file handle. */
647     for (i = fd->ndigests - 1; i >= 0; i--) {
648         FDDIGEST_t fddig = fd->digests + i;
649         if (fddig->hashctx == NULL)
650             continue;
651         if (fddig->hashalgo == PGPHASHALGO_MD5) {
652 assert(dig->md5ctx == NULL);
653             dig->md5ctx = fddig->hashctx;
654             fddig->hashctx = NULL;
655             continue;
656         }
657         if (fddig->hashalgo == PGPHASHALGO_SHA1) {
658 assert(dig->sha1ctx == NULL);
659             dig->sha1ctx = fddig->hashctx;
660             fddig->hashctx = NULL;
661             continue;
662         }
663     }
664
665     rc = 0;
666
667 exit:
668     return rc;
669 }
670
671 int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd,
672                 const char * fn)
673 {
674     int res2, res3;
675     struct rpmlead lead, *l = &lead;
676     char result[1024];
677     char buf[8192], * b;
678     char missingKeys[7164], * m;
679     char untrustedKeys[7164], * u;
680     int_32 sigtag;
681     int_32 sigtype;
682     const void * sig;
683     pgpDig dig;
684     pgpDigParams sigp;
685     int_32 siglen;
686     Header sigh = NULL;
687     HeaderIterator hi;
688     const char * msg;
689     int res = 0;
690     int xx;
691     rpmRC rc;
692     int nodigests = !(qva->qva_flags & VERIFY_DIGEST);
693     int nosignatures = !(qva->qva_flags & VERIFY_SIGNATURE);
694
695     {
696 /*@-boundswrite@*/
697         memset(l, 0, sizeof(*l));
698 /*@=boundswrite@*/
699         rc = readLead(fd, l);
700         if (rc != RPMRC_OK) {
701             rpmError(RPMERR_READLEAD, _("%s: not an rpm package\n"), fn);
702             res++;
703             goto exit;
704         }
705         switch (l->major) {
706         case 1:
707             rpmError(RPMERR_BADSIGTYPE, _("%s: No signature available (v1.0 RPM)\n"), fn);
708             res++;
709             goto exit;
710             /*@notreached@*/ /*@switchbreak@*/ break;
711         default:
712             /*@switchbreak@*/ break;
713         }
714
715         msg = NULL;
716         rc = rpmReadSignature(fd, &sigh, l->signature_type, &msg);
717         switch (rc) {
718         default:
719             rpmError(RPMERR_SIGGEN, _("%s: rpmReadSignature failed: %s"), fn,
720                         (msg && *msg ? msg : "\n"));
721             msg = _free(msg);
722             res++;
723             goto exit;
724             /*@notreached@*/ /*@switchbreak@*/ break;
725         case RPMRC_OK:
726             if (sigh == NULL) {
727                 rpmError(RPMERR_SIGGEN, _("%s: No signature available\n"), fn);
728                 res++;
729                 goto exit;
730             }
731             /*@switchbreak@*/ break;
732         }
733         msg = _free(msg);
734
735         /* Grab a hint of what needs doing to avoid duplication. */
736         sigtag = 0;
737         if (sigtag == 0 && !nosignatures) {
738             if (headerIsEntry(sigh, RPMSIGTAG_DSA))
739                 sigtag = RPMSIGTAG_DSA;
740             else if (headerIsEntry(sigh, RPMSIGTAG_RSA))
741                 sigtag = RPMSIGTAG_RSA;
742             else if (headerIsEntry(sigh, RPMSIGTAG_GPG))
743                 sigtag = RPMSIGTAG_GPG;
744             else if (headerIsEntry(sigh, RPMSIGTAG_PGP))
745                 sigtag = RPMSIGTAG_PGP;
746         }
747         if (sigtag == 0 && !nodigests) {
748             if (headerIsEntry(sigh, RPMSIGTAG_MD5))
749                 sigtag = RPMSIGTAG_MD5;
750             else if (headerIsEntry(sigh, RPMSIGTAG_SHA1))
751                 sigtag = RPMSIGTAG_SHA1;        /* XXX never happens */
752         }
753
754         if (headerIsEntry(sigh, RPMSIGTAG_PGP)
755         ||  headerIsEntry(sigh, RPMSIGTAG_PGP5)
756         ||  headerIsEntry(sigh, RPMSIGTAG_MD5))
757             fdInitDigest(fd, PGPHASHALGO_MD5, 0);
758         if (headerIsEntry(sigh, RPMSIGTAG_GPG))
759             fdInitDigest(fd, PGPHASHALGO_SHA1, 0);
760
761         dig = rpmtsDig(ts);
762         sigp = rpmtsSignature(ts);
763
764         /* Read the file, generating digest(s) on the fly. */
765         if (dig == NULL || sigp == NULL || readFile(fd, fn, dig)) {
766             res++;
767             goto exit;
768         }
769
770         res2 = 0;
771         b = buf;                *b = '\0';
772         m = missingKeys;        *m = '\0';
773         u = untrustedKeys;      *u = '\0';
774         sprintf(b, "%s:%c", fn, (rpmIsVerbose() ? '\n' : ' ') );
775         b += strlen(b);
776
777         for (hi = headerInitIterator(sigh);
778             headerNextIterator(hi, &sigtag, &sigtype, &sig, &siglen) != 0;
779             (void) rpmtsSetSig(ts, sigtag, sigtype, NULL, siglen))
780         {
781
782             if (sig == NULL) /* XXX can't happen */
783                 continue;
784
785             (void) rpmtsSetSig(ts, sigtag, sigtype, sig, siglen);
786
787             /* Clean up parameters from previous sigtag. */
788             pgpCleanDig(dig);
789
790             switch (sigtag) {
791             case RPMSIGTAG_RSA:
792             case RPMSIGTAG_DSA:
793             case RPMSIGTAG_GPG:
794             case RPMSIGTAG_PGP5:        /* XXX legacy */
795             case RPMSIGTAG_PGP:
796                 if (nosignatures)
797                      continue;
798                 xx = pgpPrtPkts(sig, siglen, dig,
799                         (_print_pkts & rpmIsDebug()));
800
801                 /* XXX only V3 signatures for now. */
802                 if (sigp->version != 3) {
803                     rpmError(RPMERR_SIGVFY,
804                 _("only V3 signatures can be verified, skipping V%u signature\n"),
805                         sigp->version);
806                     continue;
807                 }
808                 /*@switchbreak@*/ break;
809             case RPMSIGTAG_SHA1:
810                 if (nodigests)
811                      continue;
812                 /* XXX Don't bother with header sha1 if header dsa. */
813                 if (!nosignatures && sigtag == RPMSIGTAG_DSA)
814                     continue;
815                 /*@switchbreak@*/ break;
816             case RPMSIGTAG_LEMD5_2:
817             case RPMSIGTAG_LEMD5_1:
818             case RPMSIGTAG_MD5:
819                 if (nodigests)
820                      continue;
821                 /*
822                  * Don't bother with md5 if pgp, as RSA/MD5 is more reliable
823                  * than the -- now unsupported -- legacy md5 breakage.
824                  */
825                 if (!nosignatures && sigtag == RPMSIGTAG_PGP)
826                     continue;
827                 /*@switchbreak@*/ break;
828             default:
829                 continue;
830                 /*@notreached@*/ /*@switchbreak@*/ break;
831             }
832
833             res3 = rpmVerifySignature(ts, result);
834
835 /*@-bounds@*/
836             if (res3) {
837                 if (rpmIsVerbose()) {
838                     b = stpcpy(b, "    ");
839                     b = stpcpy(b, result);
840                     res2 = 1;
841                 } else {
842                     char *tempKey;
843                     switch (sigtag) {
844                     case RPMSIGTAG_SIZE:
845                         b = stpcpy(b, "SIZE ");
846                         res2 = 1;
847                         /*@switchbreak@*/ break;
848                     case RPMSIGTAG_SHA1:
849                         b = stpcpy(b, "SHA1 ");
850                         res2 = 1;
851                         /*@switchbreak@*/ break;
852                     case RPMSIGTAG_LEMD5_2:
853                     case RPMSIGTAG_LEMD5_1:
854                     case RPMSIGTAG_MD5:
855                         b = stpcpy(b, "MD5 ");
856                         res2 = 1;
857                         /*@switchbreak@*/ break;
858                     case RPMSIGTAG_RSA:
859                         b = stpcpy(b, "RSA ");
860                         res2 = 1;
861                         /*@switchbreak@*/ break;
862                     case RPMSIGTAG_PGP5:        /* XXX legacy */
863                     case RPMSIGTAG_PGP:
864                         switch (res3) {
865                         case RPMRC_NOKEY:
866                             res2 = 1;
867                             /*@fallthrough@*/
868                         case RPMRC_NOTTRUSTED:
869                         {   int offset = 6;
870                             b = stpcpy(b, "(MD5) (PGP) ");
871                             tempKey = strstr(result, "ey ID");
872                             if (tempKey == NULL) {
873                                 tempKey = strstr(result, "keyid:");
874                                 offset = 9;
875                             }
876                             if (tempKey) {
877                               if (res3 == RPMRC_NOKEY) {
878                                 m = stpcpy(m, " PGP#");
879                                 m = stpncpy(m, tempKey + offset, 8);
880                                 *m = '\0';
881                               } else {
882                                 u = stpcpy(u, " PGP#");
883                                 u = stpncpy(u, tempKey + offset, 8);
884                                 *u = '\0';
885                               }
886                             }
887                         }   /*@innerbreak@*/ break;
888                         default:
889                             b = stpcpy(b, "MD5 PGP ");
890                             res2 = 1;
891                             /*@innerbreak@*/ break;
892                         }
893                         /*@switchbreak@*/ break;
894                     case RPMSIGTAG_DSA:
895                         b = stpcpy(b, "(SHA1) DSA ");
896                         res2 = 1;
897                         /*@switchbreak@*/ break;
898                     case RPMSIGTAG_GPG:
899                         /* Do not consider this a failure */
900                         switch (res3) {
901                         case RPMRC_NOKEY:
902                             b = stpcpy(b, "(GPG) ");
903                             m = stpcpy(m, " GPG#");
904                             tempKey = strstr(result, "ey ID");
905                             if (tempKey) {
906                                 m = stpncpy(m, tempKey+6, 8);
907                                 *m = '\0';
908                             }
909                             res2 = 1;
910                             /*@innerbreak@*/ break;
911                         default:
912                             b = stpcpy(b, "GPG ");
913                             res2 = 1;
914                             /*@innerbreak@*/ break;
915                         }
916                         /*@switchbreak@*/ break;
917                     default:
918                         b = stpcpy(b, "?UnknownSignatureType? ");
919                         res2 = 1;
920                         /*@switchbreak@*/ break;
921                     }
922                 }
923             } else {
924                 if (rpmIsVerbose()) {
925                     b = stpcpy(b, "    ");
926                     b = stpcpy(b, result);
927                 } else {
928                     switch (sigtag) {
929                     case RPMSIGTAG_SIZE:
930                         b = stpcpy(b, "size ");
931                         /*@switchbreak@*/ break;
932                     case RPMSIGTAG_SHA1:
933                         b = stpcpy(b, "sha1 ");
934                         /*@switchbreak@*/ break;
935                     case RPMSIGTAG_LEMD5_2:
936                     case RPMSIGTAG_LEMD5_1:
937                     case RPMSIGTAG_MD5:
938                         b = stpcpy(b, "md5 ");
939                         /*@switchbreak@*/ break;
940                     case RPMSIGTAG_RSA:
941                         b = stpcpy(b, "rsa ");
942                         /*@switchbreak@*/ break;
943                     case RPMSIGTAG_PGP5:        /* XXX legacy */
944                     case RPMSIGTAG_PGP:
945                         b = stpcpy(b, "(md5) pgp ");
946                         /*@switchbreak@*/ break;
947                     case RPMSIGTAG_DSA:
948                         b = stpcpy(b, "(sha1) dsa ");
949                         /*@switchbreak@*/ break;
950                     case RPMSIGTAG_GPG:
951                         b = stpcpy(b, "gpg ");
952                         /*@switchbreak@*/ break;
953                     default:
954                         b = stpcpy(b, "??? ");
955                         /*@switchbreak@*/ break;
956                     }
957                 }
958             }
959 /*@=bounds@*/
960         }
961         hi = headerFreeIterator(hi);
962
963         res += res2;
964
965         if (res2) {
966             if (rpmIsVerbose()) {
967                 rpmError(RPMERR_SIGVFY, "%s", buf);
968             } else {
969                 rpmError(RPMERR_SIGVFY, "%s%s%s%s%s%s%s%s\n", buf,
970                         _("NOT OK"),
971                         (missingKeys[0] != '\0') ? _(" (MISSING KEYS:") : "",
972                         missingKeys,
973                         (missingKeys[0] != '\0') ? _(") ") : "",
974                         (untrustedKeys[0] != '\0') ? _(" (UNTRUSTED KEYS:") : "",
975                         untrustedKeys,
976                         (untrustedKeys[0] != '\0') ? _(")") : "");
977
978             }
979         } else {
980             if (rpmIsVerbose()) {
981                 rpmError(RPMERR_SIGVFY, "%s", buf);
982             } else {
983                 rpmError(RPMERR_SIGVFY, "%s%s%s%s%s%s%s%s\n", buf,
984                         _("OK"),
985                         (missingKeys[0] != '\0') ? _(" (MISSING KEYS:") : "",
986                         missingKeys,
987                         (missingKeys[0] != '\0') ? _(") ") : "",
988                         (untrustedKeys[0] != '\0') ? _(" (UNTRUSTED KEYS:") : "",
989                         untrustedKeys,
990                         (untrustedKeys[0] != '\0') ? _(")") : "");
991             }
992         }
993
994     }
995
996 exit:
997     sigh = rpmFreeSignature(sigh);
998     rpmtsCleanDig(ts);
999     return res;
1000 }
1001
1002 int rpmcliSign(rpmts ts, QVA_t qva, const char ** argv)
1003 {
1004     const char * arg;
1005     int res = 0;
1006     int xx;
1007
1008     if (argv == NULL) return res;
1009
1010     switch (qva->qva_mode) {
1011     case RPMSIGN_CHK_SIGNATURE:
1012         break;
1013     case RPMSIGN_IMPORT_PUBKEY:
1014         return rpmcliImportPubkeys(ts, qva, argv);
1015         /*@notreached@*/ break;
1016     case RPMSIGN_NEW_SIGNATURE:
1017     case RPMSIGN_ADD_SIGNATURE:
1018         return rpmReSign(ts, qva, argv);
1019         /*@notreached@*/ break;
1020     case RPMSIGN_NONE:
1021     default:
1022         return -1;
1023         /*@notreached@*/ break;
1024     }
1025
1026     while ((arg = *argv++) != NULL) {
1027         FD_t fd;
1028
1029         if ((fd = Fopen(arg, "r.ufdio")) == NULL
1030          || Ferror(fd)
1031          || rpmVerifySignatures(qva, ts, fd, arg))
1032             res++;
1033
1034         if (fd != NULL) xx = Fclose(fd);
1035     }
1036
1037     return res;
1038 }