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