--- pam_unix2-2.7.2.1/doc/pam_unix2.8	1 Mar 2011 16:25:10 -0000	1.7
+++ pam_unix2-2.7.2.1/doc/pam_unix2.8	22 Sep 2011 13:41:34 -0000
@@ -1,7 +1,7 @@
 .\" -*- nroff -*-
-.\" Copyright (c) 2002, 2003, 2004, 2006 Thorsten Kukuk kukuk@suse.de
+.\" Copyright (c) 2002, 2003, 2004, 2006, 2011 Thorsten Kukuk kukuk@suse.de
 .\"
-.TH pam_unix2 8 "August 2006" "pam_unix2" "Reference Manual"
+.TH pam_unix2 8 "September 2011" "pam_unix2" "Reference Manual"
 .SH NAME
 pam_unix2 - Standard PAM module for traditional password authentication
 .SH DESCRIPTION
@@ -21,6 +21,14 @@
 .B /etc/default/passwd
 defines, which password encryption algorithm should be used in case of a
 password change.
+.PP
+If a user matches an entry in
+.BR /etc/security/uname26.conf
+and a kernel 3.0 or newer is running, the kernel version number will
+be changed for this session to 2.6.40. Every line of that config file
+is an own entry in
+.BR regexec (3p)
+format.
 .SH OPTIONS
 The following options may be passed to all types of management groups
 except
--- pam_unix2-2.7.2.1/src/unix_sess.c	28 Aug 2008 15:11:04 -0000	1.10
+++ pam_unix2-2.7.2.1/src/unix_sess.c	22 Sep 2011 13:41:34 -0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2008 SUSE Linux Products GmbH Nuernberg,Germany.
+ * Copyright (c) 2006, 2008, 2011 SUSE Linux Products GmbH Nuernberg,Germany.
  * Copyright (c) 1999, 2000, 2002, 2003, 2004 SuSE GmbH Nuernberg, Germany.
  * Author: Thorsten Kukuk <kukuk@suse.de>
  *
@@ -53,24 +53,13 @@
 #include "public.h"
 
 static int
-pam_log_session (pam_handle_t *pamh, int flags, int argc,
-		 const char **argv, const char *kind)
+pam_log_session (pam_handle_t *pamh, const char *kind, options_t *options)
 {
   int retval;
   const char *name;
   char *service, *tty, *rhost;
-  options_t options;
   char *logmsg = NULL;
 
-  memset (&options, 0, sizeof (options));
-  options.log_level = -1; /* Initialize to default "none".  */
-
-  if (get_options (pamh, &options, "session", argc, argv) < 0)
-    {
-      pam_syslog (pamh, LOG_ERR, "cannot get options");
-      return PAM_SYSTEM_ERR;
-    }
-
   /* get the user name */
   if ((retval = pam_get_user (pamh, &name, NULL)) != PAM_SUCCESS)
     return retval;
@@ -80,7 +69,7 @@
 
   /* Move this after getting the user name, else PAM test suite
      will not pass ... */
-  if (options.log_level == -1)
+  if (options->log_level == -1)
     return PAM_SUCCESS;
 
   retval = pam_get_item (pamh, PAM_SERVICE, (void *) &service);
@@ -124,22 +113,245 @@
 	return PAM_SESSION_ERR;
     }
 
-  pam_syslog (pamh, options.log_level, logmsg);
+  pam_syslog (pamh, options->log_level, logmsg);
   free (logmsg);
 
   return PAM_SUCCESS;
 }
 
