int main (int argc, char **argv) { mach_port_t bootstrap; mach_port_t control; error_t err; /* Parse our options... */ argp_parse (&argp, argc, argv, 0, 0, 0); task_get_bootstrap_port (mach_task_self (), &bootstrap); if (bootstrap == MACH_PORT_NULL) error (1, 0, "Must be started as a translator"); linktarget = argv[1]; /* Reply to our parent */ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &control); mach_port_insert_right (mach_task_self (), control, control, MACH_MSG_TYPE_MAKE_SEND); err = fsys_startup (bootstrap, 0, control, MACH_MSG_TYPE_COPY_SEND, &realnode); mach_port_deallocate (mach_task_self (), control); mach_port_deallocate (mach_task_self (), bootstrap); if (err) error (1, err, "Starting up translator"); io_restrict_auth (realnode, &realnodenoauth, 0, 0, 0, 0); mach_port_deallocate (mach_task_self (), realnode); /* Mark us as important. */ mach_port_t proc = getproc (); if (proc == MACH_PORT_NULL) error (2, err, "cannot get a handle to our process"); err = proc_mark_important (proc); /* This might fail due to permissions or because the old proc server is still running, ignore any such errors. */ if (err && err != EPERM && err != EMIG_BAD_ID) error (2, err, "Cannot mark us as important"); mach_port_deallocate (mach_task_self (), proc); /* Launch */ while (1) { /* The timeout here is 10 minutes */ err = mach_msg_server_timeout (fsys_server, 0, control, MACH_RCV_TIMEOUT, 1000 * 60 * 10); if (err == MACH_RCV_TIMED_OUT) exit (0); } }
kern_return_t trivfs_S_io_restrict_auth (struct trivfs_protid *cred, mach_port_t reply, mach_msg_type_name_t replytype, mach_port_t *newport, mach_msg_type_name_t *newporttype, uid_t *uids, size_t nuids, uid_t *gids, size_t ngids) { int i; error_t err; struct trivfs_protid *newcred; struct idvec *uvec, *gvec; struct iouser *user; if (!cred) return EOPNOTSUPP; if (cred->isroot) /* CRED has root access, and so may use any ids. */ { err = iohelp_create_complex_iouser (&user, uids, nuids, gids, ngids); if (err) return err; } else { uvec = make_idvec (); if (! uvec) return ENOMEM; gvec = make_idvec (); if (! gvec) { idvec_free (uvec); return ENOMEM; } /* Otherwise, use any of the requested ids that CRED already has. */ for (i = 0; i < cred->user->uids->num; i++) if (listmember (uids, cred->user->uids->ids[i], nuids)) { err = idvec_add (uvec, cred->user->uids->ids[i]); if (err) goto out; } for (i = 0; i < cred->user->gids->num; i++) if (listmember (gids, cred->user->gids->ids[i], ngids)) { err = idvec_add (gvec, cred->user->gids->ids[i]); if (err) goto out; } err = iohelp_create_iouser (&user, uvec, gvec); if (err) { out: idvec_free (uvec); idvec_free (gvec); return err; } } err = ports_create_port (cred->po->cntl->protid_class, cred->po->cntl->protid_bucket, sizeof (struct trivfs_protid), &newcred); if (err) { iohelp_free_iouser (user); return err; } newcred->isroot = 0; mutex_lock (&cred->po->cntl->lock); newcred->po = cred->po; newcred->po->refcnt++; mutex_unlock (&cred->po->cntl->lock); if (cred->isroot && idvec_contains (user->uids, 0)) newcred->isroot = 1; newcred->user = user; newcred->hook = cred->hook; err = io_restrict_auth (cred->realnode, &newcred->realnode, user->uids->ids, user->uids->num, user->gids->ids, user->gids->num); if (!err && trivfs_protid_create_hook) { err = (*trivfs_protid_create_hook) (newcred); if (err) mach_port_deallocate (mach_task_self (), newcred->realnode); } if (err) /* Signal that the user destroy hook shouldn't be called on NEWCRED. */ newcred->realnode = MACH_PORT_NULL; else { *newport = ports_get_right (newcred); *newporttype = MACH_MSG_TYPE_MAKE_SEND; } /* This will destroy NEWCRED if we got an error and didn't do the ports_get_right above. */ ports_port_deref (newcred); return 0; }
kern_return_t trivfs_S_fsys_getroot (struct trivfs_control *cntl, mach_port_t reply_port, mach_msg_type_name_t reply_port_type, mach_port_t dotdot, uid_t *uids, size_t nuids, uid_t *gids, size_t ngids, int flags, retry_type *do_retry, char *retry_name, mach_port_t *newpt, mach_msg_type_name_t *newpttype) { int perms; error_t err = 0; mach_port_t new_realnode; struct trivfs_protid *cred; struct iouser *user; if (!cntl) return EOPNOTSUPP; if (trivfs_getroot_hook) { err = (*trivfs_getroot_hook) (cntl, reply_port, reply_port_type, dotdot, uids, nuids, gids, ngids, flags, do_retry, retry_name, newpt, newpttype); if (err != EAGAIN) return err; } if ((flags & O_WRITE & trivfs_allow_open) != (flags & O_WRITE)) return EROFS; if ((flags & (O_READ|O_WRITE|O_EXEC) & trivfs_allow_open) != (flags & (O_READ|O_WRITE|O_EXEC))) return EACCES; /* O_CREAT and O_EXCL are not meaningful here; O_NOLINK and O_NOTRANS will only be useful when trivfs supports translators (which it doesn't now). */ flags &= O_HURD; flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS); struct idvec idvec = { .ids = uids, .num = nuids, .alloced = nuids, }; if (_is_privileged (&idvec)) /* Privileged users should be given all our rights. */ err = io_duplicate (cntl->underlying, &new_realnode); else /* Non-privileged, restrict rights. */ err = io_restrict_auth (cntl->underlying, &new_realnode, uids, nuids, gids, ngids); if (err) return err; err = iohelp_create_complex_iouser (&user, uids, nuids, gids, ngids); if (err) return err; /* Validate permissions. */ if (! trivfs_check_access_hook) file_check_access (new_realnode, &perms); else (*trivfs_check_access_hook) (cntl, user, new_realnode, &perms); if ((flags & (O_READ|O_WRITE|O_EXEC) & perms) != (flags & (O_READ|O_WRITE|O_EXEC))) err = EACCES; if (!err && trivfs_check_open_hook) err = (*trivfs_check_open_hook) (cntl, user, flags); if (!err) { if (! trivfs_open_hook) { err = trivfs_open (cntl, user, flags, new_realnode, &cred); if (!err) mach_port_deallocate (mach_task_self (), dotdot); } else err = (*trivfs_open_hook) (cntl, user, dotdot, flags, new_realnode, &cred); } if (err) { mach_port_deallocate (mach_task_self (), new_realnode); iohelp_free_iouser (user); } else { *do_retry = FS_RETRY_NORMAL; *retry_name = '\0'; *newpt = ports_get_right (cred); *newpttype = MACH_MSG_TYPE_MAKE_SEND; ports_port_deref (cred); } return err; }
/*Traces the translator stack on the given underlying node until it finds the first translator called `name` and returns the port pointing to the translator sitting under this one.*/ error_t trace_find (mach_port_t underlying, const char *name, int flags, mach_port_t * port) { error_t err = 0; /*Identity information about the current process */ uid_t *uids; size_t nuids; gid_t *gids; size_t ngids; /*The name and arguments of the translator being passed now */ char *argz = NULL; size_t argz_len = 0; /*The current working directory */ char *cwd = NULL; /*The port to the directory containing the file pointed to by `underlying` */ file_t dir; /*The unauthenticated version of `dir` */ file_t unauth_dir; /*The control port of the current translator */ fsys_t fsys; /*The port to the translator we are currently looking at */ mach_port_t node = underlying; /*The port to the previous translator */ mach_port_t prev_node = MACH_PORT_NULL; /*The retry name and retry type returned by fsys_getroot */ string_t retry_name; retry_type retry; /*Finalizes the execution of this function */ void finalize (void) { /*If there is a working directory, free it */ if (cwd) free (cwd); /*If the ports to the directory exist */ if (dir) PORT_DEALLOC (dir); } /*finalize */ /*Obtain the current working directory */ cwd = getcwd (NULL, 0); if (!cwd) { LOG_MSG ("trace_find: Could not obtain cwd."); return EINVAL; } LOG_MSG ("trace_find: cwd: '%s'", cwd); /*Open a port to this directory */ dir = file_name_lookup (cwd, 0, 0); if (!dir) { finalize (); return ENOENT; } /*Try to get the number of effective UIDs */ nuids = geteuids (0, 0); if (nuids < 0) { finalize (); return EINVAL; } /*Allocate some memory for the UIDs on the stack */ uids = alloca (nuids * sizeof (uid_t)); /*Fetch the UIDs themselves */ nuids = geteuids (nuids, uids); if (nuids < 0) { finalize (); return EINVAL; } /*Try to get the number of effective GIDs */ ngids = getgroups (0, 0); if (ngids < 0) { finalize (); return EINVAL; } /*Allocate some memory for the GIDs on the stack */ gids = alloca (ngids * sizeof (gid_t)); /*Fetch the GIDs themselves */ ngids = getgroups (ngids, gids); if (ngids < 0) { finalize (); return EINVAL; } /*Obtain the unauthenticated version of `dir` */ err = io_restrict_auth (dir, &unauth_dir, 0, 0, 0, 0); if (err) { finalize (); return err; } char buf[256]; char *_buf = buf; size_t len = 256; io_read (node, &_buf, &len, 0, len); LOG_MSG ("trace_find: Read from underlying: '%s'", buf); /*Go up the translator stack */ for (; !err;) { /*retreive the name and options of the current translator */ err = file_get_fs_options (node, &argz, &argz_len); if (err) break; LOG_MSG ("trace_find: Obtained translator '%s'", argz); if (strcmp (argz, name) == 0) { LOG_MSG ("trace_find: Match. Stopping here."); break; } /*try to fetch the control port for this translator */ err = file_get_translator_cntl (node, &fsys); LOG_MSG ("trace_find: err = %d", (int) err); if (err) break; LOG_MSG ("trace_find: Translator control port: %lu", (unsigned long) fsys); prev_node = node; /*fetch the root of the translator */ err = fsys_getroot (fsys, unauth_dir, MACH_MSG_TYPE_COPY_SEND, uids, nuids, gids, ngids, flags | O_NOTRANS, &retry, retry_name, &node); LOG_MSG ("trace_find: fsys_getroot returned %d", (int) err); LOG_MSG ("trace_find: Translator root: %lu", (unsigned long) node); /*TODO: Remove this debug output. */ /*char buf[256]; char * _buf = buf; size_t len = 256; io_read(node, &_buf, &len, 0, len); LOG_MSG("trace_find: Read: '%s'", buf); */ } /*If the error occurred (most probably) because of the fact that we have reached the top of the translator stack */ if ((err == EMACH_SEND_INVALID_DEST) || (err == ENXIO)) /*this is OK */ err = 0; /*Return the port to read from */ *port = prev_node; /*Return the result of operations */ finalize (); return err; } /*trace_find */