[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Third patch for proposal 121



-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi Nick,

here is the third patch to implement proposal 121---which is the first
that actually changes protocol messages and not just the local
configuration. This is the plan:

3. This third patch uses authorization data that was configured using
the code in patch 1 to encode, encrypt, and upload hidden service
descriptors for services with either 'basic' or 'stealth' client
authorization.

4. The fourth patch will then use authorization data on client side
(that was introduced with patch 2) to download and decrypt hidden
service descriptors and establish connections to services that perform
client authorization.

Thanks!
- --Karsten
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFIos1k0M+WPffBEmURAhm8AKCS7CHjycCZCxp9zfbbsXyPEWU9qgCfXGoO
Dr3KOfPQDwOAAoDL6SlYnrE=
=rr0n
-----END PGP SIGNATURE-----
Index: /home/karsten/tor/tor-trunk-121-patches/src/or/or.h
===================================================================
--- /home/karsten/tor/tor-trunk-121-patches/src/or/or.h	(revision 16516)
+++ /home/karsten/tor/tor-trunk-121-patches/src/or/or.h	(working copy)
@@ -651,6 +651,23 @@
  * exchanging client authorization between hidden service and client. */
 #define REND_DESC_COOKIE_LEN_BASE64 22
 
+/** Length of client identifier in encrypted introduction points for hidden
+ * service authorization type 'basic'. */
+#define REND_BASIC_AUTH_CLIENT_ID_LEN 4
+
+/** Multiple of the number of clients to which the real number of clients
+ * is padded with fake clients for hidden service authorization type
+ * 'basic'. */
+#define REND_BASIC_AUTH_CLIENT_MULTIPLE 16
+
+/** Length of client entry consisting of client identifier and encrypted
+ * session key for hidden service authorization type 'basic'. */
+#define REND_BASIC_AUTH_CLIENT_ENTRY_LEN (REND_BASIC_AUTH_CLIENT_ID_LEN \
+                                          + CIPHER_KEY_LEN)
+
+/** Maximum size of v2 hidden service descriptors. */
+#define REND_DESC_MAX_SIZE (20 * 1024)
+
 /** Legal characters for use in authorized client names for a hidden
  * service. */
 #define REND_LEGAL_CLIENTNAME_CHARACTERS \
@@ -3907,7 +3924,9 @@
 int rend_cache_size(void);
 int rend_encode_v2_descriptors(smartlist_t *descs_out,
                                rend_service_descriptor_t *desc, time_t now,
-                               const char *descriptor_cookie, uint8_t period);
+                               uint8_t period, rend_auth_type_t auth_type,
+                               crypto_pk_env_t *client_key,
+                               smartlist_t *client_cookies);
 int rend_compute_v2_desc_id(char *desc_id_out, const char *service_id,
                             const char *descriptor_cookie,
                             time_t now, uint8_t replica);
@@ -4295,10 +4314,14 @@
                                      size_t *intro_points_encrypted_size_out,
                                      size_t *encoded_size_out,
                                      const char **next_out, const char *desc);
-int rend_decrypt_introduction_points(rend_service_descriptor_t *parsed,
+int rend_decrypt_introduction_points(char **ipos_decrypted,
+                                     size_t *ipos_decrypted_size,
                                      const char *descriptor_cookie,
-                                     const char *intro_content,
-                                     size_t intro_size);
+                                     const char *ipos_encrypted,
+                                     size_t ipos_encrypted_size);
+int rend_parse_introduction_points(rend_service_descriptor_t *parsed,
+                                   const char *intro_points_encoded,
+                                   size_t intro_points_encoded_size);
 int rend_parse_client_keys(strmap_t *parsed_clients, const char *str);
 
 #endif
Index: /home/karsten/tor/tor-trunk-121-patches/src/or/rendcommon.c
===================================================================
--- /home/karsten/tor/tor-trunk-121-patches/src/or/rendcommon.c	(revision 16516)
+++ /home/karsten/tor/tor-trunk-121-patches/src/or/rendcommon.c	(working copy)
@@ -150,15 +150,11 @@
   return 0;
 }
 
-/* Encode the introduction points in <b>desc</b>, optionally encrypt them with
- * an optional <b>descriptor_cookie</b> of length REND_DESC_COOKIE_LEN,
- * encode it in base64, and write it to a newly allocated string, and write a
- * pointer to it to *<b>ipos_base64</b>. Return 0 for success, -1
- * otherwise. */
+/** Encode the introduction points in <b>desc</b> and write the result to a
+ * newly allocated string pointed to by <b>encoded</b>. Return 0 for
+ * success, -1 otherwise. */
 static int
