f1f6cd7e09fd6ec896defa7ac37f49cae76c0cdb
[gpgme.git] / gpgme / verify.c
1 /* verify.c -  signature verification
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *      Copyright (C) 2001 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 struct verify_result_s
34 {
35   struct verify_result_s *next;
36   GpgmeSigStat status;
37   GpgmeData notation;   /* We store an XML fragment here.  */
38   int collecting;       /* Private to finish_sig().  */
39   int notation_in_data; /* Private to add_notation().  */
40   char fpr[41];         /* Fingerprint of a good signature or keyid of
41                            a bad one.  */
42   ulong timestamp;      /* Signature creation time.  */
43 };
44
45
46 void
47 _gpgme_release_verify_result (VerifyResult result)
48 {
49   while (result)
50     {
51       VerifyResult next_result = result->next;
52       gpgme_data_release (result->notation);
53       xfree (result);
54       result = next_result;
55     }
56 }
57
58 /* FIXME: Check that we are adding this to the correct signature.  */
59 static void
60 add_notation ( GpgmeCtx ctx, GpgStatusCode code, const char *data )
61 {
62     GpgmeData dh = ctx->result.verify->notation;
63
64     if ( !dh ) {
65         if ( gpgme_data_new ( &dh ) ) {
66             ctx->out_of_core = 1;
67             return;
68         }
69         ctx->result.verify->notation = dh;
70         _gpgme_data_append_string (dh, "  <notation>\n");
71     }
72
73     if ( code == STATUS_NOTATION_DATA ) {
74         if ( !ctx->result.verify->notation_in_data )
75             _gpgme_data_append_string (dh, "  <data>");
76         _gpgme_data_append_percentstring_for_xml (dh, data);
77         ctx->result.verify->notation_in_data = 1;
78         return;
79     }
80
81     if ( ctx->result.verify->notation_in_data ) {
82         _gpgme_data_append_string (dh, "</data>\n");
83         ctx->result.verify->notation_in_data = 0;
84     }
85
86     if ( code == STATUS_NOTATION_NAME ) {
87         _gpgme_data_append_string (dh, "  <name>");
88         _gpgme_data_append_percentstring_for_xml (dh, data);
89         _gpgme_data_append_string (dh, "</name>\n");
90     }
91     else if ( code == STATUS_POLICY_URL ) {
92         _gpgme_data_append_string (dh, "  <policy>");
93         _gpgme_data_append_percentstring_for_xml (dh, data);
94         _gpgme_data_append_string (dh, "</policy>\n");
95     }
96     else {
97         assert (0);
98     }
99 }
100
101
102 /* 
103  * finish a pending signature info collection and prepare for a new
104  * signature info collection
105  */
106 static void
107 finish_sig (GpgmeCtx ctx, int stop)
108 {
109     if (stop)
110         return; /* nothing to do */
111
112     if (ctx->result.verify->collecting) {
113         VerifyResult res2;
114
115         ctx->result.verify->collecting = 0;
116         /* create a new result structure */
117         res2 = xtrycalloc ( 1, sizeof *res2 );
118         if ( !res2 ) {
119             ctx->out_of_core = 1;
120             return;
121         }
122
123         res2->next = ctx->result.verify;
124         ctx->result.verify = res2;
125     }
126     
127     ctx->result.verify->collecting = 1;
128 }
129
130 void
131 _gpgme_verify_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args)
132 {
133     char *p;
134     int i;
135
136     if ( ctx->out_of_core )
137         return;
138     if (!ctx->result.verify)
139       {
140         ctx->result.verify = xtrycalloc (1, sizeof *ctx->result.verify);
141         if (!ctx->result.verify)
142           {
143             ctx->out_of_core = 1;
144             return;
145           }
146       }
147
148     if (code == STATUS_GOODSIG
149         || code == STATUS_BADSIG || code == STATUS_ERRSIG) {
150         finish_sig (ctx,0);
151         if ( ctx->out_of_core )
152             return;
153     }
154
155     switch (code) {
156       case STATUS_NODATA:
157         ctx->result.verify->status = GPGME_SIG_STAT_NOSIG;
158         break;
159
160       case STATUS_GOODSIG:
161         /* We only look at VALIDSIG */
162         break;
163
164       case STATUS_VALIDSIG:
165         ctx->result.verify->status = GPGME_SIG_STAT_GOOD;
166         p = ctx->result.verify->fpr;
167         for (i=0; i < DIM(ctx->result.verify->fpr)
168                  && args[i] && args[i] != ' ' ; i++ )
169             *p++ = args[i];
170         *p = 0;
171         /* skip the formatted date */
172         while ( args[i] && args[i] == ' ')
173             i++;
174         while ( args[i] && args[i] != ' ')
175             i++;
176         /* and get the timestamp */
177         ctx->result.verify->timestamp = strtoul (args+i, NULL, 10);
178         break;
179
180       case STATUS_BADSIG:
181         ctx->result.verify->status = GPGME_SIG_STAT_BAD;
182         /* store the keyID in the fpr field */
183         p = ctx->result.verify->fpr;
184         for (i=0; i < DIM(ctx->result.verify->fpr)
185                  && args[i] && args[i] != ' ' ; i++ )
186             *p++ = args[i];
187         *p = 0;
188         break;
189
190       case STATUS_ERRSIG:
191         ctx->result.verify->status = GPGME_SIG_STAT_ERROR;
192         /* FIXME: distinguish between a regular error and a missing key.
193          * this is encoded in the args. */
194         /* store the keyID in the fpr field */
195         p = ctx->result.verify->fpr;
196         for (i=0; i < DIM(ctx->result.verify->fpr)
197                  && args[i] && args[i] != ' ' ; i++ )
198             *p++ = args[i];
199         *p = 0;
200         break;
201
202       case STATUS_NOTATION_NAME:
203       case STATUS_NOTATION_DATA:
204       case STATUS_POLICY_URL:
205         add_notation ( ctx, code, args );
206         break;
207
208       case STATUS_END_STREAM:
209         break;
210
211       case STATUS_EOF:
212         finish_sig(ctx,1);
213         break;
214
215       default:
216         /* ignore all other codes */
217         break;
218     }
219 }
220
221 GpgmeError
222 gpgme_op_verify_start (GpgmeCtx ctx, GpgmeData sig, GpgmeData text)
223 {
224   int err = 0;
225   int pipemode = 0;      /* !!text; use pipemode for detached sigs.  */
226
227   fail_on_pending_request(ctx);
228   ctx->pending = 1;
229
230   _gpgme_release_result (ctx);
231   ctx->out_of_core = 0;
232     
233   if (!pipemode)
234     {
235       _gpgme_engine_release (ctx->engine);
236       ctx->engine = NULL;
237     }
238
239   if (!ctx->engine)
240     err = _gpgme_engine_new (ctx->use_cms ? GPGME_PROTOCOL_CMS
241                              : GPGME_PROTOCOL_OpenPGP, &ctx->engine);
242   if (err)
243     goto leave;
244
245 #if 0   /* FIXME */
246   if (pipemode)
247     _gpgme_gpg_enable_pipemode (c->engine->engine.gpg);
248 #endif
249
250   _gpgme_engine_set_status_handler (ctx->engine, _gpgme_verify_status_handler,
251                                     ctx);
252   _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
253
254   /* Check the supplied data.  */
255   if (gpgme_data_get_type (sig) == GPGME_DATA_TYPE_NONE)
256     {
257       err = mk_error (No_Data);
258       goto leave;
259     }
260   if (text && gpgme_data_get_type (text) == GPGME_DATA_TYPE_NONE)
261     {
262       err = mk_error (No_Data);
263       goto leave;
264     }
265   _gpgme_data_set_mode (sig, GPGME_DATA_MODE_OUT);
266   if (text)         /* Detached signature.  */
267     _gpgme_data_set_mode (text, GPGME_DATA_MODE_OUT);
268
269   err = _gpgme_engine_op_verify (ctx->engine, sig, text);
270   if (!err)     /* And kick off the process.  */
271     err = _gpgme_engine_start (ctx->engine, ctx);
272
273  leave:
274   if (err)
275     {
276       ctx->pending = 0; 
277       _gpgme_engine_release (ctx->engine);
278       ctx->engine = NULL;
279     }
280   return err;
281 }
282
283 /* 
284  * Figure out a common status value for all signatures 
285  */
286 GpgmeSigStat
287 _gpgme_intersect_stati (VerifyResult result)
288 {
289   GpgmeSigStat status = result->status;
290
291   for (result = result->next; result; result = result->next)
292     {
293       if (status != result->status) 
294         return GPGME_SIG_STAT_DIFF;
295     }
296   return status;
297 }
298
299 /**
300  * gpgme_op_verify:
301  * @c: the context
302  * @sig: the signature data
303  * @text: the signed text
304  * @r_stat: returns the status of the signature
305  * 
306  * Perform a signature check on the signature given in @sig. Currently it is
307  * assumed that this is a detached signature for the material given in @text.
308  * The result of this operation is returned in @r_stat which can take these
309  * values:
310  *  GPGME_SIG_STAT_NONE:  No status - should not happen
311  *  GPGME_SIG_STAT_GOOD:  The signature is valid 
312  *  GPGME_SIG_STAT_BAD:   The signature is not valid
313  *  GPGME_SIG_STAT_NOKEY: The signature could not be checked due to a
314  *                        missing key
315  *  GPGME_SIG_STAT_NOSIG: This is not a signature
316  *  GPGME_SIG_STAT_ERROR: Due to some other error the check could not be done.
317  *  GPGME_SIG_STAT_DIFF:  There is more than 1 signature and they have not
318  *                        the same status.
319  *
320  * Return value: 0 on success or an errorcode if something not related to
321  *               the signature itself did go wrong.
322  **/
323 GpgmeError
324 gpgme_op_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text,
325                   GpgmeSigStat *r_stat )
326 {
327     int rc;
328
329     if ( !r_stat )
330         return mk_error (Invalid_Value);
331
332     gpgme_data_release (c->notation);
333     c->notation = NULL;
334     
335     *r_stat = GPGME_SIG_STAT_NONE;
336     rc = gpgme_op_verify_start ( c, sig, text );
337     if ( !rc ) {
338         gpgme_wait (c, 1);
339         if (!c->result.verify)
340             rc = mk_error (General_Error);
341         else if (c->out_of_core)
342             rc = mk_error (Out_Of_Core);
343         else {
344             /* FIXME: Put all notation data into one XML fragment.  */
345             if ( c->result.verify->notation ) {
346                 GpgmeData dh = c->result.verify->notation;
347                 
348                 if ( c->result.verify->notation_in_data ) {
349                     _gpgme_data_append_string (dh, "</data>\n");
350                     c->result.verify->notation_in_data = 0;
351                 }
352                 _gpgme_data_append_string (dh, "</notation>\n");
353                 c->notation = dh;
354                 c->result.verify->notation = NULL;
355             }
356             *r_stat = _gpgme_intersect_stati (c->result.verify);
357         }
358     }
359     return rc;
360 }
361
362
363 /**
364  * gpgme_get_sig_status:
365  * @c: Context
366  * @idx: Index of the signature starting at 0
367  * @r_stat: Returns the status
368  * @r_created: Returns the creation timestamp
369  * 
370  * Return information about an already verified signatures. 
371  * 
372  * Return value: The fingerprint or NULL in case of an problem or
373  *               when there are no more signatures.
374  **/
375 const char *
376 gpgme_get_sig_status (GpgmeCtx c, int idx,
377                       GpgmeSigStat *r_stat, time_t *r_created)
378 {
379   VerifyResult result;
380
381   if (!c || c->pending || !c->result.verify)
382     return NULL;        /* No results yet or verification error.  */
383
384   for (result = c->result.verify;
385        result && idx > 0; result = result->next, idx--)
386     ;
387   if (!result)
388     return NULL;        /* No more signatures.  */
389
390   if (r_stat)
391     *r_stat = result->status;
392   if (r_created)
393     *r_created = result->timestamp;
394   return result->fpr;
395 }
396
397
398 /**
399  * gpgme_get_sig_key:
400  * @c: context
401  * @idx: Index of the signature starting at 0
402  * @r_key: Returns the key object
403  * 
404  * Return a key object which was used to check the signature. 
405  * 
406  * Return value: An Errorcode or 0 for success. GPGME_EOF is returned to
407  *               indicate that there are no more signatures. 
408  **/
409 GpgmeError
410 gpgme_get_sig_key (GpgmeCtx c, int idx, GpgmeKey *r_key)
411 {
412   VerifyResult result;
413   GpgmeError err = 0;
414
415   if (!c || !r_key)
416     return mk_error (Invalid_Value);
417   if (c->pending || !c->result.verify)
418     return mk_error (Busy);
419   
420   for (result = c->result.verify;
421        result && idx > 0; result = result->next, idx--)
422     ;
423   if (!result)
424     return mk_error (EOF);
425   
426   if (strlen(result->fpr) < 16) /* We have at least a key ID.  */
427     return mk_error (Invalid_Key);
428   
429   *r_key = _gpgme_key_cache_get (result->fpr);
430   if (!*r_key)
431     {
432       GpgmeCtx listctx;
433       
434       /* Fixme: This can be optimized by keeping an internal context
435          used for such key listings.  */
436       err = gpgme_new (&listctx);
437       if (err)
438         return err;
439       gpgme_set_keylist_mode (listctx, c->keylist_mode);
440       err = gpgme_op_keylist_start (listctx, result->fpr, 0);
441       if (!err)
442         err = gpgme_op_keylist_next (listctx, r_key);
443       gpgme_release (listctx);
444     }
445   return err;
446 }