Add support for global LDFLAGS
[platform/upstream/rpm.git] / lib / rpmchecksig.c
index 8e7d9bb..0d3e95a 100644 (file)
 
 #include "system.h"
 
+#include <ctype.h>
+
+#include <rpm/rpmlib.h>                        /* RPMSIGTAG & related */
 #include <rpm/rpmpgp.h>
-#include "rpmio/rpmio_internal.h"      /* for fd*Digest() */
 #include <rpm/rpmcli.h>
-
+#include <rpm/rpmfileutil.h>   /* rpmMkTemp() */
 #include <rpm/rpmdb.h>
-
 #include <rpm/rpmts.h>
-
 #include <rpm/rpmlog.h>
+#include <rpm/rpmstring.h>
+#include <rpm/rpmkeyring.h>
+
+#include "rpmio/rpmio_internal.h"      /* fdSetBundle() */
 #include "lib/rpmlead.h"
 #include "lib/signature.h"
-#include <rpm/rpmfileutil.h>   /* rpmMkTempFile() */
+
 #include "debug.h"
 
 int _print_pkts = 0;
 
-/**
- */
-static int manageFile(FD_t *fdp,
-               const char **fnp,
-               int flags, int rc)
-{
-    const char *fn;
-    FD_t fd;
-
-    if (fdp == NULL)   /* programmer error */
-       return 1;
-
-    /* close and reset *fdp to NULL */
-    if (*fdp && (fnp == NULL || *fnp == NULL)) {
-       (void) Fclose(*fdp);
-       *fdp = NULL;
-       return 0;
-    }
-
-    /* open a file and set *fdp */
-    if (*fdp == NULL && fnp != NULL && *fnp != NULL) {
-       fd = Fopen(*fnp, ((flags & O_WRONLY) ? "w.ufdio" : "r.ufdio"));
-       if (fd == NULL || Ferror(fd)) {
-           rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), *fnp,
-               Fstrerror(fd));
-           return 1;
-       }
-       *fdp = fd;
-       return 0;
-    }
-
-    /* open a temp file */
-    if (*fdp == NULL && (fnp == NULL || *fnp == NULL)) {
-       fn = NULL;
-       if (rpmMkTempFile(NULL, (fnp ? &fn : NULL), &fd)) {
-           rpmlog(RPMLOG_ERR, _("rpmMkTempFile failed\n"));
-           return 1;
-       }
-       if (fnp != NULL)
-           *fnp = fn;
-       *fdp = fdLink(fd, RPMDBG_M("manageFile return"));
-       fd = fdFree(fd, RPMDBG_M("manageFile return"));
-       return 0;
-    }
-
-    /* no operation */
-    if (*fdp != NULL && fnp != NULL && *fnp != NULL)
-       return 0;
-
-    /* XXX never reached */
-    return 1;
-}
-
-/**
- * Copy header+payload, calculating digest(s) on the fly.
- */
-static int copyFile(FD_t *sfdp, const char **sfnp,
-               FD_t *tfdp, const char **tfnp)
+static int doImport(rpmts ts, const char *fn, char *buf, ssize_t blen)
 {
-    unsigned char buf[BUFSIZ];
-    ssize_t count;
-    int rc = 1;
-
-    if (manageFile(sfdp, sfnp, O_RDONLY, 0))
-       goto exit;
-    if (manageFile(tfdp, tfnp, O_WRONLY|O_CREAT|O_TRUNC, 0))
-       goto exit;
-
-    while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), *sfdp)) > 0)
-    {
-       if (Fwrite(buf, sizeof(buf[0]), count, *tfdp) != count) {
-           rpmlog(RPMLOG_ERR, _("%s: Fwrite failed: %s\n"), *tfnp,
-               Fstrerror(*tfdp));
-           goto exit;
-       }
-    }
-    if (count < 0) {
-       rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), *sfnp, Fstrerror(*sfdp));
-       goto exit;
-    }
-    if (Fflush(*tfdp) != 0) {
-       rpmlog(RPMLOG_ERR, _("%s: Fflush failed: %s\n"), *tfnp,
-           Fstrerror(*tfdp));
-    }
-
-    rc = 0;
-
-exit:
-    if (*sfdp) (void) manageFile(sfdp, NULL, 0, rc);
-    if (*tfdp) (void) manageFile(tfdp, NULL, 0, rc);
-    return rc;
-}
-
-/**
- * Retrieve signer fingerprint from an OpenPGP signature tag.
- * @param sig          signature header
- * @param sigtag       signature tag
- * @retval signid      signer fingerprint
- * @return             0 on success
- */
-static int getSignid(Header sig, int sigtag, pgpKeyID_t signid)
-{
-    void * pkt = NULL;
-    int32_t pkttyp = 0;
-    rpm_count_t pktlen = 0;
-    int rc = 1;
-
-    if (headerGetEntry(sig, sigtag, &pkttyp, &pkt, &pktlen) && pkt != NULL) {
-       pgpDig dig = pgpNewDig();
-
-       if (!pgpPrtPkts(pkt, pktlen, dig, 0)) {
-           memcpy(signid, dig->signature.signid, sizeof(dig->signature.signid));
-           rc = 0;
-       }
-     
-       dig = pgpFreeDig(dig);
-    }
-    pkt = headerFreeData(pkt, pkttyp);
-    return rc;
-}
-
-/** \ingroup rpmcli
- * Create/modify elements in signature header.
- * @param ts           transaction set
- * @param qva          mode flags and parameters
- * @param argv         array of package file names (NULL terminated)
- * @return             0 on success
- */
-static int rpmReSign(rpmts ts,
-               QVA_t qva, const char ** argv)
-{
-    FD_t fd = NULL;
-    FD_t ofd = NULL;
-    rpmlead lead;
-    int32_t sigtag;
-    const char *rpm, *trpm;
-    const char *sigtarget = NULL;
-    char tmprpm[1024+1];
-    Header sigh = NULL;
-    const char * msg;
-    void * uh = NULL;
-    int32_t uht;
-    rpm_count_t uhc;
-    int res = EXIT_FAILURE;
-    int deleting = (qva->qva_mode == RPMSIGN_DEL_SIGNATURE);
-    rpmRC rc;
-    int xx;
-    
-    tmprpm[0] = '\0';
-    if (argv)
-    while ((rpm = *argv++) != NULL)
-    {
-
-       fprintf(stdout, "%s:\n", rpm);
-
-       if (manageFile(&fd, &rpm, O_RDONLY, 0))
-           goto exit;
-
-       lead = rpmLeadNew();
-
-       if ((rc = rpmLeadRead(fd, lead)) == RPMRC_OK) {
-           rc = rpmLeadCheck(lead, rpm);
-       }
-
-       if (rc != RPMRC_OK) {
-           lead = rpmLeadFree(lead);
-           goto exit;
-       }
-
-       msg = NULL;
-       rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg);
-       switch (rc) {
-       default:
-           rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), rpm,
-                       (msg && *msg ? msg : "\n"));
-           msg = _free(msg);
-           goto exit;
-           break;
-       case RPMRC_OK:
-           if (sigh == NULL) {
-               rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), rpm);
-               goto exit;
-           }
-           break;
-       }
-       msg = _free(msg);
-
-       /* Write the header and archive to a temp file */
-       /* ASSERT: ofd == NULL && sigtarget == NULL */
-       if (copyFile(&fd, &rpm, &ofd, &sigtarget))
-           goto exit;
-       /* Both fd and ofd are now closed. sigtarget contains tempfile name. */
-       /* ASSERT: fd == NULL && ofd == NULL */
-
-       /* Dump the immutable region (if present). */
-       if (headerGetEntry(sigh, RPMTAG_HEADERSIGNATURES, &uht, &uh, &uhc)) {
-           HeaderIterator hi;
-           int32_t tag, type;
-           rpm_count_t count;
-           hPTR_t ptr;
-           Header oh;
-           Header nh;
-
-           nh = headerNew();
-           if (nh == NULL) {
-               uh = headerFreeData(uh, uht);
-               goto exit;
-           }
-
-           oh = headerCopyLoad(uh);
-           for (hi = headerInitIterator(oh);
-               headerNextIterator(hi, &tag, &type, &ptr, &count);
-               ptr = headerFreeData(ptr, type))
-           {
-               if (ptr)
-                   xx = headerAddEntry(nh, tag, type, ptr, count);
-           }
-           hi = headerFreeIterator(hi);
-           oh = headerFree(oh);
-
-           sigh = headerFree(sigh);
-           sigh = headerLink(nh);
-           nh = headerFree(nh);
-       }
-
-       /* Eliminate broken digest values. */
-       xx = headerRemoveEntry(sigh, RPMSIGTAG_LEMD5_1);
-       xx = headerRemoveEntry(sigh, RPMSIGTAG_LEMD5_2);
-       xx = headerRemoveEntry(sigh, RPMSIGTAG_BADSHA1_1);
-       xx = headerRemoveEntry(sigh, RPMSIGTAG_BADSHA1_2);
-
-       /* Toss and recalculate header+payload size and digests. */
-       xx = headerRemoveEntry(sigh, RPMSIGTAG_SIZE);
-       xx = rpmAddSignature(sigh, sigtarget, RPMSIGTAG_SIZE, qva->passPhrase);
-       xx = headerRemoveEntry(sigh, RPMSIGTAG_MD5);
-       xx = rpmAddSignature(sigh, sigtarget, RPMSIGTAG_MD5, qva->passPhrase);
-       xx = headerRemoveEntry(sigh, RPMSIGTAG_SHA1);
-       xx = rpmAddSignature(sigh, sigtarget, RPMSIGTAG_SHA1, qva->passPhrase);
-
-       if (deleting) { /* Nuke all the signature tags. */
-           xx = headerRemoveEntry(sigh, RPMSIGTAG_GPG);
-           xx = headerRemoveEntry(sigh, RPMSIGTAG_DSA);
-           xx = headerRemoveEntry(sigh, RPMSIGTAG_PGP5);
-           xx = headerRemoveEntry(sigh, RPMSIGTAG_PGP);
-           xx = headerRemoveEntry(sigh, RPMSIGTAG_RSA);
-       } else          /* If gpg/pgp is configured, replace the signature. */
-       if ((sigtag = rpmLookupSignatureType(RPMLOOKUPSIG_QUERY)) > 0) {
-           pgpKeyID_t oldsignid, newsignid;
-
-           /* Grab the old signature fingerprint (if any) */
-           memset(oldsignid, 0, sizeof(oldsignid));
-           xx = getSignid(sigh, sigtag, oldsignid);
-
-           switch (sigtag) {
-           case RPMSIGTAG_DSA:
-               xx = headerRemoveEntry(sigh, RPMSIGTAG_GPG);
-               break;
-           case RPMSIGTAG_RSA:
-               xx = headerRemoveEntry(sigh, RPMSIGTAG_PGP);
-               break;
-           case RPMSIGTAG_GPG:
-               xx = headerRemoveEntry(sigh, RPMSIGTAG_DSA);
-           case RPMSIGTAG_PGP5:
-           case RPMSIGTAG_PGP:
-               xx = headerRemoveEntry(sigh, RPMSIGTAG_RSA);
-               break;
-           }
-
-           xx = headerRemoveEntry(sigh, sigtag);
-           xx = rpmAddSignature(sigh, sigtarget, sigtag, qva->passPhrase);
-
-           /* If package was previously signed, check for same signer. */
-           memset(newsignid, 0, sizeof(newsignid));
-           if (memcmp(oldsignid, newsignid, sizeof(oldsignid))) {
-
-               /* Grab the new signature fingerprint */
-               xx = getSignid(sigh, sigtag, newsignid);
-
-               /* If same signer, skip resigning the package. */
-               if (!memcmp(oldsignid, newsignid, sizeof(oldsignid))) {
-
-                   rpmlog(RPMLOG_WARNING,
-                       _("%s: was already signed by key ID %s, skipping\n"),
-                       rpm, pgpHexStr(newsignid+4, sizeof(newsignid)-4));
+    char const * const pgpmark = "-----BEGIN PGP ";
+    size_t marklen = strlen(pgpmark);
+    int res = 0;
+    int keyno = 1;
+    char *start = strstr(buf, pgpmark);
 
-                   /* Clean up intermediate target */
-                   xx = unlink(sigtarget);
-                   sigtarget = _free(sigtarget);
-                   continue;
-               }
+    do {
+       uint8_t *pkt = NULL;
+       size_t pktlen = 0;
+       
+       /* Read pgp packet. */
+       if (pgpParsePkts(start, &pkt, &pktlen) == PGPARMOR_PUBKEY) {
+           /* Import pubkey packet(s). */
+           if (rpmtsImportPubkey(ts, pkt, pktlen) != RPMRC_OK) {
+               rpmlog(RPMLOG_ERR, _("%s: key %d import failed.\n"), fn, keyno);
+               res++;
            }
+       } else {
+           rpmlog(RPMLOG_ERR, _("%s: key %d not an armored public key.\n"),
+                  fn, keyno);
+           res++;
        }
-
-       /* Reallocate the signature into one contiguous region. */
-       sigh = headerReload(sigh, RPMTAG_HEADERSIGNATURES);
-       if (sigh == NULL)       /* XXX can't happen */
-           goto exit;
-
-       /* Write the lead/signature of the output rpm */
-       strcpy(tmprpm, rpm);
-       strcat(tmprpm, ".XXXXXX");
-#if defined(HAVE_MKSTEMP)
-       (void) close(mkstemp(tmprpm));
-#else
-       (void) mktemp(tmprpm);
-#endif
-       trpm = tmprpm;
-
-       if (manageFile(&ofd, &trpm, O_WRONLY|O_CREAT|O_TRUNC, 0))
-           goto exit;
-
-       rc = rpmLeadWrite(ofd, lead);
-       lead = rpmLeadFree(lead);
-       if (rc != RPMRC_OK) {
-           rpmlog(RPMLOG_ERR, _("%s: writeLead failed: %s\n"), trpm,
-               Fstrerror(ofd));
-           goto exit;
-       }
-
-       if (rpmWriteSignature(ofd, sigh)) {
-           rpmlog(RPMLOG_ERR, _("%s: rpmWriteSignature failed: %s\n"), trpm,
-               Fstrerror(ofd));
-           goto exit;
+       
+       /* See if there are more keys in the buffer */
+       if (start && start + marklen < buf + blen) {
+           start = strstr(start + marklen, pgpmark);
+       } else {
+           start = NULL;
        }
 
-       /* Append the header and archive from the temp file */
-       /* ASSERT: fd == NULL && ofd != NULL */
-       if (copyFile(&fd, &sigtarget, &ofd, &trpm))
-           goto exit;
-       /* Both fd and ofd are now closed. */
-       /* ASSERT: fd == NULL && ofd == NULL */
-
-       /* Move final target into place. */
-       xx = unlink(rpm);
-       xx = rename(trpm, rpm);
-       tmprpm[0] = '\0';
-
-       /* Clean up intermediate target */
-       xx = unlink(sigtarget);
-       sigtarget = _free(sigtarget);
-    }
-
-    res = 0;
-
-exit:
-    if (fd)    (void) manageFile(&fd, NULL, 0, res);
-    if (ofd)   (void) manageFile(&ofd, NULL, 0, res);
-
-    sigh = rpmFreeSignature(sigh);
-
-    if (sigtarget) {
-       xx = unlink(sigtarget);
-       sigtarget = _free(sigtarget);
-    }
-    if (tmprpm[0] != '\0') {
-       xx = unlink(tmprpm);
-       tmprpm[0] = '\0';
-    }
+       keyno++;
+       free(pkt);
+    } while (start != NULL);
 
     return res;
 }
 
-/** \ingroup rpmcli
- * Import public key(s).
- * @todo Implicit --update policy for gpg-pubkey headers.
- * @param ts           transaction set
- * @param qva          mode flags and parameters
- * @param argv         array of pubkey file names (NULL terminated)
- * @return             0 on success
- */
-static int rpmcliImportPubkeys(const rpmts ts,
-               QVA_t qva,
-               const char ** argv)
+int rpmcliImportPubkeys(rpmts ts, ARGV_const_t argv)
 {
-    const char * fn;
-    const unsigned char * pkt = NULL;
-    size_t pktlen = 0;
-    char * t = NULL;
     int res = 0;
-    rpmRC rpmrc;
-    int rc;
-
-    if (argv == NULL) return res;
-
-    while ((fn = *argv++) != NULL) {
-
-       rpmtsClean(ts);
-       pkt = _free(pkt);
-       t = _free(t);
+    for (ARGV_const_t arg = argv; arg && *arg; arg++) {
+       const char *fn = *arg;
+       uint8_t *buf = NULL;
+       ssize_t blen = 0;
+       char *t = NULL;
+       int iorc;
 
        /* If arg looks like a keyid, then attempt keyserver retrieve. */
-       if (fn[0] == '0' && fn[1] == 'x') {
-           const char * s;
+       if (rstreqn(fn, "0x", 2)) {
+           const char * s = fn + 2;
            int i;
-           for (i = 0, s = fn+2; *s && isxdigit(*s); s++, i++)
+           for (i = 0; *s && isxdigit(*s); s++, i++)
                {};
            if (i == 8 || i == 16) {
                t = rpmExpand("%{_hkp_keyserver_query}", fn+2, NULL);
@@ -424,466 +87,359 @@ static int rpmcliImportPubkeys(const rpmts ts,
            }
        }
 
-       /* Read pgp packet. */
-       if ((rc = pgpReadPkts(fn, &pkt, &pktlen)) <= 0) {
-           rpmlog(RPMLOG_ERR, _("%s: import read failed(%d).\n"), fn, rc);
-           res++;
-           continue;
-       }
-       if (rc != PGPARMOR_PUBKEY) {
-           rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn);
-           res++;
-           continue;
-       }
-
-       /* Import pubkey packet(s). */
-       if ((rpmrc = rpmtsImportPubkey(ts, pkt, pktlen)) != RPMRC_OK) {
-           rpmlog(RPMLOG_ERR, _("%s: import failed.\n"), fn);
+       /* Read the file and try to import all contained keys */
+       iorc = rpmioSlurp(fn, &buf, &blen);
+       if (iorc || buf == NULL || blen < 64) {
+           rpmlog(RPMLOG_ERR, _("%s: import read failed(%d).\n"), fn, iorc);
            res++;
-           continue;
+       } else {
+           res += doImport(ts, fn, (char *)buf, blen);
        }
-
+       
+       free(t);
+       free(buf);
     }
-    
-rpmtsClean(ts);
-    pkt = _free(pkt);
-    t = _free(t);
     return res;
 }
 
-static unsigned char header_magic[8] = {
-        0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
-};
-
 /**
  * @todo If the GPG key was known available, the md5 digest could be skipped.
  */
-static int readFile(FD_t fd, const char * fn, pgpDig dig)
+static int readFile(FD_t fd, const char * fn,
+                   rpmDigestBundle plbundle, rpmDigestBundle hdrbundle)
 {
     unsigned char buf[4*BUFSIZ];
     ssize_t count;
     int rc = 1;
-
-    dig->nbytes = 0;
+    Header h = NULL;
+    char *msg = NULL;
 
     /* Read the header from the package. */
-    {  Header h = headerRead(fd, HEADER_MAGIC_YES);
-       if (h == NULL) {
-           rpmlog(RPMLOG_ERR, _("%s: headerRead failed\n"), fn);
-           goto exit;
-       }
-
-       dig->nbytes += headerSizeof(h, HEADER_MAGIC_YES);
+    if (rpmReadHeader(NULL, fd, &h, &msg) != RPMRC_OK) {
+       rpmlog(RPMLOG_ERR, _("%s: headerRead failed: %s\n"), fn, msg);
+       goto exit;
+    }
 
-       if (headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) {
-           void * uh;
-           int32_t uht;
-           rpm_count_t uhc;
-       
-           if (!headerGetEntry(h, RPMTAG_HEADERIMMUTABLE, &uht, &uh, &uhc)
-           ||   uh == NULL)
-           {
-               h = headerFree(h);
-               rpmlog(RPMLOG_ERR, _("%s: headerGetEntry failed\n"), fn);
-               goto exit;
-           }
-           dig->hdrsha1ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE);
-           (void) rpmDigestUpdate(dig->hdrsha1ctx, header_magic, sizeof(header_magic));
-           (void) rpmDigestUpdate(dig->hdrsha1ctx, uh, uhc);
-           dig->hdrmd5ctx = rpmDigestInit(dig->signature.hash_algo, RPMDIGEST_NONE);
-           (void) rpmDigestUpdate(dig->hdrmd5ctx, header_magic, sizeof(header_magic));
-           (void) rpmDigestUpdate(dig->hdrmd5ctx, uh, uhc);
-           uh = headerFreeData(uh, uht);
+    if (headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) {
+       struct rpmtd_s utd;
+    
+       if (!headerGet(h, RPMTAG_HEADERIMMUTABLE, &utd, HEADERGET_DEFAULT)){
+           rpmlog(RPMLOG_ERR, 
+                   _("%s: Immutable header region could not be read. "
+                   "Corrupted package?\n"), fn);
+           goto exit;
        }
-       h = headerFree(h);
+       rpmDigestBundleUpdate(hdrbundle, rpm_header_magic, sizeof(rpm_header_magic));
+       rpmDigestBundleUpdate(hdrbundle, utd.data, utd.count);
+       rpmtdFreeData(&utd);
     }
 
     /* Read the payload from the package. */
-    while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0)
-       dig->nbytes += count;
+    while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0) {}
     if (count < 0) {
        rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), fn, Fstrerror(fd));
        goto exit;
     }
-    fdStealDigest(fd, dig);
 
     rc = 0;
 
 exit:
+    free(msg);
+    headerFree(h);
     return rc;
 }
 
-int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd,
-               const char * fn)
+/* 
+ * Figure best available signature. 
+ * XXX TODO: Similar detection in rpmReadPackageFile(), unify these.
+ */
+static rpmTagVal bestSig(Header sigh, int nosignatures, int nodigests)
 {
-    int res2, res3;
-    char result[1024];
-    char buf[8192], * b;
-    char missingKeys[7164], * m;
-    char untrustedKeys[7164], * u;
-    int32_t sigtag;
-    int32_t sigtype;
-    const void * sig;
-    pgpDig dig;
-    pgpDigParams sigp;
-    rpm_count_t siglen;
-    Header sigh = NULL;
-    HeaderIterator hi;
-    const char * msg;
-    int res = 0;
-    int xx;
-    rpmRC rc;
-    int nodigests = !(qva->qva_flags & VERIFY_DIGEST);
-    int nosignatures = !(qva->qva_flags & VERIFY_SIGNATURE);
-
-    {
-       rpmlead lead = rpmLeadNew();
-       if ((rc = rpmLeadRead(fd, lead)) == RPMRC_OK) {
-           rc = rpmLeadCheck(lead, fn);
-       }
-       lead = rpmLeadFree(lead);
+    rpmTagVal sigtag = 0;
+    if (sigtag == 0 && !nosignatures) {
+       if (headerIsEntry(sigh, RPMSIGTAG_DSA))
+           sigtag = RPMSIGTAG_DSA;
+       else if (headerIsEntry(sigh, RPMSIGTAG_RSA))
+           sigtag = RPMSIGTAG_RSA;
+       else if (headerIsEntry(sigh, RPMSIGTAG_GPG))
+           sigtag = RPMSIGTAG_GPG;
+       else if (headerIsEntry(sigh, RPMSIGTAG_PGP))
+           sigtag = RPMSIGTAG_PGP;
+    }
+    if (sigtag == 0 && !nodigests) {
+       if (headerIsEntry(sigh, RPMSIGTAG_MD5))
+           sigtag = RPMSIGTAG_MD5;
+       else if (headerIsEntry(sigh, RPMSIGTAG_SHA1))
+           sigtag = RPMSIGTAG_SHA1;    /* XXX never happens */
+    }
+    return sigtag;
+}
 
-       if (rc != RPMRC_OK) {
-           res++;
-           goto exit;
-       }
+static const char *sigtagname(rpmTagVal sigtag, int upper)
+{
+    const char *n = NULL;
 
+    switch (sigtag) {
+    case RPMSIGTAG_SIZE:
+       n = (upper ? "SIZE" : "size");
+       break;
+    case RPMSIGTAG_SHA1:
+       n = (upper ? "SHA1" : "sha1");
+       break;
+    case RPMSIGTAG_MD5:
+       n = (upper ? "MD5" : "md5");
+       break;
+    case RPMSIGTAG_RSA:
+       n = (upper ? "RSA" : "rsa");
+       break;
+    case RPMSIGTAG_PGP5:       /* XXX legacy */
+    case RPMSIGTAG_PGP:
+       n = (upper ? "(MD5) PGP" : "(md5) pgp");
+       break;
+    case RPMSIGTAG_DSA:
+       n = (upper ? "(SHA1) DSA" : "(sha1) dsa");
+       break;
+    case RPMSIGTAG_GPG:
+       n = (upper ? "GPG" : "gpg");
+       break;
+    default:
+       n = (upper ? "?UnknownSigatureType?" : "???");
+       break;
+    }
+    return n;
+}
 
-       msg = NULL;
-       rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg);
-       switch (rc) {
-       default:
-           rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), fn,
-                       (msg && *msg ? msg : "\n"));
-           msg = _free(msg);
-           res++;
-           goto exit;
-           break;
-       case RPMRC_OK:
-           if (sigh == NULL) {
-               rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), fn);
-               res++;
-               goto exit;
+/* 
+ * Format sigcheck result for output, appending the message spew to buf and
+ * bad/missing keyids to keyprob.
+ *
+ * In verbose mode, just dump it all. Otherwise ok signatures
+ * are dumped lowercase, bad sigs uppercase and for PGP/GPG
+ * if misssing/untrusted key it's uppercase in parenthesis
+ * and stash the key id as <SIGTYPE>#<keyid>. Pfft.
+ */
+static void formatResult(rpmTagVal sigtag, rpmRC sigres, const char *result,
+                        int havekey, char **keyprob, char **buf)
+{
+    char *msg = NULL;
+    if (rpmIsVerbose()) {
+       rasprintf(&msg, "    %s", result);
+    } else { 
+       /* Check for missing / untrusted keys in result. */
+       const char *signame = sigtagname(sigtag, (sigres != RPMRC_OK));
+       
+       if (havekey && (sigres == RPMRC_NOKEY || sigres == RPMRC_NOTTRUSTED)) {
+           const char *tempKey = strstr(result, "ey ID");
+           if (tempKey) {
+               char keyid[sizeof(pgpKeyID_t) + 1];
+               rstrlcpy(keyid, tempKey + 6, sizeof(keyid));
+               rstrscat(keyprob, " ", signame, "#", keyid, NULL);
            }
-           break;
        }
-       msg = _free(msg);
+       rasprintf(&msg, (*keyprob ? "(%s) " : "%s "), signame);
+    }
+    rstrcat(buf, msg);
+    free(msg);
+}
+
+static int rpmpkgVerifySigs(rpmKeyring keyring, rpmQueryFlags flags,
+                          FD_t fd, const char *fn)
+{
+
+    char *buf = NULL;
+    char *missingKeys = NULL; 
+    char *untrustedKeys = NULL;
+    struct rpmtd_s sigtd;
+    rpmTagVal sigtag;
+    pgpDigParams sig = NULL;
+    Header sigh = NULL;
+    HeaderIterator hi = NULL;
+    char * msg = NULL;
+    int res = 1; /* assume failure */
+    rpmRC rc;
+    int failed = 0;
+    int nodigests = !(flags & VERIFY_DIGEST);
+    int nosignatures = !(flags & VERIFY_SIGNATURE);
+    rpmDigestBundle plbundle = rpmDigestBundleNew();
+    rpmDigestBundle hdrbundle = rpmDigestBundleNew();
+
+    if ((rc = rpmLeadRead(fd, NULL, NULL, &msg)) != RPMRC_OK) {
+       rpmlog(RPMLOG_ERR, "%s: %s\n", fn, msg);
+       free(msg);
+       goto exit;
+    }
 
-       /* Grab a hint of what needs doing to avoid duplication. */
-       sigtag = 0;
-       if (sigtag == 0 && !nosignatures) {
-           if (headerIsEntry(sigh, RPMSIGTAG_DSA))
-               sigtag = RPMSIGTAG_DSA;
-           else if (headerIsEntry(sigh, RPMSIGTAG_RSA))
-               sigtag = RPMSIGTAG_RSA;
-           else if (headerIsEntry(sigh, RPMSIGTAG_GPG))
-               sigtag = RPMSIGTAG_GPG;
-           else if (headerIsEntry(sigh, RPMSIGTAG_PGP))
-               sigtag = RPMSIGTAG_PGP;
+    rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg);
+    switch (rc) {
+    default:
+       rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), fn,
+                   (msg && *msg ? msg : "\n"));
+       msg = _free(msg);
+       goto exit;
+       break;
+    case RPMRC_OK:
+       if (sigh == NULL) {
+           rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), fn);
+           goto exit;
        }
