/src/mod_auth_openidc/test/fuzz/fuzz_url.c
Line | Count | Source |
1 | | /* |
2 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
3 | | * contributor license agreements. Licensed under the Apache License, |
4 | | * Version 2.0 (the "License"); you may not use this file except in |
5 | | * compliance with the License. You may obtain a copy of the License at |
6 | | * |
7 | | * http://www.apache.org/licenses/LICENSE-2.0 |
8 | | * |
9 | | * Copyright (C) 2017-2026 ZmartZone Holding BV - hans.zandbelt@openidc.com |
10 | | * |
11 | | * Fuzz target for oidc_validate_redirect_url(): the open-redirect guard that |
12 | | * vets attacker-supplied return-to / post_logout_redirect / target_link_uri |
13 | | * values before the module redirects to them. Seed corpus: |
14 | | * test/open-redirect-payload-list.txt (replayed line by line). |
15 | | * |
16 | | * Every input is validated three times, against the three configurations the |
17 | | * guard distinguishes: same-host scope (the logout/return-to case), any-host |
18 | | * scope (the target_link_uri case once it has passed the configured-URL |
19 | | * match), and same-host scope with an OIDCRedirectURLsAllowed list -- which |
20 | | * switches the guard from hostname comparison to regex matching, a branch the |
21 | | * fixture's plain configuration never takes. The allow-list copy of the |
22 | | * fixture config is made once at startup; the fixture itself is not changed. |
23 | | */ |
24 | | |
25 | | #include "cfg/cfg_int.h" /* oidc_cfg_t members, for the allow-list */ |
26 | | #include "fuzz.h" |
27 | | #include "mod_auth_openidc.h" /* oidc_validate_redirect_url */ |
28 | | #include "util.h" /* test fixture */ |
29 | | |
30 | | #include <apr_hash.h> |
31 | | #include <apr_pools.h> |
32 | | #include <apr_strings.h> |
33 | | |
34 | | static int g_ready = 0; |
35 | | static oidc_cfg_t *g_cfg_allowed = NULL; |
36 | | |
37 | | /* engine-called one-time init, pre-forkserver on AFL++: see fuzz.h */ |
38 | 34 | int LLVMFuzzerInitialize(int *argc, char ***argv) { |
39 | 34 | (void)argc; |
40 | 34 | (void)argv; |
41 | 34 | if (!g_ready) { |
42 | 34 | oidc_test_setup(); |
43 | | /* a second server config carrying an allow-list: one anchored host |
44 | | * pattern and one deliberately loose one, so both the match and the |
45 | | * no-match legs of the regex loop see traffic. Allocated by the library |
46 | | * rather than declared here by value: sizeof(oidc_cfg_t) depends on the |
47 | | * USE_* feature macros, so a by-value instance in this TU is only as big |
48 | | * as the harness build flags make it, not as big as the library expects. */ |
49 | 34 | apr_pool_t *pool = oidc_test_pool_get(); |
50 | 34 | g_cfg_allowed = oidc_cfg_server_create(pool, oidc_test_request_get()->server); |
51 | 34 | g_cfg_allowed->redirect_urls_allowed = apr_hash_make(pool); |
52 | 34 | const char *anchored = "^https://www\\.example\\.com/"; |
53 | 34 | const char *loose = "example\\.org/.*callback"; |
54 | 34 | apr_hash_set(g_cfg_allowed->redirect_urls_allowed, anchored, APR_HASH_KEY_STRING, anchored); |
55 | 34 | apr_hash_set(g_cfg_allowed->redirect_urls_allowed, loose, APR_HASH_KEY_STRING, loose); |
56 | 34 | g_ready = 1; |
57 | 34 | } |
58 | 34 | return 0; |
59 | 34 | } |
60 | | |
61 | 6.63k | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
62 | 6.63k | if (!g_ready) |
63 | 0 | LLVMFuzzerInitialize(NULL, NULL); |
64 | | |
65 | 6.63k | apr_pool_t *pool = NULL; |
66 | 6.63k | apr_pool_create(&pool, oidc_test_pool_get()); |
67 | | |
68 | | /* shallow-copy the fixture request and give it a per-input pool so all |
69 | | * allocations made while validating are reclaimed each iteration */ |
70 | 6.63k | request_rec r = *oidc_test_request_get(); |
71 | 6.63k | r.pool = pool; |
72 | 6.63k | oidc_cfg_t *cfg = oidc_test_cfg_get(); |
73 | | |
74 | 6.63k | char *url = apr_pstrmemdup(pool, (const char *)data, size); |
75 | 6.63k | char *err_str = NULL; |
76 | 6.63k | char *err_desc = NULL; |
77 | 6.63k | oidc_validate_redirect_url(&r, cfg, url, OIDC_REDIRECT_URL_SAME_HOST, &err_str, &err_desc); |
78 | 6.63k | oidc_validate_redirect_url(&r, cfg, url, OIDC_REDIRECT_URL_ANY_HOST, &err_str, &err_desc); |
79 | 6.63k | oidc_validate_redirect_url(&r, g_cfg_allowed, url, OIDC_REDIRECT_URL_SAME_HOST, &err_str, &err_desc); |
80 | | |
81 | 6.63k | apr_pool_destroy(pool); |
82 | 6.63k | return 0; |
83 | 6.63k | } |