OpenSolaris_b135/cmd/cmd-crypto/cryptoadm/adm_kef_ioctl.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <locale.h>
#include <libgen.h>
#include <sys/types.h>
#include <zone.h>
#include <sys/crypto/ioctladmin.h>
#include "cryptoadm.h"

#define	DEFAULT_DEV_NUM 5
#define	DEFAULT_SOFT_NUM 10

static crypto_get_soft_info_t *setup_get_soft_info(char *, int);

/*
 * Prepare the argument for the LOAD_SOFT_CONFIG ioctl call for the
 * provider pointed by pent.  Return NULL if out of memory.
 */
crypto_load_soft_config_t *
setup_soft_conf(entry_t *pent)
{
	crypto_load_soft_config_t	*pload_soft_conf;
	mechlist_t	*plist;
	uint_t		sup_count;
	size_t		extra_mech_size = 0;
	int		i;

	if (pent == NULL) {
		return (NULL);
	}

	sup_count = pent->sup_count;
	if (sup_count > 1) {
		extra_mech_size = sizeof (crypto_mech_name_t) *
		    (sup_count - 1);
	}

	pload_soft_conf = malloc(sizeof (crypto_load_soft_config_t) +
	    extra_mech_size);
	if (pload_soft_conf == NULL) {
		cryptodebug("out of memory.");
		return (NULL);
	}

	(void) strlcpy(pload_soft_conf->sc_name, pent->name, MAXNAMELEN);
	pload_soft_conf->sc_count = sup_count;

	i = 0;
	plist =  pent->suplist;
	while (i < sup_count) {
		(void) strlcpy(pload_soft_conf->sc_list[i++],
		    plist->name, CRYPTO_MAX_MECH_NAME);
		plist = plist->next;
	}

	return (pload_soft_conf);
}


/*
 * Prepare the argument for the LOAD_SOFT_DISABLED ioctl call for the
 * provider pointed by pent.  Return NULL if out of memory.
 */
crypto_load_soft_disabled_t *
setup_soft_dis(entry_t *pent)
{
	crypto_load_soft_disabled_t	*pload_soft_dis = NULL;
	mechlist_t	*plist = NULL;
	size_t		extra_mech_size = 0;
	uint_t		dis_count;
	int		i;

	if (pent == NULL) {
		return (NULL);
	}

	dis_count = pent->dis_count;
	if (dis_count > 1) {
		extra_mech_size = sizeof (crypto_mech_name_t) *
		    (dis_count - 1);
	}

	pload_soft_dis = malloc(sizeof (crypto_load_soft_disabled_t) +
	    extra_mech_size);
	if (pload_soft_dis == NULL) {
		cryptodebug("out of memory.");
		return (NULL);
	}

	(void) strlcpy(pload_soft_dis->sd_name, pent->name, MAXNAMELEN);
	pload_soft_dis->sd_count = dis_count;

	i = 0;
	plist =  pent->dislist;
	while (i < dis_count) {
		(void) strlcpy(pload_soft_dis->sd_list[i++],
		    plist->name, CRYPTO_MAX_MECH_NAME);
		plist = plist->next;
	}

	return (pload_soft_dis);
}


/*
 * Prepare the argument for the LOAD_DEV_DISABLED ioctl call for the
 * provider pointed by pent.  Return NULL if out of memory.
 */
crypto_load_dev_disabled_t *
setup_dev_dis(entry_t *pent)
{
	crypto_load_dev_disabled_t	*pload_dev_dis = NULL;
	mechlist_t	*plist = NULL;
	size_t		extra_mech_size = 0;
	uint_t		dis_count;
	int		i;
	char		pname[MAXNAMELEN];
	int		inst_num;

	if (pent == NULL) {
		return (NULL);
	}

	/* get the device name and the instance number */
	if (split_hw_provname(pent->name, pname, &inst_num) == FAILURE) {
		return (NULL);
	}

	/* allocate space for pload_dev_des */
	dis_count = pent->dis_count;
	if (dis_count > 1) {
		extra_mech_size = sizeof (crypto_mech_name_t) *
		    (dis_count - 1);
	}

	pload_dev_dis = malloc(sizeof (crypto_load_dev_disabled_t) +
	    extra_mech_size);
	if (pload_dev_dis == NULL) {
		cryptodebug("out of memory.");
		return (NULL);
	}

	/* set the values for pload_dev_dis */
	(void) strlcpy(pload_dev_dis->dd_dev_name, pname, MAXNAMELEN);
	pload_dev_dis->dd_dev_instance = inst_num;
	pload_dev_dis->dd_count = dis_count;

	i = 0;
	plist =  pent->dislist;
	while (i < dis_count) {
		(void) strlcpy(pload_dev_dis->dd_list[i++],
		    plist->name, CRYPTO_MAX_MECH_NAME);
		plist = plist->next;
	}

	return (pload_dev_dis);
}


