adm_uef.c revision 12240b1daf1d29749412cd4091e50e29cda86615
/*
* 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.
*/
#include <cryptoutil.h>
#include <fcntl.h>
#include <libintl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <dlfcn.h>
#include <link.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <security/cryptoki.h>
#include "cryptoadm.h"
#define HDR1 " P\n"
#define HDR2 " S V K a U D\n"
#define HDR3 " i e e i n e\n"
#define HDR4 " S g V r y r W w r\n"
#define HDR5 " E D D i n e i G G r r i\n"
#define HDR6 " H n e i g + r + e e a a v E\n"
#define HDR7 "min max W c c g n R i R n n p p e C\n"
static int err; /* To store errno which may be overwritten by gettext() */
static boolean_t is_in_policylist(midstr_t, umechlist_t *);
static char *uent2str(uentry_t *);
static boolean_t check_random(CK_SLOT_ID, CK_FUNCTION_LIST_PTR);
static void display_slot_flags(CK_FLAGS flags)
{
(void) printf(gettext("Slot Flags: "));
if (flags & CKF_TOKEN_PRESENT)
(void) printf("CKF_TOKEN_PRESENT ");
if (flags & CKF_REMOVABLE_DEVICE)
(void) printf("CKF_REMOVABLE_DEVICE ");
if (flags & CKF_HW_SLOT)
(void) printf("CKF_HW_SLOT ");
(void) printf("\n");
}
void
display_token_flags(CK_FLAGS flags)
{
(void) printf(gettext("Flags: "));
if (flags & CKF_RNG)
(void) printf("CKF_RNG ");
if (flags & CKF_WRITE_PROTECTED)
(void) printf("CKF_WRITE_PROTECTED ");
if (flags & CKF_LOGIN_REQUIRED)
(void) printf("CKF_LOGIN_REQUIRED ");
if (flags & CKF_USER_PIN_INITIALIZED)
(void) printf("CKF_USER_PIN_INITIALIZED ");
if (flags & CKF_RESTORE_KEY_NOT_NEEDED)
(void) printf("CKF_RESTORE_KEY_NOT_NEEDED ");
if (flags & CKF_CLOCK_ON_TOKEN)
(void) printf("CKF_CLOCK_ON_TOKEN ");
if (flags & CKF_PROTECTED_AUTHENTICATION_PATH)
(void) printf("CKF_PROTECTED_AUTHENTICATION_PATH ");
if (flags & CKF_DUAL_CRYPTO_OPERATIONS)
(void) printf("CKF_DUAL_CRYPTO_OPERATIONS ");
if (flags & CKF_TOKEN_INITIALIZED)
(void) printf("CKF_TOKEN_INITIALIZED ");
if (flags & CKF_SECONDARY_AUTHENTICATION)
(void) printf("CKF_SECONDARY_AUTHENTICATION ");
if (flags & CKF_USER_PIN_COUNT_LOW)
(void) printf("CKF_USER_PIN_COUNT_LOW ");
if (flags & CKF_USER_PIN_FINAL_TRY)
(void) printf("CKF_USER_PIN_FINAL_TRY ");
if (flags & CKF_USER_PIN_LOCKED)
(void) printf("CKF_USER_PIN_LOCKED ");
if (flags & CKF_USER_PIN_TO_BE_CHANGED)
(void) printf("CKF_USER_PIN_TO_BE_CHANGED ");
if (flags & CKF_SO_PIN_COUNT_LOW)
(void) printf("CKF_SO_PIN_COUNT_LOW ");
if (flags & CKF_SO_PIN_FINAL_TRY)
(void) printf("CKF_SO_PIN_FINAL_TRY ");
if (flags & CKF_SO_PIN_LOCKED)
(void) printf("CKF_SO_PIN_LOCKED ");
if (flags & CKF_SO_PIN_TO_BE_CHANGED)
(void) printf("CKF_SO_PIN_TO_BE_CHANGED ");
if (flags & CKF_SO_PIN_TO_BE_CHANGED)
(void) printf("CKF_SO_PIN_TO_BE_CHANGED ");
(void) printf("\n");
}
void
display_mech_info(CK_MECHANISM_INFO *mechInfo)
{
CK_FLAGS ec_flags = CKF_EC_F_P | CKF_EC_F_2M | CKF_EC_ECPARAMETERS |
CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS | CKF_EC_COMPRESS;
(void) printf("%-4ld %-4ld ", mechInfo->ulMinKeySize,
mechInfo->ulMaxKeySize);
(void) printf("%s %s %s %s %s %s %s %s %s %s %s %s "
"%s %s",
(mechInfo->flags & CKF_HW) ? "X" : ".",
(mechInfo->flags & CKF_ENCRYPT) ? "X" : ".",
(mechInfo->flags & CKF_DECRYPT) ? "X" : ".",
(mechInfo->flags & CKF_DIGEST) ? "X" : ".",
(mechInfo->flags & CKF_SIGN) ? "X" : ".",
(mechInfo->flags & CKF_SIGN_RECOVER) ? "X" : ".",
(mechInfo->flags & CKF_VERIFY) ? "X" : ".",
(mechInfo->flags & CKF_VERIFY_RECOVER) ? "X" : ".",
(mechInfo->flags & CKF_GENERATE) ? "X" : ".",
(mechInfo->flags & CKF_GENERATE_KEY_PAIR) ? "X" : ".",
(mechInfo->flags & CKF_WRAP) ? "X" : ".",
(mechInfo->flags & CKF_UNWRAP) ? "X" : ".",
(mechInfo->flags & CKF_DERIVE) ? "X" : ".",
(mechInfo->flags & ec_flags) ? "X" : ".");
}
/*
* Converts the provided list of mechanism names in their string format to
* their corresponding PKCS#11 mechanism IDs.
*
* The list of mechanism names to be converted is provided in the
* "mlist" argument. The list of converted mechanism IDs is returned
* in the "pmech_list" argument.
*
* This function is called by list_metaslot_info() and
* list_mechlist_for_lib() functions.
*/
int
convert_mechlist(CK_MECHANISM_TYPE **pmech_list, CK_ULONG *mech_count,
mechlist_t *mlist)
{
int i, n = 0;
mechlist_t *p = mlist;
while (p != NULL) {
p = p->next;
n++;
}
*pmech_list = malloc(n * sizeof (CK_MECHANISM_TYPE));
if (pmech_list == NULL) {
cryptodebug("out of memory");
return (FAILURE);
}
p = mlist;
for (i = 0; i < n; i++) {
if (pkcs11_str2mech(p->name, &(*pmech_list[i])) != CKR_OK) {
free(*pmech_list);
return (FAILURE);
}
p = p->next;
}
*mech_count = n;
return (SUCCESS);
}
/*
* Display the mechanism list for a user-level library
*/
int
list_mechlist_for_lib(char *libname, mechlist_t *mlist,
flag_val_t *rng_flag, boolean_t no_warn,
boolean_t verbose, boolean_t show_mechs)
{
CK_RV rv = CKR_OK;
CK_RV (*Tmp_C_GetFunctionList)(CK_FUNCTION_LIST_PTR_PTR);
CK_FUNCTION_LIST_PTR prov_funcs; /* Provider's function list */
CK_SLOT_ID_PTR prov_slots = NULL; /* Provider's slot list */
CK_MECHANISM_TYPE_PTR pmech_list = NULL; /* mech list for a slot */
CK_SLOT_INFO slotinfo;
CK_ULONG slot_count;
CK_ULONG mech_count;
uentry_t *puent = NULL;
boolean_t lib_initialized = B_FALSE;
void *dldesc = NULL;
char *dl_error;
const char *mech_name;
char *isa;
char libpath[MAXPATHLEN];
char buf[MAXPATHLEN];
int i, j;
int rc = SUCCESS;
if (libname == NULL) {
/* should not happen */
cryptoerror(LOG_STDERR, gettext("internal error."));
cryptodebug("list_mechlist_for_lib() - libname is NULL.");
return (FAILURE);
}
/* Check if the library is in the pkcs11.conf file */
if ((puent = getent_uef(libname)) == NULL) {
cryptoerror(LOG_STDERR,
gettext("%s does not exist."), libname);
return (FAILURE);
}
free_uentry(puent);
/* Remove $ISA from the library name */
if (strlcpy(buf, libname, sizeof (buf)) >= sizeof (buf)) {
(void) printf(gettext("%s: the provider name is too long."),
libname);
return (FAILURE);
}
if ((isa = strstr(buf, PKCS11_ISA)) != NULL) {
*isa = '\000';
isa += strlen(PKCS11_ISA);
(void) snprintf(libpath, MAXPATHLEN, "%s%s%s", buf, "/", isa);
} else {
(void) strlcpy(libpath, libname, sizeof (libpath));
}
/* Open the provider */
dldesc = dlopen(libpath, RTLD_NOW);
if (dldesc == NULL) {
dl_error = dlerror();
cryptodebug("Cannot load PKCS#11 library %s. dlerror: %s",
libname, dl_error != NULL ? dl_error : "Unknown");
rc = FAILURE;
goto clean_exit;
}
/* Get the pointer to provider's C_GetFunctionList() */
Tmp_C_GetFunctionList = (CK_RV(*)())dlsym(dldesc, "C_GetFunctionList");
if (Tmp_C_GetFunctionList == NULL) {
cryptodebug("Cannot get the address of the C_GetFunctionList "
"from %s", libname);
rc = FAILURE;
goto clean_exit;
}
/* Get the provider's function list */
rv = Tmp_C_GetFunctionList(&prov_funcs);
if (rv != CKR_OK) {
cryptodebug("failed to call C_GetFunctionList from %s",
libname);
rc = FAILURE;
goto clean_exit;
}
/* Initialize this provider */
rv = prov_funcs->C_Initialize(NULL_PTR);
if (rv != CKR_OK) {
cryptodebug("failed to call C_Initialize from %s, "
"return code = %d", libname, rv);
rc = FAILURE;
goto clean_exit;
} else {
lib_initialized = B_TRUE;
}
/*
* Find out how many slots this provider has, call with tokenPresent
* set to FALSE so all potential slots are returned.
*/
rv = prov_funcs->C_GetSlotList(FALSE, NULL_PTR, &slot_count);
if (rv != CKR_OK) {
cryptodebug("failed to get the slotlist from %s.", libname);
rc = FAILURE;
goto clean_exit;
} else if (slot_count == 0) {
if (!no_warn)
(void) printf(gettext("%s: no slots presented.\n"),
libname);
rc = SUCCESS;
goto clean_exit;
}
/* Allocate memory for the slot list */
prov_slots = malloc(slot_count * sizeof (CK_SLOT_ID));
if (prov_slots == NULL) {
cryptodebug("out of memory.");
rc = FAILURE;
goto clean_exit;
}
/* Get the slot list from provider */
rv = prov_funcs->C_GetSlotList(FALSE, prov_slots, &slot_count);
if (rv != CKR_OK) {
cryptodebug("failed to call C_GetSlotList() from %s.",
libname);
rc = FAILURE;
goto clean_exit;
}
if (verbose) {
(void) printf(gettext("Number of slots: %d\n"), slot_count);
}
/* Get the mechanism list for each slot */
for (i = 0; i < slot_count; i++) {
if (verbose)
/*
* TRANSLATION_NOTE
* In some languages, the # symbol should be
* converted to "no", an "n" followed by a
* superscript "o"..
*/
(void) printf(gettext("\nSlot #%d\n"), i+1);
if ((rng_flag != NULL) && (*rng_flag == NO_RNG)) {
if (check_random(prov_slots[i], prov_funcs)) {
*rng_flag = HAS_RNG;
rc = SUCCESS;
goto clean_exit;
} else
continue;
}
rv = prov_funcs->C_GetSlotInfo(prov_slots[i], &slotinfo);
if (rv != CKR_OK) {
cryptodebug("failed to get slotinfo from %s", libname);
rc = FAILURE;
break;
}
if (verbose) {
CK_TOKEN_INFO tokeninfo;
(void) printf(gettext("Description: %.64s\n"
"Manufacturer: %.32s\n"
"PKCS#11 Version: %d.%d\n"),
slotinfo.slotDescription,
slotinfo.manufacturerID,
prov_funcs->version.major,
prov_funcs->version.minor);
(void) printf(gettext("Hardware Version: %d.%d\n"
"Firmware Version: %d.%d\n"),
slotinfo.hardwareVersion.major,
slotinfo.hardwareVersion.minor,
slotinfo.firmwareVersion.major,
slotinfo.firmwareVersion.minor);
(void) printf(gettext("Token Present: %s\n"),
(slotinfo.flags & CKF_TOKEN_PRESENT ?
gettext("True") : gettext("False")));
display_slot_flags(slotinfo.flags);
rv = prov_funcs->C_GetTokenInfo(prov_slots[i],
&tokeninfo);
if (rv != CKR_OK) {
cryptodebug("Failed to get "
"token info from %s", libname);
rc = FAILURE;
break;
}
(void) printf(gettext("Token Label: %.32s\n"
"Manufacturer ID: %.32s\n"
"Model: %.16s\n"
"Serial Number: %.16s\n"
"Hardware Version: %d.%d\n"
"Firmware Version: %d.%d\n"
"UTC Time: %.16s\n"
"PIN Min Length: %d\n"
"PIN Max Length: %d\n"),
tokeninfo.label,
tokeninfo.manufacturerID,
tokeninfo.model,
tokeninfo.serialNumber,
tokeninfo.hardwareVersion.major,
tokeninfo.hardwareVersion.minor,
tokeninfo.firmwareVersion.major,
tokeninfo.firmwareVersion.minor,
tokeninfo.utcTime,
tokeninfo.ulMinPinLen,
tokeninfo.ulMaxPinLen);
display_token_flags(tokeninfo.flags);
}
if (mlist == NULL) {
rv = prov_funcs->C_GetMechanismList(prov_slots[i],
NULL_PTR, &mech_count);
if (rv != CKR_OK) {
cryptodebug(
"failed to call C_GetMechanismList() "
"from %s.", libname);
rc = FAILURE;
break;
}
if (mech_count == 0) {
/* no mechanisms in this slot */
continue;
}
pmech_list = malloc(mech_count *
sizeof (CK_MECHANISM_TYPE));
if (pmech_list == NULL) {
cryptodebug("out of memory");
rc = FAILURE;
break;
}
/* Get the actual mechanism list */
rv = prov_funcs->C_GetMechanismList(prov_slots[i],
pmech_list, &mech_count);
if (rv != CKR_OK) {
cryptodebug(
"failed to call C_GetMechanismList() "
"from %s.", libname);
free(pmech_list);
rc = FAILURE;
break;
}
} else {
/* use the mechanism list passed in */
rc = convert_mechlist(&pmech_list, &mech_count, mlist);
if (rc != SUCCESS) {
goto clean_exit;
}
}
if (show_mechs)
(void) printf(gettext("Mechanisms:\n"));
if (verbose && show_mechs) {
display_verbose_mech_header();
}
/*
* Merge the current mechanism list into the returning
* mechanism list.
*/
for (j = 0; show_mechs && j < mech_count; j++) {
CK_MECHANISM_TYPE mech = pmech_list[j];
CK_MECHANISM_INFO mech_info;
rv = prov_funcs->C_GetMechanismInfo(
prov_slots[i], mech, &mech_info);
if (rv != CKR_OK) {
cryptodebug(
"failed to call "
"C_GetMechanismInfo() from %s.",
libname);
free(pmech_list);
pmech_list = NULL;
rc = FAILURE;
break;
}
if (mech >= CKM_VENDOR_DEFINED) {
(void) printf("%#lx", mech);
} else {
mech_name = pkcs11_mech2str(mech);
(void) printf("%-29s", mech_name);
}
if (verbose) {
display_mech_info(&mech_info);
}
(void) printf("\n");
}
if (pmech_list)
free(pmech_list);
if (rc == FAILURE) {
break;
}
}
if (rng_flag != NULL || rc == FAILURE) {
goto clean_exit;
}
clean_exit:
if (rc == FAILURE) {
(void) printf(gettext(
"%s: failed to retrieve the mechanism list.\n"), libname);
}
if (lib_initialized) {
(void) prov_funcs->C_Finalize(NULL_PTR);
}
if (dldesc != NULL) {
(void) dlclose(dldesc);
}
if (prov_slots != NULL) {
free(prov_slots);
}
return (rc);
}
/*
* Display the mechanism policy for a user-level library
*/
int
list_policy_for_lib(char *libname)
{
uentry_t *puent = NULL;
int rc;
if (libname == NULL) {
/* should not happen */
cryptoerror(LOG_STDERR, gettext("internal error."));
cryptodebug("list_policy_for_lib() - libname is NULL.");
return (FAILURE);
}
/* Get the library entry from the pkcs11.conf file */
if ((puent = getent_uef(libname)) == NULL) {
cryptoerror(LOG_STDERR,
gettext("%s does not exist."), libname);
return (FAILURE);
}
/* Print the policy for this library */
rc = print_uef_policy(puent);
free_uentry(puent);
return (rc);
}
/*
* Disable mechanisms for a user-level library
*/
int
disable_uef_lib(char *libname, boolean_t rndflag, boolean_t allflag,
mechlist_t *marglist)
{
uentry_t *puent;
int rc;
if (libname == NULL) {
/* should not happen */
cryptoerror(LOG_STDERR, gettext("internal error."));
cryptodebug("disable_uef_lib() - libname is NULL.");
return (FAILURE);
}
/* Get the provider entry from the pkcs11.conf file */
if ((puent = getent_uef(libname)) == NULL) {
cryptoerror(LOG_STDERR,
gettext("%s does not exist."), libname);
return (FAILURE);
}
/*
* Update the mechanism policy of this library entry, based on
* the current policy mode of the library and the mechanisms specified
* in CLI.
*/
if (allflag) {
/*
* If disabling all, just need to clean up the policylist and
* set the flag_enabledlist flag to be B_TRUE.
*/
free_umechlist(puent->policylist);
puent->policylist = NULL;
puent->count = 0;
puent->flag_enabledlist = B_TRUE;
rc = SUCCESS;
} else if (marglist != NULL) {
if (puent->flag_enabledlist == B_TRUE) {
/*
* The current default policy mode of this library
* is "all are disabled, except ...", so if a
* specified mechanism is in the exception list
* (the policylist), delete it from the policylist.
*/
rc = update_policylist(puent, marglist, DELETE_MODE);
} else {
/*
* The current default policy mode of this library
* is "all are enabled", so if a specified mechanism
* is not in the exception list (policylist), add
* it into the policylist.
*/
rc = update_policylist(puent, marglist, ADD_MODE);
}
} else if (!rndflag) {
/* should not happen */
cryptoerror(LOG_STDERR, gettext("internal error."));
cryptodebug("disable_uef_lib() - wrong arguments.");
return (FAILURE);
}
if (rndflag)
puent->flag_norandom = B_TRUE;
if (rc == FAILURE) {
free_uentry(puent);
return (FAILURE);
}
/* Update the pkcs11.conf file with the updated entry */
rc = update_pkcs11conf(puent);
free_uentry(puent);
return (rc);
}
/*
* Enable disabled mechanisms for a user-level library.
*/
int
enable_uef_lib(char *libname, boolean_t rndflag, boolean_t allflag,
mechlist_t *marglist)
{
uentry_t *puent;
int rc = SUCCESS;
if (libname == NULL) {
/* should not happen */
cryptoerror(LOG_STDERR, gettext("internal error."));
cryptodebug("enable_uef_lib() - libname is NULL.");
return (FAILURE);
}
/* Get the provider entry from the pkcs11.conf file */
if ((puent = getent_uef(libname)) == NULL) {
cryptoerror(LOG_STDERR,
gettext("%s does not exist."), libname);
return (FAILURE);
}
/*
* Update the mechanism policy of this library entry, based on
* the current policy mode of the library and the mechanisms
* specified in CLI.
*/
if (allflag) {
/*
* If enabling all, what needs to be done are cleaning up the
* policylist and setting the "flag_enabledlist" flag to
* B_FALSE.
*/
free_umechlist(puent->policylist);
puent->policylist = NULL;
puent->count = 0;
puent->flag_enabledlist = B_FALSE;
rc = SUCCESS;
} else if (marglist != NULL) {
if (puent->flag_enabledlist == B_TRUE) {
/*
* The current default policy mode of this library
* is "all are disabled, except ...", so if a
* specified mechanism is not in the exception list
* (policylist), add it.
*/
rc = update_policylist(puent, marglist, ADD_MODE);
} else {
/*
* The current default policy mode of this library
* is "all are enabled, except", so if a specified
* mechanism is in the exception list (policylist),
* delete it.
*/
rc = update_policylist(puent, marglist, DELETE_MODE);
}
} else if (!rndflag) {
/* should not come here */
cryptoerror(LOG_STDERR, gettext("internal error."));
cryptodebug("enable_uef_lib() - wrong arguments.");
return (FAILURE);
}
if (rndflag)
puent->flag_norandom = B_FALSE;
if (rc == FAILURE) {
free_uentry(puent);
return (FAILURE);
}
/* Update the pkcs11.conf file with the updated entry */
rc = update_pkcs11conf(puent);
free_uentry(puent);
return (rc);
}
/*
* Install a user-level library.
*/
int
install_uef_lib(char *libname)
{
uentry_t *puent;
struct stat statbuf;
char libpath[MAXPATHLEN];
char libbuf[MAXPATHLEN];
char *isa;
if (libname == NULL) {
/* should not happen */
cryptoerror(LOG_STDERR, gettext("internal error."));
cryptodebug("install_uef_lib() - libname is NULL.");
return (FAILURE);
}
/* Check if the provider already exists in the framework */
if ((puent = getent_uef(libname)) != NULL) {
cryptoerror(LOG_STDERR, gettext("%s exists already."),
libname);
free_uentry(puent);
return (FAILURE);
}
/*
* Check if the library exists in the system. if $ISA is in the
* path, only check the 32bit version.
*/
if (strlcpy(libbuf, libname, MAXPATHLEN) >= MAXPATHLEN) {
cryptoerror(LOG_STDERR,
gettext("the provider name is too long - %s"), libname);
return (FAILURE);
}
if ((isa = strstr(libbuf, PKCS11_ISA)) != NULL) {
*isa = '\000';
isa += strlen(PKCS11_ISA);
(void) snprintf(libpath, sizeof (libpath), "%s%s%s", libbuf,
"/", isa);
} else {
(void) strlcpy(libpath, libname, sizeof (libpath));
}
/* Check if it is same as the framework library */
if (strcmp(libpath, UEF_FRAME_LIB) == 0) {
cryptoerror(LOG_STDERR, gettext(
"The framework library %s can not be installed."),
libname);
return (FAILURE);
}
if (stat(libpath, &statbuf) != 0) {
cryptoerror(LOG_STDERR, gettext("%s not found"), libname);
return (FAILURE);
}
/* Need to add "\n" to libname for adding into the config file */
if (strlcat(libname, "\n", MAXPATHLEN) >= MAXPATHLEN) {
cryptoerror(LOG_STDERR, gettext(
"can not install %s; the name is too long."), libname);
return (FAILURE);
}
return (update_conf(_PATH_PKCS11_CONF, libname));
}
/*
* Uninstall a user-level library.
*/
int
uninstall_uef_lib(char *libname)
{
uentry_t *puent;
FILE *pfile;
FILE *pfile_tmp;
char buffer[BUFSIZ];
char buffer2[BUFSIZ];
char tmpfile_name[MAXPATHLEN];
char *name;
boolean_t found;
boolean_t in_package;
int len;
int rc = SUCCESS;
if (libname == NULL) {
/* should not happen */
cryptoerror(LOG_STDERR, gettext("internal error."));
cryptodebug("uninstall_uef_lib() - libname is NULL.");
return (FAILURE);
}
/* Check if the provider exists */
if ((puent = getent_uef(libname)) == NULL) {
cryptoerror(LOG_STDERR,
gettext("%s does not exist."), libname);
return (FAILURE);
}
free_uentry(puent);
/* Open the pkcs11.conf file and lock it */
if ((pfile = fopen(_PATH_PKCS11_CONF, "r+")) == NULL) {
err = errno;
cryptoerror(LOG_STDERR,
gettext("failed to update the configuration - %s"),
strerror(err));
cryptodebug("failed to open %s for write.", _PATH_PKCS11_CONF);
return (FAILURE);
}
if (lockf(fileno(pfile), F_TLOCK, 0) == -1) {
err = errno;
cryptoerror(LOG_STDERR,
gettext("failed to lock the configuration - %s"),
strerror(err));
(void) fclose(pfile);
return (FAILURE);
}
/*
* Create a temporary file in the /etc/crypto directory to save
* the new configuration file first.
*/
(void) strlcpy(tmpfile_name, TMPFILE_TEMPLATE, sizeof (tmpfile_name));
if (mkstemp(tmpfile_name) == -1) {
err = errno;
cryptoerror(LOG_STDERR,
gettext("failed to create a temporary file - %s"),
strerror(err));
(void) fclose(pfile);
return (FAILURE);
}
if ((pfile_tmp = fopen(tmpfile_name, "w")) == NULL) {
err = errno;
cryptoerror(LOG_STDERR, gettext("failed to open %s - %s"),
tmpfile_name, strerror(err));
if (unlink(tmpfile_name) != 0) {
err = errno;
cryptoerror(LOG_STDERR, gettext(
"(Warning) failed to remove %s: %s"),
tmpfile_name, strerror(err));
}
(void) fclose(pfile);
return (FAILURE);
}
/*
* Loop thru the config file. If the library to be uninstalled
* is in a package, just comment it off.
*/
in_package = B_FALSE;
while (fgets(buffer, BUFSIZ, pfile) != NULL) {
found = B_FALSE;
if (!(buffer[0] == ' ' || buffer[0] == '\n' ||
buffer[0] == '\t')) {
if (strstr(buffer, " Start ") != NULL) {
in_package = B_TRUE;
} else if (strstr(buffer, " End ") != NULL) {
in_package = B_FALSE;
} else if (buffer[0] != '#') {
(void) strlcpy(buffer2, buffer, BUFSIZ);
/* get rid of trailing '\n' */
len = strlen(buffer2);
if (buffer2[len-1] == '\n') {
len--;
}
buffer2[len] = '\0';
if ((name = strtok(buffer2, SEP_COLON))
== NULL) {
rc = FAILURE;
break;
} else if (strcmp(libname, name) == 0) {
found = B_TRUE;
}
}
}
if (found) {
if (in_package) {
(void) snprintf(buffer2, sizeof (buffer2),
"%s%s%s", "#", libname, "\n");
if (fputs(buffer2, pfile_tmp) == EOF) {
rc = FAILURE;
}
}
} else {
if (fputs(buffer, pfile_tmp) == EOF) {
rc = FAILURE;
}
}
if (rc == FAILURE) {
break;
}
}
if (rc == FAILURE) {
cryptoerror(LOG_STDERR, gettext("write error."));
(void) fclose(pfile);
(void) fclose(pfile_tmp);
if (unlink(tmpfile_name) != 0) {
err = errno;
cryptoerror(LOG_STDERR, gettext(
"(Warning) failed to remove %s: %s"),
tmpfile_name, strerror(err));
}
return (FAILURE);
}
(void) fclose(pfile);
if (fclose(pfile_tmp) != 0) {
err = errno;
cryptoerror(LOG_STDERR,
gettext("failed to close a temporary file - %s"),
strerror(err));
return (FAILURE);
}
/* Now update the real config file */
if (rename(tmpfile_name, _PATH_PKCS11_CONF) == -1) {
err = errno;
cryptoerror(LOG_STDERR,
gettext("failed to update the configuration - %s"),
strerror(err));
cryptodebug("failed to rename %s to %s: %s", tmpfile,
_PATH_PKCS11_CONF, strerror(err));
rc = FAILURE;
} else if (chmod(_PATH_PKCS11_CONF,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
err = errno;
cryptoerror(LOG_STDERR,
gettext("failed to update the configuration - %s"),
strerror(err));
cryptodebug("failed to chmod to %s: %s", _PATH_PKCS11_CONF,
strerror(err));
rc = FAILURE;
} else {
rc = SUCCESS;
}
if ((rc == FAILURE) && (unlink(tmpfile_name) != 0)) {
err = errno;
cryptoerror(LOG_STDERR, gettext(
"(Warning) failed to remove %s: %s"),
tmpfile_name, strerror(err));
}
return (rc);
}
int
display_policy(uentry_t *puent)
{
CK_MECHANISM_TYPE mech_id;
const char *mech_name;
umechlist_t *ptr;
if (puent == NULL) {
return (SUCCESS);
}
if (puent->flag_enabledlist == B_FALSE) {
(void) printf(gettext("%s: all mechanisms are enabled"),
puent->name);
ptr = puent->policylist;
if (ptr == NULL) {
(void) printf(".");
} else {
(void) printf(gettext(", except "));
while (ptr != NULL) {
mech_id = strtoul(ptr->name, NULL, 0);
if (mech_id & CKO_VENDOR_DEFINED) {
/* vendor defined mechanism */
(void) printf("%s", ptr->name);
} else {
if (mech_id >= CKM_VENDOR_DEFINED) {
(void) printf("%#lx", mech_id);
} else {
mech_name = pkcs11_mech2str(
mech_id);
if (mech_name == NULL) {
return (FAILURE);
}
(void) printf("%s", mech_name);
}
}
ptr = ptr->next;
if (ptr == NULL) {
(void) printf(".");
} else {
(void) printf(",");
}
}
}
} else { /* puent->flag_enabledlist == B_TRUE */
(void) printf(gettext("%s: all mechanisms are disabled"),
puent->name);
ptr = puent->policylist;
if (ptr == NULL) {
(void) printf(".");
} else {
(void) printf(gettext(", except "));
while (ptr != NULL) {
mech_id = strtoul(ptr->name, NULL, 0);
if (mech_id & CKO_VENDOR_DEFINED) {
/* vendor defined mechanism */
(void) printf("%s", ptr->name);
} else {
mech_name = pkcs11_mech2str(mech_id);
if (mech_name == NULL) {
return (FAILURE);
}
(void) printf("%s", mech_name);
}
ptr = ptr->next;
if (ptr == NULL) {
(void) printf(".");
} else {
(void) printf(",");
}
}
}
}
return (SUCCESS);
}
/*
* Print out the mechanism policy for a user-level provider pointed by puent.
*/
int
print_uef_policy(uentry_t *puent)
{
flag_val_t rng_flag;
if (puent == NULL) {
return (FAILURE);
}
rng_flag = NO_RNG;
if (list_mechlist_for_lib(puent->name, NULL, &rng_flag, B_TRUE,
B_FALSE, B_FALSE) != SUCCESS) {
cryptoerror(LOG_STDERR,
gettext("%s internal error."), puent->name);
return (FAILURE);
}
if (display_policy(puent) != SUCCESS) {
goto failed_exit;
}
if (puent->flag_norandom == B_TRUE)
/*
* TRANSLATION_NOTE
* "random" is a keyword and not to be translated.
*/
(void) printf(gettext(" %s is disabled."), "random");
else {
if (rng_flag == HAS_RNG)
/*
* TRANSLATION_NOTE
* "random" is a keyword and not to be translated.
*/
(void) printf(gettext(" %s is enabled."), "random");
}
(void) printf("\n");
return (SUCCESS);
failed_exit:
(void) printf(gettext("\nout of memory.\n"));
return (FAILURE);
}
/*
* Check if the mechanism is in the mechanism list.
*/
static boolean_t
is_in_policylist(midstr_t mechname, umechlist_t *plist)
{
boolean_t found = B_FALSE;
if (mechname == NULL) {
return (B_FALSE);
}
while (plist != NULL) {
if (strcmp(plist->name, mechname) == 0) {
found = B_TRUE;
break;
}
plist = plist->next;
}
return (found);
}
/*
* Update the pkcs11.conf file with the updated entry.
*/
int
update_pkcs11conf(uentry_t *puent)
{
FILE *pfile;
FILE *pfile_tmp;
char buffer[BUFSIZ];
char buffer2[BUFSIZ];
char tmpfile_name[MAXPATHLEN];
char *name;
char *str;
int len;
int rc = SUCCESS;
boolean_t found;
if (puent == NULL) {
cryptoerror(LOG_STDERR, gettext("internal error."));
return (FAILURE);
}
/* Open the pkcs11.conf file */
if ((pfile = fopen(_PATH_PKCS11_CONF, "r+")) == NULL) {
err = errno;
cryptoerror(LOG_STDERR,
gettext("failed to update the configuration - %s"),
strerror(err));
cryptodebug("failed to open %s for write.", _PATH_PKCS11_CONF);
return (FAILURE);
}
/* Lock the pkcs11.conf file */
if (lockf(fileno(pfile), F_TLOCK, 0) == -1) {
err = errno;
cryptoerror(LOG_STDERR,
gettext("failed to update the configuration - %s"),
strerror(err));
(void) fclose(pfile);
return (FAILURE);
}
/*
* Create a temporary file in the /etc/crypto directory to save
* updated configuration file first.
*/
(void) strlcpy(tmpfile_name, TMPFILE_TEMPLATE, sizeof (tmpfile_name));
if (mkstemp(tmpfile_name) == -1) {
err = errno;
cryptoerror(LOG_STDERR,
gettext("failed to create a temporary file - %s"),
strerror(err));
(void) fclose(pfile);
return (FAILURE);
}
if ((pfile_tmp = fopen(tmpfile_name, "w")) == NULL) {
err = errno;
cryptoerror(LOG_STDERR, gettext("failed to open %s - %s"),
tmpfile_name, strerror(err));
if (unlink(tmpfile_name) != 0) {
err = errno;
cryptoerror(LOG_STDERR, gettext(
"(Warning) failed to remove %s: %s"),
tmpfile_name, strerror(err));
}
(void) fclose(pfile);
return (FAILURE);
}
/*
* Loop thru entire pkcs11.conf file, update the entry to be
* updated and save the updated file to the temporary file first.
*/
while (fgets(buffer, BUFSIZ, pfile) != NULL) {
found = B_FALSE;
if (!(buffer[0] == '#' || buffer[0] == ' ' ||
buffer[0] == '\n'|| buffer[0] == '\t')) {
/*
* Get the provider name from this line and check if
* this is the entry to be updated. Note: can not use
* "buffer" directly because strtok will change its
* value.
*/
(void) strlcpy(buffer2, buffer, BUFSIZ);
/* get rid of trailing '\n' */
len = strlen(buffer2);
if (buffer2[len-1] == '\n') {
len--;
}
buffer2[len] = '\0';
if ((name = strtok(buffer2, SEP_COLON)) == NULL) {
rc = FAILURE;
break;
} else if (strcmp(puent->name, name) == 0) {
found = B_TRUE;
}
}
if (found) {
/*
* This is the entry to be modified, get the updated
* string.
*/
if ((str = uent2str(puent)) == NULL) {
rc = FAILURE;
break;
} else {
(void) strlcpy(buffer, str, BUFSIZ);
free(str);
}
}
if (fputs(buffer, pfile_tmp) == EOF) {
err = errno;
cryptoerror(LOG_STDERR, gettext(
"failed to write to a temp file: %s."),
strerror(err));
rc = FAILURE;
break;
}
}
if (rc == FAILURE) {
(void) fclose(pfile);
(void) fclose(pfile_tmp);
if (unlink(tmpfile_name) != 0) {
err = errno;
cryptoerror(LOG_STDERR, gettext(
"(Warning) failed to remove %s: %s"),
tmpfile_name, strerror(err));
}
return (FAILURE);
}
(void) fclose(pfile);
if (fclose(pfile_tmp) != 0) {
err = errno;
cryptoerror(LOG_STDERR,
gettext("failed to close %s: %s"), tmpfile_name,
strerror(err));
return (FAILURE);
}
/* Copy the temporary file to the pkcs11.conf file */
if (rename(tmpfile_name, _PATH_PKCS11_CONF) == -1) {
err = errno;
cryptoerror(LOG_STDERR,
gettext("failed to update the configuration - %s"),
strerror(err));
cryptodebug("failed to rename %s to %s: %s", tmpfile_name,
_PATH_PKCS11_CONF, strerror(err));
rc = FAILURE;
} else if (chmod(_PATH_PKCS11_CONF,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
err = errno;
cryptoerror(LOG_STDERR,
gettext("failed to update the configuration - %s"),
strerror(err));
cryptodebug("failed to chmod to %s: %s", _PATH_PKCS11_CONF,
strerror(err));
rc = FAILURE;
} else {
rc = SUCCESS;
}
if ((rc == FAILURE) && (unlink(tmpfile_name) != 0)) {
err = errno;
cryptoerror(LOG_STDERR, gettext(
"(Warning) failed to remove %s: %s"),
tmpfile_name, strerror(err));
}
return (rc);
}
/*
* Convert an uentry to a character string
*/
static char *
uent2str(uentry_t *puent)
{
umechlist_t *phead;
boolean_t tok1_present = B_FALSE;
char *buf;
char blank_buf[128];
if (puent == NULL) {
cryptoerror(LOG_STDERR, gettext("internal error."));
return (NULL);
}
buf = malloc(BUFSIZ);
if (buf == NULL) {
cryptoerror(LOG_STDERR, gettext("out of memory."));
return (NULL);
}
/* convert the library name */
if (strlcpy(buf, puent->name, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
/* convert the enabledlist or the disabledlist */
if (puent->flag_enabledlist == B_TRUE) {
if (strlcat(buf, SEP_COLON, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
if (strlcat(buf, EF_ENABLED, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
phead = puent->policylist;
while (phead != NULL) {
if (strlcat(buf, phead->name, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
phead = phead->next;
if (phead != NULL) {
if (strlcat(buf, SEP_COMMA, BUFSIZ)
>= BUFSIZ) {
free(buf);
return (NULL);
}
}
}
tok1_present = B_TRUE;
} else if (puent->policylist != NULL) {
if (strlcat(buf, SEP_COLON, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
if (strlcat(buf, EF_DISABLED, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
phead = puent->policylist;
while (phead != NULL) {
if (strlcat(buf, phead->name, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
phead = phead->next;
if (phead != NULL) {
if (strlcat(buf, SEP_COMMA, BUFSIZ)
>= BUFSIZ) {
free(buf);
return (NULL);
}
}
}
tok1_present = B_TRUE;
}
if (puent->flag_norandom == B_TRUE) {
if (strlcat(buf, (tok1_present ? SEP_SEMICOLON : SEP_COLON),
BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
if (strlcat(buf, EF_NORANDOM, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
}
if (strcmp(puent->name, METASLOT_KEYWORD) == 0) {
/* write the metaslot_status= value */
if (strlcat(buf, (tok1_present ? SEP_SEMICOLON : SEP_COLON),
BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
if (strlcat(buf, METASLOT_STATUS, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
if (puent->flag_metaslot_enabled) {
if (strlcat(buf, ENABLED_KEYWORD, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
} else {
if (strlcat(buf, DISABLED_KEYWORD, BUFSIZ)
>= BUFSIZ) {
free(buf);
return (NULL);
}
}
if (!tok1_present) {
tok1_present = B_TRUE;
}
if (strlcat(buf, SEP_SEMICOLON, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
if (strlcat(buf, METASLOT_AUTO_KEY_MIGRATE, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
if (puent->flag_metaslot_auto_key_migrate) {
if (strlcat(buf, ENABLED_KEYWORD, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
} else {
if (strlcat(buf, DISABLED_KEYWORD, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
}
bzero(blank_buf, sizeof (blank_buf));
/* write metaslot_token= if specified */
if (memcmp(puent->metaslot_ks_token, blank_buf,
TOKEN_LABEL_SIZE) != 0) {
/* write the metaslot_status= value */
if (strlcat(buf, (tok1_present ?
SEP_SEMICOLON : SEP_COLON), BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
if (strlcat(buf, METASLOT_TOKEN, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
if (strlcat(buf,
(const char *)puent->metaslot_ks_token, BUFSIZ)
>= BUFSIZ) {
free(buf);
return (NULL);
}
}
/* write metaslot_slot= if specified */
if (memcmp(puent->metaslot_ks_slot, blank_buf,
SLOT_DESCRIPTION_SIZE) != 0) {
/* write the metaslot_status= value */
if (strlcat(buf, (tok1_present ?
SEP_SEMICOLON : SEP_COLON), BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
if (strlcat(buf, METASLOT_SLOT, BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
if (strlcat(buf,
(const char *)puent->metaslot_ks_slot, BUFSIZ)
>= BUFSIZ) {
free(buf);
return (NULL);
}
}
}
if (strlcat(buf, "\n", BUFSIZ) >= BUFSIZ) {
free(buf);
return (NULL);
}
return (buf);
}
/*
* This function updates the default policy mode and the policy exception list
* for a user-level provider based on the mechanism specified in the disable
* or enable subcommand and the update mode. This function is called by the
* enable_uef_lib() or disable_uef_lib().
*/
int
update_policylist(uentry_t *puent, mechlist_t *marglist, int update_mode)
{
CK_MECHANISM_TYPE mech_type;
midstr_t midname;
umechlist_t *phead;
umechlist_t *pcur;
umechlist_t *pumech;
boolean_t found;
int rc = SUCCESS;
if ((puent == NULL) || (marglist == NULL)) {
/* should not happen */
cryptoerror(LOG_STDERR, gettext("internal error."));
cryptodebug("update_policylist()- puent or marglist is NULL.");
return (FAILURE);
}
if ((update_mode != ADD_MODE) && (update_mode != DELETE_MODE)) {
/* should not happen */
cryptoerror(LOG_STDERR, gettext("internal error."));
cryptodebug("update_policylist() - update_mode is incorrect.");
return (FAILURE);
}
/*
* For each mechanism operand, get its mechanism type first.
* If fails to get the mechanism type, the mechanism operand must be
* invalid, gives an warning and ignore it. Otherwise,
* - convert the mechanism type to the internal representation (hex)
* in the pkcs11.conf file
* - If update_mode == DELETE_MODE,
* If the mechanism is in the policy list, delete it.
* If the mechanism is not in the policy list, do nothing.
* - If update_mode == ADD_MODE,
* If the mechanism is not in the policy list, add it.
* If the mechanism is in the policy list already, do nothing.
*/
while (marglist) {
if (pkcs11_str2mech(marglist->name, &mech_type) != CKR_OK) {
/*
* This mechanism is not a valid PKCS11 mechanism,
* give warning and ignore it.
*/
cryptoerror(LOG_STDERR, gettext(
"(Warning) %s is not a valid PKCS#11 mechanism."),
marglist->name);
rc = FAILURE;
} else {
(void) snprintf(midname, sizeof (midname), "%#010x",
(int)mech_type);
if (update_mode == DELETE_MODE) {
found = B_FALSE;
phead = pcur = puent->policylist;
while (!found && pcur) {
if (strcmp(pcur->name, midname) == 0) {
found = B_TRUE;
} else {
phead = pcur;
pcur = pcur->next;
}
}
if (found) {
if (phead == pcur) {
puent->policylist =
puent->policylist->next;
free(pcur);
} else {
phead->next = pcur->next;
free(pcur);
}
puent->count--;
if (puent->count == 0) {
puent->policylist = NULL;
}
}
} else if (update_mode == ADD_MODE) {
if (!is_in_policylist(midname,
puent->policylist)) {
pumech = create_umech(midname);
if (pumech == NULL) {
rc = FAILURE;
break;
}
phead = puent->policylist;
puent->policylist = pumech;
pumech->next = phead;
puent->count++;
}
}
}
marglist = marglist->next;
}
return (rc);
}
/*
* Open a session to the given slot and check if we can do
* random numbers by asking for one byte.
*/
static boolean_t
check_random(CK_SLOT_ID slot_id, CK_FUNCTION_LIST_PTR prov_funcs)
{
CK_RV rv;
CK_SESSION_HANDLE hSession;
CK_BYTE test_byte;
CK_BYTE_PTR test_byte_ptr = &test_byte;
rv = prov_funcs->C_OpenSession(slot_id, CKF_SERIAL_SESSION,
NULL_PTR, NULL, &hSession);
if (rv != CKR_OK)
return (B_FALSE);
/* We care only about the return value */
rv = prov_funcs->C_GenerateRandom(hSession, test_byte_ptr,
sizeof (test_byte));
(void) prov_funcs->C_CloseSession(hSession);
/*
* These checks are purely to determine whether the slot can do
* random numbers. So, we don't check whether the routine
* succeeds. The reason we check for CKR_RANDOM_NO_RNG also is that
* this error effectively means CKR_FUNCTION_NOT_SUPPORTED.
*/
if (rv != CKR_FUNCTION_NOT_SUPPORTED && rv != CKR_RANDOM_NO_RNG)
return (B_TRUE);
else
return (B_FALSE);
}
void
display_verbose_mech_header()
{
(void) printf("%28s %s", " ", HDR1);
(void) printf("%28s %s", " ", HDR2);
(void) printf("%28s %s", " ", HDR3);
(void) printf("%28s %s", " ", HDR4);
(void) printf("%28s %s", " ", HDR5);
(void) printf("%28s %s", " ", HDR6);
(void) printf("%-28.28s %s", gettext("mechanism name"), HDR7);
/*
* TRANSLATION_NOTE
* Strictly for appearance's sake, the first header line should be
* as long as the length of the translated text above. The format
* lengths should all match too.
*/
(void) printf("%28s ---- ---- "
"- - - - - - - - - - - - - -\n",
gettext("----------------------------"));
}
int
fips_update_pkcs11conf(int action)
{
char *str;
if (action == FIPS140_ENABLE)
str = "fips-140:fips_status=enabled\n";
else
str = "fips-140:fips_status=disabled\n";
if (update_conf(_PATH_PKCS11_CONF, str) != SUCCESS)
return (FAILURE);
return (SUCCESS);
}
void
fips_status_pkcs11conf(int *status)
{
uentry_t *puent = NULL;
if ((puent = getent_uef(FIPS_KEYWORD)) == NULL) {
/*
* By default (no fips-140 entry), we assume fips-140
* mode is disabled.
*/
*status = CRYPTO_FIPS_MODE_DISABLED;
return;
}
if (puent->flag_fips_enabled)
*status = CRYPTO_FIPS_MODE_ENABLED;
else
*status = CRYPTO_FIPS_MODE_DISABLED;
return;
}