summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libnm-core/nm-keyfile-internal.h4
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.c3
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.c238
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.h37
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile.c105
5 files changed, 369 insertions, 18 deletions
diff --git a/libnm-core/nm-keyfile-internal.h b/libnm-core/nm-keyfile-internal.h
index 98df586596..5487f59da3 100644
--- a/libnm-core/nm-keyfile-internal.h
+++ b/libnm-core/nm-keyfile-internal.h
@@ -175,6 +175,10 @@ gboolean _nm_keyfile_has_values (GKeyFile *keyfile);
#define NM_KEYFILE_PATH_SUFFIX_NMCONNECTION ".nmconnection"
+#define NM_KEYFILE_PATH_PREFIX_NMLOADED ".loaded-"
+
+#define NM_KEYFILE_PATH_NMLOADED_NULL "/dev/null"
+
gboolean nm_keyfile_utils_ignore_filename (const char *filename, gboolean require_extension);
char *nm_keyfile_utils_create_filename (const char *filename, gboolean with_extension);
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c
index d9d796e726..314b103392 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c
@@ -172,7 +172,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 98eecd7ca7..8d4ec943c4 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-utils.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-utils.c
@@ -27,6 +27,7 @@
#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"
@@ -34,18 +35,213 @@
/*****************************************************************************/
+char *
+nms_keyfile_loaded_uuid_filename (const char *dirname,
+ const char *uuid,
+ gboolean temporary)
+{
+ 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;
+ }
+
+ return g_build_filename (dirname, filename, NULL);
+}
+
gboolean
-nms_keyfile_utils_check_file_permissions_stat (const struct stat *st,
- GError **error)
+nms_keyfile_loaded_uuid_read (const char *dirname,
+ const char *filename,
+ char **out_full_filename,
+ char **out_uuid,
+ char **out_loaded_path)
{
- g_return_val_if_fail (st, FALSE);
+ const char *uuid;
+ const char *tmp;
+ gsize len;
+ gs_free char *full_filename = NULL;
+ gs_free char *ln = NULL;
- 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");
+ 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;
+
+ 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;
+}
+
+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)
+{
+ gs_free char *dirname = NULL;
+ gs_free char *filename = NULL;
+
+ nm_assert (full_filename && full_filename[0] == '/');
+
+ filename = g_path_get_basename (full_filename);
+ dirname = g_path_get_dirname (full_filename);
+
+ 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_loaded_uuid_write (const char *dirname,
+ const char *uuid,
+ const char *loaded_path,
+ gboolean allow_relative,
+ char **out_full_filename)
+{
+ 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;
+ }
+
+ 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 (NMSKeyfileFiletype filetype,
+ const struct stat *st,
+ GError **error)
+{
+ g_return_val_if_fail (st, 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) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -54,7 +250,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);
@@ -66,7 +263,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)
{
@@ -75,14 +273,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", g_strerror (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", g_strerror (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);
diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.h b/src/settings/plugins/keyfile/nms-keyfile-utils.h
index a74139c472..bc601dad51 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-utils.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-utils.h
@@ -29,13 +29,46 @@
#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)
+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/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c
index 2521798ab8..f9d006967e 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile.c
@@ -2511,6 +2511,109 @@ test_nm_keyfile_plugin_utils_escape_filename (void)
/*****************************************************************************/
+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)
@@ -2593,6 +2696,8 @@ 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 ();
}