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
parse_private_key_from_control_port(const char * client_privkey_str,curve25519_secret_key_t * privkey,control_connection_t * conn)26 parse_private_key_from_control_port(const char *client_privkey_str,
27                                     curve25519_secret_key_t *privkey,
28                                     control_connection_t *conn)
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")) {
46     control_printf_endreply(conn, 552,
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))) {
60     control_printf_endreply(conn, 553,
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 */
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
handle_control_onion_client_auth_add(control_connection_t * conn,const control_cmd_args_t * args)86 handle_control_onion_client_auth_add(control_connection_t *conn,
87                                      const control_cmd_args_t *args)
88 {
89   int retval = -1;
90   smartlist_t *flags = smartlist_new();
91   hs_client_service_authorization_t *creds = NULL;
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) {
98     control_printf_endreply(conn, 512,
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",
140                                 REND_CLIENTNAME_MAX_LEN);
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:
168     tor_assert_nonfatal_unreached();
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 */
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
handle_control_onion_client_auth_remove(control_connection_t * conn,const control_cmd_args_t * args)194 handle_control_onion_client_auth_remove(control_connection_t *conn,
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:
228     tor_assert_nonfatal_unreached();
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 */
239 static char *
encode_client_auth_cred_for_control_port(hs_client_service_authorization_t * cred)240 encode_client_auth_cred_for_control_port(
241                                        hs_client_service_authorization_t *cred)
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) {
252     tor_assert_nonfatal_unreached();
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 */
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
handle_control_onion_client_auth_view(control_connection_t * conn,const control_cmd_args_t * args)290 handle_control_onion_client_auth_view(control_connection_t *conn,
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);
325     hs_client_service_authorization_t *cred = 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 }
355