summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOndrej Oprala2014-04-07 18:24:19 +0200
committerKarel Zak2014-05-15 13:55:59 +0200
commit29cc2a55686f7182ae16e949d6d2a2b2b44c8536 (patch)
treebe392b41187d4be72db7302a1b492e20d19df48e
parentlogin: extract get_hushlogin_status (diff)
downloadkernel-qcow2-util-linux-29cc2a55686f7182ae16e949d6d2a2b2b44c8536.tar.gz
kernel-qcow2-util-linux-29cc2a55686f7182ae16e949d6d2a2b2b44c8536.tar.xz
kernel-qcow2-util-linux-29cc2a55686f7182ae16e949d6d2a2b2b44c8536.zip
lslogins: add functionality
Signed-off-by: Ondrej Oprala <ooprala@redhat.com>
-rw-r--r--.gitignore1
-rw-r--r--include/Makemodule.am1
-rw-r--r--lib/Makemodule.am3
-rw-r--r--login-utils/Makemodule.am26
-rw-r--r--login-utils/login.c2
-rw-r--r--login-utils/logindefs.c18
-rw-r--r--login-utils/logindefs.h2
-rw-r--r--login-utils/lslogins.13
-rw-r--r--login-utils/lslogins.c923
9 files changed, 891 insertions, 88 deletions
diff --git a/.gitignore b/.gitignore
index 968a0890d..b04276989 100644
--- a/.gitignore
+++ b/.gitignore
@@ -116,6 +116,7 @@ update.log
/lsblk
/lscpu
/lslocks
+/lslogins
/mcookie
/mesg
/mkfs
diff --git a/include/Makemodule.am b/include/Makemodule.am
index 7f461c6fe..08479fa0f 100644
--- a/include/Makemodule.am
+++ b/include/Makemodule.am
@@ -37,6 +37,7 @@ dist_noinst_HEADERS += \
include/pathnames.h \
include/procutils.h \
include/randutils.h \
+ include/readutmp.h \
include/rpmatch.h \
include/setproctitle.h \
include/strutils.h \
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index b4ad0e9ae..a16509670 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -26,7 +26,8 @@ libcommon_la_SOURCES = \
lib/timeutils.c \
lib/ttyutils.c \
lib/xgetpass.c \
- lib/exec_shell.c
+ lib/exec_shell.c \
+ lib/readutmp.c
if LINUX
libcommon_la_SOURCES += \
diff --git a/login-utils/Makemodule.am b/login-utils/Makemodule.am
index 0275a6952..137067295 100644
--- a/login-utils/Makemodule.am
+++ b/login-utils/Makemodule.am
@@ -165,6 +165,19 @@ newgrp_LDADD += -lcrypt
endif
endif # BUILD_NEWGRP
+if BUILD_LSLOGINS
+usrbin_exec_PROGRAMS += lslogins
+dist_man_MANS += login-utils/lslogins.1
+lslogins_SOURCES = \
+ login-utils/lslogins.c \
+ login-utils/logindefs.c \
+ login-utils/logindefs.h
+lslogins_LDADD = $(LDADD) libcommon.la libsmartcols.la
+lslogins_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir)
+if HAVE_SELINUX
+lslogins_LDADD += -lselinux
+endif
+endif # BUILD_LSLOGINS
if BUILD_VIPW
usrsbin_exec_PROGRAMS += vipw
@@ -207,16 +220,3 @@ endif
if BUILD_VIPW
cd $(DESTDIR)$(usrsbin_execdir) && ln -sf vipw vigr
endif
-
-if BUILD_LSLOGINS
-usrbin_exec_PROGRAMS += lslogins
-dist_man_MANS += login-utils/lslogins.1
-lslogins_SOURCES = \
- login-utils/lslogins.c \
- login-utils/lslogins.h
-lslogins_LDADD = $(LDADD) libcommon.la libsmartcols.la
-lslogins_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir)
-if HAVE_SELINUX
-lslogins_LDADD += -lselinux
-endif
-endif # BUILD_LSLOGINS
diff --git a/login-utils/login.c b/login-utils/login.c
index b16a81231..470076f8c 100644
--- a/login-utils/login.c
+++ b/login-utils/login.c
@@ -1248,7 +1248,7 @@ int main(int argc, char **argv)
endpwent();
- cxt.quiet = get_hushlogin_status(pwd);
+ cxt.quiet = get_hushlogin_status(pwd, 1);
log_utmp(&cxt);
log_audit(&cxt, 1);
diff --git a/login-utils/logindefs.c b/login-utils/logindefs.c
index 804ab3e31..f02c4752d 100644
--- a/login-utils/logindefs.c
+++ b/login-utils/logindefs.c
@@ -307,7 +307,7 @@ int effective_access(const char *path, int mode)
* BSD setreuid().
*/
-int get_hushlogin_status(struct passwd *pwd)
+int get_hushlogin_status(struct passwd *pwd, int force_check)
{
const char *files[] = { _PATH_HUSHLOGINS, _PATH_HUSHLOGIN, NULL };
const char *file;
@@ -358,12 +358,13 @@ int get_hushlogin_status(struct passwd *pwd)
/* per-account setting */
if (strlen(pwd->pw_dir) + sizeof(file) + 2 > sizeof(buf))
continue;
- else {
+
+ sprintf(buf, "%s/%s", pwd->pw_dir, file);
+
+ if (force_check) {
uid_t ruid = getuid();
gid_t egid = getegid();
- sprintf(buf, "%s/%s", pwd->pw_dir, file);
-
if (setregid(-1, pwd->pw_gid) == 0 &&
setreuid(0, pwd->pw_uid) == 0)
ok = effective_access(buf, O_RDONLY) == 0;
@@ -377,6 +378,15 @@ int get_hushlogin_status(struct passwd *pwd)
if (ok)
return 1; /* enabled by user */
}
+ else {
+ int rc;
+ rc = effective_access(buf, O_RDONLY);
+ if (rc == 0)
+ return 1;
+ else if (rc == -1 && errno == EACCES)
+ return -1;
+ }
+
}
return 0;
diff --git a/login-utils/logindefs.h b/login-utils/logindefs.h
index 6a72762f4..f05d733ba 100644
--- a/login-utils/logindefs.h
+++ b/login-utils/logindefs.h
@@ -9,6 +9,6 @@ extern const char *getlogindefs_str(const char *name, const char *dflt);
extern void free_getlogindefs_data(void);
extern int logindefs_setenv(const char *name, const char *conf, const char *dflt);
extern int effective_access(const char *path, int mode);
-extern int get_hushlogin_status(struct passwd *pwd);
+extern int get_hushlogin_status(struct passwd *pwd, int force_check);
#endif /* UTIL_LINUX_LOGINDEFS_H */
diff --git a/login-utils/lslogins.1 b/login-utils/lslogins.1
index 9b62cb86c..a961efea4 100644
--- a/login-utils/lslogins.1
+++ b/login-utils/lslogins.1
@@ -73,6 +73,9 @@ Show extra information about users - home diretory, default login shell, passwor
\fB\-z\fR, \fB\-\-print0\fR
Delimit user entries with a nul character, instead of a newline.
.TP
+\fB\-Z\fR, \fB\-\-context\fR
+Display the users' security context.
+.TP
\fB\-h\fR, \fB\-\-help\fR
Display help information and exit.
\fB\-v\fR, \fB\-\-version\fR
diff --git a/login-utils/lslogins.c b/login-utils/lslogins.c
index c9f812f36..657a408ab 100644
--- a/login-utils/lslogins.c
+++ b/login-utils/lslogins.c
@@ -23,17 +23,33 @@
#include <unistd.h>
#include <getopt.h>
#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
#include <pwd.h>
+#include <grp.h>
#include <shadow.h>
#include <paths.h>
#include <time.h>
+#include <utmp.h>
+#include <signal.h>
+#include <err.h>
+#include <limits.h>
+
+#include <libsmartcols.h>
+#ifdef HAVE_LIBSELINUX
+#include <selinux/selinux.h>
+#endif
#include "c.h"
#include "nls.h"
#include "closestream.h"
#include "xalloc.h"
+#include "list.h"
#include "strutils.h"
#include "optutils.h"
+#include "pathnames.h"
+#include "logindefs.h"
+#include "readutmp.h"
/*
* column description
@@ -42,12 +58,27 @@ struct lslogins_coldesc {
const char *name;
const char *help;
- unsigned int is_abbr:1; /* name is abbreviation */
+ double whint; /* width hint */
};
+static struct list_head userlist;
/* the most uber of flags */
static int uberflag;
+struct utmp *wtmp, *btmp;
+size_t wtmp_size, btmp_size;
+int want_wtmp, want_btmp;
+
+#define UL_UID_MIN "1000"
+#define UL_UID_MAX "60000"
+#define UL_SYS_UID_MIN "201"
+#define UL_SYS_UID_MAX "999"
+
+uid_t UID_MIN;
+uid_t UID_MAX;
+
+uid_t SYS_UID_MIN;
+uid_t SYS_UID_MAX;
/* we use the value of outmode to determine
* appropriate flags for the libsmartcols table
* (e.g., a value of out_newline would imply a raw
@@ -58,7 +89,7 @@ static int outmode;
* output modes
*/
enum {
- out_colon = 0,
+ out_colon = 1,
out_export,
out_newline,
out_raw,
@@ -72,40 +103,48 @@ struct lslogins_user {
gid_t gid;
char *gecos;
- int nopasswd:1;
+ int nopasswd;
+ int nologin;
+ int locked;
char *sgroups;
- struct tm *pwd_ctime;
- struct tm *pwd_expir;
+ char *pwd_ctime;
+ char *pwd_expir;
+ char *pwd_ctime_min;
+ char *pwd_ctime_max;
- struct tm *last_login;
- char * last_tty;
- char * last_hostname;
+ char *last_login;
+ char *last_tty;
+ char *last_hostname;
- struct tm *failed_login;
- struct tm *failed_tty;
+ char *failed_login;
+ char *failed_tty;
+#ifdef HAVE_LIBSELINUX
+ security_context_t context;
+#endif
char *homedir;
char *shell;
char *pwd_status;
- char *hush_status;
+ int hushed;
+
+ struct list_head ulist;
};
/*
* flags
*/
enum {
F_EXPIR = (1 << 0),
- F_DUP = (1 << 1),
- F_EXPRT = (1 << 2),
- F_MORE = (1 << 3),
- F_NOPWD = (1 << 4),
- F_SYSAC = (1 << 5),
- F_USRAC = (1 << 6),
- F_SORT = (1 << 7),
- F_EXTRA = (1 << 8),
- F_FAIL = (1 << 9),
- F_LAST = (1 << 10),
+ F_MORE = (1 << 1),
+ F_NOPWD = (1 << 2),
+ F_SYSAC = (1 << 3),
+ F_USRAC = (1 << 4),
+ F_SORT = (1 << 5),
+ F_EXTRA = (1 << 6),
+ F_FAIL = (1 << 7),
+ F_LAST = (1 << 8),
+ F_SELINUX = (1 << 9),
};
/*
@@ -114,7 +153,6 @@ enum {
enum {
COL_LOGIN = 0,
COL_UID,
- COL_NOPASSWD,
COL_PGRP,
COL_PGID,
COL_SGRPS,
@@ -127,35 +165,41 @@ enum {
COL_FAILED_LOGIN,
COL_FAILED_TTY,
COL_HUSH_STATUS,
- COL_PWD_STATUS,
+ COL_NOLOGIN,
+ COL_LOCKED,
+ COL_NOPASSWD,
COL_PWD_EXPIR,
COL_PWD_CTIME,
- /*COL_PWD_CTIME_MAX,
- COL_PWD_CTIME_MIN,*/
+ COL_PWD_CTIME_MIN,
+ COL_PWD_CTIME_MAX,
+ COL_SELINUX,
};
+static const char *const status[] = { "0", "1", "-" };
static struct lslogins_coldesc coldescs[] =
{
- [COL_LOGIN] = { "LOGIN", N_("user/system login"), 1 },
- [COL_UID] = { "UID", N_("user UID") },
- [COL_NOPASSWD] = { "HAS PASSWORD", N_("account has a password?") },
- [COL_PGRP] = { "GRP", N_("primary group name") },
- [COL_PGID] = { "GRP_GID", N_("primary group GID") },
- [COL_SGRPS] = { "SEC_GRPS", N_("secondary group names and GIDs") },
- [COL_HOME] = { "HOMEDIR", N_("home directory") },
- [COL_SHELL] = { "SHELL", N_("login shell") },
- [COL_FULLNAME] = { "FULLNAME", N_("full user name") },
- [COL_LAST_LOGIN] = { "LAST_LOGIN", N_("date of last login") },
- [COL_LAST_TTY] = { "LAST_TTY", N_("last tty used") },
- [COL_LAST_HOSTNAME] = { "LAST_HOSTNAME", N_("hostname during the last session") },
- [COL_FAILED_LOGIN] = { "FAILED_LOGIN", N_("date of last failed login") },
- [COL_FAILED_TTY] = { "FAILED_TTY", N_("where did the login fail?") },
- [COL_HUSH_STATUS] = { "HUSH_STATUS", N_("User's hush settings") },
- [COL_PWD_STATUS] = { "PWD_STATUS", N_("password status - see the -x option description") },
- [COL_PWD_EXPIR] = { "PWD_EXPIR", N_("password expiration date") },
- [COL_PWD_CTIME] = { "PWD_CHANGE", N_("date of last password change") },
- /*[COL_PWD_CTIME_MAX] = { "PWD UNTIL", N_("max number of days a password may remain unchanged") },
- [COL_PWD_CTIME_MIN] = { "PWD CAN CHANGE", N_("number of days required between changes") },*/
+ [COL_LOGIN] = { "LOGIN", N_("user/system login"), 0.2 },
+ [COL_UID] = { "UID", N_("user UID"), 0.05 },
+ [COL_NOPASSWD] = { "NOPASSWD", N_("account has a password?"), 1 },
+ [COL_NOLOGIN] = { "NOLOGIN", N_("account has a password?"), 1 },
+ [COL_LOCKED] = { "LOCKED", N_("account has a password?"), 1 },
+ [COL_PGRP] = { "GRP", N_("primary group name"), 0.2 },
+ [COL_PGID] = { "GID", N_("primary group GID"), 0.05 },
+ [COL_SGRPS] = { "SEC_GRPS", N_("secondary group names and GIDs"), 0.5 },
+ [COL_HOME] = { "HOMEDIR", N_("home directory"), 0.3 },
+ [COL_SHELL] = { "SHELL", N_("login shell"), 0.1 },
+ [COL_FULLNAME] = { "FULLNAME", N_("full user name"), 0.3 },
+ [COL_LAST_LOGIN] = { "LAST_LOGIN", N_("date of last login"), 24 },
+ [COL_LAST_TTY] = { "LAST_TTY", N_("last tty used"), 0.05 },
+ [COL_LAST_HOSTNAME] = { "LAST_HOSTNAME", N_("hostname during the last session"), 0.2},
+ [COL_FAILED_LOGIN] = { "FAILED_LOGIN", N_("date of last failed login"), 24 },
+ [COL_FAILED_TTY] = { "FAILED_TTY", N_("where did the login fail?"), 0.05 },
+ [COL_HUSH_STATUS] = { "HUSHED", N_("User's hush settings"), 1 },
+ [COL_PWD_EXPIR] = { "PWD_EXPIR", N_("password expiration date"), 24 },
+ [COL_PWD_CTIME] = { "PWD_CHANGE", N_("date of last password change"), 24 },
+ [COL_PWD_CTIME_MIN] = { "PWD_MIN", N_("number of days required between changes"), 24 },
+ [COL_PWD_CTIME_MAX] = { "PWD_MAX", N_("max number of days a password may remain unchanged"), 24 },
+ [COL_SELINUX] = { "CONTEXT", N_("the user's security context"), 0.4 },
};
static int
@@ -166,13 +210,33 @@ column_name_to_id(const char *name, size_t namesz)
for (i = 0; i < ARRAY_SIZE(coldescs); i++) {
const char *cn = coldescs[i].name;
- if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
+ if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) {
+ if (i <= COL_LAST_HOSTNAME && i >= COL_LAST_LOGIN)
+ want_wtmp = 1;
+ if (i == COL_FAILED_TTY && i >= COL_FAILED_LOGIN)
+ want_btmp = 1;
return i;
+ }
}
warnx(_("unknown column: %s"), name);
return -1;
}
+static char *make_time(struct tm *tm)
+{
+ char *t, *s;
+
+ if (!tm)
+ return NULL;
+
+ s = asctime(tm);
+ if (!s)
+ return NULL;
+
+ if (*(t = s + strlen(s) - 1) == '\n')
+ *t = '\0';
+ return strdup(s);
+}
static void __attribute__((__noreturn__)) usage(FILE *out)
{
size_t i;
@@ -183,22 +247,21 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
fputs(USAGE_OPTIONS, out);
fputs(_(" -a, --acc-expiration Display data\n"), out);
fputs(_(" -c, --colon-separate Display data in a format similar to /etc/passwd\n"), out);
- fputs(_(" -d, --duplicates Display users with duplicate UIDs\n"), out);
fputs(_(" -e, --export Display in an export-able output format\n"), out);
fputs(_(" -f, --failed Display data about the last users' failed logins\n"), out);
fputs(_(" -g, --groups=<GROUPS> Display users belonging to a group in GROUPS\n"), out);
fputs(_(" -l, --logins=<LOGINS> Display only users from LOGINS\n"), out);
- fputs(_(" --last Show info about the last login sessions\n"), out);
+ fputs(_(" --last Show info about the users' last login sessions\n"), out);
fputs(_(" -m, --more Display secondary groups as well\n"), out);
fputs(_(" -n, --newline Display each piece of information on a new line\n"), out);
fputs(_(" -o, --output[=<LIST>] Define the columns to output\n"), out);
- fputs(_(" -p, --no-password Display users without a password\n"), out);
fputs(_(" -r, --raw Display the raw table\n"), out);
- fputs(_(" -s, --sys-accs[=<UID>] Display system accounts\n"), out);
+ fputs(_(" -s, --sys-accs Display system accounts\n"), out);
fputs(_(" -t, --sort Sort output by login instead of UID\n"), out);
- fputs(_(" -u, --user-accs[=<UID>] Display user accounts\n"), out);
+ fputs(_(" -u, --user-accs Display user accounts\n"), out);
fputs(_(" -x, --extra Display extra information\n"), out);
- fputs(_(" -z, --print0 Delimit user entries with a nul character"), out);
+ fputs(_(" -z, --print0 Delimit user entries with a nul character\n"), out);
+ fputs(_(" -Z, --context Display the users' security context\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(USAGE_HELP, out);
fputs(USAGE_VERSION, out);
@@ -212,6 +275,699 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
+struct lslogins_sgroups {
+ char *gid;
+ char *uname;
+ struct lslogins_sgroups *next;
+};
+
+static char *uidtostr(uid_t uid)
+{
+ char *str_uid = NULL;
+ return (0 > xasprintf(&str_uid, "%u", uid)) ? NULL : str_uid;
+}
+
+static char *gidtostr(gid_t gid)
+{
+ char *str_gid = NULL;
+ return (0 > xasprintf(&str_gid, "%u", gid)) ? NULL : str_gid;
+}
+
+static struct lslogins_sgroups *build_sgroups_list(int len, gid_t *list, int *slen)
+{
+ int n = 0;
+ struct lslogins_sgroups *sgrps, *retgrps;
+
+ *slen = 0;
+
+ if (!len || !list)
+ return NULL;
+
+ retgrps = sgrps = calloc(1, sizeof(struct lslogins_sgroups));
+ while (n < len) {
+ if (sgrps->next)
+ sgrps = sgrps->next;
+
+ sgrps->gid = gidtostr(list[n]);
+ sgrps->uname = strdup(getgrgid(list[n])->gr_name);
+
+ *slen += strlen(sgrps->gid) + strlen(sgrps->uname);
+
+ sgrps->next = calloc(1, sizeof(struct lslogins_sgroups));
+
+ ++n;
+
+ }
+ /* a pair of parentheses for each group + (n - 1) commas in between */
+ slen += 3 * n - 1;
+
+ free (sgrps->next);
+ sgrps->next = NULL;
+
+ return retgrps;
+}
+static void free_sgroups_list(struct lslogins_sgroups *sgrps)
+{
+ struct lslogins_sgroups *tmp;
+
+ if (!sgrps)
+ return;
+
+ tmp = sgrps->next;
+ while (tmp) {
+ free(sgrps->gid);
+ free(sgrps->uname);
+ free(sgrps);
+ sgrps = tmp;
+ tmp = tmp->next;
+ }
+}
+static char *build_sgroups_string(int len, gid_t *list)
+{
+ char *ret = NULL, *slist;
+ int slen, prlen;
+ struct lslogins_sgroups *sgrps;
+
+ sgrps = build_sgroups_list(len, list, &slen);
+
+ if (!sgrps)
+ return NULL;
+
+ ret = slist = calloc (1, sizeof(char) * (slen + 1));
+
+ while (sgrps->next) {
+ prlen = sprintf(slist, "%s(%s),", sgrps->gid, sgrps->uname);
+ if (prlen < 0) {
+ free_sgroups_list(sgrps);
+ return NULL;
+ }
+ slist += prlen;
+ sgrps = sgrps->next;
+ }
+ prlen = sprintf(slist, "%s(%s)", sgrps->gid, sgrps->uname);
+
+ return ret;
+}
+
+static struct utmp *get_recent_wtmp(const char *username)
+{
+ size_t n = 0;
+ size_t len;
+
+ if (!username)
+ return NULL;
+
+ len = strlen(username);
+ n = wtmp_size - 1;
+ while (n) {
+ if (!strncmp(username, wtmp[n].ut_user, len < UT_NAMESIZE ? len : UT_NAMESIZE))
+ return wtmp + n;
+ --n;
+ }
+ return NULL;
+
+}
+static struct utmp *get_recent_btmp(const char *username)
+{
+ size_t n = 0;
+ size_t len;
+
+ if (!username)
+ return NULL;
+
+ len = strlen(username);
+ n = btmp_size - 1;
+ while (n) {
+ if (!strncmp(username, btmp[n].ut_user, len < UT_NAMESIZE ? len : UT_NAMESIZE))
+ return btmp + n;
+ --n;
+ }
+ return NULL;
+
+}
+
+static int parse_wtmp(void)
+{
+ int rc = 0;
+
+ rc = read_utmp(_PATH_WTMP, &wtmp_size, &wtmp);
+ if (rc < 0 && errno != EACCES)
+ err(EXIT_FAILURE, "%s: %s", _PATH_WTMP, strerror(errno));
+ return rc;
+
+ return rc;
+}
+static int parse_btmp(void)
+{
+ int rc = 0;
+
+ rc = read_utmp(_PATH_BTMP, &btmp_size, &btmp);
+ if (rc < 0 && errno != EACCES)
+ err(EXIT_FAILURE, "%s: %s", _PATH_BTMP, strerror(errno));
+ return rc;
+
+ return rc;
+}
+
+static int get_sgroups(int *len, gid_t **list, struct passwd *pwd)
+{
+ int n = 0;
+ gid_t *safelist;
+
+ getgrouplist(pwd->pw_name, pwd->pw_gid, *list, len);
+ if (!*len)
+ return -1;
+
+ *list = malloc(*len * sizeof(gid_t));
+
+ if (-1 == getgrouplist(pwd->pw_name, pwd->pw_gid, *list, len))
+ return -1;
+
+ while (n < *len) {
+ if ((*list)[n] == pwd->pw_gid)
+ break;
+ ++n;
+ }
+ (*list)[n] = (*list)[--(*len)];
+
+ safelist = xrealloc (*list, *len * sizeof(gid_t));
+ if (!safelist && *len) {
+ free (*list);
+ return -1;
+ }
+ *list = safelist;
+
+ return 0;
+
+}
+
+static struct lslogins_user *get_user_info(const char *username, const int *columns, const int ncolumns)
+{
+ struct lslogins_user *user;
+ struct passwd *pwd;
+ struct group *grp;
+ struct spwd *shadow;
+ struct utmp *user_wtmp = NULL, *user_btmp = NULL;
+ int n = 0;
+ time_t time;
+ struct tm tm;
+ uid_t uid;
+
+ user = xcalloc(1, sizeof(struct lslogins_user));
+ if (!user) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ INIT_LIST_HEAD(&user->ulist);
+
+ errno = 0;
+ pwd = username ? getpwnam(username) : getpwent();
+ if (!pwd)
+ return NULL;
+
+ uid = pwd->pw_uid;
+ if ((uberflag & F_USRAC) &&
+ strcmp("nfsnobody", pwd->pw_name)) {
+ if (uid < UID_MIN || uid > UID_MAX) {
+ errno = EAGAIN;
+ return NULL;
+ }
+ }
+ else if (uberflag & F_SYSAC) {
+ if (uid < SYS_UID_MIN || uid > SYS_UID_MAX) {
+ errno = EAGAIN;
+ return NULL;
+ }
+ }
+
+ grp = getgrgid(pwd->pw_gid);
+ if (!grp)
+ return NULL;
+
+ if (wtmp)
+ user_wtmp = get_recent_wtmp(pwd->pw_name);
+ if (btmp)
+ user_btmp = get_recent_btmp(pwd->pw_name);
+
+ /* sufficient permissions to get a shadow entry? */
+ errno = 0;
+ lckpwdf();
+ shadow = getspnam(pwd->pw_name);
+ ulckpwdf();
+ if (!shadow && errno != EACCES)
+ err(EXIT_FAILURE, "%s", strerror(errno));
+
+ if (shadow) {
+ /* we want these dates in seconds */
+ shadow->sp_min *= 86400;
+ shadow->sp_max*= 86400;
+ shadow->sp_lstchg *= 86400;
+ }
+ /* we store the uid unconditionally, for sorting */
+ /* TODO: only if not -t --> faster?? */
+ user->uid = pwd->pw_uid;
+
+ while (n < ncolumns) {
+ switch (columns[n++]) {
+ case COL_LOGIN:
+ user->login = strdup(pwd->pw_name);
+ break;
+ case COL_UID:
+ /* just a placeholder, see above*/
+ break;
+ case COL_PGRP:
+ user->group = strdup(grp->gr_name);
+ break;
+ case COL_PGID:
+ user->gid = (pwd->pw_gid);
+ break;
+ case COL_SGRPS:
+ {
+ int n = 0;
+ gid_t *list = NULL;
+
+ if (get_sgroups(&n, &list, pwd))
+ err(EXIT_FAILURE, "Unable to get secondary groups(%s)", strerror(errno));
+ user->sgroups = build_sgroups_string(n, list);
+ if (!user->sgroups)
+ user->sgroups = "";
+ break;
+ }
+ case COL_HOME:
+ user->homedir = strdup(pwd->pw_dir);
+ break;
+ case COL_SHELL:
+ user->shell = strdup(pwd->pw_shell);
+ break;
+ case COL_FULLNAME:
+ user->gecos = strdup(pwd->pw_gecos);
+ break;
+ /* strdup us? ? */
+ case COL_LAST_LOGIN:
+ if (user_wtmp) {
+ time = user_wtmp->ut_tv.tv_sec;
+ localtime_r(&time, &tm);
+ user->last_login = make_time(&tm);
+ }
+ else
+ user->last_login = "-";
+ break;
+ case COL_LAST_TTY:
+ if (user_wtmp)
+ user->last_tty = user_wtmp->ut_line;
+ else
+ user->last_tty = "-";
+ break;
+ case COL_LAST_HOSTNAME:
+ if (user_wtmp)
+ user->last_hostname = user_wtmp->ut_host;
+ else
+ user->last_hostname = "-";
+ break;
+ case COL_FAILED_LOGIN:
+ if (user_btmp) {
+ time = user_btmp->ut_tv.tv_sec;
+ localtime_r(&time, &tm);
+ user->failed_login = make_time(&tm);
+ }
+ else
+ user->failed_login = "-";
+ break;
+ case COL_FAILED_TTY:
+ if (user_btmp)
+ user->failed_tty = user_btmp->ut_line;
+ else
+ user->failed_tty = "-";
+ break;
+ case COL_HUSH_STATUS:
+ user->hushed = get_hushlogin_status(pwd, 0);
+ if (user->hushed == -1)
+ user->hushed = 2;
+ break;
+ case COL_NOPASSWD:
+ if (shadow) {
+ if (!*shadow->sp_pwdp) /* '\0' */
+ user->nopasswd = 1;
+ else
+ user->nopasswd = 0;
+ }
+ else
+ user->nopasswd = 2;
+ break;
+ case COL_NOLOGIN:
+ if ((pwd->pw_uid && !(close(open("/etc/nologin", O_RDONLY)))) ||
+ strstr(pwd->pw_shell, "nologin")) {
+ user->nologin = 1;
+ }
+ else
+ user->nologin = 0;
+ break;
+ case COL_LOCKED:
+ if (shadow) {
+ if (*shadow->sp_pwdp == '!')
+ user->locked = 1;
+ else
+ user->locked = 0;
+ break;
+ }
+ else
+ user->locked = 2;
+ break;
+ case COL_PWD_EXPIR:
+ if (shadow && shadow->sp_expire != -1) {
+ localtime_r(&shadow->sp_expire, &tm);
+ user->pwd_expir = make_time(&tm);
+ }
+ else
+ user->pwd_expir = "-";
+ break;
+ case COL_PWD_CTIME:
+ if (shadow) {
+ localtime_r(&shadow->sp_lstchg, &tm);
+ user->pwd_ctime = make_time(&tm);
+ }
+ else
+ user->pwd_ctime = "-";
+ break;
+ case COL_PWD_CTIME_MIN:
+
+ if (shadow) {
+ if (shadow->sp_min <= 0) {
+ user->pwd_ctime_min = "unlimited";
+ }
+ else {
+ localtime_r(&shadow->sp_min, &tm);
+ user->pwd_ctime_min = make_time(&tm);
+ }
+ }
+ else
+ user->pwd_ctime_min = "-";
+ break;
+ case COL_PWD_CTIME_MAX:
+ if (shadow) {
+ if (shadow->sp_max <= 0)
+ user->pwd_ctime_max = "unlimited";
+ else {
+ localtime_r(&shadow->sp_max, &tm);
+ user->pwd_ctime_max = make_time(&tm);
+ }
+ }
+ else
+ user->pwd_ctime_max = "-";
+ break;
+ case COL_SELINUX:
+ {
+#ifdef HAVE_LIBSELINUX
+ /* typedefs and pointers are pure evil */
+ security_context_t con = NULL;
+ if (getcon(&con))
+ return NULL;
+ user->context = con;
+#endif
+ }
+ break;
+ default:
+ /* something went very wrong here */
+ err(EXIT_FAILURE, "fatal: unknown error");
+ }
+ }
+
+ return user;
+}
+/* some UNIX implementations set errno iff a passwd/grp/...
+ * entry was not found. The original UNIX logins utility always
+ * ignores invalid login/group names, so we're going to as well.*/
+#define IS_REAL_ERRNO(e) !((e) == ENOENT || (e) == ESRCH || (e) == EBADF || (e) == EPERM || (e) == EAGAIN)
+#define HANDLE_ERROR(e) do { if ((e) && IS_REAL_ERRNO(e)) { err(1,"%s",strerror(errno)); return -1;} } while (0)
+
+static struct lslogins_user *get_next_user(const int *columns, const int ncolumns)
+{
+ struct lslogins_user *u;
+ errno = 0;
+ while (!(u = get_user_info(NULL, columns, ncolumns))) {
+ /* no "false" errno-s here, if we're unable to
+ * get a valid user entry for any reason, quit */
+ if (errno == EAGAIN)
+ continue;
+ return NULL;
+ }
+ return u;
+}
+
+static int get_user(struct lslogins_user **user,
+ const char *username, const int *columns, const int ncolumns)
+{
+ *user = get_user_info(username, columns, ncolumns);
+ if (!*user && errno)
+ HANDLE_ERROR(errno);
+ return 0;
+}
+
+static int user_in_list(struct list_head *head, char *uname)
+{
+ struct list_head *p;
+ list_for_each(p, head) {
+ if (!strcmp(uname, list_entry(p, struct lslogins_user, ulist)->login))
+ return 1;
+ }
+ return 0;
+}
+
+static int create_userlist(char *logins, char *groups, const int *const columns, const int ncolumns)
+{
+ char *username, *gname;
+ struct lslogins_user *user = NULL;
+ struct group *grp;
+ int n = 0;
+
+ if (!logins && !groups) {
+ while ((user = get_next_user(columns, ncolumns)))
+ list_add(&user->ulist, &userlist);
+
+ HANDLE_ERROR(errno);
+ }
+ if (logins) {
+ while ((username = strtok(logins, ",")) != NULL) {
+ logins = NULL;
+ if (get_user(&user, username, columns, ncolumns))
+ return -1;
+ if (user) /* otherwise an invalid user name has probably been given */
+ list_add(&user->ulist, &userlist);
+ }
+ }
+ if (groups) {
+ while ((gname = strtok(groups, ",")) != NULL) {
+ groups = NULL;
+ errno = 0;
+ grp = getgrnam(gname);
+ if (!grp) {
+ HANDLE_ERROR(errno);
+ continue; /* not a "real" errno */
+ }
+
+ while ((username = grp->gr_mem[n++])) {
+
+ if (!user_in_list(&userlist, username)) {
+ if (get_user(&user, username, columns, ncolumns))
+ return -1;
+ if (user) /* otherwise an invalid user name has probably been given */
+ list_add(&user->ulist, &userlist);
+ }
+ }
+ }
+ }
+ return 0;
+}
+static int cmp_uname(struct list_head *a, struct list_head *b, void *data __attribute__((unused)))
+{
+ return strcmp(list_entry(a,struct lslogins_user,ulist)->login,
+ list_entry(b,struct lslogins_user,ulist)->login);
+}
+static int cmp_uid(struct list_head *a, struct list_head *b, void *data __attribute__((unused)))
+{
+ return bcmp(&list_entry(a,struct lslogins_user,ulist)->uid,
+ &list_entry(b,struct lslogins_user,ulist)->uid,
+ sizeof(uid_t));
+}
+static void sort_userlist(void)
+{
+ if (uberflag & F_SORT)
+ list_sort(&userlist,cmp_uname, NULL);
+ else
+ list_sort(&userlist,cmp_uid, NULL);
+}
+static struct libscols_table *setup_table(const int *const columns, const int ncolumns)
+{
+ struct libscols_table *tb = scols_new_table();
+ int n = 0;
+ if (!tb)
+ return NULL;
+
+ switch(outmode) {
+ case out_colon:
+ scols_table_enable_raw(tb, 1);
+ scols_table_set_column_separator(tb, ":");
+ break;
+ case out_newline:
+ scols_table_set_column_separator(tb, "\n");
+ /* fallthrough */
+ case out_export:
+ scols_table_enable_export(tb, 1);
+ break;
+ case out_nul:
+ scols_table_set_line_separator(tb, "\0");
+ /* fallthrough */
+ case out_raw:
+ scols_table_enable_raw(tb, 1);
+ break;
+ default:
+ break;
+ }
+
+ while (n < ncolumns) {
+ if (!scols_table_new_column(tb, coldescs[columns[n]].name,
+ coldescs[columns[n]].whint, 0))
+ goto fail;
+ ++n;
+ }
+
+ return tb;
+fail:
+ scols_unref_table(tb);
+ return NULL;
+}
+
+static int print_user_table(const int *const columns, const int ncolumns)
+{
+ int n;
+ struct list_head *p;
+ struct libscols_table *tb;
+ struct libscols_line *ln;
+ struct lslogins_user *user;
+
+ tb = setup_table(columns, ncolumns);
+ if (!tb)
+ return -1;
+
+ sort_userlist();
+
+ list_for_each(p, &userlist) {
+ user = list_entry(p, struct lslogins_user, ulist);
+ n = 0;
+ ln = scols_table_new_line(tb, NULL);
+ while (n < ncolumns) {
+ switch (columns[n]) {
+ case COL_LOGIN:
+ if (scols_line_set_data(ln, n, user->login))
+ goto fail;
+ break;
+ case COL_UID:
+ {
+ char *str_uid = uidtostr(user->uid);
+ if (!str_uid || scols_line_set_data(ln, n, str_uid))
+ goto fail;
+ break;
+ }
+ case COL_NOPASSWD:
+ if (scols_line_set_data(ln, n, status[user->nopasswd]))
+ goto fail;
+ break;
+ case COL_NOLOGIN:
+ if (scols_line_set_data(ln, n, status[user->nologin]))
+ goto fail;
+ break;
+ case COL_LOCKED:
+ if (scols_line_set_data(ln, n, status[user->locked]))
+ goto fail;
+ break;
+ case COL_PGRP:
+ if (scols_line_set_data(ln, n, user->group))
+ goto fail;
+ break;
+ case COL_PGID:
+ {
+ char *str_gid = gidtostr(user->gid);
+ if (!str_gid || scols_line_set_data(ln, n, str_gid))
+ goto fail;
+ break;
+ }
+ case COL_SGRPS:
+ if (scols_line_set_data(ln, n, user->sgroups))
+ goto fail;
+ break;
+ case COL_HOME:
+ if (scols_line_set_data(ln, n, user->homedir))
+ goto fail;
+ break;
+ case COL_SHELL:
+ if (scols_line_set_data(ln, n, user->shell))
+ goto fail;
+ break;
+ case COL_FULLNAME:
+ if (scols_line_set_data(ln, n, user->gecos))
+ goto fail;
+ break;
+ case COL_LAST_LOGIN:
+ if (scols_line_set_data(ln, n, user->last_login))
+ goto fail;
+ break;
+ case COL_LAST_TTY:
+ if (scols_line_set_data(ln, n, user->last_tty))
+ goto fail;
+ break;
+ case COL_LAST_HOSTNAME:
+ if (scols_line_set_data(ln, n, user->last_hostname))
+ goto fail;
+ break;
+ case COL_FAILED_LOGIN:
+ if (scols_line_set_data(ln, n, user->failed_login))
+ goto fail;
+ break;
+ case COL_FAILED_TTY:
+ if (scols_line_set_data(ln, n, user->failed_tty))
+ goto fail;
+ break;
+ case COL_HUSH_STATUS:
+ if (scols_line_set_data(ln, n, status[user->hushed]))
+ goto fail;
+ break;
+#define PWD_TIME(S,L,T) strftime((S),(L), "%a %b %d %Y", (T))
+ case COL_PWD_EXPIR:
+ if (scols_line_set_data(ln, n, user->pwd_expir))
+ goto fail;
+ break;
+ case COL_PWD_CTIME:
+ if (scols_line_set_data(ln, n, user->pwd_ctime))
+ goto fail;
+ break;
+ case COL_PWD_CTIME_MIN:
+ if (scols_line_set_data(ln, n, user->pwd_ctime_min))
+ goto fail;
+ break;
+ case COL_PWD_CTIME_MAX:
+ if (scols_line_set_data(ln, n, user->pwd_ctime_max))
+ goto fail;
+ break;
+#ifdef HAVE_LIBSELINUX
+ case COL_SELINUX:
+ if (scols_line_set_data(ln, n, user->context))
+ goto fail;
+ break;
+#endif
+ default:
+ /* something went very wrong here */
+ err(EXIT_FAILURE, "fatal: unknown error");
+ }
+ ++n;
+ }
+ }
+ scols_print_table(tb);
+ scols_unref_table(tb);
+ return 0;
+fail:
+ return -1;
+}
+
int main(int argc, char *argv[])
{
@@ -227,7 +983,6 @@ int main(int argc, char *argv[])
static const struct option longopts[] = {
{ "acc-expiration", no_argument, 0, 'a' },
{ "colon", no_argument, 0, 'c' },
- { "duplicates", no_argument, 0, 'd' },
{ "export", no_argument, 0, 'e' },
{ "failed", no_argument, 0, 'f' },
{ "groups", required_argument, 0, 'g' },
@@ -235,16 +990,18 @@ int main(int argc, char *argv[])
{ "logins", required_argument, 0, 'l' },
{ "more", no_argument, 0, 'm' },
{ "newline", no_argument, 0, 'n' },
- { "output", optional_argument, 0, 'o' },
- { "no-password", no_argument, 0, 'p' },
+ { "output", required_argument, 0, 'o' },
{ "last", no_argument, 0, OPT_LAST },
{ "raw", no_argument, 0, 'r' },
- { "sys-accs", optional_argument, 0, 's' },
+ { "sys-accs", no_argument, 0, 's' },
{ "sort", no_argument, 0, 't' },
- { "user-accs", optional_argument, 0, 'u' },
+ { "user-accs", no_argument, 0, 'u' },
{ "version", no_argument, 0, OPT_VER },
{ "extra", no_argument, 0, 'x' },
{ "print0", no_argument, 0, 'z' },
+#ifdef HAVE_LIBSELINUX
+ { "context", no_argument, 0, 'Z' },
+#endif
{ NULL, 0, 0, 0 }
};
@@ -259,7 +1016,7 @@ int main(int argc, char *argv[])
textdomain(PACKAGE);
atexit(close_stdout);
- while ((c = getopt_long(argc, argv, "acdefg:hl:mno::prs::tu::xz",
+ while ((c = getopt_long(argc, argv, "acefg:hl:mno:rstuxzZ",
longopts, NULL)) != -1) {
err_exclusive_options(c, longopts, excl, excl_st);
@@ -271,9 +1028,8 @@ int main(int argc, char *argv[])
case 'c':
outmode = out_colon;
break;
- case 'd':
- uberflag |= F_DUP;
- break;
+ /* a slow option; however the only reason to use it
+ * is after hand-editing the /etc/passwd file */
case 'e':
outmode = out_export;
break;
@@ -309,9 +1065,6 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
break;
- case 'p':
- uberflag |= F_NOPWD;
- break;
case 'r':
outmode = out_raw;
break;
@@ -319,12 +1072,16 @@ int main(int argc, char *argv[])
uberflag |= F_LAST;
break;
case 's':
+ SYS_UID_MIN = strtoul(getlogindefs_str("SYS_UID_MIN", UL_SYS_UID_MIN), NULL, 0);
+ SYS_UID_MAX = strtoul(getlogindefs_str("SYS_UID_MAX", UL_SYS_UID_MAX), NULL, 0);
uberflag |= F_SYSAC;
break;
case 't':
uberflag |= F_SORT;
break;
case 'u':
+ UID_MIN = strtoul(getlogindefs_str("UID_MIN", UL_UID_MIN), NULL, 0);
+ UID_MAX = strtoul(getlogindefs_str("UID_MAX", UL_UID_MAX), NULL, 0);
uberflag |= F_USRAC;
break;
case OPT_VER:
@@ -337,6 +1094,14 @@ int main(int argc, char *argv[])
case 'z':
outmode = out_nul;
break;
+ case 'Z':
+#ifdef HAVE_LIBSELINUX
+ if (0 < is_selinux_enabled())
+ uberflag |= F_SELINUX;
+ else
+#endif
+ err(0, "warning: --context only works on a system with SELinux enabled");
+ break;
default:
usage(stderr);
}
@@ -344,9 +1109,13 @@ int main(int argc, char *argv[])
if (argc != optind)
usage(stderr);
+ /* lslogins -u -s == lslogins */
+ if (uberflag & F_USRAC && uberflag & F_SYSAC)
+ uberflag &= ~(F_USRAC & F_SYSAC);
+
if (!ncolumns) {
columns[ncolumns++] = COL_LOGIN;
- columns[ncolumns++] = COL_UID;
+ columns[ncolumns++] = COL_UID; /* TODO: always get this info, for sorting */
columns[ncolumns++] = COL_PGRP;
columns[ncolumns++] = COL_PGID;
columns[ncolumns++] = COL_FULLNAME;
@@ -365,19 +1134,37 @@ int main(int argc, char *argv[])
columns[ncolumns++] = COL_LAST_LOGIN;
columns[ncolumns++] = COL_LAST_TTY;
columns[ncolumns++] = COL_LAST_HOSTNAME;
+ want_wtmp = 1;
}
if (uberflag & F_FAIL) {
columns[ncolumns++] = COL_FAILED_LOGIN;
columns[ncolumns++] = COL_FAILED_TTY;
+ want_btmp = 1;
}
if (uberflag & F_EXTRA) {
columns[ncolumns++] = COL_HOME;
columns[ncolumns++] = COL_SHELL;
- columns[ncolumns++] = COL_PWD_STATUS;
+ columns[ncolumns++] = COL_NOPASSWD;
+ columns[ncolumns++] = COL_NOLOGIN;
+ columns[ncolumns++] = COL_LOCKED;
columns[ncolumns++] = COL_HUSH_STATUS;
- /* columns[ncolumns++] = COL_PWD_CTIME_MAX;
- columns[ncolumns++] = COL_PWD_CTIME_MIN; */
+ columns[ncolumns++] = COL_PWD_CTIME_MIN;
+ columns[ncolumns++] = COL_PWD_CTIME_MAX;
}
+ if (uberflag & F_SELINUX)
+ columns[ncolumns++] = COL_SELINUX;
}
+
+ if (want_wtmp)
+ parse_wtmp();
+ if (want_btmp)
+ parse_btmp();
+
+ INIT_LIST_HEAD(&userlist);
+ if (create_userlist(logins, groups, columns, ncolumns))
+ return EXIT_FAILURE;
+
+ print_user_table(columns, ncolumns);
+
return EXIT_SUCCESS;
}