Esempio n. 1
0
bud_error_t bud_config_load_file(bud_config_t* config,
                                 const char* path,
                                 const char** out) {
  bud_error_t err;
  char* content;

  /* Check if we already have cache entry */
  content = bud_hashmap_get(&config->files.hashmap, path, strlen(path));
  if (content != NULL) {
    *out = content;
    return bud_ok();
  }

  ASSERT(config->loop != NULL, "Loop should be present");
  err = bud_read_file_by_path(config->loop, path, &content);
  if (!bud_is_ok(err))
    return err;

  err = bud_hashmap_insert(&config->files.hashmap,
                           path,
                           strlen(path),
                           content);
  if (!bud_is_ok(err)) {
    free(content);
    return err;
  }

  *out = content;
  return bud_ok();
}
Esempio n. 2
0
File: config.c Progetto: indutny/bud
bud_error_t bud_config_load_backend(bud_config_t* config,
                                    JSON_Object* obj,
                                    bud_config_backend_t* backend,
                                    bud_hashmap_t* map,
                                    unsigned int* ext_count) {
  bud_error_t err;
  JSON_Value* val;
  const char* external;
  int r;

  bud_config_load_addr(obj, (bud_config_addr_t*) backend);
  backend->config = config;
  backend->xforward = -1;

  val = json_object_get_value(obj, "proxyline");
  if (json_value_get_type(val) == JSONString) {
    const char* pline;

    pline = json_value_get_string(val);
    if (strcmp(pline, "haproxy") == 0)
      backend->proxyline = kBudProxylineHAProxy;
    else if (strcmp(pline, "json") == 0)
      backend->proxyline = kBudProxylineJSON;
    else
      return bud_error_dstr(kBudErrProxyline, pline);
  } else {
    backend->proxyline = val != NULL && json_value_get_boolean(val) ?
        kBudProxylineHAProxy :
        kBudProxylineNone;
  }

  val = json_object_get_value(obj, "x-forward");
  if (val != NULL)
    backend->xforward = json_value_get_boolean(val);

  /* Set defaults here to use them in sni.c */
  bud_config_set_backend_defaults(backend);

  r = bud_config_str_to_addr(backend->host, backend->port, &backend->addr);
  if (r != 0)
    return bud_error_num(kBudErrPton, r);

  external = json_object_get_string(obj, "external");
  if (external == NULL)
    return bud_ok();

  /* Insert backend into a hashmap */
  err = bud_hashmap_insert(map, external, strlen(external), backend);
  if (!bud_is_ok(err))
    return err;

  (*ext_count)++;

  return bud_ok();
}
Esempio n. 3
0
bud_error_t bud_context_set_ticket(bud_context_t* context,
                                   const char* ticket,
                                   size_t size,
                                   bud_encoding_t enc) {
  bud_config_t* config;
  bud_context_t* root;
  size_t max_len;
  int i;

  config = context->config;
  root = &config->contexts[0];

  if (enc == kBudEncodingRaw) {
    if (size != sizeof(context->ticket_key_storage))
      return bud_error(kBudErrSmallTicketKey);

    memcpy(context->ticket_key_storage, ticket, size);
  } else {
    ASSERT(enc == kBudEncodingBase64, "Unexpected encoding of ticket key");

    max_len = sizeof(context->ticket_key_storage);
    if (bud_base64_decode(context->ticket_key_storage,
                          max_len,
                          ticket,
                          size) < max_len) {
      return bud_error(kBudErrSmallTicketKey);
    }
  }

  context->ticket_key_on = 1;
  if (context->ctx != NULL) {
    SSL_CTX_set_tlsext_ticket_keys(context->ctx,
                                   context->ticket_key_storage,
                                   sizeof(context->ticket_key_storage));
  }

  if (context != root)
    return bud_ok();

  /* Update ticket key in dependent contexts */
  for (i = 0; i < config->context_count + 1; i++) {
    bud_context_t* cur;

    cur = &config->contexts[i];
    if (cur->ticket_key_on || cur->ctx == NULL)
      continue;

    SSL_CTX_set_tlsext_ticket_keys(cur->ctx,
                                   cur->ticket_key_storage,
                                   sizeof(cur->ticket_key_storage));
  }

  return bud_ok();
}
Esempio n. 4
0
File: ocsp.c Progetto: laggyluke/bud
bud_error_t bud_client_ocsp_stapling(bud_client_t* client) {
  bud_config_t* config;
  bud_context_t* context;
  bud_error_t err;
  const char* id;
  size_t id_size;

  config = client->config;

  if (client->sni_ctx.ctx != NULL) {
    /* Async SNI success */
    context = &client->sni_ctx;
  } else if (client->hello.servername_len != 0) {
    /* Matching context */
    context = bud_config_select_context(config,
                                        client->hello.servername,
                                        client->hello.servername_len);
  } else {
    /* Default context */
    context = &config->contexts[0];
  }

  /* Cache context to prevent second search in OpenSSL's callback */
  if (!SSL_set_ex_data(client->ssl, kBudSSLSNIIndex, context)) {
    err = bud_error(kBudErrStaplingSetData);
    goto fatal;
  }

  id = bud_context_get_ocsp_id(context, &id_size);

  /* Certificate has no OCSP id */
  if (id == NULL)
    return bud_ok();

  /* Request backend for cached respose first */
  client->stapling_cache_req = bud_http_get(config->stapling.pool,
                                            config->stapling.query_fmt,
                                            id,
                                            id_size,
                                            bud_client_stapling_cache_req_cb,
                                            &err);
  client->stapling_cache_req->data = client;

  if (!bud_is_ok(err))
    goto fatal;

  client->hello_parse = kBudProgressRunning;
  return bud_ok();

fatal:
  return err;
}
Esempio n. 5
0
char* bud_config_encode_npn(bud_config_t* config,
                            const JSON_Array* npn,
                            size_t* len,
                            bud_error_t* err) {
  int i;
  char* npn_line;
  size_t npn_line_len;
  unsigned int offset;
  int npn_count;
  const char* npn_item;
  int npn_item_len;

  /* Try global defaults */
  if (npn == NULL)
    npn = config->frontend.npn;
  if (npn == NULL) {
    *err = bud_ok();
    *len = 0;
    return NULL;
  }

  /* Calculate storage requirements */
  npn_count = json_array_get_count(npn);
  npn_line_len = 0;
  for (i = 0; i < npn_count; i++)
    npn_line_len += 1 + strlen(json_array_get_string(npn, i));

  if (npn_line_len != 0) {
    npn_line = malloc(npn_line_len);
    if (npn_line == NULL) {
      *err = bud_error_str(kBudErrNoMem, "NPN copy");
      return NULL;
    }
  }

  /* Fill npn line */
  for (i = 0, offset = 0; i < npn_count; i++) {
    npn_item = json_array_get_string(npn, i);
    npn_item_len = strlen(npn_item);

    npn_line[offset++] = npn_item_len;
    memcpy(npn_line + offset, npn_item, npn_item_len);
    offset += npn_item_len;
  }
  ASSERT(offset == npn_line_len, "NPN Line overflow");

  *len = npn_line_len;
  *err = bud_ok();

  return npn_line;
}
Esempio n. 6
0
bud_error_t bud_config_set_ticket_raw(bud_config_t* config,
                                      uint32_t index,
                                      uint32_t size,
                                      const char* data) {
  bud_error_t err;
  int i;

  err = bud_context_set_ticket(&config->contexts[index],
                               data,
                               size,
                               kBudEncodingRaw);
  if (!bud_is_ok(err))
    return err;


  if (config->is_worker) {
    bud_clog(config,
             kBudLogInfo,
             "Worker updated ticket key for context: %d",
             index);
    return bud_ok();
  }


  /* Retransmit */
  for (i = 0; i < config->worker_count; i++) {
    bud_error_t worker_err;
    if (config->workers[i].state & kBudWorkerStateActive) {
      worker_err = bud_ipc_set_ticket(&config->workers[i].ipc,
                                      index,
                                      data,
                                      size);

      /* Send to everyone anyway */
      if (!bud_is_ok(worker_err))
        err = worker_err;
    }
  }

  if (bud_is_ok(err)) {
    bud_clog(config,
             kBudLogInfo,
             "Master retransmitted ticket key for context: %d",
             index);
  }

  return bud_ok();
}
Esempio n. 7
0
bud_error_t bud_config_drop_privileges(bud_config_t* config) {
#ifndef _WIN32
  if (config->user != NULL) {
    struct passwd* p;

    p = getpwnam(config->user);
    if (p == NULL)
      return bud_error_dstr(kBudErrInvalidUser, config->user);

    if (setgid(p->pw_gid) != 0)
      return bud_error_num(kBudErrSetgid, errno);
    if (setuid(p->pw_uid) != 0)
      return bud_error_num(kBudErrSetuid, errno);
  } else if (config->group != NULL) {
    struct group* g;

    g = getgrnam(config->group);
    if (g == NULL)
      return bud_error_dstr(kBudErrInvalidGroup, config->group);

    if (setgid(g->gr_gid) != 0)
      return bud_error_num(kBudErrSetgid, errno);
  }
#endif  /* !_WIN32 */

  return bud_ok();
}
Esempio n. 8
0
bud_error_t bud_config_reload(bud_config_t* config) {
  bud_error_t err;
  bud_config_t* loaded;
  bud_config_t restore;

  loaded = bud_config_load(config->path, config->inlined, &err);
  if (!bud_is_ok(err))
    return err;

  memset(&restore, 0, sizeof(restore));
  bud_config_copy(&restore, config);
  bud_config_copy(config, loaded);

  /* Initialize config with new params */
  err = bud_config_init(config);

  /* Restore everything on failure */
  if (!bud_is_ok(err)) {
    bud_config_copy(config, &restore);
    bud_config_free(loaded);
    return err;
  }

  free(loaded);
  bud_config_destroy(&restore);
  return bud_ok();
}
Esempio n. 9
0
bud_error_t bud_config_format_proxyline(bud_config_t* config) {
  int r;
  char host[INET6_ADDRSTRLEN];
  struct sockaddr_in* addr4;
  struct sockaddr_in6* addr6;

  addr4 = (struct sockaddr_in*) &config->frontend.addr;
  addr6 = (struct sockaddr_in6*) &config->frontend.addr;

  if (config->frontend.addr.ss_family == AF_INET)
    r = uv_inet_ntop(AF_INET, &addr4->sin_addr, host, sizeof(host));
  else
    r = uv_inet_ntop(AF_INET6, &addr6->sin6_addr, host, sizeof(host));
  if (r != 0)
    return bud_error(kBudErrNtop);

  r = snprintf(config->proxyline_fmt,
               sizeof(config->proxyline_fmt),
               "PROXY %%s %%s %s %%hu %hu\r\n",
               host,
               config->frontend.port);
  ASSERT(r < (int) sizeof(config->proxyline_fmt),
         "Proxyline format overflowed");

  return bud_ok();
}
Esempio n. 10
0
bud_error_t bud_parse_record_header(const uint8_t* data,
                                    size_t size,
                                    bud_parser_state_t* state) {
  /* >= 5 bytes for header parsing */
  if (size < 5)
    return bud_error(kBudErrParserNeedMore);

  if (data[0] == kChangeCipherSpec ||
      data[0] == kAlert ||
      data[0] == kHandshake ||
      data[0] == kApplicationData) {
    state->frame_len = (data[3] << 8) + data[4];
    state->body_offset = 5;
  } else {
    return bud_error_str(kBudErrParserErr, "Unknown record type");
  }

  /*
   * Sanity check (too big frame, or too small)
   * Let OpenSSL handle it
   */
  if (state->frame_len >= kMaxTLSFrameLen)
    return bud_error_str(kBudErrParserErr, "Record length OOB");

  return bud_ok();
}
Esempio n. 11
0
bud_error_t bud_worker_finalize(bud_config_t* config) {
  uv_close((uv_handle_t*) config->signal.sighup, bud_worker_close_cb);
  config->signal.sighup = NULL;
  bud_ipc_close(&config->ipc);

  return bud_ok();
}
Esempio n. 12
0
bud_error_t bud_config_verify_npn(const JSON_Array* npn) {
  int i;
  int npn_count;

  if (npn == NULL)
    return bud_ok();

  npn_count = json_array_get_count(npn);
  for (i = 0; i < npn_count; i++) {
    if (json_value_get_type(json_array_get_value(npn, i)) == JSONString)
      continue;
    return bud_error(kBudErrNPNNonString);
  }

  return bud_ok();
}
Esempio n. 13
0
bud_error_t bud_parse_header(const uint8_t* data,
                             size_t size,
                             bud_parser_state_t* state) {
  bud_error_t err;

  /* >= 5 + frame size bytes for frame parsing */
  if (state->body_offset + state->frame_len > size)
    return bud_error(kBudErrParserNeedMore);

  if (data[state->body_offset] == kClientHello) {
    /* Clear hello, just in case if we will return bud_ok() ;) */
    memset(state->hello, 0, sizeof(*state->hello));

    err = bud_parse_tls_client_hello(data, size, state);
    if (!bud_is_ok(err))
      return err;

    /* Check if we overflowed (do not reply with any private data) */
    if (state->hello->session == NULL ||
        state->hello->session_len > 32 ||
        state->hello->session + state->hello->session_len >
            (const char*) data + size) {
      return bud_error_str(kBudErrParserErr, "Session id overflow");
    }
  } else {
    return bud_error_str(kBudErrParserErr, "Unexpected first record");
  }

  return bud_ok();
}
Esempio n. 14
0
File: config.c Progetto: indutny/bud
bud_error_t bud_config_load_frontend(JSON_Object* obj,
                                     bud_config_frontend_t* frontend) {
  JSON_Value* val;

  bud_config_load_addr(obj, (bud_config_addr_t*) frontend);

  frontend->max_send_fragment = -1;
  frontend->allow_half_open = -1;
  frontend->reneg_limit = -1;
  if (obj == NULL)
    return bud_ok();

  frontend->security = json_object_get_string(obj, "security");
  frontend->reneg_window = json_object_get_number(obj, "reneg_window");
  val = json_object_get_value(obj, "reneg_limit");
  if (val != NULL)
    frontend->reneg_limit = json_value_get_number(val);

  val = json_object_get_value(obj, "max_send_fragment");
  if (val != NULL)
    frontend->max_send_fragment = json_value_get_number(val);
  val = json_object_get_value(obj, "allow_half_open");
  if (val != NULL)
    frontend->allow_half_open = json_value_get_boolean(val);

  return bud_config_load_frontend_ifaces(obj, &frontend->interface);
}
Esempio n. 15
0
bud_error_t bud_config_verify_all_strings(const JSON_Array* arr,
                                          const char* name) {
  int i;
  int count;

  if (arr == NULL)
    return bud_ok();

  count = json_array_get_count(arr);
  for (i = 0; i < count; i++) {
    if (json_value_get_type(json_array_get_value(arr, i)) == JSONString)
      continue;
    return bud_error_dstr(kBudErrNonString, name);
  }

  return bud_ok();
}
Esempio n. 16
0
bud_error_t bud_worker_finalize(bud_config_t* config) {
    uv_close((uv_handle_t*) config->ipc, bud_worker_close_cb);
#ifndef _WIN32
    uv_close((uv_handle_t*) config->signal.sighup, bud_worker_close_cb);
#endif  /* !_WIN32 */
    config->ipc = NULL;

    return bud_ok();
}
Esempio n. 17
0
File: config.c Progetto: indutny/bud
bud_error_t bud_config_load_frontend_ifaces(
    JSON_Object* obj,
    bud_config_frontend_interface_t* interface) {
  JSON_Array* arr;
  int i;

  arr = json_object_get_array(obj, "interfaces");
  interface->count = arr == NULL ? 0 : json_array_get_count(arr);
  if (interface->count == 0)
    return bud_ok();

  interface->list = calloc(interface->count, sizeof(*interface->list));
  if (interface->list == NULL)
    return bud_error_str(kBudErrNoMem, "bud_frontend_interface_t");

  for (i = 0; i < interface->count; i++)
    bud_config_load_addr(json_array_get_object(arr, i), &interface->list[i]);

  return bud_ok();
}
Esempio n. 18
0
bud_error_t bud_config_load_ca_file(X509_STORE** store, const char* filename) {
  BIO* b;
  X509* x509;
  bud_error_t err;

  b = BIO_new_file(filename, "r");
  if (b == NULL)
    return bud_error_dstr(kBudErrLoadCert, filename);

  x509 = NULL;
  *store = X509_STORE_new();
  if (*store == NULL) {
    err = bud_error_dstr(kBudErrNoMem, "CA store");
    goto fatal;
  }

  while ((x509 = PEM_read_bio_X509(b, NULL, NULL, NULL)) != NULL) {
    if (x509 == NULL) {
      err = bud_error_dstr(kBudErrParseCert, filename);
      goto fatal;
    }

    if (X509_STORE_add_cert(*store, x509) != 1) {
      err = bud_error(kBudErrAddCert);
      goto fatal;
    }
    X509_free(x509);
    x509 = NULL;
  }

  err = bud_ok();

fatal:
  if (x509 != NULL)
    X509_free(x509);
  BIO_free_all(b);
  return bud_ok();
}
Esempio n. 19
0
bud_error_t bud_config_load_frontend(JSON_Object* obj,
                                     bud_config_frontend_t* frontend) {
  bud_error_t err;
  JSON_Value* val;

  bud_config_load_addr(obj, (bud_config_addr_t*) frontend);

  frontend->server_preference = -1;
  frontend->ssl3 = -1;
  frontend->max_send_fragment = -1;
  frontend->allow_half_open = -1;
  if (obj == NULL)
    return bud_ok();

  frontend->security = json_object_get_string(obj, "security");
  frontend->ciphers = json_object_get_string(obj, "ciphers");
  frontend->ecdh = json_object_get_string(obj, "ecdh");
  frontend->cert_file = json_object_get_string(obj, "cert");
  frontend->key_file = json_object_get_string(obj, "key");
  frontend->reneg_window = json_object_get_number(obj, "reneg_window");
  frontend->reneg_limit = json_object_get_number(obj, "reneg_limit");
  frontend->ticket_key = json_object_get_string(obj, "ticket_key");

  /* Get and verify NPN */
  frontend->npn = json_object_get_array(obj, "npn");
  err = bud_config_verify_npn(frontend->npn);
  if (!bud_is_ok(err))
    goto fatal;

  val = json_object_get_value(obj, "server_preference");
  if (val != NULL)
    frontend->server_preference = json_value_get_boolean(val);
  val = json_object_get_value(obj, "ssl3");
  if (val != NULL)
    frontend->ssl3 = json_value_get_boolean(val);
  val = json_object_get_value(obj, "max_send_fragment");
  if (val != NULL)
    frontend->max_send_fragment = json_value_get_number(val);
  val = json_object_get_value(obj, "allow_half_open");
  if (val != NULL)
    frontend->allow_half_open = json_value_get_boolean(val);

fatal:
  return err;
}
Esempio n. 20
0
File: config.c Progetto: indutny/bud
bud_error_t bud_config_format_proxyline(bud_config_t* config) {
  int r;
  char host[INET6_ADDRSTRLEN];
  struct sockaddr_in* addr4;
  struct sockaddr_in6* addr6;

  addr4 = (struct sockaddr_in*) &config->frontend.addr;
  addr6 = (struct sockaddr_in6*) &config->frontend.addr;

  if (config->frontend.addr.ss_family == AF_INET)
    r = uv_inet_ntop(AF_INET, &addr4->sin_addr, host, sizeof(host));
  else
    r = uv_inet_ntop(AF_INET6, &addr6->sin6_addr, host, sizeof(host));
  if (r != 0)
    return bud_error(kBudErrNtop);

  r = snprintf(config->proxyline_fmt.haproxy,
               sizeof(config->proxyline_fmt.haproxy),
               "PROXY %%s %%s %s %%hu %hu\r\n",
               host,
               config->frontend.port);
  ASSERT(r < (int) sizeof(config->proxyline_fmt.haproxy),
         "Proxyline format overflowed");

  r = snprintf(config->proxyline_fmt.json,
               sizeof(config->proxyline_fmt.json),
               "BUD {\"family\":\"%%s\","
                   "\"bud\":{\"host\":\"%s\",\"port\":%hu},"
                   "\"peer\":{"
                     "\"host\":\"%%s\","
                     "\"port\":%%hu,"
                     "\"cn\":%%c%%s%%c,"
                     "\"dn\":%%c%%s%%c}"
                   "}\r\n",
               host,
               config->frontend.port);
  ASSERT(r < (int) sizeof(config->proxyline_fmt.json),
         "Proxyline format overflowed");

  return bud_ok();
}
Esempio n. 21
0
bud_error_t bud_server_new(bud_config_t* config, bud_config_addr_t* addr) {
  int r;
  bud_error_t err;
  bud_server_t* server;

  server = calloc(1, sizeof(*server));

  server->config = config;

  /* Initialize tcp handle */
  r = uv_tcp_init(config->loop, &server->tcp);
  if (r != 0) {
    err = bud_error_num(kBudErrTcpServerInit, r);
    goto failed_tcp_init;
  }

  r = uv_tcp_bind(&server->tcp, (struct sockaddr*) &addr->addr, 0);
  if (r != 0) {
    err = bud_error_num(kBudErrTcpServerBind, r);
    goto failed_bind;
  }

  r = uv_listen((uv_stream_t*) &server->tcp, 256, bud_server_connection_cb);
  if (r != 0) {
    err = bud_error_num(kBudErrServerListen, r);
    goto failed_bind;
  }

  server->prev = config->server;
  config->server = server;
  return bud_ok();

failed_bind:
  uv_close((uv_handle_t*) &server->tcp, bud_server_close_cb);
  return err;

failed_tcp_init:
  free(server);

  return err;
}
Esempio n. 22
0
bud_error_t bud_create_servers(bud_config_t* config) {
  bud_error_t err;
  int i;

  if (config->frontend.interface.count == 0) {
    err = bud_server_new(config, (bud_config_addr_t*) &config->frontend);
    if (!bud_is_ok(err))
      goto fatal;
  }

  for (i = 0; i < config->frontend.interface.count; i++) {
    err = bud_server_new(config, &config->frontend.interface.list[i]);
    if (!bud_is_ok(err))
      goto fatal;
  }
  return bud_ok();

fatal:
  bud_free_servers(config);
  return err;
}
Esempio n. 23
0
bud_error_t bud_config_load_backend(bud_config_t* config,
                                    JSON_Object* obj,
                                    bud_config_backend_t* backend) {
  JSON_Value* val;

  bud_config_load_addr(obj, (bud_config_addr_t*) backend);
  backend->config = config;
  backend->proxyline = -1;
  backend->xforward = -1;

  val = json_object_get_value(obj, "proxyline");
  if (val != NULL)
    backend->proxyline = json_value_get_boolean(val);

  val = json_object_get_value(obj, "x-forward");
  if (val != NULL)
    backend->xforward = json_value_get_boolean(val);

  /* Set defaults here to use them in sni.c */
  bud_config_set_backend_defaults(backend);

  return bud_ok();
}
Esempio n. 24
0
bud_error_t bud_parse_tls_client_hello(const uint8_t* data,
                                       size_t size,
                                       bud_parser_state_t* state) {
  bud_error_t err;
  const uint8_t* body;
  size_t session_offset;
  size_t cipher_offset;
  uint16_t cipher_len;
  size_t comp_offset;
  uint8_t comp_len;
  size_t extension_offset;
  size_t ext_off;
  uint16_t ext_type;
  uint16_t ext_len;

  /* Skip frame header, hello header, protocol version and random data */
  session_offset = state->body_offset + 4 + 2 + 32;
  if (session_offset + 1 >= size)
    return bud_error_str(kBudErrParserErr, "Header OOB");

  body = data + session_offset;
  state->hello->session_len = *body;
  state->hello->session = (const char*) body + 1;

  cipher_offset = session_offset + 1 + state->hello->session_len;
  if (cipher_offset + 1 >= size)
    return bud_error_str(kBudErrParserErr, "Session OOB");

  cipher_len = (data[cipher_offset] << 8) + data[cipher_offset + 1];
  comp_offset = cipher_offset + 2 + cipher_len;
  if (comp_offset > size)
    return bud_error_str(kBudErrParserErr, "Cipher suite OOB");

  comp_len = data[comp_offset];
  extension_offset = comp_offset + 1 + comp_len;
  if (extension_offset > size)
    return bud_error_str(kBudErrParserErr, "Compression methods OOB");

  /* No extensions present */
  if (extension_offset == size)
    return bud_ok();

  ext_off = extension_offset + 2;

  // Parse known extensions
  while (ext_off < size) {
    // Extension OOB
    if (ext_off + 4 > size)
      return bud_error_str(kBudErrParserErr, "Extension header OOB");

    ext_type = (data[ext_off] << 8) + data[ext_off + 1];
    ext_len = (data[ext_off + 2] << 8) + data[ext_off + 3];
    ext_off += 4;

    // Extension OOB
    if (ext_off + ext_len > size)
      return bud_error_str(kBudErrParserErr, "Extension body OOB");

    err = bud_parse_extension((extension_type_t) ext_type,
                              data + ext_off,
                              ext_len,
                              state);
    if (!bud_is_ok(err))
      return err;

    ext_off += ext_len;
  }

  // Extensions OOB failure
  if (ext_off > size)
    return bud_error_str(kBudErrParserErr, "Extensions OOB");

  return bud_ok();
}
Esempio n. 25
0
bud_error_t bud_worker(bud_config_t* config) {
  int r;
  bud_error_t err;

  bud_log(config, kBudLogDebug, "worker starting");

  config->loop = uv_default_loop();
  if (config->loop == NULL) {
    err = bud_error_str(kBudErrNoMem, "config->loop");
    goto fatal;
  }

  err = bud_ipc_init(&config->ipc, config);
  if (!bud_is_ok(err))
    goto fatal;

  config->ipc.client_cb = bud_worker_ipc_client_cb;

  err = bud_ipc_open(&config->ipc, 0);
  if (!bud_is_ok(err))
    goto failed_ipc_open;

  err = bud_ipc_start(&config->ipc);
  if (!bud_is_ok(err))
    goto failed_ipc_open;

  config->signal.sighup = malloc(sizeof(*config->signal.sighup));
  if (config->signal.sighup == NULL) {
    err = bud_error_str(kBudErrNoMem, "config->.sighup");
    goto failed_ipc_open;
  }

  config->signal.sighup->data = config;

  r = uv_signal_init(config->loop, config->signal.sighup);
  if (r != 0) {
    err = bud_error_num(kBudErrSignalInit, r);
    goto failed_signal_init;
  }

  r = uv_signal_start(config->signal.sighup, bud_worker_signal_cb, SIGHUP);
  if (r != 0) {
    err = bud_error_num(kBudErrSignalInit, r);
    goto failed_signal_start;
  }

#ifndef _WIN32
  /* Drop privileges */
  err = bud_config_drop_privileges(config);
  if (!bud_is_ok(err))
    goto failed_signal_start;
#endif  /* !_WIN32 */

  err = bud_ok();
  return err;

failed_signal_start:
  uv_close((uv_handle_t*) config->signal.sighup, bud_worker_close_cb);
  goto failed_ipc_open;

failed_signal_init:
  free(config->signal.sighup);
  config->signal.sighup = NULL;

failed_ipc_open:
  bud_ipc_close(&config->ipc);

fatal:
  return err;
}
Esempio n. 26
0
File: config.c Progetto: indutny/bud
bud_error_t bud_config_init(bud_config_t* config) {
  bud_error_t err;
  int i;
  int r;

  /* Get addresses of frontend and backend */
  r = bud_config_str_to_addr(config->frontend.host,
                             config->frontend.port,
                             &config->frontend.addr);
  if (r != 0)
    return bud_error_num(kBudErrPton, r);

  for (i = 0; i < config->frontend.interface.count; i++) {
    bud_config_addr_t* addr;

    addr = &config->frontend.interface.list[i];
    r = bud_config_str_to_addr(addr->host, addr->port, &addr->addr);
    if (r != 0)
      return bud_error_num(kBudErrPton, r);
  }

  err = bud_config_format_proxyline(config);
  if (!bud_is_ok(err))
    return err;

  i = 0;

  config->balance_e = bud_config_balance_to_enum(config->balance);

  /* At least one backend should be present for non-SNI balancing */
  if (config->contexts[0].backend.count == 0 &&
      config->balance_e != kBudBalanceSNI) {
    err = bud_error(kBudErrNoBackend);
    goto fatal;
  }

  /* Get indexes for SSL_set_ex_data()/SSL_get_ex_data() */
  if (kBudSSLClientIndex == -1) {
    kBudSSLConfigIndex = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
    kBudSSLClientIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
    kBudSSLSNIIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
    kBudSSLTicketKeyIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
    if (kBudSSLConfigIndex == -1 ||
        kBudSSLClientIndex == -1 ||
        kBudSSLSNIIndex == -1 ||
        kBudSSLTicketKeyIndex == -1) {
      err = bud_error(kBudErrNoSSLIndex);
      goto fatal;
    }
  }

#ifndef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
  if (config->context_count != 0) {
    err = bud_error(kBudErrSNINotSupported);
    goto fatal;
  }
#endif  /* !SSL_CTRL_SET_TLSEXT_SERVERNAME_CB */

  /* Allocate workers */
  if (!config->is_worker && config->worker_count != 0) {
    config->workers = calloc(config->worker_count, sizeof(*config->workers));
    if (config->workers == NULL) {
      err = bud_error_str(kBudErrNoMem, "workers");
      goto fatal;
    }
  }

  /* Initialize logger */
  config->logger = bud_logger_new(config, &err);
  if (!bud_is_ok(err))
    goto fatal;

  err = bud_config_init_tracing(&config->trace);
  if (!bud_is_ok(err))
    goto fatal;

  if (config->is_worker || config->worker_count == 0) {
    /* Connect to SNI server */
    if (config->sni.enabled) {
      config->sni.pool = bud_http_pool_new(config,
                                           config->sni.host,
                                           config->sni.port,
                                           &err);
      if (config->sni.pool == NULL)
        goto fatal;
    }

    /* Connect to OCSP Stapling server */
    if (config->stapling.enabled) {
      config->stapling.pool = bud_http_pool_new(config,
                                                config->stapling.host,
                                                config->stapling.port,
                                                &err);
      if (config->stapling.pool == NULL)
        goto fatal;
    }
  }

  /* Init all contexts */
  for (i = 0; i < config->context_count + 1; i++) {
    err = bud_context_init(config, &config->contexts[i]);
    if (!bud_is_ok(err))
      goto fatal;
  }

  return bud_ok();

fatal:
  /* Free all allocated contexts */
  do
    bud_context_free(&config->contexts[i--]);
  while (i >= 0);

  return err;
}
Esempio n. 27
0
File: config.c Progetto: indutny/bud
bud_error_t bud_config_new(int argc, char** argv, bud_config_t** out) {
  bud_error_t err;
  bud_config_t* config;
  int i;
  int r;
  size_t path_len;
  int c;
  int index;
  int loaded;

  config = calloc(1, sizeof(*config));
  if (config == NULL)
    return bud_error_str(kBudErrNoMem, "bud_config_t");

  loaded = 0;
  do {
    index = 0;
    c = getopt_long(argc, argv, bud_long_flags, bud_long_options, &index);
    switch (c) {
      case 'v':
        bud_print_version();
        err = bud_error(kBudErrSkip);
        goto fatal;
#ifndef _WIN32
      case 'd':
        config->is_daemon = 1;
#endif  /* !_WIN32 */
        break;
      case 'p':
      case 'i':
      case 'c':
        if (loaded) {
          err = bud_error(kBudErrMultipleConfigs);
          goto fatal;
        }
        loaded = 1;

        if (c == 'p') {
          config->piped = 1;
          config->path = kPipedConfigPath;
        } else {
          config->piped = 0;
          config->path = optarg;
          config->inlined = c == 'i';
        }
        break;
      case 1000:
        config->is_worker = 1;
        break;
      case 1001:
        bud_config_print_default();
        err = bud_error(kBudErrSkip);
        goto fatal;
      default:
        if (loaded)
          break;

        bud_print_help(argc, argv);
        goto no_config;
    }
  } while (c != -1);

  if (!config->piped) {
    config->piped_index = -1;
  } else {
    /* get_opt does not provide the argc offset so must manually retrieve it */
    for (i = 0; i < argc; i++) {
      if (strcmp(argv[i], "--piped-config") == 0 ||
          strcmp(argv[i], "-p") == 0) {
        config->piped_index = i;
        break;
      }
    }
  }

  /* CLI options */
  config->argc = argc;
  config->argv = argv;

  /* Get executable path */
  path_len = sizeof(config->exepath);
  r = uv_exepath(config->exepath, &path_len);
  ASSERT(path_len < sizeof(config->exepath), "Exepath OOB");

  if (r != 0) {
    bud_config_free(config);
    config = NULL;
    return bud_error_num(kBudErrExePath, r);
  }

  err = bud_hashmap_init(&config->files.hashmap, kBudFileCacheSize);
  if (!bud_is_ok(err))
    goto fatal;

  *out = config;
  return bud_ok();

no_config:
  free(config);
  return bud_error(kBudErrNoConfig);

fatal:
  free(config);
  return err;
}
Esempio n. 28
0
bud_error_t bud_parse_extension(extension_type_t type,
                                const uint8_t* data,
                                size_t size,
                                bud_parser_state_t* state) {
  uint32_t server_names_len;
  size_t offset;
  uint8_t name_type;
  uint16_t name_len;

  switch (type) {
    case kServername:
      if (size < 2)
        return bud_error_str(kBudErrParserErr, "Servername ext is too small");

      server_names_len = (data[0] << 8) + data[1];
      if (server_names_len + 2 > size)
        return bud_error_str(kBudErrParserErr, "Servername ext OOB");

      for (offset = 2; offset < 2 + server_names_len; ) {
        if (offset + 3 > size)
          return bud_error_str(kBudErrParserErr, "Servername name OOB");

        name_type = data[offset];
        if (name_type != kServernameHostname)
          return bud_error_str(kBudErrParserErr, "Servername type unexpected");

        name_len = (data[offset + 1] << 8) + data[offset + 2];
        offset += 3;
        if (offset + name_len > size)
          return bud_error_str(kBudErrParserErr, "Servername value OOB");

        state->hello->servername = (const char*) data + offset;
        state->hello->servername_len = name_len;
        offset += name_len;
      }
      break;
    case kStatusRequest:
      /* We are ignoring any data, just indicating the presence of extension */
      if (size < 5)
        return bud_error_str(kBudErrParserErr, "StatusRequest is too small");

      /* Unknown type, ignore it */
      if (data[0] != kStatusRequestOCSP)
        break;

      /* TODO(indutny): support extensions */
      if (size != 5)
        break;

      state->hello->ocsp_request = 1;
      break;
    case kTLSSessionTicket:
      state->hello->ticket_len = size;
      state->hello->ticket = (const char*) data + size;
      break;
    default:
      /* Ignore */
      break;
  }

  return bud_ok();
}
Esempio n. 29
0
bud_error_t bud_sni_from_json(bud_config_t* config,
                              struct json_value_t* json,
                              bud_context_t* ctx) {
  JSON_Object* obj;
  JSON_Value* val;
  const char* cert_str;
  const char* key_str;
  const char* pass_str;
  JSON_Array* cert_strs;
  JSON_Array* key_strs;
  JSON_Array* pass_strs;
  bud_error_t err;

  cert_str = NULL;
  key_str = NULL;
  pass_str = NULL;
  cert_strs = NULL;
  key_strs = NULL;
  pass_strs = NULL;

  obj = json_value_get_object(json);
  val = json_object_get_value(obj, "cert");
  if (json_value_get_type(val) == JSONString)
    cert_str = json_value_get_string(val);
  else
    cert_strs = json_value_get_array(val);
  val = json_object_get_value(obj, "key");
  if (json_value_get_type(val) == JSONString)
    key_str = json_value_get_string(val);
  else
    key_strs = json_value_get_array(val);
  val = json_object_get_value(obj, "passphrase");
  if (json_value_get_type(val) == JSONString)
    pass_str = json_value_get_string(val);
  else
    pass_strs = json_value_get_array(val);

  if (obj == NULL ||
      !((cert_str != NULL && key_str != NULL) ||
        (cert_strs != NULL && key_strs != NULL))) {
    err = bud_error_str(kBudErrJSONParse, "<SNI Response>");
    goto fatal;
  }

  /* Load NPN from response */
  memset(ctx, 0, sizeof(*ctx));
  ctx->cert_file = cert_str;
  ctx->key_file = key_str;
  ctx->key_pass = pass_str;
  ctx->cert_files = cert_strs;
  ctx->key_files = key_strs;
  ctx->key_passes = pass_strs;
  ctx->ciphers = json_object_get_string(obj, "ciphers");
  ctx->ecdh = json_object_get_string(obj, "ecdh");
  ctx->ticket_key = json_object_get_string(obj, "ticket_key");
  ctx->npn = json_object_get_array(obj, "npn");
  ctx->ca_array = json_object_get_array(obj, "ca");
  val = json_object_get_value(obj, "request_cert");
  if (val != NULL)
    ctx->request_cert = json_value_get_boolean(val);
  err = bud_config_load_backend_list(config, obj, &ctx->backend);
  if (!bud_is_ok(err))
    goto fatal;

  err = bud_context_init(config, ctx);

  /* Make sure that deallocated values won't be used */
  ctx->cert_file = NULL;
  ctx->key_file = NULL;
  ctx->key_pass = NULL;
  ctx->cert_files = NULL;
  ctx->key_files = NULL;
  ctx->key_passes = NULL;
  if (!bud_is_ok(err))
    goto fatal;

  return bud_ok();

fatal:
  if (!bud_is_ok(err)) {
    SSL_CTX_free(ctx->ctx);
    ctx->ctx = NULL;
  }
  free(ctx->backend.list);
  ctx->backend.list = NULL;
  return err;
}
Esempio n. 30
0
File: config.c Progetto: indutny/bud
bud_error_t bud_config_free_files(bud_hashmap_item_t* item, void* arg) {
  free(item->value);
  return bud_ok();
}