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