mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
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>
This commit is contained in:
committed by
Jan Höppner
parent
081499f355
commit
56fecf1832
5
.gitignore
vendored
5
.gitignore
vendored
@@ -45,6 +45,11 @@ libekmfweb/detect-openssl-version.dep
|
||||
libekmfweb/libekmfweb.so
|
||||
libekmfweb/libekmfweb.so.1
|
||||
libekmfweb/libekmfweb.so.1.0
|
||||
libkmipclient/check-dep-libkmipclient
|
||||
libkmipclient/detect-openssl-version.dep
|
||||
libkmipclient/libkmipclient.so
|
||||
libkmipclient/libkmipclient.so.1
|
||||
libkmipclient/libkmipclient.so.1.0
|
||||
libseckey/check-dep-libseckey
|
||||
libseckey/detect-openssl-version.dep
|
||||
libutil/*_example
|
||||
|
||||
2
Makefile
2
Makefile
@@ -4,7 +4,7 @@ ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/ar
|
||||
include common.mak
|
||||
|
||||
LIB_DIRS = libvtoc libutil libzds libdasd libvmdump libccw libvmcp libekmfweb \
|
||||
libseckey
|
||||
libseckey libkmipclient
|
||||
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
||||
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
||||
vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
||||
|
||||
23
README.md
23
README.md
@@ -249,6 +249,12 @@ Package contents
|
||||
Management Foundation - Web Edition, and is used to manage keys in an
|
||||
enterprise.
|
||||
|
||||
* libkmipclient:
|
||||
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.
|
||||
|
||||
* hsci:
|
||||
Manage HiperSockets Converged Interfaces (HSCI).
|
||||
|
||||
@@ -285,11 +291,14 @@ build options:
|
||||
| pfm | `HAVE_PFM` | cpacfstats |
|
||||
| net-snmp | `HAVE_SNMP` | osasnmpd |
|
||||
| glibc-static | `HAVE_LIBC_STATIC` | zfcpdump |
|
||||
| openssl | `HAVE_OPENSSL` | genprotimg, zkey, libekmfweb |
|
||||
| openssl | `HAVE_OPENSSL` | genprotimg, zkey, libekmfweb, |
|
||||
| | | libkmipclient |
|
||||
| cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup |
|
||||
| json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb |
|
||||
| json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb, |
|
||||
| | | libkmipclient |
|
||||
| glib2 | `HAVE_GLIB2` | genprotimg |
|
||||
| libcurl | `HAVE_LIBCURL` | genprotimg, libekmfweb |
|
||||
| libcurl | `HAVE_LIBCURL` | genprotimg, libekmfweb, libkmipclient |
|
||||
| libxml2 | `HAVE_LIBXML2` | libkmipclient |
|
||||
| systemd | `HAVE_SYSTEMD` | hsavmcore |
|
||||
|
||||
This table lists additional build or install options:
|
||||
@@ -444,3 +453,11 @@ the different tools are provided:
|
||||
add `HAVE_SYSTEMD=0` to the make invocation.
|
||||
Tip: you may skip the hsavmcore build by adding `HAVE_FUSE=0`
|
||||
to the make invocation.
|
||||
|
||||
* libkmipclient:
|
||||
For building the libkmipclient shared library you need openssl version 1.1.1
|
||||
or newer installed (openssl-devel.rpm). Also required are json-c version 0.13
|
||||
or newer (json-c-devel.rpm), libxml2 version 2.9.10 or newer
|
||||
(libxml2-devel.rpm), and libcurl version 7.59 or newer (libcurl-devel.rpm).
|
||||
Tip: you may skip the libkmipclient build by adding `HAVE_OPENSSL=0`,
|
||||
`HAVE_JSONC=0`, `HAVE_LIBXML2=0`, or `HAVE_LIBCURL=0` to the make invocation.
|
||||
|
||||
@@ -378,6 +378,10 @@ $(rootdir)/libseckey/libseckey.a: $(rootdir)/libseckey
|
||||
$(MAKE) -C $(rootdir)/libseckey/ libseckey.a
|
||||
.PHONY: $(rootdir)/libseckey
|
||||
|
||||
$(rootdir)/libkmipclient/libkmipclient.so: $(rootdir)/libkmipclient
|
||||
$(MAKE) -C $(rootdir)/libkmipclient/ libkmipclient.so
|
||||
.PHONY: $(rootdir)/libkmipclient
|
||||
|
||||
$(rootdir)/zipl/boot/data.o:
|
||||
$(MAKE) -C $(rootdir)/zipl/boot/ data.o
|
||||
|
||||
|
||||
2066
include/kmipclient/kmipclient.h
Normal file
2066
include/kmipclient/kmipclient.h
Normal file
File diff suppressed because it is too large
Load Diff
130
libkmipclient/Makefile
Normal file
130
libkmipclient/Makefile
Normal file
@@ -0,0 +1,130 @@
|
||||
include ../common.mak
|
||||
|
||||
VERSION = 1.0
|
||||
VERM = $(shell echo $(VERSION) | cut -d '.' -f 1)
|
||||
|
||||
ifneq (${HAVE_OPENSSL},0)
|
||||
ifneq (${HAVE_JSONC},0)
|
||||
ifneq (${HAVE_LIBXML2},0)
|
||||
ifneq (${HAVE_LIBCURL},0)
|
||||
BUILD_TARGETS += libkmipclient.so.$(VERSION)
|
||||
INSTALL_TARGETS += install-libkmipclient.so.$(VERSION)
|
||||
else
|
||||
BUILD_TARGETS += skip-libkmipclient-curl
|
||||
INSTALL_TARGETS += skip-libkmipclient-curl
|
||||
endif
|
||||
else
|
||||
BUILD_TARGETS += skip-libkmipclient-xml
|
||||
INSTALL_TARGETS += skip-libkmipclient-xml
|
||||
endif
|
||||
else
|
||||
BUILD_TARGETS += skip-libkmipclient-jsonc
|
||||
INSTALL_TARGETS += skip-libkmipclient-jsonc
|
||||
endif
|
||||
else
|
||||
BUILD_TARGETS += skip-libkmipclient-openssl
|
||||
INSTALL_TARGETS += skip-libkmipclient-openssl
|
||||
endif
|
||||
|
||||
TMPFILE := $(shell mktemp)
|
||||
|
||||
detect-openssl-version.dep:
|
||||
echo "#include <openssl/opensslv.h>" > $(TMPFILE)
|
||||
echo "#include <openssl/evp.h>" >> $(TMPFILE)
|
||||
echo "#ifndef OPENSSL_VERSION_PREREQ" >> $(TMPFILE)
|
||||
echo " #if defined(OPENSSL_VERSION_MAJOR) && defined(OPENSSL_VERSION_MINOR)" >> $(TMPFILE)
|
||||
echo " #define OPENSSL_VERSION_PREREQ(maj, min) \\" >> $(TMPFILE)
|
||||
echo " ((OPENSSL_VERSION_MAJOR << 16) + \\" >> $(TMPFILE)
|
||||
echo " OPENSSL_VERSION_MINOR >= ((maj) << 16) + (min))" >> $(TMPFILE)
|
||||
echo " #else" >> $(TMPFILE)
|
||||
echo " #define OPENSSL_VERSION_PREREQ(maj, min) \\" >> $(TMPFILE)
|
||||
echo " (OPENSSL_VERSION_NUMBER >= (((maj) << 28) | \\" >> $(TMPFILE)
|
||||
echo " ((min) << 20)))" >> $(TMPFILE)
|
||||
echo " #endif" >> $(TMPFILE)
|
||||
echo "#endif" >> $(TMPFILE)
|
||||
echo "#if !OPENSSL_VERSION_PREREQ(1, 1)" >> $(TMPFILE)
|
||||
echo " #error openssl version 1.1 is required" >> $(TMPFILE)
|
||||
echo "#endif" >> $(TMPFILE)
|
||||
echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE)
|
||||
echo " EVP_PKEY_meth_remove(NULL);" >> $(TMPFILE)
|
||||
echo "}" >> $(TMPFILE)
|
||||
mv $(TMPFILE) $@
|
||||
|
||||
check-dep-libkmipclient: detect-openssl-version.dep
|
||||
$(call check_dep, \
|
||||
"libkmipclient", \
|
||||
"detect-openssl-version.dep", \
|
||||
"openssl-devel version >= 1.1.1", \
|
||||
"HAVE_OPENSSL=0", \
|
||||
-I. -lcrypto -DOPENSSL_SUPPRESS_DEPRECATED)
|
||||
$(call check_dep, \
|
||||
"libkmipclient", \
|
||||
"json-c/json.h", \
|
||||
"json-c-devel", \
|
||||
"HAVE_JSONC=0")
|
||||
$(call check_dep, \
|
||||
"libkmipclient", \
|
||||
"libxml/tree.h", \
|
||||
"libxml2-devel", \
|
||||
"HAVE_LIBXML2=0", \
|
||||
`xml2-config --cflags` `xml2-config --libs`)
|
||||
$(call check_dep, \
|
||||
"libkmipclient", \
|
||||
"curl/curl.h", \
|
||||
"libcurl-devel", \
|
||||
"HAVE_LIBCURL=0" \
|
||||
`curl-config --cflags` `curl-config --libs`)
|
||||
curl-config --ssl-backends | grep OpenSSL >/dev/null 2>&1 || { echo "Error: libcurl is not built with the OpenSSL backend"; exit 1; }
|
||||
touch check-dep-libkmipclient
|
||||
|
||||
skip-libkmipclient-openssl:
|
||||
echo " SKIP libkmipclient due to HAVE_OPENSSL=0"
|
||||
|
||||
skip-libkmipclient-jsonc:
|
||||
echo " SKIP libkmipclient due to HAVE_JSONC=0"
|
||||
|
||||
skip-libkmipclient-xml:
|
||||
echo " SKIP libkmipclient due to HAVE_LIBXML2=0"
|
||||
|
||||
skip-libkmipclient-curl:
|
||||
echo " SKIP libkmipclient due to HAVE_LIBCURL=0"
|
||||
|
||||
all: $(BUILD_TARGETS)
|
||||
|
||||
kmip.o: check-dep-libkmipclient kmip.c kmip.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
request.o: check-dep-libkmipclient request.c kmip.h names.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
response.o: check-dep-libkmipclient response.c kmip.h names.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
attribute.o: check-dep-libkmipclient attribute.c kmip.h names.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
key.o: check-dep-libkmipclient key.c kmip.h names.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
ttlv.o: check-dep-libkmipclient ttlv.c kmip.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
json.o: check-dep-libkmipclient json.c kmip.h names.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
xml.o: check-dep-libkmipclient xml.c kmip.h names.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
https.o: check-dep-libkmipclient https.c kmip.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
tls.o: check-dep-libkmipclient tls.c kmip.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
names.o: check-dep-libkmipclient names.c names.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
utils.o: check-dep-libkmipclient utils.c names.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
|
||||
libkmipclient.so.$(VERSION): ALL_CFLAGS += -fPIC `xml2-config --cflags` `curl-config --cflags`
|
||||
libkmipclient.so.$(VERSION): LDLIBS = -ljson-c -lcrypto -lssl `xml2-config --libs` `curl-config --libs`
|
||||
libkmipclient.so.$(VERSION): ALL_LDFLAGS += -shared -Wl,--version-script=libkmipclient.map \
|
||||
-Wl,-z,defs,-Bsymbolic -Wl,-soname,libkmipclient.so.$(VERM)
|
||||
libkmipclient.so.$(VERSION): kmip.o request.o response.o attribute.o key.o ttlv.o json.o \
|
||||
xml.o https.o tls.o names.o utils.o
|
||||
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
|
||||
ln -srf libkmipclient.so.$(VERSION) libkmipclient.so.$(VERM)
|
||||
ln -srf libkmipclient.so.$(VERSION) libkmipclient.so
|
||||
|
||||
install-libkmipclient.so.$(VERSION): libkmipclient.so.$(VERSION)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 -T libkmipclient.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libkmipclient.so.$(VERSION)
|
||||
ln -srf $(DESTDIR)$(SOINSTALLDIR)/libkmipclient.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libkmipclient.so.$(VERM)
|
||||
ln -srf $(DESTDIR)$(SOINSTALLDIR)/libkmipclient.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libkmipclient.so
|
||||
$(INSTALL) -d -m 770 $(DESTDIR)$(USRINCLUDEDIR)/kmipclient
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 $(rootdir)include/kmipclient/kmipclient.h $(DESTDIR)$(USRINCLUDEDIR)/kmipclient
|
||||
|
||||
install: all $(INSTALL_TARGETS)
|
||||
|
||||
clean:
|
||||
rm -f *.o libkmipclient.so* check-dep-libkmipclient detect-openssl-version.dep
|
||||
|
||||
.PHONY: all install clean skip-libkmipclient-openssl skip-libkmipclient-jsonc \
|
||||
skip-libkmipclient-xml skip-libkmipclient-curl install-libkmipclient.so.$(VERSION)
|
||||
4011
libkmipclient/attribute.c
Normal file
4011
libkmipclient/attribute.c
Normal file
File diff suppressed because it is too large
Load Diff
848
libkmipclient/https.c
Normal file
848
libkmipclient/https.c
Normal file
@@ -0,0 +1,848 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
649
libkmipclient/json.c
Normal file
649
libkmipclient/json.c
Normal file
@@ -0,0 +1,649 @@
|
||||
/*
|
||||
* 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 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)
|
||||
{
|
||||
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 (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(tag_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, 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);
|
||||
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;
|
||||
}
|
||||
|
||||
1439
libkmipclient/key.c
Normal file
1439
libkmipclient/key.c
Normal file
File diff suppressed because it is too large
Load Diff
1595
libkmipclient/kmip.c
Normal file
1595
libkmipclient/kmip.c
Normal file
File diff suppressed because it is too large
Load Diff
111
libkmipclient/kmip.h
Normal file
111
libkmipclient/kmip.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef KMIP_H
|
||||
#define KMIP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <json-c/json.h>
|
||||
#include <libxml/tree.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "kmipclient/kmipclient.h"
|
||||
|
||||
/* KMIP Connection related structures */
|
||||
#define KMIP_DEFAULT_PROTOCOL_VERSION_MAJOR 1
|
||||
#define KMIP_DEFAULT_PROTOCOL_VERSION_MINOR 0
|
||||
|
||||
struct kmip_connection {
|
||||
struct kmip_conn_config config;
|
||||
union {
|
||||
struct {
|
||||
SSL_CTX *ssl_ctx;
|
||||
SSL *ssl;
|
||||
BIO *bio;
|
||||
} plain_tls;
|
||||
struct {
|
||||
CURL **curl;
|
||||
struct curl_slist *headers;
|
||||
} https;
|
||||
};
|
||||
};
|
||||
|
||||
/* KMIP node related structures */
|
||||
struct kmip_node {
|
||||
enum kmip_tag tag;
|
||||
enum kmip_type type;
|
||||
unsigned int length;
|
||||
char *name; /* optional, only used for JSON and XML encoding */
|
||||
union {
|
||||
struct kmip_node *structure_value;
|
||||
int32_t integer_value;
|
||||
int64_t long_value;
|
||||
BIGNUM *big_integer_value;
|
||||
uint32_t enumeration_value;
|
||||
bool boolean_value;
|
||||
char *text_value;
|
||||
unsigned char *bytes_value;
|
||||
int64_t date_time_value;
|
||||
uint32_t interval_value;
|
||||
int64_t date_time_ext_value;
|
||||
};
|
||||
struct kmip_node *parent;
|
||||
struct kmip_node *next;
|
||||
volatile unsigned long ref_count;
|
||||
};
|
||||
|
||||
/* Attribute related internal functions */
|
||||
int kmip_v2_attr_from_v1_attr(struct kmip_node *v1_attr,
|
||||
struct kmip_node **v2_attr);
|
||||
int kmip_v1_attr_from_v2_attr(struct kmip_node *v2_attr,
|
||||
struct kmip_node **v1_attr);
|
||||
|
||||
char *kmip_build_v1_custom_attr_name(const char *vendor_id,
|
||||
const char *attr_name);
|
||||
|
||||
struct kmip_node *kmip_new_attribute_name_v1(
|
||||
const struct kmip_node *v2_attr_ref);
|
||||
int kmip_get_attribute_name_v1(const struct kmip_node *node,
|
||||
struct kmip_node **v2_attr_ref);
|
||||
|
||||
/* Connection related internal functions */
|
||||
int kmip_connection_tls_init(struct kmip_connection *connection, bool debug);
|
||||
int kmip_connection_tls_perform(struct kmip_connection *connection,
|
||||
struct kmip_node *request,
|
||||
struct kmip_node **response,
|
||||
bool debug);
|
||||
void kmip_connection_tls_term(struct kmip_connection *connection);
|
||||
|
||||
int kmip_connection_https_init(struct kmip_connection *connection, bool debug);
|
||||
int kmip_connection_https_perform(struct kmip_connection *connection,
|
||||
struct kmip_node *request,
|
||||
struct kmip_node **response,
|
||||
bool debug);
|
||||
void kmip_connection_https_term(struct kmip_connection *connection);
|
||||
|
||||
/* KIMP decoding and encoding internal functions */
|
||||
int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node,
|
||||
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);
|
||||
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);
|
||||
int kmip_encode_xml(const struct kmip_node *node, xmlNode **xml, bool debug);
|
||||
|
||||
#endif
|
||||
225
libkmipclient/libkmipclient.map
Normal file
225
libkmipclient/libkmipclient.map
Normal file
@@ -0,0 +1,225 @@
|
||||
LIBKMIPCLIENT_1.0 {
|
||||
global:
|
||||
kmip_node_clone;
|
||||
kmip_node_upref;
|
||||
kmip_node_free;
|
||||
kmip_node_get_tag;
|
||||
kmip_node_get_type;
|
||||
kmip_node_get_name;
|
||||
kmip_node_dump;
|
||||
kmip_node_new_structure;
|
||||
kmip_node_new_structure_va;
|
||||
kmip_node_get_structure_element_count;
|
||||
kmip_node_get_structure_element_by_index;
|
||||
kmip_node_get_structure_element_by_tag_count;
|
||||
kmip_node_get_structure_element_by_tag;
|
||||
kmip_node_add_structure_element;
|
||||
kmip_node_add_structure_elements;
|
||||
kmip_node_new_integer;
|
||||
kmip_node_get_integer;
|
||||
kmip_node_new_long;
|
||||
kmip_node_get_long;
|
||||
kmip_node_new_bigint;
|
||||
kmip_node_get_bigint;
|
||||
kmip_node_new_enumeration;
|
||||
kmip_node_get_enumeration;
|
||||
kmip_node_new_boolean;
|
||||
kmip_node_get_boolean;
|
||||
kmip_node_new_text_string;
|
||||
kmip_node_get_text_string;
|
||||
kmip_node_new_byte_string;
|
||||
kmip_node_get_byte_string;
|
||||
kmip_node_new_date_time;
|
||||
kmip_node_get_date_time;
|
||||
kmip_node_new_interval;
|
||||
kmip_node_get_interval;
|
||||
kmip_node_new_date_time_ext;
|
||||
kmip_node_get_date_time_ext;
|
||||
kmip_set_default_protocol_version;
|
||||
kmip_get_default_protocol_version;
|
||||
kmip_new_protocol_version;
|
||||
kmip_new_profile_version;
|
||||
kmip_new_request_header;
|
||||
kmip_new_request_batch_item;
|
||||
kmip_new_request;
|
||||
kmip_new_request_va;
|
||||
kmip_new_query_request_payload;
|
||||
kmip_new_query_request_payload_va;
|
||||
kmip_new_discover_versions_payload;
|
||||
kmip_new_discover_versions_payload_va;
|
||||
kmip_new_protection_storage_masks;
|
||||
kmip_new_protection_storage_masks_va;
|
||||
kmip_new_create_request_payload;
|
||||
kmip_new_create_request_payload_va;
|
||||
kmip_new_get_attribute_list_request_payload;
|
||||
kmip_new_get_attributes_request_payload;
|
||||
kmip_new_get_attributes_request_payload_va;
|
||||
kmip_new_add_attribute_request_payload;
|
||||
kmip_new_modify_attribute_request_payload;
|
||||
kmip_new_set_attribute_v2_request_payload;
|
||||
kmip_new_delete_attribute_request_payload;
|
||||
kmip_new_activate_request_payload;
|
||||
kmip_new_destroy_request_payload;
|
||||
kmip_new_archive_request_payload;
|
||||
kmip_new_recover_request_payload;
|
||||
kmip_new_revoke_request_payload;
|
||||
kmip_new_locate_request_payload;
|
||||
kmip_new_locate_request_payload_va;
|
||||
kmip_new_register_request_payload;
|
||||
kmip_new_register_request_payload_va;
|
||||
kmip_new_get_request_payload;
|
||||
kmip_get_protocol_version;
|
||||
kmip_get_profile_version;
|
||||
kmip_get_response_header;
|
||||
kmip_get_response_batch_item;
|
||||
kmip_get_response;
|
||||
kmip_get_query_response_payload;
|
||||
kmip_get_discover_versions_response_payload;
|
||||
kmip_get_create_response_payload;
|
||||
kmip_get_get_attribute_list_response_payload;
|
||||
kmip_get_get_attributes_response_payload;
|
||||
kmip_get_add_attribute_response_payload;
|
||||
kmip_get_modify_attribute_response_payload;
|
||||
kmip_get_set_attribute_v2_response_payload;
|
||||
kmip_get_delete_attribute_response_payload;
|
||||
kmip_get_activate_response_payload;
|
||||
kmip_get_destroy_response_payload;
|
||||
kmip_get_archive_response_payload;
|
||||
kmip_get_recover_response_payload;
|
||||
kmip_get_revoke_response_payload;
|
||||
kmip_get_activate_response_payload;
|
||||
kmip_get_locate_response_payload;
|
||||
kmip_get_register_response_payload;
|
||||
kmip_get_get_response_payload;
|
||||
kmip_new_attributes;
|
||||
kmip_new_attributes_va;
|
||||
kmip_get_attributes;
|
||||
kmip_new_vendor_attribute;
|
||||
kmip_get_vendor_attribute;
|
||||
kmip_new_attribute_reference;
|
||||
kmip_get_attribute_reference;
|
||||
kmip_new_current_new_attribute;
|
||||
kmip_new_unique_identifier;
|
||||
kmip_get_unique_identifier;
|
||||
kmip_new_name;
|
||||
kmip_get_name;
|
||||
kmip_new_alternative_name;
|
||||
kmip_get_alternative_name;
|
||||
kmip_new_object_type;
|
||||
kmip_get_object_type;
|
||||
kmip_new_cryptographic_algorithm;
|
||||
kmip_get_cryptographic_algorithm;
|
||||
kmip_new_cryptographic_length;
|
||||
kmip_get_cryptographic_length;
|
||||
kmip_new_certificate_type;
|
||||
kmip_get_certificate_type;
|
||||
kmip_new_cryptographic_usage_mask;
|
||||
kmip_get_cryptographic_usage_mask;
|
||||
kmip_new_state;
|
||||
kmip_get_state;
|
||||
kmip_new_initial_date;
|
||||
kmip_get_initial_date;
|
||||
kmip_new_activation_date;
|
||||
kmip_get_activation_date;
|
||||
kmip_new_deactivation_date;
|
||||
kmip_get_deactivation_date;
|
||||
kmip_new_destroy_date;
|
||||
kmip_get_destroy_date;
|
||||
kmip_new_compromise_date;
|
||||
kmip_get_compromise_date;
|
||||
kmip_new_compromise_occurrence_date;
|
||||
kmip_get_compromise_occurrence_date;
|
||||
kmip_new_last_change_date;
|
||||
kmip_get_last_change_date;
|
||||
kmip_new_original_creation_date;
|
||||
kmip_get_original_creation_date;
|
||||
kmip_new_archive_date;
|
||||
kmip_get_archive_date;
|
||||
kmip_new_process_start_date;
|
||||
kmip_get_process_start_date;
|
||||
kmip_new_protect_stop_date;
|
||||
kmip_get_protect_stop_date;
|
||||
kmip_new_cryptographic_parameters;
|
||||
kmip_get_cryptographic_parameter;
|
||||
kmip_new_cryptographic_domain_parameters;
|
||||
kmip_get_cryptographic_domain_parameters;
|
||||
kmip_new_digital_signature_algorithm;
|
||||
kmip_get_digital_signature_algorithm;
|
||||
kmip_new_object_group;
|
||||
kmip_get_object_group;
|
||||
kmip_new_revocation_reason;
|
||||
kmip_get_revocation_reason;
|
||||
kmip_new_contact_information;
|
||||
kmip_get_contact_information;
|
||||
kmip_new_description;
|
||||
kmip_get_description;
|
||||
kmip_new_comment;
|
||||
kmip_get_comment;
|
||||
kmip_new_key_format_type;
|
||||
kmip_get_key_format_type;
|
||||
kmip_new_protection_level;
|
||||
kmip_get_protection_level;
|
||||
kmip_new_protection_period;
|
||||
kmip_get_protection_period;
|
||||
kmip_new_protection_storage_mask;
|
||||
kmip_get_protection_storage_mask;
|
||||
kmip_new_fresh;
|
||||
kmip_get_fresh;
|
||||
kmip_new_key_value_present;
|
||||
kmip_get_key_value_present;
|
||||
kmip_new_short_unique_identifier;
|
||||
kmip_get_short_unique_identifier;
|
||||
kmip_new_application_specific_information;
|
||||
kmip_get_application_specific_information;
|
||||
kmip_new_key_value_location;
|
||||
kmip_get_key_value_location;
|
||||
kmip_new_digest;
|
||||
kmip_get_digest;
|
||||
kmip_new_sensitive;
|
||||
kmip_get_sensitive;
|
||||
kmip_new_always_sensitive;
|
||||
kmip_get_always_sensitive;
|
||||
kmip_new_extractable;
|
||||
kmip_get_extractable;
|
||||
kmip_new_never_extractable;
|
||||
kmip_get_never_extractable;
|
||||
kmip_new_link;
|
||||
kmip_get_link;
|
||||
kmip_new_linked_object_identifier;
|
||||
kmip_get_linked_object_identifier;
|
||||
kmip_new_operation_policy_name;
|
||||
kmip_get_operation_policy_name;
|
||||
kmip_new_lease_time;
|
||||
kmip_get_lease_time;
|
||||
kmip_new_key_block;
|
||||
kmip_get_key_block;
|
||||
kmip_new_key_value;
|
||||
kmip_new_key_value_va;
|
||||
kmip_get_key_value;
|
||||
kmip_new_key_wrapping_data;
|
||||
kmip_get_key_wrapping_data;
|
||||
kmip_new_key_wrapping_specification;
|
||||
kmip_new_key_wrapping_specification_va;
|
||||
kmip_get_key_wrapping_specification;
|
||||
kmip_new_key_info;
|
||||
kmip_get_key_info;
|
||||
kmip_new_transparent_symmetric_key;
|
||||
kmip_get_transparent_symmetric_key;
|
||||
kmip_new_transparent_rsa_public_key;
|
||||
kmip_get_transparent_rsa_public_key;
|
||||
kmip_new_pkcs1_public_key;
|
||||
kmip_get_pkcs1_public_key;
|
||||
kmip_new_pkcs8_public_key;
|
||||
kmip_get_pkcs8_public_key;
|
||||
kmip_new_raw_key;
|
||||
kmip_get_raw_key;
|
||||
kmip_new_symmetric_key;
|
||||
kmip_get_symmetric_key;
|
||||
kmip_new_public_key;
|
||||
kmip_get_public_key;
|
||||
kmip_connection_new;
|
||||
kmip_connection_perform;
|
||||
kmip_connection_free;
|
||||
kmip_connection_get_server_cert;
|
||||
local: *;
|
||||
};
|
||||
2253
libkmipclient/names.c
Normal file
2253
libkmipclient/names.c
Normal file
File diff suppressed because it is too large
Load Diff
41
libkmipclient/names.h
Normal file
41
libkmipclient/names.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef NAMES_H
|
||||
#define NAMES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "kmip.h"
|
||||
|
||||
struct kmip_enum {
|
||||
uint32_t val;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
|
||||
const struct kmip_enum *kmip_enum_info_by_tag(enum kmip_tag tag);
|
||||
bool kmip_is_tag_mask(enum kmip_tag tag);
|
||||
int kmip_enum_value_by_name_or_hex(const struct kmip_enum *info,
|
||||
const char *name, uint32_t *value);
|
||||
|
||||
const char *kmip_enum_name_by_tag_value(enum kmip_tag tag, uint32_t val);
|
||||
int kmip_enum_value_by_tag_name_or_hex(enum kmip_tag tag, const char *name,
|
||||
uint32_t *value);
|
||||
|
||||
const char *kmip_tag_name_by_tag(enum kmip_tag tag);
|
||||
const char *kmip_tag_name_or_hex_by_tag(enum kmip_tag tag, char tmp_buff[20]);
|
||||
enum kmip_tag kmip_tag_by_name_or_hex(const char *name);
|
||||
|
||||
const char *kmip_type_name_by_type(enum kmip_type type);
|
||||
enum kmip_type kmip_type_by_name_or_hex(const char *name);
|
||||
|
||||
const char *kmip_v1_attr_name_by_tag(enum kmip_tag attr_tag);
|
||||
enum kmip_tag kmip_attr_tag_by_v1_attr_name(const char *name);
|
||||
|
||||
#endif
|
||||
1795
libkmipclient/request.c
Normal file
1795
libkmipclient/request.c
Normal file
File diff suppressed because it is too large
Load Diff
1377
libkmipclient/response.c
Normal file
1377
libkmipclient/response.c
Normal file
File diff suppressed because it is too large
Load Diff
528
libkmipclient/tls.c
Normal file
528
libkmipclient/tls.c
Normal file
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
* 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/x509v3.h>
|
||||
#include <openssl/opensslv.h>
|
||||
|
||||
#include "kmip.h"
|
||||
#include "utils.h"
|
||||
|
||||
#ifndef OPENSSL_VERSION_PREREQ
|
||||
#if defined(OPENSSL_VERSION_MAJOR) && defined(OPENSSL_VERSION_MINOR)
|
||||
#define OPENSSL_VERSION_PREREQ(maj, min) \
|
||||
((OPENSSL_VERSION_MAJOR << 16) + \
|
||||
OPENSSL_VERSION_MINOR >= ((maj) << 16) + (min))
|
||||
#else
|
||||
#define OPENSSL_VERSION_PREREQ(maj, min) \
|
||||
(OPENSSL_VERSION_NUMBER >= (((maj) << 28) | \
|
||||
((min) << 20)))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Verify the pinned public key of the server of a plain TLS KMIP connection
|
||||
*
|
||||
* @param conn the KMIP connection to free
|
||||
* @param cert_pubkey the server certificate's public key
|
||||
* @param debug if true, debug messages are printed
|
||||
*/
|
||||
static int kmip_connection_tls_verify_pinned_pubkey(
|
||||
struct kmip_connection *conn,
|
||||
EVP_PKEY *cert_pubkey, bool debug)
|
||||
{
|
||||
EVP_PKEY *pinned_key = NULL;
|
||||
int rc = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(conn->config.tls_pinned_pubkey, "r");
|
||||
if (fp == NULL) {
|
||||
rc = -errno;
|
||||
kmip_debug(debug, "Failed to read pinned public key '%s': %s",
|
||||
conn->config.tls_pinned_pubkey, strerror(-rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
pinned_key = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
|
||||
fclose(fp);
|
||||
|
||||
if (pinned_key == NULL) {
|
||||
kmip_debug(debug, "PEM_read_PUBKEY failed: '%s'",
|
||||
conn->config.tls_pinned_pubkey);
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#if !OPENSSL_VERSION_PREREQ(3, 0)
|
||||
if (EVP_PKEY_cmp(pinned_key, cert_pubkey) != 1) {
|
||||
#else
|
||||
if (EVP_PKEY_eq(pinned_key, cert_pubkey) != 1) {
|
||||
#endif
|
||||
kmip_debug(debug, "Server public key does not match the pinned "
|
||||
"public key '%s'", conn->config.tls_pinned_pubkey);
|
||||
rc = -EPERM;
|
||||
}
|
||||
|
||||
EVP_PKEY_free(pinned_key);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the pinned server certificate key of the server of a plain TLS KMIP
|
||||
* connection
|
||||
*
|
||||
* @param conn the KMIP connection to free
|
||||
* @param server_cert the server certificate
|
||||
* @param debug if true, debug messages are printed
|
||||
*/
|
||||
static int kmip_connection_tls_verify_pinned_cert(
|
||||
struct kmip_connection *conn,
|
||||
X509 *server_cert, bool debug)
|
||||
{
|
||||
X509 *pinned_cert = NULL;
|
||||
int rc = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(conn->config.tls_server_cert, "r");
|
||||
if (fp == NULL) {
|
||||
rc = -errno;
|
||||
kmip_debug(debug, "Failed to read pinned server cert: %s",
|
||||
conn->config.tls_server_cert, strerror(-rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
pinned_cert = PEM_read_X509(fp, NULL, NULL, NULL);
|
||||
fclose(fp);
|
||||
|
||||
if (pinned_cert == NULL) {
|
||||
kmip_debug(debug, "PEM_read_X509 failed: '%s'",
|
||||
conn->config.tls_server_cert);
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (X509_cmp(pinned_cert, server_cert) != 0) {
|
||||
kmip_debug(debug, "Server certificate does not match the "
|
||||
"pinned certificate '%s'",
|
||||
conn->config.tls_server_cert);
|
||||
rc = -EPERM;
|
||||
}
|
||||
|
||||
X509_free(pinned_cert);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the issuer certificate key of the server of a plain TLS KMIP
|
||||
* connection
|
||||
*
|
||||
* @param conn the KMIP connection to free
|
||||
* @param server_cert the server certificate
|
||||
* @param debug if true, debug messages are printed
|
||||
*/
|
||||
static int kmip_connection_tls_verify_issuer_cert(
|
||||
struct kmip_connection *conn,
|
||||
X509 *server_cert, bool debug)
|
||||
{
|
||||
X509 *issuer_cert = NULL;
|
||||
int rc = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(conn->config.tls_issuer_cert, "r");
|
||||
if (fp == NULL) {
|
||||
rc = -errno;
|
||||
kmip_debug(debug, "Failed to read issuer cert '%s': %s",
|
||||
conn->config.tls_issuer_cert, strerror(-rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
issuer_cert = PEM_read_X509(fp, NULL, NULL, NULL);
|
||||
fclose(fp);
|
||||
|
||||
if (issuer_cert == NULL) {
|
||||
kmip_debug(debug, "PEM_read_X509 failed: '%s'",
|
||||
conn->config.tls_issuer_cert);
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (X509_check_issued(issuer_cert, server_cert) != X509_V_OK) {
|
||||
kmip_debug(debug, "The server certificate was not issued by "
|
||||
"certificate '%s'", conn->config.tls_issuer_cert);
|
||||
rc = -EPERM;
|
||||
}
|
||||
|
||||
X509_free(issuer_cert);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the server of a plain TLS KMIP connection
|
||||
*
|
||||
* @param conn the KMIP connection to free
|
||||
* @param debug if true, debug messages are printed
|
||||
*/
|
||||
static int kmip_connection_tls_verify_server(struct kmip_connection *conn,
|
||||
bool debug)
|
||||
{
|
||||
X509 *server_cert;
|
||||
int rc;
|
||||
|
||||
server_cert = SSL_get_peer_certificate(conn->plain_tls.ssl);
|
||||
if (server_cert == NULL) {
|
||||
kmip_debug(debug, "SSL_get_peer_certificate failed");
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (conn->config.tls_issuer_cert != NULL) {
|
||||
rc = kmip_connection_tls_verify_issuer_cert(conn, server_cert,
|
||||
debug);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug,
|
||||
"kmip_connection_tls_verify_issuer_cert "
|
||||
"failed");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->config.tls_server_cert != NULL) {
|
||||
rc = kmip_connection_tls_verify_pinned_cert(conn, server_cert,
|
||||
debug);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug,
|
||||
"kmip_connection_tls_verify_pinned_cert "
|
||||
"failed");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->config.tls_pinned_pubkey != NULL) {
|
||||
rc = kmip_connection_tls_verify_pinned_pubkey(conn,
|
||||
X509_get0_pubkey(server_cert),
|
||||
debug);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug,
|
||||
"kmip_connection_tls_pinned_pubkey failed");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
if (server_cert != NULL)
|
||||
X509_free(server_cert);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new plain TLS connection to a KMIP server.
|
||||
*
|
||||
* @param conn 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_tls_init(struct kmip_connection *conn, bool debug)
|
||||
{
|
||||
char *hostname = NULL, *port = NULL, *tok;
|
||||
struct stat sb;
|
||||
int rc;
|
||||
|
||||
if (conn == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
conn->plain_tls.ssl_ctx = SSL_CTX_new(TLS_client_method());
|
||||
if (conn->plain_tls.ssl_ctx == NULL) {
|
||||
kmip_debug(debug, "SSL_CTX_new failed");
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (SSL_CTX_use_certificate_file(conn->plain_tls.ssl_ctx,
|
||||
conn->config.tls_client_cert,
|
||||
SSL_FILETYPE_PEM) != 1) {
|
||||
kmip_debug(debug, "Loading the client certificate from '%s' "
|
||||
"failed", conn->config.tls_client_cert);
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (SSL_CTX_use_PrivateKey(conn->plain_tls.ssl_ctx,
|
||||
conn->config.tls_client_key) != 1) {
|
||||
kmip_debug(debug, "Setting the client key from PKEY %p "
|
||||
"failed", conn->config.tls_client_key);
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
rc = -EIO;
|
||||
goto 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)) {
|
||||
if (SSL_CTX_load_verify_locations(
|
||||
conn->plain_tls.ssl_ctx, NULL,
|
||||
conn->config.tls_ca) != 1) {
|
||||
kmip_debug(debug, "Setting the verify location "
|
||||
"to '%s' failed",
|
||||
conn->config.tls_ca);
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (SSL_CTX_load_verify_locations(
|
||||
conn->plain_tls.ssl_ctx,
|
||||
conn->config.tls_ca, NULL) != 1) {
|
||||
kmip_debug(debug, "Setting the verify location "
|
||||
"to '%s' failed",
|
||||
conn->config.tls_ca);
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conn->plain_tls.bio =
|
||||
BIO_new_buffer_ssl_connect(conn->plain_tls.ssl_ctx);
|
||||
if (conn->plain_tls.bio == NULL) {
|
||||
kmip_debug(debug, "BIO_new_ssl_connect failed");
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
BIO_get_ssl(conn->plain_tls.bio, &conn->plain_tls.ssl);
|
||||
if (conn->plain_tls.ssl == NULL) {
|
||||
kmip_debug(debug, "BIO_get_ssl failed");
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
hostname = strdup(conn->config.server);
|
||||
if (hostname == NULL) {
|
||||
kmip_debug(debug, "strdup failed");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Split port number from hostname, if specified */
|
||||
if (hostname[0] == '[') {
|
||||
/* IPv6 address enclosed in square brackets */
|
||||
tok = strchr(hostname, ']');
|
||||
if (tok == NULL) {
|
||||
kmip_debug(debug, "malformed IPv6 address");
|
||||
rc = -EINVAL;
|
||||
free(hostname);
|
||||
goto out;
|
||||
}
|
||||
tok++;
|
||||
if (*tok == ':') {
|
||||
port = tok + 1;
|
||||
*tok = 0;
|
||||
}
|
||||
} else {
|
||||
/* hostname or IPv4 address */
|
||||
tok = strchr(hostname, ':');
|
||||
if (tok != NULL) {
|
||||
port = tok + 1;
|
||||
*tok = 0;
|
||||
}
|
||||
}
|
||||
|
||||
kmip_debug(debug, "hostname: '%s'", hostname);
|
||||
if (port == NULL) {
|
||||
port = KMIP_DEFAULT_PLAIN_TLS_PORT;
|
||||
kmip_debug(debug, "port: default (%s)", port);
|
||||
} else {
|
||||
kmip_debug(debug, "port: %s", port);
|
||||
}
|
||||
|
||||
if (conn->config.tls_verify_host) {
|
||||
SSL_set_hostflags(conn->plain_tls.ssl,
|
||||
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
|
||||
if (SSL_set1_host(conn->plain_tls.ssl, hostname) != 1) {
|
||||
kmip_debug(debug, "SSL_set1_host failed");
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
SSL_set_verify(conn->plain_tls.ssl, (conn->config.tls_verify_peer ||
|
||||
conn->config.tls_verify_host) ?
|
||||
SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
|
||||
|
||||
if (conn->config.tls_cipher_list != NULL) {
|
||||
if (SSL_set_cipher_list(conn->plain_tls.ssl,
|
||||
conn->config.tls_cipher_list) != 1) {
|
||||
kmip_debug(debug, "SSL_set_cipher_list failed");
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->config.tls13_cipher_list != NULL) {
|
||||
if (SSL_set_ciphersuites(conn->plain_tls.ssl,
|
||||
conn->config.tls13_cipher_list) != 1) {
|
||||
kmip_debug(debug, "SSL_set_ciphersuites failed");
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
SSL_set_mode(conn->plain_tls.ssl, SSL_MODE_AUTO_RETRY);
|
||||
|
||||
BIO_set_conn_hostname(conn->plain_tls.bio, hostname);
|
||||
BIO_set_conn_port(conn->plain_tls.bio, port);
|
||||
|
||||
if (BIO_do_connect(conn->plain_tls.bio) != 1) {
|
||||
kmip_debug(debug, "BIO_do_connect failed");
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
kmip_debug(debug, "TLS connection established using %s",
|
||||
SSL_get_cipher_name(conn->plain_tls.ssl));
|
||||
|
||||
rc = kmip_connection_tls_verify_server(conn, debug);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_connection_tls_verify_server failed");
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
if (rc != 0)
|
||||
kmip_connection_tls_term(conn);
|
||||
if (hostname != NULL)
|
||||
free(hostname);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a request over the KMIP connection
|
||||
*
|
||||
* @param conn n 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_tls_perform(struct kmip_connection *conn,
|
||||
struct kmip_node *request,
|
||||
struct kmip_node **response,
|
||||
bool debug)
|
||||
{
|
||||
size_t size;
|
||||
int rc;
|
||||
|
||||
if (conn == NULL || request == NULL || response == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
*response = NULL;
|
||||
|
||||
/* Send out the request */
|
||||
rc = kmip_encode_ttlv(request, conn->plain_tls.bio, &size, debug);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_encode_ttlv failed");
|
||||
goto out;
|
||||
}
|
||||
if (BIO_flush(conn->plain_tls.bio) != 1) {
|
||||
kmip_debug(debug, "BIO_flush failed");
|
||||
goto out;
|
||||
}
|
||||
kmip_debug(debug, "%lu bytes sent", size);
|
||||
|
||||
/* receive the response */
|
||||
rc = kmip_decode_ttlv(conn->plain_tls.bio, NULL, response, debug);
|
||||
if (rc != 0 || *response == NULL) {
|
||||
kmip_debug(debug, "kmip_decode_ttlv failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
if (rc != 0) {
|
||||
if (BIO_reset(conn->plain_tls.bio) != 1)
|
||||
kmip_debug(debug, "BIO_reset failed");
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates a plain TLS KMIP connection.
|
||||
*
|
||||
* @param conn the KMIP connection to free
|
||||
*/
|
||||
void kmip_connection_tls_term(struct kmip_connection *conn)
|
||||
{
|
||||
if (conn == NULL)
|
||||
return;
|
||||
|
||||
if (conn->plain_tls.bio != NULL) {
|
||||
BIO_ssl_shutdown(conn->plain_tls.bio);
|
||||
BIO_free_all(conn->plain_tls.bio);
|
||||
}
|
||||
if (conn->plain_tls.ssl_ctx != NULL)
|
||||
SSL_CTX_free(conn->plain_tls.ssl_ctx);
|
||||
|
||||
conn->plain_tls.bio = NULL;
|
||||
conn->plain_tls.ssl_ctx = NULL;
|
||||
conn->plain_tls.ssl = NULL;
|
||||
}
|
||||
511
libkmipclient/ttlv.c
Normal file
511
libkmipclient/ttlv.c
Normal file
@@ -0,0 +1,511 @@
|
||||
/*
|
||||
* 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 "utils.h"
|
||||
|
||||
#define KMIP_TTLV_HEADER_LENGTH 8
|
||||
#define KMIP_TTLV_BLOCK_LENGTH 8
|
||||
|
||||
/**
|
||||
* Decode a KMIP node from the data in BIO using the TTLV encoding.
|
||||
*
|
||||
* @param bio the OpenSSL bio to read the data from
|
||||
* @param size Optional: If not NULL:
|
||||
* On entry: The number of bytes available to read
|
||||
* On return: decremented by the number of bytes read
|
||||
* If NULL, it is assumed that we can read from bio
|
||||
* as many bytes as needed.
|
||||
* @param node On return: the decoded node. The newly allocated
|
||||
* node has a reference count of 1.
|
||||
* @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)
|
||||
{
|
||||
unsigned char padding[KMIP_TTLV_BLOCK_LENGTH];
|
||||
unsigned char ttlv[KMIP_TTLV_HEADER_LENGTH];
|
||||
size_t value_len, pad_len;
|
||||
struct kmip_node *n, *e;
|
||||
void *value = NULL;
|
||||
uint32_t int32;
|
||||
uint64_t int64;
|
||||
int rc;
|
||||
|
||||
if (bio == NULL || node == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (size != NULL)
|
||||
kmip_debug(debug, "size: %lu", *size);
|
||||
else
|
||||
kmip_debug(debug, "size: unknown");
|
||||
|
||||
if (size != NULL && *size < sizeof(ttlv)) {
|
||||
kmip_debug(debug, "length %u > available size %lu",
|
||||
sizeof(ttlv), *size);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
if (BIO_read(bio, ttlv, sizeof(ttlv)) != sizeof(ttlv)) {
|
||||
kmip_debug(debug, "BIO_read failed");
|
||||
return -EIO;
|
||||
}
|
||||
if (size != NULL)
|
||||
*size -= sizeof(ttlv);
|
||||
|
||||
n = calloc(1, sizeof(struct kmip_node));
|
||||
if (n == NULL) {
|
||||
kmip_debug(debug, "calloc failed");
|
||||
return -ENOMEM;
|
||||
}
|
||||
n->ref_count = 1;
|
||||
|
||||
/* Tag: 3-byte binary unsigned integer, transmitted big endian */
|
||||
n->tag |= (uint32_t)(ttlv[0] << 16);
|
||||
n->tag |= (uint32_t)(ttlv[1] << 8);
|
||||
n->tag |= (uint32_t)(ttlv[2]);
|
||||
|
||||
/* Type: 1 byte containing a coded value that indicates the data type */
|
||||
n->type = ttlv[3];
|
||||
|
||||
/* Length: 32-bit binary integer, transmitted big-endian */
|
||||
n->length |= (uint32_t)(ttlv[4] << 24);
|
||||
n->length |= (uint32_t)(ttlv[5] << 16);
|
||||
n->length |= (uint32_t)(ttlv[6] << 8);
|
||||
n->length |= (uint32_t)(ttlv[7]);
|
||||
|
||||
kmip_debug(debug, "tag: 0x%x type: 0x%x, length: %u", n->tag, n->type,
|
||||
n->length);
|
||||
|
||||
switch (n->type) {
|
||||
case KMIP_TYPE_STRUCTURE:
|
||||
value_len = n->length;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_BIG_INTEGER:
|
||||
case KMIP_TYPE_TEXT_STRING:
|
||||
case KMIP_TYPE_BYTE_STRING:
|
||||
value_len = n->length;
|
||||
value = calloc(1, value_len + 1);
|
||||
if (value == NULL) {
|
||||
kmip_debug(debug, "calloc failed");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_INTEGER:
|
||||
case KMIP_TYPE_ENUMERATION:
|
||||
case KMIP_TYPE_INTERVAL:
|
||||
value_len = sizeof(int32);
|
||||
value = &int32;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_LONG_INTEGER:
|
||||
case KMIP_TYPE_BOOLEAN:
|
||||
case KMIP_TYPE_DATE_TIME:
|
||||
case KMIP_TYPE_DATE_TIME_EXTENDED:
|
||||
value_len = sizeof(int64);
|
||||
value = &int64;
|
||||
break;
|
||||
|
||||
default:
|
||||
kmip_debug(debug, "unknown type: 0x%x", n->type);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (n->length != value_len) {
|
||||
kmip_debug(debug, "length %u not as expected (%lu)", n->length,
|
||||
value_len);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
if (size != NULL && *size < n->length) {
|
||||
kmip_debug(debug, "length %u > available size %lu", n->length,
|
||||
*size);
|
||||
rc = -EMSGSIZE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (n->type != KMIP_TYPE_STRUCTURE && value_len > 0) {
|
||||
if (BIO_read(bio, value, value_len) != (int)value_len) {
|
||||
kmip_debug(debug, "BIO_read failed");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (size != NULL)
|
||||
*size -= value_len;
|
||||
|
||||
if ((value_len % KMIP_TTLV_BLOCK_LENGTH) != 0) {
|
||||
pad_len = KMIP_TTLV_BLOCK_LENGTH -
|
||||
(value_len % KMIP_TTLV_BLOCK_LENGTH);
|
||||
|
||||
kmip_debug(debug, "pad_len: %lu", pad_len);
|
||||
if (BIO_read(bio, padding, pad_len) != (int)pad_len) {
|
||||
kmip_debug(debug, "BIO_read failed (padding)");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
if (size != NULL)
|
||||
*size -= pad_len;
|
||||
}
|
||||
|
||||
switch (n->type) {
|
||||
case KMIP_TYPE_STRUCTURE:
|
||||
while (value_len > 0) {
|
||||
rc = kmip_decode_ttlv(bio, &value_len, &e, debug);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_decode_ttlv failed: "
|
||||
"rc: %d", rc);
|
||||
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:
|
||||
n->integer_value = be32toh(int32);
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_LONG_INTEGER:
|
||||
n->long_value = be64toh(int64);
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_BIG_INTEGER:
|
||||
rc = kmip_decode_bignum(value, value_len,
|
||||
&n->big_integer_value);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_decode_bignum failed");
|
||||
goto out;
|
||||
}
|
||||
free(value);
|
||||
value = NULL;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_ENUMERATION:
|
||||
n->enumeration_value = be32toh(int32);
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_BOOLEAN:
|
||||
n->boolean_value = int64 != 0;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_TEXT_STRING:
|
||||
n->text_value = value;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_BYTE_STRING:
|
||||
n->bytes_value = value;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_DATE_TIME:
|
||||
n->date_time_value = be64toh(int64);
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_INTERVAL:
|
||||
n->interval_value = be32toh(int32);
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_DATE_TIME_EXTENDED:
|
||||
n->date_time_ext_value = be64toh(int64);
|
||||
break;
|
||||
|
||||
default:
|
||||
kmip_debug(debug, "unknown type: 0x%x", n->type);
|
||||
rc = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*node = n;
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
if (rc != 0) {
|
||||
switch (n->type) {
|
||||
case KMIP_TYPE_BIG_INTEGER:
|
||||
case KMIP_TYPE_TEXT_STRING:
|
||||
case KMIP_TYPE_BYTE_STRING:
|
||||
free(value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
kmip_node_free(n);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the length of the value part of a KMIP node (in TTLV encoding)
|
||||
*/
|
||||
static int kmip_node_get_length(struct kmip_node *node, size_t *length)
|
||||
{
|
||||
struct kmip_node *element;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
if (node == NULL || length == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
switch (node->type) {
|
||||
case KMIP_TYPE_STRUCTURE:
|
||||
*length = 0;
|
||||
element = node->structure_value;
|
||||
while (element != NULL) {
|
||||
rc = kmip_node_get_length(element, &len);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
*length += KMIP_TTLV_HEADER_LENGTH + len;
|
||||
if ((len % KMIP_TTLV_BLOCK_LENGTH) != 0)
|
||||
*length += KMIP_TTLV_BLOCK_LENGTH -
|
||||
(len % KMIP_TTLV_BLOCK_LENGTH);
|
||||
|
||||
element = element->next;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_INTEGER:
|
||||
case KMIP_TYPE_ENUMERATION:
|
||||
case KMIP_TYPE_INTERVAL:
|
||||
*length = sizeof(int32_t);
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_LONG_INTEGER:
|
||||
case KMIP_TYPE_BOOLEAN:
|
||||
case KMIP_TYPE_DATE_TIME:
|
||||
case KMIP_TYPE_DATE_TIME_EXTENDED:
|
||||
*length = sizeof(int64_t);
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_BIG_INTEGER:
|
||||
*length = kmip_encode_bignum_length(node->big_integer_value);
|
||||
/* BIG INTEGERS must be a multiple of 8 bytes long */
|
||||
if ((*length % KMIP_BIG_INTEGER_BLOCK_LENGTH) != 0)
|
||||
*length += KMIP_BIG_INTEGER_BLOCK_LENGTH -
|
||||
(*length % KMIP_BIG_INTEGER_BLOCK_LENGTH);
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_BYTE_STRING:
|
||||
*length = node->length;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_TEXT_STRING:
|
||||
if (node->text_value != NULL)
|
||||
*length = strlen(node->text_value);
|
||||
else
|
||||
*length = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a KMIP node into a BIO using the TTLV encoding.
|
||||
*
|
||||
* @param node the node to encode
|
||||
* @param bio the OpenSSL bio to write the data to
|
||||
* @param size On return: the number of bytes written to BIO
|
||||
* @param debug if true, debug messages are printed
|
||||
*
|
||||
* @returns 0 in case of success, or a negative errno value
|
||||
*/
|
||||
int kmip_encode_ttlv(struct kmip_node *node, BIO *bio, size_t *size,
|
||||
bool debug)
|
||||
{
|
||||
const unsigned char padding[KMIP_TTLV_BLOCK_LENGTH] = { 0 };
|
||||
unsigned char ttlv[KMIP_TTLV_HEADER_LENGTH];
|
||||
size_t len, elem_len, value_len, pad_len;
|
||||
struct kmip_node *element;
|
||||
void *value = NULL;
|
||||
uint32_t int32;
|
||||
uint64_t int64;
|
||||
int rc;
|
||||
|
||||
if (bio == NULL || node == NULL || size == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
kmip_debug(debug, "tag: 0x%x type: 0x%x, length: %u", node->tag,
|
||||
node->type, node->length);
|
||||
|
||||
*size = 0;
|
||||
|
||||
/* Update node's length field to match node's current data */
|
||||
rc = kmip_node_get_length(node, &len);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_node_get_length failed");
|
||||
return rc;
|
||||
}
|
||||
node->length = len;
|
||||
|
||||
/* Tag: 3-byte binary unsigned integer, transmitted big endian */
|
||||
ttlv[0] = (node->tag & 0xff0000) >> 16;
|
||||
ttlv[1] = (node->tag & 0xff00) >> 8;
|
||||
ttlv[2] = (node->tag & 0xff);
|
||||
|
||||
/* Type: 1 byte containing a coded value that indicates the data type */
|
||||
ttlv[3] = node->type;
|
||||
|
||||
/* Length: 32-bit binary integer, transmitted big-endian */
|
||||
ttlv[4] = (node->length & 0xff000000) >> 24;
|
||||
ttlv[5] = (node->length & 0xff0000) >> 16;
|
||||
ttlv[6] = (node->length & 0xff00) >> 8;
|
||||
ttlv[7] = (node->length & 0xff);
|
||||
|
||||
if (BIO_write(bio, ttlv, sizeof(ttlv)) != sizeof(ttlv)) {
|
||||
kmip_debug(debug, "BIO_write failed");
|
||||
return -EIO;
|
||||
}
|
||||
*size += sizeof(ttlv);
|
||||
|
||||
switch (node->type) {
|
||||
case KMIP_TYPE_STRUCTURE:
|
||||
value_len = 0;
|
||||
element = node->structure_value;
|
||||
while (element != NULL) {
|
||||
rc = kmip_encode_ttlv(element, bio, &elem_len, debug);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_encode_ttlv failed");
|
||||
return rc;
|
||||
}
|
||||
value_len += elem_len;
|
||||
element = element->next;
|
||||
}
|
||||
if (value_len != node->length) {
|
||||
kmip_debug(debug, "written length %lu not as expected "
|
||||
"(%u)", len, node->length);
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_INTEGER:
|
||||
int32 = htobe32(node->integer_value);
|
||||
value_len = sizeof(int32);
|
||||
value = &int32;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_LONG_INTEGER:
|
||||
int64 = htobe64(node->long_value);
|
||||
value_len = sizeof(int64);
|
||||
value = &int64;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_BIG_INTEGER:
|
||||
value_len = node->length; /* was already calculated above */
|
||||
value = malloc(value_len);
|
||||
if (value == NULL) {
|
||||
kmip_debug(debug, "malloc failed");
|
||||
return -ENOMEM;
|
||||
}
|
||||
rc = kmip_encode_bignum(node->big_integer_value, value,
|
||||
value_len);
|
||||
if (rc != 0) {
|
||||
kmip_debug(debug, "kmip_encode_bignum failed");
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_ENUMERATION:
|
||||
int32 = htobe32(node->enumeration_value);
|
||||
value_len = sizeof(int32);
|
||||
value = &int32;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_BOOLEAN:
|
||||
int64 = node->boolean_value ? 1 : 0;
|
||||
value_len = sizeof(int64);
|
||||
value = &int64;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_TEXT_STRING:
|
||||
value_len = node->length;
|
||||
value = node->text_value;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_BYTE_STRING:
|
||||
value_len = node->length;
|
||||
value = node->bytes_value;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_DATE_TIME:
|
||||
int64 = htobe64(node->date_time_value);
|
||||
value_len = sizeof(int64);
|
||||
value = &int64;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_INTERVAL:
|
||||
int32 = htobe32(node->interval_value);
|
||||
value_len = sizeof(int32);
|
||||
value = &int32;
|
||||
break;
|
||||
|
||||
case KMIP_TYPE_DATE_TIME_EXTENDED:
|
||||
int64 = htobe64(node->date_time_ext_value);
|
||||
value_len = sizeof(int64);
|
||||
value = &int64;
|
||||
break;
|
||||
|
||||
default:
|
||||
kmip_debug(debug, "unknown type: 0x%x", node->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (value != NULL) {
|
||||
if (BIO_write(bio, value, value_len) != (int)value_len) {
|
||||
kmip_debug(debug, "BIO_write failed");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
}
|
||||
*size += value_len;
|
||||
|
||||
if ((value_len % KMIP_TTLV_BLOCK_LENGTH) != 0) {
|
||||
pad_len = KMIP_TTLV_BLOCK_LENGTH -
|
||||
(value_len % KMIP_TTLV_BLOCK_LENGTH);
|
||||
|
||||
kmip_debug(debug, "pad_len: %lu", pad_len);
|
||||
if (BIO_write(bio, padding, pad_len) != (int)pad_len) {
|
||||
kmip_debug(debug, "BIO_write failed (padding)");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
*size += pad_len;
|
||||
}
|
||||
|
||||
kmip_debug(debug, "size: %lu", *size);
|
||||
|
||||
rc = 0;
|
||||
out:
|
||||
if (node->type == KMIP_TYPE_BIG_INTEGER)
|
||||
free(value);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
726
libkmipclient/utils.c
Normal file
726
libkmipclient/utils.c
Normal file
@@ -0,0 +1,726 @@
|
||||
/*
|
||||
* 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 <err.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "names.h"
|
||||
|
||||
/**
|
||||
* Print a debug message
|
||||
*/
|
||||
void kmip_print_debug(const char *func, const char *fmt, ...)
|
||||
{
|
||||
char tmp_fmt[200];
|
||||
va_list ap;
|
||||
|
||||
if (snprintf(tmp_fmt, sizeof(tmp_fmt), "DBG: %s: %s", func, fmt) >
|
||||
(int)sizeof(tmp_fmt))
|
||||
return;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vwarnx(tmp_fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse a decimal string into a 64 bit signed value
|
||||
*/
|
||||
int kmip_parse_decimal_int(const char *str, int64_t *val)
|
||||
{
|
||||
long long v;
|
||||
char *endptr;
|
||||
|
||||
if (str == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
errno = 0;
|
||||
v = strtoll(str, &endptr, 10);
|
||||
|
||||
if ((errno == ERANGE && (v == LLONG_MAX || v == LLONG_MIN)) ||
|
||||
(errno != 0 && v == 0))
|
||||
return -EBADMSG;
|
||||
|
||||
if (endptr == str || *endptr != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
*val = v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a decimal string into a 64 bit unsigned value
|
||||
*/
|
||||
int kmip_parse_decimal_uint(const char *str, uint64_t *val)
|
||||
{
|
||||
unsigned long long v;
|
||||
char *endptr;
|
||||
|
||||
if (str == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
errno = 0;
|
||||
v = strtoull(str, &endptr, 10);
|
||||
|
||||
if ((errno == ERANGE && (v == 0 || v == ULLONG_MAX)) ||
|
||||
(errno != 0 && v == 0))
|
||||
return -EBADMSG;
|
||||
|
||||
if (endptr == str || *endptr != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
*val = v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a hex string into a 64 bit signed value
|
||||
*/
|
||||
int kmip_parse_hex_int(const char *str, int64_t *val)
|
||||
{
|
||||
long long v;
|
||||
char *endptr;
|
||||
|
||||
if (str == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (strncmp(str, "0x", 2) != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
errno = 0;
|
||||
v = strtoll(str, &endptr, 16);
|
||||
|
||||
if ((errno == ERANGE && (v == LLONG_MAX || v == LLONG_MIN)) ||
|
||||
(errno != 0 && v == 0))
|
||||
return -EBADMSG;
|
||||
|
||||
if (endptr == str || *endptr != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
*val = v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a hex string into a variable length signed big integer.
|
||||
* On return, val and length is set. The buffer returned in val must be freed
|
||||
* by the caller.
|
||||
*/
|
||||
int kmip_parse_hex(const char *str, bool has_prefix, unsigned char **val,
|
||||
uint32_t *length)
|
||||
{
|
||||
unsigned char *buf;
|
||||
BIGNUM *b = NULL;
|
||||
int len, rc;
|
||||
|
||||
if (str == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (has_prefix && strncmp(str, "0x", 2) != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
rc = BN_hex2bn(&b, str + (has_prefix ? 2 : 0));
|
||||
if (rc <= 0)
|
||||
return -EBADMSG;
|
||||
|
||||
len = BN_num_bytes(b);
|
||||
buf = calloc(1, len);
|
||||
if (buf == NULL) {
|
||||
BN_free(b);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (BN_bn2bin(b, buf) != len) {
|
||||
BN_free(b);
|
||||
free(buf);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*val = buf;
|
||||
*length = len;
|
||||
|
||||
BN_free(b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a hex string from the byte array specified in val. The caller must
|
||||
* free the returned str.
|
||||
*/
|
||||
int kmip_format_hex(const unsigned char *val, uint32_t length, bool prefix,
|
||||
char **str)
|
||||
{
|
||||
uint32_t str_len, i;
|
||||
char tmp[4];
|
||||
char *ret;
|
||||
|
||||
str_len = length * 2 + (prefix ? 2 : 0) + 1;
|
||||
ret = calloc(1, str_len);
|
||||
if (ret == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (prefix)
|
||||
strcat(ret, "0x");
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
sprintf(tmp, "%02x", val[i]);
|
||||
strcat(ret, tmp);
|
||||
}
|
||||
|
||||
*str = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a hex string into a big number.
|
||||
* On return, val and length is set. The buffer returned in val must be freed
|
||||
* by the caller.
|
||||
*/
|
||||
int kmip_parse_bignum(const char *str, bool has_prefix, BIGNUM **bn)
|
||||
{
|
||||
unsigned char *buf;
|
||||
uint32_t len;
|
||||
int rc;
|
||||
|
||||
if (str == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
rc = kmip_parse_hex(str, has_prefix, &buf, &len);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = kmip_decode_bignum(buf, len, bn);
|
||||
|
||||
free(buf);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a hex string from a big number. The caller must free the returned str.
|
||||
*/
|
||||
int kmip_format_bignum(const BIGNUM *bn, bool prefix, char **str)
|
||||
{
|
||||
unsigned char *buf;
|
||||
uint32_t len;
|
||||
int rc;
|
||||
|
||||
len = kmip_encode_bignum_length(bn);
|
||||
/* BIG INTEGERS must be a multiple of 8 bytes long */
|
||||
if ((len % KMIP_BIG_INTEGER_BLOCK_LENGTH) != 0)
|
||||
len += KMIP_BIG_INTEGER_BLOCK_LENGTH -
|
||||
(len % KMIP_BIG_INTEGER_BLOCK_LENGTH);
|
||||
|
||||
buf = malloc(len);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = kmip_encode_bignum(bn, buf, len);
|
||||
if (rc != 0) {
|
||||
free(buf);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rc = kmip_format_hex(buf, len, prefix, str);
|
||||
|
||||
free(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a binary big integer in two's complement form into an OpenSSL BIGNUM.
|
||||
*/
|
||||
int kmip_decode_bignum(const unsigned char *data, uint32_t length, BIGNUM **bn)
|
||||
{
|
||||
unsigned char *tmp = (unsigned char *)data;
|
||||
int i, neg = 0, rc = 0;
|
||||
|
||||
if (data == NULL || bn == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (data[0] & 0x80) {
|
||||
neg = 1;
|
||||
|
||||
tmp = malloc(length);
|
||||
if (tmp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < (int)length; i++)
|
||||
tmp[i] = ~data[i];
|
||||
|
||||
for (i = length - 1; i >= 0; i--) {
|
||||
tmp[i]++;
|
||||
if (tmp[i] != 0x00)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*bn = BN_bin2bn(tmp, length, NULL);
|
||||
if (*bn == NULL) {
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
BN_set_negative(*bn, neg);
|
||||
|
||||
out:
|
||||
if (neg)
|
||||
free(tmp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length required by a binary big integer in two's complement form
|
||||
*/
|
||||
uint32_t kmip_encode_bignum_length(const BIGNUM *bn)
|
||||
{
|
||||
uint32_t length;
|
||||
|
||||
if (bn == NULL)
|
||||
return 0;
|
||||
|
||||
length = BN_num_bytes(bn);
|
||||
if (BN_is_negative(bn) && BN_is_bit_set(bn, (length * 8) - 1))
|
||||
length += 1;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode an OpenSSL BIGNUM to a binary big integer in two's complement form,
|
||||
* in the desired length.
|
||||
*/
|
||||
int kmip_encode_bignum(const BIGNUM *bn, unsigned char *data, uint32_t length)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (bn == NULL || data == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (BN_bn2binpad(bn, data, length) != (int)length)
|
||||
return -EIO;
|
||||
|
||||
if (BN_is_negative(bn)) {
|
||||
for (i = 0; i < (int)length; i++)
|
||||
data[i] = ~data[i];
|
||||
|
||||
for (i = length - 1; i >= 0; i--) {
|
||||
data[i]++;
|
||||
if (data[i] != 0x00)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a timestamp in ISO8601 format and return it as time_t value
|
||||
*/
|
||||
int kmip_parse_timestamp(const char *str, int64_t *val)
|
||||
{
|
||||
struct tm tm = { 0 };
|
||||
char *p;
|
||||
int rc;
|
||||
|
||||
rc = kmip_parse_hex_int(str, val);
|
||||
if (rc == 0)
|
||||
return 0;
|
||||
if (rc != -EBADMSG)
|
||||
return rc;
|
||||
|
||||
p = strptime(str, KMIP_ISO8601_TIMESTAMP_TZ, &tm);
|
||||
if (p == NULL)
|
||||
p = strptime(str, KMIP_ISO8601_TIMESTAMP, &tm);
|
||||
if (p == NULL || *p != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
/* Adjust according to the parsed time zone */
|
||||
tm.tm_sec -= tm.tm_gmtoff;
|
||||
tm.tm_gmtoff = 0;
|
||||
tm.tm_isdst = 0;
|
||||
|
||||
*val = (time_t)timegm(&tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a mask specification of the specified tag and separator character
|
||||
*/
|
||||
int kmip_parse_mask(enum kmip_tag tag, const char *str, char separator,
|
||||
int64_t *val)
|
||||
{
|
||||
const struct kmip_enum *info;
|
||||
char *save_ptr, *s, *tok;
|
||||
char delimiter[2];
|
||||
uint32_t enum_val;
|
||||
int rc = 0;
|
||||
|
||||
info = kmip_enum_info_by_tag(tag);
|
||||
if (info == NULL)
|
||||
return kmip_parse_hex_int(str, val);
|
||||
|
||||
*val = 0;
|
||||
|
||||
s = strdup(str);
|
||||
if (s == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
delimiter[0] = separator;
|
||||
delimiter[1] = 0;
|
||||
tok = strtok_r(s, delimiter, &save_ptr);
|
||||
while (tok != NULL) {
|
||||
rc = kmip_enum_value_by_name_or_hex(info, tok, &enum_val);
|
||||
if (rc != 0)
|
||||
break;
|
||||
|
||||
*val |= enum_val;
|
||||
|
||||
tok = strtok_r(NULL, delimiter, &save_ptr);
|
||||
}
|
||||
|
||||
free(s);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int kmip_append_string(char **str, int *str_len, char separator,
|
||||
const char *append)
|
||||
{
|
||||
int new_len;
|
||||
char *tmp;
|
||||
|
||||
if (str == NULL || str_len == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (*str == NULL)
|
||||
*str_len = 0;
|
||||
|
||||
new_len = *str_len;
|
||||
if (*str == NULL)
|
||||
new_len++;
|
||||
else if (separator != 0)
|
||||
new_len++;
|
||||
if (append != NULL)
|
||||
new_len += strlen(append);
|
||||
|
||||
tmp = realloc(*str, new_len);
|
||||
if (tmp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (*str == NULL)
|
||||
memset(tmp, 0, new_len);
|
||||
else if (separator != 0)
|
||||
strncat(tmp, &separator, 1);
|
||||
if (append != NULL)
|
||||
strcat(tmp, append);
|
||||
|
||||
*str = tmp;
|
||||
*str_len = new_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a mask specification of the specified tag and separator character
|
||||
*/
|
||||
int kmip_format_mask(enum kmip_tag tag, int32_t value, char separator,
|
||||
char **str)
|
||||
{
|
||||
const struct kmip_enum *info;
|
||||
int rc = 0, i, s_len = 0;
|
||||
char *s = NULL, *tmp;
|
||||
|
||||
info = kmip_enum_info_by_tag(tag);
|
||||
if (info == NULL || value == 0)
|
||||
return kmip_format_hex((const unsigned char *)&value,
|
||||
sizeof(value), true, str);
|
||||
|
||||
/* Process all known mask bits */
|
||||
for (i = 0; value != 0 && info[i].name != NULL; i++) {
|
||||
if (value & info[i].val) {
|
||||
rc = kmip_append_string(&s, &s_len, separator,
|
||||
info[i].name);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
||||
value &= ~info[i].val;
|
||||
}
|
||||
}
|
||||
|
||||
/* Any bits left in the value? */
|
||||
if (value != 0) {
|
||||
rc = kmip_format_hex((const unsigned char *)&value,
|
||||
sizeof(value), true, &tmp);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
||||
rc = kmip_append_string(&s, &s_len, separator, tmp);
|
||||
free(tmp);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
||||
}
|
||||
|
||||
*str = s;
|
||||
|
||||
out:
|
||||
if (rc != 0)
|
||||
free(s);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void kmip_print_dump(const char *func, unsigned char *data, size_t size,
|
||||
unsigned int indent)
|
||||
{
|
||||
char outstr[200], hexstr[4];
|
||||
size_t i;
|
||||
|
||||
if (data == NULL)
|
||||
return;
|
||||
|
||||
strcpy(outstr, "");
|
||||
for (i = 0; i < size; i++) {
|
||||
sprintf(hexstr, "%02x ", data[i]);
|
||||
strcat(outstr, hexstr);
|
||||
|
||||
if (i % 16 == 15) {
|
||||
kmip_print_debug(func, "%*s%s", indent, "",
|
||||
outstr);
|
||||
strcpy(outstr, "");
|
||||
}
|
||||
}
|
||||
if (i % 16 != 0)
|
||||
kmip_print_debug(func, "%*s%s", indent, "", outstr);
|
||||
}
|
||||
|
||||
static void kmip_print_bignum(const char *func, const BIGNUM *bn,
|
||||
unsigned int indent)
|
||||
{
|
||||
unsigned char *buf;
|
||||
uint32_t len;
|
||||
int rc;
|
||||
|
||||
if (bn == NULL)
|
||||
return;
|
||||
|
||||
len = kmip_encode_bignum_length(bn);
|
||||
buf = malloc(len);
|
||||
if (buf == NULL)
|
||||
return;
|
||||
|
||||
rc = kmip_encode_bignum(bn, buf, len);
|
||||
if (rc != 0) {
|
||||
free(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
kmip_print_dump(func, buf, len, indent);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void kmip_node_dump_int(struct kmip_node *node, unsigned int indent)
|
||||
{
|
||||
enum kmip_tag tag, v1_attr_tag = 0;
|
||||
struct kmip_node *element;
|
||||
char outstr[200] = { 0 };
|
||||
struct tm *tm;
|
||||
const char *s;
|
||||
char *tmp;
|
||||
time_t t;
|
||||
int rc;
|
||||
|
||||
if (node == NULL)
|
||||
return;
|
||||
|
||||
s = kmip_tag_name_by_tag(node->tag);
|
||||
kmip_print_debug("kmip_node_dump", "%*sTag: %s (0x%x)", indent, "",
|
||||
s ? s : "UNKNOWN", node->tag);
|
||||
s = kmip_type_name_by_type(node->type);
|
||||
kmip_print_debug("kmip_node_dump", "%*s Type: %s (0x%x)", indent, "",
|
||||
s ? s : "UNKNOWN", node->type);
|
||||
|
||||
if (node->name != NULL)
|
||||
kmip_print_debug("kmip_node_dump", "%*s Name: %s", indent,
|
||||
"", node->name);
|
||||
|
||||
/*
|
||||
* KMIP v1.x attribute values may be Enumerations or Integer Masks.
|
||||
* To correctly print 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:
|
||||
kmip_print_debug("kmip_node_dump", "%*s Elements (%u):",
|
||||
indent, "",
|
||||
kmip_node_get_structure_element_count(node));
|
||||
element = node->structure_value;
|
||||
while (element != NULL) {
|
||||
kmip_node_dump_int(element, indent + 4);
|
||||
element = element->next;
|
||||
}
|
||||
break;
|
||||
case KMIP_TYPE_INTEGER:
|
||||
if (kmip_is_tag_mask(tag)) {
|
||||
rc = kmip_format_mask(tag, node->integer_value,
|
||||
'|', &tmp);
|
||||
if (rc == 0) {
|
||||
kmip_print_debug("kmip_node_dump", "%*s "
|
||||
"Value: %s (0x%x)",
|
||||
indent, "", tmp,
|
||||
node->integer_value);
|
||||
free(tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
kmip_print_debug("kmip_node_dump", "%*s Value: %d (0x%x)",
|
||||
indent, "", node->integer_value,
|
||||
node->integer_value);
|
||||
break;
|
||||
case KMIP_TYPE_LONG_INTEGER:
|
||||
kmip_print_debug("kmip_node_dump", "%*s Value: %ld (0x%lx)",
|
||||
indent, "", node->long_value,
|
||||
node->long_value);
|
||||
break;
|
||||
case KMIP_TYPE_BIG_INTEGER:
|
||||
kmip_print_debug("kmip_node_dump", "%*s Value: (%u bytes)",
|
||||
indent, "", kmip_encode_bignum_length(
|
||||
node->big_integer_value));
|
||||
kmip_print_bignum("kmip_node_dump", node->big_integer_value,
|
||||
indent + 4);
|
||||
break;
|
||||
case KMIP_TYPE_ENUMERATION:
|
||||
s = kmip_enum_name_by_tag_value(tag, node->enumeration_value);
|
||||
kmip_print_debug("kmip_node_dump", "%*s Value: %s (0x%x)",
|
||||
indent, "", s ? s : "UNKNOWN",
|
||||
node->enumeration_value);
|
||||
break;
|
||||
case KMIP_TYPE_BOOLEAN:
|
||||
kmip_print_debug("kmip_node_dump", "%*s Value: %s", indent, "",
|
||||
node->boolean_value ? "True" : "False");
|
||||
break;
|
||||
case KMIP_TYPE_TEXT_STRING:
|
||||
kmip_print_debug("kmip_node_dump", "%*s Value: '%s' "
|
||||
"(%u characters)", indent, "",
|
||||
node->text_value, strlen(node->text_value));
|
||||
break;
|
||||
case KMIP_TYPE_BYTE_STRING:
|
||||
kmip_print_debug("kmip_node_dump", "%*s Value: (%u bytes)",
|
||||
indent, "", node->length);
|
||||
kmip_print_dump("kmip_node_dump", node->bytes_value,
|
||||
node->length, indent + 4);
|
||||
break;
|
||||
case KMIP_TYPE_DATE_TIME:
|
||||
tm = gmtime((time_t *)&node->date_time_value);
|
||||
if (tm != NULL)
|
||||
strftime(outstr, sizeof(outstr),
|
||||
KMIP_ISO8601_TIMESTAMP_UTC, tm);
|
||||
else
|
||||
strcpy(outstr, "INVALID");
|
||||
kmip_print_debug("kmip_node_dump", "%*s Value: %s (0x%lx)",
|
||||
indent, "", outstr, node->date_time_value);
|
||||
break;
|
||||
case KMIP_TYPE_INTERVAL:
|
||||
kmip_print_debug("kmip_node_dump", "%*s Value: %d (0x%x)",
|
||||
indent, "", node->interval_value,
|
||||
node->interval_value);
|
||||
break;
|
||||
case KMIP_TYPE_DATE_TIME_EXTENDED:
|
||||
t = (time_t)node->date_time_ext_value / 1000000;
|
||||
tm = gmtime(&t);
|
||||
if (tm != NULL)
|
||||
strftime(outstr, sizeof(outstr),
|
||||
KMIP_ISO8601_TIMESTAMP_UTC, tm);
|
||||
else
|
||||
strcpy(outstr, "INVALID");
|
||||
kmip_print_debug("kmip_node_dump", "%*s Value: %s %lu (0x%lx)",
|
||||
indent, "", outstr,
|
||||
node->date_time_ext_value % 1000000,
|
||||
node->date_time_ext_value);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dump a KMIP node
|
||||
*
|
||||
* @param node the node to free
|
||||
* @param debug if false, the function is a no-op
|
||||
*/
|
||||
void kmip_node_dump(struct kmip_node *node, bool debug)
|
||||
{
|
||||
if (node == NULL || !debug)
|
||||
return;
|
||||
|
||||
kmip_node_dump_int(node, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a KMIP v1.x Attribute Name node in the elements of the specified parent
|
||||
* node, and return the tag value of the attribute name.
|
||||
*
|
||||
* @param parent the parent node of the attribute name and value
|
||||
*
|
||||
* @returns the tag value of the attribute name, or 0 if not found, or unknown
|
||||
* attribute name
|
||||
*/
|
||||
enum kmip_tag kmip_find_v1_attribute_name_tag(struct kmip_node *parent)
|
||||
{
|
||||
struct kmip_node *e;
|
||||
|
||||
if (parent == NULL)
|
||||
return 0;
|
||||
|
||||
if (parent->tag != KMIP_TAG_ATTRIBUTE)
|
||||
return 0;
|
||||
if (parent->type != KMIP_TYPE_STRUCTURE)
|
||||
return 0;
|
||||
|
||||
e = parent->structure_value;
|
||||
while (e != NULL) {
|
||||
/*
|
||||
* A KMIP v2.x Vendor Attribute looks similar to a KMIP v1.x
|
||||
* Attribute, but has a Vendor Identification node. If we find
|
||||
* a Vendor Identification node, then it can't be a KMIP v1.x
|
||||
* Attribute.
|
||||
*/
|
||||
if (e->tag == KMIP_TAG_VENDOR_IDENTIFICATION &&
|
||||
e->type == KMIP_TYPE_TEXT_STRING)
|
||||
return 0;
|
||||
|
||||
if (e->tag == KMIP_TAG_ATTRIBUTE_NAME &&
|
||||
e->type == KMIP_TYPE_TEXT_STRING)
|
||||
return kmip_attr_tag_by_v1_attr_name(e->text_value);
|
||||
|
||||
e = e->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
61
libkmipclient/utils.h
Normal file
61
libkmipclient/utils.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <openssl/bn.h>
|
||||
|
||||
#include "kmip.h"
|
||||
|
||||
#define KMIP_BIG_INTEGER_BLOCK_LENGTH 8
|
||||
|
||||
#define KMIP_ISO8601_TIMESTAMP_UTC "%FT%TZ"
|
||||
#define KMIP_ISO8601_TIMESTAMP_TZ "%FT%T%z"
|
||||
#define KMIP_ISO8601_TIMESTAMP "%FT%T"
|
||||
|
||||
#define kmip_debug(debug, fmt...) \
|
||||
do { \
|
||||
if (debug) \
|
||||
kmip_print_debug(__func__, fmt); \
|
||||
} while (0)
|
||||
|
||||
void kmip_print_debug(const char *func, const char *fmt, ...);
|
||||
void kmip_print_dump(const char *func, unsigned char *data, size_t size,
|
||||
unsigned int indent);
|
||||
|
||||
int kmip_parse_decimal_int(const char *str, int64_t *val);
|
||||
int kmip_parse_decimal_uint(const char *str, uint64_t *val);
|
||||
|
||||
int kmip_parse_hex_int(const char *str, int64_t *val);
|
||||
int kmip_parse_hex(const char *str, bool has_prefix, unsigned char **val,
|
||||
uint32_t *length);
|
||||
int kmip_format_hex(const unsigned char *val, uint32_t length, bool prefix,
|
||||
char **str);
|
||||
|
||||
int kmip_parse_bignum(const char *str, bool has_prefix, BIGNUM **bn);
|
||||
int kmip_format_bignum(const BIGNUM *bn, bool prefix, char **str);
|
||||
int kmip_decode_bignum(const unsigned char *data, uint32_t length, BIGNUM **bn);
|
||||
uint32_t kmip_encode_bignum_length(const BIGNUM *bn);
|
||||
int kmip_encode_bignum(const BIGNUM *bn, unsigned char *data, uint32_t length);
|
||||
|
||||
int kmip_parse_timestamp(const char *str, int64_t *val);
|
||||
|
||||
int kmip_parse_mask(enum kmip_tag tag, const char *str, char separator,
|
||||
int64_t *val);
|
||||
int kmip_format_mask(enum kmip_tag tag, int32_t value, char separator,
|
||||
char **str);
|
||||
|
||||
void kmip_node_dump(struct kmip_node *node, bool debug);
|
||||
|
||||
enum kmip_tag kmip_find_v1_attribute_name_tag(struct kmip_node *parent);
|
||||
|
||||
#endif
|
||||
490
libkmipclient/xml.c
Normal file
490
libkmipclient/xml.c
Normal file
@@ -0,0 +1,490 @@
|
||||
/*
|
||||
* 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 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)
|
||||
{
|
||||
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 (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 JSON 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, 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_json 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);
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user