xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs4_idmap.c (revision bbf21555)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 
26 /*
27  * There are well defined policies for mapping uid and gid values to and
28  * from utf8 strings, as specified in RFC 7530. The protocol ops that are
29  * most significantly affected by any changes in policy are GETATTR and
30  * SETATTR, as these have different behavior depending on whether the id
31  * mapping code is executing on the client or server. Thus, the following
32  * rules represents the latest incantation of the id mapping policies.
33  *
34  * 1) For the case in which the nfsmapid(8) daemon has _never_ been
35  *    started, the policy is to _always_ work with stringified uid's
36  *    and gid's
37  *
38  * 2) For the case in which the nfsmapid(8) daemon _was_ started but
39  *    has either died or become unresponsive, the mapping policies are
40  *    as follows:
41  *
42  *                      Server                             Client
43  *         .-------------------------------.---------------------------------.
44  *         |                               |                                 |
45  *         | . Respond to req by replying  | . If attr string does not have  |
46  *         |   success and map the [u/g]id |   '@' sign, attempt to decode   |
47  *         |   into its literal id string  |   a stringified id; map to      |
48  *         |                               |   *ID_NOBODY if not an encoded  |
49  *         |                               |   id.                           |
50  *         |                               |                                 |
51  * GETATTR |                               | . If attr string _does_ have    |
52  *         |                               |   '@' sign			     |
53  *         |                               |   Map to *ID_NOBODY on failure. |
54  *         |                               |                                 |
55  *         | nfs_idmap_*id_str             | nfs_idmap_str_*id               |
56  *         +-------------------------------+---------------------------------+
57  *         |                               |                                 |
58  *         | . Respond to req by returning | . _Must_ map the user's passed  |
59  *         |  ECOMM, which will be mapped  |  in [u/g]id into it's network   |
60  *         |  to NFS4ERR_DELAY to clnt     |  attr string, so contact the    |
61  *         |                               |  daemon, retrying forever if    |
62  *         |   Server must not allow the   |  necessary, unless interrupted  |
63  * SETATTR |   mapping to *ID_NOBODY upon  |                                 |
64  *         |   lack of communication with  |   Client _should_ specify the   |
65  *         |   the daemon, which could     |   correct attr string for a     |
66  *         |   result in the file being    |   SETATTR operation, otherwise  |
67  *         |   inadvertently given away !  |   it can also result in the     |
68  *         |                               |   file being inadvertently      |
69  *         |                               |   given away !                  |
70  *         |                               |                                 |
71  *         | nfs_idmap_str_*id             |   nfs_idmap_*id_str             |
72  *         `-------------------------------'---------------------------------'
73  *
74  * 3) Lastly, in order to leverage better cache utilization whenever
75  *    communication with nfsmapid(8) is currently hindered, cache
76  *    entry eviction is throttled whenever nfsidmap_daemon_dh == NULL.
77  *
78  *
79  *  Server-side behavior for upcall communication errors
80  *  ====================================================
81  *
82  *   GETATTR - Server-side GETATTR *id to attr string conversion policies
83  *             for unresponsive/dead nfsmapid(8) daemon
84  *
85  *	a) If the *id is *ID_NOBODY, the string "nobody" is returned
86  *
87  *	b) If the *id is not *ID_NOBODY _and_ the nfsmapid(8) daemon
88  *	   _is_ operational, the daemon is contacted to convert the
89  *	   [u/g]id into a string of type "[user/group]@domain"
90  *
91  *	c) If the nfsmapid(8) daemon has died or has become unresponsive,
92  *	   the server returns status == NFS4_OK for the GETATTR operation,
93  *	   and returns a strigified [u/g]id to let the client map it into
94  *	   the appropriate value.
95  *
96  *   SETATTR - Server-side SETATTR attr string to *id conversion policies
97  *             for unresponsive/dead nfsmapid(8) daemon
98  *
99  *	a) If the otw string is a stringified uid (ie. does _not_ contain
100  *	   an '@' sign and is of the form "12345") then the literal uid is
101  *	   decoded and it is used to perform the mapping.
102  *
103  *	b) If, on the other hand, the otw string _is_ of the form
104  *	   "[user/group]@domain" and problems arise contacting nfsmapid(8),
105  *	   the SETATTR operation _must_ fail w/NFS4ERR_DELAY, as the server
106  *	   cannot default to *ID_NOBODY, which would allow a file to be
107  *	   given away by setting it's owner or owner_group to "nobody".
108  */
109 #include <sys/param.h>
110 #include <sys/errno.h>
111 #include <sys/disp.h>
112 #include <sys/vfs.h>
113 #include <sys/vnode.h>
114 #include <sys/cred.h>
115 #include <sys/cmn_err.h>
116 #include <sys/systm.h>
117 #include <sys/kmem.h>
118 #include <sys/pathname.h>
119 #include <sys/utsname.h>
120 #include <sys/debug.h>
121 #include <sys/sysmacros.h>
122 #include <sys/list.h>
123 #include <sys/sunddi.h>
124 #include <sys/dnlc.h>
125 #include <sys/sdt.h>
126 #include <sys/pkp_hash.h>
127 #include <nfs/nfs4.h>
128 #include <nfs/rnode4.h>
129 #include <nfs/nfsid_map.h>
130 #include <nfs/nfs4_idmap_impl.h>
131 #include <nfs/nfssys.h>
132 
133 /*
134  * Truly global modular globals
135  */
136 zone_key_t			nfsidmap_zone_key;
137 static list_t			nfsidmap_globals_list;
138 static kmutex_t			nfsidmap_globals_lock;
139 static kmem_cache_t		*nfsidmap_cache;
140 static int			nfs4_idcache_tout;
141 
142 /*
143  * Some useful macros
144  */
145 #define		MOD2(a, pow_of_2)	((a) & ((pow_of_2) - 1))
146 #define		_CACHE_TOUT		(60*60)		/* secs in 1 hour */
147 #define		TIMEOUT(x)		(gethrestime_sec() > \
148 					((x) + nfs4_idcache_tout))
149 /*
150  * Max length of valid id string including the trailing null
151  */
152 #define		_MAXIDSTRLEN		11
153 
154 #define		ID_HASH(id, hash)					\
155 {									\
156 	(hash) = MOD2(((id) ^ NFSID_CACHE_ANCHORS), NFSID_CACHE_ANCHORS); \
157 }
158 
159 /*
160  * Prototypes
161  */
162 
163 static void	*nfs_idmap_init_zone(zoneid_t);
164 static void	 nfs_idmap_fini_zone(zoneid_t, void *);
165 
166 static int	 is_stringified_id(utf8string *);
167 static void	 nfs_idmap_i2s_literal(uid_t, utf8string *);
168 static int	 nfs_idmap_s2i_literal(utf8string *, uid_t *, int);
169 static void	 nfs_idmap_reclaim(void *);
170 static void	 nfs_idmap_cache_reclaim(idmap_cache_info_t *);
171 static void	 nfs_idmap_cache_create(idmap_cache_info_t *, const char *);
172 static void	 nfs_idmap_cache_destroy(idmap_cache_info_t *);
173 static void	 nfs_idmap_cache_flush(idmap_cache_info_t *);
174 
175 static uint_t	 nfs_idmap_cache_s2i_lkup(idmap_cache_info_t *, utf8string *,
176 			uint_t *, uid_t *);
177 
178 static uint_t	 nfs_idmap_cache_i2s_lkup(idmap_cache_info_t *, uid_t,
179 			uint_t *, utf8string *);
180 
181 static void	 nfs_idmap_cache_s2i_insert(idmap_cache_info_t *, uid_t,
182 			utf8string *, hash_stat, uint_t);
183 
184 static void	 nfs_idmap_cache_i2s_insert(idmap_cache_info_t *, uid_t,
185 			utf8string *, hash_stat, uint_t);
186 
187 static void	 nfs_idmap_cache_rment(nfsidmap_t *);
188 
189 /*
190  * Initialization routine for NFSv4 id mapping
191  */
192 void
nfs_idmap_init(void)193 nfs_idmap_init(void)
194 {
195 	/*
196 	 * Initialize the kmem cache
197 	 */
198 	nfsidmap_cache = kmem_cache_create("NFS_idmap_cache",
199 	    sizeof (nfsidmap_t), 0, NULL, NULL, nfs_idmap_reclaim, NULL,
200 	    NULL, 0);
201 	/*
202 	 * If not set in "/etc/system", set to default value
203 	 */
204 	if (!nfs4_idcache_tout)
205 		nfs4_idcache_tout = _CACHE_TOUT;
206 	/*
207 	 * Initialize the list of nfsidmap_globals
208 	 */
209 	mutex_init(&nfsidmap_globals_lock, NULL, MUTEX_DEFAULT, NULL);
210 	list_create(&nfsidmap_globals_list, sizeof (struct nfsidmap_globals),
211 	    offsetof(struct nfsidmap_globals, nig_link));
212 	/*
213 	 * Initialize the zone_key_t for per-zone idmaps
214 	 */
215 	zone_key_create(&nfsidmap_zone_key, nfs_idmap_init_zone, NULL,
216 	    nfs_idmap_fini_zone);
217 }
218 
219 /*
220  * Called only when module was not loaded properly
221  */
222 void
nfs_idmap_fini(void)223 nfs_idmap_fini(void)
224 {
225 	(void) zone_key_delete(nfsidmap_zone_key);
226 	list_destroy(&nfsidmap_globals_list);
227 	mutex_destroy(&nfsidmap_globals_lock);
228 	kmem_cache_destroy(nfsidmap_cache);
229 }
230 
231 /*ARGSUSED*/
232 static void *
nfs_idmap_init_zone(zoneid_t zoneid)233 nfs_idmap_init_zone(zoneid_t zoneid)
234 {
235 	struct nfsidmap_globals *nig;
236 
237 	nig = kmem_alloc(sizeof (*nig), KM_SLEEP);
238 	nig->nig_msg_done = 0;
239 	mutex_init(&nig->nfsidmap_daemon_lock, NULL, MUTEX_DEFAULT, NULL);
240 
241 	/*
242 	 * nfsidmap certainly isn't running.
243 	 */
244 	nig->nfsidmap_pid = NOPID;
245 	nig->nfsidmap_daemon_dh = NULL;
246 
247 	/*
248 	 * Create the idmap caches
249 	 */
250 	nfs_idmap_cache_create(&nig->u2s_ci, "u2s_cache");
251 	nig->u2s_ci.nfsidmap_daemon_dh = &nig->nfsidmap_daemon_dh;
252 	nfs_idmap_cache_create(&nig->s2u_ci, "s2u_cache");
253 	nig->s2u_ci.nfsidmap_daemon_dh = &nig->nfsidmap_daemon_dh;
254 	nfs_idmap_cache_create(&nig->g2s_ci, "g2s_cache");
255 	nig->g2s_ci.nfsidmap_daemon_dh = &nig->nfsidmap_daemon_dh;
256 	nfs_idmap_cache_create(&nig->s2g_ci, "s2g_cache");
257 	nig->s2g_ci.nfsidmap_daemon_dh = &nig->nfsidmap_daemon_dh;
258 
259 	/*
260 	 * Add to global list.
261 	 */
262 	mutex_enter(&nfsidmap_globals_lock);
263 	list_insert_head(&nfsidmap_globals_list, nig);
264 	mutex_exit(&nfsidmap_globals_lock);
265 
266 	return (nig);
267 }
268 
269 /*ARGSUSED*/
270 static void
nfs_idmap_fini_zone(zoneid_t zoneid,void * arg)271 nfs_idmap_fini_zone(zoneid_t zoneid, void *arg)
272 {
273 	struct nfsidmap_globals *nig = arg;
274 
275 	/*
276 	 * Remove from list.
277 	 */
278 	mutex_enter(&nfsidmap_globals_lock);
279 	list_remove(&nfsidmap_globals_list, nig);
280 	/*
281 	 * Destroy the idmap caches
282 	 */
283 	nfs_idmap_cache_destroy(&nig->u2s_ci);
284 	nfs_idmap_cache_destroy(&nig->s2u_ci);
285 	nfs_idmap_cache_destroy(&nig->g2s_ci);
286 	nfs_idmap_cache_destroy(&nig->s2g_ci);
287 	mutex_exit(&nfsidmap_globals_lock);
288 	/*
289 	 * Cleanup
290 	 */
291 	if (nig->nfsidmap_daemon_dh)
292 		door_ki_rele(nig->nfsidmap_daemon_dh);
293 	mutex_destroy(&nig->nfsidmap_daemon_lock);
294 	kmem_free(nig, sizeof (*nig));
295 }
296 
297 /*
298  * Convert a user utf-8 string identifier into its local uid.
299  */
300 int
nfs_idmap_str_uid(utf8string * u8s,uid_t * uid,bool_t isserver)301 nfs_idmap_str_uid(utf8string *u8s, uid_t *uid, bool_t isserver)
302 {
303 	int			error;
304 	uint_t			hashno = 0;
305 	const char		*whoami = "nfs_idmap_str_uid";
306 	struct nfsidmap_globals *nig;
307 	struct mapid_arg	*mapargp;
308 	struct mapid_res	mapres;
309 	struct mapid_res	*mapresp = &mapres;
310 	struct mapid_res	*resp = mapresp;
311 	door_arg_t		door_args;
312 	door_handle_t		dh;
313 
314 	nig = zone_getspecific(nfsidmap_zone_key, nfs_zone());
315 	ASSERT(nig != NULL);
316 
317 	if (!u8s || !u8s->utf8string_val || u8s->utf8string_len == 0 ||
318 	    (u8s->utf8string_val[0] == '\0')) {
319 		*uid = UID_NOBODY;
320 		return (isserver ? EINVAL : 0);
321 	}
322 
323 	/*
324 	 * If "nobody", just short circuit and bail
325 	 */
326 	if (bcmp(u8s->utf8string_val, "nobody", 6) == 0) {
327 		*uid = UID_NOBODY;
328 		return (0);
329 	}
330 
331 	/*
332 	 * Start-off with upcalls disabled, and once nfsmapid(8) is up and
333 	 * running, we'll leverage it's first flush to let the kernel know
334 	 * when it's up and available to perform mappings. Also, on client
335 	 * only, be smarter about when to issue upcalls by checking the
336 	 * string for existence of an '@' sign. If no '@' sign, then we just
337 	 * make our best effort to decode the string ourselves.
338 	 */
339 retry:
340 	mutex_enter(&nig->nfsidmap_daemon_lock);
341 	dh = nig->nfsidmap_daemon_dh;
342 	if (dh)
343 		door_ki_hold(dh);
344 	mutex_exit(&nig->nfsidmap_daemon_lock);
345 
346 	if (dh == NULL || nig->nfsidmap_pid == curproc->p_pid ||
347 	    (!utf8_strchr(u8s, '@') && !isserver)) {
348 		if (dh)
349 			door_ki_rele(dh);
350 		error = nfs_idmap_s2i_literal(u8s, uid, isserver);
351 		/*
352 		 * If we get a numeric value, but we only do so because
353 		 * we are nfsmapid, return ENOTSUP to indicate a valid
354 		 * response, but not to cache it.
355 		 */
356 		if (!error && nig->nfsidmap_pid == curproc->p_pid)
357 			return (ENOTSUP);
358 		return (error);
359 	}
360 
361 	/* cache hit */
362 	if (nfs_idmap_cache_s2i_lkup(&nig->s2u_ci, u8s, &hashno, uid)) {
363 		door_ki_rele(dh);
364 		return (0);
365 	}
366 
367 	/* cache miss */
368 	mapargp = kmem_alloc(MAPID_ARG_LEN(u8s->utf8string_len), KM_SLEEP);
369 	mapargp->cmd = NFSMAPID_STR_UID;
370 	mapargp->u_arg.len = u8s->utf8string_len;
371 	(void) bcopy(u8s->utf8string_val, mapargp->str, mapargp->u_arg.len);
372 	mapargp->str[mapargp->u_arg.len] = '\0';
373 
374 	door_args.data_ptr = (char *)mapargp;
375 	door_args.data_size = MAPID_ARG_LEN(mapargp->u_arg.len);
376 	door_args.desc_ptr = NULL;
377 	door_args.desc_num = 0;
378 	door_args.rbuf = (char *)mapresp;
379 	door_args.rsize = sizeof (struct mapid_res);
380 
381 	error = door_ki_upcall_limited(dh, &door_args, NULL, SIZE_MAX, 0);
382 	if (!error) {
383 		resp = (struct mapid_res *)door_args.rbuf;
384 
385 		/* Should never provide daemon with bad args */
386 		ASSERT(resp->status != NFSMAPID_INVALID);
387 
388 		switch (resp->status) {
389 		case NFSMAPID_OK:
390 			/*
391 			 * Valid mapping. Cache it.
392 			 */
393 			*uid = resp->u_res.uid;
394 			nfs_idmap_cache_s2i_insert(&nig->s2u_ci, *uid,
395 			    u8s, HQ_HASH_HINT, hashno);
396 			break;
397 
398 		case NFSMAPID_NUMSTR:
399 			/*
400 			 * string came in as stringified id. Don't cache !
401 			 *
402 			 * nfsmapid(8) semantics have changed in order to
403 			 * support diskless clients. Thus, for stringified
404 			 * id's that have passwd/group entries, we'll go
405 			 * ahead and map them, returning no error.
406 			 */
407 			*uid = resp->u_res.uid;
408 			break;
409 
410 		case NFSMAPID_BADDOMAIN:
411 			/*
412 			 * Make the offending "user@domain" string readily
413 			 * available to D scripts that enable the probe.
414 			 */
415 			DTRACE_PROBE1(nfs4__str__uid, char *, mapargp->str);
416 			/* FALLTHROUGH */
417 
418 		case NFSMAPID_INVALID:
419 		case NFSMAPID_UNMAPPABLE:
420 		case NFSMAPID_INTERNAL:
421 		case NFSMAPID_BADID:
422 		case NFSMAPID_NOTFOUND:
423 		default:
424 			/*
425 			 * For now, treat all of these errors as equal.
426 			 *
427 			 * Return error on the server side, then the
428 			 * server returns NFS4_BADOWNER to the client.
429 			 * On client side, just map to UID_NOBODY.
430 			 */
431 			if (isserver)
432 				error = EPERM;
433 			else
434 				*uid = UID_NOBODY;
435 			break;
436 		}
437 		kmem_free(mapargp, MAPID_ARG_LEN(u8s->utf8string_len));
438 		if (resp != mapresp)
439 			kmem_free(door_args.rbuf, door_args.rsize);
440 		door_ki_rele(dh);
441 		return (error);
442 	}
443 
444 	kmem_free(mapargp, MAPID_ARG_LEN(u8s->utf8string_len));
445 	/*
446 	 * We got some door error
447 	 */
448 	switch (error) {
449 	case EINTR:
450 		/*
451 		 * If we took an interrupt we have to bail out.
452 		 */
453 		if (ttolwp(curthread) && ISSIG(curthread, JUSTLOOKING)) {
454 			door_ki_rele(dh);
455 			return (EINTR);
456 		}
457 
458 		/*
459 		 * We may have gotten EINTR for other reasons like the
460 		 * door being revoked on us, instead of trying to
461 		 * extract this out of the door handle, sleep
462 		 * and try again, if still revoked we will get EBADF
463 		 * next time through.
464 		 */
465 		/* FALLTHROUGH */
466 	case EAGAIN:    /* process may be forking */
467 		door_ki_rele(dh);
468 		/*
469 		 * Back off for a bit
470 		 */
471 		delay(hz);
472 		goto retry;
473 	default:	/* Unknown must be fatal */
474 	case EBADF:	/* Invalid door */
475 	case EINVAL:	/* Not a door, wrong target */
476 		/*
477 		 * A fatal door error, if our failing door handle is the
478 		 * current door handle, clean up our state and
479 		 * mark the server dead.
480 		 */
481 		mutex_enter(&nig->nfsidmap_daemon_lock);
482 		if (dh == nig->nfsidmap_daemon_dh) {
483 			door_ki_rele(nig->nfsidmap_daemon_dh);
484 			nig->nfsidmap_daemon_dh = NULL;
485 		}
486 		mutex_exit(&nig->nfsidmap_daemon_lock);
487 		door_ki_rele(dh);
488 
489 		if (isserver)
490 			return (ECOMM);
491 
492 		/*
493 		 * Note: We've already done optimizations above to check
494 		 *	 for '@' sign, so if we can't comm w/nfsmapid, we
495 		 *	 _know_ this _can't_ be a stringified uid.
496 		 */
497 		if (!nig->nig_msg_done) {
498 			zcmn_err(getzoneid(), CE_WARN,
499 			    "!%s: Can't communicate with mapping daemon "
500 			    "nfsmapid", whoami);
501 
502 			nig->nig_msg_done = 1;
503 		}
504 		*uid = UID_NOBODY;
505 		return (0);
506 	}
507 	/* NOTREACHED */
508 }
509 
510 /*
511  * Convert a uid into its utf-8 string representation.
512  */
513 int
nfs_idmap_uid_str(uid_t uid,utf8string * u8s,bool_t isserver)514 nfs_idmap_uid_str(uid_t uid, utf8string *u8s, bool_t isserver)
515 {
516 	int			error;
517 	uint_t			hashno = 0;
518 	const char		*whoami = "nfs_idmap_uid_str";
519 	struct nfsidmap_globals	*nig;
520 	struct mapid_arg	maparg;
521 	struct mapid_res	mapres;
522 	struct mapid_res	*mapresp = &mapres;
523 	struct mapid_res	*resp = mapresp;
524 	door_arg_t		door_args;
525 	door_handle_t		dh;
526 
527 	nig = zone_getspecific(nfsidmap_zone_key, nfs_zone());
528 	ASSERT(nig != NULL);
529 
530 	/*
531 	 * If the supplied uid is "nobody", then we don't look at the
532 	 * cache, since we DON'T cache it in the u2s_cache. We cannot
533 	 * tell two strings apart from caching the same uid.
534 	 */
535 	if (uid == UID_NOBODY) {
536 		(void) str_to_utf8("nobody", u8s);
537 		return (0);
538 	}
539 
540 	/*
541 	 * Start-off with upcalls disabled, and once nfsmapid(8) is
542 	 * up and running, we'll leverage it's first flush to let the
543 	 * kernel know when it's up and available to perform mappings.
544 	 * We fall back to answering with stringified uid's.
545 	 */
546 retry:
547 	mutex_enter(&nig->nfsidmap_daemon_lock);
548 	dh = nig->nfsidmap_daemon_dh;
549 	if (dh)
550 		door_ki_hold(dh);
551 	mutex_exit(&nig->nfsidmap_daemon_lock);
552 
553 	if (dh == NULL || nig->nfsidmap_pid == curproc->p_pid) {
554 		if (dh)
555 			door_ki_rele(dh);
556 		nfs_idmap_i2s_literal(uid, u8s);
557 		return (0);
558 	}
559 
560 	/* cache hit */
561 	if (nfs_idmap_cache_i2s_lkup(&nig->u2s_ci, uid, &hashno, u8s)) {
562 		door_ki_rele(dh);
563 		return (0);
564 	}
565 
566 	/* cache miss */
567 	maparg.cmd = NFSMAPID_UID_STR;
568 	maparg.u_arg.uid = uid;
569 
570 	door_args.data_ptr = (char *)&maparg;
571 	door_args.data_size = sizeof (struct mapid_arg);
572 	door_args.desc_ptr = NULL;
573 	door_args.desc_num = 0;
574 	door_args.rbuf = (char *)mapresp;
575 	door_args.rsize = sizeof (struct mapid_res);
576 
577 	error = door_ki_upcall_limited(dh, &door_args, NULL, SIZE_MAX, 0);
578 	if (!error) {
579 		resp = (struct mapid_res *)door_args.rbuf;
580 
581 		/* Should never provide daemon with bad args */
582 		ASSERT(resp->status != NFSMAPID_INVALID);
583 
584 		switch (resp->status) {
585 		case NFSMAPID_OK:
586 			/*
587 			 * We now have a valid result from the
588 			 * user-land daemon, so cache the result (if need be).
589 			 * Load return value first then do the caches.
590 			 */
591 			(void) str_to_utf8(resp->str, u8s);
592 			nfs_idmap_cache_i2s_insert(&nig->u2s_ci, uid,
593 			    u8s, HQ_HASH_HINT, hashno);
594 			break;
595 
596 		case NFSMAPID_INVALID:
597 		case NFSMAPID_UNMAPPABLE:
598 		case NFSMAPID_INTERNAL:
599 		case NFSMAPID_BADDOMAIN:
600 		case NFSMAPID_BADID:
601 		case NFSMAPID_NOTFOUND:
602 		default:
603 			/*
604 			 * For now, treat all of these errors as equal.
605 			 */
606 			error = EPERM;
607 			break;
608 		}
609 
610 		if (resp != mapresp)
611 			kmem_free(door_args.rbuf, door_args.rsize);
612 		door_ki_rele(dh);
613 		return (error);
614 	}
615 
616 	/*
617 	 * We got some door error
618 	 */
619 	switch (error) {
620 	case EINTR:
621 		/*
622 		 * If we took an interrupt we have to bail out.
623 		 */
624 		if (ttolwp(curthread) && ISSIG(curthread, JUSTLOOKING)) {
625 			door_ki_rele(dh);
626 			return (EINTR);
627 		}
628 
629 		/*
630 		 * We may have gotten EINTR for other reasons like the
631 		 * door being revoked on us, instead of trying to
632 		 * extract this out of the door handle, sleep
633 		 * and try again, if still revoked we will get EBADF
634 		 * next time through.
635 		 */
636 		/* FALLTHROUGH */
637 	case EAGAIN:    /* process may be forking */
638 		door_ki_rele(dh);
639 		/*
640 		 * Back off for a bit
641 		 */
642 		delay(hz);
643 		goto retry;
644 	default:	/* Unknown must be fatal */
645 	case EBADF:	/* Invalid door */
646 	case EINVAL:	/* Not a door, wrong target */
647 		/*
648 		 * A fatal door error, if our failing door handle is the
649 		 * current door handle, clean up our state and
650 		 * mark the server dead.
651 		 */
652 		mutex_enter(&nig->nfsidmap_daemon_lock);
653 		if (dh == nig->nfsidmap_daemon_dh) {
654 			door_ki_rele(nig->nfsidmap_daemon_dh);
655 			nig->nfsidmap_daemon_dh = NULL;
656 		}
657 		mutex_exit(&nig->nfsidmap_daemon_lock);
658 		door_ki_rele(dh);
659 
660 		/*
661 		 * Log error on client-side only
662 		 */
663 		if (!nig->nig_msg_done && !isserver) {
664 			zcmn_err(getzoneid(), CE_WARN,
665 			    "!%s: Can't communicate with mapping daemon "
666 			    "nfsmapid", whoami);
667 
668 			nig->nig_msg_done = 1;
669 		}
670 		nfs_idmap_i2s_literal(uid, u8s);
671 		return (0);
672 	}
673 	/* NOTREACHED */
674 }
675 
676 /*
677  * Convert a group utf-8 string identifier into its local gid.
678  */
679 int
nfs_idmap_str_gid(utf8string * u8s,gid_t * gid,bool_t isserver)680 nfs_idmap_str_gid(utf8string *u8s, gid_t *gid, bool_t isserver)
681 {
682 	int			error;
683 	uint_t			hashno = 0;
684 	const char		*whoami = "nfs_idmap_str_gid";
685 	struct nfsidmap_globals *nig;
686 	struct mapid_arg	*mapargp;
687 	struct mapid_res	mapres;
688 	struct mapid_res	*mapresp = &mapres;
689 	struct mapid_res	*resp = mapresp;
690 	door_arg_t		door_args;
691 	door_handle_t		dh;
692 
693 	nig = zone_getspecific(nfsidmap_zone_key, nfs_zone());
694 	ASSERT(nig != NULL);
695 
696 	if (!u8s || !u8s->utf8string_val || u8s->utf8string_len == 0 ||
697 	    (u8s->utf8string_val[0] == '\0')) {
698 		*gid = GID_NOBODY;
699 		return (isserver ? EINVAL : 0);
700 	}
701 
702 	/*
703 	 * If "nobody", just short circuit and bail
704 	 */
705 	if (bcmp(u8s->utf8string_val, "nobody", 6) == 0) {
706 		*gid = GID_NOBODY;
707 		return (0);
708 	}
709 
710 	/*
711 	 * Start-off with upcalls disabled, and once nfsmapid(8) is up and
712 	 * running, we'll leverage it's first flush to let the kernel know
713 	 * when it's up and available to perform mappings. Also, on client
714 	 * only, be smarter about when to issue upcalls by checking the
715 	 * string for existence of an '@' sign. If no '@' sign, then we just
716 	 * make our best effort to decode the string ourselves.
717 	 */
718 retry:
719 	mutex_enter(&nig->nfsidmap_daemon_lock);
720 	dh = nig->nfsidmap_daemon_dh;
721 	if (dh)
722 		door_ki_hold(dh);
723 	mutex_exit(&nig->nfsidmap_daemon_lock);
724 
725 	if (dh == NULL || nig->nfsidmap_pid == curproc->p_pid ||
726 	    (!utf8_strchr(u8s, '@') && !isserver)) {
727 		if (dh)
728 			door_ki_rele(dh);
729 		error = nfs_idmap_s2i_literal(u8s, gid, isserver);
730 		/*
731 		 * If we get a numeric value, but we only do so because
732 		 * we are nfsmapid, return ENOTSUP to indicate a valid
733 		 * response, but not to cache it.
734 		 */
735 		if (!error && nig->nfsidmap_pid == curproc->p_pid)
736 			return (ENOTSUP);
737 		return (error);
738 	}
739 
740 	/* cache hit */
741 	if (nfs_idmap_cache_s2i_lkup(&nig->s2g_ci, u8s, &hashno, gid)) {
742 		door_ki_rele(dh);
743 		return (0);
744 	}
745 
746 	/* cache miss */
747 	mapargp = kmem_alloc(MAPID_ARG_LEN(u8s->utf8string_len), KM_SLEEP);
748 	mapargp->cmd = NFSMAPID_STR_GID;
749 	mapargp->u_arg.len = u8s->utf8string_len;
750 	(void) bcopy(u8s->utf8string_val, mapargp->str, mapargp->u_arg.len);
751 	mapargp->str[mapargp->u_arg.len] = '\0';
752 
753 	door_args.data_ptr = (char *)mapargp;
754 	door_args.data_size = MAPID_ARG_LEN(mapargp->u_arg.len);
755 	door_args.desc_ptr = NULL;
756 	door_args.desc_num = 0;
757 	door_args.rbuf = (char *)mapresp;
758 	door_args.rsize = sizeof (struct mapid_res);
759 
760 	error = door_ki_upcall_limited(dh, &door_args, NULL, SIZE_MAX, 0);
761 	if (!error) {
762 		resp = (struct mapid_res *)door_args.rbuf;
763 
764 		/* Should never provide daemon with bad args */
765 		ASSERT(resp->status != NFSMAPID_INVALID);
766 
767 		switch (resp->status) {
768 		case NFSMAPID_OK:
769 			/*
770 			 * Valid mapping. Cache it.
771 			 */
772 			*gid = resp->u_res.gid;
773 			error = 0;
774 			nfs_idmap_cache_s2i_insert(&nig->s2g_ci, *gid,
775 			    u8s, HQ_HASH_HINT, hashno);
776 			break;
777 
778 		case NFSMAPID_NUMSTR:
779 			/*
780 			 * string came in as stringified id. Don't cache !
781 			 *
782 			 * nfsmapid(8) semantics have changed in order to
783 			 * support diskless clients. Thus, for stringified
784 			 * id's that have passwd/group entries, we'll go
785 			 * ahead and map them, returning no error.
786 			 */
787 			*gid = resp->u_res.gid;
788 			break;
789 
790 		case NFSMAPID_BADDOMAIN:
791 			/*
792 			 * Make the offending "group@domain" string readily
793 			 * available to D scripts that enable the probe.
794 			 */
795 			DTRACE_PROBE1(nfs4__str__gid, char *, mapargp->str);
796 			/* FALLTHROUGH */
797 
798 		case NFSMAPID_INVALID:
799 		case NFSMAPID_UNMAPPABLE:
800 		case NFSMAPID_INTERNAL:
801 		case NFSMAPID_BADID:
802 		case NFSMAPID_NOTFOUND:
803 		default:
804 			/*
805 			 * For now, treat all of these errors as equal.
806 			 *
807 			 * Return error on the server side, then the
808 			 * server returns NFS4_BADOWNER to the client.
809 			 * On client side, just map to GID_NOBODY.
810 			 */
811 			if (isserver)
812 				error = EPERM;
813 			else
814 				*gid = GID_NOBODY;
815 			break;
816 		}
817 		kmem_free(mapargp, MAPID_ARG_LEN(u8s->utf8string_len));
818 		if (resp != mapresp)
819 			kmem_free(door_args.rbuf, door_args.rsize);
820 		door_ki_rele(dh);
821 		return (error);
822 	}
823 
824 	kmem_free(mapargp, MAPID_ARG_LEN(u8s->utf8string_len));
825 	/*
826 	 * We got some door error
827 	 */
828 	switch (error) {
829 	case EINTR:
830 		/*
831 		 * If we took an interrupt we have to bail out.
832 		 */
833 		if (ttolwp(curthread) && ISSIG(curthread, JUSTLOOKING)) {
834 			door_ki_rele(dh);
835 			return (EINTR);
836 		}
837 
838 		/*
839 		 * We may have gotten EINTR for other reasons like the
840 		 * door being revoked on us, instead of trying to
841 		 * extract this out of the door handle, sleep
842 		 * and try again, if still revoked we will get EBADF
843 		 * next time through.
844 		 */
845 		/* FALLTHROUGH */
846 	case EAGAIN:    /* process may be forking */
847 		door_ki_rele(dh);
848 		/*
849 		 * Back off for a bit
850 		 */
851 		delay(hz);
852 		goto retry;
853 	default:	/* Unknown must be fatal */
854 	case EBADF:	/* Invalid door */
855 	case EINVAL:	/* Not a door, wrong target */
856 		/*
857 		 * A fatal door error, clean up our state and
858 		 * mark the server dead.
859 		 */
860 
861 		mutex_enter(&nig->nfsidmap_daemon_lock);
862 		if (dh == nig->nfsidmap_daemon_dh) {
863 			door_ki_rele(nig->nfsidmap_daemon_dh);
864 			nig->nfsidmap_daemon_dh = NULL;
865 		}
866 		mutex_exit(&nig->nfsidmap_daemon_lock);
867 		door_ki_rele(dh);
868 
869 		if (isserver)
870 			return (ECOMM);
871 
872 		/*
873 		 * Note: We've already done optimizations above to check
874 		 *	 for '@' sign, so if we can't comm w/nfsmapid, we
875 		 *	 _know_ this _can't_ be a stringified gid.
876 		 */
877 		if (!nig->nig_msg_done) {
878 			zcmn_err(getzoneid(), CE_WARN,
879 			    "!%s: Can't communicate with mapping daemon "
880 			    "nfsmapid", whoami);
881 
882 			nig->nig_msg_done = 1;
883 		}
884 		*gid = GID_NOBODY;
885 		return (0);
886 	}
887 	/* NOTREACHED */
888 }
889 
890 /*
891  * Convert a gid into its utf-8 string representation.
892  */
893 int
nfs_idmap_gid_str(gid_t gid,utf8string * u8s,bool_t isserver)894 nfs_idmap_gid_str(gid_t gid, utf8string *u8s, bool_t isserver)
895 {
896 	int			error;
897 	uint_t			hashno = 0;
898 	const char		*whoami = "nfs_idmap_gid_str";
899 	struct nfsidmap_globals	*nig;
900 	struct mapid_arg	maparg;
901 	struct mapid_res	mapres;
902 	struct mapid_res	*mapresp = &mapres;
903 	struct mapid_res	*resp = mapresp;
904 	door_arg_t		door_args;
905 	door_handle_t		dh;
906 
907 	nig = zone_getspecific(nfsidmap_zone_key, nfs_zone());
908 	ASSERT(nig != NULL);
909 
910 	/*
911 	 * If the supplied gid is "nobody", then we don't look at the
912 	 * cache, since we DON'T cache it in the u2s_cache. We cannot
913 	 * tell two strings apart from caching the same gid.
914 	 */
915 	if (gid == GID_NOBODY) {
916 		(void) str_to_utf8("nobody", u8s);
917 		return (0);
918 	}
919 
920 	/*
921 	 * Start-off with upcalls disabled, and once nfsmapid(8) is
922 	 * up and running, we'll leverage it's first flush to let the
923 	 * kernel know when it's up and available to perform mappings.
924 	 * We fall back to answering with stringified gid's.
925 	 */
926 retry:
927 	mutex_enter(&nig->nfsidmap_daemon_lock);
928 	dh = nig->nfsidmap_daemon_dh;
929 	if (dh)
930 		door_ki_hold(dh);
931 	mutex_exit(&nig->nfsidmap_daemon_lock);
932 
933 	if (dh == NULL || nig->nfsidmap_pid == curproc->p_pid) {
934 		if (dh)
935 			door_ki_rele(dh);
936 		nfs_idmap_i2s_literal(gid, u8s);
937 		return (0);
938 	}
939 
940 	/* cache hit */
941 	if (nfs_idmap_cache_i2s_lkup(&nig->g2s_ci, gid, &hashno, u8s)) {
942 		door_ki_rele(dh);
943 		return (0);
944 	}
945 
946 	/* cache miss */
947 	maparg.cmd = NFSMAPID_GID_STR;
948 	maparg.u_arg.gid = gid;
949 
950 	door_args.data_ptr = (char *)&maparg;
951 	door_args.data_size = sizeof (struct mapid_arg);
952 	door_args.desc_ptr = NULL;
953 	door_args.desc_num = 0;
954 	door_args.rbuf = (char *)mapresp;
955 	door_args.rsize = sizeof (struct mapid_res);
956 
957 	error = door_ki_upcall_limited(dh, &door_args, NULL, SIZE_MAX, 0);
958 	if (!error) {
959 		resp = (struct mapid_res *)door_args.rbuf;
960 
961 		/* Should never provide daemon with bad args */
962 		ASSERT(resp->status != NFSMAPID_INVALID);
963 
964 		switch (resp->status) {
965 		case NFSMAPID_OK:
966 			/*
967 			 * We now have a valid result from the
968 			 * user-land daemon, so cache the result (if need be).
969 			 * Load return value first then do the caches.
970 			 */
971 			(void) str_to_utf8(resp->str, u8s);
972 			nfs_idmap_cache_i2s_insert(&nig->g2s_ci, gid,
973 			    u8s, HQ_HASH_HINT, hashno);
974 			break;
975 
976 		case NFSMAPID_INVALID:
977 		case NFSMAPID_UNMAPPABLE:
978 		case NFSMAPID_INTERNAL:
979 		case NFSMAPID_BADDOMAIN:
980 		case NFSMAPID_BADID:
981 		case NFSMAPID_NOTFOUND:
982 		default:
983 			/*
984 			 * For now, treat all of these errors as equal.
985 			 */
986 			error = EPERM;
987 			break;
988 		}
989 
990 		if (resp != mapresp)
991 			kmem_free(door_args.rbuf, door_args.rsize);
992 		door_ki_rele(dh);
993 		return (error);
994 	}
995 
996 	/*
997 	 * We got some door error
998 	 */
999 	switch (error) {
1000 	case EINTR:
1001 		/*
1002 		 * If we took an interrupt we have to bail out.
1003 		 */
1004 		if (ttolwp(curthread) && ISSIG(curthread, JUSTLOOKING)) {
1005 			door_ki_rele(dh);
1006 			return (EINTR);
1007 		}
1008 
1009 		/*
1010 		 * We may have gotten EINTR for other reasons like the
1011 		 * door being revoked on us, instead of trying to
1012 		 * extract this out of the door handle, sleep
1013 		 * and try again, if still revoked we will get EBADF
1014 		 * next time through.
1015 		 */
1016 		/* FALLTHROUGH */
1017 	case EAGAIN:    /* process may be forking */
1018 		door_ki_rele(dh);
1019 		/*
1020 		 * Back off for a bit
1021 		 */
1022 		delay(hz);
1023 		goto retry;
1024 	default:	/* Unknown must be fatal */
1025 	case EBADF:	/* Invalid door */
1026 	case EINVAL:	/* Not a door, wrong target */
1027 		/*
1028 		 * A fatal door error, if our failing door handle is the
1029 		 * current door handle, clean up our state and
1030 		 * mark the server dead.
1031 		 */
1032 		mutex_enter(&nig->nfsidmap_daemon_lock);
1033 		if (dh == nig->nfsidmap_daemon_dh) {
1034 			door_ki_rele(nig->nfsidmap_daemon_dh);
1035 			nig->nfsidmap_daemon_dh = NULL;
1036 		}
1037 		door_ki_rele(dh);
1038 		mutex_exit(&nig->nfsidmap_daemon_lock);
1039 
1040 		/*
1041 		 * Log error on client-side only
1042 		 */
1043 		if (!nig->nig_msg_done && !isserver) {
1044 			zcmn_err(getzoneid(), CE_WARN,
1045 			    "!%s: Can't communicate with mapping daemon "
1046 			    "nfsmapid", whoami);
1047 
1048 			nig->nig_msg_done = 1;
1049 		}
1050 		nfs_idmap_i2s_literal(gid, u8s);
1051 		return (0);
1052 	}
1053 	/* NOTREACHED */
1054 }
1055 
1056 /* -- idmap cache management -- */
1057 
1058 /*
1059  * Cache creation and initialization routine
1060  */
1061 static void
nfs_idmap_cache_create(idmap_cache_info_t * cip,const char * name)1062 nfs_idmap_cache_create(idmap_cache_info_t *cip, const char *name)
1063 {
1064 	int		 i;
1065 	nfsidhq_t	*hq = NULL;
1066 
1067 	cip->table = kmem_alloc((NFSID_CACHE_ANCHORS * sizeof (nfsidhq_t)),
1068 	    KM_SLEEP);
1069 
1070 	for (i = 0, hq = cip->table; i < NFSID_CACHE_ANCHORS; i++, hq++) {
1071 		hq->hq_que_forw = hq;
1072 		hq->hq_que_back = hq;
1073 		mutex_init(&(hq->hq_lock), NULL, MUTEX_DEFAULT, NULL);
1074 	}
1075 	cip->name = name;
1076 }
1077 
1078 /*
1079  * Cache destruction routine
1080  *
1081  * Ops per hash queue
1082  *
1083  * - dequeue cache entries
1084  * - release string storage per entry
1085  * - release cache entry storage
1086  * - destroy HQ lock when HQ is empty
1087  * - once all HQ's empty, release HQ storage
1088  */
1089 static void
nfs_idmap_cache_destroy(idmap_cache_info_t * cip)1090 nfs_idmap_cache_destroy(idmap_cache_info_t *cip)
1091 {
1092 	int		 i;
1093 	nfsidhq_t	*hq;
1094 
1095 	ASSERT(MUTEX_HELD(&nfsidmap_globals_lock));
1096 	nfs_idmap_cache_flush(cip);
1097 	/*
1098 	 * We can safely destroy per-queue locks since the only
1099 	 * other entity that could be mucking with this table is the
1100 	 * kmem reaper thread which does everything under
1101 	 * nfsidmap_globals_lock (which we're holding).
1102 	 */
1103 	for (i = 0, hq = cip->table; i < NFSID_CACHE_ANCHORS; i++, hq++)
1104 		mutex_destroy(&(hq->hq_lock));
1105 	kmem_free(cip->table, NFSID_CACHE_ANCHORS * sizeof (nfsidhq_t));
1106 }
1107 
1108 void
nfs_idmap_args(struct nfsidmap_args * idmp)1109 nfs_idmap_args(struct nfsidmap_args *idmp)
1110 {
1111 	struct nfsidmap_globals *nig;
1112 
1113 	nig = zone_getspecific(nfsidmap_zone_key, nfs_zone());
1114 	ASSERT(nig != NULL);
1115 
1116 	nfs_idmap_cache_flush(&nig->u2s_ci);
1117 	nfs_idmap_cache_flush(&nig->s2u_ci);
1118 	nfs_idmap_cache_flush(&nig->g2s_ci);
1119 	nfs_idmap_cache_flush(&nig->s2g_ci);
1120 
1121 	/*
1122 	 * nfsmapid(8) up and running; enable upcalls
1123 	 * State:
1124 	 *	0	Just flush caches
1125 	 *	1	Re-establish door knob
1126 	 */
1127 	if (idmp->state) {
1128 		/*
1129 		 * When reestablishing the nfsmapid we need to
1130 		 * not only purge the idmap cache but also
1131 		 * the dnlc as it will have cached uid/gid's.
1132 		 * While heavyweight, this should almost never happen
1133 		 */
1134 		dnlc_purge();
1135 
1136 		/*
1137 		 * Invalidate the attrs of all rnodes to force new uid and gids
1138 		 */
1139 		nfs4_rnode_invalidate(NULL);
1140 
1141 		mutex_enter(&nig->nfsidmap_daemon_lock);
1142 		if (nig->nfsidmap_daemon_dh)
1143 			door_ki_rele(nig->nfsidmap_daemon_dh);
1144 		nig->nfsidmap_daemon_dh = door_ki_lookup(idmp->did);
1145 		nig->nfsidmap_pid = curproc->p_pid;
1146 		nig->nig_msg_done = 0;
1147 		mutex_exit(&nig->nfsidmap_daemon_lock);
1148 	}
1149 }
1150 
1151 /*
1152  * Cache flush routine
1153  *
1154  *	The only serialization required it to hold the hash chain lock
1155  *	when destroying cache entries.  There is no need to prevent access
1156  *	to all hash chains while flushing.  It is possible that (valid)
1157  *	entries could be cached in later hash chains after we start flushing.
1158  *	It is unfortunate that the entry will be instantly destroyed, but
1159  *	it isn't a major concern.  This is only a cache.  It'll be repopulated.
1160  *
1161  * Ops per hash queue
1162  *
1163  * - dequeue cache entries
1164  * - release string storage per entry
1165  * - release cache entry storage
1166  */
1167 static void
nfs_idmap_cache_flush(idmap_cache_info_t * cip)1168 nfs_idmap_cache_flush(idmap_cache_info_t *cip)
1169 {
1170 	int		 i;
1171 	nfsidmap_t	*p, *next;
1172 	nfsidhq_t	*hq;
1173 
1174 	for (i = 0, hq = cip->table; i < NFSID_CACHE_ANCHORS; i++, hq++) {
1175 
1176 		mutex_enter(&(hq->hq_lock));
1177 
1178 		/*
1179 		 * remove list from hash header so we can release
1180 		 * the lock early.
1181 		 */
1182 		p = hq->hq_lru_forw;
1183 		hq->hq_que_forw = hq;
1184 		hq->hq_que_back = hq;
1185 
1186 		mutex_exit(&(hq->hq_lock));
1187 
1188 		/*
1189 		 * Iterate over the orphan'd list and free all elements.
1190 		 * There's no need to bother with remque since we're
1191 		 * freeing the entire list.
1192 		 */
1193 		while (p != (nfsidmap_t *)hq) {
1194 			next = p->id_forw;
1195 			if (p->id_val != 0)
1196 				kmem_free(p->id_val, p->id_len);
1197 			kmem_cache_free(nfsidmap_cache, p);
1198 			p = next;
1199 		}
1200 
1201 	}
1202 }
1203 
1204 static void
nfs_idmap_cache_reclaim(idmap_cache_info_t * cip)1205 nfs_idmap_cache_reclaim(idmap_cache_info_t *cip)
1206 {
1207 	nfsidhq_t		*hq;
1208 	nfsidmap_t		*pprev = NULL;
1209 	int			 i;
1210 	nfsidmap_t		*p;
1211 
1212 	ASSERT(cip != NULL && cip->table != NULL);
1213 
1214 	/*
1215 	 * If the daemon is down, do not flush anything
1216 	 */
1217 	if ((*cip->nfsidmap_daemon_dh) == NULL)
1218 		return;
1219 
1220 	for (i = 0, hq = cip->table; i < NFSID_CACHE_ANCHORS; i++, hq++) {
1221 		if (!mutex_tryenter(&(hq->hq_lock)))
1222 			continue;
1223 
1224 		/*
1225 		 * Start at end of list and work backwards since LRU
1226 		 */
1227 		for (p = hq->hq_lru_back; p != (nfsidmap_t *)hq; p = pprev) {
1228 			pprev = p->id_back;
1229 
1230 			/*
1231 			 * List is LRU. If trailing end does not
1232 			 * contain stale entries, then no need to
1233 			 * continue.
1234 			 */
1235 			if (!TIMEOUT(p->id_time))
1236 				break;
1237 
1238 			nfs_idmap_cache_rment(p);
1239 		}
1240 		mutex_exit(&(hq->hq_lock));
1241 	}
1242 }
1243 
1244 /*
1245  * Callback reclaim function for VM.  We reap timed-out entries from all hash
1246  * tables in all zones.
1247  */
1248 /* ARGSUSED */
1249 void
nfs_idmap_reclaim(void * arg)1250 nfs_idmap_reclaim(void *arg)
1251 {
1252 	struct nfsidmap_globals *nig;
1253 
1254 	mutex_enter(&nfsidmap_globals_lock);
1255 	for (nig = list_head(&nfsidmap_globals_list); nig != NULL;
1256 	    nig = list_next(&nfsidmap_globals_list, nig)) {
1257 		nfs_idmap_cache_reclaim(&nig->u2s_ci);
1258 		nfs_idmap_cache_reclaim(&nig->s2u_ci);
1259 		nfs_idmap_cache_reclaim(&nig->g2s_ci);
1260 		nfs_idmap_cache_reclaim(&nig->s2g_ci);
1261 	}
1262 	mutex_exit(&nfsidmap_globals_lock);
1263 }
1264 
1265 /*
1266  * Search the specified cache for the existence of the specified utf-8
1267  * string. If found, the corresponding mapping is returned in id_buf and
1268  * the cache entry is updated to the head of the LRU list. The computed
1269  * hash queue number, is returned in hashno.
1270  *
1271  * cip    - cache info ptr
1272  * u8s    - utf8 string to resolve
1273  * hashno - hash number, retval
1274  * id_buf - if found, id for u8s
1275  */
1276 static uint_t
nfs_idmap_cache_s2i_lkup(idmap_cache_info_t * cip,utf8string * u8s,uint_t * hashno,uid_t * id_buf)1277 nfs_idmap_cache_s2i_lkup(idmap_cache_info_t *cip, utf8string *u8s,
1278     uint_t *hashno, uid_t *id_buf)
1279 {
1280 	nfsidmap_t	*p;
1281 	nfsidmap_t	*pnext;
1282 	nfsidhq_t	*hq;
1283 	char		*rqst_c_str;
1284 	uint_t		 rqst_len;
1285 	uint_t		 found_stat = 0;
1286 
1287 	if ((rqst_c_str = utf8_to_str(u8s, &rqst_len, NULL)) == NULL) {
1288 		/*
1289 		 * Illegal string, return not found.
1290 		 */
1291 		return (0);
1292 	}
1293 
1294 	/*
1295 	 * Compute hash queue
1296 	 */
1297 	*hashno = pkp_tab_hash(rqst_c_str, rqst_len - 1);
1298 	hq = &cip->table[*hashno];
1299 
1300 	/*
1301 	 * Look for the entry in the HQ
1302 	 */
1303 	mutex_enter(&(hq->hq_lock));
1304 	for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
1305 
1306 		pnext = p->id_forw;
1307 
1308 		/*
1309 		 * Check entry for staleness first, as user's id
1310 		 * may have changed and may need to be remapped.
1311 		 * Note that we don't evict entries from the cache
1312 		 * if we're having trouble contacting nfsmapid(8)
1313 		 */
1314 		if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
1315 			nfs_idmap_cache_rment(p);
1316 			continue;
1317 		}
1318 
1319 		/*
1320 		 * Compare equal length strings
1321 		 */
1322 		if (p->id_len == (rqst_len - 1)) {
1323 			if (bcmp(p->id_val, rqst_c_str, (rqst_len - 1)) == 0) {
1324 				/*
1325 				 * Found it. Update it and load return value.
1326 				 */
1327 				*id_buf = p->id_no;
1328 				remque(p);
1329 				insque(p, hq);
1330 				p->id_time = gethrestime_sec();
1331 
1332 				found_stat = 1;
1333 				break;
1334 			}
1335 		}
1336 	}
1337 	mutex_exit(&(hq->hq_lock));
1338 
1339 	if (rqst_c_str != NULL)
1340 		kmem_free(rqst_c_str, rqst_len);
1341 
1342 	return (found_stat);
1343 }
1344 
1345 /*
1346  * Search the specified cache for the existence of the specified utf8
1347  * string, as it may have been inserted before this instance got a chance
1348  * to do it. If NOT found, then a new entry is allocated for the specified
1349  * cache, and inserted. The hash queue number is obtained from hash_number
1350  * if the behavior is HQ_HASH_HINT, or computed otherwise.
1351  *
1352  * cip         - cache info ptr
1353  * id          - id result from upcall
1354  * u8s         - utf8 string to resolve
1355  * behavior    - hash algorithm behavior
1356  * hash_number - hash number iff hint
1357  */
1358 static void
nfs_idmap_cache_s2i_insert(idmap_cache_info_t * cip,uid_t id,utf8string * u8s,hash_stat behavior,uint_t hash_number)1359 nfs_idmap_cache_s2i_insert(idmap_cache_info_t *cip, uid_t id, utf8string *u8s,
1360     hash_stat behavior, uint_t hash_number)
1361 {
1362 	uint_t			 hashno;
1363 	char			*c_str;
1364 	nfsidhq_t		*hq;
1365 	nfsidmap_t		*newp;
1366 	nfsidmap_t		*p;
1367 	nfsidmap_t		*pnext;
1368 	uint_t			 c_len;
1369 
1370 	/*
1371 	 * This shouldn't fail, since already successful at lkup.
1372 	 * So, if it does happen, just drop the request-to-insert
1373 	 * on the floor.
1374 	 */
1375 	if ((c_str = utf8_to_str(u8s, &c_len, NULL)) == NULL)
1376 		return;
1377 
1378 	/*
1379 	 * Obtain correct hash queue to insert new entry in
1380 	 */
1381 	switch (behavior) {
1382 		case HQ_HASH_HINT:
1383 			hashno = hash_number;
1384 			break;
1385 
1386 		case HQ_HASH_FIND:
1387 		default:
1388 			hashno = pkp_tab_hash(c_str, c_len - 1);
1389 			break;
1390 	}
1391 	hq = &cip->table[hashno];
1392 
1393 
1394 	/*
1395 	 * Look for an existing entry in the cache. If one exists
1396 	 * update it, and return. Otherwise, allocate a new cache
1397 	 * entry, initialize it and insert it.
1398 	 */
1399 	mutex_enter(&(hq->hq_lock));
1400 	for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
1401 
1402 		pnext = p->id_forw;
1403 
1404 		/*
1405 		 * Check entry for staleness first, as user's id
1406 		 * may have changed and may need to be remapped.
1407 		 * Note that we don't evict entries from the cache
1408 		 * if we're having trouble contacting nfsmapid(8)
1409 		 */
1410 		if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
1411 			nfs_idmap_cache_rment(p);
1412 			continue;
1413 		}
1414 
1415 		/*
1416 		 * Compare equal length strings
1417 		 */
1418 		if (p->id_len == (c_len - 1)) {
1419 			if (bcmp(p->id_val, c_str, (c_len - 1)) == 0) {
1420 				/*
1421 				 * Move to front, and update time.
1422 				 */
1423 				remque(p);
1424 				insque(p, hq);
1425 				p->id_time = gethrestime_sec();
1426 
1427 				mutex_exit(&(hq->hq_lock));
1428 				kmem_free(c_str, c_len);
1429 				return;
1430 			}
1431 		}
1432 	}
1433 
1434 	/*
1435 	 * Not found ! Alloc, init and insert new entry
1436 	 */
1437 	newp = kmem_cache_alloc(nfsidmap_cache, KM_SLEEP);
1438 	newp->id_len = u8s->utf8string_len;
1439 	newp->id_val = kmem_alloc(u8s->utf8string_len, KM_SLEEP);
1440 	bcopy(u8s->utf8string_val, newp->id_val, u8s->utf8string_len);
1441 	newp->id_no = id;
1442 	newp->id_time = gethrestime_sec();
1443 	insque(newp, hq);
1444 
1445 	mutex_exit(&(hq->hq_lock));
1446 	kmem_free(c_str, c_len);
1447 }
1448 
1449 /*
1450  * Search the specified cache for the existence of the specified id.
1451  * If found, the corresponding mapping is returned in u8s and the
1452  * cache entry is updated to the head of the LRU list. The computed
1453  * hash queue number, is returned in hashno.
1454  *
1455  * cip    - cache info ptr
1456  * id     - id to resolve
1457  * hashno - hash number, retval
1458  * u8s    - if found, utf8 str for id
1459  */
1460 static uint_t
nfs_idmap_cache_i2s_lkup(idmap_cache_info_t * cip,uid_t id,uint_t * hashno,utf8string * u8s)1461 nfs_idmap_cache_i2s_lkup(idmap_cache_info_t *cip, uid_t id, uint_t *hashno,
1462     utf8string *u8s)
1463 {
1464 	uint_t			 found_stat = 0;
1465 	nfsidmap_t		*p;
1466 	nfsidmap_t		*pnext;
1467 	nfsidhq_t		*hq;
1468 	uint_t			 hash;
1469 
1470 	/*
1471 	 * Compute hash queue
1472 	 */
1473 	ID_HASH(id, hash);
1474 	*hashno = hash;
1475 	hq = &cip->table[hash];
1476 
1477 	/*
1478 	 * Look for the entry in the HQ
1479 	 */
1480 	mutex_enter(&(hq->hq_lock));
1481 	for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
1482 
1483 		pnext = p->id_forw;
1484 
1485 		/*
1486 		 * Check entry for staleness first, as user's id
1487 		 * may have changed and may need to be remapped.
1488 		 * Note that we don't evict entries from the cache
1489 		 * if we're having trouble contacting nfsmapid(8)
1490 		 */
1491 		if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
1492 			nfs_idmap_cache_rment(p);
1493 			continue;
1494 		}
1495 
1496 		if (p->id_no == id) {
1497 
1498 			/*
1499 			 * Found it. Load return value and move to head
1500 			 */
1501 			ASSERT(u8s->utf8string_val == NULL);
1502 			u8s->utf8string_len = p->id_len;
1503 			u8s->utf8string_val = kmem_alloc(p->id_len, KM_SLEEP);
1504 			bcopy(p->id_val, u8s->utf8string_val, p->id_len);
1505 
1506 			remque(p);
1507 			insque(p, hq);
1508 			p->id_time = gethrestime_sec();
1509 
1510 			found_stat = 1;
1511 			break;
1512 		}
1513 	}
1514 	mutex_exit(&(hq->hq_lock));
1515 
1516 	return (found_stat);
1517 }
1518 
1519 /*
1520  * Search the specified cache for the existence of the specified id,
1521  * as it may have been inserted before this instance got a chance to
1522  * do it. If NOT found, then a new entry is allocated for the specified
1523  * cache, and inserted. The hash queue number is obtained from hash_number
1524  * if the behavior is HQ_HASH_HINT, or computed otherwise.
1525  *
1526  * cip         - cache info ptr
1527  * id          - id to resolve
1528  * u8s         - utf8 result from upcall
1529  * behavior    - has algorithm behavior
1530  * hash_number - hash number iff hint
1531  */
1532 static void
nfs_idmap_cache_i2s_insert(idmap_cache_info_t * cip,uid_t id,utf8string * u8s,hash_stat behavior,uint_t hash_number)1533 nfs_idmap_cache_i2s_insert(idmap_cache_info_t *cip, uid_t id, utf8string *u8s,
1534     hash_stat behavior, uint_t hash_number)
1535 {
1536 	uint_t		 hashno;
1537 	nfsidhq_t	*hq;
1538 	nfsidmap_t	*newp;
1539 	nfsidmap_t	*pnext;
1540 	nfsidmap_t	*p;
1541 
1542 
1543 	/*
1544 	 * Obtain correct hash queue to insert new entry in
1545 	 */
1546 	switch (behavior) {
1547 		case HQ_HASH_HINT:
1548 			hashno = hash_number;
1549 			break;
1550 
1551 		case HQ_HASH_FIND:
1552 		default:
1553 			ID_HASH(id, hashno);
1554 			break;
1555 	}
1556 	hq = &cip->table[hashno];
1557 
1558 
1559 	/*
1560 	 * Look for an existing entry in the cache. If one exists
1561 	 * update it, and return. Otherwise, allocate a new cache
1562 	 * entry, initialize and insert it.
1563 	 */
1564 	mutex_enter(&(hq->hq_lock));
1565 	for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
1566 
1567 		pnext = p->id_forw;
1568 
1569 		/*
1570 		 * Check entry for staleness first, as user's id
1571 		 * may have changed and may need to be remapped.
1572 		 * Note that we don't evict entries from the cache
1573 		 * if we're having trouble contacting nfsmapid(8)
1574 		 */
1575 		if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
1576 			nfs_idmap_cache_rment(p);
1577 			continue;
1578 		}
1579 
1580 
1581 		if ((p->id_no == id) && (p->id_len == u8s->utf8string_len)) {
1582 			/*
1583 			 * Found It ! Move to front, and update time.
1584 			 */
1585 			remque(p);
1586 			insque(p, hq);
1587 			p->id_time = gethrestime_sec();
1588 
1589 			mutex_exit(&(hq->hq_lock));
1590 			return;
1591 		}
1592 	}
1593 
1594 	/*
1595 	 * Not found ! Alloc, init and insert new entry
1596 	 */
1597 	newp = kmem_cache_alloc(nfsidmap_cache, KM_SLEEP);
1598 	newp->id_len = u8s->utf8string_len;
1599 	newp->id_val = kmem_alloc(u8s->utf8string_len, KM_SLEEP);
1600 	bcopy(u8s->utf8string_val, newp->id_val, u8s->utf8string_len);
1601 	newp->id_no = id;
1602 	newp->id_time = gethrestime_sec();
1603 	insque(newp, hq);
1604 
1605 	mutex_exit(&(hq->hq_lock));
1606 }
1607 
1608 /*
1609  * Remove and free one cache entry
1610  */
1611 static void
nfs_idmap_cache_rment(nfsidmap_t * p)1612 nfs_idmap_cache_rment(nfsidmap_t *p)
1613 {
1614 	remque(p);
1615 	if (p->id_val != 0)
1616 		kmem_free(p->id_val, p->id_len);
1617 	kmem_cache_free(nfsidmap_cache, p);
1618 }
1619 
1620 #ifndef		UID_MAX
1621 #define		UID_MAX		2147483647		/* see limits.h */
1622 #endif
1623 
1624 #ifndef		isdigit
1625 #define		isdigit(c)	((c) >= '0' && (c) <= '9')
1626 #endif
1627 
1628 static int
is_stringified_id(utf8string * u8s)1629 is_stringified_id(utf8string *u8s)
1630 {
1631 	int	i;
1632 
1633 	for (i = 0; i < u8s->utf8string_len; i++)
1634 		if (!isdigit(u8s->utf8string_val[i]))
1635 			return (0);
1636 	return (1);
1637 }
1638 
1639 int
nfs_idmap_s2i_literal(utf8string * u8s,uid_t * id,int isserver)1640 nfs_idmap_s2i_literal(utf8string *u8s, uid_t *id, int isserver)
1641 {
1642 	long	tmp;
1643 	int	convd;
1644 	char	ids[_MAXIDSTRLEN];
1645 
1646 	/*
1647 	 * "nobody" unless we can actually decode it.
1648 	 */
1649 	*id = UID_NOBODY;
1650 
1651 	/*
1652 	 * We're here because it has already been determined that the
1653 	 * string contains no '@' _or_ the nfsmapid daemon has yet to
1654 	 * be started.
1655 	 */
1656 	if (!is_stringified_id(u8s))
1657 		return (0);
1658 
1659 	/*
1660 	 * If utf8string_len is greater than _MAXIDSTRLEN-1, then the id
1661 	 * is going to be greater than UID_MAX. Return id of "nobody"
1662 	 * right away.
1663 	 */
1664 	if (u8s->utf8string_len >= _MAXIDSTRLEN)
1665 		return (isserver ? EPERM : 0);
1666 
1667 	/*
1668 	 * Make sure we pass a NULL terminated 'C' string to ddi_strtol
1669 	 */
1670 	bcopy(u8s->utf8string_val, ids, u8s->utf8string_len);
1671 	ids[u8s->utf8string_len] = '\0';
1672 	convd = ddi_strtol(ids, NULL, 10, &tmp);
1673 	if (convd == 0 && tmp >= 0 && tmp <= UID_MAX) {
1674 		*id = tmp;
1675 		return (0);
1676 	}
1677 	return (isserver ? EPERM : 0);
1678 }
1679 
1680 static void
nfs_idmap_i2s_literal(uid_t id,utf8string * u8s)1681 nfs_idmap_i2s_literal(uid_t id, utf8string *u8s)
1682 {
1683 	char	ids[_MAXIDSTRLEN];
1684 
1685 	(void) snprintf(ids, _MAXIDSTRLEN, "%d", id);
1686 	(void) str_to_utf8(ids, u8s);
1687 }
1688 
1689 /* -- Utility functions -- */
1690 
1691 char *
utf8_strchr(utf8string * u8s,const char c)1692 utf8_strchr(utf8string *u8s, const char c)
1693 {
1694 	int	i;
1695 	char	*u8p = u8s->utf8string_val;
1696 	int	len = u8s->utf8string_len;
1697 
1698 	for (i = 0; i < len; i++)
1699 		if (u8p[i] == c)
1700 			return (&u8p[i]);
1701 	return (NULL);
1702 }
1703