experimental import of pam_lastlog from netbsd
authorAlex Hornung <ahornung@gmail.com>
Sun, 4 Oct 2009 18:17:45 +0000 (19:17 +0100)
committerAlex Hornung <ahornung@gmail.com>
Wed, 15 Dec 2010 17:46:03 +0000 (17:46 +0000)
experimental import of pam_lastlog from netbsd

lib/pam_module/pam_lastlog/pam_lastlog.c

index 5cb931c..ab59265 100644 (file)
@@ -57,7 +57,6 @@
 #include <syslog.h>
 #include <time.h>
 #include <unistd.h>
-#include <utmp.h>
 
 #define PAM_SM_SESSION
 
 #include <security/pam_modules.h>
 #include <security/pam_mod_misc.h>
 
+#define SUPPORT_UTMP 1
+#define SUPPORT_UTMPX 1
+
+#ifdef SUPPORT_UTMP
+#include <utmp.h>
+static void doutmp(const char *, const char *, const char *,
+    const struct timeval *);
+static void dolastlog(pam_handle_t *, int, const struct passwd *, const char *,
+    const char *, const struct timeval *);
+#endif
+
+#ifdef SUPPORT_UTMPX
+#include <utmpx.h>
+static void doutmpx(const char *, const char *, const char *,
+    const struct sockaddr_storage *ss, const struct timeval *);
+static void dolastlogx(pam_handle_t *, int, const struct passwd *, const char *,
+    const char *, const struct sockaddr_storage *ss, const struct timeval *);
+#endif
+
+#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
+static void domsg(pam_handle_t *, time_t, const char *, size_t, const char *,
+    size_t);
+#endif
+
+static void
+logit(int level, const char *fmt, ...)
+{
+       va_list ap;
+
+       openlog("pam_lastlog", LOG_PID, LOG_AUTHPRIV);
+       va_start(ap, fmt);
+       vsyslog_r(level, fmt, ap);
+       va_end(ap);
+       closelog();
+}
+
+
 PAM_EXTERN int
 pam_sm_open_session(pam_handle_t *pamh, int flags,
-                   int argc __unused, const char *argv[] __unused)
+    int argc __unused, const char *argv[] __unused)
 {
-       struct passwd *pwd;
-       struct utmp utmp;
-       struct lastlog ll;
-       time_t t;
-       const char *user;
-       const void *rhost, *tty;
-       off_t llpos;
-       int fd, pam_err;
+       struct passwd *pwd, pwres;
+       struct timeval now;
+       const char *user, *rhost, *tty;
+       const void *vrhost, *vtty;
+       int pam_err;
+       int quiet;
+       char pwbuf[1024];
+#ifdef LOGIN_CAP
+       login_cap_t *lc;
+#endif
 
        pam_err = pam_get_user(pamh, &user, NULL);
        if (pam_err != PAM_SUCCESS)
-               return (pam_err);
-       if (user == NULL || (pwd = getpwnam(user)) == NULL)
-               return (PAM_SERVICE_ERR);
+               return pam_err;
+
+       if (user == NULL ||
+           getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
+           pwd == NULL)
+               return PAM_SERVICE_ERR;
+
        PAM_LOG("Got user: %s", user);
 
-       pam_err = pam_get_item(pamh, PAM_RHOST, &rhost);
-       if (pam_err != PAM_SUCCESS) {
-               PAM_LOG("No PAM_RHOST");
+       pam_err = pam_get_item(pamh, PAM_RHOST, &vrhost);
+       if (pam_err != PAM_SUCCESS)
                goto err;
-       }
-       pam_err = pam_get_item(pamh, PAM_TTY, &tty);
-       if (pam_err != PAM_SUCCESS) {
-               PAM_LOG("No PAM_TTY");
+       rhost = (const char *)vrhost;
+
+       pam_err = pam_get_item(pamh, PAM_TTY, &vtty);
+       if (pam_err != PAM_SUCCESS)
                goto err;
-       }
+       tty = (const char *)vtty;
+
        if (tty == NULL) {
-               PAM_LOG("No PAM_TTY");
                pam_err = PAM_SERVICE_ERR;
                goto err;
        }
+
        if (strncmp(tty, _PATH_DEV, strlen(_PATH_DEV)) == 0)
-               tty = (const char *)tty + strlen(_PATH_DEV);
-       if (*(const char *)tty == '\0')
-               return (PAM_SERVICE_ERR);
-
-       fd = open(_PATH_LASTLOG, O_RDWR|O_CREAT, 0644);
-       if (fd == -1) {
-               PAM_LOG("Failed to open %s", _PATH_LASTLOG);
-               goto file_err;
-       }
+               tty = tty + strlen(_PATH_DEV);
 
-       /*
-        * Record session in lastlog(5).
-        */
-       llpos = (off_t)(pwd->pw_uid * sizeof(ll));
-       if (lseek(fd, llpos, L_SET) != llpos)
-               goto file_err;
-       if ((flags & PAM_SILENT) == 0) {
-               if (read(fd, &ll, sizeof ll) == sizeof ll && ll.ll_time != 0) {
-                       t = ll.ll_time;
-                       if (*ll.ll_host != '\0')
-                               pam_info(pamh, "Last login: %.*s from %.*s",
-                                   24 - 5, ctime(&t),
-                                   (int)sizeof(ll.ll_host), ll.ll_host);
-                       else
-                               pam_info(pamh, "Last login: %.*s on %.*s",
-                                   24 - 5, ctime(&t),
-                                   (int)sizeof(ll.ll_line), ll.ll_line);
-               }
-               if (lseek(fd, llpos, L_SET) != llpos)
-                       goto file_err;
+       if (*tty == '\0') {
+               pam_err = PAM_SERVICE_ERR;
+               goto err;
        }
 
-       bzero(&ll, sizeof(ll));
-       ll.ll_time = time(NULL);
+       (void)gettimeofday(&now, NULL);
+
+       if ((flags & PAM_SILENT) != 0)
+               quiet = 1;
+       else {
+#ifdef LOGIN_CAP
+               lc = login_getpwclass(pwd);
+               quiet = login_getcapbool(lc, "hushlogin", 0);
+               login_close(lc);
+#else
+               quiet = 0;
+#endif
+       }
+#ifdef SUPPORT_UTMPX
+       doutmpx(user, rhost, tty, ss, &now);
+       dolastlogx(pamh, quiet, pwd, rhost, tty, ss, &now);
+       quiet = 1;
+#endif
+#ifdef SUPPORT_UTMP
+       doutmp(user, rhost, tty, &now);
+       dolastlog(pamh, quiet, pwd, rhost, tty, &now);
+#endif
+err:
+       if (openpam_get_option(pamh, "no_fail"))
+               return PAM_SUCCESS;
+       return pam_err;
+}
 
-       /* note: does not need to be NUL-terminated */
-       strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
-       if (rhost != NULL && *(const char *)rhost != '\0')
-               /* note: does not need to be NUL-terminated */
-               strncpy(ll.ll_host, rhost, sizeof(ll.ll_host));
+PAM_EXTERN int
+pam_sm_close_session(pam_handle_t *pamh __unused, int flags __unused,
+    int argc __unused, const char *argv[] __unused)
+{
+       const void *vtty;
+       const char *tty;
 
-       if (write(fd, (char *)&ll, sizeof(ll)) != sizeof(ll) || close(fd) != 0)
-               goto file_err;
+       pam_get_item(pamh, PAM_TTY, &vtty);
+       if (vtty == NULL)
+               return PAM_SERVICE_ERR;
+       tty = (const char *)vtty;
 
-       PAM_LOG("Login recorded in %s", _PATH_LASTLOG);
+       if (strncmp(tty, _PATH_DEV, strlen(_PATH_DEV)) == 0)
+               tty = tty + strlen(_PATH_DEV);
 
-       /*
-        * Record session in utmp(5) and wtmp(5).
-        */
-       bzero(&utmp, sizeof(utmp));
-       utmp.ut_time = time(NULL);
-       /* note: does not need to be NUL-terminated */
-       strncpy(utmp.ut_name, user, sizeof(utmp.ut_name));
-       if (rhost != NULL && *(const char *)rhost != '\0')
-               strncpy(utmp.ut_host, rhost, sizeof(utmp.ut_host));
-       strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
-       login(&utmp);
+       if (*tty == '\0')
+               return PAM_SERVICE_ERR;
 
-       return (PAM_SUCCESS);
+#ifdef SUPPORT_UTMPX
+       if (logoutx(tty, 0, DEAD_PROCESS))
+               logwtmpx(tty, "", "", 0, DEAD_PROCESS);
+       else
+               logit(LOG_NOTICE, "%s(): no utmpx record for %s",
+                   __func__, tty);
+#endif
 
-file_err:
-       syslog(LOG_ERR, "%s: %m", _PATH_LASTLOG);
-       if (fd != -1)
-               close(fd);
-       pam_err = PAM_SYSTEM_ERR;
-err:
-       if (openpam_get_option(pamh, "no_fail"))
-               return (PAM_SUCCESS);
-       return (pam_err);
+#ifdef SUPPORT_UTMP
+       if (logout(tty))
+               logwtmp(tty, "", "");
+       else
+               logit(LOG_NOTICE, "%s(): no utmp record for %s",
+                   __func__, tty);
+#endif
+        return PAM_SUCCESS;
 }
 
-PAM_EXTERN int
-pam_sm_close_session(pam_handle_t *pamh __unused, int flags __unused,
-                    int argc __unused, const char *argv[] __unused)
+#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
+static void
+domsg(pam_handle_t *pamh, time_t t, const char *host, size_t hsize,
+    const char *line, size_t lsize)
 {
-       const void *tty;
+       char buf[MAXHOSTNAMELEN + 32], *promptresp = NULL;
        int pam_err;
 
-       pam_err = pam_get_item(pamh, PAM_TTY, (const void **)&tty);
-       if (pam_err != PAM_SUCCESS)
-               goto err;
-       if (strncmp(tty, _PATH_DEV, strlen(_PATH_DEV)) == 0)
-               tty = (const char *)tty + strlen(_PATH_DEV);
-       if (*(const char *)tty == '\0')
-               return (PAM_SERVICE_ERR);
-       if (logout(tty) != 1)
-               syslog(LOG_ERR, "%s(): no utmp record for %s",
-                   __func__, (const char *)tty);
-       logwtmp(tty, "", "");
-       return (PAM_SUCCESS);
-
- err:
-       if (openpam_get_option(pamh, "no_fail"))
-               return (PAM_SUCCESS);
-       return (pam_err);
+       if (*host) {
+               (void)snprintf(buf, sizeof(buf), "from %.*s ",
+                   (int)hsize, host);
+               host = buf;
+       }
+
+       pam_err = pam_prompt(pamh, PAM_TEXT_INFO, &promptresp,
+           "Last login: %.24s %son %.*s\n", ctime(&t), host, (int)lsize, line);
+
+       if (pam_err == PAM_SUCCESS && promptresp)
+               free(promptresp);
+}
+#endif
+
+#ifdef SUPPORT_UTMPX
+static void
+doutmpx(const char *username, const char *hostname, const char *tty,
+    const struct sockaddr_storage *ss, const struct timeval *now)
+{
+       struct utmpx utmpx;
+       const char *t;
+
+       memset((void *)&utmpx, 0, sizeof(utmpx));
+       utmpx.ut_tv = *now;
+       (void)strncpy(utmpx.ut_name, username, sizeof(utmpx.ut_name));
+       if (hostname) {
+               (void)strncpy(utmpx.ut_host, hostname, sizeof(utmpx.ut_host));
+               if (ss)
+                       utmpx.ut_ss = *ss;
+       }
+       (void)strncpy(utmpx.ut_line, tty, sizeof(utmpx.ut_line));
+       utmpx.ut_type = USER_PROCESS;
+       utmpx.ut_pid = getpid();
+       t = tty + strlen(tty);
+       if ((size_t)(t - tty) >= sizeof(utmpx.ut_id)) {
+               (void)strncpy(utmpx.ut_id, t - sizeof(utmpx.ut_id),
+                   sizeof(utmpx.ut_id));
+       } else {
+               (void)strncpy(utmpx.ut_id, tty, sizeof(utmpx.ut_id));
+       }
+       if (pututxline(&utmpx) == NULL)
+               logit(LOG_NOTICE, "Cannot update utmpx %m");
+       endutxent();
+       if (updwtmpx(_PATH_WTMPX, &utmpx) != 0)
+               logit(LOG_NOTICE, "Cannot update wtmpx %m");
+}
+
+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)
+{
+       struct lastlogx ll;
+       if (!quiet) {
+           if (getlastlogx(_PATH_LASTLOGX, pwd->pw_uid, &ll) != NULL)
+                   domsg(pamh, (time_t)ll.ll_tv.tv_sec, ll.ll_host,
+                       sizeof(ll.ll_host), ll.ll_line,
+                       sizeof(ll.ll_line));
+       }
+       ll.ll_tv = *now;
+       (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
+
+       if (hostname)
+               (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
+       else
+               (void)memset(ll.ll_host, 0, sizeof(ll.ll_host));
+
+       if (ss)
+               ll.ll_ss = *ss;
+       else
+               (void)memset(&ll.ll_ss, 0, sizeof(ll.ll_ss));
+
+       if (updlastlogx(_PATH_LASTLOGX, pwd->pw_uid, &ll) != 0)
+               logit(LOG_NOTICE, "Cannot update lastlogx %m");
+       PAM_LOG("Login recorded in %s", _PATH_LASTLOGX);
 }
+#endif
+
+#ifdef SUPPORT_UTMP
+static void
+doutmp(const char *username, const char *hostname, const char *tty,
+    const struct timeval *now)
+{
+       struct utmp utmp;
+
+       (void)memset((void *)&utmp, 0, sizeof(utmp));
+       utmp.ut_time = now->tv_sec;
+       (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
+       if (hostname)
+               (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
+       (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
+       login(&utmp);
+}
+
+static void
+dolastlog(pam_handle_t *pamh, int quiet, const struct passwd *pwd,
+    const char *hostname, const char *tty, const struct timeval *now)
+{
+       struct lastlog ll;
+       int fd;
+
+       if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) == -1) {
+               logit(LOG_NOTICE, "Cannot open `%s' %m", _PATH_LASTLOG);
+               return;
+       }
+       (void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), SEEK_SET);
+
+       if (!quiet) {
+               if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
+                   ll.ll_time != 0)
+                       domsg(pamh, ll.ll_time, ll.ll_host,
+                           sizeof(ll.ll_host), ll.ll_line,
+                           sizeof(ll.ll_line));
+               (void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), SEEK_SET);
+       }
+
+       ll.ll_time = now->tv_sec;
+       (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
+
+       if (hostname)
+               (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
+       else
+               (void)memset(ll.ll_host, 0, sizeof(ll.ll_host));
+
+       (void)write(fd, &ll, sizeof(ll));
+       (void)close(fd);
+
+       PAM_LOG("Login recorded in %s", _PATH_LASTLOG);
+}
+#endif
 
 PAM_MODULE_ENTRY("pam_lastlog");
+