opencdk: small fixed to reduce warnings
[gnutls:gnutls.git] / lib / opencdk / kbnode.c
1 /* kbnode.c -  keyblock node utility functions
2  * Copyright (C) 1998-2012 Free Software Foundation, Inc.
3  *
4  * Author: Timo Schulz
5  *
6  * This file is part of OpenCDK.
7  *
8  * The OpenCDK library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "opencdk.h"
31 #include "main.h"
32 #include "packet.h"
33
34
35 /**
36  * cdk_kbnode_new:
37  * @pkt: the packet to add
38  *
39  * Allocates a new key node and adds a packet.
40  **/
41 cdk_kbnode_t cdk_kbnode_new(cdk_packet_t pkt)
42 {
43         cdk_kbnode_t n;
44
45         n = cdk_calloc(1, sizeof *n);
46         if (!n)
47                 return NULL;
48         n->pkt = pkt;
49         return n;
50 }
51
52
53 void _cdk_kbnode_clone(cdk_kbnode_t node)
54 {
55         /* Mark the node as clone which means that the packet
56            will not be freed, just the node itself. */
57         if (node)
58                 node->is_cloned = 1;
59 }
60
61
62 /**
63  * cdk_kbnode_release:
64  * @n: the key node
65  *
66  * Releases the memory of the node.
67  **/
68 void cdk_kbnode_release(cdk_kbnode_t node)
69 {
70         cdk_kbnode_t n2;
71
72         while (node) {
73                 n2 = node->next;
74                 if (!node->is_cloned)
75                         cdk_pkt_release(node->pkt);
76                 cdk_free(node);
77                 node = n2;
78         }
79 }
80
81
82 /**
83  * cdk_kbnode_delete:
84  * @node: the key node
85  *
86  * Marks the given node as deleted.
87  **/
88 void cdk_kbnode_delete(cdk_kbnode_t node)
89 {
90         if (node)
91                 node->is_deleted = 1;
92 }
93
94
95 /* Append NODE to ROOT.  ROOT must exist! */
96 void _cdk_kbnode_add(cdk_kbnode_t root, cdk_kbnode_t node)
97 {
98         cdk_kbnode_t n1;
99
100         for (n1 = root; n1->next; n1 = n1->next);
101         n1->next = node;
102 }
103
104
105 /**
106  * cdk_kbnode_insert:
107  * @root: the root key node
108  * @node: the node to add
109  * @pkttype: packet type
110  *
111  * Inserts @node into the list after @root but before a packet which is not of
112  * type @pkttype (only if @pkttype != 0).
113  **/
114 void
115 cdk_kbnode_insert(cdk_kbnode_t root, cdk_kbnode_t node,
116                   cdk_packet_type_t pkttype)
117 {
118         if (!pkttype) {
119                 node->next = root->next;
120                 root->next = node;
121         } else {
122                 cdk_kbnode_t n1;
123
124                 for (n1 = root; n1->next; n1 = n1->next)
125                         if (pkttype != n1->next->pkt->pkttype) {
126                                 node->next = n1->next;
127                                 n1->next = node;
128                                 return;
129                         }
130                 /* No such packet, append */
131                 node->next = NULL;
132                 n1->next = node;
133         }
134 }
135
136
137 /**
138  * cdk_kbnode_find_prev:
139  * @root: the root key node
140  * @node: the key node
141  * @pkttype: packet type
142  *
143  * Finds the previous node (if @pkttype = 0) or the previous node
144  * with pkttype @pkttype in the list starting with @root of @node.
145  **/
146 cdk_kbnode_t
147 cdk_kbnode_find_prev(cdk_kbnode_t root, cdk_kbnode_t node,
148                      cdk_packet_type_t pkttype)
149 {
150         cdk_kbnode_t n1;
151
152         for (n1 = NULL; root && root != node; root = root->next) {
153                 if (!pkttype || root->pkt->pkttype == pkttype)
154                         n1 = root;
155         }
156         return n1;
157 }
158
159
160 /**
161  * cdk_kbnode_find_next:
162  * @node: the key node
163  * @pkttype: packet type
164  *
165  * Ditto, but find the next packet.  The behaviour is trivial if
166  * @pkttype is 0 but if it is specified, the next node with a packet
167  * of this type is returned.  The function has some knowledge about
168  * the valid ordering of packets: e.g. if the next signature packet
169  * is requested, the function will not return one if it encounters
170  * a user-id.
171  **/
172 cdk_kbnode_t
173 cdk_kbnode_find_next(cdk_kbnode_t node, cdk_packet_type_t pkttype)
174 {
175         for (node = node->next; node; node = node->next) {
176                 if (!pkttype)
177                         return node;
178                 else if (pkttype == CDK_PKT_USER_ID &&
179                          (node->pkt->pkttype == CDK_PKT_PUBLIC_KEY ||
180                           node->pkt->pkttype == CDK_PKT_SECRET_KEY))
181                         return NULL;
182                 else if (pkttype == CDK_PKT_SIGNATURE &&
183                          (node->pkt->pkttype == CDK_PKT_USER_ID ||
184                           node->pkt->pkttype == CDK_PKT_PUBLIC_KEY ||
185                           node->pkt->pkttype == CDK_PKT_SECRET_KEY))
186                         return NULL;
187                 else if (node->pkt->pkttype == pkttype)
188                         return node;
189         }
190         return NULL;
191 }
192
193
194 /**
195  * cdk_kbnode_find:
196  * @node: the key node
197  * @pkttype: packet type
198  *
199  * Tries to find the next node with the packettype @pkttype.
200  **/
201 cdk_kbnode_t cdk_kbnode_find(cdk_kbnode_t node, cdk_packet_type_t pkttype)
202 {
203         for (; node; node = node->next) {
204                 if (node->pkt->pkttype == pkttype)
205                         return node;
206         }
207         return NULL;
208 }
209
210
211 /**
212  * cdk_kbnode_find_packet:
213  * @node: the key node
214  * @pkttype: packet type
215  *
216  * Same as cdk_kbnode_find but it returns the packet instead of the node.
217  **/
218 cdk_packet_t
219 cdk_kbnode_find_packet(cdk_kbnode_t node, cdk_packet_type_t pkttype)
220 {
221         cdk_kbnode_t res;
222
223         res = cdk_kbnode_find(node, pkttype);
224         return res ? res->pkt : NULL;
225 }
226
227
228 /**
229  * cdk_kbnode_walk:
230  * 
231  * Walks through a list of kbnodes. This function returns
232  * the next kbnode for each call; before using the function the first
233  * time, the caller must set CONTEXT to NULL (This has simply the effect
234  * to start with ROOT).
235  */
236 cdk_kbnode_t
237 cdk_kbnode_walk(cdk_kbnode_t root, cdk_kbnode_t * ctx, int all)
238 {
239         cdk_kbnode_t n;
240
241         do {
242                 if (!*ctx) {
243                         *ctx = root;
244                         n = root;
245                 } else {
246                         n = (*ctx)->next;
247                         *ctx = n;
248                 }
249         }
250         while (!all && n && n->is_deleted);
251         return n;
252 }
253
254
255 /**
256  * cdk_kbnode_commit:
257  * @root: the nodes
258  * 
259  * Commits changes made to the kblist at ROOT. Note that ROOT my change,
260  * and it is therefore passed by reference.
261  * The function has the effect of removing all nodes marked as deleted.
262  *
263  * Returns: true if any node has been changed
264  */
265 int cdk_kbnode_commit(cdk_kbnode_t * root)
266 {
267         cdk_kbnode_t n, nl;
268         int changed = 0;
269
270         for (n = *root, nl = NULL; n; n = nl->next) {
271                 if (n->is_deleted) {
272                         if (n == *root)
273                                 *root = nl = n->next;
274                         else
275                                 nl->next = n->next;
276                         if (!n->is_cloned)
277                                 cdk_pkt_release(n->pkt);
278                         cdk_free(n);
279                         changed = 1;
280                 } else
281                         nl = n;
282         }
283         return changed;
284 }
285
286
287 /**
288  * cdk_kbnode_remove:
289  * @root: the root node
290  * @node: the node to delete
291  * 
292  * Removes a node from the root node.
293  */
294 void cdk_kbnode_remove(cdk_kbnode_t * root, cdk_kbnode_t node)
295 {
296         cdk_kbnode_t n, nl;
297
298         for (n = *root, nl = NULL; n; n = nl->next) {
299                 if (n == node) {
300                         if (n == *root)
301                                 *root = nl = n->next;
302                         else
303                                 nl->next = n->next;
304                         if (!n->is_cloned)
305                                 cdk_pkt_release(n->pkt);
306                         cdk_free(n);
307                 } else
308                         nl = n;
309         }
310 }
311
312
313
314 /**
315  * cdk_cdknode_move:
316  * @root: root node
317  * @node: the node to move
318  * @where: destination place where to move the node.
319  * 
320  * Moves NODE behind right after WHERE or to the beginning if WHERE is NULL.
321  */
322 void
323 cdk_kbnode_move(cdk_kbnode_t * root, cdk_kbnode_t node, cdk_kbnode_t where)
324 {
325         cdk_kbnode_t tmp, prev;
326
327         if (!root || !*root || !node)
328                 return;
329         for (prev = *root; prev && prev->next != node; prev = prev->next);
330         if (!prev)
331                 return;         /* Node is not in the list */
332
333         if (!where) {           /* Move node before root */
334                 if (node == *root)
335                         return;
336                 prev->next = node->next;
337                 node->next = *root;
338                 *root = node;
339                 return;
340         }
341         if (node == where)      /* Move it after where. */
342                 return;
343         tmp = node->next;
344         node->next = where->next;
345         where->next = node;
346         prev->next = tmp;
347 }
348
349
350 /**
351  * cdk_kbnode_get_packet:
352  * @node: the key node
353  *
354  * Get packet in node.
355  *
356  * Returns: the packet which is stored inside the node in @node.
357  **/
358 cdk_packet_t cdk_kbnode_get_packet(cdk_kbnode_t node)
359 {
360         if (node)
361                 return node->pkt;
362         return NULL;
363 }
364
365
366 /**
367  * cdk_kbnode_read_from_mem:
368  * @ret_node: the new key node
369  * @armor: whether base64 or not
370  * @buf: the buffer which stores the key sequence
371  * @buflen: the length of the buffer
372  *
373  * Tries to read a key node from the memory buffer @buf.
374  **/
375 cdk_error_t
376 cdk_kbnode_read_from_mem(cdk_kbnode_t * ret_node,
377                          int armor, const byte * buf, size_t buflen)
378 {
379         cdk_stream_t inp;
380         cdk_error_t rc;
381
382         if (!ret_node || !buf)
383                 return CDK_Inv_Value;
384
385         *ret_node = NULL;
386         if (!buflen)
387                 return gnutls_assert_val(CDK_Too_Short);
388
389         rc = cdk_stream_tmp_from_mem(buf, buflen, &inp);
390         if (rc)
391                 return gnutls_assert_val(rc);
392
393         if (armor)
394                 cdk_stream_set_armor_flag(inp, 0);
395
396         rc = cdk_keydb_get_keyblock(inp, ret_node);
397         if (rc)
398                 gnutls_assert();
399         cdk_stream_close(inp);
400         return rc;
401 }
402
403
404 /**
405  * cdk_kbnode_write_to_mem_alloc:
406  * @node: the key node
407  * @r_buf: buffer to hold the raw data
408  * @r_buflen: buffer length of the allocated raw data.
409  * 
410  * The function acts similar to cdk_kbnode_write_to_mem but
411  * it allocates the buffer to avoid the lengthy second run.
412  */
413 cdk_error_t
414 cdk_kbnode_write_to_mem_alloc(cdk_kbnode_t node,
415                               byte ** r_buf, size_t * r_buflen)
416 {
417         cdk_kbnode_t n;
418         cdk_stream_t s;
419         cdk_error_t rc;
420         ssize_t len;
421
422         if (!node || !r_buf || !r_buflen) {
423                 gnutls_assert();
424                 return CDK_Inv_Value;
425         }
426
427         *r_buf = NULL;
428         *r_buflen = 0;
429
430         rc = cdk_stream_tmp_new(&s);
431         if (rc) {
432                 gnutls_assert();
433                 return rc;
434         }
435
436         for (n = node; n; n = n->next) {
437                 /* Skip all packets which cannot occur in a key composition. */
438                 if (n->pkt->pkttype != CDK_PKT_PUBLIC_KEY &&
439                     n->pkt->pkttype != CDK_PKT_PUBLIC_SUBKEY &&
440                     n->pkt->pkttype != CDK_PKT_SECRET_KEY &&
441                     n->pkt->pkttype != CDK_PKT_SECRET_SUBKEY &&
442                     n->pkt->pkttype != CDK_PKT_SIGNATURE &&
443                     n->pkt->pkttype != CDK_PKT_USER_ID &&
444                     n->pkt->pkttype != CDK_PKT_ATTRIBUTE)
445                         continue;
446                 rc = cdk_pkt_write(s, n->pkt);
447                 if (rc) {
448                         cdk_stream_close(s);
449                         gnutls_assert();
450                         return rc;
451                 }
452         }
453
454         cdk_stream_seek(s, 0);
455         len = cdk_stream_get_length(s);
456         if (len == 0)
457                 return gnutls_assert_val(CDK_General_Error);
458
459         *r_buf = cdk_calloc(1, len);
460         *r_buflen = cdk_stream_read(s, *r_buf, len);
461         cdk_stream_close(s);
462         return 0;
463 }
464
465
466 /**
467  * cdk_kbnode_write_to_mem:
468  * @node: the key node
469  * @buf: the buffer to store the node data
470  * @r_nbytes: the new length of the buffer.
471  *
472  * Tries to write the contents of the key node to the buffer @buf and
473  * return the length of it in @r_nbytes. If buf is (0), only the
474  * length of the node is calculated and returned in @r_nbytes.
475  * Whenever it is possible, the cdk_kbnode_write_to_mem_alloc should be used.
476  **/
477 cdk_error_t
478 cdk_kbnode_write_to_mem(cdk_kbnode_t node, byte * buf, size_t * r_nbytes)
479 {
480         cdk_kbnode_t n;
481         cdk_stream_t s;
482         cdk_error_t rc;
483         ssize_t len;
484
485         if (!node || !r_nbytes) {
486                 gnutls_assert();
487                 return CDK_Inv_Value;
488         }
489
490         rc = cdk_stream_tmp_new(&s);
491         if (rc) {
492                 gnutls_assert();
493                 return rc;
494         }
495
496         for (n = node; n; n = n->next) {
497                 /* Skip all packets which cannot occur in a key composition. */
498                 if (n->pkt->pkttype != CDK_PKT_PUBLIC_KEY &&
499                     n->pkt->pkttype != CDK_PKT_PUBLIC_SUBKEY &&
500                     n->pkt->pkttype != CDK_PKT_SECRET_KEY &&
501                     n->pkt->pkttype != CDK_PKT_SECRET_SUBKEY &&
502                     n->pkt->pkttype != CDK_PKT_SIGNATURE &&
503                     n->pkt->pkttype != CDK_PKT_USER_ID &&
504                     n->pkt->pkttype != CDK_PKT_ATTRIBUTE)
505                         continue;
506                 rc = cdk_pkt_write(s, n->pkt);
507                 if (rc) {
508                         cdk_stream_close(s);
509                         gnutls_assert();
510                         return rc;
511                 }
512         }
513
514         cdk_stream_seek(s, 0);
515
516         len = cdk_stream_get_length(s);
517         if (len == 0)
518                 return gnutls_assert_val(CDK_General_Error);
519
520         if (!buf) {
521                 *r_nbytes = len;        /* Only return the length of the buffer */
522                 cdk_stream_close(s);
523                 return 0;
524         }
525         if (*r_nbytes < (size_t)len) {
526                 *r_nbytes = len;
527                 rc = CDK_Too_Short;
528         }
529         if (!rc)
530                 *r_nbytes = cdk_stream_read(s, buf, len);
531         else
532                 gnutls_assert();
533         cdk_stream_close(s);
534         return rc;
535 }
536
537
538 /**
539  * cdk_kbnode_hash:
540  * @node: the key node
541  * @hashctx: uint8_t pointer to the hash context
542  * @is_v4: OpenPGP signature (yes=1, no=0)
543  * @pkttype: packet type to hash (if (0) use the packet type from the node)
544  * @flags: flags which depend on the operation
545  *
546  * Hashes the key node contents. Two modes are supported. If the packet
547  * type is used (!= 0) then the function searches the first node with
548  * this type. Otherwise the node is seen as a single node and the type
549  * is extracted from it.
550  **/
551 cdk_error_t
552 cdk_kbnode_hash(cdk_kbnode_t node, digest_hd_st * md, int is_v4,
553                 cdk_packet_type_t pkttype, int flags)
554 {
555         cdk_packet_t pkt;
556
557         if (!node || !md) {
558                 gnutls_assert();
559                 return CDK_Inv_Value;
560         }
561         if (!pkttype) {
562                 pkt = cdk_kbnode_get_packet(node);
563                 pkttype = pkt->pkttype;
564         } else {
565                 pkt = cdk_kbnode_find_packet(node, pkttype);
566                 if (!pkt) {
567                         gnutls_assert();
568                         return CDK_Inv_Packet;
569                 }
570         }
571
572         switch (pkttype) {
573         case CDK_PKT_PUBLIC_KEY:
574         case CDK_PKT_PUBLIC_SUBKEY:
575                 _cdk_hash_pubkey(pkt->pkt.public_key, md, flags & 1);
576                 break;
577
578         case CDK_PKT_USER_ID:
579                 _cdk_hash_userid(pkt->pkt.user_id, is_v4, md);
580                 break;
581
582         case CDK_PKT_SIGNATURE:
583                 _cdk_hash_sig_data(pkt->pkt.signature, md);
584                 break;
585
586         default:
587                 gnutls_assert();
588                 return CDK_Inv_Mode;
589         }
590         return 0;
591 }