]> the.earth.li Git - onak.git/blob - parsekey.c
ceff4292b7e4742f6f47dad412790fe6152172ec
[onak.git] / parsekey.c
1 /*
2  * parsekey.c - Routines to parse an OpenPGP key.
3  *
4  * Copyright 2002-2004,2007-2008,2011 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 <stdbool.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "keystructs.h"
25 #include "ll.h"
26 #include "mem.h"
27 #include "onak.h"
28 #include "openpgp.h"
29 #include "parsekey.h"
30
31 /**
32  *      parse_keys - Process a stream of packets for public keys + sigs.
33  *      @packets: The packet list to parse.
34  *      @keys: The returned list of public keys.
35  *
36  *      This function takes an list of OpenPGP packets and attempts to parse it
37  *      into a list of public keys with signatures and subkeys.
38  *
39  *      Returns a count of how many keys we parsed.
40  */
41 int parse_keys(struct openpgp_packet_list *packets,
42                 struct openpgp_publickey **keys)
43 {
44         struct openpgp_publickey *curkey = NULL;
45         int count;
46
47         count = 0;
48
49         /*
50          * If keys already has some keys in it then set curkey to the last one
51          * so we add to the end of the list.
52          */
53         for (curkey = *keys; curkey != NULL && curkey->next != NULL;
54                         curkey = curkey->next) ;
55
56         while (packets != NULL) {
57                 switch (packets->packet->tag) {
58                 case OPENPGP_PACKET_SIGNATURE:
59                         /*
60                          * It's a signature packet. Add it to either the public
61                          * key, to the current UID or the current subkey.
62                          */
63                         if (curkey == NULL)
64                                 return ONAK_E_INVALID_PARAM;
65                         if (curkey->subkeys != NULL) {
66                                 ADD_PACKET_TO_LIST_END(curkey->last_subkey,
67                                         sig,
68                                         packet_dup(packets->packet));
69                         } else if (curkey->uids != NULL) {
70                                 ADD_PACKET_TO_LIST_END(curkey->last_uid,
71                                         sig,
72                                         packet_dup(packets->packet));
73                         } else {
74                                 ADD_PACKET_TO_LIST_END(curkey,
75                                         sig,
76                                         packet_dup(packets->packet));
77                                 /*
78                                  * This is a signature on the public key; check
79                                  * if it's a revocation.
80                                  */
81                                 if (packets->packet->data[0] == 3 &&
82                                         packets->packet->data[2] ==
83                                                 OPENPGP_SIGTYPE_KEY_REV) {
84                                         /*
85                                          * Type 3 key, 0x20 == revocation
86                                          */
87                                         curkey->revoked = true;
88                                 } else if (packets->packet->data[0] == 4 &&
89                                         packets->packet->data[1] ==
90                                                 OPENPGP_SIGTYPE_KEY_REV) {
91                                         /*
92                                          * Type 4 key, 0x20 == revocation
93                                          */
94                                         curkey->revoked = true;
95                                 }
96                         }
97                         break;
98                 case OPENPGP_PACKET_PUBLICKEY:
99                         /*
100                          * It's a public key packet, so start a new key in our
101                          * list.
102                          */
103                         if (curkey != NULL) {
104                                 curkey->next = malloc(sizeof (*curkey));
105                                 curkey = curkey->next;
106                         } else {
107                                 *keys = curkey =
108                                         malloc(sizeof (*curkey));
109                         }
110                         memset(curkey, 0, sizeof(*curkey));
111                         curkey->publickey = packet_dup(packets->packet);
112                         count++;
113                         break;
114                 case OPENPGP_PACKET_UID:
115                 case OPENPGP_PACKET_UAT:
116                         /*
117                          * It's a UID packet (or a photo id, which is similar).
118                          */
119                         if (curkey == NULL)
120                                 return ONAK_E_INVALID_PARAM;
121                         if (curkey->subkeys != NULL)
122                                 return ONAK_E_INVALID_PARAM;
123                         ADD_PACKET_TO_LIST_END(curkey,
124                                 uid,
125                                 packet_dup(packets->packet));
126                         break;
127                 case OPENPGP_PACKET_PUBLICSUBKEY:
128                         /*
129                          * It's a subkey packet.
130                          */
131                         if (curkey == NULL)
132                                 return ONAK_E_INVALID_PARAM;
133                         ADD_PACKET_TO_LIST_END(curkey,
134                                 subkey,
135                                 packet_dup(packets->packet));
136                         break;
137                 case OPENPGP_PACKET_TRUST:
138                 case OPENPGP_PACKET_COMMENT:
139                         /*
140                          * One of:
141                          *
142                          * Trust packet. Ignore.
143                          * Comment packet. Ignore.
144                          */
145                         break;
146                 default:
147                         /* Unsupported packet. Do what? Ignore for now. */
148                         break;
149                 }
150                 packets = packets->next;
151         }
152
153         return count;
154 }
155
156 /**
157  *      debug_packet - Print debug info about a packet
158  *      @packet: The packet to display.
159  *
160  *      This function takes an OpenPGP packet and displays some information
161  *      about it to stdout. Useful for debugging purposes or curiousity about
162  *      an OpenPGP packet stream.
163  */
164 int debug_packet(struct openpgp_packet *packet)
165 {
166         printf("\tNew format: %d, Tag: %u, Length: %zd\n",
167                         packet->newformat,
168                         packet->tag,
169                         packet->length);
170
171         return 0;
172 }
173
174 /**
175  *      read_openpgp_stream - Reads a stream of OpenPGP packets.
176  *      @getchar_func: The function to get the next character from the stream.
177  *      @ctx: A pointer to the context structure for getchar_func.
178  *      @packets: The outputted list of packets.
179  *      @maxnum: The maximum number of keys to read. 0 means unlimited.
180  *
181  *      This function uses getchar_func to read characters from an OpenPGP
182  *      packet stream and reads the packets into a linked list of packets
183  *      ready for parsing as a public key or whatever.
184  */
185 onak_status_t read_openpgp_stream(size_t (*getchar_func)(void *ctx, size_t count,
186                                 void *c),
187                                 void *ctx,
188                                 struct openpgp_packet_list **packets,
189                                 int maxnum)
190 {
191         unsigned char                    curchar = 0;
192         struct openpgp_packet_list      *curpacket = NULL, **packetend = NULL;
193         onak_status_t                    rc = ONAK_E_OK;
194         int                              keys = 0;
195
196         if (packets == NULL)
197                 return ONAK_E_INVALID_PARAM;
198
199         curpacket = *packets;
200         if (curpacket != NULL) {
201                 while (curpacket->next != NULL) {
202                         curpacket = curpacket->next;
203                 }
204         }
205
206         while (rc == ONAK_E_OK && (maxnum == 0 || keys < maxnum) &&
207                         (getchar_func(ctx, 1, &curchar) == 1)) {
208                 if (curchar & 0x80) {
209                         /*
210                          * New packet. Allocate memory for it.
211                          */
212                         if (curpacket != NULL) {
213                                 curpacket->next = malloc(sizeof (*curpacket));
214                                 packetend = &curpacket->next;
215                                 curpacket = curpacket->next;
216                         } else {
217                                 *packets = curpacket =
218                                         malloc(sizeof (*curpacket));
219                                 packetend = packets;
220                         }
221                         memset(curpacket, 0, sizeof(*curpacket));
222                         curpacket->packet =
223                                 malloc(sizeof (*curpacket->packet));
224                         memset(curpacket->packet, 0,
225                                         sizeof(*curpacket->packet));
226
227                         curpacket->packet->newformat = (curchar & 0x40);
228
229                         /*
230                          * TODO: Better error checking on getchar_func.
231                          */
232                         if (curpacket->packet->newformat) {
233                                 curpacket->packet->tag = (curchar & 0x3F);
234                                 if (getchar_func(ctx, 1, &curchar) == 0) {
235                                         rc = ONAK_E_INVALID_PKT;
236                                         break;
237                                 }
238                                 curpacket->packet->length = curchar;
239                                 if (curpacket->packet->length > 191 &&
240                                         curpacket->packet->length < 224) {
241                                         rc = getchar_func(ctx, 1, &curchar) ? ONAK_E_OK : ONAK_E_IO_ERROR;
242                                         curpacket->packet->length -= 192;
243                                         curpacket->packet->length <<= 8;
244                                         curpacket->packet->length += curchar;
245                                         curpacket->packet->length += 192;
246                                 } else if (curpacket->packet->length > 223 &&
247                                         curpacket->packet->length < 255) {
248                                         free(curpacket->packet);
249                                         curpacket->packet = NULL;
250                                         rc = ONAK_E_UNSUPPORTED_FEATURE;
251                                 } else if (curpacket->packet->length == 255) {
252                                         /*
253                                          * 5 byte length; ie 255 followed by 4
254                                          * bytes of MSB length.
255                                          */
256                                         if (getchar_func(ctx, 1, &curchar) != 1) {
257                                                 rc = ONAK_E_INVALID_PKT;
258                                                 break;
259                                         }
260                                         curpacket->packet->length = curchar;
261                                         curpacket->packet->length <<= 8;
262                                         if (getchar_func(ctx, 1, &curchar) != 1) {
263                                                 rc = ONAK_E_INVALID_PKT;
264                                                 break;
265                                         }
266                                         curpacket->packet->length += curchar;
267                                         curpacket->packet->length <<= 8;
268                                         if (getchar_func(ctx, 1, &curchar) != 1) {
269                                                 rc = ONAK_E_INVALID_PKT;
270                                                 break;
271                                         }
272                                         curpacket->packet->length += curchar;
273                                         curpacket->packet->length <<= 8;
274                                         if (getchar_func(ctx, 1, &curchar) != 1) {
275                                                 rc = ONAK_E_INVALID_PKT;
276                                                 break;
277                                         }
278                                         curpacket->packet->length += curchar;
279                                 }
280                         } else {
281                                 curpacket->packet->tag = (curchar & 0x3C) >> 2;
282                                 switch (curchar & 3) {
283                                 case 0:
284                                         if (getchar_func(ctx, 1, &curchar) != 1) {
285                                                 rc = ONAK_E_INVALID_PKT;
286                                                 break;
287                                         }
288                                         curpacket->packet->length = curchar;
289                                         break;
290                                 case 1:
291                                         if (getchar_func(ctx, 1, &curchar) != 1) {
292                                                 rc = ONAK_E_INVALID_PKT;
293                                                 break;
294                                         }
295                                         curpacket->packet->length = curchar;
296                                         curpacket->packet->length <<= 8;
297                                         if (getchar_func(ctx, 1, &curchar) != 1) {
298                                                 rc = ONAK_E_INVALID_PKT;
299                                                 break;
300                                         }
301                                         curpacket->packet->length += curchar;
302                                         break;
303                                 case 2:
304                                         if (getchar_func(ctx, 1, &curchar) != 1) {
305                                                 rc = ONAK_E_INVALID_PKT;
306                                                 break;
307                                         }
308                                         curpacket->packet->length = 
309                                                 ((unsigned) curchar << 24);
310                                         if (getchar_func(ctx, 1, &curchar) != 1) {
311                                                 rc = ONAK_E_INVALID_PKT;
312                                                 break;
313                                         }
314                                         curpacket->packet->length +=
315                                                 (curchar << 16);
316                                         if (getchar_func(ctx, 1, &curchar) != 1) {
317                                                 rc = ONAK_E_INVALID_PKT;
318                                                 break;
319                                         }
320                                         curpacket->packet->length +=
321                                                 (curchar << 8);
322                                         if (getchar_func(ctx, 1, &curchar) != 1) {
323                                                 rc = ONAK_E_INVALID_PKT;
324                                                 break;
325                                         }
326                                         curpacket->packet->length += curchar;
327                                         break;
328                                 case 3:
329                                         rc = ONAK_E_UNSUPPORTED_FEATURE;
330                                         free(curpacket->packet);
331                                         curpacket->packet = NULL;
332                                         break;
333                                 }
334                         }
335
336                         if (rc == 0) {
337                                 if (curpacket->packet->tag ==
338                                                 OPENPGP_PACKET_PUBLICKEY) {
339                                         keys++;
340                                 }
341                                 curpacket->packet->data =
342                                         malloc(curpacket->packet->length *
343                                         sizeof(unsigned char));
344                                 if (curpacket->packet->data == NULL) {
345                                         rc = ONAK_E_NOMEM;
346                                 } else {
347                                         rc = getchar_func(ctx,
348                                                 curpacket->packet->length,
349                                                 curpacket->packet->data) ?
350                                                 ONAK_E_OK : ONAK_E_IO_ERROR;
351                                 }
352                         }
353                 } else {
354                         rc = ONAK_E_INVALID_PKT;
355                 }
356                 if (rc == ONAK_E_OK) {
357                         /* Make sure the packet version is sane */
358                         switch (curpacket->packet->tag) {
359                         case OPENPGP_PACKET_ENCRYPTED_MDC:
360                                 /* These packets must be v1 */
361                                 if (curpacket->packet->data[0] != 1) {
362                                         rc = ONAK_E_INVALID_PKT;
363                                 }
364                                 break;
365                         case OPENPGP_PACKET_PKSESSIONKEY:
366                         case OPENPGP_PACKET_ONEPASSSIG:
367                                 /* These packets must be v3 */
368                                 if (curpacket->packet->data[0] != 3) {
369                                         rc = ONAK_E_INVALID_PKT;
370                                 }
371                                 break;
372                         case OPENPGP_PACKET_SYMSESSIONKEY:
373                                 /* These packets must be v4 */
374                                 if (curpacket->packet->data[0] != 4) {
375                                         rc = ONAK_E_INVALID_PKT;
376                                 }
377                                 break;
378                         case OPENPGP_PACKET_SIGNATURE:
379                         case OPENPGP_PACKET_SECRETKEY:
380                         case OPENPGP_PACKET_PUBLICKEY:
381                                 /* Must be v2 -> v5 */
382                                 if (curpacket->packet->data[0] < 2 ||
383                                         curpacket->packet->data[0] > 5) {
384                                         rc = ONAK_E_INVALID_PKT;
385                                 }
386                                 break;
387                         default:
388                                 break;
389                         }
390                 }
391         }
392
393         if (packetend != NULL) {
394                 if ((*packetend)->packet != NULL) {
395                         /* If we got an invalid final packet, discard it. */
396                         if ((*packetend)->packet->data != NULL &&
397                                         rc != ONAK_E_OK) {
398                                 free((*packetend)->packet->data);
399                                 (*packetend)->packet->data = NULL;
400                         }
401                         /* If we didn't get any data, clean it up. */
402                         if ((*packetend)->packet->data == NULL) {
403                                 free((*packetend)->packet);
404                                 (*packetend)->packet = NULL;
405                         }
406                 }
407                 /* Trim the last packet if it doesn't actually exist */
408                 if ((*packetend)->packet == NULL) {
409                         free(*packetend);
410                         *packetend = NULL;
411                 }
412         }
413
414         return (rc);
415 }
416
417 /**
418  *      write_openpgp_stream - Reads a stream of OpenPGP packets.
419  *      @putchar_func: The function to put the next character to the stream.
420  *      @ctx: A pointer to the context structure for putchar_func.
421  *      @packets: The list of packets.
422  *
423  *      This function uses putchar_func to write characters to an OpenPGP
424  *      packet stream from a linked list of packets.
425  */
426 onak_status_t write_openpgp_stream(size_t (*putchar_func)(void *ctx, size_t count,
427                                                 void *c),
428                                 void *ctx,
429                                 struct openpgp_packet_list *packets)
430 {
431         unsigned char   curchar = 0;
432
433         while (packets != NULL) {
434                 curchar = 0x80;
435                 if (packets->packet->newformat) {
436                         curchar |= 0x40;
437                         curchar |= packets->packet->tag;
438                         putchar_func(ctx, 1, &curchar);
439
440                         if (packets->packet->length < 192) {
441                                 curchar = packets->packet->length;
442                                 putchar_func(ctx, 1, &curchar);
443                         } else if (packets->packet->length > 191 &&
444                                 packets->packet->length < 8383) {
445                                 curchar = (((packets->packet->length - 192) &
446                                          0xFF00) >> 8) + 192;
447                                 putchar_func(ctx, 1, &curchar);
448
449                                 curchar = (packets->packet->length - 192) &
450                                          0xFF;
451                                 putchar_func(ctx, 1, &curchar);
452                         } else if (packets->packet->length > 8382 &&
453                                 packets->packet->length < 0xFFFFFFFF) {
454                                 curchar = 255;
455                                 putchar_func(ctx, 1, &curchar);
456                                 
457                                 curchar = (packets->packet->length >> 24);
458                                 curchar &= 0xFF;
459                                 putchar_func(ctx, 1, &curchar);
460                                 
461                                 curchar = (packets->packet->length >> 16);
462                                 curchar &= 0xFF;
463                                 putchar_func(ctx, 1, &curchar);
464                                 
465                                 curchar = (packets->packet->length >> 8);
466                                 curchar &= 0xFF;
467                                 putchar_func(ctx, 1, &curchar);
468                                 
469                                 curchar = packets->packet->length;
470                                 curchar &= 0xFF;
471                                 putchar_func(ctx, 1, &curchar);
472                         } else {
473                                 return ONAK_E_UNSUPPORTED_FEATURE;
474                         }
475                 } else {
476                         curchar |= (packets->packet->tag << 2);
477                         if (packets->packet->length < 256) {
478                                 putchar_func(ctx, 1, &curchar);
479                                 curchar = packets->packet->length;
480                                 putchar_func(ctx, 1, &curchar);
481                         } else if (packets->packet->length < 0x10000) {
482                                 curchar |= 1;
483                                 putchar_func(ctx, 1, &curchar);
484                                 curchar = packets->packet->length >> 8;
485                                 putchar_func(ctx, 1, &curchar);
486                                 curchar = packets->packet->length & 0xFF;
487                                 putchar_func(ctx, 1, &curchar);
488                         } else {
489                                 curchar |= 2;
490                                 putchar_func(ctx, 1, &curchar);
491                                 curchar = packets->packet->length >> 24;
492                                 putchar_func(ctx, 1, &curchar);
493                                 curchar = (packets->packet->length >> 16) & 0xFF;
494                                 putchar_func(ctx, 1, &curchar);
495                                 curchar = (packets->packet->length >> 8) & 0xFF;
496                                 putchar_func(ctx, 1, &curchar);
497                                 curchar = packets->packet->length & 0xFF;
498                                 putchar_func(ctx, 1, &curchar);
499                         }
500                 }
501
502                 putchar_func(ctx, packets->packet->length,
503                                 packets->packet->data);
504                 packets = packets->next;
505         }
506
507         return ONAK_E_OK;
508 }
509
510 /**
511  *      flatten_publickey - Convert a publickey to an OpenPGP packet list.
512  *      @key: The public key.
513  *      @packets: The outputted packet list.
514  *
515  *      This function converts public key structure to a linked list of OpenPGP
516  *      packets ready for outputing or storage.
517  */
518 int flatten_publickey(struct openpgp_publickey *key,
519                         struct openpgp_packet_list **packets,
520                         struct openpgp_packet_list **list_end)
521 {
522         struct openpgp_signedpacket_list        *tmpsignedlist = NULL;
523         struct openpgp_packet_list              *tmplist = NULL;
524
525         while (key != NULL) {
526                 /*
527                  * First write the public key packet out.
528                  */
529                 ADD_PACKET_TO_LIST((*list_end), packet_dup(key->publickey));
530                 if (*packets == NULL) {
531                         *packets = *list_end;
532                 }
533
534                 /*
535                  * Now do any signatures on the main key.
536                  */
537                 for (tmplist = key->sigs; tmplist != NULL;
538                                 tmplist = tmplist->next) {
539                         ADD_PACKET_TO_LIST((*list_end),
540                                         packet_dup(tmplist->packet));
541                 }
542
543                 /*
544                  * Output any UIDs along with their signatures.
545                  */
546                 for (tmpsignedlist = key->uids; tmpsignedlist != NULL;
547                                 tmpsignedlist = tmpsignedlist->next) {
548
549                         ADD_PACKET_TO_LIST((*list_end),
550                                 packet_dup(tmpsignedlist->packet));
551                         for (tmplist = tmpsignedlist->sigs; tmplist != NULL;
552                                         tmplist = tmplist->next) {
553                                 ADD_PACKET_TO_LIST((*list_end), 
554                                         packet_dup(tmplist->packet));
555                         }
556                 }
557
558                 /*
559                  * Output any subkeys along with their signatures.
560                  */
561                 for (tmpsignedlist = key->subkeys; tmpsignedlist != NULL;
562                                 tmpsignedlist = tmpsignedlist->next) {
563
564                         ADD_PACKET_TO_LIST((*list_end),
565                                 packet_dup(tmpsignedlist->packet));
566                         for (tmplist = tmpsignedlist->sigs; tmplist != NULL;
567                                         tmplist = tmplist->next) {
568                                 ADD_PACKET_TO_LIST((*list_end), 
569                                         packet_dup(tmplist->packet));
570                         }
571                 }
572                 key = key->next;
573         }
574         return 0;
575 }