changeset 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 31fd4bc94a37
children b95259752377
files usr/src/cmd/cmd-crypto/cryptoadm/adm_kef.c usr/src/cmd/truss/codes.c usr/src/lib/libelfsign/common/elfcertlib.c usr/src/lib/libelfsign/common/elfsignlib.c usr/src/lib/libelfsign/common/libelfsign.h usr/src/lib/pkcs11/libpkcs11/common/pkcs11Conf.c usr/src/uts/common/c2/audit.c usr/src/uts/common/crypto/api/kcf_random.c usr/src/uts/common/crypto/core/kcf.c usr/src/uts/common/crypto/core/kcf_cryptoadm.c usr/src/uts/common/crypto/core/kcf_prov_tabs.c usr/src/uts/common/crypto/io/aes.c usr/src/uts/common/crypto/io/cryptoadm.c usr/src/uts/common/crypto/io/ecc.c usr/src/uts/common/crypto/io/rsa.c usr/src/uts/common/crypto/io/sha1_mod.c usr/src/uts/common/crypto/io/sha2_mod.c usr/src/uts/common/crypto/io/swrand.c usr/src/uts/common/crypto/spi/kcf_spi.c usr/src/uts/common/des/des_crypt.c usr/src/uts/common/sys/crypto/common.h usr/src/uts/common/sys/crypto/elfsign.h usr/src/uts/common/sys/crypto/impl.h usr/src/uts/common/sys/crypto/ioctladmin.h usr/src/uts/common/sys/crypto/sched_impl.h usr/src/uts/common/sys/crypto/spi.h usr/src/uts/common/sys/random.h
diffstat 27 files changed, 866 insertions(+), 208 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/cmd-crypto/cryptoadm/adm_kef.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/cmd/cmd-crypto/cryptoadm/adm_kef.c	Wed Oct 07 14:16:17 2009 -0700
@@ -1210,12 +1210,10 @@
 	 * handle fips_status=enabled|disabled
 	 */
 	ptr = pfipslist;
-	if (ptr != NULL) {
-		if (ptr->pent->flag_fips_enabled) {
-			rc = do_fips_actions(FIPS140_ENABLE, REFRESH);
-		} else {
-			rc = do_fips_actions(FIPS140_DISABLE, REFRESH);
-		}
+	if (ptr != NULL && ptr->pent->flag_fips_enabled) {
+		rc = do_fips_actions(FIPS140_ENABLE, REFRESH);
+	} else {
+		rc = do_fips_actions(FIPS140_DISABLE, REFRESH);
 	}
 
 	(void) close(fd);
--- a/usr/src/cmd/truss/codes.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/cmd/truss/codes.c	Wed Oct 07 14:16:17 2009 -0700
@@ -1046,6 +1046,8 @@
 	    "CRYPTO_NOSTORE_GENERATE_KEY_PAIR",	NULL },
 	{ (uint_t)CRYPTO_NOSTORE_DERIVE_KEY,
 	    "CRYPTO_NOSTORE_DERIVE_KEY",	NULL },
+	{ (uint_t)CRYPTO_FIPS140_STATUS,	"CRYPTO_FIPS140_STATUS", NULL },
+	{ (uint_t)CRYPTO_FIPS140_SET,	"CRYPTO_FIPS140_SET",	NULL },
 
 	/* kbio ioctls */
 	{ (uint_t)KIOCTRANS,		"KIOCTRANS",	NULL },
--- a/usr/src/lib/libelfsign/common/elfcertlib.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/lib/libelfsign/common/elfcertlib.c	Wed Oct 07 14:16:17 2009 -0700
@@ -52,11 +52,17 @@
 /*
  * The CACERT and OBJCACERT are the Cryptographic Trust Anchors
  * for the Solaris Cryptographic Framework.
+ *
+ * The SECACERT is the Signed Execution Trust Anchor that the
+ * Cryptographic Framework uses for FIPS-140 validation of non-crypto
+ * binaries
  */
 static const char _PATH_CRYPTO_CACERT[] = CRYPTO_CERTS_DIR "/CA";
 static const char _PATH_CRYPTO_OBJCACERT[] = CRYPTO_CERTS_DIR "/SUNWObjectCA";
+static const char _PATH_CRYPTO_SECACERT[] = ETC_CERTS_DIR "/SUNWSolarisCA";
 static ELFCert_t CACERT = NULL;
 static ELFCert_t OBJCACERT = NULL;
+static ELFCert_t SECACERT = NULL;
 static pthread_mutex_t ca_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static void elfcertlib_freecert(ELFsign_t, ELFCert_t);
@@ -95,10 +101,18 @@
 		(void) elfcertlib_getcert(ess, (char *)_PATH_CRYPTO_CACERT,
 		    NULL, &CACERT, ES_GET);
 	}
+
 	if (OBJCACERT == NULL) {
 		(void) elfcertlib_getcert(ess, (char *)_PATH_CRYPTO_OBJCACERT,
 		    NULL, &OBJCACERT, ES_GET);
 	}
+
+	if (SECACERT == NULL) {
+		(void) elfcertlib_getcert(ess,
+		    (char *)_PATH_CRYPTO_SECACERT, NULL, &SECACERT,
+		    ES_GET_FIPS140);
+	}
+
 	(void) pthread_mutex_unlock(&ca_mutex);
 
 	if (CACERT != NULL) {
@@ -139,6 +153,19 @@
 		}
 	}
 
+	if (SECACERT != NULL) {
+		rv = KMF_VerifyCertWithCert(ess->es_kmfhandle,
+		    (const KMF_DATA *)&cert->c_cert,
+		    (const KMF_DATA *)&SECACERT->c_cert.certificate);
+		if (rv == KMF_OK) {
+			if (ess->es_certCAcallback != NULL)
+				(ess->es_certvercallback)(ess->es_callbackctx,
+				    cert, SECACERT);
+			cert->c_verified = E_OK;
+			return (B_TRUE);
+		}
+	}
+
 	return (B_FALSE);
 }
 
