162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * mmap based event notifications for SELinux 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: KaiGai Kohei <kaigai@ak.jp.nec.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2010 NEC corporation 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/gfp.h> 1162306a36Sopenharmony_ci#include <linux/mm.h> 1262306a36Sopenharmony_ci#include <linux/mutex.h> 1362306a36Sopenharmony_ci#include "avc.h" 1462306a36Sopenharmony_ci#include "security.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * The selinux_status_page shall be exposed to userspace applications 1862306a36Sopenharmony_ci * using mmap interface on /selinux/status. 1962306a36Sopenharmony_ci * It enables to notify applications a few events that will cause reset 2062306a36Sopenharmony_ci * of userspace access vector without context switching. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * The selinux_kernel_status structure on the head of status page is 2362306a36Sopenharmony_ci * protected from concurrent accesses using seqlock logic, so userspace 2462306a36Sopenharmony_ci * application should reference the status page according to the seqlock 2562306a36Sopenharmony_ci * logic. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Typically, application checks status->sequence at the head of access 2862306a36Sopenharmony_ci * control routine. If it is odd-number, kernel is updating the status, 2962306a36Sopenharmony_ci * so please wait for a moment. If it is changed from the last sequence 3062306a36Sopenharmony_ci * number, it means something happen, so application will reset userspace 3162306a36Sopenharmony_ci * avc, if needed. 3262306a36Sopenharmony_ci * In most cases, application shall confirm the kernel status is not 3362306a36Sopenharmony_ci * changed without any system call invocations. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * selinux_kernel_status_page 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * It returns a reference to selinux_status_page. If the status page is 4062306a36Sopenharmony_ci * not allocated yet, it also tries to allocate it at the first time. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_cistruct page *selinux_kernel_status_page(void) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct selinux_kernel_status *status; 4562306a36Sopenharmony_ci struct page *result = NULL; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci mutex_lock(&selinux_state.status_lock); 4862306a36Sopenharmony_ci if (!selinux_state.status_page) { 4962306a36Sopenharmony_ci selinux_state.status_page = alloc_page(GFP_KERNEL|__GFP_ZERO); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (selinux_state.status_page) { 5262306a36Sopenharmony_ci status = page_address(selinux_state.status_page); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci status->version = SELINUX_KERNEL_STATUS_VERSION; 5562306a36Sopenharmony_ci status->sequence = 0; 5662306a36Sopenharmony_ci status->enforcing = enforcing_enabled(); 5762306a36Sopenharmony_ci /* 5862306a36Sopenharmony_ci * NOTE: the next policyload event shall set 5962306a36Sopenharmony_ci * a positive value on the status->policyload, 6062306a36Sopenharmony_ci * although it may not be 1, but never zero. 6162306a36Sopenharmony_ci * So, application can know it was updated. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci status->policyload = 0; 6462306a36Sopenharmony_ci status->deny_unknown = 6562306a36Sopenharmony_ci !security_get_allow_unknown(); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci result = selinux_state.status_page; 6962306a36Sopenharmony_ci mutex_unlock(&selinux_state.status_lock); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return result; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * selinux_status_update_setenforce 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * It updates status of the current enforcing/permissive mode. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_civoid selinux_status_update_setenforce(bool enforcing) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct selinux_kernel_status *status; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci mutex_lock(&selinux_state.status_lock); 8462306a36Sopenharmony_ci if (selinux_state.status_page) { 8562306a36Sopenharmony_ci status = page_address(selinux_state.status_page); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci status->sequence++; 8862306a36Sopenharmony_ci smp_wmb(); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci status->enforcing = enforcing ? 1 : 0; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci smp_wmb(); 9362306a36Sopenharmony_ci status->sequence++; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci mutex_unlock(&selinux_state.status_lock); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* 9962306a36Sopenharmony_ci * selinux_status_update_policyload 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * It updates status of the times of policy reloaded, and current 10262306a36Sopenharmony_ci * setting of deny_unknown. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_civoid selinux_status_update_policyload(u32 seqno) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct selinux_kernel_status *status; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci mutex_lock(&selinux_state.status_lock); 10962306a36Sopenharmony_ci if (selinux_state.status_page) { 11062306a36Sopenharmony_ci status = page_address(selinux_state.status_page); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci status->sequence++; 11362306a36Sopenharmony_ci smp_wmb(); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci status->policyload = seqno; 11662306a36Sopenharmony_ci status->deny_unknown = !security_get_allow_unknown(); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci smp_wmb(); 11962306a36Sopenharmony_ci status->sequence++; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci mutex_unlock(&selinux_state.status_lock); 12262306a36Sopenharmony_ci} 123