mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 03:27:30 -04:00
SUNRPC: Remove dead code from rpcsec_gss_krb5
With all per-message crypto operations routed through crypto/krb5, a substantial body of code in rpcsec_gss_krb5 has no remaining callers. The internal key derivation functions (krb5_derive_key_v2, krb5_kdf_hmac_sha2, krb5_kdf_feedback_cmac) and the low-level crypto primitives (krb5_encrypt, gss_krb5_checksum, krb5_cbc_cts_ encrypt/decrypt, krb5_etm_checksum) are unreachable because their only call sites were the per-enctype function pointers removed in previous patches. Delete gss_krb5_keys.c entirely and strip the dead functions from gss_krb5_crypto.c. The KUnit test suite in gss_krb5_test.c exercised exactly these internal functions: RFC 3961 n-fold, RFC 3962 key derivation, RFC 6803 Camellia key derivation, and RFC 8009 AES-SHA2 key derivation, plus encryption self-tests that drove the now-removed encrypt routines. The corresponding test coverage is provided by the crypto/krb5 selftests in crypto/krb5/selftest.c. Remove the test file, the RPCSEC_GSS_KRB5_KUNIT_TEST Kconfig symbol, the .kunitconfig, and all VISIBLE_IF_KUNIT / EXPORT_SYMBOL_IF_KUNIT annotations. xdr_process_buf() walked xdr_buf segments through a per-segment callback and existed solely for the crypto routines in gss_krb5_crypto.c. With that file removed, xdr_process_buf() has no remaining callers. Its successor, xdr_buf_to_sg(), populates a scatterlist directly from an xdr_buf byte range and was introduced earlier in this series. With every consumer of struct gss_krb5_enctype removed, replace its remaining uses with the equivalent fields from struct krb5_enctype (key_len). Remove struct gss_krb5_enctype, the supported_gss_krb5_enctypes[] table, gss_krb5_lookup_enctype(), and the gk5e pointer from krb5_ctx. Assisted-by: Claude:claude-opus-4-6 Reviewed-by: Jeff Layton <jlayton@kernel.org> Acked-by: Anna Schumaker <anna.schumaker@hammerspace.com> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
@@ -275,7 +275,6 @@ extern void xdr_finish_decode(struct xdr_stream *xdr);
|
||||
extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes);
|
||||
extern unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len);
|
||||
extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len);
|
||||
extern int xdr_process_buf(const struct xdr_buf *buf, unsigned int offset, unsigned int len, int (*actor)(struct scatterlist *, void *), void *data);
|
||||
extern void xdr_set_pagelen(struct xdr_stream *, unsigned int len);
|
||||
extern bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf,
|
||||
unsigned int len);
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
CONFIG_KUNIT=y
|
||||
CONFIG_UBSAN=y
|
||||
CONFIG_STACKTRACE=y
|
||||
CONFIG_NET=y
|
||||
CONFIG_NETWORK_FILESYSTEMS=y
|
||||
CONFIG_INET=y
|
||||
CONFIG_FILE_LOCKING=y
|
||||
CONFIG_MULTIUSER=y
|
||||
CONFIG_CRYPTO=y
|
||||
CONFIG_CRYPTO_CBC=y
|
||||
CONFIG_CRYPTO_CTS=y
|
||||
CONFIG_CRYPTO_ECB=y
|
||||
CONFIG_CRYPTO_HMAC=y
|
||||
CONFIG_CRYPTO_CMAC=y
|
||||
CONFIG_CRYPTO_MD5=y
|
||||
CONFIG_CRYPTO_SHA1=y
|
||||
CONFIG_CRYPTO_SHA256=y
|
||||
CONFIG_CRYPTO_SHA512=y
|
||||
CONFIG_CRYPTO_DES=y
|
||||
CONFIG_CRYPTO_AES=y
|
||||
CONFIG_CRYPTO_CAMELLIA=y
|
||||
CONFIG_NFS_FS=y
|
||||
CONFIG_SUNRPC=y
|
||||
CONFIG_SUNRPC_GSS=y
|
||||
CONFIG_RPCSEC_GSS_KRB5=y
|
||||
CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1=y
|
||||
CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA=y
|
||||
CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2=y
|
||||
CONFIG_RPCSEC_GSS_KRB5_KUNIT_TEST=y
|
||||
@@ -73,21 +73,6 @@ config RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2
|
||||
SHA-2 digests. These include aes128-cts-hmac-sha256-128 and
|
||||
aes256-cts-hmac-sha384-192.
|
||||
|
||||
config RPCSEC_GSS_KRB5_KUNIT_TEST
|
||||
tristate "KUnit tests for RPCSEC GSS Kerberos" if !KUNIT_ALL_TESTS
|
||||
depends on RPCSEC_GSS_KRB5 && KUNIT
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
This builds the KUnit tests for RPCSEC GSS Kerberos 5.
|
||||
|
||||
KUnit tests run during boot and output the results to the debug
|
||||
log in TAP format (https://testanything.org/). Only useful for
|
||||
kernel devs running KUnit test harness and are not for inclusion
|
||||
into a production build.
|
||||
|
||||
For more information on KUnit and unit tests in general, refer
|
||||
to the KUnit documentation in Documentation/dev-tools/kunit/.
|
||||
|
||||
config SUNRPC_DEBUG
|
||||
bool "RPC: Enable dprintk debugging"
|
||||
depends on SUNRPC && SYSCTL
|
||||
|
||||
@@ -12,6 +12,4 @@ auth_rpcgss-y := auth_gss.o \
|
||||
obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o
|
||||
|
||||
rpcsec_gss_krb5-y := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \
|
||||
gss_krb5_wrap.o gss_krb5_crypto.o gss_krb5_keys.o
|
||||
|
||||
obj-$(CONFIG_RPCSEC_GSS_KRB5_KUNIT_TEST) += gss_krb5_test.o
|
||||
gss_krb5_wrap.o gss_krb5_crypto.o
|
||||
|
||||
@@ -34,19 +34,14 @@
|
||||
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/skcipher.h>
|
||||
#include <crypto/utils.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/sunrpc/gss_krb5.h>
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
#include <kunit/visibility.h>
|
||||
|
||||
#include "gss_krb5_internal.h"
|
||||
|
||||
@@ -54,303 +49,6 @@
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
/**
|
||||
* krb5_make_confounder - Generate a confounder string
|
||||
* @p: memory location into which to write the string
|
||||
* @conflen: string length to write, in octets
|
||||
*
|
||||
* RFCs 1964 and 3961 mention only "a random confounder" without going
|
||||
* into detail about its function or cryptographic requirements. The
|
||||
* assumed purpose is to prevent repeated encryption of a plaintext with
|
||||
* the same key from generating the same ciphertext. It is also used to
|
||||
* pad minimum plaintext length to at least a single cipher block.
|
||||
*
|
||||
* However, in situations like the GSS Kerberos 5 mechanism, where the
|
||||
* encryption IV is always all zeroes, the confounder also effectively
|
||||
* functions like an IV. Thus, not only must it be unique from message
|
||||
* to message, but it must also be difficult to predict. Otherwise an
|
||||
* attacker can correlate the confounder to previous or future values,
|
||||
* making the encryption easier to break.
|
||||
*
|
||||
* Given that the primary consumer of this encryption mechanism is a
|
||||
* network storage protocol, a type of traffic that often carries
|
||||
* predictable payloads (eg, all zeroes when reading unallocated blocks
|
||||
* from a file), our confounder generation has to be cryptographically
|
||||
* strong.
|
||||
*/
|
||||
void krb5_make_confounder(u8 *p, int conflen)
|
||||
{
|
||||
get_random_bytes(p, conflen);
|
||||
}
|
||||
|
||||
/**
|
||||
* krb5_encrypt - simple encryption of an RPCSEC GSS payload
|
||||
* @tfm: initialized cipher transform
|
||||
* @iv: pointer to an IV
|
||||
* @in: plaintext to encrypt
|
||||
* @out: OUT: ciphertext
|
||||
* @length: length of input and output buffers, in bytes
|
||||
*
|
||||
* @iv may be NULL to force the use of an all-zero IV.
|
||||
* The buffer containing the IV must be as large as the
|
||||
* cipher's ivsize.
|
||||
*
|
||||
* Return values:
|
||||
* %0: @in successfully encrypted into @out
|
||||
* negative errno: @in not encrypted
|
||||
*/
|
||||
u32
|
||||
krb5_encrypt(
|
||||
struct crypto_sync_skcipher *tfm,
|
||||
void * iv,
|
||||
void * in,
|
||||
void * out,
|
||||
int length)
|
||||
{
|
||||
u32 ret = -EINVAL;
|
||||
struct scatterlist sg[1];
|
||||
u8 local_iv[GSS_KRB5_MAX_BLOCKSIZE] = {0};
|
||||
SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
|
||||
|
||||
if (length % crypto_sync_skcipher_blocksize(tfm) != 0)
|
||||
goto out;
|
||||
|
||||
if (crypto_sync_skcipher_ivsize(tfm) > GSS_KRB5_MAX_BLOCKSIZE) {
|
||||
dprintk("RPC: gss_k5encrypt: tfm iv size too large %d\n",
|
||||
crypto_sync_skcipher_ivsize(tfm));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (iv)
|
||||
memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm));
|
||||
|
||||
memcpy(out, in, length);
|
||||
sg_init_one(sg, out, length);
|
||||
|
||||
skcipher_request_set_sync_tfm(req, tfm);
|
||||
skcipher_request_set_callback(req, 0, NULL, NULL);
|
||||
skcipher_request_set_crypt(req, sg, sg, length, local_iv);
|
||||
|
||||
ret = crypto_skcipher_encrypt(req);
|
||||
skcipher_request_zero(req);
|
||||
out:
|
||||
dprintk("RPC: krb5_encrypt returns %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
checksummer(struct scatterlist *sg, void *data)
|
||||
{
|
||||
struct ahash_request *req = data;
|
||||
|
||||
ahash_request_set_crypt(req, sg, NULL, sg->length);
|
||||
|
||||
return crypto_ahash_update(req);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
min_t(int, cksumout->len, crypto_ahash_digestsize(tfm)));
|
||||
|
||||
out_free_ahash:
|
||||
ahash_request_free(req);
|
||||
out_free_cksum:
|
||||
kfree_sensitive(checksumdata);
|
||||
return err ? GSS_S_FAILURE : GSS_S_COMPLETE;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(gss_krb5_checksum);
|
||||
|
||||
struct encryptor_desc {
|
||||
u8 iv[GSS_KRB5_MAX_BLOCKSIZE];
|
||||
struct skcipher_request *req;
|
||||
int pos;
|
||||
struct xdr_buf *outbuf;
|
||||
struct page **pages;
|
||||
struct scatterlist infrags[4];
|
||||
struct scatterlist outfrags[4];
|
||||
int fragno;
|
||||
int fraglen;
|
||||
};
|
||||
|
||||
static int
|
||||
encryptor(struct scatterlist *sg, void *data)
|
||||
{
|
||||
struct encryptor_desc *desc = data;
|
||||
struct xdr_buf *outbuf = desc->outbuf;
|
||||
struct crypto_sync_skcipher *tfm =
|
||||
crypto_sync_skcipher_reqtfm(desc->req);
|
||||
struct page *in_page;
|
||||
int thislen = desc->fraglen + sg->length;
|
||||
int fraglen, ret;
|
||||
int page_pos;
|
||||
|
||||
/* Worst case is 4 fragments: head, end of page 1, start
|
||||
* of page 2, tail. Anything more is a bug. */
|
||||
BUG_ON(desc->fragno > 3);
|
||||
|
||||
page_pos = desc->pos - outbuf->head[0].iov_len;
|
||||
if (page_pos >= 0 && page_pos < outbuf->page_len) {
|
||||
/* pages are not in place: */
|
||||
int i = (page_pos + outbuf->page_base) >> PAGE_SHIFT;
|
||||
in_page = desc->pages[i];
|
||||
} else {
|
||||
in_page = sg_page(sg);
|
||||
}
|
||||
sg_set_page(&desc->infrags[desc->fragno], in_page, sg->length,
|
||||
sg->offset);
|
||||
sg_set_page(&desc->outfrags[desc->fragno], sg_page(sg), sg->length,
|
||||
sg->offset);
|
||||
desc->fragno++;
|
||||
desc->fraglen += sg->length;
|
||||
desc->pos += sg->length;
|
||||
|
||||
fraglen = thislen & (crypto_sync_skcipher_blocksize(tfm) - 1);
|
||||
thislen -= fraglen;
|
||||
|
||||
if (thislen == 0)
|
||||
return 0;
|
||||
|
||||
sg_mark_end(&desc->infrags[desc->fragno - 1]);
|
||||
sg_mark_end(&desc->outfrags[desc->fragno - 1]);
|
||||
|
||||
skcipher_request_set_crypt(desc->req, desc->infrags, desc->outfrags,
|
||||
thislen, desc->iv);
|
||||
|
||||
ret = crypto_skcipher_encrypt(desc->req);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sg_init_table(desc->infrags, 4);
|
||||
sg_init_table(desc->outfrags, 4);
|
||||
|
||||
if (fraglen) {
|
||||
sg_set_page(&desc->outfrags[0], sg_page(sg), fraglen,
|
||||
sg->offset + sg->length - fraglen);
|
||||
desc->infrags[0] = desc->outfrags[0];
|
||||
sg_assign_page(&desc->infrags[0], in_page);
|
||||
desc->fragno = 1;
|
||||
desc->fraglen = fraglen;
|
||||
} else {
|
||||
desc->fragno = 0;
|
||||
desc->fraglen = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct decryptor_desc {
|
||||
u8 iv[GSS_KRB5_MAX_BLOCKSIZE];
|
||||
struct skcipher_request *req;
|
||||
struct scatterlist frags[4];
|
||||
int fragno;
|
||||
int fraglen;
|
||||
};
|
||||
|
||||
static int
|
||||
decryptor(struct scatterlist *sg, void *data)
|
||||
{
|
||||
struct decryptor_desc *desc = data;
|
||||
int thislen = desc->fraglen + sg->length;
|
||||
struct crypto_sync_skcipher *tfm =
|
||||
crypto_sync_skcipher_reqtfm(desc->req);
|
||||
int fraglen, ret;
|
||||
|
||||
/* Worst case is 4 fragments: head, end of page 1, start
|
||||
* of page 2, tail. Anything more is a bug. */
|
||||
BUG_ON(desc->fragno > 3);
|
||||
sg_set_page(&desc->frags[desc->fragno], sg_page(sg), sg->length,
|
||||
sg->offset);
|
||||
desc->fragno++;
|
||||
desc->fraglen += sg->length;
|
||||
|
||||
fraglen = thislen & (crypto_sync_skcipher_blocksize(tfm) - 1);
|
||||
thislen -= fraglen;
|
||||
|
||||
if (thislen == 0)
|
||||
return 0;
|
||||
|
||||
sg_mark_end(&desc->frags[desc->fragno - 1]);
|
||||
|
||||
skcipher_request_set_crypt(desc->req, desc->frags, desc->frags,
|
||||
thislen, desc->iv);
|
||||
|
||||
ret = crypto_skcipher_decrypt(desc->req);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sg_init_table(desc->frags, 4);
|
||||
|
||||
if (fraglen) {
|
||||
sg_set_page(&desc->frags[0], sg_page(sg), fraglen,
|
||||
sg->offset + sg->length - fraglen);
|
||||
desc->fragno = 1;
|
||||
desc->fraglen = fraglen;
|
||||
} else {
|
||||
desc->fragno = 0;
|
||||
desc->fraglen = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function makes the assumption that it was ultimately called
|
||||
@@ -363,7 +61,7 @@ decryptor(struct scatterlist *sg, void *data)
|
||||
*
|
||||
* Even with that guarantee, this function may be called more than
|
||||
* once in the processing of gss_wrap(). The best we can do is
|
||||
* verify at compile-time (see GSS_KRB5_SLACK_CHECK) that the
|
||||
* verify at compile-time (see GSS_KRB5_MAX_SLACK_NEEDED) that the
|
||||
* largest expected shift will fit within RPC_MAX_AUTH_SIZE.
|
||||
* At run-time we can verify that a single invocation of this
|
||||
* function doesn't attempt to use more the RPC_MAX_AUTH_SIZE.
|
||||
@@ -389,263 +87,6 @@ xdr_extend_head(struct xdr_buf *buf, unsigned int base, unsigned int shiftlen)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32
|
||||
gss_krb5_cts_crypt(struct crypto_sync_skcipher *cipher, struct xdr_buf *buf,
|
||||
u32 offset, u8 *iv, struct page **pages, int encrypt)
|
||||
{
|
||||
u32 ret;
|
||||
struct scatterlist sg[1];
|
||||
SYNC_SKCIPHER_REQUEST_ON_STACK(req, cipher);
|
||||
u8 *data;
|
||||
struct page **save_pages;
|
||||
u32 len = buf->len - offset;
|
||||
|
||||
if (len > GSS_KRB5_MAX_BLOCKSIZE * 2) {
|
||||
WARN_ON(0);
|
||||
return -ENOMEM;
|
||||
}
|
||||
data = kmalloc(GSS_KRB5_MAX_BLOCKSIZE * 2, GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* For encryption, we want to read from the cleartext
|
||||
* page cache pages, and write the encrypted data to
|
||||
* the supplied xdr_buf pages.
|
||||
*/
|
||||
save_pages = buf->pages;
|
||||
if (encrypt)
|
||||
buf->pages = pages;
|
||||
|
||||
ret = read_bytes_from_xdr_buf(buf, offset, data, len);
|
||||
buf->pages = save_pages;
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
sg_init_one(sg, data, len);
|
||||
|
||||
skcipher_request_set_sync_tfm(req, cipher);
|
||||
skcipher_request_set_callback(req, 0, NULL, NULL);
|
||||
skcipher_request_set_crypt(req, sg, sg, len, iv);
|
||||
|
||||
if (encrypt)
|
||||
ret = crypto_skcipher_encrypt(req);
|
||||
else
|
||||
ret = crypto_skcipher_decrypt(req);
|
||||
|
||||
skcipher_request_zero(req);
|
||||
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = write_bytes_to_xdr_buf(buf, offset, data, len);
|
||||
|
||||
#if IS_ENABLED(CONFIG_KUNIT)
|
||||
/*
|
||||
* CBC-CTS does not define an output IV but RFC 3962 defines it as the
|
||||
* penultimate block of ciphertext, so copy that into the IV buffer
|
||||
* before returning.
|
||||
*/
|
||||
if (encrypt)
|
||||
memcpy(iv, data, crypto_sync_skcipher_ivsize(cipher));
|
||||
#endif
|
||||
|
||||
out:
|
||||
kfree(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* krb5_cbc_cts_encrypt - encrypt in CBC mode with CTS
|
||||
* @cts_tfm: CBC cipher with CTS
|
||||
* @cbc_tfm: base CBC cipher
|
||||
* @offset: starting byte offset for plaintext
|
||||
* @buf: OUT: output buffer
|
||||
* @pages: plaintext
|
||||
* @iv: output CBC initialization vector, or NULL
|
||||
* @ivsize: size of @iv, in octets
|
||||
*
|
||||
* To provide confidentiality, encrypt using cipher block chaining
|
||||
* with ciphertext stealing. Message integrity is handled separately.
|
||||
*
|
||||
* Return values:
|
||||
* %0: encryption successful
|
||||
* negative errno: encryption could not be completed
|
||||
*/
|
||||
VISIBLE_IF_KUNIT
|
||||
int krb5_cbc_cts_encrypt(struct crypto_sync_skcipher *cts_tfm,
|
||||
struct crypto_sync_skcipher *cbc_tfm,
|
||||
u32 offset, struct xdr_buf *buf, struct page **pages,
|
||||
u8 *iv, unsigned int ivsize)
|
||||
{
|
||||
u32 blocksize, nbytes, nblocks, cbcbytes;
|
||||
struct encryptor_desc desc;
|
||||
int err;
|
||||
|
||||
blocksize = crypto_sync_skcipher_blocksize(cts_tfm);
|
||||
nbytes = buf->len - offset;
|
||||
nblocks = (nbytes + blocksize - 1) / blocksize;
|
||||
cbcbytes = 0;
|
||||
if (nblocks > 2)
|
||||
cbcbytes = (nblocks - 2) * blocksize;
|
||||
|
||||
memset(desc.iv, 0, sizeof(desc.iv));
|
||||
|
||||
/* Handle block-sized chunks of plaintext with CBC. */
|
||||
if (cbcbytes) {
|
||||
SYNC_SKCIPHER_REQUEST_ON_STACK(req, cbc_tfm);
|
||||
|
||||
desc.pos = offset;
|
||||
desc.fragno = 0;
|
||||
desc.fraglen = 0;
|
||||
desc.pages = pages;
|
||||
desc.outbuf = buf;
|
||||
desc.req = req;
|
||||
|
||||
skcipher_request_set_sync_tfm(req, cbc_tfm);
|
||||
skcipher_request_set_callback(req, 0, NULL, NULL);
|
||||
|
||||
sg_init_table(desc.infrags, 4);
|
||||
sg_init_table(desc.outfrags, 4);
|
||||
|
||||
err = xdr_process_buf(buf, offset, cbcbytes, encryptor, &desc);
|
||||
skcipher_request_zero(req);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Remaining plaintext is handled with CBC-CTS. */
|
||||
err = gss_krb5_cts_crypt(cts_tfm, buf, offset + cbcbytes,
|
||||
desc.iv, pages, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (unlikely(iv))
|
||||
memcpy(iv, desc.iv, ivsize);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(krb5_cbc_cts_encrypt);
|
||||
|
||||
/**
|
||||
* krb5_cbc_cts_decrypt - decrypt in CBC mode with CTS
|
||||
* @cts_tfm: CBC cipher with CTS
|
||||
* @cbc_tfm: base CBC cipher
|
||||
* @offset: starting byte offset for plaintext
|
||||
* @buf: OUT: output buffer
|
||||
*
|
||||
* Return values:
|
||||
* %0: decryption successful
|
||||
* negative errno: decryption could not be completed
|
||||
*/
|
||||
VISIBLE_IF_KUNIT
|
||||
int krb5_cbc_cts_decrypt(struct crypto_sync_skcipher *cts_tfm,
|
||||
struct crypto_sync_skcipher *cbc_tfm,
|
||||
u32 offset, struct xdr_buf *buf)
|
||||
{
|
||||
u32 blocksize, nblocks, cbcbytes;
|
||||
struct decryptor_desc desc;
|
||||
int err;
|
||||
|
||||
blocksize = crypto_sync_skcipher_blocksize(cts_tfm);
|
||||
nblocks = (buf->len + blocksize - 1) / blocksize;
|
||||
cbcbytes = 0;
|
||||
if (nblocks > 2)
|
||||
cbcbytes = (nblocks - 2) * blocksize;
|
||||
|
||||
memset(desc.iv, 0, sizeof(desc.iv));
|
||||
|
||||
/* Handle block-sized chunks of plaintext with CBC. */
|
||||
if (cbcbytes) {
|
||||
SYNC_SKCIPHER_REQUEST_ON_STACK(req, cbc_tfm);
|
||||
|
||||
desc.fragno = 0;
|
||||
desc.fraglen = 0;
|
||||
desc.req = req;
|
||||
|
||||
skcipher_request_set_sync_tfm(req, cbc_tfm);
|
||||
skcipher_request_set_callback(req, 0, NULL, NULL);
|
||||
|
||||
sg_init_table(desc.frags, 4);
|
||||
|
||||
err = xdr_process_buf(buf, 0, cbcbytes, decryptor, &desc);
|
||||
skcipher_request_zero(req);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Remaining plaintext is handled with CBC-CTS. */
|
||||
return gss_krb5_cts_crypt(cts_tfm, buf, cbcbytes, desc.iv, NULL, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(krb5_cbc_cts_decrypt);
|
||||
|
||||
/**
|
||||
* krb5_etm_checksum - Compute a MAC for a GSS Wrap token
|
||||
* @cipher: an initialized cipher transform
|
||||
* @tfm: an initialized hash transform
|
||||
* @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, IV | ciphertext)[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
|
||||
*/
|
||||
VISIBLE_IF_KUNIT
|
||||
u32 krb5_etm_checksum(struct crypto_sync_skcipher *cipher,
|
||||
struct crypto_ahash *tfm, const struct xdr_buf *body,
|
||||
int body_offset, struct xdr_netobj *cksumout)
|
||||
{
|
||||
unsigned int ivsize = crypto_sync_skcipher_ivsize(cipher);
|
||||
struct ahash_request *req;
|
||||
struct scatterlist sg[1];
|
||||
u8 *iv, *checksumdata;
|
||||
int err = -ENOMEM;
|
||||
|
||||
checksumdata = kmalloc(crypto_ahash_digestsize(tfm), GFP_KERNEL);
|
||||
if (!checksumdata)
|
||||
return GSS_S_FAILURE;
|
||||
/* For RPCSEC, the "initial cipher state" is always all zeroes. */
|
||||
iv = kzalloc(ivsize, GFP_KERNEL);
|
||||
if (!iv)
|
||||
goto out_free_mem;
|
||||
|
||||
req = ahash_request_alloc(tfm, GFP_KERNEL);
|
||||
if (!req)
|
||||
goto out_free_mem;
|
||||
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
|
||||
err = crypto_ahash_init(req);
|
||||
if (err)
|
||||
goto out_free_ahash;
|
||||
|
||||
sg_init_one(sg, iv, ivsize);
|
||||
ahash_request_set_crypt(req, sg, NULL, ivsize);
|
||||
err = crypto_ahash_update(req);
|
||||
if (err)
|
||||
goto out_free_ahash;
|
||||
err = xdr_process_buf(body, body_offset, body->len - body_offset,
|
||||
checksummer, 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_mem:
|
||||
kfree(iv);
|
||||
kfree_sensitive(checksumdata);
|
||||
return err ? GSS_S_FAILURE : GSS_S_COMPLETE;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(krb5_etm_checksum);
|
||||
|
||||
/**
|
||||
* gss_krb5_aead_encrypt - Encrypt a wrap token using crypto/krb5
|
||||
|
||||
@@ -10,38 +10,8 @@
|
||||
|
||||
#include <crypto/krb5.h>
|
||||
|
||||
/*
|
||||
* The RFCs often specify payload lengths in bits. This helper
|
||||
* converts a specified bit-length to the number of octets/bytes.
|
||||
*/
|
||||
#define BITS2OCTETS(x) ((x) / 8)
|
||||
|
||||
struct krb5_ctx;
|
||||
|
||||
struct gss_krb5_enctype {
|
||||
const u32 etype; /* encryption (key) type */
|
||||
const u32 ctype; /* checksum type */
|
||||
const char *name; /* "friendly" name */
|
||||
const char *encrypt_name; /* crypto encrypt name */
|
||||
const char *aux_cipher; /* aux encrypt cipher name */
|
||||
const char *cksum_name; /* crypto checksum name */
|
||||
const u16 signalg; /* signing algorithm */
|
||||
const u16 sealalg; /* sealing algorithm */
|
||||
const u32 cksumlength; /* checksum length */
|
||||
const u32 keyed_cksum; /* is it a keyed cksum? */
|
||||
const u32 keybytes; /* raw key len, in bytes */
|
||||
const u32 keylength; /* protocol key length, in octets */
|
||||
const u32 Kc_length; /* checksum subkey length, in octets */
|
||||
const u32 Ke_length; /* encryption subkey length, in octets */
|
||||
const u32 Ki_length; /* integrity subkey length, in octets */
|
||||
|
||||
int (*derive_key)(const struct gss_krb5_enctype *gk5e,
|
||||
const struct xdr_netobj *in,
|
||||
struct xdr_netobj *out,
|
||||
const struct xdr_netobj *label,
|
||||
gfp_t gfp_mask);
|
||||
};
|
||||
|
||||
/* krb5_ctx flags definitions */
|
||||
#define KRB5_CTX_FLAG_INITIATOR 0x00000001
|
||||
#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
|
||||
@@ -50,7 +20,6 @@ struct krb5_ctx {
|
||||
int initiate; /* 1 = initiating, 0 = accepting */
|
||||
u32 enctype;
|
||||
u32 flags;
|
||||
const struct gss_krb5_enctype *gk5e; /* enctype-specific info */
|
||||
const struct krb5_enctype *krb5e; /* crypto/krb5 enctype */
|
||||
struct crypto_aead *initiator_enc_aead;
|
||||
struct crypto_aead *acceptor_enc_aead;
|
||||
@@ -58,7 +27,6 @@ struct krb5_ctx {
|
||||
struct crypto_shash *acceptor_sign_shash;
|
||||
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;
|
||||
@@ -85,35 +53,6 @@ u32 gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
|
||||
* Implementation internal functions
|
||||
*/
|
||||
|
||||
/* Key Derivation Functions */
|
||||
|
||||
int krb5_derive_key_v2(const struct gss_krb5_enctype *gk5e,
|
||||
const struct xdr_netobj *inkey,
|
||||
struct xdr_netobj *outkey,
|
||||
const struct xdr_netobj *label,
|
||||
gfp_t gfp_mask);
|
||||
|
||||
int krb5_kdf_hmac_sha2(const struct gss_krb5_enctype *gk5e,
|
||||
const struct xdr_netobj *inkey,
|
||||
struct xdr_netobj *outkey,
|
||||
const struct xdr_netobj *in_constant,
|
||||
gfp_t gfp_mask);
|
||||
|
||||
int krb5_kdf_feedback_cmac(const struct gss_krb5_enctype *gk5e,
|
||||
const struct xdr_netobj *inkey,
|
||||
struct xdr_netobj *outkey,
|
||||
const struct xdr_netobj *in_constant,
|
||||
gfp_t gfp_mask);
|
||||
|
||||
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);
|
||||
|
||||
u32 krb5_encrypt(struct crypto_sync_skcipher *key, void *iv, void *in,
|
||||
void *out, int length);
|
||||
|
||||
int xdr_extend_head(struct xdr_buf *buf, unsigned int base,
|
||||
unsigned int shiftlen);
|
||||
|
||||
@@ -130,19 +69,5 @@ u32 gss_krb5_aead_encrypt(struct krb5_ctx *kctx, u32 offset,
|
||||
u32 gss_krb5_aead_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
|
||||
struct xdr_buf *buf, u32 *headskip, u32 *tailskip);
|
||||
|
||||
#if IS_ENABLED(CONFIG_KUNIT)
|
||||
void krb5_nfold(u32 inbits, const u8 *in, u32 outbits, u8 *out);
|
||||
const struct gss_krb5_enctype *gss_krb5_lookup_enctype(u32 etype);
|
||||
int krb5_cbc_cts_encrypt(struct crypto_sync_skcipher *cts_tfm,
|
||||
struct crypto_sync_skcipher *cbc_tfm, u32 offset,
|
||||
struct xdr_buf *buf, struct page **pages,
|
||||
u8 *iv, unsigned int ivsize);
|
||||
int krb5_cbc_cts_decrypt(struct crypto_sync_skcipher *cts_tfm,
|
||||
struct crypto_sync_skcipher *cbc_tfm,
|
||||
u32 offset, struct xdr_buf *buf);
|
||||
u32 krb5_etm_checksum(struct crypto_sync_skcipher *cipher,
|
||||
struct crypto_ahash *tfm, const struct xdr_buf *body,
|
||||
int body_offset, struct xdr_netobj *cksumout);
|
||||
#endif
|
||||
|
||||
#endif /* _NET_SUNRPC_AUTH_GSS_KRB5_INTERNAL_H */
|
||||
|
||||
@@ -1,546 +0,0 @@
|
||||
/*
|
||||
* COPYRIGHT (c) 2008
|
||||
* The Regents of the University of Michigan
|
||||
* ALL RIGHTS RESERVED
|
||||
*
|
||||
* Permission is granted to use, copy, create derivative works
|
||||
* and redistribute this software and such derivative works
|
||||
* for any purpose, so long as the name of The University of
|
||||
* Michigan is not used in any advertising or publicity
|
||||
* pertaining to the use of distribution of this software
|
||||
* without specific, written prior authorization. If the
|
||||
* above copyright notice or any other identification of the
|
||||
* University of Michigan is included in any copy of any
|
||||
* portion of this software, then the disclaimer below must
|
||||
* also be included.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
|
||||
* FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
|
||||
* PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
|
||||
* MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
|
||||
* REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
|
||||
* FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
|
||||
* CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
|
||||
* IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGES.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 1998 by the FundsXpress, INC.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Export of this software from the United States of America may require
|
||||
* a specific license from the United States Government. It is the
|
||||
* responsibility of any person or organization contemplating export to
|
||||
* obtain such a license before exporting.
|
||||
*
|
||||
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
|
||||
* distribute this software and its documentation for any purpose and
|
||||
* without fee is hereby granted, provided that the above copyright
|
||||
* notice appear in all copies and that both that copyright notice and
|
||||
* this permission notice appear in supporting documentation, and that
|
||||
* the name of FundsXpress. not be used in advertising or publicity pertaining
|
||||
* to distribution of the software without specific, written prior
|
||||
* permission. FundsXpress makes no representations about the suitability of
|
||||
* this software for any purpose. It is provided "as is" without express
|
||||
* or implied warranty.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#include <crypto/skcipher.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/sunrpc/gss_krb5.h>
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
#include <linux/lcm.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <kunit/visibility.h>
|
||||
|
||||
#include "gss_krb5_internal.h"
|
||||
|
||||
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
/**
|
||||
* krb5_nfold - n-fold function
|
||||
* @inbits: number of bits in @in
|
||||
* @in: buffer containing input to fold
|
||||
* @outbits: number of bits in the output buffer
|
||||
* @out: buffer to hold the result
|
||||
*
|
||||
* This is the n-fold function as described in rfc3961, sec 5.1
|
||||
* Taken from MIT Kerberos and modified.
|
||||
*/
|
||||
VISIBLE_IF_KUNIT
|
||||
void krb5_nfold(u32 inbits, const u8 *in, u32 outbits, u8 *out)
|
||||
{
|
||||
unsigned long ulcm;
|
||||
int byte, i, msbit;
|
||||
|
||||
/* the code below is more readable if I make these bytes
|
||||
instead of bits */
|
||||
|
||||
inbits >>= 3;
|
||||
outbits >>= 3;
|
||||
|
||||
/* first compute lcm(n,k) */
|
||||
ulcm = lcm(inbits, outbits);
|
||||
|
||||
/* now do the real work */
|
||||
|
||||
memset(out, 0, outbits);
|
||||
byte = 0;
|
||||
|
||||
/* this will end up cycling through k lcm(k,n)/k times, which
|
||||
is correct */
|
||||
for (i = ulcm-1; i >= 0; i--) {
|
||||
/* compute the msbit in k which gets added into this byte */
|
||||
msbit = (
|
||||
/* first, start with the msbit in the first,
|
||||
* unrotated byte */
|
||||
((inbits << 3) - 1)
|
||||
/* then, for each byte, shift to the right
|
||||
* for each repetition */
|
||||
+ (((inbits << 3) + 13) * (i/inbits))
|
||||
/* last, pick out the correct byte within
|
||||
* that shifted repetition */
|
||||
+ ((inbits - (i % inbits)) << 3)
|
||||
) % (inbits << 3);
|
||||
|
||||
/* pull out the byte value itself */
|
||||
byte += (((in[((inbits - 1) - (msbit >> 3)) % inbits] << 8)|
|
||||
(in[((inbits) - (msbit >> 3)) % inbits]))
|
||||
>> ((msbit & 7) + 1)) & 0xff;
|
||||
|
||||
/* do the addition */
|
||||
byte += out[i % outbits];
|
||||
out[i % outbits] = byte & 0xff;
|
||||
|
||||
/* keep around the carry bit, if any */
|
||||
byte >>= 8;
|
||||
|
||||
}
|
||||
|
||||
/* if there's a carry bit left over, add it back in */
|
||||
if (byte) {
|
||||
for (i = outbits - 1; i >= 0; i--) {
|
||||
/* do the addition */
|
||||
byte += out[i];
|
||||
out[i] = byte & 0xff;
|
||||
|
||||
/* keep around the carry bit, if any */
|
||||
byte >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(krb5_nfold);
|
||||
|
||||
/*
|
||||
* This is the DK (derive_key) function as described in rfc3961, sec 5.1
|
||||
* Taken from MIT Kerberos and modified.
|
||||
*/
|
||||
static int krb5_DK(const struct gss_krb5_enctype *gk5e,
|
||||
const struct xdr_netobj *inkey, u8 *rawkey,
|
||||
const struct xdr_netobj *in_constant, gfp_t gfp_mask)
|
||||
{
|
||||
size_t blocksize, keybytes, keylength, n;
|
||||
unsigned char *inblockdata, *outblockdata;
|
||||
struct xdr_netobj inblock, outblock;
|
||||
struct crypto_sync_skcipher *cipher;
|
||||
int ret = -EINVAL;
|
||||
|
||||
keybytes = gk5e->keybytes;
|
||||
keylength = gk5e->keylength;
|
||||
|
||||
if (inkey->len != keylength)
|
||||
goto err_return;
|
||||
|
||||
cipher = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0);
|
||||
if (IS_ERR(cipher))
|
||||
goto err_return;
|
||||
blocksize = crypto_sync_skcipher_blocksize(cipher);
|
||||
if (crypto_sync_skcipher_setkey(cipher, inkey->data, inkey->len))
|
||||
goto err_free_cipher;
|
||||
|
||||
ret = -ENOMEM;
|
||||
inblockdata = kmalloc(blocksize, gfp_mask);
|
||||
if (inblockdata == NULL)
|
||||
goto err_free_cipher;
|
||||
|
||||
outblockdata = kmalloc(blocksize, gfp_mask);
|
||||
if (outblockdata == NULL)
|
||||
goto err_free_in;
|
||||
|
||||
inblock.data = (char *) inblockdata;
|
||||
inblock.len = blocksize;
|
||||
|
||||
outblock.data = (char *) outblockdata;
|
||||
outblock.len = blocksize;
|
||||
|
||||
/* initialize the input block */
|
||||
|
||||
if (in_constant->len == inblock.len) {
|
||||
memcpy(inblock.data, in_constant->data, inblock.len);
|
||||
} else {
|
||||
krb5_nfold(in_constant->len * 8, in_constant->data,
|
||||
inblock.len * 8, inblock.data);
|
||||
}
|
||||
|
||||
/* loop encrypting the blocks until enough key bytes are generated */
|
||||
|
||||
n = 0;
|
||||
while (n < keybytes) {
|
||||
krb5_encrypt(cipher, NULL, inblock.data, outblock.data,
|
||||
inblock.len);
|
||||
|
||||
if ((keybytes - n) <= outblock.len) {
|
||||
memcpy(rawkey + n, outblock.data, (keybytes - n));
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(rawkey + n, outblock.data, outblock.len);
|
||||
memcpy(inblock.data, outblock.data, outblock.len);
|
||||
n += outblock.len;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
kfree_sensitive(outblockdata);
|
||||
err_free_in:
|
||||
kfree_sensitive(inblockdata);
|
||||
err_free_cipher:
|
||||
crypto_free_sync_skcipher(cipher);
|
||||
err_return:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the identity function, with some sanity checking.
|
||||
*/
|
||||
static int krb5_random_to_key_v2(const struct gss_krb5_enctype *gk5e,
|
||||
struct xdr_netobj *randombits,
|
||||
struct xdr_netobj *key)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (key->len != 16 && key->len != 32) {
|
||||
dprintk("%s: key->len is %d\n", __func__, key->len);
|
||||
goto err_out;
|
||||
}
|
||||
if (randombits->len != 16 && randombits->len != 32) {
|
||||
dprintk("%s: randombits->len is %d\n",
|
||||
__func__, randombits->len);
|
||||
goto err_out;
|
||||
}
|
||||
if (randombits->len != key->len) {
|
||||
dprintk("%s: randombits->len is %d, key->len is %d\n",
|
||||
__func__, randombits->len, key->len);
|
||||
goto err_out;
|
||||
}
|
||||
memcpy(key->data, randombits->data, key->len);
|
||||
ret = 0;
|
||||
err_out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* krb5_derive_key_v2 - Derive a subkey for an RFC 3962 enctype
|
||||
* @gk5e: Kerberos 5 enctype profile
|
||||
* @inkey: base protocol key
|
||||
* @outkey: OUT: derived key
|
||||
* @label: subkey usage label
|
||||
* @gfp_mask: memory allocation control flags
|
||||
*
|
||||
* Caller sets @outkey->len to the desired length of the derived key.
|
||||
*
|
||||
* On success, returns 0 and fills in @outkey. A negative errno value
|
||||
* is returned on failure.
|
||||
*/
|
||||
int krb5_derive_key_v2(const struct gss_krb5_enctype *gk5e,
|
||||
const struct xdr_netobj *inkey,
|
||||
struct xdr_netobj *outkey,
|
||||
const struct xdr_netobj *label,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
struct xdr_netobj inblock;
|
||||
int ret;
|
||||
|
||||
inblock.len = gk5e->keybytes;
|
||||
inblock.data = kmalloc(inblock.len, gfp_mask);
|
||||
if (!inblock.data)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = krb5_DK(gk5e, inkey, inblock.data, label, gfp_mask);
|
||||
if (!ret)
|
||||
ret = krb5_random_to_key_v2(gk5e, &inblock, outkey);
|
||||
|
||||
kfree_sensitive(inblock.data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* K(i) = CMAC(key, K(i-1) | i | constant | 0x00 | k)
|
||||
*
|
||||
* i: A block counter is used with a length of 4 bytes, represented
|
||||
* in big-endian order.
|
||||
*
|
||||
* constant: The label input to the KDF is the usage constant supplied
|
||||
* to the key derivation function
|
||||
*
|
||||
* k: The length of the output key in bits, represented as a 4-byte
|
||||
* string in big-endian order.
|
||||
*
|
||||
* Caller fills in K(i-1) in @step, and receives the result K(i)
|
||||
* in the same buffer.
|
||||
*/
|
||||
static int
|
||||
krb5_cmac_Ki(struct crypto_shash *tfm, const struct xdr_netobj *constant,
|
||||
u32 outlen, u32 count, struct xdr_netobj *step)
|
||||
{
|
||||
__be32 k = cpu_to_be32(outlen * 8);
|
||||
SHASH_DESC_ON_STACK(desc, tfm);
|
||||
__be32 i = cpu_to_be32(count);
|
||||
u8 zero = 0;
|
||||
int ret;
|
||||
|
||||
desc->tfm = tfm;
|
||||
ret = crypto_shash_init(desc);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
ret = crypto_shash_update(desc, step->data, step->len);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = crypto_shash_update(desc, (u8 *)&i, sizeof(i));
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = crypto_shash_update(desc, constant->data, constant->len);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = crypto_shash_update(desc, &zero, sizeof(zero));
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = crypto_shash_update(desc, (u8 *)&k, sizeof(k));
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = crypto_shash_final(desc, step->data);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
out_err:
|
||||
shash_desc_zero(desc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* krb5_kdf_feedback_cmac - Derive a subkey for a Camellia/CMAC-based enctype
|
||||
* @gk5e: Kerberos 5 enctype parameters
|
||||
* @inkey: base protocol key
|
||||
* @outkey: OUT: derived key
|
||||
* @constant: subkey usage label
|
||||
* @gfp_mask: memory allocation control flags
|
||||
*
|
||||
* RFC 6803 Section 3:
|
||||
*
|
||||
* "We use a key derivation function from the family specified in
|
||||
* [SP800-108], Section 5.2, 'KDF in Feedback Mode'."
|
||||
*
|
||||
* n = ceiling(k / 128)
|
||||
* K(0) = zeros
|
||||
* K(i) = CMAC(key, K(i-1) | i | constant | 0x00 | k)
|
||||
* DR(key, constant) = k-truncate(K(1) | K(2) | ... | K(n))
|
||||
* KDF-FEEDBACK-CMAC(key, constant) = random-to-key(DR(key, constant))
|
||||
*
|
||||
* Caller sets @outkey->len to the desired length of the derived key (k).
|
||||
*
|
||||
* On success, returns 0 and fills in @outkey. A negative errno value
|
||||
* is returned on failure.
|
||||
*/
|
||||
int
|
||||
krb5_kdf_feedback_cmac(const struct gss_krb5_enctype *gk5e,
|
||||
const struct xdr_netobj *inkey,
|
||||
struct xdr_netobj *outkey,
|
||||
const struct xdr_netobj *constant,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
struct xdr_netobj step = { .data = NULL };
|
||||
struct xdr_netobj DR = { .data = NULL };
|
||||
unsigned int blocksize, offset;
|
||||
struct crypto_shash *tfm;
|
||||
int n, count, ret;
|
||||
|
||||
/*
|
||||
* This implementation assumes the CMAC used for an enctype's
|
||||
* key derivation is the same as the CMAC used for its
|
||||
* checksumming. This happens to be true for enctypes that
|
||||
* are currently supported by this implementation.
|
||||
*/
|
||||
tfm = crypto_alloc_shash(gk5e->cksum_name, 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
ret = PTR_ERR(tfm);
|
||||
goto out;
|
||||
}
|
||||
ret = crypto_shash_setkey(tfm, inkey->data, inkey->len);
|
||||
if (ret)
|
||||
goto out_free_tfm;
|
||||
|
||||
blocksize = crypto_shash_digestsize(tfm);
|
||||
n = (outkey->len + blocksize - 1) / blocksize;
|
||||
|
||||
/* K(0) is all zeroes */
|
||||
ret = -ENOMEM;
|
||||
step.len = blocksize;
|
||||
step.data = kzalloc(step.len, gfp_mask);
|
||||
if (!step.data)
|
||||
goto out_free_tfm;
|
||||
|
||||
DR.len = blocksize * n;
|
||||
DR.data = kmalloc(DR.len, gfp_mask);
|
||||
if (!DR.data)
|
||||
goto out_free_tfm;
|
||||
|
||||
/* XXX: Does not handle partial-block key sizes */
|
||||
for (offset = 0, count = 1; count <= n; count++) {
|
||||
ret = krb5_cmac_Ki(tfm, constant, outkey->len, count, &step);
|
||||
if (ret)
|
||||
goto out_free_tfm;
|
||||
|
||||
memcpy(DR.data + offset, step.data, blocksize);
|
||||
offset += blocksize;
|
||||
}
|
||||
|
||||
/* k-truncate and random-to-key */
|
||||
memcpy(outkey->data, DR.data, outkey->len);
|
||||
ret = 0;
|
||||
|
||||
out_free_tfm:
|
||||
crypto_free_shash(tfm);
|
||||
out:
|
||||
kfree_sensitive(step.data);
|
||||
kfree_sensitive(DR.data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* K1 = HMAC-SHA(key, 0x00000001 | label | 0x00 | k)
|
||||
*
|
||||
* key: The source of entropy from which subsequent keys are derived.
|
||||
*
|
||||
* label: An octet string describing the intended usage of the
|
||||
* derived key.
|
||||
*
|
||||
* k: Length in bits of the key to be outputted, expressed in
|
||||
* big-endian binary representation in 4 bytes.
|
||||
*/
|
||||
static int
|
||||
krb5_hmac_K1(struct crypto_shash *tfm, const struct xdr_netobj *label,
|
||||
u32 outlen, struct xdr_netobj *K1)
|
||||
{
|
||||
__be32 k = cpu_to_be32(outlen * 8);
|
||||
SHASH_DESC_ON_STACK(desc, tfm);
|
||||
__be32 one = cpu_to_be32(1);
|
||||
u8 zero = 0;
|
||||
int ret;
|
||||
|
||||
desc->tfm = tfm;
|
||||
ret = crypto_shash_init(desc);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = crypto_shash_update(desc, (u8 *)&one, sizeof(one));
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = crypto_shash_update(desc, label->data, label->len);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = crypto_shash_update(desc, &zero, sizeof(zero));
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = crypto_shash_update(desc, (u8 *)&k, sizeof(k));
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = crypto_shash_final(desc, K1->data);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
out_err:
|
||||
shash_desc_zero(desc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* krb5_kdf_hmac_sha2 - Derive a subkey for an AES/SHA2-based enctype
|
||||
* @gk5e: Kerberos 5 enctype policy parameters
|
||||
* @inkey: base protocol key
|
||||
* @outkey: OUT: derived key
|
||||
* @label: subkey usage label
|
||||
* @gfp_mask: memory allocation control flags
|
||||
*
|
||||
* RFC 8009 Section 3:
|
||||
*
|
||||
* "We use a key derivation function from Section 5.1 of [SP800-108],
|
||||
* which uses the HMAC algorithm as the PRF."
|
||||
*
|
||||
* function KDF-HMAC-SHA2(key, label, [context,] k):
|
||||
* k-truncate(K1)
|
||||
*
|
||||
* Caller sets @outkey->len to the desired length of the derived key.
|
||||
*
|
||||
* On success, returns 0 and fills in @outkey. A negative errno value
|
||||
* is returned on failure.
|
||||
*/
|
||||
int
|
||||
krb5_kdf_hmac_sha2(const struct gss_krb5_enctype *gk5e,
|
||||
const struct xdr_netobj *inkey,
|
||||
struct xdr_netobj *outkey,
|
||||
const struct xdr_netobj *label,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
struct crypto_shash *tfm;
|
||||
struct xdr_netobj K1 = {
|
||||
.data = NULL,
|
||||
};
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* This implementation assumes the HMAC used for an enctype's
|
||||
* key derivation is the same as the HMAC used for its
|
||||
* checksumming. This happens to be true for enctypes that
|
||||
* are currently supported by this implementation.
|
||||
*/
|
||||
tfm = crypto_alloc_shash(gk5e->cksum_name, 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
ret = PTR_ERR(tfm);
|
||||
goto out;
|
||||
}
|
||||
ret = crypto_shash_setkey(tfm, inkey->data, inkey->len);
|
||||
if (ret)
|
||||
goto out_free_tfm;
|
||||
|
||||
K1.len = crypto_shash_digestsize(tfm);
|
||||
K1.data = kmalloc(K1.len, gfp_mask);
|
||||
if (!K1.data) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_tfm;
|
||||
}
|
||||
|
||||
ret = krb5_hmac_K1(tfm, label, outkey->len, &K1);
|
||||
if (ret)
|
||||
goto out_free_tfm;
|
||||
|
||||
/* k-truncate and random-to-key */
|
||||
memcpy(outkey->data, K1.data, outkey->len);
|
||||
|
||||
out_free_tfm:
|
||||
kfree_sensitive(K1.data);
|
||||
crypto_free_shash(tfm);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <linux/sunrpc/auth.h>
|
||||
#include <linux/sunrpc/gss_krb5.h>
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
#include <kunit/visibility.h>
|
||||
|
||||
#include "auth_gss_internal.h"
|
||||
#include "gss_krb5_internal.h"
|
||||
@@ -28,141 +27,6 @@
|
||||
|
||||
static struct gss_api_mech gss_kerberos_mech;
|
||||
|
||||
static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = {
|
||||
#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1)
|
||||
/*
|
||||
* AES-128 with SHA-1 (RFC 3962)
|
||||
*/
|
||||
{
|
||||
.etype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
|
||||
.ctype = CKSUMTYPE_HMAC_SHA1_96_AES128,
|
||||
.name = "aes128-cts",
|
||||
.encrypt_name = "cts(cbc(aes))",
|
||||
.aux_cipher = "cbc(aes)",
|
||||
.cksum_name = "hmac(sha1)",
|
||||
.derive_key = krb5_derive_key_v2,
|
||||
|
||||
.signalg = -1,
|
||||
.sealalg = -1,
|
||||
.keybytes = 16,
|
||||
.keylength = BITS2OCTETS(128),
|
||||
.Kc_length = BITS2OCTETS(128),
|
||||
.Ke_length = BITS2OCTETS(128),
|
||||
.Ki_length = BITS2OCTETS(128),
|
||||
.cksumlength = BITS2OCTETS(96),
|
||||
.keyed_cksum = 1,
|
||||
},
|
||||
/*
|
||||
* AES-256 with SHA-1 (RFC 3962)
|
||||
*/
|
||||
{
|
||||
.etype = ENCTYPE_AES256_CTS_HMAC_SHA1_96,
|
||||
.ctype = CKSUMTYPE_HMAC_SHA1_96_AES256,
|
||||
.name = "aes256-cts",
|
||||
.encrypt_name = "cts(cbc(aes))",
|
||||
.aux_cipher = "cbc(aes)",
|
||||
.cksum_name = "hmac(sha1)",
|
||||
.derive_key = krb5_derive_key_v2,
|
||||
|
||||
.signalg = -1,
|
||||
.sealalg = -1,
|
||||
.keybytes = 32,
|
||||
.keylength = BITS2OCTETS(256),
|
||||
.Kc_length = BITS2OCTETS(256),
|
||||
.Ke_length = BITS2OCTETS(256),
|
||||
.Ki_length = BITS2OCTETS(256),
|
||||
.cksumlength = BITS2OCTETS(96),
|
||||
.keyed_cksum = 1,
|
||||
},
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA)
|
||||
/*
|
||||
* Camellia-128 with CMAC (RFC 6803)
|
||||
*/
|
||||
{
|
||||
.etype = ENCTYPE_CAMELLIA128_CTS_CMAC,
|
||||
.ctype = CKSUMTYPE_CMAC_CAMELLIA128,
|
||||
.name = "camellia128-cts-cmac",
|
||||
.encrypt_name = "cts(cbc(camellia))",
|
||||
.aux_cipher = "cbc(camellia)",
|
||||
.cksum_name = "cmac(camellia)",
|
||||
.cksumlength = BITS2OCTETS(128),
|
||||
.keyed_cksum = 1,
|
||||
.keylength = BITS2OCTETS(128),
|
||||
.Kc_length = BITS2OCTETS(128),
|
||||
.Ke_length = BITS2OCTETS(128),
|
||||
.Ki_length = BITS2OCTETS(128),
|
||||
|
||||
.derive_key = krb5_kdf_feedback_cmac,
|
||||
|
||||
},
|
||||
/*
|
||||
* Camellia-256 with CMAC (RFC 6803)
|
||||
*/
|
||||
{
|
||||
.etype = ENCTYPE_CAMELLIA256_CTS_CMAC,
|
||||
.ctype = CKSUMTYPE_CMAC_CAMELLIA256,
|
||||
.name = "camellia256-cts-cmac",
|
||||
.encrypt_name = "cts(cbc(camellia))",
|
||||
.aux_cipher = "cbc(camellia)",
|
||||
.cksum_name = "cmac(camellia)",
|
||||
.cksumlength = BITS2OCTETS(128),
|
||||
.keyed_cksum = 1,
|
||||
.keylength = BITS2OCTETS(256),
|
||||
.Kc_length = BITS2OCTETS(256),
|
||||
.Ke_length = BITS2OCTETS(256),
|
||||
.Ki_length = BITS2OCTETS(256),
|
||||
|
||||
.derive_key = krb5_kdf_feedback_cmac,
|
||||
|
||||
},
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2)
|
||||
/*
|
||||
* AES-128 with SHA-256 (RFC 8009)
|
||||
*/
|
||||
{
|
||||
.etype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
|
||||
.ctype = CKSUMTYPE_HMAC_SHA256_128_AES128,
|
||||
.name = "aes128-cts-hmac-sha256-128",
|
||||
.encrypt_name = "cts(cbc(aes))",
|
||||
.aux_cipher = "cbc(aes)",
|
||||
.cksum_name = "hmac(sha256)",
|
||||
.cksumlength = BITS2OCTETS(128),
|
||||
.keyed_cksum = 1,
|
||||
.keylength = BITS2OCTETS(128),
|
||||
.Kc_length = BITS2OCTETS(128),
|
||||
.Ke_length = BITS2OCTETS(128),
|
||||
.Ki_length = BITS2OCTETS(128),
|
||||
|
||||
.derive_key = krb5_kdf_hmac_sha2,
|
||||
|
||||
},
|
||||
/*
|
||||
* AES-256 with SHA-384 (RFC 8009)
|
||||
*/
|
||||
{
|
||||
.etype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
|
||||
.ctype = CKSUMTYPE_HMAC_SHA384_192_AES256,
|
||||
.name = "aes256-cts-hmac-sha384-192",
|
||||
.encrypt_name = "cts(cbc(aes))",
|
||||
.aux_cipher = "cbc(aes)",
|
||||
.cksum_name = "hmac(sha384)",
|
||||
.cksumlength = BITS2OCTETS(192),
|
||||
.keyed_cksum = 1,
|
||||
.keylength = BITS2OCTETS(256),
|
||||
.Kc_length = BITS2OCTETS(192),
|
||||
.Ke_length = BITS2OCTETS(256),
|
||||
.Ki_length = BITS2OCTETS(192),
|
||||
|
||||
.derive_key = krb5_kdf_hmac_sha2,
|
||||
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* The list of advertised enctypes is specified in order of most
|
||||
* preferred to least.
|
||||
@@ -204,30 +68,11 @@ static void gss_krb5_prepare_enctype_priority_list(void)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gss_krb5_lookup_enctype - Retrieve profile information for a given enctype
|
||||
* @etype: ENCTYPE value
|
||||
*
|
||||
* Returns a pointer to a gss_krb5_enctype structure, or NULL if no
|
||||
* matching etype is found.
|
||||
*/
|
||||
VISIBLE_IF_KUNIT
|
||||
const struct gss_krb5_enctype *gss_krb5_lookup_enctype(u32 etype)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(supported_gss_krb5_enctypes); i++)
|
||||
if (supported_gss_krb5_enctypes[i].etype == etype)
|
||||
return &supported_gss_krb5_enctypes[i];
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(gss_krb5_lookup_enctype);
|
||||
|
||||
static int
|
||||
gss_krb5_import_ctx_v2(struct krb5_ctx *ctx, gfp_t gfp_mask)
|
||||
{
|
||||
struct krb5_buffer TK = {
|
||||
.len = ctx->gk5e->keylength,
|
||||
.len = ctx->krb5e->key_len,
|
||||
.data = ctx->Ksess,
|
||||
};
|
||||
int ret;
|
||||
@@ -298,32 +143,17 @@ gss_import_v2_context(const void *p, const void *end, struct krb5_ctx *ctx,
|
||||
if (IS_ERR(p))
|
||||
goto out_err;
|
||||
atomic64_set(&ctx->seq_send64, seq_send64);
|
||||
/* set seq_send for use by "older" enctypes */
|
||||
atomic_set(&ctx->seq_send, seq_send64);
|
||||
if (seq_send64 != atomic_read(&ctx->seq_send)) {
|
||||
dprintk("%s: seq_send64 %llx, seq_send %x overflow?\n", __func__,
|
||||
seq_send64, atomic_read(&ctx->seq_send));
|
||||
p = ERR_PTR(-EINVAL);
|
||||
goto out_err;
|
||||
}
|
||||
p = simple_get_bytes(p, end, &ctx->enctype, sizeof(ctx->enctype));
|
||||
if (IS_ERR(p))
|
||||
goto out_err;
|
||||
ctx->gk5e = gss_krb5_lookup_enctype(ctx->enctype);
|
||||
if (ctx->gk5e == NULL) {
|
||||
ctx->krb5e = crypto_krb5_find_enctype(ctx->enctype);
|
||||
if (!ctx->krb5e) {
|
||||
dprintk("gss_kerberos_mech: unsupported krb5 enctype %u\n",
|
||||
ctx->enctype);
|
||||
p = ERR_PTR(-EINVAL);
|
||||
goto out_err;
|
||||
}
|
||||
ctx->krb5e = crypto_krb5_find_enctype(ctx->enctype);
|
||||
if (!ctx->krb5e) {
|
||||
dprintk("gss_kerberos_mech: crypto/krb5 missing enctype %u\n",
|
||||
ctx->enctype);
|
||||
p = ERR_PTR(-EINVAL);
|
||||
goto out_err;
|
||||
}
|
||||
keylen = ctx->gk5e->keylength;
|
||||
keylen = ctx->krb5e->key_len;
|
||||
|
||||
p = simple_get_bytes(p, end, ctx->Ksess, keylen);
|
||||
if (IS_ERR(p))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2348,73 +2348,6 @@ int xdr_encode_array2(const struct xdr_buf *buf, unsigned int base,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xdr_encode_array2);
|
||||
|
||||
int xdr_process_buf(const struct xdr_buf *buf, unsigned int offset,
|
||||
unsigned int len,
|
||||
int (*actor)(struct scatterlist *, void *), void *data)
|
||||
{
|
||||
int i, ret = 0;
|
||||
unsigned int page_len, thislen, page_offset;
|
||||
struct scatterlist sg[1];
|
||||
|
||||
sg_init_table(sg, 1);
|
||||
|
||||
if (offset >= buf->head[0].iov_len) {
|
||||
offset -= buf->head[0].iov_len;
|
||||
} else {
|
||||
thislen = buf->head[0].iov_len - offset;
|
||||
if (thislen > len)
|
||||
thislen = len;
|
||||
sg_set_buf(sg, buf->head[0].iov_base + offset, thislen);
|
||||
ret = actor(sg, data);
|
||||
if (ret)
|
||||
goto out;
|
||||
offset = 0;
|
||||
len -= thislen;
|
||||
}
|
||||
if (len == 0)
|
||||
goto out;
|
||||
|
||||
if (offset >= buf->page_len) {
|
||||
offset -= buf->page_len;
|
||||
} else {
|
||||
page_len = buf->page_len - offset;
|
||||
if (page_len > len)
|
||||
page_len = len;
|
||||
len -= page_len;
|
||||
page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1);
|
||||
i = (offset + buf->page_base) >> PAGE_SHIFT;
|
||||
thislen = PAGE_SIZE - page_offset;
|
||||
do {
|
||||
if (thislen > page_len)
|
||||
thislen = page_len;
|
||||
sg_set_page(sg, buf->pages[i], thislen, page_offset);
|
||||
ret = actor(sg, data);
|
||||
if (ret)
|
||||
goto out;
|
||||
page_len -= thislen;
|
||||
i++;
|
||||
page_offset = 0;
|
||||
thislen = PAGE_SIZE;
|
||||
} while (page_len != 0);
|
||||
offset = 0;
|
||||
}
|
||||
if (len == 0)
|
||||
goto out;
|
||||
if (offset < buf->tail[0].iov_len) {
|
||||
thislen = buf->tail[0].iov_len - offset;
|
||||
if (thislen > len)
|
||||
thislen = len;
|
||||
sg_set_buf(sg, buf->tail[0].iov_base + offset, thislen);
|
||||
ret = actor(sg, data);
|
||||
len -= thislen;
|
||||
}
|
||||
if (len != 0)
|
||||
ret = -EINVAL;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xdr_process_buf);
|
||||
|
||||
/**
|
||||
* xdr_stream_decode_string_dup - Decode and duplicate variable length string
|
||||
* @xdr: pointer to xdr_stream
|
||||
|
||||
Reference in New Issue
Block a user