diff options
| author | Michael Biebl <biebl@debian.org> | 2019-03-26 23:25:23 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2019-03-26 23:25:23 +0100 |
| commit | 9a6dcbf895f9da01768e64b73cec88c16157d91e (patch) | |
| tree | a359958930d731e9f1b59344642e10754419fe84 /src/settings/plugins/keyfile | |
| parent | 964ae8cc391520440cf5aa13e2b9cc34850ea6c2 (diff) | |
New upstream version 1.16.0 upstream/1.16.0
Diffstat (limited to 'src/settings/plugins/keyfile')
| -rw-r--r-- | src/settings/plugins/keyfile/nms-keyfile-connection.c | 1 | ||||
| -rw-r--r-- | src/settings/plugins/keyfile/nms-keyfile-plugin.c | 71 | ||||
| -rw-r--r-- | src/settings/plugins/keyfile/nms-keyfile-reader.c | 10 | ||||
| -rw-r--r-- | src/settings/plugins/keyfile/nms-keyfile-utils.c | 328 | ||||
| -rw-r--r-- | src/settings/plugins/keyfile/nms-keyfile-utils.h | 43 | ||||
| -rw-r--r-- | src/settings/plugins/keyfile/nms-keyfile-writer.c | 77 | ||||
| -rw-r--r-- | src/settings/plugins/keyfile/tests/meson.build | 3 | ||||
| -rw-r--r-- | src/settings/plugins/keyfile/tests/test-keyfile.c | 131 |
8 files changed, 416 insertions, 248 deletions
diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.c b/src/settings/plugins/keyfile/nms-keyfile-connection.c index 7511f206..3b362978 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-connection.c +++ b/src/settings/plugins/keyfile/nms-keyfile-connection.c @@ -23,7 +23,6 @@ #include "nms-keyfile-connection.h" -#include <string.h> #include <glib/gstdio.h> #include "nm-dbus-interface.h" diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c index 346b78c0..c13cc1ff 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c +++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c @@ -26,8 +26,6 @@ #include <sys/stat.h> #include <unistd.h> #include <sys/types.h> -#include <string.h> - #include <glib/gstdio.h> #include "nm-connection.h" @@ -36,6 +34,7 @@ #include "nm-utils.h" #include "nm-config.h" #include "nm-core-internal.h" +#include "nm-keyfile-internal.h" #include "settings/nm-settings-plugin.h" @@ -171,7 +170,6 @@ update_connection (NMSKeyfilePlugin *self, NMSKeyfileConnection *connection_by_uuid; GError *local = NULL; const char *uuid; - int dir_len; g_return_val_if_fail (!source || NM_IS_CONNECTION (source), NULL); g_return_val_if_fail (full_path || source, NULL); @@ -179,17 +177,8 @@ update_connection (NMSKeyfilePlugin *self, if (full_path) _LOGD ("loading from file \"%s\"...", full_path); - if (g_str_has_prefix (full_path, nms_keyfile_utils_get_path ())) { - dir_len = strlen (nms_keyfile_utils_get_path ()); - } else if (g_str_has_prefix (full_path, NM_CONFIG_KEYFILE_PATH_IN_MEMORY)) { - dir_len = NM_STRLEN (NM_CONFIG_KEYFILE_PATH_IN_MEMORY); - } else { - /* Just make sure the file name is not going go pass the following check. */ - dir_len = strlen (full_path); - } - - if ( full_path[dir_len] != '/' - || strchr (full_path + dir_len + 1, '/') != NULL) { + if ( !nm_utils_file_is_in_path (full_path, nms_keyfile_utils_get_path ()) + && !nm_utils_file_is_in_path (full_path, NM_KEYFILE_PATH_NAME_RUN)) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "File not in recognized system-connections directory"); return FALSE; @@ -322,7 +311,7 @@ dir_changed (GFileMonitor *monitor, gboolean exists; full_path = g_file_get_path (file); - if (nms_keyfile_utils_should_ignore_file (full_path, FALSE)) { + if (nm_keyfile_utils_ignore_filename (full_path, FALSE)) { g_free (full_path); return; } @@ -444,7 +433,7 @@ _read_dir (GPtrArray *filenames, } while ((item = g_dir_read_name (dir))) { - if (nms_keyfile_utils_should_ignore_file (item, require_extension)) + if (nm_keyfile_utils_ignore_filename (item, require_extension)) continue; g_ptr_array_add (filenames, g_build_filename (path, item, NULL)); } @@ -467,7 +456,7 @@ read_connections (NMSettingsPlugin *config) filenames = g_ptr_array_new_with_free_func (g_free); - _read_dir (filenames, NM_CONFIG_KEYFILE_PATH_IN_MEMORY, TRUE); + _read_dir (filenames, NM_KEYFILE_PATH_NAME_RUN, TRUE); _read_dir (filenames, nms_keyfile_utils_get_path (), FALSE); alive_connections = g_hash_table_new (nm_direct_hash, NULL); @@ -476,7 +465,7 @@ read_connections (NMSettingsPlugin *config) * iterating over the files. * * To have sensible, reproducible behavior, sort the paths by last modification - * time prefering older files. + * time preferring older files. */ paths = _paths_from_connections (priv->connections); g_ptr_array_sort_with_data (filenames, (GCompareDataFunc) _sort_paths, paths); @@ -523,35 +512,6 @@ get_connections (NMSettingsPlugin *config) } static gboolean -_file_is_in_path (const char *abs_filename, - const char *abs_path) -{ - gsize l; - - /* FIXME: ensure that both paths are at least normalized (coalescing ".", - * duplicate '/', and trailing '/'). */ - - nm_assert (abs_filename && abs_filename[0] == '/'); - nm_assert (abs_path && abs_path[0] == '/'); - - l = strlen (abs_path); - if (strncmp (abs_filename, abs_path, l) != 0) - return FALSE; - - abs_filename += l; - while (abs_filename[0] == '/') - abs_filename++; - - if (!abs_filename[0]) - return FALSE; - - if (strchr (abs_filename, '/')) - return FALSE; - - return TRUE; -} - -static gboolean load_connection (NMSettingsPlugin *config, const char *filename) { @@ -559,25 +519,14 @@ load_connection (NMSettingsPlugin *config, NMSKeyfileConnection *connection; gboolean require_extension; - /* the test whether to require a file extension tries to figure out whether - * the provided filename is inside /etc or /run. - * - * However, on Posix a filename just resolves to an Inode, and there can - * be any kind of paths that point to the same Inode. It's not generally possible - * to check for that (unless, we would stat all files in the target directory - * and see whether their inode matches). - * - * So, when loading the file do something simpler: require that the path - * starts with the well-known prefix. This rejects symlinks or hard links - * which would actually also point to the same file. */ - if (_file_is_in_path (filename, nms_keyfile_utils_get_path ())) + if (nm_utils_file_is_in_path (filename, nms_keyfile_utils_get_path ())) require_extension = FALSE; - else if (_file_is_in_path (filename, NM_CONFIG_KEYFILE_PATH_IN_MEMORY)) + else if (nm_utils_file_is_in_path (filename, NM_KEYFILE_PATH_NAME_RUN)) require_extension = TRUE; else return FALSE; - if (nms_keyfile_utils_should_ignore_file (filename, require_extension)) + if (nm_keyfile_utils_ignore_filename (filename, require_extension)) return FALSE; connection = update_connection (self, NULL, filename, find_by_path (self, filename), TRUE, NULL, NULL); diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c index 580a857a..5778f13c 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-reader.c +++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c @@ -23,7 +23,6 @@ #include "nms-keyfile-reader.h" #include <sys/stat.h> -#include <string.h> #include "nm-keyfile-internal.h" @@ -142,11 +141,11 @@ nms_keyfile_reader_from_keyfile (GKeyFile *key_file, if (!connection) return NULL; - if (g_str_has_suffix (filename, NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION)) { + if (g_str_has_suffix (filename, NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)) { gsize l = strlen (filename); - if (l > NM_STRLEN (NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION)) - filename_id = g_strndup (filename, l - NM_STRLEN (NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION)); + if (l > NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)) + filename_id = g_strndup (filename, l - NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)); } nm_keyfile_read_ensure_id (connection, filename_id ?: filename); @@ -172,7 +171,8 @@ nms_keyfile_reader_from_file (const char *full_filename, nm_assert (full_filename && full_filename[0] == '/'); nm_assert (!profile_dir || profile_dir[0] == '/'); - if (!nms_keyfile_utils_check_file_permissions (full_filename, + if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_KEYFILE, + full_filename, NULL, error)) return NULL; diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.c b/src/settings/plugins/keyfile/nms-keyfile-utils.c index c3bfcdee..3c4b0288 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-utils.c +++ b/src/settings/plugins/keyfile/nms-keyfile-utils.c @@ -23,111 +23,223 @@ #include "nms-keyfile-utils.h" #include <stdlib.h> -#include <string.h> #include <sys/stat.h> +#include "nm-keyfile-internal.h" +#include "nm-utils.h" #include "nm-setting-wired.h" #include "nm-setting-wireless.h" #include "nm-setting-wireless-security.h" #include "nm-config.h" -#define NM_CONFIG_KEYFILE_PATH_DEFAULT NMCONFDIR "/system-connections" - /*****************************************************************************/ -static const char temp_letters[] = -"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - -/* - * Check '.[a-zA-Z0-9]{6}' file suffix used for temporary files by g_file_set_contents() (mkstemp()). - */ -static gboolean -check_mkstemp_suffix (const char *path) +char * +nms_keyfile_loaded_uuid_filename (const char *dirname, + const char *uuid, + gboolean temporary) { - const char *ptr; - - g_return_val_if_fail (path != NULL, FALSE); + char filename[250]; + + nm_assert (dirname && dirname[0] == '/'); + nm_assert (uuid && nm_utils_is_uuid (uuid) && !strchr (uuid, '/')); + + if (g_snprintf (filename, + sizeof (filename), + "%s%s%s%s", + NM_KEYFILE_PATH_PREFIX_NMLOADED, + uuid, + NM_KEYFILE_PATH_SUFFIX_NMCONNECTION, + temporary ? "~" : "") >= sizeof (filename)) { + /* valid uuids are limited in length. The buffer should always be large + * enough. */ + nm_assert_not_reached (); + return NULL; + } - /* Matches *.[a-zA-Z0-9]{6} suffix of mkstemp()'s temporary files */ - ptr = strrchr (path, '.'); - if (ptr && (strspn (ptr + 1, temp_letters) == 6) && (! ptr[7])) - return TRUE; - return FALSE; + return g_build_filename (dirname, filename, NULL); } -static gboolean -check_prefix_dot (const char *base) +gboolean +nms_keyfile_loaded_uuid_read (const char *dirname, + const char *filename, + char **out_full_filename, + char **out_uuid, + char **out_loaded_path) { - nm_assert (base && base[0]); + const char *uuid; + const char *tmp; + gsize len; + gs_free char *full_filename = NULL; + gs_free char *ln = NULL; + + nm_assert (dirname && dirname[0] == '/'); + nm_assert (filename && filename[0] && !strchr (filename, '/')); + + if (filename[0] != '.') { + /* the hidden-uuid filename must start with '.'. That is, + * so that it does not conflict with regular keyfiles according + * to nm_keyfile_utils_ignore_filename(). */ + return FALSE; + } + + len = strlen (filename); + if ( len <= NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED) + || memcmp (filename, NM_KEYFILE_PATH_PREFIX_NMLOADED, NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED)) != 0) { + /* the filename does not have the right prefix. */ + return FALSE; + } + + tmp = &filename[NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED)]; + len -= NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED); + + if ( len <= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION) + || memcmp (&tmp[len - NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)], + NM_KEYFILE_PATH_SUFFIX_NMCONNECTION, + NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)) != 0) { + /* the file does not have the right suffix. */ + return FALSE; + } + len -= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION); + + if (!NM_IN_SET (len, 36, 40)) { + /* the remaining part of the filename has not the right length to + * contain a UUID (according to nm_utils_is_uuid()). */ + return FALSE; + } + + uuid = nm_strndup_a (100, tmp, len, NULL); + if (!nm_utils_is_uuid (uuid)) + return FALSE; - return base[0] == '.'; + full_filename = g_build_filename (dirname, filename, NULL); + + if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_NMLOADED, + full_filename, + NULL, + NULL)) + return FALSE; + + ln = nm_utils_read_link_absolute (full_filename, NULL); + if (!ln) + return FALSE; + + NM_SET_OUT (out_uuid, g_strdup (uuid)); + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); + NM_SET_OUT (out_loaded_path, g_steal_pointer (&ln)); + return TRUE; } -static gboolean -check_suffix (const char *base, const char *tag) +gboolean +nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, + char **out_dirname, + char **out_filename, + char **out_uuid, + char **out_loaded_path) { - int len, tag_len; + gs_free char *dirname = NULL; + gs_free char *filename = NULL; - g_return_val_if_fail (base != NULL, TRUE); - g_return_val_if_fail (tag != NULL, TRUE); + nm_assert (full_filename && full_filename[0] == '/'); - len = strlen (base); - tag_len = strlen (tag); - if ((len > tag_len) && !g_ascii_strcasecmp (base + len - tag_len, tag)) - return TRUE; - return FALSE; -} + filename = g_path_get_basename (full_filename); + dirname = g_path_get_dirname (full_filename); -#define SWP_TAG ".swp" -#define SWPX_TAG ".swpx" -#define PEM_TAG ".pem" -#define DER_TAG ".der" + if (!nms_keyfile_loaded_uuid_read (dirname, + filename, + NULL, + out_uuid, + out_loaded_path)) + return FALSE; + + NM_SET_OUT (out_dirname, g_steal_pointer (&dirname)); + NM_SET_OUT (out_filename, g_steal_pointer (&filename)); + return TRUE; +} gboolean -nms_keyfile_utils_should_ignore_file (const char *filename, gboolean require_extension) +nms_keyfile_loaded_uuid_write (const char *dirname, + const char *uuid, + const char *loaded_path, + gboolean allow_relative, + char **out_full_filename) { - gs_free char *base = NULL; - - g_return_val_if_fail (filename != NULL, TRUE); - - base = g_path_get_basename (filename); - g_return_val_if_fail (base != NULL, TRUE); - - /* Ignore hidden and backup files */ - /* should_ignore_file() must mirror escape_filename() */ - if (check_prefix_dot (base) || check_suffix (base, "~")) - return TRUE; - /* Ignore temporary files */ - if (check_mkstemp_suffix (base)) - return TRUE; - /* Ignore 802.1x certificates and keys */ - if (check_suffix (base, PEM_TAG) || check_suffix (base, DER_TAG)) - return TRUE; - - if (require_extension) { - gsize l = strlen (base); - - if ( l <= NM_STRLEN (NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION) - || !g_str_has_suffix (base, NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION)) - return TRUE; + gs_free char *full_filename_tmp = NULL; + gs_free char *full_filename = NULL; + + nm_assert (dirname && dirname[0] == '/'); + nm_assert (uuid && nm_utils_is_uuid (uuid) && !strchr (uuid, '/')); + nm_assert (!loaded_path || loaded_path[0] == '/'); + + full_filename_tmp = nms_keyfile_loaded_uuid_filename (dirname, uuid, TRUE); + + nm_assert (g_str_has_suffix (full_filename_tmp, "~")); + nm_assert (nm_utils_file_is_in_path (full_filename_tmp, dirname)); + + (void) unlink (full_filename_tmp); + + if (!loaded_path) { + gboolean success = TRUE; + + full_filename_tmp[strlen (full_filename_tmp) - 1] = '\0'; + if (unlink (full_filename_tmp) != 0) + success = NM_IN_SET (errno, ENOENT); + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp)); + return success; + } + + if (allow_relative) { + const char *f; + + f = nm_utils_file_is_in_path (loaded_path, dirname); + if (f) { + /* @loaded_path points to a file directly in @dirname. + * Don't use absolute paths. */ + loaded_path = f; + } + } + + if (symlink (loaded_path, full_filename_tmp) != 0) { + full_filename_tmp[strlen (full_filename_tmp) - 1] = '\0'; + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp)); + return FALSE; } - return FALSE; + full_filename = g_strdup (full_filename_tmp); + full_filename[strlen (full_filename) - 1] = '\0'; + if (rename (full_filename_tmp, full_filename) != 0) { + (void) unlink (full_filename_tmp); + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); + return FALSE; + } + + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); + return TRUE; } /*****************************************************************************/ gboolean -nms_keyfile_utils_check_file_permissions_stat (const struct stat *st, +nms_keyfile_utils_check_file_permissions_stat (NMSKeyfileFiletype filetype, + const struct stat *st, GError **error) { g_return_val_if_fail (st, FALSE); - if (!S_ISREG (st->st_mode)) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "file is not a regular file"); - return FALSE; - } + if (filetype == NMS_KEYFILE_FILETYPE_KEYFILE) { + if (!S_ISREG (st->st_mode)) { + g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "file is not a regular file"); + return FALSE; + } + } else if (filetype == NMS_KEYFILE_FILETYPE_NMLOADED) { + if (!S_ISLNK (st->st_mode)) { + g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "file is not a slink"); + return FALSE; + } + } else + g_return_val_if_reached (FALSE); if (!NM_FLAGS_HAS (nm_utils_get_testing (), NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK)) { if (st->st_uid != 0) { @@ -137,7 +249,8 @@ nms_keyfile_utils_check_file_permissions_stat (const struct stat *st, return FALSE; } - if (st->st_mode & 0077) { + if ( filetype == NMS_KEYFILE_FILETYPE_KEYFILE + && (st->st_mode & 0077)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "File permissions (%03o) are insecure", st->st_mode); @@ -149,7 +262,8 @@ nms_keyfile_utils_check_file_permissions_stat (const struct stat *st, } gboolean -nms_keyfile_utils_check_file_permissions (const char *filename, +nms_keyfile_utils_check_file_permissions (NMSKeyfileFiletype filetype, + const char *filename, struct stat *out_st, GError **error) { @@ -158,14 +272,24 @@ nms_keyfile_utils_check_file_permissions (const char *filename, g_return_val_if_fail (filename && filename[0] == '/', FALSE); - if (stat (filename, &st) != 0) { - errsv = errno; - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "cannot access file: %s", g_strerror (errsv)); - return FALSE; - } + if (filetype == NMS_KEYFILE_FILETYPE_KEYFILE) { + if (stat (filename, &st) != 0) { + errsv = errno; + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "cannot access file: %s", nm_strerror_native (errsv)); + return FALSE; + } + } else if (filetype == NMS_KEYFILE_FILETYPE_NMLOADED) { + if (lstat (filename, &st) != 0) { + errsv = errno; + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "cannot access file: %s", nm_strerror_native (errsv)); + return FALSE; + } + } else + g_return_val_if_reached (FALSE); - if (!nms_keyfile_utils_check_file_permissions_stat (&st, error)) + if (!nms_keyfile_utils_check_file_permissions_stat (filetype, &st, error)) return FALSE; NM_SET_OUT (out_st, st); @@ -174,50 +298,6 @@ nms_keyfile_utils_check_file_permissions (const char *filename, /*****************************************************************************/ -char * -nms_keyfile_utils_escape_filename (const char *filename, - gboolean with_extension) -{ - GString *str; - const char *f = filename; - /* keyfile used to escape with '*', do not change that behavior. - * - * But for newly added escapings, use '_' instead. - * Also, @with_extension is new-style. */ - const char ESCAPE_CHAR = with_extension ? '_' : '*'; - const char ESCAPE_CHAR2 = '_'; - - g_return_val_if_fail (filename && filename[0], NULL); - - str = g_string_sized_new (60); - - /* Convert '/' to ESCAPE_CHAR */ - for (f = filename; f[0]; f++) { - if (f[0] == '/') - g_string_append_c (str, ESCAPE_CHAR); - else - g_string_append_c (str, f[0]); - } - - /* escape_filename() must avoid anything that should_ignore_file() would reject. - * We can escape here more aggressivly then what we would read back. */ - if (check_prefix_dot (str->str)) - str->str[0] = ESCAPE_CHAR2; - if (check_suffix (str->str, "~")) - str->str[str->len - 1] = ESCAPE_CHAR2; - if ( check_mkstemp_suffix (str->str) - || check_suffix (str->str, PEM_TAG) - || check_suffix (str->str, DER_TAG)) - g_string_append_c (str, ESCAPE_CHAR2); - - if (with_extension) - g_string_append (str, NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION); - - return g_string_free (str, FALSE);; -} - -/*****************************************************************************/ - const char * nms_keyfile_utils_get_path (void) { @@ -229,7 +309,7 @@ nms_keyfile_utils_get_path (void) NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH, NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); if (!path) - path = g_strdup (""NM_CONFIG_KEYFILE_PATH_DEFAULT""); + path = g_strdup (""NM_KEYFILE_PATH_NAME_ETC_DEFAULT""); } return path; } diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.h b/src/settings/plugins/keyfile/nms-keyfile-utils.h index 297dd4ea..bc601dad 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-utils.h +++ b/src/settings/plugins/keyfile/nms-keyfile-utils.h @@ -23,27 +23,52 @@ #include "NetworkManagerUtils.h" -#define NM_CONFIG_KEYFILE_PATH_IN_MEMORY NMRUNDIR "/system-connections" - -#define NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION ".nmconnection" - #define NMS_KEYFILE_CONNECTION_LOG_PATH(path) ((path) ?: "in-memory") #define NMS_KEYFILE_CONNECTION_LOG_FMT "%s (%s,\"%s\")" #define NMS_KEYFILE_CONNECTION_LOG_ARG(con) NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con)) #define NMS_KEYFILE_CONNECTION_LOG_FMTD "%s (%s,\"%s\",%p)" #define NMS_KEYFILE_CONNECTION_LOG_ARGD(con) NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con)), (con) -gboolean nms_keyfile_utils_should_ignore_file (const char *filename, gboolean require_extension); - -char *nms_keyfile_utils_escape_filename (const char *filename, gboolean with_extension); +typedef enum { + NMS_KEYFILE_FILETYPE_KEYFILE, + NMS_KEYFILE_FILETYPE_NMLOADED, +} NMSKeyfileFiletype; const char *nms_keyfile_utils_get_path (void); +/*****************************************************************************/ + +char *nms_keyfile_loaded_uuid_filename (const char *dirname, + const char *uuid, + gboolean temporary); + +gboolean nms_keyfile_loaded_uuid_read (const char *dirname, + const char *filename, + char **out_full_filename, + char **out_uuid, + char **out_loaded_path); + +gboolean nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, + char **out_dirname, + char **out_filename, + char **out_uuid, + char **out_loaded_path); + +gboolean nms_keyfile_loaded_uuid_write (const char *dirname, + const char *uuid, + const char *loaded_path, + gboolean allow_relative, + char **out_full_filename); + +/*****************************************************************************/ + struct stat; -gboolean nms_keyfile_utils_check_file_permissions_stat (const struct stat *st, +gboolean nms_keyfile_utils_check_file_permissions_stat (NMSKeyfileFiletype filetype, + const struct stat *st, GError **error); -gboolean nms_keyfile_utils_check_file_permissions (const char *filename, +gboolean nms_keyfile_utils_check_file_permissions (NMSKeyfileFiletype filetype, + const char *filename, struct stat *out_st, GError **error); diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c index df26ea60..8c75d8c7 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-writer.c +++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c @@ -26,8 +26,6 @@ #include <stdlib.h> #include <sys/stat.h> #include <unistd.h> -#include <errno.h> -#include <string.h> #include "nm-keyfile-internal.h" @@ -177,27 +175,30 @@ _internal_write_connection (NMConnection *connection, uid_t owner_uid, pid_t owner_grp, const char *existing_path, + gboolean existing_path_read_only, gboolean force_rename, char **out_path, NMConnection **out_reread, gboolean *out_reread_same, GError **error) { - gs_unref_keyfile GKeyFile *key_file = NULL; - gs_free char *data = NULL; - gsize len; + gs_unref_keyfile GKeyFile *kf_file = NULL; + gs_free char *kf_content_buf = NULL; + gsize kf_content_len; gs_free char *path = NULL; const char *id; WriteInfo info = { 0 }; GError *local_err = NULL; int errsv; - gboolean rename = force_rename; + gboolean rename; g_return_val_if_fail (!out_path || !*out_path, FALSE); g_return_val_if_fail (keyfile_dir && keyfile_dir[0] == '/', FALSE); - if (existing_path && !g_str_has_prefix (existing_path, keyfile_dir)) - rename = TRUE; + rename = force_rename + || existing_path_read_only + || ( existing_path + && !nm_utils_file_is_in_path (existing_path, keyfile_dir)); switch (_nm_connection_verify (connection, error)) { case NM_SETTING_VERIFY_NORMALIZABLE: @@ -214,11 +215,11 @@ _internal_write_connection (NMConnection *connection, info.keyfile_dir = keyfile_dir; - key_file = nm_keyfile_write (connection, _handler_write, &info, error); - if (!key_file) + kf_file = nm_keyfile_write (connection, _handler_write, &info, error); + if (!kf_file) return FALSE; - data = g_key_file_to_data (key_file, &len, error); - if (!data) + kf_content_buf = g_key_file_to_data (kf_file, &kf_content_len, error); + if (!kf_content_buf) return FALSE; if (!g_file_test (keyfile_dir, G_FILE_TEST_IS_DIR)) @@ -227,13 +228,14 @@ _internal_write_connection (NMConnection *connection, /* If we have existing file path, use it. Else generate one from * connection's ID. */ - if (existing_path != NULL && !rename) { + if ( existing_path + && !rename) path = g_strdup (existing_path); - } else { - char *filename_escaped = nms_keyfile_utils_escape_filename (id, with_extension); + else { + gs_free char *filename_escaped = NULL; + filename_escaped = nm_keyfile_utils_create_filename (id, with_extension); path = g_build_filename (keyfile_dir, filename_escaped, NULL); - g_free (filename_escaped); } /* If a file with this path already exists (but isn't the existing path @@ -243,32 +245,34 @@ _internal_write_connection (NMConnection *connection, * there's a race here, but there's not a lot we can do about it, and * we shouldn't get more than one connection with the same UUID either. */ - if (g_strcmp0 (path, existing_path) != 0 && g_file_test (path, G_FILE_TEST_EXISTS)) { + if ( !nm_streq0 (path, existing_path) + && g_file_test (path, G_FILE_TEST_EXISTS)) { guint i; gboolean name_found = FALSE; /* A keyfile with this connection's ID already exists. Pick another name. */ for (i = 0; i < 100; i++) { - char *filename, *filename_escaped; + gs_free char *filename_escaped = NULL; + gs_free char *filename = NULL; if (i == 0) filename = g_strdup_printf ("%s-%s", id, nm_connection_get_uuid (connection)); else filename = g_strdup_printf ("%s-%s-%u", id, nm_connection_get_uuid (connection), i); - filename_escaped = nms_keyfile_utils_escape_filename (filename, with_extension); + filename_escaped = nm_keyfile_utils_create_filename (filename, with_extension); g_free (path); path = g_strdup_printf ("%s/%s", keyfile_dir, filename_escaped); - g_free (filename); - g_free (filename_escaped); - if (g_strcmp0 (path, existing_path) == 0 || !g_file_test (path, G_FILE_TEST_EXISTS)) { + + if ( nm_streq0 (path, existing_path) + || !g_file_test (path, G_FILE_TEST_EXISTS)) { name_found = TRUE; break; } } if (!name_found) { - if (existing_path == NULL) { + if (existing_path_read_only || !existing_path) { /* this really should not happen, we tried hard to find an unused name... bail out. */ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "could not find suitable keyfile file name (%s already used)", path); @@ -281,13 +285,7 @@ _internal_write_connection (NMConnection *connection, } } - /* In case of updating the connection and changing the file path, - * we need to remove the old one, not to end up with two connections. - */ - if (existing_path != NULL && strcmp (path, existing_path) != 0) - unlink (existing_path); - - nm_utils_file_set_contents (path, data, len, 0600, &local_err); + nm_utils_file_set_contents (path, kf_content_buf, kf_content_len, 0600, &local_err); if (local_err) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "error writing to file '%s': %s", @@ -300,17 +298,24 @@ _internal_write_connection (NMConnection *connection, errsv = errno; g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "error chowning '%s': %s (%d)", - path, g_strerror (errsv), errsv); + path, nm_strerror_native (errsv), errsv); unlink (path); return FALSE; } - if (out_reread || out_reread_same) - { + /* In case of updating the connection and changing the file path, + * we need to remove the old one, not to end up with two connections. + */ + if ( existing_path + && !existing_path_read_only + && !nm_streq (path, existing_path)) + unlink (existing_path); + + if (out_reread || out_reread_same) { gs_unref_object NMConnection *reread = NULL; gboolean reread_same = FALSE; - reread = nms_keyfile_reader_from_keyfile (key_file, path, NULL, profile_dir, FALSE, NULL); + reread = nms_keyfile_reader_from_keyfile (kf_file, path, NULL, profile_dir, FALSE, NULL); nm_assert (NM_IS_CONNECTION (reread)); @@ -356,7 +361,7 @@ nms_keyfile_writer_connection (NMConnection *connection, if (save_to_disk) keyfile_dir = nms_keyfile_utils_get_path (); else - keyfile_dir = NM_CONFIG_KEYFILE_PATH_IN_MEMORY; + keyfile_dir = NM_KEYFILE_PATH_NAME_RUN; return _internal_write_connection (connection, keyfile_dir, @@ -365,6 +370,7 @@ nms_keyfile_writer_connection (NMConnection *connection, 0, 0, existing_path, + FALSE, force_rename, out_path, out_reread, @@ -390,6 +396,7 @@ nms_keyfile_writer_test_connection (NMConnection *connection, owner_grp, NULL, FALSE, + FALSE, out_path, out_reread, out_reread_same, diff --git a/src/settings/plugins/keyfile/tests/meson.build b/src/settings/plugins/keyfile/tests/meson.build index 8b94b256..752b6d7b 100644 --- a/src/settings/plugins/keyfile/tests/meson.build +++ b/src/settings/plugins/keyfile/tests/meson.build @@ -11,5 +11,6 @@ exe = executable( test( 'keyfile/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], + timeout: default_test_timeout, ) diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c index 4a0e01b3..baecac13 100644 --- a/src/settings/plugins/keyfile/tests/test-keyfile.c +++ b/src/settings/plugins/keyfile/tests/test-keyfile.c @@ -23,7 +23,6 @@ #include <stdio.h> #include <stdarg.h> #include <unistd.h> -#include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> @@ -1177,7 +1176,6 @@ test_write_bt_dun_connection (void) NM_SETTING_GSM_APN, "internet2.voicestream.com", NM_SETTING_GSM_USERNAME, "george.clinton", NM_SETTING_GSM_PASSWORD, "parliament", - NM_SETTING_GSM_NUMBER, "*99#", NULL); write_test_connection_and_reread (connection, TRUE); @@ -1259,7 +1257,6 @@ test_write_gsm_connection (void) NM_SETTING_GSM_APN, "internet2.voicestream.com", NM_SETTING_GSM_USERNAME, "george.clinton.again", NM_SETTING_GSM_PASSWORD, "parliament2", - NM_SETTING_GSM_NUMBER, "*99#", NM_SETTING_GSM_PIN, "123456", NM_SETTING_GSM_NETWORK_ID, "254098", NM_SETTING_GSM_HOME_ONLY, TRUE, @@ -2073,7 +2070,7 @@ test_write_new_wireless_group_names (void) NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, NULL); - /* WiFi setting */ + /* Wi-Fi setting */ s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); nm_connection_add_setting (connection, NM_SETTING (s_wifi)); @@ -2084,7 +2081,7 @@ test_write_new_wireless_group_names (void) NULL); g_bytes_unref (ssid); - /* WiFi security setting */ + /* Wi-Fi security setting */ s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); nm_connection_add_setting (connection, NM_SETTING (s_wsec)); g_object_set (s_wsec, @@ -2333,7 +2330,6 @@ test_write_flags_property (void) s_gsm = nm_setting_gsm_new (); nm_connection_add_setting (connection, s_gsm); g_object_set (s_gsm, - NM_SETTING_GSM_NUMBER, "#99*", NM_SETTING_GSM_APN, "myapn", NM_SETTING_GSM_USERNAME, "adfasdfasdf", NM_SETTING_GSM_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED | NM_SETTING_SECRET_FLAG_NOT_REQUIRED, @@ -2466,18 +2462,18 @@ _escape_filename (gboolean with_extension, const char *filename, gboolean would_ g_assert (filename && filename[0]); - if (!!would_be_ignored != !!nms_keyfile_utils_should_ignore_file (filename, with_extension)) { + if (!!would_be_ignored != !!nm_keyfile_utils_ignore_filename (filename, with_extension)) { if (would_be_ignored) g_error ("We expect filename \"%s\" to be ignored, but it isn't", filename); else g_error ("We expect filename \"%s\" not to be ignored, but it is", filename); } - esc = nms_keyfile_utils_escape_filename (filename, with_extension); + esc = nm_keyfile_utils_create_filename (filename, with_extension); g_assert (esc && esc[0]); g_assert (!strchr (esc, '/')); - if (nms_keyfile_utils_should_ignore_file (esc, with_extension)) + if (nm_keyfile_utils_ignore_filename (esc, with_extension)) g_error ("Escaping filename \"%s\" yielded \"%s\", but this is ignored", filename, esc); } @@ -2503,21 +2499,131 @@ test_nm_keyfile_plugin_utils_escape_filename (void) _escape_filename (FALSE, ".#emacs-locking", TRUE); _escape_filename (FALSE, "file-with-tilde~", TRUE); _escape_filename (FALSE, ".file-with-dot", TRUE); + _escape_filename (FALSE, "/some/path/with/trailing/slash/", TRUE); + _escape_filename (FALSE, "/some/path/without/trailing/slash", FALSE); _escape_filename (TRUE, "lala", TRUE); } /*****************************************************************************/ +static void +_assert_keyfile_loaded_uuid (const char *dirname, + const char *uuid, + const char *loaded_path, + gboolean allow_relative, + const char *exp_full_filename, + const char *exp_uuid, + const char *exp_symlink_target, + const char *exp_loaded_path) +{ + gs_free char *full_filename = NULL; + gs_free char *symlink_target = NULL; + gs_free char *uuid2 = NULL; + gs_free char *loaded_path2 = NULL; + gs_free char *dirname3 = NULL; + gs_free char *filename3 = NULL; + gs_free char *uuid3 = NULL; + gs_free char *loaded_path3 = NULL; + gboolean success; + gs_free char *filename = NULL; + + g_assert (dirname && dirname[0] == '/'); + g_assert (exp_full_filename && exp_full_filename[0]); + g_assert (!exp_loaded_path || exp_loaded_path[0] == '/'); + + filename = g_path_get_basename (exp_full_filename); + + full_filename = nms_keyfile_loaded_uuid_filename (dirname, uuid, FALSE); + g_assert_cmpstr (full_filename, ==, full_filename); + nm_clear_g_free (&full_filename); + + + g_assert (nms_keyfile_loaded_uuid_write (dirname, uuid, loaded_path, allow_relative, &full_filename)); + g_assert_cmpstr (full_filename, ==, exp_full_filename); + nm_clear_g_free (&full_filename); + + if (exp_symlink_target) + g_assert (g_file_test (exp_full_filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_SYMLINK)); + else + g_assert (!g_file_test (exp_full_filename, G_FILE_TEST_EXISTS)); + symlink_target = g_file_read_link (exp_full_filename, NULL); + g_assert_cmpstr (symlink_target, ==, exp_symlink_target); + + + success = nms_keyfile_loaded_uuid_read (dirname, filename, &full_filename, &uuid2, &loaded_path2); + g_assert_cmpint (!!exp_uuid, ==, success); + if (success) + g_assert_cmpstr (full_filename, ==, exp_full_filename); + else + g_assert_cmpstr (full_filename, ==, NULL); + nm_clear_g_free (&full_filename); + g_assert_cmpstr (uuid2, ==, exp_uuid); + g_assert_cmpstr (loaded_path2, ==, exp_loaded_path); + + + success = nms_keyfile_loaded_uuid_read_from_file (exp_full_filename, &dirname3, &filename3, &uuid3, &loaded_path3); + g_assert_cmpint (!!exp_uuid, ==, success); + if (success) { + g_assert_cmpstr (dirname3, ==, dirname); + g_assert_cmpstr (filename3, ==, filename); + } else { + g_assert_cmpstr (dirname3, ==, NULL); + g_assert_cmpstr (filename3, ==, NULL); + } + g_assert_cmpstr (uuid3, ==, exp_uuid); + g_assert_cmpstr (loaded_path3, ==, exp_loaded_path); +} + +static void +test_loaded_uuid (void) +{ + const char *uuid = "3c03fd17-ddc3-4100-a954-88b6fafff959"; + gs_free char *filename = g_strdup_printf ("%s%s%s", + NM_KEYFILE_PATH_PREFIX_NMLOADED, + uuid, + NM_KEYFILE_PATH_SUFFIX_NMCONNECTION); + gs_free char *full_filename = g_strdup_printf ("%s/%s", + TEST_SCRATCH_DIR, + filename); + const char *loaded_path0 = NM_KEYFILE_PATH_NMLOADED_NULL; + const char *loaded_path1 = "/some/where/but/not/scratch/dir"; + const char *filename2 = "foo1"; + gs_free char *loaded_path2 = g_strdup_printf ("%s/%s", + TEST_SCRATCH_DIR, + filename2); + + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, NULL, FALSE, full_filename, NULL, NULL, NULL); + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, NULL, TRUE, full_filename, NULL, NULL, NULL); + + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path0, FALSE, full_filename, uuid, loaded_path0, loaded_path0); + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path0, TRUE, full_filename, uuid, loaded_path0, loaded_path0); + + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path1, FALSE, full_filename, uuid, loaded_path1, loaded_path1); + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path1, TRUE, full_filename, uuid, loaded_path1, loaded_path1); + + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path2, FALSE, full_filename, uuid, loaded_path2, loaded_path2); + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path2, TRUE, full_filename, uuid, filename2, loaded_path2); + + (void) unlink (full_filename); +} + +/*****************************************************************************/ + NMTST_DEFINE (); int main (int argc, char **argv) { + int errsv; + _nm_utils_set_testing (NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK); + nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); - if (g_mkdir_with_parents (TEST_SCRATCH_DIR, 0755) != 0) - g_error ("failure to create test directory \"%s\": %s", TEST_SCRATCH_DIR, g_strerror (errno)); + if (g_mkdir_with_parents (TEST_SCRATCH_DIR, 0755) != 0) { + errsv = errno; + g_error ("failure to create test directory \"%s\": %s", TEST_SCRATCH_DIR, nm_strerror_native (errsv)); + } /* The tests */ g_test_add_func ("/keyfile/test_read_valid_wired_connection", test_read_valid_wired_connection); @@ -2591,6 +2697,7 @@ int main (int argc, char **argv) g_test_add_func ("/keyfile/test_nm_keyfile_plugin_utils_escape_filename", test_nm_keyfile_plugin_utils_escape_filename); + g_test_add_func ("/keyfile/test_loaded_uuid", test_loaded_uuid); + return g_test_run (); } - |