idmap.c 19.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/*
 * fs/nfs/idmap.c
 *
 *  UID and GID to name mapping for clients.
 *
 *  Copyright (c) 2002 The Regents of the University of Michigan.
 *  All rights reserved.
 *
 *  Marius Aamodt Eriksen <marius@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.
 */
36
#include <linux/types.h>
37 38
#include <linux/parser.h>
#include <linux/fs.h>
39
#include <linux/nfs_idmap.h>
40 41
#include <net/net_namespace.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
42
#include <linux/nfs_fs.h>
43
#include <linux/nfs_fs_sb.h>
44
#include <linux/key.h>
45 46 47 48
#include <linux/keyctl.h>
#include <linux/key-type.h>
#include <keys/user-type.h>
#include <linux/module.h>
49

50
#include "internal.h"
51
#include "netns.h"
52
#include "nfs4trace.h"
53 54 55

#define NFS_UINT_MAXLEN 11

56 57
static const struct cred *id_resolver_cache;
static struct key_type key_type_id_resolver_legacy;
58

59 60 61
struct idmap_legacy_upcalldata {
	struct rpc_pipe_msg pipe_msg;
	struct idmap_msg idmap_msg;
62
	struct key_construction	*key_cons;
63 64 65
	struct idmap *idmap;
};

66
struct idmap {
67
	struct rpc_pipe_dir_object idmap_pdo;
68 69 70 71 72
	struct rpc_pipe		*idmap_pipe;
	struct idmap_legacy_upcalldata *idmap_upcall_data;
	struct mutex		idmap_mutex;
};

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
/**
 * nfs_fattr_init_names - initialise the nfs_fattr owner_name/group_name fields
 * @fattr: fully initialised struct nfs_fattr
 * @owner_name: owner name string cache
 * @group_name: group name string cache
 */
void nfs_fattr_init_names(struct nfs_fattr *fattr,
		struct nfs4_string *owner_name,
		struct nfs4_string *group_name)
{
	fattr->owner_name = owner_name;
	fattr->group_name = group_name;
}

static void nfs_fattr_free_owner_name(struct nfs_fattr *fattr)
{
	fattr->valid &= ~NFS_ATTR_FATTR_OWNER_NAME;
	kfree(fattr->owner_name->data);
}

static void nfs_fattr_free_group_name(struct nfs_fattr *fattr)
{
	fattr->valid &= ~NFS_ATTR_FATTR_GROUP_NAME;
	kfree(fattr->group_name->data);
}

static bool nfs_fattr_map_owner_name(struct nfs_server *server, struct nfs_fattr *fattr)
{
	struct nfs4_string *owner = fattr->owner_name;
102
	kuid_t uid;
103 104 105 106 107 108 109 110 111 112 113 114 115

	if (!(fattr->valid & NFS_ATTR_FATTR_OWNER_NAME))
		return false;
	if (nfs_map_name_to_uid(server, owner->data, owner->len, &uid) == 0) {
		fattr->uid = uid;
		fattr->valid |= NFS_ATTR_FATTR_OWNER;
	}
	return true;
}

static bool nfs_fattr_map_group_name(struct nfs_server *server, struct nfs_fattr *fattr)
{
	struct nfs4_string *group = fattr->group_name;
116
	kgid_t gid;
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153

	if (!(fattr->valid & NFS_ATTR_FATTR_GROUP_NAME))
		return false;
	if (nfs_map_group_to_gid(server, group->data, group->len, &gid) == 0) {
		fattr->gid = gid;
		fattr->valid |= NFS_ATTR_FATTR_GROUP;
	}
	return true;
}

/**
 * nfs_fattr_free_names - free up the NFSv4 owner and group strings
 * @fattr: a fully initialised nfs_fattr structure
 */
