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"
42 #include "solv_pgpvrfy.h"
46 setutf8string(Repodata *repodata, Id handle, Id tag, const char *str)
48 if (str[solv_validutf8(str)])
50 char *ustr = solv_latin1toutf8(str); /* not utf8, assume latin1 */
51 repodata_set_str(repodata, handle, tag, ustr);
55 repodata_set_str(repodata, handle, tag, str);
59 r64dec1(char *p, unsigned int *vp, int *eofp)
69 if (x >= 'A' && x <= 'Z')
71 else if (x >= 'a' && x <= 'z')
73 else if (x >= '0' && x <= '9')
100 crc24(unsigned char *p, int len)
102 unsigned int crc = 0xb704ceL;
108 for (i = 0; i < 8; i++)
109 if ((crc <<= 1) & 0x1000000)
112 return crc & 0xffffffL;
115 static unsigned char *
116 unarmor(char *pubkey, int *pktlp)
120 unsigned char *buf, *bp;
124 while (strncmp(pubkey, "-----BEGIN PGP PUBLIC KEY BLOCK-----", 36) != 0)
126 pubkey = strchr(pubkey, '\n');
131 pubkey = strchr(pubkey, '\n');
134 /* skip header lines */
137 while (*pubkey == ' ' || *pubkey == '\t')
141 pubkey = strchr(pubkey, '\n');
146 p = strchr(pubkey, '=');
150 bp = buf = solv_malloc(l * 3 / 4 + 4);
154 pubkey = r64dec1(pubkey, &v, &eof);
164 while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
167 if (*pubkey != '=' || (pubkey = r64dec1(pubkey + 1, &v, &eof)) == 0)
172 if (v != crc24(buf, bp - buf))
177 while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
179 if (strncmp(pubkey, "-----END PGP PUBLIC KEY BLOCK-----", 34) != 0)
190 unsigned char issuer[8];
192 unsigned int created;
193 unsigned int expires;
194 unsigned int keyexpires;
195 unsigned char *sigdata;
200 parsesigpacket(struct gpgsig *sig, unsigned char *p, int l, unsigned char *pubkey, int pubkeyl, unsigned char *userid, int useridl)
206 /* printf("V3 signature packet\n"); */
207 if (l <= 19 || p[1] != 5)
209 if (p[2] != 0x10 && p[2] != 0x11 && p[2] != 0x12 && p[2] != 0x13 && p[2] != 0x1f)
213 memcpy(sig->issuer, p + 7, 8);
214 sig->created = p[3] << 24 | p[4] << 16 | p[5] << 8 | p[6];
216 htype = REPOKEY_TYPE_MD5;
218 htype = REPOKEY_TYPE_SHA1;
220 htype = REPOKEY_TYPE_SHA256;
223 void *h = solv_chksum_create(htype);
224 const unsigned char *cs;
228 if ((p[2] >= 0x10 && p[2] <= 0x13) || p[2] == 0x1f || p[2] == 0x18 || p[2] == 0x20 || p[2] == 0x28)
233 solv_chksum_add(h, b, 3);
234 solv_chksum_add(h, pubkey, pubkeyl);
236 if (p[2] >= 0x10 && p[2] <= 0x13)
237 solv_chksum_add(h, userid, useridl);
238 solv_chksum_add(h, p + 2, 5);
239 cs = solv_chksum_get(h, &csl);
240 if (cs[0] == p[17] && cs[1] == p[18])
242 sig->sigdata = solv_malloc(2 + csl + l - 19);
243 sig->sigdatal = 2 + csl + l - 19;
244 sig->sigdata[0] = p[15];
245 sig->sigdata[1] = p[16];
246 memcpy(sig->sigdata + 2, cs, csl);
247 memcpy(sig->sigdata + 2 + csl, p + 19, l - 19);
249 solv_chksum_free(h, 0);
258 /* printf("V4 signature packet\n"); */
261 if (p[1] != 0x10 && p[1] != 0x11 && p[1] != 0x12 && p[1] != 0x13 && p[1] != 0x1f)
265 for (j = 0; q && j < 2; j++)
272 ql = q[0] << 8 | q[1];
282 /* decode sub-packet length */
289 if (ql < 4 || q[0] != 0)
294 sl = q[1] << 16 | q[2] << 8 | q[3];
305 sl = ((x - 192) << 8) + *q++ + 192;
314 /* printf("%d SIGSUB %d %d\n", j, x, sl); */
315 if (x == 16 && sl == 9 && !sig->haveissuer)
318 memcpy(sig->issuer, q + 1, 8);
320 if (x == 2 && j == 0)
321 sig->created = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
322 if (x == 3 && j == 0)
323 sig->expires = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
324 if (x == 9 && j == 0)
325 sig->keyexpires = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
331 htype = REPOKEY_TYPE_MD5;
333 htype = REPOKEY_TYPE_SHA1;
335 htype = REPOKEY_TYPE_SHA256;
336 if (sig->haveissuer && htype && pubkey && q && q - p + 2 < l)
338 void *h = solv_chksum_create(htype);
340 const unsigned char *cs;
342 int csl, ml = l - (q - p + 2);
344 if ((p[1] >= 0x10 && p[1] <= 0x13) || p[1] == 0x1f || p[1] == 0x18 || p[1] == 0x20 || p[1] == 0x28)
349 solv_chksum_add(h, b, 3);
350 solv_chksum_add(h, pubkey, pubkeyl);
352 if (p[1] >= 0x10 && p[1] <= 0x13)
355 b[1] = useridl >> 24;
356 b[2] = useridl >> 16;
359 solv_chksum_add(h, b, 5);
360 solv_chksum_add(h, userid, useridl);
362 hl = 6 + (p[4] << 8 | p[5]);
363 solv_chksum_add(h, p, hl);
370 solv_chksum_add(h, b, 6);
371 cs = solv_chksum_get(h, &csl);
372 if (cs[0] == q[0] && cs[1] == q[1])
374 sig->sigdata = solv_malloc(2 + csl + ml);
375 sig->sigdatal = 2 + csl + ml;
376 sig->sigdata[0] = p[2];
377 sig->sigdata[1] = p[3];
378 memcpy(sig->sigdata + 2, cs, csl);
379 memcpy(sig->sigdata + 2 + csl, q + 2, ml);
381 solv_chksum_free(h, 0);
387 parsekeydata(Solvable *s, Repodata *data, unsigned char *p, int pl)
390 unsigned char keyid[8];
391 unsigned int kcr = 0, maxex = 0, maxsigcr = 0;
392 unsigned char *pubkey = 0;
394 unsigned char *userid = 0;
396 unsigned char *pubdata = 0;
399 for (; pl; p += l, pl -= l)
403 if (!(x & 128) || pl <= 0)
408 tag = (x & 0x3c) >> 2;
430 else if (x >= 192 && x < 224)
434 l = ((x - 192) << 8) + *p++ + 192;
439 /* sanity: p[0] must be zero */
440 if (pl <= 4 || p[0] != 0)
442 l = p[1] << 16 | p[2] << 8 | p[3];
454 break; /* one key at a time, please */
455 pubkey = solv_malloc(l);
457 memcpy(pubkey, p, l);
459 if (p[0] == 3 && l >= 10)
463 maxsigcr = kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
467 ex = kcr + 24*3600 * (p[5] << 8 | p[6]);
472 if (p[7] == 1) /* RSA */
475 unsigned char fp[16];
479 ql = ((p[8] << 8 | p[9]) + 7) / 8; /* length of public modulus */
480 if (ql >= 8 && 10 + ql + 2 <= l)
482 memcpy(keyid, p + 10 + ql - 8, 8); /* keyid is last 64 bits of public modulus */
484 ql2 = ((q[0] << 8 | q[1]) + 7) / 8; /* length of encryption exponent */
485 if (10 + ql + 2 + ql2 <= l)
487 /* fingerprint is the md5 over the two MPI bodies */
488 h = solv_chksum_create(REPOKEY_TYPE_MD5);
489 solv_chksum_add(h, p + 10, ql);
490 solv_chksum_add(h, q + 2, ql2);
491 solv_chksum_free(h, fp);
492 solv_bin2hex(fp, 16, fpx);
493 repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
500 else if (p[0] == 4 && l >= 6)
503 unsigned char hdr[3];
504 unsigned char fp[20];
507 maxsigcr = kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
511 /* fingerprint is the sha1 over the packet */
512 h = solv_chksum_create(REPOKEY_TYPE_SHA1);
513 solv_chksum_add(h, hdr, 3);
514 solv_chksum_add(h, p, l);
515 solv_chksum_free(h, fp);
516 solv_bin2hex(fp, 20, fpx);
517 repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
518 memcpy(keyid, fp + 12, 8); /* keyid is last 64 bits of fingerprint */
528 memset(&sig, 0, sizeof(sig));
529 parsesigpacket(&sig, p, l, pubkey, pubkeyl, userid, useridl);
530 if (!sig.haveissuer || !((sig.type >= 0x10 && sig.type <= 0x13) || sig.type == 0x1f))
532 solv_free(sig.sigdata);
535 if (!memcmp(keyid, sig.issuer, 8))
537 #ifdef ENABLE_PGPVRFY
538 /* found self sig, verify */
539 if (solv_pgpvrfy(pubdata, pubdatal, sig.sigdata, sig.sigdatal))
542 if (!maxex && sig.keyexpires)
543 maxex = sig.keyexpires + kcr;
544 if (sig.created > maxsigcr)
545 maxsigcr = sig.created;
551 Id shandle = repodata_new_handle(data);
552 solv_bin2hex(sig.issuer, 8, issuerstr);
553 repodata_set_str(data, shandle, SIGNATURE_ISSUER, issuerstr);
555 repodata_set_num(data, shandle, SIGNATURE_TIME, sig.created);
557 repodata_set_num(data, shandle, SIGNATURE_EXPIRES, sig.created + sig.expires);
559 repodata_set_binary(data, shandle, SIGNATURE_DATA, sig.sigdata, sig.sigdatal);
560 repodata_add_flexarray(data, s - s->repo->pool->solvables, PUBKEY_SIGNATURES, shandle);
562 solv_free(sig.sigdata);
566 userid = solv_realloc(userid, l);
568 memcpy(userid, p, l);
573 repodata_set_num(data, s - s->repo->pool->solvables, SOLVABLE_BUILDTIME, kcr);
575 repodata_set_num(data, s - s->repo->pool->solvables, PUBKEY_EXPIRES, maxex);
576 s->name = pool_str2id(s->repo->pool, "gpg-pubkey", 1);
579 if (userid && useridl)
581 char *useridstr = solv_malloc(useridl + 1);
582 memcpy(useridstr, userid, useridl);
583 useridstr[useridl] = 0;
584 setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_SUMMARY, useridstr);
590 solv_bin2hex(keyid, 8, keyidstr);
591 repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_KEYID, keyidstr);
595 /* build rpm-style evr */
596 char evr[8 + 1 + 8 + 1];
597 solv_bin2hex(keyid + 4, 4, evr);
598 sprintf(evr + 8, "-%08x", maxsigcr);
599 s->evr = pool_str2id(s->repo->pool, evr, 1);
601 repodata_set_binary(data, s - s->repo->pool->solvables, PUBKEY_DATA, pubdata, pubdatal);
610 /* this is private to rpm, but rpm lacks an interface to retrieve
611 * the values. Sigh. */
612 struct pgpDigParams_s {
614 const unsigned char * hash;
615 #ifndef HAVE_PGPDIGGETPARAMS
616 const char * params[4];
619 unsigned char version; /*!< version number. */
620 unsigned char time[4]; /*!< time that the key was created. */
621 unsigned char pubkey_algo; /*!< public key algorithm. */
622 unsigned char hash_algo;
623 unsigned char sigtype;
624 unsigned char hashlen;
625 unsigned char signhash16[2];
626 unsigned char signid[8];
630 #ifndef HAVE_PGPDIGGETPARAMS
632 struct pgpDigParams_s signature;
633 struct pgpDigParams_s pubkey;
638 /* only rpm knows how to do the release calculation, we don't dare
639 * to recreate all the bugs in libsolv */
641 parsekeydata_rpm(Solvable *s, Repodata *data, unsigned char *pkts, int pktsl)
643 Pool *pool = s->repo->pool;
644 struct pgpDigParams_s *digpubkey;
647 char evrbuf[8 + 1 + 8 + 1];
653 dig = pgpDigNew(RPMVSF_DEFAULT, 0);
655 (void) pgpPrtPkts(pkts, pktsl, dig, 0);
656 #ifdef HAVE_PGPDIGGETPARAMS
657 digpubkey = pgpDigGetParams(dig, PGPTAG_PUBLIC_KEY);
659 digpubkey = &dig->pubkey;
663 btime = digpubkey->time[0] << 24 | digpubkey->time[1] << 16 | digpubkey->time[2] << 8 | digpubkey->time[3];
664 solv_bin2hex(digpubkey->signid, 8, keyid);
665 solv_bin2hex(digpubkey->signid + 4, 4, evrbuf);
667 solv_bin2hex(digpubkey->time, 4, evrbuf + 9);
668 s->evr = pool_str2id(pool, evrbuf, 1);
669 repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_KEYID, keyid);
670 if (digpubkey->userid)
671 setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_SUMMARY, digpubkey->userid);
673 repodata_set_num(data, s - s->repo->pool->solvables, SOLVABLE_BUILDTIME, btime);
676 (void)pgpFreeDig(dig);
678 (void)pgpDigFree(dig);
682 #endif /* ENABLE_RPMDB */
685 pubkey2solvable(Solvable *s, Repodata *data, char *pubkey)
690 pkts = unarmor(pubkey, &pktsl);
693 pool_error(s->repo->pool, 0, "unarmor failure");
696 setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_DESCRIPTION, pubkey);
697 parsekeydata(s, data, pkts, pktsl);
699 parsekeydata_rpm(s, data, pkts, pktsl);
701 solv_free((void *)pkts);
708 repo_add_rpmdb_pubkeys(Repo *repo, int flags)
710 Pool *pool = repo->pool;
716 const char *rootdir = 0;
719 data = repo_add_repodata(repo, flags);
720 if (flags & REPO_USE_ROOTDIR)
721 rootdir = pool_get_rootdir(pool);
722 state = rpm_state_create(repo->pool, rootdir);
724 rpm_installedrpmdbids(state, "Name", "gpg-pubkey", &q);
725 for (i = 0; i < q.count; i++)
728 unsigned long long itime;
730 handle = rpm_byrpmdbid(state, q.elements[i]);
733 str = rpm_query(handle, SOLVABLE_DESCRIPTION);
736 s = pool_id2solvable(pool, repo_add_solvable(repo));
737 pubkey2solvable(s, data, str);
739 itime = rpm_query_num(handle, SOLVABLE_INSTALLTIME, 0);
741 repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLTIME, itime);
743 repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
744 repo->rpmdbid[s - pool->solvables - repo->start] = q.elements[i];
747 rpm_state_free(state);
748 if (!(flags & REPO_NO_INTERNALIZE))
749 repodata_internalize(data);
756 repo_add_pubkey(Repo *repo, const char *key, int flags)
758 Pool *pool = repo->pool;
765 data = repo_add_repodata(repo, flags);
768 if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, key) : key, "r")) == 0)
770 pool_error(pool, -1, "%s: %s", key, strerror(errno));
778 buf = solv_realloc(buf, bufl);
780 ll = fread(buf + l, 1, bufl - l, fp);
784 pool_error(pool, -1, "%s: %s", key, strerror(errno));
793 s = pool_id2solvable(pool, repo_add_solvable(repo));
794 if (!pubkey2solvable(s, data, buf))
796 repo_free_solvable(repo, s - pool->solvables, 1);
801 if (!(flags & REPO_NO_INTERNALIZE))
802 repodata_internalize(data);
803 return s - pool->solvables;