Print this page
Revert exi_zone to exi_zoneid, and install exi_ne backpointer
NFS Auth per-zone needs better cleanup
curzone reality check and teardown changes to use the RIGHT zone
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/uts/common/fs/nfs/nfs_auth.c
+++ new/usr/src/uts/common/fs/nfs/nfs_auth.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
24 24 * Copyright (c) 2015 by Delphix. All rights reserved.
25 25 * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
26 26 */
27 27
28 28 #include <sys/param.h>
29 29 #include <sys/errno.h>
30 30 #include <sys/vfs.h>
31 31 #include <sys/vnode.h>
32 32 #include <sys/cred.h>
33 33 #include <sys/cmn_err.h>
34 34 #include <sys/systm.h>
35 35 #include <sys/kmem.h>
36 36 #include <sys/pathname.h>
37 37 #include <sys/utsname.h>
38 38 #include <sys/debug.h>
39 39 #include <sys/door.h>
40 40 #include <sys/sdt.h>
41 41 #include <sys/thread.h>
42 42 #include <sys/avl.h>
43 43
44 44 #include <rpc/types.h>
45 45 #include <rpc/auth.h>
46 46 #include <rpc/clnt.h>
47 47
48 48 #include <nfs/nfs.h>
49 49 #include <nfs/export.h>
50 50 #include <nfs/nfs_clnt.h>
51 51 #include <nfs/auth.h>
52 52
53 53 static struct kmem_cache *exi_cache_handle;
54 54 static void exi_cache_reclaim(void *);
55 55 static void exi_cache_reclaim_zone(nfs_globals_t *);
56 56 static void exi_cache_trim(struct exportinfo *exi);
57 57
58 58 extern pri_t minclsyspri;
59 59
60 60 /* NFS auth cache statistics */
61 61 volatile uint_t nfsauth_cache_hit;
62 62 volatile uint_t nfsauth_cache_miss;
63 63 volatile uint_t nfsauth_cache_refresh;
64 64 volatile uint_t nfsauth_cache_reclaim;
65 65 volatile uint_t exi_cache_auth_reclaim_failed;
66 66 volatile uint_t exi_cache_clnt_reclaim_failed;
67 67
68 68 /*
69 69 * The lifetime of an auth cache entry:
70 70 * ------------------------------------
71 71 *
72 72 * An auth cache entry is created with both the auth_time
73 73 * and auth_freshness times set to the current time.
74 74 *
75 75 * Upon every client access which results in a hit, the
76 76 * auth_time will be updated.
77 77 *
78 78 * If a client access determines that the auth_freshness
79 79 * indicates that the entry is STALE, then it will be
80 80 * refreshed. Note that this will explicitly reset
81 81 * auth_time.
82 82 *
83 83 * When the REFRESH successfully occurs, then the
84 84 * auth_freshness is updated.
85 85 *
86 86 * There are two ways for an entry to leave the cache:
87 87 *
88 88 * 1) Purged by an action on the export (remove or changed)
89 89 * 2) Memory backpressure from the kernel (check against NFSAUTH_CACHE_TRIM)
90 90 *
91 91 * For 2) we check the timeout value against auth_time.
92 92 */
93 93
94 94 /*
95 95 * Number of seconds until we mark for refresh an auth cache entry.
96 96 */
97 97 #define NFSAUTH_CACHE_REFRESH 600
98 98
99 99 /*
100 100 * Number of idle seconds until we yield to backpressure
101 101 * to trim a cache entry.
102 102 */
103 103 #define NFSAUTH_CACHE_TRIM 3600
104 104
105 105 /*
106 106 * While we could encapuslate the exi_list inside the
107 107 * exi structure, we can't do that for the auth_list.
108 108 * So, to keep things looking clean, we keep them both
109 109 * in these external lists.
110 110 */
111 111 typedef struct refreshq_exi_node {
112 112 struct exportinfo *ren_exi;
113 113 list_t ren_authlist;
114 114 list_node_t ren_node;
115 115 } refreshq_exi_node_t;
116 116
117 117 typedef struct refreshq_auth_node {
118 118 struct auth_cache *ran_auth;
119 119 char *ran_netid;
120 120 list_node_t ran_node;
121 121 } refreshq_auth_node_t;
122 122
123 123 /*
124 124 * Used to manipulate things on the refreshq_queue. Note that the refresh
125 125 * thread will effectively pop a node off of the queue, at which point it
126 126 * will no longer need to hold the mutex.
127 127 */
128 128 static kmutex_t refreshq_lock;
129 129 static list_t refreshq_queue;
130 130 static kcondvar_t refreshq_cv;
131 131
132 132 /*
133 133 * If there is ever a problem with loading the module, then nfsauth_fini()
134 134 * needs to be called to remove state. In that event, since the refreshq
135 135 * thread has been started, they need to work together to get rid of state.
136 136 */
137 137 typedef enum nfsauth_refreshq_thread_state {
138 138 REFRESHQ_THREAD_RUNNING,
139 139 REFRESHQ_THREAD_FINI_REQ,
140 140 REFRESHQ_THREAD_HALTED,
141 141 REFRESHQ_THREAD_NEED_CREATE
142 142 } nfsauth_refreshq_thread_state_t;
143 143
144 144 typedef struct nfsauth_globals {
145 145 kmutex_t mountd_lock;
146 146 door_handle_t mountd_dh;
147 147
148 148 /*
149 149 * Used to manipulate things on the refreshq_queue. Note that the
150 150 * refresh thread will effectively pop a node off of the queue,
151 151 * at which point it will no longer need to hold the mutex.
152 152 */
153 153 kmutex_t refreshq_lock;
154 154 list_t refreshq_queue;
155 155 kcondvar_t refreshq_cv;
156 156
157 157 /*
158 158 * A list_t would be overkill. These are auth_cache entries which are
159 159 * no longer linked to an exi. It should be the case that all of their
160 160 * states are NFS_AUTH_INVALID, i.e., the only way to be put on this
161 161 * list is iff their state indicated that they had been placed on the
162 162 * refreshq_queue.
163 163 *
164 164 * Note that while there is no link from the exi or back to the exi,
165 165 * the exi can not go away until these entries are harvested.
166 166 */
167 167 struct auth_cache *refreshq_dead_entries;
168 168 nfsauth_refreshq_thread_state_t refreshq_thread_state;
169 169
170 170 } nfsauth_globals_t;
171 171
172 172 static void nfsauth_free_node(struct auth_cache *);
173 173 static void nfsauth_refresh_thread(nfsauth_globals_t *);
174 174
175 175 static int nfsauth_cache_compar(const void *, const void *);
176 176
177 177 static nfsauth_globals_t *
178 178 nfsauth_get_zg(void)
179 179 {
180 180 nfs_globals_t *ng = zone_getspecific(nfssrv_zone_key, curzone);
181 181 nfsauth_globals_t *nag = ng->nfs_auth;
182 182 ASSERT(nag != NULL);
183 183 return (nag);
184 184 }
185 185
186 186 void
187 187 mountd_args(uint_t did)
188 188 {
189 189 nfsauth_globals_t *nag;
190 190
191 191 nag = nfsauth_get_zg();
192 192 mutex_enter(&nag->mountd_lock);
193 193 if (nag->mountd_dh != NULL)
194 194 door_ki_rele(nag->mountd_dh);
195 195 nag->mountd_dh = door_ki_lookup(did);
196 196 mutex_exit(&nag->mountd_lock);
197 197 }
198 198
199 199 void
200 200 nfsauth_init(void)
201 201 {
202 202 exi_cache_handle = kmem_cache_create("exi_cache_handle",
203 203 sizeof (struct auth_cache), 0, NULL, NULL,
204 204 exi_cache_reclaim, NULL, NULL, 0);
205 205 }
206 206
207 207 void
208 208 nfsauth_fini(void)
209 209 {
210 210 kmem_cache_destroy(exi_cache_handle);
211 211 }
212 212
213 213 void
214 214 nfsauth_zone_init(nfs_globals_t *ng)
215 215 {
216 216 nfsauth_globals_t *nag;
217 217
218 218 nag = kmem_zalloc(sizeof (*nag), KM_SLEEP);
219 219
220 220 /*
221 221 * mountd can be restarted by smf(5). We need to make sure
222 222 * the updated door handle will safely make it to mountd_dh.
223 223 */
224 224 mutex_init(&nag->mountd_lock, NULL, MUTEX_DEFAULT, NULL);
225 225 mutex_init(&nag->refreshq_lock, NULL, MUTEX_DEFAULT, NULL);
226 226 list_create(&nag->refreshq_queue, sizeof (refreshq_exi_node_t),
227 227 offsetof(refreshq_exi_node_t, ren_node));
228 228 cv_init(&nag->refreshq_cv, NULL, CV_DEFAULT, NULL);
229 229 nag->refreshq_thread_state = REFRESHQ_THREAD_NEED_CREATE;
230 230
231 231 ng->nfs_auth = nag;
232 232 }
233 233
234 234 void
235 235 nfsauth_zone_shutdown(nfs_globals_t *ng)
236 236 {
237 237 refreshq_exi_node_t *ren;
238 238 nfsauth_globals_t *nag = ng->nfs_auth;
239 239
240 240 /* Prevent the nfsauth_refresh_thread from getting new work */
241 241 mutex_enter(&nag->refreshq_lock);
242 242 if (nag->refreshq_thread_state == REFRESHQ_THREAD_RUNNING) {
243 243 nag->refreshq_thread_state = REFRESHQ_THREAD_FINI_REQ;
244 244 cv_broadcast(&nag->refreshq_cv);
245 245
246 246 /* Wait for nfsauth_refresh_thread() to exit */
247 247 while (nag->refreshq_thread_state != REFRESHQ_THREAD_HALTED)
248 248 cv_wait(&nag->refreshq_cv, &nag->refreshq_lock);
249 249 }
250 250 mutex_exit(&nag->refreshq_lock);
251 251
252 252 /*
253 253 * Walk the exi_list and in turn, walk the auth_lists and free all
254 254 * lists. In addition, free INVALID auth_cache entries.
255 255 */
256 256 while ((ren = list_remove_head(&nag->refreshq_queue))) {
257 257 refreshq_auth_node_t *ran;
258 258
259 259 while ((ran = list_remove_head(&ren->ren_authlist)) != NULL) {
260 260 struct auth_cache *p = ran->ran_auth;
261 261 if (p->auth_state == NFS_AUTH_INVALID)
262 262 nfsauth_free_node(p);
263 263 strfree(ran->ran_netid);
264 264 kmem_free(ran, sizeof (*ran));
265 265 }
266 266
267 267 list_destroy(&ren->ren_authlist);
268 268 exi_rele(ren->ren_exi);
269 269 kmem_free(ren, sizeof (*ren));
270 270 }
271 271 }
272 272
273 273 void
|
↓ open down ↓ |
273 lines elided |
↑ open up ↑ |
274 274 nfsauth_zone_fini(nfs_globals_t *ng)
275 275 {
276 276 nfsauth_globals_t *nag = ng->nfs_auth;
277 277
278 278 ng->nfs_auth = NULL;
279 279
280 280 list_destroy(&nag->refreshq_queue);
281 281 cv_destroy(&nag->refreshq_cv);
282 282 mutex_destroy(&nag->refreshq_lock);
283 283 mutex_destroy(&nag->mountd_lock);
284 + /* Extra cleanup. */
285 + if (nag->mountd_dh != NULL)
286 + door_ki_rele(nag->mountd_dh);
284 287 kmem_free(nag, sizeof (*nag));
285 288 }
286 289
287 290 /*
288 291 * Convert the address in a netbuf to
289 292 * a hash index for the auth_cache table.
290 293 */
291 294 static int
292 295 hash(struct netbuf *a)
293 296 {
294 297 int i, h = 0;
295 298
296 299 for (i = 0; i < a->len; i++)
297 300 h ^= a->buf[i];
298 301
299 302 return (h & (AUTH_TABLESIZE - 1));
300 303 }
301 304
302 305 /*
303 306 * Mask out the components of an
304 307 * address that do not identify
305 308 * a host. For socket addresses the
306 309 * masking gets rid of the port number.
307 310 */
308 311 static void
309 312 addrmask(struct netbuf *addr, struct netbuf *mask)
310 313 {
311 314 int i;
312 315
313 316 for (i = 0; i < addr->len; i++)
314 317 addr->buf[i] &= mask->buf[i];
315 318 }
316 319
317 320 /*
318 321 * nfsauth4_access is used for NFS V4 auth checking. Besides doing
319 322 * the common nfsauth_access(), it will check if the client can
320 323 * have a limited access to this vnode even if the security flavor
321 324 * used does not meet the policy.
322 325 */
323 326 int
324 327 nfsauth4_access(struct exportinfo *exi, vnode_t *vp, struct svc_req *req,
325 328 cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
326 329 {
327 330 int access;
328 331
329 332 access = nfsauth_access(exi, req, cr, uid, gid, ngids, gids);
330 333
331 334 /*
332 335 * There are cases that the server needs to allow the client
333 336 * to have a limited view.
334 337 *
335 338 * e.g.
336 339 * /export is shared as "sec=sys,rw=dfs-test-4,sec=krb5,rw"
337 340 * /export/home is shared as "sec=sys,rw"
338 341 *
339 342 * When the client mounts /export with sec=sys, the client
340 343 * would get a limited view with RO access on /export to see
341 344 * "home" only because the client is allowed to access
342 345 * /export/home with auth_sys.
343 346 */
344 347 if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) {
345 348 /*
346 349 * Allow ro permission with LIMITED view if there is a
347 350 * sub-dir exported under vp.
348 351 */
349 352 if (has_visible(exi, vp))
350 353 return (NFSAUTH_LIMITED);
351 354 }
352 355
353 356 return (access);
354 357 }
355 358
356 359 static void
357 360 sys_log(const char *msg)
358 361 {
359 362 static time_t tstamp = 0;
360 363 time_t now;
361 364
362 365 /*
363 366 * msg is shown (at most) once per minute
364 367 */
365 368 now = gethrestime_sec();
366 369 if ((tstamp + 60) < now) {
367 370 tstamp = now;
368 371 cmn_err(CE_WARN, msg);
369 372 }
370 373 }
371 374
372 375 /*
373 376 * Callup to the mountd to get access information in the kernel.
374 377 */
375 378 static bool_t
376 379 nfsauth_retrieve(nfsauth_globals_t *nag, struct exportinfo *exi,
377 380 char *req_netid, int flavor, struct netbuf *addr, int *access,
378 381 cred_t *clnt_cred, uid_t *srv_uid, gid_t *srv_gid, uint_t *srv_gids_cnt,
379 382 gid_t **srv_gids)
380 383 {
381 384 varg_t varg = {0};
382 385 nfsauth_res_t res = {0};
383 386 XDR xdrs;
384 387 size_t absz;
385 388 caddr_t abuf;
386 389 int last = 0;
387 390 door_arg_t da;
388 391 door_info_t di;
389 392 door_handle_t dh;
390 393 uint_t ntries = 0;
391 394
392 395 /*
393 396 * No entry in the cache for this client/flavor
394 397 * so we need to call the nfsauth service in the
395 398 * mount daemon.
396 399 */
397 400
398 401 varg.vers = V_PROTO;
399 402 varg.arg_u.arg.cmd = NFSAUTH_ACCESS;
400 403 varg.arg_u.arg.areq.req_client.n_len = addr->len;
401 404 varg.arg_u.arg.areq.req_client.n_bytes = addr->buf;
402 405 varg.arg_u.arg.areq.req_netid = req_netid;
403 406 varg.arg_u.arg.areq.req_path = exi->exi_export.ex_path;
404 407 varg.arg_u.arg.areq.req_flavor = flavor;
405 408 varg.arg_u.arg.areq.req_clnt_uid = crgetuid(clnt_cred);
406 409 varg.arg_u.arg.areq.req_clnt_gid = crgetgid(clnt_cred);
407 410 varg.arg_u.arg.areq.req_clnt_gids.len = crgetngroups(clnt_cred);
408 411 varg.arg_u.arg.areq.req_clnt_gids.val = (gid_t *)crgetgroups(clnt_cred);
409 412
410 413 DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg);
411 414
412 415 /*
413 416 * Setup the XDR stream for encoding the arguments. Notice that
414 417 * in addition to the args having variable fields (req_netid and
415 418 * req_path), the argument data structure is itself versioned,
416 419 * so we need to make sure we can size the arguments buffer
417 420 * appropriately to encode all the args. If we can't get sizing
418 421 * info _or_ properly encode the arguments, there's really no
419 422 * point in continuting, so we fail the request.
420 423 */
421 424 if ((absz = xdr_sizeof(xdr_varg, &varg)) == 0) {
422 425 *access = NFSAUTH_DENIED;
423 426 return (FALSE);
424 427 }
425 428
426 429 abuf = (caddr_t)kmem_alloc(absz, KM_SLEEP);
427 430 xdrmem_create(&xdrs, abuf, absz, XDR_ENCODE);
428 431 if (!xdr_varg(&xdrs, &varg)) {
429 432 XDR_DESTROY(&xdrs);
430 433 goto fail;
431 434 }
432 435 XDR_DESTROY(&xdrs);
433 436
434 437 /*
435 438 * Prepare the door arguments
436 439 *
437 440 * We don't know the size of the message the daemon
438 441 * will pass back to us. By setting rbuf to NULL,
439 442 * we force the door code to allocate a buf of the
440 443 * appropriate size. We must set rsize > 0, however,
441 444 * else the door code acts as if no response was
442 445 * expected and doesn't pass the data to us.
443 446 */
444 447 da.data_ptr = (char *)abuf;
445 448 da.data_size = absz;
446 449 da.desc_ptr = NULL;
447 450 da.desc_num = 0;
448 451 da.rbuf = NULL;
449 452 da.rsize = 1;
450 453
451 454 retry:
452 455 mutex_enter(&nag->mountd_lock);
453 456 dh = nag->mountd_dh;
454 457 if (dh != NULL)
455 458 door_ki_hold(dh);
456 459 mutex_exit(&nag->mountd_lock);
457 460
458 461 if (dh == NULL) {
459 462 /*
460 463 * The rendezvous point has not been established yet!
461 464 * This could mean that either mountd(1m) has not yet
462 465 * been started or that _this_ routine nuked the door
463 466 * handle after receiving an EINTR for a REVOKED door.
464 467 *
465 468 * Returning NFSAUTH_DROP will cause the NFS client
466 469 * to retransmit the request, so let's try to be more
467 470 * rescillient and attempt for ntries before we bail.
468 471 */
469 472 if (++ntries % NFSAUTH_DR_TRYCNT) {
470 473 delay(hz);
471 474 goto retry;
472 475 }
473 476
474 477 kmem_free(abuf, absz);
475 478
476 479 sys_log("nfsauth: mountd has not established door");
477 480 *access = NFSAUTH_DROP;
478 481 return (FALSE);
479 482 }
480 483
481 484 ntries = 0;
482 485
483 486 /*
484 487 * Now that we've got what we need, place the call.
485 488 */
486 489 switch (door_ki_upcall_limited(dh, &da, NULL, SIZE_MAX, 0)) {
487 490 case 0: /* Success */
488 491 door_ki_rele(dh);
489 492
490 493 if (da.data_ptr == NULL && da.data_size == 0) {
491 494 /*
492 495 * The door_return that contained the data
493 496 * failed! We're here because of the 2nd
494 497 * door_return (w/o data) such that we can
495 498 * get control of the thread (and exit
496 499 * gracefully).
497 500 */
498 501 DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil,
499 502 door_arg_t *, &da);
500 503 goto fail;
501 504 }
502 505
503 506 break;
504 507
505 508 case EAGAIN:
506 509 /*
507 510 * Server out of resources; back off for a bit
508 511 */
509 512 door_ki_rele(dh);
510 513 delay(hz);
511 514 goto retry;
512 515 /* NOTREACHED */
513 516
514 517 case EINTR:
515 518 if (!door_ki_info(dh, &di)) {
516 519 door_ki_rele(dh);
517 520
518 521 if (di.di_attributes & DOOR_REVOKED) {
519 522 /*
520 523 * The server barfed and revoked
521 524 * the (existing) door on us; we
522 525 * want to wait to give smf(5) a
523 526 * chance to restart mountd(1m)
524 527 * and establish a new door handle.
525 528 */
526 529 mutex_enter(&nag->mountd_lock);
527 530 if (dh == nag->mountd_dh) {
528 531 door_ki_rele(nag->mountd_dh);
529 532 nag->mountd_dh = NULL;
530 533 }
531 534 mutex_exit(&nag->mountd_lock);
532 535 delay(hz);
533 536 goto retry;
534 537 }
535 538 /*
536 539 * If the door was _not_ revoked on us,
537 540 * then more than likely we took an INTR,
538 541 * so we need to fail the operation.
539 542 */
540 543 goto fail;
541 544 }
542 545 /*
543 546 * The only failure that can occur from getting
544 547 * the door info is EINVAL, so we let the code
545 548 * below handle it.
546 549 */
547 550 /* FALLTHROUGH */
548 551
549 552 case EBADF:
550 553 case EINVAL:
551 554 default:
552 555 /*
553 556 * If we have a stale door handle, give smf a last
554 557 * chance to start it by sleeping for a little bit.
555 558 * If we're still hosed, we'll fail the call.
556 559 *
557 560 * Since we're going to reacquire the door handle
558 561 * upon the retry, we opt to sleep for a bit and
559 562 * _not_ to clear mountd_dh. If mountd restarted
560 563 * and was able to set mountd_dh, we should see
561 564 * the new instance; if not, we won't get caught
562 565 * up in the retry/DELAY loop.
563 566 */
564 567 door_ki_rele(dh);
565 568 if (!last) {
566 569 delay(hz);
567 570 last++;
568 571 goto retry;
569 572 }
570 573 sys_log("nfsauth: stale mountd door handle");
571 574 goto fail;
572 575 }
573 576
574 577 ASSERT(da.rbuf != NULL);
575 578
576 579 /*
577 580 * No door errors encountered; setup the XDR stream for decoding
578 581 * the results. If we fail to decode the results, we've got no
579 582 * other recourse than to fail the request.
580 583 */
581 584 xdrmem_create(&xdrs, da.rbuf, da.rsize, XDR_DECODE);
582 585 if (!xdr_nfsauth_res(&xdrs, &res)) {
583 586 xdr_free(xdr_nfsauth_res, (char *)&res);
584 587 XDR_DESTROY(&xdrs);
585 588 kmem_free(da.rbuf, da.rsize);
586 589 goto fail;
587 590 }
588 591 XDR_DESTROY(&xdrs);
589 592 kmem_free(da.rbuf, da.rsize);
590 593
591 594 DTRACE_PROBE1(nfsserv__func__nfsauth__results, nfsauth_res_t *, &res);
592 595 switch (res.stat) {
593 596 case NFSAUTH_DR_OKAY:
594 597 *access = res.ares.auth_perm;
595 598 *srv_uid = res.ares.auth_srv_uid;
596 599 *srv_gid = res.ares.auth_srv_gid;
597 600 *srv_gids_cnt = res.ares.auth_srv_gids.len;
598 601 *srv_gids = kmem_alloc(*srv_gids_cnt * sizeof (gid_t),
599 602 KM_SLEEP);
600 603 bcopy(res.ares.auth_srv_gids.val, *srv_gids,
601 604 *srv_gids_cnt * sizeof (gid_t));
602 605 break;
603 606
604 607 case NFSAUTH_DR_EFAIL:
605 608 case NFSAUTH_DR_DECERR:
606 609 case NFSAUTH_DR_BADCMD:
607 610 default:
608 611 xdr_free(xdr_nfsauth_res, (char *)&res);
609 612 fail:
610 613 *access = NFSAUTH_DENIED;
611 614 kmem_free(abuf, absz);
612 615 return (FALSE);
613 616 /* NOTREACHED */
614 617 }
615 618
616 619 xdr_free(xdr_nfsauth_res, (char *)&res);
617 620 kmem_free(abuf, absz);
618 621
619 622 return (TRUE);
620 623 }
621 624
622 625 static void
623 626 nfsauth_refresh_thread(nfsauth_globals_t *nag)
624 627 {
625 628 refreshq_exi_node_t *ren;
626 629 refreshq_auth_node_t *ran;
627 630
628 631 struct exportinfo *exi;
629 632
630 633 int access;
631 634 bool_t retrieval;
632 635
633 636 callb_cpr_t cprinfo;
634 637
635 638 CALLB_CPR_INIT(&cprinfo, &nag->refreshq_lock, callb_generic_cpr,
636 639 "nfsauth_refresh");
637 640
638 641 for (;;) {
639 642 mutex_enter(&nag->refreshq_lock);
640 643 if (nag->refreshq_thread_state != REFRESHQ_THREAD_RUNNING) {
641 644 /* Keep the hold on the lock! */
642 645 break;
643 646 }
644 647
645 648 ren = list_remove_head(&nag->refreshq_queue);
646 649 if (ren == NULL) {
647 650 CALLB_CPR_SAFE_BEGIN(&cprinfo);
648 651 cv_wait(&nag->refreshq_cv, &nag->refreshq_lock);
649 652 CALLB_CPR_SAFE_END(&cprinfo, &nag->refreshq_lock);
650 653 mutex_exit(&nag->refreshq_lock);
651 654 continue;
652 655 }
653 656 mutex_exit(&nag->refreshq_lock);
654 657
655 658 exi = ren->ren_exi;
656 659 ASSERT(exi != NULL);
657 660
658 661 /*
659 662 * Since the ren was removed from the refreshq_queue above,
660 663 * this is the only thread aware about the ren existence, so we
661 664 * have the exclusive ownership of it and we do not need to
662 665 * protect it by any lock.
663 666 */
664 667 while ((ran = list_remove_head(&ren->ren_authlist))) {
665 668 uid_t uid;
666 669 gid_t gid;
667 670 uint_t ngids;
668 671 gid_t *gids;
669 672 struct auth_cache *p = ran->ran_auth;
670 673 char *netid = ran->ran_netid;
671 674
672 675 ASSERT(p != NULL);
673 676 ASSERT(netid != NULL);
674 677
675 678 kmem_free(ran, sizeof (refreshq_auth_node_t));
676 679
677 680 mutex_enter(&p->auth_lock);
678 681
679 682 /*
680 683 * Once the entry goes INVALID, it can not change
681 684 * state.
682 685 *
683 686 * No need to refresh entries also in a case we are
684 687 * just shutting down.
685 688 *
686 689 * In general, there is no need to hold the
687 690 * refreshq_lock to test the refreshq_thread_state. We
688 691 * do hold it at other places because there is some
689 692 * related thread synchronization (or some other tasks)
690 693 * close to the refreshq_thread_state check.
691 694 *
692 695 * The check for the refreshq_thread_state value here
693 696 * is purely advisory to allow the faster
694 697 * nfsauth_refresh_thread() shutdown. In a case we
695 698 * will miss such advisory, nothing catastrophic
696 699 * happens: we will just spin longer here before the
697 700 * shutdown.
698 701 */
699 702 if (p->auth_state == NFS_AUTH_INVALID ||
700 703 nag->refreshq_thread_state !=
701 704 REFRESHQ_THREAD_RUNNING) {
702 705 mutex_exit(&p->auth_lock);
703 706
704 707 if (p->auth_state == NFS_AUTH_INVALID)
705 708 nfsauth_free_node(p);
706 709
707 710 strfree(netid);
708 711
709 712 continue;
710 713 }
711 714
712 715 /*
713 716 * Make sure the state is valid. Note that once we
714 717 * change the state to NFS_AUTH_REFRESHING, no other
715 718 * thread will be able to work on this entry.
716 719 */
717 720 ASSERT(p->auth_state == NFS_AUTH_STALE);
718 721
719 722 p->auth_state = NFS_AUTH_REFRESHING;
720 723 mutex_exit(&p->auth_lock);
721 724
722 725 DTRACE_PROBE2(nfsauth__debug__cache__refresh,
723 726 struct exportinfo *, exi,
724 727 struct auth_cache *, p);
725 728
726 729 /*
727 730 * The first caching of the access rights
728 731 * is done with the netid pulled out of the
729 732 * request from the client. All subsequent
730 733 * users of the cache may or may not have
731 734 * the same netid. It doesn't matter. So
732 735 * when we refresh, we simply use the netid
733 736 * of the request which triggered the
734 737 * refresh attempt.
735 738 */
736 739 retrieval = nfsauth_retrieve(nag, exi, netid,
737 740 p->auth_flavor, &p->auth_clnt->authc_addr, &access,
738 741 p->auth_clnt_cred, &uid, &gid, &ngids, &gids);
739 742
740 743 /*
741 744 * This can only be set in one other place
742 745 * and the state has to be NFS_AUTH_FRESH.
743 746 */
744 747 strfree(netid);
745 748
746 749 mutex_enter(&p->auth_lock);
747 750 if (p->auth_state == NFS_AUTH_INVALID) {
748 751 mutex_exit(&p->auth_lock);
749 752 nfsauth_free_node(p);
750 753 if (retrieval == TRUE)
751 754 kmem_free(gids, ngids * sizeof (gid_t));
752 755 } else {
753 756 /*
754 757 * If we got an error, do not reset the
755 758 * time. This will cause the next access
756 759 * check for the client to reschedule this
757 760 * node.
758 761 */
759 762 if (retrieval == TRUE) {
760 763 p->auth_access = access;
761 764
762 765 p->auth_srv_uid = uid;
763 766 p->auth_srv_gid = gid;
764 767 kmem_free(p->auth_srv_gids,
765 768 p->auth_srv_ngids * sizeof (gid_t));
766 769 p->auth_srv_ngids = ngids;
767 770 p->auth_srv_gids = gids;
768 771
769 772 p->auth_freshness = gethrestime_sec();
770 773 }
771 774 p->auth_state = NFS_AUTH_FRESH;
772 775
773 776 cv_broadcast(&p->auth_cv);
774 777 mutex_exit(&p->auth_lock);
775 778 }
776 779 }
777 780
778 781 list_destroy(&ren->ren_authlist);
779 782 exi_rele(ren->ren_exi);
780 783 kmem_free(ren, sizeof (refreshq_exi_node_t));
781 784 }
782 785
783 786 nag->refreshq_thread_state = REFRESHQ_THREAD_HALTED;
784 787 cv_broadcast(&nag->refreshq_cv);
785 788 CALLB_CPR_EXIT(&cprinfo);
786 789 DTRACE_PROBE(nfsauth__nfsauth__refresh__thread__exit);
787 790 zthread_exit();
788 791 }
789 792
790 793 int
791 794 nfsauth_cache_clnt_compar(const void *v1, const void *v2)
792 795 {
793 796 int c;
794 797
795 798 const struct auth_cache_clnt *a1 = (const struct auth_cache_clnt *)v1;
796 799 const struct auth_cache_clnt *a2 = (const struct auth_cache_clnt *)v2;
797 800
798 801 if (a1->authc_addr.len < a2->authc_addr.len)
799 802 return (-1);
800 803 if (a1->authc_addr.len > a2->authc_addr.len)
801 804 return (1);
802 805
803 806 c = memcmp(a1->authc_addr.buf, a2->authc_addr.buf, a1->authc_addr.len);
804 807 if (c < 0)
805 808 return (-1);
806 809 if (c > 0)
807 810 return (1);
808 811
809 812 return (0);
810 813 }
811 814
812 815 static int
813 816 nfsauth_cache_compar(const void *v1, const void *v2)
814 817 {
815 818 int c;
816 819
817 820 const struct auth_cache *a1 = (const struct auth_cache *)v1;
818 821 const struct auth_cache *a2 = (const struct auth_cache *)v2;
819 822
820 823 if (a1->auth_flavor < a2->auth_flavor)
821 824 return (-1);
822 825 if (a1->auth_flavor > a2->auth_flavor)
823 826 return (1);
824 827
825 828 if (crgetuid(a1->auth_clnt_cred) < crgetuid(a2->auth_clnt_cred))
826 829 return (-1);
827 830 if (crgetuid(a1->auth_clnt_cred) > crgetuid(a2->auth_clnt_cred))
828 831 return (1);
829 832
830 833 if (crgetgid(a1->auth_clnt_cred) < crgetgid(a2->auth_clnt_cred))
831 834 return (-1);
832 835 if (crgetgid(a1->auth_clnt_cred) > crgetgid(a2->auth_clnt_cred))
833 836 return (1);
834 837
835 838 if (crgetngroups(a1->auth_clnt_cred) < crgetngroups(a2->auth_clnt_cred))
836 839 return (-1);
837 840 if (crgetngroups(a1->auth_clnt_cred) > crgetngroups(a2->auth_clnt_cred))
838 841 return (1);
839 842
840 843 c = memcmp(crgetgroups(a1->auth_clnt_cred),
841 844 crgetgroups(a2->auth_clnt_cred), crgetngroups(a1->auth_clnt_cred));
842 845 if (c < 0)
843 846 return (-1);
844 847 if (c > 0)
845 848 return (1);
846 849
847 850 return (0);
848 851 }
849 852
850 853 /*
851 854 * Get the access information from the cache or callup to the mountd
852 855 * to get and cache the access information in the kernel.
853 856 */
854 857 static int
855 858 nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
856 859 cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
857 860 {
858 861 nfsauth_globals_t *nag;
859 862 struct netbuf *taddrmask;
860 863 struct netbuf addr; /* temporary copy of client's address */
861 864 const struct netbuf *claddr;
862 865 avl_tree_t *tree;
863 866 struct auth_cache ac; /* used as a template for avl_find() */
864 867 struct auth_cache_clnt *c;
865 868 struct auth_cache_clnt acc; /* used as a template for avl_find() */
866 869 struct auth_cache *p = NULL;
867 870 int access;
|
↓ open down ↓ |
574 lines elided |
↑ open up ↑ |
868 871
869 872 uid_t tmpuid;
870 873 gid_t tmpgid;
871 874 uint_t tmpngids;
872 875 gid_t *tmpgids;
873 876
874 877 avl_index_t where; /* used for avl_find()/avl_insert() */
875 878
876 879 ASSERT(cr != NULL);
877 880
881 + ASSERT3P(curzone->zone_id, ==, exi->exi_zoneid);
878 882 nag = nfsauth_get_zg();
879 883
880 884 /*
881 885 * Now check whether this client already
882 886 * has an entry for this flavor in the cache
883 887 * for this export.
884 888 * Get the caller's address, mask off the
885 889 * parts of the address that do not identify
886 890 * the host (port number, etc), and then hash
887 891 * it to find the chain of cache entries.
888 892 */
889 893
890 894 claddr = svc_getrpccaller(req->rq_xprt);
891 895 addr = *claddr;
892 896 addr.buf = kmem_alloc(addr.maxlen, KM_SLEEP);
893 897 bcopy(claddr->buf, addr.buf, claddr->len);
894 898
895 899 SVC_GETADDRMASK(req->rq_xprt, SVC_TATTR_ADDRMASK, (void **)&taddrmask);
896 900 ASSERT(taddrmask != NULL);
897 901 addrmask(&addr, taddrmask);
898 902
899 903 ac.auth_flavor = flavor;
900 904 ac.auth_clnt_cred = crdup(cr);
901 905
902 906 acc.authc_addr = addr;
903 907
904 908 tree = exi->exi_cache[hash(&addr)];
905 909
906 910 rw_enter(&exi->exi_cache_lock, RW_READER);
907 911 c = (struct auth_cache_clnt *)avl_find(tree, &acc, NULL);
908 912
909 913 if (c == NULL) {
910 914 struct auth_cache_clnt *nc;
911 915
912 916 rw_exit(&exi->exi_cache_lock);
913 917
914 918 nc = kmem_alloc(sizeof (*nc), KM_NOSLEEP | KM_NORMALPRI);
915 919 if (nc == NULL)
916 920 goto retrieve;
917 921
918 922 /*
919 923 * Initialize the new auth_cache_clnt
920 924 */
921 925 nc->authc_addr = addr;
922 926 nc->authc_addr.buf = kmem_alloc(addr.maxlen,
923 927 KM_NOSLEEP | KM_NORMALPRI);
924 928 if (addr.maxlen != 0 && nc->authc_addr.buf == NULL) {
925 929 kmem_free(nc, sizeof (*nc));
926 930 goto retrieve;
927 931 }
928 932 bcopy(addr.buf, nc->authc_addr.buf, addr.len);
929 933 rw_init(&nc->authc_lock, NULL, RW_DEFAULT, NULL);
930 934 avl_create(&nc->authc_tree, nfsauth_cache_compar,
931 935 sizeof (struct auth_cache),
932 936 offsetof(struct auth_cache, auth_link));
933 937
934 938 rw_enter(&exi->exi_cache_lock, RW_WRITER);
935 939 c = (struct auth_cache_clnt *)avl_find(tree, &acc, &where);
936 940 if (c == NULL) {
937 941 avl_insert(tree, nc, where);
938 942 rw_downgrade(&exi->exi_cache_lock);
939 943 c = nc;
940 944 } else {
941 945 rw_downgrade(&exi->exi_cache_lock);
942 946
943 947 avl_destroy(&nc->authc_tree);
944 948 rw_destroy(&nc->authc_lock);
945 949 kmem_free(nc->authc_addr.buf, nc->authc_addr.maxlen);
946 950 kmem_free(nc, sizeof (*nc));
947 951 }
948 952 }
949 953
950 954 ASSERT(c != NULL);
951 955
952 956 rw_enter(&c->authc_lock, RW_READER);
953 957 p = (struct auth_cache *)avl_find(&c->authc_tree, &ac, NULL);
954 958
955 959 if (p == NULL) {
956 960 struct auth_cache *np;
957 961
958 962 rw_exit(&c->authc_lock);
959 963
960 964 np = kmem_cache_alloc(exi_cache_handle,
961 965 KM_NOSLEEP | KM_NORMALPRI);
962 966 if (np == NULL) {
963 967 rw_exit(&exi->exi_cache_lock);
964 968 goto retrieve;
965 969 }
966 970
967 971 /*
968 972 * Initialize the new auth_cache
969 973 */
970 974 np->auth_clnt = c;
971 975 np->auth_flavor = flavor;
972 976 np->auth_clnt_cred = ac.auth_clnt_cred;
973 977 np->auth_srv_ngids = 0;
974 978 np->auth_srv_gids = NULL;
975 979 np->auth_time = np->auth_freshness = gethrestime_sec();
976 980 np->auth_state = NFS_AUTH_NEW;
977 981 mutex_init(&np->auth_lock, NULL, MUTEX_DEFAULT, NULL);
978 982 cv_init(&np->auth_cv, NULL, CV_DEFAULT, NULL);
979 983
980 984 rw_enter(&c->authc_lock, RW_WRITER);
981 985 rw_exit(&exi->exi_cache_lock);
982 986
983 987 p = (struct auth_cache *)avl_find(&c->authc_tree, &ac, &where);
984 988 if (p == NULL) {
985 989 avl_insert(&c->authc_tree, np, where);
986 990 rw_downgrade(&c->authc_lock);
987 991 p = np;
988 992 } else {
989 993 rw_downgrade(&c->authc_lock);
990 994
991 995 cv_destroy(&np->auth_cv);
992 996 mutex_destroy(&np->auth_lock);
993 997 crfree(ac.auth_clnt_cred);
994 998 kmem_cache_free(exi_cache_handle, np);
995 999 }
996 1000 } else {
997 1001 rw_exit(&exi->exi_cache_lock);
998 1002 crfree(ac.auth_clnt_cred);
999 1003 }
1000 1004
1001 1005 mutex_enter(&p->auth_lock);
1002 1006 rw_exit(&c->authc_lock);
1003 1007
1004 1008 /*
1005 1009 * If the entry is in the WAITING state then some other thread is just
1006 1010 * retrieving the required info. The entry was either NEW, or the list
1007 1011 * of client's supplemental groups is going to be changed (either by
1008 1012 * this thread, or by some other thread). We need to wait until the
1009 1013 * nfsauth_retrieve() is done.
1010 1014 */
1011 1015 while (p->auth_state == NFS_AUTH_WAITING)
1012 1016 cv_wait(&p->auth_cv, &p->auth_lock);
1013 1017
1014 1018 /*
1015 1019 * Here the entry cannot be in WAITING or INVALID state.
1016 1020 */
1017 1021 ASSERT(p->auth_state != NFS_AUTH_WAITING);
1018 1022 ASSERT(p->auth_state != NFS_AUTH_INVALID);
1019 1023
1020 1024 /*
1021 1025 * If the cache entry is not valid yet, we need to retrieve the
1022 1026 * info ourselves.
1023 1027 */
1024 1028 if (p->auth_state == NFS_AUTH_NEW) {
1025 1029 bool_t res;
1026 1030 /*
1027 1031 * NFS_AUTH_NEW is the default output auth_state value in a
1028 1032 * case we failed somewhere below.
1029 1033 */
1030 1034 auth_state_t state = NFS_AUTH_NEW;
1031 1035
1032 1036 p->auth_state = NFS_AUTH_WAITING;
1033 1037 mutex_exit(&p->auth_lock);
1034 1038 kmem_free(addr.buf, addr.maxlen);
1035 1039 addr = p->auth_clnt->authc_addr;
1036 1040
1037 1041 atomic_inc_uint(&nfsauth_cache_miss);
1038 1042
1039 1043 res = nfsauth_retrieve(nag, exi, svc_getnetid(req->rq_xprt),
1040 1044 flavor, &addr, &access, cr, &tmpuid, &tmpgid, &tmpngids,
1041 1045 &tmpgids);
1042 1046
1043 1047 p->auth_access = access;
1044 1048 p->auth_time = p->auth_freshness = gethrestime_sec();
1045 1049
1046 1050 if (res == TRUE) {
1047 1051 if (uid != NULL)
1048 1052 *uid = tmpuid;
1049 1053 if (gid != NULL)
1050 1054 *gid = tmpgid;
1051 1055 if (ngids != NULL && gids != NULL) {
1052 1056 *ngids = tmpngids;
1053 1057 *gids = tmpgids;
1054 1058
1055 1059 /*
1056 1060 * We need a copy of gids for the
1057 1061 * auth_cache entry
1058 1062 */
1059 1063 tmpgids = kmem_alloc(tmpngids * sizeof (gid_t),
1060 1064 KM_NOSLEEP | KM_NORMALPRI);
1061 1065 if (tmpgids != NULL)
1062 1066 bcopy(*gids, tmpgids,
1063 1067 tmpngids * sizeof (gid_t));
1064 1068 }
1065 1069
1066 1070 if (tmpgids != NULL || tmpngids == 0) {
1067 1071 p->auth_srv_uid = tmpuid;
1068 1072 p->auth_srv_gid = tmpgid;
1069 1073 p->auth_srv_ngids = tmpngids;
1070 1074 p->auth_srv_gids = tmpgids;
1071 1075
1072 1076 state = NFS_AUTH_FRESH;
1073 1077 }
1074 1078 }
1075 1079
1076 1080 /*
1077 1081 * Set the auth_state and notify waiters.
1078 1082 */
1079 1083 mutex_enter(&p->auth_lock);
1080 1084 p->auth_state = state;
1081 1085 cv_broadcast(&p->auth_cv);
1082 1086 mutex_exit(&p->auth_lock);
1083 1087 } else {
1084 1088 uint_t nach;
1085 1089 time_t refresh;
1086 1090
1087 1091 refresh = gethrestime_sec() - p->auth_freshness;
1088 1092
1089 1093 p->auth_time = gethrestime_sec();
1090 1094
1091 1095 if (uid != NULL)
1092 1096 *uid = p->auth_srv_uid;
1093 1097 if (gid != NULL)
1094 1098 *gid = p->auth_srv_gid;
1095 1099 if (ngids != NULL && gids != NULL) {
1096 1100 *ngids = p->auth_srv_ngids;
1097 1101 *gids = kmem_alloc(*ngids * sizeof (gid_t), KM_SLEEP);
1098 1102 bcopy(p->auth_srv_gids, *gids, *ngids * sizeof (gid_t));
1099 1103 }
1100 1104
1101 1105 access = p->auth_access;
1102 1106
1103 1107 if ((refresh > NFSAUTH_CACHE_REFRESH) &&
1104 1108 p->auth_state == NFS_AUTH_FRESH) {
1105 1109 refreshq_auth_node_t *ran;
1106 1110 uint_t nacr;
1107 1111
1108 1112 p->auth_state = NFS_AUTH_STALE;
1109 1113 mutex_exit(&p->auth_lock);
1110 1114
1111 1115 nacr = atomic_inc_uint_nv(&nfsauth_cache_refresh);
1112 1116 DTRACE_PROBE3(nfsauth__debug__cache__stale,
1113 1117 struct exportinfo *, exi,
1114 1118 struct auth_cache *, p,
1115 1119 uint_t, nacr);
1116 1120
1117 1121 ran = kmem_alloc(sizeof (refreshq_auth_node_t),
1118 1122 KM_SLEEP);
1119 1123 ran->ran_auth = p;
1120 1124 ran->ran_netid = strdup(svc_getnetid(req->rq_xprt));
1121 1125
1122 1126 mutex_enter(&nag->refreshq_lock);
1123 1127
1124 1128 if (nag->refreshq_thread_state ==
1125 1129 REFRESHQ_THREAD_NEED_CREATE) {
1126 1130 /* Launch nfsauth refresh thread */
1127 1131 nag->refreshq_thread_state =
1128 1132 REFRESHQ_THREAD_RUNNING;
1129 1133 (void) zthread_create(NULL, 0,
1130 1134 nfsauth_refresh_thread, nag, 0,
1131 1135 minclsyspri);
1132 1136 }
1133 1137
1134 1138 /*
1135 1139 * We should not add a work queue item if the thread
1136 1140 * is not accepting them.
1137 1141 */
1138 1142 if (nag->refreshq_thread_state ==
1139 1143 REFRESHQ_THREAD_RUNNING) {
1140 1144 refreshq_exi_node_t *ren;
1141 1145
1142 1146 /*
1143 1147 * Is there an existing exi_list?
1144 1148 */
1145 1149 for (ren = list_head(&nag->refreshq_queue);
1146 1150 ren != NULL;
1147 1151 ren = list_next(&nag->refreshq_queue,
1148 1152 ren)) {
1149 1153 if (ren->ren_exi == exi) {
1150 1154 list_insert_tail(
1151 1155 &ren->ren_authlist, ran);
1152 1156 break;
1153 1157 }
1154 1158 }
1155 1159
1156 1160 if (ren == NULL) {
1157 1161 ren = kmem_alloc(
1158 1162 sizeof (refreshq_exi_node_t),
1159 1163 KM_SLEEP);
1160 1164
1161 1165 exi_hold(exi);
1162 1166 ren->ren_exi = exi;
1163 1167
1164 1168 list_create(&ren->ren_authlist,
1165 1169 sizeof (refreshq_auth_node_t),
1166 1170 offsetof(refreshq_auth_node_t,
1167 1171 ran_node));
1168 1172
1169 1173 list_insert_tail(&ren->ren_authlist,
1170 1174 ran);
1171 1175 list_insert_tail(&nag->refreshq_queue,
1172 1176 ren);
1173 1177 }
1174 1178
1175 1179 cv_broadcast(&nag->refreshq_cv);
1176 1180 } else {
1177 1181 strfree(ran->ran_netid);
1178 1182 kmem_free(ran, sizeof (refreshq_auth_node_t));
1179 1183 }
1180 1184
1181 1185 mutex_exit(&nag->refreshq_lock);
1182 1186 } else {
1183 1187 mutex_exit(&p->auth_lock);
1184 1188 }
1185 1189
1186 1190 nach = atomic_inc_uint_nv(&nfsauth_cache_hit);
1187 1191 DTRACE_PROBE2(nfsauth__debug__cache__hit,
1188 1192 uint_t, nach,
1189 1193 time_t, refresh);
1190 1194
1191 1195 kmem_free(addr.buf, addr.maxlen);
1192 1196 }
1193 1197
1194 1198 return (access);
1195 1199
1196 1200 retrieve:
1197 1201 crfree(ac.auth_clnt_cred);
1198 1202
1199 1203 /*
1200 1204 * Retrieve the required data without caching.
1201 1205 */
1202 1206
1203 1207 ASSERT(p == NULL);
1204 1208
1205 1209 atomic_inc_uint(&nfsauth_cache_miss);
1206 1210
1207 1211 if (nfsauth_retrieve(nag, exi, svc_getnetid(req->rq_xprt), flavor,
1208 1212 &addr, &access, cr, &tmpuid, &tmpgid, &tmpngids, &tmpgids)) {
1209 1213 if (uid != NULL)
1210 1214 *uid = tmpuid;
1211 1215 if (gid != NULL)
1212 1216 *gid = tmpgid;
1213 1217 if (ngids != NULL && gids != NULL) {
1214 1218 *ngids = tmpngids;
1215 1219 *gids = tmpgids;
1216 1220 } else {
1217 1221 kmem_free(tmpgids, tmpngids * sizeof (gid_t));
1218 1222 }
1219 1223 }
1220 1224
1221 1225 kmem_free(addr.buf, addr.maxlen);
1222 1226
1223 1227 return (access);
1224 1228 }
1225 1229
1226 1230 /*
1227 1231 * Check if the requesting client has access to the filesystem with
1228 1232 * a given nfs flavor number which is an explicitly shared flavor.
1229 1233 */
1230 1234 int
1231 1235 nfsauth4_secinfo_access(struct exportinfo *exi, struct svc_req *req,
1232 1236 int flavor, int perm, cred_t *cr)
1233 1237 {
1234 1238 int access;
1235 1239
1236 1240 if (! (perm & M_4SEC_EXPORTED)) {
1237 1241 return (NFSAUTH_DENIED);
1238 1242 }
1239 1243
1240 1244 /*
1241 1245 * Optimize if there are no lists
1242 1246 */
1243 1247 if ((perm & (M_ROOT | M_NONE | M_MAP)) == 0) {
1244 1248 perm &= ~M_4SEC_EXPORTED;
1245 1249 if (perm == M_RO)
1246 1250 return (NFSAUTH_RO);
1247 1251 if (perm == M_RW)
1248 1252 return (NFSAUTH_RW);
1249 1253 }
1250 1254
1251 1255 access = nfsauth_cache_get(exi, req, flavor, cr, NULL, NULL, NULL,
1252 1256 NULL);
1253 1257
1254 1258 return (access);
1255 1259 }
1256 1260
1257 1261 int
1258 1262 nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr,
1259 1263 uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
1260 1264 {
1261 1265 int access, mapaccess;
1262 1266 struct secinfo *sp;
1263 1267 int i, flavor, perm;
1264 1268 int authnone_entry = -1;
1265 1269
1266 1270 /*
1267 1271 * By default root is mapped to anonymous user.
1268 1272 * This might get overriden later in nfsauth_cache_get().
1269 1273 */
1270 1274 if (crgetuid(cr) == 0) {
1271 1275 if (uid != NULL)
1272 1276 *uid = exi->exi_export.ex_anon;
1273 1277 if (gid != NULL)
1274 1278 *gid = exi->exi_export.ex_anon;
1275 1279 } else {
1276 1280 if (uid != NULL)
1277 1281 *uid = crgetuid(cr);
1278 1282 if (gid != NULL)
1279 1283 *gid = crgetgid(cr);
1280 1284 }
1281 1285
1282 1286 if (ngids != NULL)
1283 1287 *ngids = 0;
1284 1288 if (gids != NULL)
1285 1289 *gids = NULL;
1286 1290
1287 1291 /*
1288 1292 * Get the nfs flavor number from xprt.
1289 1293 */
1290 1294 flavor = (int)(uintptr_t)req->rq_xprt->xp_cookie;
1291 1295
1292 1296 /*
1293 1297 * First check the access restrictions on the filesystem. If
1294 1298 * there are no lists associated with this flavor then there's no
1295 1299 * need to make an expensive call to the nfsauth service or to
1296 1300 * cache anything.
1297 1301 */
1298 1302
1299 1303 sp = exi->exi_export.ex_secinfo;
1300 1304 for (i = 0; i < exi->exi_export.ex_seccnt; i++) {
1301 1305 if (flavor != sp[i].s_secinfo.sc_nfsnum) {
1302 1306 if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE)
1303 1307 authnone_entry = i;
1304 1308 continue;
1305 1309 }
1306 1310 break;
1307 1311 }
1308 1312
1309 1313 mapaccess = 0;
1310 1314
1311 1315 if (i >= exi->exi_export.ex_seccnt) {
1312 1316 /*
1313 1317 * Flavor not found, but use AUTH_NONE if it exists
1314 1318 */
1315 1319 if (authnone_entry == -1)
1316 1320 return (NFSAUTH_DENIED);
1317 1321 flavor = AUTH_NONE;
1318 1322 mapaccess = NFSAUTH_MAPNONE;
1319 1323 i = authnone_entry;
1320 1324 }
1321 1325
1322 1326 /*
1323 1327 * If the flavor is in the ex_secinfo list, but not an explicitly
1324 1328 * shared flavor by the user, it is a result of the nfsv4 server
1325 1329 * namespace setup. We will grant an RO permission similar for
1326 1330 * a pseudo node except that this node is a shared one.
1327 1331 *
1328 1332 * e.g. flavor in (flavor) indicates that it is not explictly
1329 1333 * shared by the user:
1330 1334 *
1331 1335 * / (sys, krb5)
1332 1336 * |
1333 1337 * export #share -o sec=sys (krb5)
1334 1338 * |
1335 1339 * secure #share -o sec=krb5
1336 1340 *
1337 1341 * In this case, when a krb5 request coming in to access
1338 1342 * /export, RO permission is granted.
1339 1343 */
1340 1344 if (!(sp[i].s_flags & M_4SEC_EXPORTED))
1341 1345 return (mapaccess | NFSAUTH_RO);
1342 1346
1343 1347 /*
1344 1348 * Optimize if there are no lists.
1345 1349 * We cannot optimize for AUTH_SYS with NGRPS (16) supplemental groups.
1346 1350 */
1347 1351 perm = sp[i].s_flags;
1348 1352 if ((perm & (M_ROOT | M_NONE | M_MAP)) == 0 && (ngroups_max <= NGRPS ||
1349 1353 flavor != AUTH_SYS || crgetngroups(cr) < NGRPS)) {
1350 1354 perm &= ~M_4SEC_EXPORTED;
1351 1355 if (perm == M_RO)
1352 1356 return (mapaccess | NFSAUTH_RO);
1353 1357 if (perm == M_RW)
1354 1358 return (mapaccess | NFSAUTH_RW);
1355 1359 }
1356 1360
1357 1361 access = nfsauth_cache_get(exi, req, flavor, cr, uid, gid, ngids, gids);
1358 1362
1359 1363 /*
1360 1364 * For both NFSAUTH_DENIED and NFSAUTH_WRONGSEC we do not care about
1361 1365 * the supplemental groups.
1362 1366 */
1363 1367 if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) {
1364 1368 if (ngids != NULL && gids != NULL) {
1365 1369 kmem_free(*gids, *ngids * sizeof (gid_t));
1366 1370 *ngids = 0;
1367 1371 *gids = NULL;
1368 1372 }
1369 1373 }
1370 1374
1371 1375 /*
1372 1376 * Client's security flavor doesn't match with "ro" or
1373 1377 * "rw" list. Try again using AUTH_NONE if present.
1374 1378 */
1375 1379 if ((access & NFSAUTH_WRONGSEC) && (flavor != AUTH_NONE)) {
1376 1380 /*
1377 1381 * Have we already encountered AUTH_NONE ?
1378 1382 */
1379 1383 if (authnone_entry != -1) {
1380 1384 mapaccess = NFSAUTH_MAPNONE;
1381 1385 access = nfsauth_cache_get(exi, req, AUTH_NONE, cr,
1382 1386 NULL, NULL, NULL, NULL);
1383 1387 } else {
1384 1388 /*
1385 1389 * Check for AUTH_NONE presence.
1386 1390 */
1387 1391 for (; i < exi->exi_export.ex_seccnt; i++) {
1388 1392 if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE) {
1389 1393 mapaccess = NFSAUTH_MAPNONE;
1390 1394 access = nfsauth_cache_get(exi, req,
1391 1395 AUTH_NONE, cr, NULL, NULL, NULL,
1392 1396 NULL);
1393 1397 break;
1394 1398 }
1395 1399 }
1396 1400 }
1397 1401 }
1398 1402
1399 1403 if (access & NFSAUTH_DENIED)
1400 1404 access = NFSAUTH_DENIED;
1401 1405
1402 1406 return (access | mapaccess);
1403 1407 }
1404 1408
1405 1409 static void
1406 1410 nfsauth_free_clnt_node(struct auth_cache_clnt *p)
1407 1411 {
1408 1412 void *cookie = NULL;
1409 1413 struct auth_cache *node;
1410 1414
1411 1415 while ((node = avl_destroy_nodes(&p->authc_tree, &cookie)) != NULL)
1412 1416 nfsauth_free_node(node);
1413 1417 avl_destroy(&p->authc_tree);
1414 1418
1415 1419 kmem_free(p->authc_addr.buf, p->authc_addr.maxlen);
1416 1420 rw_destroy(&p->authc_lock);
1417 1421
1418 1422 kmem_free(p, sizeof (*p));
1419 1423 }
1420 1424
1421 1425 static void
1422 1426 nfsauth_free_node(struct auth_cache *p)
1423 1427 {
1424 1428 crfree(p->auth_clnt_cred);
1425 1429 kmem_free(p->auth_srv_gids, p->auth_srv_ngids * sizeof (gid_t));
1426 1430 mutex_destroy(&p->auth_lock);
1427 1431 cv_destroy(&p->auth_cv);
1428 1432 kmem_cache_free(exi_cache_handle, p);
1429 1433 }
1430 1434
1431 1435 /*
1432 1436 * Free the nfsauth cache for a given export
1433 1437 */
1434 1438 void
1435 1439 nfsauth_cache_free(struct exportinfo *exi)
1436 1440 {
1437 1441 int i;
1438 1442
1439 1443 /*
1440 1444 * The only way we got here was with an exi_rele, which means that no
1441 1445 * auth cache entry is being refreshed.
1442 1446 */
1443 1447
1444 1448 for (i = 0; i < AUTH_TABLESIZE; i++) {
1445 1449 avl_tree_t *tree = exi->exi_cache[i];
1446 1450 void *cookie = NULL;
1447 1451 struct auth_cache_clnt *node;
1448 1452
1449 1453 while ((node = avl_destroy_nodes(tree, &cookie)) != NULL)
1450 1454 nfsauth_free_clnt_node(node);
1451 1455 }
1452 1456 }
1453 1457
1454 1458 /*
1455 1459 * Called by the kernel memory allocator when memory is low.
1456 1460 * Free unused cache entries. If that's not enough, the VM system
1457 1461 * will call again for some more.
1458 1462 *
1459 1463 * This needs to operate on all zones, so we take a reader lock
1460 1464 * on the list of zones and walk the list. This is OK here
1461 1465 * becuase exi_cache_trim doesn't block or cause new objects
1462 1466 * to be allocated (basically just frees lots of stuff).
1463 1467 * Use care if nfssrv_globals_rwl is taken as reader in any
1464 1468 * other cases because it will block nfs_server_zone_init
1465 1469 * and nfs_server_zone_fini, which enter as writer.
1466 1470 */
1467 1471 /*ARGSUSED*/
1468 1472 void
1469 1473 exi_cache_reclaim(void *cdrarg)
1470 1474 {
1471 1475 nfs_globals_t *ng;
1472 1476
1473 1477 rw_enter(&nfssrv_globals_rwl, RW_READER);
1474 1478
1475 1479 ng = list_head(&nfssrv_globals_list);
1476 1480 while (ng != NULL) {
1477 1481 exi_cache_reclaim_zone(ng);
1478 1482 ng = list_next(&nfssrv_globals_list, ng);
1479 1483 }
1480 1484
1481 1485 rw_exit(&nfssrv_globals_rwl);
1482 1486 }
1483 1487
1484 1488 static void
1485 1489 exi_cache_reclaim_zone(nfs_globals_t *ng)
1486 1490 {
1487 1491 int i;
1488 1492 struct exportinfo *exi;
1489 1493 nfs_export_t *ne = ng->nfs_export;
1490 1494
1491 1495 rw_enter(&ne->exported_lock, RW_READER);
1492 1496
1493 1497 for (i = 0; i < EXPTABLESIZE; i++) {
1494 1498 for (exi = ne->exptable[i]; exi; exi = exi->fid_hash.next)
1495 1499 exi_cache_trim(exi);
1496 1500 }
1497 1501
1498 1502 rw_exit(&ne->exported_lock);
1499 1503
1500 1504 atomic_inc_uint(&nfsauth_cache_reclaim);
1501 1505 }
1502 1506
1503 1507 static void
1504 1508 exi_cache_trim(struct exportinfo *exi)
1505 1509 {
1506 1510 struct auth_cache_clnt *c;
1507 1511 struct auth_cache_clnt *nextc;
1508 1512 struct auth_cache *p;
1509 1513 struct auth_cache *next;
1510 1514 int i;
1511 1515 time_t stale_time;
1512 1516 avl_tree_t *tree;
1513 1517
1514 1518 for (i = 0; i < AUTH_TABLESIZE; i++) {
1515 1519 tree = exi->exi_cache[i];
1516 1520 stale_time = gethrestime_sec() - NFSAUTH_CACHE_TRIM;
1517 1521 rw_enter(&exi->exi_cache_lock, RW_READER);
1518 1522
1519 1523 /*
1520 1524 * Free entries that have not been
1521 1525 * used for NFSAUTH_CACHE_TRIM seconds.
1522 1526 */
1523 1527 for (c = avl_first(tree); c != NULL; c = AVL_NEXT(tree, c)) {
1524 1528 /*
1525 1529 * We are being called by the kmem subsystem to reclaim
1526 1530 * memory so don't block if we can't get the lock.
1527 1531 */
1528 1532 if (rw_tryenter(&c->authc_lock, RW_WRITER) == 0) {
1529 1533 exi_cache_auth_reclaim_failed++;
1530 1534 rw_exit(&exi->exi_cache_lock);
1531 1535 return;
1532 1536 }
1533 1537
1534 1538 for (p = avl_first(&c->authc_tree); p != NULL;
1535 1539 p = next) {
1536 1540 next = AVL_NEXT(&c->authc_tree, p);
1537 1541
1538 1542 ASSERT(p->auth_state != NFS_AUTH_INVALID);
1539 1543
1540 1544 mutex_enter(&p->auth_lock);
1541 1545
1542 1546 /*
1543 1547 * We won't trim recently used and/or WAITING
1544 1548 * entries.
1545 1549 */
1546 1550 if (p->auth_time > stale_time ||
1547 1551 p->auth_state == NFS_AUTH_WAITING) {
1548 1552 mutex_exit(&p->auth_lock);
1549 1553 continue;
1550 1554 }
1551 1555
1552 1556 DTRACE_PROBE1(nfsauth__debug__trim__state,
1553 1557 auth_state_t, p->auth_state);
1554 1558
1555 1559 /*
1556 1560 * STALE and REFRESHING entries needs to be
1557 1561 * marked INVALID only because they are
1558 1562 * referenced by some other structures or
1559 1563 * threads. They will be freed later.
1560 1564 */
1561 1565 if (p->auth_state == NFS_AUTH_STALE ||
1562 1566 p->auth_state == NFS_AUTH_REFRESHING) {
1563 1567 p->auth_state = NFS_AUTH_INVALID;
1564 1568 mutex_exit(&p->auth_lock);
1565 1569
1566 1570 avl_remove(&c->authc_tree, p);
1567 1571 } else {
1568 1572 mutex_exit(&p->auth_lock);
1569 1573
1570 1574 avl_remove(&c->authc_tree, p);
1571 1575 nfsauth_free_node(p);
1572 1576 }
1573 1577 }
1574 1578 rw_exit(&c->authc_lock);
1575 1579 }
1576 1580
1577 1581 if (rw_tryupgrade(&exi->exi_cache_lock) == 0) {
1578 1582 rw_exit(&exi->exi_cache_lock);
1579 1583 exi_cache_clnt_reclaim_failed++;
1580 1584 continue;
1581 1585 }
1582 1586
1583 1587 for (c = avl_first(tree); c != NULL; c = nextc) {
1584 1588 nextc = AVL_NEXT(tree, c);
1585 1589
1586 1590 if (avl_is_empty(&c->authc_tree) == B_FALSE)
1587 1591 continue;
1588 1592
1589 1593 avl_remove(tree, c);
1590 1594
1591 1595 nfsauth_free_clnt_node(c);
1592 1596 }
1593 1597
1594 1598 rw_exit(&exi->exi_cache_lock);
1595 1599 }
1596 1600 }
|
↓ open down ↓ |
709 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX