nss_util.c revision 625bb2ddf15e8f305a53afa44e87f2146fa930af
121N/A/*
121N/A SSSD
292N/A
292N/A NSS crypto wrappers
292N/A
292N/A Authors:
292N/A Sumit Bose <sbose@redhat.com>
292N/A Jakub Hrozek <jhrozek@redhat.com>
292N/A
292N/A Copyright (C) Red Hat, Inc 2010
292N/A
292N/A This program is free software; you can redistribute it and/or modify
292N/A it under the terms of the GNU General Public License as published by
292N/A the Free Software Foundation; either version 3 of the License, or
292N/A (at your option) any later version.
292N/A
292N/A This program is distributed in the hope that it will be useful,
292N/A but WITHOUT ANY WARRANTY; without even the implied warranty of
292N/A MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
292N/A GNU General Public License for more details.
292N/A
292N/A You should have received a copy of the GNU General Public License
292N/A along with this program. If not, see <http://www.gnu.org/licenses/>.
292N/A*/
292N/A
292N/A#include "config.h"
292N/A
292N/A#include <prinit.h>
292N/A#include <nss.h>
292N/A
292N/A#include "util/util.h"
292N/A#include "util/crypto/nss/nss_util.h"
292N/A#include "util/crypto/nss/nss_crypto.h"
292N/A
292N/Astatic int nspr_nss_init_done = 0;
292N/A
292N/Aint nspr_nss_init(void)
292N/A{
292N/A SECStatus sret;
292N/A
292N/A /* nothing to do */
292N/A if (nspr_nss_init_done == 1) return SECSuccess;
121N/A
121N/A PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
121N/A
121N/A sret = NSS_NoDB_Init(NULL);
194N/A if (sret != SECSuccess) {
121N/A DEBUG(SSSDBG_CRIT_FAILURE,
194N/A "Error initializing connection to NSS [%d]\n",
194N/A PR_GetError());
194N/A return EIO;
194N/A }
182N/A
121N/A nspr_nss_init_done = 1;
121N/A return EOK;
194N/A}
121N/A
121N/Aint nspr_nss_cleanup(void)
121N/A{
121N/A SECStatus sret;
194N/A
194N/A /* nothing to do */
121N/A if (nspr_nss_init_done == 0) return SECSuccess;
194N/A
194N/A sret = NSS_Shutdown();
121N/A if (sret != SECSuccess) {
121N/A DEBUG(SSSDBG_CRIT_FAILURE,
121N/A "Error shutting down connection to NSS [%d]\n",
PR_GetError());
return EIO;
}
PR_Cleanup();
nspr_nss_init_done = 0;
return EOK;
}
static int sss_nss_crypto_ctx_destructor(struct sss_nss_crypto_ctx *cctx)
{
if (cctx->ectx) PK11_DestroyContext(cctx->ectx, PR_TRUE);
if (cctx->sparam) SECITEM_FreeItem(cctx->sparam, PR_TRUE);
if (cctx->slot) PK11_FreeSlot(cctx->slot);
if (cctx->keyobj) PK11_FreeSymKey(cctx->keyobj);
return EOK;
}
static int generate_random_key(TALLOC_CTX *mem_ctx,
PK11SlotInfo *slot,
struct crypto_mech_data *mech_props,
SECItem **_key)
{
SECStatus sret;
SECItem *randkeydata;
SECItem *key = NULL;
PK11SymKey *randkey;
int ret;
randkey = PK11_KeyGen(slot, mech_props->cipher,
NULL, mech_props->keylen, NULL);
if (randkey == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failure to generate key (err %d)\n",
PR_GetError());
ret = EIO;
goto done;
}
sret = PK11_ExtractKeyValue(randkey);
if (sret != SECSuccess) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failure to extract key value (err %d)\n",
PR_GetError());
ret = EIO;
goto done;
}
randkeydata = PK11_GetKeyData(randkey);
if (randkeydata == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failure to get key data (err %d)\n",
PR_GetError());
ret = EIO;
goto done;
}
/* randkeydata is valid until randkey is. Copy with talloc to
* get a nice memory hierarchy symmetrical in encrypt
* and decrypt case */
key = talloc_zero(mem_ctx, SECItem);
if (!key) {
ret = ENOMEM;
goto done;
}
key->data = talloc_memdup(key, randkeydata->data, randkeydata->len);
if (!key->data) {
ret = ENOMEM;
goto done;
}
key->len = randkeydata->len;
*_key = key;
ret = EOK;
done:
if (ret != EOK) talloc_zfree(key);
PK11_FreeSymKey(randkey);
return ret;
}
int nss_ctx_init(TALLOC_CTX *mem_ctx,
struct crypto_mech_data *mech_props,
uint8_t *key, int keylen,
uint8_t *iv, int ivlen,
struct sss_nss_crypto_ctx **_cctx)
{
struct sss_nss_crypto_ctx *cctx;
int ret;
cctx = talloc_zero(mem_ctx, struct sss_nss_crypto_ctx);
if (!cctx) {
return ENOMEM;
}
talloc_set_destructor(cctx, sss_nss_crypto_ctx_destructor);
cctx->slot = PK11_GetBestSlot(mech_props->cipher, NULL);
if (cctx->slot == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Unable to find security device (err %d)\n",
PR_GetError());
ret = EIO;
goto done;
}
if (keylen > 0) {
cctx->key = talloc(cctx, SECItem);
if (cctx->key == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to allocate Key buffer\n");
ret = ENOMEM;
goto done;
}
if (key) {
MAKE_SECITEM(key, keylen, cctx->key);
} else {
ret = generate_random_key(cctx, cctx->slot,
mech_props, &cctx->key);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Could not generate encryption key\n");
goto done;
}
}
}
if (ivlen > 0) {
cctx->iv = talloc(cctx, SECItem);
if (cctx->iv == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate IV buffer\n");
ret = ENOMEM;
goto done;
}
if (iv) {
MAKE_SECITEM(iv, ivlen, cctx->iv);
} else {
ret = generate_random_key(cctx, cctx->slot,
mech_props, &cctx->iv);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Could not generate initialization vector\n");
goto done;
}
}
}
ret = EOK;
*_cctx = cctx;
done:
if (ret) talloc_zfree(cctx);
return ret;
}
int nss_crypto_init(struct crypto_mech_data *mech_props,
enum crypto_mech_op crypto_op,
struct sss_nss_crypto_ctx *cctx)
{
CK_ATTRIBUTE_TYPE op;
int ret;
switch (crypto_op) {
case op_encrypt:
op = CKA_ENCRYPT;
break;
case op_decrypt:
op = CKA_DECRYPT;
break;
case op_sign:
op = CKA_SIGN;
break;
default:
return EFAULT;
}
/* turn the raw key into a key object */
cctx->keyobj = PK11_ImportSymKey(cctx->slot, mech_props->cipher,
PK11_OriginUnwrap, op, cctx->key, NULL);
if (cctx->keyobj == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failure to import key into NSS (err %d)\n",
PR_GetError());
ret = EIO;
goto done;
}
if (crypto_op == op_encrypt || crypto_op == op_decrypt) {
/* turn the raw IV into a initialization vector object */
cctx->sparam = PK11_ParamFromIV(mech_props->cipher, cctx->iv);
if (cctx->sparam == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failure to set up PKCS11 param (err %d)\n",
PR_GetError());
ret = EIO;
goto done;
}
} else {
cctx->sparam = SECITEM_AllocItem(NULL, NULL, 0);
if (cctx->sparam == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failure to allocate SECItem\n");
ret = EIO;
goto done;
}
MAKE_SECITEM(NULL, 0, cctx->sparam);
}
/* Create cipher context */
cctx->ectx = PK11_CreateContextBySymKey(mech_props->cipher, op,
cctx->keyobj, cctx->sparam);
if (cctx->ectx == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create cipher context (err %d)\n",
PORT_GetError());
ret = EIO;
goto done;
}
ret = EOK;
done:
return ret;
}