Commit bf2620bc by Edward Thomson

fs_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 7e8d9be0
...@@ -1170,14 +1170,18 @@ int git_config_find_programdata(git_buf *path) ...@@ -1170,14 +1170,18 @@ int git_config_find_programdata(git_buf *path)
int git_config__find_programdata(git_str *path) int git_config__find_programdata(git_str *path)
{ {
int ret; bool is_safe;
ret = git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA); if (git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA) < 0 ||
git_fs_path_owner_is_system_or_current_user(&is_safe, path->ptr) < 0)
return -1;
if (ret != GIT_OK) if (!is_safe) {
return ret; git_error_set(GIT_ERROR_CONFIG, "programdata path has invalid ownership");
return -1;
}
return git_fs_path_validate_system_file_ownership(path->ptr); return 0;
} }
int git_config__global_location(git_str *buf) int git_config__global_location(git_str *buf)
......
...@@ -1785,82 +1785,199 @@ done: ...@@ -1785,82 +1785,199 @@ done:
return supported; return supported;
} }
int git_fs_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 (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
git_error_set(GIT_ERROR_OS, "could not lookup process information");
goto done;
} }
if (!IsValidSid(owner_sid)) { if (GetTokenInformation(token, TokenUser, NULL, 0, &len) ||
git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is unknown"); GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
ret = GIT_ERROR; git_error_set(GIT_ERROR_OS, "could not lookup token metadata");
goto cleanup; 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_fs_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_fs_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_fs_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) || if (IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) ||
IsWellKnownSid(owner_sid, WinLocalSystemSid)) { IsWellKnownSid(owner_sid, WinLocalSystemSid)) {
ret = GIT_OK; *out = 1;
goto cleanup; error = 0;
goto done;
} }
/* Obtain current user's SID */ if (current_user_sid(&user_sid) < 0)
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token) && goto done;
!GetTokenInformation(token, TokenUser, NULL, 0, &len)) {
info = git__malloc(len); *out = EqualSid(owner_sid, user_sid);
GIT_ERROR_CHECK_ALLOC(info); error = 0;
if (!GetTokenInformation(token, TokenUser, info, len, &len)) {
git__free(info); done:
info = NULL; git__free(owner_sid);
} git__free(user_sid);
return error;
}
#else
static int fs_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 the file is owned by the same account that is running the current if (uids[i] == st.st_uid) {
* process, it's okay to read from that file. *out = true;
*/ break;
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;
} }
git__free(info);
cleanup: return 0;
if (descriptor) }
LocalFree(descriptor);
return ret; int git_fs_path_owner_is_current_user(bool *out, const char *path)
#endif {
uid_t userid = geteuid();
return fs_path_owner_is(out, path, &userid, 1);
} }
int git_fs_path_owner_is_system(bool *out, const char *path)
{
uid_t userid = 0;
return fs_path_owner_is(out, path, &userid, 1);
}
int git_fs_path_owner_is_system_or_current_user(bool *out, const char *path)
{
uid_t userids[2] = { geteuid(), 0 };
return fs_path_owner_is(out, path, userids, 2);
}
#endif
int git_fs_path_find_executable(git_str *fullpath, const char *executable) int git_fs_path_find_executable(git_str *fullpath, const char *executable)
{ {
#ifdef GIT_WIN32 #ifdef GIT_WIN32
......
...@@ -732,16 +732,22 @@ int git_fs_path_normalize_slashes(git_str *out, const char *path); ...@@ -732,16 +732,22 @@ int git_fs_path_normalize_slashes(git_str *out, const char *path);
bool git_fs_path_supports_symlinks(const char *dir); bool git_fs_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_fs_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_fs_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_fs_path_validate_system_file_ownership(const char *path); int git_fs_path_owner_is_system_or_current_user(bool *out, const char *path);
/** /**
* Search the current PATH for the given executable, returning the full * Search the current PATH for the given executable, returning the full
......
...@@ -737,3 +737,28 @@ void test_path__find_exe_in_path(void) ...@@ -737,3 +737,28 @@ void test_path__find_exe_in_path(void)
git_str_dispose(&sandbox_path); git_str_dispose(&sandbox_path);
git__free(orig_path); git__free(orig_path);
} }
void test_path__validate_current_user_ownership(void)
{
bool is_cur;
cl_must_pass(p_mkdir("testdir", 0777));
cl_git_pass(git_fs_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_fs_path_owner_is_current_user(&is_cur, "testfile"));
cl_assert_equal_i(is_cur, 1);
#ifdef GIT_WIN32
cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "C:\\"));
cl_assert_equal_i(is_cur, 0);
cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "c:\\path\\does\\not\\exist"));
#else
cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "/"));
cl_assert_equal_i(is_cur, 0);
cl_git_fail(git_fs_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