* verify.c (gpgme_op_verify): Make sure that we never access an
[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   char trust_errtok[31]; /* error token send with the trust status */
48 };
49
50
51 void
52 _gpgme_release_verify_result (VerifyResult result)
53 {
54   while (result)
55     {
56       VerifyResult next_result = result->next;
57       gpgme_data_release (result->notation);
58       xfree (result);
59       result = next_result;
60     }
61 }
62
63 /* Check whether STRING starts with TOKEN and return true in this
64    case.  This is case insensitive.  If NEXT is not NULL return the
65    number of bytes to be added to STRING to get to the next token; a
66    returned value of 0 indicates end of line. */
67 static int 
68 is_token (const char *string, const char *token, size_t *next)
69 {
70   size_t n = 0;
71
72   for (;*string && *token && *string == *token; string++, token++, n++)
73     ;
74   if (*token || (*string != ' ' && !*string))
75     return 0;
76   if (next)
77     {
78       for (; *string == ' '; string++, n++)
79         ;
80       *next = n;
81     }
82   return 1;
83 }
84
85 static size_t
86 copy_token (const char *string, char *buffer, size_t length)
87 {
88   const char *s = string;
89   char *p = buffer;
90   size_t i;
91
92   for (i = 1; i < length && *s && *s != ' ' ; i++)
93     *p++ = *s++;
94   *p = 0;
95   /* continue scanning in case the copy was truncated */
96   while (*s && *s != ' ')
97     s++;
98   return s - string;
99 }
100
101
102 /* FIXME: Check that we are adding this to the correct signature.  */
103 static void
104 add_notation (GpgmeCtx ctx, GpgStatusCode code, const char *data)
105 {
106   GpgmeData dh = ctx->result.verify->notation;
107
108   if (!dh)
109     {
110       if (gpgme_data_new (&dh))
111         {
112           ctx->error = mk_error (Out_Of_Core);
113           return;
114         }
115       ctx->result.verify->notation = dh;
116       _gpgme_data_append_string (dh, "  <notation>\n");
117     }
118
119   if (code == STATUS_NOTATION_DATA)
120     {
121       if (!ctx->result.verify->notation_in_data)
122         _gpgme_data_append_string (dh, "  <data>");
123       _gpgme_data_append_percentstring_for_xml (dh, data);
124       ctx->result.verify->notation_in_data = 1;
125       return;
126     }
127
128   if (ctx->result.verify->notation_in_data)
129     {
130       _gpgme_data_append_string (dh, "</data>\n");
131       ctx->result.verify->notation_in_data = 0;
132     }
133
134   if (code == STATUS_NOTATION_NAME)
135     {
136       _gpgme_data_append_string (dh, "  <name>");
137       _gpgme_data_append_percentstring_for_xml (dh, data);
138       _gpgme_data_append_string (dh, "</name>\n");
139     }
140   else if (code == STATUS_POLICY_URL)
141     {
142       _gpgme_data_append_string (dh, "  <policy>");
143       _gpgme_data_append_percentstring_for_xml (dh, data);
144       _gpgme_data_append_string (dh, "</policy>\n");
145     }
146   else
147     assert (0);
148 }
149
150
151 /* 
152  * finish a pending signature info collection and prepare for a new
153  * signature info collection
154  */
155 static void
156 finish_sig (GpgmeCtx ctx, int stop)
157 {
158   if (ctx->result.verify->status == GPGME_SIG_STAT_GOOD)
159     ctx->result.verify->status = ctx->result.verify->expstatus;
160
161   if (stop)
162     return; /* nothing to do */
163
164   if (ctx->result.verify->collecting)
165     {
166       VerifyResult res2;
167
168       ctx->result.verify->collecting = 0;
169       /* Create a new result structure.  */
170       res2 = xtrycalloc (1, sizeof *res2);
171       if (!res2)
172         {
173           ctx->error = mk_error (Out_Of_Core);
174           return;
175         }
176
177       res2->next = ctx->result.verify;
178       ctx->result.verify = res2;
179     }
180     
181   ctx->result.verify->collecting = 1;
182 }
183
184
185 void
186 _gpgme_verify_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args)
187 {
188   char *p;
189   size_t n;
190   int i;
191
192   if (ctx->error)
193     return;
194   test_and_allocate_result (ctx, verify);
195
196   if (code == STATUS_GOODSIG
197       || code == STATUS_EXPSIG
198       || code == STATUS_EXPKEYSIG
199       || code == STATUS_BADSIG
200       || code == STATUS_ERRSIG)
201     {
202       finish_sig (ctx,0);
203       if (ctx->error)
204         return;
205     }
206
207   switch (code)
208     {
209     case STATUS_NODATA:
210       ctx->result.verify->status = GPGME_SIG_STAT_NOSIG;
211       break;
212
213     case STATUS_GOODSIG:
214       ctx->result.verify->expstatus = GPGME_SIG_STAT_GOOD;
215       break;
216     
217     case STATUS_EXPSIG:
218       ctx->result.verify->expstatus = GPGME_SIG_STAT_GOOD_EXP;
219       break;
220
221     case STATUS_EXPKEYSIG:
222       ctx->result.verify->expstatus = GPGME_SIG_STAT_GOOD_EXPKEY;
223       break;
224
225     case STATUS_VALIDSIG:
226       ctx->result.verify->status = GPGME_SIG_STAT_GOOD;
227       i = copy_token (args, ctx->result.verify->fpr,
228                       DIM(ctx->result.verify->fpr));
229       /* Skip the formatted date.  */
230       while (args[i] && args[i] == ' ')
231         i++;
232       while (args[i] && args[i] != ' ')
233         i++;
234       /* And get the timestamp.  */
235       ctx->result.verify->timestamp = strtoul (args+i, &p, 10);
236       if (args[i])
237         ctx->result.verify->exptimestamp = strtoul (p, NULL, 10);
238       break;
239
240     case STATUS_BADSIG:
241       ctx->result.verify->status = GPGME_SIG_STAT_BAD;
242       /* Store the keyID in the fpr field.  */
243       copy_token (args, ctx->result.verify->fpr,
244                   DIM(ctx->result.verify->fpr));
245       break;
246
247     case STATUS_ERRSIG:
248       /* The return code is the 6th argument, if it is 9, the problem
249          is a missing key.  Note that this is not emitted by gpgsm */
250       for (p = args, i = 0; p && *p && i < 5; i++)
251         {
252           p = strchr (p, ' ');
253           if (p)
254             while (*p == ' ')
255               p++;
256         }
257       if (p && *(p++) == '9' && (*p == '\0' || *p == ' '))
258         ctx->result.verify->status = GPGME_SIG_STAT_NOKEY;
259       else
260         ctx->result.verify->status = GPGME_SIG_STAT_ERROR;
261       /* Store the keyID in the fpr field.  */
262       copy_token (args, ctx->result.verify->fpr,
263                   DIM(ctx->result.verify->fpr));
264       break;
265
266     case STATUS_NOTATION_NAME:
267     case STATUS_NOTATION_DATA:
268     case STATUS_POLICY_URL:
269       add_notation (ctx, code, args);
270       break;
271
272     case STATUS_TRUST_UNDEFINED:
273       ctx->result.verify->validity = GPGME_VALIDITY_UNKNOWN;
274       copy_token (args, ctx->result.verify->trust_errtok,
275                   DIM(ctx->result.verify->trust_errtok));
276       break;
277     case STATUS_TRUST_NEVER:
278       ctx->result.verify->validity = GPGME_VALIDITY_NEVER;
279       copy_token (args, ctx->result.verify->trust_errtok,
280                   DIM(ctx->result.verify->trust_errtok));
281       break;
282     case STATUS_TRUST_MARGINAL:
283       if (ctx->result.verify->status == GPGME_SIG_STAT_GOOD)
284         ctx->result.verify->validity = GPGME_VALIDITY_MARGINAL;
285       copy_token (args, ctx->result.verify->trust_errtok,
286                   DIM(ctx->result.verify->trust_errtok));
287       break;
288     case STATUS_TRUST_FULLY:
289     case STATUS_TRUST_ULTIMATE:
290       if (ctx->result.verify->status == GPGME_SIG_STAT_GOOD)
291         ctx->result.verify->validity = GPGME_VALIDITY_FULL;
292       break;
293
294     case STATUS_END_STREAM:
295       break;
296
297     case STATUS_ERROR:
298       /* Generic error, we need this for gpgsm (and maybe for gpg in future)
299          to get error descriptions. */
300       if (is_token (args, "verify.findkey", &n) && n)
301         {
302           args += n;
303           if (is_token (args, "No_Public_Key", NULL))
304             ctx->result.verify->status = GPGME_SIG_STAT_NOKEY;
305           else
306             ctx->result.verify->status = GPGME_SIG_STAT_ERROR;
307
308         }
309       break;
310
311     case STATUS_EOF:
312       finish_sig (ctx,1);
313
314       /* FIXME: Put all notation data into one XML fragment.  */
315       if (ctx->result.verify->notation)
316         {
317           GpgmeData dh = ctx->result.verify->notation;
318
319           if (ctx->result.verify->notation_in_data)
320             {
321               _gpgme_data_append_string (dh, "</data>\n");
322               ctx->result.verify->notation_in_data = 0;
323             }
324           _gpgme_data_append_string (dh, "</notation>\n");
325           ctx->notation = dh;
326           ctx->result.verify->notation = NULL;
327         }
328       break;
329  
330     default:
331       /* Ignore all other codes.  */
332       break;
333     }
334 }
335
336 static GpgmeError
337 _gpgme_op_verify_start (GpgmeCtx ctx, int synchronous,
338                         GpgmeData sig, GpgmeData text)
339 {
340   int err = 0;
341   int pipemode = 0;      /* !!text; use pipemode for detached sigs.  */
342
343   if (!pipemode)
344     ;   /* XXX I am not sure what should happen/not happen in
345            pipemode.  */
346
347   err = _gpgme_op_reset (ctx, synchronous);
348   if (err)
349     goto leave;
350
351 #if 0   /* FIXME */
352   if (pipemode)
353     _gpgme_gpg_enable_pipemode (c->engine->engine.gpg);
354 #endif
355
356   _gpgme_engine_set_status_handler (ctx->engine, _gpgme_verify_status_handler,
357                                     ctx);
358   _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
359
360   /* Check the supplied data.  */
361   if (gpgme_data_get_type (sig) == GPGME_DATA_TYPE_NONE)
362     {
363       err = mk_error (No_Data);
364       goto leave;
365     }
366   if (!text)
367     {
368       err = mk_error (Invalid_Value);
369       goto leave;
370     }
371   _gpgme_data_set_mode (sig, GPGME_DATA_MODE_OUT);
372   if (gpgme_data_get_type (text) == GPGME_DATA_TYPE_NONE)
373     /* Normal or cleartext signature.  */
374     _gpgme_data_set_mode (text, GPGME_DATA_MODE_IN);
375   else
376     /* Detached signature.  */
377     _gpgme_data_set_mode (text, GPGME_DATA_MODE_OUT);
378
379   err = _gpgme_engine_op_verify (ctx->engine, sig, text);
380   if (!err)     /* And kick off the process.  */
381     err = _gpgme_engine_start (ctx->engine, ctx);
382
383  leave:
384   if (err)
385     {
386       ctx->pending = 0; 
387       _gpgme_engine_release (ctx->engine);
388       ctx->engine = NULL;
389     }
390   return err;
391 }
392
393 GpgmeError
394 gpgme_op_verify_start (GpgmeCtx ctx, GpgmeData sig, GpgmeData text)
395 {
396   return _gpgme_op_verify_start (ctx, 0, sig, text);
397 }
398
399 /* 
400  * Figure out a common status value for all signatures 
401  */
402 GpgmeSigStat
403 _gpgme_intersect_stati (VerifyResult result)
404 {
405   GpgmeSigStat status = result->status;
406
407   for (result = result->next; result; result = result->next)
408     {
409       if (status != result->status) 
410         return GPGME_SIG_STAT_DIFF;
411     }
412   return status;
413 }
414
415 /**
416  * gpgme_op_verify:
417  * @c: the context
418  * @sig: the signature data
419  * @text: the signed text
420  * @r_stat: returns the status of the signature
421  * 
422  * Perform a signature check on the signature given in @sig.  If @text
423  * is a new and uninitialized data object, it is assumed that @sig
424  * contains a normal or cleartext signature, and the plaintext is
425  * returned in @text upon successful verification.
426  *
427  * If @text is initialized, it is assumed that @sig is a detached
428  * signature for the material given in @text.
429  *
430  * The result of this operation is returned in @r_stat which can take these
431  * values:
432  *  GPGME_SIG_STAT_NONE:  No status - should not happen
433  *  GPGME_SIG_STAT_GOOD:  The signature is valid 
434  *  GPGME_SIG_STAT_BAD:   The signature is not valid
435  *  GPGME_SIG_STAT_NOKEY: The signature could not be checked due to a
436  *                        missing key
437  *  GPGME_SIG_STAT_NOSIG: This is not a signature
438  *  GPGME_SIG_STAT_ERROR: Due to some other error the check could not be done.
439  *  GPGME_SIG_STAT_DIFF:  There is more than 1 signature and they have not
440  *                        the same status.
441  *  GPGME_SIG_STAT_GOOD_EXP:  The signature is good but has expired.
442  *  GPGME_SIG_STAT_GOOD_KEYEXP:  The signature is good but the key has expired.
443  *
444  * Return value: 0 on success or an errorcode if something not related to
445  *               the signature itself did go wrong.
446  **/
447 GpgmeError
448 gpgme_op_verify (GpgmeCtx ctx, GpgmeData sig, GpgmeData text,
449                  GpgmeSigStat *r_stat)
450 {
451   GpgmeError err;
452
453   if (!r_stat)
454     return mk_error (Invalid_Value);
455
456   gpgme_data_release (ctx->notation);
457   ctx->notation = NULL;
458     
459   *r_stat = GPGME_SIG_STAT_NONE;
460   err = _gpgme_op_verify_start (ctx, 1, sig, text);
461   if (!err)
462     {
463       err = _gpgme_wait_one (ctx);
464       if (!err && ctx->result.verify)
465         *r_stat = _gpgme_intersect_stati (ctx->result.verify);
466     }
467     return err;
468 }
469
470
471 /**
472  * gpgme_get_sig_status:
473  * @c: Context
474  * @idx: Index of the signature starting at 0
475  * @r_stat: Returns the status
476  * @r_created: Returns the creation timestamp
477  * 
478  * Return information about an already verified signatures. 
479  * 
480  * Return value: The fingerprint or NULL in case of an problem or
481  *               when there are no more signatures.
482  **/
483 const char *
484 gpgme_get_sig_status (GpgmeCtx c, int idx,
485                       GpgmeSigStat *r_stat, time_t *r_created)
486 {
487   VerifyResult result;
488
489   if (!c || c->pending || !c->result.verify)
490     return NULL;        /* No results yet or verification error.  */
491
492   for (result = c->result.verify;
493        result && idx > 0; result = result->next, idx--)
494     ;
495   if (!result)
496     return NULL;        /* No more signatures.  */
497
498   if (r_stat)
499     *r_stat = result->status;
500   if (r_created)
501     *r_created = result->timestamp;
502   return result->fpr;
503 }
504
505
506 /* Build a summary vector from RESULT. */
507 static unsigned long
508 calc_sig_summary (VerifyResult result)
509 {
510   unsigned long sum = 0;
511
512   if (result->validity == GPGME_VALIDITY_FULL
513      || result->validity == GPGME_VALIDITY_ULTIMATE)
514     {
515       if (result->status == GPGME_SIG_STAT_GOOD
516           || result->status == GPGME_SIG_STAT_GOOD_EXP
517           || result->status == GPGME_SIG_STAT_GOOD_EXPKEY)
518         sum |= GPGME_SIGSUM_GREEN;
519     }
520   else if (result->validity == GPGME_VALIDITY_NEVER)
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_RED;
526     }
527   else if (result->status == GPGME_SIG_STAT_BAD)
528     sum |= GPGME_SIGSUM_RED;
529
530   /* fixme: handle the case when key and message are expired. */
531   if (result->status == GPGME_SIG_STAT_GOOD_EXP)
532     sum |= GPGME_SIGSUM_SIG_EXPIRED;
533   else if (result->status == GPGME_SIG_STAT_GOOD_EXPKEY)
534     sum |= GPGME_SIGSUM_KEY_EXPIRED;
535   else if (result->status == GPGME_SIG_STAT_NOKEY)
536     sum |= GPGME_SIGSUM_KEY_MISSING;
537   else if (result->status == GPGME_SIG_STAT_ERROR)
538     sum |= GPGME_SIGSUM_SYS_ERROR;
539
540   if ( !strcmp (result->trust_errtok, "Certificate_Revoked"))
541     sum |= GPGME_SIGSUM_KEY_REVOKED;
542   else if ( !strcmp (result->trust_errtok, "No_CRL_Known"))
543     sum |= GPGME_SIGSUM_CRL_MISSING;
544   else if ( !strcmp (result->trust_errtok, "CRL_Too_Old"))
545     sum |= GPGME_SIGSUM_CRL_TOO_OLD;
546   else if ( !strcmp (result->trust_errtok, "No_Policy_Match"))
547     sum |= GPGME_SIGSUM_BAD_POLICY;
548   else if (*result->trust_errtok)
549     sum |= GPGME_SIGSUM_SYS_ERROR;
550
551   /* Set the valid flag when the signature is unquestionable
552      valid. */
553   if ((sum & GPGME_SIGSUM_GREEN) && !(sum & ~GPGME_SIGSUM_GREEN))
554     sum |= GPGME_SIGSUM_VALID;
555
556   return sum;
557 }
558
559
560 const char *
561 gpgme_get_sig_string_attr (GpgmeCtx c, int idx, GpgmeAttr what, int reserved)
562 {
563   VerifyResult result;
564
565   if (!c || c->pending || !c->result.verify)
566     return NULL;        /* No results yet or verification error.  */
567   if (reserved)
568     return NULL; /* We might want to use it to enumerate attributes of
569                     one signature */
570   for (result = c->result.verify;
571        result && idx > 0; result = result->next, idx--)
572     ;
573   if (!result)
574     return NULL;        /* No more signatures.  */
575
576   switch (what)
577     {
578     case GPGME_ATTR_FPR:
579       return result->fpr;
580     case GPGME_ATTR_ERRTOK:
581       return result->trust_errtok;
582     default:
583       break;
584     }
585   return NULL;
586 }
587
588 unsigned long
589 gpgme_get_sig_ulong_attr (GpgmeCtx c, int idx, GpgmeAttr what, int reserved)
590 {
591   VerifyResult result;
592
593   if (!c || c->pending || !c->result.verify)
594     return 0;   /* No results yet or verification error.  */
595   if (reserved)
596     return 0; 
597   for (result = c->result.verify;
598        result && idx > 0; result = result->next, idx--)
599     ;
600   if (!result)
601     return 0;   /* No more signatures.  */
602
603   switch (what)
604     {
605     case GPGME_ATTR_CREATED:
606       return result->timestamp;
607     case GPGME_ATTR_EXPIRE:
608       return result->exptimestamp;
609     case GPGME_ATTR_VALIDITY:
610       return (unsigned long)result->validity;
611     case GPGME_ATTR_SIG_STATUS:
612       return (unsigned long)result->status;
613     case GPGME_ATTR_SIG_SUMMARY:
614       return calc_sig_summary (result);
615     default:
616       break;
617     }
618   return 0;
619 }
620
621
622
623 /**
624  * gpgme_get_sig_key:
625  * @c: context
626  * @idx: Index of the signature starting at 0
627  * @r_key: Returns the key object
628  * 
629  * Return a key object which was used to check the signature. 
630  * 
631  * Return value: An Errorcode or 0 for success. GPGME_EOF is returned to
632  *               indicate that there are no more signatures. 
633  **/
634 GpgmeError
635 gpgme_get_sig_key (GpgmeCtx c, int idx, GpgmeKey *r_key)
636 {
637   VerifyResult result;
638   GpgmeError err = 0;
639
640   if (!c || !r_key)
641     return mk_error (Invalid_Value);
642   if (c->pending || !c->result.verify)
643     return mk_error (Busy);
644   
645   for (result = c->result.verify;
646        result && idx > 0; result = result->next, idx--)
647     ;
648   if (!result)
649     return mk_error (EOF);
650   
651   if (strlen(result->fpr) < 16) /* We have at least a key ID.  */
652     return mk_error (Invalid_Key);
653   
654   *r_key = _gpgme_key_cache_get (result->fpr);
655   if (!*r_key)
656     {
657       GpgmeCtx listctx;
658       
659       /* Fixme: This can be optimized by keeping an internal context
660          used for such key listings.  */
661       err = gpgme_new (&listctx);
662       if (err)
663         return err;
664       gpgme_set_protocol (listctx, gpgme_get_protocol (c));
665       gpgme_set_keylist_mode (listctx, c->keylist_mode);
666       err = gpgme_op_keylist_start (listctx, result->fpr, 0);
667       if (!err)
668         err = gpgme_op_keylist_next (listctx, r_key);
669       gpgme_release (listctx);
670     }
671   return err;
672 }
673