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