Browse Source

Merge branch 'maint-0.2.9'

Conflicts:
	src/or/rendservice.c
Nick Mathewson 7 years ago
parent
commit
3e3040a5d9

+ 2 - 2
changes/20460

@@ -1,4 +1,4 @@
-  o Minor bugfixes (testing)
+  o Minor bugfixes (testing):
     - Use ECDHE ciphers instead of ECDH in tortls tests. LibreSSL has
       removed the ECDH ciphers which caused the tests to fail on
-      platforms which use it. Closes ticket 20460.
+      platforms which use it. Fixes bug 20460; bugfix on 0.2.8.1-alpha.

+ 5 - 0
changes/bug20484

@@ -0,0 +1,5 @@
+  o Minor bugfixes (single onion services):
+    - Start correctly when creating a single onion service in a
+      directory that did not previously exist. Fixes bug 20484; bugfix on
+      0.2.9.3-alpha.
+

+ 4 - 0
changes/bug20529

@@ -0,0 +1,4 @@
+  o Minor bugfixes (hidden services):
+    - When configuring hidden services, check every hidden service directory's
+      permissions. Previously, we only checked the last hidden service.
+      Fixes bug 20529; bugfix on 13942 commit 85bfad1 in 0.2.6.2-alpha.

+ 0 - 1
changes/bug20587

@@ -1,4 +1,3 @@
-
   o Minor bugfixes (download timing):
     - When determining when to download a directory object, handle times
       after 2038 if the operating system supports that.  (Someday this will be

+ 1 - 1
changes/bug20588

@@ -1,3 +1,3 @@
-  o Minor bugfixes (portability):
+  o Minor features (portability):
     - Fix compilation with OpenSSL 1.1 and less commonly-used
       CPU architectures. Closes ticket 20588.

+ 1 - 1
changes/geoip-november2016

@@ -1,4 +1,4 @@
-  o Minor features:
+  o Minor features (ge0oip):
     - Update geoip and geoip6 to the November 3 2016 Maxmind GeoLite2
       Country database.
 

+ 4 - 0
doc/tor.1.txt

@@ -2898,6 +2898,10 @@ __HiddenServiceDirectory__**/client_keys**::
     Authorization data for a hidden service that is only accessible by
     authorized clients.
 
+__HiddenServiceDirectory__**/onion_service_non_anonymous**::
+    This file is present if a hidden service key was created in
+    **HiddenServiceNonAnonymousMode**.
+
 SEE ALSO
 --------
 **torsocks**(1), **torify**(1) +

+ 7 - 5
src/common/util.c

@@ -2191,11 +2191,13 @@ file_status(const char *fname)
   }
 }
 
-/** Check whether <b>dirname</b> exists and is private.  If yes return 0.  If
- * it does not exist, and <b>check</b>&CPD_CREATE is set, try to create it
- * and return 0 on success. If it does not exist, and
- * <b>check</b>&CPD_CHECK, and we think we can create it, return 0.  Else
- * return -1.  If CPD_GROUP_OK is set, then it's okay if the directory
+/** Check whether <b>dirname</b> exists and is private.  If yes return 0.
+ * If <b>dirname</b> does not exist:
+ *  - if <b>check</b>&CPD_CREATE, try to create it and return 0 on success.
+ *  - if <b>check</b>&CPD_CHECK, and we think we can create it, return 0.
+ *  - if <b>check</b>&CPD_CHECK is false, and the directory exists, return 0.
+ *  - otherwise, return -1.
+ * If CPD_GROUP_OK is set, then it's okay if the directory
  * is group-readable, but in all cases we create the directory mode 0700.
  * If CPD_GROUP_READ is set, existing directory behaves as CPD_GROUP_OK and
  * if the directory is created it will use mode 0750 with group read

+ 52 - 24
src/or/rendservice.c

@@ -73,6 +73,10 @@ static ssize_t rend_service_parse_intro_for_v3(
     size_t plaintext_len,
     char **err_msg_out);
 
+static int rend_service_check_private_dir(const or_options_t *options,
+                                          const rend_service_t *s,
+                                          int create);
+
 /** Represents the mapping from a virtual port of a rendezvous service to
  * a real port on some IP.
  */
