mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-11 16:30:39 +01:00
crypto: merge branch 'th/crypto-secrets'
https://github.com/NetworkManager/NetworkManager/pull/191
This commit is contained in:
commit
4b5680898f
42 changed files with 4323 additions and 3960 deletions
13
.travis.yml
13
.travis.yml
|
|
@ -79,6 +79,11 @@ before_install:
|
|||
script:
|
||||
- |
|
||||
if test "$BUILD_TYPE" == 'meson'; then
|
||||
if [ "$CC" == gcc ]; then
|
||||
CRYPTO=nss
|
||||
else
|
||||
CRYPTO=gnutls
|
||||
fi &&
|
||||
meson build \
|
||||
\
|
||||
-D ld_gc=false \
|
||||
|
|
@ -91,6 +96,7 @@ script:
|
|||
-D vapi=false \
|
||||
-D introspection=false \
|
||||
-D qt=false \
|
||||
-D crypto=$CRYPTO \
|
||||
\
|
||||
-D docs=true \
|
||||
\
|
||||
|
|
@ -103,11 +109,16 @@ script:
|
|||
fi
|
||||
- |
|
||||
if test "$BUILD_TYPE" == 'autotools'; then
|
||||
if [ "$CC" == gcc ]; then
|
||||
CRYPTO=gnutls
|
||||
else
|
||||
CRYPTO=nss
|
||||
fi &&
|
||||
git clean -fdx &&
|
||||
NOCONFIGURE=1 ./autogen.sh &&
|
||||
mkdir ./build &&
|
||||
pushd ./build &&
|
||||
../configure --prefix="$PWD/INST" --with-systemd-logind=no --enable-more-warnings=no --enable-ifcfg-rh --enable-config-plugin-ibft --enable-ifupdown --enable-tests &&
|
||||
../configure --prefix="$PWD/INST" --with-systemd-logind=no --enable-more-warnings=no --enable-ifcfg-rh --enable-config-plugin-ibft --enable-ifupdown --enable-tests --with-crypto=$CRYPTO &&
|
||||
make -j4 &&
|
||||
if [ "$CC" == gcc ]; then
|
||||
sudo locale-gen pl_PL.UTF-8 &&
|
||||
|
|
|
|||
82
Makefile.am
82
Makefile.am
|
|
@ -501,12 +501,15 @@ 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-io-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 \
|
||||
shared/nm-ethtool-utils.h \
|
||||
shared/nm-meta-setting.h \
|
||||
libnm-core/crypto.h \
|
||||
libnm-core/nm-crypto.h \
|
||||
libnm-core/nm-crypto-impl.h \
|
||||
libnm-core/nm-connection-private.h \
|
||||
libnm-core/nm-core-internal.h \
|
||||
libnm-core/nm-core-types-internal.h \
|
||||
|
|
@ -567,12 +570,14 @@ 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-io-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 \
|
||||
shared/nm-ethtool-utils.c \
|
||||
shared/nm-meta-setting.c \
|
||||
libnm-core/crypto.c \
|
||||
libnm-core/nm-crypto.c \
|
||||
libnm-core/nm-connection.c \
|
||||
libnm-core/nm-dbus-utils.c \
|
||||
libnm-core/nm-errors.c \
|
||||
|
|
@ -615,14 +620,6 @@ dflt_cppflags_libnm_core = \
|
|||
$(SANITIZER_LIB_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
if WITH_GNUTLS
|
||||
dflt_cppflags_libnm_core += $(GNUTLS_CFLAGS)
|
||||
endif
|
||||
|
||||
if WITH_NSS
|
||||
dflt_cppflags_libnm_core += $(NSS_CFLAGS)
|
||||
endif
|
||||
|
||||
noinst_LTLIBRARIES += libnm-core/libnm-core.la
|
||||
|
||||
GLIB_GENERATED += \
|
||||
|
|
@ -668,7 +665,6 @@ nodist_libnm_core_libnm_core_la_SOURCES = \
|
|||
$(libnm_core_lib_c_mkenums)
|
||||
|
||||
libnm_core_libnm_core_la_LIBADD = \
|
||||
shared/libcsiphash.la \
|
||||
$(GLIB_LIBS) \
|
||||
$(UUID_LIBS) \
|
||||
$(LIBUDEV_LIBS) \
|
||||
|
|
@ -678,19 +674,9 @@ libnm_core_libnm_core_la_LDFLAGS = \
|
|||
$(CODE_COVERAGE_LDFLAGS) \
|
||||
$(SANITIZER_LIB_LDFLAGS)
|
||||
|
||||
if WITH_GNUTLS
|
||||
libnm_core_lib_c_real += libnm-core/crypto_gnutls.c
|
||||
libnm_core_libnm_core_la_LIBADD += $(GNUTLS_LIBS)
|
||||
endif
|
||||
|
||||
if WITH_NSS
|
||||
libnm_core_lib_c_real += libnm-core/crypto_nss.c
|
||||
libnm_core_libnm_core_la_LIBADD += $(NSS_LIBS)
|
||||
endif
|
||||
|
||||
EXTRA_DIST += \
|
||||
libnm-core/crypto_gnutls.c \
|
||||
libnm-core/crypto_nss.c \
|
||||
libnm-core/nm-crypto-gnutls.c \
|
||||
libnm-core/nm-crypto-nss.c \
|
||||
libnm-core/nm-core-enum-types.c.template \
|
||||
libnm-core/nm-core-enum-types.h.template \
|
||||
libnm-core/meson.build
|
||||
|
|
@ -713,6 +699,46 @@ dist_dependencies += \
|
|||
|
||||
###############################################################################
|
||||
|
||||
if HAVE_CRYPTO_GNUTLS
|
||||
if WITH_GNUTLS
|
||||
libnm_crypto_lib = libnm-core/libnm-crypto-gnutls.la
|
||||
else
|
||||
check_ltlibraries += libnm-core/libnm-crypto-gnutls.la
|
||||
endif
|
||||
|
||||
libnm_core_libnm_crypto_gnutls_la_SOURCES = libnm-core/nm-crypto-gnutls.c
|
||||
libnm_core_libnm_crypto_gnutls_la_CPPFLAGS = \
|
||||
$(libnm_core_libnm_core_la_CPPFLAGS) \
|
||||
$(GNUTLS_CFLAGS)
|
||||
libnm_core_libnm_crypto_gnutls_la_LDFLAGS = \
|
||||
$(libnm_core_libnm_core_la_LDFLAGS)
|
||||
libnm_core_libnm_crypto_gnutls_la_LIBADD = \
|
||||
$(libnm_core_libnm_core_la_LIBADD) \
|
||||
$(GNUTLS_LIBS)
|
||||
endif
|
||||
|
||||
if HAVE_CRYPTO_NSS
|
||||
if WITH_NSS
|
||||
libnm_crypto_lib = libnm-core/libnm-crypto-nss.la
|
||||
else
|
||||
check_ltlibraries += libnm-core/libnm-crypto-nss.la
|
||||
endif
|
||||
|
||||
libnm_core_libnm_crypto_nss_la_SOURCES = libnm-core/nm-crypto-nss.c
|
||||
libnm_core_libnm_crypto_nss_la_CPPFLAGS = \
|
||||
$(libnm_core_libnm_core_la_CPPFLAGS) \
|
||||
$(NSS_CFLAGS)
|
||||
libnm_core_libnm_crypto_nss_la_LDFLAGS = \
|
||||
$(libnm_core_libnm_core_la_LDFLAGS)
|
||||
libnm_core_libnm_crypto_nss_la_LIBADD = \
|
||||
$(libnm_core_libnm_core_la_LIBADD) \
|
||||
$(NSS_LIBS)
|
||||
endif
|
||||
|
||||
noinst_LTLIBRARIES += $(libnm_crypto_lib)
|
||||
|
||||
###############################################################################
|
||||
|
||||
check_programs += \
|
||||
libnm-core/tests/test-compare \
|
||||
libnm-core/tests/test-crypto \
|
||||
|
|
@ -759,6 +785,8 @@ nodist_libnm_core_tests_test_general_SOURCES = \
|
|||
|
||||
libnm_core_tests_ldadd = \
|
||||
libnm-core/libnm-core.la \
|
||||
shared/libcsiphash.la \
|
||||
$(libnm_crypto_lib) \
|
||||
$(GLIB_LIBS)
|
||||
|
||||
libnm_core_tests_ldflags = \
|
||||
|
|
@ -963,6 +991,8 @@ libnm_libnm_utils_la_SOURCES = \
|
|||
|
||||
libnm_libnm_utils_la_LIBADD = \
|
||||
libnm-core/libnm-core.la \
|
||||
shared/libcsiphash.la \
|
||||
$(libnm_crypto_lib) \
|
||||
introspection/libnmdbus.la \
|
||||
$(GLIB_LIBS)
|
||||
|
||||
|
|
@ -1588,6 +1618,8 @@ endif
|
|||
|
||||
src_libNetworkManagerBase_la_LIBADD = \
|
||||
libnm-core/libnm-core.la \
|
||||
shared/libcsiphash.la \
|
||||
$(libnm_crypto_lib) \
|
||||
$(GLIB_LIBS) \
|
||||
$(SYSTEMD_JOURNAL_LIBS) \
|
||||
$(LIBUDEV_LIBS) \
|
||||
|
|
@ -4733,9 +4765,13 @@ EXTRA_DIST += \
|
|||
shared/nm-utils/nm-compat.c \
|
||||
shared/nm-utils/nm-compat.h \
|
||||
shared/nm-utils/nm-glib.h \
|
||||
shared/nm-utils/nm-io-utils.c \
|
||||
shared/nm-utils/nm-io-utils.h \
|
||||
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 \
|
||||
|
|
|
|||
|
|
@ -226,18 +226,6 @@
|
|||
/* Define if you have iwd support */
|
||||
#mesondefine WITH_IWD
|
||||
|
||||
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
|
||||
significant byte first (like Motorola and SPARC, unlike Intel). */
|
||||
#if defined AC_APPLE_UNIVERSAL_BUILD
|
||||
# if defined __BIG_ENDIAN__
|
||||
# define WORDS_BIGENDIAN 1
|
||||
# endif
|
||||
#else
|
||||
# ifndef WORDS_BIGENDIAN
|
||||
/* # undef WORDS_BIGENDIAN */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Define to 1 if on MINIX. */
|
||||
#mesondefine _MINIX
|
||||
|
||||
|
|
|
|||
41
configure.ac
41
configure.ac
|
|
@ -109,11 +109,6 @@ GETTEXT_PACKAGE=NetworkManager
|
|||
AC_SUBST(GETTEXT_PACKAGE)
|
||||
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package])
|
||||
|
||||
dnl
|
||||
dnl Make sha1.c happy on big endian systems
|
||||
dnl
|
||||
AC_C_BIGENDIAN
|
||||
|
||||
# Add runstatedir if not specified manually in autoconf < 2.70
|
||||
AS_IF([test -z "$runstatedir"], runstatedir="$localstatedir/run")
|
||||
AC_SUBST(runstatedir)
|
||||
|
|
@ -668,21 +663,41 @@ else
|
|||
fi
|
||||
AC_SUBST(NM_MODIFY_SYSTEM_POLICY)
|
||||
|
||||
PKG_CHECK_MODULES(GNUTLS, [gnutls >= 2.12], [have_crypto_gnutls=yes], [have_crypto_gnutls=no])
|
||||
PKG_CHECK_MODULES(NSS, [nss], [have_crypto_nss=yes], [have_crypto_nss=yes])
|
||||
if test "${have_crypto_nss}" = "yes"; then
|
||||
# Work around a pkg-config bug (fdo #29801) where exists != usable
|
||||
FOO=`$PKG_CONFIG --cflags --libs nss`
|
||||
if test x"$?" != "x0"; then
|
||||
have_crypto_nss=no
|
||||
fi
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_CRYPTO_GNUTLS, test "${have_crypto_gnutls}" = 'yes')
|
||||
AM_CONDITIONAL(HAVE_CRYPTO_NSS, test "${have_crypto_nss}" = 'yes')
|
||||
if test "${have_crypto_gnutls}" = 'yes'; then
|
||||
AC_DEFINE(HAVE_CRYPTO_GNUTLS, 1, [Define if you have gnutls support])
|
||||
else
|
||||
AC_DEFINE(HAVE_CRYPTO_GNUTLS, 0, [Define if you have gnutls support])
|
||||
fi
|
||||
if test "${have_crypto_nss}" = 'yes'; then
|
||||
AC_DEFINE(HAVE_CRYPTO_NSS, 1, [Define if you have nss support])
|
||||
else
|
||||
AC_DEFINE(HAVE_CRYPTO_NSS, 0, [Define if you have nss support])
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(crypto,
|
||||
AS_HELP_STRING([--with-crypto=nss|gnutls],
|
||||
[Cryptography library to use for certificate and key operations]),
|
||||
with_crypto=$withval,
|
||||
with_crypto=nss)
|
||||
if test "$with_crypto" = 'nss'; then
|
||||
PKG_CHECK_MODULES(NSS, [nss])
|
||||
|
||||
# Work around a pkg-config bug (fdo #29801) where exists != usable
|
||||
FOO=`$PKG_CONFIG --cflags --libs nss`
|
||||
if test x"$?" != "x0"; then
|
||||
AC_MSG_ERROR([No usable NSS found])
|
||||
if test "${have_crypto_nss}" != "yes"; then
|
||||
AC_MSG_ERROR([No usable NSS found for --with-crypto=nss])
|
||||
fi
|
||||
elif test "$with_crypto" = 'gnutls'; then
|
||||
PKG_CHECK_MODULES(GNUTLS, [gnutls >= 2.12])
|
||||
if test "${have_crypto_gnutls}" != "yes"; then
|
||||
AC_MSG_ERROR([No usable gnutls found for --with-crypto=gnutls])
|
||||
fi
|
||||
else
|
||||
AC_MSG_ERROR([Please choose either 'nss' or 'gnutls' for certificate and crypto operations])
|
||||
fi
|
||||
|
|
@ -1385,7 +1400,7 @@ echo " code coverage: $enable_code_coverage"
|
|||
echo " LTO: $enable_lto"
|
||||
echo " linker garbage collection: $enable_ld_gc"
|
||||
echo " JSON validation for libnm: $enable_json_validation"
|
||||
echo " crypto: $with_crypto"
|
||||
echo " crypto: $with_crypto (have-gnutls: $have_crypto_gnutls, have-nss: $have_crypto_nss)"
|
||||
echo " sanitizers: $sanitizers"
|
||||
echo " Mozilla Public Suffix List: $with_libpsl"
|
||||
echo
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ CFILE_GLOB=$(top_srcdir)/libnm-core/*.c $(top_srcdir)/libnm/*.c
|
|||
# Header files to ignore when scanning.
|
||||
IGNORE_HFILES= \
|
||||
common.h \
|
||||
crypto.h \
|
||||
nm-crypto.h \
|
||||
nm-crypto-impl.h \
|
||||
nm-dbus-helpers.h \
|
||||
nm-core-internal.h \
|
||||
nm-core-types-internal.h \
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ doc_module = libnm_name
|
|||
|
||||
private_headers = [
|
||||
'common.h',
|
||||
'crypto.h',
|
||||
'nm-crypto.h',
|
||||
'nm-crypto-impl.h',
|
||||
'nm-dbus-helpers.h',
|
||||
'nm-core-internal.h',
|
||||
'nm-core-types-internal.h',
|
||||
|
|
|
|||
|
|
@ -1,826 +0,0 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
|
||||
/*
|
||||
* Dan Williams <dcbw@redhat.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Copyright 2007 - 2011 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#include "nm-default.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "crypto.h"
|
||||
#include "nm-errors.h"
|
||||
|
||||
#define PEM_RSA_KEY_BEGIN "-----BEGIN RSA PRIVATE KEY-----"
|
||||
#define PEM_RSA_KEY_END "-----END RSA PRIVATE KEY-----"
|
||||
|
||||
#define PEM_DSA_KEY_BEGIN "-----BEGIN DSA PRIVATE KEY-----"
|
||||
#define PEM_DSA_KEY_END "-----END DSA PRIVATE KEY-----"
|
||||
|
||||
#define PEM_CERT_BEGIN "-----BEGIN CERTIFICATE-----"
|
||||
#define PEM_CERT_END "-----END CERTIFICATE-----"
|
||||
|
||||
#define PEM_PKCS8_ENC_KEY_BEGIN "-----BEGIN ENCRYPTED PRIVATE KEY-----"
|
||||
#define PEM_PKCS8_ENC_KEY_END "-----END ENCRYPTED PRIVATE KEY-----"
|
||||
|
||||
#define PEM_PKCS8_DEC_KEY_BEGIN "-----BEGIN PRIVATE KEY-----"
|
||||
#define PEM_PKCS8_DEC_KEY_END "-----END PRIVATE KEY-----"
|
||||
|
||||
static gboolean
|
||||
find_tag (const char *tag,
|
||||
const guint8 *data,
|
||||
gsize data_len,
|
||||
gsize start_at,
|
||||
gsize *out_pos)
|
||||
{
|
||||
gsize i, taglen;
|
||||
gsize len = data_len - start_at;
|
||||
|
||||
g_return_val_if_fail (out_pos != NULL, FALSE);
|
||||
|
||||
taglen = strlen (tag);
|
||||
if (len >= taglen) {
|
||||
for (i = 0; i < len - taglen + 1; i++) {
|
||||
if (memcmp (data + start_at + i, tag, taglen) == 0) {
|
||||
*out_pos = start_at + i;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#define DEK_INFO_TAG "DEK-Info: "
|
||||
#define PROC_TYPE_TAG "Proc-Type: "
|
||||
|
||||
static GByteArray *
|
||||
parse_old_openssl_key_file (const guint8 *data,
|
||||
gsize data_len,
|
||||
NMCryptoKeyType *out_key_type,
|
||||
char **out_cipher,
|
||||
char **out_iv,
|
||||
GError **error)
|
||||
{
|
||||
GByteArray *bindata = NULL;
|
||||
char **lines = NULL;
|
||||
char **ln = NULL;
|
||||
gsize start = 0, end = 0;
|
||||
GString *str = NULL;
|
||||
int enc_tags = 0;
|
||||
NMCryptoKeyType key_type;
|
||||
char *iv = NULL;
|
||||
char *cipher = NULL;
|
||||
unsigned char *tmp = NULL;
|
||||
gsize tmp_len = 0;
|
||||
const char *start_tag;
|
||||
const char *end_tag;
|
||||
guint8 save_end = 0;
|
||||
|
||||
*out_key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
|
||||
*out_iv = NULL;
|
||||
*out_cipher = NULL;
|
||||
|
||||
if (find_tag (PEM_RSA_KEY_BEGIN, data, data_len, 0, &start)) {
|
||||
key_type = NM_CRYPTO_KEY_TYPE_RSA;
|
||||
start_tag = PEM_RSA_KEY_BEGIN;
|
||||
end_tag = PEM_RSA_KEY_END;
|
||||
} else if (find_tag (PEM_DSA_KEY_BEGIN, data, data_len, 0, &start)) {
|
||||
key_type = NM_CRYPTO_KEY_TYPE_DSA;
|
||||
start_tag = PEM_DSA_KEY_BEGIN;
|
||||
end_tag = PEM_DSA_KEY_END;
|
||||
} else
|
||||
goto parse_error;
|
||||
|
||||
start += strlen (start_tag);
|
||||
if (!find_tag (end_tag, data, data_len, start, &end)) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("PEM key file had no end tag '%s'."),
|
||||
end_tag);
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
save_end = data[end];
|
||||
((guint8 *)data)[end] = '\0';
|
||||
lines = g_strsplit ((const char *) (data + start), "\n", 0);
|
||||
((guint8 *)data)[end] = save_end;
|
||||
|
||||
if (!lines || g_strv_length (lines) <= 1) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Doesn't look like a PEM private key file."));
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
str = g_string_new_len (NULL, end - start);
|
||||
for (ln = lines; *ln; ln++) {
|
||||
char *p = *ln;
|
||||
|
||||
/* Chug leading spaces */
|
||||
p = g_strstrip (p);
|
||||
if (!*p)
|
||||
continue;
|
||||
|
||||
if (!strncmp (p, PROC_TYPE_TAG, strlen (PROC_TYPE_TAG))) {
|
||||
if (enc_tags++ != 0 || str->len != 0) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Malformed PEM file: Proc-Type was not first tag."));
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
p += strlen (PROC_TYPE_TAG);
|
||||
if (strcmp (p, "4,ENCRYPTED")) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Malformed PEM file: unknown Proc-Type tag '%s'."),
|
||||
p);
|
||||
goto parse_error;
|
||||
}
|
||||
} else if (!strncmp (p, DEK_INFO_TAG, strlen (DEK_INFO_TAG))) {
|
||||
static const char *const known_ciphers[] = { CIPHER_DES_EDE3_CBC,
|
||||
CIPHER_DES_CBC,
|
||||
CIPHER_AES_128_CBC,
|
||||
CIPHER_AES_192_CBC,
|
||||
CIPHER_AES_256_CBC };
|
||||
char *comma;
|
||||
guint i;
|
||||
|
||||
if (enc_tags++ != 1 || str->len != 0) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Malformed PEM file: DEK-Info was not the second tag."));
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
p += strlen (DEK_INFO_TAG);
|
||||
|
||||
/* Grab the IV first */
|
||||
comma = strchr (p, ',');
|
||||
if (!comma || (*(comma + 1) == '\0')) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Malformed PEM file: no IV found in DEK-Info tag."));
|
||||
goto parse_error;
|
||||
}
|
||||
*comma++ = '\0';
|
||||
if (!g_ascii_isxdigit (*comma)) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Malformed PEM file: invalid format of IV in DEK-Info tag."));
|
||||
goto parse_error;
|
||||
}
|
||||
iv = g_strdup (comma);
|
||||
|
||||
/* Get the private key cipher */
|
||||
for (i = 0; i < G_N_ELEMENTS (known_ciphers); i++) {
|
||||
if (!g_ascii_strcasecmp (p, known_ciphers[i])) {
|
||||
cipher = g_strdup (known_ciphers[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == G_N_ELEMENTS (known_ciphers)) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Malformed PEM file: unknown private key cipher '%s'."),
|
||||
p);
|
||||
goto parse_error;
|
||||
}
|
||||
} else {
|
||||
if (enc_tags == 1) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
"Malformed PEM file: both Proc-Type and DEK-Info tags are required.");
|
||||
goto parse_error;
|
||||
}
|
||||
g_string_append (str, p);
|
||||
}
|
||||
}
|
||||
|
||||
tmp = g_base64_decode (str->str, &tmp_len);
|
||||
if (tmp == NULL || !tmp_len) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Could not decode private key."));
|
||||
goto parse_error;
|
||||
}
|
||||
g_string_free (str, TRUE);
|
||||
|
||||
if (lines)
|
||||
g_strfreev (lines);
|
||||
|
||||
bindata = g_byte_array_sized_new (tmp_len);
|
||||
g_byte_array_append (bindata, tmp, tmp_len);
|
||||
g_free (tmp);
|
||||
|
||||
*out_key_type = key_type;
|
||||
*out_iv = iv;
|
||||
*out_cipher = cipher;
|
||||
return bindata;
|
||||
|
||||
parse_error:
|
||||
g_free (tmp);
|
||||
g_free (cipher);
|
||||
g_free (iv);
|
||||
if (str)
|
||||
g_string_free (str, TRUE);
|
||||
if (lines)
|
||||
g_strfreev (lines);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GByteArray *
|
||||
parse_pkcs8_key_file (const guint8 *data,
|
||||
gsize data_len,
|
||||
gboolean *out_encrypted,
|
||||
GError **error)
|
||||
{
|
||||
GByteArray *key = NULL;
|
||||
gsize start = 0, end = 0;
|
||||
unsigned char *der = NULL;
|
||||
guint8 save_end;
|
||||
gsize length = 0;
|
||||
const char *start_tag = NULL, *end_tag = NULL;
|
||||
gboolean encrypted = FALSE;
|
||||
|
||||
/* Try encrypted first, decrypted next */
|
||||
if (find_tag (PEM_PKCS8_ENC_KEY_BEGIN, data, data_len, 0, &start)) {
|
||||
start_tag = PEM_PKCS8_ENC_KEY_BEGIN;
|
||||
end_tag = PEM_PKCS8_ENC_KEY_END;
|
||||
encrypted = TRUE;
|
||||
} else if (find_tag (PEM_PKCS8_DEC_KEY_BEGIN, data, data_len, 0, &start)) {
|
||||
start_tag = PEM_PKCS8_DEC_KEY_BEGIN;
|
||||
end_tag = PEM_PKCS8_DEC_KEY_END;
|
||||
encrypted = FALSE;
|
||||
} else {
|
||||
g_set_error_literal (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Failed to find expected PKCS#8 start tag."));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
start += strlen (start_tag);
|
||||
if (!find_tag (end_tag, data, data_len, start, &end)) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Failed to find expected PKCS#8 end tag '%s'."),
|
||||
end_tag);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* g_base64_decode() wants a NULL-terminated string */
|
||||
save_end = data[end];
|
||||
((guint8 *)data)[end] = '\0';
|
||||
der = g_base64_decode ((const char *) (data + start), &length);
|
||||
((guint8 *)data)[end] = save_end;
|
||||
|
||||
if (der && length) {
|
||||
key = g_byte_array_sized_new (length);
|
||||
g_byte_array_append (key, der, length);
|
||||
g_assert (key->len == length);
|
||||
*out_encrypted = encrypted;
|
||||
} else {
|
||||
g_set_error_literal (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Failed to decode PKCS#8 private key."));
|
||||
}
|
||||
|
||||
g_free (der);
|
||||
return key;
|
||||
}
|
||||
|
||||
static GByteArray *
|
||||
file_to_g_byte_array (const char *filename, GError **error)
|
||||
{
|
||||
char *contents;
|
||||
GByteArray *array = NULL;
|
||||
gsize length = 0;
|
||||
|
||||
if (g_file_get_contents (filename, &contents, &length, error)) {
|
||||
array = g_byte_array_sized_new (length);
|
||||
g_byte_array_append (array, (guint8 *) contents, length);
|
||||
g_assert (array->len == length);
|
||||
g_free (contents);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a hex string into bytes.
|
||||
*/
|
||||
static char *
|
||||
convert_iv (const char *src,
|
||||
gsize *out_len,
|
||||
GError **error)
|
||||
{
|
||||
int num;
|
||||
int i;
|
||||
char conv[3];
|
||||
char *c;
|
||||
|
||||
g_return_val_if_fail (src != NULL, NULL);
|
||||
|
||||
num = strlen (src);
|
||||
if (num % 2) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("IV must be an even number of bytes in length."));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
num /= 2;
|
||||
c = g_malloc0 (num + 1);
|
||||
|
||||
conv[2] = '\0';
|
||||
for (i = 0; i < num; i++) {
|
||||
conv[0] = src[(i * 2)];
|
||||
conv[1] = src[(i * 2) + 1];
|
||||
if (!g_ascii_isxdigit (conv[0]) || !g_ascii_isxdigit (conv[1])) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("IV contains non-hexadecimal digits."));
|
||||
goto error;
|
||||
}
|
||||
|
||||
c[i] = strtol(conv, NULL, 16);
|
||||
}
|
||||
*out_len = num;
|
||||
return c;
|
||||
|
||||
error:
|
||||
g_free (c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
crypto_make_des_aes_key (const char *cipher,
|
||||
const char *salt,
|
||||
const gsize salt_len,
|
||||
const char *password,
|
||||
gsize *out_len,
|
||||
GError **error)
|
||||
{
|
||||
char *key;
|
||||
guint32 digest_len;
|
||||
|
||||
g_return_val_if_fail (cipher != NULL, NULL);
|
||||
g_return_val_if_fail (salt != NULL, NULL);
|
||||
g_return_val_if_fail (salt_len >= 8, NULL);
|
||||
g_return_val_if_fail (password != NULL, NULL);
|
||||
g_return_val_if_fail (out_len != NULL, NULL);
|
||||
|
||||
if (!strcmp (cipher, CIPHER_DES_EDE3_CBC))
|
||||
digest_len = 24;
|
||||
else if (!strcmp (cipher, CIPHER_DES_CBC))
|
||||
digest_len = 8;
|
||||
else if (!strcmp (cipher, CIPHER_AES_128_CBC))
|
||||
digest_len = 16;
|
||||
else if (!strcmp (cipher, CIPHER_AES_192_CBC))
|
||||
digest_len = 24;
|
||||
else if (!strcmp (cipher, CIPHER_AES_256_CBC))
|
||||
digest_len = 32;
|
||||
else {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_UNKNOWN_CIPHER,
|
||||
_("Private key cipher '%s' was unknown."),
|
||||
cipher);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (password[0] == '\0')
|
||||
return NULL;
|
||||
|
||||
key = g_malloc0 (digest_len + 1);
|
||||
|
||||
crypto_md5_hash (salt,
|
||||
8,
|
||||
password,
|
||||
strlen (password),
|
||||
key,
|
||||
digest_len);
|
||||
|
||||
*out_len = digest_len;
|
||||
return key;
|
||||
}
|
||||
|
||||
static GByteArray *
|
||||
decrypt_key (const char *cipher,
|
||||
int key_type,
|
||||
const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *iv,
|
||||
const char *password,
|
||||
GError **error)
|
||||
{
|
||||
char *bin_iv = NULL;
|
||||
gsize bin_iv_len = 0;
|
||||
char *key = NULL;
|
||||
gsize key_len = 0;
|
||||
char *output = NULL;
|
||||
gsize decrypted_len = 0;
|
||||
GByteArray *decrypted = NULL;
|
||||
|
||||
g_return_val_if_fail (password != NULL, NULL);
|
||||
|
||||
bin_iv = convert_iv (iv, &bin_iv_len, error);
|
||||
if (!bin_iv)
|
||||
return NULL;
|
||||
|
||||
/* Convert the password and IV into a DES or AES key */
|
||||
key = crypto_make_des_aes_key (cipher, bin_iv, bin_iv_len, password, &key_len, error);
|
||||
if (!key || !key_len)
|
||||
goto out;
|
||||
|
||||
output = crypto_decrypt (cipher, key_type,
|
||||
data, data_len,
|
||||
bin_iv, bin_iv_len,
|
||||
key, key_len,
|
||||
&decrypted_len,
|
||||
error);
|
||||
if (output && decrypted_len) {
|
||||
decrypted = g_byte_array_sized_new (decrypted_len);
|
||||
g_byte_array_append (decrypted, (guint8 *) output, decrypted_len);
|
||||
}
|
||||
|
||||
out:
|
||||
/* Don't leak stale key material */
|
||||
if (key)
|
||||
memset (key, 0, key_len);
|
||||
g_free (output);
|
||||
g_free (key);
|
||||
g_free (bin_iv);
|
||||
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
GByteArray *
|
||||
crypto_decrypt_openssl_private_key_data (const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *password,
|
||||
NMCryptoKeyType *out_key_type,
|
||||
GError **error)
|
||||
{
|
||||
GByteArray *decrypted = NULL;
|
||||
NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
|
||||
GByteArray *parsed;
|
||||
char *iv = NULL;
|
||||
char *cipher = NULL;
|
||||
|
||||
g_return_val_if_fail (data != NULL, NULL);
|
||||
if (out_key_type)
|
||||
g_return_val_if_fail (*out_key_type == NM_CRYPTO_KEY_TYPE_UNKNOWN, NULL);
|
||||
|
||||
if (!crypto_init (error))
|
||||
return NULL;
|
||||
|
||||
parsed = parse_old_openssl_key_file (data, data_len, &key_type, &cipher, &iv, NULL);
|
||||
/* return the key type even if decryption failed */
|
||||
if (out_key_type)
|
||||
*out_key_type = key_type;
|
||||
|
||||
if (!parsed) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Unable to determine private key type."));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (password) {
|
||||
if (!cipher || !iv) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_PASSWORD,
|
||||
_("Password provided, but key was not encrypted."));
|
||||
} else {
|
||||
decrypted = decrypt_key (cipher,
|
||||
key_type,
|
||||
parsed->data,
|
||||
parsed->len,
|
||||
iv,
|
||||
password,
|
||||
error);
|
||||
}
|
||||
} else if (!cipher && !iv)
|
||||
decrypted = g_byte_array_ref (parsed);
|
||||
|
||||
g_byte_array_unref (parsed);
|
||||
g_free (cipher);
|
||||
g_free (iv);
|
||||
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
GByteArray *
|
||||
crypto_decrypt_openssl_private_key (const char *file,
|
||||
const char *password,
|
||||
NMCryptoKeyType *out_key_type,
|
||||
GError **error)
|
||||
{
|
||||
GByteArray *contents;
|
||||
GByteArray *key = NULL;
|
||||
|
||||
if (!crypto_init (error))
|
||||
return NULL;
|
||||
|
||||
contents = file_to_g_byte_array (file, error);
|
||||
if (contents) {
|
||||
key = crypto_decrypt_openssl_private_key_data (contents->data, contents->len,
|
||||
password, out_key_type, error);
|
||||
g_byte_array_free (contents, TRUE);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
static GByteArray *
|
||||
extract_pem_cert_data (GByteArray *contents, GError **error)
|
||||
{
|
||||
GByteArray *cert = NULL;
|
||||
gsize start = 0, end = 0;
|
||||
unsigned char *der = NULL;
|
||||
guint8 save_end;
|
||||
gsize length = 0;
|
||||
|
||||
if (!find_tag (PEM_CERT_BEGIN, contents->data, contents->len, 0, &start)) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("PEM certificate had no start tag '%s'."),
|
||||
PEM_CERT_BEGIN);
|
||||
goto done;
|
||||
}
|
||||
|
||||
start += strlen (PEM_CERT_BEGIN);
|
||||
if (!find_tag (PEM_CERT_END, contents->data, contents->len, start, &end)) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("PEM certificate had no end tag '%s'."),
|
||||
PEM_CERT_END);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* g_base64_decode() wants a NULL-terminated string */
|
||||
save_end = contents->data[end];
|
||||
contents->data[end] = '\0';
|
||||
der = g_base64_decode ((const char *) (contents->data + start), &length);
|
||||
contents->data[end] = save_end;
|
||||
|
||||
if (der && length) {
|
||||
cert = g_byte_array_sized_new (length);
|
||||
g_byte_array_append (cert, der, length);
|
||||
g_assert (cert->len == length);
|
||||
} else {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Failed to decode certificate."));
|
||||
}
|
||||
|
||||
done:
|
||||
g_free (der);
|
||||
return cert;
|
||||
}
|
||||
|
||||
GByteArray *
|
||||
crypto_load_and_verify_certificate (const char *file,
|
||||
NMCryptoFileFormat *out_file_format,
|
||||
GError **error)
|
||||
{
|
||||
GByteArray *array, *contents;
|
||||
|
||||
g_return_val_if_fail (file != NULL, NULL);
|
||||
g_return_val_if_fail (out_file_format != NULL, NULL);
|
||||
g_return_val_if_fail (*out_file_format == NM_CRYPTO_FILE_FORMAT_UNKNOWN, NULL);
|
||||
|
||||
if (!crypto_init (error))
|
||||
return NULL;
|
||||
|
||||
contents = file_to_g_byte_array (file, error);
|
||||
if (!contents)
|
||||
return NULL;
|
||||
|
||||
/* Check for PKCS#12 */
|
||||
if (crypto_is_pkcs12_data (contents->data, contents->len, NULL)) {
|
||||
*out_file_format = NM_CRYPTO_FILE_FORMAT_PKCS12;
|
||||
return contents;
|
||||
}
|
||||
|
||||
/* Check for plain DER format */
|
||||
if (contents->len > 2 && contents->data[0] == 0x30 && contents->data[1] == 0x82) {
|
||||
*out_file_format = crypto_verify_cert (contents->data, contents->len, error);
|
||||
} else {
|
||||
array = extract_pem_cert_data (contents, error);
|
||||
if (!array) {
|
||||
g_byte_array_free (contents, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*out_file_format = crypto_verify_cert (array->data, array->len, error);
|
||||
g_byte_array_free (array, TRUE);
|
||||
}
|
||||
|
||||
if (*out_file_format != NM_CRYPTO_FILE_FORMAT_X509) {
|
||||
g_byte_array_free (contents, TRUE);
|
||||
contents = NULL;
|
||||
}
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
gboolean
|
||||
crypto_is_pkcs12_data (const guint8 *data,
|
||||
gsize data_len,
|
||||
GError **error)
|
||||
{
|
||||
GError *local = NULL;
|
||||
gboolean success;
|
||||
|
||||
if (!data_len)
|
||||
return FALSE;
|
||||
|
||||
g_return_val_if_fail (data != NULL, FALSE);
|
||||
|
||||
if (!crypto_init (error))
|
||||
return FALSE;
|
||||
|
||||
success = crypto_verify_pkcs12 (data, data_len, NULL, &local);
|
||||
if (success == FALSE) {
|
||||
/* If the error was just a decryption error, then it's pkcs#12 */
|
||||
if (local) {
|
||||
if (g_error_matches (local, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_DECRYPTION_FAILED)) {
|
||||
success = TRUE;
|
||||
g_error_free (local);
|
||||
} else
|
||||
g_propagate_error (error, local);
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
gboolean
|
||||
crypto_is_pkcs12_file (const char *file, GError **error)
|
||||
{
|
||||
GByteArray *contents;
|
||||
gboolean success = FALSE;
|
||||
|
||||
g_return_val_if_fail (file != NULL, FALSE);
|
||||
|
||||
if (!crypto_init (error))
|
||||
return FALSE;
|
||||
|
||||
contents = file_to_g_byte_array (file, error);
|
||||
if (contents) {
|
||||
success = crypto_is_pkcs12_data (contents->data, contents->len, error);
|
||||
g_byte_array_free (contents, TRUE);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/* Verifies that a private key can be read, and if a password is given, that
|
||||
* the private key can be decrypted with that password.
|
||||
*/
|
||||
NMCryptoFileFormat
|
||||
crypto_verify_private_key_data (const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *password,
|
||||
gboolean *out_is_encrypted,
|
||||
GError **error)
|
||||
{
|
||||
GByteArray *tmp;
|
||||
NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
NMCryptoKeyType ktype = NM_CRYPTO_KEY_TYPE_UNKNOWN;
|
||||
gboolean is_encrypted = FALSE;
|
||||
|
||||
g_return_val_if_fail (data != NULL, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
|
||||
g_return_val_if_fail (out_is_encrypted == NULL || *out_is_encrypted == FALSE, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
|
||||
|
||||
if (!crypto_init (error))
|
||||
return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
|
||||
/* Check for PKCS#12 first */
|
||||
if (crypto_is_pkcs12_data (data, data_len, NULL)) {
|
||||
is_encrypted = TRUE;
|
||||
if (!password || crypto_verify_pkcs12 (data, data_len, password, error))
|
||||
format = NM_CRYPTO_FILE_FORMAT_PKCS12;
|
||||
} else {
|
||||
/* Maybe it's PKCS#8 */
|
||||
tmp = parse_pkcs8_key_file (data, data_len, &is_encrypted, NULL);
|
||||
if (tmp) {
|
||||
if (!password || crypto_verify_pkcs8 (tmp->data, tmp->len, is_encrypted, password, error))
|
||||
format = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
|
||||
} else {
|
||||
char *cipher, *iv;
|
||||
|
||||
/* Or it's old-style OpenSSL */
|
||||
tmp = parse_old_openssl_key_file (data, data_len, &ktype,
|
||||
&cipher, &iv, NULL);
|
||||
if (tmp) {
|
||||
format = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
|
||||
is_encrypted = (cipher && iv);
|
||||
g_free (cipher);
|
||||
g_free (iv);
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp) {
|
||||
/* Don't leave key data around */
|
||||
memset (tmp->data, 0, tmp->len);
|
||||
g_byte_array_free (tmp, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (out_is_encrypted)
|
||||
*out_is_encrypted = is_encrypted;
|
||||
return format;
|
||||
}
|
||||
|
||||
NMCryptoFileFormat
|
||||
crypto_verify_private_key (const char *filename,
|
||||
const char *password,
|
||||
gboolean *out_is_encrypted,
|
||||
GError **error)
|
||||
{
|
||||
GByteArray *contents;
|
||||
NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
|
||||
g_return_val_if_fail (filename != NULL, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
|
||||
|
||||
if (!crypto_init (error))
|
||||
return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
|
||||
contents = file_to_g_byte_array (filename, error);
|
||||
if (contents) {
|
||||
format = crypto_verify_private_key_data (contents->data, contents->len, password, out_is_encrypted, error);
|
||||
g_byte_array_free (contents, TRUE);
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
void
|
||||
crypto_md5_hash (const char *salt,
|
||||
gssize salt_len,
|
||||
const char *password,
|
||||
gssize password_len,
|
||||
char *buffer,
|
||||
gsize buflen)
|
||||
{
|
||||
GChecksum *ctx;
|
||||
gsize digest_len;
|
||||
char digest[16];
|
||||
gsize bufidx = 0;
|
||||
int i;
|
||||
|
||||
nm_assert (g_checksum_type_get_length (G_CHECKSUM_MD5) == sizeof (digest));
|
||||
|
||||
g_return_if_fail (password_len == 0 || password);
|
||||
g_return_if_fail (buffer != NULL);
|
||||
g_return_if_fail (buflen > 0);
|
||||
g_return_if_fail (salt_len == 0 || salt);
|
||||
|
||||
ctx = g_checksum_new (G_CHECKSUM_MD5);
|
||||
|
||||
if (salt_len < 0)
|
||||
salt_len = strlen (salt);
|
||||
if (password_len < 0)
|
||||
password_len = strlen (password);
|
||||
|
||||
for (;;) {
|
||||
if (password_len > 0)
|
||||
g_checksum_update (ctx, (const guchar *) password, password_len);
|
||||
if (salt_len > 0)
|
||||
g_checksum_update (ctx, (const guchar *) salt, salt_len);
|
||||
|
||||
digest_len = sizeof (digest);
|
||||
g_checksum_get_digest (ctx, (guchar *) digest, &digest_len);
|
||||
nm_assert (digest_len == sizeof (digest));
|
||||
|
||||
for (i = 0; i < sizeof (digest); i++) {
|
||||
if (bufidx >= buflen)
|
||||
goto done;
|
||||
buffer[bufidx++] = digest[i];
|
||||
}
|
||||
|
||||
g_checksum_reset (ctx);
|
||||
g_checksum_update (ctx, (const guchar *) digest, sizeof (digest));
|
||||
}
|
||||
|
||||
done:
|
||||
memset (digest, 0, sizeof (digest));
|
||||
g_checksum_free (ctx);
|
||||
}
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
|
||||
/*
|
||||
* Dan Williams <dcbw@redhat.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Copyright 2007 - 2014 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __CRYPTO_H__
|
||||
#define __CRYPTO_H__
|
||||
|
||||
#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
|
||||
#error Cannot use this header.
|
||||
#endif
|
||||
|
||||
#define MD5_HASH_LEN 20
|
||||
#define CIPHER_DES_EDE3_CBC "DES-EDE3-CBC"
|
||||
#define CIPHER_DES_CBC "DES-CBC"
|
||||
#define CIPHER_AES_128_CBC "AES-128-CBC"
|
||||
#define CIPHER_AES_192_CBC "AES-192-CBC"
|
||||
#define CIPHER_AES_256_CBC "AES-256-CBC"
|
||||
|
||||
typedef enum {
|
||||
NM_CRYPTO_KEY_TYPE_UNKNOWN = 0,
|
||||
NM_CRYPTO_KEY_TYPE_RSA,
|
||||
NM_CRYPTO_KEY_TYPE_DSA
|
||||
} NMCryptoKeyType;
|
||||
|
||||
typedef enum {
|
||||
NM_CRYPTO_FILE_FORMAT_UNKNOWN = 0,
|
||||
NM_CRYPTO_FILE_FORMAT_X509,
|
||||
NM_CRYPTO_FILE_FORMAT_RAW_KEY,
|
||||
NM_CRYPTO_FILE_FORMAT_PKCS12
|
||||
} NMCryptoFileFormat;
|
||||
|
||||
gboolean crypto_init (GError **error);
|
||||
|
||||
GByteArray *crypto_decrypt_openssl_private_key_data (const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *password,
|
||||
NMCryptoKeyType *out_key_type,
|
||||
GError **error);
|
||||
|
||||
GByteArray *crypto_decrypt_openssl_private_key (const char *file,
|
||||
const char *password,
|
||||
NMCryptoKeyType *out_key_type,
|
||||
GError **error);
|
||||
|
||||
GByteArray *crypto_load_and_verify_certificate (const char *file,
|
||||
NMCryptoFileFormat *out_file_format,
|
||||
GError **error);
|
||||
|
||||
gboolean crypto_is_pkcs12_file (const char *file, GError **error);
|
||||
|
||||
gboolean crypto_is_pkcs12_data (const guint8 *data, gsize len, GError **error);
|
||||
|
||||
NMCryptoFileFormat crypto_verify_private_key_data (const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *password,
|
||||
gboolean *out_is_encrypted,
|
||||
GError **error);
|
||||
|
||||
NMCryptoFileFormat crypto_verify_private_key (const char *file,
|
||||
const char *password,
|
||||
gboolean *out_is_encrypted,
|
||||
GError **error);
|
||||
|
||||
/* Internal utils API bits for crypto providers */
|
||||
|
||||
void crypto_md5_hash (const char *salt,
|
||||
gssize salt_len,
|
||||
const char *password,
|
||||
gssize password_len,
|
||||
char *buffer,
|
||||
gsize buflen);
|
||||
|
||||
char *crypto_make_des_aes_key (const char *cipher,
|
||||
const char *salt,
|
||||
const gsize salt_len,
|
||||
const char *password,
|
||||
gsize *out_len,
|
||||
GError **error);
|
||||
|
||||
char * crypto_decrypt (const char *cipher,
|
||||
int key_type,
|
||||
const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *iv,
|
||||
const gsize iv_len,
|
||||
const char *key,
|
||||
const gsize key_len,
|
||||
gsize *out_len,
|
||||
GError **error);
|
||||
|
||||
char * crypto_encrypt (const char *cipher,
|
||||
const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *iv,
|
||||
gsize iv_len,
|
||||
const char *key,
|
||||
gsize key_len,
|
||||
gsize *out_len,
|
||||
GError **error);
|
||||
|
||||
gboolean crypto_randomize (void *buffer, gsize buffer_len, GError **error);
|
||||
|
||||
NMCryptoFileFormat crypto_verify_cert (const guint8 *data,
|
||||
gsize len,
|
||||
GError **error);
|
||||
|
||||
gboolean crypto_verify_pkcs12 (const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *password,
|
||||
GError **error);
|
||||
|
||||
gboolean crypto_verify_pkcs8 (const guint8 *data,
|
||||
gsize data_len,
|
||||
gboolean is_encrypted,
|
||||
const char *password,
|
||||
GError **error);
|
||||
|
||||
#endif /* __CRYPTO_H__ */
|
||||
|
|
@ -107,8 +107,7 @@ libnm_core_settings_sources = files(
|
|||
)
|
||||
|
||||
libnm_core_sources = libnm_core_settings_sources + files(
|
||||
'crypto.c',
|
||||
'crypto_' + crypto + '.c',
|
||||
'nm-crypto.c',
|
||||
'nm-connection.c',
|
||||
'nm-dbus-utils.c',
|
||||
'nm-errors.c',
|
||||
|
|
@ -136,7 +135,6 @@ libnm_core_enum = gnome.mkenums(
|
|||
)
|
||||
|
||||
deps = [
|
||||
crypto_dep,
|
||||
dl_dep,
|
||||
libudev_dep,
|
||||
shared_dep,
|
||||
|
|
@ -154,6 +152,32 @@ if enable_json_validation
|
|||
deps += jansson_dep
|
||||
endif
|
||||
|
||||
if (crypto_gnutls_dep.found())
|
||||
libnm_crypto_gnutls = static_library(
|
||||
'nm-crypto-gnutls',
|
||||
sources: [ 'nm-crypto-gnutls.c' ],
|
||||
dependencies: deps + [ crypto_gnutls_dep ],
|
||||
c_args: cflags
|
||||
)
|
||||
endif
|
||||
|
||||
if (crypto_nss_dep.found())
|
||||
libnm_crypto_nss = static_library(
|
||||
'nm-crypto-nss',
|
||||
sources: [ 'nm-crypto-nss.c' ],
|
||||
dependencies: deps + [ crypto_nss_dep ],
|
||||
c_args: cflags
|
||||
)
|
||||
endif
|
||||
|
||||
if crypto == 'gnutls'
|
||||
libnm_crypto = libnm_crypto_gnutls
|
||||
elif crypto == 'nss'
|
||||
libnm_crypto = libnm_crypto_nss
|
||||
else
|
||||
error('bug')
|
||||
endif
|
||||
|
||||
libnm_core_sources_all = libnm_core_sources
|
||||
libnm_core_sources_all += libnm_core_enum
|
||||
libnm_core_sources_all += shared_nm_meta_setting_c
|
||||
|
|
@ -165,6 +189,7 @@ libnm_core = static_library(
|
|||
'nm-core',
|
||||
sources: libnm_core_sources_all,
|
||||
dependencies: deps,
|
||||
link_with: libnm_crypto,
|
||||
c_args: cflags
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -215,6 +215,13 @@ const char *nm_utils_hwaddr_ntoa_buf (gconstpointer addr, gsize addr_len, gboole
|
|||
char *_nm_utils_bin2str (gconstpointer addr, gsize length, gboolean upper_case);
|
||||
void _nm_utils_bin2str_full (gconstpointer addr, gsize length, const char delimiter, gboolean upper_case, char *out);
|
||||
|
||||
guint8 *_nm_utils_str2bin_full (const char *asc,
|
||||
gboolean delimiter_required,
|
||||
const char *delimiter_candidates,
|
||||
guint8 *buffer,
|
||||
gsize buffer_length,
|
||||
gsize *out_len);
|
||||
|
||||
GSList * _nm_utils_hash_values_to_slist (GHashTable *hash);
|
||||
|
||||
GHashTable *_nm_utils_copy_strdict (GHashTable *strdict);
|
||||
|
|
@ -287,12 +294,6 @@ void _nm_dbus_errors_init (void);
|
|||
|
||||
extern gboolean _nm_utils_is_manager_process;
|
||||
|
||||
GByteArray *nm_utils_rsa_key_encrypt (const guint8 *data,
|
||||
gsize len,
|
||||
const char *in_password,
|
||||
char **out_password,
|
||||
GError **error);
|
||||
|
||||
gulong _nm_dbus_signal_connect_data (GDBusProxy *proxy,
|
||||
const char *signal_name,
|
||||
const GVariantType *signature,
|
||||
|
|
@ -587,4 +588,13 @@ const NMSettInfoProperty *_nm_sett_info_property_get (NMSettingClass *setting_cl
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
NMSetting8021xCKScheme _nm_setting_802_1x_cert_get_scheme (GBytes *bytes, GError **error);
|
||||
|
||||
GBytes *_nm_setting_802_1x_cert_value_to_bytes (NMSetting8021xCKScheme scheme,
|
||||
const guint8 *val_bin,
|
||||
gssize val_len,
|
||||
GError **error);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -23,26 +23,53 @@
|
|||
|
||||
#include "nm-default.h"
|
||||
|
||||
#include "nm-crypto-impl.h"
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/crypto.h>
|
||||
#include <gnutls/x509.h>
|
||||
#include <gnutls/pkcs12.h>
|
||||
|
||||
#include "crypto.h"
|
||||
#include "nm-utils/nm-secret-utils.h"
|
||||
#include "nm-errors.h"
|
||||
|
||||
#define SALT_LEN 8
|
||||
/*****************************************************************************/
|
||||
|
||||
static gboolean initialized = FALSE;
|
||||
static gboolean
|
||||
_get_cipher_info (NMCryptoCipherType cipher,
|
||||
int *out_cipher_mech,
|
||||
guint8 *out_real_iv_len)
|
||||
{
|
||||
static const int cipher_mechs[] = {
|
||||
[NM_CRYPTO_CIPHER_DES_EDE3_CBC] = GNUTLS_CIPHER_3DES_CBC,
|
||||
[NM_CRYPTO_CIPHER_DES_CBC] = GNUTLS_CIPHER_DES_CBC,
|
||||
[NM_CRYPTO_CIPHER_AES_128_CBC] = GNUTLS_CIPHER_AES_128_CBC,
|
||||
[NM_CRYPTO_CIPHER_AES_192_CBC] = GNUTLS_CIPHER_AES_192_CBC,
|
||||
[NM_CRYPTO_CIPHER_AES_256_CBC] = GNUTLS_CIPHER_AES_256_CBC,
|
||||
};
|
||||
|
||||
g_return_val_if_fail (_NM_INT_NOT_NEGATIVE (cipher) && (gsize) cipher < G_N_ELEMENTS (cipher_mechs), FALSE);
|
||||
|
||||
if (cipher_mechs[cipher] == 0)
|
||||
return FALSE;
|
||||
|
||||
NM_SET_OUT (out_cipher_mech, cipher_mechs[cipher]);
|
||||
NM_SET_OUT (out_real_iv_len, nm_crypto_cipher_get_info (cipher)->real_iv_len);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
gboolean
|
||||
crypto_init (GError **error)
|
||||
_nm_crypto_init (GError **error)
|
||||
{
|
||||
static gboolean initialized = FALSE;
|
||||
|
||||
if (initialized)
|
||||
return TRUE;
|
||||
|
||||
if (gnutls_global_init() != 0) {
|
||||
gnutls_global_deinit();
|
||||
if (gnutls_global_init () != 0) {
|
||||
gnutls_global_deinit ();
|
||||
g_set_error_literal (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_FAILED,
|
||||
_("Failed to initialize the crypto engine."));
|
||||
|
|
@ -53,61 +80,47 @@ crypto_init (GError **error)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
char *
|
||||
crypto_decrypt (const char *cipher,
|
||||
int key_type,
|
||||
const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *iv,
|
||||
const gsize iv_len,
|
||||
const char *key,
|
||||
const gsize key_len,
|
||||
gsize *out_len,
|
||||
GError **error)
|
||||
/*****************************************************************************/
|
||||
|
||||
guint8 *
|
||||
_nmtst_crypto_decrypt (NMCryptoCipherType cipher,
|
||||
const guint8 *data,
|
||||
gsize data_len,
|
||||
const guint8 *iv,
|
||||
gsize iv_len,
|
||||
const guint8 *key,
|
||||
gsize key_len,
|
||||
gsize *out_len,
|
||||
GError **error)
|
||||
{
|
||||
gnutls_cipher_hd_t ctx;
|
||||
gnutls_datum_t key_dt, iv_dt;
|
||||
int err;
|
||||
int cipher_mech, i;
|
||||
char *output = NULL;
|
||||
gboolean success = FALSE;
|
||||
gsize pad_len, real_iv_len;
|
||||
int cipher_mech;
|
||||
nm_auto_clear_secret_ptr NMSecretPtr output = { 0 };
|
||||
guint8 pad_i, pad_len;
|
||||
guint8 real_iv_len;
|
||||
|
||||
if (!crypto_init (error))
|
||||
return NULL;
|
||||
|
||||
if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) {
|
||||
cipher_mech = GNUTLS_CIPHER_3DES_CBC;
|
||||
real_iv_len = SALT_LEN;
|
||||
} else if (!strcmp (cipher, CIPHER_DES_CBC)) {
|
||||
cipher_mech = GNUTLS_CIPHER_DES_CBC;
|
||||
real_iv_len = SALT_LEN;
|
||||
} else if (!strcmp (cipher, CIPHER_AES_128_CBC)) {
|
||||
cipher_mech = GNUTLS_CIPHER_AES_128_CBC;
|
||||
real_iv_len = 16;
|
||||
} else if (!strcmp (cipher, CIPHER_AES_192_CBC)) {
|
||||
cipher_mech = GNUTLS_CIPHER_AES_192_CBC;
|
||||
real_iv_len = 16;
|
||||
} else if (!strcmp (cipher, CIPHER_AES_256_CBC)) {
|
||||
cipher_mech = GNUTLS_CIPHER_AES_256_CBC;
|
||||
real_iv_len = 16;
|
||||
} else {
|
||||
if (!_get_cipher_info (cipher, &cipher_mech, &real_iv_len)) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_UNKNOWN_CIPHER,
|
||||
_("Private key cipher '%s' was unknown."),
|
||||
cipher);
|
||||
_("Unsupported key cipher for decryption"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!_nm_crypto_init (error))
|
||||
return NULL;
|
||||
|
||||
if (iv_len < real_iv_len) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Invalid IV length (must be at least %zd)."),
|
||||
real_iv_len);
|
||||
_("Invalid IV length (must be at least %u)."),
|
||||
(guint) real_iv_len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
output = g_malloc0 (data_len);
|
||||
output.len = data_len;
|
||||
output.bin = g_malloc (data_len);
|
||||
|
||||
key_dt.data = (unsigned char *) key;
|
||||
key_dt.size = key_len;
|
||||
|
|
@ -120,107 +133,81 @@ crypto_decrypt (const char *cipher,
|
|||
NM_CRYPTO_ERROR_DECRYPTION_FAILED,
|
||||
_("Failed to initialize the decryption cipher context: %s (%s)"),
|
||||
gnutls_strerror_name (err), gnutls_strerror (err));
|
||||
goto out;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err = gnutls_cipher_decrypt2 (ctx, data, data_len, output, data_len);
|
||||
err = gnutls_cipher_decrypt2 (ctx, data, data_len, output.bin, output.len);
|
||||
|
||||
gnutls_cipher_deinit (ctx);
|
||||
|
||||
if (err < 0) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_DECRYPTION_FAILED,
|
||||
_("Failed to decrypt the private key: %s (%s)"),
|
||||
gnutls_strerror_name (err), gnutls_strerror (err));
|
||||
goto out;
|
||||
return NULL;
|
||||
}
|
||||
pad_len = output[data_len - 1];
|
||||
|
||||
pad_len = output.len > 0
|
||||
? output.bin[output.len - 1]
|
||||
: 0;
|
||||
|
||||
/* Check if the padding at the end of the decrypted data is valid */
|
||||
if (pad_len == 0 || pad_len > real_iv_len) {
|
||||
if ( pad_len == 0
|
||||
|| pad_len > real_iv_len) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_DECRYPTION_FAILED,
|
||||
_("Failed to decrypt the private key: unexpected padding length."));
|
||||
goto out;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Validate tail padding; last byte is the padding size, and all pad bytes
|
||||
* should contain the padding size.
|
||||
*/
|
||||
for (i = 1; i <= pad_len; ++i) {
|
||||
if (output[data_len - i] != pad_len) {
|
||||
for (pad_i = 1; pad_i <= pad_len; ++pad_i) {
|
||||
if (output.bin[data_len - pad_i] != pad_len) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_DECRYPTION_FAILED,
|
||||
_("Failed to decrypt the private key."));
|
||||
goto out;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
*out_len = data_len - pad_len;
|
||||
success = TRUE;
|
||||
|
||||
out:
|
||||
if (!success) {
|
||||
if (output) {
|
||||
/* Don't expose key material */
|
||||
memset (output, 0, data_len);
|
||||
g_free (output);
|
||||
output = NULL;
|
||||
}
|
||||
}
|
||||
gnutls_cipher_deinit (ctx);
|
||||
return output;
|
||||
*out_len = output.len - pad_len;
|
||||
return g_steal_pointer (&output.bin);
|
||||
}
|
||||
|
||||
char *
|
||||
crypto_encrypt (const char *cipher,
|
||||
const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *iv,
|
||||
const gsize iv_len,
|
||||
const char *key,
|
||||
gsize key_len,
|
||||
gsize *out_len,
|
||||
GError **error)
|
||||
guint8 *
|
||||
_nmtst_crypto_encrypt (NMCryptoCipherType cipher,
|
||||
const guint8 *data,
|
||||
gsize data_len,
|
||||
const guint8 *iv,
|
||||
gsize iv_len,
|
||||
const guint8 *key,
|
||||
gsize key_len,
|
||||
gsize *out_len,
|
||||
GError **error)
|
||||
{
|
||||
gnutls_cipher_hd_t ctx;
|
||||
gnutls_datum_t key_dt, iv_dt;
|
||||
int err;
|
||||
int cipher_mech;
|
||||
char *output = NULL;
|
||||
gboolean success = FALSE;
|
||||
gsize padded_buf_len, pad_len, output_len;
|
||||
char *padded_buf = NULL;
|
||||
guint32 i;
|
||||
nm_auto_clear_secret_ptr NMSecretPtr output = { 0 };
|
||||
nm_auto_clear_secret_ptr NMSecretPtr padded_buf = { 0 };
|
||||
gsize i, pad_len;
|
||||
|
||||
if (!crypto_init (error))
|
||||
return NULL;
|
||||
nm_assert (iv_len);
|
||||
|
||||
if (!strcmp (cipher, CIPHER_DES_EDE3_CBC))
|
||||
cipher_mech = GNUTLS_CIPHER_3DES_CBC;
|
||||
else if (!strcmp (cipher, CIPHER_AES_128_CBC))
|
||||
cipher_mech = GNUTLS_CIPHER_AES_128_CBC;
|
||||
else if (!strcmp (cipher, CIPHER_AES_192_CBC))
|
||||
cipher_mech = GNUTLS_CIPHER_AES_192_CBC;
|
||||
else if (!strcmp (cipher, CIPHER_AES_256_CBC))
|
||||
cipher_mech = GNUTLS_CIPHER_AES_256_CBC;
|
||||
else {
|
||||
if ( cipher == NM_CRYPTO_CIPHER_DES_CBC
|
||||
|| !_get_cipher_info (cipher, &cipher_mech, NULL)) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_UNKNOWN_CIPHER,
|
||||
_("Private key cipher '%s' was unknown."),
|
||||
cipher);
|
||||
_("Unsupported key cipher for encryption"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If data_len % ivlen == 0, then we add another complete block
|
||||
* onto the end so that the decrypter knows there's padding.
|
||||
*/
|
||||
pad_len = iv_len - (data_len % iv_len);
|
||||
output_len = padded_buf_len = data_len + pad_len;
|
||||
padded_buf = g_malloc0 (padded_buf_len);
|
||||
|
||||
memcpy (padded_buf, data, data_len);
|
||||
for (i = 0; i < pad_len; i++)
|
||||
padded_buf[data_len + i] = (guint8) (pad_len & 0xFF);
|
||||
|
||||
output = g_malloc0 (output_len);
|
||||
if (!_nm_crypto_init (error))
|
||||
return NULL;
|
||||
|
||||
key_dt.data = (unsigned char *) key;
|
||||
key_dt.size = key_len;
|
||||
|
|
@ -233,51 +220,50 @@ crypto_encrypt (const char *cipher,
|
|||
NM_CRYPTO_ERROR_ENCRYPTION_FAILED,
|
||||
_("Failed to initialize the encryption cipher context: %s (%s)"),
|
||||
gnutls_strerror_name (err), gnutls_strerror (err));
|
||||
goto out;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err = gnutls_cipher_encrypt2 (ctx, padded_buf, padded_buf_len, output, output_len);
|
||||
/* If data_len % ivlen == 0, then we add another complete block
|
||||
* onto the end so that the decrypter knows there's padding.
|
||||
*/
|
||||
pad_len = iv_len - (data_len % iv_len);
|
||||
|
||||
padded_buf.len = data_len + pad_len;
|
||||
padded_buf.bin = g_malloc (padded_buf.len);
|
||||
memcpy (padded_buf.bin, data, data_len);
|
||||
for (i = 0; i < pad_len; i++)
|
||||
padded_buf.bin[data_len + i] = (guint8) (pad_len & 0xFF);
|
||||
|
||||
output.len = padded_buf.len;
|
||||
output.bin = g_malloc (output.len);
|
||||
|
||||
err = gnutls_cipher_encrypt2 (ctx, padded_buf.bin, padded_buf.len, output.bin, output.len);
|
||||
|
||||
gnutls_cipher_deinit (ctx);
|
||||
|
||||
if (err < 0) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_ENCRYPTION_FAILED,
|
||||
_("Failed to encrypt the data: %s (%s)"),
|
||||
gnutls_strerror_name (err), gnutls_strerror (err));
|
||||
goto out;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*out_len = output_len;
|
||||
success = TRUE;
|
||||
|
||||
out:
|
||||
if (padded_buf) {
|
||||
memset (padded_buf, 0, padded_buf_len);
|
||||
g_free (padded_buf);
|
||||
padded_buf = NULL;
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
if (output) {
|
||||
/* Don't expose key material */
|
||||
memset (output, 0, output_len);
|
||||
g_free (output);
|
||||
output = NULL;
|
||||
}
|
||||
}
|
||||
gnutls_cipher_deinit (ctx);
|
||||
return output;
|
||||
*out_len = output.len;
|
||||
return g_steal_pointer (&output.bin);
|
||||
}
|
||||
|
||||
NMCryptoFileFormat
|
||||
crypto_verify_cert (const unsigned char *data,
|
||||
gsize len,
|
||||
GError **error)
|
||||
gboolean
|
||||
_nm_crypto_verify_x509 (const guint8 *data,
|
||||
gsize len,
|
||||
GError **error)
|
||||
{
|
||||
gnutls_x509_crt_t der;
|
||||
gnutls_datum_t dt;
|
||||
int err;
|
||||
|
||||
if (!crypto_init (error))
|
||||
return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
if (!_nm_crypto_init (error))
|
||||
return FALSE;
|
||||
|
||||
err = gnutls_x509_crt_init (&der);
|
||||
if (err < 0) {
|
||||
|
|
@ -285,7 +271,7 @@ crypto_verify_cert (const unsigned char *data,
|
|||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Error initializing certificate data: %s"),
|
||||
gnutls_strerror (err));
|
||||
return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Try DER first */
|
||||
|
|
@ -294,36 +280,35 @@ crypto_verify_cert (const unsigned char *data,
|
|||
err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_DER);
|
||||
if (err == GNUTLS_E_SUCCESS) {
|
||||
gnutls_x509_crt_deinit (der);
|
||||
return NM_CRYPTO_FILE_FORMAT_X509;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* And PEM next */
|
||||
err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_PEM);
|
||||
gnutls_x509_crt_deinit (der);
|
||||
if (err == GNUTLS_E_SUCCESS)
|
||||
return NM_CRYPTO_FILE_FORMAT_X509;
|
||||
return TRUE;
|
||||
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Couldn't decode certificate: %s"),
|
||||
gnutls_strerror (err));
|
||||
return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
crypto_verify_pkcs12 (const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *password,
|
||||
GError **error)
|
||||
_nm_crypto_verify_pkcs12 (const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *password,
|
||||
GError **error)
|
||||
{
|
||||
gnutls_pkcs12_t p12;
|
||||
gnutls_datum_t dt;
|
||||
gboolean success = FALSE;
|
||||
int err;
|
||||
|
||||
g_return_val_if_fail (data != NULL, FALSE);
|
||||
|
||||
if (!crypto_init (error))
|
||||
if (!_nm_crypto_init (error))
|
||||
return FALSE;
|
||||
|
||||
dt.data = (unsigned char *) data;
|
||||
|
|
@ -348,31 +333,32 @@ crypto_verify_pkcs12 (const guint8 *data,
|
|||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Couldn't decode PKCS#12 file: %s"),
|
||||
gnutls_strerror (err));
|
||||
goto out;
|
||||
gnutls_pkcs12_deinit (p12);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
err = gnutls_pkcs12_verify_mac (p12, password);
|
||||
if (err == GNUTLS_E_SUCCESS)
|
||||
success = TRUE;
|
||||
else {
|
||||
|
||||
gnutls_pkcs12_deinit (p12);
|
||||
|
||||
if (err != GNUTLS_E_SUCCESS) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_DECRYPTION_FAILED,
|
||||
_("Couldn't verify PKCS#12 file: %s"),
|
||||
gnutls_strerror (err));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
out:
|
||||
gnutls_pkcs12_deinit (p12);
|
||||
return success;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
crypto_verify_pkcs8 (const guint8 *data,
|
||||
gsize data_len,
|
||||
gboolean is_encrypted,
|
||||
const char *password,
|
||||
GError **error)
|
||||
_nm_crypto_verify_pkcs8 (const guint8 *data,
|
||||
gsize data_len,
|
||||
gboolean is_encrypted,
|
||||
const char *password,
|
||||
GError **error)
|
||||
{
|
||||
gnutls_x509_privkey_t p8;
|
||||
gnutls_datum_t dt;
|
||||
|
|
@ -380,12 +366,9 @@ crypto_verify_pkcs8 (const guint8 *data,
|
|||
|
||||
g_return_val_if_fail (data != NULL, FALSE);
|
||||
|
||||
if (!crypto_init (error))
|
||||
if (!_nm_crypto_init (error))
|
||||
return FALSE;
|
||||
|
||||
dt.data = (unsigned char *) data;
|
||||
dt.size = data_len;
|
||||
|
||||
err = gnutls_x509_privkey_init (&p8);
|
||||
if (err < 0) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
|
|
@ -395,11 +378,15 @@ crypto_verify_pkcs8 (const guint8 *data,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
dt.data = (unsigned char *) data;
|
||||
dt.size = data_len;
|
||||
|
||||
err = gnutls_x509_privkey_import_pkcs8 (p8,
|
||||
&dt,
|
||||
GNUTLS_X509_FMT_DER,
|
||||
is_encrypted ? password : NULL,
|
||||
is_encrypted ? 0 : GNUTLS_PKCS_PLAIN);
|
||||
|
||||
gnutls_x509_privkey_deinit (p8);
|
||||
|
||||
if (err < 0) {
|
||||
|
|
@ -423,9 +410,9 @@ crypto_verify_pkcs8 (const guint8 *data,
|
|||
}
|
||||
|
||||
gboolean
|
||||
crypto_randomize (void *buffer, gsize buffer_len, GError **error)
|
||||
_nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error)
|
||||
{
|
||||
if (!crypto_init (error))
|
||||
if (!_nm_crypto_init (error))
|
||||
return FALSE;
|
||||
|
||||
gnutls_rnd (GNUTLS_RND_RANDOM, buffer, buffer_len);
|
||||
74
libnm-core/nm-crypto-impl.h
Normal file
74
libnm-core/nm-crypto-impl.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
|
||||
/*
|
||||
* Dan Williams <dcbw@redhat.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Copyright 2007 - 2018 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __NM_CRYPTO_IMPL_H__
|
||||
#define __NM_CRYPTO_IMPL_H__
|
||||
|
||||
#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
|
||||
#error Cannot use this header.
|
||||
#endif
|
||||
|
||||
#include "nm-crypto.h"
|
||||
|
||||
gboolean _nm_crypto_init (GError **error);
|
||||
|
||||
gboolean _nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error);
|
||||
|
||||
gboolean _nm_crypto_verify_x509 (const guint8 *data,
|
||||
gsize len,
|
||||
GError **error);
|
||||
|
||||
gboolean _nm_crypto_verify_pkcs12 (const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *password,
|
||||
GError **error);
|
||||
|
||||
gboolean _nm_crypto_verify_pkcs8 (const guint8 *data,
|
||||
gsize data_len,
|
||||
gboolean is_encrypted,
|
||||
const char *password,
|
||||
GError **error);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
guint8 *_nmtst_crypto_encrypt (NMCryptoCipherType cipher,
|
||||
const guint8 *data,
|
||||
gsize data_len,
|
||||
const guint8 *iv,
|
||||
gsize iv_len,
|
||||
const guint8 *key,
|
||||
gsize key_len,
|
||||
gsize *out_len,
|
||||
GError **error);
|
||||
|
||||
guint8 *_nmtst_crypto_decrypt (NMCryptoCipherType cipher,
|
||||
const guint8 *data,
|
||||
gsize data_len,
|
||||
const guint8 *iv,
|
||||
gsize iv_len,
|
||||
const guint8 *key,
|
||||
gsize key_len,
|
||||
gsize *out_len,
|
||||
GError **error);
|
||||
|
||||
#endif /* __NM_CRYPTO_IMPL_H__ */
|
||||
|
|
@ -23,6 +23,8 @@
|
|||
|
||||
#include "nm-default.h"
|
||||
|
||||
#include "nm-crypto-impl.h"
|
||||
|
||||
#include <prinit.h>
|
||||
#include <nss.h>
|
||||
#include <pk11pub.h>
|
||||
|
|
@ -33,20 +35,46 @@
|
|||
#include <ciferfam.h>
|
||||
#include <p12plcy.h>
|
||||
|
||||
#include "crypto.h"
|
||||
#include "nm-utils/nm-secret-utils.h"
|
||||
#include "nm-errors.h"
|
||||
|
||||
static gboolean initialized = FALSE;
|
||||
/*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
_get_cipher_info (NMCryptoCipherType cipher,
|
||||
CK_MECHANISM_TYPE *out_cipher_mech,
|
||||
guint8 *out_real_iv_len)
|
||||
{
|
||||
static const CK_MECHANISM_TYPE cipher_mechs[] = {
|
||||
[NM_CRYPTO_CIPHER_DES_EDE3_CBC] = CKM_DES3_CBC_PAD,
|
||||
[NM_CRYPTO_CIPHER_DES_CBC] = CKM_DES_CBC_PAD,
|
||||
[NM_CRYPTO_CIPHER_AES_128_CBC] = CKM_AES_CBC_PAD,
|
||||
[NM_CRYPTO_CIPHER_AES_192_CBC] = CKM_AES_CBC_PAD,
|
||||
[NM_CRYPTO_CIPHER_AES_256_CBC] = CKM_AES_CBC_PAD,
|
||||
};
|
||||
|
||||
g_return_val_if_fail (_NM_INT_NOT_NEGATIVE (cipher) && (gsize) cipher < G_N_ELEMENTS (cipher_mechs), FALSE);
|
||||
|
||||
if (!cipher_mechs[cipher])
|
||||
return FALSE;
|
||||
|
||||
NM_SET_OUT (out_cipher_mech, cipher_mechs[cipher]);
|
||||
NM_SET_OUT (out_real_iv_len, nm_crypto_cipher_get_info (cipher)->real_iv_len);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
gboolean
|
||||
crypto_init (GError **error)
|
||||
_nm_crypto_init (GError **error)
|
||||
{
|
||||
static gboolean initialized = FALSE;
|
||||
SECStatus ret;
|
||||
|
||||
if (initialized)
|
||||
return TRUE;
|
||||
|
||||
PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 1);
|
||||
PR_Init (PR_USER_THREAD, PR_PRIORITY_NORMAL, 1);
|
||||
ret = NSS_NoDB_Init (NULL);
|
||||
if (ret != SECSuccess) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
|
|
@ -57,74 +85,61 @@ crypto_init (GError **error)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
|
||||
SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1);
|
||||
SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
|
||||
SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1);
|
||||
SEC_PKCS12EnableCipher(PKCS12_DES_56, 1);
|
||||
SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1);
|
||||
SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1);
|
||||
SEC_PKCS12EnableCipher (PKCS12_RC4_40, 1);
|
||||
SEC_PKCS12EnableCipher (PKCS12_RC4_128, 1);
|
||||
SEC_PKCS12EnableCipher (PKCS12_RC2_CBC_40, 1);
|
||||
SEC_PKCS12EnableCipher (PKCS12_RC2_CBC_128, 1);
|
||||
SEC_PKCS12EnableCipher (PKCS12_DES_56, 1);
|
||||
SEC_PKCS12EnableCipher (PKCS12_DES_EDE3_168, 1);
|
||||
SEC_PKCS12SetPreferredCipher (PKCS12_DES_EDE3_168, 1);
|
||||
|
||||
initialized = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
char *
|
||||
crypto_decrypt (const char *cipher,
|
||||
int key_type,
|
||||
const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *iv,
|
||||
const gsize iv_len,
|
||||
const char *key,
|
||||
const gsize key_len,
|
||||
gsize *out_len,
|
||||
GError **error)
|
||||
guint8 *
|
||||
_nmtst_crypto_decrypt (NMCryptoCipherType cipher,
|
||||
const guint8 *data,
|
||||
gsize data_len,
|
||||
const guint8 *iv,
|
||||
gsize iv_len,
|
||||
const guint8 *key,
|
||||
gsize key_len,
|
||||
gsize *out_len,
|
||||
GError **error)
|
||||
{
|
||||
char *output = NULL;
|
||||
int decrypted_len = 0;
|
||||
CK_MECHANISM_TYPE cipher_mech;
|
||||
PK11SlotInfo *slot = NULL;
|
||||
SECItem key_item;
|
||||
PK11SymKey *sym_key = NULL;
|
||||
SECItem *sec_param = NULL;
|
||||
PK11Context *ctx = NULL;
|
||||
nm_auto_clear_secret_ptr NMSecretPtr output = { 0 };
|
||||
SECStatus s;
|
||||
gboolean success = FALSE;
|
||||
unsigned pad_len = 0, extra = 0;
|
||||
guint32 i, real_iv_len = 0;
|
||||
int decrypted_len = 0;
|
||||
unsigned extra = 0;
|
||||
unsigned pad_len = 0;
|
||||
guint32 i;
|
||||
guint8 real_iv_len;
|
||||
|
||||
if (!crypto_init (error))
|
||||
return NULL;
|
||||
|
||||
if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) {
|
||||
cipher_mech = CKM_DES3_CBC_PAD;
|
||||
real_iv_len = 8;
|
||||
} else if (!strcmp (cipher, CIPHER_DES_CBC)) {
|
||||
cipher_mech = CKM_DES_CBC_PAD;
|
||||
real_iv_len = 8;
|
||||
} else if (NM_IN_STRSET (cipher, CIPHER_AES_128_CBC,
|
||||
CIPHER_AES_192_CBC,
|
||||
CIPHER_AES_256_CBC)) {
|
||||
cipher_mech = CKM_AES_CBC_PAD;
|
||||
real_iv_len = 16;
|
||||
} else {
|
||||
if (!_get_cipher_info (cipher, &cipher_mech, &real_iv_len)) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_UNKNOWN_CIPHER,
|
||||
_("Private key cipher '%s' was unknown."),
|
||||
cipher);
|
||||
_("Unsupported key cipher for decryption"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (iv_len < real_iv_len) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Invalid IV length (must be at least %d)."),
|
||||
real_iv_len);
|
||||
_("Invalid IV length (must be at least %u)."),
|
||||
(guint) real_iv_len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
output = g_malloc0 (data_len);
|
||||
if (!_nm_crypto_init (error))
|
||||
return NULL;
|
||||
|
||||
slot = PK11_GetBestSlot (cipher_mech, NULL);
|
||||
if (!slot) {
|
||||
|
|
@ -162,10 +177,13 @@ crypto_decrypt (const char *cipher,
|
|||
goto out;
|
||||
}
|
||||
|
||||
output.len = data_len;
|
||||
output.bin = g_malloc (data_len);
|
||||
|
||||
s = PK11_CipherOp (ctx,
|
||||
(unsigned char *) output,
|
||||
(unsigned char *) output.bin,
|
||||
&decrypted_len,
|
||||
data_len,
|
||||
output.len,
|
||||
data,
|
||||
data_len);
|
||||
if (s != SECSuccess) {
|
||||
|
|
@ -184,7 +202,7 @@ crypto_decrypt (const char *cipher,
|
|||
}
|
||||
|
||||
s = PK11_DigestFinal (ctx,
|
||||
(unsigned char *) (output + decrypted_len),
|
||||
(unsigned char *) &output.bin[decrypted_len],
|
||||
&extra,
|
||||
data_len - decrypted_len);
|
||||
if (s != SECSuccess) {
|
||||
|
|
@ -194,6 +212,7 @@ crypto_decrypt (const char *cipher,
|
|||
PORT_GetError ());
|
||||
goto out;
|
||||
}
|
||||
|
||||
decrypted_len += extra;
|
||||
pad_len = data_len - decrypted_len;
|
||||
|
||||
|
|
@ -209,7 +228,7 @@ crypto_decrypt (const char *cipher,
|
|||
* should contain the padding size.
|
||||
*/
|
||||
for (i = pad_len; i > 0; i--) {
|
||||
if (output[data_len - i] != pad_len) {
|
||||
if (output.bin[data_len - i] != pad_len) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_DECRYPTION_FAILED,
|
||||
_("Failed to decrypt the private key."));
|
||||
|
|
@ -217,7 +236,6 @@ crypto_decrypt (const char *cipher,
|
|||
}
|
||||
}
|
||||
|
||||
*out_len = decrypted_len;
|
||||
success = TRUE;
|
||||
|
||||
out:
|
||||
|
|
@ -230,27 +248,25 @@ out:
|
|||
if (slot)
|
||||
PK11_FreeSlot (slot);
|
||||
|
||||
if (!success) {
|
||||
if (output) {
|
||||
/* Don't expose key material */
|
||||
memset (output, 0, data_len);
|
||||
g_free (output);
|
||||
output = NULL;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
if (!success)
|
||||
return NULL;
|
||||
|
||||
if (decrypted_len < output.len)
|
||||
nm_explicit_bzero (&output.bin[decrypted_len], output.len - decrypted_len);
|
||||
*out_len = decrypted_len;
|
||||
return g_steal_pointer (&output.bin);
|
||||
}
|
||||
|
||||
char *
|
||||
crypto_encrypt (const char *cipher,
|
||||
const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *iv,
|
||||
gsize iv_len,
|
||||
const char *key,
|
||||
gsize key_len,
|
||||
gsize *out_len,
|
||||
GError **error)
|
||||
guint8 *
|
||||
_nmtst_crypto_encrypt (NMCryptoCipherType cipher,
|
||||
const guint8 *data,
|
||||
gsize data_len,
|
||||
const guint8 *iv,
|
||||
gsize iv_len,
|
||||
const guint8 *key,
|
||||
gsize key_len,
|
||||
gsize *out_len,
|
||||
GError **error)
|
||||
{
|
||||
SECStatus ret;
|
||||
CK_MECHANISM_TYPE cipher_mech = CKM_DES3_CBC_PAD;
|
||||
|
|
@ -260,49 +276,29 @@ crypto_encrypt (const char *cipher,
|
|||
PK11SymKey *sym_key = NULL;
|
||||
SECItem *sec_param = NULL;
|
||||
PK11Context *ctx = NULL;
|
||||
unsigned char *output, *padded_buf;
|
||||
gsize output_len;
|
||||
nm_auto_clear_secret_ptr NMSecretPtr padded_buf = { 0 };
|
||||
nm_auto_clear_secret_ptr NMSecretPtr output = { 0 };
|
||||
int encrypted_len = 0, i;
|
||||
gboolean success = FALSE;
|
||||
gsize padded_buf_len, pad_len;
|
||||
gsize pad_len;
|
||||
|
||||
if (!crypto_init (error))
|
||||
return NULL;
|
||||
|
||||
if (!strcmp (cipher, CIPHER_DES_EDE3_CBC))
|
||||
cipher_mech = CKM_DES3_CBC_PAD;
|
||||
else if (NM_IN_STRSET (cipher,
|
||||
CIPHER_AES_128_CBC,
|
||||
CIPHER_AES_192_CBC,
|
||||
CIPHER_AES_256_CBC))
|
||||
cipher_mech = CKM_AES_CBC_PAD;
|
||||
else {
|
||||
if ( cipher == NM_CRYPTO_CIPHER_DES_CBC
|
||||
|| !_get_cipher_info (cipher, &cipher_mech, NULL)) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_UNKNOWN_CIPHER,
|
||||
_("Private key cipher '%s' was unknown."),
|
||||
cipher);
|
||||
_("Unsupported key cipher for encryption"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If data->len % ivlen == 0, then we add another complete block
|
||||
* onto the end so that the decrypter knows there's padding.
|
||||
*/
|
||||
pad_len = iv_len - (data_len % iv_len);
|
||||
output_len = padded_buf_len = data_len + pad_len;
|
||||
padded_buf = g_malloc0 (padded_buf_len);
|
||||
|
||||
memcpy (padded_buf, data, data_len);
|
||||
for (i = 0; i < pad_len; i++)
|
||||
padded_buf[data_len + i] = (guint8) (pad_len & 0xFF);
|
||||
|
||||
output = g_malloc0 (output_len);
|
||||
if (!_nm_crypto_init (error))
|
||||
return NULL;
|
||||
|
||||
slot = PK11_GetBestSlot (cipher_mech, NULL);
|
||||
if (!slot) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_FAILED,
|
||||
_("Failed to initialize the encryption cipher slot."));
|
||||
goto out;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sym_key = PK11_ImportSymKey (slot, cipher_mech, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL);
|
||||
|
|
@ -329,7 +325,22 @@ crypto_encrypt (const char *cipher,
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = PK11_CipherOp (ctx, output, &encrypted_len, output_len, padded_buf, padded_buf_len);
|
||||
/* If data->len % ivlen == 0, then we add another complete block
|
||||
* onto the end so that the decrypter knows there's padding.
|
||||
*/
|
||||
pad_len = iv_len - (data_len % iv_len);
|
||||
|
||||
padded_buf.len = data_len + pad_len;
|
||||
padded_buf.bin = g_malloc (padded_buf.len);
|
||||
|
||||
memcpy (padded_buf.bin, data, data_len);
|
||||
for (i = 0; i < pad_len; i++)
|
||||
padded_buf.bin[data_len + i] = (guint8) (pad_len & 0xFF);
|
||||
|
||||
output.len = padded_buf.len;
|
||||
output.bin = g_malloc (output.len);
|
||||
|
||||
ret = PK11_CipherOp (ctx, output.bin, &encrypted_len, output.len, padded_buf.bin, padded_buf.len);
|
||||
if (ret != SECSuccess) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_ENCRYPTION_FAILED,
|
||||
|
|
@ -338,46 +349,41 @@ crypto_encrypt (const char *cipher,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (encrypted_len != output_len) {
|
||||
if (encrypted_len != output.len) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_ENCRYPTION_FAILED,
|
||||
_("Unexpected amount of data after encrypting."));
|
||||
goto out;
|
||||
}
|
||||
|
||||
*out_len = encrypted_len;
|
||||
success = TRUE;
|
||||
|
||||
out:
|
||||
if (ctx)
|
||||
PK11_DestroyContext (ctx, PR_TRUE);
|
||||
if (sym_key)
|
||||
PK11_FreeSymKey (sym_key);
|
||||
if (sec_param)
|
||||
SECITEM_FreeItem (sec_param, PR_TRUE);
|
||||
if (sym_key)
|
||||
PK11_FreeSymKey (sym_key);
|
||||
if (slot)
|
||||
PK11_FreeSlot (slot);
|
||||
|
||||
memset (padded_buf, 0, padded_buf_len);
|
||||
g_free (padded_buf);
|
||||
if (!success)
|
||||
return NULL;
|
||||
|
||||
if (!success) {
|
||||
memset (output, 0, output_len);
|
||||
g_free (output);
|
||||
output = NULL;
|
||||
}
|
||||
return (char *) output;
|
||||
*out_len = output.len;
|
||||
return g_steal_pointer (&output.bin);
|
||||
}
|
||||
|
||||
NMCryptoFileFormat
|
||||
crypto_verify_cert (const unsigned char *data,
|
||||
gsize len,
|
||||
GError **error)
|
||||
gboolean
|
||||
_nm_crypto_verify_x509 (const guint8 *data,
|
||||
gsize len,
|
||||
GError **error)
|
||||
{
|
||||
CERTCertificate *cert;
|
||||
|
||||
if (!crypto_init (error))
|
||||
return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
if (!_nm_crypto_init (error))
|
||||
return FALSE;
|
||||
|
||||
/* Try DER/PEM first */
|
||||
cert = CERT_DecodeCertFromPackage ((char *) data, len);
|
||||
|
|
@ -385,76 +391,85 @@ crypto_verify_cert (const unsigned char *data,
|
|||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Couldn't decode certificate: %d"),
|
||||
PORT_GetError());
|
||||
return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
PORT_GetError ());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CERT_DestroyCertificate (cert);
|
||||
return NM_CRYPTO_FILE_FORMAT_X509;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
crypto_verify_pkcs12 (const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *password,
|
||||
GError **error)
|
||||
_nm_crypto_verify_pkcs12 (const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *password,
|
||||
GError **error)
|
||||
{
|
||||
SEC_PKCS12DecoderContext *p12ctx = NULL;
|
||||
SECItem pw = { 0 };
|
||||
PK11SlotInfo *slot = NULL;
|
||||
SECStatus s;
|
||||
gunichar2 *ucs2_password;
|
||||
long ucs2_chars = 0;
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
guint16 *p;
|
||||
#endif /* WORDS_BIGENDIAN */
|
||||
gboolean success = FALSE;
|
||||
|
||||
if (error)
|
||||
g_return_val_if_fail (*error == NULL, FALSE);
|
||||
g_return_val_if_fail (!error || !*error, FALSE);
|
||||
|
||||
if (!crypto_init (error))
|
||||
if (!_nm_crypto_init (error))
|
||||
return FALSE;
|
||||
|
||||
/* PKCS#12 passwords are apparently UCS2 BIG ENDIAN, and NSS doesn't do
|
||||
* any conversions for us.
|
||||
*/
|
||||
if (password && *password) {
|
||||
if (!g_utf8_validate (password, -1, NULL)) {
|
||||
nm_auto_clear_secret_ptr NMSecretPtr ucs2_password = { 0 };
|
||||
|
||||
if (g_utf8_validate (password, -1, NULL)) {
|
||||
long ucs2_chars;
|
||||
|
||||
ucs2_password.bin = (guint8 *) g_utf8_to_utf16 (password, strlen (password), NULL, &ucs2_chars, NULL);
|
||||
|
||||
/* cannot fail, because password is valid UTF-8*/
|
||||
nm_assert (ucs2_password.bin && ucs2_chars > 0);
|
||||
|
||||
ucs2_password.len = ucs2_chars * 2;
|
||||
}
|
||||
|
||||
if (!ucs2_password.bin || ucs2_password.len == 0) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_PASSWORD,
|
||||
_("Password must be UTF-8"));
|
||||
return FALSE;
|
||||
}
|
||||
ucs2_password = g_utf8_to_utf16 (password, strlen (password), NULL, &ucs2_chars, NULL);
|
||||
/* Can't fail if g_utf8_validate() succeeded */
|
||||
g_return_val_if_fail (ucs2_password != NULL && ucs2_chars != 0, FALSE);
|
||||
|
||||
ucs2_chars *= 2; /* convert # UCS2 characters -> bytes */
|
||||
pw.data = PORT_ZAlloc(ucs2_chars + 2);
|
||||
memcpy (pw.data, ucs2_password, ucs2_chars);
|
||||
pw.len = ucs2_chars + 2; /* include terminating NULL */
|
||||
pw.data = PORT_ZAlloc (ucs2_password.len + 2);
|
||||
memcpy (pw.data, ucs2_password.bin, ucs2_password.len);
|
||||
pw.len = ucs2_password.len + 2;
|
||||
|
||||
memset (ucs2_password, 0, ucs2_chars);
|
||||
g_free (ucs2_password);
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
{
|
||||
guint16 *p, *p_end;
|
||||
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
for (p = (guint16 *) pw.data; p < (guint16 *) (pw.data + pw.len); p++)
|
||||
*p = GUINT16_SWAP_LE_BE (*p);
|
||||
#endif /* WORDS_BIGENDIAN */
|
||||
} else {
|
||||
/* NULL password */
|
||||
pw.data = NULL;
|
||||
pw.len = 0;
|
||||
p_end = (guint16 *) &(((guint8 *) pw.data)[ucs2_password.len]);
|
||||
for (p = (guint16 *) pw.data; p < p_end; p++)
|
||||
*p = GUINT16_SWAP_LE_BE (*p);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
slot = PK11_GetInternalKeySlot ();
|
||||
if (!slot) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_FAILED,
|
||||
_("Couldn't initialize slot"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
slot = PK11_GetInternalKeySlot();
|
||||
p12ctx = SEC_PKCS12DecoderStart (&pw, slot, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (!p12ctx) {
|
||||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_FAILED,
|
||||
_("Couldn't initialize PKCS#12 decoder: %d"),
|
||||
PORT_GetError());
|
||||
goto error;
|
||||
PORT_GetError ());
|
||||
goto out;
|
||||
}
|
||||
|
||||
s = SEC_PKCS12DecoderUpdate (p12ctx, (guint8 *)data, data_len);
|
||||
|
|
@ -462,8 +477,8 @@ crypto_verify_pkcs12 (const guint8 *data,
|
|||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_INVALID_DATA,
|
||||
_("Couldn't decode PKCS#12 file: %d"),
|
||||
PORT_GetError());
|
||||
goto error;
|
||||
PORT_GetError ());
|
||||
goto out;
|
||||
}
|
||||
|
||||
s = SEC_PKCS12DecoderVerify (p12ctx);
|
||||
|
|
@ -471,35 +486,34 @@ crypto_verify_pkcs12 (const guint8 *data,
|
|||
g_set_error (error, NM_CRYPTO_ERROR,
|
||||
NM_CRYPTO_ERROR_DECRYPTION_FAILED,
|
||||
_("Couldn't verify PKCS#12 file: %d"),
|
||||
PORT_GetError());
|
||||
goto error;
|
||||
PORT_GetError ());
|
||||
goto out;
|
||||
}
|
||||
|
||||
SEC_PKCS12DecoderFinish (p12ctx);
|
||||
SECITEM_ZfreeItem (&pw, PR_FALSE);
|
||||
return TRUE;
|
||||
success = TRUE;
|
||||
|
||||
error:
|
||||
out:
|
||||
if (p12ctx)
|
||||
SEC_PKCS12DecoderFinish (p12ctx);
|
||||
|
||||
if (slot)
|
||||
PK11_FreeSlot(slot);
|
||||
PK11_FreeSlot (slot);
|
||||
|
||||
SECITEM_ZfreeItem (&pw, PR_FALSE);
|
||||
return FALSE;
|
||||
if (pw.data)
|
||||
SECITEM_ZfreeItem (&pw, PR_FALSE);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
gboolean
|
||||
crypto_verify_pkcs8 (const guint8 *data,
|
||||
gsize data_len,
|
||||
gboolean is_encrypted,
|
||||
const char *password,
|
||||
GError **error)
|
||||
_nm_crypto_verify_pkcs8 (const guint8 *data,
|
||||
gsize data_len,
|
||||
gboolean is_encrypted,
|
||||
const char *password,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (data != NULL, FALSE);
|
||||
|
||||
if (!crypto_init (error))
|
||||
if (!_nm_crypto_init (error))
|
||||
return FALSE;
|
||||
|
||||
/* NSS apparently doesn't do PKCS#8 natively, but you have to put the
|
||||
|
|
@ -510,11 +524,11 @@ crypto_verify_pkcs8 (const guint8 *data,
|
|||
}
|
||||
|
||||
gboolean
|
||||
crypto_randomize (void *buffer, gsize buffer_len, GError **error)
|
||||
_nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error)
|
||||
{
|
||||
SECStatus s;
|
||||
|
||||
if (!crypto_init (error))
|
||||
if (!_nm_crypto_init (error))
|
||||
return FALSE;
|
||||
|
||||
s = PK11_GenerateRandom (buffer, buffer_len);
|
||||
1035
libnm-core/nm-crypto.c
Normal file
1035
libnm-core/nm-crypto.c
Normal file
File diff suppressed because it is too large
Load diff
125
libnm-core/nm-crypto.h
Normal file
125
libnm-core/nm-crypto.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
|
||||
/*
|
||||
* Dan Williams <dcbw@redhat.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Copyright 2007 - 2014 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __NM_CRYPTO_H__
|
||||
#define __NM_CRYPTO_H__
|
||||
|
||||
#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
|
||||
#error Cannot use this header.
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
NM_CRYPTO_CIPHER_UNKNOWN,
|
||||
NM_CRYPTO_CIPHER_DES_EDE3_CBC,
|
||||
NM_CRYPTO_CIPHER_DES_CBC,
|
||||
NM_CRYPTO_CIPHER_AES_128_CBC,
|
||||
NM_CRYPTO_CIPHER_AES_192_CBC,
|
||||
NM_CRYPTO_CIPHER_AES_256_CBC,
|
||||
} NMCryptoCipherType;
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
NMCryptoCipherType cipher;
|
||||
guint8 digest_len;
|
||||
guint8 real_iv_len;
|
||||
} NMCryptoCipherInfo;
|
||||
|
||||
const NMCryptoCipherInfo *nm_crypto_cipher_get_info (NMCryptoCipherType cipher);
|
||||
const NMCryptoCipherInfo *nm_crypto_cipher_get_info_by_name (const char *cipher_name, gssize p_len);
|
||||
|
||||
typedef enum {
|
||||
NM_CRYPTO_KEY_TYPE_UNKNOWN = 0,
|
||||
NM_CRYPTO_KEY_TYPE_RSA,
|
||||
NM_CRYPTO_KEY_TYPE_DSA
|
||||
} NMCryptoKeyType;
|
||||
|
||||
typedef enum {
|
||||
NM_CRYPTO_FILE_FORMAT_UNKNOWN = 0,
|
||||
NM_CRYPTO_FILE_FORMAT_X509,
|
||||
NM_CRYPTO_FILE_FORMAT_RAW_KEY,
|
||||
NM_CRYPTO_FILE_FORMAT_PKCS12
|
||||
} NMCryptoFileFormat;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
GBytes *nm_crypto_read_file (const char *filename,
|
||||
GError **error);
|
||||
|
||||
gboolean nm_crypto_load_and_verify_certificate (const char *file,
|
||||
NMCryptoFileFormat *out_file_format,
|
||||
GBytes **out_certificat,
|
||||
GError **error);
|
||||
|
||||
gboolean nm_crypto_is_pkcs12_file (const char *file, GError **error);
|
||||
|
||||
gboolean nm_crypto_is_pkcs12_data (const guint8 *data, gsize len, GError **error);
|
||||
|
||||
NMCryptoFileFormat nm_crypto_verify_private_key_data (const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *password,
|
||||
gboolean *out_is_encrypted,
|
||||
GError **error);
|
||||
|
||||
NMCryptoFileFormat nm_crypto_verify_private_key (const char *file,
|
||||
const char *password,
|
||||
gboolean *out_is_encrypted,
|
||||
GError **error);
|
||||
|
||||
void nm_crypto_md5_hash (const guint8 *salt,
|
||||
gsize salt_len,
|
||||
const guint8 *password,
|
||||
gsize password_len,
|
||||
guint8 *buffer,
|
||||
gsize buflen);
|
||||
|
||||
gboolean nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
GBytes *nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data,
|
||||
gsize data_len,
|
||||
const char *password,
|
||||
NMCryptoKeyType *out_key_type,
|
||||
GError **error);
|
||||
|
||||
GBytes *nmtst_crypto_decrypt_openssl_private_key (const char *file,
|
||||
const char *password,
|
||||
NMCryptoKeyType *out_key_type,
|
||||
GError **error);
|
||||
|
||||
GBytes *nmtst_crypto_rsa_key_encrypt (const guint8 *data,
|
||||
gsize len,
|
||||
const char *in_password,
|
||||
char **out_password,
|
||||
GError **error);
|
||||
|
||||
guint8 *nmtst_crypto_make_des_aes_key (NMCryptoCipherType cipher,
|
||||
const guint8 *salt,
|
||||
gsize salt_len,
|
||||
const char *password,
|
||||
gsize *out_len,
|
||||
GError **error);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#endif /* __NM_CRYPTO_H__ */
|
||||
|
|
@ -33,6 +33,7 @@
|
|||
#include <string.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
|
||||
#include "nm-utils/nm-secret-utils.h"
|
||||
#include "nm-common-macros.h"
|
||||
#include "nm-core-internal.h"
|
||||
#include "nm-keyfile-utils.h"
|
||||
|
|
@ -939,7 +940,7 @@ unescape_semicolons (char *str)
|
|||
i++;
|
||||
str[j++] = str[i++];;
|
||||
}
|
||||
str[j] = '\0';
|
||||
nm_explicit_bzero (&str[j], i - j);
|
||||
return j;
|
||||
}
|
||||
|
||||
|
|
@ -950,9 +951,10 @@ get_bytes (KeyfileReaderInfo *info,
|
|||
gboolean zero_terminate,
|
||||
gboolean unescape_semicolon)
|
||||
{
|
||||
gs_free char *tmp_string = NULL;
|
||||
nm_auto_free_secret char *tmp_string = NULL;
|
||||
gboolean may_be_int_list = TRUE;
|
||||
gsize length;
|
||||
GBytes *result;
|
||||
|
||||
/* New format: just a string
|
||||
* Old format: integer list; e.g. 11;25;38;
|
||||
|
|
@ -969,7 +971,7 @@ get_bytes (KeyfileReaderInfo *info,
|
|||
* byte-array. The reason is that zero_terminate is there to terminate
|
||||
* *valid* strings. It's not there to terminated invalid (empty) strings.
|
||||
*/
|
||||
return g_bytes_new_take (tmp_string, 0);
|
||||
return g_bytes_new_static ("", 0);
|
||||
}
|
||||
|
||||
for (length = 0; tmp_string[length]; length++) {
|
||||
|
|
@ -986,12 +988,11 @@ get_bytes (KeyfileReaderInfo *info,
|
|||
|
||||
/* Try to parse the string as a integer list. */
|
||||
if (may_be_int_list && length > 0) {
|
||||
gs_free guint8 *bin_data = NULL;
|
||||
nm_auto_free_secret_buf NMSecretBuf *bin = NULL;
|
||||
const char *const s = tmp_string;
|
||||
gsize i, d;
|
||||
const gsize BIN_DATA_LEN = (length / 2 + 3);
|
||||
|
||||
bin_data = g_malloc (BIN_DATA_LEN);
|
||||
bin = nm_secret_buf_new (length / 2 + 3);
|
||||
|
||||
#define DIGIT(c) ((c) - '0')
|
||||
i = 0;
|
||||
|
|
@ -1024,8 +1025,8 @@ get_bytes (KeyfileReaderInfo *info,
|
|||
break;
|
||||
}
|
||||
|
||||
bin_data[d++] = n;
|
||||
nm_assert (d < BIN_DATA_LEN);
|
||||
nm_assert (d < bin->len);
|
||||
bin->bin[d++] = n;
|
||||
|
||||
/* allow whitespace after the digit. */
|
||||
while (g_ascii_isspace (s[i]))
|
||||
|
|
@ -1043,16 +1044,23 @@ get_bytes (KeyfileReaderInfo *info,
|
|||
* string format before. We expect that this conversion cannot fail. */
|
||||
if (d > 0) {
|
||||
/* note that @zero_terminate does not add a terminating '\0' to
|
||||
* binary data as an integer list.
|
||||
* binary data as an integer list. If the bytes are expressed as
|
||||
* an integer list, all potential NUL characters are supposed to
|
||||
* be included there explicitly.
|
||||
*
|
||||
* But we add a '\0' to the bin_data pointer, just to avoid somebody
|
||||
* (erronously!) reading the binary data as C-string.
|
||||
*
|
||||
* @d itself does not entail the '\0'. */
|
||||
nm_assert (d + 1 <= BIN_DATA_LEN);
|
||||
bin_data = g_realloc (bin_data, d + 1);
|
||||
bin_data[d] = '\0';
|
||||
return g_bytes_new_take (g_steal_pointer (&bin_data), d);
|
||||
* However, in the spirit of defensive programming, we do append a
|
||||
* NUL character to the buffer, although this character is hidden
|
||||
* and only a mitigation for bugs. */
|
||||
|
||||
if (d + 10 < bin->len) {
|
||||
/* hm, too much unused memory. Copy the memory to a suitable
|
||||
* sized buffer. */
|
||||
return nm_secret_copy_to_gbytes (bin->bin, d);
|
||||
}
|
||||
|
||||
nm_assert (d < bin->len);
|
||||
bin->bin[d] = '\0';
|
||||
return nm_secret_buf_to_gbytes_take (g_steal_pointer (&bin), d);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1063,8 +1071,13 @@ get_bytes (KeyfileReaderInfo *info,
|
|||
length++;
|
||||
if (length == 0)
|
||||
return NULL;
|
||||
tmp_string = g_realloc (tmp_string, length + (zero_terminate ? 0 : 1));
|
||||
return g_bytes_new_take (g_steal_pointer (&tmp_string), length);
|
||||
|
||||
result = g_bytes_new_with_free_func (tmp_string,
|
||||
length,
|
||||
(GDestroyNotify) nm_free_secret,
|
||||
tmp_string);
|
||||
tmp_string = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1108,12 +1121,12 @@ get_cert_path (const char *base_dir, const guint8 *cert_path, gsize cert_path_le
|
|||
g_return_val_if_fail (base_dir != NULL, NULL);
|
||||
g_return_val_if_fail (cert_path != NULL, NULL);
|
||||
|
||||
base = path = g_malloc0 (cert_path_len + 1);
|
||||
memcpy (path, cert_path, cert_path_len);
|
||||
path = g_strndup ((char *) cert_path, cert_path_len);
|
||||
|
||||
if (path[0] == '/')
|
||||
return path;
|
||||
|
||||
base = path;
|
||||
p = strrchr (path, '/');
|
||||
if (p)
|
||||
base = p + 1;
|
||||
|
|
@ -1147,8 +1160,9 @@ nm_keyfile_detect_unqualified_path_scheme (const char *base_dir,
|
|||
const char *data = pdata;
|
||||
gboolean exists = FALSE;
|
||||
gsize validate_len;
|
||||
gsize path_len, pathuri_len;
|
||||
gs_free char *path = NULL;
|
||||
GByteArray *tmp;
|
||||
gs_free char *pathuri = NULL;
|
||||
|
||||
g_return_val_if_fail (base_dir && base_dir[0] == '/', NULL);
|
||||
|
||||
|
|
@ -1191,18 +1205,16 @@ nm_keyfile_detect_unqualified_path_scheme (const char *base_dir,
|
|||
* When returning TRUE, we must also be sure that @data_len does not look like
|
||||
* the deprecated format of list of integers. With this implementation that is the
|
||||
* case, as long as @consider_exists is FALSE. */
|
||||
tmp = g_byte_array_sized_new (strlen (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH) + strlen (path) + 1);
|
||||
g_byte_array_append (tmp, (const guint8 *) NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, strlen (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH));
|
||||
g_byte_array_append (tmp, (const guint8 *) path, strlen (path) + 1);
|
||||
if (nm_setting_802_1x_check_cert_scheme (tmp->data, tmp->len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PATH) {
|
||||
g_byte_array_unref (tmp);
|
||||
path_len = strlen (path);
|
||||
pathuri_len = (NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH) + 1) + path_len;
|
||||
pathuri = g_new (char, pathuri_len);
|
||||
memcpy (pathuri, NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH));
|
||||
memcpy (&pathuri[NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)], path, path_len + 1);
|
||||
if (nm_setting_802_1x_check_cert_scheme (pathuri, pathuri_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PATH)
|
||||
return NULL;
|
||||
}
|
||||
g_free (path);
|
||||
path = (char *) g_byte_array_free (tmp, FALSE);
|
||||
|
||||
NM_SET_OUT (out_exists, exists);
|
||||
return g_steal_pointer (&path);
|
||||
return g_steal_pointer (&pathuri);
|
||||
}
|
||||
|
||||
#define HAS_SCHEME_PREFIX(bin, bin_len, scheme) \
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -25,6 +25,8 @@
|
|||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nm-utils/nm-secret-utils.h"
|
||||
|
||||
#include "nm-setting-vpn.h"
|
||||
#include "nm-utils.h"
|
||||
#include "nm-utils-private.h"
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
#include "nm-common-macros.h"
|
||||
#include "nm-utils-private.h"
|
||||
#include "nm-setting-private.h"
|
||||
#include "crypto.h"
|
||||
#include "nm-crypto.h"
|
||||
#include "nm-setting-bond.h"
|
||||
#include "nm-setting-bridge.h"
|
||||
#include "nm-setting-infiniband.h"
|
||||
|
|
@ -2838,9 +2838,17 @@ nm_utils_uuid_generate_from_string (const char *s, gssize slen, int uuid_type, g
|
|||
g_return_val_if_fail (uuid_type == NM_UTILS_UUID_TYPE_LEGACY || uuid_type == NM_UTILS_UUID_TYPE_VARIANT3, NULL);
|
||||
g_return_val_if_fail (!type_args || uuid_type == NM_UTILS_UUID_TYPE_VARIANT3, NULL);
|
||||
|
||||
if (slen < 0)
|
||||
slen = s ? strlen (s) : 0;
|
||||
|
||||
switch (uuid_type) {
|
||||
case NM_UTILS_UUID_TYPE_LEGACY:
|
||||
crypto_md5_hash (NULL, 0, s, slen, (char *) uuid, sizeof (uuid));
|
||||
nm_crypto_md5_hash (NULL,
|
||||
0,
|
||||
(guint8 *) s,
|
||||
slen,
|
||||
(guint8 *) uuid,
|
||||
sizeof (uuid));
|
||||
break;
|
||||
case NM_UTILS_UUID_TYPE_VARIANT3: {
|
||||
uuid_t ns_uuid = { 0 };
|
||||
|
|
@ -2851,7 +2859,12 @@ nm_utils_uuid_generate_from_string (const char *s, gssize slen, int uuid_type, g
|
|||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
|
||||
crypto_md5_hash (s, slen, (char *) ns_uuid, sizeof (ns_uuid), (char *) uuid, sizeof (uuid));
|
||||
nm_crypto_md5_hash ((guint8 *) s,
|
||||
slen,
|
||||
(guint8 *) ns_uuid,
|
||||
sizeof (ns_uuid),
|
||||
(guint8 *) uuid,
|
||||
sizeof (uuid));
|
||||
|
||||
uuid[6] = (uuid[6] & 0x0F) | 0x30;
|
||||
uuid[8] = (uuid[8] & 0x3F) | 0x80;
|
||||
|
|
@ -2911,110 +2924,6 @@ _nm_utils_uuid_generate_from_strings (const char *string1, ...)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
* nm_utils_rsa_key_encrypt:
|
||||
* @data: (array length=len): RSA private key data to be encrypted
|
||||
* @len: length of @data
|
||||
* @in_password: (allow-none): existing password to use, if any
|
||||
* @out_password: (out) (allow-none): if @in_password was %NULL, a random
|
||||
* password will be generated and returned in this argument
|
||||
* @error: detailed error information on return, if an error occurred
|
||||
*
|
||||
* Encrypts the given RSA private key data with the given password (or generates
|
||||
* a password if no password was given) and converts the data to PEM format
|
||||
* suitable for writing to a file. It uses Triple DES cipher for the encryption.
|
||||
*
|
||||
* Returns: (transfer full): on success, PEM-formatted data suitable for writing
|
||||
* to a PEM-formatted certificate/private key file.
|
||||
**/
|
||||
GByteArray *
|
||||
nm_utils_rsa_key_encrypt (const guint8 *data,
|
||||
gsize len,
|
||||
const char *in_password,
|
||||
char **out_password,
|
||||
GError **error)
|
||||
{
|
||||
char salt[16];
|
||||
int salt_len;
|
||||
char *key = NULL, *enc = NULL, *pw_buf[32];
|
||||
gsize key_len = 0, enc_len = 0;
|
||||
GString *pem = NULL;
|
||||
char *tmp, *tmp_password = NULL;
|
||||
int left;
|
||||
const char *p;
|
||||
GByteArray *ret = NULL;
|
||||
|
||||
g_return_val_if_fail (data != NULL, NULL);
|
||||
g_return_val_if_fail (len > 0, NULL);
|
||||
if (out_password)
|
||||
g_return_val_if_fail (*out_password == NULL, NULL);
|
||||
|
||||
/* Make the password if needed */
|
||||
if (!in_password) {
|
||||
if (!crypto_randomize (pw_buf, sizeof (pw_buf), error))
|
||||
return NULL;
|
||||
in_password = tmp_password = nm_utils_bin2hexstr (pw_buf, sizeof (pw_buf), -1);
|
||||
}
|
||||
|
||||
salt_len = 8;
|
||||
if (!crypto_randomize (salt, salt_len, error))
|
||||
goto out;
|
||||
|
||||
key = crypto_make_des_aes_key (CIPHER_DES_EDE3_CBC, &salt[0], salt_len, in_password, &key_len, NULL);
|
||||
if (!key)
|
||||
g_return_val_if_reached (NULL);
|
||||
|
||||
enc = crypto_encrypt (CIPHER_DES_EDE3_CBC, data, len, salt, salt_len, key, key_len, &enc_len, error);
|
||||
if (!enc)
|
||||
goto out;
|
||||
|
||||
pem = g_string_sized_new (enc_len * 2 + 100);
|
||||
g_string_append (pem, "-----BEGIN RSA PRIVATE KEY-----\n");
|
||||
g_string_append (pem, "Proc-Type: 4,ENCRYPTED\n");
|
||||
|
||||
/* Convert the salt to a hex string */
|
||||
tmp = nm_utils_bin2hexstr (salt, salt_len, salt_len * 2);
|
||||
g_string_append_printf (pem, "DEK-Info: %s,%s\n\n", CIPHER_DES_EDE3_CBC, tmp);
|
||||
g_free (tmp);
|
||||
|
||||
/* Convert the encrypted key to a base64 string */
|
||||
p = tmp = g_base64_encode ((const guchar *) enc, enc_len);
|
||||
left = strlen (tmp);
|
||||
while (left > 0) {
|
||||
g_string_append_len (pem, p, (left < 64) ? left : 64);
|
||||
g_string_append_c (pem, '\n');
|
||||
left -= 64;
|
||||
p += 64;
|
||||
}
|
||||
g_free (tmp);
|
||||
|
||||
g_string_append (pem, "-----END RSA PRIVATE KEY-----\n");
|
||||
|
||||
ret = g_byte_array_sized_new (pem->len);
|
||||
g_byte_array_append (ret, (const unsigned char *) pem->str, pem->len);
|
||||
if (tmp_password && out_password)
|
||||
*out_password = g_strdup (tmp_password);
|
||||
|
||||
out:
|
||||
if (key) {
|
||||
memset (key, 0, key_len);
|
||||
g_free (key);
|
||||
}
|
||||
if (enc) {
|
||||
memset (enc, 0, enc_len);
|
||||
g_free (enc);
|
||||
}
|
||||
if (pem)
|
||||
g_string_free (pem, TRUE);
|
||||
|
||||
if (tmp_password) {
|
||||
memset (tmp_password, 0, strlen (tmp_password));
|
||||
g_free (tmp_password);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
file_has_extension (const char *filename, const char *extensions[])
|
||||
{
|
||||
|
|
@ -3047,18 +2956,15 @@ gboolean
|
|||
nm_utils_file_is_certificate (const char *filename)
|
||||
{
|
||||
const char *extensions[] = { ".der", ".pem", ".crt", ".cer", NULL };
|
||||
NMCryptoFileFormat file_format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
GByteArray *cert;
|
||||
NMCryptoFileFormat file_format;
|
||||
|
||||
g_return_val_if_fail (filename != NULL, FALSE);
|
||||
|
||||
if (!file_has_extension (filename, extensions))
|
||||
return FALSE;
|
||||
|
||||
cert = crypto_load_and_verify_certificate (filename, &file_format, NULL);
|
||||
if (cert)
|
||||
g_byte_array_unref (cert);
|
||||
|
||||
if (!nm_crypto_load_and_verify_certificate (filename, &file_format, NULL, NULL))
|
||||
return FALSE;
|
||||
return file_format = NM_CRYPTO_FILE_FORMAT_X509;
|
||||
}
|
||||
|
||||
|
|
@ -3084,7 +2990,7 @@ nm_utils_file_is_private_key (const char *filename, gboolean *out_encrypted)
|
|||
if (!file_has_extension (filename, extensions))
|
||||
return FALSE;
|
||||
|
||||
return crypto_verify_private_key (filename, NULL, out_encrypted, NULL) != NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
return nm_crypto_verify_private_key (filename, NULL, out_encrypted, NULL) != NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -3100,7 +3006,7 @@ nm_utils_file_is_pkcs12 (const char *filename)
|
|||
{
|
||||
g_return_val_if_fail (filename != NULL, FALSE);
|
||||
|
||||
return crypto_is_pkcs12_file (filename, NULL);
|
||||
return nm_crypto_is_pkcs12_file (filename, NULL);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
@ -3620,13 +3526,13 @@ nm_utils_hwaddr_len (int type)
|
|||
g_return_val_if_reached (0);
|
||||
}
|
||||
|
||||
static guint8 *
|
||||
_str2bin (const char *asc,
|
||||
gboolean delimiter_required,
|
||||
const char *delimiter_candidates,
|
||||
guint8 *buffer,
|
||||
gsize buffer_length,
|
||||
gsize *out_len)
|
||||
guint8 *
|
||||
_nm_utils_str2bin_full (const char *asc,
|
||||
gboolean delimiter_required,
|
||||
const char *delimiter_candidates,
|
||||
guint8 *buffer,
|
||||
gsize buffer_length,
|
||||
gsize *out_len)
|
||||
{
|
||||
const char *in = asc;
|
||||
guint8 *out = buffer;
|
||||
|
|
@ -3696,7 +3602,7 @@ _str2bin (const char *asc,
|
|||
return buffer;
|
||||
}
|
||||
|
||||
#define hwaddr_aton(asc, buffer, buffer_length, out_len) _str2bin ((asc), TRUE, ":-", (buffer), (buffer_length), (out_len))
|
||||
#define hwaddr_aton(asc, buffer, buffer_length, out_len) _nm_utils_str2bin_full ((asc), TRUE, ":-", (buffer), (buffer_length), (out_len))
|
||||
|
||||
/**
|
||||
* nm_utils_hexstr2bin:
|
||||
|
|
@ -3722,7 +3628,7 @@ nm_utils_hexstr2bin (const char *hex)
|
|||
|
||||
buffer_length = strlen (hex) / 2 + 3;
|
||||
buffer = g_malloc (buffer_length);
|
||||
if (!_str2bin (hex, FALSE, ":", buffer, buffer_length, &len)) {
|
||||
if (!_nm_utils_str2bin_full (hex, FALSE, ":", buffer, buffer_length, &len)) {
|
||||
g_free (buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -4602,7 +4508,7 @@ _nm_utils_dhcp_duid_valid (const char *duid, GBytes **out_duid_bin)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
if (_str2bin (duid, FALSE, ":", duid_arr, sizeof (duid_arr), &duid_len)) {
|
||||
if (_nm_utils_str2bin_full (duid, FALSE, ":", duid_arr, sizeof (duid_arr), &duid_len)) {
|
||||
/* MAX DUID length is 128 octects + the type code (2 octects). */
|
||||
if ( duid_len > 2
|
||||
&& duid_len <= (128 + 2)) {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "crypto.h"
|
||||
#include "nm-crypto-impl.h"
|
||||
#include "nm-utils.h"
|
||||
#include "nm-errors.h"
|
||||
#include "nm-core-internal.h"
|
||||
|
|
@ -99,37 +99,20 @@ static void
|
|||
test_cert (gconstpointer test_data)
|
||||
{
|
||||
gs_free char *path = NULL;
|
||||
GByteArray *array;
|
||||
gs_unref_bytes GBytes *cert = NULL;
|
||||
NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
|
||||
GError *error = NULL;
|
||||
gboolean success;
|
||||
|
||||
path = g_build_filename (TEST_CERT_DIR, (const char *) test_data, NULL);
|
||||
|
||||
array = crypto_load_and_verify_certificate (path, &format, &error);
|
||||
g_assert_no_error (error);
|
||||
success = nm_crypto_load_and_verify_certificate (path, &format, &cert, &error);
|
||||
nmtst_assert_success (success, error);
|
||||
g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_X509);
|
||||
|
||||
g_byte_array_free (array, TRUE);
|
||||
|
||||
g_assert (nm_utils_file_is_certificate (path));
|
||||
}
|
||||
|
||||
static GByteArray *
|
||||
file_to_byte_array (const char *filename)
|
||||
{
|
||||
char *contents;
|
||||
GByteArray *array = NULL;
|
||||
gsize length = 0;
|
||||
|
||||
if (g_file_get_contents (filename, &contents, &length, NULL)) {
|
||||
array = g_byte_array_sized_new (length);
|
||||
g_byte_array_append (array, (guint8 *) contents, length);
|
||||
g_assert (array->len == length);
|
||||
g_free (contents);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
static void
|
||||
test_load_private_key (const char *path,
|
||||
const char *password,
|
||||
|
|
@ -138,13 +121,13 @@ test_load_private_key (const char *path,
|
|||
{
|
||||
NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
|
||||
gboolean is_encrypted = FALSE;
|
||||
GByteArray *array, *decrypted;
|
||||
gs_unref_bytes GBytes *array = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
g_assert (nm_utils_file_is_private_key (path, &is_encrypted));
|
||||
g_assert (is_encrypted);
|
||||
|
||||
array = crypto_decrypt_openssl_private_key (path, password, &key_type, &error);
|
||||
array = nmtst_crypto_decrypt_openssl_private_key (path, password, &key_type, &error);
|
||||
/* Even if the password is wrong, we should determine the key type */
|
||||
g_assert_cmpint (key_type, ==, NM_CRYPTO_KEY_TYPE_RSA);
|
||||
|
||||
|
|
@ -164,16 +147,14 @@ test_load_private_key (const char *path,
|
|||
g_assert (array != NULL);
|
||||
|
||||
if (decrypted_path) {
|
||||
gs_free char *contents = NULL;
|
||||
gsize length;
|
||||
|
||||
/* Compare the crypto decrypted key against a known-good decryption */
|
||||
decrypted = file_to_byte_array (decrypted_path);
|
||||
g_assert (decrypted != NULL);
|
||||
g_assert (decrypted->len == array->len);
|
||||
g_assert (memcmp (decrypted->data, array->data, array->len) == 0);
|
||||
|
||||
g_byte_array_free (decrypted, TRUE);
|
||||
if (!g_file_get_contents (decrypted_path, &contents, &length, NULL))
|
||||
g_assert_not_reached ();
|
||||
g_assert (nm_utils_gbytes_equal_mem (array, contents, length));
|
||||
}
|
||||
|
||||
g_byte_array_free (array, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -187,7 +168,7 @@ test_load_pkcs12 (const char *path,
|
|||
|
||||
g_assert (nm_utils_file_is_private_key (path, NULL));
|
||||
|
||||
format = crypto_verify_private_key (path, password, &is_encrypted, &error);
|
||||
format = nm_crypto_verify_private_key (path, password, &is_encrypted, &error);
|
||||
if (expected_error != -1) {
|
||||
g_assert_error (error, NM_CRYPTO_ERROR, expected_error);
|
||||
g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
|
||||
|
|
@ -209,7 +190,7 @@ test_load_pkcs12_no_password (const char *path)
|
|||
g_assert (nm_utils_file_is_private_key (path, NULL));
|
||||
|
||||
/* We should still get a valid returned crypto file format */
|
||||
format = crypto_verify_private_key (path, NULL, &is_encrypted, &error);
|
||||
format = nm_crypto_verify_private_key (path, NULL, &is_encrypted, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_PKCS12);
|
||||
g_assert (is_encrypted);
|
||||
|
|
@ -221,7 +202,7 @@ test_is_pkcs12 (const char *path, gboolean expect_fail)
|
|||
gboolean is_pkcs12;
|
||||
GError *error = NULL;
|
||||
|
||||
is_pkcs12 = crypto_is_pkcs12_file (path, &error);
|
||||
is_pkcs12 = nm_crypto_is_pkcs12_file (path, &error);
|
||||
|
||||
if (expect_fail) {
|
||||
g_assert_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA);
|
||||
|
|
@ -244,7 +225,7 @@ test_load_pkcs8 (const char *path,
|
|||
|
||||
g_assert (nm_utils_file_is_private_key (path, NULL));
|
||||
|
||||
format = crypto_verify_private_key (path, password, &is_encrypted, &error);
|
||||
format = nm_crypto_verify_private_key (path, password, &is_encrypted, &error);
|
||||
if (expected_error != -1) {
|
||||
g_assert_error (error, NM_CRYPTO_ERROR, expected_error);
|
||||
g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
|
||||
|
|
@ -261,34 +242,35 @@ test_encrypt_private_key (const char *path,
|
|||
const char *password)
|
||||
{
|
||||
NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
|
||||
GByteArray *array, *encrypted, *re_decrypted;
|
||||
gs_unref_bytes GBytes *array = NULL;
|
||||
gs_unref_bytes GBytes *encrypted = NULL;
|
||||
gs_unref_bytes GBytes *re_decrypted = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
array = crypto_decrypt_openssl_private_key (path, password, &key_type, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert (array != NULL);
|
||||
array = nmtst_crypto_decrypt_openssl_private_key (path, password, &key_type, &error);
|
||||
nmtst_assert_success (array, error);
|
||||
g_assert_cmpint (key_type, ==, NM_CRYPTO_KEY_TYPE_RSA);
|
||||
|
||||
/* Now re-encrypt the private key */
|
||||
encrypted = nm_utils_rsa_key_encrypt (array->data, array->len, password, NULL, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert (encrypted != NULL);
|
||||
encrypted = nmtst_crypto_rsa_key_encrypt (g_bytes_get_data (array, NULL),
|
||||
g_bytes_get_size (array),
|
||||
password,
|
||||
NULL,
|
||||
&error);
|
||||
nmtst_assert_success (encrypted, error);
|
||||
|
||||
/* Then re-decrypt the private key */
|
||||
key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
|
||||
re_decrypted = crypto_decrypt_openssl_private_key_data (encrypted->data, encrypted->len,
|
||||
password, &key_type, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert (re_decrypted != NULL);
|
||||
re_decrypted = nmtst_crypto_decrypt_openssl_private_key_data (g_bytes_get_data (encrypted, NULL),
|
||||
g_bytes_get_size (encrypted),
|
||||
password,
|
||||
&key_type,
|
||||
&error);
|
||||
nmtst_assert_success (re_decrypted, error);
|
||||
g_assert_cmpint (key_type, ==, NM_CRYPTO_KEY_TYPE_RSA);
|
||||
|
||||
/* Compare the original decrypted key with the re-decrypted key */
|
||||
g_assert_cmpint (array->len, ==, re_decrypted->len);
|
||||
g_assert (!memcmp (array->data, re_decrypted->data, array->len));
|
||||
|
||||
g_byte_array_free (re_decrypted, TRUE);
|
||||
g_byte_array_free (encrypted, TRUE);
|
||||
g_byte_array_free (array, TRUE);
|
||||
g_assert (g_bytes_equal (array, re_decrypted));
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -417,15 +399,16 @@ test_md5 (void)
|
|||
|
||||
for (i = 0; i < G_N_ELEMENTS (md5_tests); i++) {
|
||||
memset (digest, 0, sizeof (digest));
|
||||
crypto_md5_hash (md5_tests[i].salt,
|
||||
/* crypto_md5_hash() used to clamp salt_len to 8. It
|
||||
* doesn't any more, so we need to do it here now to
|
||||
* get output that matches md5_tests[i].result.
|
||||
*/
|
||||
md5_tests[i].salt ? 8 : 0,
|
||||
md5_tests[i].password,
|
||||
strlen (md5_tests[i].password),
|
||||
digest, md5_tests[i].digest_size);
|
||||
nm_crypto_md5_hash ((const guint8 *) md5_tests[i].salt,
|
||||
/* nm_crypto_md5_hash() used to clamp salt_len to 8. It
|
||||
* doesn't any more, so we need to do it here now to
|
||||
* get output that matches md5_tests[i].result.
|
||||
*/
|
||||
md5_tests[i].salt ? 8 : 0,
|
||||
(const guint8 *) md5_tests[i].password,
|
||||
strlen (md5_tests[i].password),
|
||||
(guint8 *) digest,
|
||||
md5_tests[i].digest_size);
|
||||
|
||||
hex = nm_utils_bin2hexstr (digest, md5_tests[i].digest_size, -1);
|
||||
g_assert_cmpstr (hex, ==, md5_tests[i].result);
|
||||
|
|
@ -444,7 +427,7 @@ main (int argc, char **argv)
|
|||
|
||||
nmtst_init (&argc, &argv, TRUE);
|
||||
|
||||
success = crypto_init (&error);
|
||||
success = _nm_crypto_init (&error);
|
||||
g_assert_no_error (error);
|
||||
g_assert (success);
|
||||
|
||||
|
|
|
|||
|
|
@ -67,10 +67,12 @@ static void
|
|||
check_scheme_path (GBytes *value, const char *path)
|
||||
{
|
||||
const guint8 *p;
|
||||
gsize l;
|
||||
|
||||
g_assert (value);
|
||||
|
||||
p = g_bytes_get_data (value, NULL);
|
||||
p = g_bytes_get_data (value, &l);
|
||||
g_assert_cmpint (l, ==, strlen (path) + NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + 1);
|
||||
g_assert (memcmp (p, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)) == 0);
|
||||
p += strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
|
||||
g_assert (memcmp (p, path, strlen (path)) == 0);
|
||||
|
|
|
|||
|
|
@ -442,9 +442,6 @@ crypto_verify_pkcs12 (const GByteArray *data,
|
|||
SECStatus s;
|
||||
char *ucs2_password;
|
||||
long ucs2_chars = 0;
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
guint16 *p;
|
||||
#endif /* WORDS_BIGENDIAN */
|
||||
|
||||
if (error)
|
||||
g_return_val_if_fail (*error == NULL, FALSE);
|
||||
|
|
@ -470,10 +467,14 @@ crypto_verify_pkcs12 (const GByteArray *data,
|
|||
memset (ucs2_password, 0, ucs2_chars);
|
||||
g_free (ucs2_password);
|
||||
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
for (p = (guint16 *) pw.data; p < (guint16 *) (pw.data + pw.len); p++)
|
||||
*p = GUINT16_SWAP_LE_BE (*p);
|
||||
#endif /* WORDS_BIGENDIAN */
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
{
|
||||
guint16 *p;
|
||||
|
||||
for (p = (guint16 *) pw.data; p < (guint16 *) (pw.data + pw.len); p++)
|
||||
*p = GUINT16_SWAP_LE_BE (*p);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
/* NULL password */
|
||||
pw.data = NULL;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nm-utils/nm-secret-utils.h"
|
||||
#include "nm-enum-types.h"
|
||||
#include "nm-utils.h"
|
||||
#include "nm-connection.h"
|
||||
|
|
|
|||
13
meson.build
13
meson.build
|
|
@ -470,12 +470,16 @@ if enable_polkit_agent
|
|||
endif
|
||||
config_h.set10('WITH_POLKIT_AGENT', enable_polkit_agent)
|
||||
|
||||
# crypto
|
||||
crypto_gnutls_dep = dependency('gnutls', version: '>= 2.12', required: false)
|
||||
crypto_nss_dep = dependency('nss', required: false)
|
||||
|
||||
crypto = get_option('crypto')
|
||||
if crypto == 'nss'
|
||||
crypto_dep = dependency('nss')
|
||||
assert(crypto_nss_dep.found(), 'Requires gnutls crypto support')
|
||||
elif crypto == 'gnutls'
|
||||
assert(crypto_gnutls_dep.found(), 'Requires gnutls crypto support')
|
||||
else
|
||||
crypto_dep = dependency('gnutls', version: '>= 2.12')
|
||||
error('bug')
|
||||
endif
|
||||
|
||||
dbus_conf_dir = get_option('dbus_conf_dir')
|
||||
|
|
@ -1022,7 +1026,8 @@ output += '\n'
|
|||
output += ' code coverage: ' + get_option('b_coverage').to_string() + '\n'
|
||||
output += ' LTO: ' + get_option('b_lto').to_string() + '\n'
|
||||
output += ' Linker garbage collection: ' + enable_ld_gc.to_string() + '\n'
|
||||
output += ' JSON validation for libnm: ' + enable_json_validation.to_string () + '\n'
|
||||
output += ' JSON validation for libnm: ' + enable_json_validation.to_string() + '\n'
|
||||
output += ' crypto: ' + crypto + ' (have-gnutls: ' + crypto_gnutls_dep.found().to_string() + ', have-nss: ' + crypto_nss_dep.found().to_string() + ')\n'
|
||||
output += ' sanitizers: ' + get_option('b_sanitize') + '\n'
|
||||
output += ' Mozilla Public Suffix List: ' + enable_libpsl.to_string() + '\n'
|
||||
message(output)
|
||||
|
|
|
|||
|
|
@ -51,9 +51,9 @@ clients/tui/nmtui-connect.c
|
|||
clients/tui/nmtui-edit.c
|
||||
clients/tui/nmtui-hostname.c
|
||||
clients/tui/nmtui.c
|
||||
libnm-core/crypto.c
|
||||
libnm-core/crypto_gnutls.c
|
||||
libnm-core/crypto_nss.c
|
||||
libnm-core/nm-crypto.c
|
||||
libnm-core/nm-crypto-gnutls.c
|
||||
libnm-core/nm-crypto-nss.c
|
||||
libnm-core/nm-connection.c
|
||||
libnm-core/nm-dbus-utils.c
|
||||
libnm-core/nm-keyfile.c
|
||||
|
|
|
|||
|
|
@ -50,7 +50,9 @@ shared_files_libnm_core = files('''
|
|||
nm-utils/nm-dedup-multi.c
|
||||
nm-utils/nm-enum-utils.c
|
||||
nm-utils/nm-hash-utils.c
|
||||
nm-utils/nm-io-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())
|
||||
|
|
|
|||
430
shared/nm-utils/nm-io-utils.c
Normal file
430
shared/nm-utils/nm-io-utils.c
Normal file
|
|
@ -0,0 +1,430 @@
|
|||
/* -*- 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-io-utils.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "nm-shared-utils.h"
|
||||
#include "nm-secret-utils.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
_nm_printf (3, 4)
|
||||
static int
|
||||
_get_contents_error (GError **error, int errsv, const char *format, ...)
|
||||
{
|
||||
if (errsv < 0)
|
||||
errsv = -errsv;
|
||||
else if (!errsv)
|
||||
errsv = errno;
|
||||
|
||||
if (error) {
|
||||
char *msg;
|
||||
va_list args;
|
||||
|
||||
va_start (args, format);
|
||||
msg = g_strdup_vprintf (format, args);
|
||||
va_end (args);
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errsv),
|
||||
"%s: %s",
|
||||
msg, g_strerror (errsv));
|
||||
g_free (msg);
|
||||
}
|
||||
return -errsv;
|
||||
}
|
||||
|
||||
static char *
|
||||
_mem_realloc (char *old, gboolean do_bzero_mem, gsize cur_len, gsize new_len)
|
||||
{
|
||||
char *new;
|
||||
|
||||
/* re-allocating to zero bytes is an odd case. We don't need it
|
||||
* and it's not supported. */
|
||||
nm_assert (new_len > 0);
|
||||
|
||||
/* regardless of success/failure, @old will always be freed/consumed. */
|
||||
|
||||
if (do_bzero_mem && cur_len > 0) {
|
||||
new = g_try_malloc (new_len);
|
||||
if (new)
|
||||
memcpy (new, old, NM_MIN (cur_len, new_len));
|
||||
nm_explicit_bzero (old, cur_len);
|
||||
g_free (old);
|
||||
} else {
|
||||
new = g_try_realloc (old, new_len);
|
||||
if (!new)
|
||||
g_free (old);
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_utils_fd_get_contents:
|
||||
* @fd: open file descriptor to read. The fd will not be closed,
|
||||
* but don't rely on its state afterwards.
|
||||
* @close_fd: if %TRUE, @fd will be closed by the function.
|
||||
* Passing %TRUE here might safe a syscall for dup().
|
||||
* @max_length: allocate at most @max_length bytes. If the
|
||||
* file is larger, reading will fail. Set to zero to use
|
||||
* a very large default.
|
||||
* WARNING: @max_length is here to avoid a crash for huge/unlimited files.
|
||||
* For example, stat(/sys/class/net/enp0s25/ifindex) gives a filesize of
|
||||
* 4K, although the actual real is small. @max_length is the memory
|
||||
* allocated in the process of reading the file, thus it must be at least
|
||||
* the size reported by fstat.
|
||||
* If you set it to 1K, read will fail because fstat() claims the
|
||||
* file is larger.
|
||||
* @flags: %NMUtilsFileGetContentsFlags for reading the file.
|
||||
* @contents: the output buffer with the file read. It is always
|
||||
* NUL terminated. The buffer is at most @max_length long, including
|
||||
* the NUL byte. That is, it reads only files up to a length of
|
||||
* @max_length - 1 bytes.
|
||||
* @length: optional output argument of the read file size.
|
||||
*
|
||||
* A reimplementation of g_file_get_contents() with a few differences:
|
||||
* - accepts an open fd, instead of a path name. This allows you to
|
||||
* use openat().
|
||||
* - limits the maxium filesize to max_length.
|
||||
*
|
||||
* Returns: a negative error code on failure.
|
||||
*/
|
||||
int
|
||||
nm_utils_fd_get_contents (int fd,
|
||||
gboolean close_fd,
|
||||
gsize max_length,
|
||||
NMUtilsFileGetContentsFlags flags,
|
||||
char **contents,
|
||||
gsize *length,
|
||||
GError **error)
|
||||
{
|
||||
nm_auto_close int fd_keeper = close_fd ? fd : -1;
|
||||
struct stat stat_buf;
|
||||
gs_free char *str = NULL;
|
||||
const bool do_bzero_mem = NM_FLAGS_HAS (flags, NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET);
|
||||
|
||||
g_return_val_if_fail (fd >= 0, -EINVAL);
|
||||
g_return_val_if_fail (contents, -EINVAL);
|
||||
g_return_val_if_fail (!error || !*error, -EINVAL);
|
||||
|
||||
if (fstat (fd, &stat_buf) < 0)
|
||||
return _get_contents_error (error, 0, "failure during fstat");
|
||||
|
||||
if (!max_length) {
|
||||
/* default to a very large size, but not extreme */
|
||||
max_length = 2 * 1024 * 1024;
|
||||
}
|
||||
|
||||
if ( stat_buf.st_size > 0
|
||||
&& S_ISREG (stat_buf.st_mode)) {
|
||||
const gsize n_stat = stat_buf.st_size;
|
||||
ssize_t n_read;
|
||||
|
||||
if (n_stat > max_length - 1)
|
||||
return _get_contents_error (error, EMSGSIZE, "file too large (%zu+1 bytes with maximum %zu bytes)", n_stat, max_length);
|
||||
|
||||
str = g_try_malloc (n_stat + 1);
|
||||
if (!str)
|
||||
return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu+1 bytes", n_stat);
|
||||
|
||||
n_read = nm_utils_fd_read_loop (fd, str, n_stat, TRUE);
|
||||
if (n_read < 0) {
|
||||
if (do_bzero_mem)
|
||||
nm_explicit_bzero (str, n_stat);
|
||||
return _get_contents_error (error, n_read, "error reading %zu bytes from file descriptor", n_stat);
|
||||
}
|
||||
str[n_read] = '\0';
|
||||
|
||||
if (n_read < n_stat) {
|
||||
if (!(str = _mem_realloc (str, do_bzero_mem, n_stat + 1, n_read + 1)))
|
||||
return _get_contents_error (error, ENOMEM, "failure to reallocate buffer with %zu bytes", n_read + 1);
|
||||
}
|
||||
NM_SET_OUT (length, n_read);
|
||||
} else {
|
||||
nm_auto_fclose FILE *f = NULL;
|
||||
char buf[4096];
|
||||
gsize n_have, n_alloc;
|
||||
int fd2;
|
||||
|
||||
if (fd_keeper >= 0)
|
||||
fd2 = nm_steal_fd (&fd_keeper);
|
||||
else {
|
||||
fd2 = fcntl (fd, F_DUPFD_CLOEXEC, 0);
|
||||
if (fd2 < 0)
|
||||
return _get_contents_error (error, 0, "error during dup");
|
||||
}
|
||||
|
||||
if (!(f = fdopen (fd2, "r"))) {
|
||||
nm_close (fd2);
|
||||
return _get_contents_error (error, 0, "failure during fdopen");
|
||||
}
|
||||
|
||||
n_have = 0;
|
||||
n_alloc = 0;
|
||||
|
||||
while (!feof (f)) {
|
||||
int errsv;
|
||||
gsize n_read;
|
||||
|
||||
n_read = fread (buf, 1, sizeof (buf), f);
|
||||
errsv = errno;
|
||||
if (ferror (f)) {
|
||||
if (do_bzero_mem)
|
||||
nm_explicit_bzero (buf, sizeof (buf));
|
||||
return _get_contents_error (error, errsv, "error during fread");
|
||||
}
|
||||
|
||||
if ( n_have > G_MAXSIZE - 1 - n_read
|
||||
|| n_have + n_read + 1 > max_length) {
|
||||
if (do_bzero_mem)
|
||||
nm_explicit_bzero (buf, sizeof (buf));
|
||||
return _get_contents_error (error, EMSGSIZE, "file stream too large (%zu+1 bytes with maximum %zu bytes)",
|
||||
(n_have > G_MAXSIZE - 1 - n_read) ? G_MAXSIZE : n_have + n_read,
|
||||
max_length);
|
||||
}
|
||||
|
||||
if (n_have + n_read + 1 >= n_alloc) {
|
||||
gsize old_n_alloc = n_alloc;
|
||||
|
||||
if (n_alloc != 0) {
|
||||
nm_assert (str);
|
||||
if (n_alloc >= max_length / 2)
|
||||
n_alloc = max_length;
|
||||
else
|
||||
n_alloc *= 2;
|
||||
} else {
|
||||
nm_assert (!str);
|
||||
n_alloc = NM_MIN (n_read + 1, sizeof (buf));
|
||||
}
|
||||
|
||||
if (!(str = _mem_realloc (str, do_bzero_mem, old_n_alloc, n_alloc))) {
|
||||
if (do_bzero_mem)
|
||||
nm_explicit_bzero (buf, sizeof (buf));
|
||||
return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu bytes", n_alloc);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy (str + n_have, buf, n_read);
|
||||
n_have += n_read;
|
||||
}
|
||||
|
||||
if (do_bzero_mem)
|
||||
nm_explicit_bzero (buf, sizeof (buf));
|
||||
|
||||
if (n_alloc == 0)
|
||||
str = g_new0 (char, 1);
|
||||
else {
|
||||
str[n_have] = '\0';
|
||||
if (n_have + 1 < n_alloc) {
|
||||
if (!(str = _mem_realloc (str, do_bzero_mem, n_alloc, n_have + 1)))
|
||||
return _get_contents_error (error, ENOMEM, "failure to truncate buffer to %zu bytes", n_have + 1);
|
||||
}
|
||||
}
|
||||
|
||||
NM_SET_OUT (length, n_have);
|
||||
}
|
||||
|
||||
*contents = g_steal_pointer (&str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_utils_file_get_contents:
|
||||
* @dirfd: optional file descriptor to use openat(). If negative, use plain open().
|
||||
* @filename: the filename to open. Possibly relative to @dirfd.
|
||||
* @max_length: allocate at most @max_length bytes.
|
||||
* WARNING: see nm_utils_fd_get_contents() hint about @max_length.
|
||||
* @flags: %NMUtilsFileGetContentsFlags for reading the file.
|
||||
* @contents: the output buffer with the file read. It is always
|
||||
* NUL terminated. The buffer is at most @max_length long, including
|
||||
* the NUL byte. That is, it reads only files up to a length of
|
||||
* @max_length - 1 bytes.
|
||||
* @length: optional output argument of the read file size.
|
||||
*
|
||||
* A reimplementation of g_file_get_contents() with a few differences:
|
||||
* - accepts an @dirfd to open @filename relative to that path via openat().
|
||||
* - limits the maxium filesize to max_length.
|
||||
* - uses O_CLOEXEC on internal file descriptor
|
||||
*
|
||||
* Returns: a negative error code on failure.
|
||||
*/
|
||||
int
|
||||
nm_utils_file_get_contents (int dirfd,
|
||||
const char *filename,
|
||||
gsize max_length,
|
||||
NMUtilsFileGetContentsFlags flags,
|
||||
char **contents,
|
||||
gsize *length,
|
||||
GError **error)
|
||||
{
|
||||
int fd;
|
||||
int errsv;
|
||||
|
||||
g_return_val_if_fail (filename && filename[0], -EINVAL);
|
||||
|
||||
if (dirfd >= 0) {
|
||||
fd = openat (dirfd, filename, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
errsv = errno;
|
||||
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errsv),
|
||||
"Failed to open file \"%s\" with openat: %s",
|
||||
filename,
|
||||
g_strerror (errsv));
|
||||
return -errsv;
|
||||
}
|
||||
} else {
|
||||
fd = open (filename, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
errsv = errno;
|
||||
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errsv),
|
||||
"Failed to open file \"%s\": %s",
|
||||
filename,
|
||||
g_strerror (errsv));
|
||||
return -errsv;
|
||||
}
|
||||
}
|
||||
return nm_utils_fd_get_contents (fd,
|
||||
TRUE,
|
||||
max_length,
|
||||
flags,
|
||||
contents,
|
||||
length,
|
||||
error);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* Copied from GLib's g_file_set_contents() et al., but allows
|
||||
* specifying a mode for the new file.
|
||||
*/
|
||||
gboolean
|
||||
nm_utils_file_set_contents (const char *filename,
|
||||
const char *contents,
|
||||
gssize length,
|
||||
mode_t mode,
|
||||
GError **error)
|
||||
{
|
||||
gs_free char *tmp_name = NULL;
|
||||
struct stat statbuf;
|
||||
int errsv;
|
||||
gssize s;
|
||||
int fd;
|
||||
|
||||
g_return_val_if_fail (filename, FALSE);
|
||||
g_return_val_if_fail (contents || !length, FALSE);
|
||||
g_return_val_if_fail (!error || !*error, FALSE);
|
||||
g_return_val_if_fail (length >= -1, FALSE);
|
||||
|
||||
if (length == -1)
|
||||
length = strlen (contents);
|
||||
|
||||
tmp_name = g_strdup_printf ("%s.XXXXXX", filename);
|
||||
fd = g_mkstemp_full (tmp_name, O_RDWR, mode);
|
||||
if (fd < 0) {
|
||||
errsv = errno;
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errsv),
|
||||
"failed to create file %s: %s",
|
||||
tmp_name,
|
||||
g_strerror (errsv));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while (length > 0) {
|
||||
s = write (fd, contents, length);
|
||||
if (s < 0) {
|
||||
errsv = errno;
|
||||
if (errsv == EINTR)
|
||||
continue;
|
||||
|
||||
nm_close (fd);
|
||||
unlink (tmp_name);
|
||||
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errsv),
|
||||
"failed to write to file %s: %s",
|
||||
tmp_name,
|
||||
g_strerror (errsv));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_assert (s <= length);
|
||||
|
||||
contents += s;
|
||||
length -= s;
|
||||
}
|
||||
|
||||
/* If the final destination exists and is > 0 bytes, we want to sync the
|
||||
* newly written file to ensure the data is on disk when we rename over
|
||||
* the destination. Otherwise if we get a system crash we can lose both
|
||||
* the new and the old file on some filesystems. (I.E. those that don't
|
||||
* guarantee the data is written to the disk before the metadata.)
|
||||
*/
|
||||
if ( lstat (filename, &statbuf) == 0
|
||||
&& statbuf.st_size > 0
|
||||
&& fsync (fd) != 0) {
|
||||
errsv = errno;
|
||||
|
||||
nm_close (fd);
|
||||
unlink (tmp_name);
|
||||
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errsv),
|
||||
"failed to fsync %s: %s",
|
||||
tmp_name,
|
||||
g_strerror (errsv));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
nm_close (fd);
|
||||
|
||||
if (rename (tmp_name, filename)) {
|
||||
errsv = errno;
|
||||
unlink (tmp_name);
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errsv),
|
||||
"failed to rename %s to %s: %s",
|
||||
tmp_name,
|
||||
filename,
|
||||
g_strerror (errsv));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
63
shared/nm-utils/nm-io-utils.h
Normal file
63
shared/nm-utils/nm-io-utils.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* -*- 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_IO_UTILS_H__
|
||||
#define __NM_IO_UTILS_H__
|
||||
|
||||
#include "nm-macros-internal.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
* NMUtilsFileGetContentsFlags:
|
||||
* @NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE: no flag
|
||||
* @NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET: if present, ensure that no
|
||||
* data is left in memory. Essentially, it means to call explicity_bzero()
|
||||
* to not leave key material on the heap (when reading secrets).
|
||||
*/
|
||||
typedef enum {
|
||||
NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE = 0,
|
||||
NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET = (1 << 0),
|
||||
} NMUtilsFileGetContentsFlags;
|
||||
|
||||
int nm_utils_fd_get_contents (int fd,
|
||||
gboolean close_fd,
|
||||
gsize max_length,
|
||||
NMUtilsFileGetContentsFlags flags,
|
||||
char **contents,
|
||||
gsize *length,
|
||||
GError **error);
|
||||
|
||||
int nm_utils_file_get_contents (int dirfd,
|
||||
const char *filename,
|
||||
gsize max_length,
|
||||
NMUtilsFileGetContentsFlags flags,
|
||||
char **contents,
|
||||
gsize *length,
|
||||
GError **error);
|
||||
|
||||
gboolean nm_utils_file_set_contents (const char *filename,
|
||||
const char *contents,
|
||||
gssize length,
|
||||
mode_t mode,
|
||||
GError **error);
|
||||
|
||||
#endif /* __NM_IO_UTILS_H__ */
|
||||
|
|
@ -91,12 +91,6 @@ static inline void name (Type *v) \
|
|||
func (*v); \
|
||||
}
|
||||
|
||||
#define NM_AUTO_DEFINE_FCN_STRUCT(Type, name, func) \
|
||||
static inline void name (Type *v) \
|
||||
{ \
|
||||
func (v); \
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
|
|
@ -213,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:
|
||||
|
|
@ -244,22 +237,14 @@ 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)
|
||||
|
||||
NM_AUTO_DEFINE_FCN_STRUCT (GValue, _nm_auto_unset_gvalue, g_value_unset)
|
||||
#define nm_auto_unset_gvalue nm_auto(_nm_auto_unset_gvalue)
|
||||
#define nm_auto_unset_gvalue nm_auto(g_value_unset)
|
||||
|
||||
NM_AUTO_DEFINE_FCN_VOID0 (void *, _nm_auto_unref_gtypeclass, g_type_class_unref)
|
||||
#define nm_auto_unref_gtypeclass nm_auto(_nm_auto_unref_gtypeclass)
|
||||
|
||||
NM_AUTO_DEFINE_FCN0 (GByteArray *, _nm_auto_unref_bytearray, g_byte_array_unref)
|
||||
#define nm_auto_unref_bytearray nm_auto(_nm_auto_unref_bytearray)
|
||||
|
||||
static inline void
|
||||
_nm_auto_free_gstring (GString **str)
|
||||
{
|
||||
|
|
@ -834,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)
|
||||
{
|
||||
|
|
@ -924,7 +900,7 @@ nm_str_realloc (char *str)
|
|||
|
||||
#define NM_GOBJECT_PROPERTIES_DEFINE_BASE(...) \
|
||||
typedef enum { \
|
||||
_PROPERTY_ENUMS_0, \
|
||||
PROP_0, \
|
||||
__VA_ARGS__ \
|
||||
_PROPERTY_ENUMS_LAST, \
|
||||
} _PropertyEnums; \
|
||||
|
|
@ -945,8 +921,11 @@ _nm_gobject_notify_together_impl (obj_type *obj, guint n, const _PropertyEnums *
|
|||
while (n-- > 0) { \
|
||||
const _PropertyEnums prop = *props++; \
|
||||
\
|
||||
nm_assert ((gsize) prop < G_N_ELEMENTS (obj_properties)); \
|
||||
g_object_notify_by_pspec ((GObject *) obj, obj_properties[prop]); \
|
||||
if (prop != PROP_0) { \
|
||||
nm_assert ((gsize) prop < G_N_ELEMENTS (obj_properties)); \
|
||||
nm_assert (obj_properties[prop]); \
|
||||
g_object_notify_by_pspec ((GObject *) obj, obj_properties[prop]); \
|
||||
} \
|
||||
} \
|
||||
if (freeze_thaw) \
|
||||
g_object_thaw_notify ((GObject *) obj); \
|
||||
|
|
|
|||
134
shared/nm-utils/nm-secret-utils.c
Normal file
134
shared/nm-utils/nm-secret-utils.c
Normal file
|
|
@ -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);
|
||||
}
|
||||
151
shared/nm-utils/nm-secret-utils.h
Normal file
151
shared/nm-utils/nm-secret-utils.h
Normal file
|
|
@ -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__ */
|
||||
|
|
@ -26,6 +26,25 @@
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static inline gboolean
|
||||
_NM_INT_NOT_NEGATIVE (gssize val)
|
||||
{
|
||||
/* whether an enum (without negative values) is a signed int, depends on compiler options
|
||||
* and compiler implementation.
|
||||
*
|
||||
* When using such an enum for accessing an array, one naturally wants to check
|
||||
* that the enum is not negative. However, the compiler doesn't like a plain
|
||||
* comparisong "enum_val >= 0", because (if the enum is unsigned), it will warn
|
||||
* that the expression is always true *duh*. Not even a cast to a signed
|
||||
* type helps to avoid the compiler warning in any case.
|
||||
*
|
||||
* The sole purpose of this function is to avoid a compiler warning, when checking
|
||||
* that an enum is not negative. */
|
||||
return val >= 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static inline char
|
||||
nm_utils_addr_family_to_char (int addr_family)
|
||||
{
|
||||
|
|
@ -202,6 +221,12 @@ nm_utils_is_separator (const char c)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static inline gboolean
|
||||
nm_gbytes_equal0 (GBytes *a, GBytes *b)
|
||||
{
|
||||
return a == b || (a && b && g_bytes_equal (a, b));
|
||||
}
|
||||
|
||||
gboolean nm_utils_gbytes_equal_mem (GBytes *bytes,
|
||||
gconstpointer mem_data,
|
||||
gsize mem_len);
|
||||
|
|
@ -210,6 +235,27 @@ GVariant *nm_utils_gbytes_to_variant_ay (GBytes *bytes);
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static inline int
|
||||
nm_utils_hexchar_to_int (char ch)
|
||||
{
|
||||
G_STATIC_ASSERT_EXPR ('0' < 'A');
|
||||
G_STATIC_ASSERT_EXPR ('A' < 'a');
|
||||
|
||||
if (ch >= '0') {
|
||||
if (ch <= '9')
|
||||
return ch - '0';
|
||||
if (ch >= 'A') {
|
||||
if (ch <= 'F')
|
||||
return ((int) ch) + (10 - (int) 'A');
|
||||
if (ch >= 'a' && ch <= 'f')
|
||||
return ((int) ch) + (10 - (int) 'a');
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
const char *nm_utils_dbus_path_get_last_component (const char *dbus_path);
|
||||
|
||||
int nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include <net/ethernet.h>
|
||||
|
||||
#include "nm-utils/nm-random-utils.h"
|
||||
#include "nm-utils/nm-io-utils.h"
|
||||
#include "nm-utils.h"
|
||||
#include "nm-core-internal.h"
|
||||
#include "nm-setting-connection.h"
|
||||
|
|
@ -2560,262 +2561,6 @@ nm_utils_machine_id_read (void)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
_nm_printf (3, 4)
|
||||
static int
|
||||
_get_contents_error (GError **error, int errsv, const char *format, ...)
|
||||
{
|
||||
if (errsv < 0)
|
||||
errsv = -errsv;
|
||||
else if (!errsv)
|
||||
errsv = errno;
|
||||
|
||||
if (error) {
|
||||
char *msg;
|
||||
va_list args;
|
||||
|
||||
va_start (args, format);
|
||||
msg = g_strdup_vprintf (format, args);
|
||||
va_end (args);
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errsv),
|
||||
"%s: %s",
|
||||
msg, g_strerror (errsv));
|
||||
g_free (msg);
|
||||
}
|
||||
return -errsv;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_utils_fd_get_contents:
|
||||
* @fd: open file descriptor to read. The fd will not be closed,
|
||||
* but don't rely on its state afterwards.
|
||||
* @close_fd: if %TRUE, @fd will be closed by the function.
|
||||
* Passing %TRUE here might safe a syscall for dup().
|
||||
* @max_length: allocate at most @max_length bytes. If the
|
||||
* file is larger, reading will fail. Set to zero to use
|
||||
* a very large default.
|
||||
*
|
||||
* WARNING: @max_length is here to avoid a crash for huge/unlimited files.
|
||||
* For example, stat(/sys/class/net/enp0s25/ifindex) gives a filesize of
|
||||
* 4K, although the actual real is small. @max_length is the memory
|
||||
* allocated in the process of reading the file, thus it must be at least
|
||||
* the size reported by fstat.
|
||||
* If you set it to 1K, read will fail because fstat() claims the
|
||||
* file is larger.
|
||||
*
|
||||
* @contents: the output buffer with the file read. It is always
|
||||
* NUL terminated. The buffer is at most @max_length long, including
|
||||
* the NUL byte. That is, it reads only files up to a length of
|
||||
* @max_length - 1 bytes.
|
||||
* @length: optional output argument of the read file size.
|
||||
*
|
||||
* A reimplementation of g_file_get_contents() with a few differences:
|
||||
* - accepts an open fd, instead of a path name. This allows you to
|
||||
* use openat().
|
||||
* - limits the maxium filesize to max_length.
|
||||
*
|
||||
* Returns: a negative error code on failure.
|
||||
*/
|
||||
int
|
||||
nm_utils_fd_get_contents (int fd,
|
||||
gboolean close_fd,
|
||||
gsize max_length,
|
||||
char **contents,
|
||||
gsize *length,
|
||||
GError **error)
|
||||
{
|
||||
nm_auto_close int fd_keeper = close_fd ? fd : -1;
|
||||
struct stat stat_buf;
|
||||
gs_free char *str = NULL;
|
||||
|
||||
g_return_val_if_fail (fd >= 0, -EINVAL);
|
||||
g_return_val_if_fail (contents, -EINVAL);
|
||||
g_return_val_if_fail (!error || !*error, -EINVAL);
|
||||
|
||||
if (fstat (fd, &stat_buf) < 0)
|
||||
return _get_contents_error (error, 0, "failure during fstat");
|
||||
|
||||
if (!max_length) {
|
||||
/* default to a very large size, but not extreme */
|
||||
max_length = 2 * 1024 * 1024;
|
||||
}
|
||||
|
||||
if ( stat_buf.st_size > 0
|
||||
&& S_ISREG (stat_buf.st_mode)) {
|
||||
const gsize n_stat = stat_buf.st_size;
|
||||
ssize_t n_read;
|
||||
|
||||
if (n_stat > max_length - 1)
|
||||
return _get_contents_error (error, EMSGSIZE, "file too large (%zu+1 bytes with maximum %zu bytes)", n_stat, max_length);
|
||||
|
||||
str = g_try_malloc (n_stat + 1);
|
||||
if (!str)
|
||||
return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu+1 bytes", n_stat);
|
||||
|
||||
n_read = nm_utils_fd_read_loop (fd, str, n_stat, TRUE);
|
||||
if (n_read < 0)
|
||||
return _get_contents_error (error, n_read, "error reading %zu bytes from file descriptor", n_stat);
|
||||
str[n_read] = '\0';
|
||||
|
||||
if (n_read < n_stat) {
|
||||
char *tmp;
|
||||
|
||||
tmp = g_try_realloc (str, n_read + 1);
|
||||
if (!tmp)
|
||||
return _get_contents_error (error, ENOMEM, "failure to reallocate buffer with %zu bytes", n_read + 1);
|
||||
str = tmp;
|
||||
}
|
||||
NM_SET_OUT (length, n_read);
|
||||
} else {
|
||||
nm_auto_fclose FILE *f = NULL;
|
||||
char buf[4096];
|
||||
gsize n_have, n_alloc;
|
||||
int fd2;
|
||||
|
||||
if (fd_keeper >= 0)
|
||||
fd2 = nm_steal_fd (&fd_keeper);
|
||||
else {
|
||||
fd2 = fcntl (fd, F_DUPFD_CLOEXEC, 0);
|
||||
if (fd2 < 0)
|
||||
return _get_contents_error (error, 0, "error during dup");
|
||||
}
|
||||
|
||||
if (!(f = fdopen (fd2, "r"))) {
|
||||
nm_close (fd2);
|
||||
return _get_contents_error (error, 0, "failure during fdopen");
|
||||
}
|
||||
|
||||
n_have = 0;
|
||||
n_alloc = 0;
|
||||
|
||||
while (!feof (f)) {
|
||||
int errsv;
|
||||
gsize n_read;
|
||||
|
||||
n_read = fread (buf, 1, sizeof (buf), f);
|
||||
errsv = errno;
|
||||
if (ferror (f))
|
||||
return _get_contents_error (error, errsv, "error during fread");
|
||||
|
||||
if ( n_have > G_MAXSIZE - 1 - n_read
|
||||
|| n_have + n_read + 1 > max_length) {
|
||||
return _get_contents_error (error, EMSGSIZE, "file stream too large (%zu+1 bytes with maximum %zu bytes)",
|
||||
(n_have > G_MAXSIZE - 1 - n_read) ? G_MAXSIZE : n_have + n_read,
|
||||
max_length);
|
||||
}
|
||||
|
||||
if (n_have + n_read + 1 >= n_alloc) {
|
||||
char *tmp;
|
||||
|
||||
if (str) {
|
||||
if (n_alloc >= max_length / 2)
|
||||
n_alloc = max_length;
|
||||
else
|
||||
n_alloc *= 2;
|
||||
} else
|
||||
n_alloc = NM_MIN (n_read + 1, sizeof (buf));
|
||||
|
||||
tmp = g_try_realloc (str, n_alloc);
|
||||
if (!tmp)
|
||||
return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu bytes", n_alloc);
|
||||
str = tmp;
|
||||
}
|
||||
|
||||
memcpy (str + n_have, buf, n_read);
|
||||
n_have += n_read;
|
||||
}
|
||||
|
||||
if (n_alloc == 0)
|
||||
str = g_new0 (char, 1);
|
||||
else {
|
||||
str[n_have] = '\0';
|
||||
if (n_have + 1 < n_alloc) {
|
||||
char *tmp;
|
||||
|
||||
tmp = g_try_realloc (str, n_have + 1);
|
||||
if (!tmp)
|
||||
return _get_contents_error (error, ENOMEM, "failure to truncate buffer to %zu bytes", n_have + 1);
|
||||
str = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
NM_SET_OUT (length, n_have);
|
||||
}
|
||||
|
||||
*contents = g_steal_pointer (&str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_utils_file_get_contents:
|
||||
* @dirfd: optional file descriptor to use openat(). If negative, use plain open().
|
||||
* @filename: the filename to open. Possibly relative to @dirfd.
|
||||
* @max_length: allocate at most @max_length bytes.
|
||||
* WARNING: see nm_utils_fd_get_contents() hint about @max_length.
|
||||
* @contents: the output buffer with the file read. It is always
|
||||
* NUL terminated. The buffer is at most @max_length long, including
|
||||
* the NUL byte. That is, it reads only files up to a length of
|
||||
* @max_length - 1 bytes.
|
||||
* @length: optional output argument of the read file size.
|
||||
*
|
||||
* A reimplementation of g_file_get_contents() with a few differences:
|
||||
* - accepts an @dirfd to open @filename relative to that path via openat().
|
||||
* - limits the maxium filesize to max_length.
|
||||
* - uses O_CLOEXEC on internal file descriptor
|
||||
*
|
||||
* Returns: a negative error code on failure.
|
||||
*/
|
||||
int
|
||||
nm_utils_file_get_contents (int dirfd,
|
||||
const char *filename,
|
||||
gsize max_length,
|
||||
char **contents,
|
||||
gsize *length,
|
||||
GError **error)
|
||||
{
|
||||
int fd;
|
||||
int errsv;
|
||||
|
||||
g_return_val_if_fail (filename && filename[0], -EINVAL);
|
||||
|
||||
if (dirfd >= 0) {
|
||||
fd = openat (dirfd, filename, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
errsv = errno;
|
||||
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errsv),
|
||||
"Failed to open file \"%s\" with openat: %s",
|
||||
filename,
|
||||
g_strerror (errsv));
|
||||
return -errsv;
|
||||
}
|
||||
} else {
|
||||
fd = open (filename, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
errsv = errno;
|
||||
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errsv),
|
||||
"Failed to open file \"%s\": %s",
|
||||
filename,
|
||||
g_strerror (errsv));
|
||||
return -errsv;
|
||||
}
|
||||
}
|
||||
return nm_utils_fd_get_contents (fd,
|
||||
TRUE,
|
||||
max_length,
|
||||
contents,
|
||||
length,
|
||||
error);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
_secret_key_read (guint8 **out_secret_key,
|
||||
gsize *out_key_len)
|
||||
|
|
@ -2942,6 +2687,7 @@ nm_utils_get_boot_id (void)
|
|||
gs_free char *contents = NULL;
|
||||
|
||||
nm_utils_file_get_contents (-1, "/proc/sys/kernel/random/boot_id", 0,
|
||||
NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
|
||||
&contents, NULL, NULL);
|
||||
if (contents) {
|
||||
g_strstrip (contents);
|
||||
|
|
@ -3987,110 +3733,6 @@ nm_utils_get_reverse_dns_domains_ip6 (const struct in6_addr *ip, guint8 plen, GP
|
|||
#undef N_SHIFT
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied from GLib's g_file_set_contents() et al., but allows
|
||||
* specifying a mode for the new file.
|
||||
*/
|
||||
gboolean
|
||||
nm_utils_file_set_contents (const char *filename,
|
||||
const char *contents,
|
||||
gssize length,
|
||||
mode_t mode,
|
||||
GError **error)
|
||||
{
|
||||
gs_free char *tmp_name = NULL;
|
||||
struct stat statbuf;
|
||||
int errsv;
|
||||
gssize s;
|
||||
int fd;
|
||||
|
||||
g_return_val_if_fail (filename, FALSE);
|
||||
g_return_val_if_fail (contents || !length, FALSE);
|
||||
g_return_val_if_fail (!error || !*error, FALSE);
|
||||
g_return_val_if_fail (length >= -1, FALSE);
|
||||
|
||||
if (length == -1)
|
||||
length = strlen (contents);
|
||||
|
||||
tmp_name = g_strdup_printf ("%s.XXXXXX", filename);
|
||||
fd = g_mkstemp_full (tmp_name, O_RDWR, mode);
|
||||
if (fd < 0) {
|
||||
errsv = errno;
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errsv),
|
||||
"failed to create file %s: %s",
|
||||
tmp_name,
|
||||
g_strerror (errsv));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while (length > 0) {
|
||||
s = write (fd, contents, length);
|
||||
if (s < 0) {
|
||||
errsv = errno;
|
||||
if (errsv == EINTR)
|
||||
continue;
|
||||
|
||||
nm_close (fd);
|
||||
unlink (tmp_name);
|
||||
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errsv),
|
||||
"failed to write to file %s: %s",
|
||||
tmp_name,
|
||||
g_strerror (errsv));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_assert (s <= length);
|
||||
|
||||
contents += s;
|
||||
length -= s;
|
||||
}
|
||||
|
||||
/* If the final destination exists and is > 0 bytes, we want to sync the
|
||||
* newly written file to ensure the data is on disk when we rename over
|
||||
* the destination. Otherwise if we get a system crash we can lose both
|
||||
* the new and the old file on some filesystems. (I.E. those that don't
|
||||
* guarantee the data is written to the disk before the metadata.)
|
||||
*/
|
||||
if ( lstat (filename, &statbuf) == 0
|
||||
&& statbuf.st_size > 0
|
||||
&& fsync (fd) != 0) {
|
||||
errsv = errno;
|
||||
|
||||
nm_close (fd);
|
||||
unlink (tmp_name);
|
||||
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errsv),
|
||||
"failed to fsync %s: %s",
|
||||
tmp_name,
|
||||
g_strerror (errsv));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
nm_close (fd);
|
||||
|
||||
if (rename (tmp_name, filename)) {
|
||||
errsv = errno;
|
||||
unlink (tmp_name);
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errsv),
|
||||
"failed to rename %s to %s: %s",
|
||||
tmp_name,
|
||||
filename,
|
||||
g_strerror (errsv));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
struct plugin_info {
|
||||
char *path;
|
||||
struct stat st;
|
||||
|
|
|
|||
|
|
@ -264,26 +264,6 @@ gboolean nm_utils_sysctl_ip_conf_is_path (int addr_family, const char *path, con
|
|||
|
||||
gboolean nm_utils_is_specific_hostname (const char *name);
|
||||
|
||||
int nm_utils_fd_get_contents (int fd,
|
||||
gboolean close_fd,
|
||||
gsize max_length,
|
||||
char **contents,
|
||||
gsize *length,
|
||||
GError **error);
|
||||
|
||||
int nm_utils_file_get_contents (int dirfd,
|
||||
const char *filename,
|
||||
gsize max_length,
|
||||
char **contents,
|
||||
gsize *length,
|
||||
GError **error);
|
||||
|
||||
gboolean nm_utils_file_set_contents (const char *filename,
|
||||
const char *contents,
|
||||
gssize length,
|
||||
mode_t mode,
|
||||
GError **error);
|
||||
|
||||
char *nm_utils_machine_id_read (void);
|
||||
gboolean nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid);
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@
|
|||
#include "wifi/nm-wifi-utils-wext.h"
|
||||
#include "wpan/nm-wpan-utils.h"
|
||||
#include "nm-utils/unaligned.h"
|
||||
#include "nm-utils/nm-io-utils.h"
|
||||
#include "nm-utils/nm-udev-utils.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
@ -853,7 +854,9 @@ _linktype_read_devtype (int dirfd)
|
|||
|
||||
nm_assert (dirfd >= 0);
|
||||
|
||||
if (nm_utils_file_get_contents (dirfd, "uevent", 1*1024*1024, &contents, NULL, NULL) < 0)
|
||||
if (nm_utils_file_get_contents (dirfd, "uevent", 1*1024*1024,
|
||||
NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
|
||||
&contents, NULL, NULL) < 0)
|
||||
return NULL;
|
||||
for (cont = contents; cont; cont = end) {
|
||||
end = strpbrk (cont, "\r\n");
|
||||
|
|
@ -3515,7 +3518,9 @@ _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *pathid, int dirfd, c
|
|||
char *contents;
|
||||
gs_free char *value_escaped = g_strescape (value, NULL);
|
||||
|
||||
if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, &contents, NULL, &error) < 0) {
|
||||
if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024,
|
||||
NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
|
||||
&contents, NULL, &error) < 0) {
|
||||
_LOGD ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", pathid, value_escaped, error->message);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
|
|
@ -3732,7 +3737,9 @@ sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *pat
|
|||
pathid = path;
|
||||
}
|
||||
|
||||
if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, &contents, NULL, &error) < 0) {
|
||||
if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024,
|
||||
NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
|
||||
&contents, NULL, &error) < 0) {
|
||||
/* We assume FAILED means EOPNOTSUP */
|
||||
if ( g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)
|
||||
|| g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NODEV)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <linux/if_tun.h>
|
||||
|
||||
#include "nm-utils/nm-io-utils.h"
|
||||
#include "platform/nmp-object.h"
|
||||
#include "platform/nmp-netns.h"
|
||||
#include "platform/nm-platform-utils.h"
|
||||
|
|
@ -2622,7 +2623,9 @@ test_sysctl_rename (void)
|
|||
case 0: {
|
||||
gs_free char *c = NULL;
|
||||
|
||||
if (nm_utils_file_get_contents (dirfd, "ifindex", 1*1024*1024, &c, NULL, NULL) < 0)
|
||||
if (nm_utils_file_get_contents (dirfd, "ifindex", 1*1024*1024,
|
||||
NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
|
||||
&c, NULL, NULL) < 0)
|
||||
g_assert_not_reached();
|
||||
g_assert_cmpint (ifindex[0], ==, (int) _nm_utils_ascii_str_to_int64 (c, 10, 0, G_MAXINT, -1));
|
||||
break;
|
||||
|
|
@ -2686,7 +2689,9 @@ test_sysctl_netns_switch (void)
|
|||
{
|
||||
gs_free char *c = NULL;
|
||||
|
||||
if (nm_utils_file_get_contents (dirfd, "ifindex", 0, &c, NULL, NULL) < 0)
|
||||
if (nm_utils_file_get_contents (dirfd, "ifindex", 0,
|
||||
NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
|
||||
&c, NULL, NULL) < 0)
|
||||
g_assert_not_reached();
|
||||
g_assert_cmpint (ifindex, ==, (int) _nm_utils_ascii_str_to_int64 (c, 10, 0, G_MAXINT, -1));
|
||||
}
|
||||
|
|
@ -2698,7 +2703,11 @@ test_sysctl_netns_switch (void)
|
|||
{
|
||||
gs_free char *c = NULL;
|
||||
|
||||
if (nm_utils_file_get_contents (-1, nm_sprintf_bufa (100, "/sys/class/net/%s/ifindex", IFNAME), 0, &c, NULL, NULL) < 0)
|
||||
if (nm_utils_file_get_contents (-1,
|
||||
nm_sprintf_bufa (100, "/sys/class/net/%s/ifindex", IFNAME),
|
||||
0,
|
||||
NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
|
||||
&c, NULL, NULL) < 0)
|
||||
ifindex_tmp = -1;
|
||||
else
|
||||
ifindex_tmp = _nm_utils_ascii_str_to_int64 (c, 10, 0, G_MAXINT, -2);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ sources = files(
|
|||
)
|
||||
|
||||
deps = [
|
||||
crypto_dep,
|
||||
nm_dep
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nm-utils/nm-secret-utils.h"
|
||||
#include "nm-connection.h"
|
||||
#include "nm-dbus-interface.h"
|
||||
#include "nm-setting-connection.h"
|
||||
|
|
@ -79,6 +80,210 @@
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static char *
|
||||
get_full_file_path (const char *ifcfg_path, const char *file_path)
|
||||
{
|
||||
const char *base = file_path;
|
||||
char *p, *ret, *dirname;
|
||||
|
||||
g_return_val_if_fail (ifcfg_path != NULL, NULL);
|
||||
g_return_val_if_fail (file_path != NULL, NULL);
|
||||
|
||||
if (file_path[0] == '/')
|
||||
return g_strdup (file_path);
|
||||
|
||||
p = strrchr (file_path, '/');
|
||||
if (p)
|
||||
base = p + 1;
|
||||
|
||||
dirname = g_path_get_dirname (ifcfg_path);
|
||||
ret = g_build_path ("/", dirname, base, NULL);
|
||||
g_free (dirname);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static NMSettingSecretFlags
|
||||
_secret_read_ifcfg_flags (shvarFile *ifcfg, const char *flags_key)
|
||||
{
|
||||
NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
|
||||
gs_free char *val_free = NULL;
|
||||
const char *val;
|
||||
|
||||
nm_assert (flags_key);
|
||||
nm_assert (g_str_has_suffix (flags_key, "_FLAGS"));
|
||||
|
||||
val = svGetValueStr (ifcfg, flags_key, &val_free);
|
||||
if (val) {
|
||||
if (strstr (val, SECRET_FLAG_AGENT))
|
||||
flags |= NM_SETTING_SECRET_FLAG_AGENT_OWNED;
|
||||
if (strstr (val, SECRET_FLAG_NOT_SAVED))
|
||||
flags |= NM_SETTING_SECRET_FLAG_NOT_SAVED;
|
||||
if (strstr (val, SECRET_FLAG_NOT_REQUIRED))
|
||||
flags |= NM_SETTING_SECRET_FLAG_NOT_REQUIRED;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void
|
||||
_secret_read_ifcfg (shvarFile *ifcfg,
|
||||
shvarFile *keys_ifcfg,
|
||||
const char *name,
|
||||
char **value,
|
||||
NMSettingSecretFlags *flags)
|
||||
{
|
||||
char flags_key[250];
|
||||
|
||||
nm_sprintf_buf (flags_key, "%s_FLAGS", name);
|
||||
|
||||
*flags = _secret_read_ifcfg_flags (ifcfg, flags_key);
|
||||
|
||||
if (*flags != NM_SETTING_SECRET_FLAG_NONE)
|
||||
*value = NULL;
|
||||
else {
|
||||
*value = svGetValue_cp (ifcfg, name);
|
||||
if (!*value && keys_ifcfg)
|
||||
*value = svGetValue_cp (keys_ifcfg, name);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_secret_set_from_ifcfg (gpointer setting,
|
||||
shvarFile *ifcfg,
|
||||
shvarFile *keys_ifcfg,
|
||||
const char *ifcfg_key,
|
||||
const char *property_name)
|
||||
{
|
||||
nm_auto_free_secret char *secret = NULL;
|
||||
NMSettingSecretFlags flags;
|
||||
char flags_key[250];
|
||||
|
||||
nm_assert (NM_IS_SETTING (setting));
|
||||
|
||||
_secret_read_ifcfg (ifcfg, keys_ifcfg, ifcfg_key, &secret, &flags);
|
||||
|
||||
g_object_set (setting,
|
||||
property_name,
|
||||
secret,
|
||||
nm_sprintf_buf (flags_key, "%s-flags", property_name),
|
||||
flags,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_secret_password_raw_to_bytes (const char *ifcfg_key,
|
||||
const char *password_raw,
|
||||
GBytes **out_bytes,
|
||||
GError **error)
|
||||
{
|
||||
nm_auto_free_secret_buf NMSecretBuf *secret = NULL;
|
||||
gsize len;
|
||||
|
||||
if (!password_raw) {
|
||||
NM_SET_OUT (out_bytes, NULL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (password_raw[0] == '0' && password_raw[1] == 'x')
|
||||
password_raw += 2;
|
||||
|
||||
secret = nm_secret_buf_new (strlen (password_raw) / 2 + 3);
|
||||
if (!_nm_utils_str2bin_full (password_raw, FALSE, ":", secret->bin, secret->len, &len)) {
|
||||
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
|
||||
"Invalid hex password in %s",
|
||||
ifcfg_key);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
NM_SET_OUT (out_bytes, nm_secret_buf_to_gbytes_take (g_steal_pointer (&secret), len));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static GBytes *
|
||||
_cert_get_cert_bytes (const char *ifcfg_path,
|
||||
const char *value,
|
||||
GError **error)
|
||||
{
|
||||
gs_free char *path = NULL;
|
||||
|
||||
if (g_str_has_prefix (value, "pkcs11:"))
|
||||
return _nm_setting_802_1x_cert_value_to_bytes (NM_SETTING_802_1X_CK_SCHEME_PKCS11, (guint8 *) value, -1, error);
|
||||
|
||||
path = get_full_file_path (ifcfg_path, value);
|
||||
return _nm_setting_802_1x_cert_value_to_bytes (NM_SETTING_802_1X_CK_SCHEME_PATH, (guint8 *) path, -1, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_cert_get_cert (shvarFile *ifcfg,
|
||||
const char *ifcfg_key,
|
||||
GBytes **out_cert,
|
||||
NMSetting8021xCKScheme *out_scheme,
|
||||
GError **error)
|
||||
{
|
||||
nm_auto_free_secret char *val_free = NULL;
|
||||
const char *val;
|
||||
gs_unref_bytes GBytes *cert = NULL;
|
||||
GError *local = NULL;
|
||||
NMSetting8021xCKScheme scheme;
|
||||
|
||||
val = svGetValueStr (ifcfg, ifcfg_key, &val_free);
|
||||
if (!val) {
|
||||
NM_SET_OUT (out_cert, NULL);
|
||||
NM_SET_OUT (out_scheme, NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cert = _cert_get_cert_bytes (svFileGetName (ifcfg), val, &local);
|
||||
if (!cert)
|
||||
goto err;
|
||||
|
||||
scheme = _nm_setting_802_1x_cert_get_scheme (cert, &local);
|
||||
if (scheme == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN)
|
||||
goto err;
|
||||
|
||||
NM_SET_OUT (out_cert, g_steal_pointer (&cert));
|
||||
NM_SET_OUT (out_scheme, scheme);
|
||||
return TRUE;
|
||||
|
||||
err:
|
||||
g_set_error (error,
|
||||
NM_SETTINGS_ERROR,
|
||||
NM_SETTINGS_ERROR_INVALID_CONNECTION,
|
||||
"invalid certificate %s: %s",
|
||||
ifcfg_key,
|
||||
local->message);
|
||||
g_error_free (local);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_cert_set_from_ifcfg (gpointer setting,
|
||||
shvarFile *ifcfg,
|
||||
const char *ifcfg_key,
|
||||
const char *property_name,
|
||||
GBytes **out_cert,
|
||||
GError **error)
|
||||
{
|
||||
gs_unref_bytes GBytes *cert = NULL;
|
||||
|
||||
if (!_cert_get_cert (ifcfg,
|
||||
ifcfg_key,
|
||||
&cert,
|
||||
NULL,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
g_object_set (setting, property_name, cert, NULL);
|
||||
|
||||
NM_SET_OUT (out_cert, g_steal_pointer (&cert));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
check_if_bond_slave (shvarFile *ifcfg,
|
||||
NMSettingConnection *s_con)
|
||||
|
|
@ -2608,30 +2813,6 @@ read_wep_keys (shvarFile *ifcfg,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static NMSettingSecretFlags
|
||||
read_secret_flags (shvarFile *ifcfg, const char *flags_key)
|
||||
{
|
||||
NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
|
||||
char *val;
|
||||
|
||||
g_return_val_if_fail (flags_key != NULL, NM_SETTING_SECRET_FLAG_NONE);
|
||||
g_return_val_if_fail (flags_key[0] != '\0', NM_SETTING_SECRET_FLAG_NONE);
|
||||
g_return_val_if_fail (g_str_has_suffix (flags_key, "_FLAGS"), NM_SETTING_SECRET_FLAG_NONE);
|
||||
|
||||
val = svGetValueStr_cp (ifcfg, flags_key);
|
||||
if (val) {
|
||||
if (strstr (val, SECRET_FLAG_AGENT))
|
||||
flags |= NM_SETTING_SECRET_FLAG_AGENT_OWNED;
|
||||
if (strstr (val, SECRET_FLAG_NOT_SAVED))
|
||||
flags |= NM_SETTING_SECRET_FLAG_NOT_SAVED;
|
||||
if (strstr (val, SECRET_FLAG_NOT_REQUIRED))
|
||||
flags |= NM_SETTING_SECRET_FLAG_NOT_REQUIRED;
|
||||
|
||||
g_free (val);
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static NMSetting *
|
||||
make_wep_setting (shvarFile *ifcfg,
|
||||
const char *file,
|
||||
|
|
@ -2663,7 +2844,7 @@ make_wep_setting (shvarFile *ifcfg,
|
|||
}
|
||||
|
||||
/* Read WEP key flags */
|
||||
key_flags = read_secret_flags (ifcfg, "WEP_KEY_FLAGS");
|
||||
key_flags = _secret_read_ifcfg_flags (ifcfg, "WEP_KEY_FLAGS");
|
||||
g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, key_flags, NULL);
|
||||
|
||||
/* Read keys in the ifcfg file if they are system-owned */
|
||||
|
|
@ -2862,254 +3043,118 @@ parse_wpa_psk (shvarFile *ifcfg,
|
|||
return g_steal_pointer (&psk);
|
||||
}
|
||||
|
||||
static void
|
||||
read_8021x_password (shvarFile *ifcfg, shvarFile *keys_ifcfg, const char *name,
|
||||
char **value, NMSettingSecretFlags *flags)
|
||||
{
|
||||
gs_free char *flags_key = NULL;
|
||||
|
||||
*value = NULL;
|
||||
flags_key = g_strdup_printf ("%s_FLAGS", name);
|
||||
*flags = read_secret_flags (ifcfg, flags_key);
|
||||
|
||||
if (*flags == NM_SETTING_SECRET_FLAG_NONE) {
|
||||
*value = svGetValueStr_cp (ifcfg, name);
|
||||
if (!*value && keys_ifcfg)
|
||||
*value = svGetValueStr_cp (keys_ifcfg, name);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
eap_simple_reader (const char *eap_method,
|
||||
shvarFile *ifcfg,
|
||||
shvarFile *keys,
|
||||
shvarFile *keys_ifcfg,
|
||||
NMSetting8021x *s_8021x,
|
||||
gboolean phase2,
|
||||
GError **error)
|
||||
{
|
||||
NMSettingSecretFlags flags;
|
||||
GBytes *bytes;
|
||||
char *value;
|
||||
gs_free char *identity_free = NULL;
|
||||
nm_auto_free_secret char *password_raw_str = NULL;
|
||||
gs_unref_bytes GBytes *password_raw_bytes = NULL;
|
||||
|
||||
value = svGetValueStr_cp (ifcfg, "IEEE_8021X_IDENTITY");
|
||||
if (!value) {
|
||||
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
|
||||
"Missing IEEE_8021X_IDENTITY for EAP method '%s'.",
|
||||
eap_method);
|
||||
g_object_set (s_8021x,
|
||||
NM_SETTING_802_1X_IDENTITY,
|
||||
svGetValueStr (ifcfg, "IEEE_8021X_IDENTITY", &identity_free),
|
||||
NULL);
|
||||
|
||||
_secret_set_from_ifcfg (s_8021x,
|
||||
ifcfg,
|
||||
keys_ifcfg,
|
||||
"IEEE_8021X_PASSWORD",
|
||||
NM_SETTING_802_1X_PASSWORD);
|
||||
|
||||
_secret_read_ifcfg (ifcfg, keys_ifcfg, "IEEE_8021X_PASSWORD_RAW", &password_raw_str, &flags);
|
||||
if (!_secret_password_raw_to_bytes ("IEEE_8021X_PASSWORD_RAW",
|
||||
password_raw_str,
|
||||
&password_raw_bytes,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL);
|
||||
nm_clear_g_free (&value);
|
||||
|
||||
read_8021x_password (ifcfg, keys, "IEEE_8021X_PASSWORD", &value, &flags);
|
||||
g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_FLAGS, flags, NULL);
|
||||
if (value) {
|
||||
g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD, value, NULL);
|
||||
nm_clear_g_free (&value);
|
||||
}
|
||||
|
||||
read_8021x_password (ifcfg, keys, "IEEE_8021X_PASSWORD_RAW", &value, &flags);
|
||||
g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_RAW_FLAGS, flags, NULL);
|
||||
if (value) {
|
||||
bytes = nm_utils_hexstr2bin (value);
|
||||
if (!bytes) {
|
||||
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
|
||||
"Invalid hex string '%s' in IEEE_8021X_PASSWORD_RAW.",
|
||||
value);
|
||||
g_free (value);
|
||||
return FALSE;
|
||||
}
|
||||
g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_RAW, bytes, NULL);
|
||||
g_bytes_unref (bytes);
|
||||
nm_clear_g_free (&value);
|
||||
}
|
||||
g_object_set (s_8021x,
|
||||
NM_SETTING_802_1X_PASSWORD_RAW_FLAGS,
|
||||
flags,
|
||||
NM_SETTING_802_1X_PASSWORD_RAW,
|
||||
password_raw_bytes,
|
||||
NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static char *
|
||||
get_full_file_path (const char *ifcfg_path, const char *file_path)
|
||||
{
|
||||
const char *base = file_path;
|
||||
char *p, *ret, *dirname;
|
||||
|
||||
g_return_val_if_fail (ifcfg_path != NULL, NULL);
|
||||
g_return_val_if_fail (file_path != NULL, NULL);
|
||||
|
||||
if (file_path[0] == '/')
|
||||
return g_strdup (file_path);
|
||||
|
||||
p = strrchr (file_path, '/');
|
||||
if (p)
|
||||
base = p + 1;
|
||||
|
||||
dirname = g_path_get_dirname (ifcfg_path);
|
||||
ret = g_build_path ("/", dirname, base, NULL);
|
||||
g_free (dirname);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *
|
||||
get_cert_value (const char *ifcfg_path, const char *value,
|
||||
NMSetting8021xCKScheme *out_scheme)
|
||||
{
|
||||
if (strncmp (value, "pkcs11:", 7) == 0) {
|
||||
*out_scheme = NM_SETTING_802_1X_CK_SCHEME_PKCS11;
|
||||
return g_strdup (value);
|
||||
}
|
||||
|
||||
*out_scheme = NM_SETTING_802_1X_CK_SCHEME_PATH;
|
||||
return get_full_file_path (ifcfg_path, value);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
eap_tls_reader (const char *eap_method,
|
||||
shvarFile *ifcfg,
|
||||
shvarFile *keys,
|
||||
shvarFile *keys_ifcfg,
|
||||
NMSetting8021x *s_8021x,
|
||||
gboolean phase2,
|
||||
GError **error)
|
||||
{
|
||||
gs_free char *ca_cert = NULL;
|
||||
gs_free char *privkey = NULL;
|
||||
gs_free char *privkey_password = NULL;
|
||||
char *value;
|
||||
char *ca_cert_password = NULL;
|
||||
char *client_cert_password = NULL;
|
||||
NMSetting8021xCKFormat privkey_format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
|
||||
const char *ca_cert_key = phase2 ? "IEEE_8021X_INNER_CA_CERT" : "IEEE_8021X_CA_CERT";
|
||||
const char *ca_cert_pw_key = phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD" : "IEEE_8021X_CA_CERT_PASSWORD";
|
||||
const char *ca_cert_pw_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD : NM_SETTING_802_1X_CA_CERT_PASSWORD;
|
||||
const char *ca_cert_pw_flags_key = phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD_FLAGS" : "IEEE_8021X_CA_CERT_PASSWORD_FLAGS";
|
||||
const char *ca_cert_pw_flags_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS : NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS;
|
||||
const char *cli_cert_key = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT";
|
||||
const char *cli_cert_pw_key = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD" : "IEEE_8021X_CLIENT_CERT_PASSWORD";
|
||||
const char *cli_cert_pw_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD;
|
||||
const char *cli_cert_pw_flags_key = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD_FLAGS" : "IEEE_8021X_CLIENT_CERT_PASSWORD_FLAGS";
|
||||
const char *cli_cert_pw_flags_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD_FLAGS : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS;
|
||||
const char *pk_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY";
|
||||
const char *pk_pw_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD": "IEEE_8021X_PRIVATE_KEY_PASSWORD";
|
||||
const char *pk_pw_flags_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS" : "IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS";
|
||||
const char *pk_pw_flags_prop = phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS;
|
||||
NMSettingSecretFlags flags;
|
||||
NMSetting8021xCKScheme scheme;
|
||||
gs_unref_bytes GBytes *privkey = NULL;
|
||||
gs_unref_bytes GBytes *client_cert = NULL;
|
||||
gs_free char *identity_free = NULL;
|
||||
|
||||
value = svGetValueStr_cp (ifcfg, "IEEE_8021X_IDENTITY");
|
||||
if (value) {
|
||||
g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL);
|
||||
g_free (value);
|
||||
}
|
||||
g_object_set (s_8021x,
|
||||
NM_SETTING_802_1X_IDENTITY,
|
||||
svGetValueStr (ifcfg, "IEEE_8021X_IDENTITY", &identity_free),
|
||||
NULL);
|
||||
|
||||
ca_cert = svGetValueStr_cp (ifcfg, ca_cert_key);
|
||||
if (ca_cert) {
|
||||
gs_free char *real_cert_value = NULL;
|
||||
if (!_cert_set_from_ifcfg (s_8021x,
|
||||
ifcfg,
|
||||
phase2 ? "IEEE_8021X_INNER_CA_CERT" : "IEEE_8021X_CA_CERT",
|
||||
phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT : NM_SETTING_802_1X_CA_CERT,
|
||||
NULL,
|
||||
error))
|
||||
return FALSE;
|
||||
_secret_set_from_ifcfg (s_8021x,
|
||||
ifcfg,
|
||||
keys_ifcfg,
|
||||
phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD" : "IEEE_8021X_CA_CERT_PASSWORD",
|
||||
phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD : NM_SETTING_802_1X_CA_CERT_PASSWORD);
|
||||
|
||||
real_cert_value = get_cert_value (svFileGetName (ifcfg), ca_cert, &scheme);
|
||||
if (phase2) {
|
||||
if (!nm_setting_802_1x_set_phase2_ca_cert (s_8021x, real_cert_value, scheme, NULL, error))
|
||||
return FALSE;
|
||||
} else {
|
||||
if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
|
||||
flags = read_secret_flags (ifcfg, ca_cert_pw_flags_key);
|
||||
g_object_set (s_8021x, ca_cert_pw_flags_prop, flags, NULL);
|
||||
|
||||
if (flags == NM_SETTING_SECRET_FLAG_NONE) {
|
||||
ca_cert_password = svGetValueStr_cp (ifcfg, ca_cert_pw_key);
|
||||
g_object_set (s_8021x, ca_cert_pw_prop, ca_cert_password, NULL);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PARSE_WARNING ("missing %s for EAP method '%s'; this is insecure!",
|
||||
ca_cert_key, eap_method);
|
||||
}
|
||||
|
||||
/* Read and set private key password flags */
|
||||
flags = read_secret_flags (ifcfg, pk_pw_flags_key);
|
||||
g_object_set (s_8021x, pk_pw_flags_prop, flags, NULL);
|
||||
|
||||
/* Read the private key password if it's system-owned */
|
||||
if (flags == NM_SETTING_SECRET_FLAG_NONE) {
|
||||
/* Private key password */
|
||||
privkey_password = svGetValueStr_cp (ifcfg, pk_pw_key);
|
||||
if (!privkey_password && keys) {
|
||||
/* Try the lookaside keys file */
|
||||
privkey_password = svGetValueStr_cp (keys, pk_pw_key);
|
||||
}
|
||||
}
|
||||
|
||||
/* The private key itself */
|
||||
privkey = svGetValueStr_cp (ifcfg, pk_key);
|
||||
if (!_cert_set_from_ifcfg (s_8021x,
|
||||
ifcfg,
|
||||
phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY",
|
||||
phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY : NM_SETTING_802_1X_PRIVATE_KEY,
|
||||
&privkey,
|
||||
error))
|
||||
return FALSE;
|
||||
_secret_set_from_ifcfg (s_8021x,
|
||||
ifcfg,
|
||||
keys_ifcfg,
|
||||
phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD" : "IEEE_8021X_PRIVATE_KEY_PASSWORD",
|
||||
phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
|
||||
if (!privkey) {
|
||||
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
|
||||
"Missing %s for EAP method '%s'.",
|
||||
pk_key,
|
||||
phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY",
|
||||
eap_method);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
{
|
||||
gs_free char *real_cert_value = NULL;
|
||||
|
||||
real_cert_value = get_cert_value (svFileGetName (ifcfg), privkey, &scheme);
|
||||
if (phase2) {
|
||||
if (!nm_setting_802_1x_set_phase2_private_key (s_8021x,
|
||||
real_cert_value,
|
||||
privkey_password,
|
||||
scheme,
|
||||
&privkey_format,
|
||||
error))
|
||||
return FALSE;
|
||||
} else {
|
||||
if (!nm_setting_802_1x_set_private_key (s_8021x,
|
||||
real_cert_value,
|
||||
privkey_password,
|
||||
scheme,
|
||||
&privkey_format,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only set the client certificate if the private key is not PKCS#12 format,
|
||||
* as NM (due to supplicant restrictions) requires. If the key was PKCS#12,
|
||||
* then nm_setting_802_1x_set_private_key() already set the client certificate
|
||||
* to the same value as the private key.
|
||||
*/
|
||||
if (privkey_format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
|
||||
gs_free char *real_cert_value = NULL;
|
||||
gs_free char *client_cert = NULL;
|
||||
|
||||
client_cert = svGetValueStr_cp (ifcfg, cli_cert_key);
|
||||
if (!client_cert) {
|
||||
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
|
||||
"Missing %s for EAP method '%s'.",
|
||||
cli_cert_key,
|
||||
eap_method);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
real_cert_value = get_cert_value (svFileGetName (ifcfg), client_cert, &scheme);
|
||||
if (phase2) {
|
||||
if (!nm_setting_802_1x_set_phase2_client_cert (s_8021x, real_cert_value, scheme, NULL, error))
|
||||
return FALSE;
|
||||
} else {
|
||||
if (!nm_setting_802_1x_set_client_cert (s_8021x, real_cert_value, scheme, NULL, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
|
||||
flags = read_secret_flags (ifcfg, cli_cert_pw_flags_key);
|
||||
g_object_set (s_8021x, cli_cert_pw_flags_prop, flags, NULL);
|
||||
|
||||
if (flags == NM_SETTING_SECRET_FLAG_NONE) {
|
||||
client_cert_password = svGetValueStr_cp (ifcfg, cli_cert_pw_key);
|
||||
g_object_set (s_8021x, cli_cert_pw_prop, client_cert_password, NULL);
|
||||
}
|
||||
}
|
||||
if (!_cert_set_from_ifcfg (s_8021x,
|
||||
ifcfg,
|
||||
phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT",
|
||||
phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT : NM_SETTING_802_1X_CLIENT_CERT,
|
||||
&client_cert,
|
||||
error))
|
||||
return FALSE;
|
||||
/* FIXME: writer does not actually write IEEE_8021X_CLIENT_CERT_PASSWORD and other
|
||||
* certificate related passwords. It should, because otherwise persisting such profiles
|
||||
* to ifcfg looses information. As this currently only matters for PKCS11 URIs, it seems
|
||||
* a seldomly used feature so that it is not fixed yet. */
|
||||
_secret_set_from_ifcfg (s_8021x,
|
||||
ifcfg,
|
||||
keys_ifcfg,
|
||||
phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD" : "IEEE_8021X_CLIENT_CERT_PASSWORD",
|
||||
phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD);
|
||||
if (!client_cert) {
|
||||
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
|
||||
"Missing certificate for EAP method '%s'.",
|
||||
eap_method);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
|
@ -3118,7 +3163,7 @@ eap_tls_reader (const char *eap_method,
|
|||
static gboolean
|
||||
eap_peap_reader (const char *eap_method,
|
||||
shvarFile *ifcfg,
|
||||
shvarFile *keys,
|
||||
shvarFile *keys_ifcfg,
|
||||
NMSetting8021x *s_8021x,
|
||||
gboolean phase2,
|
||||
GError **error)
|
||||
|
|
@ -3127,19 +3172,19 @@ eap_peap_reader (const char *eap_method,
|
|||
const char *v;
|
||||
gs_free const char **list = NULL;
|
||||
const char *const *iter;
|
||||
NMSetting8021xCKScheme scheme;
|
||||
|
||||
v = svGetValueStr (ifcfg, "IEEE_8021X_CA_CERT", &value);
|
||||
if (v) {
|
||||
gs_free char *real_cert_value = NULL;
|
||||
|
||||
real_cert_value = get_cert_value (svFileGetName (ifcfg), v, &scheme);
|
||||
if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error))
|
||||
return FALSE;
|
||||
} else {
|
||||
PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!",
|
||||
eap_method);
|
||||
}
|
||||
if (!_cert_set_from_ifcfg (s_8021x,
|
||||
ifcfg,
|
||||
"IEEE_8021X_CA_CERT",
|
||||
NM_SETTING_802_1X_CA_CERT,
|
||||
NULL,
|
||||
error))
|
||||
return FALSE;
|
||||
_secret_set_from_ifcfg (s_8021x,
|
||||
ifcfg,
|
||||
keys_ifcfg,
|
||||
"IEEE_8021X_CA_CERT_PASSWORD",
|
||||
NM_SETTING_802_1X_CA_CERT_PASSWORD);
|
||||
|
||||
nm_clear_g_free (&value);
|
||||
v = svGetValueStr (ifcfg, "IEEE_8021X_PEAP_VERSION", &value);
|
||||
|
|
@ -3179,10 +3224,10 @@ eap_peap_reader (const char *eap_method,
|
|||
if (NM_IN_STRSET (*iter, "MSCHAPV2",
|
||||
"MD5",
|
||||
"GTC")) {
|
||||
if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
|
||||
if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
|
||||
return FALSE;
|
||||
} else if (nm_streq (*iter, "TLS")) {
|
||||
if (!eap_tls_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
|
||||
if (!eap_tls_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
|
||||
return FALSE;
|
||||
} else {
|
||||
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
|
||||
|
|
@ -3211,7 +3256,7 @@ eap_peap_reader (const char *eap_method,
|
|||
static gboolean
|
||||
eap_ttls_reader (const char *eap_method,
|
||||
shvarFile *ifcfg,
|
||||
shvarFile *keys,
|
||||
shvarFile *keys_ifcfg,
|
||||
NMSetting8021x *s_8021x,
|
||||
gboolean phase2,
|
||||
GError **error)
|
||||
|
|
@ -3221,19 +3266,19 @@ eap_ttls_reader (const char *eap_method,
|
|||
const char *v;
|
||||
gs_free const char **list = NULL;
|
||||
const char *const *iter;
|
||||
NMSetting8021xCKScheme scheme;
|
||||
|
||||
v = svGetValueStr (ifcfg, "IEEE_8021X_CA_CERT", &value);
|
||||
if (v) {
|
||||
gs_free char *real_cert_value = NULL;
|
||||
|
||||
real_cert_value = get_cert_value (svFileGetName (ifcfg), v, &scheme);
|
||||
if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error))
|
||||
return FALSE;
|
||||
} else {
|
||||
PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!",
|
||||
eap_method);
|
||||
}
|
||||
if (!_cert_set_from_ifcfg (s_8021x,
|
||||
ifcfg,
|
||||
"IEEE_8021X_CA_CERT",
|
||||
NM_SETTING_802_1X_CA_CERT,
|
||||
NULL,
|
||||
error))
|
||||
return FALSE;
|
||||
_secret_set_from_ifcfg (s_8021x,
|
||||
ifcfg,
|
||||
keys_ifcfg,
|
||||
"IEEE_8021X_CA_CERT_PASSWORD",
|
||||
NM_SETTING_802_1X_CA_CERT_PASSWORD);
|
||||
|
||||
nm_clear_g_free (&value);
|
||||
v = svGetValueStr (ifcfg, "IEEE_8021X_ANON_IDENTITY", &value);
|
||||
|
|
@ -3258,17 +3303,17 @@ eap_ttls_reader (const char *eap_method,
|
|||
"mschap",
|
||||
"pap",
|
||||
"chap")) {
|
||||
if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
|
||||
if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
|
||||
return FALSE;
|
||||
g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, *iter, NULL);
|
||||
} else if (nm_streq (*iter, "eap-tls")) {
|
||||
if (!eap_tls_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
|
||||
if (!eap_tls_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
|
||||
return FALSE;
|
||||
g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, "tls", NULL);
|
||||
} else if (NM_IN_STRSET (*iter, "eap-mschapv2",
|
||||
"eap-md5",
|
||||
"eap-gtc")) {
|
||||
if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
|
||||
if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
|
||||
return FALSE;
|
||||
g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, (*iter + NM_STRLEN ("eap-")), NULL);
|
||||
} else {
|
||||
|
|
@ -3285,7 +3330,7 @@ eap_ttls_reader (const char *eap_method,
|
|||
static gboolean
|
||||
eap_fast_reader (const char *eap_method,
|
||||
shvarFile *ifcfg,
|
||||
shvarFile *keys,
|
||||
shvarFile *keys_ifcfg,
|
||||
NMSetting8021x *s_8021x,
|
||||
gboolean phase2,
|
||||
GError **error)
|
||||
|
|
@ -3351,7 +3396,7 @@ eap_fast_reader (const char *eap_method,
|
|||
if (iter) {
|
||||
if ( !strcmp (*iter, "MSCHAPV2")
|
||||
|| !strcmp (*iter, "GTC")) {
|
||||
if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
|
||||
if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
|
||||
goto done;
|
||||
} else {
|
||||
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
|
||||
|
|
@ -3386,7 +3431,7 @@ typedef struct {
|
|||
const char *method;
|
||||
gboolean (*reader) (const char *eap_method,
|
||||
shvarFile *ifcfg,
|
||||
shvarFile *keys,
|
||||
shvarFile *keys_ifcfg,
|
||||
NMSetting8021x *s_8021x,
|
||||
gboolean phase2,
|
||||
GError **error);
|
||||
|
|
@ -3438,7 +3483,7 @@ fill_8021x (shvarFile *ifcfg,
|
|||
gboolean wifi,
|
||||
GError **error)
|
||||
{
|
||||
nm_auto_shvar_file_close shvarFile *keys = NULL;
|
||||
nm_auto_shvar_file_close shvarFile *keys_ifcfg = NULL;
|
||||
gs_unref_object NMSetting8021x *s_8021x = NULL;
|
||||
gs_free char *value = NULL;
|
||||
const char *v;
|
||||
|
|
@ -3459,8 +3504,8 @@ fill_8021x (shvarFile *ifcfg,
|
|||
|
||||
s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
|
||||
|
||||
/* Read in the lookaside keys file, if present */
|
||||
keys = utils_get_keys_ifcfg (file, FALSE);
|
||||
/* Read in the lookaside keys_ifcfg file, if present */
|
||||
keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
|
||||
|
||||
/* Validate and handle each EAP method */
|
||||
for (iter = list; iter && *iter; iter++) {
|
||||
|
|
@ -3484,7 +3529,7 @@ fill_8021x (shvarFile *ifcfg,
|
|||
}
|
||||
|
||||
/* Parse EAP method specific options */
|
||||
if (!(*eap->reader)(lower, ifcfg, keys, s_8021x, FALSE, error))
|
||||
if (!(*eap->reader)(lower, ifcfg, keys_ifcfg, s_8021x, FALSE, error))
|
||||
return NULL;
|
||||
|
||||
nm_setting_802_1x_add_eap_method (s_8021x, lower);
|
||||
|
|
@ -3600,7 +3645,7 @@ make_wpa_setting (shvarFile *ifcfg,
|
|||
if (wpa_psk) {
|
||||
NMSettingSecretFlags psk_flags;
|
||||
|
||||
psk_flags = read_secret_flags (ifcfg, "WPA_PSK_FLAGS");
|
||||
psk_flags = _secret_read_ifcfg_flags (ifcfg, "WPA_PSK_FLAGS");
|
||||
g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS, psk_flags, NULL);
|
||||
|
||||
/* Read PSK if it's system-owned */
|
||||
|
|
@ -3687,7 +3732,7 @@ make_leap_setting (shvarFile *ifcfg,
|
|||
return NULL; /* Not LEAP */
|
||||
nm_clear_g_free (&value);
|
||||
|
||||
flags = read_secret_flags (ifcfg, "IEEE_8021X_PASSWORD_FLAGS");
|
||||
flags = _secret_read_ifcfg_flags (ifcfg, "IEEE_8021X_PASSWORD_FLAGS");
|
||||
g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, flags, NULL);
|
||||
|
||||
/* Read LEAP password if it's system-owned */
|
||||
|
|
@ -5459,7 +5504,7 @@ connection_from_file_full (const char *filename,
|
|||
GError **error,
|
||||
gboolean *out_ignore_error)
|
||||
{
|
||||
nm_auto_shvar_file_close shvarFile *parsed = NULL;
|
||||
nm_auto_shvar_file_close shvarFile *main_ifcfg = NULL;
|
||||
nm_auto_shvar_file_close shvarFile *network_ifcfg = NULL;
|
||||
gs_unref_object NMConnection *connection = NULL;
|
||||
gs_free char *type = NULL;
|
||||
|
|
@ -5487,14 +5532,14 @@ connection_from_file_full (const char *filename,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
parsed = svOpenFile (filename, error);
|
||||
if (!parsed)
|
||||
main_ifcfg = svOpenFile (filename, error);
|
||||
if (!main_ifcfg)
|
||||
return NULL;
|
||||
|
||||
network_ifcfg = svOpenFile (network_file, NULL);
|
||||
|
||||
if (!svGetValueBoolean (parsed, "NM_CONTROLLED", TRUE)) {
|
||||
connection = create_unhandled_connection (filename, parsed, "unmanaged", out_unhandled);
|
||||
if (!svGetValueBoolean (main_ifcfg, "NM_CONTROLLED", TRUE)) {
|
||||
connection = create_unhandled_connection (filename, main_ifcfg, "unmanaged", out_unhandled);
|
||||
if (!connection) {
|
||||
NM_SET_OUT (out_ignore_error, TRUE);
|
||||
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
|
||||
|
|
@ -5504,7 +5549,7 @@ connection_from_file_full (const char *filename,
|
|||
}
|
||||
|
||||
/* iBFT is handled by the iBFT settings plugin */
|
||||
bootproto = svGetValueStr_cp (parsed, "BOOTPROTO");
|
||||
bootproto = svGetValueStr_cp (main_ifcfg, "BOOTPROTO");
|
||||
if (bootproto && !g_ascii_strcasecmp (bootproto, "ibft")) {
|
||||
NM_SET_OUT (out_ignore_error, TRUE);
|
||||
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
|
||||
|
|
@ -5514,19 +5559,19 @@ connection_from_file_full (const char *filename,
|
|||
}
|
||||
g_free (bootproto);
|
||||
|
||||
devtype = svGetValueStr_cp (parsed, "DEVICETYPE");
|
||||
devtype = svGetValueStr_cp (main_ifcfg, "DEVICETYPE");
|
||||
if (devtype) {
|
||||
if (!strcasecmp (devtype, TYPE_TEAM))
|
||||
type = g_strdup (TYPE_TEAM);
|
||||
else if (!strcasecmp (devtype, TYPE_TEAM_PORT)) {
|
||||
gs_free char *device = NULL;
|
||||
|
||||
type = svGetValueStr_cp (parsed, "TYPE");
|
||||
device = svGetValueStr_cp (parsed, "DEVICE");
|
||||
type = svGetValueStr_cp (main_ifcfg, "TYPE");
|
||||
device = svGetValueStr_cp (main_ifcfg, "DEVICE");
|
||||
|
||||
if (type) {
|
||||
/* nothing to do */
|
||||
} else if (device && is_vlan_device (device, parsed))
|
||||
} else if (device && is_vlan_device (device, main_ifcfg))
|
||||
type = g_strdup (TYPE_VLAN);
|
||||
else
|
||||
type = g_strdup (TYPE_ETHERNET);
|
||||
|
|
@ -5539,26 +5584,26 @@ connection_from_file_full (const char *filename,
|
|||
/* Team and TeamPort types are also accepted by the mere
|
||||
* presence of TEAM_CONFIG/TEAM_MASTER. They don't require
|
||||
* DEVICETYPE. */
|
||||
t = svGetValueStr_cp (parsed, "TEAM_CONFIG");
|
||||
t = svGetValueStr_cp (main_ifcfg, "TEAM_CONFIG");
|
||||
if (t)
|
||||
type = g_strdup (TYPE_TEAM);
|
||||
}
|
||||
|
||||
if (!type)
|
||||
type = svGetValueStr_cp (parsed, "TYPE");
|
||||
type = svGetValueStr_cp (main_ifcfg, "TYPE");
|
||||
|
||||
if (!type) {
|
||||
gs_free char *tmp = NULL;
|
||||
char *device;
|
||||
|
||||
if ((tmp = svGetValueStr_cp (parsed, "IPV6TUNNELIPV4"))) {
|
||||
if ((tmp = svGetValueStr_cp (main_ifcfg, "IPV6TUNNELIPV4"))) {
|
||||
NM_SET_OUT (out_ignore_error, TRUE);
|
||||
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
|
||||
"Ignoring unsupported connection due to IPV6TUNNELIPV4");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device = svGetValueStr_cp (parsed, "DEVICE");
|
||||
device = svGetValueStr_cp (main_ifcfg, "DEVICE");
|
||||
if (!device) {
|
||||
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
|
||||
"File '%s' had neither TYPE nor DEVICE keys.", filename);
|
||||
|
|
@ -5574,11 +5619,11 @@ connection_from_file_full (const char *filename,
|
|||
}
|
||||
|
||||
if (!test_type) {
|
||||
if (is_bond_device (device, parsed))
|
||||
if (is_bond_device (device, main_ifcfg))
|
||||
type = g_strdup (TYPE_BOND);
|
||||
else if (is_vlan_device (device, parsed))
|
||||
else if (is_vlan_device (device, main_ifcfg))
|
||||
type = g_strdup (TYPE_VLAN);
|
||||
else if (is_wifi_device (device, parsed))
|
||||
else if (is_wifi_device (device, main_ifcfg))
|
||||
type = g_strdup (TYPE_WIRELESS);
|
||||
else {
|
||||
gs_free char *p_path = NULL;
|
||||
|
|
@ -5644,14 +5689,14 @@ connection_from_file_full (const char *filename,
|
|||
if (nm_streq0 (type, TYPE_ETHERNET)) {
|
||||
gs_free char *bond_options = NULL;
|
||||
|
||||
if (svGetValueStr (parsed, "BONDING_OPTS", &bond_options)) {
|
||||
if (svGetValueStr (main_ifcfg, "BONDING_OPTS", &bond_options)) {
|
||||
/* initscripts consider these as bond masters */
|
||||
g_free (type);
|
||||
type = g_strdup (TYPE_BOND);
|
||||
}
|
||||
}
|
||||
|
||||
if (svGetValueBoolean (parsed, "BONDING_MASTER", FALSE) &&
|
||||
if (svGetValueBoolean (main_ifcfg, "BONDING_MASTER", FALSE) &&
|
||||
strcasecmp (type, TYPE_BOND)) {
|
||||
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
|
||||
"BONDING_MASTER=yes key only allowed in TYPE=bond connections");
|
||||
|
|
@ -5660,21 +5705,21 @@ connection_from_file_full (const char *filename,
|
|||
|
||||
/* Construct the connection */
|
||||
if (!strcasecmp (type, TYPE_ETHERNET))
|
||||
connection = wired_connection_from_ifcfg (filename, parsed, error);
|
||||
connection = wired_connection_from_ifcfg (filename, main_ifcfg, error);
|
||||
else if (!strcasecmp (type, TYPE_WIRELESS))
|
||||
connection = wireless_connection_from_ifcfg (filename, parsed, error);
|
||||
connection = wireless_connection_from_ifcfg (filename, main_ifcfg, error);
|
||||
else if (!strcasecmp (type, TYPE_INFINIBAND))
|
||||
connection = infiniband_connection_from_ifcfg (filename, parsed, error);
|
||||
connection = infiniband_connection_from_ifcfg (filename, main_ifcfg, error);
|
||||
else if (!strcasecmp (type, TYPE_BOND))
|
||||
connection = bond_connection_from_ifcfg (filename, parsed, error);
|
||||
connection = bond_connection_from_ifcfg (filename, main_ifcfg, error);
|
||||
else if (!strcasecmp (type, TYPE_TEAM))
|
||||
connection = team_connection_from_ifcfg (filename, parsed, error);
|
||||
connection = team_connection_from_ifcfg (filename, main_ifcfg, error);
|
||||
else if (!strcasecmp (type, TYPE_VLAN))
|
||||
connection = vlan_connection_from_ifcfg (filename, parsed, error);
|
||||
connection = vlan_connection_from_ifcfg (filename, main_ifcfg, error);
|
||||
else if (!strcasecmp (type, TYPE_BRIDGE))
|
||||
connection = bridge_connection_from_ifcfg (filename, parsed, error);
|
||||
connection = bridge_connection_from_ifcfg (filename, main_ifcfg, error);
|
||||
else {
|
||||
connection = create_unhandled_connection (filename, parsed, "unrecognized", out_unhandled);
|
||||
connection = create_unhandled_connection (filename, main_ifcfg, "unrecognized", out_unhandled);
|
||||
if (!connection) {
|
||||
PARSE_WARNING ("connection type was unrecognized but device was not uniquely identified; device may be managed");
|
||||
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
|
||||
|
|
@ -5686,7 +5731,7 @@ connection_from_file_full (const char *filename,
|
|||
if (!connection)
|
||||
return NULL;
|
||||
|
||||
parse_ethtool_options (parsed, connection);
|
||||
parse_ethtool_options (main_ifcfg, connection);
|
||||
|
||||
has_complex_routes_v4 = utils_has_complex_routes (filename, AF_INET);
|
||||
has_complex_routes_v6 = utils_has_complex_routes (filename, AF_INET6);
|
||||
|
|
@ -5700,7 +5745,7 @@ connection_from_file_full (const char *filename,
|
|||
PARSE_WARNING ("'rule-' and 'rule6-' files are present; you will need to use a dispatcher script to apply these routes");
|
||||
}
|
||||
|
||||
s_ip6 = make_ip6_setting (parsed,
|
||||
s_ip6 = make_ip6_setting (main_ifcfg,
|
||||
network_ifcfg,
|
||||
!has_complex_routes_v4 && !has_complex_routes_v6,
|
||||
error);
|
||||
|
|
@ -5709,7 +5754,7 @@ connection_from_file_full (const char *filename,
|
|||
else
|
||||
nm_connection_add_setting (connection, s_ip6);
|
||||
|
||||
s_ip4 = make_ip4_setting (parsed,
|
||||
s_ip4 = make_ip4_setting (main_ifcfg,
|
||||
network_ifcfg,
|
||||
!has_complex_routes_v4 && !has_complex_routes_v6,
|
||||
&has_ip4_defroute,
|
||||
|
|
@ -5723,11 +5768,11 @@ connection_from_file_full (const char *filename,
|
|||
nm_connection_add_setting (connection, s_ip4);
|
||||
}
|
||||
|
||||
s_sriov = make_sriov_setting (parsed);
|
||||
s_sriov = make_sriov_setting (main_ifcfg);
|
||||
if (s_sriov)
|
||||
nm_connection_add_setting (connection, s_sriov);
|
||||
|
||||
s_tc = make_tc_setting (parsed);
|
||||
s_tc = make_tc_setting (main_ifcfg);
|
||||
if (s_tc)
|
||||
nm_connection_add_setting (connection, s_tc);
|
||||
|
||||
|
|
@ -5735,31 +5780,31 @@ connection_from_file_full (const char *filename,
|
|||
* config fails for some reason, we read DOMAIN and put the
|
||||
* values into IPv6 config instead of IPv4.
|
||||
*/
|
||||
check_dns_search_domains (parsed, s_ip4, s_ip6);
|
||||
check_dns_search_domains (main_ifcfg, s_ip4, s_ip6);
|
||||
|
||||
s_proxy = make_proxy_setting (parsed);
|
||||
s_proxy = make_proxy_setting (main_ifcfg);
|
||||
if (s_proxy)
|
||||
nm_connection_add_setting (connection, s_proxy);
|
||||
|
||||
s_user = make_user_setting (parsed);
|
||||
s_user = make_user_setting (main_ifcfg);
|
||||
if (s_user)
|
||||
nm_connection_add_setting (connection, s_user);
|
||||
|
||||
s_match = make_match_setting (parsed);
|
||||
s_match = make_match_setting (main_ifcfg);
|
||||
if (s_match)
|
||||
nm_connection_add_setting (connection, s_match);
|
||||
|
||||
/* Bridge port? */
|
||||
s_port = make_bridge_port_setting (parsed);
|
||||
s_port = make_bridge_port_setting (main_ifcfg);
|
||||
if (s_port)
|
||||
nm_connection_add_setting (connection, s_port);
|
||||
|
||||
/* Team port? */
|
||||
s_port = make_team_port_setting (parsed);
|
||||
s_port = make_team_port_setting (main_ifcfg);
|
||||
if (s_port)
|
||||
nm_connection_add_setting (connection, s_port);
|
||||
|
||||
if (!make_dcb_setting (parsed, &s_dcb, error))
|
||||
if (!make_dcb_setting (main_ifcfg, &s_dcb, error))
|
||||
return NULL;
|
||||
if (s_dcb)
|
||||
nm_connection_add_setting (connection, s_dcb);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#include "nm-utils/nm-enum-utils.h"
|
||||
#include "nm-utils/nm-io-utils.h"
|
||||
#include "nm-manager.h"
|
||||
#include "nm-setting-connection.h"
|
||||
#include "nm-setting-wired.h"
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
#include "nm-core-internal.h"
|
||||
#include "nm-core-utils.h"
|
||||
#include "nm-utils/nm-enum-utils.h"
|
||||
#include "nm-utils/nm-io-utils.h"
|
||||
#include "c-list/src/c-list.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
@ -821,6 +822,7 @@ svOpenFileInternal (const char *name, gboolean create, GError **error)
|
|||
if (nm_utils_fd_get_contents (closefd ? nm_steal_fd (&fd) : fd,
|
||||
closefd,
|
||||
10 * 1024 * 1024,
|
||||
NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
|
||||
&arena,
|
||||
NULL,
|
||||
&local) < 0) {
|
||||
|
|
|
|||
|
|
@ -1901,10 +1901,8 @@ test_read_write_802_1X_subj_matches (void)
|
|||
gs_unref_object NMConnection *reread = NULL;
|
||||
NMSetting8021x *s_8021x;
|
||||
|
||||
NMTST_EXPECT_NM_WARN ("*missing IEEE_8021X_CA_CERT*peap*");
|
||||
connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-802-1X-subj-matches",
|
||||
NULL, TYPE_ETHERNET, NULL);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
/* ===== 802.1x SETTING ===== */
|
||||
s_8021x = nm_connection_get_setting_802_1x (connection);
|
||||
|
|
@ -1922,16 +1920,12 @@ test_read_write_802_1X_subj_matches (void)
|
|||
g_assert_cmpstr (nm_setting_802_1x_get_phase2_altsubject_match (s_8021x, 0), ==, "x.yourdomain.tld");
|
||||
g_assert_cmpstr (nm_setting_802_1x_get_phase2_altsubject_match (s_8021x, 1), ==, "y.yourdomain.tld");
|
||||
|
||||
NMTST_EXPECT_NM_WARN ("*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!");
|
||||
_writer_new_connec_exp (connection,
|
||||
TEST_SCRATCH_DIR,
|
||||
TEST_IFCFG_DIR"/ifcfg-System_test-wired-802-1X-subj-matches.cexpected",
|
||||
&testfile);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
NMTST_EXPECT_NM_WARN ("*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!");
|
||||
reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
#include "nms-keyfile-utils.h"
|
||||
#include "nms-keyfile-reader.h"
|
||||
|
||||
#include "nm-utils/nm-io-utils.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue