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 955 : _gpgme_key_new (gpgme_key_t *r_key)
46 : {
47 : gpgme_key_t key;
48 :
49 955 : key = calloc (1, sizeof *key);
50 955 : if (!key)
51 0 : return gpg_error_from_syserror ();
52 955 : key->_refs = 1;
53 :
54 955 : *r_key = key;
55 955 : return 0;
56 : }
57 :
58 :
59 : gpgme_error_t
60 1943 : _gpgme_key_add_subkey (gpgme_key_t key, gpgme_subkey_t *r_subkey)
61 : {
62 : gpgme_subkey_t subkey;
63 :
64 1943 : subkey = calloc (1, sizeof *subkey);
65 1943 : if (!subkey)
66 0 : return gpg_error_from_syserror ();
67 1943 : subkey->keyid = subkey->_keyid;
68 1943 : subkey->_keyid[16] = '\0';
69 :
70 1943 : if (!key->subkeys)
71 946 : key->subkeys = subkey;
72 1943 : if (key->_last_subkey)
73 997 : key->_last_subkey->next = subkey;
74 1943 : key->_last_subkey = subkey;
75 :
76 1943 : *r_subkey = subkey;
77 1943 : return 0;
78 : }
79 :
80 :
81 : static char *
82 3672 : set_user_id_part (char *tail, const char *buf, size_t len)
83 : {
84 8630 : while (len && (buf[len - 1] == ' ' || buf[len - 1] == '\t'))
85 1286 : len--;
86 47179 : for (; len; len--)
87 43507 : *tail++ = *buf++;
88 3672 : *tail++ = 0;
89 3672 : return tail;
90 : }
91 :
92 :
93 : static void
94 1431 : parse_user_id (char *src, char **name, char **email,
95 : char **comment, char *tail)
96 : {
97 1431 : const char *start = NULL;
98 1431 : int in_name = 0;
99 1431 : int in_email = 0;
100 1431 : int in_comment = 0;
101 :
102 53111 : while (*src)
103 : {
104 50260 : if (in_email)
105 : {
106 19226 : if (*src == '<')
107 : /* Not legal but anyway. */
108 0 : in_email++;
109 19226 : else if (*src == '>')
110 : {
111 1040 : if (!--in_email && !*email)
112 : {
113 1040 : *email = tail;
114 1040 : tail = set_user_id_part (tail, start, src - start);
115 : }
116 : }
117 : }
118 31034 : else if (in_comment)
119 : {
120 11838 : if (*src == '(')
121 0 : in_comment++;
122 11838 : else if (*src == ')')
123 : {
124 1212 : if (!--in_comment && !*comment)
125 : {
126 1212 : *comment = tail;
127 1212 : tail = set_user_id_part (tail, start, src - start);
128 : }
129 : }
130 : }
131 19196 : else if (*src == '<')
132 : {
133 1040 : if (in_name)
134 : {
135 74 : if (!*name)
136 : {
137 74 : *name = tail;
138 74 : tail = set_user_id_part (tail, start, src - start);
139 : }
140 74 : in_name = 0;
141 : }
142 1040 : in_email = 1;
143 1040 : start = src + 1;
144 : }
145 18156 : else if (*src == '(')
146 : {
147 1212 : if (in_name)
148 : {
149 1212 : if (!*name)
150 : {
151 1212 : *name = tail;
152 1212 : tail = set_user_id_part (tail, start, src - start);
153 : }
154 1212 : in_name = 0;
155 : }
156 1212 : in_comment = 1;
157 1212 : start = src + 1;
158 : }
159 16944 : else if (!in_name && *src != ' ' && *src != '\t')
160 : {
161 1420 : in_name = 1;
162 1420 : start = src;
163 : }
164 50249 : src++;
165 : }
166 :
167 1420 : if (in_name)
168 : {
169 134 : if (!*name)
170 : {
171 134 : *name = tail;
172 134 : tail = set_user_id_part (tail, start, src - start);
173 : }
174 : }
175 :
176 : /* Let unused parts point to an EOS. */
177 1420 : tail--;
178 1420 : if (!*name)
179 0 : *name = tail;
180 1420 : if (!*email)
181 380 : *email = tail;
182 1420 : if (!*comment)
183 208 : *comment = tail;
184 1420 : }
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 1340 : _gpgme_key_append_name (gpgme_key_t key, const char *src, int convert)
209 : {
210 : gpgme_user_id_t uid;
211 : char *dst;
212 1340 : int src_len = strlen (src);
213 :
214 1340 : 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 1340 : uid = malloc (sizeof (*uid) + 2 * src_len + 3);
219 1340 : if (!uid)
220 0 : return gpg_error_from_syserror ();
221 1340 : memset (uid, 0, sizeof *uid);
222 :
223 1340 : uid->uid = ((char *) uid) + sizeof (*uid);
224 1340 : dst = uid->uid;
225 1340 : if (convert)
226 1325 : _gpgme_decode_c_string (src, &dst, src_len + 1);
227 : else
228 15 : memcpy (dst, src, src_len + 1);
229 :
230 1340 : dst += strlen (dst) + 1;
231 1340 : 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 1334 : parse_user_id (uid->uid, &uid->name, &uid->email,
236 : &uid->comment, dst);
237 :
238 1340 : uid->address = _gpgme_mailbox_from_userid (uid->uid);
239 1340 : if ((!uid->email || !*uid->email) && uid->address && uid->name
240 76 : && !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 76 : *uid->name = '\0';
245 76 : uid->email = uid->address;
246 : }
247 :
248 1340 : if (!key->uids)
249 950 : key->uids = uid;
250 1340 : if (key->_last_uid)
251 390 : key->_last_uid->next = uid;
252 1340 : key->_last_uid = uid;
253 :
254 1340 : return 0;
255 : }
256 :
257 :
258 : gpgme_key_sig_t
259 86 : _gpgme_key_add_sig (gpgme_key_t key, char *src)
260 : {
261 86 : int src_len = src ? strlen (src) : 0;
262 : gpgme_user_id_t uid;
263 : gpgme_key_sig_t sig;
264 :
265 86 : assert (key); /* XXX */
266 :
267 86 : uid = key->_last_uid;
268 86 : 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 86 : sig = malloc (sizeof (*sig) + 2 * src_len + 3);
274 86 : if (!sig)
275 0 : return NULL;
276 86 : memset (sig, 0, sizeof *sig);
277 :
278 86 : sig->keyid = sig->_keyid;
279 86 : sig->_keyid[16] = '\0';
280 86 : sig->uid = ((char *) sig) + sizeof (*sig);
281 :
282 86 : if (src)
283 : {
284 86 : char *dst = sig->uid;
285 86 : _gpgme_decode_c_string (src, &dst, src_len + 1);
286 86 : dst += strlen (dst) + 1;
287 86 : 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 86 : parse_user_id (sig->uid, &sig->name, &sig->email,
292 : &sig->comment, dst);
293 : }
294 : else
295 0 : sig->uid = '\0';
296 :
297 86 : if (!uid->signatures)
298 55 : uid->signatures = sig;
299 86 : if (uid->_last_keysig)
300 31 : uid->_last_keysig->next = sig;
301 86 : uid->_last_keysig = sig;
302 :
303 86 : return sig;
304 : }
305 :
306 :
307 : /* Acquire a reference to KEY. */
308 : void
309 105 : gpgme_key_ref (gpgme_key_t key)
310 : {
311 105 : LOCK (key_ref_lock);
312 105 : key->_refs++;
313 105 : UNLOCK (key_ref_lock);
314 105 : }
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 718 : gpgme_key_unref (gpgme_key_t key)
324 : {
325 : gpgme_user_id_t uid;
326 : gpgme_subkey_t subkey;
327 :
328 718 : if (!key)
329 0 : return;
330 :
331 718 : LOCK (key_ref_lock);
332 718 : assert (key->_refs > 0);
333 718 : if (--key->_refs)
334 : {
335 95 : UNLOCK (key_ref_lock);
336 95 : return;
337 : }
338 623 : UNLOCK (key_ref_lock);
339 :
340 623 : subkey = key->subkeys;
341 2497 : while (subkey)
342 : {
343 1251 : gpgme_subkey_t next = subkey->next;
344 1251 : free (subkey->fpr);
345 1251 : free (subkey->curve);
346 1251 : free (subkey->keygrip);
347 1251 : free (subkey->card_number);
348 1251 : free (subkey);
349 1251 : subkey = next;
350 : }
351 :
352 623 : uid = key->uids;
353 2100 : while (uid)
354 : {
355 854 : gpgme_user_id_t next_uid = uid->next;
356 854 : gpgme_key_sig_t keysig = uid->signatures;
357 854 : gpgme_tofu_info_t tofu = uid->tofu;
358 :
359 1736 : while (keysig)
360 : {
361 28 : gpgme_key_sig_t next_keysig = keysig->next;
362 28 : gpgme_sig_notation_t notation = keysig->notations;
363 :
364 56 : 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 28 : free (keysig);
373 28 : keysig = next_keysig;
374 : }
375 :
376 1736 : while (tofu)
377 : {
378 : /* NB: The ->next is currently not used but we are prepared
379 : * for it. */
380 28 : gpgme_tofu_info_t tofu_next = tofu->next;
381 :
382 28 : free (tofu->description);
383 28 : free (tofu);
384 28 : tofu = tofu_next;
385 : }
386 :
387 854 : free (uid->address);
388 854 : free (uid);
389 854 : uid = next_uid;
390 : }
391 :
392 623 : free (key->issuer_serial);
393 623 : free (key->issuer_name);
394 623 : free (key->chain_id);
395 623 : free (key->fpr);
396 :
397 623 : 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 : }
|