[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

please unblock tor 0.2.0.32-1



Hi,

I would like to get tor 0.2.0.32-1 into lenny.

It's a new upstream version of the current stable tree.  That means it
only saw bugfixes and no new features.

At least one of the fixes is a security fix in the traditional sense:
Tor didn't properly drop supplementary group entries on startup.  See
the changelog for a list of other bug fixes.

I have attached a somewhat cleaned upstream diff, i.e. only including
files that influence what ends up in the .deb - not stuff like the
MacOSX packaging, and the full .deb interdiff.

-- 
                           |  .''`.  ** Debian GNU/Linux **
      Peter Palfrader      | : :' :      The  universal
 http://www.palfrader.org/ | `. `'      Operating System
                           |   `-    http://www.debian.org/
diff -Nur tor-0.2.0.31/ChangeLog tor-0.2.0.32/ChangeLog
--- tor-0.2.0.31/ChangeLog	2008-09-03 23:27:18.000000000 +0200
+++ tor-0.2.0.32/ChangeLog	2008-11-20 23:33:46.000000000 +0100
@@ -1,4 +1,73 @@
+Changes in version 0.2.0.32 - 2008-11-20
+  o Security fixes:
+    - The "User" and "Group" config options did not clear the
+      supplementary group entries for the Tor process. The "User" option
+      is now more robust, and we now set the groups to the specified
+      user's primary group. The "Group" option is now ignored. For more
+      detailed logging on credential switching, set CREDENTIAL_LOG_LEVEL
+      in common/compat.c to LOG_NOTICE or higher. Patch by Jacob Appelbaum
+      and Steven Murdoch. Bugfix on 0.0.2pre14. Fixes bug 848 and 857.
+    - The "ClientDNSRejectInternalAddresses" config option wasn't being
+      consistently obeyed: if an exit relay refuses a stream because its
+      exit policy doesn't allow it, we would remember what IP address
+      the relay said the destination address resolves to, even if it's
+      an internal IP address. Bugfix on 0.2.0.7-alpha; patch by rovv.
+
+  o Major bugfixes:
+    - Fix a DOS opportunity during the voting signature collection process
+      at directory authorities. Spotted by rovv. Bugfix on 0.2.0.x.
+
+  o Major bugfixes (hidden services):
+    - When fetching v0 and v2 rendezvous service descriptors in parallel,
+      we were failing the whole hidden service request when the v0
+      descriptor fetch fails, even if the v2 fetch is still pending and
+      might succeed. Similarly, if the last v2 fetch fails, we were
+      failing the whole hidden service request even if a v0 fetch is
+      still pending. Fixes bug 814. Bugfix on 0.2.0.10-alpha.
+    - When extending a circuit to a hidden service directory to upload a
+      rendezvous descriptor using a BEGIN_DIR cell, almost 1/6 of all
+      requests failed, because the router descriptor has not been
+      downloaded yet. In these cases, do not attempt to upload the
+      rendezvous descriptor, but wait until the router descriptor is
+      downloaded and retry. Likewise, do not attempt to fetch a rendezvous
+      descriptor from a hidden service directory for which the router
+      descriptor has not yet been downloaded. Fixes bug 767. Bugfix
+      on 0.2.0.10-alpha.
+
+  o Minor bugfixes:
+    - Fix several infrequent memory leaks spotted by Coverity.
+    - When testing for libevent functions, set the LDFLAGS variable
+      correctly. Found by Riastradh.
+    - Avoid a bug where the FastFirstHopPK 0 option would keep Tor from
+      bootstrapping with tunneled directory connections. Bugfix on
+      0.1.2.5-alpha. Fixes bug 797. Found by Erwin Lam.
+    - When asked to connect to A.B.exit:80, if we don't know the IP for A
+      and we know that server B rejects most-but-not all connections to
+      port 80, we would previously reject the connection. Now, we assume
+      the user knows what they were asking for. Fixes bug 752. Bugfix
+      on 0.0.9rc5. Diagnosed by BarkerJr.
+    - If we overrun our per-second write limits a little, count this as
+      having used up our write allocation for the second, and choke
+      outgoing directory writes. Previously, we had only counted this when
+      we had met our limits precisely. Fixes bug 824. Patch from by rovv.
+      Bugfix on 0.2.0.x (??).
+    - Remove the old v2 directory authority 'lefkada' from the default
+      list. It has been gone for many months.
+    - Stop doing unaligned memory access that generated bus errors on
+      sparc64. Bugfix on 0.2.0.10-alpha. Fixes bug 862.
+    - Make USR2 log-level switch take effect immediately. Bugfix on
+      0.1.2.8-beta.
+
+  o Minor bugfixes (controller):
+    - Make DNS resolved events into "CLOSED", not "FAILED". Bugfix on
+      0.1.2.5-alpha. Fix by Robert Hogan. Resolves bug 807.
+
+
 Changes in version 0.2.0.31 - 2008-09-03
+  Tor 0.2.0.31 addresses two potential anonymity issues, starts to fix
+  a big bug we're seeing where in rare cases traffic from one Tor stream
+  gets mixed into another stream, and fixes a variety of smaller issues.
+
   o Major bugfixes:
     - Make sure that two circuits can never exist on the same connection
       with the same circuit ID, even if one is marked for close. This
@@ -49,6 +118,9 @@
 
 
 Changes in version 0.2.0.29-rc - 2008-07-08
+  Tor 0.2.0.29-rc fixes two big bugs with using bridges, fixes more
+  hidden-service performance bugs, and fixes a bunch of smaller bugs.
+
   o Major bugfixes:
     - If you have more than one bridge but don't know their keys,
       you would only launch a request for the descriptor of the first one
diff -Nur tor-0.2.0.31/configure tor-0.2.0.32/configure
--- tor-0.2.0.31/configure	2008-09-03 23:23:05.000000000 +0200
+++ tor-0.2.0.32/configure	2008-11-20 23:34:53.000000000 +0100
@@ -2100,7 +2100,7 @@
 
 # Define the identity of the package.
  PACKAGE=tor
- VERSION=0.2.0.31
+ VERSION=0.2.0.32
 
 
 cat >>confdefs.h <<_ACEOF
@@ -6533,7 +6533,7 @@
 save_LDFLAGS="$LDFLAGS"
 save_CPPFLAGS="$CPPFLAGS"
 LIBS="-levent $TOR_LIB_WS32 $LIBS"
-LDFLAGS="$TOR_LDFLAGS_libevent $LIBS"
+LDFLAGS="$TOR_LDFLAGS_libevent $LDFLAGS"
 CPPFLAGS="$TOR_CPPFLAGS_libevent $CPPFLAGS"
 
 
@@ -18919,6 +18919,103 @@
 
 
 
+# Check if we have getresuid and getresgid
+
+
+for ac_func in getresuid getresgid
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+	       { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
 # Check for gethostbyname_r in all its glorious incompatible versions.
 #   (This logic is based on that in Python's configure.in)
 
diff -Nur tor-0.2.0.31/configure.in tor-0.2.0.32/configure.in
--- tor-0.2.0.31/configure.in	2008-09-02 02:04:50.000000000 +0200
+++ tor-0.2.0.32/configure.in	2008-11-20 23:34:31.000000000 +0100
@@ -1,11 +1,11 @@
-dnl $Id: configure.in 16733 2008-09-02 00:04:50Z arma $
+dnl $Id: configure.in 17344 2008-11-20 22:34:30Z arma $
 dnl Copyright (c) 2001-2004, Roger Dingledine
 dnl Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson
 dnl Copyright (c) 2007-2008, The Tor Project, Inc.
 dnl See LICENSE for licensing information
 
 AC_INIT
-AM_INIT_AUTOMAKE(tor, 0.2.0.31)
+AM_INIT_AUTOMAKE(tor, 0.2.0.32)
 AM_CONFIG_HEADER(orconfig.h)
 
 AC_CANONICAL_HOST
@@ -254,7 +254,7 @@
 save_LDFLAGS="$LDFLAGS"
 save_CPPFLAGS="$CPPFLAGS"
 LIBS="-levent $TOR_LIB_WS32 $LIBS"
-LDFLAGS="$TOR_LDFLAGS_libevent $LIBS"
+LDFLAGS="$TOR_LDFLAGS_libevent $LDFLAGS"
 CPPFLAGS="$TOR_CPPFLAGS_libevent $CPPFLAGS"
 AC_CHECK_FUNCS(event_get_version event_get_method event_set_log_callback)
 LIBS="$save_LIBS"
@@ -616,6 +616,9 @@
 AC_DEFINE_UNQUOTED(LOGFACILITY,$syslog_facility,[name of the syslog facility])
 AC_SUBST(LOGFACILITY)
 
+# Check if we have getresuid and getresgid
+AC_CHECK_FUNCS(getresuid getresgid)
+
 # Check for gethostbyname_r in all its glorious incompatible versions.
 #   (This logic is based on that in Python's configure.in)
 AH_TEMPLATE(HAVE_GETHOSTBYNAME_R,
diff -Nur tor-0.2.0.31/contrib/linux-tor-prio.sh tor-0.2.0.32/contrib/linux-tor-prio.sh
diff -Nur tor-0.2.0.31/contrib/osx/TorBundleInfo.plist.in tor-0.2.0.32/contrib/osx/TorBundleInfo.plist.in
diff -Nur tor-0.2.0.31/contrib/osx/TorPostflight tor-0.2.0.32/contrib/osx/TorPostflight
diff -Nur tor-0.2.0.31/contrib/rc.subr tor-0.2.0.32/contrib/rc.subr
diff -Nur tor-0.2.0.31/contrib/tor-mingw.nsi.in tor-0.2.0.32/contrib/tor-mingw.nsi.in
diff -Nur tor-0.2.0.31/contrib/tor.sh tor-0.2.0.32/contrib/tor.sh
diff -Nur tor-0.2.0.31/contrib/tor.sh.in tor-0.2.0.32/contrib/tor.sh.in
diff -Nur tor-0.2.0.31/contrib/torctl tor-0.2.0.32/contrib/torctl
diff -Nur tor-0.2.0.31/contrib/torctl.in tor-0.2.0.32/contrib/torctl.in
[All not relevant]
diff -Nur tor-0.2.0.31/doc/tor.1.in tor-0.2.0.32/doc/tor.1.in
--- tor-0.2.0.31/doc/tor.1.in	2008-05-13 00:56:44.000000000 +0200
+++ tor-0.2.0.32/doc/tor.1.in	2008-11-12 11:47:38.000000000 +0100
@@ -259,10 +259,6 @@
 (Default: 0)
 .LP
 .TP
-\fBGroup \fR\fIGID\fP
-On startup, setgid to this group.
-.LP
-.TP
 \fBHttpProxy\fR \fIhost\fR[:\fIport\fR]\fP
 Tor will make all its directory requests through this host:port
 (or host:80 if port is not specified),
@@ -345,7 +341,7 @@
 .LP
 .TP
 \fBUser \fR\fIUID\fP
-On startup, setuid to this user.
+On startup, setuid to this user and setgid to their primary group.
 .LP
 .TP
 \fBHardwareAccel \fR\fB0\fR|\fB1\fP
@@ -663,11 +659,14 @@
 .LP
 .TP
 \fBFastFirstHopPK \fR\fB0\fR|\fB1\fR\fP
-When this option is enabled and we aren't running as a server, Tor
-skips the public key step for the first hop of creating circuits.  This is
-safe since we have already used TLS to authenticate the server and to
-establish forward-secure keys.  Turning this option off makes circuit
-building slower.
+When this option is disabled, Tor uses the public key step for the first
+hop of creating circuits. Skipping it is generally safe since we have
+already used TLS to authenticate the relay and to establish forward-secure
+keys. Turning this option off makes circuit building slower.
+
+Note that Tor will always use the public key step for the first hop if
+it's operating as a relay, and it will never use the public key step if
+it doesn't yet know the onion key of the first hop.
 (Default: 1)
 .LP
 .TP
diff -Nur tor-0.2.0.31/orconfig.h.in tor-0.2.0.32/orconfig.h.in
--- tor-0.2.0.31/orconfig.h.in	2008-09-03 23:23:46.000000000 +0200
+++ tor-0.2.0.32/orconfig.h.in	2008-11-20 23:35:26.000000000 +0100
@@ -72,6 +72,12 @@
 /* Define this if gethostbyname_r takes 6 arguments */
 #undef HAVE_GETHOSTBYNAME_R_6_ARG
 
+/* Define to 1 if you have the `getresgid' function. */
+#undef HAVE_GETRESGID
+
+/* Define to 1 if you have the `getresuid' function. */
+#undef HAVE_GETRESUID
+
 /* Define to 1 if you have the `getrlimit' function. */
 #undef HAVE_GETRLIMIT
 
diff -Nur tor-0.2.0.31/src/common/compat.c tor-0.2.0.32/src/common/compat.c
--- tor-0.2.0.31/src/common/compat.c	2008-04-23 23:03:35.000000000 +0200
+++ tor-0.2.0.32/src/common/compat.c	2008-11-21 00:06:37.000000000 +0100
@@ -871,62 +871,220 @@
   return 0;
 }
 
-/** Call setuid and setgid to run as <b>user</b>:<b>group</b>.  Return 0 on
- * success.  On failure, log and return -1.
+/** Log details of current user and group credentials. Return 0 on
+ * success. Logs and return -1 on failure.
+ */
+static int
+log_credential_status(void)
+{
+#define CREDENTIAL_LOG_LEVEL LOG_INFO
+#ifndef MS_WINDOWS
+  /* Real, effective and saved UIDs */
+  uid_t ruid, euid, suid;
+  /* Read, effective and saved GIDs */
+  gid_t rgid, egid, sgid;
+  /* Supplementary groups */
+  gid_t sup_gids[NGROUPS_MAX + 1];
+  /* Number of supplementary groups */
+  int ngids;
+
+  /* log UIDs */
+#ifdef HAVE_GETRESUID
+  if (getresuid(&ruid, &euid, &suid) != 0 ) {
+    log_warn(LD_GENERAL, "Error getting changed UIDs: %s", strerror(errno));
+    return -1;
+  } else {
+    log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL,
+           "UID is %u (real), %u (effective), %u (saved)", ruid, euid, suid);
+  }
+#else
+  /* getresuid is not present on MacOS X, so we can't get the saved (E)UID */
+  ruid = getuid();
+  euid = geteuid();
+  (void)suid;
+
+  log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL,
+         "UID is %u (real), %u (effective), unknown (saved)", ruid, euid);
+#endif
+
+  /* log GIDs */
+#ifdef HAVE_GETRESGID
+  if (getresgid(&rgid, &egid, &sgid) != 0 ) {
+    log_warn(LD_GENERAL, "Error getting changed GIDs: %s", strerror(errno));
+    return -1;
+  } else {
+    log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL,
+           "GID is %u (real), %u (effective), %u (saved)", rgid, egid, sgid);
+  }
+#else
+  /* getresgid is not present on MacOS X, so we can't get the saved (E)GID */
+  rgid = getgid();
+  egid = getegid();
+  (void)sgid;
+  log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL,
+         "GID is %u (real), %u (effective), unknown (saved)", rgid, egid);
+#endif
+
+  /* log supplementary groups */
+  if ((ngids = getgroups(NGROUPS_MAX + 1, sup_gids)) < 0) {
+    log_warn(LD_GENERAL, "Error getting supplementary GIDs: %s",
+             strerror(errno));
+    return -1;
+  } else {
+    int i;
+    char *strgid;
+    char *s = NULL;
+    int formatting_error = 0;
+    smartlist_t *elts = smartlist_create();
+
+    for (i = 0; i<ngids; i++) {
+      strgid = tor_malloc(11);
+      if (tor_snprintf(strgid, 11, "%u", (unsigned)sup_gids[i]) == -1) {
+        log_warn(LD_GENERAL, "Error printing supplementary GIDs");
+        formatting_error = 1;
+        goto error;
+      }
+      smartlist_add(elts, strgid);
+    }
+
+    s = smartlist_join_strings(elts, " ", 0, NULL);
+
+    log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "Supplementary groups are: %s",s);
+
+   error:
+    tor_free(s);
+    SMARTLIST_FOREACH(elts, char *, cp,
+    {
+      tor_free(cp);
+    });
+    smartlist_free(elts);
+
+    if (formatting_error)
+      return -1;
+  }
+#endif
+
+  return 0;
+}
+
+/** 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.
  */
 int
