/src/kea-fuzzer/fuzz_hook_user_chk4.cc
Line | Count | Source |
1 | | // Copyright (C) 2025 Ada Logcis Ltd. |
2 | | // |
3 | | // This Source Code Form is subject to the terms of the Mozilla Public |
4 | | // License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | // file, You can obtain one at http://mozilla.org/MPL/2.0/. |
6 | | //////////////////////////////////////////////////////////////////////////////// |
7 | | #include <config.h> |
8 | | #include <fuzzer/FuzzedDataProvider.h> |
9 | | |
10 | | #include <dhcp/dhcp4.h> |
11 | | #include <dhcp/pkt4.h> |
12 | | #include <dhcp/libdhcp++.h> |
13 | | #include <dhcp/user_chk/user.h> |
14 | | #include <dhcp/user_chk/user_data_source.h> |
15 | | #include <dhcp/user_chk/user_file.h> |
16 | | #include <dhcp/user_chk/user_registry.h> |
17 | | #include <dhcp4/ctrl_dhcp4_srv.h> |
18 | | #include <dhcpsrv/callout_handle_store.h> |
19 | | #include <dhcpsrv/cfgmgr.h> |
20 | | #include <dhcpsrv/lease_mgr_factory.h> |
21 | | #include <log/logger_support.h> |
22 | | #include <util/filesystem.h> |
23 | | |
24 | | #include <cstddef> |
25 | | #include <cstdint> |
26 | | #include <vector> |
27 | | #include <list> |
28 | | #include <memory> |
29 | | #include <iostream> |
30 | | #include <filesystem> |
31 | | #include <fstream> |
32 | | #include <string> |
33 | | #include <cstdio> |
34 | | |
35 | | #include "helper_func.h" |
36 | | |
37 | | using namespace isc::dhcp; |
38 | | using namespace isc::hooks; |
39 | | using namespace isc::util; |
40 | | using namespace user_chk; |
41 | | |
42 | | extern "C" UserRegistryPtr user_registry; |
43 | | extern "C" int pkt4_receive(CalloutHandle& handle); |
44 | | extern "C" int subnet4_select(CalloutHandle& handle); |
45 | | |
46 | | namespace isc { |
47 | | namespace dhcp { |
48 | | class MyDhcpv4Srv : public ControlledDhcpv4Srv { |
49 | | public: |
50 | 6.36k | void fuzz_classifyPacket(const Pkt4Ptr& pkt) { |
51 | 6.36k | classifyPacket(pkt); |
52 | 6.36k | } |
53 | | }; |
54 | | } |
55 | | } |
56 | | |
57 | 3.12k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
58 | 3.12k | if (size < 236) { |
59 | | // package size requires at least 236 bytes |
60 | 15 | return 0; |
61 | 15 | } |
62 | | |
63 | 3.10k | FuzzedDataProvider fdp(data, size); |
64 | | |
65 | | // Disable validatePath checking to allow writing configuration file to /tmp |
66 | 3.10k | isc::util::file::PathChecker::enableEnforcement(false); |
67 | | |
68 | | // Initialise logging |
69 | 3.10k | setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0); |
70 | 3.10k | setenv("KEA_LOCKFILE_DIR", "/tmp", 0); |
71 | 3.10k | setenv("KEA_PIDFILE_DIR", "/tmp", 0); |
72 | 3.10k | setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0); |
73 | 3.10k | try { |
74 | 3.10k | isc::log::initLogger("fuzzer"); |
75 | 3.10k | isc::process::Daemon::loggerInit("fuzzer", false); |
76 | 3.10k | isc::process::Daemon::setDefaultLoggerName("fuzzer"); |
77 | 3.10k | } catch (...) { |
78 | | // Early exit if logging initialisation failed |
79 | 0 | return 0; |
80 | 0 | } |
81 | | |
82 | | // Creating temp config file |
83 | 3.10k | std::string path = fuzz::writeTempConfig(true); |
84 | 3.10k | if (path.empty()) { |
85 | | // Early exit if configuration file creation failed |
86 | 0 | fuzz::deleteTempFile(path); |
87 | 0 | return 0; |
88 | 0 | } |
89 | | |
90 | | // Creating temp lease file |
91 | 3.10k | std::string lease_path = fuzz::writeTempLease(true); |
92 | | |
93 | | // Creating temp user file |
94 | 3.10k | std::string user_path = fuzz::writeTempUserFile(); |
95 | | |
96 | | // Creating user registry |
97 | 3.10k | try { |
98 | 3.10k | user_registry.reset(new UserRegistry()); |
99 | 3.10k | UserDataSourcePtr user_file(new UserFile(user_path)); |
100 | 3.10k | user_registry->setSource(user_file); |
101 | 3.10k | user_registry->refresh(); |
102 | 3.10k | } catch (std::exception& e) { |
103 | | // Early exit if user registry failed to create. |
104 | 0 | return 0; |
105 | 0 | } |
106 | | |
107 | 3.10k | try { |
108 | 9.81k | for (int i = 0; i < fdp.ConsumeIntegralInRange<int>(1, 5); i++) { |
109 | 6.70k | if (fdp.ConsumeBool()) { |
110 | 2.43k | std::vector<uint8_t> mac = fdp.ConsumeBytes<uint8_t>(6); |
111 | 2.43k | UserPtr user = UserPtr(new User(UserId::HW_ADDRESS, mac)); |
112 | 2.43k | user_registry->addUser(user); |
113 | 4.27k | } else { |
114 | 4.27k | const size_t len = fdp.ConsumeIntegralInRange<size_t>(2, 64); |
115 | 4.27k | std::vector<uint8_t> duid = fdp.ConsumeBytes<uint8_t>(len); |
116 | 4.27k | UserPtr user = UserPtr(new User(UserId::DUID, duid)); |
117 | 4.27k | user_registry->addUser(user); |
118 | 4.27k | } |
119 | 6.70k | } |
120 | 3.10k | } catch (...) { |
121 | | // Slient exceptions |
122 | 184 | } |
123 | | |
124 | 3.10k | Pkt4Ptr pkt; |
125 | 3.10k | std::unique_ptr<MyDhcpv4Srv> srv; |
126 | | |
127 | | // Package parsing |
128 | 3.10k | try { |
129 | | // Add fixed magic cookie and correct hardware address |
130 | 3.10k | std::vector<uint8_t> buf(data, data + size); |
131 | 3.10k | if (size >= 240) { |
132 | | // Max hardware address length is 20 |
133 | 3.00k | buf[2] = 20; |
134 | | |
135 | | // Magic cookie fixed value 0x63825363 |
136 | 3.00k | buf[236] = 0x63; |
137 | 3.00k | buf[237] = 0x82; |
138 | 3.00k | buf[238] = 0x53; |
139 | 3.00k | buf[239] = 0x63; |
140 | 3.00k | } |
141 | | |
142 | 3.10k | pkt = Pkt4Ptr(new Pkt4(buf.data(), buf.size())); |
143 | 3.10k | pkt->unpack(); |
144 | 3.10k | } catch (...) { |
145 | | // Early exit if package parsing failed. |
146 | 1.81k | return 0; |
147 | 1.81k | } |
148 | | |
149 | | // Configure random value in packet |
150 | 1.29k | uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 8); |
151 | 1.29k | pkt->setType(static_cast<DHCPMessageType>(typeChoice)); |
152 | | |
153 | | // Server initialisation |
154 | 1.29k | try { |
155 | 1.29k | srv.reset(new MyDhcpv4Srv()); |
156 | 1.29k | srv->init(path); |
157 | 1.29k | } catch (...) { |
158 | | // Early exit if server initialisation failed. |
159 | 23 | return 0; |
160 | 23 | } |
161 | | |
162 | 1.27k | if (!srv) { |
163 | | // Early exit if server initialisation failed. |
164 | 0 | return 0; |
165 | 0 | } |
166 | | |
167 | 1.27k | CalloutHandlePtr handle = nullptr; |
168 | | |
169 | | // Fuzz pkt4_receive |
170 | 1.27k | try { |
171 | 1.27k | handle = getCalloutHandle(pkt); |
172 | 1.27k | handle->setArgument("query4", pkt); |
173 | 1.27k | pkt4_receive(*handle); |
174 | 1.27k | } catch (const isc::Exception& e) { |
175 | | // Slient exceptions |
176 | 0 | } catch (const boost::exception& e) { |
177 | | // Slient exceptions |
178 | 0 | } |
179 | | |
180 | | // Clean handle to avoid mem leak |
181 | 1.27k | if (handle) { |
182 | 1.27k | handle->deleteAllArguments(); |
183 | 1.27k | } |
184 | | |
185 | | // Call classifyPacket for packet checking |
186 | 1.27k | try { |
187 | 1.27k | srv->fuzz_classifyPacket(pkt); |
188 | 1.27k | } catch (const isc::Exception& e) { |
189 | | // Slient exceptions |
190 | 0 | } catch (const boost::exception& e) { |
191 | | // Slient exceptions |
192 | 0 | } |
193 | | |
194 | | // Prepare client context |
195 | 1.27k | AllocEngine::ClientContext4Ptr ctx(new AllocEngine::ClientContext4()); |
196 | | |
197 | | // Call earlyGHRLookup |
198 | 1.27k | try { |
199 | 1.27k | srv->earlyGHRLookup(pkt, ctx); |
200 | 1.27k | } catch (const isc::Exception& e) { |
201 | | // Slient exceptions |
202 | 0 | } catch (const boost::exception& e) { |
203 | | // Slient exceptions |
204 | 0 | } |
205 | | |
206 | | // Fuzz subnet4_select |
207 | 1.27k | try { |
208 | 1.27k | handle = getCalloutHandle(pkt); |
209 | 1.27k | Pkt4Ptr rsp; |
210 | 1.27k | CfgMgr& cfgmgr = CfgMgr::instance(); |
211 | 1.27k | handle = getCalloutHandle(pkt); |
212 | 1.27k | handle->setArgument("query4", pkt); |
213 | 1.27k | handle->setArgument("subnet4collection", |
214 | 1.27k | cfgmgr.getCurrentCfg()->getCfgSubnets4()->getAll()); |
215 | 1.27k | if (!ctx) { |
216 | 0 | ctx.reset(new AllocEngine::ClientContext4()); |
217 | 0 | } |
218 | 1.27k | if (ctx) { |
219 | 1.27k | handle->setArgument("subnet4", ctx->subnet_); |
220 | 1.27k | } |
221 | 1.27k | subnet4_select(*handle); |
222 | 1.27k | } catch (const isc::Exception& e) { |
223 | | // Slient exceptions |
224 | 0 | } catch (const boost::exception& e) { |
225 | | // Slient exceptions |
226 | 0 | } |
227 | | |
228 | | // Clean handle to avoid mem leak |
229 | 1.27k | if (handle) { |
230 | 1.27k | handle->deleteAllArguments(); |
231 | 1.27k | } |
232 | | |
233 | 1.27k | srv.reset(); |
234 | | |
235 | | // Remove temp files |
236 | 1.27k | fuzz::deleteTempFile(path); |
237 | 1.27k | fuzz::deleteTempFile(lease_path); |
238 | 1.27k | fuzz::deleteTempFile(user_path); |
239 | 1.27k | return 0; |
240 | 1.27k | } |