view usr/src/uts/common/crypto/io/cryptoadm.c @ 10732:498ac26a63d5

PSARC/2009/447 Kernel Cryptographic Framework support for FIPS 140-2 6703950 Solaris cryptographic framework needs to implement changes for FIPS-140-2 compliance
author Anthony Scarpino <Anthony.Scarpino@Sun.COM>
date Wed, 07 Oct 2009 14:16:17 -0700
parents a10fbcfc2f21
children a63258283f8f
line wrap: on
line source

/*
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*
 * The ioctl interface for administrative commands.
 */

#include <sys/types.h>
#include <sys/modctl.h>
#include <sys/conf.h>
#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/kmem.h>
#include <sys/errno.h>
#include <sys/ksynch.h>
#include <sys/file.h>
#include <sys/open.h>
#include <sys/cred.h>
#include <sys/model.h>
#include <sys/sysmacros.h>
#include <sys/crypto/common.h>
#include <sys/crypto/api.h>
#include <sys/crypto/impl.h>
#include <sys/crypto/sched_impl.h>
#include <sys/crypto/ioctladmin.h>
#include <c2/audit.h>
#include <sys/disp.h>

/*
 * DDI entry points.
 */
static int cryptoadm_attach(dev_info_t *, ddi_attach_cmd_t);
static int cryptoadm_detach(dev_info_t *, ddi_detach_cmd_t);
static int cryptoadm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int cryptoadm_open(dev_t *, int, int, cred_t *);
static int cryptoadm_close(dev_t, int, int, cred_t *);
static int cryptoadm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);

extern void audit_cryptoadm(int, char *, crypto_mech_name_t *, uint_t,
    uint_t, uint32_t, int);

/*
 * Module linkage.
 */
static struct cb_ops cbops = {
	cryptoadm_open,		/* cb_open */
	cryptoadm_close,	/* cb_close */
	nodev,			/* cb_strategy */
	nodev,			/* cb_print */
	nodev,			/* cb_dump */
	nodev,			/* cb_read */
	nodev,			/* cb_write */
	cryptoadm_ioctl,	/* cb_ioctl */
	nodev,			/* cb_devmap */
	nodev,			/* cb_mmap */
	nodev,			/* cb_segmap */
	nochpoll,		/* cb_chpoll */
	ddi_prop_op,		/* cb_prop_op */
	NULL,			/* cb_streamtab */
	D_MP,			/* cb_flag */
	CB_REV,			/* cb_rev */
	nodev,			/* cb_aread */
	nodev,			/* cb_awrite */
};

static struct dev_ops devops = {
	DEVO_REV,		/* devo_rev */
	0,			/* devo_refcnt */
	cryptoadm_getinfo,	/* devo_getinfo */
	nulldev,		/* devo_identify */
	nulldev,		/* devo_probe */
	cryptoadm_attach,	/* devo_attach */
	cryptoadm_detach,	/* devo_detach */
	nodev,			/* devo_reset */
	&cbops,			/* devo_cb_ops */
	NULL,			/* devo_bus_ops */
	NULL,			/* devo_power */
	ddi_quiesce_not_needed,		/* devo_quiesce */
};

static struct modldrv modldrv = {
	&mod_driverops,					/* drv_modops */
	"Cryptographic Administrative Interface",	/* drv_linkinfo */
	&devops,
};

static struct modlinkage modlinkage = {
	MODREV_1,		/* ml_rev */
	&modldrv,		/* ml_linkage */
	NULL
};

static dev_info_t	*cryptoadm_dip = NULL;

/*
 * DDI entry points.
 */
int
_init(void)
{
	return (mod_install(&modlinkage));
}

int
_fini(void)
{
	return (mod_remove(&modlinkage));
}

int
_info(struct modinfo *modinfop)
{
	return (mod_info(&modlinkage, modinfop));
}

/* ARGSUSED */
static int
cryptoadm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
{
	switch (cmd) {
	case DDI_INFO_DEVT2DEVINFO:
		*result = (void *)cryptoadm_dip;
		return (DDI_SUCCESS);

	case DDI_INFO_DEVT2INSTANCE:
		*result = (void *)0;
		return (DDI_SUCCESS);
	}
	return (DDI_FAILURE);
}

