/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Netscape security libraries.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1994-2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

/* To edit this file, set TABSTOPS to 4 spaces. 
 * This is not the normal NSS convention. 
 */

#include "modutil.h"
/* #include "secmodti.h"  */
#include "pk11func.h"

static PK11DefaultArrayEntry *pk11_DefaultArray = NULL;
static int pk11_DefaultArraySize = 0;

/*************************************************************************
 *
 * F i p s M o d e
 * If arg=="true", enable FIPS mode on the internal module.  If arg=="false",
 * disable FIPS mode on the internal module.
 */
Error
FipsMode(char *arg)
{
    char *internal_name;

    if(!PORT_Strcasecmp(arg, "true")) {
	if(!PK11_IsFIPS()) {
	    internal_name = PR_smprintf("%s",
		SECMOD_GetInternalModule()->commonName);
	    if(SECMOD_DeleteInternalModule(internal_name) != SECSuccess) {
		PR_fprintf(PR_STDERR, "%s\n", SECU_Strerror(PORT_GetError()));
		PR_smprintf_free(internal_name);
		PR_fprintf(PR_STDERR, errStrings[FIPS_SWITCH_FAILED_ERR]);
		return FIPS_SWITCH_FAILED_ERR;
	    }
	    PR_smprintf_free(internal_name);
	    if (!PK11_IsFIPS()) {
		PR_fprintf(PR_STDERR, errStrings[FIPS_SWITCH_FAILED_ERR]);
		return FIPS_SWITCH_FAILED_ERR;
	    }
	    PR_fprintf(PR_STDOUT, msgStrings[FIPS_ENABLED_MSG]);
	} else {
	    PR_fprintf(PR_STDERR, errStrings[FIPS_ALREADY_ON_ERR]);
	    return FIPS_ALREADY_ON_ERR;
	}
    } else if(!PORT_Strcasecmp(arg, "false")) {
	if(PK11_IsFIPS()) {
	    internal_name = PR_smprintf("%s",
		SECMOD_GetInternalModule()->commonName);
	    if(SECMOD_DeleteInternalModule(internal_name) != SECSuccess) {
		PR_fprintf(PR_STDERR, "%s\n", SECU_Strerror(PORT_GetError()));
		PR_smprintf_free(internal_name);
		PR_fprintf(PR_STDERR, errStrings[FIPS_SWITCH_FAILED_ERR]);
		return FIPS_SWITCH_FAILED_ERR;
	    }
	    PR_smprintf_free(internal_name);
	    if (PK11_IsFIPS()) {
		PR_fprintf(PR_STDERR, errStrings[FIPS_SWITCH_FAILED_ERR]);
		return FIPS_SWITCH_FAILED_ERR;
	    }
	    PR_fprintf(PR_STDOUT, msgStrings[FIPS_DISABLED_MSG]);
	} else {
	    PR_fprintf(PR_STDERR, errStrings[FIPS_ALREADY_OFF_ERR]);
	    return FIPS_ALREADY_OFF_ERR;
	}
    } else {
	PR_fprintf(PR_STDERR, errStrings[INVALID_FIPS_ARG]);
	return INVALID_FIPS_ARG;
    }

    return SUCCESS;
}

/*************************************************************************
 *
 * C h k F i p s M o d e
 * If arg=="true", verify FIPS mode is enabled on the internal module.  
 * If arg=="false", verify FIPS mode is disabled on the internal module.
 */
Error
ChkFipsMode(char *arg)
{
    if(!PORT_Strcasecmp(arg, "true")) {
	if (PK11_IsFIPS()) {
	    PR_fprintf(PR_STDOUT, msgStrings[FIPS_ENABLED_MSG]);
	} else {
	    PR_fprintf(PR_STDOUT, msgStrings[FIPS_DISABLED_MSG]);
	    return FIPS_SWITCH_FAILED_ERR;
	}

    } else if(!PORT_Strcasecmp(arg, "false")) {
	if(!PK11_IsFIPS()) {
	    PR_fprintf(PR_STDOUT, msgStrings[FIPS_DISABLED_MSG]);
	} else {
	    PR_fprintf(PR_STDOUT, msgStrings[FIPS_ENABLED_MSG]);
	    return FIPS_SWITCH_FAILED_ERR;
	}
    } else {
	PR_fprintf(PR_STDERR, errStrings[INVALID_FIPS_ARG]);
	return INVALID_FIPS_ARG;
    }

    return SUCCESS;
}

