/*
 * Use database as policy and keystore
 *
 * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
 */

#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#ifdef	WITH_SECURITY
#include <openssl/pem.h>
#include <openssl/err.h>
#endif
#include <libisns/isns.h>
#include "security.h"
#include "objects.h"
#include "vendor.h"
#include <libisns/util.h>

/*
 * DB keystore
 */
typedef struct isns_db_keystore isns_db_keystore_t;
struct isns_db_keystore {
	isns_keystore_t		sd_base;
	isns_db_t *		sd_db;
	isns_object_t *		sd_control;
};

/*
 * Look up the policy object given its SPI
 */
isns_object_t *
__isns_db_keystore_lookup(isns_db_keystore_t *store,
		const char *name, size_t namelen)
{
	isns_attr_list_t keys = ISNS_ATTR_LIST_INIT;
	char		namebuf[256];

	if (namelen >= sizeof(namebuf))
		return NULL;
	memcpy(namebuf, name, namelen);
	namebuf[namelen] = '\0';

	isns_attr_list_append_string(&keys,
			OPENISNS_TAG_POLICY_SPI,
			namebuf);
	return isns_db_lookup(store->sd_db, NULL, &keys);
}

/*
 * Load a DSA key from the DB store
 */
#ifdef WITH_SECURITY
static EVP_PKEY *
__isns_db_keystore_find(isns_keystore_t *store_base,
		const char *name, size_t namelen)
{
	isns_db_keystore_t *store = (isns_db_keystore_t *) store_base;
	isns_object_t	*obj;
	const void	*key_data;
	size_t		key_size;

	obj = __isns_db_keystore_lookup(store, name, namelen);
	if (obj == NULL)
		return NULL;

	if (!isns_object_get_opaque(obj, OPENISNS_TAG_POLICY_KEY,
				&key_data, &key_size))
		return NULL;

	return isns_dsa_decode_public(key_data, key_size);
}
#else	/* WITH_SECURITY */
static EVP_PKEY *
__isns_db_keystore_find(__attribute__((unused))isns_keystore_t *store_base,
			__attribute__((unused))const char *name,
			__attribute__((unused))size_t namelen)
{
	return NULL;
}
#endif	/* WITH_SECURITY */

/*
 * Retrieve policy from database
 */
static void
__isns_db_keystore_copy_policy_string(isns_object_t *obj,
		uint32_t tag, char **var)
{
	const char	*value;

	if (!isns_object_get_string(obj, tag, &value))
		return;
	isns_assign_string(var, value);
}

static void
__isns_db_keystore_copy_policy_strings(isns_object_t *obj,
		uint32_t tag, struct string_array *array)
{
	isns_attr_list_t *attrs = &obj->ie_attrs;
	unsigned int	i;

	for (i = 0; i < attrs->ial_count; ++i) {
		isns_attr_t *attr = attrs->ial_data[i];

		if (attr->ia_tag_id != tag
		 || !ISNS_ATTR_IS_STRING(attr))
			continue;
		isns_string_array_append(array, attr->ia_value.iv_string);
	}
}

static isns_policy_t *
__isns_db_keystore_get_policy(isns_keystore_t *store_base,
		     const char *name, size_t namelen)
{
	isns_db_keystore_t *store = (isns_db_keystore_t *) store_base;
	isns_policy_t	*policy;
	isns_object_t	*obj;
	uint32_t	intval;

	obj = __isns_db_keystore_lookup(store, name, namelen);
	if (obj == NULL)
		return NULL;

	policy = __isns_policy_alloc(name, namelen);

	/* retrieve policy bits from object */
#if 0
	__isns_db_keystore_copy_policy_string(obj,
			OPENISNS_TAG_POLICY_SOURCE_NAME,
			&policy->ip_source);
#endif
	__isns_db_keystore_copy_policy_string(obj,
			OPENISNS_TAG_POLICY_ENTITY,
			&policy->ip_entity);
	__isns_db_keystore_copy_policy_string(obj,
			OPENISNS_TAG_POLICY_DEFAULT_DD,
			&policy->ip_dd_default);
	__isns_db_keystore_copy_policy_strings(obj,
			OPENISNS_TAG_POLICY_NODE_NAME,
			&policy->ip_node_names);

	if (isns_object_get_uint32(obj, OPENISNS_TAG_POLICY_OBJECT_TYPE, &intval))
		policy->ip_object_types = intval;
	if (isns_object_get_uint32(obj, OPENISNS_TAG_POLICY_NODE_TYPE, &intval))
		policy->ip_node_types = intval;
	if (isns_object_get_uint32(obj, OPENISNS_TAG_POLICY_FUNCTIONS, &intval))
		policy->ip_functions = intval;

	return policy;
}

void
__isns_db_keystore_change_notify(const isns_db_event_t *ev, void *handle)
{
	isns_db_keystore_t *store = handle;
	isns_object_t *obj = ev->ie_object;

	if (isns_object_get_entity(obj) == store->sd_control) {
		isns_debug_auth("DB keystore: policy data was modified\n");
		store->sd_base.ic_generation++;
	}
}

isns_keystore_t *
isns_create_db_keystore(isns_db_t *db)
{
	isns_db_keystore_t *store;
	isns_object_t	*entity;

	isns_debug_auth("Creating DB keystore\n");
	if (!(entity = isns_db_get_control(db))) {
		isns_error("Could not create control entity in database\n");
		return NULL;
	}
	isns_debug_auth("Control entity is 0x%08x\n", entity->ie_index);

	store = isns_calloc(1, sizeof(*store));
	store->sd_base.ic_name = "database key store";
	store->sd_base.ic_find = __isns_db_keystore_find;
	store->sd_base.ic_get_policy = __isns_db_keystore_get_policy;
	store->sd_control = entity;
	store->sd_db = db;

	isns_register_callback(__isns_db_keystore_change_notify, store);

	return (isns_keystore_t *) store;
}

