1*a97560b6Sjoerg /* $NetBSD: pam_lastlog.c,v 1.15 2014/01/07 02:07:43 joerg Exp $ */
2e7d22a2eSchristos
36f11bdf1Schristos /*-
46f11bdf1Schristos * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
56f11bdf1Schristos * The Regents of the University of California. All rights reserved.
66f11bdf1Schristos * Copyright (c) 2001 Mark R V Murray
76f11bdf1Schristos * All rights reserved.
86f11bdf1Schristos * Copyright (c) 2001 Networks Associates Technology, Inc.
96f11bdf1Schristos * All rights reserved.
106f11bdf1Schristos * Copyright (c) 2004 Joe R. Doupnik
116f11bdf1Schristos * All rights reserved.
126f11bdf1Schristos *
136f11bdf1Schristos * Portions of this software were developed for the FreeBSD Project by
146f11bdf1Schristos * ThinkSec AS and NAI Labs, the Security Research Division of Network
156f11bdf1Schristos * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
166f11bdf1Schristos * ("CBOSS"), as part of the DARPA CHATS research program.
176f11bdf1Schristos *
186f11bdf1Schristos * Redistribution and use in source and binary forms, with or without
196f11bdf1Schristos * modification, are permitted provided that the following conditions
206f11bdf1Schristos * are met:
216f11bdf1Schristos * 1. Redistributions of source code must retain the above copyright
226f11bdf1Schristos * notice, this list of conditions and the following disclaimer.
236f11bdf1Schristos * 2. Redistributions in binary form must reproduce the above copyright
246f11bdf1Schristos * notice, this list of conditions and the following disclaimer in the
256f11bdf1Schristos * documentation and/or other materials provided with the distribution.
266f11bdf1Schristos * 3. The name of the author may not be used to endorse or promote
276f11bdf1Schristos * products derived from this software without specific prior written
286f11bdf1Schristos * permission.
296f11bdf1Schristos * 4. Neither the name of the University nor the names of its contributors
306f11bdf1Schristos * may be used to endorse or promote products derived from this software
316f11bdf1Schristos * without specific prior written permission.
326f11bdf1Schristos *
336f11bdf1Schristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
346f11bdf1Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
356f11bdf1Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
366f11bdf1Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
376f11bdf1Schristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
386f11bdf1Schristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
396f11bdf1Schristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
406f11bdf1Schristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
416f11bdf1Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
426f11bdf1Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
436f11bdf1Schristos * SUCH DAMAGE.
446f11bdf1Schristos */
456f11bdf1Schristos
466f11bdf1Schristos #include <sys/cdefs.h>
47e7d22a2eSchristos #ifdef __FreeBSD__
486f11bdf1Schristos __FBSDID("$FreeBSD: src/lib/libpam/modules/pam_lastlog/pam_lastlog.c,v 1.20 2004/01/26 19:28:37 des Exp $");
49e7d22a2eSchristos #else
50*a97560b6Sjoerg __RCSID("$NetBSD: pam_lastlog.c,v 1.15 2014/01/07 02:07:43 joerg Exp $");
51e7d22a2eSchristos #endif
526f11bdf1Schristos
536f11bdf1Schristos #include <sys/param.h>
546f11bdf1Schristos
556f11bdf1Schristos #include <fcntl.h>
56e7d22a2eSchristos #include <util.h>
576f11bdf1Schristos #include <paths.h>
586f11bdf1Schristos #include <pwd.h>
596f11bdf1Schristos #include <stdio.h>
606f11bdf1Schristos #include <stdlib.h>
616f11bdf1Schristos #include <string.h>
626f11bdf1Schristos #include <syslog.h>
631b695acdSchristos #include <errno.h>
646f11bdf1Schristos #include <time.h>
656f11bdf1Schristos #include <unistd.h>
66e76ecd93Schristos #include <stdarg.h>
67fa02801fSchristos #ifdef LOGIN_CAP
68fa02801fSchristos #include <login_cap.h>
69fa02801fSchristos #endif
7044d1e609Schristos
71a3df4155Schristos #define PAM_SM_SESSION
72a3df4155Schristos
73a3df4155Schristos #include <security/pam_appl.h>
74a3df4155Schristos #include <security/pam_modules.h>
75a3df4155Schristos #include <security/pam_mod_misc.h>
76a3df4155Schristos
7744d1e609Schristos #ifdef SUPPORT_UTMP
786f11bdf1Schristos #include <utmp.h>
7944d1e609Schristos static void doutmp(const char *, const char *, const char *,
8044d1e609Schristos const struct timeval *);
81a3df4155Schristos static void dolastlog(pam_handle_t *, int, const struct passwd *, const char *,
82a3df4155Schristos const char *, const struct timeval *);
8344d1e609Schristos #endif
8444d1e609Schristos
8544d1e609Schristos #ifdef SUPPORT_UTMPX
8644d1e609Schristos #include <utmpx.h>
8744d1e609Schristos static void doutmpx(const char *, const char *, const char *,
8844d1e609Schristos const struct sockaddr_storage *ss, const struct timeval *);
89a3df4155Schristos static void dolastlogx(pam_handle_t *, int, const struct passwd *, const char *,
90a3df4155Schristos const char *, const struct sockaddr_storage *ss, const struct timeval *);
9144d1e609Schristos #endif
926f11bdf1Schristos
93a3df4155Schristos #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
94a3df4155Schristos static void domsg(pam_handle_t *, time_t, const char *, size_t, const char *,
95a3df4155Schristos size_t);
96a3df4155Schristos #endif
976f11bdf1Schristos
98*a97560b6Sjoerg __printflike(2, 3)
99e76ecd93Schristos static void
logit(int level,const char * fmt,...)100e76ecd93Schristos logit(int level, const char *fmt, ...)
101e76ecd93Schristos {
102e76ecd93Schristos va_list ap;
1034ed3eb7fSchristos struct syslog_data data = SYSLOG_DATA_INIT;
104e76ecd93Schristos
105e76ecd93Schristos openlog_r("pam_lastlog", LOG_PID, LOG_AUTHPRIV, &data);
106e76ecd93Schristos va_start(ap, fmt);
107e76ecd93Schristos vsyslog_r(level, &data, fmt, ap);
108e76ecd93Schristos va_end(ap);
109e76ecd93Schristos closelog_r(&data);
110e76ecd93Schristos }
111e76ecd93Schristos
112e76ecd93Schristos
1136f11bdf1Schristos PAM_EXTERN int
pam_sm_open_session(pam_handle_t * pamh,int flags,int argc __unused,const char * argv[]__unused)1146f11bdf1Schristos pam_sm_open_session(pam_handle_t *pamh, int flags,
1156f11bdf1Schristos int argc __unused, const char *argv[] __unused)
1166f11bdf1Schristos {
11759cbc9e2Sthorpej struct passwd *pwd, pwres;
11844d1e609Schristos struct timeval now;
119fa02801fSchristos const char *user, *rhost, *tty, *nuser;
120fa02801fSchristos const void *vrhost, *vtty, *vss, *vnuser;
12144d1e609Schristos const struct sockaddr_storage *ss;
12244d1e609Schristos int pam_err;
12359cbc9e2Sthorpej char pwbuf[1024];
124216a33afSjnemeth #ifdef LOGIN_CAP
125216a33afSjnemeth login_cap_t *lc;
126216a33afSjnemeth #endif
1276f11bdf1Schristos
1286f11bdf1Schristos pam_err = pam_get_user(pamh, &user, NULL);
1296f11bdf1Schristos if (pam_err != PAM_SUCCESS)
13044d1e609Schristos return pam_err;
13144d1e609Schristos
13259cbc9e2Sthorpej if (user == NULL ||
1332a62e4e1Schristos getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
1342a62e4e1Schristos pwd == NULL)
13544d1e609Schristos return PAM_SERVICE_ERR;
13644d1e609Schristos
1376f11bdf1Schristos PAM_LOG("Got user: %s", user);
1386f11bdf1Schristos
139dcdc758dShe pam_err = pam_get_item(pamh, PAM_RHOST, &vrhost);
1406f11bdf1Schristos if (pam_err != PAM_SUCCESS)
1416f11bdf1Schristos goto err;
142dcdc758dShe rhost = (const char *)vrhost;
14344d1e609Schristos
144dcdc758dShe pam_err = pam_get_item(pamh, PAM_SOCKADDR, &vss);
1456f11bdf1Schristos if (pam_err != PAM_SUCCESS)
1466f11bdf1Schristos goto err;
147dcdc758dShe ss = (const struct sockaddr_storage *)vss;
14844d1e609Schristos
149dcdc758dShe pam_err = pam_get_item(pamh, PAM_TTY, &vtty);
15044d1e609Schristos if (pam_err != PAM_SUCCESS)
15144d1e609Schristos goto err;
152dcdc758dShe tty = (const char *)vtty;
15344d1e609Schristos
1546f11bdf1Schristos if (tty == NULL) {
1556f11bdf1Schristos pam_err = PAM_SERVICE_ERR;
1566f11bdf1Schristos goto err;
1576f11bdf1Schristos }
15844d1e609Schristos
159fa02801fSchristos if (pam_get_item(pamh, PAM_NUSER, &vnuser) != PAM_SUCCESS)
160fa02801fSchristos nuser = NULL;
161fa02801fSchristos else
162fa02801fSchristos nuser = (const char *)vnuser;
163fa02801fSchristos
1646f11bdf1Schristos if (strncmp(tty, _PATH_DEV, strlen(_PATH_DEV)) == 0)
16544d1e609Schristos tty = tty + strlen(_PATH_DEV);
1666f11bdf1Schristos
16744d1e609Schristos if (*tty == '\0') {
16844d1e609Schristos pam_err = PAM_SERVICE_ERR;
16944d1e609Schristos goto err;
1706f11bdf1Schristos }
1716f11bdf1Schristos
17244d1e609Schristos (void)gettimeofday(&now, NULL);
1736f11bdf1Schristos
174fa02801fSchristos if (openpam_get_option(pamh, "no_nested") == NULL || nuser == NULL) {
175fa02801fSchristos int quiet;
176e551462eSchristos if ((flags & PAM_SILENT) != 0)
177e551462eSchristos quiet = 1;
178e551462eSchristos else {
179fa02801fSchristos #ifdef LOGIN_CAP
180216a33afSjnemeth lc = login_getpwclass(pwd);
181216a33afSjnemeth quiet = login_getcapbool(lc, "hushlogin", 0);
182216a33afSjnemeth login_close(lc);
183fa02801fSchristos #else
184fa02801fSchristos quiet = 0;
185fa02801fSchristos #endif
186e551462eSchristos }
18744d1e609Schristos #ifdef SUPPORT_UTMPX
18844d1e609Schristos doutmpx(user, rhost, tty, ss, &now);
189a3df4155Schristos dolastlogx(pamh, quiet, pwd, rhost, tty, ss, &now);
190fa02801fSchristos quiet = 1;
19144d1e609Schristos #endif
19244d1e609Schristos #ifdef SUPPORT_UTMP
19344d1e609Schristos doutmp(user, rhost, tty, &now);
194a3df4155Schristos dolastlog(pamh, quiet, pwd, rhost, tty, &now);
19544d1e609Schristos #endif
196fa02801fSchristos }
1976f11bdf1Schristos err:
1986f11bdf1Schristos if (openpam_get_option(pamh, "no_fail"))
19944d1e609Schristos return PAM_SUCCESS;
20044d1e609Schristos return pam_err;
2016f11bdf1Schristos }
2026f11bdf1Schristos
2036f11bdf1Schristos PAM_EXTERN int
pam_sm_close_session(pam_handle_t * pamh __unused,int flags __unused,int argc __unused,const char * argv[]__unused)2046f11bdf1Schristos pam_sm_close_session(pam_handle_t *pamh __unused, int flags __unused,
2056f11bdf1Schristos int argc __unused, const char *argv[] __unused)
2066f11bdf1Schristos {
207fa02801fSchristos const void *vtty, *vnuser;
208fa02801fSchristos const char *tty, *nuser;
209fa02801fSchristos
210fa02801fSchristos if (pam_get_item(pamh, PAM_NUSER, &vnuser) != PAM_SUCCESS)
211fa02801fSchristos nuser = NULL;
212fa02801fSchristos else
213fa02801fSchristos nuser = (const char *)vnuser;
2146f11bdf1Schristos
215dcdc758dShe pam_get_item(pamh, PAM_TTY, &vtty);
216dcdc758dShe if (vtty == NULL)
21744d1e609Schristos return PAM_SERVICE_ERR;
218dcdc758dShe tty = (const char *)vtty;
21944d1e609Schristos
2206f11bdf1Schristos if (strncmp(tty, _PATH_DEV, strlen(_PATH_DEV)) == 0)
22144d1e609Schristos tty = tty + strlen(_PATH_DEV);
22244d1e609Schristos
22344d1e609Schristos if (*tty == '\0')
22444d1e609Schristos return PAM_SERVICE_ERR;
22544d1e609Schristos
226fa02801fSchristos if (openpam_get_option(pamh, "no_nested") == NULL || nuser == NULL) {
227fa02801fSchristos
22844d1e609Schristos #ifdef SUPPORT_UTMPX
22944d1e609Schristos if (logoutx(tty, 0, DEAD_PROCESS))
23044d1e609Schristos logwtmpx(tty, "", "", 0, DEAD_PROCESS);
23144d1e609Schristos else
232e76ecd93Schristos logit(LOG_NOTICE, "%s(): no utmpx record for %s",
23344d1e609Schristos __func__, tty);
23444d1e609Schristos #endif
23544d1e609Schristos
23644d1e609Schristos #ifdef SUPPORT_UTMP
23744d1e609Schristos if (logout(tty))
2386f11bdf1Schristos logwtmp(tty, "", "");
23944d1e609Schristos else
240e76ecd93Schristos logit(LOG_NOTICE, "%s(): no utmp record for %s",
24144d1e609Schristos __func__, tty);
24244d1e609Schristos #endif
243fa02801fSchristos }
24444d1e609Schristos return PAM_SUCCESS;
2456f11bdf1Schristos }
2466f11bdf1Schristos
247a3df4155Schristos #if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
248a3df4155Schristos static void
domsg(pam_handle_t * pamh,time_t t,const char * host,size_t hsize,const char * line,size_t lsize)249a3df4155Schristos domsg(pam_handle_t *pamh, time_t t, const char *host, size_t hsize,
250a3df4155Schristos const char *line, size_t lsize)
251a3df4155Schristos {
252a3df4155Schristos char buf[MAXHOSTNAMELEN + 32], *promptresp = NULL;
253a3df4155Schristos int pam_err;
254a3df4155Schristos
255a3df4155Schristos if (*host) {
256a3df4155Schristos (void)snprintf(buf, sizeof(buf), "from %.*s ",
257a3df4155Schristos (int)hsize, host);
258a3df4155Schristos host = buf;
259a3df4155Schristos }
260a3df4155Schristos
261a3df4155Schristos pam_err = pam_prompt(pamh, PAM_TEXT_INFO, &promptresp,
262a3df4155Schristos "Last login: %.24s %son %.*s\n", ctime(&t), host, (int)lsize, line);
263a3df4155Schristos
264a3df4155Schristos if (pam_err == PAM_SUCCESS && promptresp)
265a3df4155Schristos free(promptresp);
266a3df4155Schristos }
267a3df4155Schristos #endif
268a3df4155Schristos
26944d1e609Schristos #ifdef SUPPORT_UTMPX
27044d1e609Schristos static void
doutmpx(const char * username,const char * hostname,const char * tty,const struct sockaddr_storage * ss,const struct timeval * now)27144d1e609Schristos doutmpx(const char *username, const char *hostname, const char *tty,
27244d1e609Schristos const struct sockaddr_storage *ss, const struct timeval *now)
27344d1e609Schristos {
27444d1e609Schristos struct utmpx utmpx;
27544d1e609Schristos const char *t;
27644d1e609Schristos
27744d1e609Schristos memset((void *)&utmpx, 0, sizeof(utmpx));
27844d1e609Schristos utmpx.ut_tv = *now;
27944d1e609Schristos (void)strncpy(utmpx.ut_name, username, sizeof(utmpx.ut_name));
28044d1e609Schristos if (hostname) {
28144d1e609Schristos (void)strncpy(utmpx.ut_host, hostname, sizeof(utmpx.ut_host));
28244d1e609Schristos if (ss)
28344d1e609Schristos utmpx.ut_ss = *ss;
28444d1e609Schristos }
28544d1e609Schristos (void)strncpy(utmpx.ut_line, tty, sizeof(utmpx.ut_line));
28644d1e609Schristos utmpx.ut_type = USER_PROCESS;
28744d1e609Schristos utmpx.ut_pid = getpid();
28844d1e609Schristos t = tty + strlen(tty);
289ee7c6ab3Slukem if ((size_t)(t - tty) >= sizeof(utmpx.ut_id)) {
29044d1e609Schristos (void)strncpy(utmpx.ut_id, t - sizeof(utmpx.ut_id),
29144d1e609Schristos sizeof(utmpx.ut_id));
29244d1e609Schristos } else {
29344d1e609Schristos (void)strncpy(utmpx.ut_id, tty, sizeof(utmpx.ut_id));
29444d1e609Schristos }
29544d1e609Schristos if (pututxline(&utmpx) == NULL)
2961b695acdSchristos logit(LOG_NOTICE, "Cannot update utmpx: %s", strerror(errno));
29744d1e609Schristos endutxent();
29844d1e609Schristos if (updwtmpx(_PATH_WTMPX, &utmpx) != 0)
2991b695acdSchristos logit(LOG_NOTICE, "Cannot update wtmpx: %s", strerror(errno));
30044d1e609Schristos }
30144d1e609Schristos
30244d1e609Schristos static void
dolastlogx(pam_handle_t * pamh,int quiet,const struct passwd * pwd,const char * hostname,const char * tty,const struct sockaddr_storage * ss,const struct timeval * now)303a3df4155Schristos dolastlogx(pam_handle_t *pamh, int quiet, const struct passwd *pwd,
304a3df4155Schristos const char *hostname, const char *tty, const struct sockaddr_storage *ss,
30544d1e609Schristos const struct timeval *now)
30644d1e609Schristos {
30744d1e609Schristos struct lastlogx ll;
30844d1e609Schristos if (!quiet) {
309a3df4155Schristos if (getlastlogx(_PATH_LASTLOGX, pwd->pw_uid, &ll) != NULL)
310a3df4155Schristos domsg(pamh, (time_t)ll.ll_tv.tv_sec, ll.ll_host,
311a3df4155Schristos sizeof(ll.ll_host), ll.ll_line,
312a3df4155Schristos sizeof(ll.ll_line));
31344d1e609Schristos }
31444d1e609Schristos ll.ll_tv = *now;
31544d1e609Schristos (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
316a3df4155Schristos
317a3df4155Schristos if (hostname)
31844d1e609Schristos (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
319a3df4155Schristos else
320a3df4155Schristos (void)memset(ll.ll_host, 0, sizeof(ll.ll_host));
321a3df4155Schristos
32244d1e609Schristos if (ss)
32344d1e609Schristos ll.ll_ss = *ss;
324a3df4155Schristos else
325a3df4155Schristos (void)memset(&ll.ll_ss, 0, sizeof(ll.ll_ss));
326a3df4155Schristos
32744d1e609Schristos if (updlastlogx(_PATH_LASTLOGX, pwd->pw_uid, &ll) != 0)
3281b695acdSchristos logit(LOG_NOTICE, "Cannot update lastlogx: %s", strerror(errno));
32944d1e609Schristos PAM_LOG("Login recorded in %s", _PATH_LASTLOGX);
33044d1e609Schristos }
33144d1e609Schristos #endif
33244d1e609Schristos
33344d1e609Schristos #ifdef SUPPORT_UTMP
33444d1e609Schristos static void
doutmp(const char * username,const char * hostname,const char * tty,const struct timeval * now)33544d1e609Schristos doutmp(const char *username, const char *hostname, const char *tty,
33644d1e609Schristos const struct timeval *now)
33744d1e609Schristos {
33844d1e609Schristos struct utmp utmp;
33944d1e609Schristos
34044d1e609Schristos (void)memset((void *)&utmp, 0, sizeof(utmp));
34144d1e609Schristos utmp.ut_time = now->tv_sec;
34244d1e609Schristos (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
34344d1e609Schristos if (hostname)
34444d1e609Schristos (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
34544d1e609Schristos (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
34644d1e609Schristos login(&utmp);
34744d1e609Schristos }
34844d1e609Schristos
34944d1e609Schristos static void
dolastlog(pam_handle_t * pamh,int quiet,const struct passwd * pwd,const char * hostname,const char * tty,const struct timeval * now)350a3df4155Schristos dolastlog(pam_handle_t *pamh, int quiet, const struct passwd *pwd,
351a3df4155Schristos const char *hostname, const char *tty, const struct timeval *now)
35244d1e609Schristos {
35344d1e609Schristos struct lastlog ll;
35444d1e609Schristos int fd;
35544d1e609Schristos
356a3df4155Schristos if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) == -1) {
3571b695acdSchristos logit(LOG_NOTICE, "Cannot open `%s': %s", _PATH_LASTLOG,
3581b695acdSchristos strerror(errno));
359a3df4155Schristos return;
360a3df4155Schristos }
36144d1e609Schristos (void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), SEEK_SET);
362a3df4155Schristos
36344d1e609Schristos if (!quiet) {
36444d1e609Schristos if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
365a3df4155Schristos ll.ll_time != 0)
366a3df4155Schristos domsg(pamh, ll.ll_time, ll.ll_host,
367a3df4155Schristos sizeof(ll.ll_host), ll.ll_line,
368a3df4155Schristos sizeof(ll.ll_line));
369a3df4155Schristos (void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), SEEK_SET);
37044d1e609Schristos }
371a3df4155Schristos
37244d1e609Schristos ll.ll_time = now->tv_sec;
37344d1e609Schristos (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
374a3df4155Schristos
37544d1e609Schristos if (hostname)
37644d1e609Schristos (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
377a3df4155Schristos else
378a3df4155Schristos (void)memset(ll.ll_host, 0, sizeof(ll.ll_host));
379a3df4155Schristos
380a3df4155Schristos (void)write(fd, &ll, sizeof(ll));
38144d1e609Schristos (void)close(fd);
382a3df4155Schristos
38344d1e609Schristos PAM_LOG("Login recorded in %s", _PATH_LASTLOG);
38444d1e609Schristos }
38544d1e609Schristos #endif
38644d1e609Schristos
3876f11bdf1Schristos PAM_MODULE_ENTRY("pam_lastlog");
388