123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
- /* See LICENSE for licensing information */
- /**
- * \file control_auth.c
- * \brief Authentication for Tor's control-socket interface.
- **/
- #include "core/or/or.h"
- #include "app/config/config.h"
- #include "core/mainloop/connection.h"
- #include "feature/control/control.h"
- #include "feature/control/control_cmd.h"
- #include "feature/control/control_auth.h"
- #include "feature/control/control_cmd_args_st.h"
- #include "feature/control/control_connection_st.h"
- #include "feature/control/control_proto.h"
- #include "lib/crypt_ops/crypto_rand.h"
- #include "lib/crypt_ops/crypto_util.h"
- #include "lib/encoding/confline.h"
- #include "lib/encoding/kvline.h"
- #include "lib/encoding/qstring.h"
- #include "lib/crypt_ops/crypto_s2k.h"
- /** If we're using cookie-type authentication, how long should our cookies be?
- */
- #define AUTHENTICATION_COOKIE_LEN 32
- /** If true, we've set authentication_cookie to a secret code and
- * stored it to disk. */
- static int authentication_cookie_is_set = 0;
- /** If authentication_cookie_is_set, a secret cookie that we've stored to disk
- * and which we're using to authenticate controllers. (If the controller can
- * read it off disk, it has permission to connect.) */
- static uint8_t *authentication_cookie = NULL;
- #define SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT \
- "Tor safe cookie authentication server-to-controller hash"
- #define SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT \
- "Tor safe cookie authentication controller-to-server hash"
- #define SAFECOOKIE_SERVER_NONCE_LEN DIGEST256_LEN
- /** Helper: Return a newly allocated string containing a path to the
- * file where we store our authentication cookie. */
- char *
- get_controller_cookie_file_name(void)
- {
- const or_options_t *options = get_options();
- if (options->CookieAuthFile && strlen(options->CookieAuthFile)) {
- return tor_strdup(options->CookieAuthFile);
- } else {
- return get_datadir_fname("control_auth_cookie");
- }
- }
- /* Initialize the cookie-based authentication system of the
- * ControlPort. If <b>enabled</b> is 0, then disable the cookie
- * authentication system. */
- int
- init_control_cookie_authentication(int enabled)
- {
- char *fname = NULL;
- int retval;
- if (!enabled) {
- authentication_cookie_is_set = 0;
- return 0;
- }
- fname = get_controller_cookie_file_name();
- retval = init_cookie_authentication(fname, "", /* no header */
- AUTHENTICATION_COOKIE_LEN,
- get_options()->CookieAuthFileGroupReadable,
- &authentication_cookie,
- &authentication_cookie_is_set);
- tor_free(fname);
- return retval;
- }
- /** Decode the hashed, base64'd passwords stored in <b>passwords</b>.
- * Return a smartlist of acceptable passwords (unterminated strings of
- * length S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN) on success, or NULL on
- * failure.
- */
- smartlist_t *
- decode_hashed_passwords(config_line_t *passwords)
- {
- char decoded[64];
- config_line_t *cl;
- smartlist_t *sl = smartlist_new();
- tor_assert(passwords);
- for (cl = passwords; cl; cl = cl->next) {
- const char *hashed = cl->value;
- if (!strcmpstart(hashed, "16:")) {
- if (base16_decode(decoded, sizeof(decoded), hashed+3, strlen(hashed+3))
- != S2K_RFC2440_SPECIFIER_LEN + DIGEST_LEN
- || strlen(hashed+3) != (S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN)*2) {
- goto err;
- }
- } else {
- if (base64_decode(decoded, sizeof(decoded), hashed, strlen(hashed))
- != S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN) {
- goto err;
- }
- }
- smartlist_add(sl,
- tor_memdup(decoded, S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN));
- }
- return sl;
- err:
- SMARTLIST_FOREACH(sl, char*, cp, tor_free(cp));
- smartlist_free(sl);
- return NULL;
- }
- const control_cmd_syntax_t authchallenge_syntax = {
- .min_args = 1,
- .max_args = 1,
- .accept_keywords=true,
- .kvline_flags=KV_OMIT_KEYS|KV_QUOTED_QSTRING,
- .store_raw_body=true
- };
- /** Called when we get an AUTHCHALLENGE command. */
- int
- handle_control_authchallenge(control_connection_t *conn,
- const control_cmd_args_t *args)
- {
- char *client_nonce;
- size_t client_nonce_len;
- char server_hash[DIGEST256_LEN];
- char server_hash_encoded[HEX_DIGEST256_LEN+1];
- char server_nonce[SAFECOOKIE_SERVER_NONCE_LEN];
- char server_nonce_encoded[(2*SAFECOOKIE_SERVER_NONCE_LEN) + 1];
- if (strcasecmp(smartlist_get(args->args, 0), "SAFECOOKIE")) {
- control_write_endreply(conn, 513,
- "AUTHCHALLENGE only supports SAFECOOKIE "
- "authentication");
- goto fail;
- }
- if (!authentication_cookie_is_set) {
- control_write_endreply(conn, 515, "Cookie authentication is disabled");
- goto fail;
- }
- if (args->kwargs == NULL || args->kwargs->next != NULL) {
- control_write_endreply(conn, 512,
- "Wrong number of arguments for AUTHCHALLENGE");
- goto fail;
- }
- if (strcmp(args->kwargs->key, "")) {
- control_write_endreply(conn, 512,
- "AUTHCHALLENGE does not accept keyword "
- "arguments.");
- goto fail;
- }
- bool contains_quote = strchr(args->raw_body, '\"');
- if (contains_quote) {
- /* The nonce was quoted */
- client_nonce = tor_strdup(args->kwargs->value);
- client_nonce_len = strlen(client_nonce);
- } else {
- /* The nonce was should be in hex. */
- const char *hex_nonce = args->kwargs->value;
- client_nonce_len = strlen(hex_nonce) / 2;
- client_nonce = tor_malloc(client_nonce_len);
- if (base16_decode(client_nonce, client_nonce_len, hex_nonce,
- strlen(hex_nonce)) != (int)client_nonce_len) {
- control_write_endreply(conn, 513, "Invalid base16 client nonce");
- tor_free(client_nonce);
- goto fail;
- }
- }
- crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN);
- /* Now compute and send the server-to-controller response, and the
- * server's nonce. */
- tor_assert(authentication_cookie != NULL);
- {
- size_t tmp_len = (AUTHENTICATION_COOKIE_LEN +
- client_nonce_len +
- SAFECOOKIE_SERVER_NONCE_LEN);
- char *tmp = tor_malloc_zero(tmp_len);
- char *client_hash = tor_malloc_zero(DIGEST256_LEN);
- memcpy(tmp, authentication_cookie, AUTHENTICATION_COOKIE_LEN);
- memcpy(tmp + AUTHENTICATION_COOKIE_LEN, client_nonce, client_nonce_len);
- memcpy(tmp + AUTHENTICATION_COOKIE_LEN + client_nonce_len,
- server_nonce, SAFECOOKIE_SERVER_NONCE_LEN);
- crypto_hmac_sha256(server_hash,
- SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT,
- strlen(SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT),
- tmp,
- tmp_len);
- crypto_hmac_sha256(client_hash,
- SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT,
- strlen(SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT),
- tmp,
- tmp_len);
- conn->safecookie_client_hash = client_hash;
- tor_free(tmp);
- }
- base16_encode(server_hash_encoded, sizeof(server_hash_encoded),
- server_hash, sizeof(server_hash));
- base16_encode(server_nonce_encoded, sizeof(server_nonce_encoded),
- server_nonce, sizeof(server_nonce));
- control_printf_endreply(conn, 250,
- "AUTHCHALLENGE SERVERHASH=%s SERVERNONCE=%s",
- server_hash_encoded,
- server_nonce_encoded);
- tor_free(client_nonce);
- return 0;
- fail:
- connection_mark_for_close(TO_CONN(conn));
- return -1;
- }
- const control_cmd_syntax_t authenticate_syntax = {
- .max_args = 0,
- .accept_keywords=true,
- .kvline_flags=KV_OMIT_KEYS|KV_QUOTED_QSTRING,
- .store_raw_body=true
- };
- /** Called when we get an AUTHENTICATE message. Check whether the
- * authentication is valid, and if so, update the connection's state to
- * OPEN. Reply with DONE or ERROR.
- */
- int
- handle_control_authenticate(control_connection_t *conn,
- const control_cmd_args_t *args)
- {
- bool used_quoted_string = false;
- const or_options_t *options = get_options();
- const char *errstr = "Unknown error";
- char *password;
- size_t password_len;
- int bad_cookie=0, bad_password=0;
- smartlist_t *sl = NULL;
- if (args->kwargs == NULL) {
- password = tor_strdup("");
- password_len = 0;
- } else if (args->kwargs->next) {
- control_write_endreply(conn, 512, "Too many arguments to AUTHENTICATE.");
- connection_mark_for_close(TO_CONN(conn));
- return 0;
- } else if (strcmp(args->kwargs->key, "")) {
- control_write_endreply(conn, 512,
- "AUTHENTICATE does not accept keyword arguments.");
- connection_mark_for_close(TO_CONN(conn));
- return 0;
- } else if (strchr(args->raw_body, '\"')) {
- used_quoted_string = true;
- password = tor_strdup(args->kwargs->value);
- password_len = strlen(password);
- } else {
- const char *hex_passwd = args->kwargs->value;
- password_len = strlen(hex_passwd) / 2;
- password = tor_malloc(password_len+1);
- if (base16_decode(password, password_len+1, hex_passwd, strlen(hex_passwd))
- != (int) password_len) {
- control_write_endreply(conn, 551,
- "Invalid hexadecimal encoding. Maybe you tried a plain text "
- "password? If so, the standard requires that you put it in "
- "double quotes.");
- connection_mark_for_close(TO_CONN(conn));
- tor_free(password);
- return 0;
- }
- }
- if (conn->safecookie_client_hash != NULL) {
- /* The controller has chosen safe cookie authentication; the only
- * acceptable authentication value is the controller-to-server
- * response. */
- tor_assert(authentication_cookie_is_set);
- if (password_len != DIGEST256_LEN) {
- log_warn(LD_CONTROL,
- "Got safe cookie authentication response with wrong length "
- "(%d)", (int)password_len);
- errstr = "Wrong length for safe cookie response.";
- goto err;
- }
- if (tor_memneq(conn->safecookie_client_hash, password, DIGEST256_LEN)) {
- log_warn(LD_CONTROL,
- "Got incorrect safe cookie authentication response");
- errstr = "Safe cookie response did not match expected value.";
- goto err;
- }
- tor_free(conn->safecookie_client_hash);
- goto ok;
- }
- if (!options->CookieAuthentication && !options->HashedControlPassword &&
- !options->HashedControlSessionPassword) {
- /* if Tor doesn't demand any stronger authentication, then
- * the controller can get in with anything. */
- goto ok;
- }
- if (options->CookieAuthentication) {
- int also_password = options->HashedControlPassword != NULL ||
- options->HashedControlSessionPassword != NULL;
- if (password_len != AUTHENTICATION_COOKIE_LEN) {
- if (!also_password) {
- log_warn(LD_CONTROL, "Got authentication cookie with wrong length "
- "(%d)", (int)password_len);
- errstr = "Wrong length on authentication cookie.";
- goto err;
- }
- bad_cookie = 1;
- } else if (tor_memneq(authentication_cookie, password, password_len)) {
- if (!also_password) {
- log_warn(LD_CONTROL, "Got mismatched authentication cookie");
- errstr = "Authentication cookie did not match expected value.";
- goto err;
- }
- bad_cookie = 1;
- } else {
- goto ok;
- }
- }
- if (options->HashedControlPassword ||
- options->HashedControlSessionPassword) {
- int bad = 0;
- smartlist_t *sl_tmp;
- char received[DIGEST_LEN];
- int also_cookie = options->CookieAuthentication;
- sl = smartlist_new();
- if (options->HashedControlPassword) {
- sl_tmp = decode_hashed_passwords(options->HashedControlPassword);
- if (!sl_tmp)
- bad = 1;
- else {
- smartlist_add_all(sl, sl_tmp);
- smartlist_free(sl_tmp);
- }
- }
- if (options->HashedControlSessionPassword) {
- sl_tmp = decode_hashed_passwords(options->HashedControlSessionPassword);
- if (!sl_tmp)
- bad = 1;
- else {
- smartlist_add_all(sl, sl_tmp);
- smartlist_free(sl_tmp);
- }
- }
- if (bad) {
- if (!also_cookie) {
- log_warn(LD_BUG,
- "Couldn't decode HashedControlPassword: invalid base16");
- errstr="Couldn't decode HashedControlPassword value in configuration.";
- goto err;
- }
- bad_password = 1;
- SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
- smartlist_free(sl);
- sl = NULL;
- } else {
- SMARTLIST_FOREACH(sl, char *, expected,
- {
- secret_to_key_rfc2440(received,DIGEST_LEN,
- password,password_len,expected);
- if (tor_memeq(expected + S2K_RFC2440_SPECIFIER_LEN,
- received, DIGEST_LEN))
- goto ok;
- });
- SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
- smartlist_free(sl);
- sl = NULL;
- if (used_quoted_string)
- errstr = "Password did not match HashedControlPassword value from "
- "configuration";
- else
- errstr = "Password did not match HashedControlPassword value from "
- "configuration. Maybe you tried a plain text password? "
- "If so, the standard requires that you put it in double quotes.";
- bad_password = 1;
- if (!also_cookie)
- goto err;
- }
- }
- /** We only get here if both kinds of authentication failed. */
- tor_assert(bad_password && bad_cookie);
- log_warn(LD_CONTROL, "Bad password or authentication cookie on controller.");
- errstr = "Password did not match HashedControlPassword *or* authentication "
- "cookie.";
- err:
- tor_free(password);
- control_printf_endreply(conn, 515, "Authentication failed: %s", errstr);
- connection_mark_for_close(TO_CONN(conn));
- if (sl) { /* clean up */
- SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
- smartlist_free(sl);
- }
- return 0;
- ok:
- log_info(LD_CONTROL, "Authenticated control connection ("TOR_SOCKET_T_FORMAT
- ")", conn->base_.s);
- send_control_done(conn);
- conn->base_.state = CONTROL_CONN_STATE_OPEN;
- tor_free(password);
- if (sl) { /* clean up */
- SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
- smartlist_free(sl);
- }
- return 0;
- }
- void
- control_auth_free_all(void)
- {
- if (authentication_cookie) /* Free the auth cookie */
- tor_free(authentication_cookie);
- authentication_cookie_is_set = 0;
- }
|