mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Check the right pointer after strdup. 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>
1595 lines
40 KiB
C
1595 lines
40 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.
|
|
*/
|
|
|
|
#define _XOPEN_SOURCE
|
|
#define _DEFAULT_SOURCE
|
|
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <err.h>
|
|
|
|
#include <openssl/bn.h>
|
|
#include <openssl/evp.h>
|
|
|
|
#include "kmip.h"
|
|
#include "utils.h"
|
|
|
|
void __attribute__ ((constructor)) kmip_init(void);
|
|
void __attribute__ ((destructor)) kmip_exit(void);
|
|
|
|
/**
|
|
* Constructs a new KMIP node with the specified tag and type, and an optional
|
|
* name. The newly allocated node has a reference count of 1.
|
|
*
|
|
* @param tag the tag of the new node
|
|
* @param name Optional: the name of the node (only used with JSON
|
|
* or XML encoding). Can be NULL.
|
|
* @param type the type of the new node
|
|
*
|
|
* @returns the allocated node, or NULL in case of an error
|
|
*/
|
|
static struct kmip_node *kmip_node_new(enum kmip_tag tag, const char *name,
|
|
enum kmip_type type)
|
|
{
|
|
struct kmip_node *node;
|
|
|
|
node = calloc(1, sizeof(struct kmip_node));
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
node->ref_count = 1;
|
|
node->tag = tag;
|
|
node->type = type;
|
|
|
|
if (name != NULL) {
|
|
node->name = strdup(name);
|
|
if (node->name == NULL) {
|
|
free(node);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Returns the tag of a KMIP node
|
|
*
|
|
* @param node the KMIP node
|
|
*
|
|
* @returns the tag
|
|
*/
|
|
enum kmip_tag kmip_node_get_tag(const struct kmip_node *node)
|
|
{
|
|
if (node == NULL)
|
|
return 0;
|
|
|
|
return node->tag;
|
|
}
|
|
|
|
/**
|
|
* Returns the type of a KMIP node
|
|
*
|
|
* @param node the KMIP node
|
|
*
|
|
* @returns the type
|
|
*/
|
|
enum kmip_type kmip_node_get_type(const struct kmip_node *node)
|
|
{
|
|
if (node == NULL)
|
|
return 0;
|
|
|
|
return node->type;
|
|
}
|
|
|
|
/**
|
|
* Returns the name of a KMIP node
|
|
*
|
|
* @param node the KMIP node
|
|
*
|
|
* @returns a copy of the name. The caller must free the returnd string.
|
|
*/
|
|
char *kmip_node_get_name(const struct kmip_node *node)
|
|
{
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
if (node->name == NULL)
|
|
return NULL;
|
|
|
|
return strdup(node->name);
|
|
}
|
|
|
|
/**
|
|
* Constructs a new KMIP node of type structure with the specified tag, and an
|
|
* optional name, and the elements. The reference count of each added element is
|
|
* increased.
|
|
*
|
|
* @param tag the tag of the new node
|
|
* @param name Optional: the name of the node (only used with JSON
|
|
* or XML encoding). Can be NULL.
|
|
* @param num_elements the number of elements to add
|
|
* @param elements the array elements to add.
|
|
*
|
|
* @returns the allocated node, or NULL in case of an error
|
|
*/
|
|
struct kmip_node *kmip_node_new_structure(enum kmip_tag tag, const char *name,
|
|
unsigned int num_elements,
|
|
struct kmip_node **elements)
|
|
{
|
|
struct kmip_node *node;
|
|
int rc;
|
|
|
|
node = kmip_node_new(tag, name, KMIP_TYPE_STRUCTURE);
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
rc = kmip_node_add_structure_elements(node, num_elements, elements);
|
|
if (rc != 0) {
|
|
kmip_node_free(node);
|
|
return NULL;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new KMIP node of type structure with the specified tag, and an
|
|
* optional name, and the elements. The reference count of each added element is
|
|
* increased.
|
|
*
|
|
* @param tag the tag of the new node
|
|
* @param name Optional: the name of the node (only used with JSON
|
|
* or XML encoding). Can be NULL.
|
|
* @param num_elements the number of elements following as variable args
|
|
* @param <element ...> the elements to add. Elements may be NULL, those
|
|
* are skipped
|
|
*
|
|
* @returns the allocated node, or NULL in case of an error
|
|
*/
|
|
struct kmip_node *kmip_node_new_structure_va(enum kmip_tag tag,
|
|
const char *name,
|
|
unsigned int num_elements, ...)
|
|
{
|
|
struct kmip_node *node, **elements = NULL;
|
|
unsigned int i;
|
|
va_list ap;
|
|
|
|
if (num_elements > 0) {
|
|
elements = calloc(num_elements, sizeof(struct kmip_node *));
|
|
if (elements == NULL)
|
|
return NULL;
|
|
}
|
|
|
|
va_start(ap, num_elements);
|
|
for (i = 0; i < num_elements; i++)
|
|
elements[i] = va_arg(ap, struct kmip_node *);
|
|
va_end(ap);
|
|
|
|
node = kmip_node_new_structure(tag, name, num_elements, elements);
|
|
|
|
if (elements != NULL)
|
|
free(elements);
|
|
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Add an element to a KMIP node (which must be of type KMIP_TYPE_STRUCTURE).
|
|
* The element is added as the last element. The reference count of the added
|
|
* element is increased.
|
|
*
|
|
* @param node the structure node to add the element to
|
|
* @param element the element to add
|
|
*
|
|
* @returns 0 in case of success, or a negative errno value
|
|
*/
|
|
int kmip_node_add_structure_element(struct kmip_node *node,
|
|
struct kmip_node *element)
|
|
{
|
|
if (node == NULL || element == NULL)
|
|
return -EINVAL;
|
|
|
|
return kmip_node_add_structure_elements(node, 1, &element);
|
|
}
|
|
|
|
/**
|
|
* Add elements to a KMIP node (which must be of type KMIP_TYPE_STRUCTURE).
|
|
* The elements are added after the last element. The reference count of the
|
|
* added elements is increased.
|
|
*
|
|
* @param node the structure node to add the element to
|
|
* @param num_elements the number of elements to add
|
|
* @param elements the array elements to add
|
|
*
|
|
* @returns 0 in case of success, or a negative errno value
|
|
*/
|
|
int kmip_node_add_structure_elements(struct kmip_node *node,
|
|
unsigned int num_elements,
|
|
struct kmip_node **elements)
|
|
{
|
|
struct kmip_node *element, *last;
|
|
unsigned int i;
|
|
|
|
if (node == NULL || (num_elements > 0 && elements == NULL))
|
|
return -EINVAL;
|
|
|
|
if (node->type != KMIP_TYPE_STRUCTURE)
|
|
return -EINVAL;
|
|
|
|
if (node->structure_value == NULL) {
|
|
last = NULL;
|
|
} else {
|
|
last = node->structure_value;
|
|
while (last->next != NULL)
|
|
last = last->next;
|
|
}
|
|
|
|
for (i = 0; i < num_elements; i++) {
|
|
element = elements[i];
|
|
if (element == NULL)
|
|
continue;
|
|
|
|
kmip_node_upref(element);
|
|
|
|
element->parent = node;
|
|
element->next = NULL;
|
|
|
|
if (last == NULL)
|
|
node->structure_value = element;
|
|
else
|
|
last->next = element;
|
|
|
|
last = element;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of elements of a KMIP node of type structure
|
|
*
|
|
* @param node the KMIP node
|
|
*
|
|
* @returns the number of elements, or 0 if the node is not of type structure
|
|
*/
|
|
unsigned int kmip_node_get_structure_element_count(const struct kmip_node *node)
|
|
{
|
|
struct kmip_node *element;
|
|
unsigned int i;
|
|
|
|
if (node == NULL)
|
|
return 0;
|
|
|
|
if (node->type != KMIP_TYPE_STRUCTURE)
|
|
return 0;
|
|
|
|
element = node->structure_value;
|
|
for (i = 0; element != NULL; i++)
|
|
element = element->next;
|
|
|
|
return i;
|
|
}
|
|
|
|
/**
|
|
* Returns an element of a KMIP node of type structure
|
|
*
|
|
* @param node the KMIP node
|
|
* @param index the index of the element to return
|
|
*
|
|
* @returns the element or NULL if no element is available at the specified
|
|
* index, or the node is not of type structure.
|
|
* The reference count of the returned element is increased. The caller must
|
|
* free the element via kmip_node_free() when no longer needed.
|
|
*/
|
|
struct kmip_node *kmip_node_get_structure_element_by_index(
|
|
const struct kmip_node *node,
|
|
unsigned int index)
|
|
{
|
|
struct kmip_node *element;
|
|
unsigned int i;
|
|
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
if (node->type != KMIP_TYPE_STRUCTURE)
|
|
return NULL;
|
|
|
|
element = node->structure_value;
|
|
for (i = 0; i < index && element != NULL; i++)
|
|
element = element->next;
|
|
|
|
if (element != NULL)
|
|
kmip_node_upref(element);
|
|
|
|
return element;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of elements of a KMIP node of type structure of a
|
|
* certain tag
|
|
*
|
|
* @param node the KMIP node
|
|
* @param tag the tag to find
|
|
*
|
|
* @returns the number of elements, or 0 if the node is not of type structure
|
|
*/
|
|
unsigned int kmip_node_get_structure_element_by_tag_count(
|
|
const struct kmip_node *node,
|
|
enum kmip_tag tag)
|
|
{
|
|
struct kmip_node *element;
|
|
unsigned int i;
|
|
|
|
if (node == NULL)
|
|
return 0;
|
|
|
|
if (node->type != KMIP_TYPE_STRUCTURE)
|
|
return 0;
|
|
|
|
element = node->structure_value;
|
|
for (i = 0; element != NULL; element = element->next) {
|
|
if (element->tag != tag)
|
|
continue;
|
|
i++;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
/**
|
|
* Find a structure element by its tag. If multiple elements with the matching
|
|
* tag are found, then the num'th one is returned.
|
|
*
|
|
* @param node the structure node to find the elements in
|
|
* @param tag the tag to find
|
|
* @param index the index of elements with the same tag to return.
|
|
*
|
|
* @returns the element node, or NULL if no element with the tag was found.
|
|
* The reference count of the returned element is increased. The caller must
|
|
* free the element via kmip_node_free() when no longer needed.
|
|
*/
|
|
struct kmip_node *kmip_node_get_structure_element_by_tag(
|
|
const struct kmip_node *node,
|
|
enum kmip_tag tag, unsigned int index)
|
|
{
|
|
struct kmip_node *e;
|
|
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
if (node->type != KMIP_TYPE_STRUCTURE)
|
|
return NULL;
|
|
|
|
e = node->structure_value;
|
|
while (e != NULL) {
|
|
if (e->tag == tag) {
|
|
if (index == 0) {
|
|
kmip_node_upref(e);
|
|
return e;
|
|
}
|
|
index--;
|
|
}
|
|
e = e->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new KMIP node of type integer with the specified tag, and an
|
|
* optional name, and the integer value
|
|
*
|
|
* @param tag the tag of the new node
|
|
* @param name Optional: the name of the node (only used with JSON
|
|
* or XML encoding). Can be NULL.
|
|
* @param value the value
|
|
*
|
|
* @returns the allocated node, or NULL in case of an error
|
|
*/
|
|
struct kmip_node *kmip_node_new_integer(enum kmip_tag tag, const char *name,
|
|
int32_t value)
|
|
{
|
|
struct kmip_node *node;
|
|
|
|
node = kmip_node_new(tag, name, KMIP_TYPE_INTEGER);
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
node->integer_value = value;
|
|
node->length = sizeof(int32_t);
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Returns the value of a KMIP node of type integer
|
|
*
|
|
* @param node the KMIP node
|
|
*
|
|
* @returns the value of the node, or 0 if the node is not of type integer
|
|
*/
|
|
int32_t kmip_node_get_integer(const struct kmip_node *node)
|
|
{
|
|
if (node == NULL)
|
|
return 0;
|
|
|
|
if (node->type != KMIP_TYPE_INTEGER)
|
|
return 0;
|
|
|
|
return node->integer_value;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new KMIP node of type long integer with the specified tag, and
|
|
* an optional name, and the long integer value
|
|
*
|
|
* @param tag the tag of the new node
|
|
* @param name Optional: the name of the node (only used with JSON
|
|
* or XML encoding). Can be NULL.
|
|
* @param value the value
|
|
*
|
|
* @returns the allocated node, or NULL in case of an error
|
|
*/
|
|
struct kmip_node *kmip_node_new_long(enum kmip_tag tag, const char *name,
|
|
int64_t value)
|
|
{
|
|
struct kmip_node *node;
|
|
|
|
node = kmip_node_new(tag, name, KMIP_TYPE_LONG_INTEGER);
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
node->long_value = value;
|
|
node->length = sizeof(int64_t);
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Returns the value of a KMIP node of type long integer
|
|
*
|
|
* @param node the KMIP node
|
|
*
|
|
* @returns the value of the node, or 0 if the node is not of type long integer
|
|
*/
|
|
int64_t kmip_node_get_long(const struct kmip_node *node)
|
|
{
|
|
if (node == NULL)
|
|
return 0;
|
|
|
|
if (node->type != KMIP_TYPE_LONG_INTEGER)
|
|
return 0;
|
|
|
|
return node->long_value;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new KMIP node of type big integer with the specified tag, and
|
|
* an optional name, and the big integer value
|
|
*
|
|
* @param tag the tag of the new node
|
|
* @param name Optional: the name of the node (only used with JSON
|
|
* or XML encoding). Can be NULL.
|
|
* @param value the value as OpenSSL BIGNUM (can be NULL)
|
|
*
|
|
* @returns the allocated node, or NULL in case of an error
|
|
*/
|
|
struct kmip_node *kmip_node_new_bigint(enum kmip_tag tag, const char *name,
|
|
const BIGNUM *value)
|
|
{
|
|
struct kmip_node *node;
|
|
|
|
node = kmip_node_new(tag, name, KMIP_TYPE_BIG_INTEGER);
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
if (value != NULL) {
|
|
node->big_integer_value = BN_dup(value);
|
|
node->length = kmip_encode_bignum_length(value);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Returns the value of a KMIP node of type big integer
|
|
*
|
|
* @param node the KMIP node
|
|
*
|
|
* @returns the value of the node, or NULL if the node is not of type big
|
|
* integer, or no BIGNUM is set. The returned BIGNUM still belongs to the node,
|
|
* and must not be freed by the caller. It is freed together with the node it
|
|
* was obtained from.
|
|
*/
|
|
const BIGNUM *kmip_node_get_bigint(const struct kmip_node *node)
|
|
{
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
if (node->type != KMIP_TYPE_BIG_INTEGER)
|
|
return NULL;
|
|
|
|
return node->big_integer_value;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new KMIP node of type enumeration with the specified tag, and
|
|
* an optional name, and the enumeration value
|
|
*
|
|
* @param tag the tag of the new node
|
|
* @param name Optional: the name of the node (only used with JSON
|
|
* or XML encoding). Can be NULL.
|
|
* @param enumeration the enumeration value
|
|
*
|
|
* @returns the allocated node, or NULL in case of an error
|
|
*/
|
|
struct kmip_node *kmip_node_new_enumeration(enum kmip_tag tag, const char *name,
|
|
uint32_t enumeration)
|
|
{
|
|
struct kmip_node *node;
|
|
|
|
node = kmip_node_new(tag, name, KMIP_TYPE_ENUMERATION);
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
node->enumeration_value = enumeration;
|
|
node->length = sizeof(uint32_t);
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Returns the value of a KMIP node of type enumeration
|
|
*
|
|
* @param node the KMIP node
|
|
*
|
|
* @returns the value of the node, or 0 if the node is not of type enumeration
|
|
*/
|
|
uint32_t kmip_node_get_enumeration(const struct kmip_node *node)
|
|
{
|
|
if (node == NULL)
|
|
return 0;
|
|
|
|
if (node->type != KMIP_TYPE_ENUMERATION)
|
|
return 0;
|
|
|
|
return node->enumeration_value;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new KMIP node of type boolean with the specified tag, and
|
|
* an optional name, and the boolean value
|
|
*
|
|
* @param tag the tag of the new node
|
|
* @param name Optional: the name of the node (only used with JSON
|
|
* or XML encoding). Can be NULL.
|
|
* @param value the value
|
|
*
|
|
* @returns the allocated node, or NULL in case of an error
|
|
*/
|
|
struct kmip_node *kmip_node_new_boolean(enum kmip_tag tag, const char *name,
|
|
bool value)
|
|
{
|
|
struct kmip_node *node;
|
|
|
|
node = kmip_node_new(tag, name, KMIP_TYPE_BOOLEAN);
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
node->boolean_value = value;
|
|
node->length = sizeof(uint64_t);
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Returns the value of a KMIP node of type boolean
|
|
*
|
|
* @param node the KMIP node
|
|
*
|
|
* @returns the value of the node, or 0 if the node is not of type boolean
|
|
*/
|
|
bool kmip_node_get_boolean(const struct kmip_node *node)
|
|
{
|
|
if (node == NULL)
|
|
return false;
|
|
|
|
if (node->type != KMIP_TYPE_BOOLEAN)
|
|
return false;
|
|
|
|
return node->boolean_value;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new KMIP node of type text string with the specified tag, and
|
|
* an optional name, and the text string value
|
|
*
|
|
* @param tag the tag of the new node
|
|
* @param name Optional: the name of the node (only used with JSON
|
|
* or XML encoding). Can be NULL.
|
|
* @param value the value (can be NULL)
|
|
*
|
|
* @returns the allocated node, or NULL in case of an error
|
|
*/
|
|
struct kmip_node *kmip_node_new_text_string(enum kmip_tag tag, const char *name,
|
|
const char *value)
|
|
{
|
|
struct kmip_node *node;
|
|
|
|
node = kmip_node_new(tag, name, KMIP_TYPE_TEXT_STRING);
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
if (value != NULL) {
|
|
node->text_value = strdup(value);
|
|
if (node->text_value == NULL) {
|
|
free(node);
|
|
return NULL;
|
|
}
|
|
node->length = strlen(value);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Returns the value of a KMIP node of type text string
|
|
*
|
|
* @param node the KMIP node
|
|
*
|
|
* @returns the value of the node, or NULL if the node is not of type text
|
|
* string or no string is set. The returned string still belongs to the node,
|
|
* and must not be freed by the caller. It is freed together with the node it
|
|
* was obtained from.
|
|
*/
|
|
const char *kmip_node_get_text_string(const struct kmip_node *node)
|
|
{
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
if (node->type != KMIP_TYPE_TEXT_STRING)
|
|
return NULL;
|
|
|
|
return node->text_value;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new KMIP node of type byte string with the specified tag, and
|
|
* an optional name, and the byte string value
|
|
*
|
|
* @param tag the tag of the new node
|
|
* @param name Optional: the name of the node (only used with JSON
|
|
* or XML encoding). Can be NULL.
|
|
* @param value the byte string (can be NULL)
|
|
* @param length the length of the byte string
|
|
*
|
|
* @returns the allocated node, or NULL in case of an error
|
|
*/
|
|
struct kmip_node *kmip_node_new_byte_string(enum kmip_tag tag, const char *name,
|
|
const unsigned char *value,
|
|
uint32_t length)
|
|
{
|
|
struct kmip_node *node;
|
|
|
|
node = kmip_node_new(tag, name, KMIP_TYPE_BYTE_STRING);
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
if (value != NULL && length > 0) {
|
|
node->bytes_value = malloc(length);
|
|
if (node->bytes_value == NULL) {
|
|
free(node);
|
|
return NULL;
|
|
}
|
|
memcpy(node->bytes_value, value, length);
|
|
node->length = length;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Returns the value of a KMIP node of type byte string
|
|
*
|
|
* @param node the KMIP node
|
|
* @param length On return, the length of the byte string
|
|
*
|
|
* @returns the value of the node, or NULL if the node is not of type byte
|
|
* string or no string is set. The returned string still belongs to the node,
|
|
* and must not be freed by the caller. It is freed together with the node it
|
|
* was obtained from.
|
|
*/
|
|
const unsigned char *kmip_node_get_byte_string(const struct kmip_node *node,
|
|
uint32_t *length)
|
|
{
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
if (node->type != KMIP_TYPE_BYTE_STRING)
|
|
return NULL;
|
|
|
|
if (length != NULL)
|
|
*length = node->length;
|
|
return node->bytes_value;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new KMIP node of type date and time with the specified tag, and
|
|
* an optional name, and the date and time value
|
|
*
|
|
* @param tag the tag of the new node
|
|
* @param name Optional: the name of the node (only used with JSON
|
|
* or XML encoding). Can be NULL.
|
|
* @param value the value
|
|
*
|
|
* @returns the allocated node, or NULL in case of an error
|
|
*/
|
|
struct kmip_node *kmip_node_new_date_time(enum kmip_tag tag, const char *name,
|
|
int64_t value)
|
|
{
|
|
struct kmip_node *node;
|
|
|
|
node = kmip_node_new(tag, name, KMIP_TYPE_DATE_TIME);
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
node->date_time_value = value;
|
|
node->length = sizeof(int64_t);
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Returns the value of a KMIP node of type date and time
|
|
*
|
|
* @param node the KMIP node
|
|
*
|
|
* @returns the value of the node, or 0 if the node is not of type date and time
|
|
*/
|
|
int64_t kmip_node_get_date_time(const struct kmip_node *node)
|
|
{
|
|
if (node == NULL)
|
|
return 0;
|
|
|
|
if (node->type != KMIP_TYPE_DATE_TIME)
|
|
return 0;
|
|
|
|
return node->date_time_value;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new KMIP node of type interval with the specified tag, and
|
|
* an optional name, and the interval value
|
|
*
|
|
* @param tag the tag of the new node
|
|
* @param name Optional: the name of the node (only used with JSON
|
|
* or XML encoding). Can be NULL.
|
|
* @param value the value
|
|
*
|
|
* @returns the allocated node, or NULL in case of an error
|
|
*/
|
|
struct kmip_node *kmip_node_new_interval(enum kmip_tag tag, const char *name,
|
|
uint32_t value)
|
|
{
|
|
struct kmip_node *node;
|
|
|
|
node = kmip_node_new(tag, name, KMIP_TYPE_INTERVAL);
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
node->interval_value = value;
|
|
node->length = sizeof(uint32_t);
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Returns the value of a KMIP node of type interval
|
|
*
|
|
* @param node the KMIP node
|
|
*
|
|
* @returns the value of the node, or 0 if the node is not of type interval
|
|
*/
|
|
uint32_t kmip_node_get_interval(const struct kmip_node *node)
|
|
{
|
|
if (node == NULL)
|
|
return 0;
|
|
|
|
if (node->type != KMIP_TYPE_INTERVAL)
|
|
return 0;
|
|
|
|
return node->interval_value;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new KMIP node of type date and time extended with the specified
|
|
* tag, and an optional name, and the date and time value
|
|
*
|
|
* @param tag the tag of the new node
|
|
* @param name Optional: the name of the node (only used with JSON
|
|
* or XML encoding). Can be NULL.
|
|
* @param value the value
|
|
*
|
|
* @returns the allocated node, or NULL in case of an error
|
|
*/
|
|
struct kmip_node *kmip_node_new_date_time_ext(enum kmip_tag tag,
|
|
const char *name,
|
|
int64_t value)
|
|
{
|
|
struct kmip_node *node;
|
|
|
|
node = kmip_node_new(tag, name, KMIP_TYPE_DATE_TIME_EXTENDED);
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
node->date_time_ext_value = value;
|
|
node->length = sizeof(int64_t);
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Returns the value of a KMIP node of type date and time extended
|
|
*
|
|
* @param node the KMIP node
|
|
*
|
|
* @returns the value of the node, or 0 if the node is not of type date and time
|
|
* extended
|
|
*/
|
|
int64_t kmip_node_get_date_time_ext(const struct kmip_node *node)
|
|
{
|
|
if (node == NULL)
|
|
return 0;
|
|
|
|
if (node->type != KMIP_TYPE_DATE_TIME_EXTENDED)
|
|
return 0;
|
|
|
|
return node->date_time_ext_value;
|
|
}
|
|
|
|
/**
|
|
* Clones (copies) a KMIP node with all its data and elements (in case of a
|
|
* structure node).
|
|
*
|
|
* @param node the KMIP node to clone
|
|
*
|
|
* @returns the cloned node, or NULL in case of an error
|
|
*/
|
|
struct kmip_node *kmip_node_clone(const struct kmip_node *node)
|
|
{
|
|
struct kmip_node *clone, *element, *cloned_element;
|
|
int rc;
|
|
|
|
clone = kmip_node_new(node->tag, node->name, node->type);
|
|
if (clone == NULL)
|
|
return NULL;
|
|
|
|
switch (clone->type) {
|
|
case KMIP_TYPE_STRUCTURE:
|
|
element = node->structure_value;
|
|
while (element != NULL) {
|
|
cloned_element = kmip_node_clone(element);
|
|
if (cloned_element == NULL)
|
|
goto error;
|
|
rc = kmip_node_add_structure_element(clone,
|
|
cloned_element);
|
|
kmip_node_free(cloned_element);
|
|
if (rc != 0)
|
|
goto error;
|
|
element = element->next;
|
|
}
|
|
break;
|
|
case KMIP_TYPE_INTEGER:
|
|
clone->integer_value = node->integer_value;
|
|
break;
|
|
case KMIP_TYPE_LONG_INTEGER:
|
|
clone->long_value = node->long_value;
|
|
break;
|
|
case KMIP_TYPE_BIG_INTEGER:
|
|
clone->big_integer_value = BN_dup(node->big_integer_value);
|
|
if (clone->big_integer_value == NULL)
|
|
goto error;
|
|
break;
|
|
case KMIP_TYPE_ENUMERATION:
|
|
clone->enumeration_value = node->enumeration_value;
|
|
break;
|
|
case KMIP_TYPE_BOOLEAN:
|
|
clone->boolean_value = node->boolean_value;
|
|
break;
|
|
case KMIP_TYPE_TEXT_STRING:
|
|
if (node->text_value != NULL) {
|
|
clone->text_value = strdup(node->text_value);
|
|
if (clone->text_value == NULL)
|
|
goto error;
|
|
clone->length = strlen(clone->text_value);
|
|
}
|
|
break;
|
|
case KMIP_TYPE_BYTE_STRING:
|
|
if (node->bytes_value != NULL && node->length > 0) {
|
|
clone->bytes_value = malloc(node->length);
|
|
if (clone->bytes_value == NULL)
|
|
goto error;
|
|
memcpy(clone->bytes_value, node->bytes_value,
|
|
node->length);
|
|
clone->length = node->length;
|
|
}
|
|
break;
|
|
case KMIP_TYPE_DATE_TIME:
|
|
clone->date_time_value = node->date_time_value;
|
|
break;
|
|
case KMIP_TYPE_INTERVAL:
|
|
clone->interval_value = node->interval_value;
|
|
break;
|
|
case KMIP_TYPE_DATE_TIME_EXTENDED:
|
|
clone->date_time_ext_value = node->date_time_ext_value;
|
|
break;
|
|
default:
|
|
goto error;
|
|
}
|
|
|
|
return clone;
|
|
|
|
error:
|
|
kmip_node_free(clone);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Increments the reference count of a KMIP node
|
|
*
|
|
* @param node the node to increase the reference count for
|
|
*/
|
|
void kmip_node_upref(struct kmip_node *node)
|
|
{
|
|
if (node == NULL)
|
|
return;
|
|
|
|
__sync_add_and_fetch((unsigned long *)&node->ref_count, 1);
|
|
}
|
|
|
|
/**
|
|
* Free a KMIP node, including its value (structure elements, etc)
|
|
*
|
|
* @param node the node to free
|
|
*/
|
|
void kmip_node_free(struct kmip_node *node)
|
|
{
|
|
struct kmip_node *element, *next;
|
|
unsigned long ref_count = 0;
|
|
|
|
if (node == NULL)
|
|
return;
|
|
|
|
if (node->ref_count > 0)
|
|
ref_count = __sync_sub_and_fetch(
|
|
(unsigned long *)&node->ref_count, 1);
|
|
if (ref_count > 0)
|
|
return;
|
|
|
|
switch (node->type) {
|
|
case KMIP_TYPE_STRUCTURE:
|
|
element = node->structure_value;
|
|
while (element != NULL) {
|
|
next = element->next;
|
|
|
|
/*
|
|
* Unchain the element from the parent and next element,
|
|
* even if the element itself might not be freed (due
|
|
* to reference count). But the parent is freed, and
|
|
* thus the chain of elements is not longer existent.
|
|
*/
|
|
element->parent = NULL;
|
|
element->next = NULL;
|
|
|
|
kmip_node_free(element);
|
|
|
|
element = next;
|
|
}
|
|
break;
|
|
case KMIP_TYPE_BIG_INTEGER:
|
|
BN_free(node->big_integer_value);
|
|
break;
|
|
case KMIP_TYPE_TEXT_STRING:
|
|
free(node->text_value);
|
|
break;
|
|
case KMIP_TYPE_BYTE_STRING:
|
|
free(node->bytes_value);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
free(node->name);
|
|
free(node);
|
|
}
|
|
|
|
static struct kmip_version default_protocol_version = {
|
|
.major = KMIP_DEFAULT_PROTOCOL_VERSION_MAJOR,
|
|
.minor = KMIP_DEFAULT_PROTOCOL_VERSION_MINOR
|
|
};
|
|
|
|
/**
|
|
* Sets the default KMIP protocol version
|
|
*
|
|
* @param version the version to set
|
|
*/
|
|
void kmip_set_default_protocol_version(const struct kmip_version *version)
|
|
{
|
|
if (version == NULL)
|
|
return;
|
|
|
|
default_protocol_version.major = version->major;
|
|
default_protocol_version.minor = version->minor;
|
|
}
|
|
|
|
/**
|
|
* Sets the default KMIP protocol version
|
|
*
|
|
* @returns the default KMIP protocol version
|
|
*/
|
|
const struct kmip_version *kmip_get_default_protocol_version(void)
|
|
{
|
|
return &default_protocol_version;
|
|
}
|
|
|
|
/**
|
|
* Constructs a new connection to a KMIP server using the specified connection
|
|
* configuration. The strings specified in the configuration are copied into the
|
|
* newly allocated connection, they can be freed by the caller after the new
|
|
* connection has been allocated. The reference count in the PKEY specified
|
|
* in the configuration is increased. The caller can free its PKEY as needed.
|
|
*
|
|
* @param config the connection configuration
|
|
* @param connection On return: a newly allocated KMIP connection
|
|
* @param debug if true, debug messages are printed
|
|
*
|
|
* @returns 0 in case of success, or a negative errno value
|
|
*/
|
|
int kmip_connection_new(const struct kmip_conn_config *config,
|
|
struct kmip_connection **connection,
|
|
bool debug)
|
|
{
|
|
struct kmip_connection *conn = NULL;
|
|
int rc;
|
|
|
|
if (config == NULL || connection == NULL)
|
|
return -EINVAL;
|
|
|
|
*connection = NULL;
|
|
|
|
switch (config->encoding) {
|
|
case KMIP_ENCODING_TTLV:
|
|
/* TTLV can be used with both, plain-TLS and HTTPS */
|
|
switch (config->transport) {
|
|
case KMIP_TRANSPORT_PLAIN_TLS:
|
|
case KMIP_TRANSPORT_HTTPS:
|
|
break;
|
|
default:
|
|
kmip_debug(debug, "Invalid transport: %d",
|
|
config->transport);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case KMIP_ENCODING_JSON:
|
|
case KMIP_ENCODING_XML:
|
|
/* JSON(XML can only be used with HTTPS */
|
|
switch (config->transport) {
|
|
case KMIP_TRANSPORT_PLAIN_TLS:
|
|
kmip_debug(debug, "JSON/XML encode can only be used "
|
|
"with HTTPS transport");
|
|
return -EINVAL;
|
|
case KMIP_TRANSPORT_HTTPS:
|
|
break;
|
|
default:
|
|
kmip_debug(debug, "Invalid transport: %d",
|
|
config->transport);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
default:
|
|
kmip_debug(debug, "Invalid encoding: %d", config->encoding);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (config->server == NULL) {
|
|
kmip_debug(debug, "KMIP Server must be specified");
|
|
return -EINVAL;
|
|
}
|
|
if (config->tls_client_key == NULL) {
|
|
kmip_debug(debug, "Client key must be specified");
|
|
return -EINVAL;
|
|
}
|
|
if (config->tls_client_cert == NULL) {
|
|
kmip_debug(debug, "Client certificate must be specified");
|
|
return -EINVAL;
|
|
}
|
|
|
|
conn = calloc(1, sizeof(struct kmip_connection));
|
|
if (conn == NULL) {
|
|
kmip_debug(debug, "calloc failed");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
conn->config.encoding = config->encoding;
|
|
conn->config.transport = config->transport;
|
|
kmip_debug(debug, "encoding: %d", conn->config.encoding);
|
|
kmip_debug(debug, "transport: %d", conn->config.transport);
|
|
|
|
conn->config.server = strdup(config->server);
|
|
if (conn->config.server == NULL) {
|
|
kmip_debug(debug, "strdup failed");
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
kmip_debug(debug, "server: '%s'", conn->config.server);
|
|
|
|
conn->config.tls_client_key = config->tls_client_key;
|
|
if (EVP_PKEY_up_ref(conn->config.tls_client_key) != 1) {
|
|
kmip_debug(debug, "EVP_PKEY_up_ref failed");
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
kmip_debug(debug, "client key: %p", conn->config.tls_client_key);
|
|
|
|
conn->config.tls_client_cert = strdup(config->tls_client_cert);
|
|
if (conn->config.tls_client_cert == NULL) {
|
|
kmip_debug(debug, "strdup failed");
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
kmip_debug(debug, "client cert: '%s'", conn->config.tls_client_cert);
|
|
|
|
if (config->tls_ca != NULL) {
|
|
conn->config.tls_ca = strdup(config->tls_ca);
|
|
if (conn->config.tls_ca == NULL) {
|
|
kmip_debug(debug, "strdup failed");
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
kmip_debug(debug, "CA: '%s'", conn->config.tls_ca);
|
|
}
|
|
|
|
if (config->tls_issuer_cert != NULL) {
|
|
conn->config.tls_issuer_cert = strdup(config->tls_issuer_cert);
|
|
if (conn->config.tls_issuer_cert == NULL) {
|
|
kmip_debug(debug, "strdup failed");
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
kmip_debug(debug, "issuer cert: '%s'",
|
|
conn->config.tls_issuer_cert);
|
|
}
|
|
|
|
if (config->tls_pinned_pubkey != NULL) {
|
|
conn->config.tls_pinned_pubkey =
|
|
strdup(config->tls_pinned_pubkey);
|
|
if (conn->config.tls_pinned_pubkey == NULL) {
|
|
kmip_debug(debug, "strdup failed");
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
kmip_debug(debug, "pinned pubkey: '%s'",
|
|
conn->config.tls_pinned_pubkey);
|
|
}
|
|
|
|
if (config->tls_server_cert != NULL) {
|
|
conn->config.tls_server_cert = strdup(config->tls_server_cert);
|
|
if (conn->config.tls_server_cert == NULL) {
|
|
kmip_debug(debug, "strdup failed");
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
kmip_debug(debug, "server cert: '%s'",
|
|
conn->config.tls_server_cert);
|
|
}
|
|
|
|
conn->config.tls_verify_peer = config->tls_verify_peer;
|
|
conn->config.tls_verify_host = config->tls_verify_host;
|
|
kmip_debug(debug, "verify peer: %d", conn->config.tls_verify_peer);
|
|
kmip_debug(debug, "verify host: %d", conn->config.tls_verify_host);
|
|
|
|
if (config->tls_cipher_list != NULL) {
|
|
conn->config.tls_cipher_list = strdup(config->tls_cipher_list);
|
|
if (conn->config.tls_cipher_list == NULL) {
|
|
kmip_debug(debug, "strdup failed");
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
kmip_debug(debug, "TLS cipher list: '%s'",
|
|
conn->config.tls_cipher_list);
|
|
}
|
|
|
|
if (config->tls13_cipher_list != NULL) {
|
|
conn->config.tls13_cipher_list =
|
|
strdup(config->tls13_cipher_list);
|
|
if (conn->config.tls13_cipher_list == NULL) {
|
|
kmip_debug(debug, "strdup failed");
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
kmip_debug(debug, "TLSv1.3 cipher list: '%s'",
|
|
conn->config.tls13_cipher_list);
|
|
}
|
|
|
|
switch (conn->config.transport) {
|
|
case KMIP_TRANSPORT_PLAIN_TLS:
|
|
rc = kmip_connection_tls_init(conn, debug);
|
|
if (rc != 0) {
|
|
kmip_debug(debug, "kmip_connection_tls_init failed");
|
|
goto out;
|
|
}
|
|
break;
|
|
case KMIP_TRANSPORT_HTTPS:
|
|
rc = kmip_connection_https_init(conn, debug);
|
|
if (rc != 0) {
|
|
kmip_debug(debug, "kmip_connection_https_init failed");
|
|
goto out;
|
|
}
|
|
break;
|
|
default:
|
|
kmip_debug(debug, "Invalid transport: %d",
|
|
conn->config.transport);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
*connection = conn;
|
|
rc = 0;
|
|
|
|
out:
|
|
if (rc != 0)
|
|
kmip_connection_free(conn);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Perform a request over the KMIP connection
|
|
*
|
|
* @param connection the KMIP connection
|
|
* @param request the request to send
|
|
* @param response On return: the received response. Must be freed by
|
|
* the caller.
|
|
*
|
|
* @param debug if true, debug messages are printed
|
|
*
|
|
* @returns 0 in case of success, or a negative errno value
|
|
*/
|
|
int kmip_connection_perform(struct kmip_connection *connection,
|
|
struct kmip_node *request,
|
|
struct kmip_node **response,
|
|
bool debug)
|
|
{
|
|
int rc;
|
|
|
|
if (connection == NULL || request == NULL || response == NULL)
|
|
return -EINVAL;
|
|
|
|
kmip_debug(debug, "KMIP Request:");
|
|
kmip_node_dump(request, debug);
|
|
|
|
switch (connection->config.transport) {
|
|
case KMIP_TRANSPORT_PLAIN_TLS:
|
|
rc = kmip_connection_tls_perform(connection, request,
|
|
response, debug);
|
|
break;
|
|
case KMIP_TRANSPORT_HTTPS:
|
|
rc = kmip_connection_https_perform(connection, request,
|
|
response, debug);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (rc == 0 && *response != NULL) {
|
|
kmip_debug(debug, "KMIP Response:");
|
|
kmip_node_dump(*response, debug);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Terminates and frees a KMIP connection.
|
|
*
|
|
* @param connection the KMIP connection to free
|
|
*/
|
|
void kmip_connection_free(struct kmip_connection *connection)
|
|
{
|
|
if (connection == NULL)
|
|
return;
|
|
|
|
switch (connection->config.transport) {
|
|
case KMIP_TRANSPORT_PLAIN_TLS:
|
|
kmip_connection_tls_term(connection);
|
|
break;
|
|
case KMIP_TRANSPORT_HTTPS:
|
|
kmip_connection_https_term(connection);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
free((void *)connection->config.server);
|
|
EVP_PKEY_free(connection->config.tls_client_key);
|
|
free((void *)connection->config.tls_client_cert);
|
|
if (connection->config.tls_ca != NULL)
|
|
free((void *)connection->config.tls_ca);
|
|
if (connection->config.tls_issuer_cert != NULL)
|
|
free((void *)connection->config.tls_issuer_cert);
|
|
if (connection->config.tls_pinned_pubkey != NULL)
|
|
free((void *)connection->config.tls_pinned_pubkey);
|
|
if (connection->config.tls_server_cert != NULL)
|
|
free((void *)connection->config.tls_server_cert);
|
|
if (connection->config.tls_cipher_list != NULL)
|
|
free((void *)connection->config.tls_cipher_list);
|
|
if (connection->config.tls13_cipher_list != NULL)
|
|
free((void *)connection->config.tls13_cipher_list);
|
|
|
|
free(connection);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the serevr's certificate, public key and certificate chain
|
|
*
|
|
* @param server the KMIP server.
|
|
* For Plain-TLS transport, only the hostname and
|
|
* optional port number.
|
|
* For HTTPS transport, an URL in the form
|
|
* 'https://hostname[:port]/uri'
|
|
* @param transport the transport mode
|
|
* @param ca Optional: File name of the CA bundle PEM file, or a
|
|
* name of a directory the multiple CA certificates.
|
|
* If this is NULL, then the default system path for
|
|
* CA certificates is used.
|
|
* @param client_key the client key as an OpenSSL PKEY object.
|
|
* @param client_cert File name of the client certificate PEM file
|
|
* @param server_cert_pem File name of a PEM file into which the server
|
|
* certificate is written. If NULL then ignored.
|
|
* @param server_pubkey_pem File name of a PEM file into which the server
|
|
* public key is written. If NULL then ignored.
|
|
* @param cert_chain_pem File name of a PEM file into which the certificate
|
|
* chain (excluding the server certificate) is written.
|
|
* If NULL then ignored.
|
|
* @param verified On return: If the server 's certificate has been
|
|
* verified using the CA specification (if ca = NULL:
|
|
* default system CAs, otherwise path or file to CAs).
|
|
* @param debug if true, debug messages are printed
|
|
*
|
|
* @returns 0 in case of success, or a negative errno value
|
|
*/
|
|
int kmip_connection_get_server_cert(const char *server,
|
|
enum kmip_transport transport,
|
|
const char *ca,
|
|
EVP_PKEY *client_key,
|
|
const char *client_cert,
|
|
const char *server_cert_pem,
|
|
const char *server_pubkey_pem,
|
|
const char *cert_chain_pem,
|
|
bool *verified,
|
|
bool debug)
|
|
{
|
|
struct kmip_conn_config config = { 0 };
|
|
struct kmip_connection *conn = NULL;
|
|
int rc, numcerts, i, port_found = 0;
|
|
const char *tok, *tok2;
|
|
char *hostname = NULL;
|
|
STACK_OF(X509) *chain;
|
|
bool do_verify = true;
|
|
FILE *fp = NULL;
|
|
X509 *cert;
|
|
|
|
if (server == NULL || client_key == NULL || client_cert == NULL)
|
|
return -EINVAL;
|
|
|
|
config.encoding = KMIP_ENCODING_TTLV;
|
|
config.transport = KMIP_TRANSPORT_PLAIN_TLS;
|
|
config.tls_ca = ca;
|
|
config.tls_client_key = client_key;
|
|
config.tls_client_cert = client_cert;
|
|
config.tls_verify_host = false;
|
|
config.tls_verify_peer = false;
|
|
config.tls_cipher_list = NULL;
|
|
config.tls13_cipher_list = NULL;
|
|
|
|
if (transport == KMIP_TRANSPORT_HTTPS) {
|
|
if (strncmp(server, "https://", 8) != 0) {
|
|
kmip_debug(debug, "Server must start with 'https://'");
|
|
return -EINVAL;
|
|
}
|
|
server += 8;
|
|
|
|
/* Find port (if any) and beginning of uri */
|
|
if (*server == '[') {
|
|
/* IPv6 address enclosed in square brackets */
|
|
tok = strchr(server, ']');
|
|
if (tok == NULL) {
|
|
kmip_debug(debug, "malformed IPv6 address");
|
|
return -EINVAL;
|
|
}
|
|
tok++;
|
|
if (*tok == ':') {
|
|
port_found = 1;
|
|
tok2 = strchr(tok, '/');
|
|
if (tok2 == NULL)
|
|
tok2 = tok + strlen(tok);
|
|
} else {
|
|
tok2 = strchr(tok, '/');
|
|
if (tok2 == NULL)
|
|
tok2 = tok + strlen(tok);
|
|
}
|
|
} else {
|
|
/* hostname or IPv4 address */
|
|
tok = strchr(server, ':');
|
|
if (tok != NULL) {
|
|
port_found = 1;
|
|
tok2 = strchr(tok, '/');
|
|
if (tok2 == NULL)
|
|
tok2 = tok + strlen(tok);
|
|
} else {
|
|
tok2 = strchr(server, '/');
|
|
if (tok2 == NULL)
|
|
tok2 = (char *)server + strlen(server);
|
|
}
|
|
}
|
|
|
|
hostname = calloc(1, tok2 - server + (!port_found ? 5 : 1));
|
|
if (hostname == NULL) {
|
|
kmip_debug(debug, "calloc failed");
|
|
return -ENOMEM;
|
|
}
|
|
strncpy(hostname, server, tok2 - server);
|
|
if (!port_found) {
|
|
strcat(hostname, ":");
|
|
strcat(hostname, KMIP_DEFAULT_HTTPS_PORT);
|
|
}
|
|
|
|
config.server = hostname;
|
|
} else {
|
|
config.server = server;
|
|
}
|
|
|
|
retry:
|
|
config.tls_verify_peer = do_verify;
|
|
rc = kmip_connection_new(&config, &conn, debug);
|
|
if (rc != 0) {
|
|
kmip_debug(debug, "kmip_connection_new failed (do_verify: %d)",
|
|
do_verify);
|
|
|
|
if (do_verify) {
|
|
/*
|
|
* If peer verification failed (e.g. due to a self
|
|
* signed server certificate), try again without peer
|
|
* verification.
|
|
*/
|
|
do_verify = false;
|
|
goto retry;
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
if (verified != NULL)
|
|
*verified = do_verify;
|
|
|
|
chain = SSL_get_peer_cert_chain(conn->plain_tls.ssl);
|
|
if (chain == NULL) {
|
|
kmip_debug(debug, "SSL_get_peer_cert_chain failed");
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
numcerts = sk_X509_num(chain);
|
|
for (i = 0; i < numcerts; i++) {
|
|
cert = sk_X509_value(chain, i);
|
|
if (cert == NULL)
|
|
break;
|
|
|
|
if (debug) {
|
|
kmip_debug(debug, "%d. Certificate:", i);
|
|
X509_print_ex_fp(stderr, cert, XN_FLAG_COMPAT,
|
|
X509_FLAG_COMPAT);
|
|
}
|
|
|
|
if (i == 0 && server_cert_pem != NULL) {
|
|
fp = fopen_nofollow(server_cert_pem, "w");
|
|
if (fp == NULL) {
|
|
rc = -errno;
|
|
kmip_debug(debug, "Failed to open %s for write",
|
|
server_cert_pem, strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
if (PEM_write_X509(fp, cert) != 1) {
|
|
kmip_debug(debug, "PEM_write_X509 failed to "
|
|
"write to %s", server_cert_pem);
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
fclose(fp);
|
|
fp = NULL;
|
|
|
|
if (server_pubkey_pem != NULL) {
|
|
fp = fopen_nofollow(server_pubkey_pem, "w");
|
|
if (fp == NULL) {
|
|
rc = -errno;
|
|
kmip_debug(debug, "Failed to open %s "
|
|
"for write",
|
|
server_pubkey_pem,
|
|
strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
if (PEM_write_PUBKEY(fp, X509_get0_pubkey(cert))
|
|
!= 1) {
|
|
kmip_debug(debug, "PEM_write_PUBKEY "
|
|
"failed to write to %s",
|
|
server_pubkey_pem);
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
fclose(fp);
|
|
fp = NULL;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (i > 0 && cert_chain_pem != NULL) {
|
|
if (fp == NULL)
|
|
fp = fopen_nofollow(cert_chain_pem, "w");
|
|
if (fp == NULL) {
|
|
rc = -errno;
|
|
kmip_debug(debug, "Failed to open %s for write",
|
|
cert_chain_pem, strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
if (PEM_write_X509(fp, cert) != 1) {
|
|
kmip_debug(debug, "PEM_write_X509 failed to "
|
|
"write to %s", cert_chain_pem);
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
out:
|
|
if (fp != NULL)
|
|
fclose(fp);
|
|
if (conn != NULL)
|
|
kmip_connection_free(conn);
|
|
if (hostname != NULL)
|
|
free(hostname);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Library constructor
|
|
*/
|
|
void __attribute__ ((constructor)) kmip_init(void)
|
|
{
|
|
CURLsslset rc;
|
|
|
|
/*
|
|
* Ensure that curl uses OpenSSL as SSL backend. If curl has already
|
|
* been itialized by the calling application, the backend can't be
|
|
* changed anymore, but we continue anyway. However, it will later be
|
|
* checked if curl uses the OpenSSL backend, and a HTTPS connection
|
|
* will fail if it is not using the OpenSSL backend.
|
|
*/
|
|
rc = curl_global_sslset(CURLSSLBACKEND_OPENSSL, NULL, NULL);
|
|
if (rc != CURLSSLSET_OK && rc != CURLSSLSET_TOO_LATE)
|
|
errx(EXIT_FAILURE, "libkmipclient: libcurl was not built with "
|
|
"the OpenSSL backend");
|
|
|
|
curl_global_init(CURL_GLOBAL_ALL);
|
|
}
|
|
|
|
/**
|
|
* Library destructor
|
|
*/
|
|
void __attribute__ ((destructor)) kmip_exit(void)
|
|
{
|
|
curl_global_cleanup();
|
|
}
|