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