minix/commands/simple/login.c
Thomas Veerman 958b25be50 - Introduce support for sticky bit.
- Revise VFS-FS protocol and update VFS/MFS/ISOFS accordingly.
- Clean up MFS by removing old, dead code (backwards compatibility is broken by
  the new VFS-FS protocol, anyway) and rewrite other parts. Also, make sure all
  functions have proper banners and prototypes.
- VFS should always provide a (syntactically) valid path to the FS; no need for
  the FS to do sanity checks when leaving/entering mount points.
- Fix several bugs in MFS:
  - Several path lookup bugs in MFS.
  - A link can be too big for the path buffer.
  - A mountpoint can become inaccessible when the creation of a new inode
    fails, because the inode already exists and is a mountpoint.
- Introduce support for supplemental groups.
- Add test 46 to test supplemental group functionality (and removed obsolete
  suppl. tests from test 2).
- Clean up VFS (not everything is done yet).
- ISOFS now opens device read-only. This makes the -r flag in the mount command
  unnecessary (but will still report to be mounted read-write).
- Introduce PipeFS. PipeFS is a new FS that handles all anonymous and
  named pipes. However, named pipes still reside on the (M)FS, as they are part
  of the file system on disk. To make this work VFS now has a concept of
  'mapped' inodes, which causes read, write, truncate and stat requests to be
  redirected to the mapped FS, and all other requests to the original FS.
2009-12-20 20:27:14 +00:00

507 lines
12 KiB
C

