Mercurial
view auth/auth_http.c @ 280:49e9e591c9bb
Add persistent dictation, prewarmed WebRTC speech input, Copilot SDK routing, animated conversation lifecycle controls, parking, and architecture coverage.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Tue, 18 Aug 2026 19:14:53 -0700 |
| parents | b3b547563ec7 |
| children |
line wrap: on
line source
#include "auth/auth_http.h" #include <openssl/crypto.h> #include <openssl/evp.h> #include <openssl/hmac.h> #include <string.h> #define AUTH_HTTP_COOKIE_VALUE_MAX 512 #define AUTH_HTTP_CSRF_SUFFIX ":csrf:v1" #define AUTH_HTTP_BINDING_INPUT_MAX (AUTH_CRYPTO_TOKEN_DIGEST_SIZE + 16) const char *Auth_HTTP_Request_Value( Seobeo_Request_Entry *p_request, const char *key) { if (!p_request || !key) return NULL; void *p_value = Dowa_HashMap_Get_Ptr(p_request, (char *)key); return p_value ? ((Seobeo_Request_Entry *)p_value)->value : NULL; } boolean Auth_HTTP_Parse_Cookie( const char *cookie_header, const char *name, char *value_out, size_t capacity) { if (!value_out || capacity == 0) return FALSE; value_out[0] = '\0'; if (!cookie_header || !name || name[0] == '\0') return FALSE; size_t name_length = strlen(name); const char *cursor = cookie_header; while (*cursor) { while (*cursor == ' ' || *cursor == '\t') cursor++; if (strncmp(cursor, name, name_length) == 0 && cursor[name_length] == '=') { cursor += name_length + 1; const char *start = cursor; while (*cursor && *cursor != ';') cursor++; size_t value_length = (size_t)(cursor - start); if (value_length >= capacity) return FALSE; memcpy(value_out, start, value_length); value_out[value_length] = '\0'; return TRUE; } while (*cursor && *cursor != ';') cursor++; if (*cursor == ';') cursor++; } return FALSE; } boolean Auth_HTTP_Same_Origin(Seobeo_Request_Entry *p_request) { const char *host = Auth_HTTP_Request_Value(p_request, "Host"); const char *origin = Auth_HTTP_Request_Value(p_request, "Origin"); if (!host || !origin) return FALSE; const char *origin_host = strstr(origin, "://"); if (!origin_host) return FALSE; origin_host += 3; const char *end = strchr(origin_host, '/'); size_t origin_host_length = end ? (size_t)(end - origin_host) : strlen(origin_host); return strlen(host) == origin_host_length && strncmp(host, origin_host, origin_host_length) == 0; } boolean Auth_HTTP_Derive_CSRF( const uint8 *cookie_secret, size_t cookie_secret_length, const char *binding, char *csrf_out, size_t csrf_capacity) { if (!cookie_secret || cookie_secret_length < AUTH_CRYPTO_COOKIE_SECRET_MIN_BYTES || cookie_secret_length > AUTH_CRYPTO_COOKIE_SECRET_MAX_BYTES || !binding || !csrf_out || csrf_capacity < AUTH_CRYPTO_TOKEN_SIZE) return FALSE; size_t binding_length = strlen(binding); size_t suffix_length = strlen(AUTH_HTTP_CSRF_SUFFIX); size_t input_length = binding_length + suffix_length; if (input_length >= AUTH_HTTP_BINDING_INPUT_MAX) return FALSE; char input[AUTH_HTTP_BINDING_INPUT_MAX] = {0}; memcpy(input, binding, binding_length); memcpy(input + binding_length, AUTH_HTTP_CSRF_SUFFIX, suffix_length); uint8 digest[32] = {0}; uint32 digest_length = sizeof(digest); if (!HMAC(EVP_sha256(), cookie_secret, (int)cookie_secret_length, (const uint8 *)input, input_length, digest, &digest_length)) { OPENSSL_cleanse(input, sizeof(input)); OPENSSL_cleanse(digest, sizeof(digest)); return FALSE; } OPENSSL_cleanse(input, sizeof(input)); size_t encoded_length = Auth_Crypto_Base64url_Encode( digest, sizeof(digest), csrf_out, csrf_capacity); OPENSSL_cleanse(digest, sizeof(digest)); return encoded_length == AUTH_CRYPTO_TOKEN_SIZE - 1; } boolean Auth_HTTP_Verify_CSRF( Seobeo_Request_Entry *p_request, const uint8 *cookie_secret, size_t cookie_secret_length, const char *binding) { if (!Auth_HTTP_Same_Origin(p_request) || !binding) return FALSE; const char *provided = Auth_HTTP_Request_Value(p_request, "X-CSRF-Token"); return Auth_HTTP_Verify_CSRF_Token( cookie_secret, cookie_secret_length, binding, provided); } boolean Auth_HTTP_Verify_CSRF_Token( const uint8 *cookie_secret, size_t cookie_secret_length, const char *binding, const char *provided_token) { if (!binding || !provided_token || provided_token[0] == '\0') return FALSE; char expected[AUTH_CRYPTO_TOKEN_SIZE] = {0}; char provided_digest[AUTH_CRYPTO_TOKEN_DIGEST_SIZE] = {0}; char expected_digest[AUTH_CRYPTO_TOKEN_DIGEST_SIZE] = {0}; boolean valid = FALSE; if (Auth_HTTP_Derive_CSRF( cookie_secret, cookie_secret_length, binding, expected, sizeof(expected)) && Auth_Crypto_Token_Digest( provided_token, provided_digest, sizeof(provided_digest)) == AUTH_CRYPTO_OK && Auth_Crypto_Token_Digest( expected, expected_digest, sizeof(expected_digest)) == AUTH_CRYPTO_OK) { valid = CRYPTO_memcmp( provided_digest, expected_digest, sizeof(provided_digest)) == 0; } OPENSSL_cleanse(expected, sizeof(expected)); OPENSSL_cleanse(provided_digest, sizeof(provided_digest)); OPENSSL_cleanse(expected_digest, sizeof(expected_digest)); return valid; } Auth_HTTP_Resolve_Result Auth_HTTP_Resolve_Authenticated_User( Seobeo_Request_Entry *p_request, Auth_Store *p_store, const uint8 *cookie_secret, size_t cookie_secret_length, int64 current_unix, int64 session_idle_ttl_secs, Auth_HTTP_Authenticated_User *p_user) { if (!p_user) return AUTH_HTTP_RESOLVE_ERROR; memset(p_user, 0, sizeof(*p_user)); if (!p_request || !p_store || !cookie_secret || cookie_secret_length < AUTH_CRYPTO_COOKIE_SECRET_MIN_BYTES || cookie_secret_length > AUTH_CRYPTO_COOKIE_SECRET_MAX_BYTES || current_unix < 0 || session_idle_ttl_secs <= 0) return AUTH_HTTP_RESOLVE_ERROR; char session_token[AUTH_HTTP_COOKIE_VALUE_MAX] = {0}; const char *cookie_header = Auth_HTTP_Request_Value(p_request, "Cookie"); if (!cookie_header || !Auth_HTTP_Parse_Cookie( cookie_header, AUTH_HTTP_SESSION_COOKIE_NAME, session_token, sizeof(session_token)) || session_token[0] == '\0') { OPENSSL_cleanse(session_token, sizeof(session_token)); return AUTH_HTTP_RESOLVE_NOT_FOUND; } if (Auth_Crypto_Token_Digest( session_token, p_user->token_digest, sizeof(p_user->token_digest)) != AUTH_CRYPTO_OK) { OPENSSL_cleanse(session_token, sizeof(session_token)); OPENSSL_cleanse(p_user, sizeof(*p_user)); return AUTH_HTTP_RESOLVE_NOT_FOUND; } OPENSSL_cleanse(session_token, sizeof(session_token)); Auth_Store_Result result = Auth_Store_Find_Session( p_store, p_user->token_digest, current_unix, &p_user->session, &p_user->user); if (result != AUTH_STORE_OK) { OPENSSL_cleanse(p_user, sizeof(*p_user)); return result == AUTH_STORE_ERROR ? AUTH_HTTP_RESOLVE_ERROR : AUTH_HTTP_RESOLVE_NOT_FOUND; } Auth_Store_Touch_Session( p_store, p_user->token_digest, current_unix, session_idle_ttl_secs); if (!Auth_HTTP_Derive_CSRF( cookie_secret, cookie_secret_length, p_user->token_digest, p_user->csrf_token, sizeof(p_user->csrf_token))) { OPENSSL_cleanse(p_user, sizeof(*p_user)); return AUTH_HTTP_RESOLVE_ERROR; } return AUTH_HTTP_RESOLVE_OK; }