diff --git a/cgit.c b/cgit.c index c52ef33..be1265d 100644 --- a/cgit.c +++ b/cgit.c @@ -614,22 +614,19 @@ static inline void open_auth_filter(struct cgit_context *ctx, const char *functi ctx->qry.url ? ctx->qry.url : ""); } +/* We intentionally keep this rather small, instead of looping and + * feeding it to the filter a couple bytes at a time. This way, the + * filter itself does not need to handle any denial of service or + * buffer bloat issues. If this winds up being too small, people + * will complain on the mailing list, and we'll increase it as needed. */ #define MAX_AUTHENTICATION_POST_BYTES 4096 +/* The filter is expected to spit out "Status: " and all headers. */ static inline void authenticate_post(struct cgit_context *ctx) { - if (ctx->env.http_referer && strlen(ctx->env.http_referer) > 0) { - html("Status: 302 Redirect\n"); - html("Cache-Control: no-cache, no-store\n"); - htmlf("Location: %s\n", ctx->env.http_referer); - } else { - html("Status: 501 Missing Referer\n"); - html("Cache-Control: no-cache, no-store\n\n"); - exit(0); - } - - open_auth_filter(ctx, "authenticate-post"); char buffer[MAX_AUTHENTICATION_POST_BYTES]; int len; + + open_auth_filter(ctx, "authenticate-post"); len = ctx->env.content_length; if (len > MAX_AUTHENTICATION_POST_BYTES) len = MAX_AUTHENTICATION_POST_BYTES; @@ -637,10 +634,7 @@ static inline void authenticate_post(struct cgit_context *ctx) die_errno("Could not read POST from stdin"); if (write(STDOUT_FILENO, buffer, len) < 0) die_errno("Could not write POST to stdout"); - /* The filter may now spit out a Set-Cookie: ... */ cgit_close_filter(ctx->cfg.auth_filter); - - html("\n"); exit(0); } diff --git a/cgitrc.5.txt b/cgitrc.5.txt index c45dbd3..682d8bb 100644 --- a/cgitrc.5.txt +++ b/cgitrc.5.txt @@ -662,7 +662,8 @@ auth filter:: the http cookie and return a 0 if it is invalid or 1 if it is invalid, in the exit code / close function. If the filter action is "authenticate-post", this filter receives POST'd parameters on - standard input, and should write to output one or more "Set-Cookie" + standard input, and should write a complete CGI request, preferably + with a 302 redirect, and write to output one or more "Set-Cookie" HTTP headers, each followed by a newline. Please see `filters/simple-authentication.lua` for a clear example diff --git a/filters/simple-authentication.lua b/filters/simple-authentication.lua index 4cd4983..5935d08 100644 --- a/filters/simple-authentication.lua +++ b/filters/simple-authentication.lua @@ -33,15 +33,28 @@ local secret = "BE SURE TO CUSTOMIZE THIS STRING TO SOMETHING BIG AND RANDOM" -- -- --- Sets HTTP cookie headers based on post +-- Sets HTTP cookie headers based on post and sets up redirection. function authenticate_post() local password = users[post["username"]] - -- TODO: Implement time invariant string comparison function to mitigate against timing attack. - if password == nil or password ~= post["password"] then - construct_cookie("", "cgitauth") - else - construct_cookie(post["username"], "cgitauth") + local redirect = validate_value(post["redirect"]) + + if redirect == nil then + not_found() + return 0 end + + redirect_to(redirect) + + -- TODO: Implement time invariant string comparison function to mitigate timing attack. + if password == nil or password ~= post["password"] then + set_cookie("cgitauth", "") + else + -- One week expiration time + local username = secure_value(post["username"], os.time() + 604800) + set_cookie("cgitauth", username) + end + + html("\n") return 0 end @@ -54,8 +67,8 @@ function authenticate_cookie() return 1 end - local username = validate_cookie(get_cookie(http["cookie"], "cgitauth")) - if username == nil or not accepted_users[username] then + local username = validate_value(get_cookie(http["cookie"], "cgitauth")) + if username == nil or not accepted_users[username:lower()] then return 0 else return 1 @@ -68,6 +81,9 @@ function body() html("