Files
s390-tools/libkmipclient/https.c
Ingo Franzki 56fecf1832 libkmipclient: Add KMIP client shared library
libkmipclient is a shared library that provides an KMIP client to
communicate with an KMIP server. KMIP stands for Key Management
Interoperability Protocol, and is an extensible communication protocol
that defines message formats for the manipulation of cryptographic
keys on a key management server.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-07-07 13:53:06 +02:00

849 lines
22 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 <string.h>
#include <sys/stat.h>
#include <openssl/err.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include "lib/zt_common.h"
#include "kmip.h"
#include "utils.h"
#define HTTP_HDR_CONTENT_TYPE "Content-Type:"
#define CURL_ERROR_CHECK(rc, text, debug, label) \
do { \
if ((rc) != CURLE_OK) { \
kmip_debug((debug), "%s: %s", (text), \
curl_easy_strerror((rc))); \
goto label; \
} \
} while (0)
struct curl_sslctx_cb_data {
const struct kmip_connection *conn;
bool debug;
};
struct curl_write_cb_data {
const struct kmip_connection *conn;
bool error;
bool debug;
union {
struct {
json_tokener *tok;
json_object *resp_obj;
} json;
struct {
xmlParserCtxtPtr ctx;
} xml;
struct {
BIO *resp_mem_bio;
} ttlv;
};
};
struct curl_header_cb_data {
const struct kmip_connection *conn;
bool error;
bool debug;
};
/**
* Initializes a new HTTPS connection to a KMIP server.
*
* @param connn The KMIP connection
* @param debug if true, debug messages are printed
*
* @returns 0 in case of success, or a negative errno value
*/
int kmip_connection_https_init(struct kmip_connection *conn, bool debug)
{
const char *content_type, *accept, *server, *tok;
const struct curl_tlssessioninfo *info = NULL;
bool port_found = false;
struct stat sb;
int rc;
if (conn == NULL)
return -EINVAL;
if (strncmp(conn->config.server, "https://", 8) != 0) {
kmip_debug(debug, "Server must start with 'https://'");
return -EINVAL;
}
/* Find port (if any) and beginning of uri */
server = conn->config.server + 8;
if (*server == '[') {
/* IPv6 address enclosed in square brackets */
tok = strchr(server, ']');
if (tok == NULL) {
kmip_debug(debug, "malformed IPv6 address");
return -EINVAL;
}
tok++;
port_found = (*tok == ':');
} else {
/* hostname or IPv4 address */
tok = strchr(server, ':');
port_found = (tok != NULL);
}
conn->https.curl = curl_easy_init();
if (conn->https.curl == NULL) {
kmip_debug(debug, "curl_easy_init failed");
return -EIO;
}
/*
* The CURLOPT_SSL_CTX_FUNCTION callback only works with the OpenSSL
* curl backend. Check that OpenSSL is the current curl backend.
*/
rc = curl_easy_getinfo(conn->https.curl, CURLINFO_TLS_SSL_PTR, &info);
CURL_ERROR_CHECK(rc, "curl_easy_getinfo CURLINFO_TLS_SSL_PTR", debug,
out);
if (info->backend != CURLSSLBACKEND_OPENSSL) {
kmip_debug(debug, "libcurl is not using the OpenSSL backend");
rc = -EIO;
goto out;
}
rc = curl_easy_setopt(conn->https.curl, CURLOPT_VERBOSE, debug ? 1 : 0);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_VERBOSE", debug, out);
rc = curl_easy_setopt(conn->https.curl, CURLOPT_URL,
conn->config.server);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_URL", debug, out);
if (!port_found) {
rc = curl_easy_setopt(conn->https.curl, CURLOPT_PORT,
KMIP_DEFAULT_HTTPS_PORT_NUM);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_URL", debug,
out);
}
rc = curl_easy_setopt(conn->https.curl, CURLOPT_SSL_VERIFYPEER,
conn->config.tls_verify_peer ? 1L : 0L);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_VERIFYPEER", debug,
out);
rc = curl_easy_setopt(conn->https.curl, CURLOPT_SSL_VERIFYHOST,
conn->config.tls_verify_host ? 2L : 0L);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_VERIFYHOST", debug,
out);
if (conn->config.tls_ca != NULL) {
if (stat(conn->config.tls_ca, &sb) != 0) {
rc = -errno;
kmip_debug(debug, "stat failed on '%s': %s",
conn->config.tls_ca, strerror(-rc));
goto out;
}
if (S_ISDIR(sb.st_mode)) {
rc = curl_easy_setopt(conn->https.curl, CURLOPT_CAPATH,
conn->config.tls_ca);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CAPATH",
debug, out);
} else {
rc = curl_easy_setopt(conn->https.curl, CURLOPT_CAINFO,
conn->config.tls_ca);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CAINFO",
debug, out);
}
}
if (conn->config.tls_issuer_cert != NULL) {
rc = curl_easy_setopt(conn->https.curl, CURLOPT_ISSUERCERT,
conn->config.tls_issuer_cert);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_ISSUERCERT",
debug, out);
}
if (conn->config.tls_pinned_pubkey != NULL) {
rc = curl_easy_setopt(conn->https.curl, CURLOPT_PINNEDPUBLICKEY,
conn->config.tls_pinned_pubkey);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_PINNEDPUBLICKEY",
debug, out);
}
rc = curl_easy_setopt(conn->https.curl, CURLOPT_FOLLOWLOCATION, 0L);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_FOLLOWLOCATION",
debug, out);
if (conn->config.tls_cipher_list != NULL) {
rc = curl_easy_setopt(conn->https.curl, CURLOPT_SSL_CIPHER_LIST,
conn->config.tls_cipher_list);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_CIPHER_LIST",
debug, out);
}
if (conn->config.tls13_cipher_list != NULL) {
rc = curl_easy_setopt(conn->https.curl, CURLOPT_TLS13_CIPHERS,
conn->config.tls13_cipher_list);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_TLS13_CIPHERS",
debug, out);
}
switch (conn->config.encoding) {
case KMIP_ENCODING_TTLV:
content_type = "Content-Type: application/octet-stream";
accept = "Accept: application/octet-stream";
break;
case KMIP_ENCODING_JSON:
content_type = "Content-Type: application/json;charset=UTF-8";
accept = "Accept: application/json";
break;
case KMIP_ENCODING_XML:
content_type = "Content-Type: text/xml;charset=UTF-8";
accept = "Accept: text/xml";
break;
default:
kmip_debug(debug, "invalid encoding: %d",
conn->config.encoding);
rc = -EINVAL;
goto out;
}
conn->https.headers = curl_slist_append(conn->https.headers,
content_type);
if (conn->https.headers == NULL) {
kmip_debug(debug, "curl_slist_append failed");
rc = -ENOMEM;
goto out;
}
conn->https.headers = curl_slist_append(conn->https.headers, accept);
if (conn->https.headers == NULL) {
kmip_debug(debug, "curl_slist_append failed");
rc = -ENOMEM;
goto out;
}
conn->https.headers = curl_slist_append(conn->https.headers,
"Accept-Charset: UTF-8");
if (conn->https.headers == NULL) {
kmip_debug(debug, "curl_slist_append failed");
rc = -ENOMEM;
goto out;
}
/* Disable "Expect: 100-continue" */
conn->https.headers = curl_slist_append(conn->https.headers, "Expect:");
if (conn->https.headers == NULL) {
kmip_debug(debug, "curl_slist_append failed");
rc = -ENOMEM;
goto out;
}
/* As per KMIP HTTPS profile: Cache-Control: no-cache */
conn->https.headers = curl_slist_append(conn->https.headers,
"Cache-Control: no-cache");
if (conn->https.headers == NULL) {
kmip_debug(debug, "curl_slist_append failed");
rc = -ENOMEM;
goto out;
}
rc = curl_easy_setopt(conn->https.curl, CURLOPT_HTTPHEADER,
conn->https.headers);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_HTTPHEADER", debug,
out);
rc = 0;
out:
if (rc != 0)
kmip_connection_https_term(conn);
return rc;
}
/**
* This callback called before the SSL handshake is performed.
* It sets the client certificate and private key into the context.
* It also adds a pinned server certificate to the SSL certificate store, so
* that it is treated as trusted, although it might be self-signed.
*/
static CURLcode mkip_connection_https_sslctx_cb(CURL *UNUSED(curl),
void *sslctx, void *parm)
{
struct curl_sslctx_cb_data *sslctx_cb = parm;
SSL_CTX *ssl_ctx = (SSL_CTX *)sslctx;
const struct kmip_connection *conn;
X509_STORE *store;
X509 *cert = NULL;
FILE *fp;
int rc;
if (ssl_ctx == NULL || sslctx_cb == NULL || sslctx_cb->conn == NULL)
return CURLE_ABORTED_BY_CALLBACK;
conn = sslctx_cb->conn;
if (SSL_CTX_use_certificate_file(sslctx, conn->config.tls_client_cert,
SSL_FILETYPE_PEM) != 1) {
kmip_debug(sslctx_cb->debug, "Failed to load the client "
"certificate '%s'", conn->config.tls_client_cert);
if (sslctx_cb->debug)
ERR_print_errors_fp(stderr);
return CURLE_ABORTED_BY_CALLBACK;
}
if (SSL_CTX_use_PrivateKey(ssl_ctx, conn->config.tls_client_key) != 1) {
kmip_debug(sslctx_cb->debug, "Failed to set the client key");
if (sslctx_cb->debug)
ERR_print_errors_fp(stderr);
return CURLE_ABORTED_BY_CALLBACK;
}
if (conn->config.tls_server_cert == NULL)
return CURLE_OK;
store = SSL_CTX_get_cert_store(ssl_ctx);
if (store == NULL) {
kmip_debug(sslctx_cb->debug, "Failed to get SSL Store");
if (sslctx_cb->debug)
ERR_print_errors_fp(stderr);
return CURLE_ABORTED_BY_CALLBACK;
}
fp = fopen(conn->config.tls_server_cert, "r");
if (fp == NULL) {
rc = -errno;
kmip_debug(sslctx_cb->debug,
"Failed to read server cert '%s': %s",
conn->config.tls_server_cert, strerror(-rc));
return CURLE_ABORTED_BY_CALLBACK;
}
cert = PEM_read_X509(fp, NULL, NULL, NULL);
fclose(fp);
if (cert == NULL) {
kmip_debug(sslctx_cb->debug, "Failed to read the server "
"certificate from file '%s'",
conn->config.tls_server_cert);
if (sslctx_cb->debug)
ERR_print_errors_fp(stderr);
return CURLE_ABORTED_BY_CALLBACK;
}
if (sslctx_cb->debug) {
kmip_debug(sslctx_cb->debug, "Pinned server certificate:");
X509_print_ex_fp(stderr, cert, XN_FLAG_COMPAT,
X509_FLAG_COMPAT);
}
rc = X509_STORE_add_cert(store, cert);
if (rc != 1) {
kmip_debug(sslctx_cb->debug, "Failed to add server "
"certificate to SSL Store");
if (sslctx_cb->debug)
ERR_print_errors_fp(stderr);
X509_free(cert);
return CURLE_ABORTED_BY_CALLBACK;
}
X509_free(cert);
return CURLE_OK;
}
/**
* Callback called during curl_easy_perform() to handle received headers.
* Check for the expected response content type.
*/
static size_t mkip_connection_https_header_cb(void *contents, size_t size,
size_t nmemb, void *userp)
{
struct curl_header_cb_data *cb = (struct curl_header_cb_data *)userp;
size_t num = size * nmemb;
const char *content_type;
char *hdr = contents;
size_t ofs;
char *val;
if (num < strlen(HTTP_HDR_CONTENT_TYPE))
goto out;
if (strncasecmp(hdr, HTTP_HDR_CONTENT_TYPE,
strlen(HTTP_HDR_CONTENT_TYPE)) != 0)
goto out;
ofs = strlen(HTTP_HDR_CONTENT_TYPE);
val = hdr + ofs;
while (*val == ' ' && ofs < num) {
ofs++;
val++;
}
if (ofs >= num)
goto out;
switch (cb->conn->config.encoding) {
case KMIP_ENCODING_TTLV:
content_type = "application/octet-stream";
break;
case KMIP_ENCODING_JSON:
content_type = "application/json";
break;
case KMIP_ENCODING_XML:
content_type = "text/xml";
break;
default:
return 0;
}
if (num - ofs >= strlen(content_type) &&
strncasecmp(val, content_type, strlen(content_type)) == 0)
goto out;
cb->error = true;
kmip_debug(cb->debug, "Unexpected response Content-Type: %.*s",
(int)(num - ofs), val);
return 0;
out:
return num;
}
/**
* Callback called during curl_easy_perform() to handle received data.
* Parse the (potentially partial) KMIP data.
*/
static size_t mkip_connection_https_write_cb(void *contents, size_t size,
size_t nmemb, void *userp)
{
struct curl_write_cb_data *cb = (struct curl_write_cb_data *)userp;
enum json_tokener_error jerr;
size_t num = size * nmemb;
int rc;
switch (cb->conn->config.encoding) {
case KMIP_ENCODING_TTLV:
kmip_debug(cb->debug, "Response Data (TTLV): %lu bytes", num);
if (cb->debug)
kmip_print_dump(__func__, (unsigned char *)contents,
num, 2);
if (BIO_write(cb->ttlv.resp_mem_bio, contents, num) !=
(int)num) {
cb->error = true;
kmip_debug(cb->debug, "BIO_write failed");
return 0;
}
break;
case KMIP_ENCODING_JSON:
kmip_debug(cb->debug, "Response Data (JSON):");
kmip_debug(cb->debug, " ->%*s<-", (int)num, (char *)contents);
if (cb->json.resp_obj != NULL) {
kmip_debug(cb->debug, "JSON data already complete, but "
"additional data received");
cb->error = true;
return 0;
}
cb->json.resp_obj = json_tokener_parse_ex(cb->json.tok,
(const char *)contents, num);
if (cb->json.resp_obj == NULL) {
jerr = json_tokener_get_error(cb->json.tok);
if (jerr == json_tokener_continue)
goto out;
cb->error = true;
kmip_debug(cb->debug, "json_tokener_parse_ex failed: %s",
json_tokener_error_desc(jerr));
return 0;
}
break;
case KMIP_ENCODING_XML:
kmip_debug(cb->debug, "Response Data (XML):");
kmip_debug(cb->debug, " ->%*s<-", (int)num, (char *)contents);
rc = xmlParseChunk(cb->xml.ctx, (const char *)contents, num, 0);
if (rc != XML_ERR_OK) {
cb->error = true;
kmip_debug(cb->debug, "xmlParseChunk failed: %d", rc);
return 0;
}
break;
}
out:
return num;
}
/**
* Perform a request over the KMIP connection
*
* @param conn 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_https_perform(struct kmip_connection *conn,
struct kmip_node *request,
struct kmip_node **response,
bool debug)
{
struct curl_sslctx_cb_data sslctx_cb = { 0 };
struct curl_header_cb_data header_cb = { 0 };
struct curl_write_cb_data write_cb = { 0 };
char error_str[CURL_ERROR_SIZE] = { 0 };
json_object *req_json_obj = NULL;
xmlNode *req_xml_obj = NULL;
xmlDoc *req_xml_doc = NULL;
BIO *req_mem_bio = NULL;
char *req_buff = NULL;
int req_buff_size = 0;
long status_code;
size_t size;
int rc;
if (conn == NULL || request == NULL || response == NULL)
return -EINVAL;
*response = NULL;
rc = curl_easy_setopt(conn->https.curl, CURLOPT_ERRORBUFFER,
error_str);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_ERRORBUFFER", debug,
out);
/* Setup SSL Context callback */
sslctx_cb.conn = conn;
sslctx_cb.debug = debug;
rc = curl_easy_setopt(conn->https.curl, CURLOPT_SSL_CTX_FUNCTION,
mkip_connection_https_sslctx_cb);
CURL_ERROR_CHECK(rc, "curl_easy_setopt "
"CURLOPT_SSL_CTX_FUNCTION", debug, out);
rc = curl_easy_setopt(conn->https.curl, CURLOPT_SSL_CTX_DATA,
&sslctx_cb);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_CTX_DATA",
debug, out);
/* Setup write callback to handle received data */
write_cb.conn = conn;
write_cb.debug = debug;
switch (conn->config.encoding) {
case KMIP_ENCODING_TTLV:
write_cb.ttlv.resp_mem_bio = BIO_new(BIO_s_mem());
if (write_cb.ttlv.resp_mem_bio == NULL) {
kmip_debug(debug, "BIO_new failed");
rc = -ENOMEM;
goto out;
}
break;
case KMIP_ENCODING_JSON:
write_cb.json.tok = json_tokener_new();
if (write_cb.json.tok == NULL) {
kmip_debug(debug, "json_tokener_new failed");
rc = -EIO;
goto out;
}
break;
case KMIP_ENCODING_XML:
write_cb.xml.ctx = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0,
NULL);
if (write_cb.xml.ctx == NULL) {
kmip_debug(debug, "xmlCreatePushParserCtxt failed");
rc = -EIO;
goto out;
}
break;
}
rc = curl_easy_setopt(conn->https.curl, CURLOPT_WRITEFUNCTION,
mkip_connection_https_write_cb);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_WRITEFUNCTION", debug,
out);
rc = curl_easy_setopt(conn->https.curl, CURLOPT_WRITEDATA,
(void *)&write_cb);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_WRITEDATA", debug,
out);
/* Setup header callback to check content type */
header_cb.conn = conn;
header_cb.debug = debug;
rc = curl_easy_setopt(conn->https.curl, CURLOPT_HEADERFUNCTION,
mkip_connection_https_header_cb);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_HEADERFUNCTION", debug,
out);
rc = curl_easy_setopt(conn->https.curl, CURLOPT_HEADERDATA,
(void *)&header_cb);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_HEADERDATA", debug,
out);
/* Setup POST request and post data */
rc = curl_easy_setopt(conn->https.curl, CURLOPT_CUSTOMREQUEST, "POST");
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CUSTOMREQUEST",
debug, out);
rc = curl_easy_setopt(conn->https.curl, CURLOPT_POST, 1L);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_POST",
debug, out);
switch (conn->config.encoding) {
case KMIP_ENCODING_TTLV:
req_mem_bio = BIO_new(BIO_s_mem());
if (req_mem_bio == NULL) {
kmip_debug(debug, "BIO_new failed");
rc = -ENOMEM;
goto out;
}
rc = kmip_encode_ttlv(request, req_mem_bio, &size, debug);
if (rc != 0) {
kmip_debug(debug, "kmip_encode_ttlv failed");
goto out;
}
req_buff_size = BIO_get_mem_data(req_mem_bio, &req_buff);
kmip_debug(debug, "Request Data (TTLV): %d bytes",
req_buff_size);
if (debug)
kmip_print_dump(__func__, (unsigned char *)req_buff,
req_buff_size, 2);
break;
case KMIP_ENCODING_JSON:
rc = kmip_encode_json(request, &req_json_obj, debug);
if (rc != 0) {
kmip_debug(debug, "kmip_encode_json failed");
goto out;
}
/*
* The memory returned by json_object_to_json_string_ext
* is freed when the JSON object is freed.
*/
req_buff = (char *)json_object_to_json_string_ext(req_json_obj,
JSON_C_TO_STRING_PLAIN |
JSON_C_TO_STRING_NOSLASHESCAPE);
if (req_buff == NULL) {
kmip_debug(debug,
"json_object_to_json_string_ext failed");
rc = -EIO;
goto out;
}
req_buff_size = strlen(req_buff);
kmip_debug(debug, "Request Data (JSON):");
kmip_debug(debug, " ->%*s<-", req_buff_size,
req_buff);
break;
case KMIP_ENCODING_XML:
req_xml_doc = xmlNewDoc((xmlChar *)"1.0");
if (req_xml_doc == NULL) {
kmip_debug(debug, "xmlNewDoc failed");
rc = -EIO;
goto out;
}
rc = kmip_encode_xml(request, &req_xml_obj, debug);
if (rc != 0) {
kmip_debug(debug, "kmip_encode_xml failed");
goto out;
}
xmlDocSetRootElement(req_xml_doc, req_xml_obj);
req_xml_obj = NULL;
xmlDocDumpFormatMemoryEnc(req_xml_doc, (xmlChar **)&req_buff,
&req_buff_size, "UTF-8", 0);
if (req_buff == NULL || req_buff_size == 0) {
kmip_debug(debug, "xmlDocDumpFormatMemoryEnc failed");
rc = -EIO;
goto out;
}
kmip_debug(debug, "Request Data (XML):");
kmip_debug(debug, " ->%*s<-", req_buff_size,
req_buff);
break;
}
rc = curl_easy_setopt(conn->https.curl, CURLOPT_POSTFIELDSIZE,
req_buff_size);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_POSTFIELDSIZE",
debug, out);
rc = curl_easy_setopt(conn->https.curl, CURLOPT_POSTFIELDS,
req_buff);
CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_POSTFIELDS",
debug, out);
/* Perform the request */
rc = curl_easy_perform(conn->https.curl);
if (rc != CURLE_OK) {
kmip_debug(debug, "curl_easy_perform for '%s' failed: %s",
conn->config.server, curl_easy_strerror(rc));
kmip_debug(debug, "Error: %s", error_str);
if (header_cb.error) {
kmip_debug(debug, "Unexpected Content-Type");
rc = -EBADMSG;
}
if (write_cb.error) {
kmip_debug(debug, "JSON/XML parsing failed");
rc = -EBADMSG;
}
rc = -EIO;
goto out;
}
/* Check response */
rc = curl_easy_getinfo(conn->https.curl, CURLINFO_RESPONSE_CODE,
&status_code);
CURL_ERROR_CHECK(rc, "curl_easy_getinfo CURLINFO_RESPONSE_CODE",
debug, out);
kmip_debug(debug, "HTTP status code: %d", status_code);
if (status_code != 200) {
rc = -EBADMSG;
goto out;
}
/* Process received data */
switch (conn->config.encoding) {
case KMIP_ENCODING_TTLV:
rc = kmip_decode_ttlv(write_cb.ttlv.resp_mem_bio, NULL,
response, debug);
if (rc != 0) {
kmip_debug(debug, "kmip_decode_ttlv failed");
goto out;
}
break;
case KMIP_ENCODING_JSON:
if (write_cb.json.resp_obj == NULL) {
kmip_debug(debug, "JSON content not wellformed");
rc = -EBADMSG;
goto out;
}
rc = kmip_decode_json(write_cb.json.resp_obj, NULL, response,
debug);
if (rc != 0) {
kmip_debug(debug, "kmip_decode_json failed");
goto out;
}
break;
case KMIP_ENCODING_XML:
rc = xmlParseChunk(write_cb.xml.ctx, "", 0, 1);
if (rc != XML_ERR_OK || !write_cb.xml.ctx->wellFormed ||
write_cb.xml.ctx->myDoc == NULL) {
kmip_debug(debug, "XML content not wellformed");
rc = -EBADMSG;
goto out;
}
rc = kmip_decode_xml(xmlDocGetRootElement(
write_cb.xml.ctx->myDoc),
NULL, response, debug);
if (rc != 0) {
kmip_debug(debug, "kmip_decode_xml failed");
goto out;
}
break;
}
rc = 0;
out:
/* Cleanup */
switch (conn->config.encoding) {
case KMIP_ENCODING_TTLV:
if (req_mem_bio != NULL)
BIO_free(req_mem_bio);
if (write_cb.ttlv.resp_mem_bio != NULL)
BIO_free(write_cb.ttlv.resp_mem_bio);
break;
case KMIP_ENCODING_JSON:
if (write_cb.json.tok != NULL)
json_tokener_free(write_cb.json.tok);
if (write_cb.json.resp_obj != NULL)
json_object_put(write_cb.json.resp_obj);
if (req_json_obj != NULL)
json_object_put(req_json_obj);
break;
case KMIP_ENCODING_XML:
if (write_cb.xml.ctx != NULL) {
xmlFreeDoc(write_cb.xml.ctx->myDoc);
xmlFreeParserCtxt(write_cb.xml.ctx);
}
if (req_xml_doc != NULL)
xmlFreeDoc(req_xml_doc);
if (req_xml_obj != NULL)
xmlFreeNode(req_xml_obj);
if (req_buff != NULL)
xmlFree(req_buff);
break;
}
if (rc != 0 && *response != NULL) {
kmip_node_free(*response);
*response = NULL;
}
curl_easy_setopt(conn->https.curl, CURLOPT_SSL_CTX_FUNCTION, NULL);
curl_easy_setopt(conn->https.curl, CURLOPT_SSL_CTX_DATA, NULL);
curl_easy_setopt(conn->https.curl, CURLOPT_WRITEFUNCTION, NULL);
curl_easy_setopt(conn->https.curl, CURLOPT_WRITEDATA, NULL);
curl_easy_setopt(conn->https.curl, CURLOPT_HEADERFUNCTION, NULL);
curl_easy_setopt(conn->https.curl, CURLOPT_HEADERDATA, NULL);
curl_easy_setopt(conn->https.curl, CURLOPT_ERRORBUFFER, NULL);
curl_easy_setopt(conn->https.curl, CURLOPT_POSTFIELDS, NULL);
curl_easy_setopt(conn->https.curl, CURLOPT_POSTFIELDSIZE, -1);
return rc;
}
/**
* Terminates a HTTPS KMIP connection.
*
* @param conn the KMIP connection to free
*/
void kmip_connection_https_term(struct kmip_connection *conn)
{
if (conn == NULL)
return;
if (conn->https.curl != NULL)
curl_easy_cleanup(conn->https.curl);
conn->https.curl = NULL;
if (conn->https.headers != NULL)
curl_slist_free_all(conn->https.headers);
conn->https.headers = NULL;
}