Skip to content

Commit

Permalink
SUNRPC: Obscure Kerberos signing keys
Browse files Browse the repository at this point in the history
There's no need to keep the signing keys around if we instead allocate
and key an ahash and keep that. This not only enables the subkeys to
be destroyed immediately after deriving them, but it makes the
Kerberos signing code path more efficient.

Tested-by: Scott Mayhew <smayhew@redhat.com>
Reviewed-by: Simo Sorce <simo@redhat.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
  • Loading branch information
Chuck Lever committed Feb 20, 2023
1 parent 9f0b49f commit 2dbe0ca
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 48 deletions.
5 changes: 2 additions & 3 deletions include/linux/sunrpc/gss_krb5.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,14 @@ struct krb5_ctx {
struct crypto_sync_skcipher *initiator_enc;
struct crypto_sync_skcipher *acceptor_enc_aux;
struct crypto_sync_skcipher *initiator_enc_aux;
struct crypto_ahash *acceptor_sign;
struct crypto_ahash *initiator_sign;
u8 Ksess[GSS_KRB5_MAX_KEYLEN]; /* session key */
u8 cksum[GSS_KRB5_MAX_KEYLEN];
atomic_t seq_send;
atomic64_t seq_send64;
time64_t endtime;
struct xdr_netobj mech_used;
u8 initiator_sign[GSS_KRB5_MAX_KEYLEN];
u8 acceptor_sign[GSS_KRB5_MAX_KEYLEN];
u8 initiator_integ[GSS_KRB5_MAX_KEYLEN];
u8 acceptor_integ[GSS_KRB5_MAX_KEYLEN];
};
Expand Down Expand Up @@ -252,7 +252,6 @@ u32
gss_unwrap_kerberos(struct gss_ctx *ctx_id, int offset, int len,
struct xdr_buf *buf);


u32
krb5_encrypt(struct crypto_sync_skcipher *key,
void *iv, void *in, void *out, int length);
Expand Down
70 changes: 70 additions & 0 deletions net/sunrpc/auth_gss/gss_krb5_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,76 @@ make_checksum_v2(struct krb5_ctx *kctx, char *header, int hdrlen,
return err ? GSS_S_FAILURE : 0;
}

/**
* gss_krb5_checksum - Compute the MAC for a GSS Wrap or MIC token
* @tfm: an initialized hash transform
* @header: pointer to a buffer containing the token header, or NULL
* @hdrlen: number of octets in @header
* @body: xdr_buf containing an RPC message (body.len is the message length)
* @body_offset: byte offset into @body to start checksumming
* @cksumout: OUT: a buffer to be filled in with the computed HMAC
*
* Usually expressed as H = HMAC(K, message)[1..h] .
*
* Caller provides the truncation length of the output token (h) in
* cksumout.len.
*
* Return values:
* %GSS_S_COMPLETE: Digest computed, @cksumout filled in
* %GSS_S_FAILURE: Call failed
*/
u32
gss_krb5_checksum(struct crypto_ahash *tfm, char *header, int hdrlen,
const struct xdr_buf *body, int body_offset,
struct xdr_netobj *cksumout)
{
struct ahash_request *req;
int err = -ENOMEM;
u8 *checksumdata;

checksumdata = kmalloc(crypto_ahash_digestsize(tfm), GFP_KERNEL);
if (!checksumdata)
return GSS_S_FAILURE;

req = ahash_request_alloc(tfm, GFP_KERNEL);
if (!req)
goto out_free_cksum;
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
err = crypto_ahash_init(req);
if (err)
goto out_free_ahash;

/*
* Per RFC 4121 Section 4.2.4, the checksum is performed over the
* data body first, then over the octets in "header".
*/
err = xdr_process_buf(body, body_offset, body->len - body_offset,
checksummer, req);
if (err)
goto out_free_ahash;
if (header) {
struct scatterlist sg[1];

sg_init_one(sg, header, hdrlen);
ahash_request_set_crypt(req, sg, NULL, hdrlen);
err = crypto_ahash_update(req);
if (err)
goto out_free_ahash;
}

ahash_request_set_crypt(req, NULL, checksumdata, 0);
err = crypto_ahash_final(req);
if (err)
goto out_free_ahash;
memcpy(cksumout->data, checksumdata, cksumout->len);

out_free_ahash:
ahash_request_free(req);
out_free_cksum:
kfree_sensitive(checksumdata);
return err ? GSS_S_FAILURE : GSS_S_COMPLETE;
}

