diff options
author | Vladislav Grishenko <themiron@mail.ru> | 2017-12-04 03:34:05 +0500 |
---|---|---|
committer | Vladislav Grishenko <themiron@mail.ru> | 2017-12-30 22:49:32 +0500 |
commit | 6f08b5ca4298a917ae8f41a93cb199e7b36720a8 (patch) | |
tree | 838a5f50a0159a4dc4abdd67be16883a6f467fe0 /accel-pppd/ctrl | |
parent | 4d6d7e6edc224cfc10ed389d85d1343a54848538 (diff) | |
download | accel-ppp-6f08b5ca4298a917ae8f41a93cb199e7b36720a8.tar.gz accel-ppp-6f08b5ca4298a917ae8f41a93cb199e7b36720a8.zip |
sstp: http: protect against oversized headers and improve parsing
Diffstat (limited to 'accel-pppd/ctrl')
-rw-r--r-- | accel-pppd/ctrl/sstp/sstp.c | 117 |
1 files changed, 70 insertions, 47 deletions
diff --git a/accel-pppd/ctrl/sstp/sstp.c b/accel-pppd/ctrl/sstp/sstp.c index fb379f25..3e96193f 100644 --- a/accel-pppd/ctrl/sstp/sstp.c +++ b/accel-pppd/ctrl/sstp/sstp.c @@ -512,32 +512,33 @@ error: /* http */ -static char *http_getline(struct sstp_conn_t *conn, int *pos, char *buf, int size) +static char *http_getline(struct buffer_t *buf, char *line, size_t size) { - unsigned char *src, *dst, c, pc; + char *src, *dst, *ptr; + int len; - size = min(size - 1, conn->in->len - *pos); - if (size <= 0) + if (buf->len == 0 || size == 0) return NULL; - src = conn->in->head + *pos; - dst = (unsigned char *)buf; - for (pc = 0; size--; dst++) { - c = *dst = *src++; - if (c == '\0') - break; - if (c == '\n') { - if (pc == '\r') - dst--; - break; - } - pc = c; + src = (void *)buf->head; + ptr = memchr(src, '\n', buf->len); + if (ptr) { + len = ptr - src; + buf_pull(buf, len + 1); + if (len > 0 && src[len - 1] == '\r') + len--; + } else { + len = buf->len; + buf_pull(buf, len); } - *dst = '\0'; - *pos = src - conn->in->head; + dst = line; + while (len-- > 0 && size-- > 1) + *dst++ = *src++; + if (size > 0) + *dst = '\0'; - return buf; + return line; } static int http_send_response(struct sstp_conn_t *conn, char *proto, char *status, char *headers) @@ -564,16 +565,18 @@ static int http_send_response(struct sstp_conn_t *conn, char *proto, char *statu return sstp_send(conn, buf); } -static int http_recv_request(struct sstp_conn_t *conn) +static int http_recv_request(struct sstp_conn_t *conn, uint8_t *data, int len) { - char buf[1024]; - char *line, *method, *request, *proto, *host; - int pos = 0; + char linebuf[1024], protobuf[sizeof("HTTP/1.x")]; + char *line, *method, *request, *proto; + struct buffer_t buf; + int host_match; - if (conn->sstp_state != STATE_SERVER_CALL_DISCONNECTED) - return -1; + buf.head = data; + buf.end = data + len; + buf_set_length(&buf, len); - line = http_getline(conn, &pos, buf, sizeof(buf)); + line = http_getline(&buf, linebuf, sizeof(linebuf)); if (!line) goto error; if (conf_verbose) @@ -582,39 +585,44 @@ static int http_recv_request(struct sstp_conn_t *conn) method = strsep(&line, " "); request = strsep(&line, " "); proto = strsep(&line, " "); - host = NULL; if (!method || !request || !proto) { http_send_response(conn, "HTTP/1.1", "400 Bad Request", NULL); goto error; } - if (strncmp(proto, "HTTP/1", sizeof("HTTP/1") - 1) != 0) { + if (strncasecmp(proto, "HTTP/1", sizeof("HTTP/1") - 1) != 0) { http_send_response(conn, "HTTP/1.1", "505 HTTP Version Not Supported", NULL); goto error; } - if (strcmp(method, SSTP_HTTP_METHOD) != 0) { + if (strcasecmp(method, SSTP_HTTP_METHOD) != 0) { http_send_response(conn, proto, "501 Not Implemented", NULL); goto error; } - if (strcmp(request, SSTP_HTTP_URI) != 0) { + if (strcasecmp(request, SSTP_HTTP_URI) != 0) { http_send_response(conn, proto, "404 Not Found", NULL); goto error; } - while ((line = http_getline(conn, &pos, buf, sizeof(buf))) != NULL) { + snprintf(protobuf, sizeof(protobuf), "%s", proto); + proto = protobuf; + + host_match = 0; + while ((line = http_getline(&buf, linebuf, sizeof(linebuf))) != NULL) { if (*line == '\0') break; if (conf_verbose) log_sstp_info2(conn, "recv [HTTP <%s>]\n", line); - if (conf_hostname && strncmp(line, "Host", sizeof("Host") - 1) == 0) { - host = line + sizeof("Host") - 1; - while (*host == ' ' || *host == ':') - host++; + if (conf_hostname && strncasecmp(line, "Host", sizeof("Host") - 1) == 0) { + line += sizeof("Host") - 1; + while (*line == ' ' || *line == ':') + line++; + if (strcasecmp(line, conf_hostname) == 0) + host_match = 1; } - } while (*line); + } - if (conf_hostname && (!host || strcasecmp(host, conf_hostname) != 0)) { + if (conf_hostname && !host_match) { http_send_response(conn, proto, "434 Requested host unavailable", NULL); goto error; } @@ -623,10 +631,7 @@ static int http_recv_request(struct sstp_conn_t *conn) "Content-Length: 18446744073709551615\r\n")) { goto error; } - - conn->sstp_state = STATE_SERVER_CONNECT_REQUEST_PENDING; - - return pos; + return 0; error: return -1; @@ -634,17 +639,35 @@ error: static int http_handler(struct sstp_conn_t *conn, struct buffer_t *buf) { + static const char *table[] = { "\r\n\r\n", "\n\n", NULL }; + const char **pptr; + char *ptr, *end; int n; - n = http_recv_request(conn); - if (n < 0) + if (conn->sstp_state != STATE_SERVER_CALL_DISCONNECTED) return -1; - buf_pull(buf, n); - if (conn->sstp_state == STATE_SERVER_CONNECT_REQUEST_PENDING) - conn->handler = sstp_handler; + end = NULL; + for (pptr = table; *pptr; pptr++) { + ptr = memmem(buf->head, buf->len, *pptr, strlen(*pptr)); + if (ptr && (!end || ptr < end)) + end = ptr + strlen(*pptr); + } + if (!end) { + if (buf_tailroom(buf) > 0) + return 0; + log_sstp_error(conn, "recv [HTTP too long header]\n"); + return -1; + } else + n = end - (char *)buf->head; - return n; + if (http_recv_request(conn, buf->head, n) < 0) + return -1; + buf_pull(buf, n); + + conn->sstp_state = STATE_SERVER_CONNECT_REQUEST_PENDING; + conn->handler = sstp_handler; + return 0; } /* ppp */ |