repo: allow users running with sudo to access their repositories In the ownership checks implemented for CVE-2022-24765, we disallowed users to access their own repositories when running with `sudo`. Examine the `SUDO_UID` environment variable and allow users running with `sudo`. This matches git's behavior.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
diff --git a/src/libgit2/repository.c b/src/libgit2/repository.c
index 17aec00..f761b5f 100644
--- a/src/libgit2/repository.c
+++ b/src/libgit2/repository.c
@@ -531,7 +531,8 @@ static int validate_ownership_path(bool *is_safe, const char *path)
{
git_fs_path_owner_t owner_level =
GIT_FS_PATH_OWNER_CURRENT_USER |
- GIT_FS_PATH_USER_IS_ADMINISTRATOR;
+ GIT_FS_PATH_USER_IS_ADMINISTRATOR |
+ GIT_FS_PATH_OWNER_RUNNING_SUDO;
int error = 0;
if (path)
diff --git a/src/util/fs_path.c b/src/util/fs_path.c
index 1eb41ef..6c87bfd 100644
--- a/src/util/fs_path.c
+++ b/src/util/fs_path.c
@@ -1934,27 +1934,36 @@ done:
#else
+static int sudo_uid_lookup(uid_t *out)
+{
+ git_str uid_str = GIT_STR_INIT;
+ int64_t uid;
+ int error;
+
+ if ((error = git__getenv(&uid_str, "SUDO_UID")) == 0 &&
+ (error = git__strntol64(&uid, uid_str.ptr, uid_str.size, NULL, 10)) == 0 &&
+ uid == (int64_t)((uid_t)uid)) {
+ *out = (uid_t)uid;
+ }
+
+ git_str_dispose(&uid_str);
+ return error;
+}
+
int git_fs_path_owner_is(
bool *out,
const char *path,
git_fs_path_owner_t owner_type)
{
- uid_t uids[2] = { 0 };
- size_t uid_count = 0, i;
struct stat st;
+ uid_t euid, sudo_uid;
if (mock_owner) {
*out = ((mock_owner & owner_type) != 0);
return 0;
}
- if (owner_type & GIT_FS_PATH_OWNER_CURRENT_USER)
- uids[uid_count++] = geteuid();
-
- if (owner_type & GIT_FS_PATH_OWNER_ADMINISTRATOR)
- uids[uid_count++] = 0;
-
- *out = false;
+ euid = geteuid();
if (p_lstat(path, &st) != 0) {
if (errno == ENOENT)
@@ -1964,13 +1973,27 @@ int git_fs_path_owner_is(
return -1;
}
- for (i = 0; i < uid_count; i++) {
- if (uids[i] == st.st_uid) {
- *out = true;
- break;
- }
+ if ((owner_type & GIT_FS_PATH_OWNER_CURRENT_USER) != 0 &&
+ st.st_uid == euid) {
+ *out = true;
+ return 0;
+ }
+
+ if ((owner_type & GIT_FS_PATH_OWNER_ADMINISTRATOR) != 0 &&
+ st.st_uid == 0) {
+ *out = true;
+ return 0;
+ }
+
+ if ((owner_type & GIT_FS_PATH_OWNER_RUNNING_SUDO) != 0 &&
+ euid == 0 &&
+ sudo_uid_lookup(&sudo_uid) == 0 &&
+ st.st_uid == sudo_uid) {
+ *out = true;
+ return 0;
}
+ *out = false;
return 0;
}
diff --git a/src/util/fs_path.h b/src/util/fs_path.h
index fd045a3..e5ca673 100644
--- a/src/util/fs_path.h
+++ b/src/util/fs_path.h
@@ -747,8 +747,13 @@ typedef enum {
*/
GIT_FS_PATH_USER_IS_ADMINISTRATOR = (1 << 2),
+ /**
+ * The file is owned by the current user, who is running `sudo`.
+ */
+ GIT_FS_PATH_OWNER_RUNNING_SUDO = (1 << 3),
+
/** The file may be owned by another user. */
- GIT_FS_PATH_OWNER_OTHER = (1 << 3)
+ GIT_FS_PATH_OWNER_OTHER = (1 << 4)
} git_fs_path_owner_t;
/**