diff --git a/src/or/config.c b/src/or/config.c
index e433b40336..52c7f8b8a4 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -227,6 +227,7 @@ static config_var_t _option_vars[] = {
VAR("HiddenServicePort", LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL),
+ V(HidServAuth, LINELIST, NULL),
V(HSAuthoritativeDir, BOOL, "0"),
V(HSAuthorityRecordStats, BOOL, "0"),
V(HttpProxy, STRING, NULL),
@@ -1191,6 +1192,12 @@ options_act(or_options_t *old_options)
return -1;
}
+ if (running_tor && rend_parse_service_authorization(options, 0) < 0) {
+ log_warn(LD_BUG, "Previously validated client authorization for "
+ "hidden services could not be added!");
+ return -1;
+ }
+
if (running_tor && directory_caches_v2_dir_info(options)) {
len = strlen(options->DataDirectory)+32;
fn = tor_malloc(len);
@@ -3402,6 +3409,11 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (rend_config_services(options, 1) < 0)
REJECT("Failed to configure rendezvous options. See logs for details.");
+ /* Parse client-side authorization for hidden services. */
+ if (rend_parse_service_authorization(options, 1) < 0)
+ REJECT("Failed to configure client authorization for hidden services. "
+ "See logs for details.");
+
if (parse_virtual_addr_network(options->VirtualAddrNetwork, 1, NULL)<0)
return -1;
diff --git a/src/or/main.c b/src/or/main.c
index 8e054f277c..c3f902e776 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -1873,6 +1873,7 @@ tor_free_all(int postfork)
dirserv_free_all();
rend_service_free_all();
rend_cache_free_all();
+ rend_service_authorization_free_all();
rep_hist_free_all();
hs_usage_free_all();
dns_free_all();
diff --git a/src/or/or.h b/src/or/or.h
index 8f9c168316..59b0ec627a 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -612,6 +612,9 @@ typedef enum {
/** Length of 'y' portion of 'y.onion' URL. */
#define REND_SERVICE_ID_LEN_BASE32 16
+/** Length of 'y.onion' including '.onion' URL. */
+#define REND_SERVICE_ADDRESS_LEN (16+1+5)
+
/** Length of a binary-encoded rendezvous service ID. */
#define REND_SERVICE_ID_LEN 10
@@ -2239,6 +2242,8 @@ typedef struct {
* other ORs are running. */
config_line_t *RendConfigLines; /**< List of configuration lines
* for rendezvous services. */
+ config_line_t *HidServAuth; /**< 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. */
@@ -3806,6 +3811,26 @@ extend_info_t *rend_client_get_random_intro(const char *query);
int rend_client_send_introduction(origin_circuit_t *introcirc,
origin_circuit_t *rendcirc);
+/** Client authorization type that a hidden service performs. */
+typedef enum rend_auth_type_t {
+ REND_NO_AUTH = 0,
+ REND_BASIC_AUTH = 1,
+ REND_STEALTH_AUTH = 2,
+} rend_auth_type_t;
+
+/** Client-side configuration of authorization for a hidden service. */
+typedef struct rend_service_authorization_t {
+ char descriptor_cookie[REND_DESC_COOKIE_LEN];
+ char onion_address[REND_SERVICE_ADDRESS_LEN+1];
+ rend_auth_type_t auth_type;
+} rend_service_authorization_t;
+
+int rend_parse_service_authorization(or_options_t *options,
+ int validate_only);
+rend_service_authorization_t *rend_client_lookup_service_authorization(
+ const char *onion_address);
+void rend_service_authorization_free_all(void);
+
/********************************* rendcommon.c ***************************/
/** Hidden-service side configuration of client authorization. */
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index e05fef90e8..24889bbb5f 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -713,3 +713,136 @@ rend_client_get_random_intro(const char *query)
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
+ * onion_address. Return NULL if no authorization is available for
+ * that address. */
+rend_service_authorization_t*
+rend_client_lookup_service_authorization(const 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)
+{
+ tor_free(auth);
+}
+
+/** Helper for strmap_free. */
+static void
+rend_service_authorization_strmap_item_free(void *service_auth)
+{
+ rend_service_authorization_free(service_auth);
+}
+
+/** Release all the storage held in auth_hid_servs.
+ */
+void
+rend_service_authorization_free_all(void)
+{
+ if (!auth_hid_servs) {
+ return;
+ }
+ strmap_free(auth_hid_servs, rend_service_authorization_strmap_item_free);
+ auth_hid_servs = NULL;
+}
+
+/** Parse config_line as a client-side authorization for a hidden
+ * service and add it to the local map of hidden service authorizations.
+ * Return 0 for success and -1 for failure. */
+int
+rend_parse_service_authorization(or_options_t *options, int validate_only)
+{
+ config_line_t *line;
+ int res = -1;
+ strmap_t *parsed = strmap_new();
+ smartlist_t *sl = smartlist_create();
+
+ for (line = options->HidServAuth; line; line = line->next) {
+ char *onion_address, *descriptor_cookie;
+ char descriptor_cookie_tmp[REND_DESC_COOKIE_LEN+2];
+ char descriptor_cookie_base64ext[REND_DESC_COOKIE_LEN_BASE64+2+1];
+ rend_service_authorization_t *auth = NULL;
+ int auth_type_val = 0;
+ SMARTLIST_FOREACH(sl, char *, c, tor_free(c););
+ smartlist_clear(sl);
+ smartlist_split_string(sl, line->value, " ",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3);
+ if (smartlist_len(sl) < 2) {
+ log_warn(LD_CONFIG, "Configuration line does not consist of "
+ "\"onion-address authorization-cookie [service-name]\": "
+ "'%s'", line->value);
+ goto err;
+ }
+ auth = tor_malloc_zero(sizeof(rend_service_authorization_t));
+ /* Parse onion address. */
+ onion_address = smartlist_get(sl, 0);
+ if (strlen(onion_address) != REND_SERVICE_ADDRESS_LEN ||
+ strcmpend(onion_address, ".onion")) {
+ log_warn(LD_CONFIG, "Onion address has wrong format: '%s'",
+ onion_address);
+ goto err;
+ }
+ strlcpy(auth->onion_address, onion_address, REND_SERVICE_ID_LEN_BASE32+1);
+ if (!rend_valid_service_id(auth->onion_address)) {
+ log_warn(LD_CONFIG, "Onion address has wrong format: '%s'",
+ onion_address);
+ goto err;
+ }
+ /* Parse descriptor cookie. */
+ descriptor_cookie = smartlist_get(sl, 1);
+ if (strlen(descriptor_cookie) != REND_DESC_COOKIE_LEN_BASE64) {
+ log_warn(LD_CONFIG, "Authorization cookie has wrong length: '%s'",
+ descriptor_cookie);
+ goto err;
+ }
+ /* Add trailing zero bytes (AA) to make base64-decoding happy. */
+ tor_snprintf(descriptor_cookie_base64ext,
+ REND_DESC_COOKIE_LEN_BASE64+2+1,
+ "%sAA", descriptor_cookie);
+ if (base64_decode(descriptor_cookie_tmp, sizeof(descriptor_cookie_tmp),
+ descriptor_cookie_base64ext,
+ strlen(descriptor_cookie_base64ext)) < 0) {
+ log_warn(LD_CONFIG, "Decoding authorization cookie failed: '%s'",
+ descriptor_cookie);
+ goto err;
+ }
+ auth_type_val = (descriptor_cookie_tmp[16] >> 4) + 1;
+ if (auth_type_val < 1 || auth_type_val > 2) {
+ log_warn(LD_CONFIG, "Authorization cookie has unknown authorization "
+ "type encoded.");
+ goto err;
+ }
+ auth->auth_type = auth_type_val == 1 ? REND_BASIC_AUTH : REND_STEALTH_AUTH;
+ memcpy(auth->descriptor_cookie, descriptor_cookie_tmp,
+ REND_DESC_COOKIE_LEN);
+ if (strmap_get(parsed, auth->onion_address)) {
+ log_warn(LD_CONFIG, "Duplicate authorization for the same hidden "
+ "service.");
+ goto err;
+ }
+ strmap_set(parsed, auth->onion_address, auth);
+ }
+ res = 0;
+ goto done;
+ err:
+ res = -1;
+ done:
+ SMARTLIST_FOREACH(sl, char *, c, tor_free(c););
+ smartlist_free(sl);
+ if (!validate_only && res == 0) {
+ rend_service_authorization_free_all();
+ auth_hid_servs = parsed;
+ } else {
+ strmap_free(parsed, rend_service_authorization_strmap_item_free);
+ }
+ return res;
+}
+
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 23c92f9888..b6b929f6f7 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -40,13 +40,6 @@ typedef struct rend_service_port_config_t {
* rendezvous point before giving up? */
#define MAX_REND_TIMEOUT 30
-/** DOCDOC */
-typedef enum rend_auth_type_t {
- REND_NO_AUTH = 0,
- REND_BASIC_AUTH = 1,
- REND_STEALTH_AUTH = 2,
-} rend_auth_type_t;
-
/** Represents a single hidden service running at this OP. */
typedef struct rend_service_t {
/* Fields specified in config file */