From 625bb2ddf15e8f305a53afa44e87f2146fa930af Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Mon, 9 May 2016 17:34:49 +0200 Subject: [PATCH 15/19] 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 --- Makefile.am | 13 +- src/responder/secrets/local.c | 193 +++++++++++++++++--- src/responder/secrets/secsrv_local.h | 2 +- src/util/crypto/libcrypto/crypto_nite.c | 268 ++++++++++++++++++++++++++++ src/util/crypto/nss/nss_crypto.h | 66 +++++++ src/util/crypto/nss/nss_nite.c | 305 ++++++++++++++++++++++++++++++++ src/util/crypto/nss/nss_obfuscate.c | 214 ++-------------------- src/util/crypto/nss/nss_util.c | 211 +++++++++++++++++++++- src/util/crypto/nss/nss_util.h | 1 + src/util/crypto/sss_crypto.c | 66 +++++++ src/util/crypto/sss_crypto.h | 37 ++++ 11 files changed, 1141 insertions(+), 235 deletions(-) create mode 100644 src/util/crypto/libcrypto/crypto_nite.c create mode 100644 src/util/crypto/nss/nss_crypto.h create mode 100644 src/util/crypto/nss/nss_nite.c create mode 100644 src/util/crypto/sss_crypto.c diff --git a/Makefile.am b/Makefile.am index a0a10009dedb19ff8d895e274259c6c7738336ff..c6cae36a4b583b8a9546a1500ac1a0ffd92ead74 100644 --- a/Makefile.am +++ b/Makefile.am @@ -747,7 +747,8 @@ dist_noinst_HEADERS = \ if HAVE_NSS - dist_noinst_HEADERS += src/util/crypto/nss/nss_util.h + dist_noinst_HEADERS += src/util/crypto/nss/nss_util.h \ + src/util/crypto/nss/nss_crypto.h endif SSSD_DOCS = \ @@ -806,7 +807,10 @@ if HAVE_NSS src/util/crypto/nss/nss_hmac_sha1.c \ src/util/crypto/nss/nss_sha512crypt.c \ src/util/crypto/nss/nss_obfuscate.c \ - src/util/crypto/nss/nss_util.c + src/util/crypto/nss/nss_nite.c \ + src/util/crypto/nss/nss_util.c \ + src/util/crypto/sss_crypto.c \ + $(NULL) SSS_CRYPT_CFLAGS = $(NSS_CFLAGS) SSS_CRYPT_LIBS = $(NSS_LIBS) @@ -824,7 +828,10 @@ else SSS_CRYPT_SOURCES = src/util/crypto/libcrypto/crypto_base64.c \ src/util/crypto/libcrypto/crypto_hmac_sha1.c \ src/util/crypto/libcrypto/crypto_sha512crypt.c \ - src/util/crypto/libcrypto/crypto_obfuscate.c + src/util/crypto/libcrypto/crypto_obfuscate.c \ + src/util/crypto/libcrypto/crypto_nite.c \ + src/util/crypto/sss_crypto.c \ + $(NULL) SSS_CRYPT_CFLAGS = $(CRYPTO_CFLAGS) SSS_CRYPT_LIBS = $(CRYPTO_LIBS) diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c index 2887758398e06f6c88323612f8fe69f6808f52f7..5c99c187705f1c3e3ff1592239d4e5dd5b5c85c8 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 +#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_add(ldb, msg); + ret = ldb_msg_add_string(msg, "secret", enc_secret); + if (ret != EOK) goto done; + + 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; - ret = ldb_connect(ldb, SECRETS_DB_PATH"/secrets.ldb", 0, NULL); + 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(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 537f61e53d098deecbc2530e9e3c21a07eaecada..d21b14953b0497a17f7b0b7730a3e1529f928aea 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 2016 diff --git a/src/util/crypto/libcrypto/crypto_nite.c b/src/util/crypto/libcrypto/crypto_nite.c new file mode 100644 index 0000000000000000000000000000000000000000..fa267fbcc398a6d854b6ec91ad71ed4420051b62 --- /dev/null +++ b/src/util/crypto/libcrypto/crypto_nite.c @@ -0,0 +1,268 @@ +/* + SSSD + + Encryption/Decryption primitives + + Authors: + Simo Sorce + + 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 . +*/ + +#include "config.h" +#include +#include + +#include "util/util.h" +#include "util/crypto/sss_crypto.h" + +#include +#include +#include + +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 0000000000000000000000000000000000000000..5ecb5449df84e25a434fde19f47cbbc421fb11e5 --- /dev/null +++ b/src/util/crypto/nss/nss_crypto.h @@ -0,0 +1,66 @@ +/* + SSSD + + NSS crypto wrappers + + Authors: + Jakub Hrozek + + 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 . +*/ + +#include +#include +#include +#include +#include + +#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 0000000000000000000000000000000000000000..3641e05125b6514ecf3541802325fcb1cddd1a77 --- /dev/null +++ b/src/util/crypto/nss/nss_nite.c @@ -0,0 +1,305 @@ +/* + SSSD + + Encryption/Decryption primitives + + Authors: + Simo Sorce + + 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 . +*/ + +#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 8c6bdc52524ad7f7a491603e9fd2902fdf9eed18..a55f22b6de0cfdf23f4728ccfb8d4e68d155544b 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 -#include #include -#include -#include #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 55b81c9b1c8e92e7a16348767a589eb8c92e9330..46835057b04e587b77aed4479c0243522819fd94 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 -#include #include -#include #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 7387b9a7e0d602c81b2123a9c4bd263ed6501ab8..08ab58d87e64631a5b4cfa840bc78f038fda3977 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 . */ +#include 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 0000000000000000000000000000000000000000..4c775f3d926ae32f3cb72b1329c0a025a0550ed5 --- /dev/null +++ b/src/util/crypto/sss_crypto.c @@ -0,0 +1,66 @@ +/* + Authors: + Simo Sorce + + 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 . +*/ + +#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 5b40ecfc483e3696b2c549bf5ea25a969f12bb8c..8c7a883177d6a4be3309ce3374401626eb3fd04f 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 . +*/ + +#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_ */ -- 2.4.11