-       if (sigtag == 0 && !nodigests) {
-           if (headerIsEntry(sigh, RPMSIGTAG_MD5))
-               sigtag = RPMSIGTAG_MD5;
-           else if (headerIsEntry(sigh, RPMSIGTAG_SHA1))
-               sigtag = RPMSIGTAG_SHA1;        /* XXX never happens */
+       break;
+    }
+    msg = _free(msg);
+
+    /* Grab a hint of what needs doing to avoid duplication. */
+    sigtag = bestSig(sigh, nosignatures, nodigests);
+
+    /* XXX RSA needs the hash_algo, so decode early. */
+    if (sigtag == RPMSIGTAG_RSA || sigtag == RPMSIGTAG_PGP ||
+               sigtag == RPMSIGTAG_DSA || sigtag == RPMSIGTAG_GPG) {
+       unsigned int hashalgo;
+       if (headerGet(sigh, sigtag, &sigtd, HEADERGET_DEFAULT)) {
+           parsePGPSig(&sigtd, "package", fn, &sig);
+           rpmtdFreeData(&sigtd);
        }
+       if (sig == NULL) goto exit;
+           
+       /* XXX assume same hash_algo in header-only and header+payload */
+       hashalgo = pgpDigParamsAlgo(sig, PGPVAL_HASHALGO);
+       rpmDigestBundleAdd(plbundle, hashalgo, RPMDIGEST_NONE);
+       rpmDigestBundleAdd(hdrbundle, hashalgo, RPMDIGEST_NONE);
+    }
 
