/src/gnupg/common/name-value.c
Line | Count | Source |
1 | | /* name-value.c - Parser and writer for a name-value format. |
2 | | * Copyright (C) 2016 g10 Code GmbH |
3 | | * |
4 | | * This file is part of GnuPG. |
5 | | * |
6 | | * This file is free software; you can redistribute it and/or modify |
7 | | * it under the terms of either |
8 | | * |
9 | | * - the GNU Lesser General Public License as published by the Free |
10 | | * Software Foundation; either version 3 of the License, or (at |
11 | | * your option) any later version. |
12 | | * |
13 | | * or |
14 | | * |
15 | | * - the GNU General Public License as published by the Free |
16 | | * Software Foundation; either version 2 of the License, or (at |
17 | | * your option) any later version. |
18 | | * |
19 | | * or both in parallel, as here. |
20 | | * |
21 | | * GnuPG is distributed in the hope that it will be useful, |
22 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
23 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
24 | | * GNU General Public License for more details. |
25 | | * |
26 | | * You should have received a copy of the GNU General Public License |
27 | | * along with this program; if not, see <https://www.gnu.org/licenses/>. |
28 | | */ |
29 | | |
30 | | /* |
31 | | * This module aso provides features for the extended private key |
32 | | * format of gpg-agent. |
33 | | */ |
34 | | |
35 | | #include <config.h> |
36 | | #include <assert.h> |
37 | | #include <gcrypt.h> |
38 | | #include <gpg-error.h> |
39 | | #include <string.h> |
40 | | |
41 | | #include "mischelp.h" |
42 | | #include "strlist.h" |
43 | | #include "util.h" |
44 | | #include "name-value.h" |
45 | | |
46 | | struct name_value_container |
47 | | { |
48 | | struct name_value_entry *first; |
49 | | struct name_value_entry *last; |
50 | | unsigned int private_key_mode:1; |
51 | | unsigned int modified:1; |
52 | | }; |
53 | | |
54 | | |
55 | | struct name_value_entry |
56 | | { |
57 | | struct name_value_entry *prev; |
58 | | struct name_value_entry *next; |
59 | | |
60 | | /* The name. Comments and blank lines have NAME set to NULL. */ |
61 | | char *name; |
62 | | |
63 | | /* The value as stored in the file. We store it when we parse |
64 | | a file so that we can reproduce it. */ |
65 | | strlist_t raw_value; |
66 | | |
67 | | /* The decoded value. */ |
68 | | char *value; |
69 | | }; |
70 | | |
71 | | |
72 | | /* Helper */ |
73 | | static inline gpg_error_t |
74 | | my_error_from_syserror (void) |
75 | 0 | { |
76 | 0 | return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); |
77 | 0 | } |
78 | | |
79 | | |
80 | | static inline gpg_error_t |
81 | | my_error (gpg_err_code_t ec) |
82 | 0 | { |
83 | 0 | return gpg_err_make (default_errsource, ec); |
84 | 0 | } |
85 | | |
86 | | |
87 | | |
88 | | |
89 | | /* Allocation and deallocation. */ |
90 | | |
91 | | /* Allocate a name value container structure. */ |
92 | | nvc_t |
93 | | nvc_new (void) |
94 | 0 | { |
95 | 0 | nvc_t nvc; |
96 | 0 | nvc = xtrycalloc (1, sizeof (struct name_value_container)); |
97 | 0 | if (nvc) |
98 | 0 | nvc->modified = 1; |
99 | 0 | return nvc; |
100 | 0 | } |
101 | | |
102 | | |
103 | | /* Allocate a private key container structure for use with private keys. */ |
104 | | nvc_t |
105 | | nvc_new_private_key (void) |
106 | 0 | { |
107 | 0 | nvc_t nvc = nvc_new (); |
108 | 0 | if (nvc) |
109 | 0 | nvc->private_key_mode = 1; |
110 | 0 | return nvc; |
111 | 0 | } |
112 | | |
113 | | |
114 | | static void |
115 | | nve_release (nve_t entry, int private_key_mode) |
116 | 0 | { |
117 | 0 | if (entry == NULL) |
118 | 0 | return; |
119 | | |
120 | 0 | xfree (entry->name); |
121 | 0 | if (entry->value && private_key_mode) |
122 | 0 | wipememory (entry->value, strlen (entry->value)); |
123 | 0 | xfree (entry->value); |
124 | 0 | if (private_key_mode) |
125 | 0 | free_strlist_wipe (entry->raw_value); |
126 | 0 | else |
127 | 0 | free_strlist (entry->raw_value); |
128 | 0 | xfree (entry); |
129 | 0 | } |
130 | | |
131 | | |
132 | | /* Release a private key container structure. */ |
133 | | void |
134 | | nvc_release (nvc_t pk) |
135 | 0 | { |
136 | 0 | nve_t e, next; |
137 | |
|
138 | 0 | if (pk == NULL) |
139 | 0 | return; |
140 | | |
141 | 0 | for (e = pk->first; e; e = next) |
142 | 0 | { |
143 | 0 | next = e->next; |
144 | 0 | nve_release (e, pk->private_key_mode); |
145 | 0 | } |
146 | |
|
147 | 0 | xfree (pk); |
148 | 0 | } |
149 | | |
150 | | |
151 | | /* Return the modified-flag of the container and clear it if CLEAR is |
152 | | * set. That flag is set for a new container and set with each |
153 | | * update. */ |
154 | | int |
155 | | nvc_modified (nvc_t pk, int clear) |
156 | 0 | { |
157 | 0 | int modified; |
158 | |
|
159 | 0 | if (!pk) |
160 | 0 | return 0; |
161 | 0 | modified = pk->modified; |
162 | 0 | if (clear) |
163 | 0 | pk->modified = 0; |
164 | 0 | return modified; |
165 | 0 | } |
166 | | |
167 | | |
168 | | |
169 | | |
170 | | /* Dealing with names and values. */ |
171 | | |
172 | | /* Check whether the given name is valid. Valid names start with a |
173 | | letter, end with a colon, and contain only alphanumeric characters |
174 | | and the hyphen. */ |
175 | | static int |
176 | | valid_name (const char *name) |
177 | 0 | { |
178 | 0 | size_t i, len = strlen (name); |
179 | |
|
180 | 0 | if (! alphap (name) || len == 0 || name[len - 1] != ':') |
181 | 0 | return 0; |
182 | | |
183 | 0 | for (i = 1; i < len - 1; i++) |
184 | 0 | if (! alnump (&name[i]) && name[i] != '-') |
185 | 0 | return 0; |
186 | | |
187 | 0 | return 1; |
188 | 0 | } |
189 | | |
190 | | |
191 | | /* Makes sure that ENTRY has a RAW_VALUE. */ |
192 | | static gpg_error_t |
193 | | assert_raw_value (nve_t entry) |
194 | 0 | { |
195 | 0 | gpg_error_t err = 0; |
196 | 0 | size_t len, offset; |
197 | 0 | #define LINELEN 70 |
198 | 0 | char buf[LINELEN+3]; |
199 | |
|
200 | 0 | if (entry->raw_value) |
201 | 0 | return 0; |
202 | | |
203 | 0 | len = strlen (entry->value); |
204 | 0 | offset = 0; |
205 | 0 | while (len) |
206 | 0 | { |
207 | 0 | size_t amount, linelen = LINELEN; |
208 | | |
209 | | /* On the first line we need to subtract space for the name. */ |
210 | 0 | if (entry->raw_value == NULL && strlen (entry->name) < linelen) |
211 | 0 | linelen -= strlen (entry->name); |
212 | | |
213 | | /* See if the rest of the value fits in this line. */ |
214 | 0 | if (len <= linelen) |
215 | 0 | amount = len; |
216 | 0 | else |
217 | 0 | { |
218 | 0 | size_t i; |
219 | | |
220 | | /* Find a suitable space to break on. */ |
221 | 0 | for (i = linelen - 1; linelen - i < 30; i--) |
222 | 0 | if (ascii_isspace (entry->value[offset+i])) |
223 | 0 | break; |
224 | |
|
225 | 0 | if (ascii_isspace (entry->value[offset+i])) |
226 | 0 | { |
227 | | /* Found one. */ |
228 | 0 | amount = i; |
229 | 0 | } |
230 | 0 | else |
231 | 0 | { |
232 | | /* Just induce a hard break. */ |
233 | 0 | amount = linelen; |
234 | 0 | } |
235 | 0 | } |
236 | |
|
237 | 0 | snprintf (buf, sizeof buf, " %.*s\n", (int) amount, |
238 | 0 | &entry->value[offset]); |
239 | 0 | if (append_to_strlist_try (&entry->raw_value, buf) == NULL) |
240 | 0 | { |
241 | 0 | err = my_error_from_syserror (); |
242 | 0 | goto leave; |
243 | 0 | } |
244 | | |
245 | 0 | offset += amount; |
246 | 0 | len -= amount; |
247 | 0 | } |
248 | | |
249 | 0 | leave: |
250 | 0 | if (err) |
251 | 0 | { |
252 | 0 | free_strlist_wipe (entry->raw_value); |
253 | 0 | entry->raw_value = NULL; |
254 | 0 | } |
255 | |
|
256 | 0 | return err; |
257 | 0 | #undef LINELEN |
258 | 0 | } |
259 | | |
260 | | |
261 | | /* Computes the length of the value encoded as continuation. If |
262 | | *SWALLOW_WS is set, all whitespace at the beginning of S is |
263 | | swallowed. If START is given, a pointer to the beginning of the |
264 | | value is stored there. */ |
265 | | static size_t |
266 | | continuation_length (const char *s, int *swallow_ws, const char **start) |
267 | 0 | { |
268 | 0 | size_t len; |
269 | |
|
270 | 0 | if (*swallow_ws) |
271 | 0 | { |
272 | | /* The previous line was a blank line and we inserted a newline. |
273 | | Swallow all whitespace at the beginning of this line. */ |
274 | 0 | while (ascii_isspace (*s)) |
275 | 0 | s++; |
276 | 0 | } |
277 | 0 | else |
278 | 0 | { |
279 | | /* Iff a continuation starts with more than one space, it |
280 | | encodes a space. */ |
281 | 0 | if (ascii_isspace (*s)) |
282 | 0 | s++; |
283 | 0 | } |
284 | | |
285 | | /* Strip whitespace at the end. */ |
286 | 0 | len = strlen (s); |
287 | 0 | while (len > 0 && ascii_isspace (s[len-1])) |
288 | 0 | len--; |
289 | |
|
290 | 0 | if (len == 0) |
291 | 0 | { |
292 | | /* Blank lines encode newlines. */ |
293 | 0 | len = 1; |
294 | 0 | s = "\n"; |
295 | 0 | *swallow_ws = 1; |
296 | 0 | } |
297 | 0 | else |
298 | 0 | *swallow_ws = 0; |
299 | |
|
300 | 0 | if (start) |
301 | 0 | *start = s; |
302 | |
|
303 | 0 | return len; |
304 | 0 | } |
305 | | |
306 | | |
307 | | /* Makes sure that ENTRY has a VALUE. */ |
308 | | static gpg_error_t |
309 | | assert_value (nve_t entry) |
310 | 0 | { |
311 | 0 | size_t len; |
312 | 0 | int swallow_ws; |
313 | 0 | strlist_t s; |
314 | 0 | char *p; |
315 | |
|
316 | 0 | if (entry->value) |
317 | 0 | return 0; |
318 | | |
319 | 0 | len = 0; |
320 | 0 | swallow_ws = 0; |
321 | 0 | for (s = entry->raw_value; s; s = s->next) |
322 | 0 | len += continuation_length (s->d, &swallow_ws, NULL); |
323 | | |
324 | | /* Add one for the terminating zero. */ |
325 | 0 | len += 1; |
326 | |
|
327 | 0 | entry->value = p = xtrymalloc (len); |
328 | 0 | if (entry->value == NULL) |
329 | 0 | return my_error_from_syserror (); |
330 | | |
331 | 0 | swallow_ws = 0; |
332 | 0 | for (s = entry->raw_value; s; s = s->next) |
333 | 0 | { |
334 | 0 | const char *start; |
335 | 0 | size_t l = continuation_length (s->d, &swallow_ws, &start); |
336 | |
|
337 | 0 | memcpy (p, start, l); |
338 | 0 | p += l; |
339 | 0 | } |
340 | |
|
341 | 0 | *p++ = 0; |
342 | 0 | assert (p - entry->value == len); |
343 | |
|
344 | 0 | return 0; |
345 | 0 | } |
346 | | |
347 | | |
348 | | /* Get the name. */ |
349 | | char * |
350 | | nve_name (nve_t pke) |
351 | 0 | { |
352 | 0 | return pke->name; |
353 | 0 | } |
354 | | |
355 | | |
356 | | /* Get the value. */ |
357 | | char * |
358 | | nve_value (nve_t pke) |
359 | 0 | { |
360 | 0 | if (assert_value (pke)) |
361 | 0 | return NULL; |
362 | 0 | return pke->value; |
363 | 0 | } |
364 | | |
365 | | |
366 | | |
367 | | /* Adding and modifying values. */ |
368 | | |
369 | | /* Add (NAME, VALUE, RAW_VALUE) to PK. NAME may be NULL for comments |
370 | | and blank lines. At least one of VALUE and RAW_VALUE must be |
371 | | given. If PRESERVE_ORDER is not given, entries with the same name |
372 | | are grouped. NAME, VALUE and RAW_VALUE is consumed. */ |
373 | | static gpg_error_t |
374 | | _nvc_add (nvc_t pk, char *name, char *value, strlist_t raw_value, |
375 | | int preserve_order) |
376 | 0 | { |
377 | 0 | gpg_error_t err = 0; |
378 | 0 | nve_t e; |
379 | |
|
380 | 0 | assert (value || raw_value); |
381 | |
|
382 | 0 | if (name && ! valid_name (name)) |
383 | 0 | { |
384 | 0 | err = my_error (GPG_ERR_INV_NAME); |
385 | 0 | goto leave; |
386 | 0 | } |
387 | | |
388 | 0 | if (name |
389 | 0 | && pk->private_key_mode |
390 | 0 | && !ascii_strcasecmp (name, "Key:") |
391 | 0 | && nvc_lookup (pk, "Key:")) |
392 | 0 | { |
393 | 0 | err = my_error (GPG_ERR_INV_NAME); |
394 | 0 | goto leave; |
395 | 0 | } |
396 | | |
397 | 0 | e = xtrycalloc (1, sizeof *e); |
398 | 0 | if (e == NULL) |
399 | 0 | { |
400 | 0 | err = my_error_from_syserror (); |
401 | 0 | goto leave; |
402 | 0 | } |
403 | | |
404 | 0 | e->name = name; |
405 | 0 | e->value = value; |
406 | 0 | e->raw_value = raw_value; |
407 | |
|
408 | 0 | if (pk->first) |
409 | 0 | { |
410 | 0 | nve_t last; |
411 | |
|
412 | 0 | if (preserve_order || name == NULL) |
413 | 0 | last = pk->last; |
414 | 0 | else |
415 | 0 | { |
416 | | /* See if there is already an entry with NAME. */ |
417 | 0 | last = nvc_lookup (pk, name); |
418 | | |
419 | | /* If so, find the last in that block. */ |
420 | 0 | if (last) |
421 | 0 | { |
422 | 0 | while (last->next) |
423 | 0 | { |
424 | 0 | nve_t next = last->next; |
425 | |
|
426 | 0 | if (next->name && ascii_strcasecmp (next->name, name) == 0) |
427 | 0 | last = next; |
428 | 0 | else |
429 | 0 | break; |
430 | 0 | } |
431 | 0 | } |
432 | 0 | else /* Otherwise, just find the last entry. */ |
433 | 0 | last = pk->last; |
434 | 0 | } |
435 | |
|
436 | 0 | if (last->next) |
437 | 0 | { |
438 | 0 | e->prev = last; |
439 | 0 | e->next = last->next; |
440 | 0 | last->next = e; |
441 | 0 | e->next->prev = e; |
442 | 0 | } |
443 | 0 | else |
444 | 0 | { |
445 | 0 | e->prev = last; |
446 | 0 | last->next = e; |
447 | 0 | pk->last = e; |
448 | 0 | } |
449 | 0 | } |
450 | 0 | else |
451 | 0 | pk->first = pk->last = e; |
452 | |
|
453 | 0 | pk->modified = 1; |
454 | |
|
455 | 0 | leave: |
456 | 0 | if (err) |
457 | 0 | { |
458 | 0 | xfree (name); |
459 | 0 | if (value) |
460 | 0 | wipememory (value, strlen (value)); |
461 | 0 | xfree (value); |
462 | 0 | free_strlist_wipe (raw_value); |
463 | 0 | } |
464 | |
|
465 | 0 | return err; |
466 | 0 | } |
467 | | |
468 | | |
469 | | /* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it |
470 | | is not updated but the new entry is appended. */ |
471 | | gpg_error_t |
472 | | nvc_add (nvc_t pk, const char *name, const char *value) |
473 | 0 | { |
474 | 0 | char *k, *v; |
475 | |
|
476 | 0 | k = xtrystrdup (name); |
477 | 0 | if (k == NULL) |
478 | 0 | return my_error_from_syserror (); |
479 | | |
480 | 0 | v = xtrystrdup (value); |
481 | 0 | if (v == NULL) |
482 | 0 | { |
483 | 0 | xfree (k); |
484 | 0 | return my_error_from_syserror (); |
485 | 0 | } |
486 | | |
487 | 0 | return _nvc_add (pk, k, v, NULL, 0); |
488 | 0 | } |
489 | | |
490 | | |
491 | | /* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it |
492 | | is updated with VALUE. If multiple entries with NAME exist, the |
493 | | first entry is updated. */ |
494 | | gpg_error_t |
495 | | nvc_set (nvc_t pk, const char *name, const char *value) |
496 | 0 | { |
497 | 0 | nve_t e; |
498 | |
|
499 | 0 | if (! valid_name (name)) |
500 | 0 | return GPG_ERR_INV_NAME; |
501 | | |
502 | 0 | e = nvc_lookup (pk, name); |
503 | 0 | if (e) |
504 | 0 | return nve_set (pk, e, value); |
505 | 0 | else |
506 | 0 | return nvc_add (pk, name, value); |
507 | 0 | } |
508 | | |
509 | | |
510 | | /* Update entry E to VALUE. PK is optional; if given its modified |
511 | | * flag will be updated. */ |
512 | | gpg_error_t |
513 | | nve_set (nvc_t pk, nve_t e, const char *value) |
514 | 0 | { |
515 | 0 | char *v; |
516 | |
|
517 | 0 | if (!e) |
518 | 0 | return GPG_ERR_INV_ARG; |
519 | | |
520 | 0 | if (e->value && value && !strcmp (e->value, value)) |
521 | 0 | { |
522 | | /* Setting same value - ignore this call and don't set the |
523 | | * modified flag (if PK is given). */ |
524 | 0 | return 0; |
525 | 0 | } |
526 | | |
527 | 0 | v = xtrystrdup (value? value:""); |
528 | 0 | if (!v) |
529 | 0 | return my_error_from_syserror (); |
530 | | |
531 | 0 | free_strlist_wipe (e->raw_value); |
532 | 0 | e->raw_value = NULL; |
533 | 0 | if (e->value) |
534 | 0 | wipememory (e->value, strlen (e->value)); |
535 | 0 | xfree (e->value); |
536 | 0 | e->value = v; |
537 | 0 | if (pk) |
538 | 0 | pk->modified = 1; |
539 | |
|
540 | 0 | return 0; |
541 | 0 | } |
542 | | |
543 | | |
544 | | /* Delete the given entry from PK. */ |
545 | | void |
546 | | nvc_delete (nvc_t pk, nve_t entry) |
547 | 0 | { |
548 | 0 | if (entry->prev) |
549 | 0 | entry->prev->next = entry->next; |
550 | 0 | else |
551 | 0 | pk->first = entry->next; |
552 | |
|
553 | 0 | if (entry->next) |
554 | 0 | entry->next->prev = entry->prev; |
555 | 0 | else |
556 | 0 | pk->last = entry->prev; |
557 | |
|
558 | 0 | nve_release (entry, pk->private_key_mode); |
559 | 0 | pk->modified = 1; |
560 | 0 | } |
561 | | |
562 | | |
563 | | /* Delete the entries with NAME from PK. */ |
564 | | void |
565 | | nvc_delete_named (nvc_t pk, const char *name) |
566 | 0 | { |
567 | 0 | nve_t e; |
568 | |
|
569 | 0 | if (!valid_name (name)) |
570 | 0 | return; |
571 | | |
572 | 0 | while ((e = nvc_lookup (pk, name))) |
573 | 0 | nvc_delete (pk, e); |
574 | 0 | } |
575 | | |
576 | | |
577 | | |
578 | | |
579 | | /* Lookup and iteration. */ |
580 | | |
581 | | /* Get the first non-comment entry. */ |
582 | | nve_t |
583 | | nvc_first (nvc_t pk) |
584 | 0 | { |
585 | 0 | nve_t entry; |
586 | |
|
587 | 0 | if (!pk) |
588 | 0 | return NULL; |
589 | | |
590 | 0 | for (entry = pk->first; entry; entry = entry->next) |
591 | 0 | if (entry->name) |
592 | 0 | return entry; |
593 | | |
594 | 0 | return NULL; |
595 | 0 | } |
596 | | |
597 | | |
598 | | /* Get the first entry with the given name. Return NULL if it does |
599 | | * not exist. */ |
600 | | nve_t |
601 | | nvc_lookup (nvc_t pk, const char *name) |
602 | 0 | { |
603 | 0 | nve_t entry; |
604 | |
|
605 | 0 | if (!pk) |
606 | 0 | return NULL; |
607 | | |
608 | 0 | for (entry = pk->first; entry; entry = entry->next) |
609 | 0 | if (entry->name && ascii_strcasecmp (entry->name, name) == 0) |
610 | 0 | return entry; |
611 | | |
612 | 0 | return NULL; |
613 | 0 | } |
614 | | |
615 | | |
616 | | /* Get the next non-comment entry. */ |
617 | | nve_t |
618 | | nve_next (nve_t entry) |
619 | 0 | { |
620 | 0 | for (entry = entry->next; entry; entry = entry->next) |
621 | 0 | if (entry->name) |
622 | 0 | return entry; |
623 | 0 | return NULL; |
624 | 0 | } |
625 | | |
626 | | |
627 | | /* Get the next entry with the given name. */ |
628 | | nve_t |
629 | | nve_next_value (nve_t entry, const char *name) |
630 | 0 | { |
631 | 0 | for (entry = entry->next; entry; entry = entry->next) |
632 | 0 | if (entry->name && ascii_strcasecmp (entry->name, name) == 0) |
633 | 0 | return entry; |
634 | 0 | return NULL; |
635 | 0 | } |
636 | | |
637 | | |
638 | | /* Return the string for the first entry in NVC with NAME. If an |
639 | | * entry with NAME is missing in NVC or its value is the empty string |
640 | | * NULL is returned. Note that the the returned string is a pointer |
641 | | * into NVC. */ |
642 | | const char * |
643 | | nvc_get_string (nvc_t nvc, const char *name) |
644 | 0 | { |
645 | 0 | nve_t item; |
646 | |
|
647 | 0 | if (!nvc) |
648 | 0 | return NULL; |
649 | 0 | item = nvc_lookup (nvc, name); |
650 | 0 | if (!item) |
651 | 0 | return NULL; |
652 | 0 | return nve_value (item); |
653 | 0 | } |
654 | | |
655 | | |
656 | | /* Return true (ie. a non-zero value) if NAME exists and its value is |
657 | | * true; that is either "yes", "true", or a decimal value unequal to 0. */ |
658 | | int |
659 | | nvc_get_boolean (nvc_t nvc, const char *name) |
660 | 0 | { |
661 | 0 | nve_t item; |
662 | 0 | const char *s; |
663 | 0 | int n; |
664 | |
|
665 | 0 | if (!nvc) |
666 | 0 | return 0; |
667 | 0 | item = nvc_lookup (nvc, name); |
668 | 0 | if (!item) |
669 | 0 | return 0; |
670 | 0 | s = nve_value (item); |
671 | 0 | if (!s) |
672 | 0 | return 0; |
673 | 0 | n = atoi (s); |
674 | 0 | if (n) |
675 | 0 | return n; |
676 | 0 | if (!ascii_strcasecmp (s, "yes") || !ascii_strcasecmp (s, "true")) |
677 | 0 | return 1; |
678 | 0 | return 0; |
679 | 0 | } |
680 | | |
681 | | |
682 | | |
683 | | |
684 | | /* Private key handling. */ |
685 | | |
686 | | /* Get the private key. */ |
687 | | gpg_error_t |
688 | | nvc_get_private_key (nvc_t pk, gcry_sexp_t *retsexp) |
689 | 0 | { |
690 | 0 | gpg_error_t err; |
691 | 0 | nve_t e; |
692 | |
|
693 | 0 | e = pk->private_key_mode? nvc_lookup (pk, "Key:") : NULL; |
694 | 0 | if (e == NULL) |
695 | 0 | return my_error (GPG_ERR_MISSING_KEY); |
696 | | |
697 | 0 | err = assert_value (e); |
698 | 0 | if (err) |
699 | 0 | return err; |
700 | | |
701 | 0 | return gcry_sexp_sscan (retsexp, NULL, e->value, strlen (e->value)); |
702 | 0 | } |
703 | | |
704 | | |
705 | | /* Set the private key. */ |
706 | | gpg_error_t |
707 | | nvc_set_private_key (nvc_t pk, gcry_sexp_t sexp) |
708 | 0 | { |
709 | 0 | gpg_error_t err; |
710 | 0 | char *raw, *clean, *p; |
711 | 0 | size_t len, i; |
712 | |
|
713 | 0 | if (!pk->private_key_mode) |
714 | 0 | return my_error (GPG_ERR_MISSING_KEY); |
715 | | |
716 | 0 | len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); |
717 | |
|
718 | 0 | raw = xtrymalloc (len); |
719 | 0 | if (raw == NULL) |
720 | 0 | return my_error_from_syserror (); |
721 | | |
722 | 0 | clean = xtrymalloc (len); |
723 | 0 | if (clean == NULL) |
724 | 0 | { |
725 | 0 | xfree (raw); |
726 | 0 | return my_error_from_syserror (); |
727 | 0 | } |
728 | | |
729 | 0 | gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, raw, len); |
730 | | |
731 | | /* Strip any whitespace at the end. */ |
732 | 0 | i = strlen (raw) - 1; |
733 | 0 | while (i && ascii_isspace (raw[i])) |
734 | 0 | { |
735 | 0 | raw[i] = 0; |
736 | 0 | i--; |
737 | 0 | } |
738 | | |
739 | | /* Replace any newlines with spaces, remove superfluous whitespace. */ |
740 | 0 | len = strlen (raw); |
741 | 0 | for (p = clean, i = 0; i < len; i++) |
742 | 0 | { |
743 | 0 | char c = raw[i]; |
744 | | |
745 | | /* Collapse contiguous and superfluous spaces. */ |
746 | 0 | if (ascii_isspace (c) && i > 0 |
747 | 0 | && (ascii_isspace (raw[i-1]) || raw[i-1] == '(' || raw[i-1] == ')')) |
748 | 0 | continue; |
749 | | |
750 | 0 | if (c == '\n') |
751 | 0 | c = ' '; |
752 | |
|
753 | 0 | *p++ = c; |
754 | 0 | } |
755 | 0 | *p = 0; |
756 | |
|
757 | 0 | err = nvc_set (pk, "Key:", clean); |
758 | 0 | xfree (raw); |
759 | 0 | xfree (clean); |
760 | 0 | return err; |
761 | 0 | } |
762 | | |
763 | | |
764 | | |
765 | | /* Parsing and serialization. */ |
766 | | |
767 | | static gpg_error_t |
768 | | do_nvc_parse (nvc_t *result, int *errlinep, estream_t stream, |
769 | | int for_private_key) |
770 | 0 | { |
771 | 0 | gpg_error_t err = 0; |
772 | 0 | gpgrt_ssize_t len; |
773 | 0 | char *buf = NULL; |
774 | 0 | size_t buf_len = 0; |
775 | 0 | char *name = NULL; |
776 | 0 | strlist_t raw_value = NULL; |
777 | |
|
778 | 0 | *result = for_private_key? nvc_new_private_key () : nvc_new (); |
779 | 0 | if (*result == NULL) |
780 | 0 | return my_error_from_syserror (); |
781 | | |
782 | 0 | if (errlinep) |
783 | 0 | *errlinep = 0; |
784 | 0 | while ((len = es_read_line (stream, &buf, &buf_len, NULL)) > 0) |
785 | 0 | { |
786 | 0 | char *p; |
787 | 0 | if (errlinep) |
788 | 0 | *errlinep += 1; |
789 | | |
790 | | /* Skip any whitespace. */ |
791 | 0 | for (p = buf; *p && ascii_isspace (*p); p++) |
792 | 0 | /* Do nothing. */; |
793 | |
|
794 | 0 | if (name && (spacep (buf) || *p == 0)) |
795 | 0 | { |
796 | | /* A continuation. */ |
797 | 0 | if (append_to_strlist_try (&raw_value, buf) == NULL) |
798 | 0 | { |
799 | 0 | err = my_error_from_syserror (); |
800 | 0 | goto leave; |
801 | 0 | } |
802 | 0 | continue; |
803 | 0 | } |
804 | | |
805 | | /* No continuation. Add the current entry if any. */ |
806 | 0 | if (raw_value) |
807 | 0 | { |
808 | 0 | err = _nvc_add (*result, name, NULL, raw_value, 1); |
809 | 0 | name = NULL; |
810 | 0 | if (err) |
811 | 0 | goto leave; |
812 | 0 | } |
813 | | |
814 | | /* And prepare for the next one. */ |
815 | 0 | name = NULL; |
816 | 0 | raw_value = NULL; |
817 | |
|
818 | 0 | if (*p != 0 && *p != '#') |
819 | 0 | { |
820 | 0 | char *colon, *value, tmp; |
821 | |
|
822 | 0 | colon = strchr (buf, ':'); |
823 | 0 | if (colon == NULL) |
824 | 0 | { |
825 | 0 | err = my_error (GPG_ERR_INV_VALUE); |
826 | 0 | goto leave; |
827 | 0 | } |
828 | | |
829 | 0 | value = colon + 1; |
830 | 0 | tmp = *value; |
831 | 0 | *value = 0; |
832 | 0 | name = xtrystrdup (p); |
833 | 0 | *value = tmp; |
834 | |
|
835 | 0 | if (name == NULL) |
836 | 0 | { |
837 | 0 | err = my_error_from_syserror (); |
838 | 0 | goto leave; |
839 | 0 | } |
840 | | |
841 | 0 | if (append_to_strlist_try (&raw_value, value) == NULL) |
842 | 0 | { |
843 | 0 | err = my_error_from_syserror (); |
844 | 0 | goto leave; |
845 | 0 | } |
846 | 0 | continue; |
847 | 0 | } |
848 | | |
849 | 0 | if (append_to_strlist_try (&raw_value, buf) == NULL) |
850 | 0 | { |
851 | 0 | err = my_error_from_syserror (); |
852 | 0 | goto leave; |
853 | 0 | } |
854 | 0 | } |
855 | 0 | if (len < 0) |
856 | 0 | { |
857 | 0 | err = gpg_error_from_syserror (); |
858 | 0 | goto leave; |
859 | 0 | } |
860 | | |
861 | | /* Add the final entry. */ |
862 | 0 | if (raw_value) |
863 | 0 | { |
864 | 0 | err = _nvc_add (*result, name, NULL, raw_value, 1); |
865 | 0 | name = NULL; |
866 | 0 | } |
867 | |
|
868 | 0 | leave: |
869 | 0 | xfree (name); |
870 | 0 | gpgrt_free (buf); |
871 | 0 | if (err) |
872 | 0 | { |
873 | 0 | nvc_release (*result); |
874 | 0 | *result = NULL; |
875 | 0 | } |
876 | |
|
877 | 0 | return err; |
878 | 0 | } |
879 | | |
880 | | |
881 | | /* Parse STREAM and return a newly allocated name value container |
882 | | structure in RESULT. If ERRLINEP is given, the line number the |
883 | | parser was last considering is stored there. */ |
884 | | gpg_error_t |
885 | | nvc_parse (nvc_t *result, int *errlinep, estream_t stream) |
886 | 0 | { |
887 | 0 | return do_nvc_parse (result, errlinep, stream, 0); |
888 | 0 | } |
889 | | |
890 | | |
891 | | /* Parse STREAM and return a newly allocated name value container |
892 | | structure in RESULT - assuming the extended private key format. If |
893 | | ERRLINEP is given, the line number the parser was last considering |
894 | | is stored there. */ |
895 | | gpg_error_t |
896 | | nvc_parse_private_key (nvc_t *result, int *errlinep, estream_t stream) |
897 | 0 | { |
898 | 0 | return do_nvc_parse (result, errlinep, stream, 1); |
899 | 0 | } |
900 | | |
901 | | |
902 | | /* Helper fpr nvc_write. */ |
903 | | static gpg_error_t |
904 | | write_one_entry (nve_t entry, estream_t stream) |
905 | 0 | { |
906 | 0 | gpg_error_t err; |
907 | 0 | strlist_t sl; |
908 | |
|
909 | 0 | if (entry->name) |
910 | 0 | es_fputs (entry->name, stream); |
911 | |
|
912 | 0 | err = assert_raw_value (entry); |
913 | 0 | if (err) |
914 | 0 | return err; |
915 | | |
916 | 0 | for (sl = entry->raw_value; sl; sl = sl->next) |
917 | 0 | es_fputs (sl->d, stream); |
918 | |
|
919 | 0 | if (es_ferror (stream)) |
920 | 0 | return my_error_from_syserror (); |
921 | | |
922 | 0 | return 0; |
923 | 0 | } |
924 | | |
925 | | |
926 | | /* Write a representation of PK to STREAM. */ |
927 | | gpg_error_t |
928 | | nvc_write (nvc_t pk, estream_t stream) |
929 | 0 | { |
930 | 0 | gpg_error_t err = 0; |
931 | 0 | nve_t entry; |
932 | 0 | nve_t keyentry = NULL; |
933 | |
|
934 | 0 | for (entry = pk->first; entry; entry = entry->next) |
935 | 0 | { |
936 | 0 | if (pk->private_key_mode |
937 | 0 | && entry->name && !ascii_strcasecmp (entry->name, "Key:")) |
938 | 0 | { |
939 | 0 | if (!keyentry) |
940 | 0 | keyentry = entry; |
941 | 0 | continue; |
942 | 0 | } |
943 | | |
944 | 0 | err = write_one_entry (entry, stream); |
945 | 0 | if (err) |
946 | 0 | return err; |
947 | 0 | } |
948 | | |
949 | | /* In private key mode we write the Key always last. */ |
950 | 0 | if (keyentry) |
951 | 0 | err = write_one_entry (keyentry, stream); |
952 | |
|
953 | 0 | return err; |
954 | 0 | } |