gpgme/
[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
222
223 GpgmeError
224 gpgme_op_verify_start ( GpgmeCtx c,  GpgmeData sig, GpgmeData text )
225 {
226     int rc = 0;
227     int i;
228     int pipemode = 0; /*!!text; use pipemode for detached sigs */
229
230     fail_on_pending_request( c );
231     c->pending = 1;
232
233     _gpgme_release_result (c);
234     c->out_of_core = 0;
235     
236     if ( !pipemode ) {
237         _gpgme_gpg_release ( c->gpg );
238         c->gpg = NULL;
239     }
240
241     if ( !c->gpg ) 
242         rc = _gpgme_gpg_new ( &c->gpg );
243     if (rc)
244         goto leave;
245
246     if (pipemode)
247         _gpgme_gpg_enable_pipemode ( c->gpg ); 
248     _gpgme_gpg_set_status_handler (c->gpg, _gpgme_verify_status_handler, c);
249
250     /* build the commandline */
251     _gpgme_gpg_add_arg ( c->gpg, pipemode?"--pipemode" : "--verify" );
252     for ( i=0; i < c->verbosity; i++ )
253         _gpgme_gpg_add_arg ( c->gpg, "--verbose" );
254
255     /* Check the supplied data */
256     if ( gpgme_data_get_type (sig) == GPGME_DATA_TYPE_NONE ) {
257         rc = mk_error (No_Data);
258         goto leave;
259     }
260     if ( text && gpgme_data_get_type (text) == GPGME_DATA_TYPE_NONE ) {
261         rc = mk_error (No_Data);
262         goto leave;
263     }
264     _gpgme_data_set_mode (sig, GPGME_DATA_MODE_OUT );
265     if (text) /* detached signature */
266         _gpgme_data_set_mode (text, GPGME_DATA_MODE_OUT );
267     /* Tell the gpg object about the data */
268     _gpgme_gpg_add_arg ( c->gpg, "--" );
269     if (pipemode) {
270         _gpgme_gpg_add_pm_data ( c->gpg, sig, 0 );
271         _gpgme_gpg_add_pm_data ( c->gpg, text, 1 );
272     }
273     else {
274         _gpgme_gpg_add_data ( c->gpg, sig, -1 );
275         if (text) {
276             _gpgme_gpg_add_arg ( c->gpg, "-" );
277             _gpgme_gpg_add_data ( c->gpg, text, 0 );
278         }
279     }
280
281     /* and kick off the process */
282     rc = _gpgme_gpg_spawn ( c->gpg, c );
283
284  leave:
285     if (rc) {
286         c->pending = 0; 
287         _gpgme_gpg_release ( c->gpg ); c->gpg = NULL;
288     }
289     return rc;
290 }
291
292
293 /* 
294  * Figure out a common status value for all signatures 
295  */
296 GpgmeSigStat
297 _gpgme_intersect_stati (VerifyResult result)
298 {
299   GpgmeSigStat status = result->status;
300
301   for (result = result->next; result; result = result->next)
302     {
303       if (status != result->status) 
304         return GPGME_SIG_STAT_DIFF;
305     }
306   return status;
307 }
308
309 /**
310  * gpgme_op_verify:
311  * @c: the context
312  * @sig: the signature data
313  * @text: the signed text
314  * @r_stat: returns the status of the signature
315  * 
316  * Perform a signature check on the signature given in @sig. Currently it is
317  * assumed that this is a detached signature for the material given in @text.
318  * The result of this operation is returned in @r_stat which can take these
319  * values:
320  *  GPGME_SIG_STAT_NONE:  No status - should not happen
321  *  GPGME_SIG_STAT_GOOD:  The signature is valid 
322  *  GPGME_SIG_STAT_BAD:   The signature is not valid
323  *  GPGME_SIG_STAT_NOKEY: The signature could not be checked due to a
324  *                        missing key
325  *  GPGME_SIG_STAT_NOSIG: This is not a signature
326  *  GPGME_SIG_STAT_ERROR: Due to some other error the check could not be done.
327  *  GPGME_SIG_STAT_DIFF:  There is more than 1 signature and they have not
328  *                        the same status.
329  *
330  * Return value: 0 on success or an errorcode if something not related to
331  *               the signature itself did go wrong.
332  **/
333 GpgmeError
334 gpgme_op_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text,
335                   GpgmeSigStat *r_stat )
336 {
337     int rc;
338
339     if ( !r_stat )
340         return mk_error (Invalid_Value);
341
342     gpgme_data_release (c->notation);
343     c->notation = NULL;
344     
345     *r_stat = GPGME_SIG_STAT_NONE;
346     rc = gpgme_op_verify_start ( c, sig, text );
347     if ( !rc ) {
348         gpgme_wait (c, 1);
349         if (!c->result.verify)
350             rc = mk_error (General_Error);
351         else if (c->out_of_core)
352             rc = mk_error (Out_Of_Core);
353         else {
354             /* FIXME: Put all notation data into one XML fragment.  */
355             if ( c->result.verify->notation ) {
356                 GpgmeData dh = c->result.verify->notation;
357                 
358                 if ( c->result.verify->notation_in_data ) {
359                     _gpgme_data_append_string (dh, "</data>\n");
360                     c->result.verify->notation_in_data = 0;
361                 }
362                 _gpgme_data_append_string (dh, "</notation>\n");
363                 c->notation = dh;
364                 c->result.verify->notation = NULL;
365             }
366             *r_stat = _gpgme_intersect_stati (c->result.verify);
367         }
368         c->pending = 0;
369     }
370     return rc;
371 }
372
373
374 /**
375  * gpgme_get_sig_status:
376  * @c: Context
377  * @idx: Index of the signature starting at 0
378  * @r_stat: Returns the status
379  * @r_created: Returns the creation timestamp
380  * 
381  * Return information about an already verified signatures. 
382  * 
383  * Return value: The fingerprint or NULL in case of an problem or
384  *               when there are no more signatures.
385  **/
386 const char *
387 gpgme_get_sig_status (GpgmeCtx c, int idx,
388                       GpgmeSigStat *r_stat, time_t *r_created)
389 {
390   VerifyResult result;
391
392   if (!c || c->pending || !c->result.verify)
393     return NULL;        /* No results yet or verification error.  */
394
395   for (result = c->result.verify;
396        result && idx > 0; result = result->next, idx--)
397     ;
398   if (!result)
399     return NULL;        /* No more signatures.  */
400
401   if (r_stat)
402     *r_stat = result->status;
403   if (r_created)
404     *r_created = result->timestamp;
405   return result->fpr;
406 }
407
408
409 /**
410  * gpgme_get_sig_key:
411  * @c: context
412  * @idx: Index of the signature starting at 0
413  * @r_key: Returns the key object
414  * 
415  * Return a key object which was used to check the signature. 
416  * 
417  * Return value: An Errorcode or 0 for success. GPGME_EOF is returned to
418  *               indicate that there are no more signatures. 
419  **/
420 GpgmeError
421 gpgme_get_sig_key (GpgmeCtx c, int idx, GpgmeKey *r_key)
422 {
423   VerifyResult result;
424   GpgmeError err = 0;
425
426   if (!c || !r_key)
427     return mk_error (Invalid_Value);
428   if (c->pending || !c->result.verify)
429     return mk_error (Busy);
430   
431   for (result = c->result.verify;
432        result && idx > 0; result = result->next, idx--)
433     ;
434   if (!result)
435     return mk_error (EOF);
436   
437   if (strlen(result->fpr) < 16) /* We have at least a key ID.  */
438     return mk_error (Invalid_Key);
439   
440   *r_key = _gpgme_key_cache_get (result->fpr);
441   if (!*r_key)
442     {
443       GpgmeCtx listctx;
444       
445       /* Fixme: This can be optimized by keeping an internal context
446          used for such key listings.  */
447       err = gpgme_new (&listctx);
448       if (err)
449         return err;
450       gpgme_set_keylist_mode (listctx, c->keylist_mode);
451       err = gpgme_op_keylist_start (listctx, result->fpr, 0);
452       if (!err)
453         err = gpgme_op_keylist_next (listctx, r_key);
454       gpgme_release (listctx);
455     }
456   return err;
457 }