[or-cvs] r13441: First working implementation of simplified authorization pro (in tor/branches/121-hs-authorization: doc doc/spec src/common src/or)
kloesing at seul.org
kloesing at seul.org
Sat Feb 9 10:04:44 UTC 2008
Author: kloesing
Date: 2008-02-09 05:04:44 -0500 (Sat, 09 Feb 2008)
New Revision: 13441
Modified:
tor/branches/121-hs-authorization/doc/spec/rend-spec.txt
tor/branches/121-hs-authorization/doc/tor.1.in
tor/branches/121-hs-authorization/src/common/crypto.c
tor/branches/121-hs-authorization/src/common/crypto.h
tor/branches/121-hs-authorization/src/or/circuitlist.c
tor/branches/121-hs-authorization/src/or/circuituse.c
tor/branches/121-hs-authorization/src/or/config.c
tor/branches/121-hs-authorization/src/or/connection.c
tor/branches/121-hs-authorization/src/or/connection_edge.c
tor/branches/121-hs-authorization/src/or/directory.c
tor/branches/121-hs-authorization/src/or/or.h
tor/branches/121-hs-authorization/src/or/rendclient.c
tor/branches/121-hs-authorization/src/or/rendcommon.c
tor/branches/121-hs-authorization/src/or/rendservice.c
tor/branches/121-hs-authorization/src/or/routerparse.c
tor/branches/121-hs-authorization/src/or/test.c
Log:
First working implementation of simplified authorization protocol; changes to proposal 121 pending.
Modified: tor/branches/121-hs-authorization/doc/spec/rend-spec.txt
===================================================================
--- tor/branches/121-hs-authorization/doc/spec/rend-spec.txt 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/doc/spec/rend-spec.txt 2008-02-09 10:04:44 UTC (rev 13441)
@@ -492,12 +492,11 @@
and the client 30 minutes ahead), Bob's OP publishes the descriptor
under the ID of both, the current and the next publication period.
-1.5. Alice receives a x.y.z.onion address.
+1.5. Alice receives an onion address.
When Alice receives a pointer to a location-hidden service, it is as a
- hostname of the form "z.onion" or "y.z.onion" or "x.y.z.onion", where
- z is a base-32 encoding of a 10-octet hash of Bob's service's public
- key, computed as follows:
+ hostname of the form "z.onion", where z is a base-32 encoding of a
+ 10-octet hash of Bob's service's public key, computed as follows:
1. Let H = H(PK).
2. Let H' = the first 80 bits of H, considering each octet from
@@ -509,12 +508,11 @@
don't need to worry about arbitrary collisions, and because it will
make handling the url's more convenient.)
- The string "x", if present, is the base-32 encoding of the
- authentication/authorization required by the introduction point.
- The string "y", if present, is the base-32 encoding of the
- authentication/authorization required by the hidden service.
- Omitting a string is taken to mean auth type [00 00].
- See section 2 of this document for details on auth mechanisms.
+ [Formerly, onion addresses could contain authentication data for the
+ introduction point and the hidden service in the form x.y.z.onion. This
+ was never used and is also a bad idea, because in case of HTTP the
+ requested URL may be contained in the Host and Referer fields. Instead,
+ authentication data should only be written to local files. -KL]
[Yes, numbers are allowed at the beginning. See RFC 1123. -NM]
@@ -605,6 +603,18 @@
KEY Rendezvous point onion key [KLEN octets]
RC Rendezvous cookie [20 octets]
g^x Diffie-Hellman data, part 1 [128 octets]
+ OR (in the v3 intro protocol)
+ VER Version byte: set to 3. [1 octet]
+ AUTHT The auth type that is supported [1 octet]
+ AUTHL Length of auth data [2 octets]
+ AUTHD Auth data [variable]
+ IP Rendezvous point's address [4 octets]
+ PORT Rendezvous point's OR port [2 octets]
+ ID Rendezvous point identity ID [20 octets]
+ KLEN Length of onion key [2 octets]
+ KEY Rendezvous point onion key [KLEN octets]
+ RC Rendezvous cookie [20 octets]
+ g^x Diffie-Hellman data, part 1 [128 octets]
PK_ID is the hash of Bob's public key. RP is NUL-padded and
terminated. In version 0, it must contain a nickname. In version 1,
@@ -617,6 +627,8 @@
20+42+16+20+20+128=246 bytes, and the version 1 and version 2
introduction formats have other sizes.
+ AUTHL and AUTHD are only used when AUTHT is > 0.
+
Through Tor 0.2.0.6-alpha, clients only generated the v0 introduction
format, whereas hidden services have understood and accepted v0,
v1, and v2 since 0.1.1.x. As of Tor 0.2.0.7-alpha and 0.1.2.18,
@@ -632,15 +644,15 @@
"encrypted to Bob's PK" part of the introduction, but no Tors have
ever generated these.
- VER Version byte: set to 3. [1 octet]
+ VER Version byte: set to 4. [1 octet]
ATYPE An address type (typically 4) [1 octet]
ADDR Rendezvous point's IP address [4 or 16 octets]
- PORT Rendezvous point's OR port [2 octets]
- AUTHT The auth type that is supported [2 octets]
- AUTHL Length of auth data [1 octet]
+ PORT Rendezvous point's OR port [2 octets]
+ AUTHT The auth type that is supported [1 octet]
+ AUTHL Length of auth data [2 octets]
AUTHD Auth data [variable]
ID Rendezvous point identity ID [20 octets]
- KLEN Length of onion key [2 octets]
+ KLEN Length of onion key [2 octets]
KEY Rendezvous point onion key [KLEN octets]
RC Rendezvous cookie [20 octets]
g^x Diffie-Hellman data, part 1 [128 octets]
Modified: tor/branches/121-hs-authorization/doc/tor.1.in
===================================================================
--- tor/branches/121-hs-authorization/doc/tor.1.in 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/doc/tor.1.in 2008-02-09 10:04:44 UTC (rev 13441)
@@ -1194,6 +1194,16 @@
service. Possible version numbers are 0 and 2. (Default: 0, 2)
.LP
.TP
+\fBHiddenServiceAuthorizeClient \fR\fIclient-name\fR,\fIclient-name\fR,\fI...\fP
+A list of clients that are authorized to learn existence and access the
+hidden service. Valid client names are 1 to 19 characters long and only use
+characters in A-Za-z0-9+-_ (no spaces). If this option is used multiple
+times all client names are accumulated. If this option is set, the hidden
+service is not accessible for clients without authorization any more. You
+can find generated authorization data in the hostname file. See also the
+client_keys file for more information about authorization data.
+.LP
+.TP
\fBRendPostPeriod \fR\fIN\fR \fBseconds\fR|\fBminutes\fR|\fBhours\fR|\fBdays\fR|\fBweeks\fP
Every time the specified period elapses, Tor uploads any rendezvous
service descriptors to the directory servers. This information is also
@@ -1315,10 +1325,17 @@
.TP
.B \fIHiddenServiceDirectory\fP/hostname
The <base32-encoded-fingerprint>.onion domain name for this hidden service.
+If the hidden service is restricted to authorized clients only, this file
+also contains the authorization data for all clients.
.LP
.TP
.B \fIHiddenServiceDirectory\fP/private_key
The private key for this hidden service.
+.LP
+.TP
+.B \fIHiddenServiceDirectory\fP/client_keys
+Authorization data for a hidden service that is only accessible by authorized
+clients.
.SH SEE ALSO
.BR privoxy (1),
.BR tsocks (1),
Modified: tor/branches/121-hs-authorization/src/common/crypto.c
===================================================================
--- tor/branches/121-hs-authorization/src/common/crypto.c 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/src/common/crypto.c 2008-02-09 10:04:44 UTC (rev 13441)
@@ -506,6 +506,47 @@
return 0;
}
+/** PEM-encode the private key portion of <b>env</b> and write it to a
+ * newly allocated string. On success, set *<b>dest</b> to the new
+ * string, *<b>len</b> to the string's length, and return 0. On
+ * failure, return -1.
+ */
+int
+crypto_pk_write_private_key_to_string(crypto_pk_env_t *env, char **dest,
+ size_t *len)
+{
+ BUF_MEM *buf;
+ BIO *b;
+
+ tor_assert(env);
+ tor_assert(env->key);
+ tor_assert(dest);
+
+ b = BIO_new(BIO_s_mem()); /* Create a memory BIO */
+
+ /* Now you can treat b as if it were a file. Just use the
+ * PEM_*_bio_* functions instead of the non-bio variants.
+ */
+ if (!PEM_write_bio_RSAPrivateKey(b, env->key, NULL,NULL,0,NULL,NULL)) {
+ crypto_log_errors(LOG_WARN, "writing private key to string");
+ BIO_free(b);
+ return -1;
+ }
+
+ BIO_get_mem_ptr(b, &buf);
+ (void)BIO_set_close(b, BIO_NOCLOSE); /* so BIO_free doesn't free buf */
+ BIO_free(b);
+
+ tor_assert(buf->length >= 0);
+ *dest = tor_malloc(buf->length+1);
+ memcpy(*dest, buf->data, buf->length);
+ (*dest)[buf->length] = 0; /* nul terminate it */
+ *len = buf->length;
+ BUF_MEM_free(buf);
+
+ return 0;
+}
+
/** Read a PEM-encoded public key from the first <b>len</b> characters of
* <b>src</b>, and store the result in <b>env</b>. Return 0 on success, -1 on
* failure.
Modified: tor/branches/121-hs-authorization/src/common/crypto.h
===================================================================
--- tor/branches/121-hs-authorization/src/common/crypto.h 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/src/common/crypto.h 2008-02-09 10:04:44 UTC (rev 13441)
@@ -79,6 +79,8 @@
const char *keyfile);
int crypto_pk_write_public_key_to_string(crypto_pk_env_t *env,
char **dest, size_t *len);
+int crypto_pk_write_private_key_to_string(crypto_pk_env_t *env,
+ char **dest, size_t *len);
int crypto_pk_read_public_key_from_string(crypto_pk_env_t *env,
const char *src, size_t len);
int crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env,
@@ -205,9 +207,9 @@
int private);
struct dh_st *_crypto_dh_env_get_dh(crypto_dh_env_t *dh);
/* Prototypes for private functions only used by crypto.c and test.c*/
+#endif
int crypto_pk_read_private_key_from_string(crypto_pk_env_t *env,
const char *s);
-#endif
#endif
Modified: tor/branches/121-hs-authorization/src/or/circuitlist.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/circuitlist.c 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/src/or/circuitlist.c 2008-02-09 10:04:44 UTC (rev 13441)
@@ -1016,7 +1016,9 @@
safe_str(ocirc->rend_query),
safe_str(build_state_get_exit_nickname(ocirc->build_state)));
rend_client_remove_intro_point(ocirc->build_state->chosen_exit,
- ocirc->rend_query);
+ ocirc->rend_query,
+ (ocirc->has_desc_cookie ?
+ ocirc->rend_desc_cookie : NULL));
}
if (circ->n_conn)
connection_or_send_destroy(circ->n_circ_id, circ->n_conn, reason);
Modified: tor/branches/121-hs-authorization/src/or/circuituse.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/circuituse.c 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/src/or/circuituse.c 2008-02-09 10:04:44 UTC (rev 13441)
@@ -1026,8 +1026,14 @@
log_info(LD_REND,
"No intro points for '%s': refetching service descriptor.",
safe_str(conn->rend_query));
- rend_client_refetch_renddesc(conn->rend_query);
- rend_client_refetch_v2_renddesc(conn->rend_query);
+ /* Fetch both, v0 and v2 rend descriptors in parallel. Use whichever
+ * arrives first. Exception: When using a descriptor cookie, only
+ * fetch v2 descriptors.*/
+ rend_client_refetch_v2_renddesc(conn->rend_query,
+ (conn->has_desc_cookie ?
+ conn->rend_desc_cookie : NULL));
+ if (!conn->has_desc_cookie)
+ rend_client_refetch_renddesc(conn->rend_query);
conn->_base.state = AP_CONN_STATE_RENDDESC_WAIT;
return 0;
}
@@ -1106,8 +1112,13 @@
/* help predict this next time */
rep_hist_note_used_internal(time(NULL), need_uptime, 1);
if (circ) {
- /* write the service_id into circ */
+ /* write the service_id (and descriptor cookie?) into circ */
strlcpy(circ->rend_query, conn->rend_query, sizeof(circ->rend_query));
+ if (conn->has_desc_cookie) {
+ memcpy(circ->rend_desc_cookie, conn->rend_desc_cookie,
+ REND_DESC_COOKIE_LEN);
+ circ->has_desc_cookie = 1;
+ }
if (circ->_base.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND &&
circ->_base.state == CIRCUIT_STATE_OPEN)
rend_client_rendcirc_has_opened(circ);
Modified: tor/branches/121-hs-authorization/src/or/config.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/config.c 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/src/or/config.c 2008-02-09 10:04:44 UTC (rev 13441)
@@ -208,6 +208,8 @@
VAR("HiddenServiceOptions",LINELIST_V, RendConfigLines, NULL),
VAR("HiddenServicePort", LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines, NULL),
+ VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL),
+ VAR("HidServAuth", LINELIST_S, ClientSideHidServs, NULL),
V(HSAuthoritativeDir, BOOL, "0"),
V(HSAuthorityRecordStats, BOOL, "0"),
V(HttpProxy, STRING, NULL),
@@ -3040,6 +3042,16 @@
options->MinUptimeHidServDirectoryV2 = 0;
}
+ /* Parse client-side authorization for hidden services. */
+ if (options->ClientSideHidServs) {
+ for (cl = options->ClientSideHidServs; cl; cl = cl->next) {
+ if (!rend_parse_client_auth(cl->value)) {
+ log_warn(LD_CONFIG, "HidServAuth contains illegal value: '%s'. "
+ "Discarding.", cl->value);
+ }
+ }
+ }
+
if (options->RendPostPeriod < MIN_REND_POST_PERIOD) {
log(LOG_WARN,LD_CONFIG,"RendPostPeriod option must be at least %d seconds."
" Clipping.", MIN_REND_POST_PERIOD);
Modified: tor/branches/121-hs-authorization/src/or/connection.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/connection.c 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/src/or/connection.c 2008-02-09 10:04:44 UTC (rev 13441)
@@ -477,12 +477,14 @@
/* It's a directory connection and connecting or fetching
* failed: forget about this router, and maybe try again. */
connection_dir_request_failed(dir_conn);
+ /* if we were trying to fetch a v2 rend desc, retry as needed */
+ if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC_V2)
+ rend_client_refetch_v2_renddesc(dir_conn->rend_query,
+ (dir_conn->has_desc_cookie ?
+ dir_conn->rend_desc_cookie : NULL));
}
- /* if we were trying to fetch a rend desc, retry as needed */
if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC)
- rend_client_desc_here(dir_conn->rend_query);
- if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC_V2)
- rend_client_refetch_v2_renddesc(dir_conn->rend_query);
+ rend_client_desc_here(dir_conn->rend_query); /* give it a try */
break;
case CONN_TYPE_OR:
or_conn = TO_OR_CONN(conn);
Modified: tor/branches/121-hs-authorization/src/or/connection_edge.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/connection_edge.c 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/src/or/connection_edge.c 2008-02-09 10:04:44 UTC (rev 13441)
@@ -1455,6 +1455,7 @@
/* it's a hidden-service request */
rend_cache_entry_t *entry;
int r;
+ rend_service_authorization_t *client_auth;
tor_assert(!automap);
if (SOCKS_COMMAND_IS_RESOLVE(socks->command)) {
/* if it's a resolve request, fail it right now, rather than
@@ -1487,14 +1488,27 @@
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return -1;
}
+ /* Look up if we have client authorization for it. */
+ client_auth = lookup_client_auth(conn->rend_query);
+ if (client_auth) {
+ log_info(LD_REND, "Using previously configured client authorization "
+ "for hidden service.");
+ memcpy(conn->rend_desc_cookie, client_auth->descriptor_cookie,
+ REND_DESC_COOKIE_LEN);
+ conn->has_desc_cookie = 1;
+ }
if (r==0) {
conn->_base.state = AP_CONN_STATE_RENDDESC_WAIT;
log_info(LD_REND, "Unknown descriptor %s. Fetching.",
safe_str(conn->rend_query));
/* Fetch both, v0 and v2 rend descriptors in parallel. Use whichever
- * arrives first. */
- rend_client_refetch_v2_renddesc(conn->rend_query);
- rend_client_refetch_renddesc(conn->rend_query);
+ * arrives first. Exception: When using a descriptor cookie, only
+ * fetch v2 descriptors.*/
+ rend_client_refetch_v2_renddesc(conn->rend_query,
+ (conn->has_desc_cookie ?
+ conn->rend_desc_cookie : NULL));
+ if (!conn->has_desc_cookie)
+ rend_client_refetch_renddesc(conn->rend_query);
} else { /* r > 0 */
/** How long after we receive a hidden service descriptor do we consider
* it valid? */
@@ -1511,9 +1525,13 @@
log_info(LD_REND, "Stale descriptor %s. Refetching.",
safe_str(conn->rend_query));
/* Fetch both, v0 and v2 rend descriptors in parallel. Use whichever
- * arrives first. */
- rend_client_refetch_v2_renddesc(conn->rend_query);
- rend_client_refetch_renddesc(conn->rend_query);
+ * arrives first. Exception: When using a descriptor cookie, only
+ * fetch v2 descriptors.*/
+ rend_client_refetch_v2_renddesc(conn->rend_query,
+ (conn->has_desc_cookie ?
+ conn->rend_desc_cookie : NULL));
+ if (!conn->has_desc_cookie)
+ rend_client_refetch_renddesc(conn->rend_query);
}
}
return 0;
Modified: tor/branches/121-hs-authorization/src/or/directory.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/directory.c 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/src/or/directory.c 2008-02-09 10:04:44 UTC (rev 13441)
@@ -892,8 +892,15 @@
case DIR_PURPOSE_FETCH_RENDDESC_V2:
tor_assert(resource);
tor_assert(strlen(resource) <= REND_DESC_ID_V2_LEN_BASE32);
- /* Remember the query to refer to it when a response arrives. */
+ /* Remember the query (and maybe descriptor cookie) to refer to it
+ * when a response arrives. (This is terrible hack part 2.)*/
strlcpy(conn->rend_query, payload, sizeof(conn->rend_query));
+ if (strlen(payload) > REND_SERVICE_ID_LEN_BASE32) {
+ memcpy(conn->rend_desc_cookie,
+ payload + REND_SERVICE_ID_LEN_BASE32 + 1,
+ REND_DESC_COOKIE_LEN);
+ conn->has_desc_cookie = 1;
+ }
payload = NULL;
httpcommand = "GET";
len = strlen(resource) + 32;
@@ -1796,7 +1803,8 @@
(int)body_len, status_code, escaped(reason));
switch (status_code) {
case 200:
- switch (rend_cache_store_v2_desc_as_client(body, NULL)) {
+ switch (rend_cache_store_v2_desc_as_client(body,
+ (conn->has_desc_cookie ? conn->rend_desc_cookie : NULL))) {
case -2:
log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed. "
"Retrying at another directory.");
Modified: tor/branches/121-hs-authorization/src/or/or.h
===================================================================
--- tor/branches/121-hs-authorization/src/or/or.h 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/src/or/or.h 2008-02-09 10:04:44 UTC (rev 13441)
@@ -644,6 +644,19 @@
* identity key. */
#define REND_INTRO_POINT_ID_LEN_BASE32 32
+/** Length of the descriptor cookie that is used for client authorization
+ * to hidden services. */
+#define REND_DESC_COOKIE_LEN 16
+
+/** Length of the base64-encoded descriptor cookie that is used for
+ * exchanging client authorization between hidden service and client. */
+#define REND_DESC_COOKIE_LEN_BASE64 22
+
+/** Legal characters for use in authorized client names for a hidden
+ * service. */
+#define REND_LEGAL_CLIENTNAME_CHARACTERS \
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-_"
+
#define CELL_DIRECTION_IN 1
#define CELL_DIRECTION_OUT 2
@@ -994,6 +1007,14 @@
/** What rendezvous service are we querying for? (AP only) */
char rend_query[REND_SERVICE_ID_LEN_BASE32+1];
+ /** (Optional) descriptor cookie for requesting v2 hidden service
+ * descriptors, decrypting introduction points, and authenticating at
+ * hidden service. */
+ char rend_desc_cookie[REND_DESC_COOKIE_LEN];
+
+ /** True iff this connection contains a descriptor cookie. */
+ int has_desc_cookie:1;
+
/** Number of times we've reassigned this application connection to
* a new circuit. We keep track because the timeout is longer if we've
* already retried several times. */
@@ -1048,6 +1069,14 @@
/** What rendezvous service are we querying for? */
char rend_query[REND_SERVICE_ID_LEN_BASE32+1];
+ /** (Optional) descriptor cookie for requesting v2 hidden service
+ * descriptors, decrypting introduction points, and authenticating at
+ * hidden service. */
+ char rend_desc_cookie[REND_DESC_COOKIE_LEN];
+
+ /** True iff this connection contains a descriptor cookie. */
+ int has_desc_cookie:1;
+
char identity_digest[DIGEST_LEN]; /**< Hash of the public RSA key for
* the directory server's signing key. */
@@ -1840,6 +1869,14 @@
*/
char rend_query[REND_SERVICE_ID_LEN_BASE32+1];
+ /** (Optional) descriptor cookie for requesting v2 hidden service
+ * descriptors, decrypting introduction points, and authenticating at
+ * hidden service. */
+ char rend_desc_cookie[REND_DESC_COOKIE_LEN];
+
+ /** True iff this circuit contains a descriptor cookie. */
+ int has_desc_cookie:1;
+
/** Stores the rendezvous descriptor version if purpose is S_*. Used to
* distinguish introduction and rendezvous points belonging to the same
* rendezvous service ID, but different descriptor versions.
@@ -2186,6 +2223,8 @@
char *TestVia; /**< When reachability testing, use these as middle hop. */
config_line_t *RendConfigLines; /**< List of configuration lines
* for rendezvous services. */
+ config_line_t *ClientSideHidServs; /** List of configuration lines for
+ * client-side authorizations for hidden services */
char *ContactInfo; /**< Contact info to be published in the directory. */
char *HttpProxy; /**< hostname[:port] to use as http proxy, if any. */
@@ -3608,9 +3647,11 @@
int rend_client_introduction_acked(origin_circuit_t *circ, const char *request,
size_t request_len);
void rend_client_refetch_renddesc(const char *query);
-void rend_client_refetch_v2_renddesc(const char *query);
+void rend_client_refetch_v2_renddesc(const char *query,
+ const char *descriptor_cookie);
int rend_client_remove_intro_point(extend_info_t *failed_intro,
- const char *query);
+ const char *query,
+ const char *descriptor_cookie);
int rend_client_rendezvous_acked(origin_circuit_t *circ, const char *request,
size_t request_len);
int rend_client_receive_rendezvous(origin_circuit_t *circ, const char *request,
@@ -3621,9 +3662,27 @@
int rend_client_send_introduction(origin_circuit_t *introcirc,
origin_circuit_t *rendcirc);
+int rend_parse_client_auth(char *config_line);
/********************************* rendcommon.c ***************************/
+/** Hidden-service-side configuration of client authorization. */
+typedef struct rend_authorized_client_t {
+ char *client_name;
+ char descriptor_cookie[REND_DESC_COOKIE_LEN];
+ crypto_pk_env_t *client_key;
+ smartlist_t *access_history;
+} rend_authorized_client_t;
+
+/** Client-side configuration of authorization for a hidden service. */
+typedef struct rend_service_authorization_t {
+ char *service_name;
+ char descriptor_cookie[REND_DESC_COOKIE_LEN];
+ char onion_address[REND_SERVICE_ID_LEN_BASE32+1+5+1];
+} rend_service_authorization_t;
+
+rend_service_authorization_t *lookup_client_auth(char *onion_address);
+
/** ASCII-encoded v2 hidden service descriptor. */
typedef struct rend_encoded_v2_service_descriptor_t {
char desc_id[DIGEST_LEN]; /**< Descriptor ID. */
@@ -3691,6 +3750,7 @@
int rend_cache_size(void);
int rend_encode_v2_descriptors(smartlist_t *descs_out,
rend_service_descriptor_t *desc, time_t now,
+ crypto_pk_env_t *client_key,
const char *descriptor_cookie, uint8_t period);
int rend_compute_v2_desc_id(char *desc_id_out, const char *service_id,
const char *descriptor_cookie,
@@ -4048,6 +4108,6 @@
const char *descriptor_cookie,
const char *intro_content,
size_t intro_size);
-
+int rend_parse_client_keys(strmap_t *parsed_clients, const char *str);
#endif
Modified: tor/branches/121-hs-authorization/src/or/rendclient.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/rendclient.c 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/src/or/rendclient.c 2008-02-09 10:04:44 UTC (rev 13441)
@@ -58,7 +58,7 @@
origin_circuit_t *rendcirc)
{
size_t payload_len;
- int r;
+ int r, auth_shift = 0;
char payload[RELAY_PAYLOAD_SIZE];
char tmp[RELAY_PAYLOAD_SIZE];
rend_cache_entry_t *entry;
@@ -117,22 +117,37 @@
}
}
+ /* if version is 3, possibly write authentication data */
+ if (entry->parsed->protocols & (1<<3)) {
+ tmp[0] = 3; /* version 3 of the cell format */
+ auth_shift = 1;
+ if (introcirc->has_desc_cookie) {
+ tmp[1] = 1;
+ set_uint16(tmp+2, htons(REND_DESC_COOKIE_LEN));
+ memcpy(tmp+4, introcirc->rend_desc_cookie, REND_DESC_COOKIE_LEN);
+ auth_shift += 2+REND_DESC_COOKIE_LEN;
+ }
+ } /* if version 2 only write version number */
+ else if (entry->parsed->protocols & (1<<2)) {
+ tmp[0] = 2; /* version 2 of the cell format */
+ }
+
/* write the remaining items into tmp */
- if (entry->parsed->protocols & (1<<2)) {
+ if (entry->parsed->protocols & (1<<3) || entry->parsed->protocols & (1<<2)) {
/* version 2 format */
extend_info_t *extend_info = rendcirc->build_state->chosen_exit;
int klen;
- tmp[0] = 2; /* version 2 of the cell format */
/* nul pads */
- set_uint32(tmp+1, htonl(extend_info->addr));
- set_uint16(tmp+5, htons(extend_info->port));
- memcpy(tmp+7, extend_info->identity_digest, DIGEST_LEN);
- klen = crypto_pk_asn1_encode(extend_info->onion_key, tmp+7+DIGEST_LEN+2,
- sizeof(tmp)-(7+DIGEST_LEN+2));
- set_uint16(tmp+7+DIGEST_LEN, htons(klen));
- memcpy(tmp+7+DIGEST_LEN+2+klen, rendcirc->rend_cookie,
+ set_uint32(tmp+auth_shift+1, htonl(extend_info->addr));
+ set_uint16(tmp+auth_shift+5, htons(extend_info->port));
+ memcpy(tmp+auth_shift+7, extend_info->identity_digest, DIGEST_LEN);
+ klen = crypto_pk_asn1_encode(extend_info->onion_key,
+ tmp+auth_shift+7+DIGEST_LEN+2,
+ sizeof(tmp)-(auth_shift+7+DIGEST_LEN+2));
+ set_uint16(tmp+auth_shift+7+DIGEST_LEN, htons(klen));
+ memcpy(tmp+auth_shift+7+DIGEST_LEN+2+klen, rendcirc->rend_cookie,
REND_COOKIE_LEN);
- dh_offset = 7+DIGEST_LEN+2+klen+REND_COOKIE_LEN;
+ dh_offset = auth_shift+7+DIGEST_LEN+2+klen+REND_COOKIE_LEN;
} else {
/* Version 0. */
strncpy(tmp, rendcirc->build_state->chosen_exit->nickname,
@@ -241,7 +256,9 @@
* If none remain, refetch the service descriptor.
*/
if (rend_client_remove_intro_point(circ->build_state->chosen_exit,
- circ->rend_query) > 0) {
+ circ->rend_query,
+ (circ->has_desc_cookie ?
+ circ->rend_desc_cookie : NULL)) > 0) {
/* There are introduction points left. Re-extend the circuit to
* another intro point and try again. */
extend_info_t *extend_info;
@@ -337,12 +354,15 @@
* descriptor, return 0, and in case of a failure -1. <b>query</b> is only
* passed for pretty log statements. */
static int
-directory_get_from_hs_dir(const char *desc_id, const char *query)
+directory_get_from_hs_dir(const char *desc_id, const char *query,
+ const char *descriptor_cookie)
{
smartlist_t *responsible_dirs = smartlist_create();
routerstatus_t *hs_dir;
char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
time_t now = time(NULL);
+ char payload[REND_SERVICE_ID_LEN_BASE32 + 1 + REND_DESC_COOKIE_LEN];
+ char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
tor_assert(desc_id);
tor_assert(query);
tor_assert(strlen(query) == REND_SERVICE_ID_LEN_BASE32);
@@ -376,16 +396,34 @@
* directory now. */
lookup_last_hid_serv_request(hs_dir, desc_id_base32, now, 1);
- /* Send fetch request. (Pass query as payload to write it to the directory
- * connection so that it can be referred to when the response arrives.) */
+ /* Send fetch request. (Pass query and possibly descriptor cookie
+ * as payload to write it to the directory connection so that it can be
+ * referred to when the response arrives. This is a terrible hack, but
+ * otherwise a lot of functions needed to be refactored just for fetching
+ * v0 and v2 hidden service descriptors) */
+ strlcpy(payload, query, REND_SERVICE_ID_LEN_BASE32+1);
+ if (descriptor_cookie) {
+ payload[REND_SERVICE_ID_LEN_BASE32] = ':';
+ memcpy(payload + REND_SERVICE_ID_LEN_BASE32 + 1, descriptor_cookie,
+ REND_DESC_COOKIE_LEN);
+ if (base64_encode(descriptor_cookie_base64, 3*REND_DESC_COOKIE_LEN_BASE64,
+ descriptor_cookie, REND_DESC_COOKIE_LEN) < 0) {
+ log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
+ return 0;
+ }
+ }
directory_initiate_command_routerstatus(hs_dir,
DIR_PURPOSE_FETCH_RENDDESC_V2,
ROUTER_PURPOSE_GENERAL,
- 1, desc_id_base32, query, 0, 0);
+ 1, desc_id_base32, payload, 0, 0);
log_info(LD_REND, "Sending fetch request for v2 descriptor for "
- "service '%s' with descriptor ID '%s' to hidden "
+ "service '%s' with descriptor ID '%s' and descriptor "
+ "cookie '%s' to hidden "
"service directory '%s' on port %d.",
- query, desc_id_base32, hs_dir->nickname, hs_dir->dir_port);
+ query, desc_id_base32,
+ (descriptor_cookie == NULL ? "NULL" :
+ escaped_safe_str(descriptor_cookie_base64)),
+ hs_dir->nickname, hs_dir->dir_port);
return 1;
}
@@ -414,7 +452,8 @@
* <b>query</b>.
*/
void
-rend_client_refetch_v2_renddesc(const char *query)
+rend_client_refetch_v2_renddesc(const char *query,
+ const char *descriptor_cookie)
{
char descriptor_id[DIGEST_LEN];
int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS];
@@ -439,13 +478,14 @@
int chosen_replica = replicas_left_to_try[rand];
replicas_left_to_try[rand] = replicas_left_to_try[--tries_left];
- if (rend_compute_v2_desc_id(descriptor_id, query, NULL, time(NULL),
- chosen_replica) < 0) {
+ if (rend_compute_v2_desc_id(descriptor_id, query, descriptor_cookie,
+ time(NULL), chosen_replica) < 0) {
log_warn(LD_REND, "Internal error: Computing v2 rendezvous "
"descriptor ID did not succeed.");
return;
}
- if (directory_get_from_hs_dir(descriptor_id, query) != 0)
+ if (directory_get_from_hs_dir(descriptor_id, query,
+ descriptor_cookie) != 0)
return; /* either success or failure, but we're done */
}
/* If we come here, there are no hidden service directories left. */
@@ -462,7 +502,8 @@
* unrecognized, 1 if recognized and some intro points remain.
*/
int
-rend_client_remove_intro_point(extend_info_t *failed_intro, const char *query)
+rend_client_remove_intro_point(extend_info_t *failed_intro, const char *query,
+ const char *descriptor_cookie)
{
int i, r;
rend_cache_entry_t *ent;
@@ -477,9 +518,11 @@
log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.",
escaped_safe_str(query));
/* Fetch both, v0 and v2 rend descriptors in parallel. Use whichever
- * arrives first. */
- rend_client_refetch_v2_renddesc(query);
- rend_client_refetch_renddesc(query);
+ * arrives first. Exception: When using a descriptor cookie, only
+ * fetch v2 descriptors.*/
+ rend_client_refetch_v2_renddesc(query, descriptor_cookie);
+ if (!descriptor_cookie)
+ rend_client_refetch_renddesc(query);
return 0;
}
@@ -498,9 +541,11 @@
"No more intro points remain for %s. Re-fetching descriptor.",
escaped_safe_str(query));
/* Fetch both, v0 and v2 rend descriptors in parallel. Use whichever
- * arrives first. */
- rend_client_refetch_v2_renddesc(query);
- rend_client_refetch_renddesc(query);
+ * arrives first. Exception: When using a descriptor cookie, only
+ * fetch v2 descriptors.*/
+ rend_client_refetch_v2_renddesc(query, descriptor_cookie);
+ if (!descriptor_cookie)
+ rend_client_refetch_renddesc(query);
/* move all pending streams back to renddesc_wait */
while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
@@ -692,3 +737,100 @@
return extend_info_dup(intro->extend_info);
}
+/** Client-side authorizations for hidden services; map of onion address to
+ * rend_service_authorization_t*. */
+static strmap_t *auth_hid_servs = NULL;
+
+/** Look up the client-side authorization for the hidden service with
+ * <b>onion_address</b>. Return NULL if no authorization is available for
+ * that address. */
+rend_service_authorization_t*
+lookup_client_auth(char *onion_address)
+{
+ tor_assert(onion_address);
+ if (!auth_hid_servs) return NULL;
+ return strmap_get(auth_hid_servs, onion_address);
+}
+
+/** Helper: Free storage held by rend_service_authorization_t. */
+static void
+rend_service_authorization_free(rend_service_authorization_t *auth)
+{
+ if (!auth) return;
+ tor_free(auth->service_name);
+ tor_free(auth);
+}
+
+/** Parse <b>config_line</b> as a client-side authorization for a hidden
+ * service and add it to the local map of hidden service authorizations.
+ * Return 1 for success and 0 for failure. */
+int
+rend_parse_client_auth(char *config_line)
+{
+ char *service_name, *onion_address, *descriptor_cookie;
+ char descriptor_cookie_tmp[REND_DESC_COOKIE_LEN+2];
+ smartlist_t *sl = smartlist_create();
+ rend_service_authorization_t *auth = NULL;
+ int res = 0;
+ tor_assert(config_line);
+ smartlist_split_string(sl, config_line, " ",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ if (smartlist_len(sl) != 3) {
+ log_warn(LD_CONFIG, "Configuration line does not consist of "
+ "\"service-name client-key descriptor-cookie\": '%s'",
+ config_line);
+ goto free;
+ }
+ /* Parse service name (rather meant for use in a GUI controller). */
+ service_name = smartlist_get(sl, 0);
+ auth = tor_malloc_zero(sizeof(rend_service_authorization_t));
+ auth->service_name = strdup(smartlist_get(sl, 0));
+ if (auth_hid_servs && strmap_get(auth_hid_servs, auth->service_name)) {
+ log_warn(LD_CONFIG, "Duplicate service name for configuration line: "
+ "'%s'", auth->service_name);
+ goto free;
+ }
+ /* Parse onion address. */
+ onion_address = smartlist_get(sl, 1);
+ if (strlen(onion_address) != 16+1+5 ||
+ strstr(onion_address, ".onion") != onion_address + 16) {
+ log_warn(LD_CONFIG, "Onion address has wrong format: '%s'",
+ onion_address);
+ goto free;
+ }
+ strlcpy(auth->onion_address, onion_address, 16+1);
+ if (!rend_valid_service_id(auth->onion_address)) {
+ log_warn(LD_CONFIG, "Onion address has wrong format: '%s'",
+ onion_address);
+ goto free;
+ }
+ /* Parse descriptor cookie. */
+ descriptor_cookie = smartlist_get(sl, 2);
+ if (strlen(descriptor_cookie) != 22) {
+ log_warn(LD_CONFIG, "Descriptor cookie has wrong length: '%s'",
+ descriptor_cookie);
+ goto free;
+ }
+ if (base64_decode(descriptor_cookie_tmp, REND_DESC_COOKIE_LEN+2,
+ descriptor_cookie, strlen(descriptor_cookie)) < 0) {
+ log_warn(LD_CONFIG, "Decoding descriptor cookie failed: '%s'",
+ descriptor_cookie);
+ goto free;
+ }
+ memcpy(auth->descriptor_cookie, descriptor_cookie_tmp,
+ REND_DESC_COOKIE_LEN);
+ /* Add parsed client authorization to local map. */
+ if (!auth_hid_servs)
+ auth_hid_servs = strmap_new();
+ strmap_set(auth_hid_servs, auth->onion_address, auth);
+ auth = NULL;
+ res = 1;
+ free:
+ if (sl)
+ SMARTLIST_FOREACH(sl, char *, c, tor_free(c););
+ smartlist_free(sl);
+ if (auth)
+ rend_service_authorization_free(auth);
+ return res;
+}
+
Modified: tor/branches/121-hs-authorization/src/or/rendcommon.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/rendcommon.c 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/src/or/rendcommon.c 2008-02-09 10:04:44 UTC (rev 13441)
@@ -308,15 +308,15 @@
}
/** 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>, <b>descriptor_cookie</b>
+ * (may be <b>NULL</b>), 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,
+ crypto_pk_env_t *service_key,
const char *descriptor_cookie, uint8_t period)
{
char service_id[DIGEST_LEN];
@@ -329,7 +329,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. */
@@ -369,7 +369,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 +437,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;
Modified: tor/branches/121-hs-authorization/src/or/rendservice.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/rendservice.c 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/src/or/rendservice.c 2008-02-09 10:04:44 UTC (rev 13441)
@@ -62,8 +62,16 @@
time_t next_upload_time;
int descriptor_version; /**< Rendezvous descriptor version that will be
* published. */
+ smartlist_t *clients; /**< List of rend_authorized_client_t's for
+ * clients that may access our service. */
} rend_service_t;
+/** The event of a client accessing our hidden service. */
+typedef struct client_access_event_t {
+ time_t access_time;
+ char rendezvous_cookie[DIGEST_LEN];
+} client_access_event_t;
+
/** A list of rend_service_t's for services run on this OP.
*/
static smartlist_t *rend_service_list = NULL;
@@ -77,6 +85,23 @@
return smartlist_len(rend_service_list);
}
+/** Helper: free storage held by a single service authorized client entry. */
+static void
+rend_authorized_client_free(void *authorized_client)
+{
+ rend_authorized_client_t *client = authorized_client;
+ if (!authorized_client) return;
+ if (client->access_history) {
+ SMARTLIST_FOREACH(client->access_history,
+ client_access_event_t *, ev, tor_free(ev););
+ smartlist_free(client->access_history);
+ }
+ if (client->client_key)
+ crypto_free_pk_env(client->client_key);
+ tor_free(client->client_name);
+ tor_free(client);
+}
+
/** Release the storage held by <b>service</b>.
*/
static void
@@ -97,6 +122,11 @@
tor_free(service->intro_exclude_nodes);
if (service->desc)
rend_service_descriptor_free(service->desc);
+ if (service->clients) {
+ SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *, c,
+ rend_authorized_client_free(c););
+ smartlist_free(service->clients);
+ }
tor_free(service);
}
@@ -300,6 +330,48 @@
return -1;
}
service->intro_exclude_nodes = tor_strdup(line->value);
+ } else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
+ /* Parse comma-separated list of client names and add a
+ * rend_authorized_client_t for each client to the service's list
+ * of authorized clients. */
+ smartlist_t *client_names = smartlist_create();
+ smartlist_split_string(client_names, line->value, ",",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ SMARTLIST_FOREACH(client_names, char *, client_name, {
+ rend_authorized_client_t *client;
+ size_t len = strlen(client_name);
+ if (len < 1 || len > 19 ||
+ strspn(client_name, REND_LEGAL_CLIENTNAME_CHARACTERS) != len) {
+ log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains an "
+ "illegal client name: '%s'. (Length must be between 1 "
+ "and 19, and valid characters are [A-Za-z0-9+-_].)",
+ client_name);
+ SMARTLIST_FOREACH(client_names, char *, cp, tor_free(cp));
+ smartlist_free(client_names);
+ rend_service_free(service);
+ return -1;
+ }
+ /* Check if client name is duplicate. */
+ if (service->clients) {
+ int found_duplicate = 0;
+ SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *, c, {
+ if (!strcmp(c->client_name, client_name)) {
+ log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains a "
+ "duplicate client name: '%s'. Ignoring.", client_name);
+ found_duplicate = 1;
+ break;
+ }
+ });
+ if (found_duplicate) continue;
+ }
+ client = tor_malloc_zero(sizeof(rend_authorized_client_t));
+ if (!service->clients)
+ service->clients = smartlist_create();
+ client->client_name = strdup(client_name);
+ smartlist_add(service->clients, client);
+ });
+ SMARTLIST_FOREACH(client_names, char *, cp, tor_free(cp));
+ smartlist_free(client_names);
} else {
smartlist_t *versions;
char *version_str;
@@ -361,8 +433,9 @@
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;
+ * protocol 2. So we only support 2, not 0 anymore. */
+ /* And now we also support version 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);
@@ -380,8 +453,9 @@
}
}
-/** Load and/or generate private keys for all hidden services. Return 0 on
- * success, -1 on failure.
+/** Load and/or generate private keys for all hidden services, possibly
+ * including keys for client authorization. Return 0 on success, -1 on
+ * failure.
*/
int
rend_service_load_keys(void)
@@ -431,8 +505,125 @@
return -1;
}
tor_snprintf(buf, sizeof(buf),"%s.onion\n", s->service_id);
- if (write_str_to_file(fname,buf,0)<0)
+ if (write_str_to_file(fname,buf,0)<0) {
+ log_warn(LD_CONFIG, "Could not write onion address to hostname file.");
return -1;
+ }
+
+ /* If client authorization is configured, load or generate keys. */
+ if (s->clients) {
+ char *client_keys_str;
+ strmap_t *parsed_clients = strmap_new();
+ char cfname[512];
+
+ /* Load client keys and descriptor cookies, if available */
+ if (strlcpy(cfname,s->directory,sizeof(cfname)) >= sizeof(cfname) ||
+ strlcat(cfname,PATH_SEPARATOR"client_keys",sizeof(cfname))
+ >= sizeof(cfname)) {
+ log_warn(LD_CONFIG, "Directory name too long to store client keys "
+ "file: \"%s\".", s->directory);
+ return -1;
+ }
+ client_keys_str = read_file_to_str(cfname, RFTS_IGNORE_MISSING, NULL);
+ rend_parse_client_keys(parsed_clients, client_keys_str);
+ tor_free(client_keys_str);
+ log_info(LD_CONFIG, "Parsed %d previously stored client entries.",
+ strmap_size(parsed_clients));
+
+ /* Prepare hostname file. */
+ if (write_str_to_file(fname,
+ "# This hidden service is configured to be accessed only by "
+ "authorized\n# clients. In order to allow your clients to "
+ "access your service, provide\n# them with the onion addresses "
+ "and client keys below (excluding the #\n# character and your "
+ "chosen client name.)\n\n",0)<0) {
+ log_warn(LD_CONFIG, "Could not write initial comment to hostname "
+ "file.");
+ return -1;
+ }
+
+ /* Either use loaded keys for configured clients or generate new
+ * ones if a client is new. */
+ SMARTLIST_FOREACH(s->clients, rend_authorized_client_t *, client, {
+ char desc_cook_out[3*REND_DESC_COOKIE_LEN_BASE64+1];
+ char service_id[16+1];
+ rend_authorized_client_t *parsed;
+ if ((parsed = strmap_get(parsed_clients, client->client_name))) {
+ /* Copy keys from parsed entry. */
+ client->client_key = crypto_pk_dup_key(parsed->client_key);
+ memcpy(client->descriptor_cookie, parsed->descriptor_cookie,
+ REND_DESC_COOKIE_LEN);
+ if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
+ client->descriptor_cookie, REND_DESC_COOKIE_LEN) < 0) {
+ log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
+ strmap_free(parsed_clients, rend_authorized_client_free);
+ return -1;
+ }
+ desc_cook_out[strlen(desc_cook_out)-3] = '\0'; /* Remove == signs
+ and newline. */
+ } else {
+ crypto_pk_env_t *prkey = NULL;
+ char *out;
+ size_t len;
+ char *entry;
+ size_t entry_len;
+ /* Create private key for client. */
+ if (!(prkey = crypto_new_pk_env())) {
+ log_warn(LD_BUG,"Error constructing client key");
+ strmap_free(parsed_clients, rend_authorized_client_free);
+ return -1;
+ }
+ if (crypto_pk_generate_key(prkey)) {
+ log_warn(LD_BUG,"Error generating client key");
+ strmap_free(parsed_clients, rend_authorized_client_free);
+ return -1;
+ }
+ if (crypto_pk_check_key(prkey) <= 0) {
+ log_warn(LD_BUG,"Generated client key seems invalid");
+ crypto_free_pk_env(prkey);
+ strmap_free(parsed_clients, rend_authorized_client_free);
+ return -1;
+ }
+ /* Create descriptor cookie for client. */
+ crypto_rand(client->descriptor_cookie, REND_DESC_COOKIE_LEN);
+ client->client_key = prkey;
+ /* Encode and append keys to client_keys file. */
+ crypto_pk_write_private_key_to_string(prkey, &out, &len);
+ if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
+ client->descriptor_cookie, REND_DESC_COOKIE_LEN) < 0) {
+ log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
+ strmap_free(parsed_clients, rend_authorized_client_free);
+ return -1;
+ }
+ desc_cook_out[strlen(desc_cook_out)-3] = '\0'; /* Remove == signs
+ and newline. */
+ entry_len = 100 + strlen(client->client_name) + len;
+ entry = tor_malloc_zero(entry_len+1000);
+ if (tor_snprintf(entry, entry_len+1000,
+ "client-name %s\n"
+ "descriptor-cookie %s\n"
+ "client-key\n%s",
+ client->client_name,
+ desc_cook_out,
+ out) < 0) {
+ log_warn(LD_BUG, "Could not write client entry.");
+ strmap_free(parsed_clients, rend_authorized_client_free);
+ return -1;
+ }
+ append_bytes_to_file(cfname, entry, strlen(entry), 0);
+ tor_free(entry);
+ }
+ /* Add line to hostname file. */
+ if (rend_get_service_id(client->client_key, service_id)<0) {
+ log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
+ strmap_free(parsed_clients, rend_authorized_client_free);
+ return -1;
+ }
+ tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n", service_id,
+ desc_cook_out, client->client_name);
+ append_bytes_to_file(fname, buf, strlen(buf), 0);
+ });
+ }
}
return 0;
}
@@ -469,6 +660,95 @@
return 0;
}
+/** Check client authorization of a given <b>descriptor_cookie</b> for
+ * <b>service</b>, use <b>rendezvous_cookie</b> to detect replays or
+ * denial of service attacks, and add this request to the access history.
+ * Return 1 for success and 0 for failure. */
+static int
+rend_check_authorization(rend_service_t *service,
+ const char *descriptor_cookie,
+ const char *rendezvous_cookie)
+{
+ rend_authorized_client_t *auth_client = NULL;
+ client_access_event_t *event;
+ time_t now = time(NULL);
+ int num_same_rend_cookie = -1;
+ tor_assert(service);
+ tor_assert(descriptor_cookie);
+ tor_assert(rendezvous_cookie);
+ if (!service->clients) {
+ log_warn(LD_BUG, "Can't check authorization for a service that is not "
+ "configured to perform such.");
+ return 0;
+ }
+
+ /* Look up client authorization by descriptor cookie. */
+ SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *, client, {
+ if (!memcmp(client->descriptor_cookie, descriptor_cookie,
+ REND_DESC_COOKIE_LEN)) {
+ auth_client = client;
+ break;
+ }
+ });
+ if (!auth_client) {
+ char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
+ if (base64_encode(descriptor_cookie_base64,
+ 3*REND_DESC_COOKIE_LEN_BASE64,
+ descriptor_cookie, REND_DESC_COOKIE_LEN) >= 0)
+ log_info(LD_REND, "No authorization found for descriptor cookie '%s'! "
+ "Dropping cell!", descriptor_cookie_base64);
+ else
+ log_info(LD_REND, "No authorization found! Access denied!");
+ return 0;
+ }
+
+ /* Add request to access history, including time and rendezvous cookie. */
+ event = tor_malloc_zero(sizeof(client_access_event_t));
+ event->access_time = now;
+ memcpy(event->rendezvous_cookie, rendezvous_cookie, DIGEST_LEN);
+ if (!auth_client->access_history)
+ auth_client->access_history = smartlist_create();
+ smartlist_add(auth_client->access_history, event);
+
+ /* Iterate over past requests, remove those which are older than one
+ * hour, and count the number of requests with same rendezvous cookie. */
+ SMARTLIST_FOREACH(auth_client->access_history, client_access_event_t *,
+ access, {
+ if (access->access_time + 60 * 60 < now) {
+ tor_free(access);
+ SMARTLIST_DEL_CURRENT(auth_client->access_history, access);
+ } else if (!memcmp(access->rendezvous_cookie, rendezvous_cookie,
+ DIGEST_LEN)) {
+ num_same_rend_cookie++;
+ }
+ });
+
+ /* If the total number of requests (including this request) within the
+ * last hour exceeds 10, drop this request. */
+ if (smartlist_len(auth_client->access_history) > 10) {
+ log_warn(LD_REND, "Client '%s' has exceeded the maximum number of %d "
+ "requests per hour for service '%s'. Is this an attack? "
+ "Access denied!",
+ auth_client->client_name, 10, service->service_id);
+ return 0;
+ }
+
+ /* If the number of requests with the same rendezvous cookie (including
+ * this request) exceeds 3 , drop this request. */
+ if (num_same_rend_cookie >= 3) {
+ log_warn(LD_REND, "Client '%s' has exceeded the maximum number of %d "
+ "requests using the same rendezvous cookie for service '%s'. "
+ "Is this an attack? Access denied!",
+ auth_client->client_name, 3, service->service_id);
+ return 0;
+ }
+
+ /* Allow the request. */
+ log_warn(LD_REND, "Client %s could be identified for service %s.",
+ auth_client->client_name, service->service_id);
+ return 1;
+}
+
/******
* Handle cells
******/
@@ -485,7 +765,7 @@
char buf[RELAY_PAYLOAD_SIZE];
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
rend_service_t *service;
- int r, i;
+ int r, i, auth_shift = 0;
size_t len, keylen;
crypto_dh_env_t *dh = NULL;
origin_circuit_t *launched = NULL;
@@ -496,6 +776,9 @@
int reason = END_CIRC_REASON_TORPROTOCOL;
crypto_pk_env_t *intro_key;
char intro_key_digest[DIGEST_LEN];
+ int auth_type;
+ size_t auth_len;
+ char *auth_data = NULL;
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
@@ -526,7 +809,7 @@
return -1;
}
- /* if descriptor version is 2, use intro key instead of service key. */
+ /* if descriptor version is 2, use intro key instead of permanent key. */
if (circuit->rend_desc_version == 0) {
intro_key = service->private_key;
} else {
@@ -559,33 +842,51 @@
return -1;
}
len = r;
- if (*buf == 2) {
+ if (*buf == 3) {
+ /* Version 3 INTRODUCE2 cell. */
+ auth_shift = 1;
+ auth_type = buf[1];
+ if (auth_type == 1) {
+ auth_len = ntohs(get_uint16(buf+2));
+ if (auth_len != REND_DESC_COOKIE_LEN) {
+ log_warn(LD_PROTOCOL, "Wrong auth data size %d, should be %d.",
+ auth_len, REND_DESC_COOKIE_LEN);
+ return -1;
+ }
+ auth_data = tor_malloc_zero(REND_DESC_COOKIE_LEN);
+ memcpy(auth_data, buf+4, REND_DESC_COOKIE_LEN);
+ auth_shift += 18;
+ }
+ }
+ if (*buf == 2 || *buf == 3) {
/* Version 2 INTRODUCE2 cell. */
int klen;
extend_info = tor_malloc_zero(sizeof(extend_info_t));
- extend_info->addr = ntohl(get_uint32(buf+1));
- extend_info->port = ntohs(get_uint16(buf+5));
- memcpy(extend_info->identity_digest, buf+7, DIGEST_LEN);
+ extend_info->addr = ntohl(get_uint32(buf+auth_shift+1));
+ extend_info->port = ntohs(get_uint16(buf+auth_shift+5));
+ memcpy(extend_info->identity_digest, buf+auth_shift+7, DIGEST_LEN);
extend_info->nickname[0] = '$';
base16_encode(extend_info->nickname+1, sizeof(extend_info->nickname)-1,
extend_info->identity_digest, DIGEST_LEN);
- klen = ntohs(get_uint16(buf+7+DIGEST_LEN));
- if ((int)len != 7+DIGEST_LEN+2+klen+20+128) {
- log_warn(LD_PROTOCOL, "Bad length %u for version 2 INTRODUCE2 cell.",
- (int)len);
+ klen = ntohs(get_uint16(buf+auth_shift+7+DIGEST_LEN));
+ if ((int)len != auth_shift+7+DIGEST_LEN+2+klen+20+128) {
+ log_warn(LD_PROTOCOL, "Bad length %u for version %d INTRODUCE2 cell.",
+ (int)len, *buf);
reason = END_CIRC_REASON_TORPROTOCOL;
goto err;
}
- extend_info->onion_key = crypto_pk_asn1_decode(buf+7+DIGEST_LEN+2, klen);
+ extend_info->onion_key =
+ crypto_pk_asn1_decode(buf+auth_shift+7+DIGEST_LEN+2, klen);
if (!extend_info->onion_key) {
log_warn(LD_PROTOCOL,
- "Error decoding onion key in version 2 INTRODUCE2 cell.");
+ "Error decoding onion key in version %d INTRODUCE2 cell.",
+ *buf);
reason = END_CIRC_REASON_TORPROTOCOL;
goto err;
}
- ptr = buf+7+DIGEST_LEN+2+klen;
- len -= 7+DIGEST_LEN+2+klen;
+ ptr = buf+auth_shift+7+DIGEST_LEN+2+klen;
+ len -= auth_shift+7+DIGEST_LEN+2+klen;
} else {
char *rp_nickname;
size_t nickname_field_len;
@@ -637,6 +938,18 @@
r_cookie = ptr;
base16_encode(hexcookie,9,r_cookie,4);
+ /* If the cell contains authentication data, check authorization. */
+ if (auth_data) {
+ if (service->clients) {
+ if (!rend_check_authorization(service, auth_data, r_cookie)) {
+ reason = END_CIRC_REASON_CONNECTFAILED;
+ goto err;
+ }
+ } else
+ log_info(LD_PROTOCOL, "INTRODUCE2 cell contains authentication data, "
+ "but we don't perform access control. Ignoring.");
+ }
+
/* Try DH handshake... */
dh = crypto_dh_new();
if (!dh || crypto_dh_generate_public(dh)<0) {
@@ -1133,48 +1446,67 @@
if (c && smartlist_len(c->routerstatus_list) > 0) {
int seconds_valid;
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) {
+ int i, j;
+ /* Either upload a single descriptors (including replicas) or one
+ * descriptor for each authorized client. */
+ int num_descs = (service->clients ?
+ smartlist_len(service->clients) : 1);
+ for (j = 0; j < num_descs; j++) {
+ char *descriptor_cookie = NULL;
+ crypto_pk_env_t *service_key = NULL;
+ if (service->clients) {
+ rend_authorized_client_t *client =
+ smartlist_get(service->clients, j);
+ descriptor_cookie = client->descriptor_cookie;
+ service_key = client->client_key;
+ } else {
+ service_key = service->private_key;
+ }
+ /* Encode the current descriptor. */
seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
- now, NULL, 1);
+ now, service_key,
+ descriptor_cookie, 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) {
+ seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
+ now, service_key,
+ descriptor_cookie, 1);
+ if (seconds_valid < 0) {
+ log_warn(LD_BUG, "Internal error: couldn't encode service "
+ "descriptor; not uploading.");
+ smartlist_free(descs);
+ 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);
uploaded = 1;
Modified: tor/branches/121-hs-authorization/src/or/routerparse.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/routerparse.c 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/src/or/routerparse.c 2008-02-09 10:04:44 UTC (rev 13441)
@@ -97,6 +97,10 @@
R_IPO_ONION_KEY,
R_IPO_SERVICE_KEY,
+ C_CLIENT_NAME,
+ C_DESCRIPTOR_COOKIE,
+ C_CLIENT_KEY,
+
_UNRECOGNIZED,
_ERR,
_EOF,
@@ -341,6 +345,15 @@
END_OF_TABLE
};
+/** List of tokens allowed in the (encrypted) list of introduction points of
+ * rendezvous service descriptors */
+static token_rule_t client_keys_token_table[] = {
+ T1_START("client-name", C_CLIENT_NAME, CONCAT_ARGS, NO_OBJ),
+ T1("descriptor-cookie", C_DESCRIPTOR_COOKIE, EQ(1), NO_OBJ),
+ T1("client-key", C_CLIENT_KEY, NO_ARGS, NEED_KEY_1024),
+ END_OF_TABLE
+};
+
static token_rule_t networkstatus_token_table[] = {
T1("network-status-version", K_NETWORK_STATUS_VERSION,
GE(1), NO_OBJ ),
@@ -2797,10 +2810,14 @@
ebuf[sizeof(ebuf)-1] = '\0';
RET_ERR(ebuf);
}
- if (!strcmp(tok->object_type, "RSA PUBLIC KEY")) { /* If it's a key... */
+ if (!strcmp(tok->object_type, "RSA PUBLIC KEY")) { /* If it's a public key */
tok->key = crypto_new_pk_env();
if (crypto_pk_read_public_key_from_string(tok->key, obstart, eol-obstart))
RET_ERR("Couldn't parse public key.");
+ } else if (!strcmp(tok->object_type, "RSA PRIVATE KEY")) { /* private key */
+ tok->key = crypto_new_pk_env();
+ if (crypto_pk_read_private_key_from_string(tok->key, obstart))
+ RET_ERR("Couldn't parse private key.");
} else { /* If it's something else, try to base64-decode it */
int r;
tok->object_body = tor_malloc(next-*s); /* really, this is too much RAM. */
@@ -3405,6 +3422,7 @@
intro_points_encrypted_size);
crypto_free_cipher_env(cipher);
if (unenclen < 0) {
+ log_warn(LD_REND, "Decrypting introduction points failed!");
tor_free(ipos_decrypted);
return -1;
}
@@ -3497,3 +3515,112 @@
return result;
}
+/** Parse the content of a client_key file in <b>ckstr</b> and add
+ * rend_authorized_client_t's for each parsed client to
+ * <b>parsed_clients</b>. Return the number of parsed clients as result
+ * or -1 for failure. */
+int
+rend_parse_client_keys(strmap_t *parsed_clients, const char *ckstr)
+{
+ int result = -1;
+ smartlist_t *tokens;
+ directory_token_t *tok;
+ const char *current_entry = NULL;
+ if (!ckstr)
+ return -1;
+ tokens = smartlist_create();
+ /* Begin parsing with first entry, skipping comments or whitespace at the
+ * beginning. */
+ current_entry = strstr(ckstr, "client-name ");
+ while (!strcmpstart(current_entry, "client-name ")) {
+ rend_authorized_client_t *parsed_entry;
+ size_t len;
+ char descriptor_cookie_base64[REND_DESC_COOKIE_LEN_BASE64+2+1];
+ char descriptor_cookie_tmp[REND_DESC_COOKIE_LEN+2];
+ /* Determine end of string. */
+ const char *eos = strstr(current_entry, "\nclient-name ");
+ if (!eos)
+ eos = current_entry + strlen(current_entry);
+ else
+ eos = eos + 1;
+ /* Free tokens and clear token list. */
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ smartlist_clear(tokens);
+ /* Tokenize string. */
+ if (tokenize_string(current_entry, eos, tokens,
+ client_keys_token_table, 0)) {
+ log_warn(LD_REND, "Error tokenizing client keys file.");
+ goto err;
+ }
+ /* Advance to next entry, if available. */
+ current_entry = eos;
+ /* Check minimum allowed length of token list. */
+ if (smartlist_len(tokens) < 3) {
+ log_warn(LD_REND, "Impossibly short client key entry.");
+ goto err;
+ }
+ /* Parse client name. */
+ tok = find_first_by_keyword(tokens, C_CLIENT_NAME);
+ tor_assert(tok);
+ tor_assert(tok == smartlist_get(tokens, 0));
+ tor_assert(tok->n_args == 1);
+
+ len = strlen(tok->args[0]);
+ if (len < 1 || len > 19 ||
+ strspn(tok->args[0], REND_LEGAL_CLIENTNAME_CHARACTERS) != len) {
+ log_warn(LD_CONFIG, "Illegal client name: %s. (Length must be "
+ "between 1 and 19, and valid characters are "
+ "[A-Za-z0-9+-_].)", tok->args[0]);
+ goto err;
+ }
+ /* Check if client name is duplicate. */
+ if (strmap_get(parsed_clients, tok->args[0])) {
+ log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains a "
+ "duplicate client name: '%s'. Ignoring.", tok->args[0]);
+ goto err;
+ }
+ parsed_entry = tor_malloc_zero(sizeof(rend_authorized_client_t));
+ parsed_entry->client_name = strdup(tok->args[0]);
+ strmap_set(parsed_clients, parsed_entry->client_name, parsed_entry);
+ /* Parse client key. */
+ tok = find_first_by_keyword(tokens, C_CLIENT_KEY);
+ parsed_entry->client_key = tok->key;
+ tok->key = NULL; /* Prevent free */
+
+ /* Parse descriptor cookie. */
+ tok = find_first_by_keyword(tokens, C_DESCRIPTOR_COOKIE);
+ tor_assert(tok);
+ tor_assert(tok->n_args == 1);
+ if (strlen(tok->args[0]) != REND_DESC_COOKIE_LEN_BASE64) {
+ log_warn(LD_REND, "Descriptor cookie has illegal length: %s",
+ tok->args[0]);
+ goto err;
+ }
+ /* Add trailing == signs to make base64-decoding happy. */
+ tor_snprintf(descriptor_cookie_base64, REND_DESC_COOKIE_LEN_BASE64+2+1,
+ "%s==", tok->args[0]);
+ /* The size of descriptor_cookie_tmp needs to REND_DESC_COOKIE_LEN+2,
+ * because a base64 of length 24 does not fit into 16 bytes in all
+ * cases. */
+ if ((base64_decode(descriptor_cookie_tmp, REND_DESC_COOKIE_LEN+2,
+ descriptor_cookie_base64,
+ REND_DESC_COOKIE_LEN_BASE64+2+1)
+ != REND_DESC_COOKIE_LEN)) {
+ log_warn(LD_REND, "Descriptor cookie contains illegal characters: "
+ "%s", descriptor_cookie_base64);
+ goto err;
+ }
+ memcpy(parsed_entry->descriptor_cookie, descriptor_cookie_tmp,
+ REND_DESC_COOKIE_LEN);
+ }
+ result = strmap_size(parsed_clients);
+ goto done;
+ err:
+ result = -1;
+ done:
+ /* Free tokens and clear token list. */
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ smartlist_free(tokens);
+ return result;
+}
+
Modified: tor/branches/121-hs-authorization/src/or/test.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/test.c 2008-02-09 03:11:10 UTC (rev 13440)
+++ tor/branches/121-hs-authorization/src/or/test.c 2008-02-09 10:04:44 UTC (rev 13441)
@@ -3355,7 +3355,7 @@
smartlist_add(generated->intro_nodes, intro);
}
test_assert(rend_encode_v2_descriptors(descs, generated, now,
- NULL, 0) > 0);
+ NULL, NULL, 0) > 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 *)
More information about the tor-commits
mailing list