1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
27
28 /*
29 * University Copyright- Copyright (c) 1982, 1986, 1988
30 * The Regents of the University of California
31 * All Rights Reserved
32 *
33 * University Acknowledgment- Portions of this document are derived from
34 * software developed by the University of California, Berkeley, and its
35 * contributors.
36 */
37
38 #include <sys/types.h>
39 #include <sys/sysmacros.h>
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/cred_impl.h>
43 #include <sys/policy.h>
44 #include <sys/vnode.h>
45 #include <sys/errno.h>
46 #include <sys/kmem.h>
47 #include <sys/user.h>
48 #include <sys/proc.h>
49 #include <sys/syscall.h>
50 #include <sys/debug.h>
51 #include <sys/atomic.h>
52 #include <sys/ucred.h>
53 #include <sys/prsystm.h>
54 #include <sys/modctl.h>
55 #include <sys/avl.h>
56 #include <sys/door.h>
57 #include <c2/audit.h>
58 #include <sys/zone.h>
59 #include <sys/tsol/label.h>
60 #include <sys/sid.h>
61 #include <sys/idmap.h>
62 #include <sys/klpd.h>
63 #include <sys/varargs.h>
64 #include <sys/sysconf.h>
65 #include <util/qsort.h>
66
67
68 /* Ephemeral IDs Zones specific data */
69 typedef struct ephemeral_zsd {
70 uid_t min_uid;
71 uid_t last_uid;
72 gid_t min_gid;
73 gid_t last_gid;
74 kmutex_t eph_lock;
75 cred_t *eph_nobody;
76 } ephemeral_zsd_t;
77
78 static void crgrphold(credgrp_t *);
79
80 #define CREDGRPSZ(ngrp) (sizeof (credgrp_t) + ((ngrp - 1) * sizeof (gid_t)))
81
82 static kmutex_t ephemeral_zone_mutex;
83 static zone_key_t ephemeral_zone_key;
84
85 static struct kmem_cache *cred_cache;
86 static size_t crsize = 0;
87 static int audoff = 0;
88 uint32_t ucredsize;
89 cred_t *kcred;
90 static cred_t *dummycr;
91
92 int rstlink; /* link(2) restricted to files owned by user? */
93
94 static int get_c2audit_load(void);
95
96 #define CR_AUINFO(c) (auditinfo_addr_t *)((audoff == 0) ? NULL : \
97 ((char *)(c)) + audoff)
98
99 #define REMOTE_PEER_CRED(c) ((c)->cr_gid == -1)
100
101 #define BIN_GROUP_SEARCH_CUTOFF 16
102
103 static boolean_t hasephids = B_FALSE;
104
105 static ephemeral_zsd_t *
106 get_ephemeral_zsd(zone_t *zone)
107 {
108 ephemeral_zsd_t *eph_zsd;
109
110 eph_zsd = zone_getspecific(ephemeral_zone_key, zone);
111 if (eph_zsd != NULL) {
112 return (eph_zsd);
113 }
114
115 mutex_enter(&ephemeral_zone_mutex);
116 eph_zsd = zone_getspecific(ephemeral_zone_key, zone);
117 if (eph_zsd == NULL) {
118 eph_zsd = kmem_zalloc(sizeof (ephemeral_zsd_t), KM_SLEEP);
119 eph_zsd->min_uid = MAXUID;
120 eph_zsd->last_uid = IDMAP_WK__MAX_UID;
121 eph_zsd->min_gid = MAXUID;
122 eph_zsd->last_gid = IDMAP_WK__MAX_GID;
123 mutex_init(&eph_zsd->eph_lock, NULL, MUTEX_DEFAULT, NULL);
124
125 /*
126 * nobody is used to map SID containing CRs.
127 */
128 eph_zsd->eph_nobody = crdup(zone->zone_kcred);
129 (void) crsetugid(eph_zsd->eph_nobody, UID_NOBODY, GID_NOBODY);
130 CR_FLAGS(eph_zsd->eph_nobody) = 0;
131 eph_zsd->eph_nobody->cr_zone = zone;
132
133 (void) zone_setspecific(ephemeral_zone_key, zone, eph_zsd);
134 }
135 mutex_exit(&ephemeral_zone_mutex);
136 return (eph_zsd);
137 }
138
139 static cred_t *crdup_flags(const cred_t *, int);
140 static cred_t *cralloc_flags(int);
141
142 /*
143 * This function is called when a zone is destroyed
144 */
145 static void
146 /* ARGSUSED */
147 destroy_ephemeral_zsd(zoneid_t zone_id, void *arg)
148 {
149 ephemeral_zsd_t *eph_zsd = arg;
150 if (eph_zsd != NULL) {
151 mutex_destroy(&eph_zsd->eph_lock);
152 crfree(eph_zsd->eph_nobody);
153 kmem_free(eph_zsd, sizeof (ephemeral_zsd_t));
154 }
155 }
156
157
158
159 /*
160 * Initialize credentials data structures.
161 */
162
163 void
164 cred_init(void)
165 {
166 priv_init();
167
168 crsize = sizeof (cred_t);
169
170 if (get_c2audit_load() > 0) {
171 #ifdef _LP64
172 /* assure audit context is 64-bit aligned */
173 audoff = (crsize +
174 sizeof (int64_t) - 1) & ~(sizeof (int64_t) - 1);
175 #else /* _LP64 */
176 audoff = crsize;
177 #endif /* _LP64 */
178 crsize = audoff + sizeof (auditinfo_addr_t);
179 crsize = (crsize + sizeof (int) - 1) & ~(sizeof (int) - 1);
180 }
181
182 cred_cache = kmem_cache_create("cred_cache", crsize, 0,
183 NULL, NULL, NULL, NULL, NULL, 0);
184
185 /*
186 * dummycr is used to copy initial state for creds.
187 */
188 dummycr = cralloc();
189 bzero(dummycr, crsize);
190 dummycr->cr_ref = 1;
191 dummycr->cr_uid = (uid_t)-1;
192 dummycr->cr_gid = (gid_t)-1;
193 dummycr->cr_ruid = (uid_t)-1;
194 dummycr->cr_rgid = (gid_t)-1;
195 dummycr->cr_suid = (uid_t)-1;
196 dummycr->cr_sgid = (gid_t)-1;
197
198
199 /*
200 * kcred is used by anything that needs all privileges; it's
201 * also the template used for crget as it has all the compatible
202 * sets filled in.
203 */
204 kcred = cralloc();
205
206 bzero(kcred, crsize);
207 kcred->cr_ref = 1;
208
209 /* kcred is never freed, so we don't need zone_cred_hold here */
210 kcred->cr_zone = &zone0;
211
212 priv_fillset(&CR_LPRIV(kcred));
213 CR_IPRIV(kcred) = *priv_basic;
214
215 /* Not a basic privilege, if chown is not restricted add it to I0 */
216 if (!rstchown)
217 priv_addset(&CR_IPRIV(kcred), PRIV_FILE_CHOWN_SELF);
218
219 /* Basic privilege, if link is restricted remove it from I0 */
220 if (rstlink)
221 priv_delset(&CR_IPRIV(kcred), PRIV_FILE_LINK_ANY);
222
223 CR_EPRIV(kcred) = CR_PPRIV(kcred) = CR_IPRIV(kcred);
224
225 CR_FLAGS(kcred) = NET_MAC_AWARE;
226
227 /*
228 * Set up credentials of p0.
229 */
230 ttoproc(curthread)->p_cred = kcred;
231 curthread->t_cred = kcred;
232
233 ucredsize = UCRED_SIZE;
234
235 mutex_init(&ephemeral_zone_mutex, NULL, MUTEX_DEFAULT, NULL);
236 zone_key_create(&ephemeral_zone_key, NULL, NULL, destroy_ephemeral_zsd);
237 }
238
239 /*
240 * Allocate (nearly) uninitialized cred_t.
241 */
242 static cred_t *
243 cralloc_flags(int flgs)
244 {
245 cred_t *cr = kmem_cache_alloc(cred_cache, flgs);
246
247 if (cr == NULL)
248 return (NULL);
249
250 cr->cr_ref = 1; /* So we can crfree() */
251 cr->cr_zone = NULL;
252 cr->cr_label = NULL;
253 cr->cr_ksid = NULL;
254 cr->cr_klpd = NULL;
255 cr->cr_grps = NULL;
256 return (cr);
257 }
258
259 cred_t *
260 cralloc(void)
261 {
262 return (cralloc_flags(KM_SLEEP));
263 }
264
265 /*
266 * As cralloc but prepared for ksid change (if appropriate).
267 */
268 cred_t *
269 cralloc_ksid(void)
270 {
271 cred_t *cr = cralloc();
272 if (hasephids)
273 cr->cr_ksid = kcrsid_alloc();
274 return (cr);
275 }
276
277 /*
278 * Allocate a initialized cred structure and crhold() it.
279 * Initialized means: all ids 0, group count 0, L=Full, E=P=I=I0
280 */
281 cred_t *
282 crget(void)
283 {
284 cred_t *cr = kmem_cache_alloc(cred_cache, KM_SLEEP);
285
286 bcopy(kcred, cr, crsize);
287 cr->cr_ref = 1;
288 zone_cred_hold(cr->cr_zone);
289 if (cr->cr_label)
290 label_hold(cr->cr_label);
291 ASSERT(cr->cr_klpd == NULL);
292 ASSERT(cr->cr_grps == NULL);
293 return (cr);
294 }
295
296 /*
297 * Broadcast the cred to all the threads in the process.
298 * The current thread's credentials can be set right away, but other
299 * threads must wait until the start of the next system call or trap.
300 * This avoids changing the cred in the middle of a system call.
301 *
302 * The cred has already been held for the process and the thread (2 holds),
303 * and p->p_cred set.
304 *
305 * p->p_crlock shouldn't be held here, since p_lock must be acquired.
306 */
307 void
308 crset(proc_t *p, cred_t *cr)
309 {
310 kthread_id_t t;
311 kthread_id_t first;
312 cred_t *oldcr;
313
314 ASSERT(p == curproc); /* assumes p_lwpcnt can't change */
315
316 /*
317 * DTrace accesses t_cred in probe context. t_cred must always be
318 * either NULL, or point to a valid, allocated cred structure.
319 */
320 t = curthread;
321 oldcr = t->t_cred;
322 t->t_cred = cr; /* the cred is held by caller for this thread */
323 crfree(oldcr); /* free the old cred for the thread */
324
325 /*
326 * Broadcast to other threads, if any.
327 */
328 if (p->p_lwpcnt > 1) {
329 mutex_enter(&p->p_lock); /* to keep thread list safe */
330 first = curthread;
331 for (t = first->t_forw; t != first; t = t->t_forw)
332 t->t_pre_sys = 1; /* so syscall will get new cred */
333 mutex_exit(&p->p_lock);
334 }
335 }
336
337 /*
338 * Put a hold on a cred structure.
339 */
340 void
341 crhold(cred_t *cr)
342 {
343 ASSERT(cr->cr_ref != 0xdeadbeef && cr->cr_ref != 0);
344 atomic_add_32(&cr->cr_ref, 1);
345 }
346
347 /*
348 * Release previous hold on a cred structure. Free it if refcnt == 0.
349 * If cred uses label different from zone label, free it.
350 */
351 void
352 crfree(cred_t *cr)
353 {
354 ASSERT(cr->cr_ref != 0xdeadbeef && cr->cr_ref != 0);
355 if (atomic_add_32_nv(&cr->cr_ref, -1) == 0) {
356 ASSERT(cr != kcred);
357 if (cr->cr_label)
358 label_rele(cr->cr_label);
359 if (cr->cr_klpd)
360 crklpd_rele(cr->cr_klpd);
361 if (cr->cr_zone)
362 zone_cred_rele(cr->cr_zone);
363 if (cr->cr_ksid)
364 kcrsid_rele(cr->cr_ksid);
365 if (cr->cr_grps)
366 crgrprele(cr->cr_grps);
367
368 kmem_cache_free(cred_cache, cr);
369 }
370 }
371
372 /*
373 * Copy a cred structure to a new one and free the old one.
374 * The new cred will have two references. One for the calling process,
375 * and one for the thread.
376 */
377 cred_t *
378 crcopy(cred_t *cr)
379 {
380 cred_t *newcr;
381
382 newcr = cralloc();
383 bcopy(cr, newcr, crsize);
384 if (newcr->cr_zone)
385 zone_cred_hold(newcr->cr_zone);
386 if (newcr->cr_label)
387 label_hold(newcr->cr_label);
388 if (newcr->cr_ksid)
389 kcrsid_hold(newcr->cr_ksid);
390 if (newcr->cr_klpd)
391 crklpd_hold(newcr->cr_klpd);
392 if (newcr->cr_grps)
393 crgrphold(newcr->cr_grps);
394 crfree(cr);
395 newcr->cr_ref = 2; /* caller gets two references */
396 return (newcr);
397 }
398
399 /*
400 * Copy a cred structure to a new one and free the old one.
401 * The new cred will have two references. One for the calling process,
402 * and one for the thread.
403 * This variation on crcopy uses a pre-allocated structure for the
404 * "new" cred.
405 */
406 void
407 crcopy_to(cred_t *oldcr, cred_t *newcr)
408 {
409 credsid_t *nkcr = newcr->cr_ksid;
410
411 bcopy(oldcr, newcr, crsize);
412 if (newcr->cr_zone)
413 zone_cred_hold(newcr->cr_zone);
414 if (newcr->cr_label)
415 label_hold(newcr->cr_label);
416 if (newcr->cr_klpd)
417 crklpd_hold(newcr->cr_klpd);
418 if (newcr->cr_grps)
419 crgrphold(newcr->cr_grps);
420 if (nkcr) {
421 newcr->cr_ksid = nkcr;
422 kcrsidcopy_to(oldcr->cr_ksid, newcr->cr_ksid);
423 } else if (newcr->cr_ksid)
424 kcrsid_hold(newcr->cr_ksid);
425 crfree(oldcr);
426 newcr->cr_ref = 2; /* caller gets two references */
427 }
428
429 /*
430 * Dup a cred struct to a new held one.
431 * The old cred is not freed.
432 */
433 static cred_t *
434 crdup_flags(const cred_t *cr, int flgs)
435 {
436 cred_t *newcr;
437
438 newcr = cralloc_flags(flgs);
439
440 if (newcr == NULL)
441 return (NULL);
442
443 bcopy(cr, newcr, crsize);
444 if (newcr->cr_zone)
445 zone_cred_hold(newcr->cr_zone);
446 if (newcr->cr_label)
447 label_hold(newcr->cr_label);
448 if (newcr->cr_klpd)
449 crklpd_hold(newcr->cr_klpd);
450 if (newcr->cr_ksid)
451 kcrsid_hold(newcr->cr_ksid);
452 if (newcr->cr_grps)
453 crgrphold(newcr->cr_grps);
454 newcr->cr_ref = 1;
455 return (newcr);
456 }
457
458 cred_t *
459 crdup(cred_t *cr)
460 {
461 return (crdup_flags(cr, KM_SLEEP));
462 }
463
464 /*
465 * Dup a cred struct to a new held one.
466 * The old cred is not freed.
467 * This variation on crdup uses a pre-allocated structure for the
468 * "new" cred.
469 */
470 void
471 crdup_to(cred_t *oldcr, cred_t *newcr)
472 {
473 credsid_t *nkcr = newcr->cr_ksid;
474
475 bcopy(oldcr, newcr, crsize);
476 if (newcr->cr_zone)
477 zone_cred_hold(newcr->cr_zone);
478 if (newcr->cr_label)
479 label_hold(newcr->cr_label);
480 if (newcr->cr_klpd)
481 crklpd_hold(newcr->cr_klpd);
482 if (newcr->cr_grps)
483 crgrphold(newcr->cr_grps);
484 if (nkcr) {
485 newcr->cr_ksid = nkcr;
486 kcrsidcopy_to(oldcr->cr_ksid, newcr->cr_ksid);
487 } else if (newcr->cr_ksid)
488 kcrsid_hold(newcr->cr_ksid);
489 newcr->cr_ref = 1;
490 }
491
492 /*
493 * Return the (held) credentials for the current running process.
494 */
495 cred_t *
496 crgetcred(void)
497 {
498 cred_t *cr;
499 proc_t *p;
500
501 p = ttoproc(curthread);
502 mutex_enter(&p->p_crlock);
503 crhold(cr = p->p_cred);
504 mutex_exit(&p->p_crlock);
505 return (cr);
506 }
507
508 /*
509 * Backward compatibility check for suser().
510 * Accounting flag is now set in the policy functions; auditing is
511 * done through use of privilege in the audit trail.
512 */
513 int
514 suser(cred_t *cr)
515 {
516 return (PRIV_POLICY(cr, PRIV_SYS_SUSER_COMPAT, B_FALSE, EPERM, NULL)
517 == 0);
518 }
519
520 /*
521 * Determine whether the supplied group id is a member of the group
522 * described by the supplied credentials.
523 */
524 int
525 groupmember(gid_t gid, const cred_t *cr)
526 {
527 if (gid == cr->cr_gid)
528 return (1);
529 return (supgroupmember(gid, cr));
530 }
531
532 /*
533 * As groupmember but only check against the supplemental groups.
534 */
535 int
536 supgroupmember(gid_t gid, const cred_t *cr)
537 {
538 int hi, lo;
539 credgrp_t *grps = cr->cr_grps;
540 const gid_t *gp, *endgp;
541
542 if (grps == NULL)
543 return (0);
544
545 /* For a small number of groups, use sequentials search. */
546 if (grps->crg_ngroups <= BIN_GROUP_SEARCH_CUTOFF) {
547 endgp = &grps->crg_groups[grps->crg_ngroups];
548 for (gp = grps->crg_groups; gp < endgp; gp++)
549 if (*gp == gid)
550 return (1);
551 return (0);
552 }
553
554 /* We use binary search when we have many groups. */
555 lo = 0;
556 hi = grps->crg_ngroups - 1;
557 gp = grps->crg_groups;
558
559 do {
560 int m = (lo + hi) / 2;
561
562 if (gid > gp[m])
563 lo = m + 1;
564 else if (gid < gp[m])
565 hi = m - 1;
566 else
567 return (1);
568 } while (lo <= hi);
569
570 return (0);
571 }
572
573 /*
574 * This function is called to check whether the credentials set
575 * "scrp" has permission to act on credentials set "tcrp". It enforces the
576 * permission requirements needed to send a signal to a process.
577 * The same requirements are imposed by other system calls, however.
578 *
579 * The rules are:
580 * (1) if the credentials are the same, the check succeeds
581 * (2) if the zone ids don't match, and scrp is not in the global zone or
582 * does not have the PRIV_PROC_ZONE privilege, the check fails
583 * (3) if the real or effective user id of scrp matches the real or saved
584 * user id of tcrp or scrp has the PRIV_PROC_OWNER privilege, the check
585 * succeeds
586 * (4) otherwise, the check fails
587 */
588 int
589 hasprocperm(const cred_t *tcrp, const cred_t *scrp)
590 {
591 if (scrp == tcrp)
592 return (1);
593 if (scrp->cr_zone != tcrp->cr_zone &&
594 (scrp->cr_zone != global_zone ||
595 secpolicy_proc_zone(scrp) != 0))
596 return (0);
597 if (scrp->cr_uid == tcrp->cr_ruid ||
598 scrp->cr_ruid == tcrp->cr_ruid ||
599 scrp->cr_uid == tcrp->cr_suid ||
600 scrp->cr_ruid == tcrp->cr_suid ||
601 !PRIV_POLICY(scrp, PRIV_PROC_OWNER, B_FALSE, EPERM, "hasprocperm"))
602 return (1);
603 return (0);
604 }
605
606 /*
607 * This interface replaces hasprocperm; it works like hasprocperm but
608 * additionally returns success if the proc_t's match
609 * It is the preferred interface for most uses.
610 * And it will acquire p_crlock itself, so it assert's that it shouldn't
611 * be held.
612 */
613 int
614 prochasprocperm(proc_t *tp, proc_t *sp, const cred_t *scrp)
615 {
616 int rets;
617 cred_t *tcrp;
618
619 ASSERT(MUTEX_NOT_HELD(&tp->p_crlock));
620
621 if (tp == sp)
622 return (1);
623
624 if (tp->p_sessp != sp->p_sessp && secpolicy_basic_proc(scrp) != 0)
625 return (0);
626
627 mutex_enter(&tp->p_crlock);
628 crhold(tcrp = tp->p_cred);
629 mutex_exit(&tp->p_crlock);
630 rets = hasprocperm(tcrp, scrp);
631 crfree(tcrp);
632
633 return (rets);
634 }
635
636 /*
637 * This routine is used to compare two credentials to determine if
638 * they refer to the same "user". If the pointers are equal, then
639 * they must refer to the same user. Otherwise, the contents of
640 * the credentials are compared to see whether they are equivalent.
641 *
642 * This routine returns 0 if the credentials refer to the same user,
643 * 1 if they do not.
644 */
645 int
646 crcmp(const cred_t *cr1, const cred_t *cr2)
647 {
648 credgrp_t *grp1, *grp2;
649
650 if (cr1 == cr2)
651 return (0);
652
653 if (cr1->cr_uid == cr2->cr_uid &&
654 cr1->cr_gid == cr2->cr_gid &&
655 cr1->cr_ruid == cr2->cr_ruid &&
656 cr1->cr_rgid == cr2->cr_rgid &&
657 cr1->cr_zone == cr2->cr_zone &&
658 ((grp1 = cr1->cr_grps) == (grp2 = cr2->cr_grps) ||
659 (grp1 != NULL && grp2 != NULL &&
660 grp1->crg_ngroups == grp2->crg_ngroups &&
661 bcmp(grp1->crg_groups, grp2->crg_groups,
662 grp1->crg_ngroups * sizeof (gid_t)) == 0))) {
663 return (!priv_isequalset(&CR_OEPRIV(cr1), &CR_OEPRIV(cr2)));
664 }
665 return (1);
666 }
667
668 /*
669 * Read access functions to cred_t.
670 */
671 uid_t
672 crgetuid(const cred_t *cr)
673 {
674 return (cr->cr_uid);
675 }
676
677 uid_t
678 crgetruid(const cred_t *cr)
679 {
680 return (cr->cr_ruid);
681 }
682
683 uid_t
684 crgetsuid(const cred_t *cr)
685 {
686 return (cr->cr_suid);
687 }
688
689 gid_t
690 crgetgid(const cred_t *cr)
691 {
692 return (cr->cr_gid);
693 }
694
695 gid_t
696 crgetrgid(const cred_t *cr)
697 {
698 return (cr->cr_rgid);
699 }
700
701 gid_t
702 crgetsgid(const cred_t *cr)
703 {
704 return (cr->cr_sgid);
705 }
706
707 const auditinfo_addr_t *
708 crgetauinfo(const cred_t *cr)
709 {
710 return ((const auditinfo_addr_t *)CR_AUINFO(cr));
711 }
712
713 auditinfo_addr_t *
714 crgetauinfo_modifiable(cred_t *cr)
715 {
716 return (CR_AUINFO(cr));
717 }
718
719 zoneid_t
720 crgetzoneid(const cred_t *cr)
721 {
722 return (cr->cr_zone == NULL ?
723 (cr->cr_uid == -1 ? (zoneid_t)-1 : GLOBAL_ZONEID) :
724 cr->cr_zone->zone_id);
725 }
726
727 projid_t
728 crgetprojid(const cred_t *cr)
729 {
730 return (cr->cr_projid);
731 }
732
733 zone_t *
734 crgetzone(const cred_t *cr)
735 {
736 return (cr->cr_zone);
737 }
738
739 struct ts_label_s *
740 crgetlabel(const cred_t *cr)
741 {
742 return (cr->cr_label ?
743 cr->cr_label :
744 (cr->cr_zone ? cr->cr_zone->zone_slabel : NULL));
745 }
746
747 boolean_t
748 crisremote(const cred_t *cr)
749 {
750 return (REMOTE_PEER_CRED(cr));
751 }
752
753 #define BADUID(x, zn) ((x) != -1 && !VALID_UID((x), (zn)))
754 #define BADGID(x, zn) ((x) != -1 && !VALID_GID((x), (zn)))
755
756 int
757 crsetresuid(cred_t *cr, uid_t r, uid_t e, uid_t s)
758 {
759 zone_t *zone = crgetzone(cr);
760
761 ASSERT(cr->cr_ref <= 2);
762
763 if (BADUID(r, zone) || BADUID(e, zone) || BADUID(s, zone))
764 return (-1);
765
766 if (r != -1)
767 cr->cr_ruid = r;
768 if (e != -1)
769 cr->cr_uid = e;
770 if (s != -1)
771 cr->cr_suid = s;
772
773 return (0);
774 }
775
776 int
777 crsetresgid(cred_t *cr, gid_t r, gid_t e, gid_t s)
778 {
779 zone_t *zone = crgetzone(cr);
780
781 ASSERT(cr->cr_ref <= 2);
782
783 if (BADGID(r, zone) || BADGID(e, zone) || BADGID(s, zone))
784 return (-1);
785
786 if (r != -1)
787 cr->cr_rgid = r;
788 if (e != -1)
789 cr->cr_gid = e;
790 if (s != -1)
791 cr->cr_sgid = s;
792
793 return (0);
794 }
795
796 int
797 crsetugid(cred_t *cr, uid_t uid, gid_t gid)
798 {
799 zone_t *zone = crgetzone(cr);
800
801 ASSERT(cr->cr_ref <= 2);
802
803 if (!VALID_UID(uid, zone) || !VALID_GID(gid, zone))
804 return (-1);
805
806 cr->cr_uid = cr->cr_ruid = cr->cr_suid = uid;
807 cr->cr_gid = cr->cr_rgid = cr->cr_sgid = gid;
808
809 return (0);
810 }
811
812 static int
813 gidcmp(const void *v1, const void *v2)
814 {
815 gid_t g1 = *(gid_t *)v1;
816 gid_t g2 = *(gid_t *)v2;
817
818 if (g1 < g2)
819 return (-1);
820 else if (g1 > g2)
821 return (1);
822 else
823 return (0);
824 }
825
826 int
827 crsetgroups(cred_t *cr, int n, gid_t *grp)
828 {
829 ASSERT(cr->cr_ref <= 2);
830
831 if (n > ngroups_max || n < 0)
832 return (-1);
833
834 if (cr->cr_grps != NULL)
835 crgrprele(cr->cr_grps);
836
837 if (n > 0) {
838 cr->cr_grps = kmem_alloc(CREDGRPSZ(n), KM_SLEEP);
839 bcopy(grp, cr->cr_grps->crg_groups, n * sizeof (gid_t));
840 cr->cr_grps->crg_ref = 1;
841 cr->cr_grps->crg_ngroups = n;
842 qsort(cr->cr_grps->crg_groups, n, sizeof (gid_t), gidcmp);
843 } else {
844 cr->cr_grps = NULL;
845 }
846
847 return (0);
848 }
849
850 void
851 crsetprojid(cred_t *cr, projid_t projid)
852 {
853 ASSERT(projid >= 0 && projid <= MAXPROJID);
854 cr->cr_projid = projid;
855 }
856
857 /*
858 * This routine returns the pointer to the first element of the crg_groups
859 * array. It can move around in an implementation defined way.
860 * Note that when we have no grouplist, we return one element but the
861 * caller should never reference it.
862 */
863 const gid_t *
864 crgetgroups(const cred_t *cr)
865 {
866 return (cr->cr_grps == NULL ? &cr->cr_gid : cr->cr_grps->crg_groups);
867 }
868
869 int
870 crgetngroups(const cred_t *cr)
871 {
872 return (cr->cr_grps == NULL ? 0 : cr->cr_grps->crg_ngroups);
873 }
874
875 void
876 cred2prcred(const cred_t *cr, prcred_t *pcrp)
877 {
878 pcrp->pr_euid = cr->cr_uid;
879 pcrp->pr_ruid = cr->cr_ruid;
880 pcrp->pr_suid = cr->cr_suid;
881 pcrp->pr_egid = cr->cr_gid;
882 pcrp->pr_rgid = cr->cr_rgid;
883 pcrp->pr_sgid = cr->cr_sgid;
884 pcrp->pr_groups[0] = 0; /* in case ngroups == 0 */
885 pcrp->pr_ngroups = cr->cr_grps == NULL ? 0 : cr->cr_grps->crg_ngroups;
886
887 if (pcrp->pr_ngroups != 0)
888 bcopy(cr->cr_grps->crg_groups, pcrp->pr_groups,
889 sizeof (gid_t) * pcrp->pr_ngroups);
890 }
891
892 static int
893 cred2ucaud(const cred_t *cr, auditinfo64_addr_t *ainfo, const cred_t *rcr)
894 {
895 auditinfo_addr_t *ai;
896 au_tid_addr_t tid;
897
898 if (secpolicy_audit_getattr(rcr, B_TRUE) != 0)
899 return (-1);
900
901 ai = CR_AUINFO(cr); /* caller makes sure this is non-NULL */
902 tid = ai->ai_termid;
903
904 ainfo->ai_auid = ai->ai_auid;
905 ainfo->ai_mask = ai->ai_mask;
906 ainfo->ai_asid = ai->ai_asid;
907
908 ainfo->ai_termid.at_type = tid.at_type;
909 bcopy(&tid.at_addr, &ainfo->ai_termid.at_addr, 4 * sizeof (uint_t));
910
911 ainfo->ai_termid.at_port.at_major = (uint32_t)getmajor(tid.at_port);
912 ainfo->ai_termid.at_port.at_minor = (uint32_t)getminor(tid.at_port);
913
914 return (0);
915 }
916
917 void
918 cred2uclabel(const cred_t *cr, bslabel_t *labelp)
919 {
920 ts_label_t *tslp;
921
922 if ((tslp = crgetlabel(cr)) != NULL)
923 bcopy(&tslp->tsl_label, labelp, sizeof (bslabel_t));
924 }
925
926 /*
927 * Convert a credential into a "ucred". Allow the caller to specify
928 * and aligned buffer, e.g., in an mblk, so we don't have to allocate
929 * memory and copy it twice.
930 *
931 * This function may call cred2ucaud(), which calls CRED(). Since this
932 * can be called from an interrupt thread, receiver's cred (rcr) is needed
933 * to determine whether audit info should be included.
934 */
935 struct ucred_s *
936 cred2ucred(const cred_t *cr, pid_t pid, void *buf, const cred_t *rcr)
937 {
938 struct ucred_s *uc;
939 uint32_t realsz = ucredminsize(cr);
940 ts_label_t *tslp = is_system_labeled() ? crgetlabel(cr) : NULL;
941
942 /* The structure isn't always completely filled in, so zero it */
943 if (buf == NULL) {
944 uc = kmem_zalloc(realsz, KM_SLEEP);
945 } else {
946 bzero(buf, realsz);
947 uc = buf;
948 }
949 uc->uc_size = realsz;
950 uc->uc_pid = pid;
951 uc->uc_projid = cr->cr_projid;
952 uc->uc_zoneid = crgetzoneid(cr);
953
954 if (REMOTE_PEER_CRED(cr)) {
955 /*
956 * Other than label, the rest of cred info about a
957 * remote peer isn't available. Copy the label directly
958 * after the header where we generally copy the prcred.
959 * That's why we use sizeof (struct ucred_s). The other
960 * offset fields are initialized to 0.
961 */
962 uc->uc_labeloff = tslp == NULL ? 0 : sizeof (struct ucred_s);
963 } else {
964 uc->uc_credoff = UCRED_CRED_OFF;
965 uc->uc_privoff = UCRED_PRIV_OFF;
966 uc->uc_audoff = UCRED_AUD_OFF;
967 uc->uc_labeloff = tslp == NULL ? 0 : UCRED_LABEL_OFF;
968
969 cred2prcred(cr, UCCRED(uc));
970 cred2prpriv(cr, UCPRIV(uc));
971
972 if (audoff == 0 || cred2ucaud(cr, UCAUD(uc), rcr) != 0)
973 uc->uc_audoff = 0;
974 }
975 if (tslp != NULL)
976 bcopy(&tslp->tsl_label, UCLABEL(uc), sizeof (bslabel_t));
977
978 return (uc);
979 }
980
981 /*
982 * Don't allocate the non-needed group entries. Note: this function
983 * must match the code in cred2ucred; they must agree about the
984 * minimal size of the ucred.
985 */
986 uint32_t
987 ucredminsize(const cred_t *cr)
988 {
989 int ndiff;
990
991 if (cr == NULL)
992 return (ucredsize);
993
994 if (REMOTE_PEER_CRED(cr)) {
995 if (is_system_labeled())
996 return (sizeof (struct ucred_s) + sizeof (bslabel_t));
997 else
998 return (sizeof (struct ucred_s));
999 }
1000
1001 if (cr->cr_grps == NULL)
1002 ndiff = ngroups_max - 1; /* Needs one for prcred_t */
1003 else
1004 ndiff = ngroups_max - cr->cr_grps->crg_ngroups;
1005
1006 return (ucredsize - ndiff * sizeof (gid_t));
1007 }
1008
1009 /*
1010 * Get the "ucred" of a process.
1011 */
1012 struct ucred_s *
1013 pgetucred(proc_t *p)
1014 {
1015 cred_t *cr;
1016 struct ucred_s *uc;
1017
1018 mutex_enter(&p->p_crlock);
1019 cr = p->p_cred;
1020 crhold(cr);
1021 mutex_exit(&p->p_crlock);
1022
1023 uc = cred2ucred(cr, p->p_pid, NULL, CRED());
1024 crfree(cr);
1025
1026 return (uc);
1027 }
1028
1029 /*
1030 * If the reply status is NFSERR_EACCES, it may be because we are
1031 * root (no root net access). Check the real uid, if it isn't root
1032 * make that the uid instead and retry the call.
1033 * Private interface for NFS.
1034 */
1035 cred_t *
1036 crnetadjust(cred_t *cr)
1037 {
1038 if (cr->cr_uid == 0 && cr->cr_ruid != 0) {
1039 cr = crdup(cr);
1040 cr->cr_uid = cr->cr_ruid;
1041 return (cr);
1042 }
1043 return (NULL);
1044 }
1045
1046 /*
1047 * The reference count is of interest when you want to check
1048 * whether it is ok to modify the credential in place.
1049 */
1050 uint_t
1051 crgetref(const cred_t *cr)
1052 {
1053 return (cr->cr_ref);
1054 }
1055
1056 static int
1057 get_c2audit_load(void)
1058 {
1059 static int gotit = 0;
1060 static int c2audit_load;
1061
1062 if (gotit)
1063 return (c2audit_load);
1064 c2audit_load = 1; /* set default value once */
1065 if (mod_sysctl(SYS_CHECK_EXCLUDE, "c2audit") != 0)
1066 c2audit_load = 0;
1067 gotit++;
1068
1069 return (c2audit_load);
1070 }
1071
1072 int
1073 get_audit_ucrsize(void)
1074 {
1075 return (get_c2audit_load() ? sizeof (auditinfo64_addr_t) : 0);
1076 }
1077
1078 /*
1079 * Set zone pointer in credential to indicated value. First adds a
1080 * hold for the new zone, then drops the hold on previous zone (if any).
1081 * This is done in this order in case the old and new zones are the
1082 * same.
1083 */
1084 void
1085 crsetzone(cred_t *cr, zone_t *zptr)
1086 {
1087 zone_t *oldzptr = cr->cr_zone;
1088
1089 ASSERT(cr != kcred);
1090 ASSERT(cr->cr_ref <= 2);
1091 cr->cr_zone = zptr;
1092 zone_cred_hold(zptr);
1093 if (oldzptr)
1094 zone_cred_rele(oldzptr);
1095 }
1096
1097 /*
1098 * Create a new cred based on the supplied label
1099 */
1100 cred_t *
1101 newcred_from_bslabel(bslabel_t *blabel, uint32_t doi, int flags)
1102 {
1103 ts_label_t *lbl = labelalloc(blabel, doi, flags);
1104 cred_t *cr = NULL;
1105
1106 if (lbl != NULL) {
1107 if ((cr = crdup_flags(dummycr, flags)) != NULL) {
1108 cr->cr_label = lbl;
1109 } else {
1110 label_rele(lbl);
1111 }
1112 }
1113
1114 return (cr);
1115 }
1116
1117 /*
1118 * Derive a new cred from the existing cred, but with a different label.
1119 * To be used when a cred is being shared, but the label needs to be changed
1120 * by a caller without affecting other users
1121 */
1122 cred_t *
1123 copycred_from_tslabel(const cred_t *cr, ts_label_t *label, int flags)
1124 {
1125 cred_t *newcr = NULL;
1126
1127 if ((newcr = crdup_flags(cr, flags)) != NULL) {
1128 if (newcr->cr_label != NULL)
1129 label_rele(newcr->cr_label);
1130 label_hold(label);
1131 newcr->cr_label = label;
1132 }
1133
1134 return (newcr);
1135 }
1136
1137 /*
1138 * Derive a new cred from the existing cred, but with a different label.
1139 */
1140 cred_t *
1141 copycred_from_bslabel(const cred_t *cr, bslabel_t *blabel,
1142 uint32_t doi, int flags)
1143 {
1144 ts_label_t *lbl = labelalloc(blabel, doi, flags);
1145 cred_t *newcr = NULL;
1146
1147 if (lbl != NULL) {
1148 newcr = copycred_from_tslabel(cr, lbl, flags);
1149 label_rele(lbl);
1150 }
1151
1152 return (newcr);
1153 }
1154
1155 /*
1156 * This function returns a pointer to the kcred-equivalent in the current zone.
1157 */
1158 cred_t *
1159 zone_kcred(void)
1160 {
1161 zone_t *zone;
1162
1163 if ((zone = CRED()->cr_zone) != NULL)
1164 return (zone->zone_kcred);
1165 else
1166 return (kcred);
1167 }
1168
1169 boolean_t
1170 valid_ephemeral_uid(zone_t *zone, uid_t id)
1171 {
1172 ephemeral_zsd_t *eph_zsd;
1173 if (id <= IDMAP_WK__MAX_UID)
1174 return (B_TRUE);
1175
1176 eph_zsd = get_ephemeral_zsd(zone);
1177 ASSERT(eph_zsd != NULL);
1178 membar_consumer();
1179 return (id > eph_zsd->min_uid && id <= eph_zsd->last_uid);
1180 }
1181
1182 boolean_t
1183 valid_ephemeral_gid(zone_t *zone, gid_t id)
1184 {
1185 ephemeral_zsd_t *eph_zsd;
1186 if (id <= IDMAP_WK__MAX_GID)
1187 return (B_TRUE);
1188
1189 eph_zsd = get_ephemeral_zsd(zone);
1190 ASSERT(eph_zsd != NULL);
1191 membar_consumer();
1192 return (id > eph_zsd->min_gid && id <= eph_zsd->last_gid);
1193 }
1194
1195 int
1196 eph_uid_alloc(zone_t *zone, int flags, uid_t *start, int count)
1197 {
1198 ephemeral_zsd_t *eph_zsd = get_ephemeral_zsd(zone);
1199
1200 ASSERT(eph_zsd != NULL);
1201
1202 mutex_enter(&eph_zsd->eph_lock);
1203
1204 /* Test for unsigned integer wrap around */
1205 if (eph_zsd->last_uid + count < eph_zsd->last_uid) {
1206 mutex_exit(&eph_zsd->eph_lock);
1207 return (-1);
1208 }
1209
1210 /* first call or idmap crashed and state corrupted */
1211 if (flags != 0)
1212 eph_zsd->min_uid = eph_zsd->last_uid;
1213
1214 hasephids = B_TRUE;
1215 *start = eph_zsd->last_uid + 1;
1216 atomic_add_32(&eph_zsd->last_uid, count);
1217 mutex_exit(&eph_zsd->eph_lock);
1218 return (0);
1219 }
1220
1221 int
1222 eph_gid_alloc(zone_t *zone, int flags, gid_t *start, int count)
1223 {
1224 ephemeral_zsd_t *eph_zsd = get_ephemeral_zsd(zone);
1225
1226 ASSERT(eph_zsd != NULL);
1227
1228 mutex_enter(&eph_zsd->eph_lock);
1229
1230 /* Test for unsigned integer wrap around */
1231 if (eph_zsd->last_gid + count < eph_zsd->last_gid) {
1232 mutex_exit(&eph_zsd->eph_lock);
1233 return (-1);
1234 }
1235
1236 /* first call or idmap crashed and state corrupted */
1237 if (flags != 0)
1238 eph_zsd->min_gid = eph_zsd->last_gid;
1239
1240 hasephids = B_TRUE;
1241 *start = eph_zsd->last_gid + 1;
1242 atomic_add_32(&eph_zsd->last_gid, count);
1243 mutex_exit(&eph_zsd->eph_lock);
1244 return (0);
1245 }
1246
1247 /*
1248 * IMPORTANT.The two functions get_ephemeral_data() and set_ephemeral_data()
1249 * are project private functions that are for use of the test system only and
1250 * are not to be used for other purposes.
1251 */
1252
1253 void
1254 get_ephemeral_data(zone_t *zone, uid_t *min_uid, uid_t *last_uid,
1255 gid_t *min_gid, gid_t *last_gid)
1256 {
1257 ephemeral_zsd_t *eph_zsd = get_ephemeral_zsd(zone);
1258
1259 ASSERT(eph_zsd != NULL);
1260
1261 mutex_enter(&eph_zsd->eph_lock);
1262
1263 *min_uid = eph_zsd->min_uid;
1264 *last_uid = eph_zsd->last_uid;
1265 *min_gid = eph_zsd->min_gid;
1266 *last_gid = eph_zsd->last_gid;
1267
1268 mutex_exit(&eph_zsd->eph_lock);
1269 }
1270
1271
1272 void
1273 set_ephemeral_data(zone_t *zone, uid_t min_uid, uid_t last_uid,
1274 gid_t min_gid, gid_t last_gid)
1275 {
1276 ephemeral_zsd_t *eph_zsd = get_ephemeral_zsd(zone);
1277
1278 ASSERT(eph_zsd != NULL);
1279
1280 mutex_enter(&eph_zsd->eph_lock);
1281
1282 if (min_uid != 0)
1283 eph_zsd->min_uid = min_uid;
1284 if (last_uid != 0)
1285 eph_zsd->last_uid = last_uid;
1286 if (min_gid != 0)
1287 eph_zsd->min_gid = min_gid;
1288 if (last_gid != 0)
1289 eph_zsd->last_gid = last_gid;
1290
1291 mutex_exit(&eph_zsd->eph_lock);
1292 }
1293
1294 /*
1295 * If the credential user SID or group SID is mapped to an ephemeral
1296 * ID, map the credential to nobody.
1297 */
1298 cred_t *
1299 crgetmapped(const cred_t *cr)
1300 {
1301 ephemeral_zsd_t *eph_zsd;
1302 /*
1303 * Someone incorrectly passed a NULL cred to a vnode operation
1304 * either on purpose or by calling CRED() in interrupt context.
1305 */
1306 if (cr == NULL)
1307 return (NULL);
1308
1309 if (cr->cr_ksid != NULL) {
1310 if (cr->cr_ksid->kr_sidx[KSID_USER].ks_id > MAXUID) {
1311 eph_zsd = get_ephemeral_zsd(crgetzone(cr));
1312 return (eph_zsd->eph_nobody);
1313 }
1314
1315 if (cr->cr_ksid->kr_sidx[KSID_GROUP].ks_id > MAXUID) {
1316 eph_zsd = get_ephemeral_zsd(crgetzone(cr));
1317 return (eph_zsd->eph_nobody);
1318 }
1319 }
1320
1321 return ((cred_t *)cr);
1322 }
1323
1324 /* index should be in range for a ksidindex_t */
1325 void
1326 crsetsid(cred_t *cr, ksid_t *ksp, int index)
1327 {
1328 ASSERT(cr->cr_ref <= 2);
1329 ASSERT(index >= 0 && index < KSID_COUNT);
1330 if (cr->cr_ksid == NULL && ksp == NULL)
1331 return;
1332 cr->cr_ksid = kcrsid_setsid(cr->cr_ksid, ksp, index);
1333 }
1334
1335 void
1336 crsetsidlist(cred_t *cr, ksidlist_t *ksl)
1337 {
1338 ASSERT(cr->cr_ref <= 2);
1339 if (cr->cr_ksid == NULL && ksl == NULL)
1340 return;
1341 cr->cr_ksid = kcrsid_setsidlist(cr->cr_ksid, ksl);
1342 }
1343
1344 ksid_t *
1345 crgetsid(const cred_t *cr, int i)
1346 {
1347 ASSERT(i >= 0 && i < KSID_COUNT);
1348 if (cr->cr_ksid != NULL && cr->cr_ksid->kr_sidx[i].ks_domain)
1349 return ((ksid_t *)&cr->cr_ksid->kr_sidx[i]);
1350 return (NULL);
1351 }
1352
1353 ksidlist_t *
1354 crgetsidlist(const cred_t *cr)
1355 {
1356 if (cr->cr_ksid != NULL)
1357 return (cr->cr_ksid->kr_sidlist);
1358 return (NULL);
1359 }
1360
1361 /*
1362 * Interface to set the effective and permitted privileges for
1363 * a credential; this interface does no security checks and is
1364 * intended for kernel (file)servers creating credentials with
1365 * specific privileges.
1366 */
1367 int
1368 crsetpriv(cred_t *cr, ...)
1369 {
1370 va_list ap;
1371 const char *privnm;
1372
1373 ASSERT(cr->cr_ref <= 2);
1374
1375 priv_set_PA(cr);
1376
1377 va_start(ap, cr);
1378
1379 while ((privnm = va_arg(ap, const char *)) != NULL) {
1380 int priv = priv_getbyname(privnm, 0);
1381 if (priv < 0)
1382 return (-1);
1383
1384 priv_addset(&CR_PPRIV(cr), priv);
1385 priv_addset(&CR_EPRIV(cr), priv);
1386 }
1387 priv_adjust_PA(cr);
1388 va_end(ap);
1389 return (0);
1390 }
1391
1392 /*
1393 * Interface to effectively set the PRIV_ALL for
1394 * a credential; this interface does no security checks and is
1395 * intended for kernel (file)servers to extend the user credentials
1396 * to be ALL, like either kcred or zcred.
1397 */
1398 void
1399 crset_zone_privall(cred_t *cr)
1400 {
1401 zone_t *zone = crgetzone(cr);
1402
1403 priv_fillset(&CR_LPRIV(cr));
1404 CR_EPRIV(cr) = CR_PPRIV(cr) = CR_IPRIV(cr) = CR_LPRIV(cr);
1405 priv_intersect(zone->zone_privset, &CR_LPRIV(cr));
1406 priv_intersect(zone->zone_privset, &CR_EPRIV(cr));
1407 priv_intersect(zone->zone_privset, &CR_IPRIV(cr));
1408 priv_intersect(zone->zone_privset, &CR_PPRIV(cr));
1409 }
1410
1411 struct credklpd *
1412 crgetcrklpd(const cred_t *cr)
1413 {
1414 return (cr->cr_klpd);
1415 }
1416
1417 void
1418 crsetcrklpd(cred_t *cr, struct credklpd *crklpd)
1419 {
1420 ASSERT(cr->cr_ref <= 2);
1421
1422 if (cr->cr_klpd != NULL)
1423 crklpd_rele(cr->cr_klpd);
1424 cr->cr_klpd = crklpd;
1425 }
1426
1427 credgrp_t *
1428 crgrpcopyin(int n, gid_t *gidset)
1429 {
1430 credgrp_t *mem;
1431 size_t sz = CREDGRPSZ(n);
1432
1433 ASSERT(n > 0);
1434
1435 mem = kmem_alloc(sz, KM_SLEEP);
1436
1437 if (copyin(gidset, mem->crg_groups, sizeof (gid_t) * n)) {
1438 kmem_free(mem, sz);
1439 return (NULL);
1440 }
1441 mem->crg_ref = 1;
1442 mem->crg_ngroups = n;
1443 return (mem);
1444 }
1445
1446 const gid_t *
1447 crgetggroups(const credgrp_t *grps)
1448 {
1449 return (grps->crg_groups);
1450 }
1451
1452 void
1453 crsetcredgrp(cred_t *cr, credgrp_t *grps)
1454 {
1455 ASSERT(cr->cr_ref <= 2);
1456
1457 if (cr->cr_grps != NULL)
1458 crgrprele(cr->cr_grps);
1459
1460 cr->cr_grps = grps;
1461 }
1462
1463 void
1464 crgrprele(credgrp_t *grps)
1465 {
1466 if (atomic_add_32_nv(&grps->crg_ref, -1) == 0)
1467 kmem_free(grps, CREDGRPSZ(grps->crg_ngroups));
1468 }
1469
1470 static void
1471 crgrphold(credgrp_t *grps)
1472 {
1473 atomic_add_32(&grps->crg_ref, 1);
1474 }