libnm: normalize "connection.uuid"
For NetworkManager profiles, "connection.uuid" is the identifier of the profile. It is supposed to be a UUID, however: - the UUID was not ensured to be all-lower case. We should make sure that our UUIDs are in a consistent manner, so that users can rely on the format of the string. - the UUID was never actually interpreted as a UUID. It only was some opaque string, that we use as identifier. We had nm_utils_is_uuid() which checks that the format is valid, however that did not fully validate the format, like it would accept "----7daf444dd78741a59e1ef1b3c8b1c0e8" and "549fac10a25f4bcc912d1ae688c2b4987daf444d" (40 hex characters). Both invalid UUIDs and non-normalized UUID should be normalized. We don't want to break existing profiles that use such UUIDs, thus we don't outright reject them. Let's instead mangle them during nm_connection_normalize().
This commit is contained in:
3
NEWS
3
NEWS
@@ -13,6 +13,9 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
|
||||
mode).
|
||||
* nmcli: fix setting property aliases to empty value to reset the
|
||||
default value.
|
||||
* Enforce valid "connection.uuid" by normalizing the string to lower
|
||||
case. This changes the UUID of existing profiles that had an invalid,
|
||||
non-normalized value.
|
||||
|
||||
=============================================
|
||||
NetworkManager-1.30
|
||||
|
@@ -704,14 +704,24 @@ static gboolean
|
||||
_normalize_connection_uuid(NMConnection *self)
|
||||
{
|
||||
NMSettingConnection *s_con = nm_connection_get_setting_connection(self);
|
||||
char uuid[37];
|
||||
char uuid_normalized[37];
|
||||
const char * uuid;
|
||||
|
||||
nm_assert(s_con);
|
||||
|
||||
if (nm_setting_connection_get_uuid(s_con))
|
||||
return FALSE;
|
||||
uuid = nm_setting_connection_get_uuid(s_con);
|
||||
|
||||
g_object_set(s_con, NM_SETTING_CONNECTION_UUID, nm_uuid_generate_random_str_arr(uuid), NULL);
|
||||
if (uuid) {
|
||||
gboolean uuid_is_normalized;
|
||||
|
||||
if (!nm_uuid_is_valid_nm(uuid, &uuid_is_normalized, uuid_normalized))
|
||||
return nm_assert_unreachable_val(FALSE);
|
||||
if (!uuid_is_normalized)
|
||||
return FALSE;
|
||||
} else
|
||||
nm_uuid_generate_random_str_arr(uuid_normalized);
|
||||
|
||||
g_object_set(s_con, NM_SETTING_CONNECTION_UUID, uuid_normalized, NULL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "nm-setting-connection.h"
|
||||
|
||||
#include "libnm-glib-aux/nm-uuid.h"
|
||||
#include "libnm-core-aux-intern/nm-common-macros.h"
|
||||
#include "nm-utils.h"
|
||||
#include "nm-utils-private.h"
|
||||
@@ -1065,6 +1066,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
|
||||
const char * normerr_missing_slave_type = NULL;
|
||||
const char * normerr_missing_slave_type_port = NULL;
|
||||
gboolean normerr_base_setting = FALSE;
|
||||
gboolean uuid_was_normalized = FALSE;
|
||||
|
||||
if (!priv->id) {
|
||||
g_set_error_literal(error,
|
||||
@@ -1088,7 +1090,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (priv->uuid && !nm_utils_is_uuid(priv->uuid)) {
|
||||
if (priv->uuid && !nm_uuid_is_valid_nm(priv->uuid, &uuid_was_normalized, NULL)) {
|
||||
g_set_error(error,
|
||||
NM_CONNECTION_ERROR,
|
||||
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
||||
@@ -1460,6 +1462,18 @@ after_interface_name:
|
||||
}
|
||||
}
|
||||
|
||||
if (uuid_was_normalized) {
|
||||
g_set_error_literal(error,
|
||||
NM_CONNECTION_ERROR,
|
||||
NM_CONNECTION_ERROR_MISSING_PROPERTY,
|
||||
_("UUID needs normalization"));
|
||||
g_prefix_error(error,
|
||||
"%s.%s: ",
|
||||
NM_SETTING_CONNECTION_SETTING_NAME,
|
||||
NM_SETTING_CONNECTION_UUID);
|
||||
return NM_SETTING_VERIFY_NORMALIZABLE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@@ -238,6 +238,8 @@ _do_test_connection_uuid(NMConnection *con, const char *uuid, const char *expect
|
||||
NMSettingConnection *s_con;
|
||||
gs_free char * uuid_old = NULL;
|
||||
gboolean success;
|
||||
gboolean is_normalized;
|
||||
char uuid_normalized[37];
|
||||
|
||||
nmtst_assert_connection_verifies_without_normalization(con);
|
||||
|
||||
@@ -257,6 +259,9 @@ _do_test_connection_uuid(NMConnection *con, const char *uuid, const char *expect
|
||||
if (nm_streq0(uuid, expected_uuid)) {
|
||||
nmtst_assert_connection_verifies_without_normalization(con);
|
||||
g_assert(nm_utils_is_uuid(uuid));
|
||||
g_assert(nm_uuid_is_valid(uuid));
|
||||
g_assert(nm_uuid_is_valid_nm(uuid, &is_normalized, NULL));
|
||||
g_assert(!is_normalized);
|
||||
} else if (!expected_uuid) {
|
||||
gs_free_error GError *error = NULL;
|
||||
|
||||
@@ -265,11 +270,35 @@ _do_test_connection_uuid(NMConnection *con, const char *uuid, const char *expect
|
||||
g_assert_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
|
||||
|
||||
g_assert(!nm_utils_is_uuid(uuid));
|
||||
} else
|
||||
g_assert_not_reached();
|
||||
g_assert(!nm_uuid_is_valid(uuid));
|
||||
g_assert(!nm_uuid_is_valid_nmlegacy(uuid));
|
||||
g_assert(!nm_uuid_is_valid_nm(uuid, NULL, NULL));
|
||||
} else {
|
||||
gs_free_error GError *error = NULL;
|
||||
|
||||
nmtst_assert_connection_verifies_and_normalizable(con);
|
||||
|
||||
success = nm_connection_verify(con, &error);
|
||||
nmtst_assert_success(success, error);
|
||||
|
||||
if (!nmtst_connection_normalize(con))
|
||||
g_assert_not_reached();
|
||||
|
||||
g_assert_cmpstr(expected_uuid, ==, nm_setting_connection_get_uuid(s_con));
|
||||
g_assert(nm_uuid_is_valid(expected_uuid));
|
||||
|
||||
g_assert(nm_utils_is_uuid(uuid));
|
||||
g_assert(nm_uuid_is_valid_nmlegacy(uuid));
|
||||
g_assert(nm_uuid_is_valid_nm(uuid, &is_normalized, uuid_normalized));
|
||||
|
||||
g_assert_cmpstr(expected_uuid, ==, uuid_normalized);
|
||||
}
|
||||
|
||||
g_object_set(s_con, NM_SETTING_CONNECTION_UUID, uuid_old, NULL);
|
||||
nmtst_assert_connection_verifies_without_normalization(con);
|
||||
|
||||
if (expected_uuid && !nm_streq(expected_uuid, uuid))
|
||||
_do_test_connection_uuid(con, expected_uuid, expected_uuid);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -286,19 +315,36 @@ test_connection_uuid(void)
|
||||
#define _do_test_connection_uuid_good(con, uuid) \
|
||||
_do_test_connection_uuid((con), "" uuid "", "" uuid "")
|
||||
|
||||
#define _do_test_connection_uuid_norm(con, uuid, expected_uuid) \
|
||||
_do_test_connection_uuid((con), "" uuid "", "" expected_uuid "")
|
||||
|
||||
_do_test_connection_uuid_bad(con, "x1e775e3-a316-4eb2-b4d8-4b0f2bcaea53");
|
||||
_do_test_connection_uuid_bad(con, "1e775e3aa3164eb2b4d84b0f2bcaea53abcdabc");
|
||||
_do_test_connection_uuid_bad(con, "1e775e3aa3164eb2b4d84b0f2bcaea53abcdabcdd");
|
||||
|
||||
_do_test_connection_uuid_good(con, "a1e775e3-a316-4eb2-b4d8-4b0f2bcaea53");
|
||||
|
||||
_do_test_connection_uuid_good(con, "A1E775e3-a316-4eb2-b4d8-4b0f2bcaea53");
|
||||
_do_test_connection_uuid_good(con, "A1E775E3-A316-4EB2-B4D8-4B0F2BCAEA53");
|
||||
_do_test_connection_uuid_good(con, "-1e775e3aa316-4eb2-b4d8-4b0f2bcaea53");
|
||||
_do_test_connection_uuid_good(con, "----1e775e3aa3164eb2b4d84b0f2bcaea53");
|
||||
_do_test_connection_uuid_good(con, "1e775e3aa3164eb2b4d84b0f2bcaea53abcdabcd");
|
||||
_do_test_connection_uuid_good(con, "1e775e3Aa3164eb2b4d84b0f2bcaea53abcdabcd");
|
||||
_do_test_connection_uuid_good(con, "1E775E3AA3164EB2B4D84B0F2BCAEA53ABCDABCD");
|
||||
_do_test_connection_uuid_norm(con,
|
||||
"A1E775e3-a316-4eb2-b4d8-4b0f2bcaea53",
|
||||
"a1e775e3-a316-4eb2-b4d8-4b0f2bcaea53");
|
||||
_do_test_connection_uuid_norm(con,
|
||||
"A1E775E3-A316-4EB2-B4D8-4B0F2BCAEA53",
|
||||
"a1e775e3-a316-4eb2-b4d8-4b0f2bcaea53");
|
||||
_do_test_connection_uuid_norm(con,
|
||||
"-1e775e3aa316-4eb2-b4d8-4b0f2bcaea53",
|
||||
"bdd73688-5c87-5454-917d-f5c3faed39c0");
|
||||
_do_test_connection_uuid_norm(con,
|
||||
"----1e775e3aa3164eb2b4d84b0f2bcaea53",
|
||||
"8a232814-c6cf-54c9-9384-71a60011d0b2");
|
||||
_do_test_connection_uuid_norm(con,
|
||||
"1e775e3aa3164eb2b4d84b0f2bcaea53abcdabcd",
|
||||
"ae35a4a8-4029-5770-9fa4-d79a672874c3");
|
||||
_do_test_connection_uuid_norm(con,
|
||||
"1e775e3Aa3164eb2b4d84b0f2bcaea53abcdabcd",
|
||||
"ae35a4a8-4029-5770-9fa4-d79a672874c3");
|
||||
_do_test_connection_uuid_norm(con,
|
||||
"1E775E3AA3164EB2B4D84B0F2BCAEA53ABCDABCD",
|
||||
"ae35a4a8-4029-5770-9fa4-d79a672874c3");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@@ -158,6 +158,9 @@ nm_uuid_is_valid_nmlegacy(const char *str)
|
||||
p++;
|
||||
}
|
||||
|
||||
/* While we accept here bogus strings as UUIDs, they must contain only
|
||||
* hexdigits and '-', and they must be eithr 36 or 40 chars long. */
|
||||
|
||||
if ((num_dashes == 4) && (p - str == 36))
|
||||
return TRUE;
|
||||
|
||||
@@ -170,6 +173,68 @@ nm_uuid_is_valid_nmlegacy(const char *str)
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
gboolean
|
||||
nm_uuid_is_valid_nm(const char *str,
|
||||
gboolean * out_normalized,
|
||||
char * out_normalized_str /* [static 37] */)
|
||||
{
|
||||
NMUuid uuid;
|
||||
gboolean is_normalized;
|
||||
|
||||
if (!str)
|
||||
return FALSE;
|
||||
|
||||
if (nm_uuid_parse_full(str, &uuid, &is_normalized)) {
|
||||
if (is_normalized) {
|
||||
/* @str is a normalized (lower-case), valid UUID. Nothing to normalize,
|
||||
* and return success. */
|
||||
NM_SET_OUT(out_normalized, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* @str is a valid UUID, but not normalized. That means that it's
|
||||
* upper case. Normalize the UUID. */
|
||||
NM_SET_OUT(out_normalized, TRUE);
|
||||
if (out_normalized_str)
|
||||
nm_uuid_unparse(&uuid, out_normalized_str);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (nm_uuid_is_valid_nmlegacy(str)) {
|
||||
/* This is not a valid UUID, but something that we used to
|
||||
* accept according to nm_uuid_is_valid_nmlegacy().
|
||||
*
|
||||
* Normalize it by sha1 hashing the string. Upper case characters
|
||||
* are made lower case first. */
|
||||
NM_SET_OUT(out_normalized, TRUE);
|
||||
if (out_normalized_str) {
|
||||
char str_lower[40 + 1];
|
||||
int i;
|
||||
|
||||
nm_assert(strlen(str) < G_N_ELEMENTS(str_lower));
|
||||
|
||||
/* normalize first to lower-case. */
|
||||
g_strlcpy(str_lower, str, sizeof(str_lower));
|
||||
for (i = 0; str_lower[i]; i++)
|
||||
str_lower[i] = g_ascii_tolower(str_lower[i]);
|
||||
|
||||
/* The namespace UUID is chosen randomly. */
|
||||
nm_uuid_generate_from_string(&uuid,
|
||||
str_lower,
|
||||
-1,
|
||||
NM_UUID_TYPE_VERSION5,
|
||||
"4e72f709-ca95-4405-9053-1f43294a618c");
|
||||
nm_uuid_unparse(&uuid, out_normalized_str);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* UUID is not valid. */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
gboolean
|
||||
nm_uuid_is_null(const NMUuid *uuid)
|
||||
{
|
||||
|
@@ -43,6 +43,10 @@ nm_uuid_is_valid(const char *str)
|
||||
|
||||
gboolean nm_uuid_is_valid_nmlegacy(const char *str);
|
||||
|
||||
gboolean nm_uuid_is_valid_nm(const char *str,
|
||||
gboolean * out_normalized,
|
||||
char * out_normalized_str /* [static 37] */);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
char *nm_uuid_generate_random_str(char buf[static 37]);
|
||||
|
Reference in New Issue
Block a user