void nfs_fattr_free_names(struct nfs_fattr *fattr)
{
	if (fattr->valid & NFS_ATTR_FATTR_OWNER_NAME)
		nfs_fattr_free_owner_name(fattr);
	if (fattr->valid & NFS_ATTR_FATTR_GROUP_NAME)
		nfs_fattr_free_group_name(fattr);
}

/**
 * nfs_fattr_map_and_free_names - map owner/group strings into uid/gid and free
 * @server: pointer to the filesystem nfs_server structure
 * @fattr: a fully initialised nfs_fattr structure
 *
 * This helper maps the cached NFSv4 owner/group strings in fattr into
 * their numeric uid/gid equivalents, and then frees the cached strings.
 */
void nfs_fattr_map_and_free_names(struct nfs_server *server, struct nfs_fattr *fattr)
{
	if (nfs_fattr_map_owner_name(server, fattr))
		nfs_fattr_free_owner_name(fattr);
	if (nfs_fattr_map_group_name(server, fattr))
		nfs_fattr_free_group_name(fattr);
}
154 155 156 157 158 159 160 161 162 163

static int nfs_map_string_to_numeric(const char *name, size_t namelen, __u32 *res)
{
	unsigned long val;
	char buf[16];

	if (memchr(name, '@', namelen) != NULL || namelen >= sizeof(buf))
		return 0;
	memcpy(buf, name, namelen);
	buf[namelen] = '\0';
164
	if (kstrtoul(buf, 0, &val) != 0)
165 166 167 168
		return 0;
	*res = val;
	return 1;
}
L
Linus Torvalds 已提交
169

170 171 172 173 174
static int nfs_map_numeric_to_string(__u32 id, char *buf, size_t buflen)
{
	return snprintf(buf, buflen, "%u", id);
}

175
static struct key_type key_type_id_resolver = {
B
Bryan Schumaker 已提交
176
	.name		= "id_resolver",
D
David Howells 已提交
177 178 179
	.preparse	= user_preparse,
	.free_preparse	= user_free_preparse,
	.instantiate	= generic_key_instantiate,
B
Bryan Schumaker 已提交
180 181 182 183 184 185
	.revoke		= user_revoke,
	.destroy	= user_destroy,
	.describe	= user_describe,
	.read		= user_read,
};

186
static int nfs_idmap_init_keyring(void)
B
Bryan Schumaker 已提交
187 188 189 190 191
{
	struct cred *cred;
	struct key *keyring;
	int ret = 0;

192 193
	printk(KERN_NOTICE "NFS: Registering the %s key type\n",
		key_type_id_resolver.name);
B
Bryan Schumaker 已提交
194 195 196 197 198

	cred = prepare_kernel_cred(NULL);
	if (!cred)
		return -ENOMEM;

199 200
	keyring = keyring_alloc(".id_resolver",
				GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
201 202 203
				(KEY_POS_ALL & ~KEY_POS_SETATTR) |
				KEY_USR_VIEW | KEY_USR_READ,
				KEY_ALLOC_NOT_IN_QUOTA, NULL);
B
Bryan Schumaker 已提交
204 205 206 207 208 209 210 211 212
	if (IS_ERR(keyring)) {
		ret = PTR_ERR(keyring);
		goto failed_put_cred;
	}

	ret = register_key_type(&key_type_id_resolver);
	if (ret < 0)
		goto failed_put_key;

213 214 215 216
	ret = register_key_type(&key_type_id_resolver_legacy);
	if (ret < 0)
		goto failed_reg_legacy;

217
	set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
B
Bryan Schumaker 已提交
218 219 220 221 222
	cred->thread_keyring = keyring;
	cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
	id_resolver_cache = cred;
	return 0;

223 224
failed_reg_legacy:
	unregister_key_type(&key_type_id_resolver);
B
Bryan Schumaker 已提交
225 226 227 228 229 230 231
failed_put_key:
	key_put(keyring);
failed_put_cred:
	put_cred(cred);
	return ret;
}

