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