summaryrefslogtreecommitdiff
path: root/lib/rpmvs.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rpmvs.c')
-rw-r--r--lib/rpmvs.c456
1 files changed, 456 insertions, 0 deletions
diff --git a/lib/rpmvs.c b/lib/rpmvs.c
new file mode 100644
index 000000000..f77bfb02d
--- /dev/null
+++ b/lib/rpmvs.c
@@ -0,0 +1,456 @@
+#include "system.h"
+
+#include <rpm/rpmkeyring.h>
+#include "lib/rpmvs.h"
+#include "rpmio/digest.h"
+
+#include "debug.h"
+
+struct rpmvs_s {
+ struct rpmsinfo_s *sigs;
+ rpmRC *rcs;
+ char **results;
+ int nsigs;
+ int nalloced;
+};
+
+struct vfytag_s {
+ rpmTagVal tag;
+ rpmTagType tagtype;
+ rpm_count_t tagcount;
+ rpm_count_t tagsize;
+};
+
+static const struct vfytag_s rpmvfytags[] = {
+ { RPMSIGTAG_SIZE, RPM_BIN_TYPE, 0, 0, },
+ { RPMSIGTAG_PGP, RPM_BIN_TYPE, 0, 0, },
+ { RPMSIGTAG_MD5, RPM_BIN_TYPE, 0, 16, },
+ { RPMSIGTAG_GPG, RPM_BIN_TYPE, 0, 0, },
+ { RPMSIGTAG_PGP5, RPM_BIN_TYPE, 0, 0, },
+ { RPMSIGTAG_PAYLOADSIZE, RPM_INT32_TYPE, 1, 4, },
+ { RPMSIGTAG_RESERVEDSPACE, RPM_BIN_TYPE, 0, 0, },
+ { RPMTAG_DSAHEADER, RPM_BIN_TYPE, 0, 0, },
+ { RPMTAG_RSAHEADER, RPM_BIN_TYPE, 0, 0, },
+ { RPMTAG_SHA1HEADER, RPM_STRING_TYPE, 1, 41, },
+ { RPMSIGTAG_LONGSIZE, RPM_INT64_TYPE, 1, 8, },
+ { RPMSIGTAG_LONGARCHIVESIZE, RPM_INT64_TYPE, 1, 8, },
+ { RPMTAG_SHA256HEADER, RPM_STRING_TYPE, 1, 65, },
+ { RPMTAG_PAYLOADDIGEST, RPM_STRING_ARRAY_TYPE, 0, 0, },
+ { 0 } /* sentinel */
+};
+
+struct vfyinfo_s {
+ rpmTagVal tag;
+ struct rpmsinfo_s vi;
+};
+
+static const struct vfyinfo_s rpmvfyitems[] = {
+ { RPMSIGTAG_SIZE,
+ { RPMSIG_OTHER_TYPE, 0,
+ (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, },
+ { RPMSIGTAG_PGP,
+ { RPMSIG_SIGNATURE_TYPE, RPMVSF_NORSA,
+ (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, },
+ { RPMSIGTAG_MD5,
+ { RPMSIG_DIGEST_TYPE, RPMVSF_NOMD5,
+ (RPMSIG_HEADER|RPMSIG_PAYLOAD), PGPHASHALGO_MD5, }, },
+ { RPMSIGTAG_GPG,
+ { RPMSIG_SIGNATURE_TYPE, RPMVSF_NODSA,
+ (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, },
+ { RPMSIGTAG_PGP5,
+ { RPMSIG_SIGNATURE_TYPE, RPMVSF_NORSA,
+ (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, },
+ { RPMSIGTAG_PAYLOADSIZE,
+ { RPMSIG_OTHER_TYPE, 0,
+ (RPMSIG_PAYLOAD), 0, }, },
+ { RPMSIGTAG_RESERVEDSPACE,
+ { RPMSIG_OTHER_TYPE, 0,
+ 0, 0, }, },
+ { RPMTAG_DSAHEADER,
+ { RPMSIG_SIGNATURE_TYPE, RPMVSF_NODSAHEADER,
+ (RPMSIG_HEADER), 0, }, },
+ { RPMTAG_RSAHEADER,
+ { RPMSIG_SIGNATURE_TYPE, RPMVSF_NORSAHEADER,
+ (RPMSIG_HEADER), 0, }, },
+ { RPMTAG_SHA1HEADER,
+ { RPMSIG_DIGEST_TYPE, RPMVSF_NOSHA1HEADER,
+ (RPMSIG_HEADER), PGPHASHALGO_SHA1, }, },
+ { RPMSIGTAG_LONGSIZE,
+ { RPMSIG_OTHER_TYPE, 0,
+ (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, },
+ { RPMSIGTAG_LONGARCHIVESIZE,
+ { RPMSIG_OTHER_TYPE, 0,
+ (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, },
+ { RPMTAG_SHA256HEADER,
+ { RPMSIG_DIGEST_TYPE, RPMVSF_NOSHA256HEADER,
+ (RPMSIG_HEADER), PGPHASHALGO_SHA256, }, },
+ { RPMTAG_PAYLOADDIGEST,
+ { RPMSIG_DIGEST_TYPE, RPMVSF_NOPAYLOAD,
+ (RPMSIG_PAYLOAD), PGPHASHALGO_SHA256, }, },
+ { 0 } /* sentinel */
+};
+
+static const char *rangeName(int range);
+static const char * rpmSigString(rpmRC res);
+static rpmRC rpmVerifySignature(rpmKeyring keyring, struct rpmsinfo_s *sinfo,
+ DIGEST_CTX ctx, char ** result);
+
+static int sinfoLookup(rpmTagVal tag)
+{
+ const struct vfyinfo_s *start = &rpmvfyitems[0];
+ int ix = -1;
+ for (const struct vfyinfo_s *si = start; si->tag; si++) {
+ if (tag == si->tag) {
+ ix = si - start;
+ break;
+ }
+ }
+ return ix;
+}
+
+static int validHex(const char *str, size_t slen)
+{
+ int valid = 0; /* Assume invalid */
+ const char *b;
+
+ /* Our hex data is always even sized and at least sha-1 long */
+ if (slen % 2 || slen < 40)
+ goto exit;
+
+ for (b = str ; *b != '\0'; b++) {
+ if (strchr("0123456789abcdefABCDEF", *b) == NULL)
+ goto exit;
+ }
+ valid = 1;
+
+exit:
+ return valid;
+}
+
+static rpmRC rpmsinfoInit(rpmtd td, const char *origin,
+ struct rpmsinfo_s *sinfo, char **msg)
+{
+ rpmRC rc = RPMRC_FAIL;
+ const void *data = NULL;
+ const struct vfytag_s *tinfo;
+ const struct vfyinfo_s *vinfo;
+ rpm_count_t dlen = 0;
+ int ix;
+
+ if ((ix = sinfoLookup(td->tag)) == -1) {
+ /* anything unknown just falls through for now */
+ rc = RPMRC_OK;
+ goto exit;
+ }
+ vinfo = &rpmvfyitems[ix];
+ tinfo = &rpmvfytags[ix];
+ assert(tinfo->tag == vinfo->tag);
+
+ *sinfo = rpmvfyitems[ix].vi; /* struct assignment */
+
+ if (tinfo->tagtype && tinfo->tagtype != td->type) {
+ rasprintf(msg, _("%s tag %u: invalid type %u"),
+ origin, td->tag, td->type);
+ goto exit;
+ }
+
+ if (tinfo->tagcount && tinfo->tagcount != td->count) {
+ rasprintf(msg, _("%s: tag %u: invalid count %u"),
+ origin, td->tag, td->count);
+ goto exit;
+ }
+
+ switch (td->type) {
+ case RPM_STRING_TYPE:
+ case RPM_STRING_ARRAY_TYPE:
+ data = rpmtdGetString(td);
+ if (data)
+ dlen = strlen(data);
+ break;
+ case RPM_BIN_TYPE:
+ data = td->data;
+ dlen = td->count;
+ break;
+ }
+
+ /* MD5 has data length of 16, everything else is (much) larger */
+ if (sinfo->hashalgo && (data == NULL || dlen < 16)) {
+ rasprintf(msg, _("%s tag %u: invalid data %p (%u)"),
+ origin, td->tag, data, dlen);
+ goto exit;
+ }
+
+ if (td->type == RPM_STRING_TYPE && td->size == 0)
+ td->size = dlen + 1;
+
+ if (tinfo->tagsize && (td->flags & RPMTD_IMMUTABLE) &&
+ tinfo->tagsize != td->size) {
+ rasprintf(msg, _("%s tag %u: invalid size %u"),
+ origin, td->tag, td->size);
+ goto exit;
+ }
+
+ if (sinfo->type == RPMSIG_SIGNATURE_TYPE) {
+ if (pgpPrtParams(data, dlen, PGPTAG_SIGNATURE, &sinfo->sig)) {
+ rasprintf(msg, _("%s tag %u: invalid OpenPGP signature"),
+ origin, td->tag);
+ goto exit;
+ }
+ sinfo->hashalgo = pgpDigParamsAlgo(sinfo->sig, PGPVAL_HASHALGO);
+ sinfo->keyid = pgpGrab(sinfo->sig->signid+4, 4);
+ } else if (sinfo->type == RPMSIG_DIGEST_TYPE) {
+ if (td->type == RPM_BIN_TYPE) {
+ sinfo->dig = pgpHexStr(data, dlen);
+ } else {
+ if (!validHex(data, dlen)) {
+ rasprintf(msg, _("%s: tag %u: invalid hex"), origin, td->tag);
+ goto exit;
+ }
+ sinfo->dig = xstrdup(data);
+ }
+ }
+
+ if (sinfo->hashalgo)
+ sinfo->id = (td->tag << 16) | rpmtdGetIndex(td);
+
+ rc = RPMRC_OK;
+
+exit:
+ return rc;
+}
+
+static void rpmsinfoFini(struct rpmsinfo_s *sinfo)
+{
+ if (sinfo) {
+ if (sinfo->type == RPMSIG_SIGNATURE_TYPE)
+ pgpDigParamsFree(sinfo->sig);
+ else if (sinfo->type == RPMSIG_DIGEST_TYPE)
+ free(sinfo->dig);
+ free(sinfo->descr);
+ memset(sinfo, 0, sizeof(*sinfo));
+ }
+}
+
+static int rpmsinfoDisabled(const struct rpmsinfo_s *sinfo, rpmVSFlags vsflags)
+{
+ if (!(sinfo->type & RPMSIG_VERIFIABLE_TYPE))
+ return 1;
+ if (vsflags & sinfo->disabler)
+ return 1;
+ if ((vsflags & RPMVSF_NEEDPAYLOAD) && (sinfo->range & RPMSIG_PAYLOAD))
+ return 1;
+ return 0;
+}
+
+static void rpmvsReserve(struct rpmvs_s *vs, int n)
+{
+ if (vs->nsigs + n >= vs->nalloced) {
+ vs->nalloced = (vs->nsigs * 2) + n;
+ vs->rcs = xrealloc(vs->rcs, vs->nalloced * sizeof(*vs->rcs));
+ vs->results = xrealloc(vs->results, vs->nalloced * sizeof(*vs->results));
+ vs->sigs = xrealloc(vs->sigs, vs->nalloced * sizeof(*vs->sigs));
+ }
+}
+
+const char *rpmsinfoDescr(struct rpmsinfo_s *sinfo)
+{
+ if (sinfo->descr == NULL) {
+ char *t;
+ switch (sinfo->type) {
+ case RPMSIG_DIGEST_TYPE:
+ rasprintf(&sinfo->descr, _("%s%s %s"),
+ rangeName(sinfo->range),
+ pgpValString(PGPVAL_HASHALGO, sinfo->hashalgo),
+ _("digest"));
+ break;
+ case RPMSIG_SIGNATURE_TYPE:
+ t = sinfo->sig ? pgpIdentItem(sinfo->sig) : NULL;
+ rasprintf(&sinfo->descr, _("%s%s"),
+ rangeName(sinfo->range), t ? t : _("signature"));
+ free(t);
+ break;
+ }
+ }
+ return sinfo->descr;
+}
+
+char *rpmsinfoMsg(struct rpmsinfo_s *sinfo, rpmRC rc, const char *emsg)
+{
+ char *msg = NULL;
+ if (emsg) {
+ rasprintf(&msg, "%s: %s (%s)",
+ rpmsinfoDescr(sinfo), rpmSigString(rc), emsg);
+ } else {
+ rasprintf(&msg, "%s: %s", rpmsinfoDescr(sinfo), rpmSigString(rc));
+ }
+ return msg;
+}
+
+void rpmvsAppend(struct rpmvs_s *sis, hdrblob blob, rpmTagVal tag)
+{
+ struct rpmtd_s td;
+ rpmRC rc = hdrblobGet(blob, tag, &td);
+
+ if (rc == RPMRC_OK) {
+ const char *o = (blob->il > blob->ril) ? _("header") : _("package");
+ int ix;
+
+ rpmvsReserve(sis, rpmtdCount(&td));
+
+ while ((ix = rpmtdNext(&td)) >= 0) {
+ sis->results[sis->nsigs] = NULL;
+ sis->rcs[sis->nsigs] = rpmsinfoInit(&td, o,
+ &sis->sigs[sis->nsigs],
+ &sis->results[sis->nsigs]);
+ sis->nsigs++;
+ }
+ rpmtdFreeData(&td);
+ }
+}
+
+struct rpmvs_s *rpmvsCreate(hdrblob blob, rpmVSFlags vsflags)
+{
+ struct rpmvs_s *sis = xcalloc(1, sizeof(*sis));
+
+ rpmvsReserve(sis, 2); /* XXX bump this up later */
+
+ for (const struct vfyinfo_s *si = &rpmvfyitems[0]; si->tag; si++) {
+ if (rpmsinfoDisabled(&si->vi, vsflags))
+ continue;
+ rpmvsAppend(sis, blob, si->tag);
+ }
+ return sis;
+}
+
+struct rpmvs_s *rpmvsFree(struct rpmvs_s *sis)
+{
+ if (sis) {
+ free(sis->rcs);
+ for (int i = 0; i < sis->nsigs; i++) {
+ rpmsinfoFini(&sis->sigs[i]);
+ free(sis->results[i]);
+ }
+ free(sis->sigs);
+ free(sis->results);
+ free(sis);
+ }
+ return NULL;
+}
+
+void rpmvsInitDigests(struct rpmvs_s *sis, int range, rpmDigestBundle bundle)
+{
+ for (int i = 0; i < sis->nsigs; i++) {
+ struct rpmsinfo_s *sinfo = &sis->sigs[i];
+ if (sinfo->range & range) {
+ if (sis->rcs[i] == RPMRC_OK)
+ rpmDigestBundleAddID(bundle, sinfo->hashalgo, sinfo->id, 0);
+ }
+ }
+}
+
+int rpmvsVerifyItems(rpmPlugins plugins, struct rpmvs_s *sis, int range, rpmDigestBundle bundle,
+ rpmKeyring keyring, rpmsinfoCb cb, void *cbdata)
+{
+ int failed = 0;
+
+ for (int i = 0; i < sis->nsigs; i++) {
+ struct rpmsinfo_s *sinfo = &sis->sigs[i];
+
+ if (sinfo->range == range) {
+ if (sis->rcs[i] == RPMRC_OK) {
+ DIGEST_CTX ctx = rpmDigestBundleDupCtx(bundle, sinfo->id);
+ sis->results[i] = _free(sis->results[i]);
+ sis->rcs[i] = rpmVerifySignature(keyring, sinfo, ctx, &sis->results[i]);
+ /* Run verify hook for all plugins */
+ sis->rcs[i] = rpmpluginsCallVerify(plugins, keyring, sinfo->id, sinfo->sig, ctx, sis->rcs[i]);
+ rpmDigestFinal(ctx, NULL, NULL, 0);
+ rpmDigestBundleFinal(bundle, sinfo->id, NULL, NULL, 0);
+ }
+
+ if (cb)
+ sis->rcs[i] = cb(sinfo, sis->rcs[i], sis->results[i], cbdata);
+
+ if (sis->rcs[i] != RPMRC_OK)
+ failed++;
+ }
+ }
+
+ return failed;
+}
+
+static const char * rpmSigString(rpmRC res)
+{
+ const char * str;
+ switch (res) {
+ case RPMRC_OK: str = "OK"; break;
+ case RPMRC_FAIL: str = "BAD"; break;
+ case RPMRC_NOKEY: str = "NOKEY"; break;
+ case RPMRC_NOTTRUSTED: str = "NOTTRUSTED"; break;
+ default:
+ case RPMRC_NOTFOUND: str = "UNKNOWN"; break;
+ }
+ return str;
+}
+
+static const char *rangeName(int range)
+{
+ switch (range) {
+ case RPMSIG_HEADER: return _("Header ");
+ case RPMSIG_PAYLOAD: return _("Payload ");
+ }
+ /* trad. output for (RPMSIG_HEADER|RPMSIG_PAYLOAD) range is "" */
+ return "";
+}
+
+static rpmRC verifyDigest(struct rpmsinfo_s *sinfo, DIGEST_CTX digctx,
+ char **msg)
+{
+ rpmRC res = RPMRC_FAIL; /* assume failure */
+ char * dig = NULL;
+ size_t diglen = 0;
+ DIGEST_CTX ctx = rpmDigestDup(digctx);
+
+ if (rpmDigestFinal(ctx, (void **)&dig, &diglen, 1) || diglen == 0)
+ goto exit;
+
+ if (strcasecmp(sinfo->dig, dig) == 0) {
+ res = RPMRC_OK;
+ } else {
+ rasprintf(msg, "Expected %s != %s", sinfo->dig, dig);
+ }
+
+exit:
+ free(dig);
+ return res;
+}
+
+/**
+ * Verify DSA/RSA signature.
+ * @param keyring pubkey keyring
+ * @param sinfo OpenPGP signature parameters
+ * @param hashctx digest context
+ * @retval msg verbose success/failure text
+ * @return RPMRC_OK on success
+ */
+static rpmRC
+verifySignature(rpmKeyring keyring, struct rpmsinfo_s *sinfo,
+ DIGEST_CTX hashctx, char **msg)
+{
+ rpmRC res = rpmKeyringVerifySig(keyring, sinfo->sig, hashctx);
+
+ return res;
+}
+
+static rpmRC
+rpmVerifySignature(rpmKeyring keyring, struct rpmsinfo_s *sinfo,
+ DIGEST_CTX ctx, char ** result)
+{
+ rpmRC res = RPMRC_FAIL;
+
+ if (sinfo->type == RPMSIG_DIGEST_TYPE)
+ res = verifyDigest(sinfo, ctx, result);
+ else if (sinfo->type == RPMSIG_SIGNATURE_TYPE)
+ res = verifySignature(keyring, sinfo, ctx, result);
+
+ return res;
+}