aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/Makefile.am3
-rw-r--r--common/asshelp.c117
-rw-r--r--common/exechelp-posix.c50
-rw-r--r--common/logging.c28
-rw-r--r--common/logging.h2
-rw-r--r--common/pkscreening.c159
-rw-r--r--common/pkscreening.h26
-rw-r--r--common/sysutils.c7
-rw-r--r--common/util.h5
9 files changed, 314 insertions, 83 deletions
diff --git a/common/Makefile.am b/common/Makefile.am
index fcbe7ea66..94318dae4 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -94,7 +94,8 @@ common_sources = \
name-value.c name-value.h \
recsel.c recsel.h \
ksba-io-support.c ksba-io-support.h \
- compliance.c compliance.h
+ compliance.c compliance.h \
+ pkscreening.c pkscreening.h
if HAVE_W32_SYSTEM
diff --git a/common/asshelp.c b/common/asshelp.c
index f3a92f9e5..b9c0cf441 100644
--- a/common/asshelp.c
+++ b/common/asshelp.c
@@ -307,6 +307,71 @@ unlock_spawning (lock_spawn_t *lock, const char *name)
}
}
+
+/* Helper for start_new_gpg_agent and start_new_dirmngr.
+ * Values for WHICH are:
+ * 0 - Start gpg-agent
+ * 1 - Start dirmngr
+ * SECS give the number of seconds to wait. SOCKNAME is the name of
+ * the socket to connect. VERBOSE is the usual verbose flag. CTX is
+ * the assuan context. DID_SUCCESS_MSG will be set to 1 if a success
+ * messages has been printed.
+ */
+static gpg_error_t
+wait_for_sock (int secs, int which, const char *sockname,
+ int verbose, assuan_context_t ctx, int *did_success_msg)
+{
+ gpg_error_t err = 0;
+ int target_us = secs * 1000000;
+ int elapsed_us = 0;
+ /*
+ * 977us * 1024 = just a little more than 1s.
+ * so we will double this timeout 10 times in the first
+ * second, and then switch over to 1s checkins.
+ */
+ int next_sleep_us = 977;
+ int lastalert = secs+1;
+ int secsleft;
+
+ while (elapsed_us < target_us)
+ {
+ if (verbose)
+ {
+ secsleft = (target_us - elapsed_us + 999999)/1000000;
+ /* log_clock ("left=%d last=%d targ=%d elap=%d next=%d\n", */
+ /* secsleft, lastalert, target_us, elapsed_us, */
+ /* next_sleep_us); */
+ if (secsleft < lastalert)
+ {
+ log_info (which == 1?
+ _("waiting for the dirmngr to come up ... (%ds)\n"):
+ _("waiting for the agent to come up ... (%ds)\n"),
+ secsleft);
+ lastalert = secsleft;
+ }
+ }
+ gnupg_usleep (next_sleep_us);
+ elapsed_us += next_sleep_us;
+ err = assuan_socket_connect (ctx, sockname, 0, 0);
+ if (!err)
+ {
+ if (verbose)
+ {
+ log_info (which == 1?
+ _("connection to the dirmngr established\n"):
+ _("connection to the agent established\n"));
+ *did_success_msg = 1;
+ }
+ break;
+ }
+ next_sleep_us *= 2;
+ if (next_sleep_us > 1000000)
+ next_sleep_us = 1000000;
+ }
+ return err;
+}
+
+
/* Try to connect to the agent via socket or start it if it is not
running and AUTOSTART is set. Handle the server's initial
greeting. Returns a new assuan context at R_CTX or an error
@@ -433,25 +498,8 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
log_error ("failed to start agent '%s': %s\n",
agent_program, gpg_strerror (err));
else
- {
- for (i=0; i < SECS_TO_WAIT_FOR_AGENT; i++)
- {
- if (verbose)
- log_info (_("waiting for the agent to come up ... (%ds)\n"),
- SECS_TO_WAIT_FOR_AGENT - i);
- gnupg_sleep (1);
- err = assuan_socket_connect (ctx, sockname, 0, 0);
- if (!err)
- {
- if (verbose)
- {
- log_info (_("connection to agent established\n"));
- did_success_msg = 1;
- }
- break;
- }
- }
- }
+ err = wait_for_sock (SECS_TO_WAIT_FOR_AGENT, 0,
+ sockname, verbose, ctx, &did_success_msg);
}
unlock_spawning (&lock, "agent");
@@ -468,7 +516,7 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
}
if (debug && !did_success_msg)
- log_debug ("connection to agent established\n");
+ log_debug ("connection to the agent established\n");
err = assuan_transact (ctx, "RESET",
NULL, NULL, NULL, NULL, NULL, NULL);
@@ -485,7 +533,7 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
NULL, NULL, NULL, NULL, NULL, NULL))
{
if (verbose)
- log_info (_("connection to agent is in restricted mode\n"));
+ log_info (_("connection to the agent is in restricted mode\n"));
err = 0;
}
}
@@ -542,7 +590,7 @@ start_new_dirmngr (assuan_context_t *r_ctx,
dirmngr_program = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR);
if (verbose)
- log_info (_("no running Dirmngr - starting '%s'\n"),
+ log_info (_("no running dirmngr - starting '%s'\n"),
dirmngr_program);
if (status_cb)
@@ -584,29 +632,8 @@ start_new_dirmngr (assuan_context_t *r_ctx,
log_error ("failed to start the dirmngr '%s': %s\n",
dirmngr_program, gpg_strerror (err));
else
- {
- int i;
-
- for (i=0; i < SECS_TO_WAIT_FOR_DIRMNGR; i++)
- {
- if (verbose)
- log_info (_("waiting for the dirmngr "
- "to come up ... (%ds)\n"),
- SECS_TO_WAIT_FOR_DIRMNGR - i);
- gnupg_sleep (1);
- err = assuan_socket_connect (ctx, sockname, 0, 0);
- if (!err)
- {
- if (verbose)
- {
- log_info (_("connection to the dirmngr"
- " established\n"));
- did_success_msg = 1;
- }
- break;
- }
- }
- }
+ err = wait_for_sock (SECS_TO_WAIT_FOR_DIRMNGR, 1,
+ sockname, verbose, ctx, &did_success_msg);
}
unlock_spawning (&lock, "dirmngr");
diff --git a/common/exechelp-posix.c b/common/exechelp-posix.c
index 7237993a2..3acf74ad6 100644
--- a/common/exechelp-posix.c
+++ b/common/exechelp-posix.c
@@ -784,30 +784,32 @@ gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
}
}
- if (ec == 0)
- for (i = 0; i < count; i++)
- {
- if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]) == 127)
- {
- log_error (_("error running '%s': probably not installed\n"),
- pgmnames[i]);
- ec = GPG_ERR_CONFIGURATION;
- }
- else if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]))
- {
- if (dummy)
- log_error (_("error running '%s': exit status %d\n"),
- pgmnames[i], WEXITSTATUS (r_exitcodes[i]));
- else
- r_exitcodes[i] = WEXITSTATUS (r_exitcodes[i]);
- ec = GPG_ERR_GENERAL;
- }
- else if (!WIFEXITED (r_exitcodes[i]))
- {
- log_error (_("error running '%s': terminated\n"), pgmnames[i]);
- ec = GPG_ERR_GENERAL;
- }
- }
+ for (i = 0; i < count; i++)
+ {
+ if (r_exitcodes[i] == -1)
+ continue;
+
+ if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]) == 127)
+ {
+ log_error (_("error running '%s': probably not installed\n"),
+ pgmnames[i]);
+ ec = GPG_ERR_CONFIGURATION;
+ }
+ else if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]))
+ {
+ if (dummy)
+ log_error (_("error running '%s': exit status %d\n"),
+ pgmnames[i], WEXITSTATUS (r_exitcodes[i]));
+ else
+ r_exitcodes[i] = WEXITSTATUS (r_exitcodes[i]);
+ ec = GPG_ERR_GENERAL;
+ }
+ else if (!WIFEXITED (r_exitcodes[i]))
+ {
+ log_error (_("error running '%s': terminated\n"), pgmnames[i]);
+ ec = GPG_ERR_GENERAL;
+ }
+ }
xfree (dummy);
return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
diff --git a/common/logging.c b/common/logging.c
index c4eaca411..9f04a69de 100644
--- a/common/logging.c
+++ b/common/logging.c
@@ -1039,14 +1039,16 @@ log_printsexp () {}
is found in sexputils.c
*/
-
+/* Print a microsecond timestamp followed by a FORMAT. */
void
-log_clock (const char *string)
+log_clock (const char *fmt, ...)
{
-#if 0
+#if ENABLE_LOG_CLOCK
static unsigned long long initial;
struct timespec tv;
unsigned long long now;
+ char clockbuf[50];
+ va_list arg_ptr;
if (clock_gettime (CLOCK_REALTIME, &tv))
{
@@ -1059,11 +1061,21 @@ log_clock (const char *string)
if (!initial)
initial = now;
- log_debug ("[%6llu] %s", (now - initial)/1000, string);
-#else
- /* You need to link with -ltr to enable the above code. */
- log_debug ("[not enabled in the source] %s", string);
-#endif
+ snprintf (clockbuf, sizeof clockbuf, "[%6llu] ", (now - initial)/1000);
+ va_start (arg_ptr, fmt);
+ do_logv (GPGRT_LOG_DEBUG, 0, NULL, clockbuf, fmt, arg_ptr);
+ va_end (arg_ptr);
+
+#else /*!ENABLE_LOG_CLOCK*/
+
+ /* You may need to link with -ltr to use the above code. */
+ va_list arg_ptr;
+
+ va_start (arg_ptr, fmt);
+ do_logv (GPGRT_LOG_DEBUG, 0, NULL, "[no clock] ", fmt, arg_ptr);
+ va_end (arg_ptr);
+
+#endif /*!ENABLE_LOG_CLOCK*/
}
diff --git a/common/logging.h b/common/logging.h
index e1bf56b17..34843c78a 100644
--- a/common/logging.h
+++ b/common/logging.h
@@ -109,7 +109,7 @@ void log_flush (void);
by the hexdump and a final LF. */
void log_printhex (const char *text, const void *buffer, size_t length);
-void log_clock (const char *string);
+void log_clock (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2);
#endif /*GNUPG_COMMON_LOGGING_H*/
diff --git a/common/pkscreening.c b/common/pkscreening.c
new file mode 100644
index 000000000..a3bfb474e
--- /dev/null
+++ b/common/pkscreening.c
@@ -0,0 +1,159 @@
+/* pkscreening.c - Screen public keys for vulnerabilities
+ * Copyright (C) 2017 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This file 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 Lesser General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+
+#include "util.h"
+#include "pkscreening.h"
+
+
+/* Helper */
+static inline gpg_error_t
+my_error (gpg_err_code_t ec)
+{
+ return gpg_err_make (default_errsource, ec);
+}
+
+
+/* Emulation of the new gcry_mpi_get_ui function. */
+static gpg_error_t
+my_mpi_get_ui (unsigned int *v, gcry_mpi_t a)
+{
+ gpg_error_t err;
+ unsigned char buf[8];
+ size_t n;
+ int i, mul;
+
+ if (gcry_mpi_cmp_ui (a, 16384) > 0)
+ return my_error (GPG_ERR_ERANGE); /* Clearly too large for our purpose. */
+
+ err = gcry_mpi_print (GCRYMPI_FMT_USG, buf, sizeof buf, &n, a);
+ if (err)
+ return err;
+
+ *v = 0;
+ for (i = n - 1, mul = 1; i >= 0; i--, mul *= 256)
+ *v += mul * buf[i];
+
+ return 0;
+}
+
+
+/* Detect whether the MODULUS of a public RSA key is affected by the
+ * ROCA vulnerability as found in the Infinion RSA library
+ * (CVE-2017-15361). Returns 0 if not affected, GPG_ERR_TRUE if
+ * affected, GPG_ERR_BAD_MPI if an opaque RSA was passed, or other
+ * error codes if something weird happened */
+gpg_error_t
+screen_key_for_roca (gcry_mpi_t modulus)
+{
+ static struct {
+ unsigned int prime_ui;
+ const char *print_hex;
+ gcry_mpi_t prime;
+ gcry_mpi_t print;
+ } table[] = {
+ { 3, "0x6" },
+ { 5, "0x1E" },
+ { 7, "0x7E" },
+ { 11, "0x402" },
+ { 13, "0x161A" },
+ { 17, "0x1A316" },
+ { 19, "0x30AF2" },
+ { 23, "0x7FFFFE" },
+ { 29, "0x1FFFFFFE" },
+ { 31, "0x7FFFFFFE" },
+ { 37, "0x4000402" },
+ { 41, "0x1FFFFFFFFFE" },
+ { 43, "0x7FFFFFFFFFE" },
+ { 47, "0x7FFFFFFFFFFE" },
+ { 53, "0x12DD703303AED2" },
+ { 59, "0x7FFFFFFFFFFFFFE" },
+ { 61, "0x1434026619900B0A" },
+ { 67, "0x7FFFFFFFFFFFFFFFE" },
+ { 71, "0x1164729716B1D977E" },
+ { 73, "0x147811A48004962078A" },
+ { 79, "0xB4010404000640502" },
+ { 83, "0x7FFFFFFFFFFFFFFFFFFFE" },
+ { 89, "0x1FFFFFFFFFFFFFFFFFFFFFE" },
+ { 97, "0x1000000006000001800000002" },
+ { 101, "0x1FFFFFFFFFFFFFFFFFFFFFFFFE" },
+ { 103, "0x16380E9115BD964257768FE396" },
+ { 107, "0x27816EA9821633397BE6A897E1A" },
+ { 109, "0x1752639F4E85B003685CBE7192BA" },
+ { 113, "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
+ { 127, "0x6CA09850C2813205A04C81430A190536" },
+ { 131, "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
+ { 137, "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
+ { 139, "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
+ { 149, "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
+ { 151, "0x50C018BC00482458DAC35B1A2412003D18030A" },
+ { 157, "0x161FB414D76AF63826461899071BD5BACA0B7E1A" },
+ { 163, "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
+ { 167, "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" }
+ };
+ gpg_error_t err;
+ int i;
+ gcry_mpi_t rem;
+ unsigned int bitno;
+
+ /* Initialize on the first call. */
+ if (!table[0].prime)
+ {
+ /* We pass primes[i] to the call so that in case of a concurrent
+ * second thread the already allocated space is reused. */
+ for (i = 0; i < DIM (table); i++)
+ {
+ table[i].prime = gcry_mpi_set_ui (table[i].prime, table[i].prime_ui);
+ if (gcry_mpi_scan (&table[i].print, GCRYMPI_FMT_HEX,
+ table[i].print_hex, 0, NULL))
+ BUG ();
+ }
+ }
+
+ /* Check that it is not NULL or an opaque MPI. */
+ if (!modulus || gcry_mpi_get_flag (modulus, GCRYMPI_FLAG_OPAQUE))
+ return my_error (GPG_ERR_BAD_MPI);
+
+ /* We divide the modulus of an RSA public key by a set of small
+ * PRIMEs and examine all the remainders. If all the bits at the
+ * index given by the remainder are set in the corresponding PRINT
+ * masks the key is very likely vulnerable. If any of the tested
+ * bits is zero, the key is not vulnerable. */
+ rem = gcry_mpi_new (0);
+ for (i = 0; i < DIM (table); i++)
+ {
+ gcry_mpi_mod (rem, modulus, table[i].prime);
+ err = my_mpi_get_ui (&bitno, rem);
+ if (gpg_err_code (err) == GPG_ERR_ERANGE)
+ continue;
+ if (err)
+ goto leave;
+ if (!gcry_mpi_test_bit (table[i].print, bitno))
+ goto leave; /* Not vulnerable. */
+ }
+
+ /* Very likely vulnerable */
+ err = my_error (GPG_ERR_TRUE);
+
+ leave:
+ gcry_mpi_release (rem);
+ return err;
+}
diff --git a/common/pkscreening.h b/common/pkscreening.h
new file mode 100644
index 000000000..a64758924
--- /dev/null
+++ b/common/pkscreening.h
@@ -0,0 +1,26 @@
+/* pkscreening.c - Screen public keys for vulnerabilities
+ * Copyright (C) 2017 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This file 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 Lesser General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GNUPG_COMMON_PKSCREENING_H
+#define GNUPG_COMMON_PKSCREENING_H
+
+gpg_error_t screen_key_for_roca (gcry_mpi_t modulus);
+
+
+#endif /*GNUPG_COMMON_PKSCREENING_H*/
diff --git a/common/sysutils.c b/common/sysutils.c
index e90010c44..55a7ee9ec 100644
--- a/common/sysutils.c
+++ b/common/sysutils.c
@@ -340,11 +340,10 @@ gnupg_usleep (unsigned int usecs)
struct timespec req;
struct timespec rem;
- req.tv_sec = 0;
- req.tv_nsec = usecs * 1000;
-
+ req.tv_sec = usecs / 1000000;
+ req.tv_nsec = (usecs % 1000000) * 1000;
while (nanosleep (&req, &rem) < 0 && errno == EINTR)
- req = rem;
+ req = rem;
}
#else /*Standard Unix*/
diff --git a/common/util.h b/common/util.h
index c6d19c64b..f3722812d 100644
--- a/common/util.h
+++ b/common/util.h
@@ -59,6 +59,11 @@
/* Hash function used with libksba. */
#define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write)
+/* The length of the keygrip. This is a SHA-1 hash of the key
+ * parameters as generated by gcry_pk_get_keygrip. */
+#define KEYGRIP_LEN 20
+
+
/* Get all the stuff from jnlib. */
#include "../common/logging.h"
#include "../common/argparse.h"