Skip to content

Commit 6dc69c2

Browse files
BCB Sourcing Fixes & PP Finalize API (#30)
* BSL_14a, 14c, 14d, BSL_27b, BSL_28, BSL_49 * PP Finalize * BCB SC fixes --------- Co-authored-by: Brian Sipos <brian.sipos@jhuapl.edu>
1 parent 837d9cd commit 6dc69c2

32 files changed

Lines changed: 484 additions & 254 deletions

mock-bpa-test/_generate_simple_bundles.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,12 @@ def add_bcb_to_bundle_over_x(bundle, x):
152152

153153
b = [
154154
[7, 0, 0, [2, [1, 2]], [2, [2, 1]], [2, [2, 1]], [0, 40], 1000000],
155-
# 2 byte uint - 300
156-
[7, 2, 0, 0, '19012C'],
157-
[1, 1, 0, 0, '526561647920746f2067656e657261746520612033322d62797465207061796c6f6164']
155+
[1, 1, 0, 0, '526561647920746F2067656E657261746520612033322D62797465207061796C6F6164']
158156
]
159157

160158

161159
print (f"ORIGINAL BUNDLE: {b}")
162-
b = add_bib_to_bundle_over_x(b, 2)
160+
b = add_bib_to_bundle_over_x(b, 0)
163161
print(f'BUNDLE AFTER BIB: {b}')
164162
#b = add_bcb_to_bundle_over_x(b, 1)
165163
print(f'FINAL BUNDLE: {b}')

mock-bpa-test/requirements_tests.py

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -164,44 +164,35 @@ def __init__(self):
164164
# 3. Identical output with log showing verification
165165
# 4. Remove BIB
166166
#
167-
# Bundle Primary EIDs:
168-
# src dest
169-
# 1. [5.1, 6.1]
170-
# 2. [2.1, 5.1]
171-
# 3. [3.1, 5.1]
172-
# 4. [4.1, 5.1]
173-
#
174-
# Can use ONE policy config and "filters"
175-
# (e.g. source role, BIB policyrule filter should be role src for src_eid=2.1, role ver for 3.1, etc.)
176167
self.cases["BSL_14a"] = (_TestCase(
177168
input_data=[
178-
[7, 0, 0, [2, [6, 1]], [2, [5, 1]], [2, [2, 1]], [0, 40], 1000000],
169+
[7, 0, 0, [2, [1, 2]], [2, [2, 1]], [2, [2, 1]], [0, 40], 1000000],
179170
[11, 2, 0, 0, bytes.fromhex('810001018202820201828201078203008181820158405d9bdd1e2f043cf971588111f2fe1b847666cfacb7fb403c2468ef92a8ec93df80b41620df5bc639d0c355e1cce6217e17d3b8c5560edc14aba3d005196b046e')],
180171
[1, 1, 0, 0, bytes.fromhex('526561647920746F2067656E657261746520612033322D62797465207061796C6F6164')]
181172
],
182173
#
183174
expected_output=[
184-
[7, 0, 0, [2, [6, 1]], [2, [5, 1]], [2, [2, 1]], [0, 40], 1000000],
175+
[7, 0, 0, [2, [1, 2]], [2, [2, 1]], [2, [2, 1]], [0, 40], 1000000],
185176
[11, 2, 0, 0, bytes.fromhex('810001018202820201828201078203008181820158405d9bdd1e2f043cf971588111f2fe1b847666cfacb7fb403c2468ef92a8ec93df80b41620df5bc639d0c355e1cce6217e17d3b8c5560edc14aba3d005196b046e')],
186177
[1, 1, 0, 0, bytes.fromhex('526561647920746F2067656E657261746520612033322D62797465207061796C6F6164')]
187178
],
188179
#
189-
policy_config='0x82',
180+
policy_config='0x280',
190181
is_implemented=True,
191-
is_working=False,
182+
is_working=True,
192183
expect_success=True,
193184
input_data_format=DataFormat.BUNDLEARRAY,
194185
expected_output_format=DataFormat.BUNDLEARRAY
195186
))
196187
self.cases["BSL_14b"] = (_TestCase(
197188
input_data=[
198-
[7, 0, 0, [2, [5, 1]], [2, [2, 1]], [2, [2, 1]], [0, 40], 1000000],
189+
[7, 0, 0, [2, [1, 2]], [2, [2, 1]], [2, [2, 1]], [0, 40], 1000000],
199190
[11, 2, 0, 0, bytes.fromhex('810001018202820201828201078203008181820158405d9bdd1e2f043cf971588111f2fe1b847666cfacb7fb403c2468ef92a8ec93df80b41620df5bc639d0c355e1cce6217e17d3b8c5560edc14aba3d005196b046e')],
200191
[1, 1, 0, 0, bytes.fromhex('526561647920746F2067656E657261746520612033322D62797465207061796C6F6164')]
201192
],
202193
#
203194
expected_output=[
204-
[7, 0, 0, [2, [5, 1]], [2, [2, 1]], [2, [2, 1]], [0, 40], 1000000],
195+
[7, 0, 0, [2, [1, 2]], [2, [2, 1]], [2, [2, 1]], [0, 40], 1000000],
205196
[11, 2, 0, 0, bytes.fromhex('810001018202820201828201078203008181820158405d9bdd1e2f043cf971588111f2fe1b847666cfacb7fb403c2468ef92a8ec93df80b41620df5bc639d0c355e1cce6217e17d3b8c5560edc14aba3d005196b046e')],
206197
[11, 3, 0, 0, bytes.fromhex('810101018202820201828201078203008181820158403bdc69b3a34a2b5d3a8554368bd1e808f606219d2a10a846eae3886ae4ecc83c4ee550fdfb1cc636b904e2f1a73e303dcd4b6ccece003e95e8164dcc89a156e1')],
207198
[1, 1, 0, 0, bytes.fromhex('526561647920746F2067656E657261746520612033322D62797465207061796C6F6164')]
@@ -217,19 +208,19 @@ def __init__(self):
217208
# 14c) need logs to show verification
218209
self.cases["BSL_14c"] = (_TestCase(
219210
input_data=[
220-
[7, 0, 0, [2, [5, 1]], [2, [3, 1]], [2, [2, 1]], [0, 40], 1000000],
211+
[7, 0, 0, [2, [1, 2]], [2, [2, 1]], [2, [2, 1]], [0, 40], 1000000],
221212
[11, 2, 0, 0, bytes.fromhex('810001018202820201828201078203008181820158405d9bdd1e2f043cf971588111f2fe1b847666cfacb7fb403c2468ef92a8ec93df80b41620df5bc639d0c355e1cce6217e17d3b8c5560edc14aba3d005196b046e')],
222213
[1, 1, 0, 0, bytes.fromhex('526561647920746F2067656E657261746520612033322D62797465207061796C6F6164')]
223214
],
224215
#
225216
expected_output=[
226-
[7, 0, 0, [2, [5, 1]], [2, [3, 1]], [2, [2, 1]], [0, 40], 1000000],
217+
[7, 0, 0, [2, [1, 2]], [2, [2, 1]], [2, [2, 1]], [0, 40], 1000000],
227218
[11, 2, 0, 0, bytes.fromhex('810001018202820201828201078203008181820158405d9bdd1e2f043cf971588111f2fe1b847666cfacb7fb403c2468ef92a8ec93df80b41620df5bc639d0c355e1cce6217e17d3b8c5560edc14aba3d005196b046e')],
228219
[1, 1, 0, 0, bytes.fromhex('526561647920746F2067656E657261746520612033322D62797465207061796C6F6164')]
229220
],
230221
#
231222
# policy_config = BIB_VERIFIER,
232-
policy_config='0x42',
223+
policy_config='0x62',
233224
is_implemented=True,
234225
is_working=True,
235226
expect_success=True,
@@ -238,17 +229,17 @@ def __init__(self):
238229
))
239230
self.cases["BSL_14d"] = (_TestCase(
240231
input_data=[
241-
[7, 0, 0, [2, [5, 1]], [2, [4, 1]], [2, [2, 1]], [0, 40], 1000000],
232+
[7, 0, 0, [2, [1, 2]], [2, [2, 1]], [2, [2, 1]], [0, 40], 1000000],
242233
[11, 2, 0, 0, bytes.fromhex('810001018202820201828201078203008181820158405d9bdd1e2f043cf971588111f2fe1b847666cfacb7fb403c2468ef92a8ec93df80b41620df5bc639d0c355e1cce6217e17d3b8c5560edc14aba3d005196b046e')],
243234
[1, 1, 0, 0, bytes.fromhex('526561647920746F2067656E657261746520612033322D62797465207061796C6F6164')]
244235
],
245236
#
246237
expected_output=[
247-
[7, 0, 0, [2, [5, 1]], [2, [4, 1]], [2, [2, 1]], [0, 40], 1000000],
238+
[7, 0, 0, [2, [1, 2]], [2, [2, 1]], [2, [2, 1]], [0, 40], 1000000],
248239
[1, 1, 0, 0, bytes.fromhex('526561647920746F2067656E657261746520612033322D62797465207061796C6F6164')]
249240
],
250241
#
251-
policy_config='0x82',
242+
policy_config='0xA2',
252243
is_implemented=True,
253244
is_working=True,
254245
expect_success=True,
@@ -280,7 +271,7 @@ def __init__(self):
280271
],
281272
policy_config='0x5E',
282273
is_implemented=True,
283-
is_working=False,
274+
is_working=True,
284275
expect_success=True,
285276
input_data_format=DataFormat.BUNDLEARRAY,
286277
expected_output_format=DataFormat.BUNDLEARRAY
@@ -420,12 +411,12 @@ def __init__(self):
420411
],
421412
expected_output=[
422413
[7, 0, 0, [2, [1, 2]], [2, [2, 1]], [2, [2, 1]], [0, 40], 1000000],
423-
[12, 2, 1, 0, bytes.fromhex('810101018202820201828201078203008181820158403bdc69b3a34a2b5d3a8554368bd1e808f606219d2a10a846eae3886ae4ecc83c4ee550fdfb1cc636b904e2f1a73e303dcd4b6ccece003e95e8164dcc89a156e1')],
414+
[12, 2, 1, 0, bytes.fromhex('8101020182028202018482014c5477656c76653132313231328202018203581869c411276fecddc4780df42c8a2af89296fabf34d7fae7008204008181820150efa4b5ac0108e3816c5606479801bc04')],
424415
[1, 1, 0, 0, bytes.fromhex('3a09c1e63fe23a7f66a59c7303837241e070b02619fc59c5214a22f08cd70795e73e9a')]
425416
],
426-
policy_config='0x05',
417+
policy_config='0x105',
427418
is_implemented=True,
428-
is_working=False,
419+
is_working=True,
429420
expect_success=True,
430421
input_data_format=DataFormat.BUNDLEARRAY,
431422
expected_output_format=DataFormat.BUNDLEARRAY
@@ -444,12 +435,12 @@ def __init__(self):
444435
],
445436
expected_output=[
446437
[7, 0, 0, [2, [1, 2]], [2, [2, 1]], [2, [2, 1]], [0, 40], 1000000],
447-
[12, 2, 1, 0, bytes.fromhex('810101018202820201828201078203008181820158403bdc69b3a34a2b5d3a8554368bd1e808f606219d2a10a846eae3886ae4ecc83c4ee550fdfb1cc636b904e2f1a73e303dcd4b6ccece003e95e8164dcc89a156e1')],
438+
[12, 2, 1, 0, bytes.fromhex('8101020182028202018482014c5477656c76653132313231328202018203581869c411276fecddc4780df42c8a2af89296fabf34d7fae7008204008181820150efa4b5ac0108e3816c5606479801bc04')],
448439
[1, 1, 0, 0, bytes.fromhex('3a09c1e63fe23a7f66a59c7303837241e070b02619fc59c5214a22f08cd70795e73e9a')]
449440
],
450-
policy_config='0x05',
441+
policy_config='0x105',
451442
is_implemented=True,
452-
is_working=False,
443+
is_working=True,
453444
expect_success=True,
454445
input_data_format=DataFormat.BUNDLEARRAY,
455446
expected_output_format=DataFormat.BUNDLEARRAY
@@ -684,7 +675,7 @@ def __init__(self):
684675
#
685676
policy_config='0x105',
686677
is_implemented=True,
687-
is_working=False,
678+
is_working=True,
688679
expect_success=True,
689680
input_data_format=DataFormat.BUNDLEARRAY,
690681
expected_output_format=DataFormat.BUNDLEARRAY

src/BPSecLib_Private.h

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
#include <string.h>
4343
#include <syslog.h>
4444
#include <time.h>
45+
#include <sys/types.h>
46+
#include <qcbor/UsefulBuf.h>
4547

4648
#include "BPSecLib_Public.h"
4749

@@ -840,7 +842,7 @@ bool BSL_SecOper_IsRoleVerifier(const BSL_SecOper_t *self);
840842
* @param[in] self This Security Operation
841843
* @return boolean
842844
*/
843-
bool BSL_SecOper_IsRoleAccepter(const BSL_SecOper_t *self);
845+
bool BSL_SecOper_IsRoleAcceptor(const BSL_SecOper_t *self);
844846

845847
/** Return true if this security operation is BIB
846848
* @param[in] self This security operation
@@ -940,12 +942,13 @@ int BSL_AbsSecBlock_StripResults(BSL_AbsSecBlock_t *self, uint64_t target_block_
940942

941943
/** Encodes this ASB into a CBOR string into the space pre-allocated indicated by the argument.
942944
*
943-
* @param[in,out] self This ASB.
944-
* @param[in] allocated_target A buffer with allocated space for the encoded CBOR
945+
* @param[in] self This ASB.
946+
* @param[in] buf A buffer with allocated space for the encoded CBOR
947+
* or the @c SizeCalculateUsefulBuf value to get the real size.
945948
* @return Integer contains number of bytes written to buffer, negative indicates error.
946949
*
947950
*/
948-
int BSL_AbsSecBlock_EncodeToCBOR(const BSL_AbsSecBlock_t *self, BSL_Data_t allocated_target);
951+
ssize_t BSL_AbsSecBlock_EncodeToCBOR(const BSL_AbsSecBlock_t *self, UsefulBuf buf);
949952

950953
/** Decodes and populates this ASB from a CBOR string.
951954
*
@@ -1130,28 +1133,47 @@ size_t BSL_SecurityResponseSet_CountResponses(const BSL_SecurityResponseSet_t *s
11301133
* @note The caller is obligated to allocate space for the policy_action_set output.
11311134
* This memory must be zeroed before being passed, doing otherwise will raise an assertion.
11321135
*
1133-
* @param[in] self This policy provider.
1134-
* @param[out] output_action_set @preallocated Caller-allocated, zeroed space for action set
1136+
* @param[in] bsl BSL library context
1137+
* @param[out] output_action_set policy action set, which may contain error codes and other info. @preallocated
1138+
* Caller-allocated, zeroed space for action set
11351139
* @param[in,out] bundle Bundle seeking security operations
11361140
* @param[in] location Where in the BPA lifecycle this query arises from
1137-
* @return A policy action set, which may contain error codes and other info
1141+
* @return 0 if success
11381142
*/
11391143
int BSL_PolicyRegistry_InspectActions(const BSL_LibCtx_t *bsl, BSL_SecurityActionSet_t *output_action_set,
11401144
const BSL_BundleRef_t *bundle, BSL_PolicyLocation_e location);
11411145

1146+
/** Finalizes policy provider for sec ops & sec results for a bundle
1147+
*
1148+
* @param[in] bsl BSL library context
1149+
* @param[in] policy_actions A policy action set, which may contain error codes and other info. @preallocated
1150+
* Caller-allocated, zeroed space for action set
1151+
* @param[in,out] bundle Bundle seeking security operations
1152+
* @param[in] response_output results from security context
1153+
* @param[in] location Where in the BPA lifecycle this query arises from
1154+
* @return 0 if success
1155+
*/
1156+
int BSL_PolicyRegistry_FinalizeActions(const BSL_LibCtx_t *bsl, const BSL_SecurityActionSet_t *policy_actions,
1157+
const BSL_BundleRef_t *bundle, const BSL_SecurityResponseSet_t *response_output);
1158+
11421159
/// @brief Callback interface to query policy provider to populate the action set
11431160
typedef int (*BSL_PolicyInspect_f)(const void *user_data, BSL_SecurityActionSet_t *output_action_set,
11441161
const BSL_BundleRef_t *bundle, BSL_PolicyLocation_e location);
11451162

1163+
/// @brief Callback interface to query policy provider to populate the action set
1164+
typedef int (*BSL_PolicyFinalize_f)(const void *user_data, const BSL_SecurityActionSet_t *output_action_set,
1165+
const BSL_BundleRef_t *bundle, const BSL_SecurityResponseSet_t *response_output);
1166+
11461167
/// @brief Callback interface for policy provider to shut down and release any resources
11471168
typedef void (*BSL_PolicyDeinit_f)(void *user_data);
11481169

11491170
/// @brief Descriptor of opaque data and callbacks for Policy Provider.
11501171
struct BSL_PolicyDesc_s
11511172
{
1152-
void *user_data;
1153-
BSL_PolicyInspect_f query_fn; ///< Function pointer to query policy
1154-
BSL_PolicyDeinit_f deinit_fn; ///< Function to deinit the policy provider at termination of BSL
1173+
void *user_data;
1174+
BSL_PolicyInspect_f query_fn; ///< Function pointer to query policy
1175+
BSL_PolicyFinalize_f finalize_fn; ///< Function pointer to finalize policy
1176+
BSL_PolicyDeinit_f deinit_fn; ///< Function to deinit the policy provider at termination of BSL.
11551177
};
11561178

11571179
/** Call the underlying security context to perform the given action

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ set(BSL_FRONT_C
3838
add_library(bsl_front)
3939
target_sources(bsl_front PUBLIC ${BSL_FRONT_H})
4040
target_sources(bsl_front PRIVATE ${BSL_FRONT_C})
41+
target_link_libraries(bsl_front PUBLIC QCBOR::qcbor)
4142

4243
set_target_properties(bsl_front
4344
PROPERTIES

src/backend/AbsSecBlock.c

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ void BSL_AbsSecBlock_Print(const BSL_AbsSecBlock_t *self)
5858
BSL_LOG_INFO("ASB context id: %lu", self->sec_context_id);
5959
for (size_t index = 0; index < uint64_list_size(self->targets); index++)
6060
{
61-
BSL_LOG_INFO("ASB target[%lu]: %lu", index, *uint64_list_get(self->targets, index));
61+
BSL_LOG_INFO("ASB target[%lu]: %lu", index, *uint64_list_cget(self->targets, index));
6262
}
6363

6464
for (size_t index = 0; index < BSLB_SecParamList_size(self->params); index++)
@@ -231,22 +231,19 @@ int BSL_AbsSecBlock_StripResults(BSL_AbsSecBlock_t *self, uint64_t target_block_
231231
return (int)things_removed;
232232
}
233233

234-
int BSL_AbsSecBlock_EncodeToCBOR(const BSL_AbsSecBlock_t *self, BSL_Data_t allocated_target)
234+
ssize_t BSL_AbsSecBlock_EncodeToCBOR(const BSL_AbsSecBlock_t *self, UsefulBuf buf)
235235
{
236-
CHK_ARG_NONNULL(allocated_target.ptr);
237-
CHK_ARG_EXPR(allocated_target.len > 0);
238-
239236
CHK_PRECONDITION(BSL_AbsSecBlock_IsConsistent(self));
240237

241238
QCBOREncodeContext encoder;
242-
UsefulBuf allocated_buf = { .ptr = allocated_target.ptr, .len = allocated_target.len };
243-
QCBOREncode_Init(&encoder, allocated_buf);
239+
QCBOREncode_Init(&encoder, buf);
244240

245241
{
246242
QCBOREncode_OpenArray(&encoder);
247-
for (size_t target_index = 0; target_index < uint64_list_size(self->targets); target_index++)
243+
uint64_list_it_t it;
244+
for (uint64_list_it(it, self->targets); !uint64_list_end_p(it); uint64_list_next(it))
248245
{
249-
QCBOREncode_AddUInt64(&encoder, *uint64_list_get(self->targets, target_index));
246+
QCBOREncode_AddUInt64(&encoder, *uint64_list_cref(it));
250247
}
251248
QCBOREncode_CloseArray(&encoder);
252249
}
@@ -257,14 +254,19 @@ int BSL_AbsSecBlock_EncodeToCBOR(const BSL_AbsSecBlock_t *self, BSL_Data_t alloc
257254

258255
{
259256
// TODO - Maybe this should be generated on-the-fly
260-
uint64_t flags = BSLB_SecParamList_size(self->params) > 0 ? true : false;
257+
uint64_t flags = 0;
258+
if (BSLB_SecParamList_size(self->params) > 0)
259+
{
260+
flags |= 0x1;
261+
}
261262
QCBOREncode_AddUInt64(&encoder, flags);
262263
}
263264

264265
BSL_HostEID_EncodeToCBOR(&self->source_eid, (void *)&encoder);
265266

266267
{
267268
QCBOREncode_OpenArray(&encoder);
269+
268270
for (size_t param_index = 0; param_index < BSLB_SecParamList_size(self->params); param_index++)
269271
{
270272
const BSL_SecParam_t *param = BSLB_SecParamList_cget(self->params, param_index);
@@ -312,14 +314,15 @@ int BSL_AbsSecBlock_EncodeToCBOR(const BSL_AbsSecBlock_t *self, BSL_Data_t alloc
312314
QCBOREncode_CloseArray(&encoder);
313315
}
314316

315-
UsefulBufC output_buf;
316-
QCBORError qcbor_err = QCBOREncode_Finish(&encoder, &output_buf);
317+
size_t encode_sz;
318+
QCBORError qcbor_err = QCBOREncode_FinishGetSize(&encoder, &encode_sz);
319+
BSL_LOG_INFO("QCBOR ENCODE SIZE: %zu", encode_sz);
317320
if (qcbor_err != QCBOR_SUCCESS)
318321
{
319322
BSL_LOG_ERR("Encoding ASB into BTSD failed: %s", qcbor_err_to_str(qcbor_err));
320323
return BSL_ERR_ENCODING;
321324
}
322-
return (int)output_buf.len;
325+
return (ssize_t)encode_sz;
323326
}
324327

325328
int BSL_AbsSecBlock_DecodeFromCBOR(BSL_AbsSecBlock_t *self, BSL_Data_t encoded_cbor)

src/backend/AbsSecBlock.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
#include <stddef.h>
3535
#include <stdint.h>
36+
#include <m-array.h>
3637

3738
#include <BPSecLib_Public.h>
3839

@@ -46,7 +47,7 @@
4647
*/
4748
// NOLINTBEGIN
4849
/// @cond Doxygen_Suppress
49-
LIST_DEF(uint64_list, uint64_t)
50+
M_ARRAY_DEF(uint64_list, uint64_t)
5051
// NOLINTEND
5152

5253
/** Represents the Abstract Security Block as defined in RFC9172

src/backend/PolicyProvider.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,14 @@ int BSL_PolicyRegistry_InspectActions(const BSL_LibCtx_t *bsl, BSL_SecurityActio
3939
CHK_PRECONDITION(bsl->policy_registry.query_fn != NULL);
4040
return bsl->policy_registry.query_fn(bsl->policy_registry.user_data, output_action_set, bundle, location);
4141
}
42+
43+
int BSL_PolicyRegistry_FinalizeActions(const BSL_LibCtx_t *bsl, const BSL_SecurityActionSet_t *policy_actions,
44+
const BSL_BundleRef_t *bundle, const BSL_SecurityResponseSet_t *response_output)
45+
{
46+
CHK_ARG_NONNULL(bsl);
47+
CHK_ARG_NONNULL(policy_actions);
48+
CHK_ARG_NONNULL(response_output);
49+
CHK_ARG_NONNULL(bundle);
50+
CHK_PRECONDITION(bsl->policy_registry.finalize_fn != NULL);
51+
return bsl->policy_registry.finalize_fn(bsl->policy_registry.user_data, policy_actions, bundle, response_output);
52+
}

0 commit comments

Comments
 (0)