make unarmor() more flexible, make sure pgp mpis are minimal
[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 #ifdef ENABLE_PGPVRFY
42 #include "solv_pgpvrfy.h"
43 #endif
44
45 static void 
46 setutf8string(Repodata *repodata, Id handle, Id tag, const char *str)
47 {
48   if (str[solv_validutf8(str)])
49     {    
50       char *ustr = solv_latin1toutf8(str);      /* not utf8, assume latin1 */
51       repodata_set_str(repodata, handle, tag, ustr);
52       solv_free(ustr);
53     }    
54   else 
55     repodata_set_str(repodata, handle, tag, str);
56 }
57
58 static char *
59 r64dec1(char *p, unsigned int *vp, int *eofp)
60 {
61   int i, x;
62   unsigned int v = 0;
63
64   for (i = 0; i < 4; )
65     {
66       x = *p++;
67       if (!x)
68         return 0;
69       if (x >= 'A' && x <= 'Z')
70         x -= 'A';
71       else if (x >= 'a' && x <= 'z')
72         x -= 'a' - 26;
73       else if (x >= '0' && x <= '9')
74         x -= '0' - 52;
75       else if (x == '+')
76         x = 62;
77       else if (x == '/')
78         x = 63;
79       else if (x == '=')
80         {
81           x = 0;
82           if (i == 0)
83             {
84               *eofp = 3;
85               *vp = 0;
86               return p - 1;
87             }
88           *eofp += 1;
89         }
90       else
91         continue;
92       v = v << 6 | x;
93       i++;
94     }
95   *vp = v;
96   return p;
97 }
98
99 static unsigned int
100 crc24(unsigned char *p, int len)
101 {
102   unsigned int crc = 0xb704ceL;
103   int i;
104
105   while (len--)
106     {
107       crc ^= (*p++) << 16;
108       for (i = 0; i < 8; i++)
109         if ((crc <<= 1) & 0x1000000)
110           crc ^= 0x1864cfbL;
111     }
112   return crc & 0xffffffL;
113 }
114
115 static unsigned char *
116 unarmor(char *pubkey, int *pktlp, char *startstr, char *endstr)
117 {
118   char *p;
119   int l, eof;
120   unsigned char *buf, *bp;
121   unsigned int v;
122
123   *pktlp = 0;
124   l = strlen(startstr);
125   while (strncmp(pubkey, startstr, l) != 0)
126     {
127       pubkey = strchr(pubkey, '\n');
128       if (!pubkey)
129         return 0;
130       pubkey++;
131     }
132   pubkey = strchr(pubkey, '\n');
133   if (!pubkey++)
134     return 0;
135   /* skip header lines */
136   for (;;)
137     {
138       while (*pubkey == ' ' || *pubkey == '\t')
139         pubkey++;
140       if (*pubkey == '\n')
141         break;
142       pubkey = strchr(pubkey, '\n');
143       if (!pubkey++)
144         return 0;
145     }
146   pubkey++;
147   p = strchr(pubkey, '=');
148   if (!p)
149     return 0;
150   l = p - pubkey;
151   bp = buf = solv_malloc(l * 3 / 4 + 4);
152   eof = 0;
153   while (!eof)
154     {
155       pubkey = r64dec1(pubkey, &v, &eof);
156       if (!pubkey)
157         {
158           solv_free(buf);
159           return 0;
160         }
161       *bp++ = v >> 16;
162       *bp++ = v >> 8;
163       *bp++ = v;
164     }
165   while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
166     pubkey++;
167   bp -= eof;
168   if (*pubkey != '=' || (pubkey = r64dec1(pubkey + 1, &v, &eof)) == 0)
169     {
170       solv_free(buf);
171       return 0;
172     }
173   if (v != crc24(buf, bp - buf))
174     {
175       solv_free(buf);
176       return 0;
177     }
178   while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
179     pubkey++;
180   if (strncmp(pubkey, endstr, strlen(endstr)) != 0)
181     {
182       solv_free(buf);
183       return 0;
184     }
185   *pktlp = bp - buf;
186   return buf;
187 }
188
189 struct gpgsig {
190   int type;
191   unsigned char issuer[8];
192   int haveissuer;
193   unsigned int created;
194   unsigned int expires;
195   unsigned int keyexpires;
196   unsigned char *sigdata;
197   int sigdatal;
198 };
199
200 static void
201 parsesigpacket(struct gpgsig *sig, unsigned char *p, int l, unsigned char *pubkey, int pubkeyl, unsigned char *userid, int useridl)
202 {
203   sig->type = -1;
204   if (p[0] == 3)
205     {
206       Id htype = 0;
207       /* printf("V3 signature packet\n"); */
208       if (l <= 19 || p[1] != 5)
209         return;
210       if (p[2] != 0x10 && p[2] != 0x11 && p[2] != 0x12 && p[2] != 0x13 && p[2] != 0x1f)
211         return;
212       sig->type = p[2];
213       sig->haveissuer = 1;
214       memcpy(sig->issuer, p + 7, 8);
215       sig->created = p[3] << 24 | p[4] << 16 | p[5] << 8 | p[6];
216       if (p[16] == 1)
217         htype = REPOKEY_TYPE_MD5;
218       else if (p[16] == 2)
219         htype = REPOKEY_TYPE_SHA1;
220       else if (p[16] == 8)
221         htype = REPOKEY_TYPE_SHA256;
222       if (htype && pubkey)
223         {
224           void *h = solv_chksum_create(htype);
225           const unsigned char *cs;
226           unsigned char b[3];
227           int csl;
228
229           if ((p[2] >= 0x10 && p[2] <= 0x13) || p[2] == 0x1f || p[2] == 0x18 || p[2] == 0x20 || p[2] == 0x28)
230             {
231               b[0] = 0x99;
232               b[1] = pubkeyl >> 8;
233               b[2] = pubkeyl;
234               solv_chksum_add(h, b, 3);
235               solv_chksum_add(h, pubkey, pubkeyl);
236             }
237           if (p[2] >= 0x10 && p[2] <= 0x13)
238             solv_chksum_add(h, userid, useridl);
239           solv_chksum_add(h, p + 2, 5);
240           cs = solv_chksum_get(h, &csl);
241           if (cs[0] == p[17] && cs[1] == p[18])
242             {
243               sig->sigdata = solv_malloc(2 + csl + l - 19);
244               sig->sigdatal = 2 + csl + l - 19;
245               sig->sigdata[0] = p[15];
246               sig->sigdata[1] = p[16];
247               memcpy(sig->sigdata + 2, cs, csl);
248               memcpy(sig->sigdata + 2 + csl, p + 19, l - 19);
249             }
250           solv_chksum_free(h, 0);
251         }
252     }
253   else if (p[0] == 4)
254     {
255       int j, ql, x;
256       unsigned char *q;
257       Id htype = 0;
258
259       /* printf("V4 signature packet\n"); */
260       if (l < 6)
261         return;
262       if (p[1] != 0x10 && p[1] != 0x11 && p[1] != 0x12 && p[1] != 0x13 && p[1] != 0x1f)
263         return;
264       sig->type = p[1];
265       q = p + 4;
266       sig->keyexpires = -1;
267       for (j = 0; q && j < 2; j++)
268         {
269           if (q + 2 > p + l)
270             {
271               q = 0;
272               break;
273             }
274           ql = q[0] << 8 | q[1];
275           q += 2;
276           if (q + ql > p + l)
277             {
278               q = 0;
279               break;
280             }
281           while (ql)
282             {
283               int sl;
284               /* decode sub-packet length */
285               x = *q++;
286               ql--;
287               if (x < 192)
288                 sl = x;
289               else if (x == 255)
290                 {
291                   if (ql < 4 || q[0] != 0)
292                     {
293                       q = 0;
294                       break;
295                     }
296                   sl = q[1] << 16 | q[2] << 8 | q[3];
297                   q += 4;
298                   ql -= 4;
299                 }
300               else
301                 {
302                   if (ql < 1)
303                     {
304                       q = 0;
305                       break;
306                     }
307                   sl = ((x - 192) << 8) + *q++ + 192;
308                   ql--;
309                 }
310               if (ql < sl)
311                 {
312                   q = 0;
313                   break;
314                 }
315               x = q[0] & 127;
316               /* printf("%d SIGSUB %d %d\n", j, x, sl); */
317               if (x == 16 && sl == 9 && !sig->haveissuer)
318                 {
319                   sig->haveissuer = 1;
320                   memcpy(sig->issuer, q + 1, 8);
321                 }
322               if (x == 2 && j == 0)
323                 sig->created = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
324               if (x == 3 && j == 0)
325                 sig->expires = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
326               if (x == 9 && j == 0)
327                 sig->keyexpires = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
328               q += sl;
329               ql -= sl;
330             }
331         }
332       if (p[3] == 1)
333         htype = REPOKEY_TYPE_MD5;
334       else if (p[3] == 2)
335         htype = REPOKEY_TYPE_SHA1;
336       else if (p[3] == 8)
337         htype = REPOKEY_TYPE_SHA256;
338       if (sig->haveissuer && htype && pubkey && q && q - p + 2 < l)
339         {
340           void *h = solv_chksum_create(htype);
341           unsigned char b[6];
342           const unsigned char *cs;
343           unsigned int hl;
344           int csl, ml = l - (q - p + 2);
345
346           if ((p[1] >= 0x10 && p[1] <= 0x13) || p[1] == 0x1f || p[1] == 0x18 || p[1] == 0x20 || p[1] == 0x28)
347             {
348               b[0] = 0x99;
349               b[1] = pubkeyl >> 8;
350               b[2] = pubkeyl;
351               solv_chksum_add(h, b, 3);
352               solv_chksum_add(h, pubkey, pubkeyl);
353             }
354           if (p[1] >= 0x10 && p[1] <= 0x13)
355             {
356               b[0] = 0xb4;
357               b[1] = useridl >> 24;
358               b[2] = useridl >> 16;
359               b[3] = useridl >> 8;
360               b[4] = useridl;
361               solv_chksum_add(h, b, 5);
362               solv_chksum_add(h, userid, useridl);
363             }
364           hl = 6 + (p[4] << 8 | p[5]);
365           solv_chksum_add(h, p, hl);
366           b[0] = 4;
367           b[1] = 0xff;
368           b[2] = hl >> 24;
369           b[3] = hl >> 16;
370           b[4] = hl >> 8;
371           b[5] = hl;
372           solv_chksum_add(h, b, 6);
373           cs = solv_chksum_get(h, &csl);
374           if (cs[0] == q[0] && cs[1] == q[1])
375             {
376               sig->sigdata = solv_malloc(2 + csl + ml);
377               sig->sigdatal = 2 + csl + ml;
378               sig->sigdata[0] = p[2];
379               sig->sigdata[1] = p[3];
380               memcpy(sig->sigdata + 2, cs, csl);
381               memcpy(sig->sigdata + 2 + csl, q + 2, ml);
382             }
383           solv_chksum_free(h, 0);
384         }
385     }
386 }
387
388 static void
389 parsekeydata(Solvable *s, Repodata *data, unsigned char *p, int pl)
390 {
391   int x, tag, l;
392   unsigned char keyid[8];
393   unsigned int kcr = 0, maxex = 0, maxsigcr = 0;
394   unsigned char *pubkey = 0;
395   int pubkeyl = 0;
396   unsigned char *userid = 0;
397   int useridl = 0;
398   unsigned char *pubdata = 0;
399   int pubdatal = 0;
400
401   for (; pl; p += l, pl -= l)
402     {
403       x = *p++;
404       pl--;
405       if (!(x & 128) || pl <= 0)
406         return;
407       if ((x & 64) == 0)
408         {
409           /* old format */
410           tag = (x & 0x3c) >> 2;
411           x &= 3;
412           if (x == 3)
413             return;
414           l = 1 << x;
415           if (pl < l)
416             return;
417           x = 0;
418           while (l--)
419             {
420               x = x << 8 | *p++;
421               pl--;
422             }
423           l = x;
424         }
425       else
426         {
427           tag = (x & 0x3f);
428           x = *p++;
429           pl--;
430           if (x < 192)
431             l = x;
432           else if (x >= 192 && x < 224)
433             {
434               if (pl <= 0)
435                 return;
436               l = ((x - 192) << 8) + *p++ + 192;
437               pl--;
438             }
439           else if (x == 255)
440             {
441               /* sanity: p[0] must be zero */
442               if (pl <= 4 || p[0] != 0)
443                 return;
444               l = p[1] << 16 | p[2] << 8 | p[3];
445               p += 4;
446               pl -= 4;
447             }
448           else
449             return;
450         }
451       if (pl < l)
452         return;
453       if (tag == 6)
454         {
455           if (pubkey)
456             break;      /* one key at a time, please */
457           pubkey = solv_malloc(l);
458           if (l)
459             memcpy(pubkey, p, l);
460           pubkeyl = l;
461           if (p[0] == 3 && l >= 10)
462             {
463               unsigned int ex;
464               void *h;
465               maxsigcr = kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
466               ex = 0;
467               if (p[5] || p[6])
468                 {
469                   ex = kcr + 24*3600 * (p[5] << 8 | p[6]);
470                   if (ex > maxex)
471                     maxex = ex;
472                 }
473               memset(keyid, 0, 8);
474               if (p[7] == 1)    /* RSA */
475                 {
476                   int ql, ql2;
477                   unsigned char fp[16];
478                   char fpx[32 + 1];
479                   unsigned char *q;
480
481                   ql = ((p[8] << 8 | p[9]) + 7) / 8;            /* length of public modulus */
482                   if (ql >= 8 && 10 + ql + 2 <= l)
483                     {
484                       memcpy(keyid, p + 10 + ql - 8, 8);        /* keyid is last 64 bits of public modulus */
485                       q = p + 10 + ql;
486                       ql2 = ((q[0] << 8 | q[1]) + 7) / 8;       /* length of encryption exponent */
487                       if (10 + ql + 2 + ql2 <= l)
488                         {
489                           /* fingerprint is the md5 over the two MPI bodies */
490                           h = solv_chksum_create(REPOKEY_TYPE_MD5);
491                           solv_chksum_add(h, p + 10, ql);
492                           solv_chksum_add(h, q + 2, ql2);
493                           solv_chksum_free(h, fp);
494                           solv_bin2hex(fp, 16, fpx);
495                           repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
496                         }
497                     }
498                   pubdata = p + 7;
499                   pubdatal = l - 7;
500                 }
501             }
502           else if (p[0] == 4 && l >= 6)
503             {
504               void *h;
505               unsigned char hdr[3];
506               unsigned char fp[20];
507               char fpx[40 + 1];
508
509               maxsigcr = kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
510               hdr[0] = 0x99;
511               hdr[1] = l >> 8;
512               hdr[2] = l;
513               /* fingerprint is the sha1 over the packet */
514               h = solv_chksum_create(REPOKEY_TYPE_SHA1);
515               solv_chksum_add(h, hdr, 3);
516               solv_chksum_add(h, p, l);
517               solv_chksum_free(h, fp);
518               solv_bin2hex(fp, 20, fpx);
519               repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
520               memcpy(keyid, fp + 12, 8);        /* keyid is last 64 bits of fingerprint */
521               pubdata = p + 5;
522               pubdatal = l - 5;
523             }
524         }
525       if (tag == 2)
526         {
527           struct gpgsig sig;
528           if (!pubdata)
529             continue;
530           memset(&sig, 0, sizeof(sig));
531           parsesigpacket(&sig, p, l, pubkey, pubkeyl, userid, useridl);
532           if (!sig.haveissuer || !((sig.type >= 0x10 && sig.type <= 0x13) || sig.type == 0x1f))
533             {
534               solv_free(sig.sigdata);
535               continue;
536             }
537           if (!memcmp(keyid, sig.issuer, 8))
538             {
539 #ifdef ENABLE_PGPVRFY
540               /* found self sig, verify */
541               if (solv_pgpvrfy(pubdata, pubdatal, sig.sigdata, sig.sigdatal))
542 #endif
543                 {
544                   if (sig.keyexpires && maxex != -1)
545                     {
546                       if (sig.keyexpires == -1)
547                         maxex = -1;
548                       else if (sig.keyexpires + kcr > maxex)
549                         maxex = sig.keyexpires + kcr;
550                     }
551                   if (sig.created > maxsigcr)
552                     maxsigcr = sig.created;
553                 }
554             }
555           else
556             {
557               char issuerstr[17];
558               Id shandle = repodata_new_handle(data);
559               solv_bin2hex(sig.issuer, 8, issuerstr);
560               repodata_set_str(data, shandle, SIGNATURE_ISSUER, issuerstr);
561               if (sig.created)
562                 repodata_set_num(data, shandle, SIGNATURE_TIME, sig.created);
563               if (sig.expires)
564                 repodata_set_num(data, shandle, SIGNATURE_EXPIRES, sig.created + sig.expires);
565               if (sig.sigdata)
566                 repodata_set_binary(data, shandle, SIGNATURE_DATA, sig.sigdata, sig.sigdatal);
567               repodata_add_flexarray(data, s - s->repo->pool->solvables, PUBKEY_SIGNATURES, shandle);
568             }
569           solv_free(sig.sigdata);
570         }
571       if (tag == 13)
572         {
573           userid = solv_realloc(userid, l);
574           if (l)
575             memcpy(userid, p, l);
576           useridl = l;
577         }
578     }
579   if (kcr)
580     repodata_set_num(data, s - s->repo->pool->solvables, SOLVABLE_BUILDTIME, kcr);
581   if (maxex && maxex != -1)
582     repodata_set_num(data, s - s->repo->pool->solvables, PUBKEY_EXPIRES, maxex);
583   s->name = pool_str2id(s->repo->pool, "gpg-pubkey", 1);
584   s->evr = 1;
585   s->arch = 1;
586   if (userid && useridl)
587     {
588       char *useridstr = solv_malloc(useridl + 1);
589       memcpy(useridstr, userid, useridl);
590       useridstr[useridl] = 0;
591       setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_SUMMARY, useridstr);
592       free(useridstr);
593     }
594   if (pubdata)
595     {
596       char keyidstr[17];
597       solv_bin2hex(keyid, 8, keyidstr);
598       repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_KEYID, keyidstr);
599     }
600   if (pubdata)
601     {
602       /* build rpm-style evr */
603       char evr[8 + 1 + 8 + 1];
604       solv_bin2hex(keyid + 4, 4, evr);
605       sprintf(evr + 8, "-%08x", maxsigcr);
606       s->evr = pool_str2id(s->repo->pool, evr, 1);
607       /* set data blob */
608       repodata_set_binary(data, s - s->repo->pool->solvables, PUBKEY_DATA, pubdata, pubdatal);
609     }
610   solv_free(pubkey);
611   solv_free(userid);
612 }
613
614
615 #ifdef ENABLE_RPMDB
616
617 /* this is private to rpm, but rpm lacks an interface to retrieve
618  * the values. Sigh. */
619 struct pgpDigParams_s {
620     const char * userid;
621     const unsigned char * hash;
622 #ifndef HAVE_PGPDIGGETPARAMS
623     const char * params[4];
624 #endif
625     unsigned char tag;
626     unsigned char version;               /*!< version number. */
627     unsigned char time[4];               /*!< time that the key was created. */
628     unsigned char pubkey_algo;           /*!< public key algorithm. */
629     unsigned char hash_algo;
630     unsigned char sigtype;
631     unsigned char hashlen;
632     unsigned char signhash16[2];
633     unsigned char signid[8];
634     unsigned char saved;
635 };
636
637 #ifndef HAVE_PGPDIGGETPARAMS
638 struct pgpDig_s {
639     struct pgpDigParams_s signature;
640     struct pgpDigParams_s pubkey;
641 };
642 #endif
643
644
645 /* only rpm knows how to do the release calculation, we don't dare
646  * to recreate all the bugs in libsolv */
647 static void
648 parsekeydata_rpm(Solvable *s, Repodata *data, unsigned char *pkts, int pktsl)
649 {
650   Pool *pool = s->repo->pool;
651   struct pgpDigParams_s *digpubkey;
652   pgpDig dig = 0;
653   char keyid[16 + 1];
654   char evrbuf[8 + 1 + 8 + 1];
655   unsigned int btime;
656
657 #ifndef RPM5
658   dig = pgpNewDig();
659 #else
660   dig = pgpDigNew(RPMVSF_DEFAULT, 0);
661 #endif
662   (void) pgpPrtPkts(pkts, pktsl, dig, 0);
663 #ifdef HAVE_PGPDIGGETPARAMS
664   digpubkey = pgpDigGetParams(dig, PGPTAG_PUBLIC_KEY);
665 #else
666   digpubkey = &dig->pubkey;
667 #endif
668   if (digpubkey)
669     {
670       btime = digpubkey->time[0] << 24 | digpubkey->time[1] << 16 | digpubkey->time[2] << 8 | digpubkey->time[3];
671       solv_bin2hex(digpubkey->signid, 8, keyid);
672       solv_bin2hex(digpubkey->signid + 4, 4, evrbuf);
673       evrbuf[8] = '-';
674       solv_bin2hex(digpubkey->time, 4, evrbuf + 9);
675       s->evr = pool_str2id(pool, evrbuf, 1);
676       repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_KEYID, keyid);
677       if (digpubkey->userid)
678         setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_SUMMARY, digpubkey->userid);
679       if (btime)
680         repodata_set_num(data, s - s->repo->pool->solvables, SOLVABLE_BUILDTIME, btime);
681     }
682 #ifndef RPM5
683   (void)pgpFreeDig(dig);
684 #else
685   (void)pgpDigFree(dig);
686 #endif
687 }
688
689 #endif  /* ENABLE_RPMDB */
690
691 static int
692 pubkey2solvable(Solvable *s, Repodata *data, char *pubkey)
693 {
694   unsigned char *pkts;
695   int pktsl;
696
697   pkts = unarmor(pubkey, &pktsl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----");
698   if (!pkts)
699     {
700       pool_error(s->repo->pool, 0, "unarmor failure");
701       return 0;
702     }
703   setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_DESCRIPTION, pubkey);
704   parsekeydata(s, data, pkts, pktsl);
705 #ifdef ENABLE_RPMDB
706   parsekeydata_rpm(s, data, pkts, pktsl);
707 #endif
708   solv_free((void *)pkts);
709   return 1;
710 }
711
712 #ifdef ENABLE_RPMDB
713
714 int
715 repo_add_rpmdb_pubkeys(Repo *repo, int flags)
716 {
717   Pool *pool = repo->pool;
718   Queue q;
719   int i;
720   char *str;
721   Repodata *data;
722   Solvable *s;
723   const char *rootdir = 0;
724   void *state;
725
726   data = repo_add_repodata(repo, flags);
727   if (flags & REPO_USE_ROOTDIR)
728     rootdir = pool_get_rootdir(pool);
729   state = rpm_state_create(repo->pool, rootdir);
730   queue_init(&q);
731   rpm_installedrpmdbids(state, "Name", "gpg-pubkey", &q);
732   for (i = 0; i < q.count; i++)
733     {
734       void *handle;
735       unsigned long long itime;
736
737       handle = rpm_byrpmdbid(state, q.elements[i]);
738       if (!handle)
739         continue;
740       str = rpm_query(handle, SOLVABLE_DESCRIPTION);
741       if (!str)
742         continue;
743       s = pool_id2solvable(pool, repo_add_solvable(repo));
744       pubkey2solvable(s, data, str);
745       solv_free(str);
746       itime = rpm_query_num(handle, SOLVABLE_INSTALLTIME, 0);
747       if (itime)
748         repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLTIME, itime);
749       if (!repo->rpmdbid)
750         repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
751       repo->rpmdbid[s - pool->solvables - repo->start] = q.elements[i];
752     }
753   queue_free(&q);
754   rpm_state_free(state);
755   if (!(flags & REPO_NO_INTERNALIZE))
756     repodata_internalize(data);
757   return 0;
758 }
759
760 #endif
761
762 Id
763 repo_add_pubkey(Repo *repo, const char *key, int flags)
764 {
765   Pool *pool = repo->pool;
766   Repodata *data;
767   Solvable *s;
768   char *buf;
769   int bufl, l, ll;
770   FILE *fp;
771
772   data = repo_add_repodata(repo, flags);
773   buf = 0;
774   bufl = 0;
775   if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, key) : key, "r")) == 0)
776     {
777       pool_error(pool, -1, "%s: %s", key, strerror(errno));
778       return 0;
779     }
780   for (l = 0; ;)
781     {
782       if (bufl - l < 4096)
783         {
784           bufl += 4096;
785           buf = solv_realloc(buf, bufl);
786         }
787       ll = fread(buf + l, 1, bufl - l, fp);
788       if (ll < 0)
789         {
790           fclose(fp);
791           pool_error(pool, -1, "%s: %s", key, strerror(errno));
792           return 0;
793         }
794       if (ll == 0)
795         break;
796       l += ll;
797     }
798   buf[l] = 0;
799   fclose(fp);
800   s = pool_id2solvable(pool, repo_add_solvable(repo));
801   if (!pubkey2solvable(s, data, buf))
802     {
803       repo_free_solvable(repo, s - pool->solvables, 1);
804       solv_free(buf);
805       return 0;
806     }
807   solv_free(buf);
808   if (!(flags & REPO_NO_INTERNALIZE))
809     repodata_internalize(data);
810   return s - pool->solvables;
811 }
812