-switch_id(const char *user, const char *group)
+switch_id(const char *user)
 {
 #ifndef MS_WINDOWS
   struct passwd *pw = NULL;
-  struct group *gr = NULL;
+  uid_t old_uid;
+  gid_t old_gid;
+  static int have_already_switched_id = 0;
 
-  if (user) {
-    pw = getpwnam(user);
-    if (pw == NULL) {
-      log_warn(LD_CONFIG,"User '%s' not found.", user);
-      return -1;
-    }
+  tor_assert(user);
+
+  if (have_already_switched_id)
+    return 0;
+
+  /* Log the initial credential state */
+  if (log_credential_status())
+    return -1;
+
+  log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "Changing user and groups");
+
+  /* Get old UID/GID to check if we changed correctly */
+  old_uid = getuid();
+  old_gid = getgid();
+
+  /* Lookup the user and group information, if we have a problem, bail out. */
+  pw = getpwnam(user);
+  if (pw == NULL) {
+    log_warn(LD_CONFIG, "Error setting configured user: %s not found", user);
+    return -1;
   }
 
-  /* switch the group first, while we still have the privileges to do so */
-  if (group) {
-    gr = getgrnam(group);
-    if (gr == NULL) {
-      log_warn(LD_CONFIG,"Group '%s' not found.", group);
-      return -1;
-    }
+  /* Properly switch egid,gid,euid,uid here or bail out */
+  if (setgroups(1, &pw->pw_gid)) {
+    log_warn(LD_GENERAL, "Error setting groups to gid %d: \"%s\". "
+             "If you set the \"User\" option, you must start Tor as root.",
+             (int)pw->pw_gid, strerror(errno));
+    return -1;
+  }
 
-    if (setgid(gr->gr_gid) != 0) {
-      log_warn(LD_GENERAL,"Error setting to configured GID: %s",
-               strerror(errno));
+  if (setegid(pw->pw_gid)) {
+    log_warn(LD_GENERAL, "Error setting egid to %d: %s",
+             (int)pw->pw_gid, strerror(errno));
+    return -1;
+  }
+
+  if (setgid(pw->pw_gid)) {
+    log_warn(LD_GENERAL, "Error setting gid to %d: %s",
+             (int)pw->pw_gid, strerror(errno));
+    return -1;
+  }
+
+  if (setuid(pw->pw_uid)) {
+    log_warn(LD_GENERAL, "Error setting configured uid to %s (%d): %s",
+             user, (int)pw->pw_uid, strerror(errno));
+    return -1;
+  }
+
+  if (seteuid(pw->pw_uid)) {
+    log_warn(LD_GENERAL, "Error setting configured euid to %s (%d): %s",
+             user, (int)pw->pw_uid, strerror(errno));
+    return -1;
+  }
+
+  /* This is how OpenBSD rolls:
+  if (setgroups(1, &pw->pw_gid) || setegid(pw->pw_gid) ||
+      setgid(pw->pw_gid) || setuid(pw->pw_uid) || seteuid(pw->pw_uid)) {
+      setgid(pw->pw_gid) || seteuid(pw->pw_uid) || setuid(pw->pw_uid)) {
+    log_warn(LD_GENERAL, "Error setting configured UID/GID: %s",
+    strerror(errno));
+    return -1;
+  }
+  */
+
+  /* We've properly switched egid, gid, euid, uid, and supplementary groups if
+   * we're here. */
+
+#if !defined(CYGWIN) && !defined(__CYGWIN__)
+  /* If we tried to drop privilege to a group/user other than root, attempt to
+   * restore root (E)(U|G)ID, and abort if the operation succeeds */
+
+  /* Only check for privilege dropping if we were asked to be non-root */
+  if (pw->pw_uid) {
+    /* Try changing GID/EGID */
+    if (pw->pw_gid != old_gid &&
+        (setgid(old_gid) != -1 || setegid(old_gid) != -1)) {
+      log_warn(LD_GENERAL, "Was able to restore group credentials even after "
+               "switching GID: this means that the setgid code didn't work.");
       return -1;
     }
-  } else if (user) {
-    if (setgid(pw->pw_gid) != 0) {
-      log_warn(LD_GENERAL,"Error setting to user GID: %s", strerror(errno));
+
+    /* Try changing UID/EUID */
+    if (pw->pw_uid != old_uid &&
+        (setuid(old_uid) != -1 || seteuid(old_uid) != -1)) {
+      log_warn(LD_GENERAL, "Was able to restore user credentials even after "
+               "switching UID: this means that the setuid code didn't work.");
       return -1;
     }
   }
+#endif
 
-  /* now that the group is switched, we can switch users and lose
-     privileges */
-  if (user) {
-    if (setuid(pw->pw_uid) != 0) {
-      log_warn(LD_GENERAL,"Error setting UID: %s", strerror(errno));
-      return -1;
-    }
+  /* Check what really happened */
+  if (log_credential_status()) {
+    return -1;
   }
 
+  have_already_switched_id = 1; /* mark success so we never try again */
   return 0;
+
 #else
   (void)user;
-  (void)group;
-#endif
 
   log_warn(LD_CONFIG,
-           "User or group specified, but switching users is not supported.");
+           "User specified but switching users is unsupported on your OS.");
   return -1;
+#endif
 }
 
 #ifdef HAVE_PWD_H
diff -Nur tor-0.2.0.31/src/common/compat.h tor-0.2.0.32/src/common/compat.h
--- tor-0.2.0.31/src/common/compat.h	2008-07-16 01:45:58.000000000 +0200
+++ tor-0.2.0.32/src/common/compat.h	2008-11-12 11:47:37.000000000 +0100
@@ -463,7 +463,7 @@
 typedef unsigned long rlim_t;
 #endif
 int set_max_file_descriptors(rlim_t limit, int *max);
-int switch_id(const char *user, const char *group);
+int switch_id(const char *user);
 #ifdef HAVE_PWD_H
 char *get_user_homedir(const char *username);
 #endif
diff -Nur tor-0.2.0.31/src/common/log.c tor-0.2.0.32/src/common/log.c
--- tor-0.2.0.31/src/common/log.c	2008-03-16 23:08:00.000000000 +0100
+++ tor-0.2.0.32/src/common/log.c	2008-11-20 23:14:26.000000000 +0100
@@ -701,6 +701,7 @@
   for (lf = logfiles; lf; lf=lf->next) {
     lf->min_loglevel = LOG_DEBUG;
   }
+  _log_global_min_severity = get_min_log_level();
   UNLOCK_LOGS();
 }
 
diff -Nur tor-0.2.0.31/src/or/buffers.c tor-0.2.0.32/src/or/buffers.c
--- tor-0.2.0.31/src/or/buffers.c	2008-08-20 07:22:00.000000000 +0200
+++ tor-0.2.0.32/src/or/buffers.c	2008-11-20 23:14:26.000000000 +0100
@@ -966,7 +966,7 @@
     return 1;
   result = var_cell_new(length);
   result->command = command;
-  result->circ_id = ntohs(*(uint16_t*)hdr);
+  result->circ_id = ntohs(get_uint16(hdr));
 
   buf_remove_from_front(buf, VAR_CELL_HEADER_SIZE);
   peek_from_buf(result->payload, length, buf);
diff -Nur tor-0.2.0.31/src/or/circuitbuild.c tor-0.2.0.32/src/or/circuitbuild.c
--- tor-0.2.0.31/src/or/circuitbuild.c	2008-09-02 00:25:03.000000000 +0200
+++ tor-0.2.0.32/src/or/circuitbuild.c	2008-09-23 23:00:43.000000000 +0200
@@ -541,23 +541,20 @@
   return 1;
 }
 
-/** Return true iff we should send a create_fast cell to build a circuit
- * starting at <b>router</b>. (If <b>router</b> is NULL, we don't have
- * information on the router, so assume true.) */
+/** Return true iff we should send a create_fast cell to start building a given
+ * circuit */
 static INLINE int
-should_use_create_fast_for_router(routerinfo_t *router,
-                                  origin_circuit_t *circ)
+should_use_create_fast_for_circuit(origin_circuit_t *circ)
 {
   or_options_t *options = get_options();
+  tor_assert(circ->cpath);
+  tor_assert(circ->cpath->extend_info);
 
-  if (!options->FastFirstHopPK) /* create_fast is disabled */
-    return 0;
-  if (router && router->platform &&
-      !tor_version_as_new_as(router->platform, "0.1.0.6-rc")) {
-    /* known not to work */
-    return 0;
-  }
-  if (server_mode(options) && circ->cpath->extend_info->onion_key) {
+  if (!circ->cpath->extend_info->onion_key)
+    return 1; /* our hand is forced: only a create_fast will work. */
+  if (!options->FastFirstHopPK)
+    return 0; /* we prefer to avoid create_fast */
+  if (server_mode(options)) {
     /* We're a server, and we know an onion key. We can choose.
      * Prefer to blend in. */
     return 0;
@@ -593,14 +590,9 @@
     log_debug(LD_CIRC,"First skin; sending create cell.");
 
     router = router_get_by_digest(circ->_base.n_conn->identity_digest);
-    fast = should_use_create_fast_for_router(router, circ);
-    if (!fast && !circ->cpath->extend_info->onion_key) {
-      log_warn(LD_CIRC,
-               "Can't send create_fast, but have no onion key. Failing.");
-      return - END_CIRC_REASON_INTERNAL;
-    }
+    fast = should_use_create_fast_for_circuit(circ);
     if (!fast) {
-      /* We are an OR, or we are connecting to an old Tor: we should
+      /* We are an OR and we know the right onion key: we should
        * send an old slow create cell.
        */
       cell_type = CELL_CREATE;
diff -Nur tor-0.2.0.31/src/or/config.c tor-0.2.0.32/src/or/config.c
--- tor-0.2.0.31/src/or/config.c	2008-09-02 00:06:47.000000000 +0200
+++ tor-0.2.0.32/src/or/config.c	2008-11-12 11:47:36.000000000 +0100
@@ -204,7 +204,7 @@
   V(GeoIPFile,                   STRING,
     SHARE_DATADIR PATH_SEPARATOR "tor" PATH_SEPARATOR "geoip"),
 #endif
-  V(Group,                       STRING,   NULL),
+  OBSOLETE("Group"),
   V(HardwareAccel,               BOOL,     "0"),
   V(HashedControlPassword,       LINELIST, NULL),
   V(HidServDirectoryV2,          BOOL,     "0"),
@@ -391,7 +391,6 @@
   /* { "FastFirstHopPK", "" }, */
   /* FetchServerDescriptors, FetchHidServDescriptors,
    * FetchUselessDescriptors */
-  { "Group", "On startup, setgid to this group." },
   { "HardwareAccel", "If set, Tor tries to use hardware crypto accelerators "
     "when it can." },
   /* HashedControlPassword */
@@ -837,8 +836,6 @@
       "719B E45D E224 B607 C537 07D0 E214 3E2D 423E 74CF",
     "tor26 v1 orport=443 v3ident=14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 "
       "86.59.21.38:80 847B 1F85 0344 D787 6491 A548 92F9 0493 4E4E B85D",
-    "lefkada orport=443 "
-      "140.247.60.64:80 38D4 F5FC F7B1 0232 28B8 95EA 56ED E7D5 CCDC AF32",
     "dizum orport=443 v3ident=E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 "
       "194.109.206.212:80 7EA6 EAD6 FD83 083C 538F 4403 8BBF A077 587D D755",
     "Tonga orport=443 bridge no-v2 82.94.251.206:80 "
@@ -1033,13 +1030,10 @@
 #endif
 
   /* Setuid/setgid as appropriate */
-  if (options->User || options->Group) {
-    /* XXXX021 We should only do this the first time through, not on
-     * every setconf. */
-    if (switch_id(options->User, options->Group) != 0) {
+  if (options->User) {
+    if (switch_id(options->User) != 0) {
       /* No need to roll back, since you can't change the value. */
-      *msg = tor_strdup("Problem with User or Group value. "
-                        "See logs for details.");
+      *msg = tor_strdup("Problem with User value. See logs for details.");
       goto done;
     }
   }
@@ -1870,9 +1864,9 @@
         result->value = tor_strdup("");
       break;
     case CONFIG_TYPE_OBSOLETE:
-      log_warn(LD_CONFIG,
-               "You asked me for the value of an obsolete config option '%s'.",
-               key);
+      log_fn(LOG_PROTOCOL_WARN, LD_CONFIG,
+             "You asked me for the value of an obsolete config option '%s'.",
+             key);
       tor_free(result->key);
       tor_free(result);
       return NULL;
diff -Nur tor-0.2.0.31/src/or/connection.c tor-0.2.0.32/src/or/connection.c
--- tor-0.2.0.31/src/or/connection.c	2008-06-11 04:04:59.000000000 +0200
+++ tor-0.2.0.32/src/or/connection.c	2008-11-12 11:47:37.000000000 +0100
@@ -479,8 +479,13 @@
          * failed: forget about this router, and maybe try again. */
         connection_dir_request_failed(dir_conn);
       }
-      if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC)
-        rend_client_desc_here(dir_conn->rend_query); /* give it a try */
+      if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC) {
+        /* Give it a try. However, there is no re-fetching for v0 rend
+         * descriptors; if the response is empty or the descriptor is
+         * unusable, close pending connections (unless a v2 request is
+         * still in progress). */
+        rend_client_desc_trynow(dir_conn->rend_query, 0);
+      }
       /* If we were trying to fetch a v2 rend desc and did not succeed,
        * retry as needed. (If a fetch is successful, the connection state
        * is changed to DIR_PURPOSE_HAS_FETCHED_RENDDESC to mark that
@@ -1718,7 +1723,7 @@
   tor_assert(seconds_elapsed >= 0);
 
   write_buckets_empty_last_second =
-    global_relayed_write_bucket == 0 || global_write_bucket == 0;
+    global_relayed_write_bucket <= 0 || global_write_bucket <= 0;
 
   /* refill the global buckets */
   connection_bucket_refill_helper(&global_read_bucket,
diff -Nur tor-0.2.0.31/src/or/connection_edge.c tor-0.2.0.32/src/or/connection_edge.c
--- tor-0.2.0.31/src/or/connection_edge.c	2008-09-02 00:53:16.000000000 +0200
+++ tor-0.2.0.32/src/or/connection_edge.c	2008-09-29 08:49:32.000000000 +0200
@@ -696,6 +696,8 @@
       MAP_DEL_CURRENT(address);
     }
   } STRMAP_FOREACH_END;
+
+  tor_free(suffix);
 }
 
 /** Remove all entries from the addressmap that were set via the
@@ -1366,8 +1368,7 @@
                                              map_expires);
       connection_mark_unattached_ap(conn,
                                 END_STREAM_REASON_DONE |
-                                END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED |
-                                END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED);
+                                END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
       return 0;
     }
     if (options->ClientDNSRejectInternalAddresses) {
@@ -2805,8 +2806,12 @@
       addr = ntohl(in.s_addr);
     r = compare_addr_to_addr_policy(addr, conn->socks_request->port,
                                     exit->exit_policy);
-    if (r == ADDR_POLICY_REJECTED || r == ADDR_POLICY_PROBABLY_REJECTED)
-      return 0;
+    if (r == ADDR_POLICY_REJECTED)
+      return 0; /* We know the address, and the exit policy rejects it. */
+    if (r == ADDR_POLICY_PROBABLY_REJECTED && !conn->chosen_exit_name)
+      return 0; /* We don't know the addr, but the exit policy rejects most
+                 * addresses with this port. Since the user didn't ask for
+                 * this node, err on the side of caution. */
   } else if (SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)) {
     /* Can't support reverse lookups without eventdns. */
     if (conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR &&
diff -Nur tor-0.2.0.31/src/or/connection_or.c tor-0.2.0.32/src/or/connection_or.c
--- tor-0.2.0.31/src/or/connection_or.c	2008-06-13 07:12:28.000000000 +0200
+++ tor-0.2.0.32/src/or/connection_or.c	2008-11-20 23:14:25.000000000 +0100
@@ -157,7 +157,7 @@
 void
 var_cell_pack_header(const var_cell_t *cell, char *hdr_out)
 {
-  *(uint16_t*)(hdr_out) = htons(cell->circ_id);
+  set_uint16(hdr_out, htons(cell->circ_id));
   *(uint8_t*)(hdr_out+2) = cell->command;
   set_uint16(hdr_out+3, htons(cell->payload_len));
 }
diff -Nur tor-0.2.0.31/src/or/directory.c tor-0.2.0.32/src/or/directory.c
--- tor-0.2.0.31/src/or/directory.c	2008-05-28 22:36:39.000000000 +0200
+++ tor-0.2.0.32/src/or/directory.c	2008-11-20 23:14:26.000000000 +0100
@@ -454,7 +454,12 @@
   char address_buf[INET_NTOA_BUF_LEN+1];
   struct in_addr in;
   const char *address;
-  if ((router = router_get_by_digest(status->identity_digest))) {
+  router = router_get_by_digest(status->identity_digest);
+  if (!router && anonymized_connection) {
+    log_info(LD_DIR, "Not sending anonymized request to directory '%s'; we "
+                     "don't have its router descriptor.", status->nickname);
+    return;
+  } else if (router) {
     address = router->address;
   } else {
     in.s_addr = htonl(status->addr);
@@ -1805,7 +1810,7 @@
         } else {
           /* success. notify pending connections about this. */
           conn->_base.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC;
-          rend_client_desc_here(conn->rend_query);
+          rend_client_desc_trynow(conn->rend_query, -1);
         }
         break;
       case 404:
@@ -1851,7 +1856,7 @@
             log_info(LD_REND, "Successfully fetched v2 rendezvous "
                      "descriptor.");
             conn->_base.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC;
-            rend_client_desc_here(conn->rend_query);
+            rend_client_desc_trynow(conn->rend_query, -1);
             break;
         }
         break;
@@ -2806,7 +2811,7 @@
      * receive anything. */
     write_http_status_line(conn, 400, "Nonauthoritative directory does not "
                            "accept posted server descriptors");
-    return 0;
+    goto done;
   }
 
   if (authdir_mode_handles_descs(options, -1) &&
diff -Nur tor-0.2.0.31/src/or/dirvote.c tor-0.2.0.32/src/or/dirvote.c
--- tor-0.2.0.31/src/or/dirvote.c	2008-05-13 00:56:45.000000000 +0200
+++ tor-0.2.0.32/src/or/dirvote.c	2008-11-12 11:47:37.000000000 +0100
@@ -1089,8 +1089,8 @@
         memcpy(target_voter->signing_key_digest, src_voter->signing_key_digest,
                DIGEST_LEN);
         target_voter->signature_len = src_voter->signature_len;
-        target_voter->good_signature = 1;
-        target_voter->bad_signature = 0;
+        target_voter->good_signature = src_voter->good_signature;
+        target_voter->bad_signature = src_voter->bad_signature;
       } else {
         log_info(LD_DIR, "Not adding signature from %s", voter_identity);
       }
@@ -1872,12 +1872,17 @@
                                             sigs, msg_out);
   log_info(LD_DIR,"Added %d signatures to consensus.", r);
 
