예제 #1
0
/*
 * This function tries to find a running server for the proxy <px> following
 * the Header parameter hash method. It looks for a specific parameter in the
 * URL and hashes it to compute the server ID. This is useful to optimize
 * performance by avoiding bounces between servers in contexts where sessions
 * are shared but cookies are not usable. If the parameter is not found, NULL
 * is returned. If any server is found, it will be returned. If no valid server
 * is found, NULL is returned.
 */
struct server *get_server_hh(struct session *s)
{
	unsigned long    hash = 0;
	struct http_txn *txn  = &s->txn;
	struct http_msg *msg  = &txn->req;
	struct proxy    *px   = s->be;
	unsigned int     plen = px->hh_len;
	unsigned long    len;
	struct hdr_ctx   ctx;
	const char      *p;

	/* tot_weight appears to mean srv_count */
	if (px->lbprm.tot_weight == 0)
		return NULL;

	ctx.idx = 0;

	/* if the message is chunked, we skip the chunk size, but use the value as len */
	http_find_header2(px->hh_name, plen, msg->sol, &txn->hdr_idx, &ctx);

	/* if the header is not found or empty, let's fallback to round robin */
	if (!ctx.idx || !ctx.vlen)
		return NULL;

	/* note: we won't hash if there's only one server left */
	if (px->lbprm.tot_used == 1)
		goto hash_done;

	/* Found a the hh_name in the headers.
	 * we will compute the hash based on this value ctx.val.
	 */
	len = ctx.vlen;
	p = (char *)ctx.line + ctx.val;
	if (!px->hh_match_domain) {
		while (len) {
			hash = *p + (hash << 6) + (hash << 16) - hash;
			len--;
			p++;
		}
	} else {
		int dohash = 0;
		p += len - 1;
		/* special computation, use only main domain name, not tld/host
		 * going back from the end of string, start hashing at first
		 * dot stop at next.
		 * This is designed to work with the 'Host' header, and requires
		 * a special option to activate this.
		 */
		while (len) {
			if (*p == '.') {
				if (!dohash)
					dohash = 1;
				else
					break;
			} else {
				if (dohash)
					hash = *p + (hash << 6) + (hash << 16) - hash;
			}
			len--;
			p--;
		}
	}
 hash_done:
	if (px->lbprm.algo & BE_LB_LKUP_CHTREE)
		return chash_get_server_hash(px, hash);
	else
		return map_get_server_hash(px, hash);
}
예제 #2
0
/*
 * This function tries to find a running server for the proxy <px> following
 * the Header parameter hash method. It looks for a specific parameter in the
 * URL and hashes it to compute the server ID. This is useful to optimize
 * performance by avoiding bounces between servers in contexts where sessions
 * are shared but cookies are not usable. If the parameter is not found, NULL
 * is returned. If any server is found, it will be returned. If no valid server
 * is found, NULL is returned.
 */
struct server *get_server_hh(struct session *s)
{
	unsigned int hash = 0;
	struct http_txn *txn  = &s->txn;
	struct proxy    *px   = s->be;
	unsigned int     plen = px->hh_len;
	unsigned long    len;
	struct hdr_ctx   ctx;
	const char      *p;
	const char *start, *end;

	/* tot_weight appears to mean srv_count */
	if (px->lbprm.tot_weight == 0)
		return NULL;

	ctx.idx = 0;

	/* if the message is chunked, we skip the chunk size, but use the value as len */
	http_find_header2(px->hh_name, plen, b_ptr(s->req.buf, -http_hdr_rewind(&txn->req)), &txn->hdr_idx, &ctx);

	/* if the header is not found or empty, let's fallback to round robin */
	if (!ctx.idx || !ctx.vlen)
		return NULL;

	/* note: we won't hash if there's only one server left */
	if (px->lbprm.tot_used == 1)
		goto hash_done;

