/*
 * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

Fixes for bugs:
5015296, P1, gnome/screensaver - xscreensaver doesn't audit
6417168, P3, gnome/screensaver - xscreensaver loops while trying to unlock a session for a user whose password was expired
7008058, P3, screensaver continues to accept old password for existing sessions after password changed
---
 driver/Makefile.in  |    6 +-
 driver/passwd-pam.c |  177 ++++++++++++++++++++++++++++++++++++++++++++++++---
 driver/passwd.c     |    4 +
 3 files changed, 174 insertions(+), 13 deletions(-)

diff --git xscreensaver-5.11/driver/Makefile.in xscreensaver-5.11/driver/Makefile.in
--- xscreensaver-5.11/driver/Makefile.in
+++ xscreensaver-5.11/driver/Makefile.in
@@ -109,8 +109,8 @@ TRUSTED_LIBS	= -lglib-2.0 -lsecdb
 TRUSTED_SRCS	= trusted-utils.c
 TRUSTED_OBJS	= trusted-utils.o
 
-PWENT_SRCS	= passwd-pwent.c
-PWENT_OBJS	= passwd-pwent.o
+PWENT_SRCS	= # passwd-pwent.c - Not used in Solaris builds
+PWENT_OBJS	= # passwd-pwent.o - Not used in Solaris builds
 
 KERBEROS_SRCS	= passwd-kerberos.c
 KERBEROS_OBJS	= passwd-kerberos.o
@@ -217,7 +217,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 -lXi $(X_EXTRA_LIBS) \
-		  $(PASSWD_LIBS)
+		  -lbsm $(PASSWD_LIBS)
 
 CMD_LIBS	= $(LIBS) $(X_LIBS) \
 		  $(X_PRE_LIBS) -lX11 -lXext $(X_EXTRA_LIBS)
diff --git xscreensaver-5.11/driver/passwd-pam.c xscreensaver-5.11/driver/passwd-pam.c
--- xscreensaver-5.11/driver/passwd-pam.c
+++ xscreensaver-5.11/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,22 +436,27 @@ 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)
     {
-      write_to_child (si, "ul_ok", PAM_STRERROR (pamh, pam_auth_status));
       if (verbose_p)
-        sleep (1);
+        write_to_child (si, "ul_ok", PAM_STRERROR (pamh, pam_auth_status));
     }
   else if (si->unlock_state != ul_cancel && si->unlock_state != ul_time)
     {
       write_to_child (si, "ul_fail", PAM_STRERROR (pamh, pam_auth_status));
-      if (verbose_p)
-        sleep (1);
-      else
-        usleep (500000); /* sleep for 1/2 of sec */
     }
+  if (verbose_p)
+    sleep (1);
 #endif
 
   if (verbose_p)
@@ -352,9 +486,19 @@ pam_try_unlock(saver_info *si, Bool verbose_p,
 #ifdef HAVE_XSCREENSAVER_LOCK
       /* Send status message to unlock dialog ***/
       if (acct_rc == PAM_SUCCESS)
-        write_to_child (si, "ul_acct_ok", PAM_STRERROR(pamh, acct_rc));
+        {
+          if (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
@@ -383,6 +527,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;
@@ -414,9 +562,18 @@ pam_try_unlock(saver_info *si, Bool verbose_p,
 #ifdef HAVE_XSCREENSAVER_LOCK
       /* Send status message to unlock dialog ***/
       if (setcred_rc == PAM_SUCCESS)
-        write_to_child (si, "ul_setcred_ok", PAM_STRERROR(pamh, setcred_rc));
+        {
+          if (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 xscreensaver-5.11/driver/passwd.c xscreensaver-5.11/driver/passwd.c
--- xscreensaver-5.11/driver/passwd.c
+++ xscreensaver-5.11/driver/passwd.c
@@ -79,9 +79,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);
@@ -105,8 +107,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
 };
 
 
1.7.3.2

