/src/ndpi/src/lib/protocols/kerberos.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * kerberos.c |
3 | | * |
4 | | * Copyright (C) 2011-25 - ntop.org |
5 | | * Copyright (C) 2009-11 - ipoque GmbH |
6 | | * |
7 | | * This file is part of nDPI, an open source deep packet inspection |
8 | | * library based on the OpenDPI and PACE technology by ipoque GmbH |
9 | | * |
10 | | * nDPI is free software: you can redistribute it and/or modify |
11 | | * it under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation, either version 3 of the License, or |
13 | | * (at your option) any later version. |
14 | | * |
15 | | * nDPI is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public License |
21 | | * along with nDPI. If not, see <http://www.gnu.org/licenses/>. |
22 | | * |
23 | | */ |
24 | | |
25 | | #include "ndpi_protocol_ids.h" |
26 | | |
27 | | #define NDPI_CURRENT_PROTO NDPI_PROTOCOL_KERBEROS |
28 | | |
29 | | #include "ndpi_api.h" |
30 | | #include "ndpi_private.h" |
31 | | |
32 | | /* #define KERBEROS_DEBUG 1 */ |
33 | | |
34 | 0 | #define KERBEROS_PORT 88 |
35 | | |
36 | | static int ndpi_search_kerberos_extra(struct ndpi_detection_module_struct *ndpi_struct, |
37 | | struct ndpi_flow_struct *flow); |
38 | | |
39 | | |
40 | | /* Reference: https://en.wikipedia.org/wiki/X.690#Length_octets */ |
41 | | static int krb_decode_asn1_length(struct ndpi_detection_module_struct *ndpi_struct, |
42 | | size_t * const kasn1_offset) |
43 | 0 | { |
44 | 0 | struct ndpi_packet_struct * const packet = &ndpi_struct->packet; |
45 | 0 | int64_t length; |
46 | 0 | u_int16_t value_len; |
47 | |
|
48 | 0 | length = asn1_ber_decode_length(&packet->payload[*kasn1_offset], |
49 | 0 | packet->payload_packet_len - *kasn1_offset, |
50 | 0 | &value_len); |
51 | |
|
52 | 0 | if (length == -1 || |
53 | 0 | packet->payload_packet_len < *kasn1_offset + value_len + length) |
54 | 0 | { |
55 | 0 | return -1; |
56 | 0 | } |
57 | | |
58 | 0 | *kasn1_offset += value_len; |
59 | |
|
60 | 0 | return (int)length; |
61 | 0 | } |
62 | | |
63 | | /* Reference: https://en.wikipedia.org/wiki/X.690#Identifier_octets */ |
64 | | static int krb_decode_asn1_sequence_type(struct ndpi_detection_module_struct *ndpi_struct, |
65 | | size_t * const kasn1_offset) |
66 | 0 | { |
67 | 0 | struct ndpi_packet_struct * const packet = &ndpi_struct->packet; |
68 | |
|
69 | 0 | if (packet->payload_packet_len <= *kasn1_offset + 1 /* length octet */ || |
70 | 0 | packet->payload[*kasn1_offset] != 0x30 /* Universal Constructed Tag Type: Sequence */) |
71 | 0 | { |
72 | 0 | return -1; |
73 | 0 | } |
74 | | |
75 | 0 | (*kasn1_offset)++; |
76 | |
|
77 | 0 | return krb_decode_asn1_length(ndpi_struct, kasn1_offset); |
78 | 0 | } |
79 | | |
80 | | /* Reference: https://en.wikipedia.org/wiki/X.690#Identifier_octets */ |
81 | | static int krb_decode_asn1_string_type(struct ndpi_detection_module_struct *ndpi_struct, |
82 | | size_t * const kasn1_offset, |
83 | | char const ** const out) |
84 | 0 | { |
85 | 0 | struct ndpi_packet_struct * const packet = &ndpi_struct->packet; |
86 | 0 | int length; |
87 | |
|
88 | 0 | if (packet->payload_packet_len <= *kasn1_offset + 1 /* length octet */ || |
89 | 0 | (packet->payload[*kasn1_offset] != 0xA3 /* Context-specific Constructed Tag Type: Bit String */ && |
90 | 0 | packet->payload[*kasn1_offset] != 0xA4 /* Context-specific Constructed Tag Type: Octect String */ && |
91 | 0 | packet->payload[*kasn1_offset] != 0x30 /* Sequence Of */)) |
92 | 0 | { |
93 | 0 | return -1; |
94 | 0 | } |
95 | | |
96 | 0 | (*kasn1_offset)++; |
97 | |
|
98 | 0 | length = krb_decode_asn1_length(ndpi_struct, kasn1_offset); |
99 | 0 | if (length <= 0) |
100 | 0 | { |
101 | 0 | return -1; |
102 | 0 | } |
103 | | |
104 | 0 | if (out != NULL) |
105 | 0 | { |
106 | 0 | *out = (char *)&packet->payload[*kasn1_offset]; |
107 | 0 | } |
108 | |
|
109 | 0 | return length; |
110 | 0 | } |
111 | | |
112 | | /* Reference: https://en.wikipedia.org/wiki/X.690#Identifier_octets */ |
113 | | static int krb_decode_asn1_int_type(struct ndpi_detection_module_struct *ndpi_struct, |
114 | | size_t * const kasn1_offset, |
115 | | int * const out) |
116 | 0 | { |
117 | 0 | struct ndpi_packet_struct * const packet = &ndpi_struct->packet; |
118 | 0 | int length; |
119 | |
|
120 | 0 | if (packet->payload_packet_len <= *kasn1_offset + 1 /* length octet */ || |
121 | 0 | packet->payload[*kasn1_offset] != 0x02) |
122 | 0 | { |
123 | 0 | return -1; |
124 | 0 | } |
125 | | |
126 | 0 | (*kasn1_offset)++; |
127 | |
|
128 | 0 | length = krb_decode_asn1_length(ndpi_struct, kasn1_offset); |
129 | 0 | if (length <= 0 || length > 4) |
130 | 0 | { |
131 | 0 | return -1; |
132 | 0 | } |
133 | | |
134 | 0 | if (out != NULL) |
135 | 0 | { |
136 | 0 | int i = 0; |
137 | 0 | *out = 0; |
138 | 0 | for (; i < length; ++i) |
139 | 0 | { |
140 | 0 | *out |= (unsigned int)packet->payload[*kasn1_offset + i] << (length - i - 1) * 8; |
141 | 0 | } |
142 | 0 | *kasn1_offset += i; |
143 | 0 | } |
144 | |
|
145 | 0 | return length; |
146 | 0 | } |
147 | | |
148 | | /* Tags in which we are not interested. */ |
149 | | static int krb_decode_asn1_blocks_skip(struct ndpi_detection_module_struct *ndpi_struct, |
150 | | size_t * const kasn1_offset) |
151 | 0 | { |
152 | 0 | struct ndpi_packet_struct * const packet = &ndpi_struct->packet; |
153 | 0 | int length; |
154 | |
|
155 | 0 | if (packet->payload_packet_len <= *kasn1_offset + 1 /* length octet */ || |
156 | 0 | (packet->payload[*kasn1_offset] != 0xA0 /* Constructed Context-specific NULL */ && |
157 | 0 | packet->payload[*kasn1_offset] != 0xA1 /* Constructed Context-specific BOOLEAN */ && |
158 | 0 | packet->payload[*kasn1_offset] != 0xA2 /* Constructed Context-specific INTEGER */)) |
159 | 0 | { |
160 | 0 | return -1; |
161 | 0 | } |
162 | | |
163 | 0 | (*kasn1_offset)++; |
164 | |
|
165 | 0 | length = krb_decode_asn1_length(ndpi_struct, kasn1_offset); |
166 | 0 | if (length < 0) |
167 | 0 | { |
168 | 0 | return -1; |
169 | 0 | } |
170 | | |
171 | 0 | return length; |
172 | 0 | } |
173 | | |
174 | | static void krb_strncpy_lower(char * const dst, size_t dst_siz, |
175 | | char const * const src, size_t src_siz) |
176 | 0 | { |
177 | 0 | int i, dst_len = (int)ndpi_min(src_siz, dst_siz - 1); |
178 | |
|
179 | 0 | dst[dst_len] = '\0'; |
180 | |
|
181 | 0 | for(i = 0; i < dst_len; ++i) |
182 | 0 | { |
183 | 0 | if (ndpi_isprint(src[i]) == 0) |
184 | 0 | { |
185 | 0 | dst[i] = '?'; |
186 | 0 | } else { |
187 | 0 | dst[i] = tolower(src[i]); |
188 | 0 | } |
189 | 0 | } |
190 | 0 | } |
191 | | |
192 | | /* Reference: https://datatracker.ietf.org/doc/html/rfc4120 */ |
193 | | static int krb_parse(struct ndpi_detection_module_struct * const ndpi_struct, |
194 | | struct ndpi_flow_struct * const flow, |
195 | | size_t payload_offset) |
196 | 0 | { |
197 | 0 | size_t kasn1_offset = payload_offset; |
198 | 0 | int length, krb_version, msg_type; |
199 | 0 | char const * text; |
200 | |
|
201 | 0 | length = krb_decode_asn1_sequence_type(ndpi_struct, &kasn1_offset); |
202 | 0 | if (length < 0) |
203 | 0 | { |
204 | 0 | return -1; |
205 | 0 | } |
206 | | |
207 | 0 | length = krb_decode_asn1_blocks_skip(ndpi_struct, &kasn1_offset); |
208 | 0 | if (length < 0) |
209 | 0 | { |
210 | 0 | return -1; |
211 | 0 | } |
212 | | |
213 | 0 | length = krb_decode_asn1_int_type(ndpi_struct, &kasn1_offset, &krb_version); /* pvno */ |
214 | 0 | if (length != 1 || krb_version != 5) |
215 | 0 | { |
216 | 0 | return -1; |
217 | 0 | } |
218 | | |
219 | 0 | length = krb_decode_asn1_blocks_skip(ndpi_struct, &kasn1_offset); |
220 | 0 | if (length < 0) |
221 | 0 | { |
222 | 0 | return -1; |
223 | 0 | } |
224 | | |
225 | 0 | length = krb_decode_asn1_int_type(ndpi_struct, &kasn1_offset, &msg_type); /* msg-type */ |
226 | 0 | if (length != 1 || msg_type != 0x0d /* TGS-REP */) |
227 | 0 | { |
228 | 0 | return -1; |
229 | 0 | } |
230 | | |
231 | 0 | krb_decode_asn1_blocks_skip(ndpi_struct, &kasn1_offset); |
232 | |
|
233 | 0 | length = krb_decode_asn1_sequence_type(ndpi_struct, &kasn1_offset); /* Optional PADATA */ |
234 | 0 | if (length > 0) |
235 | 0 | { |
236 | 0 | kasn1_offset += length; |
237 | 0 | } |
238 | |
|
239 | 0 | length = krb_decode_asn1_string_type(ndpi_struct, &kasn1_offset, &text); |
240 | 0 | if (length < 3) |
241 | 0 | { |
242 | 0 | return -1; |
243 | 0 | } |
244 | | |
245 | 0 | kasn1_offset += length; |
246 | 0 | text += 2; |
247 | 0 | length -= 2; |
248 | 0 | if (flow->protos.kerberos.domain[0] == '\0') |
249 | 0 | { |
250 | 0 | krb_strncpy_lower(flow->protos.kerberos.domain, sizeof(flow->protos.kerberos.domain), |
251 | 0 | text, length); |
252 | 0 | } |
253 | |
|
254 | 0 | length = krb_decode_asn1_string_type(ndpi_struct, &kasn1_offset, NULL); |
255 | 0 | if (length < 0) |
256 | 0 | { |
257 | 0 | return -1; |
258 | 0 | } |
259 | | |
260 | 0 | length = krb_decode_asn1_sequence_type(ndpi_struct, &kasn1_offset); |
261 | 0 | if (length < 0) |
262 | 0 | { |
263 | 0 | return -1; |
264 | 0 | } |
265 | | |
266 | 0 | length = krb_decode_asn1_blocks_skip(ndpi_struct, &kasn1_offset); |
267 | 0 | if (length < 0) |
268 | 0 | { |
269 | 0 | return -1; |
270 | 0 | } |
271 | 0 | kasn1_offset += length; |
272 | |
|
273 | 0 | length = krb_decode_asn1_blocks_skip(ndpi_struct, &kasn1_offset); |
274 | 0 | if (length < 0) |
275 | 0 | { |
276 | 0 | return -1; |
277 | 0 | } |
278 | | |
279 | 0 | length = krb_decode_asn1_string_type(ndpi_struct, &kasn1_offset, &text); |
280 | 0 | if (length < 3) |
281 | 0 | { |
282 | 0 | return -1; |
283 | 0 | } |
284 | | |
285 | 0 | kasn1_offset += length; |
286 | 0 | text += 2; |
287 | 0 | length -= 2; |
288 | 0 | if (flow->protos.kerberos.hostname[0] == '\0' && text[length - 1] != '$') |
289 | 0 | { |
290 | 0 | krb_strncpy_lower(flow->protos.kerberos.hostname, sizeof(flow->protos.kerberos.hostname), |
291 | 0 | text, length); |
292 | 0 | } else if (flow->protos.kerberos.username[0] == '\0') { |
293 | 0 | krb_strncpy_lower(flow->protos.kerberos.username, sizeof(flow->protos.kerberos.username), |
294 | 0 | text, length - 1); |
295 | 0 | } |
296 | |
|
297 | 0 | return 0; |
298 | 0 | } |
299 | | |
300 | | static void ndpi_int_kerberos_add_connection(struct ndpi_detection_module_struct *ndpi_struct, |
301 | 0 | struct ndpi_flow_struct *flow) { |
302 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_KERBEROS, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI); |
303 | 0 | NDPI_LOG_DBG(ndpi_struct, "trace KERBEROS\n"); |
304 | 0 | } |
305 | | |
306 | | /* ************************************************* */ |
307 | | |
308 | | static void ndpi_search_kerberos(struct ndpi_detection_module_struct *ndpi_struct, |
309 | 0 | struct ndpi_flow_struct *flow) { |
310 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
311 | 0 | u_int16_t sport = packet->tcp ? ntohs(packet->tcp->source) : ntohs(packet->udp->source); |
312 | 0 | u_int16_t dport = packet->tcp ? ntohs(packet->tcp->dest) : ntohs(packet->udp->dest); |
313 | 0 | const u_int8_t *original_packet_payload = NULL; |
314 | 0 | u_int16_t original_payload_packet_len = 0; |
315 | |
|
316 | 0 | if((sport != KERBEROS_PORT) && (dport != KERBEROS_PORT)) { |
317 | 0 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
318 | 0 | return; |
319 | 0 | } |
320 | | |
321 | 0 | NDPI_LOG_DBG(ndpi_struct, "search KERBEROS\n"); |
322 | |
|
323 | | #ifdef KERBEROS_DEBUG |
324 | | printf("\n[Kerberos] Process packet [len: %u]\n", packet->payload_packet_len); |
325 | | #endif |
326 | | |
327 | 0 | if(flow->kerberos_buf.pktbuf != NULL) { |
328 | 0 | u_int missing = flow->kerberos_buf.pktbuf_maxlen - flow->kerberos_buf.pktbuf_currlen; |
329 | |
|
330 | 0 | if(packet->payload_packet_len <= missing) { |
331 | 0 | memcpy(&flow->kerberos_buf.pktbuf[flow->kerberos_buf.pktbuf_currlen], packet->payload, packet->payload_packet_len); |
332 | 0 | flow->kerberos_buf.pktbuf_currlen += packet->payload_packet_len; |
333 | |
|
334 | 0 | if(flow->kerberos_buf.pktbuf_currlen == flow->kerberos_buf.pktbuf_maxlen) { |
335 | 0 | original_packet_payload = packet->payload; |
336 | 0 | original_payload_packet_len = packet->payload_packet_len; |
337 | 0 | packet->payload = (u_int8_t *)flow->kerberos_buf.pktbuf; |
338 | 0 | packet->payload_packet_len = flow->kerberos_buf.pktbuf_currlen; |
339 | | #ifdef KERBEROS_DEBUG |
340 | | printf("[Kerberos] Packet is now full: processing\n"); |
341 | | #endif |
342 | 0 | } else { |
343 | | #ifdef KERBEROS_DEBUG |
344 | | printf("[Kerberos] Missing %u bytes: skipping\n", |
345 | | flow->kerberos_buf.pktbuf_maxlen - flow->kerberos_buf.pktbuf_currlen); |
346 | | #endif |
347 | |
|
348 | 0 | return; |
349 | 0 | } |
350 | 0 | } |
351 | 0 | } |
352 | | |
353 | | /* I have observed 0a,0c,0d,0e at packet->payload[19/21], maybe there are other possibilities */ |
354 | 0 | if(packet->payload_packet_len >= 4) { |
355 | 0 | u_int32_t kerberos_len, expected_len; |
356 | 0 | u_int16_t base_offset = 0; |
357 | |
|
358 | 0 | if(packet->tcp) { |
359 | 0 | kerberos_len = ntohl(get_u_int32_t(packet->payload, 0)), |
360 | 0 | expected_len = packet->payload_packet_len - 4; |
361 | 0 | base_offset = 4; |
362 | 0 | } else |
363 | 0 | base_offset = 0, kerberos_len = expected_len = packet->payload_packet_len; |
364 | |
|
365 | | #ifdef KERBEROS_DEBUG |
366 | | printf("[Kerberos] [Kerberos len: %u][expected_len: %u]\n", kerberos_len, expected_len); |
367 | | #endif |
368 | |
|
369 | 0 | if(kerberos_len < 12000) { |
370 | | /* |
371 | | Kerberos packets might be too long for a TCP packet |
372 | | so it could be split across two packets. Instead of |
373 | | rebuilding the stream we use a heuristic approach |
374 | | */ |
375 | 0 | if(kerberos_len > expected_len) { |
376 | 0 | if(packet->tcp) { |
377 | 0 | if(flow->kerberos_buf.pktbuf == NULL) { |
378 | 0 | flow->kerberos_buf.pktbuf = (char*)ndpi_malloc(kerberos_len+4); |
379 | |
|
380 | 0 | if(flow->kerberos_buf.pktbuf != NULL) { |
381 | 0 | flow->kerberos_buf.pktbuf_maxlen = kerberos_len+4; |
382 | | #ifdef KERBEROS_DEBUG |
383 | | printf("[Kerberos] Allocated %u bytes\n", flow->kerberos_buf.pktbuf_maxlen); |
384 | | #endif |
385 | 0 | } |
386 | 0 | } |
387 | | |
388 | 0 | if(flow->kerberos_buf.pktbuf != NULL) { |
389 | 0 | if(packet->payload_packet_len <= flow->kerberos_buf.pktbuf_maxlen) { |
390 | 0 | memcpy(flow->kerberos_buf.pktbuf, packet->payload, packet->payload_packet_len); |
391 | 0 | flow->kerberos_buf.pktbuf_currlen = packet->payload_packet_len; |
392 | 0 | } |
393 | 0 | } |
394 | 0 | } |
395 | | |
396 | 0 | return; |
397 | 0 | } else if(kerberos_len == expected_len) { |
398 | 0 | if(packet->payload_packet_len > 64) { |
399 | 0 | u_int16_t koffset, i; |
400 | |
|
401 | 0 | for(i=8; i<16; i++) |
402 | 0 | if((packet->payload[base_offset+i] == 0x03) |
403 | 0 | && (packet->payload[base_offset+i+1] == 0x02) |
404 | 0 | && (packet->payload[base_offset+i+2] == 0x01) |
405 | 0 | && (packet->payload[base_offset+i+3] != 0x05) |
406 | 0 | ) |
407 | 0 | break; |
408 | |
|
409 | 0 | koffset = base_offset + i + 3; |
410 | |
|
411 | | #ifdef KERBEROS_DEBUG |
412 | | printf("[Kerberos] [msg-type: 0x%02X/%u][koffset: %u]\n", |
413 | | packet->payload[koffset], packet->payload[koffset], koffset); |
414 | | #endif |
415 | |
|
416 | 0 | if(((packet->payload[koffset] == 0x0A) |
417 | 0 | || (packet->payload[koffset] == 0x0C) |
418 | 0 | || (packet->payload[koffset] == 0x1E) |
419 | 0 | || (packet->payload[koffset] == 0x0D) |
420 | 0 | || (packet->payload[koffset] == 0x0E))) { |
421 | 0 | u_int32_t koffsetp, body_offset = 0, pad_len; |
422 | 0 | u_int8_t msg_type = packet->payload[koffset]; |
423 | |
|
424 | | #ifdef KERBEROS_DEBUG |
425 | | printf("[Kerberos] Packet found 0x%02X/%u\n", msg_type, msg_type); |
426 | | #endif |
427 | |
|
428 | 0 | ndpi_int_kerberos_add_connection(ndpi_struct, flow); |
429 | |
|
430 | 0 | if(msg_type != 0x0d) /* TGS-REP */ { |
431 | | /* Process only on requests */ |
432 | 0 | if(packet->payload[koffset+1] == 0xA3) { |
433 | 0 | if(packet->payload[koffset+3] == 0x30) |
434 | 0 | pad_len = packet->payload[koffset+4]; |
435 | 0 | else { |
436 | | /* Long pad */ |
437 | 0 | pad_len = packet->payload[koffset+2]; |
438 | 0 | for(i=3; i<10; i++) if(packet->payload[koffset+i] == pad_len) break; |
439 | |
|
440 | 0 | pad_len = (packet->payload[koffset+i+1] << 8) + packet->payload[koffset+i+2]; |
441 | 0 | koffset += i-2; |
442 | 0 | } |
443 | 0 | } else |
444 | 0 | pad_len = 0; |
445 | |
|
446 | | #ifdef KERBEROS_DEBUG |
447 | | printf("pad_len=0x%02X/%u\n", pad_len, pad_len); |
448 | | #endif |
449 | |
|
450 | 0 | if(pad_len > 0) { |
451 | 0 | koffsetp = koffset + 2; |
452 | 0 | for(i=0; i<4; i++) if(packet->payload[koffsetp] != 0x30) koffsetp++; /* ASN.1 */ |
453 | | #ifdef KERBEROS_DEBUG |
454 | | printf("koffsetp=%u [%02X %02X] [byte 0 must be 0x30]\n", koffsetp, packet->payload[koffsetp], packet->payload[koffsetp+1]); |
455 | | #endif |
456 | 0 | } else |
457 | 0 | koffsetp = koffset; |
458 | |
|
459 | 0 | body_offset = koffsetp + 1 + pad_len; |
460 | |
|
461 | 0 | for(i=0; i<10; i++) if(body_offset<packet->payload_packet_len && packet->payload[body_offset] != 0x05) body_offset++; /* ASN.1 */ |
462 | | #ifdef KERBEROS_DEBUG |
463 | | printf("body_offset=%u [%02X %02X] [byte 0 must be 0x05]\n", body_offset, packet->payload[body_offset], packet->payload[body_offset+1]); |
464 | | #endif |
465 | 0 | } |
466 | | |
467 | 0 | if(msg_type == 0x0A) /* AS-REQ */ { |
468 | | #ifdef KERBEROS_DEBUG |
469 | | printf("[Kerberos] Processing AS-REQ\n"); |
470 | | #endif |
471 | | |
472 | |
|
473 | 0 | if(body_offset < packet->payload_packet_len) { |
474 | 0 | u_int16_t name_offset = body_offset + 13; |
475 | | |
476 | 0 | for(i=0; (i<20) && (name_offset < packet->payload_packet_len); i++) { |
477 | 0 | if(packet->payload[name_offset] != 0x1b) |
478 | 0 | name_offset++; /* ASN.1 */ |
479 | 0 | } |
480 | | |
481 | | #ifdef KERBEROS_DEBUG |
482 | | printf("name_offset=%u [%02X %02X] [byte 0 must be 0x1b]\n", name_offset, packet->payload[name_offset], packet->payload[name_offset+1]); |
483 | | #endif |
484 | |
|
485 | 0 | if(name_offset < packet->payload_packet_len - 1) { |
486 | 0 | u_int cname_len = 0; |
487 | |
|
488 | 0 | name_offset += 1; |
489 | 0 | if(name_offset < packet->payload_packet_len - 1 && |
490 | 0 | ndpi_isprint(packet->payload[name_offset+1]) == 0) /* Isn't printable ? */ |
491 | 0 | { |
492 | 0 | name_offset++; |
493 | 0 | } |
494 | |
|
495 | 0 | if(name_offset < packet->payload_packet_len - 3 && |
496 | 0 | packet->payload[name_offset+1] == 0x1b) |
497 | 0 | { |
498 | 0 | name_offset += 2; |
499 | 0 | } |
500 | | |
501 | 0 | cname_len = packet->payload[name_offset]; |
502 | |
|
503 | 0 | if((cname_len+name_offset) < packet->payload_packet_len) { |
504 | 0 | u_int realm_len, realm_offset; |
505 | 0 | char cname_str[48]; |
506 | 0 | u_int8_t num_cname = 0; |
507 | |
|
508 | 0 | cname_str[0] = '\0'; // required, because cname_len |
509 | |
|
510 | 0 | while(++num_cname <= 2) { |
511 | 0 | if (name_offset + cname_len + 1 >= packet->payload_packet_len) |
512 | 0 | cname_len = 0; |
513 | 0 | krb_strncpy_lower(cname_str, sizeof(cname_str), (char*)&packet->payload[name_offset+1], cname_len); |
514 | |
|
515 | | #ifdef KERBEROS_DEBUG |
516 | | printf("[AS-REQ][s/dport: %u/%u][Kerberos Cname][len: %u][%s]\n", sport, dport, cname_len, cname_str); |
517 | | #endif |
518 | |
|
519 | 0 | if(((strcmp(cname_str, "host") == 0) || (strcmp(cname_str, "ldap") == 0)) && (packet->payload[name_offset+1+cname_len] == 0x1b) && num_cname == 1) { |
520 | 0 | name_offset += cname_len + 2; |
521 | 0 | if (name_offset < packet->payload_packet_len) |
522 | 0 | cname_len = packet->payload[name_offset]; |
523 | 0 | } else{ |
524 | 0 | break; |
525 | 0 | } |
526 | 0 | } |
527 | |
|
528 | 0 | realm_offset = cname_len + name_offset + 3; |
529 | | |
530 | | /* if cname does not end with a $ then it's a username */ |
531 | 0 | if(cname_len > 0 && name_offset + cname_len + 1 < packet->payload_packet_len |
532 | 0 | && (cname_len < sizeof(cname_str)) |
533 | 0 | && (cname_str[cname_len-1] == '$')) { |
534 | 0 | cname_str[cname_len-1] = '\0'; |
535 | 0 | ndpi_snprintf(flow->protos.kerberos.hostname, sizeof(flow->protos.kerberos.hostname), "%s", cname_str); |
536 | 0 | } else |
537 | 0 | ndpi_snprintf(flow->protos.kerberos.username, sizeof(flow->protos.kerberos.username), "%s", cname_str); |
538 | |
|
539 | 0 | for(i=0; (i < 14) && (realm_offset < packet->payload_packet_len); i++) { |
540 | 0 | if(packet->payload[realm_offset] != 0x1b) |
541 | 0 | realm_offset++; /* ASN.1 */ |
542 | 0 | } |
543 | | |
544 | | #ifdef KERBEROS_DEBUG |
545 | | printf("realm_offset=%u [%02X %02X] [byte 0 must be 0x1b]\n", realm_offset, |
546 | | packet->payload[realm_offset], packet->payload[realm_offset+1]); |
547 | | #endif |
548 | | |
549 | 0 | realm_offset += 1; |
550 | | //if(num_cname == 2) realm_offset++; |
551 | 0 | if(realm_offset < packet->payload_packet_len) { |
552 | 0 | realm_len = packet->payload[realm_offset]; |
553 | |
|
554 | 0 | if((realm_offset+realm_len) < packet->payload_packet_len) { |
555 | 0 | char realm_str[48]; |
556 | |
|
557 | 0 | realm_offset += 1; |
558 | 0 | krb_strncpy_lower(realm_str, sizeof(realm_str), (char*)&packet->payload[realm_offset], realm_len); |
559 | |
|
560 | | #ifdef KERBEROS_DEBUG |
561 | | printf("[AS-REQ][Kerberos Realm][len: %u][%s]\n", realm_len, realm_str); |
562 | | #endif |
563 | 0 | ndpi_snprintf(flow->protos.kerberos.domain, sizeof(flow->protos.kerberos.domain), "%s", realm_str); |
564 | 0 | } |
565 | 0 | } |
566 | 0 | } |
567 | 0 | } |
568 | 0 | } |
569 | | #ifdef KERBEROS_DEBUG |
570 | | printf("[Kerberos] Setting extra func from AS-REQ\n"); |
571 | | #endif |
572 | 0 | flow->max_extra_packets_to_check = 5; /* Reply may be split into multiple segments */ |
573 | 0 | flow->extra_packets_func = ndpi_search_kerberos_extra; |
574 | 0 | } else if(msg_type == 0x0e) /* AS-REQ */ { |
575 | | #ifdef KERBEROS_DEBUG |
576 | | printf("[Kerberos] Processing AS-REQ\n"); |
577 | | #endif |
578 | | /* Nothing specific to do; stop dissecting this flow */ |
579 | 0 | flow->extra_packets_func = NULL; |
580 | |
|
581 | 0 | } else if(msg_type == 0x0c) /* TGS-REQ */ { |
582 | | #ifdef KERBEROS_DEBUG |
583 | | printf("[Kerberos] Processing TGS-REQ\n"); |
584 | | #endif |
585 | |
|
586 | 0 | if(body_offset < packet->payload_packet_len) { |
587 | 0 | u_int16_t name_offset, padding_offset = body_offset + 4; |
588 | |
|
589 | 0 | name_offset = padding_offset; |
590 | 0 | for(i=0; i<14 && name_offset < packet->payload_packet_len; i++) if(packet->payload[name_offset] != 0x1b) name_offset++; /* ASN.1 */ |
591 | |
|
592 | | #ifdef KERBEROS_DEBUG |
593 | | printf("name_offset=%u [%02X %02X] [byte 0 must be 0x1b]\n", name_offset, packet->payload[name_offset], packet->payload[name_offset+1]); |
594 | | #endif |
595 | |
|
596 | 0 | if(name_offset < (packet->payload_packet_len - 1)) { |
597 | 0 | u_int realm_len; |
598 | |
|
599 | 0 | name_offset++; |
600 | 0 | realm_len = packet->payload[name_offset]; |
601 | |
|
602 | 0 | if((realm_len+name_offset) < packet->payload_packet_len) { |
603 | 0 | char realm_str[48]; |
604 | |
|
605 | 0 | name_offset += 1; |
606 | 0 | krb_strncpy_lower(realm_str, sizeof(realm_str), (char*)&packet->payload[name_offset], realm_len); |
607 | |
|
608 | | #ifdef KERBEROS_DEBUG |
609 | | printf("[TGS-REQ][s/dport: %u/%u][Kerberos Realm][len: %u][%s]\n", sport, dport, realm_len, realm_str); |
610 | | #endif |
611 | 0 | ndpi_snprintf(flow->protos.kerberos.domain, sizeof(flow->protos.kerberos.domain), "%s", realm_str); |
612 | | |
613 | | /* If necessary we can decode sname */ |
614 | 0 | if(flow->kerberos_buf.pktbuf) { |
615 | 0 | ndpi_free(flow->kerberos_buf.pktbuf); |
616 | 0 | packet->payload = original_packet_payload; |
617 | 0 | packet->payload_packet_len = original_payload_packet_len; |
618 | 0 | } |
619 | 0 | flow->kerberos_buf.pktbuf = NULL; |
620 | 0 | } |
621 | 0 | } |
622 | 0 | } |
623 | | #ifdef KERBEROS_DEBUG |
624 | | printf("[Kerberos] Setting extra func from TGS-REQ\n"); |
625 | | #endif |
626 | 0 | if(!packet->udp) { |
627 | 0 | flow->max_extra_packets_to_check = 5; /* Reply may be split into multiple segments */ |
628 | 0 | flow->extra_packets_func = ndpi_search_kerberos_extra; |
629 | 0 | } |
630 | |
|
631 | 0 | if(flow->kerberos_buf.pktbuf != NULL) { |
632 | 0 | ndpi_free(flow->kerberos_buf.pktbuf); |
633 | 0 | packet->payload = original_packet_payload; |
634 | 0 | packet->payload_packet_len = original_payload_packet_len; |
635 | 0 | flow->kerberos_buf.pktbuf = NULL; |
636 | 0 | } |
637 | |
|
638 | 0 | return; |
639 | 0 | } else if(msg_type == 0x0d) /* TGS-REP */ { |
640 | 0 | NDPI_LOG_DBG(ndpi_struct, "[Kerberos] Processing TGS-REP\n"); |
641 | |
|
642 | 0 | if (krb_parse(ndpi_struct, flow, 8) != 0) |
643 | 0 | { |
644 | 0 | NDPI_LOG_DBG(ndpi_struct, "[TGS-REP] Invalid packet received\n"); |
645 | 0 | return; |
646 | 0 | } |
647 | 0 | NDPI_LOG_DBG(ndpi_struct, |
648 | 0 | "[TGS-REP][s/dport: %u/%u][Kerberos Hostname,Domain,Username][%s,%s,%s]\n", |
649 | 0 | sport, dport, flow->protos.kerberos.hostname, flow->protos.kerberos.domain, |
650 | 0 | flow->protos.kerberos.username); |
651 | 0 | flow->extra_packets_func = NULL; |
652 | 0 | } else if(msg_type == 0x1e) /* Error */ { |
653 | | #ifdef KERBEROS_DEBUG |
654 | | printf("[Kerberos] Processing KRB-Error\n"); |
655 | | #endif |
656 | | /* Nothing specific to do; stop dissecting this flow */ |
657 | 0 | flow->extra_packets_func = NULL; |
658 | 0 | } |
659 | | |
660 | 0 | return; |
661 | 0 | } |
662 | 0 | } |
663 | 0 | } |
664 | 0 | } else { |
665 | | #ifdef KERBEROS_DEBUG |
666 | | printf("[Kerberos][s/dport: %u/%u] Skipping packet: too long [kerberos_len: %u]\n", |
667 | | sport, dport, kerberos_len); |
668 | | #endif |
669 | |
|
670 | 0 | if(flow->protos.kerberos.domain[0] != '\0') |
671 | 0 | return; |
672 | 0 | } |
673 | 0 | } |
674 | | |
675 | 0 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
676 | 0 | } |
677 | | |
678 | | static int ndpi_search_kerberos_extra(struct ndpi_detection_module_struct *ndpi_struct, |
679 | | struct ndpi_flow_struct *flow) |
680 | 0 | { |
681 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
682 | |
|
683 | | #ifdef KERBEROS_DEBUG |
684 | | printf("[Kerberos] Extra function\n"); |
685 | | #endif |
686 | | |
687 | | /* Unfortunately, generic "extra function" code doesn't honour protocol bitmask */ |
688 | | /* TODO: handle that in ndpi_main.c for all the protocols */ |
689 | 0 | if(packet->payload_packet_len == 0 || |
690 | 0 | packet->tcp_retransmission) |
691 | 0 | return 1; |
692 | | |
693 | | /* Possibly dissect the reply */ |
694 | 0 | ndpi_search_kerberos(ndpi_struct, flow); |
695 | | |
696 | | /* Possibly more processing */ |
697 | 0 | return flow->extra_packets_func != NULL; |
698 | 0 | } |
699 | | |
700 | 1 | void init_kerberos_dissector(struct ndpi_detection_module_struct *ndpi_struct) { |
701 | 1 | register_dissector("Kerberos", ndpi_struct, |
702 | 1 | ndpi_search_kerberos, |
703 | 1 | NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION, |
704 | 1 | 1, NDPI_PROTOCOL_KERBEROS); |
705 | 1 | } |