static int
cryptoadm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
	if (cmd != DDI_ATTACH) {
		return (DDI_FAILURE);
	}
	if (ddi_get_instance(dip) != 0) {
		/* we only allow instance 0 to attach */
		return (DDI_FAILURE);
	}

	/* create the minor node */
	if (ddi_create_minor_node(dip, "cryptoadm", S_IFCHR, 0,
	    DDI_PSEUDO, 0) != DDI_SUCCESS) {
		cmn_err(CE_WARN, "cryptoadm: failed creating minor node");
		ddi_remove_minor_node(dip, NULL);
		return (DDI_FAILURE);
	}

	mutex_init(&fips140_mode_lock, NULL, MUTEX_DEFAULT, NULL);
	cryptoadm_dip = dip;

	return (DDI_SUCCESS);
}

static int
cryptoadm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
	if (cmd != DDI_DETACH)
		return (DDI_FAILURE);

	cryptoadm_dip = NULL;
	ddi_remove_minor_node(dip, NULL);

	return (DDI_SUCCESS);
}

/* ARGSUSED */
static int
cryptoadm_open(dev_t *devp, int flag, int otyp, cred_t *credp)
{
	if (otyp != OTYP_CHR || cryptoadm_dip == NULL)
		return (ENXIO);

	/* exclusive opens are not supported */
	if (flag & FEXCL)
		return (ENOTSUP);

	*devp = makedevice(getmajor(*devp), 0);

	kcf_sched_start();

	return (0);
}

/* ARGSUSED */
static int
cryptoadm_close(dev_t dev, int flag, int otyp, cred_t *credp)
{
	return (0);
}

/*
 * Returns TRUE if array of size MAXNAMELEN contains a '\0'
 * termination character, otherwise, it returns FALSE.
 */
static boolean_t
null_terminated(char *array)
{
	int i;

	for (i = 0; i < MAXNAMELEN; i++)
		if (array[i] == '\0')
			return (B_TRUE);

	return (B_FALSE);
}

/*
 * This ioctl returns an array of hardware providers.  Each entry
 * contains a device name, device instance, and number of
 * supported mechanisms.
 */
/* ARGSUSED */
static int
get_dev_list(dev_t dev, caddr_t arg, int mode, int *rval)
{
	crypto_get_dev_list_t dev_list;
	crypto_dev_list_entry_t *entries;
	size_t copyout_size;
	uint_t count;
	ulong_t offset;

	if (copyin(arg, &dev_list, sizeof (dev_list)) != 0)
		return (EFAULT);

	/* get the list from the core module */
	if (crypto_get_dev_list(&count, &entries) != 0) {
		dev_list.dl_return_value = CRYPTO_FAILED;
		if (copyout(&dev_list, arg, sizeof (dev_list)) != 0) {
			return (EFAULT);
		}
		return (0);
	}

	/* check if buffer is too small */
	if (count > dev_list.dl_dev_count) {
		dev_list.dl_dev_count = count;
		dev_list.dl_return_value = CRYPTO_BUFFER_TOO_SMALL;
		crypto_free_dev_list(entries, count);
		if (copyout(&dev_list, arg, sizeof (dev_list)) != 0) {
			return (EFAULT);
		}
		return (0);
	}

	dev_list.dl_dev_count = count;
	dev_list.dl_return_value = CRYPTO_SUCCESS;

	copyout_size = count * sizeof (crypto_dev_list_entry_t);

	/* copyout the first stuff */
	if (copyout(&dev_list, arg, sizeof (dev_list)) != 0) {
		crypto_free_dev_list(entries, count);
		return (EFAULT);
	}

	/* copyout entries */
	offset = offsetof(crypto_get_dev_list_t, dl_devs);
	if (count > 0 && copyout(entries, arg + offset, copyout_size) != 0) {
		crypto_free_dev_list(entries, count);
		return (EFAULT);
	}
	crypto_free_dev_list(entries, count);
	return (0);
}

/*
 * This ioctl returns a buffer containing the null terminated names
 * of software providers.
 */
