diff options
| author | Mark Brown <broonie@kernel.org> | 2026-05-29 12:59:26 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-05-29 12:59:26 +0100 |
| commit | c50ad36cd988b822ace4370c2d0431cd3488092b (patch) | |
| tree | dbcc92355a7c85d5b244aa28c887da2db043af54 /net | |
| parent | 4793e201008e8b72a44e3b81b14d78e1ebc3f42d (diff) | |
| parent | 1f4cc4fcb11c640a3ef5bd1a75136111551fdf9a (diff) | |
| download | linux-next-history-c50ad36cd988b822ace4370c2d0431cd3488092b.tar.gz | |
Merge branch 'nfsd-next' of https://git.kernel.org/pub/scm/linux/kernel/git/cel/linux
# Conflicts:
# fs/exfat/file.c
Diffstat (limited to 'net')
25 files changed, 1431 insertions, 3967 deletions
diff --git a/net/sunrpc/.kunitconfig b/net/sunrpc/.kunitconfig deleted file mode 100644 index eb02b906c2959..0000000000000 --- 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 a570e7adf270f..e7808e5714dc7 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 f89c10fe7e6ac..96727df3aa854 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 452f67deebc6f..68676389d65f1 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 16dcf115de1ed..cf461ebcdde5d 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 + * 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) * - * To provide confidentiality, encrypt using cipher block chaining - * with ciphertext stealing. Message integrity is handled separately. + * 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: encryption successful - * negative errno: encryption could not be completed - */ -VISIBLE_IF_KUNIT -int krb5_cbc_cts_encrypt(struct crypto_sync_skcipher *cts_tfm, - struct crypto_sync_skcipher *cbc_tfm, - u32 offset, struct xdr_buf *buf, struct page **pages, - u8 *iv, unsigned int ivsize) -{ - u32 blocksize, nbytes, nblocks, cbcbytes; - struct encryptor_desc desc; - int err; - - blocksize = crypto_sync_skcipher_blocksize(cts_tfm); - nbytes = buf->len - offset; - nblocks = (nbytes + blocksize - 1) / blocksize; - cbcbytes = 0; - if (nblocks > 2) - cbcbytes = (nblocks - 2) * blocksize; - - memset(desc.iv, 0, sizeof(desc.iv)); - - /* Handle block-sized chunks of plaintext with CBC. */ - if (cbcbytes) { - SYNC_SKCIPHER_REQUEST_ON_STACK(req, cbc_tfm); - - desc.pos = offset; - desc.fragno = 0; - desc.fraglen = 0; - desc.pages = pages; - desc.outbuf = buf; - desc.req = req; - - skcipher_request_set_sync_tfm(req, cbc_tfm); - skcipher_request_set_callback(req, 0, NULL, NULL); - - sg_init_table(desc.infrags, 4); - sg_init_table(desc.outfrags, 4); - - err = xdr_process_buf(buf, offset, cbcbytes, encryptor, &desc); - skcipher_request_zero(req); - if (err) - return err; - } - - /* Remaining plaintext is handled with CBC-CTS. */ - err = gss_krb5_cts_crypt(cts_tfm, buf, offset + cbcbytes, - desc.iv, pages, 1); - if (err) - return err; - - if (unlikely(iv)) - memcpy(iv, desc.iv, ivsize); - return 0; -} -EXPORT_SYMBOL_IF_KUNIT(krb5_cbc_cts_encrypt); - -/** - * krb5_cbc_cts_decrypt - decrypt in CBC mode with CTS - * @cts_tfm: CBC cipher with CTS - * @cbc_tfm: base CBC cipher - * @offset: starting byte offset for plaintext - * @buf: OUT: output buffer - * - * Return values: - * %0: decryption successful - * negative errno: decryption could not be completed + * %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); + 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; - /* hide the gss token header and insert the confounder */ - offset += GSS_KRB5_TOK_HDR_LEN; - if (xdr_extend_head(buf, offset, conflen)) + /* 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; - - 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; + 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); - err = krb5_cbc_cts_encrypt(cipher, aux_cipher, - offset + GSS_KRB5_TOK_HDR_LEN, - buf, pages, NULL, 0); - if (err) - return GSS_S_FAILURE; - - /* Now update buf to account for HMAC */ - buf->tail[0].iov_len += kctx->gk5e->cksumlength; - buf->len += kctx->gk5e->cksumlength; - - 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; + 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; + } } - /* 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; + /* 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; - /* 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) + 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; - /* 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); + ret = crypto_krb5_encrypt(krb5, aead, sg, nsg, sec_len, + conflen, data_len, false); + kfree(sg_overflow); + if (ret < 0) + return GSS_S_FAILURE; -out_free_ahash: - ahash_request_free(req); -out_free_mem: - kfree(iv); - kfree_sensitive(checksumdata); - return err ? GSS_S_FAILURE : GSS_S_COMPLETE; + return 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; + 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; - 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); + /* 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; - offset += GSS_KRB5_TOK_HDR_LEN; - if (xdr_extend_head(buf, offset, conflen)) + 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; - 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) - 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 + * 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 * - * RFC 8009 Section 5: + * 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. * - * 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 + * 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; + struct scatterlist *entry; + int body_max, body_nsg, nsg; - if (kctx->initiate) { - cipher = kctx->acceptor_enc; - aux_cipher = kctx->acceptor_enc_aux; - ahash = kctx->acceptor_integ; + *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; - /* 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)); + *sg_overflow = kmalloc_array(overflow_nents, + sizeof(**sg_overflow), + GFP_NOFS); + if (!*sg_overflow) + return -ENOMEM; - 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) - 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; + 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); } - ret = krb5_cbc_cts_decrypt(cipher, aux_cipher, 0, &subbuf); - if (ret) { - ret = GSS_S_FAILURE; + 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; - } - *headskip = crypto_sync_skcipher_blocksize(cipher); - *tailskip = kctx->gk5e->cksumlength; - return GSS_S_COMPLETE; + /* + * 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]); + } + 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 8769e9e705bfa..3b392e96f25d6 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 gss_krb5_errno_to_status(int err); -u32 krb5_etm_encrypt(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf, - struct page **pages); +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 krb5_etm_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len, - struct xdr_buf *buf, u32 *headskip, u32 *tailskip); +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); -#if IS_ENABLED(CONFIG_KUNIT) -void krb5_nfold(u32 inbits, const u8 *in, u32 outbits, u8 *out); -const struct gss_krb5_enctype *gss_krb5_lookup_enctype(u32 etype); -int krb5_cbc_cts_encrypt(struct crypto_sync_skcipher *cts_tfm, - struct crypto_sync_skcipher *cbc_tfm, u32 offset, - struct xdr_buf *buf, struct page **pages, - u8 *iv, unsigned int ivsize); -int krb5_cbc_cts_decrypt(struct crypto_sync_skcipher *cts_tfm, - struct crypto_sync_skcipher *cbc_tfm, - u32 offset, struct xdr_buf *buf); -u32 krb5_etm_checksum(struct crypto_sync_skcipher *cipher, - struct crypto_ahash *tfm, const struct xdr_buf *body, - int body_offset, struct xdr_netobj *cksumout); -#endif #endif /* _NET_SUNRPC_AUTH_GSS_KRB5_INTERNAL_H */ 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 4eb19c3a54c70..0000000000000 --- 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 6db64a9111a92..c41b5f3e17890 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 ce540df9bce46..cfe066e89f236 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 dde1ee934d0d4..0000000000000 --- 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 ef0e6af9fc959..b5fb70419faaa 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 b3e1738ff6bfa..ac4b32df42b96 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 161d02cc1c2c9..d14209031e180 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 27dd6b58b8ffe..391037f152928 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 0000000000000..ce09ecc0faa2c --- /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 0000000000000..2c1012303d48b --- /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 bab6cab294052..ab88ce46afb55 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_xprt.c b/net/sunrpc/svc_xprt.c index b16e710926c1e..63d1002e63e74 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 3be69c145d2a8..64a2658faddbe 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/xdr.c b/net/sunrpc/xdr.c index e83d5d0be78b7..6bd588dfbfc0d 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -188,6 +188,205 @@ bvec_overflow: 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 +2348,6 @@ int xdr_encode_array2(const struct xdr_buf *buf, unsigned int base, } EXPORT_SYMBOL_GPL(xdr_encode_array2); -int xdr_process_buf(const struct xdr_buf *buf, unsigned int offset, - unsigned int len, - int (*actor)(struct scatterlist *, void *), void *data) -{ - int i, ret = 0; - unsigned int page_len, thislen, page_offset; - struct scatterlist sg[1]; - - sg_init_table(sg, 1); - - if (offset >= buf->head[0].iov_len) { - offset -= buf->head[0].iov_len; - } else { - thislen = buf->head[0].iov_len - offset; - if (thislen > len) - thislen = len; - sg_set_buf(sg, buf->head[0].iov_base + offset, thislen); - ret = actor(sg, data); - if (ret) - goto out; - offset = 0; - len -= thislen; - } - if (len == 0) - goto out; - - if (offset >= buf->page_len) { - offset -= buf->page_len; - } else { - page_len = buf->page_len - offset; - if (page_len > len) - page_len = len; - len -= page_len; - page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1); - i = (offset + buf->page_base) >> PAGE_SHIFT; - thislen = PAGE_SIZE - page_offset; - do { - if (thislen > page_len) - thislen = page_len; - sg_set_page(sg, buf->pages[i], thislen, page_offset); - ret = actor(sg, data); - if (ret) - goto out; - page_len -= thislen; - i++; - page_offset = 0; - thislen = PAGE_SIZE; - } while (page_len != 0); - offset = 0; - } - if (len == 0) - goto out; - if (offset < buf->tail[0].iov_len) { - thislen = buf->tail[0].iov_len - offset; - if (thislen > len) - thislen = len; - sg_set_buf(sg, buf->tail[0].iov_base + offset, thislen); - ret = actor(sg, data); - len -= thislen; - } - if (len != 0) - ret = -EINVAL; -out: - return ret; -} -EXPORT_SYMBOL_GPL(xdr_process_buf); - /** * xdr_stream_decode_string_dup - Decode and duplicate variable length string * @xdr: pointer to xdr_stream diff --git a/net/sunrpc/xprtrdma/svc_rdma.c b/net/sunrpc/xprtrdma/svc_rdma.c index 415c0310101f0..f67f0612b1a9f 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 f8a0638eb095d..19503a12d0a21 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, @@ -384,6 +390,9 @@ dropped: * 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) { diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index 402e2ceca4ff7..cca8ec973de4b 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 diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index 8b3f0c8c14b25..eceefd21bec8e 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: diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index f18bc60d9f4ff..f99cd6177504b 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -178,6 +178,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); @@ -614,7 +615,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); |
