Commit ed24b8bacc739b072a789ca7396e46bd751ca2ec

Edward Thomson 2022-07-05T23:47:15

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.

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;
 
 /**