Mercurial
diff mrjunejune/auth_api.c @ 279:b3b547563ec7
Add Google connector service and agent wiki
Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code.
Co-authored-by: Copilot <[email protected]>
Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 17 Aug 2026 22:22:36 -0700 |
| parents | 04fee26ecce0 |
| children |
line wrap: on
line diff
--- a/mrjunejune/auth_api.c Mon Aug 17 22:16:14 2026 -0700 +++ b/mrjunejune/auth_api.c Mon Aug 17 22:22:36 2026 -0700 @@ -2,6 +2,7 @@ #include "mrjunejune/template_renderer.h" #include "auth/auth_crypto.h" +#include "auth/auth_http.h" #include "auth/auth_store.h" #include "seobeo/seobeo.h" #include "dowa/dowa.h" @@ -25,7 +26,6 @@ #define BODY_MAX_BYTES 4096 #define COOKIE_VALUE_MAX 512 -#define CSRF_SUFFIX ":csrf:v1" #define RATE_TABLE_SIZE 512 /* must be power of 2 */ #define RATE_PROBE_LIMIT 16 #define RATE_LIMIT_MAX_FAILURES 5 @@ -33,7 +33,6 @@ #define SESSION_COOKIE_MAX 512 #define GUEST_COOKIE_MAX (AUTH_CRYPTO_GUEST_COOKIE_SIZE + 256) #define RATE_KEY_MAX 65 -#define BINDING_INPUT_MAX (AUTH_CRYPTO_TOKEN_DIGEST_SIZE + 16) /* * Fixed precomputed scrypt hash of the constant string "dummy-zenbu-timing". @@ -112,23 +111,6 @@ return TRUE; } -/* - * base64url encode src_len bytes from src into dst. - * dst must have capacity ceil(src_len * 4 / 3) + 1. - * Returns length of encoded string (without NUL). - * - * Delegates to Auth_Crypto_Base64url_Encode which uses the correct loop - * boundary (processes remaining bytes after full 3-byte groups). - */ -static size_t auth_base64url_encode( - const uint8 *src, - size_t src_len, - char *dst, - size_t dst_capacity) -{ - return Auth_Crypto_Base64url_Encode(src, src_len, dst, dst_capacity); -} - static void auth_hex_encode( const uint8 *src, size_t src_len, @@ -156,37 +138,9 @@ char *csrf_out, size_t csrf_capacity) { - if (!binding || !csrf_out || csrf_capacity < AUTH_CRYPTO_TOKEN_SIZE) - return FALSE; - - size_t binding_len = strlen(binding); - size_t suffix_len = strlen(CSRF_SUFFIX); - size_t input_len = binding_len + suffix_len; - - if (input_len >= BINDING_INPUT_MAX) - return FALSE; - - char input[BINDING_INPUT_MAX]; - memcpy(input, binding, binding_len); - memcpy(input + binding_len, CSRF_SUFFIX, suffix_len); - - uint8 digest[32]; - uint32 digest_len = 32; - if (!HMAC(EVP_sha256(), - g_cookie_secret, (int)g_cookie_secret_length, - (const uint8 *)input, input_len, - digest, &digest_len)) - { - OPENSSL_cleanse(input, sizeof(input)); - OPENSSL_cleanse(digest, sizeof(digest)); - return FALSE; - } - OPENSSL_cleanse(input, sizeof(input)); - - size_t encoded_length = - auth_base64url_encode(digest, 32, csrf_out, csrf_capacity); - OPENSSL_cleanse(digest, sizeof(digest)); - return encoded_length == AUTH_CRYPTO_TOKEN_SIZE - 1; + return Auth_HTTP_Derive_CSRF( + g_cookie_secret, g_cookie_secret_length, + binding, csrf_out, csrf_capacity); } /* @@ -396,35 +350,8 @@ char *value_out, size_t capacity) { - if (!cookie_header || !name || !value_out || capacity == 0) - return FALSE; - - size_t name_len = strlen(name); - const char *p = cookie_header; - - while (*p) - { - /* skip whitespace */ - while (*p == ' ' || *p == '\t') p++; - - /* check for name= */ - if (strncmp(p, name, name_len) == 0 && p[name_len] == '=') - { - p += name_len + 1; - const char *start = p; - while (*p && *p != ';') p++; - size_t vlen = (size_t)(p - start); - if (vlen >= capacity) return FALSE; - memcpy(value_out, start, vlen); - value_out[vlen] = '\0'; - return TRUE; - } - - /* skip to next ; */ - while (*p && *p != ';') p++; - if (*p == ';') p++; - } - return FALSE; + return Auth_HTTP_Parse_Cookie( + cookie_header, name, value_out, capacity); } /* @@ -462,17 +389,7 @@ static boolean auth_same_origin(Seobeo_Request_Entry *p_req) { - const char *host = auth_req_value(p_req, "Host"); - const char *origin = auth_req_value(p_req, "Origin"); - if (!host || !origin) return FALSE; - - const char *host_in_origin = strstr(origin, "://"); - if (!host_in_origin) return FALSE; - host_in_origin += 3; - - const char *end = strchr(host_in_origin, '/'); - size_t len = end ? (size_t)(end - host_in_origin) : strlen(host_in_origin); - return strlen(host) == len && strncmp(host, host_in_origin, len) == 0; + return Auth_HTTP_Same_Origin(p_req); } /* ------------------------------------------------------------------ */ @@ -588,46 +505,30 @@ int64 now = auth_now(); /* --- Try authenticated session first --- */ - char session_token[COOKIE_VALUE_MAX] = {0}; - if (cookie_header && - auth_parse_cookie(cookie_header, AUTH_API_SESSION_COOKIE_NAME, - session_token, sizeof(session_token)) && - session_token[0] != '\0') + Auth_HTTP_Authenticated_User authenticated_user; + Auth_HTTP_Resolve_Result user_result = + Auth_HTTP_Resolve_Authenticated_User( + p_request, g_auth_store, g_cookie_secret, g_cookie_secret_length, + now, g_session_idle_ttl, &authenticated_user); + if (user_result == AUTH_HTTP_RESOLVE_ERROR) + return FALSE; + if (user_result == AUTH_HTTP_RESOLVE_OK) { - char token_digest[AUTH_CRYPTO_TOKEN_DIGEST_SIZE]; - if (Auth_Crypto_Token_Digest(session_token, token_digest, - sizeof(token_digest)) == AUTH_CRYPTO_OK) - { - Auth_Session_Record session; - Auth_User_Record user; - Auth_Store_Result result = Auth_Store_Find_Session( - g_auth_store, token_digest, now, &session, &user); - - if (result == AUTH_STORE_OK) - { - Auth_Store_Touch_Session( - g_auth_store, token_digest, now, g_session_idle_ttl); - - p_principal->kind = AUTH_PRINCIPAL_USER; - strncpy(p_principal->user_id, user.id, sizeof(p_principal->user_id) - 1); - strncpy(p_principal->username, user.username, sizeof(p_principal->username) - 1); - strncpy(p_principal->role, user.role, sizeof(p_principal->role) - 1); - p_principal->must_change_password = user.must_change_password; - strncpy(p_principal->_binding, token_digest, sizeof(p_principal->_binding) - 1); - if (!auth_derive_csrf(token_digest, p_principal->csrf_token, - sizeof(p_principal->csrf_token))) - { - OPENSSL_cleanse(session_token, sizeof(session_token)); - OPENSSL_cleanse(token_digest, sizeof(token_digest)); - memset(p_principal, 0, sizeof(*p_principal)); - return FALSE; - } - OPENSSL_cleanse(session_token, sizeof(session_token)); - OPENSSL_cleanse(token_digest, sizeof(token_digest)); - return TRUE; - } - } - OPENSSL_cleanse(session_token, sizeof(session_token)); + p_principal->kind = AUTH_PRINCIPAL_USER; + strncpy(p_principal->user_id, authenticated_user.user.id, + sizeof(p_principal->user_id) - 1); + strncpy(p_principal->username, authenticated_user.user.username, + sizeof(p_principal->username) - 1); + strncpy(p_principal->role, authenticated_user.user.role, + sizeof(p_principal->role) - 1); + p_principal->must_change_password = + authenticated_user.user.must_change_password; + strncpy(p_principal->_binding, authenticated_user.token_digest, + sizeof(p_principal->_binding) - 1); + strncpy(p_principal->csrf_token, authenticated_user.csrf_token, + sizeof(p_principal->csrf_token) - 1); + OPENSSL_cleanse(&authenticated_user, sizeof(authenticated_user)); + return TRUE; } /* --- Try guest cookie --- */ @@ -761,47 +662,31 @@ int64 now = auth_now(); /* --- Try authenticated session first --- */ - char session_token[COOKIE_VALUE_MAX] = {0}; - if (cookie_header && - auth_parse_cookie(cookie_header, AUTH_API_SESSION_COOKIE_NAME, - session_token, sizeof(session_token)) && - session_token[0] != '\0') + Auth_HTTP_Authenticated_User authenticated_user; + Auth_HTTP_Resolve_Result user_result = + Auth_HTTP_Resolve_Authenticated_User( + p_request, g_auth_store, g_cookie_secret, g_cookie_secret_length, + now, g_session_idle_ttl, &authenticated_user); + if (user_result == AUTH_HTTP_RESOLVE_ERROR) + return FALSE; + if (user_result == AUTH_HTTP_RESOLVE_OK) { - char token_digest[AUTH_CRYPTO_TOKEN_DIGEST_SIZE]; - if (Auth_Crypto_Token_Digest(session_token, token_digest, - sizeof(token_digest)) == AUTH_CRYPTO_OK) - { - Auth_Session_Record session; - Auth_User_Record user; - Auth_Store_Result result = Auth_Store_Find_Session( - g_auth_store, token_digest, now, &session, &user); - - if (result == AUTH_STORE_OK) - { - Auth_Store_Touch_Session( - g_auth_store, token_digest, now, g_session_idle_ttl); - - p_principal->kind = AUTH_PRINCIPAL_USER; - strncpy(p_principal->user_id, user.id, sizeof(p_principal->user_id) - 1); - strncpy(p_principal->username, user.username, sizeof(p_principal->username) - 1); - strncpy(p_principal->role, user.role, sizeof(p_principal->role) - 1); - p_principal->must_change_password = user.must_change_password; - strncpy(p_principal->_binding, token_digest, sizeof(p_principal->_binding) - 1); - if (!auth_derive_csrf(token_digest, p_principal->csrf_token, - sizeof(p_principal->csrf_token))) - { - OPENSSL_cleanse(session_token, sizeof(session_token)); - OPENSSL_cleanse(token_digest, sizeof(token_digest)); - memset(p_principal, 0, sizeof(*p_principal)); - return FALSE; - } - OPENSSL_cleanse(session_token, sizeof(session_token)); - OPENSSL_cleanse(token_digest, sizeof(token_digest)); - *p_found = TRUE; - return TRUE; - } - } - OPENSSL_cleanse(session_token, sizeof(session_token)); + p_principal->kind = AUTH_PRINCIPAL_USER; + strncpy(p_principal->user_id, authenticated_user.user.id, + sizeof(p_principal->user_id) - 1); + strncpy(p_principal->username, authenticated_user.user.username, + sizeof(p_principal->username) - 1); + strncpy(p_principal->role, authenticated_user.user.role, + sizeof(p_principal->role) - 1); + p_principal->must_change_password = + authenticated_user.user.must_change_password; + strncpy(p_principal->_binding, authenticated_user.token_digest, + sizeof(p_principal->_binding) - 1); + strncpy(p_principal->csrf_token, authenticated_user.csrf_token, + sizeof(p_principal->csrf_token) - 1); + OPENSSL_cleanse(&authenticated_user, sizeof(authenticated_user)); + *p_found = TRUE; + return TRUE; } /* --- Try existing guest cookie (no new guest created) --- */ @@ -886,34 +771,8 @@ const char *provided_token, const char *binding) { - if (!provided_token || !binding || provided_token[0] == '\0') - return FALSE; - - char expected[AUTH_CRYPTO_TOKEN_SIZE]; - if (!auth_derive_csrf(binding, expected, sizeof(expected))) - return FALSE; - - /* Compare SHA-256 digests of both tokens (constant-time length comparison) */ - char digest_provided[AUTH_CRYPTO_TOKEN_DIGEST_SIZE]; - char digest_expected[AUTH_CRYPTO_TOKEN_DIGEST_SIZE]; - - if (Auth_Crypto_Token_Digest(provided_token, digest_provided, - sizeof(digest_provided)) != AUTH_CRYPTO_OK || - Auth_Crypto_Token_Digest(expected, digest_expected, - sizeof(digest_expected)) != AUTH_CRYPTO_OK) - { - OPENSSL_cleanse(expected, sizeof(expected)); - OPENSSL_cleanse(digest_provided, sizeof(digest_provided)); - OPENSSL_cleanse(digest_expected, sizeof(digest_expected)); - return FALSE; - } - - int match = CRYPTO_memcmp(digest_provided, digest_expected, - sizeof(digest_provided)); - OPENSSL_cleanse(expected, sizeof(expected)); - OPENSSL_cleanse(digest_provided, sizeof(digest_provided)); - OPENSSL_cleanse(digest_expected, sizeof(digest_expected)); - return match == 0; + return Auth_HTTP_Verify_CSRF_Token( + g_cookie_secret, g_cookie_secret_length, binding, provided_token); } /* ------------------------------------------------------------------ */ @@ -924,14 +783,11 @@ Seobeo_Request_Entry *p_request, const Auth_Principal *p_principal) { - if (!p_request || !p_principal) - return FALSE; - if (!auth_same_origin(p_request)) + if (!p_principal) return FALSE; - const char *csrf = auth_req_value(p_request, "X-CSRF-Token"); - if (!csrf || csrf[0] == '\0') - return FALSE; - return auth_verify_csrf(csrf, p_principal->_binding); + return Auth_HTTP_Verify_CSRF( + p_request, g_cookie_secret, g_cookie_secret_length, + p_principal->_binding); } /* ------------------------------------------------------------------ */