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