From ff65f319d40fdc0d5f9d316b9c5d48d7cdebc6b6 Mon Sep 17 00:00:00 2001
From: Alan Coopersmith <alan.coopersmith@oracle.com>
Date: Sat, 2 Jan 2016 22:38:35 -0800
Subject: [PATCH] pam audit

Fixes for bugs:
15201967 SUNBT5015296 xscreensaver doesn't audit
15326852 SUNBT6417168 xscreensaver loops while trying to unlock a session
	 	      for a user whose password was expired
15452882 SUNBT6654320 xscreensaver dies due to memory corruption
15688159 SUNBT7008058 screensaver continues to accept old password for
	 	      existing sessions after password changed

Also ensures that Xscreensaver on Solaris only uses PAM, and never attempts
to fallback to direct use of getpwent(), which isn't audited

Upstream status unknown.
---
 driver/Makefile.in  |   2 +-
 driver/passwd-pam.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 driver/passwd.c     |   4 ++
 3 files changed, 161 insertions(+), 3 deletions(-)

diff --git a/driver/Makefile.in b/driver/Makefile.in
index a44e312..7aec6d9 100644
--- a/driver/Makefile.in
+++ b/driver/Makefile.in
@@ -212,7 +212,7 @@ PDF2JPEG_LIBS	= -framework Cocoa
 SAVER_LIBS	= $(LIBS) $(X_LIBS) $(XMU_LIBS) @SAVER_LIBS@ \
 		  $(XDPMS_LIBS) $(XINERAMA_LIBS) $(GL_LIBS) $(X_PRE_LIBS) \
 		  -lXt -lX11 -lXext $(X_EXTRA_LIBS) \
-		  $(PASSWD_LIBS)
+		  -lbsm $(PASSWD_LIBS)
 
 CMD_LIBS	= $(LIBS) $(X_LIBS) \
 		  $(X_PRE_LIBS) -lX11 -lXext $(X_EXTRA_LIBS)
diff --git a/driver/passwd-pam.c b/driver/passwd-pam.c
index 93c73f1..4d24ac3 100644
--- a/driver/passwd-pam.c
+++ b/driver/passwd-pam.c
@@ -47,6 +47,8 @@
 
 #ifdef __sun
 # include <deflt.h>
+# include <bsm/adt.h>
+# include <bsm/adt_event.h>
 #endif
 
 extern char *blurb(void);
@@ -81,6 +83,9 @@ extern void unblock_sigchld (void);
 #undef countof
 #define countof(x) (sizeof((x))/sizeof(*(x)))
 
+static struct pam_response *reply = 0; /*making it global so we can free it */
+static int replies = 0;
+
 /* Some time between Red Hat 4.2 and 7.0, the words were transposed 
    in the various PAM_x_CRED macro names.  Yay!
  */
@@ -178,6 +183,124 @@ Bool pam_priv_init (int argc, char **argv, Bool verbose_p);
  */
 static void *suns_pam_implementation_blows = 0;
 
+#ifdef __sun
+#include <syslog.h>
+#include <bsm/adt.h>
+#include <bsm/adt_event.h>
+
+static Bool audit_flag_global = True;
+
+/*
+ * audit_lock - audit entry to screenlock
+ *
+ *      Entry   Process running with appropriate privilege to generate
+ *                      audit records and real uid of the user.
+ *
+ *      Exit    ADT_screenlock audit record written.
+ */
+void
+audit_lock(void)
+{
+  adt_session_data_t      *ah;          /* audit session handle */
+  adt_event_data_t        *event;       /* audit event handle */
+
+  /* Audit start of screen lock -- equivalent to logout ;-) */
+  if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0)
+    {
+      syslog(LOG_AUTH | LOG_ALERT, "adt_start_session: %m");
+      return;
+    }
+  if ((event = adt_alloc_event(ah, ADT_screenlock)) == NULL)
+    {
+      syslog(LOG_AUTH | LOG_ALERT, "adt_alloc_event(ADT_screenlock): %m");
+    } else {
+      if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0)
+        {
+          syslog(LOG_AUTH | LOG_ALERT, "adt_put_event(ADT_screenlock): %m");
+        }
+      adt_free_event(event);
+    }
+  (void) adt_end_session(ah);
+}
+
+/*
+ * audit_unlock - audit screen unlock
+ *
+ *      Entry   Process running with appropriate privilege to generate
+ *                      audit records and real uid of the user.
+ *              pam_status = PAM error code; reason for failure.
+ *
+ *      Exit    ADT_screenunlock audit record written.
+ */
+static void
+audit_unlock(int pam_status)
+{
+  adt_session_data_t      *ah;          /* audit session handle */
+  adt_event_data_t        *event;       /* audit event handle */
+
+  if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0)
+    {
+      syslog(LOG_AUTH | LOG_ALERT,
+             "adt_start_session(ADT_screenunlock): %m");
+      return;
+    }
+  if ((event = adt_alloc_event(ah, ADT_screenunlock)) == NULL)
+    {
+      syslog(LOG_AUTH | LOG_ALERT,
+             "adt_alloc_event(ADT_screenunlock): %m");
+    } else {
+      if (adt_put_event(event,
+                        pam_status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAILURE,
+                        pam_status == PAM_SUCCESS ? ADT_SUCCESS
+                                                  : ADT_FAIL_PAM + pam_status)
+          != 0)
+        {
+          syslog(LOG_AUTH | LOG_ALERT,
+                 "adt_put_event(ADT_screenunlock(%s): %m",
+                 pam_strerror(NULL, pam_status));
+        }
+      adt_free_event(event);
+    }
+  (void) adt_end_session(ah);
+}
+
+/*
+ * audit_passwd - audit password change
+ *      Entry   Process running with appropriate privilege to generate
+ *                      audit records and real uid of the user.
+ *              pam_status = PAM error code; reason for failure.
+ *
+ *      Exit    ADT_passwd audit record written.
+ */
+static void
+audit_passwd(int pam_status)
+{
+  adt_session_data_t      *ah;          /* audit session handle */
+  adt_event_data_t        *event;       /* audit event handle */
+
+  if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0)
+    {
+      syslog(LOG_AUTH | LOG_ALERT, "adt_start_session(ADT_passwd): %m");
+      return;
+    }
+  if ((event = adt_alloc_event(ah, ADT_passwd)) == NULL)
+    {
+      syslog(LOG_AUTH | LOG_ALERT, "adt_alloc_event(ADT_passwd): %m");
+    } else {
+      if (adt_put_event(event,
+                        pam_status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAILURE,
+                        pam_status == PAM_SUCCESS ? ADT_SUCCESS
+                                                  : ADT_FAIL_PAM + pam_status)
+          != 0)
+        {
+          syslog(LOG_AUTH | LOG_ALERT, "adt_put_event(ADT_passwd(%s): %m",
+                 pam_strerror(NULL, pam_status));
+        }
+      adt_free_event(event);
+    }
+  (void) adt_end_session(ah);
+}
+#endif /* sun */
 
 /**
  * This function is the PAM conversation driver. It conducts a full
@@ -231,6 +354,12 @@ pam_try_unlock(saver_info *si, Bool verbose_p,
     fprintf (stderr, "%s: pam_start (\"%s\", \"%s\", ...) ==> %d (%s)\n",
              blurb(), service, si->user,
              status, PAM_STRERROR (pamh, status));
+
+#ifdef __sun
+  if (audit_flag_global) /* We want one audit lock log per lock */
+    audit_lock ();
+#endif /**sun*/
+
   if (status != PAM_SUCCESS) goto DONE;
 
 #ifdef __sun
