diff options
Diffstat (limited to 'net/sunrpc/auth_gss/auth_gss.c')
| -rw-r--r-- | net/sunrpc/auth_gss/auth_gss.c | 112 | 
1 files changed, 73 insertions, 39 deletions
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index fcac5d14171..b6e440baccc 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -108,6 +108,7 @@ struct gss_auth {  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; @@ -142,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);  } @@ -320,6 +321,7 @@ gss_release_msg(struct gss_upcall_msg *gss_msg)  	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);  } @@ -420,41 +422,53 @@ static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg)  	memcpy(gss_msg->databuf, &uid, sizeof(uid));  	gss_msg->msg.data = gss_msg->databuf;  	gss_msg->msg.len = sizeof(uid); -	BUG_ON(sizeof(uid) > UPCALL_BUF_LEN); + +	BUILD_BUG_ON(sizeof(uid) > sizeof(gss_msg->databuf));  } -static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, +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, -				   from_kuid(&init_user_ns, gss_msg->uid)); -	p += gss_msg->msg.len; +	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 = sprintf(p, "target=%s ", target_name); +		len = scnprintf(p, buflen, "target=%s ", target_name); +		buflen -= len;  		p += len;  		gss_msg->msg.len += len;  	}  	if (service_name != NULL) { -		len = sprintf(p, "service=%s ", service_name); +		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, "enctypes=%s ", 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); +	return 0; +out_overflow: +	WARN_ON_ONCE(1); +	return -ENOMEM;  }  static struct gss_upcall_msg * @@ -463,15 +477,15 @@ gss_alloc_msg(struct gss_auth *gss_auth,  {  	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); +		goto err;  	vers = get_pipe_version(gss_auth->net); -	if (vers < 0) { -		kfree(gss_msg); -		return ERR_PTR(vers); -	} +	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"); @@ -482,10 +496,20 @@ gss_alloc_msg(struct gss_auth *gss_auth,  	switch (vers) {  	case 0:  		gss_encode_v0_msg(gss_msg); +		break;  	default: -		gss_encode_v1_msg(gss_msg, service_name, gss_auth->target_name); +		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 * @@ -513,14 +537,7 @@ gss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred)  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 @@ -581,7 +598,6 @@ gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)  	struct rpc_pipe *pipe;  	struct rpc_cred *cred = &gss_cred->gc_base;  	struct gss_upcall_msg *gss_msg; -	unsigned long timeout;  	DEFINE_WAIT(wait);  	int err; @@ -589,17 +605,16 @@ gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)  		__func__, from_kuid(&init_user_ns, cred->cr_uid));  retry:  	err = 0; -	/* Default timeout is 15s unless we know that gssd is not running */ -	timeout = 15 * HZ; -	if (!sn->gssd_running) -		timeout = HZ >> 2; +	/* 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, -				sn->pipe_version >= 0, timeout); +				sn->pipe_version >= 0, 15 * HZ);  		if (sn->pipe_version < 0) { -			if (err == 0) -				sn->gssd_running = 0;  			warn_gssd();  			err = -EACCES;  		} @@ -981,6 +996,8 @@ gss_create_new(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)  	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; @@ -1052,6 +1069,12 @@ 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 = container_of(auth, @@ -1072,9 +1095,18 @@ gss_destroy(struct rpc_auth *auth)  	gss_auth->gss_pipe[1] = NULL;  	rpcauth_destroy_credcache(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, @@ -1088,6 +1120,8 @@ gss_auth_find_or_add_hashed(struct rpc_auth_create_args *args,  			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) { @@ -1232,7 +1266,7 @@ gss_destroy_nullcred(struct rpc_cred *cred)  	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 @@ -1487,7 +1521,7 @@ out:  static int  gss_refresh_null(struct rpc_task *task)  { -	return -EACCES; +	return 0;  }  static __be32 *  | 
