2 * Copyright (c) 2007-2013, Novell Inc.
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
11 * support for pubkey reading
15 #include <sys/types.h>
27 #include <rpm/rpmio.h>
28 #include <rpm/rpmpgp.h>
30 #include <rpm/header.h>
32 #include <rpm/rpmdb.h>
40 #include "repo_rpmdb.h"
41 #include "repo_pubkey.h"
43 #include "solv_pgpvrfy.h"
47 setutf8string(Repodata *repodata, Id handle, Id tag, const char *str)
49 if (str[solv_validutf8(str)])
51 char *ustr = solv_latin1toutf8(str); /* not utf8, assume latin1 */
52 repodata_set_str(repodata, handle, tag, ustr);
56 repodata_set_str(repodata, handle, tag, str);
60 r64dec1(char *p, unsigned int *vp, int *eofp)
70 if (x >= 'A' && x <= 'Z')
72 else if (x >= 'a' && x <= 'z')
74 else if (x >= '0' && x <= '9')
101 crc24(unsigned char *p, int len)
103 unsigned int crc = 0xb704ceL;
109 for (i = 0; i < 8; i++)
110 if ((crc <<= 1) & 0x1000000)
113 return crc & 0xffffffL;
116 static unsigned char *
117 unarmor(char *pubkey, int *pktlp, char *startstr, char *endstr)
121 unsigned char *buf, *bp;
127 l = strlen(startstr);
128 while (strncmp(pubkey, startstr, l) != 0)
130 pubkey = strchr(pubkey, '\n');
135 pubkey = strchr(pubkey, '\n');
138 /* skip header lines */
141 while (*pubkey == ' ' || *pubkey == '\t')
145 pubkey = strchr(pubkey, '\n');
150 p = strchr(pubkey, '=');
154 bp = buf = solv_malloc(l * 3 / 4 + 4);
158 pubkey = r64dec1(pubkey, &v, &eof);
168 while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
171 if (*pubkey != '=' || (pubkey = r64dec1(pubkey + 1, &v, &eof)) == 0)
176 if (v != crc24(buf, bp - buf))
181 while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
183 if (strncmp(pubkey, endstr, strlen(endstr)) != 0)
195 unsigned char issuer[8];
197 unsigned int created;
198 unsigned int expires;
199 unsigned int keyexpires;
200 unsigned char *sigdata;
206 pgphashalgo2type(int algo)
209 return REPOKEY_TYPE_MD5;
211 return REPOKEY_TYPE_SHA1;
213 return REPOKEY_TYPE_SHA256;
218 createsigdata(struct pgpsig *sig, unsigned char *p, int l, unsigned char *pubkey, int pubkeyl, unsigned char *userid, int useridl, void *h)
220 int type = sig->type;
222 const unsigned char *cs;
225 if (sig->mpioff < 2 || l <= sig->mpioff)
227 if ((type >= 0x10 && type <= 0x13) || type == 0x1f || type == 0x18 || type == 0x20 || type == 0x28)
232 solv_chksum_add(h, b, 3);
233 solv_chksum_add(h, pubkey, pubkeyl);
235 if ((type >= 0x10 && type <= 0x13))
240 b[1] = useridl >> 24;
241 b[2] = useridl >> 16;
244 solv_chksum_add(h, b, 5);
246 solv_chksum_add(h, userid, useridl);
250 solv_chksum_add(h, p + 2, 5);
253 int hl = 6 + (p[4] << 8 | p[5]);
254 solv_chksum_add(h, p, hl);
261 solv_chksum_add(h, b, 6);
263 cs = solv_chksum_get(h, &csl);
264 if (cs[0] == p[sig->mpioff - 2] && cs[1] == p[sig->mpioff - 1])
266 int ml = l - sig->mpioff;
267 sig->sigdata = solv_malloc(2 + csl + ml);
268 sig->sigdatal = 2 + csl + ml;
269 sig->sigdata[0] = p[0] == 3 ? p[15] : p[2];
270 sig->sigdata[1] = p[0] == 3 ? p[16] : p[3];
271 memcpy(sig->sigdata + 2, cs, csl);
272 memcpy(sig->sigdata + 2 + csl, p + sig->mpioff, ml);
277 parsesubpkglength(unsigned char *q, int ql, int *pktlp)
280 /* decode sub-packet length, ql must be > 0 */
289 if (ql < 5 || q[0] != 0)
291 sl = q[1] << 16 | q[2] << 8 | q[3];
298 sl = ((x - 192) << 8) + q[0] + 192;
301 if (!sl || ql < sl + hl) /* sub pkg tag is included in length, i.e. sl must not be zero */
308 parsesigpacket(struct pgpsig *sig, unsigned char *p, int l)
313 /* printf("V3 signature packet\n"); */
314 if (l <= 19 || p[1] != 5)
318 memcpy(sig->issuer, p + 7, 8);
319 sig->created = p[3] << 24 | p[4] << 16 | p[5] << 8 | p[6];
320 sig->hashalgo = p[16];
328 /* printf("V4 signature packet\n"); */
332 sig->hashalgo = p[3];
334 sig->keyexpires = -1;
335 for (j = 0; q && j < 2; j++)
342 ql = q[0] << 8 | q[1];
352 hl = parsesubpkglength(q, ql, &sl);
360 x = q[0] & 127; /* strip critical bit */
361 /* printf("%d SIGSUB %d %d\n", j, x, sl); */
362 if (x == 16 && sl == 9 && !sig->haveissuer)
365 memcpy(sig->issuer, q + 1, 8);
367 if (x == 2 && j == 0)
368 sig->created = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
369 if (x == 3 && j == 0)
370 sig->expires = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
371 if (x == 9 && j == 0)
372 sig->keyexpires = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
377 if (q && q - p + 2 < l)
378 sig->mpioff = q - p + 2;
383 parsepkgheader(unsigned char *p, int pl, int *tagp, int *pktlp)
385 unsigned char *op = p;
392 if (!(x & 128) || pl <= 0)
396 *tagp = (x & 0x3c) >> 2; /* old format */
398 if (x > 4 || pl < x || (x == 4 && p[0]))
406 *tagp = (x & 0x3f); /* new format */
411 else if (x >= 192 && x < 224)
415 l = ((x - 192) << 8) + *p++ + 192;
420 if (pl <= 4 || p[0] != 0) /* sanity: p[0] must be zero */
422 l = p[1] << 16 | p[2] << 8 | p[3];
437 parsekeydata(Solvable *s, Repodata *data, unsigned char *p, int pl)
440 unsigned char keyid[8];
441 unsigned int kcr = 0, maxex = 0, maxsigcr = 0;
442 unsigned char *pubkey = 0;
444 unsigned char *userid = 0;
446 unsigned char *pubdata = 0;
449 for (; pl; p += l, pl -= l)
451 int hl = parsepkgheader(p, pl, &tag, &l);
459 break; /* one key at a time, please */
460 pubkey = solv_malloc(l);
462 memcpy(pubkey, p, l);
464 if (p[0] == 3 && l >= 10)
468 maxsigcr = kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
472 ex = kcr + 24*3600 * (p[5] << 8 | p[6]);
477 if (p[7] == 1) /* RSA */
480 unsigned char fp[16];
484 ql = ((p[8] << 8 | p[9]) + 7) / 8; /* length of public modulus */
485 if (ql >= 8 && 10 + ql + 2 <= l)
487 memcpy(keyid, p + 10 + ql - 8, 8); /* keyid is last 64 bits of public modulus */
489 ql2 = ((q[0] << 8 | q[1]) + 7) / 8; /* length of encryption exponent */
490 if (10 + ql + 2 + ql2 <= l)
492 /* fingerprint is the md5 over the two MPI bodies */
493 h = solv_chksum_create(REPOKEY_TYPE_MD5);
494 solv_chksum_add(h, p + 10, ql);
495 solv_chksum_add(h, q + 2, ql2);
496 solv_chksum_free(h, fp);
497 solv_bin2hex(fp, 16, fpx);
498 repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
505 else if (p[0] == 4 && l >= 6)
508 unsigned char hdr[3];
509 unsigned char fp[20];
512 maxsigcr = kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
516 /* fingerprint is the sha1 over the packet */
517 h = solv_chksum_create(REPOKEY_TYPE_SHA1);
518 solv_chksum_add(h, hdr, 3);
519 solv_chksum_add(h, p, l);
520 solv_chksum_free(h, fp);
521 solv_bin2hex(fp, 20, fpx);
522 repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
523 memcpy(keyid, fp + 12, 8); /* keyid is last 64 bits of fingerprint */
534 memset(&sig, 0, sizeof(sig));
535 parsesigpacket(&sig, p, l);
536 if (!sig.haveissuer || !((sig.type >= 0x10 && sig.type <= 0x13) || sig.type == 0x1f))
538 if (sig.type >= 0x10 && sig.type <= 0x13 && !userid)
540 htype = pgphashalgo2type(sig.hashalgo);
541 if (htype && sig.mpioff)
543 void *h = solv_chksum_create(htype);
544 createsigdata(&sig, p, l, pubkey, pubkeyl, userid, useridl, h);
545 solv_chksum_free(h, 0);
547 if (!memcmp(keyid, sig.issuer, 8))
549 #ifdef ENABLE_PGPVRFY
550 /* found self sig, verify */
551 if (solv_pgpvrfy(pubdata, pubdatal, sig.sigdata, sig.sigdatal))
554 if (sig.keyexpires && maxex != -1)
556 if (sig.keyexpires == -1)
558 else if (sig.keyexpires + kcr > maxex)
559 maxex = sig.keyexpires + kcr;
561 if (sig.created > maxsigcr)
562 maxsigcr = sig.created;
568 Id shandle = repodata_new_handle(data);
569 solv_bin2hex(sig.issuer, 8, issuerstr);
570 repodata_set_str(data, shandle, SIGNATURE_ISSUER, issuerstr);
572 repodata_set_num(data, shandle, SIGNATURE_TIME, sig.created);
574 repodata_set_num(data, shandle, SIGNATURE_EXPIRES, sig.created + sig.expires);
576 repodata_set_binary(data, shandle, SIGNATURE_DATA, sig.sigdata, sig.sigdatal);
577 repodata_add_flexarray(data, s - s->repo->pool->solvables, PUBKEY_SIGNATURES, shandle);
579 solv_free(sig.sigdata);
583 userid = solv_realloc(userid, l);
585 memcpy(userid, p, l);
590 repodata_set_num(data, s - s->repo->pool->solvables, SOLVABLE_BUILDTIME, kcr);
591 if (maxex && maxex != -1)
592 repodata_set_num(data, s - s->repo->pool->solvables, PUBKEY_EXPIRES, maxex);
593 s->name = pool_str2id(s->repo->pool, "gpg-pubkey", 1);
596 if (userid && useridl)
598 char *useridstr = solv_malloc(useridl + 1);
599 memcpy(useridstr, userid, useridl);
600 useridstr[useridl] = 0;
601 setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_SUMMARY, useridstr);
607 solv_bin2hex(keyid, 8, keyidstr);
608 repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_KEYID, keyidstr);
612 /* build rpm-style evr */
613 char evr[8 + 1 + 8 + 1];
614 solv_bin2hex(keyid + 4, 4, evr);
615 sprintf(evr + 8, "-%08x", maxsigcr);
616 s->evr = pool_str2id(s->repo->pool, evr, 1);
618 repodata_set_binary(data, s - s->repo->pool->solvables, PUBKEY_DATA, pubdata, pubdatal);
627 /* this is private to rpm, but rpm lacks an interface to retrieve
628 * the values. Sigh. */
629 struct pgpDigParams_s {
631 const unsigned char * hash;
632 #ifndef HAVE_PGPDIGGETPARAMS
633 const char * params[4];
636 unsigned char version; /*!< version number. */
637 unsigned char time[4]; /*!< time that the key was created. */
638 unsigned char pubkey_algo; /*!< public key algorithm. */
639 unsigned char hash_algo;
640 unsigned char sigtype;
641 unsigned char hashlen;
642 unsigned char signhash16[2];
643 unsigned char signid[8];
647 #ifndef HAVE_PGPDIGGETPARAMS
649 struct pgpDigParams_s signature;
650 struct pgpDigParams_s pubkey;
655 /* only rpm knows how to do the release calculation, we don't dare
656 * to recreate all the bugs in libsolv */
658 parsekeydata_rpm(Solvable *s, Repodata *data, unsigned char *pkts, int pktsl)
660 Pool *pool = s->repo->pool;
661 struct pgpDigParams_s *digpubkey;
664 char evrbuf[8 + 1 + 8 + 1];
670 dig = pgpDigNew(RPMVSF_DEFAULT, 0);
672 (void) pgpPrtPkts(pkts, pktsl, dig, 0);
673 #ifdef HAVE_PGPDIGGETPARAMS
674 digpubkey = pgpDigGetParams(dig, PGPTAG_PUBLIC_KEY);
676 digpubkey = &dig->pubkey;
680 btime = digpubkey->time[0] << 24 | digpubkey->time[1] << 16 | digpubkey->time[2] << 8 | digpubkey->time[3];
681 solv_bin2hex(digpubkey->signid, 8, keyid);
682 solv_bin2hex(digpubkey->signid + 4, 4, evrbuf);
684 solv_bin2hex(digpubkey->time, 4, evrbuf + 9);
685 s->evr = pool_str2id(pool, evrbuf, 1);
686 repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_KEYID, keyid);
687 if (digpubkey->userid)
688 setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_SUMMARY, digpubkey->userid);
690 repodata_set_num(data, s - s->repo->pool->solvables, SOLVABLE_BUILDTIME, btime);
693 (void)pgpFreeDig(dig);
695 (void)pgpDigFree(dig);
699 #endif /* ENABLE_RPMDB */
702 pubkey2solvable(Solvable *s, Repodata *data, char *pubkey)
707 pkts = unarmor(pubkey, &pktsl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----");
710 pool_error(s->repo->pool, 0, "unarmor failure");
713 setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_DESCRIPTION, pubkey);
714 parsekeydata(s, data, pkts, pktsl);
716 parsekeydata_rpm(s, data, pkts, pktsl);
718 solv_free((void *)pkts);
725 repo_add_rpmdb_pubkeys(Repo *repo, int flags)
727 Pool *pool = repo->pool;
733 const char *rootdir = 0;
736 data = repo_add_repodata(repo, flags);
737 if (flags & REPO_USE_ROOTDIR)
738 rootdir = pool_get_rootdir(pool);
739 state = rpm_state_create(repo->pool, rootdir);
741 rpm_installedrpmdbids(state, "Name", "gpg-pubkey", &q);
742 for (i = 0; i < q.count; i++)
745 unsigned long long itime;
747 handle = rpm_byrpmdbid(state, q.elements[i]);
750 str = rpm_query(handle, SOLVABLE_DESCRIPTION);
753 s = pool_id2solvable(pool, repo_add_solvable(repo));
754 pubkey2solvable(s, data, str);
756 itime = rpm_query_num(handle, SOLVABLE_INSTALLTIME, 0);
758 repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLTIME, itime);
760 repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
761 repo->rpmdbid[s - pool->solvables - repo->start] = q.elements[i];
764 rpm_state_free(state);
765 if (!(flags & REPO_NO_INTERNALIZE))
766 repodata_internalize(data);
773 solv_slurp(FILE *fp, int *lenp)
779 for (l = 0; ; l += ll)
784 buf = solv_realloc(buf, bufl);
786 ll = fread(buf + l, 1, bufl - l, fp);
789 buf = solv_free(buf);
795 buf[l] = 0; /* always zero-terminate */
805 repo_add_pubkey(Repo *repo, const char *key, int flags)
807 Pool *pool = repo->pool;
813 data = repo_add_repodata(repo, flags);
815 if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, key) : key, "r")) == 0)
817 pool_error(pool, -1, "%s: %s", key, strerror(errno));
820 if ((buf = solv_slurp(fp, 0)) == 0)
822 pool_error(pool, -1, "%s: %s", key, strerror(errno));
827 s = pool_id2solvable(pool, repo_add_solvable(repo));
828 if (!pubkey2solvable(s, data, buf))
830 repo_free_solvable(repo, s - pool->solvables, 1);
835 if (!(flags & REPO_NO_INTERNALIZE))
836 repodata_internalize(data);
837 return s - pool->solvables;
841 is_sig_packet(unsigned char *sig, int sigl)
845 if ((sig[0] & 0x80) == 0 || (sig[0] & 0x40 ? sig[0] & 0x3f : sig[0] >> 2 & 0x0f) != 2)
851 solv_parse_sig(FILE *fp, unsigned char **sigpktp, int *sigpktlp, char *keyidstr)
854 int sigl, hl, tag, pktl;
855 struct pgpsig pgpsig;
863 if ((sig = (unsigned char *)solv_slurp(fp, &sigl)) == 0)
865 if (!is_sig_packet(sig, sigl))
867 /* not a raw sig, check armored */
869 nsig = unarmor((char *)sig, &sigl, "-----BEGIN PGP SIGNATURE-----", "-----END PGP SIGNATURE-----");
874 if (!is_sig_packet(sig, sigl))
880 hl = parsepkgheader(sig, sigl, &tag, &pktl);
881 if (!hl || tag != 2 || !pktl)
886 memset(&pgpsig, 0, sizeof(pgpsig));
887 parsesigpacket(&pgpsig, sig + hl, pktl);
888 htype = pgphashalgo2type(pgpsig.hashalgo);
889 if (pgpsig.type != 0 || !htype || !pgpsig.haveissuer)
896 *sigpktp = solv_malloc(pktl);
897 memcpy(*sigpktp, sig + hl, pktl);
902 solv_bin2hex(pgpsig.issuer, 8, keyidstr);
906 #ifdef ENABLE_PGPVRFY
909 solv_verify_sig(const unsigned char *pubdata, int pubdatal, unsigned char *sigpkt, int sigpktl, void *chk)
911 struct pgpsig pgpsig;
916 memset(&pgpsig, 0, sizeof(pgpsig));
917 parsesigpacket(&pgpsig, sigpkt, sigpktl);
918 if (pgpsig.type != 0)
920 htype = pgphashalgo2type(pgpsig.hashalgo);
921 if (htype != solv_chksum_get_type(chk))
922 return 0; /* wrong hash type? */
923 chk2 = solv_chksum_create_clone(chk);
924 createsigdata(&pgpsig, sigpkt, sigpktl, 0, 0, 0, 0, chk2);
925 solv_chksum_free(chk2, 0);
928 res = solv_pgpvrfy(pubdata, pubdatal, pgpsig.sigdata, pgpsig.sigdatal);
929 solv_free(pgpsig.sigdata);