LCOV - code coverage report
Current view: top level - dirmngr - loadswdb.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 191 0.0 %
Date: 2016-11-29 15:00:56 Functions: 0 4 0.0 %

          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             : }

Generated by: LCOV version 1.11