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