diff --git a/Makefile.am b/Makefile.am index 8176019594..c2c7af44f0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -501,6 +501,7 @@ libnm_core_lib_h_priv = \ 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-secret-utils.h \ shared/nm-utils/nm-shared-utils.h \ shared/nm-utils/nm-random-utils.h \ shared/nm-utils/nm-udev-utils.h \ @@ -567,6 +568,7 @@ libnm_core_lib_c_real = \ 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-secret-utils.c \ shared/nm-utils/nm-shared-utils.c \ shared/nm-utils/nm-random-utils.c \ shared/nm-utils/nm-udev-utils.c \ @@ -4736,6 +4738,8 @@ EXTRA_DIST += \ shared/nm-utils/nm-jansson.h \ shared/nm-utils/nm-obj.h \ shared/nm-utils/nm-macros-internal.h \ + shared/nm-utils/nm-secret-utils.c \ + shared/nm-utils/nm-secret-utils.h \ shared/nm-utils/nm-shared-utils.c \ shared/nm-utils/nm-shared-utils.h \ shared/nm-utils/nm-test-utils.h \ diff --git a/libnm-core/nm-setting-vpn.c b/libnm-core/nm-setting-vpn.c index 3b61736128..fcbeec6854 100644 --- a/libnm-core/nm-setting-vpn.c +++ b/libnm-core/nm-setting-vpn.c @@ -25,6 +25,8 @@ #include #include +#include "nm-utils/nm-secret-utils.h" + #include "nm-setting-vpn.h" #include "nm-utils.h" #include "nm-utils-private.h" diff --git a/libnm/nm-vpn-service-plugin.c b/libnm/nm-vpn-service-plugin.c index 29c666c796..2213824edb 100644 --- a/libnm/nm-vpn-service-plugin.c +++ b/libnm/nm-vpn-service-plugin.c @@ -27,6 +27,7 @@ #include #include +#include "nm-utils/nm-secret-utils.h" #include "nm-enum-types.h" #include "nm-utils.h" #include "nm-connection.h" diff --git a/shared/meson.build b/shared/meson.build index 8faec8765b..c14b9d723b 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -51,6 +51,7 @@ shared_files_libnm_core = files(''' nm-utils/nm-enum-utils.c nm-utils/nm-hash-utils.c nm-utils/nm-random-utils.c + nm-utils/nm-secret-utils.c nm-utils/nm-shared-utils.c nm-utils/nm-udev-utils.c '''.split()) diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h index c8734a4b4a..6937994c38 100644 --- a/shared/nm-utils/nm-macros-internal.h +++ b/shared/nm-utils/nm-macros-internal.h @@ -207,7 +207,6 @@ NM_AUTO_DEFINE_FCN0 (GKeyFile *, gs_local_keyfile_unref, g_key_file_unref) /*****************************************************************************/ static inline int nm_close (int fd); -static inline void nm_free_secret (char *secret); /** * nm_auto_free: @@ -238,16 +237,6 @@ NM_AUTO_DEFINE_FCN (GList *, _nm_auto_free_list, g_list_free) NM_AUTO_DEFINE_FCN0 (GChecksum *, _nm_auto_checksum_free, g_checksum_free) #define nm_auto_free_checksum nm_auto(_nm_auto_checksum_free) -NM_AUTO_DEFINE_FCN (char *, _nm_auto_free_secret, nm_free_secret) -/** - * nm_auto_free_secret: - * - * Call g_free() on a variable location when it goes out of scope. - * Also, previously, calls memset(loc, 0, strlen(loc)) to clear out - * the secret. - */ -#define nm_auto_free_secret nm_auto(_nm_auto_free_secret) - #define nm_auto_unset_gvalue nm_auto(g_value_unset) NM_AUTO_DEFINE_FCN_VOID0 (void *, _nm_auto_unref_gtypeclass, g_type_class_unref) @@ -830,15 +819,6 @@ fcn (void) \ /*****************************************************************************/ -static inline void -nm_free_secret (char *secret) -{ - if (secret) { - memset (secret, 0, strlen (secret)); - g_free (secret); - } -} - static inline GString * nm_gstring_prepare (GString **l) { diff --git a/shared/nm-utils/nm-secret-utils.c b/shared/nm-utils/nm-secret-utils.c new file mode 100644 index 0000000000..65f99c65d9 --- /dev/null +++ b/shared/nm-utils/nm-secret-utils.c @@ -0,0 +1,134 @@ +/* -*- 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 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-secret-utils.h" + +/*****************************************************************************/ + +void +nm_explicit_bzero (void *s, gsize n) +{ + /* gracefully handle n == 0. This is important, callers rely on it. */ + if (n > 0) { + nm_assert (s); +#if defined (HAVE_DECL_EXPLICIT_BZERO) && HAVE_DECL_EXPLICIT_BZERO + explicit_bzero (s, n); +#else + /* don't bother with a workaround. Use a reasonable glibc. */ + memset (s, 0, n); +#endif + } +} + +/*****************************************************************************/ + +char * +nm_secret_strchomp (char *secret) +{ + gsize len; + + g_return_val_if_fail (secret, NULL); + + /* it's actually identical to g_strchomp(). However, + * the glib function does not document, that it clears the + * memory. For @secret, we don't only want to truncate trailing + * spaces, we want to overwrite them with NUL. */ + + len = strlen (secret); + while (len--) { + if (g_ascii_isspace ((guchar) secret[len])) + secret[len] = '\0'; + else + break; + } + + return secret; +} + +/*****************************************************************************/ + +GBytes * +nm_secret_copy_to_gbytes (gconstpointer mem, gsize mem_len) +{ + NMSecretBuf *b; + + if (mem_len == 0) + return g_bytes_new_static ("", 0); + + nm_assert (mem); + + /* NUL terminate the buffer. + * + * The entire buffer is already malloc'ed and likely has some room for padding. + * Thus, in many situations, this additional byte will cause no overhead in + * practice. + * + * Even if it causes an overhead, do it just for safety. Yes, the returned + * bytes is not a NUL terminated string and no user must rely on this. Do + * not treat binary data as NUL terminated strings, unless you know what + * you are doing. Anyway, defensive FTW. + */ + + b = nm_secret_buf_new (mem_len + 1); + memcpy (b->bin, mem, mem_len); + b->bin[mem_len] = 0; + return nm_secret_buf_to_gbytes_take (b, mem_len); +} + +/*****************************************************************************/ + +NMSecretBuf * +nm_secret_buf_new (gsize len) +{ + NMSecretBuf *secret; + + nm_assert (len > 0); + + secret = g_malloc (sizeof (NMSecretBuf) + len); + *((gsize *) &(secret->len)) = len; + return secret; +} + +static void +_secret_buf_free (gpointer user_data) +{ + NMSecretBuf *secret = user_data; + + nm_assert (secret); + nm_assert (secret->len > 0); + + nm_explicit_bzero (secret->bin, secret->len); + g_free (user_data); +} + +GBytes * +nm_secret_buf_to_gbytes_take (NMSecretBuf *secret, gssize actual_len) +{ + nm_assert (secret); + nm_assert (secret->len > 0); + nm_assert (actual_len == -1 || (actual_len >= 0 && actual_len <= secret->len)); + return g_bytes_new_with_free_func (secret->bin, + actual_len >= 0 ? (gsize) actual_len : secret->len, + _secret_buf_free, + secret); +} diff --git a/shared/nm-utils/nm-secret-utils.h b/shared/nm-utils/nm-secret-utils.h new file mode 100644 index 0000000000..21a3c1ba11 --- /dev/null +++ b/shared/nm-utils/nm-secret-utils.h @@ -0,0 +1,151 @@ +/* -*- 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 2018 Red Hat, Inc. + */ + +#ifndef __NM_SECRET_UTILS_H__ +#define __NM_SECRET_UTILS_H__ + +#include "nm-macros-internal.h" + +/*****************************************************************************/ + +void nm_explicit_bzero (void *s, gsize n); + +/*****************************************************************************/ + +char *nm_secret_strchomp (char *secret); + +/*****************************************************************************/ + +static inline void +nm_free_secret (char *secret) +{ + if (secret) { + nm_explicit_bzero (secret, strlen (secret)); + g_free (secret); + } +} + +NM_AUTO_DEFINE_FCN (char *, _nm_auto_free_secret, nm_free_secret) +/** + * nm_auto_free_secret: + * + * Call g_free() on a variable location when it goes out of scope. + * Also, previously, calls memset(loc, 0, strlen(loc)) to clear out + * the secret. + */ +#define nm_auto_free_secret nm_auto(_nm_auto_free_secret) + +/*****************************************************************************/ + +GBytes *nm_secret_copy_to_gbytes (gconstpointer mem, gsize mem_len); + +/*****************************************************************************/ + +/* NMSecretPtr is a pair of malloc'ed data pointer and the length of the + * data. The purpose is to use it in combination with nm_auto_clear_secret_ptr + * which ensures that the data pointer (with all len bytes) is cleared upon + * cleanup. */ +typedef struct { + gsize len; + + /* the data pointer. This pointer must be allocated with malloc (at least + * when used with nm_secret_ptr_clear()). */ + union { + char *str; + void *ptr; + guint8 *bin; + }; +} NMSecretPtr; + +static inline void +nm_secret_ptr_clear (NMSecretPtr *secret) +{ + if (secret) { + if (secret->len > 0) { + if (secret->ptr) + nm_explicit_bzero (secret->ptr, secret->len); + secret->len = 0; + } + nm_clear_g_free (&secret->ptr); + } +} + +#define nm_auto_clear_secret_ptr nm_auto(nm_secret_ptr_clear) + +#define NM_SECRET_PTR_STATIC(_len) \ + ((const NMSecretPtr) { \ + .len = _len, \ + .ptr = ((guint8 [_len]) { }), \ + }) + +static inline void +nm_secret_ptr_clear_static (const NMSecretPtr *secret) +{ + if (secret) { + if (secret->len > 0) { + nm_assert (secret->ptr); + nm_explicit_bzero (secret->ptr, secret->len); + } + } +} + +#define nm_auto_clear_static_secret_ptr nm_auto(nm_secret_ptr_clear_static) + +static inline void +nm_secret_ptr_move (NMSecretPtr *dst, NMSecretPtr *src) +{ + if (dst && dst != src) { + *dst = *src; + src->len = 0; + src->ptr = NULL; + } +} + +/*****************************************************************************/ + +typedef struct { + const gsize len; + union { + char str[0]; + guint8 bin[0]; + }; +} NMSecretBuf; + +static inline void +_nm_auto_free_secret_buf (NMSecretBuf **ptr) +{ + NMSecretBuf *b = *ptr; + + if (b) { + nm_assert (b->len > 0); + nm_explicit_bzero (b->bin, b->len); + g_free (b); + } +} +#define nm_auto_free_secret_buf nm_auto(_nm_auto_free_secret_buf) + +NMSecretBuf *nm_secret_buf_new (gsize len); + +GBytes *nm_secret_buf_to_gbytes_take (NMSecretBuf *secret, gssize actual_len); + +/*****************************************************************************/ + +#endif /* __NM_SECRET_UTILS_H__ */