Generate exact-hash SDK source overrides without modifying dependencies. Harden SSH allocation and algorithm policy, tighten web authentication cleanup, and add focused host contract tests and documentation.
120 lines
5.7 KiB
C
120 lines
5.7 KiB
C
/* SPDX-License-Identifier: GPL-3.0-only */
|
|
#include <sys/types.h>
|
|
#include "alloc.h"
|
|
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
|
#define HTTPD_SOCK_ERR_TIMEOUT -10
|
|
#define HTTPD_SOCK_ERR_FAIL -11
|
|
#define HTTPD_408_REQ_TIMEOUT 408
|
|
struct sock_db {
|
|
char pending_data[128]; size_t pending_len;
|
|
void *ctx; void (*free_ctx)(void *); bool ignore_sess_ctx_changes;
|
|
};
|
|
struct httpd_req_aux {
|
|
struct sock_db *sd;
|
|
char *scratch; size_t scratch_cur_size, scratch_size_limit, remaining_len;
|
|
};
|
|
typedef struct httpd_req {
|
|
struct httpd_req_aux *aux; void *sess_ctx, *handle, *user_ctx;
|
|
void (*free_ctx)(void *); bool ignore_sess_ctx_changes;
|
|
} httpd_req_t;
|
|
typedef struct { void *data; } http_parser;
|
|
typedef struct { struct { char *at; } last; } parser_data_t;
|
|
static int receive_result = 1, receive_calls;
|
|
static int httpd_req_handle_err(httpd_req_t *r, int err) { return ESP_FAIL; }
|
|
static void httpd_sess_free_ctx(void **ctx, void (*fn)(void *)) { assert(!*ctx); }
|
|
static int httpd_recv_with_opt(httpd_req_t *r, char *buf, size_t n, bool halt_after_pending);
|
|
/* SDK_FUNCTIONS */
|
|
static int httpd_recv_with_opt(httpd_req_t *r, char *buf, size_t n, bool halt_after_pending) {
|
|
++receive_calls;
|
|
assert(halt_after_pending);
|
|
if (r->aux->sd->pending_len) return (int)httpd_recv_pending(r, buf, n);
|
|
if (receive_result <= 0) return receive_result;
|
|
memset(buf, 'x', n); return (int)n;
|
|
}
|
|
static void cleanup(httpd_req_t *r, struct httpd_req_aux *ra, struct sock_db *sd) {
|
|
r->aux = ra; ra->sd = sd;
|
|
httpd_req_cleanup(r);
|
|
assert(!ra->scratch && !ra->scratch_cur_size && !r->aux && !live);
|
|
}
|
|
int main(void) {
|
|
all_secret = true;
|
|
struct sock_db sd = {0};
|
|
struct httpd_req_aux ra = {.sd=&sd, .scratch_size_limit=64, .remaining_len=37};
|
|
httpd_req_t r = {.aux=&ra};
|
|
parser_data_t data = {0}; http_parser parser = {.data=&data};
|
|
/* Equivalent pointer/size initialization to parse_init/init_req_aux:
|
|
* the first read must allocate its own scratch, with no parser position. */
|
|
assert(!data.last.at && !ra.scratch && !ra.scratch_cur_size);
|
|
unsigned initial_wipes = wiped_frees;
|
|
int initial_reads = receive_calls;
|
|
fail_at = calls + 1;
|
|
assert(read_block(&r, &parser, 0, 8) == 0);
|
|
assert(!data.last.at && !ra.scratch && !ra.scratch_cur_size && !live);
|
|
assert(receive_calls == initial_reads && wiped_frees == initial_wipes);
|
|
cleanup(&r, &ra, &sd);
|
|
assert(wiped_frees == initial_wipes);
|
|
fail_at = 0;
|
|
r.aux = &ra; ra.sd = &sd; ra.scratch_size_limit = 64;
|
|
assert(read_block(&r, &parser, 0, 8) == 8);
|
|
assert(ra.scratch && ra.scratch_cur_size == 8 && !data.last.at);
|
|
assert(receive_calls == initial_reads + 1 && wiped_frees == initial_wipes);
|
|
assert(!memcmp(ra.scratch, "xxxxxxxx", 8));
|
|
/* A fragmented request can need another read before the URL callback. */
|
|
char *initial = ra.scratch;
|
|
fail_at = calls + 1;
|
|
assert(read_block(&r, &parser, 8, 8) == 0);
|
|
assert(ra.scratch == initial && ra.scratch_cur_size == 8 && !data.last.at);
|
|
assert(receive_calls == initial_reads + 1 && wiped_frees == initial_wipes);
|
|
fail_at = 0;
|
|
assert(read_block(&r, &parser, 8, 8) == 8);
|
|
assert(ra.scratch != initial && ra.scratch_cur_size == 16 && !data.last.at);
|
|
assert(!memcmp(ra.scratch, "xxxxxxxxxxxxxxxx", 16));
|
|
assert(wiped_frees == initial_wipes + 1);
|
|
cleanup(&r, &ra, &sd);
|
|
assert(wiped_frees == initial_wipes + 2);
|
|
|
|
r.aux = &ra; ra.sd = &sd; ra.scratch_size_limit = 64;
|
|
assert(read_block(&r, &parser, 0, 16) == 16);
|
|
assert(!data.last.at);
|
|
memcpy(ra.scratch, "Cookie: secret!!", 16);
|
|
data.last.at = ra.scratch + 7;
|
|
char *old = ra.scratch; unsigned before = wiped_frees;
|
|
assert(read_block(&r, &parser, 16, 8) == 8);
|
|
assert(ra.scratch != old && ra.scratch_cur_size == 24);
|
|
assert(!memcmp(ra.scratch, "Cookie: secret!!", 16));
|
|
assert(data.last.at == ra.scratch + 7 && wiped_frees == before + 1);
|
|
old = ra.scratch; unsigned saved_calls = calls;
|
|
assert(security_override_resize_scratch(&ra, 24) && ra.scratch == old && calls == saved_calls);
|
|
fail_at = calls + 1;
|
|
int reads = receive_calls;
|
|
assert(read_block(&r, &parser, 24, 8) == 0);
|
|
assert(ra.scratch == old && ra.scratch_cur_size == 24 && receive_calls == reads);
|
|
assert(data.last.at == old + 7 && !memcmp(old, "Cookie: secret!!", 16));
|
|
assert(ra.remaining_len == 37);
|
|
fail_at = 0;
|
|
assert(httpd_unrecv(&r, "NEXT-REQUEST", 12) == 12);
|
|
assert(read_block(&r, &parser, 4, 4) == 4); /* actual shrink + pending RX */
|
|
assert(ra.scratch_cur_size == 8 && !memcmp(ra.scratch, "CookNEXT", 8));
|
|
assert(sd.pending_len == 8 && !memcmp(sd.pending_data + 120, "-REQUEST", 8));
|
|
assert(ra.remaining_len == 37);
|
|
char pending[128]; memcpy(pending, sd.pending_data, sizeof(pending));
|
|
cleanup(&r, &ra, &sd);
|
|
assert(sd.pending_len == 8 && !memcmp(pending, sd.pending_data, sizeof(pending)));
|
|
r.aux = &ra; ra.sd = &sd;
|
|
char out[12] = {0}; assert(httpd_recv_pending(&r, out, 8) == 8 && !memcmp(out, "-REQUEST", 8));
|
|
ra.scratch_size_limit = 64;
|
|
fail_at = calls + 1;
|
|
assert(!security_override_resize_scratch(&ra, 8) && !ra.scratch);
|
|
cleanup(&r, &ra, &sd); fail_at = 0;
|
|
for (int result = 0; result >= -2; --result) {
|
|
r.aux = &ra; ra.sd = &sd; ra.scratch_size_limit = 64;
|
|
assert(security_override_resize_scratch(&ra, 8)); data.last.at = ra.scratch;
|
|
receive_result = result == -2 ? HTTPD_SOCK_ERR_TIMEOUT : result;
|
|
assert(read_block(&r, &parser, 0, 8) == HTTPD_SOCK_ERR_FAIL);
|
|
int before_reads = receive_calls;
|
|
assert(read_block(&r, &parser, 64, 1) == 0 && before_reads == receive_calls);
|
|
cleanup(&r, &ra, &sd);
|
|
}
|
|
puts("HTTPD null-initial read/grow/shrink/failure/final wipe/bounds/pending-unread matrix PASS");
|
|
}
|