1 /******************************************************************************
2 * A module for Linux-PAM that will set the default security context after login
5 * Copyright (c) 2003-2008 Red Hat, Inc.
6 * Written by Dan Walsh <dwalsh@redhat.com>
7 * Additional improvements by Tomas Mraz <tmraz@redhat.com>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, and the entire permission notice in its entirety,
14 * including the disclaimer of warranties.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. The name of the author may not be used to endorse or promote
19 * products derived from this software without specific prior
22 * ALTERNATIVELY, this product may be distributed under the terms of
23 * the GNU Public License, in which case the provisions of the GPL are
24 * required INSTEAD OF the above restrictions. (This clause is
25 * necessary due to a potential bad interaction between the GPL and
26 * the restrictions contained in a BSD-style copyright.)
28 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
29 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
30 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
31 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
32 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
33 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
34 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
38 * OF THE POSSIBILITY OF SUCH DAMAGE.
51 #include <sys/types.h>
57 #define PAM_SM_SESSION
59 #include <security/pam_modules.h>
60 #include <security/_pam_macros.h>
61 #include <security/pam_modutil.h>
62 #include <security/pam_ext.h>
64 #include <selinux/selinux.h>
65 #include <selinux/get_context_list.h>
66 #include <selinux/flask.h>
67 #include <selinux/av_permissions.h>
68 #include <selinux/selinux.h>
69 #include <selinux/context.h>
70 #include <selinux/get_default_type.h>
74 #include <sys/select.h>
78 /* Send audit message */
81 int send_audit_message(pam_handle_t *pamh, int success, security_context_t default_context,
82 security_context_t selected_context)
87 int audit_fd = audit_open();
88 security_context_t default_raw=NULL;
89 security_context_t selected_raw=NULL;
92 if (errno == EINVAL || errno == EPROTONOSUPPORT ||
93 errno == EAFNOSUPPORT)
94 return 0; /* No audit support in kernel */
95 pam_syslog(pamh, LOG_ERR, "Error connecting to audit system.");
98 if (selinux_trans_to_raw_context(default_context, &default_raw) < 0) {
99 pam_syslog(pamh, LOG_ERR, "Error translating default context.");
102 if (selinux_trans_to_raw_context(selected_context, &selected_raw) < 0) {
103 pam_syslog(pamh, LOG_ERR, "Error translating selected context.");
106 if (asprintf(&msg, "pam: default-context=%s selected-context=%s",
107 default_raw ? default_raw : (default_context ? default_context : "?"),
108 selected_raw ? selected_raw : (selected_context ? selected_context : "?")) < 0) {
109 pam_syslog(pamh, LOG_ERR, "Error allocating memory.");
112 if (audit_log_user_message(audit_fd, AUDIT_USER_ROLE_CHANGE,
113 msg, NULL, NULL, NULL, success) <= 0) {
114 pam_syslog(pamh, LOG_ERR, "Error sending audit message.");
120 freecon(default_raw);
121 freecon(selected_raw);
124 pam_syslog(pamh, LOG_NOTICE, "pam: default-context=%s selected-context=%s success %d", default_context, selected_context, success);
129 send_text (pam_handle_t *pamh, const char *text, int debug)
132 pam_syslog(pamh, LOG_NOTICE, "%s", text);
133 return pam_info (pamh, "%s", text);
137 * This function sends a message to the user and gets the response. The caller
138 * is responsible for freeing the responses.
141 query_response (pam_handle_t *pamh, const char *text, const char *def,
142 char **response, int debug)
146 rc = pam_prompt (pamh, PAM_PROMPT_ECHO_ON, response, "%s [%s] ", text, def);
148 rc = pam_prompt (pamh, PAM_PROMPT_ECHO_ON, response, "%s ", text);
150 if (*response == NULL) {
154 if (rc != PAM_SUCCESS) {
155 pam_syslog(pamh, LOG_WARNING, "No response to query: %s", text);
157 pam_syslog(pamh, LOG_NOTICE, "%s %s", text, *response);
161 static security_context_t
162 manual_context (pam_handle_t *pamh, const char *user, int debug)
164 security_context_t newcon=NULL;
165 context_t new_context;
166 int mls_enabled = is_selinux_mls_enabled();
171 if (query_response(pamh,
172 _("Would you like to enter a security context? [N] "), NULL,
173 &response, debug) != PAM_SUCCESS)
176 if ((response[0] == 'y') || (response[0] == 'Y'))
179 new_context = context_new ("user:role:type:level");
181 new_context = context_new ("user:role:type");
186 if (context_user_set (new_context, user))
190 /* Allow the user to enter each field of the context individually */
191 if (query_response(pamh, _("role:"), NULL, &response, debug) == PAM_SUCCESS &&
192 response[0] != '\0') {
193 if (context_role_set (new_context, response))
195 if (get_default_type(response, &type))
197 if (context_type_set (new_context, type))
205 if (query_response(pamh, _("level:"), NULL, &response, debug) == PAM_SUCCESS &&
206 response[0] != '\0') {
207 if (context_range_set (new_context, response))
213 /* Get the string value of the context and see if it is valid. */
214 if (!security_check_context(context_str(new_context))) {
215 newcon = strdup(context_str(new_context));
216 context_free (new_context);
220 send_text(pamh,_("Not a valid security context"),debug);
222 context_free (new_context);
232 context_free (new_context);
236 static int mls_range_allowed(pam_handle_t *pamh, security_context_t src, security_context_t dst, int debug)
238 struct av_decision avd;
240 security_class_t class;
242 context_t src_context;
243 context_t dst_context;
245 class = string_to_security_class("context");
247 pam_syslog(pamh, LOG_ERR, "Failed to translate security class context. %m");
251 bit = string_to_av_perm(class, "contains");
253 pam_syslog(pamh, LOG_ERR, "Failed to translate av perm contains. %m");
257 src_context = context_new (src);
258 dst_context = context_new (dst);
259 context_range_set(dst_context, context_range_get(src_context));
261 pam_syslog(pamh, LOG_NOTICE, "Checking if %s mls range valid for %s", dst, context_str(dst_context));
263 retval = security_compute_av(context_str(dst_context), dst, class, bit, &avd);
264 context_free(src_context);
265 context_free(dst_context);
266 if (retval || ((bit & avd.allowed) != bit))
272 static security_context_t
273 config_context (pam_handle_t *pamh, security_context_t defaultcon, int use_current_range, int debug)
275 security_context_t newcon=NULL;
276 context_t new_context;
277 int mls_enabled = is_selinux_mls_enabled();
282 pam_prompt (pamh, PAM_TEXT_INFO, NULL, _("Default Security Context %s\n"), defaultcon);
285 if (query_response(pamh,
286 _("Would you like to enter a different role or level?"), "n",
287 &response, debug) == PAM_SUCCESS) {
288 resp_val = response[0];
293 if ((resp_val == 'y') || (resp_val == 'Y'))
295 if ((new_context = context_new(defaultcon)) == NULL)
298 /* Allow the user to enter role and level individually */
299 if (query_response(pamh, _("role:"), context_role_get(new_context),
300 &response, debug) == PAM_SUCCESS && response[0]) {
301 if (get_default_type(response, &type)) {
302 pam_prompt (pamh, PAM_ERROR_MSG, NULL, _("No default type for role %s\n"), response);
306 if (context_role_set(new_context, response))
308 if (context_type_set (new_context, type))
317 if (use_current_range) {
318 security_context_t mycon = NULL;
319 context_t my_context;
321 if (getcon(&mycon) != 0)
323 my_context = context_new(mycon);
324 if (my_context == NULL) {
329 if (context_range_set(new_context, context_range_get(my_context))) {
330 context_free(my_context);
333 context_free(my_context);
334 } else if (query_response(pamh, _("level:"), context_range_get(new_context),
335 &response, debug) == PAM_SUCCESS && response[0]) {
336 if (context_range_set(new_context, response))
343 pam_syslog(pamh, LOG_NOTICE, "Selected Security Context %s", context_str(new_context));
345 /* Get the string value of the context and see if it is valid. */
346 if (!security_check_context(context_str(new_context))) {
347 newcon = strdup(context_str(new_context));
350 context_free(new_context);
352 /* we have to check that this user is allowed to go into the
353 range they have specified ... role is tied to an seuser, so that'll
354 be checked at setexeccon time */
355 if (mls_enabled && !mls_range_allowed(pamh, defaultcon, newcon, debug)) {
356 pam_syslog(pamh, LOG_NOTICE, "Security context %s is not allowed for %s", defaultcon, newcon);
358 send_audit_message(pamh, 0, defaultcon, newcon);
366 send_audit_message(pamh, 0, defaultcon, context_str(new_context));
367 send_text(pamh,_("Not a valid security context"),debug);
369 context_free(new_context); /* next time around allocates another */
372 return strdup(defaultcon);
380 context_free (new_context);
381 send_audit_message(pamh, 0, defaultcon, NULL);
386 static security_context_t
387 context_from_env (pam_handle_t *pamh, security_context_t defaultcon, int env_params, int use_current_range, int debug)
389 security_context_t newcon = NULL;
390 context_t new_context;
391 context_t my_context = NULL;
392 int mls_enabled = is_selinux_mls_enabled();
393 const char *env = NULL;
397 if ((new_context = context_new(defaultcon)) == NULL)
400 if (env_params && (env = pam_getenv(pamh, "SELINUX_ROLE_REQUESTED")) != NULL && env[0] != '\0') {
402 pam_syslog(pamh, LOG_NOTICE, "Requested role: %s", env);
404 if (get_default_type(env, &type)) {
405 pam_syslog(pamh, LOG_NOTICE, "No default type for role %s", env);
408 if (context_role_set(new_context, env))
410 if (context_type_set(new_context, type))
416 if ((env = pam_getenv(pamh, "SELINUX_USE_CURRENT_RANGE")) != NULL && env[0] == '1') {
418 pam_syslog(pamh, LOG_NOTICE, "SELINUX_USE_CURRENT_RANGE is set");
419 use_current_range = 1;
422 if (use_current_range) {
423 security_context_t mycon = NULL;
425 if (getcon(&mycon) != 0)
427 my_context = context_new(mycon);
428 if (my_context == NULL) {
433 env = context_range_get(my_context);
435 env = pam_getenv(pamh, "SELINUX_LEVEL_REQUESTED");
438 if (env != NULL && env[0] != '\0') {
440 pam_syslog(pamh, LOG_NOTICE, "Requested level: %s", env);
441 if (context_range_set(new_context, env))
446 newcon = strdup(context_str(new_context));
451 pam_syslog(pamh, LOG_NOTICE, "Selected Security Context %s", newcon);
453 /* Get the string value of the context and see if it is valid. */
454 if (security_check_context(newcon)) {
455 pam_syslog(pamh, LOG_NOTICE, "Not a valid security context %s", newcon);
460 /* we have to check that this user is allowed to go into the
461 range they have specified ... role is tied to an seuser, so that'll
462 be checked at setexeccon time */
463 if (mls_enabled && !mls_range_allowed(pamh, defaultcon, newcon, debug)) {
464 pam_syslog(pamh, LOG_NOTICE, "Security context %s is not allowed for %s", defaultcon, newcon);
473 context_free(my_context);
474 context_free(new_context);
476 send_audit_message(pamh, 0, defaultcon, newcon);
484 security_restorelabel_tty(const pam_handle_t *pamh,
485 const char *tty, security_context_t context)
487 char ttybuf[PATH_MAX];
493 if(strncmp("/dev/", tty, 5)) {
494 snprintf(ttybuf,sizeof(ttybuf),"/dev/%s",tty);
500 if (setfilecon(ptr, context) && errno != ENOENT)
502 pam_syslog(pamh, LOG_NOTICE,
503 "Warning! Could not relabel %s with %s, not relabeling: %m",
508 static security_context_t
509 security_label_tty(pam_handle_t *pamh, char *tty,
510 security_context_t usercon)
512 char ttybuf[PATH_MAX];
514 security_context_t newdev_context=NULL; /* The new context of a device */
515 security_context_t prev_context=NULL; /* The new context of a device */
518 if(strncmp("/dev/", tty, 5))
520 snprintf(ttybuf,sizeof(ttybuf),"/dev/%s",tty);
526 if (getfilecon(ptr, &prev_context) < 0)
529 pam_syslog(pamh, LOG_NOTICE,
530 "Warning! Could not get current context for %s, not relabeling: %m",
534 if( security_compute_relabel(usercon,prev_context,SECCLASS_CHR_FILE,
537 pam_syslog(pamh, LOG_NOTICE,
538 "Warning! Could not get new context for %s, not relabeling: %m",
540 pam_syslog(pamh, LOG_NOTICE,
541 "usercon=%s, prev_context=%s", usercon, prev_context);
542 freecon(prev_context);
545 status=setfilecon(ptr,newdev_context);
548 pam_syslog(pamh, LOG_NOTICE,
549 "Warning! Could not relabel %s with %s, not relabeling: %m",
551 freecon(prev_context);
554 freecon(newdev_context);
558 static security_context_t user_context=NULL;
559 static security_context_t prev_user_context=NULL;
560 static security_context_t ttyn_context=NULL; /* The current context of ttyn device */
561 static int selinux_enabled=0;
562 static char *ttyn=NULL;
565 pam_sm_authenticate(pam_handle_t *pamh UNUSED, int flags UNUSED,
566 int argc UNUSED, const char **argv UNUSED)
568 /* Fail by default. */
573 pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
574 int argc UNUSED, const char **argv UNUSED)
580 pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
581 int argc, const char **argv)
583 int i, debug = 0, ttys=1;
584 int verbose=0, close_session=0;
585 int select_context = 0;
586 int use_current_range = 0;
588 security_context_t* contextlist = NULL;
589 int num_contexts = 0;
591 const char *username;
592 const void *void_username;
593 const void *tty = NULL;
596 security_context_t default_user_context=NULL;
597 #ifdef HAVE_GETSEUSER
598 const void *void_service;
602 /* Parse arguments. */
603 for (i = 0; i < argc; i++) {
604 if (strcmp(argv[i], "debug") == 0) {
607 if (strcmp(argv[i], "nottys") == 0) {
610 if (strcmp(argv[i], "verbose") == 0) {
613 if (strcmp(argv[i], "close") == 0) {
616 if (strcmp(argv[i], "select_context") == 0) {
619 if (strcmp(argv[i], "use_current_range") == 0) {
620 use_current_range = 1;
622 if (strcmp(argv[i], "env_params") == 0) {
628 pam_syslog(pamh, LOG_NOTICE, "Open Session");
630 if (select_context && env_params) {
631 pam_syslog(pamh, LOG_ERR, "select_context cannot be used with env_params");
635 /* this module is only supposed to execute close_session */
639 if (!(selinux_enabled = is_selinux_enabled()>0) )
642 if (pam_get_item(pamh, PAM_USER, &void_username) != PAM_SUCCESS ||
643 void_username == NULL) {
644 return PAM_USER_UNKNOWN;
646 username = void_username;
648 #ifdef HAVE_GETSEUSER
649 if (pam_get_item(pamh, PAM_SERVICE, (void *) &void_service) != PAM_SUCCESS ||
650 void_service == NULL) {
651 return PAM_SESSION_ERR;
653 service = void_service;
655 if (getseuser(username, service, &seuser, &level) == 0) {
657 if (getseuserbyname(username, &seuser, &level) == 0) {
659 num_contexts = get_ordered_context_list_with_level(seuser,
664 pam_syslog(pamh, LOG_DEBUG, "Username= %s SELinux User = %s Level= %s",
665 username, seuser, level);
668 if (num_contexts > 0) {
670 default_user_context=strdup(contextlist[0]);
671 freeconary(contextlist);
672 if (default_user_context == NULL) {
673 pam_syslog(pamh, LOG_ERR, "Out of memory");
677 user_context = default_user_context;
678 if (select_context) {
679 user_context = config_context(pamh, default_user_context, use_current_range, debug);
680 } else if (env_params || use_current_range) {
681 user_context = context_from_env(pamh, default_user_context, env_params, use_current_range, debug);
684 if (user_context == NULL) {
685 freecon(default_user_context);
686 pam_syslog(pamh, LOG_ERR, "Unable to get valid context for %s",
688 pam_prompt (pamh, PAM_ERROR_MSG, NULL, _("Unable to get valid context for %s"), username);
689 if (security_getenforce() == 1)
696 if (seuser != NULL) {
697 user_context = manual_context(pamh,seuser,debug);
700 if (user_context == NULL) {
701 pam_syslog (pamh, LOG_ERR, "Unable to get valid context for %s",
703 if (security_getenforce() == 1)
710 if (getexeccon(&prev_user_context)<0) {
711 prev_user_context=NULL;
714 /* Get the name of the terminal. */
715 if (pam_get_item(pamh, PAM_TTY, &tty) != PAM_SUCCESS) {
719 if ((tty == NULL) || (strlen(tty) == 0) ||
720 strcmp(tty, "ssh") == 0 || strncmp(tty, "NODEV", 5) == 0) {
721 tty = ttyname(STDIN_FILENO);
722 if ((tty == NULL) || (strlen(tty) == 0)) {
723 tty = ttyname(STDOUT_FILENO);
725 if ((tty == NULL) || (strlen(tty) == 0)) {
726 tty = ttyname(STDERR_FILENO);
732 ttyn_context=security_label_tty(pamh,ttyn,user_context);
734 send_audit_message(pamh, 1, default_user_context, user_context);
735 if (default_user_context != user_context) {
736 freecon(default_user_context);
738 ret = setexeccon(user_context);
739 if (ret==0 && verbose) {
741 snprintf(msg, sizeof(msg),
742 _("Security Context %s Assigned"), user_context);
743 send_text(pamh, msg, debug);
746 pam_syslog(pamh, LOG_ERR,
747 "Error! Unable to set %s executable context %s.",
748 username, user_context);
749 if (security_getenforce() == 1) {
750 freecon(user_context);
755 pam_syslog(pamh, LOG_NOTICE, "set %s security context to %s",
756 username, user_context);
758 #ifdef HAVE_SETKEYCREATECON
759 ret = setkeycreatecon(user_context);
760 if (ret==0 && verbose) {
762 snprintf(msg, sizeof(msg),
763 _("Key Creation Context %s Assigned"), user_context);
764 send_text(pamh, msg, debug);
767 pam_syslog(pamh, LOG_ERR,
768 "Error! Unable to set %s key creation context %s.",
769 username, user_context);
770 if (security_getenforce() == 1) {
771 freecon(user_context);
776 pam_syslog(pamh, LOG_NOTICE, "set %s key creation context to %s",
777 username, user_context);
780 freecon(user_context);
786 pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED,
787 int argc, const char **argv)
789 int i, debug = 0, status = PAM_SUCCESS, open_session = 0;
790 if (! (selinux_enabled ))
793 /* Parse arguments. */
794 for (i = 0; i < argc; i++) {
795 if (strcmp(argv[i], "debug") == 0) {
798 if (strcmp(argv[i], "open") == 0) {
804 pam_syslog(pamh, LOG_NOTICE, "Close Session");
811 pam_syslog(pamh, LOG_NOTICE, "Restore tty %s -> %s",
814 security_restorelabel_tty(pamh,ttyn,ttyn_context);
815 freecon(ttyn_context);
820 if (setexeccon(prev_user_context)) {
821 pam_syslog(pamh, LOG_ERR, "Unable to restore executable context %s.",
822 prev_user_context ? prev_user_context : "");
823 if (security_getenforce() == 1)
824 status = PAM_AUTH_ERR;
826 status = PAM_SUCCESS;
828 pam_syslog(pamh, LOG_NOTICE, "Executable context back to original");
830 if (prev_user_context) {
831 freecon(prev_user_context);
832 prev_user_context = NULL;