} __attribute__ ((packed));
/* crypto algorithms */
-#define CEPH_CRYPTO_NONE 0x0
-#define CEPH_CRYPTO_AES 0x1
+#define CEPH_CRYPTO_NONE 0x0
+#define CEPH_CRYPTO_AES 0x1
+#define CEPH_CRYPTO_AES256KRB5 0x2 /* AES256-CTS-HMAC384-192 */
#define CEPH_AES_IV "cephsageyudagreg"
select CRYPTO_AES
select CRYPTO_CBC
select CRYPTO_GCM
+ select CRYPTO_KRB5
select CRYPTO_LIB_SHA256
select CRYPTO
select KEYS
#include "auth_x.h"
#include "auth_x_protocol.h"
+static const u32 ticket_key_usages[] = {
+ CEPHX_KEY_USAGE_TICKET_SESSION_KEY,
+ CEPHX_KEY_USAGE_TICKET_BLOB,
+ CEPHX_KEY_USAGE_SESSION_CONNECTION_SECRET
+};
+
+static const u32 authorizer_key_usages[] = {
+ CEPHX_KEY_USAGE_AUTHORIZE,
+ CEPHX_KEY_USAGE_AUTHORIZE_CHALLENGE,
+ CEPHX_KEY_USAGE_AUTHORIZE_REPLY
+};
+
+static const u32 client_key_usages[] = {
+ CEPHX_KEY_USAGE_TICKET_SESSION_KEY
+};
+
static void ceph_x_validate_tickets(struct ceph_auth_client *ac, int *pneed);
static int ceph_x_is_authenticated(struct ceph_auth_client *ac)
/*
* AES: ciphertext_len | hdr | data... | padding
+ * AES256KRB5: ciphertext_len | confounder | hdr | data... | hmac
*/
static int ceph_x_encrypt_buflen(const struct ceph_crypto_key *key,
int data_len)
return sizeof(u32) + ceph_crypt_buflen(key, encrypt_len);
}
-static int ceph_x_encrypt(struct ceph_crypto_key *secret, void *buf,
- int buf_len, int plaintext_len)
+static int ceph_x_encrypt(const struct ceph_crypto_key *key, int usage_slot,
+ void *buf, int buf_len, int plaintext_len)
{
struct ceph_x_encrypt_header *hdr;
int ciphertext_len;
int ret;
- hdr = buf + sizeof(u32) + ceph_crypt_data_offset(secret);
+ hdr = buf + sizeof(u32) + ceph_crypt_data_offset(key);
hdr->struct_v = 1;
hdr->magic = cpu_to_le64(CEPHX_ENC_MAGIC);
- ret = ceph_crypt(secret, true, buf + sizeof(u32), buf_len - sizeof(u32),
- plaintext_len + sizeof(struct ceph_x_encrypt_header),
+ ret = ceph_crypt(key, usage_slot, true, buf + sizeof(u32),
+ buf_len - sizeof(u32), plaintext_len + sizeof(*hdr),
&ciphertext_len);
if (ret)
return ret;
return sizeof(u32) + ciphertext_len;
}
-static int __ceph_x_decrypt(struct ceph_crypto_key *secret, void *p,
- int ciphertext_len)
+static int __ceph_x_decrypt(const struct ceph_crypto_key *key, int usage_slot,
+ void *p, int ciphertext_len)
{
struct ceph_x_encrypt_header *hdr;
int plaintext_len;
int ret;
- ret = ceph_crypt(secret, false, p, ciphertext_len, ciphertext_len,
- &plaintext_len);
+ ret = ceph_crypt(key, usage_slot, false, p, ciphertext_len,
+ ciphertext_len, &plaintext_len);
if (ret)
return ret;
- hdr = p + ceph_crypt_data_offset(secret);
+ hdr = p + ceph_crypt_data_offset(key);
if (le64_to_cpu(hdr->magic) != CEPHX_ENC_MAGIC) {
pr_err("%s bad magic\n", __func__);
return -EINVAL;
return plaintext_len - sizeof(*hdr);
}
-static int ceph_x_decrypt(struct ceph_crypto_key *secret, void **p, void *end)
+static int ceph_x_decrypt(const struct ceph_crypto_key *key, int usage_slot,
+ void **p, void *end)
{
int ciphertext_len;
int ret;
ceph_decode_32_safe(p, end, ciphertext_len, e_inval);
ceph_decode_need(p, end, ciphertext_len, e_inval);
- ret = __ceph_x_decrypt(secret, *p, ciphertext_len);
+ ret = __ceph_x_decrypt(key, usage_slot, *p, ciphertext_len);
if (ret < 0)
return ret;
/* blob for me */
dp = *p + ceph_x_encrypt_offset(secret);
- ret = ceph_x_decrypt(secret, p, end);
+ ret = ceph_x_decrypt(secret,
+ 0 /* CEPHX_KEY_USAGE_TICKET_SESSION_KEY */,
+ p, end);
if (ret < 0)
goto out;
dout(" decrypted %d bytes\n", ret);
if (ret)
goto out;
- ret = ceph_crypto_key_prepare(&new_session_key);
+ ret = ceph_crypto_key_prepare(&new_session_key, ticket_key_usages,
+ ARRAY_SIZE(ticket_key_usages));
if (ret)
goto out;
if (is_enc) {
/* encrypted */
tp = *p + ceph_x_encrypt_offset(&th->session_key);
- ret = ceph_x_decrypt(&th->session_key, p, end);
+ ret = ceph_x_decrypt(&th->session_key,
+ 1 /* CEPHX_KEY_USAGE_TICKET_BLOB */,
+ p, end);
if (ret < 0)
goto out;
dout(" encrypted ticket, decrypted %d bytes\n", ret);
msg_b->server_challenge_plus_one = 0;
}
- ret = ceph_x_encrypt(&au->session_key, p, end - p, sizeof(*msg_b));
+ ret = ceph_x_encrypt(&au->session_key,
+ 0 /* CEPHX_KEY_USAGE_AUTHORIZE */,
+ p, end - p, sizeof(*msg_b));
if (ret < 0)
return ret;
if (ret)
goto out_au;
- ret = ceph_crypto_key_prepare(&au->session_key);
+ ret = ceph_crypto_key_prepare(&au->session_key, authorizer_key_usages,
+ ARRAY_SIZE(authorizer_key_usages));
if (ret)
goto out_au;
get_random_bytes(&auth->client_challenge, sizeof(u64));
blob->client_challenge = auth->client_challenge;
blob->server_challenge = cpu_to_le64(xi->server_challenge);
- ret = ceph_x_encrypt(&xi->secret, enc_buf, CEPHX_AU_ENC_BUF_LEN,
+ ret = ceph_x_encrypt(&xi->secret, 0 /* dummy */,
+ enc_buf, CEPHX_AU_ENC_BUF_LEN,
sizeof(*blob));
if (ret < 0)
return ret;
dout("%s connection secret blob len %d\n", __func__, len);
if (len > 0) {
dp = *p + ceph_x_encrypt_offset(&th->session_key);
- ret = ceph_x_decrypt(&th->session_key, p, *p + len);
+ ret = ceph_x_decrypt(&th->session_key,
+ 2 /* CEPHX_KEY_USAGE_SESSION_CONNECTION_SECRET */,
+ p, *p + len);
if (ret < 0)
return ret;
int ret;
/* no leading len */
- ret = __ceph_x_decrypt(secret, challenge, challenge_len);
+ ret = __ceph_x_decrypt(secret,
+ 1 /* CEPHX_KEY_USAGE_AUTHORIZE_CHALLENGE */,
+ challenge, challenge_len);
if (ret < 0)
return ret;
int ret;
dp = *p + ceph_x_encrypt_offset(secret);
- ret = ceph_x_decrypt(secret, p, end);
+ ret = ceph_x_decrypt(secret, 2 /* CEPHX_KEY_USAGE_AUTHORIZE_REPLY */,
+ p, end);
if (ret < 0)
return ret;
sigblock->middle_crc = msg->footer.middle_crc;
sigblock->data_crc = msg->footer.data_crc;
- ret = ceph_x_encrypt(&au->session_key, enc_buf,
- CEPHX_AU_ENC_BUF_LEN, sizeof(*sigblock));
+ ret = ceph_x_encrypt(&au->session_key, 0 /* dummy */,
+ enc_buf, CEPHX_AU_ENC_BUF_LEN,
+ sizeof(*sigblock));
if (ret < 0)
return ret;
sigblock->seq_lower_word = *(__le32 *)&msg->hdr.seq;
/* no leading len, no ceph_x_encrypt_header */
- ret = ceph_crypt(&au->session_key, true, enc_buf,
- CEPHX_AU_ENC_BUF_LEN, sizeof(*sigblock),
- &ciphertext_len);
+ ret = ceph_crypt(&au->session_key, 0 /* dummy */,
+ true, enc_buf, CEPHX_AU_ENC_BUF_LEN,
+ sizeof(*sigblock), &ciphertext_len);
if (ret)
return ret;
goto err_xi;
}
- ret = ceph_crypto_key_prepare(&xi->secret);
+ ret = ceph_crypto_key_prepare(&xi->secret, client_key_usages,
+ ARRAY_SIZE(client_key_usages));
if (ret) {
pr_err("cannot prepare key: %d\n", ret);
goto err_secret;
#define CEPHX_GET_PRINCIPAL_SESSION_KEY 0x0200
#define CEPHX_GET_ROTATING_KEY 0x0400
+/* Principal <-> AuthMonitor */
+/* The session's connection secret: encrypted with AUTH ticket service_key (aka auth_service_key) */
+#define CEPHX_KEY_USAGE_SESSION_CONNECTION_SECRET 0x03
+/* The ticket's CephxServiceTicket containing the session key: uses principal's key */
+#define CEPHX_KEY_USAGE_TICKET_SESSION_KEY 0x04
+/* The ticket's CephxTicketBlob: uses old auth session key (if presented) */
+#define CEPHX_KEY_USAGE_TICKET_BLOB 0x05
+
+/* Principal <-> Service */
+/* Client Authorization Request: using ticket session_key */
+#define CEPHX_KEY_USAGE_AUTHORIZE 0x10
+/* Service's Challenge: using ticket session_key */
+#define CEPHX_KEY_USAGE_AUTHORIZE_CHALLENGE 0x11
+/* Service's final reply: using ticket session key */
+#define CEPHX_KEY_USAGE_AUTHORIZE_REPLY 0x12
+
+/* Service Daemon <-> AuthMonitor */
+/* Rotating Secret Fetch by Services: service daemon's principal key */
+#define CEPHX_KEY_USAGE_ROTATING_SECRET 0x20
+
+/* Service Tickets */
+/* CephxServiceTicketInfo: rotating service key */
+#define CEPHX_KEY_USAGE_TICKET_INFO 0x30
+
/* common bits */
struct ceph_x_ticket_blob {
__u8 struct_v;
#include <linux/sched.h>
#include <linux/slab.h>
#include <crypto/aes.h>
+#include <crypto/krb5.h>
#include <crypto/skcipher.h>
#include <linux/key-type.h>
#include <linux/sched/mm.h>
int ret;
noio_flag = memalloc_noio_save();
- key->tfm = crypto_alloc_sync_skcipher("cbc(aes)", 0, 0);
+ key->aes_tfm = crypto_alloc_sync_skcipher("cbc(aes)", 0, 0);
memalloc_noio_restore(noio_flag);
- if (IS_ERR(key->tfm)) {
- ret = PTR_ERR(key->tfm);
- key->tfm = NULL;
+ if (IS_ERR(key->aes_tfm)) {
+ ret = PTR_ERR(key->aes_tfm);
+ key->aes_tfm = NULL;
return ret;
}
- ret = crypto_sync_skcipher_setkey(key->tfm, key->key, key->len);
+ ret = crypto_sync_skcipher_setkey(key->aes_tfm, key->key, key->len);
if (ret)
return ret;
return 0;
}
-int ceph_crypto_key_prepare(struct ceph_crypto_key *key)
+static int set_krb5_tfms(struct ceph_crypto_key *key, const u32 *key_usages,
+ int key_usage_cnt)
+{
+ struct krb5_buffer TK = { .len = key->len, .data = key->key };
+ unsigned int noio_flag;
+ int ret = 0;
+ int i;
+
+ if (WARN_ON_ONCE(key_usage_cnt > ARRAY_SIZE(key->krb5_tfms)))
+ return -EINVAL;
+
+ key->krb5_type = crypto_krb5_find_enctype(
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192);
+ if (!key->krb5_type)
+ return -ENOPKG;
+
+ /*
+ * Despite crypto_krb5_prepare_encryption() taking a gfp mask,
+ * crypto_alloc_aead() inside of it allocates with GFP_KERNEL.
+ */
+ noio_flag = memalloc_noio_save();
+ for (i = 0; i < key_usage_cnt; i++) {
+ key->krb5_tfms[i] = crypto_krb5_prepare_encryption(
+ key->krb5_type, &TK, key_usages[i],
+ GFP_NOIO);
+ if (IS_ERR(key->krb5_tfms[i])) {
+ ret = PTR_ERR(key->krb5_tfms[i]);
+ key->krb5_tfms[i] = NULL;
+ goto out_flag;
+ }
+ }
+
+out_flag:
+ memalloc_noio_restore(noio_flag);
+ return ret;
+}
+
+int ceph_crypto_key_prepare(struct ceph_crypto_key *key,
+ const u32 *key_usages, int key_usage_cnt)
{
switch (key->type) {
case CEPH_CRYPTO_NONE:
return 0; /* nothing to do */
case CEPH_CRYPTO_AES:
return set_aes_tfm(key);
+ case CEPH_CRYPTO_AES256KRB5:
+ return set_krb5_tfms(key, key_usages, key_usage_cnt);
default:
return -ENOTSUPP;
}
void ceph_crypto_key_destroy(struct ceph_crypto_key *key)
{
- if (key) {
- kfree_sensitive(key->key);
- key->key = NULL;
- if (key->tfm) {
- crypto_free_sync_skcipher(key->tfm);
- key->tfm = NULL;
+ int i;
+
+ if (!key)
+ return;
+
+ kfree_sensitive(key->key);
+ key->key = NULL;
+
+ if (key->type == CEPH_CRYPTO_AES) {
+ if (key->aes_tfm) {
+ crypto_free_sync_skcipher(key->aes_tfm);
+ key->aes_tfm = NULL;
+ }
+ } else if (key->type == CEPH_CRYPTO_AES256KRB5) {
+ for (i = 0; i < ARRAY_SIZE(key->krb5_tfms); i++) {
+ if (key->krb5_tfms[i]) {
+ crypto_free_aead(key->krb5_tfms[i]);
+ key->krb5_tfms[i] = NULL;
+ }
}
}
}
static int ceph_aes_crypt(const struct ceph_crypto_key *key, bool encrypt,
void *buf, int buf_len, int in_len, int *pout_len)
{
- SYNC_SKCIPHER_REQUEST_ON_STACK(req, key->tfm);
+ SYNC_SKCIPHER_REQUEST_ON_STACK(req, key->aes_tfm);
struct sg_table sgt;
struct scatterlist prealloc_sg;
char iv[AES_BLOCK_SIZE] __aligned(8);
return ret;
memcpy(iv, aes_iv, AES_BLOCK_SIZE);
- skcipher_request_set_sync_tfm(req, key->tfm);
+ skcipher_request_set_sync_tfm(req, key->aes_tfm);
skcipher_request_set_callback(req, 0, NULL, NULL);
skcipher_request_set_crypt(req, sgt.sgl, sgt.sgl, crypt_len, iv);
return ret;
}
-int ceph_crypt(const struct ceph_crypto_key *key, bool encrypt,
+static int ceph_krb5_encrypt(const struct ceph_crypto_key *key, int usage_slot,
+ void *buf, int buf_len, int in_len, int *pout_len)
+{
+ struct sg_table sgt;
+ struct scatterlist prealloc_sg;
+ int ret;
+
+ if (WARN_ON_ONCE(usage_slot >= ARRAY_SIZE(key->krb5_tfms)))
+ return -EINVAL;
+
+ ret = setup_sgtable(&sgt, &prealloc_sg, buf, buf_len);
+ if (ret)
+ return ret;
+
+ ret = crypto_krb5_encrypt(key->krb5_type, key->krb5_tfms[usage_slot],
+ sgt.sgl, sgt.nents, buf_len, AES_BLOCK_SIZE,
+ in_len, false);
+ if (ret < 0) {
+ pr_err("%s encrypt failed: %d\n", __func__, ret);
+ goto out_sgt;
+ }
+
+ *pout_len = ret;
+ ret = 0;
+
+out_sgt:
+ teardown_sgtable(&sgt);
+ return ret;
+}
+
+static int ceph_krb5_decrypt(const struct ceph_crypto_key *key, int usage_slot,
+ void *buf, int buf_len, int in_len, int *pout_len)
+{
+ struct sg_table sgt;
+ struct scatterlist prealloc_sg;
+ size_t data_off = 0;
+ size_t data_len = in_len;
+ int ret;
+
+ if (WARN_ON_ONCE(usage_slot >= ARRAY_SIZE(key->krb5_tfms)))
+ return -EINVAL;
+
+ ret = setup_sgtable(&sgt, &prealloc_sg, buf, in_len);
+ if (ret)
+ return ret;
+
+ ret = crypto_krb5_decrypt(key->krb5_type, key->krb5_tfms[usage_slot],
+ sgt.sgl, sgt.nents, &data_off, &data_len);
+ if (ret) {
+ pr_err("%s decrypt failed: %d\n", __func__, ret);
+ goto out_sgt;
+ }
+
+ WARN_ON(data_off != AES_BLOCK_SIZE);
+ *pout_len = data_len;
+
+out_sgt:
+ teardown_sgtable(&sgt);
+ return ret;
+}
+
+int ceph_crypt(const struct ceph_crypto_key *key, int usage_slot, bool encrypt,
void *buf, int buf_len, int in_len, int *pout_len)
{
switch (key->type) {
case CEPH_CRYPTO_AES:
return ceph_aes_crypt(key, encrypt, buf, buf_len, in_len,
pout_len);
+ case CEPH_CRYPTO_AES256KRB5:
+ return encrypt ?
+ ceph_krb5_encrypt(key, usage_slot, buf, buf_len, in_len,
+ pout_len) :
+ ceph_krb5_decrypt(key, usage_slot, buf, buf_len, in_len,
+ pout_len);
default:
return -ENOTSUPP;
}
case CEPH_CRYPTO_NONE:
case CEPH_CRYPTO_AES:
return 0;
+ case CEPH_CRYPTO_AES256KRB5:
+ /* confounder */
+ return AES_BLOCK_SIZE;
default:
BUG();
}
/* PKCS#7 padding at the end */
return data_len + AES_BLOCK_SIZE -
(data_len & (AES_BLOCK_SIZE - 1));
+ case CEPH_CRYPTO_AES256KRB5:
+ /* confounder at the beginning and 192-bit HMAC at the end */
+ return AES_BLOCK_SIZE + data_len + 24;
default:
BUG();
}
#include <linux/ceph/types.h>
#include <linux/ceph/buffer.h>
-#define CEPH_MAX_KEY_LEN 16
+#define CEPH_MAX_KEY_LEN 32
#define CEPH_MAX_CON_SECRET_LEN 64
/*
struct ceph_timespec created;
int len;
void *key;
- struct crypto_sync_skcipher *tfm;
+
+ union {
+ struct crypto_sync_skcipher *aes_tfm;
+ struct {
+ const struct krb5_enctype *krb5_type;
+ struct crypto_aead *krb5_tfms[3];
+ };
+ };
};
-int ceph_crypto_key_prepare(struct ceph_crypto_key *key);
+int ceph_crypto_key_prepare(struct ceph_crypto_key *key,
+ const u32 *key_usages, int key_usage_cnt);
int ceph_crypto_key_clone(struct ceph_crypto_key *dst,
const struct ceph_crypto_key *src);
int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end);
void ceph_crypto_key_destroy(struct ceph_crypto_key *key);
/* crypto.c */
-int ceph_crypt(const struct ceph_crypto_key *key, bool encrypt,
+int ceph_crypt(const struct ceph_crypto_key *key, int usage_slot, bool encrypt,
void *buf, int buf_len, int in_len, int *pout_len);
int ceph_crypt_data_offset(const struct ceph_crypto_key *key);
int ceph_crypt_buflen(const struct ceph_crypto_key *key, int data_len);