mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-04-07 02:56:44 +00:00
Allow lcal http servers to perform cross site scripting of restful APIs
This commit is contained in:
parent
d436705e64
commit
31cf3a67b5
@ -997,6 +997,27 @@ static int http_request_parse_header(struct http_request *r)
|
||||
goto malformed;
|
||||
}
|
||||
_rewind(r);
|
||||
if (_skip_literal_nocase(r, "Origin:")) {
|
||||
if (r->request_header.origin) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Skipping duplicate HTTP header Origin: %s", alloca_toprint(50, sol, r->end - sol));
|
||||
r->cursor = nextline;
|
||||
_commit(r);
|
||||
return 0;
|
||||
}
|
||||
_skip_optional_space(r);
|
||||
struct substring origin;
|
||||
if (_skip_word_printable(r, &origin)
|
||||
&& _skip_optional_space(r)
|
||||
&& r->cursor == eol) {
|
||||
r->cursor = nextline;
|
||||
_commit(r);
|
||||
_reserve(r, &r->request_header.origin, origin);
|
||||
return 0;
|
||||
}
|
||||
goto malformed;
|
||||
}
|
||||
_rewind(r);
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Skipped HTTP request header: %s", alloca_toprint(-1, sol, eol - sol));
|
||||
r->cursor = nextline;
|
||||
@ -1979,6 +2000,14 @@ static int _render_response(struct http_request *r)
|
||||
}
|
||||
if (hr.header.content_length != CONTENT_LENGTH_UNKNOWN)
|
||||
strbuf_sprintf(sb, "Content-Length: %"PRIhttp_size_t"\r\n", hr.header.content_length);
|
||||
|
||||
if (hr.header.allow_origin)
|
||||
strbuf_sprintf(sb, "Access-Control-Allow-Origin: %s\r\n", hr.header.allow_origin);
|
||||
if (hr.header.allow_methods)
|
||||
strbuf_sprintf(sb, "Access-Control-Allow-Methods: %s\r\n", hr.header.allow_methods);
|
||||
if (hr.header.allow_headers)
|
||||
strbuf_sprintf(sb, "Access-Control-Allow-Headers: %s\r\n", hr.header.allow_headers);
|
||||
|
||||
const char *scheme = NULL;
|
||||
switch (hr.header.www_authenticate.scheme) {
|
||||
case NOAUTH: break;
|
||||
|
@ -90,6 +90,7 @@ struct http_request_headers {
|
||||
http_size_t content_length;
|
||||
struct mime_content_type content_type;
|
||||
unsigned short content_range_count;
|
||||
const char *origin; // points into buffer; nul terminated
|
||||
struct http_range content_ranges[5];
|
||||
struct http_client_authorization authorization;
|
||||
};
|
||||
@ -100,6 +101,9 @@ struct http_response_headers {
|
||||
http_size_t resource_length; // size of entire resource
|
||||
const char *content_type; // "type/subtype"
|
||||
const char *boundary;
|
||||
char allow_origin[23]; // max supported str (for now) "http://localhost:65537"
|
||||
const char *allow_methods;
|
||||
const char *allow_headers;
|
||||
struct http_www_authenticate www_authenticate;
|
||||
};
|
||||
|
||||
|
18
httpd.c
18
httpd.c
@ -348,6 +348,24 @@ int authorize_restful(struct http_request *r)
|
||||
{
|
||||
if (!is_from_loopback(r))
|
||||
return 403;
|
||||
if (r->request_header.origin){
|
||||
const char *remainder;
|
||||
if (strcasecmp(r->request_header.origin, "null")==0
|
||||
|| (strcase_startswith(r->request_header.origin, "http://localhost", &remainder)
|
||||
&& (*remainder==':' || *remainder=='\0'))
|
||||
|| (strcase_startswith(r->request_header.origin, "http://127.0.0.1", &remainder)
|
||||
&& (*remainder==':' || *remainder=='\0'))
|
||||
){
|
||||
strncpy(r->response.header.allow_origin,r->request_header.origin, sizeof r->response.header.allow_origin);
|
||||
r->response.header.allow_methods="GET, POST, OPTIONS";
|
||||
r->response.header.allow_headers="Authorization";
|
||||
}else
|
||||
return 403;
|
||||
}
|
||||
if (r->verb == HTTP_VERB_OPTIONS){
|
||||
http_request_simple_response(r, 200, NULL);
|
||||
return 200;
|
||||
}
|
||||
if (!is_authorized_restful(&r->request_header.authorization)) {
|
||||
r->response.header.www_authenticate.scheme = BASIC;
|
||||
r->response.header.www_authenticate.realm = "Serval RESTful API";
|
||||
|
@ -117,6 +117,9 @@ int restful_meshms_(httpd_request *r, const char *remainder)
|
||||
r->http.response.header.content_type = CONTENT_TYPE_JSON;
|
||||
if (!is_rhizome_http_enabled())
|
||||
return 403;
|
||||
int ret = authorize_restful(&r->http);
|
||||
if (ret)
|
||||
return ret;
|
||||
const char *verb = HTTP_VERB_GET;
|
||||
http_size_t content_length = CONTENT_LENGTH_UNKNOWN;
|
||||
HTTP_HANDLER *handler = NULL;
|
||||
@ -173,19 +176,15 @@ int restful_meshms_(httpd_request *r, const char *remainder)
|
||||
}
|
||||
if (handler == NULL)
|
||||
return 404;
|
||||
if (r->http.verb != verb)
|
||||
return 405;
|
||||
if ( content_length != CONTENT_LENGTH_UNKNOWN
|
||||
&& r->http.request_header.content_length != CONTENT_LENGTH_UNKNOWN
|
||||
&& r->http.request_header.content_length != content_length) {
|
||||
http_request_simple_response(&r->http, 400, "Bad content length");
|
||||
return 400;
|
||||
}
|
||||
int ret = authorize_restful(&r->http);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = handler(r, remainder);
|
||||
return ret;
|
||||
if (r->http.verb != verb)
|
||||
return 405;
|
||||
return handler(r, remainder);
|
||||
}
|
||||
|
||||
static HTTP_CONTENT_GENERATOR restful_meshms_conversationlist_json_content;
|
||||
|
@ -152,13 +152,13 @@ int restful_rhizome_bundlelist_json(httpd_request *r, const char *remainder)
|
||||
r->http.render_extra_headers = render_manifest_headers;
|
||||
if (!is_rhizome_http_enabled())
|
||||
return 403;
|
||||
int ret = authorize_restful(&r->http);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (*remainder)
|
||||
return 404;
|
||||
if (r->http.verb != HTTP_VERB_GET)
|
||||
return 405;
|
||||
int ret = authorize_restful(&r->http);
|
||||
if (ret)
|
||||
return ret;
|
||||
r->u.rhlist.phase = LIST_HEADER;
|
||||
r->u.rhlist.rowcount = 0;
|
||||
bzero(&r->u.rhlist.cursor, sizeof r->u.rhlist.cursor);
|
||||
@ -184,15 +184,15 @@ int restful_rhizome_newsince(httpd_request *r, const char *remainder)
|
||||
r->http.response.header.content_type = CONTENT_TYPE_JSON;
|
||||
if (!is_rhizome_http_enabled())
|
||||
return 403;
|
||||
int ret = authorize_restful(&r->http);
|
||||
if (ret)
|
||||
return ret;
|
||||
uint64_t rowid;
|
||||
const char *end = NULL;
|
||||
if (!strn_to_list_token(remainder, &rowid, &end) || strcmp(end, "/bundlelist.json") != 0)
|
||||
return 404;
|
||||
if (r->http.verb != HTTP_VERB_GET)
|
||||
return 405;
|
||||
int ret = authorize_restful(&r->http);
|
||||
if (ret)
|
||||
return ret;
|
||||
r->u.rhlist.phase = LIST_HEADER;
|
||||
r->u.rhlist.rowcount = 0;
|
||||
bzero(&r->u.rhlist.cursor, sizeof r->u.rhlist.cursor);
|
||||
@ -325,15 +325,15 @@ int restful_rhizome_insert(httpd_request *r, const char *remainder)
|
||||
{
|
||||
r->http.response.header.content_type = CONTENT_TYPE_JSON;
|
||||
r->http.render_extra_headers = render_manifest_headers;
|
||||
if (*remainder)
|
||||
return 404;
|
||||
if (!is_rhizome_http_enabled())
|
||||
return 403;
|
||||
if (r->http.verb != HTTP_VERB_POST)
|
||||
return 405;
|
||||
int ret = authorize_restful(&r->http);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (*remainder)
|
||||
return 404;
|
||||
if (r->http.verb != HTTP_VERB_POST)
|
||||
return 405;
|
||||
// Parse the request body as multipart/form-data.
|
||||
assert(r->u.insert.current_part == NULL);
|
||||
assert(!r->u.insert.received_author);
|
||||
@ -686,6 +686,9 @@ int restful_rhizome_(httpd_request *r, const char *remainder)
|
||||
r->http.render_extra_headers = render_manifest_headers;
|
||||
if (!is_rhizome_http_enabled())
|
||||
return 403;
|
||||
int ret = authorize_restful(&r->http);
|
||||
if (ret)
|
||||
return ret;
|
||||
HTTP_HANDLER *handler = NULL;
|
||||
rhizome_bid_t bid;
|
||||
const char *end;
|
||||
@ -705,9 +708,6 @@ int restful_rhizome_(httpd_request *r, const char *remainder)
|
||||
return 404;
|
||||
if (r->http.verb != HTTP_VERB_GET)
|
||||
return 405;
|
||||
int ret = authorize_restful(&r->http);
|
||||
if (ret)
|
||||
return ret;
|
||||
if ((r->manifest = rhizome_new_manifest()) == NULL)
|
||||
return 500;
|
||||
ret = rhizome_retrieve_manifest(&bid, r->manifest);
|
||||
|
@ -115,6 +115,75 @@ teardown_AuthBasicWrong() {
|
||||
teardown
|
||||
}
|
||||
|
||||
doc_CORS_Request="Allow local cross site requests, and deny remote ones"
|
||||
test_CORS_Request(){
|
||||
executeOk curl \
|
||||
--silent --fail --show-error --write-out '%{http_code}' \
|
||||
--output http.output \
|
||||
--dump-header http.headers \
|
||||
--header "Origin: http://localhost" \
|
||||
--request "OPTIONS" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
||||
assertStdoutIs '200'
|
||||
assertGrep http.headers "^Access-Control-Allow-Origin: http://localhost$CR\$"
|
||||
assertGrep http.headers "^Access-Control-Allow-Methods: GET, POST, OPTIONS$CR\$"
|
||||
assertGrep http.headers "^Access-Control-Allow-Headers: Authorization$CR\$"
|
||||
executeOk curl \
|
||||
--silent --fail --show-error --write-out '%{http_code}' \
|
||||
--output http.output \
|
||||
--dump-header http.headers \
|
||||
--header "Origin: http://localhost:1234" \
|
||||
--request "OPTIONS" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
||||
assertStdoutIs '200'
|
||||
assertGrep http.headers "^Access-Control-Allow-Origin: http://localhost:1234$CR\$"
|
||||
executeOk curl \
|
||||
--silent --fail --show-error --write-out '%{http_code}' \
|
||||
--output http.output \
|
||||
--dump-header http.headers \
|
||||
--header "Origin: null" \
|
||||
--request "OPTIONS" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
||||
assertStdoutIs '200'
|
||||
assertGrep http.headers "^Access-Control-Allow-Origin: null$CR\$"
|
||||
executeOk curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output http.output \
|
||||
--dump-header http.headers \
|
||||
--header "Origin: http://malevolent.site.com" \
|
||||
--request "OPTIONS" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
||||
assertStdoutIs '403'
|
||||
executeOk curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output http.output \
|
||||
--dump-header http.headers \
|
||||
--header "Origin: http://localhost.malevolent.site.com" \
|
||||
--request "OPTIONS" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
||||
assertStdoutIs '403'
|
||||
executeOk curl \
|
||||
--silent --fail --show-error --write-out '%{http_code}' \
|
||||
--output http.output \
|
||||
--dump-header http.headers \
|
||||
--header "Origin: http://localhost" \
|
||||
--basic --user ron:weasley \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
||||
assertStdoutIs '200'
|
||||
executeOk curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output http.output \
|
||||
--dump-header http.headers \
|
||||
--header "Origin: http://malevolent.site.com" \
|
||||
--basic --user ron:weasley \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
||||
assertStdoutIs '403'
|
||||
}
|
||||
teardown_CORS_Request() {
|
||||
tfw_cat http.headers http.output
|
||||
teardown
|
||||
}
|
||||
|
||||
doc_RhizomeList="HTTP RESTful list 100 Rhizome bundles as JSON"
|
||||
setup_RhizomeList() {
|
||||
setup
|
||||
|
Loading…
x
Reference in New Issue
Block a user