libkmipclient: Limit the nesting level of KMIP STRUCTURE recursion

KMIP STRUCTURE elements can be nested, which causes a recursion of
functions kmip_decode_ttlv(), kmip_decode_xml(), and kmip_decode_json().
A malformed KMIP response may thus cause stack exhaustion.

Limit the KMIP STRUCTURE nesting level to 32 levels. This is more than
enough for currently defined KMIP responses. The practically used
nesting level is 8 or 9, dependent on the type of KMIP response.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Ingo Franzki
2026-06-30 10:19:17 +02:00
committed by Jan Höppner
parent f0bf1985c3
commit 5fdeaab3d0
6 changed files with 43 additions and 14 deletions

View File

@@ -735,7 +735,8 @@ int kmip_connection_https_perform(struct kmip_connection *conn,
switch (conn->config.encoding) { switch (conn->config.encoding) {
case KMIP_ENCODING_TTLV: case KMIP_ENCODING_TTLV:
rc = kmip_decode_ttlv(write_cb.ttlv.resp_mem_bio, NULL, rc = kmip_decode_ttlv(write_cb.ttlv.resp_mem_bio, NULL,
response, debug); response, KMIP_DECODE_MAX_NESTING_LEVEL,
debug);
if (rc != 0) { if (rc != 0) {
kmip_debug(debug, "kmip_decode_ttlv failed"); kmip_debug(debug, "kmip_decode_ttlv failed");
goto out; goto out;
@@ -750,7 +751,7 @@ int kmip_connection_https_perform(struct kmip_connection *conn,
} }
rc = kmip_decode_json(write_cb.json.resp_obj, NULL, response, rc = kmip_decode_json(write_cb.json.resp_obj, NULL, response,
debug); KMIP_DECODE_MAX_NESTING_LEVEL, debug);
if (rc != 0) { if (rc != 0) {
kmip_debug(debug, "kmip_decode_json failed"); kmip_debug(debug, "kmip_decode_json failed");
goto out; goto out;
@@ -768,7 +769,8 @@ int kmip_connection_https_perform(struct kmip_connection *conn,
rc = kmip_decode_xml(xmlDocGetRootElement( rc = kmip_decode_xml(xmlDocGetRootElement(
write_cb.xml.ctx->myDoc), write_cb.xml.ctx->myDoc),
NULL, response, debug); NULL, response,
KMIP_DECODE_MAX_NESTING_LEVEL, debug);
if (rc != 0) { if (rc != 0) {
kmip_debug(debug, "kmip_decode_xml failed"); kmip_debug(debug, "kmip_decode_xml failed");
goto out; goto out;

View File

@@ -27,12 +27,16 @@
* @param parent the parent node or NULL if no parent exists. * @param parent the parent node or NULL if no parent exists.
* @param node On return: the decoded node. The newly allocated * @param node On return: the decoded node. The newly allocated
* node has a reference count of 1. * node has a reference count of 1.
* @param max_nesting_level the maximum nesting levels of structures within the
* KMIP node. If the nesting level is reached, E2BIG
* is returned.
* @param debug if true, debug messages are printed * @param debug if true, debug messages are printed
* *
* @returns 0 in case of success, or a negative errno value * @returns 0 in case of success, or a negative errno value
*/ */
int kmip_decode_json(const json_object *obj, struct kmip_node *parent, int kmip_decode_json(const json_object *obj, struct kmip_node *parent,
struct kmip_node **node, bool debug) struct kmip_node **node, size_t max_nesting_level,
bool debug)
{ {
json_object *tag_obj, *type_obj, *value_obj, *name_obj; json_object *tag_obj, *type_obj, *value_obj, *name_obj;
enum kmip_tag tag, v1_attr_tag = 0; enum kmip_tag tag, v1_attr_tag = 0;
@@ -42,6 +46,9 @@ int kmip_decode_json(const json_object *obj, struct kmip_node *parent,
int rc, num, i; int rc, num, i;
int64_t int64; int64_t int64;
if (max_nesting_level == 0)
return -E2BIG;
if (obj == NULL || node == NULL) if (obj == NULL || node == NULL)
return -EINVAL; return -EINVAL;
@@ -138,7 +145,7 @@ int kmip_decode_json(const json_object *obj, struct kmip_node *parent,
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
rc = kmip_decode_json( rc = kmip_decode_json(
json_object_array_get_idx(value_obj, i), json_object_array_get_idx(value_obj, i),
n, &e, debug); n, &e, max_nesting_level - 1, debug);
if (rc != 0) { if (rc != 0) {
kmip_debug(debug, "Failed to parse " kmip_debug(debug, "Failed to parse "
"array element %d", i); "array element %d", i);

View File

@@ -94,19 +94,23 @@ int kmip_connection_https_perform(struct kmip_connection *connection,
bool debug); bool debug);
void kmip_connection_https_term(struct kmip_connection *connection); void kmip_connection_https_term(struct kmip_connection *connection);
/* KIMP decoding and encoding internal functions */ /* KIMP decoding and encoding internal functions and definitions */
#define KMIP_DECODE_MAX_NESTING_LEVEL 32
int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node, int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node,
bool debug); size_t max_nesting_level, bool debug);
int kmip_encode_ttlv(struct kmip_node *node, BIO *bio, size_t *size, int kmip_encode_ttlv(struct kmip_node *node, BIO *bio, size_t *size,
bool debug); bool debug);
int kmip_decode_json(const json_object *obj, struct kmip_node *parent, int kmip_decode_json(const json_object *obj, struct kmip_node *parent,
struct kmip_node **node, bool debug); struct kmip_node **node, size_t max_nesting_level,
bool debug);
int kmip_encode_json(const struct kmip_node *node, json_object **obj, int kmip_encode_json(const struct kmip_node *node, json_object **obj,
bool debug); bool debug);
int kmip_decode_xml(const xmlNode *xml, struct kmip_node *parent, int kmip_decode_xml(const xmlNode *xml, struct kmip_node *parent,
struct kmip_node **node, bool debug); struct kmip_node **node, size_t max_nesting_level,
bool debug);
int kmip_encode_xml(const struct kmip_node *node, xmlNode **xml, bool debug); int kmip_encode_xml(const struct kmip_node *node, xmlNode **xml, bool debug);
#endif #endif

View File

@@ -493,7 +493,8 @@ int kmip_connection_tls_perform(struct kmip_connection *conn,
kmip_debug(debug, "%lu bytes sent", size); kmip_debug(debug, "%lu bytes sent", size);
/* receive the response */ /* receive the response */
rc = kmip_decode_ttlv(conn->plain_tls.bio, NULL, response, debug); rc = kmip_decode_ttlv(conn->plain_tls.bio, NULL, response,
KMIP_DECODE_MAX_NESTING_LEVEL, debug);
if (rc != 0 || *response == NULL) { if (rc != 0 || *response == NULL) {
kmip_debug(debug, "kmip_decode_ttlv failed"); kmip_debug(debug, "kmip_decode_ttlv failed");
goto out; goto out;

View File

@@ -28,12 +28,15 @@
* as many bytes as needed. * as many bytes as needed.
* @param node On return: the decoded node. The newly allocated * @param node On return: the decoded node. The newly allocated
* node has a reference count of 1. * node has a reference count of 1.
* @param max_nesting_level the maximum nesting levels of structures within the
* KMIP node. If the nesting level is reached, E2BIG
* is returned.
* @param debug if true, debug messages are printed * @param debug if true, debug messages are printed
* *
* @returns 0 in case of success, or a negative errno value * @returns 0 in case of success, or a negative errno value
*/ */
int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node, int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node,
bool debug) size_t max_nesting_level, bool debug)
{ {
unsigned char padding[KMIP_TTLV_BLOCK_LENGTH]; unsigned char padding[KMIP_TTLV_BLOCK_LENGTH];
unsigned char ttlv[KMIP_TTLV_HEADER_LENGTH]; unsigned char ttlv[KMIP_TTLV_HEADER_LENGTH];
@@ -44,6 +47,9 @@ int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node,
uint64_t int64; uint64_t int64;
int rc; int rc;
if (max_nesting_level == 0)
return -E2BIG;
if (bio == NULL || node == NULL) if (bio == NULL || node == NULL)
return -EINVAL; return -EINVAL;
@@ -167,7 +173,8 @@ int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node,
switch (n->type) { switch (n->type) {
case KMIP_TYPE_STRUCTURE: case KMIP_TYPE_STRUCTURE:
while (value_len > 0) { while (value_len > 0) {
rc = kmip_decode_ttlv(bio, &value_len, &e, debug); rc = kmip_decode_ttlv(bio, &value_len, &e,
max_nesting_level - 1, debug);
if (rc != 0) { if (rc != 0) {
kmip_debug(debug, "kmip_decode_ttlv failed: " kmip_debug(debug, "kmip_decode_ttlv failed: "
"rc: %d", rc); "rc: %d", rc);

View File

@@ -28,12 +28,16 @@
* @param parent the parent node or NULL if no parent exists. * @param parent the parent node or NULL if no parent exists.
* @param node On return: the decoded node.The newly allocated * @param node On return: the decoded node.The newly allocated
* node has a reference count of 1. * node has a reference count of 1.
* @param max_nesting_level the maximum nesting levels of structures within the
* KMIP node. If the nesting level is reached, E2BIG
* is returned.
* @param debug if true, debug messages are printed * @param debug if true, debug messages are printed
* *
* @returns 0 in case of success, or a negative errno value * @returns 0 in case of success, or a negative errno value
*/ */
int kmip_decode_xml(const xmlNode *xml, struct kmip_node *parent, int kmip_decode_xml(const xmlNode *xml, struct kmip_node *parent,
struct kmip_node **node, bool debug) struct kmip_node **node, size_t max_nesting_level,
bool debug)
{ {
char *tag_attr = NULL, *name_attr = NULL, *type_attr = NULL; char *tag_attr = NULL, *name_attr = NULL, *type_attr = NULL;
enum kmip_tag tag, v1_attr_tag = 0; enum kmip_tag tag, v1_attr_tag = 0;
@@ -44,6 +48,9 @@ int kmip_decode_xml(const xmlNode *xml, struct kmip_node *parent,
int64_t int64; int64_t int64;
int rc = 0, i; int rc = 0, i;
if (max_nesting_level == 0)
return -E2BIG;
if (xml == NULL || node == NULL) if (xml == NULL || node == NULL)
return -EINVAL; return -EINVAL;
@@ -122,7 +129,8 @@ int kmip_decode_xml(const xmlNode *xml, struct kmip_node *parent,
if (child->type != XML_ELEMENT_NODE) if (child->type != XML_ELEMENT_NODE)
continue; continue;
rc = kmip_decode_xml(child, n, &e, debug); rc = kmip_decode_xml(child, n, &e,
max_nesting_level - 1, debug);
if (rc != 0) { if (rc != 0) {
kmip_debug(debug, "Failed to parse child " kmip_debug(debug, "Failed to parse child "
"element %d", i); "element %d", i);