summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2018-01-25 13:11:34 +0000
committerPatrick Steinhardt <ps@pks.im>2018-02-02 07:48:26 +0000
commit0967459ebc9e57bb2a7373ce0d00361a16eebc55 (patch)
treefbddbc0506ef47e9056dd4de026a7e881e5bf5e2
parent45f584090818c59ba27ca95b1e930a41c424d6f1 (diff)
downloadlibgit2-0967459ebc9e57bb2a7373ce0d00361a16eebc55.tar.gz
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.
-rw-r--r--src/sysdir.c81
1 files changed, 74 insertions, 7 deletions
diff --git a/src/sysdir.c b/src/sysdir.c
index 7480e82fd..509b23b82 100644
--- a/src/sysdir.c
+++ b/src/sysdir.c
@@ -13,6 +13,9 @@
#include <ctype.h>
#if GIT_WIN32
#include "win32/findfile.h"
+#else
+#include <unistd.h>
+#include <pwd.h>
#endif
static int git_sysdir_guess_programdata_dirs(git_buf *out)
@@ -34,12 +37,63 @@ static int git_sysdir_guess_system_dirs(git_buf *out)
#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)
{
#ifdef GIT_WIN32
return git_win32__find_global_dirs(out);
#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) {
giterr_clear();
@@ -57,12 +111,25 @@ static int git_sysdir_guess_xdg_dirs(git_buf *out)
#else
git_buf env = GIT_BUF_INIT;
int error;
-
- 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");
+ uid_t uid, euid;
+
+ uid = getuid();
+ euid = geteuid();
+
+ /*
+ * 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) {
giterr_clear();