Control port STREAM XON/XOFF status event notification
[tor.git] / src / feature / control / control_hs.c
blobd1a5c0a3a95a73cdab0b561e785dfd981b1da0b6
1 /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
2 * Copyright (c) 2019-2021, The Tor Project, Inc. */
3 /* See LICENSE for licensing information */
5 /**
6 * \file control_hs.c
8 * \brief Implement commands for Tor's control-socket interface that are
9 * related to onion services.
10 **/
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"
19 #include "feature/control/control_cmd_args_st.h"
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.
24 * Return 0 if all went well, -1 otherwise. */
25 static int
26 parse_private_key_from_control_port(const char *client_privkey_str,
27 curve25519_secret_key_t *privkey,
28 control_connection_t *conn)
30 int retval = -1;
31 smartlist_t *key_args = smartlist_new();
33 tor_assert(privkey);
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;
42 const char *key_type = smartlist_get(key_args, 0);
43 const char *key_blob = smartlist_get(key_args, 1);
45 if (strcasecmp(key_type, "x25519")) {
46 control_printf_endreply(conn, 552,
47 "Unrecognized key type \"%s\"", key_type);
48 goto err;
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;
58 if (fast_mem_is_zero((const char*)privkey->secret_key,
59 sizeof(privkey->secret_key))) {
60 control_printf_endreply(conn, 553,
61 "Invalid private key \"%s\"", key_blob);
62 goto err;
65 retval = 0;
67 err:
68 SMARTLIST_FOREACH(key_args, char *, c, tor_free(c));
69 smartlist_free(key_args);
70 return retval;
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,
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
85 int
86 handle_control_onion_client_auth_add(control_connection_t *conn,
87 const control_cmd_args_t *args)
89 int retval = -1;
90 smartlist_t *flags = smartlist_new();
91 hs_client_service_authorization_t *creds = NULL;
93 tor_assert(args);
95 int argc = smartlist_len(args->args);
96 /* We need at least 'HSAddress' and 'PrivateKeyBlob' */
97 if (argc < 2) {
98 control_printf_endreply(conn, 512,
99 "Incomplete ONION_CLIENT_AUTH_ADD command");
100 goto err;
103 creds = tor_malloc_zero(sizeof(hs_client_service_authorization_t));
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;
110 strlcpy(creds->onion_address, hsaddress, sizeof(creds->onion_address));
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;
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;
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;
135 } SMARTLIST_FOREACH_END(flag);
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",
140 REND_CLIENTNAME_MAX_LEN);
142 creds->client_name = tor_strdup(line->value);
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:
168 tor_assert_nonfatal_unreached();
171 retval = 0;
172 goto done;
174 err:
175 client_service_authorization_free(creds);
177 done:
178 SMARTLIST_FOREACH(flags, char *, s, tor_free(s));
179 smartlist_free(flags);
180 return retval;
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,
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
194 handle_control_onion_client_auth_remove(control_connection_t *conn,
195 const control_cmd_args_t *args)
197 int retval = -1;
199 tor_assert(args);
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;
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;
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:
228 tor_assert_nonfatal_unreached();
231 retval = 0;
233 err:
234 return retval;
237 /** Helper: Return a newly allocated string with the encoding of client
238 * authorization credentials */
239 static char *
240 encode_client_auth_cred_for_control_port(
241 hs_client_service_authorization_t *cred)
243 smartlist_t *control_line = smartlist_new();
244 char x25519_b64[128];
245 char *msg_str = NULL;
247 tor_assert(cred);
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) {
252 tor_assert_nonfatal_unreached();
253 goto err;
256 smartlist_add_asprintf(control_line, "CLIENT %s x25519:%s",
257 cred->onion_address, x25519_b64);
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");
265 if (cred->client_name) {
266 smartlist_add_asprintf(control_line, " ClientName=%s", cred->client_name);
269 /* Join all the components into a single string */
270 msg_str = smartlist_join_strings(control_line, "", 0, NULL);
272 err:
273 SMARTLIST_FOREACH(control_line, char *, cp, tor_free(cp));
274 smartlist_free(control_line);
276 return msg_str;
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,
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
290 handle_control_onion_client_auth_view(control_connection_t *conn,
291 const control_cmd_args_t *args)
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();
299 tor_assert(args);
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;
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");
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);
325 hs_client_service_authorization_t *cred = valp;
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;
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);
337 itr = digest256map_iter_next(client_auths, itr);
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);
346 send_control_done(conn);
348 retval = 0;
350 err:
351 SMARTLIST_FOREACH(creds_str_list, char *, cp, tor_free(cp));
352 smartlist_free(creds_str_list);
353 return retval;