/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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