diff options
| author | dinak <none@none> | 2005-06-15 19:38:38 -0700 |
|---|---|---|
| committer | dinak <none@none> | 2005-06-15 19:38:38 -0700 |
| commit | 7711facfe58561dd91d6ece0f5f41150c3956c83 (patch) | |
| tree | dd68055110e4f9562ff4b14f6d3ac9d2f76176b9 /usr/src/cmd | |
| parent | ac0f75d71d71ee22b0e3a392d3b2fa2885e11634 (diff) | |
| download | illumos-joyent-7711facfe58561dd91d6ece0f5f41150c3956c83.tar.gz | |
PSARC 2004/124 pktool enhancements to support object migration and softtoken interoperability
PSARC 2005/019 pktool(1) list/delete updates
4931202 Provide import utility from PKCS12 file to softtoken's keystore
5059459 provide utility to export-to-PKCS#12-file from softttoken
5059461 pktool(1) needs subcommands to list and delete objects in softtoken
6216772 update pktool(1) list/delete subcommands
6278459 add "tokens" subcommand to pktool(1)
6285539 E_NAME_USED_NOT_DEF2 lint error for ENGINE_load_builtin_engines
Diffstat (limited to 'usr/src/cmd')
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/Makefile | 22 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/biginteger.h | 58 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/common.c | 1005 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/common.h | 98 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/delete.c | 227 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/derparse.c | 371 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/derparse.h | 55 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/export.c | 1404 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/import.c | 945 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/list.c | 1016 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/osslcommon.c | 224 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/osslcommon.h | 50 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/p12common.c | 103 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/p12common.h | 46 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/pktool.c | 167 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/setpin.c | 198 | ||||
| -rw-r--r-- | usr/src/cmd/cmd-crypto/pktool/tokens.c | 104 |
17 files changed, 5781 insertions, 312 deletions
diff --git a/usr/src/cmd/cmd-crypto/pktool/Makefile b/usr/src/cmd/cmd-crypto/pktool/Makefile index 5cadc0ce1c..0ad8e96905 100644 --- a/usr/src/cmd/cmd-crypto/pktool/Makefile +++ b/usr/src/cmd/cmd-crypto/pktool/Makefile @@ -20,7 +20,7 @@ # CDDL HEADER END # # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -30,24 +30,38 @@ PROG = pktool OBJS = pktool.o \ common.o \ - setpin.o + derparse.o \ + osslcommon.o \ + p12common.o \ + setpin.o \ + list.o \ + delete.o \ + import.o \ + export.o \ + tokens.o include ../../Makefile.cmd +include $(SRC)/lib/openssl/Makefile.openssl SRCS = $(OBJS:%.o=%.c) POFILES = $(OBJS:%.o=%.po) POFILE = $(PROG)_msg.po +CPPFLAGS += -I. $(OPENSSL_CPPFLAGS) CFLAGS += $(CCVERBOSE) -LDLIBS += -lpkcs11 -lcryptoutil +DYNFLAGS += $(OPENSSL_DYNFLAGS) +LDFLAGS += $(OPENSSL_LDFLAGS) +LDLIBS += -lpkcs11 -lcryptoutil -lcrypto -lldap + +LINTFLAGS += $(OPENSSL_LDFLAGS) .KEEP_STATE: all : $(PROG) $(PROG) : $(OBJS) - $(LINK.c) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS) + $(LINK.c) -o $@ $(OBJS) $(DYNFLAGS) $(LDLIBS) $(POST_PROCESS) $(POFILE) : $(POFILES) diff --git a/usr/src/cmd/cmd-crypto/pktool/biginteger.h b/usr/src/cmd/cmd-crypto/pktool/biginteger.h new file mode 100644 index 0000000000..3764e47aaa --- /dev/null +++ b/usr/src/cmd/cmd-crypto/pktool/biginteger.h @@ -0,0 +1,58 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _PKTOOL_BIGINTEGER_H +#define _PKTOOL_BIGINTEGER_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <security/pkcs11t.h> + +/* + * NOTE: + * + * This is same "biginteger_t" found in both these places: + * usr/src/lib/pkcs11/pkcs11_softtoken/common/softObject.h + * usr/src/lib/pkcs11/pkcs11_kernel/common/kernelObject.h + * The BIGNUM implementation in usr/src/common/bignum does not + * meet the need. It is recommended that the biginteger_t be + * factored out of pkcs11_softtoken/pkcs11_kernel/pktool and + * the pkcs11 libraries and moved into cryptoutil.h + */ +typedef struct biginteger { + CK_BYTE *big_value; + CK_ULONG big_value_len; +} biginteger_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _PKTOOL_BIGINTEGER_H */ diff --git a/usr/src/cmd/cmd-crypto/pktool/common.c b/usr/src/cmd/cmd-crypto/pktool/common.c index 29d4605942..76685ba171 100644 --- a/usr/src/cmd/cmd-crypto/pktool/common.c +++ b/usr/src/cmd/cmd-crypto/pktool/common.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -29,6 +29,9 @@ /* * This file contains the functions that are shared among * the various services this tool will ultimately provide. + * The functions in this file return PKCS#11 CK_RV errors. + * Only one session and one login per token is supported + * at this time. */ #include <stdio.h> @@ -38,50 +41,395 @@ #include <cryptoutil.h> #include <security/cryptoki.h> #include "common.h" +#include "biginteger.h" -/* Global PKCS#11 error value. */ -int pk11_errno = 0; +/* True and false for attribute templates. */ +CK_BBOOL pk_true = B_TRUE; +CK_BBOOL pk_false = B_FALSE; + +/* Local status variables. */ +static boolean_t initialized = B_FALSE; +static boolean_t session_opened = B_FALSE; +static boolean_t session_writable = B_FALSE; +static boolean_t logged_in = B_FALSE; /* - * Gets passphrase from user, caller needs to free when done. + * Perform PKCS#11 setup here. Currently only C_Initialize is required, + * along with setting/resetting state variables. */ -int -get_password(char *prompt, char **password) +CK_RV +init_pk11(void) { - char *phrase; + CK_RV rv = CKR_OK; + + cryptodebug("inside init_pk11"); + + /* If C_Initialize() already called, nothing to do here. */ + if (initialized == B_TRUE) + return (CKR_OK); - /* Prompt user for password. */ - if ((phrase = getpassphrase(prompt)) == NULL) - return (-1); + /* Reset state variables because C_Initialize() not yet done. */ + session_opened = B_FALSE; + session_writable = B_FALSE; + logged_in = B_FALSE; - /* Duplicate passphrase in separate chunk of memory */ - if ((*password = strdup(phrase)) == NULL) - return (-1); + /* Initialize PKCS#11 library. */ + cryptodebug("calling C_Initialize()"); + if ((rv = C_Initialize(NULL_PTR)) != CKR_OK && + rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) { + return (rv); + } - return (strlen(phrase)); + initialized = B_TRUE; + return (CKR_OK); } /* - * Perform any PKCS#11 setup here. Right now, this tool only - * requires C_Initialize(). Additional features planned for - * this tool will require more initialization and state info - * added here. + * Finalize PKCS#11 library and reset state variables. Open sessions, + * if any, are closed, and thereby any logins are logged out also. */ -int -init_pk11(void) +void +final_pk11(CK_SESSION_HANDLE sess) { - int rv; + cryptodebug("inside final_pk11"); - cryptodebug("inside init_pk11"); + /* If the library wasn't initialized, nothing to do here. */ + if (!initialized) + return; - /* Initialize PKCS#11 library. */ - if ((rv = C_Initialize(NULL_PTR)) != CKR_OK && - rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) { - pk11_errno = rv; - return (PK_ERR_PK11INIT); + /* Make sure the sesion is closed first. */ + close_sess(sess); + + cryptodebug("calling C_Finalize()"); + (void) C_Finalize(NULL); + initialized = B_FALSE; +} + +/* + * Create a PKCS#11 session on the given slot, and set state information. + * If session is already open, check that the read-only/read-write state + * requested matches that of the session. If it doesn't, make it so. + */ +CK_RV +open_sess(CK_SLOT_ID slot_id, CK_FLAGS sess_flags, CK_SESSION_HANDLE_PTR sess) +{ + CK_RV rv = CKR_OK; + + cryptodebug("inside open_sess"); + + /* If the session is already open, check the session flags. */ + if (session_opened) { + /* + * If requesting R/W session and it is currently R/O, + * need to close the session and reopen it R/W. The + * other cases are considered acceptable: + * sess_flags current state + * ---------- ------------- + * ~CKF_RW_SESSION !session_writable + * ~CKF_RW_SESSION session_writable + * CKF_RW_SESSION session_writable + */ + if ((sess_flags & CKF_RW_SESSION) && !session_writable) + close_sess(*sess); + else + return (CKR_OK); + } + + /* Make sure the PKCS#11 is already initialized. */ + if (!initialized) + if ((rv = init_pk11()) != CKR_OK) + return (rv); + + /* Create a session for subsequent operations. */ + cryptodebug("calling C_OpenSession()"); + if ((rv = C_OpenSession(slot_id, CKF_SERIAL_SESSION|sess_flags, + NULL, NULL, sess)) != CKR_OK) + return (rv); + session_opened = B_TRUE; + session_writable = (sess_flags & CKF_RW_SESSION) ? B_TRUE : B_FALSE; + return (CKR_OK); +} + +/* + * Close PKCS#11 session and reset state variables. Any logins are + * logged out. + */ +void +close_sess(CK_SESSION_HANDLE sess) +{ + cryptodebug("inside close_sess"); + + if (sess == NULL) { + cryptodebug("session handle is null"); + return; + } + + /* If session is already closed, nothing to do here. */ + session_writable = B_FALSE; + if (!session_opened) + return; + + /* Make sure user is logged out of token. */ + logout_token(sess); + + cryptodebug("calling C_CloseSession()"); + (void) C_CloseSession(sess); + session_opened = B_FALSE; +} + +/* + * Log user into token in given slot. If this first login ever for this + * token, the initial PIN is "changeme", C_Login() will succeed, but all + * PKCS#11 calls following the C_Login() will fail with CKR_PIN_EXPIRED. + */ +CK_RV +login_token(CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG pinlen, + CK_SESSION_HANDLE_PTR sess) +{ + CK_RV rv = CKR_OK; + + cryptodebug("inside login_token"); + + /* If already logged in, nothing to do here. */ + if (logged_in) + return (CKR_OK); + + /* Make sure we have a session first, assume R/O is enough. */ + if (!session_opened) + if ((rv = open_sess(slot_id, CKF_SERIAL_SESSION, sess)) != + CKR_OK) + return (rv); + + /* Log the user into the token. */ + cryptodebug("calling C_Login()"); + if ((rv = C_Login(*sess, CKU_USER, pin, pinlen)) != CKR_OK) { + cryptodebug("C_Login returns %s", pkcs11_strerror(rv)); + return (rv); + } + + logged_in = B_TRUE; + return (CKR_OK); +} + +/* + * Log user out of token and reset status variable. + */ +void +logout_token(CK_SESSION_HANDLE sess) +{ + cryptodebug("inside logout_token"); + + if (sess == NULL) { + cryptodebug("session handle is null"); + return; } - return (PK_ERR_NONE); + /* If already logged out, nothing to do here. */ + if (!logged_in) + return; + + cryptodebug("calling C_Logout()"); + (void) C_Logout(sess); + logged_in = B_FALSE; +} + +/* + * Shortcut function to get from an uninitialized state to user logged in. + * If the library is already initialized, the session is already opened, + * or the user is already logged in, those steps are skipped and the next + * step is checked. + */ +CK_RV +quick_start(CK_SLOT_ID slot_id, CK_FLAGS sess_flags, CK_UTF8CHAR_PTR pin, + CK_ULONG pinlen, CK_SESSION_HANDLE_PTR sess) +{ + CK_RV rv = CKR_OK; + + cryptodebug("inside quick_start"); + + /* Call open_sess() explicitly if R/W session is needed. */ + if (sess_flags & CKF_RW_SESSION) + if ((rv = open_sess(slot_id, sess_flags, sess)) != CKR_OK) + return (rv); + + if ((rv = login_token(slot_id, pin, pinlen, sess)) != CKR_OK) + return (rv); + + return (CKR_OK); +} + +/* + * Shortcut function to go from any state to uninitialized PKCS#11 library. + */ +void +quick_finish(CK_SESSION_HANDLE sess) +{ + cryptodebug("inside quick_finish"); + + /* All the needed calls are done implicitly. */ + final_pk11(sess); +} + +/* + * Gets PIN from user. Caller needs to free the returned PIN when done. + * If two prompts are given, the PIN is confirmed with second prompt. + * Note that getphassphrase() may return data in static memory area. + */ +CK_RV +get_pin(char *prompt1, char *prompt2, CK_UTF8CHAR_PTR *pin, CK_ULONG *pinlen) +{ + char *save_phrase, *phrase1, *phrase2; + + cryptodebug("inside get_pin"); + + /* Prompt user for a PIN. */ + if (prompt1 == NULL) { + cryptodebug("no passphrase prompt given"); + return (CKR_ARGUMENTS_BAD); + } + if ((phrase1 = getpassphrase(prompt1)) == NULL) { + cryptodebug("getpassphrase() failed"); + return (CKR_FUNCTION_FAILED); + } + + /* Duplicate 1st PIN in separate chunk of memory. */ + if ((save_phrase = strdup(phrase1)) == NULL) + return (CKR_HOST_MEMORY); + + /* If second prompt given, PIN confirmation is requested. */ + if (prompt2 != NULL) { + if ((phrase2 = getpassphrase(prompt2)) == NULL) { + cryptodebug("getpassphrase() confirmation failed"); + free(save_phrase); + return (CKR_FUNCTION_FAILED); + } + if (strcmp(save_phrase, phrase2) != 0) { + cryptodebug("passphrases do not match"); + free(save_phrase); + return (CKR_PIN_INCORRECT); + } + } + + *pin = (CK_UTF8CHAR_PTR)save_phrase; + *pinlen = strlen(save_phrase); + return (CKR_OK); +} + +/* + * Gets yes/no response from user. If either no prompt is supplied, a + * default prompt is used. If not message for invalid input is supplied, + * a default will not be provided. If the user provides no response, + * the input default B_TRUE == yes, B_FALSE == no is returned. + * Otherwise, B_TRUE is returned for yes, and B_FALSE for no. + */ +boolean_t +yesno(char *prompt, char *invalid, boolean_t dflt) +{ + char *response, buf[1024]; + char *yes = gettext("yes"); + char *no = gettext("no"); + + cryptodebug("inside yesno"); + + if (prompt == NULL) + prompt = gettext("Enter (y)es or (n)o? "); + + for (;;) { + /* Prompt user. */ + (void) printf("%s", prompt); + (void) fflush(stdout); + + /* Get the response. */ + if ((response = fgets(buf, sizeof (buf), stdin)) == NULL) + break; /* go to default response */ + + /* Skip any leading white space. */ + while (isspace(*response)) + response++; + if (*response == '\0') + break; /* go to default response */ + + /* Is it valid input? Return appropriately. */ + if (strncasecmp(response, yes, 1) == 0) + return (B_TRUE); + if (strncasecmp(response, no, 1) == 0) + return (B_FALSE); + + /* Indicate invalid input, and try again. */ + if (invalid != NULL) + (void) printf("%s", invalid); + } + return (dflt); +} + +/* + * Gets the list of slots which have tokens in them. Keeps adjusting + * the size of the slot list buffer until the call is successful or an + * irrecoverable error occurs. + */ +CK_RV +get_token_slots(CK_SLOT_ID_PTR *slot_list, CK_ULONG *slot_count) +{ + CK_ULONG tmp_count = 0; + CK_SLOT_ID_PTR tmp_list = NULL_PTR, tmp2_list = NULL_PTR; + int rv = CKR_OK; + + cryptodebug("inside get_token_slots"); + + if (!initialized) + if ((rv = init_pk11()) != CKR_OK) + return (rv); + + /* + * Get the slot count first because we don't know how many + * slots there are and how many of those slots even have tokens. + * Don't specify an arbitrary buffer size for the slot list; + * it may be too small (see section 11.5 of PKCS#11 spec). + * Also select only those slots that have tokens in them, + * because this tool has no need to know about empty slots. + */ + cryptodebug("calling C_GetSlotList() for slot count"); + if ((rv = C_GetSlotList(1, NULL_PTR, &tmp_count)) != CKR_OK) + return (rv); + + if (tmp_count == 0) { + cryptodebug("no slots with tokens found"); + *slot_list = NULL_PTR; + *slot_count = 0; + return (CKR_OK); + } + + /* Allocate initial space for the slot list. */ + if ((tmp_list = (CK_SLOT_ID_PTR) malloc(tmp_count * + sizeof (CK_SLOT_ID))) == NULL) + return (CKR_HOST_MEMORY); + + /* Then get the slot list itself. */ + for (;;) { + cryptodebug("calling C_GetSlotList()"); + if ((rv = C_GetSlotList(1, tmp_list, &tmp_count)) == CKR_OK) { + *slot_list = tmp_list; + *slot_count = tmp_count; + break; + } + + if (rv != CKR_BUFFER_TOO_SMALL) { + free(tmp_list); + break; + } + + /* If the number of slots grew, try again. */ + cryptodebug("number of tokens present increased"); + if ((tmp2_list = (CK_SLOT_ID_PTR) realloc(tmp_list, + tmp_count * sizeof (CK_SLOT_ID))) == NULL) { + free(tmp_list); + rv = CKR_HOST_MEMORY; + break; + } + tmp_list = tmp2_list; + } + + return (rv); } /* @@ -111,7 +459,6 @@ init_pk11(void) static int memcmp_pad_max(void *d1, uint_t d1_len, void *d2, uint_t d2_len, uint_t max_sz) { - uint_t len, extra_len; char *marker; @@ -146,18 +493,19 @@ memcmp_pad_max(void *d1, uint_t d1_len, void *d2, uint_t d2_len, uint_t max_sz) } /* - * Locate a token slot whose token matches the label, manufacturer - * ID, and serial number given. Token label must be specified, - * manufacturer ID and serial number are optional. + * Locate a token slot whose token matches the label, manufacturer ID, and + * serial number given. Token label must be specified, manufacturer ID and + * serial number are optional. When the token is located, the PIN state + * is also returned to determine if it still has the default PIN. */ -int +CK_RV find_token_slot(char *token_name, char *manuf_id, char *serial_no, CK_SLOT_ID *slot_id, CK_FLAGS *pin_state) { CK_SLOT_ID_PTR slot_list; CK_TOKEN_INFO token_info; CK_ULONG slot_count = 0; - int rv; + int rv = CKR_OK; int i; uint_t len, max_sz; boolean_t tok_match = B_FALSE, @@ -166,141 +514,570 @@ find_token_slot(char *token_name, char *manuf_id, char *serial_no, cryptodebug("inside find_token_slot"); - /* - * Get the slot count first because we don't know how many - * slots there are and how many of those slots even have tokens. - * Don't specify an arbitrary buffer size for the slot list; - * it may be too small (see section 11.5 of PKCS#11 spec). - * Also select only those slots that have tokens in them, - * because this tool has no need to know about empty slots. - */ - if ((rv = C_GetSlotList(1, NULL_PTR, &slot_count)) != CKR_OK) { - pk11_errno = rv; - return (PK_ERR_PK11SLOTS); - } + if (token_name == NULL) + return (CKR_ARGUMENTS_BAD); - if (slot_count == 0) - return (PK_ERR_NOSLOTS); /* with tokens in them */ + /* Get a list of all slots with tokens present. */ + if ((rv = get_token_slots(&slot_list, &slot_count)) != CKR_OK) + return (rv); - /* Allocate space for the slot list and get it. */ - if ((slot_list = - (CK_SLOT_ID_PTR) malloc(slot_count * sizeof (CK_SLOT_ID))) == NULL) - return (PK_ERR_NOMEMORY); - - if ((rv = C_GetSlotList(1, slot_list, &slot_count)) != CKR_OK) { - /* NOTE: can slot_count change from previous call??? */ - pk11_errno = rv; - free(slot_list); - return (PK_ERR_PK11SLOTS); - } + /* If there are no such slots, the desired token won't be found. */ + if (slot_count == 0) + return (CKR_TOKEN_NOT_PRESENT); - /* Search for the token. */ + /* Search the slot list for the token. */ for (i = 0; i < slot_count; i++) { - if ((rv = - C_GetTokenInfo(slot_list[i], &token_info)) != CKR_OK) { - cryptodebug("slot %d has no token", i); + cryptodebug("calling C_GetTokenInfo()"); + if ((rv = C_GetTokenInfo(slot_list[i], &token_info)) != + CKR_OK) { + cryptodebug("token in slot %d returns %s", i, + pkcs11_strerror(rv)); continue; } + /* See if the token label matches. */ len = strlen(token_name); max_sz = sizeof (token_info.label); if (memcmp_pad_max(&(token_info.label), max_sz, token_name, len, max_sz) == 0) tok_match = B_TRUE; - cryptodebug("slot %d:", i); - cryptodebug("\tlabel = \"%.32s\"", token_info.label); - cryptodebug("\tmanuf = \"%.32s\"", token_info.manufacturerID); - cryptodebug("\tserno = \"%.16s\"", token_info.serialNumber); - cryptodebug("\tmodel = \"%.16s\"", token_info.model); - - cryptodebug("\tCKF_USER_PIN_INITIALIZED = %s", - (token_info.flags & CKF_USER_PIN_INITIALIZED) ? - "true" : "false"); - cryptodebug("\tCKF_USER_PIN_TO_BE_CHANGED = %s", - (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED) ? - "true" : "false"); - + /* + * If manufacturer id was given, see if it actually matches. + * If no manufacturer id was given, assume match is true. + */ if (manuf_id) { len = strlen(manuf_id); max_sz = sizeof ((char *)(token_info.manufacturerID)); if (memcmp_pad_max(&(token_info.manufacturerID), max_sz, manuf_id, len, max_sz) == 0) man_match = B_TRUE; - } + } else + man_match = B_TRUE; + /* + * If serial number was given, see if it actually matches. + * If no serial number was given, assume match is true. + */ if (serial_no) { len = strlen(serial_no); max_sz = sizeof ((char *)(token_info.serialNumber)); if (memcmp_pad_max(&(token_info.serialNumber), max_sz, serial_no, len, max_sz) == 0) ser_match = B_TRUE; - } + } else + ser_match = B_TRUE; + + cryptodebug("slot %d:", i); + cryptodebug("\tlabel = \"%.32s\"%s", token_info.label, + tok_match ? " match" : ""); + cryptodebug("\tmanuf = \"%.32s\"%s", token_info.manufacturerID, + man_match ? " match" : ""); + cryptodebug("\tserno = \"%.16s\"%s", token_info.serialNumber, + ser_match ? " match" : ""); + cryptodebug("\tmodel = \"%.16s\"", token_info.model); - if (tok_match && - (manuf_id ? B_TRUE : B_FALSE) == man_match && - (serial_no ? B_TRUE : B_FALSE) == ser_match) - break; /* found it! */ + cryptodebug("\tCKF_USER_PIN_INITIALIZED = %s", + (token_info.flags & CKF_USER_PIN_INITIALIZED) ? + "true" : "false"); + cryptodebug("\tCKF_USER_PIN_TO_BE_CHANGED = %s", + (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED) ? + "true" : "false"); + + if (tok_match && man_match && ser_match) + break; /* found it! */ } + /* Scanned the whole list without finding the token. */ if (i == slot_count) { + cryptodebug("token not found"); free(slot_list); - return (PK_ERR_NOTFOUND); + return (CKR_TOKEN_NOT_PRESENT); } - cryptodebug("matched token at slot %d", i); + /* Return slot id where token was found and its PIN state. */ + cryptodebug("token found at slot %d", i); *slot_id = slot_list[i]; *pin_state = (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED); free(slot_list); - return (PK_ERR_NONE); + return (CKR_OK); } /* - * Log into the token in given slot and create a session for it. + * Constructs a fully qualified token name from its label, manufacturer ID + * (if any), and its serial number (if any). Note that the given buf must + * be big enough. Do NOT i18n/l10n. + * + * FULL_NAME_LEN is defined in common.h to be 91 because a fully qualified + * token name adds up this way: + * =32(label) + 32(manuf) + 16(serial) + 4("", ) + 4("", ) + 3("" and nul) */ -int -login_token(CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG pinlen, - CK_SESSION_HANDLE_PTR hdl) +void +full_token_name(char *token_name, char *manuf_id, char *serial_no, char *buf) { - int rv; + char *marker = buf; + int n_written = 0; + int space_left = FULL_NAME_LEN; - cryptodebug("inside login_token"); + if (!token_name) + return; + + n_written = sprintf(buf, "\"%.32s\"", token_name); + marker += n_written; + space_left -= n_written; + + n_written = sprintf(marker, ", \"%.32s\"", manuf_id ? manuf_id : ""); + marker += n_written; + space_left -= n_written; - /* Create a read-write session so we can change the PIN. */ - if ((rv = C_OpenSession(slot_id, CKF_SERIAL_SESSION|CKF_RW_SESSION, - NULL, NULL, hdl)) != CKR_OK) { - pk11_errno = rv; - return (PK_ERR_PK11SESSION); + n_written = sprintf(marker, ", \"%.16s\"", serial_no ? serial_no : ""); + marker += n_written; + space_left -= n_written; + + /* space_left should always be >= 1 */ +} + +/* + * Find how many token objects with the given label. + */ +CK_RV +find_obj_count(CK_SESSION_HANDLE sess, int obj_type, CK_BYTE *label, + CK_ULONG *count) +{ + CK_RV rv = CKR_OK; + CK_ATTRIBUTE attrs[4] = { + { CKA_TOKEN, &pk_true, sizeof (pk_true) }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 } + }; + CK_ULONG num_attrs = sizeof (attrs) / sizeof (CK_ATTRIBUTE); + CK_ULONG cur_attr = 1; /* CKA_TOKEN already set */ + CK_OBJECT_CLASS obj_class; + CK_OBJECT_HANDLE tmp_obj; + CK_ULONG obj_count = 0; + + cryptodebug("inside find_obj_count"); + + if (!session_opened || sess == NULL) { + cryptodebug("session handle is null"); + return (CKR_SESSION_HANDLE_INVALID); + } + + if (label) { + cryptodebug("object label was specified"); + attrs[cur_attr].type = CKA_LABEL; + attrs[cur_attr].pValue = label; + attrs[cur_attr].ulValueLen = strlen((char *)label); + cur_attr++; + } + + if ((obj_type & PK_PRIVATE_OBJ) && !(obj_type & PK_PUBLIC_OBJ)) { + cryptodebug("only searching for private objects"); + attrs[cur_attr].type = CKA_PRIVATE; + attrs[cur_attr].pValue = &pk_true; + attrs[cur_attr].ulValueLen = sizeof (pk_true); + cur_attr++; } /* - * If the token is newly created, there initial PIN will be "changme", - * and all subsequent PKCS#11 calls will fail with CKR_PIN_EXPIRED, - * but C_Login() will succeed. + * If "certs and all keys" is not specified, but at least either + * "certs" or some "keys" is specified, then go into this block. + * If all certs and keys were specified, there's no point in + * putting that fact in the attribute template -- leave that open, + * and all certs and keys will be matched automatically. + * In other words, only if at least one of 0x10,0x20,0x40,0x80 + * bits is off, go into this code block. + * + * NOTE: For now, only one of cert or key types is allowed. + * This needs to change in the future. */ - if ((rv = C_Login(*hdl, CKU_USER, pin, pinlen)) != CKR_OK) { - pk11_errno = rv; - (void) C_CloseSession(*hdl); - cryptodebug("C_Login returns %s", pkcs11_strerror(rv)); - if (rv == CKR_USER_PIN_NOT_INITIALIZED) - return (PK_ERR_CHANGEPIN); - return (PK_ERR_PK11LOGIN); + if ((obj_type & (PK_CERT_OBJ|PK_KEY_OBJ)) != (PK_CERT_OBJ|PK_KEY_OBJ) && + ((obj_type & PK_CERT_OBJ) || (obj_type & PK_KEY_OBJ))) { + if (obj_type & PK_CERT_OBJ) { + cryptodebug("only searching for certificates"); + obj_class = CKO_CERTIFICATE; + } else if (obj_type & PK_PRIKEY_OBJ) { + cryptodebug("only searching for private keys"); + obj_class = CKO_PRIVATE_KEY; + } else if (obj_type & PK_PUBKEY_OBJ) { + cryptodebug("only searching for public keys"); + obj_class = CKO_PUBLIC_KEY; + } else if (obj_type & PK_SECKEY_OBJ) { + cryptodebug("only searching for secret keys"); + obj_class = CKO_SECRET_KEY; + } + + attrs[cur_attr].type = CKA_CLASS; + attrs[cur_attr].pValue = &obj_class; + attrs[cur_attr].ulValueLen = sizeof (CK_OBJECT_CLASS); + cur_attr++; + } + + /* + * This can't happen now. When finding objects is enhanced in the + * future. this could lead to buffer overruns. + */ + if (cur_attr > num_attrs) + cryptodebug("internal error: attr template overrun"); + + cryptodebug("calling C_FindObjectsInit"); + if ((rv = C_FindObjectsInit(sess, attrs, cur_attr)) != CKR_OK) + return (rv); + + /* Look for the object, checking if there are more than one. */ + cryptodebug("calling C_FindObjects"); + for (*count = 0; /* empty */; (*count)++) { + if ((rv = C_FindObjects(sess, &tmp_obj, 1, &obj_count)) != + CKR_OK) + break; + + /* No more found. */ + if (obj_count == 0) + break; + } + + cryptodebug("%d matching objects found", *count); + + cryptodebug("calling C_FindObjectsFinal"); + (void) C_FindObjectsFinal(sess); + return (rv); +} + +/* + * Find the token object with the given label. + */ +CK_RV +find_objs(CK_SESSION_HANDLE sess, int obj_type, CK_BYTE *label, + CK_OBJECT_HANDLE_PTR *obj, CK_ULONG *count) +{ + CK_RV rv = CKR_OK; + CK_ATTRIBUTE attrs[4] = { + { CKA_TOKEN, &pk_true, sizeof (pk_true) }, + { 0, NULL, 0 }, + { 0, NULL, 0 }, + { 0, NULL, 0 } + }; + CK_ULONG num_attrs = sizeof (attrs) / sizeof (CK_ATTRIBUTE); + CK_ULONG cur_attr = 1; /* CKA_TOKEN already set */ + CK_OBJECT_CLASS obj_class; + CK_OBJECT_HANDLE tmp_obj; + CK_ULONG obj_count = 0; + int i; + + cryptodebug("inside find_obj"); + + if ((rv = find_obj_count(sess, obj_type, label, count)) != CKR_OK) + return (rv); + + if (*count == 0) + return (CKR_OK); + + if ((*obj = (CK_OBJECT_HANDLE_PTR) malloc((*count) * + sizeof (CK_OBJECT_HANDLE))) == NULL) { + cryptodebug("no memory for found object"); + return (CKR_HOST_MEMORY); + } + + if (label) { + cryptodebug("object label was specified"); + attrs[cur_attr].type = CKA_LABEL; + attrs[cur_attr].pValue = label; + attrs[cur_attr].ulValueLen = strlen((char *)label); + cur_attr++; + } + + if ((obj_type & PK_PRIVATE_OBJ) && !(obj_type & PK_PUBLIC_OBJ)) { + cryptodebug("only searching for private objects"); + attrs[cur_attr].type = CKA_PRIVATE; + attrs[cur_attr].pValue = &pk_true; + attrs[cur_attr].ulValueLen = sizeof (pk_true); + cur_attr++; + } + + /* + * If "certs and all keys" is not specified, but at least either + * "certs" or some "keys" is specified, then go into this block. + * If all certs and keys were specified, there's no point in + * putting that fact in the attribute template -- leave that open, + * and all certs and keys will be matched automatically. + * In other words, only if at least one of 0x10,0x20,0x40,0x80 + * bits is off, go into this code block. + * + * NOTE: For now, only one of cert or key types is allowed. + * This needs to change in the future. + */ + if ((obj_type & (PK_CERT_OBJ|PK_KEY_OBJ)) != (PK_CERT_OBJ|PK_KEY_OBJ) && + ((obj_type & PK_CERT_OBJ) || (obj_type & PK_KEY_OBJ))) { + if (obj_type & PK_CERT_OBJ) { + cryptodebug("only searching for certificates"); + obj_class = CKO_CERTIFICATE; + } else if (obj_type & PK_PRIKEY_OBJ) { + cryptodebug("only searching for private keys"); + obj_class = CKO_PRIVATE_KEY; + } else if (obj_type & PK_PUBKEY_OBJ) { + cryptodebug("only searching for public keys"); + obj_class = CKO_PUBLIC_KEY; + } else if (obj_type & PK_SECKEY_OBJ) { + cryptodebug("only searching for secret keys"); + obj_class = CKO_SECRET_KEY; + } + + attrs[cur_attr].type = CKA_CLASS; + attrs[cur_attr].pValue = &obj_class; + attrs[cur_attr].ulValueLen = sizeof (CK_OBJECT_CLASS); + cur_attr++; + } + + /* + * This can't happen now. When finding objects is enhanced in the + * future. this could lead to buffer overruns. + */ + if (cur_attr > num_attrs) + cryptodebug("internal error: attr template overrun"); + + cryptodebug("calling C_FindObjectsInit"); + if ((rv = C_FindObjectsInit(sess, attrs, cur_attr)) != CKR_OK) { + free(*obj); + return (rv); + } + + /* + * Find all the matching objects. The loop goes 1 more beyond + * the number of objects found to determine if any new objects + * were created since the time the object count was done. + */ + cryptodebug("calling C_FindObjects"); + for (i = 0; i < (*count) + 1; i++) { + if ((rv = C_FindObjects(sess, &tmp_obj, 1, &obj_count)) != + CKR_OK) + break; + + /* No more found. */ + if (obj_count == 0) + break; + + /* + * Save the object in the list being created, as long as + * we don't overrun the size of the list. + */ + if (i < *count) + (*obj)[i] = tmp_obj; + else + cryptodebug("number of objects changed since last count"); + } + + if (rv != CKR_OK) { + free(*obj); + } else { + /* + * There are three cases to handle: (1) fewer objects were + * found than originally counted => change *count to the + * smaller number; (2) the number of objects found matches + * the number originally counted => do nothing; (3) more + * objects found than originally counted => list passed + * in is too small to contain the extra object(s), flag + * that in the debug output but don't change number of + * objects returned. The caller can double-check by + * calling find_obj_count() after this function to make + * sure the numbers match, if desired. + */ + /* Case 1: Fewer objects. */ + if (i < *count) { + cryptodebug("%d objects found, expected %d", i, *count); + *count = i; + /* Case 3: More objects. */ + } else if (i > *count) { + cryptodebug("at least %d objects found, expected %d", + i, *count); + } + /* + * Case 2: Same number of objects. + * + * else if (i == *count) + * ; + */ + } + + cryptodebug("calling C_FindObjectsFinal"); + (void) C_FindObjectsFinal(sess); + return (rv); +} + +char * +class_str(CK_OBJECT_CLASS class) +{ + switch (class) { + case CKO_DATA: return (gettext("data")); + case CKO_CERTIFICATE: return (gettext("certificate")); + case CKO_PUBLIC_KEY: return (gettext("public key")); + case CKO_PRIVATE_KEY: return (gettext("private key")); + case CKO_SECRET_KEY: return (gettext("secret key")); + case CKO_DOMAIN_PARAMETERS: return (gettext("domain parameter")); + default: return (gettext("unknown object")); + } +} + +char * +keytype_str(CK_KEY_TYPE keytype) +{ + switch (keytype) { + case CKK_RSA: return (gettext("RSA")); + case CKK_DSA: return (gettext("DSA")); + case CKK_DH: return (gettext("Diffie-Hellman")); + case CKK_X9_42_DH: return (gettext("X9.42 Diffie-Hellman")); + case CKK_GENERIC_SECRET: return (gettext("generic")); + case CKK_RC2: return (gettext("RC2")); + case CKK_RC4: return (gettext("RC4")); + case CKK_DES: return (gettext("DES")); + case CKK_DES2: return (gettext("Double-DES")); + case CKK_DES3: return (gettext("Triple-DES")); + case CKK_RC5: return (gettext("RC5")); + case CKK_AES: return (gettext("AES")); + default: return (gettext("typeless")); } +} - return (PK_ERR_NONE); +char * +attr_str(CK_ATTRIBUTE_TYPE attrtype) +{ + switch (attrtype) { + case CKA_PRIVATE: return (gettext("private")); + case CKA_LOCAL: return (gettext("local")); + case CKA_SENSITIVE: return (gettext("sensitive")); + case CKA_EXTRACTABLE: return (gettext("extractable")); + case CKA_ENCRYPT: return (gettext("encrypt")); + case CKA_DECRYPT: return (gettext("decrypt")); + case CKA_WRAP: return (gettext("wrap")); + case CKA_UNWRAP: return (gettext("unwrap")); + case CKA_SIGN: return (gettext("sign")); + case CKA_SIGN_RECOVER: return (gettext("sign-recover")); + case CKA_VERIFY: return (gettext("verify")); + case CKA_VERIFY_RECOVER: return (gettext("verify-recover")); + case CKA_DERIVE: return (gettext("derive")); + case CKA_ALWAYS_SENSITIVE: return (gettext("always sensitive")); + case CKA_NEVER_EXTRACTABLE: return (gettext("never extractable")); + default: return (gettext("unknown capability")); + } } /* - * Log out of the token and close the session. + * Convert a byte string into a string of octets formatted like this: + * oo oo oo oo oo ... oo + * where each "oo" is an octet is space separated and in the form: + * [0-f][0-f] if the octet is a non-printable character + * <space><char> if the octet is a printable character + * + * Note: octets_sz must be 3 * str_sz + 1, or at least as long as "blank" */ void -logout_token(CK_SESSION_HANDLE hdl) +octetify(CK_BYTE *str, CK_ULONG str_sz, char *octets, int octets_sz, + boolean_t stop_on_nul, boolean_t do_ascii, int limit, char *indent, + char *blank) { - cryptodebug("inside logout_token"); + char *marker; + int nc; + int newline; + int indent_len; + boolean_t first = B_TRUE; + + cryptodebug("inside octetify"); + + cryptodebug(stop_on_nul ? "stopping on first nul found" : + "continuing to full length of buffer"); + cryptodebug(do_ascii ? "using ascii chars where printable" : + "using only hex octets"); + cryptodebug("every %d characters indent with \"%s\"\n ", limit, indent); + cryptodebug("return \"%s\" if buffer is null or empty", blank); - if (hdl) { - (void) C_Logout(hdl); - (void) C_CloseSession(hdl); + /* If string is empty, write as much of the blank string and leave. */ + if (str_sz == 0) { + (void) snprintf(octets, octets_sz, "%s", blank); + return; } - (void) C_Finalize(NULL); + + /* If only limit or indent is set, pick default for the other. */ + if (limit > 0 && indent == NULL) + indent = "\n"; + if (indent != NULL && limit == 0) + limit = 60; + indent_len = strlen(indent); + + for (marker = octets, newline = 0, first = B_TRUE; + (stop_on_nul && *str != '\0') || + (!stop_on_nul && str_sz > 0 && octets_sz > 0); + str++, str_sz--, marker += nc, octets_sz -= nc) { + if (!first) { + if (limit > 0 && ((marker - octets) / limit) > + newline) { + nc = snprintf(marker, indent_len, "%s", indent); + newline++; + continue; + } + nc = sprintf(marker, + ((do_ascii && isprint(*str) && !isspace(*str)) ? + "%s%c" : "%s%02x"), (do_ascii ? " " : ":"), *str); + } else { + nc = sprintf(marker, + ((do_ascii && isprint(*str) && !isspace(*str)) ? + "%c" : "%02x"), *str); + first = B_FALSE; + } + } + *marker = '\0'; +} + +/* + * Copies a biginteger_t to a template attribute. + * Should be a macro instead of a function. + */ +void +copy_bigint_to_attr(biginteger_t big, CK_ATTRIBUTE_PTR attr) +{ + attr->pValue = big.big_value; + attr->ulValueLen = big.big_value_len; +} + +/* + * Copies a string and its length to a template attribute. + * Should be a macro instead of a function. + */ +void +copy_string_to_attr(CK_BYTE *buf, CK_ULONG buflen, CK_ATTRIBUTE_PTR attr) +{ + attr->pValue = buf; + attr->ulValueLen = buflen; +} + +/* + * Copies a template attribute to a biginteger_t. + * Should be a macro instead of a function. + */ +void +copy_attr_to_bigint(CK_ATTRIBUTE_PTR attr, biginteger_t *big) +{ + big->big_value = attr->pValue; + big->big_value_len = attr->ulValueLen; +} + +/* + * Copies a template attribute to a string and its length. + * Should be a macro instead of a function. + */ +void +copy_attr_to_string(CK_ATTRIBUTE_PTR attr, CK_BYTE **buf, CK_ULONG *buflen) +{ + *buf = attr->pValue; + *buflen = attr->ulValueLen; +} + +/* + * Copies a template attribute to a date and its length. + * Should be a macro instead of a function. + */ +void +copy_attr_to_date(CK_ATTRIBUTE_PTR attr, CK_DATE **buf, CK_ULONG *buflen) +{ + *buf = (CK_DATE *)attr->pValue; + *buflen = attr->ulValueLen; } diff --git a/usr/src/cmd/cmd-crypto/pktool/common.h b/usr/src/cmd/cmd-crypto/pktool/common.h index 68abed5dd6..e725bcb83d 100644 --- a/usr/src/cmd/cmd-crypto/pktool/common.h +++ b/usr/src/cmd/cmd-crypto/pktool/common.h @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -39,38 +39,90 @@ extern "C" { #endif #include <cryptoutil.h> +#include <biginteger.h> /* I18N helpers. */ #include <libintl.h> #include <locale.h> +/* Defines used throughout */ +#define FULL_NAME_LEN 91 /* See full_token_name() for this number. */ + /* Error codes */ #define PK_ERR_NONE 0 #define PK_ERR_USAGE 1 #define PK_ERR_QUIT 2 -#define PK_ERR_PK11INIT 3 -#define PK_ERR_PK11SLOTS 4 -#define PK_ERR_PK11SESSION 5 -#define PK_ERR_PK11LOGIN 6 -#define PK_ERR_PK11SETPIN 7 -#define PK_ERR_NOSLOTS 8 -#define PK_ERR_NOMEMORY 9 -#define PK_ERR_NOTFOUND 10 -#define PK_ERR_PASSPHRASE 11 -#define PK_ERR_NEWPIN 12 -#define PK_ERR_PINCONFIRM 13 -#define PK_ERR_PINMATCH 14 -#define PK_ERR_CHANGEPIN 15 - -extern int pk11_errno; - -extern int get_password(char *prompt, char **password); -extern int init_pk11(void); -extern int find_token_slot(char *token_name, char *manuf_id, +#define PK_ERR_PK11 3 +#define PK_ERR_SYSTEM 4 +#define PK_ERR_OPENSSL 5 + +/* Types of objects for searches. */ +#define PK_PRIVATE_OBJ 0x0001 +#define PK_PUBLIC_OBJ 0x0002 +#define PK_CERT_OBJ 0x0010 +#define PK_PRIKEY_OBJ 0x0020 +#define PK_PUBKEY_OBJ 0x0040 +#define PK_SECKEY_OBJ 0x0080 + +#define PK_KEY_OBJ (PK_PRIKEY_OBJ|PK_PUBKEY_OBJ|PK_SECKEY_OBJ) +#define PK_ALL_OBJ (PK_PRIVATE_OBJ|PK_PUBLIC_OBJ|\ + PK_CERT_OBJ|PK_KEY_OBJ) + +/* Constants for attribute templates. */ +extern CK_BBOOL pk_false; +extern CK_BBOOL pk_true; + + +/* Common functions. */ +extern CK_RV init_pk11(void); +extern void final_pk11(CK_SESSION_HANDLE sess); + +extern CK_RV open_sess(CK_SLOT_ID slot_id, CK_FLAGS sess_flags, + CK_SESSION_HANDLE_PTR sess); +extern void close_sess(CK_SESSION_HANDLE sess); + +extern CK_RV login_token(CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, + CK_ULONG pinlen, CK_SESSION_HANDLE_PTR sess); +extern void logout_token(CK_SESSION_HANDLE sess); + +extern CK_RV quick_start(CK_SLOT_ID slot_id, CK_FLAGS sess_flags, + CK_UTF8CHAR_PTR pin, CK_ULONG pinlen, + CK_SESSION_HANDLE_PTR sess); +extern void quick_finish(CK_SESSION_HANDLE sess); + +extern CK_RV get_pin(char *prompt1, char *prompt2, CK_UTF8CHAR_PTR *pin, + CK_ULONG *pinlen); +extern boolean_t yesno(char *prompt, char *invalid, boolean_t dflt); + +extern CK_RV get_token_slots(CK_SLOT_ID_PTR *slot_list, + CK_ULONG *slot_count); +extern CK_RV find_token_slot(char *token_name, char *manuf_id, char *serial_no, CK_SLOT_ID *slot_id, CK_FLAGS *pin_state); -extern int login_token(CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, - CK_ULONG pinlen, CK_SESSION_HANDLE_PTR hdl); -extern void logout_token(CK_SESSION_HANDLE hdl); + +extern CK_RV find_obj_count(CK_SESSION_HANDLE sess, int obj_type, + CK_BYTE *label, CK_ULONG *count); +extern CK_RV find_objs(CK_SESSION_HANDLE sess, int obj_type, + CK_BYTE *label, CK_OBJECT_HANDLE_PTR *obj, CK_ULONG *count); + +extern void full_token_name(char *token, char *manuf, char *serial, + char *buf); + +extern char *class_str(CK_OBJECT_CLASS class); +extern char *keytype_str(CK_KEY_TYPE keytype); +extern char *attr_str(CK_ATTRIBUTE_TYPE attrtype); + +extern void octetify(CK_BYTE *str, CK_ULONG str_sz, char *oct, int oct_sz, + boolean_t stop_on_nul, boolean_t do_ascii, int limit, + char *indent, char *blank); + +extern void copy_bigint_to_attr(biginteger_t big, CK_ATTRIBUTE_PTR attr); +extern void copy_string_to_attr(CK_BYTE *buf, CK_ULONG buflen, + CK_ATTRIBUTE_PTR attr); +extern void copy_attr_to_bigint(CK_ATTRIBUTE_PTR attr, biginteger_t *big); +extern void copy_attr_to_string(CK_ATTRIBUTE_PTR attr, CK_BYTE **buf, + CK_ULONG *buflen); +extern void copy_attr_to_date(CK_ATTRIBUTE_PTR attr, CK_DATE **buf, + CK_ULONG *buflen); #ifdef __cplusplus } diff --git a/usr/src/cmd/cmd-crypto/pktool/delete.c b/usr/src/cmd/cmd-crypto/pktool/delete.c new file mode 100644 index 0000000000..ca8bbf3c50 --- /dev/null +++ b/usr/src/cmd/cmd-crypto/pktool/delete.c @@ -0,0 +1,227 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file implements the token object delete operation for this tool. + * It loads the PKCS#11 modules, finds the object to delete, deletes it, + * and cleans up. User must be R/W logged into the token. + */ + +#include <stdio.h> +#include <string.h> +#include <cryptoutil.h> +#include <security/cryptoki.h> +#include "common.h" + +/* + * Delete token objects. + */ +int +pk_delete(int argc, char *argv[]) +{ + int opt; + extern int optind; + extern char *optarg; + char *token_name = NULL; + char *manuf_id = NULL; + char *serial_no = NULL; + char full_name[FULL_NAME_LEN]; + boolean_t public_objs = B_FALSE; + boolean_t private_objs = B_FALSE; + CK_BYTE *object_label = NULL; + int obj_type = 0x00; + CK_SLOT_ID slot_id; + CK_FLAGS pin_state; + CK_UTF8CHAR_PTR pin = NULL; + CK_ULONG pinlen = 0; + CK_SESSION_HANDLE sess; + CK_OBJECT_HANDLE *objs; + CK_ULONG num_objs; + CK_ATTRIBUTE label = { CKA_LABEL, NULL, 0 }; + CK_RV rv = CKR_OK; + int i; + + cryptodebug("inside pk_delete"); + + /* Parse command line options. Do NOT i18n/l10n. */ + while ((opt = getopt(argc, argv, "p(private)P(public)l:(label)")) != + EOF) { + switch (opt) { + case 'p': /* private objects */ + private_objs = B_TRUE; + obj_type |= PK_PRIVATE_OBJ; + break; + case 'P': /* public objects */ + public_objs = B_TRUE; + obj_type |= PK_PUBLIC_OBJ; + break; + case 'l': /* objects with specific label */ + if (object_label) + return (PK_ERR_USAGE); + object_label = (CK_BYTE *)optarg; + break; + default: + return (PK_ERR_USAGE); + break; + } + } + + /* At least one of public, private, or object label is required. */ + if (!private_objs && !public_objs && object_label == NULL) + return (PK_ERR_USAGE); + + /* + * If object label is given but neither public/private is specified, + * delete all objects with that label. + */ + if (!private_objs && !public_objs && object_label != NULL) + obj_type = PK_ALL_OBJ; + + /* No additional args allowed. */ + argc -= optind; + argv += optind; + if (argc) + return (PK_ERR_USAGE); + /* Done parsing command line options. */ + + /* Delete operation only supported on softtoken. */ + if (token_name == NULL) + token_name = SOFT_TOKEN_LABEL; + if (manuf_id == NULL) + manuf_id = SOFT_MANUFACTURER_ID; + if (serial_no == NULL) + serial_no = SOFT_TOKEN_SERIAL; + full_token_name(token_name, manuf_id, serial_no, full_name); + + /* Find the slot with token. */ + if ((rv = find_token_slot(token_name, manuf_id, serial_no, &slot_id, + &pin_state)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to find token %s (%s)."), full_name, + pkcs11_strerror(rv)); + return (PK_ERR_PK11); + } + + /* Always get the user's PIN for delete operations. */ + if ((rv = get_pin(gettext("Enter token passphrase:"), NULL, &pin, + &pinlen)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get token passphrase (%s)."), + pkcs11_strerror(rv)); + quick_finish(NULL); + return (PK_ERR_PK11); + } + + /* Log the user R/W into the token. */ + if ((rv = quick_start(slot_id, CKF_RW_SESSION, pin, pinlen, &sess)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to log into token (%s)."), pkcs11_strerror(rv)); + quick_finish(sess); + return (PK_ERR_PK11); + } + + /* Find the object(s) with the given label and/or type. */ + if ((rv = find_objs(sess, obj_type, object_label, &objs, &num_objs)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to find token objects (%s)."), pkcs11_strerror(rv)); + quick_finish(sess); + return (PK_ERR_PK11); + } + + if (num_objs == 0) { + (void) fprintf(stdout, gettext("No matching objects found.\n")); + quick_finish(sess); + return (0); + } + + if (num_objs != 1) { + (void) fprintf(stdout, gettext( + "Warning: %d matching objects found, deleting all.\n"), + num_objs); + if (yesno(gettext("Continue with delete? "), + gettext("Respond with yes or no.\n"), B_FALSE) == B_FALSE) { + quick_finish(sess); + return (0); + } + } + + /* Destroy the objects if found. */ + for (i = 0; i < num_objs; i++) { + /* + * To give nice feedback to the user, get the object's + * label before deleting it. + */ + cryptodebug("calling C_GetAttributeValue for label"); + label.pValue = NULL; + label.ulValueLen = 0; + if (C_GetAttributeValue(sess, objs[i], &label, 1) == CKR_OK) { + if (label.ulValueLen != (CK_ULONG)-1 && + label.ulValueLen != 0 && + (label.pValue = malloc(label.ulValueLen)) != NULL) { + if (C_GetAttributeValue(sess, objs[i], &label, + 1) != CKR_OK) { + free(label.pValue); + label.pValue = NULL; + label.ulValueLen = 0; + } + } else { + label.ulValueLen = 0; + } + } + + cryptodebug("calling C_DestroyObject"); + if ((rv = C_DestroyObject(sess, objs[i])) != CKR_OK) { + if (label.pValue != NULL) + cryptoerror(LOG_STDERR, gettext( + "Unable to delete object #%d \"%.*s\" " + "(%s)."), i+1, label.ulValueLen, + label.pValue, pkcs11_strerror(rv)); + else + cryptoerror(LOG_STDERR, gettext( + "Unable to delete object #%d (%s)."), + i+1, pkcs11_strerror(rv)); + } else { + if (label.pValue != NULL) + (void) fprintf(stdout, gettext("Object #%d " + "\"%.*s\" successfully deleted.\n"), + i+1, label.ulValueLen, label.pValue); + else + (void) fprintf(stdout, gettext( + "Object #%d successfully deleted.\n"), i+1); + } + + if (label.pValue != NULL) + free(label.pValue); + } + + /* Clean up. */ + quick_finish(sess); + return (0); +} diff --git a/usr/src/cmd/cmd-crypto/pktool/derparse.c b/usr/src/cmd/cmd-crypto/pktool/derparse.c new file mode 100644 index 0000000000..cec607220a --- /dev/null +++ b/usr/src/cmd/cmd-crypto/pktool/derparse.c @@ -0,0 +1,371 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * derparse.c - Functions for parsing DER-encoded data + * + * NOTE: This code was originally written by Cryptographic Products + * Group at Sun Microsystems for the SCA 1000 "realmparse" program. + * It is mostly intact except for necessary adaptaions to allow it to + * compile in this environment. + */ + +#include <errno.h> +#include <fcntl.h> +#include <lber.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <cryptoutil.h> +#include "derparse.h" + +/* I18N helpers. */ +#include <libintl.h> +#include <locale.h> + +/* + * Some types that we need below. + */ +typedef struct oidinfo { + uint8_t *value; /* OID value in bytes */ + size_t length; /* Length of OID */ + char *strval; /* String rep. for OID in RDN */ +} oidinfo_t; + +/* + * X.509 Issuer OIDs as recommended by RFC 3280 + * We might see these in certificates in their subject an issuer names. + */ +static uint8_t common_name_oid[] = {0x55, 0x04, 0x03}; +static uint8_t surname_oid[] = {0x55, 0x04, 0x04}; +static uint8_t serial_number_oid[] = {0x55, 0x04, 0x05}; +static uint8_t country_name_oid[] = {0x55, 0x04, 0x06}; +static uint8_t locality_name_oid[] = {0x55, 0x04, 0x07}; +static uint8_t state_name_oid[] = {0x55, 0x04, 0x08}; +static uint8_t org_name_oid[] = {0x55, 0x04, 0x0a}; +static uint8_t org_unit_name_oid[] = {0x55, 0x04, 0x0b}; +static uint8_t title_oid[] = {0x55, 0x04, 0x0c}; +static uint8_t name_oid[] = {0x55, 0x04, 0x29}; +static uint8_t given_name_oid[] = {0x55, 0x04, 0x2a}; +static uint8_t initials_oid[] = {0x55, 0x04, 0x2b}; +static uint8_t gen_qual_oid[] = {0x55, 0x04, 0x2c}; +static uint8_t dn_qual_oid[] = {0x55, 0x04, 0x2e}; +static uint8_t pseudonym_oid[] = {0x55, 0x04, 0x31}; +static uint8_t uid_oid[] = + {0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x01}; +static uint8_t domain_comp_oid[] = + {0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19}; +static uint8_t email_addr_oid[] = + {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01}; + +/* Define this structure so we can match on a given oid */ +static oidinfo_t oids[] = { + {common_name_oid, sizeof (common_name_oid), "CN"}, + {surname_oid, sizeof (surname_oid), "SN"}, + {serial_number_oid, sizeof (serial_number_oid), "SerialNum"}, + {country_name_oid, sizeof (country_name_oid), "C"}, + {locality_name_oid, sizeof (locality_name_oid), "L"}, + {state_name_oid, sizeof (state_name_oid), "ST"}, + {org_name_oid, sizeof (org_name_oid), "O"}, + {org_unit_name_oid, sizeof (org_unit_name_oid), "OU"}, + {title_oid, sizeof (title_oid), "Title"}, + {name_oid, sizeof (name_oid), "Name"}, + {given_name_oid, sizeof (given_name_oid), "GN"}, + {initials_oid, sizeof (initials_oid), "Initials"}, + {gen_qual_oid, sizeof (gen_qual_oid), "GenQual"}, + {dn_qual_oid, sizeof (dn_qual_oid), "DNQual"}, + {pseudonym_oid, sizeof (pseudonym_oid), "Pseudonym"}, + {uid_oid, sizeof (uid_oid), "UID"}, + {domain_comp_oid, sizeof (domain_comp_oid), "DC"}, + {email_addr_oid, sizeof (email_addr_oid), "E"} +}; +static int oidblocklen = sizeof (oids) / sizeof (oidinfo_t); + +/* Local functions */ +static int oid_to_str(uint8_t *, size_t, char *, size_t); +static int get_oid_type(char *); + +/* + * An RDNSequence is what is handed to us when we get attributes like + * CKA_ISSUER and CKA_SUBJECT_NAME. This function will take in a buffer + * with the DER encoded bytes of an RDNSequence and print out the components. + * + * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + * + * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue + * + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue + * } + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY DEFINED BY AttributeType + */ +void +rdnseq_to_str(uchar_t *derdata, size_t dersz, char *out, size_t outsz) +{ +#define PKTOOL_LINEMAX 1024 + char oidout[PKTOOL_LINEMAX]; + BerElement *ber = NULL; + BerValue ber_rdns; + int tag; + ber_len_t size; + char *atv_type = NULL; /* Attribute Type */ + ber_len_t atv_type_size; + char *atv_value = NULL; /* Attribute Value */ + ber_len_t atv_value_size; + char *cookie = NULL; + int idx; + char *prndata = NULL; + int prnsz; + int offset = 0; + boolean_t first = B_TRUE; + + cryptodebug("inside rdnseq_to_str"); + + if (derdata == NULL || dersz == 0) { + cryptodebug("nothing to parse"); + return; + } + + /* Take the raw bytes and stuff them into a BerValue structure */ + ber_rdns.bv_val = (char *)derdata; + ber_rdns.bv_len = dersz; + + /* Allocate the BerElement */ + if ((ber = ber_init(&ber_rdns)) == NULLBER) { + cryptodebug("ber_init failed to return ber element"); + cryptoerror(LOG_STDERR, gettext( + "Unable to begin parsing RDNSequence.")); + return; + } + + /* Begin by parsing out the outer sequence */ + tag = ber_next_element(ber, &size, cookie); + if (tag != LBER_SEQUENCE) { + cryptodebug("ber_next_element tag is not SEQUENCE"); + cryptoerror(LOG_STDERR, gettext( + "Expected RDNSequence SEQUENCE object, got tag [%02x]."), + tag); + return; + } + tag = ber_scanf(ber, "{"); + + /* Parse the sequence of RelativeDistinguishedName objects */ + while ((tag = ber_next_element(ber, &size, cookie)) != -1) { + if (tag != LBER_SET) { + cryptodebug("ber_next_element tag is not SET"); + cryptoerror(LOG_STDERR, gettext( + "Expected RelativeDistinguishedName SET object, " + "got tag [%02x]."), tag); + return; + } + tag = ber_scanf(ber, "["); + + /* AttributeTypeAndValue */ + tag = ber_next_element(ber, &size, cookie); + if (tag != LBER_SEQUENCE) { + cryptodebug("ber_next_element tag is not SEQUENCE"); + cryptoerror(LOG_STDERR, gettext( + "Expected AttributeTypeAndValue SEQUENCE object, " + "got tag [%02x]."), tag); + return; + } + tag = ber_scanf(ber, "{"); + + /* AttributeType OID */ + tag = ber_next_element(ber, &atv_type_size, cookie); + atv_type_size++; /* Add room for null terminator */ + if (tag != LBER_OID) { + cryptodebug("ber_next_element tag is not OID"); + cryptoerror(LOG_STDERR, gettext( + "Expected an OID, got tag [%02x]."), tag); + return; + } + /* Note: ber_scanf() allocates memory here for "a". */ + tag = ber_scanf(ber, "a", &atv_type, &atv_type_size); + + /* AttributeValue */ + tag = ber_next_element(ber, &atv_value_size, cookie); + atv_value_size++; + if ((tag != LBER_PRINTABLE_STRING) && (tag != LBER_IA5STRING)) { + cryptodebug("ber_next_element tag is not " + "PRINTABLE_STRING/IA5STRING"); + cryptoerror(LOG_STDERR, gettext("Expected a STRING, " + "got tag [%02x]."), tag); + free(atv_type); + return; + } + /* Note: ber_scanf() allocates memory here for "a". */ + tag = ber_scanf(ber, "a", &atv_value, &atv_value_size); + + /* + * Now go and turn the attribute type and value into + * some kind of meaningful output. + */ + if ((idx = get_oid_type(atv_type)) == -1) { + if (oid_to_str((uint8_t *)atv_type, strlen(atv_type), + oidout, sizeof (oidout)) < 0) { + cryptodebug("oid_to_str failed"); + cryptoerror(LOG_STDERR, gettext( + "Unable to convert OID to string.")); + free(atv_type); + free(atv_value); + return; + } + prndata = oidout; + } else { + prndata = oids[idx].strval; + } + + if (!first) + prnsz = snprintf(out + offset, outsz - offset, + ", %s = %s", prndata, atv_value); + else { + prnsz = snprintf(out + offset, outsz - offset, + "%s = %s", prndata, atv_value); + first = B_FALSE; + } + + free(atv_type); + free(atv_value); + atv_type = NULL; + atv_value = NULL; + + offset += prnsz; + if (offset >= outsz) + break; + } +} + +/* + * Convert OID to dotted notation string. + */ +static int +oid_to_str(uint8_t *oid, size_t oidlen, char *oidout, size_t oidout_len) +{ + int count = 0; + int offset = 0; + int prnsz; + uint_t firstnum; + uint_t secondnum; + uint64_t nextnum = 0; + + cryptodebug("inside oid_to_str"); + + if (oidlen == 0) + return (-1); + + /* + * The first octet has a value of (40 x oidnum1) + oidnum2. We + * will deconstruct it here and sanity check the result. According + * to X.690, oidnum1 should never be more than 2 and oidnum2 + * shouldn't be greater than 39 when oidnum1 = 0 or 1. + */ + firstnum = oid[count] / 40; + if (firstnum > 2) /* force remainder to be > 39 */ + firstnum = 2; + secondnum = oid[count] - (firstnum * 40); + + (void) memset(oidout, 0, oidout_len); + + prnsz = snprintf(oidout, oidout_len, "%d.%d", firstnum, secondnum); + offset += prnsz; + if (offset >= oidout_len) + return (0); + + /* Start at the second byte and move our way forward */ + for (count = 1; count < oidlen; count++) { + /* ORIGINAL COMMENT */ + /* + * Each oid byte is taken as a 7-bit number. If bit 8 is + * set, it means the next octet and this one are to be + * chained together as a single bit string, and so forth. + * We need to mask of bit 8, then shift over 7 bits in the + * resulting integer, and then stuff the new 7 bits in + * the low order byte, all the while making sure we don't + * stomp bit 1 from the previous octet. + * See X.690 or the layman's guide to ASN.1 for more. + */ + + /* + * String together as many of the next octets if each of + * their high order bits is set to 1. For example, + * 1 1010111, 1 0010100, 1 0010110, 0 1101111, ... + * (3 8-bit octets) + * becomes + * 1010111 0010100 0010110, 1101111, ... + * (one 21 bit integer) + * The high order bit functions as a "link" between octets. + * Note that if there are more than 9 octets with their + * high order bits set, it will overflow a 64-bit integer. + */ + for (nextnum = 0; (oid[count] & 0x80) && (count < oidlen); + count++) { + nextnum <<= 7; + nextnum |= (oid[count] & 0x7f); + } + if (count == oidlen) /* last number not terminated? */ + return (-1); + + /* We're done with this oid number, write it and move on */ + prnsz = snprintf(oidout + offset, oidout_len - offset, + ".%lld", nextnum); + offset += prnsz; + if (offset >= oidout_len) + return (0); + } + + return (0); +} + +/* + * Returns the index in the oids[] array that matches the input type, + * or -1 if it could not find a match. + */ +static int +get_oid_type(char *type) +{ + int count; + + cryptodebug("inside get_oid_type"); + + for (count = 0; count < oidblocklen; count++) { + if (memcmp(oids[count].value, type, oids[count].length) == 0) { + return (count); + } + } + + /* If we get here, we haven't found a match, so return -1 */ + return (-1); +} diff --git a/usr/src/cmd/cmd-crypto/pktool/derparse.h b/usr/src/cmd/cmd-crypto/pktool/derparse.h new file mode 100644 index 0000000000..2ff36d2163 --- /dev/null +++ b/usr/src/cmd/cmd-crypto/pktool/derparse.h @@ -0,0 +1,55 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _PKTOOL_DERPARSE_H +#define _PKTOOL_DERPARSE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef LBER_OID +#define LBER_OID 0x06 +#endif + +#ifndef LBER_PRINTABLE_STRING +#define LBER_PRINTABLE_STRING 0x13 +#endif + +#ifndef LBER_IA5STRING +#define LBER_IA5STRING 0x16 +#endif + +extern void rdnseq_to_str(uchar_t *from, size_t from_sz, char *to, + size_t to_sz); + +#ifdef __cplusplus +} +#endif + +#endif /* _PKTOOL_DERPARSE_H */ diff --git a/usr/src/cmd/cmd-crypto/pktool/export.c b/usr/src/cmd/cmd-crypto/pktool/export.c new file mode 100644 index 0000000000..00a4b379fd --- /dev/null +++ b/usr/src/cmd/cmd-crypto/pktool/export.c @@ -0,0 +1,1404 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file implements the export operation for this tool. + * The basic flow of the process is to find the soft token, + * log into it, find the PKCS#11 objects in the soft token + * to be exported matching keys with their certificates, export + * them to the PKCS#12 file encrypting them with a file password + * if desired, and log out. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <cryptoutil.h> +#include <security/cryptoki.h> +#include "common.h" +#include "biginteger.h" +#include "osslcommon.h" +#include "p12common.h" +#include <openssl/pkcs12.h> + +/* + * Writes OpenSSL objects to PKCS#12 file. The PKCS#11 objects from + * the soft token need to be converted to OpenSSL structures prior + * to this call, since the PKCS#12 routines depend on that format. + * This code is patterned from OpenSSL apps that write PKCS#12 files. + * + * Note: it's not clear from the usage of all the functions here by + * OpenSSL apps whether these functions have return values or error + * conditions that can be checked. This function may benefit from + * a closer review at a later time. + */ +static int +write_objs_pkcs12(BIO *fbio, CK_UTF8CHAR *pin, CK_ULONG pinlen, + CK_BYTE_PTR id, CK_ULONG id_len, EVP_PKEY *priv_key, X509 *cert, + STACK_OF(X509) *ca_certs, int *successes, int *failures) +/* ARGSUSED */ +{ + STACK_OF(PKCS12_SAFEBAG) *bag_stack = NULL; + PKCS12_SAFEBAG *bag = NULL; + X509 *ca = NULL; + PKCS7 *cert_authsafe = NULL; + PKCS8_PRIV_KEY_INFO *p8 = NULL; + PKCS7 *key_authsafe = NULL; + STACK_OF(PKCS7) *authsafe_stack = NULL; + PKCS12 *p12_elem = NULL; + unsigned char *lab = NULL; + int lab_len = 0; + int i; + int n_writes = 0; + + cryptodebug("inside write_objs_pkcs12"); + + /* Do not reset *successes or *failures -- keep running totals. */ + + /* If there is nothing to write to the PKCS#12 file, leave. */ + if (cert == NULL && ca_certs == NULL && priv_key == NULL) { + cryptodebug("nothing to write to export file"); + return (0); + } + + /* + * Section 1: + * + * The first PKCS#12 container (safebag) will hold the certificates + * associated with this key. The result of this section is a + * PIN-encrypted PKCS#7 container (authsafe). If there are no + * certificates, there is no point in creating the "safebag" or the + * "authsafe" so we go to the next section. + */ + if (cert != NULL || ca_certs != NULL) { + /* Start a PKCS#12 safebag container for the certificates. */ + cryptodebug("creating certificate PKCS#12 safebag"); + bag_stack = sk_PKCS12_SAFEBAG_new_null(); + if (bag_stack == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to create PKCS#12 certificate bag.")); + (*failures)++; + return (-1); + } + + /* Add the cert corresponding to private key to bag_stack. */ + if (cert) { + /* Convert cert from X509 struct to PKCS#12 bag */ + cryptodebug("adding certificate to PKCS#12 safebag"); + bag = PKCS12_x5092certbag(cert); + if (bag == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert certificate to " + "PKCS#12 bag.")); + /* Cleanup the safebag. */ + sk_PKCS12_SAFEBAG_pop_free(bag_stack, + PKCS12_SAFEBAG_free); + (*failures)++; + return (-1); + } + + /* Add the key id to the certificate bag. */ + cryptodebug("add key id to PKCS#12 safebag"); + if (!PKCS12_add_localkeyid(bag, id, id_len)) + cryptodebug("error not caught"); + + /* Add the friendly name to the certificate bag. */ + if ((lab = X509_alias_get0(cert, &lab_len)) != NULL) { + cryptodebug( + "label PKCS#12 safebag with friendly name"); + if (!PKCS12_add_friendlyname(bag, (char *)lab, + lab_len)) + cryptodebug("error not caught"); + } + + /* Pile it on the bag_stack. */ + if (!sk_PKCS12_SAFEBAG_push(bag_stack, bag)) + cryptodebug("error not caught"); + + n_writes++; + } + + /* Add all the CA chain certs to the bag_stack. */ + if (ca_certs) { + cryptodebug("adding CA certificate chain to PKCS#12 " + "safebag"); + /* + * Go through the stack of CA certs, converting each + * one to a PKCS#12 bag and piling them onto the + * bag_stack. + */ + for (i = 0; i < sk_X509_num(ca_certs); i++) { + /* + * sk_X509_value() is macro that embeds a + * cast to (X509 *). Here it translates + * into ((X509 *)sk_value((ca_certs), (i))). + * Lint is complaining about the embedded + * casting, and to fix it, you need to fix + * openssl header files. + */ + /* LINTED E_BAD_PTR_CAST_ALIGN */ + ca = sk_X509_value(ca_certs, i); + + /* Convert CA cert to PKCS#12 bag. */ + cryptodebug("adding CA certificate #%d " + "to PKCS#12 safebag", i+1); + bag = PKCS12_x5092certbag(ca); + if (bag == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert CA certificate " + "#%d to PKCS#12 bag."), i+1); + /* Cleanup the safebag. */ + sk_PKCS12_SAFEBAG_pop_free(bag_stack, + PKCS12_SAFEBAG_free); + (*failures)++; + return (-1); + } + + /* Note CA certs do not have friendly name. */ + + /* Pile it onto the bag_stack. */ + if (!sk_PKCS12_SAFEBAG_push(bag_stack, bag)) + cryptodebug("error not caught"); + + n_writes++; + } + } + + /* Turn bag_stack of certs into encrypted authsafe. */ + cryptodebug("encrypt certificate PKCS#12 bag into " + "PKCS#7 authsafe"); + cert_authsafe = PKCS12_pack_p7encdata( + NID_pbe_WithSHA1And40BitRC2_CBC, (char *)pin, -1, NULL, + 0, PKCS12_DEFAULT_ITER, bag_stack); + + /* Clear away this bag_stack, we're done with it. */ + sk_PKCS12_SAFEBAG_pop_free(bag_stack, PKCS12_SAFEBAG_free); + bag_stack = NULL; + + if (cert_authsafe == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to PKCS#7-encrypt certificate bag.")); + (*failures)++; + return (-1); + } + } + + /* + * Section 2: + * + * The second PKCS#12 container (safebag) will hold the private key + * that goes with the certificates above. The results of this section + * is an unencrypted PKCS#7 container (authsafe). If there is no + * private key, there is no point in creating the "safebag" or the + * "authsafe" so we go to the next section. + */ + if (priv_key != NULL) { + /* Make a PKCS#8 shrouded key bag. */ + cryptodebug("create PKCS#8 shrouded key out of private key"); + p8 = EVP_PKEY2PKCS8(priv_key); + if (p8 == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to create PKCS#8 shrouded key for " + "private key.")); + (*failures)++; + return (-1); + } + + /* Put the shrouded key into a PKCS#12 bag. */ + cryptodebug("convert shrouded key to PKCS#12 bag"); + bag = PKCS12_MAKE_SHKEYBAG( + NID_pbe_WithSHA1And3_Key_TripleDES_CBC, (char *)pin, + -1, NULL, 0, PKCS12_DEFAULT_ITER, p8); + + /* Clean up the PKCS#8 shrouded key, don't need it now. */ + PKCS8_PRIV_KEY_INFO_free(p8); + p8 = NULL; + + if (bag == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert private key to PKCS#12 bag.")); + (*failures)++; + return (-1); + } + + /* Add the key id to the certificate bag. */ + cryptodebug("add key id to PKCS#12 safebag"); + if (!PKCS12_add_localkeyid(bag, id, id_len)) + cryptodebug("error not caught"); + + /* Add the cert friendly name to the private key bag. */ + if (lab != NULL) { + cryptodebug("label PKCS#12 safebag with friendly name"); + if (!PKCS12_add_friendlyname(bag, (char *)lab, lab_len)) + cryptodebug("error not caught"); + } + + /* Start a PKCS#12 safebag container for the private key. */ + cryptodebug("creating private key PKCS#12 safebag"); + bag_stack = sk_PKCS12_SAFEBAG_new_null(); + if (bag_stack == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to create PKCS#12 private key bag.")); + (*failures)++; + return (-1); + } + + /* Pile on the private key on the bag_stack. */ + if (!sk_PKCS12_SAFEBAG_push(bag_stack, bag)) + cryptodebug("error not caught"); + + /* Turn bag_stack with private key into unencrypted authsafe. */ + cryptodebug("put private PKCS#12 bag into PKCS#7 authsafe"); + key_authsafe = PKCS12_pack_p7data(bag_stack); + + /* Clear away this bag_stack, we're done with it. */ + sk_PKCS12_SAFEBAG_pop_free(bag_stack, PKCS12_SAFEBAG_free); + bag_stack = NULL; + + if (key_authsafe == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to PKCS#7-convert private key bag.")); + (*failures)++; + return (-1); + } + + n_writes++; + } + + /* + * Section 3: + * + * This is where the two PKCS#7 containers, one for the certificates + * and one for the private key, are put together into a PKCS#12 + * element. This final PKCS#12 element is written to the export file. + */ + /* Start a PKCS#7 stack. */ + cryptodebug("create PKCS#7 authsafe for private key and certificates"); + authsafe_stack = sk_PKCS7_new_null(); + if (authsafe_stack == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to create PKCS#7 container for private key " + "and certificates.")); + (*failures)++; + return (-1); + } + + /* Put certificates and private key into PKCS#7 stack. */ + if (key_authsafe != NULL) { + cryptodebug("put private key authsafe into PKCS#7 container"); + if (!sk_PKCS7_push(authsafe_stack, key_authsafe)) + cryptodebug("error not caught"); + } + if (cert_authsafe != NULL) { + cryptodebug("put certificate authsafe into PKCS#7 container"); + if (!sk_PKCS7_push(authsafe_stack, cert_authsafe)) + cryptodebug("error not caught"); + } + + /* Create PKCS#12 element out of PKCS#7 stack. */ + cryptodebug("create PKCS#12 element for export file"); + p12_elem = PKCS12_init(NID_pkcs7_data); + if (p12_elem == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to create PKCS#12 element for export file.")); + sk_PKCS7_pop_free(authsafe_stack, PKCS7_free); + (*failures)++; + return (-1); + } + + /* Put the PKCS#7 stack into the PKCS#12 element. */ + if (!PKCS12_pack_authsafes(p12_elem, authsafe_stack)) + cryptodebug("error not caught"); + + /* Clear away the PKCS#7 stack, we're done with it. */ + sk_PKCS7_pop_free(authsafe_stack, PKCS7_free); + authsafe_stack = NULL; + + /* Set the integrity MAC on the PKCS#12 element. */ + cryptodebug("setting MAC for PKCS#12 element"); + if (!PKCS12_set_mac(p12_elem, (char *)pin, -1, NULL, 0, + PKCS12_DEFAULT_ITER, NULL)) + cryptodebug("error not caught"); + + /* Write the PKCS#12 element to the export file. */ + cryptodebug("writing PKCS#12 element to export file"); + if (!i2d_PKCS12_bio(fbio, p12_elem)) + cryptodebug("error not caught"); + + (*successes) += n_writes; + + /* Clear away the PKCS#12 element. */ + PKCS12_free(p12_elem); + return (0); +} + +/* + * Get token objects: private key, its cert, and its cert chain. + */ +static CK_RV +get_token_objs(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj, + CK_OBJECT_HANDLE *mate, CK_OBJECT_HANDLE_PTR *chain, + CK_ULONG *chain_len, CK_BYTE_PTR *id, CK_ULONG *id_len) +{ + CK_RV rv = CKR_OK; + CK_ATTRIBUTE keyid_attr[1] = { + { CKA_ID, NULL, 0 } + }; + static CK_OBJECT_CLASS class = CKO_CERTIFICATE; + static CK_CERTIFICATE_TYPE certtype = CKC_X_509; + CK_ATTRIBUTE cert_attr[4] = { + { CKA_CLASS, &class, sizeof (CK_OBJECT_CLASS) }, + { CKA_CERTIFICATE_TYPE, &certtype, sizeof (certtype) }, + { CKA_TOKEN, &pk_true, sizeof (pk_true) }, + { CKA_ID, NULL, 0 } + }; + CK_ULONG num_attr = sizeof (cert_attr) / sizeof (CK_ATTRIBUTE); + CK_OBJECT_HANDLE cert = ~0UL; + CK_ULONG num = 0; + + cryptodebug("inside get_token_objs"); + + /* Get the size of the object's CKA_ID field first. */ + cryptodebug("getting CKA_ID size for object 0x%x", obj); + if ((rv = C_GetAttributeValue(sess, obj, keyid_attr, 1)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext("Unable to get size of object" + " key id (%s)."), pkcs11_strerror(rv)); + return (rv); + } + + /* Allocate the space needed for the key id. */ + if ((keyid_attr[0].pValue = malloc(keyid_attr[0].ulValueLen)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + return (CKR_HOST_MEMORY); + } + + /* Get the CKA_ID field to match obj with its cert. */ + cryptodebug("getting CKA_ID attribute for object 0x%x", obj); + if ((rv = C_GetAttributeValue(sess, obj, keyid_attr, 1)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext("Unable to get object " + "key id (%s)."), pkcs11_strerror(rv)); + free(keyid_attr[0].pValue); + return (rv); + } + + /* Now try to find any certs that have the same id. */ + cryptodebug("searching for certificates with same CKA_ID"); + cert_attr[3].pValue = keyid_attr[0].pValue; + cert_attr[3].ulValueLen = keyid_attr[0].ulValueLen; + if ((rv = C_FindObjectsInit(sess, cert_attr, num_attr)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext("Unable to initialize " + "certificate search (%s)."), pkcs11_strerror(rv)); + free(keyid_attr[0].pValue); + return (rv); + } + + /* Find the first cert that matches the key id. */ + if ((rv = C_FindObjects(sess, &cert, 1, &num)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext("Certificate search failed " + "(%s)."), pkcs11_strerror(rv)); + free(keyid_attr[0].pValue); + return (rv); + } + + (void) C_FindObjectsFinal(sess); + + *id = keyid_attr[0].pValue; + *id_len = keyid_attr[0].ulValueLen; + + *mate = (num == 1) ? cert : ~0UL; + + /* We currently do not find all the certs in the chain. */ + *chain_len = 0; + *chain = NULL; + + return (CKR_OK); +} + +/* + * Converts PKCS#11 biginteger_t format to OpenSSL BIGNUM. + * "to" should be the address of a ptr init'ed to NULL to + * receive the BIGNUM, e.g., + * biginteger_t from; + * BIGNUM *foo = NULL; + * cvt_bigint2bn(&from, &foo); + */ +static int +cvt_bigint2bn(biginteger_t *from, BIGNUM **to) +{ + BIGNUM *temp = NULL; + + cryptodebug("inside cvt_bigint2bn"); + + if (from == NULL || to == NULL) + return (-1); + + cryptodebug("calling BN_bin2bn"); + if ((temp = BN_bin2bn(from->big_value, from->big_value_len, *to)) == + NULL) + return (-1); + + *to = temp; + return (0); +} + +/* + * Convert PKCS#11 RSA private key to OpenSSL EVP_PKEY structure. + */ +static CK_RV +cvt_rsa2evp_pkey(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj, EVP_PKEY **pk) +{ + CK_RV rv = CKR_OK; + EVP_PKEY *key = NULL; /* OpenSSL representation */ + RSA *rsa = NULL; /* OpenSSL representation */ + biginteger_t mod = { NULL, 0 }; /* required */ + biginteger_t pubexp = { NULL, 0 }; /* required */ + biginteger_t priexp = { NULL, 0 }; /* optional */ + biginteger_t prime1 = { NULL, 0 }; /* optional */ + biginteger_t prime2 = { NULL, 0 }; /* optional */ + biginteger_t exp1 = { NULL, 0 }; /* optional */ + biginteger_t exp2 = { NULL, 0 }; /* optional */ + biginteger_t coef = { NULL, 0 }; /* optional */ + CK_ATTRIBUTE rsa_pri_attrs[8] = { + { CKA_MODULUS, NULL, 0 }, + { CKA_PUBLIC_EXPONENT, NULL, 0 }, + { CKA_PRIVATE_EXPONENT, NULL, 0 }, /* optional */ + { CKA_PRIME_1, NULL, 0 }, /* | */ + { CKA_PRIME_2, NULL, 0 }, /* | */ + { CKA_EXPONENT_1, NULL, 0 }, /* | */ + { CKA_EXPONENT_2, NULL, 0 }, /* | */ + { CKA_COEFFICIENT, NULL, 0 } /* V */ + }; + CK_ULONG count = sizeof (rsa_pri_attrs) / sizeof (CK_ATTRIBUTE); + int i; + + cryptodebug("inside cvt_rsa2evp_pkey"); + + cryptodebug("calling RSA_new"); + if ((rsa = RSA_new()) == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to allocate internal RSA structure.")); + return (CKR_HOST_MEMORY); + } + + /* Get the sizes of the attributes we need. */ + cryptodebug("calling C_GetAttributeValue for size info"); + if ((rv = C_GetAttributeValue(sess, obj, rsa_pri_attrs, count)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get RSA private key attribute sizes (%s)."), + pkcs11_strerror(rv)); + return (rv); + } + + /* Allocate memory for each attribute. */ + for (i = 0; i < count; i++) { + if (rsa_pri_attrs[i].ulValueLen == (CK_ULONG)-1 || + rsa_pri_attrs[i].ulValueLen == 0) { + cryptodebug("cvt_rsa2evp_pkey: *** should not happen"); + rsa_pri_attrs[i].ulValueLen = 0; + continue; + } + if ((rsa_pri_attrs[i].pValue = + malloc(rsa_pri_attrs[i].ulValueLen)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + return (CKR_HOST_MEMORY); + } + } + + /* Now really get the attributes. */ + cryptodebug("calling C_GetAttributeValue for attribute info"); + if ((rv = C_GetAttributeValue(sess, obj, rsa_pri_attrs, count)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get RSA private key attributes (%s)."), + pkcs11_strerror(rv)); + return (rv); + } + + /* + * Fill in all the temp variables. Modulus and public exponent + * are required. The rest are optional. + */ + i = 0; + copy_attr_to_bigint(&(rsa_pri_attrs[i++]), &mod); + copy_attr_to_bigint(&(rsa_pri_attrs[i++]), &pubexp); + + if (rsa_pri_attrs[i].ulValueLen != (CK_ULONG)-1 && + rsa_pri_attrs[i].ulValueLen != 0) + copy_attr_to_bigint(&(rsa_pri_attrs[i]), &priexp); + i++; + + if (rsa_pri_attrs[i].ulValueLen != (CK_ULONG)-1 && + rsa_pri_attrs[i].ulValueLen != 0) + copy_attr_to_bigint(&(rsa_pri_attrs[i]), &prime1); + i++; + + if (rsa_pri_attrs[i].ulValueLen != (CK_ULONG)-1 && + rsa_pri_attrs[i].ulValueLen != 0) + copy_attr_to_bigint(&(rsa_pri_attrs[i]), &prime2); + i++; + + if (rsa_pri_attrs[i].ulValueLen != (CK_ULONG)-1 && + rsa_pri_attrs[i].ulValueLen != 0) + copy_attr_to_bigint(&(rsa_pri_attrs[i]), &exp1); + i++; + + if (rsa_pri_attrs[i].ulValueLen != (CK_ULONG)-1 && + rsa_pri_attrs[i].ulValueLen != 0) + copy_attr_to_bigint(&(rsa_pri_attrs[i]), &exp2); + i++; + + if (rsa_pri_attrs[i].ulValueLen != (CK_ULONG)-1 && + rsa_pri_attrs[i].ulValueLen != 0) + copy_attr_to_bigint(&(rsa_pri_attrs[i]), &coef); + i++; + + /* Start the conversion to internal OpenSSL RSA structure. */ + + /* Modulus n */ + if (cvt_bigint2bn(&mod, &(rsa->n)) < 0) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert RSA private key modulus.")); + return (CKR_GENERAL_ERROR); + } + + /* Public exponent e */ + if (cvt_bigint2bn(&pubexp, &(rsa->e)) < 0) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert RSA private key public exponent.")); + return (CKR_GENERAL_ERROR); + } + + /* Private exponent e */ + if (priexp.big_value != NULL) { + if (cvt_bigint2bn(&priexp, &(rsa->d)) < 0) { + cryptoerror(LOG_STDERR, gettext("Unable to convert " + "RSA private key private exponent.")); + return (CKR_GENERAL_ERROR); + } + } else + cryptodebug("no RSA private key private exponent"); + + /* Prime p */ + if (prime1.big_value != NULL) { + if (cvt_bigint2bn(&prime1, &(rsa->p)) < 0) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert RSA private key prime 1.")); + return (CKR_GENERAL_ERROR); + } + } else + cryptodebug("no RSA private key prime 1"); + + /* Prime q */ + if (prime2.big_value != NULL) { + if (cvt_bigint2bn(&prime2, &(rsa->q)) < 0) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert RSA private key prime 2.")); + return (CKR_GENERAL_ERROR); + } + } else + cryptodebug("no RSA private key prime 2"); + + /* Private exponent d modulo p-1 */ + if (exp1.big_value != NULL) { + if (cvt_bigint2bn(&exp1, &(rsa->dmp1)) < 0) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert RSA private key exponent 1.")); + return (CKR_GENERAL_ERROR); + } + } else + cryptodebug("no RSA private key exponent 1"); + + /* Private exponent d modulo q-1 */ + if (exp2.big_value != NULL) { + if (cvt_bigint2bn(&exp2, &(rsa->dmq1)) < 0) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert RSA private key exponent 2.")); + return (CKR_GENERAL_ERROR); + } + } else + cryptodebug("no RSA private key exponent 2"); + + /* CRT coefficient q-inverse mod p */ + if (coef.big_value != NULL) { + if (cvt_bigint2bn(&coef, &(rsa->iqmp)) < 0) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert RSA private key coefficient.")); + return (CKR_GENERAL_ERROR); + } + } else + cryptodebug("no RSA private key coefficient"); + + /* Create OpenSSL EVP_PKEY struct in which to stuff RSA struct. */ + cryptodebug("calling EVP_PKEY_new"); + if ((key = EVP_PKEY_new()) == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to allocate internal EVP_PKEY structure.")); + return (CKR_HOST_MEMORY); + } + + /* Put the RSA struct into the EVP_PKEY struct and return it. */ + cryptodebug("calling EVP_PKEY_set1_RSA"); + (void) EVP_PKEY_set1_RSA(key, rsa); + + *pk = key; + return (CKR_OK); +} + +/* + * Convert PKCS#11 DSA private key to OpenSSL EVP_PKEY structure. + */ +static CK_RV +cvt_dsa2evp_pkey(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj, EVP_PKEY **pk) +{ + CK_RV rv = CKR_OK; + EVP_PKEY *key = NULL; /* OpenSSL representation */ + DSA *dsa = NULL; /* OpenSSL representation */ + biginteger_t prime = { NULL, 0 }; /* required */ + biginteger_t subprime = { NULL, 0 }; /* required */ + biginteger_t base = { NULL, 0 }; /* required */ + biginteger_t value = { NULL, 0 }; /* required */ + CK_ATTRIBUTE dsa_pri_attrs[4] = { + { CKA_PRIME, NULL, 0 }, + { CKA_SUBPRIME, NULL, 0 }, + { CKA_BASE, NULL, 0 }, + { CKA_VALUE, NULL, 0 } + }; + CK_ULONG count = sizeof (dsa_pri_attrs) / sizeof (CK_ATTRIBUTE); + int i; + + cryptodebug("inside cvt_dsa2evp_pkey"); + + cryptodebug("calling DSA_new"); + if ((dsa = DSA_new()) == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to allocate internal DSA structure.")); + return (CKR_HOST_MEMORY); + } + + /* Get the sizes of the attributes we need. */ + cryptodebug("calling C_GetAttributeValue for size info"); + if ((rv = C_GetAttributeValue(sess, obj, dsa_pri_attrs, count)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get DSA private key object attributes (%s)."), + pkcs11_strerror(rv)); + return (rv); + } + + /* Allocate memory for each attribute. */ + for (i = 0; i < count; i++) { + if (dsa_pri_attrs[i].ulValueLen == (CK_ULONG)-1 || + dsa_pri_attrs[i].ulValueLen == 0) { + cryptodebug("cvt_dsa2evp_pkey: *** should not happen"); + dsa_pri_attrs[i].ulValueLen = 0; + continue; + } + if ((dsa_pri_attrs[i].pValue = + malloc(dsa_pri_attrs[i].ulValueLen)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + return (CKR_HOST_MEMORY); + } + } + + /* Now really get the attributes. */ + cryptodebug("calling C_GetAttributeValue for attribute info"); + if ((rv = C_GetAttributeValue(sess, obj, dsa_pri_attrs, count)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get DSA private key attributes (%s)."), + pkcs11_strerror(rv)); + return (rv); + } + + /* Fill in all the temp variables. They are all required. */ + i = 0; + copy_attr_to_bigint(&(dsa_pri_attrs[i++]), &prime); + copy_attr_to_bigint(&(dsa_pri_attrs[i++]), &subprime); + copy_attr_to_bigint(&(dsa_pri_attrs[i++]), &base); + copy_attr_to_bigint(&(dsa_pri_attrs[i++]), &value); + + /* Start the conversion to internal OpenSSL DSA structure. */ + + /* Prime p */ + if (cvt_bigint2bn(&prime, &(dsa->p)) < 0) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert DSA private key prime.")); + return (CKR_GENERAL_ERROR); + } + + /* Subprime q */ + if (cvt_bigint2bn(&subprime, &(dsa->q)) < 0) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert DSA private key subprime.")); + return (CKR_GENERAL_ERROR); + } + + /* Base g */ + if (cvt_bigint2bn(&base, &(dsa->g)) < 0) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert DSA private key base.")); + return (CKR_GENERAL_ERROR); + } + + /* Private key x */ + if (cvt_bigint2bn(&value, &(dsa->priv_key)) < 0) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert DSA private key value.")); + return (CKR_GENERAL_ERROR); + } + + /* Create OpenSSL EVP PKEY struct in which to stuff DSA struct. */ + cryptodebug("calling EVP_PKEY_new"); + if ((key = EVP_PKEY_new()) == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to allocate internal EVP_PKEY structure.")); + return (CKR_HOST_MEMORY); + } + + /* Put the DSA struct into the EVP_PKEY struct and return it. */ + cryptodebug("calling EVP_PKEY_set1_DSA"); + (void) EVP_PKEY_set1_DSA(key, dsa); + + *pk = key; + return (CKR_OK); +} + +/* + * Convert PKCS#11 DH private key to OpenSSL EVP_PKEY structure. + */ +static CK_RV +cvt_dh2evp_pkey(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj, EVP_PKEY **pk) +{ + CK_RV rv = CKR_OK; + EVP_PKEY *key = NULL; /* OpenSSL representation */ + DH *dh = NULL; /* OpenSSL representation */ + biginteger_t prime = { NULL, 0 }; /* required */ + biginteger_t base = { NULL, 0 }; /* required */ + biginteger_t value = { NULL, 0 }; /* required */ + CK_ATTRIBUTE dh_pri_attrs[3] = { + { CKA_PRIME, NULL, 0 }, + { CKA_BASE, NULL, 0 }, + { CKA_VALUE, NULL, 0 } + }; + CK_ULONG count = sizeof (dh_pri_attrs) / sizeof (CK_ATTRIBUTE); + int i; + + cryptodebug("inside cvt_dh2evp_pkey"); + + cryptodebug("calling DH_new"); + if ((dh = DH_new()) == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to allocate internal DH structure.")); + return (CKR_HOST_MEMORY); + } + + /* Get the sizes of the attributes we need. */ + cryptodebug("calling C_GetAttributeValue for size info"); + if ((rv = C_GetAttributeValue(sess, obj, dh_pri_attrs, count)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get DH private key object attributes (%s)."), + pkcs11_strerror(rv)); + return (rv); + } + + /* Allocate memory for each attribute. */ + for (i = 0; i < count; i++) { + if (dh_pri_attrs[i].ulValueLen == (CK_ULONG)-1 || + dh_pri_attrs[i].ulValueLen == 0) { + cryptodebug("cvt_dh2evp_pkey: ***should not happen"); + dh_pri_attrs[i].ulValueLen = 0; + continue; + } + if ((dh_pri_attrs[i].pValue = + malloc(dh_pri_attrs[i].ulValueLen)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + return (CKR_HOST_MEMORY); + } + } + + /* Now really get the attributes. */ + cryptodebug("calling C_GetAttributeValue for attribute info"); + if ((rv = C_GetAttributeValue(sess, obj, dh_pri_attrs, count)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get DH private key attributes (%s)."), + pkcs11_strerror(rv)); + return (rv); + } + + /* Fill in all the temp variables. They are all required. */ + i = 0; + copy_attr_to_bigint(&(dh_pri_attrs[i++]), &prime); + copy_attr_to_bigint(&(dh_pri_attrs[i++]), &base); + copy_attr_to_bigint(&(dh_pri_attrs[i++]), &value); + + /* Start the conversion to internal OpenSSL DH structure. */ + + /* Prime p */ + if (cvt_bigint2bn(&prime, &(dh->p)) < 0) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert DH private key prime.")); + return (CKR_GENERAL_ERROR); + } + + /* Base g */ + if (cvt_bigint2bn(&base, &(dh->g)) < 0) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert DH private key base.")); + return (CKR_GENERAL_ERROR); + } + + /* Private value x */ + if (cvt_bigint2bn(&value, &(dh->priv_key)) < 0) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert DH private key value.")); + return (CKR_GENERAL_ERROR); + } + + /* Create OpenSSL EVP PKEY struct in which to stuff DH struct. */ + cryptodebug("calling EVP_PKEY_new"); + if ((key = EVP_PKEY_new()) == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to allocate internal EVP_PKEY structure.")); + return (CKR_HOST_MEMORY); + } + + /* Put the DH struct into the EVP_PKEY struct and return it. */ + cryptodebug("calling EVP_PKEY_set1_DH"); + (void) EVP_PKEY_set1_DH(key, dh); + + *pk = key; + return (CKR_OK); +} + +/* + * Convert PKCS#11 private key object to OpenSSL EVP_PKEY structure. + */ +static CK_RV +cvt_obj2evp_pkey(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj, EVP_PKEY **pk) +{ + CK_RV rv = CKR_OK; + static CK_KEY_TYPE keytype = 0; + CK_ATTRIBUTE keytype_attr[1] = { + { CKA_KEY_TYPE, &keytype, sizeof (keytype) } + }; + + cryptodebug("inside cvt_obj2evp_pkey"); + + /* Find out the key type to do the right conversion. */ + cryptodebug("calling C_GetAttributeValue"); + if ((rv = C_GetAttributeValue(sess, obj, keytype_attr, 1)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get token object key type (%s)."), + pkcs11_strerror(rv)); + return (rv); + } + + switch (keytype) { + case CKK_RSA: + cryptodebug("converting RSA key"); + return (cvt_rsa2evp_pkey(sess, obj, pk)); + case CKK_DSA: + cryptodebug("converting DSA key"); + return (cvt_dsa2evp_pkey(sess, obj, pk)); + case CKK_DH: + cryptodebug("converting DH key"); + return (cvt_dh2evp_pkey(sess, obj, pk)); + default: + cryptoerror(LOG_STDERR, gettext( + "Private key type 0x%02x conversion not supported."), + keytype); + return (CKR_GENERAL_ERROR); + } +} + +/* + * Convert PKCS#11 certificate object to OpenSSL X509 structure. + */ +static CK_RV +cvt_cert2x509(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj, X509 **c) +{ + CK_RV rv = CKR_OK; + X509 *cert = NULL; /* OpenSSL representation */ + X509 *temp_cert = NULL; + CK_BYTE *subject = NULL; + CK_ULONG subject_len = 0; + CK_BYTE *value = NULL; + CK_ULONG value_len = 0; + CK_BYTE *label = NULL; + CK_ULONG label_len = 0; + CK_BYTE *id = NULL; + CK_ULONG id_len = 0; + CK_BYTE *issuer = NULL; + CK_ULONG issuer_len = 0; + CK_BYTE *serial = NULL; + CK_ULONG serial_len = 0; + CK_ATTRIBUTE cert_attrs[6] = { + { CKA_SUBJECT, NULL, 0 }, /* required */ + { CKA_VALUE, NULL, 0 }, /* required */ + { CKA_LABEL, NULL, 0 }, /* optional */ + { CKA_ID, NULL, 0 }, /* optional */ + { CKA_ISSUER, NULL, 0 }, /* optional */ + { CKA_SERIAL_NUMBER, NULL, 0 } /* optional */ + }; + CK_ULONG count = sizeof (cert_attrs) / sizeof (CK_ATTRIBUTE); + int i = 0; + X509_NAME *ssl_subject = NULL; + X509_NAME *ssl_issuer = NULL; + ASN1_INTEGER *ssl_serial = NULL; + + cryptodebug("inside cvt_cert2x509"); + + cryptodebug("calling X509_new"); + if ((cert = X509_new()) == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to allocate internal X509 structure.")); + return (CKR_HOST_MEMORY); + } + + /* Get the sizes of the attributes we need. */ + cryptodebug("calling C_GetAttributeValue for size info"); + if ((rv = C_GetAttributeValue(sess, obj, cert_attrs, count)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get certificate attribute sizes (%s)."), + pkcs11_strerror(rv)); + return (rv); + } + + /* Allocate memory for each attribute. */ + for (i = 0; i < count; i++) { + if (cert_attrs[i].ulValueLen == (CK_ULONG)-1 || + cert_attrs[i].ulValueLen == 0) { + cryptodebug("cvt_cert2x509: *** should not happen"); + cert_attrs[i].ulValueLen = 0; + continue; + } + if ((cert_attrs[i].pValue = malloc(cert_attrs[i].ulValueLen)) + == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + return (CKR_HOST_MEMORY); + } + } + + /* Now really get the attributes. */ + cryptodebug("calling C_GetAttributeValue for attribute info"); + if ((rv = C_GetAttributeValue(sess, obj, cert_attrs, count)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get certificate attributes (%s)."), + pkcs11_strerror(rv)); + return (rv); + } + + /* + * Fill in all the temp variables. Subject and value are required. + * The rest are optional. + */ + i = 0; + copy_attr_to_string(&(cert_attrs[i++]), &subject, &subject_len); + copy_attr_to_string(&(cert_attrs[i++]), &value, &value_len); + + if (cert_attrs[i].ulValueLen != (CK_ULONG)-1 && + cert_attrs[i].ulValueLen != 0) + copy_attr_to_string(&(cert_attrs[i]), &label, &label_len); + i++; + + if (cert_attrs[i].ulValueLen != (CK_ULONG)-1 && + cert_attrs[i].ulValueLen != 0) + copy_attr_to_string(&(cert_attrs[i]), &id, &id_len); + i++; + + if (cert_attrs[i].ulValueLen != (CK_ULONG)-1 && + cert_attrs[i].ulValueLen != 0) + copy_attr_to_string(&(cert_attrs[i]), &issuer, &issuer_len); + i++; + + if (cert_attrs[i].ulValueLen != (CK_ULONG)-1 && + cert_attrs[i].ulValueLen != 0) + copy_attr_to_string(&(cert_attrs[i]), &serial, &serial_len); + i++; + + /* Start the conversion to internal OpenSSL X509 structure. */ + + /* Subject name (required) */ + cryptodebug("calling d2i_X509_NAME for subject name"); + if ((ssl_subject = d2i_X509_NAME(NULL, &subject, subject_len)) == + NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert certificate subject name.")); + return (CKR_GENERAL_ERROR); + } + cryptodebug("calling X509_set_subject_name"); + if (!X509_set_subject_name(cert, ssl_subject)) { + cryptoerror(LOG_STDERR, gettext( + "Unable to pack certificate subject name entries.")); + return (CKR_GENERAL_ERROR); + } + + /* Label (optional) */ + cryptodebug("calling X509_alias_set1"); + if (!X509_alias_set1(cert, label, label_len)) + cryptodebug("error not caught"); + + /* Id (optional) */ + cryptodebug("calling X509_keyid_set1"); + if (!X509_keyid_set1(cert, id, id_len)) + cryptodebug("error not caught"); + + /* Issuer name (optional) */ + cryptodebug("calling d2i_X509_NAME for issuer name"); + if ((ssl_issuer = d2i_X509_NAME(NULL, &issuer, issuer_len)) == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert certificate issuer name.")); + return (CKR_GENERAL_ERROR); + } + cryptodebug("calling X509_set_issuer_name"); + if (!X509_set_issuer_name(cert, ssl_issuer)) { + cryptoerror(LOG_STDERR, gettext( + "Unable to pack certificate issuer name entries.")); + return (CKR_GENERAL_ERROR); + } + + /* Serial number (optional) */ + cryptodebug("calling c2i_ASN1_INTEGER for serial number"); + if ((ssl_serial = c2i_ASN1_INTEGER(NULL, &serial, serial_len)) == + NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert certificate serial number.")); + return (CKR_GENERAL_ERROR); + } + cryptodebug("calling X509_set_serialNumber"); + if (!X509_set_serialNumber(cert, ssl_serial)) + cryptodebug("error not caught"); + + /* + * Value (required) + * + * The rest of this code takes the CKA_VALUE attribute, converts + * it into a temp OpenSSL X509 structure and picks out the rest + * of the fields we need to convert it back into the current X509 + * structure that will get exported. The reason we don't just + * start with CKA_VALUE is because while the object was in the + * softtoken, it is possible that some of its attributes changed. + * Those changes would not appear in CKA_VALUE and would be lost + * if we started with CKA_VALUE that was saved originally. + */ + cryptodebug("calling d2i_X509 for cert value"); + if ((temp_cert = d2i_X509(NULL, &value, value_len)) == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert main certificate values.")); + return (CKR_GENERAL_ERROR); + } + + /* Transfer these values from temp_cert to cert. */ + cryptodebug("calling X509_set_version/X509_get_version"); + if (!X509_set_version(cert, X509_get_version(temp_cert))) + cryptodebug("error not caught"); + + cryptodebug("calling X509_set_notBefore/X509_get_notBefore"); + if (!X509_set_notBefore(cert, X509_get_notBefore(temp_cert))) + cryptodebug("error not caught"); + + cryptodebug("calling X509_set_notAfter/X509_get_notAfter"); + if (!X509_set_notAfter(cert, X509_get_notAfter(temp_cert))) + cryptodebug("error not caught"); + + cryptodebug("calling X509_set_pubkey/X509_get_pubkey"); + if (!X509_set_pubkey(cert, X509_get_pubkey(temp_cert))) + cryptodebug("error not caught"); + + /* + * These don't get transfered from temp_cert to cert. + * It -appears- that they may get regenerated as needed. + * + * cert->cert_info->signature = dup(temp_cert->cert_info->signature); + * cert->sig_alg = dup(temp_cert->sig_alg); + * cert->signature = dup(temp_cert->signature); + * cert->skid = dup(temp_cert->skid); + * cert->akid = dup(temp_cert->akid); + */ + + *c = cert; + return (CKR_OK); +} + +static CK_RV +convert_token_objs(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj, + CK_OBJECT_HANDLE mate, CK_OBJECT_HANDLE *chain, CK_ULONG chain_len, + EVP_PKEY **priv_key, X509 **cert, STACK_OF(X509) **ca) +{ + CK_RV rv = CKR_OK; + EVP_PKEY *pk = NULL; + X509 *c = NULL; + X509 *one_ca = NULL; + STACK_OF(X509) *ch = NULL; + int i; + + cryptodebug("inside convert_token_objs"); + + if ((rv = cvt_obj2evp_pkey(sess, obj, &pk)) != CKR_OK) + return (rv); + + if (mate != ~0UL) { + cryptodebug("converting cert corresponding to private key"); + if ((rv = cvt_cert2x509(sess, mate, &c)) != CKR_OK) + return (rv); + } + + if (chain_len != 0) { + cryptodebug("converting ca chain of %d certs corresponding " + "to private key", chain_len); + ch = sk_X509_new_null(); + for (i = 0; i < chain_len; i++) { + if ((rv = cvt_cert2x509(sess, chain[i], &one_ca)) != + CKR_OK) { + return (rv); + } + if (!sk_X509_push(ch, one_ca)) + cryptodebug("error not caught"); + } + } + + *priv_key = pk; + *cert = (mate != ~0UL) ? c : NULL; + *ca = (chain_len != 0) ? ch : NULL; + return (CKR_OK); +} + +/* + * Export objects from token to PKCS#12 file. + */ +int +pk_export(int argc, char *argv[]) +{ + char *token_name = NULL; + char *manuf_id = NULL; + char *serial_no = NULL; + char full_name[FULL_NAME_LEN]; + char *filename = NULL; + CK_SLOT_ID slot_id; + CK_FLAGS pin_state; + CK_UTF8CHAR_PTR pin = NULL; + CK_ULONG pinlen = 0; + CK_UTF8CHAR_PTR pk12pin = NULL; + CK_ULONG pk12pinlen = 0; + CK_SESSION_HANDLE sess; + BIO *fbio = NULL; + EVP_PKEY *priv_key = NULL; + X509 *cert = NULL; + STACK_OF(X509) *ca = NULL; + CK_RV rv = CKR_OK; + CK_OBJECT_HANDLE *objs = NULL; + CK_ULONG num_objs = 0; + CK_OBJECT_HANDLE mate = ~0UL; + CK_OBJECT_HANDLE *chain = NULL; + CK_ULONG chain_len; + CK_BYTE *id = NULL; + CK_ULONG id_len = 0; + int i = 0; + int good_ones = 0, bad_ones = 0; /* running totals */ + + cryptodebug("inside pk_export"); + + /* Get rid of subcommand work "export". */ + argc--; + argv++; + + /* One additional arg required: filename. */ + if (argc != 1) + return (PK_ERR_USAGE); + + filename = argv[0]; + /* Done parsing command line options. */ + + /* Check if the file exists and might be overwritten. */ + if (access(filename, F_OK) == 0) { + cryptoerror(LOG_STDERR, gettext("Warning: file \"%s\" exists, " + "will be overwritten."), filename); + if (yesno(gettext("Continue with export? "), + gettext("Respond with yes or no.\n"), B_FALSE) == B_FALSE) { + return (0); + } + } + + /* Export operation only supported on softtoken. */ + if (token_name == NULL) + token_name = SOFT_TOKEN_LABEL; + if (manuf_id == NULL) + manuf_id = SOFT_MANUFACTURER_ID; + if (serial_no == NULL) + serial_no = SOFT_TOKEN_SERIAL; + full_token_name(token_name, manuf_id, serial_no, full_name); + + /* Find the slot with token. */ + if ((rv = find_token_slot(token_name, manuf_id, serial_no, &slot_id, + &pin_state)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to find token %s (%s)."), full_name, + pkcs11_strerror(rv)); + return (PK_ERR_PK11); + } + + /* Get the user's PIN. */ + if ((rv = get_pin(gettext("Enter token passphrase:"), NULL, &pin, + &pinlen)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get token passphrase (%s)."), + pkcs11_strerror(rv)); + quick_finish(NULL); + return (PK_ERR_PK11); + } + + /* Assume user must be logged in R/W to export objects from token. */ + if ((rv = quick_start(slot_id, CKF_RW_SESSION, pin, pinlen, &sess)) != + CKR_OK) { + cryptoerror(LOG_STDERR, + gettext("Unable to log into token (%s)."), + pkcs11_strerror(rv)); + quick_finish(sess); + return (PK_ERR_PK11); + } + + /* Collect all private keys first. */ + if ((rv = find_objs(sess, PK_PRIVATE_OBJ|PK_KEY_OBJ, NULL, + &objs, &num_objs)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to retrieve private key token objects (%s)."), + pkcs11_strerror(rv)); + quick_finish(sess); + return (PK_ERR_PK11); + } + + /* Nothing to do? */ + if (num_objs == 0) { + cryptoerror(LOG_STDERR, gettext("No objects found.")); + quick_finish(sess); + return (0); + } + + /* Setup OpenSSL context. */ + PKTOOL_setup_openssl(); + + /* Create PKCS#12 file. */ + if ((create_pkcs12(filename, &fbio)) < 0) { + cryptoerror(LOG_STDERR, gettext("No export file created.")); + quick_finish(sess); + return (PK_ERR_SYSTEM); + } + + /* Get the PIN for the PKCS#12 export file. */ + if ((rv = get_pin(gettext("Create export file passphrase:"), gettext( + "Re-enter export file passphrase:"), &pk12pin, &pk12pinlen)) != + CKR_OK) { + cryptoerror(LOG_STDERR, + gettext("Unable to get export file passphrase (%s)."), + pkcs11_strerror(rv)); + close_pkcs12(fbio); + quick_finish(sess); + return (PK_ERR_PK11); + } + + for (i = 0; i < num_objs; i++) { + /* Get a private key and its certificate and CA chain. */ + if ((rv = get_token_objs(sess, objs[i], &mate, &chain, + &chain_len, &id, &id_len)) != CKR_OK) { + /* + * Note this "rv" is either CKR_OK or !CKR_OK. The + * real error codes/messages are handled inside + * read_token_objs(). + */ + cryptoerror(LOG_STDERR, + gettext("Unable to get token objects.")); + free(id); + close_pkcs12(fbio); + quick_finish(sess); + return (PK_ERR_PK11); + } + + /* Convert to OpenSSL equivalents. */ + if ((rv = convert_token_objs(sess, objs[i], mate, chain, + chain_len, &priv_key, &cert, &ca)) != CKR_OK) { + /* + * Note this "rv" is either CKR_OK or !CKR_OK. The + * real error codes/messages are handled inside + * read_token_objs(). + */ + cryptoerror(LOG_STDERR, + gettext("Unable to convert token objects.")); + free(id); + close_pkcs12(fbio); + quick_finish(sess); + return (PK_ERR_PK11); + } + + /* + * When exporting of cert chains is implemented, these + * messages should be updated accordingly. + */ + if (mate == ~0UL) + (void) fprintf(stdout, gettext( + "Writing object #%d...\n"), i+1); + else + (void) fprintf(stdout, gettext("Writing object #%d " + "and its certificate...\n"), i+1); + + /* Write object and its certs to the PKCS#12 export file. */ + if (write_objs_pkcs12(fbio, pk12pin, pk12pinlen, id, id_len, + priv_key, cert, ca, &good_ones, &bad_ones) < 0) { + cryptoerror(LOG_STDERR, gettext( + "Unable to write object #%d to export file."), i+1); + sk_X509_pop_free(ca, X509_free); + free(id); + close_pkcs12(fbio); + quick_finish(sess); + return (PK_ERR_OPENSSL); + } + + /* Destroy key id and CA cert chain, done with them. */ + free(id); + id = NULL; + sk_X509_pop_free(ca, X509_free); + ca = NULL; + } + + (void) fprintf(stdout, gettext( + "%d token objects exported, %d errors occurred.\n"), + good_ones, bad_ones); + + /* Close PKCS#12 file. */ + close_pkcs12(fbio); + + /* Clean up. */ + quick_finish(sess); + return (0); +} diff --git a/usr/src/cmd/cmd-crypto/pktool/import.c b/usr/src/cmd/cmd-crypto/pktool/import.c new file mode 100644 index 0000000000..fee8ffc06a --- /dev/null +++ b/usr/src/cmd/cmd-crypto/pktool/import.c @@ -0,0 +1,945 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file implements the import operation for this tool. + * The basic flow of the process is to decrypt the PKCS#12 + * input file if it has a password, parse the elements in + * the file, find the soft token, log into it, import the + * PKCS#11 objects into the soft token, and log out. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <cryptoutil.h> +#include <security/cryptoki.h> +#include "common.h" +#include "biginteger.h" +#include "osslcommon.h" +#include "p12common.h" +#include <openssl/pkcs12.h> +#include <openssl/err.h> + +/* + * Helper function decrypt and parse PKCS#12 import file. + */ +static CK_RV +extract_pkcs12(BIO *fbio, CK_UTF8CHAR *pin, CK_ULONG pinlen, + EVP_PKEY **priv_key, X509 **cert, STACK_OF(X509) **ca) +/* ARGSUSED */ +{ + PKCS12 *pk12, *pk12_tmp; + EVP_PKEY *temp_pkey = NULL; + X509 *temp_cert = NULL; + STACK_OF(X509) *temp_ca = NULL; + + cryptodebug("inside extract_pkcs12"); + + cryptodebug("calling PKCS12_new"); + if ((pk12 = PKCS12_new()) == NULL) { + cryptoerror(LOG_STDERR, gettext( + "Unable to create PKCS#12 context.")); + return (CKR_GENERAL_ERROR); + } + + cryptodebug("calling d2i_PKCS12_bio"); + if ((pk12_tmp = d2i_PKCS12_bio(fbio, &pk12)) == NULL) { + /* This is ok; it seems to mean there is no more to read. */ + if (ERR_GET_LIB(ERR_peek_error()) == ERR_LIB_ASN1 && + ERR_GET_REASON(ERR_peek_error()) == ASN1_R_HEADER_TOO_LONG) + goto end_extract_pkcs12; + + cryptoerror(LOG_STDERR, gettext( + "Unable to populate PKCS#12 context.")); + PKCS12_free(pk12); + return (CKR_GENERAL_ERROR); + } + pk12 = pk12_tmp; + + cryptodebug("calling PKCS12_parse"); + if (PKCS12_parse(pk12, (char *)pin, &temp_pkey, &temp_cert, + &temp_ca) <= 0) { + cryptoerror(LOG_STDERR, + gettext("Unable to parse import file.")); + PKCS12_free(pk12); + return (CKR_GENERAL_ERROR); + } + +end_extract_pkcs12: + + *priv_key = temp_pkey; + *cert = temp_cert; + *ca = temp_ca; + + PKCS12_free(pk12); + return (CKR_OK); +} + +/* + * Converts OpenSSL BIGNUM into PKCS#11 biginteger_t format. + */ +static CK_RV +cvt_bn2bigint(BIGNUM *from, biginteger_t *to) +{ + CK_BYTE *temp; + CK_ULONG temp_alloc_sz, temp_cvt_sz; + + cryptodebug("inside cvt_bn2bigint"); + + if (from == NULL || to == NULL) + return (CKR_ARGUMENTS_BAD); + + cryptodebug("calling BN_num_bytes"); + temp_alloc_sz = BN_num_bytes(from); + if ((temp = malloc(temp_alloc_sz)) == NULL) + return (CKR_HOST_MEMORY); + + cryptodebug("calling BN_bn2bin"); + temp_cvt_sz = BN_bn2bin(from, (unsigned char *)temp); + if (temp_cvt_sz != temp_alloc_sz) + return (CKR_GENERAL_ERROR); + + to->big_value = temp; + to->big_value_len = temp_cvt_sz; + return (CKR_OK); +} + +/* + * Write RSA private key to token. + */ +static CK_RV +write_rsa_private(CK_SESSION_HANDLE sess, RSA *rsa, X509 *cert) +{ + CK_RV rv = CKR_OK; + int i = 0; + static CK_OBJECT_CLASS objclass = CKO_PRIVATE_KEY; + static CK_KEY_TYPE keytype = CKK_RSA; + CK_BYTE *label = NULL; + CK_ULONG label_len = 0; + CK_BYTE *id = NULL; + CK_ULONG id_len = 0; + CK_DATE startdate = { "", "", "" }; + CK_DATE enddate = { "", "", "" }; + char tmpdate[8]; + biginteger_t mod = { NULL, 0 }; /* required */ + biginteger_t pubexp = { NULL, 0 }; /* required */ + biginteger_t priexp = { NULL, 0 }; /* optional */ + biginteger_t prime1 = { NULL, 0 }; /* optional */ + biginteger_t prime2 = { NULL, 0 }; /* optional */ + biginteger_t exp1 = { NULL, 0 }; /* optional */ + biginteger_t exp2 = { NULL, 0 }; /* optional */ + biginteger_t coef = { NULL, 0 }; /* optional */ + CK_ATTRIBUTE rsa_pri_attrs[16] = { + { CKA_CLASS, &objclass, sizeof (objclass) }, + { CKA_KEY_TYPE, &keytype, sizeof (keytype) }, + { CKA_PRIVATE, &pk_true, sizeof (pk_true) }, + { CKA_TOKEN, &pk_true, sizeof (pk_true) }, + { CKA_LABEL, NULL, 0 }, + { CKA_ID, NULL, 0 }, + { CKA_START_DATE, NULL, 0 }, + { CKA_END_DATE, NULL, 0 }, + { CKA_MODULUS, NULL, 0 }, + { CKA_PUBLIC_EXPONENT, NULL, 0 }, + { 0 /* CKA_PRIVATE_EXPONENT */, NULL, 0 }, /* optional */ + { 0 /* CKA_PRIME_1 */, NULL, 0 }, /* | */ + { 0 /* CKA_PRIME_2 */, NULL, 0 }, /* | */ + { 0 /* CKA_EXPONENT_1 */, NULL, 0 }, /* | */ + { 0 /* CKA_EXPONENT_2 */, NULL, 0 }, /* | */ + { 0 /* CKA_COEFFICIENT */, NULL, 0 } /* V */ + }; + CK_ULONG count = sizeof (rsa_pri_attrs) / sizeof (CK_ATTRIBUTE); + CK_OBJECT_HANDLE obj; + + cryptodebug("inside write_rsa_private"); + + /* Attributes start at array index 4. */ + i = 4; + + /* Recycle the certificate label for the private key label. */ + cryptodebug("calling X509_alias_get0"); + if ((label = X509_alias_get0(cert, (int *)&label_len)) == NULL) { + label = (CK_BYTE *)gettext("no label"); + label_len = strlen((char *)label); + } + copy_string_to_attr(label, label_len, &(rsa_pri_attrs[i++])); + + /* Recycle the certificate id for the private key id. */ + cryptodebug("calling PKTOOL_X509_keyid_get0"); + if ((id = PKTOOL_X509_keyid_get0(cert, (int *)&id_len)) == NULL) { + id = (CK_BYTE *)gettext("no id"); + id_len = strlen((char *)id); + } + copy_string_to_attr(id, id_len, &(rsa_pri_attrs[i++])); + + /* Recycle the certificate start and end dates for private key. */ + cryptodebug("calling X509_get_notBefore"); + if (PKTOOL_cvt_ossltime(X509_get_notBefore(cert), tmpdate)) { + (void) memcpy(&startdate, tmpdate, sizeof (startdate)); + copy_string_to_attr((CK_BYTE *)&startdate, sizeof (startdate), + &(rsa_pri_attrs[i++])); + } + + cryptodebug("calling X509_get_notAfter"); + if (PKTOOL_cvt_ossltime(X509_get_notAfter(cert), tmpdate)) { + (void) memcpy(&enddate, tmpdate, sizeof (enddate)); + copy_string_to_attr((CK_BYTE *)&enddate, sizeof (enddate), + &(rsa_pri_attrs[i++])); + } + + /* Modulus n */ + cryptodebug("converting RSA private key modulus"); + if ((rv = cvt_bn2bigint(rsa->n, &mod)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert RSA private key modulus.")); + return (rv); + } + copy_bigint_to_attr(mod, &(rsa_pri_attrs[i++])); + + /* Public exponent e */ + cryptodebug("converting RSA private key public exponent"); + if ((rv = cvt_bn2bigint(rsa->e, &pubexp)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert RSA private key public exponent.")); + return (rv); + } + copy_bigint_to_attr(pubexp, &(rsa_pri_attrs[i++])); + + /* Private exponent d */ + if (rsa->d != NULL) { + cryptodebug("converting RSA private key private exponent"); + if ((rv = cvt_bn2bigint(rsa->d, &priexp)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext("Unable to convert " + "RSA private key private exponent.")); + return (rv); + } + rsa_pri_attrs[i].type = CKA_PRIVATE_EXPONENT; + copy_bigint_to_attr(priexp, &(rsa_pri_attrs[i++])); + } else + cryptodebug("no RSA private key private exponent"); + + /* Prime p */ + if (rsa->p != NULL) { + cryptodebug("converting RSA private key prime 1"); + if ((rv = cvt_bn2bigint(rsa->p, &prime1)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert RSA private key prime 1.")); + return (rv); + } + rsa_pri_attrs[i].type = CKA_PRIME_1; + copy_bigint_to_attr(prime1, &(rsa_pri_attrs[i++])); + } else + cryptodebug("no RSA private key prime 1"); + + /* Prime q */ + if (rsa->q != NULL) { + cryptodebug("converting RSA private key prime 2"); + if ((rv = cvt_bn2bigint(rsa->q, &prime2)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert RSA private key prime 2.")); + return (rv); + } + rsa_pri_attrs[i].type = CKA_PRIME_2; + copy_bigint_to_attr(prime2, &(rsa_pri_attrs[i++])); + } else + cryptodebug("no RSA private key prime 2"); + + /* Private exponent d modulo p-1 */ + if (rsa->dmp1 != NULL) { + cryptodebug("converting RSA private key exponent 1"); + if ((rv = cvt_bn2bigint(rsa->dmp1, &exp1)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert RSA private key exponent 1.")); + return (rv); + } + rsa_pri_attrs[i].type = CKA_EXPONENT_1; + copy_bigint_to_attr(exp1, &(rsa_pri_attrs[i++])); + } else + cryptodebug("no RSA private key exponent 1"); + + /* Private exponent d modulo q-1 */ + if (rsa->dmq1 != NULL) { + cryptodebug("converting RSA private key exponent 2"); + if ((rv = cvt_bn2bigint(rsa->dmq1, &exp2)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert RSA private key exponent 2.")); + return (rv); + } + rsa_pri_attrs[i].type = CKA_EXPONENT_2; + copy_bigint_to_attr(exp2, &(rsa_pri_attrs[i++])); + } else + cryptodebug("no RSA private key exponent 2"); + + /* CRT coefficient q-inverse mod p */ + if (rsa->iqmp != NULL) { + cryptodebug("converting RSA private key coefficient"); + if ((rv = cvt_bn2bigint(rsa->iqmp, &coef)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert RSA private key coefficient.")); + return (rv); + } + rsa_pri_attrs[i].type = CKA_COEFFICIENT; + copy_bigint_to_attr(coef, &(rsa_pri_attrs[i++])); + } else + cryptodebug("no RSA private key coefficient"); + + /* Indicates programming error: attributes overran the template */ + if (i > count) { + cryptodebug("error: more attributes found than accounted for"); + i = count; + } + + cryptodebug("calling C_CreateObject"); + if ((rv = C_CreateObject(sess, rsa_pri_attrs, i, &obj)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to create RSA private key object.")); + return (rv); + } + + return (CKR_OK); +} + +/* + * Write DSA private key to token. + */ +static CK_RV +write_dsa_private(CK_SESSION_HANDLE sess, DSA *dsa, X509 *cert) +{ + CK_RV rv = CKR_OK; + int i = 0; + static CK_OBJECT_CLASS objclass = CKO_PRIVATE_KEY; + static CK_KEY_TYPE keytype = CKK_DSA; + CK_BYTE *label = NULL; + CK_ULONG label_len = 0; + CK_BYTE *id = NULL; + CK_ULONG id_len = 0; + CK_DATE startdate = { "", "", "" }; + CK_DATE enddate = { "", "", "" }; + char tmpdate[8]; + biginteger_t prime = { NULL, 0 }; /* required */ + biginteger_t subprime = { NULL, 0 }; /* required */ + biginteger_t base = { NULL, 0 }; /* required */ + biginteger_t value = { NULL, 0 }; /* required */ + CK_ATTRIBUTE dsa_pri_attrs[12] = { + { CKA_CLASS, &objclass, sizeof (objclass) }, + { CKA_KEY_TYPE, &keytype, sizeof (keytype) }, + { CKA_PRIVATE, &pk_true, sizeof (pk_true) }, + { CKA_TOKEN, &pk_true, sizeof (pk_true) }, + { CKA_LABEL, NULL, 0 }, + { CKA_ID, NULL, 0 }, + { CKA_START_DATE, NULL, 0 }, + { CKA_END_DATE, NULL, 0 }, + { CKA_PRIME, NULL, 0 }, + { CKA_SUBPRIME, NULL, 0 }, + { CKA_BASE, NULL, 0 }, + { CKA_VALUE, NULL, 0 } + }; + CK_ULONG count = sizeof (dsa_pri_attrs) / sizeof (CK_ATTRIBUTE); + CK_OBJECT_HANDLE obj; + + cryptodebug("inside write_dsa_private"); + + /* Attributes start at array index 4. */ + i = 4; + + /* Recycle the certificate label for the private key label. */ + cryptodebug("calling X509_alias_get0"); + if ((label = X509_alias_get0(cert, (int *)&label_len)) == NULL) { + label = (CK_BYTE *)gettext("no label"); + label_len = strlen((char *)label); + } + copy_string_to_attr(label, label_len, &(dsa_pri_attrs[i++])); + + /* Recycle the certificate id for the private key id. */ + cryptodebug("calling PKTOOL_X509_keyid_get0"); + if ((id = PKTOOL_X509_keyid_get0(cert, (int *)&id_len)) == NULL) { + id = (CK_BYTE *)gettext("no id"); + id_len = strlen((char *)id); + } + copy_string_to_attr(id, id_len, &(dsa_pri_attrs[i++])); + + /* Recycle the certificate start and end dates for private key. */ + cryptodebug("calling X509_get_notBefore"); + if (PKTOOL_cvt_ossltime(X509_get_notBefore(cert), tmpdate)) { + (void) memcpy(&startdate, tmpdate, sizeof (startdate)); + copy_string_to_attr((CK_BYTE *)&startdate, sizeof (startdate), + &(dsa_pri_attrs[i++])); + } + + cryptodebug("calling X509_get_notAfter"); + if (PKTOOL_cvt_ossltime(X509_get_notAfter(cert), tmpdate)) { + (void) memcpy(&enddate, tmpdate, sizeof (enddate)); + copy_string_to_attr((CK_BYTE *)&enddate, sizeof (enddate), + &(dsa_pri_attrs[i++])); + } + + /* Prime p */ + cryptodebug("converting DSA private key prime"); + if ((rv = cvt_bn2bigint(dsa->p, &prime)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert DSA private key prime.")); + return (rv); + } + copy_bigint_to_attr(prime, &(dsa_pri_attrs[i++])); + + /* Subprime q */ + cryptodebug("converting DSA private key subprime"); + if ((rv = cvt_bn2bigint(dsa->q, &subprime)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert DSA private key subprime.")); + return (rv); + } + copy_bigint_to_attr(subprime, &(dsa_pri_attrs[i++])); + + /* Base g */ + cryptodebug("converting DSA private key base"); + if ((rv = cvt_bn2bigint(dsa->g, &base)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert DSA private key base.")); + return (rv); + } + copy_bigint_to_attr(base, &(dsa_pri_attrs[i++])); + + /* Private key x */ + cryptodebug("converting DSA private key value"); + if ((rv = cvt_bn2bigint(dsa->priv_key, &value)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert DSA private key value.")); + return (rv); + } + copy_bigint_to_attr(value, &(dsa_pri_attrs[i++])); + + /* Indicates programming error: attributes overran the template */ + if (i > count) { + cryptodebug("error: more attributes found than accounted for"); + i = count; + } + + cryptodebug("calling C_CreateObject"); + if ((rv = C_CreateObject(sess, dsa_pri_attrs, i, &obj)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to create DSA private key object.")); + return (rv); + } + + return (CKR_OK); +} + +/* + * Write DH private key to token. + */ +static CK_RV +write_dh_private(CK_SESSION_HANDLE sess, DH *dh, X509 *cert) +{ + CK_RV rv = CKR_OK; + int i = 0; + static CK_OBJECT_CLASS objclass = CKO_PRIVATE_KEY; + static CK_KEY_TYPE keytype = CKK_DH; + CK_BYTE *label = NULL; + CK_ULONG label_len = 0; + CK_BYTE *id = NULL; + CK_ULONG id_len = 0; + CK_DATE startdate = { "", "", "" }; + CK_DATE enddate = { "", "", "" }; + char tmpdate[8]; + biginteger_t prime = { NULL, 0 }; /* required */ + biginteger_t base = { NULL, 0 }; /* required */ + biginteger_t value = { NULL, 0 }; /* required */ + CK_ATTRIBUTE dh_pri_attrs[11] = { + { CKA_CLASS, &objclass, sizeof (objclass) }, + { CKA_KEY_TYPE, &keytype, sizeof (keytype) }, + { CKA_PRIVATE, &pk_true, sizeof (pk_true) }, + { CKA_TOKEN, &pk_true, sizeof (pk_true) }, + { CKA_LABEL, NULL, 0 }, + { CKA_ID, NULL, 0 }, + { CKA_START_DATE, NULL, 0 }, + { CKA_END_DATE, NULL, 0 }, + { CKA_PRIME, NULL, 0 }, + { CKA_BASE, NULL, 0 }, + { CKA_VALUE, NULL, 0 } + }; + CK_ULONG count = sizeof (dh_pri_attrs) / sizeof (CK_ATTRIBUTE); + CK_OBJECT_HANDLE obj; + + cryptodebug("inside write_dh_private"); + + /* Attributes start at array index 4. */ + i = 4; + + /* Recycle the certificate label for the private key label. */ + cryptodebug("calling X509_alias_get0"); + if ((label = X509_alias_get0(cert, (int *)&label_len)) == NULL) { + label = (CK_BYTE *)gettext("no label"); + label_len = strlen((char *)label); + } + copy_string_to_attr(label, label_len, &(dh_pri_attrs[i++])); + + /* Recycle the certificate id for the private key id. */ + cryptodebug("PKTOOL_X509_keyid_get0"); + if ((id = PKTOOL_X509_keyid_get0(cert, (int *)&id_len)) == NULL) { + id = (CK_BYTE *)gettext("no id"); + id_len = strlen((char *)id); + } + copy_string_to_attr(id, id_len, &(dh_pri_attrs[i++])); + + /* Recycle the certificate start and end dates for private key. */ + cryptodebug("calling X509_get_notBefore"); + if (PKTOOL_cvt_ossltime(X509_get_notBefore(cert), tmpdate)) { + (void) memcpy(&startdate, tmpdate, sizeof (startdate)); + copy_string_to_attr((CK_BYTE *)&startdate, sizeof (startdate), + &(dh_pri_attrs[i++])); + } + + cryptodebug("calling X509_get_notAfter"); + if (PKTOOL_cvt_ossltime(X509_get_notAfter(cert), tmpdate)) { + (void) memcpy(&enddate, tmpdate, sizeof (enddate)); + copy_string_to_attr((CK_BYTE *)&enddate, sizeof (enddate), + &(dh_pri_attrs[i++])); + } + + /* Prime p */ + cryptodebug("converting DH private key prime"); + if ((rv = cvt_bn2bigint(dh->p, &prime)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert DH private key prime.")); + return (rv); + } + copy_bigint_to_attr(prime, &(dh_pri_attrs[i++])); + + /* Base g */ + cryptodebug("converting DH private key base"); + if ((rv = cvt_bn2bigint(dh->g, &base)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert DH private key base.")); + return (rv); + } + copy_bigint_to_attr(base, &(dh_pri_attrs[i++])); + + /* Private value x */ + cryptodebug("converting DH private key value"); + if ((rv = cvt_bn2bigint(dh->priv_key, &value)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to convert DH private key value.")); + return (rv); + } + copy_bigint_to_attr(value, &(dh_pri_attrs[i++])); + + /* Indicates programming error: attributes overran the template */ + if (i > count) { + cryptodebug("error: more attributes found than accounted for"); + i = count; + } + + cryptodebug("calling C_CreateObject"); + if ((rv = C_CreateObject(sess, dh_pri_attrs, i, &obj)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to create DH private key object.")); + return (rv); + } + + return (CKR_OK); +} + +/* + * Write certificate to token. + */ +static CK_RV +write_cert(CK_SESSION_HANDLE sess, X509 *cert) +{ + CK_RV rv = CKR_OK; + int i = 0; + static CK_OBJECT_CLASS objclass = CKO_CERTIFICATE; + static CK_CERTIFICATE_TYPE certtype = CKC_X_509; + CK_BYTE *subject = NULL; + CK_ULONG subject_len = 0; + CK_BYTE *value = NULL; + CK_ULONG value_len = 0; + CK_BYTE *label = NULL; + CK_ULONG label_len = 0; + CK_BYTE *id = NULL; + CK_ULONG id_len = 0; + CK_BYTE *issuer = NULL; + CK_ULONG issuer_len = 0; + CK_BYTE *serial = NULL; + CK_ULONG serial_len = 0; + CK_ATTRIBUTE cert_attrs[9] = { + { CKA_CLASS, &objclass, sizeof (objclass) }, + { CKA_CERTIFICATE_TYPE, &certtype, sizeof (certtype) }, + { CKA_TOKEN, &pk_true, sizeof (pk_true) }, + { CKA_SUBJECT, NULL, 0 }, /* required */ + { CKA_VALUE, NULL, 0 }, /* required */ + { 0 /* CKA_LABEL */, NULL, 0 }, /* optional */ + { 0 /* CKA_ID */, NULL, 0 }, /* optional */ + { 0 /* CKA_ISSUER */, NULL, 0 }, /* optional */ + { 0 /* CKA_SERIAL_NUMBER */, NULL, 0 } /* optional */ + }; + CK_ULONG count = sizeof (cert_attrs) / sizeof (CK_ATTRIBUTE); + CK_OBJECT_HANDLE obj; + + cryptodebug("inside write_cert"); + + /* Attributes start at array index 3. */ + i = 3; + + /* + * OpenSSL subject name and issuer (a little further below) are + * actually stack structures that contain individual ASN.1 + * components. This stack of entries is packed into one DER string. + */ + cryptodebug("calling PKTOOL_X509_subject_name"); + if ((subject = PKTOOL_X509_subject_name(cert, (int *)&subject_len)) == + NULL) { + subject = (CK_BYTE *)gettext("no subject name"); + subject_len = strlen((char *)subject); + } + copy_string_to_attr(subject, subject_len, &(cert_attrs[i++])); + + /* Get cert value, but it has to be reconstructed from cert. */ + cryptodebug("calling PKTOOL_X509_cert_value"); + if ((value = PKTOOL_X509_cert_value(cert, (int *)&value_len)) == NULL) { + value = (CK_BYTE *)gettext("no value"); + value_len = strlen((char *)value); + } + copy_string_to_attr(value, value_len, &(cert_attrs[i++])); + + /* + * Get certificate label which is "friendlyName" Netscape, + * "alias" in OpenSSL. + */ + if ((label = X509_alias_get0(cert, (int *)&label_len)) == NULL) { + cryptodebug("no certificate label"); + } else { + cert_attrs[i].type = CKA_LABEL; + copy_string_to_attr(label, label_len, &(cert_attrs[i++])); + } + + /* Get the keyid for the cert. */ + if ((id = PKTOOL_X509_keyid_get0(cert, (int *)&id_len)) == NULL) { + cryptodebug("no certificate id"); + } else { + cert_attrs[i].type = CKA_ID; + copy_string_to_attr(id, id_len, &(cert_attrs[i++])); + } + + /* Get the issuer name for the cert. */ + if ((issuer = PKTOOL_X509_issuer_name(cert, (int *)&issuer_len)) == + NULL) { + cryptodebug("no certificate issuer name"); + } else { + cert_attrs[i].type = CKA_ISSUER; + copy_string_to_attr(issuer, issuer_len, &(cert_attrs[i++])); + } + + /* Get the cert serial number. */ + if ((serial = PKTOOL_X509_serial_number(cert, (int *)&serial_len)) == + NULL) { + cryptodebug("no certificate serial number"); + } else { + cert_attrs[i].type = CKA_SERIAL_NUMBER; + copy_string_to_attr(serial, serial_len, &(cert_attrs[i++])); + } + + /* Indicates programming error: attributes overran the template */ + if (i > count) { + cryptodebug("error: more attributes found than accounted for"); + i = count; + } + + cryptodebug("calling C_CreateObject"); + if ((rv = C_CreateObject(sess, cert_attrs, i, &obj)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to create X.509 certificate object.")); + return (rv); + } + + return (CKR_OK); +} + +/* + * Helper function to write PKCS#12 items to token. Returns CKR_OK + * or CKR_GENERAL_ERROR + */ +static CK_RV +write_token_objs(CK_SESSION_HANDLE sess, EVP_PKEY *priv_key, X509 *cert, + STACK_OF(X509) *ca, int *successes, int *failures) +{ + int i; + X509 *c; + CK_RV rv = CKR_OK; + + cryptodebug("inside write_token_objs"); + + /* Do not reset *successes or *failures -- keep running totals. */ + + /* Import user key. */ + switch (priv_key->type) { + case EVP_PKEY_RSA: + (void) fprintf(stdout, gettext("Writing RSA private key...\n")); + if ((rv = write_rsa_private(sess, + EVP_PKEY_get1_RSA(priv_key), cert)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to write RSA private key (%s)."), + pkcs11_strerror(rv)); + (*failures)++; + } else + (*successes)++; + break; + case EVP_PKEY_DSA: + (void) fprintf(stdout, gettext("Writing DSA private key...\n")); + if ((rv = write_dsa_private(sess, + EVP_PKEY_get1_DSA(priv_key), cert)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to write DSA private key (%s)."), + pkcs11_strerror(rv)); + (*failures)++; + } else + (*successes)++; + break; + case EVP_PKEY_DH: + (void) fprintf(stdout, gettext("Writing DH private key...\n")); + if ((rv = write_dh_private(sess, + EVP_PKEY_get1_DH(priv_key), cert)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to write DH private key (%s)."), + pkcs11_strerror(rv)); + (*failures)++; + } else + (*successes)++; + break; + + default: + /* + * Note that EVP_PKEY_DH for X9.42 is not implemented + * in the OpenSSL library. + */ + cryptoerror(LOG_STDERR, gettext( + "Private key type 0x%02x import not supported."), + priv_key->type); + (*failures)++; + break; + } + + /* Import user certificate. */ + (void) fprintf(stdout, gettext("Writing user certificate...\n")); + if ((rv = write_cert(sess, cert)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to write user certificate (%s)."), + pkcs11_strerror(rv)); + (*failures)++; + } else + (*successes)++; + + /* Import as many stacks of authority certificates as possible. */ + for (i = 0; i != sk_X509_num(ca); i++) { + /* + * sk_X509_value() is macro that embeds a cast to (X509 *). + * Here it translates into ((X509 *)sk_value((ca), (i))). + * Lint is complaining about the embedded casting, and + * to fix it, you need to fix openssl header files. + */ + /* LINTED E_BAD_PTR_CAST_ALIGN */ + c = sk_X509_value(ca, i); + (void) fprintf(stdout, gettext( + "Writing authority certificate...\n")); + if ((rv = write_cert(sess, c)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to write authority certificate (%s)."), + pkcs11_strerror(rv)); + (*failures)++; + } else + (*successes)++; + } + + (void) fprintf(stdout, gettext("PKCS#12 element scan completed.\n")); + return (*failures != 0 ? CKR_GENERAL_ERROR : CKR_OK); +} + +/* + * Import objects from PKCS#12 file into token. + */ +int +pk_import(int argc, char *argv[]) +{ + char *token_name = NULL; + char *manuf_id = NULL; + char *serial_no = NULL; + char full_name[FULL_NAME_LEN]; + char *filename = NULL; + struct stat statbuf; + CK_SLOT_ID slot_id; + CK_FLAGS pin_state; + CK_UTF8CHAR_PTR pin = NULL; + CK_ULONG pinlen = 0; + CK_UTF8CHAR_PTR pk12pin = NULL; + CK_ULONG pk12pinlen = 0; + CK_SESSION_HANDLE sess; + BIO *fbio = NULL; + EVP_PKEY *priv_key = NULL; + X509 *cert = NULL; + STACK_OF(X509) *ca = NULL; + CK_RV rv = CKR_OK; + int i; + int good_count = 0, bad_count = 0; /* running totals */ + + cryptodebug("inside pk_import"); + + /* Get rid of subcommand word "import". */ + argc--; + argv++; + + /* One additional arg required: filename. */ + if (argc != 1) + return (PK_ERR_USAGE); + + filename = argv[0]; + /* Done parsing command line options. */ + + /* Check that the file exists and is non-empty. */ + if (access(filename, R_OK) < 0) { + cryptoerror(LOG_STDERR, gettext("File \"%s\" is unreadable " + "(%s)."), filename, strerror(errno)); + return (CKR_OK); + } + if (stat(filename, &statbuf) < 0) { + cryptoerror(LOG_STDERR, gettext("Unable to get size of " + "file \"%s\" (%s)."), filename, strerror(errno)); + return (CKR_OK); + } + if (statbuf.st_size == 0) { + cryptoerror(LOG_STDERR, gettext("File \"%s\" is empty."), + filename); + return (CKR_OK); + } + + /* Import operation only supported on softtoken. */ + if (token_name == NULL) + token_name = SOFT_TOKEN_LABEL; + if (manuf_id == NULL) + manuf_id = SOFT_MANUFACTURER_ID; + if (serial_no == NULL) + serial_no = SOFT_TOKEN_SERIAL; + full_token_name(token_name, manuf_id, serial_no, full_name); + + /* Find the slot with token. */ + if ((rv = find_token_slot(token_name, manuf_id, serial_no, &slot_id, + &pin_state)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to find token %s (%s)."), full_name, + pkcs11_strerror(rv)); + return (PK_ERR_PK11); + } + + /* Get the user's PIN. */ + if ((rv = get_pin(gettext("Enter token passphrase:"), NULL, &pin, + &pinlen)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get token passphrase (%s)."), + pkcs11_strerror(rv)); + quick_finish(NULL); + return (PK_ERR_PK11); + } + + /* Assume user must be logged in R/W to import objects into token. */ + if ((rv = quick_start(slot_id, CKF_RW_SESSION, pin, pinlen, &sess)) != + CKR_OK) { + cryptoerror(LOG_STDERR, + gettext("Unable to log into token (%s)."), + pkcs11_strerror(rv)); + quick_finish(sess); + return (PK_ERR_PK11); + } + + /* Setup OpenSSL context. */ + PKTOOL_setup_openssl(); + + /* Open PKCS#12 file. */ + if ((open_pkcs12(filename, &fbio)) < 0) { + cryptoerror(LOG_STDERR, gettext("Unable to open import file.")); + quick_finish(sess); + return (PK_ERR_SYSTEM); + } + + /* Get the PIN for the PKCS#12 import file. */ + if ((rv = get_pin(gettext("Enter import file passphrase:"), NULL, + &pk12pin, &pk12pinlen)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get import file passphrase (%s)."), + pkcs11_strerror(rv)); + close_pkcs12(fbio); + quick_finish(sess); + return (PK_ERR_PK11); + } + + /* PKCS#12 import file may have multiple elements, loop until done. */ + for (i = 0; /* */; i++) { + /* Extract the contents of the PKCS#12 import file. */ + if ((rv = extract_pkcs12(fbio, pk12pin, pk12pinlen, &priv_key, + &cert, &ca)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to parse PKCS#12 element #%d " + "in import file (%s)."), i+1, pkcs11_strerror(rv)); + close_pkcs12(fbio); + quick_finish(sess); + return (PK_ERR_OPENSSL); + } + + /* Reached end of import file? */ + if (rv == CKR_OK && priv_key == NULL && cert == NULL && + ca == NULL) + break; + + (void) fprintf(stdout, gettext( + "Scanning PKCS#12 element #%d for objects...\n"), i+1); + + /* Write the objects to the token. */ + if ((rv = write_token_objs(sess, priv_key, cert, ca, + &good_count, &bad_count)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to write PKCS#12 element #%d to token %s."), + i+1, full_name); + close_pkcs12(fbio); + quick_finish(sess); + return (PK_ERR_PK11); + } + } + + (void) fprintf(stdout, gettext("%d PKCS#12 elements scanned: " + "%d objects imported, %d errors occurred.\n"), i, + good_count, bad_count); + + /* Close PKCS#12 file. */ + close_pkcs12(fbio); + + /* Clean up. */ + quick_finish(sess); + return (0); +} diff --git a/usr/src/cmd/cmd-crypto/pktool/list.c b/usr/src/cmd/cmd-crypto/pktool/list.c new file mode 100644 index 0000000000..2e4bc811e5 --- /dev/null +++ b/usr/src/cmd/cmd-crypto/pktool/list.c @@ -0,0 +1,1016 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file implements the token object list operation for this tool. + * It loads the PKCS#11 modules, finds the object to list, lists it, + * and cleans up. User must be logged into the token to list private + * objects. + */ + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <cryptoutil.h> +#include <security/cryptoki.h> +#include "common.h" +#include "derparse.h" + +/* + * Get key size based on the key type. + */ +static CK_ULONG +get_key_size(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj, CK_KEY_TYPE key_type) +{ + CK_RV rv = CKR_OK; + CK_ULONG key_size; + CK_ATTRIBUTE modulus_sz = + { CKA_MODULUS, NULL, 0 }; /* RSA */ + CK_ATTRIBUTE prime_sz = + { CKA_PRIME, NULL, 0 }; /* DSA, DH X9.42 */ + CK_ATTRIBUTE value_sz = + { CKA_VALUE, NULL_PTR, 0 }; /* DH, DES/DES3, AES, GENERIC */ + + cryptodebug("inside get_key_size"); + + switch (key_type) { + case CKK_RSA: + if ((rv = C_GetAttributeValue(sess, obj, &modulus_sz, 1)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get modulus attribute size (%s)."), + pkcs11_strerror(rv)); + } else + /* Convert key size to bits. */ + key_size = modulus_sz.ulValueLen * 8; + break; + case CKK_DH: + if ((rv = C_GetAttributeValue(sess, obj, &value_sz, 1)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get value attribute size (%s)."), + pkcs11_strerror(rv)); + } else + /* Convert key size to bits. */ + key_size = value_sz.ulValueLen * 8; + break; + case CKK_X9_42_DH: + case CKK_DSA: + if ((rv = C_GetAttributeValue(sess, obj, &prime_sz, 1)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get prime attribute size (%s)."), + pkcs11_strerror(rv)); + } else + /* Convert key size to bits. */ + key_size = prime_sz.ulValueLen * 8; + break; + case CKK_DES: + case CKK_DES3: + if ((rv = C_GetAttributeValue(sess, obj, &value_sz, 1)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get value attribute size (%s)."), + pkcs11_strerror(rv)); + } else + /* Convert key size to bits -- omitting parity bit. */ + key_size = value_sz.ulValueLen * 7; + break; + case CKK_AES: + case CKK_GENERIC_SECRET: + if ((rv = C_GetAttributeValue(sess, obj, &value_sz, 1)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get value attribute size (%s)."), + pkcs11_strerror(rv)); + } else + /* Convert key size to bits. */ + key_size = value_sz.ulValueLen * 8; + break; + default: + cryptoerror(LOG_STDERR, gettext( + "Unknown object key type (0x%02x)."), key_type); + break; + } + + return (key_size); +} + +/* + * Display private key. + */ +static CK_RV +display_prikey(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj, int counter) +{ + CK_RV rv = CKR_OK; + static CK_BBOOL private; + static CK_BBOOL modifiable; + static CK_KEY_TYPE key_type; + CK_ULONG key_size; + CK_BYTE *label = NULL; + CK_ULONG label_len = 0; + CK_BYTE *id = NULL; + CK_ULONG id_len = 0; + CK_BYTE *subject = NULL; + CK_ULONG subject_len = 0; + CK_DATE *start_date = NULL; + CK_ULONG start_date_len = 0; + CK_DATE *end_date = NULL; + CK_ULONG end_date_len = 0; + CK_ATTRIBUTE attrs[18] = { + /* 0 to 2 */ + { CKA_PRIVATE, &private, sizeof (private) }, + { CKA_MODIFIABLE, &modifiable, sizeof (modifiable) }, + { CKA_KEY_TYPE, &key_type, sizeof (key_type) }, + /* 3 to 12 */ + { CKA_DERIVE, NULL, 0 }, + { CKA_LOCAL, NULL, 0 }, + { CKA_DECRYPT, NULL, 0 }, + { CKA_SIGN, NULL, 0 }, + { CKA_SIGN_RECOVER, NULL, 0 }, + { CKA_UNWRAP, NULL, 0 }, + { CKA_SENSITIVE, NULL, 0 }, + { CKA_ALWAYS_SENSITIVE, NULL, 0 }, + { CKA_EXTRACTABLE, NULL, 0 }, + { CKA_NEVER_EXTRACTABLE, NULL, 0 }, + /* 13 to 17 */ + { CKA_LABEL, NULL, 0 }, /* optional */ + { CKA_ID, NULL, 0 }, /* optional */ + { CKA_SUBJECT, NULL, 0 }, /* optional */ + { CKA_START_DATE, NULL, 0 }, /* optional */ + { CKA_END_DATE, NULL, 0 } /* optional */ + /* not displaying CKA_KEY_GEN_MECHANISM */ + }; + CK_ULONG n_attrs = sizeof (attrs) / sizeof (CK_ATTRIBUTE); + int i; + char *hex_id = NULL; + int hex_id_len = 0; + char *hex_subject = NULL; + int hex_subject_len = 0; + + cryptodebug("inside display_prikey"); + + /* Get the sizes of the attributes we need. */ + cryptodebug("calling C_GetAttributeValue for size info"); + if ((rv = C_GetAttributeValue(sess, obj, attrs, n_attrs)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get private key attribute sizes (%s)."), + pkcs11_strerror(rv)); + return (rv); + } + + /* Allocate memory for each variable-length attribute. */ + for (i = 3; i < n_attrs; i++) { + if (attrs[i].ulValueLen == (CK_ULONG)-1 || + attrs[i].ulValueLen == 0) { + cryptodebug("display_prikey: *** should not happen"); + attrs[i].ulValueLen = 0; + continue; + } + if ((attrs[i].pValue = malloc(attrs[i].ulValueLen)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + rv = CKR_HOST_MEMORY; + goto free_display_prikey; + } + } + + /* Now really get the attributes. */ + cryptodebug("calling C_GetAttributeValue for attribute info"); + if ((rv = C_GetAttributeValue(sess, obj, attrs, n_attrs)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get private key attributes (%s)."), + pkcs11_strerror(rv)); + goto free_display_prikey; + } + + /* Fill in all the optional temp variables. */ + i = 13; + copy_attr_to_string(&(attrs[i++]), &label, &label_len); + copy_attr_to_string(&(attrs[i++]), &id, &id_len); + copy_attr_to_string(&(attrs[i++]), &subject, &subject_len); + copy_attr_to_date(&(attrs[i++]), &start_date, &start_date_len); + copy_attr_to_date(&(attrs[i++]), &end_date, &end_date_len); + + /* Get the key size for the object. */ + key_size = get_key_size(sess, obj, key_type); + + /* Display the object ... */ + /* ... the label and what it is (and key size in bits) ... */ + (void) fprintf(stdout, gettext("%d. \"%.*s\" (%d-bit %s %s)\n"), + counter, label_len, label_len > 0 ? (char *)label : + gettext("<no label>"), key_size, keytype_str(key_type), + class_str(CKO_PRIVATE_KEY)); + + /* ... the id ... */ + if (id_len == (CK_ULONG)-1 || id_len == 0) + (void) fprintf(stdout, gettext("\tId: --\n")); + else { + hex_id_len = 3 * id_len + 1; + if ((hex_id = malloc(hex_id_len)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + rv = CKR_HOST_MEMORY; + goto free_display_prikey; + } + octetify(id, id_len, hex_id, hex_id_len, B_FALSE, B_FALSE, 60, + "\n\t\t", ""); + (void) fprintf(stdout, gettext("\tId: %s\n"), hex_id); + free(hex_id); + } + + /* ... the subject name ... */ + if (subject_len == (CK_ULONG)-1 || subject_len == 0) + (void) fprintf(stdout, gettext("\tSubject: --\n")); + else { + hex_subject_len = 2 * subject_len + 1; /* best guesstimate */ + if ((hex_subject = malloc(hex_subject_len)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + rv = CKR_HOST_MEMORY; + goto free_display_prikey; + } + rdnseq_to_str(subject, subject_len, hex_subject, + hex_subject_len); + (void) fprintf(stdout, gettext("\tSubject: %.*s\n"), + hex_subject_len, hex_subject); + free(hex_subject); + } + + /* ... the start date ... */ + if (start_date_len == (CK_ULONG)-1 || start_date_len == 0) + (void) fprintf(stdout, gettext("\tStart Date: --\n")); + else + (void) fprintf(stdout, gettext( + "\tStart Date: %02.2s/%02.2s/%04.4s\n"), + start_date->month, start_date->day, start_date->year); + + /* ... the end date ... */ + if (end_date_len == (CK_ULONG)-1 || end_date_len == 0) + (void) fprintf(stdout, gettext("\tEnd Date: --\n")); + else + (void) fprintf(stdout, gettext( + "\tEnd Date: %02.2s/%02.2s/%04.4s\n"), + end_date->month, end_date->day, end_date->year); + + /* ... and its capabilities */ + (void) fprintf(stdout, "\t(%s, %s", + private != pk_false ? gettext("private") : gettext("public"), + modifiable == B_TRUE ? gettext("modifiable") : + gettext("not modifiable")); + for (i = 3; i <= 12; i++) { + if (attrs[i].ulValueLen != (CK_ULONG)-1 && + attrs[i].ulValueLen != 0 && + *((CK_BBOOL *)(attrs[i].pValue)) == B_TRUE) + (void) fprintf(stdout, ", %s", attr_str(attrs[i].type)); + } + (void) fprintf(stdout, ")\n"); + +free_display_prikey: + for (i = 3; i < n_attrs; i++) + if (attrs[i].ulValueLen != (CK_ULONG)-1 && + attrs[i].ulValueLen != 0) + free(attrs[i].pValue); + return (rv); +} + +/* + * Display public key. + */ +static CK_RV +display_pubkey(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj, int counter) +{ + CK_RV rv = CKR_OK; + static CK_BBOOL private; + static CK_BBOOL modifiable; + static CK_BBOOL trusted; + static CK_KEY_TYPE key_type; + CK_ULONG key_size; + CK_BYTE *label = NULL; + CK_ULONG label_len = 0; + CK_BYTE *id = NULL; + CK_ULONG id_len = 0; + CK_BYTE *subject = NULL; + CK_ULONG subject_len = 0; + CK_DATE *start_date = NULL; + CK_ULONG start_date_len = 0; + CK_DATE *end_date = NULL; + CK_ULONG end_date_len = 0; + CK_ATTRIBUTE attrs[15] = { + /* 0 to 3 */ + { CKA_PRIVATE, &private, sizeof (private) }, + { CKA_MODIFIABLE, &modifiable, sizeof (modifiable) }, + { CKA_TRUSTED, &trusted, sizeof (trusted) }, + { CKA_KEY_TYPE, &key_type, sizeof (key_type) }, + /* 4 to 9 */ + { CKA_DERIVE, NULL, 0 }, + { CKA_LOCAL, NULL, 0 }, + { CKA_ENCRYPT, NULL, 0 }, + { CKA_VERIFY, NULL, 0 }, + { CKA_VERIFY_RECOVER, NULL, 0 }, + { CKA_WRAP, NULL, 0 }, + /* 10 to 14 */ + { CKA_LABEL, NULL, 0 }, /* optional */ + { CKA_ID, NULL, 0 }, /* optional */ + { CKA_SUBJECT, NULL, 0 }, /* optional */ + { CKA_START_DATE, NULL, 0 }, /* optional */ + { CKA_END_DATE, NULL, 0 } /* optional */ + /* not displaying CKA_KEY_GEN_MECHANISM */ + }; + CK_ULONG n_attrs = sizeof (attrs) / sizeof (CK_ATTRIBUTE); + int i; + char *hex_id = NULL; + int hex_id_len = 0; + char *hex_subject = NULL; + int hex_subject_len = 0; + + cryptodebug("inside display_pubkey"); + + /* Get the sizes of the attributes we need. */ + cryptodebug("calling C_GetAttributeValue for size info"); + if ((rv = C_GetAttributeValue(sess, obj, attrs, n_attrs)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get public key attribute sizes (%s)."), + pkcs11_strerror(rv)); + return (rv); + } + + /* Allocate memory for each variable-length attribute. */ + for (i = 4; i < n_attrs; i++) { + if (attrs[i].ulValueLen == (CK_ULONG)-1 || + attrs[i].ulValueLen == 0) { + cryptodebug("display_pubkey: *** should not happen"); + attrs[i].ulValueLen = 0; + continue; + } + if ((attrs[i].pValue = malloc(attrs[i].ulValueLen)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + rv = CKR_HOST_MEMORY; + goto free_display_pubkey; + } + } + + /* Now really get the attributes. */ + cryptodebug("calling C_GetAttributeValue for attribute info"); + if ((rv = C_GetAttributeValue(sess, obj, attrs, n_attrs)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get public key attributes (%s)."), + pkcs11_strerror(rv)); + goto free_display_pubkey; + } + + /* Fill in all the optional temp variables. */ + i = 10; + copy_attr_to_string(&(attrs[i++]), &label, &label_len); + copy_attr_to_string(&(attrs[i++]), &id, &id_len); + copy_attr_to_string(&(attrs[i++]), &subject, &subject_len); + copy_attr_to_date(&(attrs[i++]), &start_date, &start_date_len); + copy_attr_to_date(&(attrs[i++]), &end_date, &end_date_len); + + /* Get the key size for the object. */ + key_size = get_key_size(sess, obj, key_type); + + /* Display the object ... */ + /* ... the label and what it is (and key size in bits) ... */ + (void) fprintf(stdout, gettext("%d. \"%.*s\" (%d-bit %s %s)\n"), + counter, label_len, label_len > 0 ? (char *)label : + gettext("<no label>"), key_size, keytype_str(key_type), + class_str(CKO_PUBLIC_KEY)); + + /* ... the id ... */ + if (id_len == (CK_ULONG)-1 || id_len == 0) + (void) fprintf(stdout, gettext("\tId: --\n")); + else { + hex_id_len = 3 * id_len + 1; + if ((hex_id = malloc(hex_id_len)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + rv = CKR_HOST_MEMORY; + goto free_display_pubkey; + } + octetify(id, id_len, hex_id, hex_id_len, B_FALSE, B_FALSE, 60, + "\n\t\t", ""); + (void) fprintf(stdout, gettext("\tId: %s\n"), hex_id); + free(hex_id); + } + + /* ... the subject name ... */ + if (subject_len == (CK_ULONG)-1 || subject_len == 0) + (void) fprintf(stdout, gettext("\tSubject: --\n")); + else { + hex_subject_len = 2 * subject_len + 1; /* best guesstimate */ + if ((hex_subject = malloc(hex_subject_len)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + rv = CKR_HOST_MEMORY; + goto free_display_pubkey; + } + rdnseq_to_str(subject, subject_len, hex_subject, + hex_subject_len); + (void) fprintf(stdout, gettext("\tSubject: %.*s\n"), + hex_subject_len, hex_subject); + free(hex_subject); + } + + /* ... the start date ... */ + if (start_date_len == (CK_ULONG)-1 || start_date_len == 0) + (void) fprintf(stdout, gettext("\tStart Date: --\n")); + else + (void) fprintf(stdout, gettext( + "\tStart Date: %02.2s/%02.2s/%04.4s\n"), + start_date->month, start_date->day, start_date->year); + + /* ... the end date ... */ + if (end_date_len == (CK_ULONG)-1 || end_date_len == 0) + (void) fprintf(stdout, gettext("\tEnd Date: --\n")); + else + (void) fprintf(stdout, gettext( + "\tEnd Date: %02.2s/%02.2s/%04.4s\n"), + end_date->month, end_date->day, end_date->year); + + /* ... and its capabilities */ + (void) fprintf(stdout, "\t(%s, %s, %s", + private == B_TRUE ? gettext("private") : gettext("public"), + modifiable == B_TRUE ? gettext("modifiable") : + gettext("not modifiable"), + trusted == B_TRUE ? gettext("trusted") : gettext("untrusted")); + for (i = 4; i <= 9; i++) { + if (attrs[i].ulValueLen != (CK_ULONG)-1 && + attrs[i].ulValueLen != 0 && + *((CK_BBOOL *)(attrs[i].pValue)) == B_TRUE) + (void) fprintf(stdout, ", %s", attr_str(attrs[i].type)); + } + (void) fprintf(stdout, ")\n"); + +free_display_pubkey: + for (i = 4; i < n_attrs; i++) + if (attrs[i].ulValueLen != (CK_ULONG)-1 && + attrs[i].ulValueLen != 0) + free(attrs[i].pValue); + return (rv); +} + +/* + * Display secret key. + */ +static CK_RV +display_seckey(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj, int counter) +{ + CK_RV rv = CKR_OK; + static CK_BBOOL private; + static CK_BBOOL modifiable; + static CK_KEY_TYPE key_type; + static CK_ULONG key_size; + CK_BYTE *label = NULL; + CK_ULONG label_len = 0; + CK_BYTE *id = NULL; + CK_ULONG id_len = 0; + CK_DATE *start_date = NULL; + CK_ULONG start_date_len = 0; + CK_DATE *end_date = NULL; + CK_ULONG end_date_len = 0; + CK_ATTRIBUTE attrs[19] = { + /* 0 to 2 */ + { CKA_PRIVATE, &private, sizeof (private) }, + { CKA_MODIFIABLE, &modifiable, sizeof (modifiable) }, + { CKA_KEY_TYPE, &key_type, sizeof (key_type) }, + /* 3 to 14 */ + { CKA_DERIVE, NULL, 0 }, + { CKA_LOCAL, NULL, 0 }, + { CKA_ENCRYPT, NULL, 0 }, + { CKA_DECRYPT, NULL, 0 }, + { CKA_SIGN, NULL, 0 }, + { CKA_VERIFY, NULL, 0 }, + { CKA_WRAP, NULL, 0 }, + { CKA_UNWRAP, NULL, 0 }, + { CKA_SENSITIVE, NULL, 0 }, + { CKA_ALWAYS_SENSITIVE, NULL, 0 }, + { CKA_EXTRACTABLE, NULL, 0 }, + { CKA_NEVER_EXTRACTABLE, 0 }, + /* 15 to 18 */ + { CKA_LABEL, NULL, 0 }, /* optional */ + { CKA_ID, NULL, 0 }, /* optional */ + { CKA_START_DATE, NULL, 0 }, /* optional */ + { CKA_END_DATE, NULL, 0 } /* optional */ + /* not displaying CKA_KEY_GEN_MECHANISM */ + }; + CK_ULONG n_attrs = sizeof (attrs) / sizeof (CK_ATTRIBUTE); + int i; + char *hex_id = NULL; + int hex_id_len = 0; + + cryptodebug("inside display_seckey"); + + /* Get the sizes of the attributes we need. */ + cryptodebug("calling C_GetAttributeValue for size info"); + if ((rv = C_GetAttributeValue(sess, obj, attrs, n_attrs)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get secret key attribute sizes (%s)."), + pkcs11_strerror(rv)); + return (rv); + } + + /* Allocate memory for each variable-length attribute. */ + for (i = 3; i < n_attrs; i++) { + if (attrs[i].ulValueLen == (CK_ULONG)-1 || + attrs[i].ulValueLen == 0) { + cryptodebug("display_seckey: *** should not happen"); + attrs[i].ulValueLen = 0; + continue; + } + if ((attrs[i].pValue = malloc(attrs[i].ulValueLen)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + rv = CKR_HOST_MEMORY; + goto free_display_seckey; + } + } + + /* Now really get the attributes. */ + cryptodebug("calling C_GetAttributeValue for attribute info"); + if ((rv = C_GetAttributeValue(sess, obj, attrs, n_attrs)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get secret key attributes (%s)."), + pkcs11_strerror(rv)); + goto free_display_seckey; + } + + /* Fill in all the optional temp variables. */ + i = 15; + copy_attr_to_string(&(attrs[i++]), &label, &label_len); + copy_attr_to_string(&(attrs[i++]), &id, &id_len); + copy_attr_to_date(&(attrs[i++]), &start_date, &start_date_len); + copy_attr_to_date(&(attrs[i++]), &end_date, &end_date_len); + + /* Get the key size for the object. */ + key_size = get_key_size(sess, obj, key_type); + + /* Display the object ... */ + /* ... the label and what it is (and key size in bytes) ... */ + (void) fprintf(stdout, gettext("%d. \"%.*s\" (%d-bit %s %s)\n"), + counter, label_len, label_len > 0 ? (char *)label : + gettext("<no label>"), key_size, keytype_str(key_type), + class_str(CKO_SECRET_KEY)); + + /* ... the id ... */ + if (id_len == (CK_ULONG)-1 || id_len == 0) + (void) fprintf(stdout, gettext("\tId: --\n")); + else { + hex_id_len = 3 * id_len + 1; + if ((hex_id = malloc(hex_id_len)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + rv = CKR_HOST_MEMORY; + goto free_display_seckey; + } + octetify(id, id_len, hex_id, hex_id_len, B_FALSE, B_FALSE, 60, + "\n\t\t", ""); + (void) fprintf(stdout, gettext("\tId: %s\n"), hex_id); + free(hex_id); + } + + /* ... the start date ... */ + if (start_date_len == (CK_ULONG)-1 || start_date_len == 0) + (void) fprintf(stdout, gettext("\tStart Date: --\n")); + else + (void) fprintf(stdout, gettext( + "\tStart Date: %02.2s/%02.2s/%04.4s\n"), + start_date->month, start_date->day, start_date->year); + + /* ... the end date ... */ + if (end_date_len == (CK_ULONG)-1 || end_date_len == 0) + (void) fprintf(stdout, gettext("\tEnd Date: --\n")); + else + (void) fprintf(stdout, gettext( + "\tEnd Date: %02.2s/%02.2s/%04.4s\n"), + end_date->month, end_date->day, end_date->year); + + /* ... and its capabilities */ + (void) fprintf(stdout, "\t(%s, %s", + private == B_TRUE ? gettext("private") : gettext("public"), + modifiable == B_TRUE ? gettext("modifiable") : + gettext("not modifiable")); + for (i = 3; i <= 14; i++) { + if (attrs[i].ulValueLen != (CK_ULONG)-1 && + attrs[i].ulValueLen != 0 && + *((CK_BBOOL *)(attrs[i].pValue)) == B_TRUE) + (void) fprintf(stdout, ", %s", attr_str(attrs[i].type)); + } + (void) fprintf(stdout, ")\n"); + +free_display_seckey: + for (i = 3; i < n_attrs; i++) + if (attrs[i].ulValueLen != (CK_ULONG)-1 && + attrs[i].ulValueLen != 0) + free(attrs[i].pValue); + return (rv); +} + +/* + * Display certificate. + */ +static CK_RV +display_cert(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj, int counter) +{ + CK_RV rv = CKR_OK; + static CK_BBOOL private; + static CK_BBOOL modifiable; + static CK_BBOOL trusted; + CK_BYTE *subject = NULL; + CK_ULONG subject_len = 0; + CK_BYTE *value = NULL; + CK_ULONG value_len = 0; + CK_BYTE *label = NULL; + CK_ULONG label_len = 0; + CK_BYTE *id = NULL; + CK_ULONG id_len = 0; + CK_BYTE *issuer = NULL; + CK_ULONG issuer_len = 0; + CK_BYTE *serial = NULL; + CK_ULONG serial_len = 0; + CK_ATTRIBUTE attrs[9] = { + { CKA_PRIVATE, &private, sizeof (private) }, + { CKA_MODIFIABLE, &modifiable, sizeof (modifiable) }, + { CKA_TRUSTED, &trusted, sizeof (trusted) }, + { CKA_SUBJECT, NULL, 0 }, /* required */ + { CKA_VALUE, NULL, 0 }, /* required */ + { CKA_LABEL, NULL, 0 }, /* optional */ + { CKA_ID, NULL, 0 }, /* optional */ + { CKA_ISSUER, NULL, 0 }, /* optional */ + { CKA_SERIAL_NUMBER, NULL, 0 } /* optional */ + }; + CK_ULONG n_attrs = sizeof (attrs) / sizeof (CK_ATTRIBUTE); + int i; + char *hex_id = NULL; + int hex_id_len = 0; + char *hex_subject = NULL; + int hex_subject_len = 0; + char *hex_issuer = NULL; + int hex_issuer_len = 0; + char *hex_serial = NULL; + int hex_serial_len = NULL; + uint32_t serial_value = 0; + char *hex_value = NULL; + int hex_value_len = 0; + + cryptodebug("inside display_cert"); + + /* Get the sizes of the attributes we need. */ + cryptodebug("calling C_GetAttributeValue for size info"); + if ((rv = C_GetAttributeValue(sess, obj, attrs, n_attrs)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get certificate attribute sizes (%s)."), + pkcs11_strerror(rv)); + return (rv); + } + + /* Allocate memory for each variable-length attribute. */ + for (i = 3; i < n_attrs; i++) { + if (attrs[i].ulValueLen == (CK_ULONG)-1 || + attrs[i].ulValueLen == 0) { + cryptodebug("display_cert: *** should not happen"); + attrs[i].ulValueLen = 0; + continue; + } + if ((attrs[i].pValue = malloc(attrs[i].ulValueLen)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + rv = CKR_HOST_MEMORY; + goto free_display_cert; + } + } + + /* Now really get the attributes. */ + cryptodebug("calling C_GetAttributeValue for attribute info"); + if ((rv = C_GetAttributeValue(sess, obj, attrs, n_attrs)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get certificate attributes (%s)."), + pkcs11_strerror(rv)); + goto free_display_cert; + } + + /* + * Fill in all the temp variables. Subject and value are required. + * The rest are optional. + */ + i = 3; + copy_attr_to_string(&(attrs[i++]), &subject, &subject_len); + copy_attr_to_string(&(attrs[i++]), &value, &value_len); + copy_attr_to_string(&(attrs[i++]), &label, &label_len); + copy_attr_to_string(&(attrs[i++]), &id, &id_len); + copy_attr_to_string(&(attrs[i++]), &issuer, &issuer_len); + copy_attr_to_string(&(attrs[i++]), &serial, &serial_len); + + /* Display the object ... */ + /* ... the label and what it is ... */ + (void) fprintf(stdout, gettext("%d. \"%.*s\" (%s %s)\n"), + counter, label_len, label_len > 0 ? (char *)label : + gettext("<no label>"), "X.509", class_str(CKO_CERTIFICATE)); + + /* ... its capabilities ... */ + (void) fprintf(stdout, gettext("\t(%s, %s, %s)\n"), + private == B_TRUE ? gettext("private") : gettext("public"), + modifiable == B_TRUE ? gettext("modifiable") : + gettext("not modifiable"), + trusted == B_TRUE ? gettext("trusted") : gettext("untrusted")); + + /* ... the id ... */ + if (id_len == (CK_ULONG)-1 || id_len == 0) + (void) fprintf(stdout, gettext("\tId: --\n")); + else { + hex_id_len = 3 * id_len + 1; + if ((hex_id = malloc(hex_id_len)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + rv = CKR_HOST_MEMORY; + goto free_display_cert; + } + octetify(id, id_len, hex_id, hex_id_len, B_FALSE, B_FALSE, 60, + "\n\t\t", ""); + (void) fprintf(stdout, gettext("\tId: %s\n"), hex_id); + free(hex_id); + } + + /* ... the subject name ... */ + if (subject_len == (CK_ULONG)-1 || subject_len == 0) + (void) fprintf(stdout, gettext("\tSubject: --\n")); + else { + hex_subject_len = 2 * subject_len + 1; /* best guesstimate */ + if ((hex_subject = malloc(hex_subject_len)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + rv = CKR_HOST_MEMORY; + goto free_display_cert; + } + rdnseq_to_str(subject, subject_len, hex_subject, + hex_subject_len); + (void) fprintf(stdout, gettext("\tSubject: %.*s\n"), + hex_subject_len, hex_subject); + free(hex_subject); + } + + /* ... the issuer name ... */ + if (issuer_len == (CK_ULONG)-1 || issuer_len == 0) + (void) fprintf(stdout, gettext("\tIssuer: --\n")); + else { + hex_issuer_len = 2 * issuer_len + 1; /* best guesstimate */ + if ((hex_issuer = malloc(hex_issuer_len)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + rv = CKR_HOST_MEMORY; + goto free_display_cert; + } + rdnseq_to_str(issuer, issuer_len, hex_issuer, hex_issuer_len); + (void) fprintf(stdout, gettext("\tIssuer: %.*s\n"), + hex_issuer_len, hex_issuer); + free(hex_issuer); + } + + /* ... the serial number ... */ + if (serial_len == (CK_ULONG)-1 || serial_len == 0) + (void) fprintf(stdout, gettext("\tSerial: --\n")); + else { + hex_serial_len = 3 * serial_len + 1; + if ((hex_serial = malloc(hex_serial_len)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + rv = CKR_HOST_MEMORY; + goto free_display_cert; + } + octetify(serial, serial_len, hex_serial, hex_serial_len, + B_FALSE, B_FALSE, 60, "\n\t\t", ""); + if (serial_len > 4) + (void) fprintf(stdout, gettext("\tSerial: %s\n"), + hex_serial); + else { + for (i = 0; i < serial_len; i++) { + serial_value <<= 8; + serial_value |= (serial[i] & 0xff); + } + (void) fprintf(stdout, gettext("\tSerial: %s (%d)\n"), + hex_serial, serial_value); + } + free(hex_serial); + } + + /* ... and the value */ + if (value_len == (CK_ULONG)-1 || value_len == 0) + (void) fprintf(stdout, gettext("\tValue: --\n")); + else { + hex_value_len = 3 * value_len + 1; + if ((hex_value = malloc(hex_value_len)) == NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + rv = CKR_HOST_MEMORY; + goto free_display_cert; + } + octetify(value, value_len, hex_value, hex_value_len, + B_FALSE, B_FALSE, 60, "\n\t\t", ""); + (void) fprintf(stdout, gettext("\tValue: %s\n"), hex_value); + free(hex_value); + } + +free_display_cert: + for (i = 3; i < n_attrs; i++) + if (attrs[i].ulValueLen != (CK_ULONG)-1 && + attrs[i].ulValueLen != 0) + free(attrs[i].pValue); + return (rv); +} + +/* + * List token object. + */ +int +pk_list(int argc, char *argv[]) +{ + int opt; + extern int optind; + extern char *optarg; + char *token_name = NULL; + char *manuf_id = NULL; + char *serial_no = NULL; + char full_name[FULL_NAME_LEN]; + boolean_t public_objs = B_FALSE; + boolean_t private_objs = B_FALSE; + CK_BYTE *list_label = NULL; + int obj_type = 0x00; + CK_SLOT_ID slot_id; + CK_FLAGS pin_state; + CK_UTF8CHAR_PTR pin = NULL; + CK_ULONG pinlen = 0; + CK_SESSION_HANDLE sess; + CK_OBJECT_HANDLE *objs; + CK_ULONG num_objs; + CK_RV rv = CKR_OK; + int i; + static CK_OBJECT_CLASS objclass; + CK_ATTRIBUTE class_attr = + { CKA_CLASS, &objclass, sizeof (objclass) }; + + cryptodebug("inside pk_list"); + + /* Parse command line options. Do NOT i18n/l10n. */ + while ((opt = getopt(argc, argv, "p(private)P(public)l:(label)")) != + EOF) { + switch (opt) { + case 'p': /* private objects */ + private_objs = B_TRUE; + obj_type |= PK_PRIVATE_OBJ; + break; + case 'P': /* public objects */ + public_objs = B_TRUE; + obj_type |= PK_PUBLIC_OBJ; + break; + case 'l': /* object with specific label */ + if (list_label) + return (PK_ERR_USAGE); + list_label = (CK_BYTE *)optarg; + break; + default: + return (PK_ERR_USAGE); + break; + } + } + + /* If nothing specified, default is public objects. */ + if (!public_objs && !private_objs) { + public_objs = B_TRUE; + obj_type |= PK_PUBLIC_OBJ; + } + + /* No additional args allowed. */ + argc -= optind; + argv += optind; + if (argc) + return (PK_ERR_USAGE); + /* Done parsing command line options. */ + + /* List operation only supported on softtoken. */ + if (token_name == NULL) + token_name = SOFT_TOKEN_LABEL; + if (manuf_id == NULL) + manuf_id = SOFT_MANUFACTURER_ID; + if (serial_no == NULL) + serial_no = SOFT_TOKEN_SERIAL; + full_token_name(token_name, manuf_id, serial_no, full_name); + + /* Find the slot with token. */ + if ((rv = find_token_slot(token_name, manuf_id, serial_no, &slot_id, + &pin_state)) != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to find token %s (%s)."), full_name, + pkcs11_strerror(rv)); + return (PK_ERR_PK11); + } + + /* If private objects are to be listed, user must be logged in. */ + if (private_objs) { + /* Get the user's PIN. */ + if ((rv = get_pin(gettext("Enter token passphrase:"), NULL, + &pin, &pinlen)) != CKR_OK) { + cryptoerror(LOG_STDERR, + gettext("Unable to get token passphrase (%s)."), + pkcs11_strerror(rv)); + quick_finish(NULL); + return (PK_ERR_PK11); + } + + /* Logging in user R/O into the token is sufficient. */ + cryptodebug("logging in with readonly session"); + if ((rv = quick_start(slot_id, 0, pin, pinlen, &sess)) != + CKR_OK) { + cryptoerror(LOG_STDERR, + gettext("Unable to log into token (%s)."), + pkcs11_strerror(rv)); + quick_finish(sess); + return (PK_ERR_PK11); + } + /* Otherwise, just create a session. */ + } else { + cryptodebug("opening a readonly session"); + if ((rv = open_sess(slot_id, 0, &sess)) != CKR_OK) { + cryptoerror(LOG_STDERR, + gettext("Unable to open token session (%s)."), + pkcs11_strerror(rv)); + quick_finish(sess); + return (PK_ERR_PK11); + } + } + + /* Find the object(s) with the given label and/or type. */ + if ((rv = find_objs(sess, obj_type, list_label, &objs, &num_objs)) != + CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to find token objects (%s)."), pkcs11_strerror(rv)); + quick_finish(sess); + return (PK_ERR_PK11); + } + + if (num_objs == 0) { + cryptoerror(LOG_STDERR, gettext("No objects found.")); + quick_finish(sess); + return (0); + } + + /* List the objects found. */ + for (i = 0; i < num_objs; i++) { + /* Get object class first, then decide what is next. */ + cryptodebug("calling C_GetAttributeValue for object class"); + if ((rv = C_GetAttributeValue(sess, objs[i], &class_attr, 1)) + != CKR_OK) { + cryptoerror(LOG_STDERR, gettext( + "Unable to get object #%d class attribute (%s)."), + i+1, pkcs11_strerror(rv)); + continue; + } + + /* Display based on the type of object. */ + switch (objclass) { + case CKO_CERTIFICATE: + if ((rv = display_cert(sess, objs[i], i+1)) != CKR_OK) + cryptoerror(LOG_STDERR, + gettext("Unable to display certificate.")); + break; + case CKO_PUBLIC_KEY: + if ((rv = display_pubkey(sess, objs[i], i+1)) != CKR_OK) + cryptoerror(LOG_STDERR, + gettext("Unable to display public key.")); + break; + case CKO_PRIVATE_KEY: + if ((rv = display_prikey(sess, objs[i], i+1)) != CKR_OK) + cryptoerror(LOG_STDERR, + gettext("Unable to display private key.")); + break; + case CKO_SECRET_KEY: + if ((rv = display_seckey(sess, objs[i], i+1)) != CKR_OK) + cryptoerror(LOG_STDERR, + gettext("Unable to display secret key.")); + break; + case CKO_DATA: + cryptoerror(LOG_STDERR, + gettext("Data object display not implemented.")); + break; + default: + cryptoerror(LOG_STDERR, gettext( + "Unknown token object class (0x%02x)."), objclass); + break; + } + } + + /* Clean up. */ + quick_finish(sess); + return (0); +} diff --git a/usr/src/cmd/cmd-crypto/pktool/osslcommon.c b/usr/src/cmd/cmd-crypto/pktool/osslcommon.c new file mode 100644 index 0000000000..84b4fdbdce --- /dev/null +++ b/usr/src/cmd/cmd-crypto/pktool/osslcommon.c @@ -0,0 +1,224 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file implements some "missing" routines that should + * be part of the OpenSSL library but are not there yet. + */ + +#include <cryptoutil.h> +#include "osslcommon.h" +#include <openssl/pkcs12.h> +#include <openssl/engine.h> + +/* + * OpenSSL usage needs algorithms (ciphers and digests), strings, + * and engines loaded first to be useful. + */ +void +PKTOOL_setup_openssl(void) +{ + cryptodebug("inside PKTOOL_setup_openssl"); + + /* Add all ciphers and digests. */ + OpenSSL_add_all_algorithms(); + + /* Load up error message strings. */ + ERR_load_crypto_strings(); + + /* Load up PKCS#11 engine. */ + /* ENGINE_load_pk11(); */ + + /* Load up builtin crypto engines. */ + /* + * This function is actually defined in OpenSSL libcrypto + * library. However it doesn't make its lint library correctly + * which is why this lint error occurs. OpenSSL needs fixing. + * Do not put a LINTED comment here because lint will complain + * that the directive is ununsed. + */ + ENGINE_load_builtin_engines(); + + /* U/I methods are not necessary here. */ + /* setup_ui_method(); */ +} + +/* + * This should be an OpenSSL function, but they haven't added it yet. + * See <openssl>/crypto/asn1/x_x509a.c:X509_alias_get0() for the model. + */ +unsigned char * +PKTOOL_X509_keyid_get0(X509 *x, int *len) +{ + cryptodebug("inside PKTOOL_setup_openssl"); + + if (x->aux == NULL || x->aux->keyid == NULL) { + cryptodebug("certificate aux or aux->keyid is null"); + return (NULL); + } + if (len) + *len = x->aux->keyid->length; + return (x->aux->keyid->data); +} + +/* + * This should be an OpenSSL function, but couldn't find it yet. + * It gets the subject name safely without dereferencing null pointers. + * If it is ever found in OpenSSL, this should be removed and all + * calls to it need to be replaced with right OpenSSL function. + */ +unsigned char * +PKTOOL_X509_subject_name(X509 *x, int *len) +{ + X509_NAME *temp; + + cryptodebug("inside PKTOOL_X509_subject_name"); + + if ((temp = X509_get_subject_name(x)) == NULL) { + cryptodebug("certificate subject name stack is null"); + return (NULL); + } + if (temp->bytes == NULL) { + cryptodebug("certificate subject name stack bytes is null"); + return (NULL); + } + if (len) + *len = temp->bytes->length; + return ((unsigned char *)temp->bytes->data); +} + +/* + * This should be an OpenSSL function, but couldn't find it yet. + * It gets the issuer name safely without dereferencing null pointers. + * If it is ever found in OpenSSL, this should be removed and all + * calls to it need to be replaced with right OpenSSL function. + */ +unsigned char * +PKTOOL_X509_issuer_name(X509 *x, int *len) +{ + X509_NAME *temp; + + cryptodebug("inside PKTOOL_X509_issuer_name"); + + if ((temp = X509_get_issuer_name(x)) == NULL) { + cryptodebug("certificate issuer name stack is null"); + return (NULL); + } + if (temp->bytes == NULL) { + cryptodebug("certificate issuer name stack bytes is null"); + return (NULL); + } + if (len) + *len = temp->bytes->length; + return ((unsigned char *)temp->bytes->data); +} + +/* + * This should be an OpenSSL function, but couldn't find it yet. + * It gets the serial number safely without dereferencing null pointers. + * If it is ever found in OpenSSL, this should be removed and all + * calls to it need to be replaced with right OpenSSL function. + */ +unsigned char * +PKTOOL_X509_serial_number(X509 *x, int *len) +{ + ASN1_INTEGER *temp; + + cryptodebug("inside PKTOOL_X509_serial_number"); + + if ((temp = X509_get_serialNumber(x)) == NULL) { + cryptodebug("certificate serial number is null"); + return (NULL); + } + if (len) + *len = temp->length; + return (temp->data); +} + +/* + * This should be an OpenSSL function, but couldn't find it yet. + * It gets the cert value safely without dereferencing null pointers. + * If it is ever found in OpenSSL, this should be removed and all + * calls to it need to be replaced with right OpenSSL function. + */ +unsigned char * +PKTOOL_X509_cert_value(X509 *x, int *len) +{ + PKCS12_SAFEBAG *bag; + + cryptodebug("inside PKTOOL_X509_cert_value"); + + if ((bag = PKCS12_x5092certbag(x)) == NULL) { + cryptodebug("unable to convert cert to PKCS#12 bag"); + return (NULL); + } + if (bag->value.bag == NULL || bag->value.bag->value.x509cert == NULL) { + cryptodebug("PKCS#12 bag value or cert inside it is null"); + return (NULL); + } + if (len) + *len = bag->value.bag->value.x509cert->length; + return (bag->value.bag->value.x509cert->data); +} + +/* + * Convert OpenSSL's ASN1_TIME format into a character buffer that + * can then be converted into PKCS#11 format. The buffer must be + * at least 8 bytes long. The length of the result will be 8 bytes. + * Return value of 0 indicates failure, 1 indicates success. + */ +int +PKTOOL_cvt_ossltime(ASN1_TIME *t, char *buf) +{ + cryptodebug("inside PKTOOL_cvt_ossltime"); + + if (t == NULL) { + cryptodebug("time string is empty"); + buf[0] = '\0'; + return (0); + } + + if (t->length == 15) { /* generalized time: YYYYMMDDmmhhssZ */ + cryptodebug("time string is in generalized format"); + (void) snprintf(buf, 8, "%08.8s", t->data); + return (1); + } + + if (t->length == 13) { /* UTC time: YYMMDDmmhhssZ */ + cryptodebug("time string is in UTC format"); + /* Guess whether its a 197x to 199x date, or a 20xx date. */ + (void) snprintf(buf, 8, "%s%06.6s", + ('7' <= t->data[0] && t->data[0] <= '9') ? "19" : "20", + t->data); + return (1); + } + + cryptodebug("time string is in unknown format"); + buf[0] = '\0'; + return (0); +} diff --git a/usr/src/cmd/cmd-crypto/pktool/osslcommon.h b/usr/src/cmd/cmd-crypto/pktool/osslcommon.h new file mode 100644 index 0000000000..098d0e1f6a --- /dev/null +++ b/usr/src/cmd/cmd-crypto/pktool/osslcommon.h @@ -0,0 +1,50 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _PKTOOL_OSSLCOMMON_H +#define _PKTOOL_OSSLCOMMON_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <openssl/x509.h> + +extern void PKTOOL_setup_openssl(void); +extern unsigned char *PKTOOL_X509_keyid_get0(X509 *x, int *len); +extern unsigned char *PKTOOL_X509_subject_name(X509 *x, int *len); +extern unsigned char *PKTOOL_X509_issuer_name(X509 *x, int *len); +extern unsigned char *PKTOOL_X509_serial_number(X509 *x, int *len); +extern unsigned char *PKTOOL_X509_cert_value(X509 *x, int *len); +extern int PKTOOL_cvt_ossltime(ASN1_TIME *t, char *buf); + +#ifdef __cplusplus +} +#endif + +#endif /* _PKTOOL_OSSLCOMMON_H */ diff --git a/usr/src/cmd/cmd-crypto/pktool/p12common.c b/usr/src/cmd/cmd-crypto/pktool/p12common.c new file mode 100644 index 0000000000..4e164ea911 --- /dev/null +++ b/usr/src/cmd/cmd-crypto/pktool/p12common.c @@ -0,0 +1,103 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file implements some of the common PKCS#12 routines. + */ + +#include <errno.h> +#include <string.h> +#include <cryptoutil.h> +#include "p12common.h" +#include <openssl/pkcs12.h> + +/* I18N helpers. */ +#include <libintl.h> +#include <locale.h> + +/* + * Common function to create/open PKCS#12 files. + */ +static int +pkcs12_file(char *filename, boolean_t create, BIO **fbio) +{ + cryptodebug("inside pkcs12_file"); + + if (fbio == NULL) { + cryptoerror(LOG_STDERR, create ? + gettext("Error creating file \"%s\", invalid input.") : + gettext("Error opening file \"%s\", invalid input."), + filename); + return (-1); + } + + cryptodebug(create ? "creating %s for binary writes" : + "opening %s for binary reads", filename); + if ((*fbio = BIO_new_file(filename, create ? "wb" : "rb")) == NULL) { + cryptoerror(LOG_STDERR, create ? + gettext("Error creating file \"%s\" (%s).") : + gettext("Error opening file \"%s\" (%s)."), + filename, strerror(errno)); + return (-1); + } + + return (0); +} + +/* + * Create PKCS#12 export file. + */ +int +create_pkcs12(char *filename, BIO **fbio) +{ + cryptodebug("inside create_pkcs12"); + + return (pkcs12_file(filename, B_TRUE, fbio)); +} + +/* + * Opens PKCS#12 import file. + */ +int +open_pkcs12(char *filename, BIO **fbio) +{ + cryptodebug("inside open_pkcs12"); + + return (pkcs12_file(filename, B_FALSE, fbio)); +} + +/* + * Closes PKCS#12 export file. + */ +void +close_pkcs12(BIO *fbio) +{ + cryptodebug("inside close_pkcs12"); + + BIO_free_all(fbio); +} diff --git a/usr/src/cmd/cmd-crypto/pktool/p12common.h b/usr/src/cmd/cmd-crypto/pktool/p12common.h new file mode 100644 index 0000000000..03a2a6ae5e --- /dev/null +++ b/usr/src/cmd/cmd-crypto/pktool/p12common.h @@ -0,0 +1,46 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _PKTOOL_P12COMMON_H +#define _PKTOOL_P12COMMON_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <openssl/bio.h> + +extern int create_pkcs12(char *filename, BIO **fbio); +extern int open_pkcs12(char *filename, BIO **fbio); +extern void close_pkcs12(BIO *fbio); + +#ifdef __cplusplus +} +#endif + +#endif /* _PKTOOL_P12COMMON_H */ diff --git a/usr/src/cmd/cmd-crypto/pktool/pktool.c b/usr/src/cmd/cmd-crypto/pktool/pktool.c index 1d7a185e61..0ed4d18efd 100644 --- a/usr/src/cmd/cmd-crypto/pktool/pktool.c +++ b/usr/src/cmd/cmd-crypto/pktool/pktool.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -28,6 +28,9 @@ /* * This file comprises the main driver for this tool. + * Upon parsing the command verbs from user input, it + * branches to the appropriate modules to perform the + * requested task. */ #include <stdio.h> @@ -48,16 +51,33 @@ typedef struct verbcmd_s { char *verb; int (*action)(int, char *[]); - int mode; /* reserved */ - char *synopsis; /* reserved */ + int mode; + char *synopsis; } verbcmd; /* External declarations for supported verb actions. */ extern int pk_setpin(int argc, char *argv[]); +extern int pk_list(int argc, char *argv[]); +extern int pk_delete(int argc, char *argv[]); +extern int pk_import(int argc, char *argv[]); +extern int pk_export(int argc, char *argv[]); +extern int pk_tokens(int argc, char *argv[]); + +/* Forward declarations for "built-in" verb actions. */ +static int pk_help(int argc, char *argv[]); /* Command structure for verbs and their actions. Do NOT i18n/l10n. */ static verbcmd cmds[] = { - { "setpin", pk_setpin, 0, "" }, + { "tokens", pk_tokens, 0, "tokens" }, + { "setpin", pk_setpin, 0, "setpin" }, + { "list", pk_list, 0, "list [-p] [-P] [-l <label>]" + "\n\t\tor list [--public] [--private] [--label[=]<label>]" }, + { "delete", pk_delete, 0, + "delete { [-p] [-P] [-l <label>] }" + "\n\t\tor delete { [--public] [--private] [--label[=]<label>] }" }, + { "import", pk_import, 0, "import <file>" }, + { "export", pk_export, 0, "export <file>" }, + { "-?", pk_help, 0, "--help\t(help and usage)" }, }; static int num_cmds = sizeof (cmds) / sizeof (verbcmd); @@ -71,8 +91,34 @@ static void usage(void); static void usage(void) { - (void) fprintf(stderr, gettext("Usage:\n")); - (void) fprintf(stderr, gettext("\t%s setpin\n"), prog); + int i; + + cryptodebug("inside usage"); + + /* Display this block only in command-line mode. */ + (void) fprintf(stdout, gettext("Usage:\n")); + (void) fprintf(stdout, gettext("\t%s -?\t(help and usage)\n"), prog); + (void) fprintf(stdout, gettext("\t%s subcommand [options...]\n"), prog); + (void) fprintf(stdout, gettext("where subcommands may be:\n")); + + /* Display only those verbs that match the current tool mode. */ + for (i = 0; i < num_cmds; i++) { + /* Do NOT i18n/l10n. */ + (void) fprintf(stdout, "\t%s\n", cmds[i].synopsis); + } +} + +/* + * Provide help, in the form of displaying the usage. + */ +static int +pk_help(int argc, char *argv[]) +/* ARGSUSED */ +{ + cryptodebug("inside pk_help"); + + usage(); + return (0); } /* @@ -86,6 +132,7 @@ main(int argc, char *argv[], char *envp[]) int rv; int pk_argc = 0; char **pk_argv = NULL; + int save_errno = 0; /* Set up for i18n/l10n. */ (void) setlocale(LC_ALL, ""); @@ -101,122 +148,88 @@ main(int argc, char *argv[], char *envp[]) /* Set up for debug and error output. */ cryptodebug_init(prog); - /* There must be one remaining arg at this point */ if (argc == 0) { usage(); return (1); } - /* - * By default, metaslot is enabled, and pkcs11_softtoken is - * the keystore, so, pkcs11_softtoken is hidden. - * Always turns off Metaslot so that we can see pkcs11_softtoken. - */ + /* Check for help options. For CLIP-compliance. */ + if (argc == 1 && argv[0][0] == '-') { + switch (argv[0][1]) { + case '?': + return (pk_help(argc, argv)); + default: + usage(); + return (1); + } + } + + /* Always turns off Metaslot so that we can see softtoken. */ + cryptodebug("disabling Metaslot"); if (setenv("METASLOT_ENABLED", "false", 1) < 0) { - pk11_errno = errno; + save_errno = errno; cryptoerror(LOG_STDERR, - gettext("Disabling metaslot failed: %s"), - strerror(pk11_errno)); + gettext("Disabling Metaslot failed (%s)."), + strerror(save_errno)); return (1); } /* Begin parsing command line. */ + cryptodebug("begin parsing command line"); pk_argc = argc; pk_argv = argv; - /* Check for valid verb */ + /* Check for valid verb (or an abbreviation of it). */ found = -1; for (i = 0; i < num_cmds; i++) { if (strcmp(cmds[i].verb, pk_argv[0]) == 0) { if (found < 0) { + cryptodebug("found cmd %s", cmds[i].verb); found = i; break; + } else { + cryptodebug("also found cmd %s, skipping", + cmds[i].verb); } } } - /* Stop here if no valid verb found. */ if (found < 0) { - cryptoerror(LOG_STDERR, - gettext("Invalid verb: %s"), pk_argv[0]); + cryptoerror(LOG_STDERR, gettext("Invalid verb: %s"), + pk_argv[0]); return (1); } /* Get to work! */ + cryptodebug("begin executing cmd action"); rv = (*cmds[found].action)(pk_argc, pk_argv); + cryptodebug("end executing cmd action"); switch (rv) { case PK_ERR_NONE: + cryptodebug("subcommand succeeded"); break; /* Command succeeded, do nothing. */ case PK_ERR_USAGE: + cryptodebug("usage error detected"); usage(); break; case PK_ERR_QUIT: + cryptodebug("quit command received"); exit(0); /* NOTREACHED */ - case PK_ERR_PK11INIT: - cryptoerror(LOG_STDERR, "%s (%s)", - gettext("Unable to initialize PKCS#11"), - pkcs11_strerror(pk11_errno)); - cryptodebug("C_Initialize failed (%s)", - pkcs11_strerror(pk11_errno)); - break; - case PK_ERR_PK11SLOTS: - cryptoerror(LOG_STDERR, "%s (%s)", - gettext("Failed to find PKCS#11 slots"), - pkcs11_strerror(pk11_errno)); - cryptodebug("C_GetSlotList failed (%s)", - pkcs11_strerror(pk11_errno)); - break; - case PK_ERR_PK11SESSION: - cryptoerror(LOG_STDERR, "%s (%s)", - gettext("Unable to open PKCS#11 session"), - pkcs11_strerror(pk11_errno)); - cryptodebug("C_OpenSession failed (%s)", - pkcs11_strerror(pk11_errno)); - break; - case PK_ERR_PK11LOGIN: - if (pk11_errno == CKR_PIN_INCORRECT) - cryptoerror(LOG_STDERR, "%s", gettext("Incorrect PIN")); - else { - cryptoerror(LOG_STDERR, "%s (%s)", - gettext("PKCS#11 authentication failed"), - pkcs11_strerror(pk11_errno)); - cryptodebug("C_Login failed (%s)", - pkcs11_strerror(pk11_errno)); - } - break; - case PK_ERR_PK11SETPIN: - cryptoerror(LOG_STDERR, "%s (%s)", - gettext("Set PIN failed"), pkcs11_strerror(pk11_errno)); - break; - case PK_ERR_NOSLOTS: - cryptoerror(LOG_STDERR, "%s", gettext("No slots were found")); - break; - case PK_ERR_NOMEMORY: - cryptoerror(LOG_STDERR, "%s", gettext("Out of memory")); - break; - case PK_ERR_NOTFOUND: - cryptoerror(LOG_STDERR, "%s", gettext("Token name not found")); - break; - case PK_ERR_PASSPHRASE: + case PK_ERR_PK11: cryptoerror(LOG_STDERR, "%s", - gettext("Unable to get token PIN")); + gettext("Command failed due to PKCS#11 error.")); break; - case PK_ERR_NEWPIN: - cryptoerror(LOG_STDERR, "%s", gettext("Failed to get new PIN")); - break; - case PK_ERR_PINCONFIRM: + case PK_ERR_SYSTEM: cryptoerror(LOG_STDERR, "%s", - gettext("Failed to confirm new PIN")); - break; - case PK_ERR_PINMATCH: - cryptoerror(LOG_STDERR, "%s", gettext("PINs do not match")); + gettext("Command failed due to system error.")); break; - case PK_ERR_CHANGEPIN: - cryptoerror(LOG_STDERR, "%s", gettext("PIN must be changed")); + case PK_ERR_OPENSSL: + cryptoerror(LOG_STDERR, "%s", + gettext("Command failed due to OpenSSL error.")); break; default: - cryptoerror(LOG_STDERR, "%s (%d)", + cryptoerror(LOG_STDERR, "%s (%d).", gettext("Unknown error value"), rv); break; } diff --git a/usr/src/cmd/cmd-crypto/pktool/setpin.c b/usr/src/cmd/cmd-crypto/pktool/setpin.c index b9617e8048..947825eaff 100644 --- a/usr/src/cmd/cmd-crypto/pktool/setpin.c +++ b/usr/src/cmd/cmd-crypto/pktool/setpin.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -29,60 +29,20 @@ /* * This file implements the setpin operation for this tool. * The basic flow of the process is to load the PKCS#11 module, - * finds the soft token, log into it, prompt the user for the - * new PIN, change the token's PIN, and log out. + * finds the soft token, prompt the user for the old PIN (if + * any) and the new PIN, change the token's PIN, and clean up. */ #include <stdio.h> #include <stdlib.h> +#include <errno.h> #include <string.h> #include <cryptoutil.h> #include <security/cryptoki.h> #include "common.h" -static int -set_token_pin(CK_SESSION_HANDLE hdl, CK_UTF8CHAR_PTR oldpin, CK_ULONG oldpinlen) -{ - CK_UTF8CHAR_PTR pin1, pin2; - int len1, len2; - int rv; - - cryptodebug("inside set_token_pin"); - - if ((len1 = get_password(gettext("Enter new PIN:"), - (char **)&pin1)) < 0) - return (PK_ERR_NEWPIN); - - if ((len2 = get_password(gettext("Re-enter new PIN:"), - (char **)&pin2)) < 0) { - free(pin1); - return (PK_ERR_PINCONFIRM); - } - - /* NOTE: Do not use strcmp on pin1 and pin2; they are UTF strings */ - if (len1 != len2 || memcmp(pin1, pin2, len1) != 0) { - free(pin1); - free(pin2); - return (PK_ERR_PINMATCH); - } - - if ((rv = C_SetPIN(hdl, oldpin, oldpinlen, pin1, (CK_ULONG)len1)) - != CKR_OK) { - pk11_errno = rv; - free(pin1); - free(pin2); - return (PK_ERR_PK11SETPIN); - } - - free(pin1); - free(pin2); - return (PK_ERR_NONE); -} - /* - * This is the main entry point in this module. It controls the process - * by which the token's PIN is changed. It relies on set_token_pin() to - * handle the extra work of prompting and confirming the new PIN. + * Changes the token's PIN. */ int pk_setpin(int argc, char *argv[]) @@ -93,69 +53,119 @@ pk_setpin(int argc, char *argv[]) char *serial_no = NULL; CK_SLOT_ID slot_id; CK_FLAGS pin_state; - CK_SESSION_HANDLE hdl; - CK_UTF8CHAR_PTR pin; - int pinlen; - int rv; + CK_SESSION_HANDLE sess; + CK_UTF8CHAR_PTR old_pin = NULL, new_pin = NULL; + CK_ULONG old_pinlen = 0, new_pinlen = 0; + CK_RV rv = CKR_OK; + char full_name[FULL_NAME_LEN]; cryptodebug("inside pk_setpin"); - /* - * Token_name, manuf_id, and serial_no are all optional. - * If unspecified, token_name must have a default value - * at least. - */ - token_name = SOFT_TOKEN_LABEL; - manuf_id = SOFT_MANUFACTURER_ID; + /* Get rid of subcommand word "setpin". */ + argc--; + argv++; /* No additional args allowed. */ - if (argc != 1) + if (argc != 0) return (PK_ERR_USAGE); /* Done parsing command line options. */ - /* Initialize PKCS11, find the slot with token. */ - if ((rv = init_pk11()) != PK_ERR_NONE) - return (rv); - if ((rv = find_token_slot(token_name, manuf_id, serial_no, - &slot_id, &pin_state)) != PK_ERR_NONE) - return (rv); - - /* Check if the token flags show the PIN has not be set yet. */ - if (pin_state == CKF_USER_PIN_TO_BE_CHANGED) { - cryptodebug("pin_state: first time pin is being set"); - if ((pin = (CK_UTF8CHAR_PTR)strdup(SOFT_DEFAULT_PIN)) == NULL) - return (PK_ERR_NOMEMORY); - pinlen = strlen(SOFT_DEFAULT_PIN); - } else { - cryptodebug("pin_state: changing an existing pin "); - /* Have user unlock token with correct password */ - if ((pinlen = get_password(gettext("Enter token PIN:"), - (char **)&pin)) < 0) - return (PK_ERR_PASSPHRASE); + /* + * Token_name, manuf_id, and serial_no are all optional. + * If unspecified, token_name must have a default value + * at least, so set it to the default softtoken value. + */ + if (token_name == NULL) + token_name = SOFT_TOKEN_LABEL; + if (manuf_id == NULL) + manuf_id = SOFT_MANUFACTURER_ID; + if (serial_no == NULL) + serial_no = SOFT_TOKEN_SERIAL; + full_token_name(token_name, manuf_id, serial_no, full_name); + + /* Find the slot with token. */ + if ((rv = find_token_slot(token_name, manuf_id, serial_no, &slot_id, + &pin_state)) != CKR_OK) { + cryptoerror(LOG_STDERR, + gettext("Unable to find token %s (%s)."), full_name, + pkcs11_strerror(rv)); + final_pk11(NULL); + return (PK_ERR_PK11); } /* - * Log into the token. If login fails with an uninitialized PIN, - * it means this is the first time the token has been used. - * Or if the login is successful, but all subsequent calls to - * any function return with an expired PIN, then this is the - * first time the token is used. In either case, use the - * passphrase "changeme" as the initial PIN. + * If the token is the softtoken, check if the token flags show the + * PIN has not been set yet. If not then set the old PIN to the + * default "changeme". Otherwise, let user type in the correct old + * PIN to unlock token. */ - if ((rv = login_token(slot_id, pin, (CK_ULONG)pinlen, &hdl)) - != PK_ERR_NONE) { - free(pin); - return (rv); + if (pin_state == CKF_USER_PIN_TO_BE_CHANGED && + strcmp(token_name, SOFT_TOKEN_LABEL) == 0) { + cryptodebug("pin_state: first time passphrase is being set"); + if ((old_pin = (CK_UTF8CHAR_PTR) strdup(SOFT_DEFAULT_PIN)) == + NULL) { + cryptoerror(LOG_STDERR, "%s.", strerror(errno)); + final_pk11(NULL); + return (PK_ERR_PK11); + } + old_pinlen = strlen(SOFT_DEFAULT_PIN); + } else { + cryptodebug("pin_state: changing an existing pin "); + if ((rv = get_pin(gettext("Enter token passphrase:"), NULL, + &old_pin, &old_pinlen)) != CKR_OK) { + cryptoerror(LOG_STDERR, + gettext("Unable to get token passphrase (%s)."), + pkcs11_strerror(rv)); + final_pk11(NULL); + return (PK_ERR_PK11); + } + } + + /* Get the user's new PIN. */ + if ((rv = get_pin(gettext("Create new passphrase:"), gettext( + "Re-enter new passphrase:"), &new_pin, &new_pinlen)) != CKR_OK) { + if (rv == CKR_PIN_INCORRECT) + cryptoerror(LOG_STDERR, gettext( + "Passphrases do not match.")); + else + cryptoerror(LOG_STDERR, gettext( + "Unable to get and confirm new passphrase (%s)."), + pkcs11_strerror(rv)); + free(old_pin); + final_pk11(NULL); + return (PK_ERR_PK11); + } + + /* Open a R/W session to the token to change the PIN. */ + if ((rv = open_sess(slot_id, CKF_RW_SESSION, &sess)) != CKR_OK) { + cryptoerror(LOG_STDERR, + gettext("Unable to open token session (%s)."), + pkcs11_strerror(rv)); + free(old_pin); + final_pk11(NULL); + return (PK_ERR_PK11); } - /* Set the pin for the PKCS11 token. */ - if ((rv = set_token_pin(hdl, pin, (CK_ULONG)pinlen)) != PK_ERR_NONE) { - free(pin); - logout_token(hdl); - return (rv); + /* Change the PIN if possible. */ + cryptodebug("calling C_SetPIN"); + rv = C_SetPIN(sess, old_pin, old_pinlen, new_pin, new_pinlen); + + /* Clean up. */ + free(old_pin); + free(new_pin); + quick_finish(sess); + + if (rv != CKR_OK) { + if (rv == CKR_PIN_INCORRECT) + cryptoerror(LOG_STDERR, + gettext("Incorrect passphrase.")); + else + cryptoerror(LOG_STDERR, + gettext("Unable to change passphrase (%s)."), + pkcs11_strerror(rv)); + return (PK_ERR_PK11); } - free(pin); - logout_token(hdl); - return (PK_ERR_NONE); + (void) fprintf(stdout, gettext("Passphrase changed.\n")); + return (0); } diff --git a/usr/src/cmd/cmd-crypto/pktool/tokens.c b/usr/src/cmd/cmd-crypto/pktool/tokens.c new file mode 100644 index 0000000000..e6345c77b8 --- /dev/null +++ b/usr/src/cmd/cmd-crypto/pktool/tokens.c @@ -0,0 +1,104 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file implements the token list operation for this tool. + * It loads the PKCS#11 modules, gets the list of slots with + * tokens in them, displays the list, and cleans up. + */ + +#include <stdio.h> +#include <string.h> +#include <cryptoutil.h> +#include <security/cryptoki.h> +#include "common.h" + +/* + * Lists all slots with tokens in them. + */ +int +pk_tokens(int argc, char *argv[]) +{ + CK_SLOT_ID_PTR slots = NULL; + CK_ULONG slot_count = 0; + CK_TOKEN_INFO token_info; + const char *fmt = NULL; + CK_RV rv = CKR_OK; + int i; + + cryptodebug("inside pk_tokens"); + + /* Get rid of subcommand word "tokens". */ + argc--; + argv++; + + /* No additional args allowed. */ + if (argc != 0) + return (PK_ERR_USAGE); + /* Done parsing command line options. */ + + /* Get the list of slots with tokens in them. */ + if ((rv = get_token_slots(&slots, &slot_count)) != CKR_OK) { + cryptoerror(LOG_STDERR, + gettext("Unable to get token slot list (%s)."), + pkcs11_strerror(rv)); + return (PK_ERR_PK11); + } + + /* Make sure we have something to display. */ + if (slot_count == 0) { + cryptoerror(LOG_STDERR, gettext("No slots with tokens found.")); + return (0); + } + + /* Display the list. */ + fmt = "%-30.30s %-15.15s %-15.15s %-10.10s\n"; /* No I18N/L10N. */ + (void) fprintf(stdout, fmt, gettext("Token Label"), gettext("Manuf ID"), + gettext("Serial No"), gettext("PIN State")); + for (i = 0; i < slot_count; i++) { + cryptodebug("calling C_GetTokenInfo"); + if ((rv = C_GetTokenInfo(slots[i], &token_info)) != CKR_OK) { + cryptoerror(LOG_STDERR, + gettext("Unable to get slot %d token info (%s)."), + i, pkcs11_strerror(rv)); + cryptodebug("token info error, slot %d (%s)", i, + pkcs11_strerror(rv)); + continue; + } + + (void) fprintf(stdout, fmt, token_info.label, + token_info.manufacturerID, token_info.serialNumber, + (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED) ? + gettext("default") : gettext("user set")); + } + + /* Clean up. */ + free(slots); + quick_finish(NULL); + return (0); +} |