-       dig = rpmtsDig(ts);
-       sigp = rpmtsSignature(ts);
-
-       /* XXX RSA needs the hash_algo, so decode early. */
-       if (sigtag == RPMSIGTAG_RSA || sigtag == RPMSIGTAG_PGP) {
-           xx = headerGetEntry(sigh, sigtag, &sigtype, (void **)&sig, &siglen);
-           xx = pgpPrtPkts(sig, siglen, dig, 0);
-           sig = headerFreeData(sig, sigtype);
-           /* XXX assume same hash_algo in header-only and header+payload */
-           if ((headerIsEntry(sigh, RPMSIGTAG_PGP)
-             || headerIsEntry(sigh, RPMSIGTAG_PGP5))
-            && dig->signature.hash_algo != PGPHASHALGO_MD5)
-               fdInitDigest(fd, dig->signature.hash_algo, 0);
-       }
+    if (headerIsEntry(sigh, RPMSIGTAG_PGP) ||
+                     headerIsEntry(sigh, RPMSIGTAG_PGP5) ||
+                     headerIsEntry(sigh, RPMSIGTAG_MD5)) {
+       rpmDigestBundleAdd(plbundle, PGPHASHALGO_MD5, RPMDIGEST_NONE);
+    }
+    if (headerIsEntry(sigh, RPMSIGTAG_GPG)) {
+       rpmDigestBundleAdd(plbundle, PGPHASHALGO_SHA1, RPMDIGEST_NONE);
+    }
 
