Sanitize python object -> tag number exception handling
[platform/upstream/rpm.git] / lib / rpmchecksig.c
index 0c6a8be..b8660e6 100644 (file)
@@ -236,8 +236,6 @@ static int rpmReSign(rpmts ts, QVA_t qva, ARGV_const_t argv)
        }
 
        /* Eliminate broken digest values. */
-       xx = headerDel(sigh, RPMSIGTAG_LEMD5_1);
-       xx = headerDel(sigh, RPMSIGTAG_LEMD5_2);
        xx = headerDel(sigh, RPMSIGTAG_BADSHA1_1);
        xx = headerDel(sigh, RPMSIGTAG_BADSHA1_2);
 
@@ -453,59 +451,91 @@ rpmtsClean(ts);
 /**
  * @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, pgpDig dig,
+                   rpmDigestBundle plbundle, rpmDigestBundle hdrbundle)
 {
     unsigned char buf[4*BUFSIZ];
     ssize_t count;
     int rc = 1;
-
-    dig->nbytes = 0;
+    Header h = 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 ((h = headerRead(fd, HEADER_MAGIC_YES)) == NULL) {
+       rpmlog(RPMLOG_ERR, _("%s: headerRead failed\n"), fn);
+       goto exit;
+    }
 
-       if (headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) {
-           struct rpmtd_s utd;
-       
-           if (!headerGet(h, RPMTAG_HEADERIMMUTABLE, &utd, HEADERGET_DEFAULT)){
-               h = headerFree(h);
-               rpmlog(RPMLOG_ERR, 
-                       _("%s: Immutable header region could not be read. "
-                       "Corrupted package?\n"), fn);
-               goto exit;
-           }
-           dig->hdrsha1ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE);
-           (void) rpmDigestUpdate(dig->hdrsha1ctx, rpm_header_magic, sizeof(rpm_header_magic));
-           (void) rpmDigestUpdate(dig->hdrsha1ctx, utd.data, utd.count);
-           dig->hdrmd5ctx = rpmDigestInit(dig->signature.hash_algo, RPMDIGEST_NONE);
-           (void) rpmDigestUpdate(dig->hdrmd5ctx, rpm_header_magic, sizeof(rpm_header_magic));
-           (void) rpmDigestUpdate(dig->hdrmd5ctx, utd.data, utd.count);
-           rpmtdFreeData(&utd);
+    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:
+    headerFree(h);
     return rc;
 }
 
+/* Parse the parameters from the OpenPGP packets that will be needed. */
+/* XXX TODO: unify with similar parsePGP() in package.c */
+static rpmRC parsePGP(rpmtd sigtd, const char *fn, pgpDig dig)
+{
+    rpmRC rc = RPMRC_FAIL;
+    int debug = (_print_pkts & rpmIsDebug());
+    if ((pgpPrtPkts(sigtd->data, sigtd->count, dig, debug) == 0) &&
+        (dig->signature.version == 3 || dig->signature.version == 4)) {
+       rc = RPMRC_OK;
+    } else {
+       rpmlog(RPMLOG_ERR,
+           _("skipping package %s with unverifiable V%u signature\n"), fn,
+           dig->signature.version);
+    }
+    return rc;
+}
+
+/* 
+ * Figure best available signature. 
+ * XXX TODO: Similar detection in rpmReadPackageFile(), unify these.
+ */
+static rpmSigTag bestSig(Header sigh, int nosignatures, int nodigests)
+{
+    rpmSigTag 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;
+}
+
 static const char *sigtagname(rpmSigTag sigtag, int upper)
 {
     const char *n = NULL;
@@ -517,8 +547,6 @@ static const char *sigtagname(rpmSigTag sigtag, int upper)
     case RPMSIGTAG_SHA1:
        n = (upper ? "SHA1" : "sha1");
        break;
-    case RPMSIGTAG_LEMD5_2:
-    case RPMSIGTAG_LEMD5_1:
     case RPMSIGTAG_MD5:
        n = (upper ? "MD5" : "md5");
        break;
@@ -542,25 +570,64 @@ static const char *sigtagname(rpmSigTag sigtag, int upper)
     return n;
 }
 
