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