-  if (r >= 0) {
+  if (r >= 1) {
     char *new_detached =
       networkstatus_get_detached_signatures(pending_consensus);
     const char *src;
     char *dst, *dst_end;
-    size_t new_consensus_len =
+    size_t new_consensus_len;
+    if (!new_detached) {
+      *msg_out = "No signatures to add";
+      goto err;
+    }
+    new_consensus_len =
       strlen(pending_consensus_body) + strlen(new_detached) + 1;
     pending_consensus_body = tor_realloc(pending_consensus_body,
                                          new_consensus_len);
diff -Nur tor-0.2.0.31/src/or/main.c tor-0.2.0.32/src/or/main.c
--- tor-0.2.0.31/src/or/main.c	2008-06-13 06:18:28.000000000 +0200
+++ tor-0.2.0.32/src/or/main.c	2008-11-20 23:14:26.000000000 +0100
@@ -1113,8 +1113,10 @@
   circuit_close_all_marked();
 
   /** 7. And upload service descriptors if necessary. */
-  if (has_completed_circuit && !we_are_hibernating())
+  if (has_completed_circuit && !we_are_hibernating()) {
     rend_consider_services_upload(now);
+    rend_consider_descriptor_republication();
+  }
 
   /** 8. and blow away any connections that need to die. have to do this now,
    * because if we marked a conn for close and left its socket -1, then
diff -Nur tor-0.2.0.31/src/or/micro-revision.i tor-0.2.0.32/src/or/micro-revision.i
diff -Nur tor-0.2.0.31/src/or/or.h tor-0.2.0.32/src/or/or.h
--- tor-0.2.0.31/src/or/or.h	2008-09-02 00:06:47.000000000 +0200
+++ tor-0.2.0.32/src/or/or.h	2008-11-20 23:14:26.000000000 +0100
@@ -3645,7 +3645,7 @@
                                  size_t request_len);
 int rend_client_receive_rendezvous(origin_circuit_t *circ, const char *request,
                                    size_t request_len);
-void rend_client_desc_here(const char *query);
+void rend_client_desc_trynow(const char *query, int rend_version);
 
 extend_info_t *rend_client_get_random_intro(const char *query);
 
@@ -3677,6 +3677,13 @@
   /** List of the service's introduction points.  Elements are removed if
    * introduction attempts fail. */
   smartlist_t *intro_nodes;
+  /** Has descriptor been uploaded to all hidden service directories? */
+  int all_uploads_performed;
+  /** List of hidden service directories to which an upload request for
+   * this descriptor could be sent. Smartlist exists only when at least one
+   * of the previous upload requests failed (otherwise it's not important
+   * to know which uploads succeeded and which not). */
+  smartlist_t *successful_uploads;
 } rend_service_descriptor_t;
 
 int rend_cmp_service_ids(const char *one, const char *two);
@@ -3738,6 +3745,8 @@
 void rend_services_init(void);
 void rend_services_introduce(void);
 void rend_consider_services_upload(time_t now);
+void rend_hsdir_routers_changed(void);
+void rend_consider_descriptor_republication(void);
 
 void rend_service_intro_has_opened(origin_circuit_t *circuit);
 int rend_service_intro_established(origin_circuit_t *circuit,
diff -Nur tor-0.2.0.31/src/or/relay.c tor-0.2.0.32/src/or/relay.c
--- tor-0.2.0.31/src/or/relay.c	2008-06-13 07:12:28.000000000 +0200
+++ tor-0.2.0.32/src/or/relay.c	2008-11-20 23:21:32.000000000 +0100
@@ -751,8 +751,11 @@
             ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+5));
           else
             ttl = -1;
