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