-int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd,
-               const char * fn)
+/* 
+ * 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(rpmSigTag 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 = strndup(tempKey + 6, 8);
+               char *idprob = NULL;
+               rasprintf(&idprob, " %s#%s", signame, keyid);
+               rstrcat(keyprob, idprob);
+               free(keyid);
+               free(idprob);
+           }
+       }
+       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, *untrustedKeys;
+    char *missingKeys = NULL; 
+    char *untrustedKeys = NULL;
     struct rpmtd_s sigtd;
     rpmTag sigtag;
     pgpDig dig = NULL;
     pgpDigParams sigp;
     Header sigh = NULL;
     HeaderIterator hi = NULL;
-    char * msg;
+    char * msg = NULL;
     int res = 1; /* assume failure */
     int xx;
-    rpmRC rc, sigres;
-    int failed;
-    int nodigests = !(qva->qva_flags & VERIFY_DIGEST);
-    int nosignatures = !(qva->qva_flags & VERIFY_SIGNATURE);
-    rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
+    rpmRC rc;
+    int failed = 0;
+    int nodigests = !(flags & VERIFY_DIGEST);
+    int nosignatures = !(flags & VERIFY_SIGNATURE);
+    rpmDigestBundle plbundle = rpmDigestBundleNew();
+    rpmDigestBundle hdrbundle = rpmDigestBundleNew();
 
     rpmlead lead = rpmLeadNew();
     if ((rc = rpmLeadRead(fd, lead)) == RPMRC_OK) {
@@ -575,7 +642,6 @@ int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd,
        goto exit;
     }
 
-    msg = NULL;
     rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg);
     switch (rc) {
     default:
@@ -594,61 +660,47 @@ int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd,
     msg = _free(msg);
 
     /* 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;
-    }
-    if (sigtag == 0 && !nodigests) {
-       if (headerIsEntry(sigh, RPMSIGTAG_MD5))
-           sigtag = RPMSIGTAG_MD5;
-       else if (headerIsEntry(sigh, RPMSIGTAG_SHA1))
-           sigtag = RPMSIGTAG_SHA1;    /* XXX never happens */
-    }
+    sigtag = bestSig(sigh, nosignatures, nodigests);
 
     dig = pgpNewDig();
     sigp = &dig->signature;
 
     /* XXX RSA needs the hash_algo, so decode early. */
-    if (sigtag == RPMSIGTAG_RSA || sigtag == RPMSIGTAG_PGP) {
+    if (sigtag == RPMSIGTAG_RSA || sigtag == RPMSIGTAG_PGP ||
+               sigtag == RPMSIGTAG_DSA || sigtag == RPMSIGTAG_GPG) {
        xx = headerGet(sigh, sigtag, &sigtd, HEADERGET_DEFAULT);
        xx = pgpPrtPkts(sigtd.data, sigtd.count, dig, 0);
        rpmtdFreeData(&sigtd);
        /* 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);
+       rpmDigestBundleAdd(plbundle, sigp->hash_algo, RPMDIGEST_NONE);
+       rpmDigestBundleAdd(hdrbundle, sigp->hash_algo, RPMDIGEST_NONE);
+    }
+
+    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)) {
+    fdSetBundle(fd, plbundle);
+    if (readFile(fd, fn, dig, plbundle, hdrbundle)) {
        goto exit;
     }
 
-    failed = 0;
-    missingKeys = NULL;
-    untrustedKeys = NULL;
     rasprintf(&buf, "%s:%c", fn, (rpmIsVerbose() ? '\n' : ' ') );
 
     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;
 
@@ -664,83 +716,39 @@ int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd,
        case RPMSIGTAG_DSA:
            if (nosignatures)
                 continue;
-           xx = pgpPrtPkts(sigtd.data, sigtd.count, dig,
-                   (_print_pkts & rpmIsDebug()));
-
-           if (sigp->version != 3 && sigp->version != 4) {
-               rpmlog(RPMLOG_ERR,
-           _("skipping package %s with unverifiable V%u signature\n"),
-                   fn, sigp->version);
+           if (parsePGP(&sigtd, fn, dig) != RPMRC_OK) {
                goto exit;
            }
+           ctx = rpmDigestBundleDupCtx(havekey ? plbundle : hdrbundle,
+                                       dig->signature.hash_algo);
            break;
        case RPMSIGTAG_SHA1:
            if (nodigests)
                 continue;
-           /* XXX Don't bother with header sha1 if header dsa. */
-           if (!nosignatures && sigtd.tag == RPMSIGTAG_DSA)
-               continue;
+           ctx = rpmDigestBundleDupCtx(hdrbundle, PGPHASHALGO_SHA1);
            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 && sigtd.tag == RPMSIGTAG_PGP)
-               continue;
+           ctx = rpmDigestBundleDupCtx(plbundle, PGPHASHALGO_MD5);
            break;
        default:
            continue;
            break;
        }
 