/************************************************************************
 * Cipher and Mechanism name-bitmask translation tables
 */

typedef struct {
    const char *name;
    const unsigned long mask;
} MaskString;

static const MaskString mechanismStrings[] = {
    {"RSA", PUBLIC_MECH_RSA_FLAG},
    {"DSA", PUBLIC_MECH_DSA_FLAG},
    {"RC2", PUBLIC_MECH_RC2_FLAG},
    {"RC4", PUBLIC_MECH_RC4_FLAG},
    {"RC5", PUBLIC_MECH_RC5_FLAG},
    {"DES", PUBLIC_MECH_DES_FLAG},
    {"DH", PUBLIC_MECH_DH_FLAG},
    {"FORTEZZA", PUBLIC_MECH_FORTEZZA_FLAG},
    {"SHA1", PUBLIC_MECH_SHA1_FLAG},
    {"MD5", PUBLIC_MECH_MD5_FLAG},
    {"MD2", PUBLIC_MECH_MD2_FLAG},
    {"SSL", PUBLIC_MECH_SSL_FLAG},
    {"TLS", PUBLIC_MECH_TLS_FLAG},
    {"AES", PUBLIC_MECH_AES_FLAG},
    {"CAMELLIA", PUBLIC_MECH_CAMELLIA_FLAG},
    {"SHA256", PUBLIC_MECH_SHA256_FLAG},
    {"SHA512", PUBLIC_MECH_SHA512_FLAG},
    {"RANDOM", PUBLIC_MECH_RANDOM_FLAG},
    {"FRIENDLY", PUBLIC_MECH_FRIENDLY_FLAG}
};
static const int numMechanismStrings =
    sizeof(mechanismStrings) / sizeof(mechanismStrings[0]);

static const MaskString cipherStrings[] = {
    {"FORTEZZA", PUBLIC_CIPHER_FORTEZZA_FLAG}
};
static const int numCipherStrings =
    sizeof(cipherStrings) / sizeof(cipherStrings[0]);

/* Maximum length of a colon-separated list of all the strings in an 
 * array. */
#define MAX_STRING_LIST_LEN 240    /* or less */

/************************************************************************
 * 
 * g e t F l a g s F r o m S t r i n g
 *
 * Parses a mechanism list passed on the command line and converts it
 * to an unsigned long bitmask.
 * string is a colon-separated string of constants
 * array is an array of MaskStrings.
 * elements is the number of elements in array.
 */
static unsigned long
getFlagsFromString(char *string, const MaskString array[], int elements)
{
    unsigned long ret = 0;
    short i = 0;
    char *cp;
    char *buf;
    char *end;

    if(!string || !string[0]) {
	return ret;
    }

    /* Make a temporary copy of the string */
    buf = PR_Malloc(strlen(string)+1);
    if(!buf) {
	out_of_memory();
    }
    strcpy(buf, string);

    /* Look at each element of the list passed in */
    for(cp=buf; cp && *cp; cp = (end ? end+1 : NULL) ) {
	/* Look at the string up to the next colon */
	end = strchr(cp, ':');
	if(end) {
	    *end = '\0';
	}

	/* Find which element this is */
	for(i=0; i < elements; i++) {
	    if( !PORT_Strcasecmp(cp, array[i].name) ) {
		break;
	    }
	}
	if(i == elements) {
	    /* Skip a bogus string, but print a warning message */
	    PR_fprintf(PR_STDERR, errStrings[INVALID_CONSTANT_ERR], cp);
	    continue;
	}
	ret |= array[i].mask;
    }

    PR_Free(buf);
    return ret;
}

/**********************************************************************
 *
 * g e t S t r i n g F r o m F l a g s
 * 
 * The return string's memory is owned by this function.  Copy it
 * if you need it permanently or you want to change it.
 */
static char *
getStringFromFlags(unsigned long flags, const MaskString array[], int elements)
{
    static char buf[MAX_STRING_LIST_LEN];
    int i;
    int count=0;

    buf[0] = '\0';
    for(i=0; i<elements; i++) {
	if( flags & array[i].mask ) {
	    ++count;
	    if(count!=1) {
		strcat(buf, ":");
	    }
	    strcat(buf, array[i].name);
	}
    }
    return buf;
}

/**********************************************************************
 *
 * A d d M o d u l e
 *
 * Add the named module, with the given library file, ciphers, and
 * default mechanism flags
 */
