aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2014-05-14 22:51:51 -0400
committerNick Mathewson <nickm@torproject.org>2014-05-14 22:51:51 -0400
commita6eea86a2c915ef424c17920139babcfe42f3704 (patch)
tree5ce52e3783d8b4b3d4e3de42c62b22875c4a658c /src/common
parenta88923e455cb0cae0f5fd7e705a2cbf45df2e5aa (diff)
parentf694a443fc35e91d301470c60a7989d4fbd6de71 (diff)
downloadtor-a6eea86a2c915ef424c17920139babcfe42f3704.tar
tor-a6eea86a2c915ef424c17920139babcfe42f3704.tar.gz
Merge branch 'bug11946'
Diffstat (limited to 'src/common')
-rw-r--r--src/common/compat.c109
-rw-r--r--src/common/compat.h5
-rw-r--r--src/common/util.c10
3 files changed, 115 insertions, 9 deletions
diff --git a/src/common/compat.c b/src/common/compat.c
index 7a444df10..b95609a26 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -1702,6 +1702,107 @@ log_credential_status(void)
}
#endif
+#ifndef _WIN32
+/** Cached struct from the last getpwname() call we did successfully. */
+static struct passwd *passwd_cached = NULL;
+
+/** Helper: copy a struct passwd object.
+ *
+ * We only copy the fields pw_uid, pw_gid, pw_name, pw_dir. Tor doesn't use
+ * any others, and I don't want to run into incompatibilities.
+ */
+static struct passwd *
+tor_passwd_dup(const struct passwd *pw)
+{
+ struct passwd *new_pw = tor_malloc_zero(sizeof(struct passwd));
+ if (pw->pw_name)
+ new_pw->pw_name = tor_strdup(pw->pw_name);
+ if (pw->pw_dir)
+ new_pw->pw_dir = tor_strdup(pw->pw_dir);
+ new_pw->pw_uid = pw->pw_uid;
+ new_pw->pw_gid = pw->pw_gid;
+
+ return new_pw;
+}
+
+/** Helper: free one of our cached 'struct passwd' values. */
+static void
+tor_passwd_free(struct passwd *pw)
+{
+ if (!pw)
+ return;
+
+ tor_free(pw->pw_name);
+ tor_free(pw->pw_dir);
+ tor_free(pw);
+}
+
+/** Wrapper around getpwnam() that caches result. Used so that we don't need
+ * to give the sandbox access to /etc/passwd.
+ *
+ * The following fields alone will definitely be copied in the output: pw_uid,
+ * pw_gid, pw_name, pw_dir. Other fields are not present in cached values.
+ *
+ * When called with a NULL argument, this function clears storage associated
+ * with static variables it uses.
+ **/
+const struct passwd *
+tor_getpwnam(const char *username)
+{
+ struct passwd *pw;
+
+ if (username == NULL) {
+ tor_passwd_free(passwd_cached);
+ passwd_cached = NULL;
+ return NULL;
+ }
+
+ if ((pw = getpwnam(username))) {
+ tor_passwd_free(passwd_cached);
+ passwd_cached = tor_passwd_dup(pw);
+ log_notice(LD_GENERAL, "Caching new entry %s for %s",
+ passwd_cached->pw_name, username);
+ return pw;
+ }
+
+ /* Lookup failed */
+ if (! passwd_cached || ! passwd_cached->pw_name)
+ return NULL;
+
+ if (! strcmp(username, passwd_cached->pw_name))
+ return passwd_cached;
+
+ return NULL;
+}
+
+/** Wrapper around getpwnam() that can use cached result from
+ * tor_getpwnam(). Used so that we don't need to give the sandbox access to
+ * /etc/passwd.
+ *
+ * The following fields alone will definitely be copied in the output: pw_uid,
+ * pw_gid, pw_name, pw_dir. Other fields are not present in cached values.
+ */
+const struct passwd *
+tor_getpwuid(uid_t uid)
+{
+ struct passwd *pw;
+
+ if ((pw = getpwuid(uid))) {
+ return pw;
+ }
+
+ /* Lookup failed */
+ if (! passwd_cached)
+ return NULL;
+
+ if (uid == passwd_cached->pw_uid)
+ return passwd_cached;
+
+ return NULL;
+}
+#endif
+
+
/** Call setuid and setgid to run as <b>user</b> and switch to their
* primary group. Return 0 on success. On failure, log and return -1.
*/
@@ -1709,7 +1810,7 @@ int
switch_id(const char *user)
{
#ifndef _WIN32
- struct passwd *pw = NULL;
+ const struct passwd *pw = NULL;
uid_t old_uid;
gid_t old_gid;
static int have_already_switched_id = 0;
@@ -1730,7 +1831,7 @@ switch_id(const char *user)
old_gid = getgid();
/* Lookup the user and group information, if we have a problem, bail out. */
- pw = getpwnam(user);
+ pw = tor_getpwnam(user);
if (pw == NULL) {
log_warn(LD_CONFIG, "Error setting configured user: %s not found", user);
return -1;
@@ -1901,10 +2002,10 @@ tor_disable_debugger_attach(void)
char *
get_user_homedir(const char *username)
{
- struct passwd *pw;
+ const struct passwd *pw;
tor_assert(username);
- if (!(pw = getpwnam(username))) {
+ if (!(pw = tor_getpwnam(username))) {
log_err(LD_CONFIG,"User \"%s\" not found.", username);
return NULL;
}
diff --git a/src/common/compat.h b/src/common/compat.h
index 314b1aa00..683c4d089 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -633,6 +633,11 @@ int switch_id(const char *user);
char *get_user_homedir(const char *username);
#endif
+#ifndef _WIN32
+const struct passwd *tor_getpwnam(const char *username);
+const struct passwd *tor_getpwuid(uid_t uid);
+#endif
+
int get_parent_directory(char *fname);
char *make_path_absolute(char *fname);
diff --git a/src/common/util.c b/src/common/util.c
index f2b21407b..e27036a84 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -1871,7 +1871,7 @@ check_private_dir(const char *dirname, cpd_check_t check,
char *f;
#ifndef _WIN32
int mask;
- struct passwd *pw = NULL;
+ const struct passwd *pw = NULL;
uid_t running_uid;
gid_t running_gid;
#else
@@ -1918,7 +1918,7 @@ check_private_dir(const char *dirname, cpd_check_t check,
if (effective_user) {
/* Look up the user and group information.
* If we have a problem, bail out. */
- pw = getpwnam(effective_user);
+ pw = tor_getpwnam(effective_user);
if (pw == NULL) {
log_warn(LD_CONFIG, "Error setting configured user: %s not found",
effective_user);
@@ -1932,13 +1932,13 @@ check_private_dir(const char *dirname, cpd_check_t check,
}
if (st.st_uid != running_uid) {
- struct passwd *pw = NULL;
+ const struct passwd *pw = NULL;
char *process_ownername = NULL;
- pw = getpwuid(running_uid);
+ pw = tor_getpwuid(running_uid);
process_ownername = pw ? tor_strdup(pw->pw_name) : tor_strdup("<unknown>");
- pw = getpwuid(st.st_uid);
+ pw = tor_getpwuid(st.st_uid);
log_warn(LD_FS, "%s is not owned by this user (%s, %d) but by "
"%s (%d). Perhaps you are running Tor as the wrong user?",