* certpath.c (gpgsm_validate_path): Added EXPTIME arg and changed
[gnupg.git] / sm / verify.c
1 /* verify.c - Verify a messages signature
2  *      Copyright (C) 2001 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG 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  * GnuPG 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 <errno.h>
26 #include <unistd.h> 
27 #include <time.h>
28 #include <assert.h>
29
30 #include <gcrypt.h>
31 #include <ksba.h>
32
33 #include "gpgsm.h"
34 #include "keydb.h"
35 #include "i18n.h"
36
37 /* fixme: Move this to jnlib */
38 static char *
39 strtimestamp (time_t atime)
40 {
41   char *buffer = xmalloc (15);
42   
43   if (atime < 0) 
44     strcpy (buffer, "????" "-??" "-??");
45   else if (!atime)
46     strcpy (buffer, "none");
47   else
48     {
49       struct tm *tp;
50       
51       tp = gmtime( &atime );
52       sprintf (buffer, "%04d-%02d-%02d",
53                1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday);
54     }
55   return buffer;
56 }
57
58
59
60 /* Hash the data for a detached signature */
61 static void
62 hash_data (int fd, GCRY_MD_HD md)
63 {
64   FILE *fp;
65   char buffer[4096];
66   int nread;
67
68   fp = fdopen ( dup (fd), "rb");
69   if (!fp)
70     {
71       log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno));
72       return;
73     }
74
75   do 
76     {
77       nread = fread (buffer, 1, DIM(buffer), fp);
78       gcry_md_write (md, buffer, nread);
79     }
80   while (nread);
81   if (ferror (fp))
82       log_error ("read error on fd %d: %s\n", fd, strerror (errno));
83   fclose (fp);
84 }
85
86
87
88 \f
89 /* Perform a verify operation.  To verify detached signatures, data_fd
90    must be different than -1.  With OUT_FP given and a non-detached
91    signature, the signed material is written to that stream. */
92 int
93 gpgsm_verify (CTRL ctrl, int in_fd, int data_fd, FILE *out_fp)
94 {
95   int i, rc;
96   Base64Context b64reader = NULL;
97   Base64Context b64writer = NULL;
98   KsbaError err;
99   KsbaReader reader;
100   KsbaWriter writer = NULL;
101   KsbaCMS cms = NULL;
102   KsbaStopReason stopreason;
103   KsbaCert cert;
104   KEYDB_HANDLE kh;
105   GCRY_MD_HD data_md = NULL;
106   int signer;
107   const char *algoid;
108   int algo;
109   int is_detached;
110   FILE *fp = NULL;
111
112   kh = keydb_new (0);
113   if (!kh)
114     {
115       log_error (_("failed to allocated keyDB handle\n"));
116       rc = GNUPG_General_Error;
117       goto leave;
118     }
119
120
121   fp = fdopen ( dup (in_fd), "rb");
122   if (!fp)
123     {
124       log_error ("fdopen() failed: %s\n", strerror (errno));
125       rc = seterr (IO_Error);
126       goto leave;
127     }
128
129   rc = gpgsm_create_reader (&b64reader, ctrl, fp, &reader);
130   if (rc)
131     {
132       log_error ("can't create reader: %s\n", gnupg_strerror (rc));
133       goto leave;
134     }
135
136   if (out_fp)
137     {
138       rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer);
139       if (rc)
140         {
141           log_error ("can't create writer: %s\n", gnupg_strerror (rc));
142           goto leave;
143         }
144     }
145
146   cms = ksba_cms_new ();
147   if (!cms)
148     {
149       rc = seterr (Out_Of_Core);
150       goto leave;
151     }
152
153   err = ksba_cms_set_reader_writer (cms, reader, writer);
154   if (err)
155     {
156       log_error ("ksba_cms_set_reader_writer failed: %s\n",
157                  ksba_strerror (err));
158       rc = map_ksba_err (err);
159       goto leave;
160     }
161
162   data_md = gcry_md_open (0, 0);
163   if (!data_md)
164     {
165       rc = map_gcry_err (gcry_errno());
166       log_error ("md_open failed: %s\n", gcry_strerror (-1));
167       goto leave;
168     }
169   if (DBG_HASHING)
170     gcry_md_start_debug (data_md, "vrfy.data");
171
172   is_detached = 0;
173   do 
174     {
175       err = ksba_cms_parse (cms, &stopreason);
176       if (err)
177         {
178           log_error ("ksba_cms_parse failed: %s\n", ksba_strerror (err));
179           rc = map_ksba_err (err);
180           goto leave;
181         }
182
183       if (stopreason == KSBA_SR_NEED_HASH)
184         {
185           is_detached = 1;
186           if (opt.verbose)
187             log_info ("detached signature\n");
188         }
189
190       if (stopreason == KSBA_SR_NEED_HASH
191           || stopreason == KSBA_SR_BEGIN_DATA)
192         { /* We are now able to enable the hash algorithms */
193           for (i=0; (algoid=ksba_cms_get_digest_algo_list (cms, i)); i++)
194             {
195               algo = gcry_md_map_name (algoid);
196               if (!algo)
197                 log_error ("unknown hash algorithm `%s'\n",
198                            algoid? algoid:"?");
199               else
200                 gcry_md_enable (data_md, algo);
201             }
202           if (is_detached)
203             {
204               if (data_fd == -1)
205                 log_info ("detached signature w/o data "
206                           "- assuming certs-only\n");
207               else
208                 hash_data (data_fd, data_md);  
209             }
210           else
211             {
212               ksba_cms_set_hash_function (cms, HASH_FNC, data_md);
213             }
214         }
215       else if (stopreason == KSBA_SR_END_DATA)
216         { /* The data bas been hashed */
217
218         }
219     }
220   while (stopreason != KSBA_SR_READY);   
221
222   if (b64writer)
223     {
224       rc = gpgsm_finish_writer (b64writer);
225       if (rc) 
226         {
227           log_error ("write failed: %s\n", gnupg_strerror (rc));
228           goto leave;
229         }
230     }
231
232   if (data_fd != -1 && !is_detached)
233     {
234       log_error ("data given for a non-detached signature\n");
235       rc = GNUPG_Conflict;
236       goto leave;
237     }
238
239   for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++)
240     {
241       /* Fixme: it might be better to check the validity of the
242          certificate first before entering it into the DB.  This way
243          we would avoid cluttering the DB with invalid
244          certificates. */
245       keydb_store_cert (cert);
246       ksba_cert_release (cert);
247     }
248
249   cert = NULL;
250   err = 0;
251   for (signer=0; signer < 1; signer++)
252     {
253       char *issuer = NULL;
254       KsbaSexp sigval = NULL;
255       time_t sigtime, keyexptime;
256       KsbaSexp serial;
257       char *msgdigest = NULL;
258       size_t msgdigestlen;
259
260       err = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial);
261       if (!signer && err == KSBA_No_Data && data_fd == -1 && is_detached)
262         {
263           log_info ("certs-only message accepted\n");
264           err = 0;
265           break;
266         }
267       if (err)
268         break;
269       if (DBG_X509)
270         {
271           log_debug ("signer %d - issuer: `%s'\n",
272                      signer, issuer? issuer:"[NONE]");
273           log_debug ("signer %d - serial: ", signer);
274           gpgsm_dump_serial (serial);
275           log_printf ("\n");
276         }
277
278       err = ksba_cms_get_signing_time (cms, signer, &sigtime);
279       if (err)
280         {
281           log_error ("error getting signing time: %s\n", ksba_strerror (err));
282           sigtime = (time_t)-1;
283         }
284       if (DBG_X509)
285         {
286           log_debug ("signer %d - sigtime: ", signer);
287           gpgsm_dump_time (sigtime);  
288           log_printf ("\n");
289         }
290
291       err = ksba_cms_get_message_digest (cms, signer,
292                                          &msgdigest, &msgdigestlen);
293       if (err)
294         break;
295
296       algoid = ksba_cms_get_digest_algo (cms, signer);
297       algo = gcry_md_map_name (algoid);
298       if (DBG_X509)
299         log_debug ("signer %d - digest algo: %d\n", signer, algo);
300       if ( !gcry_md_info (data_md, GCRYCTL_IS_ALGO_ENABLED, &algo, NULL) )
301         {
302           log_error ("digest algo %d has not been enabled\n", algo);
303           goto next_signer;
304         }
305
306       sigval = ksba_cms_get_sig_val (cms, signer);
307       if (!sigval)
308         {
309           log_error ("no signature value available\n");
310           goto next_signer;
311         }
312       if (DBG_X509)
313         log_debug ("signer %d - signature available", signer);
314
315       /* Find the certificate of the signer */
316       keydb_search_reset (kh);
317       rc = keydb_search_issuer_sn (kh, issuer, serial);
318       if (rc)
319         {
320           log_error ("failed to find the certificate: %s\n",
321                      gnupg_strerror(rc));
322           goto next_signer;
323         }
324
325       rc = keydb_get_cert (kh, &cert);
326       if (rc)
327         {
328           log_error ("failed to get cert: %s\n", gnupg_strerror (rc));
329           goto next_signer;
330         }
331
332       if (msgdigest)
333         { /* Signed attributes are available. */
334           GCRY_MD_HD md;
335           unsigned char *s;
336
337           /* check that the message digest in the signed attributes
338              matches the one we calculated on the data */
339           s = gcry_md_read (data_md, algo);
340           if ( !s || !msgdigestlen
341                || gcry_md_get_algo_dlen (algo) != msgdigestlen
342                || !s || memcmp (s, msgdigest, msgdigestlen) )
343             {
344               log_error ("invalid signature: message digest attribute "
345                          "does not match calculated one\n");
346               gpgsm_status (ctrl, STATUS_BADSIG, NULL);
347               goto next_signer; 
348             }
349             
350           md = gcry_md_open (algo, 0);
351           if (!md)
352             {
353               log_error ("md_open failed: %s\n", gcry_strerror (-1));
354               goto next_signer;
355             }
356           if (DBG_HASHING)
357             gcry_md_start_debug (md, "vrfy.attr");
358
359           ksba_cms_set_hash_function (cms, HASH_FNC, md);
360           rc = ksba_cms_hash_signed_attrs (cms, signer);
361           if (rc)
362             {
363               log_error ("hashing signed attrs failed: %s\n",
364                          ksba_strerror (rc));
365               gcry_md_close (md);
366               goto next_signer;
367             }
368           rc = gpgsm_check_cms_signature (cert, sigval, md, algo);
369           gcry_md_close (md);
370         }
371       else
372         {
373           rc = gpgsm_check_cms_signature (cert, sigval, data_md, algo);
374         }
375
376       if (rc)
377         {
378           log_error ("invalid signature: %s\n", gnupg_strerror (rc));
379           gpgsm_status (ctrl, STATUS_BADSIG, NULL);
380           goto next_signer;
381         }
382       gpgsm_cert_use_verify_p (cert); /* this displays an info message */
383       if (DBG_X509)
384         log_debug ("signature okay - checking certs\n");
385       rc = gpgsm_validate_path (cert, &keyexptime);
386       if (rc == GNUPG_Certificate_Expired)
387         gpgsm_status (ctrl, STATUS_EXPKEYSIG, NULL);
388       else
389         gpgsm_status (ctrl, STATUS_GOODSIG, NULL);
390       
391       {
392         char *buf, *fpr, *tstr;
393
394         fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
395         tstr = strtimestamp (sigtime);
396         buf = xmalloc ( strlen(fpr) + strlen (tstr) + 120);
397         sprintf (buf, "%s %s %lu %lu", fpr, tstr,
398                  (unsigned long)sigtime, (unsigned long)keyexptime );
399         xfree (tstr);
400         xfree (fpr);
401         gpgsm_status (ctrl, STATUS_VALIDSIG, buf);
402         xfree (buf);
403       }
404
405       if (rc) /* of validate_path */
406         {
407           log_error ("invalid certification path: %s\n", gnupg_strerror (rc));
408           if (rc == GNUPG_Bad_Certificate_Path
409               || rc == GNUPG_Bad_Certificate)
410             gpgsm_status (ctrl, STATUS_TRUST_NEVER, NULL);
411           else
412             gpgsm_status (ctrl, STATUS_TRUST_UNDEFINED, NULL);
413           goto next_signer;
414         }
415       log_info ("signature is good\n");
416       gpgsm_status (ctrl, STATUS_TRUST_FULLY, NULL);
417           
418
419     next_signer:
420       rc = 0;
421       xfree (issuer);
422       xfree (serial);
423       xfree (sigval);
424       xfree (msgdigest);
425       ksba_cert_release (cert);
426       cert = NULL;
427     }
428   rc = 0;
429   if (err)
430     {
431       log_error ("ksba error: %s\n", ksba_strerror (err));
432       rc = map_ksba_err (rc);
433     }    
434
435
436
437  leave:
438   ksba_cms_release (cms);
439   gpgsm_destroy_reader (b64reader);
440   gpgsm_destroy_writer (b64writer);
441   keydb_release (kh); 
442   gcry_md_close (data_md);
443   if (fp)
444     fclose (fp);
445   return rc;
446 }
447