2 * \file lib/rpmchecksig.c
3 * Verify the signature of a package.
10 #include <rpm/rpmlib.h> /* RPMSIGTAG & related */
11 #include <rpm/rpmpgp.h>
12 #include <rpm/rpmcli.h>
13 #include <rpm/rpmfileutil.h> /* rpmMkTemp() */
14 #include <rpm/rpmdb.h>
15 #include <rpm/rpmts.h>
16 #include <rpm/rpmlog.h>
17 #include <rpm/rpmstring.h>
18 #include <rpm/rpmkeyring.h>
20 #include "rpmio/rpmio_internal.h" /* fdSetBundle() */
21 #include "lib/rpmlead.h"
22 #include "lib/signature.h"
28 static int doImport(rpmts ts, const char *fn, char *buf, ssize_t blen)
30 char const * const pgpmark = "-----BEGIN PGP ";
31 size_t marklen = strlen(pgpmark);
34 char *start = strstr(buf, pgpmark);
40 /* Read pgp packet. */
41 if (pgpParsePkts(start, &pkt, &pktlen) == PGPARMOR_PUBKEY) {
42 /* Import pubkey packet(s). */
43 if (rpmtsImportPubkey(ts, pkt, pktlen) != RPMRC_OK) {
44 rpmlog(RPMLOG_ERR, _("%s: key %d import failed.\n"), fn, keyno);
48 rpmlog(RPMLOG_ERR, _("%s: key %d not an armored public key.\n"),
53 /* See if there are more keys in the buffer */
54 if (start && start + marklen < buf + blen) {
55 start = strstr(start + marklen, pgpmark);
62 } while (start != NULL);
67 int rpmcliImportPubkeys(rpmts ts, ARGV_const_t argv)
70 for (ARGV_const_t arg = argv; arg && *arg; arg++) {
71 const char *fn = *arg;
77 /* If arg looks like a keyid, then attempt keyserver retrieve. */
78 if (rstreqn(fn, "0x", 2)) {
79 const char * s = fn + 2;
81 for (i = 0; *s && isxdigit(*s); s++, i++)
83 if (i == 8 || i == 16) {
84 t = rpmExpand("%{_hkp_keyserver_query}", fn+2, NULL);
90 /* Read the file and try to import all contained keys */
91 iorc = rpmioSlurp(fn, &buf, &blen);
92 if (iorc || buf == NULL || blen < 64) {
93 rpmlog(RPMLOG_ERR, _("%s: import read failed(%d).\n"), fn, iorc);
96 res += doImport(ts, fn, (char *)buf, blen);
106 * @todo If the GPG key was known available, the md5 digest could be skipped.
108 static int readFile(FD_t fd, const char * fn,
109 rpmDigestBundle plbundle, rpmDigestBundle hdrbundle)
111 unsigned char buf[4*BUFSIZ];
117 /* Read the header from the package. */
118 if (rpmReadHeader(NULL, fd, &h, &msg) != RPMRC_OK) {
119 rpmlog(RPMLOG_ERR, _("%s: headerRead failed: %s\n"), fn, msg);
123 if (headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) {
126 if (!headerGet(h, RPMTAG_HEADERIMMUTABLE, &utd, HEADERGET_DEFAULT)){
128 _("%s: Immutable header region could not be read. "
129 "Corrupted package?\n"), fn);
132 rpmDigestBundleUpdate(hdrbundle, rpm_header_magic, sizeof(rpm_header_magic));
133 rpmDigestBundleUpdate(hdrbundle, utd.data, utd.count);
137 /* Read the payload from the package. */
138 while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0) {}
140 rpmlog(RPMLOG_ERR, _("%s: Fread failed: %s\n"), fn, Fstrerror(fd));
153 * Figure best available signature.
154 * XXX TODO: Similar detection in rpmReadPackageFile(), unify these.
156 static rpmTagVal bestSig(Header sigh, int nosignatures, int nodigests)
158 rpmTagVal sigtag = 0;
159 if (sigtag == 0 && !nosignatures) {
160 if (headerIsEntry(sigh, RPMSIGTAG_DSA))
161 sigtag = RPMSIGTAG_DSA;
162 else if (headerIsEntry(sigh, RPMSIGTAG_RSA))
163 sigtag = RPMSIGTAG_RSA;
164 else if (headerIsEntry(sigh, RPMSIGTAG_GPG))
165 sigtag = RPMSIGTAG_GPG;
166 else if (headerIsEntry(sigh, RPMSIGTAG_PGP))
167 sigtag = RPMSIGTAG_PGP;
169 if (sigtag == 0 && !nodigests) {
170 if (headerIsEntry(sigh, RPMSIGTAG_MD5))
171 sigtag = RPMSIGTAG_MD5;
172 else if (headerIsEntry(sigh, RPMSIGTAG_SHA1))
173 sigtag = RPMSIGTAG_SHA1; /* XXX never happens */
178 static const char *sigtagname(rpmTagVal sigtag, int upper)
180 const char *n = NULL;
184 n = (upper ? "SIZE" : "size");
187 n = (upper ? "SHA1" : "sha1");
190 n = (upper ? "MD5" : "md5");
193 n = (upper ? "RSA" : "rsa");
195 case RPMSIGTAG_PGP5: /* XXX legacy */
197 n = (upper ? "(MD5) PGP" : "(md5) pgp");
200 n = (upper ? "(SHA1) DSA" : "(sha1) dsa");
203 n = (upper ? "GPG" : "gpg");
206 n = (upper ? "?UnknownSigatureType?" : "???");
213 * Format sigcheck result for output, appending the message spew to buf and
214 * bad/missing keyids to keyprob.
216 * In verbose mode, just dump it all. Otherwise ok signatures
217 * are dumped lowercase, bad sigs uppercase and for PGP/GPG
218 * if misssing/untrusted key it's uppercase in parenthesis
219 * and stash the key id as <SIGTYPE>#<keyid>. Pfft.
221 static void formatResult(rpmTagVal sigtag, rpmRC sigres, const char *result,
222 int havekey, char **keyprob, char **buf)
225 if (rpmIsVerbose()) {
226 rasprintf(&msg, " %s", result);
228 /* Check for missing / untrusted keys in result. */
229 const char *signame = sigtagname(sigtag, (sigres != RPMRC_OK));
231 if (havekey && (sigres == RPMRC_NOKEY || sigres == RPMRC_NOTTRUSTED)) {
232 const char *tempKey = strstr(result, "ey ID");
234 char keyid[sizeof(pgpKeyID_t) + 1];
235 rstrlcpy(keyid, tempKey + 6, sizeof(keyid));
236 rstrscat(keyprob, " ", signame, "#", keyid, NULL);
239 rasprintf(&msg, (*keyprob ? "(%s) " : "%s "), signame);
245 static int rpmpkgVerifySigs(rpmKeyring keyring, rpmQueryFlags flags,
246 FD_t fd, const char *fn)
250 char *missingKeys = NULL;
251 char *untrustedKeys = NULL;
252 struct rpmtd_s sigtd;
254 pgpDigParams sig = NULL;
256 HeaderIterator hi = NULL;
258 int res = 1; /* assume failure */
261 int nodigests = !(flags & VERIFY_DIGEST);
262 int nosignatures = !(flags & VERIFY_SIGNATURE);
263 rpmDigestBundle plbundle = rpmDigestBundleNew();
264 rpmDigestBundle hdrbundle = rpmDigestBundleNew();
266 if ((rc = rpmLeadRead(fd, NULL, NULL, &msg)) != RPMRC_OK) {
267 rpmlog(RPMLOG_ERR, "%s: %s\n", fn, msg);
272 rc = rpmReadSignature(fd, &sigh, RPMSIGTYPE_HEADERSIG, &msg);
275 rpmlog(RPMLOG_ERR, _("%s: rpmReadSignature failed: %s"), fn,
276 (msg && *msg ? msg : "\n"));
282 rpmlog(RPMLOG_ERR, _("%s: No signature available\n"), fn);
289 /* Grab a hint of what needs doing to avoid duplication. */
290 sigtag = bestSig(sigh, nosignatures, nodigests);
292 /* XXX RSA needs the hash_algo, so decode early. */
293 if (sigtag == RPMSIGTAG_RSA || sigtag == RPMSIGTAG_PGP ||
294 sigtag == RPMSIGTAG_DSA || sigtag == RPMSIGTAG_GPG) {
295 unsigned int hashalgo;
296 if (headerGet(sigh, sigtag, &sigtd, HEADERGET_DEFAULT)) {
297 parsePGPSig(&sigtd, "package", fn, &sig);
298 rpmtdFreeData(&sigtd);
300 if (sig == NULL) goto exit;
302 /* XXX assume same hash_algo in header-only and header+payload */
303 hashalgo = pgpDigParamsAlgo(sig, PGPVAL_HASHALGO);
304 rpmDigestBundleAdd(plbundle, hashalgo, RPMDIGEST_NONE);
305 rpmDigestBundleAdd(hdrbundle, hashalgo, RPMDIGEST_NONE);
308 if (headerIsEntry(sigh, RPMSIGTAG_PGP) ||
309 headerIsEntry(sigh, RPMSIGTAG_PGP5) ||
310 headerIsEntry(sigh, RPMSIGTAG_MD5)) {
311 rpmDigestBundleAdd(plbundle, PGPHASHALGO_MD5, RPMDIGEST_NONE);
313 if (headerIsEntry(sigh, RPMSIGTAG_GPG)) {
314 rpmDigestBundleAdd(plbundle, PGPHASHALGO_SHA1, RPMDIGEST_NONE);
317 /* always do sha1 hash of header */
318 rpmDigestBundleAdd(hdrbundle, PGPHASHALGO_SHA1, RPMDIGEST_NONE);
320 /* Read the file, generating digest(s) on the fly. */
321 fdSetBundle(fd, plbundle);
322 if (readFile(fd, fn, plbundle, hdrbundle)) {
326 rasprintf(&buf, "%s:%c", fn, (rpmIsVerbose() ? '\n' : ' ') );
328 hi = headerInitIterator(sigh);
329 for (; headerNext(hi, &sigtd) != 0; rpmtdFreeData(&sigtd)) {
332 DIGEST_CTX ctx = NULL;
333 if (sigtd.data == NULL) /* XXX can't happen */
336 /* Clean up parameters from previous sigtag. */
337 sig = pgpDigParamsFree(sig);
341 case RPMSIGTAG_PGP5: /* XXX legacy */
348 if (parsePGPSig(&sigtd, "package", fn, &sig))
350 ctx = rpmDigestBundleDupCtx(havekey ? plbundle : hdrbundle,
351 pgpDigParamsAlgo(sig, PGPVAL_HASHALGO));
356 ctx = rpmDigestBundleDupCtx(hdrbundle, PGPHASHALGO_SHA1);
361 ctx = rpmDigestBundleDupCtx(plbundle, PGPHASHALGO_MD5);
368 rc = rpmVerifySignature(keyring, &sigtd, sig, ctx, &result);
369 rpmDigestFinal(ctx, NULL, NULL, 0);
371 formatResult(sigtd.tag, rc, result, havekey,
372 (rc == RPMRC_NOKEY ? &missingKeys : &untrustedKeys),
376 if (rc != RPMRC_OK) {
383 if (rpmIsVerbose()) {
384 rpmlog(RPMLOG_NOTICE, "%s", buf);
386 const char *ok = (failed ? _("NOT OK") : _("OK"));
387 rpmlog(RPMLOG_NOTICE, "%s%s%s%s%s%s%s%s\n", buf, ok,
388 missingKeys ? _(" (MISSING KEYS:") : "",
389 missingKeys ? missingKeys : "",
390 missingKeys ? _(") ") : "",
391 untrustedKeys ? _(" (UNTRUSTED KEYS:") : "",
392 untrustedKeys ? untrustedKeys : "",
393 untrustedKeys ? _(")") : "");
400 rpmDigestBundleFree(hdrbundle);
401 rpmDigestBundleFree(plbundle);
402 fdSetBundle(fd, NULL); /* XXX avoid double-free from fd close */
403 sigh = rpmFreeSignature(sigh);
404 hi = headerFreeIterator(hi);
405 pgpDigParamsFree(sig);
409 /* Wrapper around rpmkVerifySigs to preserve API */
410 int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd, const char * fn)
412 int rc = 1; /* assume failure */
413 if (ts && qva && fd && fn) {
414 rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
415 rc = rpmpkgVerifySigs(keyring, qva->qva_flags, fd, fn);
416 rpmKeyringFree(keyring);
421 int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv)
425 rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
426 rpmVerifyFlags verifyFlags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
428 verifyFlags &= ~rpmcliQueryFlags;
430 while ((arg = *argv++) != NULL) {
431 FD_t fd = Fopen(arg, "r.ufdio");
432 if (fd == NULL || Ferror(fd)) {
433 rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"),
436 } else if (rpmpkgVerifySigs(keyring, verifyFlags, fd, arg)) {
443 rpmKeyringFree(keyring);