/* ARGSUSED */
static int
get_soft_list(dev_t dev, caddr_t arg, int mode, int *rval)
{
	STRUCT_DECL(crypto_get_soft_list, soft_list);
	char *names;
	size_t len;
	uint_t count;

	STRUCT_INIT(soft_list, mode);

	if (copyin(arg, STRUCT_BUF(soft_list), STRUCT_SIZE(soft_list)) != 0)
		return (EFAULT);

	/* get the list from the core module */
	if (crypto_get_soft_list(&count, &names, &len) != 0) {
		STRUCT_FSET(soft_list, sl_return_value, CRYPTO_FAILED);
		if (copyout(STRUCT_BUF(soft_list), arg,
		    STRUCT_SIZE(soft_list)) != 0) {
			return (EFAULT);
		}
		return (0);
	}

	/* check if buffer is too small */
	if (len > STRUCT_FGET(soft_list, sl_soft_len)) {
		STRUCT_FSET(soft_list, sl_soft_count, count);
		STRUCT_FSET(soft_list, sl_soft_len, len);
		STRUCT_FSET(soft_list, sl_return_value,
		    CRYPTO_BUFFER_TOO_SMALL);
		kmem_free(names, len);
		if (copyout(STRUCT_BUF(soft_list), arg,
		    STRUCT_SIZE(soft_list)) != 0) {
			return (EFAULT);
		}
		return (0);
	}

	STRUCT_FSET(soft_list, sl_soft_count, count);
	STRUCT_FSET(soft_list, sl_soft_len, len);
	STRUCT_FSET(soft_list, sl_return_value, CRYPTO_SUCCESS);

	if (count > 0 && copyout(names,
	    STRUCT_FGETP(soft_list, sl_soft_names), len) != 0) {
		kmem_free(names, len);
		return (EFAULT);
	}
	kmem_free(names, len);

	if (copyout(STRUCT_BUF(soft_list), arg, STRUCT_SIZE(soft_list)) != 0) {
		return (EFAULT);
	}

	return (0);
}

/*
 * This ioctl returns an array of mechanisms supported by the
 * specified device.
 */
/* ARGSUSED */
static int
get_dev_info(dev_t dev, caddr_t arg, int mode, int *rval)
{
	crypto_get_dev_info_t dev_info;
	crypto_mech_name_t *entries;
	size_t copyout_size;
	uint_t count;
	ulong_t offset;
	char *dev_name;
	int rv;

	if (copyin(arg, &dev_info, sizeof (dev_info)) != 0)
		return (EFAULT);

	dev_name = dev_info.di_dev_name;
	/* make sure the device name is null terminated */
	if (!null_terminated(dev_name)) {
		dev_info.di_return_value = CRYPTO_ARGUMENTS_BAD;
		if (copyout(&dev_info, arg, sizeof (dev_info)) != 0) {
			return (EFAULT);
		}
		return (0);
	}

	/* get mechanism names from the core module */
	if ((rv = crypto_get_dev_info(dev_name, dev_info.di_dev_instance,
	    &count, &entries)) != CRYPTO_SUCCESS) {
		dev_info.di_return_value = rv;
		if (copyout(&dev_info, arg, sizeof (dev_info)) != 0) {
			return (EFAULT);
		}
		return (0);
	}

	/* check if buffer is too small */
	if (count > dev_info.di_count) {
		dev_info.di_count = count;
		dev_info.di_return_value = CRYPTO_BUFFER_TOO_SMALL;
		crypto_free_mech_list(entries, count);
		if (copyout(&dev_info, arg, sizeof (dev_info)) != 0) {
			return (EFAULT);
		}
		return (0);
	}

	dev_info.di_count = count;
	dev_info.di_return_value = CRYPTO_SUCCESS;

	copyout_size = count * sizeof (crypto_mech_name_t);

	/* copyout the first stuff */
	if (copyout(&dev_info, arg, sizeof (dev_info)) != 0) {
		crypto_free_mech_list(entries, count);
		return (EFAULT);
	}

	/* copyout entries */
	offset = offsetof(crypto_get_dev_info_t, di_list);
	if (copyout(entries, arg + offset, copyout_size) != 0) {
		crypto_free_mech_list(entries, count);
		return (EFAULT);
	}
	crypto_free_mech_list(entries, count);
	return (0);
}

/*
 * This ioctl returns an array of mechanisms supported by the
 * specified cryptographic module.
 */
