Tor 0.4.9.0-alpha-dev
control_hs.c
Go to the documentation of this file.
1/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
2 * Copyright (c) 2019-2021, The Tor Project, Inc. */
3/* See LICENSE for licensing information */
4
5/**
6 * \file control_hs.c
7 *
8 * \brief Implement commands for Tor's control-socket interface that are
9 * related to onion services.
10 **/
11
12#include "core/or/or.h"
14#include "feature/control/control_hs.h"
18
20
21/** Parse the 'KeyType ":" PrivateKey' from <b>client_privkey_str</b> and store
22 * it into <b>privkey</b>. Use <b>conn</b> to output any errors if needed.
23 *
24 * Return 0 if all went well, -1 otherwise. */
25static int
26parse_private_key_from_control_port(const char *client_privkey_str,
29{
30 int retval = -1;
31 smartlist_t *key_args = smartlist_new();
32
33 tor_assert(privkey);
34
35 smartlist_split_string(key_args, client_privkey_str, ":",
36 SPLIT_IGNORE_BLANK, 0);
37 if (smartlist_len(key_args) != 2) {
38 control_printf_endreply(conn, 512, "Invalid key type/blob");
39 goto err;
40 }
41
42 const char *key_type = smartlist_get(key_args, 0);
43 const char *key_blob = smartlist_get(key_args, 1);
44
45 if (strcasecmp(key_type, "x25519")) {
47 "Unrecognized key type \"%s\"", key_type);
48 goto err;
49 }
50
51 if (base64_decode((char*)privkey->secret_key, sizeof(privkey->secret_key),
52 key_blob,
53 strlen(key_blob)) != sizeof(privkey->secret_key)) {
54 control_printf_endreply(conn, 512, "Failed to decode x25519 private key");
55 goto err;
56 }
57
58 if (fast_mem_is_zero((const char*)privkey->secret_key,
59 sizeof(privkey->secret_key))) {
61 "Invalid private key \"%s\"", key_blob);
62 goto err;
63 }
64
65 retval = 0;
66
67 err:
68 SMARTLIST_FOREACH(key_args, char *, c, tor_free(c));
69 smartlist_free(key_args);
70 return retval;
71}
72
73/** Syntax details for ONION_CLIENT_AUTH_ADD */
75 .max_args = 2,
76 .accept_keywords = true,
77};
78
79/** Called when we get an ONION_CLIENT_AUTH_ADD command; parse the body, and
80 * register the new client-side client auth credentials:
81 * "ONION_CLIENT_AUTH_ADD" SP HSAddress
82 * SP KeyType ":" PrivateKeyBlob
83 * [SP "Type=" TYPE] CRLF
84 */
85int
87 const control_cmd_args_t *args)
88{
89 int retval = -1;
90 smartlist_t *flags = smartlist_new();
92
93 tor_assert(args);
94
95 int argc = smartlist_len(args->args);
96 /* We need at least 'HSAddress' and 'PrivateKeyBlob' */
97 if (argc < 2) {
99 "Incomplete ONION_CLIENT_AUTH_ADD command");
100 goto err;
101 }
102
103 creds = tor_malloc_zero(sizeof(hs_client_service_authorization_t));
104
105 const char *hsaddress = smartlist_get(args->args, 0);
106 if (!hs_address_is_valid(hsaddress)) {
107 control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress);
108 goto err;
109 }
110 strlcpy(creds->onion_address, hsaddress, sizeof(creds->onion_address));
111
112 /* Parse the client private key */
113 const char *client_privkey = smartlist_get(args->args, 1);
114 if (parse_private_key_from_control_port(client_privkey,
115 &creds->enc_seckey, conn) < 0) {
116 goto err;
117 }
118
119 /* Now let's parse the remaining arguments (variable size) */
120 for (const config_line_t *line = args->kwargs; line; line = line->next) {
121 if (!strcasecmpstart(line->key, "Flags")) {
122 smartlist_split_string(flags, line->value, ",", SPLIT_IGNORE_BLANK, 0);
123 if (smartlist_len(flags) < 1) {
124 control_write_endreply(conn, 512, "Invalid 'Flags' argument");
125 goto err;
126 }
127 SMARTLIST_FOREACH_BEGIN(flags, const char *, flag) {
128 if (!strcasecmp(flag, "Permanent")) {
129 creds->flags |= CLIENT_AUTH_FLAG_IS_PERMANENT;
130 } else {
131 control_printf_endreply(conn, 512, "Invalid 'Flags' argument: %s",
132 escaped(flag));
133 goto err;
134 }
135 } SMARTLIST_FOREACH_END(flag);
136 }
137 if (!strcasecmp(line->key, "ClientName")) {
138 if (strlen(line->value) > REND_CLIENTNAME_MAX_LEN) {
139 control_printf_endreply(conn, 512, "ClientName longer than %d chars",
141 }
142 creds->client_name = tor_strdup(line->value);
143 }
144 }
145
146 hs_client_register_auth_status_t register_status;
147 /* Register the credential (register func takes ownership of cred.) */
148 register_status = hs_client_register_auth_credentials(creds);
149 switch (register_status) {
150 case REGISTER_FAIL_BAD_ADDRESS:
151 /* It's a bug because the service addr has already been validated above */
152 control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"", hsaddress);
153 break;
154 case REGISTER_FAIL_PERMANENT_STORAGE:
155 control_printf_endreply(conn, 553, "Unable to store creds for \"%s\"",
156 hsaddress);
157 break;
158 case REGISTER_SUCCESS_ALREADY_EXISTS:
159 control_printf_endreply(conn, 251,"Client for onion existed and replaced");
160 break;
161 case REGISTER_SUCCESS_AND_DECRYPTED:
162 control_printf_endreply(conn, 252,"Registered client and decrypted desc");
163 break;
164 case REGISTER_SUCCESS:
165 control_printf_endreply(conn, 250, "OK");
166 break;
167 default:
169 }
170
171 retval = 0;
172 goto done;
173
174 err:
175 client_service_authorization_free(creds);
176
177 done:
178 SMARTLIST_FOREACH(flags, char *, s, tor_free(s));
179 smartlist_free(flags);
180 return retval;
181}
182
183/** Syntax details for ONION_CLIENT_AUTH_REMOVE */
185 .max_args = 1,
186 .accept_keywords = true,
187};
188
189/** Called when we get an ONION_CLIENT_AUTH_REMOVE command; parse the body, and
190 * register the new client-side client auth credentials.
191 * "ONION_CLIENT_AUTH_REMOVE" SP HSAddress
192 */
193int
195 const control_cmd_args_t *args)
196{
197 int retval = -1;
198
199 tor_assert(args);
200
201 int argc = smartlist_len(args->args);
202 if (argc < 1) {
203 control_printf_endreply(conn, 512,
204 "Incomplete ONION_CLIENT_AUTH_REMOVE command");
205 goto err;
206 }
207
208 const char *hsaddress = smartlist_get(args->args, 0);
209 if (!hs_address_is_valid(hsaddress)) {
210 control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress);
211 goto err;
212 }
213
214 hs_client_removal_auth_status_t removal_status;
215 removal_status = hs_client_remove_auth_credentials(hsaddress);
216 switch (removal_status) {
217 case REMOVAL_BAD_ADDRESS:
218 /* It's a bug because the service addr has already been validated above */
219 control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress);
220 break;
221 case REMOVAL_SUCCESS_NOT_FOUND:
222 control_printf_endreply(conn, 251, "No credentials for \"%s\"",hsaddress);
223 break;
224 case REMOVAL_SUCCESS:
225 control_printf_endreply(conn, 250, "OK");
226 break;
227 default:
229 }
230
231 retval = 0;
232
233 err:
234 return retval;
235}
236
237/** Helper: Return a newly allocated string with the encoding of client
238 * authorization credentials */
239static char *
242{
243 smartlist_t *control_line = smartlist_new();
244 char x25519_b64[128];
245 char *msg_str = NULL;
246
247 tor_assert(cred);
248
249 if (base64_encode(x25519_b64, sizeof(x25519_b64),
250 (char *)cred->enc_seckey.secret_key,
251 sizeof(cred->enc_seckey.secret_key), 0) < 0) {
253 goto err;
254 }
255
256 smartlist_add_asprintf(control_line, "CLIENT %s x25519:%s",
257 cred->onion_address, x25519_b64);
258
259 if (cred->flags) { /* flags are also optional */
260 if (cred->flags & CLIENT_AUTH_FLAG_IS_PERMANENT) {
261 smartlist_add_asprintf(control_line, " Flags=Permanent");
262 }
263 }
264
265 if (cred->client_name) {
266 smartlist_add_asprintf(control_line, " ClientName=%s", cred->client_name);
267 }
268
269 /* Join all the components into a single string */
270 msg_str = smartlist_join_strings(control_line, "", 0, NULL);
271
272 err:
273 SMARTLIST_FOREACH(control_line, char *, cp, tor_free(cp));
274 smartlist_free(control_line);
275
276 return msg_str;
277}
278
279/** Syntax details for ONION_CLIENT_AUTH_VIEW */
281 .max_args = 1,
282 .accept_keywords = true,
283};
284
285/** Called when we get an ONION_CLIENT_AUTH_VIEW command; parse the body, and
286 * register the new client-side client auth credentials.
287 * "ONION_CLIENT_AUTH_VIEW" [SP HSAddress] CRLF
288 */
289int
291 const control_cmd_args_t *args)
292{
293 int retval = -1;
294 const char *hsaddress = NULL;
295 /* We are gonna put all the credential strings into a smartlist, and sort it
296 before printing, so that we can get a guaranteed order of printing. */
297 smartlist_t *creds_str_list = smartlist_new();
298
299 tor_assert(args);
300
301 int argc = smartlist_len(args->args);
302 if (argc >= 1) {
303 hsaddress = smartlist_get(args->args, 0);
304 if (!hs_address_is_valid(hsaddress)) {
305 control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",
306 hsaddress);
307 goto err;
308 }
309 }
310
311 if (hsaddress) {
312 control_printf_midreply(conn, 250, "ONION_CLIENT_AUTH_VIEW %s", hsaddress);
313 } else {
314 control_printf_midreply(conn, 250, "ONION_CLIENT_AUTH_VIEW");
315 }
316
317 /* Create an iterator out of the digest256map */
318 digest256map_t *client_auths = get_hs_client_auths_map();
319 digest256map_iter_t *itr = digest256map_iter_init(client_auths);
320 while (!digest256map_iter_done(itr)) {
321 const uint8_t *service_pubkey;
322 void *valp;
323 digest256map_iter_get(itr, &service_pubkey, &valp);
324 tor_assert(valp);
326
327 /* If a specific HS address was requested, only print creds for that one */
328 if (hsaddress && strcmp(cred->onion_address, hsaddress)) {
329 itr = digest256map_iter_next(client_auths, itr);
330 continue;
331 }
332
333 char *encoding_str = encode_client_auth_cred_for_control_port(cred);
334 tor_assert_nonfatal(encoding_str);
335 smartlist_add(creds_str_list, encoding_str);
336
337 itr = digest256map_iter_next(client_auths, itr);
338 }
339
340 /* We got everything: Now sort the strings and print them */
341 smartlist_sort_strings(creds_str_list);
342 SMARTLIST_FOREACH_BEGIN(creds_str_list, char *, c) {
343 control_printf_midreply(conn, 250, "%s", c);
344 } SMARTLIST_FOREACH_END(c);
345
346 send_control_done(conn);
347
348 retval = 0;
349
350 err:
351 SMARTLIST_FOREACH(creds_str_list, char *, cp, tor_free(cp));
352 smartlist_free(creds_str_list);
353 return retval;
354}
int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen)
Definition: binascii.c:396
int base64_encode(char *dest, size_t destlen, const char *src, size_t srclen, int flags)
Definition: binascii.c:215
Header for confline.c.
Header file for control_cmd.c.
Definition for control_cmd_args_t.
int handle_control_onion_client_auth_view(control_connection_t *conn, const control_cmd_args_t *args)
Definition: control_hs.c:290
static int parse_private_key_from_control_port(const char *client_privkey_str, curve25519_secret_key_t *privkey, control_connection_t *conn)
Definition: control_hs.c:26
int handle_control_onion_client_auth_add(control_connection_t *conn, const control_cmd_args_t *args)
Definition: control_hs.c:86
const control_cmd_syntax_t onion_client_auth_add_syntax
Definition: control_hs.c:74
static char * encode_client_auth_cred_for_control_port(hs_client_service_authorization_t *cred)
Definition: control_hs.c:240
const control_cmd_syntax_t onion_client_auth_remove_syntax
Definition: control_hs.c:184
int handle_control_onion_client_auth_remove(control_connection_t *conn, const control_cmd_args_t *args)
Definition: control_hs.c:194
const control_cmd_syntax_t onion_client_auth_view_syntax
Definition: control_hs.c:280
void control_write_endreply(control_connection_t *conn, int code, const char *s)
void control_printf_midreply(control_connection_t *conn, int code, const char *fmt,...)
void send_control_done(control_connection_t *conn)
void control_printf_endreply(control_connection_t *conn, int code, const char *fmt,...)
Header file for control_proto.c.
const char * escaped(const char *s)
Definition: escape.c:126
hs_client_register_auth_status_t hs_client_register_auth_credentials(hs_client_service_authorization_t *creds)
Definition: hs_client.c:1833
static digest256map_t * client_auths
Definition: hs_client.c:61
hs_client_removal_auth_status_t hs_client_remove_auth_credentials(const char *hsaddress)
Definition: hs_client.c:2002
digest256map_t * get_hs_client_auths_map(void)
Definition: hs_client.c:2036
Header file containing client data for the HS subsystem.
#define CLIENT_AUTH_FLAG_IS_PERMANENT
Definition: hs_client.h:63
int hs_address_is_valid(const char *address)
Definition: hs_common.c:856
#define tor_free(p)
Definition: malloc.h:56
Master header file for Tor-specific functionality.
#define REND_CLIENTNAME_MAX_LEN
Definition: or.h:349
void smartlist_add_asprintf(struct smartlist_t *sl, const char *pattern,...)
Definition: smartlist.c:36
void smartlist_sort_strings(smartlist_t *sl)
Definition: smartlist.c:549
char * smartlist_join_strings(smartlist_t *sl, const char *join, int terminate, size_t *len_out)
Definition: smartlist.c:279
smartlist_t * smartlist_new(void)
void smartlist_add(smartlist_t *sl, void *element)
#define SMARTLIST_FOREACH_BEGIN(sl, type, var)
#define SMARTLIST_FOREACH(sl, type, var, cmd)
int smartlist_split_string(smartlist_t *sl, const char *str, const char *sep, int flags, int max)
struct smartlist_t * args
struct config_line_t * kwargs
unsigned int max_args
Definition: control_cmd.h:46
char onion_address[HS_SERVICE_ADDR_LEN_BASE32+1]
Definition: hs_client.h:72
curve25519_secret_key_t enc_seckey
Definition: hs_client.h:69
#define tor_assert_nonfatal_unreached()
Definition: util_bug.h:177
#define tor_assert(expr)
Definition: util_bug.h:103
int strcasecmpstart(const char *s1, const char *s2)
Definition: util_string.c:227
int fast_mem_is_zero(const char *mem, size_t len)
Definition: util_string.c:76