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