Error
AddModule(char *moduleName, char *libFile, char *cipherString,
    char *mechanismString, char* modparms)
{
    unsigned long ciphers;
    unsigned long mechanisms;
    SECStatus status;

    mechanisms =
	getFlagsFromString(mechanismString, mechanismStrings,
			   numMechanismStrings);
    ciphers =
	getFlagsFromString(cipherString, cipherStrings, numCipherStrings);

    status =
	SECMOD_AddNewModuleEx(moduleName, libFile,
		  SECMOD_PubMechFlagstoInternal(mechanisms),
		  SECMOD_PubCipherFlagstoInternal(ciphers),
		  modparms, NULL );

    if(status != SECSuccess) {
	char* errtxt=NULL;
	PRInt32 copied = 0;
	if (PR_GetErrorTextLength()) {
	    errtxt = PR_Malloc(PR_GetErrorTextLength());
	    copied = PR_GetErrorText(errtxt);
	}
	if (copied && errtxt) {
	    PR_fprintf(PR_STDERR, errStrings[ADD_MODULE_FAILED_ERR], 
		       moduleName, errtxt);
	    PR_Free(errtxt);
	} else {
	    PR_fprintf(PR_STDERR, errStrings[ADD_MODULE_FAILED_ERR], 
		       moduleName, SECU_Strerror(PORT_GetError()));
	}
	return ADD_MODULE_FAILED_ERR;
    } else {
	PR_fprintf(PR_STDOUT, msgStrings[ADD_MODULE_SUCCESS_MSG], moduleName);
	return SUCCESS;
    }
}

/***********************************************************************
 *
 * D e l e t e M o d u l e
 *
 * Deletes the named module from the database.
 */
Error
DeleteModule(char *moduleName)
{
    SECStatus status;
    int type;
    
    status = SECMOD_DeleteModule(moduleName, &type);

    if(status != SECSuccess) {
	if(type == SECMOD_FIPS || type == SECMOD_INTERNAL) {
	    PR_fprintf(PR_STDERR, errStrings[DELETE_INTERNAL_ERR]);
	    return DELETE_INTERNAL_ERR;
	} else {
	    PR_fprintf(PR_STDERR, errStrings[DELETE_FAILED_ERR], moduleName);
	    return DELETE_FAILED_ERR;
	}
    }

    PR_fprintf(PR_STDOUT, msgStrings[DELETE_SUCCESS_MSG], moduleName);
    return SUCCESS;
}

/************************************************************************
 *
 * R a w L i s t M o d u l e s
 *
 * Lists all the modules in the database, along with their slots and tokens.
 */
Error
RawListModule(char *modulespec)
{
    SECMODModule *module;
    char **moduleSpecList;

    module = SECMOD_LoadModule(modulespec,NULL,PR_FALSE);
    if (module == NULL) {
	/* handle error */
	return NO_SUCH_MODULE_ERR;
    }

    moduleSpecList = SECMOD_GetModuleSpecList(module);
    if (!moduleSpecList || !moduleSpecList[0]) {
	SECU_PrintError("modutil",
			    "no specs in secmod DB");
	return NO_SUCH_MODULE_ERR;
    }

    for ( ;*moduleSpecList; moduleSpecList++) {
	printf("%s\n\n",*moduleSpecList);
    }

    return SUCCESS;
}

Error
RawAddModule(char *dbmodulespec, char *modulespec)
{
    SECMODModule *module;
    SECMODModule *dbmodule;


    dbmodule = SECMOD_LoadModule(dbmodulespec,NULL,PR_TRUE);
    if (dbmodule == NULL) {
	/* handle error */
	return NO_SUCH_MODULE_ERR;
    }

    module = SECMOD_LoadModule(modulespec,dbmodule,PR_FALSE);
    if (module == NULL) {
	/* handle error */
	return NO_SUCH_MODULE_ERR;
    }

    if( SECMOD_UpdateModule(module) != SECSuccess ) {
	PR_fprintf(PR_STDERR, errStrings[UPDATE_MOD_FAILED_ERR], modulespec);
	return UPDATE_MOD_FAILED_ERR;
    }
    return SUCCESS;
}

