Blob Blame History Raw
--- Linux-PAM-0.99.6.2/modules/pam_selinux/pam_selinux.8.xml.select-context	2007-02-21 20:38:10.000000000 +0100
+++ Linux-PAM-0.99.6.2/modules/pam_selinux/pam_selinux.8.xml	2007-02-21 20:38:11.000000000 +0100
@@ -33,6 +33,9 @@
       <arg choice="opt">
 	verbose
       </arg>
+      <arg choice="opt">
+	select_context
+      </arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
@@ -118,6 +121,17 @@
           </para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term>
+          <option>select_context</option>
+        </term>
+        <listitem>
+          <para>
+            Attempt to ask the user for a custom security context role.
+            If MLS is on ask also for sensitivity level.
+          </para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
--- Linux-PAM-0.99.6.2/modules/pam_selinux/pam_selinux.c.select-context	2007-02-21 20:38:10.000000000 +0100
+++ Linux-PAM-0.99.6.2/modules/pam_selinux/pam_selinux.c	2007-02-21 20:44:01.000000000 +0100
@@ -63,9 +63,64 @@
 #include <selinux/selinux.h>
 #include <selinux/get_context_list.h>
 #include <selinux/flask.h>
+#include <selinux/av_permissions.h>
 #include <selinux/selinux.h>
 #include <selinux/context.h>
+#include <selinux/get_default_type.h>
 
