summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/Kconfig20
-rw-r--r--fs/lockd/svclock.c9
-rw-r--r--fs/lockd/svcsubs.c2
-rw-r--r--fs/nfs/dir.c121
-rw-r--r--fs/nfs/inode.c154
-rw-r--r--fs/nfs/mount_clnt.c2
-rw-r--r--fs/nfs/nfs2xdr.c27
-rw-r--r--fs/nfs/nfs3proc.c24
-rw-r--r--fs/nfs/nfs3xdr.c4
-rw-r--r--fs/nfs/nfs4proc.c26
-rw-r--r--fs/nfs/nfs4xdr.c96
-rw-r--r--fs/nfs/nfsroot.c6
-rw-r--r--fs/nfs/pagelist.c15
-rw-r--r--fs/nfs/proc.c2
-rw-r--r--fs/nfs/write.c65
-rw-r--r--include/linux/nfs.h17
-rw-r--r--include/linux/nfs4.h2
-rw-r--r--include/linux/nfs_fs.h8
-rw-r--r--include/linux/nfs_fs_sb.h1
-rw-r--r--include/linux/nfs_page.h8
-rw-r--r--include/linux/nfs_xdr.h4
-rw-r--r--include/linux/sunrpc/gss_asn1.h1
-rw-r--r--include/linux/sunrpc/gss_spkm3.h61
-rw-r--r--net/sunrpc/auth_gss/Makefile4
-rw-r--r--net/sunrpc/auth_gss/auth_gss.c2
-rw-r--r--net/sunrpc/auth_gss/gss_generic_token.c2
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_unseal.c2
-rw-r--r--net/sunrpc/auth_gss/gss_spkm3_mech.c296
-rw-r--r--net/sunrpc/auth_gss/gss_spkm3_seal.c132
-rw-r--r--net/sunrpc/auth_gss/gss_spkm3_token.c266
-rw-r--r--net/sunrpc/auth_gss/gss_spkm3_unseal.c128
-rw-r--r--net/sunrpc/clnt.c41
32 files changed, 1253 insertions, 295 deletions
diff --git a/fs/Kconfig b/fs/Kconfig
index 347525531ab3..de3b1669d4e6 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -1415,8 +1415,8 @@ config NFS_V3
bool "Provide NFSv3 client support"
depends on NFS_FS
help
- Say Y here if you want your NFS client to be able to speak the newer
- version 3 of the NFS protocol.
+ Say Y here if you want your NFS client to be able to speak version
+ 3 of the NFS protocol.
If unsure, say Y.
@@ -1560,6 +1560,22 @@ config RPCSEC_GSS_KRB5
If unsure, say N.
+config RPCSEC_GSS_SPKM3
+ tristate "Secure RPC: SPKM3 mechanism (EXPERIMENTAL)"
+ depends on SUNRPC && EXPERIMENTAL
+ select SUNRPC_GSS
+ select CRYPTO
+ select CRYPTO_MD5
+ select CRYPTO_DES
+ help
+ Provides for secure RPC calls by means of a gss-api
+ mechanism based on the SPKM3 public-key mechanism.
+
+ Note: Requires an auxiliary userspace daemon which may be found on
+ http://www.citi.umich.edu/projects/nfsv4/
+
+ If unsure, say N.
+
config SMB_FS
tristate "SMB file system support (to mount Windows shares etc.)"
depends on INET
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index f38fb7e0981a..80e7cc4d44a2 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -237,8 +237,13 @@ nlmsvc_delete_block(struct nlm_block *block, int unlock)
/* Remove block from list */
nlmsvc_remove_block(block);
- posix_unblock_lock(file->f_file, fl);
- block->b_granted = 0;
+ if (fl->fl_next)
+ posix_unblock_lock(file->f_file, fl);
+ if (unlock) {
+ fl->fl_type = F_UNLCK;
+ posix_lock_file(file->f_file, fl);
+ block->b_granted = 0;
+ }
/* If the block is in the middle of a GRANT callback,
* don't kill it yet. */
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
index 4d37124b9289..de7536358c7c 100644
--- a/fs/lockd/svcsubs.c
+++ b/fs/lockd/svcsubs.c
@@ -67,7 +67,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
down(&nlm_file_sema);
for (file = nlm_files[hash]; file; file = file->f_next)
- if (!memcmp(&file->f_handle, f, sizeof(*f)))
+ if (!nfs_compare_fh(&file->f_handle, f))
goto found;
dprintk("lockd: creating file for (%08x %08x %08x %08x %08x %08x)\n",
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index f061e70cdc7d..59f88f6ee2fb 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -610,7 +610,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
verifier = nfs_save_change_attribute(dir);
error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
if (!error) {
- if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0)
+ if (nfs_compare_fh(NFS_FH(inode), &fhandle))
goto out_bad;
if (nfs_lookup_verify_inode(inode, isopen))
goto out_zap_parent;
@@ -623,7 +623,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
if (error)
goto out_bad;
- if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0)
+ if (nfs_compare_fh(NFS_FH(inode), &fhandle))
goto out_bad;
if ((error = nfs_refresh_inode(inode, &fattr)) != 0)
goto out_bad;
@@ -850,22 +850,22 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
unsigned long verifier;
int openflags, ret = 0;
- /* NFS only supports OPEN for regular files */
- if (inode && !S_ISREG(inode->i_mode))
- goto no_open;
parent = dget_parent(dentry);
dir = parent->d_inode;
if (!is_atomic_open(dir, nd))
goto no_open;
+ /* We can't create new files in nfs_open_revalidate(), so we
+ * optimize away revalidation of negative dentries.
+ */
+ if (inode == NULL)
+ goto out;
+ /* NFS only supports OPEN on regular files */
+ if (!S_ISREG(inode->i_mode))
+ goto no_open;
openflags = nd->intent.open.flags;
- if (openflags & O_CREAT) {
- /* If this is a negative dentry, just drop it */
- if (!inode)
- goto out;
- /* If this is exclusive open, just revalidate */
- if (openflags & O_EXCL)
- goto no_open;
- }
+ /* We cannot do exclusive creation on a positive dentry */
+ if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
+ goto no_open;
/* We can't create new files, or truncate existing ones here */
openflags &= ~(O_CREAT|O_TRUNC);
@@ -1299,19 +1299,6 @@ nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id,
dir->i_ino, dentry->d_name.name, symname);
- error = -ENAMETOOLONG;
- switch (NFS_PROTO(dir)->version) {
- case 2:
- if (strlen(symname) > NFS2_MAXPATHLEN)
- goto out;
- break;
- case 3:
- if (strlen(symname) > NFS3_MAXPATHLEN)
- goto out;
- default:
- break;
- }
-
#ifdef NFS_PARANOIA
if (dentry->d_inode)
printk("nfs_proc_symlink: %s/%s not negative!\n",
@@ -1341,8 +1328,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
d_drop(dentry);
}
unlock_kernel();
-
-out:
return error;
}
@@ -1498,10 +1483,56 @@ out:
return error;
}
-int
-nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+static int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res)
+{
+ struct nfs_access_entry *cache = &NFS_I(inode)->cache_access;
+
+ if (cache->cred != cred
+ || time_after(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
+ || (NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR))
+ return -ENOENT;
+ memcpy(res, cache, sizeof(*res));
+ return 0;
+}
+
+static void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
+{
+ struct nfs_access_entry *cache = &NFS_I(inode)->cache_access;
+
+ if (cache->cred != set->cred) {
+ if (cache->cred)
+ put_rpccred(cache->cred);
+ cache->cred = get_rpccred(set->cred);
+ }
+ cache->jiffies = set->jiffies;
+ cache->mask = set->mask;
+}
+
+static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
+{
+ struct nfs_access_entry cache;
+ int status;
+
+ status = nfs_access_get_cached(inode, cred, &cache);
+ if (status == 0)
+ goto out;
+
+ /* Be clever: ask server to check for all possible rights */
+ cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ;
+ cache.cred = cred;
+ cache.jiffies = jiffies;
+ status = NFS_PROTO(inode)->access(inode, &cache);
+ if (status != 0)
+ return status;
+ nfs_access_add_cache(inode, &cache);
+out:
+ if ((cache.mask & mask) == mask)
+ return 0;
+ return -EACCES;
+}
+
+int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
{
- struct nfs_access_cache *cache = &NFS_I(inode)->cache_access;
struct rpc_cred *cred;
int mode = inode->i_mode;
int res;
@@ -1542,24 +1573,7 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
goto out_notsup;
cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
- if (cache->cred == cred
- && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
- && !(NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR)) {
- if (!(res = cache->err)) {
- /* Is the mask a subset of an accepted mask? */
- if ((cache->mask & mask) == mask)
- goto out;
- } else {
- /* ...or is it a superset of a rejected mask? */
- if ((cache->mask & mask) == cache->mask)
- goto out;
- }
- }
-
- res = NFS_PROTO(inode)->access(inode, cred, mask);
- if (!res || res == -EACCES)
- goto add_cache;
-out:
+ res = nfs_do_access(inode, cred, mask);
put_rpccred(cred);
unlock_kernel();
return res;
@@ -1568,15 +1582,6 @@ out_notsup:
res = vfs_permission(inode, mask);
unlock_kernel();
return res;
-add_cache:
- cache->jiffies = jiffies;
- if (cache->cred)
- put_rpccred(cache->cred);
- cache->cred = cred;
- cache->mask = mask;
- cache->err = res;
- unlock_kernel();
- return res;
}
/*
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 36f5abc65dcb..daaa0680d737 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -57,7 +57,6 @@ static struct inode *nfs_alloc_inode(struct super_block *sb);
static void nfs_destroy_inode(struct inode *);
static void nfs_write_inode(struct inode *,int);
static void nfs_delete_inode(struct inode *);
-static void nfs_put_super(struct super_block *);
static void nfs_clear_inode(struct inode *);
static void nfs_umount_begin(struct super_block *);
static int nfs_statfs(struct super_block *, struct kstatfs *);
@@ -68,7 +67,6 @@ static struct super_operations nfs_sops = {
.destroy_inode = nfs_destroy_inode,
.write_inode = nfs_write_inode,
.delete_inode = nfs_delete_inode,
- .put_super = nfs_put_super,
.statfs = nfs_statfs,
.clear_inode = nfs_clear_inode,
.umount_begin = nfs_umount_begin,
@@ -152,27 +150,6 @@ nfs_clear_inode(struct inode *inode)
}
void
-nfs_put_super(struct super_block *sb)
-{
- struct nfs_server *server = NFS_SB(sb);
-
- nfs4_renewd_prepare_shutdown(server);
-
- if (server->client != NULL)
- rpc_shutdown_client(server->client);
- if (server->client_sys != NULL)
- rpc_shutdown_client(server->client_sys);
-
- if (!(server->flags & NFS_MOUNT_NONLM))
- lockd_down(); /* release rpc.lockd */
- rpciod_down(); /* release rpciod */
-
- destroy_nfsv4_state(server);
-
- kfree(server->hostname);
-}
-
-void
nfs_umount_begin(struct super_block *sb)
{
struct nfs_server *server = NFS_SB(sb);
@@ -293,14 +270,6 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
server->rsize = nfs_block_size(fsinfo.rtpref, NULL);
if (server->wsize == 0)
server->wsize = nfs_block_size(fsinfo.wtpref, NULL);
- if (sb->s_blocksize == 0) {
- if (fsinfo.wtmult == 0) {
- sb->s_blocksize = 512;
- sb->s_blocksize_bits = 9;
- } else
- sb->s_blocksize = nfs_block_bits(fsinfo.wtmult,
- &sb->s_blocksize_bits);
- }
if (fsinfo.rtmax >= 512 && server->rsize > fsinfo.rtmax)
server->rsize = nfs_block_size(fsinfo.rtmax, NULL);
@@ -319,6 +288,11 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
server->wsize = server->wpages << PAGE_CACHE_SHIFT;
}
+ if (sb->s_blocksize == 0)
+ sb->s_blocksize = nfs_block_bits(server->wsize,
+ &sb->s_blocksize_bits);
+ server->wtmult = nfs_block_bits(fsinfo.wtmult, NULL);
+
server->dtsize = nfs_block_size(fsinfo.dtpref, NULL);
if (server->dtsize > PAGE_CACHE_SIZE)
server->dtsize = PAGE_CACHE_SIZE;
@@ -405,7 +379,6 @@ static int
nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
{
struct nfs_server *server;
- int err = -EIO;
rpc_authflavor_t authflavor;
server = NFS_SB(sb);
@@ -424,10 +397,14 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
server->acdirmin = data->acdirmin*HZ;
server->acdirmax = data->acdirmax*HZ;
+ /* Start lockd here, before we might error out */
+ if (!(server->flags & NFS_MOUNT_NONLM))
+ lockd_up();
+
server->namelen = data->namlen;
server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL);
if (!server->hostname)
- goto out_fail;
+ return -ENOMEM;
strcpy(server->hostname, data->hostname);
/* Check NFS protocol revision and initialize RPC op vector
@@ -438,11 +415,11 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
server->caps |= NFS_CAP_READDIRPLUS;
if (data->version < 4) {
printk(KERN_NOTICE "NFS: NFSv3 not supported by mount program.\n");
- goto out_fail;
+ return -EIO;
}
#else
printk(KERN_NOTICE "NFS: NFSv3 not supported.\n");
- goto out_fail;
+ return -EIO;
#endif
} else {
server->rpc_ops = &nfs_v2_clientops;
@@ -457,30 +434,19 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
/* Create RPC client handles */
server->client = nfs_create_client(server, data);
if (IS_ERR(server->client))
- goto out_fail;
+ return PTR_ERR(server->client);
/* RFC 2623, sec 2.3.2 */
if (authflavor != RPC_AUTH_UNIX) {
server->client_sys = rpc_clone_client(server->client);
- if (server->client_sys == NULL)
- goto out_shutdown;
+ if (IS_ERR(server->client_sys))
+ return PTR_ERR(server->client_sys);
if (!rpcauth_create(RPC_AUTH_UNIX, server->client_sys))
- goto out_shutdown;
+ return -ENOMEM;
} else {
atomic_inc(&server->client->cl_count);
server->client_sys = server->client;
}
- /* Fire up rpciod if not yet running */
- if (rpciod_up() != 0) {
- printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
- goto out_shutdown;
- }
-
- sb->s_op = &nfs_sops;
- err = nfs_sb_init(sb, authflavor);
- if (err != 0)
- goto out_noinit;
-
if (server->flags & NFS_MOUNT_VER3) {
if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
server->namelen = NFS3_MAXNAMLEN;
@@ -489,21 +455,8 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
server->namelen = NFS2_MAXNAMLEN;
}
- /* Check whether to start the lockd process */
- if (!(server->flags & NFS_MOUNT_NONLM))
- lockd_up();
- return 0;
-out_noinit:
- rpciod_down();
-out_shutdown:
- if (server->client)
- rpc_shutdown_client(server->client);
- if (server->client_sys)
- rpc_shutdown_client(server->client_sys);
-out_fail:
- if (server->hostname)
- kfree(server->hostname);
- return err;
+ sb->s_op = &nfs_sops;
+ return nfs_sb_init(sb, authflavor);
}
static int
@@ -526,6 +479,7 @@ nfs_statfs(struct super_block *sb, struct kstatfs *buf)
if (error < 0)
goto out_err;
+ buf->f_frsize = server->wtmult;
buf->f_bsize = sb->s_blocksize;
blockbits = sb->s_blocksize_bits;
blockres = (1 << blockbits) - 1;
@@ -642,7 +596,7 @@ nfs_find_actor(struct inode *inode, void *opaque)
if (NFS_FILEID(inode) != fattr->fileid)
return 0;
- if (memcmp(NFS_FH(inode), fh, sizeof(struct nfs_fh)) != 0)
+ if (nfs_compare_fh(NFS_FH(inode), fh))
return 0;
if (is_bad_inode(inode))
return 0;
@@ -653,11 +607,10 @@ static int
nfs_init_locked(struct inode *inode, void *opaque)
{
struct nfs_find_desc *desc = (struct nfs_find_desc *)opaque;
- struct nfs_fh *fh = desc->fh;
struct nfs_fattr *fattr = desc->fattr;
NFS_FILEID(inode) = fattr->fileid;
- memcpy(NFS_FH(inode), fh, sizeof(struct nfs_fh));
+ nfs_copy_fh(NFS_FH(inode), desc->fh);
return 0;
}
@@ -1305,7 +1258,7 @@ static int nfs_compare_super(struct super_block *sb, void *data)
return 0;
if (old->addr.sin_port != server->addr.sin_port)
return 0;
- return !memcmp(&old->fh, &server->fh, sizeof(struct nfs_fh));
+ return !nfs_compare_fh(&old->fh, &server->fh);
}
static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
@@ -1330,9 +1283,7 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
init_nfsv4_state(server);
root = &server->fh;
- memcpy(root, &data->root, sizeof(*root));
- if (root->size < sizeof(root->data))
- memset(root->data+root->size, 0, sizeof(root->data)-root->size);
+ nfs_copy_fh(root, (struct nfs_fh *) &data->root);
if (data->version != NFS_MOUNT_VERSION) {
printk("nfs warning: mount version %s than kernel\n",
@@ -1343,7 +1294,6 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
data->bsize = 0;
if (data->version < 4) {
data->flags &= ~NFS_MOUNT_VER3;
- memset(root, 0, sizeof(*root));
root->size = NFS2_FHSIZE;
memcpy(root->data, data->old_root.data, NFS2_FHSIZE);
}
@@ -1373,6 +1323,13 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
s->s_flags = flags;
+ /* Fire up rpciod if not yet running */
+ if (rpciod_up() != 0) {
+ printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
+ kfree(server);
+ return ERR_PTR(-EIO);
+ }
+
error = nfs_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
if (error) {
up_write(&s->s_umount);
@@ -1386,7 +1343,25 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
static void nfs_kill_super(struct super_block *s)
{
struct nfs_server *server = NFS_SB(s);
+
kill_anon_super(s);
+
+ nfs4_renewd_prepare_shutdown(server);
+
+ if (server->client != NULL && !IS_ERR(server->client))
+ rpc_shutdown_client(server->client);
+ if (server->client_sys != NULL && !IS_ERR(server->client_sys))
+ rpc_shutdown_client(server->client_sys);
+
+ if (!(server->flags & NFS_MOUNT_NONLM))
+ lockd_down(); /* release rpc.lockd */
+
+ rpciod_down(); /* release rpciod */
+
+ destroy_nfsv4_state(server);
+
+ if (server->hostname != NULL)
+ kfree(server->hostname);
kfree(server);
}
@@ -1407,7 +1382,6 @@ static struct super_operations nfs4_sops = {
.destroy_inode = nfs_destroy_inode,
.write_inode = nfs_write_inode,
.delete_inode = nfs_delete_inode,
- .put_super = nfs_put_super,
.statfs = nfs_statfs,
.clear_inode = nfs4_clear_inode,
.umount_begin = nfs_umount_begin,
@@ -1498,7 +1472,7 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
clp = nfs4_get_client(&server->addr.sin_addr);
if (!clp) {
printk(KERN_WARNING "NFS: failed to create NFS4 client.\n");
- goto out_fail;
+ return -EIO;
}
/* Now create transport and client */
@@ -1547,45 +1521,29 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
if (IS_ERR(clnt)) {
printk(KERN_WARNING "NFS: cannot create RPC client.\n");
- err = PTR_ERR(clnt);
- goto out_remove_list;
+ return PTR_ERR(clnt);
}
clnt->cl_intr = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0;
clnt->cl_softrtry = (server->flags & NFS4_MOUNT_SOFT) ? 1 : 0;
server->client = clnt;
- err = -ENOMEM;
if (server->nfs4_state->cl_idmap == NULL) {
printk(KERN_WARNING "NFS: failed to create idmapper.\n");
- goto out_shutdown;
+ return -ENOMEM;
}
if (clnt->cl_auth->au_flavor != authflavour) {
if (rpcauth_create(authflavour, clnt) == NULL) {
printk(KERN_WARNING "NFS: couldn't create credcache!\n");
- goto out_shutdown;
+ return -ENOMEM;
}
}
- /* Fire up rpciod if not yet running */
- if (rpciod_up() != 0) {
- printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
- goto out_shutdown;
- }
-
sb->s_op = &nfs4_sops;
err = nfs_sb_init(sb, authflavour);
if (err == 0)
return 0;
- rpciod_down();
-out_shutdown:
- rpc_shutdown_client(server->client);
-out_remove_list:
- down_write(&server->nfs4_state->cl_sem);
- list_del_init(&server->nfs4_siblings);
- up_write(&server->nfs4_state->cl_sem);
- destroy_nfsv4_state(server);
out_fail:
if (clp)
nfs4_put_client(clp);
@@ -1691,6 +1649,13 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
s->s_flags = flags;
+ /* Fire up rpciod if not yet running */
+ if (rpciod_up() != 0) {
+ printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
+ s = ERR_PTR(-EIO);
+ goto out_free;
+ }
+
error = nfs4_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
if (error) {
up_write(&s->s_umount);
@@ -1764,6 +1729,7 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
SLAB_CTOR_CONSTRUCTOR) {
inode_init_once(&nfsi->vfs_inode);
+ spin_lock_init(&nfsi->req_lock);
INIT_LIST_HEAD(&nfsi->dirty);
INIT_LIST_HEAD(&nfsi->commit);
INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC);
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
index a2ddc6c5e2af..857e3ea4c40b 100644
--- a/fs/nfs/mount_clnt.c
+++ b/fs/nfs/mount_clnt.c
@@ -108,7 +108,6 @@ xdr_decode_fhstatus(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
{
struct nfs_fh *fh = res->fh;
- memset((void *)fh, 0, sizeof(*fh));
if ((res->status = ntohl(*p++)) == 0) {
fh->size = NFS2_FHSIZE;
memcpy(fh->data, p, NFS2_FHSIZE);
@@ -121,7 +120,6 @@ xdr_decode_fhstatus3(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
{
struct nfs_fh *fh = res->fh;
- memset((void *)fh, 0, sizeof(*fh));
if ((res->status = ntohl(*p++)) == 0) {
int size = ntohl(*p++);
if (size <= NFS3_FHSIZE) {
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
index 01aebbf13cc8..d69d2f2d5aa9 100644
--- a/fs/nfs/nfs2xdr.c
+++ b/fs/nfs/nfs2xdr.c
@@ -77,8 +77,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fhandle)
static inline u32 *
xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle)
{
- /* Zero handle first to allow comparisons */
- memset(fhandle, 0, sizeof(*fhandle));
/* NFSv2 handles have a fixed length */
fhandle->size = NFS2_FHSIZE;
memcpy(fhandle->data, p, NFS2_FHSIZE);
@@ -95,6 +93,23 @@ xdr_encode_time(u32 *p, struct timespec *timep)
}
static inline u32*
+xdr_encode_current_server_time(u32 *p, struct timespec *timep)
+{
+ /*
+ * Passing the invalid value useconds=1000000 is a
+ * Sun convention for "set to current server time".
+ * It's needed to make permissions checks for the
+ * "touch" program across v2 mounts to Solaris and
+ * Irix boxes work correctly. See description of
+ * sattr in section 6.1 of "NFS Illustrated" by
+ * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
+ */
+ *p++ = htonl(timep->tv_sec);
+ *p++ = htonl(1000000);
+ return p;
+}
+
+static inline u32*
xdr_decode_time(u32 *p, struct timespec *timep)
{
timep->tv_sec = ntohl(*p++);
@@ -142,15 +157,19 @@ xdr_encode_sattr(u32 *p, struct iattr *attr)
SATTR(p, attr, ATTR_GID, ia_gid);
SATTR(p, attr, ATTR_SIZE, ia_size);
- if (attr->ia_valid & (ATTR_ATIME|ATTR_ATIME_SET)) {
+ if (attr->ia_valid & ATTR_ATIME_SET) {
p = xdr_encode_time(p, &attr->ia_atime);
+ } else if (attr->ia_valid & ATTR_ATIME) {
+ p = xdr_encode_current_server_time(p, &attr->ia_atime);
} else {
*p++ = ~(u32) 0;
*p++ = ~(u32) 0;
}
- if (attr->ia_valid & (ATTR_MTIME|ATTR_MTIME_SET)) {
+ if (attr->ia_valid & ATTR_MTIME_SET) {
p = xdr_encode_time(p, &attr->ia_mtime);
+ } else if (attr->ia_valid & ATTR_MTIME) {
+ p = xdr_encode_current_server_time(p, &attr->ia_mtime);
} else {
*p++ = ~(u32) 0;
*p++ = ~(u32) 0;
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index a759f5a5f833..0bf9af7678b7 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -164,8 +164,7 @@ nfs3_proc_lookup(struct inode *dir, struct qstr *name,
return status;
}
-static int
-nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
+static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{
struct nfs_fattr fattr;
struct nfs3_accessargs arg = {
@@ -178,9 +177,10 @@ nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
.rpc_proc = &nfs3_procedures[NFS3PROC_ACCESS],
.rpc_argp = &arg,
.rpc_resp = &res,
- .rpc_cred = cred
+ .rpc_cred = entry->cred
};
- int status;
+ int mode = entry->mask;
+ int status;
dprintk("NFS call access\n");
fattr.valid = 0;
@@ -200,10 +200,16 @@ nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
}
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
nfs_refresh_inode(inode, &fattr);
- dprintk("NFS reply access\n");
-
- if (status == 0 && (arg.access & res.access) != arg.access)
- status = -EACCES;
+ if (status == 0) {
+ entry->mask = 0;
+ if (res.access & NFS3_ACCESS_READ)
+ entry->mask |= MAY_READ;
+ if (res.access & (NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND | NFS3_ACCESS_DELETE))
+ entry->mask |= MAY_WRITE;
+ if (res.access & (NFS3_ACCESS_LOOKUP|NFS3_ACCESS_EXECUTE))
+ entry->mask |= MAY_EXEC;
+ }
+ dprintk("NFS reply access, status = %d\n", status);
return status;
}
@@ -534,6 +540,8 @@ nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
};
int status;
+ if (path->len > NFS3_MAXPATHLEN)
+ return -ENAMETOOLONG;
dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
dir_attr.valid = 0;
fattr->valid = 0;
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index 415fa5bbb8c3..ba3ff20a643f 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -109,10 +109,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fh)
static inline u32 *
xdr_decode_fhandle(u32 *p, struct nfs_fh *fh)
{
- /*
- * Zero all nonused bytes
- */
- memset((u8 *)fh, 0, sizeof(*fh));
if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
memcpy(fh->data, p, fh->size);
return p + XDR_QUADLEN(fh->size);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 1b7338db87cb..feac998e178f 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -734,9 +734,8 @@ static int nfs4_proc_lookup(struct inode *dir, struct qstr *name,
return nfs4_map_errors(status);
}
-static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
+static int nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{
- int status;
struct nfs4_accessargs args = {
.fh = NFS_FH(inode),
};
@@ -745,8 +744,10 @@ static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ACCESS],
.rpc_argp = &args,
.rpc_resp = &res,
- .rpc_cred = cred,
+ .rpc_cred = entry->cred,
};
+ int mode = entry->mask;
+ int status;
/*
* Determine which access bits we want to ask for...
@@ -758,8 +759,7 @@ static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode
args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE;
if (mode & MAY_EXEC)
args.access |= NFS4_ACCESS_LOOKUP;
- }
- else {
+ } else {
if (mode & MAY_WRITE)
args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND;
if (mode & MAY_EXEC)
@@ -767,11 +767,13 @@ static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode
}
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (!status) {
- if (args.access != res.supported) {
- printk(KERN_NOTICE "NFS: server didn't support all access bits!\n");
- status = -ENOTSUPP;
- } else if ((args.access & res.access) != args.access)
- status = -EACCES;
+ entry->mask = 0;
+ if (res.access & NFS4_ACCESS_READ)
+ entry->mask |= MAY_READ;
+ if (res.access & (NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE))
+ entry->mask |= MAY_WRITE;
+ if (res.access & (NFS4_ACCESS_LOOKUP|NFS4_ACCESS_EXECUTE))
+ entry->mask |= MAY_EXEC;
}
return nfs4_map_errors(status);
}
@@ -1090,12 +1092,14 @@ static int nfs4_proc_symlink(struct inode *dir, struct qstr *name,
.fattr = fattr,
};
struct rpc_message msg = {
- .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE],
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SYMLINK],
.rpc_argp = &arg,
.rpc_resp = &res,
};
int status;
+ if (path->len > NFS4_MAXPATHLEN)
+ return -ENAMETOOLONG;
arg.u.symlink = path;
fattr->valid = 0;
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index a14079f53265..a8d4e4c5224e 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -84,6 +84,7 @@ static int nfs_stat_to_errno(int);
((3+NFS4_FHSIZE) >> 2))
#define encode_getattr_maxsz (op_encode_hdr_maxsz + 3)
#define nfs4_name_maxsz (1 + ((3 + NFS4_MAXNAMLEN) >> 2))
+#define nfs4_path_maxsz (1 + ((3 + NFS4_MAXPATHLEN) >> 2))
#define nfs4_fattr_bitmap_maxsz (36 + 2 * nfs4_name_maxsz)
#define decode_getattr_maxsz (op_decode_hdr_maxsz + 3 + \
nfs4_fattr_bitmap_maxsz)
@@ -118,8 +119,13 @@ static int nfs_stat_to_errno(int);
#define encode_link_maxsz (op_encode_hdr_maxsz + \
nfs4_name_maxsz)
#define decode_link_maxsz (op_decode_hdr_maxsz + 5)
+#define encode_symlink_maxsz (op_encode_hdr_maxsz + \
+ 1 + nfs4_name_maxsz + \
+ nfs4_path_maxsz + \
+ nfs4_fattr_bitmap_maxsz)
+#define decode_symlink_maxsz (op_decode_hdr_maxsz + 8)
#define encode_create_maxsz (op_encode_hdr_maxsz + \
- 2 + 2 * nfs4_name_maxsz + \
+ 2 + nfs4_name_maxsz + \
nfs4_fattr_bitmap_maxsz)
#define decode_create_maxsz (op_decode_hdr_maxsz + 8)
#define NFS4_enc_compound_sz (1024) /* XXX: large enough? */
@@ -313,6 +319,16 @@ static int nfs_stat_to_errno(int);
decode_savefh_maxsz + \
decode_putfh_maxsz + \
decode_link_maxsz)
+#define NFS4_enc_symlink_sz (compound_encode_hdr_maxsz + \
+ encode_putfh_maxsz + \
+ encode_symlink_maxsz + \
+ encode_getattr_maxsz + \
+ encode_getfh_maxsz)
+#define NFS4_dec_symlink_sz (compound_decode_hdr_maxsz + \
+ decode_putfh_maxsz + \
+ decode_symlink_maxsz + \
+ decode_getattr_maxsz + \
+ decode_getfh_maxsz)
#define NFS4_enc_create_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
encode_create_maxsz + \
@@ -927,7 +943,7 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
WRITE32(OP_READDIR);
WRITE64(readdir->cookie);
WRITEMEM(readdir->verifier.data, sizeof(readdir->verifier.data));
- WRITE32(readdir->count >> 5); /* meaningless "dircount" field */
+ WRITE32(readdir->count >> 1); /* We're not doing readdirplus */
WRITE32(readdir->count);
WRITE32(2);
WRITE32(FATTR4_WORD0_FILEID);
@@ -1244,6 +1260,14 @@ out:
}
/*
+ * Encode SYMLINK request
+ */
+static int nfs4_xdr_enc_symlink(struct rpc_rqst *req, uint32_t *p, const struct nfs4_create_arg *args)
+{
+ return nfs4_xdr_enc_create(req, p, args);
+}
+
+/*
* Encode GETATTR request
*/
static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, uint32_t *p, const struct nfs4_getattr_arg *args)
@@ -2817,8 +2841,8 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
struct kvec *iov = rcvbuf->head;
unsigned int nr, pglen = rcvbuf->page_len;
uint32_t *end, *entry, *p, *kaddr;
- uint32_t len, attrlen, word;
- int i, hdrlen, recvd, status;
+ uint32_t len, attrlen;
+ int hdrlen, recvd, status;
status = decode_op_hdr(xdr, OP_READDIR);
if (status)
@@ -2839,42 +2863,24 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
for (nr = 0; *p++; nr++) {
if (p + 3 > end)
goto short_pkt;
- p += 2; /* cookie */
- len = ntohl(*p++); /* filename length */
+ p += 2; /* cookie */
+ len = ntohl(*p++); /* filename length */
if (len > NFS4_MAXNAMLEN) {
printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)\n", len);
goto err_unmap;
}
-
p += XDR_QUADLEN(len);
if (p + 1 > end)
goto short_pkt;
- len = ntohl(*p++); /* bitmap length */
- if (len > 10) {
- printk(KERN_WARNING "NFS: giant bitmap in readdir (len 0x%x)\n", len);
- goto err_unmap;
- }
- if (p + len + 1 > end)
- goto short_pkt;
- attrlen = 0;
- for (i = 0; i < len; i++) {
- word = ntohl(*p++);
- if (!word)
- continue;
- else if (i == 0 && word == FATTR4_WORD0_FILEID) {
- attrlen = 8;
- continue;
- }
- printk(KERN_WARNING "NFS: unexpected bitmap word in readdir (0x%x)\n", word);
- goto err_unmap;
- }
- if (ntohl(*p++) != attrlen) {
- printk(KERN_WARNING "NFS: unexpected attrlen in readdir\n");
- goto err_unmap;
- }
- p += XDR_QUADLEN(attrlen);
+ len = ntohl(*p++); /* bitmap length */
+ p += len;
if (p + 1 > end)
goto short_pkt;
+ attrlen = XDR_QUADLEN(ntohl(*p++));
+ p += attrlen; /* attributes */
+ if (p + 2 > end)
+ goto short_pkt;
+ entry = p;
}
if (!nr && (entry[0] != 0 || entry[1] == 0))
goto short_pkt;
@@ -3222,6 +3228,14 @@ out:
}
/*
+ * Decode SYMLINK response
+ */
+static int nfs4_xdr_dec_symlink(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_create_res *res)
+{
+ return nfs4_xdr_dec_create(rqstp, p, res);
+}
+
+/*
* Decode GETATTR response
*/
static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_getattr_res *res)
@@ -3667,6 +3681,7 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, s
uint32_t *nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus)
{
+ uint32_t bitmap[1] = {0};
uint32_t len;
if (!*p++) {
@@ -3689,11 +3704,17 @@ uint32_t *nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus)
*/
entry->ino = 1;
- len = ntohl(*p++); /* bitmap length */
- p += len;
- len = ntohl(*p++); /* attribute buffer length */
- if (len)
- p = xdr_decode_hyper(p, &entry->ino);
+ len = ntohl(*p++); /* bitmap length */
+ if (len > 0) {
+ bitmap[0] = ntohl(*p);
+ p += len;
+ }
+ len = XDR_QUADLEN(ntohl(*p++)); /* attribute buffer length */
+ if (len > 0) {
+ if (bitmap[0] == FATTR4_WORD0_FILEID)
+ xdr_decode_hyper(p, &entry->ino);
+ p += len;
+ }
entry->eof = !p[0] && p[1];
return p;
@@ -3756,7 +3777,7 @@ nfs_stat_to_errno(int stat)
if (nfs_errtbl[i].stat == stat)
return nfs_errtbl[i].errno;
}
- if (stat < 0) {
+ if (stat <= 10000 || stat > 10100) {
/* The server is looney tunes. */
return ESERVERFAULT;
}
@@ -3804,6 +3825,7 @@ struct rpc_procinfo nfs4_procedures[] = {
PROC(REMOVE, enc_remove, dec_remove),
PROC(RENAME, enc_rename, dec_rename),
PROC(LINK, enc_link, dec_link),
+ PROC(SYMLINK, enc_symlink, dec_symlink),
PROC(CREATE, enc_create, dec_create),
PROC(PATHCONF, enc_pathconf, dec_pathconf),
PROC(STATFS, enc_statfs, dec_statfs),
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
index 864615916862..fe679026da7d 100644
--- a/fs/nfs/nfsroot.c
+++ b/fs/nfs/nfsroot.c
@@ -495,10 +495,8 @@ static int __init root_nfs_get_handle(void)
if (status < 0)
printk(KERN_ERR "Root-NFS: Server returned error %d "
"while mounting %s\n", status, nfs_path);
- else {
- nfs_data.root.size = fh.size;
- memcpy(nfs_data.root.data, fh.data, fh.size);
- }
+ else
+ nfs_copy_fh(nfs_data.root, fh);
return status;
}
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index c82fc852840e..dc496624345f 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -21,11 +21,6 @@
#define NFS_PARANOIA 1
-/*
- * Spinlock
- */
-spinlock_t nfs_wreq_lock = SPIN_LOCK_UNLOCKED;
-
static kmem_cache_t *nfs_page_cachep;
static inline struct nfs_page *
@@ -95,7 +90,7 @@ nfs_create_request(struct file *file, struct inode *inode,
req->wb_pgbase = offset;
req->wb_bytes = count;
req->wb_inode = inode;
- req->wb_count = 1;
+ atomic_set(&req->wb_count, 1);
server->rpc_ops->request_init(req, file);
return req;
@@ -137,12 +132,8 @@ void nfs_clear_request(struct nfs_page *req)
void
nfs_release_request(struct nfs_page *req)
{
- spin_lock(&nfs_wreq_lock);
- if (--req->wb_count) {
- spin_unlock(&nfs_wreq_lock);
+ if (!atomic_dec_and_test(&req->wb_count))
return;
- }
- spin_unlock(&nfs_wreq_lock);
#ifdef NFS_PARANOIA
BUG_ON (!list_empty(&req->wb_list));
@@ -254,7 +245,7 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst,
* If the number of requests is set to 0, the entire address_space
* starting at index idx_start, is scanned.
* The requests are *not* checked to ensure that they form a contiguous set.
- * You must be holding the nfs_wreq_lock when calling this function
+ * You must be holding the inode's req_lock when calling this function
*/
int
nfs_scan_list(struct list_head *head, struct list_head *dst,
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index 8dc6a981c586..279ddad6ee4e 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -400,6 +400,8 @@ nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
};
int status;
+ if (path->len > NFS2_MAXPATHLEN)
+ return -ENAMETOOLONG;
dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
fattr->valid = 0;
status = rpc_call(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, 0);
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index a01a2fa0a598..fe99d21dd3dd 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -389,7 +389,7 @@ nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
nfs_begin_data_update(inode);
}
nfsi->npages++;
- req->wb_count++;
+ atomic_inc(&req->wb_count);
return 0;
}
@@ -399,21 +399,20 @@ nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
static void
nfs_inode_remove_request(struct nfs_page *req)
{
- struct nfs_inode *nfsi;
- struct inode *inode;
+ struct inode *inode = req->wb_inode;
+ struct nfs_inode *nfsi = NFS_I(inode);
BUG_ON (!NFS_WBACK_BUSY(req));
- spin_lock(&nfs_wreq_lock);
- inode = req->wb_inode;
- nfsi = NFS_I(inode);
+
+ spin_lock(&nfsi->req_lock);
radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index);
nfsi->npages--;
if (!nfsi->npages) {
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
nfs_end_data_update_defer(inode);
iput(inode);
} else
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
nfs_clear_request(req);
nfs_release_request(req);
}
@@ -429,7 +428,7 @@ _nfs_find_request(struct inode *inode, unsigned long index)
req = (struct nfs_page*)radix_tree_lookup(&nfsi->nfs_page_tree, index);
if (req)
- req->wb_count++;
+ atomic_inc(&req->wb_count);
return req;
}
@@ -437,10 +436,11 @@ static struct nfs_page *
nfs_find_request(struct inode *inode, unsigned long index)
{
struct nfs_page *req;
+ struct nfs_inode *nfsi = NFS_I(inode);
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
req = _nfs_find_request(inode, index);
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
return req;
}
@@ -453,10 +453,10 @@ nfs_mark_request_dirty(struct nfs_page *req)
struct inode *inode = req->wb_inode;
struct nfs_inode *nfsi = NFS_I(inode);
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
nfs_list_add_request(req, &nfsi->dirty);
nfsi->ndirty++;
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
inc_page_state(nr_dirty);
mark_inode_dirty(inode);
}
@@ -481,10 +481,10 @@ nfs_mark_request_commit(struct nfs_page *req)
struct inode *inode = req->wb_inode;
struct nfs_inode *nfsi = NFS_I(inode);
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
nfs_list_add_request(req, &nfsi->commit);
nfsi->ncommit++;
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
inc_page_state(nr_unstable);
mark_inode_dirty(inode);
}
@@ -509,7 +509,7 @@ nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int
else
idx_end = idx_start + npages - 1;
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
next = idx_start;
while (radix_tree_gang_lookup(&nfsi->nfs_page_tree, (void **)&req, next, 1)) {
if (req->wb_index > idx_end)
@@ -519,16 +519,16 @@ nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int
if (!NFS_WBACK_BUSY(req))
continue;
- req->wb_count++;
- spin_unlock(&nfs_wreq_lock);
+ atomic_inc(&req->wb_count);
+ spin_unlock(&nfsi->req_lock);
error = nfs_wait_on_request(req);
nfs_release_request(req);
if (error < 0)
return error;
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
res++;
}
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
return res;
}
@@ -624,6 +624,7 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page,
unsigned int offset, unsigned int bytes)
{
struct nfs_server *server = NFS_SERVER(inode);
+ struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_page *req, *new = NULL;
unsigned long rqend, end;
@@ -635,19 +636,19 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page,
/* Loop over all inode entries and see if we find
* A request for the page we wish to update
*/
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
req = _nfs_find_request(inode, page->index);
if (req) {
if (!nfs_lock_request_dontget(req)) {
int error;
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
error = nfs_wait_on_request(req);
nfs_release_request(req);
if (error < 0)
return ERR_PTR(error);
continue;
}
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
if (new)
nfs_release_request(new);
break;
@@ -658,15 +659,15 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page,
nfs_lock_request_dontget(new);
error = nfs_inode_add_request(inode, new);
if (error) {
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
nfs_unlock_request(new);
return ERR_PTR(error);
}
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
nfs_mark_request_dirty(new);
return new;
}
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
new = nfs_create_request(file, inode, page, offset, bytes);
if (IS_ERR(new))
@@ -1347,13 +1348,14 @@ nfs_commit_done(struct rpc_task *task)
int nfs_flush_inode(struct inode *inode, unsigned long idx_start,
unsigned int npages, int how)
{
+ struct nfs_inode *nfsi = NFS_I(inode);
LIST_HEAD(head);
int res,
error = 0;
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
res = nfs_scan_dirty(inode, &head, idx_start, npages);
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
if (res)
error = nfs_flush_list(&head, NFS_SERVER(inode)->wpages, how);
if (error < 0)
@@ -1365,18 +1367,19 @@ int nfs_flush_inode(struct inode *inode, unsigned long idx_start,
int nfs_commit_inode(struct inode *inode, unsigned long idx_start,
unsigned int npages, int how)
{
+ struct nfs_inode *nfsi = NFS_I(inode);
LIST_HEAD(head);
int res,
error = 0;
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
res = nfs_scan_commit(inode, &head, idx_start, npages);
if (res) {
res += nfs_scan_commit(inode, &head, 0, 0);
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
error = nfs_commit_list(&head, how);
} else
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
if (error < 0)
return error;
return res;
diff --git a/include/linux/nfs.h b/include/linux/nfs.h
index b30265f03e00..ca2ffa6ae1d5 100644
--- a/include/linux/nfs.h
+++ b/include/linux/nfs.h
@@ -8,6 +8,7 @@
#define _LINUX_NFS_H
#include <linux/sunrpc/msg_prot.h>
+#include <linux/string.h>
#define NFS_PROGRAM 100003
#define NFS_PORT 2049
@@ -139,6 +140,22 @@ struct nfs_fh {
};
/*
+ * Returns a zero iff the size and data fields match.
+ * Checks only "size" bytes in the data field.
+ */
+static inline int nfs_compare_fh(const struct nfs_fh *a, const struct nfs_fh *b)
+{
+ return a->size != b->size || memcmp(a->data, b->data, a->size) != 0;
+}
+
+static inline void nfs_copy_fh(struct nfs_fh *target, const struct nfs_fh *source)
+{
+ target->size = source->size;
+ memcpy(target->data, source->data, source->size);
+}
+
+
+/*
* This is really a general kernel constant, but since nothing like
* this is defined in the kernel headers, I have to do it here.
*/
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 8dec3e456d02..2b53e25f25f7 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -18,6 +18,7 @@
#define NFS4_VERIFIER_SIZE 8
#define NFS4_FHSIZE 128
+#define NFS4_MAXPATHLEN PATH_MAX
#define NFS4_MAXNAMLEN NAME_MAX
#define NFS4_ACCESS_READ 0x0001
@@ -372,6 +373,7 @@ enum {
NFSPROC4_CLNT_REMOVE,
NFSPROC4_CLNT_RENAME,
NFSPROC4_CLNT_LINK,
+ NFSPROC4_CLNT_SYMLINK,
NFSPROC4_CLNT_CREATE,
NFSPROC4_CLNT_PATHCONF,
NFSPROC4_CLNT_STATFS,
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 0fd3f817831e..1b9154d67afa 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -75,13 +75,12 @@
#ifdef __KERNEL__
/*
- * NFSv3 Access mode cache
+ * NFSv3/v4 Access mode cache entry
*/
-struct nfs_access_cache {
+struct nfs_access_entry {
unsigned long jiffies;
struct rpc_cred * cred;
int mask;
- int err;
};
/*
@@ -137,7 +136,7 @@ struct nfs_inode {
*/
atomic_t data_updates;
- struct nfs_access_cache cache_access;
+ struct nfs_access_entry cache_access;
/*
* This is the cookie verifier used for NFSv3 readdir
@@ -148,6 +147,7 @@ struct nfs_inode {
/*
* This is the list of dirty unwritten pages.
*/
+ spinlock_t req_lock;
struct list_head dirty;
struct list_head commit;
struct radix_tree_root nfs_page_tree;
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 428355f8aaf9..a43a38607982 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -18,6 +18,7 @@ struct nfs_server {
unsigned int rpages; /* read size (in pages) */
unsigned int wsize; /* write size */
unsigned int wpages; /* write size (in pages) */
+ unsigned int wtmult; /* server disk block size */
unsigned int dtsize; /* readdir size */
unsigned int bsize; /* server block size */
unsigned int acregmin; /* attr cache timeouts */
diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h
index 454587123f33..12a6758cc859 100644
--- a/include/linux/nfs_page.h
+++ b/include/linux/nfs_page.h
@@ -40,8 +40,8 @@ struct nfs_page {
unsigned long wb_index; /* Offset >> PAGE_CACHE_SHIFT */
unsigned int wb_offset, /* Offset & ~PAGE_CACHE_MASK */
wb_pgbase, /* Start of page data */
- wb_bytes, /* Length of request */
- wb_count; /* reference count */
+ wb_bytes; /* Length of request */
+ atomic_t wb_count; /* reference count */
unsigned long wb_flags;
struct nfs_writeverf wb_verf; /* Commit cookie */
};
@@ -65,8 +65,6 @@ extern int nfs_coalesce_requests(struct list_head *, struct list_head *,
unsigned int);
extern int nfs_wait_on_request(struct nfs_page *);
-extern spinlock_t nfs_wreq_lock;
-
/*
* Lock the page of an asynchronous request without incrementing the wb_count
*/
@@ -86,7 +84,7 @@ nfs_lock_request(struct nfs_page *req)
{
if (test_and_set_bit(PG_BUSY, &req->wb_flags))
return 0;
- req->wb_count++;
+ atomic_inc(&req->wb_count);
return 1;
}
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index f47e3c27af27..2c7f4617e650 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -657,6 +657,8 @@ struct nfs_write_data {
void (*complete) (struct nfs_write_data *, int);
};
+struct nfs_access_entry;
+
/*
* RPC procedure vector for NFSv2/NFSv3 demuxing
*/
@@ -672,7 +674,7 @@ struct nfs_rpc_ops {
struct iattr *);
int (*lookup) (struct inode *, struct qstr *,
struct nfs_fh *, struct nfs_fattr *);
- int (*access) (struct inode *, struct rpc_cred *, int);
+ int (*access) (struct inode *, struct nfs_access_entry *);
int (*readlink)(struct inode *, struct page *);
int (*read) (struct nfs_read_data *, struct file *);
int (*write) (struct nfs_write_data *, struct file *);
diff --git a/include/linux/sunrpc/gss_asn1.h b/include/linux/sunrpc/gss_asn1.h
index 7559244d46b9..e0e4e1dc5656 100644
--- a/include/linux/sunrpc/gss_asn1.h
+++ b/include/linux/sunrpc/gss_asn1.h
@@ -69,7 +69,6 @@ u32 g_verify_token_header(
struct xdr_netobj *mech,
int *body_size,
unsigned char **buf_in,
- int tok_type,
int toksize);
u32 g_get_mech_oid(struct xdr_netobj *mech, struct xdr_netobj * in_buf);
diff --git a/include/linux/sunrpc/gss_spkm3.h b/include/linux/sunrpc/gss_spkm3.h
new file mode 100644
index 000000000000..b5c9968c3c17
--- /dev/null
+++ b/include/linux/sunrpc/gss_spkm3.h
@@ -0,0 +1,61 @@
+/*
+ * linux/include/linux/sunrpc/gss_spkm3.h
+ *
+ * Copyright (c) 2000 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ */
+
+#include <linux/sunrpc/auth_gss.h>
+#include <linux/sunrpc/gss_err.h>
+#include <linux/sunrpc/gss_asn1.h>
+
+struct spkm3_ctx {
+ struct xdr_netobj ctx_id; /* per message context id */
+ int qop; /* negotiated qop */
+ struct xdr_netobj mech_used;
+ unsigned int ret_flags ;
+ unsigned int req_flags ;
+ struct xdr_netobj share_key;
+ int conf_alg;
+ struct crypto_tfm* derived_conf_key;
+ int intg_alg;
+ struct crypto_tfm* derived_integ_key;
+ int keyestb_alg; /* alg used to get share_key */
+ int owf_alg; /* one way function */
+};
+
+/* from openssl/objects.h */
+/* XXX need SEAL_ALG_NONE */
+#define NID_md5 4
+#define NID_dhKeyAgreement 28
+#define NID_des_cbc 31
+#define NID_sha1 64
+#define NID_cast5_cbc 108
+
+/* SPKM InnerContext Token types */
+
+#define SPKM_ERROR_TOK 3
+#define SPKM_MIC_TOK 4
+#define SPKM_WRAP_TOK 5
+#define SPKM_DEL_TOK 6
+
+u32 spkm3_make_token(struct spkm3_ctx *ctx, int qop_req, struct xdr_buf * text, struct xdr_netobj * token, int toktype);
+
+u32 spkm3_read_token(struct spkm3_ctx *ctx, struct xdr_netobj *read_token, struct xdr_buf *message_buffer, int *qop_state, int toktype);
+
+#define CKSUMTYPE_RSA_MD5 0x0007
+
+s32 make_checksum(s32 cksumtype, char *header, int hdrlen, struct xdr_buf *body,
+ struct xdr_netobj *cksum);
+void asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits);
+int decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen,
+ int explen);
+void spkm3_mic_header(unsigned char **hdrbuf, unsigned int *hdrlen,
+ unsigned char *ctxhdr, int elen, int zbit);
+void spkm3_make_mic_token(unsigned char **tokp, int toklen,
+ struct xdr_netobj *mic_hdr,
+ struct xdr_netobj *md5cksum, int md5elen, int md5zbit);
+u32 spkm3_verify_mic_token(unsigned char **tokp, int *mic_hdrlen,
+ unsigned char **cksum);
diff --git a/net/sunrpc/auth_gss/Makefile b/net/sunrpc/auth_gss/Makefile
index 9495e527864b..fe1b874084bc 100644
--- a/net/sunrpc/auth_gss/Makefile
+++ b/net/sunrpc/auth_gss/Makefile
@@ -12,3 +12,7 @@ obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o
rpcsec_gss_krb5-objs := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \
gss_krb5_seqnum.o
+obj-$(CONFIG_RPCSEC_GSS_SPKM3) += rpcsec_gss_spkm3.o
+
+rpcsec_gss_spkm3-objs := gss_spkm3_mech.o gss_spkm3_seal.o gss_spkm3_unseal.o \
+ gss_spkm3_token.o
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index e32f2a709e2d..cceea09bddc0 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -397,7 +397,7 @@ retry:
spin_unlock(&gss_auth->lock);
}
gss_release_msg(gss_msg);
- dprintk("RPC: %4u gss_upcall for uid %u result %d", task->tk_pid,
+ dprintk("RPC: %4u gss_upcall for uid %u result %d\n", task->tk_pid,
uid, res);
return res;
out_sleep:
diff --git a/net/sunrpc/auth_gss/gss_generic_token.c b/net/sunrpc/auth_gss/gss_generic_token.c
index d7b040809dd3..b0951d11522a 100644
--- a/net/sunrpc/auth_gss/gss_generic_token.c
+++ b/net/sunrpc/auth_gss/gss_generic_token.c
@@ -179,7 +179,7 @@ EXPORT_SYMBOL(g_make_token_header);
*/
u32
g_verify_token_header(struct xdr_netobj *mech, int *body_size,
- unsigned char **buf_in, int tok_type, int toksize)
+ unsigned char **buf_in, int toksize)
{
unsigned char *buf = *buf_in;
int seqsize;
diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c
index dcde40fe7427..8767fc53183d 100644
--- a/net/sunrpc/auth_gss/gss_krb5_unseal.c
+++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c
@@ -96,7 +96,7 @@ krb5_read_token(struct krb5_ctx *ctx,
dprintk("RPC: krb5_read_token\n");
- if (g_verify_token_header(&ctx->mech_used, &bodysize, &ptr, toktype,
+ if (g_verify_token_header(&ctx->mech_used, &bodysize, &ptr,
read_token->len))
goto out;
diff --git a/net/sunrpc/auth_gss/gss_spkm3_mech.c b/net/sunrpc/auth_gss/gss_spkm3_mech.c
new file mode 100644
index 000000000000..2887de1cae1c
--- /dev/null
+++ b/net/sunrpc/auth_gss/gss_spkm3_mech.c
@@ -0,0 +1,296 @@
+/*
+ * linux/net/sunrpc/gss_spkm3_mech.c
+ *
+ * Copyright (c) 2003 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ * J. Bruce Fields <bfields@umich.edu>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/sunrpc/auth.h>
+#include <linux/in.h>
+#include <linux/sunrpc/svcauth_gss.h>
+#include <linux/sunrpc/gss_spkm3.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/crypto.h>
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY RPCDBG_AUTH
+#endif
+
+struct xdr_netobj gss_mech_spkm3_oid =
+ {7, "\053\006\001\005\005\001\003"};
+
+static inline int
+get_bytes(char **ptr, const char *end, void *res, int len)
+{
+ char *p, *q;
+ p = *ptr;
+ q = p + len;
+ if (q > end || q < p)
+ return -1;
+ memcpy(res, p, len);
+ *ptr = q;
+ return 0;
+}
+
+static inline int
+get_netobj(char **ptr, const char *end, struct xdr_netobj *res)
+{
+ char *p, *q;
+ p = *ptr;
+ if (get_bytes(&p, end, &res->len, sizeof(res->len)))
+ return -1;
+ q = p + res->len;
+ if(res->len == 0)
+ goto out_nocopy;
+ if (q > end || q < p)
+ return -1;
+ if (!(res->data = kmalloc(res->len, GFP_KERNEL)))
+ return -1;
+ memcpy(res->data, p, res->len);
+out_nocopy:
+ *ptr = q;
+ return 0;
+}
+
+static inline int
+get_key(char **p, char *end, struct crypto_tfm **res, int *resalg)
+{
+ struct xdr_netobj key = {
+ .len = 0,
+ .data = NULL,
+ };
+ int alg_mode,setkey = 0;
+ char *alg_name;
+
+ if (get_bytes(p, end, resalg, sizeof(int)))
+ goto out_err;
+ if ((get_netobj(p, end, &key)))
+ goto out_err;
+
+ switch (*resalg) {
+ case NID_des_cbc:
+ alg_name = "des";
+ alg_mode = CRYPTO_TFM_MODE_CBC;
+ setkey = 1;
+ break;
+ case NID_md5:
+ if (key.len == 0) {
+ dprintk("RPC: SPKM3 get_key: NID_md5 zero Key length\n");
+ }
+ alg_name = "md5";
+ alg_mode = 0;
+ setkey = 0;
+ break;
+ case NID_cast5_cbc:
+ dprintk("RPC: SPKM3 get_key: case cast5_cbc, UNSUPPORTED \n");
+ goto out_err;
+ break;
+ default:
+ dprintk("RPC: SPKM3 get_key: unsupported algorithm %d", *resalg);
+ goto out_err_free_key;
+ }
+ if (!(*res = crypto_alloc_tfm(alg_name, alg_mode)))
+ goto out_err_free_key;
+ if (setkey) {
+ if (crypto_cipher_setkey(*res, key.data, key.len))
+ goto out_err_free_tfm;
+ }
+
+ if(key.len > 0)
+ kfree(key.data);
+ return 0;
+
+out_err_free_tfm:
+ crypto_free_tfm(*res);
+out_err_free_key:
+ if(key.len > 0)
+ kfree(key.data);
+out_err:
+ return -1;
+}
+
+static u32
+gss_import_sec_context_spkm3(struct xdr_netobj *inbuf,
+ struct gss_ctx *ctx_id)
+{
+ char *p = inbuf->data;
+ char *end = inbuf->data + inbuf->len;
+ struct spkm3_ctx *ctx;
+
+ if (!(ctx = kmalloc(sizeof(*ctx), GFP_KERNEL)))
+ goto out_err;
+ memset(ctx, 0, sizeof(*ctx));
+
+ if (get_netobj(&p, end, &ctx->ctx_id))
+ goto out_err_free_ctx;
+
+ if (get_bytes(&p, end, &ctx->qop, sizeof(ctx->qop)))
+ goto out_err_free_ctx_id;
+
+ if (get_netobj(&p, end, &ctx->mech_used))
+ goto out_err_free_mech;
+
+ if (get_bytes(&p, end, &ctx->ret_flags, sizeof(ctx->ret_flags)))
+ goto out_err_free_mech;
+
+ if (get_bytes(&p, end, &ctx->req_flags, sizeof(ctx->req_flags)))
+ goto out_err_free_mech;
+
+ if (get_netobj(&p, end, &ctx->share_key))
+ goto out_err_free_s_key;
+
+ if (get_key(&p, end, &ctx->derived_conf_key, &ctx->conf_alg)) {
+ dprintk("RPC: SPKM3 confidentiality key will be NULL\n");
+ }
+
+ if (get_key(&p, end, &ctx->derived_integ_key, &ctx->intg_alg)) {
+ dprintk("RPC: SPKM3 integrity key will be NULL\n");
+ }
+
+ if (get_bytes(&p, end, &ctx->owf_alg, sizeof(ctx->owf_alg)))
+ goto out_err_free_s_key;
+
+ if (get_bytes(&p, end, &ctx->owf_alg, sizeof(ctx->owf_alg)))
+ goto out_err_free_s_key;
+
+ if (p != end)
+ goto out_err_free_s_key;
+
+ ctx_id->internal_ctx_id = ctx;
+
+ dprintk("Succesfully imported new spkm context.\n");
+ return 0;
+
+out_err_free_s_key:
+ kfree(ctx->share_key.data);
+out_err_free_mech:
+ kfree(ctx->mech_used.data);
+out_err_free_ctx_id:
+ kfree(ctx->ctx_id.data);
+out_err_free_ctx:
+ kfree(ctx);
+out_err:
+ return GSS_S_FAILURE;
+}
+
+void
+gss_delete_sec_context_spkm3(void *internal_ctx) {
+ struct spkm3_ctx *sctx = internal_ctx;
+
+ if(sctx->derived_integ_key)
+ crypto_free_tfm(sctx->derived_integ_key);
+ if(sctx->derived_conf_key)
+ crypto_free_tfm(sctx->derived_conf_key);
+ if(sctx->share_key.data)
+ kfree(sctx->share_key.data);
+ if(sctx->mech_used.data)
+ kfree(sctx->mech_used.data);
+ kfree(sctx);
+}
+
+u32
+gss_verify_mic_spkm3(struct gss_ctx *ctx,
+ struct xdr_buf *signbuf,
+ struct xdr_netobj *checksum,
+ u32 *qstate) {
+ u32 maj_stat = 0;
+ int qop_state = 0;
+ struct spkm3_ctx *sctx = ctx->internal_ctx_id;
+
+ dprintk("RPC: gss_verify_mic_spkm3 calling spkm3_read_token\n");
+ maj_stat = spkm3_read_token(sctx, checksum, signbuf, &qop_state,
+ SPKM_MIC_TOK);
+
+ if (!maj_stat && qop_state)
+ *qstate = qop_state;
+
+ dprintk("RPC: gss_verify_mic_spkm3 returning %d\n", maj_stat);
+ return maj_stat;
+}
+
+u32
+gss_get_mic_spkm3(struct gss_ctx *ctx,
+ u32 qop,
+ struct xdr_buf *message_buffer,
+ struct xdr_netobj *message_token) {
+ u32 err = 0;
+ struct spkm3_ctx *sctx = ctx->internal_ctx_id;
+
+ dprintk("RPC: gss_get_mic_spkm3\n");
+
+ err = spkm3_make_token(sctx, qop, message_buffer,
+ message_token, SPKM_MIC_TOK);
+ return err;
+}
+
+static struct gss_api_ops gss_spkm3_ops = {
+ .gss_import_sec_context = gss_import_sec_context_spkm3,
+ .gss_get_mic = gss_get_mic_spkm3,
+ .gss_verify_mic = gss_verify_mic_spkm3,
+ .gss_delete_sec_context = gss_delete_sec_context_spkm3,
+};
+
+static struct pf_desc gss_spkm3_pfs[] = {
+ {RPC_AUTH_GSS_SPKM, 0, RPC_GSS_SVC_NONE, "spkm3"},
+ {RPC_AUTH_GSS_SPKMI, 0, RPC_GSS_SVC_INTEGRITY, "spkm3i"},
+};
+
+static struct gss_api_mech gss_spkm3_mech = {
+ .gm_name = "spkm3",
+ .gm_owner = THIS_MODULE,
+ .gm_ops = &gss_spkm3_ops,
+ .gm_pf_num = ARRAY_SIZE(gss_spkm3_pfs),
+ .gm_pfs = gss_spkm3_pfs,
+};
+
+static int __init init_spkm3_module(void)
+{
+ int status;
+
+ status = gss_mech_register(&gss_spkm3_mech);
+ if (status)
+ printk("Failed to register spkm3 gss mechanism!\n");
+ return 0;
+}
+
+static void __exit cleanup_spkm3_module(void)
+{
+ gss_mech_unregister(&gss_spkm3_mech);
+}
+
+MODULE_LICENSE("GPL");
+module_init(init_spkm3_module);
+module_exit(cleanup_spkm3_module);
diff --git a/net/sunrpc/auth_gss/gss_spkm3_seal.c b/net/sunrpc/auth_gss/gss_spkm3_seal.c
new file mode 100644
index 000000000000..289ed358b7e5
--- /dev/null
+++ b/net/sunrpc/auth_gss/gss_spkm3_seal.c
@@ -0,0 +1,132 @@
+/*
+ * linux/net/sunrpc/gss_spkm3_seal.c
+ *
+ * Copyright (c) 2003 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/sunrpc/gss_spkm3.h>
+#include <linux/random.h>
+#include <linux/crypto.h>
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY RPCDBG_AUTH
+#endif
+
+/*
+ * spkm3_make_token()
+ *
+ * Only SPKM_MIC_TOK with md5 intg-alg is supported
+ */
+
+u32
+spkm3_make_token(struct spkm3_ctx *ctx, int qop_req,
+ struct xdr_buf * text, struct xdr_netobj * token,
+ int toktype)
+{
+ s32 checksum_type;
+ char tokhdrbuf[25];
+ struct xdr_netobj md5cksum = {.len = 0, .data = NULL};
+ struct xdr_netobj mic_hdr = {.len = 0, .data = tokhdrbuf};
+ int tmsglen, tokenlen = 0;
+ unsigned char *ptr;
+ s32 now;
+ int ctxelen = 0, ctxzbit = 0;
+ int md5elen = 0, md5zbit = 0;
+
+ dprintk("RPC: spkm3_make_token\n");
+
+ now = jiffies;
+ if (qop_req != 0)
+ goto out_err;
+
+ if (ctx->ctx_id.len != 16) {
+ dprintk("RPC: spkm3_make_token BAD ctx_id.len %d\n",
+ ctx->ctx_id.len);
+ goto out_err;
+ }
+
+ switch (ctx->intg_alg) {
+ case NID_md5:
+ checksum_type = CKSUMTYPE_RSA_MD5;
+ break;
+ default:
+ dprintk("RPC: gss_spkm3_seal: ctx->signalg %d not"
+ " supported\n", ctx->intg_alg);
+ goto out_err;
+ }
+ /* XXX since we don't support WRAP, perhaps we don't care... */
+ if (ctx->conf_alg != NID_cast5_cbc) {
+ dprintk("RPC: gss_spkm3_seal: ctx->sealalg %d not supported\n",
+ ctx->conf_alg);
+ goto out_err;
+ }
+
+ if (toktype == SPKM_MIC_TOK) {
+ tmsglen = 0;
+ /* Calculate checksum over the mic-header */
+ asn1_bitstring_len(&ctx->ctx_id, &ctxelen, &ctxzbit);
+ spkm3_mic_header(&mic_hdr.data, &mic_hdr.len, ctx->ctx_id.data,
+ ctxelen, ctxzbit);
+
+ if (make_checksum(checksum_type, mic_hdr.data, mic_hdr.len,
+ text, &md5cksum))
+ goto out_err;
+
+ asn1_bitstring_len(&md5cksum, &md5elen, &md5zbit);
+ tokenlen = 10 + ctxelen + 1 + 2 + md5elen + 1;
+
+ /* Create token header using generic routines */
+ token->len = g_token_size(&ctx->mech_used, tokenlen + tmsglen);
+
+ ptr = token->data;
+ g_make_token_header(&ctx->mech_used, tokenlen + tmsglen, &ptr);
+
+ spkm3_make_mic_token(&ptr, tokenlen, &mic_hdr, &md5cksum, md5elen, md5zbit);
+ } else if (toktype == SPKM_WRAP_TOK) { /* Not Supported */
+ dprintk("RPC: gss_spkm3_seal: SPKM_WRAP_TOK not supported\n");
+ goto out_err;
+ }
+ kfree(md5cksum.data);
+
+ /* XXX need to implement sequence numbers, and ctx->expired */
+
+ return GSS_S_COMPLETE;
+out_err:
+ if (md5cksum.data)
+ kfree(md5cksum.data);
+ token->data = 0;
+ token->len = 0;
+ return GSS_S_FAILURE;
+}
diff --git a/net/sunrpc/auth_gss/gss_spkm3_token.c b/net/sunrpc/auth_gss/gss_spkm3_token.c
new file mode 100644
index 000000000000..46c08a0710f6
--- /dev/null
+++ b/net/sunrpc/auth_gss/gss_spkm3_token.c
@@ -0,0 +1,266 @@
+/*
+ * linux/net/sunrpc/gss_spkm3_token.c
+ *
+ * Copyright (c) 2003 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/sunrpc/gss_spkm3.h>
+#include <linux/random.h>
+#include <linux/crypto.h>
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY RPCDBG_AUTH
+#endif
+
+/*
+ * asn1_bitstring_len()
+ *
+ * calculate the asn1 bitstring length of the xdr_netobject
+ */
+void
+asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits)
+{
+ int i, zbit = 0,elen = in->len;
+ char *ptr;
+
+ ptr = &in->data[in->len -1];
+
+ /* count trailing 0's */
+ for(i = in->len; i > 0; i--) {
+ if (*ptr == 0) {
+ ptr--;
+ elen--;
+ } else
+ break;
+ }
+
+ /* count number of 0 bits in final octet */
+ ptr = &in->data[elen - 1];
+ for(i = 0; i < 8; i++) {
+ short mask = 0x01;
+
+ if (!((mask << i) & *ptr))
+ zbit++;
+ else
+ break;
+ }
+ *enclen = elen;
+ *zerobits = zbit;
+}
+
+/*
+ * decode_asn1_bitstring()
+ *
+ * decode a bitstring into a buffer of the expected length.
+ * enclen = bit string length
+ * explen = expected length (define in rfc)
+ */
+int
+decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen, int explen)
+{
+ if (!(out->data = kmalloc(explen,GFP_KERNEL)))
+ return 0;
+ out->len = explen;
+ memset(out->data, 0, explen);
+ memcpy(out->data, in, enclen);
+ return 1;
+}
+
+/*
+ * SPKMInnerContextToken choice SPKM_MIC asn1 token layout
+ *
+ * contextid is always 16 bytes plain data. max asn1 bitstring len = 17.
+ *
+ * tokenlen = pos[0] to end of token (max pos[45] with MD5 cksum)
+ *
+ * pos value
+ * ----------
+ * [0] a4 SPKM-MIC tag
+ * [1] ?? innertoken length (max 44)
+ *
+ *
+ * tok_hdr piece of checksum data starts here
+ *
+ * the maximum mic-header len = 9 + 17 = 26
+ * mic-header
+ * ----------
+ * [2] 30 SEQUENCE tag
+ * [3] ?? mic-header length: (max 23) = TokenID + ContextID
+ *
+ * TokenID - all fields constant and can be hardcoded
+ * -------
+ * [4] 02 Type 2
+ * [5] 02 Length 2
+ * [6][7] 01 01 TokenID (SPKM_MIC_TOK)
+ *
+ * ContextID - encoded length not constant, calculated
+ * ---------
+ * [8] 03 Type 3
+ * [9] ?? encoded length
+ * [10] ?? ctxzbit
+ * [11] contextid
+ *
+ * mic_header piece of checksum data ends here.
+ *
+ * int-cksum - encoded length not constant, calculated
+ * ---------
+ * [??] 03 Type 3
+ * [??] ?? encoded length
+ * [??] ?? md5zbit
+ * [??] int-cksum (NID_md5 = 16)
+ *
+ * maximum SPKM-MIC innercontext token length =
+ * 10 + encoded contextid_size(17 max) + 2 + encoded
+ * cksum_size (17 maxfor NID_md5) = 46
+ */
+
+/*
+ * spkm3_mic_header()
+ *
+ * Prepare the SPKM_MIC_TOK mic-header for check-sum calculation
+ * elen: 16 byte context id asn1 bitstring encoded length
+ */
+void
+spkm3_mic_header(unsigned char **hdrbuf, unsigned int *hdrlen, unsigned char *ctxdata, int elen, int zbit)
+{
+ char *hptr = *hdrbuf;
+ char *top = *hdrbuf;
+
+ *(u8 *)hptr++ = 0x30;
+ *(u8 *)hptr++ = elen + 7; /* on the wire header length */
+
+ /* tokenid */
+ *(u8 *)hptr++ = 0x02;
+ *(u8 *)hptr++ = 0x02;
+ *(u8 *)hptr++ = 0x01;
+ *(u8 *)hptr++ = 0x01;
+
+ /* coniextid */
+ *(u8 *)hptr++ = 0x03;
+ *(u8 *)hptr++ = elen + 1; /* add 1 to include zbit */
+ *(u8 *)hptr++ = zbit;
+ memcpy(hptr, ctxdata, elen);
+ hptr += elen;
+ *hdrlen = hptr - top;
+}
+
+/*
+ * spkm3_mic_innercontext_token()
+ *
+ * *tokp points to the beginning of the SPKM_MIC token described
+ * in rfc 2025, section 3.2.1:
+ *
+ */
+void
+spkm3_make_mic_token(unsigned char **tokp, int toklen, struct xdr_netobj *mic_hdr, struct xdr_netobj *md5cksum, int md5elen, int md5zbit)
+{
+ unsigned char *ict = *tokp;
+
+ *(u8 *)ict++ = 0xa4;
+ *(u8 *)ict++ = toklen - 2;
+ memcpy(ict, mic_hdr->data, mic_hdr->len);
+ ict += mic_hdr->len;
+
+ *(u8 *)ict++ = 0x03;
+ *(u8 *)ict++ = md5elen + 1; /* add 1 to include zbit */
+ *(u8 *)ict++ = md5zbit;
+ memcpy(ict, md5cksum->data, md5elen);
+}
+
+u32
+spkm3_verify_mic_token(unsigned char **tokp, int *mic_hdrlen, unsigned char **cksum)
+{
+ struct xdr_netobj spkm3_ctx_id = {.len =0, .data = NULL};
+ unsigned char *ptr = *tokp;
+ int ctxelen;
+ u32 ret = GSS_S_DEFECTIVE_TOKEN;
+
+ /* spkm3 innercontext token preamble */
+ if ((ptr[0] != 0xa4) || (ptr[2] != 0x30)) {
+ dprintk("RPC: BAD SPKM ictoken preamble\n");
+ goto out;
+ }
+
+ *mic_hdrlen = ptr[3];
+
+ /* token type */
+ if ((ptr[4] != 0x02) || (ptr[5] != 0x02)) {
+ dprintk("RPC: BAD asn1 SPKM3 token type\n");
+ goto out;
+ }
+
+ /* only support SPKM_MIC_TOK */
+ if((ptr[6] != 0x01) || (ptr[7] != 0x01)) {
+ dprintk("RPC: ERROR unsupported SPKM3 token \n");
+ goto out;
+ }
+
+ /* contextid */
+ if (ptr[8] != 0x03) {
+ dprintk("RPC: BAD SPKM3 asn1 context-id type\n");
+ goto out;
+ }
+
+ ctxelen = ptr[9];
+ if (ctxelen > 17) { /* length includes asn1 zbit octet */
+ dprintk("RPC: BAD SPKM3 contextid len %d\n", ctxelen);
+ goto out;
+ }
+
+ /* ignore ptr[10] */
+
+ if(!decode_asn1_bitstring(&spkm3_ctx_id, &ptr[11], ctxelen - 1, 16))
+ goto out;
+
+ /*
+ * in the current implementation: the optional int-alg is not present
+ * so the default int-alg (md5) is used the optional snd-seq field is
+ * also not present
+ */
+
+ if (*mic_hdrlen != 6 + ctxelen) {
+ dprintk("RPC: BAD SPKM_ MIC_TOK header len %d: we only support default int-alg (should be absent) and do not support snd-seq\n", *mic_hdrlen);
+ goto out;
+ }
+ /* checksum */
+ *cksum = (&ptr[10] + ctxelen); /* ctxelen includes ptr[10] */
+
+ ret = GSS_S_COMPLETE;
+out:
+ if (spkm3_ctx_id.data)
+ kfree(spkm3_ctx_id.data);
+ return ret;
+}
+
diff --git a/net/sunrpc/auth_gss/gss_spkm3_unseal.c b/net/sunrpc/auth_gss/gss_spkm3_unseal.c
new file mode 100644
index 000000000000..65ce81bf0bc4
--- /dev/null
+++ b/net/sunrpc/auth_gss/gss_spkm3_unseal.c
@@ -0,0 +1,128 @@
+/*
+ * linux/net/sunrpc/gss_spkm3_unseal.c
+ *
+ * Copyright (c) 2003 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/sunrpc/gss_spkm3.h>
+#include <linux/crypto.h>
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY RPCDBG_AUTH
+#endif
+
+/*
+ * spkm3_read_token()
+ *
+ * only SPKM_MIC_TOK with md5 intg-alg is supported
+ */
+u32
+spkm3_read_token(struct spkm3_ctx *ctx,
+ struct xdr_netobj *read_token, /* checksum */
+ struct xdr_buf *message_buffer, /* signbuf */
+ int *qop_state, int toktype)
+{
+ s32 code;
+ struct xdr_netobj wire_cksum = {.len =0, .data = NULL};
+ struct xdr_netobj md5cksum = {.len = 0, .data = NULL};
+ unsigned char *ptr = (unsigned char *)read_token->data;
+ unsigned char *cksum;
+ int bodysize, md5elen;
+ int mic_hdrlen;
+ u32 ret = GSS_S_DEFECTIVE_TOKEN;
+
+ dprintk("RPC: spkm3_read_token read_token->len %d\n", read_token->len);
+
+ if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used,
+ &bodysize, &ptr, read_token->len))
+ goto out;
+
+ /* decode the token */
+
+ if (toktype == SPKM_MIC_TOK) {
+
+ if ((ret = spkm3_verify_mic_token(&ptr, &mic_hdrlen, &cksum)))
+ goto out;
+
+ if (*cksum++ != 0x03) {
+ dprintk("RPC: spkm3_read_token BAD checksum type\n");
+ goto out;
+ }
+ md5elen = *cksum++;
+ cksum++; /* move past the zbit */
+
+ if(!decode_asn1_bitstring(&wire_cksum, cksum, md5elen - 1, 16))
+ goto out;
+
+ /* HARD CODED FOR MD5 */
+
+ /* compute the checksum of the message.
+ * ptr + 2 = start of header piece of checksum
+ * mic_hdrlen + 2 = length of header piece of checksum
+ */
+ ret = GSS_S_DEFECTIVE_TOKEN;
+ code = make_checksum(CKSUMTYPE_RSA_MD5, ptr + 2,
+ mic_hdrlen + 2,
+ message_buffer, &md5cksum);
+
+ if (code)
+ goto out;
+
+ dprintk("RPC: spkm3_read_token: digest wire_cksum.len %d:\n",
+ wire_cksum.len);
+ dprintk(" md5cksum.data\n");
+ print_hexl((u32 *) md5cksum.data, 16, 0);
+ dprintk(" cksum.data:\n");
+ print_hexl((u32 *) wire_cksum.data, wire_cksum.len, 0);
+
+ ret = GSS_S_BAD_SIG;
+ code = memcmp(md5cksum.data, wire_cksum.data, wire_cksum.len);
+ if (code)
+ goto out;
+
+ } else {
+ dprintk("RPC: BAD or UNSUPPORTED SPKM3 token type: %d\n",toktype);
+ goto out;
+ }
+
+ /* XXX: need to add expiration and sequencing */
+ ret = GSS_S_COMPLETE;
+out:
+ if (md5cksum.data)
+ kfree(md5cksum.data);
+ if (wire_cksum.data)
+ kfree(wire_cksum.data);
+ return ret;
+}
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index e0657790127b..4f4cdb904bf3 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -196,7 +196,15 @@ rpc_clone_client(struct rpc_clnt *clnt)
memcpy(new, clnt, sizeof(*new));
atomic_set(&new->cl_count, 1);
atomic_set(&new->cl_users, 0);
- atomic_inc(&new->cl_parent->cl_count);
+ new->cl_parent = clnt;
+ atomic_inc(&clnt->cl_count);
+ /* Duplicate portmapper */
+ rpc_init_wait_queue(&new->cl_pmap_default.pm_bindwait, "bindwait");
+ /* Turn off autobind on clones */
+ new->cl_autobind = 0;
+ new->cl_oneshot = 0;
+ new->cl_dead = 0;
+ rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval);
if (new->cl_auth)
atomic_inc(&new->cl_auth->au_count);
return new;
@@ -335,7 +343,7 @@ void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset)
*/
int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
{
- struct rpc_task my_task, *task = &my_task;
+ struct rpc_task *task;
sigset_t oldset;
int status;
@@ -343,15 +351,15 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
if (clnt->cl_dead)
return -EIO;
- if (flags & RPC_TASK_ASYNC) {
- printk("rpc_call_sync: Illegal flag combination for synchronous task\n");
- flags &= ~RPC_TASK_ASYNC;
- }
+ BUG_ON(flags & RPC_TASK_ASYNC);
rpc_clnt_sigmask(clnt, &oldset);
- /* Create/initialize a new RPC task */
- rpc_init_task(task, clnt, NULL, flags);
+ status = -ENOMEM;
+ task = rpc_new_task(clnt, NULL, flags);
+ if (task == NULL)
+ goto out;
+
rpc_call_setup(task, msg, 0);
/* Set up the call info struct and execute the task */
@@ -362,6 +370,7 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
rpc_release_task(task);
}
+out:
rpc_clnt_sigunmask(clnt, &oldset);
return status;
@@ -958,8 +967,12 @@ call_header(struct rpc_task *task)
static u32 *
call_verify(struct rpc_task *task)
{
- u32 *p = task->tk_rqstp->rq_rcv_buf.head[0].iov_base, n;
+ struct kvec *iov = &task->tk_rqstp->rq_rcv_buf.head[0];
+ int len = task->tk_rqstp->rq_rcv_buf.len >> 2;
+ u32 *p = iov->iov_base, n;
+ if ((len -= 3) < 0)
+ goto garbage;
p += 1; /* skip XID */
if ((n = ntohl(*p++)) != RPC_REPLY) {
@@ -969,9 +982,11 @@ call_verify(struct rpc_task *task)
if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
int error = -EACCES;
+ if (--len < 0)
+ goto garbage;
if ((n = ntohl(*p++)) != RPC_AUTH_ERROR) {
printk(KERN_WARNING "call_verify: RPC call rejected: %x\n", n);
- } else
+ } else if (--len < 0)
switch ((n = ntohl(*p++))) {
case RPC_AUTH_REJECTEDCRED:
case RPC_AUTH_REJECTEDVERF:
@@ -1002,7 +1017,8 @@ call_verify(struct rpc_task *task)
default:
printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n);
error = -EIO;
- }
+ } else
+ goto garbage;
dprintk("RPC: %4d call_verify: call rejected %d\n",
task->tk_pid, n);
rpc_exit(task, error);
@@ -1012,6 +1028,9 @@ call_verify(struct rpc_task *task)
printk(KERN_WARNING "call_verify: auth check failed\n");
goto garbage; /* bad verifier, retry */
}
+ len = p - (u32 *)iov->iov_base - 1;
+ if (len < 0)
+ goto garbage;
switch ((n = ntohl(*p++))) {
case RPC_SUCCESS:
return p;