Commit e8db9d0c authored by Nick Mathewson's avatar Nick Mathewson 🤹
Browse files

Merge branch 'feature3523_027'

parents d4729524 841c4aa7
Loading
Loading
Loading
Loading

changes/feature3523

0 → 100644
+3 −0
Original line number Diff line number Diff line
  o Major features (controller):
    - New HSPOST command to upload a hidden service descriptor. 
      Closes ticket 3523. Patch by "DonnchaC".
+193 −5
Original line number Diff line number Diff line
@@ -36,17 +36,14 @@
#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
#include "rendcommon.h"
#include "rendservice.h"
#include "reasons.h"
#include "rendclient.h"
#include "rendcommon.h"
#include "rendservice.h"
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
#include "rendclient.h"
#include "rendcommon.h"

#ifndef _WIN32
#include <pwd.h>
@@ -170,6 +167,8 @@ static int handle_control_usefeature(control_connection_t *conn,
                                     const char *body);
static int handle_control_hsfetch(control_connection_t *conn, uint32_t len,
                                  const char *body);
static int handle_control_hspost(control_connection_t *conn, uint32_t len,
                                 const char *body);
static int handle_control_add_onion(control_connection_t *conn, uint32_t len,
                                    const char *body);
static int handle_control_del_onion(control_connection_t *conn, uint32_t len,
@@ -3424,6 +3423,101 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len,
  return 0;
}

/** Implementation for the HSPOST command. */
static int
handle_control_hspost(control_connection_t *conn,
                      uint32_t len,
                      const char *body)
{
  static const char *opt_server = "SERVER=";
  smartlist_t *args = smartlist_new();
  smartlist_t *hs_dirs = NULL;
  const char *encoded_desc = body;
  size_t encoded_desc_len = len;

  char *cp = memchr(body, '\n', len);
  char *argline = tor_strndup(body, cp-body);

  /* If any SERVER= options were specified, try parse the options line */
  if (!strcasecmpstart(argline, opt_server)) {
    /* encoded_desc begins after a newline character */
    cp = cp + 1;
    encoded_desc = cp;
    encoded_desc_len = len-(cp-body);

    smartlist_split_string(args, argline, " ",
                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
    SMARTLIST_FOREACH_BEGIN(args, const char *, arg) {
      if (!strcasecmpstart(arg, opt_server)) {
        const char *server = arg + strlen(opt_server);
        const node_t *node = node_get_by_hex_id(server);

        if (!node) {
          connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n",
                                   server);
          goto done;
        }
        if (!node->rs->is_hs_dir) {
          connection_printf_to_buf(conn, "552 Server \"%s\" is not a HSDir"
                                         "\r\n", server);
          goto done;
        }
        /* Valid server, add it to our local list. */
        if (!hs_dirs)
          hs_dirs = smartlist_new();
        smartlist_add(hs_dirs, node->rs);
      } else {
        connection_printf_to_buf(conn, "512 Unexpected argument \"%s\"\r\n",
                                 arg);
        goto done;
      }
    } SMARTLIST_FOREACH_END(arg);
  }

  /* Read the dot encoded descriptor, and parse it. */
  rend_encoded_v2_service_descriptor_t *desc =
      tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t));
  read_escaped_data(encoded_desc, encoded_desc_len, &desc->desc_str);

  rend_service_descriptor_t *parsed = NULL;
  char *intro_content = NULL;
  size_t intro_size;
  size_t encoded_size;
  const char *next_desc;
  if (!rend_parse_v2_service_descriptor(&parsed, desc->desc_id, &intro_content,
                                        &intro_size, &encoded_size,
                                        &next_desc, desc->desc_str, 1)) {
    /* Post the descriptor. */
    char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
    if (!rend_get_service_id(parsed->pk, serviceid)) {
      smartlist_t *descs = smartlist_new();
      smartlist_add(descs, desc);

      /* We are about to trigger HS descriptor upload so send the OK now
       * because after that 650 event(s) are possible so better to have the
       * 250 OK before them to avoid out of order replies. */
      send_control_done(conn);

      /* Trigger the descriptor upload */
      directory_post_to_hs_dir(parsed, descs, hs_dirs, serviceid, 0);
      smartlist_free(descs);
    }

    rend_service_descriptor_free(parsed);
  } else {
    connection_printf_to_buf(conn, "554 Invalid descriptor\r\n");
  }

  tor_free(intro_content);
  rend_encoded_v2_service_descriptor_free(desc);
 done:
  tor_free(argline);
  smartlist_free(hs_dirs); /* Contents belong to the rend service code. */
  SMARTLIST_FOREACH(args, char *, arg, tor_free(arg));
  smartlist_free(args);
  return 0;
}