232
static void nfs_idmap_quit_keyring(void)
B
Bryan Schumaker 已提交
233 234 235
{
	key_revoke(id_resolver_cache->thread_keyring);
	unregister_key_type(&key_type_id_resolver);
236
	unregister_key_type(&key_type_id_resolver_legacy);
B
Bryan Schumaker 已提交
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
	put_cred(id_resolver_cache);
}

/*
 * Assemble the description to pass to request_key()
 * This function will allocate a new string and update dest to point
 * at it.  The caller is responsible for freeing dest.
 *
 * On error 0 is returned.  Otherwise, the length of dest is returned.
 */
static ssize_t nfs_idmap_get_desc(const char *name, size_t namelen,
				const char *type, size_t typelen, char **desc)
{
	char *cp;
	size_t desclen = typelen + namelen + 2;

	*desc = kmalloc(desclen, GFP_KERNEL);
D
Dan Carpenter 已提交
254
	if (!*desc)
B
Bryan Schumaker 已提交
255 256 257 258 259 260 261 262 263 264 265 266 267
		return -ENOMEM;

	cp = *desc;
	memcpy(cp, type, typelen);
	cp += typelen;
	*cp++ = ':';

	memcpy(cp, name, namelen);
	cp += namelen;
	*cp = '\0';
	return desclen;
}

268 269
static struct key *nfs_idmap_request_key(const char *name, size_t namelen,
					 const char *type, struct idmap *idmap)
B
Bryan Schumaker 已提交
270 271
{
	char *desc;
272
	struct key *rkey;
B
Bryan Schumaker 已提交
273 274 275 276
	ssize_t ret;

	ret = nfs_idmap_get_desc(name, namelen, type, strlen(type), &desc);
	if (ret <= 0)
277 278 279 280 281 282 283 284 285
		return ERR_PTR(ret);

	rkey = request_key(&key_type_id_resolver, desc, "");
	if (IS_ERR(rkey)) {
		mutex_lock(&idmap->idmap_mutex);
		rkey = request_key_with_auxdata(&key_type_id_resolver_legacy,
						desc, "", 0, idmap);
		mutex_unlock(&idmap->idmap_mutex);
	}
286 287
	if (!IS_ERR(rkey))
		set_bit(KEY_FLAG_ROOT_CAN_INVAL, &rkey->flags);
288 289 290 291 292 293 294 295 296 297 298 299 300

	kfree(desc);
	return rkey;
}

static ssize_t nfs_idmap_get_key(const char *name, size_t namelen,
				 const char *type, void *data,
				 size_t data_size, struct idmap *idmap)
{
	const struct cred *saved_cred;
	struct key *rkey;
	struct user_key_payload *payload;
	ssize_t ret;
B
Bryan Schumaker 已提交
301 302

	saved_cred = override_creds(id_resolver_cache);
303
	rkey = nfs_idmap_request_key(name, namelen, type, idmap);
B
Bryan Schumaker 已提交
304
	revert_creds(saved_cred);
305

B
Bryan Schumaker 已提交
306 307 308 309 310 311 312 313 314 315 316 317
	if (IS_ERR(rkey)) {
		ret = PTR_ERR(rkey);
		goto out;
	}

	rcu_read_lock();
	rkey->perm |= KEY_USR_VIEW;

	ret = key_validate(rkey);
	if (ret < 0)
		goto out_up;

318
	payload = rcu_dereference(rkey->payload.rcudata);
B
Bryan Schumaker 已提交
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
	if (IS_ERR_OR_NULL(payload)) {
		ret = PTR_ERR(payload);
		goto out_up;
	}

	ret = payload->datalen;
	if (ret > 0 && ret <= data_size)
		memcpy(data, payload->data, ret);
	else
		ret = -EINVAL;

out_up:
	rcu_read_unlock();
	key_put(rkey);
out:
	return ret;
}

/* ID -> Name */
338 339
static ssize_t nfs_idmap_lookup_name(__u32 id, const char *type, char *buf,
				     size_t buflen, struct idmap *idmap)