@@ -307,6 +436,14 @@ pam_try_unlock(saver_info *si, Bool verbose_p,
 # endif /* HAVE_SIGTIMEDWAIT */
   unblock_sigchld();
 
+#ifdef __sun
+  audit_unlock(pam_auth_status);
+  if (pam_auth_status == PAM_SUCCESS)
+    audit_flag_global = True;
+  else
+    audit_flag_global = False;
+#endif /*sun*/
+
 #ifdef HAVE_XSCREENSAVER_LOCK
   /* Send status message to unlock dialog */
   if (pam_auth_status == PAM_SUCCESS)
@@ -354,7 +491,14 @@ pam_try_unlock(saver_info *si, Bool verbose_p,
             write_to_child (si, "ul_acct_ok", PAM_STRERROR(pamh, acct_rc));
         }
       else
-        write_to_child (si, "ul_acct_fail", PAM_STRERROR(pamh, acct_rc));
+        {
+#ifdef __sun
+          /* Only in failure of pam_acct_mgmt case we call audit */
+          audit_unlock (acct_rc);
+#endif /*sun*/
+
+          write_to_child (si, "ul_acct_fail", PAM_STRERROR(pamh, acct_rc));
+        }
       if (verbose_p)
         sleep (1);
 #endif
@@ -384,6 +528,10 @@ pam_try_unlock(saver_info *si, Bool verbose_p,
             fprintf (stderr, "%s: pam_chauthtok (...) ==> %d (%s)\n",
                      blurb(), chauth_rc, PAM_STRERROR(pamh, chauth_rc));
 
+#ifdef __sun
+          audit_passwd (chauth_rc);
+#endif /* sun */
+
           if (chauth_rc != PAM_SUCCESS)
             {
               pam_auth_status = chauth_rc;
@@ -433,7 +581,13 @@ pam_try_unlock(saver_info *si, Bool verbose_p,
             write_to_child (si, "ul_setcred_ok", PAM_STRERROR(pamh, setcred_rc));
         }
       else
-        write_to_child (si, "ul_setcred_fail", PAM_STRERROR(pamh, setcred_rc));
+        {
+#ifdef __sun
+          /* Only in failure of pam_setcred() case we call audit. */
+          audit_unlock (setcred_rc);
+#endif /*sun*/
+          write_to_child (si, "ul_setcred_fail", PAM_STRERROR(pamh, setcred_rc));
+        }
       if (verbose_p)
         sleep (1);
 #endif
diff --git a/driver/passwd.c b/driver/passwd.c
index 0618642..350dc4d 100644
--- a/driver/passwd.c
+++ b/driver/passwd.c
@@ -81,9 +81,11 @@ extern void pam_try_unlock (saver_info *si, Bool verbose_p,
 extern Bool ext_priv_init (int argc, char **argv, Bool verbose_p);
 extern Bool ext_passwd_valid_p (const char *typed_passwd, Bool verbose_p);
 #endif
+#ifndef __sun /* Only use PAM on Solaris, not direct getpwent */
 extern Bool pwent_lock_init (int argc, char **argv, Bool verbose_p);
 extern Bool pwent_priv_init (int argc, char **argv, Bool verbose_p);
 extern Bool pwent_passwd_valid_p (const char *typed_passwd, Bool verbose_p);
+#endif
 
 Bool lock_priv_init (int argc, char **argv, Bool verbose_p);
 Bool lock_init (int argc, char **argv, Bool verbose_p);
@@ -107,8 +109,10 @@ struct auth_methods methods[] = {
   { "external",		0, ext_priv_init, ext_passwd_valid_p, 0,
   			False, False },
 # endif
+# ifndef __sun /* Only use PAM on Solaris, not direct getpwent */
   { "normal",           pwent_lock_init, pwent_priv_init, pwent_passwd_valid_p, 0,
                         False, False }
+# endif
 };
 
 
-- 
2.6.1

