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