include repo_pubkey.h in repo_pubkey.c
[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
27 #include <rpm/rpmio.h>
28 #include <rpm/rpmpgp.h>
29 #ifndef RPM5
30 #include <rpm/header.h>
31 #endif
32 #include <rpm/rpmdb.h>
33
34 #include "pool.h"
35 #include "repo.h"
36 #include "hash.h"
37 #include "util.h"
38 #include "queue.h"
39 #include "chksum.h"
40 #include "repo_rpmdb.h"
41 #include "repo_pubkey.h"
42 #ifdef ENABLE_PGPVRFY
43 #include "solv_pgpvrfy.h"
44 #endif
45
46 static void 
47 setutf8string(Repodata *repodata, Id handle, Id tag, const char *str)
48 {
49   if (str[solv_validutf8(str)])
50     {    
51       char *ustr = solv_latin1toutf8(str);      /* not utf8, assume latin1 */
52       repodata_set_str(repodata, handle, tag, ustr);
53       solv_free(ustr);
54     }    
55   else 
56     repodata_set_str(repodata, handle, tag, str);
57 }
58
59 static char *
60 r64dec1(char *p, unsigned int *vp, int *eofp)
61 {
62   int i, x;
63   unsigned int v = 0;
64
65   for (i = 0; i < 4; )
66     {
67       x = *p++;
68       if (!x)
69         return 0;
70       if (x >= 'A' && x <= 'Z')
71         x -= 'A';
72       else if (x >= 'a' && x <= 'z')
73         x -= 'a' - 26;
74       else if (x >= '0' && x <= '9')
75         x -= '0' - 52;
76       else if (x == '+')
77         x = 62;
78       else if (x == '/')
79         x = 63;
80       else if (x == '=')
81         {
82           x = 0;
83           if (i == 0)
84             {
85               *eofp = 3;
86               *vp = 0;
87               return p - 1;
88             }
89           *eofp += 1;
90         }
91       else
92         continue;
93       v = v << 6 | x;
94       i++;
95     }
96   *vp = v;
97   return p;
98 }
99
100 static unsigned int
101 crc24(unsigned char *p, int len)
102 {
103   unsigned int crc = 0xb704ceL;
104   int i;
105
106   while (len--)
107     {
108       crc ^= (*p++) << 16;
109       for (i = 0; i < 8; i++)
110         if ((crc <<= 1) & 0x1000000)
111           crc ^= 0x1864cfbL;
112     }
113   return crc & 0xffffffL;
114 }
115
116 static unsigned char *
117 unarmor(char *pubkey, int *pktlp, char *startstr, char *endstr)
118 {
119   char *p;
120   int l, eof;
121   unsigned char *buf, *bp;
122   unsigned int v;
123
124   *pktlp = 0;
125   if (!pubkey)
126     return 0;
127   l = strlen(startstr);
128   while (strncmp(pubkey, startstr, l) != 0)
129     {
130       pubkey = strchr(pubkey, '\n');
131       if (!pubkey)
132         return 0;
133       pubkey++;
134     }
135   pubkey = strchr(pubkey, '\n');
136   if (!pubkey++)
137     return 0;
138   /* skip header lines */
139   for (;;)
140     {
141       while (*pubkey == ' ' || *pubkey == '\t')
142         pubkey++;
143       if (*pubkey == '\n')
144         break;
145       pubkey = strchr(pubkey, '\n');
146       if (!pubkey++)
147         return 0;
148     }
149   pubkey++;
150   p = strchr(pubkey, '=');
151   if (!p)
152     return 0;
153   l = p - pubkey;
154   bp = buf = solv_malloc(l * 3 / 4 + 4);
155   eof = 0;
156   while (!eof)
157     {
158       pubkey = r64dec1(pubkey, &v, &eof);
159       if (!pubkey)
160         {
161           solv_free(buf);
162           return 0;
163         }
164       *bp++ = v >> 16;
165       *bp++ = v >> 8;
166       *bp++ = v;
167     }
168   while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
169     pubkey++;
170   bp -= eof;
171   if (*pubkey != '=' || (pubkey = r64dec1(pubkey + 1, &v, &eof)) == 0)
172     {
173       solv_free(buf);
174       return 0;
175     }
176   if (v != crc24(buf, bp - buf))
177     {
178       solv_free(buf);
179       return 0;
180     }
181   while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
182     pubkey++;
183   if (strncmp(pubkey, endstr, strlen(endstr)) != 0)
184     {
185       solv_free(buf);
186       return 0;
187     }
188   *pktlp = bp - buf;
189   return buf;
190 }
191
192 struct pgpsig {
193   int type;
194   Id hashalgo;
195   unsigned char issuer[8];
196   int haveissuer;
197   unsigned int created;
198   unsigned int expires;
199   unsigned int keyexpires;
200   unsigned char *sigdata;
201   int sigdatal;
202   int mpioff;
203 };
204
205 static Id
206 pgphashalgo2type(int algo)
207 {
208   if (algo == 1)
209     return REPOKEY_TYPE_MD5;
210   if (algo == 2)
211     return REPOKEY_TYPE_SHA1;
212   if (algo == 8)
213     return REPOKEY_TYPE_SHA256;
214   return 0;
215 }
216
217 static void
218 createsigdata(struct pgpsig *sig, unsigned char *p, int l, unsigned char *pubkey, int pubkeyl, unsigned char *userid, int useridl, void *h)
219 {
220   int type = sig->type;
221   unsigned char b[6];
222   const unsigned char *cs;
223   int csl;
224
225   if (sig->mpioff < 2 || l <= sig->mpioff)
226     return;
227   if ((type >= 0x10 && type <= 0x13) || type == 0x1f || type == 0x18 || type == 0x20 || type == 0x28)
228     {
229       b[0] = 0x99;
230       b[1] = pubkeyl >> 8;
231       b[2] = pubkeyl;
232       solv_chksum_add(h, b, 3);
233       solv_chksum_add(h, pubkey, pubkeyl);
234     }
235   if ((type >= 0x10 && type <= 0x13))
236     {
237       if (p[0] != 3)
238         {
239           b[0] = 0xb4;
240           b[1] = useridl >> 24;
241           b[2] = useridl >> 16;
242           b[3] = useridl >> 8;
243           b[4] = useridl;
244           solv_chksum_add(h, b, 5);
245         }
246       solv_chksum_add(h, userid, useridl);
247     }
248   /* add trailer */
249   if (p[0] == 3)
250     solv_chksum_add(h, p + 2, 5);
251   else
252     {
253       int hl = 6 + (p[4] << 8 | p[5]);
254       solv_chksum_add(h, p, hl);
255       b[0] = 4;
256       b[1] = 0xff;
257       b[2] = hl >> 24;
258       b[3] = hl >> 16;
259       b[4] = hl >> 8;
260       b[5] = hl;
261       solv_chksum_add(h, b, 6);
262     }
263   cs = solv_chksum_get(h, &csl);
264   if (cs[0] == p[sig->mpioff - 2] && cs[1] == p[sig->mpioff - 1])
265     {
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);
273     }
274 }
275
276 static inline int
277 parsesubpkglength(unsigned char *q, int ql, int *pktlp)
278 {
279   int x, sl, hl;
280   /* decode sub-packet length, ql must be > 0 */
281   x = *q++;
282   if (x < 192)
283     {
284       sl = x;
285       hl = 1;
286     }
287   else if (x == 255)
288     {
289       if (ql < 5 || q[0] != 0)
290         return 0;
291       sl = q[1] << 16 | q[2] << 8 | q[3];
292       hl = 5;
293     }
294   else
295     {
296       if (ql < 2)
297         return 0;
298       sl = ((x - 192) << 8) + q[0] + 192;
299       hl = 2;
300     }
301   if (!sl || ql < sl + hl)      /* sub pkg tag is included in length, i.e. sl must not be zero */
302     return 0;
303   *pktlp = sl;
304   return hl;
305 }
306
307 static void
308 parsesigpacket(struct pgpsig *sig, unsigned char *p, int l)
309 {
310   sig->type = -1;
311   if (p[0] == 3)
312     {
313       /* printf("V3 signature packet\n"); */
314       if (l <= 19 || p[1] != 5)
315         return;
316       sig->type = p[2];
317       sig->haveissuer = 1;
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];
321       sig->mpioff = 19;
322     }
323   else if (p[0] == 4)
324     {
325       int j, ql, x;
326       unsigned char *q;
327
328       /* printf("V4 signature packet\n"); */
329       if (l < 6)
330         return;
331       sig->type = p[1];
332       sig->hashalgo = p[3];
333       q = p + 4;
334       sig->keyexpires = -1;
335       for (j = 0; q && j < 2; j++)
336         {
337           if (q + 2 > p + l)
338             {
339               q = 0;
340               break;
341             }
342           ql = q[0] << 8 | q[1];
343           q += 2;
344           if (q + ql > p + l)
345             {
346               q = 0;
347               break;
348             }
349           while (ql > 0)
350             {
351               int sl, hl;
352               hl = parsesubpkglength(q, ql, &sl);
353               if (!hl)
354                 {
355                   q = 0;
356                   break;
357                 }
358               q += hl;
359               ql -= hl;
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)
363                 {
364                   sig->haveissuer = 1;
365                   memcpy(sig->issuer, q + 1, 8);
366                 }
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];
373               q += sl;
374               ql -= sl;
375             }
376         }
377       if (q && q - p + 2 < l)
378         sig->mpioff = q - p + 2;
379     }
380 }
381
382 static int
383 parsepkgheader(unsigned char *p, int pl, int *tagp, int *pktlp)
384 {
385   unsigned char *op = p;
386   int x, l;
387
388   if (!pl)
389     return 0;
390   x = *p++;
391   pl--;
392   if (!(x & 128) || pl <= 0)
393     return 0;
394   if ((x & 64) == 0)
395     {
396       *tagp = (x & 0x3c) >> 2;          /* old format */
397       x = 1 << (x & 3);
398       if (x > 4 || pl < x || (x == 4 && p[0]))
399         return 0;
400       pl -= x;
401       for (l = 0; x--;)
402         l = l << 8 | *p++;
403     }
404   else
405     {
406       *tagp = (x & 0x3f);               /* new format */
407       x = *p++;
408       pl--;
409       if (x < 192)
410         l = x;
411       else if (x >= 192 && x < 224)
412         {
413           if (pl <= 0)
414             return 0;
415           l = ((x - 192) << 8) + *p++ + 192;
416           pl--;
417         }
418       else if (x == 255)
419         {
420           if (pl <= 4 || p[0] != 0)     /* sanity: p[0] must be zero */
421             return 0;
422           l = p[1] << 16 | p[2] << 8 | p[3];
423           p += 4;
424           pl -= 4;
425         }
426       else
427         return 0;
428     }
429   if (l > pl)
430     return 0;
431   *pktlp = l;
432   return p - op;
433 }
434
435
436 static void
437 parsekeydata(Solvable *s, Repodata *data, unsigned char *p, int pl)
438 {
439   int tag, l;
440   unsigned char keyid[8];
441   unsigned int kcr = 0, maxex = 0, maxsigcr = 0;
442   unsigned char *pubkey = 0;
443   int pubkeyl = 0;
444   unsigned char *userid = 0;
445   int useridl = 0;
446   unsigned char *pubdata = 0;
447   int pubdatal = 0;
448
449   for (; pl; p += l, pl -= l)
450     {
451       int hl = parsepkgheader(p, pl, &tag, &l);
452       if (!hl)
453         break;
454       p += hl;
455       pl -= hl;
456       if (tag == 6)
457         {
458           if (pubkey)
459             break;      /* one key at a time, please */
460           pubkey = solv_malloc(l);
461           if (l)
462             memcpy(pubkey, p, l);
463           pubkeyl = l;
464           if (p[0] == 3 && l >= 10)
465             {
466               unsigned int ex;
467               void *h;
468               maxsigcr = kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
469               ex = 0;
470               if (p[5] || p[6])
471                 {
472                   ex = kcr + 24*3600 * (p[5] << 8 | p[6]);
473                   if (ex > maxex)
474                     maxex = ex;
475                 }
476               memset(keyid, 0, 8);
477               if (p[7] == 1)    /* RSA */
478                 {
479                   int ql, ql2;
480                   unsigned char fp[16];
481                   char fpx[32 + 1];
482                   unsigned char *q;
483
484                   ql = ((p[8] << 8 | p[9]) + 7) / 8;            /* length of public modulus */
485                   if (ql >= 8 && 10 + ql + 2 <= l)
486                     {
487                       memcpy(keyid, p + 10 + ql - 8, 8);        /* keyid is last 64 bits of public modulus */
488                       q = p + 10 + ql;
489                       ql2 = ((q[0] << 8 | q[1]) + 7) / 8;       /* length of encryption exponent */
490                       if (10 + ql + 2 + ql2 <= l)
491                         {
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);
499                         }
500                     }
501                   pubdata = p + 7;
502                   pubdatal = l - 7;
503                 }
504             }
505           else if (p[0] == 4 && l >= 6)
506             {
507               void *h;
508               unsigned char hdr[3];
509               unsigned char fp[20];
510               char fpx[40 + 1];
511
512               maxsigcr = kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
513               hdr[0] = 0x99;
514               hdr[1] = l >> 8;
515               hdr[2] = l;
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 */
524               pubdata = p + 5;
525               pubdatal = l - 5;
526             }
527         }
528       if (tag == 2)
529         {
530           struct pgpsig sig;
531           Id htype;
532           if (!pubdata)
533             continue;
534           memset(&sig, 0, sizeof(sig));
535           parsesigpacket(&sig, p, l);
536           if (!sig.haveissuer || !((sig.type >= 0x10 && sig.type <= 0x13) || sig.type == 0x1f))
537             continue;
538           if (sig.type >= 0x10 && sig.type <= 0x13 && !userid)
539             continue;
540           htype = pgphashalgo2type(sig.hashalgo);
541           if (htype && sig.mpioff)
542             {
543               void *h = solv_chksum_create(htype);
544               createsigdata(&sig, p, l, pubkey, pubkeyl, userid, useridl, h);
545               solv_chksum_free(h, 0);
546             }
547           if (!memcmp(keyid, sig.issuer, 8))
548             {
549 #ifdef ENABLE_PGPVRFY
550               /* found self sig, verify */
551               if (solv_pgpvrfy(pubdata, pubdatal, sig.sigdata, sig.sigdatal))
552 #endif
553                 {
554                   if (sig.keyexpires && maxex != -1)
555                     {
556                       if (sig.keyexpires == -1)
557                         maxex = -1;
558                       else if (sig.keyexpires + kcr > maxex)
559                         maxex = sig.keyexpires + kcr;
560                     }
561                   if (sig.created > maxsigcr)
562                     maxsigcr = sig.created;
563                 }
564             }
565           else
566             {
567               char issuerstr[17];
568               Id shandle = repodata_new_handle(data);
569               solv_bin2hex(sig.issuer, 8, issuerstr);
570               repodata_set_str(data, shandle, SIGNATURE_ISSUER, issuerstr);
571               if (sig.created)
572                 repodata_set_num(data, shandle, SIGNATURE_TIME, sig.created);
573               if (sig.expires)
574                 repodata_set_num(data, shandle, SIGNATURE_EXPIRES, sig.created + sig.expires);
575               if (sig.sigdata)
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);
578             }
579           solv_free(sig.sigdata);
580         }
581       if (tag == 13)
582         {
583           userid = solv_realloc(userid, l);
584           if (l)
585             memcpy(userid, p, l);
586           useridl = l;
587         }
588     }
589   if (kcr)
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);
594   s->evr = 1;
595   s->arch = 1;
596   if (userid && useridl)
597     {
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);
602       free(useridstr);
603     }
604   if (pubdata)
605     {
606       char keyidstr[17];
607       solv_bin2hex(keyid, 8, keyidstr);
608       repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_KEYID, keyidstr);
609     }
610   if (pubdata)
611     {
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);
617       /* set data blob */
618       repodata_set_binary(data, s - s->repo->pool->solvables, PUBKEY_DATA, pubdata, pubdatal);
619     }
620   solv_free(pubkey);
621   solv_free(userid);
622 }
623
624
625 #ifdef ENABLE_RPMDB
626
627 /* this is private to rpm, but rpm lacks an interface to retrieve
628  * the values. Sigh. */
629 struct pgpDigParams_s {
630     const char * userid;
631     const unsigned char * hash;
632 #ifndef HAVE_PGPDIGGETPARAMS
633     const char * params[4];
634 #endif
635     unsigned char tag;
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];
644     unsigned char saved;
645 };
646
647 #ifndef HAVE_PGPDIGGETPARAMS
648 struct pgpDig_s {
649     struct pgpDigParams_s signature;
650     struct pgpDigParams_s pubkey;
651 };
652 #endif
653
654
655 /* only rpm knows how to do the release calculation, we don't dare
656  * to recreate all the bugs in libsolv */
657 static void
658 parsekeydata_rpm(Solvable *s, Repodata *data, unsigned char *pkts, int pktsl)
659 {
660   Pool *pool = s->repo->pool;
661   struct pgpDigParams_s *digpubkey;
662   pgpDig dig = 0;
663   char keyid[16 + 1];
664   char evrbuf[8 + 1 + 8 + 1];
665   unsigned int btime;
666
667 #ifndef RPM5
668   dig = pgpNewDig();
669 #else
670   dig = pgpDigNew(RPMVSF_DEFAULT, 0);
671 #endif
672   (void) pgpPrtPkts(pkts, pktsl, dig, 0);
673 #ifdef HAVE_PGPDIGGETPARAMS
674   digpubkey = pgpDigGetParams(dig, PGPTAG_PUBLIC_KEY);
675 #else
676   digpubkey = &dig->pubkey;
677 #endif
678   if (digpubkey)
679     {
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);
683       evrbuf[8] = '-';
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);
689       if (btime)
690         repodata_set_num(data, s - s->repo->pool->solvables, SOLVABLE_BUILDTIME, btime);
691     }
692 #ifndef RPM5
693   (void)pgpFreeDig(dig);
694 #else
695   (void)pgpDigFree(dig);
696 #endif
697 }
698
699 #endif  /* ENABLE_RPMDB */
700
701 static int
702 pubkey2solvable(Solvable *s, Repodata *data, char *pubkey)
703 {
704   unsigned char *pkts;
705   int pktsl;
706
707   pkts = unarmor(pubkey, &pktsl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----");
708   if (!pkts)
709     {
710       pool_error(s->repo->pool, 0, "unarmor failure");
711       return 0;
712     }
713   setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_DESCRIPTION, pubkey);
714   parsekeydata(s, data, pkts, pktsl);
715 #ifdef ENABLE_RPMDB
716   parsekeydata_rpm(s, data, pkts, pktsl);
717 #endif
718   solv_free((void *)pkts);
719   return 1;
720 }
721
722 #ifdef ENABLE_RPMDB
723
724 int
725 repo_add_rpmdb_pubkeys(Repo *repo, int flags)
726 {
727   Pool *pool = repo->pool;
728   Queue q;
729   int i;
730   char *str;
731   Repodata *data;
732   Solvable *s;
733   const char *rootdir = 0;
734   void *state;
735
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);
740   queue_init(&q);
741   rpm_installedrpmdbids(state, "Name", "gpg-pubkey", &q);
742   for (i = 0; i < q.count; i++)
743     {
744       void *handle;
745       unsigned long long itime;
746
747       handle = rpm_byrpmdbid(state, q.elements[i]);
748       if (!handle)
749         continue;
750       str = rpm_query(handle, SOLVABLE_DESCRIPTION);
751       if (!str)
752         continue;
753       s = pool_id2solvable(pool, repo_add_solvable(repo));
754       pubkey2solvable(s, data, str);
755       solv_free(str);
756       itime = rpm_query_num(handle, SOLVABLE_INSTALLTIME, 0);
757       if (itime)
758         repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLTIME, itime);
759       if (!repo->rpmdbid)
760         repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
761       repo->rpmdbid[s - pool->solvables - repo->start] = q.elements[i];
762     }
763   queue_free(&q);
764   rpm_state_free(state);
765   if (!(flags & REPO_NO_INTERNALIZE))
766     repodata_internalize(data);
767   return 0;
768 }
769
770 #endif
771
772 static char *
773 solv_slurp(FILE *fp, int *lenp)
774 {
775   int l, ll;
776   char *buf = 0;
777   int bufl = 0;
778
779   for (l = 0; ; l += ll)
780     {
781       if (bufl - l < 4096)
782         {
783           bufl += 4096;
784           buf = solv_realloc(buf, bufl);
785         }
786       ll = fread(buf + l, 1, bufl - l, fp);
787       if (ll < 0)
788         {
789           buf = solv_free(buf);
790           l = 0;
791           break;
792         }
793       if (ll == 0)
794         {
795           buf[l] = 0;   /* always zero-terminate */
796           break;
797         }
798     }
799   if (lenp)
800     *lenp = l;
801   return buf;
802 }
803
804 Id
805 repo_add_pubkey(Repo *repo, const char *key, int flags)
806 {
807   Pool *pool = repo->pool;
808   Repodata *data;
809   Solvable *s;
810   char *buf;
811   FILE *fp;
812
813   data = repo_add_repodata(repo, flags);
814   buf = 0;
815   if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, key) : key, "r")) == 0)
816     {
817       pool_error(pool, -1, "%s: %s", key, strerror(errno));
818       return 0;
819     }
820   if ((buf = solv_slurp(fp, 0)) == 0)
821     {
822       pool_error(pool, -1, "%s: %s", key, strerror(errno));
823       fclose(fp);
824       return 0;
825     }
826   fclose(fp);
827   s = pool_id2solvable(pool, repo_add_solvable(repo));
828   if (!pubkey2solvable(s, data, buf))
829     {
830       repo_free_solvable(repo, s - pool->solvables, 1);
831       solv_free(buf);
832       return 0;
833     }
834   solv_free(buf);
835   if (!(flags & REPO_NO_INTERNALIZE))
836     repodata_internalize(data);
837   return s - pool->solvables;
838 }
839
840 static int
841 is_sig_packet(unsigned char *sig, int sigl)
842 {
843   if (!sigl)
844     return 0;
845   if ((sig[0] & 0x80) == 0 || (sig[0] & 0x40 ? sig[0] & 0x3f : sig[0] >> 2 & 0x0f) != 2)
846     return 0;
847   return 1;
848 }
849
850 Id
851 solv_parse_sig(FILE *fp, unsigned char **sigpkgp, int *sigpkglp, char *keyidstr)
852 {
853   unsigned char *sig;
854   int sigl, hl, tag, pktl;
855   struct pgpsig pgpsig;
856   Id htype;
857
858   if (sigpkgp)
859     {
860       *sigpkgp = 0;
861       *sigpkglp = 0;
862     }
863   if ((sig = (unsigned char *)solv_slurp(fp, &sigl)) == 0)
864     return 0;
865   if (!is_sig_packet(sig, sigl))
866     {
867       /* not a raw sig, check armored */
868       unsigned char *nsig;
869       nsig = unarmor((char *)sig, &sigl, "-----BEGIN PGP SIGNATURE-----", "-----END PGP SIGNATURE-----");
870       solv_free(sig);
871       if (!nsig)
872         return 0;
873       sig = nsig;
874       if (!is_sig_packet(sig, sigl))
875         {
876           solv_free(sig);
877           return 0;
878         }
879     }
880   hl = parsepkgheader(sig, sigl, &tag, &pktl);
881   if (!hl || tag != 2 || !pktl)
882     {
883       solv_free(sig);
884       return 0;
885     }
886   memset(&pgpsig, 0, sizeof(pgpsig));
887   parsesigpacket(&pgpsig, sig + hl, pktl);
888   htype = pgphashalgo2type(pgpsig.hashalgo);
889   if (pgpsig.type != 0 || !htype)
890     {
891       solv_free(sig);
892       return 0;
893     }
894   if (sigpkgp)
895     {
896       *sigpkgp = solv_malloc(pktl);
897       memcpy(*sigpkgp, sig + hl, pktl);
898       *sigpkglp = pktl;
899     }
900   solv_free(sig);
901   if (keyidstr)
902     solv_bin2hex(pgpsig.issuer, 8, keyidstr);
903   return htype;
904 }
905
906 #ifdef ENABLE_PGPVRFY
907 int
908 solv_verify_sig(const unsigned char *pubdata, int pubdatal, unsigned char *sigpkg, int sigpkgl, void *chk)
909 {
910   struct pgpsig pgpsig;
911   int res;
912   Id htype;
913
914   memset(&pgpsig, 0, sizeof(pgpsig));
915   parsesigpacket(&pgpsig, sigpkg, sigpkgl);
916   if (pgpsig.type != 0)
917     return 0;
918   htype = pgphashalgo2type(pgpsig.hashalgo);
919   if (htype != solv_chksum_get_type(chk))
920      return 0;  /* wrong hash type? */
921   createsigdata(&pgpsig, sigpkg, sigpkgl, 0, 0, 0, 0, chk);
922   if (!pgpsig.sigdata)
923     return 0;
924   res = solv_pgpvrfy(pubdata, pubdatal, pgpsig.sigdata, pgpsig.sigdatal);
925   solv_free(pgpsig.sigdata);
926   return res;
927 }
928 #endif
929