1 /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
2 * Copyright (c) 2019-2021, The Tor Project, Inc. */
3 /* See LICENSE for licensing information */
8 * \brief Implement commands for Tor's control-socket interface that are
9 * related to onion services.
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. */
26 parse_private_key_from_control_port(const char *client_privkey_str
,
27 curve25519_secret_key_t
*privkey
,
28 control_connection_t
*conn
)
31 smartlist_t
*key_args
= smartlist_new();
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");
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
);
51 if (base64_decode((char*)privkey
->secret_key
, sizeof(privkey
->secret_key
),
53 strlen(key_blob
)) != sizeof(privkey
->secret_key
)) {
54 control_printf_endreply(conn
, 512, "Failed to decode x25519 private key");
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
);
68 SMARTLIST_FOREACH(key_args
, char *, c
, tor_free(c
));
69 smartlist_free(key_args
);
73 /** Syntax details for ONION_CLIENT_AUTH_ADD */
74 const control_cmd_syntax_t onion_client_auth_add_syntax
= {
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
86 handle_control_onion_client_auth_add(control_connection_t
*conn
,
87 const control_cmd_args_t
*args
)
90 smartlist_t
*flags
= smartlist_new();
91 hs_client_service_authorization_t
*creds
= NULL
;
95 int argc
= smartlist_len(args
->args
);
96 /* We need at least 'HSAddress' and 'PrivateKeyBlob' */
98 control_printf_endreply(conn
, 512,
99 "Incomplete ONION_CLIENT_AUTH_ADD command");
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
);
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) {
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");
127 SMARTLIST_FOREACH_BEGIN(flags
, const char *, flag
) {
128 if (!strcasecmp(flag
, "Permanent")) {
129 creds
->flags
|= CLIENT_AUTH_FLAG_IS_PERMANENT
;
131 control_printf_endreply(conn
, 512, "Invalid 'Flags' argument: %s",
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
);
154 case REGISTER_FAIL_PERMANENT_STORAGE
:
155 control_printf_endreply(conn
, 553, "Unable to store creds for \"%s\"",
158 case REGISTER_SUCCESS_ALREADY_EXISTS
:
159 control_printf_endreply(conn
, 251,"Client for onion existed and replaced");
161 case REGISTER_SUCCESS_AND_DECRYPTED
:
162 control_printf_endreply(conn
, 252,"Registered client and decrypted desc");
164 case REGISTER_SUCCESS
:
165 control_printf_endreply(conn
, 250, "OK");
168 tor_assert_nonfatal_unreached();
175 client_service_authorization_free(creds
);
178 SMARTLIST_FOREACH(flags
, char *, s
, tor_free(s
));
179 smartlist_free(flags
);
183 /** Syntax details for ONION_CLIENT_AUTH_REMOVE */
184 const control_cmd_syntax_t onion_client_auth_remove_syntax
= {
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
)
201 int argc
= smartlist_len(args
->args
);
203 control_printf_endreply(conn
, 512,
204 "Incomplete ONION_CLIENT_AUTH_REMOVE command");
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
);
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
);
221 case REMOVAL_SUCCESS_NOT_FOUND
:
222 control_printf_endreply(conn
, 251, "No credentials for \"%s\"",hsaddress
);
224 case REMOVAL_SUCCESS
:
225 control_printf_endreply(conn
, 250, "OK");
228 tor_assert_nonfatal_unreached();
237 /** Helper: Return a newly allocated string with the encoding of client
238 * authorization credentials */
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
;
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();
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
);
273 SMARTLIST_FOREACH(control_line
, char *, cp
, tor_free(cp
));
274 smartlist_free(control_line
);
279 /** Syntax details for ONION_CLIENT_AUTH_VIEW */
280 const control_cmd_syntax_t onion_client_auth_view_syntax
= {
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
)
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();
301 int argc
= smartlist_len(args
->args
);
303 hsaddress
= smartlist_get(args
->args
, 0);
304 if (!hs_address_is_valid(hsaddress
)) {
305 control_printf_endreply(conn
, 512, "Invalid v3 address \"%s\"",
312 control_printf_midreply(conn
, 250, "ONION_CLIENT_AUTH_VIEW %s", hsaddress
);
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
;
323 digest256map_iter_get(itr
, &service_pubkey
, &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
);
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
);
351 SMARTLIST_FOREACH(creds_str_list
, char *, cp
, tor_free(cp
));
352 smartlist_free(creds_str_list
);