]> git.ipfire.org Git - thirdparty/util-linux.git/blame - login-utils/login.c
build-sys: add missing BUILD_PRLIMIT
[thirdparty/util-linux.git] / login-utils / login.c
CommitLineData
6dbe3af9 1/*
0d28a157
KZ
2 * login(1)
3 *
4 * This program is derived from 4.3 BSD software and is subject to the
5 * copyright notice below.
6 *
ee74f262
KZ
7 * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
8 * Rewritten to PAM-only version.
9 *
0d28a157
KZ
10 * Michael Glad (glad@daimi.dk)
11 * Computer Science Department, Aarhus University, Denmark
12 * 1990-07-04
13 *
6dbe3af9
KZ
14 * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms are permitted
18 * provided that the above copyright notice and this paragraph are
19 * duplicated in all such forms and that any documentation,
20 * advertising materials, and other materials related to such
21 * distribution and use acknowledge that the software was developed
22 * by the University of California, Berkeley. The name of the
23 * University may not be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
26 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
27 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
28 */
6dbe3af9 29#include <sys/param.h>
fd6b7a7f 30#include <stdio.h>
6dbe3af9
KZ
31#include <ctype.h>
32#include <unistd.h>
33#include <getopt.h>
34#include <memory.h>
fd6b7a7f 35#include <time.h>
6dbe3af9
KZ
36#include <sys/stat.h>
37#include <sys/time.h>
38#include <sys/resource.h>
39#include <sys/file.h>
40#include <termios.h>
41#include <string.h>
6dbe3af9 42#include <sys/ioctl.h>
fd6b7a7f 43#include <sys/wait.h>
6dbe3af9
KZ
44#include <signal.h>
45#include <errno.h>
46#include <grp.h>
47#include <pwd.h>
fd6b7a7f 48#include <utmp.h>
6dbe3af9 49#include <stdlib.h>
6dbe3af9
KZ
50#include <sys/syslog.h>
51#include <sys/sysmacros.h>
5476bf9b 52#include <linux/major.h>
6dbe3af9 53#include <netdb.h>
cb5acd69 54#include <lastlog.h>
905045d4
KZ
55#include <security/pam_appl.h>
56#include <security/pam_misc.h>
4d8fc09c 57#include <sys/sendfile.h>
ab71156c 58
f8bdba2f
KZ
59#ifdef HAVE_LIBAUDIT
60# include <libaudit.h>
61#endif
0aeb57ac 62
cb5acd69
KZ
63#include "c.h"
64#include "setproctitle.h"
66ee8158 65#include "pathnames.h"
8abcf290 66#include "strutils.h"
7eda085c 67#include "nls.h"
3e31a2df 68#include "xalloc.h"
3761d0bb 69#include "writeall.h"
6dbe3af9 70
4d8fc09c
KZ
71#include "logindefs.h"
72
905045d4 73#define is_pam_failure(_rc) ((_rc) != PAM_SUCCESS)
fd6b7a7f 74
48f09788
KZ
75#define LOGIN_MAX_TRIES 3
76#define LOGIN_EXIT_TIMEOUT 5
77#define LOGIN_TIMEOUT 60
fd6b7a7f 78
fd6b7a7f 79#ifdef USE_TTY_GROUP
ab71156c 80# define TTY_MODE 0620
fd6b7a7f 81#else
ab71156c 82# define TTY_MODE 0600
fd6b7a7f
KZ
83#endif
84
ab71156c 85#define TTYGRPNAME "tty" /* name of group to own ttys */
99f7c131
KZ
86#define VCS_PATH_MAX 64
87
88/*
89 * Login control struct
90 */
91struct login_context {
92 const char *tty_path; /* ttyname() return value */
93 const char *tty_name; /* tty_path without /dev prefix */
94 const char *tty_number; /* end of the tty_path */
738246ed 95 mode_t tty_mode; /* chmod() mode */
99f7c131 96
3eb8b797
KZ
97 char *username; /* from command line or PAM */
98
67e70761
KZ
99 struct passwd *pwd; /* user info */
100
eab72c4e
KZ
101 pam_handle_t *pamh; /* PAM handler */
102 struct pam_conv conv; /* PAM conversation */
103
99f7c131
KZ
104#ifdef LOGIN_CHOWN_VCS
105 char vcsn[VCS_PATH_MAX]; /* virtual console name */
106 char vcsan[VCS_PATH_MAX];
107#endif
d20337ed 108
92e386ca
KZ
109 char thishost[MAXHOSTNAMELEN + 1]; /* this machine */
110 char *thisdomain; /* this machine domain */
111 char *hostname; /* remote machine */
112 char hostaddress[16]; /* remote address */
0180264f
KZ
113
114 pid_t pid;
3761d0bb 115 int quiet; /* 1 is hush file exists */
eab72c4e 116
a2de6177 117 unsigned int remote:1, /* login -h */
92e386ca 118 nohost:1, /* login -H */
241e4565
KZ
119 noauth:1, /* login -f */
120 keep_env:1; /* login -p */
99f7c131 121};
726f69e2 122
6dbe3af9
KZ
123/*
124 * This bounds the time given to login. Not a define so it can
125 * be patched on machines where it's too small.
126 */
a169a454
KZ
127static int timeout = LOGIN_TIMEOUT;
128static int child_pid = 0;
129static volatile int got_sig = 0;
6dbe3af9 130
9abc9dab
KZ
131/*
132 * Robert Ambrose writes:
133 * A couple of my users have a problem with login processes hanging around
134 * soaking up pts's. What they seem to hung up on is trying to write out the
135 * message 'Login timed out after %d seconds' when the connection has already
136 * been dropped.
137 * What I did was add a second timeout while trying to write the message so
138 * the process just exits if the second timeout expires.
139 */
140static void timedout2(int sig __attribute__ ((__unused__)))
141{
142 struct termios ti;
143
144 /* reset echo */
145 tcgetattr(0, &ti);
146 ti.c_lflag |= ECHO;
147 tcsetattr(0, TCSANOW, &ti);
148 exit(EXIT_SUCCESS); /* %% */
149}
150
151static void timedout(int sig __attribute__ ((__unused__)))
152{
153 signal(SIGALRM, timedout2);
154 alarm(10);
155 /* TRANSLATORS: The standard value for %d is 60. */
156 warnx(_("timed out after %d seconds"), timeout);
157 signal(SIGALRM, SIG_IGN);
158 alarm(0);
159 timedout2(0);
160}
161
162/*
163 * This handler allows to inform a shell about signals to login. If you have
164 * (root) permissions you can kill all login childrent by one signal to login
165 * process.
166 *
167 * Also, parent who is session leader is able (before setsid() in child) to
168 * inform child when controlling tty goes away (e.g. modem hangup, SIGHUP).
169 */
170static void sig_handler(int signal)
171{
172 if (child_pid)
173 kill(-child_pid, signal);
174 else
175 got_sig = 1;
176 if (signal == SIGTERM)
177 kill(-child_pid, SIGHUP); /* because the shell often ignores SIGTERM */
178}
179
f950752b
KZ
180/*
181 * Let use delay for all exit() calls when user is not authenticated or
182 * session fully initialized (loginpam_session()).
183 */
9abc9dab
KZ
184static void sleepexit(int eval)
185{
ca5ee2a8 186 sleep(getlogindefs_num("FAIL_DELAY", LOGIN_EXIT_TIMEOUT));
9abc9dab
KZ
187 exit(eval);
188}
189
92e386ca
KZ
190static const char *get_thishost(struct login_context *cxt, const char **domain)
191{
192 if (!*cxt->thishost) {
193 if (gethostname(cxt->thishost, sizeof(cxt->thishost))) {
194 if (domain)
195 *domain = NULL;
196 return NULL;
197 }
198 cxt->thishost[sizeof(cxt->thishost) -1] = '\0';
199 cxt->thisdomain = strchr(cxt->thishost, '.');
200 if (cxt->thisdomain)
201 *cxt->thisdomain++ = '\0';
202 }
203
204 if (domain)
205 *domain = cxt->thisdomain;
206 return cxt->thishost;
207}
208
4d8fc09c
KZ
209/*
210 * Output the /etc/motd file
211 *
212 * motd() determines the name of a login announcement file and outputs it to
213 * the user's terminal at login time. The MOTD_FILE configuration option is a
214 * colon-delimited list of filenames. The empty MOTD_FILE option disables motd
215 * printing at all.
216 */
9abc9dab
KZ
217static void motd(void)
218{
4d8fc09c
KZ
219 char *motdlist, *motdfile, *cp;
220 const char *mb;
9abc9dab 221
4d8fc09c
KZ
222 mb = getlogindefs_str("MOTD_FILE", _PATH_MOTDFILE);
223 if (!mb || !*mb)
9abc9dab 224 return;
4d8fc09c
KZ
225
226 motdlist = xstrdup(mb);
227
228 for (cp = motdlist; (motdfile = strtok(cp, ":")); cp = NULL) {
229 struct stat st;
230 int fd;
231
232 if (stat(motdfile, &st) || !st.st_size)
233 continue;
234 fd = open(motdfile, O_RDONLY, 0);
235 if (fd < 0)
236 continue;
237
238 sendfile(fileno(stdout), fd, NULL, st.st_size);
239 close(fd);
240 }
9abc9dab 241}
99f7c131
KZ
242
243/*
244 * Nice and simple code provided by Linus Torvalds 16-Feb-93
245 * Nonblocking stuff by Maciej W. Rozycki, macro@ds2.pg.gda.pl, 1999.
246 *
247 * He writes: "Login performs open() on a tty in a blocking mode.
248 * In some cases it may make login wait in open() for carrier infinitely,
249 * for example if the line is a simplistic case of a three-wire serial
250 * connection. I believe login should open the line in the non-blocking mode
251 * leaving the decision to make a connection to getty (where it actually
252 * belongs).
253 */
254static void open_tty(const char *tty)
ab71156c 255{
1d4ad1de
KZ
256 int i, fd, flags;
257
258 fd = open(tty, O_RDWR | O_NONBLOCK);
259 if (fd == -1) {
960cf573 260 syslog(LOG_ERR, _("FATAL: can't reopen tty: %m"));
66020e56 261 sleepexit(EXIT_FAILURE);
1d4ad1de 262 }
eb63b9b8 263
8bee984a
KZ
264 if (!isatty(fd)) {
265 close(fd);
266 syslog(LOG_ERR, _("FATAL: %s is not a terminal"), tty);
66020e56 267 sleepexit(EXIT_FAILURE);
8bee984a
KZ
268 }
269
1d4ad1de
KZ
270 flags = fcntl(fd, F_GETFL);
271 flags &= ~O_NONBLOCK;
272 fcntl(fd, F_SETFL, flags);
e09947b5 273
1d4ad1de
KZ
274 for (i = 0; i < fd; i++)
275 close(i);
276 for (i = 0; i < 3; i++)
277 if (fd != i)
278 dup2(fd, i);
279 if (fd >= 3)
280 close(fd);
6dbe3af9
KZ
281}
282
ff0392a0
KZ
283#define chown_err(_what, _uid, _gid) \
284 syslog(LOG_ERR, _("chown (%s, %lu, %lu) failed: %m"), \
285 (_what), (unsigned long) (_uid), (unsigned long) (_gid))
286
287#define chmod_err(_what, _mode) \
288 syslog(LOG_ERR, _("chmod (%s, %u) failed: %m"), (_what), (_mode))
289
290static void chown_tty(struct login_context *cxt)
291{
45d0a30e 292 const char *grname;
ff0392a0
KZ
293 uid_t uid = cxt->pwd->pw_uid;
294 gid_t gid = cxt->pwd->pw_gid;
295
45d0a30e
KZ
296 grname = getlogindefs_str("TTYGROUP", TTYGRPNAME);
297 if (grname && *grname) {
298 if (*grname >= 0 && *grname <= 9) /* group by ID */
299 gid = getlogindefs_num("TTYGROUP", gid);
300 else { /* group by name */
301 struct group *gr = getgrnam(grname);
302 if (gr)
303 gid = gr->gr_gid;
304 }
305 }
ff0392a0
KZ
306
307 if (fchown(0, uid, gid)) /* tty */
308 chown_err(cxt->tty_name, uid, gid);
738246ed
KZ
309 if (fchmod(0, cxt->tty_mode))
310 chmod_err(cxt->tty_name, cxt->tty_mode);
ff0392a0
KZ
311
312#ifdef LOGIN_CHOWN_VCS
313 if (is_consoletty(0)) {
314 if (chown(cxt->vcs, uid, gid)) /* vcs */
315 chown_err(cxt->vcs, uid, gid);
738246ed
KZ
316 if (chmod(cxt->vcs, cxt->tty_mode))
317 chmod_err(cxt->vcs, cxt->tty_mode);
ff0392a0
KZ
318
319 if (chown(cxt->vcsa, uid, gid)) /* vcsa */
320 chown_err(cxt->vcsa, uid, gid);
738246ed
KZ
321 if (chmod(cxt->vcsa, cxt->tty_mode))
322 chmod_err(cxt->vcsa, cxt->tty_mode);
ff0392a0
KZ
323 }
324#endif
325}
326
99f7c131
KZ
327/*
328 * Reads the currect terminal path and initialize cxt->tty_* variables.
329 */
330static void init_tty(struct login_context *cxt)
ab71156c 331{
99f7c131
KZ
332 const char *p;
333 struct stat st;
334 struct termios tt, ttt;
335
738246ed
KZ
336 cxt->tty_mode = (mode_t) getlogindefs_num("TTYPERM", TTY_MODE);
337
99f7c131 338 cxt->tty_path = ttyname(0); /* libc calls istty() here */
24f4bbff 339
99f7c131
KZ
340 /*
341 * In case login is suid it was possible to use a hardlink as stdin
342 * and exploit races for a local root exploit. (Wojciech Purczynski).
343 *
344 * More precisely, the problem is ttyn := ttyname(0); ...; chown(ttyn);
345 * here ttyname() might return "/tmp/x", a hardlink to a pseudotty.
346 * All of this is a problem only when login is suid, which it isnt.
347 */
348 if (!cxt->tty_path || !*cxt->tty_path ||
349 lstat(cxt->tty_path, &st) != 0 || !S_ISCHR(st.st_mode) ||
350 (st.st_nlink > 1 && strncmp(cxt->tty_path, "/dev/", 5)) ||
351 access(cxt->tty_path, R_OK | W_OK) != 0) {
66020e56 352
24f4bbff 353 syslog(LOG_ERR, _("FATAL: bad tty"));
66020e56 354 sleepexit(EXIT_FAILURE);
24f4bbff 355 }
99f7c131
KZ
356
357 if (strncmp(cxt->tty_path, "/dev/", 5) == 0)
358 cxt->tty_name = cxt->tty_path + 5;
359 else
360 cxt->tty_name = cxt->tty_path;
361
362 for (p = cxt->tty_name; p && *p; p++) {
363 if (isdigit(*p)) {
364 cxt->tty_number = p;
365 break;
366 }
367 }
368
369#ifdef LOGIN_CHOWN_VCS
370 /* find names of Virtual Console devices, for later mode change */
371 snprintf(cxt->vcsn, sizeof(cxt->vcsn), "/dev/vcs%s", cxt->tty_number);
372 snprintf(cxt->vcsan, sizeof(cxt->vcsan), "/dev/vcsa%s", cxt->tty_number);
373#endif
374
375 tcgetattr(0, &tt);
376 ttt = tt;
377 ttt.c_cflag &= ~HUPCL;
378
738246ed 379 if ((fchown(0, 0, 0) || fchmod(0, cxt->tty_mode)) && errno != EROFS) {
99f7c131
KZ
380
381 syslog(LOG_ERR, _("FATAL: %s: change permissions failed: %m"),
382 cxt->tty_path);
383 sleepexit(EXIT_FAILURE);
384 }
385
386 /* Kill processes left on this tty */
387 tcsetattr(0, TCSAFLUSH, &ttt);
388
389 signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */
390 vhangup();
391 signal(SIGHUP, SIG_DFL);
392
393 /* open stdin,stdout,stderr to the tty */
394 open_tty(cxt->tty_path);
395
396 /* restore tty modes */
397 tcsetattr(0, TCSAFLUSH, &tt);
24f4bbff
KZ
398}
399
99f7c131 400
48d7b13a 401#ifdef LOGIN_CHOWN_VCS
fd6b7a7f 402/* true if the filedescriptor fd is a console tty, very Linux specific */
99f7c131 403static int is_consoletty(int fd)
ab71156c
KZ
404{
405 struct stat stb;
406
407 if ((fstat(fd, &stb) >= 0)
408 && (major(stb.st_rdev) == TTY_MAJOR)
409 && (minor(stb.st_rdev) < 64)) {
410 return 1;
411 }
412 return 0;
fd6b7a7f 413}
48d7b13a 414#endif
fd6b7a7f 415
364cda48
KZ
416/*
417 * Log failed login attempts in _PATH_BTMP if that exists.
418 * Must be called only with username the name of an actual user.
419 * The most common login failure is to give password instead of username.
420 */
a2de6177 421static void log_btmp(struct login_context *cxt)
ab71156c 422{
364cda48 423 struct utmp ut;
75d4dbb0 424 struct timeval tv;
364cda48
KZ
425
426 memset(&ut, 0, sizeof(ut));
427
3eb8b797
KZ
428 strncpy(ut.ut_user,
429 cxt->username ? cxt->username : "(unknown)",
364cda48
KZ
430 sizeof(ut.ut_user));
431
d20337ed
KZ
432 if (cxt->tty_number)
433 strncpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
434 if (cxt->tty_name)
435 xstrncpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
364cda48 436
ab71156c 437#if defined(_HAVE_UT_TV) /* in <utmpbits.h> included by <utmp.h> */
75d4dbb0
KZ
438 gettimeofday(&tv, NULL);
439 ut.ut_tv.tv_sec = tv.tv_sec;
440 ut.ut_tv.tv_usec = tv.tv_usec;
364cda48
KZ
441#else
442 {
443 time_t t;
444 time(&t);
ab71156c 445 ut.ut_time = t; /* ut_time is not always a time_t */
364cda48
KZ
446 }
447#endif
448
ab71156c 449 ut.ut_type = LOGIN_PROCESS; /* XXX doesn't matter */
0180264f 450 ut.ut_pid = cxt->pid;
c3f974a1 451
d20337ed
KZ
452 if (cxt->hostname) {
453 xstrncpy(ut.ut_host, cxt->hostname, sizeof(ut.ut_host));
454 if (cxt->hostaddress && *cxt->hostaddress)
455 memcpy(&ut.ut_addr_v6, cxt->hostaddress,
ab71156c 456 sizeof(ut.ut_addr_v6));
364cda48 457 }
6e3bc8a6 458
364cda48 459 updwtmp(_PATH_BTMP, &ut);
364cda48 460}
b2452358 461
b2452358 462
f8bdba2f 463#ifdef HAVE_LIBAUDIT
67e70761 464static void log_audit(struct login_context *cxt, int status)
f8bdba2f 465{
f8bdba2f 466 int audit_fd;
67e70761 467 struct passwd *pwd = cxt->pwd;
f8bdba2f
KZ
468
469 audit_fd = audit_open();
470 if (audit_fd == -1)
471 return;
c3f974a1
KZ
472 if (!pwd && cxt->username)
473 pwd = getpwnam(cxt->username);
474
475 audit_log_acct_message(audit_fd,
476 AUDIT_USER_LOGIN,
477 NULL,
478 "login",
479 cxt->username ? cxt->username : "(unknown)",
480 pwd ? pwd->pw_uid : (unsigned int) -1,
481 cxt->hostname,
482 NULL,
483 cxt->tty_name,
484 status);
f8bdba2f
KZ
485
486 close(audit_fd);
487}
67e70761
KZ
488#else /* !HAVE_LIBAUDIT */
489# define log_audit(cxt, status)
ab71156c 490#endif /* HAVE_LIBAUDIT */
f8bdba2f 491
3761d0bb
KZ
492static void log_lastlog(struct login_context *cxt)
493{
494 struct lastlog ll;
495 time_t t;
496 int fd;
497
67e70761
KZ
498 if (!cxt->pwd)
499 return;
500
3761d0bb
KZ
501 fd = open(_PATH_LASTLOG, O_RDWR, 0);
502 if (fd < 0)
503 return;
504
67e70761 505 lseek(fd, (off_t) cxt->pwd->pw_uid * sizeof(ll), SEEK_SET);
3761d0bb
KZ
506
507 /*
508 * Print last log message
509 */
510 if (!cxt->quiet) {
511 if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
512 ll.ll_time != 0) {
513 time_t ll_time = (time_t) ll.ll_time;
514
515 printf(_("Last login: %.*s "), 24 - 5, ctime(&ll_time));
516 if (*ll.ll_host != '\0')
517 printf(_("from %.*s\n"),
518 (int)sizeof(ll.ll_host), ll.ll_host);
519 else
520 printf(_("on %.*s\n"),
521 (int)sizeof(ll.ll_line), ll.ll_line);
522 }
67e70761 523 lseek(fd, (off_t) cxt->pwd->pw_uid * sizeof(ll), SEEK_SET);
3761d0bb
KZ
524 }
525
526 memset((char *)&ll, 0, sizeof(ll));
527
528 time(&t);
529 ll.ll_time = t; /* ll_time is always 32bit */
530
531 if (cxt->tty_name)
532 xstrncpy(ll.ll_line, cxt->tty_name, sizeof(ll.ll_line));
533 if (cxt->hostname)
534 xstrncpy(ll.ll_host, cxt->hostname, sizeof(ll.ll_host));
535
536 if (write_all(fd, (char *)&ll, sizeof(ll)))
537 warn(_("write lastlog failed"));
538
539 close(fd);
540}
541
6e3bc8a6
KZ
542/*
543 * Update wtmp and utmp logs
544 */
545static void log_utmp(struct login_context *cxt)
546{
547 struct utmp ut;
548 struct utmp *utp;
549 struct timeval tv;
550
551 utmpname(_PATH_UTMP);
552 setutent();
553
554 /* Find pid in utmp.
555 *
556 * login sometimes overwrites the runlevel entry in /var/run/utmp,
557 * confusing sysvinit. I added a test for the entry type, and the
558 * problem was gone. (In a runlevel entry, st_pid is not really a pid
559 * but some number calculated from the previous and current runlevel).
560 * -- Michael Riepe <michael@stud.uni-hannover.de>
561 */
562 while ((utp = getutent()))
563 if (utp->ut_pid == cxt->pid
564 && utp->ut_type >= INIT_PROCESS
565 && utp->ut_type <= DEAD_PROCESS)
566 break;
567
568 /* If we can't find a pre-existing entry by pid, try by line.
569 * BSD network daemons may rely on this.
570 */
571 if (utp == NULL) {
572 setutent();
573 ut.ut_type = LOGIN_PROCESS;
574 strncpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
575 utp = getutline(&ut);
576 }
577
578 if (utp)
579 memcpy(&ut, utp, sizeof(ut));
580 else
581 /* some gettys/telnetds don't initialize utmp... */
582 memset(&ut, 0, sizeof(ut));
583
584 if (ut.ut_id[0] == 0)
585 strncpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
586
587 strncpy(ut.ut_user, cxt->username, sizeof(ut.ut_user));
588 xstrncpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
589
590#ifdef _HAVE_UT_TV /* in <utmpbits.h> included by <utmp.h> */
591 gettimeofday(&tv, NULL);
592 ut.ut_tv.tv_sec = tv.tv_sec;
593 ut.ut_tv.tv_usec = tv.tv_usec;
594#else
595 {
596 time_t t;
597 time(&t);
598 ut.ut_time = t; /* ut_time is not always a time_t */
599 /* glibc2 #defines it as ut_tv.tv_sec */
600 }
601#endif
602 ut.ut_type = USER_PROCESS;
603 ut.ut_pid = cxt->pid;
604 if (cxt->hostname) {
605 xstrncpy(ut.ut_host, cxt->hostname, sizeof(ut.ut_host));
606 if (cxt->hostaddress && *cxt->hostaddress)
607 memcpy(&ut.ut_addr_v6, cxt->hostaddress,
608 sizeof(ut.ut_addr_v6));
609 }
610
611 pututline(&ut);
612 endutent();
613
614 updwtmp(_PATH_WTMP, &ut);
615}
616
516b00c4
KZ
617static void log_syslog(struct login_context *cxt)
618{
619 struct passwd *pwd = cxt->pwd;
620
621 if (!strncmp(cxt->tty_name, "ttyS", 4))
622 syslog(LOG_INFO, _("DIALUP AT %s BY %s"),
623 cxt->tty_name, pwd->pw_name);
624
625 if (!pwd->pw_uid) {
626 if (cxt->hostname)
627 syslog(LOG_NOTICE, _("ROOT LOGIN ON %s FROM %s"),
628 cxt->tty_name, cxt->hostname);
629 else
630 syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), cxt->tty_name);
631 } else {
632 if (cxt->hostname)
633 syslog(LOG_INFO, _("LOGIN ON %s BY %s FROM %s"),
634 cxt->tty_name, pwd->pw_name, cxt->hostname);
635 else
636 syslog(LOG_INFO, _("LOGIN ON %s BY %s"), cxt->tty_name,
637 pwd->pw_name);
638 }
639}
640
67e70761
KZ
641static struct passwd *get_passwd_entry(const char *username,
642 char **pwdbuf,
643 struct passwd *pwd)
644{
645 struct passwd *res = NULL;
646 size_t sz;
67e70761
KZ
647 int x;
648
649 if (!pwdbuf || !username)
650 return NULL;
651
652#ifdef _SC_GETPW_R_SIZE_MAX
653 sz = sysconf(_SC_GETPW_R_SIZE_MAX);
654 if (sz <= 0)
655#endif
656 sz = 16384;
657
a2e84c7d 658 *pwdbuf = xrealloc(*pwdbuf, sz);
67e70761
KZ
659
660 x = getpwnam_r(username, pwd, *pwdbuf, sz, &res);
661 if (!res) {
662 errno = x;
663 return NULL;
664 }
665 return res;
666}
667
53e9eda3 668/* encapsulate stupid "void **" pam_get_item() API */
eab72c4e 669static int loginpam_get_username(pam_handle_t *pamh, char **name)
53e9eda3 670{
ab71156c 671 const void *item = (void *)*name;
53e9eda3
KZ
672 int rc;
673 rc = pam_get_item(pamh, PAM_USER, &item);
ab71156c 674 *name = (char *)item;
53e9eda3
KZ
675 return rc;
676}
53e9eda3 677
eab72c4e 678static int loginpam_err(pam_handle_t *pamh, int retcode)
905045d4
KZ
679{
680 const char *msg = pam_strerror(pamh, retcode);
681
682 if (msg) {
683 fprintf(stderr, "\n%s\n", msg);
684 syslog(LOG_ERR, "%s", msg);
685 }
686 pam_end(pamh, retcode);
f950752b 687 sleepexit(EXIT_FAILURE);
905045d4
KZ
688}
689
92e386ca
KZ
690/*
691 * Composes "<host> login: " string; or returns "login: " is -H is given
692 */
693static const char *loginpam_get_prompt(struct login_context *cxt)
694{
695 const char *host;
696 char *prompt, *dflt_prompt = _("login: ");
697 size_t sz;
698
699 if (cxt->nohost || !(host = get_thishost(cxt, NULL)))
700 return dflt_prompt;
701
702 sz = strlen(host) + 1 + strlen(dflt_prompt) + 1;
703
704 prompt = xmalloc(sz);
705 snprintf(prompt, sz, "%s %s", host, dflt_prompt);
706
707 return prompt;
708}
709
eab72c4e
KZ
710static pam_handle_t *init_loginpam(struct login_context *cxt)
711{
712 pam_handle_t *pamh = NULL;
713 int rc;
714
715 /*
716 * username is initialized to NULL and if specified on the command line
717 * it is set. Therefore, we are safe not setting it to anything
718 */
719 rc = pam_start(cxt->remote ? "remote" : "login",
720 cxt->username, &cxt->conv, &pamh);
721 if (rc != PAM_SUCCESS) {
722 warnx(_("PAM failure, aborting: %s"), pam_strerror(pamh, rc));
723 syslog(LOG_ERR, _("Couldn't initialize PAM: %s"),
724 pam_strerror(pamh, rc));
f950752b 725 sleepexit(EXIT_FAILURE);
eab72c4e
KZ
726 }
727
728 /* hostname & tty are either set to NULL or their correct values,
729 * depending on how much we know
730 */
731 rc = pam_set_item(pamh, PAM_RHOST, cxt->hostname);
732 if (is_pam_failure(rc))
733 loginpam_err(pamh, rc);
734
735 rc = pam_set_item(pamh, PAM_TTY, cxt->tty_name);
736 if (is_pam_failure(rc))
737 loginpam_err(pamh, rc);
738
739 /*
740 * Andrew.Taylor@cal.montage.ca: Provide a user prompt to PAM so that
741 * the "login: " prompt gets localized. Unfortunately, PAM doesn't have
742 * an interface to specify the "Password: " string (yet).
743 */
92e386ca 744 rc = pam_set_item(pamh, PAM_USER_PROMPT, loginpam_get_prompt(cxt));
eab72c4e
KZ
745 if (is_pam_failure(rc))
746 loginpam_err(pamh, rc);
747
748 /* we need't the original username. We have to follow PAM. */
749 free(cxt->username);
750 cxt->username = NULL;
751 cxt->pamh = pamh;
752
753 return pamh;
754}
755
a2de6177
KZ
756static void loginpam_auth(struct login_context *cxt)
757{
cea8ec53
KZ
758 int rc, failcount = 0, show_unknown;
759 const char *hostname = cxt->hostname ? cxt->hostname :
760 cxt->tty_name ? cxt->tty_name : "<unknown>";
a2de6177
KZ
761 pam_handle_t *pamh = cxt->pamh;
762
763 /* if we didn't get a user on the command line, set it to NULL */
764 loginpam_get_username(pamh, &cxt->username);
765
cea8ec53
KZ
766 show_unknown = getlogindefs_bool("LOG_UNKFAIL_ENAB", 0);
767
a2de6177
KZ
768 /*
769 * There may be better ways to deal with some of these conditions, but
770 * at least this way I don't think we'll be giving away information...
771 *
772 * Perhaps someday we can trust that all PAM modules will pay attention
773 * to failure count and get rid of MAX_LOGIN_TRIES?
774 */
775 rc = pam_authenticate(pamh, 0);
776
777 while ((failcount++ < LOGIN_MAX_TRIES) &&
778 ((rc == PAM_AUTH_ERR) ||
779 (rc == PAM_USER_UNKNOWN) ||
780 (rc == PAM_CRED_INSUFFICIENT) ||
781 (rc == PAM_AUTHINFO_UNAVAIL))) {
782
cea8ec53
KZ
783 if (rc == PAM_USER_UNKNOWN && !show_unknown)
784 /*
785 * logging unknown usernames may be a security issue if
786 * an user enter her password instead of her login name
787 */
788 cxt->username = NULL;
789 else
790 loginpam_get_username(pamh, &cxt->username);
a2de6177
KZ
791
792 syslog(LOG_NOTICE,
793 _("FAILED LOGIN %d FROM %s FOR %s, %s"),
cea8ec53
KZ
794 failcount, hostname,
795 cxt->username ? cxt->username : "(unknown)",
a2de6177
KZ
796 pam_strerror(pamh, rc));
797
798 log_btmp(cxt);
799 log_audit(cxt, 0);
800
801 fprintf(stderr, _("Login incorrect\n\n"));
802
803 pam_set_item(pamh, PAM_USER, NULL);
804 rc = pam_authenticate(pamh, 0);
805 }
806
807 if (is_pam_failure(rc)) {
808
cea8ec53
KZ
809 if (rc == PAM_USER_UNKNOWN && !show_unknown)
810 cxt->username = NULL;
811 else
812 loginpam_get_username(pamh, &cxt->username);
a2de6177
KZ
813
814 if (rc == PAM_MAXTRIES)
815 syslog(LOG_NOTICE,
816 _("TOO MANY LOGIN TRIES (%d) FROM %s FOR %s, %s"),
cea8ec53
KZ
817 failcount, hostname,
818 cxt->username ? cxt->username : "(unknown)",
a2de6177
KZ
819 pam_strerror(pamh, rc));
820 else
821 syslog(LOG_NOTICE,
822 _("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
cea8ec53
KZ
823 hostname,
824 cxt->username ? cxt->username : "(unknown)",
a2de6177
KZ
825 pam_strerror(pamh, rc));
826
827 log_btmp(cxt);
828 log_audit(cxt, 0);
829
830 fprintf(stderr, _("\nLogin incorrect\n"));
831 pam_end(pamh, rc);
f950752b 832 sleepexit(EXIT_SUCCESS);
a2de6177
KZ
833 }
834}
835
98306fc5
KZ
836static void loginpam_acct(struct login_context *cxt)
837{
838 int rc;
839 pam_handle_t *pamh = cxt->pamh;
840
841 rc = pam_acct_mgmt(pamh, 0);
842
843 if (rc == PAM_NEW_AUTHTOK_REQD)
844 rc = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
845
846 if (is_pam_failure(rc))
847 loginpam_err(pamh, rc);
848
849 /*
850 * Grab the user information out of the password file for future usage
851 * First get the username that we are actually using, though.
852 */
853 rc = loginpam_get_username(pamh, &cxt->username);
854 if (is_pam_failure(rc))
855 loginpam_err(pamh, rc);
856
857 if (!cxt->username || !*cxt->username) {
858 warnx(_("\nSession setup problem, abort."));
859 syslog(LOG_ERR, _("NULL user name in %s:%d. Abort."),
860 __FUNCTION__, __LINE__);
861 pam_end(pamh, PAM_SYSTEM_ERR);
f950752b 862 sleepexit(EXIT_FAILURE);
98306fc5
KZ
863 }
864}
865
34f7ea15
KZ
866/*
867 * Note that position of the pam_setcred() call is discussable:
868 *
869 * - the PAM docs recommends pam_setcred() before pam_open_session()
870 * - but the original RFC http://www.opengroup.org/rfc/mirror-rfc/rfc86.0.txt
871 * uses pam_setcred() after pam_open_session()
872 *
873 * The old login versions (before year 2011) followed the RFC. This is probably
874 * not optimal, because there could be dependence between some session modules
875 * and user's credentials.
876 *
877 * The best is probably to follow openssh and call pam_setcred() before and
878 * after pam_open_session(). -- kzak@redhat.com (18-Nov-2011)
879 *
880 */
59a184d9
KZ
881static void loginpam_session(struct login_context *cxt)
882{
883 int rc;
884 pam_handle_t *pamh = cxt->pamh;
885
34f7ea15 886 rc = pam_setcred(pamh, PAM_ESTABLISH_CRED);
59a184d9
KZ
887 if (is_pam_failure(rc))
888 loginpam_err(pamh, rc);
889
34f7ea15
KZ
890 rc = pam_open_session(pamh, 0);
891 if (is_pam_failure(rc)) {
892 pam_setcred(cxt->pamh, PAM_DELETE_CRED);
893 loginpam_err(pamh, rc);
894 }
895
896 rc = pam_setcred(pamh, PAM_REINITIALIZE_CRED);
59a184d9
KZ
897 if (is_pam_failure(rc)) {
898 pam_close_session(pamh, 0);
899 loginpam_err(pamh, rc);
900 }
901}
902
3c0e680c
KZ
903/*
904 * We need to check effective UID/GID. For example $HOME could be on root
905 * squashed NFS or on NFS with UID mapping and access(2) uses real UID/GID.
906 * The open(2) seems as the surest solution.
907 * -- kzak@redhat.com (10-Apr-2009)
908 */
918b1a9d 909static int effective_access(const char *path, int mode)
3c0e680c
KZ
910{
911 int fd = open(path, mode);
912 if (fd != -1)
913 close(fd);
914 return fd == -1 ? -1 : 0;
915}
916
918b1a9d
KZ
917/*
918 * Check per accout or global hush-login setting.
919 *
920 * Hushed mode is enabled:
921 *
922 * a) if global (e.g. /etc/hushlogins) hush file exists:
923 * 1) for ALL ACCOUNTS if the file is empty
924 * 2) for the current user if the username or shell are found in the file
925 *
926 * b) if ~/.hushlogin file exists
927 *
928 * The ~/.hushlogin is ignored if the global hush file exists.
929 *
84d3c9ff
KZ
930 * The HUSHLOGIN_FILE login.def variable overwrites the default hush filename.
931 *
918b1a9d
KZ
932 * Note that shadow-utils login(1) does not support "a1)". The "a1)" is
933 * necessary if you want to use PAM for "Last login" message.
934 *
935 * -- Karel Zak <kzak@redhat.com> (26-Aug-2011)
936 *
937 *
938 * Per-account check requires some explanation: As root we may not be able to
939 * read the directory of the user if it is on an NFS mounted filesystem. We
940 * temporarily set our effective uid to the user-uid making sure that we keep
941 * root privs. in the real uid.
942 *
943 * A portable solution would require a fork(), but we rely on Linux having the
944 * BSD setreuid()
945 */
946static int get_hushlogin_status(struct passwd *pwd)
947{
948 const char *files[] = { _PATH_HUSHLOGINS, _PATH_HUSHLOGIN, NULL };
84d3c9ff 949 const char *file;
918b1a9d
KZ
950 char buf[BUFSIZ];
951 int i;
952
84d3c9ff
KZ
953 file = getlogindefs_str("HUSHLOGIN_FILE", NULL);
954 if (file) {
955 if (!*file)
956 return 0; /* empty HUSHLOGIN_FILE defined */
957
958 files[0] = file;
959 files[1] = NULL;
960 }
961
918b1a9d 962 for (i = 0; files[i]; i++) {
918b1a9d
KZ
963 int ok = 0;
964
84d3c9ff
KZ
965 file = files[i];
966
918b1a9d
KZ
967 /* Global hush-file*/
968 if (*file == '/') {
969 struct stat st;
970 FILE *f;
971
972 if (stat(file, &st) != 0)
973 continue; /* file does not exist */
974
975 if (st.st_size == 0)
976 return 1; /* for all accounts */
977
978 f = fopen(file, "r");
979 if (!f)
980 continue; /* ignore errors... */
981
982 while (ok == 0 && fgets(buf, sizeof(buf), f)) {
983 buf[strlen(buf) - 1] = '\0';
984 ok = !strcmp(buf, *buf == '/' ? pwd->pw_shell :
985 pwd->pw_name);
986 }
987 fclose(f);
988 if (ok)
989 return 1; /* found username/shell */
990
991 return 0; /* ignore per-account files */
992 }
993
994 /* Per-account setting */
995 if (strlen(pwd->pw_dir) + sizeof(file) + 2 > sizeof(buf))
996 continue;
997 else {
998 uid_t ruid = getuid();
999 gid_t egid = getegid();
1000
1001 sprintf(buf, "%s/%s", pwd->pw_dir, file);
1002 setregid(-1, pwd->pw_gid);
1003 setreuid(0, pwd->pw_uid);
1004 ok = effective_access(buf, O_RDONLY) == 0;
1005 setuid(0); /* setreuid doesn't do it alone! */
1006 setreuid(ruid, 0);
1007 setregid(-1, egid);
1008
1009 if (ok)
1010 return 1; /* enabled by user */
1011 }
1012 }
1013
1014 return 0;
1015}
1016
a169a454
KZ
1017/*
1018 * Detach the controlling terminal, fork, restore syslog stuff and create a new
1019 * session.
1020 */
1021static void fork_session(struct login_context *cxt)
1022{
1023 struct sigaction sa, oldsa_hup, oldsa_term;
1024
1025 signal(SIGALRM, SIG_DFL);
1026 signal(SIGQUIT, SIG_DFL);
1027 signal(SIGTSTP, SIG_IGN);
1028
1029 memset(&sa, 0, sizeof(sa));
1030 sa.sa_handler = SIG_IGN;
1031 sigaction(SIGINT, &sa, NULL);
1032
1033 sigaction(SIGHUP, &sa, &oldsa_hup); /* ignore when TIOCNOTTY */
1034
1035 /*
1036 * detach the controlling tty
1037 * -- we needn't the tty in parent who waits for child only.
1038 * The child calls setsid() that detach from the tty as well.
1039 */
1040 ioctl(0, TIOCNOTTY, NULL);
1041
1042 /*
1043 * We have care about SIGTERM, because leave PAM session without
1044 * pam_close_session() is pretty bad thing.
1045 */
1046 sa.sa_handler = sig_handler;
1047 sigaction(SIGHUP, &sa, NULL);
1048 sigaction(SIGTERM, &sa, &oldsa_term);
1049
1050 closelog();
1051
1052 /*
1053 * We must fork before setuid() because we need to call
1054 * pam_close_session() as root.
1055 */
1056 child_pid = fork();
1057 if (child_pid < 0) {
1058 /*
1059 * fork() error
1060 */
1061 warn(_("fork failed"));
1062
1063 pam_setcred(cxt->pamh, PAM_DELETE_CRED);
1064 pam_end(cxt->pamh, pam_close_session(cxt->pamh, 0));
f950752b 1065 sleepexit(EXIT_FAILURE);
a169a454
KZ
1066 }
1067
1068 if (child_pid) {
1069 /*
1070 * parent - wait for child to finish, then cleanup session
1071 */
1072 close(0);
1073 close(1);
1074 close(2);
1075 sa.sa_handler = SIG_IGN;
1076 sigaction(SIGQUIT, &sa, NULL);
1077 sigaction(SIGINT, &sa, NULL);
1078
1079 /* wait as long as any child is there */
1080 while (wait(NULL) == -1 && errno == EINTR) ;
1081 openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
1082
1083 pam_setcred(cxt->pamh, PAM_DELETE_CRED);
1084 pam_end(cxt->pamh, pam_close_session(cxt->pamh, 0));
1085 exit(EXIT_SUCCESS);
1086 }
1087
1088 /*
1089 * child
1090 */
1091 sigaction(SIGHUP, &oldsa_hup, NULL); /* restore old state */
1092 sigaction(SIGTERM, &oldsa_term, NULL);
1093 if (got_sig)
1094 exit(EXIT_FAILURE);
1095
1096 /*
1097 * Problem: if the user's shell is a shell like ash that doesnt do
1098 * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
1099 * process in the pgrp, will kill us.
1100 */
1101
1102 /* start new session */
1103 setsid();
1104
1105 /* make sure we have a controlling tty */
1106 open_tty(cxt->tty_path);
1107 openlog("login", LOG_ODELAY, LOG_AUTHPRIV); /* reopen */
1108
1109 /*
1110 * TIOCSCTTY: steal tty from other process group.
1111 */
1112 if (ioctl(0, TIOCSCTTY, 1))
1113 syslog(LOG_ERR, _("TIOCSCTTY failed: %m"));
1114 signal(SIGINT, SIG_DFL);
1115}
1116
241e4565
KZ
1117/*
1118 * Initialize $TERM, $HOME, ...
1119 */
1120static void init_environ(struct login_context *cxt)
1121{
1122 struct passwd *pwd = cxt->pwd;
1123 char *termenv = NULL, **env;
1124 char tmp[PATH_MAX];
1125 int len, i;
1126
1127 termenv = getenv("TERM");
a2e84c7d 1128 termenv = termenv ? xstrdup(termenv) : "dumb";
241e4565
KZ
1129
1130 /* destroy environment unless user has requested preservation (-p) */
1131 if (!cxt->keep_env) {
a2e84c7d 1132 environ = (char **) xmalloc(sizeof(char *));
241e4565
KZ
1133 memset(environ, 0, sizeof(char *));
1134 }
1135
1136 setenv("HOME", pwd->pw_dir, 0); /* legal to override */
241e4565
KZ
1137 setenv("SHELL", pwd->pw_shell, 1);
1138 setenv("TERM", termenv, 1);
1139
9f7293ea
KZ
1140 if (pwd->pw_uid)
1141 setenv("PATH", getlogindefs_str("ENV_PATH", _PATH_DEFPATH), 1);
1142 else {
1143 const char *x = getlogindefs_str("ENV_ROOTPATH", NULL);
1144 if (!x)
1145 x = getlogindefs_str("ENV_SUPATH", _PATH_DEFPATH_ROOT);
1146 setenv("PATH", x, 1);
1147 }
1148
241e4565
KZ
1149 /* mailx will give a funny error msg if you forget this one */
1150 len = snprintf(tmp, sizeof(tmp), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
1151 if (len > 0 && (size_t) len + 1 <= sizeof(tmp))
1152 setenv("MAIL", tmp, 0);
1153
1154 /* LOGNAME is not documented in login(1) but HP-UX 6.5 does it. We'll
1155 * not allow modifying it.
1156 */
1157 setenv("LOGNAME", pwd->pw_name, 1);
1158
1159 env = pam_getenvlist(cxt->pamh);
1160 for (i = 0; env && env[i]; i++)
1161 putenv(env[i]);
1162}
1163
cbbd5185
KZ
1164/*
1165 * Called for -h option, initialize cxt->{hostname,hostaddress}
1166 */
1167static void init_remote_info(struct login_context *cxt, char *remotehost)
1168{
92e386ca
KZ
1169 const char *domain;
1170 char *p;
cbbd5185
KZ
1171 struct addrinfo hints, *info = NULL;
1172
1173 cxt->remote = 1;
1174
92e386ca 1175 get_thishost(cxt, &domain);
cbbd5185 1176
92e386ca
KZ
1177 if (domain && (p = strchr(remotehost, '.')) &&
1178 strcasecmp(p + 1, domain) == 0)
cbbd5185
KZ
1179 *p = '\0';
1180
1181 cxt->hostname = xstrdup(remotehost);
1182
1183 memset(&hints, 0, sizeof(hints));
1184 hints.ai_flags = AI_ADDRCONFIG;
1185 cxt->hostaddress[0] = 0;
1186
1187 if (getaddrinfo(cxt->hostname, NULL, &hints, &info) == 0 && info) {
1188 if (info->ai_family == AF_INET) {
1189 struct sockaddr_in *sa =
1190 (struct sockaddr_in *) info->ai_addr;
1191
1192 memcpy(cxt->hostaddress, &(sa->sin_addr), sizeof(sa->sin_addr));
1193
1194 } else if (info->ai_family == AF_INET6) {
1195 struct sockaddr_in6 *sa =
1196 (struct sockaddr_in6 *) info->ai_addr;
1197
1198 memcpy(cxt->hostaddress, &(sa->sin6_addr), sizeof(sa->sin6_addr));
1199 }
1200 freeaddrinfo(info);
1201 }
1202}
1203
ab71156c 1204int main(int argc, char **argv)
6dbe3af9 1205{
fc32d43e 1206 int c;
241e4565 1207 int cnt;
ab71156c
KZ
1208 char *childArgv[10];
1209 char *buff;
1210 int childArgc = 0;
1211 int retcode;
6dbe3af9 1212
67e70761
KZ
1213 char *pwdbuf = NULL;
1214 struct passwd *pwd = NULL, _pwd;
1215
0180264f 1216 struct login_context cxt = {
738246ed 1217 .tty_mode = TTY_MODE, /* tty chmod() */
eab72c4e
KZ
1218 .pid = getpid(), /* PID */
1219 .conv = { misc_conv, NULL } /* PAM conversation function */
0180264f 1220 };
ab71156c 1221
9abd9cde
KZ
1222 timeout = getlogindefs_num("LOGIN_TIMEOUT", LOGIN_TIMEOUT);
1223
ab71156c
KZ
1224 signal(SIGALRM, timedout);
1225 siginterrupt(SIGALRM, 1); /* we have to interrupt syscalls like ioclt() */
1226 alarm((unsigned int)timeout);
1227 signal(SIGQUIT, SIG_IGN);
1228 signal(SIGINT, SIG_IGN);
1229
1230 setlocale(LC_ALL, "");
1231 bindtextdomain(PACKAGE, LOCALEDIR);
1232 textdomain(PACKAGE);
1233
1234 setpriority(PRIO_PROCESS, 0, 0);
1235 initproctitle(argc, argv);
1236
1237 /*
1238 * -p is used by getty to tell login not to destroy the environment
1239 * -f is used to skip a second login authentication
1240 * -h is used by other servers to pass the name of the remote
1241 * host to login so that it may be placed in utmp and wtmp
1242 */
92e386ca 1243 while ((c = getopt(argc, argv, "fHh:p")) != -1)
fc32d43e 1244 switch (c) {
ab71156c 1245 case 'f':
a2de6177 1246 cxt.noauth = 1;
ab71156c
KZ
1247 break;
1248
92e386ca
KZ
1249 case 'H':
1250 cxt.nohost = 1;
1251 break;
1252
ab71156c
KZ
1253 case 'h':
1254 if (getuid()) {
1255 fprintf(stderr,
1256 _("login: -h for super-user only.\n"));
1257 exit(EXIT_FAILURE);
ea6c190a 1258 }
cbbd5185 1259 init_remote_info(&cxt, optarg);
ab71156c
KZ
1260 break;
1261
1262 case 'p':
241e4565 1263 cxt.keep_env = 1;
ab71156c
KZ
1264 break;
1265
1266 case '?':
1267 default:
92e386ca 1268 fprintf(stderr, _("usage: login [ -p ] [ -h host ] [ -H ] [ -f username | username ]\n"));
ab71156c 1269 exit(EXIT_FAILURE);
ea6c190a 1270 }
ab71156c
KZ
1271 argc -= optind;
1272 argv += optind;
1273
1274 if (*argv) {
1275 char *p = *argv;
a2e84c7d 1276 cxt.username = xstrdup(p);
ab71156c
KZ
1277
1278 /* wipe name - some people mistype their password here */
1279 /* (of course we are too late, but perhaps this helps a little ..) */
1280 while (*p)
1281 *p++ = ' ';
1282 }
1283
1284 for (cnt = getdtablesize(); cnt > 2; cnt--)
1285 close(cnt);
1286
99f7c131 1287 setpgrp(); /* set pgid to pid this means that setsid() will fail */
11784a84 1288
ab71156c 1289 openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
364cda48 1290
99f7c131 1291 init_tty(&cxt);
4ab0df0a 1292 init_loginpam(&cxt);
fd6b7a7f 1293
a2de6177
KZ
1294 /* login -f, then the user has already been authenticated */
1295 cxt.noauth = cxt.noauth && getuid() == 0 ? 1 : 0;
ab71156c 1296
a2de6177
KZ
1297 if (!cxt.noauth)
1298 loginpam_auth(&cxt);
6dbe3af9 1299
ab71156c 1300 /*
98306fc5
KZ
1301 * Authentication may be skipped (for example, during krlogin, rlogin,
1302 * etc...), but it doesn't mean that we can skip other account checks.
1303 * The account could be disabled or password expired (althought
1304 * kerberos ticket is valid). -- kzak@redhat.com (22-Feb-2006)
ab71156c 1305 */
98306fc5 1306 loginpam_acct(&cxt);
a7507436 1307
67e70761 1308 if (!(cxt.pwd = get_passwd_entry(cxt.username, &pwdbuf, &_pwd))) {
ab71156c
KZ
1309 warnx(_("\nSession setup problem, abort."));
1310 syslog(LOG_ERR, _("Invalid user name \"%s\" in %s:%d. Abort."),
3eb8b797 1311 cxt.username, __FUNCTION__, __LINE__);
4ab0df0a 1312 pam_end(cxt.pamh, PAM_SYSTEM_ERR);
f950752b 1313 sleepexit(EXIT_FAILURE);
2b6fc908 1314 }
e09947b5 1315
67e70761 1316 pwd = cxt.pwd;
3eb8b797 1317 cxt.username = pwd->pw_name;
e09947b5 1318
ab71156c 1319 /*
a7507436
KZ
1320 * Initialize the supplementary group list. This should be done before
1321 * pam_setcred because the PAM modules might add groups during
1322 * pam_setcred.
1323 *
1324 * For root we don't call initgroups, instead we call setgroups with
1325 * group 0. This avoids the need to step through the whole group file,
1326 * which can cause problems if NIS, NIS+, LDAP or something similar
1327 * is used and the machine has network problems.
ab71156c 1328 */
a7507436
KZ
1329 retcode = pwd->pw_uid ? initgroups(cxt.username, pwd->pw_gid) : /* user */
1330 setgroups(0, NULL); /* root */
1331 if (retcode < 0) {
1332 syslog(LOG_ERR, _("groups initialization failed: %m"));
ab71156c 1333 warnx(_("\nSession setup problem, abort."));
4ab0df0a 1334 pam_end(cxt.pamh, PAM_SYSTEM_ERR);
f950752b 1335 sleepexit(EXIT_FAILURE);
ab71156c
KZ
1336 }
1337
59a184d9
KZ
1338 /*
1339 * Open PAM session (after successful authentication and account check)
1340 */
1341 loginpam_session(&cxt);
e09947b5 1342
ab71156c
KZ
1343 /* committed to login -- turn off timeout */
1344 alarm((unsigned int)0);
1345
1346 endpwent();
1347
918b1a9d 1348 cxt.quiet = get_hushlogin_status(pwd);
e09947b5 1349
6e3bc8a6 1350 log_utmp(&cxt);
67e70761 1351 log_audit(&cxt, 1);
3761d0bb 1352 log_lastlog(&cxt);
e09947b5 1353
ff0392a0 1354 chown_tty(&cxt);
ab71156c 1355
ab71156c
KZ
1356 if (setgid(pwd->pw_gid) < 0 && pwd->pw_gid) {
1357 syslog(LOG_ALERT, _("setgid() failed"));
1358 exit(EXIT_FAILURE);
6dbe3af9 1359 }
5c36a0eb 1360
ab71156c
KZ
1361 if (*pwd->pw_shell == '\0')
1362 pwd->pw_shell = _PATH_BSHELL;
e09947b5 1363
516b00c4 1364 init_environ(&cxt); /* init $HOME, $TERM ... */
ab71156c 1365
3eb8b797 1366 setproctitle("login", cxt.username);
ab71156c 1367
516b00c4 1368 log_syslog(&cxt);
ab71156c 1369
3761d0bb 1370 if (!cxt.quiet) {
ab71156c
KZ
1371 motd();
1372
1373#ifdef LOGIN_STAT_MAIL
1374 /*
1375 * This turns out to be a bad idea: when the mail spool
1376 * is NFS mounted, and the NFS connection hangs, the
1377 * login hangs, even root cannot login.
1378 * Checking for mail should be done from the shell.
1379 */
1380 {
1381 struct stat st;
1382 char *mail;
1383
1384 mail = getenv("MAIL");
1385 if (mail && stat(mail, &st) == 0 && st.st_size != 0) {
1386 if (st.st_mtime > st.st_atime)
1387 printf(_("You have new mail.\n"));
1388 else
1389 printf(_("You have mail.\n"));
1390 }
1391 }
d162fcb5 1392#endif
ab71156c
KZ
1393 }
1394
ab71156c 1395 /*
a169a454
KZ
1396 * Detach the controlling terminal, fork() and create, new session
1397 * and reinilizalize syslog stuff.
ab71156c 1398 */
a169a454 1399 fork_session(&cxt);
ab71156c
KZ
1400
1401 /* discard permissions last so can't get killed and drop core */
1402 if (setuid(pwd->pw_uid) < 0 && pwd->pw_uid) {
1403 syslog(LOG_ALERT, _("setuid() failed"));
1404 exit(EXIT_FAILURE);
1405 }
1406
1407 /* wait until here to change directory! */
1408 if (chdir(pwd->pw_dir) < 0) {
1409 warn(_("%s: change directory failed"), pwd->pw_dir);
91d0a913
KZ
1410
1411 if (!getlogindefs_bool("DEFAULT_HOME", 1))
1412 exit(0);
ab71156c
KZ
1413 if (chdir("/"))
1414 exit(EXIT_FAILURE);
1415 pwd->pw_dir = "/";
1416 printf(_("Logging in with home = \"/\".\n"));
1417 }
1418
1419 /* if the shell field has a space: treat it like a shell script */
1420 if (strchr(pwd->pw_shell, ' ')) {
1421 buff = xmalloc(strlen(pwd->pw_shell) + 6);
1422
1423 strcpy(buff, "exec ");
1424 strcat(buff, pwd->pw_shell);
1425 childArgv[childArgc++] = "/bin/sh";
1426 childArgv[childArgc++] = "-sh";
1427 childArgv[childArgc++] = "-c";
1428 childArgv[childArgc++] = buff;
1429 } else {
cbbd5185
KZ
1430 char tbuf[PATH_MAX + 2], *p;
1431
ab71156c
KZ
1432 tbuf[0] = '-';
1433 xstrncpy(tbuf + 1, ((p = strrchr(pwd->pw_shell, '/')) ?
1434 p + 1 : pwd->pw_shell), sizeof(tbuf) - 1);
1435
1436 childArgv[childArgc++] = pwd->pw_shell;
1437 childArgv[childArgc++] = tbuf;
1438 }
1439
1440 childArgv[childArgc++] = NULL;
1441
1442 execvp(childArgv[0], childArgv + 1);
1443
1444 if (!strcmp(childArgv[0], "/bin/sh"))
1445 warn(_("couldn't exec shell script"));
1446 else
1447 warn(_("no shell"));
1448
1449 exit(EXIT_SUCCESS);
6dbe3af9
KZ
1450}
1451
df1dddf9 1452