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