SHA256
1
0
forked from pool/s390-tools
s390-tools/s390-tools-sles15sp1-0001-zkey-Add-properties-file-handling-routines.patch

507 lines
13 KiB
Diff

Subject: zkey: Add properties file handling routines
From: Philipp Rudo <prudo@linux.ibm.com>
Summary: zkey: Add support of protected key crypto for dm-crypt.
Description: Support the usage of protected key crypto for dm-crypt disks in
plain format by providing a tool to manage a key repository
allowing to associate secure keys with disk partitions or logical
volumes.
Upstream-ID: 340da73bb7f06a9fc2aecfe4e33f1f3a17b3568d
Problem-ID: SEC1800
Upstream-Description:
zkey: Add properties file handling routines
In preparation for a new feature, introduce property file
handling routines. A property file stores key value pairs
in a text file. Optionally a hash of all keys and values
contained in the properties file can be generated to
ensure integrity of the properties file and to detect
manual modifications.
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Philipp Rudo <prudo@linux.ibm.com>
---
zkey/Makefile | 5
zkey/properties.c | 409 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
zkey/properties.h | 36 ++++
3 files changed, 448 insertions(+), 2 deletions(-)
--- a/zkey/Makefile
+++ b/zkey/Makefile
@@ -1,15 +1,16 @@
include ../common.mak
CPPFLAGS += -I../include
-LDLIBS += -ldl
+LDLIBS += -ldl -lcrypto
all: zkey
libs = $(rootdir)/libutil/libutil.a
zkey.o: zkey.c pkey.h misc.h
+properties.o: properties.c properties.h
-zkey: zkey.o $(libs)
+zkey: zkey.o properties.o $(libs)
install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
--- /dev/null
+++ b/zkey/properties.c
@@ -0,0 +1,409 @@
+/*
+ * zkey - Generate, re-encipher, and validate secure keys
+ *
+ * Properties file handling functions
+ *
+ * Copyright IBM Corp. 2018
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/evp.h>
+
+#include "lib/util_libc.h"
+#include "lib/util_list.h"
+#include "lib/util_panic.h"
+
+#include "properties.h"
+
+struct properties {
+ struct util_list list;
+};
+
+struct property {
+ struct util_list_node node;
+ char *name;
+ char *value;
+};
+
+#define SHA256_DIGEST_LEN 32
+#define INTEGRITY_KEY_NAME "__hash__"
+
+#define RESTRICTED_PROPERTY_NAME_CHARS "=\n"
+#define RESTRICTED_PROPERTY_VALUE_CHARS "\n"
+
+static int openssl_initialized;
+
+/**
+ * Allocate and initialize a SHA-256 context
+ *
+ * @returns a SHA context
+ */
+static EVP_MD_CTX *sha256_init(void)
+{
+ EVP_MD_CTX *ctx;
+ int rc;
+
+ if (!openssl_initialized) {
+ OpenSSL_add_all_algorithms();
+ openssl_initialized = 1;
+ }
+
+ ctx = EVP_MD_CTX_create();
+ util_assert(ctx != NULL,
+ "Internal error: OpenSSL MD context allocation failed");
+
+ rc = EVP_DigestInit_ex(ctx, EVP_sha256(), NULL);
+ util_assert(rc == 1, "Internal error: SHA-256 digest init failed");
+
+ return ctx;
+}
+
+/**
+ * Add data to the SHA-256 context
+ *
+ * @parm[in] ctx the SHA context
+ * @parm[in] data the data to be hashed
+ * @parm[in] data_len the length of the data
+ */
+static void sha256_update(EVP_MD_CTX *ctx,
+ const char *data, unsigned int data_len)
+{
+ int rc;
+
+ util_assert(ctx != NULL, "Internal error: OpenSSL MD context is NULL");
+ util_assert(data != NULL || data_len == 0,
+ "Internal error: data is NULL");
+
+ rc = EVP_DigestUpdate(ctx, data, data_len);
+
+ util_assert(rc == 1, "Internal error: SHA-256 digest udpdate failed");
+}
+
+/**
+ * Produce the digest for the SHA-256 context and free the context
+ *
+ * @parm[in] ctx the SHA context
+ * @parm[out] digest a buffer where the digest is stored
+ * @parm[in/out] digest_len on entry, *digest_len contains the size of the
+ * digest buffer, which must be large enough to hold
+ * a SHA-256 digest (32 bytes),
+ * on exit it contains the size of the digest
+ * returned in the buffer.
+ */
+static void sha256_final(EVP_MD_CTX *ctx,
+ unsigned char *digest, unsigned int *digest_len)
+{
+ int rc;
+
+ util_assert(ctx != NULL, "Internal error: OpenSSL MD context is NULL");
+
+ if (digest != NULL && digest_len != NULL) {
+ util_assert(*digest_len >= (unsigned int)EVP_MD_CTX_size(ctx),
+ "Internal error: digest_len is too small");
+
+ rc = EVP_DigestFinal_ex(ctx, digest, digest_len);
+ util_assert(rc == 1,
+ "Internal error: SHA-256 digest final failed");
+ }
+
+ EVP_MD_CTX_destroy(ctx);
+}
+
+/**
+ * Allocates a new properties object
+ *
+ * @returns the properties object
+ */
+struct properties *properties_new(void)
+{
+ struct properties *properties;
+
+ properties = util_zalloc(sizeof(struct properties));
+
+ util_list_init_offset(&properties->list,
+ offsetof(struct property, node));
+ return properties;
+}
+
+/**
+ * Frees a properties object with all its properties
+ *
+ * @param[in] properties the properties object
+ */
+void properties_free(struct properties *properties)
+{
+ struct property *property;
+
+ util_assert(properties != NULL, "Internal error: properties is NULL");
+
+ while ((property = util_list_start(&properties->list)) != NULL) {
+ free(property->name);
+ free(property->value);
+ util_list_remove(&properties->list, property);
+ }
+
+ free(properties);
+}
+
+/**
+ * Find a property by its name in the list iof properties
+ *
+ * @param[in] properties the properties object
+ * @param[in] name the name of the property to find
+ *
+ * @returns a pointer to the proerty when it has been found, or NULL if not
+ */
+static struct property *properties_find(struct properties *properties,
+ const char *name)
+{
+ struct property *property;
+
+ property = util_list_start(&properties->list);
+ while (property != NULL) {
+ if (strcmp(property->name, name) == 0)
+ return property;
+ property = util_list_next(&properties->list, property);
+ }
+ return NULL;
+}
+
+/**
+ * Adds or updates a property
+ *
+ * @param[in] properties the properties object
+ * @param[in] name the name of the property
+ * @param[in] value the value of the property
+ *
+ * @returns 0 on success,
+ * -EINVAL if the name or value contains invalid characters
+ */
+int properties_set(struct properties *properties,
+ const char *name, const char *value)
+{
+ struct property *property;
+
+ util_assert(properties != NULL, "Internal error: properties is NULL");
+ util_assert(name != NULL, "Internal error: name is NULL");
+ util_assert(value != NULL, "Internal error: value is NULL");
+
+ if (strpbrk(name, RESTRICTED_PROPERTY_NAME_CHARS) != NULL)
+ return -EINVAL;
+ if (strpbrk(value, RESTRICTED_PROPERTY_VALUE_CHARS) != NULL)
+ return -EINVAL;
+
+ property = properties_find(properties, name);
+ if (property != NULL) {
+ free(property->value);
+ property->value = util_strdup(value);
+ } else {
+ property = util_zalloc(sizeof(struct property));
+ property->name = util_strdup(name);
+ property->value = util_strdup(value);
+ util_list_add_tail(&properties->list, property);
+ }
+ return 0;
+}
+
+/**
+ * Gets a property
+ *
+ * @param[in] properties the properties object
+ * @param[in] name the name of the property
+ *
+ * @returns a string containing the property value, or NULL if the property
+ * was not found.
+ * Note: The returned string must be freed via free() by the caller.
+ */
+char *properties_get(struct properties *properties, const char *name)
+{
+ struct property *property;
+
+ util_assert(properties != NULL, "Internal error: properties is NULL");
+ util_assert(name != NULL, "Internal error: name is NULL");
+
+ property = properties_find(properties, name);
+ if (property == NULL)
+ return NULL;
+
+ return util_strdup(property->value);
+}
+
+/**
+ * Removes a property
+ *
+ * @param[in] properties the properties object
+ * @param[in] name the name of the property
+ *
+ * @returns 0 on success, -ENOENT if the property was not found.
+ */
+int properties_remove(struct properties *properties, const char *name)
+{
+ struct property *property;
+
+ util_assert(properties != NULL, "Internal error: properties is NULL");
+ util_assert(name != NULL, "Internal error: name is NULL");
+
+ property = properties_find(properties, name);
+ if (property == NULL)
+ return -ENOENT;
+
+ free(property->name);
+ free(property->value);
+ util_list_remove(&properties->list, property);
+ return 0;
+}
+
+/**
+ * Saves the properties to a file
+ *
+ * @param[in] properties the properties object
+ * @param[in] filename the file name
+ * @param[in] check_integrity if TRUE, an hash of the key and values is
+ * stored as part of the file.
+ *
+ * @returns 0 on success, -EIO the file could not be created
+ */
+int properties_save(struct properties *properties, const char *filename,
+ bool check_integrity)
+{
+ char digest_hex[SHA256_DIGEST_LEN * 2 + 1];
+ unsigned char digest[SHA256_DIGEST_LEN];
+ unsigned int digest_len = sizeof(digest);
+ struct property *property;
+ EVP_MD_CTX *ctx = NULL;
+ unsigned int i;
+ FILE *fp;
+
+ util_assert(properties != NULL, "Internal error: properties is NULL");
+ util_assert(filename != NULL, "Internal error: filename is NULL");
+
+ fp = fopen(filename, "w");
+ if (fp == NULL)
+ return -EIO;
+
+ if (check_integrity)
+ ctx = sha256_init();
+
+ property = util_list_start(&properties->list);
+ while (property != NULL) {
+ fprintf(fp, "%s=%s\n", property->name, property->value);
+
+ if (check_integrity) {
+ sha256_update(ctx, property->name,
+ strlen(property->name));
+ sha256_update(ctx, property->value,
+ strlen(property->value));
+ }
+
+ property = util_list_next(&properties->list, property);
+ }
+
+ if (check_integrity) {
+ sha256_final(ctx, digest, &digest_len);
+ util_assert(digest_len <= SHA256_DIGEST_LEN,
+ "Internal error: digest length too long");
+
+ for (i = 0; i < digest_len; i++)
+ sprintf(&digest_hex[i * 2], "%02x", digest[i]);
+ digest_hex[digest_len * 2] = '\0';
+
+ fprintf(fp, "%s=%s\n", INTEGRITY_KEY_NAME, digest_hex);
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+/**
+ * Loads the properties from a file
+ *
+ * @param[in] properties the properties object
+ * @param[in] filename the file name
+ * @param[in] check_integrity if TRUE, an hash of the key and values is
+ * compared with the hash stored as part of the file.
+ *
+ * @returns 0 on success, -EIO the file could not be created,
+ * -EPERM in case of a syntax error or an integrity error
+ */
+int properties_load(struct properties *properties, const char *filename,
+ bool check_integrity)
+{
+ char digest_hex[SHA256_DIGEST_LEN * 2 + 1];
+ unsigned char digest[SHA256_DIGEST_LEN];
+ unsigned int digest_len = sizeof(digest);
+ char *digest_read = NULL;
+ EVP_MD_CTX *ctx = NULL;
+ char line[4096];
+ unsigned int len, i;
+ int rc = 0;
+ char *ch;
+ FILE *fp;
+
+ util_assert(properties != NULL, "Internal error: properties is NULL");
+ util_assert(filename != NULL, "Internal error: filename is NULL");
+
+ fp = fopen(filename, "r");
+ if (fp == NULL)
+ return -EIO;
+
+ if (check_integrity)
+ ctx = sha256_init();
+
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ len = strlen(line);
+ if (line[len-1] == '\n')
+ line[len-1] = '\0';
+ ch = strchr(line, '=');
+ if (ch == NULL) {
+ rc = -EPERM;
+ goto out;
+ }
+
+ *ch = '\0';
+ ch++;
+
+ if (check_integrity) {
+ if (strcmp(line, INTEGRITY_KEY_NAME) == 0) {
+ digest_read = util_strdup(ch);
+ continue;
+ }
+
+ sha256_update(ctx, line, strlen(line));
+ sha256_update(ctx, ch, strlen(ch));
+ }
+
+ properties_set(properties, line, ch);
+ }
+
+ if (check_integrity) {
+ sha256_final(ctx, digest, &digest_len);
+ ctx = NULL;
+ util_assert(digest_len <= SHA256_DIGEST_LEN,
+ "Internal error: digest length too long");
+
+ for (i = 0; i < digest_len; i++)
+ sprintf(&digest_hex[i * 2], "%02x", digest[i]);
+ digest_hex[digest_len * 2] = '\0';
+
+ if (digest_read == NULL ||
+ strcmp(digest_hex, digest_read) != 0) {
+ rc = -EPERM;
+ goto out;
+ }
+ }
+
+out:
+ if (ctx != NULL)
+ sha256_final(ctx, NULL, NULL);
+ if (digest_read != NULL)
+ free(digest_read);
+ fclose(fp);
+ return rc;
+}
--- /dev/null
+++ b/zkey/properties.h
@@ -0,0 +1,36 @@
+/*
+ * zkey - Generate, re-encipher, and validate secure keys
+ *
+ * Properties file handling functions
+ *
+ * Copyright IBM Corp. 2018
+ *
+ * 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 PROPFILE_H
+#define PROPFILE_H
+
+#include <stdbool.h>
+
+struct properties;
+
+struct properties *properties_new(void);
+
+void properties_free(struct properties *properties);
+
+int properties_set(struct properties *properties,
+ const char *name, const char *value);
+
+char *properties_get(struct properties *properties, const char *name);
+
+int properties_remove(struct properties *properties, const char *name);
+
+int properties_save(struct properties *properties, const char *filename,
+ bool check_integrity);
+
+int properties_load(struct properties *properties, const char *filename,
+ bool check_integrity);
+
+#endif