summaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
authorJohn Brooks <john.brooks@dereferenced.net>2015-04-13 21:08:31 -0600
committerNick Mathewson <nickm@torproject.org>2016-05-09 14:28:58 -0400
commitdcc11674db53cc89228ec2e8d49f30bdf6c9b8a3 (patch)
tree377e6122eaaa7733f18425d679658be442143228 /src/or
parentd15354c73b08342f9c1d22917c80194380c75e2c (diff)
downloadtor-dcc11674db53cc89228ec2e8d49f30bdf6c9b8a3.tar.gz
tor-dcc11674db53cc89228ec2e8d49f30bdf6c9b8a3.zip
Add client auth for ADD_ONION services
Diffstat (limited to 'src/or')
-rw-r--r--src/or/control.c171
-rw-r--r--src/or/control.h2
2 files changed, 152 insertions, 21 deletions
diff --git a/src/or/control.c b/src/or/control.c
index a215eb8aaa..de18c9eae8 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -3745,14 +3745,18 @@ handle_control_add_onion(control_connection_t *conn,
* the other arguments are malformed.
*/
smartlist_t *port_cfgs = smartlist_new();
+ smartlist_t *auth_clients = NULL;
+ smartlist_t *auth_created_clients = NULL;
int discard_pk = 0;
int detach = 0;
int max_streams = 0;
int max_streams_close_circuit = 0;
+ rend_auth_type_t auth_type = REND_NO_AUTH;
for (size_t i = 1; i < arg_len; i++) {
static const char *port_prefix = "Port=";
static const char *flags_prefix = "Flags=";
static const char *max_s_prefix = "MaxStreams=";
+ static const char *auth_prefix = "ClientAuth=";
const char *arg = smartlist_get(args, i);
if (!strcasecmpstart(arg, port_prefix)) {
@@ -3783,10 +3787,12 @@ handle_control_add_onion(control_connection_t *conn,
* connection.
* * 'MaxStreamsCloseCircuit' - Close the circuit if MaxStreams is
* exceeded.
+ * * 'BasicAuth' - Client authorization using the 'basic' method.
*/
static const char *discard_flag = "DiscardPK";
static const char *detach_flag = "Detach";
static const char *max_s_close_flag = "MaxStreamsCloseCircuit";
+ static const char *basicauth_flag = "BasicAuth";
smartlist_t *flags = smartlist_new();
int bad = 0;
@@ -3805,6 +3811,8 @@ handle_control_add_onion(control_connection_t *conn,
detach = 1;
} else if (!strcasecmp(flag, max_s_close_flag)) {
max_streams_close_circuit = 1;
+ } else if (!strcasecmp(flag, basicauth_flag)) {
+ auth_type = REND_BASIC_AUTH;
} else {
connection_printf_to_buf(conn,
"512 Invalid 'Flags' argument: %s\r\n",
@@ -3817,6 +3825,42 @@ handle_control_add_onion(control_connection_t *conn,
smartlist_free(flags);
if (bad)
goto out;
+ } else if (!strcasecmpstart(arg, auth_prefix)) {
+ char *err_msg = NULL;
+ int created = 0;
+ rend_authorized_client_t *client =
+ add_onion_helper_clientauth(arg + strlen(auth_prefix),
+ &created, &err_msg);
+ if (!client) {
+ if (err_msg) {
+ connection_write_str_to_buf(err_msg, conn);
+ tor_free(err_msg);
+ }
+ goto out;
+ }
+
+ if (auth_clients != NULL) {
+ int bad = 0;
+ SMARTLIST_FOREACH_BEGIN(auth_clients, rend_authorized_client_t *, ac) {
+ if (strcmp(ac->client_name, client->client_name) == 0) {
+ bad = 1;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(ac);
+ if (bad) {
+ connection_printf_to_buf(conn,
+ "512 Duplicate name in ClientAuth\r\n");
+ rend_authorized_client_free(client);
+ goto out;
+ }
+ } else {
+ auth_clients = smartlist_new();
+ auth_created_clients = smartlist_new();
+ }
+ smartlist_add(auth_clients, client);
+ if (created) {
+ smartlist_add(auth_created_clients, client);
+ }
} else {
connection_printf_to_buf(conn, "513 Invalid argument\r\n");
goto out;
@@ -3825,6 +3869,18 @@ handle_control_add_onion(control_connection_t *conn,
if (smartlist_len(port_cfgs) == 0) {
connection_printf_to_buf(conn, "512 Missing 'Port' argument\r\n");
goto out;
+ } else if (auth_type == REND_NO_AUTH && auth_clients != NULL) {
+ connection_printf_to_buf(conn, "512 No auth type specified\r\n");
+ goto out;
+ } else if (auth_type != REND_NO_AUTH && auth_clients == NULL) {
+ connection_printf_to_buf(conn, "512 No auth clients specified\r\n");
+ goto out;
+ } else if ((auth_type == REND_BASIC_AUTH &&
+ smartlist_len(auth_clients) > 512) ||
+ (auth_type == REND_STEALTH_AUTH &&
+ smartlist_len(auth_clients) > 16)) {
+ connection_printf_to_buf(conn, "512 Too many auth clients\r\n");
+ goto out;
}
/* Parse the "keytype:keyblob" argument. */
@@ -3853,29 +3909,13 @@ handle_control_add_onion(control_connection_t *conn,
char *service_id = NULL;
int ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams,
max_streams_close_circuit,
- REND_NO_AUTH, NULL,
+ auth_type, auth_clients,
&service_id);
port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */
+ auth_clients = NULL; /* so is auth_clients */
switch (ret) {
case RSAE_OKAY:
{
- char *buf = NULL;
- tor_assert(service_id);
- if (key_new_alg) {
- tor_assert(key_new_blob);
- tor_asprintf(&buf,
- "250-ServiceID=%s\r\n"
- "250-PrivateKey=%s:%s\r\n"
- "250 OK\r\n",
- service_id,
- key_new_alg,
- key_new_blob);
- } else {
- tor_asprintf(&buf,
- "250-ServiceID=%s\r\n"
- "250 OK\r\n",
- service_id);
- }
if (detach) {
if (!detached_onion_services)
detached_onion_services = smartlist_new();
@@ -3886,9 +3926,26 @@ handle_control_add_onion(control_connection_t *conn,
smartlist_add(conn->ephemeral_onion_services, service_id);
}
- connection_write_str_to_buf(buf, conn);
- memwipe(buf, 0, strlen(buf));
- tor_free(buf);
+ tor_assert(service_id);
+ connection_printf_to_buf(conn, "250-ServiceID=%s\r\n", service_id);
+ if (key_new_alg) {
+ tor_assert(key_new_blob);
+ connection_printf_to_buf(conn, "250-PrivateKey=%s:%s\r\n",
+ key_new_alg, key_new_blob);
+ }
+ if (auth_created_clients) {
+ SMARTLIST_FOREACH(auth_created_clients, rend_authorized_client_t *, ac, {
+ char *encoded = rend_auth_encode_cookie(ac->descriptor_cookie,
+ auth_type);
+ tor_assert(encoded);
+ connection_printf_to_buf(conn, "250-ClientAuth=%s:%s\r\n",
+ ac->client_name, encoded);
+ memwipe(encoded, 0, strlen(encoded));
+ tor_free(encoded);
+ });
+ }
+
+ connection_printf_to_buf(conn, "250 OK\r\n");
break;
}
case RSAE_BADPRIVKEY:
@@ -3900,6 +3957,9 @@ handle_control_add_onion(control_connection_t *conn,
case RSAE_BADVIRTPORT:
connection_printf_to_buf(conn, "512 Invalid VIRTPORT/TARGET\r\n");
break;
+ case RSAE_BADAUTH:
+ connection_printf_to_buf(conn, "512 Invalid client authorization\r\n");
+ break;
case RSAE_INTERNAL: /* FALLSTHROUGH */
default:
connection_printf_to_buf(conn, "551 Failed to add Onion Service\r\n");
@@ -3916,6 +3976,16 @@ handle_control_add_onion(control_connection_t *conn,
smartlist_free(port_cfgs);
}
+ if (auth_clients) {
+ SMARTLIST_FOREACH(auth_clients, rend_authorized_client_t *, ac,
+ rend_authorized_client_free(ac));
+ smartlist_free(auth_clients);
+ }
+ if (auth_created_clients) {
+ // Do not free entries; they are the same as auth_clients
+ smartlist_free(auth_created_clients);
+ }
+
SMARTLIST_FOREACH(args, char *, cp, {
memwipe(cp, 0, strlen(cp));
tor_free(cp);
@@ -4024,6 +4094,65 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
return pk;
}
+/** Helper function to handle parsing a ClientAuth argument to the
+ * ADD_ONION command. Return a new rend_authorized_client_t, or NULL
+ * and an optional control protocol error message on failure. The
+ * caller is responsible for freeing the returned auth_client and err_msg.
+ *
+ * If 'created' is specified, it will be set to 1 when a new cookie has
+ * been generated.
+ */
+STATIC rend_authorized_client_t *
+add_onion_helper_clientauth(const char *arg, int *created, char **err_msg)
+{
+ int ok = 0;
+
+ tor_assert(arg);
+ tor_assert(created);
+ tor_assert(err_msg);
+ *err_msg = NULL;
+
+ smartlist_t *auth_args = smartlist_new();
+ rend_authorized_client_t *client =
+ tor_malloc_zero(sizeof(rend_authorized_client_t));
+ smartlist_split_string(auth_args, arg, ":", 0, 0);
+ if (smartlist_len(auth_args) < 1 || smartlist_len(auth_args) > 2) {
+ *err_msg = tor_strdup("512 Invalid ClientAuth syntax\r\n");
+ goto err;
+ }
+ client->client_name = tor_strdup(smartlist_get(auth_args, 0));
+ if (smartlist_len(auth_args) == 2) {
+ char *decode_err_msg = NULL;
+ if (rend_auth_decode_cookie(smartlist_get(auth_args, 1),
+ client->descriptor_cookie,
+ NULL, &decode_err_msg) < 0) {
+ tor_assert(decode_err_msg);
+ tor_asprintf(err_msg, "512 %s\r\n", decode_err_msg);
+ tor_free(decode_err_msg);
+ goto err;
+ }
+ *created = 0;
+ } else {
+ crypto_rand((char *) client->descriptor_cookie, REND_DESC_COOKIE_LEN);
+ *created = 1;
+ }
+
+ if (!rend_valid_client_name(client->client_name)) {
+ *err_msg = tor_strdup("512 Invalid name in ClientAuth\r\n");
+ goto err;
+ }
+
+ ok = 1;
+ err:
+ SMARTLIST_FOREACH(auth_args, char *, arg, tor_free(arg));
+ smartlist_free(auth_args);
+ if (!ok) {
+ rend_authorized_client_free(client);
+ client = NULL;
+ }
+ return client;
+}
+
/** Called when we get a DEL_ONION command; parse the body, and remove
* the existing ephemeral Onion Service. */
static int
diff --git a/src/or/control.h b/src/or/control.h
index fdf7903cb8..9ac8995aac 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -253,6 +253,8 @@ STATIC crypto_pk_t *add_onion_helper_keyarg(const char *arg, int discard_pk,
const char **key_new_alg_out,
char **key_new_blob_out,
char **err_msg_out);
+STATIC rend_authorized_client_t *
+add_onion_helper_clientauth(const char *arg, int *created, char **err_msg_out);
#endif
#endif