-rend_encode_v2_intro_points(char **ipos_base64,
-                            rend_service_descriptor_t *desc,
-                            const char *descriptor_cookie)
+rend_encode_v2_intro_points(char **encoded, rend_service_descriptor_t *desc)
 {
   size_t unenc_len;
   char *unenc = NULL;
@@ -166,7 +162,6 @@
   int i;
   int r = -1;
   /* Assemble unencrypted list of introduction points. */
-  *ipos_base64 = NULL;
   unenc_len = smartlist_len(desc->intro_nodes) * 1000; /* too long, but ok. */
   unenc = tor_malloc_zero(unenc_len);
   for (i = 0; i < smartlist_len(desc->intro_nodes); i++) {
@@ -231,37 +226,157 @@
   }
   unenc[unenc_written++] = '\n';
   unenc[unenc_written++] = 0;
-  /* If a descriptor cookie is passed, encrypt introduction points. */
-  if (descriptor_cookie) {
-    char *enc = tor_malloc_zero(unenc_written + CIPHER_IV_LEN);
-    crypto_cipher_env_t *cipher =
-      crypto_create_init_cipher(descriptor_cookie, 1);
-    int enclen = crypto_cipher_encrypt_with_iv(cipher, enc,
-                                               unenc_written + CIPHER_IV_LEN,
-                                               unenc, unenc_written);
+  *encoded = unenc;
+  r = 0;
+ done:
+  if (r<0)
+    tor_free(unenc);
+  return r;
+}
+
+/** Encrypt the encoded introduction points in <b>encoded</b> using
+ * authorization type  'basic' with <b>client_cookies</b> and write the
+ * result to a newly allocated string pointed to by <b>encrypted</b> of
+ * length <b>encrypted_len</b>. Return 0 for success, -1 otherwise. */
+static int
+rend_encrypt_v2_intro_points_basic(char **encrypted, size_t *encrypted_len,
+                                   const char *encoded,
+                                   smartlist_t *client_cookies)
+{
+  int r = -1, i, pos, enclen, client_blocks;
+  size_t len, client_entries_len;
+  char *enc = NULL, iv[CIPHER_IV_LEN], *client_part = NULL,
+       session_key[CIPHER_KEY_LEN];
+  smartlist_t *encrypted_session_keys = NULL;
+  crypto_digest_env_t *digest;
+  crypto_cipher_env_t *cipher;
+  tor_assert(encoded);
+  tor_assert(client_cookies && smartlist_len(client_cookies) > 0);
+
+  /* Generate session key. */
+  if (crypto_rand(session_key, CIPHER_KEY_LEN) < 0) {
+    log_warn(LD_REND, "Unable to generate random session key to encrypt "
+                      "introduction point string.");
+    goto done;
+  }
+
+  /* Determine length of encrypted introduction points including session
+   * keys. */
+  client_blocks = 1 + ((smartlist_len(client_cookies) - 1) /
+                       REND_BASIC_AUTH_CLIENT_MULTIPLE);
+  client_entries_len = client_blocks * REND_BASIC_AUTH_CLIENT_MULTIPLE *
+                       REND_BASIC_AUTH_CLIENT_ENTRY_LEN;
+  len = 2 + client_entries_len + CIPHER_IV_LEN + strlen(encoded);
+  enc = tor_malloc_zero(len);
+  enc[0] = 0x01;
+  enc[1] = (uint8_t)client_blocks;
+
+  /* Encrypt with random session key. */
+  cipher = crypto_create_init_cipher(session_key, 1);
+  enclen = crypto_cipher_encrypt_with_iv(cipher,
+      enc + 2 + client_entries_len,
+      CIPHER_IV_LEN + strlen(encoded), encoded, strlen(encoded));
+  crypto_free_cipher_env(cipher);
+  if (enclen < 0) {
+    log_warn(LD_REND, "Could not encrypt introduction point string.");
+    goto done;
+  }
+  memcpy(iv, enc + 2 + client_entries_len, CIPHER_IV_LEN);
+
+  /* Encrypt session key for cookies, determine client IDs, and put both
+   * in a smartlist. */
+  encrypted_session_keys = smartlist_create();
+  SMARTLIST_FOREACH(client_cookies, char *, cookie, {
+    client_part = tor_malloc_zero(REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
+    /* Encrypt session key. */
+    cipher = crypto_create_init_cipher(cookie, 1);
+    if (crypto_cipher_encrypt(cipher, client_part +
+                                  REND_BASIC_AUTH_CLIENT_ID_LEN,
+                              session_key, CIPHER_KEY_LEN) < 0) {
+      log_warn(LD_REND, "Could not encrypt session key for client.");
+      crypto_free_cipher_env(cipher);
+      tor_free(client_part);
+      goto done;
+    }
     crypto_free_cipher_env(cipher);
-    if (enclen < 0) {
-      log_warn(LD_REND, "Could not encrypt introduction point string.");
-      tor_free(enc);
+
+    /* Determine client ID. */
+    digest = crypto_new_digest_env();
+    crypto_digest_add_bytes(digest, cookie, REND_DESC_COOKIE_LEN);
+    crypto_digest_add_bytes(digest, iv, CIPHER_IV_LEN);
+    crypto_digest_get_digest(digest, client_part,
+                             REND_BASIC_AUTH_CLIENT_ID_LEN);
+    crypto_free_digest_env(digest);
+
+    /* Put both together. */
+    smartlist_add(encrypted_session_keys, client_part);
+  });
+  /* Add some fake client IDs and encrypted session keys. */
+  for (i = (smartlist_len(client_cookies) - 1) %
+           REND_BASIC_AUTH_CLIENT_MULTIPLE;
+       i < REND_BASIC_AUTH_CLIENT_MULTIPLE - 1; i++) {
+    client_part = tor_malloc_zero(REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
+    if (crypto_rand(client_part, REND_BASIC_AUTH_CLIENT_ENTRY_LEN) < 0) {
+      log_warn(LD_REND, "Unable to generate fake client entry.");
+      tor_free(client_part);
       goto done;
     }
-    /* Replace original string with the encrypted one. */
-    tor_free(unenc);
-    unenc = enc;
-    unenc_written = enclen;
+    smartlist_add(encrypted_session_keys, client_part);
+  }
+  /* Sort smartlist and put elements in result in order. */
+  smartlist_sort_digests(encrypted_session_keys);
+  pos = 2;
+  SMARTLIST_FOREACH(encrypted_session_keys, char *, entry, {
+    memcpy(enc + pos, entry, REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
+    pos += REND_BASIC_AUTH_CLIENT_ENTRY_LEN;
+  });
+  *encrypted = enc;
+  *encrypted_len = len;
+  r = 0;
+ done:
+  if (r<0)
+    tor_free(enc);
+  if (encrypted_session_keys) {
+    SMARTLIST_FOREACH(encrypted_session_keys, char *, d, tor_free(d););
+    smartlist_free(encrypted_session_keys);
   }
-  /* Base64-encode introduction points. */
-  *ipos_base64 = tor_malloc_zero(unenc_written * 2);
-  if (base64_encode(*ipos_base64, unenc_written * 2, unenc, unenc_written)<0) {
-    log_warn(LD_REND, "Could not encode introduction point string to "
-             "base64.");
+  return r;
+}
+
+/** Encrypt the encoded introduction points in <b>encoded</b> using
+ * authorization type 'stealth' with <b>descriptor_cookie</b> of length
+ * REND_DESC_COOKIE_LEN and write the result to a newly allocated string
+ * pointed to by <b>encrypted</b> of length <b>encrypted_len</b>. Return 0
+ * for success, -1 otherwise. */
+static int
+rend_encrypt_v2_intro_points_stealth(char **encrypted,
+                                     size_t *encrypted_len,
+                                     const char *encoded,
+                                     const char *descriptor_cookie)
+{
+  int r = -1, enclen;
+  crypto_cipher_env_t *cipher;
+  char *enc;
+  tor_assert(encoded);
+  tor_assert(descriptor_cookie);
+
+  enc = tor_malloc_zero(1 + CIPHER_IV_LEN + strlen(encoded));
+  enc[0] = 0x02;
+  cipher = crypto_create_init_cipher(descriptor_cookie, 1);
+  enclen = crypto_cipher_encrypt_with_iv(cipher, enc + 1,
+                                         CIPHER_IV_LEN+strlen(encoded),
+                                         encoded, strlen(encoded));
+  crypto_free_cipher_env(cipher);
+  if (enclen < 0) {
+    log_warn(LD_REND, "Could not encrypt introduction point string.");
     goto done;
   }
+  *encrypted = enc;
+  *encrypted_len = enclen;
   r = 0;
  done:
   if (r<0)
-    tor_free(*ipos_base64);
-  tor_free(unenc);
+    tor_free(enc);
   return r;
 }
 
@@ -308,22 +423,34 @@
 }
 
 /** Encode a set of rend_encoded_v2_service_descriptor_t's for <b>desc</b>
- * at time <b>now</b> using <b>descriptor_cookie</b> (may be <b>NULL</b> if
- * introduction points shall not be encrypted) and <b>period</b> (e.g. 0
- * for the current period, 1 for the next period, etc.) and add them to
- * the existing list <b>descs_out</b>; return the number of seconds that
- * the descriptors will be found by clients, or -1 if the encoding was not
- * successful. */
+ * at time <b>now</b> using <b>service_key</b>, depending on
+ * <b>auth_type</b> a <b>descriptor_cookie</b> and a list of
+ * <b>client_cookies</b> (which are both <b>NULL</b> if no client
+ * authorization is performed), and <b>period</b> (e.g. 0 for the current
+ * period, 1 for the next period, etc.) and add them to the existing list
+ * <b>descs_out</b>; return the number of seconds that the descriptors will
+ * be found by clients, or -1 if the encoding was not successful. */
 int
 rend_encode_v2_descriptors(smartlist_t *descs_out,
                            rend_service_descriptor_t *desc, time_t now,
-                           const char *descriptor_cookie, uint8_t period)
+                           uint8_t period, rend_auth_type_t auth_type,
+                           crypto_pk_env_t *client_key,
+                           smartlist_t *client_cookies)
 {
   char service_id[DIGEST_LEN];
   uint32_t time_period;
-  char *ipos_base64 = NULL;
+  char *ipos_base64 = NULL, *ipos = NULL, *ipos_encrypted = NULL,
+       *descriptor_cookie = NULL;
+  size_t ipos_len = 0, ipos_encrypted_len = 0;
   int k;
   uint32_t seconds_valid;
+  crypto_pk_env_t *service_key = auth_type == REND_STEALTH_AUTH ?
+                                 client_key : desc->pk;
+  tor_assert(service_key);
+  if (auth_type == REND_STEALTH_AUTH) {
+    descriptor_cookie = smartlist_get(client_cookies, 0);
+    tor_assert(descriptor_cookie);
+  }
   if (!desc) {
     log_warn(LD_REND, "Could not encode v2 descriptor: No desc given.");
     return -1;
@@ -329,7 +456,7 @@
     return -1;
   }
   /* Obtain service_id from public key. */
-  crypto_pk_get_digest(desc->pk, service_id);
+  crypto_pk_get_digest(service_key, service_id);
   /* Calculate current time-period. */
   time_period = get_time_period(now, period, service_id);
   /* Determine how many seconds the descriptor will be valid. */
@@ -336,10 +463,52 @@
   seconds_valid = period * REND_TIME_PERIOD_V2_DESC_VALIDITY +
                   get_seconds_valid(now, service_id);
   /* Assemble, possibly encrypt, and encode introduction points. */
-  if (smartlist_len(desc->intro_nodes) > 0 &&
-      rend_encode_v2_intro_points(&ipos_base64, desc, descriptor_cookie) < 0) {
-    log_warn(LD_REND, "Encoding of introduction points did not succeed.");
-    return -1;
+  if (smartlist_len(desc->intro_nodes) > 0) {
+    if (rend_encode_v2_intro_points(&ipos, desc) < 0) {
+      log_warn(LD_REND, "Encoding of introduction points did not succeed.");
+      return -1;
+    }
+    switch (auth_type) {
+      case REND_NO_AUTH:
+        ipos_len = strlen(ipos);
+        break;
+      case REND_BASIC_AUTH:
+        if (rend_encrypt_v2_intro_points_basic(&ipos_encrypted,
+                                               &ipos_encrypted_len, ipos,
+                                               client_cookies) < 0) {
+          log_warn(LD_REND, "Encrypting of introduction points did not "
+                            "succeed.");
+          tor_free(ipos);
+          return -1;
+        }
+        tor_free(ipos);
+        ipos = ipos_encrypted;
+        ipos_len = ipos_encrypted_len;
+        break;
+      case REND_STEALTH_AUTH:
+        if (rend_encrypt_v2_intro_points_stealth(&ipos_encrypted,
+                                                 &ipos_encrypted_len, ipos,
+                                                 descriptor_cookie) < 0) {
+          log_warn(LD_REND, "Encrypting of introduction points did not "
+                            "succeed.");
+          tor_free(ipos);
+          return -1;
+        }
+        tor_free(ipos);
+        ipos = ipos_encrypted;
+        ipos_len = ipos_encrypted_len;
+        break;
+    }
+    /* Base64-encode introduction points. */
+    ipos_base64 = tor_malloc_zero(ipos_len * 2);
+    if (base64_encode(ipos_base64, ipos_len * 2, ipos, ipos_len)<0) {
+      log_warn(LD_REND, "Could not encode introduction point string to "
+               "base64. length=%d", ipos_len);
+      tor_free(ipos_base64);
+      tor_free(ipos);
+      return -1;
+    }
+    tor_free(ipos);
   }
   /* Encode REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS descriptors. */
   for (k = 0; k < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; k++) {
@@ -369,7 +538,7 @@
     base32_encode(desc_id_base32, sizeof(desc_id_base32),
                   enc->desc_id, DIGEST_LEN);
     /* PEM-encode the public key */
-    if (crypto_pk_write_public_key_to_string(desc->pk, &permanent_key,
+    if (crypto_pk_write_public_key_to_string(service_key, &permanent_key,
                                              &permanent_key_len) < 0) {
       log_warn(LD_BUG, "Could not write public key to string.");
       rend_encoded_v2_service_descriptor_free(enc);
@@ -437,7 +606,7 @@
     }
     if (router_append_dirobj_signature(desc_str + written,
                                        desc_len - written,
-                                       desc_digest, desc->pk) < 0) {
+                                       desc_digest, service_key) < 0) {
       log_warn(LD_BUG, "Couldn't sign desc.");
       rend_encoded_v2_service_descriptor_free(enc);
       goto err;
@@ -1085,6 +1254,7 @@
   rend_cache_entry_t *e;
   tor_assert(rend_cache);
   tor_assert(desc);
+  (void) descriptor_cookie; /* We don't use it, yet. */
   /* Parse the descriptor. */
   if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
                                        &intro_size, &encoded_size,
@@ -1103,8 +1273,8 @@
   }
   /* Decode/decrypt introduction points. */
   if (intro_content) {
-    if (rend_decrypt_introduction_points(parsed, descriptor_cookie,
-                                         intro_content, intro_size) < 0) {
+    if (rend_parse_introduction_points(parsed, intro_content,
+                                       intro_size) < 0) {
       log_warn(LD_PROTOCOL,"Couldn't decode/decrypt introduction points.");
       rend_service_descriptor_free(parsed);
       tor_free(intro_content);
Index: /home/karsten/tor/tor-trunk-121-patches/src/or/rendservice.c
===================================================================
--- /home/karsten/tor/tor-trunk-121-patches/src/or/rendservice.c	(revision 16516)
+++ /home/karsten/tor/tor-trunk-121-patches/src/or/rendservice.c	(working copy)
@@ -487,9 +487,8 @@
   d->timestamp = time(NULL);
   d->version = service->descriptor_version;
   d->intro_nodes = smartlist_create();
-  /* Whoever understands descriptor version 2 also understands intro
-   * protocol 2. So we only support 2. */
-  d->protocols = 1 << 2;
+  /* Support intro protocols 2 and 3. */
+  d->protocols = (1 << 2) + (1 << 3);
 
   for (i = 0; i < smartlist_len(service->intro_nodes); ++i) {
     rend_intro_point_t *intro_svc = smartlist_get(service->intro_nodes, i);
@@ -1446,40 +1445,38 @@
       get_options()->PublishHidServDescriptors) {
     networkstatus_t *c = networkstatus_get_latest_consensus();
     if (c && smartlist_len(c->routerstatus_list) > 0) {
-      int seconds_valid;
+      int seconds_valid, i, j, num_descs;
       smartlist_t *descs = smartlist_create();
-      int i;
-      /* Encode the current descriptor. */
-      seconds_valid = rend_encode_v2_descriptors(descs, service->desc, now,
-                                                 NULL, 0);
-      if (seconds_valid < 0) {
-        log_warn(LD_BUG, "Internal error: couldn't encode service descriptor; "
-                 "not uploading.");
-        smartlist_free(descs);
-        return;
-      }
-      /* Post the current descriptors to the hidden service directories. */
-      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);
-      /* Free memory for descriptors. */
-      for (i = 0; i < smartlist_len(descs); i++)
-        rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
-      smartlist_clear(descs);
-      /* Update next upload time. */
-      if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
-          > rendpostperiod)
-        service->next_upload_time = now + rendpostperiod;
-      else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
-        service->next_upload_time = now + seconds_valid + 1;
-      else
-        service->next_upload_time = now + seconds_valid -
-            REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
-      /* Post also the next descriptors, if necessary. */
-      if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
+      smartlist_t *client_cookies = smartlist_create();
+      /* Either upload a single descriptor (including replicas) or one
+       * descriptor for each authorized client in case of authorization
+       * type 'stealth'. */
+      num_descs = service->auth_type == REND_STEALTH_AUTH ?
+                      smartlist_len(service->clients) : 1;
+      for (j = 0; j < num_descs; j++) {
+        crypto_pk_env_t *client_key = NULL;
+        rend_authorized_client_t *client = NULL;
+        smartlist_clear(client_cookies);
+        switch (service->auth_type) {
+          case REND_NO_AUTH:
+            /* Do nothing here. */
+            break;
+          case REND_BASIC_AUTH:
+            SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *,
+                cl, smartlist_add(client_cookies, cl->descriptor_cookie));
+            break;
+          case REND_STEALTH_AUTH:
+            client = smartlist_get(service->clients, j);
+            client_key = client->client_key;
+            smartlist_add(client_cookies, client->descriptor_cookie);
+            break;
+        }
+        /* Encode the current descriptor. */
         seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
-                                                   now, NULL, 1);
+                                                   now, 0,
+                                                   service->auth_type,
+                                                   client_key,
+                                                   client_cookies);
         if (seconds_valid < 0) {
           log_warn(LD_BUG, "Internal error: couldn't encode service "
                    "descriptor; not uploading.");
@@ -1484,8 +1481,13 @@
           log_warn(LD_BUG, "Internal error: couldn't encode service "
                    "descriptor; not uploading.");
           smartlist_free(descs);
+          smartlist_free(client_cookies);
           return;
         }
+        /* Post the current descriptors to the hidden service directories. */
+        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);
         /* Free memory for descriptors. */
         for (i = 0; i < smartlist_len(descs); i++)
@@ -1490,8 +1492,39 @@
         /* Free memory for descriptors. */
         for (i = 0; i < smartlist_len(descs); i++)
           rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
+        smartlist_clear(descs);
+        /* Update next upload time. */
+        if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
+            > rendpostperiod)
+          service->next_upload_time = now + rendpostperiod;
+        else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
+          service->next_upload_time = now + seconds_valid + 1;
+        else
+          service->next_upload_time = now + seconds_valid -
+              REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
+        /* Post also the next descriptors, if necessary. */
+        if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
+          seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
+                                                     now, 1,
+                                                     service->auth_type,
+                                                     client_key,
+                                                     client_cookies);
+          if (seconds_valid < 0) {
+            log_warn(LD_BUG, "Internal error: couldn't encode service "
+                     "descriptor; not uploading.");
+            smartlist_free(descs);
+            smartlist_free(client_cookies);
+            return;
+          }
+          directory_post_to_hs_dir(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));
+          smartlist_clear(descs);
+        }
       }
       smartlist_free(descs);
+      smartlist_free(client_cookies);
       uploaded = 1;
       log_info(LD_REND, "Successfully uploaded v2 rend descriptors!");
     }
Index: /home/karsten/tor/tor-trunk-121-patches/src/or/routerparse.c
===================================================================
--- /home/karsten/tor/tor-trunk-121-patches/src/or/routerparse.c	(revision 16516)
+++ /home/karsten/tor/tor-trunk-121-patches/src/or/routerparse.c	(working copy)
@@ -3421,6 +3421,13 @@
     eos = desc + strlen(desc);
   else
     eos = eos + 1;
+  /* Check length. */
+  if (strlen(desc) > REND_DESC_MAX_SIZE) {
+    log_warn(LD_REND, "Descriptor length is %i which exceeds "
+             "maximum rendezvous descriptor size of %i kilobytes.",
+             strlen(desc), REND_DESC_MAX_SIZE);
+    goto err;
+  }
   /* Tokenize descriptor. */
   area = memarea_new(4096);
   if (tokenize_string(area, desc, eos, tokens, desc_token_table, 0)) {
@@ -3560,21 +3567,123 @@
   return -1;
 }
 
-/** Decrypt and decode the introduction points in
- * <b>intro_points_encrypted</b> of length
- * <b>intro_points_encrypted_size</b> using <b>descriptor_cookie</b>
- * (which may also be <b>NULL</b> if no decryption, but only parsing is
- * required), parse the introduction points, and write the result to
- * <b>parsed</b>; return the number of successfully parsed introduction
- * points or -1 in case of a failure.
- */
+/** Decrypt the encrypted introduction points in <b>ipos_encrypted</b> of
+ * length <b>ipos_encrypted_size</b> using <b>descriptor_cookie</b> and
+ * write the result to a newly allocated string that is pointed to by
+ * <b>ipos_decrypted</b> and its length to <b>ipos_decrypted_size</b>.
+ * Return 0 if decryption was successful and -1 otherwise. */
 int
-rend_decrypt_introduction_points(rend_service_descriptor_t *parsed,
+rend_decrypt_introduction_points(char **ipos_decrypted,
+                                 size_t *ipos_decrypted_size,
                                  const char *descriptor_cookie,
-                                 const char *intro_points_encrypted,
-                                 size_t intro_points_encrypted_size)
+                                 const char *ipos_encrypted,
+                                 size_t ipos_encrypted_size)
 {
-  char *ipos_decrypted = NULL;
+  tor_assert(ipos_encrypted);
+  tor_assert(descriptor_cookie);
+  if (ipos_encrypted_size < 2) {
+    log_warn(LD_REND, "Size of encrypted introduction points is too "
+                      "small.");
+    return -1;
+  }
+  if (ipos_encrypted[0] == (int)REND_BASIC_AUTH) {
+    char iv[CIPHER_IV_LEN], client_id[REND_BASIC_AUTH_CLIENT_ID_LEN],
+         session_key[CIPHER_KEY_LEN], *dec;
+    int declen, client_blocks;
+    size_t pos = 0, len, client_entries_len;
+    crypto_digest_env_t *digest;
+    crypto_cipher_env_t *cipher;
+    client_blocks = (int) ipos_encrypted[1];
+    client_entries_len = client_blocks * REND_BASIC_AUTH_CLIENT_MULTIPLE *
+                         REND_BASIC_AUTH_CLIENT_ENTRY_LEN;
+    if (ipos_encrypted_size < 2 + client_entries_len + CIPHER_IV_LEN + 1) {
+      log_warn(LD_REND, "Size of encrypted introduction points is too "
+                        "small.");
+      return -1;
+    }
+    memcpy(iv, ipos_encrypted + 2 + client_entries_len, CIPHER_IV_LEN);
+    digest = crypto_new_digest_env();
+    crypto_digest_add_bytes(digest, descriptor_cookie, REND_DESC_COOKIE_LEN);
+    crypto_digest_add_bytes(digest, iv, CIPHER_IV_LEN);
+    crypto_digest_get_digest(digest, client_id,
+                             REND_BASIC_AUTH_CLIENT_ID_LEN);
+    crypto_free_digest_env(digest);
+    for (pos = 2; pos < 2 + client_entries_len;
+         pos += REND_BASIC_AUTH_CLIENT_ENTRY_LEN) {
+      if (!memcmp(ipos_encrypted + pos, client_id,
+                  REND_BASIC_AUTH_CLIENT_ID_LEN)) {
+        /* Attempt to decrypt introduction points. */
+        cipher = crypto_create_init_cipher(descriptor_cookie, 0);
+        if (crypto_cipher_decrypt(cipher, session_key, ipos_encrypted
+                                  + pos + REND_BASIC_AUTH_CLIENT_ID_LEN,
+                                  CIPHER_KEY_LEN) < 0) {
+          log_warn(LD_REND, "Could not decrypt session key for client.");
+          crypto_free_cipher_env(cipher);
+          return -1;
+        }
+        crypto_free_cipher_env(cipher);
+        cipher = crypto_create_init_cipher(session_key, 0);
+        len = ipos_encrypted_size - 2 - client_entries_len - CIPHER_IV_LEN;
+        dec = tor_malloc_zero(len);
+        declen = crypto_cipher_decrypt_with_iv(cipher, dec, len,
+            ipos_encrypted + 2 + client_entries_len,
+            ipos_encrypted_size - 2 - client_entries_len);
+        crypto_free_cipher_env(cipher);
+        if (declen < 0) {
+          log_warn(LD_REND, "Could not decrypt introduction point string.");
+          tor_free(dec);
+          return -1;
+        }
+        if (strcmpstart(dec, "introduction-point ")) {
+          log_warn(LD_REND, "Decrypted introduction points don't "
+                            "look like we could parse them.");
+          tor_free(dec);
+          continue;
+        }
+        *ipos_decrypted = dec;
+        *ipos_decrypted_size = declen;
+        return 0;
+      }
+    }
+    log_warn(LD_REND, "Could not decrypt introduction points. Please "
+             "check your authorization for this service!");
+    return -1;
+  } else if (ipos_encrypted[0] == (int)REND_STEALTH_AUTH) {
+    crypto_cipher_env_t *cipher;
+    char *dec;
+    int declen;
+    dec = tor_malloc_zero(ipos_encrypted_size - CIPHER_IV_LEN - 1);
+    cipher = crypto_create_init_cipher(descriptor_cookie, 0);
+    declen = crypto_cipher_decrypt_with_iv(cipher, dec,
+                                           ipos_encrypted_size -
+                                               CIPHER_IV_LEN - 1,
+                                           ipos_encrypted + 1,
+                                           ipos_encrypted_size - 1);
+    crypto_free_cipher_env(cipher);
+    if (declen < 0) {
+      log_warn(LD_REND, "Decrypting introduction points failed!");
+      tor_free(dec);
+      return -1;
+    }
+    *ipos_decrypted = dec;
+    *ipos_decrypted_size = declen;
+    return 0;
+  } else {
+    log_warn(LD_REND, "Unknown authorization type number: %d",
+             ipos_encrypted[0]);
+    return -1;
+  }
+}
+
+/** Parse the encoded introduction points in <b>intro_points_encoded</b> of
+ * length <b>intro_points_encoded_size</b> and write the result to the
+ * descriptor in <b>parsed</b>; return the number of successfully parsed
+ * introduction points or -1 in case of a failure. */
+int
+rend_parse_introduction_points(rend_service_descriptor_t *parsed,
+                               const char *intro_points_encoded,
+                               size_t intro_points_encoded_size)
+{
   const char **current_ipo;
   smartlist_t *tokens;
   directory_token_t *tok;
@@ -3585,28 +3694,10 @@
   tor_assert(parsed);
   /** Function may only be invoked once. */
   tor_assert(!parsed->intro_nodes);
-  tor_assert(intro_points_encrypted);
-  tor_assert(intro_points_encrypted_size > 0);
-  /* Decrypt introduction points, if required. */
-  if (descriptor_cookie) {
-    crypto_cipher_env_t *cipher;
-    int unenclen;
-    ipos_decrypted = tor_malloc_zero(intro_points_encrypted_size - 16);
-    cipher = crypto_create_init_cipher(descriptor_cookie, 0);
-    unenclen = crypto_cipher_decrypt_with_iv(cipher, ipos_decrypted,
-                                             intro_points_encrypted_size - 16,
-                                             intro_points_encrypted,
-                                             intro_points_encrypted_size);
-    crypto_free_cipher_env(cipher);
-    if (unenclen < 0) {
-      tor_free(ipos_decrypted);
-      return -1;
-    }
-    intro_points_encrypted = ipos_decrypted;
-    intro_points_encrypted_size = unenclen;
-  }
+  tor_assert(intro_points_encoded);
+  tor_assert(intro_points_encoded_size > 0);
   /* Consider one intro point after the other. */
-  current_ipo = &intro_points_encrypted;
+  current_ipo = &intro_points_encoded;
   tokens = smartlist_create();
   parsed->intro_nodes = smartlist_create();
   area = memarea_new(4096);
Index: /home/karsten/tor/tor-trunk-121-patches/src/or/test.c
===================================================================
--- /home/karsten/tor/tor-trunk-121-patches/src/or/test.c	(revision 16516)
+++ /home/karsten/tor/tor-trunk-121-patches/src/or/test.c	(working copy)
@@ -3907,8 +3907,8 @@
     intro->intro_key = crypto_pk_dup_key(pk2);
     smartlist_add(generated->intro_nodes, intro);
   }
-  test_assert(rend_encode_v2_descriptors(descs, generated, now,
-                                         NULL, 0) > 0);
+  test_assert(rend_encode_v2_descriptors(descs, generated, now, 0,
+                                         REND_NO_AUTH, NULL, NULL) > 0);
   test_assert(rend_compute_v2_desc_id(computed_desc_id, service_id_base32,
                                       NULL, now, 0) == 0);
   test_memeq(((rend_encoded_v2_service_descriptor_t *)
@@ -3923,9 +3923,8 @@
   test_assert(parsed);
   test_memeq(((rend_encoded_v2_service_descriptor_t *)
              smartlist_get(descs, 0))->desc_id, parsed_desc_id, DIGEST_LEN);
-  test_assert(rend_decrypt_introduction_points(parsed, NULL,
-                                               intro_points_encrypted,
-                                               intro_points_size) == 3);
+  test_assert(rend_parse_introduction_points(parsed, intro_points_encrypted,
+                                             intro_points_size) == 3);
   test_assert(!crypto_pk_cmp_keys(generated->pk, parsed->pk));
   test_eq(parsed->timestamp, now);
   test_eq(parsed->version, 2);

Attachment: patch-121-3.txt.sig
Description: Binary data