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