Exemple #1
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();
}
Exemple #2
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();
}
Exemple #3
0
bud_client_error_t bud_client_spdy_xforward(bud_client_t* client,
                                            const char* protocol,
                                            unsigned int protocol_len) {
  int major;
  int minor;
  int r;
  unsigned char frame[256];

  /* Detect protocol version */
  major = -1;
  minor = 0;
  switch (protocol_len) {
    case 1:
      if (protocol[0] == '3')
        major = 3;
      else if (protocol[0] == '2')
        major = 2;
      break;
    case 3:
      if (strncmp(protocol, "3.1", protocol_len) == 0) {
        major = 3;
        minor = 1;
      }
      break;
    default:
      break;
  }

  /* We are done by now */
  client->xforward.crlf = 2;

  if (major == -1)
    goto skip;

  assert(12 + client->host_len <= sizeof(frame));

  frame[0] = 0x80;
  frame[1] = major;
  *(uint16_t*) (frame + 2) = ntohs(kSpdyXForwardFrameType);

  /* Frame and Host lengths */
  *(uint32_t*) (frame + 4) = htonl(4 + client->host_len);
  *(uint32_t*) (frame + 8) = htonl(client->host_len);

  /* Copy hostname */
  memcpy(frame + 12, client->host, client->host_len);

  /* Prepend it to output data */
  r = ringbuffer_insert(&client->backend.output,
                        0,
                        (const char*) frame,
                        (size_t) 12 + client->host_len);
  if (r != 0) {
    return bud_client_error(bud_error(kBudErrClientXForwardInsert),
                            &client->backend);
  }

skip:
  return bud_client_ok(&client->backend);
}
Exemple #4
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();
}
Exemple #5
0
void bud_client_handshake_start_cb(const SSL* ssl) {
  bud_client_t* client;
  uint64_t now;
  uint64_t limit;

  client = SSL_get_ex_data(ssl, kBudSSLClientIndex);

  now = uv_now(client->config->loop);

  /* NOTE: config's limit is in ms */
  limit = (uint64_t) client->config->frontend.reneg_window * 1000;
  if (now - client->last_handshake > limit)
    client->handshakes = 0;

  /* First handshake */
  if (client->last_handshake == 0)
    goto end;
  DBG(&client->frontend, "renegotation %d", client->handshakes);

  /* Too many renegotiations in a small time window */
  if (++client->handshakes > client->config->frontend.reneg_limit) {
    bud_client_close(
        client,
        bud_client_error(bud_error(kBudErrClientRenegotiationAttack),
                         &client->frontend));
  }

end:
  client->last_handshake = now;
}
Exemple #6
0
void bud_client_handshake_done_cb(const SSL* ssl) {
  bud_client_t* client;
  bud_context_t* context;
  bud_client_error_t cerr;

  client = SSL_get_ex_data(ssl, kBudSSLClientIndex);
  context = SSL_get_ex_data(ssl, kBudSSLSNIIndex);

  bud_trace_handshake(client);

  if (client->selected_backend != NULL)
    return;

  if (client->config->balance_e != kBudBalanceSNI)
    return;

  if (context != NULL)
    client->selected_backend = context->backend;
  if (client->selected_backend != NULL) {
    /* Backend provided - connect */
    cerr = bud_client_connect(client);
  } else {
    /* No backend in SNI response */
    cerr = bud_client_error(bud_error(kBudErrClientNoBackendInSNI),
                            &client->frontend);
  }

  if (!bud_is_ok(cerr.err))
    bud_client_close(client, cerr);
}
Exemple #7
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();
}
Exemple #8
0
bud_client_error_t bud_client_throttle(bud_client_t* client,
                                       bud_client_side_t* side,
                                       ringbuffer* buf) {
  int err;
  bud_client_side_t* opposite;

  if (!ringbuffer_is_full(buf))
    return bud_client_ok(side);

  opposite = side == &client->frontend ? &client->backend : &client->frontend;
  if (opposite->reading != kBudProgressRunning)
    goto done;

  DBG(opposite, "throttle, buffer full: %ld", ringbuffer_size(buf));

  err = uv_read_stop((uv_stream_t*) &opposite->tcp);
  if (err != 0) {
    NOTICE(opposite,
           "uv_read_stop failed: %d - \"%s\"",
           err,
           uv_strerror(err));
    return bud_client_error(bud_error_num(kBudErrClientReadStop, err), side);
  }
  opposite->reading = kBudProgressNone;

done:
  return bud_client_error(bud_error(kBudErrClientThrottle), side);
}
Exemple #9
0
bud_client_error_t bud_client_http_xforward(bud_client_t* client) {
  char* out;
  size_t avail;
  size_t off;
  char xforward[256];
  int r;

  out = ringbuffer_read_next(&client->backend.output, &avail);

  /* Not enough data yet */
  if (avail <= client->xforward.skip)
    goto done;

  /* Find first CRLF */
  for (off = client->xforward.skip; off < avail; off++) {
    static char* crlf = "\r\n";
    char cur;

    cur = out[off];

    /* Reset on mismatch */
    if (cur != crlf[client->xforward.crlf]) {
      client->xforward.crlf = 0;
      continue;
    }

    /* Move forward */
    if (++client->xforward.crlf == 2) {
      off++;
      break;
    }
  }
  client->xforward.skip = off;
  if (!bud_client_xforward_done(client))
    goto done;

  /* Format header */
  r = snprintf(xforward,
               sizeof(xforward),
               "X-Forwarded-For: %s\r\n",
               client->host);

  /* Shift data and insert xforward header */
  r = ringbuffer_insert(&client->backend.output,
                        client->xforward.skip,
                        xforward,
                        (size_t) r);
  if (r != 0) {
    return bud_client_error(bud_error(kBudErrClientXForwardInsert),
                            &client->backend);
  }

done:
  return bud_client_ok(&client->backend);
}
Exemple #10
0
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;
}
Exemple #11
0
void bud_client_read_cb(uv_stream_t* stream,
                        ssize_t nread,
                        const uv_buf_t* buf) {
  int r;
  bud_client_t* client;
  bud_client_side_t* side;
  bud_client_side_t* opposite;
  bud_client_error_t cerr;

  client = stream->data;
  side = bud_client_side_by_tcp(client, (uv_tcp_t*) stream);

  /* Commit data if there was no error */
  r = 0;
  if (nread >= 0)
    r = ringbuffer_write_append(&side->input, nread);

  DBG(side, "after read_cb() => %d", nread);

  /* Handle EOF */
  if (nread == UV_EOF) {
    side->reading = kBudProgressDone;

    /* Shutdown opposite side */
    opposite = side == &client->frontend ? &client->backend : &client->frontend;
    cerr = bud_client_shutdown(client, opposite);
    if (!bud_is_ok(cerr.err))
      goto done;
  }

  /* Try writing out data anyway */
  cerr = bud_client_cycle(client);
  if (!bud_is_ok(cerr.err))
    goto done;

  if ((r != 0 || nread < 0) && nread != UV_EOF) {
    if (nread < 0)
      cerr = bud_client_error(bud_error_num(kBudErrClientReadCb, nread), side);
    else
      cerr = bud_client_error(bud_error(kBudErrClientWriteAppend), side);

    /* Unrecoverable socket error, close */
    return bud_client_close(client, cerr);
  }

  /* If buffer is full - stop reading */
  cerr = bud_client_throttle(client, side, &side->input);

done:
  if (!bud_is_ok(cerr.err) && cerr.err.code != kBudErrClientThrottle)
    bud_client_close(client, cerr);
}
Exemple #12
0
bud_client_error_t bud_client_shutdown(bud_client_t* client,
                                       bud_client_side_t* side) {
  int r;
  bud_client_error_t cerr;

  /* Ignore if already shutdown or destroyed */
  if (side->shutdown != kBudProgressNone || client->close == kBudProgressDone)
    return bud_client_ok(side);

  /* Do not shutdown not-connected socket */
  if (side == &client->backend && client->connect != kBudProgressDone)
    return bud_client_error(bud_error(kBudErrClientShutdownNoConn), side);

  /* Try cycling data to figure out if there is still something to send */
  cerr = bud_client_cycle(client);
  if (!bud_is_ok(cerr.err))
    return cerr;

  /* Not empty, send everything first */
  if (!ringbuffer_is_empty(&side->output)) {
    side->shutdown = kBudProgressRunning;
    return bud_client_ok(side);
  }

  DBG_LN(side, "shutdown");

  if (side == &client->frontend) {
    if (SSL_shutdown(client->ssl) != 1)
      SSL_shutdown(client->ssl);

    /* Try writing close_notify */
    cerr = bud_client_send(client, &client->frontend);
    if (!bud_is_ok(cerr.err))
      goto fatal;
  }

  side->shutdown_req.data = client;
  r = uv_shutdown(&side->shutdown_req,
                  (uv_stream_t*) &side->tcp,
                  bud_client_shutdown_cb);
  if (r != 0) {
    cerr = bud_client_error(bud_error_num(kBudErrClientShutdown, r), side);
  } else {
    cerr = bud_client_ok(side);
    side->shutdown = kBudProgressRunning;
  }

fatal:
  side->shutdown = kBudProgressDone;

  return cerr;
}
Exemple #13
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();
}
Exemple #14
0
bud_error_t bud_config_load_ca_arr(X509_STORE** store,
                                   const JSON_Array* ca) {
  int i;
  int count;
  bud_error_t err;

  err = bud_config_verify_all_strings(ca, "ca");
  if (!bud_is_ok(err))
    return err;

  *store = X509_STORE_new();
  if (*store == NULL)
    return bud_error_str(kBudErrNoMem, "CA store");

  count = json_array_get_count(ca);
  for (i = 0; i < count; i++) {
    const char* cert;
    BIO* b;
    X509* x509;

    cert = json_array_get_string(ca, i);
    b = BIO_new_mem_buf((void*) cert, -1);
    if (b == NULL)
      return bud_error_str(kBudErrNoMem, "BIO_new_mem_buf:CA store bio");

    while ((x509 = PEM_read_bio_X509(b, NULL, NULL, NULL)) != NULL) {
      if (x509 == NULL) {
        err = bud_error_dstr(kBudErrParseCert, cert);
        break;
      }

      if (X509_STORE_add_cert(*store, x509) != 1) {
        err = bud_error(kBudErrAddCert);
        break;
      }
      X509_free(x509);
      x509 = NULL;
    }
    BIO_free_all(b);
    if (x509 != NULL)
      X509_free(x509);
  }

  return err;
}
Exemple #15
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.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();
}
Exemple #16
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();
}
Exemple #17
0
bud_error_t bud_config_new_ssl_ctx(bud_config_t* config,
                                   bud_context_t* context) {
  SSL_CTX* ctx;
  int ecdh_nid;
  EC_KEY* ecdh;
  bud_error_t err;
  int options;
  int r;
  const char* ticket_key;
  size_t max_len;

  if (context->backend != NULL) {
    if (context->backend->keepalive == -1)
      context->backend->keepalive = kBudDefaultKeepalive;
    r = bud_config_str_to_addr(context->backend->host,
                               context->backend->port,
                               &context->backend->addr);
    if (r != 0)
      return bud_error_num(kBudErrPton, r);
  }

  /* Decode ticket_key */
  ticket_key = context->ticket_key == NULL ? config->frontend.ticket_key :
                                             context->ticket_key;
  if (ticket_key != NULL) {
    max_len = sizeof(context->ticket_key_storage);
    if (bud_base64_decode(context->ticket_key_storage,
                          max_len,
                          ticket_key,
                          strlen(ticket_key)) < max_len) {
      return bud_error(kBudErrSmallTicketKey);
    }
  }

  /* Choose method, tlsv1_2 by default */
  if (config->frontend.method == NULL) {
    if (strcmp(config->frontend.security, "tls1.1") == 0)
      config->frontend.method = TLSv1_1_server_method();
    else if (strcmp(config->frontend.security, "tls1.0") == 0)
      config->frontend.method = TLSv1_server_method();
    else if (strcmp(config->frontend.security, "tls1.2") == 0)
      config->frontend.method = TLSv1_2_server_method();
    else if (strcmp(config->frontend.security, "ssl3") == 0)
      config->frontend.method = SSLv3_server_method();
    else
      config->frontend.method = SSLv23_server_method();
  }

  ctx = SSL_CTX_new(config->frontend.method);
  if (ctx == NULL)
    return bud_error_str(kBudErrNoMem, "SSL_CTX");

  /* Disable sessions, they won't work with cluster anyway */
  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);

  if (config->frontend.max_send_fragment)
    SSL_CTX_set_max_send_fragment(ctx, config->frontend.max_send_fragment);

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

  /* ECDH curve selection */
  if (context->ecdh != NULL || config->frontend.ecdh != NULL) {
    if (context->ecdh != NULL)
      ecdh_nid = OBJ_sn2nid(context->ecdh);
    else
      ecdh_nid = OBJ_sn2nid(config->frontend.ecdh);

    if (ecdh_nid == NID_undef) {
      ecdh = NULL;
      err = bud_error_str(kBudErrECDHNotFound,
                          context->ecdh == NULL ? config->frontend.ecdh :
                                                  context->ecdh);
      goto fatal;
    }

    ecdh = EC_KEY_new_by_curve_name(ecdh_nid);
    if (ecdh == NULL) {
      err = bud_error_str(kBudErrNoMem, "EC_KEY");
      goto fatal;
    }

    SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);
    SSL_CTX_set_tmp_ecdh(ctx, ecdh);
    EC_KEY_free(ecdh);
  }
  ecdh = NULL;

  /* Cipher suites */
  if (context->ciphers != NULL)
    SSL_CTX_set_cipher_list(ctx, context->ciphers);
  else if (config->frontend.ciphers != NULL)
    SSL_CTX_set_cipher_list(ctx, config->frontend.ciphers);

  /* Disable SSL2 */
  options = SSL_OP_NO_SSLv2 | SSL_OP_ALL;
  if (!config->frontend.ssl3)
    options |= SSL_OP_NO_SSLv3;

  if (config->frontend.server_preference)
    options |= SSL_OP_CIPHER_SERVER_PREFERENCE;
  SSL_CTX_set_options(ctx, options);

