1177633Sdfr/*	$NetBSD: auth_unix.c,v 1.18 2000/07/06 03:03:30 christos Exp $	*/
2177633Sdfr
3261046Smav/*-
4261046Smav * Copyright (c) 2009, Sun Microsystems, Inc.
5261046Smav * All rights reserved.
6177633Sdfr *
7261046Smav * Redistribution and use in source and binary forms, with or without
8261046Smav * modification, are permitted provided that the following conditions are met:
9261046Smav * - Redistributions of source code must retain the above copyright notice,
10261046Smav *   this list of conditions and the following disclaimer.
11261046Smav * - Redistributions in binary form must reproduce the above copyright notice,
12261046Smav *   this list of conditions and the following disclaimer in the documentation
13261046Smav *   and/or other materials provided with the distribution.
14261046Smav * - Neither the name of Sun Microsystems, Inc. nor the names of its
15261046Smav *   contributors may be used to endorse or promote products derived
16261046Smav *   from this software without specific prior written permission.
17261046Smav *
18261046Smav * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19261046Smav * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20261046Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21261046Smav * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22261046Smav * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23261046Smav * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24261046Smav * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25261046Smav * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26261046Smav * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27261046Smav * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28261046Smav * POSSIBILITY OF SUCH DAMAGE.
29177633Sdfr */
30177633Sdfr
31177633Sdfr#if defined(LIBC_SCCS) && !defined(lint)
32177633Sdfrstatic char *sccsid2 = "@(#)auth_unix.c 1.19 87/08/11 Copyr 1984 Sun Micro";
33177633Sdfrstatic char *sccsid = "@(#)auth_unix.c	2.2 88/08/01 4.0 RPCSRC";
34177633Sdfr#endif
35177633Sdfr#include <sys/cdefs.h>
36177633Sdfr__FBSDID("$FreeBSD$");
37177633Sdfr
38177633Sdfr/*
39177633Sdfr * auth_unix.c, Implements UNIX style authentication parameters.
40177633Sdfr *
41177633Sdfr * Copyright (C) 1984, Sun Microsystems, Inc.
42177633Sdfr *
43177633Sdfr * The system is very weak.  The client uses no encryption for it's
44177633Sdfr * credentials and only sends null verifiers.  The server sends backs
45177633Sdfr * null verifiers or optionally a verifier that suggests a new short hand
46177633Sdfr * for the credentials.
47177633Sdfr *
48177633Sdfr */
49177633Sdfr
50177633Sdfr#include <sys/param.h>
51177633Sdfr#include <sys/systm.h>
52180025Sdfr#include <sys/hash.h>
53180025Sdfr#include <sys/kernel.h>
54177633Sdfr#include <sys/lock.h>
55177633Sdfr#include <sys/malloc.h>
56180064Sdfr#include <sys/pcpu.h>
57180743Sdfr#include <sys/refcount.h>
58180025Sdfr#include <sys/sx.h>
59177633Sdfr#include <sys/ucred.h>
60177633Sdfr
61177633Sdfr#include <rpc/types.h>
62177633Sdfr#include <rpc/xdr.h>
63177633Sdfr#include <rpc/auth.h>
64184588Sdfr#include <rpc/clnt.h>
65177633Sdfr
66177685Sdfr#include <rpc/rpc_com.h>
67177633Sdfr
68177633Sdfr/* auth_unix.c */
69177633Sdfrstatic void authunix_nextverf (AUTH *);
70184588Sdfrstatic bool_t authunix_marshal (AUTH *, uint32_t, XDR *, struct mbuf *);
71184588Sdfrstatic bool_t authunix_validate (AUTH *, uint32_t, struct opaque_auth *,
72184588Sdfr    struct mbuf **);
73177633Sdfrstatic bool_t authunix_refresh (AUTH *, void *);
74177633Sdfrstatic void authunix_destroy (AUTH *);
75177633Sdfrstatic void marshal_new_auth (AUTH *);
76177633Sdfr
77177633Sdfrstatic struct auth_ops authunix_ops = {
78177633Sdfr	.ah_nextverf =		authunix_nextverf,
79177633Sdfr	.ah_marshal =		authunix_marshal,
80177633Sdfr	.ah_validate =		authunix_validate,
81177633Sdfr	.ah_refresh =		authunix_refresh,
82184588Sdfr	.ah_destroy =		authunix_destroy,
83177633Sdfr};
84177633Sdfr
85177633Sdfr/*
86177633Sdfr * This struct is pointed to by the ah_private field of an auth_handle.
87177633Sdfr */
88177633Sdfrstruct audata {
89180025Sdfr	TAILQ_ENTRY(audata)	au_link;
90180025Sdfr	TAILQ_ENTRY(audata)	au_alllink;
91180743Sdfr	volatile u_int		au_refs;
92180025Sdfr	struct xucred		au_xcred;
93177633Sdfr	struct opaque_auth	au_origcred;	/* original credentials */
94177633Sdfr	struct opaque_auth	au_shcred;	/* short hand cred */
95177633Sdfr	u_long			au_shfaults;	/* short hand cache faults */
96177633Sdfr	char			au_marshed[MAX_AUTH_BYTES];
97177633Sdfr	u_int			au_mpos;	/* xdr pos at end of marshed */
98180025Sdfr	AUTH			*au_auth;	/* link back to AUTH */
99177633Sdfr};
100180025SdfrTAILQ_HEAD(audata_list, audata);
101177633Sdfr#define	AUTH_PRIVATE(auth)	((struct audata *)auth->ah_private)
102177633Sdfr
103180025Sdfr#define AUTH_UNIX_HASH_SIZE	16
104180025Sdfr#define AUTH_UNIX_MAX		256
105180025Sdfrstatic struct audata_list auth_unix_cache[AUTH_UNIX_HASH_SIZE];
106180025Sdfrstatic struct audata_list auth_unix_all;
107180025Sdfrstatic struct sx auth_unix_lock;
108180025Sdfrstatic int auth_unix_count;
109180025Sdfr
110180025Sdfrstatic void
111180025Sdfrauthunix_init(void *dummy)
112180025Sdfr{
113180025Sdfr	int i;
114180025Sdfr
115180025Sdfr	for (i = 0; i < AUTH_UNIX_HASH_SIZE; i++)
116180025Sdfr		TAILQ_INIT(&auth_unix_cache[i]);
117180025Sdfr	TAILQ_INIT(&auth_unix_all);
118180025Sdfr	sx_init(&auth_unix_lock, "auth_unix_lock");
119180025Sdfr}
120180025SdfrSYSINIT(authunix_init, SI_SUB_KMEM, SI_ORDER_ANY, authunix_init, NULL);
121180025Sdfr
122177633Sdfr/*
123177633Sdfr * Create a unix style authenticator.
124177633Sdfr * Returns an auth handle with the given stuff in it.
125177633Sdfr */
126177633SdfrAUTH *
127177633Sdfrauthunix_create(struct ucred *cred)
128177633Sdfr{
129180025Sdfr	uint32_t h, th;
130177633Sdfr	struct xucred xcr;
131177633Sdfr	char mymem[MAX_AUTH_BYTES];
132177633Sdfr	XDR xdrs;
133177633Sdfr	AUTH *auth;
134180025Sdfr	struct audata *au, *tau;
135177633Sdfr	struct timeval now;
136177633Sdfr	uint32_t time;
137177633Sdfr	int len;
138177633Sdfr
139180025Sdfr	if (auth_unix_count > AUTH_UNIX_MAX) {
140180025Sdfr		while (auth_unix_count > AUTH_UNIX_MAX) {
141180025Sdfr			sx_xlock(&auth_unix_lock);
142180025Sdfr			tau = TAILQ_FIRST(&auth_unix_all);
143180025Sdfr			th = HASHSTEP(HASHINIT, tau->au_xcred.cr_uid)
144180025Sdfr				% AUTH_UNIX_HASH_SIZE;
145180025Sdfr			TAILQ_REMOVE(&auth_unix_cache[th], tau, au_link);
146180025Sdfr			TAILQ_REMOVE(&auth_unix_all, tau, au_alllink);
147180025Sdfr			auth_unix_count--;
148180025Sdfr			sx_xunlock(&auth_unix_lock);
149180025Sdfr			AUTH_DESTROY(tau->au_auth);
150180025Sdfr		}
151180025Sdfr	}
152180025Sdfr
153177633Sdfr	/*
154180025Sdfr	 * Hash the uid to see if we already have an AUTH with this cred.
155180025Sdfr	 */
156180025Sdfr	h = HASHSTEP(HASHINIT, cred->cr_uid) % AUTH_UNIX_HASH_SIZE;
157180025Sdfr	cru2x(cred, &xcr);
158180025Sdfragain:
159180025Sdfr	sx_slock(&auth_unix_lock);
160180025Sdfr	TAILQ_FOREACH(au, &auth_unix_cache[h], au_link) {
161180025Sdfr		if (!memcmp(&xcr, &au->au_xcred, sizeof(xcr))) {
162180743Sdfr			refcount_acquire(&au->au_refs);
163180025Sdfr			if (sx_try_upgrade(&auth_unix_lock)) {
164180025Sdfr				/*
165180025Sdfr				 * Keep auth_unix_all LRU sorted.
166180025Sdfr				 */
167180025Sdfr				TAILQ_REMOVE(&auth_unix_all, au, au_alllink);
168180025Sdfr				TAILQ_INSERT_TAIL(&auth_unix_all, au,
169180025Sdfr				    au_alllink);
170180025Sdfr				sx_xunlock(&auth_unix_lock);
171180025Sdfr			} else {
172180025Sdfr				sx_sunlock(&auth_unix_lock);
173180025Sdfr			}
174180743Sdfr			return (au->au_auth);
175180025Sdfr		}
176180025Sdfr	}
177180025Sdfr
178180743Sdfr	sx_sunlock(&auth_unix_lock);
179180743Sdfr
180180025Sdfr	/*
181177633Sdfr	 * Allocate and set up auth handle
182177633Sdfr	 */
183177633Sdfr	au = NULL;
184177633Sdfr	auth = mem_alloc(sizeof(*auth));
185177633Sdfr	au = mem_alloc(sizeof(*au));
186177633Sdfr	auth->ah_ops = &authunix_ops;
187177633Sdfr	auth->ah_private = (caddr_t)au;
188177633Sdfr	auth->ah_verf = au->au_shcred = _null_auth;
189180743Sdfr	refcount_init(&au->au_refs, 1);
190180025Sdfr	au->au_xcred = xcr;
191177633Sdfr	au->au_shfaults = 0;
192177633Sdfr	au->au_origcred.oa_base = NULL;
193180025Sdfr	au->au_auth = auth;
194177633Sdfr
195177633Sdfr	getmicrotime(&now);
196177633Sdfr	time = now.tv_sec;
197177633Sdfr
198177633Sdfr	/*
199177633Sdfr	 * Serialize the parameters into origcred
200177633Sdfr	 */
201177633Sdfr	xdrmem_create(&xdrs, mymem, MAX_AUTH_BYTES, XDR_ENCODE);
202177633Sdfr	cru2x(cred, &xcr);
203177633Sdfr	if (! xdr_authunix_parms(&xdrs, &time, &xcr))
204177633Sdfr		panic("authunix_create: failed to encode creds");
205177633Sdfr	au->au_origcred.oa_length = len = XDR_GETPOS(&xdrs);
206177633Sdfr	au->au_origcred.oa_flavor = AUTH_UNIX;
207177633Sdfr	au->au_origcred.oa_base = mem_alloc((u_int) len);
208177633Sdfr	memcpy(au->au_origcred.oa_base, mymem, (size_t)len);
209177633Sdfr
210177633Sdfr	/*
211177633Sdfr	 * set auth handle to reflect new cred.
212177633Sdfr	 */
213177633Sdfr	auth->ah_cred = au->au_origcred;
214177633Sdfr	marshal_new_auth(auth);
215180025Sdfr
216180743Sdfr	sx_xlock(&auth_unix_lock);
217180743Sdfr	TAILQ_FOREACH(tau, &auth_unix_cache[h], au_link) {
218180743Sdfr		if (!memcmp(&xcr, &tau->au_xcred, sizeof(xcr))) {
219180743Sdfr			/*
220180743Sdfr			 * We lost a race to create the AUTH that
221180743Sdfr			 * matches this cred.
222180743Sdfr			 */
223180743Sdfr			sx_xunlock(&auth_unix_lock);
224180743Sdfr			AUTH_DESTROY(auth);
225180743Sdfr			goto again;
226180743Sdfr		}
227177633Sdfr	}
228180743Sdfr
229180743Sdfr	auth_unix_count++;
230180743Sdfr	TAILQ_INSERT_TAIL(&auth_unix_cache[h], au, au_link);
231180743Sdfr	TAILQ_INSERT_TAIL(&auth_unix_all, au, au_alllink);
232180743Sdfr	refcount_acquire(&au->au_refs);	/* one for the cache, one for user */
233180743Sdfr	sx_xunlock(&auth_unix_lock);
234180743Sdfr
235180743Sdfr	return (auth);
236177633Sdfr}
237177633Sdfr
238177633Sdfr/*
239177633Sdfr * authunix operations
240177633Sdfr */
241177633Sdfr
242177633Sdfr/* ARGSUSED */
243177633Sdfrstatic void
244177633Sdfrauthunix_nextverf(AUTH *auth)
245177633Sdfr{
246177633Sdfr	/* no action necessary */
247177633Sdfr}
248177633Sdfr
249177633Sdfrstatic bool_t
250184588Sdfrauthunix_marshal(AUTH *auth, uint32_t xid, XDR *xdrs, struct mbuf *args)
251177633Sdfr{
252177633Sdfr	struct audata *au;
253177633Sdfr
254177633Sdfr	au = AUTH_PRIVATE(auth);
255184588Sdfr	if (!XDR_PUTBYTES(xdrs, au->au_marshed, au->au_mpos))
256184588Sdfr		return (FALSE);
257184588Sdfr
258184588Sdfr	xdrmbuf_append(xdrs, args);
259184588Sdfr
260184588Sdfr	return (TRUE);
261177633Sdfr}
262177633Sdfr
263177633Sdfrstatic bool_t
264184588Sdfrauthunix_validate(AUTH *auth, uint32_t xid, struct opaque_auth *verf,
265184588Sdfr    struct mbuf **mrepp)
266177633Sdfr{
267177633Sdfr	struct audata *au;
268184588Sdfr	XDR txdrs;
269177633Sdfr
270184588Sdfr	if (!verf)
271184588Sdfr		return (TRUE);
272184588Sdfr
273177633Sdfr	if (verf->oa_flavor == AUTH_SHORT) {
274177633Sdfr		au = AUTH_PRIVATE(auth);
275184588Sdfr		xdrmem_create(&txdrs, verf->oa_base, verf->oa_length,
276177633Sdfr		    XDR_DECODE);
277177633Sdfr
278177633Sdfr		if (au->au_shcred.oa_base != NULL) {
279177633Sdfr			mem_free(au->au_shcred.oa_base,
280177633Sdfr			    au->au_shcred.oa_length);
281177633Sdfr			au->au_shcred.oa_base = NULL;
282177633Sdfr		}
283184588Sdfr		if (xdr_opaque_auth(&txdrs, &au->au_shcred)) {
284177633Sdfr			auth->ah_cred = au->au_shcred;
285177633Sdfr		} else {
286184588Sdfr			txdrs.x_op = XDR_FREE;
287184588Sdfr			(void)xdr_opaque_auth(&txdrs, &au->au_shcred);
288177633Sdfr			au->au_shcred.oa_base = NULL;
289177633Sdfr			auth->ah_cred = au->au_origcred;
290177633Sdfr		}
291177633Sdfr		marshal_new_auth(auth);
292177633Sdfr	}
293184588Sdfr
294177633Sdfr	return (TRUE);
295177633Sdfr}
296177633Sdfr
297177633Sdfrstatic bool_t
298177633Sdfrauthunix_refresh(AUTH *auth, void *dummy)
299177633Sdfr{
300177633Sdfr	struct audata *au = AUTH_PRIVATE(auth);
301177633Sdfr	struct xucred xcr;
302177633Sdfr	uint32_t time;
303177633Sdfr	struct timeval now;
304177633Sdfr	XDR xdrs;
305177633Sdfr	int stat;
306177633Sdfr
307177633Sdfr	if (auth->ah_cred.oa_base == au->au_origcred.oa_base) {
308177633Sdfr		/* there is no hope.  Punt */
309177633Sdfr		return (FALSE);
310177633Sdfr	}
311177633Sdfr	au->au_shfaults ++;
312177633Sdfr
313177633Sdfr	/* first deserialize the creds back into a struct ucred */
314177633Sdfr	xdrmem_create(&xdrs, au->au_origcred.oa_base,
315177633Sdfr	    au->au_origcred.oa_length, XDR_DECODE);
316177633Sdfr	stat = xdr_authunix_parms(&xdrs, &time, &xcr);
317177633Sdfr	if (! stat)
318177633Sdfr		goto done;
319177633Sdfr
320177633Sdfr	/* update the time and serialize in place */
321177633Sdfr	getmicrotime(&now);
322177633Sdfr	time = now.tv_sec;
323177633Sdfr	xdrs.x_op = XDR_ENCODE;
324177633Sdfr	XDR_SETPOS(&xdrs, 0);
325177633Sdfr
326177633Sdfr	stat = xdr_authunix_parms(&xdrs, &time, &xcr);
327177633Sdfr	if (! stat)
328177633Sdfr		goto done;
329177633Sdfr	auth->ah_cred = au->au_origcred;
330177633Sdfr	marshal_new_auth(auth);
331177633Sdfrdone:
332177633Sdfr	XDR_DESTROY(&xdrs);
333177633Sdfr	return (stat);
334177633Sdfr}
335177633Sdfr
336177633Sdfrstatic void
337177633Sdfrauthunix_destroy(AUTH *auth)
338177633Sdfr{
339177633Sdfr	struct audata *au;
340177633Sdfr
341177633Sdfr	au = AUTH_PRIVATE(auth);
342180025Sdfr
343180743Sdfr	if (!refcount_release(&au->au_refs))
344180025Sdfr		return;
345180025Sdfr
346177633Sdfr	mem_free(au->au_origcred.oa_base, au->au_origcred.oa_length);
347177633Sdfr
348177633Sdfr	if (au->au_shcred.oa_base != NULL)
349177633Sdfr		mem_free(au->au_shcred.oa_base, au->au_shcred.oa_length);
350177633Sdfr
351177633Sdfr	mem_free(auth->ah_private, sizeof(struct audata));
352177633Sdfr
353177633Sdfr	if (auth->ah_verf.oa_base != NULL)
354177633Sdfr		mem_free(auth->ah_verf.oa_base, auth->ah_verf.oa_length);
355177633Sdfr
356177633Sdfr	mem_free(auth, sizeof(*auth));
357177633Sdfr}
358177633Sdfr
359177633Sdfr/*
360177633Sdfr * Marshals (pre-serializes) an auth struct.
361177633Sdfr * sets private data, au_marshed and au_mpos
362177633Sdfr */
363177633Sdfrstatic void
364177633Sdfrmarshal_new_auth(AUTH *auth)
365177633Sdfr{
366177633Sdfr	XDR	xdr_stream;
367177633Sdfr	XDR	*xdrs = &xdr_stream;
368177633Sdfr	struct audata *au;
369177633Sdfr
370177633Sdfr	au = AUTH_PRIVATE(auth);
371177633Sdfr	xdrmem_create(xdrs, au->au_marshed, MAX_AUTH_BYTES, XDR_ENCODE);
372177633Sdfr	if ((! xdr_opaque_auth(xdrs, &(auth->ah_cred))) ||
373177633Sdfr	    (! xdr_opaque_auth(xdrs, &(auth->ah_verf))))
374177633Sdfr		printf("auth_none.c - Fatal marshalling problem");
375177633Sdfr	else
376177633Sdfr		au->au_mpos = XDR_GETPOS(xdrs);
377177633Sdfr	XDR_DESTROY(xdrs);
378177633Sdfr}
379