Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-bpsec.c
blob29035dfa146488186940b469c20b92dfa25139ad
1 /* packet-bpsec.c
2 * Routines for Bundle Protocol Version 7 Security (BPSec) dissection
3 * References:
4 * RFC 9172: https://www.rfc-editor.org/rfc/rfc9172.html
6 * Copyright 2019-2024, Brian Sipos <brian.sipos@gmail.com>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * SPDX-License-Identifier: LGPL-2.1-or-later
14 #include "config.h"
15 #include <stdint.h>
17 #include "packet-bpsec.h"
18 #include <epan/packet.h>
19 #include <epan/prefs.h>
20 #include <epan/proto.h>
21 #include <epan/expert.h>
22 #include <epan/tfs.h>
23 #include <epan/wscbor.h>
25 void proto_register_bpsec(void);
26 void proto_reg_handoff_bpsec(void);
28 /// Protocol handles
29 static int proto_bpsec;
31 /// Dissect opaque CBOR data
32 static dissector_handle_t handle_cbor;
33 /// Context ID sub-dissectors
34 static dissector_table_t secctx_dissectors;
35 /// Parameter value sub-dissectors
36 static dissector_table_t param_dissectors;
37 /// Result value sub-dissectors
38 static dissector_table_t result_dissectors;
40 // Field definitions
41 static int hf_bib;
42 static int hf_bcb;
43 static int hf_asb_target_list;
44 static int hf_asb_target;
45 static int hf_asb_ctxid;
46 static int hf_asb_flags;
47 static int hf_asb_flags_has_params;
48 static int hf_asb_secsrc_nodeid;
49 static int hf_asb_secsrc_uri;
50 static int hf_asb_param_list;
51 static int hf_asb_param_pair;
52 static int hf_asb_param_id;
53 static int hf_asb_result_all_list;
54 static int hf_asb_result_tgt_list;
55 static int hf_asb_result_tgt_ref;
56 static int hf_asb_result_pair;
57 static int hf_asb_result_id;
59 static int *const asb_flags[] = {
60 &hf_asb_flags_has_params,
61 NULL
64 // Tree structures
65 static int ett_asb;
66 static int ett_asb_flags;
67 static int ett_tgt_list;
68 static int ett_param_list;
69 static int ett_param_pair;
70 static int ett_result_all_list;
71 static int ett_result_tgt_list;
72 static int ett_result_pair;
74 static expert_field ei_secsrc_diff;
75 static expert_field ei_ctxid_zero;
76 static expert_field ei_ctxid_priv;
77 static expert_field ei_target_invalid;
78 static expert_field ei_value_partial_decode;
80 bpsec_id_t * bpsec_id_new(wmem_allocator_t *alloc, int64_t context_id, int64_t type_id) {
81 bpsec_id_t *obj;
82 if (alloc) {
83 obj = wmem_new(alloc, bpsec_id_t);
85 else {
86 obj = g_new(bpsec_id_t, 1);
88 obj->context_id = context_id;
89 obj->type_id = type_id;
90 return obj;
93 void bpsec_id_free(wmem_allocator_t *alloc, void *ptr) {
94 //bpsec_id_t *obj = (bpsec_id_t *)ptr;
95 wmem_free(alloc, ptr);
98 gboolean bpsec_id_equal(const void *a, const void *b) {
99 const bpsec_id_t *aobj = a;
100 const bpsec_id_t *bobj = b;
101 return (
102 aobj && bobj
103 && (aobj->context_id == bobj->context_id)
104 && (aobj->type_id == bobj->type_id)
108 unsigned bpsec_id_hash(const void *key) {
109 const bpsec_id_t *obj = key;
110 return (
111 g_int64_hash(&(obj->context_id))
112 ^ g_int64_hash(&(obj->type_id))
116 /** Dissect an ID-value pair within a context.
119 static int dissect_value(dissector_handle_t dissector, bpsec_dissector_data_t *const data, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
120 int sublen = 0;
121 if (dissector) {
122 sublen = call_dissector_with_data(dissector, tvb, pinfo, tree, data);
123 if ((sublen < 0) || ((unsigned)sublen < tvb_captured_length(tvb))) {
124 expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_value_partial_decode);
127 if (sublen == 0) {
128 sublen = call_dissector(handle_cbor, tvb, pinfo, tree);
130 return sublen;
133 /** Dissector for abstract security block structure.
135 static int dissect_block_asb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, const bp_dissector_data_t *const data, int root_hfindex) {
136 proto_item *item_asb = proto_tree_add_item(tree, root_hfindex, tvb, 0, -1, ENC_NA);
137 proto_tree *tree_asb = proto_item_add_subtree(item_asb, ett_asb);
138 int offset = 0;
140 wmem_array_t *targets;
141 targets = wmem_array_new(pinfo->pool, sizeof(uint64_t));
143 wscbor_chunk_t *chunk_tgt_list = wscbor_chunk_read(pinfo->pool, tvb, &offset);
144 wscbor_require_array(chunk_tgt_list);
145 proto_item *item_tgt_list = proto_tree_add_cbor_container(tree_asb, hf_asb_target_list, pinfo, tvb, chunk_tgt_list);
146 if (!wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_tgt_list)) {
147 proto_tree *tree_tgt_list = proto_item_add_subtree(item_tgt_list, ett_tgt_list);
149 // iterate all targets
150 for (uint64_t param_ix = 0; param_ix < chunk_tgt_list->head_value; ++param_ix) {
151 wscbor_chunk_t *chunk_tgt = wscbor_chunk_read(pinfo->pool, tvb, &offset);
152 uint64_t *tgt_blknum = wscbor_require_uint64(pinfo->pool, chunk_tgt);
153 proto_item *item_tgt = proto_tree_add_cbor_uint64(tree_tgt_list, hf_asb_target, pinfo, tvb, chunk_tgt, tgt_blknum);
154 if (tgt_blknum) {
155 wmem_array_append(targets, tgt_blknum, 1);
157 wmem_map_t *map = NULL;
158 if (*tgt_blknum == 0) {
159 map = (root_hfindex == hf_bib)
160 ? data->bundle->primary->sec.data_i
161 : data->bundle->primary->sec.data_c;
163 else {
164 bp_block_canonical_t *found = wmem_map_lookup(data->bundle->block_nums, tgt_blknum);
165 if (found) {
166 map = (root_hfindex == hf_bib)
167 ? found->sec.data_i
168 : found->sec.data_c;
170 else {
171 expert_add_info(pinfo, item_tgt, &ei_target_invalid);
174 if (map && (data->block->block_number)) {
175 wmem_map_insert(
176 map,
177 data->block->block_number,
178 NULL
184 proto_item_set_len(item_tgt_list, offset - chunk_tgt_list->start);
187 wscbor_chunk_t *chunk_ctxid = wscbor_chunk_read(pinfo->pool, tvb, &offset);
188 int64_t *ctxid = wscbor_require_int64(pinfo->pool, chunk_ctxid);
189 proto_item *item_ctxid = proto_tree_add_cbor_int64(tree_asb, hf_asb_ctxid, pinfo, tvb, chunk_ctxid, ctxid);
190 if (ctxid && item_ctxid) {
191 if (*ctxid == 0) {
192 expert_add_info(pinfo, item_ctxid, &ei_ctxid_zero);
194 else if (*ctxid < 0) {
195 expert_add_info(pinfo, item_ctxid, &ei_ctxid_priv);
198 // apply label if registered
199 dissector_handle_t ctx_dis = dissector_get_custom_table_handle(secctx_dissectors, ctxid);
200 const char *dis_name = dissector_handle_get_description(ctx_dis);
201 if (dis_name) {
202 const header_field_info *hfinfo = PITEM_HFINFO(item_ctxid);
203 proto_item_set_text(item_ctxid, "%s: %s (%" PRId64 ")", hfinfo ? hfinfo->name : NULL, dis_name, *ctxid);
207 wscbor_chunk_t *chunk_flags = wscbor_chunk_read(pinfo->pool, tvb, &offset);
208 uint64_t *flags = wscbor_require_uint64(pinfo->pool, chunk_flags);
209 proto_tree_add_cbor_bitmask(tree_asb, hf_asb_flags, ett_asb_flags, asb_flags, pinfo, tvb, chunk_flags, flags);
212 bp_eid_t *secsrc = bp_eid_new(pinfo->pool);
213 proto_item *item_secsrc = proto_tree_add_cbor_eid(tree_asb, hf_asb_secsrc_nodeid, hf_asb_secsrc_uri, pinfo, tvb, &offset, secsrc);
214 if (!bp_eid_equal(data->bundle->primary->src_nodeid, secsrc)) {
215 expert_add_info(pinfo, item_secsrc, &ei_secsrc_diff);
219 if (flags && (*flags & BPSEC_ASB_HAS_PARAMS)) {
220 wscbor_chunk_t *chunk_param_list = wscbor_chunk_read(pinfo->pool, tvb, &offset);
221 wscbor_require_array(chunk_param_list);
222 proto_item *item_param_list = proto_tree_add_cbor_container(tree_asb, hf_asb_param_list, pinfo, tvb, chunk_param_list);
223 if (!wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_param_list)) {
224 proto_tree *tree_param_list = proto_item_add_subtree(item_param_list, ett_param_list);
226 // iterate all parameters
227 for (uint64_t param_ix = 0; param_ix < chunk_param_list->head_value; ++param_ix) {
228 wscbor_chunk_t *chunk_param_pair = wscbor_chunk_read(pinfo->pool, tvb, &offset);
229 wscbor_require_array_size(chunk_param_pair, 2, 2);
230 proto_item *item_param_pair = proto_tree_add_cbor_container(tree_param_list, hf_asb_param_pair, pinfo, tvb, chunk_param_pair);
231 if (!wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_param_pair)) {
232 proto_tree *tree_param_pair = proto_item_add_subtree(item_param_pair, ett_param_pair);
234 wscbor_chunk_t *chunk_paramid = wscbor_chunk_read(pinfo->pool, tvb, &offset);
235 int64_t *paramid = wscbor_require_int64(pinfo->pool, chunk_paramid);
236 proto_tree_add_cbor_int64(tree_param_pair, hf_asb_param_id, pinfo, tvb, chunk_paramid, paramid);
238 const int offset_value = offset;
239 if (!wscbor_skip_next_item(pinfo->pool, tvb, &offset)) {
240 return 0;
242 tvbuff_t *tvb_value = tvb_new_subset_length(tvb, offset_value, offset - offset_value);
244 bpsec_dissector_data_t bpsec_data = { .bp = data };
245 dissector_handle_t value_dissect = NULL;
246 if (ctxid && paramid) {
247 bpsec_data.id.context_id = *ctxid;
248 bpsec_data.id.type_id = *paramid;
249 value_dissect = dissector_get_custom_table_handle(param_dissectors, &(bpsec_data.id));
251 const char *dis_name = dissector_handle_get_description(value_dissect);
252 if (paramid) {
253 proto_item_append_text(item_param_pair, ": %s (%" PRId64 ")", dis_name, *paramid);
255 dissect_value(value_dissect, &bpsec_data, tvb_value, pinfo, tree_param_pair);
257 proto_item_set_len(item_param_pair, offset - chunk_param_pair->start);
261 proto_item_set_len(item_param_list, offset - chunk_param_list->start);
265 // array sizes should agree
266 const unsigned tgt_size = wmem_array_get_count(targets);
268 wscbor_chunk_t *chunk_result_all_list = wscbor_chunk_read(pinfo->pool, tvb, &offset);
269 wscbor_require_array_size(chunk_result_all_list, tgt_size, tgt_size);
270 proto_item *item_result_all_list = proto_tree_add_cbor_container(tree_asb, hf_asb_result_all_list, pinfo, tvb, chunk_result_all_list);
271 if (!wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_result_all_list)) {
272 proto_tree *tree_result_all_list = proto_item_add_subtree(item_result_all_list, ett_result_all_list);
274 // iterate each target's results
275 for (unsigned tgt_ix = 0; tgt_ix < tgt_size; ++tgt_ix) {
276 wscbor_chunk_t *chunk_result_tgt_list = wscbor_chunk_read(pinfo->pool, tvb, &offset);
277 wscbor_require_array(chunk_result_tgt_list);
278 proto_item *item_result_tgt_list = proto_tree_add_cbor_container(tree_result_all_list, hf_asb_result_tgt_list, pinfo, tvb, chunk_result_tgt_list);
279 if (!wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_result_tgt_list)) {
280 proto_tree *tree_result_tgt_list = proto_item_add_subtree(item_result_tgt_list, ett_result_tgt_list);
282 // Hint at the associated target number
283 if (tgt_ix < tgt_size) {
284 const uint64_t *tgt_blknum = wmem_array_index(targets, tgt_ix);
285 proto_item *item_tgt_blknum = proto_tree_add_uint64(tree_result_tgt_list, hf_asb_result_tgt_ref, tvb, 0, 0, *tgt_blknum);
286 proto_item_set_generated(item_tgt_blknum);
289 // iterate all results for this target
290 for (uint64_t result_ix = 0; result_ix < chunk_result_tgt_list->head_value; ++result_ix) {
291 wscbor_chunk_t *chunk_result_pair = wscbor_chunk_read(pinfo->pool, tvb, &offset);
292 wscbor_require_array_size(chunk_result_pair, 2, 2);
293 proto_item *item_result_pair = proto_tree_add_cbor_container(tree_result_tgt_list, hf_asb_result_pair, pinfo, tvb, chunk_result_pair);
294 if (!wscbor_skip_if_errors(pinfo->pool, tvb, &offset, chunk_result_pair)) {
295 proto_tree *tree_result_pair = proto_item_add_subtree(item_result_pair, ett_result_pair);
297 wscbor_chunk_t *chunk_resultid = wscbor_chunk_read(pinfo->pool, tvb, &offset);
298 int64_t *resultid = wscbor_require_int64(pinfo->pool, chunk_resultid);
299 proto_tree_add_cbor_int64(tree_result_pair, hf_asb_result_id, pinfo, tvb, chunk_resultid, resultid);
301 const int offset_value = offset;
302 if (!wscbor_skip_next_item(pinfo->pool, tvb, &offset)) {
303 return 0;
305 tvbuff_t *tvb_value = tvb_new_subset_length(tvb, offset_value, offset - offset_value);
307 bpsec_dissector_data_t bpsec_data = { .bp = data };
308 dissector_handle_t value_dissect = NULL;
309 if (ctxid && resultid) {
310 bpsec_data.id.context_id = *ctxid;
311 bpsec_data.id.type_id = *resultid;
312 value_dissect = dissector_get_custom_table_handle(result_dissectors, &(bpsec_data.id));
314 const char *dis_name = dissector_handle_get_description(value_dissect);
315 if (resultid) {
316 proto_item_append_text(item_result_pair, ": %s (%" PRId64 ")", dis_name, *resultid);
318 dissect_value(value_dissect, &bpsec_data, tvb_value, pinfo, tree_result_pair);
320 proto_item_set_len(item_result_pair, offset - chunk_result_pair->start);
324 proto_item_set_len(item_result_tgt_list, offset - chunk_result_tgt_list->start);
328 proto_item_set_len(item_result_all_list, offset - chunk_result_all_list->start);
331 proto_item_set_len(item_asb, offset);
332 return offset;
335 /** Dissector for Bundle Integrity block.
337 static int dissect_block_bib(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
338 return dissect_block_asb(tvb, pinfo, tree, (bp_dissector_data_t *)data, hf_bib);
341 /** Dissector for Bundle Confidentiality block.
343 static int dissect_block_bcb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
344 return dissect_block_asb(tvb, pinfo, tree, (bp_dissector_data_t *)data, hf_bcb);
347 /// Re-initialize after a configuration change
348 static void reinit_bpsec(void) {
351 /// Overall registration of the protocol
352 void proto_register_bpsec(void) {
353 static hf_register_info fields[] = {
354 {&hf_bib, {"BPSec Block Integrity Block", "bpsec.bib", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}},
355 {&hf_bcb, {"BPSec Block Confidentiality Block", "bpsec.bcb", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}},
356 {&hf_asb_target_list, {"Security Targets, Count", "bpsec.asb.target_count", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
357 {&hf_asb_target, {"Target Block Number", "bpsec.asb.target", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
358 {&hf_asb_ctxid, {"Context ID", "bpsec.asb.ctxid", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
359 {&hf_asb_flags, {"Flags", "bpsec.asb.flags", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}},
360 {&hf_asb_flags_has_params, {"Parameters Present", "bpsec.asb.flags.has_params", FT_BOOLEAN, 8, TFS(&tfs_set_notset), BPSEC_ASB_HAS_PARAMS, NULL, HFILL}},
361 {&hf_asb_secsrc_nodeid, {"Security Source", "bpsec.asb.secsrc.nodeid", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
362 {&hf_asb_secsrc_uri, {"Security Source URI", "bpsec.asb.secsrc.uri", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
363 {&hf_asb_param_list, {"Security Parameters, Count", "bpsec.asb.param_count", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
364 {&hf_asb_param_pair, {"Parameter", "bpsec.asb.param", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
365 {&hf_asb_param_id, {"Type ID", "bpsec.asb.param.id", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
366 {&hf_asb_result_all_list, {"Security Result Targets, Count", "bpsec.asb.result_count", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
367 {&hf_asb_result_tgt_ref, {"Associated Target Block Number", "bpsec.asb.result_tgt_ref", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
368 {&hf_asb_result_tgt_list, {"Security Results, Count", "bpsec.asb.result_count", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
369 {&hf_asb_result_pair, {"Result", "bpsec.asb.result", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
370 {&hf_asb_result_id, {"Type ID", "bpsec.asb.result.id", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
372 static int *ett[] = {
373 &ett_asb,
374 &ett_asb_flags,
375 &ett_tgt_list,
376 &ett_param_list,
377 &ett_param_pair,
378 &ett_result_all_list,
379 &ett_result_tgt_list,
380 &ett_result_pair,
382 static ei_register_info expertitems[] = {
383 {&ei_secsrc_diff, {"bpsec.secsrc_diff", PI_SECURITY, PI_CHAT, "BPSec Security Source different from bundle Source", EXPFILL}},
384 {&ei_ctxid_zero, {"bpsec.ctxid_zero", PI_SECURITY, PI_WARN, "BPSec Security Context ID zero is reserved", EXPFILL}},
385 {&ei_ctxid_priv, {"bpsec.ctxid_priv", PI_SECURITY, PI_NOTE, "BPSec Security Context ID from private/experimental block", EXPFILL}},
386 {&ei_target_invalid, {"bpsec.target_invalid", PI_PROTOCOL, PI_WARN, "Target block number not present", EXPFILL}},
387 {&ei_value_partial_decode, {"bpsec.value_partial_decode", PI_UNDECODED, PI_WARN, "Value data not fully dissected", EXPFILL}},
390 proto_bpsec = proto_register_protocol(
391 "DTN Bundle Protocol Security",
392 "BPSec",
393 "bpsec"
396 proto_register_field_array(proto_bpsec, fields, array_length(fields));
397 proto_register_subtree_array(ett, array_length(ett));
398 expert_module_t *expert = expert_register_protocol(proto_bpsec);
399 expert_register_field_array(expert, expertitems, array_length(expertitems));
401 secctx_dissectors = register_custom_dissector_table("bpsec.ctx", "BPSec Context", proto_bpsec, g_int64_hash, g_int64_equal, g_free);
402 param_dissectors = register_custom_dissector_table("bpsec.param", "BPSec Parameter", proto_bpsec, bpsec_id_hash, bpsec_id_equal, g_free);
403 result_dissectors = register_custom_dissector_table("bpsec.result", "BPSec Result", proto_bpsec, bpsec_id_hash, bpsec_id_equal, g_free);
405 prefs_register_protocol(proto_bpsec, reinit_bpsec);
408 void proto_reg_handoff_bpsec(void) {
409 handle_cbor = find_dissector("cbor");
411 /* Packaged extensions */
413 uint64_t *key = g_new(uint64_t, 1);
414 *key = BP_BLOCKTYPE_BIB;
415 dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_block_bib, proto_bpsec, NULL, "Block Integrity Block");
416 dissector_add_custom_table_handle("bpv7.block_type", key, hdl);
419 uint64_t *key = g_new(uint64_t, 1);
420 *key = BP_BLOCKTYPE_BCB;
421 dissector_handle_t hdl = create_dissector_handle_with_name_and_description(dissect_block_bcb, proto_bpsec, NULL, "Block Confidentiality Block");
422 dissector_add_custom_table_handle("bpv7.block_type", key, hdl);
425 reinit_bpsec();