Explorar el Código

Refactor common file code into util.c; add published to descriptors

svn:r487
Nick Mathewson hace 20 años
padre
commit
92acbe12bc
Se han modificado 8 ficheros con 198 adiciones y 78 borrados
  1. 22 0
      src/common/crypto.c
  2. 1 0
      src/common/crypto.h
  3. 116 1
      src/common/util.c
  4. 12 0
      src/common/util.h
  5. 22 75
      src/or/main.c
  6. 3 0
      src/or/or.h
  7. 17 1
      src/or/routers.c
  8. 5 1
      src/or/test.c

+ 22 - 0
src/common/crypto.c

@@ -472,6 +472,28 @@ int crypto_pk_read_public_key_from_string(crypto_pk_env_t *env, char *src, int l
   return 0;
 }
 
+int 
+crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env, 
+                                        const char *fname)
+{
+  BIO *bio;
+  char *cp;
+  long len;
+  int r;
+  assert(env->type == CRYPTO_PK_RSA);
+  if (!(bio = BIO_new(BIO_s_mem())))
+    return -1;
+  if (PEM_write_bio_RSAPrivateKey(bio, (RSA*)env->key, NULL,NULL,0,0,NULL)) {
+    BIO_free(bio);
+    return -1;
+  }
+  len = BIO_get_mem_data(bio, &cp);
+  assert(len == strlen(cp));
+  r = write_str_to_file(fname, cp);
+  BIO_free(bio);
+  return r;
+}
+
 int crypto_pk_write_private_key_to_file(crypto_pk_env_t *env, FILE *dest)
 {
   assert(env && dest);

+ 1 - 0
src/common/crypto.h

@@ -40,6 +40,7 @@ int crypto_pk_read_public_key_from_file(crypto_pk_env_t *env, FILE *src);
 int crypto_pk_write_public_key_to_string(crypto_pk_env_t *env, char **dest, int *len);
 int crypto_pk_read_public_key_from_string(crypto_pk_env_t *env, char *src, int len);
 int crypto_pk_write_private_key_to_file(crypto_pk_env_t *env, FILE *dest);
+int crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env, const char *fname);
 int crypto_pk_write_public_key_to_file(crypto_pk_env_t *env, FILE *dest);
 int crypto_pk_check_key(crypto_pk_env_t *env);
 int crypto_pk_read_private_key_from_filename(crypto_pk_env_t *env, const char *keyfile);

+ 116 - 1
src/common/util.c

@@ -13,6 +13,10 @@
 #include "util.h"
 #include "log.h"
 
