diff options
Diffstat (limited to 'src/settings/plugins/keyfile/nms-keyfile-utils.c')
-rw-r--r-- | src/settings/plugins/keyfile/nms-keyfile-utils.c | 238 |
1 files changed, 223 insertions, 15 deletions
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); |