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