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 pubkeys stored in the rpmdb database
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 /* FIXME: dedup with repo_rpmdb.c */
44 setutf8string(Repodata *repodata, Id handle, Id tag, const char *str)
46 const unsigned char *cp;
49 unsigned char *buf = 0, *bp;
51 /* check if it's already utf8, code taken from screen ;-) */
52 cp = (const unsigned char *)str;
53 while ((c = *cp++) != 0)
57 if ((c & 0xc0) != 0x80)
58 break; /* encoding error */
59 c = (c & 0x3f) | (state << 6);
60 if (!(state & 0x40000000))
62 /* check for overlong sequences */
63 if ((c & 0x820823e0) == 0x80000000)
65 else if ((c & 0x020821f0) == 0x02000000)
67 else if ((c & 0x000820f8) == 0x00080000)
69 else if ((c & 0x0000207c) == 0x00002000)
79 c = (c & 0x01) | 0xbffffffc; /* 5 bytes to follow */
81 c = (c & 0x03) | 0xbfffff00; /* 4 */
83 c = (c & 0x07) | 0xbfffc000; /* 3 */
85 c = (c & 0x0f) | 0xbff00000; /* 2 */
87 c = (c & 0x1f) | 0xfc000000; /* 1 */
91 state = (c & 0x80000000) ? c : 0;
95 /* not utf8, assume latin1 */
96 buf = solv_malloc(2 * strlen(str) + 1);
97 cp = (const unsigned char *)str;
100 while ((c = *cp++) != 0)
113 repodata_set_str(repodata, handle, tag, str);
119 r64dec1(char *p, unsigned int *vp, int *eofp)
129 if (x >= 'A' && x <= 'Z')
131 else if (x >= 'a' && x <= 'z')
133 else if (x >= '0' && x <= '9')
160 crc24(unsigned char *p, int len)
162 unsigned int crc = 0xb704ceL;
168 for (i = 0; i < 8; i++)
169 if ((crc <<= 1) & 0x1000000)
172 return crc & 0xffffffL;
175 static unsigned char *
176 unarmor(char *pubkey, int *pktlp)
180 unsigned char *buf, *bp;
184 while (strncmp(pubkey, "-----BEGIN PGP PUBLIC KEY BLOCK-----", 36) != 0)
186 pubkey = strchr(pubkey, '\n');
191 pubkey = strchr(pubkey, '\n');
194 /* skip header lines */
197 while (*pubkey == ' ' || *pubkey == '\t')
201 pubkey = strchr(pubkey, '\n');
206 p = strchr(pubkey, '=');
210 bp = buf = solv_malloc(l * 3 / 4 + 4);
214 pubkey = r64dec1(pubkey, &v, &eof);
224 while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
227 if (*pubkey != '=' || (pubkey = r64dec1(pubkey + 1, &v, &eof)) == 0)
232 if (v != crc24(buf, bp - buf))
237 while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
239 if (strncmp(pubkey, "-----END PGP PUBLIC KEY BLOCK-----", 34) != 0)
249 parsekeydata(Solvable *s, Repodata *data, unsigned char *p, int pl)
252 unsigned char keyid[8];
253 unsigned int kcr = 0, maxex = 0;
255 unsigned char *pubkey = 0;
256 unsigned char *userid = 0;
261 for (; pl; p += l, pl -= l)
265 if (!(x & 128) || pl <= 0)
270 tag = (x & 0x3c) >> 2;
292 else if (x >= 192 && x < 224)
296 l = ((x - 192) << 8) + *p++ + 192;
301 /* sanity: p[0] must be zero */
302 if (pl <= 4 || p[0] != 0)
304 l = p[1] << 16 | p[2] << 8 | p[3];
316 pubkey = solv_realloc(pubkey, l);
318 memcpy(pubkey, p, l);
322 if (p[0] == 3 && l >= 10)
326 kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
330 ex = kcr + 24*3600 * (p[5] << 8 | p[6]);
335 if (p[7] == 1) /* RSA */
338 unsigned char fp[16];
342 ql = ((p[8] << 8 | p[9]) + 7) / 8; /* length of public modulus */
343 if (ql >= 8 && 10 + ql + 2 <= l)
345 memcpy(keyid, p + 10 + ql - 8, 8); /* keyid is last 64 bits of public modulus */
347 ql2 = ((q[0] << 8 | q[1]) + 7) / 8; /* length of encryption exponent */
348 if (10 + ql + 2 + ql2 <= l)
350 /* fingerprint is the md5 over the two MPI bodies */
351 h = solv_chksum_create(REPOKEY_TYPE_MD5);
352 solv_chksum_add(h, p + 10, ql);
353 solv_chksum_add(h, q + 2, ql2);
354 solv_chksum_free(h, fp);
355 for (i = 0; i < 16; i++)
356 sprintf(fpx + i * 2, "%02x", fp[i]);
357 repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
362 else if (p[0] == 4 && l >= 6)
366 unsigned char hdr[3];
367 unsigned char fp[20];
370 kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
374 /* fingerprint is the sha1 over the packet */
375 h = solv_chksum_create(REPOKEY_TYPE_SHA1);
376 solv_chksum_add(h, hdr, 3);
377 solv_chksum_add(h, p, l);
378 solv_chksum_free(h, fp);
379 for (i = 0; i < 20; i++)
380 sprintf(fpx + i * 2, "%02x", fp[i]);
381 repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
382 memcpy(keyid, fp + 12, 8); /* keyid is last 64 bits of fingerprint */
387 if (p[0] == 3 && p[1] == 5)
392 /* printf("V3 signature packet\n"); */
395 if (p[2] != 0x10 && p[2] != 0x11 && p[2] != 0x12 && p[2] != 0x13 && p[2] != 0x1f)
397 if (!memcmp(keyid, p + 6, 8))
399 /* printf("SELF SIG\n"); */
403 /* printf("OTHER SIG\n"); */
407 htype = REPOKEY_TYPE_MD5;
409 htype = REPOKEY_TYPE_SHA1;
411 htype = REPOKEY_TYPE_SHA256;
414 void *h = solv_chksum_create(htype);
415 unsigned char b[3], *cs;
420 solv_chksum_add(h, b, 3);
421 solv_chksum_add(h, pubkey, pubkeyl);
422 if (p[2] >= 0x10 && p[2] <= 0x13)
423 solv_chksum_add(h, userid, useridl);
424 solv_chksum_add(h, p + 2, 5);
425 cs = solv_chksum_get(h, 0);
426 solv_chksum_free(h, 0);
432 int j, ql, haveissuer;
436 unsigned int scr = 0;
438 unsigned char issuer[8];
440 /* printf("V4 signature packet\n"); */
443 if (p[1] != 0x10 && p[1] != 0x11 && p[1] != 0x12 && p[1] != 0x13 && p[1] != 0x1f)
448 for (j = 0; q && j < 2; j++)
455 ql = q[0] << 8 | q[1];
465 /* decode sub-packet length */
472 if (ql < 4 || q[0] != 0)
477 sl = q[1] << 16 | q[2] << 8 | q[3];
488 sl = ((x - 192) << 8) + *q++ + 192;
497 /* printf("%d SIGSUB %d %d\n", j, x, sl); */
498 if (x == 16 && sl == 9 && !haveissuer)
500 memcpy(issuer, q + 1, 8);
504 if (x == 2 && j == 0)
505 scr = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
507 if (x == 9 && j == 0)
508 ex = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
520 htype = REPOKEY_TYPE_MD5;
522 htype = REPOKEY_TYPE_SHA1;
524 htype = REPOKEY_TYPE_SHA256;
525 if (htype && pubkeyl)
527 void *h = solv_chksum_create(htype);
528 unsigned char b[6], *cs;
534 solv_chksum_add(h, b, 3);
535 solv_chksum_add(h, pubkey, pubkeyl);
536 if (p[1] >= 0x10 && p[1] <= 0x13)
539 b[1] = useridl >> 24;
540 b[2] = useridl >> 16;
543 solv_chksum_add(h, b, 5);
544 solv_chksum_add(h, userid, useridl);
546 hl = 6 + (p[4] << 8 | p[5]);
547 solv_chksum_add(h, p, hl);
554 solv_chksum_add(h, b, 6);
555 cs = solv_chksum_get(h, 0);
556 solv_chksum_free(h, 0);
559 if (!memcmp(keyid, issuer, 8))
561 /* printf("SELF SIG cr %d ex %d\n", cr, ex); */
567 /* printf("OTHER SIG cr %d ex %d\n", cr, ex); */
575 userid = solv_realloc(userid, l);
577 memcpy(userid, p, l);
583 repodata_set_num(data, s - s->repo->pool->solvables, PUBKEY_EXPIRES, maxex);
590 /* this is private to rpm, but rpm lacks an interface to retrieve
591 * the values. Sigh. */
592 struct pgpDigParams_s {
594 const unsigned char * hash;
595 #ifndef HAVE_PGPDIGGETPARAMS
596 const char * params[4];
599 unsigned char version; /*!< version number. */
600 unsigned char time[4]; /*!< time that the key was created. */
601 unsigned char pubkey_algo; /*!< public key algorithm. */
602 unsigned char hash_algo;
603 unsigned char sigtype;
604 unsigned char hashlen;
605 unsigned char signhash16[2];
606 unsigned char signid[8];
610 #ifndef HAVE_PGPDIGGETPARAMS
612 struct pgpDigParams_s signature;
613 struct pgpDigParams_s pubkey;
618 pubkey2solvable(Solvable *s, Repodata *data, char *pubkey)
620 Pool *pool = s->repo->pool;
626 char evrbuf[8 + 1 + 8 + 1];
627 struct pgpDigParams_s *digpubkey;
629 pkts = unarmor(pubkey, &pktsl);
632 setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_DESCRIPTION, pubkey);
633 parsekeydata(s, data, pkts, pktsl);
634 /* only rpm knows how to do the release calculation, we don't dare
635 * to recreate all the bugs */
639 dig = pgpDigNew(RPMVSF_DEFAULT, 0);
641 (void) pgpPrtPkts(pkts, pktsl, dig, 0);
643 #ifdef HAVE_PGPDIGGETPARAMS
644 digpubkey = pgpDigGetParams(dig, PGPTAG_PUBLIC_KEY);
646 digpubkey = &dig->pubkey;
648 btime = digpubkey->time[0] << 24 | digpubkey->time[1] << 16 | digpubkey->time[2] << 8 | digpubkey->signid[3];
649 sprintf(evrbuf, "%02x%02x%02x%02x-%02x%02x%02x%02x", digpubkey->signid[4], digpubkey->signid[5], digpubkey->signid[6], digpubkey->signid[7], digpubkey->time[0], digpubkey->time[1], digpubkey->time[2], digpubkey->time[3]);
651 repodata_set_num(data, s - s->repo->pool->solvables, SOLVABLE_BUILDTIME, btime);
653 s->name = pool_str2id(pool, "gpg-pubkey", 1);
654 s->evr = pool_str2id(pool, evrbuf, 1);
656 for (i = 0; i < 8; i++)
657 sprintf(keyid + 2 * i, "%02x", digpubkey->signid[i]);
658 repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_KEYID, keyid);
659 if (digpubkey->userid)
660 setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_SUMMARY, digpubkey->userid);
662 (void)pgpFreeDig(dig);
664 (void)pgpDigFree(dig);
666 solv_free((void *)pkts);
671 repo_add_rpmdb_pubkeys(Repo *repo, int flags)
673 Pool *pool = repo->pool;
679 const char *rootdir = 0;
682 data = repo_add_repodata(repo, flags);
683 if (flags & REPO_USE_ROOTDIR)
684 rootdir = pool_get_rootdir(pool);
685 state = rpm_state_create(repo->pool, rootdir);
687 rpm_installedrpmdbids(state, "Name", "gpg-pubkey", &q);
688 for (i = 0; i < q.count; i++)
691 unsigned long long itime;
693 handle = rpm_byrpmdbid(state, q.elements[i]);
696 str = rpm_query(handle, SOLVABLE_DESCRIPTION);
699 s = pool_id2solvable(pool, repo_add_solvable(repo));
700 pubkey2solvable(s, data, str);
702 itime = rpm_query_num(handle, SOLVABLE_INSTALLTIME, 0);
704 repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLTIME, itime);
706 repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
707 repo->rpmdbid[s - pool->solvables - repo->start] = q.elements[i];
710 rpm_state_free(state);
711 if (!(flags & REPO_NO_INTERNALIZE))
712 repodata_internalize(data);
717 repo_add_pubkey(Repo *repo, const char *key, int flags)
719 Pool *pool = repo->pool;
726 data = repo_add_repodata(repo, flags);
729 if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, key) : key, "r")) == 0)
731 pool_error(pool, -1, "%s: %s", key, strerror(errno));
739 buf = solv_realloc(buf, bufl);
741 ll = fread(buf, 1, bufl - l, fp);
745 pool_error(pool, -1, "%s: %s", key, strerror(errno));
754 s = pool_id2solvable(pool, repo_add_solvable(repo));
755 if (!pubkey2solvable(s, data, buf))
757 repo_free_solvable(repo, s - pool->solvables, 1);
762 if (!(flags & REPO_NO_INTERNALIZE))
763 repodata_internalize(data);
764 return s - pool->solvables;