Skip to content

Commit 0967459

Browse files
committed
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.
1 parent 45f5840 commit 0967459

File tree

1 file changed

+74
-7
lines changed

1 file changed

+74
-7
lines changed

src/sysdir.c

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
#include <ctype.h>
1414
#if GIT_WIN32
1515
#include "win32/findfile.h"
16+
#else
17+
#include <unistd.h>
18+
#include <pwd.h>
1619
#endif
1720

1821
static int git_sysdir_guess_programdata_dirs(git_buf *out)
@@ -34,12 +37,63 @@ static int git_sysdir_guess_system_dirs(git_buf *out)
3437
#endif
3538
}
3639

40+
#ifndef GIT_WIN32
41+
static int get_passwd_home(git_buf *out, uid_t uid)
42+
{
43+
struct passwd pwd, *pwdptr;
44+
char *buf = NULL;
45+
long buflen;
46+
int error;
47+
48+
assert(out);
49+
50+
if ((buflen = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1)
51+
buflen = 1024;
52+
53+
do {
54+
buf = git__realloc(buf, buflen);
55+
error = getpwuid_r(uid, &pwd, buf, buflen, &pwdptr);
56+
buflen *= 2;
57+
} while (error == ERANGE && buflen <= 8192);
58+
59+
if (error) {
60+
giterr_set(GITERR_OS, "failed to get passwd entry");
61+
goto out;
62+
}
63+
64+
if (!pwdptr) {
65+
giterr_set(GITERR_OS, "no passwd entry found for user");
66+
goto out;
67+
}
68+
69+
if ((error = git_buf_puts(out, pwdptr->pw_dir)) < 0)
70+
goto out;
71+
72+
out:
73+
git__free(buf);
74+
return error;
75+
}
76+
#endif
77+
3778
static int git_sysdir_guess_global_dirs(git_buf *out)
3879
{
3980
#ifdef GIT_WIN32
4081
return git_win32__find_global_dirs(out);
4182
#else
42-
int error = git__getenv(out, "HOME");
83+
int error;
84+
uid_t uid, euid;
85+
86+
uid = getuid();
87+
euid = geteuid();
88+
89+
/*
90+
* In case we are running setuid, use the configuration
91+
* of the effective user.
92+
*/
93+
if (uid == euid)
94+
error = git__getenv(out, "HOME");
95+
else
96+
error = get_passwd_home(out, euid);
4397

4498
if (error == GIT_ENOTFOUND) {
4599
giterr_clear();
@@ -57,12 +111,25 @@ static int git_sysdir_guess_xdg_dirs(git_buf *out)
57111
#else
58112
git_buf env = GIT_BUF_INIT;
59113
int error;
60-
61-
if ((error = git__getenv(&env, "XDG_CONFIG_HOME")) == 0)
62-
error = git_buf_joinpath(out, env.ptr, "git");
63-
64-
if (error == GIT_ENOTFOUND && (error = git__getenv(&env, "HOME")) == 0)
65-
error = git_buf_joinpath(out, env.ptr, ".config/git");
114+
uid_t uid, euid;
115+
116+
uid = getuid();
117+
euid = geteuid();
118+
119+
/*
120+
* In case we are running setuid, only look up passwd
121+
* directory of the effective user.
122+
*/
123+
if (uid == euid) {
124+
if ((error = git__getenv(&env, "XDG_CONFIG_HOME")) == 0)
125+
error = git_buf_joinpath(out, env.ptr, "git");
126+
127+
if (error == GIT_ENOTFOUND && (error = git__getenv(&env, "HOME")) == 0)
128+
error = git_buf_joinpath(out, env.ptr, ".config/git");
129+
} else {
130+
if ((error = get_passwd_home(&env, euid)) == 0)
131+
error = git_buf_joinpath(out, env.ptr, ".config/git");
132+
}
66133

67134
if (error == GIT_ENOTFOUND) {
68135
giterr_clear();

0 commit comments

Comments
 (0)