Fix minor header iterator leak in rpmReSign()
[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 <rpm/rpmlib.h>                 /* RPMSIGTAG & related */
9 #include <rpm/rpmpgp.h>
10 #include <rpm/rpmcli.h>
11 #include <rpm/rpmfileutil.h>    /* rpmMkTemp() */
12 #include <rpm/rpmdb.h>
13 #include <rpm/rpmts.h>
14 #include <rpm/rpmlog.h>
15 #include <rpm/rpmstring.h>
16 #include <rpm/rpmkeyring.h>
17
18 #include "rpmio/digest.h"
19 #include "rpmio/rpmio_internal.h"       /* fdSetBundle() */
20 #include "lib/rpmlead.h"
21 #include "lib/signature.h"
22
23 #include "debug.h"
24
25 int _print_pkts = 0;
26
27 static int closeFile(FD_t *fdp)
28 {
29     if (fdp == NULL || *fdp == NULL)
30         return 1;
31
32     /* close and reset *fdp to NULL */
33     (void) Fclose(*fdp);
34     *fdp = NULL;
35     return 0;
36 }
37
38 /**
39  */
40 static int manageFile(FD_t *fdp, const char *fn, int flags)
41 {
42     FD_t fd;
43
44     if (fdp == NULL || fn == NULL)      /* programmer error */
45         return 1;
46
47     /* open a file and set *fdp */
48     if (*fdp == NULL && fn != NULL) {
49         fd = Fopen(fn, (flags & O_ACCMODE) == O_WRONLY ? "w.ufdio" : "r.ufdio");
50         if (fd == NULL || Ferror(fd)) {
51             rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), fn,
52                 Fstrerror(fd));
53             return 1;
54         }
55         *fdp = fd;
56         return 0;
57     }
58
59     /* no operation */
60     if (*fdp != NULL && fn != NULL)
61         return 0;
62
63     /* XXX never reached */
64     return 1;
65 }
66
67 /**
68  * Copy header+payload, calculating digest(s) on the fly.
69  */
70 static int copyFile(FD_t *sfdp, const char *sfnp,
71                 FD_t *tfdp, const char *tfnp)
72 {
73     unsigned char buf[BUFSIZ];
74     ssize_t count;
75     int rc = 1;
76
77     if (manageFile(sfdp, sfnp, O_RDONLY))
78         goto exit;
79     if (manageFile(tfdp, tfnp, O_WRONLY|O_CREAT|O_TRUNC))
80         goto exit;
81
82     while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), *sfdp)) > 0)
83     {
84         if (Fwrite(buf, sizeof(buf[0]), count, *tfdp) != count) {
85             rpmlog(RPMLOG_ERR, _("%s: Fwrite failed: %s\n"), tfnp,
86                 Fstrerror(*tfdp));
87             goto exit;
88         }
89     }
90     if (count < 0) {
91         rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), sfnp, Fstrerror(*sfdp));
92         goto exit;
93     }
94     if (Fflush(*tfdp) != 0) {
95         rpmlog(RPMLOG_ERR, _("%s: Fflush failed: %s\n"), tfnp,
96             Fstrerror(*tfdp));
97     }
98
99     rc = 0;
100
101 exit:
102     if (*sfdp)  (void) closeFile(sfdp);
103     if (*tfdp)  (void) closeFile(tfdp);
104     return rc;
105 }
106
107 /**
108  * Retrieve signer fingerprint from an OpenPGP signature tag.
109  * @param sig           signature header
110  * @param sigtag        signature tag
111  * @retval signid       signer fingerprint
112  * @return              0 on success
113  */
114 static int getSignid(Header sig, rpmSigTag sigtag, pgpKeyID_t signid)
115 {
116     struct rpmtd_s pkt;
117     int rc = 1;
118
119     if (headerGet(sig, sigtag, &pkt, HEADERGET_DEFAULT) && pkt.data != NULL) {
120         pgpDig dig = pgpNewDig();
121
122         if (!pgpPrtPkts(pkt.data, pkt.count, dig, 0)) {
123             memcpy(signid, dig->signature.signid, sizeof(dig->signature.signid));
124             rc = 0;
125         }
126      
127         dig = pgpFreeDig(dig);
128         rpmtdFreeData(&pkt);
129     }
130     return rc;
131 }
132
133 /** \ingroup rpmcli
134  * Create/modify elements in signature header.
135  * @param ts            transaction set
136  * @param qva           mode flags and parameters
137  * @param argv          array of package file names (NULL terminated)
138  * @return              0 on success, -1 on error
139  */
140 static int rpmReSign(rpmts ts, QVA_t qva, ARGV_const_t argv)
141 {
142     FD_t fd = NULL;
143     FD_t ofd = NULL;
144     rpmlead lead;
145     rpmSigTag sigtag;
146     const char *rpm;
147     char *sigtarget = NULL, *trpm = NULL;
148     Header sigh = NULL;
149     char * msg;
150     int res = -1; /* assume failure */
151     int deleting = (qva->qva_mode == RPMSIGN_DEL_SIGNATURE);
152     int xx;
153     
154     if (argv)
155     while ((rpm = *argv++) != NULL)
156     {
157         rpmRC rc;
158         struct rpmtd_s utd;
159
160         fprintf(stdout, "%s:\n", rpm);
161
162         if (manageFile(&fd, rpm, O_RDONLY))
163             goto exit;
164
165         lead = rpmLeadNew();
166
167         if ((rc = rpmLeadRead(fd, lead)) == RPMRC_OK) {
168             const char *lmsg = NULL;
169             rc = rpmLeadCheck(lead, &lmsg);
170             if (rc != RPMRC_OK) 
171                 rpmlog(RPMLOG_ERR, "%s: %s\n", rpm, lmsg);
172         }
173
174         if (rc != RPMRC_OK) {
175             lead = rpmLeadFree(lead);
176             goto exit;
177         }
178
179         msg = NULL;
180         rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg);
181         switch (rc) {
182         default:
183             rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), rpm,
184                         (msg && *msg ? msg : "\n"));
185             msg = _free(msg);
186             goto exit;
187             break;
188         case RPMRC_OK:
189             if (sigh == NULL) {
190                 rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), rpm);
191                 goto exit;
192             }
193             break;
194         }
195         msg = _free(msg);
196
197         /* ASSERT: ofd == NULL && sigtarget == NULL */
198         ofd = rpmMkTempFile(NULL, &sigtarget);
199         if (ofd == NULL || Ferror(ofd)) {
200             rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n"));
201             goto exit;
202         }
203         /* Write the header and archive to a temp file */
204         if (copyFile(&fd, rpm, &ofd, sigtarget))
205             goto exit;
206         /* Both fd and ofd are now closed. sigtarget contains tempfile name. */
207         /* ASSERT: fd == NULL && ofd == NULL */
208
209         /* Dump the immutable region (if present). */
210         if (headerGet(sigh, RPMTAG_HEADERSIGNATURES, &utd, HEADERGET_DEFAULT)) {
211             HeaderIterator hi;
212             struct rpmtd_s copytd;
213             Header oh;
214             Header nh;
215
216             nh = headerNew();
217             if (nh == NULL) {
218                 rpmtdFreeData(&utd);
219                 goto exit;
220             }
221
222             oh = headerCopyLoad(utd.data);
223             hi = headerInitIterator(oh);
224             while (headerNext(hi, &copytd)) {
225                 if (copytd.data)
226                     xx = headerPut(nh, &copytd, HEADERPUT_DEFAULT);
227                 rpmtdFreeData(&copytd);
228             }
229             hi = headerFreeIterator(hi);
230             oh = headerFree(oh);
231
232             sigh = headerFree(sigh);
233             sigh = headerLink(nh);
234             nh = headerFree(nh);
235         }
236
237         /* Eliminate broken digest values. */
238         xx = headerDel(sigh, RPMSIGTAG_BADSHA1_1);
239         xx = headerDel(sigh, RPMSIGTAG_BADSHA1_2);
240
241         /* Toss and recalculate header+payload size and digests. */
242         {
243             rpmSigTag const sigs[] = {  RPMSIGTAG_SIZE, 
244                                         RPMSIGTAG_MD5,
245                                         RPMSIGTAG_SHA1,
246                                      };
247             int nsigs = sizeof(sigs) / sizeof(rpmSigTag);
248             for (int i = 0; i < nsigs; i++) {
249                 (void) headerDel(sigh, sigs[i]);
250                 if (rpmAddSignature(sigh, sigtarget, sigs[i], qva->passPhrase))
251                     goto exit;
252             }
253         }
254
255         if (deleting) { /* Nuke all the signature tags. */
256             xx = headerDel(sigh, RPMSIGTAG_GPG);
257             xx = headerDel(sigh, RPMSIGTAG_DSA);
258             xx = headerDel(sigh, RPMSIGTAG_PGP5);
259             xx = headerDel(sigh, RPMSIGTAG_PGP);
260             xx = headerDel(sigh, RPMSIGTAG_RSA);
261         } else          /* If gpg/pgp is configured, replace the signature. */
262         if ((sigtag = rpmLookupSignatureType(RPMLOOKUPSIG_QUERY)) > 0) {
263             pgpKeyID_t oldsignid, newsignid;
264
265             /* Grab the old signature fingerprint (if any) */
266             memset(oldsignid, 0, sizeof(oldsignid));
267             xx = getSignid(sigh, sigtag, oldsignid);
268
269             switch (sigtag) {
270             case RPMSIGTAG_DSA:
271                 xx = headerDel(sigh, RPMSIGTAG_GPG);
272                 break;
273             case RPMSIGTAG_RSA:
274                 xx = headerDel(sigh, RPMSIGTAG_PGP);
275                 break;
276             case RPMSIGTAG_GPG:
277                 xx = headerDel(sigh, RPMSIGTAG_DSA);
278             case RPMSIGTAG_PGP5:
279             case RPMSIGTAG_PGP:
280                 xx = headerDel(sigh, RPMSIGTAG_RSA);
281                 break;
282             default:
283                 break;
284             }
285
286             xx = headerDel(sigh, sigtag);
287             if (rpmAddSignature(sigh, sigtarget, sigtag, qva->passPhrase)) {
288                 goto exit;
289             }
290
291             /* If package was previously signed, check for same signer. */
292             memset(newsignid, 0, sizeof(newsignid));
293             if (memcmp(oldsignid, newsignid, sizeof(oldsignid))) {
294
295                 /* Grab the new signature fingerprint */
296                 xx = getSignid(sigh, sigtag, newsignid);
297
298                 /* If same signer, skip resigning the package. */
299                 if (!memcmp(oldsignid, newsignid, sizeof(oldsignid))) {
300
301                     char *signid = pgpHexStr(newsignid+4, sizeof(newsignid)-4);
302                     rpmlog(RPMLOG_WARNING,
303                         _("%s: was already signed by key ID %s, skipping\n"),
304                         rpm, signid);
305                     free(signid);
306
307                     /* Clean up intermediate target */
308                     xx = unlink(sigtarget);
309                     sigtarget = _free(sigtarget);
310                     continue;
311                 }
312             }
313         }
314
315         /* Reallocate the signature into one contiguous region. */
316         sigh = headerReload(sigh, RPMTAG_HEADERSIGNATURES);
317         if (sigh == NULL)       /* XXX can't happen */
318             goto exit;
319
320         rasprintf(&trpm, "%s.XXXXXX", rpm);
321         ofd = rpmMkTemp(trpm);
322         if (ofd == NULL || Ferror(ofd)) {
323             rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n"));
324             goto exit;
325         }
326
327         /* Write the lead/signature of the output rpm */
328         rc = rpmLeadWrite(ofd, lead);
329         lead = rpmLeadFree(lead);
330         if (rc != RPMRC_OK) {
331             rpmlog(RPMLOG_ERR, _("%s: writeLead failed: %s\n"), trpm,
332                 Fstrerror(ofd));
333             goto exit;
334         }
335
336         if (rpmWriteSignature(ofd, sigh)) {
337             rpmlog(RPMLOG_ERR, _("%s: rpmWriteSignature failed: %s\n"), trpm,
338                 Fstrerror(ofd));
339             goto exit;
340         }
341
342         /* Append the header and archive from the temp file */
343         /* ASSERT: fd == NULL && ofd != NULL */
344         if (copyFile(&fd, sigtarget, &ofd, trpm))
345             goto exit;
346         /* Both fd and ofd are now closed. */
347         /* ASSERT: fd == NULL && ofd == NULL */
348
349         /* Move final target into place, restore file permissions. */
350         {
351             struct stat st;
352             xx = stat(rpm, &st);
353             xx = unlink(rpm);
354             xx = rename(trpm, rpm);
355             xx = chmod(rpm, st.st_mode);
356         }
357         trpm = _free(trpm);
358
359         /* Clean up intermediate target */
360         xx = unlink(sigtarget);
361         sigtarget = _free(sigtarget);
362     }
363
364     res = 0;
365
366 exit:
367     if (fd)     (void) closeFile(&fd);
368     if (ofd)    (void) closeFile(&ofd);
369
370     sigh = rpmFreeSignature(sigh);
371
372     if (sigtarget) {
373         xx = unlink(sigtarget);
374         sigtarget = _free(sigtarget);
375     }
376     if (trpm) {
377         (void) unlink(trpm);
378         free(trpm);
379     }
380
381     return res;
382 }
383
384 /** \ingroup rpmcli
385  * Import public key(s).
386  * @todo Implicit --update policy for gpg-pubkey headers.
387  * @param ts            transaction set
388  * @param argv          array of pubkey file names (NULL terminated)
389  * @return              0 on success
390  */
391 static int rpmcliImportPubkeys(const rpmts ts, ARGV_const_t argv)
392 {
393     const char * fn;
394     int res = 0;
395
396     while ((fn = *argv++) != NULL) {
397         unsigned char * pkt = NULL;
398         size_t pktlen = 0;
399         char * t = NULL;
400         int rc;
401
402         /* If arg looks like a keyid, then attempt keyserver retrieve. */
403         if (rstreqn(fn, "0x", 2)) {
404             const char * s;
405             int i;
406             for (i = 0, s = fn+2; *s && isxdigit(*s); s++, i++)
407                 {};
408             if (i == 8 || i == 16) {
409                 t = rpmExpand("%{_hkp_keyserver_query}", fn+2, NULL);
410                 if (t && *t != '%')
411                     fn = t;
412             }
413         }
414
415         /* Read pgp packet. */
416         rc = pgpReadPkts(fn, &pkt, &pktlen);
417         if (rc == PGPARMOR_PUBKEY) {
418             /* Import pubkey packet(s). */
419             if (rpmtsImportPubkey(ts, pkt, pktlen) != RPMRC_OK) {
420                 rpmlog(RPMLOG_ERR, _("%s: import failed.\n"), fn);
421                 res++;
422             }
423         } else if (rc < 0) {
424             rpmlog(RPMLOG_ERR, _("%s: import read failed(%d).\n"), fn, rc);
425             res++;
426         } else {
427             rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn);
428             res++;
429         }
430
431         free(pkt);
432         free(t);
433     }
434     
435     return res;
436 }
437
438 /**
439  * @todo If the GPG key was known available, the md5 digest could be skipped.
440  */
441 static int readFile(FD_t fd, const char * fn, pgpDig dig,
442                     rpmDigestBundle plbundle, rpmDigestBundle hdrbundle)
443 {
444     unsigned char buf[4*BUFSIZ];
445     ssize_t count;
446     int rc = 1;
447     Header h = NULL;
448
449     /* Read the header from the package. */
450     if ((h = headerRead(fd, HEADER_MAGIC_YES)) == NULL) {
451         rpmlog(RPMLOG_ERR, _("%s: headerRead failed\n"), fn);
452         goto exit;
453     }
454
455     if (headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) {
456         struct rpmtd_s utd;
457     
458         if (!headerGet(h, RPMTAG_HEADERIMMUTABLE, &utd, HEADERGET_DEFAULT)){
459             rpmlog(RPMLOG_ERR, 
460                     _("%s: Immutable header region could not be read. "
461                     "Corrupted package?\n"), fn);
462             goto exit;
463         }
464         rpmDigestBundleUpdate(hdrbundle, rpm_header_magic, sizeof(rpm_header_magic));
465         rpmDigestBundleUpdate(hdrbundle, utd.data, utd.count);
466         rpmtdFreeData(&utd);
467     }
468
469     /* Read the payload from the package. */
470     while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0) {}
471     if (count < 0) {
472         rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), fn, Fstrerror(fd));
473         goto exit;
474     }
475
476     rc = 0;
477
478 exit:
479     headerFree(h);
480     return rc;
481 }
482
483 /* Parse the parameters from the OpenPGP packets that will be needed. */
484 /* XXX TODO: unify with similar parsePGP() in package.c */
485 static rpmRC parsePGP(rpmtd sigtd, const char *fn, pgpDig dig)
486 {
487     rpmRC rc = RPMRC_FAIL;
488     int debug = (_print_pkts & rpmIsDebug());
489     if ((pgpPrtPkts(sigtd->data, sigtd->count, dig, debug) == 0) &&
490          (dig->signature.version == 3 || dig->signature.version == 4)) {
491         rc = RPMRC_OK;
492     } else {
493         rpmlog(RPMLOG_ERR,
494             _("skipping package %s with unverifiable V%u signature\n"), fn,
495             dig->signature.version);
496     }
497     return rc;
498 }
499
500 /* 
501  * Figure best available signature. 
502  * XXX TODO: Similar detection in rpmReadPackageFile(), unify these.
503  */
504 static rpmSigTag bestSig(Header sigh, int nosignatures, int nodigests)
505 {
506     rpmSigTag sigtag = 0;
507     if (sigtag == 0 && !nosignatures) {
508         if (headerIsEntry(sigh, RPMSIGTAG_DSA))
509             sigtag = RPMSIGTAG_DSA;
510         else if (headerIsEntry(sigh, RPMSIGTAG_RSA))
511             sigtag = RPMSIGTAG_RSA;
512         else if (headerIsEntry(sigh, RPMSIGTAG_GPG))
513             sigtag = RPMSIGTAG_GPG;
514         else if (headerIsEntry(sigh, RPMSIGTAG_PGP))
515             sigtag = RPMSIGTAG_PGP;
516     }
517     if (sigtag == 0 && !nodigests) {
518         if (headerIsEntry(sigh, RPMSIGTAG_MD5))
519             sigtag = RPMSIGTAG_MD5;
520         else if (headerIsEntry(sigh, RPMSIGTAG_SHA1))
521             sigtag = RPMSIGTAG_SHA1;    /* XXX never happens */
522     }
523     return sigtag;
524 }
525
526 static const char *sigtagname(rpmSigTag sigtag, int upper)
527 {
528     const char *n = NULL;
529
530     switch (sigtag) {
531     case RPMSIGTAG_SIZE:
532         n = (upper ? "SIZE" : "size");
533         break;
534     case RPMSIGTAG_SHA1:
535         n = (upper ? "SHA1" : "sha1");
536         break;
537     case RPMSIGTAG_MD5:
538         n = (upper ? "MD5" : "md5");
539         break;
540     case RPMSIGTAG_RSA:
541         n = (upper ? "RSA" : "rsa");
542         break;
543     case RPMSIGTAG_PGP5:        /* XXX legacy */
544     case RPMSIGTAG_PGP:
545         n = (upper ? "(MD5) PGP" : "(md5) pgp");
546         break;
547     case RPMSIGTAG_DSA:
548         n = (upper ? "(SHA1) DSA" : "(sha1) dsa");
549         break;
550     case RPMSIGTAG_GPG:
551         n = (upper ? "GPG" : "gpg");
552         break;
553     default:
554         n = (upper ? "?UnknownSigatureType?" : "???");
555         break;
556     }
557     return n;
558 }
559
560 /* 
561  * Format sigcheck result for output, appending the message spew to buf and
562  * bad/missing keyids to keyprob.
563  *
564  * In verbose mode, just dump it all. Otherwise ok signatures
565  * are dumped lowercase, bad sigs uppercase and for PGP/GPG
566  * if misssing/untrusted key it's uppercase in parenthesis
567  * and stash the key id as <SIGTYPE>#<keyid>. Pfft.
568  */
569 static void formatResult(rpmSigTag sigtag, rpmRC sigres, const char *result,
570                          int havekey, char **keyprob, char **buf)
571 {
572     char *msg = NULL;
573     if (rpmIsVerbose()) {
574         rasprintf(&msg, "    %s", result);
575     } else { 
576         /* Check for missing / untrusted keys in result. */
577         const char *signame = sigtagname(sigtag, (sigres != RPMRC_OK));
578         
579         if (havekey && (sigres == RPMRC_NOKEY || sigres == RPMRC_NOTTRUSTED)) {
580             const char *tempKey = strstr(result, "ey ID");
581             if (tempKey) {
582                 char keyid[sizeof(pgpKeyID_t) + 1];
583                 rstrlcpy(keyid, tempKey + 6, sizeof(keyid));
584                 rstrscat(keyprob, " ", signame, "#", keyid, NULL);
585             }
586         }
587         rasprintf(&msg, (*keyprob ? "(%s) " : "%s "), signame);
588     }
589     rstrcat(buf, msg);
590     free(msg);
591 }
592
593 static int rpmpkgVerifySigs(rpmKeyring keyring, rpmQueryFlags flags,
594                            FD_t fd, const char *fn)
595 {
596
597     char *buf = NULL;
598     char *missingKeys = NULL; 
599     char *untrustedKeys = NULL;
600     struct rpmtd_s sigtd;
601     rpmTag sigtag;
602     pgpDig dig = NULL;
603     pgpDigParams sigp;
604     Header sigh = NULL;
605     HeaderIterator hi = NULL;
606     char * msg = NULL;
607     int res = 1; /* assume failure */
608     int xx;
609     rpmRC rc;
610     int failed = 0;
611     int nodigests = !(flags & VERIFY_DIGEST);
612     int nosignatures = !(flags & VERIFY_SIGNATURE);
613     rpmDigestBundle plbundle = rpmDigestBundleNew();
614     rpmDigestBundle hdrbundle = rpmDigestBundleNew();
615
616     rpmlead lead = rpmLeadNew();
617     if ((rc = rpmLeadRead(fd, lead)) == RPMRC_OK) {
618         const char *lmsg = NULL;
619         rc = rpmLeadCheck(lead, &lmsg);
620         if (rc != RPMRC_OK) 
621             rpmlog(RPMLOG_ERR, "%s: %s\n", fn, lmsg);
622     }
623     lead = rpmLeadFree(lead);
624
625     if (rc != RPMRC_OK) {
626         goto exit;
627     }
628
629     rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg);
630     switch (rc) {
631     default:
632         rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), fn,
633                     (msg && *msg ? msg : "\n"));
634         msg = _free(msg);
635         goto exit;
636         break;
637     case RPMRC_OK:
638         if (sigh == NULL) {
639             rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), fn);
640             goto exit;
641         }
642         break;
643     }
644     msg = _free(msg);
645
646     /* Grab a hint of what needs doing to avoid duplication. */
647     sigtag = bestSig(sigh, nosignatures, nodigests);
648
649     dig = pgpNewDig();
650     sigp = &dig->signature;
651
652     /* XXX RSA needs the hash_algo, so decode early. */
653     if (sigtag == RPMSIGTAG_RSA || sigtag == RPMSIGTAG_PGP ||
654                 sigtag == RPMSIGTAG_DSA || sigtag == RPMSIGTAG_GPG) {
655         xx = headerGet(sigh, sigtag, &sigtd, HEADERGET_DEFAULT);
656         xx = pgpPrtPkts(sigtd.data, sigtd.count, dig, 0);
657         rpmtdFreeData(&sigtd);
658         /* XXX assume same hash_algo in header-only and header+payload */
659         rpmDigestBundleAdd(plbundle, sigp->hash_algo, RPMDIGEST_NONE);
660         rpmDigestBundleAdd(hdrbundle, sigp->hash_algo, RPMDIGEST_NONE);
661     }
662
663     if (headerIsEntry(sigh, RPMSIGTAG_PGP) ||
664                       headerIsEntry(sigh, RPMSIGTAG_PGP5) ||
665                       headerIsEntry(sigh, RPMSIGTAG_MD5)) {
666         rpmDigestBundleAdd(plbundle, PGPHASHALGO_MD5, RPMDIGEST_NONE);
667     }
668     if (headerIsEntry(sigh, RPMSIGTAG_GPG)) {
669         rpmDigestBundleAdd(plbundle, PGPHASHALGO_SHA1, RPMDIGEST_NONE);
670     }
671
672     /* always do sha1 hash of header */
673     rpmDigestBundleAdd(hdrbundle, PGPHASHALGO_SHA1, RPMDIGEST_NONE);
674
675     /* Read the file, generating digest(s) on the fly. */
676     fdSetBundle(fd, plbundle);
677     if (readFile(fd, fn, dig, plbundle, hdrbundle)) {
678         goto exit;
679     }
680
681     rasprintf(&buf, "%s:%c", fn, (rpmIsVerbose() ? '\n' : ' ') );
682
683     hi = headerInitIterator(sigh);
684     for (; headerNext(hi, &sigtd) != 0; rpmtdFreeData(&sigtd)) {
685         char *result = NULL;
686         int havekey = 0;
687         DIGEST_CTX ctx = NULL;
688         if (sigtd.data == NULL) /* XXX can't happen */
689             continue;
690
691         /* Clean up parameters from previous sigtag. */
692         pgpCleanDig(dig);
693
694         switch (sigtd.tag) {
695         case RPMSIGTAG_GPG:
696         case RPMSIGTAG_PGP5:    /* XXX legacy */
697         case RPMSIGTAG_PGP:
698             havekey = 1;
699         case RPMSIGTAG_RSA:
700         case RPMSIGTAG_DSA:
701             if (nosignatures)
702                  continue;
703             if (parsePGP(&sigtd, fn, dig) != RPMRC_OK) {
704                 goto exit;
705             }
706             ctx = rpmDigestBundleDupCtx(havekey ? plbundle : hdrbundle,
707                                         dig->signature.hash_algo);
708             break;
709         case RPMSIGTAG_SHA1:
710             if (nodigests)
711                  continue;
712             ctx = rpmDigestBundleDupCtx(hdrbundle, PGPHASHALGO_SHA1);
713             break;
714         case RPMSIGTAG_MD5:
715             if (nodigests)
716                  continue;
717             ctx = rpmDigestBundleDupCtx(plbundle, PGPHASHALGO_MD5);
718             break;
719         default:
720             continue;
721             break;
722         }
723
724         rc = rpmVerifySignature(keyring, &sigtd, dig, ctx, &result);
725         rpmDigestFinal(ctx, NULL, NULL, 0);
726
727         formatResult(sigtd.tag, rc, result, havekey, 
728                      (rc == RPMRC_NOKEY ? &missingKeys : &untrustedKeys),
729                      &buf);
730         free(result);
731
732         if (rc != RPMRC_OK) {
733             failed = 1;
734         }
735
736     }
737     res = failed;
738
739     if (rpmIsVerbose()) {
740         rpmlog(RPMLOG_NOTICE, "%s", buf);
741     } else {
742         const char *ok = (failed ? _("NOT OK") : _("OK"));
743         rpmlog(RPMLOG_NOTICE, "%s%s%s%s%s%s%s%s\n", buf, ok,
744                missingKeys ? _(" (MISSING KEYS:") : "",
745                missingKeys ? missingKeys : "",
746                missingKeys ? _(") ") : "",
747                untrustedKeys ? _(" (UNTRUSTED KEYS:") : "",
748                untrustedKeys ? untrustedKeys : "",
749                untrustedKeys ? _(")") : "");
750     }
751     free(missingKeys);
752     free(untrustedKeys);
753
754 exit:
755     free(buf);
756     rpmDigestBundleFree(hdrbundle);
757     rpmDigestBundleFree(plbundle);
758     fdSetBundle(fd, NULL); /* XXX avoid double-free from fd close */
759     sigh = rpmFreeSignature(sigh);
760     hi = headerFreeIterator(hi);
761     pgpFreeDig(dig);
762     return res;
763 }
764
765 /* Wrapper around rpmkVerifySigs to preserve API */
766 int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd, const char * fn)
767 {
768     int rc = 1; /* assume failure */
769     if (ts && qva && fd && fn) {
770         rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
771         rc = rpmpkgVerifySigs(keyring, qva->qva_flags, fd, fn);
772         rpmKeyringFree(keyring);
773     }
774     return rc;
775 }
776
777 int rpmcliSign(rpmts ts, QVA_t qva, ARGV_const_t argv)
778 {
779     const char * arg;
780     int res = 0;
781     int xx;
782     rpmKeyring keyring = NULL;
783
784     if (argv == NULL) return res;
785
786     switch (qva->qva_mode) {
787     case RPMSIGN_CHK_SIGNATURE:
788         break;
789     case RPMSIGN_IMPORT_PUBKEY:
790         return rpmcliImportPubkeys(ts, argv);
791         break;
792     case RPMSIGN_NEW_SIGNATURE:
793     case RPMSIGN_ADD_SIGNATURE:
794     case RPMSIGN_DEL_SIGNATURE:
795         return rpmReSign(ts, qva, argv);
796         break;
797     case RPMSIGN_NONE:
798     default:
799         return -1;
800         break;
801     }
802
803     keyring = rpmtsGetKeyring(ts, 1);
804     while ((arg = *argv++) != NULL) {
805         FD_t fd;
806
807         fd = Fopen(arg, "r.ufdio");
808         if (fd == NULL || Ferror(fd)) {
809             rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), 
810                      arg, Fstrerror(fd));
811             res++;
812         } else if (rpmpkgVerifySigs(keyring, qva->qva_flags, fd, arg)) {
813             res++;
814         }
815
816         if (fd != NULL) xx = Fclose(fd);
817         rpmdbCheckSignals();
818     }
819     rpmKeyringFree(keyring);
820
821     return res;
822 }