static void
printModule(SECMODModule *module, int *count) 
{
    int slotCount = module->loaded ? module->slotCount : 0;
    int i;

    if ((*count)++) {
        PR_fprintf(PR_STDOUT,"\n");
    }
        PR_fprintf(PR_STDOUT, "%3d. %s\n", *count, module->commonName);

    if (module->dllName) {
        PR_fprintf(PR_STDOUT, "\tlibrary name: %s\n", module->dllName);
    }

    if (slotCount == 0) {
        PR_fprintf(PR_STDOUT,
	"\t slots: There are no slots attached to this module\n");
    } else {
        PR_fprintf(PR_STDOUT, "\t slots: %d slot%s attached\n",
		slotCount, (slotCount==1 ? "" : "s") );
    }

    if (module->loaded == 0) {
        PR_fprintf(PR_STDOUT, "\tstatus: Not loaded\n");
    } else {
        PR_fprintf(PR_STDOUT, "\tstatus: loaded\n");
    }

    /* Print slot and token names */
    for (i = 0; i < slotCount; i++) {
        PK11SlotInfo *slot = module->slots[i];

        PR_fprintf(PR_STDOUT, "\n");
        PR_fprintf(PR_STDOUT, "\t slot: %s\n", PK11_GetSlotName(slot));
        PR_fprintf(PR_STDOUT, "\ttoken: %s\n", PK11_GetTokenName(slot));
    }
    return;
}

/************************************************************************
 *
 * L i s t M o d u l e s
 *
 * Lists all the modules in the database, along with their slots and tokens.
 */
Error
ListModules()
{
    SECMODListLock *lock;
    SECMODModuleList *list;
    SECMODModuleList *deadlist;
    SECMODModuleList *mlp;
    Error ret=UNSPECIFIED_ERR;
    int count = 0;

    lock = SECMOD_GetDefaultModuleListLock();
    if(!lock) {
	PR_fprintf(PR_STDERR, errStrings[NO_LIST_LOCK_ERR]);
	return NO_LIST_LOCK_ERR;
    }

    SECMOD_GetReadLock(lock);

    list = SECMOD_GetDefaultModuleList();
    deadlist = SECMOD_GetDeadModuleList();
    if (!list && !deadlist) {
	PR_fprintf(PR_STDERR, errStrings[NO_MODULE_LIST_ERR]);
	ret = NO_MODULE_LIST_ERR;
	goto loser;
    }

    PR_fprintf(PR_STDOUT,
	"\nListing of PKCS #11 Modules\n"
	"-----------------------------------------------------------\n");
    
    for(mlp=list; mlp != NULL; mlp = mlp->next) {
	printModule(mlp->module, &count);
    }
    for (mlp=deadlist; mlp != NULL; mlp = mlp->next) {
	printModule(mlp->module, &count);
    }


    PR_fprintf(PR_STDOUT,
	"-----------------------------------------------------------\n");

    ret = SUCCESS;

loser:
    SECMOD_ReleaseReadLock(lock);
    return ret;
}

/* Strings describing PK11DisableReasons */
static char *disableReasonStr[] = {
    "no reason",
    "user disabled",
    "could not initialize token",
    "could not verify token",
    "token not present"
};
static int numDisableReasonStr =
    sizeof(disableReasonStr) / sizeof(disableReasonStr[0]);

/***********************************************************************
 *
 * L i s t M o d u l e
 *
 * Lists detailed information about the named module.
 */