/* login - log into the system Author: Patrick van Kleef */
/* Original version by Patrick van Kleef. History of modifications:
*
* Peter S. Housel Jan. 1988
* - Set up $USER, $HOME and $TERM.
* - Set signals to SIG_DFL.
*
* Terrence W. Holm June 1988
* - Allow a username as an optional argument.
* - Time out if a password is not typed within 60 seconds.
* - Perform a dummy delay after a bad username is entered.
* - Don't allow a login if "/etc/nologin" exists.
* - Cause a failure on bad "pw_shell" fields.
* - Record the login in "/usr/adm/wtmp".
*
* Peter S. Housel Dec. 1988
* - Record the login in "/etc/utmp" also.
*
* F. van Kempen June 1989
* - various patches for Minix V1.4a.
*
* F. van Kempen September 1989
* - added login-failure administration (new utmp.h needed!).
* - support arguments in pw_shell field
* - adapted source text to MINIX Style Sheet
*
* F. van Kempen October 1989
* - adapted to new utmp database.
* F. van Kempen, December 1989
* - fixed 'slot' assumption in wtmp()
* - fixed all MSS-stuff
* - adapted to POSIX (MINIX 1.5)
* F. van Kempen, January 1990
* - made all 'bad login accounting' optional by "#ifdef BADLOG".
* F. van Kempen, Februari 1990
* - fixed 'first argument' bug and added some casts.
*
* Andy Tanenbaum April 1990
* - if /bin/sh cannot be located, try /usr/bin/sh
*
* Michael A. Temari October 1990
* - handle more than single digit tty devices
*
* Philip Homburg - Feb 28 1992
* - use ttyname to get the name of a tty.
*
* Kees J. Bot - Feb 13 1993
* - putting out garbage.
* - added lastlog.
*
* Kees J. Bot - Feb 13 1993
* - supplementary groups.
*
* Kees J. Bot - Jan 3 1996
* - ported back to standard Minix.
*/
#define _MINIX_SOURCE
#define _POSIX_C_SOURCE 2
#include <sys/types.h>
#include <ttyent.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <utmp.h>
#include <time.h>
#include <sys/utsname.h>
#include <minix/minlib.h>
#include <minix/paths.h>
char PATH_UTMP[] = _PATH_UTMP; /* current logins */
char PATH_WTMP[] = _PATH_WTMP; /* login/logout history */
char PATH_LASTLOG[] = _PATH_LASTLOG; /* last login history */
char PATH_MOTD[] = _PATH_MOTD; /* message of the day */
#define TTY_GID 4 /* group ID of ttys */
#define EXTRA_ENV 6
/* Crude indication of a tty being physically secure: */
#define securetty(dev) ((unsigned) ((dev) - 0x0400) < (unsigned) 8)
int time_out;
char *hostname;
char user[32];
char logname[35];
char home[128];
char shell[128];
char term[128];
char **env;
extern char **environ;
_PROTOTYPE(int main, (int argc, char **argv));
_PROTOTYPE(void wtmp, (char *user, int uid));
_PROTOTYPE(void show_file, (char *nam));
_PROTOTYPE(void Time_out, (int dummy));
_PROTOTYPE(void usage, (void));
_PROTOTYPE(void add2env, (char **env, char *entry, int replace));
void wtmp(user, uid)
char *user; /* user name */
int uid; /* user id */
{
/* Make entries in /usr/adm/wtmp and /etc/utmp. */
struct utmp entry;
register int fd= -1;
int lineno;
int err = 0;
char *what;
/* First, read the current UTMP entry. we need some of its
* parameters! (like PID, ID etc...).
*/
what= "ttyslot()";
lineno= ttyslot();
if (lineno == 0) err= errno; /* ttyslot failed */
if (err == 0 && (fd = open(what = PATH_UTMP, O_RDONLY)) < 0) {
if (errno == ENOENT) return;
err= errno;
}
if (err == 0 && lseek(fd, (off_t) lineno * sizeof(entry), SEEK_SET) < 0)
err= errno;
if (err == 0 && read(fd, (char *) &entry, sizeof(entry)) != sizeof(entry))
err= errno;
if (fd >= 0) close(fd);
/* Enter new fields. */
strncpy(entry.ut_user, user, sizeof(entry.ut_user));
if (hostname) strncpy(entry.ut_host, hostname, sizeof(entry.ut_host));
if (entry.ut_pid == 0) entry.ut_pid = getpid();
entry.ut_type = USER_PROCESS; /* we are past login... */
time(&entry.ut_time);
/* Write a WTMP record. */
if (err == 0) {
if ((fd = open(what = PATH_WTMP, O_WRONLY|O_APPEND)) < 0) {
if (errno != ENOENT) err= errno;
} else {
if (write(fd, (char *) &entry, sizeof(entry)) < 0) err= errno;
close(fd);
}
}
/* Rewrite the UTMP entry. */
if (err == 0 && (fd = open(what = PATH_UTMP, O_WRONLY)) < 0)
err= errno;
if (err == 0 && lseek(fd, (off_t) lineno * sizeof(entry), SEEK_SET) < 0)
err= errno;
if (err == 0 && write(fd, (char *) &entry, sizeof(entry)) < 0)
err= errno;
if (fd >= 0) close(fd);
/* Write the LASTLOG entry. */
if (err == 0 && (fd = open(what = PATH_LASTLOG, O_WRONLY)) < 0) {
if (errno == ENOENT) return;
err= errno;
}
if (err == 0 && lseek(fd, (off_t) uid * sizeof(entry), SEEK_SET) < 0)
err= errno;
if (err == 0 && write(fd, (char *) &entry, sizeof(entry)) < 0)
err= errno;
if (fd >= 0) close(fd);
if (err != 0) {
fprintf(stderr, "login: %s: %s\n", what, strerror(err));
return;
}
}
void show_file(nam)
char *nam;
{
/* Read a textfile and show it on the desired terminal. */
register int fd, len;
char buf[80];
if ((fd = open(nam, O_RDONLY)) > 0) {
len = 1;
while (len > 0) {
len = read(fd, buf, 80);
write(1, buf, len);
}
close(fd);
}
}
int main(argc, argv)
int argc;
char *argv[];
{
char name[30];
char *password, *cryptedpwd;
char *tty_name, *p;
int n, ap, check_pw, bad, secure, i, envsiz, do_banner;
struct passwd *pwd;
char *bp, *argx[8], **ep; /* pw_shell arguments */
char argx0[64]; /* argv[0] of the shell */
char *sh = "/bin/sh"; /* sh/pw_shell field value */
char *initialname;
int c, b_flag, f_flag, p_flag;
char *h_arg;
int authorized, preserv_env;
struct ttyent *ttyp;
struct stat ttystat;
struct sigaction sa;
struct utsname uts;
/* Don't let QUIT dump core. */
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = exit;
sigaction(SIGQUIT, &sa, NULL);
/* Parse options. */
b_flag= 0;
f_flag= 0;
p_flag= 0;
h_arg= NULL;
while ((c= getopt(argc, argv, "?bfh:p")) != -1)
{
switch(c)
{
case 'b': b_flag= 1; break;
case 'f': f_flag= 1; break;
case 'h':
if (h_arg)
usage();
if (getuid() == 0)
h_arg= optarg;
break;
case 'p': p_flag= 1; break;
case '?':
usage();
default:
fprintf(stderr, "login: getopt failed: '%c'\n", c);
exit(1);
}
}
if (optind < argc)
initialname= argv[optind++];
else
initialname= NULL;
if (optind != argc)
usage();
authorized= f_flag;
hostname= h_arg;
preserv_env= p_flag;
do_banner= b_flag;
/* Look up /dev/tty number. */
tty_name= ttyname(0);
if (tty_name == NULL)
{
write(1, "Unable to lookup tty name\n", 26);
exit(1);
}
if (do_banner)
{
uname(&uts);
write(1, "\n", 1);
write(1, uts.sysname, strlen(uts.sysname));
write(1, "/", 1);
write(1, uts.machine, strlen(uts.machine));
write(1, " Release ", 9);
write(1, uts.release, strlen(uts.release));
write(1, " Version ", 9);
write(1, uts.version, strlen(uts.version));
write(1, " (", 2);
p= strrchr(tty_name, '/');
if (!p)
p= tty_name;
else
p++;
write(1, p, strlen(p));
write(1, ")\n\n", 3);
write(1, uts.nodename, strlen(uts.nodename));
write(1, " ", 1);
}
/* Get login name and passwd. */
for (;;initialname= NULL) {
if (initialname)
strcpy(name, initialname);
else {
do {
write(1, "login: ", 7);
n = read(0, name, 30);
if (n == 0) exit(1);
if (n < 0)
{
if (errno != EINTR)
fprintf(stderr,
"login: read failed: %s\n",
strerror(errno));
exit(1);
}
} while (n < 2);
name[n - 1] = 0;
}
/* Start timer running. */
time_out = 0;
sa.sa_handler = Time_out;
sigaction(SIGALRM, &sa, NULL);
alarm(60);
/* Look up login/passwd. */
pwd = getpwnam(name);
check_pw = 1; /* default is check password. */
/* For now, only console is secure. */
secure = fstat(0, &ttystat) == 0 && securetty(ttystat.st_rdev);
if (pwd && authorized && initialname
&& (pwd->pw_uid == getuid() || getuid() == 0)) {
check_pw= 0; /* Don't ask a password for
* pre-authorized users.
*/
} else
if (pwd && secure && strcmp(crypt("", pwd->pw_passwd),
pwd->pw_passwd) == 0) {
check_pw= 0; /* empty password */
}
if (check_pw) {
password = getpass("Password:");
if (time_out) exit(1);
bad = 0;
if (!pwd) bad = 1;
if (!password) { password = ""; bad = 1; }
if (!secure && pwd && strcmp(crypt("", pwd->pw_passwd),
pwd->pw_passwd) == 0) bad = 1;
cryptedpwd = bad ? "*" : pwd->pw_passwd;
if (strcmp(crypt(password, cryptedpwd), cryptedpwd) != 0) {
write(1, "Login incorrect\n", 16);
continue;
}
}
/* Check if the system is going down */
if (access("/etc/nologin", 0) == 0 && strcmp(name, "root") != 0) {
write(1, "System going down\n\n", 19);
continue;
}
/* Stop timer. */
alarm(0);
/* Write login record to /usr/adm/wtmp and /etc/utmp */
wtmp(name, pwd->pw_uid);
/* Create the argv[] array from the pw_shell field. */
ap = 0;
argx[ap++] = argx0; /* "-sh" most likely */
if (pwd->pw_shell[0]) {
sh = pwd->pw_shell;
bp = sh;
while (*bp) {
while (*bp && *bp != ' ' && *bp != '\t') bp++;
if (*bp == ' ' || *bp == '\t') {
*bp++ = '\0'; /* mark end of string */
argx[ap++] = bp;
}
}
} else
argx[ap] = NULL;
strcpy(argx0, "-"); /* most shells need it for their .profile */
if ((bp= strrchr(sh, '/')) == NULL) bp = sh; else bp++;
strncat(argx0, bp, sizeof(argx0) - 2);
/* Set the environment */
if (p_flag)
{
for (ep= environ; *ep; ep++)
;
}
else
ep= environ;
envsiz= ep-environ;
env= calloc(envsiz + EXTRA_ENV, sizeof(*env));
if (env == NULL)
{
fprintf(stderr, "login: out of memory\n");
exit(1);
}
for (i= 0; i<envsiz; i++)
env[i]= environ[i];
strcpy(user, "USER=");
strcat(user, name);
add2env(env, user, 1);
strcpy(logname, "LOGNAME=");
strcat(logname, name);
add2env(env, logname, 1);
strcpy(home, "HOME=");
strcat(home, pwd->pw_dir);
add2env(env, home, 1);
strcpy(shell, "SHELL=");
strcat(shell, sh);
add2env(env, shell, 1);
if ((ttyp = getttynam(tty_name + 5)) != NULL) {
strcpy(term, "TERM=");
strcat(term, ttyp->ty_type);
add2env(env, term, 0);
}
/* Show the message-of-the-day. */
show_file(PATH_MOTD);
/* Assign the terminal to this user. */
chown(tty_name, pwd->pw_uid, TTY_GID);
chmod(tty_name, 0620);
/* Change id. */
initgroups(pwd->pw_name, pwd->pw_gid);
setgid(pwd->pw_gid);
setuid(pwd->pw_uid);
/* cd $HOME */
chdir(pwd->pw_dir);
/* Reset signals to default values. */
sa.sa_handler = SIG_DFL;
for (n = 1; n < _NSIG; ++n) sigaction(n, &sa, NULL);
/* Execute the user's shell. */
execve(sh, argx, env);
if (pwd->pw_gid == 0) {
/* Privileged user gets /bin/sh in times of crisis. */
sh= "/bin/sh";
argx[0]= "-sh";
strcpy(shell, "SHELL=");
strcat(shell, sh);
execve(sh, argx, env);
}
fprintf(stderr, "login: can't execute %s: %s\n", sh, strerror(errno));
exit(1);
}
return(0);
}
void Time_out(dummy)
int dummy; /* to keep the compiler happy */
{
write(2, "\r\nLogin timed out after 60 seconds\r\n", 36);
time_out = 1;
}
void usage()
{
fprintf(stderr,
"Usage: login [-h hostname] [-b] [-f] [-p] [username]\n");
exit(1);
}
void add2env(env, entry, replace)
char **env;
char *entry;
int replace;
{
/* Replace an environment variable with entry or add entry if the environment
* variable doesn't exit yet.
*/
char *cp;
int keylen;
cp= strchr(entry, '=');
keylen= cp-entry+1;
for(; *env; env++)
{
if (strncmp(*env, entry, keylen) == 0) {
if (!replace) return; /* Don't replace */
break;
}
}
*env= entry;
}
/*
* $PchId: login.c,v 1.6 2001/07/31 14:23:28 philip Exp $
*/