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.)
85 #include <sys/param.h>
96 #include <sys/resource.h>
101 #define rindex strrchr
102 #include <sys/ioctl.h>
103 #include <sys/wait.h>
112 #include <sys/syslog.h>
113 #include <sys/sysmacros.h>
115 #include "pathnames.h"
116 #include "my_crypt.h"
118 #include "xstrncpy.h"
122 # include <sys/sysmacros.h>
123 # include <linux/major.h>
137 # include <security/pam_appl.h>
138 # include <security/pam_misc.h>
139 # define PAM_MAX_LOGIN_TRIES 3
140 # define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \
141 fprintf(stderr,"\n%s\n",pam_strerror(pamh, retcode)); \
142 syslog(LOG_ERR,"%s",pam_strerror(pamh, retcode)); \
143 pam_end(pamh, retcode); exit(1); \
146 pam_setcred(pamh, PAM_DELETE_CRED); \
147 retcode = pam_close_session(pamh,0); \
148 pam_end(pamh,retcode); \
157 #define SLEEP_EXIT_TIMEOUT 5
160 #define DO_PS_FIDDLING
163 #ifdef DO_PS_FIDDLING
164 #include "setproctitle.h"
168 /* from before we had a lastlog.h file in linux */
177 static void getloginname (void);
178 static void checknologin (void);
179 static int rootterm (char *ttyn
);
181 static void timedout (int);
182 static void sigint (int);
183 static void motd (void);
184 static void dolastlog (int quiet
);
187 #include "cryptocard.h"
191 #include <kerberos/krb.h>
192 #include <sys/termios.h>
193 char realm
[REALM_SZ
];
194 int kerror
= KSUCCESS
, notickets
= 1;
198 # define TTY_MODE 0620
200 # define TTY_MODE 0600
203 #define TTYGRPNAME "tty" /* name of group to own ttys */
206 # define MAXPATHLEN 1024
210 * This bounds the time given to login. Not a define so it can
211 * be patched on machines where it's too small.
216 int timeout
= 60; /* used in cryptocard.c */
219 struct passwd
*pwd
; /* used in cryptocard.c */
221 static struct passwd pwdcopy
;
223 char hostaddress
[4]; /* used in checktty.c */
224 char *hostname
; /* idem */
225 static char *username
, *tty_name
, *tty_number
;
226 static char thishost
[100];
227 static int failures
= 1;
231 struct sgttyb sgttyb
;
233 CINTR
, CQUIT
, CSTART
, CSTOP
, CEOT
, CBRK
235 struct ltchars ltc
= {
236 CSUSP
, CDSUSP
, CRPRNT
, CFLUSH
, CWERASE
, CLNEXT
240 /* Nice and simple code provided by Linus Torvalds 16-Feb-93 */
241 /* Nonblocking stuff by Maciej W. Rozycki, macro@ds2.pg.gda.pl, 1999.
242 He writes: "Login performs open() on a tty in a blocking mode.
243 In some cases it may make login wait in open() for carrier infinitely,
244 for example if the line is a simplistic case of a three-wire serial
245 connection. I believe login should open the line in the non-blocking mode
246 leaving the decision to make a connection to getty (where it actually
249 opentty(const char * tty
) {
252 fd
= open(tty
, O_RDWR
| O_NONBLOCK
);
254 syslog(LOG_ERR
, _("FATAL: can't reopen tty: %s"),
260 flags
= fcntl(fd
, F_GETFL
);
261 flags
&= ~O_NONBLOCK
;
262 fcntl(fd
, F_SETFL
, flags
);
264 for (i
= 0; i
< fd
; i
++)
266 for (i
= 0; i
< 3; i
++)
273 /* In case login is suid it was possible to use a hardlink as stdin
274 and exploit races for a local root exploit. (Wojciech Purczynski). */
275 /* More precisely, the problem is ttyn := ttyname(0); ...; chown(ttyn);
276 here ttyname() might return "/tmp/x", a hardlink to a pseudotty. */
277 /* All of this is a problem only when login is suid, which it isnt. */
279 check_ttyname(char *ttyn
) {
282 if (lstat(ttyn
, &statbuf
)
283 || !S_ISCHR(statbuf
.st_mode
)
284 || (statbuf
.st_nlink
> 1 && strncmp(ttyn
, "/dev/", 5))) {
285 syslog(LOG_ERR
, _("FATAL: bad tty"));
291 /* true if the filedescriptor fd is a console tty, very Linux specific */
297 if ((fstat(fd
, &stb
) >= 0)
298 && (major(stb
.st_rdev
) == TTY_MAJOR
)
299 && (minor(stb
.st_rdev
) < 64)) {
308 * Log failed login attempts in _PATH_BTMP if that exists.
309 * Must be called only with username the name of an actual user.
310 * The most common login failure is to give password instead of username.
312 #define _PATH_BTMP "/var/log/btmp"
314 logbtmp(const char *line
, const char *username
, const char *hostname
) {
317 memset(&ut
, 0, sizeof(ut
));
319 strncpy(ut
.ut_user
, username
? username
: "(unknown)",
322 strncpy(ut
.ut_id
, line
+ 3, sizeof(ut
.ut_id
));
323 xstrncpy(ut
.ut_line
, line
, sizeof(ut
.ut_line
));
325 #if defined(_HAVE_UT_TV) /* in <utmpbits.h> included by <utmp.h> */
326 gettimeofday(&ut
.ut_tv
, NULL
);
331 ut
.ut_time
= t
; /* ut_time is not always a time_t */
335 ut
.ut_type
= LOGIN_PROCESS
; /* XXX doesn't matter */
338 xstrncpy(ut
.ut_host
, hostname
, sizeof(ut
.ut_host
));
340 memcpy(&ut
.ut_addr
, hostaddress
, sizeof(ut
.ut_addr
));
342 #ifdef HAVE_updwtmp /* bad luck for ancient systems */
343 updwtmp(_PATH_BTMP
, &ut
);
349 main(int argc
, char **argv
)
352 extern char *optarg
, **environ
;
356 int ask
, fflag
, hflag
, pflag
, cnt
, errsv
;
357 int quietlog
, passwd_req
;
359 char tbuf
[MAXPATHLEN
+ 2], tname
[sizeof(_PATH_TTY
) + 10];
366 pam_handle_t
*pamh
= NULL
;
367 struct pam_conv conv
= { misc_conv
, NULL
};
373 char vcsn
[20], vcsan
[20];
378 signal(SIGALRM
, timedout
);
379 alarm((unsigned int)timeout
);
380 signal(SIGQUIT
, SIG_IGN
);
381 signal(SIGINT
, SIG_IGN
);
383 setlocale(LC_ALL
, "");
384 bindtextdomain(PACKAGE
, LOCALEDIR
);
387 setpriority(PRIO_PROCESS
, 0, 0);
389 quota(Q_SETUID
, 0, 0, 0);
391 #ifdef DO_PS_FIDDLING
392 initproctitle(argc
, argv
);
396 * -p is used by getty to tell login not to destroy the environment
397 * -f is used to skip a second login authentication
398 * -h is used by other servers to pass the name of the remote
399 * host to login so that it may be placed in utmp and wtmp
401 gethostname(tbuf
, sizeof(tbuf
));
402 xstrncpy(thishost
, tbuf
, sizeof(thishost
));
403 domain
= index(tbuf
, '.');
405 username
= tty_name
= hostname
= NULL
;
406 fflag
= hflag
= pflag
= 0;
409 while ((ch
= getopt(argc
, argv
, "fh:p")) != -1)
418 _("login: -h for super-user only.\n"));
422 if (domain
&& (p
= index(optarg
, '.')) &&
423 strcasecmp(p
, domain
) == 0)
426 hostname
= strdup(optarg
); /* strdup: Ambrose C. Li */
428 struct hostent
*he
= gethostbyname(hostname
);
430 /* he points to static storage; copy the part we use */
432 if (he
&& he
->h_addr_list
&& he
->h_addr_list
[0])
433 memcpy(hostaddress
, he
->h_addr_list
[0],
434 sizeof(hostaddress
));
445 _("usage: login [-fp] [username]\n"));
452 username
= strdup(p
);
454 /* wipe name - some people mistype their password here */
455 /* (of course we are too late, but perhaps this helps a little ..) */
461 for (cnt
= getdtablesize(); cnt
> 2; cnt
--)
466 if (ttyn
== NULL
|| *ttyn
== '\0') {
467 /* no snprintf required - see definition of tname */
468 sprintf(tname
, "%s??", _PATH_TTY
);
474 if (strncmp(ttyn
, "/dev/", 5) == 0)
479 if (strncmp(ttyn
, "/dev/tty", 8) == 0)
483 while (*p
&& !isdigit(*p
)) p
++;
488 /* find names of Virtual Console devices, for later mode change */
489 snprintf(vcsn
, sizeof(vcsn
), "/dev/vcs%s", tty_number
);
490 snprintf(vcsan
, sizeof(vcsan
), "/dev/vcsa%s", tty_number
);
493 /* set pgid to pid */
495 /* this means that setsid() will fail */
498 struct termios tt
, ttt
;
502 ttt
.c_cflag
&= ~HUPCL
;
504 /* These can fail, e.g. with ttyn on a read-only filesystem */
506 chmod(ttyn
, TTY_MODE
);
508 /* Kill processes left on this tty */
509 tcsetattr(0,TCSAFLUSH
,&ttt
);
510 signal(SIGHUP
, SIG_IGN
); /* so vhangup() wont kill us */
512 signal(SIGHUP
, SIG_DFL
);
514 /* open stdin,stdout,stderr to the tty */
517 /* restore tty modes */
518 tcsetattr(0,TCSAFLUSH
,&tt
);
521 openlog("login", LOG_ODELAY
, LOG_AUTHPRIV
);
524 /* other than iso-8859-1 */
526 fprintf(stderr
,"\033(K");
531 * username is initialized to NULL
532 * and if specified on the command line it is set.
533 * Therefore, we are safe not setting it to anything
536 retcode
= pam_start("login",username
, &conv
, &pamh
);
537 if(retcode
!= PAM_SUCCESS
) {
538 fprintf(stderr
, _("login: PAM Failure, aborting: %s\n"),
539 pam_strerror(pamh
, retcode
));
540 syslog(LOG_ERR
, _("Couldn't initialize PAM: %s"),
541 pam_strerror(pamh
, retcode
));
544 /* hostname & tty are either set to NULL or their correct values,
545 depending on how much we know */
546 retcode
= pam_set_item(pamh
, PAM_RHOST
, hostname
);
548 retcode
= pam_set_item(pamh
, PAM_TTY
, tty_name
);
552 * Andrew.Taylor@cal.montage.ca: Provide a user prompt to PAM
553 * so that the "login: " prompt gets localized. Unfortunately,
554 * PAM doesn't have an interface to specify the "Password: " string
557 retcode
= pam_set_item(pamh
, PAM_USER_PROMPT
, _("login: "));
562 * other than iso-8859-1
563 * one more time due to reset tty by PAM
566 fprintf(stderr
,"\033(K");
569 /* if fflag == 1, then the user has already been authenticated */
570 if (fflag
&& (getuid() == 0))
575 if(passwd_req
== 1) {
578 /* if we didn't get a user on the command line, set it to NULL */
579 pam_get_item(pamh
, PAM_USER
, (const void **) &username
);
581 pam_set_item(pamh
, PAM_USER
, NULL
);
583 /* there may be better ways to deal with some of these
584 conditions, but at least this way I don't think we'll
585 be giving away information... */
586 /* Perhaps someday we can trust that all PAM modules will
587 pay attention to failure count and get rid of MAX_LOGIN_TRIES? */
589 retcode
= pam_authenticate(pamh
, 0);
590 while((failcount
++ < PAM_MAX_LOGIN_TRIES
) &&
591 ((retcode
== PAM_AUTH_ERR
) ||
592 (retcode
== PAM_USER_UNKNOWN
) ||
593 (retcode
== PAM_CRED_INSUFFICIENT
) ||
594 (retcode
== PAM_AUTHINFO_UNAVAIL
))) {
595 pam_get_item(pamh
, PAM_USER
, (const void **) &username
);
597 syslog(LOG_NOTICE
,_("FAILED LOGIN %d FROM %s FOR %s, %s"),
598 failcount
, hostname
, username
, pam_strerror(pamh
, retcode
));
599 logbtmp(tty_name
, username
, hostname
);
601 fprintf(stderr
,_("Login incorrect\n\n"));
602 pam_set_item(pamh
,PAM_USER
,NULL
);
603 retcode
= pam_authenticate(pamh
, 0);
606 if (retcode
!= PAM_SUCCESS
) {
607 pam_get_item(pamh
, PAM_USER
, (const void **) &username
);
609 if (retcode
== PAM_MAXTRIES
)
610 syslog(LOG_NOTICE
,_("TOO MANY LOGIN TRIES (%d) FROM %s FOR "
611 "%s, %s"), failcount
, hostname
, username
,
612 pam_strerror(pamh
, retcode
));
614 syslog(LOG_NOTICE
,_("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
615 hostname
, username
, pam_strerror(pamh
, retcode
));
616 logbtmp(tty_name
, username
, hostname
);
618 fprintf(stderr
,_("\nLogin incorrect\n"));
619 pam_end(pamh
, retcode
);
623 retcode
= pam_acct_mgmt(pamh
, 0);
625 if(retcode
== PAM_NEW_AUTHTOK_REQD
) {
626 retcode
= pam_chauthtok(pamh
, PAM_CHANGE_EXPIRED_AUTHTOK
);
633 * Grab the user information out of the password file for future usage
634 * First get the username that we are actually using, though.
636 retcode
= pam_get_item(pamh
, PAM_USER
, (const void **) &username
);
639 if (!username
|| !*username
) {
640 fprintf(stderr
, _("\nSession setup problem, abort.\n"));
641 syslog(LOG_ERR
, _("NULL user name in %s:%d. Abort."),
642 __FUNCTION__
, __LINE__
);
643 pam_end(pamh
, PAM_SYSTEM_ERR
);
646 if (!(pwd
= getpwnam(username
))) {
647 fprintf(stderr
, _("\nSession setup problem, abort.\n"));
648 syslog(LOG_ERR
, _("Invalid user name \"%s\" in %s:%d. Abort."),
649 username
, __FUNCTION__
, __LINE__
);
650 pam_end(pamh
, PAM_SYSTEM_ERR
);
655 * Create a copy of the pwd struct - otherwise it may get
658 memcpy(&pwdcopy
, pwd
, sizeof(*pwd
));
660 pwd
->pw_name
= strdup(pwd
->pw_name
);
661 pwd
->pw_passwd
= strdup(pwd
->pw_passwd
);
662 pwd
->pw_gecos
= strdup(pwd
->pw_gecos
);
663 pwd
->pw_dir
= strdup(pwd
->pw_dir
);
664 pwd
->pw_shell
= strdup(pwd
->pw_shell
);
665 if (!pwd
->pw_name
|| !pwd
->pw_passwd
|| !pwd
->pw_gecos
||
666 !pwd
->pw_dir
|| !pwd
->pw_shell
) {
667 fprintf(stderr
, _("login: Out of memory\n"));
668 syslog(LOG_ERR
, "Out of memory");
669 pam_end(pamh
, PAM_SYSTEM_ERR
);
672 username
= pwd
->pw_name
;
675 * Initialize the supplementary group list.
676 * This should be done before pam_setcred because
677 * the PAM modules might add groups during pam_setcred.
679 if (initgroups(username
, pwd
->pw_gid
) < 0) {
680 syslog(LOG_ERR
, "initgroups: %m");
681 fprintf(stderr
, _("\nSession setup problem, abort.\n"));
682 pam_end(pamh
, PAM_SYSTEM_ERR
);
686 retcode
= pam_open_session(pamh
, 0);
689 retcode
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
);
692 #else /* ! USE_PAM */
694 for (cnt
= 0;; ask
= 1) {
701 /* Dirty patch to fix a gigantic security hole when using
702 yellow pages. This problem should be solved by the
703 libraries, and not by programs, but this must be fixed
704 urgently! If the first char of the username is '+', we
706 Feb 95 <alvaro@etsit.upm.es> */
708 if (username
[0] == '+') {
709 puts(_("Illegal username"));
714 /* (void)strcpy(tbuf, username); why was this here? */
715 if ((pwd
= getpwnam(username
))) {
719 if ((sp
= getspnam(username
)))
720 pwd
->pw_passwd
= sp
->sp_pwdp
;
722 salt
= pwd
->pw_passwd
;
727 initgroups(username
, pwd
->pw_gid
);
728 checktty(username
, tty_name
, pwd
); /* in checktty.c */
731 /* if user not super-user, check for disabled logins */
732 if (pwd
== NULL
|| pwd
->pw_uid
)
736 * Disallow automatic login to root; if not invoked by
737 * root, disallow if the uid's differ.
742 passwd_req
= pwd
->pw_uid
== 0 ||
743 (uid
&& uid
!= pwd
->pw_uid
);
747 * If trying to log in as root, but with insecure terminal,
748 * refuse the login attempt.
750 if (pwd
&& pwd
->pw_uid
== 0 && !rootterm(tty_name
)) {
752 _("%s login refused on this terminal.\n"),
757 _("LOGIN %s REFUSED FROM %s ON TTY %s"),
758 pwd
->pw_name
, hostname
, tty_name
);
761 _("LOGIN %s REFUSED ON TTY %s"),
762 pwd
->pw_name
, tty_name
);
767 * If no pre-authentication and a password exists
768 * for this user, prompt for one and verify it.
770 if (!passwd_req
|| (pwd
&& !*pwd
->pw_passwd
))
773 setpriority(PRIO_PROCESS
, 0, -4);
774 pp
= getpass(_("Password: "));
777 if (strncmp(pp
, "CRYPTO", 6) == 0) {
778 if (pwd
&& cryptocard()) break;
780 # endif /* CRYPTOCARD */
783 setpriority(PRIO_PROCESS
, 0, 0);
787 * If not present in pw file, act as we normally would.
788 * If we aren't Kerberos-authenticated, try the normal
789 * pw file for a password. If that's ok, log the user
790 * in without issueing any tickets.
793 if (pwd
&& !krb_get_lrealm(realm
,1)) {
795 * get TGT for local realm; be careful about uid's
796 * here for ticket file ownership
798 setreuid(geteuid(),pwd
->pw_uid
);
799 kerror
= krb_get_pw_in_tkt(pwd
->pw_name
, "", realm
,
800 "krbtgt", realm
, DEFAULT_TKT_LIFE
, pp
);
802 if (kerror
== INTK_OK
) {
803 memset(pp
, 0, strlen(pp
));
804 notickets
= 0; /* user got ticket */
808 # endif /* KERBEROS */
809 memset(pp
, 0, strlen(pp
));
811 if (pwd
&& !strcmp(p
, pwd
->pw_passwd
))
814 printf(_("Login incorrect\n"));
815 badlogin(username
); /* log ALL bad logins */
818 /* we allow 10 tries, but after 3 we start backing off */
823 sleep((unsigned int)((cnt
- 3) * 5));
826 #endif /* !USE_PAM */
828 /* committed to login -- turn off timeout */
829 alarm((unsigned int)0);
832 if (quota(Q_SETUID
, pwd
->pw_uid
, 0, 0) < 0 && errno
!= EINVAL
) {
836 _("Too many users logged on already.\nTry again later.\n"));
840 _("You have too many processes running.\n"));
843 perror("quota (Q_SETUID)");
845 sleepexit(0); /* %% */
855 /* This requires some explanation: As root we may not be able to
856 read the directory of the user if it is on an NFS mounted
857 filesystem. We temporarily set our effective uid to the user-uid
858 making sure that we keep root privs. in the real uid.
860 A portable solution would require a fork(), but we rely on Linux
861 having the BSD setreuid() */
864 char tmpstr
[MAXPATHLEN
];
865 uid_t ruid
= getuid();
866 gid_t egid
= getegid();
868 /* avoid snprintf - old systems do not have it, or worse,
869 have a libc in which snprintf is the same as sprintf */
870 if (strlen(pwd
->pw_dir
) + sizeof(_PATH_HUSHLOGIN
) + 2 > MAXPATHLEN
)
873 sprintf(tmpstr
, "%s/%s", pwd
->pw_dir
, _PATH_HUSHLOGIN
);
874 setregid(-1, pwd
->pw_gid
);
875 setreuid(0, pwd
->pw_uid
);
876 quietlog
= (access(tmpstr
, R_OK
) == 0);
877 setuid(0); /* setreuid doesn't do it alone! */
883 /* for linux, write entries in utmp and wtmp */
888 utmpname(_PATH_UTMP
);
892 login sometimes overwrites the runlevel entry in /var/run/utmp,
893 confusing sysvinit. I added a test for the entry type, and the problem
894 was gone. (In a runlevel entry, st_pid is not really a pid but some number
895 calculated from the previous and current runlevel).
896 Michael Riepe <michael@stud.uni-hannover.de>
898 while ((utp
= getutent()))
899 if (utp
->ut_pid
== pid
900 && utp
->ut_type
>= INIT_PROCESS
901 && utp
->ut_type
<= DEAD_PROCESS
)
904 /* If we can't find a pre-existing entry by pid, try by line.
905 BSD network daemons may rely on this. (anonymous) */
908 ut
.ut_type
= LOGIN_PROCESS
;
909 strncpy(ut
.ut_id
, tty_number
, sizeof(ut
.ut_id
));
910 strncpy(ut
.ut_line
, tty_name
, sizeof(ut
.ut_line
));
915 memcpy(&ut
, utp
, sizeof(ut
));
917 /* some gettys/telnetds don't initialize utmp... */
918 memset(&ut
, 0, sizeof(ut
));
921 if (ut
.ut_id
[0] == 0)
922 strncpy(ut
.ut_id
, tty_number
, sizeof(ut
.ut_id
));
924 strncpy(ut
.ut_user
, username
, sizeof(ut
.ut_user
));
925 xstrncpy(ut
.ut_line
, tty_name
, sizeof(ut
.ut_line
));
926 #ifdef _HAVE_UT_TV /* in <utmpbits.h> included by <utmp.h> */
927 gettimeofday(&ut
.ut_tv
, NULL
);
932 ut
.ut_time
= t
; /* ut_time is not always a time_t */
933 /* glibc2 #defines it as ut_tv.tv_sec */
936 ut
.ut_type
= USER_PROCESS
;
939 xstrncpy(ut
.ut_host
, hostname
, sizeof(ut
.ut_host
));
941 memcpy(&ut
.ut_addr
, hostaddress
, sizeof(ut
.ut_addr
));
948 updwtmp(_PATH_WTMP
, &ut
);
951 /* The O_APPEND open() flag should be enough to guarantee
952 atomic writes at end of file. */
956 if((wtmp
= open(_PATH_WTMP
, O_APPEND
|O_WRONLY
)) >= 0) {
957 write(wtmp
, (char *)&ut
, sizeof(ut
));
962 /* Probably all this locking below is just nonsense,
963 and the short version is OK as well. */
966 if ((lf
= open(_PATH_WTMPLOCK
, O_CREAT
|O_WRONLY
, 0660)) >= 0) {
968 if ((wtmp
= open(_PATH_WTMP
, O_APPEND
|O_WRONLY
)) >= 0) {
969 write(wtmp
, (char *)&ut
, sizeof(ut
));
982 chown(ttyn
, pwd
->pw_uid
,
983 (gr
= getgrnam(TTYGRPNAME
)) ? gr
->gr_gid
: pwd
->pw_gid
);
984 chmod(ttyn
, TTY_MODE
);
987 /* if tty is one of the VC's then change owner and mode of the
988 special /dev/vcs devices as well */
990 chown(vcsn
, pwd
->pw_uid
, (gr
? gr
->gr_gid
: pwd
->pw_gid
));
991 chown(vcsan
, pwd
->pw_uid
, (gr
? gr
->gr_gid
: pwd
->pw_gid
));
992 chmod(vcsn
, TTY_MODE
);
993 chmod(vcsan
, TTY_MODE
);
1000 quota(Q_DOWARN
, pwd
->pw_uid
, (dev_t
)-1, 0);
1003 if (*pwd
->pw_shell
== '\0')
1004 pwd
->pw_shell
= _PATH_BSHELL
;
1006 /* preserve TERM even without -p flag */
1010 if(!((ep
= getenv("TERM")) && (termenv
= strdup(ep
))))
1014 /* destroy environment unless user has requested preservation */
1017 environ
= (char**)malloc(sizeof(char*));
1018 memset(environ
, 0, sizeof(char*));
1021 setenv("HOME", pwd
->pw_dir
, 0); /* legal to override */
1023 setenv("PATH", _PATH_DEFPATH
, 1);
1025 setenv("PATH", _PATH_DEFPATH_ROOT
, 1);
1027 setenv("SHELL", pwd
->pw_shell
, 1);
1028 setenv("TERM", termenv
, 1);
1030 /* mailx will give a funny error msg if you forget this one */
1032 char tmp
[MAXPATHLEN
];
1033 /* avoid snprintf */
1034 if (sizeof(_PATH_MAILDIR
) + strlen(pwd
->pw_name
) + 1 < MAXPATHLEN
) {
1035 sprintf(tmp
, "%s/%s", _PATH_MAILDIR
, pwd
->pw_name
);
1036 setenv("MAIL",tmp
,0);
1040 /* LOGNAME is not documented in login(1) but
1041 HP-UX 6.5 does it. We'll not allow modifying it.
1043 setenv("LOGNAME", pwd
->pw_name
, 1);
1048 char ** env
= pam_getenvlist(pamh
);
1051 for (i
=0; env
[i
]; i
++) {
1053 /* D(("env[%d] = %s", i,env[i])); */
1059 #ifdef DO_PS_FIDDLING
1060 setproctitle("login", username
);
1063 if (!strncmp(tty_name
, "ttyS", 4))
1064 syslog(LOG_INFO
, _("DIALUP AT %s BY %s"), tty_name
, pwd
->pw_name
);
1066 /* allow tracking of good logins.
1067 -steve philp (sphilp@mail.alliance.net) */
1069 if (pwd
->pw_uid
== 0) {
1071 syslog(LOG_NOTICE
, _("ROOT LOGIN ON %s FROM %s"),
1072 tty_name
, hostname
);
1074 syslog(LOG_NOTICE
, _("ROOT LOGIN ON %s"), tty_name
);
1077 syslog(LOG_INFO
, _("LOGIN ON %s BY %s FROM %s"), tty_name
,
1078 pwd
->pw_name
, hostname
);
1080 syslog(LOG_INFO
, _("LOGIN ON %s BY %s"), tty_name
,
1089 mail
= getenv("MAIL");
1090 if (mail
&& stat(mail
, &st
) == 0 && st
.st_size
!= 0) {
1091 if (st
.st_mtime
> st
.st_atime
)
1092 printf(_("You have new mail.\n"));
1094 printf(_("You have mail.\n"));
1098 signal(SIGALRM
, SIG_DFL
);
1099 signal(SIGQUIT
, SIG_DFL
);
1100 signal(SIGTSTP
, SIG_IGN
);
1104 * We must fork before setuid() because we need to call
1105 * pam_close_session() as root.
1111 /* error in fork() */
1112 fprintf(stderr
, _("login: failure forking: %s"), strerror(errsv
));
1118 /* parent - wait for child to finish, then cleanup session */
1119 signal(SIGHUP
, SIG_IGN
);
1120 signal(SIGINT
, SIG_IGN
);
1121 signal(SIGQUIT
, SIG_IGN
);
1122 signal(SIGTSTP
, SIG_IGN
);
1123 signal(SIGTTIN
, SIG_IGN
);
1124 signal(SIGTTOU
, SIG_IGN
);
1133 * Problem: if the user's shell is a shell like ash that doesnt do
1134 * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
1135 * process in the pgrp, will kill us.
1138 /* start new session */
1141 /* make sure we have a controlling tty */
1143 openlog("login", LOG_ODELAY
, LOG_AUTHPRIV
); /* reopen */
1146 * TIOCSCTTY: steal tty from other process group.
1148 if (ioctl(0, TIOCSCTTY
, 1))
1149 syslog(LOG_ERR
, _("TIOCSCTTY failed: %m"));
1151 signal(SIGINT
, SIG_DFL
);
1153 /* discard permissions last so can't get killed and drop core */
1154 if(setuid(pwd
->pw_uid
) < 0 && pwd
->pw_uid
) {
1155 syslog(LOG_ALERT
, _("setuid() failed"));
1159 /* wait until here to change directory! */
1160 if (chdir(pwd
->pw_dir
) < 0) {
1161 printf(_("No directory %s!\n"), pwd
->pw_dir
);
1165 printf(_("Logging in with home = \"/\".\n"));
1168 /* if the shell field has a space: treat it like a shell script */
1169 if (strchr(pwd
->pw_shell
, ' ')) {
1170 buff
= malloc(strlen(pwd
->pw_shell
) + 6);
1173 fprintf(stderr
, _("login: no memory for shell script.\n"));
1177 strcpy(buff
, "exec ");
1178 strcat(buff
, pwd
->pw_shell
);
1179 childArgv
[childArgc
++] = "/bin/sh";
1180 childArgv
[childArgc
++] = "-sh";
1181 childArgv
[childArgc
++] = "-c";
1182 childArgv
[childArgc
++] = buff
;
1185 xstrncpy(tbuf
+ 1, ((p
= rindex(pwd
->pw_shell
, '/')) ?
1186 p
+ 1 : pwd
->pw_shell
),
1189 childArgv
[childArgc
++] = pwd
->pw_shell
;
1190 childArgv
[childArgc
++] = tbuf
;
1193 childArgv
[childArgc
++] = NULL
;
1195 execvp(childArgv
[0], childArgv
+ 1);
1199 if (!strcmp(childArgv
[0], "/bin/sh"))
1200 fprintf(stderr
, _("login: couldn't exec shell script: %s.\n"),
1203 fprintf(stderr
, _("login: no shell: %s.\n"), strerror(errsv
));
1210 getloginname(void) {
1213 static char nbuf
[UT_NAMESIZE
+ 1];
1218 printf(_("\n%s login: "), thishost
); fflush(stdout
);
1219 for (p
= nbuf
; (ch
= getchar()) != '\n'; ) {
1224 if (p
< nbuf
+ UT_NAMESIZE
)
1228 if (cnt
> UT_NAMESIZE
+ 20) {
1229 fprintf(stderr
, _("login name much too long.\n"));
1230 badlogin(_("NAME too long"));
1237 _("login names may not start with '-'.\n"));
1247 fprintf(stderr
, _("too many bare linefeeds.\n"));
1248 badlogin(_("EXCESSIVE linefeeds"));
1256 * Robert Ambrose writes:
1257 * A couple of my users have a problem with login processes hanging around
1258 * soaking up pts's. What they seem to hung up on is trying to write out the
1259 * message 'Login timed out after %d seconds' when the connection has already
1261 * What I did was add a second timeout while trying to write the message so
1262 * the process just exits if the second timeout expires.
1266 timedout2(int sig
) {
1270 ioctl(0, TCGETA
, &ti
);
1272 ioctl(0, TCSETA
, &ti
);
1278 signal(SIGALRM
, timedout2
);
1280 fprintf(stderr
, _("Login timed out after %d seconds\n"), timeout
);
1281 signal(SIGALRM
, SIG_IGN
);
1288 rootterm(char * ttyn
)
1294 fd
= open(SECURETTY
, O_RDONLY
);
1295 if(fd
< 0) return 1;
1297 /* read each line in /etc/securetty, if a line matches our ttyline
1298 then root is allowed to login on this tty, and we should return
1302 while(--cnt
>= 0 && (more
= read(fd
, p
, 1)) == 1 && *p
!= '\n') p
++;
1303 if(more
&& *p
== '\n') {
1305 if(!strcmp(buf
, ttyn
)) {
1316 #endif /* !USE_PAM */
1318 jmp_buf motdinterrupt
;
1323 void (*oldint
)(int);
1326 if ((fd
= open(_PATH_MOTDFILE
, O_RDONLY
, 0)) < 0)
1328 oldint
= signal(SIGINT
, sigint
);
1329 if (setjmp(motdinterrupt
) == 0)
1330 while ((nchars
= read(fd
, tbuf
, sizeof(tbuf
))) > 0)
1331 write(fileno(stdout
), tbuf
, nchars
);
1332 signal(SIGINT
, oldint
);
1338 longjmp(motdinterrupt
, 1);
1341 #ifndef USE_PAM /* PAM takes care of this */
1343 checknologin(void) {
1347 if ((fd
= open(_PATH_NOLOGIN
, O_RDONLY
, 0)) >= 0) {
1348 while ((nchars
= read(fd
, tbuf
, sizeof(tbuf
))) > 0)
1349 write(fileno(stdout
), tbuf
, nchars
);
1357 dolastlog(int quiet
) {
1361 if ((fd
= open(_PATH_LASTLOG
, O_RDWR
, 0)) >= 0) {
1362 lseek(fd
, (off_t
)pwd
->pw_uid
* sizeof(ll
), SEEK_SET
);
1364 if (read(fd
, (char *)&ll
, sizeof(ll
)) == sizeof(ll
) &&
1366 printf(_("Last login: %.*s "),
1367 24-5, (char *)ctime(&ll
.ll_time
));
1369 if (*ll
.ll_host
!= '\0')
1370 printf(_("from %.*s\n"),
1371 (int)sizeof(ll
.ll_host
), ll
.ll_host
);
1373 printf(_("on %.*s\n"),
1374 (int)sizeof(ll
.ll_line
), ll
.ll_line
);
1376 lseek(fd
, (off_t
)pwd
->pw_uid
* sizeof(ll
), SEEK_SET
);
1378 memset((char *)&ll
, 0, sizeof(ll
));
1380 xstrncpy(ll
.ll_line
, tty_name
, sizeof(ll
.ll_line
));
1382 xstrncpy(ll
.ll_host
, hostname
, sizeof(ll
.ll_host
));
1384 write(fd
, (char *)&ll
, sizeof(ll
));
1390 badlogin(const char *name
) {
1391 if (failures
== 1) {
1393 syslog(LOG_NOTICE
, _("LOGIN FAILURE FROM %s, %s"),
1396 syslog(LOG_NOTICE
, _("LOGIN FAILURE ON %s, %s"),
1400 syslog(LOG_NOTICE
, _("%d LOGIN FAILURES FROM %s, %s"),
1401 failures
, hostname
, name
);
1403 syslog(LOG_NOTICE
, _("%d LOGIN FAILURES ON %s, %s"),
1404 failures
, tty_name
, name
);
1408 /* Should not be called from PAM code... */
1410 sleepexit(int eval
) {
1411 sleep(SLEEP_EXIT_TIMEOUT
);