From 5fdeaab3d09c71c899ba13b29629b3110ea0443e Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Tue, 30 Jun 2026 10:19:17 +0200 Subject: [PATCH] libkmipclient: Limit the nesting level of KMIP STRUCTURE recursion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Finn Callies Signed-off-by: Jan Höppner --- libkmipclient/https.c | 8 +++++--- libkmipclient/json.c | 11 +++++++++-- libkmipclient/kmip.h | 12 ++++++++---- libkmipclient/tls.c | 3 ++- libkmipclient/ttlv.c | 11 +++++++++-- libkmipclient/xml.c | 12 ++++++++++-- 6 files changed, 43 insertions(+), 14 deletions(-) diff --git a/libkmipclient/https.c b/libkmipclient/https.c index 2cea1276..c01e13f1 100644 --- a/libkmipclient/https.c +++ b/libkmipclient/https.c @@ -735,7 +735,8 @@ int kmip_connection_https_perform(struct kmip_connection *conn, switch (conn->config.encoding) { case KMIP_ENCODING_TTLV: rc = kmip_decode_ttlv(write_cb.ttlv.resp_mem_bio, NULL, - response, debug); + response, KMIP_DECODE_MAX_NESTING_LEVEL, + debug); if (rc != 0) { kmip_debug(debug, "kmip_decode_ttlv failed"); 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, - debug); + KMIP_DECODE_MAX_NESTING_LEVEL, debug); if (rc != 0) { kmip_debug(debug, "kmip_decode_json failed"); goto out; @@ -768,7 +769,8 @@ int kmip_connection_https_perform(struct kmip_connection *conn, rc = kmip_decode_xml(xmlDocGetRootElement( write_cb.xml.ctx->myDoc), - NULL, response, debug); + NULL, response, + KMIP_DECODE_MAX_NESTING_LEVEL, debug); if (rc != 0) { kmip_debug(debug, "kmip_decode_xml failed"); goto out; diff --git a/libkmipclient/json.c b/libkmipclient/json.c index 78337192..53beb236 100644 --- a/libkmipclient/json.c +++ b/libkmipclient/json.c @@ -27,12 +27,16 @@ * @param parent the parent node or NULL if no parent exists. * @param node On return: the decoded node. The newly allocated * 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 * * @returns 0 in case of success, or a negative errno value */ 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; 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; int64_t int64; + if (max_nesting_level == 0) + return -E2BIG; + if (obj == NULL || node == NULL) return -EINVAL; @@ -138,7 +145,7 @@ int kmip_decode_json(const json_object *obj, struct kmip_node *parent, for (i = 0; i < num; i++) { rc = kmip_decode_json( json_object_array_get_idx(value_obj, i), - n, &e, debug); + n, &e, max_nesting_level - 1, debug); if (rc != 0) { kmip_debug(debug, "Failed to parse " "array element %d", i); diff --git a/libkmipclient/kmip.h b/libkmipclient/kmip.h index 3344cb01..158123e0 100644 --- a/libkmipclient/kmip.h +++ b/libkmipclient/kmip.h @@ -94,19 +94,23 @@ int kmip_connection_https_perform(struct kmip_connection *connection, bool debug); 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, - bool debug); + size_t max_nesting_level, bool debug); int kmip_encode_ttlv(struct kmip_node *node, BIO *bio, size_t *size, bool debug); 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, bool debug); 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); #endif diff --git a/libkmipclient/tls.c b/libkmipclient/tls.c index 682ad1c4..d42d1fec 100644 --- a/libkmipclient/tls.c +++ b/libkmipclient/tls.c @@ -493,7 +493,8 @@ int kmip_connection_tls_perform(struct kmip_connection *conn, kmip_debug(debug, "%lu bytes sent", size); /* 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) { kmip_debug(debug, "kmip_decode_ttlv failed"); goto out; diff --git a/libkmipclient/ttlv.c b/libkmipclient/ttlv.c index 9c6702b1..fe9815de 100644 --- a/libkmipclient/ttlv.c +++ b/libkmipclient/ttlv.c @@ -28,12 +28,15 @@ * as many bytes as needed. * @param node On return: the decoded node. The newly allocated * 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 * * @returns 0 in case of success, or a negative errno value */ 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 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; int rc; + if (max_nesting_level == 0) + return -E2BIG; + if (bio == NULL || node == NULL) return -EINVAL; @@ -167,7 +173,8 @@ int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node, switch (n->type) { case KMIP_TYPE_STRUCTURE: 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) { kmip_debug(debug, "kmip_decode_ttlv failed: " "rc: %d", rc); diff --git a/libkmipclient/xml.c b/libkmipclient/xml.c index 6f2bacf6..22766085 100644 --- a/libkmipclient/xml.c +++ b/libkmipclient/xml.c @@ -28,12 +28,16 @@ * @param parent the parent node or NULL if no parent exists. * @param node On return: the decoded node.The newly allocated * 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 * * @returns 0 in case of success, or a negative errno value */ 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; 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; int rc = 0, i; + if (max_nesting_level == 0) + return -E2BIG; + if (xml == NULL || node == NULL) return -EINVAL; @@ -122,7 +129,8 @@ int kmip_decode_xml(const xmlNode *xml, struct kmip_node *parent, if (child->type != XML_ELEMENT_NODE) continue; - rc = kmip_decode_xml(child, n, &e, debug); + rc = kmip_decode_xml(child, n, &e, + max_nesting_level - 1, debug); if (rc != 0) { kmip_debug(debug, "Failed to parse child " "element %d", i);