diff options
Diffstat (limited to 'net/sunrpc/auth_gss/auth_gss.c')
| -rw-r--r-- | net/sunrpc/auth_gss/auth_gss.c | 900 | 
1 files changed, 646 insertions, 254 deletions
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 3835ce35e22..b6e440baccc 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -51,6 +51,9 @@  #include <linux/sunrpc/rpc_pipe_fs.h>  #include <linux/sunrpc/gss_api.h>  #include <asm/uaccess.h> +#include <linux/hashtable.h> + +#include "../netns.h"  static const struct rpc_authops authgss_ops; @@ -60,6 +63,9 @@ static const struct rpc_credops gss_nullops;  #define GSS_RETRY_EXPIRED 5  static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED; +#define GSS_KEY_EXPIRE_TIMEO 240 +static unsigned int gss_key_expire_timeo = GSS_KEY_EXPIRE_TIMEO; +  #ifdef RPC_DEBUG  # define RPCDBG_FACILITY	RPCDBG_AUTH  #endif @@ -69,27 +75,40 @@ static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED;   * using integrity (two 4-byte integers): */  #define GSS_VERF_SLACK		100 +static DEFINE_HASHTABLE(gss_auth_hash_table, 4); +static DEFINE_SPINLOCK(gss_auth_hash_lock); + +struct gss_pipe { +	struct rpc_pipe_dir_object pdo; +	struct rpc_pipe *pipe; +	struct rpc_clnt *clnt; +	const char *name; +	struct kref kref; +}; +  struct gss_auth {  	struct kref kref; +	struct hlist_node hash;  	struct rpc_auth rpc_auth;  	struct gss_api_mech *mech;  	enum rpc_gss_svc service;  	struct rpc_clnt *client; +	struct net *net;  	/*  	 * There are two upcall pipes; dentry[1], named "gssd", is used  	 * for the new text-based upcall; dentry[0] is named after the  	 * mechanism (for example, "krb5") and exists for  	 * backwards-compatibility with older gssd's.  	 */ -	struct dentry *dentry[2]; +	struct gss_pipe *gss_pipe[2]; +	const char *target_name;  };  /* pipe_version >= 0 if and only if someone has a pipe open. */ -static int pipe_version = -1; -static atomic_t pipe_users = ATOMIC_INIT(0);  static DEFINE_SPINLOCK(pipe_version_lock);  static struct rpc_wait_queue pipe_version_rpc_waitqueue;  static DECLARE_WAIT_QUEUE_HEAD(pipe_version_waitqueue); +static void gss_put_auth(struct gss_auth *gss_auth);  static void gss_free_ctx(struct gss_cl_ctx *);  static const struct rpc_pipe_ops gss_upcall_ops_v0; @@ -112,7 +131,7 @@ gss_put_ctx(struct gss_cl_ctx *ctx)  /* gss_cred_set_ctx:   * called by gss_upcall_callback and gss_create_upcall in order   * to set the gss context. The actual exchange of an old context - * and a new one is protected by the inode->i_lock. + * and a new one is protected by the pipe->lock.   */  static void  gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx) @@ -124,7 +143,7 @@ gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx)  	gss_get_ctx(ctx);  	rcu_assign_pointer(gss_cred->gc_ctx, ctx);  	set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); -	smp_mb__before_clear_bit(); +	smp_mb__before_atomic();  	clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags);  } @@ -192,17 +211,23 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct  	const void *q;  	unsigned int seclen;  	unsigned int timeout; +	unsigned long now = jiffies;  	u32 window_size;  	int ret; -	/* First unsigned int gives the lifetime (in seconds) of the cred */ +	/* First unsigned int gives the remaining lifetime in seconds of the +	 * credential - e.g. the remaining TGT lifetime for Kerberos or +	 * the -t value passed to GSSD. +	 */  	p = simple_get_bytes(p, end, &timeout, sizeof(timeout));  	if (IS_ERR(p))  		goto err;  	if (timeout == 0)  		timeout = GSSD_MIN_TIMEOUT; -	ctx->gc_expiry = jiffies + (unsigned long)timeout * HZ * 3 / 4; -	/* Sequence number window. Determines the maximum number of simultaneous requests */ +	ctx->gc_expiry = now + ((unsigned long)timeout * HZ); +	/* Sequence number window. Determines the maximum number of +	 * simultaneous requests +	 */  	p = simple_get_bytes(p, end, &window_size, sizeof(window_size));  	if (IS_ERR(p))  		goto err; @@ -232,14 +257,16 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct  		p = ERR_PTR(-EFAULT);  		goto err;  	} -	ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, GFP_NOFS); +	ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, NULL, GFP_NOFS);  	if (ret < 0) {  		p = ERR_PTR(ret);  		goto err;  	} +	dprintk("RPC:       %s Success. gc_expiry %lu now %lu timeout %u\n", +		__func__, ctx->gc_expiry, now, timeout);  	return q;  err: -	dprintk("RPC:       gss_fill_context returning %ld\n", -PTR_ERR(p)); +	dprintk("RPC:       %s returns error %ld\n", __func__, -PTR_ERR(p));  	return p;  } @@ -247,35 +274,38 @@ err:  struct gss_upcall_msg {  	atomic_t count; -	uid_t	uid; +	kuid_t	uid;  	struct rpc_pipe_msg msg;  	struct list_head list;  	struct gss_auth *auth; -	struct rpc_inode *inode; +	struct rpc_pipe *pipe;  	struct rpc_wait_queue rpc_waitqueue;  	wait_queue_head_t waitqueue;  	struct gss_cl_ctx *ctx;  	char databuf[UPCALL_BUF_LEN];  }; -static int get_pipe_version(void) +static int get_pipe_version(struct net *net)  { +	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);  	int ret;  	spin_lock(&pipe_version_lock); -	if (pipe_version >= 0) { -		atomic_inc(&pipe_users); -		ret = pipe_version; +	if (sn->pipe_version >= 0) { +		atomic_inc(&sn->pipe_users); +		ret = sn->pipe_version;  	} else  		ret = -EAGAIN;  	spin_unlock(&pipe_version_lock);  	return ret;  } -static void put_pipe_version(void) +static void put_pipe_version(struct net *net)  { -	if (atomic_dec_and_lock(&pipe_users, &pipe_version_lock)) { -		pipe_version = -1; +	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); + +	if (atomic_dec_and_lock(&sn->pipe_users, &pipe_version_lock)) { +		sn->pipe_version = -1;  		spin_unlock(&pipe_version_lock);  	}  } @@ -283,28 +313,30 @@ static void put_pipe_version(void)  static void  gss_release_msg(struct gss_upcall_msg *gss_msg)  { +	struct net *net = gss_msg->auth->net;  	if (!atomic_dec_and_test(&gss_msg->count))  		return; -	put_pipe_version(); +	put_pipe_version(net);  	BUG_ON(!list_empty(&gss_msg->list));  	if (gss_msg->ctx != NULL)  		gss_put_ctx(gss_msg->ctx);  	rpc_destroy_wait_queue(&gss_msg->rpc_waitqueue); +	gss_put_auth(gss_msg->auth);  	kfree(gss_msg);  }  static struct gss_upcall_msg * -__gss_find_upcall(struct rpc_inode *rpci, uid_t uid) +__gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid)  {  	struct gss_upcall_msg *pos; -	list_for_each_entry(pos, &rpci->in_downcall, list) { -		if (pos->uid != uid) +	list_for_each_entry(pos, &pipe->in_downcall, list) { +		if (!uid_eq(pos->uid, uid))  			continue;  		atomic_inc(&pos->count); -		dprintk("RPC:       gss_find_upcall found msg %p\n", pos); +		dprintk("RPC:       %s found msg %p\n", __func__, pos);  		return pos;  	} -	dprintk("RPC:       gss_find_upcall found nothing\n"); +	dprintk("RPC:       %s found nothing\n", __func__);  	return NULL;  } @@ -315,18 +347,17 @@ __gss_find_upcall(struct rpc_inode *rpci, uid_t uid)  static inline struct gss_upcall_msg *  gss_add_msg(struct gss_upcall_msg *gss_msg)  { -	struct rpc_inode *rpci = gss_msg->inode; -	struct inode *inode = &rpci->vfs_inode; +	struct rpc_pipe *pipe = gss_msg->pipe;  	struct gss_upcall_msg *old; -	spin_lock(&inode->i_lock); -	old = __gss_find_upcall(rpci, gss_msg->uid); +	spin_lock(&pipe->lock); +	old = __gss_find_upcall(pipe, gss_msg->uid);  	if (old == NULL) {  		atomic_inc(&gss_msg->count); -		list_add(&gss_msg->list, &rpci->in_downcall); +		list_add(&gss_msg->list, &pipe->in_downcall);  	} else  		gss_msg = old; -	spin_unlock(&inode->i_lock); +	spin_unlock(&pipe->lock);  	return gss_msg;  } @@ -342,14 +373,14 @@ __gss_unhash_msg(struct gss_upcall_msg *gss_msg)  static void  gss_unhash_msg(struct gss_upcall_msg *gss_msg)  { -	struct inode *inode = &gss_msg->inode->vfs_inode; +	struct rpc_pipe *pipe = gss_msg->pipe;  	if (list_empty(&gss_msg->list))  		return; -	spin_lock(&inode->i_lock); +	spin_lock(&pipe->lock);  	if (!list_empty(&gss_msg->list))  		__gss_unhash_msg(gss_msg); -	spin_unlock(&inode->i_lock); +	spin_unlock(&pipe->lock);  }  static void @@ -376,108 +407,125 @@ gss_upcall_callback(struct rpc_task *task)  	struct gss_cred *gss_cred = container_of(task->tk_rqstp->rq_cred,  			struct gss_cred, gc_base);  	struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; -	struct inode *inode = &gss_msg->inode->vfs_inode; +	struct rpc_pipe *pipe = gss_msg->pipe; -	spin_lock(&inode->i_lock); +	spin_lock(&pipe->lock);  	gss_handle_downcall_result(gss_cred, gss_msg); -	spin_unlock(&inode->i_lock); +	spin_unlock(&pipe->lock);  	task->tk_status = gss_msg->msg.errno;  	gss_release_msg(gss_msg);  }  static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg)  { -	gss_msg->msg.data = &gss_msg->uid; -	gss_msg->msg.len = sizeof(gss_msg->uid); +	uid_t uid = from_kuid(&init_user_ns, gss_msg->uid); +	memcpy(gss_msg->databuf, &uid, sizeof(uid)); +	gss_msg->msg.data = gss_msg->databuf; +	gss_msg->msg.len = sizeof(uid); + +	BUILD_BUG_ON(sizeof(uid) > sizeof(gss_msg->databuf));  } -static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, -				struct rpc_clnt *clnt, int machine_cred) +static int gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, +				const char *service_name, +				const char *target_name)  {  	struct gss_api_mech *mech = gss_msg->auth->mech;  	char *p = gss_msg->databuf; -	int len = 0; - -	gss_msg->msg.len = sprintf(gss_msg->databuf, "mech=%s uid=%d ", -				   mech->gm_name, -				   gss_msg->uid); -	p += gss_msg->msg.len; -	if (clnt->cl_principal) { -		len = sprintf(p, "target=%s ", clnt->cl_principal); +	size_t buflen = sizeof(gss_msg->databuf); +	int len; + +	len = scnprintf(p, buflen, "mech=%s uid=%d ", mech->gm_name, +			from_kuid(&init_user_ns, gss_msg->uid)); +	buflen -= len; +	p += len; +	gss_msg->msg.len = len; +	if (target_name) { +		len = scnprintf(p, buflen, "target=%s ", target_name); +		buflen -= len;  		p += len;  		gss_msg->msg.len += len;  	} -	if (machine_cred) { -		len = sprintf(p, "service=* "); -		p += len; -		gss_msg->msg.len += len; -	} else if (!strcmp(clnt->cl_program->name, "nfs4_cb")) { -		len = sprintf(p, "service=nfs "); +	if (service_name != NULL) { +		len = scnprintf(p, buflen, "service=%s ", service_name); +		buflen -= len;  		p += len;  		gss_msg->msg.len += len;  	}  	if (mech->gm_upcall_enctypes) { -		len = sprintf(p, mech->gm_upcall_enctypes); +		len = scnprintf(p, buflen, "enctypes=%s ", +				mech->gm_upcall_enctypes); +		buflen -= len;  		p += len;  		gss_msg->msg.len += len;  	} -	len = sprintf(p, "\n"); +	len = scnprintf(p, buflen, "\n"); +	if (len == 0) +		goto out_overflow;  	gss_msg->msg.len += len;  	gss_msg->msg.data = gss_msg->databuf; -	BUG_ON(gss_msg->msg.len > UPCALL_BUF_LEN); -} - -static void gss_encode_msg(struct gss_upcall_msg *gss_msg, -				struct rpc_clnt *clnt, int machine_cred) -{ -	if (pipe_version == 0) -		gss_encode_v0_msg(gss_msg); -	else /* pipe_version == 1 */ -		gss_encode_v1_msg(gss_msg, clnt, machine_cred); +	return 0; +out_overflow: +	WARN_ON_ONCE(1); +	return -ENOMEM;  } -static inline struct gss_upcall_msg * -gss_alloc_msg(struct gss_auth *gss_auth, uid_t uid, struct rpc_clnt *clnt, -		int machine_cred) +static struct gss_upcall_msg * +gss_alloc_msg(struct gss_auth *gss_auth, +		kuid_t uid, const char *service_name)  {  	struct gss_upcall_msg *gss_msg;  	int vers; +	int err = -ENOMEM;  	gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS);  	if (gss_msg == NULL) -		return ERR_PTR(-ENOMEM); -	vers = get_pipe_version(); -	if (vers < 0) { -		kfree(gss_msg); -		return ERR_PTR(vers); -	} -	gss_msg->inode = RPC_I(gss_auth->dentry[vers]->d_inode); +		goto err; +	vers = get_pipe_version(gss_auth->net); +	err = vers; +	if (err < 0) +		goto err_free_msg; +	gss_msg->pipe = gss_auth->gss_pipe[vers]->pipe;  	INIT_LIST_HEAD(&gss_msg->list);  	rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");  	init_waitqueue_head(&gss_msg->waitqueue);  	atomic_set(&gss_msg->count, 1);  	gss_msg->uid = uid;  	gss_msg->auth = gss_auth; -	gss_encode_msg(gss_msg, clnt, machine_cred); +	switch (vers) { +	case 0: +		gss_encode_v0_msg(gss_msg); +		break; +	default: +		err = gss_encode_v1_msg(gss_msg, service_name, gss_auth->target_name); +		if (err) +			goto err_put_pipe_version; +	}; +	kref_get(&gss_auth->kref);  	return gss_msg; +err_put_pipe_version: +	put_pipe_version(gss_auth->net); +err_free_msg: +	kfree(gss_msg); +err: +	return ERR_PTR(err);  }  static struct gss_upcall_msg * -gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cred *cred) +gss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred)  {  	struct gss_cred *gss_cred = container_of(cred,  			struct gss_cred, gc_base);  	struct gss_upcall_msg *gss_new, *gss_msg; -	uid_t uid = cred->cr_uid; +	kuid_t uid = cred->cr_uid; -	gss_new = gss_alloc_msg(gss_auth, uid, clnt, gss_cred->gc_machine_cred); +	gss_new = gss_alloc_msg(gss_auth, uid, gss_cred->gc_principal);  	if (IS_ERR(gss_new))  		return gss_new;  	gss_msg = gss_add_msg(gss_new);  	if (gss_msg == gss_new) { -		struct inode *inode = &gss_new->inode->vfs_inode; -		int res = rpc_queue_upcall(inode, &gss_new->msg); +		int res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg);  		if (res) {  			gss_unhash_msg(gss_new);  			gss_msg = ERR_PTR(res); @@ -489,14 +537,7 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr  static void warn_gssd(void)  { -	static unsigned long ratelimit; -	unsigned long now = jiffies; - -	if (time_after(now, ratelimit)) { -		printk(KERN_WARNING "RPC: AUTH_GSS upcall timed out.\n" -				"Please check user daemon is running.\n"); -		ratelimit = now + 15*HZ; -	} +	dprintk("AUTH_GSS upcall failed. Please check user daemon is running.\n");  }  static inline int @@ -508,26 +549,26 @@ gss_refresh_upcall(struct rpc_task *task)  	struct gss_cred *gss_cred = container_of(cred,  			struct gss_cred, gc_base);  	struct gss_upcall_msg *gss_msg; -	struct inode *inode; +	struct rpc_pipe *pipe;  	int err = 0; -	dprintk("RPC: %5u gss_refresh_upcall for uid %u\n", task->tk_pid, -								cred->cr_uid); -	gss_msg = gss_setup_upcall(task->tk_client, gss_auth, cred); +	dprintk("RPC: %5u %s for uid %u\n", +		task->tk_pid, __func__, from_kuid(&init_user_ns, cred->cr_uid)); +	gss_msg = gss_setup_upcall(gss_auth, cred);  	if (PTR_ERR(gss_msg) == -EAGAIN) {  		/* XXX: warning on the first, under the assumption we  		 * shouldn't normally hit this case on a refresh. */  		warn_gssd();  		task->tk_timeout = 15*HZ;  		rpc_sleep_on(&pipe_version_rpc_waitqueue, task, NULL); -		return 0; +		return -EAGAIN;  	}  	if (IS_ERR(gss_msg)) {  		err = PTR_ERR(gss_msg);  		goto out;  	} -	inode = &gss_msg->inode->vfs_inode; -	spin_lock(&inode->i_lock); +	pipe = gss_msg->pipe; +	spin_lock(&pipe->lock);  	if (gss_cred->gc_upcall != NULL)  		rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL);  	else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) { @@ -540,48 +581,60 @@ gss_refresh_upcall(struct rpc_task *task)  		gss_handle_downcall_result(gss_cred, gss_msg);  		err = gss_msg->msg.errno;  	} -	spin_unlock(&inode->i_lock); +	spin_unlock(&pipe->lock);  	gss_release_msg(gss_msg);  out: -	dprintk("RPC: %5u gss_refresh_upcall for uid %u result %d\n", -			task->tk_pid, cred->cr_uid, err); +	dprintk("RPC: %5u %s for uid %u result %d\n", +		task->tk_pid, __func__, +		from_kuid(&init_user_ns, cred->cr_uid),	err);  	return err;  }  static inline int  gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)  { -	struct inode *inode; +	struct net *net = gss_auth->net; +	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); +	struct rpc_pipe *pipe;  	struct rpc_cred *cred = &gss_cred->gc_base;  	struct gss_upcall_msg *gss_msg;  	DEFINE_WAIT(wait); -	int err = 0; +	int err; -	dprintk("RPC:       gss_upcall for uid %u\n", cred->cr_uid); +	dprintk("RPC:       %s for uid %u\n", +		__func__, from_kuid(&init_user_ns, cred->cr_uid));  retry: -	gss_msg = gss_setup_upcall(gss_auth->client, gss_auth, cred); +	err = 0; +	/* if gssd is down, just skip upcalling altogether */ +	if (!gssd_running(net)) { +		warn_gssd(); +		return -EACCES; +	} +	gss_msg = gss_setup_upcall(gss_auth, cred);  	if (PTR_ERR(gss_msg) == -EAGAIN) {  		err = wait_event_interruptible_timeout(pipe_version_waitqueue, -				pipe_version >= 0, 15*HZ); -		if (err) -			goto out; -		if (pipe_version < 0) +				sn->pipe_version >= 0, 15 * HZ); +		if (sn->pipe_version < 0) {  			warn_gssd(); +			err = -EACCES; +		} +		if (err < 0) +			goto out;  		goto retry;  	}  	if (IS_ERR(gss_msg)) {  		err = PTR_ERR(gss_msg);  		goto out;  	} -	inode = &gss_msg->inode->vfs_inode; +	pipe = gss_msg->pipe;  	for (;;) { -		prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_INTERRUPTIBLE); -		spin_lock(&inode->i_lock); +		prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_KILLABLE); +		spin_lock(&pipe->lock);  		if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) {  			break;  		} -		spin_unlock(&inode->i_lock); -		if (signalled()) { +		spin_unlock(&pipe->lock); +		if (fatal_signal_pending(current)) {  			err = -ERESTARTSYS;  			goto out_intr;  		} @@ -591,36 +644,16 @@ retry:  		gss_cred_set_ctx(cred, gss_msg->ctx);  	else  		err = gss_msg->msg.errno; -	spin_unlock(&inode->i_lock); +	spin_unlock(&pipe->lock);  out_intr:  	finish_wait(&gss_msg->waitqueue, &wait);  	gss_release_msg(gss_msg);  out: -	dprintk("RPC:       gss_create_upcall for uid %u result %d\n", -			cred->cr_uid, err); +	dprintk("RPC:       %s for uid %u result %d\n", +		__func__, from_kuid(&init_user_ns, cred->cr_uid), err);  	return err;  } -static ssize_t -gss_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg, -		char __user *dst, size_t buflen) -{ -	char *data = (char *)msg->data + msg->copied; -	size_t mlen = min(msg->len, buflen); -	unsigned long left; - -	left = copy_to_user(dst, data, mlen); -	if (left == mlen) { -		msg->errno = -EFAULT; -		return -EFAULT; -	} - -	mlen -= left; -	msg->copied += mlen; -	msg->errno = 0; -	return mlen; -} -  #define MSG_BUF_MAXSIZE 1024  static ssize_t @@ -629,9 +662,10 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)  	const void *p, *end;  	void *buf;  	struct gss_upcall_msg *gss_msg; -	struct inode *inode = filp->f_path.dentry->d_inode; +	struct rpc_pipe *pipe = RPC_I(file_inode(filp))->pipe;  	struct gss_cl_ctx *ctx; -	uid_t uid; +	uid_t id; +	kuid_t uid;  	ssize_t err = -EFBIG;  	if (mlen > MSG_BUF_MAXSIZE) @@ -646,12 +680,18 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)  		goto err;  	end = (const void *)((char *)buf + mlen); -	p = simple_get_bytes(buf, end, &uid, sizeof(uid)); +	p = simple_get_bytes(buf, end, &id, sizeof(id));  	if (IS_ERR(p)) {  		err = PTR_ERR(p);  		goto err;  	} +	uid = make_kuid(&init_user_ns, id); +	if (!uid_valid(uid)) { +		err = -EINVAL; +		goto err; +	} +  	err = -ENOMEM;  	ctx = gss_alloc_context();  	if (ctx == NULL) @@ -659,14 +699,14 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)  	err = -ENOENT;  	/* Find a matching upcall */ -	spin_lock(&inode->i_lock); -	gss_msg = __gss_find_upcall(RPC_I(inode), uid); +	spin_lock(&pipe->lock); +	gss_msg = __gss_find_upcall(pipe, uid);  	if (gss_msg == NULL) { -		spin_unlock(&inode->i_lock); +		spin_unlock(&pipe->lock);  		goto err_put_ctx;  	}  	list_del_init(&gss_msg->list); -	spin_unlock(&inode->i_lock); +	spin_unlock(&pipe->lock);  	p = gss_fill_context(p, end, ctx, gss_msg->auth->mech);  	if (IS_ERR(p)) { @@ -694,35 +734,37 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)  	err = mlen;  err_release_msg: -	spin_lock(&inode->i_lock); +	spin_lock(&pipe->lock);  	__gss_unhash_msg(gss_msg); -	spin_unlock(&inode->i_lock); +	spin_unlock(&pipe->lock);  	gss_release_msg(gss_msg);  err_put_ctx:  	gss_put_ctx(ctx);  err:  	kfree(buf);  out: -	dprintk("RPC:       gss_pipe_downcall returning %Zd\n", err); +	dprintk("RPC:       %s returning %Zd\n", __func__, err);  	return err;  }  static int gss_pipe_open(struct inode *inode, int new_version)  { +	struct net *net = inode->i_sb->s_fs_info; +	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);  	int ret = 0;  	spin_lock(&pipe_version_lock); -	if (pipe_version < 0) { +	if (sn->pipe_version < 0) {  		/* First open of any gss pipe determines the version: */ -		pipe_version = new_version; +		sn->pipe_version = new_version;  		rpc_wake_up(&pipe_version_rpc_waitqueue);  		wake_up(&pipe_version_waitqueue); -	} else if (pipe_version != new_version) { +	} else if (sn->pipe_version != new_version) {  		/* Trying to open a pipe of a different version */  		ret = -EBUSY;  		goto out;  	} -	atomic_inc(&pipe_users); +	atomic_inc(&sn->pipe_users);  out:  	spin_unlock(&pipe_version_lock);  	return ret; @@ -742,25 +784,26 @@ static int gss_pipe_open_v1(struct inode *inode)  static void  gss_pipe_release(struct inode *inode)  { -	struct rpc_inode *rpci = RPC_I(inode); +	struct net *net = inode->i_sb->s_fs_info; +	struct rpc_pipe *pipe = RPC_I(inode)->pipe;  	struct gss_upcall_msg *gss_msg;  restart: -	spin_lock(&inode->i_lock); -	list_for_each_entry(gss_msg, &rpci->in_downcall, list) { +	spin_lock(&pipe->lock); +	list_for_each_entry(gss_msg, &pipe->in_downcall, list) {  		if (!list_empty(&gss_msg->msg.list))  			continue;  		gss_msg->msg.errno = -EPIPE;  		atomic_inc(&gss_msg->count);  		__gss_unhash_msg(gss_msg); -		spin_unlock(&inode->i_lock); +		spin_unlock(&pipe->lock);  		gss_release_msg(gss_msg);  		goto restart;  	} -	spin_unlock(&inode->i_lock); +	spin_unlock(&pipe->lock); -	put_pipe_version(); +	put_pipe_version(net);  }  static void @@ -769,8 +812,8 @@ gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)  	struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg);  	if (msg->errno < 0) { -		dprintk("RPC:       gss_pipe_destroy_msg releasing msg %p\n", -				gss_msg); +		dprintk("RPC:       %s releasing msg %p\n", +			__func__, gss_msg);  		atomic_inc(&gss_msg->count);  		gss_unhash_msg(gss_msg);  		if (msg->errno == -ETIMEDOUT) @@ -779,14 +822,153 @@ gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)  	}  } +static void gss_pipe_dentry_destroy(struct dentry *dir, +		struct rpc_pipe_dir_object *pdo) +{ +	struct gss_pipe *gss_pipe = pdo->pdo_data; +	struct rpc_pipe *pipe = gss_pipe->pipe; + +	if (pipe->dentry != NULL) { +		rpc_unlink(pipe->dentry); +		pipe->dentry = NULL; +	} +} + +static int gss_pipe_dentry_create(struct dentry *dir, +		struct rpc_pipe_dir_object *pdo) +{ +	struct gss_pipe *p = pdo->pdo_data; +	struct dentry *dentry; + +	dentry = rpc_mkpipe_dentry(dir, p->name, p->clnt, p->pipe); +	if (IS_ERR(dentry)) +		return PTR_ERR(dentry); +	p->pipe->dentry = dentry; +	return 0; +} + +static const struct rpc_pipe_dir_object_ops gss_pipe_dir_object_ops = { +	.create = gss_pipe_dentry_create, +	.destroy = gss_pipe_dentry_destroy, +}; + +static struct gss_pipe *gss_pipe_alloc(struct rpc_clnt *clnt, +		const char *name, +		const struct rpc_pipe_ops *upcall_ops) +{ +	struct gss_pipe *p; +	int err = -ENOMEM; + +	p = kmalloc(sizeof(*p), GFP_KERNEL); +	if (p == NULL) +		goto err; +	p->pipe = rpc_mkpipe_data(upcall_ops, RPC_PIPE_WAIT_FOR_OPEN); +	if (IS_ERR(p->pipe)) { +		err = PTR_ERR(p->pipe); +		goto err_free_gss_pipe; +	} +	p->name = name; +	p->clnt = clnt; +	kref_init(&p->kref); +	rpc_init_pipe_dir_object(&p->pdo, +			&gss_pipe_dir_object_ops, +			p); +	return p; +err_free_gss_pipe: +	kfree(p); +err: +	return ERR_PTR(err); +} + +struct gss_alloc_pdo { +	struct rpc_clnt *clnt; +	const char *name; +	const struct rpc_pipe_ops *upcall_ops; +}; + +static int gss_pipe_match_pdo(struct rpc_pipe_dir_object *pdo, void *data) +{ +	struct gss_pipe *gss_pipe; +	struct gss_alloc_pdo *args = data; + +	if (pdo->pdo_ops != &gss_pipe_dir_object_ops) +		return 0; +	gss_pipe = container_of(pdo, struct gss_pipe, pdo); +	if (strcmp(gss_pipe->name, args->name) != 0) +		return 0; +	if (!kref_get_unless_zero(&gss_pipe->kref)) +		return 0; +	return 1; +} + +static struct rpc_pipe_dir_object *gss_pipe_alloc_pdo(void *data) +{ +	struct gss_pipe *gss_pipe; +	struct gss_alloc_pdo *args = data; + +	gss_pipe = gss_pipe_alloc(args->clnt, args->name, args->upcall_ops); +	if (!IS_ERR(gss_pipe)) +		return &gss_pipe->pdo; +	return NULL; +} + +static struct gss_pipe *gss_pipe_get(struct rpc_clnt *clnt, +		const char *name, +		const struct rpc_pipe_ops *upcall_ops) +{ +	struct net *net = rpc_net_ns(clnt); +	struct rpc_pipe_dir_object *pdo; +	struct gss_alloc_pdo args = { +		.clnt = clnt, +		.name = name, +		.upcall_ops = upcall_ops, +	}; + +	pdo = rpc_find_or_alloc_pipe_dir_object(net, +			&clnt->cl_pipedir_objects, +			gss_pipe_match_pdo, +			gss_pipe_alloc_pdo, +			&args); +	if (pdo != NULL) +		return container_of(pdo, struct gss_pipe, pdo); +	return ERR_PTR(-ENOMEM); +} + +static void __gss_pipe_free(struct gss_pipe *p) +{ +	struct rpc_clnt *clnt = p->clnt; +	struct net *net = rpc_net_ns(clnt); + +	rpc_remove_pipe_dir_object(net, +			&clnt->cl_pipedir_objects, +			&p->pdo); +	rpc_destroy_pipe_data(p->pipe); +	kfree(p); +} + +static void __gss_pipe_release(struct kref *kref) +{ +	struct gss_pipe *p = container_of(kref, struct gss_pipe, kref); + +	__gss_pipe_free(p); +} + +static void gss_pipe_free(struct gss_pipe *p) +{ +	if (p != NULL) +		kref_put(&p->kref, __gss_pipe_release); +} +  /*   * NOTE: we have the opportunity to use different   * parameters based on the input flavor (which must be a pseudoflavor)   */ -static struct rpc_auth * -gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) +static struct gss_auth * +gss_create_new(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)  { +	rpc_authflavor_t flavor = args->pseudoflavor;  	struct gss_auth *gss_auth; +	struct gss_pipe *gss_pipe;  	struct rpc_auth * auth;  	int err = -ENOMEM; /* XXX? */ @@ -796,17 +978,26 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)  		return ERR_PTR(err);  	if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL)))  		goto out_dec; +	INIT_HLIST_NODE(&gss_auth->hash); +	gss_auth->target_name = NULL; +	if (args->target_name) { +		gss_auth->target_name = kstrdup(args->target_name, GFP_KERNEL); +		if (gss_auth->target_name == NULL) +			goto err_free; +	}  	gss_auth->client = clnt; +	gss_auth->net = get_net(rpc_net_ns(clnt));  	err = -EINVAL;  	gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor);  	if (!gss_auth->mech) { -		printk(KERN_WARNING "%s: Pseudoflavor %d not found!\n", -				__func__, flavor); -		goto err_free; +		dprintk("RPC:       Pseudoflavor %d not found!\n", flavor); +		goto err_put_net;  	}  	gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor);  	if (gss_auth->service == 0)  		goto err_put_mech; +	if (!gssd_running(gss_auth->net)) +		goto err_put_mech;  	auth = &gss_auth->rpc_auth;  	auth->au_cslack = GSS_CRED_SLACK >> 2;  	auth->au_rslack = GSS_VERF_SLACK >> 2; @@ -815,41 +1006,41 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)  	atomic_set(&auth->au_count, 1);  	kref_init(&gss_auth->kref); +	err = rpcauth_init_credcache(auth); +	if (err) +		goto err_put_mech;  	/*  	 * Note: if we created the old pipe first, then someone who  	 * examined the directory at the right moment might conclude  	 * that we supported only the old pipe.  So we instead create  	 * the new pipe first.  	 */ -	gss_auth->dentry[1] = rpc_mkpipe(clnt->cl_path.dentry, -					 "gssd", -					 clnt, &gss_upcall_ops_v1, -					 RPC_PIPE_WAIT_FOR_OPEN); -	if (IS_ERR(gss_auth->dentry[1])) { -		err = PTR_ERR(gss_auth->dentry[1]); -		goto err_put_mech; +	gss_pipe = gss_pipe_get(clnt, "gssd", &gss_upcall_ops_v1); +	if (IS_ERR(gss_pipe)) { +		err = PTR_ERR(gss_pipe); +		goto err_destroy_credcache;  	} +	gss_auth->gss_pipe[1] = gss_pipe; -	gss_auth->dentry[0] = rpc_mkpipe(clnt->cl_path.dentry, -					 gss_auth->mech->gm_name, -					 clnt, &gss_upcall_ops_v0, -					 RPC_PIPE_WAIT_FOR_OPEN); -	if (IS_ERR(gss_auth->dentry[0])) { -		err = PTR_ERR(gss_auth->dentry[0]); -		goto err_unlink_pipe_1; +	gss_pipe = gss_pipe_get(clnt, gss_auth->mech->gm_name, +			&gss_upcall_ops_v0); +	if (IS_ERR(gss_pipe)) { +		err = PTR_ERR(gss_pipe); +		goto err_destroy_pipe_1;  	} -	err = rpcauth_init_credcache(auth); -	if (err) -		goto err_unlink_pipe_0; +	gss_auth->gss_pipe[0] = gss_pipe; -	return auth; -err_unlink_pipe_0: -	rpc_unlink(gss_auth->dentry[0]); -err_unlink_pipe_1: -	rpc_unlink(gss_auth->dentry[1]); +	return gss_auth; +err_destroy_pipe_1: +	gss_pipe_free(gss_auth->gss_pipe[1]); +err_destroy_credcache: +	rpcauth_destroy_credcache(auth);  err_put_mech:  	gss_mech_put(gss_auth->mech); +err_put_net: +	put_net(gss_auth->net);  err_free: +	kfree(gss_auth->target_name);  	kfree(gss_auth);  out_dec:  	module_put(THIS_MODULE); @@ -859,9 +1050,11 @@ out_dec:  static void  gss_free(struct gss_auth *gss_auth)  { -	rpc_unlink(gss_auth->dentry[1]); -	rpc_unlink(gss_auth->dentry[0]); +	gss_pipe_free(gss_auth->gss_pipe[0]); +	gss_pipe_free(gss_auth->gss_pipe[1]);  	gss_mech_put(gss_auth->mech); +	put_net(gss_auth->net); +	kfree(gss_auth->target_name);  	kfree(gss_auth);  	module_put(THIS_MODULE); @@ -876,17 +1069,118 @@ gss_free_callback(struct kref *kref)  }  static void +gss_put_auth(struct gss_auth *gss_auth) +{ +	kref_put(&gss_auth->kref, gss_free_callback); +} + +static void  gss_destroy(struct rpc_auth *auth)  { -	struct gss_auth *gss_auth; +	struct gss_auth *gss_auth = container_of(auth, +			struct gss_auth, rpc_auth);  	dprintk("RPC:       destroying GSS authenticator %p flavor %d\n",  			auth, auth->au_flavor); +	if (hash_hashed(&gss_auth->hash)) { +		spin_lock(&gss_auth_hash_lock); +		hash_del(&gss_auth->hash); +		spin_unlock(&gss_auth_hash_lock); +	} + +	gss_pipe_free(gss_auth->gss_pipe[0]); +	gss_auth->gss_pipe[0] = NULL; +	gss_pipe_free(gss_auth->gss_pipe[1]); +	gss_auth->gss_pipe[1] = NULL;  	rpcauth_destroy_credcache(auth); -	gss_auth = container_of(auth, struct gss_auth, rpc_auth); -	kref_put(&gss_auth->kref, gss_free_callback); +	gss_put_auth(gss_auth); +} + +/* + * Auths may be shared between rpc clients that were cloned from a + * common client with the same xprt, if they also share the flavor and + * target_name. + * + * The auth is looked up from the oldest parent sharing the same + * cl_xprt, and the auth itself references only that common parent + * (which is guaranteed to last as long as any of its descendants). + */ +static struct gss_auth * +gss_auth_find_or_add_hashed(struct rpc_auth_create_args *args, +		struct rpc_clnt *clnt, +		struct gss_auth *new) +{ +	struct gss_auth *gss_auth; +	unsigned long hashval = (unsigned long)clnt; + +	spin_lock(&gss_auth_hash_lock); +	hash_for_each_possible(gss_auth_hash_table, +			gss_auth, +			hash, +			hashval) { +		if (gss_auth->client != clnt) +			continue; +		if (gss_auth->rpc_auth.au_flavor != args->pseudoflavor) +			continue; +		if (gss_auth->target_name != args->target_name) { +			if (gss_auth->target_name == NULL) +				continue; +			if (args->target_name == NULL) +				continue; +			if (strcmp(gss_auth->target_name, args->target_name)) +				continue; +		} +		if (!atomic_inc_not_zero(&gss_auth->rpc_auth.au_count)) +			continue; +		goto out; +	} +	if (new) +		hash_add(gss_auth_hash_table, &new->hash, hashval); +	gss_auth = new; +out: +	spin_unlock(&gss_auth_hash_lock); +	return gss_auth; +} + +static struct gss_auth * +gss_create_hashed(struct rpc_auth_create_args *args, struct rpc_clnt *clnt) +{ +	struct gss_auth *gss_auth; +	struct gss_auth *new; + +	gss_auth = gss_auth_find_or_add_hashed(args, clnt, NULL); +	if (gss_auth != NULL) +		goto out; +	new = gss_create_new(args, clnt); +	if (IS_ERR(new)) +		return new; +	gss_auth = gss_auth_find_or_add_hashed(args, clnt, new); +	if (gss_auth != new) +		gss_destroy(&new->rpc_auth); +out: +	return gss_auth; +} + +static struct rpc_auth * +gss_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt) +{ +	struct gss_auth *gss_auth; +	struct rpc_xprt *xprt = rcu_access_pointer(clnt->cl_xprt); + +	while (clnt != clnt->cl_parent) { +		struct rpc_clnt *parent = clnt->cl_parent; +		/* Find the original parent for this transport */ +		if (rcu_access_pointer(parent->cl_xprt) != xprt) +			break; +		clnt = parent; +	} + +	gss_auth = gss_create_hashed(args, clnt); +	if (IS_ERR(gss_auth)) +		return ERR_CAST(gss_auth); +	return &gss_auth->rpc_auth;  }  /* @@ -927,7 +1221,7 @@ gss_destroying_context(struct rpc_cred *cred)  static void  gss_do_free_ctx(struct gss_cl_ctx *ctx)  { -	dprintk("RPC:       gss_free_ctx\n"); +	dprintk("RPC:       %s\n", __func__);  	gss_delete_sec_context(&ctx->gc_gss_ctx);  	kfree(ctx->gc_wire_ctx.data); @@ -950,7 +1244,7 @@ gss_free_ctx(struct gss_cl_ctx *ctx)  static void  gss_free_cred(struct gss_cred *gss_cred)  { -	dprintk("RPC:       gss_free_cred %p\n", gss_cred); +	dprintk("RPC:       %s cred=%p\n", __func__, gss_cred);  	kfree(gss_cred);  } @@ -968,11 +1262,11 @@ gss_destroy_nullcred(struct rpc_cred *cred)  	struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);  	struct gss_cl_ctx *ctx = gss_cred->gc_ctx; -	rcu_assign_pointer(gss_cred->gc_ctx, NULL); +	RCU_INIT_POINTER(gss_cred->gc_ctx, NULL);  	call_rcu(&cred->cr_rcu, gss_free_cred_callback);  	if (ctx)  		gss_put_ctx(ctx); -	kref_put(&gss_auth->kref, gss_free_callback); +	gss_put_auth(gss_auth);  }  static void @@ -1000,8 +1294,9 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)  	struct gss_cred	*cred = NULL;  	int err = -ENOMEM; -	dprintk("RPC:       gss_create_cred for uid %d, flavor %d\n", -		acred->uid, auth->au_flavor); +	dprintk("RPC:       %s for uid %d, flavor %d\n", +		__func__, from_kuid(&init_user_ns, acred->uid), +		auth->au_flavor);  	if (!(cred = kzalloc(sizeof(*cred), GFP_NOFS)))  		goto out_err; @@ -1013,12 +1308,14 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)  	 */  	cred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_NEW;  	cred->gc_service = gss_auth->service; -	cred->gc_machine_cred = acred->machine_cred; +	cred->gc_principal = NULL; +	if (acred->machine_cred) +		cred->gc_principal = acred->principal;  	kref_get(&gss_auth->kref);  	return &cred->gc_base;  out_err: -	dprintk("RPC:       gss_create_cred failed with error %d\n", err); +	dprintk("RPC:       %s failed with error %d\n", __func__, err);  	return ERR_PTR(err);  } @@ -1035,10 +1332,32 @@ gss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred)  	return err;  } +/* + * Returns -EACCES if GSS context is NULL or will expire within the + * timeout (miliseconds) + */ +static int +gss_key_timeout(struct rpc_cred *rc) +{ +	struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); +	unsigned long now = jiffies; +	unsigned long expire; + +	if (gss_cred->gc_ctx == NULL) +		return -EACCES; + +	expire = gss_cred->gc_ctx->gc_expiry - (gss_key_expire_timeo * HZ); + +	if (time_after(now, expire)) +		return -EACCES; +	return 0; +} +  static int  gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags)  {  	struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); +	int ret;  	if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags))  		goto out; @@ -1048,9 +1367,29 @@ gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags)  	if (!test_bit(RPCAUTH_CRED_UPTODATE, &rc->cr_flags))  		return 0;  out: -	if (acred->machine_cred != gss_cred->gc_machine_cred) +	if (acred->principal != NULL) { +		if (gss_cred->gc_principal == NULL) +			return 0; +		ret = strcmp(acred->principal, gss_cred->gc_principal) == 0; +		goto check_expire; +	} +	if (gss_cred->gc_principal != NULL)  		return 0; -	return rc->cr_uid == acred->uid; +	ret = uid_eq(rc->cr_uid, acred->uid); + +check_expire: +	if (ret == 0) +		return ret; + +	/* Notify acred users of GSS context expiration timeout */ +	if (test_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags) && +	    (gss_key_timeout(rc) != 0)) { +		/* test will now be done from generic cred */ +		test_and_clear_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags); +		/* tell NFS layer that key will expire soon */ +		set_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags); +	} +	return ret;  }  /* @@ -1071,7 +1410,7 @@ gss_marshal(struct rpc_task *task, __be32 *p)  	struct kvec	iov;  	struct xdr_buf	verf_buf; -	dprintk("RPC: %5u gss_marshal\n", task->tk_pid); +	dprintk("RPC: %5u %s\n", task->tk_pid, __func__);  	*p++ = htonl(RPC_AUTH_GSS);  	cred_len = p++; @@ -1089,7 +1428,7 @@ gss_marshal(struct rpc_task *task, __be32 *p)  	/* We compute the checksum for the verifier over the xdr-encoded bytes  	 * starting with the xid and ending at the end of the credential: */ -	iov.iov_base = xprt_skip_transport_header(task->tk_xprt, +	iov.iov_base = xprt_skip_transport_header(req->rq_xprt,  					req->rq_snd_buf.head[0].iov_base);  	iov.iov_len = (u8 *)p - (u8 *)iov.iov_base;  	xdr_buf_from_iov(&iov, &verf_buf); @@ -1122,7 +1461,8 @@ static int gss_renew_cred(struct rpc_task *task)  	struct rpc_auth *auth = oldcred->cr_auth;  	struct auth_cred acred = {  		.uid = oldcred->cr_uid, -		.machine_cred = gss_cred->gc_machine_cred, +		.principal = gss_cred->gc_principal, +		.machine_cred = (gss_cred->gc_principal != NULL ? 1 : 0),  	};  	struct rpc_cred *new; @@ -1181,7 +1521,7 @@ out:  static int  gss_refresh_null(struct rpc_task *task)  { -	return -EACCES; +	return 0;  }  static __be32 * @@ -1195,8 +1535,9 @@ gss_validate(struct rpc_task *task, __be32 *p)  	struct xdr_netobj mic;  	u32		flav,len;  	u32		maj_stat; +	__be32		*ret = ERR_PTR(-EIO); -	dprintk("RPC: %5u gss_validate\n", task->tk_pid); +	dprintk("RPC: %5u %s\n", task->tk_pid, __func__);  	flav = ntohl(*p++);  	if ((len = ntohl(*p++)) > RPC_MAX_AUTH_SIZE) @@ -1210,30 +1551,42 @@ gss_validate(struct rpc_task *task, __be32 *p)  	mic.data = (u8 *)p;  	mic.len = len; +	ret = ERR_PTR(-EACCES);  	maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);  	if (maj_stat == GSS_S_CONTEXT_EXPIRED)  		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);  	if (maj_stat) { -		dprintk("RPC: %5u gss_validate: gss_verify_mic returned " -				"error 0x%08x\n", task->tk_pid, maj_stat); +		dprintk("RPC: %5u %s: gss_verify_mic returned error 0x%08x\n", +			task->tk_pid, __func__, maj_stat);  		goto out_bad;  	}  	/* We leave it to unwrap to calculate au_rslack. For now we just  	 * calculate the length of the verifier: */  	cred->cr_auth->au_verfsize = XDR_QUADLEN(len) + 2;  	gss_put_ctx(ctx); -	dprintk("RPC: %5u gss_validate: gss_verify_mic succeeded.\n", -			task->tk_pid); +	dprintk("RPC: %5u %s: gss_verify_mic succeeded.\n", +			task->tk_pid, __func__);  	return p + XDR_QUADLEN(len);  out_bad:  	gss_put_ctx(ctx); -	dprintk("RPC: %5u gss_validate failed.\n", task->tk_pid); -	return NULL; +	dprintk("RPC: %5u %s failed ret %ld.\n", task->tk_pid, __func__, +		PTR_ERR(ret)); +	return ret; +} + +static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp, +				__be32 *p, void *obj) +{ +	struct xdr_stream xdr; + +	xdr_init_encode(&xdr, &rqstp->rq_snd_buf, p); +	encode(rqstp, &xdr, obj);  }  static inline int  gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, -		kxdrproc_t encode, struct rpc_rqst *rqstp, __be32 *p, void *obj) +		   kxdreproc_t encode, struct rpc_rqst *rqstp, +		   __be32 *p, void *obj)  {  	struct xdr_buf	*snd_buf = &rqstp->rq_snd_buf;  	struct xdr_buf	integ_buf; @@ -1249,9 +1602,7 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,  	offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;  	*p++ = htonl(rqstp->rq_seqno); -	status = encode(rqstp, p, obj); -	if (status) -		return status; +	gss_wrap_req_encode(encode, rqstp, p, obj);  	if (xdr_buf_subsegment(snd_buf, &integ_buf,  				offset, snd_buf->len - offset)) @@ -1325,7 +1676,8 @@ out:  static inline int  gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, -		kxdrproc_t encode, struct rpc_rqst *rqstp, __be32 *p, void *obj) +		  kxdreproc_t encode, struct rpc_rqst *rqstp, +		  __be32 *p, void *obj)  {  	struct xdr_buf	*snd_buf = &rqstp->rq_snd_buf;  	u32		offset; @@ -1342,9 +1694,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,  	offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;  	*p++ = htonl(rqstp->rq_seqno); -	status = encode(rqstp, p, obj); -	if (status) -		return status; +	gss_wrap_req_encode(encode, rqstp, p, obj);  	status = alloc_enc_pages(rqstp);  	if (status) @@ -1394,7 +1744,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,  static int  gss_wrap_req(struct rpc_task *task, -	     kxdrproc_t encode, void *rqstp, __be32 *p, void *obj) +	     kxdreproc_t encode, void *rqstp, __be32 *p, void *obj)  {  	struct rpc_cred *cred = task->tk_rqstp->rq_cred;  	struct gss_cred	*gss_cred = container_of(cred, struct gss_cred, @@ -1402,30 +1752,30 @@ gss_wrap_req(struct rpc_task *task,  	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);  	int             status = -EIO; -	dprintk("RPC: %5u gss_wrap_req\n", task->tk_pid); +	dprintk("RPC: %5u %s\n", task->tk_pid, __func__);  	if (ctx->gc_proc != RPC_GSS_PROC_DATA) {  		/* The spec seems a little ambiguous here, but I think that not  		 * wrapping context destruction requests makes the most sense.  		 */ -		status = encode(rqstp, p, obj); +		gss_wrap_req_encode(encode, rqstp, p, obj); +		status = 0;  		goto out;  	}  	switch (gss_cred->gc_service) { -		case RPC_GSS_SVC_NONE: -			status = encode(rqstp, p, obj); -			break; -		case RPC_GSS_SVC_INTEGRITY: -			status = gss_wrap_req_integ(cred, ctx, encode, -								rqstp, p, obj); -			break; -		case RPC_GSS_SVC_PRIVACY: -			status = gss_wrap_req_priv(cred, ctx, encode, -					rqstp, p, obj); -			break; +	case RPC_GSS_SVC_NONE: +		gss_wrap_req_encode(encode, rqstp, p, obj); +		status = 0; +		break; +	case RPC_GSS_SVC_INTEGRITY: +		status = gss_wrap_req_integ(cred, ctx, encode, rqstp, p, obj); +		break; +	case RPC_GSS_SVC_PRIVACY: +		status = gss_wrap_req_priv(cred, ctx, encode, rqstp, p, obj); +		break;  	}  out:  	gss_put_ctx(ctx); -	dprintk("RPC: %5u gss_wrap_req returning %d\n", task->tk_pid, status); +	dprintk("RPC: %5u %s returning %d\n", task->tk_pid, __func__, status);  	return status;  } @@ -1494,10 +1844,19 @@ gss_unwrap_resp_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,  	return 0;  } +static int +gss_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp, +		      __be32 *p, void *obj) +{ +	struct xdr_stream xdr; + +	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); +	return decode(rqstp, &xdr, obj); +}  static int  gss_unwrap_resp(struct rpc_task *task, -		kxdrproc_t decode, void *rqstp, __be32 *p, void *obj) +		kxdrdproc_t decode, void *rqstp, __be32 *p, void *obj)  {  	struct rpc_cred *cred = task->tk_rqstp->rq_cred;  	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, @@ -1511,28 +1870,28 @@ gss_unwrap_resp(struct rpc_task *task,  	if (ctx->gc_proc != RPC_GSS_PROC_DATA)  		goto out_decode;  	switch (gss_cred->gc_service) { -		case RPC_GSS_SVC_NONE: -			break; -		case RPC_GSS_SVC_INTEGRITY: -			status = gss_unwrap_resp_integ(cred, ctx, rqstp, &p); -			if (status) -				goto out; -			break; -		case RPC_GSS_SVC_PRIVACY: -			status = gss_unwrap_resp_priv(cred, ctx, rqstp, &p); -			if (status) -				goto out; -			break; +	case RPC_GSS_SVC_NONE: +		break; +	case RPC_GSS_SVC_INTEGRITY: +		status = gss_unwrap_resp_integ(cred, ctx, rqstp, &p); +		if (status) +			goto out; +		break; +	case RPC_GSS_SVC_PRIVACY: +		status = gss_unwrap_resp_priv(cred, ctx, rqstp, &p); +		if (status) +			goto out; +		break;  	}  	/* take into account extra slack for integrity and privacy cases: */  	cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp)  						+ (savedlen - head->iov_len);  out_decode: -	status = decode(rqstp, p, obj); +	status = gss_unwrap_req_decode(decode, rqstp, p, obj);  out:  	gss_put_ctx(ctx); -	dprintk("RPC: %5u gss_unwrap_resp returning %d\n", task->tk_pid, -			status); +	dprintk("RPC: %5u %s returning %d\n", +		task->tk_pid, __func__, status);  	return status;  } @@ -1543,7 +1902,10 @@ static const struct rpc_authops authgss_ops = {  	.create		= gss_create,  	.destroy	= gss_destroy,  	.lookup_cred	= gss_lookup_cred, -	.crcreate	= gss_create_cred +	.crcreate	= gss_create_cred, +	.list_pseudoflavors = gss_mech_list_pseudoflavors, +	.info2flavor	= gss_mech_info2flavor, +	.flavor2info	= gss_mech_flavor2info,  };  static const struct rpc_credops gss_credops = { @@ -1557,6 +1919,7 @@ static const struct rpc_credops gss_credops = {  	.crvalidate	= gss_validate,  	.crwrap_req	= gss_wrap_req,  	.crunwrap_resp	= gss_unwrap_resp, +	.crkey_timeout	= gss_key_timeout,  };  static const struct rpc_credops gss_nullops = { @@ -1572,7 +1935,7 @@ static const struct rpc_credops gss_nullops = {  };  static const struct rpc_pipe_ops gss_upcall_ops_v0 = { -	.upcall		= gss_pipe_upcall, +	.upcall		= rpc_pipe_generic_upcall,  	.downcall	= gss_pipe_downcall,  	.destroy_msg	= gss_pipe_destroy_msg,  	.open_pipe	= gss_pipe_open_v0, @@ -1580,13 +1943,28 @@ static const struct rpc_pipe_ops gss_upcall_ops_v0 = {  };  static const struct rpc_pipe_ops gss_upcall_ops_v1 = { -	.upcall		= gss_pipe_upcall, +	.upcall		= rpc_pipe_generic_upcall,  	.downcall	= gss_pipe_downcall,  	.destroy_msg	= gss_pipe_destroy_msg,  	.open_pipe	= gss_pipe_open_v1,  	.release_pipe	= gss_pipe_release,  }; +static __net_init int rpcsec_gss_init_net(struct net *net) +{ +	return gss_svc_init_net(net); +} + +static __net_exit void rpcsec_gss_exit_net(struct net *net) +{ +	gss_svc_shutdown_net(net); +} + +static struct pernet_operations rpcsec_gss_net_ops = { +	.init = rpcsec_gss_init_net, +	.exit = rpcsec_gss_exit_net, +}; +  /*   * Initialize RPCSEC_GSS module   */ @@ -1600,8 +1978,13 @@ static int __init init_rpcsec_gss(void)  	err = gss_svc_init();  	if (err)  		goto out_unregister; +	err = register_pernet_subsys(&rpcsec_gss_net_ops); +	if (err) +		goto out_svc_exit;  	rpc_init_wait_queue(&pipe_version_rpc_waitqueue, "gss pipe version");  	return 0; +out_svc_exit: +	gss_svc_shutdown();  out_unregister:  	rpcauth_unregister(&authgss_ops);  out: @@ -1610,11 +1993,13 @@ out:  static void __exit exit_rpcsec_gss(void)  { +	unregister_pernet_subsys(&rpcsec_gss_net_ops);  	gss_svc_shutdown();  	rpcauth_unregister(&authgss_ops);  	rcu_barrier(); /* Wait for completion of call_rcu()'s */  } +MODULE_ALIAS("rpc-auth-6");  MODULE_LICENSE("GPL");  module_param_named(expired_cred_retry_delay,  		   gss_expired_cred_retry_delay, @@ -1622,5 +2007,12 @@ module_param_named(expired_cred_retry_delay,  MODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until "  		"the RPC engine retries an expired credential"); +module_param_named(key_expire_timeo, +		   gss_key_expire_timeo, +		   uint, 0644); +MODULE_PARM_DESC(key_expire_timeo, "Time (in seconds) at the end of a " +		"credential keys lifetime where the NFS layer cleans up " +		"prior to key expiration"); +  module_init(init_rpcsec_gss)  module_exit(exit_rpcsec_gss)  | 