/** Called when we get a ADD_ONION command; parse the body, and set up
 * the new ephemeral Onion Service. */
static int
@@ -4075,6 +4169,9 @@ connection_control_process_inbuf(control_connection_t *conn)
  } else if (!strcasecmp(conn->incoming_cmd, "HSFETCH")) {
    if (handle_control_hsfetch(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "+HSPOST")) {
    if (handle_control_hspost(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "ADD_ONION")) {
    int ret = handle_control_add_onion(conn, cmd_data_len, args);
    memwipe(args, 0, cmd_data_len); /* Scrub the private key. */
@@ -5802,6 +5899,31 @@ control_event_hs_descriptor_requested(const rend_data_t *rend_query,
                     desc_id_base32);
}

/** send HS_DESC upload event.
 *
 * <b>service_id</b> is the descriptor onion address.
 * <b>hs_dir</b> is the description of contacting hs directory.
 * <b>desc_id_base32</b> is the ID of requested hs descriptor.
 */
void
control_event_hs_descriptor_upload(const char *service_id,
                                   const char *id_digest,
                                   const char *desc_id_base32)
{
  if (!service_id || !id_digest || !desc_id_base32) {
    log_warn(LD_BUG, "Called with service_digest==%p, "
             "desc_id_base32==%p, id_digest==%p", service_id,
             desc_id_base32, id_digest);
    return;
  }

  send_control_event(EVENT_HS_DESC, ALL_FORMATS,
                     "650 HS_DESC UPLOAD %s UNKNOWN %s %s\r\n",
                     service_id,
                     node_describe_longname_by_id(id_digest),
                     desc_id_base32);
}

/** send HS_DESC event after got response from hs directory.
 *
 * NOTE: this is an internal function used by following functions:
@@ -5840,9 +5962,43 @@ control_event_hs_descriptor_receive_end(const char *action,
  tor_free(reason_field);
}

/** send HS_DESC event after got response from hs directory.
 *
 * NOTE: this is an internal function used by following functions:
 * control_event_hs_descriptor_uploaded
 * control_event_hs_descriptor_upload_failed
 *
 * So do not call this function directly.
 */
void
control_event_hs_descriptor_upload_end(const char *action,
                                       const char *id_digest,
                                       const char *reason)
{
  char *reason_field = NULL;

  if (!action || !id_digest) {
    log_warn(LD_BUG, "Called with action==%p, id_digest==%p", action,
             id_digest);
    return;
  }

  if (reason) {
    tor_asprintf(&reason_field, " REASON=%s", reason);
  }

  send_control_event(EVENT_HS_DESC, ALL_FORMATS,
                     "650 HS_DESC %s UNKNOWN UNKNOWN %s%s\r\n",
                     action,
                     node_describe_longname_by_id(id_digest),
                     reason_field ? reason_field : "");

  tor_free(reason_field);
}

/** send HS_DESC RECEIVED event
 *
 * called when a we successfully received a hidden service descriptor.
 * called when we successfully received a hidden service descriptor.
 */
void
control_event_hs_descriptor_received(const char *onion_address,
@@ -5857,6 +6013,21 @@ control_event_hs_descriptor_received(const char *onion_address,
                                          auth_type, id_digest, NULL);
}

/** send HS_DESC UPLOADED event
 *
 * called when we successfully uploaded a hidden service descriptor.
 */
void
control_event_hs_descriptor_uploaded(const char *id_digest)
{
  if (!id_digest) {
    log_warn(LD_BUG, "Called with id_digest==%p",
             id_digest);
    return;
  }
  control_event_hs_descriptor_upload_end("UPLOADED", id_digest, NULL);
}

/** Send HS_DESC event to inform controller that query <b>rend_query</b>
 * failed to retrieve hidden service descriptor identified by
 * <b>id_digest</b>. If <b>reason</b> is not NULL, add it to REASON=
@@ -5909,6 +6080,23 @@ control_event_hs_descriptor_content(const char *onion_address,
  tor_free(esc_content);
}

/** Send HS_DESC event to inform controller upload of hidden service
 * descriptor identified by <b>id_digest</b> failed. If <b>reason</b>
 * is not NULL, add it to REASON= field.
 */
void
control_event_hs_descriptor_upload_failed(const char *id_digest,
                                          const char *reason)
{
  if (!id_digest) {
    log_warn(LD_BUG, "Called with id_digest==%p",
             id_digest);
    return;
  }
  control_event_hs_descriptor_upload_end("UPLOAD_FAILED",
                                         id_digest, reason);
}

/** Free any leftover allocated memory of the control.c subsystem. */
void
control_free_all(void)
+9 −0
Original line number Diff line number Diff line
@@ -106,18 +106,27 @@ MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest));
void control_event_hs_descriptor_requested(const rend_data_t *rend_query,
                                           const char *desc_id_base32,
                                           const char *hs_dir);
