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, see <https://gnu.org/licenses/>.
19  * SPDX-License-Identifier: LGPL-2.1-or-later
20  */
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
release_op_data(void * hook)54 release_op_data (void *hook)
55 {
56   op_data_t opd = (op_data_t) hook;
57   gpgme_signature_t sig = opd->result.signatures;
58 
59   while (sig)
60     {
61       gpgme_signature_t next = sig->next;
62       gpgme_sig_notation_t notation = sig->notations;
63 
64       while (notation)
65 	{
66 	  gpgme_sig_notation_t next_nota = notation->next;
67 
68 	  _gpgme_sig_notation_free (notation);
69 	  notation = next_nota;
70 	}
71 
72       if (sig->fpr)
73 	free (sig->fpr);
74       if (sig->pka_address)
75 	free (sig->pka_address);
76       if (sig->key)
77         gpgme_key_unref (sig->key);
78       free (sig);
79       sig = next;
80     }
81 
82   if (opd->result.file_name)
83     free (opd->result.file_name);
84 }
85 
86 
87 gpgme_verify_result_t
gpgme_op_verify_result(gpgme_ctx_t ctx)88 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   TRACE_BEG (DEBUG_CTX, "gpgme_op_verify_result", ctx, "");
96   err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
97   opd = hook;
98   if (err || !opd)
99     {
100       TRACE_SUC ("result=(null)");
101       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   for (sig = opd->result.signatures; sig; sig = sig->next)
110     {
111       if (!sig->summary)
112         {
113           switch (gpg_err_code (sig->status))
114             {
115             case GPG_ERR_KEY_EXPIRED:
116               sig->summary |= GPGME_SIGSUM_KEY_EXPIRED;
117               break;
118 
119             case GPG_ERR_NO_PUBKEY:
120               sig->summary |= GPGME_SIGSUM_KEY_MISSING;
121               break;
122 
123             default:
124               break;
125             }
126         }
127     }
128 
129   /* Now for some tracing stuff. */
130   if (_gpgme_debug_trace ())
131     {
132       int i;
133 
134       for (sig = opd->result.signatures, i = 0; sig; sig = sig->next, i++)
135 	{
136 	  TRACE_LOG  ("sig[%i] = fpr %s, summary 0x%x, status %s",
137 		      i, sig->fpr, sig->summary, gpg_strerror (sig->status));
138 	  TRACE_LOG  ("sig[%i] = timestamps 0x%lx/0x%lx 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 	  TRACE_LOG  ("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 	  if (sig->pka_address)
149 	    {
150 	      TRACE_LOG  ("sig[%i] = PKA address %s", i, sig->pka_address);
151 	    }
152 	  if (sig->notations)
153 	    {
154 	      TRACE_LOG  ("sig[%i] = has notations (not shown)", i);
155 	    }
156 	}
157     }
158 
159   TRACE_SUC ("result=%p", &opd->result);
160   return &opd->result;
161 }
162 
163 
164 /* Build a summary vector from RESULT. */
165 static void
calc_sig_summary(gpgme_signature_t sig)166 calc_sig_summary (gpgme_signature_t sig)
167 {
168   unsigned long sum = 0;
169 
170   /* Calculate the red/green flag.  */
171   if (sig->validity == GPGME_VALIDITY_FULL
172       || sig->validity == GPGME_VALIDITY_ULTIMATE)
173     {
174       if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
175 	  || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
176 	  || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
177 	sum |= GPGME_SIGSUM_GREEN;
178     }
179   else if (sig->validity == GPGME_VALIDITY_NEVER)
180     {
181       if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
182 	  || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
183 	  || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
184 	sum |= GPGME_SIGSUM_RED;
185     }
186   else if (gpg_err_code (sig->status) == GPG_ERR_BAD_SIGNATURE)
187     sum |= GPGME_SIGSUM_RED;
188 
189 
190   /* FIXME: handle the case when key and message are expired. */
191   switch (gpg_err_code (sig->status))
192     {
193     case GPG_ERR_SIG_EXPIRED:
194       sum |= GPGME_SIGSUM_SIG_EXPIRED;
195       break;
196 
197     case GPG_ERR_KEY_EXPIRED:
198       sum |= GPGME_SIGSUM_KEY_EXPIRED;
199       break;
200 
201     case GPG_ERR_NO_PUBKEY:
202       sum |= GPGME_SIGSUM_KEY_MISSING;
203       break;
204 
205     case GPG_ERR_CERT_REVOKED:
206       sum |= GPGME_SIGSUM_KEY_REVOKED;
207       break;
208 
209     case GPG_ERR_BAD_SIGNATURE:
210     case GPG_ERR_NO_ERROR:
211       break;
212 
213     default:
214       sum |= GPGME_SIGSUM_SYS_ERROR;
215       break;
216     }
217 
218   /* Now look at the certain reason codes.  */
219   switch (gpg_err_code (sig->validity_reason))
220     {
221     case GPG_ERR_CRL_TOO_OLD:
222       if (sig->validity == GPGME_VALIDITY_UNKNOWN)
223         sum |= GPGME_SIGSUM_CRL_TOO_OLD;
224       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       sum |= GPGME_SIGSUM_KEY_REVOKED;
231       break;
232 
233     default:
234       break;
235     }
236 
237   /* Check other flags. */
238   if (sig->wrong_key_usage)
239     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   if ((sum & GPGME_SIGSUM_GREEN) && !(sum & ~GPGME_SIGSUM_GREEN))
244     sum |= GPGME_SIGSUM_VALID;
245 
246   sig->summary = sum;
247 }
248 
249 
250 static gpgme_error_t
prepare_new_sig(op_data_t opd)251 prepare_new_sig (op_data_t opd)
252 {
253   gpgme_signature_t sig;
254 
255   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       sig = opd->current_sig;
261       memset (sig, 0, sizeof *sig);
262       assert (opd->result.signatures == sig);
263     }
264   else
265     {
266       sig = calloc (1, sizeof (*sig));
267       if (!sig)
268         return gpg_error_from_syserror ();
269       if (!opd->result.signatures)
270         opd->result.signatures = sig;
271       if (opd->current_sig)
272         opd->current_sig->next = sig;
273       opd->current_sig = sig;
274     }
275   opd->did_prepare_new_sig = 1;
276   opd->only_newsig_seen = 0;
277   return 0;
278 }
279 
280 static gpgme_error_t
parse_new_sig(op_data_t opd,gpgme_status_code_t code,char * args,gpgme_protocol_t protocol)281 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   char *end = strchr (args, ' ');
286   char *tail;
287   int got_fpr = 0;
288 
289   if (end)
290     {
291       *end = '\0';
292       end++;
293     }
294 
295   if (!opd->did_prepare_new_sig)
296     {
297       gpg_error_t err;
298 
299       err = prepare_new_sig (opd);
300       if (err)
301         return err;
302     }
303   assert (opd->did_prepare_new_sig);
304   opd->did_prepare_new_sig = 0;
305 
306   assert (opd->current_sig);
307   sig = opd->current_sig;
308 
309   /* FIXME: We should set the source of the state.  */
310   switch (code)
311     {
312     case GPGME_STATUS_GOODSIG:
313       sig->status = gpg_error (GPG_ERR_NO_ERROR);
314       break;
315 
316     case GPGME_STATUS_EXPSIG:
317       sig->status = gpg_error (GPG_ERR_SIG_EXPIRED);
318       break;
319 
320     case GPGME_STATUS_EXPKEYSIG:
321       sig->status = gpg_error (GPG_ERR_KEY_EXPIRED);
322       break;
323 
324     case GPGME_STATUS_BADSIG:
325       sig->status = gpg_error (GPG_ERR_BAD_SIGNATURE);
326       break;
327 
328     case GPGME_STATUS_REVKEYSIG:
329       sig->status = gpg_error (GPG_ERR_CERT_REVOKED);
330       break;
331 
332     case GPGME_STATUS_ERRSIG:
333       /* Parse the pubkey algo.  */
334       if (!end)
335 	goto parse_err_sig_fail;
336       gpg_err_set_errno (0);
337       sig->pubkey_algo = _gpgme_map_pk_algo (strtol (end, &tail, 0), protocol);
338       if (errno || end == tail || *tail != ' ')
339 	goto parse_err_sig_fail;
340       end = tail;
341       while (*end == ' ')
342 	end++;
343 
344       /* Parse the hash algo.  */
345       if (!*end)
346 	goto parse_err_sig_fail;
347       gpg_err_set_errno (0);
348       sig->hash_algo = strtol (end, &tail, 0);
349       if (errno || end == tail || *tail != ' ')
350 	goto parse_err_sig_fail;
351       end = tail;
352       while (*end == ' ')
353 	end++;
354 
355       /* Skip the sig class.  */
356       end = strchr (end, ' ');
357       if (!end)
358 	goto parse_err_sig_fail;
359       while (*end == ' ')
360 	end++;
361 
362       /* Parse the timestamp.  */
363       sig->timestamp = _gpgme_parse_timestamp (end, &tail);
364       if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' '))
365 	return trace_gpg_error (GPG_ERR_INV_ENGINE);
366       end = tail;
367       while (*end == ' ')
368 	end++;
369 
370       /* Parse the return code.  */
371       if (!*end)
372 	goto parse_err_sig_fail;
373 
374       gpg_err_set_errno (0);
375       sig->status = strtoul (end, &tail, 10);
376       if (errno || end == tail || (*tail && *tail != ' '))
377 	goto parse_err_sig_fail;
378       if (!*tail)
379         goto parse_err_sig_ok;
380       end = tail;
381       while (*end == ' ')
382 	end++;
383 
384       /* Parse the new fingerprint (from the ISSUER_FPR subpacket).  */
385       if (!*end || (*end == '-' && (end[1] == ' ' || !end[1])))
386         goto parse_err_sig_ok;  /* Okay (just trailing spaces).  */
387       sig->fpr = strdup (end);
388       if (!sig->fpr)
389 	return gpg_error_from_syserror ();
390       got_fpr = 1;
391       goto parse_err_sig_ok;
392 
393     parse_err_sig_fail:
394       sig->status = gpg_error (GPG_ERR_GENERAL);
395     parse_err_sig_ok:
396       break;
397 
398     default:
399       return gpg_error (GPG_ERR_GENERAL);
400     }
401 
402   if (*args && !got_fpr)
403     {
404       sig->fpr = strdup (args);
405       if (!sig->fpr)
406 	return gpg_error_from_syserror ();
407     }
408   return 0;
409 }
410 
411 
412 static gpgme_error_t
parse_valid_sig(gpgme_signature_t sig,char * args,gpgme_protocol_t protocol)413 parse_valid_sig (gpgme_signature_t sig, char *args, gpgme_protocol_t protocol)
414 {
415   char *end = strchr (args, ' ');
416   if (end)
417     {
418       *end = '\0';
419       end++;
420     }
421 
422   if (!*args)
423     /* We require at least the fingerprint.  */
424     return gpg_error (GPG_ERR_GENERAL);
425 
426   if (sig->fpr)
427     free (sig->fpr);
428   sig->fpr = strdup (args);
429   if (!sig->fpr)
430     return gpg_error_from_syserror ();
431 
432   /* Skip the creation date.  */
433   end = strchr (end, ' ');
434   if (end)
435     {
436       char *tail;
437 
438       sig->timestamp = _gpgme_parse_timestamp (end, &tail);
439       if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' '))
440 	return trace_gpg_error (GPG_ERR_INV_ENGINE);
441       end = tail;
442 
443       sig->exp_timestamp = _gpgme_parse_timestamp (end, &tail);
444       if (sig->exp_timestamp == -1 || end == tail || (*tail && *tail != ' '))
445 	return trace_gpg_error (GPG_ERR_INV_ENGINE);
446       end = tail;
447 
448       while (*end == ' ')
449 	end++;
450       /* Skip the signature version.  */
451       end = strchr (end, ' ');
452       if (end)
453 	{
454 	  while (*end == ' ')
455 	    end++;
456 
457 	  /* Skip the reserved field.  */
458 	  end = strchr (end, ' ');
459 	  if (end)
460 	    {
461 	      /* Parse the pubkey algo.  */
462 	      gpg_err_set_errno (0);
463 	      sig->pubkey_algo = _gpgme_map_pk_algo (strtol (end, &tail, 0),
464                                                      protocol);
465 	      if (errno || end == tail || *tail != ' ')
466 		return trace_gpg_error (GPG_ERR_INV_ENGINE);
467 	      end = tail;
468 
469 	      while (*end == ' ')
470 		end++;
471 
472 	      if (*end)
473 		{
474 		  /* Parse the hash algo.  */
475 
476 		  gpg_err_set_errno (0);
477 		  sig->hash_algo = strtol (end, &tail, 0);
478 		  if (errno || end == tail || *tail != ' ')
479 		    return trace_gpg_error (GPG_ERR_INV_ENGINE);
480 		  end = tail;
481 		}
482 	    }
483 	}
484     }
485   return 0;
486 }
487 
488 
489 static gpgme_error_t
parse_notation(gpgme_signature_t sig,gpgme_status_code_t code,char * args)490 parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
491 {
492   gpgme_error_t err;
493   gpgme_sig_notation_t *lastp = &sig->notations;
494   gpgme_sig_notation_t notation = sig->notations;
495   char *p;
496 
497   if (code == GPGME_STATUS_NOTATION_NAME || code == GPGME_STATUS_POLICY_URL)
498     {
499       p = strchr (args, ' ');
500       if (p)
501         *p = '\0';
502 
503       /* FIXME: We could keep a pointer to the last notation in the list.  */
504       while (notation && notation->value)
505 	{
506 	  lastp = &notation->next;
507 	  notation = notation->next;
508 	}
509 
510       if (notation)
511 	/* There is another notation name without data for the
512 	   previous one.  The crypto backend misbehaves.  */
513 	return trace_gpg_error (GPG_ERR_INV_ENGINE);
514 
515       err = _gpgme_sig_notation_create (&notation, NULL, 0, NULL, 0, 0);
516       if (err)
517 	return err;
518 
519       if (code == GPGME_STATUS_NOTATION_NAME)
520 	{
521 	  err = _gpgme_decode_percent_string (args, &notation->name, 0, 0);
522 	  if (err)
523 	    {
524 	      _gpgme_sig_notation_free (notation);
525 	      return err;
526 	    }
527 
528 	  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 	  notation->flags = GPGME_SIG_NOTATION_HUMAN_READABLE;
533 	  notation->human_readable = 1;
534 	}
535       else
536 	{
537 	  /* This is a policy URL.  */
538 
539 	  err = _gpgme_decode_percent_string (args, &notation->value, 0, 0);
540 	  if (err)
541 	    {
542 	      _gpgme_sig_notation_free (notation);
543 	      return err;
544 	    }
545 
546 	  notation->value_len = strlen (notation->value);
547 	}
548       *lastp = notation;
549     }
550   else if (code == GPGME_STATUS_NOTATION_FLAGS)
551     {
552       char *field[2];
553 
554       while (notation && notation->next)
555 	{
556 	  lastp = &notation->next;
557 	  notation = notation->next;
558 	}
559 
560       if (!notation || !notation->name)
561         { /* There are notation flags without a previous notation name.
562            * The crypto backend misbehaves.  */
563           return trace_gpg_error (GPG_ERR_INV_ENGINE);
564         }
565       if (_gpgme_split_fields (args, field, DIM (field)) < 2)
566         { /* Required args missing.  */
567           return trace_gpg_error (GPG_ERR_INV_ENGINE);
568         }
569       notation->flags = 0;
570       if (atoi (field[0]))
571         {
572           notation->flags |= GPGME_SIG_NOTATION_CRITICAL;
573           notation->critical = 1;
574         }
575       if (atoi (field[1]))
576         {
577           notation->flags |= GPGME_SIG_NOTATION_HUMAN_READABLE;
578           notation->human_readable = 1;
579         }
580     }
581   else if (code == GPGME_STATUS_NOTATION_DATA)
582     {
583       int len = strlen (args) + 1;
584       char *dest;
585 
586       /* FIXME: We could keep a pointer to the last notation in the list.  */
587       while (notation && notation->next)
588 	{
589 	  lastp = &notation->next;
590 	  notation = notation->next;
591 	}
592 
593       if (!notation || !notation->name)
594 	/* There is notation data without a previous notation
595 	   name.  The crypto backend misbehaves.  */
596 	return trace_gpg_error (GPG_ERR_INV_ENGINE);
597 
598       if (!notation->value)
599 	{
600 	  dest = notation->value = malloc (len);
601 	  if (!dest)
602 	    return gpg_error_from_syserror ();
603 	}
604       else
605 	{
606 	  int cur_len = strlen (notation->value);
607 	  dest = realloc (notation->value, len + strlen (notation->value));
608 	  if (!dest)
609 	    return gpg_error_from_syserror ();
610 	  notation->value = dest;
611 	  dest += cur_len;
612 	}
613 
614       err = _gpgme_decode_percent_string (args, &dest, len, 0);
615       if (err)
616 	return err;
617 
618       notation->value_len += strlen (dest);
619     }
620   else
621     return trace_gpg_error (GPG_ERR_INV_ENGINE);
622   return 0;
623 }
624 
625 
626 static gpgme_error_t
parse_trust(gpgme_signature_t sig,gpgme_status_code_t code,char * args)627 parse_trust (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
628 {
629   char *end = strchr (args, ' ');
630 
631   if (end)
632     *end = '\0';
633 
634   switch (code)
635     {
636     case GPGME_STATUS_TRUST_UNDEFINED:
637     default:
638       sig->validity = GPGME_VALIDITY_UNKNOWN;
639       break;
640 
641     case GPGME_STATUS_TRUST_NEVER:
642       sig->validity = GPGME_VALIDITY_NEVER;
643       break;
644 
645     case GPGME_STATUS_TRUST_MARGINAL:
646       sig->validity = GPGME_VALIDITY_MARGINAL;
647       break;
648 
649     case GPGME_STATUS_TRUST_FULLY:
650     case GPGME_STATUS_TRUST_ULTIMATE:
651       sig->validity = GPGME_VALIDITY_FULL;
652       break;
653     }
654 
655   sig->validity_reason = 0;
656   sig->chain_model = 0;
657   if (*args)
658     {
659       sig->validity_reason = atoi (args);
660       while (*args && *args != ' ')
661         args++;
662       if (*args)
663         {
664           while (*args == ' ')
665             args++;
666           if (!strncmp (args, "chain", 5) && (args[5] == ' ' || !args[5]))
667             sig->chain_model = 1;
668         }
669     }
670 
671   return 0;
672 }
673 
674 
675 /* Parse a TOFU_USER line and put the info into SIG.  */
676 static gpgme_error_t
parse_tofu_user(gpgme_signature_t sig,char * args,gpgme_protocol_t protocol)677 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   char *fpr = NULL;
684   char *address = NULL;
685 
686   tail = strchr (args, ' ');
687   if (!tail || tail == args)
688     {
689       err = trace_gpg_error (GPG_ERR_INV_ENGINE);  /* No fingerprint.  */
690       goto leave;
691     }
692   *tail++ = 0;
693 
694   fpr = strdup (args);
695   if (!fpr)
696     {
697       err = gpg_error_from_syserror ();
698       goto leave;
699     }
700 
701   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       err = gpg_error (GPG_ERR_DUP_VALUE);
706       goto leave;
707     }
708 
709   args = tail;
710   tail = strchr (args, ' ');
711   if (tail == args)
712     {
713       err = trace_gpg_error (GPG_ERR_INV_ENGINE);  /* No addr-spec.  */
714       goto leave;
715     }
716   if (tail)
717     *tail = 0;
718 
719   err = _gpgme_decode_percent_string (args, &address, 0, 0);
720   if (err)
721     goto leave;
722 
723   if (!sig->key)
724     {
725       err = _gpgme_key_new (&sig->key);
726       if (err)
727         goto leave;
728       sig->key->fpr = fpr;
729       sig->key->protocol = protocol;
730       fpr = NULL;
731     }
732   else if (!sig->key->fpr)
733     {
734       err = trace_gpg_error (GPG_ERR_INTERNAL);
735       goto leave;
736     }
737 
738   err = _gpgme_key_append_name (sig->key, address, 0);
739   if (err)
740     goto leave;
741 
742   uid = sig->key->_last_uid;
743   assert (uid);
744 
745   ti = calloc (1, sizeof *ti);
746   if (!ti)
747     {
748       err = gpg_error_from_syserror ();
749       goto leave;
750     }
751   uid->tofu = ti;
752 
753 
754  leave:
755   free (fpr);
756   free (address);
757   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
parse_tofu_stats(gpgme_signature_t sig,char * args)767 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   if (!sig->key || !sig->key->_last_uid || !(ti = sig->key->_last_uid->tofu))
776     return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen.  */
777   if (ti->signfirst || ti->signcount || ti->validity || ti->policy)
778     return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already set.  */
779 
780   nfields = _gpgme_split_fields (args, field, DIM (field));
781   if (nfields < 3)
782     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   err = _gpgme_strtoul_field (field[0], &uval);
787   if (err || uval > 7)
788     return trace_gpg_error (GPG_ERR_INV_ENGINE);
789   ti->validity = uval;
790 
791   /* Parse the sign-count.  */
792   err = _gpgme_strtoul_field (field[1], &uval);
793   if (err)
794     return trace_gpg_error (GPG_ERR_INV_ENGINE);
795   if (uval > USHRT_MAX)
796     uval = USHRT_MAX;
797   ti->signcount = uval;
798 
799   /* Parse the encr-count.  */
800   err = _gpgme_strtoul_field (field[2], &uval);
801   if (err)
802     return trace_gpg_error (GPG_ERR_INV_ENGINE);
803   if (uval > USHRT_MAX)
804     uval = USHRT_MAX;
805   ti->encrcount = uval;
806 
807   if (nfields == 3)
808     return 0; /* All mandatory fields parsed.  */
809 
810   /* Parse the policy.  */
811   if (!strcmp (field[3], "none"))
812     ti->policy = GPGME_TOFU_POLICY_NONE;
813   else if (!strcmp (field[3], "auto"))
814     ti->policy = GPGME_TOFU_POLICY_AUTO;
815   else if (!strcmp (field[3], "good"))
816     ti->policy = GPGME_TOFU_POLICY_GOOD;
817   else if (!strcmp (field[3], "bad"))
818     ti->policy = GPGME_TOFU_POLICY_BAD;
819   else if (!strcmp (field[3], "ask"))
820     ti->policy = GPGME_TOFU_POLICY_ASK;
821   else /* "unknown" and invalid policy strings.  */
822     ti->policy = GPGME_TOFU_POLICY_UNKNOWN;
823 
824   if (nfields == 4)
825     return 0; /* No more optional fields.  */
826 
827   /* Parse first and last seen timestamps (none or both are required).  */
828   if (nfields < 6)
829     return trace_gpg_error (GPG_ERR_INV_ENGINE); /* "tm2" missing.  */
830   err = _gpgme_strtoul_field (field[4], &uval);
831   if (err)
832     return trace_gpg_error (GPG_ERR_INV_ENGINE);
833   ti->signfirst = uval;
834   err = _gpgme_strtoul_field (field[5], &uval);
835   if (err)
836     return trace_gpg_error (GPG_ERR_INV_ENGINE);
837   ti->signlast = uval;
838   if (nfields > 7)
839     {
840       /* This condition is only to allow for gpg 2.1.15 - can
841        * eventually be removed.  */
842       err = _gpgme_strtoul_field (field[6], &uval);
843       if (err)
844         return trace_gpg_error (GPG_ERR_INV_ENGINE);
845       ti->encrfirst = uval;
846       err = _gpgme_strtoul_field (field[7], &uval);
847       if (err)
848         return trace_gpg_error (GPG_ERR_INV_ENGINE);
849       ti->encrlast = uval;
850     }
851 
852   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
parse_tofu_stats_long(gpgme_signature_t sig,char * args,int raw)858 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   if (!sig->key || !sig->key->_last_uid || !(ti = sig->key->_last_uid->tofu))
865     return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen.  */
866   if (ti->description)
867     return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already set.  */
868 
869   err = _gpgme_decode_percent_string (args, &ti->description, 0, 0);
870   if (err)
871     return err;
872 
873   /* Remove the non-breaking spaces.  */
874   if (!raw)
875     {
876       for (p = ti->description; *p; p++)
877         if (*p == '~')
878           *p = ' ';
879     }
880   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
parse_error(gpgme_signature_t sig,char * args,int set_status)888 parse_error (gpgme_signature_t sig, char *args, int set_status)
889 {
890   gpgme_error_t err;
891   char *where = strchr (args, ' ');
892   char *which;
893 
894   if (where)
895     {
896       *where = '\0';
897       which = where + 1;
898 
899       where = strchr (which, ' ');
900       if (where)
901 	*where = '\0';
902 
903       where = args;
904     }
905   else
906     return trace_gpg_error (GPG_ERR_INV_ENGINE);
907 
908   err = atoi (which);
909 
910   if (!strcmp (where, "proc_pkt.plaintext")
911       && 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       return gpg_error (GPG_ERR_BAD_DATA);
916     }
917   else if (!set_status)
918     ;
919   else if (!strcmp (where, "verify.findkey"))
920     sig->status = err;
921   else if (!strcmp (where, "verify.keyusage")
922 	   && gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
923     sig->wrong_key_usage = 1;
924 
925   return 0;
926 }
927 
928 
929 gpgme_error_t
_gpgme_verify_status_handler(void * priv,gpgme_status_code_t code,char * args)930 _gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
931 {
932   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   err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
940   opd = hook;
941   if (err)
942     return err;
943 
944   sig = opd->current_sig;
945 
946   switch (code)
947     {
948     case GPGME_STATUS_NEWSIG:
949       if (sig)
950         calc_sig_summary (sig);
951       err = prepare_new_sig (opd);
952       opd->only_newsig_seen = 1;
953       opd->conflict_user_seen = 0;
954       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       if (sig && !opd->did_prepare_new_sig)
963 	calc_sig_summary (sig);
964       opd->only_newsig_seen = 0;
965       return parse_new_sig (opd, code, args, ctx->protocol);
966 
967     case GPGME_STATUS_VALIDSIG:
968       opd->only_newsig_seen = 0;
969       return sig ? parse_valid_sig (sig, args, ctx->protocol)
970 	: trace_gpg_error (GPG_ERR_INV_ENGINE);
971 
972     case GPGME_STATUS_NODATA:
973       opd->only_newsig_seen = 0;
974       if (!sig)
975 	return gpg_error (GPG_ERR_NO_DATA);
976       sig->status = gpg_error (GPG_ERR_NO_DATA);
977       break;
978 
979     case GPGME_STATUS_UNEXPECTED:
980       opd->only_newsig_seen = 0;
981       if (!sig)
982 	return gpg_error (GPG_ERR_GENERAL);
983       sig->status = gpg_error (GPG_ERR_NO_DATA);
984       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       opd->only_newsig_seen = 0;
991       return sig ? parse_notation (sig, code, args)
992 	: 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       opd->only_newsig_seen = 0;
1000       return sig ? parse_trust (sig, code, args)
1001 	: trace_gpg_error (GPG_ERR_INV_ENGINE);
1002 
1003     case GPGME_STATUS_PKA_TRUST_BAD:
1004     case GPGME_STATUS_PKA_TRUST_GOOD:
1005       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       if (!sig || sig->pka_trust || sig->pka_address)
1009         return trace_gpg_error (GPG_ERR_INV_ENGINE);
1010       sig->pka_trust = code == GPGME_STATUS_PKA_TRUST_GOOD? 2 : 1;
1011       end = strchr (args, ' ');
1012       if (end)
1013         *end = 0;
1014       sig->pka_address = strdup (args);
1015       break;
1016 
1017     case GPGME_STATUS_TOFU_USER:
1018       opd->only_newsig_seen = 0;
1019       if (!sig)
1020         return trace_gpg_error (GPG_ERR_INV_ENGINE);
1021       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       if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
1030         {
1031           opd->conflict_user_seen = 1;
1032           break;
1033         }
1034       opd->conflict_user_seen = 0;
1035       return trace_gpg_error (err);
1036 
1037     case GPGME_STATUS_TOFU_STATS:
1038       opd->only_newsig_seen = 0;
1039       if (opd->conflict_user_seen)
1040         break;
1041       return sig ? parse_tofu_stats (sig, args)
1042         /*    */ : trace_gpg_error (GPG_ERR_INV_ENGINE);
1043 
1044     case GPGME_STATUS_TOFU_STATS_LONG:
1045       opd->only_newsig_seen = 0;
1046       if (opd->conflict_user_seen)
1047         break;
1048       return sig ? parse_tofu_stats_long (sig, args, ctx->raw_description)
1049         /*    */ : trace_gpg_error (GPG_ERR_INV_ENGINE);
1050 
1051     case GPGME_STATUS_ERROR:
1052       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       return parse_error (sig, args, !!sig );
1056 
1057     case GPGME_STATUS_FAILURE:
1058       opd->failure_code = _gpgme_parse_failure (args);
1059       break;
1060 
1061     case GPGME_STATUS_EOF:
1062       if (sig && !opd->did_prepare_new_sig)
1063 	calc_sig_summary (sig);
1064       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           assert (!sig->next);
1070           if (sig == opd->result.signatures)
1071             opd->result.signatures = NULL;
1072           else
1073             {
1074               for (sig2 = opd->result.signatures; sig2; sig2 = sig2->next)
1075                 if (sig2->next == sig)
1076                   {
1077                     sig2->next = NULL;
1078                     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           free (sig);
1084           opd->current_sig = NULL;
1085         }
1086       opd->only_newsig_seen = 0;
1087       if (opd->failure_code)
1088         return opd->failure_code;
1089       break;
1090 
1091     case GPGME_STATUS_PLAINTEXT:
1092       if (++opd->plaintext_seen > 1)
1093         return gpg_error (GPG_ERR_BAD_DATA);
1094       {
1095         int mime = 0;
1096         err = _gpgme_parse_plaintext (args, &opd->result.file_name, &mime);
1097         if (err)
1098           return err;
1099         opd->result.is_mime = !!mime;
1100       }
1101       break;
1102 
1103     case GPGME_STATUS_VERIFICATION_COMPLIANCE_MODE:
1104       PARSE_COMPLIANCE_FLAGS (args, opd->current_sig);
1105       break;
1106 
1107     default:
1108       break;
1109     }
1110   return 0;
1111 }
1112 
1113 
1114 static gpgme_error_t
verify_status_handler(void * priv,gpgme_status_code_t code,char * args)1115 verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
1116 {
1117   gpgme_error_t err;
1118 
1119   err = _gpgme_progress_status_handler (priv, code, args);
1120   if (!err)
1121     err = _gpgme_verify_status_handler (priv, code, args);
1122   return err;
1123 }
1124 
1125 
1126 gpgme_error_t
_gpgme_op_verify_init_result(gpgme_ctx_t ctx)1127 _gpgme_op_verify_init_result (gpgme_ctx_t ctx)
1128 {
1129   void *hook;
1130   op_data_t opd;
1131 
1132   return _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook,
1133 				sizeof (*opd), release_op_data);
1134 }
1135 
1136 
1137 static gpgme_error_t
verify_start(gpgme_ctx_t ctx,int synchronous,gpgme_data_t sig,gpgme_data_t signed_text,gpgme_data_t plaintext)1138 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   err = _gpgme_op_reset (ctx, synchronous);
1144   if (err)
1145     return err;
1146 
1147   err = _gpgme_op_verify_init_result (ctx);
1148   if (err)
1149     return err;
1150 
1151   _gpgme_engine_set_status_handler (ctx->engine, verify_status_handler, ctx);
1152 
1153   if (!sig)
1154     return gpg_error (GPG_ERR_NO_DATA);
1155 
1156   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
gpgme_op_verify_start(gpgme_ctx_t ctx,gpgme_data_t sig,gpgme_data_t signed_text,gpgme_data_t plaintext)1164 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   TRACE_BEG  (DEBUG_CTX, "gpgme_op_verify_start", ctx,
1169 	      "sig=%p, signed_text=%p, plaintext=%p",
1170 	      sig, signed_text, plaintext);
1171 
1172   if (!ctx)
1173     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
1174 
1175   err = verify_start (ctx, 0, sig, signed_text, plaintext);
1176   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
gpgme_op_verify(gpgme_ctx_t ctx,gpgme_data_t sig,gpgme_data_t signed_text,gpgme_data_t plaintext)1183 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   TRACE_BEG  (DEBUG_CTX, "gpgme_op_verify", ctx,
1189 	      "sig=%p, signed_text=%p, plaintext=%p",
1190 	      sig, signed_text, plaintext);
1191 
1192   if (!ctx)
1193     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
1194 
1195   err = verify_start (ctx, 1, sig, signed_text, plaintext);
1196   if (!err)
1197     err = _gpgme_wait_one (ctx);
1198   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
gpgme_get_sig_key(gpgme_ctx_t ctx,int idx,gpgme_key_t * r_key)1207 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   if (!ctx)
1213     return gpg_error (GPG_ERR_INV_VALUE);
1214 
1215   result = gpgme_op_verify_result (ctx);
1216   sig = result->signatures;
1217 
1218   while (sig && idx)
1219     {
1220       sig = sig->next;
1221       idx--;
1222     }
1223   if (!sig || idx)
1224     return gpg_error (GPG_ERR_EOF);
1225 
1226   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 *
gpgme_get_sig_status(gpgme_ctx_t ctx,int idx,_gpgme_sig_stat_t * r_stat,time_t * r_created)1235 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   result = gpgme_op_verify_result (ctx);
1242   sig = result->signatures;
1243 
1244   while (sig && idx)
1245     {
1246       sig = sig->next;
1247       idx--;
1248     }
1249   if (!sig || idx)
1250     return NULL;
1251 
1252   if (r_stat)
1253     {
1254       switch (gpg_err_code (sig->status))
1255 	{
1256 	case GPG_ERR_NO_ERROR:
1257 	  *r_stat = GPGME_SIG_STAT_GOOD;
1258 	  break;
1259 
1260 	case GPG_ERR_BAD_SIGNATURE:
1261 	  *r_stat = GPGME_SIG_STAT_BAD;
1262 	  break;
1263 
1264 	case GPG_ERR_NO_PUBKEY:
1265 	  *r_stat = GPGME_SIG_STAT_NOKEY;
1266 	  break;
1267 
1268 	case GPG_ERR_NO_DATA:
1269 	  *r_stat = GPGME_SIG_STAT_NOSIG;
1270 	  break;
1271 
1272 	case GPG_ERR_SIG_EXPIRED:
1273 	  *r_stat = GPGME_SIG_STAT_GOOD_EXP;
1274 	  break;
1275 
1276 	case GPG_ERR_KEY_EXPIRED:
1277 	  *r_stat = GPGME_SIG_STAT_GOOD_EXPKEY;
1278 	  break;
1279 
1280 	default:
1281 	  *r_stat = GPGME_SIG_STAT_ERROR;
1282 	  break;
1283 	}
1284     }
1285   if (r_created)
1286     *r_created = sig->timestamp;
1287   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
gpgme_get_sig_ulong_attr(gpgme_ctx_t ctx,int idx,_gpgme_attr_t what,int whatidx)1296 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   result = gpgme_op_verify_result (ctx);
1305   sig = result->signatures;
1306 
1307   while (sig && idx)
1308     {
1309       sig = sig->next;
1310       idx--;
1311     }
1312   if (!sig || idx)
1313     return 0;
1314 
1315   switch (what)
1316     {
1317     case GPGME_ATTR_CREATED:
1318       return sig->timestamp;
1319 
1320     case GPGME_ATTR_EXPIRE:
1321       return sig->exp_timestamp;
1322 
1323     case GPGME_ATTR_VALIDITY:
1324       return (unsigned long) sig->validity;
1325 
1326     case GPGME_ATTR_SIG_STATUS:
1327       switch (gpg_err_code (sig->status))
1328 	{
1329 	case GPG_ERR_NO_ERROR:
1330 	  return GPGME_SIG_STAT_GOOD;
1331 
1332 	case GPG_ERR_BAD_SIGNATURE:
1333 	  return GPGME_SIG_STAT_BAD;
1334 
1335 	case GPG_ERR_NO_PUBKEY:
1336 	  return GPGME_SIG_STAT_NOKEY;
1337 
1338 	case GPG_ERR_NO_DATA:
1339 	  return GPGME_SIG_STAT_NOSIG;
1340 
1341 	case GPG_ERR_SIG_EXPIRED:
1342 	  return GPGME_SIG_STAT_GOOD_EXP;
1343 
1344 	case GPG_ERR_KEY_EXPIRED:
1345 	  return GPGME_SIG_STAT_GOOD_EXPKEY;
1346 
1347 	default:
1348 	  return GPGME_SIG_STAT_ERROR;
1349 	}
1350 
1351     case GPGME_ATTR_SIG_SUMMARY:
1352       return sig->summary;
1353 
1354     default:
1355       break;
1356     }
1357   return 0;
1358 }
1359 
1360 
1361 const char *
gpgme_get_sig_string_attr(gpgme_ctx_t ctx,int idx,_gpgme_attr_t what,int whatidx)1362 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   result = gpgme_op_verify_result (ctx);
1369   sig = result->signatures;
1370 
1371   while (sig && idx)
1372     {
1373       sig = sig->next;
1374       idx--;
1375     }
1376   if (!sig || idx)
1377     return NULL;
1378 
1379   switch (what)
1380     {
1381     case GPGME_ATTR_FPR:
1382       return sig->fpr;
1383 
1384     case GPGME_ATTR_ERRTOK:
1385       if (whatidx == 1)
1386         return sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
1387       else
1388 	return "";
1389     default:
1390       break;
1391     }
1392 
1393   return NULL;
1394 }
1395