/* * login [ name ] * login [ name.group ] * login -r hostname (for rlogind) * login -h hostname (for telnetd, etc.) */ #define LOGERR /* log all errors to the console */ #define GROUP /* allow login as name.group */ #define SP_SESS /* "special session"-- root logins only */ /* used after autoreboot failures */ #include <whoami.h> #include <sys/types.h> #include <sgtty.h> #include <utmp.h> #include <signal.h> #include <pwd.h> #include <stdio.h> #include <sys/stat.h> #include <lastlog.h> #include <ctype.h> #ifdef GROUP #include <grp.h> #endif /* default path */ #define PATH "PATH=:/usr/ucb:/bin:/usr/bin:/usr/local:/usr/hosts" #define SHELL "/bin/sh" /* default shell */ #define JCLCSH "/bin/csh" /* job control shell, needs new line disc. */ #define TIMEOUT 60 /* maximum amount of time to login */ #define TRIES 4 /* number permitted login attempts */ /* * The umask is a local decision. 077 is very paranoid (everything is * highly secret). 0 is wide open (everything readable and writable * by anyone.) 022 is moderate. 027 is also a possibility. */ #define UMASK 022 #define SCPYN(a, b) strncpy(a, b, sizeof(a)) #define NMAX sizeof(utmp.ut_name) #define LMAX sizeof(utmp.ut_line) #define CNTL(x) ('x'&037) #define UNDEF '\377' char maildir[30] = "/usr/spool/mail/"; char qlog[] = ".hushlogin"; char lastlog[] = "/usr/adm/lastlog"; char nolog[] = "/etc/nologin"; struct passwd nouser = {"", "nope"}; struct sgttyb ttyb; struct utmp utmp; char *ttyn; int timeout = TIMEOUT; /* it can be set during runtime */ char hostname[32]; char minusnam[16] = "-"; char homedir[64] = "HOME="; char term[64] = "TERM="; char shell[64] = "SHELL="; char user[NMAX+9] = "USER="; char *envinit[] = {homedir, PATH, term, shell, user, 0}; #ifdef MENLO_JCL struct ltchars ltc = { CNTL(z), CNTL(y), CNTL(r), CNTL(o), CNTL(w), CNTL(v) }; #endif struct passwd *pwd; struct passwd *getpwnam(); char *strcat(); int setpwent(); char *ttyname(); char *crypt(); char *getpass(); int timedout(); #ifdef GROUP #define GRLEN 30 /* max length of group name */ char group[GRLEN]; struct group *grp, *getgrnam(); #endif #ifdef SP_SESS int sp_sess = 0; #endif char *rindex(), *index(); extern char **environ; extern char _sobuf[]; int rflag; char rusername[NMAX+1], lusername[NMAX+1]; char rpassword[NMAX+1]; char *rhost = ""; main(argc, argv) char **argv; { register char *namep; int t, f, c, wasslash, ldisc; int quietlog, zero = 0; char *cp; FILE *nlfd; setbuf(stdout, _sobuf); signal(SIGALRM, timedout); alarm(timeout); signal(SIGQUIT, SIG_IGN); nice(-100); nice(20); nice(0); #ifdef SP_SESS if (argv[0][0] == 's') sp_sess++; #endif /* * -r is used by rlogind to cause the autologin protocol; * -h is used by other servers to pass the name of the * remote host to login so that it may be placed in lastlog */ if (argc > 1) { if (strcmp(argv[1], "-r") == 0) { rflag = remotelogin(argv[2]); rhost = argv[2]; argc = 0; } if (strcmp(argv[1], "-h") == 0 && getuid() == 0) { rhost = argv[2]; argc = 0; } } ioctl(0, TIOCLSET, &zero); ioctl(0, TIOCNXCL, 0); ioctl(0, FIONBIO, &zero); ioctl(0, FIOASYNC, &zero); ioctl(0, TIOCGETP, &ttyb); /* * If talking to an rlogin process, * propagate the terminal type and * baud rate across the network. */ if (rflag) remoteterm(term, &ttyb); ioctl(0, TIOCSLTC, <c); ioctl(0, TIOCSETP, &ttyb); for (t=3; t<20; t++) close(t); ttyn = ttyname(0); if (ttyn==0) ttyn = "/dev/tty??"; gethostname(hostname, sizeof (hostname)); t = 0; loop: if (t > 0 && rflag) /* no extra tries for remote login */ exit(1); if (++t >TRIES) { logerr("\r\nlogin: failure on %s, %d login attempts, last username: %s %s\r\n",ttyn+5, TRIES, utmp.ut_name, rhost); ioctl(0, TIOCHPCL, (struct sgttyb *) 0); close(0); sleep(2); exit(1); } SCPYN(utmp.ut_name, ""); #ifdef GROUP SCPYN(group, ""); if (argc>1) { register char *av = argv[1]; namep = utmp.ut_name; while (namep < utmp.ut_name+NMAX) { if (*av == 0 || *av == '.') break; *namep++ = *av++; } if (*av++ == '.') for (namep=group; namep<group+GRLEN; ) if ((*namep++ = *av++) == 0) break; argc = 0; } #else if (argc>1) { SCPYN(utmp.ut_name, argv[1]); argc = 0; } #endif /* * If remote login take given name, * otherwise prompt user for something. */ if (rflag) { SCPYN(utmp.ut_name, lusername); /* autologin failed, prompt for passwd */ if (rflag == -1) rflag = 0; } else getloginname(&utmp); /* * If no remote login authentication and * a password exists for this user, prompt * for one and verify it. */ if (!rflag && *pwd->pw_passwd != '\0') { namep = crypt(getpass("Password:"),pwd->pw_passwd); if (strcmp(namep, pwd->pw_passwd)) { printf("Login incorrect\n"); goto loop; } } #ifdef GROUP if (group[0]) { register i; grp = getgrnam(group); endgrent(); if(grp == 0) { printf("Login incorrect\n"); goto loop; } for(i=0;grp->gr_mem[i];i++) if(strcmp(grp->gr_mem[i], pwd->pw_name) == 0) break; if(grp->gr_mem[i] == 0) { printf("Login incorrect\n"); goto loop; } if(grp->gr_passwd[0] != '\0' && pwd->pw_passwd[0] == '\0') { if(strcmp(grp->gr_passwd, crypt(getpass("Password:"),grp->gr_passwd)) != 0) { printf("Login incorrect\n"); goto loop; } } pwd->pw_gid = grp->gr_gid; } #endif if (pwd->pw_uid != 0 && (nlfd = fopen(nolog, "r")) > 0) { /* logins are disabled except for root */ while ((c = getc(nlfd)) != EOF) putchar(c); fflush(stdout); sleep(5); exit(0); } #ifdef SP_SESS if(sp_sess && pwd->pw_uid != 0) { printf("Sorry. You cannot login at this time.\n"); exit(0); } #endif time(&utmp.ut_time); t = ttyslot(); /* * Update utmp and wtmp */ 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); } if (t>0 && (f = open("/usr/adm/wtmp", 1)) >= 0) { lseek(f, 0L, 2); write(f, (char *)&utmp, sizeof(utmp)); close(f); } if (term[strlen("TERM=")] == '\0') getterm(); chown(ttyn, pwd->pw_uid, pwd->pw_gid); chmod(ttyn, 0622); setgid(pwd->pw_gid); setuid(pwd->pw_uid); if (*pwd->pw_shell == '\0') pwd->pw_shell = SHELL; #ifdef MENLO_JCL if (!strcmp(pwd->pw_shell, JCLCSH)) { ldisc = NTTYDISC; ioctl(0, TIOCSETD, &ldisc); } else { ltc.t_suspc = ltc.t_dsuspc = UNDEF; ioctl(0, TIOCSLTC, <c); } #endif environ = envinit; strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); strncat(user, pwd->pw_name, sizeof(user)-6); strncat(shell, pwd->pw_shell, sizeof(shell)-7); signal(SIGINT, SIG_IGN); alarm(0); quietlog = 0; if (access(qlog, 0) == 0) quietlog = 1; /* * Update lastlog file * Tell user when he last logged in */ if((f = open(lastlog, 2)) >= 0) { struct lastlog ll; lseek(f, (long) ((unsigned) (pwd->pw_uid) * sizeof (struct lastlog)), 0); if (read(f, (char *) &ll, sizeof ll) == sizeof ll && ll.ll_time != 0 && quietlog == 0) { register char *ep = (char *) ctime(&ll.ll_time); printf("Last login: "); ep[24 - 5] = 0; printf("%s ", ep); if (*ll.ll_host != '\0') printf("from %.*s\n", (sizeof ll.ll_host), ll.ll_host); else printf("on %.*s\n", LMAX, ll.ll_line); fflush(stdout); /* So user sees the message quickly! */ } lseek(f, (long) ((unsigned) (pwd->pw_uid) * sizeof (struct lastlog)), 0); time(&ll.ll_time); strncpy(ll.ll_line, ttyn+5, LMAX); SCPYN(ll.ll_host, rhost); write(f, (char *) &ll, sizeof ll); close(f); } if (!quietlog) showfile("/etc/motd"); namep = pwd->pw_dir; for (;;) { if (*namep == '\0') break; cp = namep++; for (; *namep != '/' && *namep != '\0'; namep++); wasslash = 0; if (*namep == '/') { *namep = '\0'; wasslash++; } if (chdir(cp)<0) { if (chdir("/") < 0) { printf("No directory!\n"); exit(1); } else { printf("No directory! Logging in with home=/\n"); break; } } showfile(".broadcast"); if (wasslash) *namep++ = '/'; } showfile(".reminder"); if ((namep = rindex(pwd->pw_shell, '/')) == NULL) namep = pwd->pw_shell; else namep++; strcat(minusnam, namep); umask(UMASK); if (!quietlog) { 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"); } } signal(SIGALRM, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGINT, SIG_DFL); #ifdef SIGTSTP signal(SIGTSTP, SIG_IGN); #endif fflush(stdout); execlp(pwd->pw_shell, minusnam, 0); printf("No shell\n"); sleep(3); exit(0); } int stopmotd; catch() { signal(SIGINT, SIG_IGN); stopmotd++; printf("\n"); fflush(stdout); /* Immediate-looking response. */ } showfile(name) char *name; { FILE *mf; register c; stopmotd = 0; signal(SIGINT, catch); if((mf = fopen(name,"r")) != NULL) { while((c = getc(mf)) != EOF && stopmotd == 0) putchar(c); fclose(mf); fflush(stdout); } signal(SIGINT, SIG_IGN); } /* * make a reasonable guess as to the kind of terminal the user is on. * We look in /etc/ttytype for this info (format: each line has two * words, first word is a term type, second is a tty name), and default * to "unknown" if we can't find any better. In the case of dialups we get * names like "dialup" which is a lousy guess but tset can * take it from there. */ getterm() { register char *sp, *tname; register int i; register FILE *fdes; char *type, *t; char ttline[64]; if ((fdes = fopen("/etc/ttytype", "r")) == NULL) { unknown: strcat(term, "unknown"); return; } for (tname = ttyn; *tname++; ) ; while (*--tname != '/') ; tname++; while (fgets(ttline, sizeof(ttline), fdes) != NULL) { ttline[strlen(ttline)-1] = 0; /* zap \n on end */ type = ttline; for (t=ttline; *t && *t!=' ' && *t != '\t'; t++) ; *t++ = 0; /* Now have term and type pointing to the right guys */ if (strcmp(t, tname) == 0) { strcat(term, type); fclose(fdes); return; } } fclose(fdes); goto unknown; } getloginname(up) register struct utmp *up; { register char *namep; char c; while (up->ut_name[0] == '\0') { namep = up->ut_name; printf("%s login: ", hostname); fflush(stdout); while ((c = getchar()) != '\n') { if (c == ' ') c = '_'; if (c == EOF) exit(0); #ifdef GROUP if (c == '.') break; #endif if (namep < up->ut_name+NMAX) *namep++ = c; } #ifdef GROUP if (c == '.') { char *pgrp = group; while ((c = getchar()) != '\n') { if (c == EOF) exit(0); if (pgrp < &group[GRLEN]) *pgrp++ = c; } } #endif } strncpy(lusername, up->ut_name, NMAX); lusername[NMAX] = 0; setpwent(); if ((pwd = getpwnam(lusername)) == NULL) pwd = &nouser; endpwent(); } timedout() { printf("Login timed out after %d seconds\n", timeout); fflush(stdout); exit(0); } remotelogin(host) char *host; { FILE *hostf; int first = 1; getstr(rusername, sizeof (rusername), "remuser"); getstr(lusername, sizeof (lusername), "locuser"); getstr(term+5, sizeof(term)-5, "Terminal type"); if (getuid()) { pwd = &nouser; goto bad; } setpwent(); pwd = getpwnam(lusername); endpwent(); if (pwd == NULL) { pwd = &nouser; goto bad; } hostf = pwd->pw_uid ? fopen("/etc/hosts.equiv", "r") : 0; again: if (hostf) { char ahost[32]; while (fgets(ahost, sizeof (ahost), hostf)) { char *user; if ((user = index(ahost, '\n')) != 0) *user++ = '\0'; if ((user = index(ahost, ' ')) != 0) *user++ = '\0'; if (!strcmp(host, ahost) && !strcmp(rusername, user ? user : lusername)) { fclose(hostf); return (1); } } fclose(hostf); } if (first == 1) { char *rhosts = ".rhosts"; struct stat sbuf; first = 0; if (chdir(pwd->pw_dir) < 0) goto again; if (lstat(rhosts, &sbuf) < 0) goto again; if ((sbuf.st_mode & S_IFMT) == S_IFLNK) { printf("login: .rhosts is a soft link.\r\n"); goto bad; } hostf = fopen(rhosts, "r"); fstat(fileno(hostf), &sbuf); if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) { printf("login: Bad .rhosts ownership.\r\n"); fclose(hostf); goto bad; } goto again; } bad: return (-1); } getstr(buf, cnt, err) char *buf; int cnt; char *err; { char c; do { if (read(0, &c, 1) != 1) exit(1); if (--cnt < 0) { printf("%s too long\r\n", err); exit(1); } *buf++ = c; } while (c != 0); } char *speeds[] = { "0", "50", "75", "110", "134", "150", "200", "300", "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" }; #define NSPEEDS (sizeof (speeds) / sizeof (speeds[0])) remoteterm(term, tp) char *term; struct sgttyb *tp; { char *cp = index(term, '/'); register int i; if (cp) { *cp++ = 0; for (i = 0; i < NSPEEDS; i++) if (!strcmp(speeds[i], cp)) { tp->sg_ispeed = tp->sg_ospeed = i; break; } } tp->sg_flags = ECHO|CRMOD|ANYP|XTABS; } logerr(fmt, a1, a2, a3, a4) char *fmt, *a1, *a2, *a3, *a4; { #ifdef LOGERR FILE *cons = fopen("/dev/console", "w"); if (cons != NULL) { fprintf(cons, fmt, a1, a2, a3, a4); fprintf(cons, "\n\r"); fflush(cons); fclose(cons); } #endif }