diff options
author | Miles Bader <miles@gnu.org> | 1995-12-21 16:28:03 +0000 |
---|---|---|
committer | Miles Bader <miles@gnu.org> | 1995-12-21 16:28:03 +0000 |
commit | 381412294cc990bda245f638ad376e48a98837a0 (patch) | |
tree | a5afc5c1763c6d87333beda1eb1ae1fc8f44129c | |
parent | bbc71f855b1cd4c4bd5bcfd27e884151fd08847e (diff) |
Everything changed.
-rw-r--r-- | utils/login.c | 618 |
1 files changed, 457 insertions, 161 deletions
diff --git a/utils/login.c b/utils/login.c index 0cab4934..7d666afb 100644 --- a/utils/login.c +++ b/utils/login.c @@ -22,179 +22,330 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <argp.h> -#include <argz.h> #include <unistd.h> #include <paths.h> -#include <error.h> #include <ctype.h> +#include <utmp.h> #include <pwd.h> #include <grp.h> + #include <sys/fcntl.h> -#define DEFAULT_SHELL _PATH_BSHELL -#define DEFAULT_PATH _PATH_DEFPATH -#define DEFAULT_HOME "/u/nobody" +#include <argp.h> +#include <argz.h> +#include <envz.h> +#include <idvec.h> +#include <error.h> + +#define DEFAULT_NOBODY "login" #define DEFAULT_UMASK 0 +#define _PATH_MOTD "/etc/motd" /* XXX */ + +/* Which shell to use if we can't find the default one. */ +#define FAILURE_SHELL _PATH_BSHELL + +/* Defaults for various login parameters. */ +char *default_args[] = { + "SHELL=" _PATH_BSHELL, + "HOME=/u/login", + "USER=login", + "NAME=Not logged in", + "HUSHLOGIN=.hushlogin", /* Looked up relative new home dir. */ + "MOTD=" _PATH_MOTD, + 0 +}; +/* Default values for the new environment. */ +char *default_env[] = { + "PATH=" _PATH_DEFPATH, + 0 +}; + +/* Which things are copied from the login parameters into the environment. */ +char *copied_args[] = { "SHELL", "HOME", "NAME", "VIA", 0 }; + static struct argp_option options[] = { - {"shell", 's', "SHELL", 0, "Use SHELL as the new shell"}, - {"arg", 'a', "ARG", 0, "Add ARG to the shell's arguments"}, - {"arg0", '0', "ARG", 0, "Make ARG the shell's argv[0]"}, - {"environ", 'e', "ENTRY", 0, "Add ENTRY to the environment"}, - {"user", 'u', "USER", 0, "Add USER to the uids in the new shell"}, - {"aux-user", 'U', "USER", 0, "Add USER to the aux uids in the new shell"}, - {"group", 'g', "GROUP", 0, "Add GROUP to the gids in the new shell"}, - {"aux-group", 'G', "GROUP", 0, "Add GROUP to the aux gids in the new shell"}, - {"home", 'h', "DIR", 0, "Make DIR the CWD of the new shell"}, - {"umask", 'm', "MASK", 0, "Make UMASK the new shell's umask"}, - {"path", 'p', "PATH", 0, "Path put in environment by default"}, - {"no-environ", 'E', 0, 0, "Don't put default entries into the new environment"}, + {"arg0", '0', "ARG", 0, "Make ARG the shell's argv[0]"}, + {"environ", 'e', "ENTRY", 0, "Add ENTRY to the environment"}, + {"environ-default", 'E', "ENTRY", 0, "Use ENTRY as a default environment variable"}, + {"no-args", 'x', 0, 0, "Don't login args into the environment"}, + {"arg", 'a', "ARG", 0, "Add login parameter ARG"}, + {"arg-default", 'A', "ARG", 0, "Use ARG as a default login parameter"}, + {"no-environ", 'X', 0, 0, "Don't add the parent environment as default login params"}, + {"user", 'u', "USER", 0, "Add USER to the effective uids"}, + {"avail-user",'U', "USER", 0, "Add USER to the available uids"}, + {"group", 'g', "GROUP", 0, "Add GROUP to the effective groups"}, + {"avail-group",'G',"GROUP", 0, "Add GROUP to the available groups"}, + {"umask", 'm', "MASK", 0, "Use a umask of MASK"}, + {"nobody", 'n', "USER", 0, "Use USER's passwd entry to fetch the default" + "login params if there are no uids (default `" DEFAULT_NOBODY "')"}, + {"inherit-environ", 'p', 0, 0, "Inherit the parent's environment"}, + {"via", 'h', "HOST", 0, "This login is from HOST"}, + {"no-passwd", 'f', 0, 0, "Don't ask for passwords"}, + {"no-utmp", 'z', 0, 0, "Don't put an entry in utmp"}, {0, 0} }; -static char *args_doc = "[USER...]"; -static char *doc = 0; +static char *args_doc = "[USER [ARG...]]"; +static char *doc = +"To give args to the shell without specifying a user, use - for USER.\n" +"Current login parameters include HOME, SHELL, USER, NAME, and ROOT."; -/* These functions should be moved into argz.c in libshouldbelibc. */ - -/* Add BUF, of length BUF_)LEN to the argz vector in ARGZ & ARGZ_LEN. */ -static error_t -argz_append (char **argz, unsigned *argz_len, char *buf, unsigned buf_len) +/* Outputs whatever can be read from file descriptor FD to standard output, + and then close it. If FD is < 0, assumes an error happened, and prints an + error message using ERRNO and STR. */ +static void +cat (int fd, char *str) { - unsigned new_argz_len = *argz_len + buf_len; - char *new_argz = realloc (*argz, new_argz_len); - if (new_argz) + int rd = fd < 0 ? -1 : 1; + while (rd > 0) { - bcopy (buf, new_argz + *argz_len, buf_len); - *argz = new_argz; - *argz_len = new_argz_len; - return 0; + char buf[4096]; + rd = read (fd, buf, sizeof (buf)); + if (rd > 0) + write (0, buf, rd); } - else - return ENOMEM; -} - -/* Add STR to the argz vector in ARGZ & ARGZ_LEN. This should be moved into - argz.c in libshouldbelibc. */ -static error_t -argz_add (char **argz, unsigned *argz_len, char *str) -{ - return argz_append (argz, argz_len, str, strlen (str) + 1); + if (rd < 0) + error (0, errno, "%s", str); } -typedef unsigned id_t; - -struct ids -{ - id_t *ids; - unsigned num, alloced; -}; - -struct -ids *make_ids () +/* Add a utmp entry based on the parameters in ARGS & ARGS_LEN, from tty + TTY_FD. */ +static void +add_utmp_entry (char *args, unsigned args_len, int tty_fd) { - struct ids *ids = malloc (sizeof (struct ids)); - if (!ids) - error(8, ENOMEM, "Can't allocate id list"); - ids->ids = 0; - ids->num = ids->alloced = 0; - return ids; -} + extern void login (struct utmp *); /* in libutil */ + struct utmp utmp; + char bogus_tty[sizeof (_PATH_TTY) + 2]; + char *tty = ttyname (0), *tail; -void -ids_add (struct ids *ids, id_t id) -{ - if (ids->alloced == ids->num) + if (! tty) { - ids->alloced = ids->alloced * 2 + 1; - ids->ids = realloc (ids->ids, ids->alloced * sizeof (id_t)); - if (ids->ids == NULL) - error(8, ENOMEM, "Can't allocate id list"); + sprintf (bogus_tty, "%s??", _PATH_TTY); + tty = bogus_tty; } - ids->ids[ids->num++] = id; + tail = rindex (tty, '/'); + if (tail) + tty = tail + 1; + + bzero (&utmp, sizeof (utmp)); + + time (&utmp.ut_time); + strncpy (utmp.ut_name, envz_get (args, args_len, "USER") ?: "", + sizeof (utmp.ut_name)); + strncpy (utmp.ut_host, envz_get (args, args_len, "VIA") ?: "", + sizeof (utmp.ut_host)); + strncpy (utmp.ut_line, tty, sizeof (utmp.ut_line)); + + login (&utmp); } void main(int argc, char *argv[]) { - error_t err; + int i, fd; + error_t err = 0; int umask = DEFAULT_UMASK; - char *shell = 0; /* The shell program to run. */ - char *home = 0; /* The new home directory. */ - char *path = DEFAULT_PATH; /* The path put in environment. */ - char *arg0 = 0; /* The shell's argv[0]. */ - char *args = 0; /* The args to the shell. */ + char *nobody = DEFAULT_NOBODY; /* Where to get params if there is no user. */ + char *args = 0; /* The login parameters */ unsigned args_len = 0; + char *passwd = 0; /* Login parameters from /etc/passwd */ + unsigned passwd_len = 0; + char *args_defs = 0; /* Defaults for login parameters. */ + unsigned args_defs_len = 0; char *env = 0; /* The new environment. */ unsigned env_len = 0; - int no_environ = 0; /* True if we shouldn't add default entries. */ - struct ids *uids = make_ids (); /* The UIDs of the new shell. */ - struct ids *gids = make_ids (); /* The GIDs. */ - struct ids *aux_uids = make_ids (); /* The aux UIDs of the new shell. */ - struct ids *aux_gids = make_ids (); /* The aux GIDs. */ + char *env_defs = 0; /* Defaults for the environment. */ + unsigned env_defs_len = 0; + char *parent_env = 0; /* The environment we got from our parent */ + unsigned parent_env_len = 0; + int no_environ = 0; /* If false, use the env as default params. */ + int no_args = 0; /* If false, put login params in the env. */ + int inherit_environ = 0; /* True if we shouldn't clear our env. */ + int saw_user_arg = 0; /* True if we've seen the USER argument. */ + int no_passwd = 0; /* Don't bother verifying what we're doing. */ + int no_utmp = 0; /* Don't put an entry in utmp. */ + struct idvec *uids = make_idvec (); /* The UIDs of the new shell. */ + struct idvec *gids = make_idvec (); /* The GIDs. */ + struct idvec *aux_uids = make_idvec (); /* The aux UIDs of the new shell. */ + struct idvec *aux_gids = make_idvec (); /* The aux GIDs. */ + char *shell = 0; /* The shell program to run. */ + char *home = 0; /* The new home directory. */ + char *root = 0; /* The optional new root directory. */ + char *hushlogin = 0; /* The hushlogin file. */ + char *sh_arg0 = 0; /* The shell's argv[0]. */ + char *sh_args = 0; /* The args to the shell. */ + unsigned sh_args_len = 0; mach_port_t exec_node; /* The shell executable. */ mach_port_t home_node; /* The home directory node. */ - mach_port_t ports[INIT_PORT_MAX]; - int ints[INIT_INT_MAX]; + mach_port_t root_node; /* The root node. */ + mach_port_t ports[INIT_PORT_MAX]; /* Init ports for the new process. */ + int ints[INIT_INT_MAX]; /* Init ints for it. */ mach_port_t dtable[3]; /* File descriptors passed. */ - char *argz = 0; /* The shell's arg vector. */ - unsigned argz_len = 0; mach_port_t auth; /* The auth port for the new shell. */ mach_port_t auth_server = getauth (); mach_port_t proc_server = getproc (); + /* Returns a copy of the io/proc object PORT reauthenticated in AUTH. If an + error occurs, NAME is used to print an error message, and the program + exited. */ + mach_port_t + reauth (mach_port_t port, int is_proc, auth_t auth, char *name) + { + if (port) + { + mach_port_t rend = mach_reply_port (), new_port; + error_t err = + is_proc ? + proc_reauthenticate (port, rend, MACH_MSG_TYPE_MAKE_SEND) + : io_reauthenticate (port, rend, MACH_MSG_TYPE_MAKE_SEND); + + if (err) + error (12, err, "reauthenticating %s", name); + + err = + auth_user_authenticate (auth, port, + rend, MACH_MSG_TYPE_MAKE_SEND, + &new_port); + if (err) + error (13, err, "reauthenticating %s", name); + + if (is_proc) + /* proc_reauthenticate modifies the existing port. */ + { + /* We promised to make a copy, so do so... */ + mach_port_mod_refs (mach_task_self (), MACH_PORT_RIGHT_SEND, + port, 1); + if (new_port != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), new_port); + } + else + port = new_port; + + mach_port_destroy (mach_task_self (), rend); + } + return port; + } + + /* Add the `=' separated environment entry ENTRY to ENV & ENV_LEN, exiting + with an error message if we can't. */ + void add_entry (char **env, unsigned *env_len, char *entry) + { + char *name = strsep (&entry, "="); + err = envz_add (env, env_len, name, entry); + if (err) + error (8, err, "Adding %s", entry); + } + + /* Make sure the user should be allowed to do this. */ + void verify_passwd (const char *name, const char *password) + { + if (! no_passwd && password && *password) + { + extern char *crypt (const char salt[2], const char *string); + char *prompt, *unencrypted, *encrypted; + + asprintf (&prompt, "Password for %s:", name); + unencrypted = getpass (prompt); + encrypted = crypt (unencrypted, password); + /* Paranoia may destroya. */ + memset (unencrypted, 0, strlen (unencrypted)); + + if (strcmp (encrypted, password) != 0) + error (50, 0, "%s: Invalid password", name); + } + } + /* Parse our options... */ error_t parse_opt (int key, char *arg, struct argp_state *state) { switch (key) { - case 's': shell = arg; break; - case 'm': umask = strtoul (arg, 0, 8); break; - case 'h': home = arg; break; - case 'p': path = arg; break; - - case 'e': - err = argz_add (&env, &env_len, arg); - if (err) - error (8, err, "Adding %s", arg); - break; - case 'a': - err = argz_add (&args, &args_len, arg); - if (err) - error (9, err, "Adding %s", arg); - break; - case '0': - arg0 = arg; - break; + case 'n': nobody = arg; break; + case 'm': umask = strtoul (arg, 0, 8); break; + case 'p': inherit_environ = 1; break; + case 'x': no_args = 1; break; + case 'X': no_environ = 1; break; + case 'e': add_entry (&env, &env_len, arg); break; + case 'E': add_entry (&env_defs, &env_defs_len, arg); break; + case 'a': add_entry (&args, &args_len, arg); break; + case 'A': add_entry (&args_defs, &args_defs_len, arg); break; + case '0': sh_arg0 = arg; break; + case 'h': envz_add (&args, &args_len, "VIA", arg); break; + case 'f': no_passwd = 1; break; + case 'z': no_utmp = 1; break; case ARGP_KEY_ARG: + if (saw_user_arg) + { + err = argz_add (&sh_args, &sh_args_len, arg); + if (err) + error (9, err, "Adding %s", arg); + break; + } + + saw_user_arg = 1; + if (strcmp (arg, "-") == 0) + arg = 0; /* Just like there weren't any args at all. */ + /* Fall through to deal with adding the user. */ + case 'u': case 'U': + case ARGP_KEY_NO_ARGS: { + /* USER is whom to look up. If it's 0, then we hit the end of + the sh_args without seeing a user, so we want to add defaults + values for `nobody'. */ + char *user = arg ?: nobody; struct passwd *pw = - isdigit (*arg) ? getpwuid (atoi (arg)) : getpwnam (arg); - if (! pw) - error (10, 0, "%s: Unknown user", arg); + isdigit (*user) ? getpwuid (atoi (user)) : getpwnam (user); - /* Should check password here. */ - - if (key != 'U') + if (! pw) + if (! arg) + /* It was nobody anyway. Just use the defaults. */ + break; + else + error (10, 0, "%s: Unknown user", user); + + if (arg + && ! idvec_contains (uids, pw->pw_uid) + && ! idvec_contains (aux_uids, pw->pw_uid)) + /* Check for a password, but only if we haven't already, and + it's not nobody. */ + verify_passwd (pw->pw_name, pw->pw_passwd); + + if (key == 'U') + /* Add aux-ids instead of real ones. */ { - ids_add (uids, pw->pw_uid); - ids_add (gids, pw->pw_gid); - - /* Add reasonable defaults. */ - if (! home && pw->pw_dir) - home = strdup (pw->pw_dir); - if (! shell && pw->pw_shell) - shell = strdup (pw->pw_shell); + idvec_add (aux_uids, pw->pw_uid); + idvec_add (aux_uids, pw->pw_gid); } else { - ids_add (aux_uids, pw->pw_uid); - ids_add (aux_uids, pw->pw_gid); + if (key == ARGP_KEY_ARG || uids->num == 0) + /* If it's the argument (as opposed to option) specifying a + user, or the first option user, then we get defaults for + various things from the password entry. */ + { + envz_add (&passwd, &passwd_len, "HOME", pw->pw_dir); + envz_add (&passwd, &passwd_len, "SHELL", pw->pw_shell); + envz_add (&passwd, &passwd_len, "NAME", pw->pw_gecos); + envz_add (&passwd, &passwd_len, "USER", pw->pw_name); + } + if (arg) /* A real user. */ + { + idvec_add (uids, pw->pw_uid); + idvec_add (gids, pw->pw_gid); + + if (key == ARGP_KEY_ARG) + /* The real user arg; make sure this is the first id in + the aux ids set (i.e. the `real' id). */ + { + idvec_insert (aux_uids, 0, pw->pw_uid); + idvec_insert (aux_gids, 0, pw->pw_gid); + } + } } } break; @@ -207,9 +358,12 @@ main(int argc, char *argv[]) if (! gr) error (11, 0, "%s: Unknown group", arg); - /* Should check password here. */ + if (! idvec_contains (gids, gr->gr_gid) + && ! idvec_contains (aux_gids, gr->gr_gid)) + /* Check for a password, but only if we haven't already. */ + verify_passwd (gr->gr_name, gr->gr_passwd); - ids_add (key == 'g' ? gids : aux_gids, gr->gr_gid); + idvec_add (key == 'g' ? gids : aux_gids, gr->gr_gid); } break; @@ -219,71 +373,213 @@ main(int argc, char *argv[]) } struct argp argp = {options, parse_opt, args_doc, doc}; + /* Don't allow logins if the nologin file exists. */ + fd = open (_PATH_NOLOGIN, O_RDONLY); + if (fd >= 0) + { + cat (fd, _PATH_NOLOGIN); + exit (40); + } + + /* Put in certain last-ditch defaults. */ + err = argz_create (default_args, &args_defs, &args_defs_len); + if (! err) + err = argz_create (default_env, &env_defs, &env_defs_len); + if (err) + error (23, err, "adding defaults"); + + err = argz_create (environ, &parent_env, &parent_env_len); + + /* Parse our options. */ argp_parse (&argp, argc, argv, 0, 0); - if (! shell) - shell = DEFAULT_SHELL; - if (! arg0) - arg0 = shell; - if (! home) - home = DEFAULT_HOME; + /* Now that we've parsed the command line, put together all these + environments we've gotten from various places. There are two targets: + (1) the login parameters, and (2) the child environment. + + The login parameters come from these sources (in priority order): + a) User specified (with the --arg option) + b) From the passwd file entry for the user being logged in as + c) From the parent environment, if --no-environ wasn't specified + d) From the user-specified defaults (--arg-default) + e) From last-ditch defaults given by the DEFAULT_* defines above + + The child environment is from: + a) User specified (--environ) + b) From the login parameters (if --no-args wasn't specified) + c) From the parent environment, if --inherit-environ was specified + d) From the user-specified default env values (--environ-default) + e) From last-ditch defaults given by the DEFAULT_* defines above + */ + + /* Merge the login parameters. */ + err = envz_merge (&args, &args_len, passwd, passwd_len, 0); + if (! err && ! no_environ) + err = envz_merge (&args, &args_len, parent_env, parent_env_len, 0); + if (! err) + err = envz_merge (&args, &args_len, args_defs, args_defs_len, 0); + if (err) + error (24, err, "merging parameters"); + + /* Verify the shell and home dir parameters. We make a copy of SHELL, as + we may frob ARGS ahead, and mess up where it's pointing. */ + shell = strdup (envz_get (args, args_len, "SHELL")); + home = envz_get (args, args_len, "HOME"); + root = envz_get (args, args_len, "ROOT"); - argz_add (&argz, &argz_len, arg0); - argz_append (&argz, &argz_len, args, args_len); + exec_node = file_name_lookup (shell, O_EXEC, 0); + if (exec_node == MACH_PORT_NULL) + { + err = errno; /* Save original lookup errno. */ - if (! no_environ) + if (strcmp (shell, FAILURE_SHELL) != 0) + exec_node = file_name_lookup (FAILURE_SHELL, O_EXEC, 0); + + /* Give the error message, but only exit if we couldn't default. */ + error (exec_node == MACH_PORT_NULL ? 1 : 0, err, "%s", shell); + + /* If we get here, we looked up the default shell ok. */ + shell = FAILURE_SHELL; + error (0, 0, "Using SHELL=%s", shell); + envz_add (&args, &args_len, "SHELL", shell); + } + + if (home && *home) { - char *entry; - asprintf (&entry, "HOME=%s", home); - argz_add (&env, &env_len, entry); - free (entry); - asprintf (&entry, "SHELL=%s", shell); - argz_add (&env, &env_len, entry); - free (entry); - if (path) + home_node = file_name_lookup (home, O_RDONLY, 0); + if (home_node == MACH_PORT_NULL) { - asprintf (&entry, "PATH=%s", path); - argz_add (&env, &env_len, entry); - free (entry); + error (0, errno, "%s", home); + error (0, 0, "Using HOME=/"); + home_node = getcrdir (); + envz_add (&args, &args_len, "HOME", "/"); } } + else + home_node = getcwdir (); - exec_node = file_name_lookup (shell, O_EXEC, 0); - if (exec_node == MACH_PORT_NULL) - error (1, errno, "%s", shell); + if (root && *root) + { + root_node = file_name_lookup (root, O_RDONLY, 0); + if (root_node == MACH_PORT_NULL) + error (40, errno, "%s", root); + } + else + root_node = getcrdir (); - home_node = file_name_lookup (home, O_RDONLY, 0); - if (home_node == MACH_PORT_NULL) - error (2, errno, "%s", home); + /* Build the child environment. */ + if (! no_args) + /* We can't just merge ARGS, because it may contain the parent + environment, which we don't always want in the child environment, so + we pick out only those values of args which actually *are* args. */ + { + char **name; + for (name = copied_args; *name && !err; name++) + if (! envz_get (env, env_len, *name)) + { + char *val = envz_get (args, args_len, *name); + if (val && *val) + err = envz_add (&env, &env_len, *name, val); + } + + if (!err && !envz_entry (args, args_len, "LOGNAME")) + /* Copy the user arg into the environment as LOGNAME. */ + { + char *user = envz_get (args, args_len, "USER"); + if (user) + err = envz_add (&env, &env_len, "LOGNAME", user); + } + } + if (! err && inherit_environ) + err = envz_merge (&env, &env_len, parent_env, parent_env_len, 0); + if (! err) + err = envz_merge (&env, &env_len, env_defs, env_defs_len, 0); + if (err) + error (24, err, "building environment"); + + if (! sh_arg0) + /* The shells argv[0] defaults to the basename of the shell. */ + { + char *shell_base = rindex (shell, '/'); + if (shell_base) + shell_base++; + else + shell_base = shell; + + sh_arg0 = malloc (strlen (shell_base) + 2); + if (! sh_arg0) + err = ENOMEM; + else + /* Prepend the name with a `-', as is the odd custom. */ + { + sh_arg0[0] = '-'; + strcpy (sh_arg0 + 1, shell_base); + } + } + if (! err) + err = argz_insert (&sh_args, &sh_args_len, sh_args, sh_arg0); + if (err) + error (21, err, "consing arguments"); err = auth_makeauth (auth_server, 0, MACH_MSG_TYPE_COPY_SEND, 0, - uids->ids, uids->num, gids->ids, gids->num, - aux_uids->ids, aux_uids->num, aux_gids->ids, aux_gids->num, + uids->ids, uids->num, aux_uids->ids, aux_uids->num, + gids->ids, gids->num, aux_gids->ids, aux_gids->num, &auth); if (err) - error (3, err, "authenticating"); + error (3, err, "Can't authenticate"); - bzero (ports, sizeof (*ports) * INIT_PORT_MAX); - ports[INIT_PORT_CRDIR] = getcrdir (); - ports[INIT_PORT_CWDIR] = home_node; - ports[INIT_PORT_AUTH] = auth; - ports[INIT_PORT_PROC] = proc_server; + proc_make_login_coll (proc_server); + if (aux_uids->num > 0) + proc_setowner (proc_server, aux_uids->ids[0]); + else if (uids->num > 0) + proc_setowner (proc_server, uids->ids[0]); + + /* Output the message of the day. */ + hushlogin = envz_get (args, args_len, "HUSHLOGIN"); + if (hushlogin && *hushlogin) + { + mach_port_t hush_login_node = + file_name_lookup_under (home_node, hushlogin, O_RDONLY, 0); + if (hush_login_node == MACH_PORT_NULL) + { + char *motd = envz_get (args, args_len, "MOTD"); + if (motd && *motd) + { + fd = open (motd, O_RDONLY); + if (fd >= 0) + cat (fd, motd); + } + } + else + mach_port_deallocate (mach_task_self (), hush_login_node); + } + + /* Get rid of any accumulated null entries in env. */ + envz_strip (&env, &env_len); bzero (ints, sizeof (*ints) * INIT_INT_MAX); ints[INIT_UMASK] = umask; - dtable[0] = getdport (0); - dtable[1] = getdport (1); - dtable[2] = getdport (2); + dtable[0] = reauth (getdport (0), 0, auth, "standard input"); + dtable[1] = reauth (getdport (1), 0, auth, "standard output"); + dtable[2] = reauth (getdport (2), 0, auth, "standard error"); + + for (i = 0; i < INIT_PORT_MAX; i++) + ports[i] = MACH_PORT_NULL; + ports[INIT_PORT_CRDIR] = reauth (root_node, 0, auth, "root directory"); + ports[INIT_PORT_CWDIR] = reauth (home_node, 0, auth, "home directory"); + ports[INIT_PORT_AUTH] = auth; + ports[INIT_PORT_PROC] = reauth (proc_server, 1, auth, "process port"); + + /* No more authentications to fail, so cross our fingers and add our utmp + entry. */ + if (! no_utmp) + add_utmp_entry (args, args_len, 0); err = file_exec (exec_node, mach_task_self (), -#if 0 EXEC_NEWTASK | EXEC_DEFAULTS | EXEC_SECURE, -#else - 0, -#endif - argz, argz_len, env, env_len, + sh_args, sh_args_len, env, env_len, dtable, MACH_MSG_TYPE_COPY_SEND, 3, ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX, ints, INIT_INT_MAX, |