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 static char *stypeof (char *ttyid
);
191 #include "cryptocard.h"
195 #include <kerberos/krb.h>
196 #include <sys/termios.h>
197 char realm
[REALM_SZ
];
198 int kerror
= KSUCCESS
, notickets
= 1;
202 # define TTY_MODE 0620
204 # define TTY_MODE 0600
207 #define TTYGRPNAME "tty" /* name of group to own ttys */
208 /**# define TTYGRPNAME "other" **/
211 # define MAXPATHLEN 1024
215 * This bounds the time given to login. Not a define so it can
216 * be patched on machines where it's too small.
221 int timeout
= 60; /* used in cryptocard.c */
224 struct passwd
*pwd
; /* used in cryptocard.c */
226 static struct passwd pwdcopy
;
228 char hostaddress
[4]; /* used in checktty.c */
229 char *hostname
; /* idem */
230 static char *username
, *tty_name
, *tty_number
;
231 static char thishost
[100];
232 static int failures
= 1;
235 struct sgttyb sgttyb
;
237 CINTR
, CQUIT
, CSTART
, CSTOP
, CEOT
, CBRK
239 struct ltchars ltc
= {
240 CSUSP
, CDSUSP
, CRPRNT
, CFLUSH
, CWERASE
, CLNEXT
244 const char *months
[] = {
245 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
246 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
249 /* Nice and simple code provided by Linus Torvalds 16-Feb-93 */
250 /* Nonblocking stuff by Maciej W. Rozycki, macro@ds2.pg.gda.pl, 1999.
251 He writes: "Login performs open() on a tty in a blocking mode.
252 In some cases it may make login wait in open() for carrier infinitely,
253 for example if the line is a simplistic case of a three-wire serial
254 connection. I believe login should open the line in the non-blocking mode
255 leaving the decision to make a connection to getty (where it actually
258 opentty(const char * tty
) {
260 int fd
= open(tty
, O_RDWR
| O_NONBLOCK
);
261 int flags
= fcntl(fd
, F_GETFL
);
263 flags
&= ~O_NONBLOCK
;
264 fcntl(fd
, F_SETFL
, flags
);
266 for (i
= 0 ; i
< fd
; i
++)
268 for (i
= 0 ; i
< 3 ; i
++)
274 /* true if the filedescriptor fd is a console tty, very Linux specific */
280 if ((fstat(fd
, &stb
) >= 0)
281 && (major(stb
.st_rdev
) == TTY_MAJOR
)
282 && (minor(stb
.st_rdev
) < 64)) {
291 * Log failed login attempts in _PATH_BTMP if that exists.
292 * Must be called only with username the name of an actual user.
293 * The most common login failure is to give password instead of username.
295 #define _PATH_BTMP "/var/log/btmp"
297 logbtmp(const char *line
, const char *username
, const char *hostname
) {
300 memset(&ut
, 0, sizeof(ut
));
302 strncpy(ut
.ut_user
, username
? username
: "(unknown)",
305 strncpy(ut
.ut_id
, line
+ 3, sizeof(ut
.ut_id
));
306 xstrncpy(ut
.ut_line
, line
, sizeof(ut
.ut_line
));
308 #if defined(_HAVE_UT_TV) /* in <utmpbits.h> included by <utmp.h> */
309 gettimeofday(&ut
.ut_tv
, NULL
);
314 ut
.ut_time
= t
; /* ut_time is not always a time_t */
318 ut
.ut_type
= LOGIN_PROCESS
; /* XXX doesn't matter */
319 ut
.ut_pid
= getpid();
321 xstrncpy(ut
.ut_host
, hostname
, sizeof(ut
.ut_host
));
323 memcpy(&ut
.ut_addr
, hostaddress
, sizeof(ut
.ut_addr
));
325 #ifdef HAVE_updwtmp /* bad luck for ancient systems */
326 updwtmp(_PATH_BTMP
, &ut
);
332 main(int argc
, char **argv
)
335 extern char *optarg
, **environ
;
339 int ask
, fflag
, hflag
, pflag
, cnt
, errsv
;
340 int quietlog
, passwd_req
;
342 char tbuf
[MAXPATHLEN
+ 2], tname
[sizeof(_PATH_TTY
) + 10];
344 char * childArgv
[10];
349 pam_handle_t
*pamh
= NULL
;
350 struct pam_conv conv
= { misc_conv
, NULL
};
356 char vcsn
[20], vcsan
[20];
362 signal(SIGALRM
, timedout
);
363 alarm((unsigned int)timeout
);
364 signal(SIGQUIT
, SIG_IGN
);
365 signal(SIGINT
, SIG_IGN
);
367 setlocale(LC_ALL
, "");
368 bindtextdomain(PACKAGE
, LOCALEDIR
);
371 setpriority(PRIO_PROCESS
, 0, 0);
373 quota(Q_SETUID
, 0, 0, 0);
375 #ifdef DO_PS_FIDDLING
376 initproctitle(argc
, argv
);
380 * -p is used by getty to tell login not to destroy the environment
381 * -f is used to skip a second login authentication
382 * -h is used by other servers to pass the name of the remote
383 * host to login so that it may be placed in utmp and wtmp
385 gethostname(tbuf
, sizeof(tbuf
));
386 xstrncpy(thishost
, tbuf
, sizeof(thishost
));
387 domain
= index(tbuf
, '.');
389 username
= tty_name
= hostname
= NULL
;
390 fflag
= hflag
= pflag
= 0;
393 while ((ch
= getopt(argc
, argv
, "fh:p")) != -1)
402 _("login: -h for super-user only.\n"));
406 if (domain
&& (p
= index(optarg
, '.')) &&
407 strcasecmp(p
, domain
) == 0)
410 hostname
= strdup(optarg
); /* strdup: Ambrose C. Li */
412 struct hostent
*he
= gethostbyname(hostname
);
414 /* he points to static storage; copy the part we use */
416 if (he
&& he
->h_addr_list
&& he
->h_addr_list
[0])
417 memcpy(hostaddress
, he
->h_addr_list
[0],
418 sizeof(hostaddress
));
429 _("usage: login [-fp] [username]\n"));
436 username
= strdup(p
);
438 /* wipe name - some people mistype their password here */
439 /* (of course we are too late, but perhaps this helps a little ..) */
447 ioctl(0, TIOCLSET
, &ioctlval
);
448 ioctl(0, TIOCNXCL
, 0);
449 fcntl(0, F_SETFL
, ioctlval
);
450 ioctl(0, TIOCGETP
, &sgttyb
);
451 sgttyb
.sg_erase
= CERASE
;
452 sgttyb
.sg_kill
= CKILL
;
453 ioctl(0, TIOCSLTC
, <c
);
454 ioctl(0, TIOCSETC
, &tc
);
455 ioctl(0, TIOCSETP
, &sgttyb
);
458 * Be sure that we're in blocking mode!!!
459 * This is really for HPUX
462 ioctl(0, FIOSNBIO
, &ioctlval
);
463 #endif /* ! __linux__ */
465 for (cnt
= getdtablesize(); cnt
> 2; cnt
--)
469 if (ttyn
== NULL
|| *ttyn
== '\0') {
470 /* no snprintf required - see definition of tname */
471 sprintf(tname
, "%s??", _PATH_TTY
);
475 if (strncmp(ttyn
, "/dev/", 5) == 0)
480 if (strncmp(ttyn
, "/dev/tty", 8) == 0)
484 while (*p
&& !isdigit(*p
)) p
++;
489 /* find names of Virtual Console devices, for later mode change */
490 snprintf(vcsn
, sizeof(vcsn
), "/dev/vcs%s", tty_number
);
491 snprintf(vcsan
, sizeof(vcsan
), "/dev/vcsa%s", tty_number
);
497 struct termios tt
, ttt
;
501 ttt
.c_cflag
&= ~HUPCL
;
503 /* These can fail, e.g. with ttyn on a read-only filesystem */
505 chmod(ttyn
, TTY_MODE
);
507 /* Kill processes left on this tty */
508 tcsetattr(0,TCSAFLUSH
,&ttt
);
509 signal(SIGHUP
, SIG_IGN
); /* so vhangup() wont kill us */
511 signal(SIGHUP
, SIG_DFL
);
515 /* re-open stdin,stdout,stderr after vhangup() closed them */
516 /* if it did, after 0.99.5 it doesn't! */
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
);
632 /* Grab the user information out of the password file for future usage
633 First get the username that we are actually using, though.
635 retcode
= pam_get_item(pamh
, PAM_USER
, (const void **) &username
);
638 if (!username
|| !*username
) {
639 fprintf(stderr
, _("\nSession setup problem, abort.\n"));
640 syslog(LOG_ERR
, _("NULL user name in %s:%d. Abort."),
641 __FUNCTION__
, __LINE__
);
642 pam_end(pamh
, PAM_SYSTEM_ERR
);
645 if (!(pwd
= getpwnam(username
))) {
646 fprintf(stderr
, _("\nSession setup problem, abort.\n"));
647 syslog(LOG_ERR
, _("Invalid user name \"%s\" in %s:%d. Abort."),
648 username
, __FUNCTION__
, __LINE__
);
649 pam_end(pamh
, PAM_SYSTEM_ERR
);
653 /* Create a copy of the pwd struct - otherwise it may get
654 * clobbered by PAM */
655 memcpy(&pwdcopy
, pwd
, sizeof(*pwd
));
657 pwd
->pw_name
= strdup(pwd
->pw_name
);
658 pwd
->pw_passwd
= strdup(pwd
->pw_passwd
);
659 pwd
->pw_gecos
= strdup(pwd
->pw_gecos
);
660 pwd
->pw_dir
= strdup(pwd
->pw_dir
);
661 pwd
->pw_shell
= strdup(pwd
->pw_shell
);
662 if (!pwd
->pw_name
|| !pwd
->pw_passwd
|| !pwd
->pw_gecos
||
663 !pwd
->pw_dir
|| !pwd
->pw_shell
) {
664 fprintf(stderr
, _("login: Out of memory\n"));
665 syslog(LOG_ERR
, "Out of memory");
666 pam_end(pamh
, PAM_SYSTEM_ERR
);
669 username
= pwd
->pw_name
;
672 * Initialize the supplementary group list.
673 * This should be done before pam_setcred because
674 * the PAM modules might add groups during pam_setcred.
676 if (initgroups(username
, pwd
->pw_gid
) < 0) {
677 syslog(LOG_ERR
, "initgroups: %m");
678 fprintf(stderr
, _("\nSession setup problem, abort.\n"));
679 pam_end(pamh
, PAM_SYSTEM_ERR
);
683 retcode
= pam_open_session(pamh
, 0);
686 retcode
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
);
689 #else /* ! USE_PAM */
691 for (cnt
= 0;; ask
= 1) {
694 ioctl(0, TIOCSETD
, &ioctlval
);
702 /* Dirty patch to fix a gigantic security hole when using
703 yellow pages. This problem should be solved by the
704 libraries, and not by programs, but this must be fixed
705 urgently! If the first char of the username is '+', we
707 Feb 95 <alvaro@etsit.upm.es> */
709 if (username
[0] == '+') {
710 puts(_("Illegal username"));
715 /* (void)strcpy(tbuf, username); why was this here? */
716 if ((pwd
= getpwnam(username
))) {
720 if ((sp
= getspnam(username
)))
721 pwd
->pw_passwd
= sp
->sp_pwdp
;
723 salt
= pwd
->pw_passwd
;
728 initgroups(username
, pwd
->pw_gid
);
729 checktty(username
, tty_name
, pwd
); /* in checktty.c */
732 /* if user not super-user, check for disabled logins */
733 if (pwd
== NULL
|| pwd
->pw_uid
)
737 * Disallow automatic login to root; if not invoked by
738 * root, disallow if the uid's differ.
743 passwd_req
= pwd
->pw_uid
== 0 ||
744 (uid
&& uid
!= pwd
->pw_uid
);
748 * If trying to log in as root, but with insecure terminal,
749 * refuse the login attempt.
751 if (pwd
&& pwd
->pw_uid
== 0 && !rootterm(tty_name
)) {
753 _("%s login refused on this terminal.\n"),
758 _("LOGIN %s REFUSED FROM %s ON TTY %s"),
759 pwd
->pw_name
, hostname
, tty_name
);
762 _("LOGIN %s REFUSED ON TTY %s"),
763 pwd
->pw_name
, tty_name
);
768 * If no pre-authentication and a password exists
769 * for this user, prompt for one and verify it.
771 if (!passwd_req
|| (pwd
&& !*pwd
->pw_passwd
))
774 setpriority(PRIO_PROCESS
, 0, -4);
775 pp
= getpass(_("Password: "));
778 if (strncmp(pp
, "CRYPTO", 6) == 0) {
779 if (pwd
&& cryptocard()) break;
781 # endif /* CRYPTOCARD */
784 setpriority(PRIO_PROCESS
, 0, 0);
788 * If not present in pw file, act as we normally would.
789 * If we aren't Kerberos-authenticated, try the normal
790 * pw file for a password. If that's ok, log the user
791 * in without issueing any tickets.
794 if (pwd
&& !krb_get_lrealm(realm
,1)) {
796 * get TGT for local realm; be careful about uid's
797 * here for ticket file ownership
799 setreuid(geteuid(),pwd
->pw_uid
);
800 kerror
= krb_get_pw_in_tkt(pwd
->pw_name
, "", realm
,
801 "krbtgt", realm
, DEFAULT_TKT_LIFE
, pp
);
803 if (kerror
== INTK_OK
) {
804 memset(pp
, 0, strlen(pp
));
805 notickets
= 0; /* user got ticket */
809 # endif /* KERBEROS */
810 memset(pp
, 0, strlen(pp
));
812 if (pwd
&& !strcmp(p
, pwd
->pw_passwd
))
815 printf(_("Login incorrect\n"));
816 badlogin(username
); /* log ALL bad logins */
819 /* we allow 10 tries, but after 3 we start backing off */
824 sleep((unsigned int)((cnt
- 3) * 5));
827 #endif /* !USE_PAM */
829 /* committed to login -- turn off timeout */
830 alarm((unsigned int)0);
833 if (quota(Q_SETUID
, pwd
->pw_uid
, 0, 0) < 0 && errno
!= EINVAL
) {
837 _("Too many users logged on already.\nTry again later.\n"));
841 _("You have too many processes running.\n"));
844 perror("quota (Q_SETUID)");
846 sleepexit(0); /* %% */
856 /* This requires some explanation: As root we may not be able to
857 read the directory of the user if it is on an NFS mounted
858 filesystem. We temporarily set our effective uid to the user-uid
859 making sure that we keep root privs. in the real uid.
861 A portable solution would require a fork(), but we rely on Linux
862 having the BSD setreuid() */
865 char tmpstr
[MAXPATHLEN
];
866 uid_t ruid
= getuid();
867 gid_t egid
= getegid();
869 /* avoid snprintf - old systems do not have it, or worse,
870 have a libc in which snprintf is the same as sprintf */
871 if (strlen(pwd
->pw_dir
) + sizeof(_PATH_HUSHLOGIN
) + 2 > MAXPATHLEN
)
874 sprintf(tmpstr
, "%s/%s", pwd
->pw_dir
, _PATH_HUSHLOGIN
);
875 setregid(-1, pwd
->pw_gid
);
876 setreuid(0, pwd
->pw_uid
);
877 quietlog
= (access(tmpstr
, R_OK
) == 0);
878 setuid(0); /* setreuid doesn't do it alone! */
884 /* for linux, write entries in utmp and wtmp */
888 pid_t mypid
= getpid();
890 utmpname(_PATH_UTMP
);
893 /* Find mypid in utmp.
894 login sometimes overwrites the runlevel entry in /var/run/utmp,
895 confusing sysvinit. I added a test for the entry type, and the problem
896 was gone. (In a runlevel entry, st_pid is not really a pid but some number
897 calculated from the previous and current runlevel).
898 Michael Riepe <michael@stud.uni-hannover.de>
900 while ((utp
= getutent()))
901 if (utp
->ut_pid
== mypid
902 && utp
->ut_type
>= INIT_PROCESS
903 && utp
->ut_type
<= DEAD_PROCESS
)
906 /* If we can't find a pre-existing entry by pid, try by line.
907 BSD network daemons may rely on this. (anonymous) */
910 ut
.ut_type
= LOGIN_PROCESS
;
911 strncpy(ut
.ut_id
, tty_number
, sizeof(ut
.ut_id
));
912 strncpy(ut
.ut_line
, tty_name
, sizeof(ut
.ut_line
));
917 memcpy(&ut
, utp
, sizeof(ut
));
919 /* some gettys/telnetds don't initialize utmp... */
920 memset(&ut
, 0, sizeof(ut
));
923 if (ut
.ut_id
[0] == 0)
924 strncpy(ut
.ut_id
, tty_number
, sizeof(ut
.ut_id
));
926 strncpy(ut
.ut_user
, username
, sizeof(ut
.ut_user
));
927 xstrncpy(ut
.ut_line
, tty_name
, sizeof(ut
.ut_line
));
928 #ifdef _HAVE_UT_TV /* in <utmpbits.h> included by <utmp.h> */
929 gettimeofday(&ut
.ut_tv
, NULL
);
934 ut
.ut_time
= t
; /* ut_time is not always a time_t */
935 /* glibc2 #defines it as ut_tv.tv_sec */
938 ut
.ut_type
= USER_PROCESS
;
941 xstrncpy(ut
.ut_host
, hostname
, sizeof(ut
.ut_host
));
943 memcpy(&ut
.ut_addr
, hostaddress
, sizeof(ut
.ut_addr
));
950 updwtmp(_PATH_WTMP
, &ut
);
953 /* The O_APPEND open() flag should be enough to guarantee
954 atomic writes at end of file. */
958 if((wtmp
= open(_PATH_WTMP
, O_APPEND
|O_WRONLY
)) >= 0) {
959 write(wtmp
, (char *)&ut
, sizeof(ut
));
964 /* Probably all this locking below is just nonsense,
965 and the short version is OK as well. */
968 if ((lf
= open(_PATH_WTMPLOCK
, O_CREAT
|O_WRONLY
, 0660)) >= 0) {
970 if ((wtmp
= open(_PATH_WTMP
, O_APPEND
|O_WRONLY
)) >= 0) {
971 write(wtmp
, (char *)&ut
, sizeof(ut
));
984 chown(ttyn
, pwd
->pw_uid
,
985 (gr
= getgrnam(TTYGRPNAME
)) ? gr
->gr_gid
: pwd
->pw_gid
);
986 chmod(ttyn
, TTY_MODE
);
989 /* if tty is one of the VC's then change owner and mode of the
990 special /dev/vcs devices as well */
992 chown(vcsn
, pwd
->pw_uid
, (gr
? gr
->gr_gid
: pwd
->pw_gid
));
993 chown(vcsan
, pwd
->pw_uid
, (gr
? gr
->gr_gid
: pwd
->pw_gid
));
994 chmod(vcsn
, TTY_MODE
);
995 chmod(vcsan
, TTY_MODE
);
1002 quota(Q_DOWARN
, pwd
->pw_uid
, (dev_t
)-1, 0);
1005 if (*pwd
->pw_shell
== '\0')
1006 pwd
->pw_shell
= _PATH_BSHELL
;
1008 /* turn on new line discipline for the csh */
1009 else if (!strcmp(pwd
->pw_shell
, _PATH_CSHELL
)) {
1010 ioctlval
= NTTYDISC
;
1011 ioctl(0, TIOCSETD
, &ioctlval
);
1015 /* preserve TERM even without -p flag */
1019 if(!((ep
= getenv("TERM")) && (termenv
= strdup(ep
))))
1023 /* destroy environment unless user has requested preservation */
1026 environ
= (char**)malloc(sizeof(char*));
1027 memset(environ
, 0, sizeof(char*));
1030 setenv("HOME", pwd
->pw_dir
, 0); /* legal to override */
1032 setenv("PATH", _PATH_DEFPATH
, 1);
1034 setenv("PATH", _PATH_DEFPATH_ROOT
, 1);
1036 setenv("SHELL", pwd
->pw_shell
, 1);
1037 setenv("TERM", termenv
, 1);
1039 /* mailx will give a funny error msg if you forget this one */
1041 char tmp
[MAXPATHLEN
];
1042 /* avoid snprintf */
1043 if (sizeof(_PATH_MAILDIR
) + strlen(pwd
->pw_name
) + 1 < MAXPATHLEN
) {
1044 sprintf(tmp
, "%s/%s", _PATH_MAILDIR
, pwd
->pw_name
);
1045 setenv("MAIL",tmp
,0);
1049 /* LOGNAME is not documented in login(1) but
1050 HP-UX 6.5 does it. We'll not allow modifying it.
1052 setenv("LOGNAME", pwd
->pw_name
, 1);
1057 char ** env
= pam_getenvlist(pamh
);
1060 for (i
=0; env
[i
]; i
++) {
1062 /* D(("env[%d] = %s", i,env[i])); */
1068 #ifdef DO_PS_FIDDLING
1069 setproctitle("login", username
);
1072 if (!strncmp(tty_name
, "ttyS", 4))
1073 syslog(LOG_INFO
, _("DIALUP AT %s BY %s"), tty_name
, pwd
->pw_name
);
1075 /* allow tracking of good logins.
1076 -steve philp (sphilp@mail.alliance.net) */
1078 if (pwd
->pw_uid
== 0) {
1080 syslog(LOG_NOTICE
, _("ROOT LOGIN ON %s FROM %s"),
1081 tty_name
, hostname
);
1083 syslog(LOG_NOTICE
, _("ROOT LOGIN ON %s"), tty_name
);
1086 syslog(LOG_INFO
, _("LOGIN ON %s BY %s FROM %s"), tty_name
,
1087 pwd
->pw_name
, hostname
);
1089 syslog(LOG_INFO
, _("LOGIN ON %s BY %s"), tty_name
,
1098 mail
= getenv("MAIL");
1099 if (mail
&& stat(mail
, &st
) == 0 && st
.st_size
!= 0) {
1100 if (st
.st_mtime
> st
.st_atime
)
1101 printf(_("You have new mail.\n"));
1103 printf(_("You have mail.\n"));
1107 signal(SIGALRM
, SIG_DFL
);
1108 signal(SIGQUIT
, SIG_DFL
);
1109 signal(SIGTSTP
, SIG_IGN
);
1113 * We must fork before setuid() because we need to call
1114 * pam_close_session() as root.
1117 * Problem: if the user's shell is a shell like ash that doesnt do
1118 * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
1119 * process in the pgrp, will kill us.
1120 * Solution: use TIOCNOTTY and setsid().
1122 signal(SIGINT
, SIG_IGN
);
1123 signal(SIGHUP
, SIG_IGN
); /* ignore signal from TIOCNOTTY below */
1128 /* error in fork() */
1129 fprintf(stderr
,_("login: failure forking: %s"), strerror(errsv
));
1132 } else if (childPid
) {
1133 /* parent - wait for child to finish, then cleanup session */
1134 ioctl(0, TIOCNOTTY
, NULL
);
1135 signal(SIGHUP
, SIG_DFL
);
1143 /* reopen, as we need controlling tty in the child */
1146 signal(SIGHUP
, SIG_DFL
);
1147 signal(SIGINT
, SIG_DFL
);
1149 /* discard permissions last so can't get killed and drop core */
1150 if(setuid(pwd
->pw_uid
) < 0 && pwd
->pw_uid
) {
1151 syslog(LOG_ALERT
, _("setuid() failed"));
1155 /* wait until here to change directory! */
1156 if (chdir(pwd
->pw_dir
) < 0) {
1157 printf(_("No directory %s!\n"), pwd
->pw_dir
);
1161 printf(_("Logging in with home = \"/\".\n"));
1164 /* if the shell field has a space: treat it like a shell script */
1165 if (strchr(pwd
->pw_shell
, ' ')) {
1166 buff
= malloc(strlen(pwd
->pw_shell
) + 6);
1169 fprintf(stderr
, _("login: no memory for shell script.\n"));
1173 strcpy(buff
, "exec ");
1174 strcat(buff
, pwd
->pw_shell
);
1175 childArgv
[childArgc
++] = "/bin/sh";
1176 childArgv
[childArgc
++] = "-sh";
1177 childArgv
[childArgc
++] = "-c";
1178 childArgv
[childArgc
++] = buff
;
1181 xstrncpy(tbuf
+ 1, ((p
= rindex(pwd
->pw_shell
, '/')) ?
1182 p
+ 1 : pwd
->pw_shell
),
1185 childArgv
[childArgc
++] = pwd
->pw_shell
;
1186 childArgv
[childArgc
++] = tbuf
;
1189 childArgv
[childArgc
++] = NULL
;
1191 execvp(childArgv
[0], childArgv
+ 1);
1195 if (!strcmp(childArgv
[0], "/bin/sh"))
1196 fprintf(stderr
, _("login: couldn't exec shell script: %s.\n"),
1199 fprintf(stderr
, _("login: no shell: %s.\n"), strerror(errsv
));
1206 getloginname(void) {
1209 static char nbuf
[UT_NAMESIZE
+ 1];
1214 printf(_("\n%s login: "), thishost
); fflush(stdout
);
1215 for (p
= nbuf
; (ch
= getchar()) != '\n'; ) {
1220 if (p
< nbuf
+ UT_NAMESIZE
)
1224 if (cnt
> UT_NAMESIZE
+ 20) {
1225 fprintf(stderr
, _("login name much too long.\n"));
1226 badlogin(_("NAME too long"));
1233 _("login names may not start with '-'.\n"));
1243 fprintf(stderr
, _("too many bare linefeeds.\n"));
1244 badlogin(_("EXCESSIVE linefeeds"));
1255 fprintf(stderr
, _("Login timed out after %d seconds\n"), timeout
);
1258 ioctl(0, TCGETA
, &ti
);
1260 ioctl(0, TCSETA
, &ti
);
1266 rootterm(char * ttyn
)
1271 return((t
= getttynam(ttyn
)) && (t
->ty_status
&TTY_SECURE
));
1279 fd
= open(SECURETTY
, O_RDONLY
);
1280 if(fd
< 0) return 1;
1282 /* read each line in /etc/securetty, if a line matches our ttyline
1283 then root is allowed to login on this tty, and we should return
1287 while(--cnt
>= 0 && (more
= read(fd
, p
, 1)) == 1 && *p
!= '\n') p
++;
1288 if(more
&& *p
== '\n') {
1290 if(!strcmp(buf
, ttyn
)) {
1301 #endif /* !__linux__ */
1302 #endif /* !USE_PAM */
1304 jmp_buf motdinterrupt
;
1309 void (*oldint
)(int);
1312 if ((fd
= open(_PATH_MOTDFILE
, O_RDONLY
, 0)) < 0)
1314 oldint
= signal(SIGINT
, sigint
);
1315 if (setjmp(motdinterrupt
) == 0)
1316 while ((nchars
= read(fd
, tbuf
, sizeof(tbuf
))) > 0)
1317 write(fileno(stdout
), tbuf
, nchars
);
1318 signal(SIGINT
, oldint
);
1324 longjmp(motdinterrupt
, 1);
1327 #ifndef USE_PAM /* PAM takes care of this */
1329 checknologin(void) {
1333 if ((fd
= open(_PATH_NOLOGIN
, O_RDONLY
, 0)) >= 0) {
1334 while ((nchars
= read(fd
, tbuf
, sizeof(tbuf
))) > 0)
1335 write(fileno(stdout
), tbuf
, nchars
);
1343 dolastlog(int quiet
) {
1347 if ((fd
= open(_PATH_LASTLOG
, O_RDWR
, 0)) >= 0) {
1348 lseek(fd
, (off_t
)pwd
->pw_uid
* sizeof(ll
), SEEK_SET
);
1350 if (read(fd
, (char *)&ll
, sizeof(ll
)) == sizeof(ll
) &&
1352 printf(_("Last login: %.*s "),
1353 24-5, (char *)ctime(&ll
.ll_time
));
1355 if (*ll
.ll_host
!= '\0')
1356 printf(_("from %.*s\n"),
1357 (int)sizeof(ll
.ll_host
), ll
.ll_host
);
1359 printf(_("on %.*s\n"),
1360 (int)sizeof(ll
.ll_line
), ll
.ll_line
);
1362 lseek(fd
, (off_t
)pwd
->pw_uid
* sizeof(ll
), SEEK_SET
);
1364 memset((char *)&ll
, 0, sizeof(ll
));
1366 xstrncpy(ll
.ll_line
, tty_name
, sizeof(ll
.ll_line
));
1368 xstrncpy(ll
.ll_host
, hostname
, sizeof(ll
.ll_host
));
1370 write(fd
, (char *)&ll
, sizeof(ll
));
1376 badlogin(const char *name
) {
1377 if (failures
== 1) {
1379 syslog(LOG_NOTICE
, _("LOGIN FAILURE FROM %s, %s"),
1382 syslog(LOG_NOTICE
, _("LOGIN FAILURE ON %s, %s"),
1386 syslog(LOG_NOTICE
, _("%d LOGIN FAILURES FROM %s, %s"),
1387 failures
, hostname
, name
);
1389 syslog(LOG_NOTICE
, _("%d LOGIN FAILURES ON %s, %s"),
1390 failures
, tty_name
, name
);
1395 #define UNKNOWN "su"
1399 stypeof(char *ttyid
) {
1402 return(ttyid
&& (t
= getttynam(ttyid
)) ? t
->ty_type
: UNKNOWN
);
1406 /* Should not be called from PAM code... */
1408 sleepexit(int eval
) {
1409 sleep(SLEEP_EXIT_TIMEOUT
);