summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-06-18 09:14:15 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-06-18 09:14:15 -0700
commit3dc7c001169d112b3e514cacff6c93091c57af9a (patch)
treebc3afccf5791c512b2e3965b4435319ba1e04324 /net
parent6edc20078ad0b05ab2dc2693965d373628d65f80 (diff)
parente5248a7426030db1e126363f72afdb3b71339a5c (diff)
Merge tag 'nfsd-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux
Pull nfsd updates from Chuck Lever: "Jeff Layton wired up netlink upcalls for the auth.unix.ip and auth.unix.gid caches in SunRPC and the svc_export and nfsd.fh caches in NFSD. The new kernel-user API is more extensible and lays the groundwork for retiring the old pipe interface. The default NFS r/w block size rises to 4MB on hosts with at least 16GB of RAM, reducing per-RPC overhead on fast networks. Smaller machines keep their previously computed default, and the value remains tunable through /proc/fs/nfsd/max_block_size. Chuck Lever converted the server's RPCSEC GSS Kerberos code to the kernel's shared crypto/krb5 library. The conversion retires and removes SunRPC's bespoke implementation of Kerberos v5, but keeps RPCSEC GSS-API. Continuing the xdrgen migration that converted the NLMv4 server XDR layer in v7.1, Chuck Lever converted the NLM version 3 server-side XDR layer from hand-written C to xdrgen-generated code. As with the NLMv4 conversion in v7.1, the goals are improved memory safety, lower maintenance burden, and groundwork for generation of Rust code for this layer instead of C. Chuck Lever fixed an issue where lingering NFSv4 state pins a mounted file system after it is unexported. A new netlink-based mechanism can now release NLM locks and NFSv4 state by client address, by filesystem, and by export. Now an administrator can quiesce an export cleanly before unmounting it. The remaining patches are bug fixes, clean-ups, and minor optimizations, including a batch of memory-leak and use-after-free fixes in the ACL, lockd, and TLS handshake paths, many of them reported by Chris Mason. Sincere thanks to all contributors, reviewers, testers, and bug reporters who participated in the v7.2 NFSD development cycle" * tag 'nfsd-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (106 commits) svcrdma: wake sq waiters when the transport closes nfsd: reset write verifier on deferred writeback errors nfsd: avoid leaking pre-allocated openowner on unconfirmed retry race sunrpc: wait for in-flight TLS handshake callback when cancel loses race sunrpc: pin svc_xprt across the asynchronous TLS handshake callback nfsd: fix posix_acl leak on SETACL decode failure nfsd: fix posix_acl leak and ignored error in nfsd4_create_file nfsd: check get_user() return when reading princhashlen nfsd: fix inverted cp_ttl check in async copy reaper nfsd: fix dead ACL conflict guard in nfsd4_create NFSD: Fix SECINFO_NO_NAME decode error cleanup sunrpc: harden rq_procinfo lifecycle to prevent double-free SUNRPC: Return an error from xdr_buf_to_bvec() on overflow SUNRPC: Bound-check xdr_buf_to_bvec() stores before writing nfsd: release layout stid on setlease failure lockd: Avoid hashing uninitialized bytes in nlm4svc_lookup_file() lockd: Plug nlm_file refcount leak on cached nlm_do_fopen() failure lockd: Plug nlm_file leak when nlm_do_fopen() fails Revert "NFSD: Defer sub-object cleanup in export put callbacks" Revert "svcrdma: Use contiguous pages for RDMA Read sink buffers" ...
Diffstat (limited to 'net')
-rw-r--r--net/sunrpc/.kunitconfig29
-rw-r--r--net/sunrpc/Kconfig56
-rw-r--r--net/sunrpc/Makefile2
-rw-r--r--net/sunrpc/auth_gss/Makefile4
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_crypto.c994
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_internal.h161
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_keys.c546
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_mech.c433
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_seal.c47
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_test.c1868
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_unseal.c36
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_wrap.c13
-rw-r--r--net/sunrpc/auth_gss/svcauth_gss.c2
-rw-r--r--net/sunrpc/cache.c127
-rw-r--r--net/sunrpc/netlink.c97
-rw-r--r--net/sunrpc/netlink.h35
-rw-r--r--net/sunrpc/sunrpc_syms.c10
-rw-r--r--net/sunrpc/svc.c10
-rw-r--r--net/sunrpc/svc_xprt.c46
-rw-r--r--net/sunrpc/svcauth_unix.c516
-rw-r--r--net/sunrpc/svcsock.c24
-rw-r--r--net/sunrpc/xdr.c289
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma.c18
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_recvfrom.c13
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_rw.c240
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_sendto.c97
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_transport.c33
27 files changed, 1524 insertions, 4222 deletions
diff --git a/net/sunrpc/.kunitconfig b/net/sunrpc/.kunitconfig
deleted file mode 100644
index eb02b906c295..000000000000
--- a/net/sunrpc/.kunitconfig
+++ /dev/null
@@ -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
diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig
index a570e7adf270..e7808e5714dc 100644
--- a/net/sunrpc/Kconfig
+++ b/net/sunrpc/Kconfig
@@ -21,8 +21,7 @@ config RPCSEC_GSS_KRB5
depends on SUNRPC && CRYPTO
default y
select SUNRPC_GSS
- select CRYPTO_SKCIPHER
- select CRYPTO_HASH
+ select CRYPTO_KRB5
help
Choose Y here to enable Secure RPC using the Kerberos version 5
GSS-API mechanism (RFC 1964).
@@ -34,59 +33,6 @@ config RPCSEC_GSS_KRB5
If unsure, say Y.
-config RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1
- bool "Enable Kerberos enctypes based on AES and SHA-1"
- depends on RPCSEC_GSS_KRB5
- depends on CRYPTO_CBC && CRYPTO_CTS
- depends on CRYPTO_HMAC && CRYPTO_SHA1
- depends on CRYPTO_AES
- default y
- help
- Choose Y to enable the use of Kerberos 5 encryption types
- that utilize Advanced Encryption Standard (AES) ciphers and
- SHA-1 digests. These include aes128-cts-hmac-sha1-96 and
- aes256-cts-hmac-sha1-96.
-
-config RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA
- bool "Enable Kerberos encryption types based on Camellia and CMAC"
- depends on RPCSEC_GSS_KRB5
- depends on CRYPTO_CBC && CRYPTO_CTS && CRYPTO_CAMELLIA
- depends on CRYPTO_CMAC
- default n
- help
- Choose Y to enable the use of Kerberos 5 encryption types
- that utilize Camellia ciphers (RFC 3713) and CMAC digests
- (NIST Special Publication 800-38B). These include
- camellia128-cts-cmac and camellia256-cts-cmac.
-
-config RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2
- bool "Enable Kerberos enctypes based on AES and SHA-2"
- depends on RPCSEC_GSS_KRB5
- depends on CRYPTO_CBC && CRYPTO_CTS
- depends on CRYPTO_HMAC && CRYPTO_SHA256 && CRYPTO_SHA512
- depends on CRYPTO_AES
- default n
- help
- Choose Y to enable the use of Kerberos 5 encryption types
- that utilize Advanced Encryption Standard (AES) ciphers and
- 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
diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile
index f89c10fe7e6a..96727df3aa85 100644
--- a/net/sunrpc/Makefile
+++ b/net/sunrpc/Makefile
@@ -14,7 +14,7 @@ sunrpc-y := clnt.o xprt.o socklib.o xprtsock.o sched.o \
addr.o rpcb_clnt.o timer.o xdr.o \
sunrpc_syms.o cache.o rpc_pipe.o sysfs.o \
svc_xprt.o \
- xprtmultipath.o
+ xprtmultipath.o netlink.o
sunrpc-$(CONFIG_SUNRPC_DEBUG) += debugfs.o
sunrpc-$(CONFIG_SUNRPC_BACKCHANNEL) += backchannel_rqst.o
sunrpc-$(CONFIG_PROC_FS) += stats.o
diff --git a/net/sunrpc/auth_gss/Makefile b/net/sunrpc/auth_gss/Makefile
index 452f67deebc6..68676389d65f 100644
--- a/net/sunrpc/auth_gss/Makefile
+++ b/net/sunrpc/auth_gss/Makefile
@@ -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
diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c
index 16dcf115de1e..cf461ebcdde5 100644
--- a/net/sunrpc/auth_gss/gss_krb5_crypto.c
+++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c
@@ -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,567 +87,235 @@ 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.
+ * gss_krb5_aead_encrypt - Encrypt a wrap token using crypto/krb5
+ * @kctx: Kerberos context
+ * @offset: byte offset of the GSS token header in @buf
+ * @buf: OUT: send buffer
+ * @pages: plaintext payload pages (page cache data)
*
- * 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
+ * The xdr_buf setup mirrors the original per-enctype encrypt
+ * functions, but the CBC-CTS encryption and HMAC are replaced
+ * by a single AEAD operation through the crypto/krb5 library.
*
* Return values:
- * %0: decryption successful
- * negative errno: decryption could not be completed
+ * %GSS_S_COMPLETE: Encryption successful
+ * %GSS_S_FAILURE: Encryption failed
*/
-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);
-
u32
-gss_krb5_aes_encrypt(struct krb5_ctx *kctx, u32 offset,
- struct xdr_buf *buf, struct page **pages)
+gss_krb5_aead_encrypt(struct krb5_ctx *kctx, u32 offset,
+ struct xdr_buf *buf, struct page **pages)
{
- u32 err;
- struct xdr_netobj hmac;
- u8 *ecptr;
- struct crypto_sync_skcipher *cipher, *aux_cipher;
- struct crypto_ahash *ahash;
- struct page **save_pages;
- unsigned int conflen;
-
- if (kctx->initiate) {
- cipher = kctx->initiator_enc;
- aux_cipher = kctx->initiator_enc_aux;
- ahash = kctx->initiator_integ;
- } else {
- cipher = kctx->acceptor_enc;
- aux_cipher = kctx->acceptor_enc_aux;
- ahash = kctx->acceptor_integ;
- }
- conflen = crypto_sync_skcipher_blocksize(cipher);
-
- /* hide the gss token header and insert the confounder */
- offset += GSS_KRB5_TOK_HDR_LEN;
- if (xdr_extend_head(buf, offset, conflen))
+ const struct krb5_enctype *krb5 = kctx->krb5e;
+ struct crypto_aead *aead = kctx->initiate ?
+ kctx->initiator_enc_aead : kctx->acceptor_enc_aead;
+ unsigned int conflen = krb5->conf_len;
+ unsigned int cksum_len = krb5->cksum_len;
+ unsigned int sec_offset, sec_len, data_len;
+ struct scatterlist sg[XDR_BUF_TO_SG_NENTS];
+ struct scatterlist *sg_overflow = NULL;
+ ssize_t ret;
+ int nsg;
+
+ /* Insert space for the confounder */
+ if (xdr_extend_head(buf, offset + GSS_KRB5_TOK_HDR_LEN, conflen))
return GSS_S_FAILURE;
- krb5_make_confounder(buf->head[0].iov_base + offset, conflen);
- offset -= GSS_KRB5_TOK_HDR_LEN;
- if (buf->tail[0].iov_base != NULL) {
- ecptr = buf->tail[0].iov_base + buf->tail[0].iov_len;
- } else {
+ /* Ensure a tail segment exists */
+ if (buf->tail[0].iov_base == NULL) {
buf->tail[0].iov_base = buf->head[0].iov_base
- + buf->head[0].iov_len;
+ + buf->head[0].iov_len;
buf->tail[0].iov_len = 0;
- ecptr = buf->tail[0].iov_base;
}
- /* copy plaintext gss token header after filler (if any) */
- memcpy(ecptr, buf->head[0].iov_base + offset, GSS_KRB5_TOK_HDR_LEN);
+ /* Append a copy of the plaintext GSS token header (RFC 4121 Sec 4.2.4) */
+ memcpy(buf->tail[0].iov_base + buf->tail[0].iov_len,
+ buf->head[0].iov_base + offset, GSS_KRB5_TOK_HDR_LEN);
buf->tail[0].iov_len += GSS_KRB5_TOK_HDR_LEN;
buf->len += GSS_KRB5_TOK_HDR_LEN;
- hmac.len = kctx->gk5e->cksumlength;
- hmac.data = buf->tail[0].iov_base + buf->tail[0].iov_len;
+ /* Reserve space for the integrity checksum */
+ buf->tail[0].iov_len += cksum_len;
+ buf->len += cksum_len;
/*
- * When we are called, pages points to the real page cache
- * data -- which we can't go and encrypt! buf->pages points
- * to scratch pages which we are going to send off to the
- * client/server. Swap in the plaintext pages to calculate
- * the hmac.
+ * The AEAD operates in-place, but on the client send path the
+ * plaintext payload lives in page cache pages that must not be
+ * modified. Copy the payload into the scratch output pages
+ * first. On the server send path @pages and buf->pages are
+ * the same array, and no copy is needed.
+ *
+ * Both arrays share buf->page_base, so the same index and
+ * intra-page offset address corresponding data in each.
*/
- save_pages = buf->pages;
- buf->pages = pages;
+ if (pages != buf->pages) {
+ unsigned int poff = buf->page_base;
+ unsigned int plen = buf->page_len;
+ unsigned int i = poff >> PAGE_SHIFT;
+ unsigned int off = offset_in_page(poff);
+
+ while (plen) {
+ unsigned int n = min_t(unsigned int, plen,
+ PAGE_SIZE - off);
+ memcpy_page(buf->pages[i], off, pages[i], off, n);
+ plen -= n;
+ i++;
+ off = 0;
+ }
+ }
- err = gss_krb5_checksum(ahash, NULL, 0, buf,
- offset + GSS_KRB5_TOK_HDR_LEN, &hmac);
- buf->pages = save_pages;
- if (err)
- return GSS_S_FAILURE;
+ /* Build scatterlist covering the secured region */
+ sec_offset = offset + GSS_KRB5_TOK_HDR_LEN;
+ sec_len = buf->len - sec_offset;
+ data_len = sec_len - conflen - cksum_len;
- err = krb5_cbc_cts_encrypt(cipher, aux_cipher,
- offset + GSS_KRB5_TOK_HDR_LEN,
- buf, pages, NULL, 0);
- if (err)
+ nsg = xdr_buf_to_sg_alloc(buf, sec_offset, sec_len,
+ sg, ARRAY_SIZE(sg),
+ &sg_overflow, GFP_NOFS);
+ if (nsg < 0)
return GSS_S_FAILURE;
- /* Now update buf to account for HMAC */
- buf->tail[0].iov_len += kctx->gk5e->cksumlength;
- buf->len += kctx->gk5e->cksumlength;
+ ret = crypto_krb5_encrypt(krb5, aead, sg, nsg, sec_len,
+ conflen, data_len, false);
+ kfree(sg_overflow);
+ if (ret < 0)
+ return GSS_S_FAILURE;
return GSS_S_COMPLETE;
}
-u32
-gss_krb5_aes_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
- struct xdr_buf *buf, u32 *headskip, u32 *tailskip)
-{
- struct crypto_sync_skcipher *cipher, *aux_cipher;
- struct crypto_ahash *ahash;
- struct xdr_netobj our_hmac_obj;
- u8 our_hmac[GSS_KRB5_MAX_CKSUM_LEN];
- u8 pkt_hmac[GSS_KRB5_MAX_CKSUM_LEN];
- struct xdr_buf subbuf;
- u32 ret = 0;
-
- if (kctx->initiate) {
- cipher = kctx->acceptor_enc;
- aux_cipher = kctx->acceptor_enc_aux;
- ahash = kctx->acceptor_integ;
- } else {
- cipher = kctx->initiator_enc;
- aux_cipher = kctx->initiator_enc_aux;
- ahash = kctx->initiator_integ;
- }
-
- /* create a segment skipping the header and leaving out the checksum */
- xdr_buf_subsegment(buf, &subbuf, offset + GSS_KRB5_TOK_HDR_LEN,
- (len - offset - GSS_KRB5_TOK_HDR_LEN -
- kctx->gk5e->cksumlength));
-
- ret = krb5_cbc_cts_decrypt(cipher, aux_cipher, 0, &subbuf);
- if (ret)
- goto out_err;
-
- our_hmac_obj.len = kctx->gk5e->cksumlength;
- our_hmac_obj.data = our_hmac;
- ret = gss_krb5_checksum(ahash, NULL, 0, &subbuf, 0, &our_hmac_obj);
- if (ret)
- goto out_err;
-
- /* Get the packet's hmac value */
- ret = read_bytes_from_xdr_buf(buf, len - kctx->gk5e->cksumlength,
- pkt_hmac, kctx->gk5e->cksumlength);
- if (ret)
- goto out_err;
-
- if (crypto_memneq(pkt_hmac, our_hmac, kctx->gk5e->cksumlength) != 0) {
- ret = GSS_S_BAD_SIG;
- goto out_err;
- }
- *headskip = crypto_sync_skcipher_blocksize(cipher);
- *tailskip = kctx->gk5e->cksumlength;
-out_err:
- if (ret && ret != GSS_S_BAD_SIG)
- ret = GSS_S_FAILURE;
- return ret;
-}
-
-/**
- * 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);
-
/**
- * krb5_etm_encrypt - Encrypt using the RFC 8009 rules
+ * gss_krb5_aead_decrypt - Decrypt a wrap token using crypto/krb5
* @kctx: Kerberos context
- * @offset: starting offset of the payload, in bytes
- * @buf: OUT: send buffer to contain the encrypted payload
- * @pages: plaintext payload
- *
- * The main difference with aes_encrypt is that "The HMAC is
- * calculated over the cipher state concatenated with the AES
- * output, instead of being calculated over the confounder and
- * plaintext. This allows the message receiver to verify the
- * integrity of the message before decrypting the message."
- *
- * RFC 8009 Section 5:
- *
- * encryption function: as follows, where E() is AES encryption in
- * CBC-CS3 mode, and h is the size of truncated HMAC (128 bits or
- * 192 bits as described above).
- *
- * N = random value of length 128 bits (the AES block size)
- * IV = cipher state
- * C = E(Ke, N | plaintext, IV)
- * H = HMAC(Ki, IV | C)
- * ciphertext = C | H[1..h]
- *
- * This encryption formula provides AEAD EtM with key separation.
+ * @offset: byte offset of the GSS token header in @buf
+ * @len: total length of the GSS token
+ * @buf: ciphertext buffer, decrypted in-place
+ * @headskip: OUT: confounder length, in octets
+ * @tailskip: OUT: checksum length, in octets
*
* Return values:
- * %GSS_S_COMPLETE: Encryption successful
- * %GSS_S_FAILURE: Encryption failed
+ * %GSS_S_COMPLETE: Decryption and integrity verification succeeded
+ * %GSS_S_BAD_SIG: Integrity checksum did not match
+ * %GSS_S_DEFECTIVE_TOKEN: Token is malformed or truncated
+ * %GSS_S_FAILURE: Decryption failed
*/
u32
-krb5_etm_encrypt(struct krb5_ctx *kctx, u32 offset,
- struct xdr_buf *buf, struct page **pages)
+gss_krb5_aead_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
+ struct xdr_buf *buf, u32 *headskip, u32 *tailskip)
{
- struct crypto_sync_skcipher *cipher, *aux_cipher;
- struct crypto_ahash *ahash;
- struct xdr_netobj hmac;
- unsigned int conflen;
- u8 *ecptr;
- u32 err;
-
- if (kctx->initiate) {
- cipher = kctx->initiator_enc;
- aux_cipher = kctx->initiator_enc_aux;
- ahash = kctx->initiator_integ;
- } else {
- cipher = kctx->acceptor_enc;
- aux_cipher = kctx->acceptor_enc_aux;
- ahash = kctx->acceptor_integ;
- }
- conflen = crypto_sync_skcipher_blocksize(cipher);
-
- offset += GSS_KRB5_TOK_HDR_LEN;
- if (xdr_extend_head(buf, offset, conflen))
- return GSS_S_FAILURE;
- krb5_make_confounder(buf->head[0].iov_base + offset, conflen);
- offset -= GSS_KRB5_TOK_HDR_LEN;
-
- if (buf->tail[0].iov_base) {
- ecptr = buf->tail[0].iov_base + buf->tail[0].iov_len;
- } else {
- buf->tail[0].iov_base = buf->head[0].iov_base
- + buf->head[0].iov_len;
- buf->tail[0].iov_len = 0;
- ecptr = buf->tail[0].iov_base;
- }
-
- memcpy(ecptr, buf->head[0].iov_base + offset, GSS_KRB5_TOK_HDR_LEN);
- buf->tail[0].iov_len += GSS_KRB5_TOK_HDR_LEN;
- buf->len += GSS_KRB5_TOK_HDR_LEN;
-
- err = krb5_cbc_cts_encrypt(cipher, aux_cipher,
- offset + GSS_KRB5_TOK_HDR_LEN,
- buf, pages, NULL, 0);
- if (err)
+ const struct krb5_enctype *krb5 = kctx->krb5e;
+ struct crypto_aead *aead = kctx->initiate ?
+ kctx->acceptor_enc_aead : kctx->initiator_enc_aead;
+ unsigned int sec_offset, sec_len;
+ size_t data_offset, data_len;
+ struct scatterlist sg[XDR_BUF_TO_SG_NENTS];
+ struct scatterlist *sg_overflow = NULL;
+ int nsg, ret;
+
+ /* Secured region starts after the GSS token header */
+ sec_offset = offset + GSS_KRB5_TOK_HDR_LEN;
+ if (len < sec_offset)
+ return GSS_S_DEFECTIVE_TOKEN;
+ sec_len = len - sec_offset;
+
+ nsg = xdr_buf_to_sg_alloc(buf, sec_offset, sec_len,
+ sg, ARRAY_SIZE(sg),
+ &sg_overflow, GFP_NOFS);
+ if (nsg < 0)
return GSS_S_FAILURE;
- hmac.data = buf->tail[0].iov_base + buf->tail[0].iov_len;
- hmac.len = kctx->gk5e->cksumlength;
- err = krb5_etm_checksum(cipher, ahash,
- buf, offset + GSS_KRB5_TOK_HDR_LEN, &hmac);
- if (err)
- goto out_err;
- buf->tail[0].iov_len += kctx->gk5e->cksumlength;
- buf->len += kctx->gk5e->cksumlength;
+ data_offset = 0;
+ data_len = sec_len;
+ ret = crypto_krb5_decrypt(krb5, aead, sg, nsg,
+ &data_offset, &data_len);
+ kfree(sg_overflow);
+ if (ret < 0)
+ return gss_krb5_errno_to_status(ret);
+ *headskip = data_offset;
+ *tailskip = sec_len - data_offset - data_len;
return GSS_S_COMPLETE;
-
-out_err:
- return GSS_S_FAILURE;
}
/**
- * krb5_etm_decrypt - Decrypt using the RFC 8009 rules
- * @kctx: Kerberos context
- * @offset: starting offset of the ciphertext, in bytes
- * @len: size of ciphertext to unwrap
- * @buf: ciphertext to unwrap
- * @headskip: OUT: the enctype's confounder length, in octets
- * @tailskip: OUT: the enctype's HMAC length, in octets
- *
- * RFC 8009 Section 5:
- *
- * decryption function: as follows, where D() is AES decryption in
- * CBC-CS3 mode, and h is the size of truncated HMAC.
- *
- * (C, H) = ciphertext
- * (Note: H is the last h bits of the ciphertext.)
- * IV = cipher state
- * if H != HMAC(Ki, IV | C)[1..h]
- * stop, report error
- * (N, P) = D(Ke, C, IV)
- *
- * Return values:
- * %GSS_S_COMPLETE: Decryption successful
- * %GSS_S_BAD_SIG: computed HMAC != received HMAC
- * %GSS_S_FAILURE: Decryption failed
+ * gss_krb5_mic_build_sg - Build scatterlist for MIC token operations
+ * @body: xdr_buf containing the message body
+ * @cksum: pointer to checksum area in the token buffer
+ * @cksum_len: length of checksum area
+ * @hdr: pointer to GSS token header
+ * @sg_head: caller-provided scatterlist array; if more than
+ * XDR_BUF_TO_SG_NENTS entries are needed, an overflow
+ * scatterlist is allocated and chained automatically
+ * @sg_overflow: OUT: overflow scatterlist, caller must kfree
+ *
+ * Per RFC 4121 Section 4.2.4, MIC token checksums cover the
+ * message body followed by the token header. The checksum
+ * output or received checksum occupies the first scatterlist
+ * entry. This layout cannot be constructed by
+ * xdr_buf_to_sg_alloc() because the checksum area and the GSS
+ * header lie outside the xdr_buf.
+ *
+ * Returns the number of scatterlist entries on success, or a
+ * negative errno on failure.
*/
-u32
-krb5_etm_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
- struct xdr_buf *buf, u32 *headskip, u32 *tailskip)
+int gss_krb5_mic_build_sg(const struct xdr_buf *body,
+ void *cksum, unsigned int cksum_len,
+ void *hdr,
+ struct scatterlist *sg_head,
+ struct scatterlist **sg_overflow)
{
- struct crypto_sync_skcipher *cipher, *aux_cipher;
- u8 our_hmac[GSS_KRB5_MAX_CKSUM_LEN];
- u8 pkt_hmac[GSS_KRB5_MAX_CKSUM_LEN];
- struct xdr_netobj our_hmac_obj;
- struct crypto_ahash *ahash;
- struct xdr_buf subbuf;
- u32 ret = 0;
-
- if (kctx->initiate) {
- cipher = kctx->acceptor_enc;
- aux_cipher = kctx->acceptor_enc_aux;
- ahash = kctx->acceptor_integ;
+ struct scatterlist *entry;
+ int body_max, body_nsg, nsg;
+
+ *sg_overflow = NULL;
+
+ body_max = 2;
+ if (body->page_len)
+ body_max += DIV_ROUND_UP(body->page_len +
+ offset_in_page(body->page_base),
+ PAGE_SIZE);
+ nsg = 1 + body_max + 1;
+ if (nsg <= XDR_BUF_TO_SG_NENTS) {
+ sg_init_table(sg_head, nsg);
} else {
- cipher = kctx->initiator_enc;
- aux_cipher = kctx->initiator_enc_aux;
- ahash = kctx->initiator_integ;
+ unsigned int overflow_nents =
+ nsg - XDR_BUF_TO_SG_NENTS + 1;
+
+ *sg_overflow = kmalloc_array(overflow_nents,
+ sizeof(**sg_overflow),
+ GFP_NOFS);
+ if (!*sg_overflow)
+ return -ENOMEM;
+
+ sg_init_table(sg_head, XDR_BUF_TO_SG_NENTS);
+ sg_init_table(*sg_overflow, overflow_nents);
+ sg_chain(sg_head, XDR_BUF_TO_SG_NENTS, *sg_overflow);
}
- /* Extract the ciphertext into @subbuf. */
- xdr_buf_subsegment(buf, &subbuf, offset + GSS_KRB5_TOK_HDR_LEN,
- (len - offset - GSS_KRB5_TOK_HDR_LEN -
- kctx->gk5e->cksumlength));
-
- our_hmac_obj.data = our_hmac;
- our_hmac_obj.len = kctx->gk5e->cksumlength;
- ret = krb5_etm_checksum(cipher, ahash, &subbuf, 0, &our_hmac_obj);
- if (ret)
+ sg_set_buf(&sg_head[0], cksum, cksum_len);
+ body_nsg = xdr_buf_to_sg(body, 0, body->len,
+ sg_next(&sg_head[0]), body_max);
+ if (body_nsg < 0)
goto out_err;
- ret = read_bytes_from_xdr_buf(buf, len - kctx->gk5e->cksumlength,
- pkt_hmac, kctx->gk5e->cksumlength);
- if (ret)
- goto out_err;
- if (crypto_memneq(pkt_hmac, our_hmac, kctx->gk5e->cksumlength) != 0) {
- ret = GSS_S_BAD_SIG;
- goto out_err;
- }
- ret = krb5_cbc_cts_decrypt(cipher, aux_cipher, 0, &subbuf);
- if (ret) {
- ret = GSS_S_FAILURE;
- goto out_err;
+ /*
+ * xdr_buf_to_sg marks the last body entry as end-of-list;
+ * clear it so the trailing header entry is reachable.
+ */
+ if (body_nsg > 0) {
+ entry = sg_last(sg_next(&sg_head[0]), body_nsg);
+ sg_unmark_end(entry);
+ entry = sg_next(entry);
+ } else {
+ entry = sg_next(&sg_head[0]);
}
-
- *headskip = crypto_sync_skcipher_blocksize(cipher);
- *tailskip = kctx->gk5e->cksumlength;
- return GSS_S_COMPLETE;
+ sg_set_buf(entry, hdr, GSS_KRB5_TOK_HDR_LEN);
+ sg_mark_end(entry);
+ return 1 + body_nsg + 1;
out_err:
- if (ret != GSS_S_BAD_SIG)
- ret = GSS_S_FAILURE;
- return ret;
+ kfree(*sg_overflow);
+ *sg_overflow = NULL;
+ return body_nsg;
}
diff --git a/net/sunrpc/auth_gss/gss_krb5_internal.h b/net/sunrpc/auth_gss/gss_krb5_internal.h
index 8769e9e705bf..3b392e96f25d 100644
--- a/net/sunrpc/auth_gss/gss_krb5_internal.h
+++ b/net/sunrpc/auth_gss/gss_krb5_internal.h
@@ -8,51 +8,10 @@
#ifndef _NET_SUNRPC_AUTH_GSS_KRB5_INTERNAL_H
#define _NET_SUNRPC_AUTH_GSS_KRB5_INTERNAL_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)
+#include <crypto/krb5.h>
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);
- u32 (*encrypt)(struct krb5_ctx *kctx, u32 offset,
- struct xdr_buf *buf, struct page **pages);
- u32 (*decrypt)(struct krb5_ctx *kctx, u32 offset, u32 len,
- struct xdr_buf *buf, u32 *headskip, u32 *tailskip);
- u32 (*get_mic)(struct krb5_ctx *kctx, struct xdr_buf *text,
- struct xdr_netobj *token);
- u32 (*verify_mic)(struct krb5_ctx *kctx, struct xdr_buf *message_buffer,
- struct xdr_netobj *read_token);
- u32 (*wrap)(struct krb5_ctx *kctx, int offset,
- struct xdr_buf *buf, struct page **pages);
- u32 (*unwrap)(struct krb5_ctx *kctx, int offset, int len,
- struct xdr_buf *buf, unsigned int *slack,
- unsigned int *align);
-};
-
/* krb5_ctx flags definitions */
#define KRB5_CTX_FLAG_INITIATOR 0x00000001
#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
@@ -61,20 +20,12 @@ struct krb5_ctx {
int initiate; /* 1 = initiating, 0 = accepting */
u32 enctype;
u32 flags;
- const struct gss_krb5_enctype *gk5e; /* enctype-specific info */
- struct crypto_sync_skcipher *enc;
- struct crypto_sync_skcipher *seq;
- struct crypto_sync_skcipher *acceptor_enc;
- 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;
- struct crypto_ahash *initiator_integ;
- struct crypto_ahash *acceptor_integ;
+ const struct krb5_enctype *krb5e; /* crypto/krb5 enctype */
+ struct crypto_aead *initiator_enc_aead;
+ struct crypto_aead *acceptor_enc_aead;
+ struct crypto_shash *initiator_sign_shash;
+ 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;
@@ -101,95 +52,21 @@ 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);
-
-/**
- * krb5_derive_key - Derive a subkey from a protocol key
- * @kctx: Kerberos 5 context
- * @inkey: base protocol key
- * @outkey: OUT: derived key
- * @usage: key usage value
- * @seed: key usage seed (one octet)
- * @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.
- */
-static inline int krb5_derive_key(struct krb5_ctx *kctx,
- const struct xdr_netobj *inkey,
- struct xdr_netobj *outkey,
- u32 usage, u8 seed, gfp_t gfp_mask)
-{
- const struct gss_krb5_enctype *gk5e = kctx->gk5e;
- u8 label_data[GSS_KRB5_K5CLENGTH];
- struct xdr_netobj label = {
- .len = sizeof(label_data),
- .data = label_data,
- };
- __be32 *p = (__be32 *)label_data;
-
- *p = cpu_to_be32(usage);
- label_data[4] = seed;
- return gk5e->derive_key(gk5e, inkey, outkey, &label, 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);
-u32 gss_krb5_aes_encrypt(struct krb5_ctx *kctx, u32 offset,
- struct xdr_buf *buf, struct page **pages);
-
-u32 gss_krb5_aes_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
- struct xdr_buf *buf, u32 *plainoffset, u32 *plainlen);
-
-u32 krb5_etm_encrypt(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf,
- struct page **pages);
-
-u32 krb5_etm_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
+u32 gss_krb5_errno_to_status(int err);
+
+int gss_krb5_mic_build_sg(const struct xdr_buf *body,
+ void *cksum, unsigned int cksum_len,
+ void *hdr,
+ struct scatterlist *sg_head,
+ struct scatterlist **sg_overflow);
+
+u32 gss_krb5_aead_encrypt(struct krb5_ctx *kctx, u32 offset,
+ struct xdr_buf *buf, struct page **pages);
+u32 gss_krb5_aead_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
+ struct xdr_buf *buf, u32 *headskip, u32 *tailskip);
+
#endif /* _NET_SUNRPC_AUTH_GSS_KRB5_INTERNAL_H */
diff --git a/net/sunrpc/auth_gss/gss_krb5_keys.c b/net/sunrpc/auth_gss/gss_krb5_keys.c
deleted file mode 100644
index 4eb19c3a54c7..000000000000
--- a/net/sunrpc/auth_gss/gss_krb5_keys.c
+++ /dev/null
@@ -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;
-}
diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c
index 6db64a9111a9..c41b5f3e1789 100644
--- a/net/sunrpc/auth_gss/gss_krb5_mech.c
+++ b/net/sunrpc/auth_gss/gss_krb5_mech.c
@@ -9,8 +9,6 @@
* J. Bruce Fields <bfields@umich.edu>
*/
-#include <crypto/hash.h>
-#include <crypto/skcipher.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -19,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"
@@ -30,201 +27,24 @@
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,
- .encrypt = gss_krb5_aes_encrypt,
- .decrypt = gss_krb5_aes_decrypt,
-
- .get_mic = gss_krb5_get_mic_v2,
- .verify_mic = gss_krb5_verify_mic_v2,
- .wrap = gss_krb5_wrap_v2,
- .unwrap = gss_krb5_unwrap_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,
- .encrypt = gss_krb5_aes_encrypt,
- .decrypt = gss_krb5_aes_decrypt,
-
- .get_mic = gss_krb5_get_mic_v2,
- .verify_mic = gss_krb5_verify_mic_v2,
- .wrap = gss_krb5_wrap_v2,
- .unwrap = gss_krb5_unwrap_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,
- .encrypt = gss_krb5_aes_encrypt,
- .decrypt = gss_krb5_aes_decrypt,
-
- .get_mic = gss_krb5_get_mic_v2,
- .verify_mic = gss_krb5_verify_mic_v2,
- .wrap = gss_krb5_wrap_v2,
- .unwrap = gss_krb5_unwrap_v2,
- },
- /*
- * 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,
- .encrypt = gss_krb5_aes_encrypt,
- .decrypt = gss_krb5_aes_decrypt,
-
- .get_mic = gss_krb5_get_mic_v2,
- .verify_mic = gss_krb5_verify_mic_v2,
- .wrap = gss_krb5_wrap_v2,
- .unwrap = gss_krb5_unwrap_v2,
- },
-#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,
- .encrypt = krb5_etm_encrypt,
- .decrypt = krb5_etm_decrypt,
-
- .get_mic = gss_krb5_get_mic_v2,
- .verify_mic = gss_krb5_verify_mic_v2,
- .wrap = gss_krb5_wrap_v2,
- .unwrap = gss_krb5_unwrap_v2,
- },
- /*
- * 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,
- .encrypt = krb5_etm_encrypt,
- .decrypt = krb5_etm_decrypt,
-
- .get_mic = gss_krb5_get_mic_v2,
- .verify_mic = gss_krb5_verify_mic_v2,
- .wrap = gss_krb5_wrap_v2,
- .unwrap = gss_krb5_unwrap_v2,
- },
-#endif
-};
-
/*
- * The list of advertised enctypes is specified in order of most
- * preferred to least.
+ * Candidate enctypes in order of most preferred to least.
+ * Each is probed against crypto/krb5 at module init; only
+ * enctypes that crypto/krb5 supports are advertised.
*/
+static const u32 gss_krb5_enctypes[] = {
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+ KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+ KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+ KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+};
+
static char gss_krb5_enctype_priority_list[64];
static void gss_krb5_prepare_enctype_priority_list(void)
{
- static const u32 gss_krb5_enctypes[] = {
-#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2)
- ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- ENCTYPE_AES128_CTS_HMAC_SHA256_128,
-#endif
-#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA)
- ENCTYPE_CAMELLIA256_CTS_CMAC,
- ENCTYPE_CAMELLIA128_CTS_CMAC,
-#endif
-#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1)
- ENCTYPE_AES256_CTS_HMAC_SHA1_96,
- ENCTYPE_AES128_CTS_HMAC_SHA1_96,
-#endif
- };
size_t total, i;
char buf[16];
char *sep;
@@ -233,6 +53,8 @@ static void gss_krb5_prepare_enctype_priority_list(void)
sep = "";
gss_krb5_enctype_priority_list[0] = '\0';
for (total = 0, i = 0; i < ARRAY_SIZE(gss_krb5_enctypes); i++) {
+ if (!crypto_krb5_find_enctype(gss_krb5_enctypes[i]))
+ continue;
n = sprintf(buf, "%s%u", sep, gss_krb5_enctypes[i]);
if (n < 0)
break;
@@ -244,151 +66,56 @@ 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 struct crypto_sync_skcipher *
-gss_krb5_alloc_cipher_v2(const char *cname, const struct xdr_netobj *key)
-{
- struct crypto_sync_skcipher *tfm;
-
- tfm = crypto_alloc_sync_skcipher(cname, 0, 0);
- if (IS_ERR(tfm))
- return NULL;
- if (crypto_sync_skcipher_setkey(tfm, key->data, key->len)) {
- crypto_free_sync_skcipher(tfm);
- return NULL;
- }
- return tfm;
-}
-
-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
gss_krb5_import_ctx_v2(struct krb5_ctx *ctx, gfp_t gfp_mask)
{
- struct xdr_netobj keyin = {
- .len = ctx->gk5e->keylength,
+ struct krb5_buffer TK = {
+ .len = ctx->krb5e->key_len,
.data = ctx->Ksess,
};
- struct xdr_netobj keyout;
- int ret = -EINVAL;
-
- keyout.data = kmalloc(GSS_KRB5_MAX_KEYLEN, gfp_mask);
- if (!keyout.data)
- return -ENOMEM;
-
- /* initiator seal encryption */
- keyout.len = ctx->gk5e->Ke_length;
- if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_INITIATOR_SEAL,
- KEY_USAGE_SEED_ENCRYPTION, gfp_mask))
- goto out;
- ctx->initiator_enc = gss_krb5_alloc_cipher_v2(ctx->gk5e->encrypt_name,
- &keyout);
- if (ctx->initiator_enc == NULL)
- goto out;
- if (ctx->gk5e->aux_cipher) {
- ctx->initiator_enc_aux =
- gss_krb5_alloc_cipher_v2(ctx->gk5e->aux_cipher,
- &keyout);
- if (ctx->initiator_enc_aux == NULL)
- goto out_free;
- }
+ int ret;
- /* acceptor seal encryption */
- if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_ACCEPTOR_SEAL,
- KEY_USAGE_SEED_ENCRYPTION, gfp_mask))
+ ctx->initiator_enc_aead =
+ crypto_krb5_prepare_encryption(ctx->krb5e, &TK,
+ KG_USAGE_INITIATOR_SEAL,
+ gfp_mask);
+ if (IS_ERR(ctx->initiator_enc_aead)) {
+ ret = PTR_ERR(ctx->initiator_enc_aead);
goto out_free;
- ctx->acceptor_enc = gss_krb5_alloc_cipher_v2(ctx->gk5e->encrypt_name,
- &keyout);
- if (ctx->acceptor_enc == NULL)
- goto out_free;
- if (ctx->gk5e->aux_cipher) {
- ctx->acceptor_enc_aux =
- gss_krb5_alloc_cipher_v2(ctx->gk5e->aux_cipher,
- &keyout);
- if (ctx->acceptor_enc_aux == NULL)
- goto out_free;
}
-
- /* initiator sign checksum */
- keyout.len = ctx->gk5e->Kc_length;
- if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_INITIATOR_SIGN,
- KEY_USAGE_SEED_CHECKSUM, gfp_mask))
+ ctx->acceptor_enc_aead =
+ crypto_krb5_prepare_encryption(ctx->krb5e, &TK,
+ KG_USAGE_ACCEPTOR_SEAL,
+ gfp_mask);
+ if (IS_ERR(ctx->acceptor_enc_aead)) {
+ ret = PTR_ERR(ctx->acceptor_enc_aead);
goto out_free;
- ctx->initiator_sign = gss_krb5_alloc_hash_v2(ctx, &keyout);
- if (ctx->initiator_sign == NULL)
- goto out_free;
-
- /* acceptor sign checksum */
- if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_ACCEPTOR_SIGN,
- KEY_USAGE_SEED_CHECKSUM, gfp_mask))
- goto out_free;
- ctx->acceptor_sign = gss_krb5_alloc_hash_v2(ctx, &keyout);
- if (ctx->acceptor_sign == NULL)
- goto out_free;
-
- /* initiator seal integrity */
- keyout.len = ctx->gk5e->Ki_length;
- if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_INITIATOR_SEAL,
- KEY_USAGE_SEED_INTEGRITY, gfp_mask))
- goto out_free;
- ctx->initiator_integ = gss_krb5_alloc_hash_v2(ctx, &keyout);
- if (ctx->initiator_integ == NULL)
- goto out_free;
-
- /* acceptor seal integrity */
- if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_ACCEPTOR_SEAL,
- KEY_USAGE_SEED_INTEGRITY, gfp_mask))
+ }
+ ctx->initiator_sign_shash =
+ crypto_krb5_prepare_checksum(ctx->krb5e, &TK,
+ KG_USAGE_INITIATOR_SIGN,
+ gfp_mask);
+ if (IS_ERR(ctx->initiator_sign_shash)) {
+ ret = PTR_ERR(ctx->initiator_sign_shash);
goto out_free;
- ctx->acceptor_integ = gss_krb5_alloc_hash_v2(ctx, &keyout);
- if (ctx->acceptor_integ == NULL)
+ }
+ ctx->acceptor_sign_shash =
+ crypto_krb5_prepare_checksum(ctx->krb5e, &TK,
+ KG_USAGE_ACCEPTOR_SIGN,
+ gfp_mask);
+ if (IS_ERR(ctx->acceptor_sign_shash)) {
+ ret = PTR_ERR(ctx->acceptor_sign_shash);
goto out_free;
+ }
- ret = 0;
-out:
- kfree_sensitive(keyout.data);
- return ret;
+ return 0;
out_free:
- crypto_free_ahash(ctx->acceptor_integ);
- crypto_free_ahash(ctx->initiator_integ);
- 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);
- crypto_free_sync_skcipher(ctx->initiator_enc);
- goto out;
+ crypto_free_shash(ctx->acceptor_sign_shash);
+ crypto_free_shash(ctx->initiator_sign_shash);
+ crypto_free_aead(ctx->acceptor_enc_aead);
+ crypto_free_aead(ctx->initiator_enc_aead);
+ return ret;
}
static int
@@ -414,25 +141,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;
}
- keylen = ctx->gk5e->keylength;
+ keylen = ctx->krb5e->key_len;
p = simple_get_bytes(p, end, ctx->Ksess, keylen);
if (IS_ERR(p))
@@ -495,21 +214,39 @@ gss_krb5_delete_sec_context(void *internal_ctx)
{
struct krb5_ctx *kctx = internal_ctx;
- crypto_free_sync_skcipher(kctx->seq);
- crypto_free_sync_skcipher(kctx->enc);
- crypto_free_sync_skcipher(kctx->acceptor_enc);
- 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);
- crypto_free_ahash(kctx->acceptor_integ);
- crypto_free_ahash(kctx->initiator_integ);
+ crypto_free_shash(kctx->acceptor_sign_shash);
+ crypto_free_shash(kctx->initiator_sign_shash);
+ crypto_free_aead(kctx->acceptor_enc_aead);
+ crypto_free_aead(kctx->initiator_enc_aead);
kfree(kctx->mech_used.data);
kfree(kctx);
}
/**
+ * gss_krb5_errno_to_status - Map a negative errno to a GSS major status
+ * @err: negative errno value, or zero
+ *
+ * Returns:
+ * %GSS_S_COMPLETE if @err is zero
+ * %GSS_S_BAD_SIG if @err is -EBADMSG (integrity check failure)
+ * %GSS_S_DEFECTIVE_TOKEN if @err is -EPROTO (malformed token)
+ * %GSS_S_FAILURE for all other negative values
+ */
+u32 gss_krb5_errno_to_status(int err)
+{
+ switch (err) {
+ case 0:
+ return GSS_S_COMPLETE;
+ case -EBADMSG:
+ return GSS_S_BAD_SIG;
+ case -EPROTO:
+ return GSS_S_DEFECTIVE_TOKEN;
+ default:
+ return GSS_S_FAILURE;
+ }
+}
+
+/**
* gss_krb5_get_mic - get_mic for the Kerberos GSS mechanism
* @gctx: GSS context
* @text: plaintext to checksum
@@ -525,7 +262,7 @@ static u32 gss_krb5_get_mic(struct gss_ctx *gctx, struct xdr_buf *text,
{
struct krb5_ctx *kctx = gctx->internal_ctx_id;
- return kctx->gk5e->get_mic(kctx, text, token);
+ return gss_krb5_get_mic_v2(kctx, text, token);
}
/**
@@ -547,7 +284,7 @@ static u32 gss_krb5_verify_mic(struct gss_ctx *gctx,
{
struct krb5_ctx *kctx = gctx->internal_ctx_id;
- return kctx->gk5e->verify_mic(kctx, message_buffer, read_token);
+ return gss_krb5_verify_mic_v2(kctx, message_buffer, read_token);
}
/**
@@ -567,7 +304,7 @@ static u32 gss_krb5_wrap(struct gss_ctx *gctx, int offset,
{
struct krb5_ctx *kctx = gctx->internal_ctx_id;
- return kctx->gk5e->wrap(kctx, offset, buf, pages);
+ return gss_krb5_wrap_v2(kctx, offset, buf, pages);
}
/**
@@ -589,7 +326,7 @@ static u32 gss_krb5_unwrap(struct gss_ctx *gctx, int offset,
{
struct krb5_ctx *kctx = gctx->internal_ctx_id;
- return kctx->gk5e->unwrap(kctx, offset, len, buf,
+ return gss_krb5_unwrap_v2(kctx, offset, len, buf,
&gctx->slack, &gctx->align);
}
diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c
index ce540df9bce4..cfe066e89f23 100644
--- a/net/sunrpc/auth_gss/gss_krb5_seal.c
+++ b/net/sunrpc/auth_gss/gss_krb5_seal.c
@@ -61,9 +61,9 @@
#include <linux/types.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_krb5.h>
-#include <linux/random.h>
-#include <linux/crypto.h>
#include <linux/atomic.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include "gss_krb5_internal.h"
@@ -78,10 +78,10 @@ setup_token_v2(struct krb5_ctx *ctx, struct xdr_netobj *token)
void *krb5_hdr;
u8 *p, flags = 0x00;
- if ((ctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0)
- flags |= 0x01;
+ if (!ctx->initiate)
+ flags |= KG2_TOKEN_FLAG_SENTBYACCEPTOR;
if (ctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY)
- flags |= 0x04;
+ flags |= KG2_TOKEN_FLAG_ACCEPTORSUBKEY;
/* Per rfc 4121, sec 4.2.6.1, there is no header,
* just start the token.
@@ -97,7 +97,7 @@ setup_token_v2(struct krb5_ctx *ctx, struct xdr_netobj *token)
*ptr++ = 0xffff;
*ptr = 0xffff;
- token->len = GSS_KRB5_TOK_HDR_LEN + ctx->gk5e->cksumlength;
+ token->len = GSS_KRB5_TOK_HDR_LEN + ctx->krb5e->cksum_len;
return krb5_hdr;
}
@@ -105,14 +105,17 @@ u32
gss_krb5_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text,
struct xdr_netobj *token)
{
- struct crypto_ahash *tfm = ctx->initiate ?
- ctx->initiator_sign : ctx->acceptor_sign;
- struct xdr_netobj cksumobj = {
- .len = ctx->gk5e->cksumlength,
- };
+ const struct krb5_enctype *krb5 = ctx->krb5e;
+ struct crypto_shash *shash = ctx->initiate ?
+ ctx->initiator_sign_shash : ctx->acceptor_sign_shash;
+ unsigned int cksum_len = krb5->cksum_len;
+ struct scatterlist sg_head[XDR_BUF_TO_SG_NENTS];
+ struct scatterlist *sg_overflow;
__be64 seq_send_be64;
void *krb5_hdr;
time64_t now;
+ ssize_t ret;
+ int nsg;
dprintk("RPC: %s\n", __func__);
@@ -123,9 +126,25 @@ gss_krb5_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);
- cksumobj.data = krb5_hdr + GSS_KRB5_TOK_HDR_LEN;
- if (gss_krb5_checksum(tfm, krb5_hdr, GSS_KRB5_TOK_HDR_LEN,
- text, 0, &cksumobj))
+ /*
+ * The checksum is written directly into the token buffer.
+ * This is safe: crypto_krb5_get_mic uses shash (software
+ * hash), so the scatterlist is never DMA-mapped.
+ */
+ nsg = gss_krb5_mic_build_sg(text,
+ krb5_hdr + GSS_KRB5_TOK_HDR_LEN,
+ cksum_len, krb5_hdr,
+ sg_head, &sg_overflow);
+ if (nsg < 0)
+ return GSS_S_FAILURE;
+
+ ret = crypto_krb5_get_mic(krb5, shash, NULL, sg_head, nsg,
+ cksum_len + text->len +
+ GSS_KRB5_TOK_HDR_LEN,
+ cksum_len,
+ text->len + GSS_KRB5_TOK_HDR_LEN);
+ kfree(sg_overflow);
+ if (ret < 0)
return GSS_S_FAILURE;
now = ktime_get_real_seconds();
diff --git a/net/sunrpc/auth_gss/gss_krb5_test.c b/net/sunrpc/auth_gss/gss_krb5_test.c
deleted file mode 100644
index dde1ee934d0d..000000000000
--- a/net/sunrpc/auth_gss/gss_krb5_test.c
+++ /dev/null
@@ -1,1868 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (c) 2022 Oracle and/or its affiliates.
- *
- * KUnit test of SunRPC's GSS Kerberos mechanism. Subsystem
- * name is "rpcsec_gss_krb5".
- */
-
-#include <kunit/test.h>
-#include <kunit/visibility.h>
-
-#include <linux/kernel.h>
-#include <crypto/hash.h>
-
-#include <linux/sunrpc/xdr.h>
-#include <linux/sunrpc/gss_krb5.h>
-
-#include "gss_krb5_internal.h"
-
-MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
-
-struct gss_krb5_test_param {
- const char *desc;
- u32 enctype;
- u32 nfold;
- u32 constant;
- const struct xdr_netobj *base_key;
- const struct xdr_netobj *Ke;
- const struct xdr_netobj *usage;
- const struct xdr_netobj *plaintext;
- const struct xdr_netobj *confounder;
- const struct xdr_netobj *expected_result;
- const struct xdr_netobj *expected_hmac;
- const struct xdr_netobj *next_iv;
-};
-
-static inline void gss_krb5_get_desc(const struct gss_krb5_test_param *param,
- char *desc)
-{
- strscpy(desc, param->desc, KUNIT_PARAM_DESC_SIZE);
-}
-
-static void kdf_case(struct kunit *test)
-{
- const struct gss_krb5_test_param *param = test->param_value;
- const struct gss_krb5_enctype *gk5e;
- struct xdr_netobj derivedkey;
- int err;
-
- /* Arrange */
- gk5e = gss_krb5_lookup_enctype(param->enctype);
- if (!gk5e)
- kunit_skip(test, "Encryption type is not available");
-
- derivedkey.data = kunit_kzalloc(test, param->expected_result->len,
- GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, derivedkey.data);
- derivedkey.len = param->expected_result->len;
-
- /* Act */
- err = gk5e->derive_key(gk5e, param->base_key, &derivedkey,
- param->usage, GFP_KERNEL);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Assert */
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_result->data,
- derivedkey.data,
- derivedkey.len,
- "key mismatch");
-}
-
-static void checksum_case(struct kunit *test)
-{
- const struct gss_krb5_test_param *param = test->param_value;
- struct xdr_buf buf = {
- .head[0].iov_len = param->plaintext->len,
- .len = param->plaintext->len,
- };
- const struct gss_krb5_enctype *gk5e;
- struct xdr_netobj Kc, checksum;
- struct crypto_ahash *tfm;
- int err;
-
- /* Arrange */
- gk5e = gss_krb5_lookup_enctype(param->enctype);
- if (!gk5e)
- kunit_skip(test, "Encryption type is not available");
-
- Kc.len = gk5e->Kc_length;
- Kc.data = kunit_kzalloc(test, Kc.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, Kc.data);
- err = gk5e->derive_key(gk5e, param->base_key, &Kc,
- param->usage, GFP_KERNEL);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- tfm = crypto_alloc_ahash(gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, tfm);
- err = crypto_ahash_setkey(tfm, Kc.data, Kc.len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- buf.head[0].iov_base = kunit_kzalloc(test, buf.head[0].iov_len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf.head[0].iov_base);
- memcpy(buf.head[0].iov_base, param->plaintext->data, buf.head[0].iov_len);
-
- checksum.len = gk5e->cksumlength;
- checksum.data = kunit_kzalloc(test, checksum.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, checksum.data);
-
- /* Act */
- err = gss_krb5_checksum(tfm, NULL, 0, &buf, 0, &checksum);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Assert */
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_result->data,
- checksum.data,
- checksum.len,
- "checksum mismatch");
-
- crypto_free_ahash(tfm);
-}
-
-#define DEFINE_HEX_XDR_NETOBJ(name, hex_array...) \
- static const u8 name ## _data[] = { hex_array }; \
- static const struct xdr_netobj name = { \
- .data = (u8 *)name##_data, \
- .len = sizeof(name##_data), \
- }
-
-#define DEFINE_STR_XDR_NETOBJ(name, string) \
- static const u8 name ## _str[] = string; \
- static const struct xdr_netobj name = { \
- .data = (u8 *)name##_str, \
- .len = sizeof(name##_str) - 1, \
- }
-
-/*
- * RFC 3961 Appendix A.1. n-fold
- *
- * The n-fold function is defined in section 5.1 of RFC 3961.
- *
- * This test material is copyright (C) The Internet Society (2005).
- */
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test1_plaintext,
- 0x30, 0x31, 0x32, 0x33, 0x34, 0x35
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test1_expected_result,
- 0xbe, 0x07, 0x26, 0x31, 0x27, 0x6b, 0x19, 0x55
-);
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test2_plaintext,
- 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test2_expected_result,
- 0x78, 0xa0, 0x7b, 0x6c, 0xaf, 0x85, 0xfa
-);
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test3_plaintext,
- 0x52, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x43, 0x6f,
- 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2c,
- 0x20, 0x61, 0x6e, 0x64, 0x20, 0x52, 0x75, 0x6e,
- 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x43, 0x6f, 0x64,
- 0x65
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test3_expected_result,
- 0xbb, 0x6e, 0xd3, 0x08, 0x70, 0xb7, 0xf0, 0xe0
-);
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test4_plaintext,
- 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test4_expected_result,
- 0x59, 0xe4, 0xa8, 0xca, 0x7c, 0x03, 0x85, 0xc3,
- 0xc3, 0x7b, 0x3f, 0x6d, 0x20, 0x00, 0x24, 0x7c,
- 0xb6, 0xe6, 0xbd, 0x5b, 0x3e
-);
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test5_plaintext,
- 0x4d, 0x41, 0x53, 0x53, 0x41, 0x43, 0x48, 0x56,
- 0x53, 0x45, 0x54, 0x54, 0x53, 0x20, 0x49, 0x4e,
- 0x53, 0x54, 0x49, 0x54, 0x56, 0x54, 0x45, 0x20,
- 0x4f, 0x46, 0x20, 0x54, 0x45, 0x43, 0x48, 0x4e,
- 0x4f, 0x4c, 0x4f, 0x47, 0x59
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test5_expected_result,
- 0xdb, 0x3b, 0x0d, 0x8f, 0x0b, 0x06, 0x1e, 0x60,
- 0x32, 0x82, 0xb3, 0x08, 0xa5, 0x08, 0x41, 0x22,
- 0x9a, 0xd7, 0x98, 0xfa, 0xb9, 0x54, 0x0c, 0x1b
-);
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test6_plaintext,
- 0x51
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test6_expected_result,
- 0x51, 0x8a, 0x54, 0xa2, 0x15, 0xa8, 0x45, 0x2a,
- 0x51, 0x8a, 0x54, 0xa2, 0x15, 0xa8, 0x45, 0x2a,
- 0x51, 0x8a, 0x54, 0xa2, 0x15
-);
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test7_plaintext,
- 0x62, 0x61
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test7_expected_result,
- 0xfb, 0x25, 0xd5, 0x31, 0xae, 0x89, 0x74, 0x49,
- 0x9f, 0x52, 0xfd, 0x92, 0xea, 0x98, 0x57, 0xc4,
- 0xba, 0x24, 0xcf, 0x29, 0x7e
-);
-
-DEFINE_HEX_XDR_NETOBJ(nfold_test_kerberos,
- 0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test8_expected_result,
- 0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test9_expected_result,
- 0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73,
- 0x7b, 0x9b, 0x5b, 0x2b, 0x93, 0x13, 0x2b, 0x93
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test10_expected_result,
- 0x83, 0x72, 0xc2, 0x36, 0x34, 0x4e, 0x5f, 0x15,
- 0x50, 0xcd, 0x07, 0x47, 0xe1, 0x5d, 0x62, 0xca,
- 0x7a, 0x5a, 0x3b, 0xce, 0xa4
-);
-DEFINE_HEX_XDR_NETOBJ(nfold_test11_expected_result,
- 0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73,
- 0x7b, 0x9b, 0x5b, 0x2b, 0x93, 0x13, 0x2b, 0x93,
- 0x5c, 0x9b, 0xdc, 0xda, 0xd9, 0x5c, 0x98, 0x99,
- 0xc4, 0xca, 0xe4, 0xde, 0xe6, 0xd6, 0xca, 0xe4
-);
-
-static const struct gss_krb5_test_param rfc3961_nfold_test_params[] = {
- {
- .desc = "64-fold(\"012345\")",
- .nfold = 64,
- .plaintext = &nfold_test1_plaintext,
- .expected_result = &nfold_test1_expected_result,
- },
- {
- .desc = "56-fold(\"password\")",
- .nfold = 56,
- .plaintext = &nfold_test2_plaintext,
- .expected_result = &nfold_test2_expected_result,
- },
- {
- .desc = "64-fold(\"Rough Consensus, and Running Code\")",
- .nfold = 64,
- .plaintext = &nfold_test3_plaintext,
- .expected_result = &nfold_test3_expected_result,
- },
- {
- .desc = "168-fold(\"password\")",
- .nfold = 168,
- .plaintext = &nfold_test4_plaintext,
- .expected_result = &nfold_test4_expected_result,
- },
- {
- .desc = "192-fold(\"MASSACHVSETTS INSTITVTE OF TECHNOLOGY\")",
- .nfold = 192,
- .plaintext = &nfold_test5_plaintext,
- .expected_result = &nfold_test5_expected_result,
- },
- {
- .desc = "168-fold(\"Q\")",
- .nfold = 168,
- .plaintext = &nfold_test6_plaintext,
- .expected_result = &nfold_test6_expected_result,
- },
- {
- .desc = "168-fold(\"ba\")",
- .nfold = 168,
- .plaintext = &nfold_test7_plaintext,
- .expected_result = &nfold_test7_expected_result,
- },
- {
- .desc = "64-fold(\"kerberos\")",
- .nfold = 64,
- .plaintext = &nfold_test_kerberos,
- .expected_result = &nfold_test8_expected_result,
- },
- {
- .desc = "128-fold(\"kerberos\")",
- .nfold = 128,
- .plaintext = &nfold_test_kerberos,
- .expected_result = &nfold_test9_expected_result,
- },
- {
- .desc = "168-fold(\"kerberos\")",
- .nfold = 168,
- .plaintext = &nfold_test_kerberos,
- .expected_result = &nfold_test10_expected_result,
- },
- {
- .desc = "256-fold(\"kerberos\")",
- .nfold = 256,
- .plaintext = &nfold_test_kerberos,
- .expected_result = &nfold_test11_expected_result,
- },
-};
-
-/* Creates the function rfc3961_nfold_gen_params */
-KUNIT_ARRAY_PARAM(rfc3961_nfold, rfc3961_nfold_test_params, gss_krb5_get_desc);
-
-static void rfc3961_nfold_case(struct kunit *test)
-{
- const struct gss_krb5_test_param *param = test->param_value;
- u8 *result;
-
- /* Arrange */
- result = kunit_kzalloc(test, 4096, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
-
- /* Act */
- krb5_nfold(param->plaintext->len * 8, param->plaintext->data,
- param->expected_result->len * 8, result);
-
- /* Assert */
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_result->data,
- result,
- param->expected_result->len,
- "result mismatch");
-}
-
-static struct kunit_case rfc3961_test_cases[] = {
- {
- .name = "RFC 3961 n-fold",
- .run_case = rfc3961_nfold_case,
- .generate_params = rfc3961_nfold_gen_params,
- },
- {}
-};
-
-static struct kunit_suite rfc3961_suite = {
- .name = "RFC 3961 tests",
- .test_cases = rfc3961_test_cases,
-};
-
-/*
- * From RFC 3962 Appendix B: Sample Test Vectors
- *
- * Some test vectors for CBC with ciphertext stealing, using an
- * initial vector of all-zero.
- *
- * This test material is copyright (C) The Internet Society (2005).
- */
-
-DEFINE_HEX_XDR_NETOBJ(rfc3962_encryption_key,
- 0x63, 0x68, 0x69, 0x63, 0x6b, 0x65, 0x6e, 0x20,
- 0x74, 0x65, 0x72, 0x69, 0x79, 0x61, 0x6b, 0x69
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test1_plaintext,
- 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20,
- 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65,
- 0x20
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test1_expected_result,
- 0xc6, 0x35, 0x35, 0x68, 0xf2, 0xbf, 0x8c, 0xb4,
- 0xd8, 0xa5, 0x80, 0x36, 0x2d, 0xa7, 0xff, 0x7f,
- 0x97
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test1_next_iv,
- 0xc6, 0x35, 0x35, 0x68, 0xf2, 0xbf, 0x8c, 0xb4,
- 0xd8, 0xa5, 0x80, 0x36, 0x2d, 0xa7, 0xff, 0x7f
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test2_plaintext,
- 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20,
- 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
- 0x20, 0x47, 0x61, 0x75, 0x27, 0x73, 0x20
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test2_expected_result,
- 0xfc, 0x00, 0x78, 0x3e, 0x0e, 0xfd, 0xb2, 0xc1,
- 0xd4, 0x45, 0xd4, 0xc8, 0xef, 0xf7, 0xed, 0x22,
- 0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
- 0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test2_next_iv,
- 0xfc, 0x00, 0x78, 0x3e, 0x0e, 0xfd, 0xb2, 0xc1,
- 0xd4, 0x45, 0xd4, 0xc8, 0xef, 0xf7, 0xed, 0x22
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test3_plaintext,
- 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20,
- 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
- 0x20, 0x47, 0x61, 0x75, 0x27, 0x73, 0x20, 0x43
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test3_expected_result,
- 0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
- 0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8,
- 0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
- 0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test3_next_iv,
- 0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
- 0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test4_plaintext,
- 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20,
- 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
- 0x20, 0x47, 0x61, 0x75, 0x27, 0x73, 0x20, 0x43,
- 0x68, 0x69, 0x63, 0x6b, 0x65, 0x6e, 0x2c, 0x20,
- 0x70, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2c
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test4_expected_result,
- 0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
- 0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84,
- 0xb3, 0xff, 0xfd, 0x94, 0x0c, 0x16, 0xa1, 0x8c,
- 0x1b, 0x55, 0x49, 0xd2, 0xf8, 0x38, 0x02, 0x9e,
- 0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
- 0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test4_next_iv,
- 0xb3, 0xff, 0xfd, 0x94, 0x0c, 0x16, 0xa1, 0x8c,
- 0x1b, 0x55, 0x49, 0xd2, 0xf8, 0x38, 0x02, 0x9e
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test5_plaintext,
- 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20,
- 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
- 0x20, 0x47, 0x61, 0x75, 0x27, 0x73, 0x20, 0x43,
- 0x68, 0x69, 0x63, 0x6b, 0x65, 0x6e, 0x2c, 0x20,
- 0x70, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2c, 0x20
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test5_expected_result,
- 0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
- 0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84,
- 0x9d, 0xad, 0x8b, 0xbb, 0x96, 0xc4, 0xcd, 0xc0,
- 0x3b, 0xc1, 0x03, 0xe1, 0xa1, 0x94, 0xbb, 0xd8,
- 0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
- 0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test5_next_iv,
- 0x9d, 0xad, 0x8b, 0xbb, 0x96, 0xc4, 0xcd, 0xc0,
- 0x3b, 0xc1, 0x03, 0xe1, 0xa1, 0x94, 0xbb, 0xd8
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test6_plaintext,
- 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20,
- 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
- 0x20, 0x47, 0x61, 0x75, 0x27, 0x73, 0x20, 0x43,
- 0x68, 0x69, 0x63, 0x6b, 0x65, 0x6e, 0x2c, 0x20,
- 0x70, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2c, 0x20,
- 0x61, 0x6e, 0x64, 0x20, 0x77, 0x6f, 0x6e, 0x74,
- 0x6f, 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x70, 0x2e
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test6_expected_result,
- 0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
- 0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84,
- 0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
- 0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8,
- 0x48, 0x07, 0xef, 0xe8, 0x36, 0xee, 0x89, 0xa5,
- 0x26, 0x73, 0x0d, 0xbc, 0x2f, 0x7b, 0xc8, 0x40,
- 0x9d, 0xad, 0x8b, 0xbb, 0x96, 0xc4, 0xcd, 0xc0,
- 0x3b, 0xc1, 0x03, 0xe1, 0xa1, 0x94, 0xbb, 0xd8
-);
-DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test6_next_iv,
- 0x48, 0x07, 0xef, 0xe8, 0x36, 0xee, 0x89, 0xa5,
- 0x26, 0x73, 0x0d, 0xbc, 0x2f, 0x7b, 0xc8, 0x40
-);
-
-static const struct gss_krb5_test_param rfc3962_encrypt_test_params[] = {
- {
- .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 1",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &rfc3962_enc_test1_plaintext,
- .expected_result = &rfc3962_enc_test1_expected_result,
- .next_iv = &rfc3962_enc_test1_next_iv,
- },
- {
- .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 2",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &rfc3962_enc_test2_plaintext,
- .expected_result = &rfc3962_enc_test2_expected_result,
- .next_iv = &rfc3962_enc_test2_next_iv,
- },
- {
- .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 3",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &rfc3962_enc_test3_plaintext,
- .expected_result = &rfc3962_enc_test3_expected_result,
- .next_iv = &rfc3962_enc_test3_next_iv,
- },
- {
- .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 4",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &rfc3962_enc_test4_plaintext,
- .expected_result = &rfc3962_enc_test4_expected_result,
- .next_iv = &rfc3962_enc_test4_next_iv,
- },
- {
- .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 5",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &rfc3962_enc_test5_plaintext,
- .expected_result = &rfc3962_enc_test5_expected_result,
- .next_iv = &rfc3962_enc_test5_next_iv,
- },
- {
- .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 6",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &rfc3962_enc_test6_plaintext,
- .expected_result = &rfc3962_enc_test6_expected_result,
- .next_iv = &rfc3962_enc_test6_next_iv,
- },
-};
-
-/* Creates the function rfc3962_encrypt_gen_params */
-KUNIT_ARRAY_PARAM(rfc3962_encrypt, rfc3962_encrypt_test_params,
- gss_krb5_get_desc);
-
-/*
- * This tests the implementation of the encryption part of the mechanism.
- * It does not apply a confounder or test the result of HMAC over the
- * plaintext.
- */
-static void rfc3962_encrypt_case(struct kunit *test)
-{
- const struct gss_krb5_test_param *param = test->param_value;
- struct crypto_sync_skcipher *cts_tfm, *cbc_tfm;
- const struct gss_krb5_enctype *gk5e;
- struct xdr_buf buf;
- void *iv, *text;
- u32 err;
-
- /* Arrange */
- gk5e = gss_krb5_lookup_enctype(param->enctype);
- if (!gk5e)
- kunit_skip(test, "Encryption type is not available");
-
- cbc_tfm = crypto_alloc_sync_skcipher(gk5e->aux_cipher, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cbc_tfm);
- err = crypto_sync_skcipher_setkey(cbc_tfm, param->Ke->data, param->Ke->len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- cts_tfm = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cts_tfm);
- err = crypto_sync_skcipher_setkey(cts_tfm, param->Ke->data, param->Ke->len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- iv = kunit_kzalloc(test, crypto_sync_skcipher_ivsize(cts_tfm), GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, iv);
-
- text = kunit_kzalloc(test, param->plaintext->len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, text);
-
- memcpy(text, param->plaintext->data, param->plaintext->len);
- memset(&buf, 0, sizeof(buf));
- buf.head[0].iov_base = text;
- buf.head[0].iov_len = param->plaintext->len;
- buf.len = buf.head[0].iov_len;
-
- /* Act */
- err = krb5_cbc_cts_encrypt(cts_tfm, cbc_tfm, 0, &buf, NULL,
- iv, crypto_sync_skcipher_ivsize(cts_tfm));
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Assert */
- KUNIT_EXPECT_EQ_MSG(test,
- param->expected_result->len, buf.len,
- "ciphertext length mismatch");
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_result->data,
- text,
- param->expected_result->len,
- "ciphertext mismatch");
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->next_iv->data,
- iv,
- param->next_iv->len,
- "IV mismatch");
-
- crypto_free_sync_skcipher(cts_tfm);
- crypto_free_sync_skcipher(cbc_tfm);
-}
-
-static struct kunit_case rfc3962_test_cases[] = {
- {
- .name = "RFC 3962 encryption",
- .run_case = rfc3962_encrypt_case,
- .generate_params = rfc3962_encrypt_gen_params,
- },
- {}
-};
-
-static struct kunit_suite rfc3962_suite = {
- .name = "RFC 3962 suite",
- .test_cases = rfc3962_test_cases,
-};
-
-/*
- * From RFC 6803 Section 10. Test vectors
- *
- * Sample results for key derivation
- *
- * Copyright (c) 2012 IETF Trust and the persons identified as the
- * document authors. All rights reserved.
- */
-
-DEFINE_HEX_XDR_NETOBJ(camellia128_cts_cmac_basekey,
- 0x57, 0xd0, 0x29, 0x72, 0x98, 0xff, 0xd9, 0xd3,
- 0x5d, 0xe5, 0xa4, 0x7f, 0xb4, 0xbd, 0xe2, 0x4b
-);
-DEFINE_HEX_XDR_NETOBJ(camellia128_cts_cmac_Kc,
- 0xd1, 0x55, 0x77, 0x5a, 0x20, 0x9d, 0x05, 0xf0,
- 0x2b, 0x38, 0xd4, 0x2a, 0x38, 0x9e, 0x5a, 0x56
-);
-DEFINE_HEX_XDR_NETOBJ(camellia128_cts_cmac_Ke,
- 0x64, 0xdf, 0x83, 0xf8, 0x5a, 0x53, 0x2f, 0x17,
- 0x57, 0x7d, 0x8c, 0x37, 0x03, 0x57, 0x96, 0xab
-);
-DEFINE_HEX_XDR_NETOBJ(camellia128_cts_cmac_Ki,
- 0x3e, 0x4f, 0xbd, 0xf3, 0x0f, 0xb8, 0x25, 0x9c,
- 0x42, 0x5c, 0xb6, 0xc9, 0x6f, 0x1f, 0x46, 0x35
-);
-
-DEFINE_HEX_XDR_NETOBJ(camellia256_cts_cmac_basekey,
- 0xb9, 0xd6, 0x82, 0x8b, 0x20, 0x56, 0xb7, 0xbe,
- 0x65, 0x6d, 0x88, 0xa1, 0x23, 0xb1, 0xfa, 0xc6,
- 0x82, 0x14, 0xac, 0x2b, 0x72, 0x7e, 0xcf, 0x5f,
- 0x69, 0xaf, 0xe0, 0xc4, 0xdf, 0x2a, 0x6d, 0x2c
-);
-DEFINE_HEX_XDR_NETOBJ(camellia256_cts_cmac_Kc,
- 0xe4, 0x67, 0xf9, 0xa9, 0x55, 0x2b, 0xc7, 0xd3,
- 0x15, 0x5a, 0x62, 0x20, 0xaf, 0x9c, 0x19, 0x22,
- 0x0e, 0xee, 0xd4, 0xff, 0x78, 0xb0, 0xd1, 0xe6,
- 0xa1, 0x54, 0x49, 0x91, 0x46, 0x1a, 0x9e, 0x50
-);
-DEFINE_HEX_XDR_NETOBJ(camellia256_cts_cmac_Ke,
- 0x41, 0x2a, 0xef, 0xc3, 0x62, 0xa7, 0x28, 0x5f,
- 0xc3, 0x96, 0x6c, 0x6a, 0x51, 0x81, 0xe7, 0x60,
- 0x5a, 0xe6, 0x75, 0x23, 0x5b, 0x6d, 0x54, 0x9f,
- 0xbf, 0xc9, 0xab, 0x66, 0x30, 0xa4, 0xc6, 0x04
-);
-DEFINE_HEX_XDR_NETOBJ(camellia256_cts_cmac_Ki,
- 0xfa, 0x62, 0x4f, 0xa0, 0xe5, 0x23, 0x99, 0x3f,
- 0xa3, 0x88, 0xae, 0xfd, 0xc6, 0x7e, 0x67, 0xeb,
- 0xcd, 0x8c, 0x08, 0xe8, 0xa0, 0x24, 0x6b, 0x1d,
- 0x73, 0xb0, 0xd1, 0xdd, 0x9f, 0xc5, 0x82, 0xb0
-);
-
-DEFINE_HEX_XDR_NETOBJ(usage_checksum,
- 0x00, 0x00, 0x00, 0x02, KEY_USAGE_SEED_CHECKSUM
-);
-DEFINE_HEX_XDR_NETOBJ(usage_encryption,
- 0x00, 0x00, 0x00, 0x02, KEY_USAGE_SEED_ENCRYPTION
-);
-DEFINE_HEX_XDR_NETOBJ(usage_integrity,
- 0x00, 0x00, 0x00, 0x02, KEY_USAGE_SEED_INTEGRITY
-);
-
-static const struct gss_krb5_test_param rfc6803_kdf_test_params[] = {
- {
- .desc = "Derive Kc subkey for camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .base_key = &camellia128_cts_cmac_basekey,
- .usage = &usage_checksum,
- .expected_result = &camellia128_cts_cmac_Kc,
- },
- {
- .desc = "Derive Ke subkey for camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .base_key = &camellia128_cts_cmac_basekey,
- .usage = &usage_encryption,
- .expected_result = &camellia128_cts_cmac_Ke,
- },
- {
- .desc = "Derive Ki subkey for camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .base_key = &camellia128_cts_cmac_basekey,
- .usage = &usage_integrity,
- .expected_result = &camellia128_cts_cmac_Ki,
- },
- {
- .desc = "Derive Kc subkey for camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .base_key = &camellia256_cts_cmac_basekey,
- .usage = &usage_checksum,
- .expected_result = &camellia256_cts_cmac_Kc,
- },
- {
- .desc = "Derive Ke subkey for camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .base_key = &camellia256_cts_cmac_basekey,
- .usage = &usage_encryption,
- .expected_result = &camellia256_cts_cmac_Ke,
- },
- {
- .desc = "Derive Ki subkey for camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .base_key = &camellia256_cts_cmac_basekey,
- .usage = &usage_integrity,
- .expected_result = &camellia256_cts_cmac_Ki,
- },
-};
-
-/* Creates the function rfc6803_kdf_gen_params */
-KUNIT_ARRAY_PARAM(rfc6803_kdf, rfc6803_kdf_test_params, gss_krb5_get_desc);
-
-/*
- * From RFC 6803 Section 10. Test vectors
- *
- * Sample checksums.
- *
- * Copyright (c) 2012 IETF Trust and the persons identified as the
- * document authors. All rights reserved.
- *
- * XXX: These tests are likely to fail on EBCDIC or Unicode platforms.
- */
-DEFINE_STR_XDR_NETOBJ(rfc6803_checksum_test1_plaintext,
- "abcdefghijk");
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test1_basekey,
- 0x1d, 0xc4, 0x6a, 0x8d, 0x76, 0x3f, 0x4f, 0x93,
- 0x74, 0x2b, 0xcb, 0xa3, 0x38, 0x75, 0x76, 0xc3
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test1_usage,
- 0x00, 0x00, 0x00, 0x07, KEY_USAGE_SEED_CHECKSUM
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test1_expected_result,
- 0x11, 0x78, 0xe6, 0xc5, 0xc4, 0x7a, 0x8c, 0x1a,
- 0xe0, 0xc4, 0xb9, 0xc7, 0xd4, 0xeb, 0x7b, 0x6b
-);
-
-DEFINE_STR_XDR_NETOBJ(rfc6803_checksum_test2_plaintext,
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test2_basekey,
- 0x50, 0x27, 0xbc, 0x23, 0x1d, 0x0f, 0x3a, 0x9d,
- 0x23, 0x33, 0x3f, 0x1c, 0xa6, 0xfd, 0xbe, 0x7c
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test2_usage,
- 0x00, 0x00, 0x00, 0x08, KEY_USAGE_SEED_CHECKSUM
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test2_expected_result,
- 0xd1, 0xb3, 0x4f, 0x70, 0x04, 0xa7, 0x31, 0xf2,
- 0x3a, 0x0c, 0x00, 0xbf, 0x6c, 0x3f, 0x75, 0x3a
-);
-
-DEFINE_STR_XDR_NETOBJ(rfc6803_checksum_test3_plaintext,
- "123456789");
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test3_basekey,
- 0xb6, 0x1c, 0x86, 0xcc, 0x4e, 0x5d, 0x27, 0x57,
- 0x54, 0x5a, 0xd4, 0x23, 0x39, 0x9f, 0xb7, 0x03,
- 0x1e, 0xca, 0xb9, 0x13, 0xcb, 0xb9, 0x00, 0xbd,
- 0x7a, 0x3c, 0x6d, 0xd8, 0xbf, 0x92, 0x01, 0x5b
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test3_usage,
- 0x00, 0x00, 0x00, 0x09, KEY_USAGE_SEED_CHECKSUM
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test3_expected_result,
- 0x87, 0xa1, 0x2c, 0xfd, 0x2b, 0x96, 0x21, 0x48,
- 0x10, 0xf0, 0x1c, 0x82, 0x6e, 0x77, 0x44, 0xb1
-);
-
-DEFINE_STR_XDR_NETOBJ(rfc6803_checksum_test4_plaintext,
- "!@#$%^&*()!@#$%^&*()!@#$%^&*()");
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test4_basekey,
- 0x32, 0x16, 0x4c, 0x5b, 0x43, 0x4d, 0x1d, 0x15,
- 0x38, 0xe4, 0xcf, 0xd9, 0xbe, 0x80, 0x40, 0xfe,
- 0x8c, 0x4a, 0xc7, 0xac, 0xc4, 0xb9, 0x3d, 0x33,
- 0x14, 0xd2, 0x13, 0x36, 0x68, 0x14, 0x7a, 0x05
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test4_usage,
- 0x00, 0x00, 0x00, 0x0a, KEY_USAGE_SEED_CHECKSUM
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test4_expected_result,
- 0x3f, 0xa0, 0xb4, 0x23, 0x55, 0xe5, 0x2b, 0x18,
- 0x91, 0x87, 0x29, 0x4a, 0xa2, 0x52, 0xab, 0x64
-);
-
-static const struct gss_krb5_test_param rfc6803_checksum_test_params[] = {
- {
- .desc = "camellia128-cts-cmac checksum test 1",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .base_key = &rfc6803_checksum_test1_basekey,
- .usage = &rfc6803_checksum_test1_usage,
- .plaintext = &rfc6803_checksum_test1_plaintext,
- .expected_result = &rfc6803_checksum_test1_expected_result,
- },
- {
- .desc = "camellia128-cts-cmac checksum test 2",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .base_key = &rfc6803_checksum_test2_basekey,
- .usage = &rfc6803_checksum_test2_usage,
- .plaintext = &rfc6803_checksum_test2_plaintext,
- .expected_result = &rfc6803_checksum_test2_expected_result,
- },
- {
- .desc = "camellia256-cts-cmac checksum test 3",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .base_key = &rfc6803_checksum_test3_basekey,
- .usage = &rfc6803_checksum_test3_usage,
- .plaintext = &rfc6803_checksum_test3_plaintext,
- .expected_result = &rfc6803_checksum_test3_expected_result,
- },
- {
- .desc = "camellia256-cts-cmac checksum test 4",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .base_key = &rfc6803_checksum_test4_basekey,
- .usage = &rfc6803_checksum_test4_usage,
- .plaintext = &rfc6803_checksum_test4_plaintext,
- .expected_result = &rfc6803_checksum_test4_expected_result,
- },
-};
-
-/* Creates the function rfc6803_checksum_gen_params */
-KUNIT_ARRAY_PARAM(rfc6803_checksum, rfc6803_checksum_test_params,
- gss_krb5_get_desc);
-
-/*
- * From RFC 6803 Section 10. Test vectors
- *
- * Sample encryptions (all using the default cipher state)
- *
- * Copyright (c) 2012 IETF Trust and the persons identified as the
- * document authors. All rights reserved.
- *
- * Key usage values are from errata 4326 against RFC 6803.
- */
-
-static const struct xdr_netobj rfc6803_enc_empty_plaintext = {
- .len = 0,
-};
-
-DEFINE_STR_XDR_NETOBJ(rfc6803_enc_1byte_plaintext, "1");
-DEFINE_STR_XDR_NETOBJ(rfc6803_enc_9byte_plaintext, "9 bytesss");
-DEFINE_STR_XDR_NETOBJ(rfc6803_enc_13byte_plaintext, "13 bytes byte");
-DEFINE_STR_XDR_NETOBJ(rfc6803_enc_30byte_plaintext,
- "30 bytes bytes bytes bytes byt"
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test1_confounder,
- 0xb6, 0x98, 0x22, 0xa1, 0x9a, 0x6b, 0x09, 0xc0,
- 0xeb, 0xc8, 0x55, 0x7d, 0x1f, 0x1b, 0x6c, 0x0a
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test1_basekey,
- 0x1d, 0xc4, 0x6a, 0x8d, 0x76, 0x3f, 0x4f, 0x93,
- 0x74, 0x2b, 0xcb, 0xa3, 0x38, 0x75, 0x76, 0xc3
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test1_expected_result,
- 0xc4, 0x66, 0xf1, 0x87, 0x10, 0x69, 0x92, 0x1e,
- 0xdb, 0x7c, 0x6f, 0xde, 0x24, 0x4a, 0x52, 0xdb,
- 0x0b, 0xa1, 0x0e, 0xdc, 0x19, 0x7b, 0xdb, 0x80,
- 0x06, 0x65, 0x8c, 0xa3, 0xcc, 0xce, 0x6e, 0xb8
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test2_confounder,
- 0x6f, 0x2f, 0xc3, 0xc2, 0xa1, 0x66, 0xfd, 0x88,
- 0x98, 0x96, 0x7a, 0x83, 0xde, 0x95, 0x96, 0xd9
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test2_basekey,
- 0x50, 0x27, 0xbc, 0x23, 0x1d, 0x0f, 0x3a, 0x9d,
- 0x23, 0x33, 0x3f, 0x1c, 0xa6, 0xfd, 0xbe, 0x7c
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test2_expected_result,
- 0x84, 0x2d, 0x21, 0xfd, 0x95, 0x03, 0x11, 0xc0,
- 0xdd, 0x46, 0x4a, 0x3f, 0x4b, 0xe8, 0xd6, 0xda,
- 0x88, 0xa5, 0x6d, 0x55, 0x9c, 0x9b, 0x47, 0xd3,
- 0xf9, 0xa8, 0x50, 0x67, 0xaf, 0x66, 0x15, 0x59,
- 0xb8
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test3_confounder,
- 0xa5, 0xb4, 0xa7, 0x1e, 0x07, 0x7a, 0xee, 0xf9,
- 0x3c, 0x87, 0x63, 0xc1, 0x8f, 0xdb, 0x1f, 0x10
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test3_basekey,
- 0xa1, 0xbb, 0x61, 0xe8, 0x05, 0xf9, 0xba, 0x6d,
- 0xde, 0x8f, 0xdb, 0xdd, 0xc0, 0x5c, 0xde, 0xa0
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test3_expected_result,
- 0x61, 0x9f, 0xf0, 0x72, 0xe3, 0x62, 0x86, 0xff,
- 0x0a, 0x28, 0xde, 0xb3, 0xa3, 0x52, 0xec, 0x0d,
- 0x0e, 0xdf, 0x5c, 0x51, 0x60, 0xd6, 0x63, 0xc9,
- 0x01, 0x75, 0x8c, 0xcf, 0x9d, 0x1e, 0xd3, 0x3d,
- 0x71, 0xdb, 0x8f, 0x23, 0xaa, 0xbf, 0x83, 0x48,
- 0xa0
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test4_confounder,
- 0x19, 0xfe, 0xe4, 0x0d, 0x81, 0x0c, 0x52, 0x4b,
- 0x5b, 0x22, 0xf0, 0x18, 0x74, 0xc6, 0x93, 0xda
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test4_basekey,
- 0x2c, 0xa2, 0x7a, 0x5f, 0xaf, 0x55, 0x32, 0x24,
- 0x45, 0x06, 0x43, 0x4e, 0x1c, 0xef, 0x66, 0x76
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test4_expected_result,
- 0xb8, 0xec, 0xa3, 0x16, 0x7a, 0xe6, 0x31, 0x55,
- 0x12, 0xe5, 0x9f, 0x98, 0xa7, 0xc5, 0x00, 0x20,
- 0x5e, 0x5f, 0x63, 0xff, 0x3b, 0xb3, 0x89, 0xaf,
- 0x1c, 0x41, 0xa2, 0x1d, 0x64, 0x0d, 0x86, 0x15,
- 0xc9, 0xed, 0x3f, 0xbe, 0xb0, 0x5a, 0xb6, 0xac,
- 0xb6, 0x76, 0x89, 0xb5, 0xea
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test5_confounder,
- 0xca, 0x7a, 0x7a, 0xb4, 0xbe, 0x19, 0x2d, 0xab,
- 0xd6, 0x03, 0x50, 0x6d, 0xb1, 0x9c, 0x39, 0xe2
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test5_basekey,
- 0x78, 0x24, 0xf8, 0xc1, 0x6f, 0x83, 0xff, 0x35,
- 0x4c, 0x6b, 0xf7, 0x51, 0x5b, 0x97, 0x3f, 0x43
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test5_expected_result,
- 0xa2, 0x6a, 0x39, 0x05, 0xa4, 0xff, 0xd5, 0x81,
- 0x6b, 0x7b, 0x1e, 0x27, 0x38, 0x0d, 0x08, 0x09,
- 0x0c, 0x8e, 0xc1, 0xf3, 0x04, 0x49, 0x6e, 0x1a,
- 0xbd, 0xcd, 0x2b, 0xdc, 0xd1, 0xdf, 0xfc, 0x66,
- 0x09, 0x89, 0xe1, 0x17, 0xa7, 0x13, 0xdd, 0xbb,
- 0x57, 0xa4, 0x14, 0x6c, 0x15, 0x87, 0xcb, 0xa4,
- 0x35, 0x66, 0x65, 0x59, 0x1d, 0x22, 0x40, 0x28,
- 0x2f, 0x58, 0x42, 0xb1, 0x05, 0xa5
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test6_confounder,
- 0x3c, 0xbb, 0xd2, 0xb4, 0x59, 0x17, 0x94, 0x10,
- 0x67, 0xf9, 0x65, 0x99, 0xbb, 0x98, 0x92, 0x6c
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test6_basekey,
- 0xb6, 0x1c, 0x86, 0xcc, 0x4e, 0x5d, 0x27, 0x57,
- 0x54, 0x5a, 0xd4, 0x23, 0x39, 0x9f, 0xb7, 0x03,
- 0x1e, 0xca, 0xb9, 0x13, 0xcb, 0xb9, 0x00, 0xbd,
- 0x7a, 0x3c, 0x6d, 0xd8, 0xbf, 0x92, 0x01, 0x5b
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test6_expected_result,
- 0x03, 0x88, 0x6d, 0x03, 0x31, 0x0b, 0x47, 0xa6,
- 0xd8, 0xf0, 0x6d, 0x7b, 0x94, 0xd1, 0xdd, 0x83,
- 0x7e, 0xcc, 0xe3, 0x15, 0xef, 0x65, 0x2a, 0xff,
- 0x62, 0x08, 0x59, 0xd9, 0x4a, 0x25, 0x92, 0x66
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test7_confounder,
- 0xde, 0xf4, 0x87, 0xfc, 0xeb, 0xe6, 0xde, 0x63,
- 0x46, 0xd4, 0xda, 0x45, 0x21, 0xbb, 0xa2, 0xd2
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test7_basekey,
- 0x1b, 0x97, 0xfe, 0x0a, 0x19, 0x0e, 0x20, 0x21,
- 0xeb, 0x30, 0x75, 0x3e, 0x1b, 0x6e, 0x1e, 0x77,
- 0xb0, 0x75, 0x4b, 0x1d, 0x68, 0x46, 0x10, 0x35,
- 0x58, 0x64, 0x10, 0x49, 0x63, 0x46, 0x38, 0x33
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test7_expected_result,
- 0x2c, 0x9c, 0x15, 0x70, 0x13, 0x3c, 0x99, 0xbf,
- 0x6a, 0x34, 0xbc, 0x1b, 0x02, 0x12, 0x00, 0x2f,
- 0xd1, 0x94, 0x33, 0x87, 0x49, 0xdb, 0x41, 0x35,
- 0x49, 0x7a, 0x34, 0x7c, 0xfc, 0xd9, 0xd1, 0x8a,
- 0x12
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test8_confounder,
- 0xad, 0x4f, 0xf9, 0x04, 0xd3, 0x4e, 0x55, 0x53,
- 0x84, 0xb1, 0x41, 0x00, 0xfc, 0x46, 0x5f, 0x88
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test8_basekey,
- 0x32, 0x16, 0x4c, 0x5b, 0x43, 0x4d, 0x1d, 0x15,
- 0x38, 0xe4, 0xcf, 0xd9, 0xbe, 0x80, 0x40, 0xfe,
- 0x8c, 0x4a, 0xc7, 0xac, 0xc4, 0xb9, 0x3d, 0x33,
- 0x14, 0xd2, 0x13, 0x36, 0x68, 0x14, 0x7a, 0x05
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test8_expected_result,
- 0x9c, 0x6d, 0xe7, 0x5f, 0x81, 0x2d, 0xe7, 0xed,
- 0x0d, 0x28, 0xb2, 0x96, 0x35, 0x57, 0xa1, 0x15,
- 0x64, 0x09, 0x98, 0x27, 0x5b, 0x0a, 0xf5, 0x15,
- 0x27, 0x09, 0x91, 0x3f, 0xf5, 0x2a, 0x2a, 0x9c,
- 0x8e, 0x63, 0xb8, 0x72, 0xf9, 0x2e, 0x64, 0xc8,
- 0x39
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test9_confounder,
- 0xcf, 0x9b, 0xca, 0x6d, 0xf1, 0x14, 0x4e, 0x0c,
- 0x0a, 0xf9, 0xb8, 0xf3, 0x4c, 0x90, 0xd5, 0x14
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test9_basekey,
- 0xb0, 0x38, 0xb1, 0x32, 0xcd, 0x8e, 0x06, 0x61,
- 0x22, 0x67, 0xfa, 0xb7, 0x17, 0x00, 0x66, 0xd8,
- 0x8a, 0xec, 0xcb, 0xa0, 0xb7, 0x44, 0xbf, 0xc6,
- 0x0d, 0xc8, 0x9b, 0xca, 0x18, 0x2d, 0x07, 0x15
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test9_expected_result,
- 0xee, 0xec, 0x85, 0xa9, 0x81, 0x3c, 0xdc, 0x53,
- 0x67, 0x72, 0xab, 0x9b, 0x42, 0xde, 0xfc, 0x57,
- 0x06, 0xf7, 0x26, 0xe9, 0x75, 0xdd, 0xe0, 0x5a,
- 0x87, 0xeb, 0x54, 0x06, 0xea, 0x32, 0x4c, 0xa1,
- 0x85, 0xc9, 0x98, 0x6b, 0x42, 0xaa, 0xbe, 0x79,
- 0x4b, 0x84, 0x82, 0x1b, 0xee
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test10_confounder,
- 0x64, 0x4d, 0xef, 0x38, 0xda, 0x35, 0x00, 0x72,
- 0x75, 0x87, 0x8d, 0x21, 0x68, 0x55, 0xe2, 0x28
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test10_basekey,
- 0xcc, 0xfc, 0xd3, 0x49, 0xbf, 0x4c, 0x66, 0x77,
- 0xe8, 0x6e, 0x4b, 0x02, 0xb8, 0xea, 0xb9, 0x24,
- 0xa5, 0x46, 0xac, 0x73, 0x1c, 0xf9, 0xbf, 0x69,
- 0x89, 0xb9, 0x96, 0xe7, 0xd6, 0xbf, 0xbb, 0xa7
-);
-DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test10_expected_result,
- 0x0e, 0x44, 0x68, 0x09, 0x85, 0x85, 0x5f, 0x2d,
- 0x1f, 0x18, 0x12, 0x52, 0x9c, 0xa8, 0x3b, 0xfd,
- 0x8e, 0x34, 0x9d, 0xe6, 0xfd, 0x9a, 0xda, 0x0b,
- 0xaa, 0xa0, 0x48, 0xd6, 0x8e, 0x26, 0x5f, 0xeb,
- 0xf3, 0x4a, 0xd1, 0x25, 0x5a, 0x34, 0x49, 0x99,
- 0xad, 0x37, 0x14, 0x68, 0x87, 0xa6, 0xc6, 0x84,
- 0x57, 0x31, 0xac, 0x7f, 0x46, 0x37, 0x6a, 0x05,
- 0x04, 0xcd, 0x06, 0x57, 0x14, 0x74
-);
-
-static const struct gss_krb5_test_param rfc6803_encrypt_test_params[] = {
- {
- .desc = "Encrypt empty plaintext with camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .constant = 0,
- .base_key = &rfc6803_enc_test1_basekey,
- .plaintext = &rfc6803_enc_empty_plaintext,
- .confounder = &rfc6803_enc_test1_confounder,
- .expected_result = &rfc6803_enc_test1_expected_result,
- },
- {
- .desc = "Encrypt 1 byte with camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .constant = 1,
- .base_key = &rfc6803_enc_test2_basekey,
- .plaintext = &rfc6803_enc_1byte_plaintext,
- .confounder = &rfc6803_enc_test2_confounder,
- .expected_result = &rfc6803_enc_test2_expected_result,
- },
- {
- .desc = "Encrypt 9 bytes with camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .constant = 2,
- .base_key = &rfc6803_enc_test3_basekey,
- .plaintext = &rfc6803_enc_9byte_plaintext,
- .confounder = &rfc6803_enc_test3_confounder,
- .expected_result = &rfc6803_enc_test3_expected_result,
- },
- {
- .desc = "Encrypt 13 bytes with camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .constant = 3,
- .base_key = &rfc6803_enc_test4_basekey,
- .plaintext = &rfc6803_enc_13byte_plaintext,
- .confounder = &rfc6803_enc_test4_confounder,
- .expected_result = &rfc6803_enc_test4_expected_result,
- },
- {
- .desc = "Encrypt 30 bytes with camellia128-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .constant = 4,
- .base_key = &rfc6803_enc_test5_basekey,
- .plaintext = &rfc6803_enc_30byte_plaintext,
- .confounder = &rfc6803_enc_test5_confounder,
- .expected_result = &rfc6803_enc_test5_expected_result,
- },
- {
- .desc = "Encrypt empty plaintext with camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .constant = 0,
- .base_key = &rfc6803_enc_test6_basekey,
- .plaintext = &rfc6803_enc_empty_plaintext,
- .confounder = &rfc6803_enc_test6_confounder,
- .expected_result = &rfc6803_enc_test6_expected_result,
- },
- {
- .desc = "Encrypt 1 byte with camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .constant = 1,
- .base_key = &rfc6803_enc_test7_basekey,
- .plaintext = &rfc6803_enc_1byte_plaintext,
- .confounder = &rfc6803_enc_test7_confounder,
- .expected_result = &rfc6803_enc_test7_expected_result,
- },
- {
- .desc = "Encrypt 9 bytes with camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .constant = 2,
- .base_key = &rfc6803_enc_test8_basekey,
- .plaintext = &rfc6803_enc_9byte_plaintext,
- .confounder = &rfc6803_enc_test8_confounder,
- .expected_result = &rfc6803_enc_test8_expected_result,
- },
- {
- .desc = "Encrypt 13 bytes with camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .constant = 3,
- .base_key = &rfc6803_enc_test9_basekey,
- .plaintext = &rfc6803_enc_13byte_plaintext,
- .confounder = &rfc6803_enc_test9_confounder,
- .expected_result = &rfc6803_enc_test9_expected_result,
- },
- {
- .desc = "Encrypt 30 bytes with camellia256-cts-cmac",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .constant = 4,
- .base_key = &rfc6803_enc_test10_basekey,
- .plaintext = &rfc6803_enc_30byte_plaintext,
- .confounder = &rfc6803_enc_test10_confounder,
- .expected_result = &rfc6803_enc_test10_expected_result,
- },
-};
-
-/* Creates the function rfc6803_encrypt_gen_params */
-KUNIT_ARRAY_PARAM(rfc6803_encrypt, rfc6803_encrypt_test_params,
- gss_krb5_get_desc);
-
-static void rfc6803_encrypt_case(struct kunit *test)
-{
- const struct gss_krb5_test_param *param = test->param_value;
- struct crypto_sync_skcipher *cts_tfm, *cbc_tfm;
- const struct gss_krb5_enctype *gk5e;
- struct xdr_netobj Ke, Ki, checksum;
- u8 usage_data[GSS_KRB5_K5CLENGTH];
- struct xdr_netobj usage = {
- .data = usage_data,
- .len = sizeof(usage_data),
- };
- struct crypto_ahash *ahash_tfm;
- unsigned int blocksize;
- struct xdr_buf buf;
- void *text;
- size_t len;
- u32 err;
-
- /* Arrange */
- gk5e = gss_krb5_lookup_enctype(param->enctype);
- if (!gk5e)
- kunit_skip(test, "Encryption type is not available");
-
- memset(usage_data, 0, sizeof(usage_data));
- usage.data[3] = param->constant;
-
- Ke.len = gk5e->Ke_length;
- Ke.data = kunit_kzalloc(test, Ke.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, Ke.data);
- usage.data[4] = KEY_USAGE_SEED_ENCRYPTION;
- err = gk5e->derive_key(gk5e, param->base_key, &Ke, &usage, GFP_KERNEL);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- cbc_tfm = crypto_alloc_sync_skcipher(gk5e->aux_cipher, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cbc_tfm);
- err = crypto_sync_skcipher_setkey(cbc_tfm, Ke.data, Ke.len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- cts_tfm = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cts_tfm);
- err = crypto_sync_skcipher_setkey(cts_tfm, Ke.data, Ke.len);
- KUNIT_ASSERT_EQ(test, err, 0);
- blocksize = crypto_sync_skcipher_blocksize(cts_tfm);
-
- len = param->confounder->len + param->plaintext->len + blocksize;
- text = kunit_kzalloc(test, len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, text);
- memcpy(text, param->confounder->data, param->confounder->len);
- memcpy(text + param->confounder->len, param->plaintext->data,
- param->plaintext->len);
-
- memset(&buf, 0, sizeof(buf));
- buf.head[0].iov_base = text;
- buf.head[0].iov_len = param->confounder->len + param->plaintext->len;
- buf.len = buf.head[0].iov_len;
-
- checksum.len = gk5e->cksumlength;
- checksum.data = kunit_kzalloc(test, checksum.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, checksum.data);
-
- Ki.len = gk5e->Ki_length;
- Ki.data = kunit_kzalloc(test, Ki.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, Ki.data);
- usage.data[4] = KEY_USAGE_SEED_INTEGRITY;
- err = gk5e->derive_key(gk5e, param->base_key, &Ki,
- &usage, GFP_KERNEL);
- KUNIT_ASSERT_EQ(test, err, 0);
- ahash_tfm = crypto_alloc_ahash(gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ahash_tfm);
- err = crypto_ahash_setkey(ahash_tfm, Ki.data, Ki.len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Act */
- err = gss_krb5_checksum(ahash_tfm, NULL, 0, &buf, 0, &checksum);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- err = krb5_cbc_cts_encrypt(cts_tfm, cbc_tfm, 0, &buf, NULL, NULL, 0);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Assert */
- KUNIT_EXPECT_EQ_MSG(test, param->expected_result->len,
- buf.len + checksum.len,
- "ciphertext length mismatch");
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_result->data,
- buf.head[0].iov_base,
- buf.len,
- "encrypted result mismatch");
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_result->data +
- (param->expected_result->len - checksum.len),
- checksum.data,
- checksum.len,
- "HMAC mismatch");
-
- crypto_free_ahash(ahash_tfm);
- crypto_free_sync_skcipher(cts_tfm);
- crypto_free_sync_skcipher(cbc_tfm);
-}
-
-static struct kunit_case rfc6803_test_cases[] = {
- {
- .name = "RFC 6803 key derivation",
- .run_case = kdf_case,
- .generate_params = rfc6803_kdf_gen_params,
- },
- {
- .name = "RFC 6803 checksum",
- .run_case = checksum_case,
- .generate_params = rfc6803_checksum_gen_params,
- },
- {
- .name = "RFC 6803 encryption",
- .run_case = rfc6803_encrypt_case,
- .generate_params = rfc6803_encrypt_gen_params,
- },
- {}
-};
-
-static struct kunit_suite rfc6803_suite = {
- .name = "RFC 6803 suite",
- .test_cases = rfc6803_test_cases,
-};
-
-/*
- * From RFC 8009 Appendix A. Test Vectors
- *
- * Sample results for SHA-2 enctype key derivation
- *
- * This test material is copyright (c) 2016 IETF Trust and the
- * persons identified as the document authors. All rights reserved.
- */
-
-DEFINE_HEX_XDR_NETOBJ(aes128_cts_hmac_sha256_128_basekey,
- 0x37, 0x05, 0xd9, 0x60, 0x80, 0xc1, 0x77, 0x28,
- 0xa0, 0xe8, 0x00, 0xea, 0xb6, 0xe0, 0xd2, 0x3c
-);
-DEFINE_HEX_XDR_NETOBJ(aes128_cts_hmac_sha256_128_Kc,
- 0xb3, 0x1a, 0x01, 0x8a, 0x48, 0xf5, 0x47, 0x76,
- 0xf4, 0x03, 0xe9, 0xa3, 0x96, 0x32, 0x5d, 0xc3
-);
-DEFINE_HEX_XDR_NETOBJ(aes128_cts_hmac_sha256_128_Ke,
- 0x9b, 0x19, 0x7d, 0xd1, 0xe8, 0xc5, 0x60, 0x9d,
- 0x6e, 0x67, 0xc3, 0xe3, 0x7c, 0x62, 0xc7, 0x2e
-);
-DEFINE_HEX_XDR_NETOBJ(aes128_cts_hmac_sha256_128_Ki,
- 0x9f, 0xda, 0x0e, 0x56, 0xab, 0x2d, 0x85, 0xe1,
- 0x56, 0x9a, 0x68, 0x86, 0x96, 0xc2, 0x6a, 0x6c
-);
-
-DEFINE_HEX_XDR_NETOBJ(aes256_cts_hmac_sha384_192_basekey,
- 0x6d, 0x40, 0x4d, 0x37, 0xfa, 0xf7, 0x9f, 0x9d,
- 0xf0, 0xd3, 0x35, 0x68, 0xd3, 0x20, 0x66, 0x98,
- 0x00, 0xeb, 0x48, 0x36, 0x47, 0x2e, 0xa8, 0xa0,
- 0x26, 0xd1, 0x6b, 0x71, 0x82, 0x46, 0x0c, 0x52
-);
-DEFINE_HEX_XDR_NETOBJ(aes256_cts_hmac_sha384_192_Kc,
- 0xef, 0x57, 0x18, 0xbe, 0x86, 0xcc, 0x84, 0x96,
- 0x3d, 0x8b, 0xbb, 0x50, 0x31, 0xe9, 0xf5, 0xc4,
- 0xba, 0x41, 0xf2, 0x8f, 0xaf, 0x69, 0xe7, 0x3d
-);
-DEFINE_HEX_XDR_NETOBJ(aes256_cts_hmac_sha384_192_Ke,
- 0x56, 0xab, 0x22, 0xbe, 0xe6, 0x3d, 0x82, 0xd7,
- 0xbc, 0x52, 0x27, 0xf6, 0x77, 0x3f, 0x8e, 0xa7,
- 0xa5, 0xeb, 0x1c, 0x82, 0x51, 0x60, 0xc3, 0x83,
- 0x12, 0x98, 0x0c, 0x44, 0x2e, 0x5c, 0x7e, 0x49
-);
-DEFINE_HEX_XDR_NETOBJ(aes256_cts_hmac_sha384_192_Ki,
- 0x69, 0xb1, 0x65, 0x14, 0xe3, 0xcd, 0x8e, 0x56,
- 0xb8, 0x20, 0x10, 0xd5, 0xc7, 0x30, 0x12, 0xb6,
- 0x22, 0xc4, 0xd0, 0x0f, 0xfc, 0x23, 0xed, 0x1f
-);
-
-static const struct gss_krb5_test_param rfc8009_kdf_test_params[] = {
- {
- .desc = "Derive Kc subkey for aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .usage = &usage_checksum,
- .expected_result = &aes128_cts_hmac_sha256_128_Kc,
- },
- {
- .desc = "Derive Ke subkey for aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .usage = &usage_encryption,
- .expected_result = &aes128_cts_hmac_sha256_128_Ke,
- },
- {
- .desc = "Derive Ki subkey for aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .usage = &usage_integrity,
- .expected_result = &aes128_cts_hmac_sha256_128_Ki,
- },
- {
- .desc = "Derive Kc subkey for aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .usage = &usage_checksum,
- .expected_result = &aes256_cts_hmac_sha384_192_Kc,
- },
- {
- .desc = "Derive Ke subkey for aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .usage = &usage_encryption,
- .expected_result = &aes256_cts_hmac_sha384_192_Ke,
- },
- {
- .desc = "Derive Ki subkey for aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .usage = &usage_integrity,
- .expected_result = &aes256_cts_hmac_sha384_192_Ki,
- },
-};
-
-/* Creates the function rfc8009_kdf_gen_params */
-KUNIT_ARRAY_PARAM(rfc8009_kdf, rfc8009_kdf_test_params, gss_krb5_get_desc);
-
-/*
- * From RFC 8009 Appendix A. Test Vectors
- *
- * These sample checksums use the above sample key derivation results,
- * including use of the same base-key and key usage values.
- *
- * This test material is copyright (c) 2016 IETF Trust and the
- * persons identified as the document authors. All rights reserved.
- */
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_checksum_plaintext,
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_checksum_test1_expected_result,
- 0xd7, 0x83, 0x67, 0x18, 0x66, 0x43, 0xd6, 0x7b,
- 0x41, 0x1c, 0xba, 0x91, 0x39, 0xfc, 0x1d, 0xee
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_checksum_test2_expected_result,
- 0x45, 0xee, 0x79, 0x15, 0x67, 0xee, 0xfc, 0xa3,
- 0x7f, 0x4a, 0xc1, 0xe0, 0x22, 0x2d, 0xe8, 0x0d,
- 0x43, 0xc3, 0xbf, 0xa0, 0x66, 0x99, 0x67, 0x2a
-);
-
-static const struct gss_krb5_test_param rfc8009_checksum_test_params[] = {
- {
- .desc = "Checksum with aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .usage = &usage_checksum,
- .plaintext = &rfc8009_checksum_plaintext,
- .expected_result = &rfc8009_checksum_test1_expected_result,
- },
- {
- .desc = "Checksum with aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .usage = &usage_checksum,
- .plaintext = &rfc8009_checksum_plaintext,
- .expected_result = &rfc8009_checksum_test2_expected_result,
- },
-};
-
-/* Creates the function rfc8009_checksum_gen_params */
-KUNIT_ARRAY_PARAM(rfc8009_checksum, rfc8009_checksum_test_params,
- gss_krb5_get_desc);
-
-/*
- * From RFC 8009 Appendix A. Test Vectors
- *
- * Sample encryptions (all using the default cipher state):
- * --------------------------------------------------------
- *
- * These sample encryptions use the above sample key derivation results,
- * including use of the same base-key and key usage values.
- *
- * This test material is copyright (c) 2016 IETF Trust and the
- * persons identified as the document authors. All rights reserved.
- */
-
-static const struct xdr_netobj rfc8009_enc_empty_plaintext = {
- .len = 0,
-};
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_short_plaintext,
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_block_plaintext,
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_long_plaintext,
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test1_confounder,
- 0x7e, 0x58, 0x95, 0xea, 0xf2, 0x67, 0x24, 0x35,
- 0xba, 0xd8, 0x17, 0xf5, 0x45, 0xa3, 0x71, 0x48
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test1_expected_result,
- 0xef, 0x85, 0xfb, 0x89, 0x0b, 0xb8, 0x47, 0x2f,
- 0x4d, 0xab, 0x20, 0x39, 0x4d, 0xca, 0x78, 0x1d
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test1_expected_hmac,
- 0xad, 0x87, 0x7e, 0xda, 0x39, 0xd5, 0x0c, 0x87,
- 0x0c, 0x0d, 0x5a, 0x0a, 0x8e, 0x48, 0xc7, 0x18
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test2_confounder,
- 0x7b, 0xca, 0x28, 0x5e, 0x2f, 0xd4, 0x13, 0x0f,
- 0xb5, 0x5b, 0x1a, 0x5c, 0x83, 0xbc, 0x5b, 0x24
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test2_expected_result,
- 0x84, 0xd7, 0xf3, 0x07, 0x54, 0xed, 0x98, 0x7b,
- 0xab, 0x0b, 0xf3, 0x50, 0x6b, 0xeb, 0x09, 0xcf,
- 0xb5, 0x54, 0x02, 0xce, 0xf7, 0xe6
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test2_expected_hmac,
- 0x87, 0x7c, 0xe9, 0x9e, 0x24, 0x7e, 0x52, 0xd1,
- 0x6e, 0xd4, 0x42, 0x1d, 0xfd, 0xf8, 0x97, 0x6c
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test3_confounder,
- 0x56, 0xab, 0x21, 0x71, 0x3f, 0xf6, 0x2c, 0x0a,
- 0x14, 0x57, 0x20, 0x0f, 0x6f, 0xa9, 0x94, 0x8f
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test3_expected_result,
- 0x35, 0x17, 0xd6, 0x40, 0xf5, 0x0d, 0xdc, 0x8a,
- 0xd3, 0x62, 0x87, 0x22, 0xb3, 0x56, 0x9d, 0x2a,
- 0xe0, 0x74, 0x93, 0xfa, 0x82, 0x63, 0x25, 0x40,
- 0x80, 0xea, 0x65, 0xc1, 0x00, 0x8e, 0x8f, 0xc2
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test3_expected_hmac,
- 0x95, 0xfb, 0x48, 0x52, 0xe7, 0xd8, 0x3e, 0x1e,
- 0x7c, 0x48, 0xc3, 0x7e, 0xeb, 0xe6, 0xb0, 0xd3
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test4_confounder,
- 0xa7, 0xa4, 0xe2, 0x9a, 0x47, 0x28, 0xce, 0x10,
- 0x66, 0x4f, 0xb6, 0x4e, 0x49, 0xad, 0x3f, 0xac
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test4_expected_result,
- 0x72, 0x0f, 0x73, 0xb1, 0x8d, 0x98, 0x59, 0xcd,
- 0x6c, 0xcb, 0x43, 0x46, 0x11, 0x5c, 0xd3, 0x36,
- 0xc7, 0x0f, 0x58, 0xed, 0xc0, 0xc4, 0x43, 0x7c,
- 0x55, 0x73, 0x54, 0x4c, 0x31, 0xc8, 0x13, 0xbc,
- 0xe1, 0xe6, 0xd0, 0x72, 0xc1
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test4_expected_hmac,
- 0x86, 0xb3, 0x9a, 0x41, 0x3c, 0x2f, 0x92, 0xca,
- 0x9b, 0x83, 0x34, 0xa2, 0x87, 0xff, 0xcb, 0xfc
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test5_confounder,
- 0xf7, 0x64, 0xe9, 0xfa, 0x15, 0xc2, 0x76, 0x47,
- 0x8b, 0x2c, 0x7d, 0x0c, 0x4e, 0x5f, 0x58, 0xe4
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test5_expected_result,
- 0x41, 0xf5, 0x3f, 0xa5, 0xbf, 0xe7, 0x02, 0x6d,
- 0x91, 0xfa, 0xf9, 0xbe, 0x95, 0x91, 0x95, 0xa0
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test5_expected_hmac,
- 0x58, 0x70, 0x72, 0x73, 0xa9, 0x6a, 0x40, 0xf0,
- 0xa0, 0x19, 0x60, 0x62, 0x1a, 0xc6, 0x12, 0x74,
- 0x8b, 0x9b, 0xbf, 0xbe, 0x7e, 0xb4, 0xce, 0x3c
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test6_confounder,
- 0xb8, 0x0d, 0x32, 0x51, 0xc1, 0xf6, 0x47, 0x14,
- 0x94, 0x25, 0x6f, 0xfe, 0x71, 0x2d, 0x0b, 0x9a
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test6_expected_result,
- 0x4e, 0xd7, 0xb3, 0x7c, 0x2b, 0xca, 0xc8, 0xf7,
- 0x4f, 0x23, 0xc1, 0xcf, 0x07, 0xe6, 0x2b, 0xc7,
- 0xb7, 0x5f, 0xb3, 0xf6, 0x37, 0xb9
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test6_expected_hmac,
- 0xf5, 0x59, 0xc7, 0xf6, 0x64, 0xf6, 0x9e, 0xab,
- 0x7b, 0x60, 0x92, 0x23, 0x75, 0x26, 0xea, 0x0d,
- 0x1f, 0x61, 0xcb, 0x20, 0xd6, 0x9d, 0x10, 0xf2
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test7_confounder,
- 0x53, 0xbf, 0x8a, 0x0d, 0x10, 0x52, 0x65, 0xd4,
- 0xe2, 0x76, 0x42, 0x86, 0x24, 0xce, 0x5e, 0x63
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test7_expected_result,
- 0xbc, 0x47, 0xff, 0xec, 0x79, 0x98, 0xeb, 0x91,
- 0xe8, 0x11, 0x5c, 0xf8, 0xd1, 0x9d, 0xac, 0x4b,
- 0xbb, 0xe2, 0xe1, 0x63, 0xe8, 0x7d, 0xd3, 0x7f,
- 0x49, 0xbe, 0xca, 0x92, 0x02, 0x77, 0x64, 0xf6
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test7_expected_hmac,
- 0x8c, 0xf5, 0x1f, 0x14, 0xd7, 0x98, 0xc2, 0x27,
- 0x3f, 0x35, 0xdf, 0x57, 0x4d, 0x1f, 0x93, 0x2e,
- 0x40, 0xc4, 0xff, 0x25, 0x5b, 0x36, 0xa2, 0x66
-);
-
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test8_confounder,
- 0x76, 0x3e, 0x65, 0x36, 0x7e, 0x86, 0x4f, 0x02,
- 0xf5, 0x51, 0x53, 0xc7, 0xe3, 0xb5, 0x8a, 0xf1
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test8_expected_result,
- 0x40, 0x01, 0x3e, 0x2d, 0xf5, 0x8e, 0x87, 0x51,
- 0x95, 0x7d, 0x28, 0x78, 0xbc, 0xd2, 0xd6, 0xfe,
- 0x10, 0x1c, 0xcf, 0xd5, 0x56, 0xcb, 0x1e, 0xae,
- 0x79, 0xdb, 0x3c, 0x3e, 0xe8, 0x64, 0x29, 0xf2,
- 0xb2, 0xa6, 0x02, 0xac, 0x86
-);
-DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test8_expected_hmac,
- 0xfe, 0xf6, 0xec, 0xb6, 0x47, 0xd6, 0x29, 0x5f,
- 0xae, 0x07, 0x7a, 0x1f, 0xeb, 0x51, 0x75, 0x08,
- 0xd2, 0xc1, 0x6b, 0x41, 0x92, 0xe0, 0x1f, 0x62
-);
-
-static const struct gss_krb5_test_param rfc8009_encrypt_test_params[] = {
- {
- .desc = "Encrypt empty plaintext with aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .plaintext = &rfc8009_enc_empty_plaintext,
- .confounder = &rfc8009_enc_test1_confounder,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .expected_result = &rfc8009_enc_test1_expected_result,
- .expected_hmac = &rfc8009_enc_test1_expected_hmac,
- },
- {
- .desc = "Encrypt short plaintext with aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .plaintext = &rfc8009_enc_short_plaintext,
- .confounder = &rfc8009_enc_test2_confounder,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .expected_result = &rfc8009_enc_test2_expected_result,
- .expected_hmac = &rfc8009_enc_test2_expected_hmac,
- },
- {
- .desc = "Encrypt block plaintext with aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .plaintext = &rfc8009_enc_block_plaintext,
- .confounder = &rfc8009_enc_test3_confounder,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .expected_result = &rfc8009_enc_test3_expected_result,
- .expected_hmac = &rfc8009_enc_test3_expected_hmac,
- },
- {
- .desc = "Encrypt long plaintext with aes128-cts-hmac-sha256-128",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .plaintext = &rfc8009_enc_long_plaintext,
- .confounder = &rfc8009_enc_test4_confounder,
- .base_key = &aes128_cts_hmac_sha256_128_basekey,
- .expected_result = &rfc8009_enc_test4_expected_result,
- .expected_hmac = &rfc8009_enc_test4_expected_hmac,
- },
- {
- .desc = "Encrypt empty plaintext with aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .plaintext = &rfc8009_enc_empty_plaintext,
- .confounder = &rfc8009_enc_test5_confounder,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .expected_result = &rfc8009_enc_test5_expected_result,
- .expected_hmac = &rfc8009_enc_test5_expected_hmac,
- },
- {
- .desc = "Encrypt short plaintext with aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .plaintext = &rfc8009_enc_short_plaintext,
- .confounder = &rfc8009_enc_test6_confounder,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .expected_result = &rfc8009_enc_test6_expected_result,
- .expected_hmac = &rfc8009_enc_test6_expected_hmac,
- },
- {
- .desc = "Encrypt block plaintext with aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .plaintext = &rfc8009_enc_block_plaintext,
- .confounder = &rfc8009_enc_test7_confounder,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .expected_result = &rfc8009_enc_test7_expected_result,
- .expected_hmac = &rfc8009_enc_test7_expected_hmac,
- },
- {
- .desc = "Encrypt long plaintext with aes256-cts-hmac-sha384-192",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .plaintext = &rfc8009_enc_long_plaintext,
- .confounder = &rfc8009_enc_test8_confounder,
- .base_key = &aes256_cts_hmac_sha384_192_basekey,
- .expected_result = &rfc8009_enc_test8_expected_result,
- .expected_hmac = &rfc8009_enc_test8_expected_hmac,
- },
-};
-
-/* Creates the function rfc8009_encrypt_gen_params */
-KUNIT_ARRAY_PARAM(rfc8009_encrypt, rfc8009_encrypt_test_params,
- gss_krb5_get_desc);
-
-static void rfc8009_encrypt_case(struct kunit *test)
-{
- const struct gss_krb5_test_param *param = test->param_value;
- struct crypto_sync_skcipher *cts_tfm, *cbc_tfm;
- const struct gss_krb5_enctype *gk5e;
- struct xdr_netobj Ke, Ki, checksum;
- u8 usage_data[GSS_KRB5_K5CLENGTH];
- struct xdr_netobj usage = {
- .data = usage_data,
- .len = sizeof(usage_data),
- };
- struct crypto_ahash *ahash_tfm;
- struct xdr_buf buf;
- void *text;
- size_t len;
- u32 err;
-
- /* Arrange */
- gk5e = gss_krb5_lookup_enctype(param->enctype);
- if (!gk5e)
- kunit_skip(test, "Encryption type is not available");
-
- *(__be32 *)usage.data = cpu_to_be32(2);
-
- Ke.len = gk5e->Ke_length;
- Ke.data = kunit_kzalloc(test, Ke.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, Ke.data);
- usage.data[4] = KEY_USAGE_SEED_ENCRYPTION;
- err = gk5e->derive_key(gk5e, param->base_key, &Ke,
- &usage, GFP_KERNEL);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- cbc_tfm = crypto_alloc_sync_skcipher(gk5e->aux_cipher, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cbc_tfm);
- err = crypto_sync_skcipher_setkey(cbc_tfm, Ke.data, Ke.len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- cts_tfm = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cts_tfm);
- err = crypto_sync_skcipher_setkey(cts_tfm, Ke.data, Ke.len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- len = param->confounder->len + param->plaintext->len;
- text = kunit_kzalloc(test, len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, text);
- memcpy(text, param->confounder->data, param->confounder->len);
- memcpy(text + param->confounder->len, param->plaintext->data,
- param->plaintext->len);
-
- memset(&buf, 0, sizeof(buf));
- buf.head[0].iov_base = text;
- buf.head[0].iov_len = param->confounder->len + param->plaintext->len;
- buf.len = buf.head[0].iov_len;
-
- checksum.len = gk5e->cksumlength;
- checksum.data = kunit_kzalloc(test, checksum.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, checksum.data);
-
- Ki.len = gk5e->Ki_length;
- Ki.data = kunit_kzalloc(test, Ki.len, GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, Ki.data);
- usage.data[4] = KEY_USAGE_SEED_INTEGRITY;
- err = gk5e->derive_key(gk5e, param->base_key, &Ki,
- &usage, GFP_KERNEL);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- ahash_tfm = crypto_alloc_ahash(gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ahash_tfm);
- err = crypto_ahash_setkey(ahash_tfm, Ki.data, Ki.len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Act */
- err = krb5_cbc_cts_encrypt(cts_tfm, cbc_tfm, 0, &buf, NULL, NULL, 0);
- KUNIT_ASSERT_EQ(test, err, 0);
- err = krb5_etm_checksum(cts_tfm, ahash_tfm, &buf, 0, &checksum);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Assert */
- KUNIT_EXPECT_EQ_MSG(test,
- param->expected_result->len, buf.len,
- "ciphertext length mismatch");
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_result->data,
- buf.head[0].iov_base,
- param->expected_result->len,
- "ciphertext mismatch");
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->expected_hmac->data,
- checksum.data,
- checksum.len,
- "HMAC mismatch");
-
- crypto_free_ahash(ahash_tfm);
- crypto_free_sync_skcipher(cts_tfm);
- crypto_free_sync_skcipher(cbc_tfm);
-}
-
-static struct kunit_case rfc8009_test_cases[] = {
- {
- .name = "RFC 8009 key derivation",
- .run_case = kdf_case,
- .generate_params = rfc8009_kdf_gen_params,
- },
- {
- .name = "RFC 8009 checksum",
- .run_case = checksum_case,
- .generate_params = rfc8009_checksum_gen_params,
- },
- {
- .name = "RFC 8009 encryption",
- .run_case = rfc8009_encrypt_case,
- .generate_params = rfc8009_encrypt_gen_params,
- },
- {}
-};
-
-static struct kunit_suite rfc8009_suite = {
- .name = "RFC 8009 suite",
- .test_cases = rfc8009_test_cases,
-};
-
-/*
- * Encryption self-tests
- */
-
-DEFINE_STR_XDR_NETOBJ(encrypt_selftest_plaintext,
- "This is the plaintext for the encryption self-test.");
-
-static const struct gss_krb5_test_param encrypt_selftest_params[] = {
- {
- .desc = "aes128-cts-hmac-sha1-96 encryption self-test",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &encrypt_selftest_plaintext,
- },
- {
- .desc = "aes256-cts-hmac-sha1-96 encryption self-test",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA1_96,
- .Ke = &rfc3962_encryption_key,
- .plaintext = &encrypt_selftest_plaintext,
- },
- {
- .desc = "camellia128-cts-cmac encryption self-test",
- .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC,
- .Ke = &camellia128_cts_cmac_Ke,
- .plaintext = &encrypt_selftest_plaintext,
- },
- {
- .desc = "camellia256-cts-cmac encryption self-test",
- .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC,
- .Ke = &camellia256_cts_cmac_Ke,
- .plaintext = &encrypt_selftest_plaintext,
- },
- {
- .desc = "aes128-cts-hmac-sha256-128 encryption self-test",
- .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128,
- .Ke = &aes128_cts_hmac_sha256_128_Ke,
- .plaintext = &encrypt_selftest_plaintext,
- },
- {
- .desc = "aes256-cts-hmac-sha384-192 encryption self-test",
- .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192,
- .Ke = &aes256_cts_hmac_sha384_192_Ke,
- .plaintext = &encrypt_selftest_plaintext,
- },
-};
-
-/* Creates the function encrypt_selftest_gen_params */
-KUNIT_ARRAY_PARAM(encrypt_selftest, encrypt_selftest_params,
- gss_krb5_get_desc);
-
-/*
- * Encrypt and decrypt plaintext, and ensure the input plaintext
- * matches the output plaintext. A confounder is not added in this
- * case.
- */
-static void encrypt_selftest_case(struct kunit *test)
-{
- const struct gss_krb5_test_param *param = test->param_value;
- struct crypto_sync_skcipher *cts_tfm, *cbc_tfm;
- const struct gss_krb5_enctype *gk5e;
- struct xdr_buf buf;
- void *text;
- int err;
-
- /* Arrange */
- gk5e = gss_krb5_lookup_enctype(param->enctype);
- if (!gk5e)
- kunit_skip(test, "Encryption type is not available");
-
- cbc_tfm = crypto_alloc_sync_skcipher(gk5e->aux_cipher, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cbc_tfm);
- err = crypto_sync_skcipher_setkey(cbc_tfm, param->Ke->data, param->Ke->len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- cts_tfm = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cts_tfm);
- err = crypto_sync_skcipher_setkey(cts_tfm, param->Ke->data, param->Ke->len);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- text = kunit_kzalloc(test, roundup(param->plaintext->len,
- crypto_sync_skcipher_blocksize(cbc_tfm)),
- GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, text);
-
- memcpy(text, param->plaintext->data, param->plaintext->len);
- memset(&buf, 0, sizeof(buf));
- buf.head[0].iov_base = text;
- buf.head[0].iov_len = param->plaintext->len;
- buf.len = buf.head[0].iov_len;
-
- /* Act */
- err = krb5_cbc_cts_encrypt(cts_tfm, cbc_tfm, 0, &buf, NULL, NULL, 0);
- KUNIT_ASSERT_EQ(test, err, 0);
- err = krb5_cbc_cts_decrypt(cts_tfm, cbc_tfm, 0, &buf);
- KUNIT_ASSERT_EQ(test, err, 0);
-
- /* Assert */
- KUNIT_EXPECT_EQ_MSG(test,
- param->plaintext->len, buf.len,
- "length mismatch");
- KUNIT_EXPECT_MEMEQ_MSG(test,
- param->plaintext->data,
- buf.head[0].iov_base,
- buf.len,
- "plaintext mismatch");
-
- crypto_free_sync_skcipher(cts_tfm);
- crypto_free_sync_skcipher(cbc_tfm);
-}
-
-static struct kunit_case encryption_test_cases[] = {
- {
- .name = "Encryption self-tests",
- .run_case = encrypt_selftest_case,
- .generate_params = encrypt_selftest_gen_params,
- },
- {}
-};
-
-static struct kunit_suite encryption_test_suite = {
- .name = "Encryption test suite",
- .test_cases = encryption_test_cases,
-};
-
-kunit_test_suites(&rfc3961_suite,
- &rfc3962_suite,
- &rfc6803_suite,
- &rfc8009_suite,
- &encryption_test_suite);
-
-MODULE_DESCRIPTION("Test RPCSEC GSS Kerberos 5 functions");
-MODULE_LICENSE("GPL");
diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c
index ef0e6af9fc95..b5fb70419faa 100644
--- a/net/sunrpc/auth_gss/gss_krb5_unseal.c
+++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c
@@ -60,6 +60,8 @@
#include <linux/types.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_krb5.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include "gss_krb5_internal.h"
@@ -71,18 +73,19 @@ u32
gss_krb5_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 = ctx->gk5e->cksumlength,
- .data = cksumdata,
- };
+ const struct krb5_enctype *krb5 = ctx->krb5e;
+ struct crypto_shash *shash = ctx->initiate ?
+ ctx->acceptor_sign_shash : ctx->initiator_sign_shash;
+ unsigned int cksum_len = krb5->cksum_len;
+ struct scatterlist sg_head[XDR_BUF_TO_SG_NENTS];
+ struct scatterlist *sg_overflow;
+ size_t mic_offset, mic_len;
u8 *ptr = read_token->data;
__be16 be16_ptr;
time64_t now;
u8 flags;
- int i;
+ int nsg, i;
+ int ret;
dprintk("RPC: %s\n", __func__);
@@ -104,13 +107,20 @@ gss_krb5_verify_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *message_buffer,
if (ptr[i] != 0xff)
return GSS_S_DEFECTIVE_TOKEN;
- if (gss_krb5_checksum(tfm, ptr, GSS_KRB5_TOK_HDR_LEN,
- message_buffer, 0, &cksumobj))
+ nsg = gss_krb5_mic_build_sg(message_buffer,
+ ptr + GSS_KRB5_TOK_HDR_LEN,
+ cksum_len, ptr,
+ sg_head, &sg_overflow);
+ if (nsg < 0)
return GSS_S_FAILURE;
- if (memcmp(cksumobj.data, ptr + GSS_KRB5_TOK_HDR_LEN,
- ctx->gk5e->cksumlength))
- return GSS_S_BAD_SIG;
+ mic_offset = 0;
+ mic_len = cksum_len + message_buffer->len + GSS_KRB5_TOK_HDR_LEN;
+ ret = crypto_krb5_verify_mic(krb5, shash, NULL, sg_head, nsg,
+ &mic_offset, &mic_len);
+ kfree(sg_overflow);
+ if (ret)
+ return gss_krb5_errno_to_status(ret);
/* it got through unscathed. Make sure the context is unexpired */
now = ktime_get_real_seconds();
diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c
index b3e1738ff6bf..ac4b32df42b9 100644
--- a/net/sunrpc/auth_gss/gss_krb5_wrap.c
+++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c
@@ -28,7 +28,6 @@
* SUCH DAMAGES.
*/
-#include <crypto/skcipher.h>
#include <linux/types.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_krb5.h>
@@ -112,9 +111,9 @@ gss_krb5_wrap_v2(struct krb5_ctx *kctx, int offset,
*ptr++ = (unsigned char) ((KG2_TOK_WRAP>>8) & 0xff);
*ptr++ = (unsigned char) (KG2_TOK_WRAP & 0xff);
- if ((kctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0)
+ if (!kctx->initiate)
flags |= KG2_TOKEN_FLAG_SENTBYACCEPTOR;
- if ((kctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY) != 0)
+ if (kctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY)
flags |= KG2_TOKEN_FLAG_ACCEPTORSUBKEY;
/* We always do confidentiality in wrap tokens */
flags |= KG2_TOKEN_FLAG_SEALED;
@@ -130,7 +129,7 @@ gss_krb5_wrap_v2(struct krb5_ctx *kctx, int offset,
be64ptr = (__be64 *)be16ptr;
*be64ptr = cpu_to_be64(atomic64_fetch_inc(&kctx->seq_send64));
- err = (*kctx->gk5e->encrypt)(kctx, offset, buf, pages);
+ err = gss_krb5_aead_encrypt(kctx, offset, buf, pages);
if (err)
return err;
@@ -184,10 +183,10 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
if (rrc != 0)
rotate_left(offset + 16, buf, rrc);
- err = (*kctx->gk5e->decrypt)(kctx, offset, len, buf,
- &headskip, &tailskip);
+ err = gss_krb5_aead_decrypt(kctx, offset, len, buf,
+ &headskip, &tailskip);
if (err)
- return GSS_S_FAILURE;
+ return err;
/*
* Retrieve the decrypted gss token header and verify
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 161d02cc1c2c..d14209031e18 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -206,7 +206,7 @@ static struct cache_head *rsi_alloc(void)
static int rsi_upcall(struct cache_detail *cd, struct cache_head *h)
{
- return sunrpc_cache_pipe_upcall_timeout(cd, h);
+ return sunrpc_cache_upcall_warn(cd, h);
}
static void rsi_request(struct cache_detail *cd,
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 27dd6b58b8ff..391037f15292 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -33,9 +33,11 @@
#include <linux/sunrpc/cache.h>
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
+#include <net/genetlink.h>
#include <trace/events/sunrpc.h>
#include "netns.h"
+#include "netlink.h"
#include "fail.h"
#define RPCDBG_FACILITY RPCDBG_CACHE
@@ -1195,12 +1197,12 @@ static bool cache_listeners_exist(struct cache_detail *detail)
}
/*
- * register an upcall request to user-space and queue it up for read() by the
- * upcall daemon.
+ * register an upcall request to user-space and queue it up to be fetched by
+ * the upcall daemon.
*
* Each request is at most one page long.
*/
-static int cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
+static int cache_do_upcall(struct cache_detail *detail, struct cache_head *h)
{
char *buf;
struct cache_request *crq;
@@ -1233,6 +1235,8 @@ static int cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
/* Lost a race, no longer PENDING, so don't enqueue */
ret = -EAGAIN;
spin_unlock(&detail->queue_lock);
+ if (ret != -EAGAIN && detail->cache_notify)
+ detail->cache_notify(detail, h);
wake_up(&detail->queue_wait);
if (ret == -EAGAIN) {
kfree(buf);
@@ -1241,25 +1245,25 @@ static int cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
return ret;
}
-int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
+int sunrpc_cache_upcall(struct cache_detail *detail, struct cache_head *h)
{
if (test_and_set_bit(CACHE_PENDING, &h->flags))
return 0;
- return cache_pipe_upcall(detail, h);
+ return cache_do_upcall(detail, h);
}
-EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall);
+EXPORT_SYMBOL_GPL(sunrpc_cache_upcall);
-int sunrpc_cache_pipe_upcall_timeout(struct cache_detail *detail,
- struct cache_head *h)
+int sunrpc_cache_upcall_warn(struct cache_detail *detail,
+ struct cache_head *h)
{
if (!cache_listeners_exist(detail)) {
warn_no_listener(detail);
trace_cache_entry_no_listener(detail, h);
return -EINVAL;
}
- return sunrpc_cache_pipe_upcall(detail, h);
+ return sunrpc_cache_upcall(detail, h);
}
-EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall_timeout);
+EXPORT_SYMBOL_GPL(sunrpc_cache_upcall_warn);
/*
* parse a message from user-space and pass it
@@ -1900,3 +1904,106 @@ void sunrpc_cache_unhash(struct cache_detail *cd, struct cache_head *h)
spin_unlock(&cd->hash_lock);
}
EXPORT_SYMBOL_GPL(sunrpc_cache_unhash);
+
+/**
+ * sunrpc_cache_requests_count - count pending upcall requests
+ * @cd: cache_detail to query
+ *
+ * Returns the number of requests on the cache's request list that
+ * still have CACHE_PENDING set.
+ */
+int sunrpc_cache_requests_count(struct cache_detail *cd)
+{
+ struct cache_request *crq;
+ int cnt = 0;
+
+ spin_lock(&cd->queue_lock);
+ list_for_each_entry(crq, &cd->requests, list) {
+ if (test_bit(CACHE_PENDING, &crq->item->flags))
+ cnt++;
+ }
+ spin_unlock(&cd->queue_lock);
+ return cnt;
+}
+EXPORT_SYMBOL_GPL(sunrpc_cache_requests_count);
+
+/**
+ * sunrpc_cache_requests_snapshot - snapshot pending upcall requests
+ * @cd: cache_detail to query
+ * @items: array to fill with cache_head pointers (caller-allocated)
+ * @seqnos: array to fill with sequence numbers (caller-allocated)
+ * @max: size of the arrays
+ * @min_seqno: only include entries with seqno > min_seqno (0 for all)
+ *
+ * Only entries with CACHE_PENDING set are included. Takes a reference
+ * on each cache_head via cache_get(). Caller must call cache_put()
+ * on each returned item when done.
+ *
+ * Returns the number of entries filled.
+ */
+int sunrpc_cache_requests_snapshot(struct cache_detail *cd,
+ struct cache_head **items,
+ u64 *seqnos, int max,
+ u64 min_seqno)
+{
+ struct cache_request *crq;
+ int i = 0;
+
+ spin_lock(&cd->queue_lock);
+ list_for_each_entry(crq, &cd->requests, list) {
+ if (i >= max)
+ break;
+ if (!test_bit(CACHE_PENDING, &crq->item->flags))
+ continue;
+ if (crq->seqno <= min_seqno)
+ continue;
+ items[i] = cache_get(crq->item);
+ seqnos[i] = crq->seqno;
+ i++;
+ }
+ spin_unlock(&cd->queue_lock);
+ return i;
+}
+EXPORT_SYMBOL_GPL(sunrpc_cache_requests_snapshot);
+
+/**
+ * sunrpc_cache_notify - send a netlink notification for a cache event
+ * @cd: cache_detail for the cache
+ * @h: cache_head entry (unused, reserved for future use)
+ * @cache_type: cache type identifier (e.g. SUNRPC_CACHE_TYPE_UNIX_GID)
+ *
+ * Sends a SUNRPC_CMD_CACHE_NOTIFY multicast message on the "exportd"
+ * group if any listeners are present. Returns 0 on success or a
+ * negative errno.
+ */
+int sunrpc_cache_notify(struct cache_detail *cd, struct cache_head *h,
+ u32 cache_type)
+{
+ struct genlmsghdr *hdr;
+ struct sk_buff *msg;
+
+ if (!genl_has_listeners(&sunrpc_nl_family, cd->net,
+ SUNRPC_NLGRP_EXPORTD))
+ return -ENOLINK;
+
+ msg = genlmsg_new(nla_total_size(sizeof(u32)), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, 0, 0, &sunrpc_nl_family, 0,
+ SUNRPC_CMD_CACHE_NOTIFY);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return -ENOMEM;
+ }
+
+ if (nla_put_u32(msg, SUNRPC_A_CACHE_NOTIFY_CACHE_TYPE, cache_type)) {
+ nlmsg_free(msg);
+ return -ENOMEM;
+ }
+
+ genlmsg_end(msg, hdr);
+ return genlmsg_multicast_netns(&sunrpc_nl_family, cd->net, msg, 0,
+ SUNRPC_NLGRP_EXPORTD, GFP_KERNEL);
+}
+EXPORT_SYMBOL_GPL(sunrpc_cache_notify);
diff --git a/net/sunrpc/netlink.c b/net/sunrpc/netlink.c
new file mode 100644
index 000000000000..ce09ecc0faa2
--- /dev/null
+++ b/net/sunrpc/netlink.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/sunrpc_cache.yaml */
+/* YNL-GEN kernel source */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include "netlink.h"
+
+#include <uapi/linux/sunrpc_netlink.h>
+
+/* Common nested types */
+const struct nla_policy sunrpc_ip_map_nl_policy[SUNRPC_A_IP_MAP_EXPIRY + 1] = {
+ [SUNRPC_A_IP_MAP_SEQNO] = { .type = NLA_U64, },
+ [SUNRPC_A_IP_MAP_CLASS] = { .type = NLA_NUL_STRING, },
+ [SUNRPC_A_IP_MAP_ADDR] = { .type = NLA_NUL_STRING, },
+ [SUNRPC_A_IP_MAP_DOMAIN] = { .type = NLA_NUL_STRING, },
+ [SUNRPC_A_IP_MAP_NEGATIVE] = { .type = NLA_FLAG, },
+ [SUNRPC_A_IP_MAP_EXPIRY] = { .type = NLA_U64, },
+};
+
+const struct nla_policy sunrpc_unix_gid_nl_policy[SUNRPC_A_UNIX_GID_EXPIRY + 1] = {
+ [SUNRPC_A_UNIX_GID_SEQNO] = { .type = NLA_U64, },
+ [SUNRPC_A_UNIX_GID_UID] = { .type = NLA_U32, },
+ [SUNRPC_A_UNIX_GID_GIDS] = { .type = NLA_U32, },
+ [SUNRPC_A_UNIX_GID_NEGATIVE] = { .type = NLA_FLAG, },
+ [SUNRPC_A_UNIX_GID_EXPIRY] = { .type = NLA_U64, },
+};
+
+/* SUNRPC_CMD_IP_MAP_SET_REQS - do */
+static const struct nla_policy sunrpc_ip_map_set_reqs_nl_policy[SUNRPC_A_IP_MAP_REQS_REQUESTS + 1] = {
+ [SUNRPC_A_IP_MAP_REQS_REQUESTS] = NLA_POLICY_NESTED(sunrpc_ip_map_nl_policy),
+};
+
+/* SUNRPC_CMD_UNIX_GID_SET_REQS - do */
+static const struct nla_policy sunrpc_unix_gid_set_reqs_nl_policy[SUNRPC_A_UNIX_GID_REQS_REQUESTS + 1] = {
+ [SUNRPC_A_UNIX_GID_REQS_REQUESTS] = NLA_POLICY_NESTED(sunrpc_unix_gid_nl_policy),
+};
+
+/* SUNRPC_CMD_CACHE_FLUSH - do */
+static const struct nla_policy sunrpc_cache_flush_nl_policy[SUNRPC_A_CACHE_FLUSH_MASK + 1] = {
+ [SUNRPC_A_CACHE_FLUSH_MASK] = NLA_POLICY_MASK(NLA_U32, 0x3),
+};
+
+/* Ops table for sunrpc */
+static const struct genl_split_ops sunrpc_nl_ops[] = {
+ {
+ .cmd = SUNRPC_CMD_IP_MAP_GET_REQS,
+ .dumpit = sunrpc_nl_ip_map_get_reqs_dumpit,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,
+ },
+ {
+ .cmd = SUNRPC_CMD_IP_MAP_SET_REQS,
+ .doit = sunrpc_nl_ip_map_set_reqs_doit,
+ .policy = sunrpc_ip_map_set_reqs_nl_policy,
+ .maxattr = SUNRPC_A_IP_MAP_REQS_REQUESTS,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = SUNRPC_CMD_UNIX_GID_GET_REQS,
+ .dumpit = sunrpc_nl_unix_gid_get_reqs_dumpit,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,
+ },
+ {
+ .cmd = SUNRPC_CMD_UNIX_GID_SET_REQS,
+ .doit = sunrpc_nl_unix_gid_set_reqs_doit,
+ .policy = sunrpc_unix_gid_set_reqs_nl_policy,
+ .maxattr = SUNRPC_A_UNIX_GID_REQS_REQUESTS,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = SUNRPC_CMD_CACHE_FLUSH,
+ .doit = sunrpc_nl_cache_flush_doit,
+ .policy = sunrpc_cache_flush_nl_policy,
+ .maxattr = SUNRPC_A_CACHE_FLUSH_MASK,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+};
+
+static const struct genl_multicast_group sunrpc_nl_mcgrps[] = {
+ [SUNRPC_NLGRP_NONE] = { "none", },
+ [SUNRPC_NLGRP_EXPORTD] = { "exportd", },
+};
+
+struct genl_family sunrpc_nl_family __ro_after_init = {
+ .name = SUNRPC_FAMILY_NAME,
+ .version = SUNRPC_FAMILY_VERSION,
+ .netnsok = true,
+ .parallel_ops = true,
+ .module = THIS_MODULE,
+ .split_ops = sunrpc_nl_ops,
+ .n_split_ops = ARRAY_SIZE(sunrpc_nl_ops),
+ .mcgrps = sunrpc_nl_mcgrps,
+ .n_mcgrps = ARRAY_SIZE(sunrpc_nl_mcgrps),
+};
diff --git a/net/sunrpc/netlink.h b/net/sunrpc/netlink.h
new file mode 100644
index 000000000000..2c1012303d48
--- /dev/null
+++ b/net/sunrpc/netlink.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/sunrpc_cache.yaml */
+/* YNL-GEN kernel header */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#ifndef _LINUX_SUNRPC_GEN_H
+#define _LINUX_SUNRPC_GEN_H
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include <uapi/linux/sunrpc_netlink.h>
+
+/* Common nested types */
+extern const struct nla_policy sunrpc_ip_map_nl_policy[SUNRPC_A_IP_MAP_EXPIRY + 1];
+extern const struct nla_policy sunrpc_unix_gid_nl_policy[SUNRPC_A_UNIX_GID_EXPIRY + 1];
+
+int sunrpc_nl_ip_map_get_reqs_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb);
+int sunrpc_nl_ip_map_set_reqs_doit(struct sk_buff *skb, struct genl_info *info);
+int sunrpc_nl_unix_gid_get_reqs_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb);
+int sunrpc_nl_unix_gid_set_reqs_doit(struct sk_buff *skb,
+ struct genl_info *info);
+int sunrpc_nl_cache_flush_doit(struct sk_buff *skb, struct genl_info *info);
+
+enum {
+ SUNRPC_NLGRP_NONE,
+ SUNRPC_NLGRP_EXPORTD,
+};
+
+extern struct genl_family sunrpc_nl_family;
+
+#endif /* _LINUX_SUNRPC_GEN_H */
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c
index bab6cab29405..ab88ce46afb5 100644
--- a/net/sunrpc/sunrpc_syms.c
+++ b/net/sunrpc/sunrpc_syms.c
@@ -23,9 +23,12 @@
#include <linux/sunrpc/rpc_pipe_fs.h>
#include <linux/sunrpc/xprtsock.h>
+#include <net/genetlink.h>
+
#include "sunrpc.h"
#include "sysfs.h"
#include "netns.h"
+#include "netlink.h"
unsigned int sunrpc_net_id;
EXPORT_SYMBOL_GPL(sunrpc_net_id);
@@ -108,6 +111,10 @@ init_sunrpc(void)
if (err)
goto out5;
+ err = genl_register_family(&sunrpc_nl_family);
+ if (err)
+ goto out6;
+
sunrpc_debugfs_init();
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
rpc_register_sysctl();
@@ -116,6 +123,8 @@ init_sunrpc(void)
init_socket_xprt(); /* clnt sock transport */
return 0;
+out6:
+ rpc_sysfs_exit();
out5:
unregister_rpc_pipefs();
out4:
@@ -131,6 +140,7 @@ out:
static void __exit
cleanup_sunrpc(void)
{
+ genl_unregister_family(&sunrpc_nl_family);
rpc_sysfs_exit();
rpc_cleanup_clids();
xprt_cleanup_ids();
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 576fa42e7abf..ae9ec4bf34f7 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1598,6 +1598,12 @@ static void svc_release_rqst(struct svc_rqst *rqstp)
if (procp && procp->pc_release)
procp->pc_release(rqstp);
+
+ /*
+ * A subsequent svc_release_rqst() on this rqstp must not
+ * re-invoke pc_release against released state.
+ */
+ rqstp->rq_procinfo = NULL;
}
/**
@@ -1616,6 +1622,9 @@ void svc_process(struct svc_rqst *rqstp)
svc_xprt_deferred_close(rqstp->rq_xprt);
#endif
+ /* Discard a stale release hook from a previous RPC. */
+ rqstp->rq_procinfo = NULL;
+
/*
* Setup response xdr_buf.
* Initially it has just one page
@@ -1672,6 +1681,7 @@ void svc_process_bc(struct rpc_rqst *req, struct svc_rqst *rqstp)
int proc_error;
/* Build the svc_rqst used by the common processing routine */
+ rqstp->rq_procinfo = NULL;
rqstp->rq_xid = req->rq_xid;
rqstp->rq_prot = req->rq_xprt->prot;
rqstp->rq_bc_net = req->rq_xprt->xprt_net;
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index b16e710926c1..63d1002e63e7 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -234,7 +234,19 @@ void svc_xprt_received(struct svc_xprt *xprt)
svc_xprt_get(xprt);
smp_mb__before_atomic();
clear_bit(XPT_BUSY, &xprt->xpt_flags);
- svc_xprt_enqueue(xprt);
+
+ /*
+ * Skip the enqueue when no actionable flags are set.
+ * Each producer both sets its flag (XPT_DATA, XPT_CLOSE,
+ * etc.) and calls svc_xprt_enqueue(); if a set_bit races
+ * with this check, the producer's own enqueue observes
+ * !XPT_BUSY and dispatches the transport.
+ */
+ if (READ_ONCE(xprt->xpt_flags) &
+ (BIT(XPT_CONN) | BIT(XPT_CLOSE) | BIT(XPT_HANDSHAKE) |
+ BIT(XPT_DATA) | BIT(XPT_DEFERRED)))
+ svc_xprt_enqueue(xprt);
+
svc_xprt_put(xprt);
}
EXPORT_SYMBOL_GPL(svc_xprt_received);
@@ -425,13 +437,35 @@ static bool svc_xprt_reserve_slot(struct svc_rqst *rqstp, struct svc_xprt *xprt)
return true;
}
+/*
+ * After a caller releases write-space or a request slot,
+ * re-enqueue the transport only when there is pending
+ * work that a thread could act on. The smp_mb() pairs
+ * with the smp_rmb() in svc_xprt_ready() and orders the
+ * preceding counter update before the flags read so a
+ * concurrent set_bit(XPT_DATA) is visible here.
+ *
+ * When the transport is BUSY, the thread holding it will
+ * call svc_xprt_received() upon completion, which checks
+ * for pending work and re-enqueues as needed.
+ */
+static void svc_xprt_resource_released(struct svc_xprt *xprt)
+{
+ unsigned long xpt_flags;
+
+ smp_mb();
+ xpt_flags = READ_ONCE(xprt->xpt_flags);
+ if (xpt_flags & (BIT(XPT_DATA) | BIT(XPT_DEFERRED)) &&
+ !(xpt_flags & BIT(XPT_BUSY)))
+ svc_xprt_enqueue(xprt);
+}
+
static void svc_xprt_release_slot(struct svc_rqst *rqstp)
{
struct svc_xprt *xprt = rqstp->rq_xprt;
if (test_and_clear_bit(RQ_DATA, &rqstp->rq_flags)) {
atomic_dec(&xprt->xpt_nr_rqsts);
- smp_wmb(); /* See smp_rmb() in svc_xprt_ready() */
- svc_xprt_enqueue(xprt);
+ svc_xprt_resource_released(xprt);
}
}
@@ -525,10 +559,10 @@ void svc_reserve(struct svc_rqst *rqstp, int space)
space += rqstp->rq_res.head[0].iov_len;
if (xprt && space < rqstp->rq_reserved) {
- atomic_sub((rqstp->rq_reserved - space), &xprt->xpt_reserved);
+ atomic_sub((rqstp->rq_reserved - space),
+ &xprt->xpt_reserved);
rqstp->rq_reserved = space;
- smp_wmb(); /* See smp_rmb() in svc_xprt_ready() */
- svc_xprt_enqueue(xprt);
+ svc_xprt_resource_released(xprt);
}
}
EXPORT_SYMBOL_GPL(svc_reserve);
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index 3be69c145d2a..64a2658faddb 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -17,11 +17,14 @@
#include <net/ipv6.h>
#include <linux/kernel.h>
#include <linux/user_namespace.h>
+#include <net/genetlink.h>
+#include <uapi/linux/sunrpc_netlink.h>
#include <trace/events/sunrpc.h>
#define RPCDBG_FACILITY RPCDBG_AUTH
#include "netns.h"
+#include "netlink.h"
/*
* AUTHUNIX and AUTHNULL credentials are both handled here.
@@ -152,7 +155,7 @@ static struct cache_head *ip_map_alloc(void)
static int ip_map_upcall(struct cache_detail *cd, struct cache_head *h)
{
- return sunrpc_cache_pipe_upcall(cd, h);
+ return sunrpc_cache_upcall(cd, h);
}
static void ip_map_request(struct cache_detail *cd,
@@ -467,7 +470,7 @@ static struct cache_head *unix_gid_alloc(void)
static int unix_gid_upcall(struct cache_detail *cd, struct cache_head *h)
{
- return sunrpc_cache_pipe_upcall_timeout(cd, h);
+ return sunrpc_cache_upcall_warn(cd, h);
}
static void unix_gid_request(struct cache_detail *cd,
@@ -582,12 +585,278 @@ static int unix_gid_show(struct seq_file *m,
return 0;
}
+static int unix_gid_notify(struct cache_detail *cd, struct cache_head *h)
+{
+ return sunrpc_cache_notify(cd, h, SUNRPC_CACHE_TYPE_UNIX_GID);
+}
+
+/**
+ * sunrpc_nl_unix_gid_get_reqs_dumpit - dump pending unix_gid requests
+ * @skb: reply buffer
+ * @cb: netlink metadata and command arguments
+ *
+ * Walk the unix_gid cache's pending request list and create a netlink
+ * message with a nested entry for each cache_request, containing the
+ * seqno and uid.
+ *
+ * Uses cb->args[0] as a seqno cursor for dump continuation across
+ * multiple netlink messages.
+ *
+ * Returns the size of the reply or a negative errno.
+ */
+int sunrpc_nl_unix_gid_get_reqs_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct sunrpc_net *sn;
+ struct cache_detail *cd;
+ struct cache_head **items;
+ u64 *seqnos;
+ int cnt, i, emitted;
+ void *hdr;
+ int ret;
+
+ sn = net_generic(sock_net(skb->sk), sunrpc_net_id);
+
+ cd = sn->unix_gid_cache;
+ if (!cd)
+ return -ENODEV;
+
+ cnt = sunrpc_cache_requests_count(cd);
+ if (!cnt)
+ return 0;
+
+ items = kcalloc(cnt, sizeof(*items), GFP_KERNEL);
+ seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL);
+ if (!items || !seqnos) {
+ ret = -ENOMEM;
+ goto out_alloc;
+ }
+
+ cnt = sunrpc_cache_requests_snapshot(cd, items, seqnos, cnt,
+ cb->args[0]);
+ if (!cnt) {
+ ret = 0;
+ goto out_alloc;
+ }
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, &sunrpc_nl_family,
+ NLM_F_MULTI, SUNRPC_CMD_UNIX_GID_GET_REQS);
+ if (!hdr) {
+ ret = -ENOBUFS;
+ goto out_put;
+ }
+
+ emitted = 0;
+ for (i = 0; i < cnt; i++) {
+ struct unix_gid *ug;
+ struct nlattr *nest;
+
+ ug = container_of(items[i], struct unix_gid, h);
+
+ nest = nla_nest_start(skb,
+ SUNRPC_A_UNIX_GID_REQS_REQUESTS);
+ if (!nest)
+ break;
+
+ if (nla_put_u64_64bit(skb, SUNRPC_A_UNIX_GID_SEQNO,
+ seqnos[i], 0) ||
+ nla_put_u32(skb, SUNRPC_A_UNIX_GID_UID,
+ from_kuid(&init_user_ns, ug->uid))) {
+ nla_nest_cancel(skb, nest);
+ break;
+ }
+
+ nla_nest_end(skb, nest);
+ cb->args[0] = seqnos[i];
+ emitted++;
+ }
+
+ if (!emitted) {
+ genlmsg_cancel(skb, hdr);
+ ret = -EMSGSIZE;
+ goto out_put;
+ }
+
+ genlmsg_end(skb, hdr);
+ ret = skb->len;
+out_put:
+ for (i = 0; i < cnt; i++)
+ cache_put(items[i], cd);
+out_alloc:
+ kfree(seqnos);
+ kfree(items);
+ return ret;
+}
+
+/**
+ * sunrpc_nl_parse_one_unix_gid - parse one unix_gid entry from netlink
+ * @cd: cache_detail for the unix_gid cache
+ * @attr: nested attribute containing unix_gid fields
+ *
+ * Parses one unix_gid entry from a netlink message and updates the
+ * cache. Mirrors the logic in unix_gid_parse().
+ *
+ * Returns 0 on success or a negative errno.
+ */
+static int sunrpc_nl_parse_one_unix_gid(struct cache_detail *cd,
+ struct nlattr *attr)
+{
+ struct nlattr *tb[SUNRPC_A_UNIX_GID_EXPIRY + 1];
+ struct unix_gid ug, *ugp;
+ struct timespec64 boot;
+ struct nlattr *gid_attr;
+ int err, rem, gids = 0;
+ kuid_t uid;
+
+ err = nla_parse_nested(tb, SUNRPC_A_UNIX_GID_EXPIRY, attr,
+ sunrpc_unix_gid_nl_policy, NULL);
+ if (err)
+ return err;
+
+ /* uid (required) */
+ if (!tb[SUNRPC_A_UNIX_GID_UID])
+ return -EINVAL;
+ uid = make_kuid(current_user_ns(),
+ nla_get_u32(tb[SUNRPC_A_UNIX_GID_UID]));
+ ug.uid = uid;
+
+ /* expiry (required, wallclock seconds) */
+ if (!tb[SUNRPC_A_UNIX_GID_EXPIRY])
+ return -EINVAL;
+ getboottime64(&boot);
+ ug.h.flags = 0;
+ ug.h.expiry_time = nla_get_u64(tb[SUNRPC_A_UNIX_GID_EXPIRY]) -
+ boot.tv_sec;
+
+ if (tb[SUNRPC_A_UNIX_GID_NEGATIVE]) {
+ ug.gi = groups_alloc(0);
+ if (!ug.gi)
+ return -ENOMEM;
+ } else {
+ /* Count gids */
+ nla_for_each_nested_type(gid_attr, SUNRPC_A_UNIX_GID_GIDS,
+ attr, rem)
+ gids++;
+
+ if (gids > 8192)
+ return -EINVAL;
+
+ ug.gi = groups_alloc(gids);
+ if (!ug.gi)
+ return -ENOMEM;
+
+ gids = 0;
+ nla_for_each_nested_type(gid_attr, SUNRPC_A_UNIX_GID_GIDS,
+ attr, rem) {
+ kgid_t kgid;
+
+ kgid = make_kgid(current_user_ns(),
+ nla_get_u32(gid_attr));
+ if (!gid_valid(kgid)) {
+ err = -EINVAL;
+ goto out;
+ }
+ ug.gi->gid[gids++] = kgid;
+ }
+ groups_sort(ug.gi);
+ }
+
+ ugp = unix_gid_lookup(cd, uid);
+ if (ugp) {
+ struct cache_head *ch;
+
+ ch = sunrpc_cache_update(cd, &ug.h, &ugp->h,
+ unix_gid_hash(uid));
+ if (!ch) {
+ err = -ENOMEM;
+ } else {
+ err = 0;
+ cache_put(ch, cd);
+ }
+ } else {
+ err = -ENOMEM;
+ }
+out:
+ if (ug.gi)
+ put_group_info(ug.gi);
+ return err;
+}
+
+/**
+ * sunrpc_nl_unix_gid_set_reqs_doit - respond to unix_gid requests
+ * @skb: reply buffer
+ * @info: netlink metadata and command arguments
+ *
+ * Parse one or more unix_gid cache responses from userspace and
+ * update the unix_gid cache accordingly.
+ *
+ * Returns 0 on success or a negative errno.
+ */
+int sunrpc_nl_unix_gid_set_reqs_doit(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct sunrpc_net *sn;
+ struct cache_detail *cd;
+ const struct nlattr *attr;
+ int rem, ret = 0;
+
+ sn = net_generic(genl_info_net(info), sunrpc_net_id);
+
+ cd = sn->unix_gid_cache;
+ if (!cd)
+ return -ENODEV;
+
+ nlmsg_for_each_attr_type(attr, SUNRPC_A_UNIX_GID_REQS_REQUESTS,
+ info->nlhdr, GENL_HDRLEN, rem) {
+ ret = sunrpc_nl_parse_one_unix_gid(cd,
+ (struct nlattr *)attr);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * sunrpc_nl_cache_flush_doit - flush sunrpc caches via netlink
+ * @skb: reply buffer
+ * @info: netlink metadata and command arguments
+ *
+ * Flush the ip_map and/or unix_gid caches. If SUNRPC_A_CACHE_FLUSH_MASK
+ * is provided, only flush the caches indicated by the bitmask (bit 1 =
+ * ip_map, bit 2 = unix_gid). If omitted, flush both.
+ *
+ * Return 0 on success or a negative errno.
+ */
+int sunrpc_nl_cache_flush_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sunrpc_net *sn;
+ u32 mask = ~0U;
+
+ sn = net_generic(genl_info_net(info), sunrpc_net_id);
+
+ if (info->attrs[SUNRPC_A_CACHE_FLUSH_MASK])
+ mask = nla_get_u32(info->attrs[SUNRPC_A_CACHE_FLUSH_MASK]);
+
+ if ((mask & SUNRPC_CACHE_TYPE_IP_MAP) &&
+ sn->ip_map_cache)
+ cache_purge(sn->ip_map_cache);
+
+ if ((mask & SUNRPC_CACHE_TYPE_UNIX_GID) &&
+ sn->unix_gid_cache)
+ cache_purge(sn->unix_gid_cache);
+
+ return 0;
+}
+
static const struct cache_detail unix_gid_cache_template = {
.owner = THIS_MODULE,
.hash_size = GID_HASHMAX,
.name = "auth.unix.gid",
.cache_put = unix_gid_put,
.cache_upcall = unix_gid_upcall,
+ .cache_notify = unix_gid_notify,
.cache_request = unix_gid_request,
.cache_parse = unix_gid_parse,
.cache_show = unix_gid_show,
@@ -1017,12 +1286,255 @@ struct auth_ops svcauth_unix = {
.set_client = svcauth_unix_set_client,
};
+static int ip_map_notify(struct cache_detail *cd, struct cache_head *h)
+{
+ return sunrpc_cache_notify(cd, h, SUNRPC_CACHE_TYPE_IP_MAP);
+}
+
+/**
+ * sunrpc_nl_ip_map_get_reqs_dumpit - dump pending ip_map requests
+ * @skb: reply buffer
+ * @cb: netlink metadata and command arguments
+ *
+ * Walk the ip_map cache's pending request list and create a netlink
+ * message with a nested entry for each cache_request, containing the
+ * seqno, class and addr.
+ *
+ * Uses cb->args[0] as a seqno cursor for dump continuation across
+ * multiple netlink messages.
+ *
+ * Returns the size of the reply or a negative errno.
+ */
+int sunrpc_nl_ip_map_get_reqs_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct sunrpc_net *sn;
+ struct cache_detail *cd;
+ struct cache_head **items;
+ u64 *seqnos;
+ int cnt, i, emitted;
+ void *hdr;
+ int ret;
+
+ sn = net_generic(sock_net(skb->sk), sunrpc_net_id);
+
+ cd = sn->ip_map_cache;
+ if (!cd)
+ return -ENODEV;
+
+ cnt = sunrpc_cache_requests_count(cd);
+ if (!cnt)
+ return 0;
+
+ items = kcalloc(cnt, sizeof(*items), GFP_KERNEL);
+ seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL);
+ if (!items || !seqnos) {
+ ret = -ENOMEM;
+ goto out_alloc;
+ }
+
+ cnt = sunrpc_cache_requests_snapshot(cd, items, seqnos, cnt,
+ cb->args[0]);
+ if (!cnt) {
+ ret = 0;
+ goto out_alloc;
+ }
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, &sunrpc_nl_family,
+ NLM_F_MULTI, SUNRPC_CMD_IP_MAP_GET_REQS);
+ if (!hdr) {
+ ret = -ENOBUFS;
+ goto out_put;
+ }
+
+ emitted = 0;
+ for (i = 0; i < cnt; i++) {
+ struct ip_map *im;
+ struct nlattr *nest;
+ char text_addr[40];
+
+ im = container_of(items[i], struct ip_map, h);
+
+ if (ipv6_addr_v4mapped(&im->m_addr))
+ snprintf(text_addr, 20, "%pI4",
+ &im->m_addr.s6_addr32[3]);
+ else
+ snprintf(text_addr, 40, "%pI6", &im->m_addr);
+
+ nest = nla_nest_start(skb, SUNRPC_A_IP_MAP_REQS_REQUESTS);
+ if (!nest)
+ break;
+
+ if (nla_put_u64_64bit(skb, SUNRPC_A_IP_MAP_SEQNO,
+ seqnos[i], 0) ||
+ nla_put_string(skb, SUNRPC_A_IP_MAP_CLASS,
+ im->m_class) ||
+ nla_put_string(skb, SUNRPC_A_IP_MAP_ADDR, text_addr)) {
+ nla_nest_cancel(skb, nest);
+ break;
+ }
+
+ nla_nest_end(skb, nest);
+ cb->args[0] = seqnos[i];
+ emitted++;
+ }
+
+ if (!emitted) {
+ genlmsg_cancel(skb, hdr);
+ ret = -EMSGSIZE;
+ goto out_put;
+ }
+
+ genlmsg_end(skb, hdr);
+ ret = skb->len;
+out_put:
+ for (i = 0; i < cnt; i++)
+ cache_put(items[i], cd);
+out_alloc:
+ kfree(seqnos);
+ kfree(items);
+ return ret;
+}
+
+/**
+ * sunrpc_nl_parse_one_ip_map - parse one ip_map entry from netlink
+ * @cd: cache_detail for the ip_map cache
+ * @attr: nested attribute containing ip_map fields
+ *
+ * Parses one ip_map entry from a netlink message and updates the
+ * cache. Mirrors the logic in ip_map_parse().
+ *
+ * Returns 0 on success or a negative errno.
+ */
+static int sunrpc_nl_parse_one_ip_map(struct cache_detail *cd,
+ struct nlattr *attr)
+{
+ struct nlattr *tb[SUNRPC_A_IP_MAP_EXPIRY + 1];
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in s4;
+ struct sockaddr_in6 s6;
+ } address;
+ struct sockaddr_in6 sin6;
+ struct ip_map *ipmp;
+ struct auth_domain *dom = NULL;
+ struct unix_domain *udom = NULL;
+ struct timespec64 boot;
+ time64_t expiry;
+ char class[8];
+ int err;
+ int len;
+
+ err = nla_parse_nested(tb, SUNRPC_A_IP_MAP_EXPIRY, attr,
+ sunrpc_ip_map_nl_policy, NULL);
+ if (err)
+ return err;
+
+ /* class (required) */
+ if (!tb[SUNRPC_A_IP_MAP_CLASS])
+ return -EINVAL;
+ len = nla_len(tb[SUNRPC_A_IP_MAP_CLASS]);
+ if (len <= 0 || len > sizeof(class))
+ return -EINVAL;
+ nla_strscpy(class, tb[SUNRPC_A_IP_MAP_CLASS], sizeof(class));
+
+ /* addr (required) */
+ if (!tb[SUNRPC_A_IP_MAP_ADDR])
+ return -EINVAL;
+ if (rpc_pton(cd->net, nla_data(tb[SUNRPC_A_IP_MAP_ADDR]),
+ nla_len(tb[SUNRPC_A_IP_MAP_ADDR]) - 1,
+ &address.sa, sizeof(address)) == 0)
+ return -EINVAL;
+
+ switch (address.sa.sa_family) {
+ case AF_INET:
+ sin6.sin6_family = AF_INET6;
+ ipv6_addr_set_v4mapped(address.s4.sin_addr.s_addr,
+ &sin6.sin6_addr);
+ break;
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6:
+ memcpy(&sin6, &address.s6, sizeof(sin6));
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ /* expiry (required, wallclock seconds) */
+ if (!tb[SUNRPC_A_IP_MAP_EXPIRY])
+ return -EINVAL;
+ getboottime64(&boot);
+ expiry = nla_get_u64(tb[SUNRPC_A_IP_MAP_EXPIRY]) - boot.tv_sec;
+
+ /* domain name or negative */
+ if (tb[SUNRPC_A_IP_MAP_NEGATIVE]) {
+ udom = NULL;
+ } else if (tb[SUNRPC_A_IP_MAP_DOMAIN]) {
+ dom = unix_domain_find(nla_data(tb[SUNRPC_A_IP_MAP_DOMAIN]));
+ if (!dom)
+ return -ENOENT;
+ udom = container_of(dom, struct unix_domain, h);
+ } else {
+ return -EINVAL;
+ }
+
+ ipmp = __ip_map_lookup(cd, class, &sin6.sin6_addr);
+ if (ipmp)
+ err = __ip_map_update(cd, ipmp, udom, expiry);
+ else
+ err = -ENOMEM;
+
+ if (dom)
+ auth_domain_put(dom);
+
+ cache_flush();
+ return err;
+}
+
+/**
+ * sunrpc_nl_ip_map_set_reqs_doit - respond to ip_map requests
+ * @skb: reply buffer
+ * @info: netlink metadata and command arguments
+ *
+ * Parse one or more ip_map cache responses from userspace and
+ * update the ip_map cache accordingly.
+ *
+ * Returns 0 on success or a negative errno.
+ */
+int sunrpc_nl_ip_map_set_reqs_doit(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct sunrpc_net *sn;
+ struct cache_detail *cd;
+ const struct nlattr *attr;
+ int rem, ret = 0;
+
+ sn = net_generic(genl_info_net(info), sunrpc_net_id);
+
+ cd = sn->ip_map_cache;
+ if (!cd)
+ return -ENODEV;
+
+ nlmsg_for_each_attr_type(attr, SUNRPC_A_IP_MAP_REQS_REQUESTS,
+ info->nlhdr, GENL_HDRLEN, rem) {
+ ret = sunrpc_nl_parse_one_ip_map(cd,
+ (struct nlattr *)attr);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
static const struct cache_detail ip_map_cache_template = {
.owner = THIS_MODULE,
.hash_size = IP_HASHMAX,
.name = "auth.unix.ip",
.cache_put = ip_map_put,
.cache_upcall = ip_map_upcall,
+ .cache_notify = ip_map_notify,
.cache_request = ip_map_request,
.cache_parse = ip_map_parse,
.cache_show = ip_map_show,
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 7be3de1a1aed..50e5e7f5b762 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -471,6 +471,7 @@ static void svc_tcp_handshake_done(void *data, int status, key_serial_t peerid)
}
clear_bit(XPT_HANDSHAKE, &xprt->xpt_flags);
complete_all(&svsk->sk_handshake_done);
+ svc_xprt_put(xprt);
}
/**
@@ -494,9 +495,13 @@ static void svc_tcp_handshake(struct svc_xprt *xprt)
clear_bit(XPT_TLS_SESSION, &xprt->xpt_flags);
init_completion(&svsk->sk_handshake_done);
+ /* Pin the transport across the asynchronous handshake callback. */
+ svc_xprt_get(xprt);
+
ret = tls_server_hello_x509(&args, GFP_KERNEL);
if (ret) {
trace_svc_tls_not_started(xprt);
+ svc_xprt_put(xprt);
goto out_failed;
}
@@ -505,8 +510,13 @@ static void svc_tcp_handshake(struct svc_xprt *xprt)
if (ret <= 0) {
if (tls_handshake_cancel(sk)) {
trace_svc_tls_timed_out(xprt);
+ svc_xprt_put(xprt);
goto out_close;
}
+ /* Cancellation lost to handshake_complete(): the
+ * callback is in flight and should finish quickly.
+ */
+ wait_for_completion(&svsk->sk_handshake_done);
}
if (!test_bit(XPT_TLS_SESSION, &xprt->xpt_flags)) {
@@ -732,7 +742,7 @@ static int svc_udp_sendto(struct svc_rqst *rqstp)
.msg_flags = MSG_SPLICE_PAGES,
.msg_controllen = sizeof(buffer),
};
- unsigned int count;
+ int count;
int err;
svc_udp_release_ctxt(xprt, rqstp->rq_xprt_ctxt);
@@ -746,6 +756,10 @@ static int svc_udp_sendto(struct svc_rqst *rqstp)
goto out_notconn;
count = xdr_buf_to_bvec(svsk->sk_bvec, SUNRPC_MAX_UDP_SENDPAGES, xdr);
+ if (count < 0) {
+ err = count;
+ goto out_trace;
+ }
iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec,
count, rqstp->rq_res.len);
@@ -757,6 +771,7 @@ static int svc_udp_sendto(struct svc_rqst *rqstp)
err = sock_sendmsg(svsk->sk_sock, &msg);
}
+out_trace:
trace_svcsock_udp_send(xprt, err);
mutex_unlock(&xprt->xpt_mutex);
@@ -1237,7 +1252,7 @@ static int svc_tcp_sendmsg(struct svc_sock *svsk, struct svc_rqst *rqstp,
struct msghdr msg = {
.msg_flags = MSG_SPLICE_PAGES,
};
- unsigned int count;
+ int count;
void *buf;
int ret;
@@ -1253,10 +1268,15 @@ static int svc_tcp_sendmsg(struct svc_sock *svsk, struct svc_rqst *rqstp,
count = xdr_buf_to_bvec(svsk->sk_bvec + 1, rqstp->rq_maxpages,
&rqstp->rq_res);
+ if (count < 0) {
+ ret = count;
+ goto out;
+ }
iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec,
1 + count, sizeof(marker) + rqstp->rq_res.len);
ret = sock_sendmsg(svsk->sk_sock, &msg);
+out:
page_frag_free(buf);
return ret;
}
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index e83d5d0be78b..fa6a30b5f046 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -139,19 +139,22 @@ xdr_free_bvec(struct xdr_buf *buf)
/**
* xdr_buf_to_bvec - Copy components of an xdr_buf into a bio_vec array
* @bvec: bio_vec array to populate
- * @bvec_size: element count of @bio_vec
+ * @bvec_size: element count of @bvec
* @xdr: xdr_buf to be copied
*
- * Returns the number of entries consumed in @bvec.
+ * Returns the number of entries consumed in @bvec on success, or
+ * -ESERVERFAULT when @xdr does not fit within @bvec_size entries.
*/
-unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
- const struct xdr_buf *xdr)
+int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
+ const struct xdr_buf *xdr)
{
const struct kvec *head = xdr->head;
const struct kvec *tail = xdr->tail;
unsigned int count = 0;
if (head->iov_len) {
+ if (unlikely(count >= bvec_size))
+ goto bvec_overflow;
bvec_set_virt(bvec++, head->iov_base, head->iov_len);
++count;
}
@@ -165,29 +168,230 @@ unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
while (remaining > 0) {
len = min_t(unsigned int, remaining,
PAGE_SIZE - offset);
+ if (unlikely(count >= bvec_size))
+ goto bvec_overflow;
bvec_set_page(bvec++, *pages++, len, offset);
remaining -= len;
offset = 0;
- if (unlikely(++count > bvec_size))
- goto bvec_overflow;
+ ++count;
}
}
if (tail->iov_len) {
- bvec_set_virt(bvec, tail->iov_base, tail->iov_len);
- if (unlikely(++count > bvec_size))
+ if (unlikely(count >= bvec_size))
goto bvec_overflow;
+ bvec_set_virt(bvec, tail->iov_base, tail->iov_len);
+ ++count;
}
return count;
bvec_overflow:
pr_warn_once("%s: bio_vec array overflow\n", __func__);
- return count - 1;
+ return -ESERVERFAULT;
}
EXPORT_SYMBOL_GPL(xdr_buf_to_bvec);
/**
+ * xdr_buf_to_sg - Populate a scatterlist from an xdr_buf range
+ * @buf: xdr_buf to map
+ * @offset: starting byte offset within @buf
+ * @len: number of bytes to cover
+ * @sg: scatterlist array initialized with sg_init_table()
+ * @nsg: number of entries available in @sg
+ *
+ * @sg is traversed with sg_next(), so callers may pass a list
+ * assembled with sg_chain().
+ *
+ * Return: on success, the number of scatterlist entries used; the
+ * last used entry is marked with sg_mark_end(). On failure, a
+ * negative errno.
+ */
+int xdr_buf_to_sg(const struct xdr_buf *buf, unsigned int offset,
+ unsigned int len, struct scatterlist *sg, unsigned int nsg)
+{
+ unsigned int page_len, thislen, page_offset;
+ struct scatterlist *cur = sg, *prev = NULL;
+ int nents = 0;
+ int i;
+
+ if (len == 0)
+ return 0;
+
+ if (offset >= buf->head[0].iov_len) {
+ offset -= buf->head[0].iov_len;
+ } else {
+ thislen = min_t(unsigned int,
+ buf->head[0].iov_len - offset, len);
+ if (nents >= nsg)
+ return -ENOSPC;
+ sg_set_buf(cur, buf->head[0].iov_base + offset,
+ thislen);
+ prev = cur;
+ cur = sg_next(cur);
+ nents++;
+ len -= thislen;
+ offset = 0;
+ }
+ if (len == 0)
+ goto done;
+
+ if (offset >= buf->page_len) {
+ offset -= buf->page_len;
+ } else {
+ page_len = min(buf->page_len - offset, 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;
+ if (nents >= nsg)
+ return -ENOSPC;
+ sg_set_page(cur, buf->pages[i],
+ thislen, page_offset);
+ prev = cur;
+ cur = sg_next(cur);
+ nents++;
+ page_len -= thislen;
+ i++;
+ page_offset = 0;
+ thislen = PAGE_SIZE;
+ } while (page_len != 0);
+ offset = 0;
+ }
+ if (len == 0)
+ goto done;
+
+ if (offset < buf->tail[0].iov_len) {
+ thislen = min_t(unsigned int,
+ buf->tail[0].iov_len - offset, len);
+ if (nents >= nsg)
+ return -ENOSPC;
+ sg_set_buf(cur, buf->tail[0].iov_base + offset,
+ thislen);
+ prev = cur;
+ nents++;
+ len -= thislen;
+ }
+ if (len != 0)
+ return -EINVAL;
+
+done:
+ if (prev)
+ sg_mark_end(prev);
+ return nents;
+}
+EXPORT_SYMBOL_GPL(xdr_buf_to_sg);
+
+/*
+ * Count the scatterlist entries needed to cover [offset, offset + len)
+ * within @buf. Mirrors the walk in xdr_buf_to_sg() so the caller can
+ * size an allocation that matches the requested sub-range rather than
+ * the full xdr_buf.
+ */
+static unsigned int xdr_buf_sg_nents(const struct xdr_buf *buf,
+ unsigned int offset, unsigned int len)
+{
+ unsigned int nsg = 0, thislen, page_offset;
+
+ if (len == 0)
+ return 0;
+
+ if (offset < buf->head[0].iov_len) {
+ thislen = min_t(unsigned int,
+ buf->head[0].iov_len - offset, len);
+ nsg++;
+ len -= thislen;
+ offset = 0;
+ } else {
+ offset -= buf->head[0].iov_len;
+ }
+ if (len == 0)
+ return nsg;
+
+ if (offset < buf->page_len) {
+ thislen = min(buf->page_len - offset, len);
+ page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1);
+ nsg += DIV_ROUND_UP(page_offset + thislen, PAGE_SIZE);
+ len -= thislen;
+ offset = 0;
+ } else {
+ offset -= buf->page_len;
+ }
+ if (len == 0)
+ return nsg;
+
+ if (offset < buf->tail[0].iov_len)
+ nsg++;
+ return nsg;
+}
+
+/**
+ * xdr_buf_to_sg_alloc - Populate a scatterlist for an xdr_buf range
+ * @buf: xdr_buf to map
+ * @offset: starting byte offset within @buf
+ * @len: number of bytes to cover
+ * @sg_head: caller-provided scatterlist array (typically stack-allocated)
+ * @sg_head_nents: number of entries in @sg_head
+ * @sg_overflow: OUT: chained extension, or NULL when @sg_head sufficed
+ * @gfp: memory allocation flags for overflow
+ *
+ * Populates @sg_head directly when the xdr_buf fits. When more
+ * entries are needed, an overflow scatterlist is allocated and
+ * chained from @sg_head so that the result is traversable with
+ * sg_next().
+ *
+ * Return: on success, the number of populated scatterlist entries
+ * (counting only data entries, not chain entries). @sg_head is
+ * the head of the resulting list. Caller must kfree @sg_overflow
+ * when done. On failure, a negative errno.
+ */
+int xdr_buf_to_sg_alloc(const struct xdr_buf *buf, unsigned int offset,
+ unsigned int len, struct scatterlist *sg_head,
+ unsigned int sg_head_nents,
+ struct scatterlist **sg_overflow, gfp_t gfp)
+{
+ unsigned int nsg;
+ int ret;
+
+ *sg_overflow = NULL;
+ if (len == 0)
+ return 0;
+
+ nsg = xdr_buf_sg_nents(buf, offset, len);
+ if (nsg == 0)
+ return -EINVAL;
+
+ if (nsg <= sg_head_nents) {
+ sg_init_table(sg_head, nsg);
+ } else {
+ /* +1 replaces the slot sg_chain() consumes as the link. */
+ unsigned int overflow_nents = nsg - sg_head_nents + 1;
+ struct scatterlist *overflow;
+
+ overflow = kmalloc_array(overflow_nents, sizeof(*overflow),
+ gfp);
+ if (!overflow)
+ return -ENOMEM;
+
+ sg_init_table(sg_head, sg_head_nents);
+ sg_init_table(overflow, overflow_nents);
+ sg_chain(sg_head, sg_head_nents, overflow);
+ *sg_overflow = overflow;
+ }
+
+ ret = xdr_buf_to_sg(buf, offset, len, sg_head, nsg);
+ if (ret < 0) {
+ kfree(*sg_overflow);
+ *sg_overflow = NULL;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(xdr_buf_to_sg_alloc);
+
+/**
* xdr_inline_pages - Prepare receive buffer for a large reply
* @xdr: xdr_buf into which reply will be placed
* @offset: expected offset where data payload will start, in bytes
@@ -2149,73 +2353,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
diff --git a/net/sunrpc/xprtrdma/svc_rdma.c b/net/sunrpc/xprtrdma/svc_rdma.c
index 415c0310101f..f67f0612b1a9 100644
--- a/net/sunrpc/xprtrdma/svc_rdma.c
+++ b/net/sunrpc/xprtrdma/svc_rdma.c
@@ -264,38 +264,22 @@ err:
return rc;
}
-struct workqueue_struct *svcrdma_wq;
-
void svc_rdma_cleanup(void)
{
svc_unreg_xprt_class(&svc_rdma_class);
svc_rdma_proc_cleanup();
- if (svcrdma_wq) {
- struct workqueue_struct *wq = svcrdma_wq;
-
- svcrdma_wq = NULL;
- destroy_workqueue(wq);
- }
dprintk("SVCRDMA Module Removed, deregister RPC RDMA transport\n");
}
int svc_rdma_init(void)
{
- struct workqueue_struct *wq;
int rc;
- wq = alloc_workqueue("svcrdma", WQ_UNBOUND, 0);
- if (!wq)
- return -ENOMEM;
-
rc = svc_rdma_proc_init();
- if (rc) {
- destroy_workqueue(wq);
+ if (rc)
return rc;
- }
- svcrdma_wq = wq;
svc_reg_xprt_class(&svc_rdma_class);
dprintk("SVCRDMA Module Init, register RPC RDMA transport\n");
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
index f8a0638eb095..fe9bf0371b6e 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
@@ -242,6 +242,10 @@ void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma,
* Ensure that the recv_ctxt is released whether or not a Reply
* was sent. For example, the client could close the connection,
* or svc_process could drop an RPC, before the Reply is sent.
+ *
+ * Also drain any send_ctxts queued for deferred release so that
+ * DMA unmap and page release run in nfsd thread context between
+ * RPCs rather than on the Send completion path.
*/
void svc_rdma_release_ctxt(struct svc_xprt *xprt, void *vctxt)
{
@@ -251,6 +255,8 @@ void svc_rdma_release_ctxt(struct svc_xprt *xprt, void *vctxt)
if (ctxt)
svc_rdma_recv_ctxt_put(rdma, ctxt);
+
+ svc_rdma_send_ctxts_drain(rdma);
}
static bool svc_rdma_refresh_recvs(struct svcxprt_rdma *rdma,
@@ -377,13 +383,16 @@ flushed:
trace_svcrdma_wc_recv_err(wc, &ctxt->rc_cid);
dropped:
svc_rdma_recv_ctxt_put(rdma, ctxt);
- svc_xprt_deferred_close(&rdma->sc_xprt);
+ svc_rdma_xprt_deferred_close(rdma);
}
/**
* svc_rdma_flush_recv_queues - Drain pending Receive work
* @rdma: svcxprt_rdma being shut down
*
+ * Caller must guarantee that @rdma's Send and Recv Completion
+ * Queues are empty (e.g., via ib_drain_qp()), so that no completion
+ * handlers can still produce work on the queues being drained.
*/
void svc_rdma_flush_recv_queues(struct svcxprt_rdma *rdma)
{
@@ -1001,7 +1010,7 @@ out_readlist:
if (ret == -EINVAL)
svc_rdma_send_error(rdma_xprt, ctxt, ret);
svc_rdma_recv_ctxt_put(rdma_xprt, ctxt);
- svc_xprt_deferred_close(xprt);
+ svc_rdma_xprt_deferred_close(rdma_xprt);
return ret;
}
return 0;
diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c
index 402e2ceca4ff..f7fd22cc4a59 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_rw.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c
@@ -236,21 +236,12 @@ svc_rdma_write_info_alloc(struct svcxprt_rdma *rdma,
return info;
}
-static void svc_rdma_write_info_free_async(struct work_struct *work)
+static void svc_rdma_write_info_free(struct svc_rdma_write_info *info)
{
- struct svc_rdma_write_info *info;
-
- info = container_of(work, struct svc_rdma_write_info, wi_work);
svc_rdma_cc_release(info->wi_rdma, &info->wi_cc, DMA_TO_DEVICE);
kfree(info);
}
-static void svc_rdma_write_info_free(struct svc_rdma_write_info *info)
-{
- INIT_WORK(&info->wi_work, svc_rdma_write_info_free_async);
- queue_work(svcrdma_wq, &info->wi_work);
-}
-
/**
* svc_rdma_write_chunk_release - Release Write chunk I/O resources
* @rdma: controlling transport
@@ -313,7 +304,7 @@ static void svc_rdma_reply_done(struct ib_cq *cq, struct ib_wc *wc)
trace_svcrdma_wc_reply_err(wc, &cc->cc_cid);
}
- svc_xprt_deferred_close(&rdma->sc_xprt);
+ svc_rdma_xprt_deferred_close(rdma);
}
/**
@@ -345,7 +336,7 @@ static void svc_rdma_write_done(struct ib_cq *cq, struct ib_wc *wc)
* some of the outgoing RPC message. Signal the loss
* to the client by closing the connection.
*/
- svc_xprt_deferred_close(&rdma->sc_xprt);
+ svc_rdma_xprt_deferred_close(rdma);
}
/**
@@ -390,7 +381,7 @@ static void svc_rdma_wc_read_done(struct ib_cq *cq, struct ib_wc *wc)
*/
svc_rdma_cc_release(rdma, cc, DMA_FROM_DEVICE);
svc_rdma_recv_ctxt_put(rdma, ctxt);
- svc_xprt_deferred_close(&rdma->sc_xprt);
+ svc_rdma_xprt_deferred_close(rdma);
}
/*
@@ -754,216 +745,6 @@ int svc_rdma_prepare_reply_chunk(struct svcxprt_rdma *rdma,
return xdr->len;
}
-/*
- * Cap contiguous RDMA Read sink allocations at order-4.
- * Higher orders risk allocation failure under
- * __GFP_NORETRY, which would negate the benefit of the
- * contiguous fast path.
- */
-#define SVC_RDMA_CONTIG_MAX_ORDER 4
-
-/**
- * svc_rdma_alloc_read_pages - Allocate physically contiguous pages
- * @nr_pages: number of pages needed
- * @order: on success, set to the allocation order
- *
- * Attempts a higher-order allocation, falling back to smaller orders.
- * The returned pages are split immediately so each sub-page has its
- * own refcount and can be freed independently.
- *
- * Returns a pointer to the first page on success, or NULL if even
- * order-1 allocation fails.
- */
-static struct page *
-svc_rdma_alloc_read_pages(unsigned int nr_pages, unsigned int *order)
-{
- unsigned int o;
- struct page *page;
-
- o = min(get_order(nr_pages << PAGE_SHIFT),
- SVC_RDMA_CONTIG_MAX_ORDER);
-
- while (o >= 1) {
- page = alloc_pages(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN,
- o);
- if (page) {
- split_page(page, o);
- *order = o;
- return page;
- }
- o--;
- }
- return NULL;
-}
-
-/*
- * svc_rdma_fill_contig_bvec - Replace rq_pages with a contiguous allocation
- * @rqstp: RPC transaction context
- * @head: context for ongoing I/O
- * @bv: bvec entry to fill
- * @pages_left: number of data pages remaining in the segment
- * @len_left: bytes remaining in the segment
- *
- * On success, fills @bv with a bvec spanning the contiguous range and
- * advances rc_curpage/rc_page_count. Returns the byte length covered,
- * or zero if the allocation failed or would overrun rq_maxpages.
- */
-static unsigned int
-svc_rdma_fill_contig_bvec(struct svc_rqst *rqstp,
- struct svc_rdma_recv_ctxt *head,
- struct bio_vec *bv, unsigned int pages_left,
- unsigned int len_left)
-{
- unsigned int order, npages, chunk_pages, chunk_len, i;
- struct page *page;
-
- page = svc_rdma_alloc_read_pages(pages_left, &order);
- if (!page)
- return 0;
- npages = 1 << order;
-
- if (head->rc_curpage + npages > rqstp->rq_maxpages) {
- for (i = 0; i < npages; i++)
- __free_page(page + i);
- return 0;
- }
-
- /*
- * Replace rq_pages[] entries with pages from the contiguous
- * allocation. If npages exceeds chunk_pages, the extra pages
- * stay in rq_pages[] for later reuse or normal rqst teardown.
- */
- for (i = 0; i < npages; i++) {
- svc_rqst_page_release(rqstp,
- rqstp->rq_pages[head->rc_curpage + i]);
- rqstp->rq_pages[head->rc_curpage + i] = page + i;
- }
-
- chunk_pages = min(npages, pages_left);
- chunk_len = min_t(unsigned int, chunk_pages << PAGE_SHIFT, len_left);
- bvec_set_page(bv, page, chunk_len, 0);
- head->rc_page_count += chunk_pages;
- head->rc_curpage += chunk_pages;
- return chunk_len;
-}
-
-/*
- * svc_rdma_fill_page_bvec - Add a single rq_page to the bvec array
- * @head: context for ongoing I/O
- * @ctxt: R/W context whose bvec array is being filled
- * @cur: page to add
- * @bvec_idx: pointer to current bvec index, not advanced on merge
- * @len_left: bytes remaining in the segment
- *
- * If @cur is physically contiguous with the preceding bvec, it is
- * merged by extending that bvec's length. Otherwise a new bvec
- * entry is created. Returns the byte length covered.
- */
-static unsigned int
-svc_rdma_fill_page_bvec(struct svc_rdma_recv_ctxt *head,
- struct svc_rdma_rw_ctxt *ctxt, struct page *cur,
- unsigned int *bvec_idx, unsigned int len_left)
-{
- unsigned int chunk_len = min_t(unsigned int, PAGE_SIZE, len_left);
-
- head->rc_page_count++;
- head->rc_curpage++;
-
- if (*bvec_idx > 0) {
- struct bio_vec *prev = &ctxt->rw_bvec[*bvec_idx - 1];
-
- if (page_to_phys(prev->bv_page) + prev->bv_offset +
- prev->bv_len == page_to_phys(cur)) {
- prev->bv_len += chunk_len;
- return chunk_len;
- }
- }
-
- bvec_set_page(&ctxt->rw_bvec[*bvec_idx], cur, chunk_len, 0);
- (*bvec_idx)++;
- return chunk_len;
-}
-
-/**
- * svc_rdma_build_read_segment_contig - Build RDMA Read WR with contiguous pages
- * @rqstp: RPC transaction context
- * @head: context for ongoing I/O
- * @segment: co-ordinates of remote memory to be read
- *
- * Greedily allocates higher-order pages to cover the segment,
- * building one bvec per contiguous chunk. Each allocation is
- * split so sub-pages have independent refcounts. When a
- * higher-order allocation fails, remaining pages are covered
- * individually, merging adjacent pages into the preceding bvec
- * when they are physically contiguous. The split sub-pages
- * replace entries in rq_pages[] so downstream cleanup is
- * unchanged.
- *
- * Returns:
- * %0: the Read WR was constructed successfully
- * %-ENOMEM: allocation failed
- * %-EIO: a DMA mapping error occurred
- */
-static int svc_rdma_build_read_segment_contig(struct svc_rqst *rqstp,
- struct svc_rdma_recv_ctxt *head,
- const struct svc_rdma_segment *segment)
-{
- struct svcxprt_rdma *rdma = svc_rdma_rqst_rdma(rqstp);
- struct svc_rdma_chunk_ctxt *cc = &head->rc_cc;
- unsigned int nr_data_pages, bvec_idx;
- struct svc_rdma_rw_ctxt *ctxt;
- unsigned int len_left;
- int ret;
-
- nr_data_pages = PAGE_ALIGN(segment->rs_length) >> PAGE_SHIFT;
- if (head->rc_curpage + nr_data_pages > rqstp->rq_maxpages)
- return -ENOMEM;
-
- ctxt = svc_rdma_get_rw_ctxt(rdma, nr_data_pages);
- if (!ctxt)
- return -ENOMEM;
-
- bvec_idx = 0;
- len_left = segment->rs_length;
- while (len_left) {
- unsigned int pages_left = PAGE_ALIGN(len_left) >> PAGE_SHIFT;
- unsigned int chunk_len = 0;
-
- if (pages_left >= 2)
- chunk_len = svc_rdma_fill_contig_bvec(rqstp, head,
- &ctxt->rw_bvec[bvec_idx],
- pages_left, len_left);
- if (chunk_len) {
- bvec_idx++;
- } else {
- struct page *cur =
- rqstp->rq_pages[head->rc_curpage];
- chunk_len = svc_rdma_fill_page_bvec(head, ctxt, cur,
- &bvec_idx,
- len_left);
- }
-
- len_left -= chunk_len;
- }
-
- ctxt->rw_nents = bvec_idx;
-
- head->rc_pageoff = offset_in_page(segment->rs_length);
- if (head->rc_pageoff)
- head->rc_curpage--;
-
- ret = svc_rdma_rw_ctx_init(rdma, ctxt, segment->rs_offset,
- segment->rs_handle, segment->rs_length,
- DMA_FROM_DEVICE);
- if (ret < 0)
- return -EIO;
- percpu_counter_inc(&svcrdma_stat_read);
-
- list_add(&ctxt->rw_list, &cc->cc_rwctxts);
- cc->cc_sqecount += ret;
- return 0;
-}
-
/**
* svc_rdma_build_read_segment - Build RDMA Read WQEs to pull one RDMA segment
* @rqstp: RPC transaction context
@@ -990,14 +771,6 @@ static int svc_rdma_build_read_segment(struct svc_rqst *rqstp,
if (check_add_overflow(head->rc_pageoff, len, &total))
return -EINVAL;
nr_bvec = PAGE_ALIGN(total) >> PAGE_SHIFT;
-
- if (head->rc_pageoff == 0 && nr_bvec >= 2) {
- ret = svc_rdma_build_read_segment_contig(rqstp, head,
- segment);
- if (ret != -ENOMEM)
- return ret;
- }
-
ctxt = svc_rdma_get_rw_ctxt(rdma, nr_bvec);
if (!ctxt)
return -ENOMEM;
@@ -1343,11 +1116,6 @@ static void svc_rdma_clear_rqst_pages(struct svc_rqst *rqstp,
{
unsigned int i;
- /*
- * Move only pages containing RPC data into rc_pages[]. Pages
- * from a contiguous allocation that were not used for the
- * payload remain in rq_pages[] for subsequent reuse.
- */
for (i = 0; i < head->rc_page_count; i++) {
head->rc_pages[i] = rqstp->rq_pages[i];
rqstp->rq_pages[i] = NULL;
diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
index 8b3f0c8c14b2..7f6d17bf8c1f 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
@@ -79,21 +79,21 @@
* The ownership of all of the Reply's pages are transferred into that
* ctxt, the Send WR is posted, and sendto returns.
*
- * The svc_rdma_send_ctxt is presented when the Send WR completes. The
- * Send completion handler finally releases the Reply's pages.
- *
- * This mechanism also assumes that completions on the transport's Send
- * Completion Queue do not run in parallel. Otherwise a Write completion
- * and Send completion running at the same time could release pages that
- * are still DMA-mapped.
+ * The svc_rdma_send_ctxt is presented when the Send WR completes.
+ * The Send completion handler queues the send_ctxt onto the
+ * per-transport sc_send_release_list (a lock-free llist). The
+ * nfsd thread drains sc_send_release_list in xpo_release_ctxt
+ * between RPCs, DMA-unmapping SGEs, releasing chunk I/O
+ * resources and pages, and returning send_ctxts to the free
+ * list in a batch.
*
* Error Handling
*
* - If the Send WR is posted successfully, it will either complete
* successfully, or get flushed. Either way, the Send completion
- * handler releases the Reply's pages.
- * - If the Send WR cannot be not posted, the forward path releases
- * the Reply's pages.
+ * handler queues the send_ctxt for deferred release.
+ * - If the Send WR cannot be posted, the forward path releases the
+ * Reply's pages.
*
* This handles the case, without the use of page reference counting,
* where two different Write segments send portions of the same page.
@@ -226,14 +226,25 @@ out:
return ctxt;
out_empty:
+ svc_rdma_send_ctxts_drain(rdma);
+
+ spin_lock(&rdma->sc_send_lock);
+ node = llist_del_first(&rdma->sc_send_ctxts);
+ spin_unlock(&rdma->sc_send_lock);
+ if (node) {
+ ctxt = llist_entry(node, struct svc_rdma_send_ctxt, sc_node);
+ goto out;
+ }
+
ctxt = svc_rdma_send_ctxt_alloc(rdma);
if (!ctxt)
return NULL;
goto out;
}
-static void svc_rdma_send_ctxt_release(struct svcxprt_rdma *rdma,
- struct svc_rdma_send_ctxt *ctxt)
+/* Release chunk I/O resources and DMA-unmap SGEs. */
+static void svc_rdma_send_ctxt_unmap(struct svcxprt_rdma *rdma,
+ struct svc_rdma_send_ctxt *ctxt)
{
struct ib_device *device = rdma->sc_cm_id->device;
unsigned int i;
@@ -241,9 +252,6 @@ static void svc_rdma_send_ctxt_release(struct svcxprt_rdma *rdma,
svc_rdma_write_chunk_release(rdma, ctxt);
svc_rdma_reply_chunk_release(rdma, ctxt);
- if (ctxt->sc_page_count)
- release_pages(ctxt->sc_pages, ctxt->sc_page_count);
-
/* The first SGE contains the transport header, which
* remains mapped until @ctxt is destroyed.
*/
@@ -256,30 +264,56 @@ static void svc_rdma_send_ctxt_release(struct svcxprt_rdma *rdma,
ctxt->sc_sges[i].length,
DMA_TO_DEVICE);
}
+}
+
+/* Unmap, release pages, and return send_ctxt to the free list. */
+static void svc_rdma_send_ctxt_release(struct svcxprt_rdma *rdma,
+ struct svc_rdma_send_ctxt *ctxt)
+{
+ svc_rdma_send_ctxt_unmap(rdma, ctxt);
+
+ if (ctxt->sc_page_count)
+ release_pages(ctxt->sc_pages, ctxt->sc_page_count);
llist_add(&ctxt->sc_node, &rdma->sc_send_ctxts);
}
-static void svc_rdma_send_ctxt_put_async(struct work_struct *work)
+/**
+ * svc_rdma_send_ctxts_drain - Release completed send_ctxts
+ * @rdma: controlling svcxprt_rdma
+ */
+void svc_rdma_send_ctxts_drain(struct svcxprt_rdma *rdma)
{
- struct svc_rdma_send_ctxt *ctxt;
+ struct svc_rdma_send_ctxt *ctxt, *next;
+ struct llist_node *node;
- ctxt = container_of(work, struct svc_rdma_send_ctxt, sc_work);
- svc_rdma_send_ctxt_release(ctxt->sc_rdma, ctxt);
+ node = llist_del_all(&rdma->sc_send_release_list);
+ llist_for_each_entry_safe(ctxt, next, node, sc_node)
+ svc_rdma_send_ctxt_release(rdma, ctxt);
}
/**
- * svc_rdma_send_ctxt_put - Return send_ctxt to free list
+ * svc_rdma_send_ctxt_put - Queue send_ctxt for deferred release
* @rdma: controlling svcxprt_rdma
- * @ctxt: object to return to the free list
+ * @ctxt: send_ctxt to queue for deferred release
+ *
+ * Queues @ctxt onto sc_send_release_list. DMA unmap and
+ * page release run later in svc_rdma_send_ctxts_drain(),
+ * typically from xpo_release_ctxt.
*
- * Pages left in sc_pages are DMA unmapped and released.
+ * On the empty-to-non-empty transition, set XPT_DATA and
+ * enqueue the transport. Without this self-trigger, a Send
+ * completion arriving after the last xpo_release_ctxt on an
+ * idle connection would leave the send_ctxt's DMA mappings
+ * and reply pages pinned until another drain occurred.
*/
void svc_rdma_send_ctxt_put(struct svcxprt_rdma *rdma,
struct svc_rdma_send_ctxt *ctxt)
{
- INIT_WORK(&ctxt->sc_work, svc_rdma_send_ctxt_put_async);
- queue_work(svcrdma_wq, &ctxt->sc_work);
+ if (llist_add(&ctxt->sc_node, &rdma->sc_send_release_list)) {
+ set_bit(XPT_DATA, &rdma->sc_xprt.xpt_flags);
+ svc_xprt_enqueue(&rdma->sc_xprt);
+ }
}
/**
@@ -367,6 +401,15 @@ int svc_rdma_sq_wait(struct svcxprt_rdma *rdma,
atomic_inc(&rdma->sc_sq_ticket_tail);
wake_up(&rdma->sc_sq_ticket_wait);
trace_svcrdma_sq_retry(rdma, cid);
+
+ /*
+ * While this thread sat on sc_send_wait or sc_sq_ticket_wait,
+ * Send completions that tried to enqueue this transport for a
+ * release-list drain were rejected: svc_rdma_has_wspace returns
+ * 0 while either waitqueue is active, and svc_xprt_ready
+ * rejects the enqueue. Drain the release list now.
+ */
+ svc_rdma_send_ctxts_drain(rdma);
return 0;
out_close:
@@ -395,7 +438,7 @@ int svc_rdma_post_send_err(struct svcxprt_rdma *rdma,
int sqecount, int ret)
{
trace_svcrdma_sq_post_err(rdma, cid, ret);
- svc_xprt_deferred_close(&rdma->sc_xprt);
+ svc_rdma_xprt_deferred_close(rdma);
/* If even one WR was posted, a Send completion will
* return the reserved SQ slots.
@@ -437,7 +480,7 @@ flushed:
else
trace_svcrdma_wc_send_flush(wc, &ctxt->sc_cid);
svc_rdma_send_ctxt_put(rdma, ctxt);
- svc_xprt_deferred_close(&rdma->sc_xprt);
+ svc_rdma_xprt_deferred_close(rdma);
}
/**
@@ -1158,7 +1201,7 @@ put_ctxt:
svc_rdma_send_ctxt_put(rdma, sctxt);
drop_connection:
trace_svcrdma_send_err(rqstp, ret);
- svc_xprt_deferred_close(&rdma->sc_xprt);
+ svc_rdma_xprt_deferred_close(rdma);
return -ENOTCONN;
}
diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c
index f18bc60d9f4f..7ca71741106b 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_transport.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c
@@ -98,10 +98,27 @@ struct svc_xprt_class svc_rdma_class = {
.xcl_ident = XPRT_TRANSPORT_RDMA,
};
+/**
+ * svc_rdma_xprt_deferred_close - Close an RDMA transport (deferred)
+ * @rdma: transport to close
+ */
+void svc_rdma_xprt_deferred_close(struct svcxprt_rdma *rdma)
+{
+ svc_xprt_deferred_close(&rdma->sc_xprt);
+
+ /* Release parked sc_sq_ticket_wait and sc_send_wait waiters.
+ * Once XPT_CLOSE is observed each returns -ENOTCONN.
+ */
+ wake_up_all(&rdma->sc_sq_ticket_wait);
+ wake_up_all(&rdma->sc_send_wait);
+}
+
/* QP event handler */
static void qp_event_handler(struct ib_event *event, void *context)
{
struct svc_xprt *xprt = context;
+ struct svcxprt_rdma *rdma =
+ container_of(xprt, struct svcxprt_rdma, sc_xprt);
trace_svcrdma_qp_error(event, (struct sockaddr *)&xprt->xpt_remote);
switch (event->event) {
@@ -119,7 +136,7 @@ static void qp_event_handler(struct ib_event *event, void *context)
case IB_EVENT_QP_ACCESS_ERR:
case IB_EVENT_DEVICE_FATAL:
default:
- svc_xprt_deferred_close(xprt);
+ svc_rdma_xprt_deferred_close(rdma);
break;
}
}
@@ -178,6 +195,7 @@ static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv,
init_llist_head(&cma_xprt->sc_send_ctxts);
init_llist_head(&cma_xprt->sc_recv_ctxts);
init_llist_head(&cma_xprt->sc_rw_ctxts);
+ init_llist_head(&cma_xprt->sc_send_release_list);
init_waitqueue_head(&cma_xprt->sc_send_wait);
init_waitqueue_head(&cma_xprt->sc_sq_ticket_wait);
@@ -340,7 +358,7 @@ static int svc_rdma_cma_handler(struct rdma_cm_id *cma_id,
svc_xprt_enqueue(xprt);
break;
case RDMA_CM_EVENT_DISCONNECTED:
- svc_xprt_deferred_close(xprt);
+ svc_rdma_xprt_deferred_close(rdma);
break;
default:
break;
@@ -597,6 +615,15 @@ static void svc_rdma_detach(struct svc_xprt *xprt)
container_of(xprt, struct svcxprt_rdma, sc_xprt);
rdma_disconnect(rdma->sc_cm_id);
+
+ /*
+ * Most close paths go through svc_rdma_xprt_deferred_close(),
+ * which wakes the SQ waitqueues. svc_xprt_close() reaches
+ * detach without that helper, so wake any threads parked in
+ * svc_rdma_sq_wait() here as well.
+ */
+ wake_up_all(&rdma->sc_sq_ticket_wait);
+ wake_up_all(&rdma->sc_send_wait);
}
/**
@@ -614,7 +641,7 @@ static void svc_rdma_free(struct svc_xprt *xprt)
/* This blocks until the Completion Queues are empty */
if (rdma->sc_qp && !IS_ERR(rdma->sc_qp))
ib_drain_qp(rdma->sc_qp);
- flush_workqueue(svcrdma_wq);
+ svc_rdma_send_ctxts_drain(rdma);
svc_rdma_flush_recv_queues(rdma);