]> git.sommitrealweird.co.uk Git - onak.git/blob - merge.c
33924f05e3078b886a94afb702b4e331b9a9f51f
[onak.git] / merge.c
1 /*
2  * merge.c - Routines to merge OpenPGP public keys.
3  *
4  * Jonathan McDowell <noodles@earth.li>
5  *
6  * Copyright 2002 Project Purple
7  */
8
9 #include <assert.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #include "decodekey.h"
15 #include "keydb.h"
16 #include "keyid.h"
17 #include "keystructs.h"
18 #include "ll.h"
19 #include "log.h"
20 #include "mem.h"
21 #include "merge.h"
22
23 /**
24  *      compare_packets - Check to see if 2 OpenPGP packets are the same.
25  *      @a: The first packet to compare.
26  *      @b: The second packet to compare.
27  *
28  *      Takes 2 packets and returns true if they are the same and false
29  *      otherwise.
30  */
31 bool compare_packets(struct openpgp_packet *a, struct openpgp_packet *b)
32 {
33         return (a->tag == b->tag && a->length == b->length &&
34                 !memcmp(a->data, b->data, b->length));
35 }
36
37 /**
38  *      compare_signatures - Check to see if 2 OpenPGP signatures are the same.
39  *      @a: The first signature to compare.
40  *      @b: The second signature to compare.
41  *
42  *      Takes 2 signature packets and returns true if they are the same and
43  *      false otherwise.
44  */
45 bool compare_signatures(struct openpgp_packet *a, struct openpgp_packet *b)
46 {
47         return (sig_keyid(a) == sig_keyid(b));
48 }
49
50 /**
51  *      find_packet - Checks to see if an OpenPGP packet exists in a list.
52  *      @packet_list: The list of packets to look in.
53  *      @packet: The packet to look for.
54  *
55  *      Walks through the packet_list checking to see if the packet given is
56  *      present in it. Returns true if it is.
57  */
58 bool find_packet(struct openpgp_packet_list *packet_list,
59                         struct openpgp_packet *packet)
60 {
61         bool found = false;
62
63         while (!found && packet_list != NULL) {
64                 if (compare_packets(packet_list->packet, packet)) {
65                         found = true;
66                 }
67                 packet_list = packet_list -> next;
68         }
69
70         return found;
71 }
72
73 /**
74  *      find_signature - Checks to see if an OpenPGP signature exists in a list.
75  *      @packet_list: The list of packets to look in.
76  *      @packet: The signature to look for.
77  *
78  *      Walks through the packet_list checking to see if the signature given is
79  *      present in it. Returns a pointer to it if it is, NULL otherwise.
80  *
81  */
82 struct openpgp_packet_list *find_signature(
83                         struct openpgp_packet_list *packet_list,
84                         struct openpgp_packet *packet)
85 {
86         struct openpgp_packet_list *found = NULL;
87
88         while (!found && packet_list != NULL) {
89                 if (compare_signatures(packet_list->packet, packet)) {
90                         found = packet_list;
91                 }
92                 packet_list = packet_list -> next;
93         }
94
95         return found;
96 }
97
98 /**
99  *      get_signed_packet - Gets a signed packet from a list.
100  *      @packet_list: The list of packets to look in.
101  *      @packet: The packet to look for.
102  *
103  *      Walks through the signedpacket_list looking for the supplied packet and
104  *      returns it if found. Otherwise returns NULL.
105  */
106 struct openpgp_signedpacket_list *find_signed_packet(
107                 struct openpgp_signedpacket_list *packet_list,
108                 struct openpgp_packet *packet)
109 {
110         struct openpgp_signedpacket_list *found = NULL;
111
112         while (found == NULL && packet_list != NULL) {
113                 if (compare_packets(packet_list->packet, packet)) {
114                         found = packet_list;
115                 }
116                 packet_list = packet_list -> next;
117         }
118
119         return found;
120 }
121
122 /**
123  *      remove_signed_packet - Removes a signed packet from a list.
124  *      @packet_list: The list of packets to look in.
125  *      @packet: The packet to remove.
126  *
127  *      Walks through the signedpacket_list looking for the supplied packet and
128  *      removes it if found. Assumes the packet can only exist a maximum of
129  *      once in the list.
130  */
131 bool remove_signed_packet(struct openpgp_signedpacket_list **packet_list,
132                 struct openpgp_signedpacket_list **list_end,
133                 struct openpgp_packet *packet)
134 {
135         struct openpgp_signedpacket_list *cur = NULL;
136         struct openpgp_signedpacket_list *prev = NULL;
137         bool found = false;
138
139         for (cur = *packet_list; !found && (cur != NULL); cur = cur->next) {
140                 if (compare_packets(cur->packet, packet)) {
141                         found = true;
142                         if (prev == NULL) {
143                                 *packet_list = cur->next;
144                         } else {
145                                 prev->next = cur->next;
146                         }
147                         if (cur->next == NULL) {
148                                 *list_end = prev;
149                         }
150                         // TODO: Free the removed signed packet...
151                 }
152                 prev = cur;
153         }
154
155         return found;
156 }
157
158 /**
159  *      merge_packet_sigs - Takes 2 signed packets and merges their sigs.
160  *      @old: The old signed packet.
161  *      @new: The new signed packet.
162  *
163  *      Takes 2 signed packet list structures and the sigs of the packets on
164  *      the head of these structures. These packets must both be the same and
165  *      the fully merged structure is returned in old and the minimal
166  *      difference to get from old to new in new.
167  */
168 int merge_packet_sigs(struct openpgp_signedpacket_list *old,
169                         struct openpgp_signedpacket_list *new)
170 {
171         struct openpgp_packet_list      *lastpacket = NULL;
172         struct openpgp_packet_list      *curpacket = NULL;
173         struct openpgp_packet_list      *nextpacket = NULL;
174
175         assert(compare_packets(old->packet, new->packet));
176
177         curpacket = new->sigs;
178         while (curpacket != NULL) {
179                 nextpacket = curpacket->next;
180                 /*
181                  * TODO: We should be checking the signature and then
182                  * potentially merging/replacing it depending on the subpackets
183                  * really. For now this stops us adding the same one twice
184                  * however.
185                  */ 
186                 if (find_signature(old->sigs, curpacket->packet)) {
187                         /*
188                          * We already have this sig, remove it from the
189                          * difference list and free the memory allocated for
190                          * it.
191                          */
192                         if (lastpacket != NULL) {
193                                 lastpacket->next = curpacket->next;
194                         } else {
195                                 assert(curpacket == new->sigs);
196                                 new->sigs = curpacket->next;
197                         }
198                         curpacket->next = NULL;
199                         free_packet_list(curpacket);
200                 } else {
201                         lastpacket = curpacket;
202                 }
203                 curpacket = nextpacket;
204         }
205         new->last_sig = lastpacket;
206
207         /*
208          * What's left on new->sigs now are the new signatures, so add them to
209          * old->sigs.
210          */
211         packet_list_add(&old->sigs, &old->last_sig, new->sigs);
212
213         return 0;
214 }
215
216 /**
217  *      merge_signed_packets - Takes 2 lists of signed packets and merges them.
218  *      @old: The old signed packet list.
219  *      @new: The new signed packet list.
220  *
221  *      Takes 2 lists of signed packets and merges them. The complete list of
222  *      signed packets & sigs is returned in old and the minimal set of
223  *      differences required to get from old to new in new.
224  */
225 int merge_signed_packets(struct openpgp_signedpacket_list **old,
226                         struct openpgp_signedpacket_list **old_end,
227                         struct openpgp_signedpacket_list **new,
228                         struct openpgp_signedpacket_list **new_end)
229 {
230         struct openpgp_signedpacket_list *curelem = NULL;
231         struct openpgp_signedpacket_list *newelem = NULL;
232
233         for (curelem = *old; curelem != NULL; curelem = curelem->next) {
234                 newelem = find_signed_packet(*new, curelem->packet);
235                 if (newelem != NULL) {
236                         merge_packet_sigs(curelem, newelem);
237                         
238                         /*
239                          * If there are no sigs left on the new signed packet
240                          * then remove it from the list.
241                          */
242                         if (newelem->sigs == NULL) {
243                                 remove_signed_packet(new,
244                                                 new_end,
245                                                 newelem->packet);
246                         }
247                 }
248         }
249
250         /*
251          * If *new != NULL now then there might be UIDs on the new key that
252          * weren't on the old key. Walk through them, checking if the UID is
253          * on the old key and if not adding them to it.
254          */
255         for (curelem = *new; curelem != NULL;
256                         curelem = curelem->next) {
257
258                 if (find_signed_packet(*old, curelem->packet) == NULL) {
259                         ADD_PACKET_TO_LIST((*old_end),
260                                 packet_dup(curelem->packet));
261                         if (*old == NULL) {
262                                 *old = *old_end;
263                         }
264                         packet_list_add(&(*old_end)->sigs,
265                                 &(*old_end)->last_sig,
266                                 curelem->sigs);
267                 }
268         }
269
270         return 0;
271 }
272
273 /**
274  *      merge_keys - Takes 2 public keys and merges them.
275  *      @a: The old key. The merged key is returned in this structure.
276  *      @b: The new key. The changed from old to new keys are returned in this
277  *              structure.
278  *
279  *      This function takes 2 keys and merges them. It then returns the merged
280  *      key in a and the difference between this new key and the original a
281  *      in b (ie newb contains the minimum amount of detail necessary to
282  *      convert olda to newa). The intention is that olda is provided from
283  *      internal storage and oldb from the remote user. newa is then stored in
284  *      internal storage and newb is sent to all our keysync peers.
285  */
286 int merge_keys(struct openpgp_publickey *a, struct openpgp_publickey *b)
287 {
288         int rc = 0; /* Return code */
289         struct openpgp_packet_list      *curpacket = NULL; 
290         struct openpgp_packet_list      *lastpacket = NULL;
291         struct openpgp_packet_list      *nextpacket = NULL;
292
293         if (a == NULL || b == NULL) {
294                 /*
295                  * Do nothing.
296                  */
297                 rc = 1;
298         } else if (get_keyid(a) != get_keyid(b)) {
299                 /*
300                  * Key IDs are different.
301                  */
302                 rc = -1;
303         } else {
304                 /*
305                  * Key IDs are the same, so I guess we have to merge them.
306                  */
307                 curpacket = b->revocations;
308                 while (curpacket != NULL) {
309                         nextpacket = curpacket->next;
310                         if (find_packet(a->revocations, curpacket->packet)) {
311                                 /*
312                                  * We already have this revocation, remove it
313                                  * from the difference list and free the memory
314                                  * allocated for it.
315                                  */
316
317                                 if (lastpacket != NULL) {
318                                         lastpacket->next = curpacket->next;
319                                 } else {
320                                         assert(curpacket == b->revocations);
321                                         b->revocations = curpacket->next;
322                                 }
323                                 curpacket->next = NULL;
324                                 free_packet_list(curpacket);
325
326                         } else {
327                                 lastpacket = curpacket;
328                         }
329                         curpacket = nextpacket;
330                 }
331                 b->last_revocation = lastpacket;
332
333                 /*
334                  * Anything left on b->revocations doesn't exist on
335                  * a->revocations, so add them to the list.
336                  */
337                 packet_list_add(&a->revocations,
338                                 &a->last_revocation,
339                                 b->revocations);
340
341                 /*
342                  * Merge uids (signed list).
343                  * Merge subkeys (signed list).
344                  */
345                 merge_signed_packets(&a->uids, &a->last_uid, 
346                                 &b->uids, &b->last_uid);
347                 merge_signed_packets(&a->subkeys, &a->last_subkey,
348                                 &b->subkeys, &b->last_subkey);
349
350         }
351
352         return rc;
353 }
354
355 /**
356  *      update_keys - Takes a list of public keys and updates them in the DB.
357  *      @keys: The keys to update in the DB.
358  *
359  *      Takes a list of keys and adds them to the database, merging them with
360  *      the key in the database if it's already present there. The key list is
361  *      update to contain the minimum set of updates required to get from what
362  *      we had before to what we have now (ie the set of data that was added to
363  *      the DB). Returns the number of entirely new keys added.
364  */
365 int update_keys(struct openpgp_publickey **keys)
366 {
367         struct openpgp_publickey *curkey = NULL;
368         struct openpgp_publickey *oldkey = NULL;
369         struct openpgp_publickey *prev = NULL;
370         int newkeys = 0;
371         bool intrans;
372
373         for (curkey = *keys; curkey != NULL; curkey = curkey->next) {
374                 intrans = starttrans();
375                 logthing(LOGTHING_INFO,
376                         "Fetching key 0x%llX, result: %d",
377                         get_keyid(curkey),
378                         fetch_key(get_keyid(curkey), &oldkey, intrans));
379
380                 /*
381                  * If we already have the key stored in the DB then merge it
382                  * with the new one that's been supplied. Otherwise the key
383                  * we've just got is the one that goes in the DB and also the
384                  * one that we send out.
385                  */
386                 if (oldkey != NULL) {
387                         merge_keys(oldkey, curkey);
388                         if (curkey->revocations == NULL &&
389                                         curkey->uids == NULL &&
390                                         curkey->subkeys == NULL) {
391                                 if (prev == NULL) {
392                                         *keys = curkey->next;
393                                 } else {
394                                         prev->next = curkey->next;
395                                         curkey->next = NULL;
396                                         free_publickey(curkey);
397                                         curkey = prev;
398                                 }
399                         } else {
400                                 prev = curkey;
401                                 logthing(LOGTHING_INFO,
402                                         "Merged key; storing updated key.");
403                                 store_key(oldkey, intrans, true);
404                         }
405                         free_publickey(oldkey);
406                         oldkey = NULL;
407                 } else {
408                         logthing(LOGTHING_INFO,
409                                 "Storing completely new key.");
410                         store_key(curkey, intrans, false);
411                         newkeys++;
412                 }
413                 endtrans();
414                 intrans = false;
415         }
416
417         return newkeys;
418 }