/*
* 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 (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Copyright 2010 Nexenta Systems, Inc. All rights reserved.
*/
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <locale.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/varargs.h>
#include <zone.h>
#include <sys/crypto/ioctladmin.h>
#include "cryptoadm.h"
#define DEFAULT_DEV_NUM 5
#define DEFAULT_SOFT_NUM 10
static crypto_get_soft_info_t *setup_get_soft_info(char *, int);
/*
* Prepare the argument for the LOAD_SOFT_CONFIG ioctl call for the
* provider pointed by pent. Return NULL if out of memory.
*/
crypto_load_soft_config_t *
setup_soft_conf(entry_t *pent)
{
crypto_load_soft_config_t *pload_soft_conf;
mechlist_t *plist;
uint_t sup_count;
size_t extra_mech_size = 0;
int i;
if (pent == NULL) {
return (NULL);
}
sup_count = pent->sup_count;
if (sup_count > 1) {
extra_mech_size = sizeof (crypto_mech_name_t) *
(sup_count - 1);
}
pload_soft_conf = malloc(sizeof (crypto_load_soft_config_t) +
extra_mech_size);
if (pload_soft_conf == NULL) {
cryptodebug("out of memory.");
return (NULL);
}
(void) strlcpy(pload_soft_conf->sc_name, pent->name, MAXNAMELEN);
pload_soft_conf->sc_count = sup_count;
i = 0;
plist = pent->suplist;
while (i < sup_count) {
(void) strlcpy(pload_soft_conf->sc_list[i++],
plist->name, CRYPTO_MAX_MECH_NAME);
plist = plist->next;
}
return (pload_soft_conf);
}
/*
* Prepare the argument for the LOAD_SOFT_DISABLED ioctl call for the
* provider pointed by pent. Return NULL if out of memory.
*/
crypto_load_soft_disabled_t *
setup_soft_dis(entry_t *pent)
{
crypto_load_soft_disabled_t *pload_soft_dis = NULL;
mechlist_t *plist = NULL;
size_t extra_mech_size = 0;
uint_t dis_count;
int i;
if (pent == NULL) {
return (NULL);
}
dis_count = pent->dis_count;
if (dis_count > 1) {
extra_mech_size = sizeof (crypto_mech_name_t) *
(dis_count - 1);
}
pload_soft_dis = malloc(sizeof (crypto_load_soft_disabled_t) +
extra_mech_size);
if (pload_soft_dis == NULL) {
cryptodebug("out of memory.");
return (NULL);
}
(void) strlcpy(pload_soft_dis->sd_name, pent->name, MAXNAMELEN);
pload_soft_dis->sd_count = dis_count;
i = 0;
plist = pent->dislist;
while (i < dis_count) {
(void) strlcpy(pload_soft_dis->sd_list[i++],
plist->name, CRYPTO_MAX_MECH_NAME);
plist = plist->next;
}
return (pload_soft_dis);
}
/*
* Prepare the argument for the LOAD_DEV_DISABLED ioctl call for the
* provider pointed by pent. Return NULL if out of memory.
*/
crypto_load_dev_disabled_t *
setup_dev_dis(entry_t *pent)
{
crypto_load_dev_disabled_t *pload_dev_dis = NULL;
mechlist_t *plist = NULL;
size_t extra_mech_size = 0;
uint_t dis_count;
int i;
char pname[MAXNAMELEN];
int inst_num;
if (pent == NULL) {
return (NULL);
}
/* get the device name and the instance number */
if (split_hw_provname(pent->name, pname, &inst_num) == FAILURE) {
return (NULL);
}
/* allocate space for pload_dev_des */
dis_count = pent->dis_count;
if (dis_count > 1) {
extra_mech_size = sizeof (crypto_mech_name_t) *
(dis_count - 1);
}
pload_dev_dis = malloc(sizeof (crypto_load_dev_disabled_t) +
extra_mech_size);
if (pload_dev_dis == NULL) {
cryptodebug("out of memory.");
return (NULL);
}
/* set the values for pload_dev_dis */
(void) strlcpy(pload_dev_dis->dd_dev_name, pname, MAXNAMELEN);
pload_dev_dis->dd_dev_instance = inst_num;
pload_dev_dis->dd_count = dis_count;
i = 0;
plist = pent->dislist;
while (i < dis_count) {
(void) strlcpy(pload_dev_dis->dd_list[i++],
plist->name, CRYPTO_MAX_MECH_NAME);
plist = plist->next;
}
return (pload_dev_dis);
}
/*
* Prepare the calling argument of the UNLOAD_SOFT_MODULE ioctl call for the
* provider pointed by pent. Return NULL if out of memory.
*/
crypto_unload_soft_module_t *
setup_unload_soft(entry_t *pent)
{
crypto_unload_soft_module_t *punload_soft;
if (pent == NULL) {
return (NULL);
}
punload_soft = malloc(sizeof (crypto_unload_soft_module_t));
if (punload_soft == NULL) {
cryptodebug("out of memory.");
return (NULL);
}
(void) strlcpy(punload_soft->sm_name, pent->name, MAXNAMELEN);
return (punload_soft);
}
/*
* Prepare the calling argument for the GET_SOFT_INFO call for the provider
* with the number of mechanisms specified in the second argument.
*
* Called by get_soft_info().
*/
static crypto_get_soft_info_t *
setup_get_soft_info(char *provname, int count)
{
crypto_get_soft_info_t *psoft_info;
size_t extra_mech_size = 0;
if (provname == NULL) {
return (NULL);
}
if (count > 1) {
extra_mech_size = sizeof (crypto_mech_name_t) * (count - 1);
}
psoft_info = malloc(sizeof (crypto_get_soft_info_t) + extra_mech_size);
if (psoft_info == NULL) {
cryptodebug("out of memory.");
return (NULL);
}
(void) strlcpy(psoft_info->si_name, provname, MAXNAMELEN);
psoft_info->si_count = count;
return (psoft_info);
}
/*
* Get the device list from kernel.
*/
int
get_dev_list(crypto_get_dev_list_t **ppdevlist)
{
crypto_get_dev_list_t *pdevlist;
int fd = -1;
int count = DEFAULT_DEV_NUM;
pdevlist = malloc(sizeof (crypto_get_dev_list_t) +
sizeof (crypto_dev_list_entry_t) * (count - 1));
if (pdevlist == NULL) {
cryptodebug("out of memory.");
return (FAILURE);
}
if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
ADMIN_IOCTL_DEVICE, strerror(errno));
return (FAILURE);
}
pdevlist->dl_dev_count = count;
if (ioctl(fd, CRYPTO_GET_DEV_LIST, pdevlist) == -1) {
cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed: %s",
strerror(errno));
free(pdevlist);
(void) close(fd);
return (FAILURE);
}
/* BUFFER is too small, get the number of devices and retry it. */
if (pdevlist->dl_return_value == CRYPTO_BUFFER_TOO_SMALL) {
count = pdevlist->dl_dev_count;
free(pdevlist);
pdevlist = malloc(sizeof (crypto_get_dev_list_t) +
sizeof (crypto_dev_list_entry_t) * (count - 1));
if (pdevlist == NULL) {
cryptodebug("out of memory.");
(void) close(fd);
return (FAILURE);
}
if (ioctl(fd, CRYPTO_GET_DEV_LIST, pdevlist) == -1) {
cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed: %s",
strerror(errno));
free(pdevlist);
(void) close(fd);
return (FAILURE);
}
}
if (pdevlist->dl_return_value != CRYPTO_SUCCESS) {
cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed, "
"return_value = %d", pdevlist->dl_return_value);
free(pdevlist);
(void) close(fd);
return (FAILURE);
}
*ppdevlist = pdevlist;
(void) close(fd);
return (SUCCESS);
}
/*
* Get all the mechanisms supported by the hardware provider.
* The result will be stored in the second argument.
*/
int
get_dev_info(char *devname, int inst_num, int count, mechlist_t **ppmechlist)
{
crypto_get_dev_info_t *dev_info;
mechlist_t *phead;
mechlist_t *pcur;
mechlist_t *pmech;
int fd = -1;
int i;
int rc;
if (devname == NULL || count < 1) {
cryptodebug("get_dev_info(): devname is NULL or bogus count");
return (FAILURE);
}
/* Set up the argument for the CRYPTO_GET_DEV_INFO ioctl call */
dev_info = malloc(sizeof (crypto_get_dev_info_t) +
sizeof (crypto_mech_name_t) * (count - 1));
if (dev_info == NULL) {
cryptodebug("out of memory.");
return (FAILURE);
}
(void) strlcpy(dev_info->di_dev_name, devname, MAXNAMELEN);
dev_info->di_dev_instance = inst_num;
dev_info->di_count = count;
/* Open the ioctl device */
if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
ADMIN_IOCTL_DEVICE, strerror(errno));
free(dev_info);
return (FAILURE);
}
if (ioctl(fd, CRYPTO_GET_DEV_INFO, dev_info) == -1) {
cryptodebug("CRYPTO_GET_DEV_INFO ioctl failed: %s",
strerror(errno));
free(dev_info);
(void) close(fd);
return (FAILURE);
}
if (dev_info->di_return_value != CRYPTO_SUCCESS) {
cryptodebug("CRYPTO_GET_DEV_INFO ioctl failed, "
"return_value = %d", dev_info->di_return_value);
free(dev_info);
(void) close(fd);
return (FAILURE);
}
phead = pcur = NULL;
rc = SUCCESS;
for (i = 0; i < dev_info->di_count; i++) {
pmech = create_mech(&dev_info->di_list[i][0]);
if (pmech == NULL) {
rc = FAILURE;
break;
} else {
if (phead == NULL) {
phead = pcur = pmech;
} else {
pcur->next = pmech;
pcur = pmech;
}
}
}
if (rc == SUCCESS) {
*ppmechlist = phead;
} else {
free_mechlist(phead);
}
free(dev_info);
(void) close(fd);
return (rc);
}
/*
* Get the supported mechanism list of the software provider from kernel.
*
* Parameters phardlist and psoftlist are supplied by get_kcfconf_info().
* If NULL, this function calls get_kcfconf_info() internally.
*/
int
get_soft_info(char *provname, mechlist_t **ppmechlist,
entrylist_t *phardlist, entrylist_t *psoftlist)
{
boolean_t in_kernel = B_FALSE;
crypto_get_soft_info_t *psoft_info;
mechlist_t *phead;
mechlist_t *pmech;
mechlist_t *pcur;
entry_t *pent = NULL;
int count;
int fd = -1;
int rc;
int i;
if (provname == NULL) {
return (FAILURE);
}
if (getzoneid() == GLOBAL_ZONEID) {
/* use kcf.conf for kernel software providers in global zone */
if ((pent = getent_kef(provname, phardlist, psoftlist)) ==
NULL) {
/* No kcf.conf entry for this provider */
if (check_kernel_for_soft(provname, NULL, &in_kernel)
== FAILURE) {
return (FAILURE);
} else if (in_kernel == B_FALSE) {
cryptoerror(LOG_STDERR,
gettext("%s does not exist."), provname);
return (FAILURE);
}
/*
* Set mech count to 1. It will be reset to the
* correct value later if the setup buffer is too small.
*/
count = 1;
} else {
count = pent->sup_count;
free_entry(pent);
}
} else {
/*
* kcf.conf not there in non-global zone: set mech count to 1.
* It will be reset to the correct value later if the setup
* buffer is too small.
*/
count = 1;
}
if ((psoft_info = setup_get_soft_info(provname, count)) == NULL) {
return (FAILURE);
}
if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
ADMIN_IOCTL_DEVICE, strerror(errno));
free(psoft_info);
return (FAILURE);
}
/* make GET_SOFT_INFO ioctl call */
if ((rc = ioctl(fd, CRYPTO_GET_SOFT_INFO, psoft_info)) == -1) {
cryptodebug("CRYPTO_GET_SOFT_INFO ioctl failed: %s",
strerror(errno));
(void) close(fd);
free(psoft_info);
return (FAILURE);
}
/* BUFFER is too small, get the number of mechanisms and retry it. */
if (psoft_info->si_return_value == CRYPTO_BUFFER_TOO_SMALL) {
count = psoft_info->si_count;
free(psoft_info);
if ((psoft_info = setup_get_soft_info(provname, count))
== NULL) {
(void) close(fd);
return (FAILURE);
} else {
rc = ioctl(fd, CRYPTO_GET_SOFT_INFO, psoft_info);
if (rc == -1) {
cryptodebug("CRYPTO_GET_SOFT_INFO ioctl "
"failed: %s", strerror(errno));
(void) close(fd);
free(psoft_info);
return (FAILURE);
}
}
}
(void) close(fd);
if (psoft_info->si_return_value != CRYPTO_SUCCESS) {
cryptodebug("CRYPTO_GET_SOFT_INFO ioctl failed, "
"return_value = %d", psoft_info->si_return_value);
free(psoft_info);
return (FAILURE);
}
/* Build the mechanism linked list and return it */
rc = SUCCESS;
phead = pcur = NULL;
for (i = 0; i < psoft_info->si_count; i++) {
pmech = create_mech(&psoft_info->si_list[i][0]);
if (pmech == NULL) {
rc = FAILURE;
break;
} else {
if (phead == NULL) {
phead = pcur = pmech;
} else {
pcur->next = pmech;
pcur = pmech;
}
}
}
if (rc == FAILURE) {
free_mechlist(phead);
} else {
*ppmechlist = phead;
}
free(psoft_info);
return (rc);
}
/*
* Get the kernel software provider list from kernel.
*/
int
get_soft_list(crypto_get_soft_list_t **ppsoftlist)
{
crypto_get_soft_list_t *psoftlist = NULL;
int count = DEFAULT_SOFT_NUM;
int len;
int fd = -1;
if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
ADMIN_IOCTL_DEVICE, strerror(errno));
return (FAILURE);
}
len = MAXNAMELEN * count;
psoftlist = malloc(sizeof (crypto_get_soft_list_t) + len);
if (psoftlist == NULL) {
cryptodebug("out of memory.");
(void) close(fd);
return (FAILURE);
}
psoftlist->sl_soft_names = (caddr_t)(psoftlist + 1);
psoftlist->sl_soft_count = count;
psoftlist->sl_soft_len = len;
if (ioctl(fd, CRYPTO_GET_SOFT_LIST, psoftlist) == -1) {
cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed: %s",
strerror(errno));
free(psoftlist);
(void) close(fd);
return (FAILURE);
}
/*
* if BUFFER is too small, get the number of software providers and
* the minimum length needed for names and length and retry it.
*/
if (psoftlist->sl_return_value == CRYPTO_BUFFER_TOO_SMALL) {
count = psoftlist->sl_soft_count;
len = psoftlist->sl_soft_len;
free(psoftlist);
psoftlist = malloc(sizeof (crypto_get_soft_list_t) + len);
if (psoftlist == NULL) {
cryptodebug("out of memory.");
(void) close(fd);
return (FAILURE);
}
psoftlist->sl_soft_names = (caddr_t)(psoftlist + 1);
psoftlist->sl_soft_count = count;
psoftlist->sl_soft_len = len;
if (ioctl(fd, CRYPTO_GET_SOFT_LIST, psoftlist) == -1) {
cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed:"
"%s", strerror(errno));
free(psoftlist);
(void) close(fd);
return (FAILURE);
}
}
if (psoftlist->sl_return_value != CRYPTO_SUCCESS) {
cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed, "
"return_value = %d", psoftlist->sl_return_value);
free(psoftlist);
(void) close(fd);
return (FAILURE);
}
*ppsoftlist = psoftlist;
(void) close(fd);
return (SUCCESS);
}