[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r16475: {tor} The first of Karsten's proposal 121 patches: configure and m (in tor/trunk: . src/common src/or)
Author: nickm
Date: 2008-08-08 10:36:11 -0400 (Fri, 08 Aug 2008)
New Revision: 16475
Modified:
tor/trunk/ChangeLog
tor/trunk/src/common/crypto.c
tor/trunk/src/common/crypto.h
tor/trunk/src/or/config.c
tor/trunk/src/or/or.h
tor/trunk/src/or/rendservice.c
tor/trunk/src/or/routerparse.c
Log:
The first of Karsten's proposal 121 patches: configure and maintain client authorization data. Tweaked a bit: see comments on or-dev.
Modified: tor/trunk/ChangeLog
===================================================================
--- tor/trunk/ChangeLog 2008-08-08 12:58:17 UTC (rev 16474)
+++ tor/trunk/ChangeLog 2008-08-08 14:36:11 UTC (rev 16475)
@@ -3,6 +3,9 @@
- Convert many internal address representations to optionally hold
IPv6 addresses.
- Generate and accept IPv6 addresses in many protocol elements.
+ - Begin implementation of proposal 121 (Client authorization for
+ hidden services): associate keys, client lists, and authorization
+ types with hidden services.
o Minor bugfixes:
- Recover 3-7 bytes that were wasted per memory chunk. Fixes bug
Modified: tor/trunk/src/common/crypto.c
===================================================================
--- tor/trunk/src/common/crypto.c 2008-08-08 12:58:17 UTC (rev 16474)
+++ tor/trunk/src/common/crypto.c 2008-08-08 14:36:11 UTC (rev 16475)
@@ -474,17 +474,14 @@
return 0;
}
-/** PEM-encode the public 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_public_key_to_string(crypto_pk_env_t *env, char **dest,
- size_t *len)
+/** Helper function to implement crypto_pk_write_*_key_to_string. */
+static int
+crypto_pk_write_key_to_string_impl(crypto_pk_env_t *env, char **dest,
+ size_t *len, int is_public)
{
BUF_MEM *buf;
BIO *b;
+ int r;
tor_assert(env);
tor_assert(env->key);
@@ -495,8 +492,13 @@
/* 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_RSAPublicKey(b, env->key)) {
- crypto_log_errors(LOG_WARN, "writing public key to string");
+ if (is_public)
+ r = PEM_write_bio_RSAPublicKey(b, env->key);
+ else
+ r = PEM_write_bio_RSAPrivateKey(b, env->key, NULL,NULL,0,NULL,NULL);
+
+ if (!r) {
+ crypto_log_errors(LOG_WARN, "writing RSA key to string");
BIO_free(b);
return -1;
}
@@ -515,6 +517,30 @@
return 0;
}
+/** PEM-encode the public 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_public_key_to_string(crypto_pk_env_t *env, char **dest,
+ size_t *len)
+{
+ return crypto_pk_write_key_to_string_impl(env, dest, len, 1);
+}
+
+/** 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)
+{
+ return crypto_pk_write_key_to_string_impl(env, dest, len, 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.
@@ -593,6 +619,15 @@
return r;
}
+/** Return true iff <b>key</b> contains the private-key portion of the RSA
+ * key. */
+int
+crypto_pk_key_is_private(const crypto_pk_env_t *key)
+{
+ tor_assert(key);
+ return PRIVATE_KEY_OK(key);
+}
+
/** Compare the public-key components of a and b. Return -1 if a\<b, 0
* if a==b, and 1 if a\>b.
*/
Modified: tor/trunk/src/common/crypto.h
===================================================================
--- tor/trunk/src/common/crypto.h 2008-08-08 12:58:17 UTC (rev 16474)
+++ tor/trunk/src/common/crypto.h 2008-08-08 14:36:11 UTC (rev 16475)
@@ -79,8 +79,12 @@
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_read_private_key_from_string(crypto_pk_env_t *env,
+ const char *s);
int crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env,
const char *fname);
@@ -88,6 +92,7 @@
int crypto_pk_cmp_keys(crypto_pk_env_t *a, crypto_pk_env_t *b);
size_t crypto_pk_keysize(crypto_pk_env_t *env);
crypto_pk_env_t *crypto_pk_dup_key(crypto_pk_env_t *orig);
+int crypto_pk_key_is_private(const crypto_pk_env_t *key);
int crypto_pk_public_encrypt(crypto_pk_env_t *env, char *to,
const char *from, size_t fromlen, int padding);
@@ -206,8 +211,6 @@
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*/
-int crypto_pk_read_private_key_from_string(crypto_pk_env_t *env,
- const char *s);
void add_spaces_to_fp(char *out, size_t outlen, const char *in);
#endif
Modified: tor/trunk/src/or/config.c
===================================================================
--- tor/trunk/src/or/config.c 2008-08-08 12:58:17 UTC (rev 16474)
+++ tor/trunk/src/or/config.c 2008-08-08 14:36:11 UTC (rev 16475)
@@ -226,6 +226,7 @@
VAR("HiddenServiceOptions",LINELIST_V, RendConfigLines, NULL),
VAR("HiddenServicePort", LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines, NULL),
+ VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL),
V(HSAuthoritativeDir, BOOL, "0"),
V(HSAuthorityRecordStats, BOOL, "0"),
V(HttpProxy, STRING, NULL),
Modified: tor/trunk/src/or/or.h
===================================================================
--- tor/trunk/src/or/or.h 2008-08-08 12:58:17 UTC (rev 16474)
+++ tor/trunk/src/or/or.h 2008-08-08 14:36:11 UTC (rev 16475)
@@ -640,6 +640,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
@@ -3792,6 +3805,13 @@
/********************************* 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;
+} rend_authorized_client_t;
+
/** ASCII-encoded v2 hidden service descriptor. */
typedef struct rend_encoded_v2_service_descriptor_t {
char desc_id[DIGEST_LEN]; /**< Descriptor ID. */
@@ -4251,6 +4271,7 @@
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/trunk/src/or/rendservice.c
===================================================================
--- tor/trunk/src/or/rendservice.c 2008-08-08 12:58:17 UTC (rev 16474)
+++ tor/trunk/src/or/rendservice.c 2008-08-08 14:36:11 UTC (rev 16475)
@@ -40,6 +40,13 @@
* 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 */
@@ -47,6 +54,10 @@
smartlist_t *ports; /**< List of rend_service_port_config_t */
int descriptor_version; /**< Rendezvous descriptor version that will be
* published. */
+ rend_auth_type_t auth_type; /**< Client authorization type or 0 if no client
+ * authorization is performed. */
+ smartlist_t *clients; /**< List of rend_authorized_client_t's of
+ * clients that may access our service. */
/* Other fields */
crypto_pk_env_t *private_key; /**< Permanent hidden-service key. */
char service_id[REND_SERVICE_ID_LEN_BASE32+1]; /**< Onion address without
@@ -79,6 +90,24 @@
return smartlist_len(rend_service_list);
}
+/** Helper: free storage held by a single service authorized client entry. */
+static void
+rend_authorized_client_free(rend_authorized_client_t *client)
+{
+ if (!client) return;
+ if (client->client_key)
+ crypto_free_pk_env(client->client_key);
+ tor_free(client->client_name);
+ tor_free(client);
+}
+
+/** Helper for strmap_free. */
+static void
+rend_authorized_client_strmap_item_free(void *authorized_client)
+{
+ rend_authorized_client_free(authorized_client);
+}
+
/** Release the storage held by <b>service</b>.
*/
static void
@@ -97,6 +126,11 @@
}
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);
}
@@ -125,24 +159,42 @@
service->intro_nodes = smartlist_create();
/* If the service is configured to publish unversioned (v0) and versioned
- * descriptors (v2 or higher), split it up into two separate services. */
+ * descriptors (v2 or higher), split it up into two separate services
+ * (unless it is configured to perform client authorization). */
if (service->descriptor_version == -1) {
- rend_service_t *v0_service = tor_malloc_zero(sizeof(rend_service_t));
- v0_service->directory = tor_strdup(service->directory);
- v0_service->ports = smartlist_create();
- SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p, {
- rend_service_port_config_t *copy =
- tor_malloc_zero(sizeof(rend_service_port_config_t));
- memcpy(copy, p, sizeof(rend_service_port_config_t));
- smartlist_add(v0_service->ports, copy);
- });
- v0_service->intro_period_started = service->intro_period_started;
- v0_service->descriptor_version = 0; /* Unversioned descriptor. */
- rend_add_service(v0_service);
+ if (service->auth_type == REND_NO_AUTH) {
+ rend_service_t *v0_service = tor_malloc_zero(sizeof(rend_service_t));
+ v0_service->directory = tor_strdup(service->directory);
+ v0_service->ports = smartlist_create();
+ SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p, {
+ rend_service_port_config_t *copy =
+ tor_malloc_zero(sizeof(rend_service_port_config_t));
+ memcpy(copy, p, sizeof(rend_service_port_config_t));
+ smartlist_add(v0_service->ports, copy);
+ });
+ v0_service->intro_period_started = service->intro_period_started;
+ v0_service->descriptor_version = 0; /* Unversioned descriptor. */
+ v0_service->auth_type = REND_NO_AUTH;
+ rend_add_service(v0_service);
+ }
service->descriptor_version = 2; /* Versioned descriptor. */
}
+ if (service->auth_type && !service->descriptor_version) {
+ log_warn(LD_CONFIG, "Hidden service with client authorization and "
+ "version 0 descriptors configured; ignoring.");
+ rend_service_free(service);
+ return;
+ }
+
+ if (service->auth_type && smartlist_len(service->clients) == 0) {
+ log_warn(LD_CONFIG, "Hidden service with client authorization but no "
+ "clients; ignoring.");
+ rend_service_free(service);
+ return;
+ }
+
if (!smartlist_len(service->ports)) {
log_warn(LD_CONFIG, "Hidden service with no ports configured; ignoring.");
rend_service_free(service);
@@ -271,6 +323,130 @@
return -1;
}
smartlist_add(service->ports, portcfg);
+ } else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
+ /* Parse auth type and 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 *type_names_split, *clients;
+ const char *authname;
+ if (service->auth_type) {
+ log_warn(LD_CONFIG, "Got multiple HiddenServiceAuthorizeClient "
+ "lines for a single service.");
+ rend_service_free(service);
+ return -1;
+ }
+ type_names_split = smartlist_create();
+ smartlist_split_string(type_names_split, line->value, " ", 0, 0);
+ if (smartlist_len(type_names_split) < 1) {
+ log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This "
+ "should have been prevented when parsing the "
+ "configuration.");
+ smartlist_free(type_names_split);
+ rend_service_free(service);
+ return -1;
+ }
+ authname = smartlist_get(type_names_split, 0);
+ if (!strcasecmp(authname, "basic") || !strcmp(authname, "1")) {
+ service->auth_type = REND_BASIC_AUTH;
+ } else if (!strcasecmp(authname, "stealth") || !strcmp(authname, "2")) {
+ service->auth_type = REND_STEALTH_AUTH;
+ } else {
+ log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
+ "unrecognized auth-type '%s'. Only 1 or 2 are recognized.",
+ (char *) smartlist_get(type_names_split, 0));
+ SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
+ smartlist_free(type_names_split);
+ rend_service_free(service);
+ return -1;
+ }
+ service->clients = smartlist_create();
+ if (smartlist_len(type_names_split) < 2) {
+ log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
+ "authorization type %d, but no client names.",
+ service->auth_type);
+ SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
+ smartlist_free(type_names_split);
+ continue;
+ }
+ if (smartlist_len(type_names_split) > 2) {
+ log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
+ "illegal value '%s'. Must be formatted "
+ "as 'HiddenServiceAuthorizeClient auth-type "
+ "client-name,client-name,...' (without "
+ "additional spaces in comma-separated client "
+ "list).",
+ line->value);
+ SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
+ smartlist_free(type_names_split);
+ rend_service_free(service);
+ return -1;
+ }
+ clients = smartlist_create();
+ smartlist_split_string(clients, smartlist_get(type_names_split, 1),
+ ",", 0, 0);
+ SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
+ smartlist_free(type_names_split);
+ SMARTLIST_FOREACH_BEGIN(clients, const char *, client_name)
+ {
+ rend_authorized_client_t *client;
+ size_t len = strlen(client_name);
+ int found_duplicate = 0;
+ /* XXXX proposal 121 Why 19? Also, this should be a constant. */
+ if (len < 1 || len > 19) {
+ log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains an "
+ "illegal client name: '%s'. Length must be "
+ "between 1 and 19 characters.",
+ client_name);
+ SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp));
+ smartlist_free(clients);
+ rend_service_free(service);
+ return -1;
+ }
+ if (strspn(client_name, REND_LEGAL_CLIENTNAME_CHARACTERS) != len) {
+ log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains an "
+ "illegal client name: '%s'. Valid "
+ "characters are [A-Za-z0-9+-_].",
+ client_name);
+ SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp));
+ smartlist_free(clients);
+ rend_service_free(service);
+ return -1;
+ }
+ /* Check if client name is duplicate. */
+ /*XXXX proposal 121 This is O(N^2). That's not so good. */
+ 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));
+ client->client_name = tor_strdup(client_name);
+ smartlist_add(service->clients, client);
+ log_debug(LD_REND, "Adding client name '%s'", client_name);
+ }
+ SMARTLIST_FOREACH_END(client_name);
+ SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp));
+ smartlist_free(clients);
+ /* Ensure maximum number of clients. */
+ if ((service->auth_type == REND_BASIC_AUTH &&
+ smartlist_len(service->clients) > 512) ||
+ (service->auth_type == REND_STEALTH_AUTH &&
+ smartlist_len(service->clients) > 16)) {
+ log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d "
+ "client authorization entries, but only a "
+ "maximum of %d entries is allowed for "
+ "authorization type %d.",
+ smartlist_len(service->clients),
+ service->auth_type == REND_BASIC_AUTH ? 512 : 16,
+ (int)service->auth_type);
+ rend_service_free(service);
+ return -1;
+ }
} else {
smartlist_t *versions;
char *version_str;
@@ -351,19 +527,18 @@
}
}
-/** 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)
{
- int i;
- rend_service_t *s;
+ int r;
char fname[512];
- char buf[128];
+ char buf[1500];
- for (i=0; i < smartlist_len(rend_service_list); ++i) {
- s = smartlist_get(rend_service_list,i);
+ SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, s) {
if (s->private_key)
continue;
log_info(LD_REND, "Loading hidden-service keys from \"%s\"",
@@ -402,10 +577,177 @@
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;
- }
- return 0;
+ }
+
+ /* If client authorization is configured, load or generate keys. */
+ if (s->auth_type) {
+ char *client_keys_str = NULL;
+ strmap_t *parsed_clients = strmap_new();
+ char cfname[512];
+ FILE *cfile, *hfile;
+ open_file_t *open_cfile = NULL, *open_hfile = NULL;
+
+ /* Load client keys and descriptor cookies, if available. */
+ if (tor_snprintf(cfname, sizeof(cfname), "%s"PATH_SEPARATOR"client_keys",
+ s->directory)<0) {
+ log_warn(LD_CONFIG, "Directory name too long to store client keys "
+ "file: \"%s\".", s->directory);
+ goto err;
+ }
+ client_keys_str = read_file_to_str(cfname, RFTS_IGNORE_MISSING, NULL);
+ if (client_keys_str) {
+ if (rend_parse_client_keys(parsed_clients, client_keys_str) < 0) {
+ log_warn(LD_CONFIG, "Previously stored client_keys file could not "
+ "be parsed.");
+ goto err;
+ } else {
+ log_info(LD_CONFIG, "Parsed %d previously stored client entries.",
+ strmap_size(parsed_clients));
+ tor_free(client_keys_str);
+ }
+ }
+
+ /* Prepare client_keys and hostname files. */
+ if (!(cfile = start_writing_to_stdio_file(cfname, OPEN_FLAGS_REPLACE,
+ 0600, &open_cfile))) {
+ log_warn(LD_CONFIG, "Could not open client_keys file %s",
+ escaped(cfname));
+ goto err;
+ }
+ if (!(hfile = start_writing_to_stdio_file(fname, OPEN_FLAGS_REPLACE,
+ 0600, &open_hfile))) {
+ log_warn(LD_CONFIG, "Could not open hostname file %s", escaped(fname));
+ goto err;
+ }
+
+ /* Either use loaded keys for configured clients or generate new
+ * ones if a client is new. */
+ SMARTLIST_FOREACH_BEGIN(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 =
+ strmap_get(parsed_clients, client->client_name);
+ int written;
+ size_t len;
+ /* Copy descriptor cookie from parsed entry or create new one. */
+ if (parsed) {
+ memcpy(client->descriptor_cookie, parsed->descriptor_cookie,
+ REND_DESC_COOKIE_LEN);
+ } else {
+ crypto_rand(client->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_strmap_item_free);
+ return -1;
+ }
+ /* Copy client key from parsed entry or create new one if required. */
+ if (parsed && parsed->client_key) {
+ client->client_key = crypto_pk_dup_key(parsed->client_key);
+ } else if (s->auth_type == REND_STEALTH_AUTH) {
+ /* Create private key for client. */
+ crypto_pk_env_t *prkey = NULL;
+ if (!(prkey = crypto_new_pk_env())) {
+ log_warn(LD_BUG,"Error constructing client key");
+ goto err;
+ }
+ if (crypto_pk_generate_key(prkey)) {
+ log_warn(LD_BUG,"Error generating client key");
+ goto err;
+ }
+ if (crypto_pk_check_key(prkey) <= 0) {
+ log_warn(LD_BUG,"Generated client key seems invalid");
+ crypto_free_pk_env(prkey);
+ goto err;
+ }
+ client->client_key = prkey;
+ }
+ /* Add entry to client_keys file. */
+ desc_cook_out[strlen(desc_cook_out)-1] = '\0'; /* Remove newline. */
+ written = tor_snprintf(buf, sizeof(buf),
+ "client-name %s\ndescriptor-cookie %s\n",
+ client->client_name, desc_cook_out);
+ if (written < 0) {
+ log_warn(LD_BUG, "Could not write client entry.");
+ goto err;
+
+ }
+ if (client->client_key) {
+ char *client_key_out;
+ crypto_pk_write_private_key_to_string(client->client_key,
+ &client_key_out, &len);
+ if (rend_get_service_id(client->client_key, service_id)<0) {
+ log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
+ goto err;
+ }
+ written = tor_snprintf(buf + written, sizeof(buf) - written,
+ "client-key\n%s", client_key_out);
+ if (written < 0) {
+ log_warn(LD_BUG, "Could not write client entry.");
+ goto err;
+ }
+ }
+
+ if (fputs(buf, cfile) < 0) {
+ log_warn(LD_FS, "Could not append client entry to file: %s",
+ strerror(errno));
+ goto err;
+ }
+
+ /* Add line to hostname file. */
+ if (s->auth_type == REND_BASIC_AUTH) {
+ /* Remove == signs (newline has been removed above). */
+ desc_cook_out[strlen(desc_cook_out)-2] = '\0';
+ tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n",
+ s->service_id, desc_cook_out, client->client_name);
+ } else {
+ char extended_desc_cookie[REND_DESC_COOKIE_LEN+1];
+ memcpy(extended_desc_cookie, client->descriptor_cookie,
+ REND_DESC_COOKIE_LEN);
+ extended_desc_cookie[REND_DESC_COOKIE_LEN] = (s->auth_type - 1) << 4;
+ if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
+ extended_desc_cookie,
+ REND_DESC_COOKIE_LEN+1) < 0) {
+ log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
+ goto err;
+ }
+ desc_cook_out[strlen(desc_cook_out)-3] = '\0'; /* Remove A= and
+ newline. */
+ tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n",
+ service_id, desc_cook_out, client->client_name);
+ }
+
+ if (fputs(buf, hfile)<0) {
+ log_warn(LD_FS, "Could not append host entry to file: %s",
+ strerror(errno));
+ goto err;
+ }
+ }
+ SMARTLIST_FOREACH_END(client);
+
+ goto done;
+ err:
+ r = -1;
+ done:
+ tor_free(client_keys_str);
+ strmap_free(parsed_clients, rend_authorized_client_strmap_item_free);
+ if (r<0) {
+ abort_writing_to_file(open_cfile);
+ abort_writing_to_file(open_hfile);
+ return r;
+ } else {
+ finish_writing_to_file(open_cfile);
+ finish_writing_to_file(open_hfile);
+ }
+ }
+ } SMARTLIST_FOREACH_END(s);
+ return r;
}
/** Return the service whose public key has a digest of <b>digest</b> and
Modified: tor/trunk/src/or/routerparse.c
===================================================================
--- tor/trunk/src/or/routerparse.c 2008-08-08 12:58:17 UTC (rev 16474)
+++ tor/trunk/src/or/routerparse.c 2008-08-08 14:36:11 UTC (rev 16475)
@@ -100,6 +100,10 @@
R_IPO_ONION_KEY,
R_IPO_SERVICE_KEY,
+ C_CLIENT_NAME,
+ C_DESCRIPTOR_COOKIE,
+ C_CLIENT_KEY,
+
_UNRECOGNIZED,
_ERR,
_EOF,
@@ -141,6 +145,7 @@
typedef enum {
NO_OBJ, /**< No object, ever. */
NEED_OBJ, /**< Object is required. */
+ NEED_SKEY_1024,/**< Object is required, and must be a 1024 bit private key */
NEED_KEY_1024, /**< Object is required, and must be a 1024 bit public key */
NEED_KEY, /**< Object is required, and must be a public key. */
OBJ_OK, /**< Object is optional. */
@@ -352,6 +357,15 @@
END_OF_TABLE
};
+/** List of tokens allowed in the (possibly 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),
+ T01("client-key", C_CLIENT_KEY, NO_ARGS, NEED_SKEY_1024),
+ END_OF_TABLE
+};
+
static token_rule_t networkstatus_token_table[] = {
T1("network-status-version", K_NETWORK_STATUS_VERSION,
GE(1), NO_OBJ ),
@@ -2789,6 +2803,7 @@
}
break;
case NEED_KEY_1024:
+ case NEED_SKEY_1024:
if (tok->key && crypto_pk_keysize(tok->key) != PK_BYTES) {
tor_snprintf(ebuf, sizeof(ebuf), "Wrong size on key for %s: %d bits",
kwd, (int)crypto_pk_keysize(tok->key));
@@ -2799,6 +2814,19 @@
if (!tok->key) {
tor_snprintf(ebuf, sizeof(ebuf), "Missing public key for %s", kwd);
}
+ if (o_syn != NEED_SKEY_1024) {
+ if (crypto_pk_key_is_private(tok->key)) {
+ tor_snprintf(ebuf, sizeof(ebuf),
+ "Private key given for %s, which wants a public key", kwd);
+ RET_ERR(ebuf);
+ }
+ } else { /* o_syn == NEED_SKEY_1024 */
+ if (!crypto_pk_key_is_private(tok->key)) {
+ tor_snprintf(ebuf, sizeof(ebuf),
+ "Public key given for %s, which wants a private key", kwd);
+ RET_ERR(ebuf);
+ }
+ }
break;
case OBJ_OK:
break;
@@ -2948,10 +2976,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 = ALLOC(next-*s); /* really, this is too much RAM. */
@@ -3668,3 +3700,117 @@
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;
+ memarea_t *area = NULL;
+ if (!ckstr || strlen(ckstr) == 0)
+ return -1;
+ tokens = smartlist_create();
+ /* Begin parsing with first entry, skipping comments or whitespace at the
+ * beginning. */
+ area = memarea_new(4096);
+ /* XXXX proposal 121 This skips _everything_, not just comments or
+ * whitespace. That's no good. */
+ 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);
+ memarea_clear(area);
+ /* Tokenize string. */
+ if (tokenize_string(area, 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) < 2) {
+ 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 = tor_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);
+ if (tok) {
+ 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 + 2) {
+ log_warn(LD_REND, "Descriptor cookie has illegal length: %s",
+ escaped(tok->args[0]));
+ goto err;
+ }
+ /* The size of descriptor_cookie_tmp needs to be REND_DESC_COOKIE_LEN+2,
+ * because a base64 encoding of length 24 does not fit into 16 bytes in all
+ * cases. */
+ if ((base64_decode(descriptor_cookie_tmp, REND_DESC_COOKIE_LEN+2,
+ tok->args[0], 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);
+ if (area)
+ memarea_drop_all(area);
+ return result;
+}
+