xref: /openbmc/linux/security/selinux/status.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
14b36cb77SOndrej Mosnacek // SPDX-License-Identifier: GPL-2.0-only
24b36cb77SOndrej Mosnacek /*
34b36cb77SOndrej Mosnacek  * mmap based event notifications for SELinux
44b36cb77SOndrej Mosnacek  *
54b36cb77SOndrej Mosnacek  * Author: KaiGai Kohei <kaigai@ak.jp.nec.com>
64b36cb77SOndrej Mosnacek  *
74b36cb77SOndrej Mosnacek  * Copyright (C) 2010 NEC corporation
84b36cb77SOndrej Mosnacek  */
94b36cb77SOndrej Mosnacek #include <linux/kernel.h>
104b36cb77SOndrej Mosnacek #include <linux/gfp.h>
114b36cb77SOndrej Mosnacek #include <linux/mm.h>
124b36cb77SOndrej Mosnacek #include <linux/mutex.h>
134b36cb77SOndrej Mosnacek #include "avc.h"
144b36cb77SOndrej Mosnacek #include "security.h"
154b36cb77SOndrej Mosnacek 
164b36cb77SOndrej Mosnacek /*
174b36cb77SOndrej Mosnacek  * The selinux_status_page shall be exposed to userspace applications
184b36cb77SOndrej Mosnacek  * using mmap interface on /selinux/status.
194b36cb77SOndrej Mosnacek  * It enables to notify applications a few events that will cause reset
204b36cb77SOndrej Mosnacek  * of userspace access vector without context switching.
214b36cb77SOndrej Mosnacek  *
224b36cb77SOndrej Mosnacek  * The selinux_kernel_status structure on the head of status page is
234b36cb77SOndrej Mosnacek  * protected from concurrent accesses using seqlock logic, so userspace
244b36cb77SOndrej Mosnacek  * application should reference the status page according to the seqlock
254b36cb77SOndrej Mosnacek  * logic.
264b36cb77SOndrej Mosnacek  *
274b36cb77SOndrej Mosnacek  * Typically, application checks status->sequence at the head of access
284b36cb77SOndrej Mosnacek  * control routine. If it is odd-number, kernel is updating the status,
294b36cb77SOndrej Mosnacek  * so please wait for a moment. If it is changed from the last sequence
304b36cb77SOndrej Mosnacek  * number, it means something happen, so application will reset userspace
314b36cb77SOndrej Mosnacek  * avc, if needed.
324b36cb77SOndrej Mosnacek  * In most cases, application shall confirm the kernel status is not
334b36cb77SOndrej Mosnacek  * changed without any system call invocations.
344b36cb77SOndrej Mosnacek  */
354b36cb77SOndrej Mosnacek 
364b36cb77SOndrej Mosnacek /*
374b36cb77SOndrej Mosnacek  * selinux_kernel_status_page
384b36cb77SOndrej Mosnacek  *
394b36cb77SOndrej Mosnacek  * It returns a reference to selinux_status_page. If the status page is
404b36cb77SOndrej Mosnacek  * not allocated yet, it also tries to allocate it at the first time.
414b36cb77SOndrej Mosnacek  */
selinux_kernel_status_page(void)42e67b7985SStephen Smalley struct page *selinux_kernel_status_page(void)
434b36cb77SOndrej Mosnacek {
444b36cb77SOndrej Mosnacek 	struct selinux_kernel_status   *status;
454b36cb77SOndrej Mosnacek 	struct page		       *result = NULL;
464b36cb77SOndrej Mosnacek 
47e67b7985SStephen Smalley 	mutex_lock(&selinux_state.status_lock);
48e67b7985SStephen Smalley 	if (!selinux_state.status_page) {
49e67b7985SStephen Smalley 		selinux_state.status_page = alloc_page(GFP_KERNEL|__GFP_ZERO);
504b36cb77SOndrej Mosnacek 
51e67b7985SStephen Smalley 		if (selinux_state.status_page) {
52e67b7985SStephen Smalley 			status = page_address(selinux_state.status_page);
534b36cb77SOndrej Mosnacek 
544b36cb77SOndrej Mosnacek 			status->version = SELINUX_KERNEL_STATUS_VERSION;
554b36cb77SOndrej Mosnacek 			status->sequence = 0;
56e67b7985SStephen Smalley 			status->enforcing = enforcing_enabled();
574b36cb77SOndrej Mosnacek 			/*
584b36cb77SOndrej Mosnacek 			 * NOTE: the next policyload event shall set
594b36cb77SOndrej Mosnacek 			 * a positive value on the status->policyload,
604b36cb77SOndrej Mosnacek 			 * although it may not be 1, but never zero.
614b36cb77SOndrej Mosnacek 			 * So, application can know it was updated.
624b36cb77SOndrej Mosnacek 			 */
634b36cb77SOndrej Mosnacek 			status->policyload = 0;
644b36cb77SOndrej Mosnacek 			status->deny_unknown =
65e67b7985SStephen Smalley 				!security_get_allow_unknown();
664b36cb77SOndrej Mosnacek 		}
674b36cb77SOndrej Mosnacek 	}
68e67b7985SStephen Smalley 	result = selinux_state.status_page;
69e67b7985SStephen Smalley 	mutex_unlock(&selinux_state.status_lock);
704b36cb77SOndrej Mosnacek 
714b36cb77SOndrej Mosnacek 	return result;
724b36cb77SOndrej Mosnacek }
734b36cb77SOndrej Mosnacek 
744b36cb77SOndrej Mosnacek /*
754b36cb77SOndrej Mosnacek  * selinux_status_update_setenforce
764b36cb77SOndrej Mosnacek  *
774b36cb77SOndrej Mosnacek  * It updates status of the current enforcing/permissive mode.
784b36cb77SOndrej Mosnacek  */
selinux_status_update_setenforce(bool enforcing)79*c867248cSChristian Göttsche void selinux_status_update_setenforce(bool enforcing)
804b36cb77SOndrej Mosnacek {
814b36cb77SOndrej Mosnacek 	struct selinux_kernel_status   *status;
824b36cb77SOndrej Mosnacek 
83e67b7985SStephen Smalley 	mutex_lock(&selinux_state.status_lock);
84e67b7985SStephen Smalley 	if (selinux_state.status_page) {
85e67b7985SStephen Smalley 		status = page_address(selinux_state.status_page);
864b36cb77SOndrej Mosnacek 
874b36cb77SOndrej Mosnacek 		status->sequence++;
884b36cb77SOndrej Mosnacek 		smp_wmb();
894b36cb77SOndrej Mosnacek 
90*c867248cSChristian Göttsche 		status->enforcing = enforcing ? 1 : 0;
914b36cb77SOndrej Mosnacek 
924b36cb77SOndrej Mosnacek 		smp_wmb();
934b36cb77SOndrej Mosnacek 		status->sequence++;
944b36cb77SOndrej Mosnacek 	}
95e67b7985SStephen Smalley 	mutex_unlock(&selinux_state.status_lock);
964b36cb77SOndrej Mosnacek }
974b36cb77SOndrej Mosnacek 
984b36cb77SOndrej Mosnacek /*
994b36cb77SOndrej Mosnacek  * selinux_status_update_policyload
1004b36cb77SOndrej Mosnacek  *
1014b36cb77SOndrej Mosnacek  * It updates status of the times of policy reloaded, and current
1024b36cb77SOndrej Mosnacek  * setting of deny_unknown.
1034b36cb77SOndrej Mosnacek  */
selinux_status_update_policyload(u32 seqno)1041f270f1cSChristian Göttsche void selinux_status_update_policyload(u32 seqno)
1054b36cb77SOndrej Mosnacek {
1064b36cb77SOndrej Mosnacek 	struct selinux_kernel_status   *status;
1074b36cb77SOndrej Mosnacek 
108e67b7985SStephen Smalley 	mutex_lock(&selinux_state.status_lock);
109e67b7985SStephen Smalley 	if (selinux_state.status_page) {
110e67b7985SStephen Smalley 		status = page_address(selinux_state.status_page);
1114b36cb77SOndrej Mosnacek 
1124b36cb77SOndrej Mosnacek 		status->sequence++;
1134b36cb77SOndrej Mosnacek 		smp_wmb();
1144b36cb77SOndrej Mosnacek 
1154b36cb77SOndrej Mosnacek 		status->policyload = seqno;
116e67b7985SStephen Smalley 		status->deny_unknown = !security_get_allow_unknown();
1174b36cb77SOndrej Mosnacek 
1184b36cb77SOndrej Mosnacek 		smp_wmb();
1194b36cb77SOndrej Mosnacek 		status->sequence++;
1204b36cb77SOndrej Mosnacek 	}
121e67b7985SStephen Smalley 	mutex_unlock(&selinux_state.status_lock);
1224b36cb77SOndrej Mosnacek }
123