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