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