X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/ed2fd28a143d5a6a2a7c16791ad7edb2966460e4..abc4056e93f8780d7cafe28754a1f7f211f948d6:/usr/src/usr.bin/login/login.c?ds=inline diff --git a/usr/src/usr.bin/login/login.c b/usr/src/usr.bin/login/login.c index 1bc2e2623b..ae1f834611 100644 --- a/usr/src/usr.bin/login/login.c +++ b/usr/src/usr.bin/login/login.c @@ -1,267 +1,520 @@ -static char *sccsid = "@(#)login.c 4.6 (Berkeley) %G%"; +/* + * Copyright (c) 1980, 1987, 1988 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1980, 1987, 1988 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)login.c 5.28 (Berkeley) %G%"; +#endif /* not lint */ + /* * login [ name ] + * login -h hostname (for telnetd, etc.) + * login -f name (for pre-authenticated login: datakit, xterm, etc.) */ -#include -#include +#include +#include +#include +#include +#include +#include +#include + #include #include +#include +#include +#include +#include +#include #include +#include #include -#include -#include -#include -#define SCPYN(a, b) strncpy(a, b, sizeof(a)) - -#define NMAX sizeof(utmp.ut_name) -#define LMAX sizeof(utmp.ut_line) - -char user[20]; -char maildir[30] = "/usr/spool/mail/"; -char lastlog[] = "/usr/adm/lastlog"; -struct passwd nouser = {"", "nope"}; -struct sgttyb ttyb; -struct utmp utmp; -char minusnam[16] = "-"; -char homedir[64] = "HOME="; -char shell[64] = "SHELL="; -char term[64] = "TERM="; -char *envinit[] = {homedir, shell, "PATH=:/usr/ucb:/bin:/usr/bin", term, user,0}; -struct passwd *pwd; - -struct passwd *getpwnam(); -char *strcat(); -int setpwent(); -char *ttyname(); -char *crypt(); -char *getpass(); -char *rindex(); -char *stypeof(); -extern char **environ; - -#define CTRL(c) ('c'&037) -#define CERASE '#' -#define CEOT CTRL(d) -#define CKILL '@' -#define CQUIT 034 /* FS, cntl shift L */ -#define CINTR 0177 /* DEL */ -#define CSTOP CTRL(s) -#define CSTART CTRL(q) -#define CBRK 0377 -struct tchars tc = { +#include + +#define TTYGRPNAME "tty" /* name of group to own ttys */ + +#define MOTDFILE "/etc/motd" +#define MAILDIR "/usr/spool/mail" +#define NOLOGIN "/etc/nologin" +#define HUSHLOGIN ".hushlogin" +#define LASTLOG "/usr/adm/lastlog" +#define BSHELL "/bin/sh" + +/* + * This bounds the time given to login. Not a define so it can + * be patched on machines where it's too small. + */ +int timeout = 300; + +struct passwd *pwd; +int repeatcnt; +char term[64], *hostname, *username, *tty; + +struct sgttyb sgttyb; +struct tchars tc = { CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK }; -struct ltchars ltc = { - CTRL(z), CTRL(y), CTRL(r), CTRL(o), CTRL(w), CTRL(v) +struct ltchars ltc = { + CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT }; main(argc, argv) -char **argv; + int argc; + char **argv; { - register char *namep; - int t, f, c; - char *ttyn; - int ldisc = 0; - - alarm(60); - signal(SIGQUIT, SIG_IGN); - signal(SIGINT, SIG_IGN); - nice(-100); - nice(20); - nice(0); - ioctl(0, TIOCLSET, 0); - ioctl(0, TIOCNXCL, 0); - gtty(0, &ttyb); - ttyb.sg_erase = '#'; - ttyb.sg_kill = '@'; - stty(0, &ttyb); - ioctl(0, TIOCSETC, &tc); - ioctl(0, TIOCSLTC, <c); - for (t=3; t<20; t++) - close(t); + extern int errno, optind; + extern char *optarg, **environ; + struct group *gr; + register int ch; + register char *p; + int ask, fflag, hflag, pflag, cnt; + int quietlog, passwd_req, ioctlval, timedout(); + char *domain, *salt, *envinit[1], *ttyn; + char tbuf[MAXPATHLEN + 2]; + char *ttyname(), *stypeof(), *crypt(), *getpass(); + time_t time(); + off_t lseek(); + + (void)signal(SIGALRM, timedout); + (void)alarm((u_int)timeout); + (void)signal(SIGQUIT, SIG_IGN); + (void)signal(SIGINT, SIG_IGN); + (void)setpriority(PRIO_PROCESS, 0, 0); + (void)quota(Q_SETUID, 0, 0, 0); + + /* + * -p is used by getty to tell login not to destroy the environment + * -f is used to skip a second login authentication + * -h is used by other servers to pass the name of the remote + * host to login so that it may be placed in utmp and wtmp + */ + (void)gethostname(tbuf, sizeof(tbuf)); + domain = index(tbuf, '.'); + + fflag = hflag = pflag = 0; + passwd_req = 1; + while ((ch = getopt(argc, argv, "fh:p")) != EOF) + switch (ch) { + case 'f': + fflag = 1; + break; + case 'h': + if (getuid()) { + fprintf(stderr, + "login: -h for super-user only.\n"); + exit(1); + } + hflag = 1; + if (domain && (p = index(optarg, '.')) && + strcasecmp(p, domain) == 0) + *p = 0; + hostname = optarg; + break; + case 'p': + pflag = 1; + break; + case '?': + default: + fprintf(stderr, "usage: login [-fp] [username]\n"); + exit(1); + } + argc -= optind; + argv += optind; + if (*argv) { + ask = 0; + username = *argv; + } + else + ask = 1; + + ioctlval = 0; + (void)ioctl(0, TIOCLSET, &ioctlval); + (void)ioctl(0, TIOCNXCL, 0); + (void)fcntl(0, F_SETFL, ioctlval); + (void)ioctl(0, TIOCGETP, &sgttyb); + sgttyb.sg_erase = CERASE; + sgttyb.sg_kill = CKILL; + (void)ioctl(0, TIOCSLTC, <c); + (void)ioctl(0, TIOCSETC, &tc); + (void)ioctl(0, TIOCSETP, &sgttyb); + + for (cnt = getdtablesize(); cnt > 2; cnt--) + close(cnt); + ttyn = ttyname(0); - if (ttyn==0) + if (ttyn == NULL || *ttyn == '\0') ttyn = "/dev/tty??"; + if (tty = rindex(ttyn, '/')) + ++tty; + else + tty = ttyn; - loop: - ldisc = 0; - ioctl(0, TIOCSETD, &ldisc); - SCPYN(utmp.ut_name, ""); - if (argc>1) { - SCPYN(utmp.ut_name, argv[1]); - argc = 0; - } - while (utmp.ut_name[0] == '\0') { - namep = utmp.ut_name; - printf("login: "); - while ((c = getchar()) != '\n') { - if(c == ' ') - c = '_'; - if (c == EOF) - exit(0); - if (namep < utmp.ut_name+NMAX) - *namep++ = c; + openlog("login", LOG_ODELAY, LOG_AUTH); + + for (cnt = 0;; ask = 1) { + ioctlval = 0; + (void)ioctl(0, TIOCSETD, &ioctlval); + + if (ask) { + fflag = 0; + getloginname(); } - } - setpwent(); - if ((pwd = getpwnam(utmp.ut_name)) == NULL) - pwd = &nouser; - endpwent(); - if (!strcmp(pwd->pw_shell, "/bin/csh")) { - ldisc = NTTYDISC; - ioctl(0, TIOCSETD, &ldisc); - } - if (*pwd->pw_passwd != '\0') { - nice(-4); - namep = crypt(getpass("Password:"),pwd->pw_passwd); - nice(4); - if (strcmp(namep, pwd->pw_passwd)) { -bad: - printf("Login incorrect\n"); - if (ttyn[LMAX] == 'd') { - FILE *console = fopen("/dev/console", "w"); - if (console != NULL) { - fprintf(console, "\r\nBADDIALUP %s %s\r\n", ttyn+5, utmp.ut_name); - fclose(console); - } + /* note if trying multiple login's */ + if (repeatcnt) { + if (strcmp(tbuf, username)) { + badlogin(tbuf); + repeatcnt = 1; + (void)strcpy(tbuf, username); } - goto loop; + else + ++repeatcnt; } - } - sprintf(user, "USER=%.*s", NMAX, pwd->pw_name); -#ifdef ERNIE - if (pwd->pw_uid == 0 && ttyn[5] != 'c') - goto bad; -#endif - if (ttyn[LMAX] == 'd') { - FILE *console = fopen("/dev/console", "w"); - if (console != NULL) { - fprintf(console, "\r\nDIALUP %s %s\r\n", ttyn+5, pwd->pw_name); - fclose(console); + else { + repeatcnt = 1; + (void)strcpy(tbuf, username); + } + if (pwd = getpwnam(username)) + salt = pwd->pw_passwd; + else + salt = "xx"; + + /* if user not super-user, check for disabled logins */ + if (pwd == NULL || pwd->pw_uid) + checknologin(); + + /* + * Disallow automatic login to root; if not invoked by + * root, disallow if the uid's differ. + */ + if (fflag && pwd) { + int uid = getuid(); + + passwd_req = pwd->pw_uid == 0 || + (uid && uid != pwd->pw_uid); + } + + /* + * If no pre-authentication and a password exists + * for this user, prompt for one and verify it. + */ + if (!passwd_req || pwd && !*pwd->pw_passwd) + break; + + setpriority(PRIO_PROCESS, 0, -4); + p = crypt(getpass("Password:"), salt); + setpriority(PRIO_PROCESS, 0, 0); + if (pwd && !strcmp(p, pwd->pw_passwd)) + break; + + printf("Login incorrect\n"); + /* we allow 10 tries, but after 3 we start backing off */ + if (++cnt > 3) { + if (cnt >= 10) { + badlogin(username); + (void)ioctl(0, TIOCHPCL, (struct sgttyb *)NULL); + sleepexit(1); + } + sleep((u_int)((cnt - 3) * 5)); } } - if((f = open(lastlog, 2)) >= 0) { - struct lastlog ll; - - lseek(f, (long) pwd->pw_uid * sizeof (struct lastlog), 0); - if (read(f, (char *) &ll, sizeof ll) == sizeof ll && ll.ll_time != 0) { - register char *ep = (char *) ctime(&ll.ll_time); - printf("Last login: "); - ep[24 - 5] = 0; - printf("%s on %.*s\n", ep, LMAX, ll.ll_line); + + /* committed to login -- turn off timeout */ + (void)alarm((u_int)0); + + /* log any mistakes -- don't count last one */ + --repeatcnt; + badlogin(username); + + /* + * If valid so far and root is logging in, see if root logins on + * this terminal are permitted. + */ + if (pwd->pw_uid == 0 && !rootterm()) { + if (hostname) + syslog(LOG_ERR, "ROOT LOGIN REFUSED ON %s FROM %s", + tty, hostname); + else + syslog(LOG_ERR, "ROOT LOGIN REFUSED ON %s", tty); + printf("Login incorrect\n"); + sleepexit(1); + } + + if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0 && errno != EINVAL) { + switch(errno) { + case EUSERS: + fprintf(stderr, + "Too many users logged on already.\nTry again later.\n"); + break; + case EPROCLIM: + fprintf(stderr, + "You have too many processes running.\n"); + break; + default: + perror("quota (Q_SETUID)"); } - lseek(f, (long) pwd->pw_uid * sizeof (struct lastlog), 0); - time(&ll.ll_time); - strcpyn(ll.ll_line, ttyn+5, LMAX); - write(f, (char *) &ll, sizeof ll); - close(f); + sleepexit(0); } - if(chdir(pwd->pw_dir) < 0) { - printf("No directory\n"); - goto loop; + + if (chdir(pwd->pw_dir) < 0) { + printf("No directory %s!\n", pwd->pw_dir); + if (chdir("/")) + exit(0); + pwd->pw_dir = "/"; + printf("Logging in with home = \"/\".\n"); } - time(&utmp.ut_time); - t = ttyslot(); - if (t>0 && (f = open("/etc/utmp", 1)) >= 0) { - lseek(f, (long)(t*sizeof(utmp)), 0); - SCPYN(utmp.ut_line, rindex(ttyn, '/')+1); - write(f, (char *)&utmp, sizeof(utmp)); - close(f); + + /* nothing else left to fail -- really log in */ + { + struct utmp utmp; + + (void)time(&utmp.ut_time); + strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); + strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); + strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); + login(&utmp); } - if (t>0 && (f = open("/usr/adm/wtmp", 1)) >= 0) { - lseek(f, 0L, 2); - write(f, (char *)&utmp, sizeof(utmp)); - close(f); + + quietlog = access(HUSHLOGIN, F_OK) == 0; + dolastlog(quietlog); + + if (!hflag) { /* XXX */ + static struct winsize win = { 0, 0, 0, 0 }; + + (void)ioctl(0, TIOCSWINSZ, &win); } - chown(ttyn, pwd->pw_uid, pwd->pw_gid); - setgid(pwd->pw_gid); - setuid(pwd->pw_uid); + + (void)chown(ttyn, pwd->pw_uid, + (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); + (void)chmod(ttyn, 0620); + (void)setgid(pwd->pw_gid); + + initgroups(username, pwd->pw_gid); + + quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0); + (void)setuid(pwd->pw_uid); + if (*pwd->pw_shell == '\0') - pwd->pw_shell = "/bin/sh"; - environ = envinit; - strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); - strncat(shell, pwd->pw_shell, sizeof(shell)-7); - strncat(term, stypeof(ttyn), sizeof(term)-6); - if ((namep = rindex(pwd->pw_shell, '/')) == NULL) - namep = pwd->pw_shell; - else - namep++; - strcat(minusnam, namep); - alarm(0); - umask(022); - showmotd(); - strcat(maildir, pwd->pw_name); - if(access(maildir,4)==0) { - struct stat statb; - stat(maildir, &statb); - if (statb.st_size) - printf("You have mail.\n"); + pwd->pw_shell = BSHELL; + /* turn on new line discipline for the csh */ + else if (!strcmp(pwd->pw_shell, "/bin/csh")) { + ioctlval = NTTYDISC; + (void)ioctl(0, TIOCSETD, &ioctlval); } - signal(SIGQUIT, SIG_DFL); - signal(SIGINT, SIG_DFL); - execlp(pwd->pw_shell, minusnam, 0); - printf("No shell\n"); + + /* destroy environment unless user has requested preservation */ + if (!pflag) + environ = envinit; + (void)setenv("HOME", pwd->pw_dir, 1); + (void)setenv("SHELL", pwd->pw_shell, 1); + if (term[0] == '\0') + strncpy(term, stypeof(), sizeof(term)); + (void)setenv("TERM", term, 0); + (void)setenv("USER", pwd->pw_name, 1); + (void)setenv("PATH", "/usr/ucb:/bin:/usr/bin:", 0); + + if (tty[sizeof("tty")-1] == 'd') + syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); + if (pwd->pw_uid == 0) + if (hostname) + syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %s", + tty, hostname); + else + syslog(LOG_NOTICE, "ROOT LOGIN %s", tty); + + if (!quietlog) { + struct stat st; + + motd(); + (void)sprintf(tbuf, "%s/%s", MAILDIR, pwd->pw_name); + if (stat(tbuf, &st) == 0 && st.st_size != 0) + printf("You have %smail.\n", + (st.st_mtime > st.st_atime) ? "new " : ""); + } + + (void)signal(SIGALRM, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGTSTP, SIG_IGN); + + tbuf[0] = '-'; + strcpy(tbuf + 1, (p = rindex(pwd->pw_shell, '/')) ? + p + 1 : pwd->pw_shell); + execlp(pwd->pw_shell, tbuf, 0); + fprintf(stderr, "login: no shell: "); + perror(pwd->pw_shell); exit(0); } -int stopmotd; -catch() +getloginname() { - signal(SIGINT, SIG_IGN); - stopmotd++; + register int ch; + register char *p; + static char nbuf[UT_NAMESIZE + 1]; + + for (;;) { + printf("login: "); + for (p = nbuf; (ch = getchar()) != '\n'; ) { + if (ch == EOF) { + badlogin(username); + exit(0); + } + if (p < nbuf + UT_NAMESIZE) + *p++ = ch; + } + if (p > nbuf) + if (nbuf[0] == '-') + fprintf(stderr, + "login names may not start with '-'.\n"); + else { + *p = '\0'; + username = nbuf; + break; + } + } +} + +timedout() +{ + fprintf(stderr, "Login timed out after %d seconds\n", timeout); + exit(0); } -showmotd() +rootterm() { - FILE *mf; - register c; - - signal(SIGINT, catch); - if((mf = fopen("/etc/motd","r")) != NULL) { - while((c = getc(mf)) != EOF && stopmotd == 0) - putchar(c); - fclose(mf); + struct ttyent *t; + + return((t = getttynam(tty)) && t->ty_status&TTY_SECURE); +} + +jmp_buf motdinterrupt; + +motd() +{ + register int fd, nchars; + int (*oldint)(), sigint(); + char tbuf[8192]; + + if ((fd = open(MOTDFILE, O_RDONLY, 0)) < 0) + return; + oldint = signal(SIGINT, sigint); + if (setjmp(motdinterrupt) == 0) + while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) + (void)write(fileno(stdout), tbuf, nchars); + (void)signal(SIGINT, oldint); + (void)close(fd); +} + +sigint() +{ + longjmp(motdinterrupt, 1); +} + +checknologin() +{ + register int fd, nchars; + char tbuf[8192]; + + if ((fd = open(NOLOGIN, O_RDONLY, 0)) >= 0) { + while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) + (void)write(fileno(stdout), tbuf, nchars); + sleepexit(0); + } +} + +dolastlog(quiet) + int quiet; +{ + struct lastlog ll; + int fd; + + if ((fd = open(LASTLOG, O_RDWR, 0)) >= 0) { + (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); + if (!quiet) { + if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && + ll.ll_time != 0) { + printf("Last login: %.*s ", + 24-5, (char *)ctime(&ll.ll_time)); + if (*ll.ll_host != '\0') + printf("from %.*s\n", + sizeof(ll.ll_host), ll.ll_host); + else + printf("on %.*s\n", + sizeof(ll.ll_line), ll.ll_line); + } + (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); + } + (void)time(&ll.ll_time); + strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); + strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); + (void)write(fd, (char *)&ll, sizeof(ll)); + (void)close(fd); } - signal(SIGINT, SIG_IGN); } -#define UNKNOWN "su" +badlogin(name) + char *name; +{ + if (!repeatcnt) + return; + if (hostname) + syslog(LOG_ERR, "%d LOGIN FAILURE%s ON %s FROM %s, %s", + repeatcnt, repeatcnt > 1 ? "S" : "", tty, hostname, name); + else + syslog(LOG_ERR, "%d LOGIN FAILURE%s ON %s, %s", + repeatcnt, repeatcnt > 1 ? "S" : "", tty, name); +} + +#undef UNKNOWN +#define UNKNOWN "su" char * -stypeof(ttyid) -char *ttyid; +stypeof() { - static char typebuf[16]; - char buf[50]; - register FILE *f; - register char *p, *t, *q; - - if (ttyid == NULL) - return (UNKNOWN); - f = fopen("/etc/ttytype", "r"); - if (f == NULL) - return (UNKNOWN); - /* split off end of name */ - for (p = q = ttyid; *p != 0; p++) - if (*p == '/') - q = p + 1; - - /* scan the file */ - while (fgets(buf, sizeof buf, f) != NULL) - { - for (t=buf; *t!=' '; t++) - ; - *t++ = 0; - for (p=t; *p>' '; p++) - ; - *p = 0; - if (strcmp(q,t)==0) { - strcpy(typebuf, buf); - fclose(f); - return (typebuf); + struct ttyent *t; + + return(tty && (t = getttynam(tty)) ? t->ty_type : UNKNOWN); +} + +getstr(buf, cnt, err) + char *buf, *err; + int cnt; +{ + char ch; + + do { + if (read(0, &ch, sizeof(ch)) != sizeof(ch)) + exit(1); + if (--cnt < 0) { + fprintf(stderr, "%s too long\r\n", err); + sleepexit(1); } - } - fclose (f); - return (UNKNOWN); + *buf++ = ch; + } while (ch); +} + +sleepexit(eval) + int eval; +{ + sleep((u_int)5); + exit(eval); }