-          client_dns_set_addressmap(conn->socks_request->address, addr,
-                                    conn->chosen_exit_name, ttl);
+
+          if (!(get_options()->ClientDNSRejectInternalAddresses &&
+                                           is_internal_IP(addr, 0)))
+            client_dns_set_addressmap(conn->socks_request->address, addr,
+                                      conn->chosen_exit_name, ttl);
         }
         /* check if he *ought* to have allowed it */
         if (exitrouter &&
diff -Nur tor-0.2.0.31/src/or/rendclient.c tor-0.2.0.32/src/or/rendclient.c
--- tor-0.2.0.31/src/or/rendclient.c	2008-09-02 00:53:16.000000000 +0200
+++ tor-0.2.0.32/src/or/rendclient.c	2008-11-20 23:14:26.000000000 +0100
@@ -354,12 +354,13 @@
                 desc_id, DIGEST_LEN);
 
   /* Only select those hidden service directories to which we did not send
-   * a request recently. */
+   * a request recently and for which we have a router descriptor here. */
   directory_clean_last_hid_serv_requests(); /* Clean request history first. */
 
   SMARTLIST_FOREACH(responsible_dirs, routerstatus_t *, dir, {
     if (lookup_last_hid_serv_request(dir, desc_id_base32, 0, 0) +
-        REND_HID_SERV_DIR_REQUERY_PERIOD >= now)
+            REND_HID_SERV_DIR_REQUERY_PERIOD >= now ||
+        !router_get_by_digest(dir->identity_digest))
       SMARTLIST_DEL_CURRENT(responsible_dirs, dir);
   });
 
