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