]> the.earth.li Git - onak.git/blob - keyindex.c
0.6.3 release
[onak.git] / keyindex.c
1 /*
2  * keyindex.c - Routines to list an OpenPGP key.
3  *
4  * Copyright 2002-2008 Jonathan McDowell <noodles@earth.li>
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18
19 #include <inttypes.h>
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
25
26 #include "decodekey.h"
27 #include "keydb.h"
28 #include "keyid.h"
29 #include "keyindex.h"
30 #include "keystructs.h"
31 #include "log.h"
32 #include "onak.h"
33 #include "openpgp.h"
34
35 /*
36  * Convert a Public Key algorithm to its single character representation.
37  */
38 char pkalgo2char(uint8_t algo)
39 {
40         char typech;
41
42         switch (algo) {
43         case OPENPGP_PKALGO_DSA:
44                 typech = 'D';
45                 break;
46         case OPENPGP_PKALGO_ECDSA:
47         case OPENPGP_PKALGO_EDDSA:
48                 typech = 'E';
49                 break;
50         case OPENPGP_PKALGO_EC:
51                 typech = 'e';
52                 break;
53         case OPENPGP_PKALGO_ELGAMAL_SIGN:
54                 typech = 'G';
55                 break;
56         case OPENPGP_PKALGO_ELGAMAL_ENC:
57                 typech = 'g';
58                 break;
59         case OPENPGP_PKALGO_RSA:
60                 typech = 'R';
61                 break;
62         case OPENPGP_PKALGO_RSA_ENC:
63                 typech = 'r';
64                 break;
65         case OPENPGP_PKALGO_RSA_SIGN:
66                 typech = 's';
67                 break;
68         default:
69                 typech = '?';
70                 break;
71         }
72
73         return typech;
74 }
75
76 /**
77  *      html_escape - Takes a string and converts it to HTML.
78  *      @src: The string to HTMLize.
79  *      @src_len: The length of the source string
80  *      @dst: A buffer to put the escaped string into
81  *      @dst_len: Length of the destination buffer (including a trailing NULL)
82  *
83  *      Takes a string and escapes any HTML entities (<, >, &, ", '). Returns
84  *      dst.
85  */
86 const char *html_escape(const char *src, size_t src_len,
87                 char *dst, size_t dst_len)
88 {
89         size_t in_pos, out_pos;
90
91         dst_len--;
92
93         for (in_pos = 0, out_pos = 0;
94                         in_pos < src_len && out_pos < (dst_len - 1);
95                         in_pos++, out_pos++) {
96                 switch (src[in_pos]) {
97                 case '<':
98                         if ((out_pos + 4) >= dst_len) {
99                                 break;
100                         }
101                         dst[out_pos++] = '&';
102                         dst[out_pos++] = 'l';
103                         dst[out_pos++] = 't';
104                         dst[out_pos] = ';';
105                         break;
106                 case '>':
107                         if ((out_pos + 4) >= dst_len) {
108                                 break;
109                         }
110                         dst[out_pos++] = '&';
111                         dst[out_pos++] = 'g';
112                         dst[out_pos++] = 't';
113                         dst[out_pos] = ';';
114                         break;
115                 case '"':
116                         if ((out_pos + 6) >= dst_len) {
117                                 break;
118                         }
119                         dst[out_pos++] = '&';
120                         dst[out_pos++] = 'q';
121                         dst[out_pos++] = 'u';
122                         dst[out_pos++] = 'o';
123                         dst[out_pos++] = 't';
124                         dst[out_pos] = ';';
125                         break;
126                 case '\'':
127                         if ((out_pos + 5) >= dst_len) {
128                                 break;
129                         }
130                         dst[out_pos++] = '&';
131                         dst[out_pos++] = '#';
132                         dst[out_pos++] = '3';
133                         dst[out_pos++] = '9';
134                         dst[out_pos] = ';';
135                         break;
136                 case '&':
137                         if ((out_pos + 5) >= dst_len) {
138                                 break;
139                         }
140                         dst[out_pos++] = '&';
141                         dst[out_pos++] = 'a';
142                         dst[out_pos++] = 'm';
143                         dst[out_pos++] = 'p';
144                         dst[out_pos] = ';';
145                         break;
146                 default:
147                         dst[out_pos] = src[in_pos];
148                 }
149         }
150         dst[out_pos] = 0;
151
152         return dst;
153 }
154
155 /*
156  * Given a public key/subkey packet return the key length.
157  */
158 unsigned int keylength(struct openpgp_packet *keydata)
159 {
160         unsigned int length;
161         uint8_t keyofs;
162         enum onak_oid oid;
163
164         switch (keydata->data[0]) {
165         case 2:
166         case 3:
167                 length = (keydata->data[8] << 8) +
168                                 keydata->data[9];
169                 break;
170         case 4:
171         case 5:
172                 /* v5 has an additional 4 bytes of key length data */
173                 keyofs = (keydata->data[0] == 4) ? 6 : 10;
174                 switch (keydata->data[5]) {
175                 case OPENPGP_PKALGO_EC:
176                 case OPENPGP_PKALGO_ECDSA:
177                 case OPENPGP_PKALGO_EDDSA:
178                         /* Elliptic curve key size is based on OID */
179                         oid = onak_parse_oid(&keydata->data[keyofs],
180                                         keydata->length - keyofs);
181                         if (oid == ONAK_OID_CURVE25519) {
182                                 length = 255;
183                         } else if (oid == ONAK_OID_ED25519) {
184                                 length = 255;
185                         } else if (oid == ONAK_OID_NISTP256) {
186                                 length = 256;
187                         } else if (oid == ONAK_OID_NISTP384) {
188                                 length = 384;
189                         } else if (oid == ONAK_OID_NISTP521) {
190                                 length = 521;
191                         } else if (oid == ONAK_OID_BRAINPOOLP256R1) {
192                                 length = 256;
193                         } else if (oid == ONAK_OID_BRAINPOOLP384R1) {
194                                 length = 384;
195                         } else if (oid == ONAK_OID_BRAINPOOLP512R1) {
196                                 length = 512;
197                         } else if (oid == ONAK_OID_SECP256K1) {
198                                 length = 256;
199                         } else {
200                                 logthing(LOGTHING_ERROR,
201                                         "Unknown elliptic curve size");
202                                 length = 0;
203                         }
204                         break;
205                 default:
206                         length = (keydata->data[keyofs] << 8) +
207                                 keydata->data[keyofs + 1];
208                 }
209                 break;
210         default:
211                 logthing(LOGTHING_ERROR, "Unknown key version: %d",
212                         keydata->data[0]);
213                 length = 0;
214         }
215
216         return length;
217 }
218
219 int list_sigs(struct onak_dbctx *dbctx,
220                 struct openpgp_packet_list *sigs, bool html)
221 {
222         char *uid = NULL;
223         uint64_t sigid = 0;
224         char *sig = NULL;
225         char buf[1024];
226
227         while (sigs != NULL) {
228                 sigid = sig_keyid(sigs->packet);
229                 if (dbctx) {
230                         uid = dbctx->keyid2uid(dbctx, sigid);
231                 }
232                 if (sigs->packet->data[0] == 4 &&
233                                 sigs->packet->data[1] == 0x30) {
234                         /* It's a Type 4 sig revocation */
235                         sig = "rev";
236                 } else {
237                         sig = "sig";
238                 }
239                 if (html && uid != NULL) {
240                         printf("%s         <a href=\"lookup?op=get&"
241                                 "search=0x%016" PRIX64 "\">0x%016" PRIX64
242                                 "</a>             "
243                                 "<a href=\"lookup?op=vindex&search=0x%016"
244                                 PRIX64 "\">%s</a>\n",
245                                 sig,
246                                 sigid,
247                                 sigid,
248                                 sigid,
249                                 html_escape(uid, strlen(uid), buf, sizeof(buf)));
250                 } else if (html && uid == NULL) {
251                         printf("%s         0x%016" PRIX64 "             "
252                                 "[User id not found]\n",
253                                 sig,
254                                 sigid);
255                 } else {
256                         printf("%s         0x%016" PRIX64
257                                 "             %s\n",
258                                 sig,
259                                 sigid,
260                                 (uid != NULL) ? uid :
261                                 "[User id not found]");
262                 }
263                 if (uid != NULL) {
264                         free(uid);
265                         uid = NULL;
266                 }
267                 sigs = sigs->next;
268         }
269
270         return 0;
271 }
272
273 int list_uids(struct onak_dbctx *dbctx,
274                 uint64_t keyid, struct openpgp_signedpacket_list *uids,
275                 bool verbose, bool html)
276 {
277         char buf[1024];
278         int  imgindx = 0;
279
280         while (uids != NULL) {
281                 if (uids->packet->tag == OPENPGP_PACKET_UID) {
282                         snprintf(buf, 1023, "%.*s",
283                                 (int) uids->packet->length,
284                                 uids->packet->data);
285                         if (html) {
286                                 printf("                                %s\n",
287                                         html_escape((char *) uids->packet->data,
288                                                 uids->packet->length,
289                                                 buf,
290                                                 sizeof(buf)));
291                         } else {
292                                 printf("                                %.*s\n",
293                                         (int) uids->packet->length,
294                                         uids->packet->data);
295                         }
296                 } else if (uids->packet->tag == OPENPGP_PACKET_UAT) {
297                         printf("                                ");
298                         if (html) {
299                                 printf("<img src=\"lookup?op=photo&search="
300                                         "0x%016" PRIX64 "&idx=%d\" alt=\""
301                                         "[photo id]\">\n",
302                                         keyid,
303                                         imgindx);
304                                 imgindx++;
305                         } else {
306                                 printf("[photo id]\n");
307                         }
308                 }
309                 if (verbose) {
310                         list_sigs(dbctx, uids->sigs, html);
311                 }
312                 uids = uids->next;
313         }
314
315         return 0;
316 }
317
318 int list_subkeys(struct onak_dbctx *dbctx,
319                 struct openpgp_signedpacket_list *subkeys, bool verbose,
320                 bool html)
321 {
322         struct tm       created;
323         time_t          created_time = 0;
324         int             type = 0;
325         int             length = 0;
326         uint64_t        keyid = 0;
327
328         while (subkeys != NULL) {
329                 if (subkeys->packet->tag == OPENPGP_PACKET_PUBLICSUBKEY) {
330
331                         created_time = (subkeys->packet->data[1] << 24) +
332                                         (subkeys->packet->data[2] << 16) +
333                                         (subkeys->packet->data[3] << 8) +
334                                         subkeys->packet->data[4];
335                         gmtime_r(&created_time, &created);
336
337                         switch (subkeys->packet->data[0]) {
338                         case 2:
339                         case 3:
340                                 type = subkeys->packet->data[7];
341                                 break;
342                         case 4:
343                         case 5:
344                                 type = subkeys->packet->data[5];
345                                 break;
346                         default:
347                                 logthing(LOGTHING_ERROR,
348                                         "Unknown key version: %d",
349                                         subkeys->packet->data[0]);
350                         }
351                         length = keylength(subkeys->packet);
352
353                         if (get_packetid(subkeys->packet,
354                                         &keyid) != ONAK_E_OK) {
355                                 logthing(LOGTHING_ERROR, "Couldn't get keyid.");
356                         }
357                         printf("sub  %5d%c/0x%016" PRIX64 " %04d/%02d/%02d\n",
358                                 length,
359                                 pkalgo2char(type),
360                                 keyid,
361                                 created.tm_year + 1900,
362                                 created.tm_mon + 1,
363                                 created.tm_mday);
364
365                 }
366                 if (verbose) {
367                         list_sigs(dbctx, subkeys->sigs, html);
368                 }
369                 subkeys = subkeys->next;
370         }
371
372         return 0;
373 }
374
375 void display_fingerprint(struct openpgp_publickey *key)
376 {
377         int             i = 0;
378         struct openpgp_fingerprint fingerprint;
379
380         get_fingerprint(key->publickey, &fingerprint);
381         printf("      Key fingerprint =");
382         for (i = 0; i < fingerprint.length; i++) {
383                 if ((fingerprint.length == 16) ||
384                         (i % 2 == 0)) {
385                         printf(" ");
386                 }
387                 if (fingerprint.length == 20 &&
388                                 (i * 2) == fingerprint.length) {
389                         /* Extra space in the middle of a SHA1 fingerprint */
390                         printf(" ");
391                 }
392                 printf("%02X", fingerprint.fp[i]);
393         }
394         printf("\n");
395
396         return;
397 }
398
399 void display_skshash(struct openpgp_publickey *key, bool html)
400 {
401         int             i = 0;
402         struct skshash  hash;
403
404         get_skshash(key, &hash);
405         printf("      Key hash = ");
406         if (html) {
407                 printf("<a href=\"lookup?op=hget&search=");
408                 for (i = 0; i < sizeof(hash.hash); i++) {
409                         printf("%02X", hash.hash[i]);
410                 }
411                 printf("\">");
412         }
413         for (i = 0; i < sizeof(hash.hash); i++) {
414                 printf("%02X", hash.hash[i]);
415         }
416         if (html) {
417                 printf("</a>");
418         }
419         printf("\n");
420
421         return;
422 }
423
424 /**
425  *      key_index - List a set of OpenPGP keys.
426  *      @keys: The keys to display.
427  *      @verbose: Should we list sigs as well?
428  *      @fingerprint: List the fingerprint?
429  *      @html: Should the output be tailored for HTML?
430  *
431  *      This function takes a list of OpenPGP public keys and displays an index
432  *      of them. Useful for debugging or the keyserver Index function.
433  */
434 int key_index(struct onak_dbctx *dbctx,
435                 struct openpgp_publickey *keys, bool verbose, bool fingerprint,
436                         bool skshash, bool html)
437 {
438         struct openpgp_signedpacket_list        *curuid = NULL;
439         struct tm                                created;
440         time_t                                   created_time = 0;
441         int                                      type = 0;
442         int                                      length = 0;
443         char                                     buf[1024];
444         uint64_t                                 keyid;
445
446
447         if (html) {
448                 puts("<pre>");
449         }
450         puts("Type   bits/keyID    Date       User ID");
451         while (keys != NULL) {
452                 created_time = (keys->publickey->data[1] << 24) +
453                                         (keys->publickey->data[2] << 16) +
454                                         (keys->publickey->data[3] << 8) +
455                                         keys->publickey->data[4];
456                 gmtime_r(&created_time, &created);
457
458                 switch (keys->publickey->data[0]) {
459                 case 2:
460                 case 3:
461                         type = keys->publickey->data[7];
462                         break;
463                 case 4:
464                 case 5:
465                         type = keys->publickey->data[5];
466                         break;
467                 default:
468                         logthing(LOGTHING_ERROR, "Unknown key version: %d",
469                                 keys->publickey->data[0]);
470                 }
471                 length = keylength(keys->publickey);
472
473                 if (get_keyid(keys, &keyid) != ONAK_E_OK) {
474                         logthing(LOGTHING_ERROR, "Couldn't get keyid.");
475                 }
476
477                 if (html) {
478                         printf("pub  %5d%c/<a href=\"lookup?op=get&"
479                                 "search=0x%016" PRIX64 "\">0x%016" PRIX64
480                                 "</a> %04d/%02d/%02d ",
481                                 length,
482                                 pkalgo2char(type),
483                                 keyid,
484                                 keyid,
485                                 created.tm_year + 1900,
486                                 created.tm_mon + 1,
487                                 created.tm_mday);
488                 } else {
489                         printf("pub  %5d%c/0x%016" PRIX64 " %04d/%02d/%02d ",
490                                 length,
491                                 pkalgo2char(type),
492                                 keyid,
493                                 created.tm_year + 1900,
494                                 created.tm_mon + 1,
495                                 created.tm_mday);
496                 }
497
498                 curuid = keys->uids;
499                 if (curuid != NULL &&
500                                 curuid->packet->tag == OPENPGP_PACKET_UID) {
501                         if (html) {
502                                 printf("<a href=\"lookup?op=vindex&"
503                                         "search=0x%016" PRIX64 "\">"
504                                         "%s</a>%s\n",
505                                         keyid,
506                                         html_escape((char *) curuid->packet->data,
507                                                 curuid->packet->length,
508                                                 buf,
509                                                 sizeof(buf)),
510                                         (keys->revoked) ? " *** REVOKED ***" : "");
511                         } else {
512                                 printf("%.*s%s\n",
513                                         (int) curuid->packet->length,
514                                         curuid->packet->data,
515                                         (keys->revoked) ? " *** REVOKED ***" : "");
516                         }
517                         if (skshash) {
518                                 display_skshash(keys, html);
519                         }
520                         if (fingerprint) {
521                                 display_fingerprint(keys);
522                         }
523                         if (verbose) {
524                                 list_sigs(dbctx, curuid->sigs, html);
525                         }
526                         curuid = curuid->next;
527                 } else {
528                         printf("%s\n", 
529                                 (keys->revoked) ? "*** REVOKED ***": "");
530                         if (fingerprint) {
531                                 display_fingerprint(keys);
532                         }
533                 }
534
535                 list_uids(dbctx, keyid, curuid, verbose, html);
536                 if (verbose) {
537                         list_subkeys(dbctx, keys->subkeys, verbose, html);
538                 }
539
540                 keys = keys->next;
541         }
542
543         if (html) {
544                 puts("</pre>");
545         }
546
547         return 0;
548 }
549
550 /**
551  *      mrkey_index - List a set of OpenPGP keys in the MRHKP format.
552  *      @keys: The keys to display.
553  *
554  *      This function takes a list of OpenPGP public keys and displays a
555  *      machine readable list of them.
556  */
557 int mrkey_index(struct openpgp_publickey *keys)
558 {
559         struct openpgp_signedpacket_list        *curuid = NULL;
560         time_t                                   created_time = 0;
561         int                                      type = 0;
562         int                                      length = 0;
563         int                                      i = 0;
564         int                                      c;
565         uint64_t                                 keyid;
566         struct openpgp_fingerprint fingerprint;
567
568         while (keys != NULL) {
569                 created_time = (keys->publickey->data[1] << 24) +
570                                         (keys->publickey->data[2] << 16) +
571                                         (keys->publickey->data[3] << 8) +
572                                         keys->publickey->data[4];
573
574                 printf("pub:");
575
576                 switch (keys->publickey->data[0]) {
577                 case 2:
578                 case 3:
579                         if (get_keyid(keys, &keyid) != ONAK_E_OK) {
580                                 logthing(LOGTHING_ERROR, "Couldn't get keyid");
581                         }
582                         printf("%016" PRIX64, keyid);
583                         type = keys->publickey->data[7];
584                         break;
585                 case 4:
586                 case 5:
587                         (void) get_fingerprint(keys->publickey, &fingerprint);
588
589                         for (i = 0; i < fingerprint.length; i++) {
590                                 printf("%02X", fingerprint.fp[i]);
591                         }
592
593                         type = keys->publickey->data[5];
594                         break;
595                 default:
596                         logthing(LOGTHING_ERROR, "Unknown key version: %d",
597                                 keys->publickey->data[0]);
598                 }
599                 length = keylength(keys->publickey);
600
601                 printf(":%d:%d:%ld::%s\n",
602                         type,
603                         length,
604                         created_time,
605                         (keys->revoked) ? "r" : "");
606         
607                 for (curuid = keys->uids; curuid != NULL;
608                          curuid = curuid->next) {
609                 
610                         if (curuid->packet->tag == OPENPGP_PACKET_UID) {
611                                 printf("uid:");
612                                 for (i = 0; i < (int) curuid->packet->length;
613                                                 i++) {
614                                         c = curuid->packet->data[i];
615                                         if (c == '%') {
616                                                 putchar('%');
617                                                 putchar(c);
618                                         } else if (c == ':' || c > 127) {
619                                                 printf("%%%X", c);
620                                         } else {
621                                                 putchar(c);
622                                         }
623                                 }
624                                 printf("\n");
625                         }
626                 }
627                 keys = keys->next;
628         }
629         return 0;
630 }