B
Bryan Schumaker 已提交
340 341 342 343 344 345
{
	char id_str[NFS_UINT_MAXLEN];
	int id_len;
	ssize_t ret;

	id_len = snprintf(id_str, sizeof(id_str), "%u", id);
346
	ret = nfs_idmap_get_key(id_str, id_len, type, buf, buflen, idmap);
B
Bryan Schumaker 已提交
347 348 349 350 351 352
	if (ret < 0)
		return -EINVAL;
	return ret;
}

/* Name -> ID */
353 354
static int nfs_idmap_lookup_id(const char *name, size_t namelen, const char *type,
			       __u32 *id, struct idmap *idmap)
B
Bryan Schumaker 已提交
355 356 357 358 359 360
{
	char id_str[NFS_UINT_MAXLEN];
	long id_long;
	ssize_t data_size;
	int ret = 0;

361
	data_size = nfs_idmap_get_key(name, namelen, type, id_str, NFS_UINT_MAXLEN, idmap);
B
Bryan Schumaker 已提交
362 363 364
	if (data_size <= 0) {
		ret = -EINVAL;
	} else {
365
		ret = kstrtol(id_str, 10, &id_long);
B
Bryan Schumaker 已提交
366 367 368 369 370
		*id = (__u32)id_long;
	}
	return ret;
}

371
/* idmap classic begins here */
372

373 374
enum {
	Opt_find_uid, Opt_find_gid, Opt_find_user, Opt_find_group, Opt_find_err
L
Linus Torvalds 已提交
375 376
};

377 378 379 380 381 382
static const match_table_t nfs_idmap_tokens = {
	{ Opt_find_uid, "uid:%s" },
	{ Opt_find_gid, "gid:%s" },
	{ Opt_find_user, "user:%s" },
	{ Opt_find_group, "group:%s" },
	{ Opt_find_err, NULL }
L
Linus Torvalds 已提交
383 384
};

385
static int nfs_idmap_legacy_upcall(struct key_construction *, const char *, void *);
C
Chuck Lever 已提交
386 387
static ssize_t idmap_pipe_downcall(struct file *, const char __user *,
				   size_t);
388
static void idmap_release_pipe(struct inode *);
C
Chuck Lever 已提交
389
static void idmap_pipe_destroy_msg(struct rpc_pipe_msg *);
L
Linus Torvalds 已提交
390

391
static const struct rpc_pipe_ops idmap_upcall_ops = {
392
	.upcall		= rpc_pipe_generic_upcall,
C
Chuck Lever 已提交
393
	.downcall	= idmap_pipe_downcall,
394
	.release_pipe	= idmap_release_pipe,
C
Chuck Lever 已提交
395
	.destroy_msg	= idmap_pipe_destroy_msg,
L
Linus Torvalds 已提交
396 397
};

398
static struct key_type key_type_id_resolver_legacy = {
399
	.name		= "id_legacy",
D
David Howells 已提交
400 401 402
	.preparse	= user_preparse,
	.free_preparse	= user_free_preparse,
	.instantiate	= generic_key_instantiate,
403 404 405 406 407 408 409
	.revoke		= user_revoke,
	.destroy	= user_destroy,
	.describe	= user_describe,
	.read		= user_read,
	.request_key	= nfs_idmap_legacy_upcall,
};

410 411
static void nfs_idmap_pipe_destroy(struct dentry *dir,
		struct rpc_pipe_dir_object *pdo)
412
{
413 414 415
	struct idmap *idmap = pdo->pdo_data;
	struct rpc_pipe *pipe = idmap->idmap_pipe;

416
	if (pipe->dentry) {
417
		rpc_unlink(pipe->dentry);
418 419
		pipe->dentry = NULL;
	}
420 421
}

422 423
static int nfs_idmap_pipe_create(struct dentry *dir,
		struct rpc_pipe_dir_object *pdo)