Error
ListModule(char *moduleName)
{
    SECMODModule *module = NULL;
    PK11SlotInfo *slot;
    int slotnum;
    CK_INFO modinfo;
    CK_SLOT_INFO slotinfo;
    CK_TOKEN_INFO tokeninfo;
    char *ciphers, *mechanisms;
    PK11DisableReasons reason;
    Error  rv = SUCCESS;

    if(!moduleName) {
	return SUCCESS;
    }

    module = SECMOD_FindModule(moduleName);
    if(!module) {
	PR_fprintf(PR_STDERR, errStrings[NO_SUCH_MODULE_ERR], moduleName);
	rv = NO_SUCH_MODULE_ERR;
        goto loser;
    }

    if ((module->loaded) && 
			(PK11_GetModInfo(module, &modinfo) != SECSuccess)) {
	PR_fprintf(PR_STDERR, errStrings[MOD_INFO_ERR], moduleName);
	rv = MOD_INFO_ERR;
        goto loser;
    }

    /* Module info */
    PR_fprintf(PR_STDOUT, 
	"\n-----------------------------------------------------------\n");
    PR_fprintf(PR_STDOUT, "Name: %s\n", module->commonName);
    if(module->internal || !module->dllName) {
	PR_fprintf(PR_STDOUT, "Library file: **Internal ONLY module**\n");
    } else {
	PR_fprintf(PR_STDOUT, "Library file: %s\n", module->dllName);
    }

    if (module->loaded) {
	PR_fprintf(PR_STDOUT, "Manufacturer: %.32s\n", modinfo.manufacturerID);
	PR_fprintf(PR_STDOUT, "Description: %.32s\n", modinfo.libraryDescription);
	PR_fprintf(PR_STDOUT, "PKCS #11 Version %d.%d\n",
	modinfo.cryptokiVersion.major, modinfo.cryptokiVersion.minor);
	PR_fprintf(PR_STDOUT, "Library Version: %d.%d\n",
	modinfo.libraryVersion.major, modinfo.libraryVersion.minor);
    } else {
	PR_fprintf(PR_STDOUT, "* Module not loaded\n");
    }
    /* Get cipher and mechanism flags */
    ciphers = getStringFromFlags(module->ssl[0], cipherStrings,
      numCipherStrings);
    if(ciphers[0] == '\0') {
	ciphers = "None";
    }
    PR_fprintf(PR_STDOUT, "Cipher Enable Flags: %s\n", ciphers);
    mechanisms = NULL;
    if (module->slotCount > 0) {
	mechanisms = getStringFromFlags(
	    PK11_GetDefaultFlags(module->slots[0]),
	    mechanismStrings, numMechanismStrings);
    }
    if ((mechanisms==NULL) || (mechanisms[0] =='\0')) {
	mechanisms = "None";
    }
    PR_fprintf(PR_STDOUT, "Default Mechanism Flags: %s\n", mechanisms);

#define PAD "  "

    /* Loop over each slot */
    for (slotnum=0; slotnum < module->slotCount; slotnum++) {
	slot = module->slots[slotnum];
	if (PK11_GetSlotInfo(slot, &slotinfo) != SECSuccess) {
	    PR_fprintf(PR_STDERR, errStrings[SLOT_INFO_ERR],
		PK11_GetSlotName(slot));
	    rv = SLOT_INFO_ERR;
	    continue;
	}

	/* Slot Info */
	PR_fprintf(PR_STDOUT, "\n"PAD"Slot: %s\n", PK11_GetSlotName(slot));
	mechanisms = getStringFromFlags(PK11_GetDefaultFlags(slot),
	    mechanismStrings, numMechanismStrings);
	if(mechanisms[0] =='\0') {
	     mechanisms = "None";
	}
	PR_fprintf(PR_STDOUT, PAD"Slot Mechanism Flags: %s\n", mechanisms);
	PR_fprintf(PR_STDOUT, PAD"Manufacturer: %.32s\n",
	    slotinfo.manufacturerID);
	if (PK11_IsHW(slot)) {
	    PR_fprintf(PR_STDOUT, PAD"Type: Hardware\n");
	} else {
	    PR_fprintf(PR_STDOUT, PAD"Type: Software\n");
	}
	PR_fprintf(PR_STDOUT, PAD"Version Number: %d.%d\n",
	    slotinfo.hardwareVersion.major, slotinfo.hardwareVersion.minor);
	PR_fprintf(PR_STDOUT, PAD"Firmware Version: %d.%d\n",
	    slotinfo.firmwareVersion.major, slotinfo.firmwareVersion.minor);
	if (PK11_IsDisabled(slot)) {
	    reason  = PK11_GetDisabledReason(slot);
	    if(reason < numDisableReasonStr) {
		PR_fprintf(PR_STDOUT, PAD"Status: DISABLED (%s)\n",
		  disableReasonStr[reason]);
	    } else {
		PR_fprintf(PR_STDOUT, PAD"Status: DISABLED\n");
	    }
	} else {
	    PR_fprintf(PR_STDOUT, PAD"Status: Enabled\n");
	}

	if(PK11_GetTokenInfo(slot, &tokeninfo) != SECSuccess) {
	    PR_fprintf(PR_STDERR, errStrings[TOKEN_INFO_ERR],
	      PK11_GetTokenName(slot));
	    rv = TOKEN_INFO_ERR;
	    continue;
	}

	/* Token Info */
	PR_fprintf(PR_STDOUT, PAD"Token Name: %.32s\n",
	    tokeninfo.label);
	PR_fprintf(PR_STDOUT, PAD"Token Manufacturer: %.32s\n",
	    tokeninfo.manufacturerID);
	PR_fprintf(PR_STDOUT, PAD"Token Model: %.16s\n", tokeninfo.model);
	PR_fprintf(PR_STDOUT, PAD"Token Serial Number: %.16s\n",
	    tokeninfo.serialNumber);
	PR_fprintf(PR_STDOUT, PAD"Token Version: %d.%d\n",
	    tokeninfo.hardwareVersion.major, tokeninfo.hardwareVersion.minor);
	PR_fprintf(PR_STDOUT, PAD"Token Firmware Version: %d.%d\n",
	    tokeninfo.firmwareVersion.major, tokeninfo.firmwareVersion.minor);
	if(tokeninfo.flags & CKF_WRITE_PROTECTED) {
	    PR_fprintf(PR_STDOUT, PAD"Access: Write Protected\n");
	} else {
	    PR_fprintf(PR_STDOUT, PAD"Access: NOT Write Protected\n");
	}
	if(tokeninfo.flags & CKF_LOGIN_REQUIRED) {
	    PR_fprintf(PR_STDOUT, PAD"Login Type: Login required\n");
	} else {
	    PR_fprintf(PR_STDOUT, PAD
	      "Login Type: Public (no login required)\n");
	}
	if(tokeninfo.flags & CKF_USER_PIN_INITIALIZED) {
	    PR_fprintf(PR_STDOUT, PAD"User Pin: Initialized\n");
	} else {
	    PR_fprintf(PR_STDOUT, PAD"User Pin: NOT Initialized\n");
	}
    }
    PR_fprintf(PR_STDOUT, 
	"\n-----------------------------------------------------------\n");
loser:
    if (module) {
        SECMOD_DestroyModule(module);
    }
    return rv;
}