-       if (headerIsEntry(sigh, RPMSIGTAG_PGP)
-       ||  headerIsEntry(sigh, RPMSIGTAG_PGP5)
-       ||  headerIsEntry(sigh, RPMSIGTAG_MD5))
-           fdInitDigest(fd, PGPHASHALGO_MD5, 0);
-       if (headerIsEntry(sigh, RPMSIGTAG_GPG))
-           fdInitDigest(fd, PGPHASHALGO_SHA1, 0);
+    /* always do sha1 hash of header */
+    rpmDigestBundleAdd(hdrbundle, PGPHASHALGO_SHA1, RPMDIGEST_NONE);
 
-       /* Read the file, generating digest(s) on the fly. */
-       if (dig == NULL || sigp == NULL || readFile(fd, fn, dig)) {
-           res++;
-           goto exit;
-       }
+    /* Read the file, generating digest(s) on the fly. */
+    fdSetBundle(fd, plbundle);
+    if (readFile(fd, fn, plbundle, hdrbundle)) {
+       goto exit;
+    }
 
-       res2 = 0;
-       b = buf;                *b = '\0';
-       m = missingKeys;        *m = '\0';
-       u = untrustedKeys;      *u = '\0';
-       sprintf(b, "%s:%c", fn, (rpmIsVerbose() ? '\n' : ' ') );
-       b += strlen(b);
-
-       for (hi = headerInitIterator(sigh);
-           headerNextIterator(hi, &sigtag, &sigtype, &sig, &siglen) != 0;
-           (void) rpmtsSetSig(ts, sigtag, sigtype, NULL, siglen))
-       {
-
-           if (sig == NULL) /* XXX can't happen */
-               continue;
-
-           (void) rpmtsSetSig(ts, sigtag, sigtype, sig, siglen);
-
-           /* Clean up parameters from previous sigtag. */
-           pgpCleanDig(dig);
-
-           switch (sigtag) {
-           case RPMSIGTAG_RSA:
-           case RPMSIGTAG_DSA:
-           case RPMSIGTAG_GPG:
-           case RPMSIGTAG_PGP5:        /* XXX legacy */
-           case RPMSIGTAG_PGP:
-               if (nosignatures)
-                    continue;
-               xx = pgpPrtPkts(sig, siglen, dig,
-                       (_print_pkts & rpmIsDebug()));
-
-               if (sigp->version != 3 && sigp->version != 4) {
-                   rpmlog(RPMLOG_NOTICE,
-               _("skipping package %s with unverifiable V%u signature\n"),
-                       fn, sigp->version);
-                   res++;
-                   goto exit;
-               }
-               break;
-           case RPMSIGTAG_SHA1:
-               if (nodigests)
-                    continue;
-               /* XXX Don't bother with header sha1 if header dsa. */
-               if (!nosignatures && sigtag == RPMSIGTAG_DSA)
-                   continue;
-               break;
-           case RPMSIGTAG_LEMD5_2:
-           case RPMSIGTAG_LEMD5_1:
-           case RPMSIGTAG_MD5:
-               if (nodigests)
-                    continue;
-               /*
-                * Don't bother with md5 if pgp, as RSA/MD5 is more reliable
-                * than the -- now unsupported -- legacy md5 breakage.
-                */
-               if (!nosignatures && sigtag == RPMSIGTAG_PGP)
-                   continue;
-               break;
-           default:
-               continue;
-               break;
-           }
+    rasprintf(&buf, "%s:%c", fn, (rpmIsVerbose() ? '\n' : ' ') );
 
-           res3 = rpmVerifySignature(ts, result);
-
-           if (res3) {
-               if (rpmIsVerbose()) {
-                   b = stpcpy(b, "    ");
-                   b = stpcpy(b, result);
-                   res2 = 1;
-               } else {
-                   char *tempKey;
-                   switch (sigtag) {
-                   case RPMSIGTAG_SIZE:
-                       b = stpcpy(b, "SIZE ");
-                       res2 = 1;
-                       break;
-                   case RPMSIGTAG_SHA1:
-                       b = stpcpy(b, "SHA1 ");
-                       res2 = 1;
-                       break;
-                   case RPMSIGTAG_LEMD5_2:
-                   case RPMSIGTAG_LEMD5_1:
-                   case RPMSIGTAG_MD5:
-                       b = stpcpy(b, "MD5 ");
-                       res2 = 1;
-                       break;
-                   case RPMSIGTAG_RSA:
-                       b = stpcpy(b, "RSA ");
-                       res2 = 1;
-                       break;
-                   case RPMSIGTAG_PGP5:        /* XXX legacy */
-                   case RPMSIGTAG_PGP:
-                       switch (res3) {
-                       case RPMRC_NOKEY:
-                           res2 = 1;
-                       case RPMRC_NOTTRUSTED:
-                       {   int offset = 6;
-                           b = stpcpy(b, "(MD5) (PGP) ");
-                           tempKey = strstr(result, "ey ID");
-                           if (tempKey == NULL) {
-                               tempKey = strstr(result, "keyid:");
-                               offset = 9;
-                           }
-                           if (tempKey) {
-                             if (res3 == RPMRC_NOKEY) {
-                               m = stpcpy(m, " PGP#");
-                               m = stpncpy(m, tempKey + offset, 8);
-                               *m = '\0';
-                             } else {
-                               u = stpcpy(u, " PGP#");
-                               u = stpncpy(u, tempKey + offset, 8);
-                               *u = '\0';
-                             }
-                           }
-                       }   break;
-                       default:
-                           b = stpcpy(b, "MD5 PGP ");
-                           res2 = 1;
-                           break;
-                       }
-                       break;
-                   case RPMSIGTAG_DSA:
-                       b = stpcpy(b, "(SHA1) DSA ");
-                       res2 = 1;
-                       break;
-                   case RPMSIGTAG_GPG:
-                       /* Do not consider this a failure */
-                       switch (res3) {
-                       case RPMRC_NOKEY:
-                           b = stpcpy(b, "(GPG) ");
-                           m = stpcpy(m, " GPG#");
-                           tempKey = strstr(result, "ey ID");
-                           if (tempKey) {
-                               m = stpncpy(m, tempKey+6, 8);
-                               *m = '\0';
-                           }
-                           res2 = 1;
-                           break;
-                       default:
-                           b = stpcpy(b, "GPG ");
-                           res2 = 1;
-                           break;
-                       }
-                       break;
-                   default:
-                       b = stpcpy(b, "?UnknownSignatureType? ");
-                       res2 = 1;
-                       break;
-                   }
-               }
-           } else {
-               if (rpmIsVerbose()) {
-                   b = stpcpy(b, "    ");
-                   b = stpcpy(b, result);
-               } else {
-                   switch (sigtag) {
-                   case RPMSIGTAG_SIZE:
-                       b = stpcpy(b, "size ");
-                       break;
-                   case RPMSIGTAG_SHA1:
-                       b = stpcpy(b, "sha1 ");
-                       break;
-                   case RPMSIGTAG_LEMD5_2:
-                   case RPMSIGTAG_LEMD5_1:
-                   case RPMSIGTAG_MD5:
-                       b = stpcpy(b, "md5 ");
-                       break;
-                   case RPMSIGTAG_RSA:
-                       b = stpcpy(b, "rsa ");
-                       break;
-                   case RPMSIGTAG_PGP5:        /* XXX legacy */
-                   case RPMSIGTAG_PGP:
-                       b = stpcpy(b, "(md5) pgp ");
-                       break;
-                   case RPMSIGTAG_DSA:
-                       b = stpcpy(b, "(sha1) dsa ");
-                       break;
-                   case RPMSIGTAG_GPG:
-                       b = stpcpy(b, "gpg ");
-                       break;
-                   default:
-                       b = stpcpy(b, "??? ");
-                       break;
-                   }
-               }
-           }
+    hi = headerInitIterator(sigh);
+    for (; headerNext(hi, &sigtd) != 0; rpmtdFreeData(&sigtd)) {
+       char *result = NULL;
+       int havekey = 0;
+       DIGEST_CTX ctx = NULL;
+       if (sigtd.data == NULL) /* XXX can't happen */
+           continue;
+
+       /* Clean up parameters from previous sigtag. */
+       sig = pgpDigParamsFree(sig);
+
+       switch (sigtd.tag) {
+       case RPMSIGTAG_GPG:
+       case RPMSIGTAG_PGP5:    /* XXX legacy */
+       case RPMSIGTAG_PGP:
+           havekey = 1;
+       case RPMSIGTAG_RSA:
+       case RPMSIGTAG_DSA:
+           if (nosignatures)
+                continue;
+           if (parsePGPSig(&sigtd, "package", fn, &sig))
+               goto exit;
+           ctx = rpmDigestBundleDupCtx(havekey ? plbundle : hdrbundle,
+                                       pgpDigParamsAlgo(sig, PGPVAL_HASHALGO));
+           break;
+       case RPMSIGTAG_SHA1:
+           if (nodigests)
+                continue;
+           ctx = rpmDigestBundleDupCtx(hdrbundle, PGPHASHALGO_SHA1);
+           break;
+       case RPMSIGTAG_MD5:
+           if (nodigests)
+                continue;
+           ctx = rpmDigestBundleDupCtx(plbundle, PGPHASHALGO_MD5);
+           break;
+       default:
+           continue;
+           break;
        }
-       hi = headerFreeIterator(hi);
-
-       res += res2;
-
-       if (res2) {
-           if (rpmIsVerbose()) {
-               rpmlog(RPMLOG_NOTICE, "%s", buf);
-           } else {
-               rpmlog(RPMLOG_NOTICE, "%s%s%s%s%s%s%s%s\n", buf,
-                       _("NOT OK"),
-                       (missingKeys[0] != '\0') ? _(" (MISSING KEYS:") : "",
-                       missingKeys,
-                       (missingKeys[0] != '\0') ? _(") ") : "",
-                       (untrustedKeys[0] != '\0') ? _(" (UNTRUSTED KEYS:") : "",
-                       untrustedKeys,
-                       (untrustedKeys[0] != '\0') ? _(")") : "");
 
-           }
-       } else {
-           if (rpmIsVerbose()) {
-               rpmlog(RPMLOG_NOTICE, "%s", buf);
-           } else {
-               rpmlog(RPMLOG_NOTICE, "%s%s%s%s%s%s%s%s\n", buf,
-                       _("OK"),
-                       (missingKeys[0] != '\0') ? _(" (MISSING KEYS:") : "",
-                       missingKeys,
-                       (missingKeys[0] != '\0') ? _(") ") : "",
-                       (untrustedKeys[0] != '\0') ? _(" (UNTRUSTED KEYS:") : "",
-                       untrustedKeys,
-                       (untrustedKeys[0] != '\0') ? _(")") : "");
-           }
+       rc = rpmVerifySignature(keyring, &sigtd, sig, ctx, &result);
+       rpmDigestFinal(ctx, NULL, NULL, 0);
+
+       formatResult(sigtd.tag, rc, result, havekey, 
+                    (rc == RPMRC_NOKEY ? &missingKeys : &untrustedKeys),
+                    &buf);
+       free(result);
+
+       if (rc != RPMRC_OK) {
+           failed = 1;
        }
 
     }
+    res = failed;
+
+    if (rpmIsVerbose()) {
+       rpmlog(RPMLOG_NOTICE, "%s", buf);
+    } else {
+       const char *ok = (failed ? _("NOT OK") : _("OK"));
+       rpmlog(RPMLOG_NOTICE, "%s%s%s%s%s%s%s%s\n", buf, ok,
+              missingKeys ? _(" (MISSING KEYS:") : "",
+              missingKeys ? missingKeys : "",
+              missingKeys ? _(") ") : "",
+              untrustedKeys ? _(" (UNTRUSTED KEYS:") : "",
+              untrustedKeys ? untrustedKeys : "",
+              untrustedKeys ? _(")") : "");
+    }
+    free(missingKeys);
+    free(untrustedKeys);
 
 exit:
+    free(buf);
+    rpmDigestBundleFree(hdrbundle);
+    rpmDigestBundleFree(plbundle);
+    fdSetBundle(fd, NULL); /* XXX avoid double-free from fd close */
     sigh = rpmFreeSignature(sigh);
-    rpmtsCleanDig(ts);
+    hi = headerFreeIterator(hi);
+    pgpDigParamsFree(sig);
     return res;
 }
 
-int rpmcliSign(rpmts ts, QVA_t qva, const char ** argv)
+/* Wrapper around rpmkVerifySigs to preserve API */
+int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd, const char * fn)
+{
+    int rc = 1; /* assume failure */
+    if (ts && qva && fd && fn) {
+       rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
+       rc = rpmpkgVerifySigs(keyring, qva->qva_flags, fd, fn);
+       rpmKeyringFree(keyring);
+    }
+    return rc;
+}
+
+int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv)
 {
     const char * arg;
     int res = 0;
-    int xx;
-
-    if (argv == NULL) return res;
-
-    switch (qva->qva_mode) {
-    case RPMSIGN_CHK_SIGNATURE:
-       break;
-    case RPMSIGN_IMPORT_PUBKEY:
-       return rpmcliImportPubkeys(ts, qva, argv);
-       break;
-    case RPMSIGN_NEW_SIGNATURE:
-    case RPMSIGN_ADD_SIGNATURE:
-    case RPMSIGN_DEL_SIGNATURE:
-       return rpmReSign(ts, qva, argv);
-       break;
-    case RPMSIGN_NONE:
-    default:
-       return -1;
-       break;
-    }
+    rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
+    rpmVerifyFlags verifyFlags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
+    
+    verifyFlags &= ~rpmcliQueryFlags;
 
     while ((arg = *argv++) != NULL) {
-       FD_t fd;
-
-       fd = Fopen(arg, "r.ufdio");
+       FD_t fd = Fopen(arg, "r.ufdio");
        if (fd == NULL || Ferror(fd)) {
            rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), 
                     arg, Fstrerror(fd));
            res++;
-       } else if (rpmVerifySignatures(qva, ts, fd, arg)) {
+       } else if (rpmpkgVerifySigs(keyring, verifyFlags, fd, arg)) {
            res++;
        }
 
-       if (fd != NULL) xx = Fclose(fd);
+       Fclose(fd);
+       rpmdbCheckSignals();
     }
-
+    rpmKeyringFree(keyring);
     return res;
 }