/* ARGSUSED */
static int
get_soft_info(dev_t dev, caddr_t arg, int mode, int *rval)
{
	crypto_get_soft_info_t soft_info;
	crypto_mech_name_t *entries;
	size_t copyout_size;
	uint_t count;
	ulong_t offset;
	char *name;

	if (copyin(arg, &soft_info, sizeof (soft_info)) != 0)
		return (EFAULT);

	name = soft_info.si_name;
	/* make sure the provider name is null terminated */
	if (!null_terminated(name)) {
		soft_info.si_return_value = CRYPTO_ARGUMENTS_BAD;
		if (copyout(&soft_info, arg, sizeof (soft_info)) != 0) {
			return (EFAULT);
		}
		return (0);
	}

	/* get mechanism names from the core module */
	if (crypto_get_soft_info(name, &count, &entries) != 0) {
		soft_info.si_return_value = CRYPTO_FAILED;
		if (copyout(&soft_info, arg, sizeof (soft_info)) != 0) {
			return (EFAULT);
		}
		return (0);
	}

	/* check if buffer is too small */
	if (count > soft_info.si_count) {
		soft_info.si_count = count;
		soft_info.si_return_value = CRYPTO_BUFFER_TOO_SMALL;
		crypto_free_mech_list(entries, count);
		if (copyout(&soft_info, arg, sizeof (soft_info)) != 0) {
			return (EFAULT);
		}
		return (0);
	}

	soft_info.si_count = count;
	soft_info.si_return_value = CRYPTO_SUCCESS;
	copyout_size = count * sizeof (crypto_mech_name_t);

	/* copyout the first stuff */
	if (copyout(&soft_info, arg, sizeof (soft_info)) != 0) {
		crypto_free_mech_list(entries, count);
		return (EFAULT);
	}

	/* copyout entries */
	offset = offsetof(crypto_get_soft_info_t, si_list);
	if (copyout(entries, arg + offset, copyout_size) != 0) {
		crypto_free_mech_list(entries, count);
		return (EFAULT);
	}
	crypto_free_mech_list(entries, count);
	return (0);
}

/*
 * This ioctl disables mechanisms supported by the specified device.
 */
/* ARGSUSED */
static int
load_dev_disabled(dev_t dev, caddr_t arg, int mode, int *rval)
{
	crypto_load_dev_disabled_t dev_disabled;
	crypto_mech_name_t *entries;
	size_t size;
	ulong_t offset;
	uint_t count;
	uint_t instance;
	char *dev_name;
	uint32_t rv;
	int error = 0;

	if (copyin(arg, &dev_disabled, sizeof (dev_disabled)) != 0) {
		error =  EFAULT;
		goto out2;
	}

	dev_name = dev_disabled.dd_dev_name;
	/* make sure the device name is null terminated */
	if (!null_terminated(dev_name)) {
		rv = CRYPTO_ARGUMENTS_BAD;
		goto out;
	}

	count = dev_disabled.dd_count;
	instance = dev_disabled.dd_dev_instance;
	if (count == 0) {
		/* remove the entry */
		if (crypto_load_dev_disabled(dev_name, instance, 0, NULL) != 0)
			rv = CRYPTO_FAILED;
		else
			rv = CRYPTO_SUCCESS;
		goto out;
	}

	if (count > KCF_MAXMECHS) {
		rv = CRYPTO_ARGUMENTS_BAD;
		goto out;
	}

	size = count * sizeof (crypto_mech_name_t);
	entries = kmem_alloc(size, KM_SLEEP);

	offset = offsetof(crypto_load_dev_disabled_t, dd_list);
	if (copyin(arg + offset, entries, size) != 0) {
		kmem_free(entries, size);
		error = EFAULT;
		goto out2;
	}

	/* 'entries' consumed (but not freed) by crypto_load_dev_disabled() */
	if (crypto_load_dev_disabled(dev_name, instance, count, entries) != 0) {
		kmem_free(entries, size);
		rv = CRYPTO_FAILED;
		goto out;
	}
	rv = CRYPTO_SUCCESS;
out:
	dev_disabled.dd_return_value = rv;

	if (copyout(&dev_disabled, arg, sizeof (dev_disabled)) != 0) {
		error = EFAULT;
	}
out2:
	if (audit_active)
		audit_cryptoadm(CRYPTO_LOAD_DEV_DISABLED, dev_name, entries,
		    count, instance, rv, error);
	return (error);
}

/*
 * This ioctl disables mechanisms supported by the specified
 * cryptographic module.
 */
