|  | /* | 
|  | * Copyright (c) 2010, 2011, Oracle America, Inc. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions are | 
|  | * met: | 
|  | * | 
|  | *     * Redistributions of source code must retain the above copyright | 
|  | *       notice, this list of conditions and the following disclaimer. | 
|  | *     * Redistributions in binary form must reproduce the above | 
|  | *       copyright notice, this list of conditions and the following | 
|  | *       disclaimer in the documentation and/or other materials | 
|  | *       provided with the distribution. | 
|  | *     * Neither the name of the "Oracle America, Inc." nor the names of its | 
|  | *       contributors may be used to endorse or promote products derived | 
|  | *       from this software without specific prior written permission. | 
|  | * | 
|  | *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
|  | *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
|  | *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | 
|  | *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | 
|  | *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | 
|  | *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE | 
|  | *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | 
|  | *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | 
|  | *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | 
|  | *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  | /* | 
|  | * auth_unix.c, Implements UNIX style authentication parameters. | 
|  | * | 
|  | * The system is very weak.  The client uses no encryption for it's | 
|  | * credentials and only sends null verifiers.  The server sends backs | 
|  | * null verifiers or optionally a verifier that suggests a new short hand | 
|  | * for the credentials. | 
|  | */ | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <limits.h> | 
|  | #include <stdbool.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <unistd.h> | 
|  | #include <libintl.h> | 
|  | #include <sys/param.h> | 
|  | #include <wchar.h> | 
|  |  | 
|  | #include <rpc/types.h> | 
|  | #include <rpc/xdr.h> | 
|  | #include <rpc/auth.h> | 
|  | #include <rpc/auth_unix.h> | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Unix authenticator operations vector | 
|  | */ | 
|  | static void authunix_nextverf (AUTH *); | 
|  | static bool_t authunix_marshal (AUTH *, XDR *); | 
|  | static bool_t authunix_validate (AUTH *, struct opaque_auth *); | 
|  | static bool_t authunix_refresh (AUTH *); | 
|  | static void authunix_destroy (AUTH *); | 
|  |  | 
|  | static const struct auth_ops auth_unix_ops = { | 
|  | authunix_nextverf, | 
|  | authunix_marshal, | 
|  | authunix_validate, | 
|  | authunix_refresh, | 
|  | authunix_destroy | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * This struct is pointed to by the ah_private field of an auth_handle. | 
|  | */ | 
|  | struct audata { | 
|  | struct opaque_auth au_origcred;	/* original credentials */ | 
|  | struct opaque_auth au_shcred;	/* short hand cred */ | 
|  | u_long au_shfaults;		/* short hand cache faults */ | 
|  | char au_marshed[MAX_AUTH_BYTES]; | 
|  | u_int au_mpos;		/* xdr pos at end of marshed */ | 
|  | }; | 
|  | #define	AUTH_PRIVATE(auth)	((struct audata *)auth->ah_private) | 
|  |  | 
|  | static bool_t marshal_new_auth (AUTH *) internal_function; | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Create a unix style authenticator. | 
|  | * Returns an auth handle with the given stuff in it. | 
|  | */ | 
|  | AUTH * | 
|  | authunix_create (char *machname, uid_t uid, gid_t gid, int len, | 
|  | gid_t *aup_gids) | 
|  | { | 
|  | struct authunix_parms aup; | 
|  | char mymem[MAX_AUTH_BYTES]; | 
|  | struct timeval now; | 
|  | XDR xdrs; | 
|  | AUTH *auth; | 
|  | struct audata *au; | 
|  |  | 
|  | /* | 
|  | * Allocate and set up auth handle | 
|  | */ | 
|  | auth = (AUTH *) mem_alloc (sizeof (*auth)); | 
|  | au = (struct audata *) mem_alloc (sizeof (*au)); | 
|  | if (auth == NULL || au == NULL) | 
|  | { | 
|  | no_memory: | 
|  | (void) __fxprintf (NULL, "%s: %s", __func__, _("out of memory\n")); | 
|  | mem_free (auth, sizeof (*auth)); | 
|  | mem_free (au, sizeof (*au)); | 
|  | return NULL; | 
|  | } | 
|  | auth->ah_ops = (struct auth_ops *) &auth_unix_ops; | 
|  | auth->ah_private = (caddr_t) au; | 
|  | auth->ah_verf = au->au_shcred = _null_auth; | 
|  | au->au_shfaults = 0; | 
|  |  | 
|  | /* | 
|  | * fill in param struct from the given params | 
|  | */ | 
|  | (void) __gettimeofday (&now, (struct timezone *) 0); | 
|  | aup.aup_time = now.tv_sec; | 
|  | aup.aup_machname = machname; | 
|  | aup.aup_uid = uid; | 
|  | aup.aup_gid = gid; | 
|  | aup.aup_len = (u_int) len; | 
|  | aup.aup_gids = aup_gids; | 
|  |  | 
|  | /* | 
|  | * Serialize the parameters into origcred | 
|  | */ | 
|  | xdrmem_create (&xdrs, mymem, MAX_AUTH_BYTES, XDR_ENCODE); | 
|  | if (!xdr_authunix_parms (&xdrs, &aup)) | 
|  | abort (); | 
|  | au->au_origcred.oa_length = len = XDR_GETPOS (&xdrs); | 
|  | au->au_origcred.oa_flavor = AUTH_UNIX; | 
|  | au->au_origcred.oa_base = mem_alloc ((u_int) len); | 
|  | if (au->au_origcred.oa_base == NULL) | 
|  | goto no_memory; | 
|  | memcpy(au->au_origcred.oa_base, mymem, (u_int) len); | 
|  |  | 
|  | /* | 
|  | * set auth handle to reflect new cred. | 
|  | */ | 
|  | auth->ah_cred = au->au_origcred; | 
|  | marshal_new_auth (auth); | 
|  | return auth; | 
|  | } | 
|  | libc_hidden_nolink_sunrpc (authunix_create, GLIBC_2_0) | 
|  |  | 
|  | /* | 
|  | * Returns an auth handle with parameters determined by doing lots of | 
|  | * syscalls. | 
|  | */ | 
|  | AUTH * | 
|  | authunix_create_default (void) | 
|  | { | 
|  | char machname[MAX_MACHINE_NAME + 1]; | 
|  |  | 
|  | if (__gethostname (machname, MAX_MACHINE_NAME) == -1) | 
|  | abort (); | 
|  | machname[MAX_MACHINE_NAME] = 0; | 
|  | uid_t uid = __geteuid (); | 
|  | gid_t gid = __getegid (); | 
|  |  | 
|  | int max_nr_groups; | 
|  | /* When we have to try a second time, do not use alloca() again.  We | 
|  | might have reached the stack limit already.  */ | 
|  | bool retry = false; | 
|  | again: | 
|  | /* Ask the kernel how many groups there are exactly.  Note that we | 
|  | might have to redo all this if the number of groups has changed | 
|  | between the two calls.  */ | 
|  | max_nr_groups = __getgroups (0, NULL); | 
|  |  | 
|  | /* Just some random reasonable stack limit.  */ | 
|  | #define ALLOCA_LIMIT (1024 / sizeof (gid_t)) | 
|  | gid_t *gids = NULL; | 
|  | if (max_nr_groups < ALLOCA_LIMIT && ! retry) | 
|  | gids = (gid_t *) alloca (max_nr_groups * sizeof (gid_t)); | 
|  | else | 
|  | { | 
|  | gids = (gid_t *) malloc (max_nr_groups * sizeof (gid_t)); | 
|  | if (gids == NULL) | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | int len = __getgroups (max_nr_groups, gids); | 
|  | if (len == -1) | 
|  | { | 
|  | if (errno == EINVAL) | 
|  | { | 
|  | /* New groups added in the meantime.  Try again.  */ | 
|  | if (max_nr_groups >= ALLOCA_LIMIT || retry) | 
|  | free (gids); | 
|  | retry = true; | 
|  | goto again; | 
|  | } | 
|  | /* No other error can happen.  */ | 
|  | abort (); | 
|  | } | 
|  |  | 
|  | /* This braindamaged Sun code forces us here to truncate the | 
|  | list of groups to NGRPS members since the code in | 
|  | authuxprot.c transforms a fixed array.  Grrr.  */ | 
|  | AUTH *result = authunix_create (machname, uid, gid, MIN (NGRPS, len), gids); | 
|  |  | 
|  | if (max_nr_groups >= ALLOCA_LIMIT || retry) | 
|  | free (gids); | 
|  |  | 
|  | return result; | 
|  | } | 
|  | #ifdef EXPORT_RPC_SYMBOLS | 
|  | libc_hidden_def (authunix_create_default) | 
|  | #else | 
|  | libc_hidden_nolink_sunrpc (authunix_create_default, GLIBC_2_0) | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * authunix operations | 
|  | */ | 
|  |  | 
|  | static void | 
|  | authunix_nextverf (AUTH *auth) | 
|  | { | 
|  | /* no action necessary */ | 
|  | } | 
|  |  | 
|  | static bool_t | 
|  | authunix_marshal (AUTH *auth, XDR *xdrs) | 
|  | { | 
|  | struct audata *au = AUTH_PRIVATE (auth); | 
|  |  | 
|  | return XDR_PUTBYTES (xdrs, au->au_marshed, au->au_mpos); | 
|  | } | 
|  |  | 
|  | static bool_t | 
|  | authunix_validate (AUTH *auth, struct opaque_auth *verf) | 
|  | { | 
|  | struct audata *au; | 
|  | XDR xdrs; | 
|  |  | 
|  | if (verf->oa_flavor == AUTH_SHORT) | 
|  | { | 
|  | au = AUTH_PRIVATE (auth); | 
|  | xdrmem_create (&xdrs, verf->oa_base, verf->oa_length, XDR_DECODE); | 
|  |  | 
|  | if (au->au_shcred.oa_base != NULL) | 
|  | { | 
|  | mem_free (au->au_shcred.oa_base, | 
|  | au->au_shcred.oa_length); | 
|  | au->au_shcred.oa_base = NULL; | 
|  | } | 
|  | if (xdr_opaque_auth (&xdrs, &au->au_shcred)) | 
|  | { | 
|  | auth->ah_cred = au->au_shcred; | 
|  | } | 
|  | else | 
|  | { | 
|  | xdrs.x_op = XDR_FREE; | 
|  | (void) xdr_opaque_auth (&xdrs, &au->au_shcred); | 
|  | au->au_shcred.oa_base = NULL; | 
|  | auth->ah_cred = au->au_origcred; | 
|  | } | 
|  | marshal_new_auth (auth); | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | static bool_t | 
|  | authunix_refresh (AUTH *auth) | 
|  | { | 
|  | struct audata *au = AUTH_PRIVATE (auth); | 
|  | struct authunix_parms aup; | 
|  | struct timeval now; | 
|  | XDR xdrs; | 
|  | int stat; | 
|  |  | 
|  | if (auth->ah_cred.oa_base == au->au_origcred.oa_base) | 
|  | { | 
|  | /* there is no hope.  Punt */ | 
|  | return FALSE; | 
|  | } | 
|  | au->au_shfaults++; | 
|  |  | 
|  | /* first deserialize the creds back into a struct authunix_parms */ | 
|  | aup.aup_machname = NULL; | 
|  | aup.aup_gids = (gid_t *) NULL; | 
|  | xdrmem_create (&xdrs, au->au_origcred.oa_base, | 
|  | au->au_origcred.oa_length, XDR_DECODE); | 
|  | stat = xdr_authunix_parms (&xdrs, &aup); | 
|  | if (!stat) | 
|  | goto done; | 
|  |  | 
|  | /* update the time and serialize in place */ | 
|  | (void) __gettimeofday (&now, (struct timezone *) 0); | 
|  | aup.aup_time = now.tv_sec; | 
|  | xdrs.x_op = XDR_ENCODE; | 
|  | XDR_SETPOS (&xdrs, 0); | 
|  | stat = xdr_authunix_parms (&xdrs, &aup); | 
|  | if (!stat) | 
|  | goto done; | 
|  | auth->ah_cred = au->au_origcred; | 
|  | marshal_new_auth (auth); | 
|  | done: | 
|  | /* free the struct authunix_parms created by deserializing */ | 
|  | xdrs.x_op = XDR_FREE; | 
|  | (void) xdr_authunix_parms (&xdrs, &aup); | 
|  | XDR_DESTROY (&xdrs); | 
|  | return stat; | 
|  | } | 
|  |  | 
|  | static void | 
|  | authunix_destroy (AUTH *auth) | 
|  | { | 
|  | struct audata *au = AUTH_PRIVATE (auth); | 
|  |  | 
|  | mem_free (au->au_origcred.oa_base, au->au_origcred.oa_length); | 
|  |  | 
|  | if (au->au_shcred.oa_base != NULL) | 
|  | mem_free (au->au_shcred.oa_base, au->au_shcred.oa_length); | 
|  |  | 
|  | mem_free (auth->ah_private, sizeof (struct audata)); | 
|  |  | 
|  | if (auth->ah_verf.oa_base != NULL) | 
|  | mem_free (auth->ah_verf.oa_base, auth->ah_verf.oa_length); | 
|  |  | 
|  | mem_free ((caddr_t) auth, sizeof (*auth)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Marshals (pre-serializes) an auth struct. | 
|  | * sets private data, au_marshed and au_mpos | 
|  | */ | 
|  | static bool_t | 
|  | internal_function | 
|  | marshal_new_auth (AUTH *auth) | 
|  | { | 
|  | XDR xdr_stream; | 
|  | XDR *xdrs = &xdr_stream; | 
|  | struct audata *au = AUTH_PRIVATE (auth); | 
|  |  | 
|  | xdrmem_create (xdrs, au->au_marshed, MAX_AUTH_BYTES, XDR_ENCODE); | 
|  | if ((!xdr_opaque_auth (xdrs, &(auth->ah_cred))) || | 
|  | (!xdr_opaque_auth (xdrs, &(auth->ah_verf)))) | 
|  | perror (_("auth_unix.c: Fatal marshalling problem")); | 
|  | else | 
|  | au->au_mpos = XDR_GETPOS (xdrs); | 
|  |  | 
|  | XDR_DESTROY (xdrs); | 
|  |  | 
|  | return TRUE; | 
|  | } |