root/usr/src/cmd/cmd-crypto/cryptoadm/adm_uef.c
/*
 * 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 resrved.
 * Copyright (c) 2018, Joyent, Inc.
 */

#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. Use RTLD_NOW here, as a way to
         * catch any providers with incomplete symbols that
         * might otherwise cause problems during libpkcs11's
         * execution.
         */
        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("----------------------------"));
}