OpenSolaris_b135/cmd/cmd-crypto/cryptoadm/cryptoadm.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <locale.h>
#include <libgen.h>
#include <sys/types.h>
#include <zone.h>
#include <sys/crypto/ioctladmin.h>
#include <cryptoutil.h>
#include "cryptoadm.h"

#define	REQ_ARG_CNT	2

/* subcommand index */
enum subcommand_index {
	CRYPTO_LIST,
	CRYPTO_DISABLE,
	CRYPTO_ENABLE,
	CRYPTO_INSTALL,
	CRYPTO_UNINSTALL,
	CRYPTO_UNLOAD,
	CRYPTO_REFRESH,
	CRYPTO_START,
	CRYPTO_STOP,
	CRYPTO_HELP };

/*
 * TRANSLATION_NOTE
 * Command keywords are not to be translated.
 */
static char *cmd_table[] = {
	"list",
	"disable",
	"enable",
	"install",
	"uninstall",
	"unload",
	"refresh",
	"start",
	"stop",
	"--help" };

/* provider type */
enum provider_type_index {
	PROV_UEF_LIB,
	PROV_KEF_SOFT,
	PROV_KEF_HARD,
	METASLOT,
	PROV_BADNAME };

typedef struct {
	char cp_name[MAXPATHLEN];
	enum provider_type_index cp_type;
} cryptoadm_provider_t;

/*
 * TRANSLATION_NOTE
 * Operand keywords are not to be translated.
 */
static const char *KN_PROVIDER = "provider=";
static const char *KN_MECH = "mechanism=";
static const char *KN_ALL = "all";
static const char *KN_TOKEN = "token=";
static const char *KN_SLOT = "slot=";
static const char *KN_DEFAULT_KS = "default-keystore";
static const char *KN_AUTO_KEY_MIGRATE = "auto-key-migrate";

/* static variables */
static boolean_t	allflag = B_FALSE;
static boolean_t	rndflag = B_FALSE;
static mechlist_t	*mecharglist = NULL;

/* static functions */
static void usage(void);
static int get_provider_type(char *);
static int process_mech_operands(int, char **, boolean_t);
static int do_list(int, char **);
static int do_disable(int, char **);
static int do_enable(int, char **);
static int do_install(int, char **);
static int do_uninstall(int, char **);
static int do_unload(int, char **);
static int do_refresh(int);
static int do_start(int);
static int do_stop(int);
static int list_simple_for_all(boolean_t);
static int list_mechlist_for_all(boolean_t);
static int list_policy_for_all(void);

int
main(int argc, char *argv[])
{
	char	*subcmd;
	int	cmdnum;
	int	cmd_index = 0;
	int	rc = SUCCESS;

	(void) setlocale(LC_ALL, "");

#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
#define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
#endif
	(void) textdomain(TEXT_DOMAIN);

	cryptodebug_init(basename(argv[0]));

	if (argc < REQ_ARG_CNT) {
		usage();
		return (ERROR_USAGE);
	}

	/* get the subcommand index */
	cmd_index = 0;
	subcmd = argv[1];
	cmdnum = sizeof (cmd_table)/sizeof (cmd_table[0]);

	while ((cmd_index < cmdnum) &&
	    (strcmp(subcmd, cmd_table[cmd_index]) != 0)) {
		cmd_index++;
	}
	if (cmd_index >= cmdnum) {
		usage();
		return (ERROR_USAGE);
	}

	/* do the subcommand */
	switch (cmd_index) {
	case CRYPTO_LIST:
		rc = do_list(argc, argv);
		break;
	case CRYPTO_DISABLE:
		rc = do_disable(argc, argv);
		break;
	case CRYPTO_ENABLE:
		rc = do_enable(argc, argv);
		break;
	case CRYPTO_INSTALL:
		rc = do_install(argc, argv);
		break;
	case CRYPTO_UNINSTALL:
		rc = do_uninstall(argc, argv);
		break;
	case CRYPTO_UNLOAD:
		rc = do_unload(argc, argv);
		break;
	case CRYPTO_REFRESH:
		rc = do_refresh(argc);
		break;
	case CRYPTO_START:
		rc = do_start(argc);
		break;
	case CRYPTO_STOP:
		rc = do_stop(argc);
		break;
	case CRYPTO_HELP:
		usage();
		rc = SUCCESS;
		break;
	default: /* should not come here */
		usage();
		rc = ERROR_USAGE;
		break;
	}
	return (rc);
}


static void
usage(void)
{
	/*
	 * TRANSLATION_NOTE
	 * Command usage is not to be translated.  Only the word "Usage:"
	 * along with localized expressions indicating what kind of value
	 * is expected for arguments.
	 */
	(void) fprintf(stderr, gettext("Usage:\n"));
	(void) fprintf(stderr,
	    "  cryptoadm list [-mpv] [provider=<%s> | metaslot]"
	    " [mechanism=<%s>]\n",
	    gettext("provider-name"), gettext("mechanism-list"));
	(void) fprintf(stderr,
	    "  cryptoadm list fips-140\n");
	(void) fprintf(stderr,
	    "  cryptoadm disable provider=<%s>"
	    " mechanism=<%s> | random | all\n",
	    gettext("provider-name"), gettext("mechanism-list"));
	(void) fprintf(stderr,
	    "  cryptoadm disable metaslot"
	    " [auto-key-migrate] [mechanism=<%s>]\n",
	    gettext("mechanism-list"));
	(void) fprintf(stderr,
	    "  cryptoadm disable fips-140\n");
	(void) fprintf(stderr,
	    "  cryptoadm enable provider=<%s>"
	    " mechanism=<%s> | random | all\n",
	    gettext("provider-name"), gettext("mechanism-list"));
	(void) fprintf(stderr,
	    "  cryptoadm enable metaslot [mechanism=<%s>]"
	    " [[token=<%s>] [slot=<%s>]"
	    " | [default-keystore]] | [auto-key-migrate]\n",
	    gettext("mechanism-list"), gettext("token-label"),
	    gettext("slot-description"));
	(void) fprintf(stderr,
	    "  cryptoadm enable fips-140\n");
	(void) fprintf(stderr,
	    "  cryptoadm install provider=<%s>\n",
	    gettext("provider-name"));
	(void) fprintf(stderr,
	    "  cryptoadm install provider=<%s> [mechanism=<%s>]\n",
	    gettext("provider-name"), gettext("mechanism-list"));
	(void) fprintf(stderr,
	    "  cryptoadm uninstall provider=<%s>\n",
	    gettext("provider-name"));
	(void) fprintf(stderr,
	    "  cryptoadm unload provider=<%s>\n",
	    gettext("provider-name"));
	(void) fprintf(stderr,
	    "  cryptoadm refresh\n"
	    "  cryptoadm start\n"
	    "  cryptoadm stop\n"
	    "  cryptoadm --help\n");
}


