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