struct encryptor_desc {
u8 iv[GSS_KRB5_MAX_BLOCKSIZE];
struct skcipher_request *req;
Expand Down
4 changes: 4 additions & 0 deletions net/sunrpc/auth_gss/gss_krb5_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@

void krb5_make_confounder(u8 *p, int conflen);

u32 gss_krb5_checksum(struct crypto_ahash *tfm, char *header, int hdrlen,
const struct xdr_buf *body, int body_offset,
struct xdr_netobj *cksumout);

#endif /* _NET_SUNRPC_AUTH_GSS_KRB5_INTERNAL_H */
37 changes: 27 additions & 10 deletions net/sunrpc/auth_gss/gss_krb5_mech.c
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,21 @@ context_derive_keys_des3(struct krb5_ctx *ctx, gfp_t gfp_mask)
return -EINVAL;
}

static struct crypto_ahash *
gss_krb5_alloc_hash_v2(struct krb5_ctx *kctx, const struct xdr_netobj *key)
{
struct crypto_ahash *tfm;

tfm = crypto_alloc_ahash(kctx->gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm))
return NULL;
if (crypto_ahash_setkey(tfm, key->data, key->len)) {
crypto_free_ahash(tfm);
return NULL;
}
return tfm;
}

static int
context_derive_keys_new(struct krb5_ctx *ctx, gfp_t gfp_mask)
{
Expand Down Expand Up @@ -414,23 +429,21 @@ context_derive_keys_new(struct krb5_ctx *ctx, gfp_t gfp_mask)

/* initiator sign checksum */
set_cdata(cdata, KG_USAGE_INITIATOR_SIGN, KEY_USAGE_SEED_CHECKSUM);
keyout.data = ctx->initiator_sign;
err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
if (err) {
dprintk("%s: Error %d deriving initiator_sign key\n",
__func__, err);
if (err)
goto out_free;
ctx->initiator_sign = gss_krb5_alloc_hash_v2(ctx, &keyout);
if (ctx->initiator_sign == NULL)
goto out_free;
}

/* acceptor sign checksum */
set_cdata(cdata, KG_USAGE_ACCEPTOR_SIGN, KEY_USAGE_SEED_CHECKSUM);
keyout.data = ctx->acceptor_sign;
err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
if (err) {
dprintk("%s: Error %d deriving acceptor_sign key\n",
__func__, err);
if (err)
goto out_free;
ctx->acceptor_sign = gss_krb5_alloc_hash_v2(ctx, &keyout);
if (ctx->acceptor_sign == NULL)
goto out_free;
}

/* initiator seal integrity */
set_cdata(cdata, KG_USAGE_INITIATOR_SEAL, KEY_USAGE_SEED_INTEGRITY);
Expand Down Expand Up @@ -458,6 +471,8 @@ context_derive_keys_new(struct krb5_ctx *ctx, gfp_t gfp_mask)
return ret;

out_free:
crypto_free_ahash(ctx->acceptor_sign);
crypto_free_ahash(ctx->initiator_sign);
crypto_free_sync_skcipher(ctx->acceptor_enc_aux);
crypto_free_sync_skcipher(ctx->acceptor_enc);
crypto_free_sync_skcipher(ctx->initiator_enc_aux);
Expand Down Expand Up @@ -581,6 +596,8 @@ gss_delete_sec_context_kerberos(void *internal_ctx) {
crypto_free_sync_skcipher(kctx->initiator_enc);
crypto_free_sync_skcipher(kctx->acceptor_enc_aux);
crypto_free_sync_skcipher(kctx->initiator_enc_aux);
crypto_free_ahash(kctx->acceptor_sign);
crypto_free_ahash(kctx->initiator_sign);
kfree(kctx->mech_used.data);
kfree(kctx);
}
Expand Down
30 changes: 11 additions & 19 deletions net/sunrpc/auth_gss/gss_krb5_seal.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
#include <linux/crypto.h>
#include <linux/atomic.h>

#include "gss_krb5_internal.h"

#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
Expand Down Expand Up @@ -166,14 +168,14 @@ static u32
gss_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text,
struct xdr_netobj *token)
{
char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
struct xdr_netobj cksumobj = { .len = sizeof(cksumdata),
.data = cksumdata};
struct crypto_ahash *tfm = ctx->initiate ?
ctx->initiator_sign : ctx->acceptor_sign;
struct xdr_netobj cksumobj = {
.len = ctx->gk5e->cksumlength,
};
__be64 seq_send_be64;
void *krb5_hdr;
time64_t now;
u8 *cksumkey;
unsigned int cksum_usage;
__be64 seq_send_be64;

dprintk("RPC: %s\n", __func__);

Expand All @@ -184,22 +186,12 @@ gss_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text,
seq_send_be64 = cpu_to_be64(atomic64_fetch_inc(&ctx->seq_send64));
memcpy(krb5_hdr + 8, (char *) &seq_send_be64, 8);

if (ctx->initiate) {
cksumkey = ctx->initiator_sign;
cksum_usage = KG_USAGE_INITIATOR_SIGN;
} else {
cksumkey = ctx->acceptor_sign;
cksum_usage = KG_USAGE_ACCEPTOR_SIGN;
}

if (make_checksum_v2(ctx, krb5_hdr, GSS_KRB5_TOK_HDR_LEN,
text, 0, cksumkey, cksum_usage, &cksumobj))
cksumobj.data = krb5_hdr + GSS_KRB5_TOK_HDR_LEN;
if (gss_krb5_checksum(tfm, krb5_hdr, GSS_KRB5_TOK_HDR_LEN,
text, 0, &cksumobj))
return GSS_S_FAILURE;

memcpy(krb5_hdr + GSS_KRB5_TOK_HDR_LEN, cksumobj.data, cksumobj.len);

now = ktime_get_real_seconds();

return (ctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
}

Expand Down
29 changes: 13 additions & 16 deletions net/sunrpc/auth_gss/gss_krb5_unseal.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,14 @@
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/

#include <crypto/algapi.h>
#include <linux/types.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_krb5.h>
#include <linux/crypto.h>

#include "gss_krb5_internal.h"

#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
Expand Down Expand Up @@ -146,16 +149,18 @@ static u32
gss_verify_mic_v2(struct krb5_ctx *ctx,
struct xdr_buf *message_buffer, struct xdr_netobj *read_token)
{
struct crypto_ahash *tfm = ctx->initiate ?
ctx->acceptor_sign : ctx->initiator_sign;
char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
struct xdr_netobj cksumobj = {.len = sizeof(cksumdata),
.data = cksumdata};
time64_t now;
struct xdr_netobj cksumobj = {
.len = ctx->gk5e->cksumlength,
.data = cksumdata,
};
u8 *ptr = read_token->data;
u8 *cksumkey;
__be16 be16_ptr;
time64_t now;
u8 flags;
int i;
unsigned int cksum_usage;
__be16 be16_ptr;

dprintk("RPC: %s\n", __func__);

Expand All @@ -177,16 +182,8 @@ gss_verify_mic_v2(struct krb5_ctx *ctx,
if (ptr[i] != 0xff)
return GSS_S_DEFECTIVE_TOKEN;

if (ctx->initiate) {
cksumkey = ctx->acceptor_sign;
cksum_usage = KG_USAGE_ACCEPTOR_SIGN;
} else {
cksumkey = ctx->initiator_sign;
cksum_usage = KG_USAGE_INITIATOR_SIGN;
}

if (make_checksum_v2(ctx, ptr, GSS_KRB5_TOK_HDR_LEN, message_buffer, 0,
cksumkey, cksum_usage, &cksumobj))
if (gss_krb5_checksum(tfm, ptr, GSS_KRB5_TOK_HDR_LEN,
message_buffer, 0, &cksumobj))
return GSS_S_FAILURE;

if (memcmp(cksumobj.data, ptr + GSS_KRB5_TOK_HDR_LEN,
Expand Down

0 comments on commit 2dbe0ca

Please sign in to comment.