/* SPDX-License-Identifier: AGPL-3.0-or-later Copyright (C) 2026 mrkubax10 */ #include #include #include #include #include #include #include #include #include #include static const char* const TOKEN_COOKIE_NAME = "webgate_token"; // Returns true if user agent of client which performed the request looks like Git static bool check_user_agent_exception(request_rec* r) { static const char GIT_USER_AGENT_PREFIX[] = "git/"; const char* const user_agent = apr_table_get(r->headers_in, "User-Agent"); if(!user_agent || strncmp(user_agent, GIT_USER_AGENT_PREFIX, sizeof(GIT_USER_AGENT_PREFIX))!=0) return false; return apr_table_get(r->headers_in, "Git-Protocol"); } static char* generate_token(apr_pool_t* pool) { char rnd[32]; apr_generate_random_bytes(rnd, sizeof(rnd)); char rnd_md5[APR_MD5_DIGESTSIZE]; apr_md5(rnd_md5, rnd, sizeof(rnd)); const apr_size_t encoded_size = apr_base64_encode_len(sizeof(rnd_md5)); char* output = apr_palloc(pool, encoded_size); apr_base64_encode(output, rnd_md5, sizeof(rnd_md5)); return output; } // Generates new token and sets HTTP cookie containing it then presents client with challenge static void handle_challenge(request_rec* r) { char* generated = generate_token(r->pool); ap_cookie_write(r, TOKEN_COOKIE_NAME, generated, NULL, 0, r->headers_out, NULL); ap_rprintf(r, "Token: %s\n", generated); } static int request_check_handler(request_rec* r) { // Provide exception for requests performed by Git if(check_user_agent_exception(r)) return DECLINED; // Check if client already has the token const char* token; if(ap_cookie_read(r, TOKEN_COOKIE_NAME, &token, 0)!=APR_SUCCESS || !token) { handle_challenge(r); return OK; } return OK; } static void register_hooks(apr_pool_t* pool) { ap_hook_handler(request_check_handler, NULL, NULL, APR_HOOK_FIRST); } module AP_MODULE_DECLARE_DATA webgate_module = { STANDARD20_MODULE_STUFF, NULL, NULL, NULL, NULL, NULL, register_hooks };