@@ -266,7 +293,8 @@
 	 */
 	if (cert_pathname != NULL && (
 	    strcmp(cert_pathname, _PATH_CRYPTO_CACERT) == 0 ||
-	    strcmp(cert_pathname, _PATH_CRYPTO_OBJCACERT) == 0)) {
+	    strcmp(cert_pathname, _PATH_CRYPTO_OBJCACERT) == 0 ||
+	    strcmp(cert_pathname, _PATH_CRYPTO_SECACERT) == 0)) {
 		if (ess->es_certCAcallback != NULL)
 			(ess->es_certCAcallback)(ess->es_callbackctx, cert,
 			    cert_pathname);
--- a/usr/src/lib/libelfsign/common/elfsignlib.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/lib/libelfsign/common/elfsignlib.c	Wed Oct 07 14:16:17 2009 -0700
@@ -256,6 +256,7 @@
 	switch (action) {
 	case ES_GET:
 	case ES_GET_CRYPTO:
+	case ES_GET_FIPS140:
 		cryptodebug("elfsign_begin for get");
 		elfcmd = ELF_C_READ;
 		oflags = O_RDONLY | O_NOCTTY | O_NDELAY;
@@ -1155,6 +1156,7 @@
 		 *	force verification of crypto certs
 		 */
 		if ((ess->es_action == ES_GET_CRYPTO ||
+		    ess->es_action == ES_GET_FIPS140 ||
 		    strstr(fsx.fsx_signer_DN, ELFSIGN_CRYPTO)) &&
 		    !elfcertlib_verifycert(ess, cert)) {
 			cryptodebug("elfsign_verify_signature: invalid cert");
--- a/usr/src/lib/libelfsign/common/libelfsign.h	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/lib/libelfsign/common/libelfsign.h	Wed Oct 07 14:16:17 2009 -0700
@@ -20,15 +20,13 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef _LIBELFSIGN_H
 #define	_LIBELFSIGN_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -78,6 +76,7 @@
 enum ES_ACTION {
 	ES_GET,
 	ES_GET_CRYPTO,
+	ES_GET_FIPS140,
 	ES_UPDATE,
 	ES_UPDATE_RSA_MD5_SHA1,
 	ES_UPDATE_RSA_SHA1
--- a/usr/src/lib/pkcs11/libpkcs11/common/pkcs11Conf.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/lib/pkcs11/libpkcs11/common/pkcs11Conf.c	Wed Oct 07 14:16:17 2009 -0700
@@ -271,6 +271,150 @@
 	return (ret);
 }
 
+
+/* Generic function for all door calls to kcfd. */
+ELFsign_status_t
+kcfd_door_call(char *fullpath, boolean_t fips140, CK_RV *rv)
+{
+	boolean_t	try_door_open_again = B_FALSE;
+	int		 kcfdfd = -1;
+	door_arg_t	darg;
+	kcf_door_arg_t *kda = NULL;
+	kcf_door_arg_t *rkda = NULL;
+	int		r;
+	int		is_cryptosvc_up_count = 0;
+	int		door_errno = 0;
+	ELFsign_status_t estatus = ELFSIGN_UNKNOWN;
+
+open_door_file:
+	while ((kcfdfd = open(_PATH_KCFD_DOOR, O_RDONLY)) == -1) {
+		/* save errno and test for EINTR or EAGAIN */
+		door_errno = errno;
+		if (door_errno == EINTR ||
+		    door_errno == EAGAIN)
+			continue;
+		/* if disabled or maintenance mode - bail */
+		if (cryptosvc_is_down())
+			break;
+		/* exceeded our number of tries? */
+		if (is_cryptosvc_up_count > MAX_CRYPTOSVC_ONLINE_TRIES)
+			break;
+		/* any other state, try again up to 1/2 minute */
+		(void) sleep(5);
+		is_cryptosvc_up_count++;
+	}
+	if (kcfdfd == -1) {
+		if (!cryptosvc_is_online()) {
+			cryptoerror(LOG_ERR, "libpkcs11: unable to open"
+			    " kcfd door_file %s: %s.  %s is not online."
+			    " (see svcs -xv for details).",
+			    _PATH_KCFD_DOOR, strerror(door_errno),
+			    CRYPTOSVC_DEFAULT_INSTANCE_FMRI);
+		} else {
+			cryptoerror(LOG_ERR, "libpkcs11: unable to open"
+			    " kcfd door_file %s: %s.", _PATH_KCFD_DOOR,
+			    strerror(door_errno));
+		}
+		*rv = CKR_CRYPTOKI_NOT_INITIALIZED;
+		estatus = ELFSIGN_UNKNOWN;
+		goto verifycleanup;
+	}
+
+	/* Mark the door "close on exec" */
+	(void) fcntl(kcfdfd, F_SETFD, FD_CLOEXEC);
+
+	if ((kda = malloc(sizeof (kcf_door_arg_t))) == NULL) {
+		cryptoerror(LOG_ERR, "libpkcs11: malloc of kda "
+		    "failed: %s", strerror(errno));
+		goto verifycleanup;
+	}
+
+	if (fips140 == B_TRUE)
+		kda->da_version = KCFD_FIPS140_INTCHECK;
+	else {
+		kda->da_version = KCF_KCFD_VERSION1;
+		(void) strlcpy(kda->da_u.filename, fullpath,
+		    strlen(fullpath) + 1);
+	}
+
+	kda->da_iskernel = B_FALSE;
+
+	darg.data_ptr = (char *)kda;
+	darg.data_size = sizeof (kcf_door_arg_t);
+	darg.desc_ptr = NULL;
+	darg.desc_num = 0;
+	darg.rbuf = (char *)kda;
+	darg.rsize = sizeof (kcf_door_arg_t);
+
+	while ((r = door_call(kcfdfd, &darg)) != 0) {
+		/* save errno and test for certain errors */
+		door_errno = errno;
+		if (door_errno == EINTR || door_errno == EAGAIN)
+			continue;
+		/* if disabled or maintenance mode - bail */
+		if (cryptosvc_is_down())
+			break;
+		/* exceeded our number of tries? */
+		if (is_cryptosvc_up_count > MAX_CRYPTOSVC_ONLINE_TRIES)
+			break;
+			/* if stale door_handle, retry the open */
+		if (door_errno == EBADF) {
+			try_door_open_again = B_TRUE;
+			is_cryptosvc_up_count++;
+			(void) sleep(5);
+			goto verifycleanup;
+		} else
+			break;
+		}
+
+	if (r != 0) {
+		if (!cryptosvc_is_online()) {
+			cryptoerror(LOG_ERR, "%s is not online "
+			    " - unable to utilize cryptographic "
+			    "services.  (see svcs -xv for details).",
+			    CRYPTOSVC_DEFAULT_INSTANCE_FMRI);
+		} else {
+			cryptoerror(LOG_ERR, "libpkcs11: door_call "
+			    "of door_file %s failed with error %s.",
+			    _PATH_KCFD_DOOR, strerror(door_errno));
+		}
+		*rv = CKR_CRYPTOKI_NOT_INITIALIZED;
+		estatus = ELFSIGN_UNKNOWN;
+		goto verifycleanup;
+	}
+
+	/*LINTED*/
+	rkda = (kcf_door_arg_t *)darg.rbuf;
+	if ((fips140 == B_FALSE && rkda->da_version != KCF_KCFD_VERSION1) ||
+	    (fips140 == B_TRUE && rkda->da_version != KCFD_FIPS140_INTCHECK)) {
+		cryptoerror(LOG_ERR,
+		    "libpkcs11: kcfd and libelfsign versions "
+		    "don't match: got %d expected %d", rkda->da_version,
+		    (fips140) ? KCFD_FIPS140_INTCHECK : KCF_KCFD_VERSION1);
+		goto verifycleanup;
+	}
+	estatus = rkda->da_u.result.status;
+verifycleanup:
+	if (kcfdfd != -1) {
+		(void) close(kcfdfd);
+	}
+	if (rkda != NULL && rkda != kda)
+		(void) munmap((char *)rkda, darg.rsize);
+	if (kda != NULL) {
+		bzero(kda, sizeof (kda));
+		free(kda);
+		kda = NULL;
+		rkda = NULL;	/* rkda is an alias of kda */
+	}
+	if (try_door_open_again) {
+		try_door_open_again = B_FALSE;
+		goto open_door_file;
+	}
+
+	return (estatus);
+}
+
+
 /*
  * For each provider found in pkcs11.conf: expand $ISA if necessary,
  * verify the module is signed, load the provider, find all of its
@@ -313,14 +457,18 @@
 
 	ELFsign_status_t estatus = ELFSIGN_UNKNOWN;
 	char *estatus_str = NULL;
-	int kcfdfd = -1;
-	door_arg_t	darg;
-	kcf_door_arg_t *kda = NULL;
-	kcf_door_arg_t *rkda = NULL;
-	int r;
-	int		is_cryptosvc_up_count = 0;
-	int		door_errno = 0;
-	boolean_t	try_door_open_again = B_FALSE;
+	int fips140_mode = CRYPTO_FIPS_MODE_DISABLED;
+
+	/* Check FIPS 140 configuration and execute check if enabled */
+	(void) get_fips_mode(&fips140_mode);
+	if (fips140_mode) {
+		estatus = kcfd_door_call(NULL, B_TRUE, &rv);
+		if (estatus != ELFSIGN_SUCCESS) {
+			cryptoerror(LOG_ERR, "libpkcs11: failed FIPS 140 "
+			    "integrity check.");
+			return (CKR_GENERAL_ERROR);
+		}
+	}
 
 	phead = pplist;
 
@@ -538,123 +686,7 @@
 		 * kcfd libelfsign door protocol to use and fd instead
 		 * of a path - but that wouldn't work in the kernel case.
 		 */
-open_door_file:
-		while ((kcfdfd = open(_PATH_KCFD_DOOR, O_RDONLY)) == -1) {
-			/* save errno and test for EINTR or EAGAIN */
-			door_errno = errno;
-			if (door_errno == EINTR ||
-			    door_errno == EAGAIN)
-				continue;
-			/* if disabled or maintenance mode - bail */
-			if (cryptosvc_is_down())
-				break;
-			/* exceeded our number of tries? */
-			if (is_cryptosvc_up_count > MAX_CRYPTOSVC_ONLINE_TRIES)
-				break;
-			/* any other state, try again up to 1/2 minute */
-			(void) sleep(5);
-			is_cryptosvc_up_count++;
-		}
-		if (kcfdfd == -1) {
-			if (!cryptosvc_is_online()) {
-				cryptoerror(LOG_ERR, "libpkcs11: unable to open"
-				    " kcfd door_file %s: %s.  %s is not online."
-				    " (see svcs -xv for details).",
-				    _PATH_KCFD_DOOR, strerror(door_errno),
-				    CRYPTOSVC_DEFAULT_INSTANCE_FMRI);
-			} else {
-				cryptoerror(LOG_ERR, "libpkcs11: unable to open"
-				    " kcfd door_file %s: %s.", _PATH_KCFD_DOOR,
-				    strerror(door_errno));
-			}
-			rv = CKR_CRYPTOKI_NOT_INITIALIZED;
-			estatus = ELFSIGN_UNKNOWN;
-			goto verifycleanup;
-		}
-
-		/* Mark the door "close on exec" */
-		(void) fcntl(kcfdfd, F_SETFD, FD_CLOEXEC);
-
-		if ((kda = malloc(sizeof (kcf_door_arg_t))) == NULL) {
-			cryptoerror(LOG_ERR, "libpkcs11: malloc of kda "
-			    "failed: %s", strerror(errno));
-			goto verifycleanup;
-		}
-		kda->da_version = KCF_KCFD_VERSION1;
-		kda->da_iskernel = B_FALSE;
-		(void) strlcpy(kda->da_u.filename, fullpath,
-		    strlen(fullpath) + 1);
-
-		darg.data_ptr = (char *)kda;
-		darg.data_size = sizeof (kcf_door_arg_t);
-		darg.desc_ptr = NULL;
-		darg.desc_num = 0;
-		darg.rbuf = (char *)kda;
-		darg.rsize = sizeof (kcf_door_arg_t);
-
-		while ((r = door_call(kcfdfd, &darg)) != 0) {
-			/* save errno and test for certain errors */
-			door_errno = errno;
-			if (door_errno == EINTR || door_errno == EAGAIN)
-				continue;
-			/* if disabled or maintenance mode - bail */
-			if (cryptosvc_is_down())
-				break;
-			/* exceeded our number of tries? */
-			if (is_cryptosvc_up_count > MAX_CRYPTOSVC_ONLINE_TRIES)
-				break;
-			/* if stale door_handle, retry the open */
-			if (door_errno == EBADF) {
-				try_door_open_again = B_TRUE;
-				is_cryptosvc_up_count++;
-				(void) sleep(5);
-				goto verifycleanup;
-			} else
-				break;
-		}
-
-		if (r != 0) {
-			if (!cryptosvc_is_online()) {
-				cryptoerror(LOG_ERR, "%s is not online "
-				    " - unable to utilize cryptographic "
-				    "services.  (see svcs -xv for details).",
-				    CRYPTOSVC_DEFAULT_INSTANCE_FMRI);
-			} else {
-				cryptoerror(LOG_ERR, "libpkcs11: door_call "
-				    "of door_file %s failed with error %s.",
-				    _PATH_KCFD_DOOR, strerror(door_errno));
-			}
-			rv = CKR_CRYPTOKI_NOT_INITIALIZED;
-			estatus = ELFSIGN_UNKNOWN;
-			goto verifycleanup;
-		}
-
-		/*LINTED*/
-		rkda = (kcf_door_arg_t *)darg.rbuf;
-		if (rkda->da_version != KCF_KCFD_VERSION1) {
-			cryptoerror(LOG_ERR,
-			    "libpkcs11: kcfd and libelfsign versions "
-			    "don't match: got %d expected %d",
-			    rkda->da_version, KCF_KCFD_VERSION1);
-			goto verifycleanup;
-		}
-		estatus = rkda->da_u.result.status;
-verifycleanup:
-		if (kcfdfd != -1) {
-			(void) close(kcfdfd);
-		}
-		if (rkda != NULL && rkda != kda)
-			(void) munmap((char *)rkda, darg.rsize);
-		if (kda != NULL) {
-			bzero(kda, sizeof (kda));
-			free(kda);
-			kda = NULL;
-			rkda = NULL;	/* rkda is an alias of kda */
-		}
-		if (try_door_open_again) {
-			try_door_open_again = B_FALSE;
-			goto open_door_file;
-		}
+		estatus = kcfd_door_call(fullpath, B_FALSE, &rv);
 
 		switch (estatus) {
 		case ELFSIGN_UNKNOWN:
--- a/usr/src/uts/common/c2/audit.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/c2/audit.c	Wed Oct 07 14:16:17 2009 -0700
@@ -2180,6 +2180,11 @@
 			    "op=CRYPTO_LOAD_DOOR, return_val=%d", rv);
 		break;
 
+	case CRYPTO_FIPS140_SET:
+		(void) snprintf(buffer, sizeof (buffer),
+		    "op=CRYPTO_FIPS140_SET, fips_state=%d", rv);
+		break;
+
 	default:
 		return;
 	}
--- a/usr/src/uts/common/crypto/api/kcf_random.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/crypto/api/kcf_random.c	Wed Oct 07 14:16:17 2009 -0700
@@ -61,6 +61,7 @@
 #include <sys/crypto/api.h>
 #include <sys/crypto/impl.h>
 #include <sys/crypto/sched_impl.h>
+#include <sys/crypto/ioctladmin.h>
 #include <sys/random.h>
 #include <sys/sha1.h>
 #include <sys/time.h>
@@ -1049,3 +1050,49 @@
 		return (0);
 	return (kcf_rnd_get_bytes(ptr, len, B_TRUE));
 }
+
+/*
+ * The two functions below are identical to random_get_pseudo_bytes() and
+ * random_get_bytes_fips, this function is called for consumers that want
+ * FIPS 140-2.  This function waits until the FIPS boundary can be verified.
+ */
+
+/*
+ * Get bytes from the /dev/urandom generator. This function
+ * always succeeds. Returns 0.
+ */
+int
+random_get_pseudo_bytes_fips140(uint8_t *ptr, size_t len)
+{
+	ASSERT(!mutex_owned(&rndpool_lock));
+
+	mutex_enter(&fips140_mode_lock);
+	while (global_fips140_mode < FIPS140_MODE_ENABLED) {
+		cv_wait(&cv_fips140, &fips140_mode_lock);
+	}
+	mutex_exit(&fips140_mode_lock);
+
+	if (len < 1)
+		return (0);
+	return (kcf_rnd_get_pseudo_bytes(ptr, len));
+}
+
+/*
+ * Get bytes from the /dev/random generator. Returns 0
+ * on success. Returns EAGAIN if there is insufficient entropy.
+ */
+int
+random_get_bytes_fips140(uint8_t *ptr, size_t len)
+{
+	ASSERT(!mutex_owned(&rndpool_lock));
+
+	mutex_enter(&fips140_mode_lock);
+	while (global_fips140_mode < FIPS140_MODE_ENABLED) {
+		cv_wait(&cv_fips140, &fips140_mode_lock);
+	}
+	mutex_exit(&fips140_mode_lock);
+
+	if (len < 1)
+		return (0);
+	return (kcf_rnd_get_bytes(ptr, len, B_TRUE));
+}
--- a/usr/src/uts/common/crypto/core/kcf.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/crypto/core/kcf.c	Wed Oct 07 14:16:17 2009 -0700
@@ -45,6 +45,7 @@
 #include <sys/crypto/impl.h>
 #include <sys/crypto/sched_impl.h>
 #include <sys/crypto/elfsign.h>
+#include <sys/crypto/ioctladmin.h>
 
 #ifdef DEBUG
 int kcf_frmwrk_debug = 0;
@@ -61,7 +62,21 @@
 kmutex_t kcf_dh_lock;
 door_handle_t kcf_dh = NULL;
 
-uint32_t global_fips140_mode;	/* FIPS140 enable/disable flag */
+/* Setup FIPS 140 support variables */
+uint32_t global_fips140_mode = FIPS140_MODE_UNSET;
+kmutex_t fips140_mode_lock;
+kcondvar_t cv_fips140;
+
+/*
+ * Kernel FIPS140 boundary module list
+ * NOTE: "swrand" must be the last entry.  FIPS 140 shutdown functions stop
+ *       before getting to swrand as it is used for non-FIPS 140
+ *       operations to.  The FIPS 140 random API separately controls access.
+ */
+#define	FIPS140_MODULES_MAX 7
+static char *fips140_module_list[FIPS140_MODULES_MAX] = {
+	"aes", "des", "ecc", "sha1", "sha2", "rsa", "swrand"
+};
 
 static struct modlmisc modlmisc = {
 	&mod_miscops, "Kernel Crypto Framework"
@@ -73,10 +88,12 @@
 
 static int rngtimer_started;
 
-
 int
 _init()
 {
+	mutex_init(&fips140_mode_lock, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&cv_fips140, NULL, CV_DEFAULT, NULL);
+
 	/* initialize the mechanisms tables supported out-of-the-box */
 	kcf_init_mech_tabs();
 
@@ -117,11 +134,8 @@
 	return (EBUSY);
 }
 
-/*
- * Return the FIPS140 mode status:
- * FIPS140_MODE_DISABLED (0)
- * FIPS140_MODE_ENABLED (1)
- */
+
+/* Returns the value of global_fips140_mode */
 int
 kcf_get_fips140_mode(void)
 {
@@ -129,6 +143,260 @@
 }
 
 /*
+ * If FIPS 140 has failed its tests.  The providers must be disabled from the
+ * framework.
+ */
+void
+kcf_fips140_shutdown()
+{
+	kcf_provider_desc_t *pd;
+	int i;
+
+	cmn_err(CE_WARN,
+	    "Shutting down FIPS 140 boundary as verification failed.");
+
+	/* Disable FIPS 140 modules, but leave swrand alone */
+	for (i = 0; i < (FIPS140_MODULES_MAX - 1); i++) {
+		/*
+		 * Remove the predefined entries from the soft_config_list
+		 * so the framework does not report the providers.
+		 */
+		remove_soft_config(fips140_module_list[i]);
+
+		pd = kcf_prov_tab_lookup_by_name(fips140_module_list[i]);
+		if (pd == NULL)
+			continue;
+
+		/* Allow the unneeded providers to be unloaded */
+		pd->pd_mctlp->mod_loadflags &= ~(MOD_NOAUTOUNLOAD);
+
+		/* Invalidate the FIPS 140 providers */
+		mutex_enter(&pd->pd_lock);
+		pd->pd_state = KCF_PROV_VERIFICATION_FAILED;
+		mutex_exit(&pd->pd_lock);
+		KCF_PROV_REFRELE(pd);
+		undo_register_provider(pd, B_FALSE);
+
+	}
+}
+
+/*
+ * Activates the kernel providers
+ *
+ * If we are getting ready to enable FIPS 140 mode, then all providers should
+ * be loaded and ready.
+ *
+ * If FIPS 140 is disabled, then we can skip any errors because some crypto
+ * modules may not have been loaded.
+ */
+void
+kcf_activate()
+{
+	kcf_provider_desc_t *pd;
+	int i;
+
+	for (i = 0; i < (FIPS140_MODULES_MAX - 1); i++) {
+		pd = kcf_prov_tab_lookup_by_name(fips140_module_list[i]);
+		if (pd == NULL) {
+			if (global_fips140_mode == FIPS140_MODE_DISABLED)
+				continue;
+
+			/* There should never be a NULL value in FIPS 140 */
+			cmn_err(CE_WARN, "FIPS 140 activation: %s not in "
+			    "kernel provider table", fips140_module_list[i]);
+			kcf_fips140_shutdown();
+			break;
+		}
+
+		/*
+		 * Change the provider state so the verification functions
+		 * can signature verify, if necessary, and ready it.
+		 */
+		if (pd->pd_state == KCF_PROV_UNVERIFIED_FIPS140) {
+			mutex_enter(&pd->pd_lock);
+			pd->pd_state = KCF_PROV_UNVERIFIED;
+			mutex_exit(&pd->pd_lock);
+		}
+
+		KCF_PROV_REFRELE(pd);
+	}
+
+	/* If we in the process of validating FIPS 140, enable it */
+	if (global_fips140_mode != FIPS140_MODE_DISABLED) {
+		mutex_enter(&fips140_mode_lock);
+		global_fips140_mode = FIPS140_MODE_ENABLED;
+		cv_signal(&cv_fips140);
+		mutex_exit(&fips140_mode_lock);
+	}
+
+	verify_unverified_providers();
+}
+
+
+/*
+ * Perform a door call to kcfd to have it check the integrity of the
+ * kernel boundary.  Failure of the boundary will cause a FIPS 140
+ * configuration to fail
+ */
+int
+kcf_fips140_integrity_check()
+{
+	door_arg_t darg;
+	door_handle_t ldh;
+	kcf_door_arg_t *kda = { 0 }, *rkda;
+	int ret = 0;
+
+	KCF_FRMWRK_DEBUG(1, ("Starting IC check"));
+
+	mutex_enter(&kcf_dh_lock);
+	if (kcf_dh == NULL) {
+		mutex_exit(&kcf_dh_lock);
+		cmn_err(CE_WARN, "FIPS 140 Integrity Check failed, Door not "
+		    "available\n");
+		return (1);
+	}
+
+	ldh = kcf_dh;
+	door_ki_hold(ldh);
+	mutex_exit(&kcf_dh_lock);
+
+	kda = kmem_alloc(sizeof (kcf_door_arg_t), KM_SLEEP);
+	kda->da_version = KCFD_FIPS140_INTCHECK;
+	kda->da_iskernel = B_TRUE;
+
+	darg.data_ptr = (char *)kda;
+	darg.data_size = sizeof (kcf_door_arg_t);
+	darg.desc_ptr = NULL;
+	darg.desc_num = 0;
+	darg.rbuf = (char *)kda;
+	darg.rsize = sizeof (kcf_door_arg_t);
+
+	ret = door_ki_upcall_limited(ldh, &darg, NULL, SIZE_MAX, 0);
+	if (ret != 0) {
+		ret = 1;
+		goto exit;
+	}
+
+	KCF_FRMWRK_DEBUG(1, ("Integrity Check door returned = %d\n", ret));
+
+	rkda = (kcf_door_arg_t *)darg.rbuf;
+	if (rkda->da_u.result.status != ELFSIGN_SUCCESS) {
+		ret = 1;
+		KCF_FRMWRK_DEBUG(1, ("Integrity Check failed = %d\n",
+		    rkda->da_u.result.status));
+		goto exit;
+	}
+
+	KCF_FRMWRK_DEBUG(1, ("Integrity Check succeeds.\n"));
+
+exit:
+	if (rkda != kda)
+		kmem_free(rkda, darg.rsize);
+
+	kmem_free(kda, sizeof (kcf_door_arg_t));
+	door_ki_rele(ldh);
+	if (ret)
+		cmn_err(CE_WARN, "FIPS 140 Integrity Check failed.\n");
+	return (ret);
+}
+
+/*
+ * If FIPS 140 is configured to be enabled, before it can be turned on, the
+ * providers must run their Power On Self Test (POST) and we must wait to sure
+ * userland has performed its validation tests.
+ */
+void
+kcf_fips140_validate()
+{
+	kcf_provider_desc_t *pd;
+	kthread_t *post_thr;
+	int post_rv[FIPS140_MODULES_MAX];
+	kt_did_t post_t_did[FIPS140_MODULES_MAX];
+	int ret = 0;
+	int i;
+
+	/*
+	 * Run POST tests for FIPS 140 modules, if they aren't loaded, load them
+	 */
+	for (i = 0; i < FIPS140_MODULES_MAX; i++) {
+		pd = kcf_prov_tab_lookup_by_name(fips140_module_list[i]);
+		if (pd == NULL) {
+			/* If the module isn't loaded, load it */
+			ret = modload("crypto", fips140_module_list[i]);
+			if (ret == -1) {
+				cmn_err(CE_WARN, "FIPS 140 validation failed: "
+				    "error modloading module %s.",
+				    fips140_module_list[i]);
+				goto error;
+			}
+
+			/* Try again to get provider desc */
+			pd = kcf_prov_tab_lookup_by_name(
+			    fips140_module_list[i]);
+			if (pd == NULL) {
+				cmn_err(CE_WARN, "FIPS 140 validation failed: "
+				    "Could not find module %s.",
+				    fips140_module_list[i]);
+				goto error;
+			}
+		}
+
+		/* Make sure there are FIPS 140 entry points */
+		if (KCF_PROV_FIPS140_OPS(pd) == NULL) {
+			cmn_err(CE_WARN, "FIPS 140 validation failed: "
+			    "No POST function entry point in %s.",
+			    fips140_module_list[i]);
+			goto error;
+		}
+
+		/* Make sure the module is not unloaded */
+		pd->pd_mctlp->mod_loadflags |= MOD_NOAUTOUNLOAD;
+
+		/*
+		 * With the FIPS 140 POST function provided by the module in
+		 * SPI v4, start a thread to run the function.
+		 */
+		post_rv[i] = CRYPTO_OPERATION_NOT_INITIALIZED;
+		post_thr = thread_create(NULL, 0,
+		    (*(KCF_PROV_FIPS140_OPS(pd)->fips140_post)), &post_rv[i],
+		    0, &p0, TS_RUN, MAXCLSYSPRI);
+		post_thr->t_did = post_t_did[i];
+		KCF_FRMWRK_DEBUG(1, ("kcf_fips140_validate: started POST "
+		    "for %s\n", fips140_module_list[i]));
+		KCF_PROV_REFRELE(pd);
+	}
+
+	/* Do integrity check of kernel boundary */
+	ret = kcf_fips140_integrity_check();
+	if (ret == 1)
+		goto error;
+
+	/* Wait for POST threads to come back and verify results */
+	for (i = 0; i < FIPS140_MODULES_MAX; i++) {
+		if (post_t_did[i] != NULL)
+			thread_join(post_t_did[i]);
+
+		if (post_rv[i] != 0) {
+			cmn_err(CE_WARN, "FIPS 140 POST failed for %s. "
+			    "Error = %d", fips140_module_list[i], post_rv[i]);
+			goto error;
+		}
+	}
+
+	kcf_activate();
+	return;
+
+error:
+	mutex_enter(&fips140_mode_lock);
+	global_fips140_mode = FIPS140_MODE_SHUTDOWN;
+	kcf_fips140_shutdown();
+	cv_signal(&cv_fips140);
+	mutex_exit(&fips140_mode_lock);
+
+}
+
+
+/*
  * Return a pointer to the modctl structure of the
  * provider's module.
  */
@@ -155,6 +423,67 @@
 	return (mctlp);
 }
 
+/* Check if this provider requires to be verified. */
+int
+verifiable_provider(crypto_ops_t *prov_ops)
+{
+
+	if (prov_ops->co_cipher_ops == NULL && prov_ops->co_dual_ops == NULL &&
+	    prov_ops->co_dual_cipher_mac_ops == NULL &&
+	    prov_ops->co_key_ops == NULL && prov_ops->co_sign_ops == NULL &&
+	    prov_ops->co_verify_ops == NULL)
+		return (0);
+
+	return (1);
+}
+
+/*
+ * With a given provider being registered, this looks through the FIPS 140
+ * modules list and returns a 1 if it's part of the FIPS 140 boundary and
+ * the framework registration must be delayed until we know the FIPS 140 mode
+ * status.  A zero mean the provider does not need to wait for the FIPS 140
+ * boundary.
+ *
+ * If the provider in the boundary only provides random (like swrand), we
+ * can let it register as the random API will block operations.
+ */
+int
+kcf_need_fips140_verification(kcf_provider_desc_t *pd)
+{
+	int i, ret = 0;
+
+	if (pd->pd_prov_type == CRYPTO_LOGICAL_PROVIDER)
+		return (0);
+
+	mutex_enter(&fips140_mode_lock);
+
+	if (global_fips140_mode >= FIPS140_MODE_ENABLED)
+		goto exit;
+
+	for (i = 0; i < FIPS140_MODULES_MAX; i++) {
+		if (strcmp(fips140_module_list[i], pd->pd_name) != 0)
+			continue;
+
+		/* If this module is only random, we can let it register */
+		if (KCF_PROV_RANDOM_OPS(pd) &&
+		    !verifiable_provider(pd->pd_ops_vector))
+			break;
+
+		if (global_fips140_mode == FIPS140_MODE_SHUTDOWN) {
+			ret = -1;
+			break;
+		}
+
+		ret = 1;
+		break;
+	}
+
+exit:
+	mutex_exit(&fips140_mode_lock);
+	return (ret);
+}
+
+
 /*
  * Check if signature verification is needed for a provider.
  *
@@ -167,7 +496,6 @@
 {
 	struct module *mp;
 	struct modctl *mctlp = pd->pd_mctlp;
-	crypto_ops_t *prov_ops = pd->pd_ops_vector;
 
 	if (pd->pd_prov_type == CRYPTO_LOGICAL_PROVIDER)
 		return (0);
@@ -178,27 +506,15 @@
 	mp = (struct module *)mctlp->mod_mp;
 
 	/*
-	 * Check if this provider needs to be verified. We always verify
-	 * the module if it carries a signature. Any operation set which has
-	 * a encryption/decryption component is a candidate for verification.
+	 * Check if we need to verify this provider signature and if so,
+	 * make sure it has a signature section.
 	 */
-	if (prov_ops->co_cipher_ops == NULL && prov_ops->co_dual_ops == NULL &&
-	    prov_ops->co_dual_cipher_mac_ops == NULL &&
-	    prov_ops->co_key_ops == NULL && prov_ops->co_sign_ops == NULL &&
-	    prov_ops->co_verify_ops == NULL && mp->sigdata == NULL) {
+	if (verifiable_provider(pd->pd_ops_vector) == 0)
 		return (0);
-	}
 
-	/*
-	 * See if this module has a proper signature section.
-	 */
-	if (mp->sigdata == NULL) {
+	/* See if this module has its required signature section. */
+	if (mp->sigdata == NULL)
 		return (-1);
-	}
-
-	mutex_enter(&pd->pd_lock);
-	pd->pd_state = KCF_PROV_UNVERIFIED;
-	mutex_exit(&pd->pd_lock);
 
 	return (1);
 }
