]> git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/login.c
Imported from util-linux-2.9i 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 has 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
56 /*
57 * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
58 * All rights reserved.
59 *
60 * Redistribution and use in source and binary forms are permitted
61 * provided that the above copyright notice and this paragraph are
62 * duplicated in all such forms and that any documentation,
63 * advertising materials, and other materials related to such
64 * distribution and use acknowledge that the software was developed
65 * by the University of California, Berkeley. The name of the
66 * University may not be used to endorse or promote products derived
67 * from this software without specific prior written permission.
68 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
69 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
70 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
71 */
72
73 /*
74 * login [ name ]
75 * login -h hostname (for telnetd, etc.)
76 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
77 */
78
79 /* #define TESTING */
80
81 #ifdef TESTING
82 #include "param.h"
83 #else
84 #include <sys/param.h>
85 #endif
86
87 #include <stdio.h>
88 #include <ctype.h>
89 #include <unistd.h>
90 #include <getopt.h>
91 #include <memory.h>
92 #include <time.h>
93 #include <sys/stat.h>
94 #include <sys/time.h>
95 #include <sys/resource.h>
96 #include <sys/file.h>
97 #include <termios.h>
98 #include <string.h>
99 #define index strchr
100 #define rindex strrchr
101 #include <sys/ioctl.h>
102 #include <sys/wait.h>
103 #include <signal.h>
104 #include <errno.h>
105 #include <grp.h>
106 #include <pwd.h>
107 #include <utmp.h>
108 #include <setjmp.h>
109 #include <stdlib.h>
110 #include <string.h>
111 #include <sys/syslog.h>
112 #include <sys/sysmacros.h>
113 #include <netdb.h>
114 #include "my_crypt.h"
115
116 #ifdef __linux__
117 # include <sys/sysmacros.h>
118 # include <linux/major.h>
119 #endif
120
121 #ifdef TESTING
122 # include "utmp.h"
123 #else
124 # include <utmp.h>
125 #endif
126
127 #ifdef SHADOW_PWD
128 # include <shadow.h>
129 #endif
130
131 #ifdef USE_PAM
132 # include <security/pam_appl.h>
133 # include <security/pam_misc.h>
134 # define PAM_MAX_LOGIN_TRIES 3
135 # define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \
136 fprintf(stderr,"\n%s\n",pam_strerror(pamh, retcode)); \
137 syslog(LOG_ERR,"%s",pam_strerror(pamh, retcode)); \
138 pam_end(pamh, retcode); exit(1); \
139 }
140 # define PAM_END { retcode = pam_close_session(pamh,0); \
141 pam_end(pamh,retcode); }
142 #endif
143
144 #ifndef __linux__
145 # include <tzfile.h>
146 #endif
147 #include <lastlog.h>
148
149 #define SLEEP_EXIT_TIMEOUT 5
150
151 #ifdef __linux__
152 #define DO_PS_FIDDLING
153 #endif
154
155 #ifdef DO_PS_FIDDLING
156 #include "setproctitle.h"
157 #endif
158
159 #if 0
160 /* from before we had a lastlog.h file in linux */
161 struct lastlog
162 { long ll_time;
163 char ll_line[12];
164 char ll_host[16];
165 };
166 #endif
167
168 #include "pathnames.h"
169
170 #define P_(s) ()
171 void opentty P_((const char *tty));
172 void getloginname P_((void));
173 void timedout P_((void));
174 int rootterm P_((char *ttyn));
175 void motd P_((void));
176 void sigint P_((void));
177 void checknologin P_((void));
178 void dolastlog P_((int quiet));
179 void badlogin P_((const char *name));
180 char *stypeof P_((char *ttyid));
181 void checktty P_((char *user, char *tty, struct passwd *pwd));
182 void sleepexit P_((int eval));
183 #ifdef CRYPTOCARD
184 int cryptocard P_((void));
185 #endif
186 #undef P_
187
188 #ifdef KERBEROS
189 #include <kerberos/krb.h>
190 #include <sys/termios.h>
191 char realm[REALM_SZ];
192 int kerror = KSUCCESS, notickets = 1;
193 #endif
194
195 #ifdef USE_TTY_GROUP
196 # define TTY_MODE 0620
197 #else
198 # define TTY_MODE 0600
199 #endif
200
201 #define TTYGRPNAME "tty" /* name of group to own ttys */
202 /**# define TTYGRPNAME "other" **/
203
204 #ifndef MAXPATHLEN
205 # define MAXPATHLEN 1024
206 #endif
207
208 /*
209 * This bounds the time given to login. Not a define so it can
210 * be patched on machines where it's too small.
211 */
212 #ifndef __linux__
213 int timeout = 300;
214 #else
215 int timeout = 60;
216 #endif
217
218 struct passwd *pwd;
219 int failures = 1;
220 char term[64], *hostname, *username, *tty;
221 struct hostent hostaddress;
222 char thishost[100];
223
224 #ifndef __linux__
225 struct sgttyb sgttyb;
226 struct tchars tc = {
227 CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK
228 };
229 struct ltchars ltc = {
230 CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT
231 };
232 #endif
233
234 const char *months[] =
235 { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
236 "Sep", "Oct", "Nov", "Dec" };
237
238 /* provided by Linus Torvalds 16-Feb-93 */
239 void
240 opentty(const char * tty)
241 {
242 int i;
243 int fd = open(tty, O_RDWR);
244
245 for (i = 0 ; i < fd ; i++)
246 close(i);
247 for (i = 0 ; i < 3 ; i++)
248 dup2(fd, i);
249 if (fd >= 3)
250 close(fd);
251 }
252
253 /* true if the filedescriptor fd is a console tty, very Linux specific */
254 static int
255 consoletty(int fd)
256 {
257 #ifdef __linux__
258 struct stat stb;
259
260 if ((fstat(fd, &stb) >= 0)
261 && (major(stb.st_rdev) == TTY_MAJOR)
262 && (minor(stb.st_rdev) < 64)) {
263 return 1;
264 }
265 #endif
266 return 0;
267 }
268
269
270 int
271 main(int argc, char **argv)
272 {
273 extern int errno, optind;
274 extern char *optarg, **environ;
275 struct group *gr;
276 register int ch;
277 register char *p;
278 int ask, fflag, hflag, pflag, cnt;
279 int quietlog, passwd_req;
280 char *domain, *ttyn;
281 char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
282 char *ctime(), *ttyname(), *stypeof();
283 time_t time();
284 void timedout();
285 char *termenv;
286 char vcsn[20], vcsan[20];
287 char * childArgv[10];
288 char * buff;
289 int childArgc = 0;
290 #ifdef USE_PAM
291 int retcode;
292 pam_handle_t *pamh = NULL;
293 struct pam_conv conv = { misc_conv, NULL };
294 pid_t childPid;
295 void (*oldSigHandler) ();
296 #else
297 char *salt, *pp;
298 #endif
299 #ifndef __linux__
300 int ioctlval;
301 #endif
302
303 signal(SIGALRM, timedout);
304 alarm((unsigned int)timeout);
305 signal(SIGQUIT, SIG_IGN);
306 signal(SIGINT, SIG_IGN);
307
308 setpriority(PRIO_PROCESS, 0, 0);
309 #ifdef HAVE_QUOTA
310 quota(Q_SETUID, 0, 0, 0);
311 #endif
312 #ifdef DO_PS_FIDDLING
313 initproctitle(argc, argv);
314 #endif
315
316 /*
317 * -p is used by getty to tell login not to destroy the environment
318 * -f is used to skip a second login authentication
319 * -h is used by other servers to pass the name of the remote
320 * host to login so that it may be placed in utmp and wtmp
321 */
322 gethostname(tbuf, sizeof(tbuf));
323 strncpy(thishost, tbuf, sizeof(thishost)-1);
324 thishost[sizeof(thishost)-1] = 0;
325 domain = index(tbuf, '.');
326
327 username = tty = hostname = NULL;
328 fflag = hflag = pflag = 0;
329 passwd_req = 1;
330
331 while ((ch = getopt(argc, argv, "fh:p")) != EOF)
332 switch (ch) {
333 case 'f':
334 fflag = 1;
335 break;
336
337 case 'h':
338 if (getuid()) {
339 fprintf(stderr,
340 "login: -h for super-user only.\n");
341 exit(1);
342 }
343 hflag = 1;
344 if (domain && (p = index(optarg, '.')) &&
345 strcasecmp(p, domain) == 0)
346 *p = 0;
347 hostname = optarg;
348 {
349 struct hostent *he = gethostbyname(hostname);
350 if (he) {
351 memcpy(&hostaddress, he, sizeof(hostaddress));
352 } else {
353 memset(&hostaddress, 0, sizeof(hostaddress));
354 }
355 }
356 break;
357
358 case 'p':
359 pflag = 1;
360 break;
361
362 case '?':
363 default:
364 fprintf(stderr,
365 "usage: login [-fp] [username]\n");
366 exit(1);
367 }
368 argc -= optind;
369 argv += optind;
370 if (*argv) {
371 char *p = *argv;
372 username = strdup(p);
373 ask = 0;
374 /* wipe name - some people mistype their password here */
375 /* (of course we are too late, but perhaps this helps a little ..) */
376 while(*p)
377 *p++ = ' ';
378 } else
379 ask = 1;
380
381 #ifndef __linux__
382 ioctlval = 0;
383 ioctl(0, TIOCLSET, &ioctlval);
384 ioctl(0, TIOCNXCL, 0);
385 fcntl(0, F_SETFL, ioctlval);
386 ioctl(0, TIOCGETP, &sgttyb);
387 sgttyb.sg_erase = CERASE;
388 sgttyb.sg_kill = CKILL;
389 ioctl(0, TIOCSLTC, &ltc);
390 ioctl(0, TIOCSETC, &tc);
391 ioctl(0, TIOCSETP, &sgttyb);
392
393 /*
394 * Be sure that we're in
395 * blocking mode!!!
396 * This is really for HPUX
397 */
398 ioctlval = 0;
399 ioctl(0, FIOSNBIO, &ioctlval);
400 #endif /* ! __linux__ */
401
402 for (cnt = getdtablesize(); cnt > 2; cnt--)
403 close(cnt);
404
405 ttyn = ttyname(0);
406 if (ttyn == NULL || *ttyn == '\0') {
407 /* no snprintf required - see definition of tname */
408 sprintf(tname, "%s??", _PATH_TTY);
409 ttyn = tname;
410 }
411
412 /* find names of Virtual Console devices, for later mode change */
413 {
414 char *p = ttyn;
415 /* find number of tty */
416 while (*p && !isdigit(*p)) p++;
417
418 strcpy(vcsn, "/dev/vcs"); strcat(vcsn, p);
419 strcpy(vcsan, "/dev/vcsa"); strcat(vcsan, p);
420 }
421
422 setpgrp();
423
424 {
425 struct termios tt, ttt;
426
427 tcgetattr(0, &tt);
428 ttt = tt;
429 ttt.c_cflag &= ~HUPCL;
430
431 if((chown(ttyn, 0, 0) == 0) && (chmod(ttyn, 0622) == 0)) {
432 tcsetattr(0,TCSAFLUSH,&ttt);
433 signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */
434 vhangup();
435 signal(SIGHUP, SIG_DFL);
436 }
437
438 setsid();
439
440 /* re-open stdin,stdout,stderr after vhangup() closed them */
441 /* if it did, after 0.99.5 it doesn't! */
442 opentty(ttyn);
443 tcsetattr(0,TCSAFLUSH,&tt);
444 }
445
446 if ((tty = rindex(ttyn, '/')))
447 ++tty;
448 else
449 tty = ttyn;
450
451 openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
452
453 #ifdef USE_PAM
454 /* username is initialized to NULL
455 and if specified on the command line it is set.
456 Therefore, we are safe not setting it to anything
457 */
458
459 retcode = pam_start("login",username, &conv, &pamh);
460 if(retcode != PAM_SUCCESS) {
461 fprintf(stderr,"login: PAM Failure, aborting: %s\n",
462 pam_strerror(pamh, retcode));
463 syslog(LOG_ERR,"Couldn't initialize PAM: %s", pam_strerror(pamh, retcode));
464 exit(99);
465 }
466 /* hostname & tty are either set to NULL or their correct values,
467 depending on how much we know */
468 retcode = pam_set_item(pamh, PAM_RHOST, hostname);
469 PAM_FAIL_CHECK;
470 retcode = pam_set_item(pamh, PAM_TTY, tty);
471 PAM_FAIL_CHECK;
472 /* if fflag == 1, then the user has already been authenticated */
473 if (fflag && (getuid() == 0))
474 passwd_req = 0;
475 else
476 passwd_req = 1;
477
478 if(passwd_req == 1) {
479 int failcount=0;
480
481 /* there may be better ways to deal with some of these
482 conditions, but at least this way I don't think we'll
483 be giving away information... */
484 /* Perhaps someday we can trust that all PAM modules will
485 pay attention to failure count and get rid of MAX_LOGIN_TRIES? */
486
487 retcode = pam_authenticate(pamh, 0);
488 while((failcount++ < PAM_MAX_LOGIN_TRIES) &&
489 ((retcode == PAM_AUTH_ERR) ||
490 (retcode == PAM_USER_UNKNOWN) ||
491 (retcode == PAM_CRED_INSUFFICIENT) ||
492 (retcode == PAM_AUTHINFO_UNAVAIL))) {
493 pam_get_item(pamh, PAM_USER, (const void **) &username);
494 syslog(LOG_NOTICE,"FAILED LOGIN %d FROM %s FOR %s, %s",
495 failcount, hostname, username, pam_strerror(pamh, retcode));
496 fprintf(stderr,"Login incorrect\n\n");
497 pam_set_item(pamh,PAM_USER,NULL);
498 retcode = pam_authenticate(pamh, 0);
499 }
500
501 if (retcode != PAM_SUCCESS) {
502 pam_get_item(pamh, PAM_USER, (const void **) &username);
503
504 if (retcode == PAM_MAXTRIES)
505 syslog(LOG_NOTICE,"TOO MANY LOGIN TRIES (%d) FROM %s FOR "
506 "%s, %s", failcount, hostname, username,
507 pam_strerror(pamh, retcode));
508 else
509 syslog(LOG_NOTICE,"FAILED LOGIN SESSION FROM %s FOR %s, %s",
510 hostname, username, pam_strerror(pamh, retcode));
511
512 fprintf(stderr,"\nLogin incorrect\n");
513 pam_end(pamh, retcode);
514 exit(0);
515 }
516
517 retcode = pam_acct_mgmt(pamh, 0);
518
519 if(retcode == PAM_NEW_AUTHTOK_REQD) {
520 retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
521 }
522
523 PAM_FAIL_CHECK;
524 }
525
526 /* Grab the user information out of the password file for future usage
527 First get the username that we are actually using, though.
528 */
529 retcode = pam_get_item(pamh, PAM_USER, (const void **) &username);
530 setpwent();
531 pwd = getpwnam(username);
532 if (pwd) initgroups(username, pwd->pw_gid);
533
534 retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
535 PAM_FAIL_CHECK;
536
537 retcode = pam_open_session(pamh, 0);
538 PAM_FAIL_CHECK;
539
540 #else /* ! USE_PAM */
541
542 for (cnt = 0;; ask = 1) {
543 # ifndef __linux__
544 ioctlval = 0;
545 ioctl(0, TIOCSETD, &ioctlval);
546 # endif
547
548 if (ask) {
549 fflag = 0;
550 getloginname();
551 }
552
553 /* Dirty patch to fix a gigantic security hole when using
554 yellow pages. This problem should be solved by the
555 libraries, and not by programs, but this must be fixed
556 urgently! If the first char of the username is '+', we
557 avoid login success.
558 Feb 95 <alvaro@etsit.upm.es> */
559
560 if (username[0] == '+') {
561 puts("Illegal username");
562 badlogin(username);
563 sleepexit(1);
564 }
565
566 /* (void)strcpy(tbuf, username); why was this here? */
567 if ((pwd = getpwnam(username))) {
568 # ifdef SHADOW_PWD
569 struct spwd *sp;
570
571 if ((sp = getspnam(username)))
572 pwd->pw_passwd = sp->sp_pwdp;
573 # endif
574 salt = pwd->pw_passwd;
575 } else
576 salt = "xx";
577
578 if (pwd) {
579 initgroups(username, pwd->pw_gid);
580 checktty(username, tty, pwd); /* in checktty.c */
581 }
582
583 /* if user not super-user, check for disabled logins */
584 if (pwd == NULL || pwd->pw_uid)
585 checknologin();
586
587 /*
588 * Disallow automatic login to root; if not invoked by
589 * root, disallow if the uid's differ.
590 */
591 if (fflag && pwd) {
592 int uid = getuid();
593
594 passwd_req = pwd->pw_uid == 0 ||
595 (uid && uid != pwd->pw_uid);
596 }
597
598 /*
599 * If trying to log in as root, but with insecure terminal,
600 * refuse the login attempt.
601 */
602 if (pwd && pwd->pw_uid == 0 && !rootterm(tty)) {
603 fprintf(stderr,
604 "%s login refused on this terminal.\n",
605 pwd->pw_name);
606
607 if (hostname)
608 syslog(LOG_NOTICE,
609 "LOGIN %s REFUSED FROM %s ON TTY %s",
610 pwd->pw_name, hostname, tty);
611 else
612 syslog(LOG_NOTICE,
613 "LOGIN %s REFUSED ON TTY %s",
614 pwd->pw_name, tty);
615 continue;
616 }
617
618 /*
619 * If no pre-authentication and a password exists
620 * for this user, prompt for one and verify it.
621 */
622 if (!passwd_req || (pwd && !*pwd->pw_passwd))
623 break;
624
625 setpriority(PRIO_PROCESS, 0, -4);
626 pp = getpass("Password: ");
627
628 # ifdef CRYPTOCARD
629 if (strncmp(pp, "CRYPTO", 6) == 0) {
630 if (pwd && cryptocard()) break;
631 }
632 # endif /* CRYPTOCARD */
633
634 p = crypt(pp, salt);
635 setpriority(PRIO_PROCESS, 0, 0);
636
637 # ifdef KERBEROS
638 /*
639 * If not present in pw file, act as we normally would.
640 * If we aren't Kerberos-authenticated, try the normal
641 * pw file for a password. If that's ok, log the user
642 * in without issueing any tickets.
643 */
644
645 if (pwd && !krb_get_lrealm(realm,1)) {
646 /*
647 * get TGT for local realm; be careful about uid's
648 * here for ticket file ownership
649 */
650 setreuid(geteuid(),pwd->pw_uid);
651 kerror = krb_get_pw_in_tkt(pwd->pw_name, "", realm,
652 "krbtgt", realm, DEFAULT_TKT_LIFE, pp);
653 setuid(0);
654 if (kerror == INTK_OK) {
655 memset(pp, 0, strlen(pp));
656 notickets = 0; /* user got ticket */
657 break;
658 }
659 }
660 # endif /* KERBEROS */
661 memset(pp, 0, strlen(pp));
662 if (pwd && !strcmp(p, pwd->pw_passwd))
663 break;
664
665 printf("Login incorrect\n");
666 badlogin(username); /* log ALL bad logins */
667 failures++;
668
669 /* we allow 10 tries, but after 3 we start backing off */
670 if (++cnt > 3) {
671 if (cnt >= 10) {
672 sleepexit(1);
673 }
674 sleep((unsigned int)((cnt - 3) * 5));
675 }
676 }
677 #endif /* !USE_PAM */
678
679 /* committed to login -- turn off timeout */
680 alarm((unsigned int)0);
681
682 #ifdef HAVE_QUOTA
683 if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0 && errno != EINVAL) {
684 switch(errno) {
685 case EUSERS:
686 fprintf(stderr,
687 "Too many users logged on already.\nTry again later.\n");
688 break;
689 case EPROCLIM:
690 fprintf(stderr,
691 "You have too many processes running.\n");
692 break;
693 default:
694 perror("quota (Q_SETUID)");
695 }
696 sleepexit(0);
697 }
698 #endif
699
700 /* paranoia... */
701 #ifdef SHADOW_PWD
702 endspent();
703 #endif
704 endpwent();
705
706 /* This requires some explanation: As root we may not be able to
707 read the directory of the user if it is on an NFS mounted
708 filesystem. We temporarily set our effective uid to the user-uid
709 making sure that we keep root privs. in the real uid.
710
711 A portable solution would require a fork(), but we rely on Linux
712 having the BSD setreuid() */
713
714 {
715 char tmpstr[MAXPATHLEN];
716 uid_t ruid = getuid();
717 gid_t egid = getegid();
718
719 /* avoid snprintf - old systems do not have it, or worse,
720 have a libc in which snprintf is the same as sprintf */
721 if (strlen(pwd->pw_dir) + sizeof(_PATH_HUSHLOGIN) + 2 > MAXPATHLEN)
722 quietlog = 0;
723 else {
724 sprintf(tmpstr, "%s/%s", pwd->pw_dir, _PATH_HUSHLOGIN);
725 setregid(-1, pwd->pw_gid);
726 setreuid(0, pwd->pw_uid);
727 quietlog = (access(tmpstr, R_OK) == 0);
728 setuid(0); /* setreuid doesn't do it alone! */
729 setreuid(ruid, 0);
730 setregid(-1, egid);
731 }
732 }
733
734 #ifndef __linux__
735 # ifdef KERBEROS
736 if (notickets && !quietlog)
737 printf("Warning: no Kerberos tickets issued\n");
738 # endif
739
740 # ifndef USE_PAM /* PAM does all of this for us */
741 # define TWOWEEKS (14*24*60*60)
742 if (pwd->pw_change || pwd->pw_expire) {
743 struct timeval tp;
744
745 gettimeofday(&tp, (struct timezone *)NULL);
746
747 if (pwd->pw_change) {
748 if (tp.tv_sec >= pwd->pw_change) {
749 printf("Sorry -- your password has expired.\n");
750 sleepexit(1);
751 }
752 else if (tp.tv_sec - pwd->pw_change < TWOWEEKS && !quietlog) {
753 struct tm *ttp;
754 ttp = localtime(&pwd->pw_change);
755 printf("Warning: your password expires on %s %d, %d\n",
756 months[ttp->tm_mon], ttp->tm_mday,
757 TM_YEAR_BASE + ttp->tm_year);
758 }
759 }
760
761 if (pwd->pw_expire) {
762 if (tp.tv_sec >= pwd->pw_expire) {
763 printf("Sorry -- your account has expired.\n");
764 sleepexit(1);
765 }
766 else if (tp.tv_sec - pwd->pw_expire < TWOWEEKS && !quietlog) {
767 struct tm *ttp;
768 ttp = localtime(&pwd->pw_expire);
769 printf("Warning: your account expires on %s %d, %d\n",
770 months[ttp->tm_mon], ttp->tm_mday,
771 TM_YEAR_BASE + ttp->tm_year);
772 }
773 }
774 }
775 # endif /* !USE_PAM */
776
777 /* nothing else left to fail -- really log in */
778 {
779 struct utmp utmp;
780
781 memset((char *)&utmp, 0, sizeof(utmp));
782 time(&utmp.ut_time);
783 strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
784 /* ut_name may legally be non-null-terminated */
785 if (hostname) {
786 strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
787 utmp.ut_host[sizeof(utmp.ut_host)-1] = 0;
788 }
789 strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
790 utmp.ut_line[sizeof(utmp.ut_line)-1] = 0;
791 login(&utmp);
792 }
793 #else /* __linux__ defined */
794 /* for linux, write entries in utmp and wtmp */
795 {
796 struct utmp ut;
797 int wtmp;
798 struct utmp *utp;
799 time_t t;
800 pid_t mypid = getpid();
801
802 utmpname(_PATH_UTMP);
803 setutent();
804
805 /* Find mypid in utmp.
806 login sometimes overwrites the runlevel entry in /var/run/utmp,
807 confusing sysvinit. I added a test for the entry type, and the problem
808 was gone. (In a runlevel entry, st_pid is not really a pid but some number
809 calculated from the previous and current runlevel).
810 Michael Riepe <michael@stud.uni-hannover.de>
811 */
812 while ((utp = getutent()))
813 if (utp->ut_pid == mypid
814 && utp->ut_type >= INIT_PROCESS
815 && utp->ut_type <= DEAD_PROCESS)
816 break;
817
818 if (utp) {
819 memcpy(&ut, utp, sizeof(ut));
820 } else {
821 /* some gettys/telnetds don't initialize utmp... */
822 memset(&ut, 0, sizeof(ut));
823 }
824 /* endutent(); superfluous, error for glibc */
825
826 if (ut.ut_id[0] == 0)
827 strncpy(ut.ut_id, ttyn + 8, sizeof(ut.ut_id));
828
829 strncpy(ut.ut_user, username, sizeof(ut.ut_user));
830 strncpy(ut.ut_line, ttyn + 5, sizeof(ut.ut_line));
831 ut.ut_line[sizeof(ut.ut_line)-1] = 0;
832 time(&t);
833 ut.ut_time = t; /* ut_time is not always a time_t */
834 /* (we might test #ifdef _HAVE_UT_TV ) */
835 ut.ut_type = USER_PROCESS;
836 ut.ut_pid = mypid;
837 if (hostname) {
838 strncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
839 ut.ut_host[sizeof(ut.ut_host)-1] = 0;
840 if (hostaddress.h_addr_list)
841 memcpy(&ut.ut_addr, hostaddress.h_addr_list[0],
842 sizeof(ut.ut_addr));
843 }
844
845 pututline(&ut);
846 endutent();
847
848 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
849 updwtmp(_PATH_WTMP, &ut);
850 #else
851 #if 0
852 /* The O_APPEND open() flag should be enough to guarantee
853 atomic writes at end of file. */
854 if((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
855 write(wtmp, (char *)&ut, sizeof(ut));
856 close(wtmp);
857 }
858 #else
859 /* Probably all this locking below is just nonsense,
860 and the short version is OK as well. */
861 {
862 int lf;
863 if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) {
864 flock(lf, LOCK_EX);
865 if ((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
866 write(wtmp, (char *)&ut, sizeof(ut));
867 close(wtmp);
868 }
869 flock(lf, LOCK_UN);
870 close(lf);
871 }
872 }
873 #endif
874 #endif /* __GLIBC__ */
875 }
876 #endif /* __linux__ */
877
878 dolastlog(quietlog);
879
880 #ifndef __linux__
881 if (!hflag) { /* XXX */
882 static struct winsize win = { 0, 0, 0, 0 };
883
884 ioctl(0, TIOCSWINSZ, &win);
885 }
886 #endif
887 chown(ttyn, pwd->pw_uid,
888 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
889 chmod(ttyn, TTY_MODE);
890
891 /* if tty is one of the VC's then change owner and mode of the
892 special /dev/vcs devices as well */
893 if (consoletty(0)) {
894 chown(vcsn, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid));
895 chown(vcsan, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid));
896 chmod(vcsn, TTY_MODE);
897 chmod(vcsan, TTY_MODE);
898 }
899
900 setgid(pwd->pw_gid);
901
902 #ifdef HAVE_QUOTA
903 quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0);
904 #endif
905
906 if (*pwd->pw_shell == '\0')
907 pwd->pw_shell = _PATH_BSHELL;
908 #ifndef __linux__
909 /* turn on new line discipline for the csh */
910 else if (!strcmp(pwd->pw_shell, _PATH_CSHELL)) {
911 ioctlval = NTTYDISC;
912 ioctl(0, TIOCSETD, &ioctlval);
913 }
914 #endif
915
916 /* preserve TERM even without -p flag */
917 {
918 char *ep;
919
920 if(!((ep = getenv("TERM")) && (termenv = strdup(ep))))
921 termenv = "dumb";
922 }
923
924 /* destroy environment unless user has requested preservation */
925 if (!pflag)
926 {
927 environ = (char**)malloc(sizeof(char*));
928 memset(environ, 0, sizeof(char*));
929 }
930
931 #ifndef __linux__
932 setenv("HOME", pwd->pw_dir, 1);
933 setenv("SHELL", pwd->pw_shell, 1);
934 if (term[0] == '\0') {
935 strncpy(term, stypeof(tty), sizeof(term));
936 term[sizeof(term)-1] = 0;
937 }
938 setenv("TERM", term, 0);
939 setenv("USER", pwd->pw_name, 1);
940 setenv("PATH", _PATH_DEFPATH, 0);
941 #else
942 setenv("HOME", pwd->pw_dir, 0); /* legal to override */
943 if(pwd->pw_uid)
944 setenv("PATH", _PATH_DEFPATH, 1);
945 else
946 setenv("PATH", _PATH_DEFPATH_ROOT, 1);
947
948 setenv("SHELL", pwd->pw_shell, 1);
949 setenv("TERM", termenv, 1);
950
951 /* mailx will give a funny error msg if you forget this one */
952 {
953 char tmp[MAXPATHLEN];
954 /* avoid snprintf */
955 if (sizeof(_PATH_MAILDIR) + strlen(pwd->pw_name) + 1 < MAXPATHLEN) {
956 sprintf(tmp, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
957 setenv("MAIL",tmp,0);
958 }
959 }
960
961 /* LOGNAME is not documented in login(1) but
962 HP-UX 6.5 does it. We'll not allow modifying it.
963 */
964 setenv("LOGNAME", pwd->pw_name, 1);
965 #endif
966
967 #ifdef USE_PAM
968 {
969 int i;
970 const char * const * env;
971
972 env = (const char * const *)pam_getenvlist(pamh);
973
974 if (env != NULL) {
975 for (i=0; env[i++]; ) {
976 putenv(env[i-1]);
977 /* D(("env[%d] = %s", i-1,env[i-1])); */
978 }
979 }
980 }
981 #endif
982
983 #ifdef DO_PS_FIDDLING
984 setproctitle("login", username);
985 #endif
986
987 if (tty[sizeof("tty")-1] == 'S')
988 syslog(LOG_INFO, "DIALUP AT %s BY %s", tty, pwd->pw_name);
989
990 /* allow tracking of good logins.
991 -steve philp (sphilp@mail.alliance.net) */
992
993 if (pwd->pw_uid == 0) {
994 if (hostname)
995 syslog(LOG_NOTICE, "ROOT LOGIN ON %s FROM %s",
996 tty, hostname);
997 else
998 syslog(LOG_NOTICE, "ROOT LOGIN ON %s", tty);
999 } else {
1000 if (hostname)
1001 syslog(LOG_INFO, "LOGIN ON %s BY %s FROM %s", tty,
1002 pwd->pw_name, hostname);
1003 else
1004 syslog(LOG_INFO, "LOGIN ON %s BY %s", tty,
1005 pwd->pw_name);
1006 }
1007
1008 if (!quietlog) {
1009 struct stat st;
1010 char *mail;
1011
1012 motd();
1013 mail = getenv("MAIL");
1014 if (mail && stat(mail, &st) == 0 && st.st_size != 0) {
1015 printf("You have %smail.\n",
1016 (st.st_mtime > st.st_atime) ? "new " : "");
1017 }
1018 }
1019
1020 signal(SIGALRM, SIG_DFL);
1021 signal(SIGQUIT, SIG_DFL);
1022 signal(SIGTSTP, SIG_IGN);
1023 signal(SIGHUP, SIG_DFL);
1024
1025 #ifdef USE_PAM
1026 /* We must fork before setuid() because we need to call
1027 * pam_close_session() as root.
1028 */
1029 signal(SIGINT, SIG_IGN);
1030 childPid = fork();
1031 if (childPid < 0) {
1032 /* error in fork() */
1033 fprintf(stderr,"login: failure forking: %s", strerror(errno));
1034 PAM_END;
1035 exit(0);
1036 } else if (childPid) {
1037 /* parent - wait for child to finish, then cleanup session */
1038 wait(NULL);
1039 PAM_END;
1040 exit(0);
1041 }
1042 /* child */
1043 #endif
1044 signal(SIGINT, SIG_DFL);
1045
1046 /* discard permissions last so can't get killed and drop core */
1047 if(setuid(pwd->pw_uid) < 0 && pwd->pw_uid) {
1048 syslog(LOG_ALERT, "setuid() failed");
1049 exit(1);
1050 }
1051
1052 /* wait until here to change directory! */
1053 if (chdir(pwd->pw_dir) < 0) {
1054 printf("No directory %s!\n", pwd->pw_dir);
1055 if (chdir("/"))
1056 exit(0);
1057 pwd->pw_dir = "/";
1058 printf("Logging in with home = \"/\".\n");
1059 }
1060
1061 /* if the shell field has a space: treat it like a shell script */
1062 if (strchr(pwd->pw_shell, ' ')) {
1063 buff = malloc(strlen(pwd->pw_shell) + 6);
1064
1065 if (!buff) {
1066 fprintf(stderr, "login: no memory for shell script.\n");
1067 exit(0);
1068 }
1069
1070 strcpy(buff, "exec ");
1071 strcat(buff, pwd->pw_shell);
1072 childArgv[childArgc++] = "/bin/sh";
1073 childArgv[childArgc++] = "-sh";
1074 childArgv[childArgc++] = "-c";
1075 childArgv[childArgc++] = buff;
1076 } else {
1077 tbuf[0] = '-';
1078 strncpy(tbuf + 1, ((p = rindex(pwd->pw_shell, '/')) ?
1079 p + 1 : pwd->pw_shell),
1080 sizeof(tbuf)-1);
1081 tbuf[sizeof(tbuf)-1] = 0;
1082
1083 childArgv[childArgc++] = pwd->pw_shell;
1084 childArgv[childArgc++] = tbuf;
1085 }
1086
1087 childArgv[childArgc++] = NULL;
1088
1089 execvp(childArgv[0], childArgv + 1);
1090
1091 if (!strcmp(childArgv[0], "/bin/sh"))
1092 fprintf(stderr, "login: couldn't exec shell script: %s.\n",
1093 strerror(errno));
1094 else
1095 fprintf(stderr, "login: no shell: %s.\n", strerror(errno));
1096
1097 exit(0);
1098 }
1099
1100 void
1101 getloginname()
1102 {
1103 register int ch;
1104 register char *p;
1105 static char nbuf[UT_NAMESIZE + 1];
1106 int cnt, cnt2;
1107
1108 cnt2 = 0;
1109 for (;;) {
1110 cnt = 0;
1111 printf("\n%s login: ", thishost); fflush(stdout);
1112 for (p = nbuf; (ch = getchar()) != '\n'; ) {
1113 if (ch == EOF) {
1114 badlogin("EOF");
1115 exit(0);
1116 }
1117 if (p < nbuf + UT_NAMESIZE)
1118 *p++ = ch;
1119
1120 cnt++;
1121 if (cnt > UT_NAMESIZE + 20) {
1122 fprintf(stderr, "login name much too long.\n");
1123 badlogin("NAME too long");
1124 exit(0);
1125 }
1126 }
1127 if (p > nbuf) {
1128 if (nbuf[0] == '-')
1129 fprintf(stderr,
1130 "login names may not start with '-'.\n");
1131 else {
1132 *p = '\0';
1133 username = nbuf;
1134 break;
1135 }
1136 }
1137
1138 cnt2++;
1139 if (cnt2 > 50) {
1140 fprintf(stderr, "too many bare linefeeds.\n");
1141 badlogin("EXCESSIVE linefeeds");
1142 exit(0);
1143 }
1144 }
1145 }
1146
1147 void
1148 timedout()
1149 {
1150 struct termio ti;
1151
1152 fprintf(stderr, "Login timed out after %d seconds\n", timeout);
1153
1154 /* reset echo */
1155 ioctl(0, TCGETA, &ti);
1156 ti.c_lflag |= ECHO;
1157 ioctl(0, TCSETA, &ti);
1158 exit(0);
1159 }
1160
1161 int
1162 rootterm(ttyn)
1163 char *ttyn;
1164 #ifndef __linux__
1165 {
1166 struct ttyent *t;
1167
1168 return((t = getttynam(ttyn)) && t->ty_status&TTY_SECURE);
1169 }
1170 #else
1171 {
1172 int fd;
1173 char buf[100],*p;
1174 int cnt, more;
1175
1176 fd = open(SECURETTY, O_RDONLY);
1177 if(fd < 0) return 1;
1178
1179 /* read each line in /etc/securetty, if a line matches our ttyline
1180 then root is allowed to login on this tty, and we should return
1181 true. */
1182 for(;;) {
1183 p = buf; cnt = 100;
1184 while(--cnt >= 0 && (more = read(fd, p, 1)) == 1 && *p != '\n') p++;
1185 if(more && *p == '\n') {
1186 *p = '\0';
1187 if(!strcmp(buf, ttyn)) {
1188 close(fd);
1189 return 1;
1190 } else
1191 continue;
1192 } else {
1193 close(fd);
1194 return 0;
1195 }
1196 }
1197 }
1198 #endif
1199
1200 jmp_buf motdinterrupt;
1201
1202 void
1203 motd()
1204 {
1205 register int fd, nchars;
1206 void (*oldint)(), sigint();
1207 char tbuf[8192];
1208
1209 if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0)
1210 return;
1211 oldint = signal(SIGINT, sigint);
1212 if (setjmp(motdinterrupt) == 0)
1213 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
1214 write(fileno(stdout), tbuf, nchars);
1215 signal(SIGINT, oldint);
1216 close(fd);
1217 }
1218
1219 void
1220 sigint()
1221 {
1222 longjmp(motdinterrupt, 1);
1223 }
1224
1225 #ifndef USE_PAM /* PAM takes care of this */
1226 void
1227 checknologin()
1228 {
1229 register int fd, nchars;
1230 char tbuf[8192];
1231
1232 if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
1233 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
1234 write(fileno(stdout), tbuf, nchars);
1235 sleepexit(0);
1236 }
1237 }
1238 #endif
1239
1240 void
1241 dolastlog(quiet)
1242 int quiet;
1243 {
1244 struct lastlog ll;
1245 int fd;
1246
1247 if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
1248 lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET);
1249 if (!quiet) {
1250 if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
1251 ll.ll_time != 0) {
1252 printf("Last login: %.*s ",
1253 24-5, (char *)ctime(&ll.ll_time));
1254
1255 if (*ll.ll_host != '\0')
1256 printf("from %.*s\n",
1257 (int)sizeof(ll.ll_host), ll.ll_host);
1258 else
1259 printf("on %.*s\n",
1260 (int)sizeof(ll.ll_line), ll.ll_line);
1261 }
1262 lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET);
1263 }
1264 memset((char *)&ll, 0, sizeof(ll));
1265 time(&ll.ll_time);
1266 strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
1267 ll.ll_line[sizeof(ll.ll_line)-1] = 0;
1268 if (hostname) {
1269 strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
1270 ll.ll_host[sizeof(ll.ll_host)-1] = 0;
1271 }
1272 write(fd, (char *)&ll, sizeof(ll));
1273 close(fd);
1274 }
1275 }
1276
1277 void
1278 badlogin(const char *name)
1279 {
1280 if (hostname)
1281 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s, %s",
1282 failures, failures > 1 ? "S" : "", hostname, name);
1283 else
1284 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s, %s",
1285 failures, failures > 1 ? "S" : "", tty, name);
1286 }
1287
1288 #undef UNKNOWN
1289 #define UNKNOWN "su"
1290
1291 #ifndef __linux__
1292 char *
1293 stypeof(ttyid)
1294 char *ttyid;
1295 {
1296 struct ttyent *t;
1297
1298 return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
1299 }
1300 #endif
1301
1302 /* should not be called from PAM code... Why? */
1303 void
1304 sleepexit(eval)
1305 int eval;
1306 {
1307 sleep(SLEEP_EXIT_TIMEOUT);
1308 exit(eval);
1309 }