Browse Source

Implement two flavors of authentication for control connections: one for trusted FS, one for untrusted FS.

svn:r2664
Nick Mathewson 19 years ago
parent
commit
cea9125d71
6 changed files with 133 additions and 9 deletions
  1. 41 0
      src/common/crypto.c
  2. 4 0
      src/common/crypto.h
  3. 10 3
      src/or/config.c
  4. 49 5
      src/or/control.c
  5. 22 0
      src/or/main.c
  6. 7 1
      src/or/or.h

+ 41 - 0
src/common/crypto.c

@@ -1575,6 +1575,47 @@ base32_encode(char *dest, size_t destlen, const char *src, size_t srclen)
   dest[i] = '\0';
 }
 
+/** Implement RFC2440-style iterated-salted S2K conversion: convert the
+ * <b>secret_len</b>-byte <b>secret</b> into a <b>key_out_len</b> byte
+ * <b>key_out</b>.  As in RFC2440, the first 8 bytes of s2k_specifier
+ * are a salt; the 9th byte describes how much iteration to do.
+ * Does not support <b>key_out_len</b> &gt; DIGEST_LEN.
+ */
+void
+secret_to_key(char *key_out, size_t key_out_len, const char *secret,
+              size_t secret_len, const char *s2k_specifier)
+{
+  crypto_digest_env_t *d;
+  uint8_t c;
+  size_t count;
+  char *tmp;
+
+#define EXPBIAS 6
+  c = s2k_specifier[8];
+  count = ((uint32_t)16 + (c & 15)) << ((c >> 4) + EXPBIAS);
+#undef EXPBIAS
+
+  tor_assert(key_out_len <= DIGEST_LEN);
+
+  d = crypto_new_digest_env();
+  tmp = tor_malloc(8+secret_len);
+  memcpy(tmp,s2k_specifier,8);
+  memcpy(tmp+8,secret,secret_len);
+  secret_len += 8;
+  while (count) {
+    if (count >= secret_len) {
+      crypto_digest_add_bytes(d, tmp, secret_len);
+      count -= secret_len;
+    } else {
+      crypto_digest_add_bytes(d, tmp, count);
+      count = 0;
+    }
+  }
+  crypto_digest_get_digest(d, key_out, key_out_len);
+  tor_free(tmp);
+  crypto_free_digest_env(d);
+}
+
 /*
   Local Variables:
   mode:c

+ 4 - 0
src/common/crypto.h

@@ -153,6 +153,10 @@ int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen);
 #define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567"
 void base32_encode(char *dest, size_t destlen, const char *src, size_t srclen);
 
+#define S2K_SPECIFIER_LEN 9
+void secret_to_key(char *key_out, size_t key_out_len, const char *secret,
+                   size_t secret_len, const char *s2k_specifier);
+
 #endif
 
 /*

+ 10 - 3
src/or/config.c

@@ -101,6 +101,7 @@ static config_var_t config_vars[] = {
   VAR("MyFamily",            STRING,   MyFamily,             NULL),
   VAR("NodeFamily",          LINELIST, NodeFamilies,         NULL),
   VAR("Group",               STRING,   Group,                NULL),
+  VAR("HashedControlPassword",STRING,  HashedControlPassword, NULL),
   VAR("HttpProxy",           STRING,   HttpProxy,            NULL),
   VAR("HiddenServiceDir",    LINELIST, RendConfigLines,      NULL),
   VAR("HiddenServicePort",   LINELIST, RendConfigLines,      NULL),
@@ -183,12 +184,13 @@ config_get_commandlines(int argc, char **argv)
   int i = 1;
 
   while (i < argc-1) {
-    if (!strcmp(argv[i],"-f")) {
-//      log(LOG_DEBUG,"Commandline: skipping over -f.");
-      i += 2; /* this is the config file option. ignore it. */
+    if (!strcmp(argv[i],"-f") ||
+        !strcmp(argv[i],"--hash-password")) {
+      i += 2; /* command-line option with argument. ignore them. */
       continue;
     } else if (!strcmp(argv[i],"--list-fingerprint")) {
       i += 1; /* command-line option. ignore it. */
+      continue;
     }
 
     new = tor_malloc(sizeof(struct config_line_t));
@@ -803,7 +805,12 @@ getconfig(int argc, char **argv, or_options_t *options)
       ++i;
     } else if (!strcmp(argv[i],"--list-fingerprint")) {
       options->command = CMD_LIST_FINGERPRINT;
+    } else if (!strcmp(argv[i],"--hash-password")) {
+      options->command = CMD_HASH_PASSWORD;
+      options->command_arg = tor_strdup(argv[i+1]);
+      ++i;
     }