/*
 * Get the provider type.  This function returns
 * - PROV_UEF_LIB if provname contains an absolute path name
 * - PROV_KEF_SOFT if provname is a base name only (e.g., "aes").
 * - PROV_KEF_HARD if provname contains one slash only and the slash is not
 *	the 1st character (e.g., "mca/0").
 * - PROV_BADNAME otherwise.
 */
static int
get_provider_type(char *provname)
{
	char *pslash1;
	char *pslash2;

	if (provname == NULL) {
		return (FAILURE);
	}

	if (provname[0] == '/') {
		return (PROV_UEF_LIB);
	} else if ((pslash1 = strchr(provname, SEP_SLASH)) == NULL) {
		/* no slash */
		return (PROV_KEF_SOFT);
	} else {
		pslash2 = strrchr(provname, SEP_SLASH);
		if (pslash1 == pslash2) {
			return (PROV_KEF_HARD);
		} else {
			return (PROV_BADNAME);
		}
	}
}

/*
 * Get the provider structure.  This function returns NULL if no valid
 * provider= is found in argv[], otherwise a cryptoadm_provider_t is returned.
 * If provider= is found but has no argument, then a cryptoadm_provider_t
 * with cp_type = PROV_BADNAME is returned.
 */
static cryptoadm_provider_t *
get_provider(int argc, char **argv)
{
	int			c = 0;
	boolean_t		found = B_FALSE;
	cryptoadm_provider_t	*provider = NULL;
	char			*provstr = NULL, *savstr;
	boolean_t		is_metaslot = B_FALSE;

	while (!found && ++c < argc) {
		if (strncmp(argv[c], METASLOT_KEYWORD,
		    strlen(METASLOT_KEYWORD)) == 0) {
			is_metaslot = B_TRUE;
			found = B_TRUE;
		} else if (strncmp(argv[c], KN_PROVIDER,
		    strlen(KN_PROVIDER)) == 0 &&
		    strlen(argv[c]) > strlen(KN_PROVIDER)) {
			if ((provstr = strdup(argv[c])) == NULL) {
				int err = errno;
				/*
				 * TRANSLATION_NOTE
				 * "get_provider" is a function name and should
				 * not be translated.
				 */
				cryptoerror(LOG_STDERR, "get_provider: %s.",
				    strerror(err));
				return (NULL);
			}
			found = B_TRUE;
		}
	}
	if (!found)
		return (NULL);

	provider = malloc(sizeof (cryptoadm_provider_t));
	if (provider == NULL) {
		cryptoerror(LOG_STDERR, gettext("out of memory."));
		if (provstr) {
			free(provstr);
		}
		return (NULL);
	}

	if (is_metaslot) {
		(void) strlcpy(provider->cp_name, METASLOT_KEYWORD,
		    strlen(METASLOT_KEYWORD));
		provider->cp_type = METASLOT;
	} else {

		savstr = provstr;
		(void) strtok(provstr, "=");
		provstr = strtok(NULL, "=");
		if (provstr == NULL) {
			cryptoerror(LOG_STDERR, gettext("bad provider name."));
			provider->cp_type = PROV_BADNAME;
			free(savstr);
			return (provider);
		}

		(void) strlcpy(provider->cp_name, provstr,
		    sizeof (provider->cp_name));
		provider->cp_type = get_provider_type(provider->cp_name);

		free(savstr);
	}
	return (provider);
}

/*
 * Process the "feature" operands.
 *
 * "argc" and "argv" contain values specified on the command line.
 * All other arguments are used for returning parsing results.
 * If any of these arguments are NULL, that keyword is not expected,
 * and FAILURE will be returned.
 */
static int
process_metaslot_operands(int argc, char **argv, char **meta_ks_token,
    char **meta_ks_slot, boolean_t *use_default,
    boolean_t *auto_key_migrate_flag)
{
	int c = 2;
	int rc = SUCCESS;

	while (++c < argc) {
		if ((strncmp(argv[c], KN_MECH, strlen(KN_MECH)) == 0) &&
		    strlen(argv[c]) > strlen(KN_MECH)) {

			/* process mechanism operands */
			if ((rc = process_mech_operands(argc, argv, B_TRUE))
			    != SUCCESS) {
				goto finish;
			}

		} else if ((strncmp(argv[c], KN_TOKEN,
		    strlen(KN_TOKEN)) == 0) &&
		    strlen(argv[c]) > strlen(KN_TOKEN)) {
			if ((meta_ks_token) && (strtok(argv[c], "=") != NULL)) {
				char *tmp;
				if ((tmp = strtok(NULL, "=")) != NULL) {
					*meta_ks_token = strdup(tmp);
				} else {
					return (FAILURE);
				}
			} else {
				return (FAILURE);
			}

		} else if ((strncmp(argv[c], KN_SLOT,
		    strlen(KN_SLOT)) == 0) &&
		    strlen(argv[c]) > strlen(KN_SLOT)) {

			if ((meta_ks_slot) && (strtok(argv[c], "=") != NULL)) {
				char *tmp;
				if ((tmp = strtok(NULL, "=")) != NULL) {
					*meta_ks_slot = strdup(tmp);
				} else {
					return (FAILURE);
				}
			} else {
				return (FAILURE);
			}

		} else if (strncmp(argv[c], KN_DEFAULT_KS,
		    strlen(KN_DEFAULT_KS)) == 0) {

			if (use_default) {
				*use_default = B_TRUE;
			} else {
				return (FAILURE);
			}
		} else if (strncmp(argv[c], KN_AUTO_KEY_MIGRATE,
		    strlen(KN_AUTO_KEY_MIGRATE)) == 0) {

			if (auto_key_migrate_flag) {
				*auto_key_migrate_flag = B_TRUE;
			} else {
				return (FAILURE);
			}
		} else {
			return (FAILURE);
		}
	}
finish:
	return (rc);
}

/*
 * Process the "feature" operands.
 */
static int
process_feature_operands(int argc, char **argv)
{
	int c = 2;

	while (++c < argc) {
		if (strcmp(argv[c], KN_ALL) == 0) {
			allflag = B_TRUE;
			rndflag = B_TRUE; /* all includes random also. */
		} else if (strcmp(argv[c], RANDOM) == 0) {
			rndflag = B_TRUE;
		}
	}
	return (SUCCESS);
}

