/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include "cryptoadm.h" #define DEFAULT_DEV_NUM 5 #define DEFAULT_SOFT_NUM 10 static crypto_get_soft_info_t *setup_get_soft_info(char *, int); /* * Prepare the argument for the LOAD_SOFT_CONFIG ioctl call for the * provider pointed by pent. Return NULL if out of memory. */ crypto_load_soft_config_t * setup_soft_conf(entry_t *pent) { crypto_load_soft_config_t *pload_soft_conf; mechlist_t *plist; uint_t sup_count; size_t extra_mech_size = 0; int i; if (pent == NULL) { return (NULL); } sup_count = pent->sup_count; if (sup_count > 1) { extra_mech_size = sizeof (crypto_mech_name_t) * (sup_count - 1); } pload_soft_conf = malloc(sizeof (crypto_load_soft_config_t) + extra_mech_size); if (pload_soft_conf == NULL) { cryptodebug("out of memory."); return (NULL); } (void) strlcpy(pload_soft_conf->sc_name, pent->name, MAXNAMELEN); pload_soft_conf->sc_count = sup_count; i = 0; plist = pent->suplist; while (i < sup_count) { (void) strlcpy(pload_soft_conf->sc_list[i++], plist->name, CRYPTO_MAX_MECH_NAME); plist = plist->next; } return (pload_soft_conf); } /* * Prepare the argument for the LOAD_SOFT_DISABLED ioctl call for the * provider pointed by pent. Return NULL if out of memory. */ crypto_load_soft_disabled_t * setup_soft_dis(entry_t *pent) { crypto_load_soft_disabled_t *pload_soft_dis = NULL; mechlist_t *plist = NULL; size_t extra_mech_size = 0; uint_t dis_count; int i; if (pent == NULL) { return (NULL); } dis_count = pent->dis_count; if (dis_count > 1) { extra_mech_size = sizeof (crypto_mech_name_t) * (dis_count - 1); } pload_soft_dis = malloc(sizeof (crypto_load_soft_disabled_t) + extra_mech_size); if (pload_soft_dis == NULL) { cryptodebug("out of memory."); return (NULL); } (void) strlcpy(pload_soft_dis->sd_name, pent->name, MAXNAMELEN); pload_soft_dis->sd_count = dis_count; i = 0; plist = pent->dislist; while (i < dis_count) { (void) strlcpy(pload_soft_dis->sd_list[i++], plist->name, CRYPTO_MAX_MECH_NAME); plist = plist->next; } return (pload_soft_dis); } /* * Prepare the argument for the LOAD_DEV_DISABLED ioctl call for the * provider pointed by pent. Return NULL if out of memory. */ crypto_load_dev_disabled_t * setup_dev_dis(entry_t *pent) { crypto_load_dev_disabled_t *pload_dev_dis = NULL; mechlist_t *plist = NULL; size_t extra_mech_size = 0; uint_t dis_count; int i; char pname[MAXNAMELEN]; int inst_num; if (pent == NULL) { return (NULL); } /* get the device name and the instance number */ if (split_hw_provname(pent->name, pname, &inst_num) == FAILURE) { return (NULL); } /* allocate space for pload_dev_des */ dis_count = pent->dis_count; if (dis_count > 1) { extra_mech_size = sizeof (crypto_mech_name_t) * (dis_count - 1); } pload_dev_dis = malloc(sizeof (crypto_load_dev_disabled_t) + extra_mech_size); if (pload_dev_dis == NULL) { cryptodebug("out of memory."); return (NULL); } /* set the values for pload_dev_dis */ (void) strlcpy(pload_dev_dis->dd_dev_name, pname, MAXNAMELEN); pload_dev_dis->dd_dev_instance = inst_num; pload_dev_dis->dd_count = dis_count; i = 0; plist = pent->dislist; while (i < dis_count) { (void) strlcpy(pload_dev_dis->dd_list[i++], plist->name, CRYPTO_MAX_MECH_NAME); plist = plist->next; } return (pload_dev_dis); } /* * Prepare the calling argument of the UNLOAD_SOFT_MODULE ioctl call for the * provider pointed by pent. Return NULL if out of memory. */ crypto_unload_soft_module_t * setup_unload_soft(entry_t *pent) { crypto_unload_soft_module_t *punload_soft; if (pent == NULL) { return (NULL); } punload_soft = malloc(sizeof (crypto_unload_soft_module_t)); if (punload_soft == NULL) { cryptodebug("out of memory."); return (NULL); } (void) strlcpy(punload_soft->sm_name, pent->name, MAXNAMELEN); return (punload_soft); } /* * Prepare the calling argument for the GET_SOFT_INFO call for the provider * with the number of mechanisms specified in the second argument. * * Called by get_soft_info(). */ static crypto_get_soft_info_t * setup_get_soft_info(char *provname, int count) { crypto_get_soft_info_t *psoft_info; size_t extra_mech_size = 0; if (provname == NULL) { return (NULL); } if (count > 1) { extra_mech_size = sizeof (crypto_mech_name_t) * (count - 1); } psoft_info = malloc(sizeof (crypto_get_soft_info_t) + extra_mech_size); if (psoft_info == NULL) { cryptodebug("out of memory."); return (NULL); } (void) strlcpy(psoft_info->si_name, provname, MAXNAMELEN); psoft_info->si_count = count; return (psoft_info); } /* * Get the device list from kernel. */ int get_dev_list(crypto_get_dev_list_t **ppdevlist) { crypto_get_dev_list_t *pdevlist; int fd = -1; int count = DEFAULT_DEV_NUM; pdevlist = malloc(sizeof (crypto_get_dev_list_t) + sizeof (crypto_dev_list_entry_t) * (count - 1)); if (pdevlist == NULL) { cryptodebug("out of memory."); return (FAILURE); } if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) { cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"), ADMIN_IOCTL_DEVICE, strerror(errno)); return (FAILURE); } pdevlist->dl_dev_count = count; if (ioctl(fd, CRYPTO_GET_DEV_LIST, pdevlist) == -1) { cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed: %s", strerror(errno)); free(pdevlist); (void) close(fd); return (FAILURE); } /* BUFFER is too small, get the number of devices and retry it. */ if (pdevlist->dl_return_value == CRYPTO_BUFFER_TOO_SMALL) { count = pdevlist->dl_dev_count; free(pdevlist); pdevlist = malloc(sizeof (crypto_get_dev_list_t) + sizeof (crypto_dev_list_entry_t) * (count - 1)); if (pdevlist == NULL) { cryptodebug("out of memory."); (void) close(fd); return (FAILURE); } if (ioctl(fd, CRYPTO_GET_DEV_LIST, pdevlist) == -1) { cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed: %s", strerror(errno)); free(pdevlist); (void) close(fd); return (FAILURE); } } if (pdevlist->dl_return_value != CRYPTO_SUCCESS) { cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed, " "return_value = %d", pdevlist->dl_return_value); free(pdevlist); (void) close(fd); return (FAILURE); } *ppdevlist = pdevlist; (void) close(fd); return (SUCCESS); } /* * Get all the mechanisms supported by the hardware provider. * The result will be stored in the second argument. */ int get_dev_info(char *devname, int inst_num, int count, mechlist_t **ppmechlist) { crypto_get_dev_info_t *dev_info; mechlist_t *phead; mechlist_t *pcur; mechlist_t *pmech; int fd = -1; int i; int rc; if (devname == NULL || count < 1) { cryptodebug("get_dev_info(): devname is NULL or bogus count"); return (FAILURE); } /* Set up the argument for the CRYPTO_GET_DEV_INFO ioctl call */ dev_info = malloc(sizeof (crypto_get_dev_info_t) + sizeof (crypto_mech_name_t) * (count - 1)); if (dev_info == NULL) { cryptodebug("out of memory."); return (FAILURE); } (void) strlcpy(dev_info->di_dev_name, devname, MAXNAMELEN); dev_info->di_dev_instance = inst_num; dev_info->di_count = count; /* Open the ioctl device */ if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) { cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"), ADMIN_IOCTL_DEVICE, strerror(errno)); free(dev_info); return (FAILURE); } if (ioctl(fd, CRYPTO_GET_DEV_INFO, dev_info) == -1) { cryptodebug("CRYPTO_GET_DEV_INFO ioctl failed: %s", strerror(errno)); free(dev_info); (void) close(fd); return (FAILURE); } if (dev_info->di_return_value != CRYPTO_SUCCESS) { cryptodebug("CRYPTO_GET_DEV_INFO ioctl failed, " "return_value = %d", dev_info->di_return_value); free(dev_info); (void) close(fd); return (FAILURE); } phead = pcur = NULL; rc = SUCCESS; for (i = 0; i < dev_info->di_count; i++) { pmech = create_mech(&dev_info->di_list[i][0]); if (pmech == NULL) { rc = FAILURE; break; } else { if (phead == NULL) { phead = pcur = pmech; } else { pcur->next = pmech; pcur = pmech; } } } if (rc == SUCCESS) { *ppmechlist = phead; } else { free_mechlist(phead); } free(dev_info); (void) close(fd); return (rc); } /* * Get the supported mechanism list of the software provider from kernel. * * Parameters phardlist and psoftlist are supplied by get_kcfconf_info(). * If NULL, this function calls get_kcfconf_info() internally. */ int get_soft_info(char *provname, mechlist_t **ppmechlist, entrylist_t *phardlist, entrylist_t *psoftlist) { boolean_t in_kernel = B_FALSE; crypto_get_soft_info_t *psoft_info; mechlist_t *phead; mechlist_t *pmech; mechlist_t *pcur; entry_t *pent = NULL; int count; int fd = -1; int rc; int i; if (provname == NULL) { return (FAILURE); } if (getzoneid() == GLOBAL_ZONEID) { /* use kcf.conf for kernel software providers in global zone */ if ((pent = getent_kef(provname, phardlist, psoftlist)) == NULL) { /* No kcf.conf entry for this provider */ if (check_kernel_for_soft(provname, NULL, &in_kernel) == FAILURE) { return (FAILURE); } else if (in_kernel == B_FALSE) { cryptoerror(LOG_STDERR, gettext("%s does not exist."), provname); return (FAILURE); } /* * Set mech count to 1. It will be reset to the * correct value later if the setup buffer is too small. */ count = 1; } else { count = pent->sup_count; free_entry(pent); } } else { /* * kcf.conf not there in non-global zone: set mech count to 1. * It will be reset to the correct value later if the setup * buffer is too small. */ count = 1; } if ((psoft_info = setup_get_soft_info(provname, count)) == NULL) { return (FAILURE); } if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) { cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"), ADMIN_IOCTL_DEVICE, strerror(errno)); free(psoft_info); return (FAILURE); } /* make GET_SOFT_INFO ioctl call */ if ((rc = ioctl(fd, CRYPTO_GET_SOFT_INFO, psoft_info)) == -1) { cryptodebug("CRYPTO_GET_SOFT_INFO ioctl failed: %s", strerror(errno)); (void) close(fd); free(psoft_info); return (FAILURE); } /* BUFFER is too small, get the number of mechanisms and retry it. */ if (psoft_info->si_return_value == CRYPTO_BUFFER_TOO_SMALL) { count = psoft_info->si_count; free(psoft_info); if ((psoft_info = setup_get_soft_info(provname, count)) == NULL) { (void) close(fd); return (FAILURE); } else { rc = ioctl(fd, CRYPTO_GET_SOFT_INFO, psoft_info); if (rc == -1) { cryptodebug("CRYPTO_GET_SOFT_INFO ioctl " "failed: %s", strerror(errno)); (void) close(fd); free(psoft_info); return (FAILURE); } } } (void) close(fd); if (psoft_info->si_return_value != CRYPTO_SUCCESS) { cryptodebug("CRYPTO_GET_SOFT_INFO ioctl failed, " "return_value = %d", psoft_info->si_return_value); free(psoft_info); return (FAILURE); } /* Build the mechanism linked list and return it */ rc = SUCCESS; phead = pcur = NULL; for (i = 0; i < psoft_info->si_count; i++) { pmech = create_mech(&psoft_info->si_list[i][0]); if (pmech == NULL) { rc = FAILURE; break; } else { if (phead == NULL) { phead = pcur = pmech; } else { pcur->next = pmech; pcur = pmech; } } } if (rc == FAILURE) { free_mechlist(phead); } else { *ppmechlist = phead; } free(psoft_info); return (rc); } /* * Get the kernel software provider list from kernel. */ int get_soft_list(crypto_get_soft_list_t **ppsoftlist) { crypto_get_soft_list_t *psoftlist = NULL; int count = DEFAULT_SOFT_NUM; int len; int fd = -1; if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) { cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"), ADMIN_IOCTL_DEVICE, strerror(errno)); return (FAILURE); } len = MAXNAMELEN * count; psoftlist = malloc(sizeof (crypto_get_soft_list_t) + len); if (psoftlist == NULL) { cryptodebug("out of memory."); (void) close(fd); return (FAILURE); } psoftlist->sl_soft_names = (caddr_t)(psoftlist + 1); psoftlist->sl_soft_count = count; psoftlist->sl_soft_len = len; if (ioctl(fd, CRYPTO_GET_SOFT_LIST, psoftlist) == -1) { cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed: %s", strerror(errno)); free(psoftlist); (void) close(fd); return (FAILURE); } /* * if BUFFER is too small, get the number of software providers and * the minimum length needed for names and length and retry it. */ if (psoftlist->sl_return_value == CRYPTO_BUFFER_TOO_SMALL) { count = psoftlist->sl_soft_count; len = psoftlist->sl_soft_len; free(psoftlist); psoftlist = malloc(sizeof (crypto_get_soft_list_t) + len); if (psoftlist == NULL) { cryptodebug("out of memory."); (void) close(fd); return (FAILURE); } psoftlist->sl_soft_names = (caddr_t)(psoftlist + 1); psoftlist->sl_soft_count = count; psoftlist->sl_soft_len = len; if (ioctl(fd, CRYPTO_GET_SOFT_LIST, psoftlist) == -1) { cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed:" "%s", strerror(errno)); free(psoftlist); (void) close(fd); return (FAILURE); } } if (psoftlist->sl_return_value != CRYPTO_SUCCESS) { cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed, " "return_value = %d", psoftlist->sl_return_value); free(psoftlist); (void) close(fd); return (FAILURE); } *ppsoftlist = psoftlist; (void) close(fd); return (SUCCESS); } /* * Perform the FIPS related actions */ int do_fips_actions(int action, int caller) { crypto_fips140_t fips_info; int fd; int rc = SUCCESS; int pkcs11_fips_mode = 0; /* Get FIPS-140 status from pkcs11.conf */ fips_status_pkcs11conf(&pkcs11_fips_mode); if (action == FIPS140_STATUS) { if (pkcs11_fips_mode == CRYPTO_FIPS_MODE_ENABLED) (void) printf(gettext( "\tFIPS-140 mode is enabled.\n")); else (void) printf(gettext( "\tFIPS-140 mode is disabled.\n")); return (SUCCESS); } if (caller == NOT_REFRESH) { /* Is it a duplicate operation? */ if ((action == FIPS140_ENABLE) && (pkcs11_fips_mode == CRYPTO_FIPS_MODE_ENABLED)) { cryptoerror(LOG_STDERR, gettext("FIPS-140 mode has already " "been enabled.\n")); return (FAILURE); } if ((action == FIPS140_DISABLE) && (pkcs11_fips_mode == CRYPTO_FIPS_MODE_DISABLED)) { cryptoerror(LOG_STDERR, gettext("FIPS-140 mode has already " "been disabled.\n")); return (FAILURE); } if ((action == FIPS140_ENABLE) || (action == FIPS140_DISABLE)) { /* Update pkcs11.conf */ if ((rc = fips_update_pkcs11conf(action)) != SUCCESS) return (rc); } /* No need to inform kernel */ if (action == FIPS140_ENABLE) { (void) printf(gettext( "FIPS-140 mode was enabled successfully.\n")); } else { (void) printf(gettext( "FIPS-140 mode was disabled successfully.\n")); } (void) printf(gettext( "The FIPS-140 mode has changed.\n")); (void) printf(gettext( "The system will require a reboot.\n\n")); return (SUCCESS); } /* This is refresh, need to inform kernel */ (void) memset(&fips_info, 0, sizeof (crypto_fips140_t)); if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) { cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"), ADMIN_IOCTL_DEVICE, strerror(errno)); return (FAILURE); } switch (action) { case FIPS140_ENABLE: /* make CRYPTO_FIPS_SET ioctl call */ fips_info.fips140_op = FIPS140_ENABLE; if ((rc = ioctl(fd, CRYPTO_FIPS140_SET, &fips_info)) == -1) { cryptodebug("CRYPTO_FIPS140_ENABLE ioctl failed: %s", strerror(errno)); rc = FAILURE; goto out; } if (fips_info.fips140_return_value != CRYPTO_SUCCESS) { cryptodebug("CRYPTO_FIPS140_ENABLE ioctl failed, " "return_value = %d", fips_info.fips140_return_value); rc = FAILURE; } break; case FIPS140_DISABLE: /* make CRYPTO_FIPS140_SET ioctl call */ fips_info.fips140_op = FIPS140_DISABLE; if ((rc = ioctl(fd, CRYPTO_FIPS140_SET, &fips_info)) == -1) { cryptodebug("CRYPTO_FIPS140_DISABLE ioctl failed: %s", strerror(errno)); rc = FAILURE; goto out; } if (fips_info.fips140_return_value != CRYPTO_SUCCESS) { cryptodebug("CRYPTO_FIPS140_DISABLE ioctl failed, " "return_value = %d", fips_info.fips140_return_value); rc = FAILURE; } break; default: rc = FAILURE; break; }; out: (void) close(fd); return (rc); }