1/*	$NetBSD: auth_unix.c,v 1.18 2000/07/06 03:03:30 christos Exp $	*/
2
3/*-
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 * Copyright (c) 2009, Sun Microsystems, Inc.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 * - Redistributions of source code must retain the above copyright notice,
12 *   this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright notice,
14 *   this list of conditions and the following disclaimer in the documentation
15 *   and/or other materials provided with the distribution.
16 * - Neither the name of Sun Microsystems, Inc. nor the names of its
17 *   contributors may be used to endorse or promote products derived
18 *   from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34/*
35 * auth_unix.c, Implements UNIX style authentication parameters.
36 *
37 * Copyright (C) 1984, Sun Microsystems, Inc.
38 *
39 * The system is very weak.  The client uses no encryption for it's
40 * credentials and only sends null verifiers.  The server sends backs
41 * null verifiers or optionally a verifier that suggests a new short hand
42 * for the credentials.
43 *
44 */
45
46#include <sys/param.h>
47#include <sys/systm.h>
48#include <sys/hash.h>
49#include <sys/kernel.h>
50#include <sys/lock.h>
51#include <sys/malloc.h>
52#include <sys/pcpu.h>
53#include <sys/refcount.h>
54#include <sys/sx.h>
55#include <sys/ucred.h>
56
57#include <rpc/types.h>
58#include <rpc/xdr.h>
59#include <rpc/auth.h>
60#include <rpc/clnt.h>
61
62#include <rpc/rpc_com.h>
63
64/* auth_unix.c */
65static void authunix_nextverf (AUTH *);
66static bool_t authunix_marshal (AUTH *, uint32_t, XDR *, struct mbuf *);
67static bool_t authunix_validate (AUTH *, uint32_t, struct opaque_auth *,
68    struct mbuf **);
69static bool_t authunix_refresh (AUTH *, void *);
70static void authunix_destroy (AUTH *);
71static void marshal_new_auth (AUTH *);
72
73static const struct auth_ops authunix_ops = {
74	.ah_nextverf =		authunix_nextverf,
75	.ah_marshal =		authunix_marshal,
76	.ah_validate =		authunix_validate,
77	.ah_refresh =		authunix_refresh,
78	.ah_destroy =		authunix_destroy,
79};
80
81/*
82 * This struct is pointed to by the ah_private field of an auth_handle.
83 */
84struct audata {
85	TAILQ_ENTRY(audata)	au_link;
86	TAILQ_ENTRY(audata)	au_alllink;
87	volatile u_int		au_refs;
88	struct xucred		au_xcred;
89	struct opaque_auth	au_origcred;	/* original credentials */
90	struct opaque_auth	au_shcred;	/* short hand cred */
91	u_long			au_shfaults;	/* short hand cache faults */
92	char			au_marshed[MAX_AUTH_BYTES];
93	u_int			au_mpos;	/* xdr pos at end of marshed */
94	AUTH			*au_auth;	/* link back to AUTH */
95};
96TAILQ_HEAD(audata_list, audata);
97#define	AUTH_PRIVATE(auth)	((struct audata *)auth->ah_private)
98
99#define AUTH_UNIX_HASH_SIZE	16
100#define AUTH_UNIX_MAX		256
101static struct audata_list auth_unix_cache[AUTH_UNIX_HASH_SIZE];
102static struct audata_list auth_unix_all;
103static struct sx auth_unix_lock;
104static int auth_unix_count;
105
106static void
107authunix_init(void *dummy)
108{
109	int i;
110
111	for (i = 0; i < AUTH_UNIX_HASH_SIZE; i++)
112		TAILQ_INIT(&auth_unix_cache[i]);
113	TAILQ_INIT(&auth_unix_all);
114	sx_init(&auth_unix_lock, "auth_unix_lock");
115}
116SYSINIT(authunix_init, SI_SUB_KMEM, SI_ORDER_ANY, authunix_init, NULL);
117
118/*
119 * Create a unix style authenticator.
120 * Returns an auth handle with the given stuff in it.
121 */
122AUTH *
123authunix_create(struct ucred *cred)
124{
125	uint32_t h, th;
126	struct xucred xcr;
127	char mymem[MAX_AUTH_BYTES];
128	XDR xdrs;
129	AUTH *auth;
130	struct audata *au, *tau;
131	struct timeval now;
132	uint32_t time;
133	int len;
134
135	if (auth_unix_count > AUTH_UNIX_MAX) {
136		while (auth_unix_count > AUTH_UNIX_MAX) {
137			sx_xlock(&auth_unix_lock);
138			tau = TAILQ_FIRST(&auth_unix_all);
139			th = HASHSTEP(HASHINIT, tau->au_xcred.cr_uid)
140				% AUTH_UNIX_HASH_SIZE;
141			TAILQ_REMOVE(&auth_unix_cache[th], tau, au_link);
142			TAILQ_REMOVE(&auth_unix_all, tau, au_alllink);
143			auth_unix_count--;
144			sx_xunlock(&auth_unix_lock);
145			AUTH_DESTROY(tau->au_auth);
146		}
147	}
148
149	/*
150	 * Hash the uid to see if we already have an AUTH with this cred.
151	 */
152	h = HASHSTEP(HASHINIT, cred->cr_uid) % AUTH_UNIX_HASH_SIZE;
153	cru2x(cred, &xcr);
154again:
155	sx_slock(&auth_unix_lock);
156	TAILQ_FOREACH(au, &auth_unix_cache[h], au_link) {
157		if (!memcmp(&xcr, &au->au_xcred, sizeof(xcr))) {
158			refcount_acquire(&au->au_refs);
159			if (sx_try_upgrade(&auth_unix_lock)) {
160				/*
161				 * Keep auth_unix_all LRU sorted.
162				 */
163				TAILQ_REMOVE(&auth_unix_all, au, au_alllink);
164				TAILQ_INSERT_TAIL(&auth_unix_all, au,
165				    au_alllink);
166				sx_xunlock(&auth_unix_lock);
167			} else {
168				sx_sunlock(&auth_unix_lock);
169			}
170			return (au->au_auth);
171		}
172	}
173
174	sx_sunlock(&auth_unix_lock);
175
176	/*
177	 * Allocate and set up auth handle
178	 */
179	au = NULL;
180	auth = mem_alloc(sizeof(*auth));
181	au = mem_alloc(sizeof(*au));
182	auth->ah_ops = &authunix_ops;
183	auth->ah_private = (caddr_t)au;
184	auth->ah_verf = au->au_shcred = _null_auth;
185	refcount_init(&au->au_refs, 1);
186	au->au_xcred = xcr;
187	au->au_shfaults = 0;
188	au->au_origcred.oa_base = NULL;
189	au->au_auth = auth;
190
191	getmicrotime(&now);
192	time = now.tv_sec;
193
194	/*
195	 * Serialize the parameters into origcred
196	 */
197	xdrmem_create(&xdrs, mymem, MAX_AUTH_BYTES, XDR_ENCODE);
198	cru2x(cred, &xcr);
199	if (! xdr_authunix_parms(&xdrs, &time, &xcr))
200		panic("authunix_create: failed to encode creds");
201	au->au_origcred.oa_length = len = XDR_GETPOS(&xdrs);
202	au->au_origcred.oa_flavor = AUTH_UNIX;
203	au->au_origcred.oa_base = mem_alloc((u_int) len);
204	memcpy(au->au_origcred.oa_base, mymem, (size_t)len);
205
206	/*
207	 * set auth handle to reflect new cred.
208	 */
209	auth->ah_cred = au->au_origcred;
210	marshal_new_auth(auth);
211
212	sx_xlock(&auth_unix_lock);
213	TAILQ_FOREACH(tau, &auth_unix_cache[h], au_link) {
214		if (!memcmp(&xcr, &tau->au_xcred, sizeof(xcr))) {
215			/*
216			 * We lost a race to create the AUTH that
217			 * matches this cred.
218			 */
219			sx_xunlock(&auth_unix_lock);
220			AUTH_DESTROY(auth);
221			goto again;
222		}
223	}
224
225	auth_unix_count++;
226	TAILQ_INSERT_TAIL(&auth_unix_cache[h], au, au_link);
227	TAILQ_INSERT_TAIL(&auth_unix_all, au, au_alllink);
228	refcount_acquire(&au->au_refs);	/* one for the cache, one for user */
229	sx_xunlock(&auth_unix_lock);
230
231	return (auth);
232}
233
234/*
235 * authunix operations
236 */
237
238/* ARGSUSED */
239static void
240authunix_nextverf(AUTH *auth)
241{
242	/* no action necessary */
243}
244
245static bool_t
246authunix_marshal(AUTH *auth, uint32_t xid, XDR *xdrs, struct mbuf *args)
247{
248	struct audata *au;
249
250	au = AUTH_PRIVATE(auth);
251	if (!XDR_PUTBYTES(xdrs, au->au_marshed, au->au_mpos))
252		return (FALSE);
253
254	xdrmbuf_append(xdrs, args);
255
256	return (TRUE);
257}
258
259static bool_t
260authunix_validate(AUTH *auth, uint32_t xid, struct opaque_auth *verf,
261    struct mbuf **mrepp)
262{
263	struct audata *au;
264	XDR txdrs;
265
266	if (!verf)
267		return (TRUE);
268
269	if (verf->oa_flavor == AUTH_SHORT) {
270		au = AUTH_PRIVATE(auth);
271		xdrmem_create(&txdrs, verf->oa_base, verf->oa_length,
272		    XDR_DECODE);
273
274		if (au->au_shcred.oa_base != NULL) {
275			mem_free(au->au_shcred.oa_base,
276			    au->au_shcred.oa_length);
277			au->au_shcred.oa_base = NULL;
278		}
279		if (xdr_opaque_auth(&txdrs, &au->au_shcred)) {
280			auth->ah_cred = au->au_shcred;
281		} else {
282			txdrs.x_op = XDR_FREE;
283			(void)xdr_opaque_auth(&txdrs, &au->au_shcred);
284			au->au_shcred.oa_base = NULL;
285			auth->ah_cred = au->au_origcred;
286		}
287		marshal_new_auth(auth);
288	}
289
290	return (TRUE);
291}
292
293static bool_t
294authunix_refresh(AUTH *auth, void *dummy)
295{
296	struct audata *au = AUTH_PRIVATE(auth);
297	struct xucred xcr;
298	uint32_t time;
299	struct timeval now;
300	XDR xdrs;
301	int stat;
302
303	if (auth->ah_cred.oa_base == au->au_origcred.oa_base) {
304		/* there is no hope.  Punt */
305		return (FALSE);
306	}
307	au->au_shfaults ++;
308
309	/* first deserialize the creds back into a struct ucred */
310	xdrmem_create(&xdrs, au->au_origcred.oa_base,
311	    au->au_origcred.oa_length, XDR_DECODE);
312	stat = xdr_authunix_parms(&xdrs, &time, &xcr);
313	if (! stat)
314		goto done;
315
316	/* update the time and serialize in place */
317	getmicrotime(&now);
318	time = now.tv_sec;
319	xdrs.x_op = XDR_ENCODE;
320	XDR_SETPOS(&xdrs, 0);
321
322	stat = xdr_authunix_parms(&xdrs, &time, &xcr);
323	if (! stat)
324		goto done;
325	auth->ah_cred = au->au_origcred;
326	marshal_new_auth(auth);
327done:
328	XDR_DESTROY(&xdrs);
329	return (stat);
330}
331
332static void
333authunix_destroy(AUTH *auth)
334{
335	struct audata *au;
336
337	au = AUTH_PRIVATE(auth);
338
339	if (!refcount_release(&au->au_refs))
340		return;
341
342	mem_free(au->au_origcred.oa_base, au->au_origcred.oa_length);
343
344	if (au->au_shcred.oa_base != NULL)
345		mem_free(au->au_shcred.oa_base, au->au_shcred.oa_length);
346
347	mem_free(auth->ah_private, sizeof(struct audata));
348
349	if (auth->ah_verf.oa_base != NULL)
350		mem_free(auth->ah_verf.oa_base, auth->ah_verf.oa_length);
351
352	mem_free(auth, sizeof(*auth));
353}
354
355/*
356 * Marshals (pre-serializes) an auth struct.
357 * sets private data, au_marshed and au_mpos
358 */
359static void
360marshal_new_auth(AUTH *auth)
361{
362	XDR	xdr_stream;
363	XDR	*xdrs = &xdr_stream;
364	struct audata *au;
365
366	au = AUTH_PRIVATE(auth);
367	xdrmem_create(xdrs, au->au_marshed, MAX_AUTH_BYTES, XDR_ENCODE);
368	if ((! xdr_opaque_auth(xdrs, &(auth->ah_cred))) ||
369	    (! xdr_opaque_auth(xdrs, &(auth->ah_verf))))
370		printf("auth_none.c - Fatal marshalling problem");
371	else
372		au->au_mpos = XDR_GETPOS(xdrs);
373	XDR_DESTROY(xdrs);
374}
375