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