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>
117 #include "pathnames.h"
119 #include "strutils.h"
124 #ifdef HAVE_SECURITY_PAM_MISC_H
125 # include <security/pam_appl.h>
126 # include <security/pam_misc.h>
127 # define PAM_MAX_LOGIN_TRIES 3
128 # define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \
129 fprintf(stderr,"\n%s\n",pam_strerror(pamh, retcode)); \
130 syslog(LOG_ERR,"%s",pam_strerror(pamh, retcode)); \
131 pam_end(pamh, retcode); exit(EXIT_FAILURE); \
134 pam_setcred(pamh, PAM_DELETE_CRED); \
135 retcode = pam_close_session(pamh,0); \
136 pam_end(pamh,retcode); \
142 #define SLEEP_EXIT_TIMEOUT 5
144 #include "setproctitle.h"
146 #ifndef HAVE_SECURITY_PAM_MISC_H
147 static void getloginname (void);
148 static void checknologin (void);
149 static int rootterm (char *ttyn);
151 static void timedout (int);
152 static void sigint (int);
153 static void motd (void);
154 static void dolastlog (int quiet);
157 # define TTY_MODE 0620
159 # define TTY_MODE 0600
162 #define TTYGRPNAME "tty" /* name of group to own ttys */
165 # define MAXPATHLEN 1024
169 * This bounds the time given to login. Not a define so it can
170 * be patched on machines where it's too small.
176 #ifdef HAVE_SECURITY_PAM_MISC_H
177 static struct passwd pwdcopy;
179 char hostaddress[16]; /* used in checktty.c */
180 sa_family_t hostfamily; /* used in checktty.c */
181 char *hostname; /* idem */
182 static char *username, *tty_name, *tty_number;
183 static char thishost[100];
184 static int failures = 1;
187 /* Nice and simple code provided by Linus Torvalds 16-Feb-93 */
188 /* Nonblocking stuff by Maciej W. Rozycki, macro@ds2.pg.gda.pl, 1999.
189 He writes: "Login performs open() on a tty in a blocking mode.
190 In some cases it may make login wait in open() for carrier infinitely,
191 for example if the line is a simplistic case of a three-wire serial
192 connection. I believe login should open the line in the non-blocking mode
193 leaving the decision to make a connection to getty (where it actually
196 opentty(const char * tty) {
199 fd = open(tty, O_RDWR | O_NONBLOCK);
201 syslog(LOG_ERR, _("FATAL: can't reopen tty: %s"),
203 sleepexit(EXIT_FAILURE);
208 syslog(LOG_ERR, _("FATAL: %s is not a terminal"), tty);
209 sleepexit(EXIT_FAILURE);
212 flags = fcntl(fd, F_GETFL);
213 flags &= ~O_NONBLOCK;
214 fcntl(fd, F_SETFL, flags);
216 for (i = 0; i < fd; i++)
218 for (i = 0; i < 3; i++)
225 /* In case login is suid it was possible to use a hardlink as stdin
226 and exploit races for a local root exploit. (Wojciech Purczynski). */
227 /* More precisely, the problem is ttyn := ttyname(0); ...; chown(ttyn);
228 here ttyname() might return "/tmp/x", a hardlink to a pseudotty. */
229 /* All of this is a problem only when login is suid, which it isnt. */
231 check_ttyname(char *ttyn) {
236 || lstat(ttyn, &statbuf)
237 || !S_ISCHR(statbuf.st_mode)
238 || (statbuf.st_nlink > 1 && strncmp(ttyn, "/dev/", 5))
239 || (access(ttyn, R_OK | W_OK) != 0)) {
241 syslog(LOG_ERR, _("FATAL: bad tty"));
242 sleepexit(EXIT_FAILURE);
246 #ifdef LOGIN_CHOWN_VCS
247 /* true if the filedescriptor fd is a console tty, very Linux specific */
252 if ((fstat(fd, &stb) >= 0)
253 && (major(stb.st_rdev) == TTY_MAJOR)
254 && (minor(stb.st_rdev) < 64)) {
261 #ifdef HAVE_SECURITY_PAM_MISC_H
263 * Log failed login attempts in _PATH_BTMP if that exists.
264 * Must be called only with username the name of an actual user.
265 * The most common login failure is to give password instead of username.
267 #define _PATH_BTMP "/var/log/btmp"
269 logbtmp(const char *line, const char *username, const char *hostname) {
273 memset(&ut, 0, sizeof(ut));
275 strncpy(ut.ut_user, username ? username : "(unknown)",
278 strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
279 xstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
281 #if defined(_HAVE_UT_TV) /* in <utmpbits.h> included by <utmp.h> */
282 gettimeofday(&tv, NULL);
283 ut.ut_tv.tv_sec = tv.tv_sec;
284 ut.ut_tv.tv_usec = tv.tv_usec;
289 ut.ut_time = t; /* ut_time is not always a time_t */
293 ut.ut_type = LOGIN_PROCESS; /* XXX doesn't matter */
296 xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
298 memcpy(&ut.ut_addr_v6, hostaddress, sizeof(ut.ut_addr_v6));
300 #if HAVE_UPDWTMP /* bad luck for ancient systems */
301 updwtmp(_PATH_BTMP, &ut);
306 static int child_pid = 0;
307 static volatile int got_sig = 0;
310 * This handler allows to inform a shell about signals to login. If you have
311 * (root) permissions you can kill all login childrent by one signal to login
314 * Also, parent who is session leader is able (before setsid() in child) to
315 * inform child when controlling tty goes away (e.g. modem hangup, SIGHUP).
318 sig_handler(int signal)
321 kill(-child_pid, signal);
324 if(signal == SIGTERM)
325 kill(-child_pid, SIGHUP); /* because the shell often ignores SIGTERM */
328 #endif /* HAVE_SECURITY_PAM_MISC_H */
332 logaudit(const char *tty, const char *username, const char *hostname,
333 struct passwd *pwd, int status)
337 audit_fd = audit_open();
340 if (!pwd && username)
341 pwd = getpwnam(username);
343 audit_log_acct_message(audit_fd, AUDIT_USER_LOGIN,
344 NULL, "login", username ? username : "(unknown)",
345 pwd ? pwd->pw_uid : (unsigned int) -1, hostname, NULL, tty, status);
349 #else /* ! HAVE_LIBAUDIT */
350 # define logaudit(tty, username, hostname, pwd, status)
351 #endif /* HAVE_LIBAUDIT */
353 #ifdef HAVE_SECURITY_PAM_MISC_H
354 /* encapsulate stupid "void **" pam_get_item() API */
356 get_pam_username(pam_handle_t *pamh, char **name)
358 const void *item = (void *) *name;
360 rc = pam_get_item(pamh, PAM_USER, &item);
361 *name = (char *) item;
367 * We need to check effective UID/GID. For example $HOME could be on root
368 * squashed NFS or on NFS with UID mapping and access(2) uses real UID/GID.
369 * The open(2) seems as the surest solution.
370 * -- kzak@redhat.com (10-Apr-2009)
373 effective_access(const char *path, int mode)
375 int fd = open(path, mode);
378 return fd == -1 ? -1 : 0;
382 main(int argc, char **argv)
385 extern char *optarg, **environ;
389 int fflag, hflag, pflag, cnt;
390 int quietlog, passwd_req;
392 char tbuf[MAXPATHLEN + 2];
397 #ifdef HAVE_SECURITY_PAM_MISC_H
399 pam_handle_t *pamh = NULL;
400 struct pam_conv conv = { misc_conv, NULL };
401 struct sigaction sa, oldsa_hup, oldsa_term;
406 #ifdef LOGIN_CHOWN_VCS
407 char vcsn[20], vcsan[20];
412 signal(SIGALRM, timedout);
413 siginterrupt(SIGALRM,1); /* we have to interrupt syscalls like ioclt() */
414 alarm((unsigned int)timeout);
415 signal(SIGQUIT, SIG_IGN);
416 signal(SIGINT, SIG_IGN);
418 setlocale(LC_ALL, "");
419 bindtextdomain(PACKAGE, LOCALEDIR);
422 setpriority(PRIO_PROCESS, 0, 0);
423 initproctitle(argc, argv);
426 * -p is used by getty to tell login not to destroy the environment
427 * -f is used to skip a second login authentication
428 * -h is used by other servers to pass the name of the remote
429 * host to login so that it may be placed in utmp and wtmp
431 gethostname(tbuf, sizeof(tbuf));
432 xstrncpy(thishost, tbuf, sizeof(thishost));
433 domain = strchr(tbuf, '.');
435 username = tty_name = hostname = NULL;
436 fflag = hflag = pflag = 0;
439 while ((ch = getopt(argc, argv, "fh:p")) != -1)
448 _("login: -h for super-user only.\n"));
452 if (domain && (p = strchr(optarg, '.')) &&
453 strcasecmp(p, domain) == 0)
456 hostname = strdup(optarg); /* strdup: Ambrose C. Li */
458 struct addrinfo hints, *info = NULL;
460 memset(&hints, 0, sizeof(hints));
461 hints.ai_flags = AI_ADDRCONFIG;
465 if (getaddrinfo(hostname, NULL, &hints, &info)==0 && info) {
466 if (info->ai_family == AF_INET) {
467 struct sockaddr_in *sa =
468 (struct sockaddr_in *) info->ai_addr;
469 memcpy(hostaddress, &(sa->sin_addr),
470 sizeof(sa->sin_addr));
472 else if (info->ai_family == AF_INET6) {
473 struct sockaddr_in6 *sa =
474 (struct sockaddr_in6 *) info->ai_addr;
475 memcpy(hostaddress, &(sa->sin6_addr),
476 sizeof(sa->sin6_addr));
478 hostfamily = info->ai_family;
491 _("usage: login [-fp] [username]\n"));
497 #ifndef HAVE_SECURITY_PAM_MISC_H
498 ask = *argv ? 0 : 1; /* Do we need ask for login name? */
503 username = strdup(p);
505 /* wipe name - some people mistype their password here */
506 /* (of course we are too late, but perhaps this helps a little ..) */
511 for (cnt = getdtablesize(); cnt > 2; cnt--)
514 /* note that libc checks that the file descriptor is a terminal, so we don't
515 * have to call isatty() here */
519 if (strncmp(ttyn, "/dev/", 5) == 0)
524 if (strncmp(ttyn, "/dev/tty", 8) == 0)
528 while (*p && !isdigit(*p)) p++;
532 #ifdef LOGIN_CHOWN_VCS
533 /* find names of Virtual Console devices, for later mode change */
534 snprintf(vcsn, sizeof(vcsn), "/dev/vcs%s", tty_number);
535 snprintf(vcsan, sizeof(vcsan), "/dev/vcsa%s", tty_number);
538 /* set pgid to pid */
540 /* this means that setsid() will fail */
543 struct termios tt, ttt;
547 ttt.c_cflag &= ~HUPCL;
549 /* These can fail, e.g. with ttyn on a read-only filesystem */
550 if (fchown(0, 0, 0)) {
551 ; /* glibc warn_unused_result */
556 /* Kill processes left on this tty */
557 tcsetattr(0,TCSAFLUSH,&ttt);
558 signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */
560 signal(SIGHUP, SIG_DFL);
562 /* open stdin,stdout,stderr to the tty */
565 /* restore tty modes */
566 tcsetattr(0,TCSAFLUSH,&tt);
569 openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
572 /* other than iso-8859-1 */
574 fprintf(stderr,"\033(K");
577 #ifdef HAVE_SECURITY_PAM_MISC_H
579 * username is initialized to NULL
580 * and if specified on the command line it is set.
581 * Therefore, we are safe not setting it to anything
584 retcode = pam_start(hflag?"remote":"login",username, &conv, &pamh);
585 if(retcode != PAM_SUCCESS) {
586 warnx(_("PAM failure, aborting: %s"), pam_strerror(pamh, retcode));
587 syslog(LOG_ERR, _("Couldn't initialize PAM: %s"),
588 pam_strerror(pamh, retcode));
591 /* hostname & tty are either set to NULL or their correct values,
592 depending on how much we know */
593 retcode = pam_set_item(pamh, PAM_RHOST, hostname);
595 retcode = pam_set_item(pamh, PAM_TTY, tty_name);
599 * Andrew.Taylor@cal.montage.ca: Provide a user prompt to PAM
600 * so that the "login: " prompt gets localized. Unfortunately,
601 * PAM doesn't have an interface to specify the "Password: " string
604 retcode = pam_set_item(pamh, PAM_USER_PROMPT, _("login: "));
609 * other than iso-8859-1
610 * one more time due to reset tty by PAM
613 fprintf(stderr,"\033(K");
617 /* we need't the original username. We have to follow PAM. */
622 /* if fflag == 1, then the user has already been authenticated */
623 if (fflag && (getuid() == 0))
628 if(passwd_req == 1) {
631 /* if we didn't get a user on the command line, set it to NULL */
632 get_pam_username(pamh, &username);
634 /* there may be better ways to deal with some of these
635 conditions, but at least this way I don't think we'll
636 be giving away information... */
637 /* Perhaps someday we can trust that all PAM modules will
638 pay attention to failure count and get rid of MAX_LOGIN_TRIES? */
640 retcode = pam_authenticate(pamh, 0);
641 while((failcount++ < PAM_MAX_LOGIN_TRIES) &&
642 ((retcode == PAM_AUTH_ERR) ||
643 (retcode == PAM_USER_UNKNOWN) ||
644 (retcode == PAM_CRED_INSUFFICIENT) ||
645 (retcode == PAM_AUTHINFO_UNAVAIL))) {
646 get_pam_username(pamh, &username);
648 syslog(LOG_NOTICE,_("FAILED LOGIN %d FROM %s FOR %s, %s"),
649 failcount, hostname, username, pam_strerror(pamh, retcode));
650 logbtmp(tty_name, username, hostname);
651 logaudit(tty_name, username, hostname, NULL, 0);
653 fprintf(stderr,_("Login incorrect\n\n"));
654 pam_set_item(pamh,PAM_USER,NULL);
655 retcode = pam_authenticate(pamh, 0);
658 if (retcode != PAM_SUCCESS) {
659 get_pam_username(pamh, &username);
661 if (retcode == PAM_MAXTRIES)
662 syslog(LOG_NOTICE,_("TOO MANY LOGIN TRIES (%d) FROM %s FOR "
663 "%s, %s"), failcount, hostname, username,
664 pam_strerror(pamh, retcode));
666 syslog(LOG_NOTICE,_("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
667 hostname, username, pam_strerror(pamh, retcode));
668 logbtmp(tty_name, username, hostname);
669 logaudit(tty_name, username, hostname, NULL, 0);
671 fprintf(stderr,_("\nLogin incorrect\n"));
672 pam_end(pamh, retcode);
678 * Authentication may be skipped (for example, during krlogin, rlogin, etc...),
679 * but it doesn't mean that we can skip other account checks. The account
680 * could be disabled or password expired (althought kerberos ticket is valid).
681 * -- kzak@redhat.com (22-Feb-2006)
683 retcode = pam_acct_mgmt(pamh, 0);
685 if(retcode == PAM_NEW_AUTHTOK_REQD) {
686 retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
692 * Grab the user information out of the password file for future usage
693 * First get the username that we are actually using, though.
695 retcode = get_pam_username(pamh, &username);
698 if (!username || !*username) {
699 warnx(_("\nSession setup problem, abort."));
700 syslog(LOG_ERR, _("NULL user name in %s:%d. Abort."),
701 __FUNCTION__, __LINE__);
702 pam_end(pamh, PAM_SYSTEM_ERR);
705 if (!(pwd = getpwnam(username))) {
706 warnx(_("\nSession setup problem, abort."));
707 syslog(LOG_ERR, _("Invalid user name \"%s\" in %s:%d. Abort."),
708 username, __FUNCTION__, __LINE__);
709 pam_end(pamh, PAM_SYSTEM_ERR);
714 * Create a copy of the pwd struct - otherwise it may get
717 memcpy(&pwdcopy, pwd, sizeof(*pwd));
719 pwd->pw_name = strdup(pwd->pw_name);
720 pwd->pw_passwd = strdup(pwd->pw_passwd);
721 pwd->pw_gecos = strdup(pwd->pw_gecos);
722 pwd->pw_dir = strdup(pwd->pw_dir);
723 pwd->pw_shell = strdup(pwd->pw_shell);
724 if (!pwd->pw_name || !pwd->pw_passwd || !pwd->pw_gecos ||
725 !pwd->pw_dir || !pwd->pw_shell) {
726 warnx(_("out of memory"));
727 syslog(LOG_ERR, "Out of memory");
728 pam_end(pamh, PAM_SYSTEM_ERR);
731 username = pwd->pw_name;
734 * Initialize the supplementary group list.
735 * This should be done before pam_setcred because
736 * the PAM modules might add groups during pam_setcred.
738 if (initgroups(username, pwd->pw_gid) < 0) {
739 syslog(LOG_ERR, "initgroups: %m");
740 warnx(_("\nSession setup problem, abort."));
741 pam_end(pamh, PAM_SYSTEM_ERR);
745 retcode = pam_open_session(pamh, 0);
748 retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
749 if (retcode != PAM_SUCCESS)
750 pam_close_session(pamh, 0);
753 #else /* ! HAVE_SECURITY_PAM_MISC_H */
755 for (cnt = 0;; ask = 1) {
762 /* Dirty patch to fix a gigantic security hole when using
763 yellow pages. This problem should be solved by the
764 libraries, and not by programs, but this must be fixed
765 urgently! If the first char of the username is '+', we
767 Feb 95 <alvaro@etsit.upm.es> */
769 if (username[0] == '+') {
770 puts(_("Illegal username"));
772 sleepexit(EXIT_FAILURE);
775 /* (void)strcpy(tbuf, username); why was this here? */
776 if ((pwd = getpwnam(username))) {
780 if ((sp = getspnam(username)))
781 pwd->pw_passwd = sp->sp_pwdp;
783 salt = pwd->pw_passwd;
788 initgroups(username, pwd->pw_gid);
789 checktty(username, tty_name, pwd); /* in checktty.c */
792 /* if user not super-user, check for disabled logins */
793 if (pwd == NULL || pwd->pw_uid)
797 * Disallow automatic login to root; if not invoked by
798 * root, disallow if the uid's differ.
803 passwd_req = pwd->pw_uid == 0 ||
804 (uid && uid != pwd->pw_uid);
808 * If trying to log in as root, but with insecure terminal,
809 * refuse the login attempt.
811 if (pwd && pwd->pw_uid == 0 && !rootterm(tty_name)) {
812 warnx(_("%s login refused on this terminal."),
817 _("LOGIN %s REFUSED FROM %s ON TTY %s"),
818 pwd->pw_name, hostname, tty_name);
821 _("LOGIN %s REFUSED ON TTY %s"),
822 pwd->pw_name, tty_name);
823 logaudit(tty_name, pwd->pw_name, hostname, pwd, 0);
828 * If no pre-authentication and a password exists
829 * for this user, prompt for one and verify it.
831 if (!passwd_req || (pwd && !*pwd->pw_passwd))
834 setpriority(PRIO_PROCESS, 0, -4);
835 pp = getpass(_("Password: "));
838 if (strncmp(pp, "CRYPTO", 6) == 0) {
839 if (pwd && cryptocard()) break;
841 # endif /* CRYPTOCARD */
844 setpriority(PRIO_PROCESS, 0, 0);
848 * If not present in pw file, act as we normally would.
849 * If we aren't Kerberos-authenticated, try the normal
850 * pw file for a password. If that's ok, log the user
851 * in without issueing any tickets.
854 if (pwd && !krb_get_lrealm(realm,1)) {
856 * get TGT for local realm; be careful about uid's
857 * here for ticket file ownership
859 setreuid(geteuid(),pwd->pw_uid);
860 kerror = krb_get_pw_in_tkt(pwd->pw_name, "", realm,
861 "krbtgt", realm, DEFAULT_TKT_LIFE, pp);
863 if (kerror == INTK_OK) {
864 memset(pp, 0, strlen(pp));
865 notickets = 0; /* user got ticket */
869 # endif /* KERBEROS */
870 memset(pp, 0, strlen(pp));
872 if (pwd && !strcmp(p, pwd->pw_passwd))
875 printf(_("Login incorrect\n"));
876 badlogin(username); /* log ALL bad logins */
879 /* we allow 10 tries, but after 3 we start backing off */
882 sleepexit(EXIT_FAILURE);
884 sleep((unsigned int)((cnt - 3) * 5));
887 #endif /* !HAVE_SECURITY_PAM_MISC_H */
889 /* committed to login -- turn off timeout */
890 alarm((unsigned int)0);
894 /* This requires some explanation: As root we may not be able to
895 read the directory of the user if it is on an NFS mounted
896 filesystem. We temporarily set our effective uid to the user-uid
897 making sure that we keep root privs. in the real uid.
899 A portable solution would require a fork(), but we rely on Linux
900 having the BSD setreuid() */
903 char tmpstr[MAXPATHLEN];
904 uid_t ruid = getuid();
905 gid_t egid = getegid();
907 /* avoid snprintf - old systems do not have it, or worse,
908 have a libc in which snprintf is the same as sprintf */
909 if (strlen(pwd->pw_dir) + sizeof(_PATH_HUSHLOGIN) + 2 > MAXPATHLEN)
912 sprintf(tmpstr, "%s/%s", pwd->pw_dir, _PATH_HUSHLOGIN);
913 setregid(-1, pwd->pw_gid);
914 setreuid(0, pwd->pw_uid);
915 quietlog = (effective_access(tmpstr, O_RDONLY) == 0);
916 setuid(0); /* setreuid doesn't do it alone! */
922 /* for linux, write entries in utmp and wtmp */
928 utmpname(_PATH_UTMP);
932 login sometimes overwrites the runlevel entry in /var/run/utmp,
933 confusing sysvinit. I added a test for the entry type, and the problem
934 was gone. (In a runlevel entry, st_pid is not really a pid but some number
935 calculated from the previous and current runlevel).
936 Michael Riepe <michael@stud.uni-hannover.de>
938 while ((utp = getutent()))
939 if (utp->ut_pid == pid
940 && utp->ut_type >= INIT_PROCESS
941 && utp->ut_type <= DEAD_PROCESS)
944 /* If we can't find a pre-existing entry by pid, try by line.
945 BSD network daemons may rely on this. (anonymous) */
948 ut.ut_type = LOGIN_PROCESS;
949 strncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
950 utp = getutline(&ut);
954 memcpy(&ut, utp, sizeof(ut));
956 /* some gettys/telnetds don't initialize utmp... */
957 memset(&ut, 0, sizeof(ut));
960 if (ut.ut_id[0] == 0)
961 strncpy(ut.ut_id, tty_number, sizeof(ut.ut_id));
963 strncpy(ut.ut_user, username, sizeof(ut.ut_user));
964 xstrncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
965 #ifdef _HAVE_UT_TV /* in <utmpbits.h> included by <utmp.h> */
966 gettimeofday(&tv, NULL);
967 ut.ut_tv.tv_sec = tv.tv_sec;
968 ut.ut_tv.tv_usec = tv.tv_usec;
973 ut.ut_time = t; /* ut_time is not always a time_t */
974 /* glibc2 #defines it as ut_tv.tv_sec */
977 ut.ut_type = USER_PROCESS;
980 xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
982 memcpy(&ut.ut_addr_v6, hostaddress, sizeof(ut.ut_addr_v6));
989 updwtmp(_PATH_WTMP, &ut);
992 /* The O_APPEND open() flag should be enough to guarantee
993 atomic writes at end of file. */
997 if((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
998 write(wtmp, (char *)&ut, sizeof(ut));
1003 /* Probably all this locking below is just nonsense,
1004 and the short version is OK as well. */
1007 if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) {
1009 if ((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
1010 write(wtmp, (char *)&ut, sizeof(ut));
1021 logaudit(tty_name, username, hostname, pwd, 1);
1022 dolastlog(quietlog);
1024 if (fchown(0, pwd->pw_uid,
1025 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
1026 warn(_("change terminal owner failed"));
1028 fchmod(0, TTY_MODE);
1030 #ifdef LOGIN_CHOWN_VCS
1031 /* if tty is one of the VC's then change owner and mode of the
1032 special /dev/vcs devices as well */
1033 if (consoletty(0)) {
1035 if (chown(vcsn, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid)))
1036 warn(_("change terminal owner failed"));
1037 if (chown(vcsan, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid)))
1038 warn(_("change terminal owner failed"));
1040 chmod(vcsn, TTY_MODE);
1041 chmod(vcsan, TTY_MODE);
1045 if (setgid(pwd->pw_gid) < 0 && pwd->pw_gid) {
1046 syslog(LOG_ALERT, _("setgid() failed"));
1051 if (*pwd->pw_shell == '\0')
1052 pwd->pw_shell = _PATH_BSHELL;
1054 /* preserve TERM even without -p flag */
1058 if(!((ep = getenv("TERM")) && (termenv = strdup(ep))))
1062 /* destroy environment unless user has requested preservation */
1065 environ = (char**)malloc(sizeof(char*));
1066 memset(environ, 0, sizeof(char*));
1069 setenv("HOME", pwd->pw_dir, 0); /* legal to override */
1071 setenv("PATH", _PATH_DEFPATH, 1);
1073 setenv("PATH", _PATH_DEFPATH_ROOT, 1);
1075 setenv("SHELL", pwd->pw_shell, 1);
1076 setenv("TERM", termenv, 1);
1078 /* mailx will give a funny error msg if you forget this one */
1080 char tmp[MAXPATHLEN];
1081 /* avoid snprintf */
1082 if (sizeof(_PATH_MAILDIR) + strlen(pwd->pw_name) + 1 < MAXPATHLEN) {
1083 sprintf(tmp, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
1084 setenv("MAIL",tmp,0);
1088 /* LOGNAME is not documented in login(1) but
1089 HP-UX 6.5 does it. We'll not allow modifying it.
1091 setenv("LOGNAME", pwd->pw_name, 1);
1093 #ifdef HAVE_SECURITY_PAM_MISC_H
1096 char ** env = pam_getenvlist(pamh);
1099 for (i=0; env[i]; i++) {
1101 /* D(("env[%d] = %s", i,env[i])); */
1107 setproctitle("login", username);
1109 if (!strncmp(tty_name, "ttyS", 4))
1110 syslog(LOG_INFO, _("DIALUP AT %s BY %s"), tty_name, pwd->pw_name);
1112 /* allow tracking of good logins.
1113 -steve philp (sphilp@mail.alliance.net) */
1115 if (pwd->pw_uid == 0) {
1117 syslog(LOG_NOTICE, _("ROOT LOGIN ON %s FROM %s"),
1118 tty_name, hostname);
1120 syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), tty_name);
1123 syslog(LOG_INFO, _("LOGIN ON %s BY %s FROM %s"), tty_name,
1124 pwd->pw_name, hostname);
1126 syslog(LOG_INFO, _("LOGIN ON %s BY %s"), tty_name,
1133 #ifdef LOGIN_STAT_MAIL
1135 * This turns out to be a bad idea: when the mail spool
1136 * is NFS mounted, and the NFS connection hangs, the
1137 * login hangs, even root cannot login.
1138 * Checking for mail should be done from the shell.
1144 mail = getenv("MAIL");
1145 if (mail && stat(mail, &st) == 0 && st.st_size != 0) {
1146 if (st.st_mtime > st.st_atime)
1147 printf(_("You have new mail.\n"));
1149 printf(_("You have mail.\n"));
1155 signal(SIGALRM, SIG_DFL);
1156 signal(SIGQUIT, SIG_DFL);
1157 signal(SIGTSTP, SIG_IGN);
1159 #ifdef HAVE_SECURITY_PAM_MISC_H
1161 memset(&sa, 0, sizeof(sa));
1162 sa.sa_handler = SIG_IGN;
1163 sigaction(SIGINT, &sa, NULL);
1165 sigaction(SIGHUP, &sa, &oldsa_hup); /* ignore when TIOCNOTTY */
1168 * detach the controlling tty
1169 * -- we needn't the tty in parent who waits for child only.
1170 * The child calls setsid() that detach from the tty as well.
1172 ioctl(0, TIOCNOTTY, NULL);
1175 * We have care about SIGTERM, because leave PAM session without
1176 * pam_close_session() is pretty bad thing.
1178 sa.sa_handler = sig_handler;
1179 sigaction(SIGHUP, &sa, NULL);
1180 sigaction(SIGTERM, &sa, &oldsa_term);
1185 * We must fork before setuid() because we need to call
1186 * pam_close_session() as root.
1190 if (child_pid < 0) {
1191 /* error in fork() */
1192 warn(_("failure forking"));
1198 /* parent - wait for child to finish, then cleanup session */
1202 sa.sa_handler = SIG_IGN;
1203 sigaction(SIGQUIT, &sa, NULL);
1204 sigaction(SIGINT, &sa, NULL);
1206 /* wait as long as any child is there */
1207 while(wait(NULL) == -1 && errno == EINTR)
1209 openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
1216 /* restore to old state */
1217 sigaction(SIGHUP, &oldsa_hup, NULL);
1218 sigaction(SIGTERM, &oldsa_term, NULL);
1223 * Problem: if the user's shell is a shell like ash that doesnt do
1224 * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
1225 * process in the pgrp, will kill us.
1228 /* start new session */
1231 /* make sure we have a controlling tty */
1233 openlog("login", LOG_ODELAY, LOG_AUTHPRIV); /* reopen */
1236 * TIOCSCTTY: steal tty from other process group.
1238 if (ioctl(0, TIOCSCTTY, 1))
1239 syslog(LOG_ERR, _("TIOCSCTTY failed: %m"));
1241 signal(SIGINT, SIG_DFL);
1243 /* discard permissions last so can't get killed and drop core */
1244 if(setuid(pwd->pw_uid) < 0 && pwd->pw_uid) {
1245 syslog(LOG_ALERT, _("setuid() failed"));
1249 /* wait until here to change directory! */
1250 if (chdir(pwd->pw_dir) < 0) {
1251 warn(_("%s: change directory failed"), pwd->pw_dir);
1255 printf(_("Logging in with home = \"/\".\n"));
1258 /* if the shell field has a space: treat it like a shell script */
1259 if (strchr(pwd->pw_shell, ' ')) {
1260 buff = xmalloc(strlen(pwd->pw_shell) + 6);
1262 strcpy(buff, "exec ");
1263 strcat(buff, pwd->pw_shell);
1264 childArgv[childArgc++] = "/bin/sh";
1265 childArgv[childArgc++] = "-sh";
1266 childArgv[childArgc++] = "-c";
1267 childArgv[childArgc++] = buff;
1270 xstrncpy(tbuf + 1, ((p = strrchr(pwd->pw_shell, '/')) ?
1271 p + 1 : pwd->pw_shell),
1274 childArgv[childArgc++] = pwd->pw_shell;
1275 childArgv[childArgc++] = tbuf;
1278 childArgv[childArgc++] = NULL;
1280 execvp(childArgv[0], childArgv + 1);
1282 if (!strcmp(childArgv[0], "/bin/sh"))
1283 warn(_("couldn't exec shell script"));
1285 warn(_("no shell"));
1290 #ifndef HAVE_SECURITY_PAM_MISC_H
1292 getloginname(void) {
1295 static char nbuf[UT_NAMESIZE + 1];
1300 printf(_("\n%s login: "), thishost); fflush(stdout);
1301 for (p = nbuf; (ch = getchar()) != '\n'; ) {
1306 if (p < nbuf + UT_NAMESIZE)
1310 if (cnt > UT_NAMESIZE + 20) {
1311 badlogin(_("NAME too long"));
1312 errx(EXIT_FAILURE, _("login name much too long."));
1317 warnx(_("login names may not start with '-'."));
1327 badlogin(_("EXCESSIVE linefeeds"));
1328 errx(EXIT_FAILURE, _("too many bare linefeeds."));
1335 * Robert Ambrose writes:
1336 * A couple of my users have a problem with login processes hanging around
1337 * soaking up pts's. What they seem to hung up on is trying to write out the
1338 * message 'Login timed out after %d seconds' when the connection has already
1340 * What I did was add a second timeout while trying to write the message so
1341 * the process just exits if the second timeout expires.
1345 timedout2(int sig __attribute__((__unused__))) {
1351 tcsetattr(0, TCSANOW, &ti);
1352 exit(EXIT_SUCCESS); /* %% */
1356 timedout(int sig __attribute__((__unused__))) {
1357 signal(SIGALRM, timedout2);
1359 /* TRANSLATORS: The standard value for %d is 60. */
1360 warnx(_("timed out after %d seconds"), timeout);
1361 signal(SIGALRM, SIG_IGN);
1366 #ifndef HAVE_SECURITY_PAM_MISC_H
1368 rootterm(char * ttyn)
1374 fd = open(_PATH_SECURETTY, O_RDONLY);
1375 if(fd < 0) return 1;
1377 /* read each line in /etc/securetty, if a line matches our ttyline
1378 then root is allowed to login on this tty, and we should return
1382 while(--cnt >= 0 && (more = read(fd, p, 1)) == 1 && *p != '\n') p++;
1383 if(more && *p == '\n') {
1385 if(!strcmp(buf, ttyn)) {
1396 #endif /* !HAVE_SECURITY_PAM_MISC_H */
1398 jmp_buf motdinterrupt;
1403 void (*oldint)(int);
1406 if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0)
1408 oldint = signal(SIGINT, sigint);
1409 if (setjmp(motdinterrupt) == 0)
1410 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) {
1411 if (write(fileno(stdout), tbuf, nchars)) {
1412 ; /* glibc warn_unused_result */
1415 signal(SIGINT, oldint);
1420 sigint(int sig __attribute__((__unused__))) {
1421 longjmp(motdinterrupt, 1);
1424 #ifndef HAVE_SECURITY_PAM_MISC_H /* PAM takes care of this */
1426 checknologin(void) {
1430 if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
1431 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) {
1432 if (write(fileno(stdout), tbuf, nchars)) {
1433 ; /* glibc warn_unused_result */
1437 sleepexit(EXIT_SUCCESS);
1443 dolastlog(int quiet) {
1447 if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
1448 lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET);
1450 if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
1452 time_t ll_time = (time_t) ll.ll_time;
1454 printf(_("Last login: %.*s "),
1455 24-5, ctime(&ll_time));
1457 if (*ll.ll_host != '\0')
1458 printf(_("from %.*s\n"),
1459 (int)sizeof(ll.ll_host), ll.ll_host);
1461 printf(_("on %.*s\n"),
1462 (int)sizeof(ll.ll_line), ll.ll_line);
1464 lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET);
1466 memset((char *)&ll, 0, sizeof(ll));
1471 ll.ll_time = t; /* ll_time is always 32bit */
1474 xstrncpy(ll.ll_line, tty_name, sizeof(ll.ll_line));
1476 xstrncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
1478 if (write(fd, (char *)&ll, sizeof(ll)) < 0)
1479 warn(_("write lastlog failed"));
1485 badlogin(const char *name) {
1486 if (failures == 1) {
1488 syslog(LOG_NOTICE, _("LOGIN FAILURE FROM %s, %s"),
1491 syslog(LOG_NOTICE, _("LOGIN FAILURE ON %s, %s"),
1495 syslog(LOG_NOTICE, _("%d LOGIN FAILURES FROM %s, %s"),
1496 failures, hostname, name);
1498 syslog(LOG_NOTICE, _("%d LOGIN FAILURES ON %s, %s"),
1499 failures, tty_name, name);
1503 /* Should not be called from PAM code... */
1505 sleepexit(int eval) {
1506 sleep(SLEEP_EXIT_TIMEOUT);