void control_event_hs_descriptor_upload(const char *service_id,
                                        const char *desc_id_base32,
                                        const char *hs_dir);
void control_event_hs_descriptor_receive_end(const char *action,
                                             const char *onion_address,
                                             rend_auth_type_t auth_type,
                                             const char *id_digest,
                                             const char *reason);
void control_event_hs_descriptor_upload_end(const char *action,
                                            const char *hs_dir,
                                            const char *reason);
void control_event_hs_descriptor_received(const char *onion_address,
                                          rend_auth_type_t auth_type,
                                          const char *id_digest);
void control_event_hs_descriptor_uploaded(const char *hs_dir);
void control_event_hs_descriptor_failed(const char *onion_address,
                                        rend_auth_type_t auth_type,
                                        const char *id_digest,
                                        const char *reason);
void control_event_hs_descriptor_upload_failed(const char *hs_dir,
                                               const char *reason);
void control_event_hs_descriptor_content(const char *onion_address,
                                         const char *desc_id,
                                         const char *hsdir_fp,
+6 −0
Original line number Diff line number Diff line
@@ -2185,6 +2185,9 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
  }

  if (conn->base_.purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) {
    #define SEND_HS_DESC_UPLOAD_FAILED_EVENT(reason) ( \
      control_event_hs_descriptor_upload_failed(conn->identity_digest, \
                                                reason) )
    log_info(LD_REND,"Uploaded rendezvous descriptor (status %d "
             "(%s))",
             status_code, escaped(reason));
@@ -2193,17 +2196,20 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
        log_info(LD_REND,
                 "Uploading rendezvous descriptor: finished with status "
                 "200 (%s)", escaped(reason));
        control_event_hs_descriptor_uploaded(conn->identity_digest);
        break;
      case 400:
        log_warn(LD_REND,"http status 400 (%s) response from dirserver "
                 "'%s:%d'. Malformed rendezvous descriptor?",
                 escaped(reason), conn->base_.address, conn->base_.port);
        SEND_HS_DESC_UPLOAD_FAILED_EVENT("UPLOAD_REJECTED");
        break;
      default:
        log_warn(LD_REND,"http status %d (%s) response unexpected (server "
                 "'%s:%d').",
                 status_code, escaped(reason), conn->base_.address,
                 conn->base_.port);
        SEND_HS_DESC_UPLOAD_FAILED_EVENT("UNEXPECTED");
        break;
    }
  }
