# HG changeset patch # User Anthony Scarpino # Date 1254950177 25200 # Node ID 498ac26a63d5a2eeb78c0303f0b11688ac13eb29 # Parent 31fd4bc94a37e0a16cef7e70b5155fd8556faa60 PSARC/2009/447 Kernel Cryptographic Framework support for FIPS 140-2 6703950 Solaris cryptographic framework needs to implement changes for FIPS-140-2 compliance diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/cmd/cmd-crypto/cryptoadm/adm_kef.c --- 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); diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/cmd/truss/codes.c --- 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 }, diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/lib/libelfsign/common/elfcertlib.c --- 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); diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/lib/libelfsign/common/elfsignlib.c --- 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"); diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/lib/libelfsign/common/libelfsign.h --- 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 diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/lib/pkcs11/libpkcs11/common/pkcs11Conf.c --- 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: diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/c2/audit.c --- 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; } diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/crypto/api/kcf_random.c --- 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 #include #include +#include #include #include #include @@ -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)); +} diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/crypto/core/kcf.c --- 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 #include #include +#include #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); } diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/crypto/core/kcf_cryptoadm.c --- 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 diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/crypto/core/kcf_prov_tabs.c --- 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() diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/crypto/io/aes.c --- 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}, diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/crypto/io/cryptoadm.c --- 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 #include #include +#include /* * 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; diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/crypto/io/ecc.c --- 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() " diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/crypto/io/rsa.c --- 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); } diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/crypto/io/sha1_mod.c --- 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}, diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/crypto/io/sha2_mod.c --- 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}, diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/crypto/io/swrand.c --- 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}, diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/crypto/spi/kcf_spi.c --- 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 #include #include +#include #include #include #include @@ -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; diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/des/des_crypt.c --- 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}, diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/sys/crypto/common.h --- 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 diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/sys/crypto/elfsign.h --- 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" diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/sys/crypto/impl.h --- 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 */ diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/sys/crypto/ioctladmin.h --- 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) diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/sys/crypto/sched_impl.h --- 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 *); diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/sys/crypto/spi.h --- 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. diff -r 31fd4bc94a37 -r 498ac26a63d5 usr/src/uts/common/sys/random.h --- 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