-       sigres = rpmVerifySignature(keyring, &sigtd, dig, &result);
-       if (sigres != RPMRC_OK) {
-           failed = 1;
-       }
+       rc = rpmVerifySignature(keyring, &sigtd, dig, ctx, &result);
+       rpmDigestFinal(ctx, NULL, NULL, 0);
 
-       /* 
-        * 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.
-        */
-       msg = NULL;
-       if (rpmIsVerbose()) {
-           rasprintf(&msg, "    %s", result);
-       } else { 
-           const char *signame;
-           char ** keyprob = NULL;
-           signame = sigtagname(sigtd.tag, (sigres == RPMRC_OK ? 0 : 1));
-
-           /* 
-            * Check for missing / untrusted keys in result. In theory
-            * there could be several missing keys of which only
-            * last is shown, in practise not.
-            */
-            if (havekey && 
-                (sigres == RPMRC_NOKEY || sigres == RPMRC_NOTTRUSTED)) {
-                const char *tempKey = NULL;
-                char *keyid = NULL;
-                keyprob = (sigres == RPMRC_NOKEY ? 
-                           &missingKeys : &untrustedKeys);
-                if (*keyprob) free(*keyprob);
-                tempKey = strstr(result, "ey ID");
-                if (tempKey) 
-                   keyid = strndup(tempKey + 6, 8);
-                rasprintf(keyprob, "%s#%s", signame, keyid);
-                free(keyid);
-            }
-            rasprintf(&msg, (keyprob ? "(%s) " : "%s "), signame);
-       }
+       formatResult(sigtd.tag, rc, result, havekey, 
+                    (rc == RPMRC_NOKEY ? &missingKeys : &untrustedKeys),
+                    &buf);
        free(result);
 
-       rstrcat(&buf, msg);
-       free(msg);
+       if (rc != RPMRC_OK) {
+           failed = 1;
+       }
+
     }
     res = failed;
 
@@ -761,18 +769,33 @@ int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd,
 
 exit:
     free(buf);
+    rpmDigestBundleFree(hdrbundle);
+    rpmDigestBundleFree(plbundle);
+    fdSetBundle(fd, NULL); /* XXX avoid double-free from fd close */
     sigh = rpmFreeSignature(sigh);
     hi = headerFreeIterator(hi);
-    rpmKeyringFree(keyring);
     pgpFreeDig(dig);
     return res;
 }
 
+/* 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 rpmcliSign(rpmts ts, QVA_t qva, ARGV_const_t argv)
 {
     const char * arg;
     int res = 0;
     int xx;
+    rpmKeyring keyring = NULL;
 
     if (argv == NULL) return res;
 
@@ -793,6 +816,7 @@ int rpmcliSign(rpmts ts, QVA_t qva, ARGV_const_t argv)
        break;
     }
 
+    keyring = rpmtsGetKeyring(ts, 1);
     while ((arg = *argv++) != NULL) {
        FD_t fd;
 
@@ -801,12 +825,14 @@ int rpmcliSign(rpmts ts, QVA_t qva, ARGV_const_t argv)
            rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), 
                     arg, Fstrerror(fd));
            res++;
-       } else if (rpmVerifySignatures(qva, ts, fd, arg)) {
+       } else if (rpmpkgVerifySigs(keyring, qva->qva_flags, fd, arg)) {
            res++;
        }
 
        if (fd != NULL) xx = Fclose(fd);
+       rpmdbCheckSignals();
     }
+    rpmKeyringFree(keyring);
 
     return res;
 }