@@ -234,6 +550,16 @@
 	ASSERT(pd->pd_prov_type != CRYPTO_LOGICAL_PROVIDER);
 	ASSERT(mctlp != NULL);
 
+	/*
+	 * Because of FIPS 140 delays module loading, we may be running through
+	 * this code with a non-crypto signed module; therefore, another
+	 * check is necessary
+	 */
+	if (verifiable_provider(pd->pd_ops_vector) == 0) {
+		error = 0;
+		goto setverify;
+	}
+
 	for (;;) {
 		mutex_enter(&pd->pd_lock);
 		/* No need to do verification */
@@ -358,6 +684,7 @@
 	kmem_free(kda, sizeof (kcf_door_arg_t) + mp->sigsize);
 	door_ki_rele(ldh);
 
+setverify:
 	mutex_enter(&pd->pd_lock);
 	/* change state only if the original state is unchanged */
 	if (pd->pd_state == KCF_PROV_UNVERIFIED) {
@@ -396,6 +723,5 @@
 		kcf_rnd_schedule_timeout(B_TRUE);
 		rngtimer_started = 1;
 	}
-
 	return (0);
 }
--- a/usr/src/uts/common/crypto/core/kcf_cryptoadm.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/crypto/core/kcf_cryptoadm.c	Wed Oct 07 14:16:17 2009 -0700
@@ -1134,6 +1134,41 @@
 }
 
 /*
+ * This function removes a module entry from the soft_config_list.
+ *
+ * This comes in handy if FIPS 140 is enabled, but fails to validate.  At
+ * which point when the kernel reports its' supported modules, it shows only
+ * those that are not within the boundary
+ */
+void
+remove_soft_config(char *name)
+{
+	kcf_soft_conf_entry_t *p, *entry = NULL, *prev = NULL;
+
+	mutex_enter(&soft_config_mutex);
+	/* Search for provider in soft_config_list */
+	for (p = soft_config_list; p != NULL; p = p->ce_next) {
+		if (strncmp(name, p->ce_name, MAXNAMELEN) == 0) {
+			entry = p;
+			break;
+		}
+		prev = p;
+	}
+
+	if (prev == NULL) {
+		/* entry to remove is at the head of the list */
+		soft_config_list = entry->ce_next;
+	} else {
+		prev->ce_next = entry->ce_next;
+	}
+
+	mutex_exit(&soft_config_mutex);
+
+	/* free entry */
+	free_soft_config_entry(entry);
+}
+
+/*
  * This routine searches the soft_config_list for the first entry that
  * has the specified mechanism in its mechanism list.  If found,
  * a buffer containing the name of the software module that implements
--- a/usr/src/uts/common/crypto/core/kcf_prov_tabs.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/crypto/core/kcf_prov_tabs.c	Wed Oct 07 14:16:17 2009 -0700
@@ -306,6 +306,14 @@
 		    kmem_alloc(sizeof (crypto_nostore_key_ops_t), KM_SLEEP);
 }
 
+static void
+allocate_ops_v4(crypto_ops_t *src, crypto_ops_t *dst)
+{
+	if (src->co_fips140_ops != NULL)
+		dst->co_fips140_ops =
+		    kmem_alloc(sizeof (crypto_fips140_ops_t), KM_SLEEP);
+}
+
 /*
  * Allocate a provider descriptor. mech_list_count specifies the
  * number of mechanisms supported by the providers, and is used
@@ -353,8 +361,10 @@
 		allocate_ops_v1(src_ops, desc->pd_ops_vector, &mech_list_count);
 		if (info->pi_interface_version >= CRYPTO_SPI_VERSION_2)
 			allocate_ops_v2(src_ops, desc->pd_ops_vector);
-		if (info->pi_interface_version == CRYPTO_SPI_VERSION_3)
+		if (info->pi_interface_version >= CRYPTO_SPI_VERSION_3)
 			allocate_ops_v3(src_ops, desc->pd_ops_vector);
+		if (info->pi_interface_version == CRYPTO_SPI_VERSION_4)
+			allocate_ops_v4(src_ops, desc->pd_ops_vector);
 	}
 
 	desc->pd_mech_list_count = mech_list_count;
@@ -469,6 +479,10 @@
 			kmem_free(desc->pd_ops_vector->co_nostore_key_ops,
 			    sizeof (crypto_nostore_key_ops_t));
 
+		if (desc->pd_ops_vector->co_fips140_ops != NULL)
+			kmem_free(desc->pd_ops_vector->co_fips140_ops,
+			    sizeof (crypto_fips140_ops_t));
+
 		kmem_free(desc->pd_ops_vector, sizeof (crypto_ops_t));
 	}
 
@@ -857,9 +871,11 @@
 
 /*
  * This function goes through the provider table and verifies
- * any unverified providers.
+ * any KCF_PROV_UNVERIFIED providers.
  *
- * This is called when kcfd is up and the door handle is ready.
+ * This is called when kcfd is up and the door handle is ready.  It is
+ * again called when the status of FIPS 140 has been determined, so providers
+ * delayed by FIPS 140 can now be verified.
  */
 void
 verify_unverified_providers()
