| .. | .. | 
|---|
| 11 | 11 | #include <linux/types.h> | 
|---|
| 12 | 12 | #include <linux/sched.h> | 
|---|
| 13 | 13 | #include <linux/module.h> | 
|---|
|  | 14 | +#include <linux/mempool.h> | 
|---|
| 14 | 15 | #include <linux/sunrpc/clnt.h> | 
|---|
| 15 | 16 | #include <linux/sunrpc/auth.h> | 
|---|
| 16 | 17 | #include <linux/user_namespace.h> | 
|---|
| 17 | 18 |  | 
|---|
| 18 |  | -struct unx_cred { | 
|---|
| 19 |  | -	struct rpc_cred		uc_base; | 
|---|
| 20 |  | -	kgid_t			uc_gid; | 
|---|
| 21 |  | -	kgid_t			uc_gids[UNX_NGROUPS]; | 
|---|
| 22 |  | -}; | 
|---|
| 23 |  | -#define uc_uid			uc_base.cr_uid | 
|---|
| 24 | 19 |  | 
|---|
| 25 | 20 | #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) | 
|---|
| 26 | 21 | # define RPCDBG_FACILITY	RPCDBG_AUTH | 
|---|
| .. | .. | 
|---|
| 28 | 23 |  | 
|---|
| 29 | 24 | static struct rpc_auth		unix_auth; | 
|---|
| 30 | 25 | static const struct rpc_credops	unix_credops; | 
|---|
|  | 26 | +static mempool_t		*unix_pool; | 
|---|
| 31 | 27 |  | 
|---|
| 32 | 28 | static struct rpc_auth * | 
|---|
| 33 | 29 | unx_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) | 
|---|
| 34 | 30 | { | 
|---|
| 35 |  | -	dprintk("RPC:       creating UNIX authenticator for client %p\n", | 
|---|
| 36 |  | -			clnt); | 
|---|
| 37 |  | -	atomic_inc(&unix_auth.au_count); | 
|---|
|  | 31 | +	refcount_inc(&unix_auth.au_count); | 
|---|
| 38 | 32 | return &unix_auth; | 
|---|
| 39 | 33 | } | 
|---|
| 40 | 34 |  | 
|---|
| 41 | 35 | static void | 
|---|
| 42 | 36 | unx_destroy(struct rpc_auth *auth) | 
|---|
| 43 | 37 | { | 
|---|
| 44 |  | -	dprintk("RPC:       destroying UNIX authenticator %p\n", auth); | 
|---|
| 45 |  | -	rpcauth_clear_credcache(auth->au_credcache); | 
|---|
| 46 |  | -} | 
|---|
| 47 |  | - | 
|---|
| 48 |  | -static int | 
|---|
| 49 |  | -unx_hash_cred(struct auth_cred *acred, unsigned int hashbits) | 
|---|
| 50 |  | -{ | 
|---|
| 51 |  | -	return hash_64(from_kgid(&init_user_ns, acred->gid) | | 
|---|
| 52 |  | -		((u64)from_kuid(&init_user_ns, acred->uid) << | 
|---|
| 53 |  | -			(sizeof(gid_t) * 8)), hashbits); | 
|---|
| 54 | 38 | } | 
|---|
| 55 | 39 |  | 
|---|
| 56 | 40 | /* | 
|---|
| .. | .. | 
|---|
| 59 | 43 | static struct rpc_cred * | 
|---|
| 60 | 44 | unx_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) | 
|---|
| 61 | 45 | { | 
|---|
| 62 |  | -	return rpcauth_lookup_credcache(auth, acred, flags, GFP_NOFS); | 
|---|
| 63 |  | -} | 
|---|
|  | 46 | +	struct rpc_cred *ret = mempool_alloc(unix_pool, GFP_NOFS); | 
|---|
| 64 | 47 |  | 
|---|
| 65 |  | -static struct rpc_cred * | 
|---|
| 66 |  | -unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, gfp_t gfp) | 
|---|
| 67 |  | -{ | 
|---|
| 68 |  | -	struct unx_cred	*cred; | 
|---|
| 69 |  | -	unsigned int groups = 0; | 
|---|
| 70 |  | -	unsigned int i; | 
|---|
| 71 |  | - | 
|---|
| 72 |  | -	dprintk("RPC:       allocating UNIX cred for uid %d gid %d\n", | 
|---|
| 73 |  | -			from_kuid(&init_user_ns, acred->uid), | 
|---|
| 74 |  | -			from_kgid(&init_user_ns, acred->gid)); | 
|---|
| 75 |  | - | 
|---|
| 76 |  | -	if (!(cred = kmalloc(sizeof(*cred), gfp))) | 
|---|
| 77 |  | -		return ERR_PTR(-ENOMEM); | 
|---|
| 78 |  | - | 
|---|
| 79 |  | -	rpcauth_init_cred(&cred->uc_base, acred, auth, &unix_credops); | 
|---|
| 80 |  | -	cred->uc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE; | 
|---|
| 81 |  | - | 
|---|
| 82 |  | -	if (acred->group_info != NULL) | 
|---|
| 83 |  | -		groups = acred->group_info->ngroups; | 
|---|
| 84 |  | -	if (groups > UNX_NGROUPS) | 
|---|
| 85 |  | -		groups = UNX_NGROUPS; | 
|---|
| 86 |  | - | 
|---|
| 87 |  | -	cred->uc_gid = acred->gid; | 
|---|
| 88 |  | -	for (i = 0; i < groups; i++) | 
|---|
| 89 |  | -		cred->uc_gids[i] = acred->group_info->gid[i]; | 
|---|
| 90 |  | -	if (i < UNX_NGROUPS) | 
|---|
| 91 |  | -		cred->uc_gids[i] = INVALID_GID; | 
|---|
| 92 |  | - | 
|---|
| 93 |  | -	return &cred->uc_base; | 
|---|
| 94 |  | -} | 
|---|
| 95 |  | - | 
|---|
| 96 |  | -static void | 
|---|
| 97 |  | -unx_free_cred(struct unx_cred *unx_cred) | 
|---|
| 98 |  | -{ | 
|---|
| 99 |  | -	dprintk("RPC:       unx_free_cred %p\n", unx_cred); | 
|---|
| 100 |  | -	kfree(unx_cred); | 
|---|
|  | 48 | +	rpcauth_init_cred(ret, acred, auth, &unix_credops); | 
|---|
|  | 49 | +	ret->cr_flags = 1UL << RPCAUTH_CRED_UPTODATE; | 
|---|
|  | 50 | +	return ret; | 
|---|
| 101 | 51 | } | 
|---|
| 102 | 52 |  | 
|---|
| 103 | 53 | static void | 
|---|
| 104 | 54 | unx_free_cred_callback(struct rcu_head *head) | 
|---|
| 105 | 55 | { | 
|---|
| 106 |  | -	struct unx_cred *unx_cred = container_of(head, struct unx_cred, uc_base.cr_rcu); | 
|---|
| 107 |  | -	unx_free_cred(unx_cred); | 
|---|
|  | 56 | +	struct rpc_cred *rpc_cred = container_of(head, struct rpc_cred, cr_rcu); | 
|---|
|  | 57 | + | 
|---|
|  | 58 | +	put_cred(rpc_cred->cr_cred); | 
|---|
|  | 59 | +	mempool_free(rpc_cred, unix_pool); | 
|---|
| 108 | 60 | } | 
|---|
| 109 | 61 |  | 
|---|
| 110 | 62 | static void | 
|---|
| .. | .. | 
|---|
| 114 | 66 | } | 
|---|
| 115 | 67 |  | 
|---|
| 116 | 68 | /* | 
|---|
| 117 |  | - * Match credentials against current process creds. | 
|---|
| 118 |  | - * The root_override argument takes care of cases where the caller may | 
|---|
| 119 |  | - * request root creds (e.g. for NFS swapping). | 
|---|
|  | 69 | + * Match credentials against current the auth_cred. | 
|---|
| 120 | 70 | */ | 
|---|
| 121 | 71 | static int | 
|---|
| 122 |  | -unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags) | 
|---|
|  | 72 | +unx_match(struct auth_cred *acred, struct rpc_cred *cred, int flags) | 
|---|
| 123 | 73 | { | 
|---|
| 124 |  | -	struct unx_cred	*cred = container_of(rcred, struct unx_cred, uc_base); | 
|---|
| 125 | 74 | unsigned int groups = 0; | 
|---|
| 126 | 75 | unsigned int i; | 
|---|
| 127 | 76 |  | 
|---|
|  | 77 | +	if (cred->cr_cred == acred->cred) | 
|---|
|  | 78 | +		return 1; | 
|---|
| 128 | 79 |  | 
|---|
| 129 |  | -	if (!uid_eq(cred->uc_uid, acred->uid) || !gid_eq(cred->uc_gid, acred->gid)) | 
|---|
|  | 80 | +	if (!uid_eq(cred->cr_cred->fsuid, acred->cred->fsuid) || !gid_eq(cred->cr_cred->fsgid, acred->cred->fsgid)) | 
|---|
| 130 | 81 | return 0; | 
|---|
| 131 | 82 |  | 
|---|
| 132 |  | -	if (acred->group_info != NULL) | 
|---|
| 133 |  | -		groups = acred->group_info->ngroups; | 
|---|
|  | 83 | +	if (acred->cred->group_info != NULL) | 
|---|
|  | 84 | +		groups = acred->cred->group_info->ngroups; | 
|---|
| 134 | 85 | if (groups > UNX_NGROUPS) | 
|---|
| 135 | 86 | groups = UNX_NGROUPS; | 
|---|
| 136 |  | -	for (i = 0; i < groups ; i++) | 
|---|
| 137 |  | -		if (!gid_eq(cred->uc_gids[i], acred->group_info->gid[i])) | 
|---|
| 138 |  | -			return 0; | 
|---|
| 139 |  | -	if (groups < UNX_NGROUPS && gid_valid(cred->uc_gids[groups])) | 
|---|
|  | 87 | +	if (cred->cr_cred->group_info == NULL) | 
|---|
|  | 88 | +		return groups == 0; | 
|---|
|  | 89 | +	if (groups != cred->cr_cred->group_info->ngroups) | 
|---|
| 140 | 90 | return 0; | 
|---|
|  | 91 | + | 
|---|
|  | 92 | +	for (i = 0; i < groups ; i++) | 
|---|
|  | 93 | +		if (!gid_eq(cred->cr_cred->group_info->gid[i], acred->cred->group_info->gid[i])) | 
|---|
|  | 94 | +			return 0; | 
|---|
| 141 | 95 | return 1; | 
|---|
| 142 | 96 | } | 
|---|
| 143 | 97 |  | 
|---|
| .. | .. | 
|---|
| 145 | 99 | * Marshal credentials. | 
|---|
| 146 | 100 | * Maybe we should keep a cached credential for performance reasons. | 
|---|
| 147 | 101 | */ | 
|---|
| 148 |  | -static __be32 * | 
|---|
| 149 |  | -unx_marshal(struct rpc_task *task, __be32 *p) | 
|---|
|  | 102 | +static int | 
|---|
|  | 103 | +unx_marshal(struct rpc_task *task, struct xdr_stream *xdr) | 
|---|
| 150 | 104 | { | 
|---|
| 151 | 105 | struct rpc_clnt	*clnt = task->tk_client; | 
|---|
| 152 |  | -	struct unx_cred	*cred = container_of(task->tk_rqstp->rq_cred, struct unx_cred, uc_base); | 
|---|
| 153 |  | -	__be32		*base, *hold; | 
|---|
|  | 106 | +	struct rpc_cred	*cred = task->tk_rqstp->rq_cred; | 
|---|
|  | 107 | +	__be32		*p, *cred_len, *gidarr_len; | 
|---|
| 154 | 108 | int		i; | 
|---|
|  | 109 | +	struct group_info *gi = cred->cr_cred->group_info; | 
|---|
|  | 110 | +	struct user_namespace *userns = clnt->cl_cred ? | 
|---|
|  | 111 | +		clnt->cl_cred->user_ns : &init_user_ns; | 
|---|
| 155 | 112 |  | 
|---|
| 156 |  | -	*p++ = htonl(RPC_AUTH_UNIX); | 
|---|
| 157 |  | -	base = p++; | 
|---|
| 158 |  | -	*p++ = htonl(jiffies/HZ); | 
|---|
|  | 113 | +	/* Credential */ | 
|---|
| 159 | 114 |  | 
|---|
| 160 |  | -	/* | 
|---|
| 161 |  | -	 * Copy the UTS nodename captured when the client was created. | 
|---|
| 162 |  | -	 */ | 
|---|
| 163 |  | -	p = xdr_encode_array(p, clnt->cl_nodename, clnt->cl_nodelen); | 
|---|
|  | 115 | +	p = xdr_reserve_space(xdr, 3 * sizeof(*p)); | 
|---|
|  | 116 | +	if (!p) | 
|---|
|  | 117 | +		goto marshal_failed; | 
|---|
|  | 118 | +	*p++ = rpc_auth_unix; | 
|---|
|  | 119 | +	cred_len = p++; | 
|---|
|  | 120 | +	*p++ = xdr_zero;	/* stamp */ | 
|---|
|  | 121 | +	if (xdr_stream_encode_opaque(xdr, clnt->cl_nodename, | 
|---|
|  | 122 | +				     clnt->cl_nodelen) < 0) | 
|---|
|  | 123 | +		goto marshal_failed; | 
|---|
|  | 124 | +	p = xdr_reserve_space(xdr, 3 * sizeof(*p)); | 
|---|
|  | 125 | +	if (!p) | 
|---|
|  | 126 | +		goto marshal_failed; | 
|---|
|  | 127 | +	*p++ = cpu_to_be32(from_kuid_munged(userns, cred->cr_cred->fsuid)); | 
|---|
|  | 128 | +	*p++ = cpu_to_be32(from_kgid_munged(userns, cred->cr_cred->fsgid)); | 
|---|
| 164 | 129 |  | 
|---|
| 165 |  | -	*p++ = htonl((u32) from_kuid(&init_user_ns, cred->uc_uid)); | 
|---|
| 166 |  | -	*p++ = htonl((u32) from_kgid(&init_user_ns, cred->uc_gid)); | 
|---|
| 167 |  | -	hold = p++; | 
|---|
| 168 |  | -	for (i = 0; i < UNX_NGROUPS && gid_valid(cred->uc_gids[i]); i++) | 
|---|
| 169 |  | -		*p++ = htonl((u32) from_kgid(&init_user_ns, cred->uc_gids[i])); | 
|---|
| 170 |  | -	*hold = htonl(p - hold - 1);		/* gid array length */ | 
|---|
| 171 |  | -	*base = htonl((p - base - 1) << 2);	/* cred length */ | 
|---|
|  | 130 | +	gidarr_len = p++; | 
|---|
|  | 131 | +	if (gi) | 
|---|
|  | 132 | +		for (i = 0; i < UNX_NGROUPS && i < gi->ngroups; i++) | 
|---|
|  | 133 | +			*p++ = cpu_to_be32(from_kgid_munged(userns, gi->gid[i])); | 
|---|
|  | 134 | +	*gidarr_len = cpu_to_be32(p - gidarr_len - 1); | 
|---|
|  | 135 | +	*cred_len = cpu_to_be32((p - cred_len - 1) << 2); | 
|---|
|  | 136 | +	p = xdr_reserve_space(xdr, (p - gidarr_len - 1) << 2); | 
|---|
|  | 137 | +	if (!p) | 
|---|
|  | 138 | +		goto marshal_failed; | 
|---|
| 172 | 139 |  | 
|---|
| 173 |  | -	*p++ = htonl(RPC_AUTH_NULL); | 
|---|
| 174 |  | -	*p++ = htonl(0); | 
|---|
|  | 140 | +	/* Verifier */ | 
|---|
| 175 | 141 |  | 
|---|
| 176 |  | -	return p; | 
|---|
|  | 142 | +	p = xdr_reserve_space(xdr, 2 * sizeof(*p)); | 
|---|
|  | 143 | +	if (!p) | 
|---|
|  | 144 | +		goto marshal_failed; | 
|---|
|  | 145 | +	*p++ = rpc_auth_null; | 
|---|
|  | 146 | +	*p   = xdr_zero; | 
|---|
|  | 147 | + | 
|---|
|  | 148 | +	return 0; | 
|---|
|  | 149 | + | 
|---|
|  | 150 | +marshal_failed: | 
|---|
|  | 151 | +	return -EMSGSIZE; | 
|---|
| 177 | 152 | } | 
|---|
| 178 | 153 |  | 
|---|
| 179 | 154 | /* | 
|---|
| .. | .. | 
|---|
| 186 | 161 | return 0; | 
|---|
| 187 | 162 | } | 
|---|
| 188 | 163 |  | 
|---|
| 189 |  | -static __be32 * | 
|---|
| 190 |  | -unx_validate(struct rpc_task *task, __be32 *p) | 
|---|
|  | 164 | +static int | 
|---|
|  | 165 | +unx_validate(struct rpc_task *task, struct xdr_stream *xdr) | 
|---|
| 191 | 166 | { | 
|---|
| 192 |  | -	rpc_authflavor_t	flavor; | 
|---|
| 193 |  | -	u32			size; | 
|---|
|  | 167 | +	struct rpc_auth *auth = task->tk_rqstp->rq_cred->cr_auth; | 
|---|
|  | 168 | +	__be32 *p; | 
|---|
|  | 169 | +	u32 size; | 
|---|
| 194 | 170 |  | 
|---|
| 195 |  | -	flavor = ntohl(*p++); | 
|---|
| 196 |  | -	if (flavor != RPC_AUTH_NULL && | 
|---|
| 197 |  | -	    flavor != RPC_AUTH_UNIX && | 
|---|
| 198 |  | -	    flavor != RPC_AUTH_SHORT) { | 
|---|
| 199 |  | -		printk("RPC: bad verf flavor: %u\n", flavor); | 
|---|
| 200 |  | -		return ERR_PTR(-EIO); | 
|---|
|  | 171 | +	p = xdr_inline_decode(xdr, 2 * sizeof(*p)); | 
|---|
|  | 172 | +	if (!p) | 
|---|
|  | 173 | +		return -EIO; | 
|---|
|  | 174 | +	switch (*p++) { | 
|---|
|  | 175 | +	case rpc_auth_null: | 
|---|
|  | 176 | +	case rpc_auth_unix: | 
|---|
|  | 177 | +	case rpc_auth_short: | 
|---|
|  | 178 | +		break; | 
|---|
|  | 179 | +	default: | 
|---|
|  | 180 | +		return -EIO; | 
|---|
| 201 | 181 | } | 
|---|
|  | 182 | +	size = be32_to_cpup(p); | 
|---|
|  | 183 | +	if (size > RPC_MAX_AUTH_SIZE) | 
|---|
|  | 184 | +		return -EIO; | 
|---|
|  | 185 | +	p = xdr_inline_decode(xdr, size); | 
|---|
|  | 186 | +	if (!p) | 
|---|
|  | 187 | +		return -EIO; | 
|---|
| 202 | 188 |  | 
|---|
| 203 |  | -	size = ntohl(*p++); | 
|---|
| 204 |  | -	if (size > RPC_MAX_AUTH_SIZE) { | 
|---|
| 205 |  | -		printk("RPC: giant verf size: %u\n", size); | 
|---|
| 206 |  | -		return ERR_PTR(-EIO); | 
|---|
| 207 |  | -	} | 
|---|
| 208 |  | -	task->tk_rqstp->rq_cred->cr_auth->au_rslack = (size >> 2) + 2; | 
|---|
| 209 |  | -	p += (size >> 2); | 
|---|
| 210 |  | - | 
|---|
| 211 |  | -	return p; | 
|---|
|  | 189 | +	auth->au_verfsize = XDR_QUADLEN(size) + 2; | 
|---|
|  | 190 | +	auth->au_rslack = XDR_QUADLEN(size) + 2; | 
|---|
|  | 191 | +	auth->au_ralign = XDR_QUADLEN(size) + 2; | 
|---|
|  | 192 | +	return 0; | 
|---|
| 212 | 193 | } | 
|---|
| 213 | 194 |  | 
|---|
| 214 | 195 | int __init rpc_init_authunix(void) | 
|---|
| 215 | 196 | { | 
|---|
| 216 |  | -	return rpcauth_init_credcache(&unix_auth); | 
|---|
|  | 197 | +	unix_pool = mempool_create_kmalloc_pool(16, sizeof(struct rpc_cred)); | 
|---|
|  | 198 | +	return unix_pool ? 0 : -ENOMEM; | 
|---|
| 217 | 199 | } | 
|---|
| 218 | 200 |  | 
|---|
| 219 | 201 | void rpc_destroy_authunix(void) | 
|---|
| 220 | 202 | { | 
|---|
| 221 |  | -	rpcauth_destroy_credcache(&unix_auth); | 
|---|
|  | 203 | +	mempool_destroy(unix_pool); | 
|---|
| 222 | 204 | } | 
|---|
| 223 | 205 |  | 
|---|
| 224 | 206 | const struct rpc_authops authunix_ops = { | 
|---|
| .. | .. | 
|---|
| 227 | 209 | .au_name	= "UNIX", | 
|---|
| 228 | 210 | .create		= unx_create, | 
|---|
| 229 | 211 | .destroy	= unx_destroy, | 
|---|
| 230 |  | -	.hash_cred	= unx_hash_cred, | 
|---|
| 231 | 212 | .lookup_cred	= unx_lookup_cred, | 
|---|
| 232 |  | -	.crcreate	= unx_create_cred, | 
|---|
| 233 | 213 | }; | 
|---|
| 234 | 214 |  | 
|---|
| 235 | 215 | static | 
|---|
| 236 | 216 | struct rpc_auth		unix_auth = { | 
|---|
| 237 | 217 | .au_cslack	= UNX_CALLSLACK, | 
|---|
| 238 | 218 | .au_rslack	= NUL_REPLYSLACK, | 
|---|
| 239 |  | -	.au_flags	= RPCAUTH_AUTH_NO_CRKEY_TIMEOUT, | 
|---|
|  | 219 | +	.au_verfsize	= NUL_REPLYSLACK, | 
|---|
| 240 | 220 | .au_ops		= &authunix_ops, | 
|---|
| 241 | 221 | .au_flavor	= RPC_AUTH_UNIX, | 
|---|
| 242 |  | -	.au_count	= ATOMIC_INIT(0), | 
|---|
|  | 222 | +	.au_count	= REFCOUNT_INIT(1), | 
|---|
| 243 | 223 | }; | 
|---|
| 244 | 224 |  | 
|---|
| 245 | 225 | static | 
|---|
| 246 | 226 | const struct rpc_credops unix_credops = { | 
|---|
| 247 | 227 | .cr_name	= "AUTH_UNIX", | 
|---|
| 248 | 228 | .crdestroy	= unx_destroy_cred, | 
|---|
| 249 |  | -	.crbind		= rpcauth_generic_bind_cred, | 
|---|
| 250 | 229 | .crmatch	= unx_match, | 
|---|
| 251 | 230 | .crmarshal	= unx_marshal, | 
|---|
|  | 231 | +	.crwrap_req	= rpcauth_wrap_req_encode, | 
|---|
| 252 | 232 | .crrefresh	= unx_refresh, | 
|---|
| 253 | 233 | .crvalidate	= unx_validate, | 
|---|
|  | 234 | +	.crunwrap_resp	= rpcauth_unwrap_resp_decode, | 
|---|
| 254 | 235 | }; | 
|---|