summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2016-05-09 17:34:49 +0200
committerJakub Hrozek <jhrozek@redhat.com>2016-06-29 21:46:52 +0200
commit625bb2ddf15e8f305a53afa44e87f2146fa930af (patch)
treee80c6bdcb073d4c597af6c8ef8202726145286ad /src
parent1dd679584241a0f9b29072c7eed1c5c5e4a577e4 (diff)
downloadsssd-625bb2ddf15e8f305a53afa44e87f2146fa930af.tar.gz
sssd-625bb2ddf15e8f305a53afa44e87f2146fa930af.tar.xz
sssd-625bb2ddf15e8f305a53afa44e87f2146fa930af.zip
Secrets: Add encryption at rest
Generates a master key file if it doesn't exist and encrypts secrets using the master key contained in the file. Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
Diffstat (limited to 'src')
-rw-r--r--src/responder/secrets/local.c193
-rw-r--r--src/responder/secrets/secsrv_local.h2
-rw-r--r--src/util/crypto/libcrypto/crypto_nite.c268
-rw-r--r--src/util/crypto/nss/nss_crypto.h66
-rw-r--r--src/util/crypto/nss/nss_nite.c305
-rw-r--r--src/util/crypto/nss/nss_obfuscate.c214
-rw-r--r--src/util/crypto/nss/nss_util.c211
-rw-r--r--src/util/crypto/nss/nss_util.h1
-rw-r--r--src/util/crypto/sss_crypto.c66
-rw-r--r--src/util/crypto/sss_crypto.h37
10 files changed, 1131 insertions, 232 deletions
diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c
index 288775839..5c99c1877 100644
--- a/src/responder/secrets/local.c
+++ b/src/responder/secrets/local.c
@@ -20,9 +20,75 @@
*/
#include "responder/secrets/secsrv_private.h"
+#include "util/crypto/sss_crypto.h"
#include <ldb.h>
+#define MKEY_SIZE (256 / 8)
+struct local_context {
+ struct ldb_context *ldb;
+ struct sec_data master_key;
+};
+
+int local_decrypt(struct local_context *lctx, TALLOC_CTX *mem_ctx,
+ const char *secret, const char *enctype,
+ char **plain_secret)
+{
+ char *output;
+
+ if (enctype && strcmp(enctype, "masterkey") == 0) {
+ struct sec_data _secret;
+ size_t outlen;
+ int ret;
+
+ _secret.data = (char *)sss_base64_decode(mem_ctx, secret,
+ &_secret.length);
+ if (!_secret.data) return EINVAL;
+
+ ret = sss_decrypt(mem_ctx, AES256CBC_HMAC_SHA256,
+ (uint8_t *)lctx->master_key.data,
+ lctx->master_key.length,
+ (uint8_t *)_secret.data, _secret.length,
+ (uint8_t **)&output, &outlen);
+ if (ret) return ret;
+
+ if (((strnlen(output, outlen) + 1) != outlen) ||
+ output[outlen - 1] != '\0') {
+ return EIO;
+ }
+ } else {
+ output = talloc_strdup(mem_ctx, secret);
+ if (!output) return ENOMEM;
+ }
+
+ *plain_secret = output;
+ return EOK;
+}
+
+int local_encrypt(struct local_context *lctx, TALLOC_CTX *mem_ctx,
+ const char *secret, const char *enctype,
+ char **ciphertext)
+{
+ struct sec_data _secret;
+ char *output;
+ int ret;
+
+ if (!enctype || strcmp(enctype, "masterkey") != 0) return EINVAL;
+
+ ret = sss_encrypt(mem_ctx, AES256CBC_HMAC_SHA256,
+ (uint8_t *)lctx->master_key.data,
+ lctx->master_key.length,
+ (const uint8_t *)secret, strlen(secret) + 1,
+ (uint8_t **)&_secret.data, &_secret.length);
+ if (ret) return ret;
+
+ output = sss_base64_encode(mem_ctx,
+ (uint8_t *)_secret.data, _secret.length);
+ if (!output) return ENOMEM;
+
+ *ciphertext = output;
+ return EOK;
+}
int local_db_dn(TALLOC_CTX *mem_ctx,
struct ldb_context *ldb,
@@ -101,25 +167,26 @@ char *local_dn_to_path(TALLOC_CTX *mem_ctx,
}
int local_db_get_simple(TALLOC_CTX *mem_ctx,
- struct ldb_context *ldb,
+ struct local_context *lctx,
const char *req_path,
char **secret)
{
TALLOC_CTX *tmp_ctx;
- static const char *attrs[] = { "secret", NULL };
+ static const char *attrs[] = { "secret", "enctype", NULL };
const char *filter = "(type=simple)";
struct ldb_result *res;
struct ldb_dn *dn;
const char *attr_secret;
+ const char *attr_enctype;
int ret;
tmp_ctx = talloc_new(mem_ctx);
if (!tmp_ctx) return ENOMEM;
- ret = local_db_dn(tmp_ctx, ldb, req_path, &dn);
+ ret = local_db_dn(tmp_ctx, lctx->ldb, req_path, &dn);
if (ret != EOK) goto done;
- ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
+ ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
attrs, "%s", filter);
if (ret != EOK) {
ret = ENOENT;
@@ -143,7 +210,14 @@ int local_db_get_simple(TALLOC_CTX *mem_ctx,
goto done;
}
- *secret = talloc_strdup(mem_ctx, attr_secret);
+ attr_enctype = ldb_msg_find_attr_as_string(res->msgs[0], "enctype", NULL);
+
+ if (attr_enctype) {
+ ret = local_decrypt(lctx, mem_ctx, attr_secret, attr_enctype, secret);
+ if (ret) goto done;
+ } else {
+ *secret = talloc_strdup(mem_ctx, attr_secret);
+ }
ret = EOK;
done:
@@ -152,7 +226,7 @@ done:
}
int local_db_list_keys(TALLOC_CTX *mem_ctx,
- struct ldb_context *ldb,
+ struct local_context *lctx,
const char *req_path,
char ***_keys,
int *num_keys)
@@ -168,10 +242,10 @@ int local_db_list_keys(TALLOC_CTX *mem_ctx,
tmp_ctx = talloc_new(mem_ctx);
if (!tmp_ctx) return ENOMEM;
- ret = local_db_dn(tmp_ctx, ldb, req_path, &dn);
+ ret = local_db_dn(tmp_ctx, lctx->ldb, req_path, &dn);
if (ret != EOK) goto done;
- ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
+ ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
attrs, "%s", filter);
if (ret != EOK) {
ret = ENOENT;
@@ -189,7 +263,7 @@ int local_db_list_keys(TALLOC_CTX *mem_ctx,
goto done;
}
- for (int i = 0; i < res->count; i++) {
+ for (unsigned i = 0; i < res->count; i++) {
keys[i] = local_dn_to_path(keys, dn, res->msgs[i]->dn);
if (!keys[i]) {
ret = ENOMEM;
@@ -207,11 +281,13 @@ done:
}
int local_db_put_simple(TALLOC_CTX *mem_ctx,
- struct ldb_context *ldb,
+ struct local_context *lctx,
const char *req_path,
const char *secret)
{
struct ldb_message *msg;
+ const char *enctype = "masterkey";
+ char *enc_secret;
int ret;
msg = ldb_msg_new(mem_ctx);
@@ -220,16 +296,24 @@ int local_db_put_simple(TALLOC_CTX *mem_ctx,
goto done;
}
- ret = local_db_dn(msg, ldb, req_path, &msg->dn);
+ /* FIXME: verify containers (except for user's namespace) exists */
+
+ ret = local_encrypt(lctx, msg, secret, enctype, &enc_secret);
+ if (ret != EOK) goto done;
+
+ ret = local_db_dn(msg, lctx->ldb, req_path, &msg->dn);
if (ret != EOK) goto done;
ret = ldb_msg_add_string(msg, "type", "simple");
if (ret != EOK) goto done;
- ret = ldb_msg_add_string(msg, "secret", secret);
+ ret = ldb_msg_add_string(msg, "enctype", enctype);
+ if (ret != EOK) goto done;
+
+ ret = ldb_msg_add_string(msg, "secret", enc_secret);
if (ret != EOK) goto done;
- ret = ldb_add(ldb, msg);
+ ret = ldb_add(lctx->ldb, msg);
if (ret != EOK) {
if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) ret = EEXIST;
else ret = EIO;
@@ -244,16 +328,16 @@ done:
}
int local_db_delete(TALLOC_CTX *mem_ctx,
- struct ldb_context *ldb,
+ struct local_context *lctx,
const char *req_path)
{
struct ldb_dn *dn;
int ret;
- ret = local_db_dn(mem_ctx, ldb, req_path, &dn);
+ ret = local_db_dn(mem_ctx, lctx->ldb, req_path, &dn);
if (ret != EOK) goto done;
- ret = ldb_delete(ldb, dn);
+ ret = ldb_delete(lctx->ldb, dn);
if (ret != EOK) {
ret = EIO;
}
@@ -319,7 +403,7 @@ struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
{
struct tevent_req *req;
struct local_secret_state *state;
- struct ldb_context *ldb;
+ struct local_context *lctx;
struct sec_data body = { 0 };
char *req_path;
char *secret;
@@ -333,8 +417,8 @@ struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
state->ev = ev;
state->secreq = secreq;
- ldb = talloc_get_type(provider_ctx, struct ldb_context);
- if (!ldb) {
+ lctx = talloc_get_type(provider_ctx, struct local_context);
+ if (!lctx) {
ret = EIO;
goto done;
}
@@ -345,13 +429,13 @@ struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
switch (secreq->method) {
case HTTP_GET:
if (req_path[strlen(req_path) - 1] == '/') {
- ret = local_db_list_keys(state, ldb, req_path, &keys, &nkeys);
+ ret = local_db_list_keys(state, lctx, req_path, &keys, &nkeys);
if (ret) goto done;
ret = sec_array_to_json(state, keys, nkeys, &body.data);
if (ret) goto done;
} else {
- ret = local_db_get_simple(state, ldb, req_path, &secret);
+ ret = local_db_get_simple(state, lctx, req_path, &secret);
if (ret) goto done;
ret = sec_simple_secret_to_json(state, secret, &body.data);
@@ -362,15 +446,17 @@ struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
break;
case HTTP_PUT:
+ /*FIXME: check fot content-type */
+
ret = sec_json_to_simple_secret(state, secreq->body.data, &secret);
if (ret) goto done;
- ret = local_db_put_simple(state, ldb, req_path, secret);
+ ret = local_db_put_simple(state, lctx, req_path, secret);
if (ret) goto done;
break;
case HTTP_DELETE:
- ret = local_db_delete(state, ldb, req_path);
+ ret = local_db_delete(state, lctx, req_path);
if (ret) goto done;
break;
@@ -397,6 +483,29 @@ done:
return tevent_req_post(req, state->ev);
}
+int generate_master_key(const char *filename, size_t size)
+{
+ uint8_t buf[size];
+ ssize_t rsize;
+ int ret;
+ int fd;
+
+ ret = generate_csprng_buffer(buf, size);
+ if (ret) return ret;
+
+ fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0600);
+ if (fd == -1) return errno;
+
+ rsize = sss_atomic_io_s(fd, buf, size, false);
+ close(fd);
+ if (rsize != size) {
+ unlink(filename);
+ return EFAULT;
+ }
+
+ return EOK;
+}
+
/* FIXME: allocate on the responder context */
static struct provider_handle local_secrets_handle = {
.fn = local_secret_req,
@@ -406,20 +515,46 @@ static struct provider_handle local_secrets_handle = {
int local_secrets_provider_handle(TALLOC_CTX *mem_ctx,
struct provider_handle **handle)
{
- struct ldb_context *ldb;
+ struct local_context *lctx;
int ret;
if (local_secrets_handle.context == NULL) {
- ldb = ldb_init(NULL, NULL);
- if (!ldb) return EIO;
+ const char *mkey = SECRETS_DB_PATH"/.secrets.mkey";
+ ssize_t size;
+ int mfd;
+
+ lctx = talloc_zero(NULL, struct local_context);
+ if (!lctx) return ENOMEM;
+
+ lctx->ldb = ldb_init(lctx, NULL);
+ if (!lctx->ldb) return ENOMEM;
- ret = ldb_connect(ldb, SECRETS_DB_PATH"/secrets.ldb", 0, NULL);
+ ret = ldb_connect(lctx->ldb, SECRETS_DB_PATH"/secrets.ldb", 0, NULL);
if (ret != LDB_SUCCESS) {
- talloc_free(ldb);
+ talloc_free(lctx->ldb);
return EIO;
}
- local_secrets_handle.context = ldb;
+ lctx->master_key.data = talloc_size(lctx, MKEY_SIZE);
+ if (!lctx->master_key.data) return ENOMEM;
+ lctx->master_key.length = MKEY_SIZE;
+
+ ret = check_and_open_readonly(mkey, &mfd, 0, 0,
+ S_IFREG|S_IRUSR|S_IWUSR, 0);
+ if (ret == ENOENT) {
+ ret = generate_master_key(mkey, MKEY_SIZE);
+ if (ret) return EFAULT;
+ ret = check_and_open_readonly(mkey, &mfd, 0, 0,
+ S_IFREG|S_IRUSR|S_IWUSR, 0);
+ }
+ if (ret) return EFAULT;
+
+ size = sss_atomic_io_s(mfd, lctx->master_key.data,
+ lctx->master_key.length, true);
+ close(mfd);
+ if (size < 0 || size != lctx->master_key.length) return EIO;
+
+ local_secrets_handle.context = lctx;
}
*handle = &local_secrets_handle;
diff --git a/src/responder/secrets/secsrv_local.h b/src/responder/secrets/secsrv_local.h
index 537f61e53..d21b14953 100644
--- a/src/responder/secrets/secsrv_local.h
+++ b/src/responder/secrets/secsrv_local.h
@@ -1,7 +1,7 @@
/*
SSSD
- Secrets Local Provider
+ Secrets Proxy Provider
Copyright (C) Simo Sorce <ssorce@redhat.com> 2016
diff --git a/src/util/crypto/libcrypto/crypto_nite.c b/src/util/crypto/libcrypto/crypto_nite.c
new file mode 100644
index 000000000..fa267fbcc
--- /dev/null
+++ b/src/util/crypto/libcrypto/crypto_nite.c
@@ -0,0 +1,268 @@
+/*
+ SSSD
+
+ Encryption/Decryption primitives
+
+ Authors:
+ Simo Sorce <simo@redhat.com>
+
+ Copyright (C) Simo Sorce 2016
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+#include <talloc.h>
+#include <errno.h>
+
+#include "util/util.h"
+#include "util/crypto/sss_crypto.h"
+
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/crypto.h>
+
+struct cipher_mech {
+ const EVP_CIPHER * (*cipher)(void);
+ const EVP_MD * (*digest)(void);
+} mechs[] = {
+ { EVP_aes_256_cbc, EVP_sha256 }
+};
+
+int sss_encrypt(TALLOC_CTX *mem_ctx, enum encmethod enctype,
+ uint8_t *key, size_t keylen,
+ const uint8_t *plaintext, size_t plainlen,
+ uint8_t **ciphertext, size_t *cipherlen)
+{
+ const EVP_CIPHER *cipher;
+ const EVP_MD *digest;
+ EVP_PKEY *hmackey;
+ EVP_CIPHER_CTX ctx;
+ EVP_MD_CTX mdctx;
+ uint8_t *out = NULL;
+ int evpkeylen;
+ int evpivlen;
+ int hmaclen;
+ int outlen, tmplen;
+ size_t slen;
+ int ret;
+
+ if (!plaintext || !plainlen) return EINVAL;
+
+ if (enctype != AES256CBC_HMAC_SHA256) return EINVAL;
+ cipher = mechs[AES256CBC_HMAC_SHA256].cipher();
+ digest = mechs[AES256CBC_HMAC_SHA256].digest();
+
+ evpkeylen = EVP_CIPHER_key_length(cipher);
+ if (!key || keylen != evpkeylen) return EINVAL;
+
+ hmackey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, keylen);
+ if (!hmackey) return ENOMEM;
+
+ /* We have no function to return the size of the output for arbitray HMAC
+ * algorithms so we just truncate to the key size should the hmac be bigger
+ * (or pad with zeros should the HMAC be smaller) */
+ hmaclen = keylen;
+
+ evpivlen = EVP_CIPHER_iv_length(cipher);
+ outlen = plainlen + (2 * EVP_CIPHER_block_size(cipher))
+ + evpivlen + hmaclen;
+ out = talloc_zero_size(mem_ctx, outlen);
+
+ /* First Encrypt */
+
+ if (evpivlen != 0) {
+ RAND_bytes(out, evpivlen);
+ }
+
+ EVP_CIPHER_CTX_init(&ctx);
+ ret = EVP_EncryptInit_ex(&ctx, cipher, 0, key, evpivlen ? out : NULL);
+ if (ret != 1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ outlen = evpivlen;
+ tmplen = 0;
+ ret = EVP_EncryptUpdate(&ctx, out + outlen, &tmplen, plaintext, plainlen);
+ if (ret != 1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ outlen += tmplen;
+
+ ret = EVP_EncryptFinal_ex(&ctx, out + outlen, &tmplen);
+ if (ret != 1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ outlen += tmplen;
+
+ /* Then HMAC */
+
+ EVP_MD_CTX_init(&mdctx);
+
+ ret = EVP_DigestInit_ex(&mdctx, digest, NULL);
+ if (ret != 1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = EVP_DigestSignInit(&mdctx, NULL, digest, NULL, hmackey);
+ if (ret != 1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = EVP_DigestSignUpdate(&mdctx, out, outlen);
+ if (ret != 1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ slen = hmaclen;
+ ret = EVP_DigestSignFinal(&mdctx, &out[outlen], &slen);
+ if (ret != 1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ outlen += hmaclen;
+
+ *ciphertext = out;
+ *cipherlen = outlen;
+ ret = EOK;
+
+done:
+ EVP_MD_CTX_cleanup(&mdctx);
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ EVP_PKEY_free(hmackey);
+ return ret;
+}
+
+int sss_decrypt(TALLOC_CTX *mem_ctx, enum encmethod enctype,
+ uint8_t *key, size_t keylen,
+ const uint8_t *ciphertext, size_t cipherlen,
+ uint8_t **plaintext, size_t *plainlen)
+{
+ const EVP_CIPHER *cipher;
+ const EVP_MD *digest;
+ EVP_PKEY *hmackey;
+ EVP_CIPHER_CTX ctx;
+ EVP_MD_CTX mdctx;
+ const uint8_t *iv = NULL;
+ uint8_t *out;
+ int evpkeylen;
+ int evpivlen;
+ int hmaclen;
+ int outlen, tmplen;
+ size_t slen;
+ int ret;
+
+ if (!ciphertext || !cipherlen) return EINVAL;
+
+ if (enctype != AES256CBC_HMAC_SHA256) return EINVAL;
+ cipher = mechs[AES256CBC_HMAC_SHA256].cipher();
+ digest = mechs[AES256CBC_HMAC_SHA256].digest();
+
+ evpkeylen = EVP_CIPHER_key_length(cipher);
+ if (!key || keylen != evpkeylen) return EINVAL;
+
+ hmackey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, keylen);
+ if (!hmackey) return ENOMEM;
+
+ /* We have no function to return the size of the output for arbitray HMAC
+ * algorithms so we just assume it was truncated to the key size should
+ * the hmac be bigger (or pad with zeros should the HMAC be smaller) */
+ hmaclen = keylen;
+
+ evpivlen = EVP_CIPHER_iv_length(cipher);
+ out = talloc_zero_size(mem_ctx, cipherlen);
+
+ /* First check HMAC */
+
+ EVP_MD_CTX_init(&mdctx);
+
+ ret = EVP_DigestInit_ex(&mdctx, digest, NULL);
+ if (ret != 1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = EVP_DigestSignInit(&mdctx, NULL, digest, NULL, hmackey);
+ if (ret != 1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = EVP_DigestSignUpdate(&mdctx, ciphertext, cipherlen - hmaclen);
+ if (ret != 1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ slen = hmaclen;
+ ret = EVP_DigestSignFinal(&mdctx, out, &slen);
+ if (ret != 1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = CRYPTO_memcmp(&ciphertext[cipherlen - hmaclen], out, hmaclen);
+ if (ret != 1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ /* Then Decrypt */
+
+ if (evpivlen != 0) {
+ iv = ciphertext;
+ }
+
+ EVP_CIPHER_CTX_init(&ctx);
+ ret = EVP_DecryptInit_ex(&ctx, cipher, 0, key, iv);
+ if (ret != 1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = EVP_DecryptUpdate(&ctx, out, &outlen,
+ ciphertext + evpivlen,
+ cipherlen - evpivlen - hmaclen);
+ if (ret != 1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = EVP_DecryptFinal_ex(&ctx, out + outlen, &tmplen);
+ if (ret != 1) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ outlen += tmplen;
+
+ *plaintext = out;
+ *plainlen = outlen;
+ ret = EOK;
+
+done:
+ EVP_MD_CTX_cleanup(&mdctx);
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ EVP_PKEY_free(hmackey);
+ return ret;
+}
diff --git a/src/util/crypto/nss/nss_crypto.h b/src/util/crypto/nss/nss_crypto.h
new file mode 100644
index 000000000..5ecb5449d
--- /dev/null
+++ b/src/util/crypto/nss/nss_crypto.h
@@ -0,0 +1,66 @@
+/*
+ SSSD
+
+ NSS crypto wrappers
+
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) Red Hat, Inc 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <nss.h>
+#include <prerror.h>
+#include <pk11func.h>
+#include <base64.h>
+#include <talloc.h>
+
+#define MAKE_SECITEM(sdata, slen, sitem) do { \
+ (sitem)->type = (siBuffer); \
+ (sitem)->data = (sdata); \
+ (sitem)->len = (slen); \
+} while(0)
+
+struct sss_nss_crypto_ctx {
+ PK11SlotInfo *slot;
+ PK11Context *ectx;
+ PK11SymKey *keyobj;
+ SECItem *sparam;
+
+ SECItem *iv;
+ SECItem *key;
+};
+
+struct crypto_mech_data {
+ CK_MECHANISM_TYPE cipher;
+ uint16_t keylen;
+ uint16_t bsize;
+};
+
+enum crypto_mech_op {
+ op_encrypt,
+ op_decrypt,
+ op_sign
+};
+
+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);
+int nss_crypto_init(struct crypto_mech_data *mech_props,
+ enum crypto_mech_op crypto_op,
+ struct sss_nss_crypto_ctx *cctx);
diff --git a/src/util/crypto/nss/nss_nite.c b/src/util/crypto/nss/nss_nite.c
new file mode 100644
index 000000000..3641e0512
--- /dev/null
+++ b/src/util/crypto/nss/nss_nite.c
@@ -0,0 +1,305 @@
+/*
+ SSSD
+
+ Encryption/Decryption primitives
+
+ Authors:
+ Simo Sorce <simo@redhat.com>
+
+ Copyright (C) Simo Sorce 2016
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include "util/util.h"
+#include "util/crypto/sss_crypto.h"
+#include "util/crypto/nss/nss_util.h"
+#include "util/crypto/nss/nss_crypto.h"
+
+struct cipher_mech {
+ struct crypto_mech_data enc;
+ struct crypto_mech_data hmac;
+} mechs[] = {
+ { { CKM_AES_CBC_PAD, 32, 16 }, { CKM_SHA256_HMAC, 32, 16 } }
+};
+
+int sss_encrypt(TALLOC_CTX *mem_ctx, enum encmethod enctype,
+ uint8_t *key, size_t keylen,
+ const uint8_t *plaintext, size_t plainlen,
+ uint8_t **ciphertext, size_t *cipherlen)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct sss_nss_crypto_ctx *cctx;
+ struct sss_nss_crypto_ctx *hctx;
+ struct crypto_mech_data *enc;
+ struct crypto_mech_data *hmac;
+ SECStatus sret;
+ uint8_t *out = NULL;
+ int ivlen;
+ int hmaclen;
+ int outlen;
+ int clen;
+ union {
+ unsigned int u;
+ int s;
+ } tmplen;
+ unsigned int digestlen;
+ int ret;
+
+ if (!plaintext || !plainlen) return EINVAL;
+
+ if (enctype != AES256CBC_HMAC_SHA256) return EINVAL;
+ enc = &mechs[AES256CBC_HMAC_SHA256].enc;
+ hmac = &mechs[AES256CBC_HMAC_SHA256].hmac;
+ ivlen = enc->bsize;
+
+ /* We have no function to return the size of the output for arbitray HMAC
+ * algorithms so we just truncate to the key size should the hmac be bigger
+ * (or pad with zeros should the HMAC be smaller) */
+ hmaclen = keylen;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ /* initialize NSS if needed */
+ ret = nspr_nss_init();
+ if (ret != EOK) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ outlen = plainlen + (2 * enc->bsize) + ivlen + hmaclen;
+ out = talloc_zero_size(tmp_ctx, outlen);
+
+ /* First Encrypt */
+
+ if (ivlen != 0) {
+ ret = generate_csprng_buffer(out, ivlen);
+ if (ret) return ret;
+ }
+
+ ret = nss_ctx_init(tmp_ctx, enc, key, keylen, out, ivlen, &cctx);
+ if (ret != EOK) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = nss_crypto_init(enc, op_encrypt, cctx);
+ if (ret != EOK) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ clen = ivlen;
+
+ sret = PK11_CipherOp(cctx->ectx, out + clen, &tmplen.s,
+ outlen - clen, plaintext, plainlen);
+ if (sret != SECSuccess) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ clen += tmplen.s;
+
+ sret = PK11_DigestFinal(cctx->ectx, out + clen, &tmplen.u, outlen - clen);
+ if (sret != SECSuccess) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ clen += tmplen.u;
+ if (clen < 0 || clen > UINT16_MAX) {
+ ret = ERANGE;
+ goto done;
+ }
+
+ /* Then HMAC */
+
+ ret = nss_ctx_init(tmp_ctx, hmac, key, keylen, NULL, 0, &hctx);
+ if (ret != EOK) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = nss_crypto_init(hmac, op_sign, hctx);
+ if (ret != EOK) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ sret = PK11_DigestBegin(hctx->ectx);
+ if (sret != SECSuccess) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ sret = PK11_DigestOp(hctx->ectx, out, clen);
+ if (sret != SECSuccess) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ sret = PK11_DigestFinal(hctx->ectx, out + clen, &digestlen,
+ outlen - clen);
+ if (sret != SECSuccess) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ *ciphertext = talloc_move(mem_ctx, &out);
+ *cipherlen = clen + hmaclen;
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ nspr_nss_cleanup();
+ return ret;
+}
+
+int sss_decrypt(TALLOC_CTX *mem_ctx, enum encmethod enctype,
+ uint8_t *key, size_t keylen,
+ const uint8_t *ciphertext, size_t cipherlen,
+ uint8_t **plaintext, size_t *plainlen)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct sss_nss_crypto_ctx *cctx;
+ struct sss_nss_crypto_ctx *hctx;
+ struct crypto_mech_data *enc;
+ struct crypto_mech_data *hmac;
+ SECStatus sret;
+ uint8_t *out = NULL;
+ uint8_t *ivbuf = NULL;
+ int ivlen;
+ int hmaclen;
+ int outlen;
+ unsigned int tmplen;
+ unsigned int digestlen;
+ int ret;
+
+ if (!plaintext || !plainlen) return EINVAL;
+
+ if (enctype != AES256CBC_HMAC_SHA256) return EINVAL;
+ enc = &mechs[AES256CBC_HMAC_SHA256].enc;
+ hmac = &mechs[AES256CBC_HMAC_SHA256].hmac;
+ ivlen = enc->bsize;
+
+ /* We have no function to return the size of the output for arbitray HMAC
+ * algorithms so we just truncate to the key size should the hmac be bigger
+ * (or pad with zeros should the HMAC be smaller) */
+ hmaclen = keylen;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ /* initialize NSS if needed */
+ ret = nspr_nss_init();
+ if (ret != EOK) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ out = talloc_zero_size(tmp_ctx, cipherlen);
+
+ /* First check HMAC */
+
+ ret = nss_ctx_init(tmp_ctx, hmac, key, keylen, NULL, 0, &hctx);
+ if (ret != EOK) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = nss_crypto_init(hmac, op_sign, hctx);
+ if (ret != EOK) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ sret = PK11_DigestBegin(hctx->ectx);
+ if (sret != SECSuccess) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ sret = PK11_DigestOp(hctx->ectx, ciphertext, cipherlen - hmaclen);
+ if (sret != SECSuccess) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ sret = PK11_DigestFinal(hctx->ectx, out, &digestlen, hmaclen);
+ if (sret != SECSuccess) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = NSS_SecureMemcmp(&ciphertext[cipherlen - hmaclen], out, hmaclen);
+ if (ret != 0) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ /* Then Decrypt */
+
+ if (ivlen != 0) {
+ ivbuf = talloc_size(tmp_ctx, ivlen);
+ if (!ivbuf) {
+ ret = ENOMEM;
+ goto done;
+ }
+ memcpy(ivbuf, ciphertext, ivlen);
+ }
+
+ ret = nss_ctx_init(tmp_ctx, enc, key, keylen, ivbuf, ivlen, &cctx);
+ if (ret != EOK) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = nss_crypto_init(enc, op_decrypt, cctx);
+ if (ret != EOK) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ sret = PK11_CipherOp(cctx->ectx, out, &outlen, cipherlen,
+ ciphertext + ivlen, cipherlen - ivlen - hmaclen);
+ if (sret != SECSuccess) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ sret = PK11_DigestFinal(cctx->ectx, out + outlen, &tmplen,
+ cipherlen - outlen);
+ if (sret != SECSuccess) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ outlen += tmplen;
+
+ *plaintext = talloc_move(mem_ctx, &out);
+ *plainlen = outlen;
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ nspr_nss_cleanup();
+ return ret;
+}
diff --git a/src/util/crypto/nss/nss_obfuscate.c b/src/util/crypto/nss/nss_obfuscate.c
index 8c6bdc525..a55f22b6d 100644
--- a/src/util/crypto/nss/nss_obfuscate.c
+++ b/src/util/crypto/nss/nss_obfuscate.c
@@ -31,42 +31,17 @@
*/
#include "config.h"
-
#include <prerror.h>
-#include <nss.h>
#include <pk11func.h>
-#include <base64.h>
-#include <talloc.h>
#include "util/util.h"
#include "util/crypto/sss_crypto.h"
#include "util/crypto/nss/nss_util.h"
+#include "util/crypto/nss/nss_crypto.h"
#define OBF_BUFFER_SENTINEL "\0\1\2\3"
#define OBF_BUFFER_SENTINEL_SIZE 4
-#define MAKE_SECITEM(sdata, slen, sitem) do { \
- (sitem)->type = (siBuffer); \
- (sitem)->data = (sdata); \
- (sitem)->len = (slen); \
-} while(0)
-
-struct sss_nss_crypto_ctx {
- PK11SlotInfo *slot;
- PK11Context *ectx;
- PK11SymKey *keyobj;
- SECItem *sparam;
-
- SECItem *iv;
- SECItem *key;
-};
-
-struct crypto_mech_data {
- CK_MECHANISM_TYPE cipher;
- uint16_t keylen;
- uint16_t bsize;
-};
-
static struct crypto_mech_data cmdata[] = {
/* AES with automatic padding, 256b key, 128b block */
{ CKM_AES_CBC_PAD, 32, 16 },
@@ -83,147 +58,6 @@ static struct crypto_mech_data *get_crypto_mech_data(enum obfmethod meth)
return &cmdata[meth];
}
-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;
-}
-
-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 nss_ctx_init(TALLOC_CTX *mem_ctx,
- struct crypto_mech_data *mech_props,
- 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;
- }
-
- ret = EOK;
- *_cctx = cctx;
-done:
- if (ret) talloc_zfree(cctx);
- return ret;
-}
-
-static int nss_encrypt_decrypt_init(struct crypto_mech_data *mech_props,
- bool do_encrypt,
- struct sss_nss_crypto_ctx *cctx)
-{
- CK_ATTRIBUTE_TYPE op;
- int ret;
-
- op = do_encrypt ? CKA_ENCRYPT : CKA_DECRYPT;
-
- /* 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;
- }
-
- /* 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;
- }
-
- /* 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;
-}
-
int sss_password_encrypt(TALLOC_CTX *mem_ctx, const char *password, int plen,
enum obfmethod meth, char **obfpwd)
{
@@ -263,27 +97,14 @@ int sss_password_encrypt(TALLOC_CTX *mem_ctx, const char *password, int plen,
goto done;
}
- ret = nss_ctx_init(tmp_ctx, mech_props, &cctx);
+ /* Initiualize ctx and generate random encryption and IV key */
+ ret = nss_ctx_init(tmp_ctx, mech_props, NULL, 1, NULL, 1, &cctx);
if (ret) {
DEBUG(SSSDBG_CRIT_FAILURE, "Cannot initialize NSS context\n");
goto done;
}
- /* generate random encryption and IV key */
- 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;
- }
-
- 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 = nss_encrypt_decrypt_init(mech_props, true, cctx);
+ ret = nss_crypto_init(mech_props, op_encrypt, cctx);
if (ret) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Cannot initialize NSS context properties\n");
@@ -431,7 +252,8 @@ int sss_password_decrypt(TALLOC_CTX *mem_ctx, char *b64encoded,
memcpy(sentinel_check,
obfbuf + p + mech_props->keylen + mech_props->bsize + ctsize,
OBF_BUFFER_SENTINEL_SIZE);
- if (memcmp(sentinel_check, OBF_BUFFER_SENTINEL, OBF_BUFFER_SENTINEL_SIZE) != 0) {
+ if (memcmp(sentinel_check,
+ OBF_BUFFER_SENTINEL, OBF_BUFFER_SENTINEL_SIZE) != 0) {
DEBUG(SSSDBG_FATAL_FAILURE,
"Obfuscation buffer seems corrupt, aborting\n");
ret = EFAULT;
@@ -460,23 +282,15 @@ int sss_password_decrypt(TALLOC_CTX *mem_ctx, char *b64encoded,
}
safealign_memcpy(cryptotext, obfbuf+p, ctsize, &p);
- ret = nss_ctx_init(tmp_ctx, mech_props, &cctx);
+ ret = nss_ctx_init(tmp_ctx, mech_props,
+ keybuf, mech_props->keylen,
+ ivbuf, mech_props->bsize, &cctx);
if (ret) {
DEBUG(SSSDBG_CRIT_FAILURE, "Cannot initialize NSS context\n");
goto done;
}
- cctx->iv = talloc_zero(cctx, SECItem);
- cctx->key = talloc_zero(cctx, SECItem);
- if (!cctx->iv || !cctx->key) {
- ret = ENOMEM;
- goto done;
- }
-
- MAKE_SECITEM(ivbuf, mech_props->bsize, cctx->iv);
- MAKE_SECITEM(keybuf, mech_props->keylen, cctx->key);
-
- ret = nss_encrypt_decrypt_init(mech_props, false, cctx);
+ ret = nss_crypto_init(mech_props, op_decrypt, cctx);
if (ret) {
goto done;
}
@@ -487,8 +301,8 @@ int sss_password_decrypt(TALLOC_CTX *mem_ctx, char *b64encoded,
goto done;
}
- sret = PK11_CipherOp(cctx->ectx, (unsigned char *) pwdbuf, &plainlen, ctsize,
- cryptotext, ctsize);
+ sret = PK11_CipherOp(cctx->ectx, (unsigned char *) pwdbuf, &plainlen,
+ ctsize, cryptotext, ctsize);
if (sret != SECSuccess) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Cannot execute the encryption operation (err %d)\n",
@@ -497,8 +311,8 @@ int sss_password_decrypt(TALLOC_CTX *mem_ctx, char *b64encoded,
goto done;
}
- sret = PK11_DigestFinal(cctx->ectx, (unsigned char *) pwdbuf+plainlen, &digestlen,
- ctsize - plainlen);
+ sret = PK11_DigestFinal(cctx->ectx, (unsigned char *) pwdbuf+plainlen,
+ &digestlen, ctsize - plainlen);
if (sret != SECSuccess) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Cannot execute the encryption operation (err %d)\n",
diff --git a/src/util/crypto/nss/nss_util.c b/src/util/crypto/nss/nss_util.c
index 55b81c9b1..46835057b 100644
--- a/src/util/crypto/nss/nss_util.c
+++ b/src/util/crypto/nss/nss_util.c
@@ -26,12 +26,11 @@
#include "config.h"
#include <prinit.h>
-#include <prerror.h>
#include <nss.h>
-#include <pk11func.h>
#include "util/util.h"
#include "util/crypto/nss/nss_util.h"
+#include "util/crypto/nss/nss_crypto.h"
static int nspr_nss_init_done = 0;
@@ -75,3 +74,211 @@ int nspr_nss_cleanup(void)
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;
+}
diff --git a/src/util/crypto/nss/nss_util.h b/src/util/crypto/nss/nss_util.h
index 7387b9a7e..08ab58d87 100644
--- a/src/util/crypto/nss/nss_util.h
+++ b/src/util/crypto/nss/nss_util.h
@@ -22,6 +22,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <nss.h>
int nspr_nss_init(void);
int nspr_nss_cleanup(void);
diff --git a/src/util/crypto/sss_crypto.c b/src/util/crypto/sss_crypto.c
new file mode 100644
index 000000000..4c775f3d9
--- /dev/null
+++ b/src/util/crypto/sss_crypto.c
@@ -0,0 +1,66 @@
+/*
+ Authors:
+ Simo Sorce <ssorce@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+#include "util/util.h"
+#include "util/crypto/sss_crypto.h"
+
+int generate_csprng_buffer(uint8_t *buf, size_t size)
+{
+ ssize_t rsize;
+ ssize_t pos;
+ int ret;
+ int fd;
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd == -1) return errno;
+
+ rsize = 0;
+ pos = 0;
+ while (rsize < size) {
+ rsize = read(fd, buf + pos, size - pos);
+ switch (rsize) {
+ case -1:
+ if (errno == EINTR) continue;
+ ret = EIO;
+ goto done;
+ case 0:
+ ret = EIO;
+ goto done;
+ default:
+ if (rsize + pos < size - pos) {
+ pos += rsize;
+ continue;
+ }
+ ret = EIO;
+ goto done;
+ }
+ }
+ if (rsize != size) {
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ close(fd);
+ return ret;
+}
diff --git a/src/util/crypto/sss_crypto.h b/src/util/crypto/sss_crypto.h
index 5b40ecfc4..8c7a88317 100644
--- a/src/util/crypto/sss_crypto.h
+++ b/src/util/crypto/sss_crypto.h
@@ -1,3 +1,24 @@
+/*
+ Copyright (C) 2009-2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SSS_CRYPTO_H_
+#define _SSS_CRYPTO_H_
+
+int generate_csprng_buffer(uint8_t *buf, size_t size);
int s3crypt_sha512(TALLOC_CTX *mmectx,
const char *key, const char *salt, char **_hash);
@@ -32,3 +53,19 @@ int sss_password_encrypt(TALLOC_CTX *mem_ctx, const char *password, int plen,
int sss_password_decrypt(TALLOC_CTX *mem_ctx, char *b64encoded,
char **password);
+
+enum encmethod {
+ AES256CBC_HMAC_SHA256,
+ NUM_ENCMETHODS
+};
+
+int sss_encrypt(TALLOC_CTX *mem_ctx, enum encmethod enctype,
+ uint8_t *key, size_t keylen,
+ const uint8_t *plaintext, size_t plainlen,
+ uint8_t **ciphertext, size_t *cipherlen);
+int sss_decrypt(TALLOC_CTX *mem_ctx, enum encmethod enctype,
+ uint8_t *key, size_t keylen,
+ const uint8_t *ciphertext, size_t cipherlen,
+ uint8_t **plaintext, size_t *plainlen);
+
+#endif /* _SSS_CRYPTO_H_ */