/*
 * Process the mechanism operands for the disable, enable and install
 * subcommands.  This function sets the static variable allflag to be B_TRUE
 * if the keyword "all" is specified, otherwise builds a link list of the
 * mechanism operands and save it in the static variable mecharglist.
 *
 * This function returns
 *	ERROR_USAGE: mechanism operand is missing.
 *	FAILURE: out of memory.
 *	SUCCESS: otherwise.
 */
static int
process_mech_operands(int argc, char **argv, boolean_t quiet)
{
	mechlist_t	*pmech;
	mechlist_t	*pcur = NULL;
	mechlist_t	*phead = NULL;
	boolean_t	found = B_FALSE;
	char		*mechliststr = NULL;
	char		*curmech = NULL;
	int		c = -1;
	int		rc = SUCCESS;

	while (!found && ++c < argc) {
		if ((strncmp(argv[c], KN_MECH, strlen(KN_MECH)) == 0) &&
		    strlen(argv[c]) > strlen(KN_MECH)) {
			found = B_TRUE;
		}
	}
	if (!found) {
		if (!quiet)
			/*
			 * TRANSLATION_NOTE
			 * "mechanism" could be either a literal keyword
			 * and hence not to be translated, or a descriptive
			 * word and translatable.  A choice was made to
			 * view it as a literal keyword.
			 */
			cryptoerror(LOG_STDERR,
			    gettext("the %s operand is missing.\n"),
			    "mechanism");
		return (ERROR_USAGE);
	}
	(void) strtok(argv[c], "=");
	mechliststr = strtok(NULL, "=");

	if (strcmp(mechliststr, "all") == 0) {
		allflag = B_TRUE;
		mecharglist = NULL;
		return (SUCCESS);
	}

	curmech = strtok(mechliststr, ",");
	do {
		if ((pmech = create_mech(curmech)) == NULL) {
			rc = FAILURE;
			break;
		} else {
			if (phead == NULL) {
				phead = pcur = pmech;
			} else {
				pcur->next = pmech;
				pcur = pmech;
			}
		}
	} while ((curmech = strtok(NULL, ",")) != NULL);

	if (rc == FAILURE) {
		cryptoerror(LOG_STDERR, gettext("out of memory."));
		free_mechlist(phead);
	} else {
		mecharglist = phead;
		rc = SUCCESS;
	}
	return (rc);
}



/*
 * The top level function for the "cryptoadm list" subcommand and options.
 */
static int
do_list(int argc, char **argv)
{
	boolean_t		mflag = B_FALSE;
	boolean_t		pflag = B_FALSE;
	boolean_t		vflag = B_FALSE;
	char			ch;
	cryptoadm_provider_t	*prov = NULL;
	int			rc = SUCCESS;

	if ((argc == 3) && (strncmp(argv[2], FIPS_KEYWORD,
	    strlen(FIPS_KEYWORD))) == 0) {
		/*
		 * cryptoadm list fips-140
		 */
		rc = do_fips_actions(FIPS140_STATUS, NOT_REFRESH);
		return (rc);
	}

	argc -= 1;
	argv += 1;

	if (argc == 1) {
		rc = list_simple_for_all(B_FALSE);
		goto out;
	}

	/*
	 * cryptoadm list [-v] [-m] [-p] [provider=<>] [mechanism=<>]
	 */
	if (argc > 5) {
		usage();
		return (rc);
	}

	while ((ch = getopt(argc, argv, "mpv")) != EOF) {
		switch (ch) {
		case 'm':
			mflag = B_TRUE;
			if (pflag) {
				rc = ERROR_USAGE;
			}
			break;
		case 'p':
			pflag = B_TRUE;
			if (mflag || vflag) {
				rc = ERROR_USAGE;
			}
			break;
		case 'v':
			vflag = B_TRUE;
			if (pflag)
				rc = ERROR_USAGE;
			break;
		default:
			rc = ERROR_USAGE;
			break;
		}
	}

	if (rc == ERROR_USAGE) {
		usage();
		return (rc);
	}

	if ((rc = process_feature_operands(argc, argv)) != SUCCESS) {
		goto out;
	}

	prov = get_provider(argc, argv);

	if (mflag || vflag) {
		if (argc > 0) {
			rc = process_mech_operands(argc, argv, B_TRUE);
			if (rc == FAILURE)
				goto out;
			/* "-m" is implied when a mechanism list is given */
			if (mecharglist != NULL || allflag)
				mflag = B_TRUE;
		}
	}

	if (prov == NULL) {
		if (mflag) {
			rc = list_mechlist_for_all(vflag);
		} else if (pflag) {
			rc = list_policy_for_all();
		} else if (vflag) {
			rc = list_simple_for_all(vflag);
		}
	} else if (prov->cp_type == METASLOT) {
		if ((!mflag) && (!vflag) && (!pflag)) {
			/* no flag is specified, just list metaslot status */
			rc = list_metaslot_info(mflag, vflag, mecharglist);
		} else if (mflag || vflag) {
			rc = list_metaslot_info(mflag, vflag, mecharglist);
		} else if (pflag) {
			rc = list_metaslot_policy();
		} else {
			/* error message */
			usage();
			rc = ERROR_USAGE;
		}
	} else if (prov->cp_type == PROV_BADNAME) {
		usage();
		rc = ERROR_USAGE;
		goto out;
	} else { /* do the listing for a provider only */
		char	*provname = prov->cp_name;

		if (mflag || vflag) {
			if (vflag)
				(void) printf(gettext("Provider: %s\n"),
				    provname);
			switch (prov->cp_type) {
			case PROV_UEF_LIB:
				rc = list_mechlist_for_lib(provname,
				    mecharglist, NULL, B_FALSE, vflag, mflag);
				break;
			case PROV_KEF_SOFT:
				rc = list_mechlist_for_soft(provname,
				    NULL, NULL);
				break;
			case PROV_KEF_HARD:
				rc = list_mechlist_for_hard(provname);
				break;
			default: /* should not come here */
				rc = FAILURE;
				break;
			}
		} else if (pflag) {
			switch (prov->cp_type) {
			case PROV_UEF_LIB:
				rc = list_policy_for_lib(provname);
				break;
			case PROV_KEF_SOFT:
				if (getzoneid() == GLOBAL_ZONEID) {
					rc = list_policy_for_soft(provname,
					    NULL, NULL);
				} else {
					/*
					 * TRANSLATION_NOTE
					 * "global" is keyword and not to
					 * be translated.
					 */
					cryptoerror(LOG_STDERR, gettext(
					    "policy information for kernel "
					    "providers is available "
					    "in the %s zone only"), "global");
					rc = FAILURE;
				}
				break;
			case PROV_KEF_HARD:
				if (getzoneid() == GLOBAL_ZONEID) {
					rc = list_policy_for_hard(
					    provname, NULL, NULL, NULL);
				} else {
					/*
					 * TRANSLATION_NOTE
					 * "global" is keyword and not to
					 * be translated.
					 */
					cryptoerror(LOG_STDERR, gettext(
					    "policy information for kernel "
					    "providers is available "
					    "in the %s zone only"), "global");
					rc = FAILURE;
				}

				break;
			default: /* should not come here */
				rc = FAILURE;
				break;
			}
		} else {
			/* error message */
			usage();
			rc = ERROR_USAGE;
		}
	}

out:
	if (prov != NULL)
		free(prov);

	if (mecharglist != NULL)
		free_mechlist(mecharglist);
	return (rc);
}


