2003-01-29 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 GpgmeError
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         return GPGME_Out_Of_Core;
131       ctx->result.verify->notation = dh;
132       _gpgme_data_append_string (dh, "  <notation>\n");
133     }
134
135   if (code == GPGME_STATUS_NOTATION_DATA)
136     {
137       if (!ctx->result.verify->notation_in_data)
138         _gpgme_data_append_string (dh, "  <data>");
139       _gpgme_data_append_percentstring_for_xml (dh, data);
140       ctx->result.verify->notation_in_data = 1;
141       return 0;
142     }
143
144   if (ctx->result.verify->notation_in_data)
145     {
146       _gpgme_data_append_string (dh, "</data>\n");
147       ctx->result.verify->notation_in_data = 0;
148     }
149
150   if (code == GPGME_STATUS_NOTATION_NAME)
151     {
152       _gpgme_data_append_string (dh, "  <name>");
153       _gpgme_data_append_percentstring_for_xml (dh, data);
154       _gpgme_data_append_string (dh, "</name>\n");
155     }
156   else if (code == GPGME_STATUS_POLICY_URL)
157     {
158       _gpgme_data_append_string (dh, "  <policy>");
159       _gpgme_data_append_percentstring_for_xml (dh, data);
160       _gpgme_data_append_string (dh, "</policy>\n");
161     }
162   else
163     assert (0);
164   return 0;
165 }
166
167
168 /* Finish a pending signature info collection and prepare for a new
169    signature info collection.  */
170 static GpgmeError
171 finish_sig (GpgmeCtx ctx, int stop)
172 {
173   if (ctx->result.verify->status == GPGME_SIG_STAT_GOOD)
174     ctx->result.verify->status = ctx->result.verify->expstatus;
175
176   if (stop)
177     return 0; /* nothing to do */
178
179   if (ctx->result.verify->collecting)
180     {
181       VerifyResult res2;
182
183       ctx->result.verify->collecting = 0;
184       /* Create a new result structure.  */
185       res2 = calloc (1, sizeof *res2);
186       if (!res2)
187         return GPGME_Out_Of_Core;
188
189       res2->next = ctx->result.verify;
190       ctx->result.verify = res2;
191     }
192     
193   ctx->result.verify->collecting = 1;
194   return 0;
195 }
196
197
198 GpgmeError
199 _gpgme_verify_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
200 {
201   GpgmeError err;
202   char *p;
203   size_t n;
204   int i;
205
206   test_and_allocate_result (ctx, verify);
207
208   if (code == GPGME_STATUS_GOODSIG
209       || code == GPGME_STATUS_EXPSIG
210       || code == GPGME_STATUS_EXPKEYSIG
211       || code == GPGME_STATUS_BADSIG
212       || code == GPGME_STATUS_ERRSIG)
213     {
214       err = finish_sig (ctx,0);
215       if (err)
216         return err;
217     }
218
219   switch (code)
220     {
221     case GPGME_STATUS_NODATA:
222     case GPGME_STATUS_UNEXPECTED:
223       ctx->result.verify->status = GPGME_SIG_STAT_NOSIG;
224       break;
225
226     case GPGME_STATUS_GOODSIG:
227       ctx->result.verify->expstatus = GPGME_SIG_STAT_GOOD;
228       break;
229     
230     case GPGME_STATUS_EXPSIG:
231       ctx->result.verify->expstatus = GPGME_SIG_STAT_GOOD_EXP;
232       break;
233
234     case GPGME_STATUS_EXPKEYSIG:
235       ctx->result.verify->expstatus = GPGME_SIG_STAT_GOOD_EXPKEY;
236       break;
237
238     case GPGME_STATUS_VALIDSIG:
239       ctx->result.verify->status = GPGME_SIG_STAT_GOOD;
240       i = copy_token (args, ctx->result.verify->fpr,
241                       DIM(ctx->result.verify->fpr));
242       /* Skip the formatted date.  */
243       while (args[i] && args[i] == ' ')
244         i++;
245       while (args[i] && args[i] != ' ')
246         i++;
247       /* And get the timestamp.  */
248       ctx->result.verify->timestamp = strtoul (args+i, &p, 10);
249       if (args[i])
250         ctx->result.verify->exptimestamp = strtoul (p, NULL, 10);
251       break;
252
253     case GPGME_STATUS_BADSIG:
254       ctx->result.verify->status = GPGME_SIG_STAT_BAD;
255       /* Store the keyID in the fpr field.  */
256       copy_token (args, ctx->result.verify->fpr,
257                   DIM(ctx->result.verify->fpr));
258       break;
259
260     case GPGME_STATUS_ERRSIG:
261       /* The return code is the 6th argument, if it is 9, the problem
262          is a missing key.  Note that this is not emitted by gpgsm */
263       for (p = args, i = 0; p && *p && i < 5; i++)
264         {
265           p = strchr (p, ' ');
266           if (p)
267             while (*p == ' ')
268               p++;
269         }
270       if (p && *(p++) == '9' && (*p == '\0' || *p == ' '))
271         ctx->result.verify->status = GPGME_SIG_STAT_NOKEY;
272       else
273         ctx->result.verify->status = GPGME_SIG_STAT_ERROR;
274       /* Store the keyID in the fpr field.  */
275       copy_token (args, ctx->result.verify->fpr,
276                   DIM(ctx->result.verify->fpr));
277       break;
278
279     case GPGME_STATUS_NOTATION_NAME:
280     case GPGME_STATUS_NOTATION_DATA:
281     case GPGME_STATUS_POLICY_URL:
282       err = add_notation (ctx, code, args);
283       if (err)
284         return err;
285       break;
286
287     case GPGME_STATUS_TRUST_UNDEFINED:
288       ctx->result.verify->validity = GPGME_VALIDITY_UNKNOWN;
289       copy_token (args, ctx->result.verify->trust_errtok,
290                   DIM(ctx->result.verify->trust_errtok));
291       break;
292     case GPGME_STATUS_TRUST_NEVER:
293       ctx->result.verify->validity = GPGME_VALIDITY_NEVER;
294       copy_token (args, ctx->result.verify->trust_errtok,
295                   DIM(ctx->result.verify->trust_errtok));
296       break;
297     case GPGME_STATUS_TRUST_MARGINAL:
298       if (ctx->result.verify->status == GPGME_SIG_STAT_GOOD)
299         ctx->result.verify->validity = GPGME_VALIDITY_MARGINAL;
300       copy_token (args, ctx->result.verify->trust_errtok,
301                   DIM(ctx->result.verify->trust_errtok));
302       break;
303     case GPGME_STATUS_TRUST_FULLY:
304     case GPGME_STATUS_TRUST_ULTIMATE:
305       if (ctx->result.verify->status == GPGME_SIG_STAT_GOOD)
306         ctx->result.verify->validity = GPGME_VALIDITY_FULL;
307       break;
308
309     case GPGME_STATUS_END_STREAM:
310       break;
311
312     case GPGME_STATUS_ERROR:
313       /* Generic error, we need this for gpgsm (and maybe for gpg in future)
314          to get error descriptions. */
315       if (is_token (args, "verify.findkey", &n) && n)
316         {
317           args += n;
318           if (is_token (args, "No_Public_Key", NULL))
319             ctx->result.verify->status = GPGME_SIG_STAT_NOKEY;
320           else
321             ctx->result.verify->status = GPGME_SIG_STAT_ERROR;
322
323         }
324       else if (skip_token (args, &n) && n)
325         {
326           args += n;
327           if (is_token (args, "Wrong_Key_Usage", NULL))
328             ctx->result.verify->wrong_key_usage = 1;
329         }
330       break;
331
332     case GPGME_STATUS_EOF:
333       err = finish_sig (ctx,1);
334       if (err)
335         return err;
336
337       /* FIXME: Put all notation data into one XML fragment.  */
338       if (ctx->result.verify->notation)
339         {
340           GpgmeData dh = ctx->result.verify->notation;
341
342           if (ctx->result.verify->notation_in_data)
343             {
344               _gpgme_data_append_string (dh, "</data>\n");
345               ctx->result.verify->notation_in_data = 0;
346             }
347           _gpgme_data_append_string (dh, "</notation>\n");
348           ctx->notation = dh;
349           ctx->result.verify->notation = NULL;
350         }
351       break;
352  
353     default:
354       /* Ignore all other codes.  */
355       break;
356     }
357   return 0;
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 = GPGME_No_Data;
379       goto leave;
380     }
381   if (!signed_text && !plaintext)
382     {
383       err = GPGME_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 GPGME_Invalid_Value;
628   if (ctx->pending || !ctx->result.verify)
629     return GPGME_Busy;
630   
631   for (result = ctx->result.verify;
632        result && idx > 0; result = result->next, idx--)
633     ;
634   if (!result)
635     return GPGME_EOF;
636
637   return gpgme_get_key (ctx, result->fpr, r_key, 0, 0);
638 }