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