Line data Source code
1 : /* key.c - Key objects.
2 : Copyright (C) 2000 Werner Koch (dd9jn)
3 : Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
4 :
5 : This file is part of GPGME.
6 :
7 : GPGME is free software; you can redistribute it and/or modify it
8 : under the terms of the GNU Lesser General Public License as
9 : published by the Free Software Foundation; either version 2.1 of
10 : the License, or (at your option) any later version.
11 :
12 : GPGME is distributed in the hope that it will be useful, but
13 : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : Lesser General Public License for more details.
16 :
17 : You should have received a copy of the GNU Lesser General Public
18 : License along with this program; if not, write to the Free Software
19 : Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 : 02111-1307, USA. */
21 :
22 : #if HAVE_CONFIG_H
23 : #include <config.h>
24 : #endif
25 : #include <stdlib.h>
26 : #include <string.h>
27 : #include <assert.h>
28 : #include <errno.h>
29 :
30 : #include "util.h"
31 : #include "ops.h"
32 : #include "sema.h"
33 : #include "debug.h"
34 : #include "mbox-util.h"
35 :
36 :
37 :
38 : /* Protects all reference counters in keys. All other accesses to a
39 : key are read only. */
40 : DEFINE_STATIC_LOCK (key_ref_lock);
41 :
42 :
43 : /* Create a new key. */
44 : gpgme_error_t
45 1035 : _gpgme_key_new (gpgme_key_t *r_key)
46 : {
47 : gpgme_key_t key;
48 :
49 1035 : key = calloc (1, sizeof *key);
50 1035 : if (!key)
51 0 : return gpg_error_from_syserror ();
52 1035 : key->_refs = 1;
53 :
54 1035 : *r_key = key;
55 1035 : return 0;
56 : }
57 :
58 :
59 : gpgme_error_t
60 2096 : _gpgme_key_add_subkey (gpgme_key_t key, gpgme_subkey_t *r_subkey)
61 : {
62 : gpgme_subkey_t subkey;
63 :
64 2096 : subkey = calloc (1, sizeof *subkey);
65 2096 : if (!subkey)
66 0 : return gpg_error_from_syserror ();
67 2096 : subkey->keyid = subkey->_keyid;
68 2096 : subkey->_keyid[16] = '\0';
69 :
70 2096 : if (!key->subkeys)
71 1024 : key->subkeys = subkey;
72 2096 : if (key->_last_subkey)
73 1072 : key->_last_subkey->next = subkey;
74 2096 : key->_last_subkey = subkey;
75 :
76 2096 : *r_subkey = subkey;
77 2096 : return 0;
78 : }
79 :
80 :
81 : static char *
82 3970 : set_user_id_part (char *tail, const char *buf, size_t len)
83 : {
84 9329 : while (len && (buf[len - 1] == ' ' || buf[len - 1] == '\t'))
85 1389 : len--;
86 51031 : for (; len; len--)
87 47061 : *tail++ = *buf++;
88 3970 : *tail++ = 0;
89 3970 : return tail;
90 : }
91 :
92 :
93 : static void
94 1526 : parse_user_id (char *src, char **name, char **email,
95 : char **comment, char *tail)
96 : {
97 1526 : const char *start = NULL;
98 1526 : int in_name = 0;
99 1526 : int in_email = 0;
100 1526 : int in_comment = 0;
101 :
102 57444 : while (*src)
103 : {
104 54393 : if (in_email)
105 : {
106 20977 : if (*src == '<')
107 : /* Not legal but anyway. */
108 0 : in_email++;
109 20977 : else if (*src == '>')
110 : {
111 1132 : if (!--in_email && !*email)
112 : {
113 1132 : *email = tail;
114 1132 : tail = set_user_id_part (tail, start, src - start);
115 : }
116 : }
117 : }
118 33416 : else if (in_comment)
119 : {
120 12775 : if (*src == '(')
121 0 : in_comment++;
122 12775 : else if (*src == ')')
123 : {
124 1311 : if (!--in_comment && !*comment)
125 : {
126 1311 : *comment = tail;
127 1311 : tail = set_user_id_part (tail, start, src - start);
128 : }
129 : }
130 : }
131 20641 : else if (*src == '<')
132 : {
133 1133 : if (in_name)
134 : {
135 78 : if (!*name)
136 : {
137 78 : *name = tail;
138 78 : tail = set_user_id_part (tail, start, src - start);
139 : }
140 78 : in_name = 0;
141 : }
142 1133 : in_email = 1;
143 1133 : start = src + 1;
144 : }
145 19508 : else if (*src == '(')
146 : {
147 1311 : if (in_name)
148 : {
149 1311 : if (!*name)
150 : {
151 1311 : *name = tail;
152 1311 : tail = set_user_id_part (tail, start, src - start);
153 : }
154 1311 : in_name = 0;
155 : }
156 1311 : in_comment = 1;
157 1311 : start = src + 1;
158 : }
159 18197 : else if (!in_name && *src != ' ' && *src != '\t')
160 : {
161 1527 : in_name = 1;
162 1527 : start = src;
163 : }
164 54392 : src++;
165 : }
166 :
167 1525 : if (in_name)
168 : {
169 138 : if (!*name)
170 : {
171 138 : *name = tail;
172 138 : tail = set_user_id_part (tail, start, src - start);
173 : }
174 : }
175 :
176 : /* Let unused parts point to an EOS. */
177 1525 : tail--;
178 1525 : if (!*name)
179 0 : *name = tail;
180 1525 : if (!*email)
181 394 : *email = tail;
182 1525 : if (!*comment)
183 216 : *comment = tail;
184 1525 : }
185 :
186 :
187 : static void
188 6 : parse_x509_user_id (char *src, char **name, char **email,
189 : char **comment, char *tail)
190 : {
191 6 : if (*src == '<' && src[strlen (src) - 1] == '>')
192 2 : *email = src;
193 :
194 : /* Let unused parts point to an EOS. */
195 6 : tail--;
196 6 : if (!*name)
197 6 : *name = tail;
198 6 : if (!*email)
199 4 : *email = tail;
200 6 : if (!*comment)
201 6 : *comment = tail;
202 6 : }
203 :
204 :
205 : /* Take a name from the --with-colon listing, remove certain escape
206 : sequences sequences and put it into the list of UIDs. */
207 : gpgme_error_t
208 1439 : _gpgme_key_append_name (gpgme_key_t key, const char *src, int convert)
209 : {
210 : gpgme_user_id_t uid;
211 : char *dst;
212 1439 : int src_len = strlen (src);
213 :
214 1439 : assert (key);
215 : /* We can malloc a buffer of the same length, because the converted
216 : string will never be larger. Actually we allocate it twice the
217 : size, so that we are able to store the parsed stuff there too. */
218 1439 : uid = malloc (sizeof (*uid) + 2 * src_len + 3);
219 1439 : if (!uid)
220 0 : return gpg_error_from_syserror ();
221 1439 : memset (uid, 0, sizeof *uid);
222 :
223 1439 : uid->uid = ((char *) uid) + sizeof (*uid);
224 1439 : dst = uid->uid;
225 1439 : if (convert)
226 1422 : _gpgme_decode_c_string (src, &dst, src_len + 1);
227 : else
228 17 : memcpy (dst, src, src_len + 1);
229 :
230 1439 : dst += strlen (dst) + 1;
231 1439 : if (key->protocol == GPGME_PROTOCOL_CMS)
232 6 : parse_x509_user_id (uid->uid, &uid->name, &uid->email,
233 : &uid->comment, dst);
234 : else
235 1433 : parse_user_id (uid->uid, &uid->name, &uid->email,
236 : &uid->comment, dst);
237 :
238 1438 : uid->address = _gpgme_mailbox_from_userid (uid->uid);
239 1438 : if ((!uid->email || !*uid->email) && uid->address && uid->name
240 80 : && !strcmp (uid->name, uid->address))
241 : {
242 : /* Name and address are the same. This is a mailbox only key.
243 : Use address as email and remove name. */
244 80 : *uid->name = '\0';
245 80 : uid->email = uid->address;
246 : }
247 :
248 1438 : if (!key->uids)
249 1033 : key->uids = uid;
250 1438 : if (key->_last_uid)
251 405 : key->_last_uid->next = uid;
252 1438 : key->_last_uid = uid;
253 :
254 1438 : return 0;
255 : }
256 :
257 :
258 : gpgme_key_sig_t
259 94 : _gpgme_key_add_sig (gpgme_key_t key, char *src)
260 : {
261 94 : int src_len = src ? strlen (src) : 0;
262 : gpgme_user_id_t uid;
263 : gpgme_key_sig_t sig;
264 :
265 94 : assert (key); /* XXX */
266 :
267 94 : uid = key->_last_uid;
268 94 : assert (uid); /* XXX */
269 :
270 : /* We can malloc a buffer of the same length, because the converted
271 : string will never be larger. Actually we allocate it twice the
272 : size, so that we are able to store the parsed stuff there too. */
273 94 : sig = malloc (sizeof (*sig) + 2 * src_len + 3);
274 94 : if (!sig)
275 0 : return NULL;
276 94 : memset (sig, 0, sizeof *sig);
277 :
278 94 : sig->keyid = sig->_keyid;
279 94 : sig->_keyid[16] = '\0';
280 94 : sig->uid = ((char *) sig) + sizeof (*sig);
281 :
282 94 : if (src)
283 : {
284 94 : char *dst = sig->uid;
285 94 : _gpgme_decode_c_string (src, &dst, src_len + 1);
286 94 : dst += strlen (dst) + 1;
287 94 : if (key->protocol == GPGME_PROTOCOL_CMS)
288 0 : parse_x509_user_id (sig->uid, &sig->name, &sig->email,
289 : &sig->comment, dst);
290 : else
291 94 : parse_user_id (sig->uid, &sig->name, &sig->email,
292 : &sig->comment, dst);
293 : }
294 : else
295 0 : sig->uid[0] = '\0';
296 :
297 94 : if (!uid->signatures)
298 61 : uid->signatures = sig;
299 94 : if (uid->_last_keysig)
300 33 : uid->_last_keysig->next = sig;
301 94 : uid->_last_keysig = sig;
302 :
303 94 : return sig;
304 : }
305 :
306 :
307 : /* Acquire a reference to KEY. */
308 : void
309 109 : gpgme_key_ref (gpgme_key_t key)
310 : {
311 109 : LOCK (key_ref_lock);
312 109 : key->_refs++;
313 109 : UNLOCK (key_ref_lock);
314 109 : }
315 :
316 :
317 : /* gpgme_key_unref releases the key object. Note, that this function
318 : may not do an actual release if there are other shallow copies of
319 : the objects. You have to call this function for every newly
320 : created key object as well as for every gpgme_key_ref() done on the
321 : key object. */
322 : void
323 695 : gpgme_key_unref (gpgme_key_t key)
324 : {
325 : gpgme_user_id_t uid;
326 : gpgme_subkey_t subkey;
327 :
328 695 : if (!key)
329 0 : return;
330 :
331 695 : LOCK (key_ref_lock);
332 695 : assert (key->_refs > 0);
333 695 : if (--key->_refs)
334 : {
335 99 : UNLOCK (key_ref_lock);
336 99 : return;
337 : }
338 596 : UNLOCK (key_ref_lock);
339 :
340 596 : subkey = key->subkeys;
341 2378 : while (subkey)
342 : {
343 1186 : gpgme_subkey_t next = subkey->next;
344 1186 : free (subkey->fpr);
345 1186 : free (subkey->curve);
346 1186 : free (subkey->keygrip);
347 1186 : free (subkey->card_number);
348 1186 : free (subkey);
349 1186 : subkey = next;
350 : }
351 :
352 596 : uid = key->uids;
353 2017 : while (uid)
354 : {
355 825 : gpgme_user_id_t next_uid = uid->next;
356 825 : gpgme_key_sig_t keysig = uid->signatures;
357 825 : gpgme_tofu_info_t tofu = uid->tofu;
358 :
359 1686 : while (keysig)
360 : {
361 36 : gpgme_key_sig_t next_keysig = keysig->next;
362 36 : gpgme_sig_notation_t notation = keysig->notations;
363 :
364 72 : while (notation)
365 : {
366 0 : gpgme_sig_notation_t next_notation = notation->next;
367 :
368 0 : _gpgme_sig_notation_free (notation);
369 0 : notation = next_notation;
370 : }
371 :
372 36 : free (keysig);
373 36 : keysig = next_keysig;
374 : }
375 :
376 1681 : while (tofu)
377 : {
378 : /* NB: The ->next is currently not used but we are prepared
379 : * for it. */
380 31 : gpgme_tofu_info_t tofu_next = tofu->next;
381 :
382 31 : free (tofu->description);
383 31 : free (tofu);
384 31 : tofu = tofu_next;
385 : }
386 :
387 825 : free (uid->address);
388 825 : free (uid);
389 825 : uid = next_uid;
390 : }
391 :
392 596 : free (key->issuer_serial);
393 596 : free (key->issuer_name);
394 596 : free (key->chain_id);
395 596 : free (key->fpr);
396 :
397 596 : free (key);
398 : }
399 :
400 :
401 :
402 : /* Support functions. */
403 :
404 : /* Create a dummy key to specify an email address. */
405 : gpgme_error_t
406 0 : gpgme_key_from_uid (gpgme_key_t *r_key, const char *name)
407 : {
408 : gpgme_error_t err;
409 : gpgme_key_t key;
410 :
411 0 : *r_key = NULL;
412 0 : err = _gpgme_key_new (&key);
413 0 : if (err)
414 0 : return err;
415 :
416 : /* Note: protocol doesn't matter if only email is provided. */
417 0 : err = _gpgme_key_append_name (key, name, 0);
418 0 : if (err)
419 0 : gpgme_key_unref (key);
420 : else
421 0 : *r_key = key;
422 :
423 0 : return err;
424 : }
425 :
426 :
427 :
428 : /* Compatibility interfaces. */
429 :
430 : void
431 0 : gpgme_key_release (gpgme_key_t key)
432 : {
433 0 : gpgme_key_unref (key);
434 0 : }
435 :
436 :
437 : static const char *
438 0 : otrust_to_string (int otrust)
439 : {
440 0 : switch (otrust)
441 : {
442 : case GPGME_VALIDITY_NEVER:
443 0 : return "n";
444 :
445 : case GPGME_VALIDITY_MARGINAL:
446 0 : return "m";
447 :
448 : case GPGME_VALIDITY_FULL:
449 0 : return "f";
450 :
451 : case GPGME_VALIDITY_ULTIMATE:
452 0 : return "u";
453 :
454 : default:
455 0 : return "?";
456 : }
457 : }
458 :
459 :
460 : static const char *
461 0 : validity_to_string (int validity)
462 : {
463 0 : switch (validity)
464 : {
465 : case GPGME_VALIDITY_UNDEFINED:
466 0 : return "q";
467 :
468 : case GPGME_VALIDITY_NEVER:
469 0 : return "n";
470 :
471 : case GPGME_VALIDITY_MARGINAL:
472 0 : return "m";
473 :
474 : case GPGME_VALIDITY_FULL:
475 0 : return "f";
476 :
477 : case GPGME_VALIDITY_ULTIMATE:
478 0 : return "u";
479 :
480 : case GPGME_VALIDITY_UNKNOWN:
481 : default:
482 0 : return "?";
483 : }
484 : }
485 :
486 :
487 : static const char *
488 0 : capabilities_to_string (gpgme_subkey_t subkey)
489 : {
490 : static const char *const strings[8] =
491 : {
492 : "",
493 : "c",
494 : "s",
495 : "sc",
496 : "e",
497 : "ec",
498 : "es",
499 : "esc"
500 : };
501 0 : return strings[(!!subkey->can_encrypt << 2)
502 0 : | (!!subkey->can_sign << 1)
503 0 : | (!!subkey->can_certify)];
504 : }
505 :
506 :
507 : /* Return the value of the attribute WHAT of ITEM, which has to be
508 : representable by a string. */
509 : const char *
510 0 : gpgme_key_get_string_attr (gpgme_key_t key, _gpgme_attr_t what,
511 : const void *reserved, int idx)
512 : {
513 : gpgme_subkey_t subkey;
514 : gpgme_user_id_t uid;
515 : int i;
516 :
517 0 : if (!key || reserved || idx < 0)
518 0 : return NULL;
519 :
520 : /* Select IDXth subkey. */
521 0 : subkey = key->subkeys;
522 0 : for (i = 0; i < idx; i++)
523 : {
524 0 : subkey = subkey->next;
525 0 : if (!subkey)
526 0 : break;
527 : }
528 :
529 : /* Select the IDXth user ID. */
530 0 : uid = key->uids;
531 0 : for (i = 0; i < idx; i++)
532 : {
533 0 : uid = uid->next;
534 0 : if (!uid)
535 0 : break;
536 : }
537 :
538 0 : switch (what)
539 : {
540 : case GPGME_ATTR_KEYID:
541 0 : return subkey ? subkey->keyid : NULL;
542 :
543 : case GPGME_ATTR_FPR:
544 0 : return subkey ? subkey->fpr : NULL;
545 :
546 : case GPGME_ATTR_ALGO:
547 0 : return subkey ? gpgme_pubkey_algo_name (subkey->pubkey_algo) : NULL;
548 :
549 : case GPGME_ATTR_TYPE:
550 0 : return key->protocol == GPGME_PROTOCOL_CMS ? "X.509" : "PGP";
551 :
552 : case GPGME_ATTR_OTRUST:
553 0 : return otrust_to_string (key->owner_trust);
554 :
555 : case GPGME_ATTR_USERID:
556 0 : return uid ? uid->uid : NULL;
557 :
558 : case GPGME_ATTR_NAME:
559 0 : return uid ? uid->name : NULL;
560 :
561 : case GPGME_ATTR_EMAIL:
562 0 : return uid ? uid->email : NULL;
563 :
564 : case GPGME_ATTR_COMMENT:
565 0 : return uid ? uid->comment : NULL;
566 :
567 : case GPGME_ATTR_VALIDITY:
568 0 : return uid ? validity_to_string (uid->validity) : NULL;
569 :
570 : case GPGME_ATTR_KEY_CAPS:
571 0 : return subkey ? capabilities_to_string (subkey) : NULL;
572 :
573 : case GPGME_ATTR_SERIAL:
574 0 : return key->issuer_serial;
575 :
576 : case GPGME_ATTR_ISSUER:
577 0 : return idx ? NULL : key->issuer_name;
578 :
579 : case GPGME_ATTR_CHAINID:
580 0 : return key->chain_id;
581 :
582 : default:
583 0 : return NULL;
584 : }
585 : }
586 :
587 :
588 : unsigned long
589 0 : gpgme_key_get_ulong_attr (gpgme_key_t key, _gpgme_attr_t what,
590 : const void *reserved, int idx)
591 : {
592 : gpgme_subkey_t subkey;
593 : gpgme_user_id_t uid;
594 : int i;
595 :
596 0 : if (!key || reserved || idx < 0)
597 0 : return 0;
598 :
599 : /* Select IDXth subkey. */
600 0 : subkey = key->subkeys;
601 0 : for (i = 0; i < idx; i++)
602 : {
603 0 : subkey = subkey->next;
604 0 : if (!subkey)
605 0 : break;
606 : }
607 :
608 : /* Select the IDXth user ID. */
609 0 : uid = key->uids;
610 0 : for (i = 0; i < idx; i++)
611 : {
612 0 : uid = uid->next;
613 0 : if (!uid)
614 0 : break;
615 : }
616 :
617 0 : switch (what)
618 : {
619 : case GPGME_ATTR_ALGO:
620 0 : return subkey ? (unsigned long) subkey->pubkey_algo : 0;
621 :
622 : case GPGME_ATTR_LEN:
623 0 : return subkey ? (unsigned long) subkey->length : 0;
624 :
625 : case GPGME_ATTR_TYPE:
626 0 : return key->protocol == GPGME_PROTOCOL_CMS ? 1 : 0;
627 :
628 : case GPGME_ATTR_CREATED:
629 0 : return (subkey && subkey->timestamp >= 0)
630 0 : ? (unsigned long) subkey->timestamp : 0;
631 :
632 : case GPGME_ATTR_EXPIRE:
633 0 : return (subkey && subkey->expires >= 0)
634 0 : ? (unsigned long) subkey->expires : 0;
635 :
636 : case GPGME_ATTR_VALIDITY:
637 0 : return uid ? uid->validity : 0;
638 :
639 : case GPGME_ATTR_OTRUST:
640 0 : return key->owner_trust;
641 :
642 : case GPGME_ATTR_IS_SECRET:
643 0 : return !!key->secret;
644 :
645 : case GPGME_ATTR_KEY_REVOKED:
646 0 : return subkey ? subkey->revoked : 0;
647 :
648 : case GPGME_ATTR_KEY_INVALID:
649 0 : return subkey ? subkey->invalid : 0;
650 :
651 : case GPGME_ATTR_KEY_EXPIRED:
652 0 : return subkey ? subkey->expired : 0;
653 :
654 : case GPGME_ATTR_KEY_DISABLED:
655 0 : return subkey ? subkey->disabled : 0;
656 :
657 : case GPGME_ATTR_UID_REVOKED:
658 0 : return uid ? uid->revoked : 0;
659 :
660 : case GPGME_ATTR_UID_INVALID:
661 0 : return uid ? uid->invalid : 0;
662 :
663 : case GPGME_ATTR_CAN_ENCRYPT:
664 0 : return key->can_encrypt;
665 :
666 : case GPGME_ATTR_CAN_SIGN:
667 0 : return key->can_sign;
668 :
669 : case GPGME_ATTR_CAN_CERTIFY:
670 0 : return key->can_certify;
671 :
672 : default:
673 0 : return 0;
674 : }
675 : }
676 :
677 :
678 : static gpgme_key_sig_t
679 0 : get_keysig (gpgme_key_t key, int uid_idx, int idx)
680 : {
681 : gpgme_user_id_t uid;
682 : gpgme_key_sig_t sig;
683 :
684 0 : if (!key || uid_idx < 0 || idx < 0)
685 0 : return NULL;
686 :
687 0 : uid = key->uids;
688 0 : while (uid && uid_idx > 0)
689 : {
690 0 : uid = uid->next;
691 0 : uid_idx--;
692 : }
693 0 : if (!uid)
694 0 : return NULL;
695 :
696 0 : sig = uid->signatures;
697 0 : while (sig && idx > 0)
698 : {
699 0 : sig = sig->next;
700 0 : idx--;
701 : }
702 0 : return sig;
703 : }
704 :
705 :
706 : const char *
707 0 : gpgme_key_sig_get_string_attr (gpgme_key_t key, int uid_idx,
708 : _gpgme_attr_t what,
709 : const void *reserved, int idx)
710 : {
711 0 : gpgme_key_sig_t certsig = get_keysig (key, uid_idx, idx);
712 :
713 0 : if (!certsig || reserved)
714 0 : return NULL;
715 :
716 0 : switch (what)
717 : {
718 : case GPGME_ATTR_KEYID:
719 0 : return certsig->keyid;
720 :
721 : case GPGME_ATTR_ALGO:
722 0 : return gpgme_pubkey_algo_name (certsig->pubkey_algo);
723 :
724 : case GPGME_ATTR_USERID:
725 0 : return certsig->uid;
726 :
727 : case GPGME_ATTR_NAME:
728 0 : return certsig->name;
729 :
730 : case GPGME_ATTR_EMAIL:
731 0 : return certsig->email;
732 :
733 : case GPGME_ATTR_COMMENT:
734 0 : return certsig->comment;
735 :
736 : default:
737 0 : return NULL;
738 : }
739 : }
740 :
741 :
742 : unsigned long
743 0 : gpgme_key_sig_get_ulong_attr (gpgme_key_t key, int uid_idx, _gpgme_attr_t what,
744 : const void *reserved, int idx)
745 : {
746 0 : gpgme_key_sig_t certsig = get_keysig (key, uid_idx, idx);
747 :
748 0 : if (!certsig || reserved)
749 0 : return 0;
750 :
751 0 : switch (what)
752 : {
753 : case GPGME_ATTR_ALGO:
754 0 : return (unsigned long) certsig->pubkey_algo;
755 :
756 : case GPGME_ATTR_CREATED:
757 0 : return certsig->timestamp < 0 ? 0L : (unsigned long) certsig->timestamp;
758 :
759 : case GPGME_ATTR_EXPIRE:
760 0 : return certsig->expires < 0 ? 0L : (unsigned long) certsig->expires;
761 :
762 : case GPGME_ATTR_KEY_REVOKED:
763 0 : return certsig->revoked;
764 :
765 : case GPGME_ATTR_KEY_INVALID:
766 0 : return certsig->invalid;
767 :
768 : case GPGME_ATTR_KEY_EXPIRED:
769 0 : return certsig->expired;
770 :
771 : case GPGME_ATTR_SIG_CLASS:
772 0 : return certsig->sig_class;
773 :
774 : case GPGME_ATTR_SIG_STATUS:
775 0 : return certsig->status;
776 :
777 : default:
778 0 : return 0;
779 : }
780 : }
|