/************************************************************************
 *
 * C h a n g e P W
 */
Error
ChangePW(char *tokenName, char *pwFile, char *newpwFile)
{
    char *oldpw=NULL, *newpw=NULL, *newpw2=NULL;
    PK11SlotInfo *slot;
    Error ret=UNSPECIFIED_ERR;
    PRBool matching;

    slot = PK11_FindSlotByName(tokenName);
    if(!slot) {
	PR_fprintf(PR_STDERR, errStrings[NO_SUCH_TOKEN_ERR], tokenName);
	return NO_SUCH_TOKEN_ERR;
    }

    /* Get old password */
    if(! PK11_NeedUserInit(slot)) {
	if(pwFile) {
	    oldpw = SECU_FilePasswd(NULL, PR_FALSE, pwFile);
	    if(PK11_CheckUserPassword(slot, oldpw) != SECSuccess) {
		PR_fprintf(PR_STDERR, errStrings[BAD_PW_ERR]);
		ret=BAD_PW_ERR;
		goto loser;
	    }
	} else {
	    for(matching=PR_FALSE; !matching; ) {
		oldpw = SECU_GetPasswordString(NULL, "Enter old password: ");
		if(PK11_CheckUserPassword(slot, oldpw) == SECSuccess) {
		    matching = PR_TRUE;
		} else {
		    PR_fprintf(PR_STDOUT, msgStrings[BAD_PW_MSG]);
		}
	    }
	}
    }

    /* Get new password */
    if(newpwFile) {
	newpw = SECU_FilePasswd(NULL, PR_FALSE, newpwFile);
    } else {
	for(matching=PR_FALSE; !matching; ) {
	    newpw = SECU_GetPasswordString(NULL, "Enter new password: ");
	    newpw2 = SECU_GetPasswordString(NULL, "Re-enter new password: ");
	    if(strcmp(newpw, newpw2)) {
		PR_fprintf(PR_STDOUT, msgStrings[PW_MATCH_MSG]);
	    } else {
		matching = PR_TRUE;
	    }
	}
    }

    /* Change the password */
    if(PK11_NeedUserInit(slot)) {
	if(PK11_InitPin(slot, NULL /*ssopw*/, newpw) != SECSuccess) {
	    PR_fprintf(PR_STDERR, errStrings[CHANGEPW_FAILED_ERR], tokenName);
	    ret = CHANGEPW_FAILED_ERR;
	    goto loser;
	}
    } else {
	if(PK11_ChangePW(slot, oldpw, newpw) != SECSuccess) {
	    PR_fprintf(PR_STDERR, errStrings[CHANGEPW_FAILED_ERR], tokenName);
	    ret = CHANGEPW_FAILED_ERR;
	    goto loser;
	}
    }

    PR_fprintf(PR_STDOUT, msgStrings[CHANGEPW_SUCCESS_MSG], tokenName);
    ret = SUCCESS;

loser:
    if(oldpw) {
	memset(oldpw, 0, strlen(oldpw));
	PORT_Free(oldpw);
    }
    if(newpw) {
	memset(newpw, 0, strlen(newpw));
	PORT_Free(newpw);
    }
    if(newpw2) {
	memset(newpw2, 0, strlen(newpw2));
	PORT_Free(newpw2);
    }
    PK11_FreeSlot(slot);

    return ret;
}

