summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2007-08-16 17:31:23 +0000
committerNick Mathewson <nickm@torproject.org>2007-08-16 17:31:23 +0000
commitf4398feadb8e3c1af51a825ac0eafe5abc08683d (patch)
treeff141025989fb4a5ec45f6f64b426873a78d6c53
parentb89efa770596d9bac9a03e60b5ff26bb936c0eaa (diff)
downloadtor-f4398feadb8e3c1af51a825ac0eafe5abc08683d.tar.gz
tor-f4398feadb8e3c1af51a825ac0eafe5abc08683d.zip
r14600@catbus: nickm | 2007-08-16 13:30:22 -0400
Implement proposal 119. Backport candidate. svn:r11138
-rw-r--r--ChangeLog6
-rw-r--r--doc/spec/control-spec.txt58
-rw-r--r--doc/spec/proposals/000-index.txt2
-rw-r--r--doc/spec/proposals/119-controlport-auth.txt2
-rw-r--r--src/or/control.c108
-rw-r--r--src/or/or.h3
6 files changed, 169 insertions, 10 deletions
diff --git a/ChangeLog b/ChangeLog
index 1ec7eb14b0..8b00a69f5d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/doc/spec/control-spec.txt b/doc/spec/control-spec.txt
index 1c0f85b6c2..e102a552bf 100644
--- a/doc/spec/control-spec.txt
+++ b/doc/spec/control-spec.txt
@@ -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
diff --git a/doc/spec/proposals/000-index.txt b/doc/spec/proposals/000-index.txt
index 3b1e8f46ed..b2de017d07 100644
--- a/doc/spec/proposals/000-index.txt
+++ b/doc/spec/proposals/000-index.txt
@@ -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]
diff --git a/doc/spec/proposals/119-controlport-auth.txt b/doc/spec/proposals/119-controlport-auth.txt
index 8dbe1c3309..f248f6828c 100644
--- a/doc/spec/proposals/119-controlport-auth.txt
+++ b/doc/spec/proposals/119-controlport-auth.txt
@@ -4,7 +4,7 @@ Version: $Revision$
Last-Modified: $Date$
Author: Roger Dingledine
Created: 14-Aug-2007
-Status: Accepted
+Status: Closd
Overview:
diff --git a/src/or/control.c b/src/or/control.c
index d210428a4d..5a256783df 100644
--- a/src/or/control.c
+++ b/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;
}
diff --git a/src/or/or.h b/src/or/or.h
index a0d9b087e7..f7b77a3361 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -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;