+#ifdef HAVE_LIBAUDIT
+#include <libaudit.h>
+#include <sys/select.h>
+#include <errno.h>
+#endif
+
+/* Send audit message */
+static
+
+int send_audit_message(pam_handle_t *pamh, int success, security_context_t default_context,
+		       security_context_t selected_context)
+{
+	int rc=0;
+#ifdef HAVE_LIBAUDIT
+	char *msg = NULL;
+	int audit_fd = audit_open();
+	security_context_t default_raw=NULL;
+	security_context_t selected_raw=NULL;
+	rc = -1;
+	if (audit_fd < 0) {
+		pam_syslog(pamh, LOG_ERR, _("Error connecting to audit system.\n"));
+		return rc;
+	}
+	if (selinux_trans_to_raw_context(default_context, &default_raw) < 0) {
+		pam_syslog(pamh, LOG_ERR, _("Error translating default context.\n"));
+		goto out;
+	}
+	if (selinux_trans_to_raw_context(selected_context, &selected_raw) < 0) {
+		pam_syslog(pamh, LOG_ERR, _("Error translating selected context.\n"));
+		goto out;
+	}
+	if (asprintf(&msg, "pam: default-context=%s selected-context=%s",
+		     default_context ? default_raw : "?",
+		     selected_context ? selected_raw : "?") < 0) {
+		pam_syslog(pamh, LOG_ERR, ("Error allocating memory.\n"));
+		goto out;
+	}
+	if (audit_log_user_message(audit_fd, AUDIT_USER_ROLE_CHANGE,
+				   msg, NULL, NULL, NULL, success) <= 0) {
+		pam_syslog(pamh, LOG_ERR, _("Error sending audit message.\n"));
+		goto out;
+	}
+	rc = 0;
+      out:
+	free(msg);
+	freecon(default_raw);
+	free(selected_raw);
+	close(audit_fd);
+#else
+	pam_syslog(pamh, LOG_NOTICE, "pam: default-context=%s selected-context=%s success %d", default_context, selected_context, success);
+#endif
+	return rc;
+}
 static int
 send_text (pam_handle_t *pamh, const char *text, int debug)
 {
@@ -79,69 +134,64 @@
  * is responsible for freeing the responses.
  */
 static int
-query_response (pam_handle_t *pamh, const char *text,
+query_response (pam_handle_t *pamh, const char *text, const char *def,
 		char **responses, int debug)
 {
+  int rc;
+  if (def) 
+    rc = pam_prompt (pamh, PAM_PROMPT_ECHO_ON, responses, "%s [%s] ", text, def);
+  else
+    rc = pam_prompt (pamh, PAM_PROMPT_ECHO_ON, responses, "%s ", text);
   if (debug)
-    pam_syslog(pamh, LOG_NOTICE, "%s", text);
-
-  return pam_prompt (pamh, PAM_PROMPT_ECHO_ON, responses, "%s", text);
+    pam_syslog(pamh, LOG_NOTICE, "%s %s", text, responses[0]);
+  return rc;
 }
 
 static security_context_t
 manual_context (pam_handle_t *pamh, const char *user, int debug)
 {
-  security_context_t newcon;
+  security_context_t newcon=NULL;
   context_t new_context;
   int mls_enabled = is_selinux_mls_enabled();
-
-  char *responses;
+  char *type=NULL;
+  char *responses=NULL;
 
   while (1) {
     query_response(pamh,
-		   _("Would you like to enter a security context? [y] "),
+		   _("Would you like to enter a security context? [N] "), NULL, 
 		   &responses,debug);
-    if ((responses[0] == 'y') || (responses[0] == 'Y') ||
-	(responses[0] == '\0') )
+    if ((responses[0] == 'y') || (responses[0] == 'Y'))
       {
 	if (mls_enabled)
 	  new_context = context_new ("user:role:type:level");
 	else
 	  new_context = context_new ("user:role:type");
-	_pam_drop(responses);
 
-	/* Allow the user to enter each field of the context individually */
+	if (!new_context)
+              goto fail_set;
+
 	if (context_user_set (new_context, user))
-	  {
-	    context_free (new_context);
-	    return NULL;
-	  }
-	query_response(pamh,_("role: "),&responses,debug);
-	if (context_role_set (new_context, responses))
-	  {
-	    _pam_drop(responses);
-	    context_free (new_context);
-	    return NULL;
-	  }
+              goto fail_set;
+
 	_pam_drop(responses);
-	query_response(pamh,_("type: "),&responses,debug);
-	if (context_type_set (new_context, responses))
-	  {
-	    _pam_drop(responses);
-	    context_free (new_context);
-	    return NULL;
-	  }
+	/* Allow the user to enter each field of the context individually */
+	query_response(pamh,_("role:"), NULL, &responses,debug);
+	if (responses[0] != '\0') {
+	   if (context_role_set (new_context, responses)) 
+              goto fail_set;
+	   if (get_default_type(responses, &type)) 
+              goto fail_set;
+	   if (context_type_set (new_context, type)) 
+              goto fail_set;
+	}
 	_pam_drop(responses);
 	if (mls_enabled)
 	  {
-	    query_response(pamh,_("level: "),&responses,debug);
-	    if (context_range_set (new_context, responses))
-	      {
-		_pam_drop(responses);
-		context_free (new_context);
-		return NULL;
-	      }
-	    _pam_drop(responses);
+	    query_response(pamh,_("level:"), NULL, &responses,debug);
+	    if (responses[0] != '\0') {
+	      if (context_range_set (new_context, responses))
+		goto fail_set;
+	    }
 	  }
 	/* Get the string value of the context and see if it is valid. */
 	if (!security_check_context(context_str(new_context))) {
@@ -151,14 +201,125 @@
 	}
 	else
 	  send_text(pamh,_("Not a valid security context"),debug);
+	context_free (new_context);
       }
     else {
       _pam_drop(responses);
       return NULL;
     }
   } /* end while */
+ fail_set:
+  free(type);
+  _pam_drop(responses);
+  context_free (new_context);
+  return NULL;
+}
+
+static int mls_range_allowed(pam_handle_t *pamh, security_context_t src, security_context_t dst, int debug)
+{
+  struct av_decision avd;
+  int retval;
+  unsigned int bit = CONTEXT__CONTAINS;
+  context_t src_context = context_new (src);
+  context_t dst_context = context_new (dst);
+  context_range_set(dst_context, context_range_get(src_context));
+  if (debug)
+    pam_syslog(pamh, LOG_NOTICE, "Checking if %s mls range valid for  %s", dst, context_str(dst_context));
+
+  retval = security_compute_av(context_str(dst_context), dst, SECCLASS_CONTEXT, bit, &avd);
+  context_free(src_context);
+  context_free(dst_context);
+  if (retval || ((bit & avd.allowed) != bit))
+    return 0;
+  
+  return 1;
+}
+
+static security_context_t
+config_context (pam_handle_t *pamh, security_context_t puser_context, int debug)
+{
+  security_context_t newcon=NULL;
+  context_t new_context;
+  int mls_enabled = is_selinux_mls_enabled();
+  char *responses=NULL;
+  char *type=NULL;
+  char resp_val = 0;
+
+  pam_prompt (pamh, PAM_TEXT_INFO, NULL, _("Default Security Context %s\n"), puser_context);
+
+  while (1) {
+    query_response(pamh,
+		   _("Would you like to enter a different role or level?"), "n", 
+		   &responses,debug);
+
+    resp_val = responses[0];
+    _pam_drop(responses);
+    if ((resp_val == 'y') || (resp_val == 'Y'))
+      {
+        new_context = context_new(puser_context);
+        
+	/* Allow the user to enter role and level individually */
+	query_response(pamh,_("role:"), context_role_get(new_context), 
+		       &responses, debug);
+	if (responses[0]) {
+	  if (get_default_type(responses, &type)) {
+	    pam_prompt (pamh, PAM_ERROR_MSG, NULL, _("No default type for role %s\n"), responses);
+	    _pam_drop(responses);
+	    continue;
+	  } else {
+	    if (context_role_set(new_context, responses)) 
+	      goto fail_set;
+	    if (context_type_set (new_context, type))
+	      goto fail_set;
+	  } 
+	}
+	_pam_drop(responses);
+	if (mls_enabled)
+	  {
+	    query_response(pamh,_("level:"), context_range_get(new_context), 
+			   &responses, debug);
+	    if (responses[0]) {
+	      if (context_range_set(new_context, responses))
+		goto fail_set;
+	    } 
+	    _pam_drop(responses);
+	  }
+	if (debug)
+	  pam_syslog(pamh, LOG_NOTICE, "Selected Security Context %s", context_str(new_context));
+
+        /* Get the string value of the context and see if it is valid. */
+        if (!security_check_context(context_str(new_context))) {
+	  newcon = strdup(context_str(new_context));
+	  context_free (new_context);
+
+          /* we have to check that this user is allowed to go into the
+             range they have specified ... role is tied to an seuser, so that'll
+             be checked at setexeccon time */
+          if (mls_enabled && !mls_range_allowed(pamh, puser_context, newcon, debug)) {
+	    pam_syslog(pamh, LOG_NOTICE, "Security context %s is not allowed for %s", puser_context, newcon);
+
+
+            goto fail_range;
+	  }
+	  return newcon;
+	}
+	else
+	  send_text(pamh,_("Not a valid security context"),debug);
+
+        context_free(new_context); /* next time around allocates another */
+      }
+    else
+      return strdup(puser_context);
+  } /* end while */
 
   return NULL;
+
+ fail_set:
+  free(type);
+  _pam_drop(responses);
+  context_free (new_context);
+ fail_range:
+  return NULL;  
 }
 
 static void
@@ -273,13 +434,15 @@
 {
   int i, debug = 0, ttys=1, has_tty=isatty(0);
   int verbose=0, close_session=0;
+  int select_context = 0;
   int ret = 0;
   security_context_t* contextlist = NULL;
   int num_contexts = 0;
-  const void *username = NULL;
+  const char *username = NULL;
   const void *tty = NULL;
   char *seuser=NULL;
   char *level=NULL;
+  security_context_t default_user_context=NULL;
 
   /* Parse arguments. */
   for (i = 0; i < argc; i++) {
@@ -295,6 +458,9 @@
     if (strcmp(argv[i], "close") == 0) {
       close_session = 1;
     }
+    if (strcmp(argv[i], "select_context") == 0) {
+      select_context = 1;
+    }
   }
 
   if (debug)
@@ -307,7 +473,7 @@
   if (!(selinux_enabled = is_selinux_enabled()>0) )
       return PAM_SUCCESS;
 
-  if (pam_get_item(pamh, PAM_USER, &username) != PAM_SUCCESS ||
+  if (pam_get_item(pamh, PAM_USER, (void *) &username) != PAM_SUCCESS ||
                    username == NULL) {
     return PAM_USER_UNKNOWN;
   }
@@ -319,19 +485,39 @@
 							     &contextlist);
 	  if (debug)
 		  pam_syslog(pamh, LOG_DEBUG, "Username= %s SELinux User = %s Level= %s",
-			 (const char *)username, seuser, level);
+                             username, seuser, level);
 	  free(seuser);
 	  free(level);
   }
   if (num_contexts > 0) {
-    user_context = (security_context_t) strdup(contextlist[0]);
+    default_user_context=strdup(contextlist[0]);
     freeconary(contextlist);
-  } else {
+    if (default_user_context == NULL) {
+	  pam_syslog(pamh, LOG_ERR, _("Out of memory"));
+          return PAM_AUTH_ERR;
+    }
+    user_context = default_user_context;
+    if (select_context && has_tty) {
+      user_context = config_context(pamh, default_user_context, debug);
+      if (user_context == NULL) {
+        send_audit_message(pamh, 0, default_user_context, default_user_context);
+	freecon(default_user_context);
+	pam_syslog(pamh, LOG_ERR, _("Unable to get valid context for %s"),
+		    username);
+	pam_prompt (pamh, PAM_ERROR_MSG, NULL,  _("Unable to get valid context for %s"), username);
+        if (security_getenforce() == 1)
+          return PAM_AUTH_ERR;
+        else
+          return PAM_SUCCESS;
+      }
+    } 
+  }
+  else { 
     if (has_tty) {
-      user_context = manual_context(pamh,username,debug);
+      user_context = manual_context(pamh,seuser,debug);
       if (user_context == NULL) {
 	pam_syslog (pamh, LOG_ERR, "Unable to get valid context for %s",
-		    (const char *)username);
+		    username);
         if (security_getenforce() == 1)
           return PAM_AUTH_ERR;
         else
@@ -340,7 +526,7 @@
     } else {
         pam_syslog (pamh, LOG_ERR,
 		    "Unable to get valid context for %s, No valid tty",
-		    (const char *)username);
+		    username);
         if (security_getenforce() == 1)
           return PAM_AUTH_ERR;
         else
@@ -371,6 +557,10 @@
     ttyn=strdup(tty);
     ttyn_context=security_label_tty(pamh,ttyn,user_context);
   }
+  send_audit_message(pamh, 1, default_user_context, user_context);
+  if (default_user_context != user_context) {
+    freecon(default_user_context);
+  }
   ret = setexeccon(user_context);
   if (ret==0 && verbose) {
     char msg[PATH_MAX];
@@ -381,7 +571,7 @@
   if (ret) {
     pam_syslog(pamh, LOG_ERR,
 	       "Error!  Unable to set %s executable context %s.",
-	       (const char *)username, user_context);
+	       username, user_context);
     if (security_getenforce() == 1) {
        freecon(user_context);
        return PAM_AUTH_ERR;
@@ -389,7 +579,7 @@
   } else {
     if (debug)
       pam_syslog(pamh, LOG_NOTICE, "set %s security context to %s",
-		 (const char *)username, user_context);
+		 username, user_context);
   }
 #ifdef HAVE_SETKEYCREATECON
   ret = setkeycreatecon(user_context);
@@ -402,7 +592,7 @@
   if (ret) {
     pam_syslog(pamh, LOG_ERR,
 	       "Error!  Unable to set %s key creation context %s.",
-	       (const char *)username, user_context);
+	       username, user_context);
     if (security_getenforce() == 1) {
        freecon(user_context);
        return PAM_AUTH_ERR;
@@ -410,7 +600,7 @@
   } else {
     if (debug)
       pam_syslog(pamh, LOG_NOTICE, "set %s key creation context to %s",
-		 (const char *)username, user_context);
+		 username, user_context);
   }
 #endif
   freecon(user_context);