424
{
425 426
	struct idmap *idmap = pdo->pdo_data;
	struct rpc_pipe *pipe = idmap->idmap_pipe;
427 428 429 430 431 432 433 434 435
	struct dentry *dentry;

	dentry = rpc_mkpipe_dentry(dir, "idmap", idmap, pipe);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
	pipe->dentry = dentry;
	return 0;
}

436 437 438 439
static const struct rpc_pipe_dir_object_ops nfs_idmap_pipe_dir_object_ops = {
	.create = nfs_idmap_pipe_create,
	.destroy = nfs_idmap_pipe_destroy,
};
440

441
int
442
nfs_idmap_new(struct nfs_client *clp)
L
Linus Torvalds 已提交
443 444
{
	struct idmap *idmap;
445
	struct rpc_pipe *pipe;
446
	int error;
L
Linus Torvalds 已提交
447

C
Chuck Lever 已提交
448 449 450
	idmap = kzalloc(sizeof(*idmap), GFP_KERNEL);
	if (idmap == NULL)
		return -ENOMEM;
L
Linus Torvalds 已提交
451

452 453 454 455
	rpc_init_pipe_dir_object(&idmap->idmap_pdo,
			&nfs_idmap_pipe_dir_object_ops,
			idmap);

456 457 458
	pipe = rpc_mkpipe_data(&idmap_upcall_ops, 0);
	if (IS_ERR(pipe)) {
		error = PTR_ERR(pipe);
459
		goto err;
460 461
	}
	idmap->idmap_pipe = pipe;
462
	mutex_init(&idmap->idmap_mutex);
L
Linus Torvalds 已提交
463

464 465 466 467 468 469
	error = rpc_add_pipe_dir_object(clp->cl_net,
			&clp->cl_rpcclient->cl_pipedir_objects,
			&idmap->idmap_pdo);
	if (error)
		goto err_destroy_pipe;

L
Linus Torvalds 已提交
470
	clp->cl_idmap = idmap;
471
	return 0;
472 473 474 475 476
err_destroy_pipe:
	rpc_destroy_pipe_data(idmap->idmap_pipe);
err:
	kfree(idmap);
	return error;
L
Linus Torvalds 已提交
477 478 479
}

void
480
nfs_idmap_delete(struct nfs_client *clp)
L
Linus Torvalds 已提交
481 482 483 484 485 486
{
	struct idmap *idmap = clp->cl_idmap;

	if (!idmap)
		return;
	clp->cl_idmap = NULL;
487 488 489 490
	rpc_remove_pipe_dir_object(clp->cl_net,
			&clp->cl_rpcclient->cl_pipedir_objects,
			&idmap->idmap_pdo);
	rpc_destroy_pipe_data(idmap->idmap_pipe);
L
Linus Torvalds 已提交
491 492 493
	kfree(idmap);
}

494
int nfs_idmap_init(void)
L
Linus Torvalds 已提交
495
{
496 497 498 499 500 501
	int ret;
	ret = nfs_idmap_init_keyring();
	if (ret != 0)
		goto out;
out:
	return ret;
L
Linus Torvalds 已提交
502 503
}

504
void nfs_idmap_quit(void)
L
Linus Torvalds 已提交
505
{
506
	nfs_idmap_quit_keyring();
L
Linus Torvalds 已提交
507 508
}

509 510
static int nfs_idmap_prepare_message(char *desc, struct idmap *idmap,
				     struct idmap_msg *im,
511
				     struct rpc_pipe_msg *msg)
