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 */