diff options
79 files changed, 6128 insertions, 5758 deletions
@@ -202,9 +202,9 @@ Christophe Ricard <christophe.ricard@gmail.com> Christopher Obbard <christopher.obbard@linaro.org> <chris.obbard@collabora.com> Christoph Hellwig <hch@lst.de> Christoph Manszewski <c.manszewski@gmail.com> <christoph.manszewski@intel.com> -Chuck Lever <chuck.lever@oracle.com> <cel@kernel.org> -Chuck Lever <chuck.lever@oracle.com> <cel@netapp.com> -Chuck Lever <chuck.lever@oracle.com> <cel@citi.umich.edu> +Chuck Lever <cel@kernel.org> <chuck.lever@oracle.com> +Chuck Lever <cel@kernel.org> <cel@netapp.com> +Chuck Lever <cel@kernel.org> <cel@citi.umich.edu> Claudiu Beznea <claudiu.beznea@tuxon.dev> <claudiu.beznea@microchip.com> Colin Ian King <colin.i.king@gmail.com> <colin.king@canonical.com> Corey Minyard <minyard@acm.org> diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml index 8ab43c8253b2..8f36fadd68f7 100644 --- a/Documentation/netlink/specs/nfsd.yaml +++ b/Documentation/netlink/specs/nfsd.yaml @@ -6,8 +6,52 @@ uapi-header: linux/nfsd_netlink.h doc: NFSD configuration over generic netlink. +definitions: + - + type: flags + name: cache-type + entries: [svc_export, expkey] + - + type: flags + name: export-flags + doc: These flags are ordered to match the NFSEXP_* flags in include/linux/nfsd/export.h + entries: + - readonly + - insecure-port + - rootsquash + - allsquash + - async + - gathered-writes + - noreaddirplus + - security-label + - sign-fh + - nohide + - nosubtreecheck + - noauthnlm + - msnfs + - fsid + - crossmount + - noacl + - v4root + - pnfs + - + type: flags + name: xprtsec-mode + doc: These flags are ordered to match the NFSEXP_XPRTSEC_* flags in include/linux/nfsd/export.h + entries: + - none + - tls + - mtls + attribute-sets: - + name: cache-notify + attributes: + - + name: cache-type + type: u32 + enum: cache-type + - name: rpc-status attributes: - @@ -132,6 +176,160 @@ attribute-sets: - name: npools type: u32 + - + name: fslocation + attributes: + - + name: host + type: string + - + name: path + type: string + - + name: fslocations + attributes: + - + name: location + type: nest + nested-attributes: fslocation + multi-attr: true + - + name: auth-flavor + attributes: + - + name: pseudoflavor + type: u32 + - + name: flags + type: u32 + enum: export-flags + enum-as-flags: true + - + name: svc-export + attributes: + - + name: seqno + type: u64 + - + name: client + type: string + - + name: path + type: string + - + name: negative + type: flag + - + name: expiry + type: u64 + - + name: anon-uid + type: u32 + - + name: anon-gid + type: u32 + - + name: fslocations + type: nest + nested-attributes: fslocations + - + name: uuid + type: binary + - + name: secinfo + type: nest + nested-attributes: auth-flavor + multi-attr: true + - + name: xprtsec + type: u32 + enum: xprtsec-mode + multi-attr: true + - + name: flags + type: u32 + enum: export-flags + enum-as-flags: true + - + name: fsid + type: s32 + - + name: svc-export-reqs + attributes: + - + name: requests + type: nest + nested-attributes: svc-export + multi-attr: true + - + name: expkey + attributes: + - + name: seqno + type: u64 + - + name: client + type: string + - + name: fsidtype + type: u8 + - + name: fsid + type: binary + - + name: negative + type: flag + - + name: expiry + type: u64 + - + name: path + type: string + - + name: expkey-reqs + attributes: + - + name: requests + type: nest + nested-attributes: expkey + multi-attr: true + - + name: cache-flush + attributes: + - + name: mask + type: u32 + enum: cache-type + enum-as-flags: true + - + name: unlock-ip + attributes: + - + name: address + type: binary + doc: struct sockaddr_in or struct sockaddr_in6. + checks: + min-len: 16 + - + name: unlock-filesystem + attributes: + - + name: path + type: string + doc: Filesystem path whose state should be released. + - + name: unlock-export + attributes: + - + name: path + type: string + doc: >- + Export path whose NFSv4 state should be revoked. + All state (opens, locks, delegations, layouts) acquired + through any export of this path is revoked, regardless + of which client holds the state. Intended for use after + all clients have been unexported from a given path, + enabling the underlying filesystem to be unmounted. operations: list: @@ -233,3 +431,95 @@ operations: attributes: - mode - npools + - + name: cache-notify + doc: Notification that there are cache requests that need servicing + attribute-set: cache-notify + mcgrp: exportd + event: + attributes: + - cache-type + - + name: svc-export-get-reqs + doc: Dump all pending svc_export requests + attribute-set: svc-export-reqs + flags: [admin-perm] + dump: + reply: + attributes: + - requests + - + name: svc-export-set-reqs + doc: Respond to one or more svc_export requests + attribute-set: svc-export-reqs + flags: [admin-perm] + do: + request: + attributes: + - requests + - + name: expkey-get-reqs + doc: Dump all pending expkey requests + attribute-set: expkey-reqs + flags: [admin-perm] + dump: + reply: + attributes: + - requests + - + name: expkey-set-reqs + doc: Respond to one or more expkey requests + attribute-set: expkey-reqs + flags: [admin-perm] + do: + request: + attributes: + - requests + - + name: cache-flush + doc: Flush nfsd caches (svc_export and/or expkey) + attribute-set: cache-flush + flags: [admin-perm] + do: + request: + attributes: + - mask + - + name: unlock-ip + doc: release NLM locks held by an IP address + attribute-set: unlock-ip + flags: [admin-perm] + do: + request: + attributes: + - address + - + name: unlock-filesystem + doc: revoke NFS state under a filesystem path + attribute-set: unlock-filesystem + flags: [admin-perm] + do: + request: + attributes: + - path + - + name: unlock-export + doc: >- + Revoke NFSv4 state acquired through exports of a given path. + Unlike unlock-filesystem, which operates at superblock granularity, + this command targets only state associated with a specific export + path. Userspace (exportfs -u) sends this after removing the last + client for a path so the underlying filesystem can be unmounted. + attribute-set: unlock-export + flags: [admin-perm] + do: + request: + attributes: + - path + +mcast-groups: + list: + - + name: none + - + name: exportd diff --git a/Documentation/netlink/specs/sunrpc_cache.yaml b/Documentation/netlink/specs/sunrpc_cache.yaml new file mode 100644 index 000000000000..f22ff22b9418 --- /dev/null +++ b/Documentation/netlink/specs/sunrpc_cache.yaml @@ -0,0 +1,149 @@ +# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) +--- +name: sunrpc +protocol: genetlink +uapi-header: linux/sunrpc_netlink.h + +doc: SUNRPC cache upcall support over generic netlink. + +definitions: + - + type: flags + name: cache-type + entries: [ip_map, unix_gid] + +attribute-sets: + - + name: cache-notify + attributes: + - + name: cache-type + type: u32 + enum: cache-type + - + name: ip-map + attributes: + - + name: seqno + type: u64 + - + name: class + type: string + - + name: addr + type: string + - + name: domain + type: string + - + name: negative + type: flag + - + name: expiry + type: u64 + - + name: ip-map-reqs + attributes: + - + name: requests + type: nest + nested-attributes: ip-map + multi-attr: true + - + name: unix-gid + attributes: + - + name: seqno + type: u64 + - + name: uid + type: u32 + - + name: gids + type: u32 + multi-attr: true + - + name: negative + type: flag + - + name: expiry + type: u64 + - + name: unix-gid-reqs + attributes: + - + name: requests + type: nest + nested-attributes: unix-gid + multi-attr: true + - + name: cache-flush + attributes: + - + name: mask + type: u32 + enum: cache-type + enum-as-flags: true + +operations: + list: + - + name: cache-notify + doc: Notification that there are cache requests that need servicing + attribute-set: cache-notify + mcgrp: exportd + event: + attributes: + - cache-type + - + name: ip-map-get-reqs + doc: Dump all pending ip_map requests + attribute-set: ip-map-reqs + flags: [admin-perm] + dump: + reply: + attributes: + - requests + - + name: ip-map-set-reqs + doc: Respond to one or more ip_map requests + attribute-set: ip-map-reqs + flags: [admin-perm] + do: + request: + attributes: + - requests + - + name: unix-gid-get-reqs + doc: Dump all pending unix_gid requests + attribute-set: unix-gid-reqs + flags: [admin-perm] + dump: + reply: + attributes: + - requests + - + name: unix-gid-set-reqs + doc: Respond to one or more unix_gid requests + attribute-set: unix-gid-reqs + flags: [admin-perm] + do: + request: + attributes: + - requests + - + name: cache-flush + doc: Flush sunrpc caches (ip_map and/or unix_gid) + attribute-set: cache-flush + flags: [admin-perm] + do: + request: + attributes: + - mask + +mcast-groups: + list: + - + name: none + - + name: exportd diff --git a/Documentation/sunrpc/xdr/nlm3.x b/Documentation/sunrpc/xdr/nlm3.x new file mode 100644 index 000000000000..b2e704f7b864 --- /dev/null +++ b/Documentation/sunrpc/xdr/nlm3.x @@ -0,0 +1,168 @@ +/* + * This file was extracted by hand from + * https://pubs.opengroup.org/onlinepubs/9629799/chap10.htm#tagcjh_11_03 + */ + +/* + * The NLMv3 protocol + */ + +pragma header nlm3; + +const LM_MAXSTRLEN = 1024; + +const LM_MAXNAMELEN = 1025; + +const MAXNETOBJ_SZ = 1024; + +typedef opaque netobj<MAXNETOBJ_SZ>; + +enum nlm_stats { + LCK_GRANTED = 0, + LCK_DENIED = 1, + LCK_DENIED_NOLOCKS = 2, + LCK_BLOCKED = 3, + LCK_DENIED_GRACE_PERIOD = 4 +}; + +pragma big_endian nlm_stats; + +struct nlm_stat { + nlm_stats stat; +}; + +struct nlm_res { + netobj cookie; + nlm_stat stat; +}; + +struct nlm_holder { + bool exclusive; + int uppid; + netobj oh; + unsigned int l_offset; + unsigned int l_len; +}; + +union nlm_testrply switch (nlm_stats stat) { + case LCK_DENIED: + nlm_holder holder; + default: + void; +}; + +struct nlm_testres { + netobj cookie; + nlm_testrply test_stat; +}; + +struct nlm_lock { + string caller_name<LM_MAXSTRLEN>; + netobj fh; + netobj oh; + int uppid; + unsigned int l_offset; + unsigned int l_len; +}; + +struct nlm_lockargs { + netobj cookie; + bool block; + bool exclusive; + nlm_lock alock; + bool reclaim; + int state; +}; + +struct nlm_cancargs { + netobj cookie; + bool block; + bool exclusive; + nlm_lock alock; +}; + +struct nlm_testargs { + netobj cookie; + bool exclusive; + nlm_lock alock; +}; + +struct nlm_unlockargs { + netobj cookie; + nlm_lock alock; +}; + +enum fsh_mode { + fsm_DN = 0, + fsm_DR = 1, + fsm_DW = 2, + fsm_DRW = 3 +}; + +enum fsh_access { + fsa_NONE = 0, + fsa_R = 1, + fsa_W = 2, + fsa_RW = 3 +}; + +struct nlm_share { + string caller_name<LM_MAXSTRLEN>; + netobj fh; + netobj oh; + fsh_mode mode; + fsh_access access; +}; + +struct nlm_shareargs { + netobj cookie; + nlm_share share; + bool reclaim; +}; + +struct nlm_shareres { + netobj cookie; + nlm_stats stat; + int sequence; +}; + +struct nlm_notify { + string name<LM_MAXNAMELEN>; + long state; +}; + +/* + * Argument for the Linux-private SM_NOTIFY procedure + */ +const SM_PRIV_SIZE = 16; + +struct nlm_notifyargs { + nlm_notify notify; + opaque private[SM_PRIV_SIZE]; +}; + +program NLM_PROG { + version NLM_VERS { + void NLM_NULL(void) = 0; + nlm_testres NLM_TEST(nlm_testargs) = 1; + nlm_res NLM_LOCK(nlm_lockargs) = 2; + nlm_res NLM_CANCEL(nlm_cancargs) = 3; + nlm_res NLM_UNLOCK(nlm_unlockargs) = 4; + nlm_res NLM_GRANTED(nlm_testargs) = 5; + void NLM_TEST_MSG(nlm_testargs) = 6; + void NLM_LOCK_MSG(nlm_lockargs) = 7; + void NLM_CANCEL_MSG(nlm_cancargs) = 8; + void NLM_UNLOCK_MSG(nlm_unlockargs) = 9; + void NLM_GRANTED_MSG(nlm_testargs) = 10; + void NLM_TEST_RES(nlm_testres) = 11; + void NLM_LOCK_RES(nlm_res) = 12; + void NLM_CANCEL_RES(nlm_res) = 13; + void NLM_UNLOCK_RES(nlm_res) = 14; + void NLM_GRANTED_RES(nlm_res) = 15; + void NLM_SM_NOTIFY(nlm_notifyargs) = 16; + nlm_shareres NLM_SHARE(nlm_shareargs) = 20; + nlm_shareres NLM_UNSHARE(nlm_shareargs) = 21; + nlm_res NLM_NM_LOCK(nlm_lockargs) = 22; + void NLM_FREE_ALL(nlm_notify) = 23; + } = 3; +} = 100021; diff --git a/MAINTAINERS b/MAINTAINERS index bbf2c7715e29..635713a76fd4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9902,7 +9902,7 @@ F: include/uapi/scsi/fc/ FILE LOCKING (flock() and fcntl()/lockf()) M: Jeff Layton <jlayton@kernel.org> -M: Chuck Lever <chuck.lever@oracle.com> +M: Chuck Lever <cel@kernel.org> R: Alexander Aring <alex.aring@gmail.com> L: linux-fsdevel@vger.kernel.org S: Maintained @@ -9943,7 +9943,7 @@ F: init/do_mounts* F: init/*initramfs* FILESYSTEMS [EXPORTFS] -M: Chuck Lever <chuck.lever@oracle.com> +M: Chuck Lever <cel@kernel.org> M: Jeff Layton <jlayton@kernel.org> R: Amir Goldstein <amir73il@gmail.com> L: linux-fsdevel@vger.kernel.org @@ -11306,7 +11306,7 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/ F: drivers/media/usb/hackrf/ HANDSHAKE UPCALL FOR TRANSPORT LAYER SECURITY -M: Chuck Lever <chuck.lever@oracle.com> +M: Chuck Lever <cel@kernel.org> L: kernel-tls-handshake@lists.linux.dev L: netdev@vger.kernel.org S: Maintained @@ -14000,7 +14000,7 @@ S: Odd Fixes W: http://kernelnewbies.org/KernelJanitors KERNEL NFSD, SUNRPC, AND LOCKD SERVERS -M: Chuck Lever <chuck.lever@oracle.com> +M: Chuck Lever <cel@kernel.org> M: Jeff Layton <jlayton@kernel.org> R: NeilBrown <neil@brown.name> R: Olga Kornievskaia <okorniev@redhat.com> diff --git a/fs/lockd/Makefile b/fs/lockd/Makefile index 808f0f2a7be1..3f9569d3ba0e 100644 --- a/fs/lockd/Makefile +++ b/fs/lockd/Makefile @@ -8,7 +8,8 @@ ccflags-y += -I$(src) # needed for trace events obj-$(CONFIG_LOCKD) += lockd.o lockd-y := clntlock.o clntproc.o clntxdr.o host.o svc.o svclock.o \ - svcshare.o svcproc.o svcsubs.o mon.o trace.o xdr.o netlink.o + svcshare.o svcproc.o svcsubs.o mon.o trace.o netlink.o \ + nlm3xdr_gen.o lockd-$(CONFIG_LOCKD_V4) += clnt4xdr.o svc4proc.o nlm4xdr_gen.o lockd-$(CONFIG_PROC_FS) += procfs.o @@ -25,17 +26,27 @@ lockd-$(CONFIG_PROC_FS) += procfs.o XDRGEN = ../../tools/net/sunrpc/xdrgen/xdrgen -XDRGEN_DEFINITIONS = ../../include/linux/sunrpc/xdrgen/nlm4.h -XDRGEN_DECLARATIONS = nlm4xdr_gen.h -XDRGEN_SOURCE = nlm4xdr_gen.c +XDRGEN_DEFINITIONS = ../../include/linux/sunrpc/xdrgen/nlm4.h \ + ../../include/linux/sunrpc/xdrgen/nlm3.h +XDRGEN_DECLARATIONS = nlm4xdr_gen.h nlm3xdr_gen.h +XDRGEN_SOURCE = nlm4xdr_gen.c nlm3xdr_gen.c xdrgen: $(XDRGEN_DEFINITIONS) $(XDRGEN_DECLARATIONS) $(XDRGEN_SOURCE) ../../include/linux/sunrpc/xdrgen/nlm4.h: ../../Documentation/sunrpc/xdr/nlm4.x $(XDRGEN) definitions $< > $@ +../../include/linux/sunrpc/xdrgen/nlm3.h: ../../Documentation/sunrpc/xdr/nlm3.x + $(XDRGEN) definitions $< > $@ + nlm4xdr_gen.h: ../../Documentation/sunrpc/xdr/nlm4.x $(XDRGEN) declarations $< > $@ +nlm3xdr_gen.h: ../../Documentation/sunrpc/xdr/nlm3.x + $(XDRGEN) declarations $< > $@ + nlm4xdr_gen.c: ../../Documentation/sunrpc/xdr/nlm4.x $(XDRGEN) source --peer server $< > $@ + +nlm3xdr_gen.c: ../../Documentation/sunrpc/xdr/nlm3.x + $(XDRGEN) source --peer server $< > $@ diff --git a/fs/lockd/clnt4xdr.c b/fs/lockd/clnt4xdr.c index 2058733eacf8..96a4a1e6a6b6 100644 --- a/fs/lockd/clnt4xdr.c +++ b/fs/lockd/clnt4xdr.c @@ -63,7 +63,7 @@ static s64 loff_t_to_s64(loff_t offset) return res; } -static void nlm4_compute_offsets(const struct nlm_lock *lock, +static void nlm4_compute_offsets(const struct lockd_lock *lock, u64 *l_offset, u64 *l_len) { const struct file_lock *fl = &lock->fl; @@ -132,13 +132,13 @@ static int decode_netobj(struct xdr_stream *xdr, * netobj cookie; */ static void encode_cookie(struct xdr_stream *xdr, - const struct nlm_cookie *cookie) + const struct lockd_cookie *cookie) { encode_netobj(xdr, (u8 *)&cookie->data, cookie->len); } static int decode_cookie(struct xdr_stream *xdr, - struct nlm_cookie *cookie) + struct lockd_cookie *cookie) { u32 length; __be32 *p; @@ -238,9 +238,9 @@ out_overflow: * }; */ static void encode_nlm4_holder(struct xdr_stream *xdr, - const struct nlm_res *result) + const struct lockd_res *result) { - const struct nlm_lock *lock = &result->lock; + const struct lockd_lock *lock = &result->lock; u64 l_offset, l_len; __be32 *p; @@ -254,9 +254,9 @@ static void encode_nlm4_holder(struct xdr_stream *xdr, xdr_encode_hyper(p, l_len); } -static int decode_nlm4_holder(struct xdr_stream *xdr, struct nlm_res *result) +static int decode_nlm4_holder(struct xdr_stream *xdr, struct lockd_res *result) { - struct nlm_lock *lock = &result->lock; + struct lockd_lock *lock = &result->lock; struct file_lock *fl = &lock->fl; u64 l_offset, l_len; u32 exclusive; @@ -317,7 +317,7 @@ static void encode_caller_name(struct xdr_stream *xdr, const char *name) * }; */ static void encode_nlm4_lock(struct xdr_stream *xdr, - const struct nlm_lock *lock) + const struct lockd_lock *lock) { u64 l_offset, l_len; __be32 *p; @@ -354,8 +354,8 @@ static void nlm4_xdr_enc_testargs(struct rpc_rqst *req, struct xdr_stream *xdr, const void *data) { - const struct nlm_args *args = data; - const struct nlm_lock *lock = &args->lock; + const struct lockd_args *args = data; + const struct lockd_lock *lock = &args->lock; encode_cookie(xdr, &args->cookie); encode_bool(xdr, lock->fl.c.flc_type == F_WRLCK); @@ -376,8 +376,8 @@ static void nlm4_xdr_enc_lockargs(struct rpc_rqst *req, struct xdr_stream *xdr, const void *data) { - const struct nlm_args *args = data; - const struct nlm_lock *lock = &args->lock; + const struct lockd_args *args = data; + const struct lockd_lock *lock = &args->lock; encode_cookie(xdr, &args->cookie); encode_bool(xdr, args->block); @@ -399,8 +399,8 @@ static void nlm4_xdr_enc_cancargs(struct rpc_rqst *req, struct xdr_stream *xdr, const void *data) { - const struct nlm_args *args = data; - const struct nlm_lock *lock = &args->lock; + const struct lockd_args *args = data; + const struct lockd_lock *lock = &args->lock; encode_cookie(xdr, &args->cookie); encode_bool(xdr, args->block); @@ -418,8 +418,8 @@ static void nlm4_xdr_enc_unlockargs(struct rpc_rqst *req, struct xdr_stream *xdr, const void *data) { - const struct nlm_args *args = data; - const struct nlm_lock *lock = &args->lock; + const struct lockd_args *args = data; + const struct lockd_lock *lock = &args->lock; encode_cookie(xdr, &args->cookie); encode_nlm4_lock(xdr, lock); @@ -435,7 +435,7 @@ static void nlm4_xdr_enc_res(struct rpc_rqst *req, struct xdr_stream *xdr, const void *data) { - const struct nlm_res *result = data; + const struct lockd_res *result = data; encode_cookie(xdr, &result->cookie); encode_nlm4_stat(xdr, result->status); @@ -458,7 +458,7 @@ static void nlm4_xdr_enc_testres(struct rpc_rqst *req, struct xdr_stream *xdr, const void *data) { - const struct nlm_res *result = data; + const struct lockd_res *result = data; encode_cookie(xdr, &result->cookie); encode_nlm4_stat(xdr, result->status); @@ -489,7 +489,7 @@ static void nlm4_xdr_enc_testres(struct rpc_rqst *req, * }; */ static int decode_nlm4_testrply(struct xdr_stream *xdr, - struct nlm_res *result) + struct lockd_res *result) { int error; @@ -506,7 +506,7 @@ static int nlm4_xdr_dec_testres(struct rpc_rqst *req, struct xdr_stream *xdr, void *data) { - struct nlm_res *result = data; + struct lockd_res *result = data; int error; error = decode_cookie(xdr, &result->cookie); @@ -527,7 +527,7 @@ static int nlm4_xdr_dec_res(struct rpc_rqst *req, struct xdr_stream *xdr, void *data) { - struct nlm_res *result = data; + struct lockd_res *result = data; int error; error = decode_cookie(xdr, &result->cookie); diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 8fa30c42c92a..f797cc99f94d 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -158,7 +158,7 @@ int nlmclnt_wait(struct nlm_wait *block, struct nlm_rqst *req, long timeout) /* * The server lockd has called us back to tell us the lock was granted */ -__be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock) +__be32 nlmclnt_grant(const struct sockaddr *addr, const struct lockd_lock *lock) { const struct file_lock *fl = &lock->fl; const struct nfs_fh *fh = &lock->fh; diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 7f211008a5d2..f06faf577cea 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -42,7 +42,7 @@ static const struct rpc_call_ops nlmclnt_cancel_ops; */ static atomic_t nlm_cookie = ATOMIC_INIT(0x1234); -void nlmclnt_next_cookie(struct nlm_cookie *c) +void nlmclnt_next_cookie(struct lockd_cookie *c) { u32 cookie = atomic_inc_return(&nlm_cookie); @@ -128,8 +128,8 @@ static struct nlm_lockowner *nlmclnt_find_lockowner(struct nlm_host *host, fl_ow */ static void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) { - struct nlm_args *argp = &req->a_args; - struct nlm_lock *lock = &argp->lock; + struct lockd_args *argp = &req->a_args; + struct lockd_lock *lock = &argp->lock; char *nodename = req->a_host->h_rpcclnt->cl_nodename; nlmclnt_next_cookie(&argp->cookie); @@ -266,8 +266,8 @@ nlmclnt_call(const struct cred *cred, struct nlm_rqst *req, u32 proc) { struct nlm_host *host = req->a_host; struct rpc_clnt *clnt; - struct nlm_args *argp = &req->a_args; - struct nlm_res *resp = &req->a_res; + struct lockd_args *argp = &req->a_args; + struct lockd_res *resp = &req->a_res; struct rpc_message msg = { .rpc_argp = argp, .rpc_resp = resp, @@ -523,7 +523,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) { const struct cred *cred = nfs_file_cred(fl->c.flc_file); struct nlm_host *host = req->a_host; - struct nlm_res *resp = &req->a_res; + struct lockd_res *resp = &req->a_res; struct nlm_wait block; unsigned char flags = fl->c.flc_flags; unsigned char type; @@ -686,7 +686,7 @@ static int nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) { struct nlm_host *host = req->a_host; - struct nlm_res *resp = &req->a_res; + struct lockd_res *resp = &req->a_res; int status; unsigned char flags = fl->c.flc_flags; diff --git a/fs/lockd/clntxdr.c b/fs/lockd/clntxdr.c index 65555f5224b1..3789ecb0b984 100644 --- a/fs/lockd/clntxdr.c +++ b/fs/lockd/clntxdr.c @@ -60,7 +60,7 @@ static s32 loff_t_to_s32(loff_t offset) return res; } -static void nlm_compute_offsets(const struct nlm_lock *lock, +static void nlm_compute_offsets(const struct lockd_lock *lock, u32 *l_offset, u32 *l_len) { const struct file_lock *fl = &lock->fl; @@ -130,13 +130,13 @@ static int decode_netobj(struct xdr_stream *xdr, * netobj cookie; */ static void encode_cookie(struct xdr_stream *xdr, - const struct nlm_cookie *cookie) + const struct lockd_cookie *cookie) { encode_netobj(xdr, (u8 *)&cookie->data, cookie->len); } static int decode_cookie(struct xdr_stream *xdr, - struct nlm_cookie *cookie) + struct lockd_cookie *cookie) { u32 length; __be32 *p; @@ -234,9 +234,9 @@ out_overflow: * }; */ static void encode_nlm_holder(struct xdr_stream *xdr, - const struct nlm_res *result) + const struct lockd_res *result) { - const struct nlm_lock *lock = &result->lock; + const struct lockd_lock *lock = &result->lock; u32 l_offset, l_len; __be32 *p; @@ -250,9 +250,9 @@ static void encode_nlm_holder(struct xdr_stream *xdr, *p = cpu_to_be32(l_len); } -static int decode_nlm_holder(struct xdr_stream *xdr, struct nlm_res *result) +static int decode_nlm_holder(struct xdr_stream *xdr, struct lockd_res *result) { - struct nlm_lock *lock = &result->lock; + struct lockd_lock *lock = &result->lock; struct file_lock *fl = &lock->fl; u32 exclusive, l_offset, l_len; int error; @@ -319,7 +319,7 @@ static void encode_caller_name(struct xdr_stream *xdr, const char *name) * }; */ static void encode_nlm_lock(struct xdr_stream *xdr, - const struct nlm_lock *lock) + const struct lockd_lock *lock) { u32 l_offset, l_len; __be32 *p; @@ -355,8 +355,8 @@ static void nlm_xdr_enc_testargs(struct rpc_rqst *req, struct xdr_stream *xdr, const void *data) { - const struct nlm_args *args = data; - const struct nlm_lock *lock = &args->lock; + const struct lockd_args *args = data; + const struct lockd_lock *lock = &args->lock; encode_cookie(xdr, &args->cookie); encode_bool(xdr, lock->fl.c.flc_type == F_WRLCK); @@ -377,8 +377,8 @@ static void nlm_xdr_enc_lockargs(struct rpc_rqst *req, struct xdr_stream *xdr, const void *data) { - const struct nlm_args *args = data; - const struct nlm_lock *lock = &args->lock; + const struct lockd_args *args = data; + const struct lockd_lock *lock = &args->lock; encode_cookie(xdr, &args->cookie); encode_bool(xdr, args->block); @@ -400,8 +400,8 @@ static void nlm_xdr_enc_cancargs(struct rpc_rqst *req, struct xdr_stream *xdr, const void *data) { - const struct nlm_args *args = data; - const struct nlm_lock *lock = &args->lock; + const struct lockd_args *args = data; + const struct lockd_lock *lock = &args->lock; encode_cookie(xdr, &args->cookie); encode_bool(xdr, args->block); @@ -419,8 +419,8 @@ static void nlm_xdr_enc_unlockargs(struct rpc_rqst *req, struct xdr_stream *xdr, const void *data) { - const struct nlm_args *args = data; - const struct nlm_lock *lock = &args->lock; + const struct lockd_args *args = data; + const struct lockd_lock *lock = &args->lock; encode_cookie(xdr, &args->cookie); encode_nlm_lock(xdr, lock); @@ -436,7 +436,7 @@ static void nlm_xdr_enc_res(struct rpc_rqst *req, struct xdr_stream *xdr, const void *data) { - const struct nlm_res *result = data; + const struct lockd_res *result = data; encode_cookie(xdr, &result->cookie); encode_nlm_stat(xdr, result->status); @@ -456,7 +456,7 @@ static void nlm_xdr_enc_res(struct rpc_rqst *req, * }; */ static void encode_nlm_testrply(struct xdr_stream *xdr, - const struct nlm_res *result) + const struct lockd_res *result) { if (result->status == nlm_lck_denied) encode_nlm_holder(xdr, result); @@ -466,7 +466,7 @@ static void nlm_xdr_enc_testres(struct rpc_rqst *req, struct xdr_stream *xdr, const void *data) { - const struct nlm_res *result = data; + const struct lockd_res *result = data; encode_cookie(xdr, &result->cookie); encode_nlm_stat(xdr, result->status); @@ -495,7 +495,7 @@ static void nlm_xdr_enc_testres(struct rpc_rqst *req, * }; */ static int decode_nlm_testrply(struct xdr_stream *xdr, - struct nlm_res *result) + struct lockd_res *result) { int error; @@ -512,7 +512,7 @@ static int nlm_xdr_dec_testres(struct rpc_rqst *req, struct xdr_stream *xdr, void *data) { - struct nlm_res *result = data; + struct lockd_res *result = data; int error; error = decode_cookie(xdr, &result->cookie); @@ -533,7 +533,7 @@ static int nlm_xdr_dec_res(struct rpc_rqst *req, struct xdr_stream *xdr, void *data) { - struct nlm_res *result = data; + struct lockd_res *result = data; int error; error = decode_cookie(xdr, &result->cookie); diff --git a/fs/lockd/host.c b/fs/lockd/host.c index ea8a8e166f7e..d572cb27533f 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -552,7 +552,7 @@ struct nlm_host * nlm_get_host(struct nlm_host *host) static struct nlm_host *next_host_state(struct hlist_head *cache, struct nsm_handle *nsm, - const struct nlm_reboot *info) + const struct lockd_reboot *info) { struct nlm_host *host; struct hlist_head *chain; @@ -582,7 +582,7 @@ static struct nlm_host *next_host_state(struct hlist_head *cache, * We were notified that the specified host has rebooted. Release * all resources held by that peer. */ -void nlm_host_rebooted(const struct net *net, const struct nlm_reboot *info) +void nlm_host_rebooted(const struct net *net, const struct lockd_reboot *info) { struct nsm_handle *nsm; struct nlm_host *host; diff --git a/fs/lockd/lockd.h b/fs/lockd/lockd.h index 1db6cb352542..e418a50c4180 100644 --- a/fs/lockd/lockd.h +++ b/fs/lockd/lockd.h @@ -52,6 +52,14 @@ */ #define LOCKD_DFLT_TIMEO 10 +/* + * Number of leading bytes of nfs_fh.data that file_hash() + * digests when bucketing nlm_files[]. Sized for historical + * NFSv2 handles; nfs_fh.data must be initialized at least + * this far before lookup, regardless of fh.size. + */ +#define LOCKD_FH_HASH_SIZE 32 + /* error codes new to NLMv4 */ #define nlm4_deadlock cpu_to_be32(NLM_DEADLCK) #define nlm4_rofs cpu_to_be32(NLM_ROFS) @@ -171,15 +179,15 @@ struct nlm_rqst { refcount_t a_count; unsigned int a_flags; /* initial RPC task flags */ struct nlm_host * a_host; /* host handle */ - struct nlm_args a_args; /* arguments */ - struct nlm_res a_res; /* result */ + struct lockd_args a_args; /* arguments */ + struct lockd_res a_res; /* result */ struct nlm_block * a_block; unsigned int a_retries; /* Retry count */ u8 a_owner[NLMCLNT_OHSIZE]; void * a_callback_data; /* sent to nlmclnt_operations callbacks */ }; -struct nlm_share; +struct lockd_share; /* * This struct describes a file held open by lockd on behalf of @@ -190,7 +198,7 @@ struct nlm_file { struct nfs_fh f_handle; /* NFS file handle */ struct file * f_file[2]; /* VFS file pointers, indexed by O_ flags */ - struct nlm_share * f_shares; /* DOS shares */ + struct lockd_share * f_shares; /* DOS shares */ struct list_head f_blocks; /* blocked locks */ unsigned int f_locks; /* guesstimate # of locks */ unsigned int f_count; /* reference count */ @@ -253,11 +261,11 @@ void nlmclnt_queue_block(struct nlm_wait *block); __be32 nlmclnt_dequeue_block(struct nlm_wait *block); int nlmclnt_wait(struct nlm_wait *block, struct nlm_rqst *req, long timeout); __be32 nlmclnt_grant(const struct sockaddr *addr, - const struct nlm_lock *lock); + const struct lockd_lock *lock); void nlmclnt_recovery(struct nlm_host *); int nlmclnt_reclaim(struct nlm_host *, struct file_lock *, struct nlm_rqst *); -void nlmclnt_next_cookie(struct nlm_cookie *); +void nlmclnt_next_cookie(struct lockd_cookie *); #ifdef CONFIG_LOCKD_V4 extern const struct rpc_version nlm_version4; @@ -285,7 +293,7 @@ struct nlm_host * nlm_get_host(struct nlm_host *); void nlm_shutdown_hosts(void); void nlm_shutdown_hosts_net(struct net *net); void nlm_host_rebooted(const struct net *net, - const struct nlm_reboot *); + const struct lockd_reboot *); /* * Host monitoring @@ -299,7 +307,7 @@ struct nsm_handle *nsm_get_handle(const struct net *net, const char *hostname, const size_t hostname_len); struct nsm_handle *nsm_reboot_lookup(const struct net *net, - const struct nlm_reboot *info); + const struct lockd_reboot *info); void nsm_release(struct nsm_handle *nsm); /* @@ -313,17 +321,17 @@ typedef int (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref); */ int lock_to_openmode(struct file_lock *); __be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *, - struct nlm_host *, struct nlm_lock *, int, - struct nlm_cookie *, int); -__be32 nlmsvc_unlock(struct net *net, struct nlm_file *, struct nlm_lock *); + struct nlm_host *, struct lockd_lock *, int, + struct lockd_cookie *, int); +__be32 nlmsvc_unlock(struct net *net, struct nlm_file *, struct lockd_lock *); __be32 nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, - struct nlm_host *host, struct nlm_lock *lock, - struct nlm_lock *conflock); -__be32 nlmsvc_cancel_blocked(struct net *net, struct nlm_file *, struct nlm_lock *); + struct nlm_host *host, struct lockd_lock *lock, + struct lockd_lock *conflock); +__be32 nlmsvc_cancel_blocked(struct net *net, struct nlm_file *, struct lockd_lock *); void nlmsvc_retry_blocked(struct svc_rqst *rqstp); void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *, nlm_host_match_fn_t match); -void nlmsvc_grant_reply(struct nlm_cookie *, __be32); +void nlmsvc_grant_reply(struct lockd_cookie *, __be32); void nlmsvc_release_call(struct nlm_rqst *); void nlmsvc_locks_init_private(struct file_lock *, struct nlm_host *, pid_t); int nlmsvc_dispatch(struct svc_rqst *rqstp); @@ -332,10 +340,10 @@ int nlmsvc_dispatch(struct svc_rqst *rqstp); * File handling for the server personality */ __be32 nlm_lookup_file(struct svc_rqst *, struct nlm_file **, - struct nlm_lock *, int); + struct lockd_lock *, int); void nlm_release_file(struct nlm_file *); void nlmsvc_put_lockowner(struct nlm_lockowner *); -void nlmsvc_release_lockowner(struct nlm_lock *); +void nlmsvc_release_lockowner(struct lockd_lock *); void nlmsvc_mark_resources(struct net *); void nlmsvc_free_host_resources(struct nlm_host *); void nlmsvc_invalidate_all(void); @@ -423,6 +431,29 @@ static inline int nlm_compare_locks(const struct file_lock *fl1, } /** + * lockd_set_file_lock_range3 - set the byte range of a file_lock + * @fl: file_lock whose length fields are to be initialized + * @off: starting offset of the lock, in bytes + * @len: length of the byte range, in bytes, or zero + * + * NLMv3 uses a (start, length) representation for lock byte ranges, + * while the kernel's file_lock uses (start, end). Treat a length of + * zero or arithmetic overflow (end wrapping negative when the sum + * exceeds S32_MAX) as "lock to end of file." + */ +static inline void +lockd_set_file_lock_range3(struct file_lock *fl, u32 off, u32 len) +{ + s32 end = off + len - 1; + + fl->fl_start = off; + if (len == 0 || end < 0) + fl->fl_end = OFFSET_MAX; + else + fl->fl_end = end; +} + +/** * lockd_set_file_lock_range4 - set the byte range of a file_lock * @fl: file_lock whose length fields are to be initialized * @off: starting offset of the lock, in bytes diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 3d3ee88ca4dc..a8f5ac6f0577 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -377,7 +377,7 @@ retry: * error occurred. */ struct nsm_handle *nsm_reboot_lookup(const struct net *net, - const struct nlm_reboot *info) + const struct lockd_reboot *info) { struct nsm_handle *cached; struct lockd_net *ln = net_generic(net, lockd_net_id); diff --git a/fs/lockd/nlm3xdr_gen.c b/fs/lockd/nlm3xdr_gen.c new file mode 100644 index 000000000000..9ed5a41b5daf --- /dev/null +++ b/fs/lockd/nlm3xdr_gen.c @@ -0,0 +1,714 @@ +// SPDX-License-Identifier: GPL-2.0 +// Generated by xdrgen. Manual edits will be lost. +// XDR specification file: ../../Documentation/sunrpc/xdr/nlm3.x +// XDR specification modification time: Thu Apr 23 10:56:34 2026 + +#include <linux/sunrpc/svc.h> + +#include "nlm3xdr_gen.h" + +static bool __maybe_unused +xdrgen_decode_netobj(struct xdr_stream *xdr, netobj *ptr) +{ + return xdrgen_decode_opaque(xdr, ptr, MAXNETOBJ_SZ); +} + +static bool __maybe_unused +xdrgen_decode_nlm_stats(struct xdr_stream *xdr, nlm_stats *ptr) +{ + __be32 raw; + u32 val; + + if (xdr_stream_decode_be32(xdr, &raw) < 0) + return false; + val = be32_to_cpu(raw); + /* Compiler may optimize to a range check for dense enums */ + switch (val) { + case LCK_GRANTED: + case LCK_DENIED: + case LCK_DENIED_NOLOCKS: + case LCK_BLOCKED: + case LCK_DENIED_GRACE_PERIOD: + break; + default: + return false; + } + *ptr = raw; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm_stat(struct xdr_stream *xdr, struct nlm_stat *ptr) +{ + if (!xdrgen_decode_nlm_stats(xdr, &ptr->stat)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm_res(struct xdr_stream *xdr, struct nlm_res *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_nlm_stat(xdr, &ptr->stat)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm_holder(struct xdr_stream *xdr, struct nlm_holder *ptr) +{ + if (!xdrgen_decode_bool(xdr, &ptr->exclusive)) + return false; + if (!xdrgen_decode_int(xdr, &ptr->uppid)) + return false; + if (!xdrgen_decode_netobj(xdr, &ptr->oh)) + return false; + if (!xdrgen_decode_unsigned_int(xdr, &ptr->l_offset)) + return false; + if (!xdrgen_decode_unsigned_int(xdr, &ptr->l_len)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm_testrply(struct xdr_stream *xdr, struct nlm_testrply *ptr) +{ + if (!xdrgen_decode_nlm_stats(xdr, &ptr->stat)) + return false; + switch (ptr->stat) { + case __constant_cpu_to_be32(LCK_DENIED): + if (!xdrgen_decode_nlm_holder(xdr, &ptr->u.holder)) + return false; + break; + default: + break; + } + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm_testres(struct xdr_stream *xdr, struct nlm_testres *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_nlm_testrply(xdr, &ptr->test_stat)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm_lock(struct xdr_stream *xdr, struct nlm_lock *ptr) +{ + if (!xdrgen_decode_string(xdr, (string *)ptr, LM_MAXSTRLEN)) + return false; + if (!xdrgen_decode_netobj(xdr, &ptr->fh)) + return false; + if (!xdrgen_decode_netobj(xdr, &ptr->oh)) + return false; + if (!xdrgen_decode_int(xdr, &ptr->uppid)) + return false; + if (!xdrgen_decode_unsigned_int(xdr, &ptr->l_offset)) + return false; + if (!xdrgen_decode_unsigned_int(xdr, &ptr->l_len)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm_lockargs(struct xdr_stream *xdr, struct nlm_lockargs *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_bool(xdr, &ptr->block)) + return false; + if (!xdrgen_decode_bool(xdr, &ptr->exclusive)) + return false; + if (!xdrgen_decode_nlm_lock(xdr, &ptr->alock)) + return false; + if (!xdrgen_decode_bool(xdr, &ptr->reclaim)) + return false; + if (!xdrgen_decode_int(xdr, &ptr->state)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm_cancargs(struct xdr_stream *xdr, struct nlm_cancargs *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_bool(xdr, &ptr->block)) + return false; + if (!xdrgen_decode_bool(xdr, &ptr->exclusive)) + return false; + if (!xdrgen_decode_nlm_lock(xdr, &ptr->alock)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm_testargs(struct xdr_stream *xdr, struct nlm_testargs *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_bool(xdr, &ptr->exclusive)) + return false; + if (!xdrgen_decode_nlm_lock(xdr, &ptr->alock)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm_unlockargs(struct xdr_stream *xdr, struct nlm_unlockargs *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_nlm_lock(xdr, &ptr->alock)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_fsh_mode(struct xdr_stream *xdr, fsh_mode *ptr) +{ + u32 val; + + if (xdr_stream_decode_u32(xdr, &val) < 0) + return false; + /* Compiler may optimize to a range check for dense enums */ + switch (val) { + case fsm_DN: + case fsm_DR: + case fsm_DW: + case fsm_DRW: + break; + default: + return false; + } + *ptr = val; + return true; +} + +static bool __maybe_unused +xdrgen_decode_fsh_access(struct xdr_stream *xdr, fsh_access *ptr) +{ + u32 val; + + if (xdr_stream_decode_u32(xdr, &val) < 0) + return false; + /* Compiler may optimize to a range check for dense enums */ + switch (val) { + case fsa_NONE: + case fsa_R: + case fsa_W: + case fsa_RW: + break; + default: + return false; + } + *ptr = val; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm_share(struct xdr_stream *xdr, struct nlm_share *ptr) +{ + if (!xdrgen_decode_string(xdr, (string *)ptr, LM_MAXSTRLEN)) + return false; + if (!xdrgen_decode_netobj(xdr, &ptr->fh)) + return false; + if (!xdrgen_decode_netobj(xdr, &ptr->oh)) + return false; + if (!xdrgen_decode_fsh_mode(xdr, &ptr->mode)) + return false; + if (!xdrgen_decode_fsh_access(xdr, &ptr->access)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm_shareargs(struct xdr_stream *xdr, struct nlm_shareargs *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_nlm_share(xdr, &ptr->share)) + return false; + if (!xdrgen_decode_bool(xdr, &ptr->reclaim)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm_shareres(struct xdr_stream *xdr, struct nlm_shareres *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_nlm_stats(xdr, &ptr->stat)) + return false; + if (!xdrgen_decode_int(xdr, &ptr->sequence)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm_notify(struct xdr_stream *xdr, struct nlm_notify *ptr) +{ + if (!xdrgen_decode_string(xdr, (string *)ptr, LM_MAXNAMELEN)) + return false; + if (!xdrgen_decode_long(xdr, &ptr->state)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm_notifyargs(struct xdr_stream *xdr, struct nlm_notifyargs *ptr) +{ + if (!xdrgen_decode_nlm_notify(xdr, &ptr->notify)) + return false; + if (xdr_stream_decode_opaque_fixed(xdr, ptr->private, SM_PRIV_SIZE) < 0) + return false; + return true; +} + +/** + * nlm_svc_decode_void - Decode a void argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm_svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + return xdrgen_decode_void(xdr); +} + +/** + * nlm_svc_decode_nlm_testargs - Decode a nlm_testargs argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm_svc_decode_nlm_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm_testargs *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm_testargs(xdr, argp); +} + +/** + * nlm_svc_decode_nlm_lockargs - Decode a nlm_lockargs argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm_svc_decode_nlm_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm_lockargs *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm_lockargs(xdr, argp); +} + +/** + * nlm_svc_decode_nlm_cancargs - Decode a nlm_cancargs argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm_svc_decode_nlm_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm_cancargs *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm_cancargs(xdr, argp); +} + +/** + * nlm_svc_decode_nlm_unlockargs - Decode a nlm_unlockargs argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm_svc_decode_nlm_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm_unlockargs *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm_unlockargs(xdr, argp); +} + +/** + * nlm_svc_decode_nlm_testres - Decode a nlm_testres argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm_svc_decode_nlm_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm_testres *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm_testres(xdr, argp); +} + +/** + * nlm_svc_decode_nlm_res - Decode a nlm_res argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm_svc_decode_nlm_res(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm_res *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm_res(xdr, argp); +} + +/** + * nlm_svc_decode_nlm_notifyargs - Decode a nlm_notifyargs argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm_svc_decode_nlm_notifyargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm_notifyargs *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm_notifyargs(xdr, argp); +} + +/** + * nlm_svc_decode_nlm_shareargs - Decode a nlm_shareargs argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm_svc_decode_nlm_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm_shareargs *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm_shareargs(xdr, argp); +} + +/** + * nlm_svc_decode_nlm_notify - Decode a nlm_notify argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm_svc_decode_nlm_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm_notify *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm_notify(xdr, argp); +} + +static bool __maybe_unused +xdrgen_encode_netobj(struct xdr_stream *xdr, const netobj value) +{ + return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0; +} + +static bool __maybe_unused +xdrgen_encode_nlm_stats(struct xdr_stream *xdr, nlm_stats value) +{ + return xdr_stream_encode_be32(xdr, value) == XDR_UNIT; +} + +static bool __maybe_unused +xdrgen_encode_nlm_stat(struct xdr_stream *xdr, const struct nlm_stat *value) +{ + if (!xdrgen_encode_nlm_stats(xdr, value->stat)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm_res(struct xdr_stream *xdr, const struct nlm_res *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_nlm_stat(xdr, &value->stat)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm_holder(struct xdr_stream *xdr, const struct nlm_holder *value) +{ + if (!xdrgen_encode_bool(xdr, value->exclusive)) + return false; + if (!xdrgen_encode_int(xdr, value->uppid)) + return false; + if (!xdrgen_encode_netobj(xdr, value->oh)) + return false; + if (!xdrgen_encode_unsigned_int(xdr, value->l_offset)) + return false; + if (!xdrgen_encode_unsigned_int(xdr, value->l_len)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm_testrply(struct xdr_stream *xdr, const struct nlm_testrply *ptr) +{ + if (!xdrgen_encode_nlm_stats(xdr, ptr->stat)) + return false; + switch (ptr->stat) { + case __constant_cpu_to_be32(LCK_DENIED): + if (!xdrgen_encode_nlm_holder(xdr, &ptr->u.holder)) + return false; + break; + default: + break; + } + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm_testres(struct xdr_stream *xdr, const struct nlm_testres *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_nlm_testrply(xdr, &value->test_stat)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm_lock(struct xdr_stream *xdr, const struct nlm_lock *value) +{ + if (value->caller_name.len > LM_MAXSTRLEN) + return false; + if (xdr_stream_encode_opaque(xdr, value->caller_name.data, value->caller_name.len) < 0) + return false; + if (!xdrgen_encode_netobj(xdr, value->fh)) + return false; + if (!xdrgen_encode_netobj(xdr, value->oh)) + return false; + if (!xdrgen_encode_int(xdr, value->uppid)) + return false; + if (!xdrgen_encode_unsigned_int(xdr, value->l_offset)) + return false; + if (!xdrgen_encode_unsigned_int(xdr, value->l_len)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm_lockargs(struct xdr_stream *xdr, const struct nlm_lockargs *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_bool(xdr, value->block)) + return false; + if (!xdrgen_encode_bool(xdr, value->exclusive)) + return false; + if (!xdrgen_encode_nlm_lock(xdr, &value->alock)) + return false; + if (!xdrgen_encode_bool(xdr, value->reclaim)) + return false; + if (!xdrgen_encode_int(xdr, value->state)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm_cancargs(struct xdr_stream *xdr, const struct nlm_cancargs *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_bool(xdr, value->block)) + return false; + if (!xdrgen_encode_bool(xdr, value->exclusive)) + return false; + if (!xdrgen_encode_nlm_lock(xdr, &value->alock)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm_testargs(struct xdr_stream *xdr, const struct nlm_testargs *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_bool(xdr, value->exclusive)) + return false; + if (!xdrgen_encode_nlm_lock(xdr, &value->alock)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm_unlockargs(struct xdr_stream *xdr, const struct nlm_unlockargs *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_nlm_lock(xdr, &value->alock)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_fsh_mode(struct xdr_stream *xdr, fsh_mode value) +{ + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT; +} + +static bool __maybe_unused +xdrgen_encode_fsh_access(struct xdr_stream *xdr, fsh_access value) +{ + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT; +} + +static bool __maybe_unused +xdrgen_encode_nlm_share(struct xdr_stream *xdr, const struct nlm_share *value) +{ + if (value->caller_name.len > LM_MAXSTRLEN) + return false; + if (xdr_stream_encode_opaque(xdr, value->caller_name.data, value->caller_name.len) < 0) + return false; + if (!xdrgen_encode_netobj(xdr, value->fh)) + return false; + if (!xdrgen_encode_netobj(xdr, value->oh)) + return false; + if (!xdrgen_encode_fsh_mode(xdr, value->mode)) + return false; + if (!xdrgen_encode_fsh_access(xdr, value->access)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm_shareargs(struct xdr_stream *xdr, const struct nlm_shareargs *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_nlm_share(xdr, &value->share)) + return false; + if (!xdrgen_encode_bool(xdr, value->reclaim)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm_shareres(struct xdr_stream *xdr, const struct nlm_shareres *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_nlm_stats(xdr, value->stat)) + return false; + if (!xdrgen_encode_int(xdr, value->sequence)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm_notify(struct xdr_stream *xdr, const struct nlm_notify *value) +{ + if (value->name.len > LM_MAXNAMELEN) + return false; + if (xdr_stream_encode_opaque(xdr, value->name.data, value->name.len) < 0) + return false; + if (!xdrgen_encode_long(xdr, value->state)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm_notifyargs(struct xdr_stream *xdr, const struct nlm_notifyargs *value) +{ + if (!xdrgen_encode_nlm_notify(xdr, &value->notify)) + return false; + if (xdr_stream_encode_opaque_fixed(xdr, value->private, SM_PRIV_SIZE) < 0) + return false; + return true; +} + +/** + * nlm_svc_encode_void - Encode a void result + * @rqstp: RPC transaction context + * @xdr: target XDR data stream + * + * Return values: + * %true: procedure results encoded successfully + * %false: encode failed + */ +bool nlm_svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + return xdrgen_encode_void(xdr); +} + +/** + * nlm_svc_encode_nlm_testres - Encode a nlm_testres result + * @rqstp: RPC transaction context + * @xdr: target XDR data stream + * + * Return values: + * %true: procedure results encoded successfully + * %false: encode failed + */ +bool nlm_svc_encode_nlm_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm_testres *resp = rqstp->rq_resp; + + return xdrgen_encode_nlm_testres(xdr, resp); +} + +/** + * nlm_svc_encode_nlm_res - Encode a nlm_res result + * @rqstp: RPC transaction context + * @xdr: target XDR data stream + * + * Return values: + * %true: procedure results encoded successfully + * %false: encode failed + */ +bool nlm_svc_encode_nlm_res(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm_res *resp = rqstp->rq_resp; + + return xdrgen_encode_nlm_res(xdr, resp); +} + +/** + * nlm_svc_encode_nlm_shareres - Encode a nlm_shareres result + * @rqstp: RPC transaction context + * @xdr: target XDR data stream + * + * Return values: + * %true: procedure results encoded successfully + * %false: encode failed + */ +bool nlm_svc_encode_nlm_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm_shareres *resp = rqstp->rq_resp; + + return xdrgen_encode_nlm_shareres(xdr, resp); +} diff --git a/fs/lockd/nlm3xdr_gen.h b/fs/lockd/nlm3xdr_gen.h new file mode 100644 index 000000000000..c99038e99805 --- /dev/null +++ b/fs/lockd/nlm3xdr_gen.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Generated by xdrgen. Manual edits will be lost. */ +/* XDR specification file: ../../Documentation/sunrpc/xdr/nlm3.x */ +/* XDR specification modification time: Thu Apr 23 10:56:34 2026 */ + +#ifndef _LINUX_XDRGEN_NLM3_DECL_H +#define _LINUX_XDRGEN_NLM3_DECL_H + +#include <linux/types.h> + +#include <linux/sunrpc/xdr.h> +#include <linux/sunrpc/xdrgen/_defs.h> +#include <linux/sunrpc/xdrgen/_builtins.h> +#include <linux/sunrpc/xdrgen/nlm3.h> + +bool nlm_svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm_svc_decode_nlm_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm_svc_decode_nlm_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm_svc_decode_nlm_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm_svc_decode_nlm_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm_svc_decode_nlm_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm_svc_decode_nlm_res(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm_svc_decode_nlm_notifyargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm_svc_decode_nlm_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm_svc_decode_nlm_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr); + +bool nlm_svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm_svc_encode_nlm_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm_svc_encode_nlm_res(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm_svc_encode_nlm_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr); + +#endif /* _LINUX_XDRGEN_NLM3_DECL_H */ diff --git a/fs/lockd/share.h b/fs/lockd/share.h index 20ea8ee49168..1ec3ccdb2aef 100644 --- a/fs/lockd/share.h +++ b/fs/lockd/share.h @@ -14,8 +14,8 @@ /* * DOS share for a specific file */ -struct nlm_share { - struct nlm_share * s_next; /* linked list */ +struct lockd_share { + struct lockd_share * s_next; /* linked list */ struct nlm_host * s_host; /* client host */ struct nlm_file * s_file; /* shared file */ struct xdr_netobj s_owner; /* owner handle */ diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 41cab858de57..78e675470c4b 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -26,42 +26,42 @@ #include "nlm4xdr_gen.h" /* - * Wrapper structures combine xdrgen types with legacy nlm_lock. + * Wrapper structures combine xdrgen types with legacy lockd_lock. * The xdrgen field must be first so the structure can be cast * to its XDR type for the RPC dispatch layer. */ struct nlm4_testargs_wrapper { struct nlm4_testargs xdrgen; - struct nlm_lock lock; + struct lockd_lock lock; }; static_assert(offsetof(struct nlm4_testargs_wrapper, xdrgen) == 0); struct nlm4_lockargs_wrapper { struct nlm4_lockargs xdrgen; - struct nlm_cookie cookie; - struct nlm_lock lock; + struct lockd_cookie cookie; + struct lockd_lock lock; }; static_assert(offsetof(struct nlm4_lockargs_wrapper, xdrgen) == 0); struct nlm4_cancargs_wrapper { struct nlm4_cancargs xdrgen; - struct nlm_lock lock; + struct lockd_lock lock; }; static_assert(offsetof(struct nlm4_cancargs_wrapper, xdrgen) == 0); struct nlm4_unlockargs_wrapper { struct nlm4_unlockargs xdrgen; - struct nlm_lock lock; + struct lockd_lock lock; }; static_assert(offsetof(struct nlm4_unlockargs_wrapper, xdrgen) == 0); struct nlm4_notifyargs_wrapper { struct nlm4_notifyargs xdrgen; - struct nlm_reboot reboot; + struct lockd_reboot reboot; }; static_assert(offsetof(struct nlm4_notifyargs_wrapper, xdrgen) == 0); @@ -74,12 +74,12 @@ static_assert(offsetof(struct nlm4_notify_wrapper, xdrgen) == 0); struct nlm4_testres_wrapper { struct nlm4_testres xdrgen; - struct nlm_lock lock; + struct lockd_lock lock; }; struct nlm4_shareargs_wrapper { struct nlm4_shareargs xdrgen; - struct nlm_lock lock; + struct lockd_lock lock; }; static_assert(offsetof(struct nlm4_shareargs_wrapper, xdrgen) == 0); @@ -88,7 +88,7 @@ static_assert(offsetof(struct nlm4_testres_wrapper, xdrgen) == 0); struct nlm4_res_wrapper { struct nlm4_res xdrgen; - struct nlm_cookie cookie; + struct lockd_cookie cookie; }; static_assert(offsetof(struct nlm4_res_wrapper, xdrgen) == 0); @@ -100,7 +100,7 @@ struct nlm4_shareres_wrapper { static_assert(offsetof(struct nlm4_shareres_wrapper, xdrgen) == 0); static __be32 -nlm4_netobj_to_cookie(struct nlm_cookie *cookie, netobj *object) +nlm4_netobj_to_cookie(struct lockd_cookie *cookie, netobj *object) { if (object->len > NLM_MAXCOOKIELEN) return nlm_lck_denied_nolocks; @@ -110,7 +110,7 @@ nlm4_netobj_to_cookie(struct nlm_cookie *cookie, netobj *object) } static __be32 -nlm4_lock_to_nlm_lock(struct nlm_lock *lock, struct nlm4_lock *alock) +nlm4_lock_to_lockd_lock(struct lockd_lock *lock, struct nlm4_lock *alock) { if (alock->fh.len > NFS_MAXFHSIZE) return nlm_lck_denied; @@ -119,7 +119,6 @@ nlm4_lock_to_nlm_lock(struct nlm_lock *lock, struct nlm4_lock *alock) lock->oh.len = alock->oh.len; lock->oh.data = alock->oh.data; lock->svid = alock->svid; - locks_init_lock(&lock->fl); lockd_set_file_lock_range4(&lock->fl, alock->l_offset, alock->l_len); return nlm_granted; } @@ -143,7 +142,7 @@ nlm4svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored) static __be32 nlm4svc_lookup_file(struct svc_rqst *rqstp, struct nlm_host *host, - struct nlm_lock *lock, struct nlm_file **filp, + struct lockd_lock *lock, struct nlm_file **filp, struct nlm4_lock *xdr_lock, unsigned char type) { bool is_test = (rqstp->rq_proc == NLMPROC4_TEST || @@ -157,6 +156,9 @@ nlm4svc_lookup_file(struct svc_rqst *rqstp, struct nlm_host *host, return nlm_lck_denied_nolocks; lock->fh.size = xdr_lock->fh.len; memcpy(lock->fh.data, xdr_lock->fh.data, xdr_lock->fh.len); + if (xdr_lock->fh.len < LOCKD_FH_HASH_SIZE) + memset(lock->fh.data + xdr_lock->fh.len, 0, + LOCKD_FH_HASH_SIZE - xdr_lock->fh.len); lock->oh.len = xdr_lock->oh.len; lock->oh.data = xdr_lock->oh.data; @@ -270,7 +272,7 @@ static __be32 nlm4svc_proc_test(struct svc_rqst *rqstp) nlmsvc_release_lockowner(&argp->lock); if (resp->xdrgen.stat.stat == nlm_lck_denied) { - struct nlm_lock *conf = &resp->lock; + struct lockd_lock *conf = &resp->lock; struct nlm4_holder *holder = &resp->xdrgen.stat.u.holder; holder->exclusive = (conf->fl.c.flc_type != F_RDLCK); @@ -513,12 +515,12 @@ out: * nlm4_res NLMPROC4_GRANTED(nlm4_testargs) = 5; * * Permissible procedure status codes: - * %NLM4_GRANTED: The requested lock was granted. - * %NLM4_DENIED: The server could not allocate the resources - * needed to process the request. - * %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is - * re-establishing existing locks, and is not - * yet ready to accept normal service requests. + * %NLM4_GRANTED: The granted lock was accepted. + * %NLM4_DENIED: The procedure failed, possibly due to + * internal resource constraints. + * %NLM4_DENIED_GRACE_PERIOD: The client host recently restarted and + * its NLM is re-establishing existing locks, + * so it is not yet ready to accept callbacks. */ static __be32 nlm4svc_proc_granted(struct svc_rqst *rqstp) @@ -528,8 +530,8 @@ nlm4svc_proc_granted(struct svc_rqst *rqstp) resp->xdrgen.cookie = argp->xdrgen.cookie; - resp->xdrgen.stat.stat = nlm4_lock_to_nlm_lock(&argp->lock, - &argp->xdrgen.alock); + resp->xdrgen.stat.stat = nlm4_lock_to_lockd_lock(&argp->lock, + &argp->xdrgen.alock); if (resp->xdrgen.stat.stat) goto out; @@ -563,7 +565,7 @@ static const struct rpc_call_ops nlm4svc_callback_ops = { */ static __be32 nlm4svc_callback(struct svc_rqst *rqstp, struct nlm_host *host, u32 proc, - __be32 (*func)(struct svc_rqst *, struct nlm_res *)) + __be32 (*func)(struct svc_rqst *, struct lockd_res *)) { struct nlm_rqst *call; __be32 stat; @@ -586,7 +588,7 @@ nlm4svc_callback(struct svc_rqst *rqstp, struct nlm_host *host, u32 proc, } static __be32 -__nlm4svc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_res *resp) +__nlm4svc_proc_test_msg(struct svc_rqst *rqstp, struct lockd_res *resp) { struct nlm4_testargs_wrapper *argp = rqstp->rq_argp; unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; @@ -646,7 +648,7 @@ static __be32 nlm4svc_proc_test_msg(struct svc_rqst *rqstp) } static __be32 -__nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_res *resp) +__nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct lockd_res *resp) { struct nlm4_lockargs_wrapper *argp = rqstp->rq_argp; unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; @@ -669,6 +671,8 @@ __nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_res *resp) resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock, argp->xdrgen.block, &resp->cookie, argp->xdrgen.reclaim); + if (resp->status == nlm__int__deadlock) + resp->status = nlm4_deadlock; nlmsvc_release_lockowner(&argp->lock); out: @@ -697,7 +701,7 @@ static __be32 nlm4svc_proc_lock_msg(struct svc_rqst *rqstp) struct nlm4_lockargs_wrapper *argp = rqstp->rq_argp; struct nlm_host *host; - host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, true); + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); if (!host) return rpc_system_err; @@ -706,7 +710,7 @@ static __be32 nlm4svc_proc_lock_msg(struct svc_rqst *rqstp) } static __be32 -__nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_res *resp) +__nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp, struct lockd_res *resp) { struct nlm4_cancargs_wrapper *argp = rqstp->rq_argp; unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; @@ -770,7 +774,7 @@ static __be32 nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp) } static __be32 -__nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_res *resp) +__nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp, struct lockd_res *resp) { struct nlm4_unlockargs_wrapper *argp = rqstp->rq_argp; struct net *net = SVC_NET(rqstp); @@ -833,7 +837,7 @@ static __be32 nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp) } static __be32 -__nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_res *resp) +__nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct lockd_res *resp) { struct nlm4_testargs_wrapper *argp = rqstp->rq_argp; @@ -841,7 +845,7 @@ __nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_res *resp) if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie)) goto out; - if (nlm4_lock_to_nlm_lock(&argp->lock, &argp->xdrgen.alock)) + if (nlm4_lock_to_lockd_lock(&argp->lock, &argp->xdrgen.alock)) goto out; resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock); @@ -917,7 +921,7 @@ static __be32 nlm4svc_proc_granted_res(struct svc_rqst *rqstp) static __be32 nlm4svc_proc_sm_notify(struct svc_rqst *rqstp) { struct nlm4_notifyargs_wrapper *argp = rqstp->rq_argp; - struct nlm_reboot *reboot = &argp->reboot; + struct lockd_reboot *reboot = &argp->reboot; if (!nlm_privileged_requester(rqstp)) { char buf[RPC_MAX_ADDRBUFLEN]; @@ -981,7 +985,7 @@ static __be32 nlm4svc_proc_share(struct svc_rqst *rqstp) { struct nlm4_shareargs_wrapper *argp = rqstp->rq_argp; struct nlm4_shareres_wrapper *resp = rqstp->rq_resp; - struct nlm_lock *lock = &argp->lock; + struct lockd_lock *lock = &argp->lock; struct nlm_host *host = NULL; struct nlm_file *file = NULL; struct nlm4_lock xdr_lock = { @@ -1049,7 +1053,7 @@ static __be32 nlm4svc_proc_unshare(struct svc_rqst *rqstp) { struct nlm4_shareargs_wrapper *argp = rqstp->rq_argp; struct nlm4_shareres_wrapper *resp = rqstp->rq_resp; - struct nlm_lock *lock = &argp->lock; + struct lockd_lock *lock = &argp->lock; struct nlm4_lock xdr_lock = { .fh = argp->xdrgen.share.fh, .oh = argp->xdrgen.share.oh, diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index f4520149d6d7..e48d31f14a65 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -37,7 +37,7 @@ static void nlmsvc_release_block(struct nlm_block *block); static void nlmsvc_insert_block(struct nlm_block *block, unsigned long); static void nlmsvc_remove_block(struct nlm_block *block); -static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock); +static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct lockd_lock *lock); static void nlmsvc_freegrantargs(struct nlm_rqst *call); static const struct rpc_call_ops nlmsvc_grant_ops; @@ -48,7 +48,7 @@ static LIST_HEAD(nlm_blocked); static DEFINE_SPINLOCK(nlm_blocked_lock); #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) -static const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie) +static const char *nlmdbg_cookie2a(const struct lockd_cookie *cookie) { /* * We can get away with a static buffer because this is only called @@ -75,7 +75,7 @@ static const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie) return buf; } #else -static inline const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie) +static inline const char *nlmdbg_cookie2a(const struct lockd_cookie *cookie) { return "???"; } @@ -142,7 +142,7 @@ nlmsvc_remove_block(struct nlm_block *block) * Find a block for a given lock */ static struct nlm_block * -nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock) +nlmsvc_lookup_block(struct nlm_file *file, struct lockd_lock *lock) { struct nlm_block *block; struct file_lock *fl; @@ -171,7 +171,7 @@ nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock) return NULL; } -static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b) +static int lockd_cookie_match(struct lockd_cookie *a, struct lockd_cookie *b) { if (a->len != b->len) return 0; @@ -184,13 +184,13 @@ static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b) * Find a block with a given NLM cookie. */ static inline struct nlm_block * -nlmsvc_find_block(struct nlm_cookie *cookie) +nlmsvc_find_block(struct lockd_cookie *cookie) { struct nlm_block *block; spin_lock(&nlm_blocked_lock); list_for_each_entry(block, &nlm_blocked, b_list) { - if (nlm_cookie_match(&block->b_call->a_args.cookie,cookie)) + if (lockd_cookie_match(&block->b_call->a_args.cookie, cookie)) goto found; } spin_unlock(&nlm_blocked_lock); @@ -221,8 +221,8 @@ found: */ static struct nlm_block * nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_host *host, - struct nlm_file *file, struct nlm_lock *lock, - struct nlm_cookie *cookie) + struct nlm_file *file, struct lockd_lock *lock, + struct lockd_cookie *cookie) { struct nlm_block *block; struct nlm_rqst *call = NULL; @@ -399,7 +399,7 @@ static struct nlm_lockowner *nlmsvc_find_lockowner(struct nlm_host *host, pid_t } void -nlmsvc_release_lockowner(struct nlm_lock *lock) +nlmsvc_release_lockowner(struct lockd_lock *lock) { if (lock->fl.c.flc_owner) nlmsvc_put_lockowner(lock->fl.c.flc_owner); @@ -415,7 +415,7 @@ void nlmsvc_locks_init_private(struct file_lock *fl, struct nlm_host *host, * Initialize arguments for GRANTED call. The nlm_rqst structure * has been cleared already. */ -static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock) +static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct lockd_lock *lock) { locks_copy_lock(&call->a_args.lock.fl, &lock->fl); memcpy(&call->a_args.lock.fh, &lock->fh, sizeof(call->a_args.lock.fh)); @@ -476,8 +476,8 @@ nlmsvc_defer_lock_rqst(struct svc_rqst *rqstp, struct nlm_block *block) */ __be32 nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, - struct nlm_host *host, struct nlm_lock *lock, int wait, - struct nlm_cookie *cookie, int reclaim) + struct nlm_host *host, struct lockd_lock *lock, int wait, + struct lockd_cookie *cookie, int reclaim) { struct inode *inode __maybe_unused = nlmsvc_file_inode(file); struct nlm_block *block = NULL; @@ -609,8 +609,8 @@ out: */ __be32 nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, - struct nlm_host *host, struct nlm_lock *lock, - struct nlm_lock *conflock) + struct nlm_host *host, struct lockd_lock *lock, + struct lockd_lock *conflock) { int error; __be32 ret; @@ -669,7 +669,7 @@ out: * must be removed. */ __be32 -nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock) +nlmsvc_unlock(struct net *net, struct nlm_file *file, struct lockd_lock *lock) { int error = 0; @@ -707,7 +707,7 @@ nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock) * The calling procedure must check whether the file can be closed. */ __be32 -nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *lock) +nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct lockd_lock *lock) { struct nlm_block *block; int status = 0; @@ -848,7 +848,7 @@ static void nlmsvc_grant_blocked(struct nlm_block *block) { struct nlm_file *file = block->b_file; - struct nlm_lock *lock = &block->b_call->a_args.lock; + struct lockd_lock *lock = &block->b_call->a_args.lock; int mode; int error; loff_t fl_start, fl_end; @@ -982,7 +982,7 @@ static const struct rpc_call_ops nlmsvc_grant_ops = { * block. */ void -nlmsvc_grant_reply(struct nlm_cookie *cookie, __be32 status) +nlmsvc_grant_reply(struct lockd_cookie *cookie, __be32 status) { struct nlm_block *block; struct file_lock *fl; diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index c0a3487719e2..4836887f11ef 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -13,303 +13,534 @@ #include <linux/sunrpc/svc_xprt.h> #include "lockd.h" + +/* + * xdr.h defines SM_PRIV_SIZE as a macro. nlm3xdr_gen.h defines + * it as an enum constant. Undefine the macro before including + * the generated header. + */ +#undef SM_PRIV_SIZE + #include "share.h" +#include "nlm3xdr_gen.h" + +/* + * Size of an NFSv2 file handle, in bytes, which is 32. + * Defined locally to avoid including uapi/linux/nfs2.h. + */ +#define NLM3_FHSIZE 32 + +/* + * Wrapper structures combine xdrgen types with legacy lockd_lock. + * The xdrgen field must be first so the structure can be cast + * to its XDR type for the RPC dispatch layer. + */ +struct nlm_testargs_wrapper { + struct nlm_testargs xdrgen; + struct lockd_lock lock; +}; -#define NLMDBG_FACILITY NLMDBG_CLIENT +static_assert(offsetof(struct nlm_testargs_wrapper, xdrgen) == 0); -#ifdef CONFIG_LOCKD_V4 -static inline __be32 cast_status(__be32 status) +struct nlm_testres_wrapper { + struct nlm_testres xdrgen; + struct lockd_lock lock; +}; + +static_assert(offsetof(struct nlm_testres_wrapper, xdrgen) == 0); + +struct nlm_lockargs_wrapper { + struct nlm_lockargs xdrgen; + struct lockd_cookie cookie; + struct lockd_lock lock; +}; + +static_assert(offsetof(struct nlm_lockargs_wrapper, xdrgen) == 0); + +struct nlm_res_wrapper { + struct nlm_res xdrgen; + struct lockd_cookie cookie; +}; + +static_assert(offsetof(struct nlm_res_wrapper, xdrgen) == 0); + +struct nlm_cancargs_wrapper { + struct nlm_cancargs xdrgen; + struct lockd_lock lock; +}; + +static_assert(offsetof(struct nlm_cancargs_wrapper, xdrgen) == 0); + +struct nlm_unlockargs_wrapper { + struct nlm_unlockargs xdrgen; + struct lockd_lock lock; +}; + +static_assert(offsetof(struct nlm_unlockargs_wrapper, xdrgen) == 0); + +struct nlm_notifyargs_wrapper { + struct nlm_notifyargs xdrgen; + struct lockd_reboot reboot; +}; + +static_assert(offsetof(struct nlm_notifyargs_wrapper, xdrgen) == 0); + +struct nlm_shareargs_wrapper { + struct nlm_shareargs xdrgen; + struct lockd_lock lock; +}; + +static_assert(offsetof(struct nlm_shareargs_wrapper, xdrgen) == 0); + +struct nlm_shareres_wrapper { + struct nlm_shareres xdrgen; +}; + +static_assert(offsetof(struct nlm_shareres_wrapper, xdrgen) == 0); + +struct nlm_notify_wrapper { + struct nlm_notify xdrgen; +}; + +static_assert(offsetof(struct nlm_notify_wrapper, xdrgen) == 0); + +static __be32 +nlm_netobj_to_cookie(struct lockd_cookie *cookie, netobj *object) { - switch (status) { - case nlm_granted: - case nlm_lck_denied: - case nlm_lck_denied_nolocks: - case nlm_lck_blocked: - case nlm_lck_denied_grace_period: - case nlm__int__drop_reply: - break; - case nlm__int__deadlock: - status = nlm_lck_denied; - break; - default: - status = nlm_lck_denied_nolocks; - } + if (object->len > NLM_MAXCOOKIELEN) + return nlm_lck_denied_nolocks; + cookie->len = object->len; + memcpy(cookie->data, object->data, object->len); + return nlm_granted; +} - return status; +static __be32 +nlm_lock_to_lockd_lock(struct lockd_lock *lock, struct nlm_lock *alock) +{ + if (alock->fh.len != NLM3_FHSIZE) + return nlm_lck_denied; + lock->fh.size = alock->fh.len; + memcpy(lock->fh.data, alock->fh.data, alock->fh.len); + lock->oh.len = alock->oh.len; + lock->oh.data = alock->oh.data; + lock->svid = alock->uppid; + lockd_set_file_lock_range3(&lock->fl, alock->l_offset, alock->l_len); + return nlm_granted; } -#else -static inline __be32 cast_status(__be32 status) + +static struct nlm_host * +nlm3svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored) { - switch (status) { - case nlm__int__deadlock: - status = nlm_lck_denied; - break; - case nlm__int__stale_fh: - case nlm__int__failed: - status = nlm_lck_denied_nolocks; - break; - default: - if (be32_to_cpu(status) >= 30000) - pr_warn_once("lockd: unhandled internal status %u\n", - be32_to_cpu(status)); - break; + struct nlm_host *host; + + if (!nlmsvc_ops) + return NULL; + host = nlmsvc_lookup_host(rqstp, caller.data, caller.len); + if (!host) + return NULL; + if (monitored && nsm_monitor(host) < 0) { + nlmsvc_release_host(host); + return NULL; } - return status; + return host; } -#endif -/* - * Obtain client and file from arguments - */ static __be32 -nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, - struct nlm_host **hostp, struct nlm_file **filp) +nlm3svc_lookup_file(struct svc_rqst *rqstp, struct nlm_host *host, + struct lockd_lock *lock, struct nlm_file **filp, + struct nlm_lock *xdr_lock, unsigned char type) { - struct nlm_host *host = NULL; - struct nlm_file *file = NULL; - struct nlm_lock *lock = &argp->lock; - bool is_test = (rqstp->rq_proc == NLMPROC_TEST || - rqstp->rq_proc == NLMPROC_TEST_MSG); - int mode; - __be32 error = 0; + bool is_test = (rqstp->rq_proc == NLMPROC_TEST || + rqstp->rq_proc == NLMPROC_TEST_MSG); + struct file_lock *fl = &lock->fl; + struct nlm_file *file = NULL; + __be32 error; + int mode; - /* nfsd callbacks must have been installed for this procedure */ - if (!nlmsvc_ops) + if (xdr_lock->fh.len != NLM3_FHSIZE) return nlm_lck_denied_nolocks; + lock->fh.size = xdr_lock->fh.len; + memcpy(lock->fh.data, xdr_lock->fh.data, xdr_lock->fh.len); - /* Obtain host handle */ - if (!(host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len)) - || (argp->monitor && nsm_monitor(host) < 0)) - goto no_locks; - *hostp = host; + lock->oh.len = xdr_lock->oh.len; + lock->oh.data = xdr_lock->oh.data; - /* Obtain file pointer. Not used by FREE_ALL call. */ - if (filp != NULL) { - mode = lock_to_openmode(&lock->fl); + lock->svid = xdr_lock->uppid; + lock->lock_start = xdr_lock->l_offset; + lock->lock_len = xdr_lock->l_len; - if (is_test) - mode = O_RDWR; + locks_init_lock(fl); + fl->c.flc_type = type; + lockd_set_file_lock_range3(fl, lock->lock_start, lock->lock_len); - error = cast_status(nlm_lookup_file(rqstp, &file, lock, mode)); - if (error != 0) - goto no_locks; - *filp = file; + mode = lock_to_openmode(fl); + if (is_test) + mode = O_RDWR; - /* Set up the missing parts of the file_lock structure */ - lock->fl.c.flc_flags = FL_POSIX; - if (is_test) - lock->fl.c.flc_file = nlmsvc_file_file(file); - else - lock->fl.c.flc_file = file->f_file[mode]; - lock->fl.c.flc_pid = current->tgid; - lock->fl.fl_lmops = &nlmsvc_lock_operations; - nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid); - if (!lock->fl.c.flc_owner) { - /* lockowner allocation has failed */ - nlmsvc_release_host(host); - return nlm_lck_denied_nolocks; - } + error = nlm_lookup_file(rqstp, &file, lock, mode); + switch (error) { + case nlm_granted: + break; + case nlm__int__stale_fh: + case nlm__int__failed: + return nlm_lck_denied_nolocks; + default: + return error; } + *filp = file; - return 0; + fl->c.flc_flags = FL_POSIX; + if (is_test) + fl->c.flc_file = nlmsvc_file_file(file); + else + fl->c.flc_file = file->f_file[mode]; + fl->c.flc_pid = current->tgid; + fl->fl_lmops = &nlmsvc_lock_operations; + nlmsvc_locks_init_private(fl, host, (pid_t)lock->svid); + if (!fl->c.flc_owner) + return nlm_lck_denied_nolocks; -no_locks: - nlmsvc_release_host(host); - if (error) - return error; - return nlm_lck_denied_nolocks; + return nlm_granted; } -/* - * NULL: Test for presence of service +static inline __be32 cast_status(__be32 status) +{ + switch (status) { + case nlm_granted: + case nlm_lck_denied: + case nlm_lck_denied_nolocks: + case nlm_lck_blocked: + case nlm_lck_denied_grace_period: + case nlm__int__drop_reply: + break; + case nlm__int__deadlock: + status = nlm_lck_denied; + break; + default: + pr_warn_once("lockd: unhandled internal status %u\n", + be32_to_cpu(status)); + status = nlm_lck_denied_nolocks; + break; + } + return status; +} + +/** + * nlmsvc_proc_null - NULL: Test for presence of service + * @rqstp: RPC transaction context + * + * Return: + * %rpc_success: RPC executed successfully + * + * RPC synopsis: + * void NLM_NULL(void) = 0; */ -static __be32 -nlmsvc_proc_null(struct svc_rqst *rqstp) +static __be32 nlmsvc_proc_null(struct svc_rqst *rqstp) { - dprintk("lockd: NULL called\n"); return rpc_success; } -/* - * TEST: Check for conflicting lock +/** + * nlmsvc_proc_test - TEST: Check for conflicting lock + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * + * RPC synopsis: + * nlm_testres NLM_TEST(nlm_testargs) = 1; + * + * Permissible procedure status codes: + * %LCK_GRANTED: The server would be able to grant the + * requested lock. + * %LCK_DENIED: The requested lock conflicted with existing + * lock reservations for the file. + * %LCK_DENIED_NOLOCKS: The server could not allocate the resources + * needed to process the request. + * %LCK_DENIED_GRACE_PERIOD: The server has recently restarted and is + * re-establishing existing locks, and is not + * yet ready to accept normal service requests. */ -static __be32 -__nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp) +static __be32 nlmsvc_proc_test(struct svc_rqst *rqstp) { - struct nlm_args *argp = rqstp->rq_argp; + struct nlm_testargs_wrapper *argp = rqstp->rq_argp; + unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; + struct nlm_testres_wrapper *resp = rqstp->rq_resp; + struct nlm_file *file = NULL; struct nlm_host *host; - struct nlm_file *file; - __be32 rc = rpc_success; - dprintk("lockd: TEST called\n"); - resp->cookie = argp->cookie; + resp->xdrgen.cookie = argp->xdrgen.cookie; - /* Obtain client and file */ - if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm__int__drop_reply ? - rpc_drop_reply : rpc_success; + resp->xdrgen.test_stat.stat = nlm_lck_denied_nolocks; + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + goto out; - /* Now check for conflicting locks */ - resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, - &argp->lock, &resp->lock)); - if (resp->status == nlm__int__drop_reply) - rc = rpc_drop_reply; - else - dprintk("lockd: TEST status %d vers %d\n", - ntohl(resp->status), rqstp->rq_vers); + resp->xdrgen.test_stat.stat = + nlm3svc_lookup_file(rqstp, host, &argp->lock, &file, + &argp->xdrgen.alock, type); + if (resp->xdrgen.test_stat.stat) + goto out; + resp->xdrgen.test_stat.stat = + cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock, + &resp->lock)); nlmsvc_release_lockowner(&argp->lock); + + if (resp->xdrgen.test_stat.stat == nlm_lck_denied) { + struct lockd_lock *conf = &resp->lock; + struct nlm_holder *holder = &resp->xdrgen.test_stat.u.holder; + + holder->exclusive = (conf->fl.c.flc_type != F_RDLCK); + holder->uppid = conf->svid; + holder->oh.len = conf->oh.len; + holder->oh.data = conf->oh.data; + holder->l_offset = min_t(loff_t, conf->fl.fl_start, + NLM_OFFSET_MAX); + if (conf->fl.fl_end == OFFSET_MAX) + holder->l_len = 0; + else + holder->l_len = min_t(loff_t, + conf->fl.fl_end - + conf->fl.fl_start + 1, + NLM_OFFSET_MAX); + } + +out: + if (file) + nlm_release_file(file); nlmsvc_release_host(host); - nlm_release_file(file); - return rc; + return resp->xdrgen.test_stat.stat == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; } static __be32 -nlmsvc_proc_test(struct svc_rqst *rqstp) +nlmsvc_do_lock(struct svc_rqst *rqstp, bool monitored) { - return __nlmsvc_proc_test(rqstp, rqstp->rq_resp); -} + struct nlm_lockargs_wrapper *argp = rqstp->rq_argp; + unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; + struct nlm_res_wrapper *resp = rqstp->rq_resp; + struct nlm_file *file = NULL; + struct nlm_host *host = NULL; -static __be32 -__nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_res *resp) -{ - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_host *host; - struct nlm_file *file; - __be32 rc = rpc_success; + resp->xdrgen.cookie = argp->xdrgen.cookie; - dprintk("lockd: LOCK called\n"); + resp->xdrgen.stat.stat = nlm_netobj_to_cookie(&argp->cookie, + &argp->xdrgen.cookie); + if (resp->xdrgen.stat.stat) + goto out; - resp->cookie = argp->cookie; + resp->xdrgen.stat.stat = nlm_lck_denied_nolocks; + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, + monitored); + if (!host) + goto out; - /* Obtain client and file */ - if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm__int__drop_reply ? - rpc_drop_reply : rpc_success; + resp->xdrgen.stat.stat = nlm3svc_lookup_file(rqstp, host, &argp->lock, + &file, &argp->xdrgen.alock, + type); + if (resp->xdrgen.stat.stat) + goto out; - /* Now try to lock the file */ - resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock, - argp->block, &argp->cookie, - argp->reclaim)); - if (resp->status == nlm__int__drop_reply) - rc = rpc_drop_reply; - else - dprintk("lockd: LOCK status %d\n", ntohl(resp->status)); + resp->xdrgen.stat.stat = cast_status(nlmsvc_lock(rqstp, file, host, + &argp->lock, + argp->xdrgen.block, + &argp->cookie, + argp->xdrgen.reclaim)); nlmsvc_release_lockowner(&argp->lock); + +out: + if (file) + nlm_release_file(file); nlmsvc_release_host(host); - nlm_release_file(file); - return rc; + return resp->xdrgen.stat.stat == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; } +/** + * nlmsvc_proc_lock - LOCK: Establish a monitored lock + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * + * RPC synopsis: + * nlm_res NLM_LOCK(nlm_lockargs) = 2; + * + * Permissible procedure status codes: + * %LCK_GRANTED: The requested lock was granted. + * %LCK_DENIED: The requested lock conflicted with existing + * lock reservations for the file. + * %LCK_DENIED_NOLOCKS: The server could not allocate the resources + * needed to process the request. + * %LCK_BLOCKED: The blocking request cannot be granted + * immediately. The server will send an + * NLM_GRANTED callback to the client when + * the lock can be granted. + * %LCK_DENIED_GRACE_PERIOD: The server has recently restarted and is + * re-establishing existing locks, and is not + * yet ready to accept normal service requests. + */ static __be32 nlmsvc_proc_lock(struct svc_rqst *rqstp) { - return __nlmsvc_proc_lock(rqstp, rqstp->rq_resp); + return nlmsvc_do_lock(rqstp, true); } +/** + * nlmsvc_proc_cancel - CANCEL: Cancel an outstanding blocked lock request + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * + * RPC synopsis: + * nlm_res NLM_CANCEL(nlm_cancargs) = 3; + * + * Permissible procedure status codes: + * %LCK_GRANTED: The requested lock was canceled. + * %LCK_DENIED: There was no lock to cancel. + * %LCK_DENIED_GRACE_PERIOD: The server has recently restarted and is + * re-establishing existing locks, and is not + * yet ready to accept normal service requests. + * + * The Linux NLM server implementation also returns: + * %LCK_DENIED_NOLOCKS: A needed resource could not be allocated. + */ static __be32 -__nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_res *resp) +nlmsvc_proc_cancel(struct svc_rqst *rqstp) { - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_host *host; - struct nlm_file *file; + struct nlm_cancargs_wrapper *argp = rqstp->rq_argp; + unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; + struct nlm_res_wrapper *resp = rqstp->rq_resp; struct net *net = SVC_NET(rqstp); + struct nlm_host *host = NULL; + struct nlm_file *file = NULL; - dprintk("lockd: CANCEL called\n"); - - resp->cookie = argp->cookie; + resp->xdrgen.cookie = argp->xdrgen.cookie; - /* Don't accept requests during grace period */ - if (locks_in_grace(net)) { - resp->status = nlm_lck_denied_grace_period; - return rpc_success; - } + resp->xdrgen.stat.stat = nlm_lck_denied_grace_period; + if (locks_in_grace(net)) + goto out; - /* Obtain client and file */ - if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm__int__drop_reply ? - rpc_drop_reply : rpc_success; + resp->xdrgen.stat.stat = nlm_lck_denied_nolocks; + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + goto out; - /* Try to cancel request. */ - resp->status = cast_status(nlmsvc_cancel_blocked(net, file, &argp->lock)); + resp->xdrgen.stat.stat = nlm3svc_lookup_file(rqstp, host, &argp->lock, + &file, &argp->xdrgen.alock, + type); + if (resp->xdrgen.stat.stat) + goto out; - dprintk("lockd: CANCEL status %d\n", ntohl(resp->status)); + resp->xdrgen.stat.stat = nlmsvc_cancel_blocked(net, file, &argp->lock); nlmsvc_release_lockowner(&argp->lock); - nlmsvc_release_host(host); - nlm_release_file(file); - return rpc_success; -} -static __be32 -nlmsvc_proc_cancel(struct svc_rqst *rqstp) -{ - return __nlmsvc_proc_cancel(rqstp, rqstp->rq_resp); +out: + if (file) + nlm_release_file(file); + nlmsvc_release_host(host); + return resp->xdrgen.stat.stat == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; } -/* - * UNLOCK: release a lock +/** + * nlmsvc_proc_unlock - UNLOCK: Remove a lock + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * + * RPC synopsis: + * nlm_res NLM_UNLOCK(nlm_unlockargs) = 4; + * + * Permissible procedure status codes: + * %LCK_GRANTED: The requested lock was released. + * %LCK_DENIED_GRACE_PERIOD: The server has recently restarted and is + * re-establishing existing locks, and is not + * yet ready to accept normal service requests. + * + * The Linux NLM server implementation also returns: + * %LCK_DENIED_NOLOCKS: A needed resource could not be allocated. */ static __be32 -__nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_res *resp) +nlmsvc_proc_unlock(struct svc_rqst *rqstp) { - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_host *host; - struct nlm_file *file; + struct nlm_unlockargs_wrapper *argp = rqstp->rq_argp; + struct nlm_res_wrapper *resp = rqstp->rq_resp; struct net *net = SVC_NET(rqstp); + struct nlm_host *host = NULL; + struct nlm_file *file = NULL; - dprintk("lockd: UNLOCK called\n"); + resp->xdrgen.cookie = argp->xdrgen.cookie; - resp->cookie = argp->cookie; + resp->xdrgen.stat.stat = nlm_lck_denied_grace_period; + if (locks_in_grace(net)) + goto out; - /* Don't accept new lock requests during grace period */ - if (locks_in_grace(net)) { - resp->status = nlm_lck_denied_grace_period; - return rpc_success; - } - - /* Obtain client and file */ - if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm__int__drop_reply ? - rpc_drop_reply : rpc_success; + resp->xdrgen.stat.stat = nlm_lck_denied_nolocks; + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + goto out; - /* Now try to remove the lock */ - resp->status = cast_status(nlmsvc_unlock(net, file, &argp->lock)); + resp->xdrgen.stat.stat = nlm3svc_lookup_file(rqstp, host, &argp->lock, + &file, &argp->xdrgen.alock, + F_UNLCK); + if (resp->xdrgen.stat.stat) + goto out; - dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status)); + resp->xdrgen.stat.stat = nlmsvc_unlock(net, file, &argp->lock); nlmsvc_release_lockowner(&argp->lock); - nlmsvc_release_host(host); - nlm_release_file(file); - return rpc_success; -} -static __be32 -nlmsvc_proc_unlock(struct svc_rqst *rqstp) -{ - return __nlmsvc_proc_unlock(rqstp, rqstp->rq_resp); +out: + if (file) + nlm_release_file(file); + nlmsvc_release_host(host); + return resp->xdrgen.stat.stat == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; } -/* - * GRANTED: A server calls us to tell that a process' lock request - * was granted +/** + * nlmsvc_proc_granted - GRANTED: Blocked lock has been granted + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * + * RPC synopsis: + * nlm_res NLM_GRANTED(nlm_testargs) = 5; + * + * Permissible procedure status codes: + * %LCK_GRANTED: The granted lock was accepted. + * %LCK_DENIED: The procedure failed, possibly due to + * internal resource constraints. + * %LCK_DENIED_GRACE_PERIOD: The client host recently restarted and + * its NLM is re-establishing existing locks, + * so it is not yet ready to accept callbacks. */ static __be32 -__nlmsvc_proc_granted(struct svc_rqst *rqstp, struct nlm_res *resp) +nlmsvc_proc_granted(struct svc_rqst *rqstp) { - struct nlm_args *argp = rqstp->rq_argp; + struct nlm_testargs_wrapper *argp = rqstp->rq_argp; + struct nlm_res_wrapper *resp = rqstp->rq_resp; - resp->cookie = argp->cookie; + resp->xdrgen.cookie = argp->xdrgen.cookie; - dprintk("lockd: GRANTED called\n"); - resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock); - dprintk("lockd: GRANTED status %d\n", ntohl(resp->status)); - return rpc_success; -} + resp->xdrgen.stat.stat = nlm_lock_to_lockd_lock(&argp->lock, + &argp->xdrgen.alock); + if (resp->xdrgen.stat.stat) + goto out; -static __be32 -nlmsvc_proc_granted(struct svc_rqst *rqstp) -{ - return __nlmsvc_proc_granted(rqstp, rqstp->rq_resp); + resp->xdrgen.stat.stat = nlmclnt_grant(svc_addr(rqstp), &argp->lock); + +out: + return rpc_success; } /* @@ -342,20 +573,13 @@ static const struct rpc_call_ops nlmsvc_callback_ops = { * because we send the callback before the reply proper. I hope this * doesn't break any clients. */ -static __be32 nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, - __be32 (*func)(struct svc_rqst *, struct nlm_res *)) +static __be32 +nlmsvc_callback(struct svc_rqst *rqstp, struct nlm_host *host, u32 proc, + __be32 (*func)(struct svc_rqst *, struct lockd_res *)) { - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_host *host; struct nlm_rqst *call; __be32 stat; - host = nlmsvc_lookup_host(rqstp, - argp->lock.caller, - argp->lock.len); - if (host == NULL) - return rpc_system_err; - call = nlm_alloc_call(host); nlmsvc_release_host(host); if (call == NULL) @@ -373,440 +597,819 @@ static __be32 nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, return rpc_success; } -static __be32 nlmsvc_proc_test_msg(struct svc_rqst *rqstp) +static __be32 +__nlmsvc_proc_test_msg(struct svc_rqst *rqstp, struct lockd_res *resp) { - dprintk("lockd: TEST_MSG called\n"); - return nlmsvc_callback(rqstp, NLMPROC_TEST_RES, __nlmsvc_proc_test); + struct nlm_testargs_wrapper *argp = rqstp->rq_argp; + unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; + struct nlm_lockowner *owner; + struct nlm_file *file = NULL; + struct nlm_host *host = NULL; + + resp->status = nlm_lck_denied_nolocks; + if (nlm_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie)) + goto out; + + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + goto out; + + resp->status = nlm3svc_lookup_file(rqstp, host, &argp->lock, + &file, &argp->xdrgen.alock, type); + if (resp->status) + goto out; + + owner = argp->lock.fl.c.flc_owner; + resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, + &argp->lock, &resp->lock)); + nlmsvc_put_lockowner(owner); + +out: + if (file) + nlm_release_file(file); + nlmsvc_release_host(host); + return resp->status == nlm__int__drop_reply ? rpc_drop_reply : rpc_success; } -static __be32 nlmsvc_proc_lock_msg(struct svc_rqst *rqstp) +/** + * nlmsvc_proc_test_msg - TEST_MSG: Check for conflicting lock + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * %rpc_garbage_args: The request arguments are malformed. + * %rpc_system_err: RPC execution failed. + * + * RPC synopsis: + * void NLMPROC_TEST_MSG(nlm_testargs) = 6; + * + * The response to this request is delivered via the TEST_RES procedure. + */ +static __be32 nlmsvc_proc_test_msg(struct svc_rqst *rqstp) { - dprintk("lockd: LOCK_MSG called\n"); - return nlmsvc_callback(rqstp, NLMPROC_LOCK_RES, __nlmsvc_proc_lock); + struct nlm_testargs_wrapper *argp = rqstp->rq_argp; + struct nlm_host *host; + + if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN) + return rpc_garbage_args; + + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + return rpc_system_err; + + return nlmsvc_callback(rqstp, host, NLMPROC_TEST_RES, + __nlmsvc_proc_test_msg); } -static __be32 nlmsvc_proc_cancel_msg(struct svc_rqst *rqstp) +static __be32 +__nlmsvc_proc_lock_msg(struct svc_rqst *rqstp, struct lockd_res *resp) { - dprintk("lockd: CANCEL_MSG called\n"); - return nlmsvc_callback(rqstp, NLMPROC_CANCEL_RES, __nlmsvc_proc_cancel); + struct nlm_lockargs_wrapper *argp = rqstp->rq_argp; + unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; + struct nlm_file *file = NULL; + struct nlm_host *host = NULL; + + resp->status = nlm_lck_denied_nolocks; + if (nlm_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie)) + goto out; + + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, true); + if (!host) + goto out; + + resp->status = nlm3svc_lookup_file(rqstp, host, &argp->lock, + &file, &argp->xdrgen.alock, type); + if (resp->status) + goto out; + + resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock, + argp->xdrgen.block, &resp->cookie, + argp->xdrgen.reclaim)); + nlmsvc_release_lockowner(&argp->lock); + +out: + if (file) + nlm_release_file(file); + nlmsvc_release_host(host); + return resp->status == nlm__int__drop_reply ? rpc_drop_reply : rpc_success; } -static __be32 -nlmsvc_proc_unlock_msg(struct svc_rqst *rqstp) +/** + * nlmsvc_proc_lock_msg - LOCK_MSG: Establish a monitored lock + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * %rpc_garbage_args: The request arguments are malformed. + * %rpc_system_err: RPC execution failed. + * + * RPC synopsis: + * void NLMPROC_LOCK_MSG(nlm_lockargs) = 7; + * + * The response to this request is delivered via the LOCK_RES procedure. + */ +static __be32 nlmsvc_proc_lock_msg(struct svc_rqst *rqstp) { - dprintk("lockd: UNLOCK_MSG called\n"); - return nlmsvc_callback(rqstp, NLMPROC_UNLOCK_RES, __nlmsvc_proc_unlock); + struct nlm_lockargs_wrapper *argp = rqstp->rq_argp; + struct nlm_host *host; + + if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN) + return rpc_garbage_args; + + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + return rpc_system_err; + + return nlmsvc_callback(rqstp, host, NLMPROC_LOCK_RES, + __nlmsvc_proc_lock_msg); } static __be32 -nlmsvc_proc_granted_msg(struct svc_rqst *rqstp) +__nlmsvc_proc_cancel_msg(struct svc_rqst *rqstp, struct lockd_res *resp) { - dprintk("lockd: GRANTED_MSG called\n"); - return nlmsvc_callback(rqstp, NLMPROC_GRANTED_RES, __nlmsvc_proc_granted); + struct nlm_cancargs_wrapper *argp = rqstp->rq_argp; + unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; + struct net *net = SVC_NET(rqstp); + struct nlm_file *file = NULL; + struct nlm_host *host = NULL; + + resp->status = nlm_lck_denied_nolocks; + if (nlm_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie)) + goto out; + + resp->status = nlm_lck_denied_grace_period; + if (locks_in_grace(net)) + goto out; + + resp->status = nlm_lck_denied_nolocks; + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + goto out; + + resp->status = nlm3svc_lookup_file(rqstp, host, &argp->lock, + &file, &argp->xdrgen.alock, type); + if (resp->status) + goto out; + + resp->status = nlmsvc_cancel_blocked(net, file, &argp->lock); + nlmsvc_release_lockowner(&argp->lock); + +out: + if (file) + nlm_release_file(file); + nlmsvc_release_host(host); + return resp->status == nlm__int__drop_reply ? rpc_drop_reply : rpc_success; } -/* - * SHARE: create a DOS share or alter existing share. +/** + * nlmsvc_proc_cancel_msg - CANCEL_MSG: Cancel an outstanding lock request + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * %rpc_garbage_args: The request arguments are malformed. + * %rpc_system_err: RPC execution failed. + * + * RPC synopsis: + * void NLMPROC_CANCEL_MSG(nlm_cancargs) = 8; + * + * The response to this request is delivered via the CANCEL_RES procedure. */ -static __be32 -nlmsvc_proc_share(struct svc_rqst *rqstp) +static __be32 nlmsvc_proc_cancel_msg(struct svc_rqst *rqstp) { - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_res *resp = rqstp->rq_resp; - struct nlm_host *host; - struct nlm_file *file; + struct nlm_cancargs_wrapper *argp = rqstp->rq_argp; + struct nlm_host *host; + + if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN) + return rpc_garbage_args; - dprintk("lockd: SHARE called\n"); + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + return rpc_system_err; - resp->cookie = argp->cookie; + return nlmsvc_callback(rqstp, host, NLMPROC_CANCEL_RES, + __nlmsvc_proc_cancel_msg); +} - /* Don't accept new lock requests during grace period */ - if (locks_in_grace(SVC_NET(rqstp)) && !argp->reclaim) { - resp->status = nlm_lck_denied_grace_period; - return rpc_success; - } +static __be32 +__nlmsvc_proc_unlock_msg(struct svc_rqst *rqstp, struct lockd_res *resp) +{ + struct nlm_unlockargs_wrapper *argp = rqstp->rq_argp; + struct net *net = SVC_NET(rqstp); + struct nlm_file *file = NULL; + struct nlm_host *host = NULL; + + resp->status = nlm_lck_denied_nolocks; + if (nlm_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie)) + goto out; + + resp->status = nlm_lck_denied_grace_period; + if (locks_in_grace(net)) + goto out; - /* Obtain client and file */ - if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm__int__drop_reply ? - rpc_drop_reply : rpc_success; + resp->status = nlm_lck_denied_nolocks; + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + goto out; - /* Now try to create the share */ - resp->status = cast_status(nlmsvc_share_file(host, file, &argp->lock.oh, - argp->fsm_access, - argp->fsm_mode)); + resp->status = nlm3svc_lookup_file(rqstp, host, &argp->lock, + &file, &argp->xdrgen.alock, F_UNLCK); + if (resp->status) + goto out; - dprintk("lockd: SHARE status %d\n", ntohl(resp->status)); + resp->status = nlmsvc_unlock(net, file, &argp->lock); nlmsvc_release_lockowner(&argp->lock); + +out: + if (file) + nlm_release_file(file); nlmsvc_release_host(host); - nlm_release_file(file); - return rpc_success; + return resp->status == nlm__int__drop_reply ? rpc_drop_reply : rpc_success; } -/* - * UNSHARE: Release a DOS share. +/** + * nlmsvc_proc_unlock_msg - UNLOCK_MSG: Remove an existing lock + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * %rpc_garbage_args: The request arguments are malformed. + * %rpc_system_err: RPC execution failed. + * + * RPC synopsis: + * void NLMPROC_UNLOCK_MSG(nlm_unlockargs) = 9; + * + * The response to this request is delivered via the UNLOCK_RES procedure. */ -static __be32 -nlmsvc_proc_unshare(struct svc_rqst *rqstp) +static __be32 nlmsvc_proc_unlock_msg(struct svc_rqst *rqstp) { - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_res *resp = rqstp->rq_resp; - struct nlm_host *host; - struct nlm_file *file; + struct nlm_unlockargs_wrapper *argp = rqstp->rq_argp; + struct nlm_host *host; - dprintk("lockd: UNSHARE called\n"); + if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN) + return rpc_garbage_args; - resp->cookie = argp->cookie; + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + return rpc_system_err; - /* Don't accept requests during grace period */ - if (locks_in_grace(SVC_NET(rqstp))) { - resp->status = nlm_lck_denied_grace_period; - return rpc_success; - } + return nlmsvc_callback(rqstp, host, NLMPROC_UNLOCK_RES, + __nlmsvc_proc_unlock_msg); +} - /* Obtain client and file */ - if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm__int__drop_reply ? - rpc_drop_reply : rpc_success; +static __be32 +__nlmsvc_proc_granted_msg(struct svc_rqst *rqstp, struct lockd_res *resp) +{ + struct nlm_testargs_wrapper *argp = rqstp->rq_argp; - /* Now try to unshare the file */ - resp->status = cast_status(nlmsvc_unshare_file(host, file, - &argp->lock.oh)); + resp->status = nlm_lck_denied; + if (nlm_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie)) + goto out; - dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status)); - nlmsvc_release_lockowner(&argp->lock); - nlmsvc_release_host(host); - nlm_release_file(file); + if (nlm_lock_to_lockd_lock(&argp->lock, &argp->xdrgen.alock)) + goto out; + + resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock); + +out: return rpc_success; } -/* - * NM_LOCK: Create an unmonitored lock +/** + * nlmsvc_proc_granted_msg - GRANTED_MSG: Blocked lock has been granted + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_garbage_args: The request arguments are malformed. + * %rpc_system_err: RPC execution failed. + * + * RPC synopsis: + * void NLMPROC_GRANTED_MSG(nlm_testargs) = 10; + * + * The response to this request is delivered via the GRANTED_RES procedure. */ -static __be32 -nlmsvc_proc_nm_lock(struct svc_rqst *rqstp) +static __be32 nlmsvc_proc_granted_msg(struct svc_rqst *rqstp) { - struct nlm_args *argp = rqstp->rq_argp; + struct nlm_testargs_wrapper *argp = rqstp->rq_argp; + struct nlm_host *host; - dprintk("lockd: NM_LOCK called\n"); + if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN) + return rpc_garbage_args; - argp->monitor = 0; /* just clean the monitor flag */ - return nlmsvc_proc_lock(rqstp); + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + return rpc_system_err; + + return nlmsvc_callback(rqstp, host, NLMPROC_GRANTED_RES, + __nlmsvc_proc_granted_msg); } -/* - * FREE_ALL: Release all locks and shares held by client +/** + * nlmsvc_proc_granted_res - GRANTED_RES: Lock Granted result + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * + * RPC synopsis: + * void NLMPROC_GRANTED_RES(nlm_res) = 15; */ -static __be32 -nlmsvc_proc_free_all(struct svc_rqst *rqstp) +static __be32 nlmsvc_proc_granted_res(struct svc_rqst *rqstp) { - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_host *host; + struct nlm_res_wrapper *argp = rqstp->rq_argp; - /* Obtain client */ - if (nlmsvc_retrieve_args(rqstp, argp, &host, NULL)) + if (!nlmsvc_ops) return rpc_success; - nlmsvc_free_host_resources(host); - nlmsvc_release_host(host); + if (nlm_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie)) + return rpc_success; + nlmsvc_grant_reply(&argp->cookie, argp->xdrgen.stat.stat); + return rpc_success; } -/* - * SM_NOTIFY: private callback from statd (not part of official NLM proto) +/** + * nlmsvc_proc_sm_notify - SM_NOTIFY: Peer has rebooted + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_system_err: RPC execution failed. + * + * The SM_NOTIFY procedure is a private callback from Linux statd and is + * not part of the official NLM protocol. + * + * RPC synopsis: + * void NLM_SM_NOTIFY(nlm_notifyargs) = 16; */ -static __be32 -nlmsvc_proc_sm_notify(struct svc_rqst *rqstp) +static __be32 nlmsvc_proc_sm_notify(struct svc_rqst *rqstp) { - struct nlm_reboot *argp = rqstp->rq_argp; - - dprintk("lockd: SM_NOTIFY called\n"); + struct nlm_notifyargs_wrapper *argp = rqstp->rq_argp; + struct lockd_reboot *reboot = &argp->reboot; if (!nlm_privileged_requester(rqstp)) { char buf[RPC_MAX_ADDRBUFLEN]; - printk(KERN_WARNING "lockd: rejected NSM callback from %s\n", - svc_print_addr(rqstp, buf, sizeof(buf))); + + pr_warn("lockd: rejected NSM callback from %s\n", + svc_print_addr(rqstp, buf, sizeof(buf))); return rpc_system_err; } - nlm_host_rebooted(SVC_NET(rqstp), argp); + reboot->len = argp->xdrgen.notify.name.len; + reboot->mon = (char *)argp->xdrgen.notify.name.data; + reboot->state = argp->xdrgen.notify.state; + memcpy(&reboot->priv.data, argp->xdrgen.private, + sizeof(reboot->priv.data)); + + nlm_host_rebooted(SVC_NET(rqstp), reboot); + return rpc_success; } -/* - * client sent a GRANTED_RES, let's remove the associated block +/** + * nlmsvc_proc_unused - stub for unused procedures + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_proc_unavail: Program can't support procedure. */ -static __be32 -nlmsvc_proc_granted_res(struct svc_rqst *rqstp) +static __be32 nlmsvc_proc_unused(struct svc_rqst *rqstp) { - struct nlm_res *argp = rqstp->rq_argp; + return rpc_proc_unavail; +} - if (!nlmsvc_ops) - return rpc_success; +/** + * nlmsvc_proc_share - SHARE: Open a file using DOS file-sharing modes + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * + * RPC synopsis: + * nlm_shareres NLM_SHARE(nlm_shareargs) = 20; + * + * Permissible procedure status codes: + * %LCK_GRANTED: The requested share lock was granted. + * %LCK_DENIED: The requested lock conflicted with existing + * lock reservations for the file. + * %LCK_DENIED_GRACE_PERIOD: The server has recently restarted and is + * re-establishing existing locks, and is not + * yet ready to accept normal service requests. + * + * The Linux NLM server implementation also returns: + * %LCK_DENIED_NOLOCKS: A needed resource could not be allocated. + */ +static __be32 nlmsvc_proc_share(struct svc_rqst *rqstp) +{ + struct nlm_shareargs_wrapper *argp = rqstp->rq_argp; + struct nlm_shareres_wrapper *resp = rqstp->rq_resp; + struct lockd_lock *lock = &argp->lock; + struct nlm_host *host = NULL; + struct nlm_file *file = NULL; + struct nlm_lock xdr_lock = { + .fh = argp->xdrgen.share.fh, + .oh = argp->xdrgen.share.oh, + .uppid = LOCKD_SHARE_SVID, + }; - dprintk("lockd: GRANTED_RES called\n"); + resp->xdrgen.cookie = argp->xdrgen.cookie; - nlmsvc_grant_reply(&argp->cookie, argp->status); - return rpc_success; + resp->xdrgen.stat = nlm_lck_denied_grace_period; + if (locks_in_grace(SVC_NET(rqstp)) && !argp->xdrgen.reclaim) + goto out; + + resp->xdrgen.stat = nlm_lck_denied_nolocks; + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.share.caller_name, false); + if (!host) + goto out; + + resp->xdrgen.stat = nlm3svc_lookup_file(rqstp, host, lock, &file, + &xdr_lock, F_RDLCK); + if (resp->xdrgen.stat) + goto out; + + resp->xdrgen.stat = nlmsvc_share_file(host, file, &lock->oh, + argp->xdrgen.share.access, + argp->xdrgen.share.mode); + + nlmsvc_release_lockowner(lock); + +out: + if (file) + nlm_release_file(file); + nlmsvc_release_host(host); + return resp->xdrgen.stat == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; } -static __be32 -nlmsvc_proc_unused(struct svc_rqst *rqstp) +/** + * nlmsvc_proc_unshare - UNSHARE: Release a share reservation + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * + * RPC synopsis: + * nlm_shareres NLM_UNSHARE(nlm_shareargs) = 21; + * + * Permissible procedure status codes: + * %LCK_GRANTED: The share reservation was released. + * %LCK_DENIED_GRACE_PERIOD: The server has recently restarted and is + * re-establishing existing locks, and is not + * yet ready to accept normal service requests. + * + * The Linux NLM server implementation also returns: + * %LCK_DENIED_NOLOCKS: A needed resource could not be allocated. + */ +static __be32 nlmsvc_proc_unshare(struct svc_rqst *rqstp) { - return rpc_proc_unavail; + struct nlm_shareargs_wrapper *argp = rqstp->rq_argp; + struct nlm_shareres_wrapper *resp = rqstp->rq_resp; + struct lockd_lock *lock = &argp->lock; + struct nlm_lock xdr_lock = { + .fh = argp->xdrgen.share.fh, + .oh = argp->xdrgen.share.oh, + .uppid = LOCKD_SHARE_SVID, + }; + struct nlm_host *host = NULL; + struct nlm_file *file = NULL; + + resp->xdrgen.cookie = argp->xdrgen.cookie; + + resp->xdrgen.stat = nlm_lck_denied_grace_period; + if (locks_in_grace(SVC_NET(rqstp))) + goto out; + + resp->xdrgen.stat = nlm_lck_denied_nolocks; + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.share.caller_name, false); + if (!host) + goto out; + + resp->xdrgen.stat = nlm3svc_lookup_file(rqstp, host, lock, &file, + &xdr_lock, F_RDLCK); + if (resp->xdrgen.stat) + goto out; + + resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh); + + nlmsvc_release_lockowner(lock); + +out: + if (file) + nlm_release_file(file); + nlmsvc_release_host(host); + return resp->xdrgen.stat == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; } -/* - * NLM Server procedures. +/** + * nlmsvc_proc_nm_lock - NM_LOCK: Establish a non-monitored lock + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * + * RPC synopsis: + * nlm_res NLM_NM_LOCK(nlm_lockargs) = 22; + * + * Permissible procedure status codes: + * %LCK_GRANTED: The requested lock was granted. + * %LCK_DENIED: The requested lock conflicted with existing + * lock reservations for the file. + * %LCK_DENIED_NOLOCKS: The server could not allocate the resources + * needed to process the request. + * %LCK_BLOCKED: The blocking request cannot be granted + * immediately. The server will send an + * NLM_GRANTED callback to the client when + * the lock can be granted. + * %LCK_DENIED_GRACE_PERIOD: The server has recently restarted and is + * re-establishing existing locks, and is not + * yet ready to accept normal service requests. + */ +static __be32 nlmsvc_proc_nm_lock(struct svc_rqst *rqstp) +{ + return nlmsvc_do_lock(rqstp, false); +} + +/** + * nlmsvc_proc_free_all - FREE_ALL: Discard client's lock and share state + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * + * RPC synopsis: + * void NLMPROC_FREE_ALL(nlm_notify) = 23; */ +static __be32 nlmsvc_proc_free_all(struct svc_rqst *rqstp) +{ + struct nlm_notify_wrapper *argp = rqstp->rq_argp; + struct nlm_host *host; + + host = nlm3svc_lookup_host(rqstp, argp->xdrgen.name, false); + if (!host) + goto out; -struct nlm_void { int dummy; }; + nlmsvc_free_host_resources(host); + + nlmsvc_release_host(host); -#define Ck (1+XDR_QUADLEN(NLM_MAXCOOKIELEN)) /* cookie */ -#define St 1 /* status */ -#define No (1+1024/4) /* Net Obj */ -#define Rg 2 /* range - offset + size */ +out: + return rpc_success; +} + +/* + * NLMv3 Server procedures. + */ static const struct svc_procedure nlmsvc_procedures[24] = { - [NLMPROC_NULL] = { - .pc_func = nlmsvc_proc_null, - .pc_decode = nlmsvc_decode_void, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_void), - .pc_argzero = sizeof(struct nlm_void), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "NULL", + [NLM_NULL] = { + .pc_func = nlmsvc_proc_null, + .pc_decode = nlm_svc_decode_void, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = XDR_void, + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "NULL", }, - [NLMPROC_TEST] = { - .pc_func = nlmsvc_proc_test, - .pc_decode = nlmsvc_decode_testargs, - .pc_encode = nlmsvc_encode_testres, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St+2+No+Rg, - .pc_name = "TEST", + [NLM_TEST] = { + .pc_func = nlmsvc_proc_test, + .pc_decode = nlm_svc_decode_nlm_testargs, + .pc_encode = nlm_svc_encode_nlm_testres, + .pc_argsize = sizeof(struct nlm_testargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm_testres_wrapper), + .pc_xdrressize = NLM3_nlm_testres_sz, + .pc_name = "TEST", }, - [NLMPROC_LOCK] = { - .pc_func = nlmsvc_proc_lock, - .pc_decode = nlmsvc_decode_lockargs, - .pc_encode = nlmsvc_encode_res, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St, - .pc_name = "LOCK", + [NLM_LOCK] = { + .pc_func = nlmsvc_proc_lock, + .pc_decode = nlm_svc_decode_nlm_lockargs, + .pc_encode = nlm_svc_encode_nlm_res, + .pc_argsize = sizeof(struct nlm_lockargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm_res_wrapper), + .pc_xdrressize = NLM3_nlm_res_sz, + .pc_name = "LOCK", }, - [NLMPROC_CANCEL] = { - .pc_func = nlmsvc_proc_cancel, - .pc_decode = nlmsvc_decode_cancargs, - .pc_encode = nlmsvc_encode_res, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St, - .pc_name = "CANCEL", + [NLM_CANCEL] = { + .pc_func = nlmsvc_proc_cancel, + .pc_decode = nlm_svc_decode_nlm_cancargs, + .pc_encode = nlm_svc_encode_nlm_res, + .pc_argsize = sizeof(struct nlm_cancargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm_res_wrapper), + .pc_xdrressize = NLM3_nlm_res_sz, + .pc_name = "CANCEL", }, - [NLMPROC_UNLOCK] = { - .pc_func = nlmsvc_proc_unlock, - .pc_decode = nlmsvc_decode_unlockargs, - .pc_encode = nlmsvc_encode_res, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St, - .pc_name = "UNLOCK", + [NLM_UNLOCK] = { + .pc_func = nlmsvc_proc_unlock, + .pc_decode = nlm_svc_decode_nlm_unlockargs, + .pc_encode = nlm_svc_encode_nlm_res, + .pc_argsize = sizeof(struct nlm_unlockargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm_res_wrapper), + .pc_xdrressize = NLM3_nlm_res_sz, + .pc_name = "UNLOCK", }, - [NLMPROC_GRANTED] = { - .pc_func = nlmsvc_proc_granted, - .pc_decode = nlmsvc_decode_testargs, - .pc_encode = nlmsvc_encode_res, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St, - .pc_name = "GRANTED", + [NLM_GRANTED] = { + .pc_func = nlmsvc_proc_granted, + .pc_decode = nlm_svc_decode_nlm_testargs, + .pc_encode = nlm_svc_encode_nlm_res, + .pc_argsize = sizeof(struct nlm_testargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm_res_wrapper), + .pc_xdrressize = NLM3_nlm_res_sz, + .pc_name = "GRANTED", }, - [NLMPROC_TEST_MSG] = { - .pc_func = nlmsvc_proc_test_msg, - .pc_decode = nlmsvc_decode_testargs, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "TEST_MSG", + [NLM_TEST_MSG] = { + .pc_func = nlmsvc_proc_test_msg, + .pc_decode = nlm_svc_decode_nlm_testargs, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = sizeof(struct nlm_testargs_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "TEST_MSG", }, - [NLMPROC_LOCK_MSG] = { - .pc_func = nlmsvc_proc_lock_msg, - .pc_decode = nlmsvc_decode_lockargs, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "LOCK_MSG", + [NLM_LOCK_MSG] = { + .pc_func = nlmsvc_proc_lock_msg, + .pc_decode = nlm_svc_decode_nlm_lockargs, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = sizeof(struct nlm_lockargs_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "LOCK_MSG", }, - [NLMPROC_CANCEL_MSG] = { - .pc_func = nlmsvc_proc_cancel_msg, - .pc_decode = nlmsvc_decode_cancargs, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "CANCEL_MSG", + [NLM_CANCEL_MSG] = { + .pc_func = nlmsvc_proc_cancel_msg, + .pc_decode = nlm_svc_decode_nlm_cancargs, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = sizeof(struct nlm_cancargs_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "CANCEL_MSG", }, - [NLMPROC_UNLOCK_MSG] = { - .pc_func = nlmsvc_proc_unlock_msg, - .pc_decode = nlmsvc_decode_unlockargs, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "UNLOCK_MSG", + [NLM_UNLOCK_MSG] = { + .pc_func = nlmsvc_proc_unlock_msg, + .pc_decode = nlm_svc_decode_nlm_unlockargs, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = sizeof(struct nlm_unlockargs_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "UNLOCK_MSG", }, - [NLMPROC_GRANTED_MSG] = { - .pc_func = nlmsvc_proc_granted_msg, - .pc_decode = nlmsvc_decode_testargs, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "GRANTED_MSG", + [NLM_GRANTED_MSG] = { + .pc_func = nlmsvc_proc_granted_msg, + .pc_decode = nlm_svc_decode_nlm_testargs, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = sizeof(struct nlm_testargs_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "GRANTED_MSG", }, - [NLMPROC_TEST_RES] = { - .pc_func = nlmsvc_proc_null, - .pc_decode = nlmsvc_decode_void, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_res), - .pc_argzero = sizeof(struct nlm_res), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "TEST_RES", + [NLM_TEST_RES] = { + .pc_func = nlmsvc_proc_null, + .pc_decode = nlm_svc_decode_nlm_testres, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = sizeof(struct nlm_testres), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "TEST_RES", }, - [NLMPROC_LOCK_RES] = { - .pc_func = nlmsvc_proc_null, - .pc_decode = nlmsvc_decode_void, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_res), - .pc_argzero = sizeof(struct nlm_res), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "LOCK_RES", + [NLM_LOCK_RES] = { + .pc_func = nlmsvc_proc_null, + .pc_decode = nlm_svc_decode_nlm_res, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = sizeof(struct nlm_res), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "LOCK_RES", }, - [NLMPROC_CANCEL_RES] = { - .pc_func = nlmsvc_proc_null, - .pc_decode = nlmsvc_decode_void, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_res), - .pc_argzero = sizeof(struct nlm_res), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "CANCEL_RES", + [NLM_CANCEL_RES] = { + .pc_func = nlmsvc_proc_null, + .pc_decode = nlm_svc_decode_nlm_res, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = sizeof(struct nlm_res), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "CANCEL_RES", }, - [NLMPROC_UNLOCK_RES] = { - .pc_func = nlmsvc_proc_null, - .pc_decode = nlmsvc_decode_void, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_res), - .pc_argzero = sizeof(struct nlm_res), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "UNLOCK_RES", + [NLM_UNLOCK_RES] = { + .pc_func = nlmsvc_proc_null, + .pc_decode = nlm_svc_decode_nlm_res, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = sizeof(struct nlm_res), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "UNLOCK_RES", }, - [NLMPROC_GRANTED_RES] = { - .pc_func = nlmsvc_proc_granted_res, - .pc_decode = nlmsvc_decode_res, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_res), - .pc_argzero = sizeof(struct nlm_res), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "GRANTED_RES", + [NLM_GRANTED_RES] = { + .pc_func = nlmsvc_proc_granted_res, + .pc_decode = nlm_svc_decode_nlm_res, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = sizeof(struct nlm_res_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "GRANTED_RES", }, - [NLMPROC_NSM_NOTIFY] = { - .pc_func = nlmsvc_proc_sm_notify, - .pc_decode = nlmsvc_decode_reboot, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_reboot), - .pc_argzero = sizeof(struct nlm_reboot), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "SM_NOTIFY", + [NLM_SM_NOTIFY] = { + .pc_func = nlmsvc_proc_sm_notify, + .pc_decode = nlm_svc_decode_nlm_notifyargs, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = sizeof(struct nlm_notifyargs_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "SM_NOTIFY", }, [17] = { - .pc_func = nlmsvc_proc_unused, - .pc_decode = nlmsvc_decode_void, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_void), - .pc_argzero = sizeof(struct nlm_void), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "UNUSED", + .pc_func = nlmsvc_proc_unused, + .pc_decode = nlm_svc_decode_void, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = 0, + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "UNUSED", }, [18] = { - .pc_func = nlmsvc_proc_unused, - .pc_decode = nlmsvc_decode_void, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_void), - .pc_argzero = sizeof(struct nlm_void), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "UNUSED", + .pc_func = nlmsvc_proc_unused, + .pc_decode = nlm_svc_decode_void, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = 0, + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "UNUSED", }, [19] = { - .pc_func = nlmsvc_proc_unused, - .pc_decode = nlmsvc_decode_void, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_void), - .pc_argzero = sizeof(struct nlm_void), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "UNUSED", + .pc_func = nlmsvc_proc_unused, + .pc_decode = nlm_svc_decode_void, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = 0, + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "UNUSED", }, - [NLMPROC_SHARE] = { - .pc_func = nlmsvc_proc_share, - .pc_decode = nlmsvc_decode_shareargs, - .pc_encode = nlmsvc_encode_shareres, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St+1, - .pc_name = "SHARE", + [NLM_SHARE] = { + .pc_func = nlmsvc_proc_share, + .pc_decode = nlm_svc_decode_nlm_shareargs, + .pc_encode = nlm_svc_encode_nlm_shareres, + .pc_argsize = sizeof(struct nlm_shareargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm_shareres_wrapper), + .pc_xdrressize = NLM3_nlm_shareres_sz, + .pc_name = "SHARE", }, - [NLMPROC_UNSHARE] = { - .pc_func = nlmsvc_proc_unshare, - .pc_decode = nlmsvc_decode_shareargs, - .pc_encode = nlmsvc_encode_shareres, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St+1, - .pc_name = "UNSHARE", + [NLM_UNSHARE] = { + .pc_func = nlmsvc_proc_unshare, + .pc_decode = nlm_svc_decode_nlm_shareargs, + .pc_encode = nlm_svc_encode_nlm_shareres, + .pc_argsize = sizeof(struct nlm_shareargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm_shareres_wrapper), + .pc_xdrressize = NLM3_nlm_shareres_sz, + .pc_name = "UNSHARE", }, - [NLMPROC_NM_LOCK] = { - .pc_func = nlmsvc_proc_nm_lock, - .pc_decode = nlmsvc_decode_lockargs, - .pc_encode = nlmsvc_encode_res, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St, - .pc_name = "NM_LOCK", + [NLM_NM_LOCK] = { + .pc_func = nlmsvc_proc_nm_lock, + .pc_decode = nlm_svc_decode_nlm_lockargs, + .pc_encode = nlm_svc_encode_nlm_res, + .pc_argsize = sizeof(struct nlm_lockargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm_res_wrapper), + .pc_xdrressize = NLM3_nlm_res_sz, + .pc_name = "NM_LOCK", }, - [NLMPROC_FREE_ALL] = { - .pc_func = nlmsvc_proc_free_all, - .pc_decode = nlmsvc_decode_notify, - .pc_encode = nlmsvc_encode_void, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = 0, - .pc_name = "FREE_ALL", + [NLM_FREE_ALL] = { + .pc_func = nlmsvc_proc_free_all, + .pc_decode = nlm_svc_decode_nlm_notify, + .pc_encode = nlm_svc_encode_void, + .pc_argsize = sizeof(struct nlm_notify_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "FREE_ALL", }, }; @@ -814,9 +1417,16 @@ static const struct svc_procedure nlmsvc_procedures[24] = { * Storage requirements for XDR arguments and results */ union nlmsvc_xdrstore { - struct nlm_args args; - struct nlm_res res; - struct nlm_reboot reboot; + struct nlm_testargs_wrapper testargs; + struct nlm_lockargs_wrapper lockargs; + struct nlm_cancargs_wrapper cancargs; + struct nlm_unlockargs_wrapper unlockargs; + struct nlm_notifyargs_wrapper notifyargs; + struct nlm_shareargs_wrapper shareargs; + struct nlm_notify_wrapper notify; + struct nlm_testres_wrapper testres; + struct nlm_res_wrapper res; + struct nlm_shareres_wrapper shareres; }; /* diff --git a/fs/lockd/svcshare.c b/fs/lockd/svcshare.c index 53f5655c128c..5ac0ec25d62d 100644 --- a/fs/lockd/svcshare.c +++ b/fs/lockd/svcshare.c @@ -19,7 +19,7 @@ #include "share.h" static inline int -nlm_cmp_owner(struct nlm_share *share, struct xdr_netobj *oh) +nlm_cmp_owner(struct lockd_share *share, struct xdr_netobj *oh) { return share->s_owner.len == oh->len && !memcmp(share->s_owner.data, oh->data, oh->len); @@ -39,7 +39,7 @@ __be32 nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file, struct xdr_netobj *oh, u32 access, u32 mode) { - struct nlm_share *share; + struct lockd_share *share; u8 *ohdata; if (nlmsvc_file_cannot_lock(file)) @@ -85,7 +85,7 @@ __be32 nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file, struct xdr_netobj *oh) { - struct nlm_share *share, **shpp; + struct lockd_share *share, **shpp; if (nlmsvc_file_cannot_lock(file)) return nlm_lck_denied_nolocks; @@ -111,7 +111,7 @@ nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file, void nlmsvc_traverse_shares(struct nlm_host *host, struct nlm_file *file, nlm_host_match_fn_t match) { - struct nlm_share *share, **shpp; + struct lockd_share *share, **shpp; shpp = &file->f_shares; while ((share = *shpp) != NULL) { diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 9da9d6e0b42e..a0d1a6fbf61e 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -17,7 +17,6 @@ #include <linux/sunrpc/addr.h> #include <linux/module.h> #include <linux/mount.h> -#include <uapi/linux/nfs2.h> #include "lockd.h" #include "share.h" @@ -67,7 +66,7 @@ static inline unsigned int file_hash(struct nfs_fh *f) { unsigned int tmp=0; int i; - for (i=0; i<NFS2_FHSIZE;i++) + for (i = 0; i < LOCKD_FH_HASH_SIZE; i++) tmp += f->data[i]; return tmp & (FILE_NRHASH - 1); } @@ -132,7 +131,7 @@ static __be32 nlm_do_fopen(struct svc_rqst *rqstp, */ __be32 nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, - struct nlm_lock *lock, int mode) + struct lockd_lock *lock, int mode) { struct nlm_file *file; unsigned int hash; @@ -150,6 +149,8 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, mutex_lock(&file->f_mutex); nfserr = nlm_do_fopen(rqstp, file, mode); mutex_unlock(&file->f_mutex); + if (nfserr) + goto out_unlock; goto found; } nlm_debug_print_fh("creating file for", &lock->fh); @@ -166,7 +167,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, nfserr = nlm_do_fopen(rqstp, file, mode); if (nfserr) - goto out_unlock; + goto out_free; hlist_add_head(&file->f_list, &nlm_files[hash]); diff --git a/fs/lockd/svcxdr.h b/fs/lockd/svcxdr.h deleted file mode 100644 index 4f1a451da5ba..000000000000 --- a/fs/lockd/svcxdr.h +++ /dev/null @@ -1,142 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Encode/decode NLM basic data types - * - * Basic NLMv3 XDR data types are not defined in an IETF standards - * document. X/Open has a description of these data types that - * is useful. See Chapter 10 of "Protocols for Interworking: - * XNFS, Version 3W". - * - * Basic NLMv4 XDR data types are defined in Appendix II.1.4 of - * RFC 1813: "NFS Version 3 Protocol Specification". - * - * Author: Chuck Lever <chuck.lever@oracle.com> - * - * Copyright (c) 2020, Oracle and/or its affiliates. - */ - -#ifndef _LOCKD_SVCXDR_H_ -#define _LOCKD_SVCXDR_H_ - -static inline bool -svcxdr_decode_stats(struct xdr_stream *xdr, __be32 *status) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, XDR_UNIT); - if (!p) - return false; - *status = *p; - - return true; -} - -static inline bool -svcxdr_encode_stats(struct xdr_stream *xdr, __be32 status) -{ - __be32 *p; - - p = xdr_reserve_space(xdr, XDR_UNIT); - if (!p) - return false; - *p = status; - - return true; -} - -static inline bool -svcxdr_decode_string(struct xdr_stream *xdr, char **data, unsigned int *data_len) -{ - __be32 *p; - u32 len; - - if (xdr_stream_decode_u32(xdr, &len) < 0) - return false; - if (len > NLM_MAXSTRLEN) - return false; - p = xdr_inline_decode(xdr, len); - if (!p) - return false; - *data_len = len; - *data = (char *)p; - - return true; -} - -/* - * NLM cookies are defined by specification to be a variable-length - * XDR opaque no longer than 1024 bytes. However, this implementation - * limits their length to 32 bytes, and treats zero-length cookies - * specially. - */ -static inline bool -svcxdr_decode_cookie(struct xdr_stream *xdr, struct nlm_cookie *cookie) -{ - __be32 *p; - u32 len; - - if (xdr_stream_decode_u32(xdr, &len) < 0) - return false; - if (len > NLM_MAXCOOKIELEN) - return false; - if (!len) - goto out_hpux; - - p = xdr_inline_decode(xdr, len); - if (!p) - return false; - cookie->len = len; - memcpy(cookie->data, p, len); - - return true; - - /* apparently HPUX can return empty cookies */ -out_hpux: - cookie->len = 4; - memset(cookie->data, 0, 4); - return true; -} - -static inline bool -svcxdr_encode_cookie(struct xdr_stream *xdr, const struct nlm_cookie *cookie) -{ - __be32 *p; - - if (xdr_stream_encode_u32(xdr, cookie->len) < 0) - return false; - p = xdr_reserve_space(xdr, cookie->len); - if (!p) - return false; - memcpy(p, cookie->data, cookie->len); - - return true; -} - -static inline bool -svcxdr_decode_owner(struct xdr_stream *xdr, struct xdr_netobj *obj) -{ - __be32 *p; - u32 len; - - if (xdr_stream_decode_u32(xdr, &len) < 0) - return false; - if (len > XDR_MAX_NETOBJ) - return false; - p = xdr_inline_decode(xdr, len); - if (!p) - return false; - obj->len = len; - obj->data = (u8 *)p; - - return true; -} - -static inline bool -svcxdr_encode_owner(struct xdr_stream *xdr, const struct xdr_netobj *obj) -{ - if (obj->len > XDR_MAX_NETOBJ) - return false; - return xdr_stream_encode_opaque(xdr, obj->data, obj->len) > 0; -} - -#endif /* _LOCKD_SVCXDR_H_ */ diff --git a/fs/lockd/trace.h b/fs/lockd/trace.h index 7214d7e96a42..a11d04e8c835 100644 --- a/fs/lockd/trace.h +++ b/fs/lockd/trace.h @@ -48,7 +48,7 @@ NLM_STATUS_LIST DECLARE_EVENT_CLASS(nlmclnt_lock_event, TP_PROTO( - const struct nlm_lock *lock, + const struct lockd_lock *lock, const struct sockaddr *addr, unsigned int addrlen, __be32 status @@ -61,8 +61,8 @@ DECLARE_EVENT_CLASS(nlmclnt_lock_event, __field(u32, svid) __field(u32, fh) __field(unsigned long, status) - __field(u64, start) - __field(u64, len) + __field(loff_t, start) + __field(loff_t, end) __sockaddr(addr, addrlen) ), @@ -70,16 +70,16 @@ DECLARE_EVENT_CLASS(nlmclnt_lock_event, __entry->oh = ~crc32_le(0xffffffff, lock->oh.data, lock->oh.len); __entry->svid = lock->svid; __entry->fh = nfs_fhandle_hash(&lock->fh); - __entry->start = lock->lock_start; - __entry->len = lock->lock_len; + __entry->start = lock->fl.fl_start; + __entry->end = lock->fl.fl_end; __entry->status = be32_to_cpu(status); __assign_sockaddr(addr, addr, addrlen); ), TP_printk( - "addr=%pISpc oh=0x%08x svid=0x%08x fh=0x%08x start=%llu len=%llu status=%s", + "addr=%pISpc oh=0x%08x svid=0x%08x fh=0x%08x start=%lld end=%lld status=%s", __get_sockaddr(addr), __entry->oh, __entry->svid, - __entry->fh, __entry->start, __entry->len, + __entry->fh, __entry->start, __entry->end, show_nlm_status(__entry->status) ) ); @@ -87,7 +87,7 @@ DECLARE_EVENT_CLASS(nlmclnt_lock_event, #define DEFINE_NLMCLNT_EVENT(name) \ DEFINE_EVENT(nlmclnt_lock_event, name, \ TP_PROTO( \ - const struct nlm_lock *lock, \ + const struct lockd_lock *lock, \ const struct sockaddr *addr, \ unsigned int addrlen, \ __be32 status \ diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c deleted file mode 100644 index dfca8b8dab73..000000000000 --- a/fs/lockd/xdr.c +++ /dev/null @@ -1,354 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * linux/fs/lockd/xdr.c - * - * XDR support for lockd and the lock client. - * - * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> - */ - -#include <linux/types.h> -#include <linux/sched.h> -#include <linux/nfs.h> - -#include <linux/sunrpc/xdr.h> -#include <linux/sunrpc/clnt.h> -#include <linux/sunrpc/svc.h> -#include <linux/sunrpc/stats.h> - -#include <uapi/linux/nfs2.h> - -#include "lockd.h" -#include "share.h" -#include "svcxdr.h" - -static inline loff_t -s32_to_loff_t(__s32 offset) -{ - return (loff_t)offset; -} - -static inline __s32 -loff_t_to_s32(loff_t offset) -{ - __s32 res; - if (offset >= NLM_OFFSET_MAX) - res = NLM_OFFSET_MAX; - else if (offset <= -NLM_OFFSET_MAX) - res = -NLM_OFFSET_MAX; - else - res = offset; - return res; -} - -/* - * NLM file handles are defined by specification to be a variable-length - * XDR opaque no longer than 1024 bytes. However, this implementation - * constrains their length to exactly the length of an NFSv2 file - * handle. - */ -static bool -svcxdr_decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh) -{ - __be32 *p; - u32 len; - - if (xdr_stream_decode_u32(xdr, &len) < 0) - return false; - if (len != NFS2_FHSIZE) - return false; - - p = xdr_inline_decode(xdr, len); - if (!p) - return false; - fh->size = NFS2_FHSIZE; - memcpy(fh->data, p, len); - memset(fh->data + NFS2_FHSIZE, 0, sizeof(fh->data) - NFS2_FHSIZE); - - return true; -} - -static bool -svcxdr_decode_lock(struct xdr_stream *xdr, struct nlm_lock *lock) -{ - struct file_lock *fl = &lock->fl; - s32 start, len, end; - - if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) - return false; - if (!svcxdr_decode_fhandle(xdr, &lock->fh)) - return false; - if (!svcxdr_decode_owner(xdr, &lock->oh)) - return false; - if (xdr_stream_decode_u32(xdr, &lock->svid) < 0) - return false; - if (xdr_stream_decode_u32(xdr, &start) < 0) - return false; - if (xdr_stream_decode_u32(xdr, &len) < 0) - return false; - - locks_init_lock(fl); - fl->c.flc_flags = FL_POSIX; - fl->c.flc_type = F_RDLCK; - end = start + len - 1; - fl->fl_start = s32_to_loff_t(start); - if (len == 0 || end < 0) - fl->fl_end = OFFSET_MAX; - else - fl->fl_end = s32_to_loff_t(end); - - return true; -} - -static bool -svcxdr_encode_holder(struct xdr_stream *xdr, const struct nlm_lock *lock) -{ - const struct file_lock *fl = &lock->fl; - s32 start, len; - - /* exclusive */ - if (xdr_stream_encode_bool(xdr, fl->c.flc_type != F_RDLCK) < 0) - return false; - if (xdr_stream_encode_u32(xdr, lock->svid) < 0) - return false; - if (!svcxdr_encode_owner(xdr, &lock->oh)) - return false; - start = loff_t_to_s32(fl->fl_start); - if (fl->fl_end == OFFSET_MAX) - len = 0; - else - len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1); - if (xdr_stream_encode_u32(xdr, start) < 0) - return false; - if (xdr_stream_encode_u32(xdr, len) < 0) - return false; - - return true; -} - -static bool -svcxdr_encode_testrply(struct xdr_stream *xdr, const struct nlm_res *resp) -{ - if (!svcxdr_encode_stats(xdr, resp->status)) - return false; - switch (resp->status) { - case nlm_lck_denied: - if (!svcxdr_encode_holder(xdr, &resp->lock)) - return false; - } - - return true; -} - - -/* - * Decode Call arguments - */ - -bool -nlmsvc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - return true; -} - -bool -nlmsvc_decode_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_args *argp = rqstp->rq_argp; - u32 exclusive; - - if (!svcxdr_decode_cookie(xdr, &argp->cookie)) - return false; - if (xdr_stream_decode_bool(xdr, &exclusive) < 0) - return false; - if (!svcxdr_decode_lock(xdr, &argp->lock)) - return false; - if (exclusive) - argp->lock.fl.c.flc_type = F_WRLCK; - - return true; -} - -bool -nlmsvc_decode_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_args *argp = rqstp->rq_argp; - u32 exclusive; - - if (!svcxdr_decode_cookie(xdr, &argp->cookie)) - return false; - if (xdr_stream_decode_bool(xdr, &argp->block) < 0) - return false; - if (xdr_stream_decode_bool(xdr, &exclusive) < 0) - return false; - if (!svcxdr_decode_lock(xdr, &argp->lock)) - return false; - if (exclusive) - argp->lock.fl.c.flc_type = F_WRLCK; - if (xdr_stream_decode_bool(xdr, &argp->reclaim) < 0) - return false; - if (xdr_stream_decode_u32(xdr, &argp->state) < 0) - return false; - argp->monitor = 1; /* monitor client by default */ - - return true; -} - -bool -nlmsvc_decode_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_args *argp = rqstp->rq_argp; - u32 exclusive; - - if (!svcxdr_decode_cookie(xdr, &argp->cookie)) - return false; - if (xdr_stream_decode_bool(xdr, &argp->block) < 0) - return false; - if (xdr_stream_decode_bool(xdr, &exclusive) < 0) - return false; - if (!svcxdr_decode_lock(xdr, &argp->lock)) - return false; - if (exclusive) - argp->lock.fl.c.flc_type = F_WRLCK; - - return true; -} - -bool -nlmsvc_decode_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_args *argp = rqstp->rq_argp; - - if (!svcxdr_decode_cookie(xdr, &argp->cookie)) - return false; - if (!svcxdr_decode_lock(xdr, &argp->lock)) - return false; - argp->lock.fl.c.flc_type = F_UNLCK; - - return true; -} - -bool -nlmsvc_decode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_res *resp = rqstp->rq_argp; - - if (!svcxdr_decode_cookie(xdr, &resp->cookie)) - return false; - if (!svcxdr_decode_stats(xdr, &resp->status)) - return false; - - return true; -} - -bool -nlmsvc_decode_reboot(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_reboot *argp = rqstp->rq_argp; - __be32 *p; - u32 len; - - if (xdr_stream_decode_u32(xdr, &len) < 0) - return false; - if (len > SM_MAXSTRLEN) - return false; - p = xdr_inline_decode(xdr, len); - if (!p) - return false; - argp->len = len; - argp->mon = (char *)p; - if (xdr_stream_decode_u32(xdr, &argp->state) < 0) - return false; - p = xdr_inline_decode(xdr, SM_PRIV_SIZE); - if (!p) - return false; - memcpy(&argp->priv.data, p, sizeof(argp->priv.data)); - - return true; -} - -bool -nlmsvc_decode_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_lock *lock = &argp->lock; - - memset(lock, 0, sizeof(*lock)); - locks_init_lock(&lock->fl); - lock->svid = LOCKD_SHARE_SVID; - - if (!svcxdr_decode_cookie(xdr, &argp->cookie)) - return false; - if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) - return false; - if (!svcxdr_decode_fhandle(xdr, &lock->fh)) - return false; - if (!svcxdr_decode_owner(xdr, &lock->oh)) - return false; - /* XXX: Range checks are missing in the original code */ - if (xdr_stream_decode_u32(xdr, &argp->fsm_mode) < 0) - return false; - if (xdr_stream_decode_u32(xdr, &argp->fsm_access) < 0) - return false; - - return true; -} - -bool -nlmsvc_decode_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_lock *lock = &argp->lock; - - if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) - return false; - if (xdr_stream_decode_u32(xdr, &argp->state) < 0) - return false; - - return true; -} - - -/* - * Encode Reply results - */ - -bool -nlmsvc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - return true; -} - -bool -nlmsvc_encode_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_res *resp = rqstp->rq_resp; - - return svcxdr_encode_cookie(xdr, &resp->cookie) && - svcxdr_encode_testrply(xdr, resp); -} - -bool -nlmsvc_encode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_res *resp = rqstp->rq_resp; - - return svcxdr_encode_cookie(xdr, &resp->cookie) && - svcxdr_encode_stats(xdr, resp->status); -} - -bool -nlmsvc_encode_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_res *resp = rqstp->rq_resp; - - if (!svcxdr_encode_cookie(xdr, &resp->cookie)) - return false; - if (!svcxdr_encode_stats(xdr, resp->status)) - return false; - /* sequence */ - if (xdr_stream_encode_u32(xdr, 0) < 0) - return false; - - return true; -} diff --git a/fs/lockd/xdr.h b/fs/lockd/xdr.h index 3c60817c4349..a1126cca98c6 100644 --- a/fs/lockd/xdr.h +++ b/fs/lockd/xdr.h @@ -20,8 +20,6 @@ struct nsm_private { unsigned char data[SM_PRIV_SIZE]; }; -struct svc_rqst; - #define NLM_MAXCOOKIELEN 32 #define NLM_MAXSTRLEN 1024 @@ -32,7 +30,7 @@ struct svc_rqst; #define nlm_lck_denied_grace_period cpu_to_be32(NLM_LCK_DENIED_GRACE_PERIOD) /* Lock info passed via NLM */ -struct nlm_lock { +struct lockd_lock { char * caller; unsigned int len; /* length of "caller" */ struct nfs_fh fh; @@ -49,8 +47,7 @@ struct nlm_lock { * 32 bytes. */ -struct nlm_cookie -{ +struct lockd_cookie { unsigned char data[NLM_MAXCOOKIELEN]; unsigned int len; }; @@ -58,49 +55,31 @@ struct nlm_cookie /* * Generic lockd arguments for all but sm_notify */ -struct nlm_args { - struct nlm_cookie cookie; - struct nlm_lock lock; +struct lockd_args { + struct lockd_cookie cookie; + struct lockd_lock lock; u32 block; u32 reclaim; u32 state; - u32 monitor; - u32 fsm_access; - u32 fsm_mode; }; /* * Generic lockd result */ -struct nlm_res { - struct nlm_cookie cookie; +struct lockd_res { + struct lockd_cookie cookie; __be32 status; - struct nlm_lock lock; + struct lockd_lock lock; }; /* * statd callback when client has rebooted */ -struct nlm_reboot { +struct lockd_reboot { char *mon; unsigned int len; u32 state; struct nsm_private priv; }; -bool nlmsvc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlmsvc_decode_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlmsvc_decode_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlmsvc_decode_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlmsvc_decode_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlmsvc_decode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlmsvc_decode_reboot(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlmsvc_decode_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlmsvc_decode_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr); - -bool nlmsvc_encode_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlmsvc_encode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlmsvc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlmsvc_encode_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr); - #endif /* _LOCKD_XDR_H */ diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c index 2ed2126201f4..acd3511c04a6 100644 --- a/fs/nfs/dns_resolve.c +++ b/fs/nfs/dns_resolve.c @@ -156,7 +156,7 @@ static int nfs_dns_upcall(struct cache_detail *cd, if (!nfs_cache_upcall(cd, key->hostname)) return 0; clear_bit(CACHE_PENDING, &ch->flags); - return sunrpc_cache_pipe_upcall_timeout(cd, ch); + return sunrpc_cache_upcall_warn(cd, ch); } static int nfs_dns_match(struct cache_head *ca, diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 35fef3197a66..a47c90f40422 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -17,6 +17,8 @@ #include <linux/module.h> #include <linux/exportfs.h> #include <linux/sunrpc/svc_xprt.h> +#include <net/genetlink.h> +#include <uapi/linux/nfsd_netlink.h> #include "nfsd.h" #include "nfsfh.h" @@ -24,6 +26,7 @@ #include "pnfs.h" #include "filecache.h" #include "trace.h" +#include "netlink.h" #define NFSDDBG_FACILITY NFSDDBG_EXPORT @@ -36,35 +39,24 @@ * second map contains a reference to the entry in the first map. */ -static struct workqueue_struct *nfsd_export_wq; - #define EXPKEY_HASHBITS 8 #define EXPKEY_HASHMAX (1 << EXPKEY_HASHBITS) #define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1) -static void expkey_release(struct work_struct *work) +static void expkey_put(struct kref *ref) { - struct svc_expkey *key = container_of(to_rcu_work(work), - struct svc_expkey, ek_rwork); + struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); if (test_bit(CACHE_VALID, &key->h.flags) && !test_bit(CACHE_NEGATIVE, &key->h.flags)) path_put(&key->ek_path); auth_domain_put(key->ek_client); - kfree(key); -} - -static void expkey_put(struct kref *ref) -{ - struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); - - INIT_RCU_WORK(&key->ek_rwork, expkey_release); - queue_rcu_work(nfsd_export_wq, &key->ek_rwork); + kfree_rcu(key, ek_rcu); } static int expkey_upcall(struct cache_detail *cd, struct cache_head *h) { - return sunrpc_cache_pipe_upcall(cd, h); + return sunrpc_cache_upcall(cd, h); } static void expkey_request(struct cache_detail *cd, @@ -263,12 +255,18 @@ static void expkey_flush(void) mutex_unlock(&nfsd_mutex); } +static int expkey_notify(struct cache_detail *cd, struct cache_head *h) +{ + return nfsd_cache_notify(cd, h, NFSD_CACHE_TYPE_EXPKEY); +} + static const struct cache_detail svc_expkey_cache_template = { .owner = THIS_MODULE, .hash_size = EXPKEY_HASHMAX, .name = "nfsd.fh", .cache_put = expkey_put, .cache_upcall = expkey_upcall, + .cache_notify = expkey_notify, .cache_request = expkey_request, .cache_parse = expkey_parse, .cache_show = expkey_show, @@ -319,6 +317,266 @@ svc_expkey_update(struct cache_detail *cd, struct svc_expkey *new, return NULL; } +/** + * nfsd_nl_expkey_get_reqs_dumpit - dump pending expkey requests + * @skb: reply buffer + * @cb: netlink metadata and command arguments + * + * Walk the expkey cache's pending request list and create a netlink + * message with a nested entry for each cache_request, containing the + * seqno, client string, fsidtype and fsid. + * + * 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 nfsd_nl_expkey_get_reqs_dumpit(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct nfsd_net *nn; + struct cache_detail *cd; + struct cache_head **items; + u64 *seqnos; + int cnt, i, emitted; + void *hdr; + int ret; + + nn = net_generic(sock_net(skb->sk), nfsd_net_id); + + mutex_lock(&nfsd_mutex); + + cd = nn->svc_expkey_cache; + if (!cd) { + ret = -ENODEV; + goto out_unlock; + } + + cnt = sunrpc_cache_requests_count(cd); + if (!cnt) { + ret = 0; + goto out_unlock; + } + + 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, &nfsd_nl_family, + NLM_F_MULTI, NFSD_CMD_EXPKEY_GET_REQS); + if (!hdr) { + ret = -ENOBUFS; + goto out_put; + } + + emitted = 0; + for (i = 0; i < cnt; i++) { + struct svc_expkey *ek; + struct nlattr *nest; + + ek = container_of(items[i], struct svc_expkey, h); + + nest = nla_nest_start(skb, NFSD_A_EXPKEY_REQS_REQUESTS); + if (!nest) + break; + + if (nla_put_u64_64bit(skb, NFSD_A_EXPKEY_SEQNO, + seqnos[i], 0) || + nla_put_string(skb, NFSD_A_EXPKEY_CLIENT, + ek->ek_client->name) || + nla_put_u8(skb, NFSD_A_EXPKEY_FSIDTYPE, + ek->ek_fsidtype) || + nla_put(skb, NFSD_A_EXPKEY_FSID, + key_len(ek->ek_fsidtype), ek->ek_fsid)) { + 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); +out_unlock: + mutex_unlock(&nfsd_mutex); + return ret; +} + +/** + * nfsd_nl_parse_one_expkey - parse one expkey entry from netlink + * @cd: cache_detail for the expkey cache + * @attr: nested attribute containing expkey fields + * + * Parses one expkey entry from a netlink message and updates the + * cache. Mirrors the logic in expkey_parse(). + * + * Returns 0 on success or a negative errno. + */ +static int nfsd_nl_parse_one_expkey(struct cache_detail *cd, + struct nlattr *attr) +{ + struct nlattr *tb[NFSD_A_EXPKEY_PATH + 1]; + struct auth_domain *dom = NULL; + struct svc_expkey key; + struct svc_expkey *ek = NULL; + struct timespec64 boot; + int err; + u8 fsidtype; + int fsid_len; + + err = nla_parse_nested(tb, NFSD_A_EXPKEY_PATH, attr, + nfsd_expkey_nl_policy, NULL); + if (err) + return err; + + /* client (required) */ + if (!tb[NFSD_A_EXPKEY_CLIENT]) + return -EINVAL; + + dom = auth_domain_find(nla_data(tb[NFSD_A_EXPKEY_CLIENT])); + if (!dom) + return -ENOENT; + + /* fsidtype (required) */ + if (!tb[NFSD_A_EXPKEY_FSIDTYPE]) { + err = -EINVAL; + goto out_dom; + } + fsidtype = nla_get_u8(tb[NFSD_A_EXPKEY_FSIDTYPE]); + if (key_len(fsidtype) == 0) { + err = -EINVAL; + goto out_dom; + } + + /* fsid (required) */ + if (!tb[NFSD_A_EXPKEY_FSID]) { + err = -EINVAL; + goto out_dom; + } + fsid_len = nla_len(tb[NFSD_A_EXPKEY_FSID]); + if (fsid_len != key_len(fsidtype)) { + err = -EINVAL; + goto out_dom; + } + + /* expiry (required, wallclock seconds) */ + if (!tb[NFSD_A_EXPKEY_EXPIRY]) { + err = -EINVAL; + goto out_dom; + } + + key.h.flags = 0; + getboottime64(&boot); + key.h.expiry_time = nla_get_u64(tb[NFSD_A_EXPKEY_EXPIRY]) - + boot.tv_sec; + key.ek_client = dom; + key.ek_fsidtype = fsidtype; + memcpy(key.ek_fsid, nla_data(tb[NFSD_A_EXPKEY_FSID]), fsid_len); + + ek = svc_expkey_lookup(cd, &key); + if (!ek) { + err = -ENOMEM; + goto out_dom; + } + + if (tb[NFSD_A_EXPKEY_NEGATIVE]) { + set_bit(CACHE_NEGATIVE, &key.h.flags); + ek = svc_expkey_update(cd, &key, ek); + if (ek) + trace_nfsd_expkey_update(ek, NULL); + else + err = -ENOMEM; + } else if (tb[NFSD_A_EXPKEY_PATH]) { + err = kern_path(nla_data(tb[NFSD_A_EXPKEY_PATH]), 0, + &key.ek_path); + if (err) + goto out_ek; + ek = svc_expkey_update(cd, &key, ek); + if (ek) + trace_nfsd_expkey_update(ek, + nla_data(tb[NFSD_A_EXPKEY_PATH])); + else + err = -ENOMEM; + path_put(&key.ek_path); + } else { + err = -EINVAL; + goto out_ek; + } + + cache_flush(); + +out_ek: + if (ek) + cache_put(&ek->h, cd); +out_dom: + auth_domain_put(dom); + return err; +} + +/** + * nfsd_nl_expkey_set_reqs_doit - respond to expkey requests + * @skb: reply buffer + * @info: netlink metadata and command arguments + * + * Parse one or more expkey cache responses from userspace and + * update the expkey cache accordingly. + * + * Returns 0 on success or a negative errno. + */ +int nfsd_nl_expkey_set_reqs_doit(struct sk_buff *skb, + struct genl_info *info) +{ + struct nfsd_net *nn; + struct cache_detail *cd; + const struct nlattr *attr; + int rem, ret = 0; + + nn = net_generic(genl_info_net(info), nfsd_net_id); + + mutex_lock(&nfsd_mutex); + + cd = nn->svc_expkey_cache; + if (!cd) { + ret = -ENODEV; + goto out_unlock; + } + + nlmsg_for_each_attr_type(attr, NFSD_A_EXPKEY_REQS_REQUESTS, + info->nlhdr, GENL_HDRLEN, rem) { + ret = nfsd_nl_parse_one_expkey(cd, (struct nlattr *)attr); + if (ret) + break; + } + +out_unlock: + mutex_unlock(&nfsd_mutex); + return ret; +} #define EXPORT_HASHBITS 8 #define EXPORT_HASHMAX (1<< EXPORT_HASHBITS) @@ -364,13 +622,11 @@ static void export_stats_destroy(struct export_stats *stats) EXP_STATS_COUNTERS_NUM); } -static void svc_export_release(struct work_struct *work) +static void svc_export_release(struct rcu_head *rcu_head) { - struct svc_export *exp = container_of(to_rcu_work(work), - struct svc_export, ex_rwork); + struct svc_export *exp = container_of(rcu_head, struct svc_export, + ex_rcu); - path_put(&exp->ex_path); - auth_domain_put(exp->ex_client); nfsd4_fslocs_free(&exp->ex_fslocs); export_stats_destroy(exp->ex_stats); kfree(exp->ex_stats); @@ -382,13 +638,450 @@ static void svc_export_put(struct kref *ref) { struct svc_export *exp = container_of(ref, struct svc_export, h.ref); - INIT_RCU_WORK(&exp->ex_rwork, svc_export_release); - queue_rcu_work(nfsd_export_wq, &exp->ex_rwork); + path_put(&exp->ex_path); + auth_domain_put(exp->ex_client); + call_rcu(&exp->ex_rcu, svc_export_release); +} + +/** + * nfsd_nl_svc_export_get_reqs_dumpit - dump pending svc_export requests + * @skb: reply buffer + * @cb: netlink metadata and command arguments + * + * Walk the svc_export cache's pending request list and create a netlink + * message with a nested entry for each cache_request, containing the + * seqno, client string, and path. + * + * 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 nfsd_nl_svc_export_get_reqs_dumpit(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct nfsd_net *nn; + struct cache_detail *cd; + struct cache_head **items; + u64 *seqnos; + int cnt, i, emitted; + char *pathbuf; + void *hdr; + int ret; + + nn = net_generic(sock_net(skb->sk), nfsd_net_id); + + mutex_lock(&nfsd_mutex); + + cd = nn->svc_export_cache; + if (!cd) { + ret = -ENODEV; + goto out_unlock; + } + + cnt = sunrpc_cache_requests_count(cd); + if (!cnt) { + ret = 0; + goto out_unlock; + } + + items = kcalloc(cnt, sizeof(*items), GFP_KERNEL); + seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL); + pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); + if (!items || !seqnos || !pathbuf) { + 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, &nfsd_nl_family, + NLM_F_MULTI, NFSD_CMD_SVC_EXPORT_GET_REQS); + if (!hdr) { + ret = -ENOBUFS; + goto out_put; + } + + emitted = 0; + for (i = 0; i < cnt; i++) { + struct svc_export *exp; + struct nlattr *nest; + char *pth; + + exp = container_of(items[i], struct svc_export, h); + + pth = d_path(&exp->ex_path, pathbuf, PATH_MAX); + if (IS_ERR(pth)) + continue; + + nest = nla_nest_start(skb, + NFSD_A_SVC_EXPORT_REQS_REQUESTS); + if (!nest) + break; + + if (nla_put_u64_64bit(skb, NFSD_A_SVC_EXPORT_SEQNO, + seqnos[i], 0) || + nla_put_string(skb, NFSD_A_SVC_EXPORT_CLIENT, + exp->ex_client->name) || + nla_put_string(skb, NFSD_A_SVC_EXPORT_PATH, pth)) { + 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(pathbuf); + kfree(seqnos); + kfree(items); +out_unlock: + mutex_unlock(&nfsd_mutex); + return ret; +} + +/** + * nfsd_nl_parse_fslocations - parse fslocations from netlink + * @attr: NFSD_A_SVC_EXPORT_FSLOCATIONS nested attribute + * @fsloc: fslocations struct to fill in + * + * Returns 0 on success or a negative errno. + */ +static int nfsd_nl_parse_fslocations(struct nlattr *attr, + struct nfsd4_fs_locations *fsloc) +{ + struct nlattr *loc_attr; + int rem, count = 0; + int err; + + if (fsloc->locations) + return -EINVAL; + + /* Count locations first */ + nla_for_each_nested_type(loc_attr, NFSD_A_FSLOCATIONS_LOCATION, + attr, rem) + count++; + + if (count > MAX_FS_LOCATIONS) + return -EINVAL; + if (!count) + return 0; + + fsloc->locations = kcalloc(count, sizeof(struct nfsd4_fs_location), + GFP_KERNEL); + if (!fsloc->locations) + return -ENOMEM; + + nla_for_each_nested_type(loc_attr, NFSD_A_FSLOCATIONS_LOCATION, + attr, rem) { + struct nlattr *tb[NFSD_A_FSLOCATION_PATH + 1]; + struct nfsd4_fs_location *loc; + + err = nla_parse_nested(tb, NFSD_A_FSLOCATION_PATH, loc_attr, + nfsd_fslocation_nl_policy, NULL); + if (err) + goto out_free; + + if (!tb[NFSD_A_FSLOCATION_HOST] || + !tb[NFSD_A_FSLOCATION_PATH]) { + err = -EINVAL; + goto out_free; + } + + loc = &fsloc->locations[fsloc->locations_count++]; + loc->hosts = kstrdup(nla_data(tb[NFSD_A_FSLOCATION_HOST]), + GFP_KERNEL); + loc->path = kstrdup(nla_data(tb[NFSD_A_FSLOCATION_PATH]), + GFP_KERNEL); + if (!loc->hosts || !loc->path) { + err = -ENOMEM; + goto out_free; + } + } + + return 0; +out_free: + nfsd4_fslocs_free(fsloc); + return err; +} + +static struct svc_export *svc_export_update(struct svc_export *new, + struct svc_export *old); +static struct svc_export *svc_export_lookup(struct svc_export *); +static int check_export(const struct path *path, int *flags, + unsigned char *uuid); + +/** + * nfsd_nl_parse_one_export - parse one svc_export entry from a netlink message + * @cd: cache_detail for the svc_export cache + * @attr: nested attribute containing svc-export fields + * + * Parses one svc-export entry from a netlink message and updates the + * cache. Mirrors the logic in svc_export_parse(). + * + * Returns 0 on success or a negative errno. + */ +static int nfsd_nl_parse_one_export(struct cache_detail *cd, + struct nlattr *attr) +{ + struct nlattr *tb[NFSD_A_SVC_EXPORT_FSID + 1]; + struct auth_domain *dom = NULL; + struct svc_export exp = {}, *expp; + struct nlattr *secinfo_attr; + struct timespec64 boot; + int err, rem; + + err = nla_parse_nested(tb, NFSD_A_SVC_EXPORT_FSID, attr, + nfsd_svc_export_nl_policy, NULL); + if (err) + return err; + + /* client (required) */ + if (!tb[NFSD_A_SVC_EXPORT_CLIENT]) + return -EINVAL; + + dom = auth_domain_find(nla_data(tb[NFSD_A_SVC_EXPORT_CLIENT])); + if (!dom) + return -ENOENT; + + /* path (required) */ + if (!tb[NFSD_A_SVC_EXPORT_PATH]) { + err = -EINVAL; + goto out_dom; + } + + err = kern_path(nla_data(tb[NFSD_A_SVC_EXPORT_PATH]), 0, + &exp.ex_path); + if (err) + goto out_dom; + + exp.ex_client = dom; + exp.cd = cd; + exp.ex_devid_map = NULL; + exp.ex_xprtsec_modes = NFSEXP_XPRTSEC_ALL; + + /* expiry (required, wallclock seconds) */ + if (!tb[NFSD_A_SVC_EXPORT_EXPIRY]) { + err = -EINVAL; + goto out_path; + } + getboottime64(&boot); + exp.h.expiry_time = nla_get_u64(tb[NFSD_A_SVC_EXPORT_EXPIRY]) - + boot.tv_sec; + + if (tb[NFSD_A_SVC_EXPORT_NEGATIVE]) { + set_bit(CACHE_NEGATIVE, &exp.h.flags); + } else { + /* flags */ + if (tb[NFSD_A_SVC_EXPORT_FLAGS]) + exp.ex_flags = nla_get_u32(tb[NFSD_A_SVC_EXPORT_FLAGS]); + + /* anon uid */ + if (tb[NFSD_A_SVC_EXPORT_ANON_UID]) { + u32 uid = nla_get_u32(tb[NFSD_A_SVC_EXPORT_ANON_UID]); + + exp.ex_anon_uid = make_kuid(current_user_ns(), uid); + } + + /* anon gid */ + if (tb[NFSD_A_SVC_EXPORT_ANON_GID]) { + u32 gid = nla_get_u32(tb[NFSD_A_SVC_EXPORT_ANON_GID]); + + exp.ex_anon_gid = make_kgid(current_user_ns(), gid); + } + + /* fsid */ + if (tb[NFSD_A_SVC_EXPORT_FSID]) + exp.ex_fsid = nla_get_s32(tb[NFSD_A_SVC_EXPORT_FSID]); + + /* fslocations */ + if (tb[NFSD_A_SVC_EXPORT_FSLOCATIONS]) { + struct nlattr *fsl = tb[NFSD_A_SVC_EXPORT_FSLOCATIONS]; + + err = nfsd_nl_parse_fslocations(fsl, + &exp.ex_fslocs); + if (err) + goto out_path; + } + + /* uuid */ + if (tb[NFSD_A_SVC_EXPORT_UUID]) { + if (nla_len(tb[NFSD_A_SVC_EXPORT_UUID]) != + EX_UUID_LEN) { + err = -EINVAL; + goto out_fslocs; + } + exp.ex_uuid = kmemdup(nla_data(tb[NFSD_A_SVC_EXPORT_UUID]), + EX_UUID_LEN, GFP_KERNEL); + if (!exp.ex_uuid) { + err = -ENOMEM; + goto out_fslocs; + } + } + + /* secinfo (multi-attr) */ + nla_for_each_nested_type(secinfo_attr, + NFSD_A_SVC_EXPORT_SECINFO, + attr, rem) { + struct nlattr *ftb[NFSD_A_AUTH_FLAVOR_FLAGS + 1]; + struct exp_flavor_info *f; + + if (exp.ex_nflavors >= MAX_SECINFO_LIST) { + err = -EINVAL; + goto out_uuid; + } + + err = nla_parse_nested(ftb, + NFSD_A_AUTH_FLAVOR_FLAGS, + secinfo_attr, + nfsd_auth_flavor_nl_policy, + NULL); + if (err) + goto out_uuid; + + f = &exp.ex_flavors[exp.ex_nflavors++]; + + if (ftb[NFSD_A_AUTH_FLAVOR_PSEUDOFLAVOR]) + f->pseudoflavor = nla_get_u32(ftb[NFSD_A_AUTH_FLAVOR_PSEUDOFLAVOR]); + + if (ftb[NFSD_A_AUTH_FLAVOR_FLAGS]) + f->flags = nla_get_u32(ftb[NFSD_A_AUTH_FLAVOR_FLAGS]); + + /* Only some flags are allowed to differ between flavors: */ + if (~NFSEXP_SECINFO_FLAGS & (f->flags ^ exp.ex_flags)) { + err = -EINVAL; + goto out_uuid; + } + } + + /* xprtsec (multi-attr u32) */ + if (tb[NFSD_A_SVC_EXPORT_XPRTSEC]) { + struct nlattr *xp_attr; + + exp.ex_xprtsec_modes = 0; + nla_for_each_nested_type(xp_attr, + NFSD_A_SVC_EXPORT_XPRTSEC, + attr, rem) { + u32 mode = nla_get_u32(xp_attr); + + if (mode > NFSEXP_XPRTSEC_MTLS) { + err = -EINVAL; + goto out_uuid; + } + exp.ex_xprtsec_modes |= mode; + } + } + + err = check_export(&exp.ex_path, &exp.ex_flags, + exp.ex_uuid); + if (err) + goto out_uuid; + + if (exp.h.expiry_time < seconds_since_boot()) + goto out_uuid; + + err = -EINVAL; + if (!uid_valid(exp.ex_anon_uid)) + goto out_uuid; + if (!gid_valid(exp.ex_anon_gid)) + goto out_uuid; + err = 0; + + nfsd4_setup_layout_type(&exp); + } + + expp = svc_export_lookup(&exp); + if (!expp) { + err = -ENOMEM; + goto out_uuid; + } + expp = svc_export_update(&exp, expp); + if (expp) { + trace_nfsd_export_update(expp); + cache_flush(); + exp_put(expp); + } else { + err = -ENOMEM; + } + +out_uuid: + kfree(exp.ex_uuid); +out_fslocs: + nfsd4_fslocs_free(&exp.ex_fslocs); +out_path: + path_put(&exp.ex_path); +out_dom: + auth_domain_put(dom); + return err; +} + +/** + * nfsd_nl_svc_export_set_reqs_doit - respond to svc_export requests + * @skb: reply buffer + * @info: netlink metadata and command arguments + * + * Parse one or more svc_export cache responses from userspace and + * update the export cache accordingly. + * + * Returns 0 on success or a negative errno. + */ +int nfsd_nl_svc_export_set_reqs_doit(struct sk_buff *skb, + struct genl_info *info) +{ + struct nfsd_net *nn; + struct cache_detail *cd; + const struct nlattr *attr; + int rem, ret = 0; + + nn = net_generic(genl_info_net(info), nfsd_net_id); + + mutex_lock(&nfsd_mutex); + + cd = nn->svc_export_cache; + if (!cd) { + ret = -ENODEV; + goto out_unlock; + } + + nlmsg_for_each_attr_type(attr, NFSD_A_SVC_EXPORT_REQS_REQUESTS, + info->nlhdr, GENL_HDRLEN, rem) { + ret = nfsd_nl_parse_one_export(cd, (struct nlattr *)attr); + if (ret) + break; + } + +out_unlock: + mutex_unlock(&nfsd_mutex); + return ret; } static int svc_export_upcall(struct cache_detail *cd, struct cache_head *h) { - return sunrpc_cache_pipe_upcall(cd, h); + return sunrpc_cache_upcall(cd, h); +} + +static int svc_export_notify(struct cache_detail *cd, struct cache_head *h) +{ + return nfsd_cache_notify(cd, h, NFSD_CACHE_TYPE_SVC_EXPORT); } static void svc_export_request(struct cache_detail *cd, @@ -410,10 +1103,6 @@ static void svc_export_request(struct cache_detail *cd, (*bpp)[-1] = '\n'; } -static struct svc_export *svc_export_update(struct svc_export *new, - struct svc_export *old); -static struct svc_export *svc_export_lookup(struct svc_export *); - static int check_export(const struct path *path, int *flags, unsigned char *uuid) { struct inode *inode = d_inode(path->dentry); @@ -908,6 +1597,7 @@ static const struct cache_detail svc_export_cache_template = { .name = "nfsd.export", .cache_put = svc_export_put, .cache_upcall = svc_export_upcall, + .cache_notify = svc_export_notify, .cache_request = svc_export_request, .cache_parse = svc_export_parse, .cache_show = svc_export_show, @@ -1493,36 +2183,6 @@ const struct seq_operations nfs_exports_op = { .show = e_show, }; -/** - * nfsd_export_wq_init - allocate the export release workqueue - * - * Called once at module load. The workqueue runs deferred svc_export and - * svc_expkey release work scheduled by queue_rcu_work() in the cache put - * callbacks. - * - * Return values: - * %0: workqueue allocated - * %-ENOMEM: allocation failed - */ -int nfsd_export_wq_init(void) -{ - nfsd_export_wq = alloc_workqueue("nfsd_export", WQ_UNBOUND, 0); - if (!nfsd_export_wq) - return -ENOMEM; - return 0; -} - -/** - * nfsd_export_wq_shutdown - drain and free the export release workqueue - * - * Called once at module unload. Per-namespace teardown in - * nfsd_export_shutdown() has already drained all deferred work. - */ -void nfsd_export_wq_shutdown(void) -{ - destroy_workqueue(nfsd_export_wq); -} - /* * Initialize the exports module. */ @@ -1584,9 +2244,6 @@ nfsd_export_shutdown(struct net *net) cache_unregister_net(nn->svc_expkey_cache, net); cache_unregister_net(nn->svc_export_cache, net); - /* Drain deferred export and expkey release work. */ - rcu_barrier(); - flush_workqueue(nfsd_export_wq); cache_destroy_net(nn->svc_expkey_cache, net); cache_destroy_net(nn->svc_export_cache, net); svcauth_unix_purge(net); diff --git a/fs/nfsd/export.h b/fs/nfsd/export.h index b05399374574..d2b09cd76145 100644 --- a/fs/nfsd/export.h +++ b/fs/nfsd/export.h @@ -7,7 +7,6 @@ #include <linux/sunrpc/cache.h> #include <linux/percpu_counter.h> -#include <linux/workqueue.h> #include <uapi/linux/nfsd/export.h> #include <linux/nfs4.h> @@ -76,7 +75,7 @@ struct svc_export { u32 ex_layout_types; struct nfsd4_deviceid_map *ex_devid_map; struct cache_detail *cd; - struct rcu_work ex_rwork; + struct rcu_head ex_rcu; unsigned long ex_xprtsec_modes; struct export_stats *ex_stats; }; @@ -93,7 +92,7 @@ struct svc_expkey { u32 ek_fsid[6]; struct path ek_path; - struct rcu_work ek_rwork; + struct rcu_head ek_rcu; }; #define EX_ISSYNC(exp) (!((exp)->ex_flags & NFSEXP_ASYNC)) @@ -111,8 +110,6 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp, /* * Function declarations */ -int nfsd_export_wq_init(void); -void nfsd_export_wq_shutdown(void); int nfsd_export_init(struct net *); void nfsd_export_shutdown(struct net *); void nfsd_export_flush(struct net *); diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c index 1e2b38ed1d35..24511c3208db 100644 --- a/fs/nfsd/filecache.c +++ b/fs/nfsd/filecache.c @@ -724,6 +724,52 @@ nfsd_file_close_inode_sync(struct inode *inode) nfsd_file_dispose_list(&dispose); } +/** + * nfsd_file_close_export - close cached file handles for an export + * @net: net namespace in which to operate + * @path: export path whose cached files should be closed + * + * Close out GC-managed nfsd_file entries whose underlying file is on + * the same filesystem as, and a descendant of, @path. nfsd_file + * entries do not carry an export reference, so the check uses the + * file's dentry ancestry. False positives (closing a cached handle + * that did not originate from the target export) are harmless -- the + * handle is simply reopened on the next access. + * + * Called from the NFSD_CMD_UNLOCK_EXPORT handler before revoking + * NFSv4 state, to ensure that cached file handles do not hold the + * filesystem busy. + */ +void nfsd_file_close_export(struct net *net, const struct path *path) +{ + struct rhashtable_iter iter; + struct nfsd_file *nf; + LIST_HEAD(dispose); + + rhltable_walk_enter(&nfsd_file_rhltable, &iter); + do { + rhashtable_walk_start(&iter); + + nf = rhashtable_walk_next(&iter); + while (!IS_ERR_OR_NULL(nf)) { + if (nf->nf_net == net && + test_bit(NFSD_FILE_GC, &nf->nf_flags) && + nf->nf_file && + file_inode(nf->nf_file)->i_sb == + path->dentry->d_sb && + is_subdir(nf->nf_file->f_path.dentry, + path->dentry)) + nfsd_file_cond_queue(nf, &dispose); + nf = rhashtable_walk_next(&iter); + } + + rhashtable_walk_stop(&iter); + } while (nf == ERR_PTR(-EAGAIN)); + rhashtable_walk_exit(&iter); + + nfsd_file_dispose_list(&dispose); +} + static int nfsd_file_lease_notifier_call(struct notifier_block *nb, unsigned long arg, void *data) diff --git a/fs/nfsd/filecache.h b/fs/nfsd/filecache.h index b383dbc5b921..683b6437cacc 100644 --- a/fs/nfsd/filecache.h +++ b/fs/nfsd/filecache.h @@ -70,6 +70,7 @@ struct net *nfsd_file_put_local(struct nfsd_file __rcu **nf); struct nfsd_file *nfsd_file_get(struct nfsd_file *nf); struct file *nfsd_file_file(struct nfsd_file *nf); void nfsd_file_close_inode_sync(struct inode *inode); +void nfsd_file_close_export(struct net *net, const struct path *path); void nfsd_file_net_dispose(struct nfsd_net *nn); bool nfsd_file_is_cached(struct inode *inode); __be32 nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp, diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c index 81c943345d13..fbee3676d253 100644 --- a/fs/nfsd/netlink.c +++ b/fs/nfsd/netlink.c @@ -12,11 +12,51 @@ #include <uapi/linux/nfsd_netlink.h> /* Common nested types */ +const struct nla_policy nfsd_auth_flavor_nl_policy[NFSD_A_AUTH_FLAVOR_FLAGS + 1] = { + [NFSD_A_AUTH_FLAVOR_PSEUDOFLAVOR] = { .type = NLA_U32, }, + [NFSD_A_AUTH_FLAVOR_FLAGS] = NLA_POLICY_MASK(NLA_U32, 0x3ffff), +}; + +const struct nla_policy nfsd_expkey_nl_policy[NFSD_A_EXPKEY_PATH + 1] = { + [NFSD_A_EXPKEY_SEQNO] = { .type = NLA_U64, }, + [NFSD_A_EXPKEY_CLIENT] = { .type = NLA_NUL_STRING, }, + [NFSD_A_EXPKEY_FSIDTYPE] = { .type = NLA_U8, }, + [NFSD_A_EXPKEY_FSID] = { .type = NLA_BINARY, }, + [NFSD_A_EXPKEY_NEGATIVE] = { .type = NLA_FLAG, }, + [NFSD_A_EXPKEY_EXPIRY] = { .type = NLA_U64, }, + [NFSD_A_EXPKEY_PATH] = { .type = NLA_NUL_STRING, }, +}; + +const struct nla_policy nfsd_fslocation_nl_policy[NFSD_A_FSLOCATION_PATH + 1] = { + [NFSD_A_FSLOCATION_HOST] = { .type = NLA_NUL_STRING, }, + [NFSD_A_FSLOCATION_PATH] = { .type = NLA_NUL_STRING, }, +}; + +const struct nla_policy nfsd_fslocations_nl_policy[NFSD_A_FSLOCATIONS_LOCATION + 1] = { + [NFSD_A_FSLOCATIONS_LOCATION] = NLA_POLICY_NESTED(nfsd_fslocation_nl_policy), +}; + const struct nla_policy nfsd_sock_nl_policy[NFSD_A_SOCK_TRANSPORT_NAME + 1] = { [NFSD_A_SOCK_ADDR] = { .type = NLA_BINARY, }, [NFSD_A_SOCK_TRANSPORT_NAME] = { .type = NLA_NUL_STRING, }, }; +const struct nla_policy nfsd_svc_export_nl_policy[NFSD_A_SVC_EXPORT_FSID + 1] = { + [NFSD_A_SVC_EXPORT_SEQNO] = { .type = NLA_U64, }, + [NFSD_A_SVC_EXPORT_CLIENT] = { .type = NLA_NUL_STRING, }, + [NFSD_A_SVC_EXPORT_PATH] = { .type = NLA_NUL_STRING, }, + [NFSD_A_SVC_EXPORT_NEGATIVE] = { .type = NLA_FLAG, }, + [NFSD_A_SVC_EXPORT_EXPIRY] = { .type = NLA_U64, }, + [NFSD_A_SVC_EXPORT_ANON_UID] = { .type = NLA_U32, }, + [NFSD_A_SVC_EXPORT_ANON_GID] = { .type = NLA_U32, }, + [NFSD_A_SVC_EXPORT_FSLOCATIONS] = NLA_POLICY_NESTED(nfsd_fslocations_nl_policy), + [NFSD_A_SVC_EXPORT_UUID] = { .type = NLA_BINARY, }, + [NFSD_A_SVC_EXPORT_SECINFO] = NLA_POLICY_NESTED(nfsd_auth_flavor_nl_policy), + [NFSD_A_SVC_EXPORT_XPRTSEC] = NLA_POLICY_MASK(NLA_U32, 0x7), + [NFSD_A_SVC_EXPORT_FLAGS] = NLA_POLICY_MASK(NLA_U32, 0x3ffff), + [NFSD_A_SVC_EXPORT_FSID] = { .type = NLA_S32, }, +}; + const struct nla_policy nfsd_version_nl_policy[NFSD_A_VERSION_ENABLED + 1] = { [NFSD_A_VERSION_MAJOR] = { .type = NLA_U32, }, [NFSD_A_VERSION_MINOR] = { .type = NLA_U32, }, @@ -48,6 +88,36 @@ static const struct nla_policy nfsd_pool_mode_set_nl_policy[NFSD_A_POOL_MODE_MOD [NFSD_A_POOL_MODE_MODE] = { .type = NLA_NUL_STRING, }, }; +/* NFSD_CMD_SVC_EXPORT_SET_REQS - do */ +static const struct nla_policy nfsd_svc_export_set_reqs_nl_policy[NFSD_A_SVC_EXPORT_REQS_REQUESTS + 1] = { + [NFSD_A_SVC_EXPORT_REQS_REQUESTS] = NLA_POLICY_NESTED(nfsd_svc_export_nl_policy), +}; + +/* NFSD_CMD_EXPKEY_SET_REQS - do */ +static const struct nla_policy nfsd_expkey_set_reqs_nl_policy[NFSD_A_EXPKEY_REQS_REQUESTS + 1] = { + [NFSD_A_EXPKEY_REQS_REQUESTS] = NLA_POLICY_NESTED(nfsd_expkey_nl_policy), +}; + +/* NFSD_CMD_CACHE_FLUSH - do */ +static const struct nla_policy nfsd_cache_flush_nl_policy[NFSD_A_CACHE_FLUSH_MASK + 1] = { + [NFSD_A_CACHE_FLUSH_MASK] = NLA_POLICY_MASK(NLA_U32, 0x3), +}; + +/* NFSD_CMD_UNLOCK_IP - do */ +static const struct nla_policy nfsd_unlock_ip_nl_policy[NFSD_A_UNLOCK_IP_ADDRESS + 1] = { + [NFSD_A_UNLOCK_IP_ADDRESS] = NLA_POLICY_MIN_LEN(16), +}; + +/* NFSD_CMD_UNLOCK_FILESYSTEM - do */ +static const struct nla_policy nfsd_unlock_filesystem_nl_policy[NFSD_A_UNLOCK_FILESYSTEM_PATH + 1] = { + [NFSD_A_UNLOCK_FILESYSTEM_PATH] = { .type = NLA_NUL_STRING, }, +}; + +/* NFSD_CMD_UNLOCK_EXPORT - do */ +static const struct nla_policy nfsd_unlock_export_nl_policy[NFSD_A_UNLOCK_EXPORT_PATH + 1] = { + [NFSD_A_UNLOCK_EXPORT_PATH] = { .type = NLA_NUL_STRING, }, +}; + /* Ops table for nfsd */ static const struct genl_split_ops nfsd_nl_ops[] = { { @@ -103,6 +173,63 @@ static const struct genl_split_ops nfsd_nl_ops[] = { .doit = nfsd_nl_pool_mode_get_doit, .flags = GENL_CMD_CAP_DO, }, + { + .cmd = NFSD_CMD_SVC_EXPORT_GET_REQS, + .dumpit = nfsd_nl_svc_export_get_reqs_dumpit, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP, + }, + { + .cmd = NFSD_CMD_SVC_EXPORT_SET_REQS, + .doit = nfsd_nl_svc_export_set_reqs_doit, + .policy = nfsd_svc_export_set_reqs_nl_policy, + .maxattr = NFSD_A_SVC_EXPORT_REQS_REQUESTS, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = NFSD_CMD_EXPKEY_GET_REQS, + .dumpit = nfsd_nl_expkey_get_reqs_dumpit, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP, + }, + { + .cmd = NFSD_CMD_EXPKEY_SET_REQS, + .doit = nfsd_nl_expkey_set_reqs_doit, + .policy = nfsd_expkey_set_reqs_nl_policy, + .maxattr = NFSD_A_EXPKEY_REQS_REQUESTS, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = NFSD_CMD_CACHE_FLUSH, + .doit = nfsd_nl_cache_flush_doit, + .policy = nfsd_cache_flush_nl_policy, + .maxattr = NFSD_A_CACHE_FLUSH_MASK, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = NFSD_CMD_UNLOCK_IP, + .doit = nfsd_nl_unlock_ip_doit, + .policy = nfsd_unlock_ip_nl_policy, + .maxattr = NFSD_A_UNLOCK_IP_ADDRESS, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = NFSD_CMD_UNLOCK_FILESYSTEM, + .doit = nfsd_nl_unlock_filesystem_doit, + .policy = nfsd_unlock_filesystem_nl_policy, + .maxattr = NFSD_A_UNLOCK_FILESYSTEM_PATH, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = NFSD_CMD_UNLOCK_EXPORT, + .doit = nfsd_nl_unlock_export_doit, + .policy = nfsd_unlock_export_nl_policy, + .maxattr = NFSD_A_UNLOCK_EXPORT_PATH, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, +}; + +static const struct genl_multicast_group nfsd_nl_mcgrps[] = { + [NFSD_NLGRP_NONE] = { "none", }, + [NFSD_NLGRP_EXPORTD] = { "exportd", }, }; struct genl_family nfsd_nl_family __ro_after_init = { @@ -113,4 +240,6 @@ struct genl_family nfsd_nl_family __ro_after_init = { .module = THIS_MODULE, .split_ops = nfsd_nl_ops, .n_split_ops = ARRAY_SIZE(nfsd_nl_ops), + .mcgrps = nfsd_nl_mcgrps, + .n_mcgrps = ARRAY_SIZE(nfsd_nl_mcgrps), }; diff --git a/fs/nfsd/netlink.h b/fs/nfsd/netlink.h index 478117ff6b8c..af41aa0d4a65 100644 --- a/fs/nfsd/netlink.h +++ b/fs/nfsd/netlink.h @@ -13,7 +13,12 @@ #include <uapi/linux/nfsd_netlink.h> /* Common nested types */ +extern const struct nla_policy nfsd_auth_flavor_nl_policy[NFSD_A_AUTH_FLAVOR_FLAGS + 1]; +extern const struct nla_policy nfsd_expkey_nl_policy[NFSD_A_EXPKEY_PATH + 1]; +extern const struct nla_policy nfsd_fslocation_nl_policy[NFSD_A_FSLOCATION_PATH + 1]; +extern const struct nla_policy nfsd_fslocations_nl_policy[NFSD_A_FSLOCATIONS_LOCATION + 1]; extern const struct nla_policy nfsd_sock_nl_policy[NFSD_A_SOCK_TRANSPORT_NAME + 1]; +extern const struct nla_policy nfsd_svc_export_nl_policy[NFSD_A_SVC_EXPORT_FSID + 1]; extern const struct nla_policy nfsd_version_nl_policy[NFSD_A_VERSION_ENABLED + 1]; int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb, @@ -26,6 +31,22 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info); int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info); int nfsd_nl_pool_mode_set_doit(struct sk_buff *skb, struct genl_info *info); int nfsd_nl_pool_mode_get_doit(struct sk_buff *skb, struct genl_info *info); +int nfsd_nl_svc_export_get_reqs_dumpit(struct sk_buff *skb, + struct netlink_callback *cb); +int nfsd_nl_svc_export_set_reqs_doit(struct sk_buff *skb, + struct genl_info *info); +int nfsd_nl_expkey_get_reqs_dumpit(struct sk_buff *skb, + struct netlink_callback *cb); +int nfsd_nl_expkey_set_reqs_doit(struct sk_buff *skb, struct genl_info *info); +int nfsd_nl_cache_flush_doit(struct sk_buff *skb, struct genl_info *info); +int nfsd_nl_unlock_ip_doit(struct sk_buff *skb, struct genl_info *info); +int nfsd_nl_unlock_filesystem_doit(struct sk_buff *skb, struct genl_info *info); +int nfsd_nl_unlock_export_doit(struct sk_buff *skb, struct genl_info *info); + +enum { + NFSD_NLGRP_NONE, + NFSD_NLGRP_EXPORTD, +}; extern struct genl_family nfsd_nl_family; diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c index 0ac538c76180..76305b86c1a9 100644 --- a/fs/nfsd/nfs2acl.c +++ b/fs/nfsd/nfs2acl.c @@ -131,10 +131,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp) resp->status = fh_getattr(fh, &resp->stat); out: - /* argp->acl_{access,default} may have been allocated in - nfssvc_decode_setaclargs. */ - posix_acl_release(argp->acl_access); - posix_acl_release(argp->acl_default); + /* argp->acl_{access,default} are released in nfsaclsvc_release_setacl. */ return rpc_success; out_drop_lock: @@ -310,6 +307,16 @@ static void nfsaclsvc_release_access(struct svc_rqst *rqstp) fh_put(&resp->fh); } +static void nfsaclsvc_release_setacl(struct svc_rqst *rqstp) +{ + struct nfsd3_setaclargs *argp = rqstp->rq_argp; + struct nfsd_attrstat *resp = rqstp->rq_resp; + + fh_put(&resp->fh); + posix_acl_release(argp->acl_access); + posix_acl_release(argp->acl_default); +} + #define ST 1 /* status*/ #define AT 21 /* attributes */ #define pAT (1+AT) /* post attributes - conditional */ @@ -343,7 +350,7 @@ static const struct svc_procedure nfsd_acl_procedures2[5] = { .pc_func = nfsacld_proc_setacl, .pc_decode = nfsaclsvc_decode_setaclargs, .pc_encode = nfssvc_encode_attrstatres, - .pc_release = nfssvc_release_attrstat, + .pc_release = nfsaclsvc_release_setacl, .pc_argsize = sizeof(struct nfsd3_setaclargs), .pc_argzero = sizeof(struct nfsd3_setaclargs), .pc_ressize = sizeof(struct nfsd_attrstat), diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c index 7b5433bd3019..e87731380be8 100644 --- a/fs/nfsd/nfs3acl.c +++ b/fs/nfsd/nfs3acl.c @@ -118,10 +118,7 @@ out_drop_lock: out_errno: resp->status = nfserrno(error); out: - /* argp->acl_{access,default} may have been allocated in - nfs3svc_decode_setaclargs. */ - posix_acl_release(argp->acl_access); - posix_acl_release(argp->acl_default); + /* argp->acl_{access,default} are released in nfs3svc_release_setacl. */ return rpc_success; } @@ -223,6 +220,16 @@ static void nfs3svc_release_getacl(struct svc_rqst *rqstp) posix_acl_release(resp->acl_default); } +static void nfs3svc_release_setacl(struct svc_rqst *rqstp) +{ + struct nfsd3_setaclargs *argp = rqstp->rq_argp; + struct nfsd3_attrstat *resp = rqstp->rq_resp; + + fh_put(&resp->fh); + posix_acl_release(argp->acl_access); + posix_acl_release(argp->acl_default); +} + #define ST 1 /* status*/ #define AT 21 /* attributes */ #define pAT (1+AT) /* post attributes - conditional */ @@ -256,7 +263,7 @@ static const struct svc_procedure nfsd_acl_procedures3[3] = { .pc_func = nfsd3_proc_setacl, .pc_decode = nfs3svc_decode_setaclargs, .pc_encode = nfs3svc_encode_setaclres, - .pc_release = nfs3svc_release_fhandle, + .pc_release = nfs3svc_release_setacl, .pc_argsize = sizeof(struct nfsd3_setaclargs), .pc_argzero = sizeof(struct nfsd3_setaclargs), .pc_ressize = sizeof(struct nfsd3_attrstat), diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index ba06d3d3e6dd..71ba61b5d0a3 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -126,7 +126,7 @@ idtoname_hash(struct ent *ent) static int idtoname_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 @@ -306,7 +306,7 @@ nametoid_hash(struct ent *ent) static int nametoid_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 diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c index c3543d456702..f34320e4c2f4 100644 --- a/fs/nfsd/nfs4layouts.c +++ b/fs/nfsd/nfs4layouts.c @@ -234,6 +234,8 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate, get_nfs4_file(fp); stp->sc_file = fp; + if (parent->sc_export) + stp->sc_export = exp_get(parent->sc_export); ls = layoutstateid(stp); INIT_LIST_HEAD(&ls->ls_perclnt); @@ -251,10 +253,12 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate, ls->ls_file = find_any_file(fp); BUG_ON(!ls->ls_file); + ls->ls_fenced = false; + ls->ls_fence_delay = 0; + INIT_DELAYED_WORK(&ls->ls_fence_work, nfsd4_layout_fence_worker); + if (nfsd4_layout_setlease(ls)) { - nfsd_file_put(ls->ls_file); - put_nfs4_file(fp); - kmem_cache_free(nfs4_layout_stateid_cache, ls); + nfs4_put_stid(stp); return NULL; } @@ -267,10 +271,6 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate, list_add(&ls->ls_perfile, &fp->fi_lo_states); spin_unlock(&fp->fi_lock); - ls->ls_fenced = false; - ls->ls_fence_delay = 0; - INIT_DELAYED_WORK(&ls->ls_fence_work, nfsd4_layout_fence_worker); - trace_nfsd_layoutstate_alloc(&ls->ls_stid.sc_stateid); return ls; } diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index ab39ec885440..8561540ab2db 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -253,7 +253,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, .na_iattr = iap, .na_seclabel = &open->op_label, }; - struct dentry *parent, *child; + struct dentry *parent, *child = ERR_PTR(-EINVAL); __u32 v_mtime, v_atime; struct inode *inode; __be32 status; @@ -277,10 +277,14 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, if (open->op_acl) { if (open->op_dpacl || open->op_pacl) { status = nfserr_inval; - goto out_write; + goto out; + } + if (is_create_with_attrs(open)) { + status = nfsd4_acl_to_attr(NF4REG, open->op_acl, + &attrs); + if (status) + goto out; } - if (is_create_with_attrs(open)) - nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs); } else if (is_create_with_attrs(open)) { /* The dpacl and pacl will get released by nfsd_attrs_free(). */ attrs.na_dpacl = open->op_dpacl; @@ -293,7 +297,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, &QSTR_LEN(open->op_fname, open->op_fnamelen)); if (IS_ERR(child)) { status = nfserrno(PTR_ERR(child)); - goto out_write; + goto out; } if (d_really_is_negative(child)) { @@ -407,7 +411,6 @@ set_attr: out: end_creating(child); nfsd_attrs_free(&attrs); -out_write: fh_drop_write(fhp); return status; } @@ -837,7 +840,7 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out_aftermask; if (create->cr_acl) { - if (create->cr_dpacl || create->cr_pacl) { + if (attrs.na_dpacl || attrs.na_pacl) { status = nfserr_inval; goto out_aftermask; } @@ -1470,7 +1473,7 @@ void nfsd4_async_copy_reaper(struct nfsd_net *nn) list_for_each_safe(pos, next, &clp->async_copies) { copy = list_entry(pos, struct nfsd4_copy, copies); if (test_bit(NFSD4_COPY_F_OFFLOAD_DONE, ©->cp_flags)) { - if (--copy->cp_ttl) { + if (!--copy->cp_ttl) { list_del_init(©->copies); list_add(©->copies, &reaplist); } diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index b338473d6e52..6ea25a52d2f4 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -718,7 +718,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, return PTR_ERR(namecopy); name.data = namecopy; name.len = namelen; - get_user(princhashlen, &ci->cc_princhash.cp_len); + if (get_user(princhashlen, &ci->cc_princhash.cp_len)) + return -EFAULT; if (princhashlen > 0) { princhashcopy = memdup_user( &ci->cc_princhash.cp_data, diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6837b63d9864..a42f34842d77 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1168,6 +1168,7 @@ out_dec: void nfs4_put_stid(struct nfs4_stid *s) { + struct svc_export *exp = s->sc_export; struct nfs4_file *fp = s->sc_file; struct nfs4_client *clp = s->sc_client; @@ -1183,6 +1184,8 @@ nfs4_put_stid(struct nfs4_stid *s) nfs4_free_cpntf_statelist(clp->net, s); spin_unlock(&clp->cl_lock); s->sc_free(s); + if (exp) + exp_put(exp); if (fp) put_nfs4_file(fp); } @@ -1376,7 +1379,8 @@ static void destroy_delegation(struct nfs4_delegation *dp) * stateid or it's called from a laundromat thread (nfsd4_landromat()) that * determined that this specific state has expired and needs to be revoked * (both mark state with the appropriate stid sc_status mode). It is also - * assumed that a reference was taken on the @dp state. + * assumed that a reference was taken on the @dp state. This function + * consumes that reference. * * If this function finds that the @dp state is SC_STATUS_FREED it means * that a FREE_STATEID operation for this stateid has been processed and @@ -1745,21 +1749,119 @@ static struct nfs4_stid *find_one_sb_stid(struct nfs4_client *clp, struct super_block *sb, unsigned int sc_types) { - unsigned long id, tmp; + unsigned long id = 0; struct nfs4_stid *stid; spin_lock(&clp->cl_lock); - idr_for_each_entry_ul(&clp->cl_stateids, stid, tmp, id) + while ((stid = idr_get_next_ul(&clp->cl_stateids, &id)) != NULL) { if ((stid->sc_type & sc_types) && stid->sc_status == 0 && stid->sc_file->fi_inode->i_sb == sb) { refcount_inc(&stid->sc_count); break; } + id++; + } spin_unlock(&clp->cl_lock); return stid; } +/* + * Release the export reference an admin-revoked stateid holds, + * so the svc_export (and its vfsmount) is not pinned until the + * client issues FREE_STATEID. sc_export is no longer consulted + * once SC_STATUS_ADMIN_REVOKED is set. + */ +static void drop_stid_export(struct nfs4_client *clp, + struct nfs4_stid *stid) +{ + struct svc_export *exp; + + spin_lock(&clp->cl_lock); + exp = stid->sc_export; + stid->sc_export = NULL; + spin_unlock(&clp->cl_lock); + if (exp) + exp_put(exp); +} + +static void revoke_ol_stid(struct nfs4_client *clp, + struct nfs4_ol_stateid *stp) +{ + struct nfs4_stid *stid = &stp->st_stid; + + lockdep_assert_held(&stp->st_mutex); + spin_lock(&clp->cl_lock); + if (stid->sc_status == 0) { + stid->sc_status |= SC_STATUS_ADMIN_REVOKED; + atomic_inc(&clp->cl_admin_revoked); + spin_unlock(&clp->cl_lock); + if (stid->sc_type == SC_TYPE_LOCK) { + struct nfs4_lockowner *lo = + lockowner(stp->st_stateowner); + struct nfsd_file *nf; + + nf = find_any_file(stp->st_stid.sc_file); + if (nf) { + get_file(nf->nf_file); + filp_close(nf->nf_file, (fl_owner_t)lo); + nfsd_file_put(nf); + } + } + release_all_access(stp); + drop_stid_export(clp, stid); + } else + spin_unlock(&clp->cl_lock); +} + +static void revoke_one_stid(struct nfsd_net *nn, struct nfs4_client *clp, + struct nfs4_stid *stid) +{ + struct nfs4_ol_stateid *stp; + struct nfs4_delegation *dp; + + switch (stid->sc_type) { + case SC_TYPE_OPEN: + stp = openlockstateid(stid); + mutex_lock_nested(&stp->st_mutex, OPEN_STATEID_MUTEX); + revoke_ol_stid(clp, stp); + mutex_unlock(&stp->st_mutex); + break; + case SC_TYPE_LOCK: + stp = openlockstateid(stid); + mutex_lock_nested(&stp->st_mutex, LOCK_STATEID_MUTEX); + revoke_ol_stid(clp, stp); + mutex_unlock(&stp->st_mutex); + break; + case SC_TYPE_DELEG: + /* + * Extra reference guards against concurrent FREE_STATEID. + */ + refcount_inc(&stid->sc_count); + dp = delegstateid(stid); + spin_lock(&nn->deleg_lock); + if (!unhash_delegation_locked(dp, SC_STATUS_ADMIN_REVOKED)) + dp = NULL; + spin_unlock(&nn->deleg_lock); + if (dp) { + revoke_delegation(dp); + drop_stid_export(clp, stid); + } else + nfs4_put_stid(stid); + break; + case SC_TYPE_LAYOUT: + spin_lock(&clp->cl_lock); + if (stid->sc_status == 0) { + stid->sc_status |= SC_STATUS_ADMIN_REVOKED; + atomic_inc(&clp->cl_admin_revoked); + } + spin_unlock(&clp->cl_lock); + nfsd4_close_layout(layoutstateid(stid)); + drop_stid_export(clp, stid); + break; + } +} + /** * nfsd4_revoke_states - revoke all nfsv4 states associated with given filesystem * @nn: used to identify instance of nfsd (there is one per net namespace) @@ -1790,77 +1892,8 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) struct nfs4_stid *stid = find_one_sb_stid(clp, sb, sc_types); if (stid) { - struct nfs4_ol_stateid *stp; - struct nfs4_delegation *dp; - struct nfs4_layout_stateid *ls; - spin_unlock(&nn->client_lock); - switch (stid->sc_type) { - case SC_TYPE_OPEN: - stp = openlockstateid(stid); - mutex_lock_nested(&stp->st_mutex, - OPEN_STATEID_MUTEX); - - spin_lock(&clp->cl_lock); - if (stid->sc_status == 0) { - stid->sc_status |= - SC_STATUS_ADMIN_REVOKED; - atomic_inc(&clp->cl_admin_revoked); - spin_unlock(&clp->cl_lock); - release_all_access(stp); - } else - spin_unlock(&clp->cl_lock); - mutex_unlock(&stp->st_mutex); - break; - case SC_TYPE_LOCK: - stp = openlockstateid(stid); - mutex_lock_nested(&stp->st_mutex, - LOCK_STATEID_MUTEX); - spin_lock(&clp->cl_lock); - if (stid->sc_status == 0) { - struct nfs4_lockowner *lo = - lockowner(stp->st_stateowner); - struct nfsd_file *nf; - - stid->sc_status |= - SC_STATUS_ADMIN_REVOKED; - atomic_inc(&clp->cl_admin_revoked); - spin_unlock(&clp->cl_lock); - nf = find_any_file(stp->st_stid.sc_file); - if (nf) { - get_file(nf->nf_file); - filp_close(nf->nf_file, - (fl_owner_t)lo); - nfsd_file_put(nf); - } - release_all_access(stp); - } else - spin_unlock(&clp->cl_lock); - mutex_unlock(&stp->st_mutex); - break; - case SC_TYPE_DELEG: - refcount_inc(&stid->sc_count); - dp = delegstateid(stid); - spin_lock(&nn->deleg_lock); - if (!unhash_delegation_locked( - dp, SC_STATUS_ADMIN_REVOKED)) - dp = NULL; - spin_unlock(&nn->deleg_lock); - if (dp) - revoke_delegation(dp); - break; - case SC_TYPE_LAYOUT: - ls = layoutstateid(stid); - spin_lock(&clp->cl_lock); - if (stid->sc_status == 0) { - stid->sc_status |= - SC_STATUS_ADMIN_REVOKED; - atomic_inc(&clp->cl_admin_revoked); - } - spin_unlock(&clp->cl_lock); - nfsd4_close_layout(ls); - break; - } + revoke_one_stid(nn, clp, stid); nfs4_put_stid(stid); spin_lock(&nn->client_lock); if (clp->cl_minorversion == 0) @@ -1878,6 +1911,73 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) spin_unlock(&nn->client_lock); } +static struct nfs4_stid *find_one_export_stid(struct nfs4_client *clp, + const struct path *path, + unsigned int sc_types) +{ + unsigned long id = 0; + struct nfs4_stid *stid; + + spin_lock(&clp->cl_lock); + while ((stid = idr_get_next_ul(&clp->cl_stateids, &id)) != NULL) { + if ((stid->sc_type & sc_types) && + stid->sc_status == 0 && + stid->sc_export && + path_equal(&stid->sc_export->ex_path, path)) { + refcount_inc(&stid->sc_count); + break; + } + id++; + } + spin_unlock(&clp->cl_lock); + return stid; +} + +/** + * nfsd4_revoke_export_states - revoke nfsv4 states acquired through an export + * @nn: used to identify instance of nfsd (there is one per net namespace) + * @path: export path whose states should be revoked + * + * All nfs4 states (open, lock, delegation, layout) acquired through any + * export matching @path are revoked, regardless of which client holds + * them. Matching is by path identity (dentry + vfsmount), so multiple + * svc_export objects for the same path -- one per auth_domain -- are + * handled correctly. + * + * Userspace (exportfs -u) sends this after removing the last client + * for a path, enabling the underlying filesystem to be unmounted. + */ +void nfsd4_revoke_export_states(struct nfsd_net *nn, const struct path *path) +{ + unsigned int idhashval; + unsigned int sc_types; + + sc_types = SC_TYPE_OPEN | SC_TYPE_LOCK | SC_TYPE_DELEG | SC_TYPE_LAYOUT; + + spin_lock(&nn->client_lock); + for (idhashval = 0; idhashval < CLIENT_HASH_SIZE; idhashval++) { + struct list_head *head = &nn->conf_id_hashtbl[idhashval]; + struct nfs4_client *clp; + retry: + list_for_each_entry(clp, head, cl_idhash) { + struct nfs4_stid *stid = find_one_export_stid( + clp, path, + sc_types); + if (stid) { + spin_unlock(&nn->client_lock); + revoke_one_stid(nn, clp, stid); + nfs4_put_stid(stid); + spin_lock(&nn->client_lock); + if (clp->cl_minorversion == 0) + nn->nfs40_last_revoke = + ktime_get_boottime_seconds(); + goto retry; + } + } + } + spin_unlock(&nn->client_lock); +} + static inline int hash_sessionid(struct nfs4_sessionid *sessionid) { @@ -5049,6 +5149,7 @@ static void nfsd4_drop_revoked_stid(struct nfs4_stid *s) { struct nfs4_client *cl = s->sc_client; LIST_HEAD(reaplist); + struct nfs4_layout_stateid *ls; struct nfs4_ol_stateid *stp; struct nfs4_delegation *dp; bool unhashed; @@ -5074,6 +5175,12 @@ static void nfsd4_drop_revoked_stid(struct nfs4_stid *s) spin_unlock(&cl->cl_lock); nfs4_put_stid(s); break; + case SC_TYPE_LAYOUT: + ls = layoutstateid(s); + list_del_init(&ls->ls_perclnt); + spin_unlock(&cl->cl_lock); + nfs4_put_stid(s); + break; default: spin_unlock(&cl->cl_lock); } @@ -5169,6 +5276,7 @@ retry: /* Replace unconfirmed owners without checking for replay. */ release_openowner(oo); oo = NULL; + goto retry; } if (oo) { if (new) @@ -6157,6 +6265,8 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp, dp = alloc_init_deleg(clp, fp, odstate, dl_type); if (!dp) goto out_delegees; + if (stp->st_stid.sc_export) + dp->dl_stid.sc_export = exp_get(stp->st_stid.sc_export); fl = nfs4_alloc_init_lease(dp); if (!fl) @@ -6490,8 +6600,11 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf goto out; } - if (!open->op_stp) + if (!open->op_stp) { new_stp = true; + stp->st_stid.sc_export = + exp_get(current_fh->fh_export); + } } /* @@ -8187,6 +8300,9 @@ retry: stp->st_stateowner = nfs4_get_stateowner(&lo->lo_owner); get_nfs4_file(fp); stp->st_stid.sc_file = fp; + if (open_stp->st_stid.sc_export) + stp->st_stid.sc_export = + exp_get(open_stp->st_stid.sc_export); stp->st_access_bmap = 0; stp->st_deny_bmap = open_stp->st_deny_bmap; stp->st_openstp = open_stp; @@ -9517,6 +9633,9 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate, dp = alloc_init_deleg(clp, fp, NULL, NFS4_OPEN_DELEGATE_READ); if (!dp) goto out_delegees; + if (cstate->current_fh.fh_export) + dp->dl_stid.sc_export = + exp_get(cstate->current_fh.fh_export); fl = nfs4_alloc_init_lease(dp); if (!fl) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 20355dc3f1d1..e17488a911f7 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -2008,10 +2008,11 @@ static __be32 nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) { struct nfsd4_secinfo_no_name *sin = &u->secinfo_no_name; + + sin->sin_exp = NULL; if (xdr_stream_decode_u32(argp->xdr, &sin->sin_style) < 0) return nfserr_bad_xdr; - sin->sin_exp = NULL; return nfs_ok; } diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 04e3954d54bd..d0486f4a47ba 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -10,6 +10,7 @@ #include <linux/ctype.h> #include <linux/fs_context.h> +#include <linux/sunrpc/cache.h> #include <linux/sunrpc/svcsock.h> #include <linux/lockd/bind.h> #include <linux/sunrpc/addr.h> @@ -246,7 +247,7 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size) if (rpc_pton(net, fo_path, size, sap, salen) == 0) return -EINVAL; - trace_nfsd_ctl_unlock_ip(net, buf); + trace_nfsd_ctl_unlock_ip(net, sap, svc_addr_len(sap)); return nlmsvc_unlock_all_by_ip(sap); } @@ -1412,6 +1413,21 @@ static int create_proc_exports_entry(void) unsigned int nfsd_net_id; +struct nfsd_genl_rqstp { + struct sockaddr rq_daddr; + struct sockaddr rq_saddr; + unsigned long rq_flags; + ktime_t rq_stime; + __be32 rq_xid; + u32 rq_vers; + u32 rq_prog; + u32 rq_proc; + + /* NFSv4 compound */ + u32 rq_opcnt; + u32 rq_opnum[16]; +}; + static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb, struct netlink_callback *cb, struct nfsd_genl_rqstp *genl_rqstp) @@ -2198,6 +2214,193 @@ err_free_msg: } /** + * nfsd_nl_cache_flush_doit - flush nfsd caches via netlink + * @skb: reply buffer + * @info: netlink metadata and command arguments + * + * Flush the svc_export and/or expkey caches. If NFSD_A_CACHE_FLUSH_MASK + * is provided, only flush the caches indicated by the bitmask (bit 0 = + * svc_export, bit 1 = expkey). If omitted, flush both. + * + * Return 0 on success or a negative errno. + */ +int nfsd_nl_cache_flush_doit(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = genl_info_net(info); + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + u32 mask = ~0U; + + if (info->attrs[NFSD_A_CACHE_FLUSH_MASK]) + mask = nla_get_u32(info->attrs[NFSD_A_CACHE_FLUSH_MASK]); + + mutex_lock(&nfsd_mutex); + + if ((mask & NFSD_CACHE_TYPE_SVC_EXPORT) && + nn->svc_export_cache) + cache_purge(nn->svc_export_cache); + + if ((mask & NFSD_CACHE_TYPE_EXPKEY) && + nn->svc_expkey_cache) + cache_purge(nn->svc_expkey_cache); + + mutex_unlock(&nfsd_mutex); + + return 0; +} + +int nfsd_cache_notify(struct cache_detail *cd, struct cache_head *h, u32 cache_type) +{ + struct genlmsghdr *hdr; + struct sk_buff *msg; + + if (!genl_has_listeners(&nfsd_nl_family, cd->net, NFSD_NLGRP_EXPORTD)) + return -ENOLINK; + + msg = genlmsg_new(nla_total_size(sizeof(u32)), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = genlmsg_put(msg, 0, 0, &nfsd_nl_family, 0, NFSD_CMD_CACHE_NOTIFY); + if (!hdr) { + nlmsg_free(msg); + return -ENOMEM; + } + + if (nla_put_u32(msg, NFSD_A_CACHE_NOTIFY_CACHE_TYPE, cache_type)) { + nlmsg_free(msg); + return -ENOMEM; + } + + genlmsg_end(msg, hdr); + return genlmsg_multicast_netns(&nfsd_nl_family, cd->net, msg, 0, + NFSD_NLGRP_EXPORTD, GFP_KERNEL); +} + +/** + * nfsd_nl_unlock_ip_doit - release NLM locks held by an IP address + * @skb: reply buffer + * @info: netlink metadata and command arguments + * + * Return: 0 on success or a negative errno. + */ +int nfsd_nl_unlock_ip_doit(struct sk_buff *skb, struct genl_info *info) +{ + struct sockaddr *sap; + + if (GENL_REQ_ATTR_CHECK(info, NFSD_A_UNLOCK_IP_ADDRESS)) + return -EINVAL; + sap = nla_data(info->attrs[NFSD_A_UNLOCK_IP_ADDRESS]); + switch (sap->sa_family) { + case AF_INET: + if (nla_len(info->attrs[NFSD_A_UNLOCK_IP_ADDRESS]) < + sizeof(struct sockaddr_in)) + return -EINVAL; + break; + case AF_INET6: + if (nla_len(info->attrs[NFSD_A_UNLOCK_IP_ADDRESS]) < + sizeof(struct sockaddr_in6)) + return -EINVAL; + break; + default: + return -EAFNOSUPPORT; + } + /* + * nlmsvc_unlock_all_by_ip() releases matching locks + * across all network namespaces because lockd operates + * a single global instance. + */ + trace_nfsd_ctl_unlock_ip(genl_info_net(info), sap, + svc_addr_len(sap)); + return nlmsvc_unlock_all_by_ip(sap); +} + +/** + * nfsd_nl_unlock_filesystem_doit - revoke NFS state under a filesystem path + * @skb: reply buffer + * @info: netlink metadata and command arguments + * + * Return: 0 on success or a negative errno. + */ +int nfsd_nl_unlock_filesystem_doit(struct sk_buff *skb, + struct genl_info *info) +{ + struct net *net = genl_info_net(info); + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct path path; + int error; + + if (GENL_REQ_ATTR_CHECK(info, NFSD_A_UNLOCK_FILESYSTEM_PATH)) + return -EINVAL; + + trace_nfsd_ctl_unlock_fs(net, + nla_data(info->attrs[NFSD_A_UNLOCK_FILESYSTEM_PATH])); + error = kern_path( + nla_data(info->attrs[NFSD_A_UNLOCK_FILESYSTEM_PATH]), + 0, &path); + if (error) + return error; + + nfsd4_cancel_copy_by_sb(net, path.dentry->d_sb); + error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb); + + mutex_lock(&nfsd_mutex); + if (nn->nfsd_serv) + nfsd4_revoke_states(nn, path.dentry->d_sb); + else + error = -EINVAL; + mutex_unlock(&nfsd_mutex); + + path_put(&path); + return error; +} + +/** + * nfsd_nl_unlock_export_doit - revoke NFSv4 state for an export path + * @skb: reply buffer + * @info: netlink metadata and command arguments + * + * Revokes all NFSv4 state (opens, locks, delegations, layouts) acquired + * through any export of the given path, regardless of which client holds + * the state. Userspace (exportfs -u) sends this after removing the last + * client for a path so the underlying filesystem can be unmounted. + * + * Unlike NFSD_CMD_UNLOCK_FILESYSTEM, which operates at superblock + * granularity, this command revokes only the state associated with + * exports of a specific path. + * + * Return: 0 on success or a negative errno. + */ +int nfsd_nl_unlock_export_doit(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = genl_info_net(info); + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct path path; + int error; + + if (GENL_REQ_ATTR_CHECK(info, NFSD_A_UNLOCK_EXPORT_PATH)) + return -EINVAL; + + trace_nfsd_ctl_unlock_export(net, + nla_data(info->attrs[NFSD_A_UNLOCK_EXPORT_PATH])); + error = kern_path( + nla_data(info->attrs[NFSD_A_UNLOCK_EXPORT_PATH]), + 0, &path); + if (error) + return error; + + mutex_lock(&nfsd_mutex); + if (nn->nfsd_serv) { + nfsd_file_close_export(net, &path); + nfsd4_revoke_export_states(nn, &path); + } else + error = -EINVAL; + mutex_unlock(&nfsd_mutex); + + path_put(&path); + return error; +} + +/** * nfsd_net_init - Prepare the nfsd_net portion of a new net namespace * @net: a freshly-created network namespace * @@ -2320,12 +2523,9 @@ static int __init init_nfsd(void) if (retval) goto out_free_pnfs; nfsd_lockd_init(); /* lockd->nfsd callbacks */ - retval = nfsd_export_wq_init(); - if (retval) - goto out_free_lockd; retval = register_pernet_subsys(&nfsd_net_ops); if (retval < 0) - goto out_free_export_wq; + goto out_free_lockd; retval = register_cld_notifier(); if (retval) goto out_free_subsys; @@ -2354,8 +2554,6 @@ out_free_cld: unregister_cld_notifier(); out_free_subsys: unregister_pernet_subsys(&nfsd_net_ops); -out_free_export_wq: - nfsd_export_wq_shutdown(); out_free_lockd: nfsd_lockd_shutdown(); nfsd_drc_slab_free(); @@ -2376,7 +2574,6 @@ static void __exit exit_nfsd(void) nfsd4_destroy_laundry_wq(); unregister_cld_notifier(); unregister_pernet_subsys(&nfsd_net_ops); - nfsd_export_wq_shutdown(); nfsd_drc_slab_free(); nfsd_lockd_shutdown(); nfsd4_free_slabs(); diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 7c009f07c90b..11bce03b9031 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -45,11 +45,10 @@ bool nfsd_support_version(int vers); /* * Default and maximum payload size (NFS READ or WRITE), in bytes. - * The default is historical, and the maximum is an implementation - * limit. + * The maximum is an implementation limit. */ enum { - NFSSVC_DEFBLKSIZE = 1 * 1024 * 1024, + NFSSVC_DEFBLKSIZE = 4 * 1024 * 1024, NFSSVC_MAXBLKSIZE = RPCSVC_MAXPAYLOAD, }; @@ -60,21 +59,6 @@ struct readdir_cd { /* Maximum number of operations per session compound */ #define NFSD_MAX_OPS_PER_COMPOUND 200 -struct nfsd_genl_rqstp { - struct sockaddr rq_daddr; - struct sockaddr rq_saddr; - unsigned long rq_flags; - ktime_t rq_stime; - __be32 rq_xid; - u32 rq_vers; - u32 rq_prog; - u32 rq_proc; - - /* NFSv4 compound */ - u32 rq_opcnt; - u32 rq_opnum[16]; -}; - extern struct svc_program nfsd_programs[]; extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4; extern struct mutex nfsd_mutex; @@ -123,7 +107,7 @@ struct dentry *nfsd_client_mkdir(struct nfsd_net *nn, const struct tree_descr *, struct dentry **fdentries); void nfsd_client_rmdir(struct dentry *dentry); - +int nfsd_cache_notify(struct cache_detail *cd, struct cache_head *h, u32 cache_type); #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) #ifdef CONFIG_NFSD_V2_ACL diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index c5ccea64c281..dec83e92650d 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -145,6 +145,7 @@ struct nfs4_stid { spinlock_t sc_lock; struct nfs4_client *sc_client; struct nfs4_file *sc_file; + struct svc_export *sc_export; void (*sc_free)(struct nfs4_stid *); }; @@ -862,6 +863,7 @@ struct nfsd_file *find_any_file(struct nfs4_file *f); #ifdef CONFIG_NFSD_V4 void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb); +void nfsd4_revoke_export_states(struct nfsd_net *nn, const struct path *path); void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb); int nfsd_net_cb_init(struct nfsd_net *nn); void nfsd_net_cb_shutdown(struct nfsd_net *nn); @@ -869,6 +871,10 @@ void nfsd_net_cb_shutdown(struct nfsd_net *nn); static inline void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) { } +static inline void nfsd4_revoke_export_states(struct nfsd_net *nn, + const struct path *path) +{ +} static inline void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb) { } diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h index b631a472222b..1c5a1e50f946 100644 --- a/fs/nfsd/trace.h +++ b/fs/nfsd/trace.h @@ -1985,19 +1985,20 @@ TRACE_EVENT(nfsd_cb_recall_any_done, TRACE_EVENT(nfsd_ctl_unlock_ip, TP_PROTO( const struct net *net, - const char *address + const struct sockaddr *addr, + const unsigned int addrlen ), - TP_ARGS(net, address), + TP_ARGS(net, addr, addrlen), TP_STRUCT__entry( __field(unsigned int, netns_ino) - __string(address, address) + __sockaddr(addr, addrlen) ), TP_fast_assign( __entry->netns_ino = net->ns.inum; - __assign_str(address); + __assign_sockaddr(addr, addr, addrlen); ), - TP_printk("address=%s", - __get_str(address) + TP_printk("addr=%pISpc", + __get_sockaddr(addr) ) ); @@ -2020,6 +2021,25 @@ TRACE_EVENT(nfsd_ctl_unlock_fs, ) ); +TRACE_EVENT(nfsd_ctl_unlock_export, + TP_PROTO( + const struct net *net, + const char *path + ), + TP_ARGS(net, path), + TP_STRUCT__entry( + __field(unsigned int, netns_ino) + __string(path, path) + ), + TP_fast_assign( + __entry->netns_ino = net->ns.inum; + __assign_str(path); + ), + TP_printk("path=%s", + __get_str(path) + ) +); + TRACE_EVENT(nfsd_ctl_filehandle, TP_PROTO( const struct net *net, diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 653cd6fea202..1e89c7ff9493 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1440,7 +1440,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long exp_op_flags = 0; unsigned int pflags = current->flags; bool restore_flags = false; - unsigned int nvecs; + int nvecs; trace_nfsd_write_opened(rqstp, fhp, offset, *cnt); @@ -1480,6 +1480,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, } nvecs = xdr_buf_to_bvec(rqstp->rq_bvec, rqstp->rq_maxpages, payload); + if (nvecs < 0) { + host_err = nvecs; + goto out_nfserr; + } since = READ_ONCE(file->f_wb_err); if (verf) @@ -1509,8 +1513,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, nfsd_stats_io_write_add(nn, exp, *cnt); fsnotify_modify(file); host_err = filemap_check_wb_err(file->f_mapping, since); - if (host_err < 0) + if (host_err < 0) { + commit_reset_write_verifier(nn, rqstp, host_err); goto out_nfserr; + } if (stable && fhp->fh_use_wgather) { host_err = wait_for_concurrent_writes(file); @@ -1690,6 +1696,8 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf, nfsd_copy_write_verifier(verf, nn); err2 = filemap_check_wb_err(nf->nf_file->f_mapping, since); + if (err2 < 0) + commit_reset_write_verifier(nn, rqstp, err2); err = nfserrno(err2); break; case -EINVAL: diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index b1e595c2615b..2735c332ddb7 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -80,6 +80,9 @@ struct cache_detail { int (*cache_upcall)(struct cache_detail *, struct cache_head *); + int (*cache_notify)(struct cache_detail *cd, + struct cache_head *h); + void (*cache_request)(struct cache_detail *cd, struct cache_head *ch, char **bpp, int *blen); @@ -189,9 +192,9 @@ sunrpc_cache_update(struct cache_detail *detail, struct cache_head *new, struct cache_head *old, int hash); extern int -sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h); +sunrpc_cache_upcall(struct cache_detail *detail, struct cache_head *h); extern int -sunrpc_cache_pipe_upcall_timeout(struct cache_detail *detail, +sunrpc_cache_upcall_warn(struct cache_detail *detail, struct cache_head *h); @@ -248,6 +251,14 @@ extern int sunrpc_cache_register_pipefs(struct dentry *parent, const char *, extern void sunrpc_cache_unregister_pipefs(struct cache_detail *); extern void sunrpc_cache_unhash(struct cache_detail *, struct cache_head *); +int sunrpc_cache_requests_count(struct cache_detail *cd); +int sunrpc_cache_requests_snapshot(struct cache_detail *cd, + struct cache_head **items, + u64 *seqnos, int max, + u64 min_seqno); +int sunrpc_cache_notify(struct cache_detail *cd, struct cache_head *h, + u32 cache_type); + /* Must store cache_detail in seq_file->private if using next three functions */ extern void *cache_seq_start_rcu(struct seq_file *file, loff_t *pos); extern void *cache_seq_next_rcu(struct seq_file *file, void *p, loff_t *pos); diff --git a/include/linux/sunrpc/gss_krb5.h b/include/linux/sunrpc/gss_krb5.h index 43950b5237c8..1cd452ed1db5 100644 --- a/include/linux/sunrpc/gss_krb5.h +++ b/include/linux/sunrpc/gss_krb5.h @@ -37,13 +37,9 @@ #ifndef _LINUX_SUNRPC_GSS_KRB5_H #define _LINUX_SUNRPC_GSS_KRB5_H -#include <crypto/skcipher.h> #include <linux/sunrpc/auth_gss.h> #include <linux/sunrpc/gss_err.h> -/* Length of constant used in key derivation */ -#define GSS_KRB5_K5CLENGTH (5) - /* Maximum key length (in bytes) for the supported crypto algorithms */ #define GSS_KRB5_MAX_KEYLEN (32) @@ -56,11 +52,6 @@ /* The length of the Kerberos GSS token header */ #define GSS_KRB5_TOK_HDR_LEN (16) -#define KG_TOK_MIC_MSG 0x0101 -#define KG_TOK_WRAP_MSG 0x0201 - -#define KG2_TOK_INITIAL 0x0101 -#define KG2_TOK_RESPONSE 0x0202 #define KG2_TOK_MIC 0x0404 #define KG2_TOK_WRAP 0x0504 @@ -68,102 +59,6 @@ #define KG2_TOKEN_FLAG_SEALED 0x02 #define KG2_TOKEN_FLAG_ACCEPTORSUBKEY 0x04 -#define KG2_RESP_FLAG_ERROR 0x0001 -#define KG2_RESP_FLAG_DELEG_OK 0x0002 - -enum sgn_alg { - SGN_ALG_DES_MAC_MD5 = 0x0000, - SGN_ALG_MD2_5 = 0x0001, - SGN_ALG_DES_MAC = 0x0002, - SGN_ALG_3 = 0x0003, /* not published */ - SGN_ALG_HMAC_SHA1_DES3_KD = 0x0004 -}; -enum seal_alg { - SEAL_ALG_NONE = 0xffff, - SEAL_ALG_DES = 0x0000, - SEAL_ALG_1 = 0x0001, /* not published */ - SEAL_ALG_DES3KD = 0x0002 -}; - -/* - * These values are assigned by IANA and published via the - * subregistry at the link below: - * - * https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-2 - */ -#define CKSUMTYPE_CRC32 0x0001 -#define CKSUMTYPE_RSA_MD4 0x0002 -#define CKSUMTYPE_RSA_MD4_DES 0x0003 -#define CKSUMTYPE_DESCBC 0x0004 -#define CKSUMTYPE_RSA_MD5 0x0007 -#define CKSUMTYPE_RSA_MD5_DES 0x0008 -#define CKSUMTYPE_NIST_SHA 0x0009 -#define CKSUMTYPE_HMAC_SHA1_DES3 0x000c -#define CKSUMTYPE_HMAC_SHA1_96_AES128 0x000f -#define CKSUMTYPE_HMAC_SHA1_96_AES256 0x0010 -#define CKSUMTYPE_CMAC_CAMELLIA128 0x0011 -#define CKSUMTYPE_CMAC_CAMELLIA256 0x0012 -#define CKSUMTYPE_HMAC_SHA256_128_AES128 0x0013 -#define CKSUMTYPE_HMAC_SHA384_192_AES256 0x0014 -#define CKSUMTYPE_HMAC_MD5_ARCFOUR -138 /* Microsoft md5 hmac cksumtype */ - -/* from gssapi_err_krb5.h */ -#define KG_CCACHE_NOMATCH (39756032L) -#define KG_KEYTAB_NOMATCH (39756033L) -#define KG_TGT_MISSING (39756034L) -#define KG_NO_SUBKEY (39756035L) -#define KG_CONTEXT_ESTABLISHED (39756036L) -#define KG_BAD_SIGN_TYPE (39756037L) -#define KG_BAD_LENGTH (39756038L) -#define KG_CTX_INCOMPLETE (39756039L) -#define KG_CONTEXT (39756040L) -#define KG_CRED (39756041L) -#define KG_ENC_DESC (39756042L) -#define KG_BAD_SEQ (39756043L) -#define KG_EMPTY_CCACHE (39756044L) -#define KG_NO_CTYPES (39756045L) - -/* per Kerberos v5 protocol spec crypto types from the wire. - * these get mapped to linux kernel crypto routines. - * - * These values are assigned by IANA and published via the - * subregistry at the link below: - * - * https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-1 - */ -#define ENCTYPE_NULL 0x0000 -#define ENCTYPE_DES_CBC_CRC 0x0001 /* DES cbc mode with CRC-32 */ -#define ENCTYPE_DES_CBC_MD4 0x0002 /* DES cbc mode with RSA-MD4 */ -#define ENCTYPE_DES_CBC_MD5 0x0003 /* DES cbc mode with RSA-MD5 */ -#define ENCTYPE_DES_CBC_RAW 0x0004 /* DES cbc mode raw */ -/* XXX deprecated? */ -#define ENCTYPE_DES3_CBC_SHA 0x0005 /* DES-3 cbc mode with NIST-SHA */ -#define ENCTYPE_DES3_CBC_RAW 0x0006 /* DES-3 cbc mode raw */ -#define ENCTYPE_DES_HMAC_SHA1 0x0008 -#define ENCTYPE_DES3_CBC_SHA1 0x0010 -#define ENCTYPE_AES128_CTS_HMAC_SHA1_96 0x0011 -#define ENCTYPE_AES256_CTS_HMAC_SHA1_96 0x0012 -#define ENCTYPE_AES128_CTS_HMAC_SHA256_128 0x0013 -#define ENCTYPE_AES256_CTS_HMAC_SHA384_192 0x0014 -#define ENCTYPE_ARCFOUR_HMAC 0x0017 -#define ENCTYPE_ARCFOUR_HMAC_EXP 0x0018 -#define ENCTYPE_CAMELLIA128_CTS_CMAC 0x0019 -#define ENCTYPE_CAMELLIA256_CTS_CMAC 0x001A -#define ENCTYPE_UNKNOWN 0x01ff - -/* - * Constants used for key derivation - */ -/* for 3DES */ -#define KG_USAGE_SEAL (22) -#define KG_USAGE_SIGN (23) -#define KG_USAGE_SEQ (24) - -/* from rfc3961 */ -#define KEY_USAGE_SEED_CHECKSUM (0x99) -#define KEY_USAGE_SEED_ENCRYPTION (0xAA) -#define KEY_USAGE_SEED_INTEGRITY (0x55) - /* from rfc4121 */ #define KG_USAGE_ACCEPTOR_SEAL (22) #define KG_USAGE_ACCEPTOR_SIGN (23) diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index df6e08aaad57..5aadb47b3b0e 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -66,7 +66,6 @@ extern unsigned int svcrdma_ord; extern unsigned int svcrdma_max_requests; extern unsigned int svcrdma_max_bc_requests; extern unsigned int svcrdma_max_req_size; -extern struct workqueue_struct *svcrdma_wq; extern struct percpu_counter svcrdma_stat_read; extern struct percpu_counter svcrdma_stat_recv; @@ -117,6 +116,8 @@ struct svcxprt_rdma { struct llist_head sc_recv_ctxts; + struct llist_head sc_send_release_list; + atomic_t sc_completion_ids; }; /* sc_flags */ @@ -230,13 +231,11 @@ struct svc_rdma_write_info { unsigned int wi_next_off; struct svc_rdma_chunk_ctxt wi_cc; - struct work_struct wi_work; }; struct svc_rdma_send_ctxt { struct llist_node sc_node; struct rpc_rdma_cid sc_cid; - struct work_struct sc_work; struct svcxprt_rdma *sc_rdma; struct ib_send_wr sc_send_wr; @@ -300,6 +299,7 @@ extern int svc_rdma_process_read_list(struct svcxprt_rdma *rdma, /* svc_rdma_sendto.c */ extern void svc_rdma_send_ctxts_destroy(struct svcxprt_rdma *rdma); +extern void svc_rdma_send_ctxts_drain(struct svcxprt_rdma *rdma); extern struct svc_rdma_send_ctxt * svc_rdma_send_ctxt_get(struct svcxprt_rdma *rdma); extern void svc_rdma_send_ctxt_put(struct svcxprt_rdma *rdma, @@ -328,6 +328,7 @@ extern int svc_rdma_result_payload(struct svc_rqst *rqstp, unsigned int offset, unsigned int length); /* svc_rdma_transport.c */ +extern void svc_rdma_xprt_deferred_close(struct svcxprt_rdma *rdma); extern struct svc_xprt_class svc_rdma_class; #ifdef CONFIG_SUNRPC_BACKCHANNEL extern struct svc_xprt_class svc_rdma_bc_class; diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index b639a6fafcbc..b102b4f21e6b 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -138,8 +138,23 @@ void xdr_terminate_string(const struct xdr_buf *, const u32); size_t xdr_buf_pagecount(const struct xdr_buf *buf); int xdr_alloc_bvec(struct xdr_buf *buf, gfp_t gfp); void xdr_free_bvec(struct xdr_buf *buf); -unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, - const struct xdr_buf *xdr); +int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, + const struct xdr_buf *xdr); +int xdr_buf_to_sg(const struct xdr_buf *buf, unsigned int offset, + unsigned int len, struct scatterlist *sg, unsigned int nsg); +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); + +/* + * Inline scatterlist entries for xdr_buf_to_sg_alloc(). Sized to cover the + * head kvec, tail kvec, and a few page fragments without any heap allocation. + */ +enum { + XDR_BUF_TO_SG_NENTS = 8, +}; + static inline __be32 *xdr_encode_array(__be32 *p, const void *s, unsigned int len) { @@ -260,7 +275,6 @@ extern void xdr_finish_decode(struct xdr_stream *xdr); extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes); extern unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len); extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len); -extern int xdr_process_buf(const struct xdr_buf *buf, unsigned int offset, unsigned int len, int (*actor)(struct scatterlist *, void *), void *data); extern void xdr_set_pagelen(struct xdr_stream *, unsigned int len); extern bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf, unsigned int len); diff --git a/include/linux/sunrpc/xdrgen/nlm3.h b/include/linux/sunrpc/xdrgen/nlm3.h new file mode 100644 index 000000000000..897e7d91807c --- /dev/null +++ b/include/linux/sunrpc/xdrgen/nlm3.h @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Generated by xdrgen. Manual edits will be lost. */ +/* XDR specification file: ../../Documentation/sunrpc/xdr/nlm3.x */ +/* XDR specification modification time: Thu Apr 23 10:56:34 2026 */ + +#ifndef _LINUX_XDRGEN_NLM3_DEF_H +#define _LINUX_XDRGEN_NLM3_DEF_H + +#include <linux/types.h> +#include <linux/sunrpc/xdrgen/_defs.h> + +enum { LM_MAXSTRLEN = 1024 }; + +enum { LM_MAXNAMELEN = 1025 }; + +enum { MAXNETOBJ_SZ = 1024 }; + +typedef opaque netobj; + +enum nlm_stats { + LCK_GRANTED = 0, + LCK_DENIED = 1, + LCK_DENIED_NOLOCKS = 2, + LCK_BLOCKED = 3, + LCK_DENIED_GRACE_PERIOD = 4, +}; + +typedef __be32 nlm_stats; + +struct nlm_stat { + nlm_stats stat; +}; + +struct nlm_res { + netobj cookie; + struct nlm_stat stat; +}; + +struct nlm_holder { + bool exclusive; + s32 uppid; + netobj oh; + u32 l_offset; + u32 l_len; +}; + +struct nlm_testrply { + nlm_stats stat; + union { + struct nlm_holder holder; + } u; +}; + +struct nlm_testres { + netobj cookie; + struct nlm_testrply test_stat; +}; + +struct nlm_lock { + string caller_name; + netobj fh; + netobj oh; + s32 uppid; + u32 l_offset; + u32 l_len; +}; + +struct nlm_lockargs { + netobj cookie; + bool block; + bool exclusive; + struct nlm_lock alock; + bool reclaim; + s32 state; +}; + +struct nlm_cancargs { + netobj cookie; + bool block; + bool exclusive; + struct nlm_lock alock; +}; + +struct nlm_testargs { + netobj cookie; + bool exclusive; + struct nlm_lock alock; +}; + +struct nlm_unlockargs { + netobj cookie; + struct nlm_lock alock; +}; + +enum fsh_mode { + fsm_DN = 0, + fsm_DR = 1, + fsm_DW = 2, + fsm_DRW = 3, +}; + +typedef enum fsh_mode fsh_mode; + +enum fsh_access { + fsa_NONE = 0, + fsa_R = 1, + fsa_W = 2, + fsa_RW = 3, +}; + +typedef enum fsh_access fsh_access; + +struct nlm_share { + string caller_name; + netobj fh; + netobj oh; + fsh_mode mode; + fsh_access access; +}; + +struct nlm_shareargs { + netobj cookie; + struct nlm_share share; + bool reclaim; +}; + +struct nlm_shareres { + netobj cookie; + nlm_stats stat; + s32 sequence; +}; + +struct nlm_notify { + string name; + s32 state; +}; + +enum { SM_PRIV_SIZE = 16 }; + +struct nlm_notifyargs { + struct nlm_notify notify; + u8 private[SM_PRIV_SIZE]; +}; + +enum { + NLM_NULL = 0, + NLM_TEST = 1, + NLM_LOCK = 2, + NLM_CANCEL = 3, + NLM_UNLOCK = 4, + NLM_GRANTED = 5, + NLM_TEST_MSG = 6, + NLM_LOCK_MSG = 7, + NLM_CANCEL_MSG = 8, + NLM_UNLOCK_MSG = 9, + NLM_GRANTED_MSG = 10, + NLM_TEST_RES = 11, + NLM_LOCK_RES = 12, + NLM_CANCEL_RES = 13, + NLM_UNLOCK_RES = 14, + NLM_GRANTED_RES = 15, + NLM_SM_NOTIFY = 16, + NLM_SHARE = 20, + NLM_UNSHARE = 21, + NLM_NM_LOCK = 22, + NLM_FREE_ALL = 23, +}; + +#ifndef NLM_PROG +#define NLM_PROG (100021) +#endif + +#define NLM3_netobj_sz (XDR_unsigned_int + XDR_QUADLEN(MAXNETOBJ_SZ)) +#define NLM3_nlm_stats_sz (XDR_int) +#define NLM3_nlm_stat_sz \ + (NLM3_nlm_stats_sz) +#define NLM3_nlm_res_sz \ + (NLM3_netobj_sz + NLM3_nlm_stat_sz) +#define NLM3_nlm_holder_sz \ + (XDR_bool + XDR_int + NLM3_netobj_sz + XDR_unsigned_int + XDR_unsigned_int) +#define NLM3_nlm_testrply_sz \ + (NLM3_nlm_stats_sz + NLM3_nlm_holder_sz) +#define NLM3_nlm_testres_sz \ + (NLM3_netobj_sz + NLM3_nlm_testrply_sz) +#define NLM3_nlm_lock_sz \ + (XDR_unsigned_int + XDR_QUADLEN(LM_MAXSTRLEN) + NLM3_netobj_sz + NLM3_netobj_sz + XDR_int + XDR_unsigned_int + XDR_unsigned_int) +#define NLM3_nlm_lockargs_sz \ + (NLM3_netobj_sz + XDR_bool + XDR_bool + NLM3_nlm_lock_sz + XDR_bool + XDR_int) +#define NLM3_nlm_cancargs_sz \ + (NLM3_netobj_sz + XDR_bool + XDR_bool + NLM3_nlm_lock_sz) +#define NLM3_nlm_testargs_sz \ + (NLM3_netobj_sz + XDR_bool + NLM3_nlm_lock_sz) +#define NLM3_nlm_unlockargs_sz \ + (NLM3_netobj_sz + NLM3_nlm_lock_sz) +#define NLM3_fsh_mode_sz (XDR_int) +#define NLM3_fsh_access_sz (XDR_int) +#define NLM3_nlm_share_sz \ + (XDR_unsigned_int + XDR_QUADLEN(LM_MAXSTRLEN) + NLM3_netobj_sz + NLM3_netobj_sz + NLM3_fsh_mode_sz + NLM3_fsh_access_sz) +#define NLM3_nlm_shareargs_sz \ + (NLM3_netobj_sz + NLM3_nlm_share_sz + XDR_bool) +#define NLM3_nlm_shareres_sz \ + (NLM3_netobj_sz + NLM3_nlm_stats_sz + XDR_int) +#define NLM3_nlm_notify_sz \ + (XDR_unsigned_int + XDR_QUADLEN(LM_MAXNAMELEN) + XDR_long) +#define NLM3_nlm_notifyargs_sz \ + (NLM3_nlm_notify_sz + XDR_QUADLEN(SM_PRIV_SIZE)) +#define NLM3_MAX_ARGS_SZ \ + (NLM3_nlm_lockargs_sz) + +#endif /* _LINUX_XDRGEN_NLM3_DEF_H */ diff --git a/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h index 97c7447f4d14..f5b75d5caba9 100644 --- a/include/uapi/linux/nfsd_netlink.h +++ b/include/uapi/linux/nfsd_netlink.h @@ -10,6 +10,53 @@ #define NFSD_FAMILY_NAME "nfsd" #define NFSD_FAMILY_VERSION 1 +enum nfsd_cache_type { + NFSD_CACHE_TYPE_SVC_EXPORT = 1, + NFSD_CACHE_TYPE_EXPKEY = 2, +}; + +/* + * These flags are ordered to match the NFSEXP_* flags in + * include/linux/nfsd/export.h + */ +enum nfsd_export_flags { + NFSD_EXPORT_FLAGS_READONLY = 1, + NFSD_EXPORT_FLAGS_INSECURE_PORT = 2, + NFSD_EXPORT_FLAGS_ROOTSQUASH = 4, + NFSD_EXPORT_FLAGS_ALLSQUASH = 8, + NFSD_EXPORT_FLAGS_ASYNC = 16, + NFSD_EXPORT_FLAGS_GATHERED_WRITES = 32, + NFSD_EXPORT_FLAGS_NOREADDIRPLUS = 64, + NFSD_EXPORT_FLAGS_SECURITY_LABEL = 128, + NFSD_EXPORT_FLAGS_SIGN_FH = 256, + NFSD_EXPORT_FLAGS_NOHIDE = 512, + NFSD_EXPORT_FLAGS_NOSUBTREECHECK = 1024, + NFSD_EXPORT_FLAGS_NOAUTHNLM = 2048, + NFSD_EXPORT_FLAGS_MSNFS = 4096, + NFSD_EXPORT_FLAGS_FSID = 8192, + NFSD_EXPORT_FLAGS_CROSSMOUNT = 16384, + NFSD_EXPORT_FLAGS_NOACL = 32768, + NFSD_EXPORT_FLAGS_V4ROOT = 65536, + NFSD_EXPORT_FLAGS_PNFS = 131072, +}; + +/* + * These flags are ordered to match the NFSEXP_XPRTSEC_* flags in + * include/linux/nfsd/export.h + */ +enum nfsd_xprtsec_mode { + NFSD_XPRTSEC_MODE_NONE = 1, + NFSD_XPRTSEC_MODE_TLS = 2, + NFSD_XPRTSEC_MODE_MTLS = 4, +}; + +enum { + NFSD_A_CACHE_NOTIFY_CACHE_TYPE = 1, + + __NFSD_A_CACHE_NOTIFY_MAX, + NFSD_A_CACHE_NOTIFY_MAX = (__NFSD_A_CACHE_NOTIFY_MAX - 1) +}; + enum { NFSD_A_RPC_STATUS_XID = 1, NFSD_A_RPC_STATUS_FLAGS, @@ -82,6 +129,103 @@ enum { }; enum { + NFSD_A_FSLOCATION_HOST = 1, + NFSD_A_FSLOCATION_PATH, + + __NFSD_A_FSLOCATION_MAX, + NFSD_A_FSLOCATION_MAX = (__NFSD_A_FSLOCATION_MAX - 1) +}; + +enum { + NFSD_A_FSLOCATIONS_LOCATION = 1, + + __NFSD_A_FSLOCATIONS_MAX, + NFSD_A_FSLOCATIONS_MAX = (__NFSD_A_FSLOCATIONS_MAX - 1) +}; + +enum { + NFSD_A_AUTH_FLAVOR_PSEUDOFLAVOR = 1, + NFSD_A_AUTH_FLAVOR_FLAGS, + + __NFSD_A_AUTH_FLAVOR_MAX, + NFSD_A_AUTH_FLAVOR_MAX = (__NFSD_A_AUTH_FLAVOR_MAX - 1) +}; + +enum { + NFSD_A_SVC_EXPORT_SEQNO = 1, + NFSD_A_SVC_EXPORT_CLIENT, + NFSD_A_SVC_EXPORT_PATH, + NFSD_A_SVC_EXPORT_NEGATIVE, + NFSD_A_SVC_EXPORT_EXPIRY, + NFSD_A_SVC_EXPORT_ANON_UID, + NFSD_A_SVC_EXPORT_ANON_GID, + NFSD_A_SVC_EXPORT_FSLOCATIONS, + NFSD_A_SVC_EXPORT_UUID, + NFSD_A_SVC_EXPORT_SECINFO, + NFSD_A_SVC_EXPORT_XPRTSEC, + NFSD_A_SVC_EXPORT_FLAGS, + NFSD_A_SVC_EXPORT_FSID, + + __NFSD_A_SVC_EXPORT_MAX, + NFSD_A_SVC_EXPORT_MAX = (__NFSD_A_SVC_EXPORT_MAX - 1) +}; + +enum { + NFSD_A_SVC_EXPORT_REQS_REQUESTS = 1, + + __NFSD_A_SVC_EXPORT_REQS_MAX, + NFSD_A_SVC_EXPORT_REQS_MAX = (__NFSD_A_SVC_EXPORT_REQS_MAX - 1) +}; + +enum { + NFSD_A_EXPKEY_SEQNO = 1, + NFSD_A_EXPKEY_CLIENT, + NFSD_A_EXPKEY_FSIDTYPE, + NFSD_A_EXPKEY_FSID, + NFSD_A_EXPKEY_NEGATIVE, + NFSD_A_EXPKEY_EXPIRY, + NFSD_A_EXPKEY_PATH, + + __NFSD_A_EXPKEY_MAX, + NFSD_A_EXPKEY_MAX = (__NFSD_A_EXPKEY_MAX - 1) +}; + +enum { + NFSD_A_EXPKEY_REQS_REQUESTS = 1, + + __NFSD_A_EXPKEY_REQS_MAX, + NFSD_A_EXPKEY_REQS_MAX = (__NFSD_A_EXPKEY_REQS_MAX - 1) +}; + +enum { + NFSD_A_CACHE_FLUSH_MASK = 1, + + __NFSD_A_CACHE_FLUSH_MAX, + NFSD_A_CACHE_FLUSH_MAX = (__NFSD_A_CACHE_FLUSH_MAX - 1) +}; + +enum { + NFSD_A_UNLOCK_IP_ADDRESS = 1, + + __NFSD_A_UNLOCK_IP_MAX, + NFSD_A_UNLOCK_IP_MAX = (__NFSD_A_UNLOCK_IP_MAX - 1) +}; + +enum { + NFSD_A_UNLOCK_FILESYSTEM_PATH = 1, + + __NFSD_A_UNLOCK_FILESYSTEM_MAX, + NFSD_A_UNLOCK_FILESYSTEM_MAX = (__NFSD_A_UNLOCK_FILESYSTEM_MAX - 1) +}; + +enum { + NFSD_A_UNLOCK_EXPORT_PATH = 1, + + __NFSD_A_UNLOCK_EXPORT_MAX, + NFSD_A_UNLOCK_EXPORT_MAX = (__NFSD_A_UNLOCK_EXPORT_MAX - 1) +}; + +enum { NFSD_CMD_RPC_STATUS_GET = 1, NFSD_CMD_THREADS_SET, NFSD_CMD_THREADS_GET, @@ -91,9 +235,21 @@ enum { NFSD_CMD_LISTENER_GET, NFSD_CMD_POOL_MODE_SET, NFSD_CMD_POOL_MODE_GET, + NFSD_CMD_CACHE_NOTIFY, + NFSD_CMD_SVC_EXPORT_GET_REQS, + NFSD_CMD_SVC_EXPORT_SET_REQS, + NFSD_CMD_EXPKEY_GET_REQS, + NFSD_CMD_EXPKEY_SET_REQS, + NFSD_CMD_CACHE_FLUSH, + NFSD_CMD_UNLOCK_IP, + NFSD_CMD_UNLOCK_FILESYSTEM, + NFSD_CMD_UNLOCK_EXPORT, __NFSD_CMD_MAX, NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1) }; +#define NFSD_MCGRP_NONE "none" +#define NFSD_MCGRP_EXPORTD "exportd" + #endif /* _UAPI_LINUX_NFSD_NETLINK_H */ diff --git a/include/uapi/linux/sunrpc_netlink.h b/include/uapi/linux/sunrpc_netlink.h new file mode 100644 index 000000000000..34677f0ec2f9 --- /dev/null +++ b/include/uapi/linux/sunrpc_netlink.h @@ -0,0 +1,84 @@ +/* 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 uapi header */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ + +#ifndef _UAPI_LINUX_SUNRPC_NETLINK_H +#define _UAPI_LINUX_SUNRPC_NETLINK_H + +#define SUNRPC_FAMILY_NAME "sunrpc" +#define SUNRPC_FAMILY_VERSION 1 + +enum sunrpc_cache_type { + SUNRPC_CACHE_TYPE_IP_MAP = 1, + SUNRPC_CACHE_TYPE_UNIX_GID = 2, +}; + +enum { + SUNRPC_A_CACHE_NOTIFY_CACHE_TYPE = 1, + + __SUNRPC_A_CACHE_NOTIFY_MAX, + SUNRPC_A_CACHE_NOTIFY_MAX = (__SUNRPC_A_CACHE_NOTIFY_MAX - 1) +}; + +enum { + SUNRPC_A_IP_MAP_SEQNO = 1, + SUNRPC_A_IP_MAP_CLASS, + SUNRPC_A_IP_MAP_ADDR, + SUNRPC_A_IP_MAP_DOMAIN, + SUNRPC_A_IP_MAP_NEGATIVE, + SUNRPC_A_IP_MAP_EXPIRY, + + __SUNRPC_A_IP_MAP_MAX, + SUNRPC_A_IP_MAP_MAX = (__SUNRPC_A_IP_MAP_MAX - 1) +}; + +enum { + SUNRPC_A_IP_MAP_REQS_REQUESTS = 1, + + __SUNRPC_A_IP_MAP_REQS_MAX, + SUNRPC_A_IP_MAP_REQS_MAX = (__SUNRPC_A_IP_MAP_REQS_MAX - 1) +}; + +enum { + SUNRPC_A_UNIX_GID_SEQNO = 1, + SUNRPC_A_UNIX_GID_UID, + SUNRPC_A_UNIX_GID_GIDS, + SUNRPC_A_UNIX_GID_NEGATIVE, + SUNRPC_A_UNIX_GID_EXPIRY, + + __SUNRPC_A_UNIX_GID_MAX, + SUNRPC_A_UNIX_GID_MAX = (__SUNRPC_A_UNIX_GID_MAX - 1) +}; + +enum { + SUNRPC_A_UNIX_GID_REQS_REQUESTS = 1, + + __SUNRPC_A_UNIX_GID_REQS_MAX, + SUNRPC_A_UNIX_GID_REQS_MAX = (__SUNRPC_A_UNIX_GID_REQS_MAX - 1) +}; + +enum { + SUNRPC_A_CACHE_FLUSH_MASK = 1, + + __SUNRPC_A_CACHE_FLUSH_MAX, + SUNRPC_A_CACHE_FLUSH_MAX = (__SUNRPC_A_CACHE_FLUSH_MAX - 1) +}; + +enum { + SUNRPC_CMD_CACHE_NOTIFY = 1, + SUNRPC_CMD_IP_MAP_GET_REQS, + SUNRPC_CMD_IP_MAP_SET_REQS, + SUNRPC_CMD_UNIX_GID_GET_REQS, + SUNRPC_CMD_UNIX_GID_SET_REQS, + SUNRPC_CMD_CACHE_FLUSH, + + __SUNRPC_CMD_MAX, + SUNRPC_CMD_MAX = (__SUNRPC_CMD_MAX - 1) +}; + +#define SUNRPC_MCGRP_NONE "none" +#define SUNRPC_MCGRP_EXPORTD "exportd" + +#endif /* _UAPI_LINUX_SUNRPC_NETLINK_H */ diff --git a/net/sunrpc/.kunitconfig b/net/sunrpc/.kunitconfig deleted file mode 100644 index eb02b906c295..000000000000 --- a/net/sunrpc/.kunitconfig +++ /dev/null @@ -1,29 +0,0 @@ -CONFIG_KUNIT=y -CONFIG_UBSAN=y -CONFIG_STACKTRACE=y -CONFIG_NET=y -CONFIG_NETWORK_FILESYSTEMS=y -CONFIG_INET=y -CONFIG_FILE_LOCKING=y -CONFIG_MULTIUSER=y -CONFIG_CRYPTO=y -CONFIG_CRYPTO_CBC=y -CONFIG_CRYPTO_CTS=y -CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_CMAC=y -CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_SHA1=y -CONFIG_CRYPTO_SHA256=y -CONFIG_CRYPTO_SHA512=y -CONFIG_CRYPTO_DES=y -CONFIG_CRYPTO_AES=y -CONFIG_CRYPTO_CAMELLIA=y -CONFIG_NFS_FS=y -CONFIG_SUNRPC=y -CONFIG_SUNRPC_GSS=y -CONFIG_RPCSEC_GSS_KRB5=y -CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1=y -CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA=y -CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2=y -CONFIG_RPCSEC_GSS_KRB5_KUNIT_TEST=y diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig index a570e7adf270..e7808e5714dc 100644 --- a/net/sunrpc/Kconfig +++ b/net/sunrpc/Kconfig @@ -21,8 +21,7 @@ config RPCSEC_GSS_KRB5 depends on SUNRPC && CRYPTO default y select SUNRPC_GSS - select CRYPTO_SKCIPHER - select CRYPTO_HASH + select CRYPTO_KRB5 help Choose Y here to enable Secure RPC using the Kerberos version 5 GSS-API mechanism (RFC 1964). @@ -34,59 +33,6 @@ config RPCSEC_GSS_KRB5 If unsure, say Y. -config RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1 - bool "Enable Kerberos enctypes based on AES and SHA-1" - depends on RPCSEC_GSS_KRB5 - depends on CRYPTO_CBC && CRYPTO_CTS - depends on CRYPTO_HMAC && CRYPTO_SHA1 - depends on CRYPTO_AES - default y - help - Choose Y to enable the use of Kerberos 5 encryption types - that utilize Advanced Encryption Standard (AES) ciphers and - SHA-1 digests. These include aes128-cts-hmac-sha1-96 and - aes256-cts-hmac-sha1-96. - -config RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA - bool "Enable Kerberos encryption types based on Camellia and CMAC" - depends on RPCSEC_GSS_KRB5 - depends on CRYPTO_CBC && CRYPTO_CTS && CRYPTO_CAMELLIA - depends on CRYPTO_CMAC - default n - help - Choose Y to enable the use of Kerberos 5 encryption types - that utilize Camellia ciphers (RFC 3713) and CMAC digests - (NIST Special Publication 800-38B). These include - camellia128-cts-cmac and camellia256-cts-cmac. - -config RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2 - bool "Enable Kerberos enctypes based on AES and SHA-2" - depends on RPCSEC_GSS_KRB5 - depends on CRYPTO_CBC && CRYPTO_CTS - depends on CRYPTO_HMAC && CRYPTO_SHA256 && CRYPTO_SHA512 - depends on CRYPTO_AES - default n - help - Choose Y to enable the use of Kerberos 5 encryption types - that utilize Advanced Encryption Standard (AES) ciphers and - SHA-2 digests. These include aes128-cts-hmac-sha256-128 and - aes256-cts-hmac-sha384-192. - -config RPCSEC_GSS_KRB5_KUNIT_TEST - tristate "KUnit tests for RPCSEC GSS Kerberos" if !KUNIT_ALL_TESTS - depends on RPCSEC_GSS_KRB5 && KUNIT - default KUNIT_ALL_TESTS - help - This builds the KUnit tests for RPCSEC GSS Kerberos 5. - - KUnit tests run during boot and output the results to the debug - log in TAP format (https://testanything.org/). Only useful for - kernel devs running KUnit test harness and are not for inclusion - into a production build. - - For more information on KUnit and unit tests in general, refer - to the KUnit documentation in Documentation/dev-tools/kunit/. - config SUNRPC_DEBUG bool "RPC: Enable dprintk debugging" depends on SUNRPC && SYSCTL diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile index f89c10fe7e6a..96727df3aa85 100644 --- a/net/sunrpc/Makefile +++ b/net/sunrpc/Makefile @@ -14,7 +14,7 @@ sunrpc-y := clnt.o xprt.o socklib.o xprtsock.o sched.o \ addr.o rpcb_clnt.o timer.o xdr.o \ sunrpc_syms.o cache.o rpc_pipe.o sysfs.o \ svc_xprt.o \ - xprtmultipath.o + xprtmultipath.o netlink.o sunrpc-$(CONFIG_SUNRPC_DEBUG) += debugfs.o sunrpc-$(CONFIG_SUNRPC_BACKCHANNEL) += backchannel_rqst.o sunrpc-$(CONFIG_PROC_FS) += stats.o diff --git a/net/sunrpc/auth_gss/Makefile b/net/sunrpc/auth_gss/Makefile index 452f67deebc6..68676389d65f 100644 --- a/net/sunrpc/auth_gss/Makefile +++ b/net/sunrpc/auth_gss/Makefile @@ -12,6 +12,4 @@ auth_rpcgss-y := auth_gss.o \ obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o rpcsec_gss_krb5-y := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \ - gss_krb5_wrap.o gss_krb5_crypto.o gss_krb5_keys.o - -obj-$(CONFIG_RPCSEC_GSS_KRB5_KUNIT_TEST) += gss_krb5_test.o + gss_krb5_wrap.o gss_krb5_crypto.o diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c index 16dcf115de1e..cf461ebcdde5 100644 --- a/net/sunrpc/auth_gss/gss_krb5_crypto.c +++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c @@ -34,19 +34,14 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#include <crypto/hash.h> -#include <crypto/skcipher.h> -#include <crypto/utils.h> #include <linux/err.h> #include <linux/types.h> #include <linux/mm.h> #include <linux/scatterlist.h> #include <linux/highmem.h> #include <linux/pagemap.h> -#include <linux/random.h> #include <linux/sunrpc/gss_krb5.h> #include <linux/sunrpc/xdr.h> -#include <kunit/visibility.h> #include "gss_krb5_internal.h" @@ -54,303 +49,6 @@ # define RPCDBG_FACILITY RPCDBG_AUTH #endif -/** - * krb5_make_confounder - Generate a confounder string - * @p: memory location into which to write the string - * @conflen: string length to write, in octets - * - * RFCs 1964 and 3961 mention only "a random confounder" without going - * into detail about its function or cryptographic requirements. The - * assumed purpose is to prevent repeated encryption of a plaintext with - * the same key from generating the same ciphertext. It is also used to - * pad minimum plaintext length to at least a single cipher block. - * - * However, in situations like the GSS Kerberos 5 mechanism, where the - * encryption IV is always all zeroes, the confounder also effectively - * functions like an IV. Thus, not only must it be unique from message - * to message, but it must also be difficult to predict. Otherwise an - * attacker can correlate the confounder to previous or future values, - * making the encryption easier to break. - * - * Given that the primary consumer of this encryption mechanism is a - * network storage protocol, a type of traffic that often carries - * predictable payloads (eg, all zeroes when reading unallocated blocks - * from a file), our confounder generation has to be cryptographically - * strong. - */ -void krb5_make_confounder(u8 *p, int conflen) -{ - get_random_bytes(p, conflen); -} - -/** - * krb5_encrypt - simple encryption of an RPCSEC GSS payload - * @tfm: initialized cipher transform - * @iv: pointer to an IV - * @in: plaintext to encrypt - * @out: OUT: ciphertext - * @length: length of input and output buffers, in bytes - * - * @iv may be NULL to force the use of an all-zero IV. - * The buffer containing the IV must be as large as the - * cipher's ivsize. - * - * Return values: - * %0: @in successfully encrypted into @out - * negative errno: @in not encrypted - */ -u32 -krb5_encrypt( - struct crypto_sync_skcipher *tfm, - void * iv, - void * in, - void * out, - int length) -{ - u32 ret = -EINVAL; - struct scatterlist sg[1]; - u8 local_iv[GSS_KRB5_MAX_BLOCKSIZE] = {0}; - SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm); - - if (length % crypto_sync_skcipher_blocksize(tfm) != 0) - goto out; - - if (crypto_sync_skcipher_ivsize(tfm) > GSS_KRB5_MAX_BLOCKSIZE) { - dprintk("RPC: gss_k5encrypt: tfm iv size too large %d\n", - crypto_sync_skcipher_ivsize(tfm)); - goto out; - } - - if (iv) - memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm)); - - memcpy(out, in, length); - sg_init_one(sg, out, length); - - skcipher_request_set_sync_tfm(req, tfm); - skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, sg, sg, length, local_iv); - - ret = crypto_skcipher_encrypt(req); - skcipher_request_zero(req); -out: - dprintk("RPC: krb5_encrypt returns %d\n", ret); - return ret; -} - -static int -checksummer(struct scatterlist *sg, void *data) -{ - struct ahash_request *req = data; - - ahash_request_set_crypt(req, sg, NULL, sg->length); - - return crypto_ahash_update(req); -} - -/** - * gss_krb5_checksum - Compute the MAC for a GSS Wrap or MIC token - * @tfm: an initialized hash transform - * @header: pointer to a buffer containing the token header, or NULL - * @hdrlen: number of octets in @header - * @body: xdr_buf containing an RPC message (body.len is the message length) - * @body_offset: byte offset into @body to start checksumming - * @cksumout: OUT: a buffer to be filled in with the computed HMAC - * - * Usually expressed as H = HMAC(K, message)[1..h] . - * - * Caller provides the truncation length of the output token (h) in - * cksumout.len. - * - * Return values: - * %GSS_S_COMPLETE: Digest computed, @cksumout filled in - * %GSS_S_FAILURE: Call failed - */ -u32 -gss_krb5_checksum(struct crypto_ahash *tfm, char *header, int hdrlen, - const struct xdr_buf *body, int body_offset, - struct xdr_netobj *cksumout) -{ - struct ahash_request *req; - int err = -ENOMEM; - u8 *checksumdata; - - checksumdata = kmalloc(crypto_ahash_digestsize(tfm), GFP_KERNEL); - if (!checksumdata) - return GSS_S_FAILURE; - - req = ahash_request_alloc(tfm, GFP_KERNEL); - if (!req) - goto out_free_cksum; - ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); - err = crypto_ahash_init(req); - if (err) - goto out_free_ahash; - - /* - * Per RFC 4121 Section 4.2.4, the checksum is performed over the - * data body first, then over the octets in "header". - */ - err = xdr_process_buf(body, body_offset, body->len - body_offset, - checksummer, req); - if (err) - goto out_free_ahash; - if (header) { - struct scatterlist sg[1]; - - sg_init_one(sg, header, hdrlen); - ahash_request_set_crypt(req, sg, NULL, hdrlen); - err = crypto_ahash_update(req); - if (err) - goto out_free_ahash; - } - - ahash_request_set_crypt(req, NULL, checksumdata, 0); - err = crypto_ahash_final(req); - if (err) - goto out_free_ahash; - - memcpy(cksumout->data, checksumdata, - min_t(int, cksumout->len, crypto_ahash_digestsize(tfm))); - -out_free_ahash: - ahash_request_free(req); -out_free_cksum: - kfree_sensitive(checksumdata); - return err ? GSS_S_FAILURE : GSS_S_COMPLETE; -} -EXPORT_SYMBOL_IF_KUNIT(gss_krb5_checksum); - -struct encryptor_desc { - u8 iv[GSS_KRB5_MAX_BLOCKSIZE]; - struct skcipher_request *req; - int pos; - struct xdr_buf *outbuf; - struct page **pages; - struct scatterlist infrags[4]; - struct scatterlist outfrags[4]; - int fragno; - int fraglen; -}; - -static int -encryptor(struct scatterlist *sg, void *data) -{ - struct encryptor_desc *desc = data; - struct xdr_buf *outbuf = desc->outbuf; - struct crypto_sync_skcipher *tfm = - crypto_sync_skcipher_reqtfm(desc->req); - struct page *in_page; - int thislen = desc->fraglen + sg->length; - int fraglen, ret; - int page_pos; - - /* Worst case is 4 fragments: head, end of page 1, start - * of page 2, tail. Anything more is a bug. */ - BUG_ON(desc->fragno > 3); - - page_pos = desc->pos - outbuf->head[0].iov_len; - if (page_pos >= 0 && page_pos < outbuf->page_len) { - /* pages are not in place: */ - int i = (page_pos + outbuf->page_base) >> PAGE_SHIFT; - in_page = desc->pages[i]; - } else { - in_page = sg_page(sg); - } - sg_set_page(&desc->infrags[desc->fragno], in_page, sg->length, - sg->offset); - sg_set_page(&desc->outfrags[desc->fragno], sg_page(sg), sg->length, - sg->offset); - desc->fragno++; - desc->fraglen += sg->length; - desc->pos += sg->length; - - fraglen = thislen & (crypto_sync_skcipher_blocksize(tfm) - 1); - thislen -= fraglen; - - if (thislen == 0) - return 0; - - sg_mark_end(&desc->infrags[desc->fragno - 1]); - sg_mark_end(&desc->outfrags[desc->fragno - 1]); - - skcipher_request_set_crypt(desc->req, desc->infrags, desc->outfrags, - thislen, desc->iv); - - ret = crypto_skcipher_encrypt(desc->req); - if (ret) - return ret; - - sg_init_table(desc->infrags, 4); - sg_init_table(desc->outfrags, 4); - - if (fraglen) { - sg_set_page(&desc->outfrags[0], sg_page(sg), fraglen, - sg->offset + sg->length - fraglen); - desc->infrags[0] = desc->outfrags[0]; - sg_assign_page(&desc->infrags[0], in_page); - desc->fragno = 1; - desc->fraglen = fraglen; - } else { - desc->fragno = 0; - desc->fraglen = 0; - } - return 0; -} - -struct decryptor_desc { - u8 iv[GSS_KRB5_MAX_BLOCKSIZE]; - struct skcipher_request *req; - struct scatterlist frags[4]; - int fragno; - int fraglen; -}; - -static int -decryptor(struct scatterlist *sg, void *data) -{ - struct decryptor_desc *desc = data; - int thislen = desc->fraglen + sg->length; - struct crypto_sync_skcipher *tfm = - crypto_sync_skcipher_reqtfm(desc->req); - int fraglen, ret; - - /* Worst case is 4 fragments: head, end of page 1, start - * of page 2, tail. Anything more is a bug. */ - BUG_ON(desc->fragno > 3); - sg_set_page(&desc->frags[desc->fragno], sg_page(sg), sg->length, - sg->offset); - desc->fragno++; - desc->fraglen += sg->length; - - fraglen = thislen & (crypto_sync_skcipher_blocksize(tfm) - 1); - thislen -= fraglen; - - if (thislen == 0) - return 0; - - sg_mark_end(&desc->frags[desc->fragno - 1]); - - skcipher_request_set_crypt(desc->req, desc->frags, desc->frags, - thislen, desc->iv); - - ret = crypto_skcipher_decrypt(desc->req); - if (ret) - return ret; - - sg_init_table(desc->frags, 4); - - if (fraglen) { - sg_set_page(&desc->frags[0], sg_page(sg), fraglen, - sg->offset + sg->length - fraglen); - desc->fragno = 1; - desc->fraglen = fraglen; - } else { - desc->fragno = 0; - desc->fraglen = 0; - } - return 0; -} /* * This function makes the assumption that it was ultimately called @@ -363,7 +61,7 @@ decryptor(struct scatterlist *sg, void *data) * * Even with that guarantee, this function may be called more than * once in the processing of gss_wrap(). The best we can do is - * verify at compile-time (see GSS_KRB5_SLACK_CHECK) that the + * verify at compile-time (see GSS_KRB5_MAX_SLACK_NEEDED) that the * largest expected shift will fit within RPC_MAX_AUTH_SIZE. * At run-time we can verify that a single invocation of this * function doesn't attempt to use more the RPC_MAX_AUTH_SIZE. @@ -389,567 +87,235 @@ xdr_extend_head(struct xdr_buf *buf, unsigned int base, unsigned int shiftlen) return 0; } -static u32 -gss_krb5_cts_crypt(struct crypto_sync_skcipher *cipher, struct xdr_buf *buf, - u32 offset, u8 *iv, struct page **pages, int encrypt) -{ - u32 ret; - struct scatterlist sg[1]; - SYNC_SKCIPHER_REQUEST_ON_STACK(req, cipher); - u8 *data; - struct page **save_pages; - u32 len = buf->len - offset; - - if (len > GSS_KRB5_MAX_BLOCKSIZE * 2) { - WARN_ON(0); - return -ENOMEM; - } - data = kmalloc(GSS_KRB5_MAX_BLOCKSIZE * 2, GFP_KERNEL); - if (!data) - return -ENOMEM; - - /* - * For encryption, we want to read from the cleartext - * page cache pages, and write the encrypted data to - * the supplied xdr_buf pages. - */ - save_pages = buf->pages; - if (encrypt) - buf->pages = pages; - - ret = read_bytes_from_xdr_buf(buf, offset, data, len); - buf->pages = save_pages; - if (ret) - goto out; - - sg_init_one(sg, data, len); - - skcipher_request_set_sync_tfm(req, cipher); - skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, sg, sg, len, iv); - - if (encrypt) - ret = crypto_skcipher_encrypt(req); - else - ret = crypto_skcipher_decrypt(req); - - skcipher_request_zero(req); - - if (ret) - goto out; - - ret = write_bytes_to_xdr_buf(buf, offset, data, len); - -#if IS_ENABLED(CONFIG_KUNIT) - /* - * CBC-CTS does not define an output IV but RFC 3962 defines it as the - * penultimate block of ciphertext, so copy that into the IV buffer - * before returning. - */ - if (encrypt) - memcpy(iv, data, crypto_sync_skcipher_ivsize(cipher)); -#endif - -out: - kfree(data); - return ret; -} /** - * krb5_cbc_cts_encrypt - encrypt in CBC mode with CTS - * @cts_tfm: CBC cipher with CTS - * @cbc_tfm: base CBC cipher - * @offset: starting byte offset for plaintext - * @buf: OUT: output buffer - * @pages: plaintext - * @iv: output CBC initialization vector, or NULL - * @ivsize: size of @iv, in octets + * 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 8769e9e705bf..3b392e96f25d 100644 --- a/net/sunrpc/auth_gss/gss_krb5_internal.h +++ b/net/sunrpc/auth_gss/gss_krb5_internal.h @@ -8,51 +8,10 @@ #ifndef _NET_SUNRPC_AUTH_GSS_KRB5_INTERNAL_H #define _NET_SUNRPC_AUTH_GSS_KRB5_INTERNAL_H -/* - * The RFCs often specify payload lengths in bits. This helper - * converts a specified bit-length to the number of octets/bytes. - */ -#define BITS2OCTETS(x) ((x) / 8) +#include <crypto/krb5.h> struct krb5_ctx; -struct gss_krb5_enctype { - const u32 etype; /* encryption (key) type */ - const u32 ctype; /* checksum type */ - const char *name; /* "friendly" name */ - const char *encrypt_name; /* crypto encrypt name */ - const char *aux_cipher; /* aux encrypt cipher name */ - const char *cksum_name; /* crypto checksum name */ - const u16 signalg; /* signing algorithm */ - const u16 sealalg; /* sealing algorithm */ - const u32 cksumlength; /* checksum length */ - const u32 keyed_cksum; /* is it a keyed cksum? */ - const u32 keybytes; /* raw key len, in bytes */ - const u32 keylength; /* protocol key length, in octets */ - const u32 Kc_length; /* checksum subkey length, in octets */ - const u32 Ke_length; /* encryption subkey length, in octets */ - const u32 Ki_length; /* integrity subkey length, in octets */ - - int (*derive_key)(const struct gss_krb5_enctype *gk5e, - const struct xdr_netobj *in, - struct xdr_netobj *out, - const struct xdr_netobj *label, - gfp_t gfp_mask); - u32 (*encrypt)(struct krb5_ctx *kctx, u32 offset, - struct xdr_buf *buf, struct page **pages); - u32 (*decrypt)(struct krb5_ctx *kctx, u32 offset, u32 len, - struct xdr_buf *buf, u32 *headskip, u32 *tailskip); - u32 (*get_mic)(struct krb5_ctx *kctx, struct xdr_buf *text, - struct xdr_netobj *token); - u32 (*verify_mic)(struct krb5_ctx *kctx, struct xdr_buf *message_buffer, - struct xdr_netobj *read_token); - u32 (*wrap)(struct krb5_ctx *kctx, int offset, - struct xdr_buf *buf, struct page **pages); - u32 (*unwrap)(struct krb5_ctx *kctx, int offset, int len, - struct xdr_buf *buf, unsigned int *slack, - unsigned int *align); -}; - /* krb5_ctx flags definitions */ #define KRB5_CTX_FLAG_INITIATOR 0x00000001 #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 @@ -61,20 +20,12 @@ struct krb5_ctx { int initiate; /* 1 = initiating, 0 = accepting */ u32 enctype; u32 flags; - const struct gss_krb5_enctype *gk5e; /* enctype-specific info */ - struct crypto_sync_skcipher *enc; - struct crypto_sync_skcipher *seq; - struct crypto_sync_skcipher *acceptor_enc; - struct crypto_sync_skcipher *initiator_enc; - struct crypto_sync_skcipher *acceptor_enc_aux; - struct crypto_sync_skcipher *initiator_enc_aux; - struct crypto_ahash *acceptor_sign; - struct crypto_ahash *initiator_sign; - struct crypto_ahash *initiator_integ; - struct crypto_ahash *acceptor_integ; + const struct krb5_enctype *krb5e; /* crypto/krb5 enctype */ + struct crypto_aead *initiator_enc_aead; + struct crypto_aead *acceptor_enc_aead; + struct crypto_shash *initiator_sign_shash; + struct crypto_shash *acceptor_sign_shash; u8 Ksess[GSS_KRB5_MAX_KEYLEN]; /* session key */ - u8 cksum[GSS_KRB5_MAX_KEYLEN]; - atomic_t seq_send; atomic64_t seq_send64; time64_t endtime; struct xdr_netobj mech_used; @@ -101,95 +52,21 @@ u32 gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len, * Implementation internal functions */ -/* Key Derivation Functions */ - -int krb5_derive_key_v2(const struct gss_krb5_enctype *gk5e, - const struct xdr_netobj *inkey, - struct xdr_netobj *outkey, - const struct xdr_netobj *label, - gfp_t gfp_mask); - -int krb5_kdf_hmac_sha2(const struct gss_krb5_enctype *gk5e, - const struct xdr_netobj *inkey, - struct xdr_netobj *outkey, - const struct xdr_netobj *in_constant, - gfp_t gfp_mask); - -int krb5_kdf_feedback_cmac(const struct gss_krb5_enctype *gk5e, - const struct xdr_netobj *inkey, - struct xdr_netobj *outkey, - const struct xdr_netobj *in_constant, - gfp_t gfp_mask); - -/** - * krb5_derive_key - Derive a subkey from a protocol key - * @kctx: Kerberos 5 context - * @inkey: base protocol key - * @outkey: OUT: derived key - * @usage: key usage value - * @seed: key usage seed (one octet) - * @gfp_mask: memory allocation control flags - * - * Caller sets @outkey->len to the desired length of the derived key. - * - * On success, returns 0 and fills in @outkey. A negative errno value - * is returned on failure. - */ -static inline int krb5_derive_key(struct krb5_ctx *kctx, - const struct xdr_netobj *inkey, - struct xdr_netobj *outkey, - u32 usage, u8 seed, gfp_t gfp_mask) -{ - const struct gss_krb5_enctype *gk5e = kctx->gk5e; - u8 label_data[GSS_KRB5_K5CLENGTH]; - struct xdr_netobj label = { - .len = sizeof(label_data), - .data = label_data, - }; - __be32 *p = (__be32 *)label_data; - - *p = cpu_to_be32(usage); - label_data[4] = seed; - return gk5e->derive_key(gk5e, inkey, outkey, &label, gfp_mask); -} - -void krb5_make_confounder(u8 *p, int conflen); - -u32 gss_krb5_checksum(struct crypto_ahash *tfm, char *header, int hdrlen, - const struct xdr_buf *body, int body_offset, - struct xdr_netobj *cksumout); - -u32 krb5_encrypt(struct crypto_sync_skcipher *key, void *iv, void *in, - void *out, int length); - int xdr_extend_head(struct xdr_buf *buf, unsigned int base, unsigned int shiftlen); -u32 gss_krb5_aes_encrypt(struct krb5_ctx *kctx, u32 offset, - struct xdr_buf *buf, struct page **pages); - -u32 gss_krb5_aes_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len, - struct xdr_buf *buf, u32 *plainoffset, u32 *plainlen); +u32 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 4eb19c3a54c7..000000000000 --- a/net/sunrpc/auth_gss/gss_krb5_keys.c +++ /dev/null @@ -1,546 +0,0 @@ -/* - * COPYRIGHT (c) 2008 - * The Regents of the University of Michigan - * ALL RIGHTS RESERVED - * - * Permission is granted to use, copy, create derivative works - * and redistribute this software and such derivative works - * for any purpose, so long as the name of The University of - * Michigan is not used in any advertising or publicity - * pertaining to the use of distribution of this software - * without specific, written prior authorization. If the - * above copyright notice or any other identification of the - * University of Michigan is included in any copy of any - * portion of this software, then the disclaimer below must - * also be included. - * - * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION - * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY - * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF - * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING - * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE - * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE - * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR - * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING - * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN - * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGES. - */ - -/* - * Copyright (C) 1998 by the FundsXpress, INC. - * - * All rights reserved. - * - * Export of this software from the United States of America may require - * a specific license from the United States Government. It is the - * responsibility of any person or organization contemplating export to - * obtain such a license before exporting. - * - * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and - * distribute this software and its documentation for any purpose and - * without fee is hereby granted, provided that the above copyright - * notice appear in all copies and that both that copyright notice and - * this permission notice appear in supporting documentation, and that - * the name of FundsXpress. not be used in advertising or publicity pertaining - * to distribution of the software without specific, written prior - * permission. FundsXpress makes no representations about the suitability of - * this software for any purpose. It is provided "as is" without express - * or implied warranty. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -#include <crypto/skcipher.h> -#include <linux/err.h> -#include <linux/types.h> -#include <linux/sunrpc/gss_krb5.h> -#include <linux/sunrpc/xdr.h> -#include <linux/lcm.h> -#include <crypto/hash.h> -#include <kunit/visibility.h> - -#include "gss_krb5_internal.h" - -#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) -# define RPCDBG_FACILITY RPCDBG_AUTH -#endif - -/** - * krb5_nfold - n-fold function - * @inbits: number of bits in @in - * @in: buffer containing input to fold - * @outbits: number of bits in the output buffer - * @out: buffer to hold the result - * - * This is the n-fold function as described in rfc3961, sec 5.1 - * Taken from MIT Kerberos and modified. - */ -VISIBLE_IF_KUNIT -void krb5_nfold(u32 inbits, const u8 *in, u32 outbits, u8 *out) -{ - unsigned long ulcm; - int byte, i, msbit; - - /* the code below is more readable if I make these bytes - instead of bits */ - - inbits >>= 3; - outbits >>= 3; - - /* first compute lcm(n,k) */ - ulcm = lcm(inbits, outbits); - - /* now do the real work */ - - memset(out, 0, outbits); - byte = 0; - - /* this will end up cycling through k lcm(k,n)/k times, which - is correct */ - for (i = ulcm-1; i >= 0; i--) { - /* compute the msbit in k which gets added into this byte */ - msbit = ( - /* first, start with the msbit in the first, - * unrotated byte */ - ((inbits << 3) - 1) - /* then, for each byte, shift to the right - * for each repetition */ - + (((inbits << 3) + 13) * (i/inbits)) - /* last, pick out the correct byte within - * that shifted repetition */ - + ((inbits - (i % inbits)) << 3) - ) % (inbits << 3); - - /* pull out the byte value itself */ - byte += (((in[((inbits - 1) - (msbit >> 3)) % inbits] << 8)| - (in[((inbits) - (msbit >> 3)) % inbits])) - >> ((msbit & 7) + 1)) & 0xff; - - /* do the addition */ - byte += out[i % outbits]; - out[i % outbits] = byte & 0xff; - - /* keep around the carry bit, if any */ - byte >>= 8; - - } - - /* if there's a carry bit left over, add it back in */ - if (byte) { - for (i = outbits - 1; i >= 0; i--) { - /* do the addition */ - byte += out[i]; - out[i] = byte & 0xff; - - /* keep around the carry bit, if any */ - byte >>= 8; - } - } -} -EXPORT_SYMBOL_IF_KUNIT(krb5_nfold); - -/* - * This is the DK (derive_key) function as described in rfc3961, sec 5.1 - * Taken from MIT Kerberos and modified. - */ -static int krb5_DK(const struct gss_krb5_enctype *gk5e, - const struct xdr_netobj *inkey, u8 *rawkey, - const struct xdr_netobj *in_constant, gfp_t gfp_mask) -{ - size_t blocksize, keybytes, keylength, n; - unsigned char *inblockdata, *outblockdata; - struct xdr_netobj inblock, outblock; - struct crypto_sync_skcipher *cipher; - int ret = -EINVAL; - - keybytes = gk5e->keybytes; - keylength = gk5e->keylength; - - if (inkey->len != keylength) - goto err_return; - - cipher = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0); - if (IS_ERR(cipher)) - goto err_return; - blocksize = crypto_sync_skcipher_blocksize(cipher); - if (crypto_sync_skcipher_setkey(cipher, inkey->data, inkey->len)) - goto err_free_cipher; - - ret = -ENOMEM; - inblockdata = kmalloc(blocksize, gfp_mask); - if (inblockdata == NULL) - goto err_free_cipher; - - outblockdata = kmalloc(blocksize, gfp_mask); - if (outblockdata == NULL) - goto err_free_in; - - inblock.data = (char *) inblockdata; - inblock.len = blocksize; - - outblock.data = (char *) outblockdata; - outblock.len = blocksize; - - /* initialize the input block */ - - if (in_constant->len == inblock.len) { - memcpy(inblock.data, in_constant->data, inblock.len); - } else { - krb5_nfold(in_constant->len * 8, in_constant->data, - inblock.len * 8, inblock.data); - } - - /* loop encrypting the blocks until enough key bytes are generated */ - - n = 0; - while (n < keybytes) { - krb5_encrypt(cipher, NULL, inblock.data, outblock.data, - inblock.len); - - if ((keybytes - n) <= outblock.len) { - memcpy(rawkey + n, outblock.data, (keybytes - n)); - break; - } - - memcpy(rawkey + n, outblock.data, outblock.len); - memcpy(inblock.data, outblock.data, outblock.len); - n += outblock.len; - } - - ret = 0; - - kfree_sensitive(outblockdata); -err_free_in: - kfree_sensitive(inblockdata); -err_free_cipher: - crypto_free_sync_skcipher(cipher); -err_return: - return ret; -} - -/* - * This is the identity function, with some sanity checking. - */ -static int krb5_random_to_key_v2(const struct gss_krb5_enctype *gk5e, - struct xdr_netobj *randombits, - struct xdr_netobj *key) -{ - int ret = -EINVAL; - - if (key->len != 16 && key->len != 32) { - dprintk("%s: key->len is %d\n", __func__, key->len); - goto err_out; - } - if (randombits->len != 16 && randombits->len != 32) { - dprintk("%s: randombits->len is %d\n", - __func__, randombits->len); - goto err_out; - } - if (randombits->len != key->len) { - dprintk("%s: randombits->len is %d, key->len is %d\n", - __func__, randombits->len, key->len); - goto err_out; - } - memcpy(key->data, randombits->data, key->len); - ret = 0; -err_out: - return ret; -} - -/** - * krb5_derive_key_v2 - Derive a subkey for an RFC 3962 enctype - * @gk5e: Kerberos 5 enctype profile - * @inkey: base protocol key - * @outkey: OUT: derived key - * @label: subkey usage label - * @gfp_mask: memory allocation control flags - * - * Caller sets @outkey->len to the desired length of the derived key. - * - * On success, returns 0 and fills in @outkey. A negative errno value - * is returned on failure. - */ -int krb5_derive_key_v2(const struct gss_krb5_enctype *gk5e, - const struct xdr_netobj *inkey, - struct xdr_netobj *outkey, - const struct xdr_netobj *label, - gfp_t gfp_mask) -{ - struct xdr_netobj inblock; - int ret; - - inblock.len = gk5e->keybytes; - inblock.data = kmalloc(inblock.len, gfp_mask); - if (!inblock.data) - return -ENOMEM; - - ret = krb5_DK(gk5e, inkey, inblock.data, label, gfp_mask); - if (!ret) - ret = krb5_random_to_key_v2(gk5e, &inblock, outkey); - - kfree_sensitive(inblock.data); - return ret; -} - -/* - * K(i) = CMAC(key, K(i-1) | i | constant | 0x00 | k) - * - * i: A block counter is used with a length of 4 bytes, represented - * in big-endian order. - * - * constant: The label input to the KDF is the usage constant supplied - * to the key derivation function - * - * k: The length of the output key in bits, represented as a 4-byte - * string in big-endian order. - * - * Caller fills in K(i-1) in @step, and receives the result K(i) - * in the same buffer. - */ -static int -krb5_cmac_Ki(struct crypto_shash *tfm, const struct xdr_netobj *constant, - u32 outlen, u32 count, struct xdr_netobj *step) -{ - __be32 k = cpu_to_be32(outlen * 8); - SHASH_DESC_ON_STACK(desc, tfm); - __be32 i = cpu_to_be32(count); - u8 zero = 0; - int ret; - - desc->tfm = tfm; - ret = crypto_shash_init(desc); - if (ret) - goto out_err; - - ret = crypto_shash_update(desc, step->data, step->len); - if (ret) - goto out_err; - ret = crypto_shash_update(desc, (u8 *)&i, sizeof(i)); - if (ret) - goto out_err; - ret = crypto_shash_update(desc, constant->data, constant->len); - if (ret) - goto out_err; - ret = crypto_shash_update(desc, &zero, sizeof(zero)); - if (ret) - goto out_err; - ret = crypto_shash_update(desc, (u8 *)&k, sizeof(k)); - if (ret) - goto out_err; - ret = crypto_shash_final(desc, step->data); - if (ret) - goto out_err; - -out_err: - shash_desc_zero(desc); - return ret; -} - -/** - * krb5_kdf_feedback_cmac - Derive a subkey for a Camellia/CMAC-based enctype - * @gk5e: Kerberos 5 enctype parameters - * @inkey: base protocol key - * @outkey: OUT: derived key - * @constant: subkey usage label - * @gfp_mask: memory allocation control flags - * - * RFC 6803 Section 3: - * - * "We use a key derivation function from the family specified in - * [SP800-108], Section 5.2, 'KDF in Feedback Mode'." - * - * n = ceiling(k / 128) - * K(0) = zeros - * K(i) = CMAC(key, K(i-1) | i | constant | 0x00 | k) - * DR(key, constant) = k-truncate(K(1) | K(2) | ... | K(n)) - * KDF-FEEDBACK-CMAC(key, constant) = random-to-key(DR(key, constant)) - * - * Caller sets @outkey->len to the desired length of the derived key (k). - * - * On success, returns 0 and fills in @outkey. A negative errno value - * is returned on failure. - */ -int -krb5_kdf_feedback_cmac(const struct gss_krb5_enctype *gk5e, - const struct xdr_netobj *inkey, - struct xdr_netobj *outkey, - const struct xdr_netobj *constant, - gfp_t gfp_mask) -{ - struct xdr_netobj step = { .data = NULL }; - struct xdr_netobj DR = { .data = NULL }; - unsigned int blocksize, offset; - struct crypto_shash *tfm; - int n, count, ret; - - /* - * This implementation assumes the CMAC used for an enctype's - * key derivation is the same as the CMAC used for its - * checksumming. This happens to be true for enctypes that - * are currently supported by this implementation. - */ - tfm = crypto_alloc_shash(gk5e->cksum_name, 0, 0); - if (IS_ERR(tfm)) { - ret = PTR_ERR(tfm); - goto out; - } - ret = crypto_shash_setkey(tfm, inkey->data, inkey->len); - if (ret) - goto out_free_tfm; - - blocksize = crypto_shash_digestsize(tfm); - n = (outkey->len + blocksize - 1) / blocksize; - - /* K(0) is all zeroes */ - ret = -ENOMEM; - step.len = blocksize; - step.data = kzalloc(step.len, gfp_mask); - if (!step.data) - goto out_free_tfm; - - DR.len = blocksize * n; - DR.data = kmalloc(DR.len, gfp_mask); - if (!DR.data) - goto out_free_tfm; - - /* XXX: Does not handle partial-block key sizes */ - for (offset = 0, count = 1; count <= n; count++) { - ret = krb5_cmac_Ki(tfm, constant, outkey->len, count, &step); - if (ret) - goto out_free_tfm; - - memcpy(DR.data + offset, step.data, blocksize); - offset += blocksize; - } - - /* k-truncate and random-to-key */ - memcpy(outkey->data, DR.data, outkey->len); - ret = 0; - -out_free_tfm: - crypto_free_shash(tfm); -out: - kfree_sensitive(step.data); - kfree_sensitive(DR.data); - return ret; -} - -/* - * K1 = HMAC-SHA(key, 0x00000001 | label | 0x00 | k) - * - * key: The source of entropy from which subsequent keys are derived. - * - * label: An octet string describing the intended usage of the - * derived key. - * - * k: Length in bits of the key to be outputted, expressed in - * big-endian binary representation in 4 bytes. - */ -static int -krb5_hmac_K1(struct crypto_shash *tfm, const struct xdr_netobj *label, - u32 outlen, struct xdr_netobj *K1) -{ - __be32 k = cpu_to_be32(outlen * 8); - SHASH_DESC_ON_STACK(desc, tfm); - __be32 one = cpu_to_be32(1); - u8 zero = 0; - int ret; - - desc->tfm = tfm; - ret = crypto_shash_init(desc); - if (ret) - goto out_err; - ret = crypto_shash_update(desc, (u8 *)&one, sizeof(one)); - if (ret) - goto out_err; - ret = crypto_shash_update(desc, label->data, label->len); - if (ret) - goto out_err; - ret = crypto_shash_update(desc, &zero, sizeof(zero)); - if (ret) - goto out_err; - ret = crypto_shash_update(desc, (u8 *)&k, sizeof(k)); - if (ret) - goto out_err; - ret = crypto_shash_final(desc, K1->data); - if (ret) - goto out_err; - -out_err: - shash_desc_zero(desc); - return ret; -} - -/** - * krb5_kdf_hmac_sha2 - Derive a subkey for an AES/SHA2-based enctype - * @gk5e: Kerberos 5 enctype policy parameters - * @inkey: base protocol key - * @outkey: OUT: derived key - * @label: subkey usage label - * @gfp_mask: memory allocation control flags - * - * RFC 8009 Section 3: - * - * "We use a key derivation function from Section 5.1 of [SP800-108], - * which uses the HMAC algorithm as the PRF." - * - * function KDF-HMAC-SHA2(key, label, [context,] k): - * k-truncate(K1) - * - * Caller sets @outkey->len to the desired length of the derived key. - * - * On success, returns 0 and fills in @outkey. A negative errno value - * is returned on failure. - */ -int -krb5_kdf_hmac_sha2(const struct gss_krb5_enctype *gk5e, - const struct xdr_netobj *inkey, - struct xdr_netobj *outkey, - const struct xdr_netobj *label, - gfp_t gfp_mask) -{ - struct crypto_shash *tfm; - struct xdr_netobj K1 = { - .data = NULL, - }; - int ret; - - /* - * This implementation assumes the HMAC used for an enctype's - * key derivation is the same as the HMAC used for its - * checksumming. This happens to be true for enctypes that - * are currently supported by this implementation. - */ - tfm = crypto_alloc_shash(gk5e->cksum_name, 0, 0); - if (IS_ERR(tfm)) { - ret = PTR_ERR(tfm); - goto out; - } - ret = crypto_shash_setkey(tfm, inkey->data, inkey->len); - if (ret) - goto out_free_tfm; - - K1.len = crypto_shash_digestsize(tfm); - K1.data = kmalloc(K1.len, gfp_mask); - if (!K1.data) { - ret = -ENOMEM; - goto out_free_tfm; - } - - ret = krb5_hmac_K1(tfm, label, outkey->len, &K1); - if (ret) - goto out_free_tfm; - - /* k-truncate and random-to-key */ - memcpy(outkey->data, K1.data, outkey->len); - -out_free_tfm: - kfree_sensitive(K1.data); - crypto_free_shash(tfm); -out: - return ret; -} diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index 6db64a9111a9..c41b5f3e1789 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -9,8 +9,6 @@ * J. Bruce Fields <bfields@umich.edu> */ -#include <crypto/hash.h> -#include <crypto/skcipher.h> #include <linux/err.h> #include <linux/module.h> #include <linux/init.h> @@ -19,7 +17,6 @@ #include <linux/sunrpc/auth.h> #include <linux/sunrpc/gss_krb5.h> #include <linux/sunrpc/xdr.h> -#include <kunit/visibility.h> #include "auth_gss_internal.h" #include "gss_krb5_internal.h" @@ -30,201 +27,24 @@ static struct gss_api_mech gss_kerberos_mech; -static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = { -#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1) - /* - * AES-128 with SHA-1 (RFC 3962) - */ - { - .etype = ENCTYPE_AES128_CTS_HMAC_SHA1_96, - .ctype = CKSUMTYPE_HMAC_SHA1_96_AES128, - .name = "aes128-cts", - .encrypt_name = "cts(cbc(aes))", - .aux_cipher = "cbc(aes)", - .cksum_name = "hmac(sha1)", - .derive_key = krb5_derive_key_v2, - .encrypt = gss_krb5_aes_encrypt, - .decrypt = gss_krb5_aes_decrypt, - - .get_mic = gss_krb5_get_mic_v2, - .verify_mic = gss_krb5_verify_mic_v2, - .wrap = gss_krb5_wrap_v2, - .unwrap = gss_krb5_unwrap_v2, - - .signalg = -1, - .sealalg = -1, - .keybytes = 16, - .keylength = BITS2OCTETS(128), - .Kc_length = BITS2OCTETS(128), - .Ke_length = BITS2OCTETS(128), - .Ki_length = BITS2OCTETS(128), - .cksumlength = BITS2OCTETS(96), - .keyed_cksum = 1, - }, - /* - * AES-256 with SHA-1 (RFC 3962) - */ - { - .etype = ENCTYPE_AES256_CTS_HMAC_SHA1_96, - .ctype = CKSUMTYPE_HMAC_SHA1_96_AES256, - .name = "aes256-cts", - .encrypt_name = "cts(cbc(aes))", - .aux_cipher = "cbc(aes)", - .cksum_name = "hmac(sha1)", - .derive_key = krb5_derive_key_v2, - .encrypt = gss_krb5_aes_encrypt, - .decrypt = gss_krb5_aes_decrypt, - - .get_mic = gss_krb5_get_mic_v2, - .verify_mic = gss_krb5_verify_mic_v2, - .wrap = gss_krb5_wrap_v2, - .unwrap = gss_krb5_unwrap_v2, - - .signalg = -1, - .sealalg = -1, - .keybytes = 32, - .keylength = BITS2OCTETS(256), - .Kc_length = BITS2OCTETS(256), - .Ke_length = BITS2OCTETS(256), - .Ki_length = BITS2OCTETS(256), - .cksumlength = BITS2OCTETS(96), - .keyed_cksum = 1, - }, -#endif - -#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA) - /* - * Camellia-128 with CMAC (RFC 6803) - */ - { - .etype = ENCTYPE_CAMELLIA128_CTS_CMAC, - .ctype = CKSUMTYPE_CMAC_CAMELLIA128, - .name = "camellia128-cts-cmac", - .encrypt_name = "cts(cbc(camellia))", - .aux_cipher = "cbc(camellia)", - .cksum_name = "cmac(camellia)", - .cksumlength = BITS2OCTETS(128), - .keyed_cksum = 1, - .keylength = BITS2OCTETS(128), - .Kc_length = BITS2OCTETS(128), - .Ke_length = BITS2OCTETS(128), - .Ki_length = BITS2OCTETS(128), - - .derive_key = krb5_kdf_feedback_cmac, - .encrypt = gss_krb5_aes_encrypt, - .decrypt = gss_krb5_aes_decrypt, - - .get_mic = gss_krb5_get_mic_v2, - .verify_mic = gss_krb5_verify_mic_v2, - .wrap = gss_krb5_wrap_v2, - .unwrap = gss_krb5_unwrap_v2, - }, - /* - * Camellia-256 with CMAC (RFC 6803) - */ - { - .etype = ENCTYPE_CAMELLIA256_CTS_CMAC, - .ctype = CKSUMTYPE_CMAC_CAMELLIA256, - .name = "camellia256-cts-cmac", - .encrypt_name = "cts(cbc(camellia))", - .aux_cipher = "cbc(camellia)", - .cksum_name = "cmac(camellia)", - .cksumlength = BITS2OCTETS(128), - .keyed_cksum = 1, - .keylength = BITS2OCTETS(256), - .Kc_length = BITS2OCTETS(256), - .Ke_length = BITS2OCTETS(256), - .Ki_length = BITS2OCTETS(256), - - .derive_key = krb5_kdf_feedback_cmac, - .encrypt = gss_krb5_aes_encrypt, - .decrypt = gss_krb5_aes_decrypt, - - .get_mic = gss_krb5_get_mic_v2, - .verify_mic = gss_krb5_verify_mic_v2, - .wrap = gss_krb5_wrap_v2, - .unwrap = gss_krb5_unwrap_v2, - }, -#endif - -#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2) - /* - * AES-128 with SHA-256 (RFC 8009) - */ - { - .etype = ENCTYPE_AES128_CTS_HMAC_SHA256_128, - .ctype = CKSUMTYPE_HMAC_SHA256_128_AES128, - .name = "aes128-cts-hmac-sha256-128", - .encrypt_name = "cts(cbc(aes))", - .aux_cipher = "cbc(aes)", - .cksum_name = "hmac(sha256)", - .cksumlength = BITS2OCTETS(128), - .keyed_cksum = 1, - .keylength = BITS2OCTETS(128), - .Kc_length = BITS2OCTETS(128), - .Ke_length = BITS2OCTETS(128), - .Ki_length = BITS2OCTETS(128), - - .derive_key = krb5_kdf_hmac_sha2, - .encrypt = krb5_etm_encrypt, - .decrypt = krb5_etm_decrypt, - - .get_mic = gss_krb5_get_mic_v2, - .verify_mic = gss_krb5_verify_mic_v2, - .wrap = gss_krb5_wrap_v2, - .unwrap = gss_krb5_unwrap_v2, - }, - /* - * AES-256 with SHA-384 (RFC 8009) - */ - { - .etype = ENCTYPE_AES256_CTS_HMAC_SHA384_192, - .ctype = CKSUMTYPE_HMAC_SHA384_192_AES256, - .name = "aes256-cts-hmac-sha384-192", - .encrypt_name = "cts(cbc(aes))", - .aux_cipher = "cbc(aes)", - .cksum_name = "hmac(sha384)", - .cksumlength = BITS2OCTETS(192), - .keyed_cksum = 1, - .keylength = BITS2OCTETS(256), - .Kc_length = BITS2OCTETS(192), - .Ke_length = BITS2OCTETS(256), - .Ki_length = BITS2OCTETS(192), - - .derive_key = krb5_kdf_hmac_sha2, - .encrypt = krb5_etm_encrypt, - .decrypt = krb5_etm_decrypt, - - .get_mic = gss_krb5_get_mic_v2, - .verify_mic = gss_krb5_verify_mic_v2, - .wrap = gss_krb5_wrap_v2, - .unwrap = gss_krb5_unwrap_v2, - }, -#endif -}; - /* - * The list of advertised enctypes is specified in order of most - * preferred to least. + * Candidate enctypes in order of most preferred to least. + * Each is probed against crypto/krb5 at module init; only + * enctypes that crypto/krb5 supports are advertised. */ +static const u32 gss_krb5_enctypes[] = { + KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192, + KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128, + KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC, + KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC, + KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96, + KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96, +}; + static char gss_krb5_enctype_priority_list[64]; static void gss_krb5_prepare_enctype_priority_list(void) { - static const u32 gss_krb5_enctypes[] = { -#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2) - ENCTYPE_AES256_CTS_HMAC_SHA384_192, - ENCTYPE_AES128_CTS_HMAC_SHA256_128, -#endif -#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA) - ENCTYPE_CAMELLIA256_CTS_CMAC, - ENCTYPE_CAMELLIA128_CTS_CMAC, -#endif -#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1) - ENCTYPE_AES256_CTS_HMAC_SHA1_96, - ENCTYPE_AES128_CTS_HMAC_SHA1_96, -#endif - }; size_t total, i; char buf[16]; char *sep; @@ -233,6 +53,8 @@ static void gss_krb5_prepare_enctype_priority_list(void) sep = ""; gss_krb5_enctype_priority_list[0] = '\0'; for (total = 0, i = 0; i < ARRAY_SIZE(gss_krb5_enctypes); i++) { + if (!crypto_krb5_find_enctype(gss_krb5_enctypes[i])) + continue; n = sprintf(buf, "%s%u", sep, gss_krb5_enctypes[i]); if (n < 0) break; @@ -244,151 +66,56 @@ static void gss_krb5_prepare_enctype_priority_list(void) } } -/** - * gss_krb5_lookup_enctype - Retrieve profile information for a given enctype - * @etype: ENCTYPE value - * - * Returns a pointer to a gss_krb5_enctype structure, or NULL if no - * matching etype is found. - */ -VISIBLE_IF_KUNIT -const struct gss_krb5_enctype *gss_krb5_lookup_enctype(u32 etype) -{ - size_t i; - - for (i = 0; i < ARRAY_SIZE(supported_gss_krb5_enctypes); i++) - if (supported_gss_krb5_enctypes[i].etype == etype) - return &supported_gss_krb5_enctypes[i]; - return NULL; -} -EXPORT_SYMBOL_IF_KUNIT(gss_krb5_lookup_enctype); - -static struct crypto_sync_skcipher * -gss_krb5_alloc_cipher_v2(const char *cname, const struct xdr_netobj *key) -{ - struct crypto_sync_skcipher *tfm; - - tfm = crypto_alloc_sync_skcipher(cname, 0, 0); - if (IS_ERR(tfm)) - return NULL; - if (crypto_sync_skcipher_setkey(tfm, key->data, key->len)) { - crypto_free_sync_skcipher(tfm); - return NULL; - } - return tfm; -} - -static struct crypto_ahash * -gss_krb5_alloc_hash_v2(struct krb5_ctx *kctx, const struct xdr_netobj *key) -{ - struct crypto_ahash *tfm; - - tfm = crypto_alloc_ahash(kctx->gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(tfm)) - return NULL; - if (crypto_ahash_setkey(tfm, key->data, key->len)) { - crypto_free_ahash(tfm); - return NULL; - } - return tfm; -} - static int gss_krb5_import_ctx_v2(struct krb5_ctx *ctx, gfp_t gfp_mask) { - struct xdr_netobj keyin = { - .len = ctx->gk5e->keylength, + struct krb5_buffer TK = { + .len = ctx->krb5e->key_len, .data = ctx->Ksess, }; - struct xdr_netobj keyout; - int ret = -EINVAL; - - keyout.data = kmalloc(GSS_KRB5_MAX_KEYLEN, gfp_mask); - if (!keyout.data) - return -ENOMEM; - - /* initiator seal encryption */ - keyout.len = ctx->gk5e->Ke_length; - if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_INITIATOR_SEAL, - KEY_USAGE_SEED_ENCRYPTION, gfp_mask)) - goto out; - ctx->initiator_enc = gss_krb5_alloc_cipher_v2(ctx->gk5e->encrypt_name, - &keyout); - if (ctx->initiator_enc == NULL) - goto out; - if (ctx->gk5e->aux_cipher) { - ctx->initiator_enc_aux = - gss_krb5_alloc_cipher_v2(ctx->gk5e->aux_cipher, - &keyout); - if (ctx->initiator_enc_aux == NULL) - goto out_free; - } + int ret; - /* acceptor seal encryption */ - if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_ACCEPTOR_SEAL, - KEY_USAGE_SEED_ENCRYPTION, gfp_mask)) + ctx->initiator_enc_aead = + crypto_krb5_prepare_encryption(ctx->krb5e, &TK, + KG_USAGE_INITIATOR_SEAL, + gfp_mask); + if (IS_ERR(ctx->initiator_enc_aead)) { + ret = PTR_ERR(ctx->initiator_enc_aead); goto out_free; - ctx->acceptor_enc = gss_krb5_alloc_cipher_v2(ctx->gk5e->encrypt_name, - &keyout); - if (ctx->acceptor_enc == NULL) - goto out_free; - if (ctx->gk5e->aux_cipher) { - ctx->acceptor_enc_aux = - gss_krb5_alloc_cipher_v2(ctx->gk5e->aux_cipher, - &keyout); - if (ctx->acceptor_enc_aux == NULL) - goto out_free; } - - /* initiator sign checksum */ - keyout.len = ctx->gk5e->Kc_length; - if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_INITIATOR_SIGN, - KEY_USAGE_SEED_CHECKSUM, gfp_mask)) + ctx->acceptor_enc_aead = + crypto_krb5_prepare_encryption(ctx->krb5e, &TK, + KG_USAGE_ACCEPTOR_SEAL, + gfp_mask); + if (IS_ERR(ctx->acceptor_enc_aead)) { + ret = PTR_ERR(ctx->acceptor_enc_aead); goto out_free; - ctx->initiator_sign = gss_krb5_alloc_hash_v2(ctx, &keyout); - if (ctx->initiator_sign == NULL) - goto out_free; - - /* acceptor sign checksum */ - if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_ACCEPTOR_SIGN, - KEY_USAGE_SEED_CHECKSUM, gfp_mask)) - goto out_free; - ctx->acceptor_sign = gss_krb5_alloc_hash_v2(ctx, &keyout); - if (ctx->acceptor_sign == NULL) - goto out_free; - - /* initiator seal integrity */ - keyout.len = ctx->gk5e->Ki_length; - if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_INITIATOR_SEAL, - KEY_USAGE_SEED_INTEGRITY, gfp_mask)) - goto out_free; - ctx->initiator_integ = gss_krb5_alloc_hash_v2(ctx, &keyout); - if (ctx->initiator_integ == NULL) - goto out_free; - - /* acceptor seal integrity */ - if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_ACCEPTOR_SEAL, - KEY_USAGE_SEED_INTEGRITY, gfp_mask)) + } + ctx->initiator_sign_shash = + crypto_krb5_prepare_checksum(ctx->krb5e, &TK, + KG_USAGE_INITIATOR_SIGN, + gfp_mask); + if (IS_ERR(ctx->initiator_sign_shash)) { + ret = PTR_ERR(ctx->initiator_sign_shash); goto out_free; - ctx->acceptor_integ = gss_krb5_alloc_hash_v2(ctx, &keyout); - if (ctx->acceptor_integ == NULL) + } + ctx->acceptor_sign_shash = + crypto_krb5_prepare_checksum(ctx->krb5e, &TK, + KG_USAGE_ACCEPTOR_SIGN, + gfp_mask); + if (IS_ERR(ctx->acceptor_sign_shash)) { + ret = PTR_ERR(ctx->acceptor_sign_shash); goto out_free; + } - ret = 0; -out: - kfree_sensitive(keyout.data); - return ret; + return 0; out_free: - crypto_free_ahash(ctx->acceptor_integ); - crypto_free_ahash(ctx->initiator_integ); - crypto_free_ahash(ctx->acceptor_sign); - crypto_free_ahash(ctx->initiator_sign); - crypto_free_sync_skcipher(ctx->acceptor_enc_aux); - crypto_free_sync_skcipher(ctx->acceptor_enc); - crypto_free_sync_skcipher(ctx->initiator_enc_aux); - crypto_free_sync_skcipher(ctx->initiator_enc); - goto out; + crypto_free_shash(ctx->acceptor_sign_shash); + crypto_free_shash(ctx->initiator_sign_shash); + crypto_free_aead(ctx->acceptor_enc_aead); + crypto_free_aead(ctx->initiator_enc_aead); + return ret; } static int @@ -414,25 +141,17 @@ gss_import_v2_context(const void *p, const void *end, struct krb5_ctx *ctx, if (IS_ERR(p)) goto out_err; atomic64_set(&ctx->seq_send64, seq_send64); - /* set seq_send for use by "older" enctypes */ - atomic_set(&ctx->seq_send, seq_send64); - if (seq_send64 != atomic_read(&ctx->seq_send)) { - dprintk("%s: seq_send64 %llx, seq_send %x overflow?\n", __func__, - seq_send64, atomic_read(&ctx->seq_send)); - p = ERR_PTR(-EINVAL); - goto out_err; - } p = simple_get_bytes(p, end, &ctx->enctype, sizeof(ctx->enctype)); if (IS_ERR(p)) goto out_err; - ctx->gk5e = gss_krb5_lookup_enctype(ctx->enctype); - if (ctx->gk5e == NULL) { + ctx->krb5e = crypto_krb5_find_enctype(ctx->enctype); + if (!ctx->krb5e) { dprintk("gss_kerberos_mech: unsupported krb5 enctype %u\n", ctx->enctype); p = ERR_PTR(-EINVAL); goto out_err; } - keylen = ctx->gk5e->keylength; + keylen = ctx->krb5e->key_len; p = simple_get_bytes(p, end, ctx->Ksess, keylen); if (IS_ERR(p)) @@ -495,21 +214,39 @@ gss_krb5_delete_sec_context(void *internal_ctx) { struct krb5_ctx *kctx = internal_ctx; - crypto_free_sync_skcipher(kctx->seq); - crypto_free_sync_skcipher(kctx->enc); - crypto_free_sync_skcipher(kctx->acceptor_enc); - crypto_free_sync_skcipher(kctx->initiator_enc); - crypto_free_sync_skcipher(kctx->acceptor_enc_aux); - crypto_free_sync_skcipher(kctx->initiator_enc_aux); - crypto_free_ahash(kctx->acceptor_sign); - crypto_free_ahash(kctx->initiator_sign); - crypto_free_ahash(kctx->acceptor_integ); - crypto_free_ahash(kctx->initiator_integ); + crypto_free_shash(kctx->acceptor_sign_shash); + crypto_free_shash(kctx->initiator_sign_shash); + crypto_free_aead(kctx->acceptor_enc_aead); + crypto_free_aead(kctx->initiator_enc_aead); kfree(kctx->mech_used.data); kfree(kctx); } /** + * gss_krb5_errno_to_status - Map a negative errno to a GSS major status + * @err: negative errno value, or zero + * + * Returns: + * %GSS_S_COMPLETE if @err is zero + * %GSS_S_BAD_SIG if @err is -EBADMSG (integrity check failure) + * %GSS_S_DEFECTIVE_TOKEN if @err is -EPROTO (malformed token) + * %GSS_S_FAILURE for all other negative values + */ +u32 gss_krb5_errno_to_status(int err) +{ + switch (err) { + case 0: + return GSS_S_COMPLETE; + case -EBADMSG: + return GSS_S_BAD_SIG; + case -EPROTO: + return GSS_S_DEFECTIVE_TOKEN; + default: + return GSS_S_FAILURE; + } +} + +/** * gss_krb5_get_mic - get_mic for the Kerberos GSS mechanism * @gctx: GSS context * @text: plaintext to checksum @@ -525,7 +262,7 @@ static u32 gss_krb5_get_mic(struct gss_ctx *gctx, struct xdr_buf *text, { struct krb5_ctx *kctx = gctx->internal_ctx_id; - return kctx->gk5e->get_mic(kctx, text, token); + return gss_krb5_get_mic_v2(kctx, text, token); } /** @@ -547,7 +284,7 @@ static u32 gss_krb5_verify_mic(struct gss_ctx *gctx, { struct krb5_ctx *kctx = gctx->internal_ctx_id; - return kctx->gk5e->verify_mic(kctx, message_buffer, read_token); + return gss_krb5_verify_mic_v2(kctx, message_buffer, read_token); } /** @@ -567,7 +304,7 @@ static u32 gss_krb5_wrap(struct gss_ctx *gctx, int offset, { struct krb5_ctx *kctx = gctx->internal_ctx_id; - return kctx->gk5e->wrap(kctx, offset, buf, pages); + return gss_krb5_wrap_v2(kctx, offset, buf, pages); } /** @@ -589,7 +326,7 @@ static u32 gss_krb5_unwrap(struct gss_ctx *gctx, int offset, { struct krb5_ctx *kctx = gctx->internal_ctx_id; - return kctx->gk5e->unwrap(kctx, offset, len, buf, + return gss_krb5_unwrap_v2(kctx, offset, len, buf, &gctx->slack, &gctx->align); } diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c index ce540df9bce4..cfe066e89f23 100644 --- a/net/sunrpc/auth_gss/gss_krb5_seal.c +++ b/net/sunrpc/auth_gss/gss_krb5_seal.c @@ -61,9 +61,9 @@ #include <linux/types.h> #include <linux/jiffies.h> #include <linux/sunrpc/gss_krb5.h> -#include <linux/random.h> -#include <linux/crypto.h> #include <linux/atomic.h> +#include <linux/scatterlist.h> +#include <linux/slab.h> #include "gss_krb5_internal.h" @@ -78,10 +78,10 @@ setup_token_v2(struct krb5_ctx *ctx, struct xdr_netobj *token) void *krb5_hdr; u8 *p, flags = 0x00; - if ((ctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0) - flags |= 0x01; + if (!ctx->initiate) + flags |= KG2_TOKEN_FLAG_SENTBYACCEPTOR; if (ctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY) - flags |= 0x04; + flags |= KG2_TOKEN_FLAG_ACCEPTORSUBKEY; /* Per rfc 4121, sec 4.2.6.1, there is no header, * just start the token. @@ -97,7 +97,7 @@ setup_token_v2(struct krb5_ctx *ctx, struct xdr_netobj *token) *ptr++ = 0xffff; *ptr = 0xffff; - token->len = GSS_KRB5_TOK_HDR_LEN + ctx->gk5e->cksumlength; + token->len = GSS_KRB5_TOK_HDR_LEN + ctx->krb5e->cksum_len; return krb5_hdr; } @@ -105,14 +105,17 @@ u32 gss_krb5_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text, struct xdr_netobj *token) { - struct crypto_ahash *tfm = ctx->initiate ? - ctx->initiator_sign : ctx->acceptor_sign; - struct xdr_netobj cksumobj = { - .len = ctx->gk5e->cksumlength, - }; + const struct krb5_enctype *krb5 = ctx->krb5e; + struct crypto_shash *shash = ctx->initiate ? + ctx->initiator_sign_shash : ctx->acceptor_sign_shash; + unsigned int cksum_len = krb5->cksum_len; + struct scatterlist sg_head[XDR_BUF_TO_SG_NENTS]; + struct scatterlist *sg_overflow; __be64 seq_send_be64; void *krb5_hdr; time64_t now; + ssize_t ret; + int nsg; dprintk("RPC: %s\n", __func__); @@ -123,9 +126,25 @@ gss_krb5_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text, seq_send_be64 = cpu_to_be64(atomic64_fetch_inc(&ctx->seq_send64)); memcpy(krb5_hdr + 8, (char *) &seq_send_be64, 8); - cksumobj.data = krb5_hdr + GSS_KRB5_TOK_HDR_LEN; - if (gss_krb5_checksum(tfm, krb5_hdr, GSS_KRB5_TOK_HDR_LEN, - text, 0, &cksumobj)) + /* + * The checksum is written directly into the token buffer. + * This is safe: crypto_krb5_get_mic uses shash (software + * hash), so the scatterlist is never DMA-mapped. + */ + nsg = gss_krb5_mic_build_sg(text, + krb5_hdr + GSS_KRB5_TOK_HDR_LEN, + cksum_len, krb5_hdr, + sg_head, &sg_overflow); + if (nsg < 0) + return GSS_S_FAILURE; + + ret = crypto_krb5_get_mic(krb5, shash, NULL, sg_head, nsg, + cksum_len + text->len + + GSS_KRB5_TOK_HDR_LEN, + cksum_len, + text->len + GSS_KRB5_TOK_HDR_LEN); + kfree(sg_overflow); + if (ret < 0) return GSS_S_FAILURE; now = ktime_get_real_seconds(); diff --git a/net/sunrpc/auth_gss/gss_krb5_test.c b/net/sunrpc/auth_gss/gss_krb5_test.c deleted file mode 100644 index dde1ee934d0d..000000000000 --- a/net/sunrpc/auth_gss/gss_krb5_test.c +++ /dev/null @@ -1,1868 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2022 Oracle and/or its affiliates. - * - * KUnit test of SunRPC's GSS Kerberos mechanism. Subsystem - * name is "rpcsec_gss_krb5". - */ - -#include <kunit/test.h> -#include <kunit/visibility.h> - -#include <linux/kernel.h> -#include <crypto/hash.h> - -#include <linux/sunrpc/xdr.h> -#include <linux/sunrpc/gss_krb5.h> - -#include "gss_krb5_internal.h" - -MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); - -struct gss_krb5_test_param { - const char *desc; - u32 enctype; - u32 nfold; - u32 constant; - const struct xdr_netobj *base_key; - const struct xdr_netobj *Ke; - const struct xdr_netobj *usage; - const struct xdr_netobj *plaintext; - const struct xdr_netobj *confounder; - const struct xdr_netobj *expected_result; - const struct xdr_netobj *expected_hmac; - const struct xdr_netobj *next_iv; -}; - -static inline void gss_krb5_get_desc(const struct gss_krb5_test_param *param, - char *desc) -{ - strscpy(desc, param->desc, KUNIT_PARAM_DESC_SIZE); -} - -static void kdf_case(struct kunit *test) -{ - const struct gss_krb5_test_param *param = test->param_value; - const struct gss_krb5_enctype *gk5e; - struct xdr_netobj derivedkey; - int err; - - /* Arrange */ - gk5e = gss_krb5_lookup_enctype(param->enctype); - if (!gk5e) - kunit_skip(test, "Encryption type is not available"); - - derivedkey.data = kunit_kzalloc(test, param->expected_result->len, - GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, derivedkey.data); - derivedkey.len = param->expected_result->len; - - /* Act */ - err = gk5e->derive_key(gk5e, param->base_key, &derivedkey, - param->usage, GFP_KERNEL); - KUNIT_ASSERT_EQ(test, err, 0); - - /* Assert */ - KUNIT_EXPECT_MEMEQ_MSG(test, - param->expected_result->data, - derivedkey.data, - derivedkey.len, - "key mismatch"); -} - -static void checksum_case(struct kunit *test) -{ - const struct gss_krb5_test_param *param = test->param_value; - struct xdr_buf buf = { - .head[0].iov_len = param->plaintext->len, - .len = param->plaintext->len, - }; - const struct gss_krb5_enctype *gk5e; - struct xdr_netobj Kc, checksum; - struct crypto_ahash *tfm; - int err; - - /* Arrange */ - gk5e = gss_krb5_lookup_enctype(param->enctype); - if (!gk5e) - kunit_skip(test, "Encryption type is not available"); - - Kc.len = gk5e->Kc_length; - Kc.data = kunit_kzalloc(test, Kc.len, GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, Kc.data); - err = gk5e->derive_key(gk5e, param->base_key, &Kc, - param->usage, GFP_KERNEL); - KUNIT_ASSERT_EQ(test, err, 0); - - tfm = crypto_alloc_ahash(gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, tfm); - err = crypto_ahash_setkey(tfm, Kc.data, Kc.len); - KUNIT_ASSERT_EQ(test, err, 0); - - buf.head[0].iov_base = kunit_kzalloc(test, buf.head[0].iov_len, GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf.head[0].iov_base); - memcpy(buf.head[0].iov_base, param->plaintext->data, buf.head[0].iov_len); - - checksum.len = gk5e->cksumlength; - checksum.data = kunit_kzalloc(test, checksum.len, GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, checksum.data); - - /* Act */ - err = gss_krb5_checksum(tfm, NULL, 0, &buf, 0, &checksum); - KUNIT_ASSERT_EQ(test, err, 0); - - /* Assert */ - KUNIT_EXPECT_MEMEQ_MSG(test, - param->expected_result->data, - checksum.data, - checksum.len, - "checksum mismatch"); - - crypto_free_ahash(tfm); -} - -#define DEFINE_HEX_XDR_NETOBJ(name, hex_array...) \ - static const u8 name ## _data[] = { hex_array }; \ - static const struct xdr_netobj name = { \ - .data = (u8 *)name##_data, \ - .len = sizeof(name##_data), \ - } - -#define DEFINE_STR_XDR_NETOBJ(name, string) \ - static const u8 name ## _str[] = string; \ - static const struct xdr_netobj name = { \ - .data = (u8 *)name##_str, \ - .len = sizeof(name##_str) - 1, \ - } - -/* - * RFC 3961 Appendix A.1. n-fold - * - * The n-fold function is defined in section 5.1 of RFC 3961. - * - * This test material is copyright (C) The Internet Society (2005). - */ - -DEFINE_HEX_XDR_NETOBJ(nfold_test1_plaintext, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35 -); -DEFINE_HEX_XDR_NETOBJ(nfold_test1_expected_result, - 0xbe, 0x07, 0x26, 0x31, 0x27, 0x6b, 0x19, 0x55 -); - -DEFINE_HEX_XDR_NETOBJ(nfold_test2_plaintext, - 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64 -); -DEFINE_HEX_XDR_NETOBJ(nfold_test2_expected_result, - 0x78, 0xa0, 0x7b, 0x6c, 0xaf, 0x85, 0xfa -); - -DEFINE_HEX_XDR_NETOBJ(nfold_test3_plaintext, - 0x52, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x43, 0x6f, - 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2c, - 0x20, 0x61, 0x6e, 0x64, 0x20, 0x52, 0x75, 0x6e, - 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x43, 0x6f, 0x64, - 0x65 -); -DEFINE_HEX_XDR_NETOBJ(nfold_test3_expected_result, - 0xbb, 0x6e, 0xd3, 0x08, 0x70, 0xb7, 0xf0, 0xe0 -); - -DEFINE_HEX_XDR_NETOBJ(nfold_test4_plaintext, - 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64 -); -DEFINE_HEX_XDR_NETOBJ(nfold_test4_expected_result, - 0x59, 0xe4, 0xa8, 0xca, 0x7c, 0x03, 0x85, 0xc3, - 0xc3, 0x7b, 0x3f, 0x6d, 0x20, 0x00, 0x24, 0x7c, - 0xb6, 0xe6, 0xbd, 0x5b, 0x3e -); - -DEFINE_HEX_XDR_NETOBJ(nfold_test5_plaintext, - 0x4d, 0x41, 0x53, 0x53, 0x41, 0x43, 0x48, 0x56, - 0x53, 0x45, 0x54, 0x54, 0x53, 0x20, 0x49, 0x4e, - 0x53, 0x54, 0x49, 0x54, 0x56, 0x54, 0x45, 0x20, - 0x4f, 0x46, 0x20, 0x54, 0x45, 0x43, 0x48, 0x4e, - 0x4f, 0x4c, 0x4f, 0x47, 0x59 -); -DEFINE_HEX_XDR_NETOBJ(nfold_test5_expected_result, - 0xdb, 0x3b, 0x0d, 0x8f, 0x0b, 0x06, 0x1e, 0x60, - 0x32, 0x82, 0xb3, 0x08, 0xa5, 0x08, 0x41, 0x22, - 0x9a, 0xd7, 0x98, 0xfa, 0xb9, 0x54, 0x0c, 0x1b -); - -DEFINE_HEX_XDR_NETOBJ(nfold_test6_plaintext, - 0x51 -); -DEFINE_HEX_XDR_NETOBJ(nfold_test6_expected_result, - 0x51, 0x8a, 0x54, 0xa2, 0x15, 0xa8, 0x45, 0x2a, - 0x51, 0x8a, 0x54, 0xa2, 0x15, 0xa8, 0x45, 0x2a, - 0x51, 0x8a, 0x54, 0xa2, 0x15 -); - -DEFINE_HEX_XDR_NETOBJ(nfold_test7_plaintext, - 0x62, 0x61 -); -DEFINE_HEX_XDR_NETOBJ(nfold_test7_expected_result, - 0xfb, 0x25, 0xd5, 0x31, 0xae, 0x89, 0x74, 0x49, - 0x9f, 0x52, 0xfd, 0x92, 0xea, 0x98, 0x57, 0xc4, - 0xba, 0x24, 0xcf, 0x29, 0x7e -); - -DEFINE_HEX_XDR_NETOBJ(nfold_test_kerberos, - 0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73 -); -DEFINE_HEX_XDR_NETOBJ(nfold_test8_expected_result, - 0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73 -); -DEFINE_HEX_XDR_NETOBJ(nfold_test9_expected_result, - 0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73, - 0x7b, 0x9b, 0x5b, 0x2b, 0x93, 0x13, 0x2b, 0x93 -); -DEFINE_HEX_XDR_NETOBJ(nfold_test10_expected_result, - 0x83, 0x72, 0xc2, 0x36, 0x34, 0x4e, 0x5f, 0x15, - 0x50, 0xcd, 0x07, 0x47, 0xe1, 0x5d, 0x62, 0xca, - 0x7a, 0x5a, 0x3b, 0xce, 0xa4 -); -DEFINE_HEX_XDR_NETOBJ(nfold_test11_expected_result, - 0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73, - 0x7b, 0x9b, 0x5b, 0x2b, 0x93, 0x13, 0x2b, 0x93, - 0x5c, 0x9b, 0xdc, 0xda, 0xd9, 0x5c, 0x98, 0x99, - 0xc4, 0xca, 0xe4, 0xde, 0xe6, 0xd6, 0xca, 0xe4 -); - -static const struct gss_krb5_test_param rfc3961_nfold_test_params[] = { - { - .desc = "64-fold(\"012345\")", - .nfold = 64, - .plaintext = &nfold_test1_plaintext, - .expected_result = &nfold_test1_expected_result, - }, - { - .desc = "56-fold(\"password\")", - .nfold = 56, - .plaintext = &nfold_test2_plaintext, - .expected_result = &nfold_test2_expected_result, - }, - { - .desc = "64-fold(\"Rough Consensus, and Running Code\")", - .nfold = 64, - .plaintext = &nfold_test3_plaintext, - .expected_result = &nfold_test3_expected_result, - }, - { - .desc = "168-fold(\"password\")", - .nfold = 168, - .plaintext = &nfold_test4_plaintext, - .expected_result = &nfold_test4_expected_result, - }, - { - .desc = "192-fold(\"MASSACHVSETTS INSTITVTE OF TECHNOLOGY\")", - .nfold = 192, - .plaintext = &nfold_test5_plaintext, - .expected_result = &nfold_test5_expected_result, - }, - { - .desc = "168-fold(\"Q\")", - .nfold = 168, - .plaintext = &nfold_test6_plaintext, - .expected_result = &nfold_test6_expected_result, - }, - { - .desc = "168-fold(\"ba\")", - .nfold = 168, - .plaintext = &nfold_test7_plaintext, - .expected_result = &nfold_test7_expected_result, - }, - { - .desc = "64-fold(\"kerberos\")", - .nfold = 64, - .plaintext = &nfold_test_kerberos, - .expected_result = &nfold_test8_expected_result, - }, - { - .desc = "128-fold(\"kerberos\")", - .nfold = 128, - .plaintext = &nfold_test_kerberos, - .expected_result = &nfold_test9_expected_result, - }, - { - .desc = "168-fold(\"kerberos\")", - .nfold = 168, - .plaintext = &nfold_test_kerberos, - .expected_result = &nfold_test10_expected_result, - }, - { - .desc = "256-fold(\"kerberos\")", - .nfold = 256, - .plaintext = &nfold_test_kerberos, - .expected_result = &nfold_test11_expected_result, - }, -}; - -/* Creates the function rfc3961_nfold_gen_params */ -KUNIT_ARRAY_PARAM(rfc3961_nfold, rfc3961_nfold_test_params, gss_krb5_get_desc); - -static void rfc3961_nfold_case(struct kunit *test) -{ - const struct gss_krb5_test_param *param = test->param_value; - u8 *result; - - /* Arrange */ - result = kunit_kzalloc(test, 4096, GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result); - - /* Act */ - krb5_nfold(param->plaintext->len * 8, param->plaintext->data, - param->expected_result->len * 8, result); - - /* Assert */ - KUNIT_EXPECT_MEMEQ_MSG(test, - param->expected_result->data, - result, - param->expected_result->len, - "result mismatch"); -} - -static struct kunit_case rfc3961_test_cases[] = { - { - .name = "RFC 3961 n-fold", - .run_case = rfc3961_nfold_case, - .generate_params = rfc3961_nfold_gen_params, - }, - {} -}; - -static struct kunit_suite rfc3961_suite = { - .name = "RFC 3961 tests", - .test_cases = rfc3961_test_cases, -}; - -/* - * From RFC 3962 Appendix B: Sample Test Vectors - * - * Some test vectors for CBC with ciphertext stealing, using an - * initial vector of all-zero. - * - * This test material is copyright (C) The Internet Society (2005). - */ - -DEFINE_HEX_XDR_NETOBJ(rfc3962_encryption_key, - 0x63, 0x68, 0x69, 0x63, 0x6b, 0x65, 0x6e, 0x20, - 0x74, 0x65, 0x72, 0x69, 0x79, 0x61, 0x6b, 0x69 -); - -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test1_plaintext, - 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, - 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65, - 0x20 -); -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test1_expected_result, - 0xc6, 0x35, 0x35, 0x68, 0xf2, 0xbf, 0x8c, 0xb4, - 0xd8, 0xa5, 0x80, 0x36, 0x2d, 0xa7, 0xff, 0x7f, - 0x97 -); -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test1_next_iv, - 0xc6, 0x35, 0x35, 0x68, 0xf2, 0xbf, 0x8c, 0xb4, - 0xd8, 0xa5, 0x80, 0x36, 0x2d, 0xa7, 0xff, 0x7f -); - -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test2_plaintext, - 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, - 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, - 0x20, 0x47, 0x61, 0x75, 0x27, 0x73, 0x20 -); -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test2_expected_result, - 0xfc, 0x00, 0x78, 0x3e, 0x0e, 0xfd, 0xb2, 0xc1, - 0xd4, 0x45, 0xd4, 0xc8, 0xef, 0xf7, 0xed, 0x22, - 0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0, - 0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5 -); -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test2_next_iv, - 0xfc, 0x00, 0x78, 0x3e, 0x0e, 0xfd, 0xb2, 0xc1, - 0xd4, 0x45, 0xd4, 0xc8, 0xef, 0xf7, 0xed, 0x22 -); - -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test3_plaintext, - 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, - 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, - 0x20, 0x47, 0x61, 0x75, 0x27, 0x73, 0x20, 0x43 -); -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test3_expected_result, - 0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5, - 0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8, - 0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0, - 0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84 -); -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test3_next_iv, - 0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5, - 0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8 -); - -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test4_plaintext, - 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, - 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, - 0x20, 0x47, 0x61, 0x75, 0x27, 0x73, 0x20, 0x43, - 0x68, 0x69, 0x63, 0x6b, 0x65, 0x6e, 0x2c, 0x20, - 0x70, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2c -); -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test4_expected_result, - 0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0, - 0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84, - 0xb3, 0xff, 0xfd, 0x94, 0x0c, 0x16, 0xa1, 0x8c, - 0x1b, 0x55, 0x49, 0xd2, 0xf8, 0x38, 0x02, 0x9e, - 0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5, - 0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5 -); -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test4_next_iv, - 0xb3, 0xff, 0xfd, 0x94, 0x0c, 0x16, 0xa1, 0x8c, - 0x1b, 0x55, 0x49, 0xd2, 0xf8, 0x38, 0x02, 0x9e -); - -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test5_plaintext, - 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, - 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, - 0x20, 0x47, 0x61, 0x75, 0x27, 0x73, 0x20, 0x43, - 0x68, 0x69, 0x63, 0x6b, 0x65, 0x6e, 0x2c, 0x20, - 0x70, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2c, 0x20 -); -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test5_expected_result, - 0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0, - 0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84, - 0x9d, 0xad, 0x8b, 0xbb, 0x96, 0xc4, 0xcd, 0xc0, - 0x3b, 0xc1, 0x03, 0xe1, 0xa1, 0x94, 0xbb, 0xd8, - 0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5, - 0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8 -); -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test5_next_iv, - 0x9d, 0xad, 0x8b, 0xbb, 0x96, 0xc4, 0xcd, 0xc0, - 0x3b, 0xc1, 0x03, 0xe1, 0xa1, 0x94, 0xbb, 0xd8 -); - -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test6_plaintext, - 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, - 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, - 0x20, 0x47, 0x61, 0x75, 0x27, 0x73, 0x20, 0x43, - 0x68, 0x69, 0x63, 0x6b, 0x65, 0x6e, 0x2c, 0x20, - 0x70, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2c, 0x20, - 0x61, 0x6e, 0x64, 0x20, 0x77, 0x6f, 0x6e, 0x74, - 0x6f, 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x70, 0x2e -); -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test6_expected_result, - 0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0, - 0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84, - 0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5, - 0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8, - 0x48, 0x07, 0xef, 0xe8, 0x36, 0xee, 0x89, 0xa5, - 0x26, 0x73, 0x0d, 0xbc, 0x2f, 0x7b, 0xc8, 0x40, - 0x9d, 0xad, 0x8b, 0xbb, 0x96, 0xc4, 0xcd, 0xc0, - 0x3b, 0xc1, 0x03, 0xe1, 0xa1, 0x94, 0xbb, 0xd8 -); -DEFINE_HEX_XDR_NETOBJ(rfc3962_enc_test6_next_iv, - 0x48, 0x07, 0xef, 0xe8, 0x36, 0xee, 0x89, 0xa5, - 0x26, 0x73, 0x0d, 0xbc, 0x2f, 0x7b, 0xc8, 0x40 -); - -static const struct gss_krb5_test_param rfc3962_encrypt_test_params[] = { - { - .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 1", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96, - .Ke = &rfc3962_encryption_key, - .plaintext = &rfc3962_enc_test1_plaintext, - .expected_result = &rfc3962_enc_test1_expected_result, - .next_iv = &rfc3962_enc_test1_next_iv, - }, - { - .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 2", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96, - .Ke = &rfc3962_encryption_key, - .plaintext = &rfc3962_enc_test2_plaintext, - .expected_result = &rfc3962_enc_test2_expected_result, - .next_iv = &rfc3962_enc_test2_next_iv, - }, - { - .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 3", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96, - .Ke = &rfc3962_encryption_key, - .plaintext = &rfc3962_enc_test3_plaintext, - .expected_result = &rfc3962_enc_test3_expected_result, - .next_iv = &rfc3962_enc_test3_next_iv, - }, - { - .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 4", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96, - .Ke = &rfc3962_encryption_key, - .plaintext = &rfc3962_enc_test4_plaintext, - .expected_result = &rfc3962_enc_test4_expected_result, - .next_iv = &rfc3962_enc_test4_next_iv, - }, - { - .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 5", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96, - .Ke = &rfc3962_encryption_key, - .plaintext = &rfc3962_enc_test5_plaintext, - .expected_result = &rfc3962_enc_test5_expected_result, - .next_iv = &rfc3962_enc_test5_next_iv, - }, - { - .desc = "Encrypt with aes128-cts-hmac-sha1-96 case 6", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96, - .Ke = &rfc3962_encryption_key, - .plaintext = &rfc3962_enc_test6_plaintext, - .expected_result = &rfc3962_enc_test6_expected_result, - .next_iv = &rfc3962_enc_test6_next_iv, - }, -}; - -/* Creates the function rfc3962_encrypt_gen_params */ -KUNIT_ARRAY_PARAM(rfc3962_encrypt, rfc3962_encrypt_test_params, - gss_krb5_get_desc); - -/* - * This tests the implementation of the encryption part of the mechanism. - * It does not apply a confounder or test the result of HMAC over the - * plaintext. - */ -static void rfc3962_encrypt_case(struct kunit *test) -{ - const struct gss_krb5_test_param *param = test->param_value; - struct crypto_sync_skcipher *cts_tfm, *cbc_tfm; - const struct gss_krb5_enctype *gk5e; - struct xdr_buf buf; - void *iv, *text; - u32 err; - - /* Arrange */ - gk5e = gss_krb5_lookup_enctype(param->enctype); - if (!gk5e) - kunit_skip(test, "Encryption type is not available"); - - cbc_tfm = crypto_alloc_sync_skcipher(gk5e->aux_cipher, 0, 0); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cbc_tfm); - err = crypto_sync_skcipher_setkey(cbc_tfm, param->Ke->data, param->Ke->len); - KUNIT_ASSERT_EQ(test, err, 0); - - cts_tfm = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cts_tfm); - err = crypto_sync_skcipher_setkey(cts_tfm, param->Ke->data, param->Ke->len); - KUNIT_ASSERT_EQ(test, err, 0); - - iv = kunit_kzalloc(test, crypto_sync_skcipher_ivsize(cts_tfm), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, iv); - - text = kunit_kzalloc(test, param->plaintext->len, GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, text); - - memcpy(text, param->plaintext->data, param->plaintext->len); - memset(&buf, 0, sizeof(buf)); - buf.head[0].iov_base = text; - buf.head[0].iov_len = param->plaintext->len; - buf.len = buf.head[0].iov_len; - - /* Act */ - err = krb5_cbc_cts_encrypt(cts_tfm, cbc_tfm, 0, &buf, NULL, - iv, crypto_sync_skcipher_ivsize(cts_tfm)); - KUNIT_ASSERT_EQ(test, err, 0); - - /* Assert */ - KUNIT_EXPECT_EQ_MSG(test, - param->expected_result->len, buf.len, - "ciphertext length mismatch"); - KUNIT_EXPECT_MEMEQ_MSG(test, - param->expected_result->data, - text, - param->expected_result->len, - "ciphertext mismatch"); - KUNIT_EXPECT_MEMEQ_MSG(test, - param->next_iv->data, - iv, - param->next_iv->len, - "IV mismatch"); - - crypto_free_sync_skcipher(cts_tfm); - crypto_free_sync_skcipher(cbc_tfm); -} - -static struct kunit_case rfc3962_test_cases[] = { - { - .name = "RFC 3962 encryption", - .run_case = rfc3962_encrypt_case, - .generate_params = rfc3962_encrypt_gen_params, - }, - {} -}; - -static struct kunit_suite rfc3962_suite = { - .name = "RFC 3962 suite", - .test_cases = rfc3962_test_cases, -}; - -/* - * From RFC 6803 Section 10. Test vectors - * - * Sample results for key derivation - * - * Copyright (c) 2012 IETF Trust and the persons identified as the - * document authors. All rights reserved. - */ - -DEFINE_HEX_XDR_NETOBJ(camellia128_cts_cmac_basekey, - 0x57, 0xd0, 0x29, 0x72, 0x98, 0xff, 0xd9, 0xd3, - 0x5d, 0xe5, 0xa4, 0x7f, 0xb4, 0xbd, 0xe2, 0x4b -); -DEFINE_HEX_XDR_NETOBJ(camellia128_cts_cmac_Kc, - 0xd1, 0x55, 0x77, 0x5a, 0x20, 0x9d, 0x05, 0xf0, - 0x2b, 0x38, 0xd4, 0x2a, 0x38, 0x9e, 0x5a, 0x56 -); -DEFINE_HEX_XDR_NETOBJ(camellia128_cts_cmac_Ke, - 0x64, 0xdf, 0x83, 0xf8, 0x5a, 0x53, 0x2f, 0x17, - 0x57, 0x7d, 0x8c, 0x37, 0x03, 0x57, 0x96, 0xab -); -DEFINE_HEX_XDR_NETOBJ(camellia128_cts_cmac_Ki, - 0x3e, 0x4f, 0xbd, 0xf3, 0x0f, 0xb8, 0x25, 0x9c, - 0x42, 0x5c, 0xb6, 0xc9, 0x6f, 0x1f, 0x46, 0x35 -); - -DEFINE_HEX_XDR_NETOBJ(camellia256_cts_cmac_basekey, - 0xb9, 0xd6, 0x82, 0x8b, 0x20, 0x56, 0xb7, 0xbe, - 0x65, 0x6d, 0x88, 0xa1, 0x23, 0xb1, 0xfa, 0xc6, - 0x82, 0x14, 0xac, 0x2b, 0x72, 0x7e, 0xcf, 0x5f, - 0x69, 0xaf, 0xe0, 0xc4, 0xdf, 0x2a, 0x6d, 0x2c -); -DEFINE_HEX_XDR_NETOBJ(camellia256_cts_cmac_Kc, - 0xe4, 0x67, 0xf9, 0xa9, 0x55, 0x2b, 0xc7, 0xd3, - 0x15, 0x5a, 0x62, 0x20, 0xaf, 0x9c, 0x19, 0x22, - 0x0e, 0xee, 0xd4, 0xff, 0x78, 0xb0, 0xd1, 0xe6, - 0xa1, 0x54, 0x49, 0x91, 0x46, 0x1a, 0x9e, 0x50 -); -DEFINE_HEX_XDR_NETOBJ(camellia256_cts_cmac_Ke, - 0x41, 0x2a, 0xef, 0xc3, 0x62, 0xa7, 0x28, 0x5f, - 0xc3, 0x96, 0x6c, 0x6a, 0x51, 0x81, 0xe7, 0x60, - 0x5a, 0xe6, 0x75, 0x23, 0x5b, 0x6d, 0x54, 0x9f, - 0xbf, 0xc9, 0xab, 0x66, 0x30, 0xa4, 0xc6, 0x04 -); -DEFINE_HEX_XDR_NETOBJ(camellia256_cts_cmac_Ki, - 0xfa, 0x62, 0x4f, 0xa0, 0xe5, 0x23, 0x99, 0x3f, - 0xa3, 0x88, 0xae, 0xfd, 0xc6, 0x7e, 0x67, 0xeb, - 0xcd, 0x8c, 0x08, 0xe8, 0xa0, 0x24, 0x6b, 0x1d, - 0x73, 0xb0, 0xd1, 0xdd, 0x9f, 0xc5, 0x82, 0xb0 -); - -DEFINE_HEX_XDR_NETOBJ(usage_checksum, - 0x00, 0x00, 0x00, 0x02, KEY_USAGE_SEED_CHECKSUM -); -DEFINE_HEX_XDR_NETOBJ(usage_encryption, - 0x00, 0x00, 0x00, 0x02, KEY_USAGE_SEED_ENCRYPTION -); -DEFINE_HEX_XDR_NETOBJ(usage_integrity, - 0x00, 0x00, 0x00, 0x02, KEY_USAGE_SEED_INTEGRITY -); - -static const struct gss_krb5_test_param rfc6803_kdf_test_params[] = { - { - .desc = "Derive Kc subkey for camellia128-cts-cmac", - .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC, - .base_key = &camellia128_cts_cmac_basekey, - .usage = &usage_checksum, - .expected_result = &camellia128_cts_cmac_Kc, - }, - { - .desc = "Derive Ke subkey for camellia128-cts-cmac", - .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC, - .base_key = &camellia128_cts_cmac_basekey, - .usage = &usage_encryption, - .expected_result = &camellia128_cts_cmac_Ke, - }, - { - .desc = "Derive Ki subkey for camellia128-cts-cmac", - .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC, - .base_key = &camellia128_cts_cmac_basekey, - .usage = &usage_integrity, - .expected_result = &camellia128_cts_cmac_Ki, - }, - { - .desc = "Derive Kc subkey for camellia256-cts-cmac", - .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC, - .base_key = &camellia256_cts_cmac_basekey, - .usage = &usage_checksum, - .expected_result = &camellia256_cts_cmac_Kc, - }, - { - .desc = "Derive Ke subkey for camellia256-cts-cmac", - .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC, - .base_key = &camellia256_cts_cmac_basekey, - .usage = &usage_encryption, - .expected_result = &camellia256_cts_cmac_Ke, - }, - { - .desc = "Derive Ki subkey for camellia256-cts-cmac", - .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC, - .base_key = &camellia256_cts_cmac_basekey, - .usage = &usage_integrity, - .expected_result = &camellia256_cts_cmac_Ki, - }, -}; - -/* Creates the function rfc6803_kdf_gen_params */ -KUNIT_ARRAY_PARAM(rfc6803_kdf, rfc6803_kdf_test_params, gss_krb5_get_desc); - -/* - * From RFC 6803 Section 10. Test vectors - * - * Sample checksums. - * - * Copyright (c) 2012 IETF Trust and the persons identified as the - * document authors. All rights reserved. - * - * XXX: These tests are likely to fail on EBCDIC or Unicode platforms. - */ -DEFINE_STR_XDR_NETOBJ(rfc6803_checksum_test1_plaintext, - "abcdefghijk"); -DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test1_basekey, - 0x1d, 0xc4, 0x6a, 0x8d, 0x76, 0x3f, 0x4f, 0x93, - 0x74, 0x2b, 0xcb, 0xa3, 0x38, 0x75, 0x76, 0xc3 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test1_usage, - 0x00, 0x00, 0x00, 0x07, KEY_USAGE_SEED_CHECKSUM -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test1_expected_result, - 0x11, 0x78, 0xe6, 0xc5, 0xc4, 0x7a, 0x8c, 0x1a, - 0xe0, 0xc4, 0xb9, 0xc7, 0xd4, 0xeb, 0x7b, 0x6b -); - -DEFINE_STR_XDR_NETOBJ(rfc6803_checksum_test2_plaintext, - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); -DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test2_basekey, - 0x50, 0x27, 0xbc, 0x23, 0x1d, 0x0f, 0x3a, 0x9d, - 0x23, 0x33, 0x3f, 0x1c, 0xa6, 0xfd, 0xbe, 0x7c -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test2_usage, - 0x00, 0x00, 0x00, 0x08, KEY_USAGE_SEED_CHECKSUM -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test2_expected_result, - 0xd1, 0xb3, 0x4f, 0x70, 0x04, 0xa7, 0x31, 0xf2, - 0x3a, 0x0c, 0x00, 0xbf, 0x6c, 0x3f, 0x75, 0x3a -); - -DEFINE_STR_XDR_NETOBJ(rfc6803_checksum_test3_plaintext, - "123456789"); -DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test3_basekey, - 0xb6, 0x1c, 0x86, 0xcc, 0x4e, 0x5d, 0x27, 0x57, - 0x54, 0x5a, 0xd4, 0x23, 0x39, 0x9f, 0xb7, 0x03, - 0x1e, 0xca, 0xb9, 0x13, 0xcb, 0xb9, 0x00, 0xbd, - 0x7a, 0x3c, 0x6d, 0xd8, 0xbf, 0x92, 0x01, 0x5b -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test3_usage, - 0x00, 0x00, 0x00, 0x09, KEY_USAGE_SEED_CHECKSUM -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test3_expected_result, - 0x87, 0xa1, 0x2c, 0xfd, 0x2b, 0x96, 0x21, 0x48, - 0x10, 0xf0, 0x1c, 0x82, 0x6e, 0x77, 0x44, 0xb1 -); - -DEFINE_STR_XDR_NETOBJ(rfc6803_checksum_test4_plaintext, - "!@#$%^&*()!@#$%^&*()!@#$%^&*()"); -DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test4_basekey, - 0x32, 0x16, 0x4c, 0x5b, 0x43, 0x4d, 0x1d, 0x15, - 0x38, 0xe4, 0xcf, 0xd9, 0xbe, 0x80, 0x40, 0xfe, - 0x8c, 0x4a, 0xc7, 0xac, 0xc4, 0xb9, 0x3d, 0x33, - 0x14, 0xd2, 0x13, 0x36, 0x68, 0x14, 0x7a, 0x05 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test4_usage, - 0x00, 0x00, 0x00, 0x0a, KEY_USAGE_SEED_CHECKSUM -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_checksum_test4_expected_result, - 0x3f, 0xa0, 0xb4, 0x23, 0x55, 0xe5, 0x2b, 0x18, - 0x91, 0x87, 0x29, 0x4a, 0xa2, 0x52, 0xab, 0x64 -); - -static const struct gss_krb5_test_param rfc6803_checksum_test_params[] = { - { - .desc = "camellia128-cts-cmac checksum test 1", - .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC, - .base_key = &rfc6803_checksum_test1_basekey, - .usage = &rfc6803_checksum_test1_usage, - .plaintext = &rfc6803_checksum_test1_plaintext, - .expected_result = &rfc6803_checksum_test1_expected_result, - }, - { - .desc = "camellia128-cts-cmac checksum test 2", - .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC, - .base_key = &rfc6803_checksum_test2_basekey, - .usage = &rfc6803_checksum_test2_usage, - .plaintext = &rfc6803_checksum_test2_plaintext, - .expected_result = &rfc6803_checksum_test2_expected_result, - }, - { - .desc = "camellia256-cts-cmac checksum test 3", - .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC, - .base_key = &rfc6803_checksum_test3_basekey, - .usage = &rfc6803_checksum_test3_usage, - .plaintext = &rfc6803_checksum_test3_plaintext, - .expected_result = &rfc6803_checksum_test3_expected_result, - }, - { - .desc = "camellia256-cts-cmac checksum test 4", - .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC, - .base_key = &rfc6803_checksum_test4_basekey, - .usage = &rfc6803_checksum_test4_usage, - .plaintext = &rfc6803_checksum_test4_plaintext, - .expected_result = &rfc6803_checksum_test4_expected_result, - }, -}; - -/* Creates the function rfc6803_checksum_gen_params */ -KUNIT_ARRAY_PARAM(rfc6803_checksum, rfc6803_checksum_test_params, - gss_krb5_get_desc); - -/* - * From RFC 6803 Section 10. Test vectors - * - * Sample encryptions (all using the default cipher state) - * - * Copyright (c) 2012 IETF Trust and the persons identified as the - * document authors. All rights reserved. - * - * Key usage values are from errata 4326 against RFC 6803. - */ - -static const struct xdr_netobj rfc6803_enc_empty_plaintext = { - .len = 0, -}; - -DEFINE_STR_XDR_NETOBJ(rfc6803_enc_1byte_plaintext, "1"); -DEFINE_STR_XDR_NETOBJ(rfc6803_enc_9byte_plaintext, "9 bytesss"); -DEFINE_STR_XDR_NETOBJ(rfc6803_enc_13byte_plaintext, "13 bytes byte"); -DEFINE_STR_XDR_NETOBJ(rfc6803_enc_30byte_plaintext, - "30 bytes bytes bytes bytes byt" -); - -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test1_confounder, - 0xb6, 0x98, 0x22, 0xa1, 0x9a, 0x6b, 0x09, 0xc0, - 0xeb, 0xc8, 0x55, 0x7d, 0x1f, 0x1b, 0x6c, 0x0a -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test1_basekey, - 0x1d, 0xc4, 0x6a, 0x8d, 0x76, 0x3f, 0x4f, 0x93, - 0x74, 0x2b, 0xcb, 0xa3, 0x38, 0x75, 0x76, 0xc3 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test1_expected_result, - 0xc4, 0x66, 0xf1, 0x87, 0x10, 0x69, 0x92, 0x1e, - 0xdb, 0x7c, 0x6f, 0xde, 0x24, 0x4a, 0x52, 0xdb, - 0x0b, 0xa1, 0x0e, 0xdc, 0x19, 0x7b, 0xdb, 0x80, - 0x06, 0x65, 0x8c, 0xa3, 0xcc, 0xce, 0x6e, 0xb8 -); - -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test2_confounder, - 0x6f, 0x2f, 0xc3, 0xc2, 0xa1, 0x66, 0xfd, 0x88, - 0x98, 0x96, 0x7a, 0x83, 0xde, 0x95, 0x96, 0xd9 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test2_basekey, - 0x50, 0x27, 0xbc, 0x23, 0x1d, 0x0f, 0x3a, 0x9d, - 0x23, 0x33, 0x3f, 0x1c, 0xa6, 0xfd, 0xbe, 0x7c -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test2_expected_result, - 0x84, 0x2d, 0x21, 0xfd, 0x95, 0x03, 0x11, 0xc0, - 0xdd, 0x46, 0x4a, 0x3f, 0x4b, 0xe8, 0xd6, 0xda, - 0x88, 0xa5, 0x6d, 0x55, 0x9c, 0x9b, 0x47, 0xd3, - 0xf9, 0xa8, 0x50, 0x67, 0xaf, 0x66, 0x15, 0x59, - 0xb8 -); - -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test3_confounder, - 0xa5, 0xb4, 0xa7, 0x1e, 0x07, 0x7a, 0xee, 0xf9, - 0x3c, 0x87, 0x63, 0xc1, 0x8f, 0xdb, 0x1f, 0x10 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test3_basekey, - 0xa1, 0xbb, 0x61, 0xe8, 0x05, 0xf9, 0xba, 0x6d, - 0xde, 0x8f, 0xdb, 0xdd, 0xc0, 0x5c, 0xde, 0xa0 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test3_expected_result, - 0x61, 0x9f, 0xf0, 0x72, 0xe3, 0x62, 0x86, 0xff, - 0x0a, 0x28, 0xde, 0xb3, 0xa3, 0x52, 0xec, 0x0d, - 0x0e, 0xdf, 0x5c, 0x51, 0x60, 0xd6, 0x63, 0xc9, - 0x01, 0x75, 0x8c, 0xcf, 0x9d, 0x1e, 0xd3, 0x3d, - 0x71, 0xdb, 0x8f, 0x23, 0xaa, 0xbf, 0x83, 0x48, - 0xa0 -); - -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test4_confounder, - 0x19, 0xfe, 0xe4, 0x0d, 0x81, 0x0c, 0x52, 0x4b, - 0x5b, 0x22, 0xf0, 0x18, 0x74, 0xc6, 0x93, 0xda -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test4_basekey, - 0x2c, 0xa2, 0x7a, 0x5f, 0xaf, 0x55, 0x32, 0x24, - 0x45, 0x06, 0x43, 0x4e, 0x1c, 0xef, 0x66, 0x76 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test4_expected_result, - 0xb8, 0xec, 0xa3, 0x16, 0x7a, 0xe6, 0x31, 0x55, - 0x12, 0xe5, 0x9f, 0x98, 0xa7, 0xc5, 0x00, 0x20, - 0x5e, 0x5f, 0x63, 0xff, 0x3b, 0xb3, 0x89, 0xaf, - 0x1c, 0x41, 0xa2, 0x1d, 0x64, 0x0d, 0x86, 0x15, - 0xc9, 0xed, 0x3f, 0xbe, 0xb0, 0x5a, 0xb6, 0xac, - 0xb6, 0x76, 0x89, 0xb5, 0xea -); - -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test5_confounder, - 0xca, 0x7a, 0x7a, 0xb4, 0xbe, 0x19, 0x2d, 0xab, - 0xd6, 0x03, 0x50, 0x6d, 0xb1, 0x9c, 0x39, 0xe2 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test5_basekey, - 0x78, 0x24, 0xf8, 0xc1, 0x6f, 0x83, 0xff, 0x35, - 0x4c, 0x6b, 0xf7, 0x51, 0x5b, 0x97, 0x3f, 0x43 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test5_expected_result, - 0xa2, 0x6a, 0x39, 0x05, 0xa4, 0xff, 0xd5, 0x81, - 0x6b, 0x7b, 0x1e, 0x27, 0x38, 0x0d, 0x08, 0x09, - 0x0c, 0x8e, 0xc1, 0xf3, 0x04, 0x49, 0x6e, 0x1a, - 0xbd, 0xcd, 0x2b, 0xdc, 0xd1, 0xdf, 0xfc, 0x66, - 0x09, 0x89, 0xe1, 0x17, 0xa7, 0x13, 0xdd, 0xbb, - 0x57, 0xa4, 0x14, 0x6c, 0x15, 0x87, 0xcb, 0xa4, - 0x35, 0x66, 0x65, 0x59, 0x1d, 0x22, 0x40, 0x28, - 0x2f, 0x58, 0x42, 0xb1, 0x05, 0xa5 -); - -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test6_confounder, - 0x3c, 0xbb, 0xd2, 0xb4, 0x59, 0x17, 0x94, 0x10, - 0x67, 0xf9, 0x65, 0x99, 0xbb, 0x98, 0x92, 0x6c -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test6_basekey, - 0xb6, 0x1c, 0x86, 0xcc, 0x4e, 0x5d, 0x27, 0x57, - 0x54, 0x5a, 0xd4, 0x23, 0x39, 0x9f, 0xb7, 0x03, - 0x1e, 0xca, 0xb9, 0x13, 0xcb, 0xb9, 0x00, 0xbd, - 0x7a, 0x3c, 0x6d, 0xd8, 0xbf, 0x92, 0x01, 0x5b -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test6_expected_result, - 0x03, 0x88, 0x6d, 0x03, 0x31, 0x0b, 0x47, 0xa6, - 0xd8, 0xf0, 0x6d, 0x7b, 0x94, 0xd1, 0xdd, 0x83, - 0x7e, 0xcc, 0xe3, 0x15, 0xef, 0x65, 0x2a, 0xff, - 0x62, 0x08, 0x59, 0xd9, 0x4a, 0x25, 0x92, 0x66 -); - -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test7_confounder, - 0xde, 0xf4, 0x87, 0xfc, 0xeb, 0xe6, 0xde, 0x63, - 0x46, 0xd4, 0xda, 0x45, 0x21, 0xbb, 0xa2, 0xd2 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test7_basekey, - 0x1b, 0x97, 0xfe, 0x0a, 0x19, 0x0e, 0x20, 0x21, - 0xeb, 0x30, 0x75, 0x3e, 0x1b, 0x6e, 0x1e, 0x77, - 0xb0, 0x75, 0x4b, 0x1d, 0x68, 0x46, 0x10, 0x35, - 0x58, 0x64, 0x10, 0x49, 0x63, 0x46, 0x38, 0x33 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test7_expected_result, - 0x2c, 0x9c, 0x15, 0x70, 0x13, 0x3c, 0x99, 0xbf, - 0x6a, 0x34, 0xbc, 0x1b, 0x02, 0x12, 0x00, 0x2f, - 0xd1, 0x94, 0x33, 0x87, 0x49, 0xdb, 0x41, 0x35, - 0x49, 0x7a, 0x34, 0x7c, 0xfc, 0xd9, 0xd1, 0x8a, - 0x12 -); - -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test8_confounder, - 0xad, 0x4f, 0xf9, 0x04, 0xd3, 0x4e, 0x55, 0x53, - 0x84, 0xb1, 0x41, 0x00, 0xfc, 0x46, 0x5f, 0x88 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test8_basekey, - 0x32, 0x16, 0x4c, 0x5b, 0x43, 0x4d, 0x1d, 0x15, - 0x38, 0xe4, 0xcf, 0xd9, 0xbe, 0x80, 0x40, 0xfe, - 0x8c, 0x4a, 0xc7, 0xac, 0xc4, 0xb9, 0x3d, 0x33, - 0x14, 0xd2, 0x13, 0x36, 0x68, 0x14, 0x7a, 0x05 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test8_expected_result, - 0x9c, 0x6d, 0xe7, 0x5f, 0x81, 0x2d, 0xe7, 0xed, - 0x0d, 0x28, 0xb2, 0x96, 0x35, 0x57, 0xa1, 0x15, - 0x64, 0x09, 0x98, 0x27, 0x5b, 0x0a, 0xf5, 0x15, - 0x27, 0x09, 0x91, 0x3f, 0xf5, 0x2a, 0x2a, 0x9c, - 0x8e, 0x63, 0xb8, 0x72, 0xf9, 0x2e, 0x64, 0xc8, - 0x39 -); - -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test9_confounder, - 0xcf, 0x9b, 0xca, 0x6d, 0xf1, 0x14, 0x4e, 0x0c, - 0x0a, 0xf9, 0xb8, 0xf3, 0x4c, 0x90, 0xd5, 0x14 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test9_basekey, - 0xb0, 0x38, 0xb1, 0x32, 0xcd, 0x8e, 0x06, 0x61, - 0x22, 0x67, 0xfa, 0xb7, 0x17, 0x00, 0x66, 0xd8, - 0x8a, 0xec, 0xcb, 0xa0, 0xb7, 0x44, 0xbf, 0xc6, - 0x0d, 0xc8, 0x9b, 0xca, 0x18, 0x2d, 0x07, 0x15 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test9_expected_result, - 0xee, 0xec, 0x85, 0xa9, 0x81, 0x3c, 0xdc, 0x53, - 0x67, 0x72, 0xab, 0x9b, 0x42, 0xde, 0xfc, 0x57, - 0x06, 0xf7, 0x26, 0xe9, 0x75, 0xdd, 0xe0, 0x5a, - 0x87, 0xeb, 0x54, 0x06, 0xea, 0x32, 0x4c, 0xa1, - 0x85, 0xc9, 0x98, 0x6b, 0x42, 0xaa, 0xbe, 0x79, - 0x4b, 0x84, 0x82, 0x1b, 0xee -); - -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test10_confounder, - 0x64, 0x4d, 0xef, 0x38, 0xda, 0x35, 0x00, 0x72, - 0x75, 0x87, 0x8d, 0x21, 0x68, 0x55, 0xe2, 0x28 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test10_basekey, - 0xcc, 0xfc, 0xd3, 0x49, 0xbf, 0x4c, 0x66, 0x77, - 0xe8, 0x6e, 0x4b, 0x02, 0xb8, 0xea, 0xb9, 0x24, - 0xa5, 0x46, 0xac, 0x73, 0x1c, 0xf9, 0xbf, 0x69, - 0x89, 0xb9, 0x96, 0xe7, 0xd6, 0xbf, 0xbb, 0xa7 -); -DEFINE_HEX_XDR_NETOBJ(rfc6803_enc_test10_expected_result, - 0x0e, 0x44, 0x68, 0x09, 0x85, 0x85, 0x5f, 0x2d, - 0x1f, 0x18, 0x12, 0x52, 0x9c, 0xa8, 0x3b, 0xfd, - 0x8e, 0x34, 0x9d, 0xe6, 0xfd, 0x9a, 0xda, 0x0b, - 0xaa, 0xa0, 0x48, 0xd6, 0x8e, 0x26, 0x5f, 0xeb, - 0xf3, 0x4a, 0xd1, 0x25, 0x5a, 0x34, 0x49, 0x99, - 0xad, 0x37, 0x14, 0x68, 0x87, 0xa6, 0xc6, 0x84, - 0x57, 0x31, 0xac, 0x7f, 0x46, 0x37, 0x6a, 0x05, - 0x04, 0xcd, 0x06, 0x57, 0x14, 0x74 -); - -static const struct gss_krb5_test_param rfc6803_encrypt_test_params[] = { - { - .desc = "Encrypt empty plaintext with camellia128-cts-cmac", - .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC, - .constant = 0, - .base_key = &rfc6803_enc_test1_basekey, - .plaintext = &rfc6803_enc_empty_plaintext, - .confounder = &rfc6803_enc_test1_confounder, - .expected_result = &rfc6803_enc_test1_expected_result, - }, - { - .desc = "Encrypt 1 byte with camellia128-cts-cmac", - .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC, - .constant = 1, - .base_key = &rfc6803_enc_test2_basekey, - .plaintext = &rfc6803_enc_1byte_plaintext, - .confounder = &rfc6803_enc_test2_confounder, - .expected_result = &rfc6803_enc_test2_expected_result, - }, - { - .desc = "Encrypt 9 bytes with camellia128-cts-cmac", - .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC, - .constant = 2, - .base_key = &rfc6803_enc_test3_basekey, - .plaintext = &rfc6803_enc_9byte_plaintext, - .confounder = &rfc6803_enc_test3_confounder, - .expected_result = &rfc6803_enc_test3_expected_result, - }, - { - .desc = "Encrypt 13 bytes with camellia128-cts-cmac", - .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC, - .constant = 3, - .base_key = &rfc6803_enc_test4_basekey, - .plaintext = &rfc6803_enc_13byte_plaintext, - .confounder = &rfc6803_enc_test4_confounder, - .expected_result = &rfc6803_enc_test4_expected_result, - }, - { - .desc = "Encrypt 30 bytes with camellia128-cts-cmac", - .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC, - .constant = 4, - .base_key = &rfc6803_enc_test5_basekey, - .plaintext = &rfc6803_enc_30byte_plaintext, - .confounder = &rfc6803_enc_test5_confounder, - .expected_result = &rfc6803_enc_test5_expected_result, - }, - { - .desc = "Encrypt empty plaintext with camellia256-cts-cmac", - .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC, - .constant = 0, - .base_key = &rfc6803_enc_test6_basekey, - .plaintext = &rfc6803_enc_empty_plaintext, - .confounder = &rfc6803_enc_test6_confounder, - .expected_result = &rfc6803_enc_test6_expected_result, - }, - { - .desc = "Encrypt 1 byte with camellia256-cts-cmac", - .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC, - .constant = 1, - .base_key = &rfc6803_enc_test7_basekey, - .plaintext = &rfc6803_enc_1byte_plaintext, - .confounder = &rfc6803_enc_test7_confounder, - .expected_result = &rfc6803_enc_test7_expected_result, - }, - { - .desc = "Encrypt 9 bytes with camellia256-cts-cmac", - .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC, - .constant = 2, - .base_key = &rfc6803_enc_test8_basekey, - .plaintext = &rfc6803_enc_9byte_plaintext, - .confounder = &rfc6803_enc_test8_confounder, - .expected_result = &rfc6803_enc_test8_expected_result, - }, - { - .desc = "Encrypt 13 bytes with camellia256-cts-cmac", - .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC, - .constant = 3, - .base_key = &rfc6803_enc_test9_basekey, - .plaintext = &rfc6803_enc_13byte_plaintext, - .confounder = &rfc6803_enc_test9_confounder, - .expected_result = &rfc6803_enc_test9_expected_result, - }, - { - .desc = "Encrypt 30 bytes with camellia256-cts-cmac", - .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC, - .constant = 4, - .base_key = &rfc6803_enc_test10_basekey, - .plaintext = &rfc6803_enc_30byte_plaintext, - .confounder = &rfc6803_enc_test10_confounder, - .expected_result = &rfc6803_enc_test10_expected_result, - }, -}; - -/* Creates the function rfc6803_encrypt_gen_params */ -KUNIT_ARRAY_PARAM(rfc6803_encrypt, rfc6803_encrypt_test_params, - gss_krb5_get_desc); - -static void rfc6803_encrypt_case(struct kunit *test) -{ - const struct gss_krb5_test_param *param = test->param_value; - struct crypto_sync_skcipher *cts_tfm, *cbc_tfm; - const struct gss_krb5_enctype *gk5e; - struct xdr_netobj Ke, Ki, checksum; - u8 usage_data[GSS_KRB5_K5CLENGTH]; - struct xdr_netobj usage = { - .data = usage_data, - .len = sizeof(usage_data), - }; - struct crypto_ahash *ahash_tfm; - unsigned int blocksize; - struct xdr_buf buf; - void *text; - size_t len; - u32 err; - - /* Arrange */ - gk5e = gss_krb5_lookup_enctype(param->enctype); - if (!gk5e) - kunit_skip(test, "Encryption type is not available"); - - memset(usage_data, 0, sizeof(usage_data)); - usage.data[3] = param->constant; - - Ke.len = gk5e->Ke_length; - Ke.data = kunit_kzalloc(test, Ke.len, GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, Ke.data); - usage.data[4] = KEY_USAGE_SEED_ENCRYPTION; - err = gk5e->derive_key(gk5e, param->base_key, &Ke, &usage, GFP_KERNEL); - KUNIT_ASSERT_EQ(test, err, 0); - - cbc_tfm = crypto_alloc_sync_skcipher(gk5e->aux_cipher, 0, 0); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cbc_tfm); - err = crypto_sync_skcipher_setkey(cbc_tfm, Ke.data, Ke.len); - KUNIT_ASSERT_EQ(test, err, 0); - - cts_tfm = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cts_tfm); - err = crypto_sync_skcipher_setkey(cts_tfm, Ke.data, Ke.len); - KUNIT_ASSERT_EQ(test, err, 0); - blocksize = crypto_sync_skcipher_blocksize(cts_tfm); - - len = param->confounder->len + param->plaintext->len + blocksize; - text = kunit_kzalloc(test, len, GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, text); - memcpy(text, param->confounder->data, param->confounder->len); - memcpy(text + param->confounder->len, param->plaintext->data, - param->plaintext->len); - - memset(&buf, 0, sizeof(buf)); - buf.head[0].iov_base = text; - buf.head[0].iov_len = param->confounder->len + param->plaintext->len; - buf.len = buf.head[0].iov_len; - - checksum.len = gk5e->cksumlength; - checksum.data = kunit_kzalloc(test, checksum.len, GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, checksum.data); - - Ki.len = gk5e->Ki_length; - Ki.data = kunit_kzalloc(test, Ki.len, GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, Ki.data); - usage.data[4] = KEY_USAGE_SEED_INTEGRITY; - err = gk5e->derive_key(gk5e, param->base_key, &Ki, - &usage, GFP_KERNEL); - KUNIT_ASSERT_EQ(test, err, 0); - ahash_tfm = crypto_alloc_ahash(gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ahash_tfm); - err = crypto_ahash_setkey(ahash_tfm, Ki.data, Ki.len); - KUNIT_ASSERT_EQ(test, err, 0); - - /* Act */ - err = gss_krb5_checksum(ahash_tfm, NULL, 0, &buf, 0, &checksum); - KUNIT_ASSERT_EQ(test, err, 0); - - err = krb5_cbc_cts_encrypt(cts_tfm, cbc_tfm, 0, &buf, NULL, NULL, 0); - KUNIT_ASSERT_EQ(test, err, 0); - - /* Assert */ - KUNIT_EXPECT_EQ_MSG(test, param->expected_result->len, - buf.len + checksum.len, - "ciphertext length mismatch"); - KUNIT_EXPECT_MEMEQ_MSG(test, - param->expected_result->data, - buf.head[0].iov_base, - buf.len, - "encrypted result mismatch"); - KUNIT_EXPECT_MEMEQ_MSG(test, - param->expected_result->data + - (param->expected_result->len - checksum.len), - checksum.data, - checksum.len, - "HMAC mismatch"); - - crypto_free_ahash(ahash_tfm); - crypto_free_sync_skcipher(cts_tfm); - crypto_free_sync_skcipher(cbc_tfm); -} - -static struct kunit_case rfc6803_test_cases[] = { - { - .name = "RFC 6803 key derivation", - .run_case = kdf_case, - .generate_params = rfc6803_kdf_gen_params, - }, - { - .name = "RFC 6803 checksum", - .run_case = checksum_case, - .generate_params = rfc6803_checksum_gen_params, - }, - { - .name = "RFC 6803 encryption", - .run_case = rfc6803_encrypt_case, - .generate_params = rfc6803_encrypt_gen_params, - }, - {} -}; - -static struct kunit_suite rfc6803_suite = { - .name = "RFC 6803 suite", - .test_cases = rfc6803_test_cases, -}; - -/* - * From RFC 8009 Appendix A. Test Vectors - * - * Sample results for SHA-2 enctype key derivation - * - * This test material is copyright (c) 2016 IETF Trust and the - * persons identified as the document authors. All rights reserved. - */ - -DEFINE_HEX_XDR_NETOBJ(aes128_cts_hmac_sha256_128_basekey, - 0x37, 0x05, 0xd9, 0x60, 0x80, 0xc1, 0x77, 0x28, - 0xa0, 0xe8, 0x00, 0xea, 0xb6, 0xe0, 0xd2, 0x3c -); -DEFINE_HEX_XDR_NETOBJ(aes128_cts_hmac_sha256_128_Kc, - 0xb3, 0x1a, 0x01, 0x8a, 0x48, 0xf5, 0x47, 0x76, - 0xf4, 0x03, 0xe9, 0xa3, 0x96, 0x32, 0x5d, 0xc3 -); -DEFINE_HEX_XDR_NETOBJ(aes128_cts_hmac_sha256_128_Ke, - 0x9b, 0x19, 0x7d, 0xd1, 0xe8, 0xc5, 0x60, 0x9d, - 0x6e, 0x67, 0xc3, 0xe3, 0x7c, 0x62, 0xc7, 0x2e -); -DEFINE_HEX_XDR_NETOBJ(aes128_cts_hmac_sha256_128_Ki, - 0x9f, 0xda, 0x0e, 0x56, 0xab, 0x2d, 0x85, 0xe1, - 0x56, 0x9a, 0x68, 0x86, 0x96, 0xc2, 0x6a, 0x6c -); - -DEFINE_HEX_XDR_NETOBJ(aes256_cts_hmac_sha384_192_basekey, - 0x6d, 0x40, 0x4d, 0x37, 0xfa, 0xf7, 0x9f, 0x9d, - 0xf0, 0xd3, 0x35, 0x68, 0xd3, 0x20, 0x66, 0x98, - 0x00, 0xeb, 0x48, 0x36, 0x47, 0x2e, 0xa8, 0xa0, - 0x26, 0xd1, 0x6b, 0x71, 0x82, 0x46, 0x0c, 0x52 -); -DEFINE_HEX_XDR_NETOBJ(aes256_cts_hmac_sha384_192_Kc, - 0xef, 0x57, 0x18, 0xbe, 0x86, 0xcc, 0x84, 0x96, - 0x3d, 0x8b, 0xbb, 0x50, 0x31, 0xe9, 0xf5, 0xc4, - 0xba, 0x41, 0xf2, 0x8f, 0xaf, 0x69, 0xe7, 0x3d -); -DEFINE_HEX_XDR_NETOBJ(aes256_cts_hmac_sha384_192_Ke, - 0x56, 0xab, 0x22, 0xbe, 0xe6, 0x3d, 0x82, 0xd7, - 0xbc, 0x52, 0x27, 0xf6, 0x77, 0x3f, 0x8e, 0xa7, - 0xa5, 0xeb, 0x1c, 0x82, 0x51, 0x60, 0xc3, 0x83, - 0x12, 0x98, 0x0c, 0x44, 0x2e, 0x5c, 0x7e, 0x49 -); -DEFINE_HEX_XDR_NETOBJ(aes256_cts_hmac_sha384_192_Ki, - 0x69, 0xb1, 0x65, 0x14, 0xe3, 0xcd, 0x8e, 0x56, - 0xb8, 0x20, 0x10, 0xd5, 0xc7, 0x30, 0x12, 0xb6, - 0x22, 0xc4, 0xd0, 0x0f, 0xfc, 0x23, 0xed, 0x1f -); - -static const struct gss_krb5_test_param rfc8009_kdf_test_params[] = { - { - .desc = "Derive Kc subkey for aes128-cts-hmac-sha256-128", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128, - .base_key = &aes128_cts_hmac_sha256_128_basekey, - .usage = &usage_checksum, - .expected_result = &aes128_cts_hmac_sha256_128_Kc, - }, - { - .desc = "Derive Ke subkey for aes128-cts-hmac-sha256-128", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128, - .base_key = &aes128_cts_hmac_sha256_128_basekey, - .usage = &usage_encryption, - .expected_result = &aes128_cts_hmac_sha256_128_Ke, - }, - { - .desc = "Derive Ki subkey for aes128-cts-hmac-sha256-128", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128, - .base_key = &aes128_cts_hmac_sha256_128_basekey, - .usage = &usage_integrity, - .expected_result = &aes128_cts_hmac_sha256_128_Ki, - }, - { - .desc = "Derive Kc subkey for aes256-cts-hmac-sha384-192", - .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192, - .base_key = &aes256_cts_hmac_sha384_192_basekey, - .usage = &usage_checksum, - .expected_result = &aes256_cts_hmac_sha384_192_Kc, - }, - { - .desc = "Derive Ke subkey for aes256-cts-hmac-sha384-192", - .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192, - .base_key = &aes256_cts_hmac_sha384_192_basekey, - .usage = &usage_encryption, - .expected_result = &aes256_cts_hmac_sha384_192_Ke, - }, - { - .desc = "Derive Ki subkey for aes256-cts-hmac-sha384-192", - .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192, - .base_key = &aes256_cts_hmac_sha384_192_basekey, - .usage = &usage_integrity, - .expected_result = &aes256_cts_hmac_sha384_192_Ki, - }, -}; - -/* Creates the function rfc8009_kdf_gen_params */ -KUNIT_ARRAY_PARAM(rfc8009_kdf, rfc8009_kdf_test_params, gss_krb5_get_desc); - -/* - * From RFC 8009 Appendix A. Test Vectors - * - * These sample checksums use the above sample key derivation results, - * including use of the same base-key and key usage values. - * - * This test material is copyright (c) 2016 IETF Trust and the - * persons identified as the document authors. All rights reserved. - */ - -DEFINE_HEX_XDR_NETOBJ(rfc8009_checksum_plaintext, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14 -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_checksum_test1_expected_result, - 0xd7, 0x83, 0x67, 0x18, 0x66, 0x43, 0xd6, 0x7b, - 0x41, 0x1c, 0xba, 0x91, 0x39, 0xfc, 0x1d, 0xee -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_checksum_test2_expected_result, - 0x45, 0xee, 0x79, 0x15, 0x67, 0xee, 0xfc, 0xa3, - 0x7f, 0x4a, 0xc1, 0xe0, 0x22, 0x2d, 0xe8, 0x0d, - 0x43, 0xc3, 0xbf, 0xa0, 0x66, 0x99, 0x67, 0x2a -); - -static const struct gss_krb5_test_param rfc8009_checksum_test_params[] = { - { - .desc = "Checksum with aes128-cts-hmac-sha256-128", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128, - .base_key = &aes128_cts_hmac_sha256_128_basekey, - .usage = &usage_checksum, - .plaintext = &rfc8009_checksum_plaintext, - .expected_result = &rfc8009_checksum_test1_expected_result, - }, - { - .desc = "Checksum with aes256-cts-hmac-sha384-192", - .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192, - .base_key = &aes256_cts_hmac_sha384_192_basekey, - .usage = &usage_checksum, - .plaintext = &rfc8009_checksum_plaintext, - .expected_result = &rfc8009_checksum_test2_expected_result, - }, -}; - -/* Creates the function rfc8009_checksum_gen_params */ -KUNIT_ARRAY_PARAM(rfc8009_checksum, rfc8009_checksum_test_params, - gss_krb5_get_desc); - -/* - * From RFC 8009 Appendix A. Test Vectors - * - * Sample encryptions (all using the default cipher state): - * -------------------------------------------------------- - * - * These sample encryptions use the above sample key derivation results, - * including use of the same base-key and key usage values. - * - * This test material is copyright (c) 2016 IETF Trust and the - * persons identified as the document authors. All rights reserved. - */ - -static const struct xdr_netobj rfc8009_enc_empty_plaintext = { - .len = 0, -}; -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_short_plaintext, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_block_plaintext, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_long_plaintext, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14 -); - -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test1_confounder, - 0x7e, 0x58, 0x95, 0xea, 0xf2, 0x67, 0x24, 0x35, - 0xba, 0xd8, 0x17, 0xf5, 0x45, 0xa3, 0x71, 0x48 -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test1_expected_result, - 0xef, 0x85, 0xfb, 0x89, 0x0b, 0xb8, 0x47, 0x2f, - 0x4d, 0xab, 0x20, 0x39, 0x4d, 0xca, 0x78, 0x1d -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test1_expected_hmac, - 0xad, 0x87, 0x7e, 0xda, 0x39, 0xd5, 0x0c, 0x87, - 0x0c, 0x0d, 0x5a, 0x0a, 0x8e, 0x48, 0xc7, 0x18 -); - -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test2_confounder, - 0x7b, 0xca, 0x28, 0x5e, 0x2f, 0xd4, 0x13, 0x0f, - 0xb5, 0x5b, 0x1a, 0x5c, 0x83, 0xbc, 0x5b, 0x24 -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test2_expected_result, - 0x84, 0xd7, 0xf3, 0x07, 0x54, 0xed, 0x98, 0x7b, - 0xab, 0x0b, 0xf3, 0x50, 0x6b, 0xeb, 0x09, 0xcf, - 0xb5, 0x54, 0x02, 0xce, 0xf7, 0xe6 -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test2_expected_hmac, - 0x87, 0x7c, 0xe9, 0x9e, 0x24, 0x7e, 0x52, 0xd1, - 0x6e, 0xd4, 0x42, 0x1d, 0xfd, 0xf8, 0x97, 0x6c -); - -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test3_confounder, - 0x56, 0xab, 0x21, 0x71, 0x3f, 0xf6, 0x2c, 0x0a, - 0x14, 0x57, 0x20, 0x0f, 0x6f, 0xa9, 0x94, 0x8f -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test3_expected_result, - 0x35, 0x17, 0xd6, 0x40, 0xf5, 0x0d, 0xdc, 0x8a, - 0xd3, 0x62, 0x87, 0x22, 0xb3, 0x56, 0x9d, 0x2a, - 0xe0, 0x74, 0x93, 0xfa, 0x82, 0x63, 0x25, 0x40, - 0x80, 0xea, 0x65, 0xc1, 0x00, 0x8e, 0x8f, 0xc2 -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test3_expected_hmac, - 0x95, 0xfb, 0x48, 0x52, 0xe7, 0xd8, 0x3e, 0x1e, - 0x7c, 0x48, 0xc3, 0x7e, 0xeb, 0xe6, 0xb0, 0xd3 -); - -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test4_confounder, - 0xa7, 0xa4, 0xe2, 0x9a, 0x47, 0x28, 0xce, 0x10, - 0x66, 0x4f, 0xb6, 0x4e, 0x49, 0xad, 0x3f, 0xac -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test4_expected_result, - 0x72, 0x0f, 0x73, 0xb1, 0x8d, 0x98, 0x59, 0xcd, - 0x6c, 0xcb, 0x43, 0x46, 0x11, 0x5c, 0xd3, 0x36, - 0xc7, 0x0f, 0x58, 0xed, 0xc0, 0xc4, 0x43, 0x7c, - 0x55, 0x73, 0x54, 0x4c, 0x31, 0xc8, 0x13, 0xbc, - 0xe1, 0xe6, 0xd0, 0x72, 0xc1 -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test4_expected_hmac, - 0x86, 0xb3, 0x9a, 0x41, 0x3c, 0x2f, 0x92, 0xca, - 0x9b, 0x83, 0x34, 0xa2, 0x87, 0xff, 0xcb, 0xfc -); - -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test5_confounder, - 0xf7, 0x64, 0xe9, 0xfa, 0x15, 0xc2, 0x76, 0x47, - 0x8b, 0x2c, 0x7d, 0x0c, 0x4e, 0x5f, 0x58, 0xe4 -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test5_expected_result, - 0x41, 0xf5, 0x3f, 0xa5, 0xbf, 0xe7, 0x02, 0x6d, - 0x91, 0xfa, 0xf9, 0xbe, 0x95, 0x91, 0x95, 0xa0 -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test5_expected_hmac, - 0x58, 0x70, 0x72, 0x73, 0xa9, 0x6a, 0x40, 0xf0, - 0xa0, 0x19, 0x60, 0x62, 0x1a, 0xc6, 0x12, 0x74, - 0x8b, 0x9b, 0xbf, 0xbe, 0x7e, 0xb4, 0xce, 0x3c -); - -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test6_confounder, - 0xb8, 0x0d, 0x32, 0x51, 0xc1, 0xf6, 0x47, 0x14, - 0x94, 0x25, 0x6f, 0xfe, 0x71, 0x2d, 0x0b, 0x9a -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test6_expected_result, - 0x4e, 0xd7, 0xb3, 0x7c, 0x2b, 0xca, 0xc8, 0xf7, - 0x4f, 0x23, 0xc1, 0xcf, 0x07, 0xe6, 0x2b, 0xc7, - 0xb7, 0x5f, 0xb3, 0xf6, 0x37, 0xb9 -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test6_expected_hmac, - 0xf5, 0x59, 0xc7, 0xf6, 0x64, 0xf6, 0x9e, 0xab, - 0x7b, 0x60, 0x92, 0x23, 0x75, 0x26, 0xea, 0x0d, - 0x1f, 0x61, 0xcb, 0x20, 0xd6, 0x9d, 0x10, 0xf2 -); - -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test7_confounder, - 0x53, 0xbf, 0x8a, 0x0d, 0x10, 0x52, 0x65, 0xd4, - 0xe2, 0x76, 0x42, 0x86, 0x24, 0xce, 0x5e, 0x63 -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test7_expected_result, - 0xbc, 0x47, 0xff, 0xec, 0x79, 0x98, 0xeb, 0x91, - 0xe8, 0x11, 0x5c, 0xf8, 0xd1, 0x9d, 0xac, 0x4b, - 0xbb, 0xe2, 0xe1, 0x63, 0xe8, 0x7d, 0xd3, 0x7f, - 0x49, 0xbe, 0xca, 0x92, 0x02, 0x77, 0x64, 0xf6 -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test7_expected_hmac, - 0x8c, 0xf5, 0x1f, 0x14, 0xd7, 0x98, 0xc2, 0x27, - 0x3f, 0x35, 0xdf, 0x57, 0x4d, 0x1f, 0x93, 0x2e, - 0x40, 0xc4, 0xff, 0x25, 0x5b, 0x36, 0xa2, 0x66 -); - -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test8_confounder, - 0x76, 0x3e, 0x65, 0x36, 0x7e, 0x86, 0x4f, 0x02, - 0xf5, 0x51, 0x53, 0xc7, 0xe3, 0xb5, 0x8a, 0xf1 -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test8_expected_result, - 0x40, 0x01, 0x3e, 0x2d, 0xf5, 0x8e, 0x87, 0x51, - 0x95, 0x7d, 0x28, 0x78, 0xbc, 0xd2, 0xd6, 0xfe, - 0x10, 0x1c, 0xcf, 0xd5, 0x56, 0xcb, 0x1e, 0xae, - 0x79, 0xdb, 0x3c, 0x3e, 0xe8, 0x64, 0x29, 0xf2, - 0xb2, 0xa6, 0x02, 0xac, 0x86 -); -DEFINE_HEX_XDR_NETOBJ(rfc8009_enc_test8_expected_hmac, - 0xfe, 0xf6, 0xec, 0xb6, 0x47, 0xd6, 0x29, 0x5f, - 0xae, 0x07, 0x7a, 0x1f, 0xeb, 0x51, 0x75, 0x08, - 0xd2, 0xc1, 0x6b, 0x41, 0x92, 0xe0, 0x1f, 0x62 -); - -static const struct gss_krb5_test_param rfc8009_encrypt_test_params[] = { - { - .desc = "Encrypt empty plaintext with aes128-cts-hmac-sha256-128", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128, - .plaintext = &rfc8009_enc_empty_plaintext, - .confounder = &rfc8009_enc_test1_confounder, - .base_key = &aes128_cts_hmac_sha256_128_basekey, - .expected_result = &rfc8009_enc_test1_expected_result, - .expected_hmac = &rfc8009_enc_test1_expected_hmac, - }, - { - .desc = "Encrypt short plaintext with aes128-cts-hmac-sha256-128", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128, - .plaintext = &rfc8009_enc_short_plaintext, - .confounder = &rfc8009_enc_test2_confounder, - .base_key = &aes128_cts_hmac_sha256_128_basekey, - .expected_result = &rfc8009_enc_test2_expected_result, - .expected_hmac = &rfc8009_enc_test2_expected_hmac, - }, - { - .desc = "Encrypt block plaintext with aes128-cts-hmac-sha256-128", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128, - .plaintext = &rfc8009_enc_block_plaintext, - .confounder = &rfc8009_enc_test3_confounder, - .base_key = &aes128_cts_hmac_sha256_128_basekey, - .expected_result = &rfc8009_enc_test3_expected_result, - .expected_hmac = &rfc8009_enc_test3_expected_hmac, - }, - { - .desc = "Encrypt long plaintext with aes128-cts-hmac-sha256-128", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128, - .plaintext = &rfc8009_enc_long_plaintext, - .confounder = &rfc8009_enc_test4_confounder, - .base_key = &aes128_cts_hmac_sha256_128_basekey, - .expected_result = &rfc8009_enc_test4_expected_result, - .expected_hmac = &rfc8009_enc_test4_expected_hmac, - }, - { - .desc = "Encrypt empty plaintext with aes256-cts-hmac-sha384-192", - .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192, - .plaintext = &rfc8009_enc_empty_plaintext, - .confounder = &rfc8009_enc_test5_confounder, - .base_key = &aes256_cts_hmac_sha384_192_basekey, - .expected_result = &rfc8009_enc_test5_expected_result, - .expected_hmac = &rfc8009_enc_test5_expected_hmac, - }, - { - .desc = "Encrypt short plaintext with aes256-cts-hmac-sha384-192", - .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192, - .plaintext = &rfc8009_enc_short_plaintext, - .confounder = &rfc8009_enc_test6_confounder, - .base_key = &aes256_cts_hmac_sha384_192_basekey, - .expected_result = &rfc8009_enc_test6_expected_result, - .expected_hmac = &rfc8009_enc_test6_expected_hmac, - }, - { - .desc = "Encrypt block plaintext with aes256-cts-hmac-sha384-192", - .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192, - .plaintext = &rfc8009_enc_block_plaintext, - .confounder = &rfc8009_enc_test7_confounder, - .base_key = &aes256_cts_hmac_sha384_192_basekey, - .expected_result = &rfc8009_enc_test7_expected_result, - .expected_hmac = &rfc8009_enc_test7_expected_hmac, - }, - { - .desc = "Encrypt long plaintext with aes256-cts-hmac-sha384-192", - .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192, - .plaintext = &rfc8009_enc_long_plaintext, - .confounder = &rfc8009_enc_test8_confounder, - .base_key = &aes256_cts_hmac_sha384_192_basekey, - .expected_result = &rfc8009_enc_test8_expected_result, - .expected_hmac = &rfc8009_enc_test8_expected_hmac, - }, -}; - -/* Creates the function rfc8009_encrypt_gen_params */ -KUNIT_ARRAY_PARAM(rfc8009_encrypt, rfc8009_encrypt_test_params, - gss_krb5_get_desc); - -static void rfc8009_encrypt_case(struct kunit *test) -{ - const struct gss_krb5_test_param *param = test->param_value; - struct crypto_sync_skcipher *cts_tfm, *cbc_tfm; - const struct gss_krb5_enctype *gk5e; - struct xdr_netobj Ke, Ki, checksum; - u8 usage_data[GSS_KRB5_K5CLENGTH]; - struct xdr_netobj usage = { - .data = usage_data, - .len = sizeof(usage_data), - }; - struct crypto_ahash *ahash_tfm; - struct xdr_buf buf; - void *text; - size_t len; - u32 err; - - /* Arrange */ - gk5e = gss_krb5_lookup_enctype(param->enctype); - if (!gk5e) - kunit_skip(test, "Encryption type is not available"); - - *(__be32 *)usage.data = cpu_to_be32(2); - - Ke.len = gk5e->Ke_length; - Ke.data = kunit_kzalloc(test, Ke.len, GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, Ke.data); - usage.data[4] = KEY_USAGE_SEED_ENCRYPTION; - err = gk5e->derive_key(gk5e, param->base_key, &Ke, - &usage, GFP_KERNEL); - KUNIT_ASSERT_EQ(test, err, 0); - - cbc_tfm = crypto_alloc_sync_skcipher(gk5e->aux_cipher, 0, 0); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cbc_tfm); - err = crypto_sync_skcipher_setkey(cbc_tfm, Ke.data, Ke.len); - KUNIT_ASSERT_EQ(test, err, 0); - - cts_tfm = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cts_tfm); - err = crypto_sync_skcipher_setkey(cts_tfm, Ke.data, Ke.len); - KUNIT_ASSERT_EQ(test, err, 0); - - len = param->confounder->len + param->plaintext->len; - text = kunit_kzalloc(test, len, GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, text); - memcpy(text, param->confounder->data, param->confounder->len); - memcpy(text + param->confounder->len, param->plaintext->data, - param->plaintext->len); - - memset(&buf, 0, sizeof(buf)); - buf.head[0].iov_base = text; - buf.head[0].iov_len = param->confounder->len + param->plaintext->len; - buf.len = buf.head[0].iov_len; - - checksum.len = gk5e->cksumlength; - checksum.data = kunit_kzalloc(test, checksum.len, GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, checksum.data); - - Ki.len = gk5e->Ki_length; - Ki.data = kunit_kzalloc(test, Ki.len, GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, Ki.data); - usage.data[4] = KEY_USAGE_SEED_INTEGRITY; - err = gk5e->derive_key(gk5e, param->base_key, &Ki, - &usage, GFP_KERNEL); - KUNIT_ASSERT_EQ(test, err, 0); - - ahash_tfm = crypto_alloc_ahash(gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ahash_tfm); - err = crypto_ahash_setkey(ahash_tfm, Ki.data, Ki.len); - KUNIT_ASSERT_EQ(test, err, 0); - - /* Act */ - err = krb5_cbc_cts_encrypt(cts_tfm, cbc_tfm, 0, &buf, NULL, NULL, 0); - KUNIT_ASSERT_EQ(test, err, 0); - err = krb5_etm_checksum(cts_tfm, ahash_tfm, &buf, 0, &checksum); - KUNIT_ASSERT_EQ(test, err, 0); - - /* Assert */ - KUNIT_EXPECT_EQ_MSG(test, - param->expected_result->len, buf.len, - "ciphertext length mismatch"); - KUNIT_EXPECT_MEMEQ_MSG(test, - param->expected_result->data, - buf.head[0].iov_base, - param->expected_result->len, - "ciphertext mismatch"); - KUNIT_EXPECT_MEMEQ_MSG(test, - param->expected_hmac->data, - checksum.data, - checksum.len, - "HMAC mismatch"); - - crypto_free_ahash(ahash_tfm); - crypto_free_sync_skcipher(cts_tfm); - crypto_free_sync_skcipher(cbc_tfm); -} - -static struct kunit_case rfc8009_test_cases[] = { - { - .name = "RFC 8009 key derivation", - .run_case = kdf_case, - .generate_params = rfc8009_kdf_gen_params, - }, - { - .name = "RFC 8009 checksum", - .run_case = checksum_case, - .generate_params = rfc8009_checksum_gen_params, - }, - { - .name = "RFC 8009 encryption", - .run_case = rfc8009_encrypt_case, - .generate_params = rfc8009_encrypt_gen_params, - }, - {} -}; - -static struct kunit_suite rfc8009_suite = { - .name = "RFC 8009 suite", - .test_cases = rfc8009_test_cases, -}; - -/* - * Encryption self-tests - */ - -DEFINE_STR_XDR_NETOBJ(encrypt_selftest_plaintext, - "This is the plaintext for the encryption self-test."); - -static const struct gss_krb5_test_param encrypt_selftest_params[] = { - { - .desc = "aes128-cts-hmac-sha1-96 encryption self-test", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96, - .Ke = &rfc3962_encryption_key, - .plaintext = &encrypt_selftest_plaintext, - }, - { - .desc = "aes256-cts-hmac-sha1-96 encryption self-test", - .enctype = ENCTYPE_AES256_CTS_HMAC_SHA1_96, - .Ke = &rfc3962_encryption_key, - .plaintext = &encrypt_selftest_plaintext, - }, - { - .desc = "camellia128-cts-cmac encryption self-test", - .enctype = ENCTYPE_CAMELLIA128_CTS_CMAC, - .Ke = &camellia128_cts_cmac_Ke, - .plaintext = &encrypt_selftest_plaintext, - }, - { - .desc = "camellia256-cts-cmac encryption self-test", - .enctype = ENCTYPE_CAMELLIA256_CTS_CMAC, - .Ke = &camellia256_cts_cmac_Ke, - .plaintext = &encrypt_selftest_plaintext, - }, - { - .desc = "aes128-cts-hmac-sha256-128 encryption self-test", - .enctype = ENCTYPE_AES128_CTS_HMAC_SHA256_128, - .Ke = &aes128_cts_hmac_sha256_128_Ke, - .plaintext = &encrypt_selftest_plaintext, - }, - { - .desc = "aes256-cts-hmac-sha384-192 encryption self-test", - .enctype = ENCTYPE_AES256_CTS_HMAC_SHA384_192, - .Ke = &aes256_cts_hmac_sha384_192_Ke, - .plaintext = &encrypt_selftest_plaintext, - }, -}; - -/* Creates the function encrypt_selftest_gen_params */ -KUNIT_ARRAY_PARAM(encrypt_selftest, encrypt_selftest_params, - gss_krb5_get_desc); - -/* - * Encrypt and decrypt plaintext, and ensure the input plaintext - * matches the output plaintext. A confounder is not added in this - * case. - */ -static void encrypt_selftest_case(struct kunit *test) -{ - const struct gss_krb5_test_param *param = test->param_value; - struct crypto_sync_skcipher *cts_tfm, *cbc_tfm; - const struct gss_krb5_enctype *gk5e; - struct xdr_buf buf; - void *text; - int err; - - /* Arrange */ - gk5e = gss_krb5_lookup_enctype(param->enctype); - if (!gk5e) - kunit_skip(test, "Encryption type is not available"); - - cbc_tfm = crypto_alloc_sync_skcipher(gk5e->aux_cipher, 0, 0); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cbc_tfm); - err = crypto_sync_skcipher_setkey(cbc_tfm, param->Ke->data, param->Ke->len); - KUNIT_ASSERT_EQ(test, err, 0); - - cts_tfm = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cts_tfm); - err = crypto_sync_skcipher_setkey(cts_tfm, param->Ke->data, param->Ke->len); - KUNIT_ASSERT_EQ(test, err, 0); - - text = kunit_kzalloc(test, roundup(param->plaintext->len, - crypto_sync_skcipher_blocksize(cbc_tfm)), - GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, text); - - memcpy(text, param->plaintext->data, param->plaintext->len); - memset(&buf, 0, sizeof(buf)); - buf.head[0].iov_base = text; - buf.head[0].iov_len = param->plaintext->len; - buf.len = buf.head[0].iov_len; - - /* Act */ - err = krb5_cbc_cts_encrypt(cts_tfm, cbc_tfm, 0, &buf, NULL, NULL, 0); - KUNIT_ASSERT_EQ(test, err, 0); - err = krb5_cbc_cts_decrypt(cts_tfm, cbc_tfm, 0, &buf); - KUNIT_ASSERT_EQ(test, err, 0); - - /* Assert */ - KUNIT_EXPECT_EQ_MSG(test, - param->plaintext->len, buf.len, - "length mismatch"); - KUNIT_EXPECT_MEMEQ_MSG(test, - param->plaintext->data, - buf.head[0].iov_base, - buf.len, - "plaintext mismatch"); - - crypto_free_sync_skcipher(cts_tfm); - crypto_free_sync_skcipher(cbc_tfm); -} - -static struct kunit_case encryption_test_cases[] = { - { - .name = "Encryption self-tests", - .run_case = encrypt_selftest_case, - .generate_params = encrypt_selftest_gen_params, - }, - {} -}; - -static struct kunit_suite encryption_test_suite = { - .name = "Encryption test suite", - .test_cases = encryption_test_cases, -}; - -kunit_test_suites(&rfc3961_suite, - &rfc3962_suite, - &rfc6803_suite, - &rfc8009_suite, - &encryption_test_suite); - -MODULE_DESCRIPTION("Test RPCSEC GSS Kerberos 5 functions"); -MODULE_LICENSE("GPL"); diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c index ef0e6af9fc95..b5fb70419faa 100644 --- a/net/sunrpc/auth_gss/gss_krb5_unseal.c +++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c @@ -60,6 +60,8 @@ #include <linux/types.h> #include <linux/jiffies.h> #include <linux/sunrpc/gss_krb5.h> +#include <linux/scatterlist.h> +#include <linux/slab.h> #include "gss_krb5_internal.h" @@ -71,18 +73,19 @@ u32 gss_krb5_verify_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *message_buffer, struct xdr_netobj *read_token) { - struct crypto_ahash *tfm = ctx->initiate ? - ctx->acceptor_sign : ctx->initiator_sign; - char cksumdata[GSS_KRB5_MAX_CKSUM_LEN]; - struct xdr_netobj cksumobj = { - .len = ctx->gk5e->cksumlength, - .data = cksumdata, - }; + const struct krb5_enctype *krb5 = ctx->krb5e; + struct crypto_shash *shash = ctx->initiate ? + ctx->acceptor_sign_shash : ctx->initiator_sign_shash; + unsigned int cksum_len = krb5->cksum_len; + struct scatterlist sg_head[XDR_BUF_TO_SG_NENTS]; + struct scatterlist *sg_overflow; + size_t mic_offset, mic_len; u8 *ptr = read_token->data; __be16 be16_ptr; time64_t now; u8 flags; - int i; + int nsg, i; + int ret; dprintk("RPC: %s\n", __func__); @@ -104,13 +107,20 @@ gss_krb5_verify_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *message_buffer, if (ptr[i] != 0xff) return GSS_S_DEFECTIVE_TOKEN; - if (gss_krb5_checksum(tfm, ptr, GSS_KRB5_TOK_HDR_LEN, - message_buffer, 0, &cksumobj)) + nsg = gss_krb5_mic_build_sg(message_buffer, + ptr + GSS_KRB5_TOK_HDR_LEN, + cksum_len, ptr, + sg_head, &sg_overflow); + if (nsg < 0) return GSS_S_FAILURE; - if (memcmp(cksumobj.data, ptr + GSS_KRB5_TOK_HDR_LEN, - ctx->gk5e->cksumlength)) - return GSS_S_BAD_SIG; + mic_offset = 0; + mic_len = cksum_len + message_buffer->len + GSS_KRB5_TOK_HDR_LEN; + ret = crypto_krb5_verify_mic(krb5, shash, NULL, sg_head, nsg, + &mic_offset, &mic_len); + kfree(sg_overflow); + if (ret) + return gss_krb5_errno_to_status(ret); /* it got through unscathed. Make sure the context is unexpired */ now = ktime_get_real_seconds(); diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c index b3e1738ff6bf..ac4b32df42b9 100644 --- a/net/sunrpc/auth_gss/gss_krb5_wrap.c +++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c @@ -28,7 +28,6 @@ * SUCH DAMAGES. */ -#include <crypto/skcipher.h> #include <linux/types.h> #include <linux/jiffies.h> #include <linux/sunrpc/gss_krb5.h> @@ -112,9 +111,9 @@ gss_krb5_wrap_v2(struct krb5_ctx *kctx, int offset, *ptr++ = (unsigned char) ((KG2_TOK_WRAP>>8) & 0xff); *ptr++ = (unsigned char) (KG2_TOK_WRAP & 0xff); - if ((kctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0) + if (!kctx->initiate) flags |= KG2_TOKEN_FLAG_SENTBYACCEPTOR; - if ((kctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY) != 0) + if (kctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY) flags |= KG2_TOKEN_FLAG_ACCEPTORSUBKEY; /* We always do confidentiality in wrap tokens */ flags |= KG2_TOKEN_FLAG_SEALED; @@ -130,7 +129,7 @@ gss_krb5_wrap_v2(struct krb5_ctx *kctx, int offset, be64ptr = (__be64 *)be16ptr; *be64ptr = cpu_to_be64(atomic64_fetch_inc(&kctx->seq_send64)); - err = (*kctx->gk5e->encrypt)(kctx, offset, buf, pages); + err = gss_krb5_aead_encrypt(kctx, offset, buf, pages); if (err) return err; @@ -184,10 +183,10 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len, if (rrc != 0) rotate_left(offset + 16, buf, rrc); - err = (*kctx->gk5e->decrypt)(kctx, offset, len, buf, - &headskip, &tailskip); + err = gss_krb5_aead_decrypt(kctx, offset, len, buf, + &headskip, &tailskip); if (err) - return GSS_S_FAILURE; + return err; /* * Retrieve the decrypted gss token header and verify diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 161d02cc1c2c..d14209031e18 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -206,7 +206,7 @@ static struct cache_head *rsi_alloc(void) static int rsi_upcall(struct cache_detail *cd, struct cache_head *h) { - return sunrpc_cache_pipe_upcall_timeout(cd, h); + return sunrpc_cache_upcall_warn(cd, h); } static void rsi_request(struct cache_detail *cd, diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 27dd6b58b8ff..391037f15292 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -33,9 +33,11 @@ #include <linux/sunrpc/cache.h> #include <linux/sunrpc/stats.h> #include <linux/sunrpc/rpc_pipe_fs.h> +#include <net/genetlink.h> #include <trace/events/sunrpc.h> #include "netns.h" +#include "netlink.h" #include "fail.h" #define RPCDBG_FACILITY RPCDBG_CACHE @@ -1195,12 +1197,12 @@ static bool cache_listeners_exist(struct cache_detail *detail) } /* - * register an upcall request to user-space and queue it up for read() by the - * upcall daemon. + * register an upcall request to user-space and queue it up to be fetched by + * the upcall daemon. * * Each request is at most one page long. */ -static int cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h) +static int cache_do_upcall(struct cache_detail *detail, struct cache_head *h) { char *buf; struct cache_request *crq; @@ -1233,6 +1235,8 @@ static int cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h) /* Lost a race, no longer PENDING, so don't enqueue */ ret = -EAGAIN; spin_unlock(&detail->queue_lock); + if (ret != -EAGAIN && detail->cache_notify) + detail->cache_notify(detail, h); wake_up(&detail->queue_wait); if (ret == -EAGAIN) { kfree(buf); @@ -1241,25 +1245,25 @@ static int cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h) return ret; } -int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h) +int sunrpc_cache_upcall(struct cache_detail *detail, struct cache_head *h) { if (test_and_set_bit(CACHE_PENDING, &h->flags)) return 0; - return cache_pipe_upcall(detail, h); + return cache_do_upcall(detail, h); } -EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall); +EXPORT_SYMBOL_GPL(sunrpc_cache_upcall); -int sunrpc_cache_pipe_upcall_timeout(struct cache_detail *detail, - struct cache_head *h) +int sunrpc_cache_upcall_warn(struct cache_detail *detail, + struct cache_head *h) { if (!cache_listeners_exist(detail)) { warn_no_listener(detail); trace_cache_entry_no_listener(detail, h); return -EINVAL; } - return sunrpc_cache_pipe_upcall(detail, h); + return sunrpc_cache_upcall(detail, h); } -EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall_timeout); +EXPORT_SYMBOL_GPL(sunrpc_cache_upcall_warn); /* * parse a message from user-space and pass it @@ -1900,3 +1904,106 @@ void sunrpc_cache_unhash(struct cache_detail *cd, struct cache_head *h) spin_unlock(&cd->hash_lock); } EXPORT_SYMBOL_GPL(sunrpc_cache_unhash); + +/** + * sunrpc_cache_requests_count - count pending upcall requests + * @cd: cache_detail to query + * + * Returns the number of requests on the cache's request list that + * still have CACHE_PENDING set. + */ +int sunrpc_cache_requests_count(struct cache_detail *cd) +{ + struct cache_request *crq; + int cnt = 0; + + spin_lock(&cd->queue_lock); + list_for_each_entry(crq, &cd->requests, list) { + if (test_bit(CACHE_PENDING, &crq->item->flags)) + cnt++; + } + spin_unlock(&cd->queue_lock); + return cnt; +} +EXPORT_SYMBOL_GPL(sunrpc_cache_requests_count); + +/** + * sunrpc_cache_requests_snapshot - snapshot pending upcall requests + * @cd: cache_detail to query + * @items: array to fill with cache_head pointers (caller-allocated) + * @seqnos: array to fill with sequence numbers (caller-allocated) + * @max: size of the arrays + * @min_seqno: only include entries with seqno > min_seqno (0 for all) + * + * Only entries with CACHE_PENDING set are included. Takes a reference + * on each cache_head via cache_get(). Caller must call cache_put() + * on each returned item when done. + * + * Returns the number of entries filled. + */ +int sunrpc_cache_requests_snapshot(struct cache_detail *cd, + struct cache_head **items, + u64 *seqnos, int max, + u64 min_seqno) +{ + struct cache_request *crq; + int i = 0; + + spin_lock(&cd->queue_lock); + list_for_each_entry(crq, &cd->requests, list) { + if (i >= max) + break; + if (!test_bit(CACHE_PENDING, &crq->item->flags)) + continue; + if (crq->seqno <= min_seqno) + continue; + items[i] = cache_get(crq->item); + seqnos[i] = crq->seqno; + i++; + } + spin_unlock(&cd->queue_lock); + return i; +} +EXPORT_SYMBOL_GPL(sunrpc_cache_requests_snapshot); + +/** + * sunrpc_cache_notify - send a netlink notification for a cache event + * @cd: cache_detail for the cache + * @h: cache_head entry (unused, reserved for future use) + * @cache_type: cache type identifier (e.g. SUNRPC_CACHE_TYPE_UNIX_GID) + * + * Sends a SUNRPC_CMD_CACHE_NOTIFY multicast message on the "exportd" + * group if any listeners are present. Returns 0 on success or a + * negative errno. + */ +int sunrpc_cache_notify(struct cache_detail *cd, struct cache_head *h, + u32 cache_type) +{ + struct genlmsghdr *hdr; + struct sk_buff *msg; + + if (!genl_has_listeners(&sunrpc_nl_family, cd->net, + SUNRPC_NLGRP_EXPORTD)) + return -ENOLINK; + + msg = genlmsg_new(nla_total_size(sizeof(u32)), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = genlmsg_put(msg, 0, 0, &sunrpc_nl_family, 0, + SUNRPC_CMD_CACHE_NOTIFY); + if (!hdr) { + nlmsg_free(msg); + return -ENOMEM; + } + + if (nla_put_u32(msg, SUNRPC_A_CACHE_NOTIFY_CACHE_TYPE, cache_type)) { + nlmsg_free(msg); + return -ENOMEM; + } + + genlmsg_end(msg, hdr); + return genlmsg_multicast_netns(&sunrpc_nl_family, cd->net, msg, 0, + SUNRPC_NLGRP_EXPORTD, GFP_KERNEL); +} +EXPORT_SYMBOL_GPL(sunrpc_cache_notify); diff --git a/net/sunrpc/netlink.c b/net/sunrpc/netlink.c new file mode 100644 index 000000000000..ce09ecc0faa2 --- /dev/null +++ b/net/sunrpc/netlink.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) +/* Do not edit directly, auto-generated from: */ +/* Documentation/netlink/specs/sunrpc_cache.yaml */ +/* YNL-GEN kernel source */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ + +#include <net/netlink.h> +#include <net/genetlink.h> + +#include "netlink.h" + +#include <uapi/linux/sunrpc_netlink.h> + +/* Common nested types */ +const struct nla_policy sunrpc_ip_map_nl_policy[SUNRPC_A_IP_MAP_EXPIRY + 1] = { + [SUNRPC_A_IP_MAP_SEQNO] = { .type = NLA_U64, }, + [SUNRPC_A_IP_MAP_CLASS] = { .type = NLA_NUL_STRING, }, + [SUNRPC_A_IP_MAP_ADDR] = { .type = NLA_NUL_STRING, }, + [SUNRPC_A_IP_MAP_DOMAIN] = { .type = NLA_NUL_STRING, }, + [SUNRPC_A_IP_MAP_NEGATIVE] = { .type = NLA_FLAG, }, + [SUNRPC_A_IP_MAP_EXPIRY] = { .type = NLA_U64, }, +}; + +const struct nla_policy sunrpc_unix_gid_nl_policy[SUNRPC_A_UNIX_GID_EXPIRY + 1] = { + [SUNRPC_A_UNIX_GID_SEQNO] = { .type = NLA_U64, }, + [SUNRPC_A_UNIX_GID_UID] = { .type = NLA_U32, }, + [SUNRPC_A_UNIX_GID_GIDS] = { .type = NLA_U32, }, + [SUNRPC_A_UNIX_GID_NEGATIVE] = { .type = NLA_FLAG, }, + [SUNRPC_A_UNIX_GID_EXPIRY] = { .type = NLA_U64, }, +}; + +/* SUNRPC_CMD_IP_MAP_SET_REQS - do */ +static const struct nla_policy sunrpc_ip_map_set_reqs_nl_policy[SUNRPC_A_IP_MAP_REQS_REQUESTS + 1] = { + [SUNRPC_A_IP_MAP_REQS_REQUESTS] = NLA_POLICY_NESTED(sunrpc_ip_map_nl_policy), +}; + +/* SUNRPC_CMD_UNIX_GID_SET_REQS - do */ +static const struct nla_policy sunrpc_unix_gid_set_reqs_nl_policy[SUNRPC_A_UNIX_GID_REQS_REQUESTS + 1] = { + [SUNRPC_A_UNIX_GID_REQS_REQUESTS] = NLA_POLICY_NESTED(sunrpc_unix_gid_nl_policy), +}; + +/* SUNRPC_CMD_CACHE_FLUSH - do */ +static const struct nla_policy sunrpc_cache_flush_nl_policy[SUNRPC_A_CACHE_FLUSH_MASK + 1] = { + [SUNRPC_A_CACHE_FLUSH_MASK] = NLA_POLICY_MASK(NLA_U32, 0x3), +}; + +/* Ops table for sunrpc */ +static const struct genl_split_ops sunrpc_nl_ops[] = { + { + .cmd = SUNRPC_CMD_IP_MAP_GET_REQS, + .dumpit = sunrpc_nl_ip_map_get_reqs_dumpit, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP, + }, + { + .cmd = SUNRPC_CMD_IP_MAP_SET_REQS, + .doit = sunrpc_nl_ip_map_set_reqs_doit, + .policy = sunrpc_ip_map_set_reqs_nl_policy, + .maxattr = SUNRPC_A_IP_MAP_REQS_REQUESTS, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = SUNRPC_CMD_UNIX_GID_GET_REQS, + .dumpit = sunrpc_nl_unix_gid_get_reqs_dumpit, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP, + }, + { + .cmd = SUNRPC_CMD_UNIX_GID_SET_REQS, + .doit = sunrpc_nl_unix_gid_set_reqs_doit, + .policy = sunrpc_unix_gid_set_reqs_nl_policy, + .maxattr = SUNRPC_A_UNIX_GID_REQS_REQUESTS, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = SUNRPC_CMD_CACHE_FLUSH, + .doit = sunrpc_nl_cache_flush_doit, + .policy = sunrpc_cache_flush_nl_policy, + .maxattr = SUNRPC_A_CACHE_FLUSH_MASK, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, +}; + +static const struct genl_multicast_group sunrpc_nl_mcgrps[] = { + [SUNRPC_NLGRP_NONE] = { "none", }, + [SUNRPC_NLGRP_EXPORTD] = { "exportd", }, +}; + +struct genl_family sunrpc_nl_family __ro_after_init = { + .name = SUNRPC_FAMILY_NAME, + .version = SUNRPC_FAMILY_VERSION, + .netnsok = true, + .parallel_ops = true, + .module = THIS_MODULE, + .split_ops = sunrpc_nl_ops, + .n_split_ops = ARRAY_SIZE(sunrpc_nl_ops), + .mcgrps = sunrpc_nl_mcgrps, + .n_mcgrps = ARRAY_SIZE(sunrpc_nl_mcgrps), +}; diff --git a/net/sunrpc/netlink.h b/net/sunrpc/netlink.h new file mode 100644 index 000000000000..2c1012303d48 --- /dev/null +++ b/net/sunrpc/netlink.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* Do not edit directly, auto-generated from: */ +/* Documentation/netlink/specs/sunrpc_cache.yaml */ +/* YNL-GEN kernel header */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ + +#ifndef _LINUX_SUNRPC_GEN_H +#define _LINUX_SUNRPC_GEN_H + +#include <net/netlink.h> +#include <net/genetlink.h> + +#include <uapi/linux/sunrpc_netlink.h> + +/* Common nested types */ +extern const struct nla_policy sunrpc_ip_map_nl_policy[SUNRPC_A_IP_MAP_EXPIRY + 1]; +extern const struct nla_policy sunrpc_unix_gid_nl_policy[SUNRPC_A_UNIX_GID_EXPIRY + 1]; + +int sunrpc_nl_ip_map_get_reqs_dumpit(struct sk_buff *skb, + struct netlink_callback *cb); +int sunrpc_nl_ip_map_set_reqs_doit(struct sk_buff *skb, struct genl_info *info); +int sunrpc_nl_unix_gid_get_reqs_dumpit(struct sk_buff *skb, + struct netlink_callback *cb); +int sunrpc_nl_unix_gid_set_reqs_doit(struct sk_buff *skb, + struct genl_info *info); +int sunrpc_nl_cache_flush_doit(struct sk_buff *skb, struct genl_info *info); + +enum { + SUNRPC_NLGRP_NONE, + SUNRPC_NLGRP_EXPORTD, +}; + +extern struct genl_family sunrpc_nl_family; + +#endif /* _LINUX_SUNRPC_GEN_H */ diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index bab6cab29405..ab88ce46afb5 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -23,9 +23,12 @@ #include <linux/sunrpc/rpc_pipe_fs.h> #include <linux/sunrpc/xprtsock.h> +#include <net/genetlink.h> + #include "sunrpc.h" #include "sysfs.h" #include "netns.h" +#include "netlink.h" unsigned int sunrpc_net_id; EXPORT_SYMBOL_GPL(sunrpc_net_id); @@ -108,6 +111,10 @@ init_sunrpc(void) if (err) goto out5; + err = genl_register_family(&sunrpc_nl_family); + if (err) + goto out6; + sunrpc_debugfs_init(); #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) rpc_register_sysctl(); @@ -116,6 +123,8 @@ init_sunrpc(void) init_socket_xprt(); /* clnt sock transport */ return 0; +out6: + rpc_sysfs_exit(); out5: unregister_rpc_pipefs(); out4: @@ -131,6 +140,7 @@ out: static void __exit cleanup_sunrpc(void) { + genl_unregister_family(&sunrpc_nl_family); rpc_sysfs_exit(); rpc_cleanup_clids(); xprt_cleanup_ids(); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 576fa42e7abf..ae9ec4bf34f7 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -1598,6 +1598,12 @@ static void svc_release_rqst(struct svc_rqst *rqstp) if (procp && procp->pc_release) procp->pc_release(rqstp); + + /* + * A subsequent svc_release_rqst() on this rqstp must not + * re-invoke pc_release against released state. + */ + rqstp->rq_procinfo = NULL; } /** @@ -1616,6 +1622,9 @@ void svc_process(struct svc_rqst *rqstp) svc_xprt_deferred_close(rqstp->rq_xprt); #endif + /* Discard a stale release hook from a previous RPC. */ + rqstp->rq_procinfo = NULL; + /* * Setup response xdr_buf. * Initially it has just one page @@ -1672,6 +1681,7 @@ void svc_process_bc(struct rpc_rqst *req, struct svc_rqst *rqstp) int proc_error; /* Build the svc_rqst used by the common processing routine */ + rqstp->rq_procinfo = NULL; rqstp->rq_xid = req->rq_xid; rqstp->rq_prot = req->rq_xprt->prot; rqstp->rq_bc_net = req->rq_xprt->xprt_net; diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index b16e710926c1..63d1002e63e7 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -234,7 +234,19 @@ void svc_xprt_received(struct svc_xprt *xprt) svc_xprt_get(xprt); smp_mb__before_atomic(); clear_bit(XPT_BUSY, &xprt->xpt_flags); - svc_xprt_enqueue(xprt); + + /* + * Skip the enqueue when no actionable flags are set. + * Each producer both sets its flag (XPT_DATA, XPT_CLOSE, + * etc.) and calls svc_xprt_enqueue(); if a set_bit races + * with this check, the producer's own enqueue observes + * !XPT_BUSY and dispatches the transport. + */ + if (READ_ONCE(xprt->xpt_flags) & + (BIT(XPT_CONN) | BIT(XPT_CLOSE) | BIT(XPT_HANDSHAKE) | + BIT(XPT_DATA) | BIT(XPT_DEFERRED))) + svc_xprt_enqueue(xprt); + svc_xprt_put(xprt); } EXPORT_SYMBOL_GPL(svc_xprt_received); @@ -425,13 +437,35 @@ static bool svc_xprt_reserve_slot(struct svc_rqst *rqstp, struct svc_xprt *xprt) return true; } +/* + * After a caller releases write-space or a request slot, + * re-enqueue the transport only when there is pending + * work that a thread could act on. The smp_mb() pairs + * with the smp_rmb() in svc_xprt_ready() and orders the + * preceding counter update before the flags read so a + * concurrent set_bit(XPT_DATA) is visible here. + * + * When the transport is BUSY, the thread holding it will + * call svc_xprt_received() upon completion, which checks + * for pending work and re-enqueues as needed. + */ +static void svc_xprt_resource_released(struct svc_xprt *xprt) +{ + unsigned long xpt_flags; + + smp_mb(); + xpt_flags = READ_ONCE(xprt->xpt_flags); + if (xpt_flags & (BIT(XPT_DATA) | BIT(XPT_DEFERRED)) && + !(xpt_flags & BIT(XPT_BUSY))) + svc_xprt_enqueue(xprt); +} + static void svc_xprt_release_slot(struct svc_rqst *rqstp) { struct svc_xprt *xprt = rqstp->rq_xprt; if (test_and_clear_bit(RQ_DATA, &rqstp->rq_flags)) { atomic_dec(&xprt->xpt_nr_rqsts); - smp_wmb(); /* See smp_rmb() in svc_xprt_ready() */ - svc_xprt_enqueue(xprt); + svc_xprt_resource_released(xprt); } } @@ -525,10 +559,10 @@ void svc_reserve(struct svc_rqst *rqstp, int space) space += rqstp->rq_res.head[0].iov_len; if (xprt && space < rqstp->rq_reserved) { - atomic_sub((rqstp->rq_reserved - space), &xprt->xpt_reserved); + atomic_sub((rqstp->rq_reserved - space), + &xprt->xpt_reserved); rqstp->rq_reserved = space; - smp_wmb(); /* See smp_rmb() in svc_xprt_ready() */ - svc_xprt_enqueue(xprt); + svc_xprt_resource_released(xprt); } } EXPORT_SYMBOL_GPL(svc_reserve); diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 3be69c145d2a..64a2658faddb 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -17,11 +17,14 @@ #include <net/ipv6.h> #include <linux/kernel.h> #include <linux/user_namespace.h> +#include <net/genetlink.h> +#include <uapi/linux/sunrpc_netlink.h> #include <trace/events/sunrpc.h> #define RPCDBG_FACILITY RPCDBG_AUTH #include "netns.h" +#include "netlink.h" /* * AUTHUNIX and AUTHNULL credentials are both handled here. @@ -152,7 +155,7 @@ static struct cache_head *ip_map_alloc(void) static int ip_map_upcall(struct cache_detail *cd, struct cache_head *h) { - return sunrpc_cache_pipe_upcall(cd, h); + return sunrpc_cache_upcall(cd, h); } static void ip_map_request(struct cache_detail *cd, @@ -467,7 +470,7 @@ static struct cache_head *unix_gid_alloc(void) static int unix_gid_upcall(struct cache_detail *cd, struct cache_head *h) { - return sunrpc_cache_pipe_upcall_timeout(cd, h); + return sunrpc_cache_upcall_warn(cd, h); } static void unix_gid_request(struct cache_detail *cd, @@ -582,12 +585,278 @@ static int unix_gid_show(struct seq_file *m, return 0; } +static int unix_gid_notify(struct cache_detail *cd, struct cache_head *h) +{ + return sunrpc_cache_notify(cd, h, SUNRPC_CACHE_TYPE_UNIX_GID); +} + +/** + * sunrpc_nl_unix_gid_get_reqs_dumpit - dump pending unix_gid requests + * @skb: reply buffer + * @cb: netlink metadata and command arguments + * + * Walk the unix_gid cache's pending request list and create a netlink + * message with a nested entry for each cache_request, containing the + * seqno and uid. + * + * Uses cb->args[0] as a seqno cursor for dump continuation across + * multiple netlink messages. + * + * Returns the size of the reply or a negative errno. + */ +int sunrpc_nl_unix_gid_get_reqs_dumpit(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct sunrpc_net *sn; + struct cache_detail *cd; + struct cache_head **items; + u64 *seqnos; + int cnt, i, emitted; + void *hdr; + int ret; + + sn = net_generic(sock_net(skb->sk), sunrpc_net_id); + + cd = sn->unix_gid_cache; + if (!cd) + return -ENODEV; + + cnt = sunrpc_cache_requests_count(cd); + if (!cnt) + return 0; + + items = kcalloc(cnt, sizeof(*items), GFP_KERNEL); + seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL); + if (!items || !seqnos) { + ret = -ENOMEM; + goto out_alloc; + } + + cnt = sunrpc_cache_requests_snapshot(cd, items, seqnos, cnt, + cb->args[0]); + if (!cnt) { + ret = 0; + goto out_alloc; + } + + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, &sunrpc_nl_family, + NLM_F_MULTI, SUNRPC_CMD_UNIX_GID_GET_REQS); + if (!hdr) { + ret = -ENOBUFS; + goto out_put; + } + + emitted = 0; + for (i = 0; i < cnt; i++) { + struct unix_gid *ug; + struct nlattr *nest; + + ug = container_of(items[i], struct unix_gid, h); + + nest = nla_nest_start(skb, + SUNRPC_A_UNIX_GID_REQS_REQUESTS); + if (!nest) + break; + + if (nla_put_u64_64bit(skb, SUNRPC_A_UNIX_GID_SEQNO, + seqnos[i], 0) || + nla_put_u32(skb, SUNRPC_A_UNIX_GID_UID, + from_kuid(&init_user_ns, ug->uid))) { + nla_nest_cancel(skb, nest); + break; + } + + nla_nest_end(skb, nest); + cb->args[0] = seqnos[i]; + emitted++; + } + + if (!emitted) { + genlmsg_cancel(skb, hdr); + ret = -EMSGSIZE; + goto out_put; + } + + genlmsg_end(skb, hdr); + ret = skb->len; +out_put: + for (i = 0; i < cnt; i++) + cache_put(items[i], cd); +out_alloc: + kfree(seqnos); + kfree(items); + return ret; +} + +/** + * sunrpc_nl_parse_one_unix_gid - parse one unix_gid entry from netlink + * @cd: cache_detail for the unix_gid cache + * @attr: nested attribute containing unix_gid fields + * + * Parses one unix_gid entry from a netlink message and updates the + * cache. Mirrors the logic in unix_gid_parse(). + * + * Returns 0 on success or a negative errno. + */ +static int sunrpc_nl_parse_one_unix_gid(struct cache_detail *cd, + struct nlattr *attr) +{ + struct nlattr *tb[SUNRPC_A_UNIX_GID_EXPIRY + 1]; + struct unix_gid ug, *ugp; + struct timespec64 boot; + struct nlattr *gid_attr; + int err, rem, gids = 0; + kuid_t uid; + + err = nla_parse_nested(tb, SUNRPC_A_UNIX_GID_EXPIRY, attr, + sunrpc_unix_gid_nl_policy, NULL); + if (err) + return err; + + /* uid (required) */ + if (!tb[SUNRPC_A_UNIX_GID_UID]) + return -EINVAL; + uid = make_kuid(current_user_ns(), + nla_get_u32(tb[SUNRPC_A_UNIX_GID_UID])); + ug.uid = uid; + + /* expiry (required, wallclock seconds) */ + if (!tb[SUNRPC_A_UNIX_GID_EXPIRY]) + return -EINVAL; + getboottime64(&boot); + ug.h.flags = 0; + ug.h.expiry_time = nla_get_u64(tb[SUNRPC_A_UNIX_GID_EXPIRY]) - + boot.tv_sec; + + if (tb[SUNRPC_A_UNIX_GID_NEGATIVE]) { + ug.gi = groups_alloc(0); + if (!ug.gi) + return -ENOMEM; + } else { + /* Count gids */ + nla_for_each_nested_type(gid_attr, SUNRPC_A_UNIX_GID_GIDS, + attr, rem) + gids++; + + if (gids > 8192) + return -EINVAL; + + ug.gi = groups_alloc(gids); + if (!ug.gi) + return -ENOMEM; + + gids = 0; + nla_for_each_nested_type(gid_attr, SUNRPC_A_UNIX_GID_GIDS, + attr, rem) { + kgid_t kgid; + + kgid = make_kgid(current_user_ns(), + nla_get_u32(gid_attr)); + if (!gid_valid(kgid)) { + err = -EINVAL; + goto out; + } + ug.gi->gid[gids++] = kgid; + } + groups_sort(ug.gi); + } + + ugp = unix_gid_lookup(cd, uid); + if (ugp) { + struct cache_head *ch; + + ch = sunrpc_cache_update(cd, &ug.h, &ugp->h, + unix_gid_hash(uid)); + if (!ch) { + err = -ENOMEM; + } else { + err = 0; + cache_put(ch, cd); + } + } else { + err = -ENOMEM; + } +out: + if (ug.gi) + put_group_info(ug.gi); + return err; +} + +/** + * sunrpc_nl_unix_gid_set_reqs_doit - respond to unix_gid requests + * @skb: reply buffer + * @info: netlink metadata and command arguments + * + * Parse one or more unix_gid cache responses from userspace and + * update the unix_gid cache accordingly. + * + * Returns 0 on success or a negative errno. + */ +int sunrpc_nl_unix_gid_set_reqs_doit(struct sk_buff *skb, + struct genl_info *info) +{ + struct sunrpc_net *sn; + struct cache_detail *cd; + const struct nlattr *attr; + int rem, ret = 0; + + sn = net_generic(genl_info_net(info), sunrpc_net_id); + + cd = sn->unix_gid_cache; + if (!cd) + return -ENODEV; + + nlmsg_for_each_attr_type(attr, SUNRPC_A_UNIX_GID_REQS_REQUESTS, + info->nlhdr, GENL_HDRLEN, rem) { + ret = sunrpc_nl_parse_one_unix_gid(cd, + (struct nlattr *)attr); + if (ret) + break; + } + + return ret; +} + +/** + * sunrpc_nl_cache_flush_doit - flush sunrpc caches via netlink + * @skb: reply buffer + * @info: netlink metadata and command arguments + * + * Flush the ip_map and/or unix_gid caches. If SUNRPC_A_CACHE_FLUSH_MASK + * is provided, only flush the caches indicated by the bitmask (bit 1 = + * ip_map, bit 2 = unix_gid). If omitted, flush both. + * + * Return 0 on success or a negative errno. + */ +int sunrpc_nl_cache_flush_doit(struct sk_buff *skb, struct genl_info *info) +{ + struct sunrpc_net *sn; + u32 mask = ~0U; + + sn = net_generic(genl_info_net(info), sunrpc_net_id); + + if (info->attrs[SUNRPC_A_CACHE_FLUSH_MASK]) + mask = nla_get_u32(info->attrs[SUNRPC_A_CACHE_FLUSH_MASK]); + + if ((mask & SUNRPC_CACHE_TYPE_IP_MAP) && + sn->ip_map_cache) + cache_purge(sn->ip_map_cache); + + if ((mask & SUNRPC_CACHE_TYPE_UNIX_GID) && + sn->unix_gid_cache) + cache_purge(sn->unix_gid_cache); + + return 0; +} + static const struct cache_detail unix_gid_cache_template = { .owner = THIS_MODULE, .hash_size = GID_HASHMAX, .name = "auth.unix.gid", .cache_put = unix_gid_put, .cache_upcall = unix_gid_upcall, + .cache_notify = unix_gid_notify, .cache_request = unix_gid_request, .cache_parse = unix_gid_parse, .cache_show = unix_gid_show, @@ -1017,12 +1286,255 @@ struct auth_ops svcauth_unix = { .set_client = svcauth_unix_set_client, }; +static int ip_map_notify(struct cache_detail *cd, struct cache_head *h) +{ + return sunrpc_cache_notify(cd, h, SUNRPC_CACHE_TYPE_IP_MAP); +} + +/** + * sunrpc_nl_ip_map_get_reqs_dumpit - dump pending ip_map requests + * @skb: reply buffer + * @cb: netlink metadata and command arguments + * + * Walk the ip_map cache's pending request list and create a netlink + * message with a nested entry for each cache_request, containing the + * seqno, class and addr. + * + * Uses cb->args[0] as a seqno cursor for dump continuation across + * multiple netlink messages. + * + * Returns the size of the reply or a negative errno. + */ +int sunrpc_nl_ip_map_get_reqs_dumpit(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct sunrpc_net *sn; + struct cache_detail *cd; + struct cache_head **items; + u64 *seqnos; + int cnt, i, emitted; + void *hdr; + int ret; + + sn = net_generic(sock_net(skb->sk), sunrpc_net_id); + + cd = sn->ip_map_cache; + if (!cd) + return -ENODEV; + + cnt = sunrpc_cache_requests_count(cd); + if (!cnt) + return 0; + + items = kcalloc(cnt, sizeof(*items), GFP_KERNEL); + seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL); + if (!items || !seqnos) { + ret = -ENOMEM; + goto out_alloc; + } + + cnt = sunrpc_cache_requests_snapshot(cd, items, seqnos, cnt, + cb->args[0]); + if (!cnt) { + ret = 0; + goto out_alloc; + } + + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, &sunrpc_nl_family, + NLM_F_MULTI, SUNRPC_CMD_IP_MAP_GET_REQS); + if (!hdr) { + ret = -ENOBUFS; + goto out_put; + } + + emitted = 0; + for (i = 0; i < cnt; i++) { + struct ip_map *im; + struct nlattr *nest; + char text_addr[40]; + + im = container_of(items[i], struct ip_map, h); + + if (ipv6_addr_v4mapped(&im->m_addr)) + snprintf(text_addr, 20, "%pI4", + &im->m_addr.s6_addr32[3]); + else + snprintf(text_addr, 40, "%pI6", &im->m_addr); + + nest = nla_nest_start(skb, SUNRPC_A_IP_MAP_REQS_REQUESTS); + if (!nest) + break; + + if (nla_put_u64_64bit(skb, SUNRPC_A_IP_MAP_SEQNO, + seqnos[i], 0) || + nla_put_string(skb, SUNRPC_A_IP_MAP_CLASS, + im->m_class) || + nla_put_string(skb, SUNRPC_A_IP_MAP_ADDR, text_addr)) { + nla_nest_cancel(skb, nest); + break; + } + + nla_nest_end(skb, nest); + cb->args[0] = seqnos[i]; + emitted++; + } + + if (!emitted) { + genlmsg_cancel(skb, hdr); + ret = -EMSGSIZE; + goto out_put; + } + + genlmsg_end(skb, hdr); + ret = skb->len; +out_put: + for (i = 0; i < cnt; i++) + cache_put(items[i], cd); +out_alloc: + kfree(seqnos); + kfree(items); + return ret; +} + +/** + * sunrpc_nl_parse_one_ip_map - parse one ip_map entry from netlink + * @cd: cache_detail for the ip_map cache + * @attr: nested attribute containing ip_map fields + * + * Parses one ip_map entry from a netlink message and updates the + * cache. Mirrors the logic in ip_map_parse(). + * + * Returns 0 on success or a negative errno. + */ +static int sunrpc_nl_parse_one_ip_map(struct cache_detail *cd, + struct nlattr *attr) +{ + struct nlattr *tb[SUNRPC_A_IP_MAP_EXPIRY + 1]; + union { + struct sockaddr sa; + struct sockaddr_in s4; + struct sockaddr_in6 s6; + } address; + struct sockaddr_in6 sin6; + struct ip_map *ipmp; + struct auth_domain *dom = NULL; + struct unix_domain *udom = NULL; + struct timespec64 boot; + time64_t expiry; + char class[8]; + int err; + int len; + + err = nla_parse_nested(tb, SUNRPC_A_IP_MAP_EXPIRY, attr, + sunrpc_ip_map_nl_policy, NULL); + if (err) + return err; + + /* class (required) */ + if (!tb[SUNRPC_A_IP_MAP_CLASS]) + return -EINVAL; + len = nla_len(tb[SUNRPC_A_IP_MAP_CLASS]); + if (len <= 0 || len > sizeof(class)) + return -EINVAL; + nla_strscpy(class, tb[SUNRPC_A_IP_MAP_CLASS], sizeof(class)); + + /* addr (required) */ + if (!tb[SUNRPC_A_IP_MAP_ADDR]) + return -EINVAL; + if (rpc_pton(cd->net, nla_data(tb[SUNRPC_A_IP_MAP_ADDR]), + nla_len(tb[SUNRPC_A_IP_MAP_ADDR]) - 1, + &address.sa, sizeof(address)) == 0) + return -EINVAL; + + switch (address.sa.sa_family) { + case AF_INET: + sin6.sin6_family = AF_INET6; + ipv6_addr_set_v4mapped(address.s4.sin_addr.s_addr, + &sin6.sin6_addr); + break; +#if IS_ENABLED(CONFIG_IPV6) + case AF_INET6: + memcpy(&sin6, &address.s6, sizeof(sin6)); + break; +#endif + default: + return -EINVAL; + } + + /* expiry (required, wallclock seconds) */ + if (!tb[SUNRPC_A_IP_MAP_EXPIRY]) + return -EINVAL; + getboottime64(&boot); + expiry = nla_get_u64(tb[SUNRPC_A_IP_MAP_EXPIRY]) - boot.tv_sec; + + /* domain name or negative */ + if (tb[SUNRPC_A_IP_MAP_NEGATIVE]) { + udom = NULL; + } else if (tb[SUNRPC_A_IP_MAP_DOMAIN]) { + dom = unix_domain_find(nla_data(tb[SUNRPC_A_IP_MAP_DOMAIN])); + if (!dom) + return -ENOENT; + udom = container_of(dom, struct unix_domain, h); + } else { + return -EINVAL; + } + + ipmp = __ip_map_lookup(cd, class, &sin6.sin6_addr); + if (ipmp) + err = __ip_map_update(cd, ipmp, udom, expiry); + else + err = -ENOMEM; + + if (dom) + auth_domain_put(dom); + + cache_flush(); + return err; +} + +/** + * sunrpc_nl_ip_map_set_reqs_doit - respond to ip_map requests + * @skb: reply buffer + * @info: netlink metadata and command arguments + * + * Parse one or more ip_map cache responses from userspace and + * update the ip_map cache accordingly. + * + * Returns 0 on success or a negative errno. + */ +int sunrpc_nl_ip_map_set_reqs_doit(struct sk_buff *skb, + struct genl_info *info) +{ + struct sunrpc_net *sn; + struct cache_detail *cd; + const struct nlattr *attr; + int rem, ret = 0; + + sn = net_generic(genl_info_net(info), sunrpc_net_id); + + cd = sn->ip_map_cache; + if (!cd) + return -ENODEV; + + nlmsg_for_each_attr_type(attr, SUNRPC_A_IP_MAP_REQS_REQUESTS, + info->nlhdr, GENL_HDRLEN, rem) { + ret = sunrpc_nl_parse_one_ip_map(cd, + (struct nlattr *)attr); + if (ret) + break; + } + + return ret; +} + static const struct cache_detail ip_map_cache_template = { .owner = THIS_MODULE, .hash_size = IP_HASHMAX, .name = "auth.unix.ip", .cache_put = ip_map_put, .cache_upcall = ip_map_upcall, + .cache_notify = ip_map_notify, .cache_request = ip_map_request, .cache_parse = ip_map_parse, .cache_show = ip_map_show, diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 7be3de1a1aed..50e5e7f5b762 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -471,6 +471,7 @@ static void svc_tcp_handshake_done(void *data, int status, key_serial_t peerid) } clear_bit(XPT_HANDSHAKE, &xprt->xpt_flags); complete_all(&svsk->sk_handshake_done); + svc_xprt_put(xprt); } /** @@ -494,9 +495,13 @@ static void svc_tcp_handshake(struct svc_xprt *xprt) clear_bit(XPT_TLS_SESSION, &xprt->xpt_flags); init_completion(&svsk->sk_handshake_done); + /* Pin the transport across the asynchronous handshake callback. */ + svc_xprt_get(xprt); + ret = tls_server_hello_x509(&args, GFP_KERNEL); if (ret) { trace_svc_tls_not_started(xprt); + svc_xprt_put(xprt); goto out_failed; } @@ -505,8 +510,13 @@ static void svc_tcp_handshake(struct svc_xprt *xprt) if (ret <= 0) { if (tls_handshake_cancel(sk)) { trace_svc_tls_timed_out(xprt); + svc_xprt_put(xprt); goto out_close; } + /* Cancellation lost to handshake_complete(): the + * callback is in flight and should finish quickly. + */ + wait_for_completion(&svsk->sk_handshake_done); } if (!test_bit(XPT_TLS_SESSION, &xprt->xpt_flags)) { @@ -732,7 +742,7 @@ static int svc_udp_sendto(struct svc_rqst *rqstp) .msg_flags = MSG_SPLICE_PAGES, .msg_controllen = sizeof(buffer), }; - unsigned int count; + int count; int err; svc_udp_release_ctxt(xprt, rqstp->rq_xprt_ctxt); @@ -746,6 +756,10 @@ static int svc_udp_sendto(struct svc_rqst *rqstp) goto out_notconn; count = xdr_buf_to_bvec(svsk->sk_bvec, SUNRPC_MAX_UDP_SENDPAGES, xdr); + if (count < 0) { + err = count; + goto out_trace; + } iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec, count, rqstp->rq_res.len); @@ -757,6 +771,7 @@ static int svc_udp_sendto(struct svc_rqst *rqstp) err = sock_sendmsg(svsk->sk_sock, &msg); } +out_trace: trace_svcsock_udp_send(xprt, err); mutex_unlock(&xprt->xpt_mutex); @@ -1237,7 +1252,7 @@ static int svc_tcp_sendmsg(struct svc_sock *svsk, struct svc_rqst *rqstp, struct msghdr msg = { .msg_flags = MSG_SPLICE_PAGES, }; - unsigned int count; + int count; void *buf; int ret; @@ -1253,10 +1268,15 @@ static int svc_tcp_sendmsg(struct svc_sock *svsk, struct svc_rqst *rqstp, count = xdr_buf_to_bvec(svsk->sk_bvec + 1, rqstp->rq_maxpages, &rqstp->rq_res); + if (count < 0) { + ret = count; + goto out; + } iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec, 1 + count, sizeof(marker) + rqstp->rq_res.len); ret = sock_sendmsg(svsk->sk_sock, &msg); +out: page_frag_free(buf); return ret; } diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index e83d5d0be78b..fa6a30b5f046 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -139,19 +139,22 @@ xdr_free_bvec(struct xdr_buf *buf) /** * xdr_buf_to_bvec - Copy components of an xdr_buf into a bio_vec array * @bvec: bio_vec array to populate - * @bvec_size: element count of @bio_vec + * @bvec_size: element count of @bvec * @xdr: xdr_buf to be copied * - * Returns the number of entries consumed in @bvec. + * Returns the number of entries consumed in @bvec on success, or + * -ESERVERFAULT when @xdr does not fit within @bvec_size entries. */ -unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, - const struct xdr_buf *xdr) +int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, + const struct xdr_buf *xdr) { const struct kvec *head = xdr->head; const struct kvec *tail = xdr->tail; unsigned int count = 0; if (head->iov_len) { + if (unlikely(count >= bvec_size)) + goto bvec_overflow; bvec_set_virt(bvec++, head->iov_base, head->iov_len); ++count; } @@ -165,29 +168,230 @@ unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, while (remaining > 0) { len = min_t(unsigned int, remaining, PAGE_SIZE - offset); + if (unlikely(count >= bvec_size)) + goto bvec_overflow; bvec_set_page(bvec++, *pages++, len, offset); remaining -= len; offset = 0; - if (unlikely(++count > bvec_size)) - goto bvec_overflow; + ++count; } } if (tail->iov_len) { - bvec_set_virt(bvec, tail->iov_base, tail->iov_len); - if (unlikely(++count > bvec_size)) + if (unlikely(count >= bvec_size)) goto bvec_overflow; + bvec_set_virt(bvec, tail->iov_base, tail->iov_len); + ++count; } return count; bvec_overflow: pr_warn_once("%s: bio_vec array overflow\n", __func__); - return count - 1; + return -ESERVERFAULT; } EXPORT_SYMBOL_GPL(xdr_buf_to_bvec); /** + * xdr_buf_to_sg - Populate a scatterlist from an xdr_buf range + * @buf: xdr_buf to map + * @offset: starting byte offset within @buf + * @len: number of bytes to cover + * @sg: scatterlist array initialized with sg_init_table() + * @nsg: number of entries available in @sg + * + * @sg is traversed with sg_next(), so callers may pass a list + * assembled with sg_chain(). + * + * Return: on success, the number of scatterlist entries used; the + * last used entry is marked with sg_mark_end(). On failure, a + * negative errno. + */ +int xdr_buf_to_sg(const struct xdr_buf *buf, unsigned int offset, + unsigned int len, struct scatterlist *sg, unsigned int nsg) +{ + unsigned int page_len, thislen, page_offset; + struct scatterlist *cur = sg, *prev = NULL; + int nents = 0; + int i; + + if (len == 0) + return 0; + + if (offset >= buf->head[0].iov_len) { + offset -= buf->head[0].iov_len; + } else { + thislen = min_t(unsigned int, + buf->head[0].iov_len - offset, len); + if (nents >= nsg) + return -ENOSPC; + sg_set_buf(cur, buf->head[0].iov_base + offset, + thislen); + prev = cur; + cur = sg_next(cur); + nents++; + len -= thislen; + offset = 0; + } + if (len == 0) + goto done; + + if (offset >= buf->page_len) { + offset -= buf->page_len; + } else { + page_len = min(buf->page_len - offset, len); + len -= page_len; + page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1); + i = (offset + buf->page_base) >> PAGE_SHIFT; + thislen = PAGE_SIZE - page_offset; + do { + if (thislen > page_len) + thislen = page_len; + if (nents >= nsg) + return -ENOSPC; + sg_set_page(cur, buf->pages[i], + thislen, page_offset); + prev = cur; + cur = sg_next(cur); + nents++; + page_len -= thislen; + i++; + page_offset = 0; + thislen = PAGE_SIZE; + } while (page_len != 0); + offset = 0; + } + if (len == 0) + goto done; + + if (offset < buf->tail[0].iov_len) { + thislen = min_t(unsigned int, + buf->tail[0].iov_len - offset, len); + if (nents >= nsg) + return -ENOSPC; + sg_set_buf(cur, buf->tail[0].iov_base + offset, + thislen); + prev = cur; + nents++; + len -= thislen; + } + if (len != 0) + return -EINVAL; + +done: + if (prev) + sg_mark_end(prev); + return nents; +} +EXPORT_SYMBOL_GPL(xdr_buf_to_sg); + +/* + * Count the scatterlist entries needed to cover [offset, offset + len) + * within @buf. Mirrors the walk in xdr_buf_to_sg() so the caller can + * size an allocation that matches the requested sub-range rather than + * the full xdr_buf. + */ +static unsigned int xdr_buf_sg_nents(const struct xdr_buf *buf, + unsigned int offset, unsigned int len) +{ + unsigned int nsg = 0, thislen, page_offset; + + if (len == 0) + return 0; + + if (offset < buf->head[0].iov_len) { + thislen = min_t(unsigned int, + buf->head[0].iov_len - offset, len); + nsg++; + len -= thislen; + offset = 0; + } else { + offset -= buf->head[0].iov_len; + } + if (len == 0) + return nsg; + + if (offset < buf->page_len) { + thislen = min(buf->page_len - offset, len); + page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1); + nsg += DIV_ROUND_UP(page_offset + thislen, PAGE_SIZE); + len -= thislen; + offset = 0; + } else { + offset -= buf->page_len; + } + if (len == 0) + return nsg; + + if (offset < buf->tail[0].iov_len) + nsg++; + return nsg; +} + +/** + * xdr_buf_to_sg_alloc - Populate a scatterlist for an xdr_buf range + * @buf: xdr_buf to map + * @offset: starting byte offset within @buf + * @len: number of bytes to cover + * @sg_head: caller-provided scatterlist array (typically stack-allocated) + * @sg_head_nents: number of entries in @sg_head + * @sg_overflow: OUT: chained extension, or NULL when @sg_head sufficed + * @gfp: memory allocation flags for overflow + * + * Populates @sg_head directly when the xdr_buf fits. When more + * entries are needed, an overflow scatterlist is allocated and + * chained from @sg_head so that the result is traversable with + * sg_next(). + * + * Return: on success, the number of populated scatterlist entries + * (counting only data entries, not chain entries). @sg_head is + * the head of the resulting list. Caller must kfree @sg_overflow + * when done. On failure, a negative errno. + */ +int xdr_buf_to_sg_alloc(const struct xdr_buf *buf, unsigned int offset, + unsigned int len, struct scatterlist *sg_head, + unsigned int sg_head_nents, + struct scatterlist **sg_overflow, gfp_t gfp) +{ + unsigned int nsg; + int ret; + + *sg_overflow = NULL; + if (len == 0) + return 0; + + nsg = xdr_buf_sg_nents(buf, offset, len); + if (nsg == 0) + return -EINVAL; + + if (nsg <= sg_head_nents) { + sg_init_table(sg_head, nsg); + } else { + /* +1 replaces the slot sg_chain() consumes as the link. */ + unsigned int overflow_nents = nsg - sg_head_nents + 1; + struct scatterlist *overflow; + + overflow = kmalloc_array(overflow_nents, sizeof(*overflow), + gfp); + if (!overflow) + return -ENOMEM; + + sg_init_table(sg_head, sg_head_nents); + sg_init_table(overflow, overflow_nents); + sg_chain(sg_head, sg_head_nents, overflow); + *sg_overflow = overflow; + } + + ret = xdr_buf_to_sg(buf, offset, len, sg_head, nsg); + if (ret < 0) { + kfree(*sg_overflow); + *sg_overflow = NULL; + } + return ret; +} +EXPORT_SYMBOL_GPL(xdr_buf_to_sg_alloc); + +/** * xdr_inline_pages - Prepare receive buffer for a large reply * @xdr: xdr_buf into which reply will be placed * @offset: expected offset where data payload will start, in bytes @@ -2149,73 +2353,6 @@ int xdr_encode_array2(const struct xdr_buf *buf, unsigned int base, } EXPORT_SYMBOL_GPL(xdr_encode_array2); -int xdr_process_buf(const struct xdr_buf *buf, unsigned int offset, - unsigned int len, - int (*actor)(struct scatterlist *, void *), void *data) -{ - int i, ret = 0; - unsigned int page_len, thislen, page_offset; - struct scatterlist sg[1]; - - sg_init_table(sg, 1); - - if (offset >= buf->head[0].iov_len) { - offset -= buf->head[0].iov_len; - } else { - thislen = buf->head[0].iov_len - offset; - if (thislen > len) - thislen = len; - sg_set_buf(sg, buf->head[0].iov_base + offset, thislen); - ret = actor(sg, data); - if (ret) - goto out; - offset = 0; - len -= thislen; - } - if (len == 0) - goto out; - - if (offset >= buf->page_len) { - offset -= buf->page_len; - } else { - page_len = buf->page_len - offset; - if (page_len > len) - page_len = len; - len -= page_len; - page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1); - i = (offset + buf->page_base) >> PAGE_SHIFT; - thislen = PAGE_SIZE - page_offset; - do { - if (thislen > page_len) - thislen = page_len; - sg_set_page(sg, buf->pages[i], thislen, page_offset); - ret = actor(sg, data); - if (ret) - goto out; - page_len -= thislen; - i++; - page_offset = 0; - thislen = PAGE_SIZE; - } while (page_len != 0); - offset = 0; - } - if (len == 0) - goto out; - if (offset < buf->tail[0].iov_len) { - thislen = buf->tail[0].iov_len - offset; - if (thislen > len) - thislen = len; - sg_set_buf(sg, buf->tail[0].iov_base + offset, thislen); - ret = actor(sg, data); - len -= thislen; - } - if (len != 0) - ret = -EINVAL; -out: - return ret; -} -EXPORT_SYMBOL_GPL(xdr_process_buf); - /** * xdr_stream_decode_string_dup - Decode and duplicate variable length string * @xdr: pointer to xdr_stream diff --git a/net/sunrpc/xprtrdma/svc_rdma.c b/net/sunrpc/xprtrdma/svc_rdma.c index 415c0310101f..f67f0612b1a9 100644 --- a/net/sunrpc/xprtrdma/svc_rdma.c +++ b/net/sunrpc/xprtrdma/svc_rdma.c @@ -264,38 +264,22 @@ err: return rc; } -struct workqueue_struct *svcrdma_wq; - void svc_rdma_cleanup(void) { svc_unreg_xprt_class(&svc_rdma_class); svc_rdma_proc_cleanup(); - if (svcrdma_wq) { - struct workqueue_struct *wq = svcrdma_wq; - - svcrdma_wq = NULL; - destroy_workqueue(wq); - } dprintk("SVCRDMA Module Removed, deregister RPC RDMA transport\n"); } int svc_rdma_init(void) { - struct workqueue_struct *wq; int rc; - wq = alloc_workqueue("svcrdma", WQ_UNBOUND, 0); - if (!wq) - return -ENOMEM; - rc = svc_rdma_proc_init(); - if (rc) { - destroy_workqueue(wq); + if (rc) return rc; - } - svcrdma_wq = wq; svc_reg_xprt_class(&svc_rdma_class); dprintk("SVCRDMA Module Init, register RPC RDMA transport\n"); diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index f8a0638eb095..fe9bf0371b6e 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -242,6 +242,10 @@ void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma, * Ensure that the recv_ctxt is released whether or not a Reply * was sent. For example, the client could close the connection, * or svc_process could drop an RPC, before the Reply is sent. + * + * Also drain any send_ctxts queued for deferred release so that + * DMA unmap and page release run in nfsd thread context between + * RPCs rather than on the Send completion path. */ void svc_rdma_release_ctxt(struct svc_xprt *xprt, void *vctxt) { @@ -251,6 +255,8 @@ void svc_rdma_release_ctxt(struct svc_xprt *xprt, void *vctxt) if (ctxt) svc_rdma_recv_ctxt_put(rdma, ctxt); + + svc_rdma_send_ctxts_drain(rdma); } static bool svc_rdma_refresh_recvs(struct svcxprt_rdma *rdma, @@ -377,13 +383,16 @@ flushed: trace_svcrdma_wc_recv_err(wc, &ctxt->rc_cid); dropped: svc_rdma_recv_ctxt_put(rdma, ctxt); - svc_xprt_deferred_close(&rdma->sc_xprt); + svc_rdma_xprt_deferred_close(rdma); } /** * svc_rdma_flush_recv_queues - Drain pending Receive work * @rdma: svcxprt_rdma being shut down * + * Caller must guarantee that @rdma's Send and Recv Completion + * Queues are empty (e.g., via ib_drain_qp()), so that no completion + * handlers can still produce work on the queues being drained. */ void svc_rdma_flush_recv_queues(struct svcxprt_rdma *rdma) { @@ -1001,7 +1010,7 @@ out_readlist: if (ret == -EINVAL) svc_rdma_send_error(rdma_xprt, ctxt, ret); svc_rdma_recv_ctxt_put(rdma_xprt, ctxt); - svc_xprt_deferred_close(xprt); + svc_rdma_xprt_deferred_close(rdma_xprt); return ret; } return 0; diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index 402e2ceca4ff..f7fd22cc4a59 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c @@ -236,21 +236,12 @@ svc_rdma_write_info_alloc(struct svcxprt_rdma *rdma, return info; } -static void svc_rdma_write_info_free_async(struct work_struct *work) +static void svc_rdma_write_info_free(struct svc_rdma_write_info *info) { - struct svc_rdma_write_info *info; - - info = container_of(work, struct svc_rdma_write_info, wi_work); svc_rdma_cc_release(info->wi_rdma, &info->wi_cc, DMA_TO_DEVICE); kfree(info); } -static void svc_rdma_write_info_free(struct svc_rdma_write_info *info) -{ - INIT_WORK(&info->wi_work, svc_rdma_write_info_free_async); - queue_work(svcrdma_wq, &info->wi_work); -} - /** * svc_rdma_write_chunk_release - Release Write chunk I/O resources * @rdma: controlling transport @@ -313,7 +304,7 @@ static void svc_rdma_reply_done(struct ib_cq *cq, struct ib_wc *wc) trace_svcrdma_wc_reply_err(wc, &cc->cc_cid); } - svc_xprt_deferred_close(&rdma->sc_xprt); + svc_rdma_xprt_deferred_close(rdma); } /** @@ -345,7 +336,7 @@ static void svc_rdma_write_done(struct ib_cq *cq, struct ib_wc *wc) * some of the outgoing RPC message. Signal the loss * to the client by closing the connection. */ - svc_xprt_deferred_close(&rdma->sc_xprt); + svc_rdma_xprt_deferred_close(rdma); } /** @@ -390,7 +381,7 @@ static void svc_rdma_wc_read_done(struct ib_cq *cq, struct ib_wc *wc) */ svc_rdma_cc_release(rdma, cc, DMA_FROM_DEVICE); svc_rdma_recv_ctxt_put(rdma, ctxt); - svc_xprt_deferred_close(&rdma->sc_xprt); + svc_rdma_xprt_deferred_close(rdma); } /* @@ -754,216 +745,6 @@ int svc_rdma_prepare_reply_chunk(struct svcxprt_rdma *rdma, return xdr->len; } -/* - * Cap contiguous RDMA Read sink allocations at order-4. - * Higher orders risk allocation failure under - * __GFP_NORETRY, which would negate the benefit of the - * contiguous fast path. - */ -#define SVC_RDMA_CONTIG_MAX_ORDER 4 - -/** - * svc_rdma_alloc_read_pages - Allocate physically contiguous pages - * @nr_pages: number of pages needed - * @order: on success, set to the allocation order - * - * Attempts a higher-order allocation, falling back to smaller orders. - * The returned pages are split immediately so each sub-page has its - * own refcount and can be freed independently. - * - * Returns a pointer to the first page on success, or NULL if even - * order-1 allocation fails. - */ -static struct page * -svc_rdma_alloc_read_pages(unsigned int nr_pages, unsigned int *order) -{ - unsigned int o; - struct page *page; - - o = min(get_order(nr_pages << PAGE_SHIFT), - SVC_RDMA_CONTIG_MAX_ORDER); - - while (o >= 1) { - page = alloc_pages(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN, - o); - if (page) { - split_page(page, o); - *order = o; - return page; - } - o--; - } - return NULL; -} - -/* - * svc_rdma_fill_contig_bvec - Replace rq_pages with a contiguous allocation - * @rqstp: RPC transaction context - * @head: context for ongoing I/O - * @bv: bvec entry to fill - * @pages_left: number of data pages remaining in the segment - * @len_left: bytes remaining in the segment - * - * On success, fills @bv with a bvec spanning the contiguous range and - * advances rc_curpage/rc_page_count. Returns the byte length covered, - * or zero if the allocation failed or would overrun rq_maxpages. - */ -static unsigned int -svc_rdma_fill_contig_bvec(struct svc_rqst *rqstp, - struct svc_rdma_recv_ctxt *head, - struct bio_vec *bv, unsigned int pages_left, - unsigned int len_left) -{ - unsigned int order, npages, chunk_pages, chunk_len, i; - struct page *page; - - page = svc_rdma_alloc_read_pages(pages_left, &order); - if (!page) - return 0; - npages = 1 << order; - - if (head->rc_curpage + npages > rqstp->rq_maxpages) { - for (i = 0; i < npages; i++) - __free_page(page + i); - return 0; - } - - /* - * Replace rq_pages[] entries with pages from the contiguous - * allocation. If npages exceeds chunk_pages, the extra pages - * stay in rq_pages[] for later reuse or normal rqst teardown. - */ - for (i = 0; i < npages; i++) { - svc_rqst_page_release(rqstp, - rqstp->rq_pages[head->rc_curpage + i]); - rqstp->rq_pages[head->rc_curpage + i] = page + i; - } - - chunk_pages = min(npages, pages_left); - chunk_len = min_t(unsigned int, chunk_pages << PAGE_SHIFT, len_left); - bvec_set_page(bv, page, chunk_len, 0); - head->rc_page_count += chunk_pages; - head->rc_curpage += chunk_pages; - return chunk_len; -} - -/* - * svc_rdma_fill_page_bvec - Add a single rq_page to the bvec array - * @head: context for ongoing I/O - * @ctxt: R/W context whose bvec array is being filled - * @cur: page to add - * @bvec_idx: pointer to current bvec index, not advanced on merge - * @len_left: bytes remaining in the segment - * - * If @cur is physically contiguous with the preceding bvec, it is - * merged by extending that bvec's length. Otherwise a new bvec - * entry is created. Returns the byte length covered. - */ -static unsigned int -svc_rdma_fill_page_bvec(struct svc_rdma_recv_ctxt *head, - struct svc_rdma_rw_ctxt *ctxt, struct page *cur, - unsigned int *bvec_idx, unsigned int len_left) -{ - unsigned int chunk_len = min_t(unsigned int, PAGE_SIZE, len_left); - - head->rc_page_count++; - head->rc_curpage++; - - if (*bvec_idx > 0) { - struct bio_vec *prev = &ctxt->rw_bvec[*bvec_idx - 1]; - - if (page_to_phys(prev->bv_page) + prev->bv_offset + - prev->bv_len == page_to_phys(cur)) { - prev->bv_len += chunk_len; - return chunk_len; - } - } - - bvec_set_page(&ctxt->rw_bvec[*bvec_idx], cur, chunk_len, 0); - (*bvec_idx)++; - return chunk_len; -} - -/** - * svc_rdma_build_read_segment_contig - Build RDMA Read WR with contiguous pages - * @rqstp: RPC transaction context - * @head: context for ongoing I/O - * @segment: co-ordinates of remote memory to be read - * - * Greedily allocates higher-order pages to cover the segment, - * building one bvec per contiguous chunk. Each allocation is - * split so sub-pages have independent refcounts. When a - * higher-order allocation fails, remaining pages are covered - * individually, merging adjacent pages into the preceding bvec - * when they are physically contiguous. The split sub-pages - * replace entries in rq_pages[] so downstream cleanup is - * unchanged. - * - * Returns: - * %0: the Read WR was constructed successfully - * %-ENOMEM: allocation failed - * %-EIO: a DMA mapping error occurred - */ -static int svc_rdma_build_read_segment_contig(struct svc_rqst *rqstp, - struct svc_rdma_recv_ctxt *head, - const struct svc_rdma_segment *segment) -{ - struct svcxprt_rdma *rdma = svc_rdma_rqst_rdma(rqstp); - struct svc_rdma_chunk_ctxt *cc = &head->rc_cc; - unsigned int nr_data_pages, bvec_idx; - struct svc_rdma_rw_ctxt *ctxt; - unsigned int len_left; - int ret; - - nr_data_pages = PAGE_ALIGN(segment->rs_length) >> PAGE_SHIFT; - if (head->rc_curpage + nr_data_pages > rqstp->rq_maxpages) - return -ENOMEM; - - ctxt = svc_rdma_get_rw_ctxt(rdma, nr_data_pages); - if (!ctxt) - return -ENOMEM; - - bvec_idx = 0; - len_left = segment->rs_length; - while (len_left) { - unsigned int pages_left = PAGE_ALIGN(len_left) >> PAGE_SHIFT; - unsigned int chunk_len = 0; - - if (pages_left >= 2) - chunk_len = svc_rdma_fill_contig_bvec(rqstp, head, - &ctxt->rw_bvec[bvec_idx], - pages_left, len_left); - if (chunk_len) { - bvec_idx++; - } else { - struct page *cur = - rqstp->rq_pages[head->rc_curpage]; - chunk_len = svc_rdma_fill_page_bvec(head, ctxt, cur, - &bvec_idx, - len_left); - } - - len_left -= chunk_len; - } - - ctxt->rw_nents = bvec_idx; - - head->rc_pageoff = offset_in_page(segment->rs_length); - if (head->rc_pageoff) - head->rc_curpage--; - - ret = svc_rdma_rw_ctx_init(rdma, ctxt, segment->rs_offset, - segment->rs_handle, segment->rs_length, - DMA_FROM_DEVICE); - if (ret < 0) - return -EIO; - percpu_counter_inc(&svcrdma_stat_read); - - list_add(&ctxt->rw_list, &cc->cc_rwctxts); - cc->cc_sqecount += ret; - return 0; -} - /** * svc_rdma_build_read_segment - Build RDMA Read WQEs to pull one RDMA segment * @rqstp: RPC transaction context @@ -990,14 +771,6 @@ static int svc_rdma_build_read_segment(struct svc_rqst *rqstp, if (check_add_overflow(head->rc_pageoff, len, &total)) return -EINVAL; nr_bvec = PAGE_ALIGN(total) >> PAGE_SHIFT; - - if (head->rc_pageoff == 0 && nr_bvec >= 2) { - ret = svc_rdma_build_read_segment_contig(rqstp, head, - segment); - if (ret != -ENOMEM) - return ret; - } - ctxt = svc_rdma_get_rw_ctxt(rdma, nr_bvec); if (!ctxt) return -ENOMEM; @@ -1343,11 +1116,6 @@ static void svc_rdma_clear_rqst_pages(struct svc_rqst *rqstp, { unsigned int i; - /* - * Move only pages containing RPC data into rc_pages[]. Pages - * from a contiguous allocation that were not used for the - * payload remain in rq_pages[] for subsequent reuse. - */ for (i = 0; i < head->rc_page_count; i++) { head->rc_pages[i] = rqstp->rq_pages[i]; rqstp->rq_pages[i] = NULL; diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index 8b3f0c8c14b2..7f6d17bf8c1f 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -79,21 +79,21 @@ * The ownership of all of the Reply's pages are transferred into that * ctxt, the Send WR is posted, and sendto returns. * - * The svc_rdma_send_ctxt is presented when the Send WR completes. The - * Send completion handler finally releases the Reply's pages. - * - * This mechanism also assumes that completions on the transport's Send - * Completion Queue do not run in parallel. Otherwise a Write completion - * and Send completion running at the same time could release pages that - * are still DMA-mapped. + * The svc_rdma_send_ctxt is presented when the Send WR completes. + * The Send completion handler queues the send_ctxt onto the + * per-transport sc_send_release_list (a lock-free llist). The + * nfsd thread drains sc_send_release_list in xpo_release_ctxt + * between RPCs, DMA-unmapping SGEs, releasing chunk I/O + * resources and pages, and returning send_ctxts to the free + * list in a batch. * * Error Handling * * - If the Send WR is posted successfully, it will either complete * successfully, or get flushed. Either way, the Send completion - * handler releases the Reply's pages. - * - If the Send WR cannot be not posted, the forward path releases - * the Reply's pages. + * handler queues the send_ctxt for deferred release. + * - If the Send WR cannot be posted, the forward path releases the + * Reply's pages. * * This handles the case, without the use of page reference counting, * where two different Write segments send portions of the same page. @@ -226,14 +226,25 @@ out: return ctxt; out_empty: + svc_rdma_send_ctxts_drain(rdma); + + spin_lock(&rdma->sc_send_lock); + node = llist_del_first(&rdma->sc_send_ctxts); + spin_unlock(&rdma->sc_send_lock); + if (node) { + ctxt = llist_entry(node, struct svc_rdma_send_ctxt, sc_node); + goto out; + } + ctxt = svc_rdma_send_ctxt_alloc(rdma); if (!ctxt) return NULL; goto out; } -static void svc_rdma_send_ctxt_release(struct svcxprt_rdma *rdma, - struct svc_rdma_send_ctxt *ctxt) +/* Release chunk I/O resources and DMA-unmap SGEs. */ +static void svc_rdma_send_ctxt_unmap(struct svcxprt_rdma *rdma, + struct svc_rdma_send_ctxt *ctxt) { struct ib_device *device = rdma->sc_cm_id->device; unsigned int i; @@ -241,9 +252,6 @@ static void svc_rdma_send_ctxt_release(struct svcxprt_rdma *rdma, svc_rdma_write_chunk_release(rdma, ctxt); svc_rdma_reply_chunk_release(rdma, ctxt); - if (ctxt->sc_page_count) - release_pages(ctxt->sc_pages, ctxt->sc_page_count); - /* The first SGE contains the transport header, which * remains mapped until @ctxt is destroyed. */ @@ -256,30 +264,56 @@ static void svc_rdma_send_ctxt_release(struct svcxprt_rdma *rdma, ctxt->sc_sges[i].length, DMA_TO_DEVICE); } +} + +/* Unmap, release pages, and return send_ctxt to the free list. */ +static void svc_rdma_send_ctxt_release(struct svcxprt_rdma *rdma, + struct svc_rdma_send_ctxt *ctxt) +{ + svc_rdma_send_ctxt_unmap(rdma, ctxt); + + if (ctxt->sc_page_count) + release_pages(ctxt->sc_pages, ctxt->sc_page_count); llist_add(&ctxt->sc_node, &rdma->sc_send_ctxts); } -static void svc_rdma_send_ctxt_put_async(struct work_struct *work) +/** + * svc_rdma_send_ctxts_drain - Release completed send_ctxts + * @rdma: controlling svcxprt_rdma + */ +void svc_rdma_send_ctxts_drain(struct svcxprt_rdma *rdma) { - struct svc_rdma_send_ctxt *ctxt; + struct svc_rdma_send_ctxt *ctxt, *next; + struct llist_node *node; - ctxt = container_of(work, struct svc_rdma_send_ctxt, sc_work); - svc_rdma_send_ctxt_release(ctxt->sc_rdma, ctxt); + node = llist_del_all(&rdma->sc_send_release_list); + llist_for_each_entry_safe(ctxt, next, node, sc_node) + svc_rdma_send_ctxt_release(rdma, ctxt); } /** - * svc_rdma_send_ctxt_put - Return send_ctxt to free list + * svc_rdma_send_ctxt_put - Queue send_ctxt for deferred release * @rdma: controlling svcxprt_rdma - * @ctxt: object to return to the free list + * @ctxt: send_ctxt to queue for deferred release + * + * Queues @ctxt onto sc_send_release_list. DMA unmap and + * page release run later in svc_rdma_send_ctxts_drain(), + * typically from xpo_release_ctxt. * - * Pages left in sc_pages are DMA unmapped and released. + * On the empty-to-non-empty transition, set XPT_DATA and + * enqueue the transport. Without this self-trigger, a Send + * completion arriving after the last xpo_release_ctxt on an + * idle connection would leave the send_ctxt's DMA mappings + * and reply pages pinned until another drain occurred. */ void svc_rdma_send_ctxt_put(struct svcxprt_rdma *rdma, struct svc_rdma_send_ctxt *ctxt) { - INIT_WORK(&ctxt->sc_work, svc_rdma_send_ctxt_put_async); - queue_work(svcrdma_wq, &ctxt->sc_work); + if (llist_add(&ctxt->sc_node, &rdma->sc_send_release_list)) { + set_bit(XPT_DATA, &rdma->sc_xprt.xpt_flags); + svc_xprt_enqueue(&rdma->sc_xprt); + } } /** @@ -367,6 +401,15 @@ int svc_rdma_sq_wait(struct svcxprt_rdma *rdma, atomic_inc(&rdma->sc_sq_ticket_tail); wake_up(&rdma->sc_sq_ticket_wait); trace_svcrdma_sq_retry(rdma, cid); + + /* + * While this thread sat on sc_send_wait or sc_sq_ticket_wait, + * Send completions that tried to enqueue this transport for a + * release-list drain were rejected: svc_rdma_has_wspace returns + * 0 while either waitqueue is active, and svc_xprt_ready + * rejects the enqueue. Drain the release list now. + */ + svc_rdma_send_ctxts_drain(rdma); return 0; out_close: @@ -395,7 +438,7 @@ int svc_rdma_post_send_err(struct svcxprt_rdma *rdma, int sqecount, int ret) { trace_svcrdma_sq_post_err(rdma, cid, ret); - svc_xprt_deferred_close(&rdma->sc_xprt); + svc_rdma_xprt_deferred_close(rdma); /* If even one WR was posted, a Send completion will * return the reserved SQ slots. @@ -437,7 +480,7 @@ flushed: else trace_svcrdma_wc_send_flush(wc, &ctxt->sc_cid); svc_rdma_send_ctxt_put(rdma, ctxt); - svc_xprt_deferred_close(&rdma->sc_xprt); + svc_rdma_xprt_deferred_close(rdma); } /** @@ -1158,7 +1201,7 @@ put_ctxt: svc_rdma_send_ctxt_put(rdma, sctxt); drop_connection: trace_svcrdma_send_err(rqstp, ret); - svc_xprt_deferred_close(&rdma->sc_xprt); + svc_rdma_xprt_deferred_close(rdma); return -ENOTCONN; } diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index f18bc60d9f4f..7ca71741106b 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -98,10 +98,27 @@ struct svc_xprt_class svc_rdma_class = { .xcl_ident = XPRT_TRANSPORT_RDMA, }; +/** + * svc_rdma_xprt_deferred_close - Close an RDMA transport (deferred) + * @rdma: transport to close + */ +void svc_rdma_xprt_deferred_close(struct svcxprt_rdma *rdma) +{ + svc_xprt_deferred_close(&rdma->sc_xprt); + + /* Release parked sc_sq_ticket_wait and sc_send_wait waiters. + * Once XPT_CLOSE is observed each returns -ENOTCONN. + */ + wake_up_all(&rdma->sc_sq_ticket_wait); + wake_up_all(&rdma->sc_send_wait); +} + /* QP event handler */ static void qp_event_handler(struct ib_event *event, void *context) { struct svc_xprt *xprt = context; + struct svcxprt_rdma *rdma = + container_of(xprt, struct svcxprt_rdma, sc_xprt); trace_svcrdma_qp_error(event, (struct sockaddr *)&xprt->xpt_remote); switch (event->event) { @@ -119,7 +136,7 @@ static void qp_event_handler(struct ib_event *event, void *context) case IB_EVENT_QP_ACCESS_ERR: case IB_EVENT_DEVICE_FATAL: default: - svc_xprt_deferred_close(xprt); + svc_rdma_xprt_deferred_close(rdma); break; } } @@ -178,6 +195,7 @@ static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv, init_llist_head(&cma_xprt->sc_send_ctxts); init_llist_head(&cma_xprt->sc_recv_ctxts); init_llist_head(&cma_xprt->sc_rw_ctxts); + init_llist_head(&cma_xprt->sc_send_release_list); init_waitqueue_head(&cma_xprt->sc_send_wait); init_waitqueue_head(&cma_xprt->sc_sq_ticket_wait); @@ -340,7 +358,7 @@ static int svc_rdma_cma_handler(struct rdma_cm_id *cma_id, svc_xprt_enqueue(xprt); break; case RDMA_CM_EVENT_DISCONNECTED: - svc_xprt_deferred_close(xprt); + svc_rdma_xprt_deferred_close(rdma); break; default: break; @@ -597,6 +615,15 @@ static void svc_rdma_detach(struct svc_xprt *xprt) container_of(xprt, struct svcxprt_rdma, sc_xprt); rdma_disconnect(rdma->sc_cm_id); + + /* + * Most close paths go through svc_rdma_xprt_deferred_close(), + * which wakes the SQ waitqueues. svc_xprt_close() reaches + * detach without that helper, so wake any threads parked in + * svc_rdma_sq_wait() here as well. + */ + wake_up_all(&rdma->sc_sq_ticket_wait); + wake_up_all(&rdma->sc_send_wait); } /** @@ -614,7 +641,7 @@ static void svc_rdma_free(struct svc_xprt *xprt) /* This blocks until the Completion Queues are empty */ if (rdma->sc_qp && !IS_ERR(rdma->sc_qp)) ib_drain_qp(rdma->sc_qp); - flush_workqueue(svcrdma_wq); + svc_rdma_send_ctxts_drain(rdma); svc_rdma_flush_recv_queues(rdma); |