Line data Source code
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"
13 : #include "feature/control/control_cmd.h"
14 : #include "feature/control/control_hs.h"
15 : #include "feature/control/control_proto.h"
16 : #include "feature/hs/hs_client.h"
17 : #include "lib/encoding/confline.h"
18 :
19 : #include "feature/control/control_cmd_args_st.h"
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. */
25 : static int
26 9 : parse_private_key_from_control_port(const char *client_privkey_str,
27 : curve25519_secret_key_t *privkey,
28 : control_connection_t *conn)
29 : {
30 9 : int retval = -1;
31 9 : smartlist_t *key_args = smartlist_new();
32 :
33 9 : tor_assert(privkey);
34 :
35 9 : smartlist_split_string(key_args, client_privkey_str, ":",
36 : SPLIT_IGNORE_BLANK, 0);
37 9 : if (smartlist_len(key_args) != 2) {
38 0 : control_printf_endreply(conn, 512, "Invalid key type/blob");
39 0 : goto err;
40 : }
41 :
42 9 : const char *key_type = smartlist_get(key_args, 0);
43 9 : const char *key_blob = smartlist_get(key_args, 1);
44 :
45 9 : if (strcasecmp(key_type, "x25519")) {
46 1 : control_printf_endreply(conn, 552,
47 : "Unrecognized key type \"%s\"", key_type);
48 1 : goto err;
49 : }
50 :
51 8 : if (base64_decode((char*)privkey->secret_key, sizeof(privkey->secret_key),
52 : key_blob,
53 : strlen(key_blob)) != sizeof(privkey->secret_key)) {
54 1 : control_printf_endreply(conn, 512, "Failed to decode x25519 private key");
55 1 : goto err;
56 : }
57 :
58 7 : if (fast_mem_is_zero((const char*)privkey->secret_key,
59 : sizeof(privkey->secret_key))) {
60 1 : control_printf_endreply(conn, 553,
61 : "Invalid private key \"%s\"", key_blob);
62 1 : goto err;
63 : }
64 :
65 : retval = 0;
66 :
67 9 : err:
68 27 : SMARTLIST_FOREACH(key_args, char *, c, tor_free(c));
69 9 : smartlist_free(key_args);
70 9 : return retval;
71 : }
72 :
73 : /** Syntax details for ONION_CLIENT_AUTH_ADD */
74 : const control_cmd_syntax_t onion_client_auth_add_syntax = {
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 : */
85 : int
86 10 : handle_control_onion_client_auth_add(control_connection_t *conn,
87 : const control_cmd_args_t *args)
88 : {
89 10 : int retval = -1;
90 10 : smartlist_t *flags = smartlist_new();
91 10 : hs_client_service_authorization_t *creds = NULL;
92 :
93 10 : tor_assert(args);
94 :
95 10 : int argc = smartlist_len(args->args);
96 : /* We need at least 'HSAddress' and 'PrivateKeyBlob' */
97 10 : if (argc < 2) {
98 0 : control_printf_endreply(conn, 512,
99 : "Incomplete ONION_CLIENT_AUTH_ADD command");
100 0 : goto err;
101 : }
102 :
103 10 : creds = tor_malloc_zero(sizeof(hs_client_service_authorization_t));
104 :
105 10 : const char *hsaddress = smartlist_get(args->args, 0);
106 10 : if (!hs_address_is_valid(hsaddress)) {
107 1 : control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress);
108 1 : goto err;
109 : }
110 9 : strlcpy(creds->onion_address, hsaddress, sizeof(creds->onion_address));
111 :
112 : /* Parse the client private key */
113 9 : const char *client_privkey = smartlist_get(args->args, 1);
114 9 : if (parse_private_key_from_control_port(client_privkey,
115 : &creds->enc_seckey, conn) < 0) {
116 3 : goto err;
117 : }
118 :
119 : /* Now let's parse the remaining arguments (variable size) */
120 11 : for (const config_line_t *line = args->kwargs; line; line = line->next) {
121 5 : if (!strcasecmpstart(line->key, "Flags")) {
122 3 : smartlist_split_string(flags, line->value, ",", SPLIT_IGNORE_BLANK, 0);
123 3 : if (smartlist_len(flags) < 1) {
124 0 : control_write_endreply(conn, 512, "Invalid 'Flags' argument");
125 0 : goto err;
126 : }
127 6 : SMARTLIST_FOREACH_BEGIN(flags, const char *, flag) {
128 3 : if (!strcasecmp(flag, "Permanent")) {
129 3 : creds->flags |= CLIENT_AUTH_FLAG_IS_PERMANENT;
130 : } else {
131 0 : control_printf_endreply(conn, 512, "Invalid 'Flags' argument: %s",
132 : escaped(flag));
133 0 : goto err;
134 : }
135 3 : } SMARTLIST_FOREACH_END(flag);
136 : }
137 5 : if (!strcasecmp(line->key, "ClientName")) {
138 1 : if (strlen(line->value) > REND_CLIENTNAME_MAX_LEN) {
139 0 : control_printf_endreply(conn, 512, "ClientName longer than %d chars",
140 : REND_CLIENTNAME_MAX_LEN);
141 : }
142 1 : creds->client_name = tor_strdup(line->value);
143 : }
144 : }
145 :
146 6 : hs_client_register_auth_status_t register_status;
147 : /* Register the credential (register func takes ownership of cred.) */
148 6 : register_status = hs_client_register_auth_credentials(creds);
149 6 : switch (register_status) {
150 0 : case REGISTER_FAIL_BAD_ADDRESS:
151 : /* It's a bug because the service addr has already been validated above */
152 0 : control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"", hsaddress);
153 0 : break;
154 1 : case REGISTER_FAIL_PERMANENT_STORAGE:
155 1 : control_printf_endreply(conn, 553, "Unable to store creds for \"%s\"",
156 : hsaddress);
157 1 : break;
158 1 : case REGISTER_SUCCESS_ALREADY_EXISTS:
159 1 : control_printf_endreply(conn, 251,"Client for onion existed and replaced");
160 1 : break;
161 0 : case REGISTER_SUCCESS_AND_DECRYPTED:
162 0 : control_printf_endreply(conn, 252,"Registered client and decrypted desc");
163 0 : break;
164 4 : case REGISTER_SUCCESS:
165 4 : control_printf_endreply(conn, 250, "OK");
166 4 : break;
167 0 : default:
168 0 : tor_assert_nonfatal_unreached();
169 : }
170 :
171 6 : retval = 0;
172 6 : goto done;
173 :
174 4 : err:
175 4 : client_service_authorization_free(creds);
176 :
177 10 : done:
178 13 : SMARTLIST_FOREACH(flags, char *, s, tor_free(s));
179 10 : smartlist_free(flags);
180 10 : return retval;
181 : }
182 :
183 : /** Syntax details for ONION_CLIENT_AUTH_REMOVE */
184 : const control_cmd_syntax_t onion_client_auth_remove_syntax = {
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 : */
193 : int
194 6 : handle_control_onion_client_auth_remove(control_connection_t *conn,
195 : const control_cmd_args_t *args)
196 : {
197 6 : int retval = -1;
198 :
199 6 : tor_assert(args);
200 :
201 6 : int argc = smartlist_len(args->args);
202 6 : if (argc < 1) {
203 0 : control_printf_endreply(conn, 512,
204 : "Incomplete ONION_CLIENT_AUTH_REMOVE command");
205 0 : goto err;
206 : }
207 :
208 6 : const char *hsaddress = smartlist_get(args->args, 0);
209 6 : if (!hs_address_is_valid(hsaddress)) {
210 1 : control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress);
211 1 : goto err;
212 : }
213 :
214 5 : hs_client_removal_auth_status_t removal_status;
215 5 : removal_status = hs_client_remove_auth_credentials(hsaddress);
216 5 : switch (removal_status) {
217 0 : case REMOVAL_BAD_ADDRESS:
218 : /* It's a bug because the service addr has already been validated above */
219 0 : control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress);
220 0 : break;
221 1 : case REMOVAL_SUCCESS_NOT_FOUND:
222 1 : control_printf_endreply(conn, 251, "No credentials for \"%s\"",hsaddress);
223 1 : break;
224 4 : case REMOVAL_SUCCESS:
225 4 : control_printf_endreply(conn, 250, "OK");
226 4 : break;
227 0 : default:
228 0 : tor_assert_nonfatal_unreached();
229 : }
230 :
231 : retval = 0;
232 :
233 6 : err:
234 6 : return retval;
235 : }
236 :
237 : /** Helper: Return a newly allocated string with the encoding of client
238 : * authorization credentials */
239 : static char *
240 4 : encode_client_auth_cred_for_control_port(
241 : hs_client_service_authorization_t *cred)
242 : {
243 4 : smartlist_t *control_line = smartlist_new();
244 4 : char x25519_b64[128];
245 4 : char *msg_str = NULL;
246 :
247 4 : tor_assert(cred);
248 :
249 4 : if (base64_encode(x25519_b64, sizeof(x25519_b64),
250 4 : (char *)cred->enc_seckey.secret_key,
251 : sizeof(cred->enc_seckey.secret_key), 0) < 0) {
252 0 : tor_assert_nonfatal_unreached();
253 0 : goto err;
254 : }
255 :
256 4 : smartlist_add_asprintf(control_line, "CLIENT %s x25519:%s",
257 4 : cred->onion_address, x25519_b64);
258 :
259 4 : if (cred->flags) { /* flags are also optional */
260 0 : if (cred->flags & CLIENT_AUTH_FLAG_IS_PERMANENT) {
261 0 : smartlist_add_asprintf(control_line, " Flags=Permanent");
262 : }
263 : }
264 :
265 4 : if (cred->client_name) {
266 1 : smartlist_add_asprintf(control_line, " ClientName=%s", cred->client_name);
267 : }
268 :
269 : /* Join all the components into a single string */
270 4 : msg_str = smartlist_join_strings(control_line, "", 0, NULL);
271 :
272 4 : err:
273 9 : SMARTLIST_FOREACH(control_line, char *, cp, tor_free(cp));
274 4 : smartlist_free(control_line);
275 :
276 4 : return msg_str;
277 : }
278 :
279 : /** Syntax details for ONION_CLIENT_AUTH_VIEW */
280 : const control_cmd_syntax_t onion_client_auth_view_syntax = {
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 : */
289 : int
290 4 : handle_control_onion_client_auth_view(control_connection_t *conn,
291 : const control_cmd_args_t *args)
292 : {
293 4 : int retval = -1;
294 4 : 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 4 : smartlist_t *creds_str_list = smartlist_new();
298 :
299 4 : tor_assert(args);
300 :
301 4 : int argc = smartlist_len(args->args);
302 4 : if (argc >= 1) {
303 2 : hsaddress = smartlist_get(args->args, 0);
304 2 : if (!hs_address_is_valid(hsaddress)) {
305 1 : control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",
306 : hsaddress);
307 1 : goto err;
308 : }
309 : }
310 :
311 1 : if (hsaddress) {
312 1 : control_printf_midreply(conn, 250, "ONION_CLIENT_AUTH_VIEW %s", hsaddress);
313 : } else {
314 2 : control_printf_midreply(conn, 250, "ONION_CLIENT_AUTH_VIEW");
315 : }
316 :
317 : /* Create an iterator out of the digest256map */
318 3 : digest256map_t *client_auths = get_hs_client_auths_map();
319 3 : digest256map_iter_t *itr = digest256map_iter_init(client_auths);
320 9 : while (!digest256map_iter_done(itr)) {
321 6 : const uint8_t *service_pubkey;
322 6 : void *valp;
323 6 : digest256map_iter_get(itr, &service_pubkey, &valp);
324 6 : tor_assert(valp);
325 6 : hs_client_service_authorization_t *cred = valp;
326 :
327 : /* If a specific HS address was requested, only print creds for that one */
328 6 : if (hsaddress && strcmp(cred->onion_address, hsaddress)) {
329 2 : itr = digest256map_iter_next(client_auths, itr);
330 2 : continue;
331 : }
332 :
333 4 : char *encoding_str = encode_client_auth_cred_for_control_port(cred);
334 4 : tor_assert_nonfatal(encoding_str);
335 4 : smartlist_add(creds_str_list, encoding_str);
336 :
337 4 : itr = digest256map_iter_next(client_auths, itr);
338 : }
339 :
340 : /* We got everything: Now sort the strings and print them */
341 3 : smartlist_sort_strings(creds_str_list);
342 7 : SMARTLIST_FOREACH_BEGIN(creds_str_list, char *, c) {
343 4 : control_printf_midreply(conn, 250, "%s", c);
344 4 : } SMARTLIST_FOREACH_END(c);
345 :
346 3 : send_control_done(conn);
347 :
348 3 : retval = 0;
349 :
350 4 : err:
351 8 : SMARTLIST_FOREACH(creds_str_list, char *, cp, tor_free(cp));
352 4 : smartlist_free(creds_str_list);
353 4 : return retval;
354 : }
|