Files
s390-tools/libkmipclient/json.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

661 lines
16 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_JSON_TAG "tag"
#define KMIP_JSON_NAME "name"
#define KMIP_JSON_TYPE "type"
#define KMIP_JSON_VALUE "value"
/**
* Decode a KMIP node from the data in a JSON object using the JSON encoding.
*
* @param obj the JSON object 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_json(const json_object *obj, struct kmip_node *parent,
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;
enum json_type value_type;
struct kmip_node *n, *e;
const char *str;
int rc, num, i;
int64_t int64;
if (max_nesting_level == 0)
return -E2BIG;
if (obj == NULL || node == NULL)
return -EINVAL;
if (!json_object_is_type(obj, json_type_object)) {
kmip_debug(debug, "Object is not a JSON object");
return -EINVAL;
}
n = calloc(1, sizeof(struct kmip_node));
if (n == NULL) {
kmip_debug(debug, "calloc failed");
return -ENOMEM;
}
n->ref_count = 1;
tag_obj = json_object_object_get(obj, KMIP_JSON_TAG);
if (tag_obj == NULL ||
!json_object_is_type(tag_obj, json_type_string)) {
kmip_debug(debug, "Missing or invalid '%s' in JSON object",
KMIP_JSON_TAG);
rc = -EBADMSG;
goto out;
}
str = json_object_get_string(tag_obj);
n->tag = kmip_tag_by_name_or_hex(str);
if (n->tag == 0) {
kmip_debug(debug, "Unknown 'tag' in JSON object: '%s'", str);
rc = -EBADMSG;
goto out;
}
name_obj = json_object_object_get(obj, KMIP_JSON_NAME);
if (name_obj != NULL) {
if (!json_object_is_type(name_obj, json_type_string)) {
kmip_debug(debug, "Invalid '%s' in JSON object",
KMIP_JSON_NAME);
rc = -EBADMSG;
goto out;
}
n->name = strdup(json_object_get_string(name_obj));
}
type_obj = json_object_object_get(obj, KMIP_JSON_TYPE);
if (type_obj == NULL) {
n->type = KMIP_TYPE_STRUCTURE;
} else {
if (!json_object_is_type(type_obj, json_type_string)) {
kmip_debug(debug,
"Missing or invalid '%s' in JSON object",
KMIP_JSON_TYPE);
rc = -EBADMSG;
goto out;
}
str = json_object_get_string(type_obj);
n->type = kmip_type_by_name_or_hex(str);
if (n->type == 0) {
kmip_debug(debug, "Unknown 'type' in JSON object: '%s'",
str);
rc = -EBADMSG;
goto out;
}
}
value_obj = json_object_object_get(obj, KMIP_JSON_VALUE);
if (value_obj == NULL) {
kmip_debug(debug, "Missing '%s' in JSON object",
KMIP_JSON_VALUE);
rc = -EBADMSG;
goto out;
}
value_type = json_object_get_type(value_obj);
/*
* 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 value_type: %d,", n->tag,
n->type, value_type);
switch (n->type) {
case KMIP_TYPE_STRUCTURE:
switch (value_type) {
case json_type_null:
break;
case json_type_array:
num = json_object_array_length(value_obj);
for (i = 0; i < num; i++) {
rc = kmip_decode_json(
json_object_array_get_idx(value_obj, i),
n, &e, max_nesting_level - 1, debug);
if (rc != 0) {
kmip_debug(debug, "Failed to parse "
"array 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;
default:
kmip_debug(debug, "Invalid JSON type %d for node type "
"0x%x", value_type, n->type);
rc = -EBADMSG;
goto out;
}
break;
case KMIP_TYPE_INTEGER:
case KMIP_TYPE_LONG_INTEGER:
switch (value_type) {
case json_type_int:
case json_type_double:
int64 = json_object_get_int64(value_obj);
break;
case json_type_string:
str = json_object_get_string(value_obj);
if (n->type == KMIP_TYPE_INTEGER &&
kmip_is_tag_mask(tag)) {
rc = kmip_parse_mask(tag, str, '|', &int64);
if (rc != 0) {
kmip_debug(debug, "Failed to parse "
"mask string '%s'", str);
goto out;
}
} else {
rc = kmip_parse_hex_int(str, &int64);
if (rc != 0) {
kmip_debug(debug, "Failed to parse "
"hex string '%s'", str);
goto out;
}
}
break;
default:
kmip_debug(debug, "Invalid JSON type %d for node type "
"0x%x", value_type, n->type);
rc = -EBADMSG;
goto out;
}
if (n->type == KMIP_TYPE_INTEGER)
n->integer_value = int64;
else
n->long_value = int64;
break;
case KMIP_TYPE_INTERVAL:
switch (value_type) {
case json_type_int:
case json_type_double:
n->interval_value = json_object_get_int64(value_obj);
break;
case json_type_string:
str = json_object_get_string(value_obj);
rc = kmip_parse_hex_int(str, &int64);
if (rc != 0) {
kmip_debug(debug, "Failed to parse "
"hex string '%s'", str);
goto out;
}
n->interval_value = int64;
break;
default:
kmip_debug(debug, "Invalid JSON type %d for node type "
"0x%x", value_type, n->type);
rc = -EBADMSG;
goto out;
}
break;
case KMIP_TYPE_BIG_INTEGER:
switch (value_type) {
case json_type_int:
case json_type_double:
int64 = htobe64(json_object_get_int64(value_obj));
rc = kmip_decode_bignum((const unsigned char *)&int64,
sizeof(int64),
&n->big_integer_value);
if (rc != 0) {
kmip_debug(debug, "kmip_decode_bignum failed");
goto out;
}
break;
case json_type_string:
str = json_object_get_string(value_obj);
rc = kmip_parse_bignum(str, true,
&n->big_integer_value);
if (rc != 0) {
kmip_debug(debug,
"Failed to parse bignum string '%s'",
str);
goto out;
}
break;
default:
kmip_debug(debug, "Invalid JSON type %d for node type "
"0x%x", value_type, n->type);
rc = -EBADMSG;
goto out;
}
break;
case KMIP_TYPE_ENUMERATION:
switch (value_type) {
case json_type_int:
case json_type_double:
n->enumeration_value = json_object_get_int64(value_obj);
break;
case json_type_string:
str = json_object_get_string(value_obj);
rc = kmip_enum_value_by_tag_name_or_hex(tag, str,
&n->enumeration_value);
if (rc != 0) {
kmip_debug(debug,
"Failed to parse enumeration '%s'",
str);
goto out;
}
break;
default:
kmip_debug(debug, "Invalid JSON type %d for node type "
"0x%x", value_type, n->type);
rc = -EBADMSG;
goto out;
}
break;
case KMIP_TYPE_BOOLEAN:
switch (value_type) {
case json_type_boolean:
n->boolean_value = json_object_get_boolean(value_obj);
break;
case json_type_string:
str = json_object_get_string(value_obj);
rc = kmip_parse_hex_int(str, &int64);
if (rc != 0) {
kmip_debug(debug,
"Failed to parse hex string '%s'",
str);
goto out;
}
n->boolean_value = (int64 != 0);
break;
default:
kmip_debug(debug, "Invalid JSON type %d for node type "
"0x%x", value_type, n->type);
rc = -EBADMSG;
goto out;
}
break;
case KMIP_TYPE_TEXT_STRING:
switch (value_type) {
case json_type_string:
n->text_value = strdup(
json_object_get_string(value_obj));
if (n->text_value == NULL) {
rc = -ENOMEM;
goto out;
}
n->length = strlen(n->text_value);
break;
default:
kmip_debug(debug, "Invalid JSON type %d for node type "
"0x%x", value_type, n->type);
rc = -EBADMSG;
goto out;
}
break;
case KMIP_TYPE_BYTE_STRING:
switch (value_type) {
case json_type_string:
str = json_object_get_string(value_obj);
rc = kmip_parse_hex(str, false, &n->bytes_value,
&n->length);
if (rc != 0) {
kmip_debug(debug,
"Failed to parse hex string '%s'",
str);
goto out;
}
break;
default:
kmip_debug(debug, "Invalid JSON type %d for node type "
"0x%x", value_type, n->type);
rc = -EBADMSG;
goto out;
}
break;
case KMIP_TYPE_DATE_TIME:
switch (value_type) {
case json_type_int:
case json_type_double:
n->date_time_value = json_object_get_int64(value_obj);
break;
case json_type_string:
str = json_object_get_string(value_obj);
rc = kmip_parse_timestamp(str, &n->date_time_value);
if (rc != 0) {
kmip_debug(debug,
"Failed to parse time stamp '%s'",
str);
goto out;
}
break;
default:
kmip_debug(debug, "Invalid JSON type %d for node type "
"0x%x", value_type, n->type);
rc = -EBADMSG;
goto out;
}
break;
case KMIP_TYPE_DATE_TIME_EXTENDED:
switch (value_type) {
case json_type_int:
case json_type_double:
n->date_time_ext_value =
json_object_get_int64(value_obj);
break;
case json_type_string:
str = json_object_get_string(value_obj);
rc = kmip_parse_hex_int(str, &int64);
if (rc != 0) {
kmip_debug(debug,
"Failed to parse hex string '%s'",
str);
goto out;
}
n->date_time_ext_value = int64;
break;
default:
kmip_debug(debug, "Invalid JSON type %d for node type "
"0x%x", value_type, n->type);
rc = -EBADMSG;
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)
kmip_node_free(n);
return rc;
}
/**
* Encode a KMIP node into a JSON object using the JSON encoding.
*
* @param node the node to encode
* @param obj On return: the JSON object
* @param debug if true, debug messages are printed
*
* @returns 0 in case of success, or a negative errno value
*/
int kmip_encode_json(const struct kmip_node *node, json_object **obj,
bool debug)
{
json_object *ret_obj = NULL, *memb_obj, *elem_obj;
enum kmip_tag tag, v1_attr_tag = 0;
struct kmip_node *element;
char outstr[200] = { 0 };
const char *str;
int64_t int64;
struct tm *tm;
char *tmp;
char *s;
int rc;
if (node == NULL || obj == NULL)
return -EINVAL;
kmip_debug(debug, "tag: 0x%x type: 0x%x", node->tag, node->type);
ret_obj = json_object_new_object();
if (ret_obj == NULL) {
kmip_debug(debug, "Failed to allocate a JSON object");
return -ENOMEM;
}
memb_obj = json_object_new_string(
kmip_tag_name_or_hex_by_tag(node->tag,
outstr));
if (memb_obj == NULL) {
kmip_debug(debug, "Failed to build JSON object for tag");
rc = -ENOMEM;
goto out;
}
rc = json_object_object_add(ret_obj, KMIP_JSON_TAG, memb_obj);
if (rc != 0) {
kmip_debug(debug, "Failed to add JSON object for tag");
rc = -EIO;
goto out;
}
if (node->name != NULL) {
memb_obj = json_object_new_string(node->name);
if (memb_obj == NULL) {
kmip_debug(debug,
"Failed to build JSON object for name");
rc = -ENOMEM;
goto out;
}
rc = json_object_object_add(ret_obj, KMIP_JSON_NAME, memb_obj);
if (rc != 0) {
kmip_debug(debug, "Failed to add JSON object for name");
rc = -EIO;
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;
}
memb_obj = json_object_new_string(str);
if (memb_obj == NULL) {
kmip_debug(debug,
"Failed to build JSON object for type");
rc = -ENOMEM;
goto out;
}
rc = json_object_object_add(ret_obj, KMIP_JSON_TYPE, memb_obj);
if (rc != 0) {
kmip_debug(debug, "Failed to add JSON object for type");
rc = -EIO;
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:
memb_obj = json_object_new_array();
if (memb_obj == NULL) {
kmip_debug(debug,
"Failed to build JSON object for value array");
rc = -ENOMEM;
goto out;
}
element = node->structure_value;
while (element != NULL) {
rc = kmip_encode_json(element, &elem_obj, debug);
if (rc != 0) {
kmip_debug(debug, "kmip_encode_json failed");
goto out;
}
rc = json_object_array_add(memb_obj, elem_obj);
if (rc != 0) {
kmip_debug(debug,
"json_object_array_add failed");
rc = EIO;
goto out;
}
element = element->next;
}
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;
}
memb_obj = json_object_new_string(tmp);
free(tmp);
} else {
memb_obj = json_object_new_int(node->integer_value);
}
break;
case KMIP_TYPE_INTERVAL:
memb_obj = json_object_new_int(node->interval_value);
break;
case KMIP_TYPE_LONG_INTEGER:
case KMIP_TYPE_DATE_TIME_EXTENDED:
if (node->type == KMIP_TYPE_LONG_INTEGER)
int64 = node->long_value;
else
int64 = node->date_time_ext_value;
/* any values >= 2^52 must be represented as hex strings */
if (int64 < 4503599627370496 &&
int64 > -4503599627370496) {
memb_obj = json_object_new_int64(int64);
} else {
rc = kmip_format_hex((const unsigned char *)&int64,
sizeof(int64), true, &tmp);
if (rc != 0) {
kmip_debug(debug, "kmip_format_hex failed");
goto out;
}
memb_obj = json_object_new_string(tmp);
free(tmp);
}
break;
case KMIP_TYPE_BIG_INTEGER:
rc = kmip_format_bignum(node->big_integer_value, true, &s);
if (rc != 0) {
kmip_debug(debug, "kmip_format_bignum failed");
goto out;
}
memb_obj = json_object_new_string(s);
free(s);
break;
case KMIP_TYPE_ENUMERATION:
str = kmip_enum_name_by_tag_value(tag, node->enumeration_value);
if (str != NULL)
memb_obj = json_object_new_string(str);
else
memb_obj = json_object_new_int(node->enumeration_value);
break;
case KMIP_TYPE_BOOLEAN:
memb_obj = json_object_new_boolean(node->boolean_value);
break;
case KMIP_TYPE_TEXT_STRING:
memb_obj = json_object_new_string(node->text_value);
break;
case KMIP_TYPE_BYTE_STRING:
rc = kmip_format_hex(node->bytes_value, node->length,
false, &s);
if (rc != 0) {
kmip_debug(debug, "kmip_format_hex_long failed");
goto out;
}
memb_obj = json_object_new_string(s);
free(s);
break;
case KMIP_TYPE_DATE_TIME:
tm = gmtime((time_t *)&node->date_time_value);
if (tm == NULL) {
rc = -EINVAL;
goto out;
}
strftime(outstr, sizeof(outstr), KMIP_ISO8601_TIMESTAMP_UTC,
tm);
memb_obj = json_object_new_string(outstr);
break;
default:
kmip_debug(debug, "unknown type: 0x%x", node->type);
rc = -EINVAL;
goto out;
}
if (memb_obj == NULL) {
rc = -ENOMEM;
goto out;
}
rc = json_object_object_add(ret_obj, KMIP_JSON_VALUE, memb_obj);
if (rc != 0) {
kmip_debug(debug, "Failed to add JSON object for value");
rc = -EIO;
goto out;
}
rc = 0;
*obj = ret_obj;
out:
if (rc != 0)
json_object_put(ret_obj);
return rc;
}