Commit 973d959a by Edward Thomson

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.
parent 670415a5
...@@ -1118,16 +1118,20 @@ int git_config_find_system(git_buf *path) ...@@ -1118,16 +1118,20 @@ int git_config_find_system(git_buf *path)
int git_config_find_programdata(git_buf *path) int git_config_find_programdata(git_buf *path)
{ {
int ret; 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; return ret;
ret = git_sysdir_find_programdata_file(path, if (!is_safe) {
GIT_CONFIG_FILENAME_PROGRAMDATA); git_error_set(GIT_ERROR_CONFIG, "programdata path has invalid ownership");
if (ret != GIT_OK) return -1;
return ret; }
return git_path_validate_system_file_ownership(path->ptr); return 0;
} }
int git_config__global_location(git_buf *buf) int git_config__global_location(git_buf *buf)
......
...@@ -2024,78 +2024,195 @@ done: ...@@ -2024,78 +2024,195 @@ done:
return supported; return supported;
} }
int git_path_validate_system_file_ownership(const char *path) #ifdef GIT_WIN32
static PSID *sid_dup(PSID sid)
{ {
#ifndef GIT_WIN32 DWORD len;
GIT_UNUSED(path); PSID dup;
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;
if (git_win32_path_from_utf8(buf, path) < 0) len = GetLengthSid(sid);
return -1;
err = GetNamedSecurityInfoW(buf, SE_FILE_OBJECT, if ((dup = git__malloc(len)) == NULL)
OWNER_SECURITY_INFORMATION | return NULL;
DACL_SECURITY_INFORMATION,
&owner_sid, NULL, NULL, NULL, &descriptor);
if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { if (!CopySid(len, dup, sid)) {
ret = GIT_ENOTFOUND; git_error_set(GIT_ERROR_OS, "could not duplicate sid");
goto cleanup; git__free(dup);
return NULL;
} }
if (err != ERROR_SUCCESS) { return dup;
git_error_set(GIT_ERROR_OS, "failed to get security information"); }
ret = GIT_ERROR;
goto cleanup; static int current_user_sid(PSID *out)
} {
TOKEN_USER *info = NULL;
HANDLE token = NULL;
DWORD len = 0;
int error = -1;
if (!IsValidSid(owner_sid)) { if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is unknown"); git_error_set(GIT_ERROR_OS, "could not lookup process information");
ret = GIT_ERROR; goto done;
goto cleanup;
} }
if (IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) || if (GetTokenInformation(token, TokenUser, NULL, 0, &len) ||
IsWellKnownSid(owner_sid, WinLocalSystemSid)) { GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
ret = GIT_OK; git_error_set(GIT_ERROR_OS, "could not lookup token metadata");
goto cleanup; 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;
}
} }
/* info = git__malloc(len);
* If the file is owned by the same account that is running the current GIT_ERROR_CHECK_ALLOC(info);
* process, it's okay to read from that file.
*/ if (!GetTokenInformation(token, TokenUser, info, len, &len)) {
if (info && EqualSid(owner_sid, info->User.Sid)) git_error_set(GIT_ERROR_OS, "could not lookup current user");
ret = GIT_OK; goto done;
else {
git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is not valid");
ret = GIT_ERROR;
} }
if ((*out = sid_dup(info->User.Sid)))
error = 0;
done:
if (token)
CloseHandle(token);
git__free(info); 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;
cleanup:
if (descriptor) if (descriptor)
LocalFree(descriptor); LocalFree(descriptor);
return ret; return error;
#endif
} }
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)) {
*out = 1;
error = 0;
goto done;
}
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;
}
for (i = 0; i < uids_len; i++) {
if (uids[i] == st.st_uid) {
*out = true;
break;
}
}
return 0;
}
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
...@@ -723,15 +723,21 @@ int git_path_normalize_slashes(git_buf *out, const char *path); ...@@ -723,15 +723,21 @@ int git_path_normalize_slashes(git_buf *out, const char *path);
bool git_path_supports_symlinks(const char *dir); bool git_path_supports_symlinks(const char *dir);
/** /**
* Validate a system file's ownership
*
* Verify that the file in question is owned by an administrator or system * Verify that the file in question is owned by an administrator or system
* account, or at least by the current user. * account.
* */
* This function returns 0 if successful. If the file is not owned by any of int git_path_owner_is_system(bool *out, const char *path);
* these, or any other if there have been problems determining the file
* ownership, it returns -1. /**
* 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 #endif
...@@ -659,3 +659,28 @@ void test_core_path__git_path_is_file(void) ...@@ -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_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)); 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
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment