Sfoglia il codice sorgente

Merge branch 'bug11946'

Nick Mathewson 10 anni fa
parent
commit
a6eea86a2c
7 ha cambiato i file con 126 aggiunte e 12 eliminazioni
  1. 5 0
      changes/bug11946
  2. 105 4
      src/common/compat.c
  3. 5 0
      src/common/compat.h
  4. 5 5
      src/common/util.c
  5. 2 2
      src/or/connection.c
  6. 1 1
      src/or/control.c
  7. 3 0
      src/or/main.c

+ 5 - 0
changes/bug11946

@@ -0,0 +1,5 @@
+  o Minor bugfixes (sandbox):
+
+    - Handle failures in getpwnam()/getpwuid() when running with the
+      User option set and the Linux syscall sandbox enabled. Fixes bug
+      11946; bugfix on 0.2.5.1-alpha.

+ 105 - 4
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;
   }

+ 5 - 0
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);
 

+ 5 - 5
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?",

+ 2 - 2
src/or/connection.c

@@ -1017,7 +1017,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
   tor_socket_t s = TOR_INVALID_SOCKET;  /* the socket we're going to make */
   or_options_t const *options = get_options();
 #if defined(HAVE_PWD_H) && defined(HAVE_SYS_UN_H)
-  struct passwd *pw = NULL;
+  const struct passwd *pw = NULL;
 #endif
   uint16_t usePort = 0, gotPort = 0;
   int start_reading = 0;
@@ -1157,7 +1157,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
     }
 #ifdef HAVE_PWD_H
     if (options->User) {
-      pw = getpwnam(options->User);
+      pw = tor_getpwnam(options->User);
       if (pw == NULL) {
         log_warn(LD_NET,"Unable to chown() %s socket: user %s not found.",
                  address, options->User);

+ 1 - 1
src/or/control.c

@@ -1492,7 +1492,7 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
       *answer = tor_strdup("");
     #else
       int myUid = geteuid();
-      struct passwd *myPwEntry = getpwuid(myUid);
+      const struct passwd *myPwEntry = tor_getpwuid(myUid);
 
       if (myPwEntry) {
         *answer = tor_strdup(myPwEntry->pw_name);

+ 3 - 0
src/or/main.c

@@ -2548,6 +2548,9 @@ tor_free_all(int postfork)
   free_cell_pool();
   if (!postfork) {
     tor_tls_free_all();
+#ifndef _WIN32
+    tor_getpwnam(NULL);
+#endif
   }
   /* stuff in main.c */