diff --git a/.clangd b/.clangd
index 7a07116..8006528 100644
--- a/.clangd
+++ b/.clangd
@@ -6,4 +6,4 @@ CompileFlags:
- "-I../nginx/objs"
- "-I../nginx/src/os/unix"
- "-I/opt/homebrew/Cellar/pcre2/10.47/include"
- - "-I/opt/homebrew/Cellar/openssl@3/3.6.0/include"
+ - "-I/opt/homebrew/Cellar/openssl@3/3.6.1/include"
diff --git a/Makefile b/Makefile
index 081d20f..e0a3fec 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,7 @@ build: ngx_http_pow.c
.PHONY: configure
configure:
+ cd ../nginx; \
../nginx/auto/configure \
--prefix=/Users/jona/repos/ngx-pow/nginx/install \
--with-debug \
@@ -12,4 +13,4 @@ configure:
.PHONY: run
run: build
- ../nginx/objs/nginx -c "$(PWD)/ngx_http_pow.conf" -g "daemon off;"
+ ../nginx/objs/nginx -c "$(PWD)/ngx_http_pow.conf"
diff --git a/html/pow.html b/html/pow.html
new file mode 100644
index 0000000..eb38c19
--- /dev/null
+++ b/html/pow.html
@@ -0,0 +1,95 @@
+
+
+
+
+
+ PoW Shield
+
+
+
diff --git a/ngx_http_pow.c b/ngx_http_pow.c
index c7bd12a..c763fad 100644
--- a/ngx_http_pow.c
+++ b/ngx_http_pow.c
@@ -6,24 +6,24 @@
#define NGX_HTTP_POW_CHALLENGE_RANDOM_LENGTH 64
#define NGX_HTTP_POW_HMAC_KEY "thisisaverysecretkey"
-#define NGX_HTTP_POW_CHALLENGE_HEADER_KEY "pow-challenge"
-#define NGX_HTTP_POW_CHALLENGE_COOKIE_NAME "pow"
+#define NGX_HTTP_POW_CHALLENGE_COOKIE_KEY "pow-challenge="
+#define NGX_HTTP_POW_CHALLENGE_COOKIE_ATTRIBUTES "; Path=/; SameSite=Lax"
+#define NGX_HTTP_POW_COOKIE_NAME "pow"
#define NGX_HTTP_POW_UNUSED __attribute__((unused))
static ngx_int_t ngx_http_pow_init(ngx_conf_t *cf);
static ngx_int_t ngx_http_pow_handler(ngx_http_request_t *r);
+static ngx_str_t *pow_html;
+
static ngx_http_module_t ngx_http_pow_module_ctx = {
NULL, /* preconfiguration */
ngx_http_pow_init, /* postconfiguration */
-
NULL, /* create main configuration */
NULL, /* init main configuration */
-
NULL, /* create server configuration */
NULL, /* merge server configuration */
-
NULL, /* create location configuration */
NULL /* merge location configuration */
};
@@ -43,8 +43,6 @@ ngx_module_t ngx_http_pow = {
NGX_MODULE_V1_PADDING
};
-#define NGX_HTTP_POW_CHALLENGE_SERIALIZED_LENGTH 256
-
typedef struct __attribute__((packed)) ngx_http_pow_challenge_s {
uint32_t version;
uint32_t created;
@@ -54,16 +52,23 @@ typedef struct __attribute__((packed)) ngx_http_pow_challenge_s {
u_char digest[EVP_MAX_MD_SIZE];
} ngx_http_pow_challenge_t;
+typedef struct __attribute__((packed)) ngx_http_pow_s {
+ ngx_http_pow_challenge_t challenge;
+ uint32_t nonce;
+ u_char hash[EVP_MAX_MD_SIZE];
+} ngx_http_pow_t;
+
static ngx_http_pow_challenge_t
*ngx_http_pow_create_challenge(ngx_pool_t *pool)
{
ngx_http_pow_challenge_t *challenge;
- challenge = ngx_palloc(pool, sizeof(*challenge));
+ challenge = ngx_pcalloc(pool, sizeof(*challenge));
- challenge->version = 1;
- challenge->created = ngx_time();
- challenge->ttl = 30;
+ challenge->version = 1;
+ challenge->created = ngx_time();
+ challenge->ttl = 30;
+ challenge->difficulty = 3;
RAND_bytes(challenge->random, NGX_HTTP_POW_CHALLENGE_RANDOM_LENGTH);
@@ -87,8 +92,8 @@ static ngx_str_t
challenge_string.len = sizeof(*challenge);
encoded_length = ngx_base64_encoded_length(sizeof(*challenge));
- buf = ngx_palloc(pool, encoded_length);
- marshalled = ngx_palloc(pool, sizeof(ngx_str_t));
+ buf = ngx_pcalloc(pool, encoded_length);
+ marshalled = ngx_pcalloc(pool, sizeof(ngx_str_t));
marshalled->len = encoded_length;
marshalled->data = buf;
@@ -104,8 +109,8 @@ static ngx_http_pow_challenge_t NGX_HTTP_POW_UNUSED
ngx_http_pow_challenge_t *challenge;
ngx_str_t *unmarshalled;
- challenge = ngx_palloc(pool, sizeof(*challenge));
- unmarshalled = ngx_palloc(pool, sizeof(ngx_str_t));
+ challenge = ngx_pcalloc(pool, sizeof(*challenge));
+ unmarshalled = ngx_pcalloc(pool, sizeof(ngx_str_t));
unmarshalled->data = (u_char *)challenge;
unmarshalled->len = sizeof(*challenge);
@@ -120,32 +125,58 @@ ngx_http_pow_require_pow(ngx_http_request_t *r)
{
ngx_http_pow_challenge_t *challenge;
ngx_table_elt_t *header;
+ ngx_chain_t out;
ngx_str_t *marshalled;
+ ngx_buf_t *body_buf;
+ u_char *p;
challenge = ngx_http_pow_create_challenge(r->pool);
marshalled = ngx_http_pow_challenge_marshal(challenge, r->pool);
header = ngx_list_push(&r->headers_out.headers);
- ngx_str_set(&header->key, NGX_HTTP_POW_CHALLENGE_HEADER_KEY);
- header->value = *marshalled;
- header->hash = 1;
+ ngx_str_set(&header->key, "Set-Cookie");
- r->headers_out.status = NGX_HTTP_UNAUTHORIZED;
- r->headers_out.content_length = 0;
+ header->hash = 1;
+
+ header->value.len = sizeof(NGX_HTTP_POW_CHALLENGE_COOKIE_KEY) - 1
+ + marshalled->len
+ + sizeof(NGX_HTTP_POW_CHALLENGE_COOKIE_ATTRIBUTES) - 1;
+
+ header->value.data = ngx_pcalloc(r->pool, header->value.len);
+
+ p = header->value.data;
+
+ p = ngx_cpymem(p, NGX_HTTP_POW_CHALLENGE_COOKIE_KEY,
+ sizeof(NGX_HTTP_POW_CHALLENGE_COOKIE_KEY) - 1);
+ p = ngx_cpymem(p, marshalled->data, marshalled->len);
+ p = ngx_cpymem(p, NGX_HTTP_POW_CHALLENGE_COOKIE_ATTRIBUTES,
+ sizeof(NGX_HTTP_POW_CHALLENGE_COOKIE_ATTRIBUTES) - 1);
+
+ r->headers_out.status = NGX_HTTP_UNAUTHORIZED;
+ r->headers_out.content_length_n = pow_html->len;
+
+ body_buf = ngx_calloc_buf(r->pool);
+ if (body_buf == NULL) {
+ /* We done fucked up */
+ }
+
+ body_buf->pos = pow_html->data;
+ body_buf->last = pow_html->data + pow_html->len;
+ body_buf->memory = 1;
+ body_buf->last_buf = 1;
+ body_buf->last_in_chain = 1;
+ body_buf->sync = 0;
+
+ out.buf = body_buf;
+ out.next = NULL;
ngx_http_send_header(r);
+ ngx_http_output_filter(r, &out);
ngx_http_finalize_request(r, NGX_HTTP_UNAUTHORIZED);
}
-typedef struct __attribute__((packed)) ngx_http_pow_s {
- ngx_http_pow_challenge_t challenge;
- uint32_t nonce;
- u_char hash[EVP_MAX_MD_SIZE];
-} ngx_http_pow_t;
-
static ngx_str_t NGX_HTTP_POW_UNUSED
-*ngx_http_pow_marshal(ngx_http_pow_t *pow,
- ngx_pool_t *pool)
+*ngx_http_pow_marshal(ngx_http_pow_t *pow, ngx_pool_t *pool)
{
ngx_str_t pow_string;
ngx_str_t *marshalled;
@@ -156,8 +187,8 @@ static ngx_str_t NGX_HTTP_POW_UNUSED
pow_string.len = sizeof(*pow);
encoded_length = ngx_base64_encoded_length(sizeof(*pow));
- buf = ngx_palloc(pool, encoded_length);
- marshalled = ngx_palloc(pool, sizeof(ngx_str_t));
+ buf = ngx_pcalloc(pool, encoded_length);
+ marshalled = ngx_pcalloc(pool, sizeof(ngx_str_t));
marshalled->len = encoded_length;
marshalled->data = buf;
@@ -173,8 +204,8 @@ static ngx_http_pow_t
ngx_http_pow_t *pow;
ngx_str_t *unmarshalled;
- pow = ngx_palloc(pool, sizeof(*pow));
- unmarshalled = ngx_palloc(pool, sizeof(ngx_str_t));
+ pow = ngx_pcalloc(pool, sizeof(*pow));
+ unmarshalled = ngx_pcalloc(pool, sizeof(ngx_str_t));
unmarshalled->data = (u_char *)pow;
unmarshalled->len = sizeof(*pow);
@@ -232,7 +263,7 @@ ngx_http_pow_check_pow(ngx_http_request_t *r)
ngx_http_pow_t *pow;
ngx_str_t pow_cookie_value;
- ngx_str_t pow_cookie_name = ngx_string(NGX_HTTP_POW_CHALLENGE_COOKIE_NAME);
+ ngx_str_t pow_cookie_name = ngx_string(NGX_HTTP_POW_COOKIE_NAME);
/* No PoW cookie is present */
if (!ngx_http_parse_multi_header_lines(r, r->headers_in.cookie,
@@ -245,8 +276,8 @@ ngx_http_pow_check_pow(ngx_http_request_t *r)
/* Check digest matches */
if (!ngx_http_pow_check_digest(challenge,
- sizeof(*challenge) - sizeof(challenge->digest),
- challenge->digest)) {
+ sizeof(*challenge) - sizeof(challenge->digest),
+ challenge->digest)) {
return false;
}
@@ -256,12 +287,13 @@ ngx_http_pow_check_pow(ngx_http_request_t *r)
}
/* Check pow difficulty */
- ngx_http_pow_check_difficulty(pow, challenge->difficulty);
+ if (!ngx_http_pow_check_difficulty(pow, challenge->difficulty)) {
+ return false;
+ }
/* Check pow is correct */
- if (!ngx_http_pow_check_digest(pow,
- sizeof(*pow) - sizeof(pow->hash),
- pow->hash)) {
+ if (!ngx_http_pow_check_digest(pow, sizeof(*pow) - sizeof(pow->hash),
+ pow->hash)) {
return false;
}
@@ -281,15 +313,59 @@ ngx_http_pow_handler(ngx_http_request_t *r)
return NGX_DECLINED;
}
+static ngx_str_t
+*ngx_http_pow_load_js(ngx_pool_t *pool, ngx_str_t *filename, ngx_log_t *log)
+{
+ size_t filesize;
+ ngx_str_t *pow_html = NULL;
+ ngx_file_t file;
+ ngx_file_info_t file_info;
+
+ file.name = *filename;
+ file.log = log;
+ file.fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+
+ if (file.fd == NGX_INVALID_FILE) {
+ goto cleanup;
+ }
+
+ if (ngx_file_info(filename->data, &file_info) == NGX_FILE_ERROR) {
+ goto cleanup;
+ }
+
+ filesize = ngx_file_size(&file_info);
+
+ pow_html = ngx_pcalloc(pool, sizeof(ngx_str_t));
+ pow_html->data = ngx_pcalloc(pool, filesize);
+ pow_html->len = ngx_read_file(&file, pow_html->data, filesize, 0);
+
+ if (/* pow_html->len == NGX_ERROR || */ pow_html->len != filesize) {
+ pow_html = NULL;
+ }
+
+cleanup:
+
+ ngx_close_file(file.fd);
+
+ return pow_html;
+}
static ngx_int_t
ngx_http_pow_init(ngx_conf_t *cf)
{
+ ngx_str_t pow_html_path =
+ ngx_string("/Users/jona/repos/ngx-pow/ngx-http-pow/html/pow.html");
+
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+ pow_html = ngx_http_pow_load_js(cf->pool, &pow_html_path, cf->log);
+ if (!pow_html) {
+ return NGX_ERROR;
+ }
+
h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
diff --git a/ngx_http_pow.conf b/ngx_http_pow.conf
index c8100a4..77543a0 100644
--- a/ngx_http_pow.conf
+++ b/ngx_http_pow.conf
@@ -1,4 +1,6 @@
-worker_processes 1;
+worker_processes 1;
+daemon off;
+master_process off;
error_log stderr debug;
pid /tmp/nginx.pid;