/*
 * Prepare the calling argument of the UNLOAD_SOFT_MODULE ioctl call for the
 * provider pointed by pent.  Return NULL if out of memory.
 */
crypto_unload_soft_module_t *
setup_unload_soft(entry_t *pent)
{
	crypto_unload_soft_module_t *punload_soft;

	if (pent == NULL) {
		return (NULL);
	}

	punload_soft = malloc(sizeof (crypto_unload_soft_module_t));
	if (punload_soft == NULL) {
		cryptodebug("out of memory.");
		return (NULL);
	}

	(void) strlcpy(punload_soft->sm_name, pent->name, MAXNAMELEN);

	return (punload_soft);
}


/*
 * Prepare the calling argument for the GET_SOFT_INFO call for the provider
 * with the number of mechanisms specified in the second argument.
 *
 * Called by get_soft_info().
 */
static crypto_get_soft_info_t *
setup_get_soft_info(char *provname, int count)
{
	crypto_get_soft_info_t	*psoft_info;
	size_t			extra_mech_size = 0;

	if (provname == NULL) {
		return (NULL);
	}

	if (count > 1) {
		extra_mech_size = sizeof (crypto_mech_name_t) * (count - 1);
	}

	psoft_info = malloc(sizeof (crypto_get_soft_info_t) + extra_mech_size);
	if (psoft_info == NULL) {
		cryptodebug("out of memory.");
		return (NULL);
	}

	(void) strlcpy(psoft_info->si_name, provname, MAXNAMELEN);
	psoft_info->si_count = count;

	return (psoft_info);
}


/*
 * Get the device list from kernel.
 */
int
get_dev_list(crypto_get_dev_list_t **ppdevlist)
{
	crypto_get_dev_list_t	*pdevlist;
	int			fd = -1;
	int			count = DEFAULT_DEV_NUM;

	pdevlist = malloc(sizeof (crypto_get_dev_list_t) +
	    sizeof (crypto_dev_list_entry_t) * (count - 1));
	if (pdevlist == NULL) {
		cryptodebug("out of memory.");
		return (FAILURE);
	}

	if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
		cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
		    ADMIN_IOCTL_DEVICE, strerror(errno));
		return (FAILURE);
	}

	pdevlist->dl_dev_count = count;
	if (ioctl(fd, CRYPTO_GET_DEV_LIST, pdevlist) == -1) {
		cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed: %s",
		    strerror(errno));
		free(pdevlist);
		(void) close(fd);
		return (FAILURE);
	}

	/* BUFFER is too small, get the number of devices and retry it. */
	if (pdevlist->dl_return_value == CRYPTO_BUFFER_TOO_SMALL) {
		count = pdevlist->dl_dev_count;
		free(pdevlist);
		pdevlist = malloc(sizeof (crypto_get_dev_list_t) +
		    sizeof (crypto_dev_list_entry_t) * (count - 1));
		if (pdevlist == NULL) {
			cryptodebug("out of memory.");
			(void) close(fd);
			return (FAILURE);
		}

		if (ioctl(fd, CRYPTO_GET_DEV_LIST, pdevlist) == -1) {
			cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed: %s",
			    strerror(errno));
			free(pdevlist);
			(void) close(fd);
			return (FAILURE);
		}
	}

	if (pdevlist->dl_return_value != CRYPTO_SUCCESS) {
		cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed, "
		    "return_value = %d", pdevlist->dl_return_value);
		free(pdevlist);
		(void) close(fd);
		return (FAILURE);
	}

	*ppdevlist = pdevlist;
	(void) close(fd);
	return (SUCCESS);
}


/*
 * Get all the mechanisms supported by the hardware provider.
 * The result will be stored in the second argument.
 */
