2003-02-04 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   _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
386
387   /* Check the supplied data.  */
388   if (!sig)
389     {
390       err = GPGME_No_Data;
391       goto leave;
392     }
393   if (!signed_text && !plaintext)
394     {
395       err = GPGME_Invalid_Value;
396       goto leave;
397     }
398   err = _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext);
399
400  leave:
401   if (err)
402     {
403       ctx->pending = 0; 
404       _gpgme_engine_release (ctx->engine);
405       ctx->engine = NULL;
406     }
407   return err;
408 }
409
410
411 GpgmeError
412 gpgme_op_verify_start (GpgmeCtx ctx, GpgmeData sig, GpgmeData signed_text,
413                        GpgmeData plaintext)
414 {
415   return _gpgme_op_verify_start (ctx, 0, sig, signed_text, plaintext);
416 }
417
418
419 /**
420  * gpgme_op_verify:
421  * @c: the context
422  * @sig: the signature data
423  * @text: the signed text
424  * 
425  * Perform a signature check on the signature given in @sig.  If @text
426  * is a new and uninitialized data object, it is assumed that @sig
427  * contains a normal or cleartext signature, and the plaintext is
428  * returned in @text upon successful verification.
429  *
430  * If @text is initialized, it is assumed that @sig is a detached
431  * signature for the material given in @text.
432  *
433  * Return value: 0 on success or an errorcode if something not related to
434  *               the signature itself did go wrong.
435  **/
436 GpgmeError
437 gpgme_op_verify (GpgmeCtx ctx, GpgmeData sig, GpgmeData signed_text,
438                  GpgmeData plaintext)
439 {
440   GpgmeError err;
441
442   gpgme_data_release (ctx->notation);
443   ctx->notation = NULL;
444     
445   err = _gpgme_op_verify_start (ctx, 1, sig, signed_text, plaintext);
446   if (!err)
447     err = _gpgme_wait_one (ctx);
448   return err;
449 }
450
451
452 /**
453  * gpgme_get_sig_status:
454  * @c: Context
455  * @idx: Index of the signature starting at 0
456  * @r_stat: Returns the status
457  * @r_created: Returns the creation timestamp
458  * 
459  * Return information about an already verified signatures. 
460  *
461  * The result of this operation is returned in @r_stat which can take these
462  * values:
463  *  GPGME_SIG_STAT_NONE:  No status - should not happen
464  *  GPGME_SIG_STAT_GOOD:  The signature is valid 
465  *  GPGME_SIG_STAT_BAD:   The signature is not valid
466  *  GPGME_SIG_STAT_NOKEY: The signature could not be checked due to a
467  *                        missing key
468  *  GPGME_SIG_STAT_NOSIG: This is not a signature
469  *  GPGME_SIG_STAT_ERROR: Due to some other error the check could not be done.
470  *  GPGME_SIG_STAT_DIFF:  There is more than 1 signature and they have not
471  *                        the same status.
472  *  GPGME_SIG_STAT_GOOD_EXP:  The signature is good but has expired.
473  *  GPGME_SIG_STAT_GOOD_KEYEXP:  The signature is good but the key has expired.
474  *
475  * 
476  * Return value: The fingerprint or NULL in case of an problem or
477  *               when there are no more signatures.
478  **/
479 const char *
480 gpgme_get_sig_status (GpgmeCtx ctx, int idx,
481                       GpgmeSigStat *r_stat, time_t *r_created)
482 {
483   struct ctx_op_data *op_data;
484   VerifyResult result;
485
486   if (!ctx || ctx->pending)
487     return NULL;        /* No results yet or verification error.  */
488
489   op_data = ctx->op_data;
490   while (op_data)
491     {
492       while (op_data && op_data->type != OPDATA_VERIFY)
493         op_data = op_data->next;
494       if (idx-- == 0)
495         break;
496       op_data = op_data->next;  
497     }
498   if (!op_data)
499     return NULL;        /* No more signatures.  */
500
501   result = (VerifyResult) op_data->hook;
502   if (r_stat)
503     *r_stat = result->status;
504   if (r_created)
505     *r_created = result->timestamp;
506   return result->fpr;
507 }
508
509
510 /* Build a summary vector from RESULT. */
511 static unsigned long
512 calc_sig_summary (VerifyResult result)
513 {
514   unsigned long sum = 0;
515
516   if (result->validity == GPGME_VALIDITY_FULL
517      || result->validity == GPGME_VALIDITY_ULTIMATE)
518     {
519       if (result->status == GPGME_SIG_STAT_GOOD
520           || result->status == GPGME_SIG_STAT_GOOD_EXP
521           || result->status == GPGME_SIG_STAT_GOOD_EXPKEY)
522         sum |= GPGME_SIGSUM_GREEN;
523     }
524   else if (result->validity == GPGME_VALIDITY_NEVER)
525     {
526       if (result->status == GPGME_SIG_STAT_GOOD
527           || result->status == GPGME_SIG_STAT_GOOD_EXP
528           || result->status == GPGME_SIG_STAT_GOOD_EXPKEY)
529         sum |= GPGME_SIGSUM_RED;
530     }
531   else if (result->status == GPGME_SIG_STAT_BAD)
532     sum |= GPGME_SIGSUM_RED;
533
534   /* fixme: handle the case when key and message are expired. */
535   if (result->status == GPGME_SIG_STAT_GOOD_EXP)
536     sum |= GPGME_SIGSUM_SIG_EXPIRED;
537   else if (result->status == GPGME_SIG_STAT_GOOD_EXPKEY)
538     sum |= GPGME_SIGSUM_KEY_EXPIRED;
539   else if (result->status == GPGME_SIG_STAT_NOKEY)
540     sum |= GPGME_SIGSUM_KEY_MISSING;
541   else if (result->status == GPGME_SIG_STAT_ERROR)
542     sum |= GPGME_SIGSUM_SYS_ERROR;
543
544   if ( !strcmp (result->trust_errtok, "Certificate_Revoked"))
545     sum |= GPGME_SIGSUM_KEY_REVOKED;
546   else if ( !strcmp (result->trust_errtok, "No_CRL_Known"))
547     sum |= GPGME_SIGSUM_CRL_MISSING;
548   else if ( !strcmp (result->trust_errtok, "CRL_Too_Old"))
549     sum |= GPGME_SIGSUM_CRL_TOO_OLD;
550   else if ( !strcmp (result->trust_errtok, "No_Policy_Match"))
551     sum |= GPGME_SIGSUM_BAD_POLICY;
552   else if (*result->trust_errtok)
553     sum |= GPGME_SIGSUM_SYS_ERROR;
554
555   if (result->wrong_key_usage)
556     sum |= GPGME_SIGSUM_BAD_POLICY;
557
558   /* Set the valid flag when the signature is unquestionable
559      valid. */
560   if ((sum & GPGME_SIGSUM_GREEN) && !(sum & ~GPGME_SIGSUM_GREEN))
561     sum |= GPGME_SIGSUM_VALID;
562
563   return sum;
564 }
565
566
567 const char *
568 gpgme_get_sig_string_attr (GpgmeCtx ctx, int idx, GpgmeAttr what, int whatidx)
569 {
570   struct ctx_op_data *op_data;
571   VerifyResult result;
572
573   if (!ctx || ctx->pending)
574     return NULL;        /* No results yet or verification error.  */
575
576   op_data = ctx->op_data;
577   while (op_data)
578     {
579       while (op_data && op_data->type != OPDATA_VERIFY)
580         op_data = op_data->next;
581       if (idx-- == 0)
582         break;
583       op_data = op_data->next;  
584     }
585   if (!op_data)
586     return NULL;        /* No more signatures.  */
587
588   result = (VerifyResult) op_data->hook;
589   switch (what)
590     {
591     case GPGME_ATTR_FPR:
592       return result->fpr;
593     case GPGME_ATTR_ERRTOK:
594       if (whatidx == 1)
595         return result->wrong_key_usage? "Wrong_Key_Usage":"";
596       else
597         return result->trust_errtok;
598     default:
599       break;
600     }
601   return NULL;
602 }
603
604
605 unsigned long
606 gpgme_get_sig_ulong_attr (GpgmeCtx ctx, int idx, GpgmeAttr what, int reserved)
607 {
608   struct ctx_op_data *op_data;
609   VerifyResult result;
610
611   if (!ctx || ctx->pending)
612     return 0;   /* No results yet or verification error.  */
613
614   op_data = ctx->op_data;
615   while (op_data)
616     {
617       while (op_data && op_data->type != OPDATA_VERIFY)
618         op_data = op_data->next;
619       if (idx-- == 0)
620         break;
621       op_data = op_data->next;  
622     }
623   if (!op_data)
624     return 0;   /* No more signatures.  */
625
626   result = (VerifyResult) op_data->hook;
627   switch (what)
628     {
629     case GPGME_ATTR_CREATED:
630       return result->timestamp;
631     case GPGME_ATTR_EXPIRE:
632       return result->exptimestamp;
633     case GPGME_ATTR_VALIDITY:
634       return (unsigned long) result->validity;
635     case GPGME_ATTR_SIG_STATUS:
636       return (unsigned long) result->status;
637     case GPGME_ATTR_SIG_SUMMARY:
638       return calc_sig_summary (result);
639     default:
640       break;
641     }
642   return 0;
643 }
644
645
646 /**
647  * gpgme_get_sig_key:
648  * @c: context
649  * @idx: Index of the signature starting at 0
650  * @r_key: Returns the key object
651  * 
652  * Return a key object which was used to check the signature. 
653  * 
654  * Return value: An Errorcode or 0 for success. GPGME_EOF is returned to
655  *               indicate that there are no more signatures. 
656  **/
657 GpgmeError
658 gpgme_get_sig_key (GpgmeCtx ctx, int idx, GpgmeKey *r_key)
659 {
660   struct ctx_op_data *op_data;
661   VerifyResult result;
662
663   if (!ctx || !r_key)
664     return GPGME_Invalid_Value;
665
666   if (ctx->pending)
667     return GPGME_Busy;
668
669   op_data = ctx->op_data;
670   while (op_data)
671     {
672       while (op_data && op_data->type != OPDATA_VERIFY)
673         op_data = op_data->next;
674       if (idx-- == 0)
675         break;
676       op_data = op_data->next;  
677     }
678   if (!op_data)
679     return GPGME_EOF;
680
681   result = (VerifyResult) op_data->hook;
682
683   return gpgme_get_key (ctx, result->fpr, r_key, 0, 0);
684 }