/*
 * The top level function for the "cryptoadm disable" subcommand.
 */
static int
do_disable(int argc, char **argv)
{
	cryptoadm_provider_t	*prov = NULL;
	int			rc = SUCCESS;
	boolean_t		auto_key_migrate_flag = B_FALSE;

	if ((argc == 3) && (strncmp(argv[2], FIPS_KEYWORD,
	    strlen(FIPS_KEYWORD))) == 0) {
		/*
		 * cryptoadm disable fips-140
		 */
		rc = do_fips_actions(FIPS140_DISABLE, NOT_REFRESH);
		return (rc);
	}

	if ((argc < 3) || (argc > 5)) {
		usage();
		return (ERROR_USAGE);
	}

	prov = get_provider(argc, argv);
	if (prov == NULL) {
		usage();
		return (ERROR_USAGE);
	}
	if (prov->cp_type == PROV_BADNAME) {
		return (FAILURE);
	}

	if ((rc = process_feature_operands(argc, argv)) != SUCCESS) {
		goto out;
	}

	/*
	 * If allflag or rndflag has already been set there is no reason to
	 * process mech=
	 */
	if (prov->cp_type == METASLOT) {
		if ((argc > 3) &&
		    (rc = process_metaslot_operands(argc, argv,
		    NULL, NULL, NULL, &auto_key_migrate_flag)) != SUCCESS) {
			usage();
			return (rc);
		}
	} else if (!allflag && !rndflag &&
	    (rc = process_mech_operands(argc, argv, B_FALSE)) != SUCCESS) {
			return (rc);
	}

	switch (prov->cp_type) {
	case METASLOT:
		rc = disable_metaslot(mecharglist, allflag,
		    auto_key_migrate_flag);
		break;
	case PROV_UEF_LIB:
		rc = disable_uef_lib(prov->cp_name, rndflag, allflag,
		    mecharglist);
		break;
	case PROV_KEF_SOFT:
		if (rndflag && !allflag) {
			if ((mecharglist = create_mech(RANDOM)) == NULL) {
				rc = FAILURE;
				break;
			}
		}
		if (getzoneid() == GLOBAL_ZONEID) {
			rc = disable_kef_software(prov->cp_name, rndflag,
			    allflag, mecharglist);
		} else {
			/*
			 * TRANSLATION_NOTE
			 * "disable" could be either a literal keyword
			 * and hence not to be translated, or a verb and
			 * translatable.  A choice was made to view it as
			 * a literal keyword.  "global" is keyword and not
			 * to be translated.
			 */
			cryptoerror(LOG_STDERR, gettext("%1$s for kernel "
			    "providers is supported in the %2$s zone only"),
			    "disable", "global");
			rc = FAILURE;
		}
		break;
	case PROV_KEF_HARD:
		if (rndflag && !allflag) {
			if ((mecharglist = create_mech(RANDOM)) == NULL) {
				rc = FAILURE;
				break;
			}
		}
		if (getzoneid() == GLOBAL_ZONEID) {
			rc = disable_kef_hardware(prov->cp_name, rndflag,
			    allflag, mecharglist);
		} else {
			/*
			 * TRANSLATION_NOTE
			 * "disable" could be either a literal keyword
			 * and hence not to be translated, or a verb and
			 * translatable.  A choice was made to view it as
			 * a literal keyword.  "global" is keyword and not
			 * to be translated.
			 */
			cryptoerror(LOG_STDERR, gettext("%1$s for kernel "
			    "providers is supported in the %2$s zone only"),
			    "disable", "global");
			rc = FAILURE;
		}
		break;
	default: /* should not come here */
		rc = FAILURE;
		break;
	}

out:
	free(prov);
	if (mecharglist != NULL) {
		free_mechlist(mecharglist);
	}
	return (rc);
}


/*
 * The top level function for the "cryptoadm enable" subcommand.
 */
static int
do_enable(int argc, char **argv)
{
	cryptoadm_provider_t	*prov = NULL;
	int			rc = SUCCESS;
	char 			*alt_token = NULL, *alt_slot = NULL;
	boolean_t		use_default = B_FALSE;
	boolean_t		auto_key_migrate_flag = B_FALSE;

	if ((argc == 3) && (strncmp(argv[2], FIPS_KEYWORD,
	    strlen(FIPS_KEYWORD))) == 0) {
		/*
		 * cryptoadm enable fips-140
		 */
		rc = do_fips_actions(FIPS140_ENABLE, NOT_REFRESH);
		return (rc);
	}

	if ((argc < 3) || (argc > 6)) {
		usage();
		return (ERROR_USAGE);
	}

	prov = get_provider(argc, argv);
	if (prov == NULL) {
		usage();
		return (ERROR_USAGE);
	}
	if ((prov->cp_type != METASLOT) && (argc != 4)) {
		usage();
		return (ERROR_USAGE);
	}
	if (prov->cp_type == PROV_BADNAME) {
		rc = FAILURE;
		goto out;
	}


	if (prov->cp_type == METASLOT) {
		if ((rc = process_metaslot_operands(argc, argv, &alt_token,
		    &alt_slot, &use_default, &auto_key_migrate_flag))
		    != SUCCESS) {
			usage();
			goto out;
		}
		if ((alt_slot || alt_token) && use_default) {
			usage();
			rc = FAILURE;
			goto out;
		}
	} else {
		if ((rc = process_feature_operands(argc, argv)) != SUCCESS) {
			goto out;
		}

		/*
		 * If allflag or rndflag has already been set there is
		 * no reason to process mech=
		 */
		if (!allflag && !rndflag &&
		    (rc = process_mech_operands(argc, argv, B_FALSE))
		    != SUCCESS) {
			goto out;
		}
	}

	switch (prov->cp_type) {
	case METASLOT:
		rc = enable_metaslot(alt_token, alt_slot, use_default,
		    mecharglist, allflag, auto_key_migrate_flag);
		break;
	case PROV_UEF_LIB:
		rc = enable_uef_lib(prov->cp_name, rndflag, allflag,
		    mecharglist);
		break;
	case PROV_KEF_SOFT:
	case PROV_KEF_HARD:
		if (rndflag && !allflag) {
			if ((mecharglist = create_mech(RANDOM)) == NULL) {
				rc = FAILURE;
				break;
			}
		}
		if (getzoneid() == GLOBAL_ZONEID) {
			rc = enable_kef(prov->cp_name, rndflag, allflag,
			    mecharglist);
		} else {
			/*
			 * TRANSLATION_NOTE
			 * "enable" could be either a literal keyword
			 * and hence not to be translated, or a verb and
			 * translatable.  A choice was made to view it as
			 * a literal keyword.  "global" is keyword and not
			 * to be translated.
			 */
			cryptoerror(LOG_STDERR, gettext("%1$s for kernel "
			    "providers is supported in the %2$s zone only"),
			    "enable", "global");
			rc = FAILURE;
		}
		break;
	default: /* should not come here */
		rc = FAILURE;
		break;
	}
out:
	free(prov);
	if (mecharglist != NULL) {
		free_mechlist(mecharglist);
	}
	if (alt_token != NULL) {
		free(alt_token);
	}
	if (alt_slot != NULL) {
		free(alt_slot);
	}
	return (rc);
}



