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