int
get_dev_info(char *devname, int inst_num, int count, mechlist_t **ppmechlist)
{
	crypto_get_dev_info_t	*dev_info;
	mechlist_t	*phead;
	mechlist_t	*pcur;
	mechlist_t	*pmech;
	int		fd = -1;
	int		i;
	int		rc;

	if (devname == NULL || count < 1) {
		cryptodebug("get_dev_info(): devname is NULL or bogus count");
		return (FAILURE);
	}

	/* Set up the argument for the CRYPTO_GET_DEV_INFO ioctl call */
	dev_info = malloc(sizeof (crypto_get_dev_info_t) +
	    sizeof (crypto_mech_name_t) * (count - 1));
	if (dev_info == NULL) {
		cryptodebug("out of memory.");
		return (FAILURE);
	}
	(void) strlcpy(dev_info->di_dev_name, devname, MAXNAMELEN);
	dev_info->di_dev_instance = inst_num;
	dev_info->di_count = count;

	/* Open the ioctl device */
	if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
		cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
		    ADMIN_IOCTL_DEVICE, strerror(errno));
		free(dev_info);
		return (FAILURE);
	}

	if (ioctl(fd, CRYPTO_GET_DEV_INFO, dev_info) == -1) {
		cryptodebug("CRYPTO_GET_DEV_INFO ioctl failed: %s",
		    strerror(errno));
		free(dev_info);
		(void) close(fd);
		return (FAILURE);
	}

	if (dev_info->di_return_value != CRYPTO_SUCCESS) {
		cryptodebug("CRYPTO_GET_DEV_INFO ioctl failed, "
		    "return_value = %d", dev_info->di_return_value);
		free(dev_info);
		(void) close(fd);
		return (FAILURE);
	}

	phead = pcur = NULL;
	rc = SUCCESS;
	for (i = 0; i < dev_info->di_count; i++) {
		pmech = create_mech(&dev_info->di_list[i][0]);
		if (pmech == NULL) {
			rc = FAILURE;
			break;
		} else {
			if (phead == NULL) {
				phead = pcur = pmech;
			} else {
				pcur->next = pmech;
				pcur = pmech;
			}
		}
	}

	if (rc == SUCCESS) {
		*ppmechlist = phead;
	} else {
		free_mechlist(phead);
	}

	free(dev_info);
	(void) close(fd);
	return (rc);
}


/*
 * Get the supported mechanism list of the software provider from kernel.
 *
 * Parameters phardlist and psoftlist are supplied by get_kcfconf_info().
 * If NULL, this function calls get_kcfconf_info() internally.
 */
