support ADD_WITH_SUBKEYS/ADD_MULTIPLE_PUBKEYS/ADD_WITH_KEYSIGNATURES
[platform/upstream/libsolv.git] / ext / repo_pubkey.c
1 /*
2  * Copyright (c) 2007-2013, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * repo_pubkey
10  *
11  * support for pubkey reading
12  *
13  */
14
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <limits.h>
18 #include <fcntl.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <assert.h>
24 #include <stdint.h>
25 #include <errno.h>
26 #include <dirent.h>
27
28 #include <rpm/rpmio.h>
29 #include <rpm/rpmpgp.h>
30 #ifndef RPM5
31 #include <rpm/header.h>
32 #endif
33 #include <rpm/rpmdb.h>
34
35 #include "pool.h"
36 #include "repo.h"
37 #include "hash.h"
38 #include "util.h"
39 #include "queue.h"
40 #include "chksum.h"
41 #include "repo_rpmdb.h"
42 #include "repo_pubkey.h"
43 #ifdef ENABLE_PGPVRFY
44 #include "solv_pgpvrfy.h"
45 #endif
46
47 static void
48 setutf8string(Repodata *repodata, Id handle, Id tag, const char *str)
49 {
50   if (str[solv_validutf8(str)])
51     {
52       char *ustr = solv_latin1toutf8(str);      /* not utf8, assume latin1 */
53       repodata_set_str(repodata, handle, tag, ustr);
54       solv_free(ustr);
55     }
56   else
57     repodata_set_str(repodata, handle, tag, str);
58 }
59
60 static char *
61 r64dec1(char *p, unsigned int *vp, int *eofp)
62 {
63   int i, x;
64   unsigned int v = 0;
65
66   for (i = 0; i < 4; )
67     {
68       x = *p++;
69       if (!x)
70         return 0;
71       if (x >= 'A' && x <= 'Z')
72         x -= 'A';
73       else if (x >= 'a' && x <= 'z')
74         x -= 'a' - 26;
75       else if (x >= '0' && x <= '9')
76         x -= '0' - 52;
77       else if (x == '+')
78         x = 62;
79       else if (x == '/')
80         x = 63;
81       else if (x == '=')
82         {
83           x = 0;
84           if (i == 0)
85             {
86               *eofp = 3;
87               *vp = 0;
88               return p - 1;
89             }
90           *eofp += 1;
91         }
92       else
93         continue;
94       v = v << 6 | x;
95       i++;
96     }
97   *vp = v;
98   return p;
99 }
100
101 static unsigned int
102 crc24(unsigned char *p, int len)
103 {
104   unsigned int crc = 0xb704ce;
105   int i;
106
107   while (len--)
108     {
109       crc ^= (*p++) << 16;
110       for (i = 0; i < 8; i++)
111         if ((crc <<= 1) & 0x1000000)
112           crc ^= 0x1864cfb;
113     }
114   return crc & 0xffffff;
115 }
116
117 static int
118 unarmor(char *pubkey, unsigned char **pktp, int *pktlp, const char *startstr, const char *endstr)
119 {
120   char *p, *pubkeystart = pubkey;
121   int l, eof;
122   unsigned char *buf, *bp;
123   unsigned int v;
124
125   *pktp = 0;
126   *pktlp = 0;
127   if (!pubkey)
128     return 0;
129   l = strlen(startstr);
130   while (strncmp(pubkey, startstr, l) != 0)
131     {
132       pubkey = strchr(pubkey, '\n');
133       if (!pubkey)
134         return 0;
135       pubkey++;
136     }
137   pubkey = strchr(pubkey, '\n');
138   if (!pubkey++)
139     return 0;
140   /* skip header lines */
141   for (;;)
142     {
143       while (*pubkey == ' ' || *pubkey == '\t')
144         pubkey++;
145       if (*pubkey == '\n')
146         break;
147       pubkey = strchr(pubkey, '\n');
148       if (!pubkey++)
149         return 0;
150     }
151   pubkey++;
152   p = strchr(pubkey, '=');
153   if (!p)
154     return 0;
155   l = p - pubkey;
156   bp = buf = solv_malloc(l * 3 / 4 + 4);
157   eof = 0;
158   while (!eof)
159     {
160       pubkey = r64dec1(pubkey, &v, &eof);
161       if (!pubkey)
162         {
163           solv_free(buf);
164           return 0;
165         }
166       *bp++ = v >> 16;
167       *bp++ = v >> 8;
168       *bp++ = v;
169     }
170   while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
171     pubkey++;
172   bp -= eof;
173   if (*pubkey != '=' || (pubkey = r64dec1(pubkey + 1, &v, &eof)) == 0)
174     {
175       solv_free(buf);
176       return 0;
177     }
178   if (v != crc24(buf, bp - buf))
179     {
180       solv_free(buf);
181       return 0;
182     }
183   while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
184     pubkey++;
185   if (strncmp(pubkey, endstr, strlen(endstr)) != 0)
186     {
187       solv_free(buf);
188       return 0;
189     }
190   p = strchr(pubkey, '\n');
191   if (!p)
192     p = pubkey + strlen(pubkey);
193   *pktp = buf;
194   *pktlp = bp - buf;
195   return (p ? p + 1 : pubkey + strlen(pubkey)) - pubkeystart;
196 }
197
198 #define ARMOR_NLAFTER   16
199
200 static char *
201 armor(unsigned char *pkt, int pktl, const char *startstr, const char *endstr, const char *version)
202 {
203   static const char bintoasc[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
204   char *str = solv_malloc(strlen(startstr) + strlen(endstr) + strlen(version) + (pktl / 3) * 4 + (pktl / (ARMOR_NLAFTER * 3)) + 30);
205   char *p = str;
206   int a, b, c, i;
207   unsigned int v;
208
209   v = crc24(pkt, pktl);
210   sprintf(p, "%s\nVersion: %s\n\n", startstr, version);
211   p += strlen(p);
212   for (i = -1; pktl > 0; pktl -= 3)
213     {
214       if (++i == ARMOR_NLAFTER)
215         {
216           i = 0;
217           *p++ = '\n';
218         }
219       a = *pkt++;
220       b = pktl > 1 ? *pkt++ : 0;
221       c = pktl > 2 ? *pkt++ : 0;
222       *p++ = bintoasc[a >> 2];
223       *p++ = bintoasc[(a & 3) << 4 | b >> 4];
224       *p++ = pktl > 1 ? bintoasc[(b & 15) << 2 | c >> 6] : '=';
225       *p++ = pktl > 2 ? bintoasc[c & 63] : '=';
226     }
227   *p++ = '\n';
228   *p++ = '=';
229   *p++ = bintoasc[v >> 18 & 0x3f];
230   *p++ = bintoasc[v >> 12 & 0x3f];
231   *p++ = bintoasc[v >>  6 & 0x3f];
232   *p++ = bintoasc[v       & 0x3f];
233   sprintf(p, "\n%s\n", endstr);
234   return str;
235 }
236
237 struct pgpsig {
238   int type;
239   Id hashalgo;
240   unsigned char issuer[8];
241   int haveissuer;
242   unsigned int created;
243   unsigned int expires;
244   unsigned int keyexpires;
245   unsigned char *sigdata;
246   int sigdatal;
247   int mpioff;
248 };
249
250 static Id
251 pgphashalgo2type(int algo)
252 {
253   if (algo == 1)
254     return REPOKEY_TYPE_MD5;
255   if (algo == 2)
256     return REPOKEY_TYPE_SHA1;
257   if (algo == 8)
258     return REPOKEY_TYPE_SHA256;
259   return 0;
260 }
261
262 static void
263 createsigdata(struct pgpsig *sig, unsigned char *p, int l, unsigned char *pubkey, int pubkeyl, unsigned char *userid, int useridl, void *h)
264 {
265   int type = sig->type;
266   unsigned char b[6];
267   const unsigned char *cs;
268   int csl;
269
270   if (!h || sig->mpioff < 2 || l <= sig->mpioff)
271     return;
272   if ((type >= 0x10 && type <= 0x13) || type == 0x1f || type == 0x18 || type == 0x20 || type == 0x28)
273     {
274       b[0] = 0x99;
275       b[1] = pubkeyl >> 8;
276       b[2] = pubkeyl;
277       solv_chksum_add(h, b, 3);
278       solv_chksum_add(h, pubkey, pubkeyl);
279     }
280   if ((type >= 0x10 && type <= 0x13))
281     {
282       if (p[0] != 3)
283         {
284           b[0] = 0xb4;
285           b[1] = useridl >> 24;
286           b[2] = useridl >> 16;
287           b[3] = useridl >> 8;
288           b[4] = useridl;
289           solv_chksum_add(h, b, 5);
290         }
291       solv_chksum_add(h, userid, useridl);
292     }
293   /* add trailer */
294   if (p[0] == 3)
295     solv_chksum_add(h, p + 2, 5);
296   else
297     {
298       int hl = 6 + (p[4] << 8 | p[5]);
299       solv_chksum_add(h, p, hl);
300       b[0] = 4;
301       b[1] = 0xff;
302       b[2] = hl >> 24;
303       b[3] = hl >> 16;
304       b[4] = hl >> 8;
305       b[5] = hl;
306       solv_chksum_add(h, b, 6);
307     }
308   cs = solv_chksum_get(h, &csl);
309   if (cs[0] == p[sig->mpioff - 2] && cs[1] == p[sig->mpioff - 1])
310     {
311       int ml = l - sig->mpioff;
312       sig->sigdata = solv_malloc(2 + csl + ml);
313       sig->sigdatal = 2 + csl + ml;
314       sig->sigdata[0] = p[0] == 3 ? p[15] : p[2];
315       sig->sigdata[1] = p[0] == 3 ? p[16] : p[3];
316       memcpy(sig->sigdata + 2, cs, csl);
317       memcpy(sig->sigdata + 2 + csl, p + sig->mpioff, ml);
318     }
319 }
320
321 static inline int
322 parsesubpkglength(unsigned char *q, int ql, int *pktlp)
323 {
324   int x, sl, hl;
325   /* decode sub-packet length, ql must be > 0 */
326   x = *q++;
327   if (x < 192)
328     {
329       sl = x;
330       hl = 1;
331     }
332   else if (x == 255)
333     {
334       if (ql < 5 || q[0] != 0)
335         return 0;
336       sl = q[1] << 16 | q[2] << 8 | q[3];
337       hl = 5;
338     }
339   else
340     {
341       if (ql < 2)
342         return 0;
343       sl = ((x - 192) << 8) + q[0] + 192;
344       hl = 2;
345     }
346   if (!sl || ql < sl + hl)      /* sub pkg tag is included in length, i.e. sl must not be zero */
347     return 0;
348   *pktlp = sl;
349   return hl;
350 }
351
352 static void
353 parsesigpacket(struct pgpsig *sig, unsigned char *p, int l)
354 {
355   sig->type = -1;
356   if (p[0] == 3)
357     {
358       /* printf("V3 signature packet\n"); */
359       if (l <= 19 || p[1] != 5)
360         return;
361       sig->type = p[2];
362       sig->haveissuer = 1;
363       memcpy(sig->issuer, p + 7, 8);
364       sig->created = p[3] << 24 | p[4] << 16 | p[5] << 8 | p[6];
365       sig->hashalgo = p[16];
366       sig->mpioff = 19;
367     }
368   else if (p[0] == 4)
369     {
370       int j, ql, x;
371       unsigned char *q;
372
373       /* printf("V4 signature packet\n"); */
374       if (l < 6)
375         return;
376       sig->type = p[1];
377       sig->hashalgo = p[3];
378       q = p + 4;
379       sig->keyexpires = -1;
380       for (j = 0; q && j < 2; j++)
381         {
382           if (q + 2 > p + l)
383             {
384               q = 0;
385               break;
386             }
387           ql = q[0] << 8 | q[1];
388           q += 2;
389           if (q + ql > p + l)
390             {
391               q = 0;
392               break;
393             }
394           while (ql > 0)
395             {
396               int sl, hl;
397               hl = parsesubpkglength(q, ql, &sl);
398               if (!hl)
399                 {
400                   q = 0;
401                   break;
402                 }
403               q += hl;
404               ql -= hl;
405               x = q[0] & 127;   /* strip critical bit */
406               /* printf("%d SIGSUB %d %d\n", j, x, sl); */
407               if (x == 16 && sl == 9 && !sig->haveissuer)
408                 {
409                   sig->haveissuer = 1;
410                   memcpy(sig->issuer, q + 1, 8);
411                 }
412               if (x == 2 && j == 0)
413                 sig->created = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
414               if (x == 3 && j == 0)
415                 sig->expires = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
416               if (x == 9 && j == 0)
417                 sig->keyexpires = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
418               q += sl;
419               ql -= sl;
420             }
421         }
422       if (q && q - p + 2 < l)
423         sig->mpioff = q - p + 2;
424     }
425 }
426
427 static int
428 parsepkgheader(unsigned char *p, int pl, int *tagp, int *pktlp)
429 {
430   unsigned char *op = p;
431   int x, l;
432
433   if (!pl)
434     return 0;
435   x = *p++;
436   pl--;
437   if (!(x & 128) || pl <= 0)
438     return 0;
439   if ((x & 64) == 0)
440     {
441       *tagp = (x & 0x3c) >> 2;          /* old format */
442       x = 1 << (x & 3);
443       if (x > 4 || pl < x || (x == 4 && p[0]))
444         return 0;
445       pl -= x;
446       for (l = 0; x--;)
447         l = l << 8 | *p++;
448     }
449   else
450     {
451       *tagp = (x & 0x3f);               /* new format */
452       x = *p++;
453       pl--;
454       if (x < 192)
455         l = x;
456       else if (x >= 192 && x < 224)
457         {
458           if (pl <= 0)
459             return 0;
460           l = ((x - 192) << 8) + *p++ + 192;
461           pl--;
462         }
463       else if (x == 255)
464         {
465           if (pl <= 4 || p[0] != 0)     /* sanity: p[0] must be zero */
466             return 0;
467           l = p[1] << 16 | p[2] << 8 | p[3];
468           p += 4;
469           pl -= 4;
470         }
471       else
472         return 0;
473     }
474   if (l > pl)
475     return 0;
476   *pktlp = l;
477   return p - op;
478 }
479
480 /* parse the first pubkey (possible creating new packages for the subkeys)
481  * returns the number of parsed bytes */
482 static int
483 parsekeydata(Solvable *s, Repodata *data, unsigned char *p, int pl, int flags)
484 {
485   Repo *repo = s->repo;
486   unsigned char *pstart = p;
487   int tag, l;
488   unsigned char keyid[8];
489   unsigned int kcr = 0, maxex = 0, maxsigcr = 0;
490   unsigned char *pubkey = 0;
491   int pubkeyl = 0;
492   int insubkey = 0;
493   unsigned char *userid = 0;
494   int useridl = 0;
495   unsigned char *pubdata = 0;
496   int pubdatal = 0;
497
498   for (; ; p += l, pl -= l)
499     {
500       int hl = parsepkgheader(p, pl, &tag, &l);
501       if (!hl || (pubkey && (tag == 6 || tag == 14)))
502         {
503           /* finish old key */
504           if (kcr)
505             repodata_set_num(data, s - repo->pool->solvables, SOLVABLE_BUILDTIME, kcr);
506           if (maxex && maxex != -1)
507             repodata_set_num(data, s - repo->pool->solvables, PUBKEY_EXPIRES, maxex);
508           s->name = pool_str2id(s->repo->pool, insubkey ? "gpg-subkey" : "gpg-pubkey", 1);
509           s->evr = 1;
510           s->arch = 1;
511           if (userid && useridl)
512             {
513               char *useridstr = solv_malloc(useridl + 1);
514               memcpy(useridstr, userid, useridl);
515               useridstr[useridl] = 0;
516               setutf8string(data, s - repo->pool->solvables, SOLVABLE_SUMMARY, useridstr);
517               free(useridstr);
518             }
519           if (pubdata)
520             {
521               char keyidstr[17];
522               char evr[8 + 1 + 8 + 1];
523               solv_bin2hex(keyid, 8, keyidstr);
524               repodata_set_str(data, s - repo->pool->solvables, PUBKEY_KEYID, keyidstr);
525               /* build rpm-style evr */
526               strcpy(evr, keyidstr + 8);
527               sprintf(evr + 8, "-%08x", maxsigcr);
528               s->evr = pool_str2id(repo->pool, evr, 1);
529             }
530           if (pubdata)          /* set data blob */
531             repodata_set_binary(data, s - repo->pool->solvables, PUBKEY_DATA, pubdata, pubdatal);
532           if (!pl)
533             break;
534           if (!hl)
535             {
536               p = 0;    /* parse error */
537               break;
538             }
539           if (tag == 6 || (tag == 14 && !(flags & ADD_WITH_SUBKEYS)))
540             break;
541           s = pool_id2solvable(repo->pool, repo_add_solvable(repo));
542         }
543       p += hl;
544       pl -= hl;
545       if (!pubkey && tag != 6)
546         continue;
547       if (tag == 6 || (tag == 14 && (flags & ADD_WITH_SUBKEYS) != 0))           /* Public-Key Packet */
548         {
549           if (tag == 6)
550             {
551               pubkey = solv_memdup(p, l);
552               pubkeyl = l;
553             }
554           else
555             insubkey = 1;
556           if (p[0] == 3 && l >= 10)
557             {
558               unsigned int ex;
559               void *h;
560               maxsigcr = kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
561               ex = 0;
562               if (p[5] || p[6])
563                 {
564                   ex = kcr + 24*3600 * (p[5] << 8 | p[6]);
565                   if (ex > maxex)
566                     maxex = ex;
567                 }
568               memset(keyid, 0, 8);
569               if (p[7] == 1)    /* RSA */
570                 {
571                   int ql, ql2;
572                   unsigned char fp[16];
573                   char fpx[32 + 1];
574                   unsigned char *q;
575
576                   ql = ((p[8] << 8 | p[9]) + 7) / 8;            /* length of public modulus */
577                   if (ql >= 8 && 10 + ql + 2 <= l)
578                     {
579                       memcpy(keyid, p + 10 + ql - 8, 8);        /* keyid is last 64 bits of public modulus */
580                       q = p + 10 + ql;
581                       ql2 = ((q[0] << 8 | q[1]) + 7) / 8;       /* length of encryption exponent */
582                       if (10 + ql + 2 + ql2 <= l)
583                         {
584                           /* fingerprint is the md5 over the two MPI bodies */
585                           h = solv_chksum_create(REPOKEY_TYPE_MD5);
586                           solv_chksum_add(h, p + 10, ql);
587                           solv_chksum_add(h, q + 2, ql2);
588                           solv_chksum_free(h, fp);
589                           solv_bin2hex(fp, 16, fpx);
590                           repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
591                         }
592                     }
593                   pubdata = p + 7;
594                   pubdatal = l - 7;
595                 }
596             }
597           else if (p[0] == 4 && l >= 6)
598             {
599               void *h;
600               unsigned char hdr[3];
601               unsigned char fp[20];
602               char fpx[40 + 1];
603
604               maxsigcr = kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
605               hdr[0] = 0x99;
606               hdr[1] = l >> 8;
607               hdr[2] = l;
608               /* fingerprint is the sha1 over the packet */
609               h = solv_chksum_create(REPOKEY_TYPE_SHA1);
610               solv_chksum_add(h, hdr, 3);
611               solv_chksum_add(h, p, l);
612               solv_chksum_free(h, fp);
613               solv_bin2hex(fp, 20, fpx);
614               repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
615               memcpy(keyid, fp + 12, 8);        /* keyid is last 64 bits of fingerprint */
616               pubdata = p + 5;
617               pubdatal = l - 5;
618             }
619         }
620       if (tag == 2)             /* Signature Packet */
621         {
622           struct pgpsig sig;
623           Id htype;
624           if (!pubdata)
625             continue;
626           memset(&sig, 0, sizeof(sig));
627           parsesigpacket(&sig, p, l);
628           if (!sig.haveissuer || !((sig.type >= 0x10 && sig.type <= 0x13) || sig.type == 0x1f))
629             continue;
630           if (sig.type >= 0x10 && sig.type <= 0x13 && !userid)
631             continue;
632           htype = pgphashalgo2type(sig.hashalgo);
633           if (htype && sig.mpioff)
634             {
635               void *h = solv_chksum_create(htype);
636               createsigdata(&sig, p, l, pubkey, pubkeyl, userid, useridl, h);
637               solv_chksum_free(h, 0);
638             }
639           if (!memcmp(keyid, sig.issuer, 8))
640             {
641 #ifdef ENABLE_PGPVRFY
642               /* found self sig, verify */
643               if (solv_pgpvrfy(pubdata, pubdatal, sig.sigdata, sig.sigdatal))
644 #endif
645                 {
646                   if (sig.keyexpires && maxex != -1)
647                     {
648                       if (sig.keyexpires == -1)
649                         maxex = -1;
650                       else if (sig.keyexpires + kcr > maxex)
651                         maxex = sig.keyexpires + kcr;
652                     }
653                   if (sig.created > maxsigcr)
654                     maxsigcr = sig.created;
655                 }
656             }
657           else if (flags & ADD_WITH_KEYSIGNATURES)
658             {
659               char issuerstr[17];
660               Id shandle = repodata_new_handle(data);
661               solv_bin2hex(sig.issuer, 8, issuerstr);
662               repodata_set_str(data, shandle, SIGNATURE_ISSUER, issuerstr);
663               if (sig.created)
664                 repodata_set_num(data, shandle, SIGNATURE_TIME, sig.created);
665               if (sig.expires)
666                 repodata_set_num(data, shandle, SIGNATURE_EXPIRES, sig.created + sig.expires);
667               if (sig.sigdata)
668                 repodata_set_binary(data, shandle, SIGNATURE_DATA, sig.sigdata, sig.sigdatal);
669               repodata_add_flexarray(data, s - s->repo->pool->solvables, PUBKEY_SIGNATURES, shandle);
670             }
671           solv_free(sig.sigdata);
672         }
673       if (tag == 13 && !insubkey)               /* User ID Packet */
674         {
675           userid = solv_realloc(userid, l);
676           if (l)
677             memcpy(userid, p, l);
678           useridl = l;
679         }
680     }
681   solv_free(pubkey);
682   solv_free(userid);
683   return p ? p - pstart : 0;
684 }
685
686
687 #ifdef ENABLE_RPMDB
688
689 /* this is private to rpm, but rpm lacks an interface to retrieve
690  * the values. Sigh. */
691 struct pgpDigParams_s {
692     const char * userid;
693     const unsigned char * hash;
694 #ifndef HAVE_PGPDIGGETPARAMS
695     const char * params[4];
696 #endif
697     unsigned char tag;
698     unsigned char version;               /*!< version number. */
699     unsigned char time[4];               /*!< time that the key was created. */
700     unsigned char pubkey_algo;           /*!< public key algorithm. */
701     unsigned char hash_algo;
702     unsigned char sigtype;
703     unsigned char hashlen;
704     unsigned char signhash16[2];
705     unsigned char signid[8];
706     unsigned char saved;
707 };
708
709 #ifndef HAVE_PGPDIGGETPARAMS
710 struct pgpDig_s {
711     struct pgpDigParams_s signature;
712     struct pgpDigParams_s pubkey;
713 };
714 #endif
715
716
717 /* only rpm knows how to do the release calculation, we don't dare
718  * to recreate all the bugs in libsolv */
719 static void
720 parsekeydata_rpm(Solvable *s, Repodata *data, unsigned char *pkts, int pktsl)
721 {
722   Pool *pool = s->repo->pool;
723   struct pgpDigParams_s *digpubkey;
724   pgpDig dig = 0;
725   char keyid[16 + 1];
726   char evrbuf[8 + 1 + 8 + 1];
727   unsigned int btime;
728
729 #ifndef RPM5
730   dig = pgpNewDig();
731 #else
732   dig = pgpDigNew(RPMVSF_DEFAULT, 0);
733 #endif
734   (void) pgpPrtPkts(pkts, pktsl, dig, 0);
735 #ifdef HAVE_PGPDIGGETPARAMS
736   digpubkey = pgpDigGetParams(dig, PGPTAG_PUBLIC_KEY);
737 #else
738   digpubkey = &dig->pubkey;
739 #endif
740   if (digpubkey)
741     {
742       btime = digpubkey->time[0] << 24 | digpubkey->time[1] << 16 | digpubkey->time[2] << 8 | digpubkey->time[3];
743       solv_bin2hex(digpubkey->signid, 8, keyid);
744       solv_bin2hex(digpubkey->signid + 4, 4, evrbuf);
745       evrbuf[8] = '-';
746       solv_bin2hex(digpubkey->time, 4, evrbuf + 9);
747       s->evr = pool_str2id(pool, evrbuf, 1);
748       repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_KEYID, keyid);
749       if (digpubkey->userid)
750         setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_SUMMARY, digpubkey->userid);
751       if (btime)
752         repodata_set_num(data, s - s->repo->pool->solvables, SOLVABLE_BUILDTIME, btime);
753     }
754 #ifndef RPM5
755   (void)pgpFreeDig(dig);
756 #else
757   (void)pgpDigFree(dig);
758 #endif
759 }
760
761 #endif  /* ENABLE_RPMDB */
762
763 static int
764 pubkey2solvable(Pool *pool, Id p, Repodata *data, char *pubkey, int flags)
765 {
766   unsigned char *pkts, *pkts_orig;
767   int pktsl, pl = 0, tag, l, hl;
768
769   if (!unarmor(pubkey, &pkts, &pktsl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----"))
770     {
771       pool_error(pool, 0, "unarmor failure");
772       return 0;
773     }
774   pkts_orig = pkts;
775   tag = 6;
776   while (pktsl)
777     {
778       if (tag == 6)
779         {
780           setutf8string(data, p, SOLVABLE_DESCRIPTION, pubkey);
781           pl = parsekeydata(pool->solvables + p, data, pkts, pktsl, flags);
782 #ifdef ENABLE_RPMDB
783           parsekeydata_rpm(pool->solvables + p, data, pkts, pktsl);
784 #endif
785           if (!pl || !(flags & ADD_MULTIPLE_PUBKEYS))
786             break;
787         }
788       pkts += pl;
789       pktsl -= pl;
790       hl = parsepkgheader(pkts, pktsl, &tag, &l);
791       if (!hl)
792         break;
793       pl = l + hl;
794       if (tag == 6)
795         p = repo_add_solvable(pool->solvables[p].repo);
796     }
797   solv_free((void *)pkts_orig);
798   return 1;
799 }
800
801 #ifdef ENABLE_RPMDB
802
803 int
804 repo_add_rpmdb_pubkeys(Repo *repo, int flags)
805 {
806   Pool *pool = repo->pool;
807   Queue q;
808   int i;
809   char *str;
810   Repodata *data;
811   const char *rootdir = 0;
812   void *state;
813
814   data = repo_add_repodata(repo, flags);
815   if (flags & REPO_USE_ROOTDIR)
816     rootdir = pool_get_rootdir(pool);
817   state = rpm_state_create(repo->pool, rootdir);
818   queue_init(&q);
819   rpm_installedrpmdbids(state, "Name", "gpg-pubkey", &q);
820   for (i = 0; i < q.count; i++)
821     {
822       Id p, p2;
823       void *handle;
824       unsigned long long itime;
825
826       handle = rpm_byrpmdbid(state, q.elements[i]);
827       if (!handle)
828         continue;
829       str = rpm_query(handle, SOLVABLE_DESCRIPTION);
830       if (!str)
831         continue;
832       p = repo_add_solvable(repo);
833       if (!pubkey2solvable(pool, p, data, str, flags))
834         {
835           solv_free(str);
836           repo_free_solvable(repo, p, 1);
837           continue;
838         }
839       solv_free(str);
840       itime = rpm_query_num(handle, SOLVABLE_INSTALLTIME, 0);
841       for (p2 = p; p2 < pool->nsolvables; p2++)
842         {
843           if (itime)
844             repodata_set_num(data, p2, SOLVABLE_INSTALLTIME, itime);
845           if (!repo->rpmdbid)
846             repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
847           repo->rpmdbid[p2 - repo->start] = q.elements[i];
848         }
849     }
850   queue_free(&q);
851   rpm_state_free(state);
852   if (!(flags & REPO_NO_INTERNALIZE))
853     repodata_internalize(data);
854   return 0;
855 }
856
857 #endif
858
859 static char *
860 solv_slurp(FILE *fp, int *lenp)
861 {
862   int l, ll;
863   char *buf = 0;
864   int bufl = 0;
865
866   for (l = 0; ; l += ll)
867     {
868       if (bufl - l < 4096)
869         {
870           bufl += 4096;
871           buf = solv_realloc(buf, bufl);
872         }
873       ll = fread(buf + l, 1, bufl - l, fp);
874       if (ll < 0)
875         {
876           buf = solv_free(buf);
877           l = 0;
878           break;
879         }
880       if (ll == 0)
881         {
882           buf[l] = 0;   /* always zero-terminate */
883           break;
884         }
885     }
886   if (lenp)
887     *lenp = l;
888   return buf;
889 }
890
891 Id
892 repo_add_pubkey(Repo *repo, const char *key, int flags)
893 {
894   Pool *pool = repo->pool;
895   Repodata *data;
896   Id p;
897   char *buf;
898   FILE *fp;
899
900   data = repo_add_repodata(repo, flags);
901   buf = 0;
902   if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, key) : key, "r")) == 0)
903     {
904       pool_error(pool, -1, "%s: %s", key, strerror(errno));
905       return 0;
906     }
907   if ((buf = solv_slurp(fp, 0)) == 0)
908     {
909       pool_error(pool, -1, "%s: %s", key, strerror(errno));
910       fclose(fp);
911       return 0;
912     }
913   fclose(fp);
914   p = repo_add_solvable(repo);
915   if (!pubkey2solvable(pool, p, data, buf, flags))
916     {
917       repo_free_solvable(repo, p, 1);
918       solv_free(buf);
919       return 0;
920     }
921   if (!(flags & REPO_NO_LOCATION))
922     {
923       Id p2;
924       for (p2 = p; p2 < pool->nsolvables; p2++)
925         repodata_set_location(data, p2, 0, 0, key);
926     }
927   solv_free(buf);
928   if (!(flags & REPO_NO_INTERNALIZE))
929     repodata_internalize(data);
930   return p;
931 }
932
933 static int
934 is_sig_packet(unsigned char *sig, int sigl)
935 {
936   if (!sigl)
937     return 0;
938   if ((sig[0] & 0x80) == 0 || (sig[0] & 0x40 ? sig[0] & 0x3f : sig[0] >> 2 & 0x0f) != 2)
939     return 0;
940   return 1;
941 }
942
943 static int
944 is_pubkey_packet(unsigned char *pkt, int pktl)
945 {
946   if (!pktl)
947     return 0;
948   if ((pkt[0] & 0x80) == 0 || (pkt[0] & 0x40 ? pkt[0] & 0x3f : pkt[0] >> 2 & 0x0f) != 6)
949     return 0;
950   return 1;
951 }
952
953 static void
954 add_one_pubkey(Pool *pool, Repo *repo, Repodata *data, unsigned char *pbuf, int pbufl, int flags)
955 {
956   Id p = repo_add_solvable(repo);
957   char *solvversion = pool_tmpjoin(pool, "libsolv-", LIBSOLV_VERSION_STRING, 0);
958   char *descr = armor(pbuf, pbufl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----", solvversion);
959   setutf8string(data, p, SOLVABLE_DESCRIPTION, descr);
960   parsekeydata(pool->solvables + p, data, pbuf, pbufl, flags);
961 #ifdef ENABLE_RPMDB
962   parsekeydata_rpm(pool->solvables + p, data, pbuf, pbufl);
963 #endif
964 }
965
966 int
967 repo_add_keyring(Repo *repo, FILE *fp, int flags)
968 {
969   Pool *pool = repo->pool;
970   Repodata *data;
971   unsigned char *buf, *p, *pbuf;
972   int bufl, l, pl, pbufl;
973
974   data = repo_add_repodata(repo, flags);
975   buf = (unsigned char *)solv_slurp(fp, &bufl);
976   if (buf && !is_pubkey_packet(buf, bufl))
977     {
978       /* assume ascii armored */
979       unsigned char *nbuf = 0, *ubuf;
980       int nl, ubufl;
981       bufl = 0;
982       for (l = 0; (nl = unarmor((char *)buf + l, &ubuf, &ubufl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----")) != 0; l += nl)
983         {
984           /* found another block. concat. */
985           nbuf = solv_realloc(nbuf, bufl + ubufl);
986           if (ubufl)
987             memcpy(nbuf + bufl, ubuf, ubufl);
988           bufl += ubufl;
989           solv_free(ubuf);
990         }
991       solv_free(buf);
992       buf = nbuf;
993     }
994   /* now split into pubkey parts, ignoring the packets we don't know */
995   pbuf = 0;
996   pbufl = 0;
997   for (p = buf; bufl; p += pl, bufl -= pl)
998     {
999       int tag;
1000       int hl = parsepkgheader(p, bufl, &tag, &pl);
1001       if (!hl)
1002         break;
1003       pl += hl;
1004       if (tag == 6)
1005         {
1006           /* found new pubkey! flush old */
1007           if (pbufl)
1008             {
1009               add_one_pubkey(pool, repo, data, pbuf, pbufl, flags);
1010               pbuf = solv_free(pbuf);
1011               pbufl = 0;
1012             }
1013         }
1014       if (tag != 6 && !pbufl)
1015         continue;
1016       if (tag != 6 && tag != 2 && tag != 13 && tag != 14 && tag != 17)
1017         continue;
1018       /* we want that packet. concat. */
1019       pbuf = solv_realloc(pbuf, pbufl + pl);
1020       memcpy(pbuf + pbufl, p, pl);
1021       pbufl += pl;
1022     }
1023   if (pbufl)
1024     add_one_pubkey(pool, repo, data, pbuf, pbufl, flags);
1025   solv_free(pbuf);
1026   solv_free(buf);
1027   if (!(flags & REPO_NO_INTERNALIZE))
1028     repodata_internalize(data);
1029   return 0;
1030 }
1031
1032 int
1033 repo_add_keydir(Repo *repo, const char *keydir, int flags, const char *suffix)
1034 {
1035   Pool *pool = repo->pool;
1036   Repodata *data;
1037   int i, nent, sl;
1038   struct dirent **namelist;
1039   char *rkeydir;
1040
1041   data = repo_add_repodata(repo, flags);
1042   nent = scandir(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, keydir) : keydir, &namelist, 0, alphasort);
1043   if (nent == -1)
1044     return pool_error(pool, -1, "%s: %s", keydir, strerror(errno));
1045   rkeydir = pool_prepend_rootdir(pool, keydir);
1046   sl = suffix ? strlen(suffix) : 0;
1047   for (i = 0; i < nent; i++)
1048     {
1049       const char *dn = namelist[i]->d_name;
1050       int l;
1051       if (*dn == '.' && !(flags & ADD_KEYDIR_WITH_DOTFILES))
1052         continue;
1053       l = strlen(dn);
1054       if (sl && (l < sl || strcmp(dn + l - sl, suffix) != 0))
1055         continue;
1056       repo_add_pubkey(repo, pool_tmpjoin(pool, rkeydir, "/", dn), flags);
1057     }
1058   solv_free(rkeydir);
1059   for (i = 0; i < nent; i++)
1060     solv_free(namelist[i]);
1061   solv_free(namelist);
1062   if (!(flags & REPO_NO_INTERNALIZE))
1063     repodata_internalize(data);
1064   return 0;
1065 }
1066
1067 Solvsig *
1068 solvsig_create(FILE *fp)
1069 {
1070   Solvsig *ss;
1071   unsigned char *sig;
1072   int sigl, hl, tag, pktl;
1073   struct pgpsig pgpsig;
1074
1075   if ((sig = (unsigned char *)solv_slurp(fp, &sigl)) == 0)
1076     return 0;
1077   if (!is_sig_packet(sig, sigl))
1078     {
1079       /* not a raw sig, check armored */
1080       unsigned char *nsig;
1081       if (!unarmor((char *)sig, &nsig, &sigl, "-----BEGIN PGP SIGNATURE-----", "-----END PGP SIGNATURE-----"))
1082         {
1083           solv_free(sig);
1084           return 0;
1085         }
1086       solv_free(sig);
1087       sig = nsig;
1088       if (!is_sig_packet(sig, sigl))
1089         {
1090           solv_free(sig);
1091           return 0;
1092         }
1093     }
1094   hl = parsepkgheader(sig, sigl, &tag, &pktl);
1095   if (!hl || tag != 2 || !pktl)
1096     {
1097       solv_free(sig);
1098       return 0;
1099     }
1100   memset(&pgpsig, 0, sizeof(pgpsig));
1101   parsesigpacket(&pgpsig, sig + hl, pktl);
1102   if (pgpsig.type != 0 || !pgpsig.haveissuer)
1103     {
1104       solv_free(sig);
1105       return 0;
1106     }
1107   ss = solv_calloc(1, sizeof(*ss));
1108   ss->sigpkt = solv_memdup(sig + hl, pktl);
1109   ss->sigpktl = pktl;
1110   solv_free(sig);
1111   solv_bin2hex(pgpsig.issuer, 8, ss->keyid);
1112   ss->htype = pgphashalgo2type(pgpsig.hashalgo);
1113   ss->created = pgpsig.created;
1114   ss->expires = pgpsig.expires;
1115   return ss;
1116 }
1117
1118 void
1119 solvsig_free(Solvsig *ss)
1120 {
1121   solv_free(ss->sigpkt);
1122   solv_free(ss);
1123 }
1124
1125 #ifdef ENABLE_PGPVRFY
1126
1127 int
1128 solv_verify_sig(const unsigned char *pubdata, int pubdatal, unsigned char *sigpkt, int sigpktl, void *chk)
1129 {
1130   struct pgpsig pgpsig;
1131   int res;
1132   Id htype;
1133   void *chk2;
1134
1135   memset(&pgpsig, 0, sizeof(pgpsig));
1136   parsesigpacket(&pgpsig, sigpkt, sigpktl);
1137   if (pgpsig.type != 0)
1138     return 0;
1139   htype = pgphashalgo2type(pgpsig.hashalgo);
1140   if (!htype || htype != solv_chksum_get_type(chk))
1141      return 0;  /* wrong hash type? */
1142   chk2 = solv_chksum_create_clone(chk);
1143   createsigdata(&pgpsig, sigpkt, sigpktl, 0, 0, 0, 0, chk2);
1144   solv_chksum_free(chk2, 0);
1145   if (!pgpsig.sigdata)
1146     return 0;
1147   res = solv_pgpvrfy(pubdata, pubdatal, pgpsig.sigdata, pgpsig.sigdatal);
1148   solv_free(pgpsig.sigdata);
1149   return res;
1150 }
1151
1152 /* warning: does not check key expiry/revokation, like gpgv or rpm */
1153 /* returns the Id of the pubkey that verified the signature */
1154 Id
1155 repo_verify_sigdata(Repo *repo, unsigned char *sigdata, int sigdatal, const char *keyid)
1156 {
1157   Id p;
1158   Solvable *s;
1159
1160   if (!sigdata || !keyid)
1161     return 0;
1162   FOR_REPO_SOLVABLES(repo, p, s)
1163     {
1164       const char *evr = pool_id2str(s->repo->pool, s->evr);
1165       const char *kidstr;
1166       const unsigned char *pubdata;
1167       int pubdatal;
1168
1169       if (!evr || strncmp(evr, keyid + 8, 8) != 0)
1170        continue;
1171       kidstr = solvable_lookup_str(s, PUBKEY_KEYID);
1172       if (!kidstr || strcmp(kidstr, keyid) != 0)
1173         continue;
1174       pubdata = repo_lookup_binary(repo, p, PUBKEY_DATA, &pubdatal);
1175       if (solv_pgpvrfy(pubdata, pubdatal, sigdata, sigdatal))
1176         return p;
1177     }
1178   return 0;
1179 }
1180
1181 Id
1182 solvsig_verify(Solvsig *ss, Repo *repo, void *chk)
1183 {
1184   struct pgpsig pgpsig;
1185   void *chk2;
1186   Id p;
1187
1188   parsesigpacket(&pgpsig, ss->sigpkt, ss->sigpktl);
1189   chk2 = solv_chksum_create_clone(chk);
1190   createsigdata(&pgpsig, ss->sigpkt, ss->sigpktl, 0, 0, 0, 0, chk2);
1191   solv_chksum_free(chk2, 0);
1192   if (!pgpsig.sigdata)
1193     return 0;
1194   p = repo_verify_sigdata(repo, pgpsig.sigdata, pgpsig.sigdatal, ss->keyid);
1195   solv_free(pgpsig.sigdata);
1196   return p;
1197 }
1198
1199 #endif
1200