@@ -461,6 +462,8 @@
   log_info(LD_REND, "Could not pick one of the responsible hidden "
                     "service directories to fetch descriptors, because "
                     "we already tried them all unsuccessfully.");
+  /* Close pending connections (unless a v0 request is still going on). */
+  rend_client_desc_trynow(query, 2);
   return;
 }
 
@@ -617,11 +620,14 @@
 
 /** Find all the apconns in state AP_CONN_STATE_RENDDESC_WAIT that
  * are waiting on query. If there's a working cache entry here
- * with at least one intro point, move them to the next state;
- * else fail them.
+ * with at least one intro point, move them to the next state. If
+ * <b>rend_version</b> is non-negative, fail connections that have
+ * requested <b>query</b> unless there are still descriptor fetch
+ * requests in progress for other descriptor versions than
+ * <b>rend_version</b>.
  */
 void
-rend_client_desc_here(const char *query)
+rend_client_desc_trynow(const char *query, int rend_version)
 {
   edge_connection_t *conn;
   rend_cache_entry_t *entry;
@@ -657,9 +663,15 @@
         connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
       }
     } else { /* 404, or fetch didn't get that far */
-      log_notice(LD_REND,"Closing stream for '%s.onion': hidden service is "
-                 "unavailable (try again later).", safe_str(query));
-      connection_mark_unattached_ap(conn, END_STREAM_REASON_RESOLVEFAILED);
+      /* Unless there are requests for another descriptor version pending,
+       * close the connection. */
+      if (rend_version >= 0 &&
+          !connection_get_by_type_state_rendquery(CONN_TYPE_DIR, 0, query,
+                                                  rend_version == 0 ? 2 : 0)) {
+        log_notice(LD_REND,"Closing stream for '%s.onion': hidden service is "
+                   "unavailable (try again later).", safe_str(query));
+        connection_mark_unattached_ap(conn, END_STREAM_REASON_RESOLVEFAILED);
+      }
     }
   });
 }
