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