]> git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/login.c
Imported from util-linux-2.10s tarball.
[thirdparty/util-linux.git] / login-utils / login.c
1 /* This program is derived from 4.3 BSD software and is
2 subject to the copyright notice below.
3
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.
6
7 Changes:
8
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.
12
13 - The program uses BSD command line options to be used
14 in connection with e.g. 'rlogind' i.e. 'new login'.
15
16 - HP features left out: logging of bad login attempts in /etc/btmp,
17 they are sent to syslog
18
19 password expiry
20
21 '*' as login shell, add it if you need it
22
23 - BSD features left out: quota checks
24 password expiry
25 analysis of terminal type (tset feature)
26
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
30
31 'Lastlog' feature.
32
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'.
36
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.
39
40 Michael Glad (glad@daimi.dk)
41 Computer Science Department
42 Aarhus University
43 Denmark
44
45 1990-07-04
46
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
50
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/
54
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
59 */
60
61 /*
62 * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
63 * All rights reserved.
64 *
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.
76 */
77
78 /*
79 * login [ name ]
80 * login -h hostname (for telnetd, etc.)
81 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
82 */
83
84 /* #define TESTING */
85
86 #ifdef TESTING
87 #include "param.h"
88 #else
89 #include <sys/param.h>
90 #endif
91
92 #include <stdio.h>
93 #include <ctype.h>
94 #include <unistd.h>
95 #include <getopt.h>
96 #include <memory.h>
97 #include <time.h>
98 #include <sys/stat.h>
99 #include <sys/time.h>
100 #include <sys/resource.h>
101 #include <sys/file.h>
102 #include <termios.h>
103 #include <string.h>
104 #define index strchr
105 #define rindex strrchr
106 #include <sys/ioctl.h>
107 #include <sys/wait.h>
108 #include <signal.h>
109 #include <errno.h>
110 #include <grp.h>
111 #include <pwd.h>
112 #include <utmp.h>
113 #include <setjmp.h>
114 #include <stdlib.h>
115 #include <string.h>
116 #include <sys/syslog.h>
117 #include <sys/sysmacros.h>
118 #include <netdb.h>
119 #include "pathnames.h"
120 #include "my_crypt.h"
121 #include "login.h"
122 #include "nls.h"
123
124 #ifdef __linux__
125 # include <sys/sysmacros.h>
126 # include <linux/major.h>
127 #endif
128
129 #ifdef TESTING
130 # include "utmp.h"
131 #else
132 # include <utmp.h>
133 #endif
134
135 #ifdef SHADOW_PWD
136 # include <shadow.h>
137 #endif
138
139 #ifdef USE_PAM
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); \
147 }
148 # define PAM_END { retcode = pam_close_session(pamh,0); \
149 pam_end(pamh,retcode); }
150 #endif
151
152 #ifndef __linux__
153 # include <tzfile.h>
154 #endif
155 #include <lastlog.h>
156
157 #define SLEEP_EXIT_TIMEOUT 5
158
159 #ifdef __linux__
160 #define DO_PS_FIDDLING
161 #endif
162
163 #ifdef DO_PS_FIDDLING
164 #include "setproctitle.h"
165 #endif
166
167 /*
168 * RedHat writes:
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.
174 */
175 /*
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..
181 */
182 #define REDHAT_IGNORED_MAILSIZE 523
183
184 #if 0
185 /* from before we had a lastlog.h file in linux */
186 struct lastlog
187 { long ll_time;
188 char ll_line[12];
189 char ll_host[16];
190 };
191 #endif
192
193
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);
201
202 #ifndef __linux__
203 static char *stypeof (char *ttyid);
204 #endif
205
206 #ifdef CRYPTOCARD
207 #include "cryptocard.h"
208 #endif
209
210 #ifdef KERBEROS
211 #include <kerberos/krb.h>
212 #include <sys/termios.h>
213 char realm[REALM_SZ];
214 int kerror = KSUCCESS, notickets = 1;
215 #endif
216
217 #ifdef USE_TTY_GROUP
218 # define TTY_MODE 0620
219 #else
220 # define TTY_MODE 0600
221 #endif
222
223 #define TTYGRPNAME "tty" /* name of group to own ttys */
224 /**# define TTYGRPNAME "other" **/
225
226 #ifndef MAXPATHLEN
227 # define MAXPATHLEN 1024
228 #endif
229
230 /*
231 * This bounds the time given to login. Not a define so it can
232 * be patched on machines where it's too small.
233 */
234 #ifndef __linux__
235 int timeout = 300;
236 #else
237 int timeout = 60; /* used in cryptocard.c */
238 #endif
239
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;
245
246 #ifndef __linux__
247 struct sgttyb sgttyb;
248 struct tchars tc = {
249 CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK
250 };
251 struct ltchars ltc = {
252 CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT
253 };
254 #endif
255
256 const char *months[] =
257 { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
258 "Sep", "Oct", "Nov", "Dec" };
259
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
267 belongs). */
268 static void
269 opentty(const char * tty)
270 {
271 int i;
272 int fd = open(tty, O_RDWR | O_NONBLOCK);
273 int flags = fcntl(fd, F_GETFL);
274
275 flags &= ~O_NONBLOCK;
276 fcntl(fd, F_SETFL, flags);
277
278 for (i = 0 ; i < fd ; i++)
279 close(i);
280 for (i = 0 ; i < 3 ; i++)
281 dup2(fd, i);
282 if (fd >= 3)
283 close(fd);
284 }
285
286 /* true if the filedescriptor fd is a console tty, very Linux specific */
287 static int
288 consoletty(int fd)
289 {
290 #ifdef __linux__
291 struct stat stb;
292
293 if ((fstat(fd, &stb) >= 0)
294 && (major(stb.st_rdev) == TTY_MAJOR)
295 && (minor(stb.st_rdev) < 64)) {
296 return 1;
297 }
298 #endif
299 return 0;
300 }
301
302
303 int
304 main(int argc, char **argv)
305 {
306 extern int optind;
307 extern char *optarg, **environ;
308 struct group *gr;
309 register int ch;
310 register char *p;
311 int ask, fflag, hflag, pflag, cnt, errsv;
312 int quietlog, passwd_req;
313 char *domain, *ttyn;
314 char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
315 char *termenv;
316 char * childArgv[10];
317 char * buff;
318 int childArgc = 0;
319 #ifdef USE_PAM
320 int retcode;
321 pam_handle_t *pamh = NULL;
322 struct pam_conv conv = { misc_conv, NULL };
323 pid_t childPid;
324 #else
325 char *salt, *pp;
326 #endif
327 #ifdef CHOWNVCS
328 char vcsn[20], vcsan[20];
329 #endif
330 #ifndef __linux__
331 int ioctlval;
332 #endif
333
334 signal(SIGALRM, timedout);
335 alarm((unsigned int)timeout);
336 signal(SIGQUIT, SIG_IGN);
337 signal(SIGINT, SIG_IGN);
338
339 setlocale(LC_ALL, "");
340 bindtextdomain(PACKAGE, LOCALEDIR);
341 textdomain(PACKAGE);
342
343 setpriority(PRIO_PROCESS, 0, 0);
344 #ifdef HAVE_QUOTA
345 quota(Q_SETUID, 0, 0, 0);
346 #endif
347 #ifdef DO_PS_FIDDLING
348 initproctitle(argc, argv);
349 #endif
350
351 /*
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
356 */
357 gethostname(tbuf, sizeof(tbuf));
358 strncpy(thishost, tbuf, sizeof(thishost)-1);
359 thishost[sizeof(thishost)-1] = 0;
360 domain = index(tbuf, '.');
361
362 username = tty = hostname = NULL;
363 fflag = hflag = pflag = 0;
364 passwd_req = 1;
365
366 while ((ch = getopt(argc, argv, "fh:p")) != EOF)
367 switch (ch) {
368 case 'f':
369 fflag = 1;
370 break;
371
372 case 'h':
373 if (getuid()) {
374 fprintf(stderr,
375 _("login: -h for super-user only.\n"));
376 exit(1);
377 }
378 hflag = 1;
379 if (domain && (p = index(optarg, '.')) &&
380 strcasecmp(p, domain) == 0)
381 *p = 0;
382 hostname = strdup(optarg); /* strdup: Ambrose C. Li */
383 {
384 struct hostent *he = gethostbyname(hostname);
385 if (he) {
386 memcpy(&hostaddress, he, sizeof(hostaddress));
387 } else {
388 memset(&hostaddress, 0, sizeof(hostaddress));
389 }
390 }
391 break;
392
393 case 'p':
394 pflag = 1;
395 break;
396
397 case '?':
398 default:
399 fprintf(stderr,
400 _("usage: login [-fp] [username]\n"));
401 exit(1);
402 }
403 argc -= optind;
404 argv += optind;
405 if (*argv) {
406 char *p = *argv;
407 username = strdup(p);
408 ask = 0;
409 /* wipe name - some people mistype their password here */
410 /* (of course we are too late, but perhaps this helps a little ..) */
411 while(*p)
412 *p++ = ' ';
413 } else
414 ask = 1;
415
416 #ifndef __linux__
417 ioctlval = 0;
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, &ltc);
425 ioctl(0, TIOCSETC, &tc);
426 ioctl(0, TIOCSETP, &sgttyb);
427
428 /*
429 * Be sure that we're in
430 * blocking mode!!!
431 * This is really for HPUX
432 */
433 ioctlval = 0;
434 ioctl(0, FIOSNBIO, &ioctlval);
435 #endif /* ! __linux__ */
436
437 for (cnt = getdtablesize(); cnt > 2; cnt--)
438 close(cnt);
439
440 ttyn = ttyname(0);
441 if (ttyn == NULL || *ttyn == '\0') {
442 /* no snprintf required - see definition of tname */
443 sprintf(tname, "%s??", _PATH_TTY);
444 ttyn = tname;
445 }
446
447 #ifdef CHOWNVCS
448 /* find names of Virtual Console devices, for later mode change */
449 {
450 char *p = ttyn;
451 /* find number of tty */
452 while (*p && !isdigit(*p)) p++;
453
454 strcpy(vcsn, "/dev/vcs"); strcat(vcsn, p);
455 strcpy(vcsan, "/dev/vcsa"); strcat(vcsan, p);
456 }
457 #endif
458
459 setpgrp();
460
461 {
462 struct termios tt, ttt;
463
464 tcgetattr(0, &tt);
465 ttt = tt;
466 ttt.c_cflag &= ~HUPCL;
467
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 */
471 vhangup();
472 signal(SIGHUP, SIG_DFL);
473 }
474
475 setsid();
476
477 /* re-open stdin,stdout,stderr after vhangup() closed them */
478 /* if it did, after 0.99.5 it doesn't! */
479 opentty(ttyn);
480 tcsetattr(0,TCSAFLUSH,&tt);
481 }
482
483 if (strncmp(ttyn, "/dev/", 5) == 0)
484 tty = ttyn+5;
485 else
486 tty = ttyn;
487
488 openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
489
490 #if 0
491 /* other than iso-8859-1 */
492 printf("\033(K");
493 fprintf(stderr,"\033(K");
494 #endif
495
496 #ifdef USE_PAM
497 /*
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
501 */
502
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));
508 exit(99);
509 }
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);
513 PAM_FAIL_CHECK;
514 retcode = pam_set_item(pamh, PAM_TTY, tty);
515 PAM_FAIL_CHECK;
516
517 /*
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
521 * (yet).
522 */
523 retcode = pam_set_item(pamh, PAM_USER_PROMPT, _("login: "));
524 PAM_FAIL_CHECK;
525
526 #if 0
527 /*
528 * other than iso-8859-1
529 * one more time due to reset tty by PAM
530 */
531 printf("\033(K");
532 fprintf(stderr,"\033(K");
533 #endif
534
535 /* if fflag == 1, then the user has already been authenticated */
536 if (fflag && (getuid() == 0))
537 passwd_req = 0;
538 else
539 passwd_req = 1;
540
541 if(passwd_req == 1) {
542 int failcount=0;
543
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? */
549
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);
562 }
563
564 if (retcode != PAM_SUCCESS) {
565 pam_get_item(pamh, PAM_USER, (const void **) &username);
566
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));
571 else
572 syslog(LOG_NOTICE,_("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
573 hostname, username, pam_strerror(pamh, retcode));
574
575 fprintf(stderr,_("\nLogin incorrect\n"));
576 pam_end(pamh, retcode);
577 exit(0);
578 }
579
580 retcode = pam_acct_mgmt(pamh, 0);
581
582 if(retcode == PAM_NEW_AUTHTOK_REQD) {
583 retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
584 }
585
586 PAM_FAIL_CHECK;
587 }
588
589 /* Grab the user information out of the password file for future usage
590 First get the username that we are actually using, though.
591 */
592 retcode = pam_get_item(pamh, PAM_USER, (const void **) &username);
593 setpwent();
594 pwd = getpwnam(username);
595 if (pwd) initgroups(username, pwd->pw_gid);
596
597 retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
598 PAM_FAIL_CHECK;
599
600 retcode = pam_open_session(pamh, 0);
601 PAM_FAIL_CHECK;
602
603 #else /* ! USE_PAM */
604
605 for (cnt = 0;; ask = 1) {
606 # ifndef __linux__
607 ioctlval = 0;
608 ioctl(0, TIOCSETD, &ioctlval);
609 # endif
610
611 if (ask) {
612 fflag = 0;
613 getloginname();
614 }
615
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
620 avoid login success.
621 Feb 95 <alvaro@etsit.upm.es> */
622
623 if (username[0] == '+') {
624 puts(_("Illegal username"));
625 badlogin(username);
626 sleepexit(1);
627 }
628
629 /* (void)strcpy(tbuf, username); why was this here? */
630 if ((pwd = getpwnam(username))) {
631 # ifdef SHADOW_PWD
632 struct spwd *sp;
633
634 if ((sp = getspnam(username)))
635 pwd->pw_passwd = sp->sp_pwdp;
636 # endif
637 salt = pwd->pw_passwd;
638 } else
639 salt = "xx";
640
641 if (pwd) {
642 initgroups(username, pwd->pw_gid);
643 checktty(username, tty, pwd); /* in checktty.c */
644 }
645
646 /* if user not super-user, check for disabled logins */
647 if (pwd == NULL || pwd->pw_uid)
648 checknologin();
649
650 /*
651 * Disallow automatic login to root; if not invoked by
652 * root, disallow if the uid's differ.
653 */
654 if (fflag && pwd) {
655 int uid = getuid();
656
657 passwd_req = pwd->pw_uid == 0 ||
658 (uid && uid != pwd->pw_uid);
659 }
660
661 /*
662 * If trying to log in as root, but with insecure terminal,
663 * refuse the login attempt.
664 */
665 if (pwd && pwd->pw_uid == 0 && !rootterm(tty)) {
666 fprintf(stderr,
667 _("%s login refused on this terminal.\n"),
668 pwd->pw_name);
669
670 if (hostname)
671 syslog(LOG_NOTICE,
672 _("LOGIN %s REFUSED FROM %s ON TTY %s"),
673 pwd->pw_name, hostname, tty);
674 else
675 syslog(LOG_NOTICE,
676 _("LOGIN %s REFUSED ON TTY %s"),
677 pwd->pw_name, tty);
678 continue;
679 }
680
681 /*
682 * If no pre-authentication and a password exists
683 * for this user, prompt for one and verify it.
684 */
685 if (!passwd_req || (pwd && !*pwd->pw_passwd))
686 break;
687
688 setpriority(PRIO_PROCESS, 0, -4);
689 pp = getpass(_("Password: "));
690
691 # ifdef CRYPTOCARD
692 if (strncmp(pp, "CRYPTO", 6) == 0) {
693 if (pwd && cryptocard()) break;
694 }
695 # endif /* CRYPTOCARD */
696
697 p = crypt(pp, salt);
698 setpriority(PRIO_PROCESS, 0, 0);
699
700 # ifdef KERBEROS
701 /*
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.
706 */
707
708 if (pwd && !krb_get_lrealm(realm,1)) {
709 /*
710 * get TGT for local realm; be careful about uid's
711 * here for ticket file ownership
712 */
713 setreuid(geteuid(),pwd->pw_uid);
714 kerror = krb_get_pw_in_tkt(pwd->pw_name, "", realm,
715 "krbtgt", realm, DEFAULT_TKT_LIFE, pp);
716 setuid(0);
717 if (kerror == INTK_OK) {
718 memset(pp, 0, strlen(pp));
719 notickets = 0; /* user got ticket */
720 break;
721 }
722 }
723 # endif /* KERBEROS */
724 memset(pp, 0, strlen(pp));
725
726 if (pwd && !strcmp(p, pwd->pw_passwd))
727 break;
728
729 printf(_("Login incorrect\n"));
730 badlogin(username); /* log ALL bad logins */
731 failures++;
732
733 /* we allow 10 tries, but after 3 we start backing off */
734 if (++cnt > 3) {
735 if (cnt >= 10) {
736 sleepexit(1);
737 }
738 sleep((unsigned int)((cnt - 3) * 5));
739 }
740 }
741 #endif /* !USE_PAM */
742
743 /* committed to login -- turn off timeout */
744 alarm((unsigned int)0);
745
746 #ifdef HAVE_QUOTA
747 if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0 && errno != EINVAL) {
748 switch(errno) {
749 case EUSERS:
750 fprintf(stderr,
751 _("Too many users logged on already.\nTry again later.\n"));
752 break;
753 case EPROCLIM:
754 fprintf(stderr,
755 _("You have too many processes running.\n"));
756 break;
757 default:
758 perror("quota (Q_SETUID)");
759 }
760 sleepexit(0);
761 }
762 #endif
763
764 /* paranoia... */
765 #ifdef SHADOW_PWD
766 endspent();
767 #endif
768 endpwent();
769
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.
774
775 A portable solution would require a fork(), but we rely on Linux
776 having the BSD setreuid() */
777
778 {
779 char tmpstr[MAXPATHLEN];
780 uid_t ruid = getuid();
781 gid_t egid = getegid();
782
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)
786 quietlog = 0;
787 else {
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! */
793 setreuid(ruid, 0);
794 setregid(-1, egid);
795 }
796 }
797
798 #ifndef __linux__
799 # ifdef KERBEROS
800 if (notickets && !quietlog)
801 printf(_("Warning: no Kerberos tickets issued\n"));
802 # endif
803
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) {
807 struct timeval tp;
808
809 gettimeofday(&tp, (struct timezone *)NULL);
810
811 if (pwd->pw_change) {
812 if (tp.tv_sec >= pwd->pw_change) {
813 printf(_("Sorry -- your password has expired.\n"));
814 sleepexit(1);
815 }
816 else if (tp.tv_sec - pwd->pw_change < TWOWEEKS && !quietlog) {
817 struct tm *ttp;
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);
822 }
823 }
824
825 if (pwd->pw_expire) {
826 if (tp.tv_sec >= pwd->pw_expire) {
827 printf(_("Sorry -- your account has expired.\n"));
828 sleepexit(1);
829 }
830 else if (tp.tv_sec - pwd->pw_expire < TWOWEEKS && !quietlog) {
831 struct tm *ttp;
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);
836 }
837 }
838 }
839 # endif /* !USE_PAM */
840
841 /* nothing else left to fail -- really log in */
842 {
843 struct utmp utmp;
844
845 memset((char *)&utmp, 0, sizeof(utmp));
846 time(&utmp.ut_time);
847 strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
848 /* ut_name may legally be non-null-terminated */
849 if (hostname) {
850 strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
851 utmp.ut_host[sizeof(utmp.ut_host)-1] = 0;
852 }
853 strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
854 utmp.ut_line[sizeof(utmp.ut_line)-1] = 0;
855 login(&utmp);
856 }
857 #else /* __linux__ defined */
858 /* for linux, write entries in utmp and wtmp */
859 {
860 struct utmp ut;
861 struct utmp *utp;
862 pid_t mypid = getpid();
863
864 utmpname(_PATH_UTMP);
865 setutent();
866
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>
873 */
874 while ((utp = getutent()))
875 if (utp->ut_pid == mypid
876 && utp->ut_type >= INIT_PROCESS
877 && utp->ut_type <= DEAD_PROCESS)
878 break;
879
880 /* If we can't find a pre-existing entry by pid, try by line.
881 BSD network daemons may rely on this. (anonymous) */
882 if (utp == NULL) {
883 setutent();
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));
887 utp = getutid(&ut);
888 }
889
890 if (utp) {
891 memcpy(&ut, utp, sizeof(ut));
892 } else {
893 /* some gettys/telnetds don't initialize utmp... */
894 memset(&ut, 0, sizeof(ut));
895 }
896
897 if (ut.ut_id[0] == 0)
898 strncpy(ut.ut_id, ttyn + 8, sizeof(ut.ut_id));
899
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);
905 #else
906 {
907 time_t t;
908 time(&t);
909 ut.ut_time = t; /* ut_time is not always a time_t */
910 /* glibc2 #defines it as ut_tv.tv_sec */
911 }
912 #endif
913 ut.ut_type = USER_PROCESS;
914 ut.ut_pid = mypid;
915 if (hostname) {
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],
920 sizeof(ut.ut_addr));
921 }
922
923 pututline(&ut);
924 endutent();
925
926 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
927 updwtmp(_PATH_WTMP, &ut);
928 #else
929 #if 0
930 /* The O_APPEND open() flag should be enough to guarantee
931 atomic writes at end of file. */
932 {
933 int wtmp;
934
935 if((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
936 write(wtmp, (char *)&ut, sizeof(ut));
937 close(wtmp);
938 }
939 }
940 #else
941 /* Probably all this locking below is just nonsense,
942 and the short version is OK as well. */
943 {
944 int lf, wtmp;
945 if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) {
946 flock(lf, LOCK_EX);
947 if ((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
948 write(wtmp, (char *)&ut, sizeof(ut));
949 close(wtmp);
950 }
951 flock(lf, LOCK_UN);
952 close(lf);
953 }
954 }
955 #endif
956 #endif /* __GLIBC__ */
957 }
958 #endif /* __linux__ */
959
960 dolastlog(quietlog);
961
962 #ifndef __linux__
963 if (!hflag) { /* XXX */
964 static struct winsize win = { 0, 0, 0, 0 };
965
966 ioctl(0, TIOCSWINSZ, &win);
967 }
968 #endif
969 chown(ttyn, pwd->pw_uid,
970 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
971 chmod(ttyn, TTY_MODE);
972
973 #ifdef CHOWNVCS
974 /* if tty is one of the VC's then change owner and mode of the
975 special /dev/vcs devices as well */
976 if (consoletty(0)) {
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);
981 }
982 #endif
983
984 setgid(pwd->pw_gid);
985
986 #ifdef HAVE_QUOTA
987 quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0);
988 #endif
989
990 if (*pwd->pw_shell == '\0')
991 pwd->pw_shell = _PATH_BSHELL;
992 #ifndef __linux__
993 /* turn on new line discipline for the csh */
994 else if (!strcmp(pwd->pw_shell, _PATH_CSHELL)) {
995 ioctlval = NTTYDISC;
996 ioctl(0, TIOCSETD, &ioctlval);
997 }
998 #endif
999
1000 /* preserve TERM even without -p flag */
1001 {
1002 char *ep;
1003
1004 if(!((ep = getenv("TERM")) && (termenv = strdup(ep))))
1005 termenv = "dumb";
1006 }
1007
1008 /* destroy environment unless user has requested preservation */
1009 if (!pflag)
1010 {
1011 environ = (char**)malloc(sizeof(char*));
1012 memset(environ, 0, sizeof(char*));
1013 }
1014
1015 #ifndef __linux__
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;
1021 }
1022 setenv("TERM", term, 0);
1023 setenv("USER", pwd->pw_name, 1);
1024 setenv("PATH", _PATH_DEFPATH, 0);
1025 #else
1026 setenv("HOME", pwd->pw_dir, 0); /* legal to override */
1027 if(pwd->pw_uid)
1028 setenv("PATH", _PATH_DEFPATH, 1);
1029 else
1030 setenv("PATH", _PATH_DEFPATH_ROOT, 1);
1031
1032 setenv("SHELL", pwd->pw_shell, 1);
1033 setenv("TERM", termenv, 1);
1034
1035 /* mailx will give a funny error msg if you forget this one */
1036 {
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);
1042 }
1043 }
1044
1045 /* LOGNAME is not documented in login(1) but
1046 HP-UX 6.5 does it. We'll not allow modifying it.
1047 */
1048 setenv("LOGNAME", pwd->pw_name, 1);
1049 #endif
1050
1051 #ifdef USE_PAM
1052 {
1053 int i;
1054 const char * const * env;
1055
1056 env = (const char * const *)pam_getenvlist(pamh);
1057
1058 if (env != NULL) {
1059 for (i=0; env[i++]; ) {
1060 putenv(env[i-1]);
1061 /* D(("env[%d] = %s", i-1,env[i-1])); */
1062 }
1063 }
1064 }
1065 #endif
1066
1067 #ifdef DO_PS_FIDDLING
1068 setproctitle("login", username);
1069 #endif
1070
1071 if (tty[sizeof("tty")-1] == 'S')
1072 syslog(LOG_INFO, _("DIALUP AT %s BY %s"), tty, pwd->pw_name);
1073
1074 /* allow tracking of good logins.
1075 -steve philp (sphilp@mail.alliance.net) */
1076
1077 if (pwd->pw_uid == 0) {
1078 if (hostname)
1079 syslog(LOG_NOTICE, _("ROOT LOGIN ON %s FROM %s"),
1080 tty, hostname);
1081 else
1082 syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), tty);
1083 } else {
1084 if (hostname)
1085 syslog(LOG_INFO, _("LOGIN ON %s BY %s FROM %s"), tty,
1086 pwd->pw_name, hostname);
1087 else
1088 syslog(LOG_INFO, _("LOGIN ON %s BY %s"), tty,
1089 pwd->pw_name);
1090 }
1091
1092 if (!quietlog) {
1093 struct stat st;
1094 char *mail;
1095
1096 motd();
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 ") : "");
1102 }
1103 }
1104
1105 signal(SIGALRM, SIG_DFL);
1106 signal(SIGQUIT, SIG_DFL);
1107 signal(SIGTSTP, SIG_IGN);
1108 signal(SIGHUP, SIG_DFL);
1109
1110 #ifdef USE_PAM
1111 /* We must fork before setuid() because we need to call
1112 * pam_close_session() as root.
1113 */
1114 signal(SIGINT, SIG_IGN);
1115 childPid = fork();
1116 if (childPid < 0) {
1117 int errsv = errno;
1118 /* error in fork() */
1119 fprintf(stderr,_("login: failure forking: %s"), strerror(errsv));
1120 PAM_END;
1121 exit(0);
1122 } else if (childPid) {
1123 /* parent - wait for child to finish, then cleanup session */
1124 wait(NULL);
1125 PAM_END;
1126 exit(0);
1127 }
1128 /* child */
1129 #endif
1130 signal(SIGINT, SIG_DFL);
1131
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"));
1135 exit(1);
1136 }
1137
1138 /* wait until here to change directory! */
1139 if (chdir(pwd->pw_dir) < 0) {
1140 printf(_("No directory %s!\n"), pwd->pw_dir);
1141 if (chdir("/"))
1142 exit(0);
1143 pwd->pw_dir = "/";
1144 printf(_("Logging in with home = \"/\".\n"));
1145 }
1146
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);
1150
1151 if (!buff) {
1152 fprintf(stderr, _("login: no memory for shell script.\n"));
1153 exit(0);
1154 }
1155
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;
1162 } else {
1163 tbuf[0] = '-';
1164 strncpy(tbuf + 1, ((p = rindex(pwd->pw_shell, '/')) ?
1165 p + 1 : pwd->pw_shell),
1166 sizeof(tbuf)-1);
1167 tbuf[sizeof(tbuf)-1] = 0;
1168
1169 childArgv[childArgc++] = pwd->pw_shell;
1170 childArgv[childArgc++] = tbuf;
1171 }
1172
1173 childArgv[childArgc++] = NULL;
1174
1175 execvp(childArgv[0], childArgv + 1);
1176
1177 errsv = errno;
1178
1179 if (!strcmp(childArgv[0], "/bin/sh"))
1180 fprintf(stderr, _("login: couldn't exec shell script: %s.\n"),
1181 strerror(errsv));
1182 else
1183 fprintf(stderr, _("login: no shell: %s.\n"), strerror(errsv));
1184
1185 exit(0);
1186 }
1187
1188 void
1189 getloginname(void) {
1190 int ch, cnt, cnt2;
1191 char *p;
1192 static char nbuf[UT_NAMESIZE + 1];
1193
1194 cnt2 = 0;
1195 for (;;) {
1196 cnt = 0;
1197 printf(_("\n%s login: "), thishost); fflush(stdout);
1198 for (p = nbuf; (ch = getchar()) != '\n'; ) {
1199 if (ch == EOF) {
1200 badlogin("EOF");
1201 exit(0);
1202 }
1203 if (p < nbuf + UT_NAMESIZE)
1204 *p++ = ch;
1205
1206 cnt++;
1207 if (cnt > UT_NAMESIZE + 20) {
1208 fprintf(stderr, _("login name much too long.\n"));
1209 badlogin(_("NAME too long"));
1210 exit(0);
1211 }
1212 }
1213 if (p > nbuf) {
1214 if (nbuf[0] == '-')
1215 fprintf(stderr,
1216 _("login names may not start with '-'.\n"));
1217 else {
1218 *p = '\0';
1219 username = nbuf;
1220 break;
1221 }
1222 }
1223
1224 cnt2++;
1225 if (cnt2 > 50) {
1226 fprintf(stderr, _("too many bare linefeeds.\n"));
1227 badlogin(_("EXCESSIVE linefeeds"));
1228 exit(0);
1229 }
1230 }
1231 }
1232
1233 void
1234 timedout(int sig) {
1235 struct termio ti;
1236
1237 fprintf(stderr, _("Login timed out after %d seconds\n"), timeout);
1238
1239 /* reset echo */
1240 ioctl(0, TCGETA, &ti);
1241 ti.c_lflag |= ECHO;
1242 ioctl(0, TCSETA, &ti);
1243 exit(0);
1244 }
1245
1246 int
1247 rootterm(char * ttyn)
1248 #ifndef __linux__
1249 {
1250 struct ttyent *t;
1251
1252 return((t = getttynam(ttyn)) && t->ty_status&TTY_SECURE);
1253 }
1254 #else
1255 {
1256 int fd;
1257 char buf[100],*p;
1258 int cnt, more = 0;
1259
1260 fd = open(SECURETTY, O_RDONLY);
1261 if(fd < 0) return 1;
1262
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
1265 true. */
1266 for(;;) {
1267 p = buf; cnt = 100;
1268 while(--cnt >= 0 && (more = read(fd, p, 1)) == 1 && *p != '\n') p++;
1269 if(more && *p == '\n') {
1270 *p = '\0';
1271 if(!strcmp(buf, ttyn)) {
1272 close(fd);
1273 return 1;
1274 } else
1275 continue;
1276 } else {
1277 close(fd);
1278 return 0;
1279 }
1280 }
1281 }
1282 #endif
1283
1284 jmp_buf motdinterrupt;
1285
1286 void
1287 motd(void) {
1288 int fd, nchars;
1289 void (*oldint)(int);
1290 char tbuf[8192];
1291
1292 if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0)
1293 return;
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);
1299 close(fd);
1300 }
1301
1302 void
1303 sigint(int sig) {
1304 longjmp(motdinterrupt, 1);
1305 }
1306
1307 #ifndef USE_PAM /* PAM takes care of this */
1308 void
1309 checknologin(void) {
1310 register int fd, nchars;
1311 char tbuf[8192];
1312
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);
1316 sleepexit(0);
1317 }
1318 }
1319 #endif
1320
1321 void
1322 dolastlog(int quiet) {
1323 struct lastlog ll;
1324 int fd;
1325
1326 if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
1327 lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET);
1328 if (!quiet) {
1329 if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
1330 ll.ll_time != 0) {
1331 printf(_("Last login: %.*s "),
1332 24-5, (char *)ctime(&ll.ll_time));
1333
1334 if (*ll.ll_host != '\0')
1335 printf(_("from %.*s\n"),
1336 (int)sizeof(ll.ll_host), ll.ll_host);
1337 else
1338 printf(_("on %.*s\n"),
1339 (int)sizeof(ll.ll_line), ll.ll_line);
1340 }
1341 lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET);
1342 }
1343 memset((char *)&ll, 0, sizeof(ll));
1344 time(&ll.ll_time);
1345 strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
1346 ll.ll_line[sizeof(ll.ll_line)-1] = 0;
1347 if (hostname) {
1348 strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
1349 ll.ll_host[sizeof(ll.ll_host)-1] = 0;
1350 }
1351 write(fd, (char *)&ll, sizeof(ll));
1352 close(fd);
1353 }
1354 }
1355
1356 void
1357 badlogin(const char *name)
1358 {
1359 if (failures == 1) {
1360 if (hostname)
1361 syslog(LOG_NOTICE, _("LOGIN FAILURE FROM %s, %s"),
1362 hostname, name);
1363 else
1364 syslog(LOG_NOTICE, _("LOGIN FAILURE ON %s, %s"),
1365 tty, name);
1366 } else {
1367 if (hostname)
1368 syslog(LOG_NOTICE, _("%d LOGIN FAILURES FROM %s, %s"),
1369 failures, hostname, name);
1370 else
1371 syslog(LOG_NOTICE, _("%d LOGIN FAILURES ON %s, %s"),
1372 failures, tty, name);
1373 }
1374 }
1375
1376 #undef UNKNOWN
1377 #define UNKNOWN "su"
1378
1379 #ifndef __linux__
1380 char *
1381 stypeof(char *ttyid) {
1382 struct ttyent *t;
1383
1384 return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
1385 }
1386 #endif
1387
1388 /* should not be called from PAM code... Why? */
1389 void
1390 sleepexit(int eval) {
1391 sleep(SLEEP_EXIT_TIMEOUT);
1392 exit(eval);
1393 }