rpmRC rpmnsProbeSignature(void * _ts, const char * fn, const char * sigfn, const char * pubfn, const char * pubid, /*@unused@*/ int flags) { rpmts ts = (rpmts) _ts; pgpDig dig = rpmtsDig(ts); pgpDigParams sigp = pgpGetSignature(dig); pgpDigParams pubp = pgpGetPubkey(dig); rpmuint8_t * sigpkt = NULL; size_t sigpktlen = 0; DIGEST_CTX ctx = NULL; rpmRC rc = RPMRC_FAIL; /* assume failure */ int xx; rpmhkp hkp = NULL; pgpPkt pp = (pgpPkt) alloca(sizeof(*pp)); size_t pleft; int validate = 1; SPEW((stderr, "==> check(%s, %s, %s, %s)\n", fn, (sigfn ? sigfn : "(null)"), (pubfn ? pubfn : "(null)"), (pubid ? pubid : "(null)"))); /* Choose signature location: clearsign from fn if sigfn is NULL */ assert(fn && *fn); if (!(sigfn && *sigfn)) sigfn = fn; /* Load the signature from the file. */ { const char * _sigfn = rpmExpand(sigfn, NULL); xx = pgpReadPkts(_sigfn, &sigpkt, &sigpktlen); if (xx != PGPARMOR_SIGNATURE) { SPEW((stderr, "==> pgpReadPkts(%s) SIG %p[%u] ret %d\n", _sigfn, sigpkt, (unsigned)sigpktlen, xx)); _sigfn = _free(_sigfn); goto exit; } _sigfn = _free(_sigfn); } pleft = sigpktlen; xx = pgpPktLen(sigpkt, pleft, pp); xx = rpmhkpLoadSignature(NULL, dig, pp); if (xx) goto exit; if (sigp->version != (rpmuint8_t)3 && sigp->version != (rpmuint8_t)4) { SPEW((stderr, "==> unverifiable V%u\n", (unsigned)sigp->version)); goto exit; } if (ts->hkp == NULL) ts->hkp = rpmhkpNew(NULL, 0); hkp = rpmhkpLink(ts->hkp); /* Load the pubkey. Use pubfn if specified, otherwise rpmdb keyring. */ if (pubfn && *pubfn) { const char * _pubfn = rpmExpand(pubfn, NULL); /*@-type@*/ hkp->pkt = _free(hkp->pkt); /* XXX memleaks */ hkp->pktlen = 0; xx = pgpReadPkts(_pubfn, &hkp->pkt, &hkp->pktlen); /*@=type@*/ if (xx != PGPARMOR_PUBKEY) { SPEW((stderr, "==> pgpReadPkts(%s) PUB %p[%u] rc %d\n", _pubfn, hkp->pkt, (unsigned)hkp->pktlen, xx)); _pubfn = _free(_pubfn); goto exit; } _pubfn = _free(_pubfn); /* Split the result into packet array. */ hkp->pkts = _free(hkp->pkts); /* XXX memleaks */ hkp->npkts = 0; xx = pgpGrabPkts(hkp->pkt, hkp->pktlen, &hkp->pkts, &hkp->npkts); #ifdef DYING _rpmhkpDumpDig(__FUNCTION__, dig, NULL); #endif if (!xx) (void) pgpPubkeyFingerprint(hkp->pkt, hkp->pktlen, hkp->keyid); memcpy(pubp->signid, hkp->keyid, sizeof(pubp->signid));/* XXX useless */ /* Validate pubkey self-signatures (if any). */ /* XXX TODO: only validate once, then cache using rpmku */ /* XXX need at least 3 packets to validate a pubkey */ if (validate && hkp->npkts >= 3) { #ifdef DYING pgpPrtPkts(hkp->pkt, hkp->pktlen, NULL, 1); #endif xx = rpmhkpValidate(hkp, NULL); switch (xx) { case RPMRC_OK: break; case RPMRC_NOTFOUND: case RPMRC_FAIL: /* XXX remap to NOTFOUND? */ case RPMRC_NOTTRUSTED: case RPMRC_NOKEY: default: SPEW((stderr, "\t<-- rpmhkpValidate() rc %d\n", xx)); rc = (rpmRC)xx; goto exit; } } /* Retrieve parameters from pubkey/subkey packet(s). */ xx = rpmhkpFindKey(hkp, dig, sigp->signid, sigp->pubkey_algo); if (xx) { SPEW((stderr, "\t<-- rpmhkpFindKey() rc %d\n", xx)); goto exit; } } else { rc = (rpmRC)pgpFindPubkey(dig); if (rc != RPMRC_OK) { SPEW((stderr, "\t<-- pgpFindPubkey() rc %d\n", rc)); goto exit; } } /* Is this the requested pubkey? */ if (pubid && *pubid) { size_t ns = strlen(pubid); const char * s; char * t; size_t i; /* At least 8 hex digits please. */ for (i = 0, s = pubid; *s && isxdigit(*s); s++, i++) {}; if (!(*s == '\0' && i > 8 && (i%2) == 0)) { SPEW((stderr, "==> invalid pubid: %s\n", pubid)); goto exit; } /* Truncate to key id size. */ s = pubid; if (ns > 16) { s += (ns - 16); ns = 16; } ns >>= 1; t = (char *) memset(alloca(ns), 0, ns); for (i = 0; i < ns; i++) t[i] = (char)((nibble(s[2*i]) << 4) | nibble(s[2*i+1])); /* Compare the pubkey id. */ s = (const char *)pubp->signid; xx = memcmp(t, s + (8 - ns), ns); #ifdef DYING /* XXX HACK: V4 RSA key id's are wonky atm. */ if (pubp->pubkey_algo == (rpmuint8_t)PGPPUBKEYALGO_RSA) xx = 0; #endif if (xx) { SPEW((stderr, "==> mismatched: pubkey id (%08x %08x) != %s\n", pgpGrab(pubp->signid, 4), pgpGrab(pubp->signid+4, 4), pubid)); goto exit; } }
int showVerifyPackage(QVA_t qva, rpmts ts, Header h) { static int scareMem = 0; rpmVerifyAttrs omitMask = ((qva->qva_flags & VERIFY_ATTRS) ^ VERIFY_ATTRS); int spew = (qva->qva_mode != 'v'); /* XXX no output w verify(...) probe. */ int ec = 0; int i; rpmfi fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem); uint32_t fc = rpmfiFC(fi); { /* Verify header digest/signature. */ if (qva->qva_flags & (VERIFY_DIGEST | VERIFY_SIGNATURE)) { const char * horigin = headerGetOrigin(h); const char * msg = NULL; size_t uhlen = 0; void * uh = headerUnload(h, &uhlen); int lvl = headerCheck(rpmtsDig(ts), uh, uhlen, &msg) == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG; rpmlog(lvl, "%s: %s\n", (horigin ? horigin : "verify"), (msg ? msg : "")); rpmtsCleanDig(ts); uh = _free(uh); msg = _free(msg); } /* Verify file digests. */ if (fc > 0 && (qva->qva_flags & VERIFY_FILES)) #if defined(_OPENMP) #pragma omp parallel for private(i) reduction(+:ec) #endif for (i = 0; i < (int)fc; i++) { int fflags = fi->fflags[i]; rpmvf vf; int rc; /* If not querying %config, skip config files. */ if ((qva->qva_fflags & RPMFILE_CONFIG) && (fflags & RPMFILE_CONFIG)) continue; /* If not querying %doc, skip doc files. */ if ((qva->qva_fflags & RPMFILE_DOC) && (fflags & RPMFILE_DOC)) continue; /* If not verifying %ghost, skip ghost files. */ /* XXX the broken!!! logic disables %ghost queries always. */ if (!(qva->qva_fflags & RPMFILE_GHOST) && (fflags & RPMFILE_GHOST)) continue; /* Gather per-file data into a carrier. */ vf = rpmvfNew(ts, fi, i, omitMask); /* Verify per-file metadata. */ rc = rpmvfVerify(vf, spew); if (rc) ec += rc; (void) rpmvfFree(vf); vf = NULL; } /* Run verify/sanity scripts (if any). */ if (qva->qva_flags & VERIFY_SCRIPT) { int rc; if (headerIsEntry(h, RPMTAG_VERIFYSCRIPT) || headerIsEntry(h, RPMTAG_SANITYCHECK)) { FD_t fdo = fdDup(STDOUT_FILENO); rc = rpmfiSetHeader(fi, h); if ((rc = rpmVerifyScript(qva, ts, fi, fdo)) != 0) ec += rc; if (fdo != NULL) rc = Fclose(fdo); rc = rpmfiSetHeader(fi, NULL); } } /* Verify dependency assertions. */ if (qva->qva_flags & VERIFY_DEPS) { int save_noise = _rpmds_unspecified_epoch_noise; int rc; /*@-mods@*/ if (rpmIsVerbose()) _rpmds_unspecified_epoch_noise = 1; if ((rc = verifyDependencies(qva, ts, h)) != 0) ec += rc; _rpmds_unspecified_epoch_noise = save_noise; /*@=mods@*/ } } fi = rpmfiFree(fi); return ec; }
/*@-mods@*/ rpmRC rpmReadPackageFile(rpmts ts, FD_t fd, const char * fn, Header * hdrp) { HE_t he = (HE_t) memset(alloca(sizeof(*he)), 0, sizeof(*he)); HE_t she = (HE_t) memset(alloca(sizeof(*she)), 0, sizeof(*she)); pgpDig dig = rpmtsDig(ts); char buf[8*BUFSIZ]; ssize_t count; Header sigh = NULL; rpmtsOpX opx; rpmop op = NULL; size_t nb; unsigned ix; Header h = NULL; const char * msg = NULL; rpmVSFlags vsflags; rpmRC rc = RPMRC_FAIL; /* assume failure */ rpmop opsave = (rpmop) memset(alloca(sizeof(*opsave)), 0, sizeof(*opsave)); int xx; pgpPkt pp = (pgpPkt) alloca(sizeof(*pp)); if (hdrp) *hdrp = NULL; assert(dig != NULL); (void) fdSetDig(fd, dig); /* Snapshot current I/O counters (cached persistent I/O reuses counters) */ (void) rpmswAdd(opsave, fdstat_op(fd, FDSTAT_READ)); { const char item[] = "Lead"; msg = NULL; rc = rpmpkgRead(item, fd, NULL, &msg); switch (rc) { default: rpmlog(RPMLOG_ERR, "%s: %s: %s\n", fn, item, msg); /*@fallthrough@*/ case RPMRC_NOTFOUND: msg = _free(msg); goto exit; /*@notreached@*/ break; case RPMRC_OK: break; } msg = _free(msg); } { const char item[] = "Signature"; msg = NULL; rc = rpmpkgRead(item, fd, &sigh, &msg); switch (rc) { default: rpmlog(RPMLOG_ERR, "%s: %s: %s", fn, item, (msg && *msg ? msg : _("read failed\n"))); msg = _free(msg); goto exit; /*@notreached@*/ break; case RPMRC_OK: if (sigh == NULL) { rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), fn); rc = RPMRC_FAIL; goto exit; } break; } msg = _free(msg); } #define _chk(_mask) (she->tag == 0 && !(vsflags & (_mask))) /* * Figger the most effective available signature. * Prefer signatures over digests, then header-only over header+payload. * DSA will be preferred over RSA if both exist because tested first. * Note that NEEDPAYLOAD prevents header+payload signatures and digests. */ she->tag = (rpmTag)0; opx = (rpmtsOpX)0; vsflags = pgpDigVSFlags; if (_chk(RPMVSF_NOECDSAHEADER) && headerIsEntry(sigh, (rpmTag)RPMSIGTAG_ECDSA)) { she->tag = (rpmTag)RPMSIGTAG_ECDSA; } else if (_chk(RPMVSF_NODSAHEADER) && headerIsEntry(sigh, (rpmTag)RPMSIGTAG_DSA)) { she->tag = (rpmTag)RPMSIGTAG_DSA; } else if (_chk(RPMVSF_NORSAHEADER) && headerIsEntry(sigh, (rpmTag)RPMSIGTAG_RSA)) { she->tag = (rpmTag)RPMSIGTAG_RSA; } else if (_chk(RPMVSF_NOSHA1HEADER) && headerIsEntry(sigh, (rpmTag)RPMSIGTAG_SHA1)) { she->tag = (rpmTag)RPMSIGTAG_SHA1; } else if (_chk(RPMVSF_NOMD5|RPMVSF_NEEDPAYLOAD) && headerIsEntry(sigh, (rpmTag)RPMSIGTAG_MD5)) { she->tag = (rpmTag)RPMSIGTAG_MD5; fdInitDigest(fd, PGPHASHALGO_MD5, 0); opx = RPMTS_OP_DIGEST; } /* Read the metadata, computing digest(s) on the fly. */ h = NULL; msg = NULL; /* XXX stats will include header i/o and setup overhead. */ /* XXX repackaged packages have appended tags, legacy dig/sig check fails */ if (opx > 0) { op = (rpmop) pgpStatsAccumulator(dig, opx); (void) rpmswEnter(op, 0); } /*@-type@*/ /* XXX arrow access of non-pointer (FDSTAT_t) */ nb = fd->stats->ops[FDSTAT_READ].bytes; { const char item[] = "Header"; msg = NULL; rc = rpmpkgRead(item, fd, &h, &msg); if (rc != RPMRC_OK) { rpmlog(RPMLOG_ERR, "%s: %s: %s\n", fn, item, msg); msg = _free(msg); goto exit; } msg = _free(msg); } nb = fd->stats->ops[FDSTAT_READ].bytes - nb; /*@=type@*/ if (opx > 0 && op != NULL) { (void) rpmswExit(op, nb); op = NULL; } /* Any digests or signatures to check? */ if (she->tag == 0) { rc = RPMRC_OK; goto exit; } dig->nbytes = 0; /* Fish out the autosign pubkey (if present). */ he->tag = RPMTAG_PUBKEYS; xx = headerGet(h, he, 0); if (xx && he->p.argv != NULL && he->c > 0) switch (he->t) { default: break; case RPM_STRING_ARRAY_TYPE: ix = he->c - 1; /* XXX FIXME: assumes last pubkey */ dig->pub = _free(dig->pub); dig->publen = 0; { rpmiob iob = rpmiobNew(0); iob = rpmiobAppend(iob, he->p.argv[ix], 0); xx = pgpArmorUnwrap(iob, (rpmuint8_t **)&dig->pub, &dig->publen); iob = rpmiobFree(iob); } if (xx != PGPARMOR_PUBKEY) { dig->pub = _free(dig->pub); dig->publen = 0; } break; } he->p.ptr = _free(he->p.ptr); /* Retrieve the tag parameters from the signature header. */ xx = headerGet(sigh, she, 0); if (she->p.ptr == NULL) { rc = RPMRC_FAIL; goto exit; } /*@-ownedtrans -noeffect@*/ xx = pgpSetSig(dig, she->tag, she->t, she->p.ptr, she->c); /*@=ownedtrans =noeffect@*/ switch ((rpmSigTag)she->tag) { default: /* XXX keep gcc quiet. */ assert(0); /*@notreached@*/ break; case RPMSIGTAG_RSA: /* Parse the parameters from the OpenPGP packets that will be needed. */ xx = pgpPktLen(she->p.ui8p, she->c, pp); xx = rpmhkpLoadSignature(NULL, dig, pp); if (dig->signature.version != 3 && dig->signature.version != 4) { rpmlog(RPMLOG_ERR, _("skipping package %s with unverifiable V%u signature\n"), fn, dig->signature.version); rc = RPMRC_FAIL; goto exit; } xx = hBlobDigest(h, dig, dig->signature.hash_algo, &dig->hrsa); break; case RPMSIGTAG_DSA: /* Parse the parameters from the OpenPGP packets that will be needed. */ xx = pgpPktLen(she->p.ui8p, she->c, pp); xx = rpmhkpLoadSignature(NULL, dig, pp); if (dig->signature.version != 3 && dig->signature.version != 4) { rpmlog(RPMLOG_ERR, _("skipping package %s with unverifiable V%u signature\n"), fn, dig->signature.version); rc = RPMRC_FAIL; goto exit; } xx = hBlobDigest(h, dig, dig->signature.hash_algo, &dig->hdsa); break; case RPMSIGTAG_ECDSA: /* Parse the parameters from the OpenPGP packets that will be needed. */ xx = pgpPktLen(she->p.ui8p, she->c, pp); xx = rpmhkpLoadSignature(NULL, dig, pp); if (dig->signature.version != 3 && dig->signature.version != 4) { rpmlog(RPMLOG_ERR, _("skipping package %s with unverifiable V%u signature\n"), fn, dig->signature.version); rc = RPMRC_FAIL; goto exit; } xx = hBlobDigest(h, dig, dig->signature.hash_algo, &dig->hecdsa); break; case RPMSIGTAG_SHA1: /* XXX dig->hsha? */ xx = hBlobDigest(h, dig, PGPHASHALGO_SHA1, &dig->hdsa); break; case RPMSIGTAG_MD5: /* Legacy signatures need the compressed payload in the digest too. */ op = (rpmop) pgpStatsAccumulator(dig, 10); /* RPMTS_OP_DIGEST */ (void) rpmswEnter(op, 0); while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0) dig->nbytes += count; (void) rpmswExit(op, dig->nbytes); op->count--; /* XXX one too many */ dig->nbytes += nb; /* XXX include size of header blob. */ if (count < 0) { rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), fn, Fstrerror(fd)); rc = RPMRC_FAIL; goto exit; } /* XXX Steal the digest-in-progress from the file handle. */ fdStealDigest(fd, dig); break; } /** @todo Implement disable/enable/warn/error/anal policy. */ buf[0] = '\0'; rc = rpmVerifySignature(dig, buf); switch (rc) { case RPMRC_OK: /* Signature is OK. */ rpmlog(RPMLOG_DEBUG, "%s: %s\n", fn, buf); break; case RPMRC_NOTTRUSTED: /* Signature is OK, but key is not trusted. */ case RPMRC_NOKEY: /* Public key is unavailable. */ #ifndef DYING /* XXX Print NOKEY/NOTTRUSTED warning only once. */ { int lvl = (pgpStashKeyid(dig) ? RPMLOG_DEBUG : RPMLOG_WARNING); rpmlog(lvl, "%s: %s\n", fn, buf); } break; case RPMRC_NOTFOUND: /* Signature is unknown type. */ rpmlog(RPMLOG_WARNING, "%s: %s\n", fn, buf); break; #else case RPMRC_NOTFOUND: /* Signature is unknown type. */ case RPMRC_NOSIG: /* Signature is unavailable. */ #endif default: case RPMRC_FAIL: /* Signature does not verify. */ rpmlog(RPMLOG_ERR, "%s: %s\n", fn, buf); break; } exit: if (rc != RPMRC_FAIL && h != NULL && hdrp != NULL) { /* Append (and remap) signature tags to the metadata. */ headerMergeLegacySigs(h, sigh); /* Bump reference count for return. */ *hdrp = headerLink(h); } (void)headerFree(h); h = NULL; /* Accumulate time reading package header. */ (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_READHDR), fdstat_op(fd, FDSTAT_READ)); (void) rpmswSub(rpmtsOp(ts, RPMTS_OP_READHDR), opsave); #ifdef NOTYET /* Return RPMRC_NOSIG for MANDATORY signature verification. */ { rpmSigTag sigtag = pgpGetSigtag(dig); switch (sigtag) { default: rc = RPMRC_NOSIG; /*@fallthrough@*/ case RPMSIGTAG_RSA: case RPMSIGTAG_DSA: case RPMSIGTAG_ECDSA: break; } } #endif rpmtsCleanDig(ts); (void)headerFree(sigh); sigh = NULL; return rc; }