+
   }
 
   if (using_default_torrc) {

+ 49 - 5
src/or/control.c

@@ -47,6 +47,10 @@ extern or_options_t options;
 
 static uint32_t global_event_mask = 0;
 
+#define AUTHENTICATION_COOKIE_LEN 32
+static int authentication_cookie_is_set = 0;
+static char authentication_cookie[AUTHENTICATION_COOKIE_LEN];
+
 static void update_global_event_mask(void);
 static void send_control_message(connection_t *conn, uint16_t type,
                                  uint16_t len, const char *body);
@@ -216,13 +220,34 @@ static int handle_control_setevents(connection_t *conn, uint16_t len,
 static int handle_control_authenticate(connection_t *conn, uint16_t len,
                                        const char *body)
 {
-  if (0/* XXXX009 NM */) {
-    send_control_done(conn);
-    conn->state = CONTROL_CONN_STATE_OPEN;
-  } else {
-    send_control_error(conn, ERR_FAILED_AUTHENTICATION,"Authentication failed");
+  if (len == AUTHENTICATION_COOKIE_LEN &&
+      authentication_cookie_is_set &&
+      !memcmp(authentication_cookie, body, len)) {
+    goto ok;
+  } else if (options.HashedControlPassword) {
+    char expected[S2K_SPECIFIER_LEN+DIGEST_LEN];
+    char received[DIGEST_LEN];
+    if (base64_decode(expected,sizeof(expected),
+                      options.HashedControlPassword,
+                      strlen(options.HashedControlPassword))<0) {
+      /* XXXX009 NM we should warn sooner. */
+      log_fn(LOG_WARN,"Couldn't decode HashedControlPassword: invalid base64");
+      goto err;
+    }
+    secret_to_key(received,DIGEST_LEN,body,len,expected);
+    if (!memcmp(expected+S2K_SPECIFIER_LEN, received, DIGEST_LEN))
+      goto ok;
   }
+
+ err:
+  send_control_error(conn, ERR_FAILED_AUTHENTICATION,"Authentication failed");
   return 0;
+ ok:
+  log_fn(LOG_INFO, "Authenticated control connection (%d)", conn->s);
+  send_control_done(conn);
+  conn->state = CONTROL_CONN_STATE_OPEN;
+  return 0;
+
 }
 
 int connection_control_finished_flushing(connection_t *conn) {
@@ -391,6 +416,25 @@ void control_event_logmsg(int severity, const char *msg)
   send_control_event(EVENT_WARNING, (uint16_t)(len+1), msg);
 }
 
+int init_cookie_authentication(void)
+{
+  char fname[512];
+
+  /* XXXX009 NM add config option to disable this. */
+
+  tor_snprintf(fname, sizeof(fname), "%s/control_auth_cookie",
+               get_data_directory(&options));
+  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_fn(LOG_WARN,"Error writing authentication cookie.");
+    return -1;
+  }
+
+  return 0;
+}
+
 /*
   Local Variabls:
   mode:c

+ 22 - 0
src/or/main.c

@@ -1373,6 +1373,25 @@ static void do_list_fingerprint(void)
   }
 }
 
+/** DOCDOC **/
+static void do_hash_password(void)
+{
+
+  char output[256];
+  char key[S2K_SPECIFIER_LEN+DIGEST_LEN];
+
+  crypto_rand(key, S2K_SPECIFIER_LEN-1);
+  key[S2K_SPECIFIER_LEN-1] = (uint8_t)96; /* Hash 64 K of data. */
+  secret_to_key(key+S2K_SPECIFIER_LEN, DIGEST_LEN,
+                options.command_arg, strlen(options.command_arg),
+                key);
+  if (base64_encode(output, sizeof(output), key, sizeof(key))<0) {
+    log_fn(LOG_ERR, "Unable to compute base64");
+  } else {
+    printf("%s",output);
+  }
+}
+
 #ifdef MS_WINDOWS_SERVICE
 void nt_service_control(DWORD request)
 {
@@ -1449,6 +1468,9 @@ int tor_main(int argc, char *argv[]) {
   case CMD_LIST_FINGERPRINT:
     do_list_fingerprint();
     break;
+  case CMD_HASH_PASSWORD:
+    do_hash_password();
+    break;
   default:
     log_fn(LOG_ERR, "Illegal command number %d: internal error.",
            options.command);

+ 7 - 1
src/or/or.h

@@ -852,8 +852,10 @@ typedef struct exit_redirect_t {
 typedef struct {
   /** What should the tor process actually do? */
   enum {
-    CMD_RUN_TOR=0, CMD_LIST_FINGERPRINT
+    CMD_RUN_TOR=0, CMD_LIST_FINGERPRINT, CMD_HASH_PASSWORD,
   } command;
+  const char *command_arg; /**< Argument for command-line option. */
+
   struct config_line_t *LogOptions; /**< List of configuration lines
                                      * for logfiles */
 
@@ -949,6 +951,8 @@ typedef struct {
   int AccountingMaxKB; /**< How many KB do we allow per accounting
                         * interval before hibernation?  0 for "never
                         * hibernate." */
+  char *HashedControlPassword; /**< Base64-encoded hash of a password for
+                                * the control system. */
 } or_options_t;
 
 /* XXX are these good enough defaults? */
@@ -1241,6 +1245,8 @@ int control_event_or_conn_status(connection_t *conn, or_conn_status_event_t e);
 int control_event_bandwidth_used(uint32_t n_read, uint32_t n_written);
 void control_event_logmsg(int severity, const char *msg);
 
+int init_cookie_authentication(void);
+
 /********************************* cpuworker.c *****************************/
 
 void cpu_init(void);