1 /* This program is derived from 4.3 BSD software and is
2 subject to the copyright notice below.
4 The port to HP-UX has been motivated by the incapability
5 of 'rlogin'/'rlogind' as per HP-UX 6.5 (and 7.0) to transfer window sizes.
9 - General HP-UX portation. Use of facilities not available
10 in HP-UX (e.g. setpriority) has been eliminated.
11 Utmp/wtmp handling has been ported.
13 - The program uses BSD command line options to be used
14 in connection with e.g. 'rlogind' i.e. 'new login'.
16 - HP features left out: password expiry
17 '*' as login shell, add it if you need it
19 - BSD features left out: quota checks
21 analysis of terminal type (tset feature)
23 - BSD features thrown in: Security logging to syslogd.
24 This requires you to have a (ported) syslog
25 system -- 7.0 comes with syslog
29 - A lot of nitty gritty details have been adjusted in favour of
30 HP-UX, e.g. /etc/securetty, default paths and the environment
31 variables assigned by 'login'.
33 - We do *nothing* to setup/alter tty state, under HP-UX this is
34 to be done by getty/rlogind/telnetd/some one else.
36 Michael Glad (glad@daimi.dk)
37 Computer Science Department
43 1991-09-24 glad@daimi.aau.dk: HP-UX 8.0 port:
44 - now explictly sets non-blocking mode on descriptors
45 - strcasecmp is now part of HP-UX
47 1992-02-05 poe@daimi.aau.dk: Ported the stuff to Linux 0.12
48 From 1992 till now (1997) this code for Linux has been maintained at
49 ftp.daimi.aau.dk:/pub/linux/poe/
51 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
52 - added Native Language Support
53 Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
54 - fixed strerr(errno) in gettext calls
58 * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
59 * All rights reserved.
61 * Redistribution and use in source and binary forms are permitted
62 * provided that the above copyright notice and this paragraph are
63 * duplicated in all such forms and that any documentation,
64 * advertising materials, and other materials related to such
65 * distribution and use acknowledge that the software was developed
66 * by the University of California, Berkeley. The name of the
67 * University may not be used to endorse or promote products derived
68 * from this software without specific prior written permission.
69 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
70 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
71 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
76 * login -h hostname (for telnetd, etc.)
77 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
80 #include <sys/param.h>
90 #include <sys/resource.h>
95 #define rindex strrchr
96 #include <sys/ioctl.h>
105 #include <sys/syslog.h>
106 #include <sys/sysmacros.h>
107 #include <linux/major.h>
110 # include <libaudit.h>
113 #include "pathnames.h"
114 #include "my_crypt.h"
116 #include "xstrncpy.h"
120 #ifdef HAVE_SECURITY_PAM_MISC_H
121 # include <security/pam_appl.h>
122 # include <security/pam_misc.h>
123 # define PAM_MAX_LOGIN_TRIES 3
124 # define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \
125 fprintf(stderr,"\n%s\n",pam_strerror(pamh, retcode)); \
126 syslog(LOG_ERR,"%s",pam_strerror(pamh, retcode)); \
127 pam_end(pamh, retcode); exit(1); \
130 pam_setcred(pamh, PAM_DELETE_CRED); \
131 retcode = pam_close_session(pamh,0); \
132 pam_end(pamh,retcode); \
138 #define SLEEP_EXIT_TIMEOUT 5
140 #include "setproctitle.h"
142 #ifndef HAVE_SECURITY_PAM_MISC_H
143 static void getloginname (void);
144 static void checknologin (void);
145 static int rootterm (char *ttyn);
147 static void timedout (int);
148 static void sigint (int);
149 static void motd (void);
150 static void dolastlog (int quiet);
153 # define TTY_MODE 0620
155 # define TTY_MODE 0600
158 #define TTYGRPNAME "tty" /* name of group to own ttys */
161 # define MAXPATHLEN 1024
165 * This bounds the time given to login. Not a define so it can
166 * be patched on machines where it's too small.
172 #ifdef HAVE_SECURITY_PAM_MISC_H
173 static struct passwd pwdcopy;
175 char hostaddress[16]; /* used in checktty.c */
176 sa_family_t hostfamily; /* used in checktty.c */
177 char *hostname; /* idem */
178 static char *username, *tty_name, *tty_number;
179 static char thishost[100];
180 static int failures = 1;
183 /* Nice and simple code provided by Linus Torvalds 16-Feb-93 */
184 /* Nonblocking stuff by Maciej W. Rozycki, macro@ds2.pg.gda.pl, 1999.
185 He writes: "Login performs open() on a tty in a blocking mode.
186 In some cases it may make login wait in open() for carrier infinitely,
187 for example if the line is a simplistic case of a three-wire serial
188 connection. I believe login should open the line in the non-blocking mode
189 leaving the decision to make a connection to getty (where it actually
192 opentty(const char * tty) {
195 fd = open(tty, O_RDWR | O_NONBLOCK);
197 syslog(LOG_ERR, _("FATAL: can't reopen tty: %s"),
203 flags = fcntl(fd, F_GETFL);
204 flags &= ~O_NONBLOCK;
205 fcntl(fd, F_SETFL, flags);
207 for (i = 0; i < fd; i++)
209 for (i = 0; i < 3; i++)
216 /* In case login is suid it was possible to use a hardlink as stdin
217 and exploit races for a local root exploit. (Wojciech Purczynski). */
218 /* More precisely, the problem is ttyn := ttyname(0); ...; chown(ttyn);
219 here ttyname() might return "/tmp/x", a hardlink to a pseudotty. */
220 /* All of this is a problem only when login is suid, which it isnt. */
222 check_ttyname(char *ttyn) {
225 if (lstat(ttyn, &statbuf)
226 || !S_ISCHR(statbuf.st_mode)
227 || (statbuf.st_nlink > 1 && strncmp(ttyn, "/dev/", 5))
228 || (access(ttyn, R_OK | W_OK) != 0)) {
229 syslog(LOG_ERR, _("FATAL: bad tty"));
235 #ifdef LOGIN_CHOWN_VCS
236 /* true if the filedescriptor fd is a console tty, very Linux specific */
241 if ((fstat(fd, &stb) >= 0)
242 && (major(stb.st_rdev) == TTY_MAJOR)
243 && (minor(stb.st_rdev) < 64)) {
250 #ifdef HAVE_SECURITY_PAM_MISC_H
252 * Log failed login attempts in _PATH_BTMP if that exists.
253 * Must be called only with username the name of an actual user.
254 * The most common login failure is to give password instead of username.
256 #define _PATH_BTMP "/var/log/btmp"
258 logbtmp(const char *line, const char *username, const char *hostname) {
262 memset(&ut, 0, sizeof(ut));
264 strncpy(ut.ut_user, username ? username : "(unknown)",
267 strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
268 xstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
270 #if defined(_HAVE_UT_TV) /* in <utmpbits.h> included by <utmp.h> */
271 gettimeofday(&tv, NULL);
272 ut.ut_tv.tv_sec = tv.tv_sec;
273 ut.ut_tv.tv_usec = tv.tv_usec;
278 ut.ut_time = t; /* ut_time is not always a time_t */
282 ut.ut_type = LOGIN_PROCESS; /* XXX doesn't matter */
285 xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
287 memcpy(&ut.ut_addr_v6, hostaddress, sizeof(ut.ut_addr_v6));
289 #if HAVE_UPDWTMP /* bad luck for ancient systems */
290 updwtmp(_PATH_BTMP, &ut);
295 static int child_pid = 0;
296 static volatile int got_sig = 0;
299 * This handler allows to inform a shell about signals to login. If you have
300 * (root) permissions you can kill all login childrent by one signal to login
303 * Also, parent who is session leader is able (before setsid() in child) to
304 * inform child when controlling tty goes away (e.g. modem hangup, SIGHUP).
307 sig_handler(int signal)
310 kill(-child_pid, signal);
313 if(signal == SIGTERM)
314 kill(-child_pid, SIGHUP); /* because the shell often ignores SIGTERM */
317 #endif /* HAVE_SECURITY_PAM_MISC_H */
321 logaudit(const char *tty, const char *username, const char *hostname,
322 struct passwd *pwd, int status)
326 audit_fd = audit_open();
329 if (!pwd && username)
330 pwd = getpwnam(username);
332 audit_log_acct_message(audit_fd, AUDIT_USER_LOGIN,
333 NULL, "login", username ? username : "(unknown)",
334 pwd ? pwd->pw_uid : -1, hostname, NULL, tty, status);
338 #else /* ! HAVE_LIBAUDIT */
339 # define logaudit(tty, username, hostname, pwd, status)
340 #endif /* HAVE_LIBAUDIT */
342 #ifdef HAVE_SECURITY_PAM_MISC_H
343 /* encapsulate stupid "void **" pam_get_item() API */
345 get_pam_username(pam_handle_t *pamh, char **name)
347 const void *item = (void *) *name;
349 rc = pam_get_item(pamh, PAM_USER, &item);
350 *name = (char *) item;
356 * We need to check effective UID/GID. For example $HOME could be on root
357 * squashed NFS or on NFS with UID mapping and access(2) uses real UID/GID.
358 * The open(2) seems as the surest solution.
359 * -- kzak@redhat.com (10-Apr-2009)
362 effective_access(const char *path, int mode)
364 int fd = open(path, mode);
367 return fd == -1 ? -1 : 0;
371 main(int argc, char **argv)
374 extern char *optarg, **environ;
378 int ask, fflag, hflag, pflag, cnt, errsv;
379 int quietlog, passwd_req;
381 char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_DEV_TTY) + 10];
386 #ifdef HAVE_SECURITY_PAM_MISC_H
388 pam_handle_t *pamh = NULL;
389 struct pam_conv conv = { misc_conv, NULL };
390 struct sigaction sa, oldsa_hup, oldsa_term;
394 #ifdef LOGIN_CHOWN_VCS
395 char vcsn[20], vcsan[20];
400 signal(SIGALRM, timedout);
401 siginterrupt(SIGALRM,1); /* we have to interrupt syscalls like ioclt() */
402 alarm((unsigned int)timeout);
403 signal(SIGQUIT, SIG_IGN);
404 signal(SIGINT, SIG_IGN);
406 setlocale(LC_ALL, "");
407 bindtextdomain(PACKAGE, LOCALEDIR);
410 setpriority(PRIO_PROCESS, 0, 0);
411 initproctitle(argc, argv);
414 * -p is used by getty to tell login not to destroy the environment
415 * -f is used to skip a second login authentication
416 * -h is used by other servers to pass the name of the remote
417 * host to login so that it may be placed in utmp and wtmp
419 gethostname(tbuf, sizeof(tbuf));
420 xstrncpy(thishost, tbuf, sizeof(thishost));
421 domain = strchr(tbuf, '.');
423 username = tty_name = hostname = NULL;
424 fflag = hflag = pflag = 0;
427 while ((ch = getopt(argc, argv, "fh:p")) != -1)
436 _("login: -h for super-user only.\n"));
440 if (domain && (p = strchr(optarg, '.')) &&
441 strcasecmp(p, domain) == 0)
444 hostname = strdup(optarg); /* strdup: Ambrose C. Li */
446 struct addrinfo hints, *info = NULL;
448 memset(&hints, 0, sizeof(hints));
449 hints.ai_flags = AI_ADDRCONFIG;
453 if (getaddrinfo(hostname, NULL, &hints, &info)==0 && info) {
454 if (info->ai_family == AF_INET) {
455 struct sockaddr_in *sa =
456 (struct sockaddr_in *) info->ai_addr;
457 memcpy(hostaddress, &(sa->sin_addr),
458 sizeof(sa->sin_addr));
460 else if (info->ai_family == AF_INET6) {
461 struct sockaddr_in6 *sa =
462 (struct sockaddr_in6 *) info->ai_addr;
463 memcpy(hostaddress, &(sa->sin6_addr),
464 sizeof(sa->sin6_addr));
466 hostfamily = info->ai_family;
479 _("usage: login [-fp] [username]\n"));
486 username = strdup(p);
488 /* wipe name - some people mistype their password here */
489 /* (of course we are too late, but perhaps this helps a little ..) */
495 for (cnt = getdtablesize(); cnt > 2; cnt--)
500 if (ttyn == NULL || *ttyn == '\0') {
501 /* no snprintf required - see definition of tname */
502 snprintf(tname, sizeof(tname), "%s??", _PATH_DEV_TTY);
508 if (strncmp(ttyn, "/dev/", 5) == 0)
513 if (strncmp(ttyn, "/dev/tty", 8) == 0)
517 while (*p && !isdigit(*p)) p++;
521 #ifdef LOGIN_CHOWN_VCS
522 /* find names of Virtual Console devices, for later mode change */
523 snprintf(vcsn, sizeof(vcsn), "/dev/vcs%s", tty_number);
524 snprintf(vcsan, sizeof(vcsan), "/dev/vcsa%s", tty_number);
527 /* set pgid to pid */
529 /* this means that setsid() will fail */
532 struct termios tt, ttt;
536 ttt.c_cflag &= ~HUPCL;
538 /* These can fail, e.g. with ttyn on a read-only filesystem */
540 chmod(ttyn, TTY_MODE);
542 /* Kill processes left on this tty */
543 tcsetattr(0,TCSAFLUSH,&ttt);
544 signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */
546 signal(SIGHUP, SIG_DFL);
548 /* open stdin,stdout,stderr to the tty */
551 /* restore tty modes */
552 tcsetattr(0,TCSAFLUSH,&tt);
555 openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
558 /* other than iso-8859-1 */
560 fprintf(stderr,"\033(K");
563 #ifdef HAVE_SECURITY_PAM_MISC_H
565 * username is initialized to NULL
566 * and if specified on the command line it is set.
567 * Therefore, we are safe not setting it to anything
570 retcode = pam_start(hflag?"remote":"login",username, &conv, &pamh);
571 if(retcode != PAM_SUCCESS) {
572 fprintf(stderr, _("%s: PAM failure, aborting: %s\n"),
573 "login", pam_strerror(pamh, retcode));
574 syslog(LOG_ERR, _("Couldn't initialize PAM: %s"),
575 pam_strerror(pamh, retcode));
578 /* hostname & tty are either set to NULL or their correct values,
579 depending on how much we know */
580 retcode = pam_set_item(pamh, PAM_RHOST, hostname);
582 retcode = pam_set_item(pamh, PAM_TTY, tty_name);
586 * Andrew.Taylor@cal.montage.ca: Provide a user prompt to PAM
587 * so that the "login: " prompt gets localized. Unfortunately,
588 * PAM doesn't have an interface to specify the "Password: " string
591 retcode = pam_set_item(pamh, PAM_USER_PROMPT, _("login: "));
596 * other than iso-8859-1
597 * one more time due to reset tty by PAM
600 fprintf(stderr,"\033(K");
604 /* we need't the original username. We have to follow PAM. */
609 /* if fflag == 1, then the user has already been authenticated */
610 if (fflag && (getuid() == 0))
615 if(passwd_req == 1) {
618 /* if we didn't get a user on the command line, set it to NULL */
619 get_pam_username(pamh, &username);
621 /* there may be better ways to deal with some of these
622 conditions, but at least this way I don't think we'll
623 be giving away information... */
624 /* Perhaps someday we can trust that all PAM modules will
625 pay attention to failure count and get rid of MAX_LOGIN_TRIES? */
627 retcode = pam_authenticate(pamh, 0);
628 while((failcount++ < PAM_MAX_LOGIN_TRIES) &&
629 ((retcode == PAM_AUTH_ERR) ||
630 (retcode == PAM_USER_UNKNOWN) ||
631 (retcode == PAM_CRED_INSUFFICIENT) ||
632 (retcode == PAM_AUTHINFO_UNAVAIL))) {
633 get_pam_username(pamh, &username);
635 syslog(LOG_NOTICE,_("FAILED LOGIN %d FROM %s FOR %s, %s"),
636 failcount, hostname, username, pam_strerror(pamh, retcode));
637 logbtmp(tty_name, username, hostname);
638 logaudit(tty_name, username, hostname, NULL, 0);
640 fprintf(stderr,_("Login incorrect\n\n"));
641 pam_set_item(pamh,PAM_USER,NULL);
642 retcode = pam_authenticate(pamh, 0);
645 if (retcode != PAM_SUCCESS) {
646 get_pam_username(pamh, &username);
648 if (retcode == PAM_MAXTRIES)
649 syslog(LOG_NOTICE,_("TOO MANY LOGIN TRIES (%d) FROM %s FOR "
650 "%s, %s"), failcount, hostname, username,
651 pam_strerror(pamh, retcode));
653 syslog(LOG_NOTICE,_("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
654 hostname, username, pam_strerror(pamh, retcode));
655 logbtmp(tty_name, username, hostname);
656 logaudit(tty_name, username, hostname, NULL, 0);
658 fprintf(stderr,_("\nLogin incorrect\n"));
659 pam_end(pamh, retcode);
665 * Authentication may be skipped (for example, during krlogin, rlogin, etc...),
666 * but it doesn't mean that we can skip other account checks. The account
667 * could be disabled or password expired (althought kerberos ticket is valid).
668 * -- kzak@redhat.com (22-Feb-2006)
670 retcode = pam_acct_mgmt(pamh, 0);
672 if(retcode == PAM_NEW_AUTHTOK_REQD) {
673 retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
679 * Grab the user information out of the password file for future usage
680 * First get the username that we are actually using, though.
682 retcode = get_pam_username(pamh, &username);
685 if (!username || !*username) {
686 fprintf(stderr, _("\nSession setup problem, abort.\n"));
687 syslog(LOG_ERR, _("NULL user name in %s:%d. Abort."),
688 __FUNCTION__, __LINE__);
689 pam_end(pamh, PAM_SYSTEM_ERR);
692 if (!(pwd = getpwnam(username))) {
693 fprintf(stderr, _("\nSession setup problem, abort.\n"));
694 syslog(LOG_ERR, _("Invalid user name \"%s\" in %s:%d. Abort."),
695 username, __FUNCTION__, __LINE__);
696 pam_end(pamh, PAM_SYSTEM_ERR);
701 * Create a copy of the pwd struct - otherwise it may get
704 memcpy(&pwdcopy, pwd, sizeof(*pwd));
706 pwd->pw_name = strdup(pwd->pw_name);
707 pwd->pw_passwd = strdup(pwd->pw_passwd);
708 pwd->pw_gecos = strdup(pwd->pw_gecos);
709 pwd->pw_dir = strdup(pwd->pw_dir);
710 pwd->pw_shell = strdup(pwd->pw_shell);
711 if (!pwd->pw_name || !pwd->pw_passwd || !pwd->pw_gecos ||
712 !pwd->pw_dir || !pwd->pw_shell) {
713 fprintf(stderr, _("login: Out of memory\n"));
714 syslog(LOG_ERR, "Out of memory");
715 pam_end(pamh, PAM_SYSTEM_ERR);
718 username = pwd->pw_name;
721 * Initialize the supplementary group list.
722 * This should be done before pam_setcred because
723 * the PAM modules might add groups during pam_setcred.
725 if (initgroups(username, pwd->pw_gid) < 0) {
726 syslog(LOG_ERR, "initgroups: %m");
727 fprintf(stderr, _("\nSession setup problem, abort.\n"));
728 pam_end(pamh, PAM_SYSTEM_ERR);
732 retcode = pam_open_session(pamh, 0);
735 retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
736 if (retcode != PAM_SUCCESS)
737 pam_close_session(pamh, 0);
740 #else /* ! HAVE_SECURITY_PAM_MISC_H */
742 for (cnt = 0;; ask = 1) {
749 /* Dirty patch to fix a gigantic security hole when using
750 yellow pages. This problem should be solved by the
751 libraries, and not by programs, but this must be fixed
752 urgently! If the first char of the username is '+', we
754 Feb 95 <alvaro@etsit.upm.es> */
756 if (username[0] == '+') {
757 puts(_("Illegal username"));
762 /* (void)strcpy(tbuf, username); why was this here? */
763 if ((pwd = getpwnam(username))) {
767 if ((sp = getspnam(username)))
768 pwd->pw_passwd = sp->sp_pwdp;
770 salt = pwd->pw_passwd;
775 initgroups(username, pwd->pw_gid);
776 checktty(username, tty_name, pwd); /* in checktty.c */
779 /* if user not super-user, check for disabled logins */
780 if (pwd == NULL || pwd->pw_uid)
784 * Disallow automatic login to root; if not invoked by
785 * root, disallow if the uid's differ.
790 passwd_req = pwd->pw_uid == 0 ||
791 (uid && uid != pwd->pw_uid);
795 * If trying to log in as root, but with insecure terminal,
796 * refuse the login attempt.
798 if (pwd && pwd->pw_uid == 0 && !rootterm(tty_name)) {
800 _("%s login refused on this terminal.\n"),
805 _("LOGIN %s REFUSED FROM %s ON TTY %s"),
806 pwd->pw_name, hostname, tty_name);
809 _("LOGIN %s REFUSED ON TTY %s"),
810 pwd->pw_name, tty_name);
811 logaudit(tty_name, pwd->pw_name, hostname, pwd, 0);
816 * If no pre-authentication and a password exists
817 * for this user, prompt for one and verify it.
819 if (!passwd_req || (pwd && !*pwd->pw_passwd))
822 setpriority(PRIO_PROCESS, 0, -4);
823 pp = getpass(_("Password: "));
826 if (strncmp(pp, "CRYPTO", 6) == 0) {
827 if (pwd && cryptocard()) break;
829 # endif /* CRYPTOCARD */
832 setpriority(PRIO_PROCESS, 0, 0);
836 * If not present in pw file, act as we normally would.
837 * If we aren't Kerberos-authenticated, try the normal
838 * pw file for a password. If that's ok, log the user
839 * in without issueing any tickets.
842 if (pwd && !krb_get_lrealm(realm,1)) {
844 * get TGT for local realm; be careful about uid's
845 * here for ticket file ownership
847 setreuid(geteuid(),pwd->pw_uid);
848 kerror = krb_get_pw_in_tkt(pwd->pw_name, "", realm,
849 "krbtgt", realm, DEFAULT_TKT_LIFE, pp);
851 if (kerror == INTK_OK) {
852 memset(pp, 0, strlen(pp));
853 notickets = 0; /* user got ticket */
857 # endif /* KERBEROS */
858 memset(pp, 0, strlen(pp));
860 if (pwd && !strcmp(p, pwd->pw_passwd))
863 printf(_("Login incorrect\n"));
864 badlogin(username); /* log ALL bad logins */
867 /* we allow 10 tries, but after 3 we start backing off */
872 sleep((unsigned int)((cnt - 3) * 5));
875 #endif /* !HAVE_SECURITY_PAM_MISC_H */
877 /* committed to login -- turn off timeout */
878 alarm((unsigned int)0);
882 /* This requires some explanation: As root we may not be able to
883 read the directory of the user if it is on an NFS mounted
884 filesystem. We temporarily set our effective uid to the user-uid
885 making sure that we keep root privs. in the real uid.
887 A portable solution would require a fork(), but we rely on Linux
888 having the BSD setreuid() */
891 char tmpstr[MAXPATHLEN];
892 uid_t ruid = getuid();
893 gid_t egid = getegid();
895 /* avoid snprintf - old systems do not have it, or worse,
896 have a libc in which snprintf is the same as sprintf */
897 if (strlen(pwd->pw_dir) + sizeof(_PATH_HUSHLOGIN) + 2 > MAXPATHLEN)
900 sprintf(tmpstr, "%s/%s", pwd->pw_dir, _PATH_HUSHLOGIN);
901 setregid(-1, pwd->pw_gid);
902 setreuid(0, pwd->pw_uid);
903 quietlog = (effective_access(tmpstr, O_RDONLY) == 0);
904 setuid(0); /* setreuid doesn't do it alone! */
910 /* for linux, write entries in utmp and wtmp */
916 utmpname(_PATH_UTMP);
920 login sometimes overwrites the runlevel entry in /var/run/utmp,
921 confusing sysvinit. I added a test for the entry type, and the problem
922 was gone. (In a runlevel entry, st_pid is not really a pid but some number
923 calculated from the previous and current runlevel).
924 Michael Riepe <michael@stud.uni-hannover.de>
926 while ((utp = getutent()))
927 if (utp->ut_pid == pid
928 && utp->ut_type >= INIT_PROCESS
929 && utp->ut_type <= DEAD_PROCESS)
932 /* If we can't find a pre-existing entry by pid, try by line.
933 BSD network daemons may rely on this. (anonymous) */
936 ut.ut_type = LOGIN_PROCESS;
937 strncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
938 utp = getutline(&ut);
942 memcpy(&ut, utp, sizeof(ut));
944 /* some gettys/telnetds don't initialize utmp... */
945 memset(&ut, 0, sizeof(ut));
948 if (ut.ut_id[0] == 0)
949 strncpy(ut.ut_id, tty_number, sizeof(ut.ut_id));
951 strncpy(ut.ut_user, username, sizeof(ut.ut_user));
952 xstrncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
953 #ifdef _HAVE_UT_TV /* in <utmpbits.h> included by <utmp.h> */
954 gettimeofday(&tv, NULL);
955 ut.ut_tv.tv_sec = tv.tv_sec;
956 ut.ut_tv.tv_usec = tv.tv_usec;
961 ut.ut_time = t; /* ut_time is not always a time_t */
962 /* glibc2 #defines it as ut_tv.tv_sec */
965 ut.ut_type = USER_PROCESS;
968 xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
970 memcpy(&ut.ut_addr_v6, hostaddress, sizeof(ut.ut_addr_v6));
977 updwtmp(_PATH_WTMP, &ut);
980 /* The O_APPEND open() flag should be enough to guarantee
981 atomic writes at end of file. */
985 if((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
986 write(wtmp, (char *)&ut, sizeof(ut));
991 /* Probably all this locking below is just nonsense,
992 and the short version is OK as well. */
995 if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) {
997 if ((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
998 write(wtmp, (char *)&ut, sizeof(ut));
1009 logaudit(tty_name, username, hostname, pwd, 1);
1010 dolastlog(quietlog);
1012 chown(ttyn, pwd->pw_uid,
1013 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
1014 chmod(ttyn, TTY_MODE);
1016 #ifdef LOGIN_CHOWN_VCS
1017 /* if tty is one of the VC's then change owner and mode of the
1018 special /dev/vcs devices as well */
1019 if (consoletty(0)) {
1020 chown(vcsn, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid));
1021 chown(vcsan, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid));
1022 chmod(vcsn, TTY_MODE);
1023 chmod(vcsan, TTY_MODE);
1027 setgid(pwd->pw_gid);
1029 if (*pwd->pw_shell == '\0')
1030 pwd->pw_shell = _PATH_BSHELL;
1032 /* preserve TERM even without -p flag */
1036 if(!((ep = getenv("TERM")) && (termenv = strdup(ep))))
1040 /* destroy environment unless user has requested preservation */
1043 environ = (char**)malloc(sizeof(char*));
1044 memset(environ, 0, sizeof(char*));
1047 setenv("HOME", pwd->pw_dir, 0); /* legal to override */
1049 setenv("PATH", _PATH_DEFPATH, 1);
1051 setenv("PATH", _PATH_DEFPATH_ROOT, 1);
1053 setenv("SHELL", pwd->pw_shell, 1);
1054 setenv("TERM", termenv, 1);
1056 /* mailx will give a funny error msg if you forget this one */
1058 char tmp[MAXPATHLEN];
1059 /* avoid snprintf */
1060 if (sizeof(_PATH_MAILDIR) + strlen(pwd->pw_name) + 1 < MAXPATHLEN) {
1061 sprintf(tmp, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
1062 setenv("MAIL",tmp,0);
1066 /* LOGNAME is not documented in login(1) but
1067 HP-UX 6.5 does it. We'll not allow modifying it.
1069 setenv("LOGNAME", pwd->pw_name, 1);
1071 #ifdef HAVE_SECURITY_PAM_MISC_H
1074 char ** env = pam_getenvlist(pamh);
1077 for (i=0; env[i]; i++) {
1079 /* D(("env[%d] = %s", i,env[i])); */
1085 setproctitle("login", username);
1087 if (!strncmp(tty_name, "ttyS", 4))
1088 syslog(LOG_INFO, _("DIALUP AT %s BY %s"), tty_name, pwd->pw_name);
1090 /* allow tracking of good logins.
1091 -steve philp (sphilp@mail.alliance.net) */
1093 if (pwd->pw_uid == 0) {
1095 syslog(LOG_NOTICE, _("ROOT LOGIN ON %s FROM %s"),
1096 tty_name, hostname);
1098 syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), tty_name);
1101 syslog(LOG_INFO, _("LOGIN ON %s BY %s FROM %s"), tty_name,
1102 pwd->pw_name, hostname);
1104 syslog(LOG_INFO, _("LOGIN ON %s BY %s"), tty_name,
1111 #ifdef LOGIN_STAT_MAIL
1113 * This turns out to be a bad idea: when the mail spool
1114 * is NFS mounted, and the NFS connection hangs, the
1115 * login hangs, even root cannot login.
1116 * Checking for mail should be done from the shell.
1122 mail = getenv("MAIL");
1123 if (mail && stat(mail, &st) == 0 && st.st_size != 0) {
1124 if (st.st_mtime > st.st_atime)
1125 printf(_("You have new mail.\n"));
1127 printf(_("You have mail.\n"));
1133 signal(SIGALRM, SIG_DFL);
1134 signal(SIGQUIT, SIG_DFL);
1135 signal(SIGTSTP, SIG_IGN);
1137 #ifdef HAVE_SECURITY_PAM_MISC_H
1139 memset(&sa, 0, sizeof(sa));
1140 sa.sa_handler = SIG_IGN;
1141 sigaction(SIGINT, &sa, NULL);
1143 sigaction(SIGHUP, &sa, &oldsa_hup); /* ignore when TIOCNOTTY */
1146 * detach the controlling tty
1147 * -- we needn't the tty in parent who waits for child only.
1148 * The child calls setsid() that detach from the tty as well.
1150 ioctl(0, TIOCNOTTY, NULL);
1153 * We have care about SIGTERM, because leave PAM session without
1154 * pam_close_session() is pretty bad thing.
1156 sa.sa_handler = sig_handler;
1157 sigaction(SIGHUP, &sa, NULL);
1158 sigaction(SIGTERM, &sa, &oldsa_term);
1163 * We must fork before setuid() because we need to call
1164 * pam_close_session() as root.
1168 if (child_pid < 0) {
1170 /* error in fork() */
1171 fprintf(stderr, _("login: failure forking: %s"), strerror(errsv));
1177 /* parent - wait for child to finish, then cleanup session */
1181 sa.sa_handler = SIG_IGN;
1182 sigaction(SIGQUIT, &sa, NULL);
1183 sigaction(SIGINT, &sa, NULL);
1185 /* wait as long as any child is there */
1186 while(wait(NULL) == -1 && errno == EINTR)
1188 openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
1195 /* restore to old state */
1196 sigaction(SIGHUP, &oldsa_hup, NULL);
1197 sigaction(SIGTERM, &oldsa_term, NULL);
1202 * Problem: if the user's shell is a shell like ash that doesnt do
1203 * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
1204 * process in the pgrp, will kill us.
1207 /* start new session */
1210 /* make sure we have a controlling tty */
1212 openlog("login", LOG_ODELAY, LOG_AUTHPRIV); /* reopen */
1215 * TIOCSCTTY: steal tty from other process group.
1217 if (ioctl(0, TIOCSCTTY, 1))
1218 syslog(LOG_ERR, _("TIOCSCTTY failed: %m"));
1220 signal(SIGINT, SIG_DFL);
1222 /* discard permissions last so can't get killed and drop core */
1223 if(setuid(pwd->pw_uid) < 0 && pwd->pw_uid) {
1224 syslog(LOG_ALERT, _("setuid() failed"));
1228 /* wait until here to change directory! */
1229 if (chdir(pwd->pw_dir) < 0) {
1230 printf(_("No directory %s!\n"), pwd->pw_dir);
1234 printf(_("Logging in with home = \"/\".\n"));
1237 /* if the shell field has a space: treat it like a shell script */
1238 if (strchr(pwd->pw_shell, ' ')) {
1239 buff = malloc(strlen(pwd->pw_shell) + 6);
1242 fprintf(stderr, _("login: no memory for shell script.\n"));
1246 strcpy(buff, "exec ");
1247 strcat(buff, pwd->pw_shell);
1248 childArgv[childArgc++] = "/bin/sh";
1249 childArgv[childArgc++] = "-sh";
1250 childArgv[childArgc++] = "-c";
1251 childArgv[childArgc++] = buff;
1254 xstrncpy(tbuf + 1, ((p = strrchr(pwd->pw_shell, '/')) ?
1255 p + 1 : pwd->pw_shell),
1258 childArgv[childArgc++] = pwd->pw_shell;
1259 childArgv[childArgc++] = tbuf;
1262 childArgv[childArgc++] = NULL;
1264 execvp(childArgv[0], childArgv + 1);
1268 if (!strcmp(childArgv[0], "/bin/sh"))
1269 fprintf(stderr, _("login: couldn't exec shell script: %s.\n"),
1272 fprintf(stderr, _("login: no shell: %s.\n"), strerror(errsv));
1277 #ifndef HAVE_SECURITY_PAM_MISC_H
1279 getloginname(void) {
1282 static char nbuf[UT_NAMESIZE + 1];
1287 printf(_("\n%s login: "), thishost); fflush(stdout);
1288 for (p = nbuf; (ch = getchar()) != '\n'; ) {
1293 if (p < nbuf + UT_NAMESIZE)
1297 if (cnt > UT_NAMESIZE + 20) {
1298 fprintf(stderr, _("login name much too long.\n"));
1299 badlogin(_("NAME too long"));
1306 _("login names may not start with '-'.\n"));
1316 fprintf(stderr, _("too many bare linefeeds.\n"));
1317 badlogin(_("EXCESSIVE linefeeds"));
1325 * Robert Ambrose writes:
1326 * A couple of my users have a problem with login processes hanging around
1327 * soaking up pts's. What they seem to hung up on is trying to write out the
1328 * message 'Login timed out after %d seconds' when the connection has already
1330 * What I did was add a second timeout while trying to write the message so
1331 * the process just exits if the second timeout expires.
1335 timedout2(int sig) {
1341 tcsetattr(0, TCSANOW, &ti);
1347 signal(SIGALRM, timedout2);
1349 fprintf(stderr, _("Login timed out after %d seconds\n"), timeout);
1350 signal(SIGALRM, SIG_IGN);
1355 #ifndef HAVE_SECURITY_PAM_MISC_H
1357 rootterm(char * ttyn)
1363 fd = open(_PATH_SECURETTY, O_RDONLY);
1364 if(fd < 0) return 1;
1366 /* read each line in /etc/securetty, if a line matches our ttyline
1367 then root is allowed to login on this tty, and we should return
1371 while(--cnt >= 0 && (more = read(fd, p, 1)) == 1 && *p != '\n') p++;
1372 if(more && *p == '\n') {
1374 if(!strcmp(buf, ttyn)) {
1385 #endif /* !HAVE_SECURITY_PAM_MISC_H */
1387 jmp_buf motdinterrupt;
1392 void (*oldint)(int);
1395 if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0)
1397 oldint = signal(SIGINT, sigint);
1398 if (setjmp(motdinterrupt) == 0)
1399 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
1400 write(fileno(stdout), tbuf, nchars);
1401 signal(SIGINT, oldint);
1407 longjmp(motdinterrupt, 1);
1410 #ifndef HAVE_SECURITY_PAM_MISC_H /* PAM takes care of this */
1412 checknologin(void) {
1416 if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
1417 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
1418 write(fileno(stdout), tbuf, nchars);
1426 dolastlog(int quiet) {
1430 if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
1431 lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET);
1433 if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
1435 time_t ll_time = (time_t) ll.ll_time;
1437 printf(_("Last login: %.*s "),
1438 24-5, ctime(&ll_time));
1440 if (*ll.ll_host != '\0')
1441 printf(_("from %.*s\n"),
1442 (int)sizeof(ll.ll_host), ll.ll_host);
1444 printf(_("on %.*s\n"),
1445 (int)sizeof(ll.ll_line), ll.ll_line);
1447 lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET);
1449 memset((char *)&ll, 0, sizeof(ll));
1454 ll.ll_time = t; /* ll_time is always 32bit */
1457 xstrncpy(ll.ll_line, tty_name, sizeof(ll.ll_line));
1459 xstrncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
1461 write(fd, (char *)&ll, sizeof(ll));
1467 badlogin(const char *name) {
1468 if (failures == 1) {
1470 syslog(LOG_NOTICE, _("LOGIN FAILURE FROM %s, %s"),
1473 syslog(LOG_NOTICE, _("LOGIN FAILURE ON %s, %s"),
1477 syslog(LOG_NOTICE, _("%d LOGIN FAILURES FROM %s, %s"),
1478 failures, hostname, name);
1480 syslog(LOG_NOTICE, _("%d LOGIN FAILURES ON %s, %s"),
1481 failures, tty_name, name);
1485 /* Should not be called from PAM code... */
1487 sleepexit(int eval) {
1488 sleep(SLEEP_EXIT_TIMEOUT);