2003-04-24 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / verify.c
1 /* verify.c - Signature verification.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003 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 General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (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    General Public License for more details.
16  
17    You should have received a copy of the GNU General Public License
18    along with GPGME; if not, write to the Free Software Foundation,
19    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <assert.h>
28
29 #include "util.h"
30 #include "context.h"
31 #include "ops.h"
32 #include "key.h"
33
34
35 struct verify_result
36 {
37   GpgmeSigStat status;
38   GpgmeSigStat expstatus;       /* Only used by finish_sig.  */
39   GpgmeData notation;           /* We store an XML fragment here.  */
40   int collecting;               /* Private to finish_sig().  */
41   int notation_in_data;         /* Private to add_notation().  */
42   char fpr[41];                 /* Fingerprint of a good signature or keyid of
43                                    a bad one.  */
44   ulong timestamp;              /* Signature creation time.  */
45   ulong exptimestamp;           /* Signature exipration time or 0.  */
46   GpgmeValidity validity;
47   int wrong_key_usage;  
48   char trust_errtok[31];        /* Error token send with the trust status.  */
49 };
50 typedef struct verify_result *VerifyResult;
51
52
53 static void
54 release_verify_result (void *hook)
55 {
56   VerifyResult result = (VerifyResult) hook;
57
58   gpgme_data_release (result->notation);
59 }
60
61
62 /* Check whether STRING starts with TOKEN and return true in this
63    case.  This is case insensitive.  If NEXT is not NULL return the
64    number of bytes to be added to STRING to get to the next token; a
65    returned value of 0 indicates end of line.  */
66 static int 
67 is_token (const char *string, const char *token, size_t *next)
68 {
69   size_t n = 0;
70
71   for (;*string && *token && *string == *token; string++, token++, n++)
72     ;
73   if (*token || (*string != ' ' && !*string))
74     return 0;
75   if (next)
76     {
77       for (; *string == ' '; string++, n++)
78         ;
79       *next = n;
80     }
81   return 1;
82 }
83
84 static int
85 skip_token (const char *string, size_t *next)
86 {
87   size_t n = 0;
88
89   for (;*string && *string != ' '; string++, n++)
90     ;
91   for (;*string == ' '; string++, n++)
92     ;
93   if (!*string)
94     return 0;
95   if (next)
96     *next = n;
97   return 1;
98 }
99
100
101 static size_t
102 copy_token (const char *string, char *buffer, size_t length)
103 {
104   const char *s = string;
105   char *p = buffer;
106   size_t i;
107
108   for (i = 1; i < length && *s && *s != ' ' ; i++)
109     *p++ = *s++;
110   *p = 0;
111   /* continue scanning in case the copy was truncated */
112   while (*s && *s != ' ')
113     s++;
114   return s - string;
115 }
116
117
118 /* FIXME: Check that we are adding this to the correct signature.  */
119 static GpgmeError
120 add_notation (VerifyResult result, GpgmeStatusCode code, const char *notation)
121 {
122   GpgmeData dh = result->notation;
123
124   if (!dh)
125     {
126       if (gpgme_data_new (&dh))
127         return GPGME_Out_Of_Core;
128       result->notation = dh;
129       _gpgme_data_append_string (dh, "  <notation>\n");
130     }
131
132   if (code == GPGME_STATUS_NOTATION_DATA)
133     {
134       if (!result->notation_in_data)
135         _gpgme_data_append_string (dh, "  <data>");
136       _gpgme_data_append_percentstring_for_xml (dh, notation);
137       result->notation_in_data = 1;
138       return 0;
139     }
140
141   if (result->notation_in_data)
142     {
143       _gpgme_data_append_string (dh, "</data>\n");
144       result->notation_in_data = 0;
145     }
146
147   if (code == GPGME_STATUS_NOTATION_NAME)
148     {
149       _gpgme_data_append_string (dh, "  <name>");
150       _gpgme_data_append_percentstring_for_xml (dh, notation);
151       _gpgme_data_append_string (dh, "</name>\n");
152     }
153   else if (code == GPGME_STATUS_POLICY_URL)
154     {
155       _gpgme_data_append_string (dh, "  <policy>");
156       _gpgme_data_append_percentstring_for_xml (dh, notation);
157       _gpgme_data_append_string (dh, "</policy>\n");
158     }
159   else
160     assert (0);
161   return 0;
162 }
163
164
165 /* Finish a pending signature info collection.  */
166 static void
167 finish_sig (VerifyResult result)
168 {
169   struct ctx_op_data *op_data;
170
171   /* We intimately know that gpgme_op_data_lookup appends the data to
172      the op_data structure.  We can use this here to change the type
173      knowing only the hook value.  */
174   op_data = (struct ctx_op_data *) ((void *) result
175                                     - sizeof (struct ctx_op_data));
176   op_data->type = OPDATA_VERIFY;
177 }
178
179
180 GpgmeError
181 _gpgme_verify_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
182 {
183   VerifyResult result;
184   GpgmeError err;
185   char *p;
186   size_t n;
187   int i;
188
189   err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY_COLLECTING, (void **) &result,
190                                -1, NULL);
191   if (err)
192     return err;
193
194   if (code == GPGME_STATUS_GOODSIG || code == GPGME_STATUS_EXPSIG
195       || code == GPGME_STATUS_EXPKEYSIG || code == GPGME_STATUS_BADSIG
196       || code == GPGME_STATUS_ERRSIG)
197     {
198       /* A new signature starts.  */
199       if (result)
200         finish_sig (result);
201       err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY_COLLECTING, (void **) &result,
202                                    sizeof (*result), release_verify_result);
203       if (err)
204         return err;
205     }
206
207   switch (code)
208     {
209     case GPGME_STATUS_NODATA:
210     case GPGME_STATUS_UNEXPECTED:
211       if (!result)
212         return GPGME_General_Error;
213       result->status = GPGME_SIG_STAT_NOSIG;
214       break;
215
216     case GPGME_STATUS_GOODSIG:
217       if (!result)
218         return GPGME_General_Error;
219       result->expstatus = GPGME_SIG_STAT_GOOD;
220       break;
221
222     case GPGME_STATUS_EXPSIG:
223       if (!result)
224         return GPGME_General_Error;
225       result->expstatus = GPGME_SIG_STAT_GOOD_EXP;
226       break;
227
228     case GPGME_STATUS_EXPKEYSIG:
229       if (!result)
230         return GPGME_General_Error;
231       result->expstatus = GPGME_SIG_STAT_GOOD_EXPKEY;
232       break;
233
234     case GPGME_STATUS_VALIDSIG:
235       if (!result)
236         return GPGME_General_Error;
237       result->status = GPGME_SIG_STAT_GOOD;
238       i = copy_token (args, result->fpr, DIM (result->fpr));
239       /* Skip the formatted date.  */
240       while (args[i] && args[i] == ' ')
241         i++;
242       while (args[i] && args[i] != ' ')
243         i++;
244       /* And get the timestamp.  */
245       result->timestamp = strtoul (args + i, &p, 10);
246       if (args[i])
247         result->exptimestamp = strtoul (p, NULL, 10);
248       break;
249
250     case GPGME_STATUS_BADSIG:
251       if (!result)
252         return GPGME_General_Error;
253       result->status = GPGME_SIG_STAT_BAD;
254       /* Store the keyID in the fpr field.  */
255       copy_token (args, result->fpr, DIM (result->fpr));
256       break;
257
258     case GPGME_STATUS_ERRSIG:
259       if (!result)
260         return GPGME_General_Error;
261       /* The return code is the 6th argument, if it is 9, the problem
262          is a missing key.  Note that this is not emitted by gpgsm */
263       for (p = args, i = 0; p && *p && i < 5; i++)
264         {
265           p = strchr (p, ' ');
266           if (p)
267             while (*p == ' ')
268               p++;
269         }
270       if (p && *(p++) == '9' && (*p == '\0' || *p == ' '))
271         result->status = GPGME_SIG_STAT_NOKEY;
272       else
273         result->status = GPGME_SIG_STAT_ERROR;
274       /* Store the keyID in the fpr field.  */
275       copy_token (args, result->fpr, DIM (result->fpr));
276       break;
277
278     case GPGME_STATUS_NOTATION_NAME:
279     case GPGME_STATUS_NOTATION_DATA:
280     case GPGME_STATUS_POLICY_URL:
281       if (!result)
282         return GPGME_General_Error;
283       err = add_notation (result, code, args);
284       if (err)
285         return err;
286       break;
287
288     case GPGME_STATUS_TRUST_UNDEFINED:
289       if (!result)
290         return GPGME_General_Error;
291       result->validity = GPGME_VALIDITY_UNKNOWN;
292       copy_token (args, result->trust_errtok,
293                   DIM(result->trust_errtok));
294       break;
295     case GPGME_STATUS_TRUST_NEVER:
296       if (!result)
297         return GPGME_General_Error;
298       result->validity = GPGME_VALIDITY_NEVER;
299       copy_token (args, result->trust_errtok,
300                   DIM(result->trust_errtok));
301       break;
302     case GPGME_STATUS_TRUST_MARGINAL:
303       if (!result)
304         return GPGME_General_Error;
305       if (result->status == GPGME_SIG_STAT_GOOD)
306         result->validity = GPGME_VALIDITY_MARGINAL;
307       copy_token (args, result->trust_errtok,
308                   DIM(result->trust_errtok));
309       break;
310     case GPGME_STATUS_TRUST_FULLY:
311     case GPGME_STATUS_TRUST_ULTIMATE:
312       if (!result)
313         return GPGME_General_Error;
314       if (result->status == GPGME_SIG_STAT_GOOD)
315         result->validity = GPGME_VALIDITY_FULL;
316       break;
317
318     case GPGME_STATUS_END_STREAM:
319       break;
320
321     case GPGME_STATUS_ERROR:
322       if (!result)
323         return GPGME_General_Error;
324       /* Generic error, we need this for gpgsm (and maybe for gpg in future)
325          to get error descriptions. */
326       if (is_token (args, "verify.findkey", &n) && n)
327         {
328           args += n;
329           if (is_token (args, "No_Public_Key", NULL))
330             result->status = GPGME_SIG_STAT_NOKEY;
331           else
332             result->status = GPGME_SIG_STAT_ERROR;
333
334         }
335       else if (skip_token (args, &n) && n)
336         {
337           args += n;
338           if (is_token (args, "Wrong_Key_Usage", NULL))
339             result->wrong_key_usage = 1;
340         }
341       break;
342
343     case GPGME_STATUS_EOF:
344       if (result)
345         {
346           finish_sig (result);
347
348           /* FIXME: Put all notation data into one XML fragment.  */
349           if (result->notation)
350             {
351               GpgmeData dh = result->notation;
352               
353               if (result->notation_in_data)
354                 {
355                   _gpgme_data_append_string (dh, "</data>\n");
356                   result->notation_in_data = 0;
357                 }
358               _gpgme_data_append_string (dh, "</notation>\n");
359               ctx->notation = dh;
360               result->notation = NULL;
361             }
362         }
363       break;
364  
365     default:
366       /* Ignore all other codes.  */
367       break;
368     }
369   return 0;
370 }
371
372
373 static GpgmeError
374 _gpgme_op_verify_start (GpgmeCtx ctx, int synchronous,
375                         GpgmeData sig, GpgmeData signed_text, GpgmeData plaintext)
376 {
377   int err = 0;
378
379   err = _gpgme_op_reset (ctx, synchronous);
380   if (err)
381     goto leave;
382
383   _gpgme_engine_set_status_handler (ctx->engine, _gpgme_verify_status_handler,
384                                     ctx);
385
386   /* Check the supplied data.  */
387   if (!sig)
388     {
389       err = GPGME_No_Data;
390       goto leave;
391     }
392   if (!signed_text && !plaintext)
393     {
394       err = GPGME_Invalid_Value;
395       goto leave;
396     }
397   err = _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext);
398
399  leave:
400   if (err)
401     {
402       _gpgme_engine_release (ctx->engine);
403       ctx->engine = NULL;
404     }
405   return err;
406 }
407
408
409 GpgmeError
410 gpgme_op_verify_start (GpgmeCtx ctx, GpgmeData sig, GpgmeData signed_text,
411                        GpgmeData plaintext)
412 {
413   return _gpgme_op_verify_start (ctx, 0, sig, signed_text, plaintext);
414 }
415
416
417 /**
418  * gpgme_op_verify:
419  * @c: the context
420  * @sig: the signature data
421  * @text: the signed text
422  * 
423  * Perform a signature check on the signature given in @sig.  If @text
424  * is a new and uninitialized data object, it is assumed that @sig
425  * contains a normal or cleartext signature, and the plaintext is
426  * returned in @text upon successful verification.
427  *
428  * If @text is initialized, it is assumed that @sig is a detached
429  * signature for the material given in @text.
430  *
431  * Return value: 0 on success or an errorcode if something not related to
432  *               the signature itself did go wrong.
433  **/
434 GpgmeError
435 gpgme_op_verify (GpgmeCtx ctx, GpgmeData sig, GpgmeData signed_text,
436                  GpgmeData plaintext)
437 {
438   GpgmeError err;
439
440   gpgme_data_release (ctx->notation);
441   ctx->notation = NULL;
442     
443   err = _gpgme_op_verify_start (ctx, 1, sig, signed_text, plaintext);
444   if (!err)
445     err = _gpgme_wait_one (ctx);
446   return err;
447 }
448
449
450 /**
451  * gpgme_get_sig_status:
452  * @c: Context
453  * @idx: Index of the signature starting at 0
454  * @r_stat: Returns the status
455  * @r_created: Returns the creation timestamp
456  * 
457  * Return information about an already verified signatures. 
458  *
459  * The result of this operation is returned in @r_stat which can take these
460  * values:
461  *  GPGME_SIG_STAT_NONE:  No status - should not happen
462  *  GPGME_SIG_STAT_GOOD:  The signature is valid 
463  *  GPGME_SIG_STAT_BAD:   The signature is not valid
464  *  GPGME_SIG_STAT_NOKEY: The signature could not be checked due to a
465  *                        missing key
466  *  GPGME_SIG_STAT_NOSIG: This is not a signature
467  *  GPGME_SIG_STAT_ERROR: Due to some other error the check could not be done.
468  *  GPGME_SIG_STAT_DIFF:  There is more than 1 signature and they have not
469  *                        the same status.
470  *  GPGME_SIG_STAT_GOOD_EXP:  The signature is good but has expired.
471  *  GPGME_SIG_STAT_GOOD_KEYEXP:  The signature is good but the key has expired.
472  *
473  * 
474  * Return value: The fingerprint or NULL in case of an problem or
475  *               when there are no more signatures.
476  **/
477 const char *
478 gpgme_get_sig_status (GpgmeCtx ctx, int idx,
479                       GpgmeSigStat *r_stat, time_t *r_created)
480 {
481   struct ctx_op_data *op_data;
482   VerifyResult result;
483
484   if (!ctx)
485     return NULL;        /* No results yet or verification error.  */
486
487   op_data = ctx->op_data;
488   while (op_data)
489     {
490       while (op_data && op_data->type != OPDATA_VERIFY)
491         op_data = op_data->next;
492       if (idx-- == 0)
493         break;
494       op_data = op_data->next;  
495     }
496   if (!op_data)
497     return NULL;        /* No more signatures.  */
498
499   result = (VerifyResult) op_data->hook;
500   if (r_stat)
501     *r_stat = result->status;
502   if (r_created)
503     *r_created = result->timestamp;
504   return result->fpr;
505 }
506
507
508 /* Build a summary vector from RESULT. */
509 static unsigned long
510 calc_sig_summary (VerifyResult result)
511 {
512   unsigned long sum = 0;
513
514   if (result->validity == GPGME_VALIDITY_FULL
515      || result->validity == GPGME_VALIDITY_ULTIMATE)
516     {
517       if (result->status == GPGME_SIG_STAT_GOOD
518           || result->status == GPGME_SIG_STAT_GOOD_EXP
519           || result->status == GPGME_SIG_STAT_GOOD_EXPKEY)
520         sum |= GPGME_SIGSUM_GREEN;
521     }
522   else if (result->validity == GPGME_VALIDITY_NEVER)
523     {
524       if (result->status == GPGME_SIG_STAT_GOOD
525           || result->status == GPGME_SIG_STAT_GOOD_EXP
526           || result->status == GPGME_SIG_STAT_GOOD_EXPKEY)
527         sum |= GPGME_SIGSUM_RED;
528     }
529   else if (result->status == GPGME_SIG_STAT_BAD)
530     sum |= GPGME_SIGSUM_RED;
531
532   /* fixme: handle the case when key and message are expired. */
533   if (result->status == GPGME_SIG_STAT_GOOD_EXP)
534     sum |= GPGME_SIGSUM_SIG_EXPIRED;
535   else if (result->status == GPGME_SIG_STAT_GOOD_EXPKEY)
536     sum |= GPGME_SIGSUM_KEY_EXPIRED;
537   else if (result->status == GPGME_SIG_STAT_NOKEY)
538     sum |= GPGME_SIGSUM_KEY_MISSING;
539   else if (result->status == GPGME_SIG_STAT_ERROR)
540     sum |= GPGME_SIGSUM_SYS_ERROR;
541
542   if ( !strcmp (result->trust_errtok, "Certificate_Revoked"))
543     sum |= GPGME_SIGSUM_KEY_REVOKED;
544   else if ( !strcmp (result->trust_errtok, "No_CRL_Known"))
545     sum |= GPGME_SIGSUM_CRL_MISSING;
546   else if ( !strcmp (result->trust_errtok, "CRL_Too_Old"))
547     sum |= GPGME_SIGSUM_CRL_TOO_OLD;
548   else if ( !strcmp (result->trust_errtok, "No_Policy_Match"))
549     sum |= GPGME_SIGSUM_BAD_POLICY;
550   else if (*result->trust_errtok)
551     sum |= GPGME_SIGSUM_SYS_ERROR;
552
553   if (result->wrong_key_usage)
554     sum |= GPGME_SIGSUM_BAD_POLICY;
555
556   /* Set the valid flag when the signature is unquestionable
557      valid. */
558   if ((sum & GPGME_SIGSUM_GREEN) && !(sum & ~GPGME_SIGSUM_GREEN))
559     sum |= GPGME_SIGSUM_VALID;
560
561   return sum;
562 }
563
564
565 const char *
566 gpgme_get_sig_string_attr (GpgmeCtx ctx, int idx, GpgmeAttr what, int whatidx)
567 {
568   struct ctx_op_data *op_data;
569   VerifyResult result;
570
571   if (!ctx)
572     return NULL;        /* No results yet or verification error.  */
573
574   op_data = ctx->op_data;
575   while (op_data)
576     {
577       while (op_data && op_data->type != OPDATA_VERIFY)
578         op_data = op_data->next;
579       if (idx-- == 0)
580         break;
581       op_data = op_data->next;  
582     }
583   if (!op_data)
584     return NULL;        /* No more signatures.  */
585
586   result = (VerifyResult) op_data->hook;
587   switch (what)
588     {
589     case GPGME_ATTR_FPR:
590       return result->fpr;
591     case GPGME_ATTR_ERRTOK:
592       if (whatidx == 1)
593         return result->wrong_key_usage? "Wrong_Key_Usage":"";
594       else
595         return result->trust_errtok;
596     default:
597       break;
598     }
599   return NULL;
600 }
601
602
603 unsigned long
604 gpgme_get_sig_ulong_attr (GpgmeCtx ctx, int idx, GpgmeAttr what, int reserved)
605 {
606   struct ctx_op_data *op_data;
607   VerifyResult result;
608
609   if (!ctx)
610     return 0;   /* No results yet or verification error.  */
611
612   op_data = ctx->op_data;
613   while (op_data)
614     {
615       while (op_data && op_data->type != OPDATA_VERIFY)
616         op_data = op_data->next;
617       if (idx-- == 0)
618         break;
619       op_data = op_data->next;  
620     }
621   if (!op_data)
622     return 0;   /* No more signatures.  */
623
624   result = (VerifyResult) op_data->hook;
625   switch (what)
626     {
627     case GPGME_ATTR_CREATED:
628       return result->timestamp;
629     case GPGME_ATTR_EXPIRE:
630       return result->exptimestamp;
631     case GPGME_ATTR_VALIDITY:
632       return (unsigned long) result->validity;
633     case GPGME_ATTR_SIG_STATUS:
634       return (unsigned long) result->status;
635     case GPGME_ATTR_SIG_SUMMARY:
636       return calc_sig_summary (result);
637     default:
638       break;
639     }
640   return 0;
641 }
642
643
644 /**
645  * gpgme_get_sig_key:
646  * @c: context
647  * @idx: Index of the signature starting at 0
648  * @r_key: Returns the key object
649  * 
650  * Return a key object which was used to check the signature. 
651  * 
652  * Return value: An Errorcode or 0 for success. GPGME_EOF is returned to
653  *               indicate that there are no more signatures. 
654  **/
655 GpgmeError
656 gpgme_get_sig_key (GpgmeCtx ctx, int idx, GpgmeKey *r_key)
657 {
658   struct ctx_op_data *op_data;
659   VerifyResult result;
660
661   if (!ctx || !r_key)
662     return GPGME_Invalid_Value;
663
664   op_data = ctx->op_data;
665   while (op_data)
666     {
667       while (op_data && op_data->type != OPDATA_VERIFY)
668         op_data = op_data->next;
669       if (idx-- == 0)
670         break;
671       op_data = op_data->next;  
672     }
673   if (!op_data)
674     return GPGME_EOF;
675
676   result = (VerifyResult) op_data->hook;
677
678   return gpgme_get_key (ctx, result->fpr, r_key, 0, 0);
679 }