@@ -472,6 +476,11 @@ rend_config_services(const or_options_t *options, int validate_only)
   for (line = options->RendConfigLines; line; line = line->next) {
     if (!strcasecmp(line->key, "HiddenServiceDir")) {
       if (service) { /* register the one we just finished parsing */
+        if (rend_service_check_private_dir(options, service, 0) < 0) {
+          rend_service_free(service);
+          return -1;
+        }
+
         if (validate_only)
           rend_service_free(service);
         else
@@ -687,12 +696,7 @@ rend_config_services(const or_options_t *options, int validate_only)
     }
   }
   if (service) {
-    cpd_check_t check_opts = CPD_CHECK_MODE_ONLY|CPD_CHECK;
-    if (service->dir_group_readable) {
-      check_opts |= CPD_GROUP_READ;
-    }
-
-    if (check_private_dir(service->directory, check_opts, options->User) < 0) {
+    if (rend_service_check_private_dir(options, service, 0) < 0) {
       rend_service_free(service);
       return -1;
     }
@@ -1016,7 +1020,9 @@ service_is_single_onion_poisoned(const rend_service_t *service)
   fstatus = file_status(poison_fname);
   tor_free(poison_fname);
 
-  /* If this fname is occupied, the hidden service has been poisoned. */
+  /* If this fname is occupied, the hidden service has been poisoned.
+   * fstatus can be FN_ERROR if the service directory does not exist, in that
+   * case, there is obviously no private key. */
   if (fstatus == FN_FILE || fstatus == FN_EMPTY) {
     return 1;
   }
@@ -1032,7 +1038,9 @@ rend_service_private_key_exists(const rend_service_t *service)
   char *private_key_path = rend_service_path(service, private_key_fname);
   const file_status_t private_key_status = file_status(private_key_path);
   tor_free(private_key_path);
-  /* Only non-empty regular private key files could have been used before. */
+  /* Only non-empty regular private key files could have been used before.
+   * fstatus can be FN_ERROR if the service directory does not exist, in that
+   * case, there is obviously no private key. */
   return private_key_status == FN_FILE;
 }
 
@@ -1105,6 +1113,10 @@ poison_new_single_onion_hidden_service_dir(const rend_service_t *service)
     return -1;
   }
 
+  /* Make sure the directory exists */
+  if (rend_service_check_private_dir(get_options(), service, 1) < 0)
+    return -1;
+
   poison_fname = rend_service_sos_poison_path(service);
 
   switch (file_status(poison_fname)) {
@@ -1260,6 +1272,37 @@ rend_service_derive_key_digests(struct rend_service_t *s)
   return 0;
 }
 
+/** Make sure that the directory for <b>s</b> is private, using the config in
+ * <b>options</b>.
+ * If <b>create</b> is true:
+ *  - if the directory exists, change permissions if needed,
+ *  - if the directory does not exist, create it with the correct permissions.
+ * If <b>create</b> is false:
+ *  - if the directory exists, check permissions,
+ *  - if the directory does not exist, check if we think we can create it.
+ * Return 0 on success, -1 on failure. */
+static int
+rend_service_check_private_dir(const or_options_t *options,
+                               const rend_service_t *s,
+                               int create)
+{
+  cpd_check_t  check_opts = CPD_NONE;
+  if (create) {
+    check_opts |= CPD_CREATE;
+  } else {
+    check_opts |= CPD_CHECK_MODE_ONLY;
+    check_opts |= CPD_CHECK;
+  }
+  if (s->dir_group_readable) {
+    check_opts |= CPD_GROUP_READ;
+  }
+  /* Check/create directory */
+  if (check_private_dir(s->directory, check_opts, options->User) < 0) {
+    return -1;
+  }
+  return 0;
+}
+
 /** Load and/or generate private keys for the hidden service <b>s</b>,
  * possibly including keys for client authorization.  Return 0 on success, -1
  * on failure. */
@@ -1268,24 +1311,9 @@ rend_service_load_keys(rend_service_t *s)
 {
   char *fname = NULL;
   char buf[128];
-  cpd_check_t  check_opts = CPD_CREATE;
 
-  if (s->dir_group_readable) {
-    check_opts |= CPD_GROUP_READ;
-  }
-  /* Check/create directory */
-  if (check_private_dir(s->directory, check_opts, get_options()->User) < 0) {
+  if (rend_service_check_private_dir(get_options(), s, 1) < 0)
     goto err;
-  }
-#ifndef _WIN32
-  if (s->dir_group_readable) {
-    /* Only new dirs created get new opts, also enforce group read. */
-    if (chmod(s->directory, 0750)) {
-      log_warn(LD_FS,"Unable to make %s group-readable.",
-      rend_service_escaped_dir(s));
-    }
-  }
-#endif
 
   /* Load key */
   fname = rend_service_path(s, private_key_fname);

+ 1 - 0
src/test/test.h

@@ -73,6 +73,7 @@
     {print_ = (I64_PRINTF_TYPE) value_;}, {}, TT_EXIT_TEST_FUNCTION)
 
 const char *get_fname(const char *name);
+const char *get_fname_rnd(const char *name);
 struct crypto_pk_t *pk_generate(int idx);
 void init_pregenerated_keys(void);
 void free_pregenerated_keys(void);

+ 32 - 23
src/test/test_hs.c

@@ -527,6 +527,11 @@ mock_get_options(void)
   return mock_options;
 }
 