+#include <errno.h>
+#include <syscall.h>
+#include <linux/personality.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <regex.h>
+
+#define set_pers(pers) ((long)syscall(SYS_personality, pers))
+
+#define UNAME26_CONF "/etc/security/uname26.conf"
+
+static int
+set_arch(pam_handle_t *pamh, const char *pers, unsigned long options)
+{
+  struct utsname un;
+  int i;
+  unsigned long pers_value, res;
+
+  struct {
+    int perval;
+    const char *target_arch, *result_arch;
+  } transitions[] = {
+    {PER_LINUX32, "linux32", NULL},
+    {PER_LINUX, "linux64", NULL},
+#if defined(__powerpc__) || defined(__powerpc64__)
+    {PER_LINUX32, "ppc32", "ppc"},
+    {PER_LINUX32, "ppc", "ppc"},
+    {PER_LINUX, "ppc64", "ppc64"},
+    {PER_LINUX, "ppc64pseries", "ppc64"},
+    {PER_LINUX, "ppc64iseries", "ppc64"},
+#endif
+#if defined(__x86_64__) || defined(__i386__) || defined(__ia64__)
+    {PER_LINUX32, "i386", "i386"},
+    {PER_LINUX32, "i486", "i386"},
+    {PER_LINUX32, "i586", "i386"},
+    {PER_LINUX32, "i686", "i386"},
+    {PER_LINUX32, "athlon", "i386"},
+#endif
+#if defined(__x86_64__) || defined(__i386__)
+    {PER_LINUX, "x86_64", "x86_64"},
+#endif
+#if defined(__ia64__) || defined(__i386__)
+    {PER_LINUX, "ia64", "ia64"},
+#endif
+#if defined(__hppa__)
+    {PER_LINUX32, "parisc32", "parisc"},
+    {PER_LINUX32, "parisc", "parisc"},
+    {PER_LINUX, "parisc64", "parisc64"},
+#endif
+#if defined(__s390x__) || defined(__s390__)
+    {PER_LINUX32, "s390", "s390"},
+    {PER_LINUX, "s390x", "s390x"},
+#endif
+#if defined(__sparc64__) || defined(__sparc__)
+    {PER_LINUX32, "sparc", "sparc"},
+    {PER_LINUX32, "sparc32bash", "sparc"},
+    {PER_LINUX32, "sparc32", "sparc"},
+    {PER_LINUX, "sparc64", "sparc64"},
+#endif
+#if defined(__mips64__) || defined(__mips__)
+    {PER_LINUX32, "mips32", "mips"},
+    {PER_LINUX32, "mips", "mips"},
+    {PER_LINUX, "mips64", "mips64"},
+#endif
+#if defined(__alpha__)
+    {PER_LINUX, "alpha", "alpha"},
+    {PER_LINUX, "alphaev5", "alpha"},
+    {PER_LINUX, "alphaev56", "alpha"},
+    {PER_LINUX, "alphaev6", "alpha"},
+    {PER_LINUX, "alphaev67", "alpha"},
+#endif
+    {-1, NULL, NULL}
+  };
+
+  for(i = 0; transitions[i].perval >= 0; i++)
+      if(!strcmp(pers, transitions[i].target_arch))
+        break;
+
+  if(transitions[i].perval < 0)
+    {
+      pam_syslog (pamh, LOG_ERR, "%s: Unrecognized architecture", pers);
+      return PAM_SYSTEM_ERR;
+    }
+
+  pers_value = transitions[i].perval | options;
+  res = set_pers(pers_value);
+  if(res == -EINVAL)
+    return PAM_SYSTEM_ERR;
+
+  uname(&un);
+  if(transitions[i].result_arch &&
+        strcmp(un.machine, transitions[i].result_arch))
+  {
+    if(strcmp(transitions[i].result_arch, "i386")
+       || (strcmp(un.machine, "i486")
+           && strcmp(un.machine, "i586")
+           && strcmp(un.machine, "i686")
+           && strcmp(un.machine, "athlon")))
+      {
+	pam_syslog (pamh, LOG_ERR, "%s: Unrecognized architecture", pers);
+	return PAM_SYSTEM_ERR;
+      }
+  }
+
+  return PAM_SUCCESS;
+}
+
+
+static int
+uname26 (pam_handle_t *pamh, options_t *options)
+{
+# define UNAME26 0x0020000
+  const char *name;
+  char accountline[256];
+  struct utsname un;
+  struct stat st;
+  int found = 0;
+  int retval;
+  FILE *fp;
+
+  if (stat(UNAME26_CONF, &st) != 0)
+    {
+      if (options->log_level == LOG_DEBUG)
+	pam_syslog (pamh, LOG_DEBUG, "%s not found", UNAME26_CONF);
+
+      return PAM_SUCCESS; /* no file, no error */
+    }
+
+  /* get the user name */
+  if ((retval = pam_get_user (pamh, &name, NULL)) != PAM_SUCCESS)
+    return retval;
+
+  if (name == NULL || name[0] == '\0')
+    return PAM_SESSION_ERR;
+
+  fp = fopen(UNAME26_CONF, "r");
+  if (fp == NULL )
+    { /* Check that we opened it successfully */
+      pam_syslog(pamh, LOG_ERR, "Error opening %s: %m", UNAME26_CONF);
+      return PAM_SERVICE_ERR;
+    }
+
+  while (fgets(accountline, sizeof(accountline)-1, fp) != NULL)
+    {
+      regex_t reg;
+      int result;
+
+      if (strlen (accountline) < 1)
+	continue;
+
+      if (accountline[0] == '#')
+	continue;
+
+      if (accountline[strlen(accountline) - 1] == '\n')
+	accountline[strlen(accountline) - 1] = '\0';
+
+        memset (&reg, 0, sizeof (regex_t));
+	result = regcomp (&reg, accountline, 0);
+
+	if (result)
+	  {
+	    size_t length = regerror (result, &reg, NULL, 0);
+	    char *buffer = malloc (length);
+	    if (buffer == NULL)
+	      pam_syslog (pamh, LOG_ERR, "running out of memory!");
+	    else
+	      {
+		regerror (result, &reg, buffer, length);
+		pam_syslog (pamh, LOG_ERR,
+			    "Can't compile regular expression: %s",
+			    buffer);
+		return PAM_SYSTEM_ERR;
+	      }
+	  }
+
+	if (regexec (&reg, name, 0, NULL, 0) == 0)
+	  {
+	    if (options->log_level == LOG_DEBUG)
+	      pam_syslog (pamh, LOG_DEBUG, "%s matches %s",
+			  name, accountline);
+	    found = 1;
+	    break;
+	  }
+	else if (options->log_level == LOG_DEBUG)
+	  pam_syslog (pamh, LOG_DEBUG, "%s does not match %s",
+		      name, accountline);
+    }
+  fclose(fp);
+
+  if (!found)
+    return PAM_SUCCESS;
+
+  uname(&un);
+  return set_arch (pamh, un.machine, UNAME26);
+}
+
 int
 pam_sm_open_session (pam_handle_t *pamh, int flags, int argc,
 		     const char **argv)
 {
-  return pam_log_session (pamh, flags, argc, argv, "started");
+  int retval;
+  options_t options;
+
+  memset (&options, 0, sizeof (options));
+  options.log_level = -1; /* Initialize to default "none".  */
+
+  if (get_options (pamh, &options, "session", argc, argv) < 0)
+    {
+      pam_syslog (pamh, LOG_ERR, "cannot get options");
+      return PAM_SYSTEM_ERR;
+    }
+
+  retval = pam_log_session (pamh, "started", &options);
+  if (retval != PAM_SUCCESS)
+    return retval;
+
+  return uname26 (pamh, &options);
 }
 
 int
 pam_sm_close_session (pam_handle_t * pamh, int flags,
 		      int argc, const char **argv)
 {
-  return pam_log_session (pamh, flags, argc, argv, "finished");
+  options_t options;
+
+  memset (&options, 0, sizeof (options));
+  options.log_level = -1; /* Initialize to default "none".  */
+
+  if (get_options (pamh, &options, "session", argc, argv) < 0)
+    {
+      pam_syslog (pamh, LOG_ERR, "cannot get options");
+      return PAM_SYSTEM_ERR;
+    }
+
+  return pam_log_session (pamh, "finished", &options);
 }
