Browse Source

Make clients regenerate their keys when their IP address changes.

svn:r4688
Nick Mathewson 19 years ago
parent
commit
11ff0aba80
4 changed files with 130 additions and 16 deletions
  1. 57 2
      src/common/util.c
  2. 1 0
      src/common/util.h
  3. 72 12
      src/or/connection.c
  4. 0 2
      src/or/router.c

+ 57 - 2
src/common/util.c

@@ -695,7 +695,8 @@ int parse_iso_time(const char *cp, time_t *t) {
  * must be 1 if fd was returned by socket() or accept(), and 0 if fd
  * was returned by open().  Return the number of bytes written, or -1
  * on error.  Only use if fd is a blocking fd.  */
-int write_all(int fd, const char *buf, size_t count, int isSocket) {
+int
+write_all(int fd, const char *buf, size_t count, int isSocket) {
   size_t written = 0;
   int result;
 
@@ -716,7 +717,8 @@ int write_all(int fd, const char *buf, size_t count, int isSocket) {
  * was returned by socket() or accept(), and 0 if fd was returned by
  * open().  Return the number of bytes read, or -1 on error. Only use
  * if fd is a blocking fd. */
-int read_all(int fd, char *buf, size_t count, int isSocket) {
+int
+read_all(int fd, char *buf, size_t count, int isSocket) {
   size_t numread = 0;
   int result;
 
@@ -1319,6 +1321,59 @@ is_plausible_address(const char *name)
   return 1;
 }
 
+/**
+ * Set *<b>addr</b> to the host-order IPv4 address (if any) of whatever
+ * interface connects to the internet.  This address should only be used in
+ * checking whether our address has changed.  Return 0 on success, -1 on
+ * failure.
+ */
+int
+get_interface_address(uint32_t *addr)
+{
+  int sock=-1, r=-1;
+  struct sockaddr_in target_addr, my_addr;
+  socklen_t my_addr_len = sizeof(my_addr);
+
+  tor_assert(addr);
+  *addr = 0;
+
+  sock = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
+  if (sock < 0) {
+    int e = tor_socket_errno(-1);
+    log_fn(LOG_WARN, "unable to create socket: %s", tor_socket_strerror(e));
+    goto err;
+  }
+
+  memset(&target_addr, 0, sizeof(target_addr));
+  target_addr.sin_family = AF_INET;
+  /* discard port */
+  target_addr.sin_port = 9;
+  /* 18.0.0.1 (Don't worry: no packets are sent. We just need a real address
+   * on the internet.) */
+  target_addr.sin_addr.s_addr = htonl(0x12000001);
+
+  if (connect(sock,(struct sockaddr *)&target_addr,sizeof(target_addr))<0) {
+    int e = tor_socket_errno(sock);
+    log_fn(LOG_WARN, "connnect() failed: %s", tor_socket_strerror(e));
+    goto err;
+  }
+
+  /* XXXX Can this be right on IPv6 clients? */
+  if (getsockname(sock, &my_addr, &my_addr_len)) {
+    int e = tor_socket_errno(sock);
+    log_fn(LOG_WARN, "getsockname() failed: %s", tor_socket_strerror(e));
+    goto err;
+  }
+
+  *addr = ntohl(my_addr.sin_addr.s_addr);
+
+  r=0;
+ err:
+  if (sock >= 0)
+    tor_close_socket(sock);
+  return r;
+}
+
 /* =====
  * Process helpers
  * ===== */

+ 1 - 0
src/common/util.h

@@ -135,6 +135,7 @@ int parse_addr_and_port_range(const char *s, uint32_t *addr_out,
 #define INET_NTOA_BUF_LEN 16
 int tor_inet_ntoa(struct in_addr *in, char *buf, size_t buf_len);
 int is_plausible_address(const char *name);
+int get_interface_address(uint32_t *addr);
 
 /* Process helpers */
 void start_daemon(void);

+ 72 - 12
src/or/connection.c

@@ -24,6 +24,10 @@ static int connection_reached_eof(connection_t *conn);
 static int connection_read_to_buf(connection_t *conn, int *max_to_read);
 static int connection_process_inbuf(connection_t *conn, int package_partial);
 static int connection_bucket_read_limit(connection_t *conn);
+static void client_check_address_changed(int sock);
+
+static uint32_t last_interface_ip = 0;
+static smartlist_t *outgoing_addrs = NULL;
 
 /**************************************************************/
 
@@ -251,11 +255,16 @@ connection_free(connection_t *conn)
   }
   connection_unregister(conn);
   _connection_free(conn);
+
+  SMARTLIST_FOREACH(outgoing_addrs, void*, addr, tor_free(addr));
+  smartlist_free(outgoing_addrs);
+  outgoing_addrs = NULL;
 }
 
-/** Call _connection_free() on every connection in our array.
- * This is used by cpuworkers and dnsworkers when they fork,
- * so they don't keep resources held open (especially sockets).
+/** Call _connection_free() on every connection in our array, and release all
+ * storage helpd by connection.c. This is used by cpuworkers and dnsworkers
+ * when they fork, so they don't keep resources held open (especially
+ * sockets).
  *
  * Don't do the checks in connection_free(), because they will
  * fail.
@@ -701,7 +710,7 @@ int
 connection_connect(connection_t *conn, char *address,
                    uint32_t addr, uint16_t port)
 {
-  int s;
+  int s, inprogress = 0;
   struct sockaddr_in dest_addr;
   or_options_t *options = get_options();
 
@@ -754,21 +763,21 @@ connection_connect(connection_t *conn, char *address,
       tor_close_socket(s);
       return -1;
     } else {
-      /* it's in progress. set state appropriately and return. */
-      conn->s = s;
-      if (connection_add(conn) < 0) /* no space, forget it */
-        return -1;
-      log_fn(LOG_DEBUG,"connect in progress, socket %d.",s);
-      return 0;
+      inprogress = 1;
     }
   }
 
+  if (!server_mode(options))
+    client_check_address_changed(s);
+
   /* it succeeded. we're connected. */
-  log_fn(LOG_INFO,"Connection to %s:%u established.",safe_str(address),port);
+  log_fn(inprogress?LOG_DEBUG:LOG_INFO,
+         "Connection to %s:%u %s (sock %d).",safe_str(address),port,
+         inprogress?"in progress":"established",s);
   conn->s = s;
   if (connection_add(conn) < 0) /* no space, forget it */
     return -1;
-  return 1;
+  return inprogress ? 0 : 1;
 }
 
 /**
@@ -1680,6 +1689,57 @@ alloc_http_authenticator(const char *authenticator)
   return base64_authenticator;
 }
 
+/** DOCDOC
+ * XXXX ipv6 NM
+ */
+static void
+client_check_address_changed(int sock)
+{
+  uint32_t iface_ip, ip_out;
+  struct sockaddr_in out_addr;
+  socklen_t out_addr_len = sizeof(out_addr);
+  uint32_t *ip;
+
+  if (!last_interface_ip)
+    get_interface_address(&last_interface_ip);
+  if (!outgoing_addrs)
+    outgoing_addrs = smartlist_create();
+
+  if (getsockname(sock, (struct sockaddr*)&out_addr, &out_addr_len)<0) {
+    int e = tor_socket_errno(sock);
+    log_fn(LOG_WARN, "getsockname() failed: %s", tor_socket_strerror(e));
+    return;
+  }
+
+  /* Okay.  If we've used this address previously, we're okay. */
+  ip_out = ntohl(out_addr.sin_addr.s_addr);
+  SMARTLIST_FOREACH(outgoing_addrs, uint32_t*, ip,
+                    if (*ip == ip_out) return;
+                    );
+
+  /* Uh-oh.  We haven't connected from this address before. Has the interface
+   * address changed? */
+  if (get_interface_address(&iface_ip)<0)
+    return;
+  ip = tor_malloc(sizeof(uint32_t));
+  *ip = ip_out;
+
+  if (iface_ip == last_interface_ip) {
+    /* Nope, it hasn't changed.  Add this address to the list. */
+    smartlist_add(outgoing_addrs, ip);
+  } else {
+    /* The interface changed.  We're a client, so we need to regenerate our
+     * keys.  First, reset the state. */
+    log_fn(LOG_NOTICE, "Our IP has changed.  Rotating keys...");
+    last_interface_ip = iface_ip;
+    SMARTLIST_FOREACH(outgoing_addrs, void*, ip, tor_free(ip));
+    smartlist_clear(outgoing_addrs);
+    smartlist_add(outgoing_addrs, ip);
+    /* Okay, now change our keys. */
+    init_keys(); /* XXXX NM return value-- safe to ignore? */
+  }
+}
+
 /** Process new bytes that have arrived on conn-\>inbuf.
  *
  * This function just passes conn to the connection-specific

+ 0 - 2
src/or/router.c

@@ -245,8 +245,6 @@ init_keys(void)
   /* XXX009 Two problems with how this is called:
    * 1. It should be idempotent for servers, so we can call init_keys
    *    as much as we need to.
-   * 2. Clients should rotate their identity keys at least whenever
-   *    their IPs change.
    */
   char keydir[512];
   char keydir2[512];