--- a/usr/src/uts/common/crypto/io/aes.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/crypto/io/aes.c	Wed Oct 07 14:16:17 2009 -0700
@@ -187,6 +187,12 @@
 	aes_free_context
 };
 
+static void aes_POST(int *);
+
+static crypto_fips140_ops_t aes_fips140_ops = {
+	aes_POST
+};
+
 static crypto_ops_t aes_crypto_ops = {
 	&aes_control_ops,
 	NULL,
@@ -201,11 +207,14 @@
 	NULL,
 	NULL,
 	NULL,
-	&aes_ctx_ops
+	&aes_ctx_ops,
+	NULL,
+	NULL,
+	&aes_fips140_ops
 };
 
 static crypto_provider_info_t aes_prov_info = {
-	CRYPTO_SPI_VERSION_1,
+	CRYPTO_SPI_VERSION_4,
 	"AES Software Provider",
 	CRYPTO_SW_PROVIDER,
 	{&modlinkage},
--- a/usr/src/uts/common/crypto/io/cryptoadm.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/crypto/io/cryptoadm.c	Wed Oct 07 14:16:17 2009 -0700
@@ -48,6 +48,7 @@
 #include <sys/crypto/sched_impl.h>
 #include <sys/crypto/ioctladmin.h>
 #include <c2/audit.h>
+#include <sys/disp.h>
 
 /*
  * DDI entry points.
@@ -62,9 +63,6 @@
 extern void audit_cryptoadm(int, char *, crypto_mech_name_t *, uint_t,
     uint_t, uint32_t, int);
 
-kmutex_t fips140_mode_lock;
-extern uint32_t global_fips140_mode;
-
 /*
  * Module linkage.
  */
@@ -808,14 +806,15 @@
 }
 
 /*
- * This function enables/disables FIPS140 mode or gets the  current
- * FIPS140 mode status.
+ * This function enables/disables FIPS140 mode or gets the current
+ * FIPS 140 mode status.
  *
- * Enable or disable FIPS140 ioctl operation name:
- *	FIPS140_ENABLE or FIPS140_DISABLE
- *
- * Global fips140 mode status in kernel:
- *	FIPS140_MODE_ENABLED or FIPS140_MODE_DISABLED
+ * 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
@@ -833,17 +832,42 @@
 		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_ENABLED;
-		else if (fips140_info.fips140_op == FIPS140_DISABLE)
+
+		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;
-		else {
+			kcf_activate();
+			cv_signal(&cv_fips140);
+
+		} else if (fips140_info.fips140_op != FIPS140_DISABLE &&
+		    fips140_info.fips140_op != FIPS140_ENABLE) {
 			rv = CRYPTO_ARGUMENTS_BAD;
-			error = CRYPTO_FAILED;
 		}
+
 		mutex_exit(&fips140_mode_lock);
 		break;
+
+	default:
+		rv = CRYPTO_ARGUMENTS_BAD;
 	}
 
 	fips140_info.fips140_return_value = rv;
--- a/usr/src/uts/common/crypto/io/ecc.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/crypto/io/ecc.c	Wed Oct 07 14:16:17 2009 -0700
@@ -188,6 +188,13 @@
 	ecc_nostore_key_derive
 };
 
+static void ecc_POST(int *);
+
+static crypto_fips140_ops_t ecc_fips140_ops = {
+	ecc_POST
+};
+
+
 static crypto_ops_t ecc_crypto_ops = {
 	&ecc_control_ops,
 	NULL,
@@ -204,11 +211,12 @@
 	NULL,
 	NULL,
 	NULL,
-	&ecc_nostore_key_ops
+	&ecc_nostore_key_ops,
+	&ecc_fips140_ops
 };
 
 static crypto_provider_info_t ecc_prov_info = {
-	CRYPTO_SPI_VERSION_3,
+	CRYPTO_SPI_VERSION_4,
 	"EC Software Provider",
 	CRYPTO_SW_PROVIDER,
 	{&modlinkage},
@@ -422,7 +430,7 @@
 	uint8_t extrarand[32];
 	size_t extrarand_len;
 
-	if ((rv = random_get_pseudo_bytes(ran_out, ran_len)) != 0)
+	if ((rv = random_get_pseudo_bytes_fips140(ran_out, ran_len)) != 0)
 		return (rv);
 
 	/*
@@ -445,7 +453,7 @@
 		if (ebc == 0) {
 			/* refresh extrarand */
 			extrarand_len = sizeof (extrarand);
