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