/src/wireshark/epan/dissectors/packet-kpasswd.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* packet-kpasswd.c |
2 | | * Routines for kpasswd packet dissection |
3 | | * Ronnie Sahlberg 2003 |
4 | | * |
5 | | * See RFC 3244 |
6 | | * |
7 | | * Wireshark - Network traffic analyzer |
8 | | * By Gerald Combs <gerald@wireshark.org> |
9 | | * Copyright 1998 Gerald Combs |
10 | | * |
11 | | * SPDX-License-Identifier: GPL-2.0-or-later |
12 | | */ |
13 | | |
14 | | #include "config.h" |
15 | | |
16 | | #include <epan/packet.h> |
17 | | #include <epan/asn1.h> |
18 | | #include "packet-tcp.h" |
19 | | #include "packet-kerberos.h" |
20 | | #include <epan/prefs.h> |
21 | | |
22 | | void proto_register_kpasswd(void); |
23 | | void proto_reg_handoff_kpasswd(void); |
24 | | |
25 | | static dissector_handle_t kpasswd_handle_udp; |
26 | | static dissector_handle_t kpasswd_handle_tcp; |
27 | | |
28 | | /* Desegment Kerberos over TCP messages */ |
29 | | static bool kpasswd_desegment = true; |
30 | | |
31 | | static int proto_kpasswd; |
32 | | static int hf_kpasswd_message_len; |
33 | | static int hf_kpasswd_version; |
34 | | static int hf_kpasswd_result; |
35 | | static int hf_kpasswd_result_string; |
36 | | static int hf_kpasswd_ap_req_len; |
37 | | static int hf_kpasswd_ap_req_data; |
38 | | static int hf_kpasswd_krb_priv_message; |
39 | | static int hf_kpasswd_ChangePasswdData; |
40 | | |
41 | | static int ett_kpasswd; |
42 | | static int ett_ap_req_data; |
43 | | static int ett_krb_priv_message; |
44 | | |
45 | | |
46 | 14 | #define UDP_PORT_KPASSWD 464 |
47 | 14 | #define TCP_PORT_KPASSWD 464 |
48 | | |
49 | | |
50 | | static const value_string vers_vals[] = { |
51 | | { 0x0001, "Reply" }, |
52 | | { 0xff80, "Request" }, |
53 | | { 0, NULL }, |
54 | | }; |
55 | | |
56 | | |
57 | | /** Dissects AP-REQ or AP-REP part of password change. */ |
58 | | static void |
59 | | dissect_kpasswd_ap_req_data(packet_info *pinfo _U_, tvbuff_t *tvb, proto_tree *parent_tree) |
60 | 19 | { |
61 | 19 | proto_item *it; |
62 | 19 | proto_tree *tree; |
63 | | |
64 | 19 | it=proto_tree_add_item(parent_tree, hf_kpasswd_ap_req_data, tvb, 0, -1, ENC_NA); |
65 | 19 | tree=proto_item_add_subtree(it, ett_ap_req_data); |
66 | 19 | dissect_kerberos_main(tvb, pinfo, tree, false, NULL); |
67 | 19 | } |
68 | | |
69 | | static int |
70 | | dissect_kpasswd_user_data_request(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tree) |
71 | 0 | { |
72 | 0 | int offset=0; |
73 | 0 | asn1_ctx_t asn1_ctx; |
74 | 0 | asn1_ctx_init(&asn1_ctx, ASN1_ENC_BER, true, pinfo); |
75 | |
|
76 | 0 | offset=dissect_kerberos_ChangePasswdData (false, tvb, offset, &asn1_ctx, tree, hf_kpasswd_ChangePasswdData); |
77 | 0 | return offset; |
78 | 0 | } |
79 | | |
80 | | static kerberos_callbacks cb_req[] = { |
81 | | { KRB_CBTAG_PRIV_USER_DATA, dissect_kpasswd_user_data_request }, |
82 | | { 0, NULL } |
83 | | }; |
84 | | |
85 | | #define KRB5_KPASSWD_SUCCESS 0 |
86 | | #define KRB5_KPASSWD_MALFORMED 1 |
87 | | #define KRB5_KPASSWD_HARDERROR 2 |
88 | | #define KRB5_KPASSWD_AUTHERROR 3 |
89 | | #define KRB5_KPASSWD_SOFTERROR 4 |
90 | | #define KRB5_KPASSWD_ACCESSDENIED 5 |
91 | | #define KRB5_KPASSWD_BAD_VERSION 6 |
92 | | #define KRB5_KPASSWD_INITIAL_FLAG_NEEDED 7 |
93 | | static const value_string kpasswd_result_types[] = { |
94 | | { KRB5_KPASSWD_SUCCESS, "Success" }, |
95 | | { KRB5_KPASSWD_MALFORMED, "Malformed" }, |
96 | | { KRB5_KPASSWD_HARDERROR, "HardError" }, |
97 | | { KRB5_KPASSWD_AUTHERROR, "AuthError" }, |
98 | | { KRB5_KPASSWD_SOFTERROR, "SoftError" }, |
99 | | { KRB5_KPASSWD_ACCESSDENIED, "AccessDenied" }, |
100 | | { KRB5_KPASSWD_BAD_VERSION, "BadVersion" }, |
101 | | { KRB5_KPASSWD_INITIAL_FLAG_NEEDED, "InitialFlagNeeded" }, |
102 | | { 0, NULL } |
103 | | }; |
104 | | |
105 | | static int |
106 | | dissect_kpasswd_user_data_reply(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tree) |
107 | 0 | { |
108 | 0 | int offset=0; |
109 | 0 | uint16_t result; |
110 | | |
111 | | /* result */ |
112 | 0 | result = tvb_get_ntohs(tvb, offset); |
113 | 0 | proto_tree_add_uint(tree, hf_kpasswd_result, tvb, offset, 2, result); |
114 | 0 | offset+=2; |
115 | 0 | col_add_str(pinfo->cinfo, COL_INFO, |
116 | 0 | val_to_str(result, kpasswd_result_types, "Result: %u")); |
117 | | |
118 | | |
119 | | /* optional result string */ |
120 | 0 | if(tvb_reported_length_remaining(tvb, offset) > 0){ |
121 | 0 | proto_tree_add_item(tree, hf_kpasswd_result_string, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_ASCII); |
122 | 0 | offset = tvb_reported_length(tvb); |
123 | 0 | } |
124 | |
|
125 | 0 | return offset; |
126 | 0 | } |
127 | | |
128 | | |
129 | | static kerberos_callbacks cb_rep[] = { |
130 | | { KRB_CBTAG_PRIV_USER_DATA, dissect_kpasswd_user_data_reply }, |
131 | | { 0, NULL } |
132 | | }; |
133 | | |
134 | | static int |
135 | | dissect_kpasswd_krb_priv_message(packet_info *pinfo _U_, tvbuff_t *tvb, proto_tree *parent_tree, bool isrequest) |
136 | 1 | { |
137 | 1 | proto_item *it; |
138 | 1 | proto_tree *tree=NULL; |
139 | 1 | int offset; |
140 | | |
141 | 1 | if(parent_tree){ |
142 | 1 | it=proto_tree_add_item(parent_tree, hf_kpasswd_krb_priv_message, tvb, 0, -1, ENC_NA); |
143 | 1 | tree=proto_item_add_subtree(it, ett_krb_priv_message); |
144 | 1 | } |
145 | 1 | if(isrequest){ |
146 | 0 | offset = dissect_kerberos_main(tvb, pinfo, tree, false, cb_req); |
147 | 1 | } else { |
148 | 1 | offset = dissect_kerberos_main(tvb, pinfo, tree, false, cb_rep); |
149 | 1 | } |
150 | | |
151 | | /* offset is bytes consumed in child tvb given to us */ |
152 | 1 | return offset; |
153 | 1 | } |
154 | | |
155 | | |
156 | | static int |
157 | | dissect_kpasswd_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, bool have_rm) |
158 | 39 | { |
159 | 39 | proto_item *kpasswd_item=NULL; |
160 | 39 | proto_tree *kpasswd_tree=NULL; |
161 | 39 | int offset = 0; |
162 | 39 | uint16_t message_len, version, ap_req_len; |
163 | 39 | tvbuff_t *next_tvb; |
164 | | |
165 | | /* TCP record mark and length */ |
166 | 39 | uint32_t krb_rm = 0; |
167 | 39 | int krb_reclen = 0; |
168 | 39 | int krb_rm_size = 0; /* bytes consumed by record mark: 0 or 4 */ |
169 | | |
170 | 39 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "KPASSWD"); |
171 | 39 | col_clear(pinfo->cinfo, COL_INFO); |
172 | | |
173 | | /* can't pass have_rm to dissect_kerberos_main, so strip rm first */ |
174 | 39 | if (have_rm) { |
175 | 24 | krb_rm = tvb_get_ntohl(tvb, offset); |
176 | 24 | krb_reclen = kerberos_rm_to_reclen(krb_rm); |
177 | 24 | krb_rm_size = 4; |
178 | | /* |
179 | | * What is a reasonable size limit? |
180 | | */ |
181 | 24 | if (krb_reclen > 10 * 1024 * 1024) { |
182 | 3 | return (-1); |
183 | 3 | } |
184 | 21 | offset += krb_rm_size; |
185 | 21 | } |
186 | | |
187 | | /* it might be a KERBEROS ERROR */ |
188 | 36 | if(tvb_get_uint8(tvb, offset)==0x7e){ |
189 | | /* TCP record mark, if any, not displayed. But hopefully |
190 | | * KRB-ERROR dissection will proceed correctly. */ |
191 | 3 | next_tvb=tvb_new_subset_remaining(tvb, offset); |
192 | 3 | return dissect_kerberos_main(next_tvb, pinfo, tree, false, NULL); |
193 | 3 | } |
194 | | |
195 | 33 | message_len=tvb_get_ntohs(tvb, offset); |
196 | 33 | version=tvb_get_ntohs(tvb, offset+2); |
197 | 33 | ap_req_len=tvb_get_ntohs(tvb, offset+4); |
198 | 33 | if(tree){ |
199 | 19 | kpasswd_item=proto_tree_add_item(tree, proto_kpasswd, tvb, offset-krb_rm_size, message_len+krb_rm_size, ENC_NA); |
200 | 19 | kpasswd_tree=proto_item_add_subtree(kpasswd_item, ett_kpasswd); |
201 | 19 | if (have_rm) { |
202 | 7 | show_krb_recordmark(kpasswd_tree, tvb, offset-krb_rm_size, krb_rm); |
203 | 7 | } |
204 | 19 | } |
205 | | |
206 | 33 | proto_tree_add_uint(kpasswd_tree, hf_kpasswd_message_len, tvb, offset, 2, message_len); |
207 | 33 | proto_tree_add_uint(kpasswd_tree, hf_kpasswd_version, tvb, offset+2, 2, version); |
208 | 33 | col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(version, vers_vals, "Unknown command")); |
209 | 33 | proto_tree_add_uint(kpasswd_tree, hf_kpasswd_ap_req_len, tvb, offset+4, 2, ap_req_len); |
210 | 33 | offset+=6; |
211 | | |
212 | | /* AP-REQ / AP-REP data */ |
213 | 33 | next_tvb=tvb_new_subset_length(tvb, offset, ap_req_len); |
214 | 33 | dissect_kpasswd_ap_req_data(pinfo, next_tvb, kpasswd_tree); |
215 | 33 | offset+=ap_req_len; |
216 | | |
217 | | /* KRB-PRIV message */ |
218 | 33 | next_tvb=tvb_new_subset_remaining(tvb, offset); |
219 | 33 | offset += dissect_kpasswd_krb_priv_message(pinfo, next_tvb, kpasswd_tree, (version==0xff80)); |
220 | | |
221 | 33 | proto_item_set_len(kpasswd_item, offset); |
222 | 33 | return offset; |
223 | | |
224 | 36 | } |
225 | | |
226 | | static int |
227 | | dissect_kpasswd_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
228 | 15 | { |
229 | 15 | dissect_kpasswd_common(tvb, pinfo, tree, false); |
230 | 15 | return tvb_captured_length(tvb); |
231 | 15 | } |
232 | | |
233 | | static int |
234 | | dissect_kpasswd_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
235 | 24 | { |
236 | 24 | pinfo->fragmented = true; |
237 | 24 | if (dissect_kpasswd_common(tvb, pinfo, tree, true) < 0) { |
238 | | /* |
239 | | * The dissector failed to recognize this as a valid |
240 | | * Kerberos message. Mark it as a continuation packet. |
241 | | */ |
242 | 3 | col_set_str(pinfo->cinfo, COL_INFO, "Continuation"); |
243 | 3 | } |
244 | 24 | return tvb_captured_length(tvb); |
245 | 24 | } |
246 | | |
247 | | static int |
248 | | dissect_kpasswd_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) |
249 | 4 | { |
250 | 4 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "KPASSWD"); |
251 | 4 | col_clear(pinfo->cinfo, COL_INFO); |
252 | | |
253 | 4 | tcp_dissect_pdus(tvb, pinfo, tree, kpasswd_desegment, 4, get_krb_pdu_len, dissect_kpasswd_tcp_pdu, data); |
254 | 4 | return tvb_captured_length(tvb); |
255 | 4 | } |
256 | | |
257 | | void |
258 | | proto_register_kpasswd(void) |
259 | 14 | { |
260 | 14 | static hf_register_info hf[] = { |
261 | 14 | { &hf_kpasswd_message_len, |
262 | 14 | { "Message Length", "kpasswd.message_len", FT_UINT16, BASE_DEC, |
263 | 14 | NULL, 0, NULL, HFILL }}, |
264 | 14 | { &hf_kpasswd_ap_req_len, |
265 | 14 | { "AP_REQ Length", "kpasswd.ap_req_len", FT_UINT16, BASE_DEC, |
266 | 14 | NULL, 0, "Length of AP_REQ data", HFILL }}, |
267 | 14 | { &hf_kpasswd_version, |
268 | 14 | { "Version", "kpasswd.version", FT_UINT16, BASE_HEX, |
269 | 14 | VALS(vers_vals), 0, NULL, HFILL }}, |
270 | 14 | { &hf_kpasswd_result, |
271 | 14 | { "Result", "kpasswd.result", FT_UINT16, BASE_DEC, |
272 | 14 | VALS(kpasswd_result_types), 0, NULL, HFILL }}, |
273 | 14 | { &hf_kpasswd_result_string, |
274 | 14 | { "Result String", "kpasswd.result_string", FT_STRING, BASE_NONE, |
275 | 14 | NULL, 0, NULL, HFILL }}, |
276 | 14 | { &hf_kpasswd_ap_req_data, |
277 | 14 | { "AP_REQ", "kpasswd.ap_req", FT_NONE, BASE_NONE, |
278 | 14 | NULL, 0, "AP_REQ structure", HFILL }}, |
279 | 14 | { &hf_kpasswd_krb_priv_message, |
280 | 14 | { "KRB-PRIV", "kpasswd.krb_priv", FT_NONE, BASE_NONE, |
281 | 14 | NULL, 0, "KRB-PRIV message", HFILL }}, |
282 | 14 | { &hf_kpasswd_ChangePasswdData, { |
283 | 14 | "ChangePasswdData", "kpasswd.ChangePasswdData", FT_NONE, BASE_NONE, |
284 | 14 | NULL, 0, "Change Password Data structure", HFILL }}, |
285 | 14 | }; |
286 | | |
287 | 14 | static int *ett[] = { |
288 | 14 | &ett_kpasswd, |
289 | 14 | &ett_ap_req_data, |
290 | 14 | &ett_krb_priv_message, |
291 | 14 | }; |
292 | 14 | module_t *kpasswd_module; |
293 | | |
294 | 14 | proto_kpasswd = proto_register_protocol("MS Kpasswd", "Kpasswd", "kpasswd"); |
295 | 14 | proto_register_field_array(proto_kpasswd, hf, array_length(hf)); |
296 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
297 | | |
298 | | /* Register preferences */ |
299 | 14 | kpasswd_module = prefs_register_protocol(proto_kpasswd, NULL); |
300 | 14 | prefs_register_bool_preference(kpasswd_module, "desegment", |
301 | 14 | "Reassemble Kpasswd over TCP messages spanning multiple TCP segments", |
302 | 14 | "Whether the Kpasswd dissector should reassemble messages spanning multiple TCP segments." |
303 | 14 | " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.", |
304 | 14 | &kpasswd_desegment); |
305 | | |
306 | | /* Register dissectors */ |
307 | 14 | kpasswd_handle_udp = register_dissector("kpasswd.udp", dissect_kpasswd_udp, proto_kpasswd); |
308 | 14 | kpasswd_handle_tcp = register_dissector("kpasswd.tcp", dissect_kpasswd_tcp, proto_kpasswd); |
309 | 14 | } |
310 | | |
311 | | void |
312 | | proto_reg_handoff_kpasswd(void) |
313 | 14 | { |
314 | 14 | dissector_add_uint_with_preference("udp.port", UDP_PORT_KPASSWD, kpasswd_handle_udp); |
315 | 14 | dissector_add_uint_with_preference("tcp.port", TCP_PORT_KPASSWD, kpasswd_handle_tcp); |
316 | 14 | } |
317 | | |
318 | | /* |
319 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
320 | | * |
321 | | * Local variables: |
322 | | * c-basic-offset: 4 |
323 | | * tab-width: 8 |
324 | | * indent-tabs-mode: nil |
325 | | * End: |
326 | | * |
327 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
328 | | * :indentSize=4:tabSize=8:noTabs=true: |
329 | | */ |