/*
 * The top level function for the "cryptoadm install" subcommand.
 */
static int
do_install(int argc, char **argv)
{
	cryptoadm_provider_t	*prov = NULL;
	int	rc;

	if (argc < 3) {
		usage();
		return (ERROR_USAGE);
	}

	prov = get_provider(argc, argv);
	if (prov == NULL ||
	    prov->cp_type == PROV_BADNAME || prov->cp_type == PROV_KEF_HARD) {
		/*
		 * TRANSLATION_NOTE
		 * "install" could be either a literal keyword and hence
		 * not to be translated, or a verb and translatable.  A
		 * choice was made to view it as a literal keyword.
		 */
		cryptoerror(LOG_STDERR,
		    gettext("bad provider name for %s."), "install");
		rc = FAILURE;
		goto out;
	}

	if (prov->cp_type == PROV_UEF_LIB) {
		rc = install_uef_lib(prov->cp_name);
		goto out;
	}

	/* It is the PROV_KEF_SOFT type now  */

	/* check if there are mechanism operands */
	if (argc < 4) {
		/*
		 * TRANSLATION_NOTE
		 * "mechanism" could be either a literal keyword and hence
		 * not to be translated, or a descriptive word and
		 * translatable.  A choice was made to view it as a literal
		 * keyword.
		 */
		cryptoerror(LOG_STDERR,
		    gettext("need %s operands for installing a"
		    " kernel software provider."), "mechanism");
		rc = ERROR_USAGE;
		goto out;
	}

	if ((rc = process_mech_operands(argc, argv, B_FALSE)) != SUCCESS) {
		goto out;
	}

	if (allflag == B_TRUE) {
		/*
		 * TRANSLATION_NOTE
		 * "all", "mechanism", and "install" are all keywords and
		 * not to be translated.
		 */
		cryptoerror(LOG_STDERR,
		    gettext("can not use the %1$s keyword for %2$s "
		    "in the %3$s subcommand."), "all", "mechanism", "install");
		rc = ERROR_USAGE;
		goto out;
	}

	if (getzoneid() == GLOBAL_ZONEID) {
		rc = install_kef(prov->cp_name, mecharglist);
	} else {
		/*
		 * TRANSLATION_NOTE
		 * "install" could be either a literal keyword and hence
		 * not to be translated, or a verb and translatable.  A
		 * choice was made to view it as a literal keyword.
		 * "global" is keyword and not to be translated.
		 */
		cryptoerror(LOG_STDERR, gettext("%1$s for kernel providers "
		    "is supported in the %2$s zone only"), "install", "global");
		rc = FAILURE;
	}
out:
	free(prov);
	return (rc);
}



/*
 * The top level function for the "cryptoadm uninstall" subcommand.
 */
static int
do_uninstall(int argc, char **argv)
{
	cryptoadm_provider_t	*prov = NULL;
	int	rc = SUCCESS;

	if (argc != 3) {
		usage();
		return (ERROR_USAGE);
	}

	prov = get_provider(argc, argv);
	if (prov == NULL ||
	    prov->cp_type == PROV_BADNAME || prov->cp_type == PROV_KEF_HARD) {
		/*
		 * TRANSLATION_NOTE
		 * "uninstall" could be either a literal keyword and hence
		 * not to be translated, or a verb and translatable.  A
		 * choice was made to view it as a literal keyword.
		 */
		cryptoerror(LOG_STDERR,
		    gettext("bad provider name for %s."), "uninstall");
		free(prov);
		return (FAILURE);
	}

	if (prov->cp_type == PROV_UEF_LIB) {
		rc = uninstall_uef_lib(prov->cp_name);

	} else if (prov->cp_type == PROV_KEF_SOFT) {
		if (getzoneid() == GLOBAL_ZONEID) {
			/* unload and remove from kcf.conf */
			rc = uninstall_kef(prov->cp_name);
		} else {
			/*
			 * TRANSLATION_NOTE
			 * "uninstall" could be either a literal keyword and
			 * hence not to be translated, or a verb and
			 * translatable.  A choice was made to view it as a
			 * literal keyword.  "global" is keyword and not to
			 * be translated.
			 */
			cryptoerror(LOG_STDERR, gettext("%1$s for kernel "
			    "providers is supported in the %2$s zone only"),
			    "uninstall", "global");
			rc = FAILURE;
		}
	}

	free(prov);
	return (rc);
}


/*
 * The top level function for the "cryptoadm unload" subcommand.
 */
