aboutsummaryrefslogtreecommitdiff
path: root/utils/fakeauth.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/fakeauth.c')
-rw-r--r--utils/fakeauth.c434
1 files changed, 434 insertions, 0 deletions
diff --git a/utils/fakeauth.c b/utils/fakeauth.c
new file mode 100644
index 00000000..49fa7f1c
--- /dev/null
+++ b/utils/fakeauth.c
@@ -0,0 +1,434 @@
+/* fakeauth -- proxy auth server to lie to users about what their IDs are
+ Copyright (C) 2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <hurd.h>
+#include <hurd/auth.h>
+#include <hurd/interrupt.h>
+#include <hurd/ports.h>
+#include <idvec.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <argp.h>
+#include <error.h>
+#include "auth_S.h"
+#include "auth_request_U.h"
+#include "interrupt_S.h"
+
+/* Auth handles are server ports with sets of ids. */
+struct authhandle
+ {
+ struct port_info pi;
+ struct idvec euids, egids, auids, agids;
+ };
+
+struct port_bucket *auth_bucket;
+struct port_class *authhandle_portclass;
+
+/* Create a new auth port. */
+
+static error_t
+create_authhandle (struct authhandle **new)
+{
+ error_t err = ports_create_port (authhandle_portclass, auth_bucket,
+ sizeof **new, new);
+ if (! err)
+ bzero (&(*new)->euids, (void *) &(*new)[1] - (void *) &(*new)->euids);
+ return err;
+}
+
+/* Clean up a dead auth port. */
+
+static void
+destroy_authhandle (void *p)
+{
+ struct authhandle *h = p;
+ idvec_free_contents (&h->euids);
+ idvec_free_contents (&h->egids);
+ idvec_free_contents (&h->auids);
+ idvec_free_contents (&h->agids);
+}
+
+/* Called by server stub functions. */
+
+authhandle_t
+auth_port_to_handle (auth_t auth)
+{
+ return ports_lookup_port (auth_bucket, auth, authhandle_portclass);
+}
+
+/* id management. */
+
+static inline void
+idvec_copyout (struct idvec *idvec, uid_t **ids, size_t *nids)
+{
+ if (idvec->num > *nids)
+ *ids = idvec->ids;
+ else
+ memcpy (*ids, idvec->ids, idvec->num * sizeof *ids);
+ *nids = idvec->num;
+}
+
+#define C(auth, ids) idvec_copyout (&auth->ids, ids, n##ids)
+#define OUTIDS(auth) (C (auth, euids), C (auth, egids), \
+ C (auth, auids), C (auth, agids))
+
+/* Implement auth_getids as described in <hurd/auth.defs>. */
+kern_return_t
+S_auth_getids (struct authhandle *auth,
+ uid_t **euids,
+ size_t *neuids,
+ uid_t **auids,
+ size_t *nauids,
+ uid_t **egids,
+ size_t *negids,
+ uid_t **agids,
+ size_t *nagids)
+{
+ if (! auth)
+ return EOPNOTSUPP;
+
+ OUTIDS (auth);
+
+ return 0;
+}
+
+/* Implement auth_makeauth as described in <hurd/auth.defs>. */
+kern_return_t
+S_auth_makeauth (struct authhandle *auth,
+ mach_port_t *authpts, size_t nauths,
+ uid_t *euids, size_t neuids,
+ uid_t *auids, size_t nauids,
+ uid_t *egids, size_t negids,
+ uid_t *agids, size_t nagids,
+ mach_port_t *newhandle)
+{
+ struct authhandle *newauth, *auths[1 + nauths];
+ int hasroot = 0;
+ error_t err;
+ size_t i, j;
+
+ if (!auth)
+ return EOPNOTSUPP;
+
+ auths[0] = auth;
+
+ /* Fetch the auth structures for all the ports passed in. */
+ for (i = 0; i < nauths; i++)
+ auths[i + 1] = auth_port_to_handle (authpts[i]);
+
+ ++nauths;
+
+ /* Verify that the union of the handles passed in either contains euid 0
+ (root), or contains all the requested ids. */
+
+#define isuid(uid, auth) \
+ (idvec_contains (&(auth)->euids, uid) \
+ || idvec_contains (&(auth)->auids, uid))
+#define groupmember(gid, auth) \
+ (idvec_contains (&(auth)->egids, gid) \
+ || idvec_contains (&(auth)->agids, gid))
+#define isroot(auth) isuid (0, auth)
+
+ for (i = 0; i < nauths; i++)
+ if (auths[i] && isroot (auths[i]))
+ {
+ hasroot = 1;
+ break;
+ }
+
+ if (!hasroot)
+ {
+ int has_it;
+
+ for (i = 0; i < neuids; i++)
+ {
+ has_it = 0;
+ for (j = 0; j < nauths; j++)
+ if (auths[j] && isuid (euids[i], auths[j]))
+ {
+ has_it = 1;
+ break;
+ }
+ if (!has_it)
+ goto eperm;
+ }
+
+ for (i = 0; i < nauids; i++)
+ {
+ has_it = 0;
+ for (j = 0; j < nauths; j++)
+ if (auths[j] && isuid (auids[i], auths[j]))
+ {
+ has_it = 1;
+ break;
+ }
+ if (!has_it)
+ goto eperm;
+ }
+
+ for (i = 0; i < negids; i++)
+ {
+ has_it = 0;
+ for (j = 0; j < nauths; j++)
+ if (auths[j] && groupmember (egids[i], auths[j]))
+ {
+ has_it = 1;
+ break;
+ }
+ if (!has_it)
+ goto eperm;
+ }
+
+ for (i = 0; i < nagids; i++)
+ {
+ has_it = 0;
+ for (j = 0; j < nauths; j++)
+ if (auths[j] && groupmember (agids[i], auths[j]))
+ {
+ has_it = 1;
+ break;
+ }
+ if (!has_it)
+ goto eperm;
+ }
+ }
+
+ err = create_authhandle (&newauth);
+
+ /* Create a new handle with the specified ids. */
+
+#define MERGE S (euids); S (egids); S (auids); S (agids);
+#define S(uids) if (!err) err = idvec_merge_ids (&newauth->uids, uids, n##uids)
+
+ MERGE;
+
+#undef S
+
+ if (! err)
+ {
+ for (j = 1; j < nauths; ++j)
+ mach_port_deallocate (mach_task_self (), authpts[j - 1]);
+ *newhandle = ports_get_right (newauth);
+ ports_port_deref (newauth);
+ }
+
+ for (j = 1; j < nauths; j++)
+ if (auths[j])
+ ports_port_deref (auths[j]);
+ return err;
+
+ eperm:
+ for (j = 1; j < nauths; j++)
+ if (auths[j])
+ ports_port_deref (auths[j]);
+ return EPERM;
+}
+
+
+/* This is our original auth port, where we will send all proxied
+ authentication requests. */
+static auth_t real_auth_port;
+
+kern_return_t
+S_auth_user_authenticate (struct authhandle *userauth,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mach_port_t rendezvous,
+ mach_port_t *newport,
+ mach_msg_type_name_t *newporttype)
+{
+ if (! userauth)
+ return EOPNOTSUPP;
+
+ if (rendezvous == MACH_PORT_DEAD) /* Port died in transit. */
+ return EINVAL;
+
+ return auth_user_authenticate_request (real_auth_port, reply, reply_type,
+ rendezvous, MACH_MSG_TYPE_MOVE_SEND)
+ ? : MIG_NO_REPLY;
+}
+
+kern_return_t
+S_auth_server_authenticate (struct authhandle *serverauth,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mach_port_t rendezvous,
+ mach_port_t newport,
+ mach_msg_type_name_t newport_type,
+ uid_t **euids,
+ size_t *neuids,
+ uid_t **auids,
+ size_t *nauids,
+ uid_t **egids,
+ size_t *negids,
+ uid_t **agids,
+ size_t *nagids)
+{
+ if (! serverauth)
+ return EOPNOTSUPP;
+
+ if (rendezvous == MACH_PORT_DEAD) /* Port died in transit. */
+ return EINVAL;
+
+ return auth_server_authenticate_request (real_auth_port,
+ reply, reply_type,
+ rendezvous, MACH_MSG_TYPE_MOVE_SEND,
+ newport, newport_type)
+ ? : MIG_NO_REPLY;
+}
+
+kern_return_t
+S_interrupt_operation (mach_port_t port, mach_port_seqno_t seqno)
+{
+ return interrupt_operation (real_auth_port, 0);
+}
+
+static int
+auth_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ extern int auth_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+ extern int interrupt_server (mach_msg_header_t *inp, mach_msg_header_t *);
+ return (auth_server (inp, outp) ||
+ interrupt_server (inp, outp) ||
+ ports_notify_server (inp, outp));
+}
+
+
+static any_t
+handle_auth_requests (any_t ignored)
+{
+ while (1)
+ ports_manage_port_operations_multithread (auth_bucket, auth_demuxer,
+ 30 * 1000, 0, 0);
+ return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ struct authhandle *firstauth;
+ auth_t authport;
+ pid_t child;
+ int status;
+ int argi;
+
+ /* Parse our options. */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ return EINVAL;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = { 0, parse_opt, "COMMAND...", "\
+Run COMMAND with a fake authentication handle that claims to be root or \
+any arbitrary identity derived from that handle, but in fact is always just \
+a proxy for your real authentication handle. This means that all processes \
+created by the COMMAND will have your privileges, even though it may \
+believe it has restricted them to different identities or no identity at all.\
+" };
+
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &argi, 0);
+
+ auth_bucket = ports_create_bucket ();
+ authhandle_portclass = ports_create_class (&destroy_authhandle, 0);
+
+ /* Create the initial root auth handle. */
+ err = create_authhandle (&firstauth);
+ assert_perror (err);
+ idvec_add (&firstauth->euids, 0);
+ idvec_add (&firstauth->auids, 0);
+ idvec_add (&firstauth->auids, 0);
+ idvec_merge (&firstauth->egids, &firstauth->euids);
+ idvec_merge (&firstauth->agids, &firstauth->auids);
+
+ /* Get a send right for it. */
+ authport = ports_get_right (firstauth);
+ err = mach_port_insert_right (mach_task_self (), authport, authport,
+ MACH_MSG_TYPE_MAKE_SEND);
+ assert_perror (err);
+ ports_port_deref (firstauth);
+
+ /* Stash our original auth port for later use. */
+ real_auth_port = getauth ();
+
+ /* Start handling auth requests on our fake handles. */
+ cthread_detach (cthread_fork (&handle_auth_requests, (any_t)0));
+
+ /* Now we start faking ourselves out. This will immediately
+ reauthenticate all our descriptors through our proxy auth port.
+ The POSIXoid calls we make below will continue to use the fake
+ port and pass it on to the child. */
+ if (setauth (authport))
+ error (2, errno, "Cannot switch to fake auth handle");
+ mach_port_deallocate (mach_task_self (), authport);
+
+ /* We cannot use fork because it doesn't do the right thing with our send
+ rights that point to our own receive rights, i.e. the new auth port.
+ Since posix_spawn might be implemented with fork (prior to glibc 2.3),
+ we cannot use that simple interface either. We use _hurd_exec
+ directly to effect what posix_spawn does in the simple case. */
+ {
+ task_t newtask;
+ process_t proc;
+ file_t execfile = file_name_lookup (argv[argi], O_EXEC, 0);
+ if (execfile == MACH_PORT_NULL)
+ error (3, errno, "%s", argv[argi]);
+
+ err = task_create (mach_task_self (),
+#ifdef KERN_INVALID_LEDGER
+ NULL, 0, /* OSF Mach */
+#endif
+ 0, &newtask);
+ if (err)
+ error (3, err, "cannot create child task");
+ child = task2pid (newtask);
+ if (child < 0)
+ error (3, errno, "task2pid");
+ proc = getproc ();
+ err = proc_child (proc, newtask);
+ mach_port_deallocate (mach_task_self (), proc);
+ if (err)
+ error (3, err, "proc_child");
+
+ err = _hurd_exec (newtask, execfile, &argv[argi], environ);
+ mach_port_deallocate (mach_task_self (), newtask);
+ mach_port_deallocate (mach_task_self (), execfile);
+ if (err)
+ error (3, err, "cannot execute %s", argv[argi]);
+ }
+
+ if (waitpid (child, &status, 0) != child)
+ error (4, errno, "waitpid on %d", child);
+
+ if (WIFSIGNALED (status))
+ error (WTERMSIG (status) + 128, 0,
+ "%s for child %d", strsignal (WTERMSIG (status)), child);
+ if (WEXITSTATUS (status) != 0)
+ error (WEXITSTATUS (status), 0,
+ "Error %d for child %d", WEXITSTATUS (status), child);
+
+ return 0;
+}