diff -Nur tor-0.2.0.31/src/or/rendcommon.c tor-0.2.0.32/src/or/rendcommon.c
--- tor-0.2.0.31/src/or/rendcommon.c	2008-02-26 20:56:28.000000000 +0100
+++ tor-0.2.0.32/src/or/rendcommon.c	2008-11-20 23:14:26.000000000 +0100
@@ -32,6 +32,10 @@
       rend_intro_point_free(intro););
     smartlist_free(desc->intro_nodes);
   }
+  if (desc->successful_uploads) {
+    SMARTLIST_FOREACH(desc->successful_uploads, char *, c, tor_free(c););
+    smartlist_free(desc->successful_uploads);
+  }
   tor_free(desc);
 }
 
@@ -884,6 +888,7 @@
   if (!published && strmap_get_lc(rend_cache, key)) {
     log_info(LD_REND, "We already have a v2 descriptor for service %s.",
              safe_str(query));
+    rend_service_descriptor_free(parsed);
     return -1;
   }
   /* report novel publication to statistics */
diff -Nur tor-0.2.0.31/src/or/rendservice.c tor-0.2.0.32/src/or/rendservice.c
--- tor-0.2.0.31/src/or/rendservice.c	2008-06-28 06:19:17.000000000 +0200
+++ tor-0.2.0.32/src/or/rendservice.c	2008-11-20 23:14:25.000000000 +0100
@@ -1066,11 +1066,13 @@
  * <b>service_id</b> and <b>seconds_valid</b> are only passed for logging
  * purposes. */
 static void
