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