1/*
2 * Copyright (c) 2010, 2011, Oracle America, Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer in the documentation and/or other materials
13 * provided with the distribution.
14 * * Neither the name of the "Oracle America, Inc." nor the names of its
15 * contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31/*
32 * auth_unix.c, Implements UNIX style authentication parameters.
33 *
34 * The system is very weak. The client uses no encryption for it's
35 * credentials and only sends null verifiers. The server sends backs
36 * null verifiers or optionally a verifier that suggests a new short hand
37 * for the credentials.
38 */
39
40#include <errno.h>
41#include <limits.h>
42#include <stdbool.h>
43#include <stdio.h>
44#include <string.h>
45#include <unistd.h>
46#include <libintl.h>
47#include <sys/param.h>
48#include <wchar.h>
49#include <shlib-compat.h>
50
51#include <rpc/types.h>
52#include <rpc/xdr.h>
53#include <rpc/auth.h>
54#include <rpc/auth_unix.h>
55
56
57/*
58 * Unix authenticator operations vector
59 */
60static void authunix_nextverf (AUTH *);
61static bool_t authunix_marshal (AUTH *, XDR *);
62static bool_t authunix_validate (AUTH *, struct opaque_auth *);
63static bool_t authunix_refresh (AUTH *);
64static void authunix_destroy (AUTH *);
65
66static const struct auth_ops auth_unix_ops = {
67 authunix_nextverf,
68 authunix_marshal,
69 authunix_validate,
70 authunix_refresh,
71 authunix_destroy
72};
73
74/*
75 * This struct is pointed to by the ah_private field of an auth_handle.
76 */
77struct audata {
78 struct opaque_auth au_origcred; /* original credentials */
79 struct opaque_auth au_shcred; /* short hand cred */
80 u_long au_shfaults; /* short hand cache faults */
81 char au_marshed[MAX_AUTH_BYTES];
82 u_int au_mpos; /* xdr pos at end of marshed */
83};
84#define AUTH_PRIVATE(auth) ((struct audata *)auth->ah_private)
85
86static bool_t marshal_new_auth (AUTH *);
87
88
89/*
90 * Create a unix style authenticator.
91 * Returns an auth handle with the given stuff in it.
92 */
93AUTH *
94authunix_create (char *machname, uid_t uid, gid_t gid, int len,
95 gid_t *aup_gids)
96{
97 struct authunix_parms aup;
98 char mymem[MAX_AUTH_BYTES];
99 struct timeval now;
100 XDR xdrs;
101 AUTH *auth;
102 struct audata *au;
103
104 /*
105 * Allocate and set up auth handle
106 */
107 auth = (AUTH *) mem_alloc (sizeof (*auth));
108 au = (struct audata *) mem_alloc (sizeof (*au));
109 if (auth == NULL || au == NULL)
110 {
111no_memory:
112 (void) __fxprintf (NULL, "%s: %s", __func__, _("out of memory\n"));
113 mem_free (auth, sizeof (*auth));
114 mem_free (au, sizeof (*au));
115 return NULL;
116 }
117 auth->ah_ops = (struct auth_ops *) &auth_unix_ops;
118 auth->ah_private = (caddr_t) au;
119 auth->ah_verf = au->au_shcred = _null_auth;
120 au->au_shfaults = 0;
121
122 /*
123 * fill in param struct from the given params
124 */
125 (void) __gettimeofday (&now, (struct timezone *) 0);
126 aup.aup_time = now.tv_sec;
127 aup.aup_machname = machname;
128 aup.aup_uid = uid;
129 aup.aup_gid = gid;
130 aup.aup_len = (u_int) len;
131 aup.aup_gids = aup_gids;
132
133 /*
134 * Serialize the parameters into origcred
135 */
136 xdrmem_create (&xdrs, mymem, MAX_AUTH_BYTES, XDR_ENCODE);
137 if (!xdr_authunix_parms (&xdrs, &aup))
138 abort ();
139 au->au_origcred.oa_length = len = XDR_GETPOS (&xdrs);
140 au->au_origcred.oa_flavor = AUTH_UNIX;
141 au->au_origcred.oa_base = mem_alloc ((u_int) len);
142 if (au->au_origcred.oa_base == NULL)
143 goto no_memory;
144 memcpy(au->au_origcred.oa_base, mymem, (u_int) len);
145
146 /*
147 * set auth handle to reflect new cred.
148 */
149 auth->ah_cred = au->au_origcred;
150 marshal_new_auth (auth);
151 return auth;
152}
153libc_hidden_nolink_sunrpc (authunix_create, GLIBC_2_0)
154
155/*
156 * Returns an auth handle with parameters determined by doing lots of
157 * syscalls.
158 */
159AUTH *
160authunix_create_default (void)
161{
162 char machname[MAX_MACHINE_NAME + 1];
163
164 if (__gethostname (machname, MAX_MACHINE_NAME) == -1)
165 abort ();
166 machname[MAX_MACHINE_NAME] = 0;
167 uid_t uid = __geteuid ();
168 gid_t gid = __getegid ();
169
170 int max_nr_groups;
171 /* When we have to try a second time, do not use alloca() again. We
172 might have reached the stack limit already. */
173 bool retry = false;
174 again:
175 /* Ask the kernel how many groups there are exactly. Note that we
176 might have to redo all this if the number of groups has changed
177 between the two calls. */
178 max_nr_groups = __getgroups (0, NULL);
179
180 /* Just some random reasonable stack limit. */
181#define ALLOCA_LIMIT (1024 / sizeof (gid_t))
182 gid_t *gids = NULL;
183 if (max_nr_groups < ALLOCA_LIMIT && ! retry)
184 gids = (gid_t *) alloca (max_nr_groups * sizeof (gid_t));
185 else
186 {
187 gids = (gid_t *) malloc (max_nr_groups * sizeof (gid_t));
188 if (gids == NULL)
189 return NULL;
190 }
191
192 int len = __getgroups (max_nr_groups, gids);
193 if (len == -1)
194 {
195 if (errno == EINVAL)
196 {
197 /* New groups added in the meantime. Try again. */
198 if (max_nr_groups >= ALLOCA_LIMIT || retry)
199 free (gids);
200 retry = true;
201 goto again;
202 }
203 /* No other error can happen. */
204 abort ();
205 }
206
207 /* This braindamaged Sun code forces us here to truncate the
208 list of groups to NGRPS members since the code in
209 authuxprot.c transforms a fixed array. Grrr. */
210 AUTH *result = authunix_create (machname, uid, gid, MIN (NGRPS, len), gids);
211
212 if (max_nr_groups >= ALLOCA_LIMIT || retry)
213 free (gids);
214
215 return result;
216}
217#ifdef EXPORT_RPC_SYMBOLS
218libc_hidden_def (authunix_create_default)
219#else
220libc_hidden_nolink_sunrpc (authunix_create_default, GLIBC_2_0)
221#endif
222
223/*
224 * authunix operations
225 */
226
227static void
228authunix_nextverf (AUTH *auth)
229{
230 /* no action necessary */
231}
232
233static bool_t
234authunix_marshal (AUTH *auth, XDR *xdrs)
235{
236 struct audata *au = AUTH_PRIVATE (auth);
237
238 return XDR_PUTBYTES (xdrs, au->au_marshed, au->au_mpos);
239}
240
241static bool_t
242authunix_validate (AUTH *auth, struct opaque_auth *verf)
243{
244 struct audata *au;
245 XDR xdrs;
246
247 if (verf->oa_flavor == AUTH_SHORT)
248 {
249 au = AUTH_PRIVATE (auth);
250 xdrmem_create (&xdrs, verf->oa_base, verf->oa_length, XDR_DECODE);
251
252 if (au->au_shcred.oa_base != NULL)
253 {
254 mem_free (au->au_shcred.oa_base,
255 au->au_shcred.oa_length);
256 au->au_shcred.oa_base = NULL;
257 }
258 if (xdr_opaque_auth (&xdrs, &au->au_shcred))
259 {
260 auth->ah_cred = au->au_shcred;
261 }
262 else
263 {
264 xdrs.x_op = XDR_FREE;
265 (void) xdr_opaque_auth (&xdrs, &au->au_shcred);
266 au->au_shcred.oa_base = NULL;
267 auth->ah_cred = au->au_origcred;
268 }
269 marshal_new_auth (auth);
270 }
271 return TRUE;
272}
273
274static bool_t
275authunix_refresh (AUTH *auth)
276{
277 struct audata *au = AUTH_PRIVATE (auth);
278 struct authunix_parms aup;
279 struct timeval now;
280 XDR xdrs;
281 int stat;
282
283 if (auth->ah_cred.oa_base == au->au_origcred.oa_base)
284 {
285 /* there is no hope. Punt */
286 return FALSE;
287 }
288 au->au_shfaults++;
289
290 /* first deserialize the creds back into a struct authunix_parms */
291 aup.aup_machname = NULL;
292 aup.aup_gids = (gid_t *) NULL;
293 xdrmem_create (&xdrs, au->au_origcred.oa_base,
294 au->au_origcred.oa_length, XDR_DECODE);
295 stat = xdr_authunix_parms (&xdrs, &aup);
296 if (!stat)
297 goto done;
298
299 /* update the time and serialize in place */
300 (void) __gettimeofday (&now, (struct timezone *) 0);
301 aup.aup_time = now.tv_sec;
302 xdrs.x_op = XDR_ENCODE;
303 XDR_SETPOS (&xdrs, 0);
304 stat = xdr_authunix_parms (&xdrs, &aup);
305 if (!stat)
306 goto done;
307 auth->ah_cred = au->au_origcred;
308 marshal_new_auth (auth);
309done:
310 /* free the struct authunix_parms created by deserializing */
311 xdrs.x_op = XDR_FREE;
312 (void) xdr_authunix_parms (&xdrs, &aup);
313 XDR_DESTROY (&xdrs);
314 return stat;
315}
316
317static void
318authunix_destroy (AUTH *auth)
319{
320 struct audata *au = AUTH_PRIVATE (auth);
321
322 mem_free (au->au_origcred.oa_base, au->au_origcred.oa_length);
323
324 if (au->au_shcred.oa_base != NULL)
325 mem_free (au->au_shcred.oa_base, au->au_shcred.oa_length);
326
327 mem_free (auth->ah_private, sizeof (struct audata));
328
329 if (auth->ah_verf.oa_base != NULL)
330 mem_free (auth->ah_verf.oa_base, auth->ah_verf.oa_length);
331
332 mem_free ((caddr_t) auth, sizeof (*auth));
333}
334
335/*
336 * Marshals (pre-serializes) an auth struct.
337 * sets private data, au_marshed and au_mpos
338 */
339static bool_t
340marshal_new_auth (AUTH *auth)
341{
342 XDR xdr_stream;
343 XDR *xdrs = &xdr_stream;
344 struct audata *au = AUTH_PRIVATE (auth);
345
346 xdrmem_create (xdrs, au->au_marshed, MAX_AUTH_BYTES, XDR_ENCODE);
347 if ((!xdr_opaque_auth (xdrs, &(auth->ah_cred))) ||
348 (!xdr_opaque_auth (xdrs, &(auth->ah_verf))))
349 perror (_("auth_unix.c: Fatal marshalling problem"));
350 else
351 au->au_mpos = XDR_GETPOS (xdrs);
352
353 XDR_DESTROY (xdrs);
354
355 return TRUE;
356}
357