/***********************************************************************
 *
 * E n a b l e M o d u l e
 *
 * If enable==PR_TRUE, enables the module or slot.
 * If enable==PR_FALSE, disables the module or slot.
 * moduleName is the name of the module.
 * slotName is the name of the slot.  It is optional.
 */
Error
EnableModule(char *moduleName, char *slotName, PRBool enable)
{
    int i;
    SECMODModule *module = NULL;
    PK11SlotInfo *slot = NULL;
    PRBool found = PR_FALSE;
    Error rv;

    module = SECMOD_FindModule(moduleName);
    if(!module) {
	PR_fprintf(PR_STDERR, errStrings[NO_SUCH_MODULE_ERR], moduleName);
	rv = NO_SUCH_MODULE_ERR;
        goto loser;
    }

    for(i=0; i < module->slotCount; i++) {
	slot = module->slots[i];
	if(slotName && strcmp(PK11_GetSlotName(slot), slotName)) {
		/* Not the right slot */
		continue;
	}
	if(enable) {
	    if(! PK11_UserEnableSlot(slot)) {
		PR_fprintf(PR_STDERR, errStrings[ENABLE_FAILED_ERR],
		    "enable", PK11_GetSlotName(slot));
		rv = ENABLE_FAILED_ERR;
                goto loser;
	    } else {
		found = PR_TRUE;
		PR_fprintf(PR_STDOUT, msgStrings[ENABLE_SUCCESS_MSG],
		    PK11_GetSlotName(slot), "enabled");
	    }
	} else {
	    if(! PK11_UserDisableSlot(slot)) {
		PR_fprintf(PR_STDERR, errStrings[ENABLE_FAILED_ERR],
		    "disable", PK11_GetSlotName(slot));
		rv = ENABLE_FAILED_ERR;
                goto loser;
	    } else {
		found = PR_TRUE;
		PR_fprintf(PR_STDOUT, msgStrings[ENABLE_SUCCESS_MSG],
		    PK11_GetSlotName(slot), "disabled");
	    }
	}
    }

    if(slotName && !found) {
	PR_fprintf(PR_STDERR, errStrings[NO_SUCH_SLOT_ERR], slotName);
	rv = NO_SUCH_SLOT_ERR;
        goto loser;
    }

    /* Delete and re-add module to save changes */
    if( SECMOD_UpdateModule(module) != SECSuccess ) {
	PR_fprintf(PR_STDERR, errStrings[UPDATE_MOD_FAILED_ERR], moduleName);
	rv = UPDATE_MOD_FAILED_ERR;
        goto loser;
    }

    rv = SUCCESS;
loser:
    if (module) {
        SECMOD_DestroyModule(module);
    }
    return rv;
}

/*************************************************************************
 *
 * S e t D e f a u l t M o d u l e
 *
 */
