dirmngr: More fix for Windows.
[gnupg.git] / dirmngr / loadswdb.c
1 /* loadswdb.c - Load the swdb file from versions.gnupg.org
2  * Copyright (C) 2016 g10 Code GmbH
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 3 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, see <https://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "dirmngr.h"
27 #include "../common/ccparray.h"
28 #include "../common/exectool.h"
29 #include "misc.h"
30 #include "ks-engine.h"
31
32
33 /* Get the time from the current swdb file and store it at R_FILEDATE
34  * and R_VERIFIED.  If the file does not exist 0 is stored at there.
35  * The function returns 0 on sucess or an error code.  */
36 static gpg_error_t
37 time_of_saved_swdb (const char *fname, time_t *r_filedate, time_t *r_verified)
38 {
39   gpg_error_t err;
40   estream_t fp = NULL;
41   char *line = NULL;
42   size_t length_of_line = 0;
43   size_t  maxlen;
44   ssize_t len;
45   char *fields[2];
46   gnupg_isotime_t isot;
47   time_t filedate = (time_t)(-1);
48   time_t verified = (time_t)(-1);
49
50   *r_filedate = 0;
51   *r_verified = 0;
52
53   fp = es_fopen (fname, "r");
54   err = fp? 0 : gpg_error_from_syserror ();
55   if (err)
56     {
57       if (gpg_err_code (err) == GPG_ERR_ENOENT)
58         err = 0; /* No file - assume time is the year of Unix.  */
59       goto leave;
60     }
61
62   /* Note that the parser uses the first occurance of a matching
63    * values and ignores possible duplicated values.  */
64   maxlen = 2048; /* Set limit.  */
65   while ((len = es_read_line (fp, &line, &length_of_line, &maxlen)) > 0)
66     {
67       if (!maxlen)
68         {
69           err = gpg_error (GPG_ERR_LINE_TOO_LONG);
70           goto leave;
71         }
72       /* Strip newline and carriage return, if present.  */
73       while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
74         line[--len] = '\0';
75
76       if (split_fields (line, fields, DIM (fields)) < DIM(fields))
77         continue; /* Skip empty lines and names w/o a value.  */
78       if (*fields[0] == '#')
79         continue; /* Skip comments.  */
80
81       /* Record the meta data.  */
82       if (filedate == (time_t)(-1) && !strcmp (fields[0], ".filedate"))
83         {
84           if (string2isotime (isot, fields[1]))
85             filedate = isotime2epoch (isot);
86         }
87       else if (verified == (time_t)(-1) && !strcmp (fields[0], ".verified"))
88         {
89           if (string2isotime (isot, fields[1]))
90             verified = isotime2epoch (isot);
91         }
92     }
93   if (len < 0 || es_ferror (fp))
94     {
95       err = gpg_error_from_syserror ();
96       goto leave;
97     }
98   if (filedate == (time_t)(-1) || verified == (time_t)(-1))
99     {
100       err = gpg_error (GPG_ERR_INV_TIME);
101       goto leave;
102     }
103
104   *r_filedate = filedate;
105   *r_verified = verified;
106
107  leave:
108   if (err)
109     log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (err));
110   xfree (line);
111   es_fclose (fp);
112   return err;
113 }
114
115
116
117 /* Read a file from URL and return it as an estream memory buffer at
118  * R_FP.  */
119 static gpg_error_t
120 fetch_file (ctrl_t ctrl, const char *url, estream_t *r_fp)
121 {
122   gpg_error_t err;
123   estream_t fp = NULL;
124   estream_t httpfp = NULL;
125   size_t nread, nwritten;
126   char buffer[1024];
127
128   if ((err = ks_http_fetch (ctrl, url, &httpfp)))
129     goto leave;
130
131   /* We now read the data from the web server into a memory buffer.
132    * To avoid excessive memory use in case of a ill behaving server we
133    * put a 64 k size limit on the buffer.  As of today the actual size
134    * of the swdb.lst file is 3k.  */
135   fp = es_fopenmem (64*1024, "rw");
136   if (!fp)
137     {
138       err = gpg_error_from_syserror ();
139       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
140       goto leave;
141     }
142
143   for (;;)
144     {
145       if (es_read (httpfp, buffer, sizeof buffer, &nread))
146         {
147           err = gpg_error_from_syserror ();
148           log_error ("error reading '%s': %s\n",
149                      es_fname_get (httpfp), gpg_strerror (err));
150           goto leave;
151         }
152
153       if (!nread)
154         break; /* Ready.  */
155       if (es_write (fp, buffer, nread, &nwritten))
156         {
157           err = gpg_error_from_syserror ();
158           log_error ("error writing '%s': %s\n",
159                      es_fname_get (fp), gpg_strerror (err));
160           goto leave;
161         }
162       else if (nread != nwritten)
163         {
164           err = gpg_error (GPG_ERR_EIO);
165           log_error ("error writing '%s': %s\n",
166                      es_fname_get (fp), "short write");
167           goto leave;
168         }
169     }
170
171   es_rewind (fp);
172   *r_fp = fp;
173   fp = NULL;
174
175  leave:
176   es_fclose (httpfp);
177   es_fclose (fp);
178   return err;
179 }
180
181
182 /* Communication object for verify_status_cb.  */
183 struct verify_status_parm_s
184 {
185   time_t sigtime;
186   int anyvalid;
187 };
188
189 static void
190 verify_status_cb (void *opaque, const char *keyword, char *args)
191 {
192   struct verify_status_parm_s *parm = opaque;
193
194   if (DBG_EXTPROG)
195     log_debug ("gpgv status: %s %s\n", keyword, args);
196
197   /* We care only about the first valid signature.  */
198   if (!strcmp (keyword, "VALIDSIG") && !parm->anyvalid)
199     {
200       char *fields[3];
201
202       parm->anyvalid = 1;
203       if (split_fields (args, fields, DIM (fields)) >= 3)
204         parm->sigtime = parse_timestamp (fields[2], NULL);
205     }
206 }
207
208
209
210 /* Load the swdb file into the current home directory.  Do this onlky
211  * when needed unless FORCE is set which will always get a new
212  * copy.  */
213 gpg_error_t
214 dirmngr_load_swdb (ctrl_t ctrl, int force)
215 {
216   gpg_error_t err;
217   char *fname = NULL;      /* The swdb.lst file.  */
218   char *tmp_fname = NULL;  /* The temporary swdb.lst file.  */
219   char *keyfile_fname = NULL;
220   estream_t swdb = NULL;
221   estream_t swdb_sig = NULL;
222   ccparray_t ccp;
223   const char **argv = NULL;
224   struct verify_status_parm_s verify_status_parm = { (time_t)(-1), 0 };
225   estream_t outfp = NULL;
226   time_t now = gnupg_get_time ();
227   time_t filedate = 0;  /* ".filedate" from our swdb.  */
228   time_t verified = 0;  /* ".verified" from our swdb.  */
229   gnupg_isotime_t isotime;
230
231
232   fname = make_filename_try (gnupg_homedir (), "swdb.lst", NULL);
233   if (!fname)
234     {
235       err = gpg_error_from_syserror ();
236       goto leave;
237     }
238
239   /* Check whether there is a need to get an update.  */
240   if (!force)
241     {
242       static int not_older_than;
243       static time_t lastcheck;
244
245       if (!not_older_than)
246         {
247           /* To balance access to the server we use a random time from
248            * 5 to 7 days for update checks.  */
249           not_older_than = 5 * 86400;
250           not_older_than += (get_uint_nonce () % (2*86400));
251         }
252
253       if (now - lastcheck < 3600)
254         {
255           /* We checked our swdb file in the last hour - don't check
256            * again to avoid unnecessary disk access.  */
257           err = 0;
258           goto leave;
259         }
260       lastcheck = now;
261
262       err = time_of_saved_swdb (fname, &filedate, &verified);
263       if (gpg_err_code (err) == GPG_ERR_INV_TIME)
264         err = 0; /* Force reading. */
265       if (err)
266         goto leave;
267       if (filedate >= now)
268         goto leave; /* Current or newer.  */
269       if (now - filedate < not_older_than)
270         goto leave; /* Our copy is pretty new (not older than 7 days).  */
271       if (verified > now && now - verified < 3*3600)
272         goto leave; /* We downloaded and verified in the last 3 hours.  */
273     }
274
275   /* Create the filename of the file with the keys. */
276   keyfile_fname = make_filename_try (gnupg_datadir (), "distsigkey.gpg", NULL);
277   if (!keyfile_fname)
278     {
279       err = gpg_error_from_syserror ();
280       goto leave;
281     }
282
283   /* Fetch the swdb from the web.  */
284   err = fetch_file (ctrl, "https://versions.gnupg.org/swdb.lst", &swdb);
285   if (err)
286     goto leave;
287   err = fetch_file (ctrl, "https://versions.gnupg.org/swdb.lst.sig", &swdb_sig);
288   if (err)
289     goto leave;
290
291   /* Run gpgv.  */
292   ccparray_init (&ccp, 0);
293   ccparray_put (&ccp, "--enable-special-filenames");
294   ccparray_put (&ccp, "--status-fd=2");
295   ccparray_put (&ccp, "--keyring");
296   ccparray_put (&ccp, keyfile_fname);
297   ccparray_put (&ccp, "--");
298   ccparray_put (&ccp, "-&@INEXTRA@");
299   ccparray_put (&ccp, "-");
300   ccparray_put (&ccp, NULL);
301   argv = ccparray_get (&ccp, NULL);
302   if (!argv)
303     {
304       err = gpg_error_from_syserror ();
305       goto leave;
306     }
307
308   if (DBG_EXTPROG)
309     log_debug ("starting gpgv\n");
310   err = gnupg_exec_tool_stream (gnupg_module_name (GNUPG_MODULE_NAME_GPGV),
311                                 argv, swdb, swdb_sig, NULL,
312                                 verify_status_cb, &verify_status_parm);
313   if (!err && verify_status_parm.sigtime == (time_t)(-1))
314     err = gpg_error (verify_status_parm.anyvalid? GPG_ERR_BAD_SIGNATURE
315                      /**/                       : GPG_ERR_INV_TIME      );
316   if (DBG_EXTPROG)
317     log_debug ("gpgv finished: err=%d\n", err);
318   if (err)
319     goto leave;
320
321   /* If our swdb is not older than the downloaded one.  We don't
322    * bother to update.  */
323   if (!force && filedate >= verify_status_parm.sigtime)
324     goto leave;
325
326   /* Create a file name for a temporary file in the home directory.
327    * We will later rename that file to the real name.  */
328   {
329     char *tmpstr;
330
331 #ifdef HAVE_W32_SYSTEM
332     tmpstr = es_bsprintf ("tmp-%u-swdb", (unsigned int)getpid ());
333 #else
334     tmpstr = es_bsprintf (".#%u.swdb", (unsigned int)getpid ());
335 #endif
336     if (!tmpstr)
337       {
338         err = gpg_error_from_syserror ();
339         goto leave;
340       }
341     tmp_fname = make_filename_try (gnupg_homedir (), tmpstr, NULL);
342     xfree (tmpstr);
343     if (!tmp_fname)
344       {
345         err = gpg_error_from_syserror ();
346         goto leave;
347       }
348   }
349
350   outfp = es_fopen (tmp_fname, "w");
351   if (!outfp)
352     {
353       err = gpg_error_from_syserror ();
354       log_error (_("error creating '%s': %s\n"), tmp_fname, gpg_strerror (err));
355       goto leave;
356     }
357
358   epoch2isotime (isotime, verify_status_parm.sigtime);
359   es_fprintf (outfp, ".filedate %s\n", isotime);
360   epoch2isotime (isotime, now);
361   es_fprintf (outfp, ".verified %s\n", isotime);
362
363   if (es_fseek (swdb, 0, SEEK_SET))
364     {
365       err = gpg_error_from_syserror ();
366       goto leave;
367     }
368
369   err = copy_stream (swdb, outfp);
370   if (err)
371     {
372       /* Well, it might also be a reading error, but that is pretty
373        * unlikely for a memory stream.  */
374       log_error (_("error writing '%s': %s\n"), tmp_fname, gpg_strerror (err));
375       goto leave;
376     }
377
378   if (es_fclose (outfp))
379     {
380       err = gpg_error_from_syserror ();
381       log_error (_("error writing '%s': %s\n"), tmp_fname, gpg_strerror (err));
382       goto leave;
383     }
384   outfp = NULL;
385
386   err = gnupg_rename_file (tmp_fname, fname, NULL);
387   if (err)
388     goto leave;
389   xfree (tmp_fname);
390   tmp_fname = NULL;
391
392
393  leave:
394   es_fclose (outfp);
395   if (tmp_fname)
396     gnupg_remove (tmp_fname);  /* This is a temporary file.  */
397   xfree (argv);
398   es_fclose (swdb_sig);
399   es_fclose (swdb);
400   xfree (keyfile_fname);
401   xfree (tmp_fname);
402   xfree (fname);
403   return err;
404 }