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