Files
s390-tools/libkmipclient/xml.c
Ingo Franzki 866324bcd2 libkmipclient: Fix possible NULL pointer dereference
Function gmtime() might return NULL which would then be passed to
strftime(). Return an error in case gmtime() return NULL.

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>
2026-07-20 15:25:58 +02:00

503 lines
12 KiB
C

/*
* libkmipclient - KMIP client library
*
* Copyright IBM Corp. 2021
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <endian.h>
#include <string.h>
#include "kmip.h"
#include "names.h"
#include "utils.h"
#define KMIP_XML_TTLV "TTLV"
#define KMIP_XML_TAG "tag"
#define KMIP_XML_NAME "name"
#define KMIP_XML_TYPE "type"
#define KMIP_XML_VALUE "value"
/**
* Decode a KMIP node from the data in the XML node using the XML encoding.
*
* @param xml the XML node to decode
* @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, 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;
char *tag_name, *value_attr = NULL;
struct kmip_node *n = NULL, *e;
uint64_t uint64;
xmlNode *child;
int64_t int64;
int rc = 0, i;
if (max_nesting_level == 0)
return -E2BIG;
if (xml == NULL || node == NULL)
return -EINVAL;
if (xml->type != XML_ELEMENT_NODE) {
kmip_debug(debug, "Invalid XML node type: %d", xml->type);
return -EINVAL;
}
n = calloc(1, sizeof(struct kmip_node));
if (n == NULL) {
kmip_debug(debug, "calloc failed");
return -ENOMEM;
}
n->ref_count = 1;
if (strcmp((char *)xml->name, KMIP_XML_TTLV) == 0) {
tag_attr = (char *)xmlGetProp(xml, (xmlChar *)KMIP_XML_TAG);
if (tag_attr == NULL) {
kmip_debug(debug, "Missing '%s' attribute in XML node",
KMIP_XML_TAG);
rc = -EBADMSG;
goto out;
}
tag_name = tag_attr;
} else {
tag_name = (char *)xml->name;
}
n->tag = kmip_tag_by_name_or_hex(tag_name);
if (n->tag == 0) {
kmip_debug(debug, "Unknown 'tag' in XML object: '%s'",
tag_name);
rc = -EBADMSG;
goto out;
}
name_attr = (char *)xmlGetProp(xml, (xmlChar *)KMIP_XML_NAME);
if (name_attr != NULL)
n->name = strdup(name_attr);
type_attr = (char *)xmlGetProp(xml, (xmlChar *)KMIP_XML_TYPE);
if (type_attr == NULL) {
n->type = KMIP_TYPE_STRUCTURE;
} else {
n->type = kmip_type_by_name_or_hex(type_attr);
if (n->type == 0) {
kmip_debug(debug, "Unknown 'type' in XML object: '%s'",
type_attr);
rc = -EBADMSG;
goto out;
}
}
value_attr = (char *)xmlGetProp(xml, (xmlChar *)KMIP_XML_VALUE);
if (n->type != KMIP_TYPE_STRUCTURE && value_attr == NULL) {
kmip_debug(debug, "Missing '%s' attribute in XML node",
KMIP_XML_VALUE);
rc = -EBADMSG;
goto out;
}
/*
* KMIP v1.x attribute values may be Enumerations or Integer Masks.
* To correctly decode them, we need to know the tag. This is contained
* in a Attribute Name node, which is an element of our parent node.
*/
if (n->tag == KMIP_TAG_ATTRIBUTE_VALUE)
v1_attr_tag = kmip_find_v1_attribute_name_tag(parent);
tag = (v1_attr_tag != 0 ? v1_attr_tag : n->tag);
kmip_debug(debug, "tag: 0x%x type: 0x%x", n->tag, n->type);
switch (n->type) {
case KMIP_TYPE_STRUCTURE:
for (child = xml->children, i = 0; child != NULL;
child = child->next, i++) {
if (child->type != XML_ELEMENT_NODE)
continue;
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);
goto out;
}
rc = kmip_node_add_structure_element(n, e);
kmip_node_free(e);
if (rc != 0) {
kmip_debug(debug,
"kmip_node_structure_add_element "
"failed: rc: %d", rc);
goto out;
}
}
break;
case KMIP_TYPE_INTEGER:
case KMIP_TYPE_LONG_INTEGER:
case KMIP_TYPE_DATE_TIME_EXTENDED:
if (n->type == KMIP_TYPE_INTEGER &&
kmip_is_tag_mask(tag)) {
rc = kmip_parse_mask(tag, value_attr, ' ', &int64);
if (rc != 0) {
kmip_debug(debug, "Failed to parse "
"mask string '%s'", value_attr);
goto out;
}
} else {
rc = kmip_parse_decimal_int(value_attr, &int64);
if (rc != 0) {
kmip_debug(debug, "Failed to parse "
"decimal string '%s'", value_attr);
goto out;
}
}
switch (n->type) {
case KMIP_TYPE_INTEGER:
n->integer_value = int64;
break;
case KMIP_TYPE_LONG_INTEGER:
n->long_value = int64;
break;
case KMIP_TYPE_DATE_TIME_EXTENDED:
n->date_time_ext_value = int64;
break;
default:
break;
}
break;
case KMIP_TYPE_INTERVAL:
rc = kmip_parse_decimal_uint(value_attr, &uint64);
if (rc != 0) {
kmip_debug(debug, "Failed to parse "
"decimal string '%s'", value_attr);
goto out;
}
n->interval_value = uint64;
break;
case KMIP_TYPE_BIG_INTEGER:
rc = kmip_parse_bignum(value_attr, false,
&n->big_integer_value);
if (rc != 0) {
kmip_debug(debug,
"Failed to parse bignum string '%s'",
value_attr);
goto out;
}
break;
case KMIP_TYPE_ENUMERATION:
rc = kmip_enum_value_by_tag_name_or_hex(tag, value_attr,
&n->enumeration_value);
if (rc != 0) {
kmip_debug(debug,
"Failed to parse enumeration '%s'",
value_attr);
goto out;
}
break;
case KMIP_TYPE_BOOLEAN:
n->boolean_value = (strcmp(value_attr, "true") == 0 ||
strcmp(value_attr, "1") == 0);
break;
case KMIP_TYPE_TEXT_STRING:
n->text_value = strdup(value_attr);
if (n->text_value == NULL) {
rc = -ENOMEM;
goto out;
}
n->length = strlen(n->text_value);
break;
case KMIP_TYPE_BYTE_STRING:
rc = kmip_parse_hex(value_attr, false, &n->bytes_value,
&n->length);
if (rc != 0) {
kmip_debug(debug,
"Failed to parse hex string '%s'",
value_attr);
goto out;
}
break;
case KMIP_TYPE_DATE_TIME:
rc = kmip_parse_timestamp(value_attr, &n->date_time_value);
if (rc != 0) {
kmip_debug(debug,
"Failed to parse time stamp '%s'",
value_attr);
goto out;
}
break;
default:
kmip_debug(debug, "unknown type: 0x%x", n->type);
rc = -EBADMSG;
goto out;
}
*node = n;
rc = 0;
out:
if (rc != 0 && n != NULL)
kmip_node_free(n);
if (tag_attr != NULL)
xmlFree(tag_attr);
if (name_attr != NULL)
xmlFree(name_attr);
if (type_attr != NULL)
xmlFree(type_attr);
if (value_attr != NULL)
xmlFree(value_attr);
return rc;
}
/**
* Encode a KMIP node into an XML node using the XML encoding.
*
* @param node the node to encode
* @param xml On return: the XML node
* @param debug if true, debug messages are printed
*
* @returns 0 in case of success, or a negative errno value
*/
int kmip_encode_xml(const struct kmip_node *node, xmlNode **xml, bool debug)
{
enum kmip_tag tag, v1_attr_tag = 0;
xmlNode *ret_xml = NULL, *elem_xml;
struct kmip_node *element;
const char *tag_name;
char tmp_str[50];
const char *str;
struct tm *tm;
xmlAttr *attr;
char *tmp;
int rc;
if (node == NULL || xml == NULL)
return -EINVAL;
kmip_debug(debug, "tag: 0x%x type: 0x%x", node->tag, node->type);
tag_name = kmip_tag_name_by_tag(node->tag);
if (tag_name != NULL)
ret_xml = xmlNewNode(NULL, (xmlChar *)tag_name);
else
ret_xml = xmlNewNode(NULL, (xmlChar *)KMIP_XML_TTLV);
if (ret_xml == NULL) {
kmip_debug(debug, "Failed to allocate a XML node");
return -ENOMEM;
}
if (tag_name == NULL) {
sprintf(tmp_str, "0x%06x", node->tag);
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_TAG,
(xmlChar *)tmp_str);
if (attr == NULL) {
kmip_debug(debug,
"Failed to add '%s' attribute to XML node",
KMIP_XML_TAG);
rc = -ENOMEM;
goto out;
}
if (node->name != NULL) {
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_NAME,
(xmlChar *)node->name);
if (attr == NULL) {
kmip_debug(debug, "Failed to add '%s' "
"attribute to XML node",
KMIP_XML_NAME);
rc = -ENOMEM;
goto out;
}
}
}
if (node->type != KMIP_TYPE_STRUCTURE) {
str = kmip_type_name_by_type(node->type);
if (str == NULL) {
kmip_debug(debug, "unknown type 0x%x", node->type);
rc = -EINVAL;
goto out;
}
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_TYPE,
(xmlChar *)str);
if (attr == NULL) {
kmip_debug(debug,
"Failed to add '%s' attribute to XML node",
KMIP_XML_TYPE);
rc = -ENOMEM;
goto out;
}
}
/*
* KMIP v1.x attribute values may be Enumerations or Integer Masks.
* To correctly encode them, we need to know the tag. This is contained
* in a Attribute Name node, which is an element of our parent node.
*/
if (node->tag == KMIP_TAG_ATTRIBUTE_VALUE)
v1_attr_tag = kmip_find_v1_attribute_name_tag(node->parent);
tag = (v1_attr_tag != 0 ? v1_attr_tag : node->tag);
switch (node->type) {
case KMIP_TYPE_STRUCTURE:
element = node->structure_value;
while (element != NULL) {
rc = kmip_encode_xml(element, &elem_xml, debug);
if (rc != 0) {
kmip_debug(debug, "kmip_encode_xml failed");
goto out;
}
if (xmlAddChild(ret_xml, elem_xml) == NULL) {
kmip_debug(debug, "xmlAddChild failed");
rc = -EIO;
goto out;
}
element = element->next;
}
attr = NULL;
break;
case KMIP_TYPE_INTEGER:
if (kmip_is_tag_mask(tag) && node->integer_value != 0) {
rc = kmip_format_mask(tag, node->integer_value,
' ', &tmp);
if (rc != 0) {
kmip_debug(debug, "kmip_format_mask failed");
goto out;
}
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE,
(xmlChar *)tmp);
free(tmp);
} else {
sprintf(tmp_str, "%d", node->integer_value);
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE,
(xmlChar *)tmp_str);
}
break;
case KMIP_TYPE_INTERVAL:
sprintf(tmp_str, "%u", node->interval_value);
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE,
(xmlChar *)tmp_str);
break;
case KMIP_TYPE_LONG_INTEGER:
sprintf(tmp_str, "%ld", node->long_value);
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE,
(xmlChar *)tmp_str);
break;
case KMIP_TYPE_BIG_INTEGER:
rc = kmip_format_bignum(node->big_integer_value, false, &tmp);
if (rc != 0) {
kmip_debug(debug, "kmip_format_bignum failed");
goto out;
}
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE,
(xmlChar *)tmp);
free(tmp);
break;
case KMIP_TYPE_ENUMERATION:
str = kmip_enum_name_by_tag_value(tag, node->enumeration_value);
if (str != NULL) {
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE,
(xmlChar *)str);
} else {
sprintf(tmp_str, "0x%08x", node->enumeration_value);
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE,
(xmlChar *)tmp_str);
}
break;
case KMIP_TYPE_BOOLEAN:
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE,
(xmlChar *)(node->boolean_value ?
"true" : "false"));
break;
case KMIP_TYPE_TEXT_STRING:
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE,
(xmlChar *)node->text_value);
break;
case KMIP_TYPE_BYTE_STRING:
rc = kmip_format_hex(node->bytes_value, node->length,
false, &tmp);
if (rc != 0) {
kmip_debug(debug, "kmip_format_hex_long failed");
goto out;
}
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE,
(xmlChar *)tmp);
free(tmp);
break;
case KMIP_TYPE_DATE_TIME:
tm = gmtime((time_t *)&node->date_time_value);
if (tm == NULL) {
rc = -EINVAL;
goto out;
}
strftime(tmp_str, sizeof(tmp_str), KMIP_ISO8601_TIMESTAMP_UTC,
tm);
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE,
(xmlChar *)tmp_str);
break;
case KMIP_TYPE_DATE_TIME_EXTENDED:
sprintf(tmp_str, "%ld", node->date_time_ext_value);
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE,
(xmlChar *)tmp_str);
break;
default:
kmip_debug(debug, "unknown type: 0x%x", node->type);
rc = -EINVAL;
goto out;
}
if (attr == NULL && node->type != KMIP_TYPE_STRUCTURE) {
kmip_debug(debug, "Failed to add '%s' "
"attribute to XML node",
KMIP_XML_VALUE);
rc = -ENOMEM;
goto out;
}
rc = 0;
*xml = ret_xml;
out:
if (rc != 0)
xmlFreeNode(ret_xml);
return rc;
}