/* * 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. */ #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("----------------------------")); }