/* ARGSUSED */
static int
load_soft_disabled(dev_t dev, caddr_t arg, int mode, int *rval)
{
	crypto_load_soft_disabled_t soft_disabled;
	crypto_mech_name_t *entries;
	size_t size;
	uint_t count;
	ulong_t offset;
	char *name;
	uint32_t rv;
	int error = 0;

	if (copyin(arg, &soft_disabled, sizeof (soft_disabled)) != 0) {
		error = EFAULT;
		goto out2;
	}

	name = soft_disabled.sd_name;
	/* make sure the name is null terminated */
	if (!null_terminated(name)) {
		soft_disabled.sd_return_value = CRYPTO_ARGUMENTS_BAD;
		if (copyout(&soft_disabled, arg, sizeof (soft_disabled)) != 0) {
			return (EFAULT);
		}
		return (0);
	}

	count = soft_disabled.sd_count;
	if (count == 0) {
		/* remove the entry */
		if (crypto_load_soft_disabled(name, 0, NULL) != 0) {
			rv = CRYPTO_FAILED;
		} else {
			rv = CRYPTO_SUCCESS;
		}
		goto out;
	}

	if (count > KCF_MAXMECHS) {
		rv = CRYPTO_ARGUMENTS_BAD;
		goto out;
	}

	size = count * sizeof (crypto_mech_name_t);
	entries = kmem_alloc(size, KM_SLEEP);

	offset = offsetof(crypto_load_soft_disabled_t, sd_list);
	if (copyin(arg + offset, entries, size) != 0) {
		kmem_free(entries, size);
		error = EFAULT;
		goto out2;
	}

	/* 'entries' is consumed by crypto_load_soft_disabled() */
	if (crypto_load_soft_disabled(name, count, entries) != 0) {
		kmem_free(entries, size);
		rv = CRYPTO_FAILED;
		goto out;
	}
	rv = CRYPTO_SUCCESS;
out:
	soft_disabled.sd_return_value = rv;

	if (copyout(&soft_disabled, arg, sizeof (soft_disabled)) != 0) {
		error = EFAULT;
	}
out2:
	if (audit_active)
		audit_cryptoadm(CRYPTO_LOAD_SOFT_DISABLED, name, entries,
		    count, 0, rv, error);
	return (error);
}

/*
 * This ioctl loads the supported mechanisms of the specfied cryptographic
 * module.  This is so, at boot time, all software providers do not
 * have to be opened in order to cause them to register their
 * supported mechanisms.
 */
/* ARGSUSED */
static int
load_soft_config(dev_t dev, caddr_t arg, int mode, int *rval)
{
	crypto_load_soft_config_t soft_config;
	crypto_mech_name_t *entries;
	size_t size;
	uint_t count;
	ulong_t offset;
	char *name;
	uint32_t rv;
	int error = 0;

	if (copyin(arg, &soft_config, sizeof (soft_config)) != 0) {
		error = EFAULT;
		goto out2;
	}

	name = soft_config.sc_name;
	/* make sure the name is null terminated */
	if (!null_terminated(name)) {
		soft_config.sc_return_value = CRYPTO_ARGUMENTS_BAD;
		if (copyout(&soft_config, arg, sizeof (soft_config)) != 0) {
			return (EFAULT);
		}
		return (0);
	}

	count = soft_config.sc_count;
	if (count == 0) {
		if (crypto_load_soft_config(name, 0, NULL) != 0) {
			rv = CRYPTO_FAILED;
		} else {
			rv = CRYPTO_SUCCESS;
		}
		goto out;
	}

	if (count > KCF_MAXMECHS) {
		rv = CRYPTO_ARGUMENTS_BAD;
		goto out;
	}

	size = count * sizeof (crypto_mech_name_t);
	entries = kmem_alloc(size, KM_SLEEP);

	offset = offsetof(crypto_load_soft_config_t, sc_list);
	if (copyin(arg + offset, entries, size) != 0) {
		kmem_free(entries, size);
		error = EFAULT;
		goto out2;
	}

	/*
	 * 'entries' is consumed (but not freed) by
	 * crypto_load_soft_config()
	 */
	if (crypto_load_soft_config(name, count, entries) != 0) {
		kmem_free(entries, size);
		rv = CRYPTO_FAILED;
		goto out;
	}
	rv = CRYPTO_SUCCESS;
out:
	soft_config.sc_return_value = rv;

	if (copyout(&soft_config, arg, sizeof (soft_config)) != 0) {
		error = EFAULT;
	}
out2:
	if (audit_active)
		audit_cryptoadm(CRYPTO_LOAD_SOFT_CONFIG, name, entries, count,
		    0, rv, error);
	return (error);
}

