summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-02-22 14:21:40 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2023-02-22 14:21:40 -0800
commit9fc2f99030b55027d84723b0dcbbe9f7e21b9c6c (patch)
treee0c158425e66d8168fe36f11f18c2a79040fe7e8
parent25ac8c12ff7886e3d9b99feb85c53302a3cc5556 (diff)
parent4b471a8b847b82a3035709dcf87661915c340c8a (diff)
Merge tag 'nfsd-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux
Pull nfsd updates from Chuck Lever: "Two significant security enhancements are part of this release: - NFSD's RPC header encoding and decoding, including RPCSEC GSS and gssproxy header parsing, has been overhauled to make it more memory-safe. - Support for Kerberos AES-SHA2-based encryption types has been added for both the NFS client and server. This provides a clean path for deprecating and removing insecure encryption types based on DES and SHA-1. AES-SHA2 is also FIPS-140 compliant, so that NFS with Kerberos may now be used on systems with fips enabled. In addition to these, NFSD is now able to handle crossing into an auto-mounted mount point on an exported NFS mount. A number of fixes have been made to NFSD's server-side copy implementation. RPC metrics have been converted to per-CPU variables. This helps reduce unnecessary cross-CPU and cross-node memory bus traffic, and significantly reduces noise when KCSAN is enabled" * tag 'nfsd-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (121 commits) NFSD: Clean up nfsd_symlink() NFSD: copy the whole verifier in nfsd_copy_write_verifier nfsd: don't fsync nfsd_files on last close SUNRPC: Fix occasional warning when destroying gss_krb5_enctypes nfsd: fix courtesy client with deny mode handling in nfs4_upgrade_open NFSD: fix problems with cleanup on errors in nfsd4_copy nfsd: fix race to check ls_layouts nfsd: don't hand out delegation on setuid files being opened for write SUNRPC: Remove ->xpo_secure_port() SUNRPC: Clean up the svc_xprt_flags() macro nfsd: remove fs/nfsd/fault_inject.c NFSD: fix leaked reference count of nfsd4_ssc_umount_item nfsd: clean up potential nfsd_file refcount leaks in COPY codepath nfsd: zero out pointers after putting nfsd_files on COPY setup error SUNRPC: Fix whitespace damage in svcauth_unix.c nfsd: eliminate __nfs4_get_fd nfsd: add some kerneldoc comments for stateid preprocessing functions nfsd: eliminate find_deleg_file_locked nfsd: don't take nfsd4_copy ref for OP_OFFLOAD_STATUS SUNRPC: Add encryption self-tests ...
-rw-r--r--fs/lockd/svc.c21
-rw-r--r--fs/namei.c6
-rw-r--r--fs/nfs/callback_xdr.c13
-rw-r--r--fs/nfs/export.c2
-rw-r--r--fs/nfsd/fault_inject.c142
-rw-r--r--fs/nfsd/filecache.c49
-rw-r--r--fs/nfsd/nfs2acl.c5
-rw-r--r--fs/nfsd/nfs3acl.c5
-rw-r--r--fs/nfsd/nfs3proc.c5
-rw-r--r--fs/nfsd/nfs4layouts.c4
-rw-r--r--fs/nfsd/nfs4proc.c202
-rw-r--r--fs/nfsd/nfs4state.c130
-rw-r--r--fs/nfsd/nfscache.c4
-rw-r--r--fs/nfsd/nfsctl.c77
-rw-r--r--fs/nfsd/nfsd.h2
-rw-r--r--fs/nfsd/nfsproc.c6
-rw-r--r--fs/nfsd/nfssvc.c23
-rw-r--r--fs/nfsd/state.h2
-rw-r--r--fs/nfsd/trace.h31
-rw-r--r--fs/nfsd/vfs.c8
-rw-r--r--fs/nfsd/xdr4.h2
-rw-r--r--include/linux/lockd/lockd.h4
-rw-r--r--include/linux/namei.h2
-rw-r--r--include/linux/nfs_ssc.h2
-rw-r--r--include/linux/sunrpc/gss_krb5.h196
-rw-r--r--include/linux/sunrpc/gss_krb5_enctypes.h41
-rw-r--r--include/linux/sunrpc/msg_prot.h5
-rw-r--r--include/linux/sunrpc/svc.h133
-rw-r--r--include/linux/sunrpc/svc_xprt.h1
-rw-r--r--include/linux/sunrpc/xdr.h28
-rw-r--r--include/trace/events/rpcgss.h22
-rw-r--r--include/trace/events/sunrpc.h28
-rw-r--r--net/sunrpc/.kunitconfig30
-rw-r--r--net/sunrpc/Kconfig100
-rw-r--r--net/sunrpc/auth_gss/Makefile2
-rw-r--r--net/sunrpc/auth_gss/auth_gss.c17
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_crypto.c656
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_internal.h232
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_keys.c416
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_mech.c730
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_seal.c122
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_seqnum.c2
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_test.c2040
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_unseal.c63
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_wrap.c124
-rw-r--r--net/sunrpc/auth_gss/svcauth_gss.c1081
-rw-r--r--net/sunrpc/netns.h1
-rw-r--r--net/sunrpc/stats.c11
-rw-r--r--net/sunrpc/svc.c158
-rw-r--r--net/sunrpc/svc_xprt.c20
-rw-r--r--net/sunrpc/svcauth.c13
-rw-r--r--net/sunrpc/svcauth_unix.c178
-rw-r--r--net/sunrpc/svcsock.c4
-rw-r--r--net/sunrpc/xdr.c79
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_recvfrom.c1
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_transport.c7
56 files changed, 5218 insertions, 2070 deletions
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 59ef8a1f843f..1da00230860c 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -685,17 +685,16 @@ module_exit(exit_nlm);
/**
* nlmsvc_dispatch - Process an NLM Request
* @rqstp: incoming request
- * @statp: pointer to location of accept_stat field in RPC Reply buffer
*
* Return values:
* %0: Processing complete; do not send a Reply
* %1: Processing complete; send Reply in rqstp->rq_res
*/
-static int nlmsvc_dispatch(struct svc_rqst *rqstp, __be32 *statp)
+static int nlmsvc_dispatch(struct svc_rqst *rqstp)
{
const struct svc_procedure *procp = rqstp->rq_procinfo;
+ __be32 *statp = rqstp->rq_accept_statp;
- svcxdr_init_decode(rqstp);
if (!procp->pc_decode(rqstp, &rqstp->rq_arg_stream))
goto out_decode_err;
@@ -705,7 +704,6 @@ static int nlmsvc_dispatch(struct svc_rqst *rqstp, __be32 *statp)
if (*statp != rpc_success)
return 1;
- svcxdr_init_encode(rqstp);
if (!procp->pc_encode(rqstp, &rqstp->rq_res_stream))
goto out_encode_err;
@@ -723,7 +721,7 @@ out_encode_err:
/*
* Define NLM program and procedures
*/
-static unsigned int nlmsvc_version1_count[17];
+static DEFINE_PER_CPU_ALIGNED(unsigned long, nlmsvc_version1_count[17]);
static const struct svc_version nlmsvc_version1 = {
.vs_vers = 1,
.vs_nproc = 17,
@@ -732,26 +730,31 @@ static const struct svc_version nlmsvc_version1 = {
.vs_dispatch = nlmsvc_dispatch,
.vs_xdrsize = NLMSVC_XDRSIZE,
};
-static unsigned int nlmsvc_version3_count[24];
+
+static DEFINE_PER_CPU_ALIGNED(unsigned long,
+ nlmsvc_version3_count[ARRAY_SIZE(nlmsvc_procedures)]);
static const struct svc_version nlmsvc_version3 = {
.vs_vers = 3,
- .vs_nproc = 24,
+ .vs_nproc = ARRAY_SIZE(nlmsvc_procedures),
.vs_proc = nlmsvc_procedures,
.vs_count = nlmsvc_version3_count,
.vs_dispatch = nlmsvc_dispatch,
.vs_xdrsize = NLMSVC_XDRSIZE,
};
+
#ifdef CONFIG_LOCKD_V4
-static unsigned int nlmsvc_version4_count[24];
+static DEFINE_PER_CPU_ALIGNED(unsigned long,
+ nlmsvc_version4_count[ARRAY_SIZE(nlmsvc_procedures4)]);
static const struct svc_version nlmsvc_version4 = {
.vs_vers = 4,
- .vs_nproc = 24,
+ .vs_nproc = ARRAY_SIZE(nlmsvc_procedures4),
.vs_proc = nlmsvc_procedures4,
.vs_count = nlmsvc_version4_count,
.vs_dispatch = nlmsvc_dispatch,
.vs_xdrsize = NLMSVC_XDRSIZE,
};
#endif
+
static const struct svc_version *nlmsvc_version[] = {
[1] = &nlmsvc_version1,
[3] = &nlmsvc_version3,
diff --git a/fs/namei.c b/fs/namei.c
index 5855dc6edbd5..edfedfbccaef 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1459,11 +1459,11 @@ EXPORT_SYMBOL(follow_down_one);
* point, the filesystem owning that dentry may be queried as to whether the
* caller is permitted to proceed or not.
*/
-int follow_down(struct path *path)
+int follow_down(struct path *path, unsigned int flags)
{
struct vfsmount *mnt = path->mnt;
bool jumped;
- int ret = traverse_mounts(path, &jumped, NULL, 0);
+ int ret = traverse_mounts(path, &jumped, NULL, flags);
if (path->mnt != mnt)
mntput(mnt);
@@ -2865,7 +2865,7 @@ int path_pts(struct path *path)
path->dentry = child;
dput(parent);
- follow_down(path);
+ follow_down(path, 0);
return 0;
}
#endif
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
index d0cccddb7d08..321af81c456e 100644
--- a/fs/nfs/callback_xdr.c
+++ b/fs/nfs/callback_xdr.c
@@ -980,14 +980,11 @@ out_invalidcred:
}
static int
-nfs_callback_dispatch(struct svc_rqst *rqstp, __be32 *statp)
+nfs_callback_dispatch(struct svc_rqst *rqstp)
{
const struct svc_procedure *procp = rqstp->rq_procinfo;
- svcxdr_init_decode(rqstp);
- svcxdr_init_encode(rqstp);
-
- *statp = procp->pc_func(rqstp);
+ *rqstp->rq_accept_statp = procp->pc_func(rqstp);
return 1;
}
@@ -1072,7 +1069,8 @@ static const struct svc_procedure nfs4_callback_procedures1[] = {
}
};
-static unsigned int nfs4_callback_count1[ARRAY_SIZE(nfs4_callback_procedures1)];
+static DEFINE_PER_CPU_ALIGNED(unsigned long,
+ nfs4_callback_count1[ARRAY_SIZE(nfs4_callback_procedures1)]);
const struct svc_version nfs4_callback_version1 = {
.vs_vers = 1,
.vs_nproc = ARRAY_SIZE(nfs4_callback_procedures1),
@@ -1084,7 +1082,8 @@ const struct svc_version nfs4_callback_version1 = {
.vs_need_cong_ctrl = true,
};
-static unsigned int nfs4_callback_count4[ARRAY_SIZE(nfs4_callback_procedures1)];
+static DEFINE_PER_CPU_ALIGNED(unsigned long,
+ nfs4_callback_count4[ARRAY_SIZE(nfs4_callback_procedures1)]);
const struct svc_version nfs4_callback_version4 = {
.vs_vers = 4,
.vs_nproc = ARRAY_SIZE(nfs4_callback_procedures1),
diff --git a/fs/nfs/export.c b/fs/nfs/export.c
index 1a9d5aa51dfb..d6a6d1ebb8fd 100644
--- a/fs/nfs/export.c
+++ b/fs/nfs/export.c
@@ -42,7 +42,7 @@ nfs_encode_fh(struct inode *inode, __u32 *p, int *max_len, struct inode *parent)
dprintk("%s: max fh len %d inode %p parent %p",
__func__, *max_len, inode, parent);
- if (*max_len < len || IS_AUTOMOUNT(inode)) {
+ if (*max_len < len) {
dprintk("%s: fh len %d too small, required %d\n",
__func__, *max_len, len);
*max_len = len;
diff --git a/fs/nfsd/fault_inject.c b/fs/nfsd/fault_inject.c
deleted file mode 100644
index 76bee0a0d308..000000000000
--- a/fs/nfsd/fault_inject.c
+++ /dev/null
@@ -1,142 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (c) 2011 Bryan Schumaker <bjschuma@netapp.com>
- *
- * Uses debugfs to create fault injection points for client testing
- */
-
-#include <linux/types.h>
-#include <linux/fs.h>
-#include <linux/debugfs.h>
-#include <linux/module.h>
-#include <linux/nsproxy.h>
-#include <linux/sunrpc/addr.h>
-#include <linux/uaccess.h>
-#include <linux/kernel.h>
-
-#include "state.h"
-#include "netns.h"
-
-struct nfsd_fault_inject_op {
- char *file;
- u64 (*get)(void);
- u64 (*set_val)(u64);
- u64 (*set_clnt)(struct sockaddr_storage *, size_t);
-};
-
-static struct dentry *debug_dir;
-
-static ssize_t fault_inject_read(struct file *file, char __user *buf,
- size_t len, loff_t *ppos)
-{
- static u64 val;
- char read_buf[25];
- size_t size;
- loff_t pos = *ppos;
- struct nfsd_fault_inject_op *op = file_inode(file)->i_private;
-
- if (!pos)
- val = op->get();
- size = scnprintf(read_buf, sizeof(read_buf), "%llu\n", val);
-
- return simple_read_from_buffer(buf, len, ppos, read_buf, size);
-}
-
-static ssize_t fault_inject_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- char write_buf[INET6_ADDRSTRLEN];
- size_t size = min(sizeof(write_buf) - 1, len);
- struct net *net = current->nsproxy->net_ns;
- struct sockaddr_storage sa;
- struct nfsd_fault_inject_op *op = file_inode(file)->i_private;
- u64 val;
- char *nl;
-
- if (copy_from_user(write_buf, buf, size))
- return -EFAULT;
- write_buf[size] = '\0';
-
- /* Deal with any embedded newlines in the string */
- nl = strchr(write_buf, '\n');
- if (nl) {
- size = nl - write_buf;
- *nl = '\0';
- }
-
- size = rpc_pton(net, write_buf, size, (struct sockaddr *)&sa, sizeof(sa));
- if (size > 0) {
- val = op->set_clnt(&sa, size);
- if (val)
- pr_info("NFSD [%s]: Client %s had %llu state object(s)\n",
- op->file, write_buf, val);
- } else {
- val = simple_strtoll(write_buf, NULL, 0);
- if (val == 0)
- pr_info("NFSD Fault Injection: %s (all)", op->file);
- else
- pr_info("NFSD Fault Injection: %s (n = %llu)",
- op->file, val);
- val = op->set_val(val);
- pr_info("NFSD: %s: found %llu", op->file, val);
- }
- return len; /* on success, claim we got the whole input */
-}
-
-static const struct file_operations fops_nfsd = {
- .owner = THIS_MODULE,
- .read = fault_inject_read,
- .write = fault_inject_write,
-};
-
-void nfsd_fault_inject_cleanup(void)
-{
- debugfs_remove_recursive(debug_dir);
-}
-
-static struct nfsd_fault_inject_op inject_ops[] = {
- {
- .file = "forget_clients",
- .get = nfsd_inject_print_clients,
- .set_val = nfsd_inject_forget_clients,
- .set_clnt = nfsd_inject_forget_client,
- },
- {
- .file = "forget_locks",
- .get = nfsd_inject_print_locks,
- .set_val = nfsd_inject_forget_locks,
- .set_clnt = nfsd_inject_forget_client_locks,
- },
- {
- .file = "forget_openowners",
- .get = nfsd_inject_print_openowners,
- .set_val = nfsd_inject_forget_openowners,
- .set_clnt = nfsd_inject_forget_client_openowners,
- },
- {
- .file = "forget_delegations",
- .get = nfsd_inject_print_delegations,
- .set_val = nfsd_inject_forget_delegations,
- .set_clnt = nfsd_inject_forget_client_delegations,
- },
- {
- .file = "recall_delegations",
- .get = nfsd_inject_print_delegations,
- .set_val = nfsd_inject_recall_delegations,
- .set_clnt = nfsd_inject_recall_client_delegations,
- },
-};
-
-void nfsd_fault_inject_init(void)
-{
- unsigned int i;
- struct nfsd_fault_inject_op *op;
- umode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
-
- debug_dir = debugfs_create_dir("nfsd", NULL);
-
- for (i = 0; i < ARRAY_SIZE(inject_ops); i++) {
- op = &inject_ops[i];
- debugfs_create_file(op->file, mode, debug_dir, op, &fops_nfsd);
- }
-}
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index c0950edb26b0..6e8712bd7c99 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -331,37 +331,27 @@ nfsd_file_alloc(struct nfsd_file_lookup_key *key, unsigned int may)
return nf;
}
+/**
+ * nfsd_file_check_write_error - check for writeback errors on a file
+ * @nf: nfsd_file to check for writeback errors
+ *
+ * Check whether a nfsd_file has an unseen error. Reset the write
+ * verifier if so.
+ */
static void
-nfsd_file_fsync(struct nfsd_file *nf)
-{
- struct file *file = nf->nf_file;
- int ret;
-
- if (!file || !(file->f_mode & FMODE_WRITE))
- return;
- ret = vfs_fsync(file, 1);
- trace_nfsd_file_fsync(nf, ret);
- if (ret)
- nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id));
-}
-
-static int
nfsd_file_check_write_error(struct nfsd_file *nf)
{
struct file *file = nf->nf_file;
- if (!file || !(file->f_mode & FMODE_WRITE))
- return 0;
- return filemap_check_wb_err(file->f_mapping, READ_ONCE(file->f_wb_err));
+ if ((file->f_mode & FMODE_WRITE) &&
+ filemap_check_wb_err(file->f_mapping, READ_ONCE(file->f_wb_err)))
+ nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id));
}
static void
nfsd_file_hash_remove(struct nfsd_file *nf)
{
trace_nfsd_file_unhash(nf);
-
- if (nfsd_file_check_write_error(nf))
- nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id));
rhashtable_remove_fast(&nfsd_file_rhash_tbl, &nf->nf_rhash,
nfsd_file_rhash_params);
}
@@ -387,23 +377,12 @@ nfsd_file_free(struct nfsd_file *nf)
this_cpu_add(nfsd_file_total_age, age);
nfsd_file_unhash(nf);
-
- /*
- * We call fsync here in order to catch writeback errors. It's not
- * strictly required by the protocol, but an nfsd_file could get
- * evicted from the cache before a COMMIT comes in. If another
- * task were to open that file in the interim and scrape the error,
- * then the client may never see it. By calling fsync here, we ensure
- * that writeback happens before the entry is freed, and that any
- * errors reported result in the write verifier changing.
- */
- nfsd_file_fsync(nf);
-
if (nf->nf_mark)
nfsd_file_mark_put(nf->nf_mark);
if (nf->nf_file) {
get_file(nf->nf_file);
filp_close(nf->nf_file, NULL);
+ nfsd_file_check_write_error(nf);
fput(nf->nf_file);
}
@@ -452,7 +431,7 @@ static bool nfsd_file_lru_remove(struct nfsd_file *nf)
struct nfsd_file *
nfsd_file_get(struct nfsd_file *nf)
{
- if (likely(refcount_inc_not_zero(&nf->nf_ref)))
+ if (nf && refcount_inc_not_zero(&nf->nf_ref))
return nf;
return NULL;
}
@@ -1107,8 +1086,7 @@ retry:
rcu_read_lock();
nf = rhashtable_lookup(&nfsd_file_rhash_tbl, &key,
nfsd_file_rhash_params);
- if (nf)
- nf = nfsd_file_get(nf);
+ nf = nfsd_file_get(nf);
rcu_read_unlock();
if (nf) {
@@ -1159,6 +1137,7 @@ wait_for_construction:
out:
if (status == nfs_ok) {
this_cpu_inc(nfsd_file_acquisitions);
+ nfsd_file_check_write_error(nf);
*pnf = nf;
} else {
if (refcount_dec_and_test(&nf->nf_ref))
diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c
index 995cb2c90b1a..12b2b9bc07bf 100644
--- a/fs/nfsd/nfs2acl.c
+++ b/fs/nfsd/nfs2acl.c
@@ -377,10 +377,11 @@ static const struct svc_procedure nfsd_acl_procedures2[5] = {
},
};
-static unsigned int nfsd_acl_count2[ARRAY_SIZE(nfsd_acl_procedures2)];
+static DEFINE_PER_CPU_ALIGNED(unsigned long,
+ nfsd_acl_count2[ARRAY_SIZE(nfsd_acl_procedures2)]);
const struct svc_version nfsd_acl_version2 = {
.vs_vers = 2,
- .vs_nproc = 5,
+ .vs_nproc = ARRAY_SIZE(nfsd_acl_procedures2),
.vs_proc = nfsd_acl_procedures2,
.vs_count = nfsd_acl_count2,
.vs_dispatch = nfsd_dispatch,
diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c
index 887803735e2a..73adca47d373 100644
--- a/fs/nfsd/nfs3acl.c
+++ b/fs/nfsd/nfs3acl.c
@@ -266,10 +266,11 @@ static const struct svc_procedure nfsd_acl_procedures3[3] = {
},
};
-static unsigned int nfsd_acl_count3[ARRAY_SIZE(nfsd_acl_procedures3)];
+static DEFINE_PER_CPU_ALIGNED(unsigned long,
+ nfsd_acl_count3[ARRAY_SIZE(nfsd_acl_procedures3)]);
const struct svc_version nfsd_acl_version3 = {
.vs_vers = 3,
- .vs_nproc = 3,
+ .vs_nproc = ARRAY_SIZE(nfsd_acl_procedures3),
.vs_proc = nfsd_acl_procedures3,
.vs_count = nfsd_acl_count3,
.vs_dispatch = nfsd_dispatch,
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index f41992ecd0d7..e6bb8eeb5bc2 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -1064,10 +1064,11 @@ static const struct svc_procedure nfsd_procedures3[22] = {
},
};
-static unsigned int nfsd_count3[ARRAY_SIZE(nfsd_procedures3)];
+static DEFINE_PER_CPU_ALIGNED(unsigned long,
+ nfsd_count3[ARRAY_SIZE(nfsd_procedures3)]);
const struct svc_version nfsd_version3 = {
.vs_vers = 3,
- .vs_nproc = 22,
+ .vs_nproc = ARRAY_SIZE(nfsd_procedures3),
.vs_proc = nfsd_procedures3,
.vs_dispatch = nfsd_dispatch,
.vs_count = nfsd_count3,
diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
index 3564d1c6f610..e8a80052cb1b 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -323,11 +323,11 @@ nfsd4_recall_file_layout(struct nfs4_layout_stateid *ls)
if (ls->ls_recalled)
goto out_unlock;
- ls->ls_recalled = true;
- atomic_inc(&ls->ls_stid.sc_file->fi_lo_recalls);
if (list_empty(&ls->ls_layouts))
goto out_unlock;
+ ls->ls_recalled = true;
+ atomic_inc(&ls->ls_stid.sc_file->fi_lo_recalls);
trace_nfsd_layout_recall(&ls->ls_stid.sc_stateid);
refcount_inc(&ls->ls_stid.sc_count);
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index f189ba7995f5..5ae670807449 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1214,8 +1214,10 @@ out:
return status;
out_put_dst:
nfsd_file_put(*dst);
+ *dst = NULL;
out_put_src:
nfsd_file_put(*src);
+ *src = NULL;
goto out;
}
@@ -1293,15 +1295,15 @@ extern void nfs_sb_deactive(struct super_block *sb);
* setup a work entry in the ssc delayed unmount list.
*/
static __be32 nfsd4_ssc_setup_dul(struct nfsd_net *nn, char *ipaddr,
- struct nfsd4_ssc_umount_item **retwork, struct vfsmount **ss_mnt)
+ struct nfsd4_ssc_umount_item **nsui)
{
struct nfsd4_ssc_umount_item *ni = NULL;
struct nfsd4_ssc_umount_item *work = NULL;
struct nfsd4_ssc_umount_item *tmp;
DEFINE_WAIT(wait);
+ __be32 status = 0;
- *ss_mnt = NULL;
- *retwork = NULL;
+ *nsui = NULL;
work = kzalloc(sizeof(*work), GFP_KERNEL);
try_again:
spin_lock(&nn->nfsd_ssc_lock);
@@ -1325,12 +1327,12 @@ try_again:
finish_wait(&nn->nfsd_ssc_waitq, &wait);
goto try_again;
}
- *ss_mnt = ni->nsui_vfsmount;
+ *nsui = ni;
refcount_inc(&ni->nsui_refcnt);
spin_unlock(&nn->nfsd_ssc_lock);
kfree(work);
- /* return vfsmount in ss_mnt */
+ /* return vfsmount in (*nsui)->nsui_vfsmount */
return 0;
}
if (work) {
@@ -1338,31 +1340,32 @@ try_again:
refcount_set(&work->nsui_refcnt, 2);
work->nsui_busy = true;
list_add_tail(&work->nsui_list, &nn->nfsd_ssc_mount_list);
- *retwork = work;
- }
+ *nsui = work;
+ } else
+ status = nfserr_resource;
spin_unlock(&nn->nfsd_ssc_lock);
- return 0;
+ return status;
}
-static void nfsd4_ssc_update_dul_work(struct nfsd_net *nn,
- struct nfsd4_ssc_umount_item *work, struct vfsmount *ss_mnt)
+static void nfsd4_ssc_update_dul(struct nfsd_net *nn,
+ struct nfsd4_ssc_umount_item *nsui,
+ struct vfsmount *ss_mnt)
{
- /* set nsui_vfsmount, clear busy flag and wakeup waiters */
spin_lock(&nn->nfsd_ssc_lock);
- work->nsui_vfsmount = ss_mnt;
- work->nsui_busy = false;
+ nsui->nsui_vfsmount = ss_mnt;
+ nsui->nsui_busy = false;
wake_up_all(&nn->nfsd_ssc_waitq);
spin_unlock(&nn->nfsd_ssc_lock);
}
-static void nfsd4_ssc_cancel_dul_work(struct nfsd_net *nn,
- struct nfsd4_ssc_umount_item *work)
+static void nfsd4_ssc_cancel_dul(struct nfsd_net *nn,
+ struct nfsd4_ssc_umount_item *nsui)
{
spin_lock(&nn->nfsd_ssc_lock);
- list_del(&work->nsui_list);
+ list_del(&nsui->nsui_list);
wake_up_all(&nn->nfsd_ssc_waitq);
spin_unlock(&nn->nfsd_ssc_lock);
- kfree(work);
+ kfree(nsui);
}
/*
@@ -1370,7 +1373,7 @@ static void nfsd4_ssc_cancel_dul_work(struct nfsd_net *nn,
*/
static __be32
nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
- struct vfsmount **mount)
+ struct nfsd4_ssc_umount_item **nsui)
{
struct file_system_type *type;
struct vfsmount *ss_mnt;
@@ -1381,7 +1384,6 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
char *ipaddr, *dev_name, *raw_data;
int len, raw_len;
__be32 status = nfserr_inval;
- struct nfsd4_ssc_umount_item *work = NULL;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
naddr = &nss->u.nl4_addr;
@@ -1389,6 +1391,7 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
naddr->addr_len,
(struct sockaddr *)&tmp_addr,
sizeof(tmp_addr));
+ *nsui = NULL;
if (tmp_addrlen == 0)
goto out_err;
@@ -1431,10 +1434,10 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
goto out_free_rawdata;
snprintf(dev_name, len + 5, "%s%s%s:/", startsep, ipaddr, endsep);
- status = nfsd4_ssc_setup_dul(nn, ipaddr, &work, &ss_mnt);
+ status = nfsd4_ssc_setup_dul(nn, ipaddr, nsui);
if (status)
goto out_free_devname;
- if (ss_mnt)
+ if ((*nsui)->nsui_vfsmount)
goto out_done;
/* Use an 'internal' mount: SB_KERNMOUNT -> MNT_INTERNAL */
@@ -1442,15 +1445,12 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
module_put(type->owner);
if (IS_ERR(ss_mnt)) {
status = nfserr_nodev;
- if (work)
- nfsd4_ssc_cancel_dul_work(nn, work);
+ nfsd4_ssc_cancel_dul(nn, *nsui);
goto out_free_devname;
}
- if (work)
- nfsd4_ssc_update_dul_work(nn, work, ss_mnt);
+ nfsd4_ssc_update_dul(nn, *nsui, ss_mnt);
out_done:
status = 0;
- *mount = ss_mnt;
out_free_devname:
kfree(dev_name);
@@ -1474,7 +1474,7 @@ out_err:
static __be32
nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate,
- struct nfsd4_copy *copy, struct vfsmount **mount)
+ struct nfsd4_copy *copy)
{
struct svc_fh *s_fh = NULL;
stateid_t *s_stid = &copy->cp_src_stateid;
@@ -1487,7 +1487,7 @@ nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
if (status)
goto out;
- status = nfsd4_interssc_connect(copy->cp_src, rqstp, mount);
+ status = nfsd4_interssc_connect(copy->cp_src, rqstp, &copy->ss_nsui);
if (status)
goto out;
@@ -1505,45 +1505,26 @@ out:
}
static void
-nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct file *filp,
+nfsd4_cleanup_inter_ssc(struct nfsd4_ssc_umount_item *nsui, struct file *filp,
struct nfsd_file *dst)
{
- bool found = false;
- long timeout;
- struct nfsd4_ssc_umount_item *tmp;
- struct nfsd4_ssc_umount_item *ni = NULL;
struct nfsd_net *nn = net_generic(dst->nf_net, nfsd_net_id);
+ long timeout = msecs_to_jiffies(nfsd4_ssc_umount_timeout);
nfs42_ssc_close(filp);
- nfsd_file_put(dst);
fput(filp);
- if (!nn) {
- mntput(ss_mnt);
- return;
- }
spin_lock(&nn->nfsd_ssc_lock);
- timeout = msecs_to_jiffies(nfsd4_ssc_umount_timeout);
- list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) {
- if (ni->nsui_vfsmount->mnt_sb == ss_mnt->mnt_sb) {
- list_del(&ni->nsui_list);
- /*
- * vfsmount can be shared by multiple exports,
- * decrement refcnt. If the count drops to 1 it
- * will be unmounted when nsui_expire expires.
- */
- refcount_dec(&ni->nsui_refcnt);
- ni->nsui_expire = jiffies + timeout;
- list_add_tail(&ni->nsui_list, &nn->nfsd_ssc_mount_list);
- found = true;
- break;
- }
- }
+ list_del(&nsui->nsui_list);
+ /*
+ * vfsmount can be shared by multiple exports,
+ * decrement refcnt. If the count drops to 1 it
+ * will be unmounted when nsui_expire expires.
+ */
+ refcount_dec(&nsui->nsui_refcnt);
+ nsui->nsui_expire = jiffies + timeout;
+ list_add_tail(&nsui->nsui_list, &nn->nfsd_ssc_mount_list);
spin_unlock(&nn->nfsd_ssc_lock);
- if (!found) {
- mntput(ss_mnt);
- return;
- }
}
#else /* CONFIG_NFSD_V4_2_INTER_SSC */
@@ -1551,15 +1532,13 @@ nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct file *filp,
static __be32
nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate,
- struct nfsd4_copy *copy,
- struct vfsmount **mount)
+ struct nfsd4_copy *copy)
{
- *mount = NULL;
return nfserr_inval;
}
static void
-nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct file *filp,
+nfsd4_cleanup_inter_ssc(struct nfsd4_ssc_umount_item *nsui, struct file *filp,
struct nfsd_file *dst)
{
}
@@ -1582,13 +1561,6 @@ nfsd4_setup_intra_ssc(struct svc_rqst *rqstp,
&copy->nf_dst);
}
-static void
-nfsd4_cleanup_intra_ssc(struct nfsd_file *src, struct nfsd_file *dst)
-{
- nfsd_file_put(src);
- nfsd_file_put(dst);
-}
-
static void nfsd4_cb_offload_release(struct nfsd4_callback *cb)
{
struct nfsd4_cb_offload *cbo =
@@ -1700,18 +1672,27 @@ static void dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst)
memcpy(dst->cp_src, src->cp_src, sizeof(struct nl4_server));
memcpy(&dst->stateid, &src->stateid, sizeof(src->stateid));
memcpy(&dst->c_fh, &src->c_fh, sizeof(src->c_fh));
- dst->ss_mnt = src->ss_mnt;
+ dst->ss_nsui = src->ss_nsui;
+}
+
+static void release_copy_files(struct nfsd4_copy *copy)
+{
+ if (copy->nf_src)
+ nfsd_file_put(copy->nf_src);
+ if (copy->nf_dst)
+ nfsd_file_put(copy->nf_dst);
}
static void cleanup_async_copy(struct nfsd4_copy *copy)
{
nfs4_free_copy_state(copy);
- nfsd_file_put(copy->nf_dst);
- if (!nfsd4_ssc_is_inter(copy))
- nfsd_file_put(copy->nf_src);
- spin_lock(&copy->cp_clp->async_lock);
- list_del(&copy->copies);
- spin_unlock(&copy->cp_clp->async_lock);
+ release_copy_files(copy);
+ if (copy->cp_clp) {
+ spin_lock(&copy->cp_clp->async_lock);
+ if (!list_empty(&copy->copies))
+ list_del_init(&copy->copies);
+ spin_unlock(&copy->cp_clp->async_lock);
+ }
nfs4_put_copy(copy);
}
@@ -1749,8 +1730,8 @@ static int nfsd4_do_async_copy(void *data)
if (nfsd4_ssc_is_inter(copy)) {
struct file *filp;
- filp = nfs42_ssc_open(copy->ss_mnt, &copy->c_fh,
- &copy->stateid);
+ filp = nfs42_ssc_open(copy->ss_nsui->nsui_vfsmount,
+ &copy->c_fh, &copy->stateid);
if (IS_ERR(filp)) {
switch (PTR_ERR(filp)) {
case -EBADF:
@@ -1764,11 +1745,10 @@ static int nfsd4_do_async_copy(void *data)
}
nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file,
false);
- nfsd4_cleanup_inter_ssc(copy->ss_mnt, filp, copy->nf_dst);
+ nfsd4_cleanup_inter_ssc(copy->ss_nsui, filp, copy->nf_dst);
} else {
nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file,
copy->nf_dst->nf_file, false);
- nfsd4_cleanup_intra_ssc(copy->nf_src, copy->nf_dst);
}
do_callback:
@@ -1790,8 +1770,7 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfserr_notsupp;
goto out;
}
- status = nfsd4_setup_inter_ssc(rqstp, cstate, copy,
- &copy->ss_mnt);
+ status = nfsd4_setup_inter_ssc(rqstp, cstate, copy);
if (status)
return nfserr_offload_denied;
} else {
@@ -1810,12 +1789,13 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
async_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL);
if (!async_copy)
goto out_err;
+ INIT_LIST_HEAD(&async_copy->copies);
+ refcount_set(&async_copy->refcount, 1);
async_copy->cp_src = kmalloc(sizeof(*async_copy->cp_src), GFP_KERNEL);
if (!async_copy->cp_src)
goto out_err;
if (!nfs4_init_copy_state(nn, copy))
goto out_err;
- refcount_set(&async_copy->refcount, 1);
memcpy(&copy->cp_res.cb_stateid, &copy->cp_stateid.cs_stid,
sizeof(copy->cp_res.cb_stateid));
dup_copy_fields(copy, async_copy);
@@ -1832,38 +1812,53 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
} else {
status = nfsd4_do_copy(copy, copy->nf_src->nf_file,
copy->nf_dst->nf_file, true);
- nfsd4_cleanup_intra_ssc(copy->nf_src, copy->nf_dst);
}
out:
+ release_copy_files(copy);
return status;
out_err:
+ if (nfsd4_ssc_is_inter(copy)) {
+ /*
+ * Source's vfsmount of inter-copy will be unmounted
+ * by the laundromat. Use copy instead of async_copy
+ * since async_copy->ss_nsui might not be set yet.
+ */
+ refcount_dec(&copy->ss_nsui->nsui_refcnt);
+ }
if (async_copy)
cleanup_async_copy(async_copy);
status = nfserrno(-ENOMEM);
- /*
- * source's vfsmount of inter-copy will be unmounted
- * by the laundromat
- */
goto out;
}
-struct nfsd4_copy *
-find_async_copy(struct nfs4_client *clp, stateid_t *stateid)
+static struct nfsd4_copy *
+find_async_copy_locked(struct nfs4_client *clp, stateid_t *stateid)
{
struct nfsd4_copy *copy;
- spin_lock(&clp->async_lock);
+ lockdep_assert_held(&clp->async_lock);
+
list_for_each_entry(copy, &clp->async_copies, copies) {
if (memcmp(&copy->cp_stateid.cs_stid, stateid, NFS4_STATEID_SIZE))
continue;
- refcount_inc(&copy->refcount);
- spin_unlock(&clp->async_lock);
return copy;
}
- spin_unlock(&clp->async_lock);
return NULL;
}
+static struct nfsd4_copy *
+find_async_copy(struct nfs4_client *clp, stateid_t *stateid)
+{
+ struct nfsd4_copy *copy;
+
+ spin_lock(&clp->async_lock);
+ copy = find_async_copy_locked(clp, stateid);
+ if (copy)
+ refcount_inc(&copy->refcount);
+ spin_unlock(&clp->async_lock);
+ return copy;
+}
+
static __be32
nfsd4_offload_cancel(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate,
@@ -1948,22 +1943,24 @@ nfsd4_fallocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfsd_file_put(nf);
return status;
}
+
static __be32
nfsd4_offload_status(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
{
struct nfsd4_offload_status *os = &u->offload_status;
- __be32 status = 0;
+ __be32 status = nfs_ok;
struct nfsd4_copy *copy;
struct nfs4_client *clp = cstate->clp;
- copy = find_async_copy(clp, &os->stateid);
- if (copy) {
+ spin_lock(&clp->async_lock);
+ copy = find_async_copy_locked(clp, &os->stateid);
+ if (copy)
os->count = copy->cp_res.wr_bytes_written;
- nfs4_put_copy(copy);
- } else
+ else
status = nfserr_bad_stateid;
+ spin_unlock(&clp->async_lock);
return status;
}
@@ -3619,12 +3616,13 @@ static const struct svc_procedure nfsd_procedures4[2] = {
},
};
-static unsigned int nfsd_count3[ARRAY_SIZE(nfsd_procedures4)];
+static DEFINE_PER_CPU_ALIGNED(unsigned long,
+ nfsd_count4[ARRAY_SIZE(nfsd_procedures4)]);
const struct svc_version nfsd_version4 = {
.vs_vers = 4,
- .vs_nproc = 2,
+ .vs_nproc = ARRAY_SIZE(nfsd_procedures4),
.vs_proc = nfsd_procedures4,
- .vs_count = nfsd_count3,
+ .vs_count = nfsd_count4,
.vs_dispatch = nfsd_dispatch,
.vs_xdrsize = NFS4_SVC_XDRSIZE,
.vs_rpcb_optnl = true,
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index c1684da6c01f..6e61fa3acaf1 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -600,23 +600,15 @@ put_nfs4_file(struct nfs4_file *fi)
}
static struct nfsd_file *
-__nfs4_get_fd(struct nfs4_file *f, int oflag)
-{
- if (f->fi_fds[oflag])
- return nfsd_file_get(f->fi_fds[oflag]);
- return NULL;
-}
-
-static struct nfsd_file *
find_writeable_file_locked(struct nfs4_file *f)
{
struct nfsd_file *ret;
lockdep_assert_held(&f->fi_lock);
- ret = __nfs4_get_fd(f, O_WRONLY);
+ ret = nfsd_file_get(f->fi_fds[O_WRONLY]);
if (!ret)
- ret = __nfs4_get_fd(f, O_RDWR);
+ ret = nfsd_file_get(f->fi_fds[O_RDWR]);
return ret;
}
@@ -639,9 +631,9 @@ find_readable_file_locked(struct nfs4_file *f)
lockdep_assert_held(&f->fi_lock);
- ret = __nfs4_get_fd(f, O_RDONLY);
+ ret = nfsd_file_get(f->fi_fds[O_RDONLY]);
if (!ret)
- ret = __nfs4_get_fd(f, O_RDWR);
+ ret = nfsd_file_get(f->fi_fds[O_RDWR]);
return ret;
}
@@ -665,11 +657,11 @@ find_any_file(struct nfs4_file *f)
if (!f)
return NULL;
spin_lock(&f->fi_lock);
- ret = __nfs4_get_fd(f, O_RDWR);
+ ret = nfsd_file_get(f->fi_fds[O_RDWR]);
if (!ret) {
- ret = __nfs4_get_fd(f, O_WRONLY);
+ ret = nfsd_file_get(f->fi_fds[O_WRONLY]);
if (!ret)
- ret = __nfs4_get_fd(f, O_RDONLY);
+ ret = nfsd_file_get(f->fi_fds[O_RDONLY]);
}
spin_unlock(&f->fi_lock);
return ret;
@@ -688,15 +680,6 @@ static struct nfsd_file *find_any_file_locked(struct nfs4_file *f)
return NULL;
}
-static struct nfsd_file *find_deleg_file_locked(struct nfs4_file *f)
-{
- lockdep_assert_held(&f->fi_lock);
-
- if (f->fi_deleg_file)
- return f->fi_deleg_file;
- return NULL;
-}
-
static atomic_long_t num_delegations;
unsigned long max_delegations;
@@ -992,7 +975,6 @@ static int nfs4_init_cp_state(struct nfsd_net *nn, copy_stateid_t *stid,
stid->cs_stid.si_opaque.so_clid.cl_boot = (u32)nn->boot_time;
stid->cs_stid.si_opaque.so_clid.cl_id = nn->s2s_cp_cl_id;
- stid->cs_type = cs_type;
idr_preload(GFP_KERNEL);
spin_lock(&nn->s2s_cp_lock);
@@ -1003,6 +985,7 @@ static int nfs4_init_cp_state(struct nfsd_net *nn, copy_stateid_t *stid,
idr_preload_end();
if (new_id < 0)
return 0;
+ stid->cs_type = cs_type;
return 1;
}
@@ -1036,7 +1019,8 @@ void nfs4_free_copy_state(struct nfsd4_copy *copy)
{
struct nfsd_net *nn;
- WARN_ON_ONCE(copy->cp_stateid.cs_type != NFS4_COPY_STID);
+ if (copy->cp_stateid.cs_type != NFS4_COPY_STID)
+ return;
nn = net_generic(copy->cp_clp->net, nfsd_net_id);
spin_lock(&nn->s2s_cp_lock);
idr_remove(&nn->s2s_cp_stateids,
@@ -2705,7 +2689,7 @@ static int nfs4_show_deleg(struct seq_file *s, struct nfs4_stid *st)
ds = delegstateid(st);
nf = st->sc_file;
spin_lock(&nf->fi_lock);
- file = find_deleg_file_locked(nf);
+ file = nf->fi_deleg_file;
if (!file)
goto out;
@@ -5298,16 +5282,17 @@ nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp,
/* test and set deny mode */
spin_lock(&fp->fi_lock);
status = nfs4_file_check_deny(fp, open->op_share_deny);
- if (status == nfs_ok) {
- if (status != nfserr_share_denied) {
- set_deny(open->op_share_deny, stp);
- fp->fi_share_deny |=
- (open->op_share_deny & NFS4_SHARE_DENY_BOTH);
- } else {
- if (nfs4_resolve_deny_conflicts_locked(fp, false,
- stp, open->op_share_deny, false))
- status = nfserr_jukebox;
- }
+ switch (status) {
+ case nfs_ok:
+ set_deny(open->op_share_deny, stp);
+ fp->fi_share_deny |=
+ (open->op_share_deny & NFS4_SHARE_DENY_BOTH);
+ break;
+ case nfserr_share_denied:
+ if (nfs4_resolve_deny_conflicts_locked(fp, false,
+ stp, open->op_share_deny, false))
+ status = nfserr_jukebox;
+ break;
}
spin_unlock(&fp->fi_lock);
@@ -5438,6 +5423,23 @@ nfsd4_verify_deleg_dentry(struct nfsd4_open *open, struct nfs4_file *fp,
return 0;
}
+/*
+ * We avoid breaking delegations held by a client due to its own activity, but
+ * clearing setuid/setgid bits on a write is an implicit activity and the client
+ * may not notice and continue using the old mode. Avoid giving out a delegation
+ * on setuid/setgid files when the client is requesting an open for write.
+ */
+static int
+nfsd4_verify_setuid_write(struct nfsd4_open *open, struct nfsd_file *nf)
+{
+ struct inode *inode = file_inode(nf->nf_file);
+
+ if ((open->op_share_access & NFS4_SHARE_ACCESS_WRITE) &&
+ (inode->i_mode & (S_ISUID|S_ISGID)))
+ return -EAGAIN;
+ return 0;
+}
+
static struct nfs4_delegation *
nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
struct svc_fh *parent)
@@ -5471,6 +5473,8 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
spin_lock(&fp->fi_lock);
if (nfs4_delegation_exists(clp, fp))
status = -EAGAIN;
+ else if (nfsd4_verify_setuid_write(open, nf))
+ status = -EAGAIN;
else if (!fp->fi_deleg_file) {
fp->fi_deleg_file = nf;
/* increment early to prevent fi_deleg_file from being
@@ -5511,6 +5515,14 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
if (status)
goto out_unlock;
+ /*
+ * Now that the deleg is set, check again to ensure that nothing
+ * raced in and changed the mode while we weren't lookng.
+ */
+ status = nfsd4_verify_setuid_write(open, fp->fi_deleg_file);
+ if (status)
+ goto out_unlock;
+
spin_lock(&state_lock);
spin_lock(&fp->fi_lock);
if (fp->fi_had_conflict)
@@ -6406,23 +6418,26 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
static struct nfsd_file *
nfs4_find_file(struct nfs4_stid *s, int flags)
{
+ struct nfsd_file *ret = NULL;
+
if (!s)
return NULL;
switch (s->sc_type) {
case NFS4_DELEG_STID:
- if (WARN_ON_ONCE(!s->sc_file->fi_deleg_file))
- return NULL;
- return nfsd_file_get(s->sc_file->fi_deleg_file);
+ spin_lock(&s->sc_file->fi_lock);
+ ret = nfsd_file_get(s->sc_file->fi_deleg_file);
+ spin_unlock(&s->sc_file->fi_lock);
+ break;
case NFS4_OPEN_STID:
case NFS4_LOCK_STID:
if (flags & RD_STATE)
- return find_readable_file(s->sc_file);
+ ret = find_readable_file(s->sc_file);
else
- return find_writeable_file(s->sc_file);
+ ret = find_writeable_file(s->sc_file);
}
- return NULL;
+ return ret;
}
static __be32
@@ -6547,8 +6562,19 @@ void nfs4_put_cpntf_state(struct nfsd_net *nn, struct nfs4_cpntf_state *cps)
spin_unlock(&nn->s2s_cp_lock);
}
-/*
- * Checks for stateid operations
+/**
+ * nfs4_preprocess_stateid_op - find and prep stateid for an operation
+ * @rqstp: incoming request from client
+ * @cstate: current compound state
+ * @fhp: filehandle associated with requested stateid
+ * @stateid: stateid (provided by client)
+ * @flags: flags describing type of operation to be done
+ * @nfp: optional nfsd_file return pointer (may be NULL)
+ * @cstid: optional returned nfs4_stid pointer (may be NULL)
+ *
+ * Given info from the client, look up a nfs4_stid for the operation. On
+ * success, it returns a reference to the nfs4_stid and/or the nfsd_file
+ * associated with it.
*/
__be32
nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
@@ -6737,8 +6763,18 @@ static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_
return status;
}
-/*
- * Checks for sequence id mutating operations.
+/**
+ * nfs4_preprocess_seqid_op - find and prep an ol_stateid for a seqid-morphing op
+ * @cstate: compund state
+ * @seqid: seqid (provided by client)
+ * @stateid: stateid (provided by client)
+ * @typemask: mask of allowable types for this operation
+ * @stpp: return pointer for the stateid found
+ * @nn: net namespace for request
+ *
+ * Given a stateid+seqid from a client, look up an nfs4_ol_stateid and
+ * return it in @stpp. On a nfs_ok return, the returned stateid will
+ * have its st_mutex locked.
*/
static __be32
nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c
index 3e64a3d50a1c..041faa13b852 100644
--- a/fs/nfsd/nfscache.c
+++ b/fs/nfsd/nfscache.c
@@ -488,7 +488,7 @@ found_entry:
case RC_NOCACHE:
break;
case RC_REPLSTAT:
- svc_putu32(&rqstp->rq_res.head[0], rp->c_replstat);
+ xdr_stream_encode_be32(&rqstp->rq_res_stream, rp->c_replstat);
rtn = RC_REPLY;
break;
case RC_REPLBUFF:
@@ -509,7 +509,7 @@ out_trace:
* nfsd_cache_update - Update an entry in the duplicate reply cache.
* @rqstp: svc_rqst with a finished Reply
* @cachetype: which cache to update
- * @statp: Reply's status code
+ * @statp: pointer to Reply's NFS status code, or NULL
*
* This is called from nfsd_dispatch when the procedure has been
* executed and the complete reply is in rqstp->rq_res.
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index c2577ee7ffb2..7b8f17ee5224 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -14,7 +14,6 @@
#include <linux/lockd/lockd.h>
#include <linux/sunrpc/addr.h>
#include <linux/sunrpc/gss_api.h>
-#include <linux/sunrpc/gss_krb5_enctypes.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
#include <linux/module.h>
#include <linux/fsnotify.h>
@@ -47,7 +46,6 @@ enum {
NFSD_MaxBlkSize,
NFSD_MaxConnections,
NFSD_Filecache,
- NFSD_SupportedEnctypes,
/*
* The below MUST come last. Otherwise we leave a hole in nfsd_files[]
* with !CONFIG_NFSD_V4 and simple_fill_super() goes oops
@@ -187,16 +185,6 @@ static int export_features_show(struct seq_file *m, void *v)
DEFINE_SHOW_ATTRIBUTE(export_features);
-#if defined(CONFIG_SUNRPC_GSS) || defined(CONFIG_SUNRPC_GSS_MODULE)
-static int supported_enctypes_show(struct seq_file *m, void *v)
-{
- seq_printf(m, KRB5_SUPPORTED_ENCTYPES);
- return 0;
-}
-
-DEFINE_SHOW_ATTRIBUTE(supported_enctypes);
-#endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */
-
static const struct file_operations pool_stats_operations = {
.open = nfsd_pool_stats_open,
.read = seq_read,
@@ -1150,6 +1138,9 @@ static struct inode *nfsd_get_inode(struct super_block *sb, umode_t mode)
inode->i_op = &simple_dir_inode_operations;
inc_nlink(inode);
break;
+ case S_IFLNK:
+ inode->i_op = &simple_symlink_inode_operations;
+ break;
default:
break;
}
@@ -1195,6 +1186,54 @@ out_err:
goto out;
}
+#if IS_ENABLED(CONFIG_SUNRPC_GSS)
+static int __nfsd_symlink(struct inode *dir, struct dentry *dentry,
+ umode_t mode, const char *content)
+{
+ struct inode *inode;
+
+ inode = nfsd_get_inode(dir->i_sb, mode);
+ if (!inode)
+ return -ENOMEM;
+
+ inode->i_link = (char *)content;
+ inode->i_size = strlen(content);
+
+ d_add(dentry, inode);
+ inc_nlink(dir);
+ fsnotify_create(dir, dentry);
+ return 0;
+}
+
+/*
+ * @content is assumed to be a NUL-terminated string that lives
+ * longer than the symlink itself.
+ */
+static void nfsd_symlink(struct dentry *parent, const char *name,
+ const char *content)
+{
+ struct inode *dir = parent->d_inode;
+ struct dentry *dentry;
+ int ret;
+
+ inode_lock(dir);
+ dentry = d_alloc_name(parent, name);
+ if (!dentry)
+ goto out;
+ ret = __nfsd_symlink(d_inode(parent), dentry, S_IFLNK | 0777, content);
+ if (ret)
+ dput(dentry);
+out:
+ inode_unlock(dir);
+}
+#else
+static inline void nfsd_symlink(struct dentry *parent, const char *name,
+ const char *content)
+{
+}
+
+#endif
+
static void clear_ncl(struct inode *inode)
{
struct nfsdfs_client *ncl = inode->i_private;
@@ -1355,10 +1394,6 @@ static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc)
[NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO},
[NFSD_MaxConnections] = {"max_connections", &transaction_ops, S_IWUSR|S_IRUGO},
[NFSD_Filecache] = {"filecache", &nfsd_file_cache_stats_fops, S_IRUGO},
-#if defined(CONFIG_SUNRPC_GSS) || defined(CONFIG_SUNRPC_GSS_MODULE)
- [NFSD_SupportedEnctypes] = {"supported_krb5_enctypes",
- &supported_enctypes_fops, S_IRUGO},
-#endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */
#ifdef CONFIG_NFSD_V4
[NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_Gracetime] = {"nfsv4gracetime", &transaction_ops, S_IWUSR|S_IRUSR},
@@ -1371,6 +1406,8 @@ static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc)
ret = simple_fill_super(sb, 0x6e667364, nfsd_files);
if (ret)
return ret;
+ nfsd_symlink(sb->s_root, "supported_krb5_enctypes",
+ "/proc/net/rpc/gss_krb5_enctypes");
dentry = nfsd_mkdir(sb->s_root, NULL, "clients");
if (IS_ERR(dentry))
return PTR_ERR(dentry);
@@ -1458,16 +1495,11 @@ static __net_init int nfsd_init_net(struct net *net)
nn->nfsd_versions = NULL;
nn->nfsd4_minorversions = NULL;
nfsd4_init_leases_net(nn);
- retval = nfsd_reply_cache_init(nn);
- if (retval)
- goto out_cache_error;
get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key));
seqlock_init(&nn->writeverf_lock);
return 0;
-out_cache_error:
- nfsd_idmap_shutdown(net);
out_idmap_error:
nfsd_export_shutdown(net);
out_export_error:
@@ -1476,9 +1508,6 @@ out_export_error:
static __net_exit void nfsd_exit_net(struct net *net)
{
- struct nfsd_net *nn = net_generic(net, nfsd_net_id);
-
- nfsd_reply_cache_shutdown(nn);
nfsd_idmap_shutdown(net);
nfsd_export_shutdown(net);
nfsd_netns_free_versions(net_generic(net, nfsd_net_id));
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index fa0144a74267..d88498f8b275 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -86,7 +86,7 @@ bool nfssvc_encode_voidres(struct svc_rqst *rqstp,
* Function prototypes.
*/
int nfsd_svc(int nrservs, struct net *net, const struct cred *cred);
-int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp);
+int nfsd_dispatch(struct svc_rqst *rqstp);
int nfsd_nrthreads(struct net *);
int nfsd_nrpools(struct net *);
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index a82d91afdc9c..c37195572fd0 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -838,11 +838,11 @@ static const struct svc_procedure nfsd_procedures2[18] = {
},
};
-
-static unsigned int nfsd_count2[ARRAY_SIZE(nfsd_procedures2)];
+static DEFINE_PER_CPU_ALIGNED(unsigned long,
+ nfsd_count2[ARRAY_SIZE(nfsd_procedures2)]);
const struct svc_version nfsd_version2 = {
.vs_vers = 2,
- .vs_nproc = 18,
+ .vs_nproc = ARRAY_SIZE(nfsd_procedures2),
.vs_proc = nfsd_procedures2,
.vs_count = nfsd_count2,
.vs_dispatch = nfsd_dispatch,
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 325d3d3f1211..9c7b1ef5be40 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -363,7 +363,7 @@ void nfsd_copy_write_verifier(__be32 verf[2], struct nfsd_net *nn)
do {
read_seqbegin_or_lock(&nn->writeverf_lock, &seq);
- memcpy(verf, nn->writeverf, sizeof(*verf));
+ memcpy(verf, nn->writeverf, sizeof(nn->writeverf));
} while (need_seqretry(&nn->writeverf_lock, seq));
done_seqretry(&nn->writeverf_lock, seq);
}
@@ -427,16 +427,23 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)
ret = nfsd_file_cache_start_net(net);
if (ret)
goto out_lockd;
- ret = nfs4_state_start_net(net);
+
+ ret = nfsd_reply_cache_init(nn);
if (ret)
goto out_filecache;
+ ret = nfs4_state_start_net(net);
+ if (ret)
+ goto out_reply_cache;
+
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
nfsd4_ssc_init_umount_work(nn);
#endif
nn->nfsd_net_up = true;
return 0;
+out_reply_cache:
+ nfsd_reply_cache_shutdown(nn);
out_filecache:
nfsd_file_cache_shutdown_net(net);
out_lockd:
@@ -454,6 +461,7 @@ static void nfsd_shutdown_net(struct net *net)
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
nfs4_state_shutdown_net(net);
+ nfsd_reply_cache_shutdown(nn);
nfsd_file_cache_shutdown_net(net);
if (nn->lockd_up) {
lockd_down(net);
@@ -1022,7 +1030,6 @@ out:
/**
* nfsd_dispatch - Process an NFS or NFSACL Request
* @rqstp: incoming request
- * @statp: pointer to location of accept_stat field in RPC Reply buffer
*
* This RPC dispatcher integrates the NFS server's duplicate reply cache.
*
@@ -1030,9 +1037,10 @@ out:
* %0: Processing complete; do not send a Reply
* %1: Processing complete; send Reply in rqstp->rq_res
*/
-int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
+int nfsd_dispatch(struct svc_rqst *rqstp)
{
const struct svc_procedure *proc = rqstp->rq_procinfo;
+ __be32 *statp = rqstp->rq_accept_statp;
/*
* Give the xdr decoder a chance to change this if it wants
@@ -1040,7 +1048,6 @@ int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
*/
rqstp->rq_cachetype = proc->pc_cachetype;
- svcxdr_init_decode(rqstp);
if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream))
goto out_decode_err;
@@ -1053,12 +1060,6 @@ int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
goto out_dropit;
}
- /*
- * Need to grab the location to store the status, as
- * NFSv4 does some encoding while processing
- */
- svcxdr_init_encode(rqstp);
-
*statp = proc->pc_func(rqstp);
if (test_bit(RQ_DROPME, &rqstp->rq_flags))
goto out_update_drop;
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index e94634d30591..d49d3060ed4f 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -705,8 +705,6 @@ extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name
extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn);
void put_nfs4_file(struct nfs4_file *fi);
-extern struct nfsd4_copy *
-find_async_copy(struct nfs4_client *clp, stateid_t *staetid);
extern void nfs4_put_cpntf_state(struct nfsd_net *nn,
struct nfs4_cpntf_state *cps);
extern __be32 manage_cpntf_state(struct nfsd_net *nn, stateid_t *st,
diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h
index 8f9c82d9e075..4183819ea082 100644
--- a/fs/nfsd/trace.h
+++ b/fs/nfsd/trace.h
@@ -1202,37 +1202,6 @@ TRACE_EVENT(nfsd_file_close,
)
);
-TRACE_EVENT(nfsd_file_fsync,
- TP_PROTO(
- const struct nfsd_file *nf,
- int ret
- ),
- TP_ARGS(nf, ret),
- TP_STRUCT__entry(
- __field(void *, nf_inode)
- __field(int, nf_ref)
- __field(int, ret)
- __field(unsigned long, nf_flags)
- __field(unsigned char, nf_may)
- __field(struct file *, nf_file)
- ),
- TP_fast_assign(
- __entry->nf_inode = nf->nf_inode;
- __entry->nf_ref = refcount_read(&nf->nf_ref);
- __entry->ret = ret;
- __entry->nf_flags = nf->nf_flags;
- __entry->nf_may = nf->nf_may;
- __entry->nf_file = nf->nf_file;
- ),
- TP_printk("inode=%p ref=%d flags=%s may=%s nf_file=%p ret=%d",
- __entry->nf_inode,
- __entry->nf_ref,
- show_nf_flags(__entry->nf_flags),
- show_nfsd_may_flags(__entry->nf_may),
- __entry->nf_file, __entry->ret
- )
-);
-
#include "cache.h"
TRACE_DEFINE_ENUM(RC_DROPIT);
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index ab4ee3509ce3..e7462b5e5f1e 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -126,9 +126,13 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
struct dentry *dentry = *dpp;
struct path path = {.mnt = mntget(exp->ex_path.mnt),
.dentry = dget(dentry)};
+ unsigned int follow_flags = 0;
int err = 0;
- err = follow_down(&path);
+ if (exp->ex_flags & NFSEXP_CROSSMOUNT)
+ follow_flags = LOOKUP_AUTOMOUNT;
+
+ err = follow_down(&path, follow_flags);
if (err < 0)
goto out;
if (path.mnt == exp->ex_path.mnt && path.dentry == dentry &&
@@ -223,7 +227,7 @@ int nfsd_mountpoint(struct dentry *dentry, struct svc_export *exp)
return 1;
if (nfsd4_is_junction(dentry))
return 1;
- if (d_mountpoint(dentry))
+ if (d_managed(dentry))
/*
* Might only be a mountpoint in a different namespace,
* but we need to check.
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 4fd2cf6d1d2d..510978e602da 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -571,7 +571,7 @@ struct nfsd4_copy {
struct task_struct *copy_task;
refcount_t refcount;
- struct vfsmount *ss_mnt;
+ struct nfsd4_ssc_umount_item *ss_nsui;
struct nfs_fh c_fh;
nfs4_stateid stateid;
};
diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h
index 2b7f067af3c4..0168ac9fdda8 100644
--- a/include/linux/lockd/lockd.h
+++ b/include/linux/lockd/lockd.h
@@ -196,9 +196,9 @@ struct nlm_block {
* Global variables
*/
extern const struct rpc_program nlm_program;
-extern const struct svc_procedure nlmsvc_procedures[];
+extern const struct svc_procedure nlmsvc_procedures[24];
#ifdef CONFIG_LOCKD_V4
-extern const struct svc_procedure nlmsvc_procedures4[];
+extern const struct svc_procedure nlmsvc_procedures4[24];
#endif
extern int nlmsvc_grace_period;
extern unsigned long nlmsvc_timeout;
diff --git a/include/linux/namei.h b/include/linux/namei.h
index 0d4531fd46e7..0d797f3367ca 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -77,7 +77,7 @@ struct dentry *lookup_one_positive_unlocked(struct mnt_idmap *idmap,
struct dentry *base, int len);
extern int follow_down_one(struct path *);
-extern int follow_down(struct path *);
+extern int follow_down(struct path *path, unsigned int flags);
extern int follow_up(struct path *);
extern struct dentry *lock_rename(struct dentry *, struct dentry *);
diff --git a/include/linux/nfs_ssc.h b/include/linux/nfs_ssc.h
index 75843c00f326..22265b1ff080 100644
--- a/include/linux/nfs_ssc.h
+++ b/include/linux/nfs_ssc.h
@@ -53,6 +53,7 @@ static inline void nfs42_ssc_close(struct file *filep)
if (nfs_ssc_client_tbl.ssc_nfs4_ops)
(*nfs_ssc_client_tbl.ssc_nfs4_ops->sco_close)(filep);
}
+#endif
struct nfsd4_ssc_umount_item {
struct list_head nsui_list;
@@ -66,7 +67,6 @@ struct nfsd4_ssc_umount_item {
struct vfsmount *nsui_vfsmount;
char nsui_ipaddr[RPC_MAX_ADDRBUFLEN + 1];
};
-#endif
/*
* NFS_FS
diff --git a/include/linux/sunrpc/gss_krb5.h b/include/linux/sunrpc/gss_krb5.h
index 91f43d86879d..78a80bf3fdcb 100644
--- a/include/linux/sunrpc/gss_krb5.h
+++ b/include/linux/sunrpc/gss_krb5.h
@@ -1,6 +1,4 @@
/*
- * linux/include/linux/sunrpc/gss_krb5_types.h
- *
* Adapted from MIT Kerberos 5-1.2.1 lib/include/krb5.h,
* lib/gssapi/krb5/gssapiP_krb5.h, and others
*
@@ -36,6 +34,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>
@@ -44,80 +45,15 @@
/* Length of constant used in key derivation */
#define GSS_KRB5_K5CLENGTH (5)
-/* Maximum key length (in bytes) for the supported crypto algorithms*/
+/* Maximum key length (in bytes) for the supported crypto algorithms */
#define GSS_KRB5_MAX_KEYLEN (32)
-/* Maximum checksum function output for the supported crypto algorithms */
-#define GSS_KRB5_MAX_CKSUM_LEN (20)
+/* Maximum checksum function output for the supported enctypes */
+#define GSS_KRB5_MAX_CKSUM_LEN (24)
/* Maximum blocksize for the supported crypto algorithms */
#define GSS_KRB5_MAX_BLOCKSIZE (16)
-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 *cksum_name; /* crypto checksum name */
- const u16 signalg; /* signing algorithm */
- const u16 sealalg; /* sealing algorithm */
- const u32 blocksize; /* encryption blocksize */
- const u32 conflen; /* confounder length
- (normally the same as
- the blocksize) */
- 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; /* final key len, in bytes */
- u32 (*encrypt) (struct crypto_sync_skcipher *tfm,
- void *iv, void *in, void *out,
- int length); /* encryption function */
- u32 (*decrypt) (struct crypto_sync_skcipher *tfm,
- void *iv, void *in, void *out,
- int length); /* decryption function */
- u32 (*mk_key) (const struct gss_krb5_enctype *gk5e,
- struct xdr_netobj *in,
- struct xdr_netobj *out); /* complete key generation */
- u32 (*encrypt_v2) (struct krb5_ctx *kctx, u32 offset,
- struct xdr_buf *buf,
- struct page **pages); /* v2 encryption function */
- u32 (*decrypt_v2) (struct krb5_ctx *kctx, u32 offset, u32 len,
- struct xdr_buf *buf, u32 *headskip,
- u32 *tailskip); /* v2 decryption function */
-};
-
-/* krb5_ctx flags definitions */
-#define KRB5_CTX_FLAG_INITIATOR 0x00000001
-#define KRB5_CTX_FLAG_CFX 0x00000002
-#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
-
-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;
- u8 Ksess[GSS_KRB5_MAX_KEYLEN]; /* session key */
- u8 cksum[GSS_KRB5_MAX_KEYLEN];
- atomic_t seq_send;
- atomic64_t seq_send64;
- time64_t endtime;
- struct xdr_netobj mech_used;
- u8 initiator_sign[GSS_KRB5_MAX_KEYLEN];
- u8 acceptor_sign[GSS_KRB5_MAX_KEYLEN];
- u8 initiator_seal[GSS_KRB5_MAX_KEYLEN];
- u8 acceptor_seal[GSS_KRB5_MAX_KEYLEN];
- u8 initiator_integ[GSS_KRB5_MAX_KEYLEN];
- u8 acceptor_integ[GSS_KRB5_MAX_KEYLEN];
-};
-
/* The length of the Kerberos GSS token header */
#define GSS_KRB5_TOK_HDR_LEN (16)
@@ -150,6 +86,12 @@ enum seal_alg {
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
@@ -160,6 +102,10 @@ enum seal_alg {
#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 */
@@ -180,6 +126,11 @@ enum seal_alg {
/* 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 */
@@ -193,8 +144,12 @@ enum seal_alg {
#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
/*
@@ -216,103 +171,4 @@ enum seal_alg {
#define KG_USAGE_INITIATOR_SEAL (24)
#define KG_USAGE_INITIATOR_SIGN (25)
-/*
- * This compile-time check verifies that we will not exceed the
- * slack space allotted by the client and server auth_gss code
- * before they call gss_wrap().
- */
-#define GSS_KRB5_MAX_SLACK_NEEDED \
- (GSS_KRB5_TOK_HDR_LEN /* gss token header */ \
- + GSS_KRB5_MAX_CKSUM_LEN /* gss token checksum */ \
- + GSS_KRB5_MAX_BLOCKSIZE /* confounder */ \
- + GSS_KRB5_MAX_BLOCKSIZE /* possible padding */ \
- + GSS_KRB5_TOK_HDR_LEN /* encrypted hdr in v2 token */\
- + GSS_KRB5_MAX_CKSUM_LEN /* encryption hmac */ \
- + 4 + 4 /* RPC verifier */ \
- + GSS_KRB5_TOK_HDR_LEN \
- + GSS_KRB5_MAX_CKSUM_LEN)
-
-u32
-make_checksum(struct krb5_ctx *kctx, char *header, int hdrlen,
- struct xdr_buf *body, int body_offset, u8 *cksumkey,
- unsigned int usage, struct xdr_netobj *cksumout);
-
-u32
-make_checksum_v2(struct krb5_ctx *, char *header, int hdrlen,
- struct xdr_buf *body, int body_offset, u8 *key,
- unsigned int usage, struct xdr_netobj *cksum);
-
-u32 gss_get_mic_kerberos(struct gss_ctx *, struct xdr_buf *,
- struct xdr_netobj *);
-
-u32 gss_verify_mic_kerberos(struct gss_ctx *, struct xdr_buf *,
- struct xdr_netobj *);
-
-u32
-gss_wrap_kerberos(struct gss_ctx *ctx_id, int offset,
- struct xdr_buf *outbuf, struct page **pages);
-
-u32
-gss_unwrap_kerberos(struct gss_ctx *ctx_id, int offset, int len,
- struct xdr_buf *buf);
-
-
-u32
-krb5_encrypt(struct crypto_sync_skcipher *key,
- void *iv, void *in, void *out, int length);
-
-u32
-krb5_decrypt(struct crypto_sync_skcipher *key,
- void *iv, void *in, void *out, int length);
-
-int
-gss_encrypt_xdr_buf(struct crypto_sync_skcipher *tfm, struct xdr_buf *outbuf,
- int offset, struct page **pages);
-
-int
-gss_decrypt_xdr_buf(struct crypto_sync_skcipher *tfm, struct xdr_buf *inbuf,
- int offset);
-
-s32
-krb5_make_seq_num(struct krb5_ctx *kctx,
- struct crypto_sync_skcipher *key,
- int direction,
- u32 seqnum, unsigned char *cksum, unsigned char *buf);
-
-s32
-krb5_get_seq_num(struct krb5_ctx *kctx,
- unsigned char *cksum,
- unsigned char *buf, int *direction, u32 *seqnum);
-
-int
-xdr_extend_head(struct xdr_buf *buf, unsigned int base, unsigned int shiftlen);
-
-u32
-krb5_derive_key(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);
-
-u32
-gss_krb5_des3_make_key(const struct gss_krb5_enctype *gk5e,
- struct xdr_netobj *randombits,
- struct xdr_netobj *key);
-
-u32
-gss_krb5_aes_make_key(const struct gss_krb5_enctype *gk5e,
- struct xdr_netobj *randombits,
- struct xdr_netobj *key);
-
-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);
-
-void
-gss_krb5_make_confounder(char *p, u32 conflen);
+#endif /* _LINUX_SUNRPC_GSS_KRB5_H */
diff --git a/include/linux/sunrpc/gss_krb5_enctypes.h b/include/linux/sunrpc/gss_krb5_enctypes.h
deleted file mode 100644
index 87eea679d750..000000000000
--- a/include/linux/sunrpc/gss_krb5_enctypes.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Define the string that exports the set of kernel-supported
- * Kerberos enctypes. This list is sent via upcall to gssd, and
- * is also exposed via the nfsd /proc API. The consumers generally
- * treat this as an ordered list, where the first item in the list
- * is the most preferred.
- */
-
-#ifndef _LINUX_SUNRPC_GSS_KRB5_ENCTYPES_H
-#define _LINUX_SUNRPC_GSS_KRB5_ENCTYPES_H
-
-#ifdef CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES
-
-/*
- * NB: This list includes DES3_CBC_SHA1, which was deprecated by RFC 8429.
- *
- * ENCTYPE_AES256_CTS_HMAC_SHA1_96
- * ENCTYPE_AES128_CTS_HMAC_SHA1_96
- * ENCTYPE_DES3_CBC_SHA1
- */
-#define KRB5_SUPPORTED_ENCTYPES "18,17,16"
-
-#else /* CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES */
-
-/*
- * NB: This list includes encryption types that were deprecated
- * by RFC 8429 and RFC 6649.
- *
- * ENCTYPE_AES256_CTS_HMAC_SHA1_96
- * ENCTYPE_AES128_CTS_HMAC_SHA1_96
- * ENCTYPE_DES3_CBC_SHA1
- * ENCTYPE_DES_CBC_MD5
- * ENCTYPE_DES_CBC_CRC
- * ENCTYPE_DES_CBC_MD4
- */
-#define KRB5_SUPPORTED_ENCTYPES "18,17,16,3,1,2"
-
-#endif /* CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES */
-
-#endif /* _LINUX_SUNRPC_GSS_KRB5_ENCTYPES_H */
diff --git a/include/linux/sunrpc/msg_prot.h b/include/linux/sunrpc/msg_prot.h
index 02117ed0fa2e..c4b0eb2b2f04 100644
--- a/include/linux/sunrpc/msg_prot.h
+++ b/include/linux/sunrpc/msg_prot.h
@@ -34,6 +34,11 @@ enum rpc_auth_flavors {
RPC_AUTH_GSS_SPKMP = 390011,
};
+/* Maximum size (in octets) of the machinename in an AUTH_UNIX
+ * credential (per RFC 5531 Appendix A)
+ */
+#define RPC_MAX_MACHINENAME (255)
+
/* Maximum size (in bytes) of an rpc credential or verifier */
#define RPC_MAX_AUTH_SIZE (400)
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index ed722dd33b46..877891536c2f 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -21,14 +21,6 @@
#include <linux/mm.h>
#include <linux/pagevec.h>
-/* statistics for svc_pool structures */
-struct svc_pool_stats {
- atomic_long_t packets;
- unsigned long sockets_queued;
- atomic_long_t threads_woken;
- atomic_long_t threads_timedout;
-};
-
/*
*
* RPC service thread pool.
@@ -45,7 +37,12 @@ struct svc_pool {
struct list_head sp_sockets; /* pending sockets */
unsigned int sp_nrthreads; /* # of threads in pool */
struct list_head sp_all_threads; /* all server threads */
- struct svc_pool_stats sp_stats; /* statistics on pool operation */
+
+ /* statistics on pool operation */
+ struct percpu_counter sp_sockets_queued;
+ struct percpu_counter sp_threads_woken;
+ struct percpu_counter sp_threads_timedout;
+
#define SP_TASK_PENDING (0) /* still work to do even if no
* xprt is queued. */
#define SP_CONGESTED (1)
@@ -193,40 +190,6 @@ extern u32 svc_max_payload(const struct svc_rqst *rqstp);
#define RPCSVC_MAXPAGES ((RPCSVC_MAXPAYLOAD+PAGE_SIZE-1)/PAGE_SIZE \
+ 2 + 1)
-static inline u32 svc_getnl(struct kvec *iov)
-{
- __be32 val, *vp;
- vp = iov->iov_base;
- val = *vp++;
- iov->iov_base = (void*)vp;
- iov->iov_len -= sizeof(__be32);
- return ntohl(val);
-}
-
-static inline void svc_putnl(struct kvec *iov, u32 val)
-{
- __be32 *vp = iov->iov_base + iov->iov_len;
- *vp = htonl(val);
- iov->iov_len += sizeof(__be32);
-}
-
-static inline __be32 svc_getu32(struct kvec *iov)
-{
- __be32 val, *vp;
- vp = iov->iov_base;
- val = *vp++;
- iov->iov_base = (void*)vp;
- iov->iov_len -= sizeof(__be32);
- return val;
-}
-
-static inline void svc_putu32(struct kvec *iov, __be32 val)
-{
- __be32 *vp = iov->iov_base + iov->iov_len;
- *vp = val;
- iov->iov_len += sizeof(__be32);
-}
-
/*
* The context of a single thread, including the request currently being
* processed.
@@ -285,6 +248,7 @@ struct svc_rqst {
void * rq_argp; /* decoded arguments */
void * rq_resp; /* xdr'd results */
+ __be32 *rq_accept_statp;
void * rq_auth_data; /* flavor-specific data */
__be32 rq_auth_stat; /* authentication status */
int rq_auth_slack; /* extra space xdr code
@@ -345,29 +309,6 @@ static inline struct sockaddr *svc_daddr(const struct svc_rqst *rqst)
return (struct sockaddr *) &rqst->rq_daddr;
}
-/*
- * Check buffer bounds after decoding arguments
- */
-static inline int
-xdr_argsize_check(struct svc_rqst *rqstp, __be32 *p)
-{
- char *cp = (char *)p;
- struct kvec *vec = &rqstp->rq_arg.head[0];
- return cp >= (char*)vec->iov_base
- && cp <= (char*)vec->iov_base + vec->iov_len;
-}
-
-static inline int
-xdr_ressize_check(struct svc_rqst *rqstp, __be32 *p)
-{
- struct kvec *vec = &rqstp->rq_res.head[0];
- char *cp = (char*)p;
-
- vec->iov_len = cp - (char*)vec->iov_base;
-
- return vec->iov_len <= PAGE_SIZE;
-}
-
static inline void svc_free_res_pages(struct svc_rqst *rqstp)
{
while (rqstp->rq_next_page != rqstp->rq_respages) {
@@ -394,7 +335,7 @@ struct svc_deferred_req {
struct svc_process_info {
union {
- int (*dispatch)(struct svc_rqst *, __be32 *);
+ int (*dispatch)(struct svc_rqst *rqstp);
struct {
unsigned int lovers;
unsigned int hivers;
@@ -433,7 +374,7 @@ struct svc_version {
u32 vs_vers; /* version number */
u32 vs_nproc; /* number of procedures */
const struct svc_procedure *vs_proc; /* per-procedure info */
- unsigned int *vs_count; /* call counts */
+ unsigned long __percpu *vs_count; /* call counts */
u32 vs_xdrsize; /* xdrsize needed for this version */
/* Don't register with rpcbind */
@@ -446,7 +387,7 @@ struct svc_version {
bool vs_need_cong_ctrl;
/* Dispatch function */
- int (*vs_dispatch)(struct svc_rqst *, __be32 *);
+ int (*vs_dispatch)(struct svc_rqst *rqstp);
};
/*
@@ -540,9 +481,6 @@ static inline void svc_reserve_auth(struct svc_rqst *rqstp, int space)
* svcxdr_init_decode - Prepare an xdr_stream for Call decoding
* @rqstp: controlling server RPC transaction context
*
- * This function currently assumes the RPC header in rq_arg has
- * already been decoded. Upon return, xdr->p points to the
- * location of the upper layer header.
*/
static inline void svcxdr_init_decode(struct svc_rqst *rqstp)
{
@@ -550,11 +488,7 @@ static inline void svcxdr_init_decode(struct svc_rqst *rqstp)
struct xdr_buf *buf = &rqstp->rq_arg;
struct kvec *argv = buf->head;
- /*
- * svc_getnl() and friends do not keep the xdr_buf's ::len
- * field up to date. Refresh that field before initializing
- * the argument decoding stream.
- */
+ WARN_ON(buf->len != buf->head->iov_len + buf->page_len + buf->tail->iov_len);
buf->len = buf->head->iov_len + buf->page_len + buf->tail->iov_len;
xdr_init_decode(xdr, buf, argv->iov_base, NULL);
@@ -577,12 +511,53 @@ static inline void svcxdr_init_encode(struct svc_rqst *rqstp)
xdr->buf = buf;
xdr->iov = resv;
xdr->p = resv->iov_base + resv->iov_len;
- xdr->end = resv->iov_base + PAGE_SIZE - rqstp->rq_auth_slack;
+ xdr->end = resv->iov_base + PAGE_SIZE;
buf->len = resv->iov_len;
xdr->page_ptr = buf->pages - 1;
buf->buflen = PAGE_SIZE * (rqstp->rq_page_end - buf->pages);
- buf->buflen -= rqstp->rq_auth_slack;
xdr->rqst = NULL;
}
+/**
+ * svcxdr_set_auth_slack -
+ * @rqstp: RPC transaction
+ * @slack: buffer space to reserve for the transaction's security flavor
+ *
+ * Set the request's slack space requirement, and set aside that much
+ * space in the rqstp's rq_res.head for use when the auth wraps the Reply.
+ */
+static inline void svcxdr_set_auth_slack(struct svc_rqst *rqstp, int slack)
+{
+ struct xdr_stream *xdr = &rqstp->rq_res_stream;
+ struct xdr_buf *buf = &rqstp->rq_res;
+ struct kvec *resv = buf->head;
+
+ rqstp->rq_auth_slack = slack;
+
+ xdr->end -= XDR_QUADLEN(slack);
+ buf->buflen -= rqstp->rq_auth_slack;
+
+ WARN_ON(xdr->iov != resv);
+ WARN_ON(xdr->p > xdr->end);
+}
+
+/**
+ * svcxdr_set_accept_stat - Reserve space for the accept_stat field
+ * @rqstp: RPC transaction context
+ *
+ * Return values:
+ * %true: Success
+ * %false: No response buffer space was available
+ */
+static inline bool svcxdr_set_accept_stat(struct svc_rqst *rqstp)
+{
+ struct xdr_stream *xdr = &rqstp->rq_res_stream;
+
+ rqstp->rq_accept_statp = xdr_reserve_space(xdr, XDR_UNIT);
+ if (unlikely(!rqstp->rq_accept_statp))
+ return false;
+ *rqstp->rq_accept_statp = rpc_success;
+ return true;
+}
+
#endif /* SUNRPC_SVC_H */
diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h
index d42a75b3be10..775368802762 100644
--- a/include/linux/sunrpc/svc_xprt.h
+++ b/include/linux/sunrpc/svc_xprt.h
@@ -26,7 +26,6 @@ struct svc_xprt_ops {
void (*xpo_release_rqst)(struct svc_rqst *);
void (*xpo_detach)(struct svc_xprt *);
void (*xpo_free)(struct svc_xprt *);
- void (*xpo_secure_port)(struct svc_rqst *rqstp);
void (*xpo_kill_temp_xprt)(struct svc_xprt *);
void (*xpo_start_tls)(struct svc_xprt *);
};
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index f84e2a1358e1..72014c9216fc 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -188,7 +188,6 @@ xdr_adjust_iovec(struct kvec *iov, __be32 *p)
/*
* XDR buffer helper functions
*/
-extern void xdr_shift_buf(struct xdr_buf *, size_t);
extern void xdr_buf_from_iov(const struct kvec *, struct xdr_buf *);
extern int xdr_buf_subsegment(const struct xdr_buf *, struct xdr_buf *, unsigned int, unsigned int);
extern void xdr_buf_trim(struct xdr_buf *, unsigned int);
@@ -247,6 +246,7 @@ extern int xdr_reserve_space_vec(struct xdr_stream *xdr, struct kvec *vec,
size_t nbytes);
extern void __xdr_commit_encode(struct xdr_stream *xdr);
extern void xdr_truncate_encode(struct xdr_stream *xdr, size_t len);
+extern void xdr_truncate_decode(struct xdr_stream *xdr, size_t len);
extern int xdr_restrict_buflen(struct xdr_stream *xdr, int newbuflen);
extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
unsigned int base, unsigned int len);
@@ -346,6 +346,11 @@ ssize_t xdr_stream_decode_string(struct xdr_stream *xdr, char *str,
size_t size);
ssize_t xdr_stream_decode_string_dup(struct xdr_stream *xdr, char **str,
size_t maxlen, gfp_t gfp_flags);
+ssize_t xdr_stream_decode_opaque_auth(struct xdr_stream *xdr, u32 *flavor,
+ void **body, unsigned int *body_len);
+ssize_t xdr_stream_encode_opaque_auth(struct xdr_stream *xdr, u32 flavor,
+ void *body, unsigned int body_len);
+
/**
* xdr_align_size - Calculate padded size of an object
* @n: Size of an object being XDR encoded (in bytes)
@@ -470,6 +475,27 @@ xdr_stream_encode_u32(struct xdr_stream *xdr, __u32 n)
}
/**
+ * xdr_stream_encode_be32 - Encode a big-endian 32-bit integer
+ * @xdr: pointer to xdr_stream
+ * @n: integer to encode
+ *
+ * Return values:
+ * On success, returns length in bytes of XDR buffer consumed
+ * %-EMSGSIZE on XDR buffer overflow
+ */
+static inline ssize_t
+xdr_stream_encode_be32(struct xdr_stream *xdr, __be32 n)
+{
+ const size_t len = sizeof(n);
+ __be32 *p = xdr_reserve_space(xdr, len);
+
+ if (unlikely(!p))
+ return -EMSGSIZE;
+ *p = n;
+ return len;
+}
+
+/**
* xdr_stream_encode_u64 - Encode a 64-bit integer
* @xdr: pointer to xdr_stream
* @n: 64-bit integer to encode
diff --git a/include/trace/events/rpcgss.h b/include/trace/events/rpcgss.h
index 3f121eed369e..ba2d96a1bc2f 100644
--- a/include/trace/events/rpcgss.h
+++ b/include/trace/events/rpcgss.h
@@ -206,8 +206,30 @@ DECLARE_EVENT_CLASS(rpcgss_svc_gssapi_class,
), \
TP_ARGS(rqstp, maj_stat))
+DEFINE_SVC_GSSAPI_EVENT(wrap);
DEFINE_SVC_GSSAPI_EVENT(unwrap);
DEFINE_SVC_GSSAPI_EVENT(mic);
+DEFINE_SVC_GSSAPI_EVENT(get_mic);
+
+TRACE_EVENT(rpcgss_svc_wrap_failed,
+ TP_PROTO(
+ const struct svc_rqst *rqstp
+ ),
+
+ TP_ARGS(rqstp),
+
+ TP_STRUCT__entry(
+ __field(u32, xid)
+ __string(addr, rqstp->rq_xprt->xpt_remotebuf)
+ ),
+
+ TP_fast_assign(
+ __entry->xid = be32_to_cpu(rqstp->rq_xid);
+ __assign_str(addr, rqstp->rq_xprt->xpt_remotebuf);
+ ),
+
+ TP_printk("addr=%s xid=0x%08x", __get_str(addr), __entry->xid)
+);
TRACE_EVENT(rpcgss_svc_unwrap_failed,
TP_PROTO(
diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h
index 37604e0e5963..3ca54536f8f7 100644
--- a/include/trace/events/sunrpc.h
+++ b/include/trace/events/sunrpc.h
@@ -1819,20 +1819,20 @@ TRACE_EVENT(svc_stats_latency,
#define show_svc_xprt_flags(flags) \
__print_flags(flags, "|", \
- { (1UL << XPT_BUSY), "XPT_BUSY"}, \
- { (1UL << XPT_CONN), "XPT_CONN"}, \
- { (1UL << XPT_CLOSE), "XPT_CLOSE"}, \
- { (1UL << XPT_DATA), "XPT_DATA"}, \
- { (1UL << XPT_TEMP), "XPT_TEMP"}, \
- { (1UL << XPT_DEAD), "XPT_DEAD"}, \
- { (1UL << XPT_CHNGBUF), "XPT_CHNGBUF"}, \
- { (1UL << XPT_DEFERRED), "XPT_DEFERRED"}, \
- { (1UL << XPT_OLD), "XPT_OLD"}, \
- { (1UL << XPT_LISTENER), "XPT_LISTENER"}, \
- { (1UL << XPT_CACHE_AUTH), "XPT_CACHE_AUTH"}, \
- { (1UL << XPT_LOCAL), "XPT_LOCAL"}, \
- { (1UL << XPT_KILL_TEMP), "XPT_KILL_TEMP"}, \
- { (1UL << XPT_CONG_CTRL), "XPT_CONG_CTRL"})
+ { BIT(XPT_BUSY), "BUSY" }, \
+ { BIT(XPT_CONN), "CONN" }, \
+ { BIT(XPT_CLOSE), "CLOSE" }, \
+ { BIT(XPT_DATA), "DATA" }, \
+ { BIT(XPT_TEMP), "TEMP" }, \
+ { BIT(XPT_DEAD), "DEAD" }, \
+ { BIT(XPT_CHNGBUF), "CHNGBUF" }, \
+ { BIT(XPT_DEFERRED), "DEFERRED" }, \
+ { BIT(XPT_OLD), "OLD" }, \
+ { BIT(XPT_LISTENER), "LISTENER" }, \
+ { BIT(XPT_CACHE_AUTH), "CACHE_AUTH" }, \
+ { BIT(XPT_LOCAL), "LOCAL" }, \
+ { BIT(XPT_KILL_TEMP), "KILL_TEMP" }, \
+ { BIT(XPT_CONG_CTRL), "CONG_CTRL" })
TRACE_EVENT(svc_xprt_create_err,
TP_PROTO(
diff --git a/net/sunrpc/.kunitconfig b/net/sunrpc/.kunitconfig
new file mode 100644
index 000000000000..a55a00fa649b
--- /dev/null
+++ b/net/sunrpc/.kunitconfig
@@ -0,0 +1,30 @@
+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_DES=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 bbbb5af0af13..4afc5fd71d44 100644
--- a/net/sunrpc/Kconfig
+++ b/net/sunrpc/Kconfig
@@ -19,10 +19,10 @@ config SUNRPC_SWAP
config RPCSEC_GSS_KRB5
tristate "Secure RPC: Kerberos V mechanism"
depends on SUNRPC && CRYPTO
- depends on CRYPTO_MD5 && CRYPTO_DES && CRYPTO_CBC && CRYPTO_CTS
- depends on CRYPTO_ECB && CRYPTO_HMAC && CRYPTO_SHA1 && CRYPTO_AES
default y
select SUNRPC_GSS
+ select CRYPTO_SKCIPHER
+ select CRYPTO_HASH
help
Choose Y here to enable Secure RPC using the Kerberos version 5
GSS-API mechanism (RFC 1964).
@@ -34,21 +34,93 @@ config RPCSEC_GSS_KRB5
If unsure, say Y.
-config SUNRPC_DISABLE_INSECURE_ENCTYPES
- bool "Secure RPC: Disable insecure Kerberos encryption types"
+config RPCSEC_GSS_KRB5_SIMPLIFIED
+ bool
+ depends on RPCSEC_GSS_KRB5
+
+config RPCSEC_GSS_KRB5_CRYPTOSYSTEM
+ bool
+ depends on RPCSEC_GSS_KRB5
+
+config RPCSEC_GSS_KRB5_ENCTYPES_DES
+ bool "Enable Kerberos enctypes based on DES (deprecated)"
+ depends on RPCSEC_GSS_KRB5
+ depends on CRYPTO_CBC && CRYPTO_CTS && CRYPTO_ECB
+ depends on CRYPTO_HMAC && CRYPTO_MD5 && CRYPTO_SHA1
+ depends on CRYPTO_DES
+ default n
+ select RPCSEC_GSS_KRB5_SIMPLIFIED
+ help
+ Choose Y to enable the use of deprecated Kerberos 5
+ encryption types that utilize Data Encryption Standard
+ (DES) based ciphers. These include des-cbc-md5,
+ des-cbc-crc, and des-cbc-md4, which were deprecated by
+ RFC 6649, and des3-cbc-sha1, which was deprecated by RFC
+ 8429.
+
+ These encryption types are known to be insecure, therefore
+ the default setting of this option is N. Support for these
+ encryption types is available only for compatibility with
+ legacy NFS client and server implementations.
+
+ Removal of support is planned for a subsequent kernel
+ release.
+
+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
+ select RPCSEC_GSS_KRB5_CRYPTOSYSTEM
+ 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
+ select RPCSEC_GSS_KRB5_CRYPTOSYSTEM
+ 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
+ select RPCSEC_GSS_KRB5_CRYPTOSYSTEM
+ 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
- Choose Y here to disable the use of deprecated encryption types
- with the Kerberos version 5 GSS-API mechanism (RFC 1964). The
- deprecated encryption types include DES-CBC-MD5, DES-CBC-CRC,
- and DES-CBC-MD4. These types were deprecated by RFC 6649 because
- they were found to be insecure.
-
- N is the default because many sites have deployed KDCs and
- keytabs that contain only these deprecated encryption types.
- Choosing Y prevents the use of known-insecure encryption types
- but might result in compatibility problems.
+ 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"
diff --git a/net/sunrpc/auth_gss/Makefile b/net/sunrpc/auth_gss/Makefile
index 4a29f4c5dac4..012ae1720689 100644
--- a/net/sunrpc/auth_gss/Makefile
+++ b/net/sunrpc/auth_gss/Makefile
@@ -13,3 +13,5 @@ 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_seqnum.o gss_krb5_wrap.o gss_krb5_crypto.o gss_krb5_keys.o
+
+obj-$(CONFIG_RPCSEC_GSS_KRB5_KUNIT_TEST) += gss_krb5_test.o
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 2d7b1e03110a..1af71fbb0d80 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -49,6 +49,22 @@ static unsigned int gss_key_expire_timeo = GSS_KEY_EXPIRE_TIMEO;
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
+/*
+ * This compile-time check verifies that we will not exceed the
+ * slack space allotted by the client and server auth_gss code
+ * before they call gss_wrap().
+ */
+#define GSS_KRB5_MAX_SLACK_NEEDED \
+ (GSS_KRB5_TOK_HDR_LEN /* gss token header */ \
+ + GSS_KRB5_MAX_CKSUM_LEN /* gss token checksum */ \
+ + GSS_KRB5_MAX_BLOCKSIZE /* confounder */ \
+ + GSS_KRB5_MAX_BLOCKSIZE /* possible padding */ \
+ + GSS_KRB5_TOK_HDR_LEN /* encrypted hdr in v2 token */ \
+ + GSS_KRB5_MAX_CKSUM_LEN /* encryption hmac */ \
+ + XDR_UNIT * 2 /* RPC verifier */ \
+ + GSS_KRB5_TOK_HDR_LEN \
+ + GSS_KRB5_MAX_CKSUM_LEN)
+
#define GSS_CRED_SLACK (RPC_MAX_AUTH_SIZE * 2)
/* length of a krb5 verifier (48), plus data added before arguments when
* using integrity (two 4-byte integers): */
@@ -1042,6 +1058,7 @@ gss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
goto err_put_mech;
auth = &gss_auth->rpc_auth;
auth->au_cslack = GSS_CRED_SLACK >> 2;
+ BUILD_BUG_ON(GSS_KRB5_MAX_SLACK_NEEDED > RPC_MAX_AUTH_SIZE);
auth->au_rslack = GSS_KRB5_MAX_SLACK_NEEDED >> 2;
auth->au_verfsize = GSS_VERF_SLACK >> 2;
auth->au_ralign = GSS_VERF_SLACK >> 2;
diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c
index 3ea58175e159..6c7c52eeed4f 100644
--- a/net/sunrpc/auth_gss/gss_krb5_crypto.c
+++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c
@@ -46,11 +46,59 @@
#include <linux/random.h>
#include <linux/sunrpc/gss_krb5.h>
#include <linux/sunrpc/xdr.h>
+#include <kunit/visibility.h>
+
+#include "gss_krb5_internal.h"
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# 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,
@@ -90,6 +138,22 @@ out:
return ret;
}
+/**
+ * krb5_decrypt - simple decryption of an RPCSEC GSS payload
+ * @tfm: initialized cipher transform
+ * @iv: pointer to an IV
+ * @in: ciphertext to decrypt
+ * @out: OUT: plaintext
+ * @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 decrypted into @out
+ * negative errno: @in not decrypted
+ */
u32
krb5_decrypt(
struct crypto_sync_skcipher *tfm,
@@ -203,8 +267,8 @@ make_checksum(struct krb5_ctx *kctx, char *header, int hdrlen,
switch (kctx->gk5e->ctype) {
case CKSUMTYPE_RSA_MD5:
- err = kctx->gk5e->encrypt(kctx->seq, NULL, checksumdata,
- checksumdata, checksumlen);
+ err = krb5_encrypt(kctx->seq, NULL, checksumdata,
+ checksumdata, checksumlen);
if (err)
goto out;
memcpy(cksumout->data,
@@ -228,92 +292,76 @@ out_free_cksum:
return err ? GSS_S_FAILURE : 0;
}
-/*
- * checksum the plaintext data and hdrlen bytes of the token header
- * Per rfc4121, sec. 4.2.4, the checksum is performed over the data
- * body then over the first 16 octets of the MIC token
- * Inclusion of the header data in the calculation of the
- * checksum is optional.
+/**
+ * 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
-make_checksum_v2(struct krb5_ctx *kctx, char *header, int hdrlen,
- struct xdr_buf *body, int body_offset, u8 *cksumkey,
- unsigned int usage, struct xdr_netobj *cksumout)
+gss_krb5_checksum(struct crypto_ahash *tfm, char *header, int hdrlen,
+ const struct xdr_buf *body, int body_offset,
+ struct xdr_netobj *cksumout)
{
- struct crypto_ahash *tfm;
struct ahash_request *req;
- struct scatterlist sg[1];
- int err = -1;
+ int err = -ENOMEM;
u8 *checksumdata;
- if (kctx->gk5e->keyed_cksum == 0) {
- dprintk("%s: expected keyed hash for %s\n",
- __func__, kctx->gk5e->name);
- return GSS_S_FAILURE;
- }
- if (cksumkey == NULL) {
- dprintk("%s: no key supplied for %s\n",
- __func__, kctx->gk5e->name);
- return GSS_S_FAILURE;
- }
-
- checksumdata = kmalloc(GSS_KRB5_MAX_CKSUM_LEN, GFP_KERNEL);
+ checksumdata = kmalloc(crypto_ahash_digestsize(tfm), GFP_KERNEL);
if (!checksumdata)
return GSS_S_FAILURE;
- tfm = crypto_alloc_ahash(kctx->gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(tfm))
- goto out_free_cksum;
-
req = ahash_request_alloc(tfm, GFP_KERNEL);
if (!req)
- goto out_free_ahash;
-
+ goto out_free_cksum;
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
-
- err = crypto_ahash_setkey(tfm, cksumkey, kctx->gk5e->keylength);
- if (err)
- goto out;
-
err = crypto_ahash_init(req);
if (err)
- goto out;
+ 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;
- if (header != NULL) {
+ 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;
+ goto out_free_ahash;
}
+
ahash_request_set_crypt(req, NULL, checksumdata, 0);
err = crypto_ahash_final(req);
if (err)
- goto out;
-
- cksumout->len = kctx->gk5e->cksumlength;
+ goto out_free_ahash;
+ memcpy(cksumout->data, checksumdata, cksumout->len);
- switch (kctx->gk5e->ctype) {
- case CKSUMTYPE_HMAC_SHA1_96_AES128:
- case CKSUMTYPE_HMAC_SHA1_96_AES256:
- /* note that this truncates the hash */
- memcpy(cksumout->data, checksumdata, kctx->gk5e->cksumlength);
- break;
- default:
- BUG();
- break;
- }
-out:
- ahash_request_free(req);
out_free_ahash:
- crypto_free_ahash(tfm);
+ ahash_request_free(req);
out_free_cksum:
- kfree(checksumdata);
- return err ? GSS_S_FAILURE : 0;
+ 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];
@@ -526,7 +574,6 @@ xdr_extend_head(struct xdr_buf *buf, unsigned int base, unsigned int shiftlen)
if (shiftlen == 0)
return 0;
- BUILD_BUG_ON(GSS_KRB5_MAX_SLACK_NEEDED > RPC_MAX_AUTH_SIZE);
BUG_ON(shiftlen > RPC_MAX_AUTH_SIZE);
p = buf->head[0].iov_base + base;
@@ -595,40 +642,157 @@ out:
return ret;
}
+/**
+ * krb5_cbc_cts_encrypt - encrypt in CBC mode with CTS
+ * @cts_tfm: CBC cipher with CTS
+ * @cbc_tfm: base CBC cipher
+ * @offset: starting byte offset for plaintext
+ * @buf: OUT: output buffer
+ * @pages: plaintext
+ * @iv: output CBC initialization vector, or NULL
+ * @ivsize: size of @iv, in octets
+ *
+ * To provide confidentiality, encrypt using cipher block chaining
+ * with ciphertext stealing. Message integrity is handled separately.
+ *
+ * Return values:
+ * %0: encryption successful
+ * negative errno: encryption could not be completed
+ */
+VISIBLE_IF_KUNIT
+int krb5_cbc_cts_encrypt(struct crypto_sync_skcipher *cts_tfm,
+ struct crypto_sync_skcipher *cbc_tfm,
+ u32 offset, struct xdr_buf *buf, struct page **pages,
+ u8 *iv, unsigned int ivsize)
+{
+ u32 blocksize, nbytes, nblocks, cbcbytes;
+ struct encryptor_desc desc;
+ int err;
+
+ blocksize = crypto_sync_skcipher_blocksize(cts_tfm);
+ nbytes = buf->len - offset;
+ nblocks = (nbytes + blocksize - 1) / blocksize;
+ cbcbytes = 0;
+ if (nblocks > 2)
+ cbcbytes = (nblocks - 2) * blocksize;
+
+ memset(desc.iv, 0, sizeof(desc.iv));
+
+ /* Handle block-sized chunks of plaintext with CBC. */
+ if (cbcbytes) {
+ SYNC_SKCIPHER_REQUEST_ON_STACK(req, cbc_tfm);
+
+ desc.pos = offset;
+ desc.fragno = 0;
+ desc.fraglen = 0;
+ desc.pages = pages;
+ desc.outbuf = buf;
+ desc.req = req;
+
+ skcipher_request_set_sync_tfm(req, cbc_tfm);
+ skcipher_request_set_callback(req, 0, NULL, NULL);
+
+ sg_init_table(desc.infrags, 4);
+ sg_init_table(desc.outfrags, 4);
+
+ err = xdr_process_buf(buf, offset, cbcbytes, encryptor, &desc);
+ skcipher_request_zero(req);
+ if (err)
+ return err;
+ }
+
+ /* Remaining plaintext is handled with CBC-CTS. */
+ err = gss_krb5_cts_crypt(cts_tfm, buf, offset + cbcbytes,
+ desc.iv, pages, 1);
+ if (err)
+ return err;
+
+ if (unlikely(iv))
+ memcpy(iv, desc.iv, ivsize);
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(krb5_cbc_cts_encrypt);
+
+/**
+ * krb5_cbc_cts_decrypt - decrypt in CBC mode with CTS
+ * @cts_tfm: CBC cipher with CTS
+ * @cbc_tfm: base CBC cipher
+ * @offset: starting byte offset for plaintext
+ * @buf: OUT: output buffer
+ *
+ * Return values:
+ * %0: decryption successful
+ * negative errno: decryption could not be completed
+ */
+VISIBLE_IF_KUNIT
+int krb5_cbc_cts_decrypt(struct crypto_sync_skcipher *cts_tfm,
+ struct crypto_sync_skcipher *cbc_tfm,
+ u32 offset, struct xdr_buf *buf)
+{
+ u32 blocksize, nblocks, cbcbytes;
+ struct decryptor_desc desc;
+ int err;
+
+ blocksize = crypto_sync_skcipher_blocksize(cts_tfm);
+ nblocks = (buf->len + blocksize - 1) / blocksize;
+ cbcbytes = 0;
+ if (nblocks > 2)
+ cbcbytes = (nblocks - 2) * blocksize;
+
+ memset(desc.iv, 0, sizeof(desc.iv));
+
+ /* Handle block-sized chunks of plaintext with CBC. */
+ if (cbcbytes) {
+ SYNC_SKCIPHER_REQUEST_ON_STACK(req, cbc_tfm);
+
+ desc.fragno = 0;
+ desc.fraglen = 0;
+ desc.req = req;
+
+ skcipher_request_set_sync_tfm(req, cbc_tfm);
+ skcipher_request_set_callback(req, 0, NULL, NULL);
+
+ sg_init_table(desc.frags, 4);
+
+ err = xdr_process_buf(buf, 0, cbcbytes, decryptor, &desc);
+ skcipher_request_zero(req);
+ if (err)
+ return err;
+ }
+
+ /* Remaining plaintext is handled with CBC-CTS. */
+ return gss_krb5_cts_crypt(cts_tfm, buf, cbcbytes, desc.iv, NULL, 0);
+}
+EXPORT_SYMBOL_IF_KUNIT(krb5_cbc_cts_decrypt);
+
u32
gss_krb5_aes_encrypt(struct krb5_ctx *kctx, u32 offset,
struct xdr_buf *buf, struct page **pages)
{
u32 err;
struct xdr_netobj hmac;
- u8 *cksumkey;
u8 *ecptr;
struct crypto_sync_skcipher *cipher, *aux_cipher;
- int blocksize;
+ struct crypto_ahash *ahash;
struct page **save_pages;
- int nblocks, nbytes;
- struct encryptor_desc desc;
- u32 cbcbytes;
- unsigned int usage;
+ unsigned int conflen;
if (kctx->initiate) {
cipher = kctx->initiator_enc;
aux_cipher = kctx->initiator_enc_aux;
- cksumkey = kctx->initiator_integ;
- usage = KG_USAGE_INITIATOR_SEAL;
+ ahash = kctx->initiator_integ;
} else {
cipher = kctx->acceptor_enc;
aux_cipher = kctx->acceptor_enc_aux;
- cksumkey = kctx->acceptor_integ;
- usage = KG_USAGE_ACCEPTOR_SEAL;
+ ahash = kctx->acceptor_integ;
}
- blocksize = crypto_sync_skcipher_blocksize(cipher);
+ conflen = crypto_sync_skcipher_blocksize(cipher);
/* hide the gss token header and insert the confounder */
offset += GSS_KRB5_TOK_HDR_LEN;
- if (xdr_extend_head(buf, offset, kctx->gk5e->conflen))
+ if (xdr_extend_head(buf, offset, conflen))
return GSS_S_FAILURE;
- gss_krb5_make_confounder(buf->head[0].iov_base + offset, kctx->gk5e->conflen);
+ krb5_make_confounder(buf->head[0].iov_base + offset, conflen);
offset -= GSS_KRB5_TOK_HDR_LEN;
if (buf->tail[0].iov_base != NULL) {
@@ -659,152 +823,322 @@ gss_krb5_aes_encrypt(struct krb5_ctx *kctx, u32 offset,
save_pages = buf->pages;
buf->pages = pages;
- err = make_checksum_v2(kctx, NULL, 0, buf,
- offset + GSS_KRB5_TOK_HDR_LEN,
- cksumkey, usage, &hmac);
+ 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;
- nbytes = buf->len - offset - GSS_KRB5_TOK_HDR_LEN;
- nblocks = (nbytes + blocksize - 1) / blocksize;
- cbcbytes = 0;
- if (nblocks > 2)
- cbcbytes = (nblocks - 2) * blocksize;
-
- memset(desc.iv, 0, sizeof(desc.iv));
-
- if (cbcbytes) {
- SYNC_SKCIPHER_REQUEST_ON_STACK(req, aux_cipher);
-
- desc.pos = offset + GSS_KRB5_TOK_HDR_LEN;
- desc.fragno = 0;
- desc.fraglen = 0;
- desc.pages = pages;
- desc.outbuf = buf;
- desc.req = req;
-
- skcipher_request_set_sync_tfm(req, aux_cipher);
- 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 + GSS_KRB5_TOK_HDR_LEN,
- cbcbytes, encryptor, &desc);
- skcipher_request_zero(req);
- if (err)
- goto out_err;
- }
-
- /* Make sure IV carries forward from any CBC results. */
- err = gss_krb5_cts_crypt(cipher, buf,
- offset + GSS_KRB5_TOK_HDR_LEN + cbcbytes,
- desc.iv, pages, 1);
- if (err) {
- err = GSS_S_FAILURE;
- goto out_err;
- }
+ 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;
-out_err:
- if (err)
- err = GSS_S_FAILURE;
- return err;
+ 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 xdr_buf subbuf;
- u32 ret = 0;
- u8 *cksum_key;
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];
- int nblocks, blocksize, cbcbytes;
- struct decryptor_desc desc;
- unsigned int usage;
+ struct xdr_buf subbuf;
+ u32 ret = 0;
if (kctx->initiate) {
cipher = kctx->acceptor_enc;
aux_cipher = kctx->acceptor_enc_aux;
- cksum_key = kctx->acceptor_integ;
- usage = KG_USAGE_ACCEPTOR_SEAL;
+ ahash = kctx->acceptor_integ;
} else {
cipher = kctx->initiator_enc;
aux_cipher = kctx->initiator_enc_aux;
- cksum_key = kctx->initiator_integ;
- usage = KG_USAGE_INITIATOR_SEAL;
+ ahash = kctx->initiator_integ;
}
- blocksize = crypto_sync_skcipher_blocksize(cipher);
-
/* 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));
- nblocks = (subbuf.len + blocksize - 1) / blocksize;
+ ret = krb5_cbc_cts_decrypt(cipher, aux_cipher, 0, &subbuf);
+ if (ret)
+ goto out_err;
- cbcbytes = 0;
- if (nblocks > 2)
- cbcbytes = (nblocks - 2) * blocksize;
+ /* Calculate our hmac over the plaintext data */
+ our_hmac_obj.len = sizeof(our_hmac);
+ our_hmac_obj.data = our_hmac;
+ ret = gss_krb5_checksum(ahash, NULL, 0, &subbuf, 0, &our_hmac_obj);
+ if (ret)
+ goto out_err;
- memset(desc.iv, 0, sizeof(desc.iv));
+ /* 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 (cbcbytes) {
- SYNC_SKCIPHER_REQUEST_ON_STACK(req, aux_cipher);
+ 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;
+}
- desc.fragno = 0;
- desc.fraglen = 0;
- desc.req = req;
+/**
+ * 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;
- skcipher_request_set_sync_tfm(req, aux_cipher);
- skcipher_request_set_callback(req, 0, NULL, NULL);
+ checksumdata = kmalloc(crypto_ahash_digestsize(tfm), GFP_KERNEL);
+ if (!checksumdata)
+ return GSS_S_FAILURE;
+ /* For RPCSEC, the "initial cipher state" is always all zeroes. */
+ iv = kzalloc(ivsize, GFP_KERNEL);
+ if (!iv)
+ goto out_free_mem;
- sg_init_table(desc.frags, 4);
+ 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;
- ret = xdr_process_buf(&subbuf, 0, cbcbytes, decryptor, &desc);
- skcipher_request_zero(req);
- if (ret)
- goto out_err;
+ sg_init_one(sg, iv, ivsize);
+ ahash_request_set_crypt(req, sg, NULL, ivsize);
+ err = crypto_ahash_update(req);
+ if (err)
+ goto out_free_ahash;
+ err = xdr_process_buf(body, body_offset, body->len - body_offset,
+ checksummer, req);
+ if (err)
+ goto out_free_ahash;
+
+ ahash_request_set_crypt(req, NULL, checksumdata, 0);
+ err = crypto_ahash_final(req);
+ if (err)
+ goto out_free_ahash;
+ memcpy(cksumout->data, checksumdata, cksumout->len);
+
+out_free_ahash:
+ ahash_request_free(req);
+out_free_mem:
+ kfree(iv);
+ kfree_sensitive(checksumdata);
+ return err ? GSS_S_FAILURE : GSS_S_COMPLETE;
+}
+EXPORT_SYMBOL_IF_KUNIT(krb5_etm_checksum);
+
+/**
+ * krb5_etm_encrypt - Encrypt using the RFC 8009 rules
+ * @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.
+ *
+ * Return values:
+ * %GSS_S_COMPLETE: Encryption successful
+ * %GSS_S_FAILURE: Encryption failed
+ */
+u32
+krb5_etm_encrypt(struct krb5_ctx *kctx, u32 offset,
+ struct xdr_buf *buf, struct page **pages)
+{
+ struct crypto_sync_skcipher *cipher, *aux_cipher;
+ struct crypto_ahash *ahash;
+ struct xdr_netobj hmac;
+ unsigned int conflen;
+ u8 *ecptr;
+ u32 err;
+
+ if (kctx->initiate) {
+ cipher = kctx->initiator_enc;
+ aux_cipher = kctx->initiator_enc_aux;
+ ahash = kctx->initiator_integ;
+ } else {
+ cipher = kctx->acceptor_enc;
+ aux_cipher = kctx->acceptor_enc_aux;
+ ahash = kctx->acceptor_integ;
}
+ conflen = crypto_sync_skcipher_blocksize(cipher);
- /* Make sure IV carries forward from any CBC results. */
- ret = gss_krb5_cts_crypt(cipher, &subbuf, cbcbytes, desc.iv, NULL, 0);
- if (ret)
+ offset += GSS_KRB5_TOK_HDR_LEN;
+ if (xdr_extend_head(buf, offset, conflen))
+ return GSS_S_FAILURE;
+ krb5_make_confounder(buf->head[0].iov_base + offset, conflen);
+ offset -= GSS_KRB5_TOK_HDR_LEN;
+
+ if (buf->tail[0].iov_base) {
+ ecptr = buf->tail[0].iov_base + buf->tail[0].iov_len;
+ } else {
+ buf->tail[0].iov_base = buf->head[0].iov_base
+ + buf->head[0].iov_len;
+ buf->tail[0].iov_len = 0;
+ ecptr = buf->tail[0].iov_base;
+ }
+
+ memcpy(ecptr, buf->head[0].iov_base + offset, GSS_KRB5_TOK_HDR_LEN);
+ buf->tail[0].iov_len += GSS_KRB5_TOK_HDR_LEN;
+ buf->len += GSS_KRB5_TOK_HDR_LEN;
+
+ err = krb5_cbc_cts_encrypt(cipher, aux_cipher,
+ offset + GSS_KRB5_TOK_HDR_LEN,
+ buf, pages, NULL, 0);
+ if (err)
+ 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;
+ return GSS_S_COMPLETE;
- /* Calculate our hmac over the plaintext data */
- our_hmac_obj.len = sizeof(our_hmac);
- our_hmac_obj.data = our_hmac;
+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:
+ * @buf:
+ * @headskip: OUT: the enctype's confounder length, in octets
+ * @tailskip: OUT: the enctype's HMAC length, in octets
+ *
+ * RFC 8009 Section 5:
+ *
+ * decryption function: as follows, where D() is AES decryption in
+ * CBC-CS3 mode, and h is the size of truncated HMAC.
+ *
+ * (C, H) = ciphertext
+ * (Note: H is the last h bits of the ciphertext.)
+ * IV = cipher state
+ * if H != HMAC(Ki, IV | C)[1..h]
+ * stop, report error
+ * (N, P) = D(Ke, C, IV)
+ *
+ * Return values:
+ * %GSS_S_COMPLETE: Decryption successful
+ * %GSS_S_BAD_SIG: computed HMAC != received HMAC
+ * %GSS_S_FAILURE: Decryption failed
+ */
+u32
+krb5_etm_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
+ struct xdr_buf *buf, u32 *headskip, u32 *tailskip)
+{
+ 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;
- ret = make_checksum_v2(kctx, NULL, 0, &subbuf, 0,
- cksum_key, usage, &our_hmac_obj);
+ 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;
+ }
+
+ /* Extract the ciphertext into @subbuf. */
+ xdr_buf_subsegment(buf, &subbuf, offset + GSS_KRB5_TOK_HDR_LEN,
+ (len - offset - GSS_KRB5_TOK_HDR_LEN -
+ kctx->gk5e->cksumlength));
+
+ our_hmac_obj.data = our_hmac;
+ our_hmac_obj.len = kctx->gk5e->cksumlength;
+ ret = krb5_etm_checksum(cipher, ahash, &subbuf, 0, &our_hmac_obj);
if (ret)
goto out_err;
-
- /* Get the packet's hmac value */
ret = read_bytes_from_xdr_buf(buf, len - kctx->gk5e->cksumlength,
pkt_hmac, kctx->gk5e->cksumlength);
if (ret)
goto out_err;
-
if (crypto_memneq(pkt_hmac, our_hmac, kctx->gk5e->cksumlength) != 0) {
ret = GSS_S_BAD_SIG;
goto out_err;
}
- *headskip = kctx->gk5e->conflen;
+
+ ret = krb5_cbc_cts_decrypt(cipher, aux_cipher, 0, &subbuf);
+ if (ret) {
+ ret = GSS_S_FAILURE;
+ goto out_err;
+ }
+
+ *headskip = crypto_sync_skcipher_blocksize(cipher);
*tailskip = kctx->gk5e->cksumlength;
+ return GSS_S_COMPLETE;
+
out_err:
- if (ret && ret != GSS_S_BAD_SIG)
+ if (ret != GSS_S_BAD_SIG)
ret = GSS_S_FAILURE;
return ret;
}
diff --git a/net/sunrpc/auth_gss/gss_krb5_internal.h b/net/sunrpc/auth_gss/gss_krb5_internal.h
new file mode 100644
index 000000000000..b673e2626acb
--- /dev/null
+++ b/net/sunrpc/auth_gss/gss_krb5_internal.h
@@ -0,0 +1,232 @@
+/* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */
+/*
+ * SunRPC GSS Kerberos 5 mechanism internal definitions
+ *
+ * Copyright (c) 2022 Oracle and/or its affiliates.
+ */
+
+#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)
+
+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 (*import_ctx)(struct krb5_ctx *ctx, gfp_t gfp_mask);
+ 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
+
+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;
+ 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;
+};
+
+/*
+ * GSS Kerberos 5 mechanism Per-Message calls.
+ */
+
+u32 gss_krb5_get_mic_v1(struct krb5_ctx *ctx, struct xdr_buf *text,
+ struct xdr_netobj *token);
+u32 gss_krb5_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text,
+ struct xdr_netobj *token);
+
+u32 gss_krb5_verify_mic_v1(struct krb5_ctx *ctx, struct xdr_buf *message_buffer,
+ struct xdr_netobj *read_token);
+u32 gss_krb5_verify_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *message_buffer,
+ struct xdr_netobj *read_token);
+
+u32 gss_krb5_wrap_v1(struct krb5_ctx *kctx, int offset,
+ struct xdr_buf *buf, struct page **pages);
+u32 gss_krb5_wrap_v2(struct krb5_ctx *kctx, int offset,
+ struct xdr_buf *buf, struct page **pages);
+
+u32 gss_krb5_unwrap_v1(struct krb5_ctx *kctx, int offset, int len,
+ struct xdr_buf *buf, unsigned int *slack,
+ unsigned int *align);
+u32 gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
+ struct xdr_buf *buf, unsigned int *slack,
+ unsigned int *align);
+
+/*
+ * Implementation internal functions
+ */
+
+/* Key Derivation Functions */
+
+int krb5_derive_key_v1(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_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);
+}
+
+s32 krb5_make_seq_num(struct krb5_ctx *kctx, struct crypto_sync_skcipher *key,
+ int direction, u32 seqnum, unsigned char *cksum,
+ unsigned char *buf);
+
+s32 krb5_get_seq_num(struct krb5_ctx *kctx, unsigned char *cksum,
+ unsigned char *buf, int *direction, u32 *seqnum);
+
+void krb5_make_confounder(u8 *p, int conflen);
+
+u32 make_checksum(struct krb5_ctx *kctx, char *header, int hdrlen,
+ struct xdr_buf *body, int body_offset, u8 *cksumkey,
+ unsigned int usage, struct xdr_netobj *cksumout);
+
+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);
+
+u32 krb5_decrypt(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);
+
+int gss_encrypt_xdr_buf(struct crypto_sync_skcipher *tfm,
+ struct xdr_buf *outbuf, int offset,
+ struct page **pages);
+
+int gss_decrypt_xdr_buf(struct crypto_sync_skcipher *tfm,
+ struct xdr_buf *inbuf, int offset);
+
+u32 gss_krb5_aes_encrypt(struct krb5_ctx *kctx, u32 offset,
+ struct xdr_buf *buf, struct page **pages);
+
+u32 gss_krb5_aes_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
+ struct xdr_buf *buf, u32 *plainoffset, u32 *plainlen);
+
+u32 krb5_etm_encrypt(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf,
+ struct page **pages);
+
+u32 krb5_etm_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
+ struct xdr_buf *buf, u32 *headskip, u32 *tailskip);
+
+#if IS_ENABLED(CONFIG_KUNIT)
+void krb5_nfold(u32 inbits, const u8 *in, u32 outbits, u8 *out);
+const struct gss_krb5_enctype *gss_krb5_lookup_enctype(u32 etype);
+int krb5_cbc_cts_encrypt(struct crypto_sync_skcipher *cts_tfm,
+ struct crypto_sync_skcipher *cbc_tfm, u32 offset,
+ struct xdr_buf *buf, struct page **pages,
+ u8 *iv, unsigned int ivsize);
+int krb5_cbc_cts_decrypt(struct crypto_sync_skcipher *cts_tfm,
+ struct crypto_sync_skcipher *cbc_tfm,
+ u32 offset, struct xdr_buf *buf);
+u32 krb5_etm_checksum(struct crypto_sync_skcipher *cipher,
+ struct crypto_ahash *tfm, const struct xdr_buf *body,
+ int body_offset, struct xdr_netobj *cksumout);
+#endif
+
+#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
index 726c076950c0..5347fe1cc93f 100644
--- a/net/sunrpc/auth_gss/gss_krb5_keys.c
+++ b/net/sunrpc/auth_gss/gss_krb5_keys.c
@@ -60,18 +60,27 @@
#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.
*/
-
-static void krb5_nfold(u32 inbits, const u8 *in,
- u32 outbits, u8 *out)
+VISIBLE_IF_KUNIT
+void krb5_nfold(u32 inbits, const u8 *in, u32 outbits, u8 *out)
{
unsigned long ulcm;
int byte, i, msbit;
@@ -132,40 +141,36 @@ static void krb5_nfold(u32 inbits, const u8 *in,
}
}
}
+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.
*/
-
-u32 krb5_derive_key(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)
+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, *rawkey;
+ unsigned char *inblockdata, *outblockdata;
struct xdr_netobj inblock, outblock;
struct crypto_sync_skcipher *cipher;
- u32 ret = EINVAL;
+ int ret = -EINVAL;
- blocksize = gk5e->blocksize;
keybytes = gk5e->keybytes;
keylength = gk5e->keylength;
- if ((inkey->len != keylength) || (outkey->len != 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_return;
- /* allocate and set up buffers */
-
- ret = ENOMEM;
+ ret = -ENOMEM;
inblockdata = kmalloc(blocksize, gfp_mask);
if (inblockdata == NULL)
goto err_free_cipher;
@@ -174,10 +179,6 @@ u32 krb5_derive_key(const struct gss_krb5_enctype *gk5e,
if (outblockdata == NULL)
goto err_free_in;
- rawkey = kmalloc(keybytes, gfp_mask);
- if (rawkey == NULL)
- goto err_free_out;
-
inblock.data = (char *) inblockdata;
inblock.len = blocksize;
@@ -197,8 +198,8 @@ u32 krb5_derive_key(const struct gss_krb5_enctype *gk5e,
n = 0;
while (n < keybytes) {
- (*(gk5e->encrypt))(cipher, NULL, inblock.data,
- outblock.data, inblock.len);
+ krb5_encrypt(cipher, NULL, inblock.data, outblock.data,
+ inblock.len);
if ((keybytes - n) <= outblock.len) {
memcpy(rawkey + n, outblock.data, (keybytes - n));
@@ -210,26 +211,8 @@ u32 krb5_derive_key(const struct gss_krb5_enctype *gk5e,
n += outblock.len;
}
- /* postprocess the key */
-
- inblock.data = (char *) rawkey;
- inblock.len = keybytes;
-
- BUG_ON(gk5e->mk_key == NULL);
- ret = (*(gk5e->mk_key))(gk5e, &inblock, outkey);
- if (ret) {
- dprintk("%s: got %d from mk_key function for '%s'\n",
- __func__, ret, gk5e->encrypt_name);
- goto err_free_raw;
- }
-
- /* clean memory, free resources and exit */
-
ret = 0;
-err_free_raw:
- kfree_sensitive(rawkey);
-err_free_out:
kfree_sensitive(outblockdata);
err_free_in:
kfree_sensitive(inblockdata);
@@ -252,15 +235,11 @@ static void mit_des_fixup_key_parity(u8 key[8])
}
}
-/*
- * This is the des3 key derivation postprocess function
- */
-u32 gss_krb5_des3_make_key(const struct gss_krb5_enctype *gk5e,
- struct xdr_netobj *randombits,
- struct xdr_netobj *key)
+static int krb5_random_to_key_v1(const struct gss_krb5_enctype *gk5e,
+ struct xdr_netobj *randombits,
+ struct xdr_netobj *key)
{
- int i;
- u32 ret = EINVAL;
+ int i, ret = -EINVAL;
if (key->len != 24) {
dprintk("%s: key->len is %d\n", __func__, key->len);
@@ -292,14 +271,49 @@ err_out:
return ret;
}
+/**
+ * krb5_derive_key_v1 - Derive a subkey for an RFC 3961 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_v1(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_v1(gk5e, &inblock, outkey);
+
+ kfree_sensitive(inblock.data);
+ return ret;
+}
+
/*
- * This is the aes key derivation postprocess function
+ * This is the identity function, with some sanity checking.
*/
-u32 gss_krb5_aes_make_key(const struct gss_krb5_enctype *gk5e,
- struct xdr_netobj *randombits,
- struct xdr_netobj *key)
+static int krb5_random_to_key_v2(const struct gss_krb5_enctype *gk5e,
+ struct xdr_netobj *randombits,
+ struct xdr_netobj *key)
{
- u32 ret = EINVAL;
+ int ret = -EINVAL;
if (key->len != 16 && key->len != 32) {
dprintk("%s: key->len is %d\n", __func__, key->len);
@@ -320,3 +334,297 @@ u32 gss_krb5_aes_make_key(const struct gss_krb5_enctype *gk5e,
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 1c092b05c2bb..20e21d08badb 100644
--- a/net/sunrpc/auth_gss/gss_krb5_mech.c
+++ b/net/sunrpc/auth_gss/gss_krb5_mech.c
@@ -19,18 +19,27 @@
#include <linux/sunrpc/auth.h>
#include <linux/sunrpc/gss_krb5.h>
#include <linux/sunrpc/xdr.h>
-#include <linux/sunrpc/gss_krb5_enctypes.h>
+#include <kunit/visibility.h>
#include "auth_gss_internal.h"
+#include "gss_krb5_internal.h"
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
-static struct gss_api_mech gss_kerberos_mech; /* forward declaration */
+static struct gss_api_mech gss_kerberos_mech;
+
+#if defined(CONFIG_RPCSEC_GSS_KRB5_SIMPLIFIED)
+static int gss_krb5_import_ctx_des(struct krb5_ctx *ctx, gfp_t gfp_mask);
+static int gss_krb5_import_ctx_v1(struct krb5_ctx *ctx, gfp_t gfp_mask);
+#endif
+#if defined(CONFIG_RPCSEC_GSS_KRB5_CRYPTOSYSTEM)
+static int gss_krb5_import_ctx_v2(struct krb5_ctx *ctx, gfp_t gfp_mask);
+#endif
static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = {
-#ifndef CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES
+#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_DES)
/*
* DES (All DES enctypes are mapped to the same gss functionality)
*/
@@ -40,19 +49,18 @@ static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = {
.name = "des-cbc-crc",
.encrypt_name = "cbc(des)",
.cksum_name = "md5",
- .encrypt = krb5_encrypt,
- .decrypt = krb5_decrypt,
- .mk_key = NULL,
+ .import_ctx = gss_krb5_import_ctx_des,
+ .get_mic = gss_krb5_get_mic_v1,
+ .verify_mic = gss_krb5_verify_mic_v1,
+ .wrap = gss_krb5_wrap_v1,
+ .unwrap = gss_krb5_unwrap_v1,
.signalg = SGN_ALG_DES_MAC_MD5,
.sealalg = SEAL_ALG_DES,
.keybytes = 7,
.keylength = 8,
- .blocksize = 8,
- .conflen = 8,
.cksumlength = 8,
.keyed_cksum = 0,
},
-#endif /* CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES */
/*
* 3DES
*/
@@ -62,100 +70,291 @@ static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = {
.name = "des3-hmac-sha1",
.encrypt_name = "cbc(des3_ede)",
.cksum_name = "hmac(sha1)",
- .encrypt = krb5_encrypt,
- .decrypt = krb5_decrypt,
- .mk_key = gss_krb5_des3_make_key,
+ .import_ctx = gss_krb5_import_ctx_v1,
+ .derive_key = krb5_derive_key_v1,
+ .get_mic = gss_krb5_get_mic_v1,
+ .verify_mic = gss_krb5_verify_mic_v1,
+ .wrap = gss_krb5_wrap_v1,
+ .unwrap = gss_krb5_unwrap_v1,
.signalg = SGN_ALG_HMAC_SHA1_DES3_KD,
.sealalg = SEAL_ALG_DES3KD,
.keybytes = 21,
.keylength = 24,
- .blocksize = 8,
- .conflen = 8,
.cksumlength = 20,
.keyed_cksum = 1,
},
+#endif
+
+#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1)
/*
- * AES128
+ * 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)",
- .encrypt = krb5_encrypt,
- .decrypt = krb5_decrypt,
- .mk_key = gss_krb5_aes_make_key,
- .encrypt_v2 = gss_krb5_aes_encrypt,
- .decrypt_v2 = gss_krb5_aes_decrypt,
+ .import_ctx = gss_krb5_import_ctx_v2,
+ .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 = 16,
- .blocksize = 16,
- .conflen = 16,
- .cksumlength = 12,
+ .keylength = BITS2OCTETS(128),
+ .Kc_length = BITS2OCTETS(128),
+ .Ke_length = BITS2OCTETS(128),
+ .Ki_length = BITS2OCTETS(128),
+ .cksumlength = BITS2OCTETS(96),
.keyed_cksum = 1,
},
/*
- * AES256
+ * 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)",
- .encrypt = krb5_encrypt,
- .decrypt = krb5_decrypt,
- .mk_key = gss_krb5_aes_make_key,
- .encrypt_v2 = gss_krb5_aes_encrypt,
- .decrypt_v2 = gss_krb5_aes_decrypt,
+ .import_ctx = gss_krb5_import_ctx_v2,
+ .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 = 32,
- .blocksize = 16,
- .conflen = 16,
- .cksumlength = 12,
+ .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),
+
+ .import_ctx = gss_krb5_import_ctx_v2,
+ .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),
+
+ .import_ctx = gss_krb5_import_ctx_v2,
+ .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),
+
+ .import_ctx = gss_krb5_import_ctx_v2,
+ .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),
+
+ .import_ctx = gss_krb5_import_ctx_v2,
+ .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
};
-static const int num_supported_enctypes =
- ARRAY_SIZE(supported_gss_krb5_enctypes);
+/*
+ * The list of advertised enctypes is specified in order of most
+ * preferred to least.
+ */
+static char gss_krb5_enctype_priority_list[64];
-static int
-supported_gss_krb5_enctype(int etype)
+static void gss_krb5_prepare_enctype_priority_list(void)
{
- int i;
- for (i = 0; i < num_supported_enctypes; i++)
- if (supported_gss_krb5_enctypes[i].etype == etype)
- return 1;
- return 0;
+ 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
+#if defined(CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_DES)
+ ENCTYPE_DES3_CBC_SHA1,
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_CRC,
+ ENCTYPE_DES_CBC_MD4,
+#endif
+ };
+ size_t total, i;
+ char buf[16];
+ char *sep;
+ int n;
+
+ sep = "";
+ gss_krb5_enctype_priority_list[0] = '\0';
+ for (total = 0, i = 0; i < ARRAY_SIZE(gss_krb5_enctypes); i++) {
+ n = sprintf(buf, "%s%u", sep, gss_krb5_enctypes[i]);
+ if (n < 0)
+ break;
+ if (total + n >= sizeof(gss_krb5_enctype_priority_list))
+ break;
+ strcat(gss_krb5_enctype_priority_list, buf);
+ sep = ",";
+ total += n;
+ }
}
-static const struct gss_krb5_enctype *
-get_gss_krb5_enctype(int etype)
+/**
+ * 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)
{
- int i;
- for (i = 0; i < num_supported_enctypes; i++)
+ 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_v1(struct krb5_ctx *ctx, struct xdr_netobj *key)
+{
+ struct crypto_sync_skcipher *tfm;
+
+ tfm = crypto_alloc_sync_skcipher(ctx->gk5e->encrypt_name, 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 inline const void *
get_key(const void *p, const void *end,
struct krb5_ctx *ctx, struct crypto_sync_skcipher **res)
{
+ struct crypto_sync_skcipher *tfm;
struct xdr_netobj key;
int alg;
p = simple_get_bytes(p, end, &alg, sizeof(alg));
if (IS_ERR(p))
goto out_err;
-
switch (alg) {
case ENCTYPE_DES_CBC_CRC:
case ENCTYPE_DES_CBC_MD4:
@@ -164,37 +363,26 @@ get_key(const void *p, const void *end,
alg = ENCTYPE_DES_CBC_RAW;
break;
}
-
- if (!supported_gss_krb5_enctype(alg)) {
- printk(KERN_WARNING "gss_kerberos_mech: unsupported "
- "encryption key algorithm %d\n", alg);
- p = ERR_PTR(-EINVAL);
- goto out_err;
+ if (!gss_krb5_lookup_enctype(alg)) {
+ pr_warn("gss_krb5: unsupported enctype: %d\n", alg);
+ goto out_err_inval;
}
+
p = simple_get_netobj(p, end, &key);
if (IS_ERR(p))
goto out_err;
-
- *res = crypto_alloc_sync_skcipher(ctx->gk5e->encrypt_name, 0, 0);
- if (IS_ERR(*res)) {
- printk(KERN_WARNING "gss_kerberos_mech: unable to initialize "
- "crypto algorithm %s\n", ctx->gk5e->encrypt_name);
- *res = NULL;
- goto out_err_free_key;
- }
- if (crypto_sync_skcipher_setkey(*res, key.data, key.len)) {
- printk(KERN_WARNING "gss_kerberos_mech: error setting key for "
- "crypto algorithm %s\n", ctx->gk5e->encrypt_name);
- goto out_err_free_tfm;
+ tfm = gss_krb5_alloc_cipher_v1(ctx, &key);
+ kfree(key.data);
+ if (!tfm) {
+ pr_warn("gss_krb5: failed to initialize cipher '%s'\n",
+ ctx->gk5e->encrypt_name);
+ goto out_err_inval;
}
+ *res = tfm;
- kfree(key.data);
return p;
-out_err_free_tfm:
- crypto_free_sync_skcipher(*res);
-out_err_free_key:
- kfree(key.data);
+out_err_inval:
p = ERR_PTR(-EINVAL);
out_err:
return p;
@@ -214,7 +402,7 @@ gss_import_v1_context(const void *p, const void *end, struct krb5_ctx *ctx)
/* Old format supports only DES! Any other enctype uses new format */
ctx->enctype = ENCTYPE_DES_CBC_RAW;
- ctx->gk5e = get_gss_krb5_enctype(ctx->enctype);
+ ctx->gk5e = gss_krb5_lookup_enctype(ctx->enctype);
if (ctx->gk5e == NULL) {
p = ERR_PTR(-EINVAL);
goto out_err;
@@ -278,70 +466,34 @@ out_err:
return PTR_ERR(p);
}
-static struct crypto_sync_skcipher *
-context_v2_alloc_cipher(struct krb5_ctx *ctx, const char *cname, u8 *key)
-{
- struct crypto_sync_skcipher *cp;
-
- cp = crypto_alloc_sync_skcipher(cname, 0, 0);
- if (IS_ERR(cp)) {
- dprintk("gss_kerberos_mech: unable to initialize "
- "crypto algorithm %s\n", cname);
- return NULL;
- }
- if (crypto_sync_skcipher_setkey(cp, key, ctx->gk5e->keylength)) {
- dprintk("gss_kerberos_mech: error setting key for "
- "crypto algorithm %s\n", cname);
- crypto_free_sync_skcipher(cp);
- return NULL;
- }
- return cp;
-}
-
-static inline void
-set_cdata(u8 cdata[GSS_KRB5_K5CLENGTH], u32 usage, u8 seed)
+#if defined(CONFIG_RPCSEC_GSS_KRB5_SIMPLIFIED)
+static int
+gss_krb5_import_ctx_des(struct krb5_ctx *ctx, gfp_t gfp_mask)
{
- cdata[0] = (usage>>24)&0xff;
- cdata[1] = (usage>>16)&0xff;
- cdata[2] = (usage>>8)&0xff;
- cdata[3] = usage&0xff;
- cdata[4] = seed;
+ return -EINVAL;
}
static int
-context_derive_keys_des3(struct krb5_ctx *ctx, gfp_t gfp_mask)
+gss_krb5_import_ctx_v1(struct krb5_ctx *ctx, gfp_t gfp_mask)
{
- struct xdr_netobj c, keyin, keyout;
- u8 cdata[GSS_KRB5_K5CLENGTH];
- u32 err;
-
- c.len = GSS_KRB5_K5CLENGTH;
- c.data = cdata;
+ struct xdr_netobj keyin, keyout;
keyin.data = ctx->Ksess;
keyin.len = ctx->gk5e->keylength;
- keyout.len = ctx->gk5e->keylength;
- /* seq uses the raw key */
- ctx->seq = context_v2_alloc_cipher(ctx, ctx->gk5e->encrypt_name,
- ctx->Ksess);
+ ctx->seq = gss_krb5_alloc_cipher_v1(ctx, &keyin);
if (ctx->seq == NULL)
goto out_err;
-
- ctx->enc = context_v2_alloc_cipher(ctx, ctx->gk5e->encrypt_name,
- ctx->Ksess);
+ ctx->enc = gss_krb5_alloc_cipher_v1(ctx, &keyin);
if (ctx->enc == NULL)
goto out_free_seq;
/* derive cksum */
- set_cdata(cdata, KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM);
keyout.data = ctx->cksum;
- err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
- if (err) {
- dprintk("%s: Error %d deriving cksum key\n",
- __func__, err);
+ keyout.len = ctx->gk5e->keylength;
+ if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_SIGN,
+ KEY_USAGE_SEED_CHECKSUM, gfp_mask))
goto out_free_enc;
- }
return 0;
@@ -352,118 +504,140 @@ out_free_seq:
out_err:
return -EINVAL;
}
+#endif
-static int
-context_derive_keys_new(struct krb5_ctx *ctx, gfp_t gfp_mask)
+#if defined(CONFIG_RPCSEC_GSS_KRB5_CRYPTOSYSTEM)
+
+static struct crypto_sync_skcipher *
+gss_krb5_alloc_cipher_v2(const char *cname, const struct xdr_netobj *key)
{
- struct xdr_netobj c, keyin, keyout;
- u8 cdata[GSS_KRB5_K5CLENGTH];
- u32 err;
+ struct crypto_sync_skcipher *tfm;
- c.len = GSS_KRB5_K5CLENGTH;
- c.data = cdata;
+ 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;
+}
- keyin.data = ctx->Ksess;
- keyin.len = ctx->gk5e->keylength;
- keyout.len = ctx->gk5e->keylength;
+static struct crypto_ahash *
+gss_krb5_alloc_hash_v2(struct krb5_ctx *kctx, const struct xdr_netobj *key)
+{
+ struct crypto_ahash *tfm;
- /* initiator seal encryption */
- set_cdata(cdata, KG_USAGE_INITIATOR_SEAL, KEY_USAGE_SEED_ENCRYPTION);
- keyout.data = ctx->initiator_seal;
- err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
- if (err) {
- dprintk("%s: Error %d deriving initiator_seal key\n",
- __func__, err);
- goto out_err;
+ 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;
}
- ctx->initiator_enc = context_v2_alloc_cipher(ctx,
- ctx->gk5e->encrypt_name,
- ctx->initiator_seal);
+ 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,
+ .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_err;
+ 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;
+ }
/* acceptor seal encryption */
- set_cdata(cdata, KG_USAGE_ACCEPTOR_SEAL, KEY_USAGE_SEED_ENCRYPTION);
- keyout.data = ctx->acceptor_seal;
- err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
- if (err) {
- dprintk("%s: Error %d deriving acceptor_seal key\n",
- __func__, err);
- goto out_free_initiator_enc;
- }
- ctx->acceptor_enc = context_v2_alloc_cipher(ctx,
- ctx->gk5e->encrypt_name,
- ctx->acceptor_seal);
+ if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_ACCEPTOR_SEAL,
+ KEY_USAGE_SEED_ENCRYPTION, gfp_mask))
+ goto out_free;
+ ctx->acceptor_enc = gss_krb5_alloc_cipher_v2(ctx->gk5e->encrypt_name,
+ &keyout);
if (ctx->acceptor_enc == NULL)
- goto out_free_initiator_enc;
+ 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 */
- set_cdata(cdata, KG_USAGE_INITIATOR_SIGN, KEY_USAGE_SEED_CHECKSUM);
- keyout.data = ctx->initiator_sign;
- err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
- if (err) {
- dprintk("%s: Error %d deriving initiator_sign key\n",
- __func__, err);
- goto out_free_acceptor_enc;
- }
+ keyout.len = ctx->gk5e->Kc_length;
+ if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_INITIATOR_SIGN,
+ KEY_USAGE_SEED_CHECKSUM, gfp_mask))
+ goto out_free;
+ ctx->initiator_sign = gss_krb5_alloc_hash_v2(ctx, &keyout);
+ if (ctx->initiator_sign == NULL)
+ goto out_free;
/* acceptor sign checksum */
- set_cdata(cdata, KG_USAGE_ACCEPTOR_SIGN, KEY_USAGE_SEED_CHECKSUM);
- keyout.data = ctx->acceptor_sign;
- err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
- if (err) {
- dprintk("%s: Error %d deriving acceptor_sign key\n",
- __func__, err);
- goto out_free_acceptor_enc;
- }
+ 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 */
- set_cdata(cdata, KG_USAGE_INITIATOR_SEAL, KEY_USAGE_SEED_INTEGRITY);
- keyout.data = ctx->initiator_integ;
- err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
- if (err) {
- dprintk("%s: Error %d deriving initiator_integ key\n",
- __func__, err);
- goto out_free_acceptor_enc;
- }
+ 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 */
- set_cdata(cdata, KG_USAGE_ACCEPTOR_SEAL, KEY_USAGE_SEED_INTEGRITY);
- keyout.data = ctx->acceptor_integ;
- err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
- if (err) {
- dprintk("%s: Error %d deriving acceptor_integ key\n",
- __func__, err);
- goto out_free_acceptor_enc;
- }
-
- switch (ctx->enctype) {
- case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
- case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
- ctx->initiator_enc_aux =
- context_v2_alloc_cipher(ctx, "cbc(aes)",
- ctx->initiator_seal);
- if (ctx->initiator_enc_aux == NULL)
- goto out_free_acceptor_enc;
- ctx->acceptor_enc_aux =
- context_v2_alloc_cipher(ctx, "cbc(aes)",
- ctx->acceptor_seal);
- if (ctx->acceptor_enc_aux == NULL) {
- crypto_free_sync_skcipher(ctx->initiator_enc_aux);
- goto out_free_acceptor_enc;
- }
- }
-
- return 0;
+ if (krb5_derive_key(ctx, &keyin, &keyout, KG_USAGE_ACCEPTOR_SEAL,
+ KEY_USAGE_SEED_INTEGRITY, gfp_mask))
+ goto out_free;
+ ctx->acceptor_integ = gss_krb5_alloc_hash_v2(ctx, &keyout);
+ if (ctx->acceptor_integ == NULL)
+ goto out_free;
+
+ ret = 0;
+out:
+ kfree_sensitive(keyout.data);
+ return ret;
-out_free_acceptor_enc:
+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);
-out_free_initiator_enc:
+ crypto_free_sync_skcipher(ctx->initiator_enc_aux);
crypto_free_sync_skcipher(ctx->initiator_enc);
-out_err:
- return -EINVAL;
+ goto out;
}
+#endif
+
static int
gss_import_v2_context(const void *p, const void *end, struct krb5_ctx *ctx,
gfp_t gfp_mask)
@@ -500,7 +674,7 @@ gss_import_v2_context(const void *p, const void *end, struct krb5_ctx *ctx,
/* Map ENCTYPE_DES3_CBC_SHA1 to ENCTYPE_DES3_CBC_RAW */
if (ctx->enctype == ENCTYPE_DES3_CBC_SHA1)
ctx->enctype = ENCTYPE_DES3_CBC_RAW;
- ctx->gk5e = get_gss_krb5_enctype(ctx->enctype);
+ ctx->gk5e = gss_krb5_lookup_enctype(ctx->enctype);
if (ctx->gk5e == NULL) {
dprintk("gss_kerberos_mech: unsupported krb5 enctype %u\n",
ctx->enctype);
@@ -526,25 +700,15 @@ gss_import_v2_context(const void *p, const void *end, struct krb5_ctx *ctx,
}
ctx->mech_used.len = gss_kerberos_mech.gm_oid.len;
- switch (ctx->enctype) {
- case ENCTYPE_DES3_CBC_RAW:
- return context_derive_keys_des3(ctx, gfp_mask);
- case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
- case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
- return context_derive_keys_new(ctx, gfp_mask);
- default:
- return -EINVAL;
- }
+ return ctx->gk5e->import_ctx(ctx, gfp_mask);
out_err:
return PTR_ERR(p);
}
static int
-gss_import_sec_context_kerberos(const void *p, size_t len,
- struct gss_ctx *ctx_id,
- time64_t *endtime,
- gfp_t gfp_mask)
+gss_krb5_import_sec_context(const void *p, size_t len, struct gss_ctx *ctx_id,
+ time64_t *endtime, gfp_t gfp_mask)
{
const void *end = (const void *)((const char *)p + len);
struct krb5_ctx *ctx;
@@ -558,20 +722,21 @@ gss_import_sec_context_kerberos(const void *p, size_t len,
ret = gss_import_v1_context(p, end, ctx);
else
ret = gss_import_v2_context(p, end, ctx, gfp_mask);
-
- if (ret == 0) {
- ctx_id->internal_ctx_id = ctx;
- if (endtime)
- *endtime = ctx->endtime;
- } else
+ memzero_explicit(&ctx->Ksess, sizeof(ctx->Ksess));
+ if (ret) {
kfree(ctx);
+ return ret;
+ }
- dprintk("RPC: %s: returning %d\n", __func__, ret);
- return ret;
+ ctx_id->internal_ctx_id = ctx;
+ if (endtime)
+ *endtime = ctx->endtime;
+ return 0;
}
static void
-gss_delete_sec_context_kerberos(void *internal_ctx) {
+gss_krb5_delete_sec_context(void *internal_ctx)
+{
struct krb5_ctx *kctx = internal_ctx;
crypto_free_sync_skcipher(kctx->seq);
@@ -580,17 +745,105 @@ gss_delete_sec_context_kerberos(void *internal_ctx) {
crypto_free_sync_skcipher(kctx->initiator_enc);
crypto_free_sync_skcipher(kctx->acceptor_enc_aux);
crypto_free_sync_skcipher(kctx->initiator_enc_aux);
+ crypto_free_ahash(kctx->acceptor_sign);
+ crypto_free_ahash(kctx->initiator_sign);
+ crypto_free_ahash(kctx->acceptor_integ);
+ crypto_free_ahash(kctx->initiator_integ);
kfree(kctx->mech_used.data);
kfree(kctx);
}
+/**
+ * gss_krb5_get_mic - get_mic for the Kerberos GSS mechanism
+ * @gctx: GSS context
+ * @text: plaintext to checksum
+ * @token: buffer into which to write the computed checksum
+ *
+ * Return values:
+ * %GSS_S_COMPLETE - success, and @token is filled in
+ * %GSS_S_FAILURE - checksum could not be generated
+ * %GSS_S_CONTEXT_EXPIRED - Kerberos context is no longer valid
+ */
+static u32 gss_krb5_get_mic(struct gss_ctx *gctx, struct xdr_buf *text,
+ struct xdr_netobj *token)
+{
+ struct krb5_ctx *kctx = gctx->internal_ctx_id;
+
+ return kctx->gk5e->get_mic(kctx, text, token);
+}
+
+/**
+ * gss_krb5_verify_mic - verify_mic for the Kerberos GSS mechanism
+ * @gctx: GSS context
+ * @message_buffer: plaintext to check
+ * @read_token: received checksum to check
+ *
+ * Return values:
+ * %GSS_S_COMPLETE - computed and received checksums match
+ * %GSS_S_DEFECTIVE_TOKEN - received checksum is not valid
+ * %GSS_S_BAD_SIG - computed and received checksums do not match
+ * %GSS_S_FAILURE - received checksum could not be checked
+ * %GSS_S_CONTEXT_EXPIRED - Kerberos context is no longer valid
+ */
+static u32 gss_krb5_verify_mic(struct gss_ctx *gctx,
+ struct xdr_buf *message_buffer,
+ struct xdr_netobj *read_token)
+{
+ struct krb5_ctx *kctx = gctx->internal_ctx_id;
+
+ return kctx->gk5e->verify_mic(kctx, message_buffer, read_token);
+}
+
+/**
+ * gss_krb5_wrap - gss_wrap for the Kerberos GSS mechanism
+ * @gctx: initialized GSS context
+ * @offset: byte offset in @buf to start writing the cipher text
+ * @buf: OUT: send buffer
+ * @pages: plaintext to wrap
+ *
+ * Return values:
+ * %GSS_S_COMPLETE - success, @buf has been updated
+ * %GSS_S_FAILURE - @buf could not be wrapped
+ * %GSS_S_CONTEXT_EXPIRED - Kerberos context is no longer valid
+ */
+static u32 gss_krb5_wrap(struct gss_ctx *gctx, int offset,
+ struct xdr_buf *buf, struct page **pages)
+{
+ struct krb5_ctx *kctx = gctx->internal_ctx_id;
+
+ return kctx->gk5e->wrap(kctx, offset, buf, pages);
+}
+
+/**
+ * gss_krb5_unwrap - gss_unwrap for the Kerberos GSS mechanism
+ * @gctx: initialized GSS context
+ * @offset: starting byte offset into @buf
+ * @len: size of ciphertext to unwrap
+ * @buf: ciphertext to unwrap
+ *
+ * Return values:
+ * %GSS_S_COMPLETE - success, @buf has been updated
+ * %GSS_S_DEFECTIVE_TOKEN - received blob is not valid
+ * %GSS_S_BAD_SIG - computed and received checksums do not match
+ * %GSS_S_FAILURE - @buf could not be unwrapped
+ * %GSS_S_CONTEXT_EXPIRED - Kerberos context is no longer valid
+ */
+static u32 gss_krb5_unwrap(struct gss_ctx *gctx, int offset,
+ int len, struct xdr_buf *buf)
+{
+ struct krb5_ctx *kctx = gctx->internal_ctx_id;
+
+ return kctx->gk5e->unwrap(kctx, offset, len, buf,
+ &gctx->slack, &gctx->align);
+}
+
static const struct gss_api_ops gss_kerberos_ops = {
- .gss_import_sec_context = gss_import_sec_context_kerberos,
- .gss_get_mic = gss_get_mic_kerberos,
- .gss_verify_mic = gss_verify_mic_kerberos,
- .gss_wrap = gss_wrap_kerberos,
- .gss_unwrap = gss_unwrap_kerberos,
- .gss_delete_sec_context = gss_delete_sec_context_kerberos,
+ .gss_import_sec_context = gss_krb5_import_sec_context,
+ .gss_get_mic = gss_krb5_get_mic,
+ .gss_verify_mic = gss_krb5_verify_mic,
+ .gss_wrap = gss_krb5_wrap,
+ .gss_unwrap = gss_krb5_unwrap,
+ .gss_delete_sec_context = gss_krb5_delete_sec_context,
};
static struct pf_desc gss_kerberos_pfs[] = {
@@ -631,13 +884,14 @@ static struct gss_api_mech gss_kerberos_mech = {
.gm_ops = &gss_kerberos_ops,
.gm_pf_num = ARRAY_SIZE(gss_kerberos_pfs),
.gm_pfs = gss_kerberos_pfs,
- .gm_upcall_enctypes = KRB5_SUPPORTED_ENCTYPES,
+ .gm_upcall_enctypes = gss_krb5_enctype_priority_list,
};
static int __init init_kerberos_module(void)
{
int status;
+ gss_krb5_prepare_enctype_priority_list();
status = gss_mech_register(&gss_kerberos_mech);
if (status)
printk("Failed to register kerberos gss mechanism!\n");
diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c
index 33061417ec97..146aa755f07d 100644
--- a/net/sunrpc/auth_gss/gss_krb5_seal.c
+++ b/net/sunrpc/auth_gss/gss_krb5_seal.c
@@ -65,10 +65,14 @@
#include <linux/crypto.h>
#include <linux/atomic.h>
+#include "gss_krb5_internal.h"
+
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
+#if defined(CONFIG_RPCSEC_GSS_KRB5_SIMPLIFIED)
+
static void *
setup_token(struct krb5_ctx *ctx, struct xdr_netobj *token)
{
@@ -95,37 +99,9 @@ setup_token(struct krb5_ctx *ctx, struct xdr_netobj *token)
return krb5_hdr;
}
-static void *
-setup_token_v2(struct krb5_ctx *ctx, struct xdr_netobj *token)
-{
- u16 *ptr;
- void *krb5_hdr;
- u8 *p, flags = 0x00;
-
- if ((ctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0)
- flags |= 0x01;
- if (ctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY)
- flags |= 0x04;
-
- /* Per rfc 4121, sec 4.2.6.1, there is no header,
- * just start the token */
- krb5_hdr = ptr = (u16 *)token->data;
-
- *ptr++ = KG2_TOK_MIC;
- p = (u8 *)ptr;
- *p++ = flags;
- *p++ = 0xff;
- ptr = (u16 *)p;
- *ptr++ = 0xffff;
- *ptr = 0xffff;
-
- token->len = GSS_KRB5_TOK_HDR_LEN + ctx->gk5e->cksumlength;
- return krb5_hdr;
-}
-
-static u32
-gss_get_mic_v1(struct krb5_ctx *ctx, struct xdr_buf *text,
- struct xdr_netobj *token)
+u32
+gss_krb5_get_mic_v1(struct krb5_ctx *ctx, struct xdr_buf *text,
+ struct xdr_netobj *token)
{
char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
struct xdr_netobj md5cksum = {.len = sizeof(cksumdata),
@@ -162,18 +138,50 @@ gss_get_mic_v1(struct krb5_ctx *ctx, struct xdr_buf *text,
return (ctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
}
-static u32
-gss_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text,
- struct xdr_netobj *token)
+#endif
+
+static void *
+setup_token_v2(struct krb5_ctx *ctx, struct xdr_netobj *token)
+{
+ u16 *ptr;
+ void *krb5_hdr;
+ u8 *p, flags = 0x00;
+
+ if ((ctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0)
+ flags |= 0x01;
+ if (ctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY)
+ flags |= 0x04;
+
+ /* Per rfc 4121, sec 4.2.6.1, there is no header,
+ * just start the token.
+ */
+ krb5_hdr = (u16 *)token->data;
+ ptr = krb5_hdr;
+
+ *ptr++ = KG2_TOK_MIC;
+ p = (u8 *)ptr;
+ *p++ = flags;
+ *p++ = 0xff;
+ ptr = (u16 *)p;
+ *ptr++ = 0xffff;
+ *ptr = 0xffff;
+
+ token->len = GSS_KRB5_TOK_HDR_LEN + ctx->gk5e->cksumlength;
+ return krb5_hdr;
+}
+
+u32
+gss_krb5_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text,
+ struct xdr_netobj *token)
{
- char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
- struct xdr_netobj cksumobj = { .len = sizeof(cksumdata),
- .data = cksumdata};
+ struct crypto_ahash *tfm = ctx->initiate ?
+ ctx->initiator_sign : ctx->acceptor_sign;
+ struct xdr_netobj cksumobj = {
+ .len = ctx->gk5e->cksumlength,
+ };
+ __be64 seq_send_be64;
void *krb5_hdr;
time64_t now;
- u8 *cksumkey;
- unsigned int cksum_usage;
- __be64 seq_send_be64;
dprintk("RPC: %s\n", __func__);
@@ -184,39 +192,11 @@ gss_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text,
seq_send_be64 = cpu_to_be64(atomic64_fetch_inc(&ctx->seq_send64));
memcpy(krb5_hdr + 8, (char *) &seq_send_be64, 8);
- if (ctx->initiate) {
- cksumkey = ctx->initiator_sign;
- cksum_usage = KG_USAGE_INITIATOR_SIGN;
- } else {
- cksumkey = ctx->acceptor_sign;
- cksum_usage = KG_USAGE_ACCEPTOR_SIGN;
- }
-
- if (make_checksum_v2(ctx, krb5_hdr, GSS_KRB5_TOK_HDR_LEN,
- text, 0, cksumkey, cksum_usage, &cksumobj))
+ cksumobj.data = krb5_hdr + GSS_KRB5_TOK_HDR_LEN;
+ if (gss_krb5_checksum(tfm, krb5_hdr, GSS_KRB5_TOK_HDR_LEN,
+ text, 0, &cksumobj))
return GSS_S_FAILURE;
- memcpy(krb5_hdr + GSS_KRB5_TOK_HDR_LEN, cksumobj.data, cksumobj.len);
-
now = ktime_get_real_seconds();
-
return (ctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
}
-
-u32
-gss_get_mic_kerberos(struct gss_ctx *gss_ctx, struct xdr_buf *text,
- struct xdr_netobj *token)
-{
- struct krb5_ctx *ctx = gss_ctx->internal_ctx_id;
-
- switch (ctx->enctype) {
- default:
- BUG();
- case ENCTYPE_DES_CBC_RAW:
- case ENCTYPE_DES3_CBC_RAW:
- return gss_get_mic_v1(ctx, text, token);
- case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
- case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
- return gss_get_mic_v2(ctx, text, token);
- }
-}
diff --git a/net/sunrpc/auth_gss/gss_krb5_seqnum.c b/net/sunrpc/auth_gss/gss_krb5_seqnum.c
index 3200b971a814..1babc3474e10 100644
--- a/net/sunrpc/auth_gss/gss_krb5_seqnum.c
+++ b/net/sunrpc/auth_gss/gss_krb5_seqnum.c
@@ -35,6 +35,8 @@
#include <linux/types.h>
#include <linux/sunrpc/gss_krb5.h>
+#include "gss_krb5_internal.h"
+
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
diff --git a/net/sunrpc/auth_gss/gss_krb5_test.c b/net/sunrpc/auth_gss/gss_krb5_test.c
new file mode 100644
index 000000000000..c287ce15c419
--- /dev/null
+++ b/net/sunrpc/auth_gss/gss_krb5_test.c
@@ -0,0 +1,2040 @@
+// 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);
+ KUNIT_ASSERT_NOT_NULL(test, gk5e);
+
+ 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_EQ_MSG(test,
+ memcmp(param->expected_result->data,
+ derivedkey.data, derivedkey.len), 0,
+ "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_base = param->plaintext->data,
+ .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);
+ KUNIT_ASSERT_NOT_NULL(test, gk5e);
+
+ 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);
+
+ 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_EQ_MSG(test,
+ memcmp(param->expected_result->data,
+ checksum.data, checksum.len), 0,
+ "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_EQ_MSG(test,
+ memcmp(param->expected_result->data,
+ result, param->expected_result->len), 0,
+ "result mismatch");
+}
+
+/*
+ * RFC 3961 Appendix A.3. DES3 DR and DK
+ *
+ * These tests show the derived-random and derived-key values for the
+ * des3-hmac-sha1-kd encryption scheme, using the DR and DK functions
+ * defined in section 6.3.1. The input keys were randomly generated;
+ * the usage values are from this specification.
+ *
+ * This test material is copyright (C) The Internet Society (2005).
+ */
+
+DEFINE_HEX_XDR_NETOBJ(des3_dk_usage_155,
+ 0x00, 0x00, 0x00, 0x01, 0x55
+);
+
+DEFINE_HEX_XDR_NETOBJ(des3_dk_usage_1aa,
+ 0x00, 0x00, 0x00, 0x01, 0xaa
+);
+
+DEFINE_HEX_XDR_NETOBJ(des3_dk_usage_kerberos,
+ 0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73
+);
+
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test1_base_key,
+ 0xdc, 0xe0, 0x6b, 0x1f, 0x64, 0xc8, 0x57, 0xa1,
+ 0x1c, 0x3d, 0xb5, 0x7c, 0x51, 0x89, 0x9b, 0x2c,
+ 0xc1, 0x79, 0x10, 0x08, 0xce, 0x97, 0x3b, 0x92
+);
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test1_derived_key,
+ 0x92, 0x51, 0x79, 0xd0, 0x45, 0x91, 0xa7, 0x9b,
+ 0x5d, 0x31, 0x92, 0xc4, 0xa7, 0xe9, 0xc2, 0x89,
+ 0xb0, 0x49, 0xc7, 0x1f, 0x6e, 0xe6, 0x04, 0xcd
+);
+
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test2_base_key,
+ 0x5e, 0x13, 0xd3, 0x1c, 0x70, 0xef, 0x76, 0x57,
+ 0x46, 0x57, 0x85, 0x31, 0xcb, 0x51, 0xc1, 0x5b,
+ 0xf1, 0x1c, 0xa8, 0x2c, 0x97, 0xce, 0xe9, 0xf2
+);
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test2_derived_key,
+ 0x9e, 0x58, 0xe5, 0xa1, 0x46, 0xd9, 0x94, 0x2a,
+ 0x10, 0x1c, 0x46, 0x98, 0x45, 0xd6, 0x7a, 0x20,
+ 0xe3, 0xc4, 0x25, 0x9e, 0xd9, 0x13, 0xf2, 0x07
+);
+
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test3_base_key,
+ 0x98, 0xe6, 0xfd, 0x8a, 0x04, 0xa4, 0xb6, 0x85,
+ 0x9b, 0x75, 0xa1, 0x76, 0x54, 0x0b, 0x97, 0x52,
+ 0xba, 0xd3, 0xec, 0xd6, 0x10, 0xa2, 0x52, 0xbc
+);
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test3_derived_key,
+ 0x13, 0xfe, 0xf8, 0x0d, 0x76, 0x3e, 0x94, 0xec,
+ 0x6d, 0x13, 0xfd, 0x2c, 0xa1, 0xd0, 0x85, 0x07,
+ 0x02, 0x49, 0xda, 0xd3, 0x98, 0x08, 0xea, 0xbf
+);
+
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test4_base_key,
+ 0x62, 0x2a, 0xec, 0x25, 0xa2, 0xfe, 0x2c, 0xad,
+ 0x70, 0x94, 0x68, 0x0b, 0x7c, 0x64, 0x94, 0x02,
+ 0x80, 0x08, 0x4c, 0x1a, 0x7c, 0xec, 0x92, 0xb5
+);
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test4_derived_key,
+ 0xf8, 0xdf, 0xbf, 0x04, 0xb0, 0x97, 0xe6, 0xd9,
+ 0xdc, 0x07, 0x02, 0x68, 0x6b, 0xcb, 0x34, 0x89,
+ 0xd9, 0x1f, 0xd9, 0xa4, 0x51, 0x6b, 0x70, 0x3e
+);
+
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test5_base_key,
+ 0xd3, 0xf8, 0x29, 0x8c, 0xcb, 0x16, 0x64, 0x38,
+ 0xdc, 0xb9, 0xb9, 0x3e, 0xe5, 0xa7, 0x62, 0x92,
+ 0x86, 0xa4, 0x91, 0xf8, 0x38, 0xf8, 0x02, 0xfb
+);
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test5_derived_key,
+ 0x23, 0x70, 0xda, 0x57, 0x5d, 0x2a, 0x3d, 0xa8,
+ 0x64, 0xce, 0xbf, 0xdc, 0x52, 0x04, 0xd5, 0x6d,
+ 0xf7, 0x79, 0xa7, 0xdf, 0x43, 0xd9, 0xda, 0x43
+);
+
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test6_base_key,
+ 0xc1, 0x08, 0x16, 0x49, 0xad, 0xa7, 0x43, 0x62,
+ 0xe6, 0xa1, 0x45, 0x9d, 0x01, 0xdf, 0xd3, 0x0d,
+ 0x67, 0xc2, 0x23, 0x4c, 0x94, 0x07, 0x04, 0xda
+);
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test6_derived_key,
+ 0x34, 0x80, 0x57, 0xec, 0x98, 0xfd, 0xc4, 0x80,
+ 0x16, 0x16, 0x1c, 0x2a, 0x4c, 0x7a, 0x94, 0x3e,
+ 0x92, 0xae, 0x49, 0x2c, 0x98, 0x91, 0x75, 0xf7
+);
+
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test7_base_key,
+ 0x5d, 0x15, 0x4a, 0xf2, 0x38, 0xf4, 0x67, 0x13,
+ 0x15, 0x57, 0x19, 0xd5, 0x5e, 0x2f, 0x1f, 0x79,
+ 0x0d, 0xd6, 0x61, 0xf2, 0x79, 0xa7, 0x91, 0x7c
+);
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test7_derived_key,
+ 0xa8, 0x80, 0x8a, 0xc2, 0x67, 0xda, 0xda, 0x3d,
+ 0xcb, 0xe9, 0xa7, 0xc8, 0x46, 0x26, 0xfb, 0xc7,
+ 0x61, 0xc2, 0x94, 0xb0, 0x13, 0x15, 0xe5, 0xc1
+);
+
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test8_base_key,
+ 0x79, 0x85, 0x62, 0xe0, 0x49, 0x85, 0x2f, 0x57,
+ 0xdc, 0x8c, 0x34, 0x3b, 0xa1, 0x7f, 0x2c, 0xa1,
+ 0xd9, 0x73, 0x94, 0xef, 0xc8, 0xad, 0xc4, 0x43
+);
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test8_derived_key,
+ 0xc8, 0x13, 0xf8, 0x8a, 0x3b, 0xe3, 0xb3, 0x34,
+ 0xf7, 0x54, 0x25, 0xce, 0x91, 0x75, 0xfb, 0xe3,
+ 0xc8, 0x49, 0x3b, 0x89, 0xc8, 0x70, 0x3b, 0x49
+);
+
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test9_base_key,
+ 0x26, 0xdc, 0xe3, 0x34, 0xb5, 0x45, 0x29, 0x2f,
+ 0x2f, 0xea, 0xb9, 0xa8, 0x70, 0x1a, 0x89, 0xa4,
+ 0xb9, 0x9e, 0xb9, 0x94, 0x2c, 0xec, 0xd0, 0x16
+);
+DEFINE_HEX_XDR_NETOBJ(des3_dk_test9_derived_key,
+ 0xf4, 0x8f, 0xfd, 0x6e, 0x83, 0xf8, 0x3e, 0x73,
+ 0x54, 0xe6, 0x94, 0xfd, 0x25, 0x2c, 0xf8, 0x3b,
+ 0xfe, 0x58, 0xf7, 0xd5, 0xba, 0x37, 0xec, 0x5d
+);
+
+static const struct gss_krb5_test_param rfc3961_kdf_test_params[] = {
+ {
+ .desc = "des3-hmac-sha1 key derivation case 1",
+ .enctype = ENCTYPE_DES3_CBC_RAW,
+ .base_key = &des3_dk_test1_base_key,
+ .usage = &des3_dk_usage_155,
+ .expected_result = &des3_dk_test1_derived_key,
+ },
+ {
+ .desc = "des3-hmac-sha1 key derivation case 2",
+ .enctype = ENCTYPE_DES3_CBC_RAW,
+ .base_key = &des3_dk_test2_base_key,
+ .usage = &des3_dk_usage_1aa,
+ .expected_result = &des3_dk_test2_derived_key,
+ },
+ {
+ .desc = "des3-hmac-sha1 key derivation case 3",
+ .enctype = ENCTYPE_DES3_CBC_RAW,
+ .base_key = &des3_dk_test3_base_key,
+ .usage = &des3_dk_usage_155,
+ .expected_result = &des3_dk_test3_derived_key,
+ },
+ {
+ .desc = "des3-hmac-sha1 key derivation case 4",
+ .enctype = ENCTYPE_DES3_CBC_RAW,
+ .base_key = &des3_dk_test4_base_key,
+ .usage = &des3_dk_usage_1aa,
+ .expected_result = &des3_dk_test4_derived_key,
+ },
+ {
+ .desc = "des3-hmac-sha1 key derivation case 5",
+ .enctype = ENCTYPE_DES3_CBC_RAW,
+ .base_key = &des3_dk_test5_base_key,
+ .usage = &des3_dk_usage_kerberos,
+ .expected_result = &des3_dk_test5_derived_key,
+ },
+ {
+ .desc = "des3-hmac-sha1 key derivation case 6",
+ .enctype = ENCTYPE_DES3_CBC_RAW,
+ .base_key = &des3_dk_test6_base_key,
+ .usage = &des3_dk_usage_155,
+ .expected_result = &des3_dk_test6_derived_key,
+ },
+ {
+ .desc = "des3-hmac-sha1 key derivation case 7",
+ .enctype = ENCTYPE_DES3_CBC_RAW,
+ .base_key = &des3_dk_test7_base_key,
+ .usage = &des3_dk_usage_1aa,
+ .expected_result = &des3_dk_test7_derived_key,
+ },
+ {
+ .desc = "des3-hmac-sha1 key derivation case 8",
+ .enctype = ENCTYPE_DES3_CBC_RAW,
+ .base_key = &des3_dk_test8_base_key,
+ .usage = &des3_dk_usage_155,
+ .expected_result = &des3_dk_test8_derived_key,
+ },
+ {
+ .desc = "des3-hmac-sha1 key derivation case 9",
+ .enctype = ENCTYPE_DES3_CBC_RAW,
+ .base_key = &des3_dk_test9_base_key,
+ .usage = &des3_dk_usage_1aa,
+ .expected_result = &des3_dk_test9_derived_key,
+ },
+};
+
+/* Creates the function rfc3961_kdf_gen_params */
+KUNIT_ARRAY_PARAM(rfc3961_kdf, rfc3961_kdf_test_params, gss_krb5_get_desc);
+
+static struct kunit_case rfc3961_test_cases[] = {
+ {
+ .name = "RFC 3961 n-fold",
+ .run_case = rfc3961_nfold_case,
+ .generate_params = rfc3961_nfold_gen_params,
+ },
+ {
+ .name = "RFC 3961 key derivation",
+ .run_case = kdf_case,
+ .generate_params = rfc3961_kdf_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);
+ KUNIT_ASSERT_NOT_NULL(test, gk5e);
+
+ 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_EQ_MSG(test,
+ memcmp(param->expected_result->data,
+ text, param->expected_result->len), 0,
+ "ciphertext mismatch");
+ KUNIT_EXPECT_EQ_MSG(test,
+ memcmp(param->next_iv->data, iv,
+ param->next_iv->len), 0,
+ "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);
+ KUNIT_ASSERT_NOT_NULL(test, gk5e);
+
+ 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_EQ_MSG(test,
+ memcmp(param->expected_result->data,
+ buf.head[0].iov_base, buf.len), 0,
+ "encrypted result mismatch");
+ KUNIT_EXPECT_EQ_MSG(test,
+ memcmp(param->expected_result->data +
+ (param->expected_result->len - checksum.len),
+ checksum.data, checksum.len), 0,
+ "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);
+ KUNIT_ASSERT_NOT_NULL(test, gk5e);
+
+ *(__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_EQ_MSG(test,
+ memcmp(param->expected_result->data,
+ buf.head[0].iov_base,
+ param->expected_result->len), 0,
+ "ciphertext mismatch");
+ KUNIT_EXPECT_EQ_MSG(test, memcmp(param->expected_hmac->data,
+ checksum.data,
+ checksum.len), 0,
+ "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);
+ KUNIT_ASSERT_NOT_NULL(test, gk5e);
+
+ 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_EQ_MSG(test,
+ memcmp(param->plaintext->data,
+ buf.head[0].iov_base, buf.len), 0,
+ "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 ba04e3ec970a..7d6d4ae4a3c9 100644
--- a/net/sunrpc/auth_gss/gss_krb5_unseal.c
+++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c
@@ -57,22 +57,25 @@
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
+#include <crypto/algapi.h>
#include <linux/types.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_krb5.h>
#include <linux/crypto.h>
+#include "gss_krb5_internal.h"
+
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
+#if defined(CONFIG_RPCSEC_GSS_KRB5_SIMPLIFIED)
/* read_token is a mic token, and message_buffer is the data that the mic was
* supposedly taken over. */
-
-static u32
-gss_verify_mic_v1(struct krb5_ctx *ctx,
- struct xdr_buf *message_buffer, struct xdr_netobj *read_token)
+u32
+gss_krb5_verify_mic_v1(struct krb5_ctx *ctx, struct xdr_buf *message_buffer,
+ struct xdr_netobj *read_token)
{
int signalg;
int sealalg;
@@ -141,21 +144,24 @@ gss_verify_mic_v1(struct krb5_ctx *ctx,
return GSS_S_COMPLETE;
}
+#endif
-static u32
-gss_verify_mic_v2(struct krb5_ctx *ctx,
- struct xdr_buf *message_buffer, struct xdr_netobj *read_token)
+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 = sizeof(cksumdata),
- .data = cksumdata};
- time64_t now;
+ struct xdr_netobj cksumobj = {
+ .len = ctx->gk5e->cksumlength,
+ .data = cksumdata,
+ };
u8 *ptr = read_token->data;
- u8 *cksumkey;
+ __be16 be16_ptr;
+ time64_t now;
u8 flags;
int i;
- unsigned int cksum_usage;
- __be16 be16_ptr;
dprintk("RPC: %s\n", __func__);
@@ -177,16 +183,8 @@ gss_verify_mic_v2(struct krb5_ctx *ctx,
if (ptr[i] != 0xff)
return GSS_S_DEFECTIVE_TOKEN;
- if (ctx->initiate) {
- cksumkey = ctx->acceptor_sign;
- cksum_usage = KG_USAGE_ACCEPTOR_SIGN;
- } else {
- cksumkey = ctx->initiator_sign;
- cksum_usage = KG_USAGE_INITIATOR_SIGN;
- }
-
- if (make_checksum_v2(ctx, ptr, GSS_KRB5_TOK_HDR_LEN, message_buffer, 0,
- cksumkey, cksum_usage, &cksumobj))
+ if (gss_krb5_checksum(tfm, ptr, GSS_KRB5_TOK_HDR_LEN,
+ message_buffer, 0, &cksumobj))
return GSS_S_FAILURE;
if (memcmp(cksumobj.data, ptr + GSS_KRB5_TOK_HDR_LEN,
@@ -205,22 +203,3 @@ gss_verify_mic_v2(struct krb5_ctx *ctx,
return GSS_S_COMPLETE;
}
-
-u32
-gss_verify_mic_kerberos(struct gss_ctx *gss_ctx,
- struct xdr_buf *message_buffer,
- struct xdr_netobj *read_token)
-{
- struct krb5_ctx *ctx = gss_ctx->internal_ctx_id;
-
- switch (ctx->enctype) {
- default:
- BUG();
- case ENCTYPE_DES_CBC_RAW:
- case ENCTYPE_DES3_CBC_RAW:
- return gss_verify_mic_v1(ctx, message_buffer, read_token);
- case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
- case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
- return gss_verify_mic_v2(ctx, message_buffer, read_token);
- }
-}
diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c
index 48337687848c..6d6b082380b2 100644
--- a/net/sunrpc/auth_gss/gss_krb5_wrap.c
+++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c
@@ -32,13 +32,16 @@
#include <linux/types.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_krb5.h>
-#include <linux/random.h>
#include <linux/pagemap.h>
+#include "gss_krb5_internal.h"
+
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
+#if defined(CONFIG_RPCSEC_GSS_KRB5_SIMPLIFIED)
+
static inline int
gss_krb5_padding(int blocksize, int length)
{
@@ -113,39 +116,6 @@ out:
return 0;
}
-void
-gss_krb5_make_confounder(char *p, u32 conflen)
-{
- static u64 i = 0;
- u64 *q = (u64 *)p;
-
- /* rfc1964 claims this should be "random". But all that's really
- * necessary is that it be unique. And not even that is necessary in
- * our case since our "gssapi" implementation exists only to support
- * rpcsec_gss, so we know that the only buffers we will ever encrypt
- * already begin with a unique sequence number. Just to hedge my bets
- * I'll make a half-hearted attempt at something unique, but ensuring
- * uniqueness would mean worrying about atomicity and rollover, and I
- * don't care enough. */
-
- /* initialize to random value */
- if (i == 0) {
- i = get_random_u32();
- i = (i << 32) | get_random_u32();
- }
-
- switch (conflen) {
- case 16:
- *q++ = i++;
- fallthrough;
- case 8:
- *q++ = i++;
- break;
- default:
- BUG();
- }
-}
-
/* Assumptions: the head and tail of inbuf are ours to play with.
* The pages, however, may be real pages in the page cache and we replace
* them with scratch pages from **pages before writing to them. */
@@ -154,9 +124,9 @@ gss_krb5_make_confounder(char *p, u32 conflen)
/* XXX factor out common code with seal/unseal. */
-static u32
-gss_wrap_kerberos_v1(struct krb5_ctx *kctx, int offset,
- struct xdr_buf *buf, struct page **pages)
+u32
+gss_krb5_wrap_v1(struct krb5_ctx *kctx, int offset,
+ struct xdr_buf *buf, struct page **pages)
{
char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
struct xdr_netobj md5cksum = {.len = sizeof(cksumdata),
@@ -168,7 +138,7 @@ gss_wrap_kerberos_v1(struct krb5_ctx *kctx, int offset,
struct page **tmp_pages;
u32 seq_send;
u8 *cksumkey;
- u32 conflen = kctx->gk5e->conflen;
+ u32 conflen = crypto_sync_skcipher_blocksize(kctx->enc);
dprintk("RPC: %s\n", __func__);
@@ -211,7 +181,7 @@ gss_wrap_kerberos_v1(struct krb5_ctx *kctx, int offset,
ptr[6] = 0xff;
ptr[7] = 0xff;
- gss_krb5_make_confounder(msg_start, conflen);
+ krb5_make_confounder(msg_start, conflen);
if (kctx->gk5e->keyed_cksum)
cksumkey = kctx->cksum;
@@ -243,10 +213,10 @@ gss_wrap_kerberos_v1(struct krb5_ctx *kctx, int offset,
return (kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
}
-static u32
-gss_unwrap_kerberos_v1(struct krb5_ctx *kctx, int offset, int len,
- struct xdr_buf *buf, unsigned int *slack,
- unsigned int *align)
+u32
+gss_krb5_unwrap_v1(struct krb5_ctx *kctx, int offset, int len,
+ struct xdr_buf *buf, unsigned int *slack,
+ unsigned int *align)
{
int signalg;
int sealalg;
@@ -261,7 +231,7 @@ gss_unwrap_kerberos_v1(struct krb5_ctx *kctx, int offset, int len,
void *data_start, *orig_start;
int data_len;
int blocksize;
- u32 conflen = kctx->gk5e->conflen;
+ u32 conflen = crypto_sync_skcipher_blocksize(kctx->enc);
int crypt_offset;
u8 *cksumkey;
unsigned int saved_len = buf->len;
@@ -355,6 +325,8 @@ gss_unwrap_kerberos_v1(struct krb5_ctx *kctx, int offset, int len,
return GSS_S_COMPLETE;
}
+#endif
+
/*
* We can shift data by up to LOCAL_BUF_LEN bytes in a pass. If we need
* to do more than that, we shift repeatedly. Kevin Coffman reports
@@ -405,9 +377,9 @@ static void rotate_left(u32 base, struct xdr_buf *buf, unsigned int shift)
_rotate_left(&subbuf, shift);
}
-static u32
-gss_wrap_kerberos_v2(struct krb5_ctx *kctx, u32 offset,
- struct xdr_buf *buf, struct page **pages)
+u32
+gss_krb5_wrap_v2(struct krb5_ctx *kctx, int offset,
+ struct xdr_buf *buf, struct page **pages)
{
u8 *ptr;
time64_t now;
@@ -418,9 +390,6 @@ gss_wrap_kerberos_v2(struct krb5_ctx *kctx, u32 offset,
dprintk("RPC: %s\n", __func__);
- if (kctx->gk5e->encrypt_v2 == NULL)
- return GSS_S_FAILURE;
-
/* make room for gss token header */
if (xdr_extend_head(buf, offset, GSS_KRB5_TOK_HDR_LEN))
return GSS_S_FAILURE;
@@ -448,7 +417,7 @@ gss_wrap_kerberos_v2(struct krb5_ctx *kctx, u32 offset,
be64ptr = (__be64 *)be16ptr;
*be64ptr = cpu_to_be64(atomic64_fetch_inc(&kctx->seq_send64));
- err = (*kctx->gk5e->encrypt_v2)(kctx, offset, buf, pages);
+ err = (*kctx->gk5e->encrypt)(kctx, offset, buf, pages);
if (err)
return err;
@@ -456,10 +425,10 @@ gss_wrap_kerberos_v2(struct krb5_ctx *kctx, u32 offset,
return (kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
}
-static u32
-gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, int len,
- struct xdr_buf *buf, unsigned int *slack,
- unsigned int *align)
+u32
+gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
+ struct xdr_buf *buf, unsigned int *slack,
+ unsigned int *align)
{
time64_t now;
u8 *ptr;
@@ -473,9 +442,6 @@ gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, int len,
dprintk("RPC: %s\n", __func__);
- if (kctx->gk5e->decrypt_v2 == NULL)
- return GSS_S_FAILURE;
-
ptr = buf->head[0].iov_base + offset;
if (be16_to_cpu(*((__be16 *)ptr)) != KG2_TOK_WRAP)
@@ -505,8 +471,8 @@ gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, int len,
if (rrc != 0)
rotate_left(offset + 16, buf, rrc);
- err = (*kctx->gk5e->decrypt_v2)(kctx, offset, len, buf,
- &headskip, &tailskip);
+ err = (*kctx->gk5e->decrypt)(kctx, offset, len, buf,
+ &headskip, &tailskip);
if (err)
return GSS_S_FAILURE;
@@ -556,41 +522,3 @@ gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, int len,
*slack = *align + XDR_QUADLEN(ec + GSS_KRB5_TOK_HDR_LEN + tailskip);
return GSS_S_COMPLETE;
}
-
-u32
-gss_wrap_kerberos(struct gss_ctx *gctx, int offset,
- struct xdr_buf *buf, struct page **pages)
-{
- struct krb5_ctx *kctx = gctx->internal_ctx_id;
-
- switch (kctx->enctype) {
- default:
- BUG();
- case ENCTYPE_DES_CBC_RAW:
- case ENCTYPE_DES3_CBC_RAW:
- return gss_wrap_kerberos_v1(kctx, offset, buf, pages);
- case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
- case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
- return gss_wrap_kerberos_v2(kctx, offset, buf, pages);
- }
-}
-
-u32
-gss_unwrap_kerberos(struct gss_ctx *gctx, int offset,
- int len, struct xdr_buf *buf)
-{
- struct krb5_ctx *kctx = gctx->internal_ctx_id;
-
- switch (kctx->enctype) {
- default:
- BUG();
- case ENCTYPE_DES_CBC_RAW:
- case ENCTYPE_DES3_CBC_RAW:
- return gss_unwrap_kerberos_v1(kctx, offset, len, buf,
- &gctx->slack, &gctx->align);
- case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
- case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
- return gss_unwrap_kerberos_v2(kctx, offset, len, buf,
- &gctx->slack, &gctx->align);
- }
-}
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index acb822b23af1..9c843974bb48 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -71,12 +71,11 @@
struct gss_svc_data {
/* decoded gss client cred: */
struct rpc_gss_wire_cred clcred;
- /* save a pointer to the beginning of the encoded verifier,
- * for use in encryption/checksumming in svcauth_gss_release: */
- __be32 *verf_start;
+ u32 gsd_databody_offset;
struct rsc *rsci;
/* for temporary results */
+ __be32 gsd_seq_num;
u8 gsd_scratch[GSS_SCRATCH_SIZE];
};
@@ -692,78 +691,49 @@ alreadyseen:
goto out;
}
-static inline u32 round_up_to_quad(u32 i)
-{
- return (i + 3 ) & ~3;
-}
-
-static inline int
-svc_safe_getnetobj(struct kvec *argv, struct xdr_netobj *o)
-{
- int l;
-
- if (argv->iov_len < 4)
- return -1;
- o->len = svc_getnl(argv);
- l = round_up_to_quad(o->len);
- if (argv->iov_len < l)
- return -1;
- o->data = argv->iov_base;
- argv->iov_base += l;
- argv->iov_len -= l;
- return 0;
-}
-
-static inline int
-svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o)
-{
- u8 *p;
-
- if (resv->iov_len + 4 > PAGE_SIZE)
- return -1;
- svc_putnl(resv, o->len);
- p = resv->iov_base + resv->iov_len;
- resv->iov_len += round_up_to_quad(o->len);
- if (resv->iov_len > PAGE_SIZE)
- return -1;
- memcpy(p, o->data, o->len);
- memset(p + o->len, 0, round_up_to_quad(o->len) - o->len);
- return 0;
-}
-
/*
- * Verify the checksum on the header and return SVC_OK on success.
- * Otherwise, return SVC_DROP (in the case of a bad sequence number)
- * or return SVC_DENIED and indicate error in rqstp->rq_auth_stat.
+ * Decode and verify a Call's verifier field. For RPC_AUTH_GSS Calls,
+ * the body of this field contains a variable length checksum.
+ *
+ * GSS-specific auth_stat values are mandated by RFC 2203 Section
+ * 5.3.3.3.
*/
static int
-gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,
- __be32 *rpcstart, struct rpc_gss_wire_cred *gc)
+svcauth_gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,
+ __be32 *rpcstart, struct rpc_gss_wire_cred *gc)
{
+ struct xdr_stream *xdr = &rqstp->rq_arg_stream;
struct gss_ctx *ctx_id = rsci->mechctx;
+ u32 flavor, maj_stat;
struct xdr_buf rpchdr;
struct xdr_netobj checksum;
- u32 flavor = 0;
- struct kvec *argv = &rqstp->rq_arg.head[0];
struct kvec iov;
- /* data to compute the checksum over: */
+ /*
+ * Compute the checksum of the incoming Call from the
+ * XID field to credential field:
+ */
iov.iov_base = rpcstart;
- iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart;
+ iov.iov_len = (u8 *)xdr->p - (u8 *)rpcstart;
xdr_buf_from_iov(&iov, &rpchdr);
- rqstp->rq_auth_stat = rpc_autherr_badverf;
- if (argv->iov_len < 4)
+ /* Call's verf field: */
+ if (xdr_stream_decode_opaque_auth(xdr, &flavor,
+ (void **)&checksum.data,
+ &checksum.len) < 0) {
+ rqstp->rq_auth_stat = rpc_autherr_badverf;
return SVC_DENIED;
- flavor = svc_getnl(argv);
- if (flavor != RPC_AUTH_GSS)
- return SVC_DENIED;
- if (svc_safe_getnetobj(argv, &checksum))
+ }
+ if (flavor != RPC_AUTH_GSS) {
+ rqstp->rq_auth_stat = rpc_autherr_badverf;
return SVC_DENIED;
+ }
- if (rqstp->rq_deferred) /* skip verification of revisited request */
+ if (rqstp->rq_deferred)
return SVC_OK;
- if (gss_verify_mic(ctx_id, &rpchdr, &checksum) != GSS_S_COMPLETE) {
+ maj_stat = gss_verify_mic(ctx_id, &rpchdr, &checksum);
+ if (maj_stat != GSS_S_COMPLETE) {
+ trace_rpcgss_svc_mic(rqstp, maj_stat);
rqstp->rq_auth_stat = rpcsec_gsserr_credproblem;
return SVC_DENIED;
}
@@ -778,54 +748,36 @@ gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,
return SVC_OK;
}
-static int
-gss_write_null_verf(struct svc_rqst *rqstp)
-{
- __be32 *p;
-
- svc_putnl(rqstp->rq_res.head, RPC_AUTH_NULL);
- p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len;
- /* don't really need to check if head->iov_len > PAGE_SIZE ... */
- *p++ = 0;
- if (!xdr_ressize_check(rqstp, p))
- return -1;
- return 0;
-}
-
-static int
-gss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq)
+/*
+ * Construct and encode a Reply's verifier field. The verifier's body
+ * field contains a variable-length checksum of the GSS sequence
+ * number.
+ */
+static bool
+svcauth_gss_encode_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq)
{
- __be32 *xdr_seq;
+ struct gss_svc_data *gsd = rqstp->rq_auth_data;
u32 maj_stat;
struct xdr_buf verf_data;
- struct xdr_netobj mic;
- __be32 *p;
+ struct xdr_netobj checksum;
struct kvec iov;
- int err = -1;
-
- svc_putnl(rqstp->rq_res.head, RPC_AUTH_GSS);
- xdr_seq = kmalloc(4, GFP_KERNEL);
- if (!xdr_seq)
- return -ENOMEM;
- *xdr_seq = htonl(seq);
- iov.iov_base = xdr_seq;
- iov.iov_len = 4;
+ gsd->gsd_seq_num = cpu_to_be32(seq);
+ iov.iov_base = &gsd->gsd_seq_num;
+ iov.iov_len = XDR_UNIT;
xdr_buf_from_iov(&iov, &verf_data);
- p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len;
- mic.data = (u8 *)(p + 1);
- maj_stat = gss_get_mic(ctx_id, &verf_data, &mic);
+
+ checksum.data = gsd->gsd_scratch;
+ maj_stat = gss_get_mic(ctx_id, &verf_data, &checksum);
if (maj_stat != GSS_S_COMPLETE)
- goto out;
- *p++ = htonl(mic.len);
- memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len);
- p += XDR_QUADLEN(mic.len);
- if (!xdr_ressize_check(rqstp, p))
- goto out;
- err = 0;
-out:
- kfree(xdr_seq);
- return err;
+ goto bad_mic;
+
+ return xdr_stream_encode_opaque_auth(&rqstp->rq_res_stream, RPC_AUTH_GSS,
+ checksum.data, checksum.len) > 0;
+
+bad_mic:
+ trace_rpcgss_svc_get_mic(rqstp, maj_stat);
+ return false;
}
struct gss_domain {
@@ -891,31 +843,28 @@ out:
}
EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor);
-static inline int
-read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
-{
- __be32 raw;
- int status;
-
- status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj));
- if (status)
- return status;
- *obj = ntohl(raw);
- return 0;
-}
-
-/* It would be nice if this bit of code could be shared with the client.
- * Obstacles:
- * The client shouldn't malloc(), would have to pass in own memory.
- * The server uses base of head iovec as read pointer, while the
- * client uses separate pointer. */
-static int
-unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
+/*
+ * RFC 2203, Section 5.3.2.2
+ *
+ * struct rpc_gss_integ_data {
+ * opaque databody_integ<>;
+ * opaque checksum<>;
+ * };
+ *
+ * struct rpc_gss_data_t {
+ * unsigned int seq_num;
+ * proc_req_arg_t arg;
+ * };
+ */
+static noinline_for_stack int
+svcauth_gss_unwrap_integ(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx)
{
struct gss_svc_data *gsd = rqstp->rq_auth_data;
- u32 integ_len, rseqno, maj_stat;
- struct xdr_netobj mic;
- struct xdr_buf integ_buf;
+ struct xdr_stream *xdr = &rqstp->rq_arg_stream;
+ u32 len, offset, seq_num, maj_stat;
+ struct xdr_buf *buf = xdr->buf;
+ struct xdr_buf databody_integ;
+ struct xdr_netobj checksum;
/* NFS READ normally uses splice to send data in-place. However
* the data in cache can change after the reply's MIC is computed
@@ -929,104 +878,100 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g
if (rqstp->rq_deferred)
return 0;
- integ_len = svc_getnl(&buf->head[0]);
- if (integ_len & 3)
+ if (xdr_stream_decode_u32(xdr, &len) < 0)
goto unwrap_failed;
- if (integ_len > buf->len)
+ if (len & 3)
goto unwrap_failed;
- if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len))
+ offset = xdr_stream_pos(xdr);
+ if (xdr_buf_subsegment(buf, &databody_integ, offset, len))
goto unwrap_failed;
- /* copy out mic... */
- if (read_u32_from_xdr_buf(buf, integ_len, &mic.len))
+ /*
+ * The xdr_stream now points to the @seq_num field. The next
+ * XDR data item is the @arg field, which contains the clear
+ * text RPC program payload. The checksum, which follows the
+ * @arg field, is located and decoded without updating the
+ * xdr_stream.
+ */
+
+ offset += len;
+ if (xdr_decode_word(buf, offset, &checksum.len))
goto unwrap_failed;
- if (mic.len > sizeof(gsd->gsd_scratch))
+ if (checksum.len > sizeof(gsd->gsd_scratch))
goto unwrap_failed;
- mic.data = gsd->gsd_scratch;
- if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len))
+ checksum.data = gsd->gsd_scratch;
+ if (read_bytes_from_xdr_buf(buf, offset + XDR_UNIT, checksum.data,
+ checksum.len))
goto unwrap_failed;
- maj_stat = gss_verify_mic(ctx, &integ_buf, &mic);
+
+ maj_stat = gss_verify_mic(ctx, &databody_integ, &checksum);
if (maj_stat != GSS_S_COMPLETE)
goto bad_mic;
- rseqno = svc_getnl(&buf->head[0]);
- if (rseqno != seq)
+
+ /* The received seqno is protected by the checksum. */
+ if (xdr_stream_decode_u32(xdr, &seq_num) < 0)
+ goto unwrap_failed;
+ if (seq_num != seq)
goto bad_seqno;
- /* trim off the mic and padding at the end before returning */
- xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4);
+
+ xdr_truncate_decode(xdr, XDR_UNIT + checksum.len);
return 0;
unwrap_failed:
trace_rpcgss_svc_unwrap_failed(rqstp);
return -EINVAL;
bad_seqno:
- trace_rpcgss_svc_seqno_bad(rqstp, seq, rseqno);
+ trace_rpcgss_svc_seqno_bad(rqstp, seq, seq_num);
return -EINVAL;
bad_mic:
trace_rpcgss_svc_mic(rqstp, maj_stat);
return -EINVAL;
}
-static inline int
-total_buf_len(struct xdr_buf *buf)
-{
- return buf->head[0].iov_len + buf->page_len + buf->tail[0].iov_len;
-}
-
-static void
-fix_priv_head(struct xdr_buf *buf, int pad)
-{
- if (buf->page_len == 0) {
- /* We need to adjust head and buf->len in tandem in this
- * case to make svc_defer() work--it finds the original
- * buffer start using buf->len - buf->head[0].iov_len. */
- buf->head[0].iov_len -= pad;
- }
-}
-
-static int
-unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
+/*
+ * RFC 2203, Section 5.3.2.3
+ *
+ * struct rpc_gss_priv_data {
+ * opaque databody_priv<>
+ * };
+ *
+ * struct rpc_gss_data_t {
+ * unsigned int seq_num;
+ * proc_req_arg_t arg;
+ * };
+ */
+static noinline_for_stack int
+svcauth_gss_unwrap_priv(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx)
{
- u32 priv_len, maj_stat;
- int pad, remaining_len, offset;
- u32 rseqno;
+ struct xdr_stream *xdr = &rqstp->rq_arg_stream;
+ u32 len, maj_stat, seq_num, offset;
+ struct xdr_buf *buf = xdr->buf;
+ unsigned int saved_len;
clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
- priv_len = svc_getnl(&buf->head[0]);
+ if (xdr_stream_decode_u32(xdr, &len) < 0)
+ goto unwrap_failed;
if (rqstp->rq_deferred) {
/* Already decrypted last time through! The sequence number
* check at out_seq is unnecessary but harmless: */
goto out_seq;
}
- /* buf->len is the number of bytes from the original start of the
- * request to the end, where head[0].iov_len is just the bytes
- * not yet read from the head, so these two values are different: */
- remaining_len = total_buf_len(buf);
- if (priv_len > remaining_len)
+ if (len > xdr_stream_remaining(xdr))
goto unwrap_failed;
- pad = remaining_len - priv_len;
- buf->len -= pad;
- fix_priv_head(buf, pad);
-
- maj_stat = gss_unwrap(ctx, 0, priv_len, buf);
- pad = priv_len - buf->len;
- /* The upper layers assume the buffer is aligned on 4-byte boundaries.
- * In the krb5p case, at least, the data ends up offset, so we need to
- * move it around. */
- /* XXX: This is very inefficient. It would be better to either do
- * this while we encrypt, or maybe in the receive code, if we can peak
- * ahead and work out the service and mechanism there. */
- offset = xdr_pad_size(buf->head[0].iov_len);
- if (offset) {
- buf->buflen = RPCSVC_MAXPAYLOAD;
- xdr_shift_buf(buf, offset);
- fix_priv_head(buf, pad);
- }
+ offset = xdr_stream_pos(xdr);
+
+ saved_len = buf->len;
+ maj_stat = gss_unwrap(ctx, offset, offset + len, buf);
if (maj_stat != GSS_S_COMPLETE)
goto bad_unwrap;
+ xdr->nwords -= XDR_QUADLEN(saved_len - buf->len);
+
out_seq:
- rseqno = svc_getnl(&buf->head[0]);
- if (rseqno != seq)
+ /* gss_unwrap() decrypted the sequence number. */
+ if (xdr_stream_decode_u32(xdr, &seq_num) < 0)
+ goto unwrap_failed;
+ if (seq_num != seq)
goto bad_seqno;
return 0;
@@ -1034,7 +979,7 @@ unwrap_failed:
trace_rpcgss_svc_unwrap_failed(rqstp);
return -EINVAL;
bad_seqno:
- trace_rpcgss_svc_seqno_bad(rqstp, seq, rseqno);
+ trace_rpcgss_svc_seqno_bad(rqstp, seq, seq_num);
return -EINVAL;
bad_unwrap:
trace_rpcgss_svc_unwrap(rqstp, maj_stat);
@@ -1071,72 +1016,29 @@ svcauth_gss_set_client(struct svc_rqst *rqstp)
return SVC_OK;
}
-static inline int
-gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp,
- struct xdr_netobj *out_handle, int *major_status)
+static bool
+svcauth_gss_proc_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp,
+ struct xdr_netobj *out_handle, int *major_status,
+ u32 seq_num)
{
+ struct xdr_stream *xdr = &rqstp->rq_res_stream;
struct rsc *rsci;
- int rc;
+ bool rc;
if (*major_status != GSS_S_COMPLETE)
- return gss_write_null_verf(rqstp);
+ goto null_verifier;
rsci = gss_svc_searchbyctx(cd, out_handle);
if (rsci == NULL) {
*major_status = GSS_S_NO_CONTEXT;
- return gss_write_null_verf(rqstp);
+ goto null_verifier;
}
- rc = gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN);
+
+ rc = svcauth_gss_encode_verf(rqstp, rsci->mechctx, seq_num);
cache_put(&rsci->h, cd);
return rc;
-}
-
-static inline int
-gss_read_common_verf(struct rpc_gss_wire_cred *gc,
- struct kvec *argv, __be32 *authp,
- struct xdr_netobj *in_handle)
-{
- /* Read the verifier; should be NULL: */
- *authp = rpc_autherr_badverf;
- if (argv->iov_len < 2 * 4)
- return SVC_DENIED;
- if (svc_getnl(argv) != RPC_AUTH_NULL)
- return SVC_DENIED;
- if (svc_getnl(argv) != 0)
- return SVC_DENIED;
- /* Martial context handle and token for upcall: */
- *authp = rpc_autherr_badcred;
- if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0)
- return SVC_DENIED;
- if (dup_netobj(in_handle, &gc->gc_ctx))
- return SVC_CLOSE;
- *authp = rpc_autherr_badverf;
- return 0;
-}
-
-static inline int
-gss_read_verf(struct rpc_gss_wire_cred *gc,
- struct kvec *argv, __be32 *authp,
- struct xdr_netobj *in_handle,
- struct xdr_netobj *in_token)
-{
- struct xdr_netobj tmpobj;
- int res;
-
- res = gss_read_common_verf(gc, argv, authp, in_handle);
- if (res)
- return res;
-
- if (svc_safe_getnetobj(argv, &tmpobj)) {
- kfree(in_handle->data);
- return SVC_DENIED;
- }
- if (dup_netobj(in_token, &tmpobj)) {
- kfree(in_handle->data);
- return SVC_CLOSE;
- }
-
- return 0;
+null_verifier:
+ return xdr_stream_encode_opaque_auth(xdr, RPC_AUTH_NULL, NULL, 0) > 0;
}
static void gss_free_in_token_pages(struct gssp_in_token *in_token)
@@ -1161,40 +1063,43 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp,
struct xdr_netobj *in_handle,
struct gssp_in_token *in_token)
{
- struct kvec *argv = &rqstp->rq_arg.head[0];
+ struct xdr_stream *xdr = &rqstp->rq_arg_stream;
unsigned int length, pgto_offs, pgfrom_offs;
- int pages, i, res, pgto, pgfrom;
- size_t inlen, to_offs, from_offs;
+ int pages, i, pgto, pgfrom;
+ size_t to_offs, from_offs;
+ u32 inlen;
- res = gss_read_common_verf(gc, argv, &rqstp->rq_auth_stat, in_handle);
- if (res)
- return res;
+ if (dup_netobj(in_handle, &gc->gc_ctx))
+ return SVC_CLOSE;
- inlen = svc_getnl(argv);
- if (inlen > (argv->iov_len + rqstp->rq_arg.page_len)) {
- kfree(in_handle->data);
- return SVC_DENIED;
- }
+ /*
+ * RFC 2203 Section 5.2.2
+ *
+ * struct rpc_gss_init_arg {
+ * opaque gss_token<>;
+ * };
+ */
+ if (xdr_stream_decode_u32(xdr, &inlen) < 0)
+ goto out_denied_free;
+ if (inlen > xdr_stream_remaining(xdr))
+ goto out_denied_free;
pages = DIV_ROUND_UP(inlen, PAGE_SIZE);
in_token->pages = kcalloc(pages, sizeof(struct page *), GFP_KERNEL);
- if (!in_token->pages) {
- kfree(in_handle->data);
- return SVC_DENIED;
- }
+ if (!in_token->pages)
+ goto out_denied_free;
in_token->page_base = 0;
in_token->page_len = inlen;
for (i = 0; i < pages; i++) {
in_token->pages[i] = alloc_page(GFP_KERNEL);
if (!in_token->pages[i]) {
- kfree(in_handle->data);
gss_free_in_token_pages(in_token);
- return SVC_DENIED;
+ goto out_denied_free;
}
}
- length = min_t(unsigned int, inlen, argv->iov_len);
- memcpy(page_address(in_token->pages[0]), argv->iov_base, length);
+ length = min_t(unsigned int, inlen, (char *)xdr->end - (char *)xdr->p);
+ memcpy(page_address(in_token->pages[0]), xdr->p, length);
inlen -= length;
to_offs = length;
@@ -1217,26 +1122,41 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp,
inlen -= length;
}
return 0;
+
+out_denied_free:
+ kfree(in_handle->data);
+ return SVC_DENIED;
}
-static inline int
-gss_write_resv(struct kvec *resv, size_t size_limit,
- struct xdr_netobj *out_handle, struct xdr_netobj *out_token,
- int major_status, int minor_status)
-{
- if (resv->iov_len + 4 > size_limit)
- return -1;
- svc_putnl(resv, RPC_SUCCESS);
- if (svc_safe_putnetobj(resv, out_handle))
- return -1;
- if (resv->iov_len + 3 * 4 > size_limit)
- return -1;
- svc_putnl(resv, major_status);
- svc_putnl(resv, minor_status);
- svc_putnl(resv, GSS_SEQ_WIN);
- if (svc_safe_putnetobj(resv, out_token))
- return -1;
- return 0;
+/*
+ * RFC 2203, Section 5.2.3.1.
+ *
+ * struct rpc_gss_init_res {
+ * opaque handle<>;
+ * unsigned int gss_major;
+ * unsigned int gss_minor;
+ * unsigned int seq_window;
+ * opaque gss_token<>;
+ * };
+ */
+static bool
+svcxdr_encode_gss_init_res(struct xdr_stream *xdr,
+ struct xdr_netobj *handle,
+ struct xdr_netobj *gss_token,
+ unsigned int major_status,
+ unsigned int minor_status, u32 seq_num)
+{
+ if (xdr_stream_encode_opaque(xdr, handle->data, handle->len) < 0)
+ return false;
+ if (xdr_stream_encode_u32(xdr, major_status) < 0)
+ return false;
+ if (xdr_stream_encode_u32(xdr, minor_status) < 0)
+ return false;
+ if (xdr_stream_encode_u32(xdr, seq_num) < 0)
+ return false;
+ if (xdr_stream_encode_opaque(xdr, gss_token->data, gss_token->len) < 0)
+ return false;
+ return true;
}
/*
@@ -1246,20 +1166,44 @@ gss_write_resv(struct kvec *resv, size_t size_limit,
* the upcall results are available, write the verifier and result.
* Otherwise, drop the request pending an answer to the upcall.
*/
-static int svcauth_gss_legacy_init(struct svc_rqst *rqstp,
- struct rpc_gss_wire_cred *gc)
+static int
+svcauth_gss_legacy_init(struct svc_rqst *rqstp,
+ struct rpc_gss_wire_cred *gc)
{
- struct kvec *argv = &rqstp->rq_arg.head[0];
- struct kvec *resv = &rqstp->rq_res.head[0];
+ struct xdr_stream *xdr = &rqstp->rq_arg_stream;
struct rsi *rsip, rsikey;
+ __be32 *p;
+ u32 len;
int ret;
struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
memset(&rsikey, 0, sizeof(rsikey));
- ret = gss_read_verf(gc, argv, &rqstp->rq_auth_stat,
- &rsikey.in_handle, &rsikey.in_token);
- if (ret)
- return ret;
+ if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx))
+ return SVC_CLOSE;
+
+ /*
+ * RFC 2203 Section 5.2.2
+ *
+ * struct rpc_gss_init_arg {
+ * opaque gss_token<>;
+ * };
+ */
+ if (xdr_stream_decode_u32(xdr, &len) < 0) {
+ kfree(rsikey.in_handle.data);
+ return SVC_DENIED;
+ }
+ p = xdr_inline_decode(xdr, len);
+ if (!p) {
+ kfree(rsikey.in_handle.data);
+ return SVC_DENIED;
+ }
+ rsikey.in_token.data = kmalloc(len, GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR(rsikey.in_token.data)) {
+ kfree(rsikey.in_handle.data);
+ return SVC_CLOSE;
+ }
+ memcpy(rsikey.in_token.data, p, len);
+ rsikey.in_token.len = len;
/* Perform upcall, or find upcall result: */
rsip = rsi_lookup(sn->rsi_cache, &rsikey);
@@ -1271,13 +1215,14 @@ static int svcauth_gss_legacy_init(struct svc_rqst *rqstp,
return SVC_CLOSE;
ret = SVC_CLOSE;
- /* Got an answer to the upcall; use it: */
- if (gss_write_init_verf(sn->rsc_cache, rqstp,
- &rsip->out_handle, &rsip->major_status))
+ if (!svcauth_gss_proc_init_verf(sn->rsc_cache, rqstp, &rsip->out_handle,
+ &rsip->major_status, GSS_SEQ_WIN))
+ goto out;
+ if (!svcxdr_set_accept_stat(rqstp))
goto out;
- if (gss_write_resv(resv, PAGE_SIZE,
- &rsip->out_handle, &rsip->out_token,
- rsip->major_status, rsip->minor_status))
+ if (!svcxdr_encode_gss_init_res(&rqstp->rq_res_stream, &rsip->out_handle,
+ &rsip->out_token, rsip->major_status,
+ rsip->minor_status, GSS_SEQ_WIN))
goto out;
ret = SVC_COMPLETE;
@@ -1361,7 +1306,6 @@ out:
static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,
struct rpc_gss_wire_cred *gc)
{
- struct kvec *resv = &rqstp->rq_res.head[0];
struct xdr_netobj cli_handle;
struct gssp_upcall_data ud;
uint64_t handle;
@@ -1399,13 +1343,14 @@ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,
goto out;
}
- /* Got an answer to the upcall; use it: */
- if (gss_write_init_verf(sn->rsc_cache, rqstp,
- &cli_handle, &ud.major_status))
+ if (!svcauth_gss_proc_init_verf(sn->rsc_cache, rqstp, &cli_handle,
+ &ud.major_status, GSS_SEQ_WIN))
goto out;
- if (gss_write_resv(resv, PAGE_SIZE,
- &cli_handle, &ud.out_token,
- ud.major_status, ud.minor_status))
+ if (!svcxdr_set_accept_stat(rqstp))
+ goto out;
+ if (!svcxdr_encode_gss_init_res(&rqstp->rq_res_stream, &cli_handle,
+ &ud.out_token, ud.major_status,
+ ud.minor_status, GSS_SEQ_WIN))
goto out;
ret = SVC_COMPLETE;
@@ -1442,6 +1387,31 @@ static bool use_gss_proxy(struct net *net)
return sn->use_gss_proxy;
}
+static noinline_for_stack int
+svcauth_gss_proc_init(struct svc_rqst *rqstp, struct rpc_gss_wire_cred *gc)
+{
+ struct xdr_stream *xdr = &rqstp->rq_arg_stream;
+ u32 flavor, len;
+ void *body;
+
+ /* Call's verf field: */
+ if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0)
+ return SVC_GARBAGE;
+ if (flavor != RPC_AUTH_NULL || len != 0) {
+ rqstp->rq_auth_stat = rpc_autherr_badverf;
+ return SVC_DENIED;
+ }
+
+ if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0) {
+ rqstp->rq_auth_stat = rpc_autherr_badcred;
+ return SVC_DENIED;
+ }
+
+ if (!use_gss_proxy(SVC_NET(rqstp)))
+ return svcauth_gss_legacy_init(rqstp, gc);
+ return svcauth_gss_proxy_init(rqstp, gc);
+}
+
#ifdef CONFIG_PROC_FS
static ssize_t write_gssp(struct file *file, const char __user *buf,
@@ -1524,6 +1494,56 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net)
clear_gssp_clnt(sn);
}
}
+
+static ssize_t read_gss_krb5_enctypes(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct rpcsec_gss_oid oid = {
+ .len = 9,
+ .data = "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02",
+ };
+ struct gss_api_mech *mech;
+ ssize_t ret;
+
+ mech = gss_mech_get_by_OID(&oid);
+ if (!mech)
+ return 0;
+ if (!mech->gm_upcall_enctypes) {
+ gss_mech_put(mech);
+ return 0;
+ }
+
+ ret = simple_read_from_buffer(buf, count, ppos,
+ mech->gm_upcall_enctypes,
+ strlen(mech->gm_upcall_enctypes));
+ gss_mech_put(mech);
+ return ret;
+}
+
+static const struct proc_ops gss_krb5_enctypes_proc_ops = {
+ .proc_open = nonseekable_open,
+ .proc_read = read_gss_krb5_enctypes,
+};
+
+static int create_krb5_enctypes_proc_entry(struct net *net)
+{
+ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+
+ sn->gss_krb5_enctypes =
+ proc_create_data("gss_krb5_enctypes", S_IFREG | 0444,
+ sn->proc_net_rpc, &gss_krb5_enctypes_proc_ops,
+ net);
+ return sn->gss_krb5_enctypes ? 0 : -ENOMEM;
+}
+
+static void destroy_krb5_enctypes_proc_entry(struct net *net)
+{
+ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+
+ if (sn->gss_krb5_enctypes)
+ remove_proc_entry("gss_krb5_enctypes", sn->proc_net_rpc);
+}
+
#else /* CONFIG_PROC_FS */
static int create_use_gss_proxy_proc_entry(struct net *net)
@@ -1533,27 +1553,94 @@ static int create_use_gss_proxy_proc_entry(struct net *net)
static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
+static int create_krb5_enctypes_proc_entry(struct net *net)
+{
+ return 0;
+}
+
+static void destroy_krb5_enctypes_proc_entry(struct net *net) {}
+
#endif /* CONFIG_PROC_FS */
/*
- * Accept an rpcsec packet.
- * If context establishment, punt to user space
- * If data exchange, verify/decrypt
- * If context destruction, handle here
- * In the context establishment and destruction case we encode
- * response here and return SVC_COMPLETE.
+ * The Call's credential body should contain a struct rpc_gss_cred_t.
+ *
+ * RFC 2203 Section 5
+ *
+ * struct rpc_gss_cred_t {
+ * union switch (unsigned int version) {
+ * case RPCSEC_GSS_VERS_1:
+ * struct {
+ * rpc_gss_proc_t gss_proc;
+ * unsigned int seq_num;
+ * rpc_gss_service_t service;
+ * opaque handle<>;
+ * } rpc_gss_cred_vers_1_t;
+ * }
+ * };
+ */
+static bool
+svcauth_gss_decode_credbody(struct xdr_stream *xdr,
+ struct rpc_gss_wire_cred *gc,
+ __be32 **rpcstart)
+{
+ ssize_t handle_len;
+ u32 body_len;
+ __be32 *p;
+
+ p = xdr_inline_decode(xdr, XDR_UNIT);
+ if (!p)
+ return false;
+ /*
+ * start of rpc packet is 7 u32's back from here:
+ * xid direction rpcversion prog vers proc flavour
+ */
+ *rpcstart = p - 7;
+ body_len = be32_to_cpup(p);
+ if (body_len > RPC_MAX_AUTH_SIZE)
+ return false;
+
+ /* struct rpc_gss_cred_t */
+ if (xdr_stream_decode_u32(xdr, &gc->gc_v) < 0)
+ return false;
+ if (xdr_stream_decode_u32(xdr, &gc->gc_proc) < 0)
+ return false;
+ if (xdr_stream_decode_u32(xdr, &gc->gc_seq) < 0)
+ return false;
+ if (xdr_stream_decode_u32(xdr, &gc->gc_svc) < 0)
+ return false;
+ handle_len = xdr_stream_decode_opaque_inline(xdr,
+ (void **)&gc->gc_ctx.data,
+ body_len);
+ if (handle_len < 0)
+ return false;
+ if (body_len != XDR_UNIT * 5 + xdr_align_size(handle_len))
+ return false;
+
+ gc->gc_ctx.len = handle_len;
+ return true;
+}
+
+/**
+ * svcauth_gss_accept - Decode and validate incoming RPC_AUTH_GSS credential
+ * @rqstp: RPC transaction
+ *
+ * Return values:
+ * %SVC_OK: Success
+ * %SVC_COMPLETE: GSS context lifetime event
+ * %SVC_DENIED: Credential or verifier is not valid
+ * %SVC_GARBAGE: Failed to decode credential or verifier
+ * %SVC_CLOSE: Temporary failure
+ *
+ * The rqstp->rq_auth_stat field is also set (see RFCs 2203 and 5531).
*/
static int
svcauth_gss_accept(struct svc_rqst *rqstp)
{
- struct kvec *argv = &rqstp->rq_arg.head[0];
- struct kvec *resv = &rqstp->rq_res.head[0];
- u32 crlen;
struct gss_svc_data *svcdata = rqstp->rq_auth_data;
+ __be32 *rpcstart;
struct rpc_gss_wire_cred *gc;
struct rsc *rsci = NULL;
- __be32 *rpcstart;
- __be32 *reject_stat = resv->iov_base + resv->iov_len;
int ret;
struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
@@ -1563,53 +1650,31 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
if (!svcdata)
goto auth_err;
rqstp->rq_auth_data = svcdata;
- svcdata->verf_start = NULL;
+ svcdata->gsd_databody_offset = 0;
svcdata->rsci = NULL;
gc = &svcdata->clcred;
- /* start of rpc packet is 7 u32's back from here:
- * xid direction rpcversion prog vers proc flavour
- */
- rpcstart = argv->iov_base;
- rpcstart -= 7;
-
- /* credential is:
- * version(==1), proc(0,1,2,3), seq, service (1,2,3), handle
- * at least 5 u32s, and is preceded by length, so that makes 6.
- */
-
- if (argv->iov_len < 5 * 4)
+ if (!svcauth_gss_decode_credbody(&rqstp->rq_arg_stream, gc, &rpcstart))
goto auth_err;
- crlen = svc_getnl(argv);
- if (svc_getnl(argv) != RPC_GSS_VERSION)
- goto auth_err;
- gc->gc_proc = svc_getnl(argv);
- gc->gc_seq = svc_getnl(argv);
- gc->gc_svc = svc_getnl(argv);
- if (svc_safe_getnetobj(argv, &gc->gc_ctx))
- goto auth_err;
- if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4)
- goto auth_err;
-
- if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0))
+ if (gc->gc_v != RPC_GSS_VERSION)
goto auth_err;
- rqstp->rq_auth_stat = rpc_autherr_badverf;
switch (gc->gc_proc) {
case RPC_GSS_PROC_INIT:
case RPC_GSS_PROC_CONTINUE_INIT:
- if (use_gss_proxy(SVC_NET(rqstp)))
- return svcauth_gss_proxy_init(rqstp, gc);
- else
- return svcauth_gss_legacy_init(rqstp, gc);
- case RPC_GSS_PROC_DATA:
+ if (rqstp->rq_proc != 0)
+ goto auth_err;
+ return svcauth_gss_proc_init(rqstp, gc);
case RPC_GSS_PROC_DESTROY:
- /* Look up the context, and check the verifier: */
+ if (rqstp->rq_proc != 0)
+ goto auth_err;
+ fallthrough;
+ case RPC_GSS_PROC_DATA:
rqstp->rq_auth_stat = rpcsec_gsserr_credproblem;
rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx);
if (!rsci)
goto auth_err;
- switch (gss_verify_header(rqstp, rsci, rpcstart, gc)) {
+ switch (svcauth_gss_verify_header(rqstp, rsci, rpcstart, gc)) {
case SVC_OK:
break;
case SVC_DENIED:
@@ -1619,6 +1684,8 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
}
break;
default:
+ if (rqstp->rq_proc != 0)
+ goto auth_err;
rqstp->rq_auth_stat = rpc_autherr_rejectedcred;
goto auth_err;
}
@@ -1626,19 +1693,20 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
/* now act upon the command: */
switch (gc->gc_proc) {
case RPC_GSS_PROC_DESTROY:
- if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
+ if (!svcauth_gss_encode_verf(rqstp, rsci->mechctx, gc->gc_seq))
+ goto auth_err;
+ if (!svcxdr_set_accept_stat(rqstp))
goto auth_err;
/* Delete the entry from the cache_list and call cache_put */
sunrpc_cache_unhash(sn->rsc_cache, &rsci->h);
- if (resv->iov_len + 4 > PAGE_SIZE)
- goto drop;
- svc_putnl(resv, RPC_SUCCESS);
goto complete;
case RPC_GSS_PROC_DATA:
rqstp->rq_auth_stat = rpcsec_gsserr_ctxproblem;
- svcdata->verf_start = resv->iov_base + resv->iov_len;
- if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
+ if (!svcauth_gss_encode_verf(rqstp, rsci->mechctx, gc->gc_seq))
+ goto auth_err;
+ if (!svcxdr_set_accept_stat(rqstp))
goto auth_err;
+ svcdata->gsd_databody_offset = xdr_stream_pos(&rqstp->rq_res_stream);
rqstp->rq_cred = rsci->cred;
get_group_info(rsci->cred.cr_group_info);
rqstp->rq_auth_stat = rpc_autherr_badcred;
@@ -1646,22 +1714,20 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
case RPC_GSS_SVC_NONE:
break;
case RPC_GSS_SVC_INTEGRITY:
- /* placeholders for length and seq. number: */
- svc_putnl(resv, 0);
- svc_putnl(resv, 0);
- if (unwrap_integ_data(rqstp, &rqstp->rq_arg,
- gc->gc_seq, rsci->mechctx))
+ /* placeholders for body length and seq. number: */
+ xdr_reserve_space(&rqstp->rq_res_stream, XDR_UNIT * 2);
+ if (svcauth_gss_unwrap_integ(rqstp, gc->gc_seq,
+ rsci->mechctx))
goto garbage_args;
- rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE;
+ svcxdr_set_auth_slack(rqstp, RPC_MAX_AUTH_SIZE);
break;
case RPC_GSS_SVC_PRIVACY:
- /* placeholders for length and seq. number: */
- svc_putnl(resv, 0);
- svc_putnl(resv, 0);
- if (unwrap_priv_data(rqstp, &rqstp->rq_arg,
- gc->gc_seq, rsci->mechctx))
+ /* placeholders for body length and seq. number: */
+ xdr_reserve_space(&rqstp->rq_res_stream, XDR_UNIT * 2);
+ if (svcauth_gss_unwrap_priv(rqstp, gc->gc_seq,
+ rsci->mechctx))
goto garbage_args;
- rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE * 2;
+ svcxdr_set_auth_slack(rqstp, RPC_MAX_AUTH_SIZE * 2);
break;
default:
goto auth_err;
@@ -1680,8 +1746,7 @@ garbage_args:
ret = SVC_GARBAGE;
goto out;
auth_err:
- /* Restore write pointer to its original value: */
- xdr_ressize_check(rqstp, reject_stat);
+ xdr_truncate_encode(&rqstp->rq_res_stream, XDR_UNIT * 2);
ret = SVC_DENIED;
goto out;
complete:
@@ -1695,104 +1760,125 @@ out:
return ret;
}
-static __be32 *
-svcauth_gss_prepare_to_wrap(struct xdr_buf *resbuf, struct gss_svc_data *gsd)
+static u32
+svcauth_gss_prepare_to_wrap(struct svc_rqst *rqstp, struct gss_svc_data *gsd)
{
- __be32 *p;
- u32 verf_len;
+ u32 offset;
- p = gsd->verf_start;
- gsd->verf_start = NULL;
+ /* Release can be called twice, but we only wrap once. */
+ offset = gsd->gsd_databody_offset;
+ gsd->gsd_databody_offset = 0;
- /* If the reply stat is nonzero, don't wrap: */
- if (*(p-1) != rpc_success)
- return NULL;
- /* Skip the verifier: */
- p += 1;
- verf_len = ntohl(*p++);
- p += XDR_QUADLEN(verf_len);
- /* move accept_stat to right place: */
- memcpy(p, p + 2, 4);
- /* Also don't wrap if the accept stat is nonzero: */
- if (*p != rpc_success) {
- resbuf->head[0].iov_len -= 2 * 4;
- return NULL;
- }
- p++;
- return p;
+ /* AUTH_ERROR replies are not wrapped. */
+ if (rqstp->rq_auth_stat != rpc_auth_ok)
+ return 0;
+
+ /* Also don't wrap if the accept_stat is nonzero: */
+ if (*rqstp->rq_accept_statp != rpc_success)
+ return 0;
+
+ return offset;
}
-static inline int
-svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp)
+/*
+ * RFC 2203, Section 5.3.2.2
+ *
+ * struct rpc_gss_integ_data {
+ * opaque databody_integ<>;
+ * opaque checksum<>;
+ * };
+ *
+ * struct rpc_gss_data_t {
+ * unsigned int seq_num;
+ * proc_req_arg_t arg;
+ * };
+ *
+ * The RPC Reply message has already been XDR-encoded. rq_res_stream
+ * is now positioned so that the checksum can be written just past
+ * the RPC Reply message.
+ */
+static int svcauth_gss_wrap_integ(struct svc_rqst *rqstp)
{
- struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
+ struct gss_svc_data *gsd = rqstp->rq_auth_data;
+ struct xdr_stream *xdr = &rqstp->rq_res_stream;
struct rpc_gss_wire_cred *gc = &gsd->clcred;
- struct xdr_buf *resbuf = &rqstp->rq_res;
- struct xdr_buf integ_buf;
- struct xdr_netobj mic;
- struct kvec *resv;
- __be32 *p;
- int integ_offset, integ_len;
- int stat = -EINVAL;
+ struct xdr_buf *buf = xdr->buf;
+ struct xdr_buf databody_integ;
+ struct xdr_netobj checksum;
+ u32 offset, maj_stat;
- p = svcauth_gss_prepare_to_wrap(resbuf, gsd);
- if (p == NULL)
- goto out;
- integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base;
- integ_len = resbuf->len - integ_offset;
- if (integ_len & 3)
+ offset = svcauth_gss_prepare_to_wrap(rqstp, gsd);
+ if (!offset)
goto out;
- *p++ = htonl(integ_len);
- *p++ = htonl(gc->gc_seq);
- if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset, integ_len)) {
- WARN_ON_ONCE(1);
- goto out_err;
- }
- if (resbuf->tail[0].iov_base == NULL) {
- if (resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE > PAGE_SIZE)
- goto out_err;
- resbuf->tail[0].iov_base = resbuf->head[0].iov_base
- + resbuf->head[0].iov_len;
- resbuf->tail[0].iov_len = 0;
- }
- resv = &resbuf->tail[0];
- mic.data = (u8 *)resv->iov_base + resv->iov_len + 4;
- if (gss_get_mic(gsd->rsci->mechctx, &integ_buf, &mic))
- goto out_err;
- svc_putnl(resv, mic.len);
- memset(mic.data + mic.len, 0,
- round_up_to_quad(mic.len) - mic.len);
- resv->iov_len += XDR_QUADLEN(mic.len) << 2;
- /* not strictly required: */
- resbuf->len += XDR_QUADLEN(mic.len) << 2;
- if (resv->iov_len > PAGE_SIZE)
- goto out_err;
+
+ if (xdr_buf_subsegment(buf, &databody_integ, offset + XDR_UNIT,
+ buf->len - offset - XDR_UNIT))
+ goto wrap_failed;
+ /* Buffer space for these has already been reserved in
+ * svcauth_gss_accept(). */
+ if (xdr_encode_word(buf, offset, databody_integ.len))
+ goto wrap_failed;
+ if (xdr_encode_word(buf, offset + XDR_UNIT, gc->gc_seq))
+ goto wrap_failed;
+
+ checksum.data = gsd->gsd_scratch;
+ maj_stat = gss_get_mic(gsd->rsci->mechctx, &databody_integ, &checksum);
+ if (maj_stat != GSS_S_COMPLETE)
+ goto bad_mic;
+
+ if (xdr_stream_encode_opaque(xdr, checksum.data, checksum.len) < 0)
+ goto wrap_failed;
+ xdr_commit_encode(xdr);
+
out:
- stat = 0;
-out_err:
- return stat;
+ return 0;
+
+bad_mic:
+ trace_rpcgss_svc_get_mic(rqstp, maj_stat);
+ return -EINVAL;
+wrap_failed:
+ trace_rpcgss_svc_wrap_failed(rqstp);
+ return -EINVAL;
}
-static inline int
-svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp)
+/*
+ * RFC 2203, Section 5.3.2.3
+ *
+ * struct rpc_gss_priv_data {
+ * opaque databody_priv<>
+ * };
+ *
+ * struct rpc_gss_data_t {
+ * unsigned int seq_num;
+ * proc_req_arg_t arg;
+ * };
+ *
+ * gss_wrap() expands the size of the RPC message payload in the
+ * response buffer. The main purpose of svcauth_gss_wrap_priv()
+ * is to ensure there is adequate space in the response buffer to
+ * avoid overflow during the wrap.
+ */
+static int svcauth_gss_wrap_priv(struct svc_rqst *rqstp)
{
- struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
+ struct gss_svc_data *gsd = rqstp->rq_auth_data;
struct rpc_gss_wire_cred *gc = &gsd->clcred;
- struct xdr_buf *resbuf = &rqstp->rq_res;
- struct page **inpages = NULL;
- __be32 *p, *len;
- int offset;
- int pad;
-
- p = svcauth_gss_prepare_to_wrap(resbuf, gsd);
- if (p == NULL)
+ struct xdr_buf *buf = &rqstp->rq_res;
+ struct kvec *head = buf->head;
+ struct kvec *tail = buf->tail;
+ u32 offset, pad, maj_stat;
+ __be32 *p;
+
+ offset = svcauth_gss_prepare_to_wrap(rqstp, gsd);
+ if (!offset)
return 0;
- len = p++;
- offset = (u8 *)p - (u8 *)resbuf->head[0].iov_base;
- *p++ = htonl(gc->gc_seq);
- inpages = resbuf->pages;
- /* XXX: Would be better to write some xdr helper functions for
- * nfs{2,3,4}xdr.c that place the data right, instead of copying: */
+
+ /*
+ * Buffer space for this field has already been reserved
+ * in svcauth_gss_accept(). Note that the GSS sequence
+ * number is encrypted along with the RPC reply payload.
+ */
+ if (xdr_encode_word(buf, offset + XDR_UNIT, gc->gc_seq))
+ goto wrap_failed;
/*
* If there is currently tail data, make sure there is
@@ -1801,19 +1887,17 @@ svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp)
* there is RPC_MAX_AUTH_SIZE slack space available in
* both the head and tail.
*/
- if (resbuf->tail[0].iov_base) {
- if (resbuf->tail[0].iov_base >=
- resbuf->head[0].iov_base + PAGE_SIZE)
- return -EINVAL;
- if (resbuf->tail[0].iov_base < resbuf->head[0].iov_base)
- return -EINVAL;
- if (resbuf->tail[0].iov_len + resbuf->head[0].iov_len
+ if (tail->iov_base) {
+ if (tail->iov_base >= head->iov_base + PAGE_SIZE)
+ goto wrap_failed;
+ if (tail->iov_base < head->iov_base)
+ goto wrap_failed;
+ if (tail->iov_len + head->iov_len
+ 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE)
- return -ENOMEM;
- memmove(resbuf->tail[0].iov_base + RPC_MAX_AUTH_SIZE,
- resbuf->tail[0].iov_base,
- resbuf->tail[0].iov_len);
- resbuf->tail[0].iov_base += RPC_MAX_AUTH_SIZE;
+ goto wrap_failed;
+ memmove(tail->iov_base + RPC_MAX_AUTH_SIZE, tail->iov_base,
+ tail->iov_len);
+ tail->iov_base += RPC_MAX_AUTH_SIZE;
}
/*
* If there is no current tail data, make sure there is
@@ -1822,55 +1906,73 @@ svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp)
* is RPC_MAX_AUTH_SIZE slack space available in both the
* head and tail.
*/
- if (resbuf->tail[0].iov_base == NULL) {
- if (resbuf->head[0].iov_len + 2*RPC_MAX_AUTH_SIZE > PAGE_SIZE)
- return -ENOMEM;
- resbuf->tail[0].iov_base = resbuf->head[0].iov_base
- + resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE;
- resbuf->tail[0].iov_len = 0;
+ if (!tail->iov_base) {
+ if (head->iov_len + 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE)
+ goto wrap_failed;
+ tail->iov_base = head->iov_base
+ + head->iov_len + RPC_MAX_AUTH_SIZE;
+ tail->iov_len = 0;
}
- if (gss_wrap(gsd->rsci->mechctx, offset, resbuf, inpages))
- return -ENOMEM;
- *len = htonl(resbuf->len - offset);
- pad = 3 - ((resbuf->len - offset - 1)&3);
- p = (__be32 *)(resbuf->tail[0].iov_base + resbuf->tail[0].iov_len);
+
+ maj_stat = gss_wrap(gsd->rsci->mechctx, offset + XDR_UNIT, buf,
+ buf->pages);
+ if (maj_stat != GSS_S_COMPLETE)
+ goto bad_wrap;
+
+ /* Wrapping can change the size of databody_priv. */
+ if (xdr_encode_word(buf, offset, buf->len - offset - XDR_UNIT))
+ goto wrap_failed;
+ pad = xdr_pad_size(buf->len - offset - XDR_UNIT);
+ p = (__be32 *)(tail->iov_base + tail->iov_len);
memset(p, 0, pad);
- resbuf->tail[0].iov_len += pad;
- resbuf->len += pad;
+ tail->iov_len += pad;
+ buf->len += pad;
+
return 0;
+wrap_failed:
+ trace_rpcgss_svc_wrap_failed(rqstp);
+ return -EINVAL;
+bad_wrap:
+ trace_rpcgss_svc_wrap(rqstp, maj_stat);
+ return -ENOMEM;
}
+/**
+ * svcauth_gss_release - Wrap payload and release resources
+ * @rqstp: RPC transaction context
+ *
+ * Return values:
+ * %0: the Reply is ready to be sent
+ * %-ENOMEM: failed to allocate memory
+ * %-EINVAL: encoding error
+ *
+ * XXX: These return values do not match the return values documented
+ * for the auth_ops ->release method in linux/sunrpc/svcauth.h.
+ */
static int
svcauth_gss_release(struct svc_rqst *rqstp)
{
- struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
- struct rpc_gss_wire_cred *gc;
- struct xdr_buf *resbuf = &rqstp->rq_res;
- int stat = -EINVAL;
struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
+ struct gss_svc_data *gsd = rqstp->rq_auth_data;
+ struct rpc_gss_wire_cred *gc;
+ int stat;
if (!gsd)
goto out;
gc = &gsd->clcred;
if (gc->gc_proc != RPC_GSS_PROC_DATA)
goto out;
- /* Release can be called twice, but we only wrap once. */
- if (gsd->verf_start == NULL)
- goto out;
- /* normally not set till svc_send, but we need it here: */
- /* XXX: what for? Do we mess it up the moment we call svc_putu32
- * or whatever? */
- resbuf->len = total_buf_len(resbuf);
+
switch (gc->gc_svc) {
case RPC_GSS_SVC_NONE:
break;
case RPC_GSS_SVC_INTEGRITY:
- stat = svcauth_gss_wrap_resp_integ(rqstp);
+ stat = svcauth_gss_wrap_integ(rqstp);
if (stat)
goto out_err;
break;
case RPC_GSS_SVC_PRIVACY:
- stat = svcauth_gss_wrap_resp_priv(rqstp);
+ stat = svcauth_gss_wrap_priv(rqstp);
if (stat)
goto out_err;
break;
@@ -1997,7 +2099,15 @@ gss_svc_init_net(struct net *net)
rv = create_use_gss_proxy_proc_entry(net);
if (rv)
goto out2;
+
+ rv = create_krb5_enctypes_proc_entry(net);
+ if (rv)
+ goto out3;
+
return 0;
+
+out3:
+ destroy_use_gss_proxy_proc_entry(net);
out2:
rsi_cache_destroy_net(net);
out1:
@@ -2008,6 +2118,7 @@ out1:
void
gss_svc_shutdown_net(struct net *net)
{
+ destroy_krb5_enctypes_proc_entry(net);
destroy_use_gss_proxy_proc_entry(net);
rsi_cache_destroy_net(net);
rsc_cache_destroy_net(net);
diff --git a/net/sunrpc/netns.h b/net/sunrpc/netns.h
index 7ec10b92bea1..4efb5f28d881 100644
--- a/net/sunrpc/netns.h
+++ b/net/sunrpc/netns.h
@@ -33,6 +33,7 @@ struct sunrpc_net {
int pipe_version;
atomic_t pipe_users;
struct proc_dir_entry *use_gssp_proc;
+ struct proc_dir_entry *gss_krb5_enctypes;
};
extern unsigned int sunrpc_net_id;
diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c
index 52908f9e6eab..65fc1297c6df 100644
--- a/net/sunrpc/stats.c
+++ b/net/sunrpc/stats.c
@@ -83,7 +83,8 @@ void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp)
{
const struct svc_program *prog = statp->program;
const struct svc_version *vers;
- unsigned int i, j;
+ unsigned int i, j, k;
+ unsigned long count;
seq_printf(seq,
"net %u %u %u %u\n",
@@ -104,8 +105,12 @@ void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp)
if (!vers)
continue;
seq_printf(seq, "proc%d %u", i, vers->vs_nproc);
- for (j = 0; j < vers->vs_nproc; j++)
- seq_printf(seq, " %u", vers->vs_count[j]);
+ for (j = 0; j < vers->vs_nproc; j++) {
+ count = 0;
+ for_each_possible_cpu(k)
+ count += per_cpu(vers->vs_count[j], k);
+ seq_printf(seq, " %lu", count);
+ }
seq_putc(seq, '\n');
}
}
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index f06622814a95..1fd3f5e57285 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -512,6 +512,10 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
INIT_LIST_HEAD(&pool->sp_sockets);
INIT_LIST_HEAD(&pool->sp_all_threads);
spin_lock_init(&pool->sp_lock);
+
+ percpu_counter_init(&pool->sp_sockets_queued, 0, GFP_KERNEL);
+ percpu_counter_init(&pool->sp_threads_woken, 0, GFP_KERNEL);
+ percpu_counter_init(&pool->sp_threads_timedout, 0, GFP_KERNEL);
}
return serv;
@@ -565,6 +569,7 @@ void
svc_destroy(struct kref *ref)
{
struct svc_serv *serv = container_of(ref, struct svc_serv, sv_refcnt);
+ unsigned int i;
dprintk("svc: svc_destroy(%s)\n", serv->sv_program->pg_name);
timer_shutdown_sync(&serv->sv_temptimer);
@@ -580,6 +585,13 @@ svc_destroy(struct kref *ref)
svc_pool_map_put(serv->sv_nrpools);
+ for (i = 0; i < serv->sv_nrpools; i++) {
+ struct svc_pool *pool = &serv->sv_pools[i];
+
+ percpu_counter_destroy(&pool->sp_sockets_queued);
+ percpu_counter_destroy(&pool->sp_threads_woken);
+ percpu_counter_destroy(&pool->sp_threads_timedout);
+ }
kfree(serv->sv_pools);
kfree(serv);
}
@@ -1208,7 +1220,7 @@ svc_generic_init_request(struct svc_rqst *rqstp,
memset(rqstp->rq_resp, 0, procp->pc_ressize);
/* Bump per-procedure stats counter */
- versp->vs_count[rqstp->rq_proc]++;
+ this_cpu_inc(versp->vs_count[rqstp->rq_proc]);
ret->dispatch = versp->vs_dispatch;
return rpc_success;
@@ -1225,22 +1237,16 @@ EXPORT_SYMBOL_GPL(svc_generic_init_request);
* Common routine for processing the RPC request.
*/
static int
-svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
+svc_process_common(struct svc_rqst *rqstp)
{
+ struct xdr_stream *xdr = &rqstp->rq_res_stream;
struct svc_program *progp;
const struct svc_procedure *procp = NULL;
struct svc_serv *serv = rqstp->rq_server;
struct svc_process_info process;
- __be32 *statp;
- u32 prog, vers;
- __be32 rpc_stat;
int auth_res, rc;
- __be32 *reply_statp;
-
- rpc_stat = rpc_success;
-
- if (argv->iov_len < 6*4)
- goto err_short_len;
+ unsigned int aoffset;
+ __be32 *p;
/* Will be turned off by GSS integrity and privacy services */
set_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
@@ -1248,27 +1254,25 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
set_bit(RQ_USEDEFERRAL, &rqstp->rq_flags);
clear_bit(RQ_DROPME, &rqstp->rq_flags);
- svc_putu32(resv, rqstp->rq_xid);
-
- vers = svc_getnl(argv);
-
- /* First words of reply: */
- svc_putnl(resv, 1); /* REPLY */
+ /* Construct the first words of the reply: */
+ svcxdr_init_encode(rqstp);
+ xdr_stream_encode_be32(xdr, rqstp->rq_xid);
+ xdr_stream_encode_be32(xdr, rpc_reply);
- if (vers != 2) /* RPC version number */
+ p = xdr_inline_decode(&rqstp->rq_arg_stream, XDR_UNIT * 4);
+ if (unlikely(!p))
+ goto err_short_len;
+ if (*p++ != cpu_to_be32(RPC_VERSION))
goto err_bad_rpc;
- /* Save position in case we later decide to reject: */
- reply_statp = resv->iov_base + resv->iov_len;
-
- svc_putnl(resv, 0); /* ACCEPT */
+ xdr_stream_encode_be32(xdr, rpc_msg_accepted);
- rqstp->rq_prog = prog = svc_getnl(argv); /* program number */
- rqstp->rq_vers = svc_getnl(argv); /* version number */
- rqstp->rq_proc = svc_getnl(argv); /* procedure number */
+ rqstp->rq_prog = be32_to_cpup(p++);
+ rqstp->rq_vers = be32_to_cpup(p++);
+ rqstp->rq_proc = be32_to_cpup(p);
for (progp = serv->sv_program; progp; progp = progp->pg_next)
- if (prog == progp->pg_prog)
+ if (rqstp->rq_prog == progp->pg_prog)
break;
/*
@@ -1285,10 +1289,9 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
case SVC_OK:
break;
case SVC_GARBAGE:
- goto err_garbage;
+ goto err_garbage_args;
case SVC_SYSERR:
- rpc_stat = rpc_system_err;
- goto err_bad;
+ goto err_system_err;
case SVC_DENIED:
goto err_bad_auth;
case SVC_CLOSE:
@@ -1302,8 +1305,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
if (progp == NULL)
goto err_bad_prog;
- rpc_stat = progp->pg_init_request(rqstp, progp, &process);
- switch (rpc_stat) {
+ switch (progp->pg_init_request(rqstp, progp, &process)) {
case rpc_success:
break;
case rpc_prog_unavail:
@@ -1323,9 +1325,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
serv->sv_stats->rpccnt++;
trace_svc_process(rqstp, progp->pg_name);
- /* Build the reply header. */
- statp = resv->iov_base +resv->iov_len;
- svc_putnl(resv, RPC_SUCCESS);
+ aoffset = xdr_stream_pos(xdr);
/* un-reserve some of the out-queue now that we have a
* better idea of reply size
@@ -1334,7 +1334,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
svc_reserve_auth(rqstp, procp->pc_xdrressize<<2);
/* Call the function that processes the request. */
- rc = process.dispatch(rqstp, statp);
+ rc = process.dispatch(rqstp);
if (procp->pc_release)
procp->pc_release(rqstp);
if (!rc)
@@ -1342,9 +1342,8 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
if (rqstp->rq_auth_stat != rpc_auth_ok)
goto err_bad_auth;
- /* Check RPC status result */
- if (*statp != rpc_success)
- resv->iov_len = ((void*)statp) - resv->iov_base + 4;
+ if (*rqstp->rq_accept_statp != rpc_success)
+ xdr_truncate_encode(xdr, aoffset);
if (procp->pc_encode == NULL)
goto dropit;
@@ -1368,33 +1367,34 @@ close_xprt:
return 0;
err_short_len:
- svc_printk(rqstp, "short len %zd, dropping request\n",
- argv->iov_len);
+ svc_printk(rqstp, "short len %u, dropping request\n",
+ rqstp->rq_arg.len);
goto close_xprt;
err_bad_rpc:
serv->sv_stats->rpcbadfmt++;
- svc_putnl(resv, 1); /* REJECT */
- svc_putnl(resv, 0); /* RPC_MISMATCH */
- svc_putnl(resv, 2); /* Only RPCv2 supported */
- svc_putnl(resv, 2);
+ xdr_stream_encode_u32(xdr, RPC_MSG_DENIED);
+ xdr_stream_encode_u32(xdr, RPC_MISMATCH);
+ /* Only RPCv2 supported */
+ xdr_stream_encode_u32(xdr, RPC_VERSION);
+ xdr_stream_encode_u32(xdr, RPC_VERSION);
goto sendit;
err_bad_auth:
dprintk("svc: authentication failed (%d)\n",
be32_to_cpu(rqstp->rq_auth_stat));
serv->sv_stats->rpcbadauth++;
- /* Restore write pointer to location of accept status: */
- xdr_ressize_check(rqstp, reply_statp);
- svc_putnl(resv, 1); /* REJECT */
- svc_putnl(resv, 1); /* AUTH_ERROR */
- svc_putu32(resv, rqstp->rq_auth_stat); /* status */
+ /* Restore write pointer to location of reply status: */
+ xdr_truncate_encode(xdr, XDR_UNIT * 2);
+ xdr_stream_encode_u32(xdr, RPC_MSG_DENIED);
+ xdr_stream_encode_u32(xdr, RPC_AUTH_ERROR);
+ xdr_stream_encode_be32(xdr, rqstp->rq_auth_stat);
goto sendit;
err_bad_prog:
- dprintk("svc: unknown program %d\n", prog);
+ dprintk("svc: unknown program %d\n", rqstp->rq_prog);
serv->sv_stats->rpcbadfmt++;
- svc_putnl(resv, RPC_PROG_UNAVAIL);
+ xdr_stream_encode_u32(xdr, RPC_PROG_UNAVAIL);
goto sendit;
err_bad_vers:
@@ -1402,25 +1402,28 @@ err_bad_vers:
rqstp->rq_vers, rqstp->rq_prog, progp->pg_name);
serv->sv_stats->rpcbadfmt++;
- svc_putnl(resv, RPC_PROG_MISMATCH);
- svc_putnl(resv, process.mismatch.lovers);
- svc_putnl(resv, process.mismatch.hivers);
+ xdr_stream_encode_u32(xdr, RPC_PROG_MISMATCH);
+ xdr_stream_encode_u32(xdr, process.mismatch.lovers);
+ xdr_stream_encode_u32(xdr, process.mismatch.hivers);
goto sendit;
err_bad_proc:
svc_printk(rqstp, "unknown procedure (%d)\n", rqstp->rq_proc);
serv->sv_stats->rpcbadfmt++;
- svc_putnl(resv, RPC_PROC_UNAVAIL);
+ xdr_stream_encode_u32(xdr, RPC_PROC_UNAVAIL);
goto sendit;
-err_garbage:
- svc_printk(rqstp, "failed to decode args\n");
+err_garbage_args:
+ svc_printk(rqstp, "failed to decode RPC header\n");
+
+ serv->sv_stats->rpcbadfmt++;
+ xdr_stream_encode_u32(xdr, RPC_GARBAGE_ARGS);
+ goto sendit;
- rpc_stat = rpc_garbage_args;
-err_bad:
+err_system_err:
serv->sv_stats->rpcbadfmt++;
- svc_putnl(resv, ntohl(rpc_stat));
+ xdr_stream_encode_u32(xdr, RPC_SYSTEM_ERR);
goto sendit;
}
@@ -1430,9 +1433,8 @@ err_bad:
int
svc_process(struct svc_rqst *rqstp)
{
- struct kvec *argv = &rqstp->rq_arg.head[0];
struct kvec *resv = &rqstp->rq_res.head[0];
- __be32 dir;
+ __be32 *p;
#if IS_ENABLED(CONFIG_FAIL_SUNRPC)
if (!fail_sunrpc.ignore_server_disconnect &&
@@ -1455,16 +1457,21 @@ svc_process(struct svc_rqst *rqstp)
rqstp->rq_res.tail[0].iov_base = NULL;
rqstp->rq_res.tail[0].iov_len = 0;
- dir = svc_getu32(argv);
- if (dir != rpc_call)
+ svcxdr_init_decode(rqstp);
+ p = xdr_inline_decode(&rqstp->rq_arg_stream, XDR_UNIT * 2);
+ if (unlikely(!p))
+ goto out_drop;
+ rqstp->rq_xid = *p++;
+ if (unlikely(*p != rpc_call))
goto out_baddir;
- if (!svc_process_common(rqstp, argv, resv))
+
+ if (!svc_process_common(rqstp))
goto out_drop;
return svc_send(rqstp);
out_baddir:
svc_printk(rqstp, "bad direction 0x%08x, dropping request\n",
- be32_to_cpu(dir));
+ be32_to_cpu(*p));
rqstp->rq_server->sv_stats->rpcbadfmt++;
out_drop:
svc_drop(rqstp);
@@ -1481,8 +1488,6 @@ int
bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
struct svc_rqst *rqstp)
{
- struct kvec *argv = &rqstp->rq_arg.head[0];
- struct kvec *resv = &rqstp->rq_res.head[0];
struct rpc_task *task;
int proc_error;
int error;
@@ -1513,18 +1518,21 @@ bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
rqstp->rq_arg.len = rqstp->rq_arg.head[0].iov_len +
rqstp->rq_arg.page_len;
- /* reset result send buffer "put" position */
- resv->iov_len = 0;
+ /* Reset the response buffer */
+ rqstp->rq_res.head[0].iov_len = 0;
/*
- * Skip the next two words because they've already been
- * processed in the transport
+ * Skip the XID and calldir fields because they've already
+ * been processed by the caller.
*/
- svc_getu32(argv); /* XID */
- svc_getnl(argv); /* CALLDIR */
+ svcxdr_init_decode(rqstp);
+ if (!xdr_inline_decode(&rqstp->rq_arg_stream, XDR_UNIT * 2)) {
+ error = -EINVAL;
+ goto out;
+ }
/* Parse and execute the bc call */
- proc_error = svc_process_common(rqstp, argv, resv);
+ proc_error = svc_process_common(rqstp);
atomic_dec(&req->rq_xprt->bc_slot_count);
if (!proc_error) {
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index c2ce12538008..ba629297da4e 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -462,11 +462,9 @@ void svc_xprt_enqueue(struct svc_xprt *xprt)
pool = svc_pool_for_cpu(xprt->xpt_server);
- atomic_long_inc(&pool->sp_stats.packets);
-
+ percpu_counter_inc(&pool->sp_sockets_queued);
spin_lock_bh(&pool->sp_lock);
list_add_tail(&xprt->xpt_ready, &pool->sp_sockets);
- pool->sp_stats.sockets_queued++;
spin_unlock_bh(&pool->sp_lock);
/* find a thread for this xprt */
@@ -474,7 +472,7 @@ void svc_xprt_enqueue(struct svc_xprt *xprt)
list_for_each_entry_rcu(rqstp, &pool->sp_all_threads, rq_all) {
if (test_and_set_bit(RQ_BUSY, &rqstp->rq_flags))
continue;
- atomic_long_inc(&pool->sp_stats.threads_woken);
+ percpu_counter_inc(&pool->sp_threads_woken);
rqstp->rq_qtime = ktime_get();
wake_up_process(rqstp->rq_task);
goto out_unlock;
@@ -769,7 +767,7 @@ static struct svc_xprt *svc_get_next_xprt(struct svc_rqst *rqstp, long timeout)
goto out_found;
if (!time_left)
- atomic_long_inc(&pool->sp_stats.threads_timedout);
+ percpu_counter_inc(&pool->sp_threads_timedout);
if (signalled() || kthread_should_stop())
return ERR_PTR(-EINTR);
@@ -888,9 +886,7 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
clear_bit(XPT_OLD, &xprt->xpt_flags);
- xprt->xpt_ops->xpo_secure_port(rqstp);
rqstp->rq_chandle.defer = svc_defer;
- rqstp->rq_xid = svc_getu32(&rqstp->rq_arg.head[0]);
if (serv->sv_stats)
serv->sv_stats->netcnt++;
@@ -1441,12 +1437,12 @@ static int svc_pool_stats_show(struct seq_file *m, void *p)
return 0;
}
- seq_printf(m, "%u %lu %lu %lu %lu\n",
+ seq_printf(m, "%u %llu %llu %llu %llu\n",
pool->sp_id,
- (unsigned long)atomic_long_read(&pool->sp_stats.packets),
- pool->sp_stats.sockets_queued,
- (unsigned long)atomic_long_read(&pool->sp_stats.threads_woken),
- (unsigned long)atomic_long_read(&pool->sp_stats.threads_timedout));
+ percpu_counter_sum_positive(&pool->sp_sockets_queued),
+ percpu_counter_sum_positive(&pool->sp_sockets_queued),
+ percpu_counter_sum_positive(&pool->sp_threads_woken),
+ percpu_counter_sum_positive(&pool->sp_threads_timedout));
return 0;
}
diff --git a/net/sunrpc/svcauth.c b/net/sunrpc/svcauth.c
index e72ba2f13f6c..67d8245a08af 100644
--- a/net/sunrpc/svcauth.c
+++ b/net/sunrpc/svcauth.c
@@ -63,14 +63,17 @@ svc_put_auth_ops(struct auth_ops *aops)
int
svc_authenticate(struct svc_rqst *rqstp)
{
- rpc_authflavor_t flavor;
- struct auth_ops *aops;
+ struct auth_ops *aops;
+ u32 flavor;
rqstp->rq_auth_stat = rpc_auth_ok;
- flavor = svc_getnl(&rqstp->rq_arg.head[0]);
-
- dprintk("svc: svc_authenticate (%d)\n", flavor);
+ /*
+ * Decode the Call credential's flavor field. The credential's
+ * body field is decoded in the chosen ->accept method below.
+ */
+ if (xdr_stream_decode_u32(&rqstp->rq_arg_stream, &flavor) < 0)
+ return SVC_GARBAGE;
aops = svc_get_auth_ops(flavor);
if (aops == NULL) {
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index b1efc34db6ed..983c5891cb56 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -729,23 +729,38 @@ out:
EXPORT_SYMBOL_GPL(svcauth_unix_set_client);
+/**
+ * svcauth_null_accept - Decode and validate incoming RPC_AUTH_NULL credential
+ * @rqstp: RPC transaction
+ *
+ * Return values:
+ * %SVC_OK: Both credential and verifier are valid
+ * %SVC_DENIED: Credential or verifier is not valid
+ * %SVC_GARBAGE: Failed to decode credential or verifier
+ * %SVC_CLOSE: Temporary failure
+ *
+ * rqstp->rq_auth_stat is set as mandated by RFC 5531.
+ */
static int
svcauth_null_accept(struct svc_rqst *rqstp)
{
- struct kvec *argv = &rqstp->rq_arg.head[0];
- struct kvec *resv = &rqstp->rq_res.head[0];
+ struct xdr_stream *xdr = &rqstp->rq_arg_stream;
struct svc_cred *cred = &rqstp->rq_cred;
+ u32 flavor, len;
+ void *body;
- if (argv->iov_len < 3*4)
+ /* Length of Call's credential body field: */
+ if (xdr_stream_decode_u32(xdr, &len) < 0)
return SVC_GARBAGE;
-
- if (svc_getu32(argv) != 0) {
- dprintk("svc: bad null cred\n");
+ if (len != 0) {
rqstp->rq_auth_stat = rpc_autherr_badcred;
return SVC_DENIED;
}
- if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
- dprintk("svc: bad null verf\n");
+
+ /* Call's verf field: */
+ if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0)
+ return SVC_GARBAGE;
+ if (flavor != RPC_AUTH_NULL || len != 0) {
rqstp->rq_auth_stat = rpc_autherr_badverf;
return SVC_DENIED;
}
@@ -757,9 +772,11 @@ svcauth_null_accept(struct svc_rqst *rqstp)
if (cred->cr_group_info == NULL)
return SVC_CLOSE; /* kmalloc failure - client must retry */
- /* Put NULL verifier */
- svc_putnl(resv, RPC_AUTH_NULL);
- svc_putnl(resv, 0);
+ if (xdr_stream_encode_opaque_auth(&rqstp->rq_res_stream,
+ RPC_AUTH_NULL, NULL, 0) < 0)
+ return SVC_CLOSE;
+ if (!svcxdr_set_accept_stat(rqstp))
+ return SVC_CLOSE;
rqstp->rq_cred.cr_flavor = RPC_AUTH_NULL;
return SVC_OK;
@@ -783,31 +800,45 @@ struct auth_ops svcauth_null = {
.name = "null",
.owner = THIS_MODULE,
.flavour = RPC_AUTH_NULL,
- .accept = svcauth_null_accept,
+ .accept = svcauth_null_accept,
.release = svcauth_null_release,
.set_client = svcauth_unix_set_client,
};
+/**
+ * svcauth_tls_accept - Decode and validate incoming RPC_AUTH_TLS credential
+ * @rqstp: RPC transaction
+ *
+ * Return values:
+ * %SVC_OK: Both credential and verifier are valid
+ * %SVC_DENIED: Credential or verifier is not valid
+ * %SVC_GARBAGE: Failed to decode credential or verifier
+ * %SVC_CLOSE: Temporary failure
+ *
+ * rqstp->rq_auth_stat is set as mandated by RFC 5531.
+ */
static int
svcauth_tls_accept(struct svc_rqst *rqstp)
{
+ struct xdr_stream *xdr = &rqstp->rq_arg_stream;
struct svc_cred *cred = &rqstp->rq_cred;
- struct kvec *argv = rqstp->rq_arg.head;
- struct kvec *resv = rqstp->rq_res.head;
+ u32 flavor, len;
+ void *body;
+ __be32 *p;
- if (argv->iov_len < XDR_UNIT * 3)
+ /* Length of Call's credential body field: */
+ if (xdr_stream_decode_u32(xdr, &len) < 0)
return SVC_GARBAGE;
-
- /* Call's cred length */
- if (svc_getu32(argv) != xdr_zero) {
+ if (len != 0) {
rqstp->rq_auth_stat = rpc_autherr_badcred;
return SVC_DENIED;
}
- /* Call's verifier flavor and its length */
- if (svc_getu32(argv) != rpc_auth_null ||
- svc_getu32(argv) != xdr_zero) {
+ /* Call's verf field: */
+ if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0)
+ return SVC_GARBAGE;
+ if (flavor != RPC_AUTH_NULL || len != 0) {
rqstp->rq_auth_stat = rpc_autherr_badverf;
return SVC_DENIED;
}
@@ -818,21 +849,27 @@ svcauth_tls_accept(struct svc_rqst *rqstp)
return SVC_DENIED;
}
- /* Mapping to nobody uid/gid is required */
+ /* Signal that mapping to nobody uid/gid is required */
cred->cr_uid = INVALID_UID;
cred->cr_gid = INVALID_GID;
cred->cr_group_info = groups_alloc(0);
if (cred->cr_group_info == NULL)
- return SVC_CLOSE; /* kmalloc failure - client must retry */
+ return SVC_CLOSE;
- /* Reply's verifier */
- svc_putnl(resv, RPC_AUTH_NULL);
if (rqstp->rq_xprt->xpt_ops->xpo_start_tls) {
- svc_putnl(resv, 8);
- memcpy(resv->iov_base + resv->iov_len, "STARTTLS", 8);
- resv->iov_len += 8;
- } else
- svc_putnl(resv, 0);
+ p = xdr_reserve_space(&rqstp->rq_res_stream, XDR_UNIT * 2 + 8);
+ if (!p)
+ return SVC_CLOSE;
+ *p++ = rpc_auth_null;
+ *p++ = cpu_to_be32(8);
+ memcpy(p, "STARTTLS", 8);
+ } else {
+ if (xdr_stream_encode_opaque_auth(&rqstp->rq_res_stream,
+ RPC_AUTH_NULL, NULL, 0) < 0)
+ return SVC_CLOSE;
+ }
+ if (!svcxdr_set_accept_stat(rqstp))
+ return SVC_CLOSE;
rqstp->rq_cred.cr_flavor = RPC_AUTH_TLS;
return SVC_OK;
@@ -842,32 +879,48 @@ struct auth_ops svcauth_tls = {
.name = "tls",
.owner = THIS_MODULE,
.flavour = RPC_AUTH_TLS,
- .accept = svcauth_tls_accept,
+ .accept = svcauth_tls_accept,
.release = svcauth_null_release,
.set_client = svcauth_unix_set_client,
};
+/**
+ * svcauth_unix_accept - Decode and validate incoming RPC_AUTH_SYS credential
+ * @rqstp: RPC transaction
+ *
+ * Return values:
+ * %SVC_OK: Both credential and verifier are valid
+ * %SVC_DENIED: Credential or verifier is not valid
+ * %SVC_GARBAGE: Failed to decode credential or verifier
+ * %SVC_CLOSE: Temporary failure
+ *
+ * rqstp->rq_auth_stat is set as mandated by RFC 5531.
+ */
static int
svcauth_unix_accept(struct svc_rqst *rqstp)
{
- struct kvec *argv = &rqstp->rq_arg.head[0];
- struct kvec *resv = &rqstp->rq_res.head[0];
+ struct xdr_stream *xdr = &rqstp->rq_arg_stream;
struct svc_cred *cred = &rqstp->rq_cred;
struct user_namespace *userns;
- u32 slen, i;
- int len = argv->iov_len;
+ u32 flavor, len, i;
+ void *body;
+ __be32 *p;
- if ((len -= 3*4) < 0)
+ /*
+ * This implementation ignores the length of the Call's
+ * credential body field and the timestamp and machinename
+ * fields.
+ */
+ p = xdr_inline_decode(xdr, XDR_UNIT * 3);
+ if (!p)
+ return SVC_GARBAGE;
+ len = be32_to_cpup(p + 2);
+ if (len > RPC_MAX_MACHINENAME)
+ return SVC_GARBAGE;
+ if (!xdr_inline_decode(xdr, len))
return SVC_GARBAGE;
- svc_getu32(argv); /* length */
- svc_getu32(argv); /* time stamp */
- slen = XDR_QUADLEN(svc_getnl(argv)); /* machname length */
- if (slen > 64 || (len -= (slen + 3)*4) < 0)
- goto badcred;
- argv->iov_base = (void*)((__be32*)argv->iov_base + slen); /* skip machname */
- argv->iov_len -= slen*4;
/*
* Note: we skip uid_valid()/gid_valid() checks here for
* backwards compatibility with clients that use -1 id's.
@@ -877,27 +930,42 @@ svcauth_unix_accept(struct svc_rqst *rqstp)
*/
userns = (rqstp->rq_xprt && rqstp->rq_xprt->xpt_cred) ?
rqstp->rq_xprt->xpt_cred->user_ns : &init_user_ns;
- cred->cr_uid = make_kuid(userns, svc_getnl(argv)); /* uid */
- cred->cr_gid = make_kgid(userns, svc_getnl(argv)); /* gid */
- slen = svc_getnl(argv); /* gids length */
- if (slen > UNX_NGROUPS || (len -= (slen + 2)*4) < 0)
+ if (xdr_stream_decode_u32(xdr, &i) < 0)
+ return SVC_GARBAGE;
+ cred->cr_uid = make_kuid(userns, i);
+ if (xdr_stream_decode_u32(xdr, &i) < 0)
+ return SVC_GARBAGE;
+ cred->cr_gid = make_kgid(userns, i);
+
+ if (xdr_stream_decode_u32(xdr, &len) < 0)
+ return SVC_GARBAGE;
+ if (len > UNX_NGROUPS)
goto badcred;
- cred->cr_group_info = groups_alloc(slen);
+ p = xdr_inline_decode(xdr, XDR_UNIT * len);
+ if (!p)
+ return SVC_GARBAGE;
+ cred->cr_group_info = groups_alloc(len);
if (cred->cr_group_info == NULL)
return SVC_CLOSE;
- for (i = 0; i < slen; i++) {
- kgid_t kgid = make_kgid(userns, svc_getnl(argv));
+ for (i = 0; i < len; i++) {
+ kgid_t kgid = make_kgid(userns, be32_to_cpup(p++));
cred->cr_group_info->gid[i] = kgid;
}
groups_sort(cred->cr_group_info);
- if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
+
+ /* Call's verf field: */
+ if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0)
+ return SVC_GARBAGE;
+ if (flavor != RPC_AUTH_NULL || len != 0) {
rqstp->rq_auth_stat = rpc_autherr_badverf;
return SVC_DENIED;
}
- /* Put NULL verifier */
- svc_putnl(resv, RPC_AUTH_NULL);
- svc_putnl(resv, 0);
+ if (xdr_stream_encode_opaque_auth(&rqstp->rq_res_stream,
+ RPC_AUTH_NULL, NULL, 0) < 0)
+ return SVC_CLOSE;
+ if (!svcxdr_set_accept_stat(rqstp))
+ return SVC_CLOSE;
rqstp->rq_cred.cr_flavor = RPC_AUTH_UNIX;
return SVC_OK;
@@ -927,7 +995,7 @@ struct auth_ops svcauth_unix = {
.name = "unix",
.owner = THIS_MODULE,
.flavour = RPC_AUTH_UNIX,
- .accept = svcauth_unix_accept,
+ .accept = svcauth_unix_accept,
.release = svcauth_unix_release,
.domain_release = svcauth_unix_domain_release,
.set_client = svcauth_unix_set_client,
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 8bcc8c3ffbfe..03a4f5615086 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -508,6 +508,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
if (serv->sv_stats)
serv->sv_stats->netudpcnt++;
+ svc_sock_secure_port(rqstp);
svc_xprt_received(rqstp->rq_xprt);
return len;
@@ -636,7 +637,6 @@ static const struct svc_xprt_ops svc_udp_ops = {
.xpo_free = svc_sock_free,
.xpo_has_wspace = svc_udp_has_wspace,
.xpo_accept = svc_udp_accept,
- .xpo_secure_port = svc_sock_secure_port,
.xpo_kill_temp_xprt = svc_udp_kill_temp_xprt,
};
@@ -1030,6 +1030,7 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
if (serv->sv_stats)
serv->sv_stats->nettcpcnt++;
+ svc_sock_secure_port(rqstp);
svc_xprt_received(rqstp->rq_xprt);
return rqstp->rq_arg.len;
@@ -1211,7 +1212,6 @@ static const struct svc_xprt_ops svc_tcp_ops = {
.xpo_free = svc_sock_free,
.xpo_has_wspace = svc_tcp_has_wspace,
.xpo_accept = svc_tcp_accept,
- .xpo_secure_port = svc_sock_secure_port,
.xpo_kill_temp_xprt = svc_tcp_kill_temp_xprt,
};
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index afe7ec02d232..36835b2f5446 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -862,13 +862,6 @@ static unsigned int xdr_shrink_pagelen(struct xdr_buf *buf, unsigned int len)
return shift;
}
-void
-xdr_shift_buf(struct xdr_buf *buf, size_t len)
-{
- xdr_shrink_bufhead(buf, buf->head->iov_len - len);
-}
-EXPORT_SYMBOL_GPL(xdr_shift_buf);
-
/**
* xdr_stream_pos - Return the current offset from the start of the xdr_stream
* @xdr: pointer to struct xdr_stream
@@ -1192,6 +1185,21 @@ void xdr_truncate_encode(struct xdr_stream *xdr, size_t len)
EXPORT_SYMBOL(xdr_truncate_encode);
/**
+ * xdr_truncate_decode - Truncate a decoding stream
+ * @xdr: pointer to struct xdr_stream
+ * @len: Number of bytes to remove
+ *
+ */
+void xdr_truncate_decode(struct xdr_stream *xdr, size_t len)
+{
+ unsigned int nbytes = xdr_align_size(len);
+
+ xdr->buf->len -= nbytes;
+ xdr->nwords -= XDR_QUADLEN(nbytes);
+}
+EXPORT_SYMBOL_GPL(xdr_truncate_decode);
+
+/**
* xdr_restrict_buflen - decrease available buffer space
* @xdr: pointer to xdr_stream
* @newbuflen: new maximum number of bytes available
@@ -2273,3 +2281,60 @@ ssize_t xdr_stream_decode_string_dup(struct xdr_stream *xdr, char **str,
return ret;
}
EXPORT_SYMBOL_GPL(xdr_stream_decode_string_dup);
+
+/**
+ * xdr_stream_decode_opaque_auth - Decode struct opaque_auth (RFC5531 S8.2)
+ * @xdr: pointer to xdr_stream
+ * @flavor: location to store decoded flavor
+ * @body: location to store decode body
+ * @body_len: location to store length of decoded body
+ *
+ * Return values:
+ * On success, returns the number of buffer bytes consumed
+ * %-EBADMSG on XDR buffer overflow
+ * %-EMSGSIZE if the decoded size of the body field exceeds 400 octets
+ */
+ssize_t xdr_stream_decode_opaque_auth(struct xdr_stream *xdr, u32 *flavor,
+ void **body, unsigned int *body_len)
+{
+ ssize_t ret, len;
+
+ len = xdr_stream_decode_u32(xdr, flavor);
+ if (unlikely(len < 0))
+ return len;
+ ret = xdr_stream_decode_opaque_inline(xdr, body, RPC_MAX_AUTH_SIZE);
+ if (unlikely(ret < 0))
+ return ret;
+ *body_len = ret;
+ return len + ret;
+}
+EXPORT_SYMBOL_GPL(xdr_stream_decode_opaque_auth);
+
+/**
+ * xdr_stream_encode_opaque_auth - Encode struct opaque_auth (RFC5531 S8.2)
+ * @xdr: pointer to xdr_stream
+ * @flavor: verifier flavor to encode
+ * @body: content of body to encode
+ * @body_len: length of body to encode
+ *
+ * Return values:
+ * On success, returns length in bytes of XDR buffer consumed
+ * %-EBADMSG on XDR buffer overflow
+ * %-EMSGSIZE if the size of @body exceeds 400 octets
+ */
+ssize_t xdr_stream_encode_opaque_auth(struct xdr_stream *xdr, u32 flavor,
+ void *body, unsigned int body_len)
+{
+ ssize_t ret, len;
+
+ if (unlikely(body_len > RPC_MAX_AUTH_SIZE))
+ return -EMSGSIZE;
+ len = xdr_stream_encode_u32(xdr, flavor);
+ if (unlikely(len < 0))
+ return len;
+ ret = xdr_stream_encode_opaque(xdr, body, body_len);
+ if (unlikely(ret < 0))
+ return ret;
+ return len + ret;
+}
+EXPORT_SYMBOL_GPL(xdr_stream_encode_opaque_auth);
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
index 5242ad121450..1c658fa43063 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
@@ -847,6 +847,7 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
rqstp->rq_xprt_ctxt = ctxt;
rqstp->rq_prot = IPPROTO_MAX;
svc_xprt_copy_addrs(rqstp, xprt);
+ set_bit(RQ_SECURE, &rqstp->rq_flags);
return rqstp->rq_arg.len;
out_err:
diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c
index 94b20fb47135..416b298f74dd 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_transport.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c
@@ -73,7 +73,6 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt);
static void svc_rdma_detach(struct svc_xprt *xprt);
static void svc_rdma_free(struct svc_xprt *xprt);
static int svc_rdma_has_wspace(struct svc_xprt *xprt);
-static void svc_rdma_secure_port(struct svc_rqst *);
static void svc_rdma_kill_temp_xprt(struct svc_xprt *);
static const struct svc_xprt_ops svc_rdma_ops = {
@@ -86,7 +85,6 @@ static const struct svc_xprt_ops svc_rdma_ops = {
.xpo_free = svc_rdma_free,
.xpo_has_wspace = svc_rdma_has_wspace,
.xpo_accept = svc_rdma_accept,
- .xpo_secure_port = svc_rdma_secure_port,
.xpo_kill_temp_xprt = svc_rdma_kill_temp_xprt,
};
@@ -600,11 +598,6 @@ static int svc_rdma_has_wspace(struct svc_xprt *xprt)
return 1;
}
-static void svc_rdma_secure_port(struct svc_rqst *rqstp)
-{
- set_bit(RQ_SECURE, &rqstp->rq_flags);
-}
-
static void svc_rdma_kill_temp_xprt(struct svc_xprt *xprt)
{
}