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 "lib/rpmlead.h"
20 #include "lib/signature.h"
26 static int closeFile(FD_t *fdp)
28 if (fdp == NULL || *fdp == NULL)
31 /* close and reset *fdp to NULL */
39 static int manageFile(FD_t *fdp, const char *fn, int flags)
43 if (fdp == NULL || fn == NULL) /* programmer error */
46 /* open a file and set *fdp */
47 if (*fdp == NULL && fn != NULL) {
48 fd = Fopen(fn, (flags & O_ACCMODE) == O_WRONLY ? "w.ufdio" : "r.ufdio");
49 if (fd == NULL || Ferror(fd)) {
50 rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), fn,
59 if (*fdp != NULL && fn != NULL)
62 /* XXX never reached */
67 * Copy header+payload, calculating digest(s) on the fly.
69 static int copyFile(FD_t *sfdp, const char *sfnp,
70 FD_t *tfdp, const char *tfnp)
72 unsigned char buf[BUFSIZ];
76 if (manageFile(sfdp, sfnp, O_RDONLY))
78 if (manageFile(tfdp, tfnp, O_WRONLY|O_CREAT|O_TRUNC))
81 while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), *sfdp)) > 0)
83 if (Fwrite(buf, sizeof(buf[0]), count, *tfdp) != count) {
84 rpmlog(RPMLOG_ERR, _("%s: Fwrite failed: %s\n"), tfnp,
90 rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), sfnp, Fstrerror(*sfdp));
93 if (Fflush(*tfdp) != 0) {
94 rpmlog(RPMLOG_ERR, _("%s: Fflush failed: %s\n"), tfnp,
101 if (*sfdp) (void) closeFile(sfdp);
102 if (*tfdp) (void) closeFile(tfdp);
107 * Retrieve signer fingerprint from an OpenPGP signature tag.
108 * @param sig signature header
109 * @param sigtag signature tag
110 * @retval signid signer fingerprint
111 * @return 0 on success
113 static int getSignid(Header sig, rpmSigTag sigtag, pgpKeyID_t signid)
118 if (headerGet(sig, sigtag, &pkt, HEADERGET_DEFAULT) && pkt.data != NULL) {
119 pgpDig dig = pgpNewDig();
121 if (!pgpPrtPkts(pkt.data, pkt.count, dig, 0)) {
122 memcpy(signid, dig->signature.signid, sizeof(dig->signature.signid));
126 dig = pgpFreeDig(dig);
133 * Create/modify elements in signature header.
134 * @param ts transaction set
135 * @param qva mode flags and parameters
136 * @param argv array of package file names (NULL terminated)
137 * @return 0 on success, -1 on error
139 static int rpmReSign(rpmts ts, QVA_t qva, ARGV_const_t argv)
146 char *sigtarget = NULL, *trpm = NULL;
149 int res = -1; /* assume failure */
150 int deleting = (qva->qva_mode == RPMSIGN_DEL_SIGNATURE);
154 while ((rpm = *argv++) != NULL)
159 fprintf(stdout, "%s:\n", rpm);
161 if (manageFile(&fd, rpm, O_RDONLY))
166 if ((rc = rpmLeadRead(fd, lead)) == RPMRC_OK) {
167 const char *lmsg = NULL;
168 rc = rpmLeadCheck(lead, &lmsg);
170 rpmlog(RPMLOG_ERR, "%s: %s\n", rpm, lmsg);
173 if (rc != RPMRC_OK) {
174 lead = rpmLeadFree(lead);
179 rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg);
182 rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), rpm,
183 (msg && *msg ? msg : "\n"));
189 rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), rpm);
196 /* ASSERT: ofd == NULL && sigtarget == NULL */
197 ofd = rpmMkTempFile(NULL, &sigtarget);
198 if (ofd == NULL || Ferror(ofd)) {
199 rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n"));
202 /* Write the header and archive to a temp file */
203 if (copyFile(&fd, rpm, &ofd, sigtarget))
205 /* Both fd and ofd are now closed. sigtarget contains tempfile name. */
206 /* ASSERT: fd == NULL && ofd == NULL */
208 /* Dump the immutable region (if present). */
209 if (headerGet(sigh, RPMTAG_HEADERSIGNATURES, &utd, HEADERGET_DEFAULT)) {
211 struct rpmtd_s copytd;
221 oh = headerCopyLoad(utd.data);
222 hi = headerInitIterator(oh);
223 for (hi = headerInitIterator(oh);
224 headerNext(hi, ©td);
225 rpmtdFreeData(©td))
228 xx = headerPut(nh, ©td, HEADERPUT_DEFAULT);
230 hi = headerFreeIterator(hi);
233 sigh = headerFree(sigh);
234 sigh = headerLink(nh);
238 /* Eliminate broken digest values. */
239 xx = headerDel(sigh, RPMSIGTAG_BADSHA1_1);
240 xx = headerDel(sigh, RPMSIGTAG_BADSHA1_2);
242 /* Toss and recalculate header+payload size and digests. */
244 rpmSigTag const sigs[] = { RPMSIGTAG_SIZE,
248 int nsigs = sizeof(sigs) / sizeof(rpmSigTag);
249 for (int i = 0; i < nsigs; i++) {
250 (void) headerDel(sigh, sigs[i]);
251 if (rpmAddSignature(sigh, sigtarget, sigs[i], qva->passPhrase))
256 if (deleting) { /* Nuke all the signature tags. */
257 xx = headerDel(sigh, RPMSIGTAG_GPG);
258 xx = headerDel(sigh, RPMSIGTAG_DSA);
259 xx = headerDel(sigh, RPMSIGTAG_PGP5);
260 xx = headerDel(sigh, RPMSIGTAG_PGP);
261 xx = headerDel(sigh, RPMSIGTAG_RSA);
262 } else /* If gpg/pgp is configured, replace the signature. */
263 if ((sigtag = rpmLookupSignatureType(RPMLOOKUPSIG_QUERY)) > 0) {
264 pgpKeyID_t oldsignid, newsignid;
266 /* Grab the old signature fingerprint (if any) */
267 memset(oldsignid, 0, sizeof(oldsignid));
268 xx = getSignid(sigh, sigtag, oldsignid);
272 xx = headerDel(sigh, RPMSIGTAG_GPG);
275 xx = headerDel(sigh, RPMSIGTAG_PGP);
278 xx = headerDel(sigh, RPMSIGTAG_DSA);
281 xx = headerDel(sigh, RPMSIGTAG_RSA);
287 xx = headerDel(sigh, sigtag);
288 if (rpmAddSignature(sigh, sigtarget, sigtag, qva->passPhrase)) {
292 /* If package was previously signed, check for same signer. */
293 memset(newsignid, 0, sizeof(newsignid));
294 if (memcmp(oldsignid, newsignid, sizeof(oldsignid))) {
296 /* Grab the new signature fingerprint */
297 xx = getSignid(sigh, sigtag, newsignid);
299 /* If same signer, skip resigning the package. */
300 if (!memcmp(oldsignid, newsignid, sizeof(oldsignid))) {
302 char *signid = pgpHexStr(newsignid+4, sizeof(newsignid)-4);
303 rpmlog(RPMLOG_WARNING,
304 _("%s: was already signed by key ID %s, skipping\n"),
308 /* Clean up intermediate target */
309 xx = unlink(sigtarget);
310 sigtarget = _free(sigtarget);
316 /* Reallocate the signature into one contiguous region. */
317 sigh = headerReload(sigh, RPMTAG_HEADERSIGNATURES);
318 if (sigh == NULL) /* XXX can't happen */
321 rasprintf(&trpm, "%s.XXXXXX", rpm);
322 ofd = rpmMkTemp(trpm);
323 if (ofd == NULL || Ferror(ofd)) {
324 rpmlog(RPMLOG_ERR, _("rpmMkTemp failed\n"));
328 /* Write the lead/signature of the output rpm */
329 rc = rpmLeadWrite(ofd, lead);
330 lead = rpmLeadFree(lead);
331 if (rc != RPMRC_OK) {
332 rpmlog(RPMLOG_ERR, _("%s: writeLead failed: %s\n"), trpm,
337 if (rpmWriteSignature(ofd, sigh)) {
338 rpmlog(RPMLOG_ERR, _("%s: rpmWriteSignature failed: %s\n"), trpm,
343 /* Append the header and archive from the temp file */
344 /* ASSERT: fd == NULL && ofd != NULL */
345 if (copyFile(&fd, sigtarget, &ofd, trpm))
347 /* Both fd and ofd are now closed. */
348 /* ASSERT: fd == NULL && ofd == NULL */
350 /* Move final target into place, restore file permissions. */
355 xx = rename(trpm, rpm);
356 xx = chmod(rpm, st.st_mode);
360 /* Clean up intermediate target */
361 xx = unlink(sigtarget);
362 sigtarget = _free(sigtarget);
368 if (fd) (void) closeFile(&fd);
369 if (ofd) (void) closeFile(&ofd);
371 sigh = rpmFreeSignature(sigh);
374 xx = unlink(sigtarget);
375 sigtarget = _free(sigtarget);
386 * Import public key(s).
387 * @todo Implicit --update policy for gpg-pubkey headers.
388 * @param ts transaction set
389 * @param qva mode flags and parameters
390 * @param argv array of pubkey file names (NULL terminated)
391 * @return 0 on success
393 static int rpmcliImportPubkeys(const rpmts ts, QVA_t qva, ARGV_const_t argv)
396 unsigned char * pkt = NULL;
403 if (argv == NULL) return res;
405 while ((fn = *argv++) != NULL) {
411 /* If arg looks like a keyid, then attempt keyserver retrieve. */
412 if (fn[0] == '0' && fn[1] == 'x') {
415 for (i = 0, s = fn+2; *s && isxdigit(*s); s++, i++)
417 if (i == 8 || i == 16) {
418 t = rpmExpand("%{_hkp_keyserver_query}", fn+2, NULL);
424 /* Read pgp packet. */
425 if ((rc = pgpReadPkts(fn, &pkt, &pktlen)) <= 0) {
426 rpmlog(RPMLOG_ERR, _("%s: import read failed(%d).\n"), fn, rc);
430 if (rc != PGPARMOR_PUBKEY) {
431 rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn);
436 /* Import pubkey packet(s). */
437 if ((rpmrc = rpmtsImportPubkey(ts, pkt, pktlen)) != RPMRC_OK) {
438 rpmlog(RPMLOG_ERR, _("%s: import failed.\n"), fn);
452 * @todo If the GPG key was known available, the md5 digest could be skipped.
454 static int readFile(FD_t fd, const char * fn, pgpDig dig,
455 rpmDigestBundle plbundle, rpmDigestBundle hdrbundle)
457 unsigned char buf[4*BUFSIZ];
462 /* Read the header from the package. */
463 if ((h = headerRead(fd, HEADER_MAGIC_YES)) == NULL) {
464 rpmlog(RPMLOG_ERR, _("%s: headerRead failed\n"), fn);
468 if (headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) {
471 if (!headerGet(h, RPMTAG_HEADERIMMUTABLE, &utd, HEADERGET_DEFAULT)){
473 _("%s: Immutable header region could not be read. "
474 "Corrupted package?\n"), fn);
477 rpmDigestBundleUpdate(hdrbundle, rpm_header_magic, sizeof(rpm_header_magic));
478 rpmDigestBundleUpdate(hdrbundle, utd.data, utd.count);
482 /* Read the payload from the package. */
483 while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0) {}
485 rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), fn, Fstrerror(fd));
496 /* Parse the parameters from the OpenPGP packets that will be needed. */
497 /* XXX TODO: unify with similar parsePGP() in package.c */
498 static rpmRC parsePGP(rpmtd sigtd, const char *fn, pgpDig dig)
500 rpmRC rc = RPMRC_FAIL;
501 int debug = (_print_pkts & rpmIsDebug());
502 if ((pgpPrtPkts(sigtd->data, sigtd->count, dig, debug) == 0) &&
503 (dig->signature.version == 3 || dig->signature.version == 4)) {
507 _("skipping package %s with unverifiable V%u signature\n"), fn,
508 dig->signature.version);
514 * Figure best available signature.
515 * XXX TODO: Similar detection in rpmReadPackageFile(), unify these.
517 static rpmSigTag bestSig(Header sigh, int nosignatures, int nodigests)
519 rpmSigTag sigtag = 0;
520 if (sigtag == 0 && !nosignatures) {
521 if (headerIsEntry(sigh, RPMSIGTAG_DSA))
522 sigtag = RPMSIGTAG_DSA;
523 else if (headerIsEntry(sigh, RPMSIGTAG_RSA))
524 sigtag = RPMSIGTAG_RSA;
525 else if (headerIsEntry(sigh, RPMSIGTAG_GPG))
526 sigtag = RPMSIGTAG_GPG;
527 else if (headerIsEntry(sigh, RPMSIGTAG_PGP))
528 sigtag = RPMSIGTAG_PGP;
530 if (sigtag == 0 && !nodigests) {
531 if (headerIsEntry(sigh, RPMSIGTAG_MD5))
532 sigtag = RPMSIGTAG_MD5;
533 else if (headerIsEntry(sigh, RPMSIGTAG_SHA1))
534 sigtag = RPMSIGTAG_SHA1; /* XXX never happens */
539 static const char *sigtagname(rpmSigTag sigtag, int upper)
541 const char *n = NULL;
545 n = (upper ? "SIZE" : "size");
548 n = (upper ? "SHA1" : "sha1");
551 n = (upper ? "MD5" : "md5");
554 n = (upper ? "RSA" : "rsa");
556 case RPMSIGTAG_PGP5: /* XXX legacy */
558 n = (upper ? "(MD5) PGP" : "(md5) pgp");
561 n = (upper ? "(SHA1) DSA" : "(sha1) dsa");
564 n = (upper ? "GPG" : "gpg");
567 n = (upper ? "?UnknownSigatureType?" : "???");
574 * Format sigcheck result for output, appending the message spew to buf and
575 * bad/missing keyids to keyprob.
577 * In verbose mode, just dump it all. Otherwise ok signatures
578 * are dumped lowercase, bad sigs uppercase and for PGP/GPG
579 * if misssing/untrusted key it's uppercase in parenthesis
580 * and stash the key id as <SIGTYPE>#<keyid>. Pfft.
582 static void formatResult(rpmSigTag sigtag, rpmRC sigres, const char *result,
583 int havekey, char **keyprob, char **buf)
586 if (rpmIsVerbose()) {
587 rasprintf(&msg, " %s", result);
589 /* Check for missing / untrusted keys in result. */
590 const char *signame = sigtagname(sigtag, (sigres != RPMRC_OK));
592 if (havekey && (sigres == RPMRC_NOKEY || sigres == RPMRC_NOTTRUSTED)) {
593 const char *tempKey = strstr(result, "ey ID");
595 char *keyid = strndup(tempKey + 6, 8);
597 rasprintf(&idprob, " %s#%s", signame, keyid);
598 rstrcat(keyprob, idprob);
603 rasprintf(&msg, (*keyprob ? "(%s) " : "%s "), signame);
609 static int rpmpkgVerifySigs(rpmKeyring keyring, rpmQueryFlags flags,
610 FD_t fd, const char *fn)
614 char *missingKeys = NULL;
615 char *untrustedKeys = NULL;
616 struct rpmtd_s sigtd;
621 HeaderIterator hi = NULL;
623 int res = 1; /* assume failure */
627 int nodigests = !(flags & VERIFY_DIGEST);
628 int nosignatures = !(flags & VERIFY_SIGNATURE);
629 rpmDigestBundle plbundle = rpmDigestBundleNew();
630 rpmDigestBundle hdrbundle = rpmDigestBundleNew();
632 rpmlead lead = rpmLeadNew();
633 if ((rc = rpmLeadRead(fd, lead)) == RPMRC_OK) {
634 const char *lmsg = NULL;
635 rc = rpmLeadCheck(lead, &lmsg);
637 rpmlog(RPMLOG_ERR, "%s: %s\n", fn, lmsg);
639 lead = rpmLeadFree(lead);
641 if (rc != RPMRC_OK) {
645 rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg);
648 rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), fn,
649 (msg && *msg ? msg : "\n"));
655 rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), fn);
662 /* Grab a hint of what needs doing to avoid duplication. */
663 sigtag = bestSig(sigh, nosignatures, nodigests);
666 sigp = &dig->signature;
668 /* XXX RSA needs the hash_algo, so decode early. */
669 if (sigtag == RPMSIGTAG_RSA || sigtag == RPMSIGTAG_PGP ||
670 sigtag == RPMSIGTAG_DSA || sigtag == RPMSIGTAG_GPG) {
671 xx = headerGet(sigh, sigtag, &sigtd, HEADERGET_DEFAULT);
672 xx = pgpPrtPkts(sigtd.data, sigtd.count, dig, 0);
673 rpmtdFreeData(&sigtd);
674 /* XXX assume same hash_algo in header-only and header+payload */
675 rpmDigestBundleAdd(plbundle, sigp->hash_algo, RPMDIGEST_NONE);
676 rpmDigestBundleAdd(hdrbundle, sigp->hash_algo, RPMDIGEST_NONE);
679 if (headerIsEntry(sigh, RPMSIGTAG_PGP) ||
680 headerIsEntry(sigh, RPMSIGTAG_PGP5) ||
681 headerIsEntry(sigh, RPMSIGTAG_MD5)) {
682 rpmDigestBundleAdd(plbundle, PGPHASHALGO_MD5, RPMDIGEST_NONE);
684 if (headerIsEntry(sigh, RPMSIGTAG_GPG)) {
685 rpmDigestBundleAdd(plbundle, PGPHASHALGO_SHA1, RPMDIGEST_NONE);
688 /* always do sha1 hash of header */
689 rpmDigestBundleAdd(hdrbundle, PGPHASHALGO_SHA1, RPMDIGEST_NONE);
691 /* Read the file, generating digest(s) on the fly. */
692 fdSetBundle(fd, plbundle);
693 if (readFile(fd, fn, dig, plbundle, hdrbundle)) {
697 rasprintf(&buf, "%s:%c", fn, (rpmIsVerbose() ? '\n' : ' ') );
699 hi = headerInitIterator(sigh);
700 for (; headerNext(hi, &sigtd) != 0; rpmtdFreeData(&sigtd)) {
703 DIGEST_CTX ctx = NULL;
704 if (sigtd.data == NULL) /* XXX can't happen */
707 /* Clean up parameters from previous sigtag. */
712 case RPMSIGTAG_PGP5: /* XXX legacy */
719 if (parsePGP(&sigtd, fn, dig) != RPMRC_OK) {
722 ctx = rpmDigestBundleDupCtx(havekey ? plbundle : hdrbundle,
723 dig->signature.hash_algo);
728 ctx = rpmDigestBundleDupCtx(hdrbundle, PGPHASHALGO_SHA1);
733 ctx = rpmDigestBundleDupCtx(plbundle, PGPHASHALGO_MD5);
740 rc = rpmVerifySignature(keyring, &sigtd, dig, ctx, &result);
741 rpmDigestFinal(ctx, NULL, NULL, 0);
743 formatResult(sigtd.tag, rc, result, havekey,
744 (rc == RPMRC_NOKEY ? &missingKeys : &untrustedKeys),
748 if (rc != RPMRC_OK) {
755 if (rpmIsVerbose()) {
756 rpmlog(RPMLOG_NOTICE, "%s", buf);
758 const char *ok = (failed ? _("NOT OK") : _("OK"));
759 rpmlog(RPMLOG_NOTICE, "%s%s%s%s%s%s%s%s\n", buf, ok,
760 missingKeys ? _(" (MISSING KEYS:") : "",
761 missingKeys ? missingKeys : "",
762 missingKeys ? _(") ") : "",
763 untrustedKeys ? _(" (UNTRUSTED KEYS:") : "",
764 untrustedKeys ? untrustedKeys : "",
765 untrustedKeys ? _(")") : "");
772 rpmDigestBundleFree(hdrbundle);
773 rpmDigestBundleFree(plbundle);
774 fdSetBundle(fd, NULL); /* XXX avoid double-free from fd close */
775 sigh = rpmFreeSignature(sigh);
776 hi = headerFreeIterator(hi);
781 /* Wrapper around rpmkVerifySigs to preserve API */
782 int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd, const char * fn)
784 int rc = 1; /* assume failure */
785 if (ts && qva && fd && fn) {
786 rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
787 rc = rpmpkgVerifySigs(keyring, qva->qva_flags, fd, fn);
788 rpmKeyringFree(keyring);
793 int rpmcliSign(rpmts ts, QVA_t qva, ARGV_const_t argv)
798 rpmKeyring keyring = NULL;
800 if (argv == NULL) return res;
802 switch (qva->qva_mode) {
803 case RPMSIGN_CHK_SIGNATURE:
805 case RPMSIGN_IMPORT_PUBKEY:
806 return rpmcliImportPubkeys(ts, qva, argv);
808 case RPMSIGN_NEW_SIGNATURE:
809 case RPMSIGN_ADD_SIGNATURE:
810 case RPMSIGN_DEL_SIGNATURE:
811 return rpmReSign(ts, qva, argv);
819 keyring = rpmtsGetKeyring(ts, 1);
820 while ((arg = *argv++) != NULL) {
823 fd = Fopen(arg, "r.ufdio");
824 if (fd == NULL || Ferror(fd)) {
825 rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"),
828 } else if (rpmpkgVerifySigs(keyring, qva->qva_flags, fd, arg)) {
832 if (fd != NULL) xx = Fclose(fd);
835 rpmKeyringFree(keyring);