static int
do_unload(int argc, char **argv)
{
	cryptoadm_provider_t	*prov = NULL;
	entry_t			*pent = NULL;
	boolean_t		in_kernel = B_FALSE;
	int			rc = SUCCESS;
	char			*provname = NULL;

	if (argc != 3) {
		usage();
		return (ERROR_USAGE);
	}

	/* check if it is a kernel software provider */
	prov = get_provider(argc, argv);
	if (prov == NULL) {
		cryptoerror(LOG_STDERR,
		    gettext("unable to determine provider name."));
		goto out;
	}
	provname = prov->cp_name;
	if (prov->cp_type != PROV_KEF_SOFT) {
		cryptoerror(LOG_STDERR,
		    gettext("%s is not a valid kernel software provider."),
		    provname);
		rc = FAILURE;
		goto out;
	}

	if (getzoneid() != GLOBAL_ZONEID) {
		/*
		 * TRANSLATION_NOTE
		 * "unload" could be either a literal keyword and hence
		 * not to be translated, or a verb and translatable.
		 * A choice was made to view it as a literal keyword.
		 * "global" is keyword and not to be translated.
		 */
		cryptoerror(LOG_STDERR, gettext("%1$s for kernel providers "
		    "is supported in the %2$s zone only"), "unload", "global");
		rc = FAILURE;
		goto out;
	}

	if (check_kernel_for_soft(provname, NULL, &in_kernel) == FAILURE) {
		cryptodebug("internal error");
		rc = FAILURE;
		goto out;
	} else if (in_kernel == B_FALSE) {
		cryptoerror(LOG_STDERR,
		    gettext("provider %s is not loaded or does not exist."),
		    provname);
		rc = FAILURE;
		goto out;
	}

	/* Get kcf.conf entry.  If none, build a new entry */
	if ((pent = getent_kef(provname, NULL, NULL)) == NULL) {
		if ((pent = create_entry(provname)) == NULL) {
			cryptoerror(LOG_STDERR, gettext("out of memory."));
			rc = FAILURE;
			goto out;
		}
	}

	/* If it is unloaded already, return  */
	if (!pent->load) { /* unloaded already */
		cryptoerror(LOG_STDERR,
		    gettext("failed to unload %s."), provname);
		rc = FAILURE;
		goto out;
	} else if (unload_kef_soft(provname) != FAILURE) {
		/* Mark as unloaded in kcf.conf */
		pent->load = B_FALSE;
		rc = update_kcfconf(pent, MODIFY_MODE);
	} else {
		cryptoerror(LOG_STDERR,
		    gettext("failed to unload %s."), provname);
		rc = FAILURE;
	}
out:
	free(prov);
	free_entry(pent);
	return (rc);
}



/*
 * The top level function for the "cryptoadm refresh" subcommand.
 */
static int
do_refresh(int argc)
{
	if (argc != 2) {
		usage();
		return (ERROR_USAGE);
	}

	if (getzoneid() == GLOBAL_ZONEID) {
		return (refresh());
	} else { /* non-global zone */
		/*
		 * Note:  in non-global zone, this must silently return SUCCESS
		 * due to integration with SMF, for "svcadm refresh cryptosvc"
		 */
		return (SUCCESS);
	}
}


/*
 * The top level function for the "cryptoadm start" subcommand.
 */
static int
do_start(int argc)
{
	int ret;

	if (argc != 2) {
		usage();
		return (ERROR_USAGE);
	}

	ret = do_refresh(argc);
	if (ret != SUCCESS)
		return (ret);

	return (start_daemon());
}

/*
 * The top level function for the "cryptoadm stop" subcommand.
 */
static int
do_stop(int argc)
{
	if (argc != 2) {
		usage();
		return (ERROR_USAGE);
	}

	return (stop_daemon());
}



/*
 * Print a list all the the providers.
 * Called for "cryptoadm list" or "cryptoadm list -v" (no -m or -p).
 */
static int
list_simple_for_all(boolean_t verbose)
{
	uentrylist_t		*pliblist = NULL;
	uentrylist_t		*plibptr = NULL;
	entry_t			*pent = NULL;
	crypto_get_dev_list_t	*pdevlist_kernel = NULL;
	int			rc = SUCCESS;
	int			i;

	/* get user-level providers */
	(void) printf(gettext("\nUser-level providers:\n"));
	if (get_pkcs11conf_info(&pliblist) != SUCCESS) {
		cryptoerror(LOG_STDERR, gettext(
		    "failed to retrieve the list of user-level providers."));
		rc = FAILURE;
	}

	for (plibptr = pliblist; plibptr != NULL; plibptr = plibptr->next) {
		/* skip metaslot and fips-140 entry */
		if ((strcmp(plibptr->puent->name, METASLOT_KEYWORD) != 0) &&
		    (strcmp(plibptr->puent->name, FIPS_KEYWORD) != 0)) {
			(void) printf(gettext("Provider: %s\n"),
			    plibptr->puent->name);
			if (verbose) {
				(void) list_mechlist_for_lib(
				    plibptr->puent->name, mecharglist, NULL,
				    B_FALSE, verbose, B_FALSE);
				(void) printf("\n");
			}
		}
	}
	free_uentrylist(pliblist);

	/* get kernel software providers */
	(void) printf(gettext("\nKernel software providers:\n"));

	if (getzoneid() == GLOBAL_ZONEID) {
		/* get kernel software providers from kernel ioctl */
		crypto_get_soft_list_t		*psoftlist_kernel = NULL;
		uint_t				sl_soft_count;
		char				*psoftname;
		entrylist_t			*pdevlist_conf = NULL;
		entrylist_t			*psoftlist_conf = NULL;

		if (get_soft_list(&psoftlist_kernel) == FAILURE) {
			cryptoerror(LOG_ERR, gettext("Failed to retrieve the "
			    "software provider list from kernel."));
			rc = FAILURE;
		} else {
			sl_soft_count = psoftlist_kernel->sl_soft_count;

			if (get_kcfconf_info(&pdevlist_conf, &psoftlist_conf)
			    == FAILURE) {
				cryptoerror(LOG_ERR,
				    "failed to retrieve the providers' "
				    "information from file kcf.conf - %s.",
				    _PATH_KCF_CONF);
				free(psoftlist_kernel);
				rc = FAILURE;
			} else {

				for (i = 0,
				    psoftname = psoftlist_kernel->sl_soft_names;
				    i < sl_soft_count;
				    ++i, psoftname += strlen(psoftname) + 1) {
					pent = getent_kef(psoftname,
					    pdevlist_conf, psoftlist_conf);
					(void) printf("\t%s%s\n", psoftname,
					    (pent == NULL) || (pent->load) ?
					    "" : gettext(" (inactive)"));
				}
				free_entrylist(pdevlist_conf);
				free_entrylist(psoftlist_conf);
			}
			free(psoftlist_kernel);
		}

	} else {
		/* kcf.conf not there in non-global zone, use /dev/cryptoadm */
		entrylist_t	*pdevlist_zone = NULL;
		entrylist_t	*psoftlist_zone = NULL;
		entrylist_t	*ptr;

		if (get_admindev_info(&pdevlist_zone, &psoftlist_zone) !=
		    SUCCESS) {
			cryptoerror(LOG_STDERR,
			    gettext("failed to retrieve the "
			    "list of kernel software providers.\n"));
			rc = FAILURE;
		}

		ptr = psoftlist_zone;
		while (ptr != NULL) {
			(void) printf("\t%s\n", ptr->pent->name);
			ptr = ptr->next;
		}

		free_entrylist(pdevlist_zone);
		free_entrylist(psoftlist_zone);
	}

	/* get kernel hardware providers */
	(void) printf(gettext("\nKernel hardware providers:\n"));
	if (get_dev_list(&pdevlist_kernel) == FAILURE) {
		cryptoerror(LOG_STDERR, gettext("failed to retrieve "
		    "the list of kernel hardware providers.\n"));
		rc = FAILURE;
	} else {
		for (i = 0; i < pdevlist_kernel->dl_dev_count; i++) {
			(void) printf("\t%s/%d\n",
			    pdevlist_kernel->dl_devs[i].le_dev_name,
			    pdevlist_kernel->dl_devs[i].le_dev_instance);
		}
	}
	free(pdevlist_kernel);

	return (rc);
}



