Enable testing of high-integrity in the loader
authorMark Young <marky@lunarg.com>
Thu, 17 Feb 2022 16:44:49 +0000 (09:44 -0700)
committerMark Young <marky@lunarg.com>
Thu, 17 Feb 2022 18:37:30 +0000 (11:37 -0700)
Enable testing to check if the loader is running with elevated
privileges.  This is to make sure we're ignoring the appropriate
environment variables in those scenarios to potentially avoid
escalation exploits.

loader/loader.c
tests/framework/shim/shim.h
tests/framework/shim/unix_shim.cpp
tests/loader_regression_tests.cpp

index 2a8d85991dd554f11fb77e007d9ab2723a5724c5..a154a1d2bf709af1872e16a17ab00d52ea35c02d 100644 (file)
@@ -2969,13 +2969,11 @@ static VkResult read_data_files_in_search_paths(const struct loader_instance *in
     if (path_override != NULL) {
         override_path = path_override;
     } else if (env_override != NULL) {
-#ifndef _WIN32
-        if (geteuid() != getuid() || getegid() != getgid()) {
-            // Don't allow setuid apps to use the env var:
-            env_override = NULL;
-        } else
-#endif
-        {
+        // Don't allow setuid apps to use the env var
+        if (is_high_integrity()) {
+            loader_log(inst, VULKAN_LOADER_WARN_BIT, 0,
+                       "read_data_files_in_search_paths: Ignoring override %s due to high-integrity", env_override);
+        } else {
             override_env = loader_secure_getenv(env_override, inst);
 
             // The ICD override is actually a specific list of filenames, not directories
@@ -3065,7 +3063,7 @@ static VkResult read_data_files_in_search_paths(const struct loader_instance *in
                         cur_path_ptr += rel_size;
                         *cur_path_ptr++ = PATH_SEPARATOR;
                         // only for ICD manifests
-                        if (env_override != NULL && strcmp(VK_ICD_FILENAMES_ENV_VAR, env_override) == 0) {
+                        if (!is_high_integrity() && env_override != NULL && strcmp(VK_ICD_FILENAMES_ENV_VAR, env_override) == 0) {
                             use_first_found_manifest = true;
                         }
                     }
index 93f3f5691bb1ca0f20267f51142d67aa4e671e6d..ff22c933b1c15f3eb9db253a62e77a38f8c9163e 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2021 The Khronos Group Inc.
- * Copyright (c) 2021 Valve Corporation
- * Copyright (c) 2021 LunarG, Inc.
+ * Copyright (c) 2021-2022 The Khronos Group Inc.
+ * Copyright (c) 2021-2022 Valve Corporation
+ * Copyright (c) 2021-2022 LunarG, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and/or associated documentation files (the "Materials"), to
@@ -132,8 +132,8 @@ struct PlatformShim {
 // platform specific shim interface
 #if defined(WIN32)
     // Control Platform Elevation Level
+    void set_elevated_privilege(bool elev) { (elev) ? SECURITY_MANDATORY_HIGH_RID : SECURITY_MANDATORY_LOW_RID; }
     unsigned long elevation_level = SECURITY_MANDATORY_LOW_RID;
-    void set_elevation_level(unsigned long new_elevation_level) { elevation_level = new_elevation_level; }
 
     void add_dxgi_adapter(fs::path const& manifest_path, GpuType gpu_preference, uint32_t known_driver_index,
                           DXGI_ADAPTER_DESC1 desc1);
@@ -162,6 +162,8 @@ struct PlatformShim {
 
     std::unordered_map<std::string, fs::path> redirection_map;
 
+    void set_elevated_privilege(bool elev) { use_fake_elevation = elev; }
+    bool use_fake_elevation = false;
 #endif
 };
 
index 3719123c0fa346082d75ddd95dd76a6db32e2ecb..ff2c6c0a80afa04d48b007d6a89e5cdc5ac7ad36 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2021 The Khronos Group Inc.
- * Copyright (c) 2021 Valve Corporation
- * Copyright (c) 2021 LunarG, Inc.
+ * Copyright (c) 2021-2022 The Khronos Group Inc.
+ * Copyright (c) 2021-2022 Valve Corporation
+ * Copyright (c) 2021-2022 LunarG, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and/or associated documentation files (the "Materials"), to
@@ -46,19 +46,27 @@ FRAMEWORK_EXPORT PlatformShim* get_platform_shim() {
 #define OPENDIR_FUNC_NAME opendir
 #define ACCESS_FUNC_NAME access
 #define FOPEN_FUNC_NAME fopen
+#define GETEUID_FUNC_NAME geteuid
+#define GETEGID_FUNC_NAME getegid
 #elif defined(__APPLE__)
 #define OPENDIR_FUNC_NAME my_opendir
 #define ACCESS_FUNC_NAME my_access
 #define FOPEN_FUNC_NAME my_fopen
+#define GETEUID_FUNC_NAME my_geteuid
+#define GETEGID_FUNC_NAME my_getegid
 #endif
 
 using PFN_OPENDIR = DIR* (*)(const char* path_name);
 using PFN_ACCESS = int (*)(const char* pathname, int mode);
 using PFN_FOPEN = FILE* (*)(const char* filename, const char* mode);
+using PFN_GETEUID = uid_t (*)(void);
+using PFN_GETEGID = gid_t (*)(void);
 
 static PFN_OPENDIR real_opendir = nullptr;
 static PFN_ACCESS real_access = nullptr;
 static PFN_FOPEN real_fopen = nullptr;
+static PFN_GETEUID real_geteuid = nullptr;
+static PFN_GETEGID real_getegid = nullptr;
 
 FRAMEWORK_EXPORT DIR* OPENDIR_FUNC_NAME(const char* path_name) {
     if (!real_opendir) real_opendir = (PFN_OPENDIR)dlsym(RTLD_NEXT, "opendir");
@@ -109,6 +117,28 @@ FRAMEWORK_EXPORT FILE* FOPEN_FUNC_NAME(const char* in_filename, const char* mode
     return f_ptr;
 }
 
+FRAMEWORK_EXPORT uid_t GETEUID_FUNC_NAME(void) {
+    if (!real_geteuid) real_geteuid = (PFN_GETEUID)dlsym(RTLD_NEXT, "geteuid");
+
+    if (platform_shim.use_fake_elevation) {
+        // Root on linux is 0, so force pretending like we're root
+        return 0;
+    } else {
+        return real_geteuid();
+    }
+}
+
+FRAMEWORK_EXPORT gid_t GETEGID_FUNC_NAME(void) {
+    if (!real_getegid) real_getegid = (PFN_GETEGID)dlsym(RTLD_NEXT, "getegid");
+
+    if (platform_shim.use_fake_elevation) {
+        // Root on linux is 0, so force pretending like we're root
+        return 0;
+    } else {
+        return real_getegid();
+    }
+}
+
 /* Shiming functions on apple is limited by the linker prefering to not use functions in the
  * executable in loaded dylibs. By adding an interposer, we redirect the linker to use our
  * version of the function over the real one, thus shimming the system function.
@@ -125,5 +155,7 @@ struct Interposer {
 __attribute__((used)) static Interposer _interpose_opendir MACOS_ATTRIB = {VOIDP_CAST(my_opendir), VOIDP_CAST(opendir)};
 __attribute__((used)) static Interposer _interpose_access MACOS_ATTRIB = {VOIDP_CAST(my_access), VOIDP_CAST(access)};
 __attribute__((used)) static Interposer _interpose_fopen MACOS_ATTRIB = {VOIDP_CAST(my_fopen), VOIDP_CAST(fopen)};
+__attribute__((used)) static Interposer _interpose_euid MACOS_ATTRIB = {VOIDP_CAST(my_geteuid), VOIDP_CAST(geteuid)};
+__attribute__((used)) static Interposer _interpose_egid MACOS_ATTRIB = {VOIDP_CAST(my_getegid), VOIDP_CAST(getegid)};
 #endif
 }  // extern "C"
index 142a9f0e0cd3f76a6ed15ba143eb07b62042559f..e23a026274cf04fbc30e403981c08a59b698ebb2 100644 (file)
@@ -2131,15 +2131,30 @@ TEST(EnvironmentVariables, VK_LAYER_PATH) {
             ManifestLayer::LayerDescription{}.set_name(layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
         "test_layer.json");
 
-    InstWrapper inst{env.vulkan_functions};
-    inst.create_info.add_layer(layer_name);
-    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
-    inst.CheckCreate();
+    InstWrapper inst1{env.vulkan_functions};
+    inst1.create_info.add_layer(layer_name);
+    FillDebugUtilsCreateDetails(inst1.create_info, env.debug_log);
+    inst1.CheckCreate();
 
     // look for VK_LAYER_PATHS
     EXPECT_TRUE(env.debug_log.find("/tmp/carol"));
     EXPECT_TRUE(env.debug_log.find("/tandy"));
     EXPECT_TRUE(env.debug_log.find((HOME / "/ with spaces/").str()));
+    EXPECT_FALSE(env.debug_log.find("Ignoring override VK_LAYER_PATH due to high-integrity"));
+
+    env.debug_log.clear();
+
+    env.platform_shim->set_elevated_privilege(true);
+
+    InstWrapper inst2{env.vulkan_functions};
+    inst2.create_info.add_layer(layer_name);
+    FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log);
+    inst2.CheckCreate();
+
+    EXPECT_TRUE(env.debug_log.find("Ignoring override VK_LAYER_PATH due to high-integrity"));
+    EXPECT_FALSE(env.debug_log.find("/tmp/carol"));
+
+    env.platform_shim->set_elevated_privilege(false);
 
     remove_env_var("VK_LAYER_PATH");
 }