/* * 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); }
/* * 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; }