xref: /illumos-gate/usr/src/uts/common/rpc/sec/sec_clnt.c (revision a38ddfee9c8c6b6c5a2947ff52fd2338362a4444)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/param.h>
29 #include <sys/types.h>
30 #include <sys/systm.h>
31 #include <sys/cred.h>
32 #include <sys/proc.h>
33 #include <sys/user.h>
34 #include <sys/time.h>
35 #include <sys/buf.h>
36 #include <sys/vfs.h>
37 #include <sys/vnode.h>
38 #include <sys/socket.h>
39 #include <sys/stat.h>
40 #include <sys/uio.h>
41 #include <sys/tiuser.h>
42 #include <sys/swap.h>
43 #include <sys/errno.h>
44 #include <sys/debug.h>
45 #include <sys/kmem.h>
46 #include <sys/kstat.h>
47 #include <sys/cmn_err.h>
48 #include <sys/vtrace.h>
49 #include <sys/session.h>
50 #include <sys/dnlc.h>
51 #include <sys/bitmap.h>
52 #include <sys/thread.h>
53 #include <sys/policy.h>
54 
55 #include <netinet/in.h>
56 #include <rpc/types.h>
57 #include <rpc/xdr.h>
58 #include <rpc/auth.h>
59 #include <rpc/auth_des.h>	/* for authdes_create() */
60 #include <rpc/clnt.h>
61 #include <rpc/rpcsec_gss.h>
62 
63 #define	MAXCLIENTS	16
64 static int clnt_authdes_cachesz = 64;
65 
66 static uint_t authdes_win = 5*60;  /* 5 minutes -- should be mount option */
67 
68 struct kmem_cache *authkern_cache;
69 
70 struct kmem_cache *authloopback_cache;
71 
72 static struct desauthent {
73 	struct	sec_data *da_data;
74 	uid_t da_uid;
75 	zoneid_t da_zoneid;
76 	short da_inuse;
77 	AUTH *da_auth;
78 } *desauthtab;
79 static int nextdesvictim;
80 static kmutex_t desauthtab_lock;	/* Lock to protect DES auth cache */
81 
82 /* RPC stuff */
83 kmutex_t authdes_ops_lock;   /* auth_ops initialization in authdes_ops() */
84 
85 static void  purge_authtab(struct sec_data *);
86 
87 /* Zone stuff */
88 zone_key_t auth_zone_key;
89 
90 /*
91  *  Load RPCSEC_GSS specific data from user space to kernel space.
92  */
93 /*ARGSUSED*/
94 static int
95 gss_clnt_loadinfo(caddr_t usrdata, caddr_t *kdata, model_t model)
96 {
97 	struct gss_clnt_data *data;
98 	caddr_t	elements;
99 	int error = 0;
100 
101 	/* map opaque data to gss specific structure */
102 	data = kmem_alloc(sizeof (*data), KM_SLEEP);
103 
104 #ifdef _SYSCALL32_IMPL
105 	if (model != DATAMODEL_NATIVE) {
106 		struct gss_clnt_data32 gd32;
107 
108 		if (copyin(usrdata, &gd32, sizeof (gd32)) == -1) {
109 			error = EFAULT;
110 		} else {
111 			data->mechanism.length = gd32.mechanism.length;
112 			data->mechanism.elements =
113 			    (caddr_t)(uintptr_t)gd32.mechanism.elements;
114 			data->service = gd32.service;
115 			bcopy(gd32.uname, data->uname, sizeof (gd32.uname));
116 			bcopy(gd32.inst, data->inst, sizeof (gd32.inst));
117 			bcopy(gd32.realm, data->realm, sizeof (gd32.realm));
118 			data->qop = gd32.qop;
119 		}
120 	} else
121 #endif /* _SYSCALL32_IMPL */
122 	if (copyin(usrdata, data, sizeof (*data)))
123 		error = EFAULT;
124 
125 	if (error == 0) {
126 		if (data->mechanism.length > 0) {
127 			elements = kmem_alloc(data->mechanism.length, KM_SLEEP);
128 			if (!(copyin(data->mechanism.elements, elements,
129 			    data->mechanism.length))) {
130 				data->mechanism.elements = elements;
131 				*kdata = (caddr_t)data;
132 				return (0);
133 			} else
134 				kmem_free(elements, data->mechanism.length);
135 		}
136 	} else {
137 		*kdata = NULL;
138 		kmem_free(data, sizeof (*data));
139 	}
140 	return (EFAULT);
141 }
142 
143 
144 /*
145  *  Load AUTH_DES specific data from user space to kernel space.
146  */
147 /*ARGSUSED2*/
148 int
149 dh_k4_clnt_loadinfo(caddr_t usrdata, caddr_t *kdata, model_t model)
150 {
151 	size_t nlen;
152 	int error = 0;
153 	char *userbufptr;
154 	dh_k4_clntdata_t *data;
155 	char netname[MAXNETNAMELEN+1];
156 	struct netbuf *syncaddr;
157 	struct knetconfig *knconf;
158 
159 	/* map opaque data to des specific strucutre */
160 	data = kmem_alloc(sizeof (*data), KM_SLEEP);
161 
162 #ifdef _SYSCALL32_IMPL
163 	if (model != DATAMODEL_NATIVE) {
164 		struct des_clnt_data32 data32;
165 
166 		if (copyin(usrdata, &data32, sizeof (data32)) == -1) {
167 			error = EFAULT;
168 		} else {
169 			data->syncaddr.maxlen = data32.syncaddr.maxlen;
170 			data->syncaddr.len = data32.syncaddr.len;
171 			data->syncaddr.buf =
172 			    (caddr_t)(uintptr_t)data32.syncaddr.buf;
173 			data->knconf =
174 			    (struct knetconfig *)(uintptr_t)data32.knconf;
175 			data->netname = (caddr_t)(uintptr_t)data32.netname;
176 			data->netnamelen = data32.netnamelen;
177 		}
178 	} else
179 #endif /* _SYSCALL32_IMPL */
180 	if (copyin(usrdata, data, sizeof (*data)))
181 		error = EFAULT;
182 
183 	if (error == 0) {
184 		syncaddr = &data->syncaddr;
185 		if (syncaddr == NULL)
186 			error = EINVAL;
187 		else {
188 			userbufptr = syncaddr->buf;
189 			syncaddr->buf =  kmem_alloc(syncaddr->len, KM_SLEEP);
190 			syncaddr->maxlen = syncaddr->len;
191 			if (copyin(userbufptr, syncaddr->buf, syncaddr->len)) {
192 				kmem_free(syncaddr->buf, syncaddr->len);
193 				syncaddr->buf = NULL;
194 				error = EFAULT;
195 			} else {
196 				(void) copyinstr(data->netname, netname,
197 				    sizeof (netname), &nlen);
198 				if (nlen != 0) {
199 					data->netname =
200 					    kmem_alloc(nlen, KM_SLEEP);
201 					bcopy(netname, data->netname, nlen);
202 					data->netnamelen = (int)nlen;
203 				}
204 			}
205 		}
206 	}
207 
208 	if (!error) {
209 		/*
210 		 * Allocate space for a knetconfig structure and
211 		 * its strings and copy in from user-land.
212 		 */
213 		knconf = kmem_alloc(sizeof (*knconf), KM_SLEEP);
214 #ifdef _SYSCALL32_IMPL
215 		if (model != DATAMODEL_NATIVE) {
216 			struct knetconfig32 knconf32;
217 
218 			if (copyin(data->knconf, &knconf32,
219 			    sizeof (knconf32)) == -1) {
220 				kmem_free(knconf, sizeof (*knconf));
221 				kmem_free(syncaddr->buf, syncaddr->len);
222 				syncaddr->buf = NULL;
223 				kmem_free(data->netname, nlen);
224 				error = EFAULT;
225 			} else {
226 				knconf->knc_semantics = knconf32.knc_semantics;
227 				knconf->knc_protofmly =
228 				    (caddr_t)(uintptr_t)knconf32.knc_protofmly;
229 				knconf->knc_proto =
230 				    (caddr_t)(uintptr_t)knconf32.knc_proto;
231 				knconf->knc_rdev = expldev(knconf32.knc_rdev);
232 			}
233 		} else
234 #endif /* _SYSCALL32_IMPL */
235 		if (copyin(data->knconf, knconf, sizeof (*knconf))) {
236 			kmem_free(knconf, sizeof (*knconf));
237 			kmem_free(syncaddr->buf, syncaddr->len);
238 			syncaddr->buf = NULL;
239 			kmem_free(data->netname, nlen);
240 			error = EFAULT;
241 		}
242 	}
243 
244 	if (!error) {
245 		size_t nmoved_tmp;
246 		char *p, *pf;
247 
248 		pf = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
249 		p = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
250 		error = copyinstr(knconf->knc_protofmly, pf,
251 		    KNC_STRSIZE, &nmoved_tmp);
252 		if (error) {
253 			kmem_free(pf, KNC_STRSIZE);
254 			kmem_free(p, KNC_STRSIZE);
255 			kmem_free(knconf, sizeof (*knconf));
256 			kmem_free(syncaddr->buf, syncaddr->len);
257 			kmem_free(data->netname, nlen);
258 		}
259 
260 		if (!error) {
261 			error = copyinstr(knconf->knc_proto,
262 			    p, KNC_STRSIZE, &nmoved_tmp);
263 			if (error) {
264 				kmem_free(pf, KNC_STRSIZE);
265 				kmem_free(p, KNC_STRSIZE);
266 				kmem_free(knconf, sizeof (*knconf));
267 				kmem_free(syncaddr->buf, syncaddr->len);
268 				kmem_free(data->netname, nlen);
269 			}
270 		}
271 
272 		if (!error) {
273 			knconf->knc_protofmly = pf;
274 			knconf->knc_proto = p;
275 		}
276 	}
277 
278 	if (error) {
279 		*kdata = NULL;
280 		kmem_free(data, sizeof (*data));
281 		return (error);
282 	}
283 
284 	data->knconf = knconf;
285 	*kdata = (caddr_t)data;
286 	return (0);
287 }
288 
289 /*
290  *  Free up AUTH_DES specific data.
291  */
292 void
293 dh_k4_clnt_freeinfo(caddr_t cdata)
294 {
295 	dh_k4_clntdata_t *data;
296 
297 	data = (dh_k4_clntdata_t *)cdata;
298 	if (data->netnamelen > 0) {
299 		kmem_free(data->netname, data->netnamelen);
300 	}
301 	if (data->syncaddr.buf != NULL) {
302 		kmem_free(data->syncaddr.buf, data->syncaddr.len);
303 	}
304 	if (data->knconf != NULL) {
305 		kmem_free(data->knconf->knc_protofmly, KNC_STRSIZE);
306 		kmem_free(data->knconf->knc_proto, KNC_STRSIZE);
307 		kmem_free(data->knconf, sizeof (*data->knconf));
308 	}
309 
310 	kmem_free(data, sizeof (*data));
311 }
312 
313 /*
314  *  Load application auth related data from user land to kernel.
315  *  Map opaque data field to dh_k4_clntdata_t for AUTH_DES
316  *
317  */
318 int
319 sec_clnt_loadinfo(struct sec_data *in, struct sec_data **out, model_t model)
320 {
321 	struct	sec_data	*secdata;
322 	int	error = 0;
323 
324 	secdata = kmem_alloc(sizeof (*secdata), KM_SLEEP);
325 
326 #ifdef _SYSCALL32_IMPL
327 	if (model != DATAMODEL_NATIVE) {
328 		struct sec_data32 sd32;
329 
330 		if (copyin(in, &sd32, sizeof (sd32)) == -1) {
331 			error = EFAULT;
332 		} else {
333 			secdata->secmod = sd32.secmod;
334 			secdata->rpcflavor = sd32.rpcflavor;
335 			secdata->uid = sd32.uid;
336 			secdata->flags = sd32.flags;
337 			secdata->data = (caddr_t)(uintptr_t)sd32.data;
338 		}
339 	} else
340 #endif /* _SYSCALL32_IMPL */
341 
342 	if (copyin(in, secdata, sizeof (*secdata)) == -1) {
343 		error = EFAULT;
344 	}
345 	/*
346 	 * Copy in opaque data field per flavor.
347 	 */
348 	if (!error) {
349 		switch (secdata->rpcflavor) {
350 		case AUTH_NONE:
351 		case AUTH_UNIX:
352 		case AUTH_LOOPBACK:
353 			break;
354 
355 		case AUTH_DES:
356 			error = dh_k4_clnt_loadinfo(secdata->data,
357 			    &secdata->data, model);
358 			break;
359 
360 		case RPCSEC_GSS:
361 			error = gss_clnt_loadinfo(secdata->data,
362 			    &secdata->data, model);
363 			break;
364 
365 		default:
366 			error = EINVAL;
367 			break;
368 		}
369 	}
370 
371 	if (!error) {
372 		*out = secdata;
373 	} else {
374 		kmem_free(secdata, sizeof (*secdata));
375 		*out = (struct sec_data *)NULL;
376 	}
377 
378 	return (error);
379 }
380 
381 /*
382  * Null the sec_data index in the cache table, and
383  * free the memory allocated by sec_clnt_loadinfo.
384  */
385 void
386 sec_clnt_freeinfo(struct sec_data *secdata)
387 {
388 	switch (secdata->rpcflavor) {
389 	case AUTH_DES:
390 		purge_authtab(secdata);
391 		if (secdata->data)
392 			dh_k4_clnt_freeinfo(secdata->data);
393 		break;
394 
395 	case RPCSEC_GSS:
396 		rpc_gss_secpurge((void *)secdata);
397 		if (secdata->data) {
398 			gss_clntdata_t *gss_data;
399 
400 			gss_data = (gss_clntdata_t *)secdata->data;
401 			if (gss_data->mechanism.elements) {
402 				kmem_free(gss_data->mechanism.elements,
403 				    gss_data->mechanism.length);
404 			}
405 			kmem_free(secdata->data, sizeof (gss_clntdata_t));
406 		}
407 		break;
408 
409 	case AUTH_NONE:
410 	case AUTH_UNIX:
411 	case AUTH_LOOPBACK:
412 	default:
413 		break;
414 	}
415 	kmem_free(secdata, sizeof (*secdata));
416 }
417 
418 /*
419  *  Get an AUTH handle for a RPC client based on the given sec_data.
420  *  If an AUTH handle exists for the same sec_data, use that AUTH handle,
421  *  otherwise create a new one.
422  */
423 int
424 sec_clnt_geth(CLIENT *client, struct sec_data *secdata, cred_t *cr, AUTH **ap)
425 {
426 	int i;
427 	struct desauthent *da;
428 	int authflavor;
429 	cred_t *savecred;
430 	int stat;			/* return (errno) status */
431 	char gss_svc_name[MAX_GSS_NAME];
432 	dh_k4_clntdata_t	*desdata;
433 	AUTH *auth;
434 	gss_clntdata_t *gssdata;
435 	zoneid_t zoneid = getzoneid();
436 
437 	if ((client == NULL) || (secdata == NULL) || (ap == NULL))
438 		return (EINVAL);
439 	*ap = (AUTH *)NULL;
440 
441 	authflavor = secdata->rpcflavor;
442 	for (;;) {
443 		int nlen;
444 		char *netname;
445 
446 		switch (authflavor) {
447 		case AUTH_NONE:
448 			/*
449 			 * XXX: should do real AUTH_NONE, instead of AUTH_UNIX
450 			 */
451 		case AUTH_UNIX:
452 			*ap = (AUTH *) authkern_create();
453 			return ((*ap != NULL) ? 0 : EINTR);
454 
455 		case AUTH_LOOPBACK:
456 			*ap = (AUTH *) authloopback_create();
457 			return ((*ap != NULL) ? 0 : EINTR);
458 
459 		case AUTH_DES:
460 			mutex_enter(&desauthtab_lock);
461 			if (desauthtab == NULL) {
462 				desauthtab = kmem_zalloc(clnt_authdes_cachesz *
463 				    sizeof (struct desauthent), KM_SLEEP);
464 			}
465 			for (da = desauthtab;
466 			    da < &desauthtab[clnt_authdes_cachesz];
467 			    da++) {
468 				if (da->da_data == secdata &&
469 				    da->da_uid == crgetuid(cr) &&
470 				    da->da_zoneid == zoneid &&
471 				    !da->da_inuse &&
472 				    da->da_auth != NULL) {
473 					da->da_inuse = 1;
474 					mutex_exit(&desauthtab_lock);
475 					*ap = da->da_auth;
476 					return (0);
477 				}
478 			}
479 			mutex_exit(&desauthtab_lock);
480 
481 			/*
482 			 *  A better way would be to have a cred paramater to
483 			 *  authdes_create.
484 			 */
485 			savecred = curthread->t_cred;
486 			curthread->t_cred = cr;
487 
488 			/*
489 			 * Note that authdes_create() expects a
490 			 * NUL-terminated string for netname, but
491 			 * dh_k4_clntdata_t gives us netname & netnamelen.
492 			 *
493 			 * We must create a string for authdes_create();
494 			 * the latter takes a copy of it, so we may
495 			 * immediately free it.
496 			 */
497 			desdata = (dh_k4_clntdata_t *)secdata->data;
498 			nlen = desdata->netnamelen;
499 			/* must be NUL-terminated */
500 			netname = kmem_zalloc(nlen + 1, KM_SLEEP);
501 			bcopy(desdata->netname, netname, nlen);
502 			stat = authdes_create(netname, authdes_win,
503 			    &desdata->syncaddr, desdata->knconf,
504 			    (des_block *)NULL,
505 			    (secdata->flags & AUTH_F_RPCTIMESYNC) ? 1 : 0,
506 			    &auth);
507 			kmem_free(netname, nlen + 1);
508 
509 			curthread->t_cred = savecred;
510 			*ap = auth;
511 
512 			if (stat != 0) {
513 				/*
514 				 *  If AUTH_F_TRYNONE is on, try again
515 				 *  with AUTH_NONE.  See bug 1180236.
516 				 */
517 				if (secdata->flags & AUTH_F_TRYNONE) {
518 					authflavor = AUTH_NONE;
519 					continue;
520 				} else
521 					return (stat);
522 			}
523 
524 			i = clnt_authdes_cachesz;
525 			mutex_enter(&desauthtab_lock);
526 			do {
527 				da = &desauthtab[nextdesvictim++];
528 				nextdesvictim %= clnt_authdes_cachesz;
529 			} while (da->da_inuse && --i > 0);
530 
531 			if (da->da_inuse) {
532 				mutex_exit(&desauthtab_lock);
533 				/* overflow of des auths */
534 				return (stat);
535 			}
536 			da->da_inuse = 1;
537 			mutex_exit(&desauthtab_lock);
538 
539 			if (da->da_auth != NULL)
540 				auth_destroy(da->da_auth);
541 
542 			da->da_auth = auth;
543 			da->da_uid = crgetuid(cr);
544 			da->da_zoneid = zoneid;
545 			da->da_data = secdata;
546 			return (stat);
547 
548 		case RPCSEC_GSS:
549 			/*
550 			 *  For RPCSEC_GSS, cache is done in rpc_gss_secget().
551 			 *  For every rpc_gss_secget(),  it should have
552 			 *  a corresponding rpc_gss_secfree() call.
553 			 */
554 			gssdata = (gss_clntdata_t *)secdata->data;
555 			(void) sprintf(gss_svc_name, "%s@%s", gssdata->uname,
556 			    gssdata->inst);
557 
558 			stat = rpc_gss_secget(client, gss_svc_name,
559 			    &gssdata->mechanism,
560 			    gssdata->service,
561 			    gssdata->qop,
562 			    NULL, NULL,
563 			    (caddr_t)secdata, cr, &auth);
564 			*ap = auth;
565 
566 			/* success */
567 			if (stat == 0)
568 				return (stat);
569 
570 			/*
571 			 * let the caller retry if connection timedout
572 			 * or reset.
573 			 */
574 			if (stat == ETIMEDOUT || stat == ECONNRESET)
575 				return (stat);
576 
577 			/*
578 			 *  If AUTH_F_TRYNONE is on, try again
579 			 *  with AUTH_NONE.  See bug 1180236.
580 			 */
581 			if (secdata->flags & AUTH_F_TRYNONE) {
582 				authflavor = AUTH_NONE;
583 				continue;
584 			}
585 
586 			RPCLOG(1, "sec_clnt_geth: rpc_gss_secget"
587 			    " failed with %d", stat);
588 			return (stat);
589 
590 		default:
591 			/*
592 			 * auth create must have failed, try AUTH_NONE
593 			 * (this relies on AUTH_NONE never failing)
594 			 */
595 			cmn_err(CE_NOTE, "sec_clnt_geth: unknown "
596 			    "authflavor %d, trying AUTH_NONE", authflavor);
597 			authflavor = AUTH_NONE;
598 		}
599 	}
600 }
601 
602 void
603 sec_clnt_freeh(AUTH *auth)
604 {
605 	struct desauthent *da;
606 
607 	switch (auth->ah_cred.oa_flavor) {
608 	case AUTH_NONE: /* XXX: do real AUTH_NONE */
609 	case AUTH_UNIX:
610 	case AUTH_LOOPBACK:
611 		auth_destroy(auth);	/* was overflow */
612 		break;
613 
614 	case AUTH_DES:
615 		mutex_enter(&desauthtab_lock);
616 		if (desauthtab != NULL) {
617 			for (da = desauthtab;
618 			    da < &desauthtab[clnt_authdes_cachesz]; da++) {
619 				if (da->da_auth == auth) {
620 					da->da_inuse = 0;
621 					mutex_exit(&desauthtab_lock);
622 					return;
623 				}
624 			}
625 		}
626 		mutex_exit(&desauthtab_lock);
627 		auth_destroy(auth);	/* was overflow */
628 		break;
629 
630 	case RPCSEC_GSS:
631 		(void) rpc_gss_secfree(auth);
632 		break;
633 
634 	default:
635 		cmn_err(CE_NOTE, "sec_clnt_freeh: unknown authflavor %d",
636 		    auth->ah_cred.oa_flavor);
637 		break;
638 	}
639 }
640 
641 /*
642  *  Revoke the authentication key in the given AUTH handle by setting
643  *  it to NULL.  If newkey is true, then generate a new key instead of
644  *  nulling out the old one.  This is necessary for AUTH_DES because
645  *  the new key will be used next time the user does a keylogin.  If
646  *  the zero'd key is used as actual key, then it cannot be revoked
647  *  again!
648  */
649 void
650 revoke_key(AUTH *auth, int newkey)
651 {
652 	if (auth == NULL)
653 		return;
654 
655 	if (newkey) {
656 		if (key_gendes(&auth->ah_key) != RPC_SUCCESS) {
657 			/* failed to get new key, munge the old one */
658 			auth->ah_key.key.high ^= auth->ah_key.key.low;
659 			auth->ah_key.key.low  += auth->ah_key.key.high;
660 		}
661 	} else {
662 		/* null out old key */
663 		auth->ah_key.key.high = 0;
664 		auth->ah_key.key.low  = 0;
665 	}
666 }
667 
668 /*
669  *  Revoke all rpc credentials (of the selected auth type) for the given uid
670  *  from the auth cache.  Must be root to do this if the requested uid is not
671  *  the effective uid of the requestor.
672  *
673  *  Called from nfssys() for backward compatibility, and also
674  *  called from krpc_sys().
675  *
676  *  AUTH_DES does not refer to the "mechanism" information.
677  *  RPCSEC_GSS requires the "mechanism" input.
678  *  The input argument, mechanism, is a user-space address and needs
679  *  to be copied into the kernel address space.
680  *
681  *  Returns error number.
682  */
683 /*ARGSUSED*/
684 int
685 sec_clnt_revoke(int rpcflavor, uid_t uid, cred_t *cr, void *mechanism,
686 						model_t model)
687 {
688 	struct desauthent *da;
689 	int error = 0;
690 	zoneid_t zoneid = getzoneid();
691 
692 	if (uid != crgetuid(cr) && secpolicy_nfs(cr) != 0)
693 		return (EPERM);
694 
695 	switch (rpcflavor) {
696 	case AUTH_DES:
697 		mutex_enter(&desauthtab_lock);
698 		if (desauthtab != NULL) {
699 			for (da = desauthtab;
700 			    da < &desauthtab[clnt_authdes_cachesz]; da++) {
701 				if (uid == da->da_uid &&
702 				    zoneid == da->da_zoneid)
703 					revoke_key(da->da_auth, 1);
704 			}
705 		}
706 		mutex_exit(&desauthtab_lock);
707 		return (0);
708 
709 	case RPCSEC_GSS: {
710 		rpc_gss_OID	mech;
711 		caddr_t		elements;
712 
713 		if (!mechanism)
714 			return (EINVAL);
715 
716 		/* copyin the gss mechanism type */
717 		mech = kmem_alloc(sizeof (rpc_gss_OID_desc), KM_SLEEP);
718 #ifdef _SYSCALL32_IMPL
719 		if (model != DATAMODEL_NATIVE) {
720 			gss_OID_desc32 mech32;
721 
722 			if (copyin(mechanism, &mech32,
723 			    sizeof (gss_OID_desc32))) {
724 				kmem_free(mech, sizeof (rpc_gss_OID_desc));
725 				return (EFAULT);
726 			}
727 			mech->length = mech32.length;
728 			mech->elements = (caddr_t)(uintptr_t)mech32.elements;
729 		} else
730 #endif /* _SYSCALL32_IMPL */
731 		if (copyin(mechanism, mech, sizeof (rpc_gss_OID_desc))) {
732 			kmem_free(mech, sizeof (rpc_gss_OID_desc));
733 			return (EFAULT);
734 		}
735 
736 		elements = kmem_alloc(mech->length, KM_SLEEP);
737 		if (copyin(mech->elements, elements, mech->length)) {
738 			kmem_free(elements, mech->length);
739 			kmem_free(mech, sizeof (rpc_gss_OID_desc));
740 			return (EFAULT);
741 		}
742 		mech->elements = elements;
743 
744 		error = rpc_gss_revauth(uid, mech);
745 
746 		kmem_free(elements, mech->length);
747 		kmem_free(mech, sizeof (rpc_gss_OID_desc));
748 
749 		return (error);
750 	}
751 
752 	default:
753 		/* not an auth type with cached creds */
754 		return (EINVAL);
755 	}
756 }
757 
758 /*
759  *  Since sec_data is the index for the client auth handles
760  *  cache table,  whenever the sec_data is freed, the index needs
761  *  to be nulled.
762  */
763 void
764 purge_authtab(struct sec_data *secdata)
765 {
766 	struct desauthent *da;
767 
768 	switch (secdata->rpcflavor) {
769 
770 	case AUTH_DES:
771 		mutex_enter(&desauthtab_lock);
772 		if (desauthtab != NULL) {
773 			for (da = desauthtab;
774 			    da < &desauthtab[clnt_authdes_cachesz]; da++) {
775 				if (da->da_data == secdata) {
776 					da->da_data = NULL;
777 					da->da_inuse = 0;
778 				}
779 			}
780 		}
781 		mutex_exit(&desauthtab_lock);
782 		return;
783 
784 	case RPCSEC_GSS:
785 		rpc_gss_secpurge((void *)secdata);
786 		return;
787 
788 	default:
789 		return;
790 	}
791 }
792 
793 void
794 sec_subrinit(void)
795 {
796 	authkern_cache = kmem_cache_create("authkern_cache",
797 	    sizeof (AUTH), 0, authkern_init, NULL, NULL, NULL, NULL, 0);
798 	authloopback_cache = kmem_cache_create("authloopback_cache",
799 	    sizeof (AUTH), 0, authloopback_init, NULL, NULL, NULL, NULL, 0);
800 	mutex_init(&desauthtab_lock, NULL, MUTEX_DEFAULT, NULL);
801 
802 	/* RPC stuff */
803 	mutex_init(&authdes_ops_lock, NULL, MUTEX_DEFAULT, NULL);
804 	zone_key_create(&auth_zone_key, auth_zone_init, NULL, auth_zone_fini);
805 }
806 
807 /*
808  * Destroys the caches and mutexes previously allocated and initialized
809  * in sec_subrinit().
810  * This routine is called by _init() if mod_install() failed.
811  */
812 void
813 sec_subrfini(void)
814 {
815 	mutex_destroy(&desauthtab_lock);
816 	kmem_cache_destroy(authkern_cache);
817 	kmem_cache_destroy(authloopback_cache);
818 
819 	/* RPC stuff */
820 	mutex_destroy(&authdes_ops_lock);
821 	(void) zone_key_delete(auth_zone_key);
822 }
823