summaryrefslogtreecommitdiffstats
path: root/login-utils
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:33:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:33:30 +0000
commitc61e14d3a8412cd50d98aab604e607692c844c8a (patch)
tree4925aca0e6b64c8664ea2f3fdfa99a52dc93d5da /login-utils
parentAdding upstream version 2.39.3. (diff)
downloadutil-linux-c61e14d3a8412cd50d98aab604e607692c844c8a.tar.xz
util-linux-c61e14d3a8412cd50d98aab604e607692c844c8a.zip
Adding upstream version 2.40.upstream/2.40
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'login-utils')
-rw-r--r--login-utils/chfn.16
-rw-r--r--login-utils/chsh.16
-rw-r--r--login-utils/chsh.c18
-rw-r--r--login-utils/last.111
-rw-r--r--login-utils/last.1.adoc3
-rw-r--r--login-utils/last.c51
-rw-r--r--login-utils/login.118
-rw-r--r--login-utils/login.1.adoc10
-rw-r--r--login-utils/login.c108
-rw-r--r--login-utils/lslogins.16
-rw-r--r--login-utils/lslogins.c14
-rw-r--r--login-utils/newgrp.16
-rw-r--r--login-utils/newgrp.1.adoc6
-rw-r--r--login-utils/newgrp.c4
-rw-r--r--login-utils/nologin.86
-rw-r--r--login-utils/nologin.c4
-rw-r--r--login-utils/runuser.122
-rw-r--r--login-utils/runuser.1.adoc10
-rw-r--r--login-utils/su-common.c19
-rw-r--r--login-utils/su.127
-rw-r--r--login-utils/su.1.adoc23
-rw-r--r--login-utils/sulogin-consoles.c4
-rw-r--r--login-utils/sulogin-consoles.h4
-rw-r--r--login-utils/sulogin.86
-rw-r--r--login-utils/sulogin.c184
-rw-r--r--login-utils/utmpdump.16
-rw-r--r--login-utils/utmpdump.c6
-rw-r--r--login-utils/vipw.86
-rw-r--r--login-utils/vipw.c4
29 files changed, 417 insertions, 181 deletions
diff --git a/login-utils/chfn.1 b/login-utils/chfn.1
index 0792231..71cc157 100644
--- a/login-utils/chfn.1
+++ b/login-utils/chfn.1
@@ -2,12 +2,12 @@
.\" Title: chfn
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-11-21
+.\" Date: 2024-01-31
.\" Manual: User Commands
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "CHFN" "1" "2023-11-21" "util\-linux 2.39.3" "User Commands"
+.TH "CHFN" "1" "2024-01-31" "util\-linux 2.40" "User Commands"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/login-utils/chsh.1 b/login-utils/chsh.1
index f4f6025..33cd793 100644
--- a/login-utils/chsh.1
+++ b/login-utils/chsh.1
@@ -2,12 +2,12 @@
.\" Title: chsh
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-11-21
+.\" Date: 2024-01-31
.\" Manual: User Commands
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "CHSH" "1" "2023-11-21" "util\-linux 2.39.3" "User Commands"
+.TH "CHSH" "1" "2024-01-31" "util\-linux 2.40" "User Commands"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/login-utils/chsh.c b/login-utils/chsh.c
index 31750d5..19f0915 100644
--- a/login-utils/chsh.c
+++ b/login-utils/chsh.c
@@ -77,25 +77,13 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" -l, --list-shells print list of shells and exit\n"), fp);
fputs(USAGE_SEPARATOR, fp);
- printf(USAGE_HELP_OPTIONS(22));
+ fprintf(fp, USAGE_HELP_OPTIONS(22));
- printf(USAGE_MAN_TAIL("chsh(1)"));
+ fprintf(fp, USAGE_MAN_TAIL("chsh(1)"));
exit(EXIT_SUCCESS);
}
/*
- * print_shells () -- /etc/shells is outputted to stdout.
- */
-static void print_shells(void)
-{
- char *s;
-
- while ((s = getusershell()))
- printf("%s\n", s);
- endusershell();
-}
-
-/*
* parse_argv () --
* parse the command line arguments, and fill in "pinfo" with any
* information from the command line.
@@ -120,7 +108,7 @@ static void parse_argv(int argc, char **argv, struct sinfo *pinfo)
case 'h':
usage();
case 'l':
- print_shells();
+ print_shells(stdout, "%s\n");
exit(EXIT_SUCCESS);
case 's':
pinfo->shell = optarg;
diff --git a/login-utils/last.1 b/login-utils/last.1
index 0689f62..1f01d71 100644
--- a/login-utils/last.1
+++ b/login-utils/last.1
@@ -2,12 +2,12 @@
.\" Title: last
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-12-01
+.\" Date: 2024-03-20
.\" Manual: User Commands
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "LAST" "1" "2023-12-01" "util\-linux 2.39.3" "User Commands"
+.TH "LAST" "1" "2024-03-20" "util\-linux 2.40" "User Commands"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -95,6 +95,11 @@ Display the state of logins since the specified \fItime\fP. This is useful, e.g.
Display the state of logins until the specified \fItime\fP.
.RE
.sp
+\fB\-T\fP, \fB\-\-tab\-separated\fP
+.RS 4
+Use ASCII \fBtab\fP characters to separate the columns in the output instead of spaces.
+.RE
+.sp
\fB\-\-time\-format\fP \fIformat\fP
.RS 4
Define the output timestamp \fIformat\fP to be one of \fInotime\fP, \fIshort\fP, \fIfull\fP, or \fIiso\fP. The \fInotime\fP variant will not print any timestamps at all, \fIshort\fP is the default, and \fIfull\fP is the same as the \fB\-\-fulltimes\fP option. The \fIiso\fP variant will display the timestamp in ISO\-8601 format. The ISO format contains timezone information, making it preferable when printouts are investigated outside of the system.
diff --git a/login-utils/last.1.adoc b/login-utils/last.1.adoc
index 1d26ae1..a3b8afe 100644
--- a/login-utils/last.1.adoc
+++ b/login-utils/last.1.adoc
@@ -75,6 +75,9 @@ Display the state of logins since the specified _time_. This is useful, e.g., to
*-t*, *--until* _time_::
Display the state of logins until the specified _time_.
+*-T*, *--tab-separated*::
+Use ASCII *tab* characters to separate the columns in the output instead of spaces.
+
*--time-format* _format_::
Define the output timestamp _format_ to be one of _notime_, _short_, _full_, or _iso_. The _notime_ variant will not print any timestamps at all, _short_ is the default, and _full_ is the same as the *--fulltimes* option. The _iso_ variant will display the timestamp in ISO-8601 format. The ISO format contains timezone information, making it preferable when printouts are investigated outside of the system.
diff --git a/login-utils/last.c b/login-utils/last.c
index 37c6abe..f5a9fec 100644
--- a/login-utils/last.c
+++ b/login-utils/last.c
@@ -95,6 +95,7 @@ struct last_control {
time_t until; /* at what time to stop displaying the file */
time_t present; /* who where present at time_t */
unsigned int time_fmt; /* time format */
+ char separator; /* output separator */
};
/* Double linked list of struct utmp's */
@@ -111,6 +112,7 @@ enum {
R_NORMAL, /* Normal */
R_NOW, /* Still logged in */
R_REBOOT, /* Reboot record. */
+ R_REBOOT_CRASH, /* Reboot record without matching shutdown */
R_PHANTOM, /* No logout record but session is stale. */
R_TIMECHANGE /* NEW_TIME or OLD_TIME */
};
@@ -349,7 +351,10 @@ static int time_formatter(int fmt, char *dst, size_t dlen, time_t *when)
{
char buf[CTIME_BUFSIZ];
- ctime_r(when, buf);
+ if (!ctime_r(when, buf)) {
+ ret = -1;
+ break;
+ }
snprintf(dst, dlen, "%s", buf);
ret = rtrim_whitespace((unsigned char *) dst);
break;
@@ -469,6 +474,7 @@ static int list(const struct last_control *ctl, struct utmpx *p, time_t logout_t
switch(what) {
case R_CRASH:
+ case R_REBOOT_CRASH:
snprintf(logouttime, sizeof(logouttime), "- crash");
break;
case R_DOWN:
@@ -518,24 +524,24 @@ static int list(const struct last_control *ctl, struct utmpx *p, time_t logout_t
if (ctl->showhost) {
if (!ctl->altlist) {
len = snprintf(final, sizeof(final),
- "%-8.*s %-12.12s %-16.*s %-*.*s %-*.*s %s\n",
- ctl->name_len, p->ut_user, utline,
- ctl->domain_len, domain,
- fmt->in_len, fmt->in_len, logintime, fmt->out_len, fmt->out_len,
- logouttime, length);
+ "%-8.*s%c%-12.12s%c%-16.*s%c%-*.*s%c%-*.*s%c%s\n",
+ ctl->name_len, p->ut_user, ctl->separator, utline, ctl->separator,
+ ctl->domain_len, domain, ctl->separator,
+ fmt->in_len, fmt->in_len, logintime, ctl->separator, fmt->out_len, fmt->out_len,
+ logouttime, ctl->separator, length);
} else {
len = snprintf(final, sizeof(final),
- "%-8.*s %-12.12s %-*.*s %-*.*s %-12.12s %s\n",
- ctl->name_len, p->ut_user, utline,
- fmt->in_len, fmt->in_len, logintime, fmt->out_len, fmt->out_len,
- logouttime, length, domain);
+ "%-8.*s%c%-12.12s%c%-*.*s%c%-*.*s%c%-12.12s%c%s\n",
+ ctl->name_len, p->ut_user, ctl->separator, utline, ctl->separator,
+ fmt->in_len, fmt->in_len, logintime, ctl->separator, fmt->out_len, fmt->out_len,
+ logouttime, ctl->separator, length, ctl->separator, domain);
}
} else
len = snprintf(final, sizeof(final),
- "%-8.*s %-12.12s %-*.*s %-*.*s %s\n",
- ctl->name_len, p->ut_user, utline,
- fmt->in_len, fmt->in_len, logintime, fmt->out_len, fmt->out_len,
- logouttime, length);
+ "%-8.*s%c%-12.12s%c%-*.*s%c%-*.*s%c%s\n",
+ ctl->name_len, p->ut_user, ctl->separator, utline, ctl->separator,
+ fmt->in_len, fmt->in_len, logintime, ctl->separator, fmt->out_len, fmt->out_len,
+ logouttime, ctl->separator, length);
#if defined(__GLIBC__)
# if (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0)
@@ -582,6 +588,7 @@ static void __attribute__((__noreturn__)) usage(const struct last_control *ctl)
fputs(_(" -R, --nohostname don't display the hostname field\n"), out);
fputs(_(" -s, --since <time> display the lines since the specified time\n"), out);
fputs(_(" -t, --until <time> display the lines until the specified time\n"), out);
+ fputs(_(" -T, --tab-separated use tabs as delimiters\n"), out);
fputs(_(" -p, --present <time> display who were present at the specified time\n"), out);
fputs(_(" -w, --fullnames display full user and domain names\n"), out);
fputs(_(" -x, --system display system shutdown entries and run level changes\n"), out);
@@ -589,8 +596,8 @@ static void __attribute__((__noreturn__)) usage(const struct last_control *ctl)
" notime|short|full|iso\n"), out);
fputs(USAGE_SEPARATOR, out);
- printf(USAGE_HELP_OPTIONS(22));
- printf(USAGE_MAN_TAIL("last(1)"));
+ fprintf(out, USAGE_HELP_OPTIONS(22));
+ fprintf(out, USAGE_MAN_TAIL("last(1)"));
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
@@ -786,7 +793,10 @@ static void process_wtmp_file(const struct last_control *ctl,
break;
case BOOT_TIME:
strcpy(ut.ut_line, "system boot");
- quit = list(ctl, &ut, lastdown, R_REBOOT);
+ if (lastdown > lastboot && lastdown != currentdate)
+ quit = list(ctl, &ut, lastboot, R_REBOOT_CRASH);
+ else
+ quit = list(ctl, &ut, lastdown, R_REBOOT);
lastboot = ut.ut_tv.tv_sec;
down = 1;
break;
@@ -973,6 +983,7 @@ int main(int argc, char **argv)
{ "ip", no_argument, NULL, 'i' },
{ "fulltimes", no_argument, NULL, 'F' },
{ "fullnames", no_argument, NULL, 'w' },
+ { "tab-separated", no_argument, NULL, 'T' },
{ "time-format", required_argument, NULL, OPT_TIME_FORMAT },
{ NULL, 0, NULL, 0 }
};
@@ -990,8 +1001,9 @@ int main(int argc, char **argv)
* Which file do we want to read?
*/
ctl.lastb = strcmp(program_invocation_short_name, "lastb") == 0 ? 1 : 0;
+ ctl.separator = ' ';
while ((c = getopt_long(argc, argv,
- "hVf:n:RxadFit:p:s:0123456789w", long_opts, NULL)) != -1) {
+ "hVf:n:RxadFit:p:s:T0123456789w", long_opts, NULL)) != -1) {
err_exclusive_options(c, long_opts, excl, excl_st);
@@ -1055,6 +1067,9 @@ int main(int argc, char **argv)
case OPT_TIME_FORMAT:
ctl.time_fmt = which_time_format(optarg);
break;
+ case 'T':
+ ctl.separator = '\t';
+ break;
default:
errtryhelp(EXIT_FAILURE);
}
diff --git a/login-utils/login.1 b/login-utils/login.1
index 65331d3..112e58d 100644
--- a/login-utils/login.1
+++ b/login-utils/login.1
@@ -2,12 +2,12 @@
.\" Title: login
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-12-01
+.\" Date: 2024-03-20
.\" Manual: User Commands
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "LOGIN" "1" "2023-12-01" "util\-linux 2.39.3" "User Commands"
+.TH "LOGIN" "1" "2024-03-20" "util\-linux 2.40" "User Commands"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -46,7 +46,7 @@ The environment variable \fB$TERM\fP will be preserved, if it exists, else it wi
.sp
The environment variables defined by PAM are always preserved.
.sp
-Then the user\(cqs shell is started. If no shell is specified for the user in \fI/etc/passwd\fP, then \fI/bin/sh\fP is used. If there is no home directory specified in \fI/etc/passwd\fP, then \fI/\fP is used, followed by \fI.hushlogin\fP check as described below.
+Then the user\(cqs shell is started. If no shell is specified for the user in \fI/etc/passwd\fP, then \fI/bin/sh\fP is used. If the specified shell contains a space, it is treated as a shell script. If there is no home directory specified in \fI/etc/passwd\fP, then \fI/\fP is used, followed by \fI.hushlogin\fP check as described below.
.sp
If the file \fI.hushlogin\fP exists, then a "quiet" login is performed. This disables the checking of mail and the printing of the last login time and message of the day. Otherwise, if \fI/var/log/lastlog\fP exists, the last login time is printed, and the current login is recorded.
.SH "OPTIONS"
@@ -189,6 +189,16 @@ If set, it will be used to define the PATH environment variable when the superus
\fI/etc/pam.d/remote\fP,
\fI/etc/hushlogins\fP,
\fI$HOME/.hushlogin\fP
+.SH "CREDENTIALS"
+.sp
+\fBlogin\fP supports configuration via systemd credentials (see \c
+.URL "https://systemd.io/CREDENTIALS/" "" ")."
+\fBlogin\fP reads the following systemd credentials:
+.sp
+\fBlogin.noauth\fP (boolean)
+.RS 4
+If set, configures \fBlogin\fP to skip login authentication, similarly to the \fB\-f\fP option.
+.RE
.SH "BUGS"
.sp
The undocumented BSD \fB\-r\fP option is not supported. This may be required by some \fBrlogind\fP(8) programs.
diff --git a/login-utils/login.1.adoc b/login-utils/login.1.adoc
index a3404f3..7a7b82d 100644
--- a/login-utils/login.1.adoc
+++ b/login-utils/login.1.adoc
@@ -32,7 +32,7 @@ The environment variable *$TERM* will be preserved, if it exists, else it will b
The environment variables defined by PAM are always preserved.
-Then the user's shell is started. If no shell is specified for the user in _/etc/passwd_, then _/bin/sh_ is used. If there is no home directory specified in _/etc/passwd_, then _/_ is used, followed by _.hushlogin_ check as described below.
+Then the user's shell is started. If no shell is specified for the user in _/etc/passwd_, then _/bin/sh_ is used. If the specified shell contains a space, it is treated as a shell script. If there is no home directory specified in _/etc/passwd_, then _/_ is used, followed by _.hushlogin_ check as described below.
If the file _.hushlogin_ exists, then a "quiet" login is performed. This disables the checking of mail and the printing of the last login time and message of the day. Otherwise, if _/var/log/lastlog_ exists, the last login time is printed, and the current login is recorded.
@@ -148,6 +148,14 @@ _/etc/pam.d/remote_,
_/etc/hushlogins_,
_$HOME/.hushlogin_
+== CREDENTIALS
+
+*login* supports configuration via systemd credentials (see https://systemd.io/CREDENTIALS/). *login* reads the following systemd credentials:
+
+*login.noauth* (boolean)::
+
+If set, configures *login* to skip login authentication, similarly to the *-f* option.
+
== BUGS
The undocumented BSD *-r* option is not supported. This may be required by some *rlogind*(8) programs.
diff --git a/login-utils/login.c b/login-utils/login.c
index 1812b90..c8544f6 100644
--- a/login-utils/login.c
+++ b/login-utils/login.c
@@ -46,6 +46,7 @@
#include <grp.h>
#include <pwd.h>
#include <utmpx.h>
+#include <path.h>
#ifdef HAVE_LASTLOG_H
# include <lastlog.h>
@@ -172,9 +173,10 @@ static void __attribute__((__noreturn__))
struct termios ti;
/* reset echo */
- tcgetattr(0, &ti);
- ti.c_lflag |= ECHO;
- tcsetattr(0, TCSANOW, &ti);
+ if (tcgetattr(0, &ti) >= 0) {
+ ti.c_lflag |= ECHO;
+ tcsetattr(0, TCSANOW, &ti);
+ }
_exit(EXIT_SUCCESS); /* %% */
}
@@ -508,12 +510,14 @@ static void chown_tty(struct login_context *cxt)
static void init_tty(struct login_context *cxt)
{
struct stat st;
- struct termios tt, ttt;
- struct winsize ws;
+ struct termios tt, ttt = { 0 };
+ struct winsize ws = { 0 };
+ int fd;
cxt->tty_mode = (mode_t) getlogindefs_num("TTYPERM", TTY_MODE);
get_terminal_name(&cxt->tty_path, &cxt->tty_name, &cxt->tty_number);
+ fd = get_terminal_stdfd();
/*
* In case login is suid it was possible to use a hardlink as stdin
@@ -526,7 +530,7 @@ static void init_tty(struct login_context *cxt)
if (!cxt->tty_path || !*cxt->tty_path ||
lstat(cxt->tty_path, &st) != 0 || !S_ISCHR(st.st_mode) ||
(st.st_nlink > 1 && strncmp(cxt->tty_path, "/dev/", 5) != 0) ||
- access(cxt->tty_path, R_OK | W_OK) != 0) {
+ access(cxt->tty_path, R_OK | W_OK) != 0 || fd == -EINVAL) {
syslog(LOG_ERR, _("FATAL: bad tty"));
sleepexit(EXIT_FAILURE);
@@ -542,15 +546,20 @@ static void init_tty(struct login_context *cxt)
/* The TTY size might be reset to 0x0 by the kernel when we close the stdin/stdout/stderr file
* descriptors so let's save the size now so we can reapply it later */
- memset(&ws, 0, sizeof(struct winsize));
- if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
+ if (ioctl(fd, TIOCGWINSZ, &ws) < 0) {
syslog(LOG_WARNING, _("TIOCGWINSZ ioctl failed: %m"));
+ ws.ws_row = 0;
+ ws.ws_col = 0;
+ }
- tcgetattr(0, &tt);
- ttt = tt;
- ttt.c_cflag &= ~HUPCL;
+ if (tcgetattr(fd, &tt) >= 0) {
+ ttt = tt;
+ ttt.c_cflag &= ~HUPCL;
+ } else {
+ ttt.c_cflag = HUPCL;
+ }
- if ((fchown(0, 0, 0) || fchmod(0, cxt->tty_mode)) && errno != EROFS) {
+ if ((fchown(fd, 0, 0) || fchmod(fd, cxt->tty_mode)) && errno != EROFS) {
syslog(LOG_ERR, _("FATAL: %s: change permissions failed: %m"),
cxt->tty_path);
@@ -558,7 +567,8 @@ static void init_tty(struct login_context *cxt)
}
/* Kill processes left on this tty */
- tcsetattr(0, TCSANOW, &ttt);
+ if ((ttt.c_cflag & HUPCL) == 0)
+ tcsetattr(fd, TCSANOW, &ttt);
/*
* Let's close file descriptors before vhangup
@@ -576,7 +586,8 @@ static void init_tty(struct login_context *cxt)
open_tty(cxt->tty_path);
/* restore tty modes */
- tcsetattr(0, TCSAFLUSH, &tt);
+ if ((ttt.c_cflag & HUPCL) == 0)
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt);
/* Restore tty size */
if ((ws.ws_row > 0 || ws.ws_col > 0)
@@ -634,16 +645,16 @@ static void log_audit(struct login_context *cxt, int status)
if (!pwd && cxt->username)
pwd = getpwnam(cxt->username);
- audit_log_acct_message(audit_fd,
- AUDIT_USER_LOGIN,
- NULL,
- "login",
- cxt->username ? cxt->username : "(unknown)",
- pwd ? pwd->pw_uid : (unsigned int)-1,
- cxt->hostname,
- NULL,
- cxt->tty_name,
- status);
+ ignore_result( audit_log_acct_message(audit_fd,
+ AUDIT_USER_LOGIN,
+ NULL,
+ "login",
+ cxt->username ? cxt->username : "(unknown)",
+ pwd ? pwd->pw_uid : (unsigned int)-1,
+ cxt->hostname,
+ NULL,
+ cxt->tty_name,
+ status) );
close(audit_fd);
}
@@ -850,8 +861,7 @@ static void loginpam_err(pam_handle_t *pamh, int retcode)
static const char *loginpam_get_prompt(struct login_context *cxt)
{
const char *host;
- char *prompt, *dflt_prompt = _("login: ");
- size_t sz;
+ char *prompt = NULL, *dflt_prompt = _("login: ");
if (cxt->nohost)
return dflt_prompt; /* -H on command line */
@@ -862,9 +872,7 @@ static const char *loginpam_get_prompt(struct login_context *cxt)
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);
+ xasprintf(&prompt, "%s %s", host, dflt_prompt);
return prompt;
}
@@ -1283,6 +1291,28 @@ static void __attribute__((__noreturn__)) usage(void)
exit(EXIT_SUCCESS);
}
+static void load_credentials(struct login_context *cxt) {
+ char str[32] = { 0 };
+ char *env;
+ struct path_cxt *pc;
+
+ env = safe_getenv("CREDENTIALS_DIRECTORY");
+ if (!env)
+ return;
+
+ pc = ul_new_path("%s", env);
+ if (!pc) {
+ syslog(LOG_WARNING, _("failed to initialize path context"));
+ return;
+ }
+
+ if (ul_path_read_buffer(pc, str, sizeof(str), "login.noauth") > 0
+ && *str && strcmp(str, "yes") == 0)
+ cxt->noauth = 1;
+
+ ul_unref_path(pc);
+}
+
static void initialize(int argc, char **argv, struct login_context *cxt)
{
int c;
@@ -1318,6 +1348,8 @@ static void initialize(int argc, char **argv, struct login_context *cxt)
setpriority(PRIO_PROCESS, 0, 0);
process_title_init(argc, argv);
+ load_credentials(cxt);
+
while ((c = getopt_long(argc, argv, "fHh:pV", longopts, NULL)) != -1)
switch (c) {
case 'f':
@@ -1388,6 +1420,7 @@ int main(int argc, char **argv)
.conv = { openpam_ttyconv, NULL } /* OpenPAM conversation function */
#endif
};
+ bool script;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
@@ -1512,7 +1545,9 @@ int main(int argc, char **argv)
}
/* if the shell field has a space: treat it like a shell script */
- if (strchr(pwd->pw_shell, ' ')) {
+ script = strchr(pwd->pw_shell, ' ') != NULL;
+
+ if (script) {
char *buff;
xasprintf(&buff, "exec %s", pwd->pw_shell);
@@ -1521,14 +1556,13 @@ int main(int argc, char **argv)
child_argv[child_argc++] = "-c";
child_argv[child_argc++] = buff;
} else {
- char tbuf[PATH_MAX + 2], *p;
-
- tbuf[0] = '-';
- xstrncpy(tbuf + 1, ((p = strrchr(pwd->pw_shell, '/')) ?
- p + 1 : pwd->pw_shell), sizeof(tbuf) - 1);
+ char *buff, *p;
+ xasprintf(&buff, "-%.*s", PATH_MAX,
+ ((p = strrchr(pwd->pw_shell, '/')) ?
+ p + 1 : pwd->pw_shell));
child_argv[child_argc++] = pwd->pw_shell;
- child_argv[child_argc++] = xstrdup(tbuf);
+ child_argv[child_argc++] = buff;
}
child_argv[child_argc++] = NULL;
@@ -1538,7 +1572,7 @@ int main(int argc, char **argv)
execvp(child_argv[0], child_argv + 1);
- if (!strcmp(child_argv[0], "/bin/sh"))
+ if (script)
warn(_("couldn't exec shell script"));
else
warn(_("no shell"));
diff --git a/login-utils/lslogins.1 b/login-utils/lslogins.1
index e4f366f..d60be27 100644
--- a/login-utils/lslogins.1
+++ b/login-utils/lslogins.1
@@ -2,12 +2,12 @@
.\" Title: lslogins
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-11-30
+.\" Date: 2024-01-31
.\" Manual: User Commands
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "LSLOGINS" "1" "2023-11-30" "util\-linux 2.39.3" "User Commands"
+.TH "LSLOGINS" "1" "2024-01-31" "util\-linux 2.40" "User Commands"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/login-utils/lslogins.c b/login-utils/lslogins.c
index effaba4..217f3f3 100644
--- a/login-utils/lslogins.c
+++ b/login-utils/lslogins.c
@@ -64,7 +64,7 @@
* column description
*/
struct lslogins_coldesc {
- const char *name;
+ const char * const name;
const char *help;
const char *pretty_name;
@@ -492,7 +492,7 @@ static int parse_utmpx(const char *path, size_t *nrecords, struct utmpx **record
* just fallback only */
if (stat(path, &st) == 0 && (size_t) st.st_size >= sizeof(struct utmpx)) {
imax = st.st_size / sizeof(struct utmpx);
- ary = xmalloc(imax * sizeof(struct utmpx));
+ ary = xreallocarray(NULL, imax, sizeof(struct utmpx));
}
for (i = 0; ; i++) {
@@ -505,7 +505,7 @@ static int parse_utmpx(const char *path, size_t *nrecords, struct utmpx **record
break;
}
if (i == imax)
- ary = xrealloc(ary, (imax *= 2) * sizeof(struct utmpx));
+ ary = xreallocarray(ary, imax *= 2, sizeof(struct utmpx));
ary[i] = *u;
}
@@ -993,7 +993,7 @@ static int get_ulist(struct lslogins_control *ctl, char *logins, char *groups)
(*ar)[i++] = xstrdup(u);
if (i == *arsiz)
- *ar = xrealloc(*ar, sizeof(char *) * (*arsiz += 32));
+ *ar = xreallocarray(*ar, *arsiz += 32, sizeof(char *));
}
ctl->ulist_on = 1;
}
@@ -1018,7 +1018,7 @@ static int get_ulist(struct lslogins_control *ctl, char *logins, char *groups)
(*ar)[i++] = xstrdup(u);
if (i == *arsiz)
- *ar = xrealloc(*ar, sizeof(char *) * (*arsiz += 32));
+ *ar = xreallocarray(*ar, *arsiz += 32, sizeof(char *));
}
}
ctl->ulist_on = 1;
@@ -1470,13 +1470,13 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" --btmp-file <path> set an alternate path for btmp\n"), out);
fputs(_(" --lastlog <path> set an alternate path for lastlog\n"), out);
fputs(USAGE_SEPARATOR, out);
- printf(USAGE_HELP_OPTIONS(26));
+ fprintf(out, USAGE_HELP_OPTIONS(26));
fputs(USAGE_COLUMNS, out);
for (i = 0; i < ARRAY_SIZE(coldescs); i++)
fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help));
- printf(USAGE_MAN_TAIL("lslogins(1)"));
+ fprintf(out, USAGE_MAN_TAIL("lslogins(1)"));
exit(EXIT_SUCCESS);
}
diff --git a/login-utils/newgrp.1 b/login-utils/newgrp.1
index bc3bcd4..9c15c67 100644
--- a/login-utils/newgrp.1
+++ b/login-utils/newgrp.1
@@ -2,12 +2,12 @@
.\" Title: newgrp
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-03-20
.\" Manual: User Commands
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "NEWGRP" "1" "2023-10-23" "util\-linux 2.39.3" "User Commands"
+.TH "NEWGRP" "1" "2024-03-20" "util\-linux 2.40" "User Commands"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/login-utils/newgrp.1.adoc b/login-utils/newgrp.1.adoc
index 0e73eaf..e9a90d3 100644
--- a/login-utils/newgrp.1.adoc
+++ b/login-utils/newgrp.1.adoc
@@ -1,4 +1,8 @@
-//po4a: entry man manual
+//
+// No copyright is claimed. This code is in the public domain; do with
+// it what you wish.
+//
+// po4a: entry man manual
// Original author unknown. This man page is in the public domain.
// Modified Sat Oct 9 17:46:48 1993 by faith@cs.unc.edu
= newgrp(1)
diff --git a/login-utils/newgrp.c b/login-utils/newgrp.c
index 2acbc91..e71a9e1 100644
--- a/login-utils/newgrp.c
+++ b/login-utils/newgrp.c
@@ -178,8 +178,8 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_("Log in to a new group.\n"), out);
fputs(USAGE_OPTIONS, out);
- printf(USAGE_HELP_OPTIONS(16));
- printf(USAGE_MAN_TAIL("newgrp(1)"));
+ fprintf(out, USAGE_HELP_OPTIONS(16));
+ fprintf(out, USAGE_MAN_TAIL("newgrp(1)"));
exit(EXIT_SUCCESS);
}
diff --git a/login-utils/nologin.8 b/login-utils/nologin.8
index f2ecf5d..201f832 100644
--- a/login-utils/nologin.8
+++ b/login-utils/nologin.8
@@ -2,12 +2,12 @@
.\" Title: nologin
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-01-31
.\" Manual: System Administration
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "NOLOGIN" "8" "2023-10-23" "util\-linux 2.39.3" "System Administration"
+.TH "NOLOGIN" "8" "2024-01-31" "util\-linux 2.40" "System Administration"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/login-utils/nologin.c b/login-utils/nologin.c
index ecbd0d2..85ef3f0 100644
--- a/login-utils/nologin.c
+++ b/login-utils/nologin.c
@@ -32,9 +32,9 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(USAGE_OPTIONS, out);
fputs(_(" -c, --command <command> does nothing (for compatibility with su -c)\n"), out);
- printf(USAGE_HELP_OPTIONS(26));
+ fprintf(out, USAGE_HELP_OPTIONS(26));
- printf(USAGE_MAN_TAIL("nologin(8)"));
+ fprintf(out, USAGE_MAN_TAIL("nologin(8)"));
exit(EXIT_FAILURE);
}
diff --git a/login-utils/runuser.1 b/login-utils/runuser.1
index 3130a03..7fed3d5 100644
--- a/login-utils/runuser.1
+++ b/login-utils/runuser.1
@@ -2,12 +2,12 @@
.\" Title: runuser
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-03-20
.\" Manual: User Commands
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "RUNUSER" "1" "2023-10-23" "util\-linux 2.39.3" "User Commands"
+.TH "RUNUSER" "1" "2024-03-20" "util\-linux 2.40" "User Commands"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -118,6 +118,11 @@ sets argv[0] of the shell to \*(Aq\fB\-\fP\*(Aq in order to make the shell a log
.RE
.RE
.sp
+\fB\-m\fP, \fB\-p\fP, \fB\-\-preserve\-environment\fP
+.RS 4
+Preserve the entire environment, i.e., do not set \fBHOME\fP, \fBSHELL\fP, \fBUSER\fP or \fBLOGNAME\fP. The option is ignored if the option \fB\-\-login\fP is specified.
+.RE
+.sp
\fB\-P\fP, \fB\-\-pty\fP
.RS 4
Create a pseudo\-terminal for the session. The independent terminal provides better security as the user does not share a terminal with the original session. This can be used to avoid TIOCSTI ioctl terminal injection and other security attacks against terminal file descriptors. The entire session can also be moved to the background (e.g., \fBrunuser \-\-pty\fP \fB\-u\fP \fIusername\fP \fB\-\-\fP \fIcommand\fP \fB&\fP). If the pseudo\-terminal is enabled, then \fBrunuser\fP works as a proxy between the sessions (sync stdin and stdout).
@@ -125,11 +130,6 @@ Create a pseudo\-terminal for the session. The independent terminal provides bet
This feature is mostly designed for interactive sessions. If the standard input is not a terminal, but for example a pipe (e.g., \fBecho "date" | runuser \-\-pty \-u\fP \fIuser\fP), then the \fBECHO\fP flag for the pseudo\-terminal is disabled to avoid messy output.
.RE
.sp
-\fB\-m\fP, \fB\-p\fP, \fB\-\-preserve\-environment\fP
-.RS 4
-Preserve the entire environment, i.e., do not set \fBHOME\fP, \fBSHELL\fP, \fBUSER\fP or \fBLOGNAME\fP. The option is ignored if the option \fB\-\-login\fP is specified.
-.RE
-.sp
\fB\-s\fP, \fB\-\-shell\fP=\fIshell\fP
.RS 4
Run the specified \fIshell\fP instead of the default. The shell to run is selected according to the following rules, in order:
@@ -186,6 +186,12 @@ If the target user has a restricted shell (i.e., not listed in \fI/etc/shells\fP
Same as \fB\-c\fP, but do not create a new session. (Discouraged.)
.RE
.sp
+\fB\-T\fP, \fB\-\-no\-pty\fP*
+.RS 4
+Do not create a pseudo\-terminal, opposite of \fB\-\-pty\fP and \fB\-P\fP.
+Note that running without a pseudo\-terminal opens the security risk of privilege escalation through TIOCSTI/TIOCLINUX ioctl command injection.
+.RE
+.sp
\fB\-w\fP, \fB\-\-whitelist\-environment\fP=\fIlist\fP
.RS 4
Don\(cqt reset the environment variables specified in the comma\-separated \fIlist\fP when clearing the environment for \fB\-\-login\fP. The whitelist is ignored for the environment variables \fBHOME\fP, \fBSHELL\fP, \fBUSER\fP, \fBLOGNAME\fP, and \fBPATH\fP.
diff --git a/login-utils/runuser.1.adoc b/login-utils/runuser.1.adoc
index 3872d8d..aea95f7 100644
--- a/login-utils/runuser.1.adoc
+++ b/login-utils/runuser.1.adoc
@@ -52,14 +52,14 @@ Start the shell as a login shell with an environment similar to a real login:
* changes to the target user's home directory
* sets argv[0] of the shell to '*-*' in order to make the shell a login shell
+*-m*, *-p*, *--preserve-environment*::
+Preserve the entire environment, i.e., do not set *HOME*, *SHELL*, *USER* or *LOGNAME*. The option is ignored if the option *--login* is specified.
+
*-P*, *--pty*::
Create a pseudo-terminal for the session. The independent terminal provides better security as the user does not share a terminal with the original session. This can be used to avoid TIOCSTI ioctl terminal injection and other security attacks against terminal file descriptors. The entire session can also be moved to the background (e.g., *runuser --pty* *-u* _username_ *--* _command_ *&*). If the pseudo-terminal is enabled, then *runuser* works as a proxy between the sessions (sync stdin and stdout).
+
This feature is mostly designed for interactive sessions. If the standard input is not a terminal, but for example a pipe (e.g., *echo "date" | runuser --pty -u* _user_), then the *ECHO* flag for the pseudo-terminal is disabled to avoid messy output.
-*-m*, *-p*, *--preserve-environment*::
-Preserve the entire environment, i.e., do not set *HOME*, *SHELL*, *USER* or *LOGNAME*. The option is ignored if the option *--login* is specified.
-
*-s*, *--shell*=_shell_::
Run the specified _shell_ instead of the default. The shell to run is selected according to the following rules, in order:
@@ -73,6 +73,10 @@ If the target user has a restricted shell (i.e., not listed in _/etc/shells_), t
**--session-command=**__command__::
Same as *-c*, but do not create a new session. (Discouraged.)
+*-T*, *--no-pty**::
+Do not create a pseudo-terminal, opposite of *--pty* and *-P*.
+Note that running without a pseudo-terminal opens the security risk of privilege escalation through TIOCSTI/TIOCLINUX ioctl command injection.
+
*-w*, *--whitelist-environment*=_list_::
Don't reset the environment variables specified in the comma-separated _list_ when clearing the environment for *--login*. The whitelist is ignored for the environment variables *HOME*, *SHELL*, *USER*, *LOGNAME*, and *PATH*.
diff --git a/login-utils/su-common.c b/login-utils/su-common.c
index b674920..9bc0231 100644
--- a/login-utils/su-common.c
+++ b/login-utils/su-common.c
@@ -26,6 +26,7 @@
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
+#include <libgen.h>
#include <security/pam_appl.h>
#ifdef HAVE_SECURITY_PAM_MISC_H
# include <security/pam_misc.h>
@@ -289,7 +290,7 @@ static void log_syslog(struct su_context *su, bool successful)
{
DBG(LOG, ul_debug("syslog logging"));
- openlog(program_invocation_short_name, LOG_PID, LOG_AUTH);
+ openlog(su->runuser ? "runuser" : "su", LOG_PID, LOG_AUTH);
syslog(LOG_NOTICE, "%s(to %s) %s on %s",
successful ? "" :
su->runuser ? "FAILED RUNUSER " : "FAILED SU ",
@@ -834,23 +835,25 @@ static void run_shell(
size_t n_args = 1 + su->fast_startup + 2 * ! !command + n_additional_args + 1;
const char **args = xcalloc(n_args, sizeof *args);
size_t argno = 1;
+ char *tmp;
DBG(MISC, ul_debug("starting shell [shell=%s, command=\"%s\"%s%s]",
shell, command,
su->simulate_login ? " login" : "",
su->fast_startup ? " fast-start" : ""));
+ tmp = xstrdup(shell);
if (su->simulate_login) {
char *arg0;
char *shell_basename;
- shell_basename = basename(shell);
+ shell_basename = basename(tmp);
arg0 = xmalloc(strlen(shell_basename) + 2);
arg0[0] = '-';
strcpy(arg0 + 1, shell_basename);
args[0] = arg0;
} else
- args[0] = basename(shell);
+ args[0] = basename(tmp);
if (su->fast_startup)
args[argno++] = "-f";
@@ -898,6 +901,7 @@ static void usage_common(void)
fputs(_(" -f, --fast pass -f to the shell (for csh or tcsh)\n"), stdout);
fputs(_(" -s, --shell <shell> run <shell> if /etc/shells allows it\n"), stdout);
fputs(_(" -P, --pty create a new pseudo-terminal\n"), stdout);
+ fputs(_(" -T, --no-pty do not create a new pseudo-terminal (bad security!)\n"), stdout);
fputs(USAGE_SEPARATOR, stdout);
printf(USAGE_HELP_OPTIONS(33));
@@ -1019,7 +1023,7 @@ static gid_t add_supp_group(const char *name, gid_t **groups, size_t *ngroups)
DBG(MISC, ul_debug("add %s group [name=%s, GID=%d]", name, gr->gr_name, (int) gr->gr_gid));
- *groups = xrealloc(*groups, sizeof(gid_t) * (*ngroups + 1));
+ *groups = xreallocarray(*groups, *ngroups + 1, sizeof(gid_t));
(*groups)[*ngroups] = gr->gr_gid;
(*ngroups)++;
@@ -1053,6 +1057,7 @@ int su_main(int argc, char **argv, int mode)
{"login", no_argument, NULL, 'l'},
{"preserve-environment", no_argument, NULL, 'p'},
{"pty", no_argument, NULL, 'P'},
+ {"no-pty", no_argument, NULL, 'T'},
{"shell", required_argument, NULL, 's'},
{"group", required_argument, NULL, 'g'},
{"supp-group", required_argument, NULL, 'G'},
@@ -1078,7 +1083,7 @@ int su_main(int argc, char **argv, int mode)
su->conv.appdata_ptr = (void *) su;
while ((optc =
- getopt_long(argc, argv, "c:fg:G:lmpPs:u:hVw:", longopts,
+ getopt_long(argc, argv, "c:fg:G:lmpPTs:u:hVw:", longopts,
NULL)) != -1) {
err_exclusive_options(optc, longopts, excl, excl_st);
@@ -1128,6 +1133,10 @@ int su_main(int argc, char **argv, int mode)
#endif
break;
+ case 'T':
+ su->force_pty = 0;
+ break;
+
case 's':
shell = optarg;
break;
diff --git a/login-utils/su.1 b/login-utils/su.1
index a768834..1ada7a0 100644
--- a/login-utils/su.1
+++ b/login-utils/su.1
@@ -2,12 +2,12 @@
.\" Title: su
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-03-20
.\" Manual: User Commands
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "SU" "1" "2023-10-23" "util\-linux 2.39.3" "User Commands"
+.TH "SU" "1" "2024-03-20" "util\-linux 2.40" "User Commands"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -71,7 +71,14 @@ Specify a supplementary group. This option is available to the root user only. T
.sp
\fB\-\fP, \fB\-l\fP, \fB\-\-login\fP
.RS 4
-Start the shell as a login shell with an environment similar to a real login:
+Start the shell as a login shell with an environment similar to a real login.
+.sp
+Note that on systemd\-based systems, a new session may be defined as a real
+entry point to the system. However, \fBsu\fP does not create a real session (by
+PAM) from this point of view. You need to use tools like \fBsystemd\-run\fP or
+\fBmachinectl\fP to initiate a complete, real session.
+.sp
+\fBsu\fP does:
.sp
.RS 4
.ie n \{\
@@ -132,7 +139,9 @@ This feature is mostly designed for interactive sessions. If the standard input
.sp
\fB\-s\fP, \fB\-\-shell\fP=\fIshell\fP
.RS 4
-Run the specified \fIshell\fP instead of the default. The shell to run is selected according to the following rules, in order:
+Run the specified \fIshell\fP instead of the default. If the target user has a restricted shell (i.e., not listed in \fI/etc/shells\fP), the \fB\-\-shell\fP option and the \fBSHELL\fP environment variables are ignored unless the calling user is root.
+.sp
+The shell to run is selected according to the following rules, in order:
.sp
.RS 4
.ie n \{\
@@ -179,13 +188,17 @@ the shell listed in the passwd entry of the target user
.RE
.RE
.sp
-If the target user has a restricted shell (i.e., not listed in \fI/etc/shells\fP), the \fB\-\-shell\fP option and the \fBSHELL\fP environment variables are ignored unless the calling user is root.
-.sp
\fB\-\-session\-command=\fP\fIcommand\fP
.RS 4
Same as \fB\-c\fP, but do not create a new session. (Discouraged.)
.RE
.sp
+\fB\-T\fP, \fB\-\-no\-pty\fP*
+.RS 4
+Do not create a pseudo\-terminal, opposite of \fB\-\-pty\fP and \fB\-P\fP.
+Note that running without a pseudo\-terminal opens the security risk of privilege escalation through TIOCSTI/TIOCLINUX ioctl command injection.
+.RE
+.sp
\fB\-w\fP, \fB\-\-whitelist\-environment\fP=\fIlist\fP
.RS 4
Don\(cqt reset the environment variables specified in the comma\-separated \fIlist\fP when clearing the environment for \fB\-\-login\fP. The whitelist is ignored for the environment variables \fBHOME\fP, \fBSHELL\fP, \fBUSER\fP, \fBLOGNAME\fP, and \fBPATH\fP.
diff --git a/login-utils/su.1.adoc b/login-utils/su.1.adoc
index 36a892f..2db2e04 100644
--- a/login-utils/su.1.adoc
+++ b/login-utils/su.1.adoc
@@ -46,8 +46,15 @@ Specify the primary group. This option is available to the root user only.
Specify a supplementary group. This option is available to the root user only. The first specified supplementary group is also used as a primary group if the option *--group* is not specified.
*-*, *-l*, *--login*::
-Start the shell as a login shell with an environment similar to a real login:
-
+Start the shell as a login shell with an environment similar to a real login.
++
+Note that on systemd-based systems, a new session may be defined as a real
+entry point to the system. However, *su* does not create a real session (by
+PAM) from this point of view. You need to use tools like *systemd-run* or
+*machinectl* to initiate a complete, real session.
++
+*su* does:
++
* clears all the environment variables except *TERM* and variables specified by *--whitelist-environment*
* initializes the environment variables *HOME*, *SHELL*, *USER*, *LOGNAME*, and *PATH*
* changes to the target user's home directory
@@ -62,18 +69,22 @@ Create a pseudo-terminal for the session. The independent terminal provides bett
This feature is mostly designed for interactive sessions. If the standard input is not a terminal, but for example a pipe (e.g., *echo "date" | su --pty*), then the *ECHO* flag for the pseudo-terminal is disabled to avoid messy output.
*-s*, **--shell**=__shell__::
-Run the specified _shell_ instead of the default. The shell to run is selected according to the following rules, in order:
-
+Run the specified _shell_ instead of the default. If the target user has a restricted shell (i.e., not listed in _/etc/shells_), the *--shell* option and the *SHELL* environment variables are ignored unless the calling user is root.
++
+The shell to run is selected according to the following rules, in order:
++
* the shell specified with *--shell*
* the shell specified in the environment variable *SHELL*, if the *--preserve-environment* option is used
* the shell listed in the passwd entry of the target user
* /bin/sh
-If the target user has a restricted shell (i.e., not listed in _/etc/shells_), the *--shell* option and the *SHELL* environment variables are ignored unless the calling user is root.
-
**--session-command=**__command__::
Same as *-c*, but do not create a new session. (Discouraged.)
+*-T*, *--no-pty**::
+Do not create a pseudo-terminal, opposite of *--pty* and *-P*.
+Note that running without a pseudo-terminal opens the security risk of privilege escalation through TIOCSTI/TIOCLINUX ioctl command injection.
+
*-w*, **--whitelist-environment**=__list__::
Don't reset the environment variables specified in the comma-separated _list_ when clearing the environment for *--login*. The whitelist is ignored for the environment variables *HOME*, *SHELL*, *USER*, *LOGNAME*, and *PATH*.
diff --git a/login-utils/sulogin-consoles.c b/login-utils/sulogin-consoles.c
index 9ae5255..0dca949 100644
--- a/login-utils/sulogin-consoles.c
+++ b/login-utils/sulogin-consoles.c
@@ -341,6 +341,10 @@ int append_console(struct list_head *consoles, const char * const name)
tail->id = last ? last->id + 1 : 0;
tail->pid = -1;
memset(&tail->tio, 0, sizeof(tail->tio));
+#ifdef HAVE_LIBSELINUX
+ tail->reset_tty_context = NULL;
+ tail->user_tty_context = NULL;
+#endif
return 0;
}
diff --git a/login-utils/sulogin-consoles.h b/login-utils/sulogin-consoles.h
index 12032c9..608c4f8 100644
--- a/login-utils/sulogin-consoles.h
+++ b/login-utils/sulogin-consoles.h
@@ -44,6 +44,10 @@ struct console {
pid_t pid;
struct chardata cp;
struct termios tio;
+#ifdef HAVE_LIBSELINUX
+ char *reset_tty_context;
+ char *user_tty_context;
+#endif
};
extern int detect_consoles(const char *device, int fallback,
diff --git a/login-utils/sulogin.8 b/login-utils/sulogin.8
index 2f639b5..df9d305 100644
--- a/login-utils/sulogin.8
+++ b/login-utils/sulogin.8
@@ -2,12 +2,12 @@
.\" Title: sulogin
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-01-31
.\" Manual: System Administration
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "SULOGIN" "8" "2023-10-23" "util\-linux 2.39.3" "System Administration"
+.TH "SULOGIN" "8" "2024-01-31" "util\-linux 2.40" "System Administration"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c
index 45a558d..2682c30 100644
--- a/login-utils/sulogin.c
+++ b/login-utils/sulogin.c
@@ -99,6 +99,81 @@ static int locked_account_password(const char * const passwd)
return 0;
}
+#ifdef HAVE_LIBSELINUX
+/*
+ * Cached check whether SELinux is enabled.
+ */
+static int is_selinux_enabled_cached(void)
+{
+ static int cache = -1;
+
+ if (cache == -1)
+ cache = is_selinux_enabled();
+
+ return cache;
+}
+
+/* Computed SELinux login context. */
+static char *login_context;
+
+/*
+ * Compute SELinux login context.
+ */
+static void compute_login_context(void)
+{
+ char *seuser = NULL;
+ char *level = NULL;
+
+ if (is_selinux_enabled_cached() == 0)
+ goto cleanup;
+
+ if (getseuserbyname("root", &seuser, &level) == -1) {
+ warnx(_("failed to compute seuser"));
+ goto cleanup;
+ }
+
+ if (get_default_context_with_level(seuser, level, NULL, &login_context) == -1) {
+ warnx(_("failed to compute default context"));
+ goto cleanup;
+ }
+
+cleanup:
+ free(seuser);
+ free(level);
+}
+
+/*
+ * Compute SELinux terminal context.
+ */
+static void tcinit_selinux(struct console *con)
+{
+ security_class_t tclass;
+
+ if (!login_context)
+ return;
+
+ if (fgetfilecon(con->fd, &con->reset_tty_context) == -1) {
+ warn(_("failed to get context of terminal %s"), con->tty);
+ return;
+ }
+
+ tclass = string_to_security_class("chr_file");
+ if (tclass == 0) {
+ warnx(_("security class chr_file not available"));
+ freecon(con->reset_tty_context);
+ con->reset_tty_context = NULL;
+ return;
+ }
+
+ if (security_compute_relabel(login_context, con->reset_tty_context, tclass, &con->user_tty_context) == -1) {
+ warnx(_("failed to compute relabel context of terminal"));
+ freecon(con->reset_tty_context);
+ con->reset_tty_context = NULL;
+ return;
+ }
+}
+#endif
+
/*
* Fix the tty modes and set reasonable defaults.
*/
@@ -132,6 +207,10 @@ static void tcinit(struct console *con)
errno = 0;
#endif
+#ifdef HAVE_LIBSELINUX
+ tcinit_selinux(con);
+#endif
+
#ifdef TIOCGSERIAL
if (ioctl(fd, TIOCGSERIAL, &serinfo) >= 0)
con->flags |= CON_SERIAL;
@@ -270,23 +349,21 @@ static void tcfinal(struct console *con)
{
struct termios *tio = &con->tio;
const int fd = con->fd;
+ char *term, *ttyname = NULL;
- if (con->flags & CON_EIO)
- return;
- if ((con->flags & CON_SERIAL) == 0) {
- xsetenv("TERM", "linux", 0);
- return;
+ if (con->tty)
+ ttyname = strncmp(con->tty, "/dev/", 5) == 0 ?
+ con->tty + 5 : con->tty;
+
+ term = get_terminal_default_type(ttyname, con->flags & CON_SERIAL);
+ if (term) {
+ xsetenv("TERM", term, 0);
+ free(term);
}
- if (con->flags & CON_NOTTY) {
- xsetenv("TERM", "dumb", 0);
+
+ if (!(con->flags & CON_SERIAL) || (con->flags & CON_NOTTY))
return;
- }
-#if defined (__s390__) || defined (__s390x__)
- xsetenv("TERM", "dumb", 0);
-#else
- xsetenv("TERM", "vt102", 0);
-#endif
tio->c_iflag |= (IXON | IXOFF);
tio->c_lflag |= (ICANON | ISIG | ECHO|ECHOE|ECHOK|ECHOKE);
tio->c_oflag |= OPOST;
@@ -787,7 +864,7 @@ out:
/*
* Password was OK, execute a shell.
*/
-static void sushell(struct passwd *pwd)
+static void sushell(struct passwd *pwd, struct console *con)
{
char shell[PATH_MAX];
char home[PATH_MAX];
@@ -844,22 +921,21 @@ static void sushell(struct passwd *pwd)
mask_signal(SIGHUP, SIG_DFL, NULL);
#ifdef HAVE_LIBSELINUX
- if (is_selinux_enabled() > 0) {
- char *scon = NULL;
- char *seuser = NULL;
- char *level = NULL;
-
- if (getseuserbyname("root", &seuser, &level) == 0) {
- if (get_default_context_with_level(seuser, level, 0, &scon) == 0) {
- if (setexeccon(scon) != 0)
- warnx(_("setexeccon failed"));
- freecon(scon);
- }
+ if (is_selinux_enabled_cached() == 1) {
+ if (con->user_tty_context) {
+ if (fsetfilecon(con->fd, con->user_tty_context) == -1)
+ warn(_("failed to set context to %s for terminal %s"), con->user_tty_context, con->tty);
+ }
+
+ if (login_context) {
+ if (setexeccon(login_context) == -1)
+ warn(_("failed to set exec context to %s"), login_context);
}
- free(seuser);
- free(level);
}
+#else
+ (void)con;
#endif
+
execl(su_shell, shell, (char *)NULL);
warn(_("failed to execute %s"), su_shell);
@@ -868,6 +944,30 @@ static void sushell(struct passwd *pwd)
warn(_("failed to execute %s"), "/bin/sh");
}
+#ifdef HAVE_LIBSELINUX
+static void tcreset_selinux(struct list_head *consoles) {
+ struct list_head *ptr;
+ struct console *con;
+
+ if (is_selinux_enabled_cached() == 0)
+ return;
+
+ list_for_each(ptr, consoles) {
+ con = list_entry(ptr, struct console, entry);
+
+ if (con->fd < 0)
+ continue;
+ if (!con->reset_tty_context)
+ continue;
+ if (fsetfilecon(con->fd, con->reset_tty_context) == -1)
+ warn(_("failed to reset context to %s for terminal %s"), con->reset_tty_context, con->tty);
+
+ freecon(con->reset_tty_context);
+ con->reset_tty_context = NULL;
+ }
+}
+#endif
+
static void usage(void)
{
FILE *out = stdout;
@@ -885,8 +985,8 @@ static void usage(void)
out);
fputs(USAGE_SEPARATOR, out);
- printf(USAGE_HELP_OPTIONS(26));
- printf(USAGE_MAN_TAIL("sulogin(8)"));
+ fprintf(out, USAGE_HELP_OPTIONS(26));
+ fprintf(out, USAGE_MAN_TAIL("sulogin(8)"));
exit(EXIT_SUCCESS);
}
@@ -1017,6 +1117,10 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
+#ifdef HAVE_LIBSELINUX
+ compute_login_context();
+#endif
+
/*
* Ask for the password on the consoles.
*/
@@ -1036,9 +1140,18 @@ int main(int argc, char **argv)
}
ptr = (&consoles)->next;
- if (ptr->next == &consoles) {
- con = list_entry(ptr, struct console, entry);
- goto nofork;
+#ifdef HAVE_LIBSELINUX
+ /*
+ * Always fork with SELinux enabled, so the parent can restore the
+ * terminal context afterwards.
+ */
+ if (is_selinux_enabled_cached() == 0)
+#endif
+ {
+ if (ptr->next == &consoles) {
+ con = list_entry(ptr, struct console, entry);
+ goto nofork;
+ }
}
@@ -1089,7 +1202,7 @@ int main(int argc, char **argv)
#endif
if (doshell) {
/* sushell() unmask signals */
- sushell(pwd);
+ sushell(pwd, con);
mask_signal(SIGQUIT, SIG_IGN, &saved_sigquit);
mask_signal(SIGTSTP, SIG_IGN, &saved_sigtstp);
@@ -1195,5 +1308,10 @@ int main(int argc, char **argv)
} while (1);
mask_signal(SIGCHLD, SIG_DFL, NULL);
+
+#ifdef HAVE_LIBSELINUX
+ tcreset_selinux(&consoles);
+#endif
+
return EXIT_SUCCESS;
}
diff --git a/login-utils/utmpdump.1 b/login-utils/utmpdump.1
index 0d054a1..f6c46d6 100644
--- a/login-utils/utmpdump.1
+++ b/login-utils/utmpdump.1
@@ -2,12 +2,12 @@
.\" Title: utmpdump
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-01-31
.\" Manual: User Commands
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "UTMPDUMP" "1" "2023-10-23" "util\-linux 2.39.3" "User Commands"
+.TH "UTMPDUMP" "1" "2024-01-31" "util\-linux 2.40" "User Commands"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/login-utils/utmpdump.c b/login-utils/utmpdump.c
index a0ff2b1..ce9ad71 100644
--- a/login-utils/utmpdump.c
+++ b/login-utils/utmpdump.c
@@ -108,7 +108,7 @@ static void print_utline(struct utmpx *ut, FILE *out)
addr_string = inet_ntop(AF_INET, &(ut->ut_addr_v6), buffer, sizeof(buffer));
tv.tv_sec = ut->ut_tv.tv_sec;
- tv.tv_usec = ut->ut_tv.tv_usec;
+ tv.tv_usec = ut->ut_tv.tv_usec < (int32_t) USEC_PER_SEC ? ut->ut_tv.tv_usec : 0;
if (strtimeval_iso(&tv, ISO_TIMESTAMP_COMMA_GT, time_string,
sizeof(time_string)) != 0)
@@ -324,9 +324,9 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" -f, --follow output appended data as the file grows\n"), out);
fputs(_(" -r, --reverse write back dumped data into utmp file\n"), out);
fputs(_(" -o, --output <file> write to file instead of standard output\n"), out);
- printf(USAGE_HELP_OPTIONS(22));
+ fprintf(out, USAGE_HELP_OPTIONS(22));
- printf(USAGE_MAN_TAIL("utmpdump(1)"));
+ fprintf(out, USAGE_MAN_TAIL("utmpdump(1)"));
exit(EXIT_SUCCESS);
}
diff --git a/login-utils/vipw.8 b/login-utils/vipw.8
index ba8c3b0..5d8db1b 100644
--- a/login-utils/vipw.8
+++ b/login-utils/vipw.8
@@ -2,12 +2,12 @@
.\" Title: vipw
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-01-31
.\" Manual: System Administration
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "VIPW" "8" "2023-10-23" "util\-linux 2.39.3" "System Administration"
+.TH "VIPW" "8" "2024-01-31" "util\-linux 2.40" "System Administration"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/login-utils/vipw.c b/login-utils/vipw.c
index 5049706..0c4565b 100644
--- a/login-utils/vipw.c
+++ b/login-utils/vipw.c
@@ -304,8 +304,8 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_("Edit the password or group file.\n"), out);
fputs(USAGE_OPTIONS, out);
- printf(USAGE_HELP_OPTIONS(16));
- printf(USAGE_MAN_TAIL("vipw(8)"));
+ fprintf(out, USAGE_HELP_OPTIONS(16));
+ fprintf(out, USAGE_MAN_TAIL("vipw(8)"));
exit(EXIT_SUCCESS);
}