Error
SetDefaultModule(char *moduleName, char *slotName, char *mechanisms)
{
    SECMODModule *module = NULL;
    PK11SlotInfo *slot;
    int s, i;
    unsigned long mechFlags = getFlagsFromString(mechanisms, mechanismStrings,
	numMechanismStrings);
    PRBool found = PR_FALSE;
    Error errcode = UNSPECIFIED_ERR;

    if (pk11_DefaultArray == NULL) {
	pk11_DefaultArray = PK11_GetDefaultArray(&pk11_DefaultArraySize);
	if (pk11_DefaultArray == NULL) {
	    /* should assert. This shouldn't happen */
	    goto loser;
	}
    }

    mechFlags =  SECMOD_PubMechFlagstoInternal(mechFlags);

    module = SECMOD_FindModule(moduleName);
    if(!module) {
	PR_fprintf(PR_STDERR, errStrings[NO_SUCH_MODULE_ERR], moduleName);
	errcode = NO_SUCH_MODULE_ERR;
	goto loser;
    }

    /* Go through each slot */
    for(s=0; s < module->slotCount; s++) {
	slot = module->slots[s];

	if ((slotName != NULL) &&
	    !((strcmp(PK11_GetSlotName(slot),slotName) == 0) ||
	    (strcmp(PK11_GetTokenName(slot),slotName) == 0)) ) {
	    /* we are only interested in changing the one slot */
	    continue;
	}

	found = PR_TRUE;

	/* Go through each mechanism */
	for(i=0; i < pk11_DefaultArraySize; i++) {
	    if(pk11_DefaultArray[i].flag & mechFlags) {
		/* Enable this default mechanism */
		PK11_UpdateSlotAttribute(slot, &(pk11_DefaultArray[i]),
		    PR_TRUE);
	    }
	}
    }
    if (slotName && !found) {
	PR_fprintf(PR_STDERR, errStrings[NO_SUCH_SLOT_ERR], slotName);
	errcode = NO_SUCH_SLOT_ERR;
	goto loser;
    }

    /* Delete and re-add module to save changes */
    if( SECMOD_UpdateModule(module) != SECSuccess ) {
	PR_fprintf(PR_STDERR, errStrings[DEFAULT_FAILED_ERR],
	  moduleName);
	errcode = DEFAULT_FAILED_ERR;
	goto loser;
    }

    PR_fprintf(PR_STDOUT, msgStrings[DEFAULT_SUCCESS_MSG]);

    errcode = SUCCESS;
loser:
    if (module) {
        SECMOD_DestroyModule(module);
    }
    return errcode;
}

/************************************************************************
 *
 * U n s e t D e f a u l t M o d u l e
 */
Error
UnsetDefaultModule(char *moduleName, char *slotName, char *mechanisms)
{
    SECMODModule * module = NULL;
    PK11SlotInfo *slot;
    int s, i;
    unsigned long mechFlags = getFlagsFromString(mechanisms,
	mechanismStrings, numMechanismStrings);
    PRBool found = PR_FALSE;
    Error rv;

    if (pk11_DefaultArray == NULL) {
	pk11_DefaultArray = PK11_GetDefaultArray(&pk11_DefaultArraySize);
	if (pk11_DefaultArray == NULL) {
	    /* should assert. This shouldn't happen */
	    rv = UNSPECIFIED_ERR;
            goto loser;
	}
    }

    mechFlags =  SECMOD_PubMechFlagstoInternal(mechFlags);

    module = SECMOD_FindModule(moduleName);
    if(!module) {
	PR_fprintf(PR_STDERR, errStrings[NO_SUCH_MODULE_ERR], moduleName);
	rv = NO_SUCH_MODULE_ERR;
        goto loser;
    }

    for(s=0; s < module->slotCount; s++) {
	slot = module->slots[s];
	if ((slotName != NULL) &&
	    !((strcmp(PK11_GetSlotName(slot),slotName) == 0) ||
	    (strcmp(PK11_GetTokenName(slot),slotName) == 0)) ) {
	    /* we are only interested in changing the one slot */
	    continue;
	}
	for(i=0; i < pk11_DefaultArraySize ; i++) {
	    if(pk11_DefaultArray[i].flag & mechFlags) {
		PK11_UpdateSlotAttribute(slot, &(pk11_DefaultArray[i]),
		    PR_FALSE);
	    }
	}
    }
    if (slotName && !found) {
	PR_fprintf(PR_STDERR, errStrings[NO_SUCH_SLOT_ERR], slotName);
	rv = NO_SUCH_SLOT_ERR;
        goto loser;
    }

    /* Delete and re-add module to save changes */
    if( SECMOD_UpdateModule(module) != SECSuccess ) {
	PR_fprintf(PR_STDERR, errStrings[UNDEFAULT_FAILED_ERR],
	  moduleName);
	rv = UNDEFAULT_FAILED_ERR;
        goto loser;
    }

    PR_fprintf(PR_STDOUT, msgStrings[UNDEFAULT_SUCCESS_MSG]);
    rv = SUCCESS;
loser:
    if (module) {
        SECMOD_DestroyModule(module);
    }
    return rv;
}
