/src/zeek/src/analyzer/protocol/rpc/Portmap.cc
Line | Count | Source |
1 | | // See the file "COPYING" in the main distribution directory for copyright. |
2 | | |
3 | | #include "zeek/analyzer/protocol/rpc/Portmap.h" |
4 | | |
5 | | #include "zeek/analyzer/protocol/rpc/XDR.h" |
6 | | #include "zeek/analyzer/protocol/rpc/events.bif.h" |
7 | | |
8 | | enum PortmapperProcs : uint8_t { |
9 | | PMAPPROC_NULL = 0, |
10 | | PMAPPROC_SET = 1, |
11 | | PMAPPROC_UNSET = 2, |
12 | | PMAPPROC_GETPORT = 3, |
13 | | PMAPPROC_DUMP = 4, |
14 | | PMAPPROC_CALLIT = 5, |
15 | | }; |
16 | | |
17 | | namespace zeek::analyzer::rpc { |
18 | | namespace detail { |
19 | | |
20 | 607k | bool PortmapperInterp::RPC_BuildCall(RPC_CallInfo* c, const u_char*& buf, int& n) { |
21 | 607k | if ( c->Program() != 100000 ) |
22 | 607k | Weird("bad_RPC_program"); |
23 | | |
24 | 607k | switch ( c->Proc() ) { |
25 | 61.3k | case PMAPPROC_NULL: break; |
26 | | |
27 | 12.2k | case PMAPPROC_SET: { |
28 | 12.2k | auto m = ExtractMapping(buf, n); |
29 | 12.2k | if ( ! m ) |
30 | 2.68k | return false; |
31 | 9.53k | c->AddVal(std::move(m)); |
32 | 9.53k | } break; |
33 | | |
34 | 25.3k | case PMAPPROC_UNSET: { |
35 | 25.3k | auto m = ExtractMapping(buf, n); |
36 | 25.3k | if ( ! m ) |
37 | 526 | return false; |
38 | 24.8k | c->AddVal(std::move(m)); |
39 | 24.8k | } break; |
40 | | |
41 | 2.65k | case PMAPPROC_GETPORT: { |
42 | 2.65k | auto pr = ExtractPortRequest(buf, n); |
43 | 2.65k | if ( ! pr ) |
44 | 382 | return false; |
45 | 2.27k | c->AddVal(std::move(pr)); |
46 | 2.27k | } break; |
47 | | |
48 | 278k | case PMAPPROC_DUMP: break; |
49 | | |
50 | 141k | case PMAPPROC_CALLIT: { |
51 | 141k | auto call_it = ExtractCallItRequest(buf, n); |
52 | 141k | if ( ! call_it ) |
53 | 9.46k | return false; |
54 | 132k | c->AddVal(std::move(call_it)); |
55 | 132k | } break; |
56 | | |
57 | 86.6k | default: return false; |
58 | 607k | } |
59 | | |
60 | 508k | return true; |
61 | 607k | } |
62 | | |
63 | | bool PortmapperInterp::RPC_BuildReply(RPC_CallInfo* c, BifEnum::rpc_status status, const u_char*& buf, int& n, |
64 | 373k | double start_time, double last_time, int reply_len) { |
65 | 373k | EventHandlerPtr event; |
66 | 373k | ValPtr reply; |
67 | 373k | int success = (status == BifEnum::RPC_SUCCESS); |
68 | | |
69 | 373k | switch ( c->Proc() ) { |
70 | 59.6k | case PMAPPROC_NULL: event = success ? pm_request_null : pm_attempt_null; break; |
71 | | |
72 | 8.92k | case PMAPPROC_SET: |
73 | 8.92k | if ( success ) { |
74 | 1.46k | uint32_t proc_status = extract_XDR_uint32(buf, n); |
75 | 1.46k | if ( ! buf ) |
76 | 66 | return false; |
77 | | |
78 | 1.39k | reply = val_mgr->Bool(proc_status); |
79 | 1.39k | event = pm_request_set; |
80 | 1.39k | } |
81 | 7.46k | else |
82 | 7.46k | event = pm_attempt_set; |
83 | | |
84 | 8.86k | break; |
85 | | |
86 | 24.1k | case PMAPPROC_UNSET: |
87 | 24.1k | if ( success ) { |
88 | 22.4k | uint32_t proc_status = extract_XDR_uint32(buf, n); |
89 | 22.4k | if ( ! buf ) |
90 | 198 | return false; |
91 | | |
92 | 22.2k | reply = val_mgr->Bool(proc_status); |
93 | 22.2k | event = pm_request_unset; |
94 | 22.2k | } |
95 | 1.70k | else |
96 | 1.70k | event = pm_attempt_unset; |
97 | | |
98 | 23.9k | break; |
99 | | |
100 | 23.9k | case PMAPPROC_GETPORT: |
101 | 1.82k | if ( success ) { |
102 | 1.42k | uint32_t port = extract_XDR_uint32(buf, n); |
103 | 1.42k | if ( ! buf ) |
104 | 270 | return false; |
105 | | |
106 | 1.15k | RecordVal* rv = c->RequestVal()->AsRecordVal(); |
107 | 1.15k | auto is_tcp = rv->GetFieldAs<BoolVal>(2); |
108 | 1.15k | reply = val_mgr->Port(CheckPort(port), is_tcp ? TRANSPORT_TCP : TRANSPORT_UDP); |
109 | 1.15k | event = pm_request_getport; |
110 | 1.15k | } |
111 | 399 | else |
112 | 399 | event = pm_attempt_getport; |
113 | 1.55k | break; |
114 | | |
115 | 266k | case PMAPPROC_DUMP: |
116 | 266k | event = success ? pm_request_dump : pm_attempt_dump; |
117 | 266k | if ( success ) { |
118 | 264k | static auto pm_mappings = id::find_type<TableType>("pm_mappings"); |
119 | 264k | auto mappings = make_intrusive<TableVal>(pm_mappings); |
120 | 264k | uint32_t nmap = 0; |
121 | | |
122 | | // Each call in the loop test pulls the next "opted" |
123 | | // element to see if there are more mappings. |
124 | 343k | while ( extract_XDR_uint32(buf, n) && buf ) { |
125 | 265k | auto m = ExtractMapping(buf, n); |
126 | 265k | if ( ! m ) |
127 | 186k | break; |
128 | | |
129 | 79.0k | auto index = val_mgr->Count(++nmap); |
130 | 79.0k | mappings->Assign(std::move(index), std::move(m)); |
131 | 79.0k | } |
132 | | |
133 | 264k | if ( ! buf ) |
134 | 187k | return false; |
135 | | |
136 | 76.8k | reply = std::move(mappings); |
137 | 76.8k | event = pm_request_dump; |
138 | 76.8k | } |
139 | 2.51k | else |
140 | 2.51k | event = pm_attempt_dump; |
141 | 79.4k | break; |
142 | | |
143 | 79.4k | case PMAPPROC_CALLIT: |
144 | 12.4k | if ( success ) { |
145 | 10.1k | uint32_t port = extract_XDR_uint32(buf, n); |
146 | 10.1k | int reply_n; |
147 | 10.1k | const u_char* opaque_reply = extract_XDR_opaque(buf, n, reply_n); |
148 | 10.1k | if ( ! opaque_reply ) |
149 | 2.64k | return false; |
150 | | |
151 | 7.46k | reply = val_mgr->Port(CheckPort(port), TRANSPORT_UDP); |
152 | 7.46k | event = pm_request_callit; |
153 | 7.46k | } |
154 | 2.33k | else |
155 | 2.33k | event = pm_attempt_callit; |
156 | 9.79k | break; |
157 | | |
158 | 9.79k | default: return false; |
159 | 373k | } |
160 | | |
161 | 183k | Event(event, c->TakeRequestVal(), status, std::move(reply)); |
162 | 183k | return true; |
163 | 373k | } |
164 | | |
165 | 303k | RecordValPtr PortmapperInterp::ExtractMapping(const u_char*& buf, int& len) { |
166 | 303k | static auto pm_mapping = id::find_type<RecordType>("pm_mapping"); |
167 | 303k | auto mapping = make_intrusive<RecordVal>(pm_mapping); |
168 | | |
169 | 303k | mapping->Assign(0, extract_XDR_uint32(buf, len)); |
170 | 303k | mapping->Assign(1, extract_XDR_uint32(buf, len)); |
171 | | |
172 | 303k | bool is_tcp = extract_XDR_uint32(buf, len) == IPPROTO_TCP; |
173 | 303k | uint32_t port = extract_XDR_uint32(buf, len); |
174 | 303k | mapping->Assign(2, val_mgr->Port(CheckPort(port), is_tcp ? TRANSPORT_TCP : TRANSPORT_UDP)); |
175 | | |
176 | 303k | if ( ! buf ) |
177 | 190k | return nullptr; |
178 | | |
179 | 113k | return mapping; |
180 | 303k | } |
181 | | |
182 | 2.65k | RecordValPtr PortmapperInterp::ExtractPortRequest(const u_char*& buf, int& len) { |
183 | 2.65k | static auto pm_port_request = id::find_type<RecordType>("pm_port_request"); |
184 | 2.65k | auto pr = make_intrusive<RecordVal>(pm_port_request); |
185 | | |
186 | 2.65k | pr->Assign(0, extract_XDR_uint32(buf, len)); |
187 | 2.65k | pr->Assign(1, extract_XDR_uint32(buf, len)); |
188 | | |
189 | 2.65k | bool is_tcp = extract_XDR_uint32(buf, len) == IPPROTO_TCP; |
190 | 2.65k | pr->Assign(2, is_tcp); |
191 | 2.65k | (void)extract_XDR_uint32(buf, len); // consume the bogus port |
192 | | |
193 | 2.65k | if ( ! buf ) |
194 | 382 | return nullptr; |
195 | | |
196 | 2.27k | return pr; |
197 | 2.65k | } |
198 | | |
199 | 141k | RecordValPtr PortmapperInterp::ExtractCallItRequest(const u_char*& buf, int& len) { |
200 | 141k | static auto pm_callit_request = id::find_type<RecordType>("pm_callit_request"); |
201 | 141k | auto c = make_intrusive<RecordVal>(pm_callit_request); |
202 | | |
203 | 141k | c->Assign(0, extract_XDR_uint32(buf, len)); |
204 | 141k | c->Assign(1, extract_XDR_uint32(buf, len)); |
205 | 141k | c->Assign(2, extract_XDR_uint32(buf, len)); |
206 | | |
207 | 141k | int arg_n; |
208 | 141k | (void)extract_XDR_opaque(buf, len, arg_n); |
209 | 141k | c->Assign(3, arg_n); |
210 | | |
211 | 141k | if ( ! buf ) |
212 | 9.46k | return nullptr; |
213 | | |
214 | 132k | return c; |
215 | 141k | } |
216 | | |
217 | 312k | uint32_t PortmapperInterp::CheckPort(uint32_t port) { |
218 | 312k | if ( port >= 65536 ) { |
219 | 69.7k | if ( pm_bad_port ) { |
220 | 69.7k | analyzer->EnqueueConnEvent(pm_bad_port, analyzer->ConnVal(), val_mgr->Count(port)); |
221 | 69.7k | } |
222 | | |
223 | 69.7k | port = 0; |
224 | 69.7k | } |
225 | | |
226 | 312k | return port; |
227 | 312k | } |
228 | | |
229 | 183k | void PortmapperInterp::Event(EventHandlerPtr f, ValPtr request, BifEnum::rpc_status status, ValPtr reply) { |
230 | 183k | if ( ! f ) |
231 | 0 | return; |
232 | | |
233 | 183k | Args vl; |
234 | | |
235 | 183k | vl.emplace_back(analyzer->ConnVal()); |
236 | | |
237 | 183k | if ( status == BifEnum::RPC_SUCCESS ) { |
238 | 109k | if ( request ) |
239 | 32.2k | vl.emplace_back(std::move(request)); |
240 | 109k | if ( reply ) |
241 | 109k | vl.emplace_back(std::move(reply)); |
242 | 109k | } |
243 | 73.7k | else { |
244 | 73.7k | vl.emplace_back(BifType::Enum::rpc_status->GetEnumVal(status)); |
245 | | |
246 | 73.7k | if ( request ) |
247 | 11.9k | vl.emplace_back(std::move(request)); |
248 | 73.7k | } |
249 | | |
250 | 183k | analyzer->EnqueueConnEvent(f, std::move(vl)); |
251 | 183k | } |
252 | | |
253 | | } // namespace detail |
254 | | |
255 | | Portmapper_Analyzer::Portmapper_Analyzer(Connection* conn) |
256 | 3.90k | : RPC_Analyzer("PORTMAPPER", conn, new detail::PortmapperInterp(this)) { |
257 | 3.90k | orig_rpc = resp_rpc = nullptr; |
258 | 3.90k | } |
259 | | |
260 | 3.90k | void Portmapper_Analyzer::Init() { |
261 | 3.90k | RPC_Analyzer::Init(); |
262 | | |
263 | 3.90k | if ( Conn()->ConnTransport() == TRANSPORT_TCP ) { |
264 | 3.90k | orig_rpc = new Contents_RPC(Conn(), true, interp); |
265 | 3.90k | resp_rpc = new Contents_RPC(Conn(), false, interp); |
266 | 3.90k | AddSupportAnalyzer(orig_rpc); |
267 | 3.90k | AddSupportAnalyzer(resp_rpc); |
268 | 3.90k | } |
269 | 3.90k | } |
270 | | |
271 | | } // namespace zeek::analyzer::rpc |