int
get_soft_info(char *provname, mechlist_t **ppmechlist,
	entrylist_t *phardlist, entrylist_t *psoftlist)
{
	boolean_t		in_kernel = B_FALSE;
	crypto_get_soft_info_t	*psoft_info;
	mechlist_t		*phead;
	mechlist_t		*pmech;
	mechlist_t		*pcur;
	entry_t			*pent = NULL;
	int			count;
	int			fd = -1;
	int			rc;
	int			i;

	if (provname == NULL) {
		return (FAILURE);
	}

	if (getzoneid() == GLOBAL_ZONEID) {
		/* use kcf.conf for kernel software providers in global zone */
		if ((pent = getent_kef(provname, phardlist, psoftlist)) ==
		    NULL) {

			/* No kcf.conf entry for this provider */
			if (check_kernel_for_soft(provname, NULL, &in_kernel)
			    == FAILURE) {
				return (FAILURE);
			} else if (in_kernel == B_FALSE) {
				cryptoerror(LOG_STDERR,
				    gettext("%s does not exist."), provname);
				return (FAILURE);
			}

			/*
			 * Set mech count to 1.  It will be reset to the
			 * correct value later if the setup buffer is too small.
			 */
			count = 1;
		} else {
			count = pent->sup_count;
			free_entry(pent);
		}
	} else {
		/*
		 * kcf.conf not there in non-global zone: set mech count to 1.
		 * It will be reset to the correct value later if the setup
		 * buffer is too small.
		 */
		count = 1;
	}

	if ((psoft_info = setup_get_soft_info(provname, count)) == NULL) {
		return (FAILURE);
	}

	if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
		cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
		    ADMIN_IOCTL_DEVICE, strerror(errno));
		free(psoft_info);
		return (FAILURE);
	}

	/* make GET_SOFT_INFO ioctl call */
	if ((rc = ioctl(fd, CRYPTO_GET_SOFT_INFO, psoft_info)) == -1) {
		cryptodebug("CRYPTO_GET_SOFT_INFO ioctl failed: %s",
		    strerror(errno));
		(void) close(fd);
		free(psoft_info);
		return (FAILURE);
	}

	/* BUFFER is too small, get the number of mechanisms and retry it. */
	if (psoft_info->si_return_value == CRYPTO_BUFFER_TOO_SMALL) {
		count = psoft_info->si_count;
		free(psoft_info);
		if ((psoft_info = setup_get_soft_info(provname, count))
		    == NULL) {
			(void) close(fd);
			return (FAILURE);
		} else {
			rc = ioctl(fd, CRYPTO_GET_SOFT_INFO, psoft_info);
			if (rc == -1) {
				cryptodebug("CRYPTO_GET_SOFT_INFO ioctl "
				    "failed: %s", strerror(errno));
				(void) close(fd);
				free(psoft_info);
				return (FAILURE);
			}
		}
	}

	(void) close(fd);
	if (psoft_info->si_return_value != CRYPTO_SUCCESS) {
		cryptodebug("CRYPTO_GET_SOFT_INFO ioctl failed, "
		    "return_value = %d", psoft_info->si_return_value);
		free(psoft_info);
		return (FAILURE);
	}


	/* Build the mechanism linked list and return it */
	rc = SUCCESS;
	phead = pcur = NULL;
	for (i = 0; i < psoft_info->si_count; i++) {
		pmech = create_mech(&psoft_info->si_list[i][0]);
		if (pmech == NULL) {
			rc = FAILURE;
			break;
		} else {
			if (phead == NULL) {
				phead = pcur = pmech;
			} else {
				pcur->next = pmech;
				pcur = pmech;
			}
		}
	}

	if (rc == FAILURE) {
		free_mechlist(phead);
	} else {
		*ppmechlist = phead;
	}

	free(psoft_info);
	return (rc);
}


/*
 * Get the kernel software provider list from kernel.
 */
int
get_soft_list(crypto_get_soft_list_t **ppsoftlist)
{
	crypto_get_soft_list_t *psoftlist = NULL;
	int	count = DEFAULT_SOFT_NUM;
	int	len;
	int	fd = -1;

	if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
		cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
		    ADMIN_IOCTL_DEVICE, strerror(errno));
		return (FAILURE);
	}

	len = MAXNAMELEN * count;
	psoftlist = malloc(sizeof (crypto_get_soft_list_t) + len);
	if (psoftlist == NULL) {
		cryptodebug("out of memory.");
		(void) close(fd);
		return (FAILURE);
	}
	psoftlist->sl_soft_names = (caddr_t)(psoftlist + 1);
	psoftlist->sl_soft_count = count;
	psoftlist->sl_soft_len = len;

	if (ioctl(fd, CRYPTO_GET_SOFT_LIST, psoftlist) == -1) {
		cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed: %s",
		    strerror(errno));
		free(psoftlist);
		(void) close(fd);
		return (FAILURE);
	}

	/*
	 * if BUFFER is too small, get the number of software providers and
	 * the minimum length needed for names and length and retry it.
	 */
	if (psoftlist->sl_return_value == CRYPTO_BUFFER_TOO_SMALL) {
		count = psoftlist->sl_soft_count;
		len = psoftlist->sl_soft_len;
		free(psoftlist);
		psoftlist = malloc(sizeof (crypto_get_soft_list_t) + len);
		if (psoftlist == NULL) {
			cryptodebug("out of memory.");
			(void) close(fd);
			return (FAILURE);
		}
		psoftlist->sl_soft_names = (caddr_t)(psoftlist + 1);
		psoftlist->sl_soft_count = count;
		psoftlist->sl_soft_len = len;

		if (ioctl(fd, CRYPTO_GET_SOFT_LIST, psoftlist) == -1) {
			cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed:"
			    "%s", strerror(errno));
			free(psoftlist);
			(void) close(fd);
			return (FAILURE);
		}
	}

	if (psoftlist->sl_return_value != CRYPTO_SUCCESS) {
		cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed, "
		    "return_value = %d", psoftlist->sl_return_value);
		free(psoftlist);
		(void) close(fd);
		return (FAILURE);
	}

	*ppsoftlist = psoftlist;
	(void) close(fd);
	return (SUCCESS);
}