+/*
+ *    Memory
+ */
+
 void *tor_malloc(size_t size) {
   void *result;
 
@@ -26,6 +30,10 @@ void *tor_malloc(size_t size) {
   return result;
 }
 
+/*
+ *    Time
+ */
+
 void 
 my_gettimeofday(struct timeval *timeval) 
 {
@@ -88,6 +96,10 @@ void tv_addms(struct timeval *a, long ms) {
   a->tv_usec %= 1000000;
 }
 
+/*
+ *   Low-level I/O.
+ */
+
 /* a wrapper for write(2) that makes sure to write all count bytes.
  * Only use if fd is a blocking socket. */
 int write_all(int fd, const void *buf, size_t count) {
@@ -129,6 +141,10 @@ void set_socket_nonblocking(int socket)
 #endif
 }
 
+/*
+ *   Process control
+ */
+
 int spawn_func(int (*func)(void *), void *data)
 {
 #ifdef MS_WINDOWS
@@ -164,7 +180,9 @@ void spawn_exit()
 }
 
 
-/* Fake socket pair over TCP.  Code adapted from perl 5.8.0's util.c */
+/*
+ *   Windows compatibility.
+ */
 int
 tor_socketpair(int family, int type, int protocol, int fd[2])
 {
@@ -276,3 +294,100 @@ int correct_socket_errno(int s)
   return WSAEWOULDBLOCK;
 }
 #endif
+
+/*
+ *    Filesystem operations.
+ */
+file_status_t file_status(const char *fname)
+{
+  struct stat st;
+  if (stat(fname, &st)) {
+    if (errno == ENOENT) {
+      return FN_NOENT;
+    }
+    return FN_ERROR;
+  }
+  if (st.st_mode & S_IFDIR) 
+    return FN_DIR;
+  else if (st.st_mode & S_IFREG)
+    return FN_FILE;
+  else
+    return FN_ERROR;
+}
+
+int check_private_dir(const char *dirname, int create)
+{
+  struct stat st;
+  if (stat(dirname, &st)) {
+    if (errno != ENOENT) {
+      log(LOG_ERR, "Directory %s cannot be read: %s", dirname, 
+          strerror(errno));
+      return -1;
+    } 
+    if (!create) {
+      log(LOG_ERR, "Directory %s does not exist.", dirname);
+      return -1;
+    }
+    log(LOG_INFO, "Creating directory %s", dirname); 
+    if (mkdir(dirname, 0700)) {
+      log(LOG_ERR, "Error creating directory %s: %s", dirname, 
+          strerror(errno));
+      return -1;
+    } else {
+      return 0;
+    }
+  }
+  if (!(st.st_mode & S_IFDIR)) {
+    log(LOG_ERR, "%s is not a directory", dirname);
+    return -1;
+  }
+  if (st.st_uid != getuid()) {
+    log(LOG_ERR, "%s is not owned by this UID (%d)", dirname, getuid());
+    return -1;
+  }
+  if (st.st_mode & 0077) {
+    log(LOG_WARNING, "Fixing permissions on directory %s", dirname);
+    if (chmod(dirname, 0700)) {
+      log(LOG_ERR, "Could not chmod directory %s: %s", dirname, 
+          strerror(errno));
+      return -1;
+    } else {
+      return 0;
+    }
+  }
+  return 0;
+}
+
+int
+write_str_to_file(const char *fname, const char *str)
+{
+  char tempname[1024];
+  int fd;
+  FILE *file;
+  if (strlen(fname) > 1000) {
+    log(LOG_ERR, "Filename %s is too long.", fname);
+    return -1;
+  }
+  strcpy(tempname,fname);
+  strcat(tempname,".tmp");
+  if ((fd = open(tempname, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
+    log(LOG_ERR, "Couldn't open %s for writing: %s", tempname, 
+        strerror(errno));
+    return -1;
+  }
+  if (!(file = fdopen(fd, "w"))) {
+    log(LOG_ERR, "Couldn't fdopen %s for writing: %s", tempname, 
+        strerror(errno));
+    close(fd); return -1;
+  }
+  if (fputs(str,file)) {
+    log(LOG_ERR, "Error writing to %s: %s", tempname, strerror(errno));
+    fclose(file); return -1;
+  }
+  fclose(file);
+  if (rename(tempname, fname)) {
+    log(LOG_ERR, "Error replacing %s: %s", fname, strerror(errno));
+    return -1;
+  }
+  return 0;
+}

+ 12 - 0
src/common/util.h

@@ -56,6 +56,18 @@ int read_all(int fd, void *buf, size_t count);
 
 void set_socket_nonblocking(int socket);
 
+typedef enum { FN_ERROR, FN_NOENT, FN_FILE, FN_DIR} file_status_t;
+
+/* Return FN_ERROR if filename can't be read, FN_NOENT if it doesn't
+ * exist, FN_FILE if it is a regular file, or FN_DIR if it's a
+ * directory. */
+file_status_t file_status(const char *filename);
+/* Check whether dirname exists and is private.  If yes returns
+ * 0.  Else returns -1.
+ */
+int check_private_dir(const char *dirname, int create);
+int write_str_to_file(const char *fname, const char *str);
+
 /* Minimalist interface to run a void function in the background.  On
    unix calls fork, on win32 calls beginthread.  Returns -1 on failure.
    func should not return, but rather should call spawn_exit.

+ 22 - 75
src/or/main.c

@@ -433,25 +433,6 @@ static int prepare_for_poll(void) {
   return (1000 - (now.tv_usec / 1000)); /* how many milliseconds til the next second? */
 }
 
-#define FN_ERROR -1
-#define FN_NOENT 0
-#define FN_FILE 1
-#define FN_DIR 2
-static int fn_exists(const char *fname)
-{
-  struct stat st;
-  if (stat(fname, &st)) {
-    if (errno == ENOENT) {
-      return FN_NOENT;
-    }
-    return FN_ERROR;
-  }
-  if (st.st_mode & S_IFDIR) 
-    return FN_DIR;
-  else
-    return FN_FILE;
-}
-
 static crypto_pk_env_t *init_key_from_file(const char *fname)
 {
   crypto_pk_env_t *prkey = NULL;
@@ -463,7 +444,7 @@ static crypto_pk_env_t *init_key_from_file(const char *fname)
     goto error;
   }
 
-  switch(fn_exists(fname)) {
+  switch(file_status(fname)) {
   case FN_DIR:
   case FN_ERROR:
     log(LOG_ERR, "Can't read key from %s", fname);
@@ -479,22 +460,10 @@ static crypto_pk_env_t *init_key_from_file(const char *fname)
       goto error;
     }
     log(LOG_INFO, "Generated key seems valid");
-    fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0400);
-    if (fd == -1) {
-      log(LOG_ERR, "Can't open %s for writing", fname);
-      goto error;
-    }
-    file = fdopen(fd, "w");
-    if (!file) {
-      log(LOG_ERR, "Can't fdopen %s for writing", fname);
+    if (crypto_pk_write_private_key_to_filename(prkey, fname)) {
+      log(LOG_ERR, "Couldn't write generated key to %s.", fname);
       goto error;
     }
-    if (crypto_pk_write_private_key_to_file(prkey, file) < 0) {
-      log(LOG_ERR, "Can't write private key to %s", fname);
-      goto error;
-    }
-    fclose(file);
-    /* XXX fingerprint */
     return prkey;
   case FN_FILE:
     if (crypto_pk_read_private_key_from_filename(prkey, fname)) {
@@ -519,10 +488,9 @@ static crypto_pk_env_t *init_key_from_file(const char *fname)
 static int init_keys(void)
 {
   char keydir[512];
-  char fingerprint[FINGERPRINT_LEN+1];
+  char fingerprint[FINGERPRINT_LEN+MAX_NICKNAME_LEN+3]; 
   char *cp;
   crypto_pk_env_t *prkey;
-  FILE *file;
 
   /* OP's don't need keys.  Just initialize the TLS context.*/
   if (!options.OnionRouter && !options.DirPort) {
@@ -538,34 +506,12 @@ static int init_keys(void)
     return -1;
   }
   strcpy(keydir, options.DataDirectory);
-  switch (fn_exists(keydir)) {
-  case FN_NOENT:
-    log_fn(LOG_ERR, "DataDirectory does not exist");
-    return -1;
-  case FN_ERROR:
-    log_fn(LOG_ERR, "DataDirectory can't be read");
-    return -1;
-  case FN_FILE:
-    log_fn(LOG_ERR, "DataDirectory is not a directory.");
+  if (check_private_dir(keydir, 1)) {
     return -1;
   }
   strcat(keydir, "/keys");
-  switch (fn_exists(keydir)) {
-  case FN_NOENT:
-    if (mkdir(keydir, 0700)) {
-      log_fn(LOG_ERR, "Error making key directory.");
-      return -1;
-    }
-    break;
-  case FN_ERROR:
-    log_fn(LOG_ERR, "Error reading key directory.");
+  if (check_private_dir(keydir, 1)) {
     return -1;
-  case FN_FILE:
-    log_fn(LOG_ERR, "Key directory is not a directory.");
-    return -1;
-  case FN_DIR:
-    chmod(keydir, 0700);
-    break;
   }
   cp = keydir + strlen(keydir); /* End of string. */
   assert(!*cp);
@@ -600,28 +546,23 @@ static int init_keys(void)
   }
   strcpy(keydir, options.DataDirectory);
   strcat(keydir, "/router.desc");
-  file = fopen(keydir, "w");
-  if (!file) {
-    log_fn(LOG_ERR, "Error opening %s for writing", keydir);
+  if (write_str_to_file(keydir, router_get_my_descriptor())) {
     return -1;
   }
-  fputs(router_get_my_descriptor(), file);
-  fclose(file);
   /* 5. Dump fingerprint to 'fingerprint' */
   strcpy(keydir, options.DataDirectory);
   strcat(keydir, "/fingerprint");
-  file = fopen(keydir, "w");
-  if (!file) {
-    log_fn(LOG_ERR, "Error opening %s for writing", keydir);
-    return -1;
-  }
-  if (crypto_pk_get_fingerprint(get_identity_key(), fingerprint)<0) {
+  assert(strlen(options.Nickname) <= MAX_NICKNAME_LEN);
+  strcpy(fingerprint, options.Nickname);
+  strcat(fingerprint, " ");
+  if (crypto_pk_get_fingerprint(get_identity_key(),
+                                fingerprint+strlen(fingerprint))<0) {
     log_fn(LOG_ERR, "Error computing fingerprint");
     return -1;
   }
-  fprintf(file, "%s %s\n", options.Nickname, fingerprint);
-  fclose(file);
-
+  strcat(fingerprint, "\n");
+  if (write_str_to_file(keydir, fingerprint))
+    return -1;
   return 0;
 }
 
@@ -774,6 +715,7 @@ int dump_router_to_string(char *s, int maxlen, routerinfo_t *router,
   char *identity_pkey;
   char digest[20];
   char signature[128];
+  char published[32];
   int onion_pkeylen, link_pkeylen, identity_pkeylen;
   int written;
   int result=0;
@@ -796,9 +738,12 @@ int dump_router_to_string(char *s, int maxlen, routerinfo_t *router,
     log_fn(LOG_WARNING,"write link_pkey to string failed!");
     return -1;
   }
+  strftime(published, 32, "%Y-%m-%d %H:%M:%S", gmtime(&router->published_on));
   
   result = snprintf(s, maxlen, 
-                    "router %s %d %d %d %d\nonion-key\n%s"
+                    "router %s %d %d %d %d\n"
+                    "published %s\n"
+                    "onion-key\n%s"
                     "link-key\n%s"
                     "signing-key\n%s",
     router->address,
@@ -806,6 +751,7 @@ int dump_router_to_string(char *s, int maxlen, routerinfo_t *router,
     router->ap_port,
     router->dir_port,
     router->bandwidth,
+    published,
     onion_pkey, link_pkey, identity_pkey);
 
   free(onion_pkey);
@@ -1005,6 +951,7 @@ static int init_descriptor(void) {
   ri->or_port = options.ORPort;
   ri->ap_port = options.APPort;
   ri->dir_port = options.DirPort;
+  ri->published_on = time(NULL);
   ri->onion_pkey = crypto_pk_dup_key(get_onion_key());
   ri->link_pkey = crypto_pk_dup_key(get_link_key());
   ri->identity_pkey = crypto_pk_dup_key(get_identity_key());

+ 3 - 0
src/or/or.h

@@ -101,6 +101,7 @@
 
 #define MAX_BUF_SIZE (640*1024)
 #define DEFAULT_BANDWIDTH_OP (1024 * 1000)
+#define MAX_NICKNAME_LEN 32
 
 #define ACI_TYPE_LOWER 0
 #define ACI_TYPE_HIGHER 1
@@ -326,6 +327,8 @@ typedef struct {
   uint16_t or_port;
   uint16_t ap_port;
   uint16_t dir_port;
+
+  time_t published_on;
  
   crypto_pk_env_t *onion_pkey; /* public RSA key for onions */
   crypto_pk_env_t *link_pkey;  /* public RSA key for TLS */

+ 17 - 1
src/or/routers.c

@@ -318,6 +318,7 @@ typedef enum {
   K_ONION_KEY,
   K_LINK_KEY,
   K_ROUTER_SIGNATURE,
+  K_PUBLISHED,
   _SIGNATURE, 
   _PUBLIC_KEY, 
   _ERR, 
@@ -337,6 +338,7 @@ static struct token_table_ent token_table[] = {
   { "onion-key", K_ONION_KEY },
   { "link-key", K_LINK_KEY },
   { "router-signature", K_ROUTER_SIGNATURE },
+  { "published", K_PUBLISHED },
   { NULL, -1 }
 };
 
@@ -492,6 +494,7 @@ router_dump_token(directory_token_t *tok) {
     case K_ONION_KEY: printf("Onion-key"); break;
     case K_LINK_KEY: printf("Link-key"); break;
     case K_ROUTER_SIGNATURE: printf("Router-signature"); break;
+    case K_PUBLISHED: printf("Published"); break;
     default:
       printf("?????? %d\n", tok->tp); return;
     }
@@ -513,7 +516,6 @@ router_get_next_token(char **s, directory_token_t *tok) {
 #endif
 
 
-
 /* return the first char of s that is not whitespace and not a comment */
 static char *eat_whitespace(char *s) {
   assert(s);
@@ -817,6 +819,7 @@ routerinfo_t *router_get_entry_from_string(char**s) {
   char digest[128];
   directory_token_t _tok;
   directory_token_t *tok = &_tok;
+  struct tm published;
 
 #define NEXT_TOKEN()                                                     \
   do { if (router_get_next_token(s, tok)) {                              \
@@ -875,6 +878,19 @@ routerinfo_t *router_get_entry_from_string(char**s) {
   log_fn(LOG_DEBUG,"or_port %d, ap_port %d, dir_port %d, bandwidth %d.",
     router->or_port, router->ap_port, router->dir_port, router->bandwidth);
 
+  NEXT_TOKEN();
+  if (tok->tp != K_PUBLISHED) {
+    log_fn(LOG_WARNING, "Missing published time"); goto err;
+  }
+  if (tok->val.cmd.n_args != 2) {
+    log_fn(LOG_WARNING, "Wrong number of arguments to published"); goto err;
+  }
+  tok->val.cmd.args[1][-1] = ' '; /* Re-insert space. */
+  if (!strptime(tok->val.cmd.args[0], "%Y-%m-%d %H:%M:%S", &published)) {
+    log_fn(LOG_WARNING, "Published time was unparseable"); goto err;
+  }
+  router->published_on = timegm(&published);
+
   NEXT_TOKEN();
   if (tok->tp != K_ONION_KEY) {
     log_fn(LOG_WARNING, "Missing onion-key"); goto err;

+ 5 - 1
src/or/test.c

@@ -519,6 +519,7 @@ test_dir_format()
   
   r1.address = "testaddr1.foo.bar";
   r1.addr = 0xc0a80001u; /* 192.168.0.1 */
+  r1.published_on = 0;
   r1.or_port = 9000;
   r1.ap_port = 9002;
   r1.dir_port = 9003;
@@ -539,6 +540,7 @@ test_dir_format()
   ex2.next = NULL;
   r2.address = "tor.tor.tor";
   r2.addr = 0x0a030201u; /* 10.3.2.1 */
+  r2.published_on = 5;
   r2.or_port = 9005;
   r2.ap_port = 0;
   r2.dir_port = 0;
@@ -555,7 +557,9 @@ test_dir_format()
   test_assert(!crypto_pk_write_public_key_to_string(pk3 , &pk3_str, 
                                                     &pk3_str_len));
   
-  strcpy(buf2, "router testaddr1.foo.bar 9000 9002 9003 1000\nonion-key\n");
+  strcpy(buf2, "router testaddr1.foo.bar 9000 9002 9003 1000\n"
+         "published 1970-01-01 00:00:00\n"
+         "onion-key\n");
   strcat(buf2, pk1_str);
   strcat(buf2, "link-key\n");
   strcat(buf2, pk3_str);