-directory_post_to_hs_dir(smartlist_t *descs, const char *service_id,
+directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
+                         smartlist_t *descs, const char *service_id,
                          int seconds_valid)
 {
-  int i, j;
+  int i, j, failed_upload = 0;
   smartlist_t *responsible_dirs = smartlist_create();
+  smartlist_t *successful_uploads = smartlist_create();
   routerstatus_t *hs_dir;
   for (i = 0; i < smartlist_len(descs); i++) {
     rend_encoded_v2_service_descriptor_t *desc = smartlist_get(descs, i);
@@ -1080,11 +1082,24 @@
       log_warn(LD_REND, "Could not determine the responsible hidden service "
                         "directories to post descriptors to.");
       smartlist_free(responsible_dirs);
+      smartlist_free(successful_uploads);
       return;
     }
     for (j = 0; j < smartlist_len(responsible_dirs); j++) {
       char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
       hs_dir = smartlist_get(responsible_dirs, j);
+      if (smartlist_digest_isin(renddesc->successful_uploads,
+                                hs_dir->identity_digest))
+        /* Don't upload descriptor if we succeeded in doing so last time. */
+        continue;
+      if (!router_get_by_digest(hs_dir->identity_digest)) {
+        log_info(LD_REND, "Not sending publish request for v2 descriptor to "
+                          "hidden service directory '%s'; we don't have its "
+                          "router descriptor. Queueing for later upload.",
+                 hs_dir->nickname);
+        failed_upload = -1;
+        continue;
+      }
       /* Send publish request. */
       directory_initiate_command_routerstatus(hs_dir,
                                               DIR_PURPOSE_UPLOAD_RENDDESC_V2,
@@ -1102,10 +1117,33 @@
                seconds_valid,
                hs_dir->nickname,
                hs_dir->dir_port);
+      /* Remember successful upload to this router for next time. */
+      if (!smartlist_digest_isin(successful_uploads, hs_dir->identity_digest))
+        smartlist_add(successful_uploads, hs_dir->identity_digest);
     }
     smartlist_clear(responsible_dirs);
   }
