control_hs.c (11337B)
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 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 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 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 * 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 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 }