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