mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
dirmngr: Add command to only load the swdb.
* dirmngr/loadswdb.c: New. * dirmngr/Makefile.am (dirmngr_SOURCES): Add that file. * dirmngr/server.c: Remove includes cpparray.h and exectool.h. (cmd_loadswdb): New. (parse_version_number,parse_version_string): Remove. (my_mktmpdir, cmp_version): Remove. (fetch_into_tmpdir): Remove. (struct verify_swdb_parm_s): Remove. (verify_swdb_status_cb): Remove. (cmd_versioncheck): Remove. (register_commands): Register LOADSWDB. Remove VERSIONCHECK. -- This change is required to to the new design where gpgconf does the version check w/o network access and only dirmngr is responsible for getting the swdb. In the next installment the loading will be triggered as needed. Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
parent
4839e6d002
commit
52c10a280a
@ -56,6 +56,7 @@ noinst_HEADERS = dirmngr.h crlcache.h crlfetch.h misc.h
|
||||
|
||||
dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c \
|
||||
certcache.c certcache.h \
|
||||
loadswdb.c \
|
||||
cdb.h cdblib.c misc.c dirmngr-err.h \
|
||||
ocsp.c ocsp.h validate.c validate.h \
|
||||
dns-stuff.c dns-stuff.h \
|
||||
|
@ -206,5 +206,8 @@ gpg_error_t dirmngr_status_help (ctrl_t ctrl, const char *text);
|
||||
gpg_error_t dirmngr_tick (ctrl_t ctrl);
|
||||
|
||||
|
||||
/*-- loadswdb.c --*/
|
||||
gpg_error_t dirmngr_load_swdb (ctrl_t ctrl, int force);
|
||||
|
||||
|
||||
#endif /*DIRMNGR_H*/
|
||||
|
358
dirmngr/loadswdb.c
Normal file
358
dirmngr/loadswdb.c
Normal file
@ -0,0 +1,358 @@
|
||||
/* loadswdb.c - Load the swdb file from versions.gnupg.org
|
||||
* Copyright (C) 2016 g10 Code GmbH
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dirmngr.h"
|
||||
#include "../common/ccparray.h"
|
||||
#include "../common/exectool.h"
|
||||
#include "misc.h"
|
||||
#include "ks-engine.h"
|
||||
|
||||
|
||||
/* Get the time from the current swdb file and store it at R_TIME. If
|
||||
* the file does not exist 0 is stored at R_TIME. The function
|
||||
* returns 0 on sucess or an error code. */
|
||||
static gpg_error_t
|
||||
time_of_saved_swdb (const char *fname, time_t *r_time)
|
||||
{
|
||||
gpg_error_t err;
|
||||
estream_t fp = NULL;
|
||||
char *line = NULL;
|
||||
size_t length_of_line = 0;
|
||||
size_t maxlen;
|
||||
ssize_t len;
|
||||
char *fields[2];
|
||||
time_t t = (time_t)(-1);
|
||||
|
||||
*r_time = 0;
|
||||
|
||||
fp = es_fopen (fname, "r");
|
||||
err = fp? 0 : gpg_error_from_syserror ();
|
||||
if (err)
|
||||
{
|
||||
if (gpg_err_code (err) == GPG_ERR_ENOENT)
|
||||
err = 0; /* No file - assume time is the year of Unix. */
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Note that the parser uses the first occurance of a matching
|
||||
* values and ignores possible duplicated values. */
|
||||
maxlen = 2048; /* Set limit. */
|
||||
while ((len = es_read_line (fp, &line, &length_of_line, &maxlen)) > 0)
|
||||
{
|
||||
if (!maxlen)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_LINE_TOO_LONG);
|
||||
goto leave;
|
||||
}
|
||||
/* Strip newline and carriage return, if present. */
|
||||
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
|
||||
line[--len] = '\0';
|
||||
|
||||
if (split_fields (line, fields, DIM (fields)) < DIM(fields))
|
||||
continue; /* Skip empty lines and names w/o a value. */
|
||||
if (*fields[0] == '#')
|
||||
continue; /* Skip comments. */
|
||||
|
||||
/* Record the meta data. */
|
||||
if (!strcmp (fields[0], ".filedate"))
|
||||
{
|
||||
gnupg_isotime_t isot;
|
||||
if (string2isotime (isot, fields[1])
|
||||
&& (t = isotime2epoch (isot)) != (time_t)(-1))
|
||||
break; /* Got the time - stop reading. */
|
||||
}
|
||||
}
|
||||
if (len < 0 || es_ferror (fp))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
if (t == (time_t)(-1))
|
||||
{
|
||||
err = gpg_error (GPG_ERR_INV_TIME);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
*r_time = t;
|
||||
|
||||
leave:
|
||||
if (err)
|
||||
log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (err));
|
||||
xfree (line);
|
||||
es_fclose (fp);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Read a file from URL and return it as an estream memory buffer at
|
||||
* R_FP. */
|
||||
static gpg_error_t
|
||||
fetch_file (ctrl_t ctrl, const char *url, estream_t *r_fp)
|
||||
{
|
||||
gpg_error_t err;
|
||||
estream_t fp = NULL;
|
||||
estream_t httpfp = NULL;
|
||||
size_t nread, nwritten;
|
||||
char buffer[1024];
|
||||
|
||||
if ((err = ks_http_fetch (ctrl, url, &httpfp)))
|
||||
goto leave;
|
||||
|
||||
/* We now read the data from the web server into a memory buffer.
|
||||
* To avoid excessive memory use in case of a ill behaving server we
|
||||
* put a 64 k size limit on the buffer. As of today the actual size
|
||||
* of the swdb.lst file is 3k. */
|
||||
fp = es_fopenmem (64*1024, "rw");
|
||||
if (!fp)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (es_read (httpfp, buffer, sizeof buffer, &nread))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error reading '%s': %s\n",
|
||||
es_fname_get (httpfp), gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (!nread)
|
||||
break; /* Ready. */
|
||||
if (es_write (fp, buffer, nread, &nwritten))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error writing '%s': %s\n",
|
||||
es_fname_get (fp), gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
else if (nread != nwritten)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_EIO);
|
||||
log_error ("error writing '%s': %s\n",
|
||||
es_fname_get (fp), "short write");
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
es_rewind (fp);
|
||||
*r_fp = fp;
|
||||
fp = NULL;
|
||||
|
||||
leave:
|
||||
es_fclose (httpfp);
|
||||
es_fclose (fp);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Communication object for verify_status_cb. */
|
||||
struct verify_status_parm_s
|
||||
{
|
||||
time_t sigtime;
|
||||
int anyvalid;
|
||||
};
|
||||
|
||||
static void
|
||||
verify_status_cb (void *opaque, const char *keyword, char *args)
|
||||
{
|
||||
struct verify_status_parm_s *parm = opaque;
|
||||
|
||||
/* We care only about the first valid signature. */
|
||||
if (!strcmp (keyword, "VALIDSIG") && !parm->anyvalid)
|
||||
{
|
||||
char *fields[3];
|
||||
|
||||
parm->anyvalid = 1;
|
||||
if (split_fields (args, fields, DIM (fields)) >= 3)
|
||||
parm->sigtime = parse_timestamp (fields[2], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Load the swdb file into the current home directory. Do this onlky
|
||||
* when needed unless FORCE is set which will always get a new
|
||||
* copy. */
|
||||
gpg_error_t
|
||||
dirmngr_load_swdb (ctrl_t ctrl, int force)
|
||||
{
|
||||
gpg_error_t err;
|
||||
char *fname = NULL; /* The swdb.lst file. */
|
||||
char *tmp_fname = NULL; /* The temporary swdb.lst file. */
|
||||
char *keyfile_fname = NULL;
|
||||
estream_t swdb = NULL;
|
||||
estream_t swdb_sig = NULL;
|
||||
ccparray_t ccp;
|
||||
const char **argv = NULL;
|
||||
struct verify_status_parm_s verify_status_parm = { (time_t)(-1), 0 };
|
||||
estream_t outfp = NULL;
|
||||
time_t now = gnupg_get_time ();
|
||||
gnupg_isotime_t isotime;
|
||||
|
||||
|
||||
fname = make_filename_try (gnupg_homedir (), "swdb.lst", NULL);
|
||||
if (!fname)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Check whether there is a need to get an update. */
|
||||
if (!force)
|
||||
{
|
||||
time_t filetime;
|
||||
|
||||
err = time_of_saved_swdb (fname, &filetime);
|
||||
if (err)
|
||||
goto leave;
|
||||
if (filetime >= now)
|
||||
goto leave; /* Current or newer. */
|
||||
if (now - filetime < 3*86400)
|
||||
goto leave; /* Not older than 3 days. */
|
||||
}
|
||||
|
||||
/* Create the filename of the file with the keys. */
|
||||
keyfile_fname = make_filename_try (gnupg_datadir (), "distsigkey.gpg", NULL);
|
||||
if (!keyfile_fname)
|
||||
goto leave;
|
||||
|
||||
/* Fetch the swdb from the web. */
|
||||
err = fetch_file (ctrl, "https://versions.gnupg.org/swdb.lst", &swdb);
|
||||
if (err)
|
||||
goto leave;
|
||||
err = fetch_file (ctrl, "https://versions.gnupg.org/swdb.lst.sig", &swdb_sig);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
/* Run gpgv. */
|
||||
ccparray_init (&ccp, 0);
|
||||
ccparray_put (&ccp, "--enable-special-filenames");
|
||||
ccparray_put (&ccp, "--status-fd=2");
|
||||
ccparray_put (&ccp, "--keyring");
|
||||
ccparray_put (&ccp, keyfile_fname);
|
||||
ccparray_put (&ccp, "--");
|
||||
ccparray_put (&ccp, "-&@INEXTRA@");
|
||||
ccparray_put (&ccp, "-");
|
||||
ccparray_put (&ccp, NULL);
|
||||
argv = ccparray_get (&ccp, NULL);
|
||||
if (!argv)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = gnupg_exec_tool_stream (gnupg_module_name (GNUPG_MODULE_NAME_GPGV),
|
||||
argv, swdb, swdb_sig, NULL,
|
||||
verify_status_cb, &verify_status_parm);
|
||||
if (!err && verify_status_parm.sigtime == (time_t)(-1))
|
||||
err = gpg_error (verify_status_parm.anyvalid? GPG_ERR_BAD_SIGNATURE
|
||||
/**/ : GPG_ERR_INV_TIME );
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
/* Create a file name for a temporary file in the home directory.
|
||||
* We will later rename that file to the real name. */
|
||||
{
|
||||
char *tmpstr;
|
||||
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
tmpstr = es_bsprintf ("tmp-%u-swdb", (unsigned int)getpid ());
|
||||
#else
|
||||
tmpstr = es_bsprintf (".#%u.swdb", (unsigned int)getpid ());
|
||||
#endif
|
||||
if (!tmpstr)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
tmp_fname = make_filename_try (gnupg_homedir (), tmpstr, NULL);
|
||||
xfree (tmpstr);
|
||||
if (!tmp_fname)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
outfp = es_fopen (tmp_fname, "w");
|
||||
if (!outfp)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error (_("error creating '%s': %s\n"), tmp_fname, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
epoch2isotime (isotime, verify_status_parm.sigtime);
|
||||
es_fprintf (outfp, ".filedate %s\n", isotime);
|
||||
epoch2isotime (isotime, now);
|
||||
es_fprintf (outfp, ".verified %s\n", isotime);
|
||||
|
||||
if (es_fseek (swdb, 0, SEEK_SET))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = copy_stream (swdb, outfp);
|
||||
if (err)
|
||||
{
|
||||
/* Well, it might also be a reading error, but that is pretty
|
||||
* unlikely for a memory stream. */
|
||||
log_error (_("error writing '%s': %s\n"), tmp_fname, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (es_fclose (outfp))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error (_("error writing '%s': %s\n"), tmp_fname, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
outfp = NULL;
|
||||
|
||||
err = gnupg_rename_file (tmp_fname, fname, NULL);
|
||||
if (err)
|
||||
goto leave;
|
||||
xfree (tmp_fname);
|
||||
tmp_fname = NULL;
|
||||
|
||||
|
||||
leave:
|
||||
es_fclose (outfp);
|
||||
if (tmp_fname)
|
||||
gnupg_remove (tmp_fname); /* This is a temporary file. */
|
||||
xfree (argv);
|
||||
es_fclose (swdb_sig);
|
||||
es_fclose (swdb);
|
||||
xfree (keyfile_fname);
|
||||
xfree (tmp_fname);
|
||||
xfree (fname);
|
||||
return err;
|
||||
}
|
402
dirmngr/server.c
402
dirmngr/server.c
@ -54,8 +54,6 @@
|
||||
#include "mbox-util.h"
|
||||
#include "zb32.h"
|
||||
#include "server-help.h"
|
||||
#include "ccparray.h"
|
||||
#include "../common/exectool.h"
|
||||
|
||||
/* To avoid DoS attacks we limit the size of a certificate to
|
||||
something reasonable. The DoS was actually only an issue back when
|
||||
@ -2239,6 +2237,22 @@ cmd_ks_put (assuan_context_t ctx, char *line)
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char hlp_loadswdb[] =
|
||||
"LOADSWDB [--force]\n"
|
||||
"\n"
|
||||
"Load and verify the swdb.lst from the Net.";
|
||||
static gpg_error_t
|
||||
cmd_loadswdb (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
gpg_error_t err;
|
||||
|
||||
err = dirmngr_load_swdb (ctrl, has_option (line, "--force"));
|
||||
|
||||
return leave_cmd (ctx, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char hlp_getinfo[] =
|
||||
@ -2342,388 +2356,6 @@ cmd_reloaddirmngr (assuan_context_t ctx, char *line)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This function parses the first portion of the version number S and
|
||||
* stores it in *NUMBER. On success, this function returns a pointer
|
||||
* into S starting with the first character, which is not part of the
|
||||
* initial number portion; on failure, NULL is returned. */
|
||||
static const char*
|
||||
parse_version_number (const char *s, int *number)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
if (*s == '0' && digitp (&s[1]))
|
||||
return NULL; /* Leading zeros are not allowed. */
|
||||
for (; digitp (s); s++)
|
||||
{
|
||||
val *= 10;
|
||||
val += *s - '0';
|
||||
}
|
||||
*number = val;
|
||||
return val < 0 ? NULL : s;
|
||||
}
|
||||
|
||||
|
||||
/* This function breaks up the complete string-representation of the
|
||||
* version number S, which is of the following struture: <major
|
||||
* number>.<minor number>[.<micro number>]<patch level>. The major,
|
||||
* minor and micro number components will be stored in *MAJOR, *MINOR
|
||||
* and *MICRO. If MICRO is not given 0 is used instead.
|
||||
*
|
||||
* On success, the last component, the patch level, will be returned;
|
||||
* on failure, NULL will be returned. */
|
||||
static const char *
|
||||
parse_version_string (const char *s, int *major, int *minor, int *micro)
|
||||
{
|
||||
s = parse_version_number (s, major);
|
||||
if (!s || *s != '.')
|
||||
return NULL;
|
||||
s++;
|
||||
s = parse_version_number (s, minor);
|
||||
if (!s)
|
||||
return NULL;
|
||||
if (*s == '.')
|
||||
{
|
||||
s++;
|
||||
s = parse_version_number (s, micro);
|
||||
if (!s)
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
micro = 0;
|
||||
return s; /* Patchlevel. */
|
||||
}
|
||||
|
||||
|
||||
/* Create temporary directory with mode 0700. Returns a dynamically
|
||||
* allocated string with the filename of the directory. */
|
||||
static char *
|
||||
my_mktmpdir (void)
|
||||
{
|
||||
char *name, *p;
|
||||
|
||||
p = getenv ("TMPDIR");
|
||||
if (!p || !*p)
|
||||
p = "/tmp";
|
||||
if (p[strlen (p) - 1] == '/')
|
||||
name = strconcat (p, "gpg-XXXXXX", NULL);
|
||||
else
|
||||
name = strconcat (p, "/", "gpg-XXXXXX", NULL);
|
||||
if (!name || !gnupg_mkdtemp (name))
|
||||
{
|
||||
int saveerr = errno;
|
||||
log_error (_("can't create temporary directory '%s': %s\n"),
|
||||
name, strerror (saveerr));
|
||||
gpg_err_set_errno (saveerr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
/* Sets result to -1 if version a is less than b, 0 if the versions are equal
|
||||
* and 1 otherwise. Patch levels are compared as strings. */
|
||||
static gpg_error_t
|
||||
cmp_version (const char *a, const char *b, int *result)
|
||||
{
|
||||
int a_major, b_major;
|
||||
int a_minor, b_minor;
|
||||
int a_micro, b_micro;
|
||||
const char *a_patch, *b_patch;
|
||||
|
||||
if (!a || !b || !result)
|
||||
return gpg_error (GPG_ERR_EINVAL);
|
||||
|
||||
a_patch = parse_version_string (a, &a_major, &a_minor, &a_micro);
|
||||
b_patch = parse_version_string (b, &b_major, &b_minor, &b_micro);
|
||||
|
||||
if (!a_patch || !b_patch)
|
||||
return gpg_error (GPG_ERR_EINVAL);
|
||||
|
||||
if (a_major == b_major)
|
||||
{
|
||||
if (a_minor == b_minor)
|
||||
{
|
||||
if (a_micro == b_micro)
|
||||
*result = strcmp (a_patch, b_patch);
|
||||
else
|
||||
*result = a_micro - b_minor;
|
||||
}
|
||||
else
|
||||
*result = a_minor - b_minor;
|
||||
}
|
||||
else
|
||||
*result = a_major - b_major;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static gpg_error_t
|
||||
fetch_into_tmpdir (ctrl_t ctrl, const char *url, estream_t *strm_out,
|
||||
char **path)
|
||||
{
|
||||
gpg_error_t err;
|
||||
char *filename = NULL;
|
||||
char *dirname = NULL;
|
||||
estream_t file = NULL;
|
||||
estream_t strm = NULL;
|
||||
size_t len, nwritten;
|
||||
char buf[1024];
|
||||
|
||||
if (!strm_out || !path || !url)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_INV_ARG);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
dirname = my_mktmpdir ();
|
||||
if (!dirname)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
filename = strconcat (dirname, DIRSEP_S, "file", NULL);
|
||||
if (!filename)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
file = es_fopen (filename, "w+");
|
||||
if (!file)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if ((err = ks_http_fetch (ctrl, url, &strm)))
|
||||
goto leave;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (es_read (strm, buf, sizeof buf, &len))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error reading '%s': %s\n",
|
||||
es_fname_get (strm), gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (!len)
|
||||
break;
|
||||
if (es_write (file, buf, len, &nwritten))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error writing '%s': %s\n", filename, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
else if (len != nwritten)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_EIO);
|
||||
log_error ("error writing '%s': %s\n", filename, "short write");
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
es_rewind (file);
|
||||
*strm_out = file;
|
||||
file = NULL;
|
||||
|
||||
if (path)
|
||||
{
|
||||
*path = dirname;
|
||||
dirname = NULL;
|
||||
}
|
||||
|
||||
leave:
|
||||
es_fclose (file);
|
||||
es_fclose (strm);
|
||||
xfree (dirname);
|
||||
xfree (filename);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
struct verify_swdb_parm_s
|
||||
{
|
||||
time_t sigtime;
|
||||
int anyvalid;
|
||||
};
|
||||
|
||||
static void
|
||||
verify_swdb_status_cb (void *opaque, const char *keyword, char *args)
|
||||
{
|
||||
struct verify_swdb_parm_s *parm = opaque;
|
||||
|
||||
/* We care only about the first valid signature. */
|
||||
if (!strcmp (keyword, "VALIDSIG") && !parm->anyvalid)
|
||||
{
|
||||
char *fields[3];
|
||||
|
||||
parm->anyvalid = 1;
|
||||
if (split_fields (args, fields, DIM (fields)) >= 3)
|
||||
parm->sigtime = parse_timestamp (fields[2], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char hlp_versioncheck[] =
|
||||
"VERSIONCHECK <name> <version>"
|
||||
"\n"
|
||||
"Checks the internet to find whenever a new program version is available."
|
||||
"\n"
|
||||
"<name> program name i.e. \"gnupg\"\n"
|
||||
"<version> current version of the program i.e. \"2.0.2\"";
|
||||
static gpg_error_t
|
||||
cmd_versioncheck (assuan_context_t ctx, char *line)
|
||||
{
|
||||
gpg_error_t err;
|
||||
|
||||
char *name;
|
||||
char *version;
|
||||
size_t name_len;
|
||||
char *cmd_fields[2];
|
||||
|
||||
ctrl_t ctrl;
|
||||
estream_t swdb = NULL;
|
||||
estream_t swdb_sig = NULL;
|
||||
char* swdb_dir = NULL;
|
||||
char* swdb_sig_dir = NULL;
|
||||
char* buf = NULL;
|
||||
size_t len = 0;
|
||||
ccparray_t ccp;
|
||||
const char **argv = NULL;
|
||||
char keyring_name[128];
|
||||
char swdb_name[128];
|
||||
char swdb_sig_name[128];
|
||||
|
||||
struct verify_swdb_parm_s verify_swdb_parm = { (time_t)(-1), 0 };
|
||||
|
||||
|
||||
swdb_name[0] = 0;
|
||||
swdb_sig_name[0] = 0;
|
||||
ctrl = assuan_get_pointer (ctx);
|
||||
|
||||
if (split_fields (line, cmd_fields, 2) != 2)
|
||||
{
|
||||
err = set_error (GPG_ERR_ASS_PARAMETER,
|
||||
"No program name and/or version given");
|
||||
goto out;
|
||||
}
|
||||
|
||||
name = cmd_fields[0];
|
||||
name_len = strlen (name);
|
||||
version = cmd_fields[1];
|
||||
|
||||
if ((err = fetch_into_tmpdir (ctrl, "https://versions.gnupg.org/swdb.lst",
|
||||
&swdb, &swdb_dir)))
|
||||
goto out;
|
||||
|
||||
snprintf (swdb_name, sizeof swdb_name, "%s%s%s", swdb_dir, DIRSEP_S, "file");
|
||||
|
||||
if ((err = fetch_into_tmpdir (ctrl, "https://versions.gnupg.org/swdb.lst.sig",
|
||||
&swdb_sig, &swdb_sig_dir)))
|
||||
goto out;
|
||||
|
||||
snprintf (keyring_name, sizeof keyring_name, "%s%s%s", gnupg_datadir (),
|
||||
DIRSEP_S, "distsigkey.gpg");
|
||||
snprintf (swdb_sig_name, sizeof swdb_sig_name, "%s%s%s", swdb_sig_dir,
|
||||
DIRSEP_S, "file");
|
||||
|
||||
ccparray_init (&ccp, 0);
|
||||
ccparray_put (&ccp, "--status-fd=2");
|
||||
ccparray_put (&ccp, "--keyring");
|
||||
ccparray_put (&ccp, keyring_name);
|
||||
ccparray_put (&ccp, "--");
|
||||
ccparray_put (&ccp, swdb_sig_name);
|
||||
ccparray_put (&ccp, "-");
|
||||
ccparray_put (&ccp, NULL);
|
||||
argv = ccparray_get (&ccp, NULL);
|
||||
if (!argv)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((err = gnupg_exec_tool_stream (gnupg_module_name (GNUPG_MODULE_NAME_GPGV),
|
||||
argv, swdb, NULL, NULL,
|
||||
verify_swdb_status_cb, &verify_swdb_parm)))
|
||||
goto out;
|
||||
if (verify_swdb_parm.sigtime == (time_t)(-1))
|
||||
{
|
||||
if (verify_swdb_parm.anyvalid)
|
||||
err = gpg_error (GPG_ERR_BAD_SIGNATURE);
|
||||
else
|
||||
err = gpg_error (GPG_ERR_INV_TIME);
|
||||
goto out;
|
||||
}
|
||||
|
||||
{
|
||||
gnupg_isotime_t tbuf;
|
||||
|
||||
epoch2isotime (tbuf, verify_swdb_parm.sigtime);
|
||||
log_debug ("swdb created: %s\n", tbuf);
|
||||
}
|
||||
|
||||
es_fseek (swdb, 0, SEEK_SET);
|
||||
|
||||
while (es_getline (&buf, &len, swdb) > 0)
|
||||
{
|
||||
if (len > name_len + 5 &&
|
||||
strncmp (buf, name, name_len) == 0 &&
|
||||
strncmp (buf + name_len, "_ver ", 5) == 0)
|
||||
{
|
||||
const char* this_ver_start = buf + name_len + 5;
|
||||
char* this_ver_end = strchr (this_ver_start, '\n');
|
||||
int cmp;
|
||||
|
||||
if (this_ver_end)
|
||||
*this_ver_end = 0;
|
||||
|
||||
err = assuan_write_status (ctx, "LINE", buf);
|
||||
|
||||
err = cmp_version (this_ver_start, version, &cmp);
|
||||
if (err > 0)
|
||||
goto out;
|
||||
|
||||
if (cmp < 0)
|
||||
err = assuan_send_data (ctx, "ROLLBACK", strlen ("ROLLBACK"));
|
||||
else if (cmp == 0)
|
||||
err = assuan_send_data (ctx, "CURRENT", strlen ("CURRENT"));
|
||||
else
|
||||
err = assuan_send_data (ctx, "UPDATE", strlen ("UPDATE"));
|
||||
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
err = assuan_send_data (ctx, "NOT_FOUND", strlen ("NOT_FOUND"));
|
||||
|
||||
out:
|
||||
es_fclose (swdb);
|
||||
es_fclose (swdb_sig);
|
||||
xfree (buf);
|
||||
|
||||
if (strlen (swdb_name) > 0)
|
||||
remove (swdb_name);
|
||||
if (swdb_dir)
|
||||
rmdir (swdb_dir);
|
||||
xfree (swdb_dir);
|
||||
|
||||
if (strlen (swdb_sig_name) > 0)
|
||||
remove (swdb_sig_name);
|
||||
if (swdb_sig_dir)
|
||||
rmdir (swdb_sig_dir);
|
||||
xfree (swdb_sig_dir);
|
||||
xfree (argv);
|
||||
|
||||
return leave_cmd (ctx, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Tell the assuan library about our commands. */
|
||||
static int
|
||||
@ -2751,9 +2383,9 @@ register_commands (assuan_context_t ctx)
|
||||
{ "KS_FETCH", cmd_ks_fetch, hlp_ks_fetch },
|
||||
{ "KS_PUT", cmd_ks_put, hlp_ks_put },
|
||||
{ "GETINFO", cmd_getinfo, hlp_getinfo },
|
||||
{ "LOADSWDB", cmd_loadswdb, hlp_loadswdb },
|
||||
{ "KILLDIRMNGR",cmd_killdirmngr,hlp_killdirmngr },
|
||||
{ "RELOADDIRMNGR",cmd_reloaddirmngr,hlp_reloaddirmngr },
|
||||
{ "VERSIONCHECK",cmd_versioncheck,hlp_versioncheck },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
int i, j, rc;
|
||||
|
Loading…
x
Reference in New Issue
Block a user