L
Linus Torvalds 已提交
512
{
513 514
	substring_t substr;
	int token, ret;
L
Linus Torvalds 已提交
515

516 517
	im->im_type = IDMAP_TYPE_GROUP;
	token = match_token(desc, nfs_idmap_tokens, &substr);
L
Linus Torvalds 已提交
518

519 520 521 522 523 524 525
	switch (token) {
	case Opt_find_uid:
		im->im_type = IDMAP_TYPE_USER;
	case Opt_find_gid:
		im->im_conv = IDMAP_CONV_NAMETOID;
		ret = match_strlcpy(im->im_name, &substr, IDMAP_NAMESZ);
		break;
L
Linus Torvalds 已提交
526

527 528 529 530 531 532
	case Opt_find_user:
		im->im_type = IDMAP_TYPE_USER;
	case Opt_find_group:
		im->im_conv = IDMAP_CONV_IDTONAME;
		ret = match_int(&substr, &im->im_id);
		break;
L
Linus Torvalds 已提交
533

534 535
	default:
		ret = -EINVAL;
L
Linus Torvalds 已提交
536 537 538
		goto out;
	}

539 540
	msg->data = im;
	msg->len  = sizeof(struct idmap_msg);
L
Linus Torvalds 已提交
541

542
out:
C
Chuck Lever 已提交
543
	return ret;
L
Linus Torvalds 已提交
544 545
}

546 547
static bool
nfs_idmap_prepare_pipe_upcall(struct idmap *idmap,
548
		struct idmap_legacy_upcalldata *data)
549
{
550
	if (idmap->idmap_upcall_data != NULL) {
551 552 553
		WARN_ON_ONCE(1);
		return false;
	}
554
	idmap->idmap_upcall_data = data;
555 556 557 558 559 560
	return true;
}

static void
nfs_idmap_complete_pipe_upcall_locked(struct idmap *idmap, int ret)
{
561
	struct key_construction *cons = idmap->idmap_upcall_data->key_cons;
562

563 564
	kfree(idmap->idmap_upcall_data);
	idmap->idmap_upcall_data = NULL;
565 566 567 568 569 570
	complete_request_key(cons, ret);
}

static void
nfs_idmap_abort_pipe_upcall(struct idmap *idmap, int ret)
{
571
	if (idmap->idmap_upcall_data != NULL)
572 573 574
		nfs_idmap_complete_pipe_upcall_locked(idmap, ret);
}

575 576 577
static int nfs_idmap_legacy_upcall(struct key_construction *cons,
				   const char *op,
				   void *aux)
L
Linus Torvalds 已提交
578
{
579
	struct idmap_legacy_upcalldata *data;
580
	struct rpc_pipe_msg *msg;
L
Linus Torvalds 已提交
581
	struct idmap_msg *im;
582 583
	struct idmap *idmap = (struct idmap *)aux;
	struct key *key = cons->key;
584
	int ret = -ENOMEM;
L
Linus Torvalds 已提交
585

586
	/* msg and im are freed in idmap_pipe_destroy_msg */
587
	data = kzalloc(sizeof(*data), GFP_KERNEL);
588
	if (!data)
589
		goto out1;
L
Linus Torvalds 已提交
590

591 592 593
	msg = &data->pipe_msg;
	im = &data->idmap_msg;
	data->idmap = idmap;
594
	data->key_cons = cons;
595 596

	ret = nfs_idmap_prepare_message(key->description, idmap, im, msg);
597 598
	if (ret < 0)
		goto out2;
L
Linus Torvalds 已提交
599

600
	ret = -EAGAIN;
601
	if (!nfs_idmap_prepare_pipe_upcall(idmap, data))
602
		goto out2;
L
Linus Torvalds 已提交
603

604 605
	ret = rpc_queue_upcall(idmap->idmap_pipe, msg);
	if (ret < 0)
606
		nfs_idmap_abort_pipe_upcall(idmap, ret);
L
Linus Torvalds 已提交
607

608
	return ret;
609
out2:
610
	kfree(data);
611
out1:
612
	complete_request_key(cons, ret);
C
Chuck Lever 已提交
613
	return ret;
L
Linus Torvalds 已提交
614 615
}

