diff --git a/SOURCES/opencryptoki-3.0-conditional-manpages.patch b/SOURCES/opencryptoki-3.0-conditional-manpages.patch new file mode 100644 index 0000000..a62f414 --- /dev/null +++ b/SOURCES/opencryptoki-3.0-conditional-manpages.patch @@ -0,0 +1,14 @@ +diff --git a/man/man1/Makefile.am b/man/man1/Makefile.am +index 41a1259..824439e 100644 +--- a/man/man1/Makefile.am ++++ b/man/man1/Makefile.am +@@ -1,3 +1,8 @@ +-man1_MANS=pkcsconf.1 pkcsicsf.1 pkcscca.1 ++man1_MANS=pkcsconf.1 pkcsicsf.1 ++ ++if ENABLE_CCATOK ++man1_MANS += pkcscca.1 ++endif ++ + EXTRA_DIST = $(man1_MANS) + CLEANFILES = $(man1_MANS) diff --git a/SOURCES/opencryptoki-3.0-pkcscca-tool.patch b/SOURCES/opencryptoki-3.0-pkcscca-tool.patch new file mode 100644 index 0000000..b65a7f8 --- /dev/null +++ b/SOURCES/opencryptoki-3.0-pkcscca-tool.patch @@ -0,0 +1,1039 @@ +diff --git a/.gitignore b/.gitignore +index 2f0768d..4e11a3c 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -18,6 +18,7 @@ libtool + ltmain.sh + man/man1/pkcsconf.1 + man/man1/pkcsicsf.1 ++man/man1/pkcscca.1 + man/man5/opencryptoki.conf.5 + man/man7/opencryptoki.7 + man/man8/pkcsslotd.8 +diff --git a/configure.in b/configure.in +index 8a47472..083c597 100644 +--- a/configure.in ++++ b/configure.in +@@ -739,6 +739,7 @@ AC_CONFIG_FILES([Makefile usr/Makefile \ + usr/sbin/pkcsslotd/Makefile \ + usr/sbin/pkcsconf/Makefile \ + usr/sbin/pkcsicsf/Makefile \ ++ usr/sbin/pkcscca/Makefile \ + usr/sbin/pkcscca_migrate/Makefile \ + usr/lib/pkcs11/methods/Makefile \ + usr/lib/pkcs11/leeds_stdll/Makefile \ +@@ -761,6 +762,7 @@ AC_CONFIG_FILES([Makefile usr/Makefile \ + man/man1/Makefile \ + man/man1/pkcsconf.1 \ + man/man1/pkcsicsf.1 \ ++ man/man1/pkcscca.1 \ + man/man5/Makefile \ + man/man5/opencryptoki.conf.5 \ + man/man7/Makefile \ +diff --git a/doc/README.cca_stdll b/doc/README.cca_stdll +index f535dfa..a0d13f1 100644 +--- a/doc/README.cca_stdll ++++ b/doc/README.cca_stdll +@@ -1,24 +1,173 @@ ++CCA TOKEN + +-README for the CCA secure-key token ++OverView ++-------- ++The CCA token is a secure key token. ++A Secure key - key value does not exist in the clear outside of the HSM ++(secure, tamper-resistent boundary of the card). It is a clear key wrapped ++with the appropriate MasterKey that has been installed into the secure hardware. ++A clear key is generated in the hardware, wrapped with the appropriate ++master key that has been installed into the hardware. The wrapped key is then ++passed back to the invoker. Upon an encryption and/or decryption request, ++the wrapped key and the data to be encrypted are passed into the hardware. ++The wrapped key is verified, and the clear key is used to encrypt and/or ++decrypt the data. All this is done in the CCA hardware. + +-Kent Yoder ++Within opencryptoki, this wrapped key value is stored in the CKA_IBM_OPAQUE ++attribute rather than the CKA_VALUE attribute. + +- The key used to encrypt private objects on disk is a secure key. ++Pre-requisites: ++The CCA token requires cca library, libcsulcca.so, which is part of the ++csulcca rpm. ++It also requires proper configuration and installation of the MK keys into ++the hardware which is outside the scope of this document. + +- The key used to encrypt that secure key is based on the hash of the +-USER and SO pins. Therefore it is a clear key and software is used to +-do the encryption/decryption of the secure key. ++Configuration ++------------- + +-MK_USER: The secure key used for internal on-disk encryption, encrypted ++To use the CCA token a slot entry must be defined in the ++opencryptoki.conf configuration file that sets the stdll attribute to ++libcsulcca.so. ++ ++The CCA token also requires that the appropriate master keys have ++been installed into the hardware. The corresponding driver must also be ++loaded, i.e. modprobe z90crypt. ++ ++CCA Token Objects ++------------------------- ++ ++Opencryptoki stores token objects on disk. Public token objects are not ++encrypted. Private token objects are encrypted. ++Versions of opencryptoki prior to version 3, used a CCA generated secure key ++(des3 key) and the crypto adapter to encrypt the private token object's data. ++In version 3, a clear key (des3 key) and software crypto (openssl) are used ++to encrypt this data. ++ ++Migration Information ++--------------------- ++ ++Migrating version 2 private token objects to version 3 is ONLY required if ++the system will run opencryptoki version 3 and will use private token ++objects saved or preserved from version 2. ++Note, public token objects do not need to be migrated. ++If there are no private token objects from version 2, then the version 3 ++does not require any migrating. ++ ++In version 2 private token objects are encrypted and decrypted with a secure ++key in the crypto adapter. In version 3, this encryption and decryption is ++done with a clear key using software crypto. Therefore, opencryptoki ++version 3, will not succesfully decrypt a version 2 private token object. ++ ++Version 2 private token objects must be "migrated" to version 3 so that ++opencryptoki version 3 can access these objects. This migration will ++decrypt the objects using the CCA call, CSNBDEC and the current ++opencryptoki key stored in MK_USER. The objects will then be re-encrypted ++using software crypto. The key bits that are stored in MK_USER will then be ++used as a clear key. ++ ++Once the migration has completed, these private token objects should then be ++accessable to version 3. ++ ++Migration Steps ++--------------- ++ ++1. Either update or install version 3. ++a. Update to opencryptoki version 3. In most linux distributions, an update ++from version 2 to version 3 will preserve the contents of the CCA data-store. ++ ++b. Install opencryptoki version 3. In most distributions, an install will ++remove the contents of the CCA data-store. You will essentially be starting ++from the beginning and have to initialize the CCA token. ++ ++In this scenario, if a prior version of opencryptoki had been running on the ++system, and you wanted to preserve your token objects, you will have saved ++or backed them up somewhere. ++ ++2. Backup the CCA data-store before migrating. It is always a good idea to ++back up the data in case the migration is unsuccessful or data is corrupted. ++The data-store is the directory in which the CCA token information is stored ++on disk. In most distributions it can be found in /var/lib/opencryptoki/ccatok. ++Within this directory there is, ++ ++MK_USER: The des3 key used for internal on-disk encryption, encrypted + under the USER's PIN by software routines + +-MK_SO: The secure key used for internal on-disk encryption, encrypted ++MK_SO: The des3 key used for internal on-disk encryption, encrypted + under the SO's PIN by software routines + +-So, MK_USER and MK_SO contain the same key, encrypted under different PINs ++NKTOK.DAT: Token information. ++ ++TOK_OBJ: The directory in which token objects are stored. ++ ++TOK_OBJ/OBJ.IDX: A list of current token objects. ++ ++**NOTE: MK_USER and MK_SO contain the same key, encrypted under ++different PINs ++ ++3. Ensure no opencryptoki processes are running. Stop the pkcsslotd daemon ++if it is running. ++ ++4. Run the pkcscca tool to perform the migration. ++For example, ++ pkcscca -m v2objectsv3 -v ++ ++Note that the "-v" option will allow you to see which objects did and did not ++get migrated. Specify the "-d" flag if you wish to migrate CCA token objects ++stored in a data-store different from the default, /var/lib/opencryptoki/ccatok. ++ ++5. (Optional) Removing shared memory may be required to pick up ++the newly migrated objects. ++ ++CCA token's shared memory segment tracks its token objects. ++Token objects stored on disk are only loaded into shared memory ++when the shared memory is created. The shared memory is usually ++created after a reboot, an install, or an update of the opencryptoki package. ++ ++If another opencryptoki process accessed the CCA token after install ++or update, then opencryptoki will have loaded all the token objects into ++shared memory, except for the private token objects requiring migration, ++since they will have failed decryption. Subsequent calls to the ++opencryptoki api will not find these objects since they have not ++been loaded into shared memory. Opencryptoki won't read the ++objects from disk and load into shared memory again until the next time ++shared memory is created. ++ ++So, in this case, shared memory must be removed and created again so ++that opencryptoki can successfuly load all the token objects including the ++newly migrated private token objects into CCA token's shared memory segment. ++ ++Remove shared memory if, ++ - after updating or installing, any opencryptoki processes or tools tried ++ to access the CCA token before migrating CCA token's private token ++ objects. For example, the pkcsconf command was run. ++ ++ The pre-migrated objects will have failed decryption and not ++ been loaded into shared memory. A reboot or removing shared memory ++ will cause the token to create shared memory again and load the newly ++ migrated private token objects into it. ++ ++CCA's shared memory can be removed two ways. ++ 1. a reboot ++ ++ 2. remove the shared memory file, ++ i.e. "rm /dev/shm/var.lib.opencryptoki.ccatok" ++ ++ Notes: (1). Ensure that no opencryptoki processes are running ++ before removing the shared memory. Otherwise, you risk corrupting ++ any running opencryptoki processes. ++ (2). If you have installed opencryptoki manually (not via a distro ++ rpm) the CCA token shared memory segment may be named ++ usr.local.var.lib.opencryptoki.ccatok. ++ ++The next opencryptoki process to run will cause opencryptoki to create ++a shared memory segment for the token and load the newly migrated objects ++as well as any other token objects for the token. + +-PKCS#11 Notes: ++6. After a successful migration, the CCA private token objects should be ++encrypted and ready to be accessed by opencryptoki version 3. + +-DES/3DES PKCS#11 key objects have the CCA key identifier stored in the CKA_VALUE +-attribute. Usually the CKA_VALUE attribute would hold a plaintext key, however +-in this case, the id used to reference the secure key is stored here. ++TroubleShooting: ++1. If version 3 cannot find the newly migrated CCA private token objects, ++reboot or remove the shared memory file. This will cause token to create ++shared memory again and load the newly migrated private token objects ++into shared memory. +diff --git a/man/man1/Makefile.am b/man/man1/Makefile.am +index a5f3c17..41a1259 100644 +--- a/man/man1/Makefile.am ++++ b/man/man1/Makefile.am +@@ -1,3 +1,3 @@ +-man1_MANS=pkcsconf.1 pkcsicsf.1 ++man1_MANS=pkcsconf.1 pkcsicsf.1 pkcscca.1 + EXTRA_DIST = $(man1_MANS) + CLEANFILES = $(man1_MANS) +diff --git a/man/man1/pkcscca.1.in b/man/man1/pkcscca.1.in +new file mode 100644 +index 0000000..c6e49d6 +--- /dev/null ++++ b/man/man1/pkcscca.1.in +@@ -0,0 +1,45 @@ ++.TH PKCSCCA 1 "September 2014" "@PACKAGE_VERSION@" "openCryptoki" ++.SH NAME ++pkcscca \- configuration utility for the CCA token ++ ++.SH SYNOPSIS ++\fBpkcscca\fP ++[\fB-h\fP] ++[\fB-m v2objectsv3\fP] ++[\fIOPTIONS\fP] ++ ++.SH DESCRIPTION ++The \fBpkcscca\fP utility assists in administering the CCA token. Currently it ++migrates opencryptoki version 2 private token objects to the encryption ++method used in opencryptoki version 3. ++ ++In verion 2 of opencryptoki, CCA private token objects were encrypted in CCA ++hardware. In version 3 these objects are encrypted in software. The ++\fBv2objectsv3\fP migration option migrates these version 2 objects by ++decrypting them in CCA hardware using a secure key and then re-encrypting ++them in software using a software key. Afterwards, v2 objects can be accessed ++in version 3. ++ ++.SH "FLAGS" ++.IP "\fB-h\fP" 10 ++show usage information ++.IP "\fB-m\fP" 10 ++perform a migration. \fBv2objectsv3\fP is currently the only type of migration ++supported and must be specified along with this flag. ++ ++.SH "MIGRATION OPTIONS" ++.IP "\fB-d|--datastore\fP \fIdirectory\fp" 10 ++the directory where the CCA token information is kept. This directory will be ++used to locate the private token objects to be migrated. i.e. /var/lib/opencryptoki/ccatok ++.IP "\fB-v|--verbose\fP" 10 ++provide detailed output during migration ++ ++.SH "FILES" ++.IP "/var/lib/opencryptoki/ccatok/TOK_OBJ/OBJ.IDX" ++contains current list of public and private token objects for the CCA token. ++ ++.SH SEE ALSO ++.PD 0 ++.TP ++\fBREADME.cca_stdll\fP (in system's doc directory) ++.PD +diff --git a/usr/sbin/Makefile.am b/usr/sbin/Makefile.am +index 1e61ece..3757735 100644 +--- a/usr/sbin/Makefile.am ++++ b/usr/sbin/Makefile.am +@@ -7,4 +7,8 @@ if ENABLE_ICSFTOK + PKCSICSF_DIR = pkcsicsf + endif + +-SUBDIRS = pkcsslotd pkcsconf $(PKCSICSF_DIR) $(PKCSCCA_MIGRATE_DIR) ++if ENABLE_CCATOK ++PKCSCCA_DIR = pkcscca ++endif ++ ++SUBDIRS = pkcsslotd pkcsconf $(PKCSICSF_DIR) $(PKCSCCA_MIGRATE_DIR) $(PKCSCCA_DIR) +diff --git a/usr/sbin/pkcscca/Makefile.am b/usr/sbin/pkcscca/Makefile.am +new file mode 100644 +index 0000000..4d5f6c7 +--- /dev/null ++++ b/usr/sbin/pkcscca/Makefile.am +@@ -0,0 +1,14 @@ ++sbin_PROGRAMS=pkcscca ++ ++pkcscca_CFLAGS = -DSTDLL_NAME=\"pkcscca\" ++pkcscca_LDFLAGS = -lcrypto -ldl ++ ++# Not all versions of automake observe sbinname_CFLAGS ++AM_CFLAGS = -DSTDLL_NAME=\"pkcscca\" ++ ++pkcscca_SOURCES = ../../lib/pkcs11/common/p11util.c \ ++ ../../lib/pkcs11/common/sw_crypt.c \ ++ ../../lib/pkcs11/common/log.c \ ++ pkcscca.c ++ ++INCLUDES = -I. -I../../include/pkcs11 -I../../lib/pkcs11/common +diff --git a/usr/sbin/pkcscca/pkcscca.c b/usr/sbin/pkcscca/pkcscca.c +new file mode 100644 +index 0000000..a1e3bae +--- /dev/null ++++ b/usr/sbin/pkcscca/pkcscca.c +@@ -0,0 +1,661 @@ ++/* ++ * Licensed materials - Property of IBM ++ * ++ * pkcscca - A tool for PKCS#11 CCA token. ++ * Currently, only migrates CCA private token objects from CCA cipher ++ * to using a software cipher. ++ * ++ * ++ * Copyright (C) International Business Machines Corp. 2014 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sw_crypt.h" ++#include "pkcscca.h" ++ ++void (*CSNBDEC)(); ++int v_flag = 0; ++ ++int compute_hash(int hash_type, int buf_size, char *buf, char *digest) ++{ ++ EVP_MD_CTX md_ctx; ++ unsigned int result_size; ++ int rc; ++ ++ switch (hash_type) { ++ case HASH_SHA1: ++ rc = EVP_DigestInit(&md_ctx, EVP_sha1()); ++ break; ++ case HASH_MD5: ++ rc = EVP_DigestInit(&md_ctx, EVP_md5()); ++ break; ++ default: ++ return -1; ++ break; ++ } ++ ++ if (rc != 1) { ++ fprintf(stderr, "EVP_DigestInit() failed: rc = %d\n", rc); ++ return -1; ++ } ++ ++ rc = EVP_DigestUpdate(&md_ctx, buf, buf_size); ++ if (rc != 1) { ++ fprintf(stderr, "EVP_DigestUpdate() failed: rc = %d\n", rc); ++ return -1; ++ } ++ ++ result_size = EVP_MD_CTX_size(&md_ctx); ++ rc = EVP_DigestFinal(&md_ctx, (unsigned char *)digest, &result_size); ++ if (rc != 1) { ++ fprintf(stderr, "EVP_DigestFinal() failed: rc = %d\n", rc); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++int cca_decrypt(unsigned char *in_data, unsigned long in_data_len, ++ unsigned char *out_data, unsigned long *out_data_len, ++ unsigned char *init_v, unsigned char *key_value) ++{ ++ long return_code, reason_code, rule_array_count, length; ++ unsigned char chaining_vector[18]; ++ unsigned char rule_array[256]; ++ ++ length = in_data_len; ++ rule_array_count = 1; ++ memcpy(rule_array, "CBC ", 8); ++ ++ CSNBDEC(&return_code, &reason_code, NULL, NULL, key_value, ++ &length, in_data, init_v, &rule_array_count, ++ rule_array, chaining_vector, out_data); ++ ++ if (return_code != 0) { ++ fprintf(stderr, "CSNBDEC (DES3 DECRYPT) failed: return_code=%ld reason_code=%ld\n", return_code, reason_code); ++ return -1; ++ } ++ *out_data_len = length; ++ return 0; ++} ++ ++int reencrypt_private_token_object(unsigned char *data, unsigned long len, ++ unsigned char *new_cipher, ++ unsigned long *new_cipher_len, ++ unsigned char *masterkey) ++{ ++ unsigned char *clear = NULL; ++ unsigned char des3_key[64]; ++ unsigned char sw_des3_key[3 * DES_KEY_SIZE]; ++ unsigned long clear_len; ++ CK_RV rc; ++ int ret; ++ ++ /* cca wants 8 extra bytes for padding purposes */ ++ clear_len = len + 8; ++ clear = (unsigned char *) malloc(clear_len); ++ if (!clear) { ++ fprintf(stderr, "malloc() failed: %s.\n", strerror(errno)); ++ ret =-1; ++ goto done; ++ } ++ ++ /* decrypt using cca des3 */ ++ memcpy(des3_key, masterkey, MASTER_KEY_SIZE); ++ ret = cca_decrypt(data, len, clear, &clear_len, "10293847", des3_key); ++ if (ret) ++ goto done; ++ ++ /* now encrypt using software des3 */ ++ memcpy(sw_des3_key, masterkey, 3 * DES_KEY_SIZE); ++ rc = sw_des3_cbc_encrypt(clear, clear_len, new_cipher, new_cipher_len, ++ "10293847", sw_des3_key); ++ if (rc != CKR_OK) ++ ret = -1; ++done: ++ if (clear) ++ free(clear); ++ ++ return ret; ++} ++ ++int load_private_token_objects(unsigned char *data_store, ++ unsigned char *masterkey) ++{ ++ FILE *fp1 = NULL, *fp2 = NULL; ++ unsigned char *buf = NULL; ++ unsigned char tmp[PATH_MAX], fname[PATH_MAX], iname[PATH_MAX]; ++ CK_BBOOL priv; ++ unsigned int size; ++ int rc, scount= 0, fcount = 0; ++ size_t read_size; ++ unsigned char *new_cipher; ++ unsigned long new_cipher_len; ++ ++ snprintf(iname, sizeof(iname), "%s/TOK_OBJ/OBJ.IDX", data_store); ++ ++ fp1 = fopen((char *)iname, "r"); ++ if (!fp1) ++ return -1; // no token objects ++ ++ while (!feof(fp1)) { ++ (void)fgets((char *)tmp, 50, fp1); ++ if (!feof(fp1)) { ++ tmp[strlen((char *)tmp) - 1] = 0; ++ ++ snprintf((char *)fname, sizeof(fname), "%s/TOK_OBJ/", ++ data_store); ++ strcat((char *)fname, (char *)tmp); ++ ++ fp2 = fopen((char *)fname, "r"); ++ if (!fp2) ++ continue; ++ ++ fread(&size, sizeof(unsigned int), 1, fp2); ++ fread(&priv, sizeof(CK_BBOOL), 1, fp2); ++ if (priv == FALSE) { ++ fclose(fp2); ++ continue; ++ } ++ ++ size = size - sizeof(unsigned int) - sizeof(CK_BBOOL); ++ buf = (unsigned char *) malloc(size); ++ if (!buf) { ++ fprintf(stderr, "Cannot malloc for object %s " ++ "(ignoring it).\n", tmp); ++ goto cleanup; ++ } ++ ++ read_size = fread((char *)buf, 1, size, fp2); ++ if (read_size != size) { ++ fprintf(stderr, "Cannot read object %s " ++ "(ignoring it).\n", tmp); ++ goto cleanup; ++ } ++ ++ new_cipher_len = size; ++ new_cipher = malloc(new_cipher_len); ++ if (!new_cipher) { ++ fprintf(stderr, "Cannot malloc space for new " ++ "cipher (ignoring object %s).\n", tmp); ++ goto cleanup; ++ } ++ ++ /* After reading the private token object, ++ * decrypt it using CCA des3 and then re-encrypt it ++ * using software des3. ++ */ ++ memset(new_cipher, 0, new_cipher_len); ++ rc = reencrypt_private_token_object(buf, size, ++ new_cipher, &new_cipher_len, ++ masterkey); ++ if (rc) ++ goto cleanup; ++ ++ fclose(fp2); ++ ++ /* now save the newly re-encrypted object back to ++ * disk in its original file. ++ */ ++ fp2 = fopen((char *)fname, "w"); ++ size = sizeof(unsigned int) + sizeof(CK_BBOOL) ++ + new_cipher_len; ++ (void)fwrite(&size, sizeof(unsigned int), 1, fp2); ++ (void)fwrite(&priv, sizeof(CK_BBOOL), 1, fp2); ++ (void)fwrite(new_cipher, new_cipher_len, 1, fp2); ++ rc = 0; ++ ++cleanup: ++ if (fp2) ++ fclose(fp2); ++ if (buf) ++ free(buf); ++ if (new_cipher) ++ free(new_cipher); ++ ++ if (rc) { ++ if (v_flag) ++ printf("Failed to process %s\n", fname); ++ fcount++; ++ } else { ++ if (v_flag) ++ printf("Processed %s.\n", fname); ++ scount++; ++ } ++ } ++ } ++ fclose(fp1); ++ printf("Successfully migrated %d object(s).\n", scount); ++ ++ if (v_flag && fcount) ++ printf("Failed to migrate %d object(s).\n", fcount); ++ ++ return 0; ++} ++ ++int load_masterkey(char *mkfile, char *pin, char *masterkey) ++{ ++ unsigned char des3_key[3 * DES_KEY_SIZE]; ++ unsigned char hash_sha[SHA1_HASH_SIZE]; ++ unsigned char pin_md5_hash[MD5_HASH_SIZE]; ++ unsigned char *cipher = NULL; ++ unsigned char *clear = NULL; ++ unsigned long cipher_len, clear_len; ++ int ret; ++ CK_RV rc; ++ FILE *fp = NULL; ++ ++ clear_len = cipher_len = MASTER_KEY_SIZE + SHA1_HASH_SIZE + (DES_BLOCK_SIZE - 1) & ~(DES_BLOCK_SIZE - 1); ++ ++ fp = fopen((char *)mkfile, "r"); ++ if (!fp) { ++ fprintf(stderr, "Could not open %s: %s\n", mkfile, ++ strerror(errno)); ++ return -1; ++ } ++ ++ cipher = malloc(cipher_len); ++ clear = malloc(clear_len); ++ if (cipher == NULL || clear == NULL) { ++ ret = -1; ++ goto done; ++ } ++ ++ ret = fread(cipher, cipher_len, 1, fp); ++ if (ret != 1) { ++ fprintf(stderr, "Could not read %s: %s\n", mkfile, ++ strerror(errno)); ++ ret = -1; ++ goto done; ++ } ++ ++ /* decrypt the masterkey */ ++ ++ ret = compute_md5(pin, strlen(pin), pin_md5_hash); ++ if (ret) { ++ fprintf(stderr, "Error calculating MD5 of PIN!\n"); ++ goto done; ++ } ++ ++ memcpy(des3_key, pin_md5_hash, MD5_HASH_SIZE); ++ memcpy(des3_key + MD5_HASH_SIZE, pin_md5_hash, DES_KEY_SIZE); ++ ++ rc = sw_des3_cbc_decrypt(cipher, cipher_len, clear, &clear_len, ++ (unsigned char *)"12345678", des3_key); ++ if (rc != CKR_OK) { ++ fprintf(stderr, "Error decrypting master key file after read"); ++ ret = -1; ++ goto done; ++ } ++ ++ /* ++ * technically should strip PKCS padding here but since I already know ++ * what the length should be, I don't bother. ++ * ++ * compare the hashes to verify integrity ++ */ ++ ++ ret = compute_sha1(clear, MASTER_KEY_SIZE, hash_sha); ++ if (ret) { ++ fprintf(stderr, "Failed to compute sha for masterkey.\n"); ++ goto done; ++ } ++ ++ if (memcmp(hash_sha, clear + MASTER_KEY_SIZE, SHA1_HASH_SIZE) != 0) { ++ fprintf(stderr, "%s appears to have been tampered!\n", mkfile); ++ fprintf(stderr, "Cannot migrate.\n"); ++ ret = -1; ++ goto done; ++ } ++ ++ memcpy(masterkey, clear, MASTER_KEY_SIZE); ++ ret = 0; ++ ++done: ++ if (fp) ++ fclose(fp); ++ if (clear) ++ free(clear); ++ if (cipher) ++ free(cipher); ++ ++ return ret; ++} ++ ++int get_pin(char **pin, size_t *pinlen) ++{ ++ struct termios old, new; ++ int nread; ++ char *buff = NULL; ++ size_t buflen; ++ int rc = 0; ++ ++ /* turn echoing off */ ++ if (tcgetattr(fileno(stdin), &old) != 0) ++ return -1; ++ ++ new = old; ++ new.c_lflag &= ~ECHO; ++ if (tcsetattr (fileno(stdin), TCSAFLUSH, &new) != 0) ++ return -1; ++ ++ /* read the pin ++ * Note: getline will allocate memory for buff. free it when done. ++ */ ++ nread = getline(&buff, &buflen, stdin); ++ if (nread == -1) { ++ rc = -1; ++ goto done; ++ } ++ ++ /* Restore terminal */ ++ (void) tcsetattr(fileno(stdin), TCSAFLUSH, &old); ++ ++ /* start a newline */ ++ printf("\n"); ++ fflush(stdout); ++ ++ /* Allocate PIN. ++ * Note: nread includes carriage return. ++ * Replace with terminating NULL. ++ */ ++ *pin = (unsigned char *)malloc(nread); ++ if (*pin == NULL) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ /* strip the carriage return since not part of pin. */ ++ buff[nread - 1] = '\0'; ++ memcpy(*pin, buff, nread); ++ /* don't include the terminating null in the pinlen */ ++ *pinlen = nread - 1; ++ ++done: ++ if (buff) ++ free(buff); ++ ++ return rc; ++} ++ ++int verify_pins(char *data_store, char *sopin, unsigned long sopinlen, ++ char *userpin, unsigned long userpinlen) ++{ ++ TOKEN_DATA td; ++ unsigned char fname[PATH_MAX]; ++ unsigned char pin_sha[SHA1_HASH_SIZE]; ++ FILE *fp = NULL; ++ int ret; ++ ++ /* read the NVTOK.DAT */ ++ snprintf(fname, PATH_MAX, "%s/NVTOK.DAT", data_store); ++ fp = fopen((char *)fname, "r"); ++ if (!fp) { ++ fprintf(stderr, "Could not open %s: %s\n", fname, ++ strerror(errno)); ++ return -1; ++ } ++ ++ ret = fread(&td, sizeof(TOKEN_DATA), 1, fp); ++ if (ret != 1) { ++ fprintf(stderr, "Could not read %s: %s\n", fname, ++ strerror(errno)); ++ ret = -1; ++ goto done; ++ } ++ ++ /* Now compute the SHAs for the SO and USER pins entered. ++ * Compare with the SHAs for SO and USER PINs saved in ++ * NVTOK.DAT to verify. ++ */ ++ ++ if (sopin != NULL) { ++ ret = compute_sha1(sopin, sopinlen, pin_sha); ++ if (ret) { ++ fprintf(stderr, "Failed to compute sha for SO.\n"); ++ goto done; ++ } ++ ++ if (memcmp(td.so_pin_sha, pin_sha, SHA1_HASH_SIZE) != 0) { ++ fprintf(stderr, "SO PIN is incorrect.\n"); ++ ret = -1; ++ goto done; ++ } ++ } ++ ++ if (userpin != NULL) { ++ ret = compute_sha1(userpin, userpinlen, pin_sha); ++ if (ret) { ++ fprintf(stderr, "Failed to compute sha for USER.\n"); ++ goto done; ++ } ++ ++ if (memcmp(td.user_pin_sha, pin_sha, SHA1_HASH_SIZE) != 0) { ++ fprintf(stderr, "USER PIN is incorrect.\n"); ++ ret = -1; ++ goto done; ++ } ++ } ++ ret = 0; ++ ++done: ++ /* clear out the hash */ ++ memset(pin_sha, 0, SHA1_HASH_SIZE); ++ if (fp) ++ fclose(fp); ++ ++ return ret; ++} ++ ++void usage(char *progname) ++{ ++ printf("usage:\t%s -h | -m v2objectsv3 [OPTIONS] \n", progname); ++ printf(" -h\t\t\t\tshow this help\n"); ++ printf(" -m=migration_type\t\tCurrently the only type of CCA "); ++ printf("migration\n\t\t\t\tsupported is v2objectsv3. v2objectsv3 "); ++ printf("migrates\n\t\t\t\tCCA private token objects from CCA "); ++ printf("encryption\n\t\t\t\t(used in v2)to software encryption "); ++ printf("(used in v3). \n\n"); ++ printf("Migrate options (with -m v2objectsv3):\n"); ++ printf(" -d, --datastore=DIRECTORY\tCCA token datastore location\n"); ++ printf(" -v, --verbose\t\t\tprovide more detailed output\n"); ++ ++ return; ++} ++ ++int main(int argc, char **argv) ++{ ++ int ret, opt; ++ unsigned int m_flag = 0; ++ char *sopin = NULL, *userpin = NULL; ++ size_t sopinlen, userpinlen; ++ unsigned char masterkey[MASTER_KEY_SIZE]; ++ unsigned char *data_store = NULL; ++ unsigned char *m_type = NULL; ++ int data_store_len; ++ char fname[PATH_MAX]; ++ struct stat statbuf; ++ void *lib_csulcca; ++ ++ struct option long_opts[] = { ++ { "datastore", required_argument, NULL, 'd' }, ++ { "verbose", no_argument, NULL, 'v'}, ++ { 0, 0, 0, 0 } ++ }; ++ ++ int long_index; ++ while ((opt = getopt_long(argc, argv, "d:m:hv", long_opts, NULL)) != -1) { ++ switch (opt) { ++ case 'd': ++ data_store = strdup(optarg); ++ break; ++ ++ case 'h': ++ usage(argv[0]); ++ return 0; ++ ++ case 'm': ++ m_type = strdup(optarg); ++ break; ++ ++ case 'v': ++ v_flag++; ++ break; ++ ++ default: ++ usage(argv[0]); ++ return -1; ++ } ++ } ++ ++ if (m_type) { ++ if (memcmp(m_type, "v2objectsv3", strlen("v2objectsv3"))) { ++ fprintf(stderr, "unknown migration type\n"); ++ usage(argv[0]); ++ return -1; ++ } ++ } ++ ++ /* use default data_store if one is not given */ ++ if (data_store == NULL) { ++ data_store_len = strlen(TOK_DATASTORE); ++ data_store = malloc(data_store_len + 1); ++ if (data_store == NULL) { ++ fprintf(stderr, "malloc failed: %s\n",strerror(errno)); ++ return -1; ++ } ++ memset(data_store, 0, data_store_len + 1); ++ memcpy(data_store, TOK_DATASTORE, data_store_len); ++ } ++ ++ /* Verify that the data store is valid by looking for ++ * MK_SO, MK_USER, and TOK_OBJ/OBJ.IDX. ++ */ ++ ++ memset(fname, 0, PATH_MAX); ++ snprintf(fname, PATH_MAX, "%s/MK_SO", data_store); ++ if (stat(fname, &statbuf) != 0) { ++ fprintf(stderr, "Cannot find %s.\n", fname); ++ ret = -1; ++ goto done; ++ } ++ ++ memset(fname, 0, PATH_MAX); ++ snprintf(fname, PATH_MAX, "%s/MK_USER", data_store); ++ if (stat(fname, &statbuf) != 0) { ++ fprintf(stderr, "Cannot find %s.\n", fname); ++ ret = -1; ++ goto done; ++ } ++ ++ memset(fname, 0, PATH_MAX); ++ snprintf(fname, PATH_MAX, "%s/TOK_OBJ/OBJ.IDX", data_store); ++ if (stat(fname, &statbuf) != 0) { ++ fprintf(stderr, "Cannot find %s.\n", fname); ++ ret = -1; ++ goto done; ++ } ++ ++ /* If the OBJ.IDX is empty, then no objects to migrate. */ ++ if (statbuf.st_size == 0) { ++ printf("OBJ.IDX file is empty. Thus no objects to migrate.\n"); ++ goto done; ++ } ++ ++ if (v_flag) ++ printf("%s has an MK_SO, MK_USER and TOK/OBJ.IDX\n", ++ data_store); ++ ++ /* get the SO pin to authorize migration */ ++ printf("Enter the SO PIN: "); ++ fflush(stdout); ++ ret = get_pin(&sopin, &sopinlen); ++ if (ret != 0) { ++ fprintf(stderr, "Could not get SO PIN.\n"); ++ goto done; ++ } ++ ++ /* get the USER pin to authorize migration */ ++ printf("Enter the USER PIN: "); ++ fflush(stdout); ++ ret = get_pin(&userpin, &userpinlen); ++ ++ if (ret != 0) { ++ fprintf(stderr, "Could not get USER PIN.\n"); ++ goto done; ++ } ++ ++ /* Verify the SO and USER PINs entered. */ ++ ret = verify_pins(data_store, sopin, sopinlen, userpin, userpinlen); ++ if (ret) ++ goto done; ++ ++ lib_csulcca = dlopen(CCA_LIBRARY, (RTLD_GLOBAL | RTLD_NOW)); ++ if (lib_csulcca == NULL) { ++ fprintf(stderr, "dlopen(%s) failed: %s\n", CCA_LIBRARY, ++ strerror(errno)); ++ return -1; ++ } ++ ++ CSNBDEC = dlsym(lib_csulcca, "CSNBDEC"); ++ ++ /* Get the masterkey from MK_SO. ++ * This also helps verify that correct SO pin was entered. ++ */ ++ memset(masterkey, 0, MASTER_KEY_SIZE); ++ memset(fname, 0, PATH_MAX); ++ snprintf(fname, PATH_MAX, "%s/MK_SO", data_store); ++ ret = load_masterkey(fname, sopin, masterkey); ++ if (ret) { ++ fprintf(stderr, "Could not load masterkey from MK_SO.\n"); ++ goto done; ++ } ++ ++ if (v_flag) ++ printf("Successfully verified SO Pin.\n"); ++ ++ /* Get the masterkey from MK_USER. ++ * This also helps verift that correct USER pin was entered. ++ */ ++ memset(masterkey, 0, MASTER_KEY_SIZE); ++ memset(fname, 0, PATH_MAX); ++ snprintf(fname, PATH_MAX, "%s/MK_USER", data_store); ++ ret = load_masterkey(fname, userpin, masterkey); ++ if (ret) { ++ fprintf(stderr, "Could not load masterkey from MK_USER.\n"); ++ goto done; ++ } ++ ++ if (v_flag) ++ printf("Successfully verified USER Pin.\n"); ++ ++ /* Load all the private token objects and re-encrypt them ++ * using software des3, instead of CSNBENC. ++ */ ++ (void)load_private_token_objects(data_store, masterkey); ++ ++done: ++ ++ if (sopin) ++ free(sopin); ++ if (userpin) ++ free(userpin); ++ if (data_store) ++ free(data_store); ++ ++ return ret; ++} +diff --git a/usr/sbin/pkcscca/pkcscca.h b/usr/sbin/pkcscca/pkcscca.h +new file mode 100644 +index 0000000..4b23c7a +--- /dev/null ++++ b/usr/sbin/pkcscca/pkcscca.h +@@ -0,0 +1,49 @@ ++/* ++ * Licensed materials - Property of IBM ++ * ++ * pkcscca - A tool for PKCS#11 CCA token. ++ * Currently, only migrates CCA private token objects from using a ++ * CCA cipher to using a software cipher. ++ * ++ * Copyright (C) International Business Machines Corp. 2014 ++ * ++ */ ++ ++ ++#ifndef __PKCSCCA_H_ ++#define __PKCSCCA_H_ ++ ++#define CCA_LIBRARY "libcsulcca.so" ++#define TOK_DATASTORE "/var/lib/opencryptoki/ccatok" ++#define MASTER_KEY_SIZE 64 ++#define SHA1_HASH_SIZE 20 ++#define MD5_HASH_SIZE 16 ++#define DES_BLOCK_SIZE 8 ++#define DES_KEY_SIZE 8 ++#define compute_sha1(a,b,c) compute_hash(HASH_SHA1,b,a,c) ++#define compute_md5(a,b,c) compute_hash(HASH_MD5,b,a,c) ++#define HASH_SHA1 1 ++#define HASH_MD5 2 ++ ++/* from host_defs.h */ ++#include "pkcs32.h" ++typedef struct _TWEAK_VEC ++{ ++ int allow_weak_des ; ++ int check_des_parity ; ++ int allow_key_mods ; ++ int netscape_mods ; ++} TWEAK_VEC; ++ ++typedef struct _TOKEN_DATA ++{ ++ CK_TOKEN_INFO_32 token_info; ++ ++ CK_BYTE user_pin_sha[3 * DES_BLOCK_SIZE]; ++ CK_BYTE so_pin_sha[3 * DES_BLOCK_SIZE]; ++ CK_BYTE next_token_object_name[8]; ++ TWEAK_VEC tweak_vector; ++} TOKEN_DATA; ++ ++ ++#endif diff --git a/SPECS/opencryptoki.spec b/SPECS/opencryptoki.spec index 8ddd86e..ca0d4e3 100644 --- a/SPECS/opencryptoki.spec +++ b/SPECS/opencryptoki.spec @@ -3,7 +3,7 @@ Name: opencryptoki Summary: Implementation of the PKCS#11 (Cryptoki) specification v2.11 Version: 3.0 -Release: 11%{?dist} +Release: 11%{?dist}.1 License: CPL Group: System Environment/Base URL: http://sourceforge.net/projects/opencryptoki @@ -27,6 +27,11 @@ Patch4: %{name}-3.0-bz1033284.patch Patch5: %{name}-3.0-format.patch # https://bugzilla.redhat.com/show_bug.cgi?id=1054661 Patch6: %{name}-3.0-bz1054661.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1165621 +Patch7: %{name}-3.0-pkcscca-tool.patch +# do not install pkcsep11_migrate.1 and pkcscca.1 when it's not enabled +Patch8: %{name}-3.0-conditional-manpages.patch + Requires(pre): shadow-utils coreutils sed BuildRequires: openssl-devel BuildRequires: trousers-devel @@ -179,6 +184,8 @@ cryptographic hardware such as IBM 4764 or 4765 that uses the %patch4 -p1 -b .bz1033284 %patch5 -p1 -b .format %patch6 -p1 -b .bz1054661 +%patch7 -p1 -b .pkcscca-tool +%patch8 -p1 -b .man # Upstream tarball has unnecessary executable perms set on the sources find . -name '*.[ch]' -print0 | xargs -0 chmod -x @@ -203,7 +210,7 @@ done %ifarch s390 s390x --enable-icatok --enable-ccatok %else - --disable-icatok --disable-ccatok + --disable-icatok --disable-ccatok --disable-pkcscca_migrate %endif make %{?_smp_mflags} CHGRP=/bin/true @@ -322,6 +329,8 @@ exit 0 %files ccatok %doc doc/README-IBM_CCA_users %doc doc/README.cca_stdll +%{_sbindir}/pkcscca +%{_mandir}/man1/pkcscca.1* %{_libdir}/opencryptoki/stdll/libpkcs11_cca.* %{_libdir}/opencryptoki/stdll/PKCS11_CCA.so %dir %attr(770,root,pkcs11) %{_sharedstatedir}/%{name}/ccatok/ @@ -330,6 +339,11 @@ exit 0 %changelog +* Mon Dec 01 2014 Petr Lautrbach 3.0-11.1 +- Add a pkcscca tool to help migrate cca private token objects + from v2 (encrypted with cca hardware) to v3 (encrypted in software) + (#1165621) + * Thu Feb 06 2014 Petr Lautrbach 3.0-11 - create the right lock directory for cca tokens (#1054442)