/*
 * This ioctl unloads the specfied cryptographic module and removes
 * its table of supported mechanisms.
 */
/* ARGSUSED */
static int
unload_soft_module(dev_t dev, caddr_t arg, int mode, int *rval)
{
	crypto_unload_soft_module_t unload_soft_module;
	char *name;
	uint32_t rv;
	int error = 0;

	if (copyin(arg, &unload_soft_module,
	    sizeof (unload_soft_module)) != 0) {
		error = EFAULT;
		goto out2;
	}

	name = unload_soft_module.sm_name;
	/* make sure the name is null terminated */
	if (!null_terminated(name)) {
		unload_soft_module.sm_return_value = CRYPTO_ARGUMENTS_BAD;
		if (copyout(&unload_soft_module, arg,
		    sizeof (unload_soft_module)) != 0) {
			return (EFAULT);
		}
		return (0);
	}

	rv = crypto_unload_soft_module(name);
out:
	unload_soft_module.sm_return_value = rv;

	if (copyout(&unload_soft_module, arg,
	    sizeof (unload_soft_module)) != 0) {
		error = EFAULT;
	}
out2:
	if (audit_active)
		audit_cryptoadm(CRYPTO_UNLOAD_SOFT_MODULE, name, NULL, 0, 0,
		    rv, error);

	return (error);
}

/*
 * This ioctl loads a door descriptor into the  kernel.  The descriptor
 * is used for module verification.
 */
/* ARGSUSED */
static int
load_door(dev_t dev, caddr_t arg, int mode, int *rval)
{
	crypto_load_door_t load_door;
	uint32_t rv;
	int error = 0;

	if (copyin(arg, &load_door, sizeof (crypto_load_door_t)) != 0) {
		error = EFAULT;
		goto out2;
	}

	if (crypto_load_door(load_door.ld_did) != 0) {
		rv = CRYPTO_FAILED;
		goto out;
	}
	rv = CRYPTO_SUCCESS;
out:
	load_door.ld_return_value = rv;

	if (copyout(&load_door, arg, sizeof (crypto_load_door_t)) != 0)
		error = EFAULT;

out2:
	if (audit_active)
		audit_cryptoadm(CRYPTO_LOAD_DOOR, NULL, NULL,
		    0, 0, rv, error);
	return (error);
}

/*
 * This function enables/disables FIPS140 mode or gets the current
 * FIPS 140 mode status.
 *
 * CRYPTO_FIPS140_STATUS: Returns back the value of global_fips140_mode.
 * CRYPTO_FIPS140_SET: Recognizes 2 operations from userland:
 *                     FIPS140_ENABLE or FIPS140_DISABLE. These can only be
 *                     called when global_fips140_mode is FIPS140_MODE_UNSET
 *                     as they are only operations that can be performed at
 *                     bootup.
 */
/* ARGSUSED */
static int
fips140_actions(dev_t dev, caddr_t arg, int mode, int *rval, int cmd)
{
	crypto_fips140_t fips140_info;
	uint32_t rv = CRYPTO_SUCCESS;
	int error = 0;

	if (copyin(arg, &fips140_info, sizeof (crypto_fips140_t)) != 0)
		return (EFAULT);

	switch (cmd) {
	case CRYPTO_FIPS140_STATUS:
		fips140_info.fips140_status = global_fips140_mode;
		break;
	case CRYPTO_FIPS140_SET:
		/* If the mode has been determined, there is nothing to set */
		mutex_enter(&fips140_mode_lock);

		if (fips140_info.fips140_op == FIPS140_ENABLE &&
		    global_fips140_mode == FIPS140_MODE_UNSET) {
			/*
			 * If FIPS 140 is enabled, all approriate modules
			 * must be loaded and validated.  This can be done in
			 * the background as the rest of the OS comes up.
			 */
			global_fips140_mode = FIPS140_MODE_VALIDATING;
			(void) thread_create(NULL, 0, kcf_fips140_validate,
			    NULL, 0, &p0, TS_RUN, MAXCLSYSPRI);
			cv_signal(&cv_fips140);

		} else if (fips140_info.fips140_op == FIPS140_DISABLE &&
		    global_fips140_mode == FIPS140_MODE_UNSET) {
			/*
			 * If FIPS 140 is not enabled, any modules that are
			 * waiting for validation must be released so they
			 * can be verified.
			 */
			global_fips140_mode = FIPS140_MODE_DISABLED;
			kcf_activate();
			cv_signal(&cv_fips140);

		} else if (fips140_info.fips140_op != FIPS140_DISABLE &&
		    fips140_info.fips140_op != FIPS140_ENABLE) {
			rv = CRYPTO_ARGUMENTS_BAD;
		}

		mutex_exit(&fips140_mode_lock);
		break;

	default:
		rv = CRYPTO_ARGUMENTS_BAD;
	}

	fips140_info.fips140_return_value = rv;

	if (copyout(&fips140_info, arg, sizeof (crypto_fips140_t)) != 0)
		error = EFAULT;

	return (error);
}

