File 0001-path-refactor-ownership-checks-into-current-user-and.patch of Package libgit2
From 973d959abaa5faaddec6a4c27889ff5b980d2550 Mon Sep 17 00:00:00 2001
From: Edward Thomson <ethomson@edwardthomson.com>
Date: Sun, 10 Apr 2022 21:29:43 +0100
Subject: [PATCH 01/20] path: refactor ownership checks into current user and
system
Provide individual file ownership checks for both the current user and
the system user, as well as a combined current user and system user
check.
---
src/config.c | 16 ++--
src/path.c | 231 ++++++++++++++++++++++++++++++++++------------
src/path.h | 22 +++--
tests/core/path.c | 25 +++++
4 files changed, 223 insertions(+), 71 deletions(-)
diff --git a/src/config.c b/src/config.c
index 3251cd51f..489eee643 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1118,16 +1118,20 @@ int git_config_find_system(git_buf *path)
int git_config_find_programdata(git_buf *path)
{
int ret;
+ bool is_safe;
- if ((ret = git_buf_sanitize(path)) < 0)
+ if ((ret = git_buf_sanitize(path)) < 0 ||
+ (ret = git_sysdir_find_programdata_file(path,
+ GIT_CONFIG_FILENAME_PROGRAMDATA)) < 0 ||
+ (ret = git_path_owner_is_system_or_current_user(&is_safe, path->ptr)) < 0)
return ret;
- ret = git_sysdir_find_programdata_file(path,
- GIT_CONFIG_FILENAME_PROGRAMDATA);
- if (ret != GIT_OK)
- return ret;
+ if (!is_safe) {
+ git_error_set(GIT_ERROR_CONFIG, "programdata path has invalid ownership");
+ return -1;
+ }
- return git_path_validate_system_file_ownership(path->ptr);
+ return 0;
}
int git_config__global_location(git_buf *buf)
diff --git a/src/path.c b/src/path.c
index c444b31a7..b3bb0b408 100644
--- a/src/path.c
+++ b/src/path.c
@@ -1942,78 +1942,195 @@
}
}
-int git_path_validate_system_file_ownership(const char *path)
+#ifdef GIT_WIN32
+static PSID *sid_dup(PSID sid)
{
-#ifndef GIT_WIN32
- GIT_UNUSED(path);
- return GIT_OK;
-#else
- git_win32_path buf;
- PSID owner_sid;
- PSECURITY_DESCRIPTOR descriptor = NULL;
- HANDLE token;
- TOKEN_USER *info = NULL;
- DWORD err, len;
- int ret;
+ DWORD len;
+ PSID dup;
- if (git_win32_path_from_utf8(buf, path) < 0)
- return -1;
+ len = GetLengthSid(sid);
- err = GetNamedSecurityInfoW(buf, SE_FILE_OBJECT,
- OWNER_SECURITY_INFORMATION |
- DACL_SECURITY_INFORMATION,
- &owner_sid, NULL, NULL, NULL, &descriptor);
+ if ((dup = git__malloc(len)) == NULL)
+ return NULL;
- if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
- ret = GIT_ENOTFOUND;
- goto cleanup;
+ if (!CopySid(len, dup, sid)) {
+ git_error_set(GIT_ERROR_OS, "could not duplicate sid");
+ git__free(dup);
+ return NULL;
}
- if (err != ERROR_SUCCESS) {
- git_error_set(GIT_ERROR_OS, "failed to get security information");
- ret = GIT_ERROR;
- goto cleanup;
+ return dup;
+}
+
+static int current_user_sid(PSID *out)
+{
+ TOKEN_USER *info = NULL;
+ HANDLE token = NULL;
+ DWORD len = 0;
+ int error = -1;
+
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
+ git_error_set(GIT_ERROR_OS, "could not lookup process information");
+ goto done;
}
- if (!IsValidSid(owner_sid)) {
- git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is unknown");
- ret = GIT_ERROR;
- goto cleanup;
+ if (GetTokenInformation(token, TokenUser, NULL, 0, &len) ||
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ git_error_set(GIT_ERROR_OS, "could not lookup token metadata");
+ goto done;
+ }
+
+ info = git__malloc(len);
+ GIT_ERROR_CHECK_ALLOC(info);
+
+ if (!GetTokenInformation(token, TokenUser, info, len, &len)) {
+ git_error_set(GIT_ERROR_OS, "could not lookup current user");
+ goto done;
}
+ if ((*out = sid_dup(info->User.Sid)))
+ error = 0;
+
+done:
+ if (token)
+ CloseHandle(token);
+
+ git__free(info);
+ return error;
+}
+
+static int file_owner_sid(PSID *out, const char *path)
+{
+ git_win32_path path_w32;
+ PSECURITY_DESCRIPTOR descriptor = NULL;
+ PSID owner_sid;
+ DWORD ret;
+ int error = -1;
+
+ if (git_win32_path_from_utf8(path_w32, path) < 0)
+ return -1;
+
+ ret = GetNamedSecurityInfoW(path_w32, SE_FILE_OBJECT,
+ OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
+ &owner_sid, NULL, NULL, NULL, &descriptor);
+
+ if (ret == ERROR_FILE_NOT_FOUND || ret == ERROR_PATH_NOT_FOUND)
+ error = GIT_ENOTFOUND;
+ else if (ret != ERROR_SUCCESS)
+ git_error_set(GIT_ERROR_OS, "failed to get security information");
+ else if (!IsValidSid(owner_sid))
+ git_error_set(GIT_ERROR_OS, "file owner is not valid");
+ else if ((*out = sid_dup(owner_sid)))
+ error = 0;
+
+ if (descriptor)
+ LocalFree(descriptor);
+
+ return error;
+}
+
+int git_path_owner_is_current_user(bool *out, const char *path)
+{
+ PSID owner_sid = NULL, user_sid = NULL;
+ int error = -1;
+
+ if ((error = file_owner_sid(&owner_sid, path)) < 0 ||
+ (error = current_user_sid(&user_sid)) < 0)
+ goto done;
+
+ *out = EqualSid(owner_sid, user_sid);
+ error = 0;
+
+done:
+ git__free(owner_sid);
+ git__free(user_sid);
+ return error;
+}
+
+int git_path_owner_is_system(bool *out, const char *path)
+{
+ PSID owner_sid;
+
+ if (file_owner_sid(&owner_sid, path) < 0)
+ return -1;
+
+ *out = IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) ||
+ IsWellKnownSid(owner_sid, WinLocalSystemSid);
+
+ git__free(owner_sid);
+ return 0;
+}
+
+int git_path_owner_is_system_or_current_user(bool *out, const char *path)
+{
+ PSID owner_sid = NULL, user_sid = NULL;
+ int error = -1;
+
+ if (file_owner_sid(&owner_sid, path) < 0)
+ goto done;
+
if (IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) ||
IsWellKnownSid(owner_sid, WinLocalSystemSid)) {
- ret = GIT_OK;
- goto cleanup;
+ *out = 1;
+ error = 0;
+ goto done;
}
- /* Obtain current user's SID */
- if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token) &&
- !GetTokenInformation(token, TokenUser, NULL, 0, &len)) {
- info = git__malloc(len);
- GIT_ERROR_CHECK_ALLOC(info);
- if (!GetTokenInformation(token, TokenUser, info, len, &len)) {
- git__free(info);
- info = NULL;
- }
+ if (current_user_sid(&user_sid) < 0)
+ goto done;
+
+ *out = EqualSid(owner_sid, user_sid);
+ error = 0;
+
+done:
+ git__free(owner_sid);
+ git__free(user_sid);
+ return error;
+}
+
+#else
+
+static int path_owner_is(bool *out, const char *path, uid_t *uids, size_t uids_len)
+{
+ struct stat st;
+ size_t i;
+
+ *out = false;
+
+ if (p_lstat(path, &st) != 0) {
+ if (errno == ENOENT)
+ return GIT_ENOTFOUND;
+
+ git_error_set(GIT_ERROR_OS, "could not stat '%s'", path);
+ return -1;
}
- /*
- * If the file is owned by the same account that is running the current
- * process, it's okay to read from that file.
- */
- if (info && EqualSid(owner_sid, info->User.Sid))
- ret = GIT_OK;
- else {
- git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is not valid");
- ret = GIT_ERROR;
+ for (i = 0; i < uids_len; i++) {
+ if (uids[i] == st.st_uid) {
+ *out = true;
+ break;
+ }
}
- free(info);
-cleanup:
- if (descriptor)
- LocalFree(descriptor);
+ return 0;
+}
- return ret;
-#endif
+int git_path_owner_is_current_user(bool *out, const char *path)
+{
+ uid_t userid = geteuid();
+ return path_owner_is(out, path, &userid, 1);
}
+
+int git_path_owner_is_system(bool *out, const char *path)
+{
+ uid_t userid = 0;
+ return path_owner_is(out, path, &userid, 1);
+}
+
+int git_path_owner_is_system_or_current_user(bool *out, const char *path)
+{
+ uid_t userids[2] = { geteuid(), 0 };
+ return path_owner_is(out, path, userids, 2);
+}
+
+#endif
diff --git a/src/path.h b/src/path.h
index de6ec8ff2..699945bd7 100644
--- a/src/path.h
+++ b/src/path.h
@@ -723,15 +723,21 @@ int git_path_normalize_slashes(git_buf *out, const char *path);
# bool git_path_supports_symlinks(const char *dir);
int git_path_normalize_slashes(git_buf *out, const char *path);
/**
- * Validate a system file's ownership
- *
* Verify that the file in question is owned by an administrator or system
- * account, or at least by the current user.
- *
- * This function returns 0 if successful. If the file is not owned by any of
- * these, or any other if there have been problems determining the file
- * ownership, it returns -1.
+ * account.
+ */
+int git_path_owner_is_system(bool *out, const char *path);
+
+/**
+ * Verify that the file in question is owned by the current user;
+ */
+
+int git_path_owner_is_current_user(bool *out, const char *path);
+
+/**
+ * Verify that the file in question is owned by an administrator or system
+ * account _or_ the current user;
*/
-int git_path_validate_system_file_ownership(const char *path);
+int git_path_owner_is_system_or_current_user(bool *out, const char *path);
#endif
diff --git a/tests/core/path.c b/tests/core/path.c
index eac3573fe..321b492c9 100644
--- a/tests/core/path.c
+++ b/tests/core/path.c
@@ -659,3 +659,28 @@ void test_core_path__git_path_is_file(void)
cl_git_pass(git_path_is_gitfile("blob", 4, GIT_PATH_GITFILE_GITATTRIBUTES, GIT_PATH_FS_HFS));
cl_git_fail(git_path_is_gitfile("blob", 4, 3, GIT_PATH_FS_HFS));
}
+
+void test_core_path__validate_current_user_ownership(void)
+{
+ bool is_cur;
+
+ cl_must_pass(p_mkdir("testdir", 0777));
+ cl_git_pass(git_path_owner_is_current_user(&is_cur, "testdir"));
+ cl_assert_equal_i(is_cur, 1);
+
+ cl_git_rewritefile("testfile", "This is a test file.");
+ cl_git_pass(git_path_owner_is_current_user(&is_cur, "testfile"));
+ cl_assert_equal_i(is_cur, 1);
+
+#ifdef GIT_WIN32
+ cl_git_pass(git_path_owner_is_current_user(&is_cur, "C:\\"));
+ cl_assert_equal_i(is_cur, 0);
+
+ cl_git_fail(git_path_owner_is_current_user(&is_cur, "c:\\path\\does\\not\\exist"));
+#else
+ cl_git_pass(git_path_owner_is_current_user(&is_cur, "/"));
+ cl_assert_equal_i(is_cur, 0);
+
+ cl_git_fail(git_path_owner_is_current_user(&is_cur, "/path/does/not/exist"));
+#endif
+}
--
2.37.1