	/* Found a the hh_name in the headers.
	 * we will compute the hash based on this value ctx.val.
	 */
	len = ctx.vlen;
	p = (char *)ctx.line + ctx.val;
	if (!px->hh_match_domain) {
		hash = gen_hash(px, p, len);
	} else {
		int dohash = 0;
		p += len;
		/* special computation, use only main domain name, not tld/host
		 * going back from the end of string, start hashing at first
		 * dot stop at next.
		 * This is designed to work with the 'Host' header, and requires
		 * a special option to activate this.
		 */
		end = p;
		while (len) {
			if (dohash) {
				/* Rewind the pointer until the previous char
				 * is a dot, this will allow to set the start
				 * position of the domain. */
				if (*(p - 1) == '.')
					break;
			}
			else if (*p == '.') {
				/* The pointer is rewinded to the dot before the
				 * tld, we memorize the end of the domain and
				 * can enter the domain processing. */
				end = p;
				dohash = 1;
			}
			p--;
			len--;
		}
		start = p;
		hash = gen_hash(px, start, (end - start));
	}
	if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL)
		hash = full_hash(hash);
 hash_done:
	if (px->lbprm.algo & BE_LB_LKUP_CHTREE)
		return chash_get_server_hash(px, hash);
	else
		return map_get_server_hash(px, hash);
}
void optionally_add_header(HMAC_CTX *ctx, struct http_txn *txn, const char* header_name, int header_len) {
  struct hdr_ctx hdr_ctx = {.idx = 0};
  char *begin = NULL;
  char *end   = NULL;
  if(http_find_header2(header_name, header_len, txn->req.sol, &txn->hdr_idx, &hdr_ctx)) {
    begin = hdr_ctx.line + hdr_ctx.val;
    end   = begin + hdr_ctx.vlen;
  }
  // For Date, haproxy does a silly thing where it considers the ',' to be a field separator
  while(http_find_header2(header_name, header_len, txn->req.sol, &txn->hdr_idx, &hdr_ctx)) {
    end = hdr_ctx.line + hdr_ctx.val + hdr_ctx.vlen;
  }
  if(begin) {
    HMAC_Update(ctx, (unsigned char*)begin, end - begin);
  }
  HMAC_Update(ctx, (unsigned const char*)"\n", 1);
}

void make_aws_signature(char *retval, const char *key, int key_len, struct http_txn *txn) {
  struct http_msg *msg = &txn->req;

  static int loaded_engines = 0;
  if(!loaded_engines) {
    ENGINE_load_builtin_engines();
    ENGINE_register_all_complete();
    loaded_engines = 1;
  }
  static unsigned char raw_sig[SIG_SIZE];

  HMAC_CTX ctx;
  HMAC_CTX_init(&ctx);
  HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL);

  HMAC_Update(&ctx, (unsigned char*)msg->sol + msg->som, msg->sl.rq.m_l);
  HMAC_Update(&ctx, (unsigned const char*)"\n", 1);

  ADD_HEADER(&ctx, txn, "Content-MD5");
  ADD_HEADER(&ctx, txn, "Content-Type");
  ADD_HEADER(&ctx, txn, "Date");

  void *header_sorter = HeaderSorter_new("x-amz-");
  for_each_header(txn, header_sorter, &HeaderSorter_add);
  HeaderSorter_update(header_sorter, &ctx);
  HeaderSorter_delete(header_sorter);
  header_sorter = NULL;

  char *rel_uri = http_get_path(txn);
  char *uri_end = msg->sol + msg->sl.rq.u + txn->req.sl.rq.u_l;
  CanonicalizeResource(&ctx, rel_uri, uri_end - rel_uri);

  unsigned int sig_len = sizeof(raw_sig);
  HMAC_Final(&ctx, raw_sig, &sig_len);
  HMAC_CTX_cleanup(&ctx);

  BIO *mem_output_stream, *b64_filter;
  BUF_MEM *output_buffer;

  b64_filter = BIO_new(BIO_f_base64());
  mem_output_stream = BIO_new(BIO_s_mem());
  b64_filter = BIO_push(b64_filter, mem_output_stream);
  BIO_write(b64_filter, raw_sig, sig_len);
  (void) BIO_flush(b64_filter);
  BIO_get_mem_ptr(b64_filter, &output_buffer);

  memcpy(retval, output_buffer->data, output_buffer->length-1);

  BIO_free_all(b64_filter);
}

int s3_resign(struct session *s, struct buffer *req, struct proxy *px) {
  // TODO: Enable support for query string signatures?

  static struct hdr_exp *exp = NULL;
  if(!exp) {
    exp = calloc(1, sizeof(struct hdr_exp));
    exp->preg    = calloc(1, sizeof(regex_t));
    exp->replace = strdup(px->s3_auth_header);
    exp->action  = ACT_REPLACE;
    regcomp((regex_t*)exp->preg, "^Authorization:.*$", REG_EXTENDED | REG_ICASE);
  }

  if(!px->s3_auth_header || !px->s3_key) {
    printf("In s3_resign but have null config fields?");
    return 0;
  }

  struct http_txn *txn = &s->txn;

  struct hdr_ctx authorization_header = {.idx = 0};
  int ret = http_find_header2("Authorization", 13, txn->req.sol, &txn->hdr_idx, &authorization_header);
  if(!ret) {
    printf("No Authorization, so pass through unsigned.");
    return 0;
  }

  // Using exp->replace is only OK because haproxy is single-threaded, so the buffer 
  // will only serve 1 request at a time.
  make_aws_signature((char*)exp->replace + px->s3_auth_header_colon,
    px->s3_key, px->s3_key_len, txn);

  apply_filter_to_req_headers(s, req, exp);

  return 0;
}