static int
cryptoadm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *c,
    int *rval)
{
	int error;
#define	ARG	((caddr_t)arg)

	switch (cmd) {
	case CRYPTO_LOAD_DEV_DISABLED:
	case CRYPTO_LOAD_SOFT_DISABLED:
	case CRYPTO_LOAD_SOFT_CONFIG:
	case CRYPTO_UNLOAD_SOFT_MODULE:
	case CRYPTO_POOL_CREATE:
	case CRYPTO_POOL_WAIT:
	case CRYPTO_POOL_RUN:
	case CRYPTO_LOAD_DOOR:
	case CRYPTO_FIPS140_SET:
		if ((error = drv_priv(c)) != 0)
			return (error);
	default:
		break;
	}

	switch (cmd) {
	case CRYPTO_GET_DEV_LIST:
		return (get_dev_list(dev, ARG, mode, rval));

	case CRYPTO_GET_DEV_INFO:
		return (get_dev_info(dev, ARG, mode, rval));

	case CRYPTO_GET_SOFT_LIST:
		return (get_soft_list(dev, ARG, mode, rval));

	case CRYPTO_GET_SOFT_INFO:
		return (get_soft_info(dev, ARG, mode, rval));

	case CRYPTO_LOAD_DEV_DISABLED:
		return (load_dev_disabled(dev, ARG, mode, rval));

	case CRYPTO_LOAD_SOFT_DISABLED:
		return (load_soft_disabled(dev, ARG, mode, rval));

	case CRYPTO_LOAD_SOFT_CONFIG:
		return (load_soft_config(dev, ARG, mode, rval));

	case CRYPTO_UNLOAD_SOFT_MODULE:
		return (unload_soft_module(dev, ARG, mode, rval));

	case CRYPTO_POOL_CREATE:
		/*
		 * The framework allocates and initializes the pool.
		 * So, this is a no op. We are keeping this ioctl around
		 * to be used for any future threadpool related work.
		 */
		if (audit_active)
			audit_cryptoadm(CRYPTO_POOL_CREATE, NULL, NULL,
			    0, 0, 0, 0);
		return (0);

	case CRYPTO_POOL_WAIT: {
		int nthrs = 0, err;

		if ((err = kcf_svc_wait(&nthrs)) == 0) {
			if (copyout((caddr_t)&nthrs, ARG, sizeof (int))
			    == -1)
				err = EFAULT;
		}
		if (audit_active)
			audit_cryptoadm(CRYPTO_POOL_WAIT, NULL, NULL,
			    0, 0, 0, err);
		return (err);
	}

	case CRYPTO_POOL_RUN: {
		int err;

		err = kcf_svc_do_run();
		if (audit_active)
			audit_cryptoadm(CRYPTO_POOL_RUN, NULL, NULL,
			    0, 0, 0, err);
		return (err);
	}

	case CRYPTO_LOAD_DOOR:
		return (load_door(dev, ARG, mode, rval));
	case CRYPTO_FIPS140_STATUS:
		return (fips140_actions(dev, ARG, mode, rval, cmd));
	case CRYPTO_FIPS140_SET: {
		int err;

		err = fips140_actions(dev, ARG, mode, rval, cmd);
		if (audit_active)
			audit_cryptoadm(CRYPTO_FIPS140_SET, NULL, NULL,
			    0, 0, 0, err);
		return (err);
	}
	}

	return (EINVAL);
}