]> the.earth.li Git - onak.git/blob - parsekey.c
Fix handling of other signature requirement
[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(int (*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)) {
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)) {
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);
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 3
254                                          * bytes of MSB length.
255                                          */
256                                         if (getchar_func(ctx, 1, &curchar)) {
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)) {
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)) {
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)) {
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)) {
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)) {
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)) {
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)) {
305                                                 rc = ONAK_E_INVALID_PKT;
306                                                 break;
307                                         }
308                                         curpacket->packet->length = 
309                                                 ((unsigned) curchar << 24);
310                                         if (getchar_func(ctx, 1, &curchar)) {
311                                                 rc = ONAK_E_INVALID_PKT;
312                                                 break;
313                                         }
314                                         curpacket->packet->length +=
315                                                 (curchar << 16);
316                                         if (getchar_func(ctx, 1, &curchar)) {
317                                                 rc = ONAK_E_INVALID_PKT;
318                                                 break;
319                                         }
320                                         curpacket->packet->length +=
321                                                 (curchar << 8);
322                                         if (getchar_func(ctx, 1, &curchar)) {
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                                 }
351                         }
352                 } else {
353                         rc = ONAK_E_INVALID_PKT;
354                 }
355                 if (rc == ONAK_E_OK) {
356                         /* Make sure the packet version is sane */
357                         switch (curpacket->packet->tag) {
358                         case OPENPGP_PACKET_ENCRYPTED_MDC:
359                                 /* These packets must be v1 */
360                                 if (curpacket->packet->data[0] != 1) {
361                                         rc = ONAK_E_INVALID_PKT;
362                                 }
363                                 break;
364                         case OPENPGP_PACKET_PKSESSIONKEY:
365                         case OPENPGP_PACKET_ONEPASSSIG:
366                                 /* These packets must be v3 */
367                                 if (curpacket->packet->data[0] != 3) {
368                                         rc = ONAK_E_INVALID_PKT;
369                                 }
370                                 break;
371                         case OPENPGP_PACKET_SYMSESSIONKEY:
372                                 /* These packets must be v4 */
373                                 if (curpacket->packet->data[0] != 4) {
374                                         rc = ONAK_E_INVALID_PKT;
375                                 }
376                                 break;
377                         case OPENPGP_PACKET_SIGNATURE:
378                         case OPENPGP_PACKET_SECRETKEY:
379                         case OPENPGP_PACKET_PUBLICKEY:
380                                 /* Must be v2 -> v5 */
381                                 if (curpacket->packet->data[0] < 2 ||
382                                         curpacket->packet->data[0] > 5) {
383                                         rc = ONAK_E_INVALID_PKT;
384                                 }
385                                 break;
386                         default:
387                                 break;
388                         }
389                 }
390         }
391
392         if (packetend != NULL) {
393                 if ((*packetend)->packet != NULL) {
394                         /* If we got an invalid final packet, discard it. */
395                         if ((*packetend)->packet->data != NULL &&
396                                         rc != ONAK_E_OK) {
397                                 free((*packetend)->packet->data);
398                                 (*packetend)->packet->data = NULL;
399                         }
400                         /* If we didn't get any data, clean it up. */
401                         if ((*packetend)->packet->data == NULL) {
402                                 free((*packetend)->packet);
403                                 (*packetend)->packet = NULL;
404                         }
405                 }
406                 /* Trim the last packet if it doesn't actually exist */
407                 if ((*packetend)->packet == NULL) {
408                         free(*packetend);
409                         *packetend = NULL;
410                 }
411         }
412
413         return (rc);
414 }
415
416 /**
417  *      write_openpgp_stream - Reads a stream of OpenPGP packets.
418  *      @putchar_func: The function to put the next character to the stream.
419  *      @ctx: A pointer to the context structure for putchar_func.
420  *      @packets: The list of packets.
421  *
422  *      This function uses putchar_func to write characters to an OpenPGP
423  *      packet stream from a linked list of packets.
424  */
425 onak_status_t write_openpgp_stream(int (*putchar_func)(void *ctx, size_t count,
426                                                 void *c),
427                                 void *ctx,
428                                 struct openpgp_packet_list *packets)
429 {
430         unsigned char   curchar = 0;
431
432         while (packets != NULL) {
433                 curchar = 0x80;
434                 if (packets->packet->newformat) {
435                         curchar |= 0x40;
436                         curchar |= packets->packet->tag;
437                         putchar_func(ctx, 1, &curchar);
438
439                         if (packets->packet->length < 192) {
440                                 curchar = packets->packet->length;
441                                 putchar_func(ctx, 1, &curchar);
442                         } else if (packets->packet->length > 191 &&
443                                 packets->packet->length < 8383) {
444                                 curchar = (((packets->packet->length - 192) &
445                                          0xFF00) >> 8) + 192;
446                                 putchar_func(ctx, 1, &curchar);
447
448                                 curchar = (packets->packet->length - 192) &
449                                          0xFF;
450                                 putchar_func(ctx, 1, &curchar);
451                         } else if (packets->packet->length > 8382 &&
452                                 packets->packet->length < 0xFFFFFFFF) {
453                                 curchar = 255;
454                                 putchar_func(ctx, 1, &curchar);
455                                 
456                                 curchar = (packets->packet->length >> 24);
457                                 curchar &= 0xFF;
458                                 putchar_func(ctx, 1, &curchar);
459                                 
460                                 curchar = (packets->packet->length >> 16);
461                                 curchar &= 0xFF;
462                                 putchar_func(ctx, 1, &curchar);
463                                 
464                                 curchar = (packets->packet->length >> 8);
465                                 curchar &= 0xFF;
466                                 putchar_func(ctx, 1, &curchar);
467                                 
468                                 curchar = packets->packet->length;
469                                 curchar &= 0xFF;
470                                 putchar_func(ctx, 1, &curchar);
471                         } else {
472                                 return ONAK_E_UNSUPPORTED_FEATURE;
473                         }
474                 } else {
475                         curchar |= (packets->packet->tag << 2);
476                         if (packets->packet->length < 256) {
477                                 putchar_func(ctx, 1, &curchar);
478                                 curchar = packets->packet->length;
479                                 putchar_func(ctx, 1, &curchar);
480                         } else if (packets->packet->length < 0x10000) {
481                                 curchar |= 1;
482                                 putchar_func(ctx, 1, &curchar);
483                                 curchar = packets->packet->length >> 8;
484                                 putchar_func(ctx, 1, &curchar);
485                                 curchar = packets->packet->length & 0xFF;
486                                 putchar_func(ctx, 1, &curchar);
487                         } else {
488                                 curchar |= 2;
489                                 putchar_func(ctx, 1, &curchar);
490                                 curchar = packets->packet->length >> 24;
491                                 putchar_func(ctx, 1, &curchar);
492                                 curchar = (packets->packet->length >> 16) & 0xFF;
493                                 putchar_func(ctx, 1, &curchar);
494                                 curchar = (packets->packet->length >> 8) & 0xFF;
495                                 putchar_func(ctx, 1, &curchar);
496                                 curchar = packets->packet->length & 0xFF;
497                                 putchar_func(ctx, 1, &curchar);
498                         }
499                 }
500
501                 putchar_func(ctx, packets->packet->length,
502                                 packets->packet->data);
503                 packets = packets->next;
504         }
505
506         return ONAK_E_OK;
507 }
508
509 /**
510  *      flatten_publickey - Convert a publickey to an OpenPGP packet list.
511  *      @key: The public key.
512  *      @packets: The outputted packet list.
513  *
514  *      This function converts public key structure to a linked list of OpenPGP
515  *      packets ready for outputing or storage.
516  */
517 int flatten_publickey(struct openpgp_publickey *key,
518                         struct openpgp_packet_list **packets,
519                         struct openpgp_packet_list **list_end)
520 {
521         struct openpgp_signedpacket_list        *tmpsignedlist = NULL;
522         struct openpgp_packet_list              *tmplist = NULL;
523
524         while (key != NULL) {
525                 /*
526                  * First write the public key packet out.
527                  */
528                 ADD_PACKET_TO_LIST((*list_end), packet_dup(key->publickey));
529                 if (*packets == NULL) {
530                         *packets = *list_end;
531                 }
532
533                 /*
534                  * Now do any signatures on the main key.
535                  */
536                 for (tmplist = key->sigs; tmplist != NULL;
537                                 tmplist = tmplist->next) {
538                         ADD_PACKET_TO_LIST((*list_end),
539                                         packet_dup(tmplist->packet));
540                 }
541
542                 /*
543                  * Output any UIDs along with their signatures.
544                  */
545                 for (tmpsignedlist = key->uids; tmpsignedlist != NULL;
546                                 tmpsignedlist = tmpsignedlist->next) {
547
548                         ADD_PACKET_TO_LIST((*list_end),
549                                 packet_dup(tmpsignedlist->packet));
550                         for (tmplist = tmpsignedlist->sigs; tmplist != NULL;
551                                         tmplist = tmplist->next) {
552                                 ADD_PACKET_TO_LIST((*list_end), 
553                                         packet_dup(tmplist->packet));
554                         }
555                 }
556
557                 /*
558                  * Output any subkeys along with their signatures.
559                  */
560                 for (tmpsignedlist = key->subkeys; tmpsignedlist != NULL;
561                                 tmpsignedlist = tmpsignedlist->next) {
562
563                         ADD_PACKET_TO_LIST((*list_end),
564                                 packet_dup(tmpsignedlist->packet));
565                         for (tmplist = tmpsignedlist->sigs; tmplist != NULL;
566                                         tmplist = tmplist->next) {
567                                 ADD_PACKET_TO_LIST((*list_end), 
568                                         packet_dup(tmplist->packet));
569                         }
570                 }
571                 key = key->next;
572         }
573         return 0;
574 }