Imported Upstream version 0.2.5
[platform/upstream/libtirpc.git] / src / auth_unix.c
1 /*
2  * Copyright (c) 2009, Sun Microsystems, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * - Redistributions of source code must retain the above copyright notice,
8  *   this list of conditions and the following disclaimer.
9  * - Redistributions in binary form must reproduce the above copyright notice,
10  *   this list of conditions and the following disclaimer in the documentation
11  *   and/or other materials provided with the distribution.
12  * - Neither the name of Sun Microsystems, Inc. nor the names of its
13  *   contributors may be used to endorse or promote products derived
14  *   from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30
31 /*
32  * auth_unix.c, Implements UNIX style authentication parameters.
33  *
34  * Copyright (C) 1984, Sun Microsystems, Inc.
35  *
36  * The system is very weak.  The client uses no encryption for it's
37  * credentials and only sends null verifiers.  The server sends backs
38  * null verifiers or optionally a verifier that suggests a new short hand
39  * for the credentials.
40  *
41  */
42 #include <pthread.h>
43 #include <reentrant.h>
44 #include <sys/param.h>
45
46 #include <assert.h>
47 #include <err.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51 #include <string.h>
52 #include <errno.h>
53
54 #include <rpc/clnt.h>
55 #include <rpc/types.h>
56 #include <rpc/xdr.h>
57 #include <rpc/auth.h>
58 #include <rpc/auth_unix.h>
59
60 /* auth_unix.c */
61 static void authunix_nextverf (AUTH *);
62 static bool_t authunix_marshal (AUTH *, XDR *);
63 static bool_t authunix_validate (AUTH *, struct opaque_auth *);
64 static bool_t authunix_refresh (AUTH *, void *);
65 static void authunix_destroy (AUTH *);
66 static void marshal_new_auth (AUTH *);
67 static struct auth_ops *authunix_ops (void);
68
69 /*
70  * This struct is pointed to by the ah_private field of an auth_handle.
71  */
72 struct audata {
73         struct opaque_auth      au_origcred;    /* original credentials */
74         struct opaque_auth      au_shcred;      /* short hand cred */
75         u_long                  au_shfaults;    /* short hand cache faults */
76         char                    au_marshed[MAX_AUTH_BYTES];
77         u_int                   au_mpos;        /* xdr pos at end of marshed */
78 };
79 #define AUTH_PRIVATE(auth)      ((struct audata *)auth->ah_private)
80
81 /*
82  * Create a unix style authenticator.
83  * Returns an auth handle with the given stuff in it.
84  */
85 AUTH *
86 authunix_create(machname, uid, gid, len, aup_gids)
87         char *machname;
88         uid_t uid;
89         gid_t gid;
90         int len;
91         gid_t *aup_gids;
92 {
93         struct authunix_parms aup;
94         char mymem[MAX_AUTH_BYTES];
95         struct timeval now;
96         XDR xdrs;
97         AUTH *auth;
98         struct audata *au;
99
100         memset(&rpc_createerr, 0, sizeof(rpc_createerr));
101
102         /*
103          * Allocate and set up auth handle
104          */
105         au = NULL;
106         auth = mem_alloc(sizeof(*auth));
107 #ifndef _KERNEL
108         if (auth == NULL) {
109                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
110                 rpc_createerr.cf_error.re_errno = ENOMEM;
111                 goto cleanup_authunix_create;
112         }
113 #endif
114         au = mem_alloc(sizeof(*au));
115 #ifndef _KERNEL
116         if (au == NULL) {
117                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
118                 rpc_createerr.cf_error.re_errno = ENOMEM;
119                 goto cleanup_authunix_create;
120         }
121 #endif
122         auth->ah_ops = authunix_ops();
123         auth->ah_private = (caddr_t)au;
124         auth->ah_verf = au->au_shcred = _null_auth;
125         au->au_shfaults = 0;
126         au->au_origcred.oa_base = NULL;
127
128         /*
129          * fill in param struct from the given params
130          */
131         (void)gettimeofday(&now, NULL);
132         aup.aup_time = now.tv_sec;
133         aup.aup_machname = machname;
134         aup.aup_uid = uid;
135         aup.aup_gid = gid;
136         aup.aup_len = (u_int)len;
137         aup.aup_gids = aup_gids;
138
139         /*
140          * Serialize the parameters into origcred
141          */
142         xdrmem_create(&xdrs, mymem, MAX_AUTH_BYTES, XDR_ENCODE);
143         if (!xdr_authunix_parms(&xdrs, &aup)) {
144                 rpc_createerr.cf_stat = RPC_CANTENCODEARGS;
145                 goto cleanup_authunix_create;
146         }
147         au->au_origcred.oa_length = len = XDR_GETPOS(&xdrs);
148         au->au_origcred.oa_flavor = AUTH_UNIX;
149 #ifdef _KERNEL
150         au->au_origcred.oa_base = mem_alloc((u_int) len);
151 #else
152         if ((au->au_origcred.oa_base = mem_alloc((u_int) len)) == NULL) {
153                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
154                 rpc_createerr.cf_error.re_errno = ENOMEM;
155                 goto cleanup_authunix_create;
156         }
157 #endif
158         memmove(au->au_origcred.oa_base, mymem, (size_t)len);
159
160         /*
161          * set auth handle to reflect new cred.
162          */
163         auth->ah_cred = au->au_origcred;
164         marshal_new_auth(auth);
165         auth_get(auth);         /* Reference for caller */
166         return (auth);
167 #ifndef _KERNEL
168  cleanup_authunix_create:
169         if (auth)
170                 mem_free(auth, sizeof(*auth));
171         if (au) {
172                 if (au->au_origcred.oa_base)
173                         mem_free(au->au_origcred.oa_base, (u_int)len);
174                 mem_free(au, sizeof(*au));
175         }
176         return (NULL);
177 #endif
178 }
179
180 /*
181  * Returns an auth handle with parameters determined by doing lots of
182  * syscalls.
183  */
184 AUTH *
185 authunix_create_default()
186 {
187         int len;
188         char machname[MAXHOSTNAMELEN + 1];
189         uid_t uid;
190         gid_t gid, *gids;
191         AUTH *result;
192
193         memset(&rpc_createerr, 0, sizeof(rpc_createerr));
194
195         if (gethostname(machname, sizeof machname) == -1) {
196                 rpc_createerr.cf_error.re_errno = errno;
197                 goto out_err;
198         }
199         machname[sizeof(machname) - 1] = 0;
200         uid = geteuid();
201         gid = getegid();
202
203         /* According to glibc comments, an intervening setgroups(2)
204          * call can increase the number of supplemental groups between
205          * these two getgroups(2) calls. */
206 retry:
207         len = getgroups(0, NULL);
208         if (len == -1) {
209                 rpc_createerr.cf_error.re_errno = errno;
210                 goto out_err;
211         }
212
213         /* Bump allocation size.  A zero allocation size may result in a
214          * NULL calloc(3) result, which is not reliably distinguishable
215          * from a memory allocation error. */
216         gids = calloc(len + 1, sizeof(gid_t));
217         if (gids == NULL) {
218                 rpc_createerr.cf_error.re_errno = ENOMEM;
219                 goto out_err;
220         }
221
222         len = getgroups(len, gids);
223         if (len == -1) {
224                 rpc_createerr.cf_error.re_errno = errno;
225                 free(gids);
226                 if (rpc_createerr.cf_error.re_errno == EINVAL) {
227                         rpc_createerr.cf_error.re_errno = 0;
228                         goto retry;
229                 }
230                 goto out_err;
231         }
232
233         /*
234          * AUTH_UNIX sends on the wire only the first NGRPS groups in the
235          * supplemental groups list.
236          */
237         if (len > NGRPS)
238                 len = NGRPS;
239
240         /* XXX: interface problem; those should all have been unsigned */
241         result = authunix_create(machname, uid, gid, len, gids);
242         free(gids);
243         return result;
244
245 out_err:
246         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
247         return NULL;
248 }
249
250 /*
251  * authunix operations
252  */
253
254 /* ARGSUSED */
255 static void
256 authunix_nextverf(auth)
257         AUTH *auth;
258 {
259         /* no action necessary */
260 }
261
262 static bool_t
263 authunix_marshal(auth, xdrs)
264         AUTH *auth;
265         XDR *xdrs;
266 {
267         struct audata *au;
268
269         assert(auth != NULL);
270         assert(xdrs != NULL);
271
272         au = AUTH_PRIVATE(auth);
273         return (XDR_PUTBYTES(xdrs, au->au_marshed, au->au_mpos));
274 }
275
276 static bool_t
277 authunix_validate(auth, verf)
278         AUTH *auth;
279         struct opaque_auth *verf;
280 {
281         struct audata *au;
282         XDR xdrs;
283
284         assert(auth != NULL);
285         assert(verf != NULL);
286
287         if (verf->oa_flavor == AUTH_SHORT) {
288                 au = AUTH_PRIVATE(auth);
289                 xdrmem_create(&xdrs, verf->oa_base, verf->oa_length,
290                     XDR_DECODE);
291
292                 if (au->au_shcred.oa_base != NULL) {
293                         mem_free(au->au_shcred.oa_base,
294                             au->au_shcred.oa_length);
295                         au->au_shcred.oa_base = NULL;
296                 }
297                 if (xdr_opaque_auth(&xdrs, &au->au_shcred)) {
298                         auth->ah_cred = au->au_shcred;
299                 } else {
300                         xdrs.x_op = XDR_FREE;
301                         (void)xdr_opaque_auth(&xdrs, &au->au_shcred);
302                         au->au_shcred.oa_base = NULL;
303                         auth->ah_cred = au->au_origcred;
304                 }
305                 marshal_new_auth(auth);
306         }
307         return (TRUE);
308 }
309
310 static bool_t
311 authunix_refresh(AUTH *auth, void *dummy)
312 {
313         struct audata *au = AUTH_PRIVATE(auth);
314         struct authunix_parms aup;
315         struct timeval now;
316         XDR xdrs;
317         int stat;
318
319         assert(auth != NULL);
320
321         if (auth->ah_cred.oa_base == au->au_origcred.oa_base) {
322                 /* there is no hope.  Punt */
323                 return (FALSE);
324         }
325         au->au_shfaults ++;
326
327         /* first deserialize the creds back into a struct authunix_parms */
328         aup.aup_machname = NULL;
329         aup.aup_gids = NULL;
330         xdrmem_create(&xdrs, au->au_origcred.oa_base,
331             au->au_origcred.oa_length, XDR_DECODE);
332         stat = xdr_authunix_parms(&xdrs, &aup);
333         if (! stat)
334                 goto done;
335
336         /* update the time and serialize in place */
337         (void)gettimeofday(&now, NULL);
338         aup.aup_time = now.tv_sec;
339         xdrs.x_op = XDR_ENCODE;
340         XDR_SETPOS(&xdrs, 0);
341         stat = xdr_authunix_parms(&xdrs, &aup);
342         if (! stat)
343                 goto done;
344         auth->ah_cred = au->au_origcred;
345         marshal_new_auth(auth);
346 done:
347         /* free the struct authunix_parms created by deserializing */
348         xdrs.x_op = XDR_FREE;
349         (void)xdr_authunix_parms(&xdrs, &aup);
350         XDR_DESTROY(&xdrs);
351         return (stat);
352 }
353
354 static void
355 authunix_destroy(auth)
356         AUTH *auth;
357 {
358         struct audata *au;
359
360         assert(auth != NULL);
361
362         au = AUTH_PRIVATE(auth);
363         mem_free(au->au_origcred.oa_base, au->au_origcred.oa_length);
364
365         if (au->au_shcred.oa_base != NULL)
366                 mem_free(au->au_shcred.oa_base, au->au_shcred.oa_length);
367
368         mem_free(auth->ah_private, sizeof(struct audata));
369
370         if (auth->ah_verf.oa_base != NULL)
371                 mem_free(auth->ah_verf.oa_base, auth->ah_verf.oa_length);
372
373         mem_free(auth, sizeof(*auth));
374 }
375
376 /*
377  * Marshals (pre-serializes) an auth struct.
378  * sets private data, au_marshed and au_mpos
379  */
380 static void
381 marshal_new_auth(auth)
382         AUTH *auth;
383 {
384         XDR     xdr_stream;
385         XDR     *xdrs = &xdr_stream;
386         struct audata *au;
387
388         assert(auth != NULL);
389
390         au = AUTH_PRIVATE(auth);
391         xdrmem_create(xdrs, au->au_marshed, MAX_AUTH_BYTES, XDR_ENCODE);
392         if ((! xdr_opaque_auth(xdrs, &(auth->ah_cred))) ||
393             (! xdr_opaque_auth(xdrs, &(auth->ah_verf))))
394                 warnx("auth_none.c - Fatal marshalling problem");
395         else
396                 au->au_mpos = XDR_GETPOS(xdrs);
397         XDR_DESTROY(xdrs);
398 }
399
400 static bool_t
401 authunix_wrap(AUTH *auth, XDR *xdrs, xdrproc_t xfunc, caddr_t xwhere)
402 {
403         return ((*xfunc)(xdrs, xwhere));
404 }
405
406 static struct auth_ops *
407 authunix_ops()
408 {
409         static struct auth_ops ops;
410         extern mutex_t ops_lock;
411
412         /* VARIABLES PROTECTED BY ops_lock: ops */
413
414         mutex_lock(&ops_lock);
415         if (ops.ah_nextverf == NULL) {
416                 ops.ah_nextverf = authunix_nextverf;
417                 ops.ah_marshal = authunix_marshal;
418                 ops.ah_validate = authunix_validate;
419                 ops.ah_refresh = authunix_refresh;
420                 ops.ah_destroy = authunix_destroy;
421                 ops.ah_wrap = authunix_wrap;
422                 ops.ah_unwrap = authunix_wrap;
423         }
424         mutex_unlock(&ops_lock);
425         return (&ops);
426 }