+/* arg can't be 0 (the test fails) or 2 (the test is skipped) */
+#define CREATE_HS_DIR_NONE ((intptr_t)0x04)
+#define CREATE_HS_DIR1     ((intptr_t)0x08)
+#define CREATE_HS_DIR2     ((intptr_t)0x10)
+
 /* Test that single onion poisoning works. */
 static void
 test_single_onion_poisoning(void *arg)
@@ -537,15 +542,15 @@ test_single_onion_poisoning(void *arg)
   MOCK(get_options, mock_get_options);
 
   int ret = -1;
-  mock_options->DataDirectory = tor_strdup(get_fname("test_data_dir"));
+  intptr_t create_dir_mask = (intptr_t)arg;
+  /* Get directories with a random suffix so we can repeat the tests */
+  mock_options->DataDirectory = tor_strdup(get_fname_rnd("test_data_dir"));
   rend_service_t *service_1 = tor_malloc_zero(sizeof(rend_service_t));
-  char *dir1 = tor_strdup(get_fname("test_hs_dir1"));
+  char *dir1 = tor_strdup(get_fname_rnd("test_hs_dir1"));
   rend_service_t *service_2 = tor_malloc_zero(sizeof(rend_service_t));
-  char *dir2 = tor_strdup(get_fname("test_hs_dir2"));
+  char *dir2 = tor_strdup(get_fname_rnd("test_hs_dir2"));
   smartlist_t *services = smartlist_new();
 
-  (void) arg;
-
   /* No services, no problem! */
   mock_options->HiddenServiceSingleHopMode = 0;
   mock_options->HiddenServiceNonAnonymousMode = 0;
@@ -558,22 +563,20 @@ test_single_onion_poisoning(void *arg)
   ret = rend_service_list_verify_single_onion_poison(services, mock_options);
   tt_assert(ret == 0);
 
-  /* Create directories for both services */
-
-#ifdef _WIN32
-  ret = mkdir(mock_options->DataDirectory);
-  tt_assert(ret == 0);
-  ret = mkdir(dir1);
-  tt_assert(ret == 0);
-  ret = mkdir(dir2);
-#else
-  ret = mkdir(mock_options->DataDirectory, 0700);
-  tt_assert(ret == 0);
-  ret = mkdir(dir1, 0700);
-  tt_assert(ret == 0);
-  ret = mkdir(dir2, 0700);
-#endif
+  /* Create the data directory, and, if the correct bit in arg is set,
+   * create a directory for that service.
+   * The data directory is required for the lockfile, which is used when
+   * loading keys. */
+  ret = check_private_dir(mock_options->DataDirectory, CPD_CREATE, NULL);
   tt_assert(ret == 0);
