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