/*
 * Perform the FIPS related actions
 */
int
do_fips_actions(int action, int caller)
{

	crypto_fips140_t	fips_info;
	int	fd;
	int	rc = SUCCESS;
	int	pkcs11_fips_mode = 0;

	/* Get FIPS-140 status from pkcs11.conf */
	fips_status_pkcs11conf(&pkcs11_fips_mode);

	if (action == FIPS140_STATUS) {
		if (pkcs11_fips_mode == CRYPTO_FIPS_MODE_ENABLED)
			(void) printf(gettext(
			    "\tFIPS-140 mode is enabled.\n"));
		else
			(void) printf(gettext(
			    "\tFIPS-140 mode is disabled.\n"));
		return (SUCCESS);
	}

	if (caller == NOT_REFRESH) {
		/* Is it a duplicate operation? */
		if ((action == FIPS140_ENABLE) &&
		    (pkcs11_fips_mode == CRYPTO_FIPS_MODE_ENABLED)) {
			cryptoerror(LOG_STDERR,
			    gettext("FIPS-140 mode has already "
			    "been enabled.\n"));
			return (FAILURE);
		}

		if ((action == FIPS140_DISABLE) &&
		    (pkcs11_fips_mode == CRYPTO_FIPS_MODE_DISABLED)) {
			cryptoerror(LOG_STDERR,
			    gettext("FIPS-140 mode has already "
			    "been disabled.\n"));
			return (FAILURE);
		}

		if ((action == FIPS140_ENABLE) || (action == FIPS140_DISABLE)) {
			/* Update pkcs11.conf */
			if ((rc = fips_update_pkcs11conf(action)) != SUCCESS)
				return (rc);
		}

		/* No need to inform kernel */
		if (action == FIPS140_ENABLE) {
			(void) printf(gettext(
			    "FIPS-140 mode was enabled successfully.\n"));
		} else {
			(void) printf(gettext(
			    "FIPS-140 mode was disabled successfully.\n"));
		}

		(void) printf(gettext(
		    "The FIPS-140 mode has changed.\n"));
		(void) printf(gettext(
		    "The system will require a reboot.\n\n"));
		return (SUCCESS);

	}

	/* This is refresh, need to inform kernel */
	(void) memset(&fips_info, 0, sizeof (crypto_fips140_t));

	if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
		cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
		    ADMIN_IOCTL_DEVICE, strerror(errno));
		return (FAILURE);
	}

	switch (action) {
	case FIPS140_ENABLE:
		/* make CRYPTO_FIPS_SET ioctl call */
		fips_info.fips140_op = FIPS140_ENABLE;
		if ((rc = ioctl(fd, CRYPTO_FIPS140_SET, &fips_info)) == -1) {
			cryptodebug("CRYPTO_FIPS140_ENABLE ioctl failed: %s",
			    strerror(errno));
			rc = FAILURE;
			goto out;
		}

		if (fips_info.fips140_return_value != CRYPTO_SUCCESS) {
			cryptodebug("CRYPTO_FIPS140_ENABLE ioctl failed, "
			    "return_value = %d",
			    fips_info.fips140_return_value);
			rc = FAILURE;
		}

		break;

	case FIPS140_DISABLE:
		/* make CRYPTO_FIPS140_SET ioctl call */
		fips_info.fips140_op = FIPS140_DISABLE;
		if ((rc = ioctl(fd, CRYPTO_FIPS140_SET, &fips_info)) == -1) {
			cryptodebug("CRYPTO_FIPS140_DISABLE ioctl failed: %s",
			    strerror(errno));
			rc = FAILURE;
			goto out;
		}

		if (fips_info.fips140_return_value != CRYPTO_SUCCESS) {
			cryptodebug("CRYPTO_FIPS140_DISABLE ioctl failed, "
			    "return_value = %d",
			    fips_info.fips140_return_value);
			rc = FAILURE;
		}

		break;

	default:
		rc = FAILURE;
		break;
	};

out:
	(void) close(fd);
	return (rc);
}