Line data Source code
1 : /* verify.c - Signature verification.
2 : Copyright (C) 2000 Werner Koch (dd9jn)
3 : Copyright (C) 2001, 2002, 2003, 2004, 2005 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 <errno.h>
28 : #include <assert.h>
29 : #include <limits.h>
30 :
31 : #include "gpgme.h"
32 : #include "debug.h"
33 : #include "util.h"
34 : #include "context.h"
35 : #include "ops.h"
36 :
37 :
38 : typedef struct
39 : {
40 : struct _gpgme_op_verify_result result;
41 :
42 : /* The error code from a FAILURE status line or 0. */
43 : gpg_error_t failure_code;
44 :
45 : gpgme_signature_t current_sig;
46 : int did_prepare_new_sig;
47 : int only_newsig_seen;
48 : int plaintext_seen;
49 : int conflict_user_seen;
50 : } *op_data_t;
51 :
52 :
53 : static void
54 76 : release_op_data (void *hook)
55 : {
56 76 : op_data_t opd = (op_data_t) hook;
57 76 : gpgme_signature_t sig = opd->result.signatures;
58 :
59 215 : while (sig)
60 : {
61 63 : gpgme_signature_t next = sig->next;
62 63 : gpgme_sig_notation_t notation = sig->notations;
63 :
64 195 : while (notation)
65 : {
66 69 : gpgme_sig_notation_t next_nota = notation->next;
67 :
68 69 : _gpgme_sig_notation_free (notation);
69 69 : notation = next_nota;
70 : }
71 :
72 63 : if (sig->fpr)
73 63 : free (sig->fpr);
74 63 : if (sig->pka_address)
75 0 : free (sig->pka_address);
76 63 : if (sig->key)
77 11 : gpgme_key_unref (sig->key);
78 63 : free (sig);
79 63 : sig = next;
80 : }
81 :
82 76 : if (opd->result.file_name)
83 23 : free (opd->result.file_name);
84 76 : }
85 :
86 :
87 : gpgme_verify_result_t
88 75 : gpgme_op_verify_result (gpgme_ctx_t ctx)
89 : {
90 : void *hook;
91 : op_data_t opd;
92 : gpgme_error_t err;
93 : gpgme_signature_t sig;
94 :
95 75 : TRACE_BEG (DEBUG_CTX, "gpgme_op_verify_result", ctx);
96 75 : err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
97 75 : opd = hook;
98 75 : if (err || !opd)
99 : {
100 0 : TRACE_SUC0 ("result=(null)");
101 0 : return NULL;
102 : }
103 :
104 : /* It is possible that we saw a new signature only followed by an
105 : ERROR line for that. In particular a missing X.509 key triggers
106 : this. In this case it is surprising that the summary field has
107 : not been updated. We fix it here by explicitly looking for this
108 : case. The real fix would be to have GPGME emit ERRSIG. */
109 142 : for (sig = opd->result.signatures; sig; sig = sig->next)
110 : {
111 67 : if (!sig->summary)
112 : {
113 32 : switch (gpg_err_code (sig->status))
114 : {
115 : case GPG_ERR_KEY_EXPIRED:
116 0 : sig->summary |= GPGME_SIGSUM_KEY_EXPIRED;
117 0 : break;
118 :
119 : case GPG_ERR_NO_PUBKEY:
120 0 : sig->summary |= GPGME_SIGSUM_KEY_MISSING;
121 0 : break;
122 :
123 : default:
124 32 : break;
125 : }
126 : }
127 : }
128 :
129 : /* Now for some tracing stuff. */
130 : if (_gpgme_debug_trace ())
131 : {
132 : int i;
133 :
134 142 : for (sig = opd->result.signatures, i = 0; sig; sig = sig->next, i++)
135 : {
136 67 : TRACE_LOG4 ("sig[%i] = fpr %s, summary 0x%x, status %s",
137 : i, sig->fpr, sig->summary, gpg_strerror (sig->status));
138 67 : TRACE_LOG6 ("sig[%i] = timestamps 0x%x/0x%x flags:%s%s%s",
139 : i, sig->timestamp, sig->exp_timestamp,
140 : sig->wrong_key_usage ? "wrong key usage" : "",
141 : sig->pka_trust == 1 ? "pka bad"
142 : : (sig->pka_trust == 2 ? "pka_okay" : "pka RFU"),
143 : sig->chain_model ? "chain model" : "");
144 67 : TRACE_LOG5 ("sig[%i] = validity 0x%x (%s), algos %s/%s",
145 : i, sig->validity, gpg_strerror (sig->validity_reason),
146 : gpgme_pubkey_algo_name (sig->pubkey_algo),
147 : gpgme_hash_algo_name (sig->hash_algo));
148 67 : if (sig->pka_address)
149 : {
150 0 : TRACE_LOG2 ("sig[%i] = PKA address %s", i, sig->pka_address);
151 : }
152 67 : if (sig->notations)
153 : {
154 25 : TRACE_LOG1 ("sig[%i] = has notations (not shown)", i);
155 : }
156 : }
157 : }
158 :
159 75 : TRACE_SUC1 ("result=%p", &opd->result);
160 75 : return &opd->result;
161 : }
162 :
163 :
164 : /* Build a summary vector from RESULT. */
165 : static void
166 68 : calc_sig_summary (gpgme_signature_t sig)
167 : {
168 68 : unsigned long sum = 0;
169 :
170 : /* Calculate the red/green flag. */
171 68 : if (sig->validity == GPGME_VALIDITY_FULL
172 40 : || sig->validity == GPGME_VALIDITY_ULTIMATE)
173 : {
174 56 : if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
175 0 : || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
176 0 : || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
177 28 : sum |= GPGME_SIGSUM_GREEN;
178 : }
179 40 : else if (sig->validity == GPGME_VALIDITY_NEVER)
180 : {
181 0 : if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
182 0 : || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
183 0 : || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
184 0 : sum |= GPGME_SIGSUM_RED;
185 : }
186 40 : else if (gpg_err_code (sig->status) == GPG_ERR_BAD_SIGNATURE)
187 6 : sum |= GPGME_SIGSUM_RED;
188 :
189 :
190 : /* FIXME: handle the case when key and message are expired. */
191 68 : switch (gpg_err_code (sig->status))
192 : {
193 : case GPG_ERR_SIG_EXPIRED:
194 0 : sum |= GPGME_SIGSUM_SIG_EXPIRED;
195 0 : break;
196 :
197 : case GPG_ERR_KEY_EXPIRED:
198 0 : sum |= GPGME_SIGSUM_KEY_EXPIRED;
199 0 : break;
200 :
201 : case GPG_ERR_NO_PUBKEY:
202 1 : sum |= GPGME_SIGSUM_KEY_MISSING;
203 1 : break;
204 :
205 : case GPG_ERR_CERT_REVOKED:
206 0 : sum |= GPGME_SIGSUM_KEY_REVOKED;
207 0 : break;
208 :
209 : case GPG_ERR_BAD_SIGNATURE:
210 : case GPG_ERR_NO_ERROR:
211 67 : break;
212 :
213 : default:
214 0 : sum |= GPGME_SIGSUM_SYS_ERROR;
215 0 : break;
216 : }
217 :
218 : /* Now look at the certain reason codes. */
219 68 : switch (gpg_err_code (sig->validity_reason))
220 : {
221 : case GPG_ERR_CRL_TOO_OLD:
222 0 : if (sig->validity == GPGME_VALIDITY_UNKNOWN)
223 0 : sum |= GPGME_SIGSUM_CRL_TOO_OLD;
224 0 : break;
225 :
226 : case GPG_ERR_CERT_REVOKED:
227 : /* Note that this is a second way to set this flag. It may also
228 : have been set due to a sig->status of STATUS_REVKEYSIG from
229 : parse_new_sig. */
230 0 : sum |= GPGME_SIGSUM_KEY_REVOKED;
231 0 : break;
232 :
233 : default:
234 68 : break;
235 : }
236 :
237 : /* Check other flags. */
238 68 : if (sig->wrong_key_usage)
239 0 : sum |= GPGME_SIGSUM_BAD_POLICY;
240 :
241 : /* Set the valid flag when the signature is unquestionable
242 : valid. (The test is identical to if(sum == GPGME_SIGSUM_GREEN)). */
243 68 : if ((sum & GPGME_SIGSUM_GREEN) && !(sum & ~GPGME_SIGSUM_GREEN))
244 28 : sum |= GPGME_SIGSUM_VALID;
245 :
246 68 : sig->summary = sum;
247 68 : }
248 :
249 :
250 : static gpgme_error_t
251 64 : prepare_new_sig (op_data_t opd)
252 : {
253 : gpgme_signature_t sig;
254 :
255 64 : if (opd->only_newsig_seen && opd->current_sig)
256 : {
257 : /* We have only seen the NEWSIG status and nothing else - we
258 : better skip this signature therefore and reuse it for the
259 : next possible signature. */
260 0 : sig = opd->current_sig;
261 0 : memset (sig, 0, sizeof *sig);
262 0 : assert (opd->result.signatures == sig);
263 : }
264 : else
265 : {
266 64 : sig = calloc (1, sizeof (*sig));
267 64 : if (!sig)
268 0 : return gpg_error_from_syserror ();
269 64 : if (!opd->result.signatures)
270 63 : opd->result.signatures = sig;
271 64 : if (opd->current_sig)
272 1 : opd->current_sig->next = sig;
273 64 : opd->current_sig = sig;
274 : }
275 64 : opd->did_prepare_new_sig = 1;
276 64 : opd->only_newsig_seen = 0;
277 64 : return 0;
278 : }
279 :
280 : static gpgme_error_t
281 67 : parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args,
282 : gpgme_protocol_t protocol)
283 : {
284 : gpgme_signature_t sig;
285 67 : char *end = strchr (args, ' ');
286 : char *tail;
287 67 : int got_fpr = 0;
288 :
289 67 : if (end)
290 : {
291 67 : *end = '\0';
292 67 : end++;
293 : }
294 :
295 67 : if (!opd->did_prepare_new_sig)
296 : {
297 : gpg_error_t err;
298 :
299 0 : err = prepare_new_sig (opd);
300 0 : if (err)
301 0 : return err;
302 : }
303 67 : assert (opd->did_prepare_new_sig);
304 67 : opd->did_prepare_new_sig = 0;
305 :
306 67 : assert (opd->current_sig);
307 67 : sig = opd->current_sig;
308 :
309 : /* FIXME: We should set the source of the state. */
310 67 : switch (code)
311 : {
312 : case GPGME_STATUS_GOODSIG:
313 60 : sig->status = gpg_error (GPG_ERR_NO_ERROR);
314 57 : break;
315 :
316 : case GPGME_STATUS_EXPSIG:
317 0 : sig->status = gpg_error (GPG_ERR_SIG_EXPIRED);
318 0 : break;
319 :
320 : case GPGME_STATUS_EXPKEYSIG:
321 0 : sig->status = gpg_error (GPG_ERR_KEY_EXPIRED);
322 0 : break;
323 :
324 : case GPGME_STATUS_BADSIG:
325 6 : sig->status = gpg_error (GPG_ERR_BAD_SIGNATURE);
326 6 : break;
327 :
328 : case GPGME_STATUS_REVKEYSIG:
329 0 : sig->status = gpg_error (GPG_ERR_CERT_REVOKED);
330 0 : break;
331 :
332 : case GPGME_STATUS_ERRSIG:
333 : /* Parse the pubkey algo. */
334 1 : if (!end)
335 0 : goto parse_err_sig_fail;
336 1 : gpg_err_set_errno (0);
337 1 : sig->pubkey_algo = _gpgme_map_pk_algo (strtol (end, &tail, 0), protocol);
338 1 : if (errno || end == tail || *tail != ' ')
339 : goto parse_err_sig_fail;
340 1 : end = tail;
341 3 : while (*end == ' ')
342 1 : end++;
343 :
344 : /* Parse the hash algo. */
345 1 : if (!*end)
346 0 : goto parse_err_sig_fail;
347 1 : gpg_err_set_errno (0);
348 1 : sig->hash_algo = strtol (end, &tail, 0);
349 1 : if (errno || end == tail || *tail != ' ')
350 : goto parse_err_sig_fail;
351 1 : end = tail;
352 3 : while (*end == ' ')
353 1 : end++;
354 :
355 : /* Skip the sig class. */
356 1 : end = strchr (end, ' ');
357 1 : if (!end)
358 0 : goto parse_err_sig_fail;
359 3 : while (*end == ' ')
360 1 : end++;
361 :
362 : /* Parse the timestamp. */
363 1 : sig->timestamp = _gpgme_parse_timestamp (end, &tail);
364 1 : if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' '))
365 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
366 1 : end = tail;
367 3 : while (*end == ' ')
368 1 : end++;
369 :
370 : /* Parse the return code. */
371 1 : if (!*end)
372 0 : goto parse_err_sig_fail;
373 :
374 1 : gpg_err_set_errno (0);
375 1 : sig->status = strtoul (end, &tail, 10);
376 1 : if (errno || end == tail || (*tail && *tail != ' '))
377 : goto parse_err_sig_fail;
378 1 : if (!*tail)
379 0 : goto parse_err_sig_ok;
380 1 : end = tail;
381 3 : while (*end == ' ')
382 1 : end++;
383 :
384 : /* Parse the new fingerprint (from the ISSUER_FPR subpacket). */
385 1 : if (!*end || (*end == '-' && (end[1] == ' ' || !end[1])))
386 : goto parse_err_sig_ok; /* Okay (just trailing spaces). */
387 1 : sig->fpr = strdup (end);
388 1 : if (!sig->fpr)
389 0 : return gpg_error_from_syserror ();
390 1 : got_fpr = 1;
391 1 : goto parse_err_sig_ok;
392 :
393 : parse_err_sig_fail:
394 0 : sig->status = gpg_error (GPG_ERR_GENERAL);
395 : parse_err_sig_ok:
396 1 : break;
397 :
398 : default:
399 0 : return gpg_error (GPG_ERR_GENERAL);
400 : }
401 :
402 64 : if (*args && !got_fpr)
403 : {
404 66 : sig->fpr = strdup (args);
405 66 : if (!sig->fpr)
406 0 : return gpg_error_from_syserror ();
407 : }
408 64 : return 0;
409 : }
410 :
411 :
412 : static gpgme_error_t
413 57 : parse_valid_sig (gpgme_signature_t sig, char *args, gpgme_protocol_t protocol)
414 : {
415 57 : char *end = strchr (args, ' ');
416 57 : if (end)
417 : {
418 58 : *end = '\0';
419 58 : end++;
420 : }
421 :
422 57 : if (!*args)
423 : /* We require at least the fingerprint. */
424 0 : return gpg_error (GPG_ERR_GENERAL);
425 :
426 57 : if (sig->fpr)
427 58 : free (sig->fpr);
428 57 : sig->fpr = strdup (args);
429 57 : if (!sig->fpr)
430 0 : return gpg_error_from_syserror ();
431 :
432 : /* Skip the creation date. */
433 57 : end = strchr (end, ' ');
434 57 : if (end)
435 : {
436 : char *tail;
437 :
438 58 : sig->timestamp = _gpgme_parse_timestamp (end, &tail);
439 57 : if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' '))
440 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
441 57 : end = tail;
442 :
443 57 : sig->exp_timestamp = _gpgme_parse_timestamp (end, &tail);
444 57 : if (sig->exp_timestamp == -1 || end == tail || (*tail && *tail != ' '))
445 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
446 58 : end = tail;
447 :
448 173 : while (*end == ' ')
449 57 : end++;
450 : /* Skip the signature version. */
451 58 : end = strchr (end, ' ');
452 58 : if (end)
453 : {
454 173 : while (*end == ' ')
455 57 : end++;
456 :
457 : /* Skip the reserved field. */
458 58 : end = strchr (end, ' ');
459 58 : if (end)
460 : {
461 : /* Parse the pubkey algo. */
462 58 : gpg_err_set_errno (0);
463 57 : sig->pubkey_algo = _gpgme_map_pk_algo (strtol (end, &tail, 0),
464 : protocol);
465 57 : if (errno || end == tail || *tail != ' ')
466 1 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
467 57 : end = tail;
468 :
469 172 : while (*end == ' ')
470 58 : end++;
471 :
472 57 : if (*end)
473 : {
474 : /* Parse the hash algo. */
475 :
476 57 : gpg_err_set_errno (0);
477 57 : sig->hash_algo = strtol (end, &tail, 0);
478 57 : if (errno || end == tail || *tail != ' ')
479 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
480 58 : end = tail;
481 : }
482 : }
483 : }
484 : }
485 57 : return 0;
486 : }
487 :
488 :
489 : static gpgme_error_t
490 180 : parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
491 : {
492 : gpgme_error_t err;
493 180 : gpgme_sig_notation_t *lastp = &sig->notations;
494 180 : gpgme_sig_notation_t notation = sig->notations;
495 : char *p;
496 :
497 180 : if (code == GPGME_STATUS_NOTATION_NAME || code == GPGME_STATUS_POLICY_URL)
498 : {
499 69 : p = strchr (args, ' ');
500 69 : if (p)
501 0 : *p = '\0';
502 :
503 : /* FIXME: We could keep a pointer to the last notation in the list. */
504 207 : while (notation && notation->value)
505 : {
506 69 : lastp = ¬ation->next;
507 69 : notation = notation->next;
508 : }
509 :
510 69 : if (notation)
511 : /* There is another notation name without data for the
512 : previous one. The crypto backend misbehaves. */
513 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
514 :
515 69 : err = _gpgme_sig_notation_create (¬ation, NULL, 0, NULL, 0, 0);
516 69 : if (err)
517 0 : return err;
518 :
519 69 : if (code == GPGME_STATUS_NOTATION_NAME)
520 : {
521 46 : err = _gpgme_decode_percent_string (args, ¬ation->name, 0, 0);
522 47 : if (err)
523 : {
524 0 : _gpgme_sig_notation_free (notation);
525 0 : return err;
526 : }
527 :
528 47 : notation->name_len = strlen (notation->name);
529 :
530 : /* Set default flags for use with older gpg versions which
531 : * do not emit a NOTATIONS_FLAG line. */
532 47 : notation->flags = GPGME_SIG_NOTATION_HUMAN_READABLE;
533 47 : notation->human_readable = 1;
534 : }
535 : else
536 : {
537 : /* This is a policy URL. */
538 :
539 23 : err = _gpgme_decode_percent_string (args, ¬ation->value, 0, 0);
540 23 : if (err)
541 : {
542 0 : _gpgme_sig_notation_free (notation);
543 0 : return err;
544 : }
545 :
546 23 : notation->value_len = strlen (notation->value);
547 : }
548 70 : *lastp = notation;
549 : }
550 111 : else if (code == GPGME_STATUS_NOTATION_FLAGS)
551 : {
552 : char *field[2];
553 :
554 161 : while (notation && notation->next)
555 : {
556 69 : lastp = ¬ation->next;
557 69 : notation = notation->next;
558 : }
559 :
560 46 : if (!notation || !notation->name)
561 : { /* There are notation flags without a previous notation name.
562 : * The crypto backend misbehaves. */
563 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
564 : }
565 46 : if (_gpgme_split_fields (args, field, DIM (field)) < 2)
566 : { /* Required args missing. */
567 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
568 : }
569 45 : notation->flags = 0;
570 45 : if (atoi (field[0]))
571 : {
572 4 : notation->flags |= GPGME_SIG_NOTATION_CRITICAL;
573 4 : notation->critical = 1;
574 : }
575 46 : if (atoi (field[1]))
576 : {
577 45 : notation->flags |= GPGME_SIG_NOTATION_HUMAN_READABLE;
578 45 : notation->human_readable = 1;
579 : }
580 : }
581 65 : else if (code == GPGME_STATUS_NOTATION_DATA)
582 : {
583 65 : int len = strlen (args) + 1;
584 : char *dest;
585 :
586 : /* FIXME: We could keep a pointer to the last notation in the list. */
587 237 : while (notation && notation->next)
588 : {
589 107 : lastp = ¬ation->next;
590 107 : notation = notation->next;
591 : }
592 :
593 65 : if (!notation || !notation->name)
594 : /* There is notation data without a previous notation
595 : name. The crypto backend misbehaves. */
596 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
597 :
598 65 : if (!notation->value)
599 : {
600 46 : dest = notation->value = malloc (len);
601 46 : if (!dest)
602 0 : return gpg_error_from_syserror ();
603 : }
604 : else
605 : {
606 19 : int cur_len = strlen (notation->value);
607 19 : dest = realloc (notation->value, len + strlen (notation->value));
608 19 : if (!dest)
609 0 : return gpg_error_from_syserror ();
610 19 : notation->value = dest;
611 19 : dest += cur_len;
612 : }
613 :
614 65 : err = _gpgme_decode_percent_string (args, &dest, len, 0);
615 65 : if (err)
616 0 : return err;
617 :
618 65 : notation->value_len += strlen (dest);
619 : }
620 : else
621 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
622 180 : return 0;
623 : }
624 :
625 :
626 : static gpgme_error_t
627 58 : parse_trust (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
628 : {
629 58 : char *end = strchr (args, ' ');
630 :
631 58 : if (end)
632 58 : *end = '\0';
633 :
634 58 : switch (code)
635 : {
636 : case GPGME_STATUS_TRUST_UNDEFINED:
637 : default:
638 20 : sig->validity = GPGME_VALIDITY_UNKNOWN;
639 20 : break;
640 :
641 : case GPGME_STATUS_TRUST_NEVER:
642 0 : sig->validity = GPGME_VALIDITY_NEVER;
643 0 : break;
644 :
645 : case GPGME_STATUS_TRUST_MARGINAL:
646 10 : sig->validity = GPGME_VALIDITY_MARGINAL;
647 10 : break;
648 :
649 : case GPGME_STATUS_TRUST_FULLY:
650 : case GPGME_STATUS_TRUST_ULTIMATE:
651 28 : sig->validity = GPGME_VALIDITY_FULL;
652 28 : break;
653 : }
654 :
655 58 : sig->validity_reason = 0;
656 58 : sig->chain_model = 0;
657 58 : if (*args)
658 : {
659 57 : sig->validity_reason = atoi (args);
660 172 : while (*args && *args != ' ')
661 58 : args++;
662 57 : if (*args)
663 : {
664 0 : while (*args == ' ')
665 0 : args++;
666 0 : if (!strncmp (args, "chain", 2) && (args[2] == ' ' || !args[2]))
667 0 : sig->chain_model = 1;
668 : }
669 : }
670 :
671 58 : return 0;
672 : }
673 :
674 :
675 : /* Parse a TOFU_USER line and put the info into SIG. */
676 : static gpgme_error_t
677 18 : parse_tofu_user (gpgme_signature_t sig, char *args, gpgme_protocol_t protocol)
678 : {
679 : gpg_error_t err;
680 : char *tail;
681 : gpgme_user_id_t uid;
682 : gpgme_tofu_info_t ti;
683 18 : char *fpr = NULL;
684 18 : char *address = NULL;
685 :
686 18 : tail = strchr (args, ' ');
687 18 : if (!tail || tail == args)
688 : {
689 0 : err = trace_gpg_error (GPG_ERR_INV_ENGINE); /* No fingerprint. */
690 0 : goto leave;
691 : }
692 18 : *tail++ = 0;
693 :
694 18 : fpr = strdup (args);
695 18 : if (!fpr)
696 : {
697 0 : err = gpg_error_from_syserror ();
698 0 : goto leave;
699 : }
700 :
701 18 : if (sig->key && sig->key->fpr && strcmp (sig->key->fpr, fpr))
702 : {
703 : /* GnuPG since 2.1.17 emits multiple TOFU_USER lines with
704 : different fingerprints in case of conflicts for a signature. */
705 1 : err = gpg_error (GPG_ERR_DUP_VALUE);
706 1 : goto leave;
707 : }
708 :
709 17 : args = tail;
710 17 : tail = strchr (args, ' ');
711 17 : if (tail == args)
712 : {
713 0 : err = trace_gpg_error (GPG_ERR_INV_ENGINE); /* No addr-spec. */
714 0 : goto leave;
715 : }
716 17 : if (tail)
717 0 : *tail = 0;
718 :
719 17 : err = _gpgme_decode_percent_string (args, &address, 0, 0);
720 17 : if (err)
721 0 : goto leave;
722 :
723 17 : if (!sig->key)
724 : {
725 11 : err = _gpgme_key_new (&sig->key);
726 11 : if (err)
727 0 : goto leave;
728 11 : sig->key->fpr = fpr;
729 11 : sig->key->protocol = protocol;
730 11 : fpr = NULL;
731 : }
732 6 : else if (!sig->key->fpr)
733 : {
734 0 : err = trace_gpg_error (GPG_ERR_INTERNAL);
735 0 : goto leave;
736 : }
737 :
738 17 : err = _gpgme_key_append_name (sig->key, address, 0);
739 17 : if (err)
740 0 : goto leave;
741 :
742 17 : uid = sig->key->_last_uid;
743 17 : assert (uid);
744 :
745 17 : ti = calloc (1, sizeof *ti);
746 17 : if (!ti)
747 : {
748 0 : err = gpg_error_from_syserror ();
749 0 : goto leave;
750 : }
751 17 : uid->tofu = ti;
752 :
753 :
754 : leave:
755 18 : free (fpr);
756 18 : free (address);
757 18 : return err;
758 : }
759 :
760 :
761 : /* Parse a TOFU_STATS line and store it in the last tofu info of SIG.
762 : *
763 : * TOFU_STATS <validity> <sign-count> <encr-count> \
764 : * [<policy> [<tm1> <tm2> <tm3> <tm4>]]
765 : */
766 : static gpgme_error_t
767 17 : parse_tofu_stats (gpgme_signature_t sig, char *args)
768 : {
769 : gpgme_error_t err;
770 : gpgme_tofu_info_t ti;
771 : char *field[8];
772 : int nfields;
773 : unsigned long uval;
774 :
775 17 : if (!sig->key || !sig->key->_last_uid || !(ti = sig->key->_last_uid->tofu))
776 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen. */
777 17 : if (ti->signfirst || ti->signcount || ti->validity || ti->policy)
778 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already set. */
779 :
780 17 : nfields = _gpgme_split_fields (args, field, DIM (field));
781 17 : if (nfields < 3)
782 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Required args missing. */
783 :
784 : /* Note that we allow a value of up to 7 which is what we can store
785 : * in the ti->validity. */
786 17 : err = _gpgme_strtoul_field (field[0], &uval);
787 17 : if (err || uval > 7)
788 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
789 17 : ti->validity = uval;
790 :
791 : /* Parse the sign-count. */
792 17 : err = _gpgme_strtoul_field (field[1], &uval);
793 17 : if (err)
794 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
795 17 : if (uval > USHRT_MAX)
796 0 : uval = USHRT_MAX;
797 17 : ti->signcount = uval;
798 :
799 : /* Parse the encr-count. */
800 17 : err = _gpgme_strtoul_field (field[2], &uval);
801 17 : if (err)
802 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
803 17 : if (uval > USHRT_MAX)
804 0 : uval = USHRT_MAX;
805 17 : ti->encrcount = uval;
806 :
807 17 : if (nfields == 3)
808 0 : return 0; /* All mandatory fields parsed. */
809 :
810 : /* Parse the policy. */
811 17 : if (!strcmp (field[3], "none"))
812 0 : ti->policy = GPGME_TOFU_POLICY_NONE;
813 17 : else if (!strcmp (field[3], "auto"))
814 16 : ti->policy = GPGME_TOFU_POLICY_AUTO;
815 1 : else if (!strcmp (field[3], "good"))
816 0 : ti->policy = GPGME_TOFU_POLICY_GOOD;
817 1 : else if (!strcmp (field[3], "bad"))
818 0 : ti->policy = GPGME_TOFU_POLICY_BAD;
819 1 : else if (!strcmp (field[3], "ask"))
820 1 : ti->policy = GPGME_TOFU_POLICY_ASK;
821 : else /* "unknown" and invalid policy strings. */
822 0 : ti->policy = GPGME_TOFU_POLICY_UNKNOWN;
823 :
824 17 : if (nfields == 4)
825 0 : return 0; /* No more optional fields. */
826 :
827 : /* Parse first and last seen timestamps (none or both are required). */
828 17 : if (nfields < 6)
829 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE); /* "tm2" missing. */
830 17 : err = _gpgme_strtoul_field (field[4], &uval);
831 17 : if (err)
832 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
833 17 : ti->signfirst = uval;
834 17 : err = _gpgme_strtoul_field (field[5], &uval);
835 17 : if (err)
836 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
837 17 : ti->signlast = uval;
838 17 : if (nfields > 7)
839 : {
840 : /* This condition is only to allow for gpg 2.1.15 - can
841 : * eventually be removed. */
842 17 : err = _gpgme_strtoul_field (field[6], &uval);
843 17 : if (err)
844 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
845 17 : ti->encrfirst = uval;
846 17 : err = _gpgme_strtoul_field (field[7], &uval);
847 17 : if (err)
848 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
849 17 : ti->encrlast = uval;
850 : }
851 :
852 17 : return 0;
853 : }
854 :
855 :
856 : /* Parse a TOFU_STATS_LONG line and store it in the last tofu info of SIG. */
857 : static gpgme_error_t
858 16 : parse_tofu_stats_long (gpgme_signature_t sig, char *args, int raw)
859 : {
860 : gpgme_error_t err;
861 : gpgme_tofu_info_t ti;
862 : char *p;
863 :
864 16 : if (!sig->key || !sig->key->_last_uid || !(ti = sig->key->_last_uid->tofu))
865 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen. */
866 16 : if (ti->description)
867 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already set. */
868 :
869 16 : err = _gpgme_decode_percent_string (args, &ti->description, 0, 0);
870 16 : if (err)
871 0 : return err;
872 :
873 : /* Remove the non-breaking spaces. */
874 16 : if (!raw)
875 : {
876 1373 : for (p = ti->description; *p; p++)
877 1357 : if (*p == '~')
878 32 : *p = ' ';
879 : }
880 16 : return 0;
881 : }
882 :
883 :
884 : /* Parse an error status line and if SET_STATUS is true update the
885 : result status as appropriate. With SET_STATUS being false, only
886 : check for an error. */
887 : static gpgme_error_t
888 5 : parse_error (gpgme_signature_t sig, char *args, int set_status)
889 : {
890 : gpgme_error_t err;
891 5 : char *where = strchr (args, ' ');
892 : char *which;
893 :
894 5 : if (where)
895 : {
896 5 : *where = '\0';
897 5 : which = where + 1;
898 :
899 5 : where = strchr (which, ' ');
900 5 : if (where)
901 0 : *where = '\0';
902 :
903 5 : where = args;
904 : }
905 : else
906 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
907 :
908 5 : err = atoi (which);
909 :
910 5 : if (!strcmp (where, "proc_pkt.plaintext")
911 5 : && gpg_err_code (err) == GPG_ERR_BAD_DATA)
912 : {
913 : /* This indicates a double plaintext. The only solid way to
914 : handle this is by failing the oepration. */
915 5 : return gpg_error (GPG_ERR_BAD_DATA);
916 : }
917 0 : else if (!set_status)
918 : ;
919 0 : else if (!strcmp (where, "verify.findkey"))
920 0 : sig->status = err;
921 0 : else if (!strcmp (where, "verify.keyusage")
922 0 : && gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
923 0 : sig->wrong_key_usage = 1;
924 :
925 0 : return 0;
926 : }
927 :
928 :
929 : gpgme_error_t
930 1238 : _gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
931 : {
932 1238 : gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
933 : gpgme_error_t err;
934 : void *hook;
935 : op_data_t opd;
936 : gpgme_signature_t sig;
937 : char *end;
938 :
939 1238 : err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
940 1239 : opd = hook;
941 1239 : if (err)
942 0 : return err;
943 :
944 1239 : sig = opd->current_sig;
945 :
946 1239 : switch (code)
947 : {
948 : case GPGME_STATUS_NEWSIG:
949 63 : if (sig)
950 1 : calc_sig_summary (sig);
951 63 : err = prepare_new_sig (opd);
952 64 : opd->only_newsig_seen = 1;
953 64 : opd->conflict_user_seen = 0;
954 64 : return err;
955 :
956 : case GPGME_STATUS_GOODSIG:
957 : case GPGME_STATUS_EXPSIG:
958 : case GPGME_STATUS_EXPKEYSIG:
959 : case GPGME_STATUS_BADSIG:
960 : case GPGME_STATUS_ERRSIG:
961 : case GPGME_STATUS_REVKEYSIG:
962 67 : if (sig && !opd->did_prepare_new_sig)
963 0 : calc_sig_summary (sig);
964 67 : opd->only_newsig_seen = 0;
965 67 : return parse_new_sig (opd, code, args, ctx->protocol);
966 :
967 : case GPGME_STATUS_VALIDSIG:
968 58 : opd->only_newsig_seen = 0;
969 58 : return sig ? parse_valid_sig (sig, args, ctx->protocol)
970 116 : : trace_gpg_error (GPG_ERR_INV_ENGINE);
971 :
972 : case GPGME_STATUS_NODATA:
973 3 : opd->only_newsig_seen = 0;
974 3 : if (!sig)
975 3 : return gpg_error (GPG_ERR_NO_DATA);
976 0 : sig->status = gpg_error (GPG_ERR_NO_DATA);
977 0 : break;
978 :
979 : case GPGME_STATUS_UNEXPECTED:
980 0 : opd->only_newsig_seen = 0;
981 0 : if (!sig)
982 0 : return gpg_error (GPG_ERR_GENERAL);
983 0 : sig->status = gpg_error (GPG_ERR_NO_DATA);
984 0 : break;
985 :
986 : case GPGME_STATUS_NOTATION_NAME:
987 : case GPGME_STATUS_NOTATION_FLAGS:
988 : case GPGME_STATUS_NOTATION_DATA:
989 : case GPGME_STATUS_POLICY_URL:
990 180 : opd->only_newsig_seen = 0;
991 : return sig ? parse_notation (sig, code, args)
992 180 : : trace_gpg_error (GPG_ERR_INV_ENGINE);
993 :
994 : case GPGME_STATUS_TRUST_UNDEFINED:
995 : case GPGME_STATUS_TRUST_NEVER:
996 : case GPGME_STATUS_TRUST_MARGINAL:
997 : case GPGME_STATUS_TRUST_FULLY:
998 : case GPGME_STATUS_TRUST_ULTIMATE:
999 58 : opd->only_newsig_seen = 0;
1000 : return sig ? parse_trust (sig, code, args)
1001 58 : : trace_gpg_error (GPG_ERR_INV_ENGINE);
1002 :
1003 : case GPGME_STATUS_PKA_TRUST_BAD:
1004 : case GPGME_STATUS_PKA_TRUST_GOOD:
1005 0 : opd->only_newsig_seen = 0;
1006 : /* Check that we only get one of these status codes per
1007 : signature; if not the crypto backend misbehaves. */
1008 0 : if (!sig || sig->pka_trust || sig->pka_address)
1009 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
1010 0 : sig->pka_trust = code == GPGME_STATUS_PKA_TRUST_GOOD? 2 : 1;
1011 0 : end = strchr (args, ' ');
1012 0 : if (end)
1013 0 : *end = 0;
1014 0 : sig->pka_address = strdup (args);
1015 0 : break;
1016 :
1017 : case GPGME_STATUS_TOFU_USER:
1018 18 : opd->only_newsig_seen = 0;
1019 18 : if (!sig)
1020 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
1021 18 : err = parse_tofu_user (sig, args, ctx->protocol);
1022 : /* gpg emits TOFU User lines for each conflicting key.
1023 : * GPGME does not expose this to have a clean API and
1024 : * a GPGME user can do a keylisting with the address
1025 : * normalisation.
1026 : * So when a duplicated TOFU_USER line is encountered
1027 : * we ignore the conflicting tofu stats emitted afterwards.
1028 : */
1029 18 : if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
1030 : {
1031 1 : opd->conflict_user_seen = 1;
1032 1 : break;
1033 : }
1034 17 : opd->conflict_user_seen = 0;
1035 17 : return trace_gpg_error (err);
1036 :
1037 : case GPGME_STATUS_TOFU_STATS:
1038 18 : opd->only_newsig_seen = 0;
1039 18 : if (opd->conflict_user_seen)
1040 1 : break;
1041 : return sig ? parse_tofu_stats (sig, args)
1042 17 : /* */ : trace_gpg_error (GPG_ERR_INV_ENGINE);
1043 :
1044 : case GPGME_STATUS_TOFU_STATS_LONG:
1045 16 : opd->only_newsig_seen = 0;
1046 16 : if (opd->conflict_user_seen)
1047 0 : break;
1048 16 : return sig ? parse_tofu_stats_long (sig, args, ctx->raw_description)
1049 32 : /* */ : trace_gpg_error (GPG_ERR_INV_ENGINE);
1050 :
1051 : case GPGME_STATUS_ERROR:
1052 5 : opd->only_newsig_seen = 0;
1053 : /* Some error stati are informational, so we don't return an
1054 : error code if we are not ready to process this status. */
1055 5 : return parse_error (sig, args, !!sig );
1056 :
1057 : case GPGME_STATUS_FAILURE:
1058 5 : opd->failure_code = _gpgme_parse_failure (args);
1059 5 : break;
1060 :
1061 : case GPGME_STATUS_EOF:
1062 74 : if (sig && !opd->did_prepare_new_sig)
1063 67 : calc_sig_summary (sig);
1064 74 : if (opd->only_newsig_seen && sig)
1065 : {
1066 : gpgme_signature_t sig2;
1067 : /* The last signature has no valid information - remove it
1068 : from the list. */
1069 0 : assert (!sig->next);
1070 0 : if (sig == opd->result.signatures)
1071 0 : opd->result.signatures = NULL;
1072 : else
1073 : {
1074 0 : for (sig2 = opd->result.signatures; sig2; sig2 = sig2->next)
1075 0 : if (sig2->next == sig)
1076 : {
1077 0 : sig2->next = NULL;
1078 0 : break;
1079 : }
1080 : }
1081 : /* Note that there is no need to release the members of SIG
1082 : because we won't be here if they have been set. */
1083 0 : free (sig);
1084 0 : opd->current_sig = NULL;
1085 : }
1086 74 : opd->only_newsig_seen = 0;
1087 74 : if (opd->failure_code)
1088 0 : return opd->failure_code;
1089 74 : break;
1090 :
1091 : case GPGME_STATUS_PLAINTEXT:
1092 49 : if (++opd->plaintext_seen > 1)
1093 0 : return gpg_error (GPG_ERR_BAD_DATA);
1094 : {
1095 49 : int mime = 0;
1096 49 : err = _gpgme_parse_plaintext (args, &opd->result.file_name, &mime);
1097 49 : if (err)
1098 0 : return err;
1099 49 : opd->result.is_mime = !!mime;
1100 : }
1101 49 : break;
1102 :
1103 : case GPGME_STATUS_VERIFICATION_COMPLIANCE_MODE:
1104 4 : PARSE_COMPLIANCE_FLAGS (args, opd->current_sig);
1105 4 : break;
1106 :
1107 : default:
1108 621 : break;
1109 : }
1110 755 : return 0;
1111 : }
1112 :
1113 :
1114 : static gpgme_error_t
1115 838 : verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
1116 : {
1117 : gpgme_error_t err;
1118 :
1119 838 : err = _gpgme_progress_status_handler (priv, code, args);
1120 835 : if (!err)
1121 835 : err = _gpgme_verify_status_handler (priv, code, args);
1122 831 : return err;
1123 : }
1124 :
1125 :
1126 : gpgme_error_t
1127 80 : _gpgme_op_verify_init_result (gpgme_ctx_t ctx)
1128 : {
1129 : void *hook;
1130 : op_data_t opd;
1131 :
1132 80 : return _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook,
1133 : sizeof (*opd), release_op_data);
1134 : }
1135 :
1136 :
1137 : static gpgme_error_t
1138 61 : verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t sig,
1139 : gpgme_data_t signed_text, gpgme_data_t plaintext)
1140 : {
1141 : gpgme_error_t err;
1142 :
1143 61 : err = _gpgme_op_reset (ctx, synchronous);
1144 61 : if (err)
1145 0 : return err;
1146 :
1147 61 : err = _gpgme_op_verify_init_result (ctx);
1148 61 : if (err)
1149 0 : return err;
1150 :
1151 61 : _gpgme_engine_set_status_handler (ctx->engine, verify_status_handler, ctx);
1152 :
1153 60 : if (!sig)
1154 0 : return gpg_error (GPG_ERR_NO_DATA);
1155 :
1156 60 : return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext,
1157 : ctx);
1158 : }
1159 :
1160 :
1161 : /* Decrypt ciphertext CIPHER and make a signature verification within
1162 : CTX and store the resulting plaintext in PLAIN. */
1163 : gpgme_error_t
1164 3 : gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig,
1165 : gpgme_data_t signed_text, gpgme_data_t plaintext)
1166 : {
1167 : gpg_error_t err;
1168 3 : TRACE_BEG3 (DEBUG_CTX, "gpgme_op_verify_start", ctx,
1169 : "sig=%p, signed_text=%p, plaintext=%p",
1170 : sig, signed_text, plaintext);
1171 :
1172 3 : if (!ctx)
1173 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
1174 :
1175 3 : err = verify_start (ctx, 0, sig, signed_text, plaintext);
1176 3 : return TRACE_ERR (err);
1177 : }
1178 :
1179 :
1180 : /* Decrypt ciphertext CIPHER and make a signature verification within
1181 : CTX and store the resulting plaintext in PLAIN. */
1182 : gpgme_error_t
1183 57 : gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text,
1184 : gpgme_data_t plaintext)
1185 : {
1186 : gpgme_error_t err;
1187 :
1188 57 : TRACE_BEG3 (DEBUG_CTX, "gpgme_op_verify", ctx,
1189 : "sig=%p, signed_text=%p, plaintext=%p",
1190 : sig, signed_text, plaintext);
1191 :
1192 58 : if (!ctx)
1193 0 : return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
1194 :
1195 58 : err = verify_start (ctx, 1, sig, signed_text, plaintext);
1196 58 : if (!err)
1197 58 : err = _gpgme_wait_one (ctx);
1198 59 : return TRACE_ERR (err);
1199 : }
1200 :
1201 :
1202 : /* Compatibility interfaces. */
1203 :
1204 : /* Get the key used to create signature IDX in CTX and return it in
1205 : R_KEY. */
1206 : gpgme_error_t
1207 0 : gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key)
1208 : {
1209 : gpgme_verify_result_t result;
1210 : gpgme_signature_t sig;
1211 :
1212 0 : if (!ctx)
1213 0 : return gpg_error (GPG_ERR_INV_VALUE);
1214 :
1215 0 : result = gpgme_op_verify_result (ctx);
1216 0 : sig = result->signatures;
1217 :
1218 0 : while (sig && idx)
1219 : {
1220 0 : sig = sig->next;
1221 0 : idx--;
1222 : }
1223 0 : if (!sig || idx)
1224 0 : return gpg_error (GPG_ERR_EOF);
1225 :
1226 0 : return gpgme_get_key (ctx, sig->fpr, r_key, 0);
1227 : }
1228 :
1229 :
1230 : /* Retrieve the signature status of signature IDX in CTX after a
1231 : successful verify operation in R_STAT (if non-null). The creation
1232 : time stamp of the signature is returned in R_CREATED (if non-null).
1233 : The function returns a string containing the fingerprint. */
1234 : const char *
1235 0 : gpgme_get_sig_status (gpgme_ctx_t ctx, int idx,
1236 : _gpgme_sig_stat_t *r_stat, time_t *r_created)
1237 : {
1238 : gpgme_verify_result_t result;
1239 : gpgme_signature_t sig;
1240 :
1241 0 : result = gpgme_op_verify_result (ctx);
1242 0 : sig = result->signatures;
1243 :
1244 0 : while (sig && idx)
1245 : {
1246 0 : sig = sig->next;
1247 0 : idx--;
1248 : }
1249 0 : if (!sig || idx)
1250 0 : return NULL;
1251 :
1252 0 : if (r_stat)
1253 : {
1254 0 : switch (gpg_err_code (sig->status))
1255 : {
1256 : case GPG_ERR_NO_ERROR:
1257 0 : *r_stat = GPGME_SIG_STAT_GOOD;
1258 0 : break;
1259 :
1260 : case GPG_ERR_BAD_SIGNATURE:
1261 0 : *r_stat = GPGME_SIG_STAT_BAD;
1262 0 : break;
1263 :
1264 : case GPG_ERR_NO_PUBKEY:
1265 0 : *r_stat = GPGME_SIG_STAT_NOKEY;
1266 0 : break;
1267 :
1268 : case GPG_ERR_NO_DATA:
1269 0 : *r_stat = GPGME_SIG_STAT_NOSIG;
1270 0 : break;
1271 :
1272 : case GPG_ERR_SIG_EXPIRED:
1273 0 : *r_stat = GPGME_SIG_STAT_GOOD_EXP;
1274 0 : break;
1275 :
1276 : case GPG_ERR_KEY_EXPIRED:
1277 0 : *r_stat = GPGME_SIG_STAT_GOOD_EXPKEY;
1278 0 : break;
1279 :
1280 : default:
1281 0 : *r_stat = GPGME_SIG_STAT_ERROR;
1282 0 : break;
1283 : }
1284 : }
1285 0 : if (r_created)
1286 0 : *r_created = sig->timestamp;
1287 0 : return sig->fpr;
1288 : }
1289 :
1290 :
1291 : /* Retrieve certain attributes of a signature. IDX is the index
1292 : number of the signature after a successful verify operation. WHAT
1293 : is an attribute where GPGME_ATTR_EXPIRE is probably the most useful
1294 : one. WHATIDX is to be passed as 0 for most attributes . */
1295 : unsigned long
1296 0 : gpgme_get_sig_ulong_attr (gpgme_ctx_t ctx, int idx,
1297 : _gpgme_attr_t what, int whatidx)
1298 : {
1299 : gpgme_verify_result_t result;
1300 : gpgme_signature_t sig;
1301 :
1302 : (void)whatidx;
1303 :
1304 0 : result = gpgme_op_verify_result (ctx);
1305 0 : sig = result->signatures;
1306 :
1307 0 : while (sig && idx)
1308 : {
1309 0 : sig = sig->next;
1310 0 : idx--;
1311 : }
1312 0 : if (!sig || idx)
1313 0 : return 0;
1314 :
1315 0 : switch (what)
1316 : {
1317 : case GPGME_ATTR_CREATED:
1318 0 : return sig->timestamp;
1319 :
1320 : case GPGME_ATTR_EXPIRE:
1321 0 : return sig->exp_timestamp;
1322 :
1323 : case GPGME_ATTR_VALIDITY:
1324 0 : return (unsigned long) sig->validity;
1325 :
1326 : case GPGME_ATTR_SIG_STATUS:
1327 0 : switch (gpg_err_code (sig->status))
1328 : {
1329 : case GPG_ERR_NO_ERROR:
1330 0 : return GPGME_SIG_STAT_GOOD;
1331 :
1332 : case GPG_ERR_BAD_SIGNATURE:
1333 0 : return GPGME_SIG_STAT_BAD;
1334 :
1335 : case GPG_ERR_NO_PUBKEY:
1336 0 : return GPGME_SIG_STAT_NOKEY;
1337 :
1338 : case GPG_ERR_NO_DATA:
1339 0 : return GPGME_SIG_STAT_NOSIG;
1340 :
1341 : case GPG_ERR_SIG_EXPIRED:
1342 0 : return GPGME_SIG_STAT_GOOD_EXP;
1343 :
1344 : case GPG_ERR_KEY_EXPIRED:
1345 0 : return GPGME_SIG_STAT_GOOD_EXPKEY;
1346 :
1347 : default:
1348 0 : return GPGME_SIG_STAT_ERROR;
1349 : }
1350 :
1351 : case GPGME_ATTR_SIG_SUMMARY:
1352 0 : return sig->summary;
1353 :
1354 : default:
1355 0 : break;
1356 : }
1357 0 : return 0;
1358 : }
1359 :
1360 :
1361 : const char *
1362 0 : gpgme_get_sig_string_attr (gpgme_ctx_t ctx, int idx,
1363 : _gpgme_attr_t what, int whatidx)
1364 : {
1365 : gpgme_verify_result_t result;
1366 : gpgme_signature_t sig;
1367 :
1368 0 : result = gpgme_op_verify_result (ctx);
1369 0 : sig = result->signatures;
1370 :
1371 0 : while (sig && idx)
1372 : {
1373 0 : sig = sig->next;
1374 0 : idx--;
1375 : }
1376 0 : if (!sig || idx)
1377 0 : return NULL;
1378 :
1379 0 : switch (what)
1380 : {
1381 : case GPGME_ATTR_FPR:
1382 0 : return sig->fpr;
1383 :
1384 : case GPGME_ATTR_ERRTOK:
1385 0 : if (whatidx == 1)
1386 0 : return sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
1387 : else
1388 0 : return "";
1389 : default:
1390 0 : break;
1391 : }
1392 :
1393 0 : return NULL;
1394 : }
|