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 3530. 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(1m) 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(1m) 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(1m) 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(1m) 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(1m) 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(1m) 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(1m) 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(1m),
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(1m) 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(1m) 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(1m) 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(1m) 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(1m) 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(1m) 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(1m) 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 static uint_t
nfs_idmap_cache_s2i_lkup(idmap_cache_info_t * cip,utf8string * u8s,uint_t * hashno,uid_t * id_buf)1272 nfs_idmap_cache_s2i_lkup(idmap_cache_info_t *cip, /* cache info ptr */
1273 utf8string *u8s, /* utf8 string to resolve */
1274 uint_t *hashno, /* hash number, retval */
1275 uid_t *id_buf) /* if found, id for u8s */
1276 {
1277 nfsidmap_t *p;
1278 nfsidmap_t *pnext;
1279 nfsidhq_t *hq;
1280 char *rqst_c_str;
1281 uint_t rqst_len;
1282 uint_t found_stat = 0;
1283
1284 if ((rqst_c_str = utf8_to_str(u8s, &rqst_len, NULL)) == NULL) {
1285 /*
1286 * Illegal string, return not found.
1287 */
1288 return (0);
1289 }
1290
1291 /*
1292 * Compute hash queue
1293 */
1294 *hashno = pkp_tab_hash(rqst_c_str, rqst_len - 1);
1295 hq = &cip->table[*hashno];
1296
1297 /*
1298 * Look for the entry in the HQ
1299 */
1300 mutex_enter(&(hq->hq_lock));
1301 for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
1302
1303 pnext = p->id_forw;
1304
1305 /*
1306 * Check entry for staleness first, as user's id
1307 * may have changed and may need to be remapped.
1308 * Note that we don't evict entries from the cache
1309 * if we're having trouble contacting nfsmapid(1m)
1310 */
1311 if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
1312 nfs_idmap_cache_rment(p);
1313 continue;
1314 }
1315
1316 /*
1317 * Compare equal length strings
1318 */
1319 if (p->id_len == (rqst_len - 1)) {
1320 if (bcmp(p->id_val, rqst_c_str, (rqst_len - 1)) == 0) {
1321 /*
1322 * Found it. Update it and load return value.
1323 */
1324 *id_buf = p->id_no;
1325 remque(p);
1326 insque(p, hq);
1327 p->id_time = gethrestime_sec();
1328
1329 found_stat = 1;
1330 break;
1331 }
1332 }
1333 }
1334 mutex_exit(&(hq->hq_lock));
1335
1336 if (rqst_c_str != NULL)
1337 kmem_free(rqst_c_str, rqst_len);
1338
1339 return (found_stat);
1340 }
1341
1342 /*
1343 * Search the specified cache for the existence of the specified utf8
1344 * string, as it may have been inserted before this instance got a chance
1345 * to do it. If NOT found, then a new entry is allocated for the specified
1346 * cache, and inserted. The hash queue number is obtained from hash_number
1347 * if the behavior is HQ_HASH_HINT, or computed otherwise.
1348 */
1349 static void
nfs_idmap_cache_s2i_insert(idmap_cache_info_t * cip,uid_t id,utf8string * u8s,hash_stat behavior,uint_t hash_number)1350 nfs_idmap_cache_s2i_insert(idmap_cache_info_t *cip, /* cache info ptr */
1351 uid_t id, /* id result from upcall */
1352 utf8string *u8s, /* utf8 string to resolve */
1353 hash_stat behavior, /* hash algorithm behavior */
1354 uint_t hash_number) /* hash number iff hint */
1355 {
1356 uint_t hashno;
1357 char *c_str;
1358 nfsidhq_t *hq;
1359 nfsidmap_t *newp;
1360 nfsidmap_t *p;
1361 nfsidmap_t *pnext;
1362 uint_t c_len;
1363
1364 /*
1365 * This shouldn't fail, since already successful at lkup.
1366 * So, if it does happen, just drop the request-to-insert
1367 * on the floor.
1368 */
1369 if ((c_str = utf8_to_str(u8s, &c_len, NULL)) == NULL)
1370 return;
1371
1372 /*
1373 * Obtain correct hash queue to insert new entry in
1374 */
1375 switch (behavior) {
1376 case HQ_HASH_HINT:
1377 hashno = hash_number;
1378 break;
1379
1380 case HQ_HASH_FIND:
1381 default:
1382 hashno = pkp_tab_hash(c_str, c_len - 1);
1383 break;
1384 }
1385 hq = &cip->table[hashno];
1386
1387
1388 /*
1389 * Look for an existing entry in the cache. If one exists
1390 * update it, and return. Otherwise, allocate a new cache
1391 * entry, initialize it and insert it.
1392 */
1393 mutex_enter(&(hq->hq_lock));
1394 for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
1395
1396 pnext = p->id_forw;
1397
1398 /*
1399 * Check entry for staleness first, as user's id
1400 * may have changed and may need to be remapped.
1401 * Note that we don't evict entries from the cache
1402 * if we're having trouble contacting nfsmapid(1m)
1403 */
1404 if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
1405 nfs_idmap_cache_rment(p);
1406 continue;
1407 }
1408
1409 /*
1410 * Compare equal length strings
1411 */
1412 if (p->id_len == (c_len - 1)) {
1413 if (bcmp(p->id_val, c_str, (c_len - 1)) == 0) {
1414 /*
1415 * Move to front, and update time.
1416 */
1417 remque(p);
1418 insque(p, hq);
1419 p->id_time = gethrestime_sec();
1420
1421 mutex_exit(&(hq->hq_lock));
1422 kmem_free(c_str, c_len);
1423 return;
1424 }
1425 }
1426 }
1427
1428 /*
1429 * Not found ! Alloc, init and insert new entry
1430 */
1431 newp = kmem_cache_alloc(nfsidmap_cache, KM_SLEEP);
1432 newp->id_len = u8s->utf8string_len;
1433 newp->id_val = kmem_alloc(u8s->utf8string_len, KM_SLEEP);
1434 bcopy(u8s->utf8string_val, newp->id_val, u8s->utf8string_len);
1435 newp->id_no = id;
1436 newp->id_time = gethrestime_sec();
1437 insque(newp, hq);
1438
1439 mutex_exit(&(hq->hq_lock));
1440 kmem_free(c_str, c_len);
1441 }
1442
1443 /*
1444 * Search the specified cache for the existence of the specified id.
1445 * If found, the corresponding mapping is returned in u8s and the
1446 * cache entry is updated to the head of the LRU list. The computed
1447 * hash queue number, is returned in hashno.
1448 */
1449 static uint_t
nfs_idmap_cache_i2s_lkup(idmap_cache_info_t * cip,uid_t id,uint_t * hashno,utf8string * u8s)1450 nfs_idmap_cache_i2s_lkup(idmap_cache_info_t *cip, /* cache info ptr */
1451 uid_t id, /* id to resolve */
1452 uint_t *hashno, /* hash number, retval */
1453 utf8string *u8s) /* if found, utf8 str for id */
1454 {
1455 uint_t found_stat = 0;
1456 nfsidmap_t *p;
1457 nfsidmap_t *pnext;
1458 nfsidhq_t *hq;
1459 uint_t hash;
1460
1461 /*
1462 * Compute hash queue
1463 */
1464 ID_HASH(id, hash);
1465 *hashno = hash;
1466 hq = &cip->table[hash];
1467
1468 /*
1469 * Look for the entry in the HQ
1470 */
1471 mutex_enter(&(hq->hq_lock));
1472 for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
1473
1474 pnext = p->id_forw;
1475
1476 /*
1477 * Check entry for staleness first, as user's id
1478 * may have changed and may need to be remapped.
1479 * Note that we don't evict entries from the cache
1480 * if we're having trouble contacting nfsmapid(1m)
1481 */
1482 if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
1483 nfs_idmap_cache_rment(p);
1484 continue;
1485 }
1486
1487 if (p->id_no == id) {
1488
1489 /*
1490 * Found it. Load return value and move to head
1491 */
1492 ASSERT(u8s->utf8string_val == NULL);
1493 u8s->utf8string_len = p->id_len;
1494 u8s->utf8string_val = kmem_alloc(p->id_len, KM_SLEEP);
1495 bcopy(p->id_val, u8s->utf8string_val, p->id_len);
1496
1497 remque(p);
1498 insque(p, hq);
1499 p->id_time = gethrestime_sec();
1500
1501 found_stat = 1;
1502 break;
1503 }
1504 }
1505 mutex_exit(&(hq->hq_lock));
1506
1507 return (found_stat);
1508 }
1509
1510 /*
1511 * Search the specified cache for the existence of the specified id,
1512 * as it may have been inserted before this instance got a chance to
1513 * do it. If NOT found, then a new entry is allocated for the specified
1514 * cache, and inserted. The hash queue number is obtained from hash_number
1515 * if the behavior is HQ_HASH_HINT, or computed otherwise.
1516 */
1517 static void
nfs_idmap_cache_i2s_insert(idmap_cache_info_t * cip,uid_t id,utf8string * u8s,hash_stat behavior,uint_t hash_number)1518 nfs_idmap_cache_i2s_insert(idmap_cache_info_t *cip, /* cache info ptr */
1519 uid_t id, /* id to resolve */
1520 utf8string *u8s, /* utf8 result from upcall */
1521 hash_stat behavior, /* has algorithm behavior */
1522 uint_t hash_number) /* hash number iff hint */
1523 {
1524 uint_t hashno;
1525 nfsidhq_t *hq;
1526 nfsidmap_t *newp;
1527 nfsidmap_t *pnext;
1528 nfsidmap_t *p;
1529
1530
1531 /*
1532 * Obtain correct hash queue to insert new entry in
1533 */
1534 switch (behavior) {
1535 case HQ_HASH_HINT:
1536 hashno = hash_number;
1537 break;
1538
1539 case HQ_HASH_FIND:
1540 default:
1541 ID_HASH(id, hashno);
1542 break;
1543 }
1544 hq = &cip->table[hashno];
1545
1546
1547 /*
1548 * Look for an existing entry in the cache. If one exists
1549 * update it, and return. Otherwise, allocate a new cache
1550 * entry, initialize and insert it.
1551 */
1552 mutex_enter(&(hq->hq_lock));
1553 for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
1554
1555 pnext = p->id_forw;
1556
1557 /*
1558 * Check entry for staleness first, as user's id
1559 * may have changed and may need to be remapped.
1560 * Note that we don't evict entries from the cache
1561 * if we're having trouble contacting nfsmapid(1m)
1562 */
1563 if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
1564 nfs_idmap_cache_rment(p);
1565 continue;
1566 }
1567
1568
1569 if ((p->id_no == id) && (p->id_len == u8s->utf8string_len)) {
1570 /*
1571 * Found It ! Move to front, and update time.
1572 */
1573 remque(p);
1574 insque(p, hq);
1575 p->id_time = gethrestime_sec();
1576
1577 mutex_exit(&(hq->hq_lock));
1578 return;
1579 }
1580 }
1581
1582 /*
1583 * Not found ! Alloc, init and insert new entry
1584 */
1585 newp = kmem_cache_alloc(nfsidmap_cache, KM_SLEEP);
1586 newp->id_len = u8s->utf8string_len;
1587 newp->id_val = kmem_alloc(u8s->utf8string_len, KM_SLEEP);
1588 bcopy(u8s->utf8string_val, newp->id_val, u8s->utf8string_len);
1589 newp->id_no = id;
1590 newp->id_time = gethrestime_sec();
1591 insque(newp, hq);
1592
1593 mutex_exit(&(hq->hq_lock));
1594 }
1595
1596 /*
1597 * Remove and free one cache entry
1598 */
1599 static void
nfs_idmap_cache_rment(nfsidmap_t * p)1600 nfs_idmap_cache_rment(nfsidmap_t *p)
1601 {
1602 remque(p);
1603 if (p->id_val != 0)
1604 kmem_free(p->id_val, p->id_len);
1605 kmem_cache_free(nfsidmap_cache, p);
1606 }
1607
1608 #ifndef UID_MAX
1609 #define UID_MAX 2147483647 /* see limits.h */
1610 #endif
1611
1612 #ifndef isdigit
1613 #define isdigit(c) ((c) >= '0' && (c) <= '9')
1614 #endif
1615
1616 static int
is_stringified_id(utf8string * u8s)1617 is_stringified_id(utf8string *u8s)
1618 {
1619 int i;
1620
1621 for (i = 0; i < u8s->utf8string_len; i++)
1622 if (!isdigit(u8s->utf8string_val[i]))
1623 return (0);
1624 return (1);
1625 }
1626
1627 int
nfs_idmap_s2i_literal(utf8string * u8s,uid_t * id,int isserver)1628 nfs_idmap_s2i_literal(utf8string *u8s, uid_t *id, int isserver)
1629 {
1630 long tmp;
1631 int convd;
1632 char ids[_MAXIDSTRLEN];
1633
1634 /*
1635 * "nobody" unless we can actually decode it.
1636 */
1637 *id = UID_NOBODY;
1638
1639 /*
1640 * We're here because it has already been determined that the
1641 * string contains no '@' _or_ the nfsmapid daemon has yet to
1642 * be started.
1643 */
1644 if (!is_stringified_id(u8s))
1645 return (0);
1646
1647 /*
1648 * If utf8string_len is greater than _MAXIDSTRLEN-1, then the id
1649 * is going to be greater than UID_MAX. Return id of "nobody"
1650 * right away.
1651 */
1652 if (u8s->utf8string_len >= _MAXIDSTRLEN)
1653 return (isserver ? EPERM : 0);
1654
1655 /*
1656 * Make sure we pass a NULL terminated 'C' string to ddi_strtol
1657 */
1658 bcopy(u8s->utf8string_val, ids, u8s->utf8string_len);
1659 ids[u8s->utf8string_len] = '\0';
1660 convd = ddi_strtol(ids, NULL, 10, &tmp);
1661 if (convd == 0 && tmp >= 0 && tmp <= UID_MAX) {
1662 *id = tmp;
1663 return (0);
1664 }
1665 return (isserver ? EPERM : 0);
1666 }
1667
1668 static void
nfs_idmap_i2s_literal(uid_t id,utf8string * u8s)1669 nfs_idmap_i2s_literal(uid_t id, utf8string *u8s)
1670 {
1671 char ids[_MAXIDSTRLEN];
1672
1673 (void) snprintf(ids, _MAXIDSTRLEN, "%d", id);
1674 (void) str_to_utf8(ids, u8s);
1675 }
1676
1677 /* -- Utility functions -- */
1678
1679 char *
utf8_strchr(utf8string * u8s,const char c)1680 utf8_strchr(utf8string *u8s, const char c)
1681 {
1682 int i;
1683 char *u8p = u8s->utf8string_val;
1684 int len = u8s->utf8string_len;
1685
1686 for (i = 0; i < len; i++)
1687 if (u8p[i] == c)
1688 return (&u8p[i]);
1689 return (NULL);
1690 }
1691