mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2025-02-22 14:23:04 +01:00
r14600@catbus: nickm | 2007-08-16 13:30:22 -0400
Implement proposal 119. Backport candidate. svn:r11138
This commit is contained in:
parent
b89efa7705
commit
f4398feadb
6 changed files with 169 additions and 10 deletions
|
@ -26,6 +26,12 @@ Changes in version 0.2.0.5-alpha - 2007-??-??
|
|||
- Read v3 keys from the right location.
|
||||
- Numerous bugfixes to directory voting code.
|
||||
|
||||
o Minor features (controller):
|
||||
- Add a PROTOCOLINFO controller command. Like AUTHENTICATE, it is valid
|
||||
before any authentication has been received. It tells a controller
|
||||
what kind of authentication is expected, and what protocol is spoken.
|
||||
Implements proposal 119.
|
||||
|
||||
o Minor bugfixes (other):
|
||||
- If we require CookieAuthentication but we fail to write the
|
||||
cookie file, we would warn but not exit, and end up in a state
|
||||
|
|
|
@ -231,9 +231,11 @@ $Id$
|
|||
The format of the 'cookie' is implementation-dependent; see 5.1 below for
|
||||
information on how the standard Tor implementation handles it.
|
||||
|
||||
If Tor requires authentication and the controller has not yet sent an
|
||||
AUTHENTICATE message, Tor sends a "514 authentication required" reply to
|
||||
any other kind of message, and then closes the connection.
|
||||
Before the client has authenticated, no command other than PROTOCOLINFO,
|
||||
AUTHENTICATE, or QUIT is valid. If the controller sends any other command,
|
||||
or sends a malformed command, or sends an unsuccessful AUTHENTICATE
|
||||
command, or sends PROTOCOLINFO more than once, Tor sends an error reply and
|
||||
closes the connection.
|
||||
|
||||
(Versions of Tor before 0.1.2.16 and 0.2.0.4-alpha did not close the
|
||||
connection after an authentication failure.)
|
||||
|
@ -733,6 +735,56 @@ $Id$
|
|||
|
||||
[Added in Tor 0.2.0.3-alpha]
|
||||
|
||||
3.21. PROTOCOLINFO
|
||||
|
||||
The syntax is:
|
||||
"PROTOCOLINFO" *(SP PIVERSION) CRLF
|
||||
|
||||
The server reply format is:
|
||||
"250+PROTOCOLINFO" SP PIVERSION CRLF *InfoLine "250 OK" CRLF
|
||||
|
||||
InfoLine = AuthLine / VersionLine / OtherLine
|
||||
|
||||
AuthLine = "250-AUTH" SP "METHODS=" AuthMethod *(",")AuthMethod
|
||||
*(SP "COOKIEFILE=" AuthCookieFile) CRLF
|
||||
VersionLine = "250-VERSION" SP "Tor=" TorVersion [SP Arguments] CRLF
|
||||
|
||||
AuthMethod =
|
||||
"NULL" / ; No authentication is required
|
||||
"HASHEDPASSWORD" / ; A controller must supply the original password
|
||||
"COOKIE" / ; A controller must supply the contents of a cookie
|
||||
|
||||
AuthCookieFile = QuotedString
|
||||
TorVersion = QuotedString
|
||||
|
||||
OtherLine = "250-" Keyword [SP Arguments] CRLF
|
||||
|
||||
PIVERSION: 1*DIGIT
|
||||
|
||||
Tor MAY give its InfoLines in any order; controllers MUST ignore InfoLines
|
||||
with keywords it does not recognize. Controllers MUST ignore extraneous
|
||||
data on any InfoLine.
|
||||
|
||||
PIVERSION is there in case we drastically change the syntax one day. For
|
||||
now it should always be "1", for the controller protocol. Controllers MAY
|
||||
provide a list of the protocol versions they support; Tor MAY select a
|
||||
version that the controller does not support.
|
||||
|
||||
AuthMethod is used to specify one or more control authentication
|
||||
methods that Tor currently accepts.
|
||||
|
||||
AuthCookieFile specifies the absolute path and filename of the
|
||||
authentication cookie that Tor is expecting and is provided iff
|
||||
the METHODS field contains the method "COOKIE". Controllers MUST handle
|
||||
escape sequences inside this string.
|
||||
|
||||
The VERSION line contains the Tor version.
|
||||
|
||||
[Unlike other commands besides AUTHENTICATE, PROTOCOLINFO may be used (but
|
||||
only once!) before AUTHENTICATE.]
|
||||
|
||||
[PROTOCOLINFO was not supported before Tor 0.2.0.5-alpha.]
|
||||
|
||||
4. Replies
|
||||
|
||||
Reply codes follow the same 3-character format as used by SMTP, with the
|
||||
|
|
|
@ -37,5 +37,5 @@ Proposals by number:
|
|||
116 Two hop paths from entry guards [OPEN]
|
||||
117 IPv6 exits [OPEN]
|
||||
118 Advertising multiple ORPorts at once [RESEARCH]
|
||||
119 New PROTOCOLINFO command for controllers [ACCEPTED]
|
||||
119 New PROTOCOLINFO command for controllers [CLOSED]
|
||||
120 Suicide descriptors when Tor servers stop [OPEN]
|
||||
|
|
|
@ -4,7 +4,7 @@ Version: $Revision$
|
|||
Last-Modified: $Date$
|
||||
Author: Roger Dingledine
|
||||
Created: 14-Aug-2007
|
||||
Status: Accepted
|
||||
Status: Closd
|
||||
|
||||
Overview:
|
||||
|
||||
|
|
108
src/or/control.c
108
src/or/control.c
|
@ -142,6 +142,7 @@ static int write_stream_target_to_buf(edge_connection_t *conn, char *buf,
|
|||
size_t len);
|
||||
static void orconn_target_get_name(int long_names, char *buf, size_t len,
|
||||
or_connection_t *conn);
|
||||
static char *get_cookie_file(void);
|
||||
|
||||
/** Given a control event code for a message event, return the corresponding
|
||||
* log severity. */
|
||||
|
@ -2236,6 +2237,7 @@ handle_control_closecircuit(control_connection_t *conn, uint32_t len,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/** DOCDOC */
|
||||
static int
|
||||
handle_control_resolve(control_connection_t *conn, uint32_t len,
|
||||
const char *body)
|
||||
|
@ -2270,6 +2272,72 @@ handle_control_resolve(control_connection_t *conn, uint32_t len,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/** DOCDOC */
|
||||
static int
|
||||
handle_control_protocolinfo(control_connection_t *conn, uint32_t len,
|
||||
const char *body)
|
||||
{
|
||||
const char *bad_arg = NULL;
|
||||
smartlist_t *args;
|
||||
(void)len;
|
||||
|
||||
conn->have_sent_protocolinfo = 1;
|
||||
args = smartlist_create();
|
||||
smartlist_split_string(args, body, " ",
|
||||
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
|
||||
SMARTLIST_FOREACH(args, const char *, arg, {
|
||||
int ok;
|
||||
tor_parse_long(arg, 10, 0, LONG_MAX, &ok, NULL);
|
||||
if (!ok) {
|
||||
bad_arg = arg;
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (bad_arg) {
|
||||
connection_printf_to_buf(conn, "513 No such version %s\r\n",
|
||||
escaped(bad_arg));
|
||||
/* Don't tolerate bad arguments when not authenticated. */
|
||||
if (!STATE_IS_OPEN(TO_CONN(conn)->state))
|
||||
connection_mark_for_close(TO_CONN(conn));
|
||||
goto done;
|
||||
} else {
|
||||
or_options_t *options = get_options();
|
||||
int cookies = options->CookieAuthentication;
|
||||
char *cfile = get_cookie_file();
|
||||
char *esc_cfile = esc_for_log(cfile);
|
||||
char *methods;
|
||||
{
|
||||
int passwd = (options->HashedControlPassword != NULL) &&
|
||||
strlen(options->HashedControlPassword);
|
||||
smartlist_t *mlist = smartlist_create();
|
||||
if (cookies)
|
||||
smartlist_add(mlist, (char*)"COOKIE");
|
||||
if (passwd)
|
||||
smartlist_add(mlist, (char*)"HASHEDPASSWORD");
|
||||
if (!cookies && !passwd)
|
||||
smartlist_add(mlist, (char*)"NULL");
|
||||
methods = smartlist_join_strings(mlist, ",", 0, NULL);
|
||||
smartlist_free(mlist);
|
||||
}
|
||||
|
||||
connection_printf_to_buf(conn,
|
||||
"250+PROTOCOLINFO 1\r\n"
|
||||
"250-AUTH METHODS=%s%s%s\r\n"
|
||||
"250-VERSION Tor=%s\r\n"
|
||||
"250 OK\r\n",
|
||||
methods,
|
||||
cookies?" COOKIEFILE=":"",
|
||||
cookies?esc_cfile:"",
|
||||
escaped(VERSION));
|
||||
tor_free(cfile);
|
||||
tor_free(esc_cfile);
|
||||
}
|
||||
done:
|
||||
SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
|
||||
smartlist_free(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Called when we get a USEFEATURE command: parse the feature list, and
|
||||
* set up the control_connection's options properly. */
|
||||
static int
|
||||
|
@ -2340,6 +2408,21 @@ connection_control_reached_eof(control_connection_t *conn)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/** Return true iff <b>cmd</b> is allowable (or at least forgivable) at this
|
||||
* stage of the protocol. */
|
||||
static int
|
||||
is_valid_initial_command(control_connection_t *conn, const char *cmd)
|
||||
{
|
||||
if (conn->_base.state == CONTROL_CONN_STATE_OPEN)
|
||||
return 1;
|
||||
if (!strcasecmp(cmd, "PROTOCOLINFO"))
|
||||
return !conn->have_sent_protocolinfo;
|
||||
if (!strcasecmp(cmd, "AUTHENTICATE") ||
|
||||
!strcasecmp(cmd, "QUIT"))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Called when data has arrived on a v1 control connection: Try to fetch
|
||||
* commands from conn->inbuf, and execute them.
|
||||
*/
|
||||
|
@ -2433,6 +2516,7 @@ connection_control_process_inbuf(control_connection_t *conn)
|
|||
--data_len;
|
||||
}
|
||||
|
||||
/* Quit is always valid. */
|
||||
if (!strcasecmp(conn->incoming_cmd, "QUIT")) {
|
||||
connection_write_str_to_buf("250 closing connection\r\n", conn);
|
||||
connection_mark_for_close(TO_CONN(conn));
|
||||
|
@ -2440,7 +2524,7 @@ connection_control_process_inbuf(control_connection_t *conn)
|
|||
}
|
||||
|
||||
if (conn->_base.state == CONTROL_CONN_STATE_NEEDAUTH &&
|
||||
strcasecmp(conn->incoming_cmd, "AUTHENTICATE")) {
|
||||
!is_valid_initial_command(conn, conn->incoming_cmd)) {
|
||||
connection_write_str_to_buf("514 Authentication required.\r\n", conn);
|
||||
connection_mark_for_close(TO_CONN(conn));
|
||||
return 0;
|
||||
|
@ -2503,6 +2587,9 @@ connection_control_process_inbuf(control_connection_t *conn)
|
|||
} else if (!strcasecmp(conn->incoming_cmd, "RESOLVE")) {
|
||||
if (handle_control_resolve(conn, data_len, args))
|
||||
return -1;
|
||||
} else if (!strcasecmp(conn->incoming_cmd, "PROTOCOLINFO")) {
|
||||
if (handle_control_protocolinfo(conn, data_len, args))
|
||||
return -1;
|
||||
} else {
|
||||
connection_printf_to_buf(conn, "510 Unrecognized command \"%s\"\r\n",
|
||||
conn->incoming_cmd);
|
||||
|
@ -3343,6 +3430,17 @@ control_event_guard(const char *nickname, const char *digest,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/** DOCDOC */
|
||||
static char *
|
||||
get_cookie_file(void)
|
||||
{
|
||||
const char *datadir = get_options()->DataDirectory;
|
||||
size_t len = strlen(datadir)+64;
|
||||
char *fname = tor_malloc(len);
|
||||
tor_snprintf(fname, len, "%s"PATH_SEPARATOR"control_auth_cookie", datadir);
|
||||
return fname;
|
||||
}
|
||||
|
||||
/** Choose a random authentication cookie and write it to disk.
|
||||
* Anybody who can read the cookie from disk will be considered
|
||||
* authorized to use the control connection. Return -1 if we can't
|
||||
|
@ -3350,8 +3448,7 @@ control_event_guard(const char *nickname, const char *digest,
|
|||
int
|
||||
init_cookie_authentication(int enabled)
|
||||
{
|
||||
char fname[512];
|
||||
|
||||
char *fname;
|
||||
if (!enabled) {
|
||||
authentication_cookie_is_set = 0;
|
||||
return 0;
|
||||
|
@ -3362,17 +3459,18 @@ init_cookie_authentication(int enabled)
|
|||
if (authentication_cookie_is_set)
|
||||
return 0; /* all set */
|
||||
|
||||
tor_snprintf(fname, sizeof(fname), "%s"PATH_SEPARATOR"control_auth_cookie",
|
||||
get_options()->DataDirectory);
|
||||
fname = get_cookie_file();
|
||||
crypto_rand(authentication_cookie, AUTHENTICATION_COOKIE_LEN);
|
||||
authentication_cookie_is_set = 1;
|
||||
if (write_bytes_to_file(fname, authentication_cookie,
|
||||
AUTHENTICATION_COOKIE_LEN, 1)) {
|
||||
log_warn(LD_FS,"Error writing authentication cookie to %s.",
|
||||
escaped(fname));
|
||||
tor_free(fname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tor_free(fname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -994,6 +994,9 @@ typedef struct control_connection_t {
|
|||
* events as appropriate. */
|
||||
unsigned int use_extended_events:1;
|
||||
|
||||
/** True if we have sent a protocolinfo reply on this connection. */
|
||||
unsigned int have_sent_protocolinfo:1;
|
||||
|
||||
uint32_t incoming_cmd_len;
|
||||
uint32_t incoming_cmd_cur_len;
|
||||
char *incoming_cmd;
|
||||
|
|
Loading…
Add table
Reference in a new issue