r14600@catbus: nickm | 2007-08-16 13:30:22 -0400

Implement proposal 119. Backport candidate.


svn:r11138
This commit is contained in:
Nick Mathewson 2007-08-16 17:31:23 +00:00
parent b89efa7705
commit f4398feadb
6 changed files with 169 additions and 10 deletions

View file

@ -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

View file

@ -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

View file

@ -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]

View file

@ -4,7 +4,7 @@ Version: $Revision$
Last-Modified: $Date$
Author: Roger Dingledine
Created: 14-Aug-2007
Status: Accepted
Status: Closd
Overview:

View file

@ -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;
}

View file

@ -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;