2 * \file lib/rpmchecksig.c
3 * Verify the signature of a package.
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>
18 #include "rpmio/digest.h"
19 #include "rpmio/rpmio_internal.h" /* fdSetBundle() */
20 #include "lib/rpmlead.h"
21 #include "lib/signature.h"
27 static int closeFile(FD_t *fdp)
29 if (fdp == NULL || *fdp == NULL)
32 /* close and reset *fdp to NULL */
40 static int manageFile(FD_t *fdp, const char *fn, int flags)
44 if (fdp == NULL || fn == NULL) /* programmer error */
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,
60 if (*fdp != NULL && fn != NULL)
63 /* XXX never reached */
68 * Copy header+payload, calculating digest(s) on the fly.
70 static int copyFile(FD_t *sfdp, const char *sfnp,
71 FD_t *tfdp, const char *tfnp)
73 unsigned char buf[BUFSIZ];
77 if (manageFile(sfdp, sfnp, O_RDONLY))
79 if (manageFile(tfdp, tfnp, O_WRONLY|O_CREAT|O_TRUNC))
82 while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), *sfdp)) > 0)
84 if (Fwrite(buf, sizeof(buf[0]), count, *tfdp) != count) {
85 rpmlog(RPMLOG_ERR, _("%s: Fwrite failed: %s\n"), tfnp,
91 rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), sfnp, Fstrerror(*sfdp));
94 if (Fflush(*tfdp) != 0) {
95 rpmlog(RPMLOG_ERR, _("%s: Fflush failed: %s\n"), tfnp,
102 if (*sfdp) (void) closeFile(sfdp);
103 if (*tfdp) (void) closeFile(tfdp);
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
114 static int getSignid(Header sig, rpmSigTag sigtag, pgpKeyID_t signid)
119 if (headerGet(sig, sigtag, &pkt, HEADERGET_DEFAULT) && pkt.data != NULL) {
120 pgpDig dig = pgpNewDig();
122 if (!pgpPrtPkts(pkt.data, pkt.count, dig, 0)) {
123 memcpy(signid, dig->signature.signid, sizeof(dig->signature.signid));
127 dig = pgpFreeDig(dig);
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
140 static int rpmReSign(rpmts ts, QVA_t qva, ARGV_const_t argv)
147 char *sigtarget = NULL, *trpm = NULL;
150 int res = -1; /* assume failure */
151 int deleting = (qva->qva_mode == RPMSIGN_DEL_SIGNATURE);
155 while ((rpm = *argv++) != NULL)
160 fprintf(stdout, "%s:\n", rpm);
162 if (manageFile(&fd, rpm, O_RDONLY))
167 if ((rc = rpmLeadRead(fd, lead)) == RPMRC_OK) {
168 const char *lmsg = NULL;
169 rc = rpmLeadCheck(lead, &lmsg);
171 rpmlog(RPMLOG_ERR, "%s: %s\n", rpm, lmsg);
174 if (rc != RPMRC_OK) {
175 lead = rpmLeadFree(lead);
180 rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg);
183 rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), rpm,
184 (msg && *msg ? msg : "\n"));
190 rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), rpm);
197 /* ASSERT: ofd == NULL && sigtarget == NULL */
198 ofd = rpmMkTempFile(NULL, &sigtarget);
199 if (ofd == NULL || Ferror(ofd)) {
200 rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n"));
203 /* Write the header and archive to a temp file */
204 if (copyFile(&fd, rpm, &ofd, sigtarget))
206 /* Both fd and ofd are now closed. sigtarget contains tempfile name. */
207 /* ASSERT: fd == NULL && ofd == NULL */
209 /* Dump the immutable region (if present). */
210 if (headerGet(sigh, RPMTAG_HEADERSIGNATURES, &utd, HEADERGET_DEFAULT)) {
212 struct rpmtd_s copytd;
222 oh = headerCopyLoad(utd.data);
223 hi = headerInitIterator(oh);
224 while (headerNext(hi, ©td)) {
226 xx = headerPut(nh, ©td, HEADERPUT_DEFAULT);
227 rpmtdFreeData(©td);
229 hi = headerFreeIterator(hi);
232 sigh = headerFree(sigh);
233 sigh = headerLink(nh);
237 /* Eliminate broken digest values. */
238 xx = headerDel(sigh, RPMSIGTAG_BADSHA1_1);
239 xx = headerDel(sigh, RPMSIGTAG_BADSHA1_2);
241 /* Toss and recalculate header+payload size and digests. */
243 rpmSigTag const sigs[] = { RPMSIGTAG_SIZE,
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))
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;
265 /* Grab the old signature fingerprint (if any) */
266 memset(oldsignid, 0, sizeof(oldsignid));
267 xx = getSignid(sigh, sigtag, oldsignid);
271 xx = headerDel(sigh, RPMSIGTAG_GPG);
274 xx = headerDel(sigh, RPMSIGTAG_PGP);
277 xx = headerDel(sigh, RPMSIGTAG_DSA);
280 xx = headerDel(sigh, RPMSIGTAG_RSA);
286 xx = headerDel(sigh, sigtag);
287 if (rpmAddSignature(sigh, sigtarget, sigtag, qva->passPhrase)) {
291 /* If package was previously signed, check for same signer. */
292 memset(newsignid, 0, sizeof(newsignid));
293 if (memcmp(oldsignid, newsignid, sizeof(oldsignid))) {
295 /* Grab the new signature fingerprint */
296 xx = getSignid(sigh, sigtag, newsignid);
298 /* If same signer, skip resigning the package. */
299 if (!memcmp(oldsignid, newsignid, sizeof(oldsignid))) {
301 char *signid = pgpHexStr(newsignid+4, sizeof(newsignid)-4);
302 rpmlog(RPMLOG_WARNING,
303 _("%s: was already signed by key ID %s, skipping\n"),
307 /* Clean up intermediate target */
308 xx = unlink(sigtarget);
309 sigtarget = _free(sigtarget);
315 /* Reallocate the signature into one contiguous region. */
316 sigh = headerReload(sigh, RPMTAG_HEADERSIGNATURES);
317 if (sigh == NULL) /* XXX can't happen */
320 rasprintf(&trpm, "%s.XXXXXX", rpm);
321 ofd = rpmMkTemp(trpm);
322 if (ofd == NULL || Ferror(ofd)) {
323 rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n"));
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,
336 if (rpmWriteSignature(ofd, sigh)) {
337 rpmlog(RPMLOG_ERR, _("%s: rpmWriteSignature failed: %s\n"), trpm,
342 /* Append the header and archive from the temp file */
343 /* ASSERT: fd == NULL && ofd != NULL */
344 if (copyFile(&fd, sigtarget, &ofd, trpm))
346 /* Both fd and ofd are now closed. */
347 /* ASSERT: fd == NULL && ofd == NULL */
349 /* Move final target into place, restore file permissions. */
354 xx = rename(trpm, rpm);
355 xx = chmod(rpm, st.st_mode);
359 /* Clean up intermediate target */
360 xx = unlink(sigtarget);
361 sigtarget = _free(sigtarget);
367 if (fd) (void) closeFile(&fd);
368 if (ofd) (void) closeFile(&ofd);
370 sigh = rpmFreeSignature(sigh);
373 xx = unlink(sigtarget);
374 sigtarget = _free(sigtarget);
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
391 static int rpmcliImportPubkeys(const rpmts ts, ARGV_const_t argv)
396 while ((fn = *argv++) != NULL) {
397 unsigned char * pkt = NULL;
402 /* If arg looks like a keyid, then attempt keyserver retrieve. */
403 if (rstreqn(fn, "0x", 2)) {
406 for (i = 0, s = fn+2; *s && isxdigit(*s); s++, i++)
408 if (i == 8 || i == 16) {
409 t = rpmExpand("%{_hkp_keyserver_query}", fn+2, NULL);
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);
424 rpmlog(RPMLOG_ERR, _("%s: import read failed(%d).\n"), fn, rc);
427 rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn);
439 * @todo If the GPG key was known available, the md5 digest could be skipped.
441 static int readFile(FD_t fd, const char * fn, pgpDig dig,
442 rpmDigestBundle plbundle, rpmDigestBundle hdrbundle)
444 unsigned char buf[4*BUFSIZ];
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);
455 if (headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) {
458 if (!headerGet(h, RPMTAG_HEADERIMMUTABLE, &utd, HEADERGET_DEFAULT)){
460 _("%s: Immutable header region could not be read. "
461 "Corrupted package?\n"), fn);
464 rpmDigestBundleUpdate(hdrbundle, rpm_header_magic, sizeof(rpm_header_magic));
465 rpmDigestBundleUpdate(hdrbundle, utd.data, utd.count);
469 /* Read the payload from the package. */
470 while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0) {}
472 rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), fn, Fstrerror(fd));
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)
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)) {
494 _("skipping package %s with unverifiable V%u signature\n"), fn,
495 dig->signature.version);
501 * Figure best available signature.
502 * XXX TODO: Similar detection in rpmReadPackageFile(), unify these.
504 static rpmSigTag bestSig(Header sigh, int nosignatures, int nodigests)
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;
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 */
526 static const char *sigtagname(rpmSigTag sigtag, int upper)
528 const char *n = NULL;
532 n = (upper ? "SIZE" : "size");
535 n = (upper ? "SHA1" : "sha1");
538 n = (upper ? "MD5" : "md5");
541 n = (upper ? "RSA" : "rsa");
543 case RPMSIGTAG_PGP5: /* XXX legacy */
545 n = (upper ? "(MD5) PGP" : "(md5) pgp");
548 n = (upper ? "(SHA1) DSA" : "(sha1) dsa");
551 n = (upper ? "GPG" : "gpg");
554 n = (upper ? "?UnknownSigatureType?" : "???");
561 * Format sigcheck result for output, appending the message spew to buf and
562 * bad/missing keyids to keyprob.
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.
569 static void formatResult(rpmSigTag sigtag, rpmRC sigres, const char *result,
570 int havekey, char **keyprob, char **buf)
573 if (rpmIsVerbose()) {
574 rasprintf(&msg, " %s", result);
576 /* Check for missing / untrusted keys in result. */
577 const char *signame = sigtagname(sigtag, (sigres != RPMRC_OK));
579 if (havekey && (sigres == RPMRC_NOKEY || sigres == RPMRC_NOTTRUSTED)) {
580 const char *tempKey = strstr(result, "ey ID");
582 char keyid[sizeof(pgpKeyID_t) + 1];
583 rstrlcpy(keyid, tempKey + 6, sizeof(keyid));
584 rstrscat(keyprob, " ", signame, "#", keyid, NULL);
587 rasprintf(&msg, (*keyprob ? "(%s) " : "%s "), signame);
593 static int rpmpkgVerifySigs(rpmKeyring keyring, rpmQueryFlags flags,
594 FD_t fd, const char *fn)
598 char *missingKeys = NULL;
599 char *untrustedKeys = NULL;
600 struct rpmtd_s sigtd;
605 HeaderIterator hi = NULL;
607 int res = 1; /* assume failure */
611 int nodigests = !(flags & VERIFY_DIGEST);
612 int nosignatures = !(flags & VERIFY_SIGNATURE);
613 rpmDigestBundle plbundle = rpmDigestBundleNew();
614 rpmDigestBundle hdrbundle = rpmDigestBundleNew();
616 rpmlead lead = rpmLeadNew();
617 if ((rc = rpmLeadRead(fd, lead)) == RPMRC_OK) {
618 const char *lmsg = NULL;
619 rc = rpmLeadCheck(lead, &lmsg);
621 rpmlog(RPMLOG_ERR, "%s: %s\n", fn, lmsg);
623 lead = rpmLeadFree(lead);
625 if (rc != RPMRC_OK) {
629 rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg);
632 rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), fn,
633 (msg && *msg ? msg : "\n"));
639 rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), fn);
646 /* Grab a hint of what needs doing to avoid duplication. */
647 sigtag = bestSig(sigh, nosignatures, nodigests);
650 sigp = &dig->signature;
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);
663 if (headerIsEntry(sigh, RPMSIGTAG_PGP) ||
664 headerIsEntry(sigh, RPMSIGTAG_PGP5) ||
665 headerIsEntry(sigh, RPMSIGTAG_MD5)) {
666 rpmDigestBundleAdd(plbundle, PGPHASHALGO_MD5, RPMDIGEST_NONE);
668 if (headerIsEntry(sigh, RPMSIGTAG_GPG)) {
669 rpmDigestBundleAdd(plbundle, PGPHASHALGO_SHA1, RPMDIGEST_NONE);
672 /* always do sha1 hash of header */
673 rpmDigestBundleAdd(hdrbundle, PGPHASHALGO_SHA1, RPMDIGEST_NONE);
675 /* Read the file, generating digest(s) on the fly. */
676 fdSetBundle(fd, plbundle);
677 if (readFile(fd, fn, dig, plbundle, hdrbundle)) {
681 rasprintf(&buf, "%s:%c", fn, (rpmIsVerbose() ? '\n' : ' ') );
683 hi = headerInitIterator(sigh);
684 for (; headerNext(hi, &sigtd) != 0; rpmtdFreeData(&sigtd)) {
687 DIGEST_CTX ctx = NULL;
688 if (sigtd.data == NULL) /* XXX can't happen */
691 /* Clean up parameters from previous sigtag. */
696 case RPMSIGTAG_PGP5: /* XXX legacy */
703 if (parsePGP(&sigtd, fn, dig) != RPMRC_OK) {
706 ctx = rpmDigestBundleDupCtx(havekey ? plbundle : hdrbundle,
707 dig->signature.hash_algo);
712 ctx = rpmDigestBundleDupCtx(hdrbundle, PGPHASHALGO_SHA1);
717 ctx = rpmDigestBundleDupCtx(plbundle, PGPHASHALGO_MD5);
724 rc = rpmVerifySignature(keyring, &sigtd, dig, ctx, &result);
725 rpmDigestFinal(ctx, NULL, NULL, 0);
727 formatResult(sigtd.tag, rc, result, havekey,
728 (rc == RPMRC_NOKEY ? &missingKeys : &untrustedKeys),
732 if (rc != RPMRC_OK) {
739 if (rpmIsVerbose()) {
740 rpmlog(RPMLOG_NOTICE, "%s", buf);
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 ? _(")") : "");
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);
765 /* Wrapper around rpmkVerifySigs to preserve API */
766 int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd, const char * fn)
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);
777 int rpmcliSign(rpmts ts, QVA_t qva, ARGV_const_t argv)
782 rpmKeyring keyring = NULL;
784 if (argv == NULL) return res;
786 switch (qva->qva_mode) {
787 case RPMSIGN_CHK_SIGNATURE:
789 case RPMSIGN_IMPORT_PUBKEY:
790 return rpmcliImportPubkeys(ts, argv);
792 case RPMSIGN_NEW_SIGNATURE:
793 case RPMSIGN_ADD_SIGNATURE:
794 case RPMSIGN_DEL_SIGNATURE:
795 return rpmReSign(ts, qva, argv);
803 keyring = rpmtsGetKeyring(ts, 1);
804 while ((arg = *argv++) != NULL) {
807 fd = Fopen(arg, "r.ufdio");
808 if (fd == NULL || Ferror(fd)) {
809 rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"),
812 } else if (rpmpkgVerifySigs(keyring, qva->qva_flags, fd, arg)) {
816 if (fd != NULL) xx = Fclose(fd);
819 rpmKeyringFree(keyring);