+  if (!failed_upload) {
+    if (renddesc->successful_uploads) {
+      SMARTLIST_FOREACH(renddesc->successful_uploads, char *, c, tor_free(c););
+      smartlist_free(renddesc->successful_uploads);
+    }
+    renddesc->all_uploads_performed = 1;
+  } else {
+    /* Remember which routers worked this time, so that we don't upload the
+     * descriptor to them again. */
+    if (!renddesc->successful_uploads)
+      renddesc->successful_uploads = smartlist_create();
+    SMARTLIST_FOREACH(successful_uploads, char *, c, {
+      if (!smartlist_digest_isin(renddesc->successful_uploads, c)) {
+        char *hsdir_id = tor_malloc_zero(DIGEST_LEN);
+        memcpy(hsdir_id, c, DIGEST_LEN);
+        smartlist_add(renddesc->successful_uploads, hsdir_id);
+      }
+    });
+  }
   smartlist_free(responsible_dirs);
+  smartlist_free(successful_uploads);
 }
 
 /** Encode and sign up-to-date v0 and/or v2 service descriptors for
@@ -1120,9 +1158,6 @@
   char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
   int uploaded = 0;
 
-  /* Update the descriptor. */
-  rend_service_update_descriptor(service);
-
   rendpostperiod = get_options()->RendPostPeriod;
 
   /* Upload unversioned (v0) descriptor? */
@@ -1172,7 +1207,8 @@
       rend_get_service_id(service->desc->pk, serviceid);
       log_info(LD_REND, "Sending publish request for hidden service %s",
                    serviceid);
-      directory_post_to_hs_dir(descs, serviceid, seconds_valid);
+      directory_post_to_hs_dir(service->desc, descs, serviceid,
+                               seconds_valid);
       /* Free memory for descriptors. */
       for (i = 0; i < smartlist_len(descs); i++)
         rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
@@ -1196,7 +1232,8 @@
           smartlist_free(descs);
           return;
         }
-        directory_post_to_hs_dir(descs, serviceid, seconds_valid);
+        directory_post_to_hs_dir(service->desc, descs, serviceid,
+                                 seconds_valid);
         /* Free memory for descriptors. */
         for (i = 0; i < smartlist_len(descs); i++)
           rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
@@ -1360,6 +1397,48 @@
       /* if it's time, or if the directory servers have a wrong service
        * descriptor and ours has been stable for 30 seconds, upload a
        * new one of each format. */
+      rend_service_update_descriptor(service);
+      upload_service_descriptor(service);
+    }
+  }
+}
+
+/** True if the list of available router descriptors might have changed so
+ * that we should have a look whether we can republish previously failed
+ * rendezvous service descriptors. */
+static int consider_republishing_rend_descriptors = 1;
+
+/** Called when our internal view of the directory has changed, so that we
+ * might have router descriptors of hidden service directories available that
+ * we did not have before. */
+void
+rend_hsdir_routers_changed(void)
+{
+  consider_republishing_rend_descriptors = 1;
+}
+
+/** Consider republication of v2 rendezvous service descriptors that failed
+ * previously, but without regenerating descriptor contents.
+ */
+void
+rend_consider_descriptor_republication(void)
+{
+  int i;
+  rend_service_t *service;
+
+  if (!consider_republishing_rend_descriptors)
+    return;
+  consider_republishing_rend_descriptors = 0;
+
+  if (!get_options()->PublishHidServDescriptors)
+    return;
+
+  for (i=0; i < smartlist_len(rend_service_list); ++i) {
+    service = smartlist_get(rend_service_list, i);
+    if (service->descriptor_version && service->desc &&
+        !service->desc->all_uploads_performed) {
+      /* If we failed in uploading a descriptor last time, try again *without*
+       * updating the descriptor's contents. */
       upload_service_descriptor(service);
     }
   }
diff -Nur tor-0.2.0.31/src/or/routerlist.c tor-0.2.0.32/src/or/routerlist.c
--- tor-0.2.0.31/src/or/routerlist.c	2008-06-19 06:59:43.000000000 +0200
+++ tor-0.2.0.32/src/or/routerlist.c	2008-11-20 23:14:25.000000000 +0100
@@ -4116,6 +4116,7 @@
 router_dir_info_changed(void)
 {
   need_to_update_have_min_dir_info = 1;
+  rend_hsdir_routers_changed();
 }
 
 /** Return a string describing what we're missing before we have enough
diff -Nur tor-0.2.0.31/src/win32/orconfig.h tor-0.2.0.32/src/win32/orconfig.h
diff -Nur tor-0.2.0.31/tor.spec tor-0.2.0.32/tor.spec
[not relevant]
diff -u tor-0.2.0.31/debian/changelog tor-0.2.0.32/debian/changelog
--- tor-0.2.0.31/debian/changelog
+++ tor-0.2.0.32/debian/changelog
@@ -1,3 +1,12 @@
+tor (0.2.0.32-1) unstable; urgency=high
+
+  * New upstream version.
+    - Properly drops privileges when being configured to do
+      so (closes: #505178).
+  * No longer set now obsolete Group setting in built-in debian config.
+
+ -- Peter Palfrader <weasel@debian.org>  Fri, 21 Nov 2008 23:33:15 +0100
+
 tor (0.2.0.31-1) unstable; urgency=low
 
   * New upstream version.
diff -u tor-0.2.0.31/debian/patches/06_add_compile_time_defaults.dpatch tor-0.2.0.32/debian/patches/06_add_compile_time_defaults.dpatch
--- tor-0.2.0.31/debian/patches/06_add_compile_time_defaults.dpatch
+++ tor-0.2.0.32/debian/patches/06_add_compile_time_defaults.dpatch
@@ -72,7 +72,7 @@
    if (errmsg) {
      log(LOG_WARN,LD_CONFIG,"Failed to parse/validate config: %s", errmsg);
      tor_free(errmsg);
-@@ -5011,3 +5018,64 @@
+@@ -5011,3 +5018,60 @@
    puts(routerparse_c_id);
  }
  
@@ -132,8 +132,4 @@
 +  var->initvalue = tor_strdup("debian-tor");
 +
-+  var = config_find_option(&options_format, "Group");
-+  tor_assert(var);
-+  var->initvalue = tor_strdup("debian-tor");
-+
 +  return 0;
 +}

Reply to: