diff --git a/zkey/kmip/Makefile b/zkey/kmip/Makefile index 6c52a2d9..54359ac6 100644 --- a/zkey/kmip/Makefile +++ b/zkey/kmip/Makefile @@ -10,6 +10,7 @@ libs = $(rootdir)/libutil/libutil.a $(rootdir)/libseckey/libseckey.a zkey-kmip.o: zkey-kmip.c zkey-kmip.h ../kms-plugin.h ../plugin-utils.h \ ../cca.h ../utils.h ../pkey.h ../properties.h \ $(rootdir)include/kmipclient/kmipclient.h libkmipclient.dep +profiles.o: profiles.c zkey-kmip.h ../properties.h plugin-utils.o: ../plugin-utils.c ../plugin-utils.h ../kms-plugin.h $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -fPIC -c $< -o $@ @@ -34,7 +35,7 @@ zkey-kmip.so: LDLIBS = -L$(rootdir)/libkmipclient -lkmipclient -ldl -lcrypto zkey-kmip.so: ALL_LDFLAGS += -shared -Wl,--version-script=zkey-kmip.map \ -Wl,-z,defs,-Bsymbolic -Wl,-soname,zkey-kmip.so.$(VERM) zkey-kmip.so: zkey-kmip.o plugin-utils.o properties.o pkey.o cca.o ep11.o \ - utils.o $(libs) + utils.o profiles.o $(libs) $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ install-libkmipclient.dep: @@ -48,6 +49,9 @@ libkmipclient.dep: install: all install-libkmipclient.dep zkey-kmip.so $(INSTALL) -d -m 755 $(DESTDIR)$(ZKEYKMSPLUGINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 -T zkey-kmip.so $(DESTDIR)$(ZKEYKMSPLUGINDIR)/zkey-kmip.so + $(INSTALL) -d -m 770 $(DESTDIR)$(SYSCONFDIR)/zkey/kmip + $(INSTALL) -d -m 770 $(DESTDIR)$(SYSCONFDIR)/zkey/kmip/profiles + $(INSTALL) -m 644 -c profiles/*.profile $(DESTDIR)$(SYSCONFDIR)/zkey/kmip/profiles clean: rm -f *.o zkey-kmip.so install-libkmipclient.dep libkmipclient.dep diff --git a/zkey/kmip/profiles.c b/zkey/kmip/profiles.c new file mode 100644 index 00000000..9ed93acf --- /dev/null +++ b/zkey/kmip/profiles.c @@ -0,0 +1,596 @@ +/* + * zkey-kmip - KMIP zkey KMS plugin + * + * 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 _DEFAULT_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lib/zt_common.h" +#include "lib/util_libc.h" + +#include "zkey-kmip.h" +#include "../properties.h" + +#define _set_error(ph, fmt...) plugin_set_error(&(ph)->pd, fmt) + +/** + * Returns the profile directory. If environment variable ZKEY_KMIP_PROFILES + * is set, then its value specifies the profile directory, otherwise the + * default profile directory '/etc/zkey/kmip/profiles' is returned. + */ +static const char *get_profiles_directory(void) +{ + const char *dir; + + dir = secure_getenv(KMIP_PROFILES_LOCATION_ENVVAR); + + return dir != NULL ? dir : KMIP_PROFILES_LOCATION; +} + +static int profile_get_bool(struct plugin_handle *ph, struct properties *props, + const char *file_name, const char *prop_name, + bool default_val, bool *bool_var) +{ + int rc = 0; + char *val; + + val = properties_get(props, prop_name); + if (val == NULL) { + *bool_var = default_val; + goto ret; + } + + if (strcasecmp(val, KMIP_PROFILES_BOOLEAN_FALSE) == 0) { + *bool_var = false; + } else if (strcasecmp(val, KMIP_PROFILES_BOOLEAN_TRUE) == 0) { + *bool_var = true; + } else { + _set_error(ph, "Profile '%s': Invalid value for '%s': '%s'", + file_name, prop_name, + val); + rc = -EINVAL; + goto out; + } + +out: + free(val); + +ret: + if (rc == 0) + pr_verbose(&ph->pd, "Profile: '%s': '%s'", prop_name, + *bool_var ? "True" : "False"); + + return rc; +} + +/** + * Reads a profile from a file + * + * @param ph the plugin handle + * @param profile_dir the directory containing the profiles. If NULL, then + * the default profile directory or the one specified + * by environment variable ZKEY_KMIP_PROFILES is used. + * @param profile_file name of the profile file to read + * @param profile On return: the allocated profile. The caller must + * free the profile with profile_free. + * + * @returns 0 on success, a negative errno in case of an error. + */ +int profile_read(struct plugin_handle *ph, const char *profile_dir, + const char *profile_file, struct kmip_profile **profile) +{ + struct kmip_profile *prof = NULL; + struct properties *props; + char *file_name = NULL; + char *val = NULL, *tok; + int rc; + + if (profile_dir == NULL) + profile_dir = get_profiles_directory(); + + util_asprintf(&file_name, "%s/%s", profile_dir, profile_file); + pr_verbose(&ph->pd, "Read profile from '%s'", file_name); + + props = properties_new(); + rc = properties_load(props, file_name, false); + if (rc != 0) { + _set_error(ph, "Failed to read profile from file '%s': %s", + file_name, strerror(-rc)); + goto out; + } + + prof = util_zalloc(sizeof(struct kmip_profile)); + + tok = strrchr(profile_file, '/'); + if (tok == NULL) + tok = (char *)profile_file; + prof->name = util_strdup(tok); + tok = strchr(prof->name, '.'); + if (tok != NULL) + *tok = 0; + + pr_verbose(&ph->pd, "Profile name: '%s'", prof->name); + + prof->server_regex = properties_get(props, KMIP_PROFILES_SERVER_REGEX); + if (prof->server_regex == NULL) { + _set_error(ph, "Profile '%s': Missing value for '%s'", + file_name, KMIP_PROFILES_SERVER_REGEX); + rc = -EINVAL; + goto out; + } + pr_verbose(&ph->pd, "Profile: '%s': '%s'", KMIP_PROFILES_SERVER_REGEX, + prof->server_regex); + + val = properties_get(props, KMIP_PROFILES_KMIP_VERSION); + if (val == NULL) + val = util_strdup(KMIP_PROFILES_VERSION_AUTO); + pr_verbose(&ph->pd, "Profile: '%s': '%s'", KMIP_PROFILES_KMIP_VERSION, + val); + if (strcasecmp(val, KMIP_PROFILES_VERSION_AUTO) == 0) { + prof->kmip_version.major = 0; + prof->kmip_version.minor = 0; + } else { + if (sscanf(val, "%u.%u", &prof->kmip_version.major, + &prof->kmip_version.minor) != 2) { + _set_error(ph, "Profile '%s': Invalid value for '%s': " + "'%s'", file_name, + KMIP_PROFILES_KMIP_VERSION, val); + rc = -EINVAL; + goto out; + } + if (prof->kmip_version.major == 0) { + _set_error(ph, "Profile '%s': Invalid value for '%s': " + "'%s'", file_name, + KMIP_PROFILES_KMIP_VERSION, val); + rc = -EINVAL; + goto out; + } + } + free(val); + + val = properties_get(props, KMIP_PROFILES_TRANSPORT); + if (val == NULL) + val = util_strdup(KMIP_PROFILES_TRANSPORT_TLS); + pr_verbose(&ph->pd, "Profile: '%s': '%s'", KMIP_PROFILES_TRANSPORT, + val); + if (strcasecmp(val, KMIP_PROFILES_TRANSPORT_TLS) == 0) { + prof->transport = KMIP_TRANSPORT_PLAIN_TLS; + } else if (strcasecmp(val, KMIP_PROFILES_TRANSPORT_HTTPS) == 0) { + prof->transport = KMIP_TRANSPORT_HTTPS; + } else { + _set_error(ph, "Profile '%s': Invalid value for '%s': '%s'", + file_name, KMIP_PROFILES_TRANSPORT, val); + rc = -EINVAL; + goto out; + } + free(val); + + + val = properties_get(props, KMIP_PROFILES_ENCODING); + if (val == NULL) + val = util_strdup(KMIP_PROFILES_ENCODING_TTLV); + pr_verbose(&ph->pd, "Profile: '%s': '%s'", KMIP_PROFILES_ENCODING, + val); + if (strcasecmp(val, KMIP_PROFILES_ENCODING_TTLV) == 0) { + prof->encoding = KMIP_ENCODING_TTLV; + } else if (strcasecmp(val, KMIP_PROFILES_ENCODING_JSON) == 0) { + if (prof->transport != KMIP_TRANSPORT_HTTPS) { + _set_error(ph, "Profile '%s': JSON encoding is only " + "possible with HTTP transport", + file_name); + rc = -EINVAL; + goto out; + } + prof->encoding = KMIP_ENCODING_JSON; + } else if (strcasecmp(val, KMIP_PROFILES_ENCODING_XML) == 0) { + prof->encoding = KMIP_ENCODING_XML; + if (prof->transport != KMIP_TRANSPORT_HTTPS) { + _set_error(ph, "Profile '%s': XML encoding is only " + "possible with HTTP transport", + file_name); + rc = -EINVAL; + goto out; + } + } else { + _set_error(ph, "Profile '%s': Invalid value for '%s': '%s'", + file_name, KMIP_PROFILES_ENCODING, val); + rc = -EINVAL; + goto out; + } + free(val); + + val = properties_get(props, KMIP_PROFILES_HTTPS_URI); + switch (prof->transport) { + case KMIP_TRANSPORT_HTTPS: + if (val == NULL) + val = util_strdup(KMIP_PROFILES_HTTPS_URI_DEFAULT); + prof->https_uri = val; + pr_verbose(&ph->pd, "Profile: '%s': '%s'", + KMIP_PROFILES_HTTPS_URI, prof->https_uri); + break; + default: + if (val != NULL) { + pr_verbose(&ph->pd, "Profile: '%s': ignored '%s'", + KMIP_PROFILES_HTTPS_URI, val); + free(val); + } + break; + } + + val = properties_get(props, KMIP_PROFILES_AUTH_SCHEME); + if (val == NULL) + val = util_strdup(KMIP_PROFILES_AUTH_TLS_CLIENT_CERT); + pr_verbose(&ph->pd, "Profile: '%s': '%s'", KMIP_PROFILES_AUTH_SCHEME, + val); + if (strcasecmp(val, KMIP_PROFILES_AUTH_TLS_CLIENT_CERT) == 0) { + prof->auth_scheme = KMIP_PROFILE_AUTH_TLS_CLIENT_CERT; + } else { + _set_error(ph, "Profile '%s': Invalid value for '%s': '%s'", + file_name, KMIP_PROFILES_AUTH_SCHEME, val); + rc = -EINVAL; + goto out; + } + free(val); + + val = properties_get(props, KMIP_PROFILES_WRAP_KEY_ALGORITHM); + if (val == NULL) + val = util_strdup(KMIP_PROFILES_WRAP_KEY_ALGORITHM_RSA); + pr_verbose(&ph->pd, "Profile: '%s': '%s'", + KMIP_PROFILES_WRAP_KEY_ALGORITHM, val); + if (strcasecmp(val, KMIP_PROFILES_WRAP_KEY_ALGORITHM_RSA) == 0) { + prof->wrap_key_algo = KMIP_CRYPTO_ALGO_RSA; + } else { + _set_error(ph, "Profile '%s': Invalid value for '%s': '%s'", + file_name, KMIP_PROFILES_WRAP_KEY_ALGORITHM, val); + rc = -EINVAL; + goto out; + } + free(val); + + val = properties_get(props, KMIP_PROFILES_WRAP_KEY_PARAMS); + if (prof->wrap_key_algo == KMIP_CRYPTO_ALGO_RSA) { + if (val == NULL) { + _set_error(ph, "Profile '%s': Missing value for '%s'", + file_name, KMIP_PROFILES_WRAP_KEY_PARAMS); + rc = -EINVAL; + goto out; + } + pr_verbose(&ph->pd, "Profile: '%s': '%s'", + KMIP_PROFILES_WRAP_KEY_PARAMS, val); + prof->wrap_key_size = atoi(val); + if (prof->wrap_key_size == 0) { + _set_error(ph, "Profile '%s': Invalid value for '%s' " + "(RSA modulus size): '%s'", file_name, + KMIP_PROFILES_WRAP_KEY_PARAMS, val); + rc = -EINVAL; + goto out; + } + } else if (val != NULL) { + pr_verbose(&ph->pd, "Profile: '%s': ignored '%s'", + KMIP_PROFILES_WRAP_KEY_PARAMS, val); + } + free(val); + + val = properties_get(props, KMIP_PROFILES_WRAP_KEY_FORMAT); + switch (prof->wrap_key_algo) { + case KMIP_CRYPTO_ALGO_RSA: + if (val == NULL) + val = util_strdup(KMIP_PROFILES_WRAP_KEY_FORMAT_PKCS1); + pr_verbose(&ph->pd, "Profile: '%s': '%s'", + KMIP_PROFILES_WRAP_KEY_FORMAT, val); + if (strcasecmp(val, KMIP_PROFILES_WRAP_KEY_FORMAT_PKCS1) == 0) { + prof->wrap_key_format = KMIP_KEY_FORMAT_TYPE_PKCS_1; + } else if (strcasecmp(val, + KMIP_PROFILES_WRAP_KEY_FORMAT_PKCS8) == 0) { + prof->wrap_key_format = KMIP_KEY_FORMAT_TYPE_PKCS_8; + } else if (strcasecmp(val, + KMIP_PROFILES_WRAP_KEY_FORMAT_TRANSP) == 0) { + prof->wrap_key_format = + KMIP_KEY_FORMAT_TYPE_TRANSPARENT_RSA_PUBLIC_KEY; + } else { + _set_error(ph, "Profile '%s': Invalid value for '%s': " + "'%s'", file_name, + KMIP_PROFILES_WRAP_KEY_FORMAT, val); + rc = -EINVAL; + goto out; + } + break; + default: + if (val != NULL) + pr_verbose(&ph->pd, "Profile: '%s': ignored '%s'", + KMIP_PROFILES_WRAP_KEY_FORMAT, val); + break; + } + free(val); + + val = properties_get(props, KMIP_PROFILES_WRAP_PADDING_METHOD); + switch (prof->wrap_key_algo) { + case KMIP_CRYPTO_ALGO_RSA: + if (val == NULL) + val = util_strdup(KMIP_PROFILES_WRAP_PADDING_PKCS1_5); + pr_verbose(&ph->pd, "Profile: '%s': '%s'", + KMIP_PROFILES_WRAP_PADDING_METHOD, val); + if (strcasecmp(val, KMIP_PROFILES_WRAP_PADDING_PKCS1_5) == 0) { + prof->wrap_padding_method = + KMIP_PADDING_METHOD_PKCS_1_5; + } else if (strcasecmp(val, + KMIP_PROFILES_WRAP_PADDING_OAEP) == 0) { + prof->wrap_padding_method = KMIP_PADDING_METHOD_OAEP; + } else { + _set_error(ph, "Profile '%s': Invalid value for '%s': " + "'%s'", file_name, + KMIP_PROFILES_WRAP_PADDING_METHOD, val); + rc = -EINVAL; + goto out; + } + break; + default: + if (val != NULL) + pr_verbose(&ph->pd, "Profile: '%s': ignored '%s'", + KMIP_PROFILES_WRAP_PADDING_METHOD, val); + break; + } + free(val); + + val = properties_get(props, KMIP_PROFILES_WRAP_HASHING_ALOGRITHM); + switch (prof->wrap_padding_method) { + case KMIP_PADDING_METHOD_OAEP: + if (val == NULL) + val = util_strdup(KMIP_PROFILES_WRAP_HASHING_ALGO_SHA1); + pr_verbose(&ph->pd, "Profile: '%s': '%s'", + KMIP_PROFILES_WRAP_HASHING_ALOGRITHM, val); + if (strcasecmp(val, + KMIP_PROFILES_WRAP_HASHING_ALGO_SHA1) == 0) { + prof->wrap_hashing_algo = KMIP_HASHING_ALGO_SHA_1; + } else if (strcasecmp(val, + KMIP_PROFILES_WRAP_HASHING_ALGO_SHA256) == 0) { + prof->wrap_hashing_algo = KMIP_HASHING_ALGO_SHA_256; + } else { + _set_error(ph, "Profile '%s': Invalid value for '%s': " + "'%s'", file_name, + KMIP_PROFILES_WRAP_PADDING_METHOD, val); + rc = -EINVAL; + goto out; + } + break; + default: + if (val != NULL) + pr_verbose(&ph->pd, "Profile: '%s': ignored '%s'", + KMIP_PROFILES_WRAP_HASHING_ALOGRITHM, val); + break; + } + free(val); + + rc = profile_get_bool(ph, props, file_name, + KMIP_PROFILES_SUPPORTS_LINK_ATTR, false, + &prof->supports_link_attr); + if (rc != 0) + goto out; + + rc = profile_get_bool(ph, props, file_name, + KMIP_PROFILES_SUPPORTS_DESCRIPTION_ATTR, false, + &prof->supports_description_attr); + if (rc != 0) + goto out; + + rc = profile_get_bool(ph, props, file_name, + KMIP_PROFILES_SUPPORTS_COMMENT_ATTR, false, + &prof->supports_comment_attr); + if (rc != 0) + goto out; + + val = properties_get(props, KMIP_PROFILES_CUSTOM_ATTR_SCHEME); + if (val == NULL) + val = util_strdup(KMIP_PROFILES_CUST_ATTR_SCHEME_V1); + pr_verbose(&ph->pd, "Profile: '%s': '%s'", + KMIP_PROFILES_CUSTOM_ATTR_SCHEME, val); + if (strcasecmp(val, KMIP_PROFILES_CUST_ATTR_SCHEME_V1) == 0) { + prof->cust_attr_scheme = KMIP_PROFILE_CUST_ATTR_V1_STYLE; + } else if (strcasecmp(val, KMIP_PROFILES_CUST_ATTR_SCHEME_V2) == 0) { + prof->cust_attr_scheme = KMIP_PROFILE_CUST_ATTR_V2_STYLE; + } else { + _set_error(ph, "Profile '%s': Invalid value for '%s': '%s'", + file_name, KMIP_PROFILES_CUSTOM_ATTR_SCHEME, val); + rc = -EINVAL; + goto out; + } + free(val); + + rc = profile_get_bool(ph, props, file_name, + KMIP_PROFILES_SUPPORTS_SENSITIVE_ATTR, false, + &prof->supports_sensitive_attr); + if (rc != 0) + goto out; + + rc = profile_get_bool(ph, props, file_name, + KMIP_PROFILES_CHECK_ALWAYS_SENS_ATTR, false, + &prof->check_always_sensitive_attr); + if (rc != 0) + goto out; + + val = NULL; + +out: + properties_free(props); + if (file_name != NULL) + free(file_name); + if (val != NULL) + free(val); + + if (rc != 0) + profile_free(prof); + else + *profile = prof; + + return rc; +} + +/** + * Frees a profile + * + * @param profile On return: the allocated profile + * + * @returns 0 on success, a negative errno in case of an error. + */ +void profile_free(struct kmip_profile *profile) +{ + if (profile == NULL) + return; + + if (profile->name != NULL) + free((char *)profile->name); + if (profile->server_regex != NULL) + free((char *)profile->server_regex); + if (profile->https_uri != NULL) + free((char *)profile->https_uri); + + free(profile); +} + +/** + * Filters directory entries for scanfile(). Only entries that are regular + * files and who's name ends with '.info' are matched. + */ +static int profile_file_filter(const struct dirent *dirent) +{ + size_t len; + + if (dirent->d_type != DT_REG && dirent->d_type != DT_UNKNOWN) + return 0; + + len = strlen(dirent->d_name); + if (len > KMIP_PROFILES_FILE_TYPE_LEN && + strcmp(&dirent->d_name[len - KMIP_PROFILES_FILE_TYPE_LEN], + KMIP_PROFILES_FILE_TYPE) == 0) + return 1; + + return 0; +} + +/** + * Scans the default profile directory or the one specified by environment + * variable ZKEY_KMIP_PROFILES for profiles that match the server info. + * Returns the first profile that matches, or the default profile if none + * matches. + * + * @param ph the plugin handle + * @param server_info the server info string to match the profiles against + * @param profile On return: the matched profile. The caller must free + * the profile with profile_free. + * + * @returns 0 on success, a negative errno in case of an error. + */ +int profile_find_by_server_info(struct plugin_handle *ph, + const char *server_info, + struct kmip_profile **profile) +{ + struct kmip_profile *prof = NULL; + struct dirent **namelist; + const char *profile_dir; + regmatch_t pmatch[1]; + char err_buf[256]; + int i, n, rc = 0; + regex_t reg_buf; + + profile_dir = get_profiles_directory(); + pr_verbose(&ph->pd, "profile_dir: %s", profile_dir); + + n = scandir(profile_dir, &namelist, profile_file_filter, alphasort); + if (n < 0) { + rc = -errno; + pr_verbose(&ph->pd, "scandir failed with: %s", strerror(-rc)); + return rc; + } + + for (i = 0; i < n; i++) { + if (strcmp(namelist[i]->d_name, + KMIP_PROFILES_DEFAULT_PROFILE) == 0) + continue; + + pr_verbose(&ph->pd, "Found profile '%s'", namelist[i]->d_name); + + rc = profile_read(ph, profile_dir, namelist[i]->d_name, &prof); + if (rc != 0) { + pr_verbose(&ph->pd, "profile_read failed with: %s", + strerror(-rc)); + goto out; + } + + rc = regcomp(®_buf, prof->server_regex, REG_EXTENDED); + if (rc != 0) { + regerror(rc, ®_buf, err_buf, sizeof(err_buf)); + _set_error(ph, "Profile '%s': Regular expression " + "error: '%s'", namelist[i]->d_name, err_buf); + rc = -EINVAL; + goto out; + } + + rc = regexec(®_buf, server_info, (size_t)1, pmatch, 0); + regfree(®_buf); + if (rc == 0) + break; + + profile_free(prof); + prof = NULL; + } + + if (prof == NULL) { + rc = profile_read(ph, profile_dir, + KMIP_PROFILES_DEFAULT_PROFILE, &prof); + if (rc != 0) { + pr_verbose(&ph->pd, "default profile_read failed with: " + "%s", strerror(-rc)); + goto out; + } + } + + *profile = prof; + +out: + for (i = 0; i < n; i++) + free(namelist[i]); + free(namelist); + + return rc; +} + +/** + * Reads a profile by its name. Tries to find a profile with the specified name + * in the default profile directory or the one specified by environment + * variable ZKEY_KMIP_PROFILES and loads it. + * + * @param ph the plugin handle + * @param profile_name name of the profile to read + * @param profile On return: the allocated profile. The caller must + * free the profile with profile_free. + * + * @returns 0 on success, a negative errno in case of an error. + */ +int profile_find_by_name(struct plugin_handle *ph, const char *profile_name, + struct kmip_profile **profile) +{ + char *profile_file; + int rc; + + util_asprintf(&profile_file, "%s%s", profile_name, + KMIP_PROFILES_FILE_TYPE); + + rc = profile_read(ph, NULL, profile_file, profile); + + free(profile_file); + return rc; +} diff --git a/zkey/kmip/profiles/IBM-GDE.profile b/zkey/kmip/profiles/IBM-GDE.profile new file mode 100644 index 00000000..2aa372e4 --- /dev/null +++ b/zkey/kmip/profiles/IBM-GDE.profile @@ -0,0 +1,68 @@ +# KMIP server profile for IBM-GDE (IBM Security Guardium Data Encryption). + +# Regular expression to match the KMIP server information obtained from the +# KMIP server via the QUERY request. +server-regex=Vormetric.* + +# KMIP protocol version to use. Either major.minor, or AUTO. AUTO means that +# the supported KMIP protocol version is automatically discovered using the +# DISCOVER VERSIONS request. +kmip-version=2.1 + +# Transport method for the KMIP protocol: TLS or HTTPS +transport=TLS + +# Encoding method for the KMIP protocol: TTLV, JSON or XML. +# JSON and XML are only posisble with HTTPS transport. +encoding=TTLV + +# URI used for HTTPS transport. Can be overridden by user via --kmip-server +# option. Ignored if not HTTPS transport. +https-uri=/kmip + +# Authentication scheme. Currently only TLS client authentication is supported. +auth-scheme=TLSClientCert + +# Key wrapping algorithm for retrieving keys from the KMIP server. +# Currently only RSA is supported. +wrap-key-algorithm=RSA + +# For RSA key wrapping: the modulus size of the RSA key: 512, 1024, 2048, 4096 +wrap-key-params=4096 + +# Format used to register the public wrapping key with the KMIP server. +# For RSA: PKCS1, PKCS8, TransparentPublicKey +wrap-key-format=PKCS1 + +# Padding method used with key wrapping. +# For RSA: PKCS1.5 or OAEP +wrap-padding-method=OAEP + +# Hashing algorithm used with key wrapping. +# For RSA with OAEP: SHA-1 or SHA-256 +wrap-hashing-algorithm=SHA-1 + +# KMIP server supports 'Link' attribute. If TRUE, use to link the 2 keys of an +# XTS key together. +supports-link-attr=TRUE + +# KMIP server supports 'Description' attribute. +supports-description-attr=TRUE + +# KMIP server supports 'Comment' attribute. +supports-comment-attr=TRUE + +# Custom/Vendor attribute usage for KMIP v2.x servers. +# V1-style means to set 'Vendor Identifier' to 'x', and 'Attribute Name' to +# 'zkey-'. This coresponds to the KMIP v1.x Custom attribute style. +# V2-style means to set 'Vendor Identifier' to 'zkey', and 'Attribute Name' to +# ''. +custom-attr-scheme=v1-style + +# KMIP server supports 'Sensitive' attribute. If TRUE, all keys are generated +# with Sensitive=True to prevent the key from being retrieved in clear. +supports-sensitive-attr=TRUE + +# KMIP server supports 'Always Sensitive' attribute and it is checked to be +# True for all keys retrieved by the zkey-kmip plugin. +check-always-sensitive-attr=TRUE diff --git a/zkey/kmip/profiles/IBM-GKLM.profile b/zkey/kmip/profiles/IBM-GKLM.profile new file mode 100644 index 00000000..cd5d2f20 --- /dev/null +++ b/zkey/kmip/profiles/IBM-GKLM.profile @@ -0,0 +1,71 @@ +# KMIP server profile for IBM-GKLM (IBM Security Guardium Key Lifecycle Manager, +# formerly SKLM). + +# Regular expression to match the KMIP server information obtained from the +# KMIP server via the QUERY request. GKLM identifies itself with a string like: +# 'SKLM 4.1.0.1 KMIP 2.0 BUILD 202103261314 KMIP_SSL_TIMEOUT 5' +# Also check the version to be 4.1.x or later. +server-regex=(SKLM|GKLM) (4\.[1-9]\.|[5..9]\.\d*.).* + +# KMIP protocol version to use. Either major.minor, or AUTO. AUTO means that +# the supported KMIP protocol version is automatically discovered using the +# DISCOVER VERSIONS request. +kmip-version=2.1 + +# Transport method for the KMIP protocol: TLS or HTTPS +transport=TLS + +# Encoding method for the KMIP protocol: TTLV, JSON or XML. +# JSON and XML are only posisble with HTTPS transport. +encoding=TTLV + +# URI used for HTTPS transport. Can be overridden by user via --kmip-server +# option. Ignored if not HTTPS transport. +https-uri=/kmip + +# Authentication scheme. Currently only TLS client authentication is supported. +auth-scheme=TLSClientCert + +# Key wrapping algorithm for retrieving keys from the KMIP server. +# Currently only RSA is supported. +wrap-key-algorithm=RSA + +# For RSA key wrapping: the modulus size of the RSA key: 512, 1024, 2048, 4096 +wrap-key-params=4096 + +# Format used to register the public wrapping key with the KMIP server. +# For RSA: PKCS1, PKCS8, TransparentPublicKey +wrap-key-format=PKCS1 + +# Padding method used with key wrapping. +# For RSA: PKCS1.5 or OAEP +wrap-padding-method=OAEP + +# Hashing algorithm used with key wrapping. +# For RSA with OAEP: SHA-1 or SHA-256 +wrap-hashing-algorithm=SHA-1 + +# KMIP server supports 'Link' attribute. If TRUE, use to link the 2 keys of an +# XTS key together. +supports-link-attr=TRUE + +# KMIP server supports 'Description' attribute. +supports-description-attr=FALSE + +# KMIP server supports 'Comment' attribute. +supports-comment-attr=FALSE + +# Custom/Vendor attribute usage for KMIP v2.x servers. +# V1-style means to set 'Vendor Identifier' to 'x', and 'Attribute Name' to +# 'zkey-'. This coresponds to the KMIP v1.x Custom attribute style. +# V2-style means to set 'Vendor Identifier' to 'zkey', and 'Attribute Name' to +# ''. +custom-attr-scheme=v1-style + +# KMIP server supports 'Sensitive' attribute. If TRUE, all keys are generated +# with Sensitive=True to prevent the key from being retrieved in clear. +supports-sensitive-attr=TRUE + +# KMIP server supports 'Always Sensitive' attribute and it is checked to be +# True for all keys retrieved by the zkey-kmip plugin. +check-always-sensitive-attr=TRUE diff --git a/zkey/kmip/profiles/default.profile b/zkey/kmip/profiles/default.profile new file mode 100644 index 00000000..250176e1 --- /dev/null +++ b/zkey/kmip/profiles/default.profile @@ -0,0 +1,69 @@ +# Default KMIP server profile. +# If no other profile matches, this one is used. + +# Regular expression to match the KMIP server information obtained from the +# KMIP server via the QUERY request. +server-regex=.* + +# KMIP protocol version to use. Either major.minor, or AUTO. AUTO means that +# the supported KMIP protocol version is automatically discovered using the +# DISCOVER VERSIONS request. +kmip-version=AUTO + +# Transport method for the KMIP protocol: TLS or HTTPS +transport=TLS + +# Encoding method for the KMIP protocol: TTLV, JSON or XML. +# JSON and XML are only posisble with HTTPS transport. +encoding=TTLV + +# URI used for HTTPS transport. Can be overridden by user via --kmip-server +# option. Ignored if not HTTPS transport. +https-uri=/kmip + +# Authentication scheme. Currently only TLS client authentication is supported. +auth-scheme=TLSClientCert + +# Key wrapping algorithm for retrieving keys from the KMIP server. +# Currently only RSA is supported. +wrap-key-algorithm=RSA + +# For RSA key wrapping: the modulus size of the RSA key: 512, 1024, 2048, 4096 +wrap-key-params=2048 + +# Format used to register the public wrapping key with the KMIP server. +# For RSA: PKCS1, PKCS8, TransparentPublicKey +wrap-key-format=PKCS1 + +# Padding method used with key wrapping. +# For RSA: PKCS1.5 or OAEP +wrap-padding-method=PKCS1.5 + +# Hashing algorithm used with key wrapping. +# For RSA with OAEP: SHA-1 or SHA-256 +wrap-hashing-algorithm=SHA-1 + +# KMIP server supports 'Link' attribute. If TRUE, use to link the 2 keys of an +# XTS key together. +supports-link-attr=FALSE + +# KMIP server supports 'Description' attribute. +supports-description-attr=FALSE + +# KMIP server supports 'Comment' attribute. +supports-comment-attr=FALSE + +# Custom/Vendor attribute usage for KMIP v2.x servers. +# V1-style means to set 'Vendor Identifier' to 'x', and 'Attribute Name' to +# 'zkey-'. This coresponds to the KMIP v1.x Custom attribute style. +# V2-style means to set 'Vendor Identifier' to 'zkey', and 'Attribute Name' to +# ''. +custom-attr-scheme=v1-style + +# KMIP server supports 'Sensitive' attribute. If TRUE, all keys are generated +# with Sensitive=True to prevent the key from being retrieved in clear. +supports-sensitive-attr=FALSE + +# KMIP server supports 'Always Sensitive' attribute and it is checked to be +# True for all keys retrieved by the zkey-kmip plugin. +check-always-sensitive-attr=FALSE diff --git a/zkey/kmip/zkey-kmip.h b/zkey/kmip/zkey-kmip.h index bb04ff3c..32202770 100644 --- a/zkey/kmip/zkey-kmip.h +++ b/zkey/kmip/zkey-kmip.h @@ -65,5 +65,98 @@ struct plugin_handle { #define KMIP_CERT_EXT_SUBJECT_ALT_NAME "subjectAltName" #define KMIP_CERT_EXT_SUBJECT_ALT_NAME_DNS "subjectAltName=DNS:%s" +#define KMIP_PROFILES_LOCATION "/etc/zkey/kmip/profiles" +#define KMIP_PROFILES_LOCATION_ENVVAR "ZKEY_KMIP_PROFILES" +#define KMIP_PROFILES_FILE_TYPE ".profile" +#define KMIP_PROFILES_FILE_TYPE_LEN 8 +#define KMIP_PROFILES_DEFAULT_PROFILE "default.profile" + +#define KMIP_PROFILES_SERVER_REGEX "server-regex" +#define KMIP_PROFILES_KMIP_VERSION "kmip-version" +#define KMIP_PROFILES_TRANSPORT "transport" +#define KMIP_PROFILES_ENCODING "encoding" +#define KMIP_PROFILES_HTTPS_URI "https-uri" +#define KMIP_PROFILES_AUTH_SCHEME "auth-scheme" +#define KMIP_PROFILES_WRAP_KEY_ALGORITHM "wrap-key-algorithm" +#define KMIP_PROFILES_WRAP_KEY_PARAMS "wrap-key-params" +#define KMIP_PROFILES_WRAP_KEY_FORMAT "wrap-key-format" +#define KMIP_PROFILES_WRAP_PADDING_METHOD "wrap-padding-method" +#define KMIP_PROFILES_WRAP_HASHING_ALOGRITHM "wrap-hashing-algorithm" +#define KMIP_PROFILES_SUPPORTS_LINK_ATTR "supports-link-attr" +#define KMIP_PROFILES_SUPPORTS_DESCRIPTION_ATTR "supports-description-attr" +#define KMIP_PROFILES_SUPPORTS_COMMENT_ATTR "supports-comment-attr" +#define KMIP_PROFILES_CUSTOM_ATTR_SCHEME "custom-attr-scheme" +#define KMIP_PROFILES_SUPPORTS_SENSITIVE_ATTR "supports-sensitive-attr" +#define KMIP_PROFILES_CHECK_ALWAYS_SENS_ATTR "check-always-sensitive-attr" + +#define KMIP_PROFILES_VERSION_AUTO "AUTO" + +#define KMIP_PROFILES_TRANSPORT_TLS "TLS" +#define KMIP_PROFILES_TRANSPORT_HTTPS "HTTPS" + +#define KMIP_PROFILES_ENCODING_TTLV "TTLV" +#define KMIP_PROFILES_ENCODING_JSON "JSON" +#define KMIP_PROFILES_ENCODING_XML "XML" + +#define KMIP_PROFILES_HTTPS_URI_DEFAULT "/kmip" + +#define KMIP_PROFILES_AUTH_TLS_CLIENT_CERT "TLSClientCert" + +#define KMIP_PROFILES_WRAP_KEY_ALGORITHM_RSA "RSA" + +#define KMIP_PROFILES_WRAP_KEY_FORMAT_PKCS1 "PKCS1" +#define KMIP_PROFILES_WRAP_KEY_FORMAT_PKCS8 "PKCS8" +#define KMIP_PROFILES_WRAP_KEY_FORMAT_TRANSP "TransparentPublicKey" + +#define KMIP_PROFILES_WRAP_PADDING_PKCS1_5 "PKCS1.5" +#define KMIP_PROFILES_WRAP_PADDING_OAEP "OAEP" + +#define KMIP_PROFILES_WRAP_HASHING_ALGO_SHA1 "SHA-1" +#define KMIP_PROFILES_WRAP_HASHING_ALGO_SHA256 "SHA-256" + +#define KMIP_PROFILES_BOOLEAN_TRUE "TRUE" +#define KMIP_PROFILES_BOOLEAN_FALSE "FALSE" + +#define KMIP_PROFILES_CUST_ATTR_SCHEME_V1 "v1-style" +#define KMIP_PROFILES_CUST_ATTR_SCHEME_V2 "v2-style" + +enum kmip_profile_auth_scheme { + KMIP_PROFILE_AUTH_TLS_CLIENT_CERT = 1, +}; + +enum kmip_profile_cust_attr_scheme { + KMIP_PROFILE_CUST_ATTR_V1_STYLE = 1, /* x-zkey-something */ + KMIP_PROFILE_CUST_ATTR_V2_STYLE = 2, /* zkey-something */ +}; + +struct kmip_profile { + const char *name; + const char *server_regex; + struct kmip_version kmip_version; /* 0.0 means AUTO */ + enum kmip_transport transport; /* Default: TLS */ + enum kmip_encoding encoding; /* Default : TTLV */ + const char *https_uri; /* Default '/kmip' for HTTPS transport */ + enum kmip_profile_auth_scheme auth_scheme; /* Default: TLSClientCert */ + enum kmip_crypto_algo wrap_key_algo; /* only RSA supported currently */ + size_t wrap_key_size; /* Required for RSA */ + enum kmip_key_format_type wrap_key_format; /* Default for RSA: PKCS1 */ + enum kmip_padding_method wrap_padding_method; /* RSA default: PKCS 1.5*/ + enum kmip_hashing_algo wrap_hashing_algo; /* OAEP default: SHA-1 */ + bool supports_link_attr; /* Default: FALSE */ + bool supports_description_attr; /* Default: FALSE */ + bool supports_comment_attr; /* Default: FALSE */ + enum kmip_profile_cust_attr_scheme cust_attr_scheme; + bool supports_sensitive_attr; /* Default: FALSE */ + bool check_always_sensitive_attr; /* Default: FALSE */ +}; + +int profile_read(struct plugin_handle *ph, const char *profile_dir, + const char *profile_file, struct kmip_profile **profile); +void profile_free(struct kmip_profile *profile); +int profile_find_by_server_info(struct plugin_handle *ph, + const char *server_info, + struct kmip_profile **profile); +int profile_find_by_name(struct plugin_handle *ph, const char *profile_name, + struct kmip_profile **profile); #endif