Home | History | Annotate | Download | only in os
      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 /*
     23  * Copyright 2008 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 #include <sys/atomic.h>
     30 #include <sys/door.h>
     31 #include <sys/proc.h>
     32 #include <sys/cred_impl.h>
     33 #include <sys/policy.h>
     34 #include <sys/priv.h>
     35 #include <sys/klpd.h>
     36 #include <sys/errno.h>
     37 #include <sys/kmem.h>
     38 #include <sys/project.h>
     39 #include <sys/systm.h>
     40 #include <sys/sysmacros.h>
     41 #include <sys/pathname.h>
     42 #include <sys/varargs.h>
     43 #include <sys/zone.h>
     44 #include <netinet/in.h>
     45 
     46 #define	ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
     47 
     48 static kmutex_t klpd_mutex;
     49 
     50 typedef struct klpd_reg {
     51 	struct klpd_reg *klpd_next;
     52 	struct klpd_reg **klpd_refp;
     53 	door_handle_t 	klpd_door;
     54 	pid_t		klpd_door_pid;
     55 	priv_set_t	klpd_pset;
     56 	cred_t		*klpd_cred;
     57 	int		klpd_indel;		/* Disabled */
     58 	uint32_t	klpd_ref;
     59 } klpd_reg_t;
     60 
     61 
     62 /*
     63  * This data structure hangs off the credential of a process; the
     64  * credential is finalized and cannot be changed; but this structure
     65  * can be changed when a new door server for the particular group
     66  * needs to be registered.  It is refcounted and shared between
     67  * processes with common ancestry.
     68  *
     69  * The reference count is atomically updated.
     70  *
     71  * But the registration probably needs to be updated under a lock.
     72  */
     73 typedef struct credklpd {
     74 	kmutex_t	crkl_lock;
     75 	klpd_reg_t	*crkl_reg;
     76 	uint32_t	crkl_ref;
     77 } credklpd_t;
     78 
     79 klpd_reg_t *klpd_list;
     80 
     81 static void klpd_unlink(klpd_reg_t *);
     82 static int klpd_unreg_dh(door_handle_t);
     83 
     84 static credklpd_t *crklpd_alloc(void);
     85 
     86 void crklpd_setreg(credklpd_t *, klpd_reg_t *);
     87 
     88 extern size_t max_vnode_path;
     89 
     90 void
     91 klpd_rele(klpd_reg_t *p)
     92 {
     93 	if (atomic_add_32_nv(&p->klpd_ref, -1) == 0) {
     94 		if (p->klpd_refp != NULL)
     95 			klpd_unlink(p);
     96 		if (p->klpd_cred != NULL)
     97 			crfree(p->klpd_cred);
     98 		door_ki_rele(p->klpd_door);
     99 		kmem_free(p, sizeof (*p));
    100 	}
    101 }
    102 
    103 /*
    104  * In order to be able to walk the lists, we can't unlink the entry
    105  * until the reference count drops to 0.  If we remove it too soon,
    106  * list walkers will terminate when they happen to call a now orphaned
    107  * entry.
    108  */
    109 static klpd_reg_t *
    110 klpd_rele_next(klpd_reg_t *p)
    111 {
    112 	klpd_reg_t *r = p->klpd_next;
    113 
    114 	klpd_rele(p);
    115 	return (r);
    116 }
    117 
    118 
    119 static void
    120 klpd_hold(klpd_reg_t *p)
    121 {
    122 	atomic_add_32(&p->klpd_ref, 1);
    123 }
    124 
    125 /*
    126  * Remove registration from where it is registered.  Returns next in list.
    127  */
    128 static void
    129 klpd_unlink(klpd_reg_t *p)
    130 {
    131 	ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p);
    132 
    133 	if (p->klpd_refp != NULL)
    134 		*p->klpd_refp = p->klpd_next;
    135 
    136 	if (p->klpd_next != NULL)
    137 		p->klpd_next->klpd_refp = p->klpd_refp;
    138 	p->klpd_refp = NULL;
    139 }
    140 
    141 /*
    142  * Remove the head of the klpd list and decrement its refcnt.
    143  * The lock guarding the list should be held; this function is
    144  * called when we are sure we want to remove the entry from the
    145  * list but not so sure that the reference count has dropped back to
    146  * 1 and is specifically intended to remove the non-list variants.
    147  */
    148 void
    149 klpd_remove(klpd_reg_t **pp)
    150 {
    151 	klpd_reg_t *p = *pp;
    152 	if (p == NULL)
    153 		return;
    154 	ASSERT(p->klpd_next == NULL);
    155 	klpd_unlink(p);
    156 	klpd_rele(p);
    157 }
    158 
    159 /*
    160  * Link new entry in list.  The Boolean argument specifies whether this
    161  * list can contain only a single item or multiple items.
    162  * Returns the entry which needs to be released if single is B_TRUE.
    163  */
    164 static klpd_reg_t *
    165 klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single)
    166 {
    167 	klpd_reg_t *old = *listp;
    168 
    169 	ASSERT(p->klpd_ref == 1);
    170 
    171 	ASSERT(old == NULL || *old->klpd_refp == old);
    172 	p->klpd_refp = listp;
    173 	p->klpd_next = single ? NULL : old;
    174 	*listp = p;
    175 	if (old != NULL) {
    176 		if (single) {
    177 			ASSERT(old->klpd_next == NULL);
    178 			old->klpd_refp = NULL;
    179 			return (old);
    180 		} else
    181 			old->klpd_refp = &p->klpd_next;
    182 	}
    183 	return (NULL);
    184 }
    185 
    186 /*
    187  * The typical call consists of:
    188  *	- priv_set_t
    189  *	- some integer data (type, value)
    190  * for now, it's just one bit.
    191  */
    192 static klpd_head_t *
    193 klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap)
    194 {
    195 	char	*comp;
    196 	uint_t	type;
    197 	vnode_t *vp;
    198 	size_t	len = sizeof (priv_set_t) + sizeof (klpd_head_t);
    199 	size_t	plen, clen;
    200 	int	proto;
    201 
    202 	klpd_arg_t *kap = NULL;
    203 	klpd_head_t *khp;
    204 
    205 	type = va_arg(ap, uint_t);
    206 	switch (type) {
    207 	case KLPDARG_NOMORE:
    208 		khp = kmem_zalloc(len, KM_SLEEP);
    209 		khp->klh_argoff = 0;
    210 		break;
    211 	case KLPDARG_VNODE:
    212 		len += offsetof(klpd_arg_t, kla_str);
    213 		vp = va_arg(ap, vnode_t *);
    214 		if (vp == NULL)
    215 			return (NULL);
    216 
    217 		comp = va_arg(ap, char *);
    218 
    219 		if (comp != NULL && *comp != '\0')
    220 			clen = strlen(comp) + 1;
    221 		else
    222 			clen = 0;
    223 
    224 		len += ROUNDUP(MAXPATHLEN, sizeof (uint_t));
    225 		khp = kmem_zalloc(len, KM_SLEEP);
    226 
    227 		khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
    228 		kap = KLH_ARG(khp);
    229 
    230 		if (vnodetopath(crgetzone(p->klpd_cred)->zone_rootvp,
    231 		    vp, kap->kla_str, MAXPATHLEN, p->klpd_cred) != 0) {
    232 			kmem_free(khp, len);
    233 			return (NULL);
    234 		}
    235 		if (clen != 0) {
    236 			plen = strlen(kap->kla_str);
    237 			if (plen + clen + 1 >= MAXPATHLEN) {
    238 				kmem_free(khp, len);
    239 				return (NULL);
    240 			}
    241 			/* Don't make root into a double "/" */
    242 			if (plen <= 2)
    243 				plen = 0;
    244 			kap->kla_str[plen] = '/';
    245 			bcopy(comp, &kap->kla_str[plen + 1], clen);
    246 		}
    247 		break;
    248 	case KLPDARG_PORT:
    249 		proto = va_arg(ap, int);
    250 		switch (proto) {
    251 		case IPPROTO_TCP:	type = KLPDARG_TCPPORT;
    252 					break;
    253 		case IPPROTO_UDP:	type = KLPDARG_UDPPORT;
    254 					break;
    255 		case IPPROTO_SCTP:	type = KLPDARG_SCTPPORT;
    256 					break;
    257 		case PROTO_SDP:		type = KLPDARG_SDPPORT;
    258 					break;
    259 		}
    260 		/* FALLTHROUGH */
    261 	case KLPDARG_INT:
    262 	case KLPDARG_TCPPORT:
    263 	case KLPDARG_UDPPORT:
    264 	case KLPDARG_SCTPPORT:
    265 	case KLPDARG_SDPPORT:
    266 		len += sizeof (*kap);
    267 		khp = kmem_zalloc(len, KM_SLEEP);
    268 		khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
    269 		kap = KLH_ARG(khp);
    270 		kap->kla_int = va_arg(ap, int);
    271 		break;
    272 	default:
    273 		return (NULL);
    274 	}
    275 	khp->klh_vers = KLPDCALL_VERS;
    276 	khp->klh_len = len;
    277 	khp->klh_privoff = sizeof (*khp);
    278 	*KLH_PRIVSET(khp) = *rq;
    279 	if (kap != NULL) {
    280 		kap->kla_type = type;
    281 		kap->kla_dlen = len - khp->klh_argoff;
    282 	}
    283 	return (khp);
    284 }
    285 
    286 static int
    287 klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap)
    288 {
    289 	door_arg_t da;
    290 	int res;
    291 	int dres;
    292 	klpd_head_t *klh;
    293 
    294 	if (p->klpd_door_pid == curproc->p_pid)
    295 		return (-1);
    296 
    297 	klh = klpd_marshall(p, req, ap);
    298 
    299 	if (klh == NULL)
    300 		return (-1);
    301 
    302 	da.data_ptr = (char *)klh;
    303 	da.data_size = klh->klh_len;
    304 	da.desc_ptr = NULL;
    305 	da.desc_num = 0;
    306 	da.rbuf = (char *)&res;
    307 	da.rsize = sizeof (res);
    308 
    309 	while ((dres = door_ki_upcall_limited(p->klpd_door, &da, NULL,
    310 	    SIZE_MAX, 0)) != 0) {
    311 		switch (dres) {
    312 		case EAGAIN:
    313 			delay(1);
    314 			continue;
    315 		case EINVAL:
    316 		case EBADF:
    317 			/* Bad door, don't call it again. */
    318 			(void) klpd_unreg_dh(p->klpd_door);
    319 			/* FALLTHROUGH */
    320 		case EINTR:
    321 			/* Pending signal, nothing we can do. */
    322 			/* FALLTHROUGH */
    323 		default:
    324 			kmem_free(klh, klh->klh_len);
    325 			return (-1);
    326 		}
    327 	}
    328 	kmem_free(klh, klh->klh_len);
    329 	/* Bogus return value, must be a failure */
    330 	if (da.rbuf != (char *)&res) {
    331 		kmem_free(da.rbuf, da.rsize);
    332 		return (-1);
    333 	}
    334 	return (res);
    335 }
    336 
    337 uint32_t klpd_bad_locks;
    338 
    339 int
    340 klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap)
    341 {
    342 	klpd_reg_t *p;
    343 	int rv = -1;
    344 	credklpd_t *ckp;
    345 	zone_t *ckzone;
    346 
    347 	/*
    348 	 * These locks must not be held when this code is called;
    349 	 * callbacks to userland with these locks held will result
    350 	 * in issues.  That said, the code at the call sides was
    351 	 * restructured not to call with any of the locks held and
    352 	 * no policies operate by default on most processes.
    353 	 */
    354 	if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) ||
    355 	    mutex_owned(&curproc->p_crlock)) {
    356 		atomic_add_32(&klpd_bad_locks, 1);
    357 		return (-1);
    358 	}
    359 
    360 	/*
    361 	 * Enforce the limit set for the call process (still).
    362 	 */
    363 	if (!priv_issubset(req, &CR_LPRIV(cr)))
    364 		return (-1);
    365 
    366 	/* Try 1: get the credential specific klpd */
    367 	if ((ckp = crgetcrklpd(cr)) != NULL) {
    368 		mutex_enter(&ckp->crkl_lock);
    369 		if ((p = ckp->crkl_reg) != NULL &&
    370 		    p->klpd_indel == 0 &&
    371 		    priv_issubset(req, &p->klpd_pset)) {
    372 			klpd_hold(p);
    373 			mutex_exit(&ckp->crkl_lock);
    374 			rv = klpd_do_call(p, req, ap);
    375 			mutex_enter(&ckp->crkl_lock);
    376 			klpd_rele(p);
    377 			mutex_exit(&ckp->crkl_lock);
    378 			if (rv != -1)
    379 				return (rv == 0 ? 0 : -1);
    380 		} else {
    381 			mutex_exit(&ckp->crkl_lock);
    382 		}
    383 	}
    384 
    385 	/* Try 2: get the project specific klpd */
    386 	mutex_enter(&klpd_mutex);
    387 
    388 	if ((p = curproj->kpj_klpd) != NULL) {
    389 		klpd_hold(p);
    390 		mutex_exit(&klpd_mutex);
    391 		if (p->klpd_indel == 0 &&
    392 		    priv_issubset(req, &p->klpd_pset)) {
    393 			rv = klpd_do_call(p, req, ap);
    394 		}
    395 		mutex_enter(&klpd_mutex);
    396 		klpd_rele(p);
    397 		mutex_exit(&klpd_mutex);
    398 
    399 		if (rv != -1)
    400 			return (rv == 0 ? 0 : -1);
    401 	} else {
    402 		mutex_exit(&klpd_mutex);
    403 	}
    404 
    405 	/* Try 3: get the global klpd list */
    406 	ckzone = crgetzone(cr);
    407 	mutex_enter(&klpd_mutex);
    408 
    409 	for (p = klpd_list; p != NULL; ) {
    410 		zone_t *kkzone = crgetzone(p->klpd_cred);
    411 		if ((kkzone == &zone0 || kkzone == ckzone) &&
    412 		    p->klpd_indel == 0 &&
    413 		    priv_issubset(req, &p->klpd_pset)) {
    414 			klpd_hold(p);
    415 			mutex_exit(&klpd_mutex);
    416 			rv = klpd_do_call(p, req, ap);
    417 			mutex_enter(&klpd_mutex);
    418 
    419 			p = klpd_rele_next(p);
    420 
    421 			if (rv != -1)
    422 				break;
    423 		} else {
    424 			p = p->klpd_next;
    425 		}
    426 	}
    427 	mutex_exit(&klpd_mutex);
    428 	return (rv == 0 ? 0 : -1);
    429 }
    430 
    431 /*
    432  * Register the klpd.
    433  * If the pid_t passed in is positive, update the registration for
    434  * the specific process; that is only possible if the process already
    435  * has a registration on it.  This change of registration will affect
    436  * all processes which share common ancestry.
    437  *
    438  * MY_PID (pid 0) can be used to create or change the context for
    439  * the current process, typically done after fork().
    440  *
    441  * A negative value can be used to register a klpd globally.
    442  *
    443  * The per-credential klpd needs to be cleaned up when entering
    444  * a zone or unsetting the flag.
    445  */
    446 int
    447 klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf)
    448 {
    449 	cred_t *cr = CRED();
    450 	door_handle_t dh;
    451 	klpd_reg_t *kpd;
    452 	priv_set_t pset;
    453 	door_info_t di;
    454 	credklpd_t *ckp = NULL;
    455 	pid_t pid = -1;
    456 	projid_t proj = -1;
    457 	kproject_t *kpp = NULL;
    458 
    459 	if (CR_FLAGS(cr) & PRIV_XPOLICY)
    460 		return (set_errno(EINVAL));
    461 
    462 	if (copyin(psetbuf, &pset, sizeof (priv_set_t)))
    463 		return (set_errno(EFAULT));
    464 
    465 	if (!priv_issubset(&pset, &CR_OEPRIV(cr)))
    466 		return (set_errno(EPERM));
    467 
    468 	switch (type) {
    469 	case P_PID:
    470 		pid = (pid_t)id;
    471 		if (pid == P_MYPID)
    472 			pid = curproc->p_pid;
    473 		if (pid == curproc->p_pid)
    474 			ckp = crklpd_alloc();
    475 		break;
    476 	case P_PROJID:
    477 		proj = (projid_t)id;
    478 		kpp = project_hold_by_id(proj, crgetzone(cr),
    479 		    PROJECT_HOLD_FIND);
    480 		if (kpp == NULL)
    481 			return (set_errno(ESRCH));
    482 		break;
    483 	default:
    484 		return (set_errno(ENOTSUP));
    485 	}
    486 
    487 
    488 	/*
    489 	 * Verify the door passed in; it must be a door and we won't
    490 	 * allow processes to be called on their own behalf.
    491 	 */
    492 	dh = door_ki_lookup(did);
    493 	if (dh == NULL || door_ki_info(dh, &di) != 0) {
    494 		if (ckp != NULL)
    495 			crklpd_rele(ckp);
    496 		if (kpp != NULL)
    497 			project_rele(kpp);
    498 		return (set_errno(EBADF));
    499 	}
    500 	if (type == P_PID && pid == di.di_target) {
    501 		if (ckp != NULL)
    502 			crklpd_rele(ckp);
    503 		ASSERT(kpp == NULL);
    504 		return (set_errno(EINVAL));
    505 	}
    506 
    507 	kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP);
    508 	crhold(kpd->klpd_cred = cr);
    509 	kpd->klpd_door = dh;
    510 	kpd->klpd_door_pid = di.di_target;
    511 	kpd->klpd_ref = 1;
    512 	kpd->klpd_pset = pset;
    513 
    514 	if (kpp != NULL) {
    515 		mutex_enter(&klpd_mutex);
    516 		kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE);
    517 		mutex_exit(&klpd_mutex);
    518 		if (kpd != NULL)
    519 			klpd_rele(kpd);
    520 		project_rele(kpp);
    521 	} else if ((int)pid < 0) {
    522 		/* Global daemon */
    523 		mutex_enter(&klpd_mutex);
    524 		(void) klpd_link(kpd, &klpd_list, B_FALSE);
    525 		mutex_exit(&klpd_mutex);
    526 	} else if (pid == curproc->p_pid) {
    527 		proc_t *p = curproc;
    528 		cred_t *newcr = cralloc();
    529 
    530 		/* No need to lock, sole reference to ckp */
    531 		kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE);
    532 
    533 		if (kpd != NULL)
    534 			klpd_rele(kpd);
    535 
    536 		mutex_enter(&p->p_crlock);
    537 		cr = p->p_cred;
    538 		crdup_to(cr, newcr);
    539 		crsetcrklpd(newcr, ckp);
    540 		p->p_cred = newcr;	/* Already held for p_cred */
    541 
    542 		crhold(newcr);		/* Hold once for the current thread */
    543 		mutex_exit(&p->p_crlock);
    544 		crfree(cr);		/* One for the p_cred */
    545 		crset(p, newcr);
    546 	} else {
    547 		proc_t *p;
    548 		cred_t *pcr;
    549 		mutex_enter(&pidlock);
    550 		p = prfind(pid);
    551 		if (p == NULL || !prochasprocperm(p, curproc, CRED())) {
    552 			mutex_exit(&pidlock);
    553 			klpd_rele(kpd);
    554 			return (set_errno(p == NULL ? ESRCH : EPERM));
    555 		}
    556 		mutex_enter(&p->p_crlock);
    557 		crhold(pcr = p->p_cred);
    558 		mutex_exit(&pidlock);
    559 		mutex_exit(&p->p_crlock);
    560 		/*
    561 		 * We're going to update the credential's ckp in place;
    562 		 * this requires that it exists.
    563 		 */
    564 		ckp = crgetcrklpd(pcr);
    565 		if (ckp == NULL) {
    566 			crfree(pcr);
    567 			klpd_rele(kpd);
    568 			return (set_errno(EINVAL));
    569 		}
    570 		crklpd_setreg(ckp, kpd);
    571 		crfree(pcr);
    572 	}
    573 
    574 	return (0);
    575 }
    576 
    577 static int
    578 klpd_unreg_dh(door_handle_t dh)
    579 {
    580 	klpd_reg_t *p;
    581 
    582 	mutex_enter(&klpd_mutex);
    583 	for (p = klpd_list; p != NULL; p = p->klpd_next) {
    584 		if (p->klpd_door == dh)
    585 			break;
    586 	}
    587 	if (p == NULL) {
    588 		mutex_exit(&klpd_mutex);
    589 		return (EINVAL);
    590 	}
    591 	if (p->klpd_indel != 0) {
    592 		mutex_exit(&klpd_mutex);
    593 		return (EAGAIN);
    594 	}
    595 	p->klpd_indel = 1;
    596 	klpd_rele(p);
    597 	mutex_exit(&klpd_mutex);
    598 	return (0);
    599 }
    600 
    601 int
    602 klpd_unreg(int did, idtype_t type, id_t id)
    603 {
    604 	door_handle_t dh;
    605 	int res = 0;
    606 	proc_t *p;
    607 	pid_t pid;
    608 	projid_t proj;
    609 	kproject_t *kpp = NULL;
    610 	credklpd_t *ckp;
    611 
    612 	switch (type) {
    613 	case P_PID:
    614 		pid = (pid_t)id;
    615 		break;
    616 	case P_PROJID:
    617 		proj = (projid_t)id;
    618 		kpp = project_hold_by_id(proj, crgetzone(CRED()),
    619 		    PROJECT_HOLD_FIND);
    620 		if (kpp == NULL)
    621 			return (set_errno(ESRCH));
    622 		break;
    623 	default:
    624 		return (set_errno(ENOTSUP));
    625 	}
    626 
    627 	dh = door_ki_lookup(did);
    628 	if (dh == NULL) {
    629 		if (kpp != NULL)
    630 			project_rele(kpp);
    631 		return (set_errno(EINVAL));
    632 	}
    633 
    634 	if (kpp != NULL) {
    635 		mutex_enter(&klpd_mutex);
    636 		if (kpp->kpj_klpd == NULL)
    637 			res = ESRCH;
    638 		else
    639 			klpd_remove(&kpp->kpj_klpd);
    640 		mutex_exit(&klpd_mutex);
    641 		project_rele(kpp);
    642 		goto out;
    643 	} else if ((int)pid > 0) {
    644 		mutex_enter(&pidlock);
    645 		p = prfind(pid);
    646 		if (p == NULL) {
    647 			mutex_exit(&pidlock);
    648 			door_ki_rele(dh);
    649 			return (set_errno(ESRCH));
    650 		}
    651 		mutex_enter(&p->p_crlock);
    652 		mutex_exit(&pidlock);
    653 	} else if (pid == 0) {
    654 		p = curproc;
    655 		mutex_enter(&p->p_crlock);
    656 	} else {
    657 		res = klpd_unreg_dh(dh);
    658 		goto out;
    659 	}
    660 
    661 	ckp = crgetcrklpd(p->p_cred);
    662 	if (ckp != NULL) {
    663 		crklpd_setreg(ckp, NULL);
    664 	} else {
    665 		res = ESRCH;
    666 	}
    667 	mutex_exit(&p->p_crlock);
    668 
    669 out:
    670 	door_ki_rele(dh);
    671 
    672 	if (res != 0)
    673 		return (set_errno(res));
    674 	return (0);
    675 }
    676 
    677 void
    678 crklpd_hold(credklpd_t *crkpd)
    679 {
    680 	atomic_add_32(&crkpd->crkl_ref, 1);
    681 }
    682 
    683 void
    684 crklpd_rele(credklpd_t *crkpd)
    685 {
    686 	if (atomic_add_32_nv(&crkpd->crkl_ref, -1) == 0) {
    687 		if (crkpd->crkl_reg != NULL)
    688 			klpd_rele(crkpd->crkl_reg);
    689 		mutex_destroy(&crkpd->crkl_lock);
    690 		kmem_free(crkpd, sizeof (*crkpd));
    691 	}
    692 }
    693 
    694 static credklpd_t *
    695 crklpd_alloc(void)
    696 {
    697 	credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP);
    698 
    699 	mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL);
    700 	res->crkl_ref = 1;
    701 	res->crkl_reg = NULL;
    702 
    703 	return (res);
    704 }
    705 
    706 void
    707 crklpd_setreg(credklpd_t *crk, klpd_reg_t *new)
    708 {
    709 	klpd_reg_t *old;
    710 
    711 	mutex_enter(&crk->crkl_lock);
    712 	if (new == NULL) {
    713 		old = crk->crkl_reg;
    714 		if (old != NULL)
    715 			klpd_unlink(old);
    716 	} else {
    717 		old = klpd_link(new, &crk->crkl_reg, B_TRUE);
    718 	}
    719 	mutex_exit(&crk->crkl_lock);
    720 
    721 	if (old != NULL)
    722 		klpd_rele(old);
    723 }
    724