#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
  if (config->context_count != 0) {
    SSL_CTX_set_tlsext_servername_callback(ctx,
                                           bud_config_select_sni_context);
    SSL_CTX_set_tlsext_servername_arg(ctx, config);
  }
#endif  /* SSL_CTRL_SET_TLSEXT_SERVERNAME_CB */

#ifdef OPENSSL_NPN_NEGOTIATED
  context->npn_line = bud_config_encode_npn(config,
                                            context->npn,
                                            &context->npn_line_len,
                                            &err);
  if (!bud_is_ok(err))
    goto fatal;

  if (context->npn_line != NULL) {
    SSL_CTX_set_next_protos_advertised_cb(ctx,
                                          bud_config_advertise_next_proto,
                                          context);
  }
#else  /* !OPENSSL_NPN_NEGOTIATED */
  err = bud_error(kBudErrNPNNotSupported);
  goto fatal;
#endif  /* OPENSSL_NPN_NEGOTIATED */

  SSL_CTX_set_tlsext_status_cb(ctx, bud_client_stapling_cb);

  context->ctx = ctx;
  return bud_ok();

fatal:
  if (ecdh != NULL)
    EC_KEY_free(ecdh);

  SSL_CTX_free(ctx);
  return err;
}
Exemple #18
0
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;
}
Exemple #19
0
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;
}
Exemple #20
0
bud_error_t bud_config_load(bud_config_t* config) {
  int i;
  bud_error_t err;
  JSON_Value* json;
  JSON_Value* val;
  JSON_Object* frontend;
  JSON_Object* obj;
  JSON_Object* log;
  JSON_Object* avail;
  JSON_Array* contexts;

  if (config->piped) {
    char* content;

    ASSERT(config->loop != NULL, "Loop should be present");
    err = bud_read_file_by_fd(config->loop, 0, &content);
    if (!bud_is_ok(err))
      goto end;

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

    json = json_parse_string(content);
  } else if (config->inlined) {
    json = json_parse_string(config->path);
  } else {
    const char* contents;

    err = bud_config_load_file(config, config->path, &contents);
    if (!bud_is_ok(err))
      goto end;
    json = json_parse_string(contents);
  }

  if (json == NULL) {
    err = bud_error_dstr(kBudErrJSONParse, config->path);
    goto end;
  }

  obj = json_value_get_object(json);
  if (obj == NULL) {
    err = bud_error(kBudErrJSONNonObjectRoot);
    goto failed_alloc_path;
  }

  err = bud_config_load_tracing(&config->trace,
                                json_object_get_object(obj, "tracing"));
  if (!bud_is_ok(err))
    goto failed_alloc_path;

  /* Allocate contexts and backends */
  contexts = json_object_get_array(obj, "contexts");
  config->context_count = contexts == NULL ? 0 : json_array_get_count(contexts);
  config->contexts = calloc(config->context_count + 1,
                            sizeof(*config->contexts));
  if (config->contexts == NULL) {
    err = bud_error_str(kBudErrNoMem, "bud_context_t");
    goto failed_alloc_contexts;
  }

  config->json = json;

  /* Workers configuration */
  config->worker_count = -1;
  config->restart_timeout = -1;
  config->master_ipc = -1;
  val = json_object_get_value(obj, "workers");
  if (val != NULL)
    config->worker_count = json_value_get_number(val);
  val = json_object_get_value(obj, "restart_timeout");
  if (val != NULL)
    config->restart_timeout = json_value_get_number(val);
  val = json_object_get_value(obj, "master_ipc");
  if (val != NULL)
    config->master_ipc = json_value_get_boolean(val);

  /* Logger configuration */
  log = json_object_get_object(obj, "log");
  config->log.stdio = -1;
  config->log.syslog = -1;
  if (log != NULL) {
    config->log.level = json_object_get_string(log, "level");
    config->log.facility = json_object_get_string(log, "facility");

    val = json_object_get_value(log, "stdio");
    if (val != NULL)
      config->log.stdio = json_value_get_boolean(val);
    val = json_object_get_value(log, "syslog");
    if (val != NULL)
      config->log.syslog = json_value_get_boolean(val);
  }

  /* Availability configuration */
  avail = json_object_get_object(obj, "availability");
  config->availability.death_timeout = -1;
  config->availability.revive_interval = -1;
  config->availability.retry_interval = -1;
  config->availability.max_retries = -1;
  if (avail != NULL) {
    val = json_object_get_value(avail, "death_timeout");
    if (val != NULL)
      config->availability.death_timeout = json_value_get_number(val);
    val = json_object_get_value(avail, "revive_interval");
    if (val != NULL)
      config->availability.revive_interval = json_value_get_number(val);
    val = json_object_get_value(avail, "retry_interval");
    if (val != NULL)
      config->availability.retry_interval = json_value_get_number(val);
    val = json_object_get_value(avail, "max_retries");
    if (val != NULL)
      config->availability.max_retries = json_value_get_number(val);
  }

  /* Frontend configuration */
  frontend = json_object_get_object(obj, "frontend");
  err = bud_config_load_frontend(frontend, &config->frontend);
  if (!bud_is_ok(err))
    goto failed_alloc_contexts;

  /* Load frontend's context */
  err = bud_context_load(frontend, &config->contexts[0]);
  if (!bud_is_ok(err))
    goto failed_alloc_contexts;

  /* Backend configuration */
  config->balance = json_object_get_string(obj, "balance");
  err = bud_config_load_backend_list(config,
                                      obj,
                                      &config->contexts[0].backend);
  if (!bud_is_ok(err))
    goto failed_alloc_contexts;

  /* User and group configuration */
  config->user = json_object_get_string(obj, "user");
  config->group = json_object_get_string(obj, "group");

  /* SNI configuration */
  bud_config_read_pool_conf(obj, "sni", &config->sni);

  /* OCSP Stapling configuration */
  bud_config_read_pool_conf(obj, "stapling", &config->stapling);

  /* SSL Contexts */

  /* TODO(indutny): sort them and do binary search */
  for (i = 0; i < config->context_count; i++) {
    bud_context_t* ctx;

    /* NOTE: contexts[0] - is a default context */
    ctx = &config->contexts[i + 1];
    obj = json_array_get_object(contexts, i);
    if (obj == NULL) {
      err = bud_error(kBudErrJSONNonObjectCtx);
      goto failed_load_context;
    }

    err = bud_context_load(obj, ctx);
    if (!bud_is_ok(err))
      goto failed_load_context;

    err = bud_config_load_backend_list(config, obj, &ctx->backend);
    if (!bud_is_ok(err))
      goto failed_load_context;
  }

  bud_config_set_defaults(config);

  return bud_config_init(config);

failed_load_context:
  /* Deinitalize contexts */
  for (i++; i >= 0; i--) {
    bud_context_t* ctx;

    ctx = &config->contexts[i];
    free(ctx->backend.list);
    ctx->backend.list = NULL;
  }

failed_alloc_contexts:
  free(config->contexts);
  config->contexts = NULL;
  free(config->trace.dso);
  config->trace.dso = NULL;

failed_alloc_path:
  json_value_free(json);
  config->json = NULL;

end:
  return err;
}
Exemple #21
0
void bud_client_sni_cb(bud_http_request_t* req, bud_error_t err) {
  bud_client_t* client;
  bud_config_t* config;
  bud_client_error_t cerr;
  int r;
  STACK_OF(X509)* chain;
  SSL_CTX* ctx;
  X509* x509;
  EVP_PKEY* pkey;

  client = req->data;
  config = client->config;

  client->sni_req = NULL;
  client->async_hello = kBudProgressDone;
  if (!bud_is_ok(err)) {
    WARNING(&client->frontend, "SNI cb failed: \"%s\"", bud_error_to_str(err));
    goto fatal;
  }

  if (req->code == 404) {
    /* Not found */
    DBG(&client->frontend,
        "SNI name not found: \"%.*s\"",
        client->hello.servername_len,
        client->hello.servername);
    goto done;
  }

  /* Parse incoming JSON */
  err = bud_sni_from_json(config, req->response, &client->sni_ctx);
  if (!bud_is_ok(err)) {
    WARNING(&client->frontend,
            "SNI from json failed: \"%s\"",
            bud_error_to_str(err));
    goto fatal;
  }

  /* Success */
  DBG(&client->frontend,
      "SNI name found: \"%.*s\"",
      client->hello.servername_len,
      client->hello.servername);
  if (!SSL_set_ex_data(client->ssl, kBudSSLSNIIndex, &client->sni_ctx)) {
    err = bud_error(kBudErrClientSetExData);
    goto fatal;
  }

  /* NOTE: reference count is not increased by this API methods */
  ctx = client->sni_ctx.ctx;
  x509 = SSL_CTX_get0_certificate(ctx);
  pkey = SSL_CTX_get0_privatekey(ctx);

  r = SSL_CTX_get0_chain_certs(ctx, &chain);
  if (r == 1)
    r = SSL_use_certificate(client->ssl, x509);
  if (r == 1)
    r = SSL_use_PrivateKey(client->ssl, pkey);
  if (r == 1 && chain != NULL)
    r = SSL_set1_chain(client->ssl, chain);
  if (r != 1) {
    err = bud_error(kBudErrClientSetSNICert);
    goto fatal;
  }

  /* Update context, may be needed for early ticket key generation */
  SSL_set_SSL_CTX(client->ssl, ctx);

  /* Do not loose the cert callback! */
  SSL_set_cert_cb(client->ssl, bud_client_ssl_cert_cb, client);
  client->ssl->options = client->sni_ctx.ctx->options;

done:
  /* Request stapling info if needed */
  if (config->stapling.enabled && client->hello.ocsp_request != 0) {
    err = bud_client_ocsp_stapling(client);
    if (!bud_is_ok(err))
      goto fatal;
  }
  json_value_free(req->response);

  if (client->async_hello == kBudProgressDone) {
    cerr = bud_client_cycle(client);
    if (!bud_is_ok(cerr.err))
      bud_client_close(client, cerr);
  }
  return;

fatal:
  bud_client_close(client, bud_client_error(err, &client->frontend));
}
Exemple #22
0
void bud_client_sni_cb(bud_http_request_t* req, bud_error_t err) {
  bud_client_t* client;
  bud_config_t* config;
  bud_client_error_t cerr;

  client = req->data;
  config = client->config;

  client->sni_req = NULL;
  client->hello_parse = kBudProgressDone;
  if (!bud_is_ok(err)) {
    WARNING(&client->frontend, "SNI cb failed: \"%s\"", bud_error_to_str(err));
    goto fatal;
  }

  if (req->code == 404) {
    /* Not found */
    DBG(&client->frontend,
        "SNI name not found: \"%.*s\"",
        client->hello.servername_len,
        client->hello.servername);
    goto done;
  }

  /* Parse incoming JSON */
  err = bud_sni_from_json(config, req->response, &client->sni_ctx);
  if (!bud_is_ok(err)) {
    WARNING(&client->frontend,
            "SNI from json failed: \"%s\"",
            bud_error_to_str(err));
    goto fatal;
  }

  /* Success */
  DBG(&client->frontend,
      "SNI name found: \"%.*s\"",
      client->hello.servername_len,
      client->hello.servername);
  if (!SSL_set_ex_data(client->ssl, kBudSSLSNIIndex, &client->sni_ctx)) {
    err = bud_error(kBudErrClientSetExData);
    goto fatal;
  }

done:
  /* Request stapling info if needed */
  if (config->stapling.enabled && client->hello.ocsp_request != 0) {
    err = bud_client_ocsp_stapling(client);
    if (!bud_is_ok(err))
      goto fatal;
  }
  json_value_free(req->response);

  if (client->hello_parse == kBudProgressDone) {
    cerr = bud_client_cycle(client);
    if (!bud_is_ok(cerr.err))
      bud_client_close(client, cerr);
  }
  return;

fatal:
  bud_client_close(client, bud_client_error(err, &client->frontend));
}
Exemple #23
0
bud_error_t bud_config_new_ssl_ctx(bud_config_t* config,
                                   bud_context_t* context) {
  SSL_CTX* ctx;
  bud_error_t err;
  int options;

  /* Choose method, tlsv1_2 by default */
  if (config->frontend.method == NULL) {
    if (strcmp(config->frontend.security, "tls1.1") == 0)
      config->frontend.method = TLSv1_1_server_method();
    else if (strcmp(config->frontend.security, "tls1.0") == 0)
      config->frontend.method = TLSv1_server_method();
    else if (strcmp(config->frontend.security, "tls1.2") == 0)
      config->frontend.method = TLSv1_2_server_method();
    else if (strcmp(config->frontend.security, "ssl3") == 0)
      config->frontend.method = SSLv3_server_method();
    else
      config->frontend.method = SSLv23_server_method();
  }

  ctx = SSL_CTX_new(config->frontend.method);
  if (ctx == NULL)
    return bud_error_str(kBudErrNoMem, "SSL_CTX");
  SSL_CTX_set_session_cache_mode(ctx,
                                 SSL_SESS_CACHE_SERVER |
                                 SSL_SESS_CACHE_NO_INTERNAL |
                                 SSL_SESS_CACHE_NO_AUTO_CLEAR);
  if (context->ciphers != NULL)
    SSL_CTX_set_cipher_list(ctx, context->ciphers);
  else if (config->frontend.ciphers != NULL)
    SSL_CTX_set_cipher_list(ctx, config->frontend.ciphers);

  /* Disable SSL2 */
  options = SSL_OP_NO_SSLv2 | SSL_OP_ALL;
  if (!config->frontend.ssl3)
    options |= SSL_OP_NO_SSLv3;

  if (config->frontend.server_preference)
    options |= SSL_OP_CIPHER_SERVER_PREFERENCE;
  SSL_CTX_set_options(ctx, options);

#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
  if (config->context_count != 0) {
    SSL_CTX_set_tlsext_servername_callback(ctx,
                                           bud_config_select_sni_context);
    SSL_CTX_set_tlsext_servername_arg(ctx, config);
  }
#endif  /* SSL_CTRL_SET_TLSEXT_SERVERNAME_CB */

#ifdef OPENSSL_NPN_NEGOTIATED
  context->npn_line = bud_config_encode_npn(config,
                                            context->npn,
                                            &context->npn_line_len,
                                            &err);
  if (!bud_is_ok(err))
    goto fatal;

  if (context->npn_line != NULL) {
    SSL_CTX_set_next_protos_advertised_cb(ctx,
                                          bud_config_advertise_next_proto,
                                          context);
  }
#else  /* !OPENSSL_NPN_NEGOTIATED */
  err = bud_error(kBudErrNPNNotSupported);
  goto fatal;
#endif  /* OPENSSL_NPN_NEGOTIATED */

  SSL_CTX_set_tlsext_status_cb(ctx, bud_client_stapling_cb);

  context->ctx = ctx;
  return bud_ok();

fatal:
  SSL_CTX_free(ctx);
  return err;
}
Exemple #24
0
bud_config_t* bud_config_load(uv_loop_t* loop,
                              const char* path,
                              bud_error_t* err) {
  int i;
  int context_count;
  JSON_Value* json;
  JSON_Value* val;
  JSON_Object* obj;
  JSON_Object* log;
  JSON_Object* frontend;
  JSON_Object* backend;
  JSON_Array* contexts;
  bud_config_t* config;
  bud_context_t* ctx;

  json = json_parse_file(path);
  if (json == NULL) {
    *err = bud_error_str(kBudErrJSONParse, path);
    goto end;
  }

  obj = json_value_get_object(json);
  if (obj == NULL) {
    *err = bud_error(kBudErrJSONNonObjectRoot);
    goto failed_get_object;
  }
  contexts = json_object_get_array(obj, "contexts");
  context_count = contexts == NULL ? 0 : json_array_get_count(contexts);

  config = calloc(1,
                  sizeof(*config) +
                      context_count * sizeof(*config->contexts));
  if (config == NULL) {
    *err = bud_error_str(kBudErrNoMem, "bud_config_t");
    goto failed_get_object;
  }

  config->loop = loop;
  config->json = json;

  /* Workers configuration */
  config->worker_count = -1;
  config->restart_timeout = -1;
  val = json_object_get_value(obj, "workers");
  if (val != NULL)
    config->worker_count = json_value_get_number(val);
  val = json_object_get_value(obj, "restart_timeout");
  if (val != NULL)
    config->restart_timeout = json_value_get_number(val);

  /* Logger configuration */
  log = json_object_get_object(obj, "log");
  config->log.stdio = -1;
  config->log.syslog = -1;
  if (log != NULL) {
    config->log.level = json_object_get_string(log, "level");
    config->log.facility = json_object_get_string(log, "facility");

    val = json_object_get_value(log, "stdio");
    if (val != NULL)
      config->log.stdio = json_value_get_boolean(val);
    val = json_object_get_value(log, "syslog");
    if (val != NULL)
      config->log.syslog = json_value_get_boolean(val);
  }

  /* Frontend configuration */

  frontend = json_object_get_object(obj, "frontend");
  config->frontend.proxyline = -1;
  config->frontend.keepalive = -1;
  config->frontend.server_preference = -1;
  config->frontend.ssl3 = -1;
  if (frontend != NULL) {
    config->frontend.port = (uint16_t) json_object_get_number(frontend, "port");
    config->frontend.host = json_object_get_string(frontend, "host");
    config->frontend.security = json_object_get_string(frontend, "security");
    config->frontend.npn = json_object_get_array(frontend, "npn");
    config->frontend.ciphers = json_object_get_string(frontend, "ciphers");
    config->frontend.cert_file = json_object_get_string(frontend, "cert");
    config->frontend.key_file = json_object_get_string(frontend, "key");
    config->frontend.reneg_window = json_object_get_number(frontend,
                                                           "reneg_window");
    config->frontend.reneg_limit = json_object_get_number(frontend,
                                                          "reneg_limit");

    *err = bud_config_verify_npn(config->frontend.npn);
    if (!bud_is_ok(*err))
      goto failed_get_index;

    val = json_object_get_value(frontend, "proxyline");
    if (val != NULL)
      config->frontend.proxyline = json_value_get_boolean(val);
    val = json_object_get_value(frontend, "keepalive");
    if (val != NULL)
      config->frontend.keepalive = json_value_get_number(val);
    val = json_object_get_value(frontend, "server_preference");
    if (val != NULL)
      config->frontend.server_preference = json_value_get_boolean(val);
    val = json_object_get_value(frontend, "ssl3");
    if (val != NULL)
      config->frontend.ssl3 = json_value_get_boolean(val);
  }

  /* Backend configuration */
  backend = json_object_get_object(obj, "backend");
  config->backend.keepalive = -1;
  if (backend != NULL) {
    config->backend.port = (uint16_t) json_object_get_number(backend, "port");
    config->backend.host = json_object_get_string(backend, "host");
    val = json_object_get_value(backend, "keepalive");
    if (val != NULL)
      config->backend.keepalive = json_value_get_number(val);
  }

  /* SNI configuration */
  bud_config_read_pool_conf(obj, "sni", &config->sni);

  /* OCSP Stapling configuration */
  bud_config_read_pool_conf(obj, "stapling", &config->stapling);

  /* SSL Contexts */

  /* TODO(indutny): sort them and do binary search */
  for (i = 0; i < context_count; i++) {
    /* NOTE: contexts[0] - is a default context */
    ctx = &config->contexts[i + 1];
    obj = json_array_get_object(contexts, i);
    if (obj == NULL) {
      *err = bud_error(kBudErrJSONNonObjectCtx);
      goto failed_get_index;
    }

    ctx->servername = json_object_get_string(obj, "servername");
    ctx->servername_len = ctx->servername == NULL ? 0 : strlen(ctx->servername);
    ctx->cert_file = json_object_get_string(obj, "cert");
    ctx->key_file = json_object_get_string(obj, "key");
    ctx->npn = json_object_get_array(obj, "npn");
    ctx->ciphers = json_object_get_string(obj, "ciphers");

    *err = bud_config_verify_npn(ctx->npn);
    if (!bud_is_ok(*err))
      goto failed_get_index;
  }
  config->context_count = context_count;

  bud_config_set_defaults(config);

  *err = bud_ok();
  return config;

failed_get_index:
  free(config);

failed_get_object:
  json_value_free(json);

end:
  return NULL;
}
Exemple #25
0
bud_error_t bud_config_init(bud_config_t* config) {
  int i;
  int r;
  bud_context_t* ctx;
  bud_error_t err;
  const char* cert_file;
  const char* key_file;
  BIO* cert_bio;

  i = 0;

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

  r = bud_config_str_to_addr(config->backend.host,
                             config->backend.port,
                             &config->backend.addr);
  if (r != 0) {
    err = bud_error_num(kBudErrPton, r);
    goto fatal;
  }

  /* Get indexes for SSL_set_ex_data()/SSL_get_ex_data() */
  if (kBudSSLClientIndex == -1) {
    kBudSSLClientIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
    kBudSSLSNIIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
    if (kBudSSLClientIndex == -1 || kBudSSLSNIIndex == -1)
      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->workers = calloc(config->worker_count, sizeof(*config->workers));
    if (config->workers == NULL) {
      err = bud_error_str(kBudErrNoMem, "workers");
      goto fatal;
    }
  }

  /* Initialize logger */
  err = bud_logger_new(config);
  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;
    }
  }

  /* Load all contexts */
  for (i = 0; i < config->context_count + 1; i++) {
    ctx = &config->contexts[i];

    err = bud_config_new_ssl_ctx(config, ctx);
    if (!bud_is_ok(err))
      goto fatal;

    /* Default context */
    if (i == 0) {
      cert_file = config->frontend.cert_file;
      key_file = config->frontend.key_file;
    } else {
      cert_file = ctx->cert_file;
      key_file = ctx->key_file;
    }

    cert_bio = BIO_new_file(cert_file, "r");
    if (cert_bio == NULL) {
      err = bud_error_str(kBudErrLoadCert, cert_file);
      goto fatal;
    }

    r = bud_context_use_certificate_chain(ctx, cert_bio);
    BIO_free_all(cert_bio);
    if (!r) {
      err = bud_error_str(kBudErrParseCert, cert_file);
      goto fatal;
    }

    if (!SSL_CTX_use_PrivateKey_file(ctx->ctx,
                                     key_file,
                                     SSL_FILETYPE_PEM)) {
      err = bud_error_str(kBudErrParseKey, key_file);
      goto fatal;
    }
  }

  return bud_ok();

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

  return err;
}
Exemple #26
0
bud_config_t* bud_config_load(const char* path, int inlined, bud_error_t* err) {
  int i;
  JSON_Value* json;
  JSON_Value* val;
  JSON_Object* obj;
  JSON_Object* tmp;
  JSON_Object* log;
  JSON_Object* avail;
  JSON_Array* contexts;
  JSON_Array* backend;
  bud_config_t* config;
  bud_context_t* ctx;

  if (inlined)
    json = json_parse_string(path);
  else
    json = json_parse_file(path);

  if (json == NULL) {
    *err = bud_error_str(kBudErrJSONParse, path);
    goto end;
  }

  obj = json_value_get_object(json);
  if (obj == NULL) {
    *err = bud_error(kBudErrJSONNonObjectRoot);
    goto failed_get_object;
  }

  config = calloc(1, sizeof(*config));
  if (config == NULL) {
    *err = bud_error_str(kBudErrNoMem, "bud_config_t");
    goto failed_get_object;
  }

  /* Copy path or inlined config value */
  config->path = strdup(path);
  if (config->path == NULL) {
    *err = bud_error_str(kBudErrNoMem, "bud_config_t strcpy(path)");
    goto failed_alloc_path;
  }

  config->inlined = inlined;

  /* Allocate contexts and backends */
  contexts = json_object_get_array(obj, "contexts");
  backend = json_object_get_array(obj, "backend");
  config->context_count = contexts == NULL ? 0 : json_array_get_count(contexts);
  config->backend_count = backend == NULL ? 0 : json_array_get_count(backend);
  config->contexts = calloc(config->context_count + 1,
                            sizeof(*config->contexts));
  config->backend = calloc(config->backend_count, sizeof(*config->backend));

  if (config->contexts == NULL || config->backend == NULL) {
    *err = bud_error_str(kBudErrNoMem, "bud_context_t");
    goto failed_get_index;
  }

  config->json = json;

  /* Workers configuration */
  config->worker_count = -1;
  config->restart_timeout = -1;
  val = json_object_get_value(obj, "workers");
  if (val != NULL)
    config->worker_count = json_value_get_number(val);
  val = json_object_get_value(obj, "restart_timeout");
  if (val != NULL)
    config->restart_timeout = json_value_get_number(val);

  /* Logger configuration */
  log = json_object_get_object(obj, "log");
  config->log.stdio = -1;
  config->log.syslog = -1;
  if (log != NULL) {
    config->log.level = json_object_get_string(log, "level");
    config->log.facility = json_object_get_string(log, "facility");

    val = json_object_get_value(log, "stdio");
    if (val != NULL)
      config->log.stdio = json_value_get_boolean(val);
    val = json_object_get_value(log, "syslog");
    if (val != NULL)
      config->log.syslog = json_value_get_boolean(val);
  }

  /* Availability configuration */
  avail = json_object_get_object(obj, "availability");
  config->availability.death_timeout = -1;
  config->availability.revive_interval = -1;
  config->availability.retry_interval = -1;
  config->availability.max_retries = -1;
  if (avail != NULL) {
    val = json_object_get_value(avail, "death_timeout");
    if (val != NULL)
      config->availability.death_timeout = json_value_get_number(val);
    val = json_object_get_value(avail, "revive_interval");
    if (val != NULL)
      config->availability.revive_interval = json_value_get_number(val);
    val = json_object_get_value(avail, "retry_interval");
    if (val != NULL)
      config->availability.retry_interval = json_value_get_number(val);
    val = json_object_get_value(avail, "max_retries");
    if (val != NULL)
      config->availability.max_retries = json_value_get_number(val);
  }

  /* Frontend configuration */
  *err = bud_config_load_frontend(json_object_get_object(obj, "frontend"),
                                  &config->frontend);
  if (!bud_is_ok(*err))
    goto failed_get_index;

  /* Backend configuration */
  config->balance = json_object_get_string(obj, "balance");
  for (i = 0; i < config->backend_count; i++) {
    bud_config_load_backend(config,
                            json_array_get_object(backend, i),
                            &config->backend[i]);
  }

  /* SNI configuration */
  bud_config_read_pool_conf(obj, "sni", &config->sni);

  /* OCSP Stapling configuration */
  bud_config_read_pool_conf(obj, "stapling", &config->stapling);

  /* SSL Contexts */

  /* TODO(indutny): sort them and do binary search */
  for (i = 0; i < config->context_count; i++) {
    /* NOTE: contexts[0] - is a default context */
    ctx = &config->contexts[i + 1];
    obj = json_array_get_object(contexts, i);
    if (obj == NULL) {
      *err = bud_error(kBudErrJSONNonObjectCtx);
      goto failed_get_index;
    }

    ctx->servername = json_object_get_string(obj, "servername");
    ctx->servername_len = ctx->servername == NULL ? 0 : strlen(ctx->servername);
    ctx->cert_file = json_object_get_string(obj, "cert");
    ctx->key_file = json_object_get_string(obj, "key");
    ctx->npn = json_object_get_array(obj, "npn");
    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");

    tmp = json_object_get_object(obj, "backend");
    if (tmp != NULL) {
      ctx->backend = &ctx->backend_st;
      bud_config_load_backend(config, tmp, ctx->backend);
    }

    *err = bud_config_verify_npn(ctx->npn);
    if (!bud_is_ok(*err))
      goto failed_get_index;
  }

  bud_config_set_defaults(config);

  *err = bud_ok();
  return config;

failed_get_index:
  free(config->contexts);
  config->contexts = NULL;
  free(config->backend);
  config->backend = NULL;
  free(config->path);
  config->path = NULL;

failed_alloc_path:
  free(config);

failed_get_object:
  json_value_free(json);

end:
  return NULL;
}