+  if (create_dir_mask & CREATE_HS_DIR1) {
+    ret = check_private_dir(dir1, CPD_CREATE, NULL);
+    tt_assert(ret == 0);
+  }
+  if (create_dir_mask & CREATE_HS_DIR2) {
+    ret = check_private_dir(dir2, CPD_CREATE, NULL);
+    tt_assert(ret == 0);
+  }
 
   service_1->directory = dir1;
   service_2->directory = dir2;
@@ -703,7 +706,7 @@ test_single_onion_poisoning(void *arg)
   tt_assert(ret < 0);
 
  done:
-  /* TODO: should we delete the directories here? */
+  /* The test harness deletes the directories at exit */
   rend_service_free(service_1);
   rend_service_free(service_2);
   smartlist_free(services);
@@ -725,8 +728,14 @@ struct testcase_t hs_tests[] = {
     NULL, NULL },
   { "hs_auth_cookies", test_hs_auth_cookies, TT_FORK,
     NULL, NULL },
-  { "single_onion_poisoning", test_single_onion_poisoning, TT_FORK,
-    NULL, NULL },
+  { "single_onion_poisoning_create_dir_none", test_single_onion_poisoning,
+    TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR_NONE) },
+  { "single_onion_poisoning_create_dir1", test_single_onion_poisoning,
+    TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR1) },
+  { "single_onion_poisoning_create_dir2", test_single_onion_poisoning,
+    TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR2) },
+  { "single_onion_poisoning_create_dir_both", test_single_onion_poisoning,
+    TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR1 | CREATE_HS_DIR2) },
   END_OF_TESTCASES
 };
 

+ 30 - 4
src/test/testing_common.c

@@ -102,18 +102,41 @@ setup_directory(void)
   temp_dir_setup_in_pid = getpid();
 }
 
-/** Return a filename relative to our testing temporary directory */
-const char *
-get_fname(const char *name)
+/** Return a filename relative to our testing temporary directory, based on
+ * name and suffix. If name is NULL, return the name of the testing temporary
+ * directory. */
+static const char *
+get_fname_suffix(const char *name, const char *suffix)
 {
   static char buf[1024];
   setup_directory();
   if (!name)
     return temp_dir;
-  tor_snprintf(buf,sizeof(buf),"%s/%s",temp_dir,name);
+  tor_snprintf(buf,sizeof(buf),"%s/%s%s%s",temp_dir,name,suffix ? "_" : "",
+               suffix ? suffix : "");
   return buf;
 }
 
+/** Return a filename relative to our testing temporary directory. If name is
+ * NULL, return the name of the testing temporary directory. */
+const char *
+get_fname(const char *name)
+{
+  return get_fname_suffix(name, NULL);
+}
+
+/** Return a filename with a random suffix, relative to our testing temporary
+ * directory. If name is NULL, return the name of the testing temporary
+ * directory, without any suffix. */
+const char *
+get_fname_rnd(const char *name)
+{
+  char rnd[256], rnd32[256];
+  crypto_rand(rnd, RAND_PATH_BYTES);
+  base32_encode(rnd32, sizeof(rnd32), rnd, RAND_PATH_BYTES);
+  return get_fname_suffix(name, rnd32);
+}
+
 /* Remove a directory and all of its subdirectories */
 static void
 rm_rf(const char *dir)
@@ -158,6 +181,9 @@ remove_directory(void)
 static void *
 passthrough_test_setup(const struct testcase_t *testcase)
 {
+  /* Make sure the passthrough doesn't unintentionally fail or skip tests */
+  tor_assert(testcase->setup_data);
+  tor_assert(testcase->setup_data != (void*)TT_SKIP);
   return testcase->setup_data;
 }
 static int