diff --git a/lib/http_request.c b/lib/http_request.c index ea28ac5..ec030c4 100644 --- a/lib/http_request.c +++ b/lib/http_request.c @@ -273,6 +273,30 @@ http_request_get_header(http_request_t *request, const char *name) return NULL; } +size_t +http_request_header_get_size(http_request_t *request, int *num_fields, size_t *max_field_len, size_t *max_value_len) { + size_t total = 0; + if (max_field_len) { + *max_field_len = 0; + } + if (max_value_len) { + *max_value_len = 0; + } + if (num_fields) { + *num_fields = request->headers_size / 2; + } + for (int i = 0; i < request->headers_size; i +=2) { + size_t len = strlen(request->headers[i]); + total += len; + if (i % 2 == 0 && max_field_len && len > *max_field_len) { + *max_field_len = len; + } else if (max_value_len && len > *max_value_len) { + *max_value_len = len; + } + } + return total; +} + const char * http_request_get_data(http_request_t *request, int *datalen) { diff --git a/lib/http_request.h b/lib/http_request.h index dd13680..5308947 100644 --- a/lib/http_request.h +++ b/lib/http_request.h @@ -32,6 +32,7 @@ const char *http_request_get_url(http_request_t *request); const char *http_request_get_protocol(http_request_t *request); const char *http_request_get_header(http_request_t *request, const char *name); const char *http_request_get_data(http_request_t *request, int *datalen); +size_t http_request_header_get_size(http_request_t *request, int *fields, size_t *max_field_len, size_t *max_value_len); int http_request_get_header_string(http_request_t *request, char **header_str); bool http_request_is_reverse(http_request_t *request); void http_request_set_reverse(http_request_t *request); diff --git a/lib/http_response.c b/lib/http_response.c index 8cbac0c..865c2a2 100644 --- a/lib/http_response.c +++ b/lib/http_response.c @@ -25,11 +25,12 @@ struct http_response_s { int disconnect; char *data; - int data_size; + int buffer_size; int data_length; }; +#define MAX_RESPONSE_SIZE (64 * 1024) static void http_response_add_data(http_response_t *response, const char *data, int datalen) { @@ -37,13 +38,24 @@ http_response_add_data(http_response_t *response, const char *data, int datalen) assert(data); assert(datalen > 0); - int newdatasize = response->data_size; - while (response->data_length + datalen > newdatasize) { - newdatasize *= 2; + if (response->data_length + datalen > MAX_RESPONSE_SIZE) { + fprintf(stderr, "ERROR: http_response_add_data: cannot add data as MAX_RESPONSE_SIZE = %d would be exceeded\n", + (int) MAX_RESPONSE_SIZE); + return; } - if (newdatasize != response->data_size) { - response->data = realloc(response->data, newdatasize); + + size_t newbufsize = response->buffer_size; + while (response->data_length + datalen > newbufsize) { + newbufsize *= 2; + if (newbufsize > MAX_RESPONSE_SIZE) { + newbufsize = MAX_RESPONSE_SIZE; + break; + } + } + if (newbufsize != response->buffer_size) { + response->data = realloc(response->data, newbufsize); assert(response->data); + response->buffer_size = newbufsize; } memcpy(response->data+response->data_length, data, datalen); response->data_length += datalen; @@ -58,8 +70,8 @@ http_response_create() return NULL; } /* Allocate response data */ - response->data_size = 1024; - response->data = (char *) malloc(response->data_size); + response->buffer_size = 1024; + response->data = (char *) malloc(response->buffer_size); if (!response->data) { free(response); return NULL; diff --git a/lib/raop.c b/lib/raop.c index a470bf5..a34fadb 100644 --- a/lib/raop.c +++ b/lib/raop.c @@ -219,8 +219,37 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) { return; } - /* ¨idenitfy if request is a response to a BLE beaconn */ - const char *cseq = http_request_get_header(request, "CSeq"); +#define MAX_HDR_FIELDS 20 +#define MAX_HDR_FIELD_LEN 64 +#define MAX_HDR_VALUE_LEN 1024 + /*impose limits on header sizes to defend against DOS attacks */ + size_t max_field_len, max_value_len; + int num_fields; + http_request_header_get_size(request, &num_fields, &max_field_len, &max_value_len); + if (num_fields > MAX_HDR_FIELDS || max_field_len > MAX_HDR_FIELD_LEN || max_value_len > MAX_HDR_VALUE_LEN) { + logger_log(raop->logger, LOGGER_ERR, "rejecting request with overlong headers: %d fields," + "max field_name length %d, max field value length %d", + num_fields, (int) max_field_len, (int) max_value_len); + *response = http_response_create(); + http_response_init(*response, protocol, 431, "Request Header Fields Too Large"); + return; + } + + /* handle CSeq header carefully, as it will be included in response: value should be non-negative int */ + char *cseq = NULL; + char cseq_buf[11] = {0}; + const char *cseq_req = http_request_get_header(request, "CSeq"); + if (cseq_req) { + int cseq_val = parse_int(cseq_req); + if (cseq_val < 0) { + logger_log(raop->logger, LOGGER_ERR, "rejecting request with invalid CSeq value %s", cseq_req); + return; //CSeq header field had invalid value + } + snprintf(cseq_buf, sizeof(cseq_buf), "%u", (unsigned int) cseq_val); + cseq = cseq_buf; + } + + /* ¨identify if request is a response to a BLE beacon */ bool ble = false; if (!strcmp(protocol,"RTSP/1.0") && !cseq && (strstr(url, "txtAirPlay") || strstr(url, "txtRAOP") )) { logger_log(raop->logger, LOGGER_INFO, "response to Bluetooth LE beacon advertisement received)"); diff --git a/lib/utils.c b/lib/utils.c index a4d1571..f7ebc22 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -21,6 +21,8 @@ #include #include #include +#include + #define SECOND_IN_NSECS 1000000000UL char * @@ -344,3 +346,17 @@ const char *gmt_time_string() { return ""; } } + +int parse_int(const char * str) { + /* verify that a string represents a non-negative int, and return it, or return -1 */ + char *end_ptr; + assert(str); + long val = strtol(str, &end_ptr, 10); + if ((val == 0 && end_ptr == str) || *end_ptr != '\0') { + return -1; + } + if (val < 0 || val > INT_MAX) { + return -1; + } + return (int) val; +} diff --git a/lib/utils.h b/lib/utils.h index c55bb0e..fcdc0ff 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -34,4 +34,5 @@ const char *gmt_time_string(); int utils_ipaddress_to_string(int addresslen, const unsigned char *address, unsigned int zone_id, char *string, int len); char *utils_strip_data_from_plist_xml(char * plist_xml); +int parse_int(const char * str); #endif