+31 −17
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include "circuitlist.h"
#include "circuituse.h"
#include "config.h"
#include "control.h"
#include "directory.h"
#include "main.h"
#include "networkstatus.h"
@@ -3098,14 +3099,16 @@ find_intro_point(origin_circuit_t *circ)
  return NULL;
}

/** Determine the responsible hidden service directories for the
 * rend_encoded_v2_service_descriptor_t's in <b>descs</b> and upload them;
 * <b>service_id</b> and <b>seconds_valid</b> are only passed for logging
 * purposes. */
static void
/** Upload the rend_encoded_v2_service_descriptor_t's in <b>descs</b>
 * associated with the rend_service_descriptor_t <b>renddesc</b> to
 * the responsible hidden service directories OR the hidden service
 * directories specified by <b>hs_dirs</b>; <b>service_id</b> and
 * <b>seconds_valid</b> are only passed for logging purposes.
 */
void
directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
                         smartlist_t *descs, const char *service_id,
                         int seconds_valid)
                         smartlist_t *descs, smartlist_t *hs_dirs,
                         const char *service_id, int seconds_valid)
{
  int i, j, failed_upload = 0;
  smartlist_t *responsible_dirs = smartlist_new();
@@ -3113,14 +3116,21 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
  routerstatus_t *hs_dir;
  for (i = 0; i < smartlist_len(descs); i++) {
    rend_encoded_v2_service_descriptor_t *desc = smartlist_get(descs, i);
    /** If any HSDirs are specified, they should be used instead of
     *  the responsible directories */
    if (hs_dirs && smartlist_len(hs_dirs) > 0) {
      smartlist_add_all(responsible_dirs, hs_dirs);
    } else {
      /* Determine responsible dirs. */
      if (hid_serv_get_responsible_directories(responsible_dirs,
                                               desc->desc_id) < 0) {
        log_warn(LD_REND, "Could not determine the responsible hidden service "
                          "directories to post descriptors to.");
      smartlist_free(responsible_dirs);
      smartlist_free(successful_uploads);
      return;
        control_event_hs_descriptor_upload(service_id,
                                           "UNKNOWN",
                                           "UNKNOWN");
        goto done;
      }
    }
    for (j = 0; j < smartlist_len(responsible_dirs); j++) {
      char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
@@ -3160,6 +3170,9 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
               hs_dir->nickname,
               hs_dir_ip,
               hs_dir->or_port);
      control_event_hs_descriptor_upload(service_id,
                                         hs_dir->identity_digest,
                                         desc_id_base32);
      tor_free(hs_dir_ip);
      /* Remember successful upload to this router for next time. */
      if (!smartlist_contains_digest(successful_uploads,
@@ -3187,6 +3200,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
      }
    });
  }
 done:
  smartlist_free(responsible_dirs);
  smartlist_free(successful_uploads);
}
@@ -3251,7 +3265,7 @@ upload_service_descriptor(rend_service_t *service)
        rend_get_service_id(service->desc->pk, serviceid);
        log_info(LD_REND, "Launching upload for hidden service %s",
                     serviceid);
        directory_post_to_hs_dir(service->desc, descs, serviceid,
        directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
                                 seconds_valid);
        /* Free memory for descriptors. */
        for (i = 0; i < smartlist_len(descs); i++)
@@ -3280,7 +3294,7 @@ upload_service_descriptor(rend_service_t *service)
            smartlist_free(client_cookies);
            return;
          }
          directory_post_to_hs_dir(service->desc, descs, serviceid,
          directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
                                   seconds_valid);
          /* Free memory for descriptors. */
          for (i = 0; i < smartlist_len(descs); i++)
Loading