--- 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 @@ verbose + + select_context + @@ -118,6 +121,17 @@ + + + + + + + Attempt to ask the user for a custom security context role. + If MLS is on ask also for sensitivity level. + + + --- 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 #include #include +#include #include #include +#include +#ifdef HAVE_LIBAUDIT +#include +#include +#include +#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);