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: logging of bad login attempts in /etc/btmp,
17 they are sent to syslog
21 '*' as login shell, add it if you need it
23 - BSD features left out: quota checks
25 analysis of terminal type (tset feature)
27 - BSD features thrown in: Security logging to syslogd.
28 This requires you to have a (ported) syslog
29 system -- 7.0 comes with syslog
33 - A lot of nitty gritty details have been adjusted in favour of
34 HP-UX, e.g. /etc/securetty, default paths and the environment
35 variables assigned by 'login'.
37 - We do *nothing* to setup/alter tty state, under HP-UX this is
38 to be done by getty/rlogind/telnetd/some one else.
40 Michael Glad (glad@daimi.dk)
41 Computer Science Department
47 1991-09-24 glad@daimi.aau.dk: HP-UX 8.0 port:
48 - now explictly sets non-blocking mode on descriptors
49 - strcasecmp is now part of HP-UX
51 1992-02-05 poe@daimi.aau.dk: Ported the stuff to Linux 0.12
52 From 1992 till now (1997) this code for Linux has been maintained at
53 ftp.daimi.aau.dk:/pub/linux/poe/
55 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
56 - added Native Language Support
57 Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
58 - fixed strerr(errno) in gettext calls
62 * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
63 * All rights reserved.
65 * Redistribution and use in source and binary forms are permitted
66 * provided that the above copyright notice and this paragraph are
67 * duplicated in all such forms and that any documentation,
68 * advertising materials, and other materials related to such
69 * distribution and use acknowledge that the software was developed
70 * by the University of California, Berkeley. The name of the
71 * University may not be used to endorse or promote products derived
72 * from this software without specific prior written permission.
73 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
74 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
75 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
80 * login -h hostname (for telnetd, etc.)
81 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
89 #include <sys/param.h>
100 #include <sys/resource.h>
101 #include <sys/file.h>
105 #define rindex strrchr
106 #include <sys/ioctl.h>
107 #include <sys/wait.h>
116 #include <sys/syslog.h>
117 #include <sys/sysmacros.h>
119 #include "pathnames.h"
120 #include "my_crypt.h"
125 # include <sys/sysmacros.h>
126 # include <linux/major.h>
140 # include <security/pam_appl.h>
141 # include <security/pam_misc.h>
142 # define PAM_MAX_LOGIN_TRIES 3
143 # define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \
144 fprintf(stderr,"\n%s\n",pam_strerror(pamh, retcode)); \
145 syslog(LOG_ERR,"%s",pam_strerror(pamh, retcode)); \
146 pam_end(pamh, retcode); exit(1); \
148 # define PAM_END { retcode = pam_close_session(pamh,0); \
149 pam_end(pamh,retcode); }
157 #define SLEEP_EXIT_TIMEOUT 5
160 #define DO_PS_FIDDLING
163 #ifdef DO_PS_FIDDLING
164 #include "setproctitle.h"
169 * we've got a REAL HACK to avoid telling people that they have
170 * mail because the imap server has left a turd in their inbox.
171 * It works, but it sucks...
172 * It turns out that the turd is always 523 bytes long, so we
173 * just check for that size.
176 * If you want to turn this strange hack off, set
177 #define REDHAT_IGNORED_MAILSIZE 0
178 * In case people complain, this may become a configuration option,
179 * or perhaps this hack is thrown out again.
180 * A better solution would be to check the contents of this file..
182 #define REDHAT_IGNORED_MAILSIZE 523
185 /* from before we had a lastlog.h file in linux */
194 static void getloginname (void);
195 static void timedout (int);
196 static void sigint (int);
197 static int rootterm (char *ttyn
);
198 static void motd (void);
199 static void checknologin (void);
200 static void dolastlog (int quiet
);
203 static char *stypeof (char *ttyid
);
207 #include "cryptocard.h"
211 #include <kerberos/krb.h>
212 #include <sys/termios.h>
213 char realm
[REALM_SZ
];
214 int kerror
= KSUCCESS
, notickets
= 1;
218 # define TTY_MODE 0620
220 # define TTY_MODE 0600
223 #define TTYGRPNAME "tty" /* name of group to own ttys */
224 /**# define TTYGRPNAME "other" **/
227 # define MAXPATHLEN 1024
231 * This bounds the time given to login. Not a define so it can
232 * be patched on machines where it's too small.
237 int timeout
= 60; /* used in cryptocard.c */
240 struct passwd
*pwd
; /* used in cryptocard.c */
241 struct hostent hostaddress
; /* used in checktty.c */
242 char term
[64], *hostname
, *username
, *tty
;
243 static char thishost
[100];
244 static int failures
= 1;
247 struct sgttyb sgttyb
;
249 CINTR
, CQUIT
, CSTART
, CSTOP
, CEOT
, CBRK
251 struct ltchars ltc
= {
252 CSUSP
, CDSUSP
, CRPRNT
, CFLUSH
, CWERASE
, CLNEXT
256 const char *months
[] =
257 { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
258 "Sep", "Oct", "Nov", "Dec" };
260 /* Nice and simple code provided by Linus Torvalds 16-Feb-93 */
261 /* Nonblocking stuff by Maciej W. Rozycki, macro@ds2.pg.gda.pl, 1999.
262 He writes: "Login performs open() on a tty in a blocking mode.
263 In some cases it may make login wait in open() for carrier infinitely,
264 for example if the line is a simplistic case of a three-wire serial
265 connection. I believe login should open the line in the non-blocking mode
266 leaving the decision to make a connection to getty (where it actually
269 opentty(const char * tty
)
272 int fd
= open(tty
, O_RDWR
| O_NONBLOCK
);
273 int flags
= fcntl(fd
, F_GETFL
);
275 flags
&= ~O_NONBLOCK
;
276 fcntl(fd
, F_SETFL
, flags
);
278 for (i
= 0 ; i
< fd
; i
++)
280 for (i
= 0 ; i
< 3 ; i
++)
286 /* true if the filedescriptor fd is a console tty, very Linux specific */
293 if ((fstat(fd
, &stb
) >= 0)
294 && (major(stb
.st_rdev
) == TTY_MAJOR
)
295 && (minor(stb
.st_rdev
) < 64)) {
304 main(int argc
, char **argv
)
307 extern char *optarg
, **environ
;
311 int ask
, fflag
, hflag
, pflag
, cnt
, errsv
;
312 int quietlog
, passwd_req
;
314 char tbuf
[MAXPATHLEN
+ 2], tname
[sizeof(_PATH_TTY
) + 10];
316 char * childArgv
[10];
321 pam_handle_t
*pamh
= NULL
;
322 struct pam_conv conv
= { misc_conv
, NULL
};
328 char vcsn
[20], vcsan
[20];
334 signal(SIGALRM
, timedout
);
335 alarm((unsigned int)timeout
);
336 signal(SIGQUIT
, SIG_IGN
);
337 signal(SIGINT
, SIG_IGN
);
339 setlocale(LC_ALL
, "");
340 bindtextdomain(PACKAGE
, LOCALEDIR
);
343 setpriority(PRIO_PROCESS
, 0, 0);
345 quota(Q_SETUID
, 0, 0, 0);
347 #ifdef DO_PS_FIDDLING
348 initproctitle(argc
, argv
);
352 * -p is used by getty to tell login not to destroy the environment
353 * -f is used to skip a second login authentication
354 * -h is used by other servers to pass the name of the remote
355 * host to login so that it may be placed in utmp and wtmp
357 gethostname(tbuf
, sizeof(tbuf
));
358 strncpy(thishost
, tbuf
, sizeof(thishost
)-1);
359 thishost
[sizeof(thishost
)-1] = 0;
360 domain
= index(tbuf
, '.');
362 username
= tty
= hostname
= NULL
;
363 fflag
= hflag
= pflag
= 0;
366 while ((ch
= getopt(argc
, argv
, "fh:p")) != EOF
)
375 _("login: -h for super-user only.\n"));
379 if (domain
&& (p
= index(optarg
, '.')) &&
380 strcasecmp(p
, domain
) == 0)
382 hostname
= strdup(optarg
); /* strdup: Ambrose C. Li */
384 struct hostent
*he
= gethostbyname(hostname
);
386 memcpy(&hostaddress
, he
, sizeof(hostaddress
));
388 memset(&hostaddress
, 0, sizeof(hostaddress
));
400 _("usage: login [-fp] [username]\n"));
407 username
= strdup(p
);
409 /* wipe name - some people mistype their password here */
410 /* (of course we are too late, but perhaps this helps a little ..) */
418 ioctl(0, TIOCLSET
, &ioctlval
);
419 ioctl(0, TIOCNXCL
, 0);
420 fcntl(0, F_SETFL
, ioctlval
);
421 ioctl(0, TIOCGETP
, &sgttyb
);
422 sgttyb
.sg_erase
= CERASE
;
423 sgttyb
.sg_kill
= CKILL
;
424 ioctl(0, TIOCSLTC
, <c
);
425 ioctl(0, TIOCSETC
, &tc
);
426 ioctl(0, TIOCSETP
, &sgttyb
);
429 * Be sure that we're in
431 * This is really for HPUX
434 ioctl(0, FIOSNBIO
, &ioctlval
);
435 #endif /* ! __linux__ */
437 for (cnt
= getdtablesize(); cnt
> 2; cnt
--)
441 if (ttyn
== NULL
|| *ttyn
== '\0') {
442 /* no snprintf required - see definition of tname */
443 sprintf(tname
, "%s??", _PATH_TTY
);
448 /* find names of Virtual Console devices, for later mode change */
451 /* find number of tty */
452 while (*p
&& !isdigit(*p
)) p
++;
454 strcpy(vcsn
, "/dev/vcs"); strcat(vcsn
, p
);
455 strcpy(vcsan
, "/dev/vcsa"); strcat(vcsan
, p
);
462 struct termios tt
, ttt
;
466 ttt
.c_cflag
&= ~HUPCL
;
468 if((chown(ttyn
, 0, 0) == 0) && (chmod(ttyn
, TTY_MODE
) == 0)) {
469 tcsetattr(0,TCSAFLUSH
,&ttt
);
470 signal(SIGHUP
, SIG_IGN
); /* so vhangup() wont kill us */
472 signal(SIGHUP
, SIG_DFL
);
477 /* re-open stdin,stdout,stderr after vhangup() closed them */
478 /* if it did, after 0.99.5 it doesn't! */
480 tcsetattr(0,TCSAFLUSH
,&tt
);
483 if (strncmp(ttyn
, "/dev/", 5) == 0)
488 openlog("login", LOG_ODELAY
, LOG_AUTHPRIV
);
491 /* other than iso-8859-1 */
493 fprintf(stderr
,"\033(K");
498 * username is initialized to NULL
499 * and if specified on the command line it is set.
500 * Therefore, we are safe not setting it to anything
503 retcode
= pam_start("login",username
, &conv
, &pamh
);
504 if(retcode
!= PAM_SUCCESS
) {
505 fprintf(stderr
,_("login: PAM Failure, aborting: %s\n"),
506 pam_strerror(pamh
, retcode
));
507 syslog(LOG_ERR
,_("Couldn't initialize PAM: %s"), pam_strerror(pamh
, retcode
));
510 /* hostname & tty are either set to NULL or their correct values,
511 depending on how much we know */
512 retcode
= pam_set_item(pamh
, PAM_RHOST
, hostname
);
514 retcode
= pam_set_item(pamh
, PAM_TTY
, tty
);
518 * Andrew.Taylor@cal.montage.ca: Provide a user prompt to PAM
519 * so that the "login: " prompt gets localized. Unfortunately,
520 * PAM doesn't have an interface to specify the "Password: " string
523 retcode
= pam_set_item(pamh
, PAM_USER_PROMPT
, _("login: "));
528 * other than iso-8859-1
529 * one more time due to reset tty by PAM
532 fprintf(stderr
,"\033(K");
535 /* if fflag == 1, then the user has already been authenticated */
536 if (fflag
&& (getuid() == 0))
541 if(passwd_req
== 1) {
544 /* there may be better ways to deal with some of these
545 conditions, but at least this way I don't think we'll
546 be giving away information... */
547 /* Perhaps someday we can trust that all PAM modules will
548 pay attention to failure count and get rid of MAX_LOGIN_TRIES? */
550 retcode
= pam_authenticate(pamh
, 0);
551 while((failcount
++ < PAM_MAX_LOGIN_TRIES
) &&
552 ((retcode
== PAM_AUTH_ERR
) ||
553 (retcode
== PAM_USER_UNKNOWN
) ||
554 (retcode
== PAM_CRED_INSUFFICIENT
) ||
555 (retcode
== PAM_AUTHINFO_UNAVAIL
))) {
556 pam_get_item(pamh
, PAM_USER
, (const void **) &username
);
557 syslog(LOG_NOTICE
,_("FAILED LOGIN %d FROM %s FOR %s, %s"),
558 failcount
, hostname
, username
, pam_strerror(pamh
, retcode
));
559 fprintf(stderr
,_("Login incorrect\n\n"));
560 pam_set_item(pamh
,PAM_USER
,NULL
);
561 retcode
= pam_authenticate(pamh
, 0);
564 if (retcode
!= PAM_SUCCESS
) {
565 pam_get_item(pamh
, PAM_USER
, (const void **) &username
);
567 if (retcode
== PAM_MAXTRIES
)
568 syslog(LOG_NOTICE
,_("TOO MANY LOGIN TRIES (%d) FROM %s FOR "
569 "%s, %s"), failcount
, hostname
, username
,
570 pam_strerror(pamh
, retcode
));
572 syslog(LOG_NOTICE
,_("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
573 hostname
, username
, pam_strerror(pamh
, retcode
));
575 fprintf(stderr
,_("\nLogin incorrect\n"));
576 pam_end(pamh
, retcode
);
580 retcode
= pam_acct_mgmt(pamh
, 0);
582 if(retcode
== PAM_NEW_AUTHTOK_REQD
) {
583 retcode
= pam_chauthtok(pamh
, PAM_CHANGE_EXPIRED_AUTHTOK
);
589 /* Grab the user information out of the password file for future usage
590 First get the username that we are actually using, though.
592 retcode
= pam_get_item(pamh
, PAM_USER
, (const void **) &username
);
594 pwd
= getpwnam(username
);
595 if (pwd
) initgroups(username
, pwd
->pw_gid
);
597 retcode
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
);
600 retcode
= pam_open_session(pamh
, 0);
603 #else /* ! USE_PAM */
605 for (cnt
= 0;; ask
= 1) {
608 ioctl(0, TIOCSETD
, &ioctlval
);
616 /* Dirty patch to fix a gigantic security hole when using
617 yellow pages. This problem should be solved by the
618 libraries, and not by programs, but this must be fixed
619 urgently! If the first char of the username is '+', we
621 Feb 95 <alvaro@etsit.upm.es> */
623 if (username
[0] == '+') {
624 puts(_("Illegal username"));
629 /* (void)strcpy(tbuf, username); why was this here? */
630 if ((pwd
= getpwnam(username
))) {
634 if ((sp
= getspnam(username
)))
635 pwd
->pw_passwd
= sp
->sp_pwdp
;
637 salt
= pwd
->pw_passwd
;
642 initgroups(username
, pwd
->pw_gid
);
643 checktty(username
, tty
, pwd
); /* in checktty.c */
646 /* if user not super-user, check for disabled logins */
647 if (pwd
== NULL
|| pwd
->pw_uid
)
651 * Disallow automatic login to root; if not invoked by
652 * root, disallow if the uid's differ.
657 passwd_req
= pwd
->pw_uid
== 0 ||
658 (uid
&& uid
!= pwd
->pw_uid
);
662 * If trying to log in as root, but with insecure terminal,
663 * refuse the login attempt.
665 if (pwd
&& pwd
->pw_uid
== 0 && !rootterm(tty
)) {
667 _("%s login refused on this terminal.\n"),
672 _("LOGIN %s REFUSED FROM %s ON TTY %s"),
673 pwd
->pw_name
, hostname
, tty
);
676 _("LOGIN %s REFUSED ON TTY %s"),
682 * If no pre-authentication and a password exists
683 * for this user, prompt for one and verify it.
685 if (!passwd_req
|| (pwd
&& !*pwd
->pw_passwd
))
688 setpriority(PRIO_PROCESS
, 0, -4);
689 pp
= getpass(_("Password: "));
692 if (strncmp(pp
, "CRYPTO", 6) == 0) {
693 if (pwd
&& cryptocard()) break;
695 # endif /* CRYPTOCARD */
698 setpriority(PRIO_PROCESS
, 0, 0);
702 * If not present in pw file, act as we normally would.
703 * If we aren't Kerberos-authenticated, try the normal
704 * pw file for a password. If that's ok, log the user
705 * in without issueing any tickets.
708 if (pwd
&& !krb_get_lrealm(realm
,1)) {
710 * get TGT for local realm; be careful about uid's
711 * here for ticket file ownership
713 setreuid(geteuid(),pwd
->pw_uid
);
714 kerror
= krb_get_pw_in_tkt(pwd
->pw_name
, "", realm
,
715 "krbtgt", realm
, DEFAULT_TKT_LIFE
, pp
);
717 if (kerror
== INTK_OK
) {
718 memset(pp
, 0, strlen(pp
));
719 notickets
= 0; /* user got ticket */
723 # endif /* KERBEROS */
724 memset(pp
, 0, strlen(pp
));
726 if (pwd
&& !strcmp(p
, pwd
->pw_passwd
))
729 printf(_("Login incorrect\n"));
730 badlogin(username
); /* log ALL bad logins */
733 /* we allow 10 tries, but after 3 we start backing off */
738 sleep((unsigned int)((cnt
- 3) * 5));
741 #endif /* !USE_PAM */
743 /* committed to login -- turn off timeout */
744 alarm((unsigned int)0);
747 if (quota(Q_SETUID
, pwd
->pw_uid
, 0, 0) < 0 && errno
!= EINVAL
) {
751 _("Too many users logged on already.\nTry again later.\n"));
755 _("You have too many processes running.\n"));
758 perror("quota (Q_SETUID)");
770 /* This requires some explanation: As root we may not be able to
771 read the directory of the user if it is on an NFS mounted
772 filesystem. We temporarily set our effective uid to the user-uid
773 making sure that we keep root privs. in the real uid.
775 A portable solution would require a fork(), but we rely on Linux
776 having the BSD setreuid() */
779 char tmpstr
[MAXPATHLEN
];
780 uid_t ruid
= getuid();
781 gid_t egid
= getegid();
783 /* avoid snprintf - old systems do not have it, or worse,
784 have a libc in which snprintf is the same as sprintf */
785 if (strlen(pwd
->pw_dir
) + sizeof(_PATH_HUSHLOGIN
) + 2 > MAXPATHLEN
)
788 sprintf(tmpstr
, "%s/%s", pwd
->pw_dir
, _PATH_HUSHLOGIN
);
789 setregid(-1, pwd
->pw_gid
);
790 setreuid(0, pwd
->pw_uid
);
791 quietlog
= (access(tmpstr
, R_OK
) == 0);
792 setuid(0); /* setreuid doesn't do it alone! */
800 if (notickets
&& !quietlog
)
801 printf(_("Warning: no Kerberos tickets issued\n"));
804 # ifndef USE_PAM /* PAM does all of this for us */
805 # define TWOWEEKS (14*24*60*60)
806 if (pwd
->pw_change
|| pwd
->pw_expire
) {
809 gettimeofday(&tp
, (struct timezone
*)NULL
);
811 if (pwd
->pw_change
) {
812 if (tp
.tv_sec
>= pwd
->pw_change
) {
813 printf(_("Sorry -- your password has expired.\n"));
816 else if (tp
.tv_sec
- pwd
->pw_change
< TWOWEEKS
&& !quietlog
) {
818 ttp
= localtime(&pwd
->pw_change
);
819 printf(_("Warning: your password expires on %d %s %d.\n"),
820 ttp
->tm_mday
, months
[ttp
->tm_mon
],
821 TM_YEAR_BASE
+ ttp
->tm_year
);
825 if (pwd
->pw_expire
) {
826 if (tp
.tv_sec
>= pwd
->pw_expire
) {
827 printf(_("Sorry -- your account has expired.\n"));
830 else if (tp
.tv_sec
- pwd
->pw_expire
< TWOWEEKS
&& !quietlog
) {
832 ttp
= localtime(&pwd
->pw_expire
);
833 printf(_("Warning: your account expires on %d %s %d.\n"),
834 ttp
->tm_mday
, months
[ttp
->tm_mon
],
835 TM_YEAR_BASE
+ ttp
->tm_year
);
839 # endif /* !USE_PAM */
841 /* nothing else left to fail -- really log in */
845 memset((char *)&utmp
, 0, sizeof(utmp
));
847 strncpy(utmp
.ut_name
, username
, sizeof(utmp
.ut_name
));
848 /* ut_name may legally be non-null-terminated */
850 strncpy(utmp
.ut_host
, hostname
, sizeof(utmp
.ut_host
));
851 utmp
.ut_host
[sizeof(utmp
.ut_host
)-1] = 0;
853 strncpy(utmp
.ut_line
, tty
, sizeof(utmp
.ut_line
));
854 utmp
.ut_line
[sizeof(utmp
.ut_line
)-1] = 0;
857 #else /* __linux__ defined */
858 /* for linux, write entries in utmp and wtmp */
862 pid_t mypid
= getpid();
864 utmpname(_PATH_UTMP
);
867 /* Find mypid in utmp.
868 login sometimes overwrites the runlevel entry in /var/run/utmp,
869 confusing sysvinit. I added a test for the entry type, and the problem
870 was gone. (In a runlevel entry, st_pid is not really a pid but some number
871 calculated from the previous and current runlevel).
872 Michael Riepe <michael@stud.uni-hannover.de>
874 while ((utp
= getutent()))
875 if (utp
->ut_pid
== mypid
876 && utp
->ut_type
>= INIT_PROCESS
877 && utp
->ut_type
<= DEAD_PROCESS
)
880 /* If we can't find a pre-existing entry by pid, try by line.
881 BSD network daemons may rely on this. (anonymous) */
884 ut
.ut_type
= LOGIN_PROCESS
;
885 strncpy(ut
.ut_id
, ttyn
+ 8, sizeof(ut
.ut_id
));
886 strncpy(ut
.ut_line
, ttyn
+ 5, sizeof(ut
.ut_line
));
891 memcpy(&ut
, utp
, sizeof(ut
));
893 /* some gettys/telnetds don't initialize utmp... */
894 memset(&ut
, 0, sizeof(ut
));
897 if (ut
.ut_id
[0] == 0)
898 strncpy(ut
.ut_id
, ttyn
+ 8, sizeof(ut
.ut_id
));
900 strncpy(ut
.ut_user
, username
, sizeof(ut
.ut_user
));
901 strncpy(ut
.ut_line
, ttyn
+ 5, sizeof(ut
.ut_line
));
902 ut
.ut_line
[sizeof(ut
.ut_line
)-1] = 0;
903 #ifdef _HAVE_UT_TV /* in <utmpbits.h> included by <utmp.h> */
904 gettimeofday(&ut
.ut_tv
, NULL
);
909 ut
.ut_time
= t
; /* ut_time is not always a time_t */
910 /* glibc2 #defines it as ut_tv.tv_sec */
913 ut
.ut_type
= USER_PROCESS
;
916 strncpy(ut
.ut_host
, hostname
, sizeof(ut
.ut_host
));
917 ut
.ut_host
[sizeof(ut
.ut_host
)-1] = 0;
918 if (hostaddress
.h_addr_list
)
919 memcpy(&ut
.ut_addr
, hostaddress
.h_addr_list
[0],
926 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
927 updwtmp(_PATH_WTMP
, &ut
);
930 /* The O_APPEND open() flag should be enough to guarantee
931 atomic writes at end of file. */
935 if((wtmp
= open(_PATH_WTMP
, O_APPEND
|O_WRONLY
)) >= 0) {
936 write(wtmp
, (char *)&ut
, sizeof(ut
));
941 /* Probably all this locking below is just nonsense,
942 and the short version is OK as well. */
945 if ((lf
= open(_PATH_WTMPLOCK
, O_CREAT
|O_WRONLY
, 0660)) >= 0) {
947 if ((wtmp
= open(_PATH_WTMP
, O_APPEND
|O_WRONLY
)) >= 0) {
948 write(wtmp
, (char *)&ut
, sizeof(ut
));
956 #endif /* __GLIBC__ */
958 #endif /* __linux__ */
963 if (!hflag
) { /* XXX */
964 static struct winsize win
= { 0, 0, 0, 0 };
966 ioctl(0, TIOCSWINSZ
, &win
);
969 chown(ttyn
, pwd
->pw_uid
,
970 (gr
= getgrnam(TTYGRPNAME
)) ? gr
->gr_gid
: pwd
->pw_gid
);
971 chmod(ttyn
, TTY_MODE
);
974 /* if tty is one of the VC's then change owner and mode of the
975 special /dev/vcs devices as well */
977 chown(vcsn
, pwd
->pw_uid
, (gr
? gr
->gr_gid
: pwd
->pw_gid
));
978 chown(vcsan
, pwd
->pw_uid
, (gr
? gr
->gr_gid
: pwd
->pw_gid
));
979 chmod(vcsn
, TTY_MODE
);
980 chmod(vcsan
, TTY_MODE
);
987 quota(Q_DOWARN
, pwd
->pw_uid
, (dev_t
)-1, 0);
990 if (*pwd
->pw_shell
== '\0')
991 pwd
->pw_shell
= _PATH_BSHELL
;
993 /* turn on new line discipline for the csh */
994 else if (!strcmp(pwd
->pw_shell
, _PATH_CSHELL
)) {
996 ioctl(0, TIOCSETD
, &ioctlval
);
1000 /* preserve TERM even without -p flag */
1004 if(!((ep
= getenv("TERM")) && (termenv
= strdup(ep
))))
1008 /* destroy environment unless user has requested preservation */
1011 environ
= (char**)malloc(sizeof(char*));
1012 memset(environ
, 0, sizeof(char*));
1016 setenv("HOME", pwd
->pw_dir
, 1);
1017 setenv("SHELL", pwd
->pw_shell
, 1);
1018 if (term
[0] == '\0') {
1019 strncpy(term
, stypeof(tty
), sizeof(term
));
1020 term
[sizeof(term
)-1] = 0;
1022 setenv("TERM", term
, 0);
1023 setenv("USER", pwd
->pw_name
, 1);
1024 setenv("PATH", _PATH_DEFPATH
, 0);
1026 setenv("HOME", pwd
->pw_dir
, 0); /* legal to override */
1028 setenv("PATH", _PATH_DEFPATH
, 1);
1030 setenv("PATH", _PATH_DEFPATH_ROOT
, 1);
1032 setenv("SHELL", pwd
->pw_shell
, 1);
1033 setenv("TERM", termenv
, 1);
1035 /* mailx will give a funny error msg if you forget this one */
1037 char tmp
[MAXPATHLEN
];
1038 /* avoid snprintf */
1039 if (sizeof(_PATH_MAILDIR
) + strlen(pwd
->pw_name
) + 1 < MAXPATHLEN
) {
1040 sprintf(tmp
, "%s/%s", _PATH_MAILDIR
, pwd
->pw_name
);
1041 setenv("MAIL",tmp
,0);
1045 /* LOGNAME is not documented in login(1) but
1046 HP-UX 6.5 does it. We'll not allow modifying it.
1048 setenv("LOGNAME", pwd
->pw_name
, 1);
1054 const char * const * env
;
1056 env
= (const char * const *)pam_getenvlist(pamh
);
1059 for (i
=0; env
[i
++]; ) {
1061 /* D(("env[%d] = %s", i-1,env[i-1])); */
1067 #ifdef DO_PS_FIDDLING
1068 setproctitle("login", username
);
1071 if (tty
[sizeof("tty")-1] == 'S')
1072 syslog(LOG_INFO
, _("DIALUP AT %s BY %s"), tty
, pwd
->pw_name
);
1074 /* allow tracking of good logins.
1075 -steve philp (sphilp@mail.alliance.net) */
1077 if (pwd
->pw_uid
== 0) {
1079 syslog(LOG_NOTICE
, _("ROOT LOGIN ON %s FROM %s"),
1082 syslog(LOG_NOTICE
, _("ROOT LOGIN ON %s"), tty
);
1085 syslog(LOG_INFO
, _("LOGIN ON %s BY %s FROM %s"), tty
,
1086 pwd
->pw_name
, hostname
);
1088 syslog(LOG_INFO
, _("LOGIN ON %s BY %s"), tty
,
1097 mail
= getenv("MAIL");
1098 if (mail
&& stat(mail
, &st
) == 0 && st
.st_size
!= 0
1099 && st
.st_size
!= REDHAT_IGNORED_MAILSIZE
) {
1100 printf(_("You have %smail.\n"),
1101 (st
.st_mtime
> st
.st_atime
) ? _("new ") : "");
1105 signal(SIGALRM
, SIG_DFL
);
1106 signal(SIGQUIT
, SIG_DFL
);
1107 signal(SIGTSTP
, SIG_IGN
);
1108 signal(SIGHUP
, SIG_DFL
);
1111 /* We must fork before setuid() because we need to call
1112 * pam_close_session() as root.
1114 signal(SIGINT
, SIG_IGN
);
1118 /* error in fork() */
1119 fprintf(stderr
,_("login: failure forking: %s"), strerror(errsv
));
1122 } else if (childPid
) {
1123 /* parent - wait for child to finish, then cleanup session */
1130 signal(SIGINT
, SIG_DFL
);
1132 /* discard permissions last so can't get killed and drop core */
1133 if(setuid(pwd
->pw_uid
) < 0 && pwd
->pw_uid
) {
1134 syslog(LOG_ALERT
, _("setuid() failed"));
1138 /* wait until here to change directory! */
1139 if (chdir(pwd
->pw_dir
) < 0) {
1140 printf(_("No directory %s!\n"), pwd
->pw_dir
);
1144 printf(_("Logging in with home = \"/\".\n"));
1147 /* if the shell field has a space: treat it like a shell script */
1148 if (strchr(pwd
->pw_shell
, ' ')) {
1149 buff
= malloc(strlen(pwd
->pw_shell
) + 6);
1152 fprintf(stderr
, _("login: no memory for shell script.\n"));
1156 strcpy(buff
, "exec ");
1157 strcat(buff
, pwd
->pw_shell
);
1158 childArgv
[childArgc
++] = "/bin/sh";
1159 childArgv
[childArgc
++] = "-sh";
1160 childArgv
[childArgc
++] = "-c";
1161 childArgv
[childArgc
++] = buff
;
1164 strncpy(tbuf
+ 1, ((p
= rindex(pwd
->pw_shell
, '/')) ?
1165 p
+ 1 : pwd
->pw_shell
),
1167 tbuf
[sizeof(tbuf
)-1] = 0;
1169 childArgv
[childArgc
++] = pwd
->pw_shell
;
1170 childArgv
[childArgc
++] = tbuf
;
1173 childArgv
[childArgc
++] = NULL
;
1175 execvp(childArgv
[0], childArgv
+ 1);
1179 if (!strcmp(childArgv
[0], "/bin/sh"))
1180 fprintf(stderr
, _("login: couldn't exec shell script: %s.\n"),
1183 fprintf(stderr
, _("login: no shell: %s.\n"), strerror(errsv
));
1189 getloginname(void) {
1192 static char nbuf
[UT_NAMESIZE
+ 1];
1197 printf(_("\n%s login: "), thishost
); fflush(stdout
);
1198 for (p
= nbuf
; (ch
= getchar()) != '\n'; ) {
1203 if (p
< nbuf
+ UT_NAMESIZE
)
1207 if (cnt
> UT_NAMESIZE
+ 20) {
1208 fprintf(stderr
, _("login name much too long.\n"));
1209 badlogin(_("NAME too long"));
1216 _("login names may not start with '-'.\n"));
1226 fprintf(stderr
, _("too many bare linefeeds.\n"));
1227 badlogin(_("EXCESSIVE linefeeds"));
1237 fprintf(stderr
, _("Login timed out after %d seconds\n"), timeout
);
1240 ioctl(0, TCGETA
, &ti
);
1242 ioctl(0, TCSETA
, &ti
);
1247 rootterm(char * ttyn
)
1252 return((t
= getttynam(ttyn
)) && t
->ty_status
&TTY_SECURE
);
1260 fd
= open(SECURETTY
, O_RDONLY
);
1261 if(fd
< 0) return 1;
1263 /* read each line in /etc/securetty, if a line matches our ttyline
1264 then root is allowed to login on this tty, and we should return
1268 while(--cnt
>= 0 && (more
= read(fd
, p
, 1)) == 1 && *p
!= '\n') p
++;
1269 if(more
&& *p
== '\n') {
1271 if(!strcmp(buf
, ttyn
)) {
1284 jmp_buf motdinterrupt
;
1289 void (*oldint
)(int);
1292 if ((fd
= open(_PATH_MOTDFILE
, O_RDONLY
, 0)) < 0)
1294 oldint
= signal(SIGINT
, sigint
);
1295 if (setjmp(motdinterrupt
) == 0)
1296 while ((nchars
= read(fd
, tbuf
, sizeof(tbuf
))) > 0)
1297 write(fileno(stdout
), tbuf
, nchars
);
1298 signal(SIGINT
, oldint
);
1304 longjmp(motdinterrupt
, 1);
1307 #ifndef USE_PAM /* PAM takes care of this */
1309 checknologin(void) {
1310 register int fd
, nchars
;
1313 if ((fd
= open(_PATH_NOLOGIN
, O_RDONLY
, 0)) >= 0) {
1314 while ((nchars
= read(fd
, tbuf
, sizeof(tbuf
))) > 0)
1315 write(fileno(stdout
), tbuf
, nchars
);
1322 dolastlog(int quiet
) {
1326 if ((fd
= open(_PATH_LASTLOG
, O_RDWR
, 0)) >= 0) {
1327 lseek(fd
, (off_t
)pwd
->pw_uid
* sizeof(ll
), SEEK_SET
);
1329 if (read(fd
, (char *)&ll
, sizeof(ll
)) == sizeof(ll
) &&
1331 printf(_("Last login: %.*s "),
1332 24-5, (char *)ctime(&ll
.ll_time
));
1334 if (*ll
.ll_host
!= '\0')
1335 printf(_("from %.*s\n"),
1336 (int)sizeof(ll
.ll_host
), ll
.ll_host
);
1338 printf(_("on %.*s\n"),
1339 (int)sizeof(ll
.ll_line
), ll
.ll_line
);
1341 lseek(fd
, (off_t
)pwd
->pw_uid
* sizeof(ll
), SEEK_SET
);
1343 memset((char *)&ll
, 0, sizeof(ll
));
1345 strncpy(ll
.ll_line
, tty
, sizeof(ll
.ll_line
));
1346 ll
.ll_line
[sizeof(ll
.ll_line
)-1] = 0;
1348 strncpy(ll
.ll_host
, hostname
, sizeof(ll
.ll_host
));
1349 ll
.ll_host
[sizeof(ll
.ll_host
)-1] = 0;
1351 write(fd
, (char *)&ll
, sizeof(ll
));
1357 badlogin(const char *name
)
1359 if (failures
== 1) {
1361 syslog(LOG_NOTICE
, _("LOGIN FAILURE FROM %s, %s"),
1364 syslog(LOG_NOTICE
, _("LOGIN FAILURE ON %s, %s"),
1368 syslog(LOG_NOTICE
, _("%d LOGIN FAILURES FROM %s, %s"),
1369 failures
, hostname
, name
);
1371 syslog(LOG_NOTICE
, _("%d LOGIN FAILURES ON %s, %s"),
1372 failures
, tty
, name
);
1377 #define UNKNOWN "su"
1381 stypeof(char *ttyid
) {
1384 return(ttyid
&& (t
= getttynam(ttyid
)) ? t
->ty_type
: UNKNOWN
);
1388 /* should not be called from PAM code... Why? */
1390 sleepexit(int eval
) {
1391 sleep(SLEEP_EXIT_TIMEOUT
);