616
static int nfs_idmap_instantiate(struct key *key, struct key *authkey, char *data, size_t datalen)
L
Linus Torvalds 已提交
617
{
618
	return key_instantiate_and_link(key, data, datalen,
619 620 621
					id_resolver_cache->thread_keyring,
					authkey);
}
L
Linus Torvalds 已提交
622

623 624 625
static int nfs_idmap_read_and_verify_message(struct idmap_msg *im,
		struct idmap_msg *upcall,
		struct key *key, struct key *authkey)
626 627
{
	char id_str[NFS_UINT_MAXLEN];
628
	size_t len;
629
	int ret = -ENOKEY;
L
Linus Torvalds 已提交
630

631 632 633
	/* ret = -ENOKEY */
	if (upcall->im_type != im->im_type || upcall->im_conv != im->im_conv)
		goto out;
634 635
	switch (im->im_conv) {
	case IDMAP_CONV_NAMETOID:
636 637
		if (strcmp(upcall->im_name, im->im_name) != 0)
			break;
638 639 640
		/* Note: here we store the NUL terminator too */
		len = sprintf(id_str, "%d", im->im_id) + 1;
		ret = nfs_idmap_instantiate(key, authkey, id_str, len);
641 642
		break;
	case IDMAP_CONV_IDTONAME:
643 644
		if (upcall->im_id != im->im_id)
			break;
645 646
		len = strlen(im->im_name);
		ret = nfs_idmap_instantiate(key, authkey, im->im_name, len);
647
		break;
648 649
	default:
		ret = -EINVAL;
L
Linus Torvalds 已提交
650
	}
651
out:
L
Linus Torvalds 已提交
652 653 654 655 656 657
	return ret;
}

static ssize_t
idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
{
A
Al Viro 已提交
658
	struct rpc_inode *rpci = RPC_I(file_inode(filp));
L
Linus Torvalds 已提交
659
	struct idmap *idmap = (struct idmap *)rpci->private;
660
	struct key_construction *cons;
661
	struct idmap_msg im;
662
	size_t namelen_in;
663
	int ret = -ENOKEY;
L
Linus Torvalds 已提交
664

665 666 667 668
	/* If instantiation is successful, anyone waiting for key construction
	 * will have been woken up and someone else may now have used
	 * idmap_key_cons - so after this point we may no longer touch it.
	 */
669
	if (idmap->idmap_upcall_data == NULL)
670
		goto out_noupcall;
671

672
	cons = idmap->idmap_upcall_data->key_cons;
673

674 675
	if (mlen != sizeof(im)) {
		ret = -ENOSPC;
L
Linus Torvalds 已提交
676 677 678
		goto out;
	}

679 680
	if (copy_from_user(&im, src, mlen) != 0) {
		ret = -EFAULT;
L
Linus Torvalds 已提交
681
		goto out;
682
	}
L
Linus Torvalds 已提交
683

684
	if (!(im.im_status & IDMAP_STATUS_SUCCESS)) {
685 686
		ret = -ENOKEY;
		goto out;
L
Linus Torvalds 已提交
687 688
	}

689 690 691
	namelen_in = strnlen(im.im_name, IDMAP_NAMESZ);
	if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) {
		ret = -EINVAL;
L
Linus Torvalds 已提交
692
		goto out;
693
}
L
Linus Torvalds 已提交
694

695 696 697
	ret = nfs_idmap_read_and_verify_message(&im,
			&idmap->idmap_upcall_data->idmap_msg,
			cons->key, cons->authkey);
698 699 700 701 702
	if (ret >= 0) {
		key_set_timeout(cons->key, nfs_idmap_cache_timeout);
		ret = mlen;
	}

L
Linus Torvalds 已提交
703
out:
704 705
	nfs_idmap_complete_pipe_upcall_locked(idmap, ret);
out_noupcall:
L
Linus Torvalds 已提交
706 707 708
	return ret;
}