-			if ((rv = random_get_pseudo_bytes(extrarand,
+			if ((rv = random_get_pseudo_bytes_fips140(extrarand,
 			    extrarand_len)) != 0) {
 				return (rv);
 			}
@@ -1183,7 +1191,7 @@
 	bcopy(privKey->publicValue.data, point, xylen);
 	pub_out_template[point_idx].oa_value_len = xylen;
 
-	if ((rv = kcf_get_fips140_mode()) == FIPS140_MODE_ENABLED) {
+	if (kcf_get_fips140_mode() == FIPS140_MODE_ENABLED) {
 		/* Pair-wise consistency test */
 		if ((rv = fips_pairwise_check(privKey)) != CRYPTO_SUCCESS)
 			cmn_err(CE_WARN, "ecc: fips_pairwise_check() "
--- a/usr/src/uts/common/crypto/io/rsa.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/crypto/io/rsa.c	Wed Oct 07 14:16:17 2009 -0700
@@ -274,6 +274,12 @@
 	rsa_free_context
 };
 
+static void rsa_POST(int *);
+
+static crypto_fips140_ops_t rsa_fips140_ops = {
+	rsa_POST
+};
+
 static crypto_ops_t rsa_crypto_ops = {
 	&rsa_control_ops,
 	NULL,
@@ -288,11 +294,14 @@
 	NULL,
 	NULL,
 	NULL,
-	&rsa_ctx_ops
+	&rsa_ctx_ops,
+	NULL,
+	NULL,
+	&rsa_fips140_ops
 };
 
 static crypto_provider_info_t rsa_prov_info = {
-	CRYPTO_SPI_VERSION_1,
+	CRYPTO_SPI_VERSION_4,
 	"RSA Software Provider",
 	CRYPTO_SW_PROVIDER,
 	{&modlinkage},
@@ -448,7 +457,7 @@
 	uint8_t extrarand[32];
 	size_t extrarand_len;
 
-	if ((rv = random_get_pseudo_bytes(ran_out, ran_len)) != 0)
+	if ((rv = random_get_pseudo_bytes_fips140(ran_out, ran_len)) != 0)
 		return (rv);
 
 	/*
@@ -471,7 +480,7 @@
 		if (ebc == 0) {
 			/* refresh extrarand */
 			extrarand_len = sizeof (extrarand);
-			if ((rv = random_get_pseudo_bytes(extrarand,
+			if ((rv = random_get_pseudo_bytes_fips140(extrarand,
 			    extrarand_len)) != 0) {
 				return (rv);
 			}
--- a/usr/src/uts/common/crypto/io/sha1_mod.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/crypto/io/sha1_mod.c	Wed Oct 07 14:16:17 2009 -0700
@@ -163,6 +163,12 @@
 	sha1_free_context
 };
 
+static void sha1_POST(int *);
+
+static crypto_fips140_ops_t sha1_fips140_ops = {
+	sha1_POST
+};
+
 static crypto_ops_t sha1_crypto_ops = {
 	&sha1_control_ops,
 	&sha1_digest_ops,
@@ -177,11 +183,14 @@
 	NULL,
 	NULL,
 	NULL,
-	&sha1_ctx_ops
+	&sha1_ctx_ops,
+	NULL,
+	NULL,
+	&sha1_fips140_ops
 };
 
 static crypto_provider_info_t sha1_prov_info = {
-	CRYPTO_SPI_VERSION_1,
+	CRYPTO_SPI_VERSION_4,
 	"SHA1 Software Provider",
 	CRYPTO_SW_PROVIDER,
 	{&modlinkage},
--- a/usr/src/uts/common/crypto/io/sha2_mod.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/crypto/io/sha2_mod.c	Wed Oct 07 14:16:17 2009 -0700
@@ -189,6 +189,12 @@
 	sha2_free_context
 };
 
+static void sha2_POST(int *);
+
+static crypto_fips140_ops_t sha2_fips140_ops = {
+	sha2_POST
+};
+
 static crypto_ops_t sha2_crypto_ops = {
 	&sha2_control_ops,
 	&sha2_digest_ops,
@@ -203,11 +209,14 @@
 	NULL,
 	NULL,
 	NULL,
-	&sha2_ctx_ops
+	&sha2_ctx_ops,
+	NULL,
+	NULL,
+	&sha2_fips140_ops
 };
 
 static crypto_provider_info_t sha2_prov_info = {
-	CRYPTO_SPI_VERSION_1,
+	CRYPTO_SPI_VERSION_4,
 	"SHA2 Software Provider",
 	CRYPTO_SW_PROVIDER,
 	{&modlinkage},
--- a/usr/src/uts/common/crypto/io/swrand.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/crypto/io/swrand.c	Wed Oct 07 14:16:17 2009 -0700
@@ -162,6 +162,12 @@
 	swrand_generate_random
 };
 
+static void swrand_POST(int *);
+
+static crypto_fips140_ops_t swrand_fips140_ops = {
+	swrand_POST
+};
+
 static crypto_ops_t swrand_crypto_ops = {
 	&swrand_control_ops,
 	NULL,
@@ -176,11 +182,14 @@
 	NULL,
 	NULL,
 	NULL,
-	NULL
+	NULL,
+	NULL,
+	NULL,
+	&swrand_fips140_ops
 };
 
 static crypto_provider_info_t swrand_prov_info = {
-	CRYPTO_SPI_VERSION_1,
+	CRYPTO_SPI_VERSION_4,
 	"Kernel Random Number Provider",
 	CRYPTO_SW_PROVIDER,
 	{&modlinkage},
--- a/usr/src/uts/common/crypto/spi/kcf_spi.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/crypto/spi/kcf_spi.c	Wed Oct 07 14:16:17 2009 -0700
@@ -38,6 +38,7 @@
 #include <sys/crypto/impl.h>
 #include <sys/crypto/sched_impl.h>
 #include <sys/crypto/spi.h>
+#include <sys/crypto/ioctladmin.h>
 #include <sys/taskq.h>
 #include <sys/disp.h>
 #include <sys/kstat.h>
@@ -108,6 +109,12 @@
 	KCF_SPI_COPY_OPS(src_ops, dst_ops, co_nostore_key_ops);
 }
 
+static void
+copy_ops_vector_v4(crypto_ops_t *src_ops, crypto_ops_t *dst_ops)
+{
+	KCF_SPI_COPY_OPS(src_ops, dst_ops, co_fips140_ops);
+}
+
 /*
  * This routine is used to add cryptographic providers to the KEF framework.
  * Providers pass a crypto_provider_info structure to crypto_register_provider()
@@ -120,15 +127,14 @@
 crypto_register_provider(crypto_provider_info_t *info,
     crypto_kcf_provider_handle_t *handle)
 {
-	int need_verify;
+	int need_fips140_verify, need_verify = 1;
 	struct modctl *mcp;
 	char *name;
 	char ks_name[KSTAT_STRLEN];
-
 	kcf_provider_desc_t *prov_desc = NULL;
 	int ret = CRYPTO_ARGUMENTS_BAD;
 
-	if (info->pi_interface_version > CRYPTO_SPI_VERSION_3)
+	if (info->pi_interface_version > CRYPTO_SPI_VERSION_4)
 		return (CRYPTO_VERSION_MISMATCH);
 
 	/*
@@ -177,10 +183,14 @@
 			    prov_desc->pd_ops_vector);
 			prov_desc->pd_flags = info->pi_flags;
 		}
-		if (info->pi_interface_version == CRYPTO_SPI_VERSION_3) {
+		if (info->pi_interface_version >= CRYPTO_SPI_VERSION_3) {
 			copy_ops_vector_v3(info->pi_ops_vector,
 			    prov_desc->pd_ops_vector);
 		}
+		if (info->pi_interface_version == CRYPTO_SPI_VERSION_4) {
+			copy_ops_vector_v4(info->pi_ops_vector,
+			    prov_desc->pd_ops_vector);
+		}
 	}
 
 	/* object_ops and nostore_key_ops are mutually exclusive */
@@ -242,6 +252,15 @@
 		goto bail;
 	}
 
+	if ((need_fips140_verify =
+	    kcf_need_fips140_verification(prov_desc)) == -1) {
+		mutex_enter(&prov_desc->pd_lock);
+		prov_desc->pd_state = KCF_PROV_VERIFICATION_FAILED;
+		mutex_exit(&prov_desc->pd_lock);
+		ret = CRYPTO_FIPS140_ERROR;
+		goto bail;
+	}
+
 	/*
 	 * We create a taskq only for a hardware provider. The global
 	 * software queue is used for software providers. We handle ordering
@@ -317,7 +336,20 @@
 	if (prov_desc->pd_prov_type == CRYPTO_HW_PROVIDER)
 		process_logical_providers(info, prov_desc);
 
+	/* This provider needs to wait until we know the FIPS 140 status */
+	if (need_fips140_verify == 1) {
+		mutex_enter(&prov_desc->pd_lock);
+		prov_desc->pd_state = KCF_PROV_UNVERIFIED_FIPS140;
+		mutex_exit(&prov_desc->pd_lock);
+		goto exit;
+	}
+
+	/* This provider needs to have the signature verified */
 	if (need_verify == 1) {
+		mutex_enter(&prov_desc->pd_lock);
+		prov_desc->pd_state = KCF_PROV_UNVERIFIED;
+		mutex_exit(&prov_desc->pd_lock);
+
 		/* kcf_verify_signature routine will release this hold */
 		KCF_PROV_REFHOLD(prov_desc);
 
@@ -346,6 +378,7 @@
 		kcf_do_notify(prov_desc, B_TRUE);
 	}
 
+exit:
 	*handle = prov_desc->pd_kcf_prov_handle;
 	ret = CRYPTO_SUCCESS;
 
--- a/usr/src/uts/common/des/des_crypt.c	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/des/des_crypt.c	Wed Oct 07 14:16:17 2009 -0700
@@ -221,6 +221,12 @@
 	des_key_check
 };
 
+static void des_POST(int *);
+
+static crypto_fips140_ops_t des_fips140_ops = {
+	des_POST
+};
+
 static crypto_ops_t des_crypto_ops = {
 	&des_control_ops,
 	NULL,
@@ -235,11 +241,14 @@
 	NULL,
 	&des_key_ops,
 	NULL,
-	&des_ctx_ops
+	&des_ctx_ops,
+	NULL,
+	NULL,
+	&des_fips140_ops
 };
 
 static crypto_provider_info_t des_prov_info = {
-	CRYPTO_SPI_VERSION_1,
+	CRYPTO_SPI_VERSION_4,
 	"DES Software Provider",
 	CRYPTO_SW_PROVIDER,
 	{&modlinkage},
--- a/usr/src/uts/common/sys/crypto/common.h	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/sys/crypto/common.h	Wed Oct 07 14:16:17 2009 -0700
@@ -565,6 +565,7 @@
 #define	CRYPTO_MODVERIFICATION_FAILED		0x00000050
 #define	CRYPTO_OLD_CTX_TEMPLATE			0x00000051
 #define	CRYPTO_WEAK_KEY				0x00000052
+#define	CRYPTO_FIPS140_ERROR			0x00000053
 
 /*
  * Special values that can be used to indicate that information is unavailable
--- a/usr/src/uts/common/sys/crypto/elfsign.h	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/sys/crypto/elfsign.h	Wed Oct 07 14:16:17 2009 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -52,7 +52,10 @@
 	ELFSIGN_RESTRICTED
 } ELFsign_status_t;
 
+/* Version values for da_version in kcf_door_args_t */
 #define	KCF_KCFD_VERSION1	1
+#define	KCFD_FIPS140_INTCHECK	2
+
 #define	SIG_MAX_LENGTH		1024
 
 #define	ELF_SIGNATURE_SECTION	".SUNW_signature"
--- a/usr/src/uts/common/sys/crypto/impl.h	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/sys/crypto/impl.h	Wed Oct 07 14:16:17 2009 -0700
@@ -139,6 +139,7 @@
 typedef enum {
 	KCF_PROV_ALLOCATED = 1,
 	KCF_PROV_UNVERIFIED,
+	KCF_PROV_UNVERIFIED_FIPS140,
 	KCF_PROV_VERIFICATION_FAILED,
 	/*
 	 * state < KCF_PROV_READY means the provider can not
@@ -523,6 +524,13 @@
 
 #define	KCF_MAX_PIN_LEN			1024
 
+/* Global FIPS 140 mode variable */
+extern uint32_t global_fips140_mode;
+/* Global FIPS 140 mode lock */
+extern kmutex_t fips140_mode_lock;
+/* Conditional variable for kcf to wait until kcfd tells the FIPS mode status */
+extern kcondvar_t cv_fips140;
+
 /*
  * Per-minor info.
  *
@@ -581,6 +589,7 @@
 #define	KCF_PROV_MECH_OPS(pd)		((pd)->pd_ops_vector->co_mech_ops)
 #define	KCF_PROV_NOSTORE_KEY_OPS(pd)	\
 	((pd)->pd_ops_vector->co_nostore_key_ops)
+#define	KCF_PROV_FIPS140_OPS(pd)	((pd)->pd_ops_vector->co_fips140_ops)
 
 /*
  * Wrappers for crypto_control_ops(9S) entry points.
@@ -1313,6 +1322,7 @@
 int crypto_load_door(uint_t did);
 void crypto_free_mech_list(crypto_mech_name_t *list, uint_t count);
 void crypto_free_dev_list(crypto_dev_list_entry_t *list, uint_t count);
+extern void kcf_activate();
 
 /* Miscellaneous */
 int crypto_get_mechanism_number(caddr_t name, crypto_mech_type_t *number);
@@ -1395,8 +1405,12 @@
     uint_t *, crypto_mech_name_t **);
 extern int kcf_policy_load_dev_disabled(char *, uint_t, uint_t,
     crypto_mech_name_t *, uint_t *, crypto_mech_name_t **);
-extern boolean_t in_soft_config_list(char *);
+extern void remove_soft_config(char *);
+
+/* FIPS 140 functions */
 extern int kcf_get_fips140_mode(void);
+extern void kcf_fips140_validate();
+extern void kcf_activate();
 
 #endif	/* _KERNEL */
 
--- a/usr/src/uts/common/sys/crypto/ioctladmin.h	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/sys/crypto/ioctladmin.h	Wed Oct 07 14:16:17 2009 -0700
@@ -127,14 +127,19 @@
 	FIPS140_STATUS,		/* get current fips140 mode */
 	FIPS140_ENABLE,		/* enable fips140 mode */
 	FIPS140_DISABLE		/* disable fips140 mode */
+
+
 };
 
 /*
  * FIPS140 Mode Status
  */
 enum {
-	FIPS140_MODE_DISABLED,
-	FIPS140_MODE_ENABLED
+	FIPS140_MODE_UNSET,	 /* userland has not told us the mode */
+	FIPS140_MODE_VALIDATING, /* In the process of validation to enable */
+	FIPS140_MODE_SHUTDOWN,	 /* Failure has occurred, shutdown framework */
+	FIPS140_MODE_ENABLED,	 /* Validation is complete and we are running */
+	FIPS140_MODE_DISABLED	 /* Not running in FIPS 140 mode */
 };
 
 #define	CRYPTO_GET_VERSION		CRYPTOADMIN(1)
--- a/usr/src/uts/common/sys/crypto/sched_impl.h	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/sys/crypto/sched_impl.h	Wed Oct 07 14:16:17 2009 -0700
@@ -524,6 +524,7 @@
 
 extern int kcf_svc_wait(int *);
 extern int kcf_svc_do_run(void);
+extern int kcf_need_fips140_verification(kcf_provider_desc_t *);
 extern int kcf_need_signature_verification(kcf_provider_desc_t *);
 extern void kcf_verify_signature(void *);
 extern struct modctl *kcf_get_modctl(crypto_provider_info_t *);
--- a/usr/src/uts/common/sys/crypto/spi.h	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/sys/crypto/spi.h	Wed Oct 07 14:16:17 2009 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -45,6 +45,7 @@
 #define	CRYPTO_SPI_VERSION_1	1
 #define	CRYPTO_SPI_VERSION_2	2
 #define	CRYPTO_SPI_VERSION_3	3
+#define	CRYPTO_SPI_VERSION_4	4
 
 /*
  * Provider-private handle. This handle is specified by a provider
@@ -490,6 +491,15 @@
 } crypto_nostore_key_ops_t;
 
 /*
+ * crypto_fips140_ops provides a function for FIPS 140 Power-On Self Test for
+ * those providers that are part of the Cryptographic Framework bounday.  See
+ * crypto_fips140_ops(9s) for details.
+ */
+typedef struct crypto_fips140_ops {
+	void (*fips140_post)(int *);
+} crypto_fips140_ops_t;
+
+/*
  * The crypto_ops(9S) structure contains the structures containing
  * the pointers to functions implemented by cryptographic providers.
  * It is specified as part of the crypto_provider_info(9S)
@@ -523,8 +533,14 @@
 	crypto_nostore_key_ops_t		*co_nostore_key_ops;
 } crypto_ops_v3_t;
 
+typedef struct crypto_ops_v4 {
+	crypto_ops_v3_t				v3_ops;
+	crypto_fips140_ops_t			*co_fips140_ops;
+} crypto_ops_v4_t;
+
 typedef struct crypto_ops {
 	union {
+		crypto_ops_v4_t	cou_v4;
 		crypto_ops_v3_t	cou_v3;
 		crypto_ops_v2_t	cou_v2;
 		crypto_ops_v1_t	cou_v1;
@@ -547,6 +563,7 @@
 #define	co_ctx_ops			cou.cou_v1.co_ctx_ops
 #define	co_mech_ops			cou.cou_v2.co_mech_ops
 #define	co_nostore_key_ops		cou.cou_v3.co_nostore_key_ops
+#define	co_fips140_ops			cou.cou_v4.co_fips140_ops
 
 /*
  * Provider device specification passed during registration.
--- a/usr/src/uts/common/sys/random.h	Wed Oct 07 11:15:29 2009 -0700
+++ b/usr/src/uts/common/sys/random.h	Wed Oct 07 14:16:17 2009 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -60,6 +60,14 @@
 extern int random_get_bytes(uint8_t *, size_t);
 extern int random_get_pseudo_bytes(uint8_t *, size_t);
 
+/*
+ * Functions for FIPS 140 validated random. Thesse functions should not be used
+ * for early booting kernel modules as modules in a FIPS 140 boundary must wait
+ * until the SMF service "cryptosvc" to run.
+ */
+extern int random_get_bytes_fips140(uint8_t *, size_t);
+extern int random_get_pseudo_bytes_fips140(uint8_t *, size_t);
+
 #endif /* _KERNEL */
 
 #ifdef	__cplusplus