aboutsummaryrefslogtreecommitdiffstats
path: root/dirmngr/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'dirmngr/server.c')
-rw-r--r--dirmngr/server.c227
1 files changed, 227 insertions, 0 deletions
diff --git a/dirmngr/server.c b/dirmngr/server.c
index e5cf2c3aa..22d6f2d1e 100644
--- a/dirmngr/server.c
+++ b/dirmngr/server.c
@@ -54,6 +54,7 @@
#include "mbox-util.h"
#include "zb32.h"
#include "server-help.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
@@ -2340,6 +2341,231 @@ cmd_reloaddirmngr (assuan_context_t ctx, char *line)
}
+
+/* Returns -1 if version a is less than b, 0 if the versions are equal and 1 otherwise.
+ * Versions are compared as period-separated tuples starting at the front. Elements are
+ * interpreted as decimals first. If this fails strcmp is used. Comparison continues
+ * until two elements are found the be unequal of the end is reached. */
+static int
+cmp_version(const char* a, const char* b)
+{
+ char *a_dup, *b_dup, *strtok_internal_a = NULL, *strtok_internal_b = NULL, *a_comp, *b_comp;
+ int ret = 0;
+
+ assert (a && b);
+
+ a_dup = xstrdup (a);
+ b_dup = xstrdup (b);
+ a_comp = strtok_r (a_dup, ".", &strtok_internal_a);
+ b_comp = strtok_r (b_dup, ".", &strtok_internal_b);
+
+ while (a_comp || b_comp)
+ {
+ if (a_comp && *a_comp && b_comp && *b_comp)
+ {
+ char* a_end;
+ char* b_end;
+ int a_ver = strtol (a_comp, &a_end, 10);
+ int b_ver = strtol (b_comp, &b_end, 10);
+
+ if (!*a_end && !*b_end)
+ {
+ if (a_ver != b_ver)
+ {
+ ret = a_ver - b_ver;
+ break;
+ }
+ }
+ else
+ {
+ int r = strcmp (a_comp, b_comp);
+ if (r != 0)
+ {
+ ret = r;
+ break;
+ }
+ }
+ }
+ else
+ {
+ if ((!a_comp || !*a_comp) && b_comp && *b_comp)
+ ret = -1;
+ else if (a_comp && *a_comp && (!b_comp || !*b_comp))
+ ret = 1;
+ else
+ ret = 0;
+ break;
+ }
+
+ a_comp = strtok_r (NULL, ".", &strtok_internal_a);
+ b_comp = strtok_r (NULL, ".", &strtok_internal_b);
+ }
+
+ xfree (a_dup);
+ xfree (b_dup);
+ return ret;
+}
+
+static int
+fetch_into_tmpfile(const char* url, ctrl_t ctrl, estream_t* strm_out, char** path)
+{
+ gpg_error_t err = 0;
+ char* filename = xmalloc (128);
+ int fd = -1;
+ estream_t file;
+ estream_t strm;
+ size_t len = 0;
+ char buf[1024];
+
+ if (!strm_out)
+ {
+ err = (GPG_ERR_INV_ARG);
+ goto leave;
+ }
+
+ snprintf (filename ,128 ,"%s%s%s" ,P_tmpdir ,DIRSEP_S ,"dirmngr_fetch_XXXXXX");
+
+ if ((fd = mkstemp (filename)) < 0)
+ {
+ err = gpg_err_code_from_syserror ();
+ goto leave;
+ }
+
+ file = es_fdopen (fd, "w+");
+
+ if ((err = ks_http_fetch (ctrl, url, &strm)))
+ goto leave;
+
+ while (!es_read (strm, buf, 1024, &len))
+ {
+ if (!len)
+ break;
+ if ((err = es_write (file, buf, len, NULL)))
+ {
+ log_error ("error writing message to pipe: %s\n", gpg_strerror (err));
+ es_free (strm);
+ goto leave;
+ }
+ }
+
+ es_rewind (file);
+ es_fclose (strm);
+ *strm_out = file;
+
+ if (path)
+ {
+ *path = filename;
+ filename = NULL;
+ }
+
+leave:
+ if (filename)
+ xfree (filename);
+ return err;
+}
+
+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\""
+ "<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* strtok_internal = NULL;
+ char* name = strtok_r (line, " ", &strtok_internal);
+ char* version = strtok_r (NULL, " ", &strtok_internal);
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ estream_t swdb;
+ estream_t swdb_sig;
+ char* swdb_path = NULL;
+ char* swdb_sig_path = NULL;
+ char* buf = NULL;
+ size_t len = 0;
+ const size_t name_len = (name ? strlen (name) : 0);
+ const size_t version_len = (version ? strlen (version) : 0);
+ const char *argv[8];
+ char keyring_path[128];
+
+ if (!name || name_len == 0)
+ {
+ err = set_error (GPG_ERR_ASS_PARAMETER, "No program name given");
+ goto out;
+ }
+
+ if (!version || version_len == 0)
+ {
+ err = set_error (GPG_ERR_ASS_PARAMETER, "No program version given");
+ goto out;
+ }
+
+ if ((err = fetch_into_tmpfile ("https://versions.gnupg.org/swdb.lst", ctrl, &swdb, &swdb_path)))
+ goto out;
+
+ if ((err = fetch_into_tmpfile ("https://versions.gnupg.org/swdb.lst.sig", ctrl, &swdb_sig, &swdb_sig_path)))
+ goto out;
+
+ snprintf(keyring_path, 128, "%s%s%s", gnupg_datadir (), DIRSEP_S, "distsigkey.gpg");
+
+ argv[0] = "--batch";
+ argv[1] = "--no-default-keyring";
+ argv[2] = "--keyring";
+ argv[3] = keyring_path;
+ argv[4] = "--verify";
+ argv[5] = swdb_sig_path;
+ argv[6] = "-";
+ argv[7] = NULL;
+
+ if ((err = gnupg_exec_tool_stream(gnupg_module_name (GNUPG_MODULE_NAME_GPG),
+ argv, swdb, NULL, NULL, NULL, NULL)))
+ goto out;
+
+ 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);
+
+ cmp = cmp_version(this_ver_start,version);
+
+ 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);
+ unlink(swdb_path);
+ unlink(swdb_sig_path);
+ xfree(swdb_path);
+ xfree(swdb_sig_path);
+ return leave_cmd (ctx, err);
+}
+
/* Tell the assuan library about our commands. */
@@ -2370,6 +2596,7 @@ register_commands (assuan_context_t ctx)
{ "GETINFO", cmd_getinfo, hlp_getinfo },
{ "KILLDIRMNGR",cmd_killdirmngr,hlp_killdirmngr },
{ "RELOADDIRMNGR",cmd_reloaddirmngr,hlp_reloaddirmngr },
+ { "VERSIONCHECK",cmd_versioncheck,hlp_versioncheck },
{ NULL, NULL }
};
int i, j, rc;