diff options
Diffstat (limited to 'pam_lastlog2')
-rw-r--r-- | pam_lastlog2/COPYING | 24 | ||||
-rw-r--r-- | pam_lastlog2/Makemodule.am | 8 | ||||
-rw-r--r-- | pam_lastlog2/man/Makemodule.am | 6 | ||||
-rw-r--r-- | pam_lastlog2/man/pam_lastlog2.8 | 107 | ||||
-rw-r--r-- | pam_lastlog2/man/pam_lastlog2.8.adoc | 78 | ||||
-rw-r--r-- | pam_lastlog2/src/Makemodule.am | 21 | ||||
-rw-r--r-- | pam_lastlog2/src/pam_lastlog2.c | 341 | ||||
-rw-r--r-- | pam_lastlog2/src/pam_lastlog2.sym | 11 |
8 files changed, 596 insertions, 0 deletions
diff --git a/pam_lastlog2/COPYING b/pam_lastlog2/COPYING new file mode 100644 index 0000000..df71a98 --- /dev/null +++ b/pam_lastlog2/COPYING @@ -0,0 +1,24 @@ +BSD 2-Clause License + +Copyright (c) 2023, Thorsten Kukuk + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pam_lastlog2/Makemodule.am b/pam_lastlog2/Makemodule.am new file mode 100644 index 0000000..f33c98b --- /dev/null +++ b/pam_lastlog2/Makemodule.am @@ -0,0 +1,8 @@ +if BUILD_PAM_LASTLOG2 + +include pam_lastlog2/man/Makemodule.am +include pam_lastlog2/src/Makemodule.am + +EXTRA_DIST += pam_lastlog2/COPYING + +endif # BUILD_PAM_LASTLOG2 diff --git a/pam_lastlog2/man/Makemodule.am b/pam_lastlog2/man/Makemodule.am new file mode 100644 index 0000000..3f19ea1 --- /dev/null +++ b/pam_lastlog2/man/Makemodule.am @@ -0,0 +1,6 @@ + +MANPAGES += \ + pam_lastlog2/man/pam_lastlog2.8 + +dist_noinst_DATA += \ + pam_lastlog2/man/pam_lastlog2.8.adoc diff --git a/pam_lastlog2/man/pam_lastlog2.8 b/pam_lastlog2/man/pam_lastlog2.8 new file mode 100644 index 0000000..99fd2f5 --- /dev/null +++ b/pam_lastlog2/man/pam_lastlog2.8 @@ -0,0 +1,107 @@ +'\" t +.\" Title: pam_lastlog2 +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: System Administration +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "PAM_LASTLOG2" "8" "2024-03-20" "util\-linux 2.40" "System Administration" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +pam_lastlog2 \- PAM module to display date of last login +.SH "SYNOPSIS" +.sp +\fBpam_lastlog2.so\fP [debug] [silent] [silent_if=<services>] [database=<file>] +.SH "DESCRIPTION" +.sp +pam_lastlog2 is a PAM module to display a line of information about the last login of the user. The module uses the /var/lib/lastlog/lastlog2.db database file to store all informations. +.sp +Compared to pam_lastlog this PAM module is Y2038 safe and uses sqlite3 to store the information. +.SH "OPTIONS" +.sp +\fBdebug\fP +.RS 4 +Print debug information. +.RE +.sp +\fBsilent\fP +.RS 4 +Avoid all messages except errors and don\(cqt inform the user about any previous login, only update the /var/lib/lastlog/lastlog2.db database. +.RE +.sp +\fBsilent_if=<services>\fP +.RS 4 +The argument \fBservices\fP is a comma separated list of PAM services. If a service is listed here, the last login message will not be shown. +.RE +.sp +\fBdatabase=<file>\fP +.RS 4 +Use \fBfile\fP instead of /var/lib/lastlog/lastlog2.db. +.RE +.SH "MODULE TYPES PROVIDED" +.sp +The \fBsession\fP module type is provided for displaying the information about the last login and updating the lastlog file. +.SH "RETURN VALUES" +.sp +\fBPAM_SUCCESS\fP +.RS 4 +Everything was successful. +.RE +.sp +\fBPAM_SERVICE_ERR\fP +.RS 4 +Internal service module error. This includes error reading from or writing to the database. +.RE +.sp +\fBPAM_USER_UNKNOWN\fP +.RS 4 +User not known. +.RE +.sp +\fBPAM_IGNORE\fP +.RS 4 +Returned by service types which do nothing. +.RE +.SH "EXAMPLES" +.sp +Add the following line to e.g. /etc/pam.d/login to display the last login time of a user: +.sp +session optional pam_lastlog2.so silent_if=gdm,gdm\-password +.sp +It is up to the administrator to decide if the user can login (optional/required) when +pam_lastlog2 has returned an error. +.SH "AUTHOR" +.sp +pam_lastlog2 was written by Thorsten Kukuk \c +.MTO "kukuk\(atsuse.com" "" "." +.SH "SEE ALSO" +.sp +\fBliblastlog2\fP(3), \fBpam.conf\fP(5), \fBpam.d\fP(5), \fBpam\fP(8) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBpam_lastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/pam_lastlog2/man/pam_lastlog2.8.adoc b/pam_lastlog2/man/pam_lastlog2.8.adoc new file mode 100644 index 0000000..1ed5ba6 --- /dev/null +++ b/pam_lastlog2/man/pam_lastlog2.8.adoc @@ -0,0 +1,78 @@ +//po4a: entry man manual += pam_lastlog2(8) +:doctype: manpage +:man manual: System Administration +:man source: util-linux {release-version} +:lib: pam_lastlog2 +:firstversion: 2.40 +:page-layout: base + +== NAME + +pam_lastlog2 - PAM module to display date of last login + +== SYNOPSIS + +*pam_lastlog2.so* [debug] [silent] [silent_if=<services>] [database=<file>] + +== DESCRIPTION + +pam_lastlog2 is a PAM module to display a line of information about the last login of the user. The module uses the /var/lib/lastlog/lastlog2.db database file to store all informations. + +Compared to pam_lastlog this PAM module is Y2038 safe and uses sqlite3 to store the information. + +== OPTIONS + +*debug*:: +Print debug information. + +*silent*:: +Avoid all messages except errors and don't inform the user about any previous login, only update the /var/lib/lastlog/lastlog2.db database. + +*silent_if=<services>*:: +The argument *services* is a comma separated list of PAM services. If a service is listed here, the last login message will not be shown. + +*database=<file>*:: +Use *file* instead of /var/lib/lastlog/lastlog2.db. + +== MODULE TYPES PROVIDED + +The *session* module type is provided for displaying the information about the last login and updating the lastlog file. + +== RETURN VALUES + +*PAM_SUCCESS*:: +Everything was successful. + +*PAM_SERVICE_ERR*:: +Internal service module error. This includes error reading from or writing to the database. + +*PAM_USER_UNKNOWN*:: +User not known. + +*PAM_IGNORE*:: +Returned by service types which do nothing. + +== EXAMPLES +Add the following line to e.g. /etc/pam.d/login to display the last login time of a user: + +session optional pam_lastlog2.so silent_if=gdm,gdm-password + +It is up to the administrator to decide if the user can login (optional/required) when +pam_lastlog2 has returned an error. + +== AUTHOR + +pam_lastlog2 was written by Thorsten Kukuk <kukuk@suse.com>. + +== SEE ALSO + +*liblastlog2*(3), *pam.conf*(5), *pam.d*(5), *pam*(8) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/pam_lastlog2/src/Makemodule.am b/pam_lastlog2/src/Makemodule.am new file mode 100644 index 0000000..9884416 --- /dev/null +++ b/pam_lastlog2/src/Makemodule.am @@ -0,0 +1,21 @@ +securelibdir = $(libdir)/security +securelib_LTLIBRARIES = pam_lastlog2.la + +pam_lastlog2_la_SOURCES = \ + pam_lastlog2/src/pam_lastlog2.c + +EXTRA_pam_lastlog2_la_DEPENDENCIES = \ + pam_lastlog2/src/pam_lastlog2.sym + +pam_lastlog2_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(SOLIB_CFLAGS) \ + -I$(ul_liblastlog2_incdir) \ + -Iliblastlog2/src + +pam_lastlog2_la_LDFLAGS = $(SOLIB_LDFLAGS) -module -avoid-version -shared +if HAVE_VSCRIPT +pam_lastlog2_la_LDFLAGS += pam_lastlog2_la_LDFLAGS += $(VSCRIPT_LDFLAGS),$(top_srcdir)/pam_lastlog2/src/pam_lastlog2.sym +endif + +EXTRA_DIST += pam_lastlog2/src/pam_lastlog2.sym diff --git a/pam_lastlog2/src/pam_lastlog2.c b/pam_lastlog2/src/pam_lastlog2.c new file mode 100644 index 0000000..e800700 --- /dev/null +++ b/pam_lastlog2/src/pam_lastlog2.c @@ -0,0 +1,341 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <security/pam_modules.h> +#include <security/pam_ext.h> +#include <security/pam_modutil.h> +#include <security/_pam_macros.h> + +#include "lastlog2.h" +#include "strutils.h" + +#define LASTLOG2_DEBUG 01 /* send info to syslog(3) */ +#define LASTLOG2_QUIET 02 /* keep quiet about things */ + +static const char *lastlog2_path = LL2_DEFAULT_DATABASE; + +/* check for list match. */ +static int +check_in_list (const char *service, const char *arg) +{ + const char *item; + const char *remaining; + + if (!service) + return 0; + + remaining = arg; + + for (;;) { + item = strstr (remaining, service); + if (item == NULL) + break; + + /* is it really the start of an item in the list? */ + if (item == arg || *(item - 1) == ',') { + item += strlen (service); + /* is item really the service? */ + if (*item == '\0' || *item == ',') + return 1; + } + + remaining = strchr (item, ','); + if (remaining == NULL) + break; + + /* skip ',' */ + ++remaining; + } + + return 0; +} + + +static int +_pam_parse_args (pam_handle_t *pamh, + int flags, int argc, + const char **argv) +{ + int ctrl = 0; + const char *str; + + /* does the application require quiet? */ + if (flags & PAM_SILENT) + ctrl |= LASTLOG2_QUIET; + + /* step through arguments */ + for (; argc-- > 0; ++argv) { + if (strcmp (*argv, "debug") == 0) + ctrl |= LASTLOG2_DEBUG; + else if (strcmp (*argv, "silent") == 0) + ctrl |= LASTLOG2_QUIET; + else if ((str = startswith (*argv, "database=")) != NULL) + lastlog2_path = str; + else if ((str = startswith (*argv, "silent_if=")) != NULL) { + const void *void_str = NULL; + const char *service; + if ((pam_get_item (pamh, PAM_SERVICE, &void_str) != PAM_SUCCESS) || + void_str == NULL) + service = ""; + else + service = void_str; + + if (check_in_list (service, str)) { + if (ctrl & LASTLOG2_DEBUG) + pam_syslog (pamh, LOG_DEBUG, "silent_if='%s' contains '%s'", str, service); + ctrl |= LASTLOG2_QUIET; + } + } else + pam_syslog (pamh, LOG_ERR, "Unknown option: %s", *argv); + } + + return ctrl; +} + +static int +write_login_data (pam_handle_t *pamh, int ctrl, const char *user) +{ + const void *void_str; + const char *tty; + const char *rhost; + const char *pam_service; + const char *xdg_vtnr; + int xdg_vtnr_nr; + char tty_buf[8]; + time_t ll_time; + char *error = NULL; + int retval; + + void_str = NULL; + retval = pam_get_item (pamh, PAM_TTY, &void_str); + if (retval != PAM_SUCCESS || void_str == NULL) + tty = ""; + else + tty = void_str; + + /* strip leading "/dev/" from tty. */ + const char *str = startswith(tty, "/dev/"); + if (str != NULL) + tty = str; + + if (ctrl & LASTLOG2_DEBUG) + pam_syslog (pamh, LOG_DEBUG, "tty=%s", tty); + + /* if PAM_TTY is not set or an X11 $DISPLAY, try XDG_VTNR */ + if ((tty[0] == '\0' || strchr(tty, ':') != NULL) && (xdg_vtnr = pam_getenv (pamh, "XDG_VTNR")) != NULL) { + xdg_vtnr_nr = atoi (xdg_vtnr); + if (xdg_vtnr_nr > 0 && snprintf (tty_buf, sizeof(tty_buf), "tty%d", xdg_vtnr_nr) < (int) sizeof(tty_buf)) { + tty = tty_buf; + if (ctrl & LASTLOG2_DEBUG) + pam_syslog (pamh, LOG_DEBUG, "tty(XDG_VTNR)=%s", tty); + } + } + + void_str = NULL; + retval = pam_get_item (pamh, PAM_RHOST, &void_str); + if (retval != PAM_SUCCESS || void_str == NULL) { + void_str = NULL; + retval = pam_get_item (pamh, PAM_XDISPLAY, &void_str); + if (retval != PAM_SUCCESS || void_str == NULL) { + rhost = ""; + } else { + rhost = void_str; + if (ctrl & LASTLOG2_DEBUG) + pam_syslog (pamh, LOG_DEBUG, "rhost(PAM_XDISPLAY)=%s", rhost); + } + } else { + rhost = void_str; + if (ctrl & LASTLOG2_DEBUG) + pam_syslog (pamh, LOG_DEBUG, "rhost(PAM_RHOST)=%s", rhost); + } + + void_str = NULL; + if ((pam_get_item (pamh, PAM_SERVICE, &void_str) != PAM_SUCCESS) || + void_str == NULL) + pam_service = ""; + else + pam_service = void_str; + + if (time (&ll_time) < 0) + return PAM_SYSTEM_ERR; + + struct ll2_context *context = ll2_new_context(lastlog2_path); + if (ll2_write_entry (context, user, ll_time, tty, rhost, + pam_service, &error) != 0) { + if (error) { + pam_syslog (pamh, LOG_ERR, "%s", error); + free (error); + } else + pam_syslog (pamh, LOG_ERR, "Unknown error writing to database %s", lastlog2_path); + ll2_unref_context(context); + return PAM_SYSTEM_ERR; + } + ll2_unref_context(context); + + return PAM_SUCCESS; +} + +static int +show_lastlogin (pam_handle_t *pamh, int ctrl, const char *user) +{ + int64_t ll_time = 0; + char *tty = NULL; + char *rhost = NULL; + char *service = NULL; + char *date = NULL; + char the_time[256]; + char *error = NULL; + int retval = PAM_SUCCESS; + + if (ctrl & LASTLOG2_QUIET) + return retval; + + struct ll2_context *context = ll2_new_context(lastlog2_path); + if (ll2_read_entry (context, user, &ll_time, &tty, &rhost, + &service, &error) != 0) { + if (errno == ENOENT) + { + /* DB file not found --> it is OK */ + ll2_unref_context(context); + free(error); + return PAM_SUCCESS; + } + if (error) { + pam_syslog (pamh, LOG_ERR, "%s", error); + free (error); + } else + pam_syslog (pamh, LOG_ERR, "Unknown error reading database %s", lastlog2_path); + ll2_unref_context(context); + return PAM_SYSTEM_ERR; + } + ll2_unref_context(context); + + if (ll_time) { + struct tm *tm, tm_buf; + /* this is necessary if you compile this on architectures with + a 32bit time_t type. */ + time_t t_time = ll_time; + + if ((tm = localtime_r (&t_time, &tm_buf)) != NULL) { + strftime (the_time, sizeof (the_time), + " %a %b %e %H:%M:%S %Z %Y", tm); + date = the_time; + } + } + + if (date != NULL || rhost != NULL || tty != NULL) + retval = pam_info(pamh, "Last login:%s%s%s%s%s", + date ? date : "", + rhost ? " from " : "", + rhost ? rhost : "", + tty ? " on " : "", + tty ? tty : ""); + + _pam_drop(service); + _pam_drop(rhost); + _pam_drop(tty); + + return retval; +} + +int +pam_sm_authenticate (pam_handle_t *pamh __attribute__((__unused__)), + int flags __attribute__((__unused__)), + int argc __attribute__((__unused__)), + const char **argv __attribute__((__unused__))) +{ + return PAM_IGNORE; +} + +int +pam_sm_setcred (pam_handle_t *pamh __attribute__((__unused__)), + int flags __attribute__((__unused__)), + int argc __attribute__((__unused__)), + const char **argv __attribute__((__unused__))) +{ + return PAM_IGNORE; +} + +int +pam_sm_acct_mgmt (pam_handle_t *pamh __attribute__((__unused__)), + int flags __attribute__((__unused__)), + int argc __attribute__((__unused__)), + const char **argv __attribute__((__unused__))) +{ + return PAM_IGNORE; +} + +int +pam_sm_open_session (pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + const struct passwd *pwd; + const void *void_str; + const char *user; + int ctrl; + + ctrl = _pam_parse_args (pamh, flags, argc, argv); + + void_str = NULL; + int retval = pam_get_item (pamh, PAM_USER, &void_str); + if (retval != PAM_SUCCESS || void_str == NULL || strlen (void_str) == 0) { + if (!(ctrl & LASTLOG2_QUIET)) + pam_syslog (pamh, LOG_NOTICE, "User unknown"); + return PAM_USER_UNKNOWN; + } + user = void_str; + + /* verify the user exists */ + pwd = pam_modutil_getpwnam (pamh, user); + if (pwd == NULL) { + if (ctrl & LASTLOG2_DEBUG) + pam_syslog (pamh, LOG_DEBUG, "Couldn't find user %s", + (const char *)user); + return PAM_USER_UNKNOWN; + } + + if (ctrl & LASTLOG2_DEBUG) + pam_syslog (pamh, LOG_DEBUG, "user=%s", user); + + show_lastlogin (pamh, ctrl, user); + + return write_login_data (pamh, ctrl, user); +} + +int +pam_sm_close_session (pam_handle_t *pamh __attribute__((__unused__)), + int flags __attribute__((__unused__)), + int argc __attribute__((__unused__)), + const char **argv __attribute__((__unused__))) +{ + return PAM_SUCCESS; +} diff --git a/pam_lastlog2/src/pam_lastlog2.sym b/pam_lastlog2/src/pam_lastlog2.sym new file mode 100644 index 0000000..1633155 --- /dev/null +++ b/pam_lastlog2/src/pam_lastlog2.sym @@ -0,0 +1,11 @@ + +{ + global: + pam_sm_acct_mgmt; + pam_sm_authenticate; + pam_sm_chauthtok; + pam_sm_close_session; + pam_sm_open_session; + pam_sm_setcred; + local: *; +};
\ No newline at end of file |