2002-11-21 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 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_s
36 {
37   struct verify_result_s *next;
38   GpgmeSigStat status;
39   GpgmeSigStat expstatus;       /* Only used by finish_sig.  */
40   GpgmeData notation;           /* We store an XML fragment here.  */
41   int collecting;               /* Private to finish_sig().  */
42   int notation_in_data;         /* Private to add_notation().  */
43   char fpr[41];                 /* Fingerprint of a good signature or keyid of
44                                    a bad one.  */
45   ulong timestamp;              /* Signature creation time.  */
46   ulong exptimestamp;           /* Signature exipration time or 0.  */
47   GpgmeValidity validity;
48   int wrong_key_usage;  
49   char trust_errtok[31];        /* Error token send with the trust status.  */
50 };
51
52
53 void
54 _gpgme_release_verify_result (VerifyResult result)
55 {
56   while (result)
57     {
58       VerifyResult next_result = result->next;
59       gpgme_data_release (result->notation);
60       free (result);
61       result = next_result;
62     }
63 }
64
65 /* Check whether STRING starts with TOKEN and return true in this
66    case.  This is case insensitive.  If NEXT is not NULL return the
67    number of bytes to be added to STRING to get to the next token; a
68    returned value of 0 indicates end of line.  */
69 static int 
70 is_token (const char *string, const char *token, size_t *next)
71 {
72   size_t n = 0;
73
74   for (;*string && *token && *string == *token; string++, token++, n++)
75     ;
76   if (*token || (*string != ' ' && !*string))
77     return 0;
78   if (next)
79     {
80       for (; *string == ' '; string++, n++)
81         ;
82       *next = n;
83     }
84   return 1;
85 }
86
87 static int
88 skip_token (const char *string, size_t *next)
89 {
90   size_t n = 0;
91
92   for (;*string && *string != ' '; string++, n++)
93     ;
94   for (;*string == ' '; string++, n++)
95     ;
96   if (!*string)
97     return 0;
98   if (next)
99     *next = n;
100   return 1;
101 }
102
103
104 static size_t
105 copy_token (const char *string, char *buffer, size_t length)
106 {
107   const char *s = string;
108   char *p = buffer;
109   size_t i;
110
111   for (i = 1; i < length && *s && *s != ' ' ; i++)
112     *p++ = *s++;
113   *p = 0;
114   /* continue scanning in case the copy was truncated */
115   while (*s && *s != ' ')
116     s++;
117   return s - string;
118 }
119
120
121 /* FIXME: Check that we are adding this to the correct signature.  */
122 static void
123 add_notation (GpgmeCtx ctx, GpgmeStatusCode code, const char *data)
124 {
125   GpgmeData dh = ctx->result.verify->notation;
126
127   if (!dh)
128     {
129       if (gpgme_data_new (&dh))
130         {
131           ctx->error = mk_error (Out_Of_Core);
132           return;
133         }
134       ctx->result.verify->notation = dh;
135       _gpgme_data_append_string (dh, "  <notation>\n");
136     }
137
138   if (code == GPGME_STATUS_NOTATION_DATA)
139     {
140       if (!ctx->result.verify->notation_in_data)
141         _gpgme_data_append_string (dh, "  <data>");
142       _gpgme_data_append_percentstring_for_xml (dh, data);
143       ctx->result.verify->notation_in_data = 1;
144       return;
145     }
146
147   if (ctx->result.verify->notation_in_data)
148     {
149       _gpgme_data_append_string (dh, "</data>\n");
150       ctx->result.verify->notation_in_data = 0;
151     }
152
153   if (code == GPGME_STATUS_NOTATION_NAME)
154     {
155       _gpgme_data_append_string (dh, "  <name>");
156       _gpgme_data_append_percentstring_for_xml (dh, data);
157       _gpgme_data_append_string (dh, "</name>\n");
158     }
159   else if (code == GPGME_STATUS_POLICY_URL)
160     {
161       _gpgme_data_append_string (dh, "  <policy>");
162       _gpgme_data_append_percentstring_for_xml (dh, data);
163       _gpgme_data_append_string (dh, "</policy>\n");
164     }
165   else
166     assert (0);
167 }
168
169
170 /* Finish a pending signature info collection and prepare for a new
171    signature info collection.  */
172 static void
173 finish_sig (GpgmeCtx ctx, int stop)
174 {
175   if (ctx->result.verify->status == GPGME_SIG_STAT_GOOD)
176     ctx->result.verify->status = ctx->result.verify->expstatus;
177
178   if (stop)
179     return; /* nothing to do */
180
181   if (ctx->result.verify->collecting)
182     {
183       VerifyResult res2;
184
185       ctx->result.verify->collecting = 0;
186       /* Create a new result structure.  */
187       res2 = calloc (1, sizeof *res2);
188       if (!res2)
189         {
190           ctx->error = mk_error (Out_Of_Core);
191           return;
192         }
193
194       res2->next = ctx->result.verify;
195       ctx->result.verify = res2;
196     }
197     
198   ctx->result.verify->collecting = 1;
199 }
200
201
202 void
203 _gpgme_verify_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
204 {
205   char *p;
206   size_t n;
207   int i;
208
209   if (ctx->error)
210     return;
211   test_and_allocate_result (ctx, verify);
212
213   if (code == GPGME_STATUS_GOODSIG
214       || code == GPGME_STATUS_EXPSIG
215       || code == GPGME_STATUS_EXPKEYSIG
216       || code == GPGME_STATUS_BADSIG
217       || code == GPGME_STATUS_ERRSIG)
218     {
219       finish_sig (ctx,0);
220       if (ctx->error)
221         return;
222     }
223
224   switch (code)
225     {
226     case GPGME_STATUS_NODATA:
227     case GPGME_STATUS_UNEXPECTED:
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
361 static GpgmeError
362 _gpgme_op_verify_start (GpgmeCtx ctx, int synchronous,
363                         GpgmeData sig, GpgmeData signed_text, GpgmeData plaintext)
364 {
365   int err = 0;
366
367   err = _gpgme_op_reset (ctx, synchronous);
368   if (err)
369     goto leave;
370
371   _gpgme_engine_set_status_handler (ctx->engine, _gpgme_verify_status_handler,
372                                     ctx);
373   _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
374
375   /* Check the supplied data.  */
376   if (!sig)
377     {
378       err = mk_error (No_Data);
379       goto leave;
380     }
381   if (!signed_text && !plaintext)
382     {
383       err = mk_error (Invalid_Value);
384       goto leave;
385     }
386   err = _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext);
387   if (!err)     /* And kick off the process.  */
388     err = _gpgme_engine_start (ctx->engine, ctx);
389
390  leave:
391   if (err)
392     {
393       ctx->pending = 0; 
394       _gpgme_engine_release (ctx->engine);
395       ctx->engine = NULL;
396     }
397   return err;
398 }
399
400
401 GpgmeError
402 gpgme_op_verify_start (GpgmeCtx ctx, GpgmeData sig, GpgmeData signed_text,
403                        GpgmeData plaintext)
404 {
405   return _gpgme_op_verify_start (ctx, 0, sig, signed_text, plaintext);
406 }
407
408
409 /* Figure out a common status value for all signatures.  */
410 GpgmeSigStat
411 _gpgme_intersect_stati (VerifyResult result)
412 {
413   GpgmeSigStat status = result->status;
414
415   for (result = result->next; result; result = result->next)
416     {
417       if (status != result->status) 
418         return GPGME_SIG_STAT_DIFF;
419     }
420   return status;
421 }
422
423 /**
424  * gpgme_op_verify:
425  * @c: the context
426  * @sig: the signature data
427  * @text: the signed text
428  * @r_stat: returns the status of the signature
429  * 
430  * Perform a signature check on the signature given in @sig.  If @text
431  * is a new and uninitialized data object, it is assumed that @sig
432  * contains a normal or cleartext signature, and the plaintext is
433  * returned in @text upon successful verification.
434  *
435  * If @text is initialized, it is assumed that @sig is a detached
436  * signature for the material given in @text.
437  *
438  * The result of this operation is returned in @r_stat which can take these
439  * values:
440  *  GPGME_SIG_STAT_NONE:  No status - should not happen
441  *  GPGME_SIG_STAT_GOOD:  The signature is valid 
442  *  GPGME_SIG_STAT_BAD:   The signature is not valid
443  *  GPGME_SIG_STAT_NOKEY: The signature could not be checked due to a
444  *                        missing key
445  *  GPGME_SIG_STAT_NOSIG: This is not a signature
446  *  GPGME_SIG_STAT_ERROR: Due to some other error the check could not be done.
447  *  GPGME_SIG_STAT_DIFF:  There is more than 1 signature and they have not
448  *                        the same status.
449  *  GPGME_SIG_STAT_GOOD_EXP:  The signature is good but has expired.
450  *  GPGME_SIG_STAT_GOOD_KEYEXP:  The signature is good but the key has expired.
451  *
452  * Return value: 0 on success or an errorcode if something not related to
453  *               the signature itself did go wrong.
454  **/
455 GpgmeError
456 gpgme_op_verify (GpgmeCtx ctx, GpgmeData sig, GpgmeData signed_text,
457                  GpgmeData plaintext, GpgmeSigStat *r_stat)
458 {
459   GpgmeError err;
460
461   if (!r_stat)
462     return mk_error (Invalid_Value);
463
464   gpgme_data_release (ctx->notation);
465   ctx->notation = NULL;
466     
467   *r_stat = GPGME_SIG_STAT_NONE;
468   err = _gpgme_op_verify_start (ctx, 1, sig, signed_text, plaintext);
469   if (!err)
470     {
471       err = _gpgme_wait_one (ctx);
472       if (!err && ctx->result.verify)
473         *r_stat = _gpgme_intersect_stati (ctx->result.verify);
474     }
475     return err;
476 }
477
478
479 /**
480  * gpgme_get_sig_status:
481  * @c: Context
482  * @idx: Index of the signature starting at 0
483  * @r_stat: Returns the status
484  * @r_created: Returns the creation timestamp
485  * 
486  * Return information about an already verified signatures. 
487  * 
488  * Return value: The fingerprint or NULL in case of an problem or
489  *               when there are no more signatures.
490  **/
491 const char *
492 gpgme_get_sig_status (GpgmeCtx c, int idx,
493                       GpgmeSigStat *r_stat, time_t *r_created)
494 {
495   VerifyResult result;
496
497   if (!c || c->pending || !c->result.verify)
498     return NULL;        /* No results yet or verification error.  */
499
500   for (result = c->result.verify;
501        result && idx > 0; result = result->next, idx--)
502     ;
503   if (!result)
504     return NULL;        /* No more signatures.  */
505
506   if (r_stat)
507     *r_stat = result->status;
508   if (r_created)
509     *r_created = result->timestamp;
510   return result->fpr;
511 }
512
513
514 /* Build a summary vector from RESULT. */
515 static unsigned long
516 calc_sig_summary (VerifyResult result)
517 {
518   unsigned long sum = 0;
519
520   if (result->validity == GPGME_VALIDITY_FULL
521      || result->validity == GPGME_VALIDITY_ULTIMATE)
522     {
523       if (result->status == GPGME_SIG_STAT_GOOD
524           || result->status == GPGME_SIG_STAT_GOOD_EXP
525           || result->status == GPGME_SIG_STAT_GOOD_EXPKEY)
526         sum |= GPGME_SIGSUM_GREEN;
527     }
528   else if (result->validity == GPGME_VALIDITY_NEVER)
529     {
530       if (result->status == GPGME_SIG_STAT_GOOD
531           || result->status == GPGME_SIG_STAT_GOOD_EXP
532           || result->status == GPGME_SIG_STAT_GOOD_EXPKEY)
533         sum |= GPGME_SIGSUM_RED;
534     }
535   else if (result->status == GPGME_SIG_STAT_BAD)
536     sum |= GPGME_SIGSUM_RED;
537
538   /* fixme: handle the case when key and message are expired. */
539   if (result->status == GPGME_SIG_STAT_GOOD_EXP)
540     sum |= GPGME_SIGSUM_SIG_EXPIRED;
541   else if (result->status == GPGME_SIG_STAT_GOOD_EXPKEY)
542     sum |= GPGME_SIGSUM_KEY_EXPIRED;
543   else if (result->status == GPGME_SIG_STAT_NOKEY)
544     sum |= GPGME_SIGSUM_KEY_MISSING;
545   else if (result->status == GPGME_SIG_STAT_ERROR)
546     sum |= GPGME_SIGSUM_SYS_ERROR;
547
548   if ( !strcmp (result->trust_errtok, "Certificate_Revoked"))
549     sum |= GPGME_SIGSUM_KEY_REVOKED;
550   else if ( !strcmp (result->trust_errtok, "No_CRL_Known"))
551     sum |= GPGME_SIGSUM_CRL_MISSING;
552   else if ( !strcmp (result->trust_errtok, "CRL_Too_Old"))
553     sum |= GPGME_SIGSUM_CRL_TOO_OLD;
554   else if ( !strcmp (result->trust_errtok, "No_Policy_Match"))
555     sum |= GPGME_SIGSUM_BAD_POLICY;
556   else if (*result->trust_errtok)
557     sum |= GPGME_SIGSUM_SYS_ERROR;
558
559   if (result->wrong_key_usage)
560     sum |= GPGME_SIGSUM_BAD_POLICY;
561
562   /* Set the valid flag when the signature is unquestionable
563      valid. */
564   if ((sum & GPGME_SIGSUM_GREEN) && !(sum & ~GPGME_SIGSUM_GREEN))
565     sum |= GPGME_SIGSUM_VALID;
566
567   return sum;
568 }
569
570
571 const char *
572 gpgme_get_sig_string_attr (GpgmeCtx c, int idx, GpgmeAttr what, int whatidx)
573 {
574   VerifyResult result;
575
576   if (!c || c->pending || !c->result.verify)
577     return NULL;        /* No results yet or verification error.  */
578
579   for (result = c->result.verify;
580        result && idx > 0; result = result->next, idx--)
581     ;
582   if (!result)
583     return NULL;        /* No more signatures.  */
584
585   switch (what)
586     {
587     case GPGME_ATTR_FPR:
588       return result->fpr;
589     case GPGME_ATTR_ERRTOK:
590       if (whatidx == 1)
591         return result->wrong_key_usage? "Wrong_Key_Usage":"";
592       else
593         return result->trust_errtok;
594     default:
595       break;
596     }
597   return NULL;
598 }
599
600
601 unsigned long
602 gpgme_get_sig_ulong_attr (GpgmeCtx c, int idx, GpgmeAttr what, int reserved)
603 {
604   VerifyResult result;
605
606   if (!c || c->pending || !c->result.verify)
607     return 0;   /* No results yet or verification error.  */
608
609   for (result = c->result.verify;
610        result && idx > 0; result = result->next, idx--)
611     ;
612   if (!result)
613     return 0;   /* No more signatures.  */
614
615   switch (what)
616     {
617     case GPGME_ATTR_CREATED:
618       return result->timestamp;
619     case GPGME_ATTR_EXPIRE:
620       return result->exptimestamp;
621     case GPGME_ATTR_VALIDITY:
622       return (unsigned long)result->validity;
623     case GPGME_ATTR_SIG_STATUS:
624       return (unsigned long)result->status;
625     case GPGME_ATTR_SIG_SUMMARY:
626       return calc_sig_summary (result);
627     default:
628       break;
629     }
630   return 0;
631 }
632
633
634 /**
635  * gpgme_get_sig_key:
636  * @c: context
637  * @idx: Index of the signature starting at 0
638  * @r_key: Returns the key object
639  * 
640  * Return a key object which was used to check the signature. 
641  * 
642  * Return value: An Errorcode or 0 for success. GPGME_EOF is returned to
643  *               indicate that there are no more signatures. 
644  **/
645 GpgmeError
646 gpgme_get_sig_key (GpgmeCtx c, int idx, GpgmeKey *r_key)
647 {
648   VerifyResult result;
649   GpgmeError err = 0;
650
651   if (!c || !r_key)
652     return mk_error (Invalid_Value);
653   if (c->pending || !c->result.verify)
654     return mk_error (Busy);
655   
656   for (result = c->result.verify;
657        result && idx > 0; result = result->next, idx--)
658     ;
659   if (!result)
660     return mk_error (EOF);
661   
662   if (strlen(result->fpr) < 16) /* We have at least a key ID.  */
663     return mk_error (Invalid_Key);
664   
665   *r_key = _gpgme_key_cache_get (result->fpr);
666   if (!*r_key)
667     {
668       GpgmeCtx listctx;
669       
670       /* Fixme: This can be optimized by keeping an internal context
671          used for such key listings.  */
672       err = gpgme_new (&listctx);
673       if (err)
674         return err;
675       gpgme_set_protocol (listctx, gpgme_get_protocol (c));
676       gpgme_set_keylist_mode (listctx, c->keylist_mode);
677       err = gpgme_op_keylist_start (listctx, result->fpr, 0);
678       if (!err)
679         err = gpgme_op_keylist_next (listctx, r_key);
680       gpgme_release (listctx);
681     }
682   return err;
683 }
684