summaryrefslogtreecommitdiffstats
path: root/pam_lastlog2
diff options
context:
space:
mode:
Diffstat (limited to 'pam_lastlog2')
-rw-r--r--pam_lastlog2/COPYING24
-rw-r--r--pam_lastlog2/Makemodule.am8
-rw-r--r--pam_lastlog2/man/Makemodule.am6
-rw-r--r--pam_lastlog2/man/pam_lastlog2.8107
-rw-r--r--pam_lastlog2/man/pam_lastlog2.8.adoc78
-rw-r--r--pam_lastlog2/src/Makemodule.am21
-rw-r--r--pam_lastlog2/src/pam_lastlog2.c341
-rw-r--r--pam_lastlog2/src/pam_lastlog2.sym11
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