Line data Source code
1 : /* keylist.c - Listing keys.
2 : Copyright (C) 2000 Werner Koch (dd9jn)
3 : Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007,
4 : 2008, 2009 g10 Code GmbH
5 :
6 : This file is part of GPGME.
7 :
8 : GPGME is free software; you can redistribute it and/or modify it
9 : under the terms of the GNU Lesser General Public License as
10 : published by the Free Software Foundation; either version 2.1 of
11 : the License, or (at your option) any later version.
12 :
13 : GPGME 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
19 : License along with this program; if not, see <https://www.gnu.org/licenses/>.
20 : */
21 :
22 : #if HAVE_CONFIG_H
23 : #include <config.h>
24 : #endif
25 : #include <stdio.h>
26 : #include <stdlib.h>
27 : #include <string.h>
28 : #ifdef HAVE_SYS_TYPES_H
29 : /* Solaris 8 needs sys/types.h before time.h. */
30 : # include <sys/types.h>
31 : #endif
32 : #include <time.h>
33 : #include <assert.h>
34 : #include <ctype.h>
35 : #include <errno.h>
36 : #include <limits.h>
37 :
38 : /* Suppress warning for accessing deprecated member "class". */
39 : #define _GPGME_IN_GPGME
40 : #include "gpgme.h"
41 : #include "util.h"
42 : #include "context.h"
43 : #include "ops.h"
44 : #include "debug.h"
45 :
46 :
47 : struct key_queue_item_s
48 : {
49 : struct key_queue_item_s *next;
50 : gpgme_key_t key;
51 : };
52 :
53 : typedef struct
54 : {
55 : struct _gpgme_op_keylist_result result;
56 :
57 : /* The error code from ERROR keydb_search. */
58 : gpgme_error_t keydb_search_err;
59 :
60 : gpgme_key_t tmp_key;
61 :
62 : /* This points to the last uid in tmp_key. */
63 : gpgme_user_id_t tmp_uid;
64 :
65 : /* This points to the last sig in tmp_uid. */
66 : gpgme_key_sig_t tmp_keysig;
67 :
68 : /* Something new is available. */
69 : int key_cond;
70 : struct key_queue_item_s *key_queue;
71 : } *op_data_t;
72 :
73 :
74 : static void
75 332 : release_op_data (void *hook)
76 : {
77 332 : op_data_t opd = (op_data_t) hook;
78 332 : struct key_queue_item_s *key = opd->key_queue;
79 :
80 332 : if (opd->tmp_key)
81 6 : gpgme_key_unref (opd->tmp_key);
82 :
83 : /* opd->tmp_uid and opd->tmp_keysig are actually part of opd->tmp_key,
84 : so we do not need to release them here. */
85 :
86 672 : while (key)
87 : {
88 8 : struct key_queue_item_s *next = key->next;
89 :
90 8 : gpgme_key_unref (key->key);
91 8 : key = next;
92 : }
93 332 : }
94 :
95 :
96 : gpgme_keylist_result_t
97 22 : gpgme_op_keylist_result (gpgme_ctx_t ctx)
98 : {
99 : void *hook;
100 : op_data_t opd;
101 : gpgme_error_t err;
102 :
103 22 : TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_result", ctx);
104 :
105 22 : err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
106 22 : opd = hook;
107 22 : if (err || !opd)
108 : {
109 0 : TRACE_SUC0 ("result=(null)");
110 0 : return NULL;
111 : }
112 :
113 22 : TRACE_LOG1 ("truncated = %i", opd->result.truncated);
114 :
115 22 : TRACE_SUC1 ("result=%p", &opd->result);
116 22 : return &opd->result;
117 : }
118 :
119 :
120 : static gpgme_error_t
121 980 : keylist_status_handler (void *priv, gpgme_status_code_t code, char *args)
122 : {
123 980 : gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
124 : gpgme_error_t err;
125 : void *hook;
126 : op_data_t opd;
127 :
128 : (void)args;
129 :
130 980 : err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
131 980 : opd = hook;
132 980 : if (err)
133 0 : return err;
134 :
135 980 : switch (code)
136 : {
137 : case GPGME_STATUS_TRUNCATED:
138 0 : opd->result.truncated = 1;
139 0 : break;
140 :
141 : case GPGME_STATUS_ERROR:
142 0 : err = _gpgme_parse_failure (args);
143 0 : if (!opd->keydb_search_err && !strcmp (args, "keydb_search"))
144 0 : opd->keydb_search_err = err;
145 0 : err = 0;
146 0 : break;
147 :
148 : default:
149 980 : break;
150 : }
151 980 : return err;
152 : }
153 :
154 :
155 : static void
156 2131 : set_subkey_trust_info (gpgme_subkey_t subkey, const char *src)
157 : {
158 6392 : while (*src && !isdigit (*src))
159 : {
160 2130 : switch (*src)
161 : {
162 : case 'e':
163 54 : subkey->expired = 1;
164 54 : break;
165 :
166 : case 'r':
167 0 : subkey->revoked = 1;
168 0 : break;
169 :
170 : case 'd':
171 : /* Note that gpg 1.3 won't print that anymore but only uses
172 : the capabilities field. */
173 0 : subkey->disabled = 1;
174 0 : break;
175 :
176 : case 'i':
177 0 : subkey->invalid = 1;
178 0 : break;
179 : }
180 2130 : src++;
181 : }
182 2131 : }
183 :
184 :
185 : static void
186 1042 : set_mainkey_trust_info (gpgme_key_t key, const char *src)
187 : {
188 : /* First set the trust info of the main key (the first subkey). */
189 1042 : set_subkey_trust_info (key->subkeys, src);
190 :
191 : /* Now set the summarized trust info. */
192 3125 : while (*src && !isdigit (*src))
193 : {
194 1041 : switch (*src)
195 : {
196 : case 'e':
197 0 : key->expired = 1;
198 0 : break;
199 :
200 : case 'r':
201 0 : key->revoked = 1;
202 0 : break;
203 :
204 : case 'd':
205 : /* Note that gpg 1.3 won't print that anymore but only uses
206 : the capabilities field. However, it is still used for
207 : external key listings. */
208 0 : key->disabled = 1;
209 0 : break;
210 :
211 : case 'i':
212 0 : key->invalid = 1;
213 0 : break;
214 : }
215 1041 : src++;
216 : }
217 1042 : }
218 :
219 :
220 : static void
221 1460 : set_userid_flags (gpgme_key_t key, const char *src)
222 : {
223 1460 : gpgme_user_id_t uid = key->_last_uid;
224 :
225 1460 : assert (uid);
226 : /* Look at letters and stop at the first digit. */
227 4378 : while (*src && !isdigit (*src))
228 : {
229 1458 : switch (*src)
230 : {
231 : case 'r':
232 21 : uid->revoked = 1;
233 21 : break;
234 :
235 : case 'i':
236 0 : uid->invalid = 1;
237 0 : break;
238 :
239 : case 'n':
240 4 : uid->validity = GPGME_VALIDITY_NEVER;
241 4 : break;
242 :
243 : case 'm':
244 11 : uid->validity = GPGME_VALIDITY_MARGINAL;
245 11 : break;
246 :
247 : case 'f':
248 2 : uid->validity = GPGME_VALIDITY_FULL;
249 2 : break;
250 :
251 : case 'u':
252 255 : uid->validity = GPGME_VALIDITY_ULTIMATE;
253 255 : break;
254 : }
255 1458 : src++;
256 : }
257 1460 : }
258 :
259 :
260 : static void
261 2131 : set_subkey_capability (gpgme_subkey_t subkey, const char *src)
262 : {
263 12239 : while (*src)
264 : {
265 7977 : switch (*src)
266 : {
267 : case 'e':
268 1063 : subkey->can_encrypt = 1;
269 1063 : break;
270 :
271 : case 's':
272 1008 : subkey->can_sign = 1;
273 1008 : break;
274 :
275 : case 'c':
276 1041 : subkey->can_certify = 1;
277 1041 : break;
278 :
279 : case 'a':
280 965 : subkey->can_authenticate = 1;
281 965 : break;
282 :
283 : case 'q':
284 0 : subkey->is_qualified = 1;
285 0 : break;
286 :
287 : case 'd':
288 0 : subkey->disabled = 1;
289 0 : break;
290 : }
291 7977 : src++;
292 : }
293 2131 : }
294 :
295 :
296 : static void
297 1041 : set_mainkey_capability (gpgme_key_t key, const char *src)
298 : {
299 : /* First set the capabilities of the main key (the first subkey). */
300 1041 : set_subkey_capability (key->subkeys, src);
301 :
302 8935 : while (*src)
303 : {
304 6851 : switch (*src)
305 : {
306 : case 'd':
307 : case 'D':
308 : /* Note, that this flag is also set using the key validity
309 : field for backward compatibility with gpg 1.2. We use d
310 : and D, so that a future gpg version will be able to
311 : disable certain subkeys. Currently it is expected that
312 : gpg sets this for the primary key. */
313 0 : key->disabled = 1;
314 0 : break;
315 :
316 : case 'e':
317 : case 'E':
318 1009 : key->can_encrypt = 1;
319 1009 : break;
320 :
321 : case 's':
322 : case 'S':
323 1904 : key->can_sign = 1;
324 1904 : break;
325 :
326 : case 'c':
327 : case 'C':
328 2084 : key->can_certify = 1;
329 2084 : break;
330 :
331 : case 'a':
332 : case 'A':
333 1854 : key->can_authenticate = 1;
334 1854 : break;
335 :
336 : case 'q':
337 : case 'Q':
338 0 : key->is_qualified = 1;
339 0 : break;
340 : }
341 6851 : src++;
342 : }
343 1042 : }
344 :
345 :
346 : static void
347 1041 : set_ownertrust (gpgme_key_t key, const char *src)
348 : {
349 : /* Look at letters and stop at the first digit. */
350 3119 : while (*src && !isdigit (*src))
351 : {
352 1037 : switch (*src)
353 : {
354 : case 'n':
355 0 : key->owner_trust = GPGME_VALIDITY_NEVER;
356 0 : break;
357 :
358 : case 'm':
359 0 : key->owner_trust = GPGME_VALIDITY_MARGINAL;
360 0 : break;
361 :
362 : case 'f':
363 0 : key->owner_trust = GPGME_VALIDITY_FULL;
364 0 : break;
365 :
366 : case 'u':
367 155 : key->owner_trust = GPGME_VALIDITY_ULTIMATE;
368 155 : break;
369 :
370 : default:
371 882 : key->owner_trust = GPGME_VALIDITY_UNKNOWN;
372 882 : break;
373 : }
374 1037 : src++;
375 : }
376 1041 : }
377 :
378 :
379 : static gpgme_keyorg_t
380 2491 : parse_keyorg (const char *string)
381 : {
382 2491 : switch (atoi (string))
383 : {
384 2491 : case 0: return GPGME_KEYORG_UNKNOWN;
385 : case 1:
386 : case 2:
387 0 : return GPGME_KEYORG_KS;
388 0 : case 3: return GPGME_KEYORG_DANE;
389 0 : case 4: return GPGME_KEYORG_WKD;
390 0 : case 5: return GPGME_KEYORG_URL;
391 0 : case 6: return GPGME_KEYORG_FILE;
392 0 : case 7: return GPGME_KEYORG_SELF;
393 0 : default: return GPGME_KEYORG_OTHER;
394 : }
395 : }
396 :
397 :
398 : /* Parse field 15 of a secret key or subkey. This fields holds a
399 : reference to smartcards. FIELD is the content of the field and we
400 : are allowed to modify it. */
401 : static gpg_error_t
402 88 : parse_sec_field15 (gpgme_key_t key, gpgme_subkey_t subkey, char *field)
403 : {
404 88 : if (!*field)
405 : ; /* Empty. */
406 24 : else if (*field == '#')
407 : {
408 : /* This is a stub for an offline key. We reset the SECRET flag
409 : of the subkey here. Note that the secret flag of the entire
410 : key will be true even then. We even explicitly set
411 : key->secret to make it works for GPGME_KEYLIST_MODE_WITH_SECRET. */
412 10 : subkey->secret = 0;
413 10 : key->secret = 1;
414 : }
415 14 : else if (strchr ("01234567890ABCDEFabcdef", *field))
416 : {
417 : /* Fields starts with a hex digit; thus it is a serial number. */
418 0 : key->secret = 1;
419 0 : subkey->is_cardkey = 1;
420 0 : subkey->card_number = strdup (field);
421 0 : if (!subkey->card_number)
422 0 : return gpg_error_from_syserror ();
423 : }
424 14 : else if (*field == '+')
425 : {
426 14 : key->secret = 1;
427 14 : subkey->secret = 1;
428 : }
429 : else
430 : {
431 : /* RFU. */
432 : }
433 :
434 88 : return 0;
435 : }
436 :
437 :
438 : /* Parse a tfs record. */
439 : static gpg_error_t
440 38 : parse_tfs_record (gpgme_user_id_t uid, char **field, int nfield)
441 : {
442 : gpg_error_t err;
443 : gpgme_tofu_info_t ti;
444 : unsigned long uval;
445 :
446 : /* We add only the first TOFU record in case future versions emit
447 : * several. */
448 38 : if (uid->tofu)
449 0 : return 0;
450 :
451 : /* Check that we have enough fields and that the version is supported. */
452 38 : if (nfield < 8 || atoi(field[1]) != 1)
453 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
454 :
455 38 : ti = calloc (1, sizeof *ti);
456 38 : if (!ti)
457 0 : return gpg_error_from_syserror ();
458 :
459 : /* Note that we allow a value of up to 7 which is what we can store
460 : * in the ti->validity. */
461 38 : err = _gpgme_strtoul_field (field[2], &uval);
462 38 : if (err || uval > 7)
463 : goto inv_engine;
464 38 : ti->validity = uval;
465 :
466 : /* Parse the sign-count. */
467 38 : err = _gpgme_strtoul_field (field[3], &uval);
468 38 : if (err)
469 0 : goto inv_engine;
470 38 : if (uval > USHRT_MAX)
471 0 : uval = USHRT_MAX;
472 38 : ti->signcount = uval;
473 :
474 : /* Parse the encr-count. */
475 38 : err = _gpgme_strtoul_field (field[4], &uval);
476 38 : if (err)
477 0 : goto inv_engine;
478 38 : if (uval > USHRT_MAX)
479 0 : uval = USHRT_MAX;
480 38 : ti->encrcount = uval;
481 :
482 : /* Parse the policy. */
483 38 : if (!strcmp (field[5], "none"))
484 0 : ti->policy = GPGME_TOFU_POLICY_NONE;
485 38 : else if (!strcmp (field[5], "auto"))
486 10 : ti->policy = GPGME_TOFU_POLICY_AUTO;
487 28 : else if (!strcmp (field[5], "good"))
488 20 : ti->policy = GPGME_TOFU_POLICY_GOOD;
489 8 : else if (!strcmp (field[5], "bad"))
490 4 : ti->policy = GPGME_TOFU_POLICY_BAD;
491 4 : else if (!strcmp (field[5], "ask"))
492 2 : ti->policy = GPGME_TOFU_POLICY_ASK;
493 : else /* "unknown" and invalid policy strings. */
494 2 : ti->policy = GPGME_TOFU_POLICY_UNKNOWN;
495 :
496 : /* Parse first and last seen timestamps. */
497 38 : err = _gpgme_strtoul_field (field[6], &uval);
498 38 : if (err)
499 0 : goto inv_engine;
500 38 : ti->signfirst = uval;
501 38 : err = _gpgme_strtoul_field (field[7], &uval);
502 38 : if (err)
503 0 : goto inv_engine;
504 38 : ti->signlast = uval;
505 :
506 38 : if (nfield > 9)
507 : {
508 : /* This condition is only to allow for gpg 2.1.15 - can
509 : * eventually be removed. */
510 38 : err = _gpgme_strtoul_field (field[8], &uval);
511 38 : if (err)
512 0 : goto inv_engine;
513 38 : ti->encrfirst = uval;
514 38 : err = _gpgme_strtoul_field (field[9], &uval);
515 38 : if (err)
516 0 : goto inv_engine;
517 38 : ti->encrlast = uval;
518 : }
519 :
520 : /* Ready. */
521 38 : uid->tofu = ti;
522 38 : return 0;
523 :
524 : inv_engine:
525 0 : free (ti);
526 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
527 : }
528 :
529 :
530 : /* We have read an entire key into tmp_key and should now finish it.
531 : It is assumed that this releases tmp_key. */
532 : static void
533 1372 : finish_key (gpgme_ctx_t ctx, op_data_t opd)
534 : {
535 1372 : gpgme_key_t key = opd->tmp_key;
536 :
537 1372 : opd->tmp_key = NULL;
538 1372 : opd->tmp_uid = NULL;
539 1372 : opd->tmp_keysig = NULL;
540 :
541 1372 : if (key)
542 1035 : _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_KEY, key);
543 1370 : }
544 :
545 :
546 : /* Note: We are allowed to modify LINE. */
547 : static gpgme_error_t
548 6561 : keylist_colon_handler (void *priv, char *line)
549 : {
550 6561 : gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
551 : enum
552 : {
553 : RT_NONE, RT_SIG, RT_UID, RT_TFS, RT_SUB, RT_PUB, RT_FPR, RT_GRP,
554 : RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV, RT_SPK
555 : }
556 6561 : rectype = RT_NONE;
557 : #define NR_FIELDS 20
558 : char *field[NR_FIELDS];
559 6561 : int fields = 0;
560 : void *hook;
561 : op_data_t opd;
562 : gpgme_error_t err;
563 : gpgme_key_t key;
564 6561 : gpgme_subkey_t subkey = NULL;
565 6561 : gpgme_key_sig_t keysig = NULL;
566 :
567 6561 : err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
568 6561 : opd = hook;
569 6561 : if (err)
570 0 : return err;
571 :
572 6561 : key = opd->tmp_key;
573 :
574 6561 : TRACE2 (DEBUG_CTX, "gpgme:keylist_colon_handler", ctx,
575 : "key = %p, line = %s", key, line ? line : "(null)");
576 :
577 6563 : if (!line)
578 : {
579 : /* End Of File. */
580 330 : finish_key (ctx, opd);
581 329 : return 0;
582 : }
583 :
584 112030 : while (line && fields < NR_FIELDS)
585 : {
586 99564 : field[fields++] = line;
587 99564 : line = strchr (line, ':');
588 99564 : if (line)
589 95826 : *(line++) = '\0';
590 : }
591 :
592 6233 : if (!strcmp (field[0], "sig"))
593 103 : rectype = RT_SIG;
594 6130 : else if (!strcmp (field[0], "rev"))
595 0 : rectype = RT_REV;
596 6130 : else if (!strcmp (field[0], "pub"))
597 982 : rectype = RT_PUB;
598 5148 : else if (!strcmp (field[0], "sec"))
599 56 : rectype = RT_SEC;
600 5092 : else if (!strcmp (field[0], "crt"))
601 2 : rectype = RT_CRT;
602 5090 : else if (!strcmp (field[0], "crs"))
603 2 : rectype = RT_CRS;
604 5088 : else if (!strcmp (field[0], "fpr") && key)
605 2132 : rectype = RT_FPR;
606 2956 : else if (!strcmp (field[0], "grp") && key)
607 90 : rectype = RT_GRP;
608 2866 : else if (!strcmp (field[0], "uid") && key)
609 1462 : rectype = RT_UID;
610 1404 : else if (!strcmp (field[0], "tfs") && key)
611 38 : rectype = RT_TFS;
612 1366 : else if (!strcmp (field[0], "sub") && key)
613 1063 : rectype = RT_SUB;
614 303 : else if (!strcmp (field[0], "ssb") && key)
615 26 : rectype = RT_SSB;
616 277 : else if (!strcmp (field[0], "spk") && key)
617 0 : rectype = RT_SPK;
618 : else
619 277 : rectype = RT_NONE;
620 :
621 : /* Only look at signature and trust info records immediately
622 : following a user ID. For this, clear the user ID pointer when
623 : encountering anything but a signature or trust record. */
624 6233 : if (rectype != RT_SIG && rectype != RT_REV && rectype != RT_TFS)
625 6090 : opd->tmp_uid = NULL;
626 :
627 : /* Only look at subpackets immediately following a signature. For
628 : this, clear the signature pointer when encountering anything but
629 : a subpacket. */
630 6233 : if (rectype != RT_SPK)
631 6231 : opd->tmp_keysig = NULL;
632 :
633 6233 : switch (rectype)
634 : {
635 : case RT_PUB:
636 : case RT_SEC:
637 : case RT_CRT:
638 : case RT_CRS:
639 : /* Start a new keyblock. */
640 1042 : err = _gpgme_key_new (&key);
641 1042 : if (err)
642 0 : return err;
643 1042 : key->keylist_mode = ctx->keylist_mode;
644 1042 : err = _gpgme_key_add_subkey (key, &subkey);
645 1042 : if (err)
646 : {
647 0 : gpgme_key_unref (key);
648 0 : return err;
649 : }
650 :
651 1042 : if (rectype == RT_SEC || rectype == RT_CRS)
652 58 : key->secret = subkey->secret = 1;
653 1042 : if (rectype == RT_CRT || rectype == RT_CRS)
654 4 : key->protocol = GPGME_PROTOCOL_CMS;
655 1042 : finish_key (ctx, opd);
656 1041 : opd->tmp_key = key;
657 :
658 : /* Field 2 has the trust info. */
659 1041 : if (fields >= 2)
660 1041 : set_mainkey_trust_info (key, field[1]);
661 :
662 : /* Field 3 has the key length. */
663 1041 : if (fields >= 3)
664 : {
665 1041 : int i = atoi (field[2]);
666 : /* Ignore invalid values. */
667 1041 : if (i > 1)
668 1041 : subkey->length = i;
669 : }
670 :
671 : /* Field 4 has the public key algorithm. */
672 1041 : if (fields >= 4)
673 : {
674 1041 : int i = atoi (field[3]);
675 1042 : if (i >= 1 && i < 128)
676 1042 : subkey->pubkey_algo = _gpgme_map_pk_algo (i, ctx->protocol);
677 : }
678 :
679 : /* Field 5 has the long keyid. Allow short key IDs for the
680 : output of an external keyserver listing. */
681 1042 : if (fields >= 5 && strlen (field[4]) <= DIM(subkey->_keyid) - 1)
682 1042 : strcpy (subkey->_keyid, field[4]);
683 :
684 : /* Field 6 has the timestamp (seconds). */
685 1042 : if (fields >= 6)
686 1042 : subkey->timestamp = _gpgme_parse_timestamp (field[5], NULL);
687 :
688 : /* Field 7 has the expiration time (seconds). */
689 1042 : if (fields >= 7)
690 1042 : subkey->expires = _gpgme_parse_timestamp (field[6], NULL);
691 :
692 : /* Field 8 has the X.509 serial number. */
693 1041 : if (fields >= 8 && (rectype == RT_CRT || rectype == RT_CRS))
694 : {
695 4 : key->issuer_serial = strdup (field[7]);
696 4 : if (!key->issuer_serial)
697 0 : return gpg_error_from_syserror ();
698 : }
699 :
700 : /* Field 9 has the ownertrust. */
701 1041 : if (fields >= 9)
702 1041 : set_ownertrust (key, field[8]);
703 :
704 : /* Field 10 is not used for gpg due to --fixed-list-mode option
705 : but GPGSM stores the issuer name. */
706 1041 : if (fields >= 10 && (rectype == RT_CRT || rectype == RT_CRS))
707 4 : if (_gpgme_decode_c_string (field[9], &key->issuer_name, 0))
708 0 : return gpg_error (GPG_ERR_ENOMEM); /* FIXME */
709 :
710 : /* Field 11 has the signature class. */
711 :
712 : /* Field 12 has the capabilities. */
713 1041 : if (fields >= 12)
714 1041 : set_mainkey_capability (key, field[11]);
715 :
716 : /* Field 15 carries special flags of a secret key. */
717 1041 : if (fields >= 15
718 1041 : && (key->secret
719 983 : || (ctx->keylist_mode & GPGME_KEYLIST_MODE_WITH_SECRET)))
720 : {
721 60 : err = parse_sec_field15 (key, subkey, field[14]);
722 60 : if (err)
723 0 : return err;
724 : }
725 :
726 : /* Field 17 has the curve name for ECC. */
727 1041 : if (fields >= 17 && *field[16])
728 : {
729 2 : subkey->curve = strdup (field[16]);
730 2 : if (!subkey->curve)
731 0 : return gpg_error_from_syserror ();
732 : }
733 :
734 : /* Field 18 has the compliance flags. */
735 1041 : if (fields >= 17 && *field[17])
736 126 : PARSE_COMPLIANCE_FLAGS (field[17], subkey);
737 :
738 1041 : if (fields >= 20)
739 : {
740 1037 : key->last_update = _gpgme_parse_timestamp_ul (field[18]);
741 1037 : key->origin = parse_keyorg (field[19]);
742 : }
743 :
744 1041 : break;
745 :
746 : case RT_SUB:
747 : case RT_SSB:
748 : /* Start a new subkey. */
749 1089 : err = _gpgme_key_add_subkey (key, &subkey);
750 1089 : if (err)
751 0 : return err;
752 :
753 1089 : if (rectype == RT_SSB)
754 26 : subkey->secret = 1;
755 :
756 : /* Field 2 has the trust info. */
757 1089 : if (fields >= 2)
758 1089 : set_subkey_trust_info (subkey, field[1]);
759 :
760 : /* Field 3 has the key length. */
761 1089 : if (fields >= 3)
762 : {
763 1089 : int i = atoi (field[2]);
764 : /* Ignore invalid values. */
765 1089 : if (i > 1)
766 1089 : subkey->length = i;
767 : }
768 :
769 : /* Field 4 has the public key algorithm. */
770 1089 : if (fields >= 4)
771 : {
772 1089 : int i = atoi (field[3]);
773 1089 : if (i >= 1 && i < 128)
774 1089 : subkey->pubkey_algo = _gpgme_map_pk_algo (i, ctx->protocol);
775 : }
776 :
777 : /* Field 5 has the long keyid. */
778 1089 : if (fields >= 5 && strlen (field[4]) == DIM(subkey->_keyid) - 1)
779 1089 : strcpy (subkey->_keyid, field[4]);
780 :
781 : /* Field 6 has the timestamp (seconds). */
782 1089 : if (fields >= 6)
783 1089 : subkey->timestamp = _gpgme_parse_timestamp (field[5], NULL);
784 :
785 : /* Field 7 has the expiration time (seconds). */
786 1089 : if (fields >= 7)
787 1089 : subkey->expires = _gpgme_parse_timestamp (field[6], NULL);
788 :
789 : /* Field 8 is reserved (LID). */
790 : /* Field 9 has the ownertrust. */
791 : /* Field 10, the user ID, is n/a for a subkey. */
792 :
793 : /* Field 11 has the signature class. */
794 :
795 : /* Field 12 has the capabilities. */
796 1089 : if (fields >= 12)
797 1089 : set_subkey_capability (subkey, field[11]);
798 :
799 : /* Field 15 carries special flags of a secret key. */
800 1089 : if (fields >= 15
801 1089 : && (key->secret
802 1061 : || (ctx->keylist_mode & GPGME_KEYLIST_MODE_WITH_SECRET)))
803 : {
804 28 : err = parse_sec_field15 (key, subkey, field[14]);
805 28 : if (err)
806 0 : return err;
807 : }
808 :
809 : /* Field 17 has the curve name for ECC. */
810 1089 : if (fields >= 17 && *field[16])
811 : {
812 2 : subkey->curve = strdup (field[16]);
813 2 : if (!subkey->curve)
814 0 : return gpg_error_from_syserror ();
815 : }
816 :
817 : /* Field 18 has the compliance flags. */
818 1089 : if (fields >= 17 && *field[17])
819 140 : PARSE_COMPLIANCE_FLAGS (field[17], subkey);
820 :
821 1089 : break;
822 :
823 : case RT_UID:
824 : /* Field 2 has the trust info, and field 10 has the user ID. */
825 1462 : if (fields >= 10)
826 : {
827 1462 : if (_gpgme_key_append_name (key, field[9], 1))
828 0 : return gpg_error (GPG_ERR_ENOMEM); /* FIXME */
829 :
830 1460 : if (field[1])
831 1460 : set_userid_flags (key, field[1]);
832 1460 : opd->tmp_uid = key->_last_uid;
833 1460 : if (fields >= 20)
834 : {
835 1454 : opd->tmp_uid->last_update = _gpgme_parse_timestamp_ul (field[18]);
836 1454 : opd->tmp_uid->origin = parse_keyorg (field[19]);
837 : }
838 : }
839 1460 : break;
840 :
841 : case RT_TFS:
842 38 : if (opd->tmp_uid)
843 : {
844 38 : err = parse_tfs_record (opd->tmp_uid, field, fields);
845 38 : if (err)
846 0 : return err;
847 : }
848 38 : break;
849 :
850 : case RT_FPR:
851 : /* Field 10 has the fingerprint (take only the first one). */
852 2133 : if (fields >= 10 && field[9] && *field[9])
853 : {
854 : /* Need to apply it to the last subkey because all subkeys
855 : do have fingerprints. */
856 2133 : subkey = key->_last_subkey;
857 2133 : if (!subkey->fpr)
858 : {
859 2133 : subkey->fpr = strdup (field[9]);
860 2133 : if (!subkey->fpr)
861 0 : return gpg_error_from_syserror ();
862 : }
863 : /* If this is the first subkey, store the fingerprint also
864 : in the KEY object. */
865 2133 : if (subkey == key->subkeys)
866 : {
867 1044 : if (key->fpr && strcmp (key->fpr, subkey->fpr))
868 : {
869 : /* FPR already set but mismatch: Should never happen. */
870 0 : return trace_gpg_error (GPG_ERR_INTERNAL);
871 : }
872 1044 : if (!key->fpr)
873 : {
874 1044 : key->fpr = strdup (subkey->fpr);
875 1044 : if (!key->fpr)
876 0 : return gpg_error_from_syserror ();
877 : }
878 : }
879 : }
880 :
881 : /* Field 13 has the gpgsm chain ID (take only the first one). */
882 2133 : if (fields >= 13 && !key->chain_id && *field[12])
883 : {
884 4 : key->chain_id = strdup (field[12]);
885 4 : if (!key->chain_id)
886 0 : return gpg_error_from_syserror ();
887 : }
888 2133 : break;
889 :
890 : case RT_GRP:
891 : /* Field 10 has the keygrip. */
892 90 : if (fields >= 10 && field[9] && *field[9])
893 : {
894 : /* Need to apply it to the last subkey because all subkeys
895 : have a keygrip. */
896 90 : subkey = key->_last_subkey;
897 90 : if (!subkey->keygrip)
898 : {
899 90 : subkey->keygrip = strdup (field[9]);
900 90 : if (!subkey->keygrip)
901 0 : return gpg_error_from_syserror ();
902 : }
903 : }
904 90 : break;
905 :
906 : case RT_SIG:
907 : case RT_REV:
908 103 : if (!opd->tmp_uid)
909 9 : return 0;
910 :
911 : /* Start a new (revoked) signature. */
912 94 : assert (opd->tmp_uid == key->_last_uid);
913 94 : keysig = _gpgme_key_add_sig (key, (fields >= 10) ? field[9] : NULL);
914 94 : if (!keysig)
915 0 : return gpg_error (GPG_ERR_ENOMEM); /* FIXME */
916 :
917 : /* Field 2 has the calculated trust ('!', '-', '?', '%'). */
918 94 : if (fields >= 2)
919 94 : switch (field[1][0])
920 : {
921 : case '!':
922 94 : keysig->status = gpg_error (GPG_ERR_NO_ERROR);
923 94 : break;
924 :
925 : case '-':
926 0 : keysig->status = gpg_error (GPG_ERR_BAD_SIGNATURE);
927 0 : break;
928 :
929 : case '?':
930 0 : keysig->status = gpg_error (GPG_ERR_NO_PUBKEY);
931 0 : break;
932 :
933 : case '%':
934 0 : keysig->status = gpg_error (GPG_ERR_GENERAL);
935 0 : break;
936 :
937 : default:
938 0 : keysig->status = gpg_error (GPG_ERR_NO_ERROR);
939 0 : break;
940 : }
941 :
942 : /* Field 4 has the public key algorithm. */
943 94 : if (fields >= 4)
944 : {
945 94 : int i = atoi (field[3]);
946 94 : if (i >= 1 && i < 128)
947 94 : keysig->pubkey_algo = _gpgme_map_pk_algo (i, ctx->protocol);
948 : }
949 :
950 : /* Field 5 has the long keyid. */
951 94 : if (fields >= 5 && strlen (field[4]) == DIM(keysig->_keyid) - 1)
952 94 : strcpy (keysig->_keyid, field[4]);
953 :
954 : /* Field 6 has the timestamp (seconds). */
955 94 : if (fields >= 6)
956 94 : keysig->timestamp = _gpgme_parse_timestamp (field[5], NULL);
957 :
958 : /* Field 7 has the expiration time (seconds). */
959 94 : if (fields >= 7)
960 94 : keysig->expires = _gpgme_parse_timestamp (field[6], NULL);
961 :
962 : /* Field 11 has the signature class (eg, 0x30 means revoked). */
963 94 : if (fields >= 11)
964 94 : if (field[10][0] && field[10][1])
965 : {
966 94 : int sig_class = _gpgme_hextobyte (field[10]);
967 94 : if (sig_class >= 0)
968 : {
969 94 : keysig->sig_class = sig_class;
970 94 : keysig->class = keysig->sig_class;
971 94 : if (sig_class == 0x30)
972 0 : keysig->revoked = 1;
973 : }
974 94 : if (field[10][2] == 'x')
975 76 : keysig->exportable = 1;
976 : }
977 :
978 94 : opd->tmp_keysig = keysig;
979 94 : break;
980 :
981 : case RT_SPK:
982 0 : if (!opd->tmp_keysig)
983 0 : return 0;
984 0 : assert (opd->tmp_keysig == key->_last_uid->_last_keysig);
985 :
986 0 : if (fields >= 4)
987 : {
988 : /* Field 2 has the subpacket type. */
989 0 : int type = atoi (field[1]);
990 :
991 : /* Field 3 has the flags. */
992 0 : int flags = atoi (field[2]);
993 :
994 : /* Field 4 has the length. */
995 0 : int len = atoi (field[3]);
996 :
997 : /* Field 5 has the data. */
998 0 : char *data = field[4];
999 :
1000 : /* Type 20: Notation data. */
1001 : /* Type 26: Policy URL. */
1002 0 : if (type == 20 || type == 26)
1003 : {
1004 : gpgme_sig_notation_t notation;
1005 :
1006 853 : keysig = opd->tmp_keysig;
1007 :
1008 : /* At this time, any error is serious. */
1009 853 : err = _gpgme_parse_notation (¬ation, type, flags, len, data);
1010 0 : if (err)
1011 0 : return err;
1012 :
1013 : /* Add a new notation. FIXME: Could be factored out. */
1014 0 : if (!keysig->notations)
1015 0 : keysig->notations = notation;
1016 0 : if (keysig->_last_notation)
1017 0 : keysig->_last_notation->next = notation;
1018 0 : keysig->_last_notation = notation;
1019 : }
1020 : }
1021 :
1022 : case RT_NONE:
1023 : /* Unknown record. */
1024 277 : break;
1025 : }
1026 5368 : return 0;
1027 : }
1028 :
1029 :
1030 : void
1031 1035 : _gpgme_op_keylist_event_cb (void *data, gpgme_event_io_t type, void *type_data)
1032 : {
1033 : gpgme_error_t err;
1034 1035 : gpgme_ctx_t ctx = (gpgme_ctx_t) data;
1035 1035 : gpgme_key_t key = (gpgme_key_t) type_data;
1036 : void *hook;
1037 : op_data_t opd;
1038 : struct key_queue_item_s *q, *q2;
1039 :
1040 1035 : assert (type == GPGME_EVENT_NEXT_KEY);
1041 :
1042 1035 : err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
1043 1034 : opd = hook;
1044 1034 : if (err)
1045 0 : return;
1046 :
1047 1034 : q = malloc (sizeof *q);
1048 1034 : if (!q)
1049 : {
1050 0 : gpgme_key_unref (key);
1051 : /* FIXME return GPGME_Out_Of_Core; */
1052 0 : return;
1053 : }
1054 1034 : q->key = key;
1055 1034 : q->next = NULL;
1056 : /* FIXME: Use a tail pointer? */
1057 1034 : if (!(q2 = opd->key_queue))
1058 629 : opd->key_queue = q;
1059 : else
1060 : {
1061 405 : for (; q2->next; q2 = q2->next)
1062 : ;
1063 405 : q2->next = q;
1064 : }
1065 1034 : opd->key_cond = 1;
1066 : }
1067 :
1068 :
1069 : /* Start a keylist operation within CTX, searching for keys which
1070 : match PATTERN. If SECRET_ONLY is true, only secret keys are
1071 : returned. */
1072 : gpgme_error_t
1073 330 : gpgme_op_keylist_start (gpgme_ctx_t ctx, const char *pattern, int secret_only)
1074 : {
1075 : gpgme_error_t err;
1076 : void *hook;
1077 : op_data_t opd;
1078 330 : int flags = 0;
1079 :
1080 330 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_keylist_start", ctx,
1081 : "pattern=%s, secret_only=%i", pattern, secret_only);
1082 :
1083 330 : if (!ctx)
1084 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
1085 :
1086 330 : err = _gpgme_op_reset (ctx, 2);
1087 330 : if (err)
1088 0 : return TRACE_ERR (err);
1089 :
1090 330 : err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook,
1091 : sizeof (*opd), release_op_data);
1092 330 : opd = hook;
1093 330 : if (err)
1094 0 : return TRACE_ERR (err);
1095 :
1096 330 : _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
1097 :
1098 330 : err = _gpgme_engine_set_colon_line_handler (ctx->engine,
1099 : keylist_colon_handler, ctx);
1100 329 : if (err)
1101 0 : return TRACE_ERR (err);
1102 :
1103 329 : if (ctx->offline)
1104 0 : flags |= GPGME_ENGINE_FLAG_OFFLINE;
1105 :
1106 329 : err = _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only,
1107 : ctx->keylist_mode, flags);
1108 327 : return TRACE_ERR (err);
1109 : }
1110 :
1111 :
1112 : /* Start a keylist operation within CTX, searching for keys which
1113 : match PATTERN. If SECRET_ONLY is true, only secret keys are
1114 : returned. */
1115 : gpgme_error_t
1116 6 : gpgme_op_keylist_ext_start (gpgme_ctx_t ctx, const char *pattern[],
1117 : int secret_only, int reserved)
1118 : {
1119 : gpgme_error_t err;
1120 : void *hook;
1121 : op_data_t opd;
1122 6 : int flags = 0;
1123 :
1124 6 : TRACE_BEG2 (DEBUG_CTX, "gpgme_op_keylist_ext_start", ctx,
1125 : "secret_only=%i, reserved=0x%x", secret_only, reserved);
1126 :
1127 6 : if (!ctx)
1128 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
1129 :
1130 6 : err = _gpgme_op_reset (ctx, 2);
1131 6 : if (err)
1132 0 : return TRACE_ERR (err);
1133 :
1134 6 : err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook,
1135 : sizeof (*opd), release_op_data);
1136 6 : opd = hook;
1137 6 : if (err)
1138 0 : return TRACE_ERR (err);
1139 :
1140 6 : _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
1141 6 : err = _gpgme_engine_set_colon_line_handler (ctx->engine,
1142 : keylist_colon_handler, ctx);
1143 6 : if (err)
1144 0 : return TRACE_ERR (err);
1145 :
1146 6 : if (ctx->offline)
1147 0 : flags |= GPGME_ENGINE_FLAG_OFFLINE;
1148 :
1149 6 : err = _gpgme_engine_op_keylist_ext (ctx->engine, pattern, secret_only,
1150 : reserved, ctx->keylist_mode,
1151 : flags);
1152 6 : return TRACE_ERR (err);
1153 : }
1154 :
1155 :
1156 : /* Start a keylist operation within CTX to show keys contained
1157 : * in DATA. */
1158 : gpgme_error_t
1159 5 : gpgme_op_keylist_from_data_start (gpgme_ctx_t ctx, gpgme_data_t data,
1160 : int reserved)
1161 : {
1162 : gpgme_error_t err;
1163 : void *hook;
1164 : op_data_t opd;
1165 :
1166 5 : TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_from_data_start", ctx);
1167 :
1168 5 : if (!ctx || !data || reserved)
1169 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
1170 :
1171 5 : err = _gpgme_op_reset (ctx, 2);
1172 5 : if (err)
1173 0 : return TRACE_ERR (err);
1174 :
1175 5 : err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook,
1176 : sizeof (*opd), release_op_data);
1177 5 : opd = hook;
1178 5 : if (err)
1179 0 : return TRACE_ERR (err);
1180 :
1181 5 : _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
1182 5 : err = _gpgme_engine_set_colon_line_handler (ctx->engine,
1183 : keylist_colon_handler, ctx);
1184 5 : if (err)
1185 0 : return TRACE_ERR (err);
1186 :
1187 5 : err = _gpgme_engine_op_keylist_data (ctx->engine, data);
1188 5 : return TRACE_ERR (err);
1189 : }
1190 :
1191 :
1192 : /* Return the next key from the keylist in R_KEY. */
1193 : gpgme_error_t
1194 1349 : gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key)
1195 : {
1196 : gpgme_error_t err;
1197 : struct key_queue_item_s *queue_item;
1198 : void *hook;
1199 : op_data_t opd;
1200 :
1201 1349 : TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_next", ctx);
1202 :
1203 1349 : if (!ctx || !r_key)
1204 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
1205 1349 : *r_key = NULL;
1206 1349 : if (!ctx)
1207 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
1208 :
1209 1349 : err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
1210 1349 : opd = hook;
1211 1349 : if (err)
1212 0 : return TRACE_ERR (err);
1213 1349 : if (opd == NULL)
1214 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
1215 :
1216 1349 : if (!opd->key_queue)
1217 : {
1218 954 : err = _gpgme_wait_on_condition (ctx, &opd->key_cond, NULL);
1219 951 : if (err)
1220 0 : return TRACE_ERR (err);
1221 :
1222 951 : if (!opd->key_cond)
1223 327 : return TRACE_ERR (opd->keydb_search_err? opd->keydb_search_err
1224 : /**/ : gpg_error (GPG_ERR_EOF));
1225 :
1226 624 : opd->key_cond = 0;
1227 624 : assert (opd->key_queue);
1228 : }
1229 1019 : queue_item = opd->key_queue;
1230 1019 : opd->key_queue = queue_item->next;
1231 1019 : if (!opd->key_queue)
1232 620 : opd->key_cond = 0;
1233 :
1234 1019 : *r_key = queue_item->key;
1235 1019 : free (queue_item);
1236 :
1237 1019 : return TRACE_SUC2 ("key=%p (%s)", *r_key,
1238 : ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ?
1239 : (*r_key)->subkeys->fpr : "invalid");
1240 : }
1241 :
1242 :
1243 : /* Terminate a pending keylist operation within CTX. */
1244 : gpgme_error_t
1245 62 : gpgme_op_keylist_end (gpgme_ctx_t ctx)
1246 : {
1247 62 : TRACE (DEBUG_CTX, "gpgme_op_keylist_end", ctx);
1248 :
1249 62 : if (!ctx)
1250 0 : return gpg_error (GPG_ERR_INV_VALUE);
1251 :
1252 62 : return 0;
1253 : }
1254 :
1255 :
1256 : /* Get the key with the fingerprint FPR from the crypto backend. If
1257 : SECRET is true, get the secret key. */
1258 : gpgme_error_t
1259 247 : gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
1260 : int secret)
1261 : {
1262 : gpgme_ctx_t listctx;
1263 : gpgme_error_t err;
1264 : gpgme_key_t result, key;
1265 :
1266 247 : TRACE_BEG2 (DEBUG_CTX, "gpgme_get_key", ctx,
1267 : "fpr=%s, secret=%i", fpr, secret);
1268 :
1269 247 : if (r_key)
1270 247 : *r_key = NULL;
1271 :
1272 247 : if (!ctx || !r_key || !fpr)
1273 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
1274 :
1275 247 : if (strlen (fpr) < 8) /* We have at least a key ID. */
1276 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
1277 :
1278 : /* FIXME: We use our own context because we have to avoid the user's
1279 : I/O callback handlers. */
1280 247 : err = gpgme_new (&listctx);
1281 247 : if (err)
1282 0 : return TRACE_ERR (err);
1283 : {
1284 : gpgme_protocol_t proto;
1285 : gpgme_engine_info_t info;
1286 :
1287 : /* Clone the relevant state. */
1288 247 : proto = gpgme_get_protocol (ctx);
1289 247 : gpgme_set_protocol (listctx, proto);
1290 247 : gpgme_set_keylist_mode (listctx, gpgme_get_keylist_mode (ctx));
1291 247 : info = gpgme_ctx_get_engine_info (ctx);
1292 495 : while (info && info->protocol != proto)
1293 1 : info = info->next;
1294 247 : if (info)
1295 247 : gpgme_ctx_set_engine_info (listctx, proto,
1296 247 : info->file_name, info->home_dir);
1297 : }
1298 :
1299 248 : err = gpgme_op_keylist_start (listctx, fpr, secret);
1300 248 : if (!err)
1301 248 : err = gpgme_op_keylist_next (listctx, &result);
1302 247 : if (!err)
1303 : {
1304 : try_next_key:
1305 243 : err = gpgme_op_keylist_next (listctx, &key);
1306 243 : if (gpgme_err_code (err) == GPG_ERR_EOF)
1307 243 : err = 0;
1308 : else
1309 : {
1310 0 : if (!err
1311 0 : && result && result->subkeys && result->subkeys->fpr
1312 0 : && key && key->subkeys && key->subkeys->fpr
1313 0 : && !strcmp (result->subkeys->fpr, key->subkeys->fpr))
1314 : {
1315 : /* The fingerprint is identical. We assume that this is
1316 : the same key and don't mark it as an ambiguous. This
1317 : problem may occur with corrupted keyrings and has
1318 : been noticed often with gpgsm. In fact gpgsm uses a
1319 : similar hack to sort out such duplicates but it can't
1320 : do that while listing keys. */
1321 0 : gpgme_key_unref (key);
1322 0 : goto try_next_key;
1323 : }
1324 0 : if (!err)
1325 : {
1326 0 : gpgme_key_unref (key);
1327 0 : err = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
1328 : }
1329 0 : gpgme_key_unref (result);
1330 0 : result = NULL;
1331 : }
1332 : }
1333 247 : gpgme_release (listctx);
1334 247 : if (! err)
1335 : {
1336 243 : *r_key = result;
1337 243 : TRACE_LOG2 ("key=%p (%s)", *r_key,
1338 : ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ?
1339 : (*r_key)->subkeys->fpr : "invalid");
1340 : }
1341 247 : return TRACE_ERR (err);
1342 : }
|