A
Adrian Bunk 已提交
709
static void
L
Linus Torvalds 已提交
710 711
idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg)
{
712 713 714 715
	struct idmap_legacy_upcalldata *data = container_of(msg,
			struct idmap_legacy_upcalldata,
			pipe_msg);
	struct idmap *idmap = data->idmap;
716 717 718

	if (msg->errno)
		nfs_idmap_abort_pipe_upcall(idmap, msg->errno);
719 720 721 722 723 724 725
}

static void
idmap_release_pipe(struct inode *inode)
{
	struct rpc_inode *rpci = RPC_I(inode);
	struct idmap *idmap = (struct idmap *)rpci->private;
726 727

	nfs_idmap_abort_pipe_upcall(idmap, -EPIPE);
L
Linus Torvalds 已提交
728 729
}

730
int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, kuid_t *uid)
L
Linus Torvalds 已提交
731
{
732
	struct idmap *idmap = server->nfs_client->cl_idmap;
733 734
	__u32 id = -1;
	int ret = 0;
L
Linus Torvalds 已提交
735

736 737 738 739 740 741 742
	if (!nfs_map_string_to_numeric(name, namelen, &id))
		ret = nfs_idmap_lookup_id(name, namelen, "uid", &id, idmap);
	if (ret == 0) {
		*uid = make_kuid(&init_user_ns, id);
		if (!uid_valid(*uid))
			ret = -ERANGE;
	}
743
	trace_nfs4_map_name_to_uid(name, namelen, id, ret);
744
	return ret;
L
Linus Torvalds 已提交
745 746
}

747
int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, kgid_t *gid)
L
Linus Torvalds 已提交
748
{
749
	struct idmap *idmap = server->nfs_client->cl_idmap;
750 751
	__u32 id = -1;
	int ret = 0;
L
Linus Torvalds 已提交
752

753 754 755 756 757 758 759
	if (!nfs_map_string_to_numeric(name, namelen, &id))
		ret = nfs_idmap_lookup_id(name, namelen, "gid", &id, idmap);
	if (ret == 0) {
		*gid = make_kgid(&init_user_ns, id);
		if (!gid_valid(*gid))
			ret = -ERANGE;
	}
760
	trace_nfs4_map_group_to_gid(name, namelen, id, ret);
761
	return ret;
L
Linus Torvalds 已提交
762 763
}

764
int nfs_map_uid_to_name(const struct nfs_server *server, kuid_t uid, char *buf, size_t buflen)
L
Linus Torvalds 已提交
765
{
766
	struct idmap *idmap = server->nfs_client->cl_idmap;
767
	int ret = -EINVAL;
768
	__u32 id;
L
Linus Torvalds 已提交
769

770
	id = from_kuid(&init_user_ns, uid);
771
	if (!(server->caps & NFS_CAP_UIDGID_NOMAP))
772
		ret = nfs_idmap_lookup_name(id, "user", buf, buflen, idmap);
773
	if (ret < 0)
774
		ret = nfs_map_numeric_to_string(id, buf, buflen);
775
	trace_nfs4_map_uid_to_name(buf, ret, id, ret);
776
	return ret;
L
Linus Torvalds 已提交
777
}
778
int nfs_map_gid_to_group(const struct nfs_server *server, kgid_t gid, char *buf, size_t buflen)
L
Linus Torvalds 已提交
779
{
780
	struct idmap *idmap = server->nfs_client->cl_idmap;
781
	int ret = -EINVAL;
782
	__u32 id;
L
Linus Torvalds 已提交
783

784
	id = from_kgid(&init_user_ns, gid);
785
	if (!(server->caps & NFS_CAP_UIDGID_NOMAP))
786
		ret = nfs_idmap_lookup_name(id, "group", buf, buflen, idmap);
787
	if (ret < 0)
788
		ret = nfs_map_numeric_to_string(id, buf, buflen);
789
	trace_nfs4_map_gid_to_group(buf, ret, id, ret);
790
	return ret;
L
Linus Torvalds 已提交
791
}