Move over to log_assert.
[onak.git] / parsekey.c
1 /*
2  * parsekey.c - Routines to parse an OpenPGP key.
3  *
4  * Jonathan McDowell <noodles@earth.li>
5  *
6  * Copyright 2002 Project Purple
7  */
8
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #include "keyid.h"
15 #include "keystructs.h"
16 #include "ll.h"
17 #include "log.h"
18 #include "mem.h"
19 #include "parsekey.h"
20
21 /**
22  *      parse_keys - Process a stream of packets for public keys + sigs.
23  *      @packets: The packet list to parse.
24  *      @keys: The returned list of public keys.
25  *
26  *      This function takes an list of OpenPGP packets and attempts to parse it
27  *      into a list of public keys with signatures and subkeys.
28  *
29  *      Returns a count of how many keys we parsed.
30  */
31 int parse_keys(struct openpgp_packet_list *packets,
32                 struct openpgp_publickey **keys)
33 {
34         struct openpgp_publickey *curkey = NULL;
35         int count;
36
37         count = 0;
38
39         /*
40          * If keys already has some keys in it then set curkey to the last one
41          * so we add to the end of the list.
42          */
43         for (curkey = *keys; curkey != NULL && curkey->next != NULL;
44                         curkey = curkey->next) ;
45
46         while (packets != NULL) {
47                 switch (packets->packet->tag) {
48                 case 2:
49                         /*
50                          * It's a signature packet. Add it to either the public
51                          * key (it should be a revocation), to the current UID
52                          * or the current subkey.
53                          */
54                         log_assert(curkey != NULL);
55                         if (curkey->subkeys != NULL) {
56                                 ADD_PACKET_TO_LIST_END(curkey->last_subkey,
57                                         sig,
58                                         packet_dup(packets->packet));
59                         } else if (curkey->uids != NULL) {
60                                 ADD_PACKET_TO_LIST_END(curkey->last_uid,
61                                         sig,
62                                         packet_dup(packets->packet));
63                         } else {
64                                 ADD_PACKET_TO_LIST_END(curkey,
65                                         revocation,
66                                         packet_dup(packets->packet));
67                         }
68                         break;
69                 case 6:
70                         /*
71                          * It's a public key packet, so start a new key in our
72                          * list.
73                          */
74                         if (curkey != NULL) {
75                                 curkey->next = malloc(sizeof (*curkey));
76                                 curkey = curkey->next;
77                         } else {
78                                 *keys = curkey =
79                                         malloc(sizeof (*curkey));
80                         }
81                         memset(curkey, 0, sizeof(*curkey));
82                         curkey->publickey = packet_dup(packets->packet);
83                         count++;
84                         break;
85                 case 13:
86                 case 17:
87                         /*
88                          * It's a UID packet (or a photo id, which is similar).
89                          */
90                         log_assert(curkey != NULL);
91                         log_assert(curkey->subkeys == NULL);
92                         ADD_PACKET_TO_LIST_END(curkey,
93                                 uid,
94                                 packet_dup(packets->packet));
95                         break;
96                 case 14:
97                         /*
98                          * It's a subkey packet.
99                          */
100                         log_assert(curkey != NULL);
101                         ADD_PACKET_TO_LIST_END(curkey,
102                                 subkey,
103                                 packet_dup(packets->packet));
104                         break;
105                 case 12:
106                 case 61:
107                         /*
108                          * One of:
109                          *
110                          * Trust packet. Ignore.
111                          * Comment packet. Ignore.
112                          */
113                         break;
114                 default:
115                         logthing(LOGTHING_ERROR,
116                                         "Unsupported packet type: %d",
117                                         packets->packet->tag);
118                 }
119                 packets = packets->next;
120         }
121
122         return count;
123 }
124
125 /**
126  *      debug_packet - Print debug info about a packet
127  *      @packet: The packet to display.
128  *
129  *      This function takes an OpenPGP packet and displays some information
130  *      about it to stdout. Useful for debugging purposes or curiousity about
131  *      an OpenPGP packet stream.
132  */
133 int debug_packet(struct openpgp_packet *packet)
134 {
135         printf("\tNew format: %d, Tag: %u, Length: %d\n",
136                         packet->newformat,
137                         packet->tag,
138                         packet->length);
139
140         return 0;
141 }
142
143 /**
144  *      read_openpgp_stream - Reads a stream of OpenPGP packets.
145  *      @getchar_func: The function to get the next character from the stream.
146  *      @ctx: A pointer to the context structure for getchar_func.
147  *      @packets: The outputted list of packets.
148  *      @maxnum: The maximum number of keys to read. 0 means unlimited.
149  *
150  *      This function uses getchar_func to read characters from an OpenPGP
151  *      packet stream and reads the packets into a linked list of packets
152  *      ready for parsing as a public key or whatever.
153  */
154 int read_openpgp_stream(int (*getchar_func)(void *ctx, size_t count,
155                                 unsigned char *c),
156                                 void *ctx,
157                                 struct openpgp_packet_list **packets,
158                                 int maxnum)
159 {
160         unsigned char                    curchar = 0;
161         struct openpgp_packet_list      *curpacket = NULL;
162         int                              rc = 0;
163         int                              keys = 0;
164         bool                             inpacket = false;
165
166         log_assert(packets != NULL);
167         curpacket = *packets;
168         if (curpacket != NULL) {
169                 while (curpacket->next != NULL) {
170                         curpacket = curpacket->next;
171                 }
172         }
173
174         while (!rc && (maxnum == 0 || keys < maxnum) &&
175                         !getchar_func(ctx, 1, &curchar)) {
176                 if (!inpacket && (curchar & 0x80)) {
177                         /*
178                          * New packet. Record the fact we're in a packet and
179                          * allocate memory for it.
180                          */
181                         inpacket = true;
182                         if (curpacket != NULL) {
183                                 curpacket->next = malloc(sizeof (*curpacket));
184                                 curpacket = curpacket->next;
185                         } else {
186                                 *packets = curpacket =
187                                         malloc(sizeof (*curpacket));
188                         }
189                         memset(curpacket, 0, sizeof(*curpacket));
190                         curpacket->packet =
191                                 malloc(sizeof (*curpacket->packet));
192                         memset(curpacket->packet, 0,
193                                         sizeof(*curpacket->packet));
194
195                         curpacket->packet->newformat = (curchar & 0x40);
196
197                         /*
198                          * TODO: Better error checking on getchar_func.
199                          */
200                         if (curpacket->packet->newformat) {
201                                 curpacket->packet->tag = (curchar & 0x3F);
202                                 rc = getchar_func(ctx, 1, &curchar);
203                                 curpacket->packet->length = curchar;
204                                 if (curpacket->packet->length > 191 &&
205                                         curpacket->packet->length < 224) {
206                                         rc = getchar_func(ctx, 1, &curchar);
207                                         curpacket->packet->length -= 192;
208                                         curpacket->packet->length <<= 8;
209                                         curpacket->packet->length += curchar;
210                                         curpacket->packet->length += 192;
211                                 } else if (curpacket->packet->length > 223 &&
212                                         curpacket->packet->length < 255) {
213                                         logthing(LOGTHING_NOTICE,
214                                                 "Partial length;"
215                                                 " not supported.");
216                                 } else if (curpacket->packet->length == 255) {
217                                         /*
218                                          * 5 byte length; ie 255 followed by 3
219                                          * bytes of MSB length.
220                                          */
221                                         rc = getchar_func(ctx, 1, &curchar);
222                                         curpacket->packet->length = curchar;
223                                         curpacket->packet->length <<= 8;
224                                         rc = getchar_func(ctx, 1, &curchar);
225                                         curpacket->packet->length += curchar;
226                                         curpacket->packet->length <<= 8;
227                                         rc = getchar_func(ctx, 1, &curchar);
228                                         curpacket->packet->length += curchar;
229                                         curpacket->packet->length <<= 8;
230                                         rc = getchar_func(ctx, 1, &curchar);
231                                         curpacket->packet->length += curchar;
232                                 }
233                         } else {
234                                 curpacket->packet->tag = (curchar & 0x3C) >> 2;
235                                 switch (curchar & 3) {
236                                 case 0:
237                                         rc = getchar_func(ctx, 1, &curchar);
238                                         curpacket->packet->length = curchar;
239                                         break;
240                                 case 1:
241                                         rc = getchar_func(ctx, 1, &curchar);
242                                         curpacket->packet->length = curchar;
243                                         curpacket->packet->length <<= 8;
244                                         rc = getchar_func(ctx, 1, &curchar);
245                                         curpacket->packet->length += curchar;
246                                         break;
247                                 case 2:
248                                         rc = getchar_func(ctx, 1, &curchar);
249                                         curpacket->packet->length = 
250                                                 (curchar << 24);
251                                         rc = getchar_func(ctx, 1, &curchar);
252                                         curpacket->packet->length +=
253                                                 (curchar << 16);
254                                         rc = getchar_func(ctx, 1, &curchar);
255                                         curpacket->packet->length +=
256                                                 (curchar << 8);
257                                         rc = getchar_func(ctx, 1, &curchar);
258                                         curpacket->packet->length += curchar;
259                                         break;
260                                 case 3:
261                                         logthing(LOGTHING_ERROR,
262                                                 "Unsupported length type 3.");
263                                         curpacket->packet->length = 0;
264                                         curpacket->packet->data = NULL;
265                                         rc = -1;
266                                         break;
267                                 }
268                         }
269
270                         if (rc == 0) {
271                                 if (curpacket->packet->tag == 6) {
272                                         keys++;
273                                 }
274                                 curpacket->packet->data =
275                                         malloc(curpacket->packet->length *
276                                         sizeof(unsigned char));
277                                 if (curpacket->packet->data == NULL) {
278                                         logthing(LOGTHING_ERROR, 
279                                                 "Can't allocate memory for "
280                                                 "packet!");
281                                         rc = -1;
282                                 } else {
283                                         rc = getchar_func(ctx,
284                                                 curpacket->packet->length,
285                                                 curpacket->packet->data);
286                                 }
287                         }
288                         inpacket = false;
289                 } else {
290                         logthing(LOGTHING_ERROR, "Unexpected character: 0x%X",
291                                 curchar);
292                         rc = 1;
293                 }
294         }
295
296         return (rc);
297 }
298
299 /**
300  *      write_openpgp_stream - Reads a stream of OpenPGP packets.
301  *      @putchar_func: The function to put the next character to the stream.
302  *      @ctx: A pointer to the context structure for putchar_func.
303  *      @packets: The list of packets.
304  *
305  *      This function uses putchar_func to write characters to an OpenPGP
306  *      packet stream from a linked list of packets.
307  */
308 int write_openpgp_stream(int (*putchar_func)(void *ctx, size_t count,
309                                                 unsigned char *c),
310                                 void *ctx,
311                                 struct openpgp_packet_list *packets)
312 {
313         unsigned char   curchar = 0;
314
315         while (packets != NULL) {
316                 curchar = 0x80;
317                 if (packets->packet->newformat) {
318                         curchar |= 0x40;
319                         curchar |= packets->packet->tag;
320                         putchar_func(ctx, 1, &curchar);
321
322                         if (packets->packet->length < 192) {
323                                 curchar = packets->packet->length;
324                                 putchar_func(ctx, 1, &curchar);
325                         } else if (packets->packet->length > 191 &&
326                                 packets->packet->length < 8383) {
327                                 curchar = (((packets->packet->length - 192) &
328                                          0xFF00) >> 8) + 192;
329                                 putchar_func(ctx, 1, &curchar);
330
331                                 curchar = (packets->packet->length - 192) &
332                                          0xFF;
333                                 putchar_func(ctx, 1, &curchar);
334                         } else if (packets->packet->length > 8382 &&
335                                 packets->packet->length < 0xFFFFFFFF) {
336                                 logthing(LOGTHING_DEBUG,
337                                         "Writing 5 byte length");
338                                 curchar = 255;
339                                 putchar_func(ctx, 1, &curchar);
340                                 
341                                 curchar = (packets->packet->length >> 24);
342                                 curchar &= 0xFF;
343                                 putchar_func(ctx, 1, &curchar);
344                                 
345                                 curchar = (packets->packet->length >> 16);
346                                 curchar &= 0xFF;
347                                 putchar_func(ctx, 1, &curchar);
348                                 
349                                 curchar = (packets->packet->length >> 8);
350                                 curchar &= 0xFF;
351                                 putchar_func(ctx, 1, &curchar);
352                                 
353                                 curchar = packets->packet->length;
354                                 curchar &= 0xFF;
355                                 putchar_func(ctx, 1, &curchar);
356                         } else {
357                                 logthing(LOGTHING_ERROR,
358                                         "Unsupported new format length.");
359                         }
360                 } else {
361                         curchar |= (packets->packet->tag << 2);
362                         if (packets->packet->length < 256) {
363                                 putchar_func(ctx, 1, &curchar);
364                                 curchar = packets->packet->length;
365                                 putchar_func(ctx, 1, &curchar);
366                         } else if (packets->packet->length < 0x10000) {
367                                 curchar |= 1;
368                                 putchar_func(ctx, 1, &curchar);
369                                 curchar = packets->packet->length >> 8;
370                                 putchar_func(ctx, 1, &curchar);
371                                 curchar = packets->packet->length & 0xFF;
372                                 putchar_func(ctx, 1, &curchar);
373                         } else {
374                                 curchar |= 2;
375                                 putchar_func(ctx, 1, &curchar);
376                                 curchar = packets->packet->length >> 24;
377                                 putchar_func(ctx, 1, &curchar);
378                                 curchar = (packets->packet->length >> 16) & 0xFF;
379                                 putchar_func(ctx, 1, &curchar);
380                                 curchar = (packets->packet->length >> 8) & 0xFF;
381                                 putchar_func(ctx, 1, &curchar);
382                                 curchar = packets->packet->length & 0xFF;
383                                 putchar_func(ctx, 1, &curchar);
384                         }
385                 }
386
387                 putchar_func(ctx, packets->packet->length,
388                                 packets->packet->data);
389                 packets = packets->next;
390         }
391         return 0;
392 }
393
394 /**
395  *      flatten_publickey - Convert a publickey to an OpenPGP packet list.
396  *      @key: The public key.
397  *      @packets: The outputted packet list.
398  *
399  *      This function converts public key structure to a linked list of OpenPGP
400  *      packets ready for outputing or storage.
401  */
402 int flatten_publickey(struct openpgp_publickey *key,
403                         struct openpgp_packet_list **packets,
404                         struct openpgp_packet_list **list_end)
405 {
406         struct openpgp_signedpacket_list        *tmpsignedlist = NULL;
407         struct openpgp_packet_list              *tmplist = NULL;
408
409         while (key != NULL) {
410                 /*
411                  * First write the public key packet out.
412                  */
413                 ADD_PACKET_TO_LIST((*list_end), packet_dup(key->publickey));
414                 if (*packets == NULL) {
415                         *packets = *list_end;
416                 }
417
418                 /*
419                  * Now do any revocation signatures on the main key.
420                  */
421                 for (tmplist = key->revocations; tmplist != NULL;
422                                 tmplist = tmplist->next) {
423                         ADD_PACKET_TO_LIST((*list_end),
424                                         packet_dup(tmplist->packet));
425                 }
426
427                 /*
428                  * Output any UIDs along with their signatures.
429                  */
430                 for (tmpsignedlist = key->uids; tmpsignedlist != NULL;
431                                 tmpsignedlist = tmpsignedlist->next) {
432
433                         ADD_PACKET_TO_LIST((*list_end),
434                                 packet_dup(tmpsignedlist->packet));
435                         for (tmplist = tmpsignedlist->sigs; tmplist != NULL;
436                                         tmplist = tmplist->next) {
437                                 ADD_PACKET_TO_LIST((*list_end), 
438                                         packet_dup(tmplist->packet));
439                         }
440                 }
441
442                 /*
443                  * Output any subkeys along with their signatures.
444                  */
445                 for (tmpsignedlist = key->subkeys; tmpsignedlist != NULL;
446                                 tmpsignedlist = tmpsignedlist->next) {
447
448                         ADD_PACKET_TO_LIST((*list_end),
449                                 packet_dup(tmpsignedlist->packet));
450                         for (tmplist = tmpsignedlist->sigs; tmplist != NULL;
451                                         tmplist = tmplist->next) {
452                                 ADD_PACKET_TO_LIST((*list_end), 
453                                         packet_dup(tmplist->packet));
454                         }
455                 }
456                 key = key->next;
457         }
458         return 0;
459 }