/*
 * List all the providers. And for each provider, list the mechanism list.
 * Called for "cryptoadm list -m" or "cryptoadm list -mv" .
 */
static int
list_mechlist_for_all(boolean_t verbose)
{
	crypto_get_dev_list_t	*pdevlist_kernel = NULL;
	uentrylist_t		*pliblist = NULL;
	uentrylist_t		*plibptr = NULL;
	entry_t			*pent = NULL;
	mechlist_t		*pmechlist = NULL;
	char			provname[MAXNAMELEN];
	char			devname[MAXNAMELEN];
	int			inst_num;
	int			count;
	int			i;
	int			rv;
	int			rc = SUCCESS;

	/* get user-level providers */
	(void) printf(gettext("\nUser-level providers:\n"));
	/*
	 * TRANSLATION_NOTE
	 * Strictly for appearance's sake, this line should be as long as
	 * the length of the translated text above.
	 */
	(void) printf(gettext("=====================\n"));
	if (get_pkcs11conf_info(&pliblist) != SUCCESS) {
		cryptoerror(LOG_STDERR, gettext("failed to retrieve "
		    "the list of user-level providers.\n"));
		rc = FAILURE;
	}

	plibptr = pliblist;
	while (plibptr != NULL) {
		/* skip metaslot and fips-140 entry */
		if ((strcmp(plibptr->puent->name, METASLOT_KEYWORD) != 0) &&
		    (strcmp(plibptr->puent->name, FIPS_KEYWORD) != 0)) {
			(void) printf(gettext("\nProvider: %s\n"),
			    plibptr->puent->name);
			rv = list_mechlist_for_lib(plibptr->puent->name,
			    mecharglist, NULL, B_FALSE, verbose, B_TRUE);
			if (rv == FAILURE) {
				rc = FAILURE;
			}
		}
		plibptr = plibptr->next;
	}
	free_uentrylist(pliblist);

	/* get kernel software providers */
	(void) printf(gettext("\nKernel software providers:\n"));

	/*
	 * TRANSLATION_NOTE
	 * Strictly for appearance's sake, this line should be as long as
	 * the length of the translated text above.
	 */
	(void) printf(gettext("==========================\n"));
	if (getzoneid() == GLOBAL_ZONEID) {
		/* get kernel software providers from kernel ioctl */
		crypto_get_soft_list_t		*psoftlist_kernel = NULL;
		uint_t				sl_soft_count;
		char				*psoftname;
		int				i;
		entrylist_t			*pdevlist_conf = NULL;
		entrylist_t			*psoftlist_conf = NULL;

		if (get_soft_list(&psoftlist_kernel) == FAILURE) {
			cryptoerror(LOG_ERR, gettext("Failed to retrieve the "
			    "software provider list from kernel."));
			return (FAILURE);
		}
		sl_soft_count = psoftlist_kernel->sl_soft_count;

		if (get_kcfconf_info(&pdevlist_conf, &psoftlist_conf)
		    == FAILURE) {
			cryptoerror(LOG_ERR,
			    "failed to retrieve the providers' "
			    "information from file kcf.conf - %s.",
			    _PATH_KCF_CONF);
			free(psoftlist_kernel);
			return (FAILURE);
		}

		for (i = 0, psoftname = psoftlist_kernel->sl_soft_names;
		    i < sl_soft_count;
		    ++i, psoftname += strlen(psoftname) + 1) {
			pent = getent_kef(psoftname, pdevlist_conf,
			    psoftlist_conf);
			if ((pent == NULL) || (pent->load)) {
				rv = list_mechlist_for_soft(psoftname,
				    NULL, NULL);
				if (rv == FAILURE) {
					rc = FAILURE;
				}
			} else {
				(void) printf(gettext("%s: (inactive)\n"),
				    psoftname);
			}
		}

		free(psoftlist_kernel);
		free_entrylist(pdevlist_conf);
		free_entrylist(psoftlist_conf);

	} else {
		/* kcf.conf not there in non-global zone, use /dev/cryptoadm */
		entrylist_t	*pdevlist_zone = NULL;
		entrylist_t	*psoftlist_zone = NULL;
		entrylist_t	*ptr;

		if (get_admindev_info(&pdevlist_zone, &psoftlist_zone) !=
		    SUCCESS) {
			cryptoerror(LOG_STDERR, gettext("failed to retrieve "
			    "the list of kernel software providers.\n"));
			rc = FAILURE;
		}

		for (ptr = psoftlist_zone; ptr != NULL; ptr = ptr->next) {
			rv = list_mechlist_for_soft(ptr->pent->name,
			    pdevlist_zone, psoftlist_zone);
			if (rv == FAILURE) {
				(void) printf(gettext(
				    "%s: failed to get the mechanism list.\n"),
				    ptr->pent->name);
				rc = FAILURE;
			}
		}

		free_entrylist(pdevlist_zone);
		free_entrylist(psoftlist_zone);
	}

	/* Get kernel hardware providers and their mechanism lists */
	(void) printf(gettext("\nKernel hardware providers:\n"));
	/*
	 * TRANSLATION_NOTE
	 * Strictly for appearance's sake, this line should be as long as
	 * the length of the translated text above.
	 */
	(void) printf(gettext("==========================\n"));
	if (get_dev_list(&pdevlist_kernel) != SUCCESS) {
		cryptoerror(LOG_STDERR, gettext("failed to retrieve "
		    "the list of hardware providers.\n"));
		return (FAILURE);
	}

	for (i = 0; i < pdevlist_kernel->dl_dev_count; i++) {
		(void) strlcpy(devname,
		    pdevlist_kernel->dl_devs[i].le_dev_name, MAXNAMELEN);
		inst_num = pdevlist_kernel->dl_devs[i].le_dev_instance;
		count = pdevlist_kernel->dl_devs[i].le_mechanism_count;
		(void) snprintf(provname, sizeof (provname), "%s/%d", devname,
		    inst_num);
		if (get_dev_info(devname, inst_num, count, &pmechlist) ==
		    SUCCESS) {
			(void) filter_mechlist(&pmechlist, RANDOM);
			print_mechlist(provname, pmechlist);
			free_mechlist(pmechlist);
		} else {
			(void) printf(gettext("%s: failed to get the mechanism"
			    " list.\n"), provname);
			rc = FAILURE;
		}
	}
	free(pdevlist_kernel);
	return (rc);
}


