summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libdiskfs/file-exec.c502
1 files changed, 154 insertions, 348 deletions
diff --git a/libdiskfs/file-exec.c b/libdiskfs/file-exec.c
index 8bbf5012..97976bf4 100644
--- a/libdiskfs/file-exec.c
+++ b/libdiskfs/file-exec.c
@@ -25,6 +25,108 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <fcntlbits.h>
#include <hurd/exec.h>
#include <hurd/paths.h>
+#include <string.h>
+
+static int
+scan_ids (uid_t *set, int setlen, uid_t test)
+{
+ int i;
+ for (i = 0; i < setlen; i++)
+ if (set[i] == test)
+ return 1;
+ return 0;
+}
+
+/* Adds ID to the sets OLDGENIDS & OLDAUXIDS, and returns the new set in
+ GENIDS and AUXIDS, which are malloced. SECURE is also updated to reflect
+ whether a secure exec is called for. ENOMEM is returned if a malloc
+ fails, otherwise 0. */
+static error_t
+setid (uid_t id, int *secure,
+ uid_t *oldgenids, size_t noldgenids,
+ uid_t *oldauxids, size_t noldauxids,
+ uid_t **genids, size_t *ngenids,
+ uid_t **auxids, size_t *nauxids)
+{
+ /* We are dumping the current first id; put it
+ into the auxids array. This is complex. The different
+ cases below are intended to make sure that we don't
+ lose any ids (unlike posix) and to make sure that aux ids
+ zero and one (if already set) behave like the posix
+ ones. */
+
+#define MALLOC(n) ({ void *_p = malloc(n); if (! _p) return ENOMEM; _p; })
+
+ if (noldauxids == 0)
+ {
+ *auxids = MALLOC (*nauxids = 1);
+ (*auxids)[0] = oldgenids[0];
+ }
+ else if (noldauxids == 1)
+ {
+ *auxids = MALLOC (*nauxids = 2);
+ (*auxids)[0] = oldauxids[0];
+ (*auxids)[1] = oldgenids[0];
+ }
+ else if (noldauxids == 2)
+ {
+ if (oldgenids[0] == oldauxids[1])
+ {
+ *auxids = MALLOC (*nauxids = 2);
+ (*auxids)[0] = oldauxids[0];
+ (*auxids)[1] = oldauxids[1];
+ }
+ else
+ {
+ /* Shift by one */
+ *auxids = MALLOC (*nauxids = 3);
+ (*auxids)[0] = oldauxids[0];
+ (*auxids)[1] = oldgenids[0];
+ (*auxids)[2] = oldauxids[1];
+ }
+ }
+ else
+ {
+ /* Just like above, but in the shift case note
+ that the new (*auxids)[2] shouldn't be allowed
+ to needlessly duplicate something further on. */
+ if (oldgenids[0] == oldauxids[1]
+ || scan_ids (&oldauxids[2], *nauxids - 2, oldauxids[1]))
+ {
+ *auxids = MALLOC (*nauxids = noldauxids);
+ bcopy (oldauxids, *auxids, *nauxids);
+ (*auxids)[1] = oldgenids[0];
+ }
+ else
+ {
+ *auxids = MALLOC (*nauxids = noldauxids + 1);
+ (*auxids)[0] = oldauxids[0];
+ (*auxids)[1] = oldgenids[0];
+ bcopy (&oldauxids[1], &(*auxids)[2], noldauxids - 1);
+ }
+ }
+
+ /* Whew. Now set the new id. */
+ *ngenids = noldgenids ?: 1;
+ *genids = malloc (*ngenids);
+
+ if (! *genids)
+ {
+ free (*auxids);
+ return ENOMEM;
+ }
+
+ (*genids)[0] = id;
+ if (noldgenids > 0)
+ bcopy (&oldgenids[1], &(*genids)[1], *ngenids - 1);
+
+ if (secure && !*secure
+ && !scan_ids (oldgenids, noldgenids, id)
+ && !scan_ids (oldauxids, noldauxids, id))
+ *secure = 1;
+
+ return 0;
+}
kern_return_t
diskfs_S_file_exec (struct protid *cred,
@@ -82,7 +184,6 @@ diskfs_S_file_exec (struct protid *cred,
return EACCES;
}
-#ifdef this_is_right_but_not_quite_complete
suid = np->dn_stat.st_mode & S_ISUID; /* XXX not if we can't do it... */
sgid = np->dn_stat.st_mode & S_ISGID; /* XXX not of we can't do it... */
secure = 0;
@@ -104,17 +205,7 @@ diskfs_S_file_exec (struct protid *cred,
int nauxgids, ngengids;
auth_t newauth;
- int isroot, i;
-
- int
- scan_ids (uid_t *set, int setlen, uid_t test)
- {
- int i;
- for (i = 0; i < setlen; i++)
- if (set[i] == test)
- return 1;
- return 0;
- }
+ int i;
void
reauth (mach_port_t *port, int isproc)
@@ -170,149 +261,18 @@ diskfs_S_file_exec (struct protid *cred,
will be wrong. No matter; we will repeat these checks
using secure id sets later if the port turns out to be
bogus. */
- isroot = (scan_ids (oldgenuids, noldgenuids, 0)
- && scan_ids (oldauxuids, noldauxuids, 0));
- if (!secure && suid && !isroot
- && !scan_ids (oldgenuids, noldgenuids, np->dn_stat.st_uid)
- && !scan_ids (oldauxuids, noldauxuids, np->dn_stat.st_uid))
- secure = 1;
- if (!secure && sgid && !isroot
- && !scan_ids (oldgengids, noldgengids, np->dn_stat.st_gid)
- && !scan_ids (oldauxgids, noldauxgids, np->dn_stat.st_gid))
- secure = 1;
-
-
- /* STEP 2: Rearrange the ids to decide what the new
- lists will look like. */
-
if (suid)
- {
- /* We are dumping the current first uid; put it
- into the auxuids array. This is complex. The different
- cases below are intended to make sure that we don't
- lose any uids (unlike posix) and to make sure that aux ids
- zero and one (if already set) behave like the posix
- ones. */
-
- if (noldauxuids == 0)
- {
- auxuids = alloca (nauxuids = 1);
- auxuids[0] = oldgenuids[0];
- }
- else if (noldauxuids == 1)
- {
- auxuids = alloca (nauxuids = 2);
- auxuids[0] = oldauxuids[0];
- auxuids[1] = oldgenuids[0];
- }
- else if (noldauxuids == 2)
- {
- if (oldgenuids[0] == oldauxuids[1])
- {
- auxuids = alloca (nauxuids = 2);
- auxuids[0] = oldauxuids[0];
- auxuids[1] = oldauxuids[1];
- }
- else
- {
- /* Shift by one */
- auxuids = alloca (nauxuids = 3);
- auxuids[0] = oldauxuids[0];
- auxuids[1] = oldgenuids[0];
- auxuids[2] = oldauxuids[1];
- }
- }
- else
- {
- /* Just like above, but in the shift case note
- that the new auxuids[2] shouldn't be allowed
- to needlessly duplicate something further on. */
- if (oldgenuids[0] == oldauxuids[1]
- || scan_uids (&oldauxuids[2], nauxuids - 2, oldauxuids[1]))
- {
- auxuids = alloca (nauxuids = noldauxuids);
- bcopy (oldauxuids, auxuids, nauxuids);
- auxuids[1] = oldgenuids[0];
- }
- else
- {
- auxuids = alloca (nauxuids = noldauxuids + 1);
- auxuids[0] = oldauxuids[0];
- auxuids[1] = oldgenuids[0];
- bcopy (&oldauxuids[1], &auxuids[2], noldauxuids - 1);
- }
- }
-
- /* Whew. Now set the new uid. */
- genuids = alloca (ngenuids = noldgenuids);
- genuids[0] = np->dn_stat.st_uid;
- bcopy (&oldgenuids[1], &genuids[1], ngenuids - 1);
- }
-
- /* And now the same thing for group ids, mutatis mutandis. */
- if (sgid)
- {
- /* We are dumping the current first gid; put it
- into the auxgids array. This is complex. The different
- cases below are intended to make sure that we don't
- lose any gids (unlike posix) and to make sure that aux ids
- zero and one (if already set) behave like the posix
- ones. */
-
- if (noldauxgids == 0)
- {
- auxgids = alloca (nauxgids = 1);
- auxgids[0] = oldgengids[0];
- }
- else if (noldauxgids == 1)
- {
- auxgids = alloca (nauxgids = 2);
- auxgids[0] = oldauxgids[0];
- auxgids[1] = oldgengids[0];
- }
- else if (noldauxgids == 2)
- {
- if (oldgengids[0] == oldauxgids[1])
- {
- auxgids = alloca (nauxgids = 2);
- auxgids[0] = oldauxgids[0];
- auxgids[1] = oldauxgids[1];
- }
- else
- {
- /* Shift by one */
- auxgids = alloca (nauxgids = 3);
- auxgids[0] = oldauxgids[0];
- auxgids[1] = oldgengids[0];
- auxgids[2] = oldauxgids[1];
- }
- }
- else
- {
- /* Just like above, but in the shift case note
- that the new auxgids[2] shouldn't be allowed
- to needlessly duplicate something further on. */
- if (oldgengids[0] == oldauxgids[1]
- || scan_gids (&oldauxgids[2], nauxgids - 2, oldauxgids[1]))
- {
- auxgids = alloca (nauxgids = noldauxgids);
- bcopy (oldauxgids, auxgids, nauxgids);
- auxgids[1] = oldgengids[0];
- }
- else
- {
- auxgids = alloca (nauxgids = noldauxgids + 1);
- auxgids[0] = oldauxgids[0];
- auxgids[1] = oldgengids[0];
- bcopy (&oldauxgids[1], &auxgids[2], noldauxgids - 1);
- }
- }
-
- /* Whew. Now set the new gid. */
- gengids = alloca (ngengids = noldgengids);
- gengids[0] = np->dn_stat.st_gid;
- bcopy (&oldgengids[1], &gengids[1], ngengids - 1);
- }
+ err = setid (np->dn_stat.st_uid, &secure,
+ oldgenuids, noldauxuids, oldauxuids, noldauxuids,
+ &genuids, &ngenuids, &auxuids, &nauxuids);
+ if (sgid && !err)
+ err = setid (np->dn_stat.st_gid, &secure,
+ oldgengids, noldauxgids, oldauxgids, noldauxgids,
+ &gengids, &ngengids, &auxgids, &nauxgids);
+
+ if (scan_ids (oldgenuids, noldgenuids, 0)
+ || scan_ids (oldauxuids, noldauxuids, 0))
+ secure = 0; /* If we're root, we don't have to be. */
/* Deallocate the buffers if MiG allocated them. */
if (oldgenuids != gubuf)
@@ -328,6 +288,9 @@ diskfs_S_file_exec (struct protid *cred,
vm_deallocate (mach_task_self (), (vm_address_t) oldauxgids,
noldauxgids * (sizeof (gid_t)));
+ if (err)
+ goto abandon_suid; /* setid() failed. */
+
/* STEP 3: Attempt to create this new auth handle. */
err = auth_makeauth (diskfs_auth_server_port, &portarray[INIT_PORT_AUTH],
@@ -338,35 +301,42 @@ diskfs_S_file_exec (struct protid *cred,
auxgids, nauxgids,
&newauth);
if (err == EINVAL)
+ /* The user's auth port was bogus. As we can't trust what the user
+ has told us about ids, we use the authentication on the file being
+ execed (which we know is good), as the effective ids, and assume
+ no aux ids. */
{
- /* The user's auth port was bogus. We have to repeat the
- check in step 1 above, but this time use the id's that
- we have verified on the incoming request port. */
- isroot = diskfs_isuid (cred, 0);
- secure = 0;
- if (suid && !isroot && !diskfs_isuid (cred, np->dn_stat.st_uid))
- secure = 1;
- if (!secure && sgid && !isroot
- && !diskfs_groupmember (cred, np->dn_stat.st_gid))
- secure = 1;
-
- /* XXX Bad bug---the id's here came from the user's bogus
- port; we shouldn't just trust them */
-
- /* And now again try and create the new auth port, this
- time not using the user for help. */
- err = auth_makeauth (diskfs_auth_server_port,
- 0, MACH_MSG_TYPE_COPY_SEND, 0,
- genuids, ngenuids,
- auxuids, nauxuids,
- gengids, ngengids,
- auxgids, nauxgids,
+ /* Free our first attempts. */
+ free (genuids);
+ free (auxuids);
+ free (gengids);
+ free (auxgids);
+
+ if (suid)
+ err = setid (np->dn_stat.st_uid, &secure,
+ cred->uids, cred->nuids, 0, 0,
+ &genuids, &ngenuids, &auxuids, &nauxuids);
+ if (sgid && !err)
+ err = setid (np->dn_stat.st_gid, &secure,
+ cred->gids, cred->ngids, 0, 0,
+ &gengids, &ngengids, &auxgids, &nauxgids);
+
+ if (diskfs_isuid (0, cred))
+ secure = 0; /* If we're root, we don't have to be. */
+
+ if (err)
+ goto abandon_suid; /* setid() failed. */
+
+ /* Trrrry again... */
+ err = auth_makeauth (diskfs_auth_server_port, 0,
+ MACH_MSG_TYPE_COPY_SEND, 1,
+ genuids, ngenuids, auxuids, nauxuids,
+ gengids, ngengids, auxgids, nauxgids,
&newauth);
}
if (err)
- goto abandon_suid;
-
+ goto free_abandon_suid;
/* STEP 4: Re-authenticate all the ports we are handing to the user
with this new port, and install the new auth port in portarray. */
@@ -388,182 +358,18 @@ diskfs_S_file_exec (struct protid *cred,
mach_port_deallocate (mach_task_self (), portarray[INIT_PORT_AUTH]);
portarray[INIT_PORT_AUTH] = newauth;
-
/* STEP 5: If we must be secure, then set the appropriate flags
to tell the exec server so. */
if (secure)
flags |= EXEC_SECURE | EXEC_NEWTASK;
- }
- abandon_suid:
-#endif
-
-
-#ifdef this_is_so_totally_wrong_and_is_replaced_with_the_above
- /* Handle S_ISUID and S_ISGID uid substitution. */
- /* XXX All this complexity should be moved to libfshelp. -mib */
- if ((((np->dn_stat.st_mode & S_ISUID)
- && !diskfs_isuid (np->dn_stat.st_uid, cred))
- || ((np->dn_stat.st_mode & S_ISGID)
- && !diskfs_groupmember (np->dn_stat.st_gid, cred)))
- && !diskfs_isuid (0, cred))
- {
- /* XXX The test above was correct for the code before Roland
- changed it; but now it's wrong. This test decides when
- permission is increasing, and therefore we need to
- protect the exec with NEWTASK and SECURE. If permission
- isn't increasing, then we still substitute id's, but we
- don't to the SECURE or NEWTASK protection. -mib */
-
- /* XXX Perhaps if there are errors in reauthenticating,
- we should just run non-setuid? */
-
- mach_port_t newauth, intermediate;
- void reauth (mach_port_t *port, int procp)
- {
- mach_port_t newport, ref;
- if (*port == MACH_PORT_NULL)
- return;
- ref = mach_reply_port ();
- err = (procp ? proc_reauthenticate : io_reauthenticate)
- (*port, ref, MACH_MSG_TYPE_MAKE_SEND);
- if (! err)
- err = auth_user_authenticate (newauth, *port,
- ref, MACH_MSG_TYPE_MAKE_SEND,
- &newport);
- if (err)
- {
- /* Could not reauthenticate. Do not give away the old port. */
- mach_port_deallocate (mach_task_self (), *port);
- *port = MACH_PORT_NULL; /* XXX ? */
- }
- else if (newport != MACH_PORT_NULL)
- {
- mach_port_deallocate (mach_task_self (), *port);
- *port = newport;
- }
- mach_port_destroy (mach_task_self (), ref);
- }
-
- uid_t auxuidbuf[2], genuidbuf[10];
- uid_t *old_aux_uids = auxuidbuf, *old_gen_uids = genuidbuf;
- int nold_aux_uids = 2, nold_gen_uids = 10;
- gid_t auxgidbuf[2], gengidbuf[10];
- gid_t *old_aux_gids = auxgidbuf, *old_gen_gids = gengidbuf;
- int nold_aux_gids = 2, nold_gen_gids = 10;
- int ngen_uids = nold_gen_uids ?: 1;
- int naux_uids = nold_aux_uids < 2 ? nold_aux_uids : 2;
- uid_t gen_uids[ngen_uids], aux_uids[naux_uids];
- int ngen_gids = nold_gen_gids ?: 1;
- int naux_gids = nold_aux_gids < 2 ? nold_aux_gids : 2;
- gid_t gen_gids[ngen_gids], aux_gids[naux_gids];
-
- unsigned int i;
-
- /* Tell the exec server to use secure ports and a new task. */
- flags |= EXEC_SECURE|EXEC_NEWTASK;
- /* Find the IDs of the old auth handle. */
- err = auth_getids (portarray[INIT_PORT_AUTH],
- &old_gen_uids, &nold_aux_uids,
- &old_aux_uids, &nold_aux_uids,
- &old_gen_gids, &nold_gen_gids,
- &old_aux_gids, &nold_aux_gids);
- if (err == MACH_SEND_INVALID_DEST)
- nold_gen_uids = nold_aux_uids = nold_gen_gids = nold_aux_gids = 0;
- else if (err)
- return err;
-
- /* XXX This is broken; there is no magical "nonexistent ID"
- number. The Posix numbering only matters for the exec of a
- Posix process; this case can't be a problem, therefore. Just
- stuff the ID in slot 0 and nothing in slot 1. */
-
- /* Set up the UIDs for the new auth handle. */
- if (nold_aux_uids == 0)
- /* No real UID; we must invent one. */
- aux_uids[0] = nold_gen_uids ? old_gen_uids[0] : -2; /* XXX */
- else
- {
- aux_uids[0] = old_aux_uids[0];
- if (nold_aux_uids > 2)
- memcpy (&aux_uids[2], &old_aux_uids[2],
- nold_aux_uids * sizeof (uid_t));
- }
- aux_uids[1] = old_gen_uids[0]; /* Set saved set-UID to effective UID. */
- gen_uids[0] = np->dn_stat.st_uid; /* Change effective to file owner. */
- memcpy (&gen_uids[1], &old_gen_uids[1],
- ((nold_gen_uids ?: 1) - 1) * sizeof (uid_t));
-
- /* Set up the GIDs for the new auth handle. */
- if (nold_aux_gids == 0)
- /* No real GID; we must invent one. */
- old_aux_gids[0] = nold_gen_gids ? old_gen_gids[0] : -2; /* XXX */
- else
- {
- aux_gids[0] = old_aux_gids[0];
- if (nold_aux_gids > 2)
- memcpy (&aux_gids[2], &old_aux_gids[2],
- nold_aux_gids * sizeof (gid_t));
- }
- aux_gids[1] = old_gen_gids[0]; /* Set saved set-GID to effective GID. */
- gen_gids[0] = np->dn_stat.st_gid; /* Change effective to file owner. */
- memcpy (&gen_gids[1], &old_gen_gids[1],
- ((nold_gen_gids ?: 1) - 1) * sizeof (gid_t));
-
- /* XXX This is totally wrong. Just do one call to auth_makeauth
- with both handles. INTERMEDIATE here has no id's at all, and
- so the second auth_makeauth call is guaranteed to fail.
-
- It should give the user as close to the correct privilege as
- possible as well; this requires looking inside the uid sets
- and doing the "right thing". If we are entirely unable to
- increase the task's privilege, then abandon the setuid part,
- but don't return an error.
-
- -mib */
-
- /* Create the new auth handle. First we must make a handle that
- combines our IDs with those in the original user handle in
- portarray[INIT_PORT_AUTH]. Only using that handle will we be
- allowed to create the final handle, which contains secondary IDs
- from the original user handle that we don't necessarily have. */
- {
- mach_port_t handles[2] =
- { diskfs_auth_server_port, portarray[INIT_PORT_AUTH] };
- err = auth_makeauth (diskfs_auth_server_port,
- handles, MACH_MSG_TYPE_COPY_SEND, 2,
- NULL, 0, NULL, 0, NULL, 0, NULL, 0,
- &intermediate);
- }
- if (err)
- return err;
- err = auth_makeauth (intermediate,
- NULL, MACH_MSG_TYPE_COPY_SEND, 0,
- gen_uids, ngen_uids,
- aux_uids, naux_uids,
- gen_gids, ngen_gids,
- aux_gids, naux_gids,
- &newauth);
- mach_port_deallocate (mach_task_self (), intermediate);
- if (err)
- return err;
-
- /* Now we must reauthenticate all the ports to other
- servers we pass along to it. */
-
- for (i = 0; i < fdslen; ++i)
- reauth (&fds[i], 0);
-
- /* XXX The first two are unimportant; EXEC_SECURE is going to
- blow them away anyhow. -mib */
- reauth (&portarray[INIT_PORT_PROC], 1);
- reauth (&portarray[INIT_PORT_CRDIR], 0);
- reauth (&portarray[INIT_PORT_CWDIR], 0);
-
- mach_port_deallocate (mach_task_self (), portarray[INIT_PORT_AUTH]);
- portarray[INIT_PORT_AUTH] = newauth;
+ free_abandon_suid:
+ free (genuids);
+ free (auxuids);
+ free (gengids);
+ free (auxgids);
}
-#endif
+ abandon_suid:
/* If the user can't read the file, then we should use a new task,
which would be inaccessible to the user. Actually, this doesn't