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