/*
 * List all the providers. And for each provider, list the policy information.
 * Called for "cryptoadm list -p".
 */
static int
list_policy_for_all(void)
{
	crypto_get_dev_list_t	*pdevlist_kernel = NULL;
	uentrylist_t		*pliblist = NULL;
	entrylist_t		*pdevlist_conf = NULL;
	entrylist_t		*psoftlist_conf = NULL;
	entrylist_t		*ptr = NULL;
	entrylist_t		*phead = NULL;
	boolean_t		found = B_FALSE;
	char			provname[MAXNAMELEN];
	int			i;
	int			rc = SUCCESS;

	/* Get user-level providers */
	(void) printf(gettext("\nUser-level providers:\n"));
	/*
	 * TRANSLATION_NOTE
	 * Strictly for appearance's sake, this line should be as long as
	 * the length of the translated text above.
	 */
	(void) printf(gettext("=====================\n"));
	if (get_pkcs11conf_info(&pliblist) == FAILURE) {
		cryptoerror(LOG_STDERR, gettext("failed to retrieve "
		    "the list of user-level providers.\n"));
		rc = FAILURE;
	} else {
		uentrylist_t	*plibptr = pliblist;

		while (plibptr != NULL) {
			/* skip metaslot and fips-140 entry */
			if ((strcmp(plibptr->puent->name,
			    METASLOT_KEYWORD) != 0) &&
			    (strcmp(plibptr->puent->name,
			    FIPS_KEYWORD) != 0)) {
				if (print_uef_policy(plibptr->puent)
				    == FAILURE) {
					rc = FAILURE;
				}
			}
			plibptr = plibptr->next;
		}
		free_uentrylist(pliblist);
	}

	/* kernel software providers */
	(void) printf(gettext("\nKernel software providers:\n"));
	/*
	 * TRANSLATION_NOTE
	 * Strictly for appearance's sake, this line should be as long as
	 * the length of the translated text above.
	 */
	(void) printf(gettext("==========================\n"));

	/* Get all entries from the kernel */
	if (getzoneid() == GLOBAL_ZONEID) {
		/* get kernel software providers from kernel ioctl */
		crypto_get_soft_list_t		*psoftlist_kernel = NULL;
		uint_t				sl_soft_count;
		char				*psoftname;
		int				i;

		if (get_soft_list(&psoftlist_kernel) == FAILURE) {
			cryptoerror(LOG_ERR, gettext("Failed to retrieve the "
			    "software provider list from kernel."));
			rc = FAILURE;
		} else {
			sl_soft_count = psoftlist_kernel->sl_soft_count;

			for (i = 0, psoftname = psoftlist_kernel->sl_soft_names;
			    i < sl_soft_count;
			    ++i, psoftname += strlen(psoftname) + 1) {
				(void) list_policy_for_soft(psoftname,
				    pdevlist_conf, psoftlist_conf);
			}
			free(psoftlist_kernel);
		}

	} else {
		/* kcf.conf not there in non-global zone, no policy info */

		/*
		 * TRANSLATION_NOTE
		 * "global" is keyword and not to be translated.
		 */
		cryptoerror(LOG_STDERR, gettext(
		    "policy information for kernel software providers is "
		    "available in the %s zone only"), "global");
	}

	/* Kernel hardware providers */
	(void) printf(gettext("\nKernel hardware providers:\n"));
	/*
	 * TRANSLATION_NOTE
	 * Strictly for appearance's sake, this line should be as long as
	 * the length of the translated text above.
	 */
	(void) printf(gettext("==========================\n"));

	if (getzoneid() != GLOBAL_ZONEID) {
		/*
		 * TRANSLATION_NOTE
		 * "global" is keyword and not to be translated.
		 */
		cryptoerror(LOG_STDERR, gettext(
		    "policy information for kernel hardware providers is "
		    "available in the %s zone only"), "global");
		return (FAILURE);
	}

	/* Get the hardware provider list from kernel */
	if (get_dev_list(&pdevlist_kernel) != SUCCESS) {
		cryptoerror(LOG_STDERR, gettext(
		    "failed to retrieve the list of hardware providers.\n"));
		return (FAILURE);
	}

	if (get_kcfconf_info(&pdevlist_conf, &psoftlist_conf) == FAILURE) {
		cryptoerror(LOG_ERR, "failed to retrieve the providers' "
		    "information from file kcf.conf - %s.",
		    _PATH_KCF_CONF);
		return (FAILURE);
	}


	/*
	 * For each hardware provider from kernel, check if it has an entry
	 * in the config file.  If it has an entry, print out the policy from
	 * config file and remove the entry from the hardware provider list
	 * of the config file.  If it does not have an entry in the config
	 * file, no mechanisms of it have been disabled. But, we still call
	 * list_policy_for_hard() to account for the "random" feature.
	 */
	for (i = 0; i < pdevlist_kernel->dl_dev_count; i++) {
		(void) snprintf(provname, sizeof (provname), "%s/%d",
		    pdevlist_kernel->dl_devs[i].le_dev_name,
		    pdevlist_kernel->dl_devs[i].le_dev_instance);

		found = B_FALSE;
		phead = ptr = pdevlist_conf;
		while (!found && ptr) {
			if (strcmp(ptr->pent->name, provname) == 0) {
				found = B_TRUE;
			} else {
				phead = ptr;
				ptr = ptr->next;
			}
		}

		if (found) {
			(void) list_policy_for_hard(ptr->pent->name,
			    pdevlist_conf, psoftlist_conf, pdevlist_kernel);
			if (phead == ptr) {
				pdevlist_conf = pdevlist_conf->next;
			} else {
				phead->next = ptr->next;
			}
			free_entry(ptr->pent);
			free(ptr);
		} else {
			(void) list_policy_for_hard(provname, pdevlist_conf,
			    psoftlist_conf, pdevlist_kernel);
		}
	}

	/*
	 * If there are still entries left in the pdevlist_conf list from
	 * the config file, these providers must have been detached.
	 * Should print out their policy information also.
	 */
	for (ptr = pdevlist_conf; ptr != NULL; ptr = ptr->next) {
		print_kef_policy(ptr->pent->name, ptr->pent, B_FALSE, B_TRUE);
	}

	free_entrylist(pdevlist_conf);
	free_entrylist(psoftlist_conf);
	free(pdevlist_kernel);

	return (rc);
}