Commit 0967459e by Patrick Steinhardt

sysdir: do not use environment in setuid case

In order to derive the location of some Git directories, we currently
use the environment variables $HOME and $XDG_CONFIG_HOME. This might
prove to be problematic whenever the binary is run with setuid, that is
when the effective user does not equal the real user. In case the
environment variables do not get sanitized by the caller, we thus might
end up using the real user's configuration when doing stuff as the
effective user.

The fix is to use the passwd entry's directory instead of $HOME in this
situation. As this might break scenarios where the user explicitly sets
$HOME to another path, this fix is only applied in case the effective
user does not equal the real user.
parent 45f58409
...@@ -13,6 +13,9 @@ ...@@ -13,6 +13,9 @@
#include <ctype.h> #include <ctype.h>
#if GIT_WIN32 #if GIT_WIN32
#include "win32/findfile.h" #include "win32/findfile.h"
#else
#include <unistd.h>
#include <pwd.h>
#endif #endif
static int git_sysdir_guess_programdata_dirs(git_buf *out) static int git_sysdir_guess_programdata_dirs(git_buf *out)
...@@ -34,12 +37,63 @@ static int git_sysdir_guess_system_dirs(git_buf *out) ...@@ -34,12 +37,63 @@ static int git_sysdir_guess_system_dirs(git_buf *out)
#endif #endif
} }
#ifndef GIT_WIN32
static int get_passwd_home(git_buf *out, uid_t uid)
{
struct passwd pwd, *pwdptr;
char *buf = NULL;
long buflen;
int error;
assert(out);
if ((buflen = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1)
buflen = 1024;
do {
buf = git__realloc(buf, buflen);
error = getpwuid_r(uid, &pwd, buf, buflen, &pwdptr);
buflen *= 2;
} while (error == ERANGE && buflen <= 8192);
if (error) {
giterr_set(GITERR_OS, "failed to get passwd entry");
goto out;
}
if (!pwdptr) {
giterr_set(GITERR_OS, "no passwd entry found for user");
goto out;
}
if ((error = git_buf_puts(out, pwdptr->pw_dir)) < 0)
goto out;
out:
git__free(buf);
return error;
}
#endif
static int git_sysdir_guess_global_dirs(git_buf *out) static int git_sysdir_guess_global_dirs(git_buf *out)
{ {
#ifdef GIT_WIN32 #ifdef GIT_WIN32
return git_win32__find_global_dirs(out); return git_win32__find_global_dirs(out);
#else #else
int error = git__getenv(out, "HOME"); int error;
uid_t uid, euid;
uid = getuid();
euid = geteuid();
/*
* In case we are running setuid, use the configuration
* of the effective user.
*/
if (uid == euid)
error = git__getenv(out, "HOME");
else
error = get_passwd_home(out, euid);
if (error == GIT_ENOTFOUND) { if (error == GIT_ENOTFOUND) {
giterr_clear(); giterr_clear();
...@@ -57,12 +111,25 @@ static int git_sysdir_guess_xdg_dirs(git_buf *out) ...@@ -57,12 +111,25 @@ static int git_sysdir_guess_xdg_dirs(git_buf *out)
#else #else
git_buf env = GIT_BUF_INIT; git_buf env = GIT_BUF_INIT;
int error; int error;
uid_t uid, euid;
if ((error = git__getenv(&env, "XDG_CONFIG_HOME")) == 0)
error = git_buf_joinpath(out, env.ptr, "git"); uid = getuid();
euid = geteuid();
if (error == GIT_ENOTFOUND && (error = git__getenv(&env, "HOME")) == 0)
error = git_buf_joinpath(out, env.ptr, ".config/git"); /*
* In case we are running setuid, only look up passwd
* directory of the effective user.
*/
if (uid == euid) {
if ((error = git__getenv(&env, "XDG_CONFIG_HOME")) == 0)
error = git_buf_joinpath(out, env.ptr, "git");
if (error == GIT_ENOTFOUND && (error = git__getenv(&env, "HOME")) == 0)
error = git_buf_joinpath(out, env.ptr, ".config/git");
} else {
if ((error = get_passwd_home(&env, euid)) == 0)
error = git_buf_joinpath(out, env.ptr, ".config/git");
}
if (error == GIT_ENOTFOUND) { if (error == GIT_ENOTFOUND) {
giterr_clear(); giterr_clear();
......
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