/*1* mmap based event notifications for SELinux2*3* Author: KaiGai Kohei <[email protected]>4*5* Copyright (C) 2010 NEC corporation6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License version 2,9* as published by the Free Software Foundation.10*/11#include <linux/kernel.h>12#include <linux/gfp.h>13#include <linux/mm.h>14#include <linux/mutex.h>15#include "avc.h"16#include "services.h"1718/*19* The selinux_status_page shall be exposed to userspace applications20* using mmap interface on /selinux/status.21* It enables to notify applications a few events that will cause reset22* of userspace access vector without context switching.23*24* The selinux_kernel_status structure on the head of status page is25* protected from concurrent accesses using seqlock logic, so userspace26* application should reference the status page according to the seqlock27* logic.28*29* Typically, application checks status->sequence at the head of access30* control routine. If it is odd-number, kernel is updating the status,31* so please wait for a moment. If it is changed from the last sequence32* number, it means something happen, so application will reset userspace33* avc, if needed.34* In most cases, application shall confirm the kernel status is not35* changed without any system call invocations.36*/37static struct page *selinux_status_page;38static DEFINE_MUTEX(selinux_status_lock);3940/*41* selinux_kernel_status_page42*43* It returns a reference to selinux_status_page. If the status page is44* not allocated yet, it also tries to allocate it at the first time.45*/46struct page *selinux_kernel_status_page(void)47{48struct selinux_kernel_status *status;49struct page *result = NULL;5051mutex_lock(&selinux_status_lock);52if (!selinux_status_page) {53selinux_status_page = alloc_page(GFP_KERNEL|__GFP_ZERO);5455if (selinux_status_page) {56status = page_address(selinux_status_page);5758status->version = SELINUX_KERNEL_STATUS_VERSION;59status->sequence = 0;60status->enforcing = selinux_enforcing;61/*62* NOTE: the next policyload event shall set63* a positive value on the status->policyload,64* although it may not be 1, but never zero.65* So, application can know it was updated.66*/67status->policyload = 0;68status->deny_unknown = !security_get_allow_unknown();69}70}71result = selinux_status_page;72mutex_unlock(&selinux_status_lock);7374return result;75}7677/*78* selinux_status_update_setenforce79*80* It updates status of the current enforcing/permissive mode.81*/82void selinux_status_update_setenforce(int enforcing)83{84struct selinux_kernel_status *status;8586mutex_lock(&selinux_status_lock);87if (selinux_status_page) {88status = page_address(selinux_status_page);8990status->sequence++;91smp_wmb();9293status->enforcing = enforcing;9495smp_wmb();96status->sequence++;97}98mutex_unlock(&selinux_status_lock);99}100101/*102* selinux_status_update_policyload103*104* It updates status of the times of policy reloaded, and current105* setting of deny_unknown.106*/107void selinux_status_update_policyload(int seqno)108{109struct selinux_kernel_status *status;110111mutex_lock(&selinux_status_lock);112if (selinux_status_page) {113status = page_address(selinux_status_page);114115status->sequence++;116smp_wmb();117118status->policyload = seqno;119status->deny_unknown = !security_get_allow_unknown();120121smp_wmb();122status->sequence++;123}124mutex_unlock(&selinux_status_lock);125}126127128