diff --git a/Makefile.am b/Makefile.am index 06cceb344..12aff3d66 100644 --- a/Makefile.am +++ b/Makefile.am @@ -421,7 +421,9 @@ libnm_core_lib_h_priv = \ shared/nm-utils/c-list-util.h \ shared/nm-utils/nm-dedup-multi.h \ shared/nm-utils/nm-enum-utils.h \ + shared/nm-utils/nm-hash-utils.h \ shared/nm-utils/nm-shared-utils.h \ + shared/nm-utils/nm-random-utils.h \ shared/nm-utils/nm-udev-utils.h \ shared/nm-meta-setting.h \ libnm-core/crypto.h \ @@ -437,7 +439,9 @@ libnm_core_lib_c_real = \ shared/nm-utils/c-list-util.c \ shared/nm-utils/nm-dedup-multi.c \ shared/nm-utils/nm-enum-utils.c \ + shared/nm-utils/nm-hash-utils.c \ shared/nm-utils/nm-shared-utils.c \ + shared/nm-utils/nm-random-utils.c \ shared/nm-utils/nm-udev-utils.c \ shared/nm-meta-setting.c \ libnm-core/crypto.c \ diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 5b8af9b20..9a79ba47c 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -38,6 +38,7 @@ #endif #include "nm-utils/nm-enum-utils.h" +#include "nm-utils/nm-hash-utils.h" #include "nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" diff --git a/shared/nm-utils/nm-dedup-multi.c b/shared/nm-utils/nm-dedup-multi.c index e7616074b..04eec1943 100644 --- a/shared/nm-utils/nm-dedup-multi.c +++ b/shared/nm-utils/nm-dedup-multi.c @@ -23,6 +23,8 @@ #include "nm-dedup-multi.h" +#include "nm-hash-utils.h" + /*****************************************************************************/ typedef struct { diff --git a/shared/nm-utils/nm-hash-utils.c b/shared/nm-utils/nm-hash-utils.c new file mode 100644 index 000000000..6d9bb7005 --- /dev/null +++ b/shared/nm-utils/nm-hash-utils.c @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-hash-utils.h" + +#include "nm-shared-utils.h" +#include "nm-random-utils.h" + +/*****************************************************************************/ + +guint +NM_HASH_INIT (guint seed) +{ + static volatile guint global_seed = 0; + guint g, s; + + /* we xor @seed with a random @global_seed. This is to make the hashing behavior + * less predictable and harder to exploit collisions. */ + g = global_seed; + if (G_UNLIKELY (g == 0)) { + nm_utils_random_bytes (&s, sizeof (s)); + if (s == 0) + s = 42; + g_atomic_int_compare_and_exchange ((int *) &global_seed, 0, s); + g = global_seed; + nm_assert (g); + } + + return g ^ seed; +} diff --git a/shared/nm-utils/nm-hash-utils.h b/shared/nm-utils/nm-hash-utils.h new file mode 100644 index 000000000..de563b7df --- /dev/null +++ b/shared/nm-utils/nm-hash-utils.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_HASH_UTILS_H__ +#define __NM_HASH_UTILS_H__ + +guint NM_HASH_INIT (guint seed); + +static inline guint +NM_HASH_COMBINE (guint h, guint val) +{ + /* see g_str_hash() for reasons */ + return (h << 5) + h + val; +} + +static inline guint +NM_HASH_COMBINE_UINT64 (guint h, guint64 val) +{ + return NM_HASH_COMBINE (h, (((guint) val) & 0xFFFFFFFFu) + ((guint) (val >> 32))); +} + +static inline guint +NM_HASH_POINTER (gconstpointer ptr) +{ + /* same as g_direct_hash(), but inline. */ + return GPOINTER_TO_UINT (ptr); +} + +#endif /* __NM_HASH_UTILS_H__ */ diff --git a/shared/nm-utils/nm-random-utils.c b/shared/nm-utils/nm-random-utils.c new file mode 100644 index 000000000..5d9e29da8 --- /dev/null +++ b/shared/nm-utils/nm-random-utils.c @@ -0,0 +1,165 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-random-utils.h" + +#include + +#if USE_SYS_RANDOM_H +#include +#else +#include +#endif + +#include "nm-shared-utils.h" + +/*****************************************************************************/ + +/** + * nm_utils_random_bytes: + * @p: the buffer to fill + * @n: the number of bytes to write to @p. + * + * Uses getrandom() or reads /dev/urandom to fill the buffer + * with random data. If all fails, as last fallback it uses + * GRand to fill the buffer with pseudo random numbers. + * The function always succeeds in writing some random numbers + * to the buffer. The return value of FALSE indicates that the + * obtained bytes are probably not of good randomness. + * + * Returns: whether the written bytes are good. If you + * don't require good randomness, you can ignore the return + * value. + * + * Note that if calling getrandom() fails because there is not enough + * entroy (at early boot), the function will read /dev/urandom. + * Which of course, still has low entropy, and cause kernel to log + * a warning. + */ +gboolean +nm_utils_random_bytes (void *p, size_t n) +{ + int fd; + int r; + gboolean has_high_quality = TRUE; + gboolean urandom_success; + guint8 *buf = p; + gboolean avoid_urandom = FALSE; + + g_return_val_if_fail (p, FALSE); + g_return_val_if_fail (n > 0, FALSE); + +#if HAVE_GETRANDOM + { + static gboolean have_syscall = TRUE; + + if (have_syscall) { + r = getrandom (buf, n, GRND_NONBLOCK); + if (r > 0) { + if ((size_t) r == n) + return TRUE; + + /* no or partial read. There is not enough entropy. + * Fill the rest reading from urandom, and remember that + * some bits are not hight quality. */ + nm_assert (r < n); + buf += r; + n -= r; + has_high_quality = FALSE; + + /* At this point, we don't want to read /dev/urandom, because + * the entropy pool is low (early boot?), and asking for more + * entropy causes kernel messages to be logged. + * + * We use our fallback via GRand. Note that g_rand_new() also + * tries to seed itself with data from /dev/urandom, but since + * we reuse the instance, it shouldn't matter. */ + avoid_urandom = TRUE; + } else { + if (errno == ENOSYS) { + /* no support for getrandom(). We don't know whether + * we urandom will give us good quality. Assume yes. */ + have_syscall = FALSE; + } else { + /* unknown error. We'll read urandom below, but we don't have + * high-quality randomness. */ + has_high_quality = FALSE; + } + } + } + } +#endif + + urandom_success = FALSE; + if (!avoid_urandom) { +fd_open: + fd = open ("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY); + if (fd < 0) { + r = errno; + if (r == EINTR) + goto fd_open; + } else { + r = nm_utils_fd_read_loop_exact (fd, buf, n, TRUE); + close (fd); + if (r >= 0) + urandom_success = TRUE; + } + } + + if (!urandom_success) { + static _nm_thread_local GRand *rand = NULL; + gsize i; + int j; + + /* we failed to fill the bytes reading from urandom. + * Fill the bits using GRand pseudo random numbers. + * + * We don't have good quality. + */ + has_high_quality = FALSE; + + if (G_UNLIKELY (!rand)) + rand = g_rand_new (); + + nm_assert (n > 0); + i = 0; + for (;;) { + const union { + guint32 v32; + guint8 v8[4]; + } v = { + .v32 = g_rand_int (rand), + }; + + for (j = 0; j < 4; ) { + buf[i++] = v.v8[j++]; + if (i >= n) + goto done; + } + } +done: + ; + } + + return has_high_quality; +} diff --git a/shared/nm-utils/nm-random-utils.h b/shared/nm-utils/nm-random-utils.h new file mode 100644 index 000000000..15a118d34 --- /dev/null +++ b/shared/nm-utils/nm-random-utils.h @@ -0,0 +1,27 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_RANDOM_UTILS_H__ +#define __NM_RANDOM_UTILS_H__ + +gboolean nm_utils_random_bytes (void *p, size_t n); + +#endif /* __NM_RANDOM_UTILS_H__ */ diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c index ba99ce2f8..cea60edfb 100644 --- a/shared/nm-utils/nm-shared-utils.c +++ b/shared/nm-utils/nm-shared-utils.c @@ -28,12 +28,6 @@ #include #include -#if USE_SYS_RANDOM_H -#include -#else -#include -#endif - /*****************************************************************************/ const void *const _NM_PTRARRAY_EMPTY[1] = { NULL }; @@ -863,29 +857,6 @@ nm_g_object_class_find_property_from_gtype (GType gtype, /*****************************************************************************/ -guint -NM_HASH_INIT (guint seed) -{ - static volatile guint global_seed = 0; - guint g, s; - - /* we xor @seed with a random @global_seed. This is to make the hashing behavior - * less predictable and harder to exploit collisions. */ - g = global_seed; - if (G_UNLIKELY (g == 0)) { - nm_utils_random_bytes (&s, sizeof (s)); - if (s == 0) - s = 42; - g_atomic_int_compare_and_exchange ((int *) &global_seed, 0, s); - g = global_seed; - nm_assert (g); - } - - return g ^ seed; -} - -/*****************************************************************************/ - static void _str_append_escape (GString *s, char ch) { @@ -1118,134 +1089,3 @@ nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll) return 0; } - -/*****************************************************************************/ - -/** - * nm_utils_random_bytes: - * @p: the buffer to fill - * @n: the number of bytes to write to @p. - * - * Uses getrandom() or reads /dev/urandom to fill the buffer - * with random data. If all fails, as last fallback it uses - * GRand to fill the buffer with pseudo random numbers. - * The function always succeeds in writing some random numbers - * to the buffer. The return value of FALSE indicates that the - * obtained bytes are probably not of good randomness. - * - * Returns: whether the written bytes are good. If you - * don't require good randomness, you can ignore the return - * value. - * - * Note that if calling getrandom() fails because there is not enough - * entroy (at early boot), the function will read /dev/urandom. - * Which of course, still has low entropy, and cause kernel to log - * a warning. - */ -gboolean -nm_utils_random_bytes (void *p, size_t n) -{ - int fd; - int r; - gboolean has_high_quality = TRUE; - gboolean urandom_success; - guint8 *buf = p; - gboolean avoid_urandom = FALSE; - - g_return_val_if_fail (p, FALSE); - g_return_val_if_fail (n > 0, FALSE); - -#if HAVE_GETRANDOM - { - static gboolean have_syscall = TRUE; - - if (have_syscall) { - r = getrandom (buf, n, GRND_NONBLOCK); - if (r > 0) { - if ((size_t) r == n) - return TRUE; - - /* no or partial read. There is not enough entropy. - * Fill the rest reading from urandom, and remember that - * some bits are not hight quality. */ - nm_assert (r < n); - buf += r; - n -= r; - has_high_quality = FALSE; - - /* At this point, we don't want to read /dev/urandom, because - * the entropy pool is low (early boot?), and asking for more - * entropy causes kernel messages to be logged. - * - * We use our fallback via GRand. Note that g_rand_new() also - * tries to seed itself with data from /dev/urandom, but since - * we reuse the instance, it shouldn't matter. */ - avoid_urandom = TRUE; - } else { - if (errno == ENOSYS) { - /* no support for getrandom(). We don't know whether - * we urandom will give us good quality. Assume yes. */ - have_syscall = FALSE; - } else { - /* unknown error. We'll read urandom below, but we don't have - * high-quality randomness. */ - has_high_quality = FALSE; - } - } - } - } -#endif - - urandom_success = FALSE; - if (!avoid_urandom) { -fd_open: - fd = open ("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY); - if (fd < 0) { - r = errno; - if (r == EINTR) - goto fd_open; - } else { - r = nm_utils_fd_read_loop_exact (fd, buf, n, TRUE); - close (fd); - if (r >= 0) - urandom_success = TRUE; - } - } - - if (!urandom_success) { - static _nm_thread_local GRand *rand = NULL; - gsize i; - int j; - - /* we failed to fill the bytes reading from urandom. - * Fill the bits using GRand pseudo random numbers. - * - * We don't have good quality. - */ - has_high_quality = FALSE; - - if (G_UNLIKELY (!rand)) - rand = g_rand_new (); - - nm_assert (n > 0); - i = 0; - for (;;) { - const union { - guint32 v32; - guint8 v8[4]; - } v = { - .v32 = g_rand_int (rand), - }; - - for (j = 0; j < 4; ) { - buf[i++] = v.v8[j++]; - if (i >= n) - goto done; - } - } -done: - ; - } - - return has_high_quality; -} diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h index 0f9df73fb..96aab5341 100644 --- a/shared/nm-utils/nm-shared-utils.h +++ b/shared/nm-utils/nm-shared-utils.h @@ -378,30 +378,6 @@ GParamSpec *nm_g_object_class_find_property_from_gtype (GType gtype, /*****************************************************************************/ -guint NM_HASH_INIT (guint seed); - -static inline guint -NM_HASH_COMBINE (guint h, guint val) -{ - /* see g_str_hash() for reasons */ - return (h << 5) + h + val; -} - -static inline guint -NM_HASH_COMBINE_UINT64 (guint h, guint64 val) -{ - return NM_HASH_COMBINE (h, (((guint) val) & 0xFFFFFFFFu) + ((guint) (val >> 32))); -} - -static inline guint -NM_HASH_POINTER (gconstpointer ptr) -{ - /* same as g_direct_hash(), but inline. */ - return GPOINTER_TO_UINT (ptr); -} - -/*****************************************************************************/ - typedef enum { NM_UTILS_STR_UTF8_SAFE_FLAG_NONE = 0, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL = 0x0001, @@ -430,8 +406,4 @@ int nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll) /*****************************************************************************/ -gboolean nm_utils_random_bytes (void *p, size_t n); - -/*****************************************************************************/ - #endif /* __NM_SHARED_UTILS_H__ */ diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c index 59845c8c7..e8ff9ff29 100644 --- a/src/dhcp/nm-dhcp-client.c +++ b/src/dhcp/nm-dhcp-client.c @@ -32,6 +32,7 @@ #include #include "nm-utils/nm-dedup-multi.h" +#include "nm-utils/nm-random-utils.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 48a4aa25d..94f71ce3e 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -36,6 +36,7 @@ #include #include +#include "nm-utils/nm-random-utils.h" #include "nm-utils.h" #include "nm-core-internal.h" #include "nm-setting-connection.h" diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 9d35e2f62..283bd686f 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -25,6 +25,8 @@ #include #include +#include "nm-utils/nm-hash-utils.h" + #include "nm-connection.h" /*****************************************************************************/