From 14d2d779cec8f6d6eb4a15fe256d8e21964a900c Mon Sep 17 00:00:00 2001 From: Matt Rodgers Date: Tue, 31 Dec 2024 14:39:30 +0000 Subject: [PATCH] net: http_server: serve resources only for their defined services Ensure that HTTP resources can only be served to a client connected on the specific service(s) that the resource was registered against using the HTTP_RESOURCE_DEFINE macro. This allows different resources to be registered to different services, for example to make some resources only available via an HTTPS service and not via unencrypted HTTP. Signed-off-by: Matt Rodgers --- include/zephyr/net/http/server.h | 3 + include/zephyr/net/http/service.h | 5 +- subsys/net/lib/http/headers/server_internal.h | 3 +- subsys/net/lib/http/http_server_core.c | 64 ++++++++++++------- subsys/net/lib/http/http_server_http1.c | 4 +- subsys/net/lib/http/http_server_http2.c | 4 +- tests/net/lib/http_server/common/src/main.c | 34 +++++++--- 7 files changed, 78 insertions(+), 39 deletions(-) diff --git a/include/zephyr/net/http/server.h b/include/zephyr/net/http/server.h index 4cb40e52eb6de5..621f14e9a887ea 100644 --- a/include/zephyr/net/http/server.h +++ b/include/zephyr/net/http/server.h @@ -399,6 +399,9 @@ struct http_client_ctx { /** Socket descriptor associated with the server. */ int fd; + /** HTTP service on which the client is connected */ + const struct http_service_desc *service; + /** Client data buffer. */ unsigned char buffer[HTTP_SERVER_CLIENT_BUFFER_SIZE]; diff --git a/include/zephyr/net/http/service.h b/include/zephyr/net/http/service.h index 6a836b7334822b..7829c8eed0a188 100644 --- a/include/zephyr/net/http/service.h +++ b/include/zephyr/net/http/service.h @@ -67,6 +67,7 @@ struct http_resource_desc { struct http_service_desc { const char *host; uint16_t *port; + int *fd; void *detail; size_t concurrent; size_t backlog; @@ -80,9 +81,11 @@ struct http_service_desc { #define __z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail, _res_begin, \ _res_end, ...) \ - const STRUCT_SECTION_ITERABLE(http_service_desc, _name) = { \ + static int _name##_fd = -1; \ + const STRUCT_SECTION_ITERABLE(http_service_desc, _name) = { \ .host = _host, \ .port = (uint16_t *)(_port), \ + .fd = &_name##_fd, \ .detail = (void *)(_detail), \ .concurrent = (_concurrent), \ .backlog = (_backlog), \ diff --git a/subsys/net/lib/http/headers/server_internal.h b/subsys/net/lib/http/headers/server_internal.h index 5048a8543fbecc..fe8fe37816d4d4 100644 --- a/subsys/net/lib/http/headers/server_internal.h +++ b/subsys/net/lib/http/headers/server_internal.h @@ -37,7 +37,8 @@ int enter_http2_request(struct http_client_ctx *client); int enter_http_done_state(struct http_client_ctx *client); /* Others */ -struct http_resource_detail *get_resource_detail(const char *path, int *len, bool is_ws); +struct http_resource_detail *get_resource_detail(const struct http_service_desc *service, + const char *path, int *len, bool is_ws); int http_server_sendall(struct http_client_ctx *client, const void *buf, size_t len); void http_server_get_content_type_from_extension(char *url, char *content_type, size_t content_type_size); diff --git a/subsys/net/lib/http/http_server_core.c b/subsys/net/lib/http/http_server_core.c index 61fae3b5862373..d5ef4999c77f02 100644 --- a/subsys/net/lib/http/http_server_core.c +++ b/subsys/net/lib/http/http_server_core.c @@ -237,6 +237,7 @@ int http_server_init(struct http_server_ctx *ctx) LOG_DBG("Initialized HTTP Service %s:%u", svc->host ? svc->host : "", *svc->port); + *svc->fd = fd; ctx->fds[count].fd = fd; ctx->fds[count].events = ZSOCK_POLLIN; count++; @@ -299,6 +300,10 @@ static void close_all_sockets(struct http_server_ctx *ctx) ctx->fds[i].fd = -1; } + + HTTP_SERVICE_FOREACH(svc) { + *svc->fd = -1; + } } static void client_release_resources(struct http_client_ctx *client) @@ -393,9 +398,22 @@ void http_client_timer_restart(struct http_client_ctx *client) k_work_reschedule(&client->inactivity_timer, INACTIVITY_TIMEOUT); } -static void init_client_ctx(struct http_client_ctx *client, int new_socket) +static const struct http_service_desc *lookup_service(int server_fd) +{ + HTTP_SERVICE_FOREACH(svc) { + if (*svc->fd == server_fd) { + return svc; + } + } + + return NULL; +} + +static void init_client_ctx(struct http_client_ctx *client, const struct http_service_desc *svc, + int new_socket) { client->fd = new_socket; + client->service = svc; client->data_len = 0; client->server_state = HTTP_SERVER_PREFACE_STATE; client->has_upgrade_header = false; @@ -523,6 +541,7 @@ static int handle_http_request(struct http_client_ctx *client) static int http_server_run(struct http_server_ctx *ctx) { struct http_client_ctx *client; + const struct http_service_desc *service; eventfd_t value; bool found_slot; int new_socket; @@ -600,6 +619,9 @@ static int http_server_run(struct http_server_ctx *ctx) continue; } + service = lookup_service(ctx->fds[i].fd); + __ASSERT(NULL != service, "fd not associated with a service"); + found_slot = false; for (j = ctx->listen_fds; j < ARRAY_SIZE(ctx->fds); j++) { @@ -615,7 +637,7 @@ static int http_server_run(struct http_server_ctx *ctx) LOG_DBG("Init client #%d", j - ctx->listen_fds); - init_client_ctx(&ctx->clients[j - ctx->listen_fds], + init_client_ctx(&ctx->clients[j - ctx->listen_fds], service, new_socket); found_slot = true; break; @@ -713,34 +735,30 @@ static bool skip_this(struct http_resource_desc *resource, bool is_websocket) return false; } -struct http_resource_detail *get_resource_detail(const char *path, - int *path_len, - bool is_websocket) +struct http_resource_detail *get_resource_detail(const struct http_service_desc *service, + const char *path, int *path_len, bool is_websocket) { - HTTP_SERVICE_FOREACH(service) { - HTTP_SERVICE_FOREACH_RESOURCE(service, resource) { - if (skip_this(resource, is_websocket)) { - continue; - } - - if (IS_ENABLED(CONFIG_HTTP_SERVER_RESOURCE_WILDCARD)) { - int ret; - - ret = fnmatch(resource->resource, path, - (FNM_PATHNAME | FNM_LEADING_DIR)); - if (ret == 0) { - *path_len = strlen(resource->resource); - return resource->detail; - } - } + HTTP_SERVICE_FOREACH_RESOURCE(service, resource) { + if (skip_this(resource, is_websocket)) { + continue; + } - if (compare_strings(path, resource->resource) == 0) { - NET_DBG("Got match for %s", resource->resource); + if (IS_ENABLED(CONFIG_HTTP_SERVER_RESOURCE_WILDCARD)) { + int ret; + ret = fnmatch(resource->resource, path, (FNM_PATHNAME | FNM_LEADING_DIR)); + if (ret == 0) { *path_len = strlen(resource->resource); return resource->detail; } } + + if (compare_strings(path, resource->resource) == 0) { + NET_DBG("Got match for %s", resource->resource); + + *path_len = strlen(resource->resource); + return resource->detail; + } } NET_DBG("No match for %s", path); diff --git a/subsys/net/lib/http/http_server_http1.c b/subsys/net/lib/http/http_server_http1.c index e5ea4d478d2811..e8881d36fe8825 100644 --- a/subsys/net/lib/http/http_server_http1.c +++ b/subsys/net/lib/http/http_server_http1.c @@ -859,7 +859,7 @@ int handle_http1_request(struct http_client_ctx *client) if (client->websocket_upgrade) { if (IS_ENABLED(CONFIG_HTTP_SERVER_WEBSOCKET)) { - detail = get_resource_detail(client->url_buffer, + detail = get_resource_detail(client->service, client->url_buffer, &path_len, true); if (detail == NULL) { goto not_found; @@ -899,7 +899,7 @@ int handle_http1_request(struct http_client_ctx *client) } } - detail = get_resource_detail(client->url_buffer, &path_len, false); + detail = get_resource_detail(client->service, client->url_buffer, &path_len, false); if (detail != NULL) { detail->path_len = path_len; diff --git a/subsys/net/lib/http/http_server_http2.c b/subsys/net/lib/http/http_server_http2.c index 5a21691d4ab848..2849101c08a5f9 100644 --- a/subsys/net/lib/http/http_server_http2.c +++ b/subsys/net/lib/http/http_server_http2.c @@ -1029,7 +1029,7 @@ int handle_http1_to_http2_upgrade(struct http_client_ctx *client) client->preface_sent = true; } - detail = get_resource_detail(client->url_buffer, &path_len, false); + detail = get_resource_detail(client->service, client->url_buffer, &path_len, false); if (detail != NULL) { detail->path_len = path_len; @@ -1509,7 +1509,7 @@ int handle_http_frame_headers(struct http_client_ctx *client) return 0; } - detail = get_resource_detail(client->url_buffer, &path_len, false); + detail = get_resource_detail(client->service, client->url_buffer, &path_len, false); if (detail != NULL) { detail->path_len = path_len; diff --git a/tests/net/lib/http_server/common/src/main.c b/tests/net/lib/http_server/common/src/main.c index 5c4ab204873a0f..c944cd7729f667 100644 --- a/tests/net/lib/http_server/common/src/main.c +++ b/tests/net/lib/http_server/common/src/main.c @@ -296,55 +296,69 @@ ZTEST(http_service, test_HTTP_RESOURCE_DEFINE) } } -extern struct http_resource_detail *get_resource_detail(const char *path, +extern struct http_resource_detail *get_resource_detail(const struct http_service_desc *service, + const char *path, int *path_len, bool is_websocket); -#define CHECK_PATH(path, len) ({ *len = 0; get_resource_detail(path, len, false); }) +#define CHECK_PATH(svc, path, len) ({ *len = 0; get_resource_detail(&svc, path, len, false); }) ZTEST(http_service, test_HTTP_RESOURCE_WILDCARD) { struct http_resource_detail *res; int len; - res = CHECK_PATH("/", &len); + res = CHECK_PATH(service_A, "/", &len); zassert_not_null(res, "Cannot find resource"); zassert_true(len > 0, "Length not set"); zassert_equal(res, RES(0), "Resource mismatch"); - res = CHECK_PATH("/f", &len); + res = CHECK_PATH(service_D, "/f", &len); zassert_is_null(res, "Resource found"); zassert_equal(len, 0, "Length set"); - res = CHECK_PATH("/foo1.html", &len); + res = CHECK_PATH(service_D, "/foo1.html", &len); zassert_not_null(res, "Cannot find resource"); zassert_true(len > 0, "Length not set"); zassert_equal(res, RES(0), "Resource mismatch"); - res = CHECK_PATH("/foo2222.html", &len); + res = CHECK_PATH(service_D, "/foo2222.html", &len); zassert_not_null(res, "Cannot find resource"); zassert_true(len > 0, "Length not set"); zassert_equal(res, RES(1), "Resource mismatch"); - res = CHECK_PATH("/fbo3.html", &len); + res = CHECK_PATH(service_D, "/fbo3.html", &len); zassert_not_null(res, "Cannot find resource"); zassert_true(len > 0, "Length not set"); zassert_equal(res, RES(1), "Resource mismatch"); - res = CHECK_PATH("/fbo3.htm", &len); + res = CHECK_PATH(service_D, "/fbo3.htm", &len); zassert_not_null(res, "Cannot find resource"); zassert_true(len > 0, "Length not set"); zassert_equal(res, RES(0), "Resource mismatch"); - res = CHECK_PATH("/fbo4.html", &len); + res = CHECK_PATH(service_D, "/fbo4.html", &len); zassert_not_null(res, "Cannot find resource"); zassert_true(len > 0, "Length not set"); zassert_equal(res, RES(3), "Resource mismatch"); - res = CHECK_PATH("/fs/index.html", &len); + res = CHECK_PATH(service_A, "/fs/index.html", &len); zassert_not_null(res, "Cannot find resource"); zassert_true(len > 0, "Length not set"); zassert_equal(res, RES(5), "Resource mismatch"); + + /* Resources that only exist on one service should not be found on another */ + res = CHECK_PATH(service_A, "/foo1.htm", &len); + zassert_is_null(res, "Resource found"); + zassert_equal(len, 0, "Length set"); + + res = CHECK_PATH(service_A, "/foo2222.html", &len); + zassert_is_null(res, "Resource found"); + zassert_equal(len, 0, "Length set"); + + res = CHECK_PATH(service_A, "/fbo3.htm", &len); + zassert_is_null(res, "Resource found"); + zassert_equal(len, 0, "Length set"); } extern void http_server_get_content_type_from_extension(char *url, char *content_type,