* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <sys/param.h>
#include <stdio.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
-#include <utmp.h>
+#include <utmpx.h>
+#ifdef HAVE_LASTLOG_H
+# include <lastlog.h>
+#endif
#include <stdlib.h>
#include <sys/syslog.h>
-#include <sys/sysmacros.h>
#ifdef HAVE_LINUX_MAJOR_H
# include <linux/major.h>
#endif
#include <netdb.h>
-#include <lastlog.h>
#include <security/pam_appl.h>
#ifdef HAVE_SECURITY_PAM_MISC_H
# include <security/pam_misc.h>
#include "pathnames.h"
#include "strutils.h"
#include "nls.h"
+#include "env.h"
#include "xalloc.h"
#include "all-io.h"
#include "fileutils.h"
#include "ttyutils.h"
+#include "pwdutils.h"
#include "logindefs.h"
const char *tty_number; /* end of the tty_path */
mode_t tty_mode; /* chmod() mode */
- char *username; /* from command line or PAM */
+ const char *username; /* points to PAM, pwd or cmd_username */
+ char *cmd_username; /* username specified on command line */
+
struct passwd *pwd; /* user info */
+ char *pwdbuf; /* pwd strings */
pam_handle_t *pamh; /* PAM handler */
struct pam_conv conv; /* PAM conversation */
static unsigned int timeout = LOGIN_TIMEOUT;
static int child_pid = 0;
static volatile int got_sig = 0;
+static char timeout_msg[128];
#ifdef LOGIN_CHOWN_VCS
/* true if the filedescriptor fd is a console tty, very Linux specific */
tcgetattr(0, &ti);
ti.c_lflag |= ECHO;
tcsetattr(0, TCSANOW, &ti);
- exit(EXIT_SUCCESS); /* %% */
+ _exit(EXIT_SUCCESS); /* %% */
}
static void timedout(int sig __attribute__ ((__unused__)))
{
signal(SIGALRM, timedout2);
alarm(10);
- /* TRANSLATORS: The standard value for %u is 60. */
- warnx(_("timed out after %u seconds"), timeout);
+ ignore_result( write(STDERR_FILENO, timeout_msg, strlen(timeout_msg)) );
signal(SIGALRM, SIG_IGN);
alarm(0);
timedout2(0);
}
/*
- * Reads the currect terminal path and initializes cxt->tty_* variables.
+ * Reads the current terminal path and initializes cxt->tty_* variables.
*/
static void init_tty(struct login_context *cxt)
{
cxt->tty_mode = (mode_t) getlogindefs_num("TTYPERM", TTY_MODE);
- get_terminal_name(0, &cxt->tty_path, &cxt->tty_name, &cxt->tty_number);
+ get_terminal_name(&cxt->tty_path, &cxt->tty_name, &cxt->tty_number);
/*
* In case login is suid it was possible to use a hardlink as stdin
tcsetattr(0, TCSANOW, &ttt);
/*
- * Let's close file decriptors before vhangup
+ * Let's close file descriptors before vhangup
* https://lkml.org/lkml/2012/6/5/145
*/
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
- signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */
+ signal(SIGHUP, SIG_IGN); /* so vhangup() won't kill us */
vhangup();
signal(SIGHUP, SIG_DFL);
*/
static void log_btmp(struct login_context *cxt)
{
- struct utmp ut;
-#if defined(_HAVE_UT_TV) /* in <utmpbits.h> included by <utmp.h> */
+ struct utmpx ut;
struct timeval tv;
-#endif
memset(&ut, 0, sizeof(ut));
- strncpy(ut.ut_user,
+ str2memcpy(ut.ut_user,
cxt->username ? cxt->username : "(unknown)",
sizeof(ut.ut_user));
if (cxt->tty_number)
- strncpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
+ str2memcpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
if (cxt->tty_name)
- xstrncpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
+ str2memcpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
-#if defined(_HAVE_UT_TV) /* in <utmpbits.h> included by <utmp.h> */
gettimeofday(&tv, NULL);
ut.ut_tv.tv_sec = tv.tv_sec;
ut.ut_tv.tv_usec = tv.tv_usec;
-#else
- {
- time_t t;
- time(&t);
- ut.ut_time = t; /* ut_time is not always a time_t */
- }
-#endif
ut.ut_type = LOGIN_PROCESS; /* XXX doesn't matter */
ut.ut_pid = cxt->pid;
if (cxt->hostname) {
- xstrncpy(ut.ut_host, cxt->hostname, sizeof(ut.ut_host));
+ str2memcpy(ut.ut_host, cxt->hostname, sizeof(ut.ut_host));
if (*cxt->hostaddress)
memcpy(&ut.ut_addr_v6, cxt->hostaddress,
sizeof(ut.ut_addr_v6));
}
- updwtmp(_PATH_BTMP, &ut);
+ updwtmpx(_PATH_BTMP, &ut);
}
if (!cxt->pwd)
return;
+ if (cxt->pwd->pw_uid > (uid_t) getlogindefs_num("LASTLOG_UID_MAX", ULONG_MAX))
+ return;
+
/* lastlog is huge on systems with large UIDs, ignore SIGXFSZ */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_IGN;
ll.ll_time = t; /* ll_time is always 32bit */
if (cxt->tty_name)
- xstrncpy(ll.ll_line, cxt->tty_name, sizeof(ll.ll_line));
+ str2memcpy(ll.ll_line, cxt->tty_name, sizeof(ll.ll_line));
if (cxt->hostname)
- xstrncpy(ll.ll_host, cxt->hostname, sizeof(ll.ll_host));
+ str2memcpy(ll.ll_host, cxt->hostname, sizeof(ll.ll_host));
if (write_all(fd, (char *)&ll, sizeof(ll)))
warn(_("write lastlog failed"));
*/
static void log_utmp(struct login_context *cxt)
{
- struct utmp ut;
- struct utmp *utp;
+ struct utmpx ut;
+ struct utmpx *utp;
struct timeval tv;
- utmpname(_PATH_UTMP);
- setutent();
+ utmpxname(_PATH_UTMP);
+ setutxent();
/* Find pid in utmp.
*
* but some number calculated from the previous and current runlevel.)
* -- Michael Riepe <michael@stud.uni-hannover.de>
*/
- while ((utp = getutent()))
+ while ((utp = getutxent()))
if (utp->ut_pid == cxt->pid
&& utp->ut_type >= INIT_PROCESS
&& utp->ut_type <= DEAD_PROCESS)
/* If we can't find a pre-existing entry by pid, try by line.
* BSD network daemons may rely on this. */
if (utp == NULL && cxt->tty_name) {
- setutent();
+ setutxent();
ut.ut_type = LOGIN_PROCESS;
- strncpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
- utp = getutline(&ut);
+ str2memcpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
+ utp = getutxline(&ut);
}
/* If we can't find a pre-existing entry by pid and line, try it by id.
* Very stupid telnetd daemons don't set up utmp at all. (kzak) */
if (utp == NULL && cxt->tty_number) {
- setutent();
+ setutxent();
ut.ut_type = DEAD_PROCESS;
- strncpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
- utp = getutid(&ut);
+ str2memcpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
+ utp = getutxid(&ut);
}
if (utp)
memset(&ut, 0, sizeof(ut));
if (cxt->tty_number && ut.ut_id[0] == 0)
- strncpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
+ str2memcpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
if (cxt->username)
- strncpy(ut.ut_user, cxt->username, sizeof(ut.ut_user));
+ str2memcpy(ut.ut_user, cxt->username, sizeof(ut.ut_user));
if (cxt->tty_name)
- xstrncpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
+ str2memcpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
-#ifdef _HAVE_UT_TV /* in <utmpbits.h> included by <utmp.h> */
gettimeofday(&tv, NULL);
ut.ut_tv.tv_sec = tv.tv_sec;
ut.ut_tv.tv_usec = tv.tv_usec;
-#else
- {
- time_t t;
- time(&t);
- ut.ut_time = t; /* ut_time is not always a time_t */
- /* glibc2 #defines it as ut_tv.tv_sec */
- }
-#endif
ut.ut_type = USER_PROCESS;
ut.ut_pid = cxt->pid;
if (cxt->hostname) {
- xstrncpy(ut.ut_host, cxt->hostname, sizeof(ut.ut_host));
+ str2memcpy(ut.ut_host, cxt->hostname, sizeof(ut.ut_host));
if (*cxt->hostaddress)
memcpy(&ut.ut_addr_v6, cxt->hostaddress,
sizeof(ut.ut_addr_v6));
}
- pututline(&ut);
- endutent();
+ pututxline(&ut);
+ endutxent();
- updwtmp(_PATH_WTMP, &ut);
+ updwtmpx(_PATH_WTMP, &ut);
}
static void log_syslog(struct login_context *cxt)
}
}
-static struct passwd *get_passwd_entry(const char *username,
- char **pwdbuf,
- struct passwd *pwd)
-{
- struct passwd *res = NULL;
- int x;
-
- if (!pwdbuf || !username)
- return NULL;
-
- *pwdbuf = xrealloc(*pwdbuf, UL_GETPW_BUFSIZ);
-
- x = getpwnam_r(username, pwd, *pwdbuf, UL_GETPW_BUFSIZ, &res);
- if (!res) {
- errno = x;
- return NULL;
- }
- return res;
-}
-
/* encapsulate stupid "void **" pam_get_item() API */
-static int loginpam_get_username(pam_handle_t *pamh, char **name)
+static int loginpam_get_username(pam_handle_t *pamh, const char **name)
{
- const void *item = (void *)*name;
+ const void *item = (const void *)*name;
int rc;
rc = pam_get_item(pamh, PAM_USER, &item);
- *name = (char *)item;
+ *name = (const char *)item;
return rc;
}
}
/*
- * Composes "<host> login: " string; or returns "login: " if -H is given.
+ * Composes "<host> login: " string; or returns "login: " if -H is given or
+ * LOGIN_PLAIN_PROMPT=yes configured.
*/
static const char *loginpam_get_prompt(struct login_context *cxt)
{
char *prompt, *dflt_prompt = _("login: ");
size_t sz;
- if (cxt->nohost || !(host = get_thishost(cxt, NULL)))
+ if (cxt->nohost)
+ return dflt_prompt; /* -H on command line */
+
+ if (getlogindefs_bool("LOGIN_PLAIN_PROMPT", 0) == 1)
return dflt_prompt;
- sz = strlen(host) + 1 + strlen(dflt_prompt) + 1;
+ if (!(host = get_thishost(cxt, NULL)))
+ return dflt_prompt;
+ sz = strlen(host) + 1 + strlen(dflt_prompt) + 1;
prompt = xmalloc(sz);
snprintf(prompt, sz, "%s %s", host, dflt_prompt);
loginpam_err(pamh, rc);
/* We don't need the original username. We have to follow PAM. */
- free(cxt->username);
cxt->username = NULL;
cxt->pamh = pamh;
if (!cxt->username || !*cxt->username) {
warnx(_("\nSession setup problem, abort."));
- syslog(LOG_ERR, _("NULL user name in %s:%d. Abort."),
- __FUNCTION__, __LINE__);
+ syslog(LOG_ERR, _("NULL user name. Abort."));
pam_end(pamh, PAM_SYSTEM_ERR);
sleepexit(EXIT_FAILURE);
}
close(0);
close(1);
close(2);
+ free_getlogindefs_data();
+
sa.sa_handler = SIG_IGN;
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
/* destroy environment unless user has requested preservation (-p) */
if (!cxt->keep_env) {
- environ = (char **) xmalloc(sizeof(char *));
+ environ = xmalloc(sizeof(char *));
memset(environ, 0, sizeof(char *));
}
- setenv("HOME", pwd->pw_dir, 0); /* legal to override */
- setenv("USER", pwd->pw_name, 1);
- setenv("SHELL", pwd->pw_shell, 1);
- setenv("TERM", termenv ? termenv : "dumb", 1);
+ xsetenv("HOME", pwd->pw_dir, 0); /* legal to override */
+ xsetenv("USER", pwd->pw_name, 1);
+ xsetenv("SHELL", pwd->pw_shell, 1);
+ xsetenv("TERM", termenv ? termenv : "dumb", 1);
free(termenv);
- if (pwd->pw_uid)
- logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH);
+ if (pwd->pw_uid) {
+ if (logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH) != 0)
+ err(EXIT_FAILURE, _("failed to set the %s environment variable"), "PATH");
- else if (logindefs_setenv("PATH", "ENV_ROOTPATH", NULL) != 0)
- logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT);
+ } else if (logindefs_setenv("PATH", "ENV_ROOTPATH", NULL) != 0 &&
+ logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT) != 0) {
+ err(EXIT_FAILURE, _("failed to set the %s environment variable"), "PATH");
+ }
/* mailx will give a funny error msg if you forget this one */
len = snprintf(tmp, sizeof(tmp), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
- if (len > 0 && (size_t) len + 1 <= sizeof(tmp))
- setenv("MAIL", tmp, 0);
+ if (len > 0 && (size_t) len < sizeof(tmp))
+ xsetenv("MAIL", tmp, 0);
/* LOGNAME is not documented in login(1) but HP-UX 6.5 does it. We'll
* not allow modifying it.
*/
- setenv("LOGNAME", pwd->pw_name, 1);
+ xsetenv("LOGNAME", pwd->pw_name, 1);
env = pam_getenvlist(cxt->pamh);
for (i = 0; env && env[i]; i++)
} else if (info->ai_family == AF_INET6) {
struct sockaddr_in6 *sa =
(struct sockaddr_in6 *) info->ai_addr;
+#ifdef IN6_IS_ADDR_V4MAPPED
+ if (IN6_IS_ADDR_V4MAPPED(&sa->sin6_addr)) {
+ const uint8_t *bytes = sa->sin6_addr.s6_addr;
+ struct in_addr addr = { *(const in_addr_t *) (bytes + 12) };
- memcpy(cxt->hostaddress, &(sa->sin6_addr), sizeof(sa->sin6_addr));
+ memcpy(cxt->hostaddress, &addr, sizeof(struct in_addr));
+ } else
+#endif
+ memcpy(cxt->hostaddress, &(sa->sin6_addr), sizeof(sa->sin6_addr));
}
freeaddrinfo(info);
}
}
+static void __attribute__((__noreturn__)) usage(void)
+{
+ fputs(USAGE_HEADER, stdout);
+ printf(_(" %s [-p] [-h <host>] [-H] [[-f] <username>]\n"), program_invocation_short_name);
+ fputs(USAGE_SEPARATOR, stdout);
+ fputs(_("Begin a session on the system.\n"), stdout);
+
+ fputs(USAGE_OPTIONS, stdout);
+ puts(_(" -p do not destroy the environment"));
+ puts(_(" -f skip a second login authentication"));
+ puts(_(" -h <host> hostname to be used for utmp logging"));
+ puts(_(" -H suppress hostname in the login prompt"));
+ printf(" --help %s\n", USAGE_OPTSTR_HELP);
+ printf(" -V, --version %s\n", USAGE_OPTSTR_VERSION);
+ printf(USAGE_MAN_TAIL("login(1)"));
+ exit(EXIT_SUCCESS);
+}
+
int main(int argc, char **argv)
{
int c;
int childArgc = 0;
int retcode;
struct sigaction act;
-
- char *pwdbuf = NULL;
- struct passwd *pwd = NULL, _pwd;
+ struct passwd *pwd;
struct login_context cxt = {
.tty_mode = TTY_MODE, /* tty chmod() */
};
+ /* the only two longopts to satisfy UL standards */
+ enum { HELP_OPTION = CHAR_MAX + 1 };
+ static const struct option longopts[] = {
+ {"help", no_argument, NULL, HELP_OPTION},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+ };
+
timeout = (unsigned int)getlogindefs_num("LOGIN_TIMEOUT", LOGIN_TIMEOUT);
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ /* TRANSLATORS: The standard value for %u is 60. */
+ snprintf(timeout_msg, sizeof(timeout_msg),
+ _("%s: timed out after %u seconds"),
+ program_invocation_short_name, timeout);
+
signal(SIGALRM, timedout);
(void) sigaction(SIGALRM, NULL, &act);
act.sa_flags &= ~SA_RESTART;
signal(SIGQUIT, SIG_IGN);
signal(SIGINT, SIG_IGN);
- setlocale(LC_ALL, "");
- bindtextdomain(PACKAGE, LOCALEDIR);
- textdomain(PACKAGE);
-
setpriority(PRIO_PROCESS, 0, 0);
initproctitle(argc, argv);
* -h is used by other servers to pass the name of the remote
* host to login so that it may be placed in utmp and wtmp
*/
- while ((c = getopt(argc, argv, "fHh:pV")) != -1)
+ while ((c = getopt_long(argc, argv, "fHh:pV", longopts, NULL)) != -1)
switch (c) {
case 'f':
cxt.noauth = 1;
break;
case 'V':
- printf(UTIL_LINUX_VERSION);
- return EXIT_SUCCESS;
- case '?':
+ print_version(EXIT_SUCCESS);
+ case HELP_OPTION:
+ usage();
default:
- fprintf(stderr, _("Usage: login [-p] [-h <host>] [-H] [[-f] <username>]\n"));
- fputs(USAGE_SEPARATOR, stderr);
- fputs(_("Begin a session on the system.\n"), stderr);
- exit(EXIT_FAILURE);
+ errtryhelp(EXIT_FAILURE);
}
argc -= optind;
argv += optind;
if (*argv) {
char *p = *argv;
- cxt.username = xstrdup(p);
+
+ /* username from command line */
+ cxt.cmd_username = xstrdup(p);
+ /* used temporary, it'll be replaced by username from PAM or/and cxt->pwd */
+ cxt.username = cxt.cmd_username;
/* Wipe the name - some people mistype their password here. */
/* (Of course we are too late, but perhaps this helps a little...) */
*/
loginpam_acct(&cxt);
- if (!(cxt.pwd = get_passwd_entry(cxt.username, &pwdbuf, &_pwd))) {
+ cxt.pwd = xgetpwnam(cxt.username, &cxt.pwdbuf);
+ if (!cxt.pwd) {
warnx(_("\nSession setup problem, abort."));
- syslog(LOG_ERR, _("Invalid user name \"%s\" in %s:%d. Abort."),
- cxt.username, __FUNCTION__, __LINE__);
+ syslog(LOG_ERR, _("Invalid user name \"%s\". Abort."),
+ cxt.username);
pam_end(cxt.pamh, PAM_SYSTEM_ERR);
sleepexit(EXIT_FAILURE);
}
/* if the shell field has a space: treat it like a shell script */
if (strchr(pwd->pw_shell, ' ')) {
- buff = xmalloc(strlen(pwd->pw_shell) + 6);
-
- strcpy(buff, "exec ");
- strcat(buff, pwd->pw_shell);
+ xasprintf(&buff, "exec %s", pwd->pw_shell);
childArgv[childArgc++] = "/bin/sh";
childArgv[childArgc++] = "-sh";
childArgv[childArgc++] = "-c";