18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * mmap based event notifications for SELinux
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: KaiGai Kohei <kaigai@ak.jp.nec.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2010 NEC corporation
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/gfp.h>
118c2ecf20Sopenharmony_ci#include <linux/mm.h>
128c2ecf20Sopenharmony_ci#include <linux/mutex.h>
138c2ecf20Sopenharmony_ci#include "avc.h"
148c2ecf20Sopenharmony_ci#include "security.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/*
178c2ecf20Sopenharmony_ci * The selinux_status_page shall be exposed to userspace applications
188c2ecf20Sopenharmony_ci * using mmap interface on /selinux/status.
198c2ecf20Sopenharmony_ci * It enables to notify applications a few events that will cause reset
208c2ecf20Sopenharmony_ci * of userspace access vector without context switching.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * The selinux_kernel_status structure on the head of status page is
238c2ecf20Sopenharmony_ci * protected from concurrent accesses using seqlock logic, so userspace
248c2ecf20Sopenharmony_ci * application should reference the status page according to the seqlock
258c2ecf20Sopenharmony_ci * logic.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * Typically, application checks status->sequence at the head of access
288c2ecf20Sopenharmony_ci * control routine. If it is odd-number, kernel is updating the status,
298c2ecf20Sopenharmony_ci * so please wait for a moment. If it is changed from the last sequence
308c2ecf20Sopenharmony_ci * number, it means something happen, so application will reset userspace
318c2ecf20Sopenharmony_ci * avc, if needed.
328c2ecf20Sopenharmony_ci * In most cases, application shall confirm the kernel status is not
338c2ecf20Sopenharmony_ci * changed without any system call invocations.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/*
378c2ecf20Sopenharmony_ci * selinux_kernel_status_page
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * It returns a reference to selinux_status_page. If the status page is
408c2ecf20Sopenharmony_ci * not allocated yet, it also tries to allocate it at the first time.
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_cistruct page *selinux_kernel_status_page(struct selinux_state *state)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct selinux_kernel_status   *status;
458c2ecf20Sopenharmony_ci	struct page		       *result = NULL;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	mutex_lock(&state->status_lock);
488c2ecf20Sopenharmony_ci	if (!state->status_page) {
498c2ecf20Sopenharmony_ci		state->status_page = alloc_page(GFP_KERNEL|__GFP_ZERO);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci		if (state->status_page) {
528c2ecf20Sopenharmony_ci			status = page_address(state->status_page);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci			status->version = SELINUX_KERNEL_STATUS_VERSION;
558c2ecf20Sopenharmony_ci			status->sequence = 0;
568c2ecf20Sopenharmony_ci			status->enforcing = enforcing_enabled(state);
578c2ecf20Sopenharmony_ci			/*
588c2ecf20Sopenharmony_ci			 * NOTE: the next policyload event shall set
598c2ecf20Sopenharmony_ci			 * a positive value on the status->policyload,
608c2ecf20Sopenharmony_ci			 * although it may not be 1, but never zero.
618c2ecf20Sopenharmony_ci			 * So, application can know it was updated.
628c2ecf20Sopenharmony_ci			 */
638c2ecf20Sopenharmony_ci			status->policyload = 0;
648c2ecf20Sopenharmony_ci			status->deny_unknown =
658c2ecf20Sopenharmony_ci				!security_get_allow_unknown(state);
668c2ecf20Sopenharmony_ci		}
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci	result = state->status_page;
698c2ecf20Sopenharmony_ci	mutex_unlock(&state->status_lock);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return result;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/*
758c2ecf20Sopenharmony_ci * selinux_status_update_setenforce
768c2ecf20Sopenharmony_ci *
778c2ecf20Sopenharmony_ci * It updates status of the current enforcing/permissive mode.
788c2ecf20Sopenharmony_ci */
798c2ecf20Sopenharmony_civoid selinux_status_update_setenforce(struct selinux_state *state,
808c2ecf20Sopenharmony_ci				      int enforcing)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct selinux_kernel_status   *status;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	mutex_lock(&state->status_lock);
858c2ecf20Sopenharmony_ci	if (state->status_page) {
868c2ecf20Sopenharmony_ci		status = page_address(state->status_page);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci		status->sequence++;
898c2ecf20Sopenharmony_ci		smp_wmb();
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci		status->enforcing = enforcing;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci		smp_wmb();
948c2ecf20Sopenharmony_ci		status->sequence++;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci	mutex_unlock(&state->status_lock);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/*
1008c2ecf20Sopenharmony_ci * selinux_status_update_policyload
1018c2ecf20Sopenharmony_ci *
1028c2ecf20Sopenharmony_ci * It updates status of the times of policy reloaded, and current
1038c2ecf20Sopenharmony_ci * setting of deny_unknown.
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_civoid selinux_status_update_policyload(struct selinux_state *state,
1068c2ecf20Sopenharmony_ci				      int seqno)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct selinux_kernel_status   *status;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	mutex_lock(&state->status_lock);
1118c2ecf20Sopenharmony_ci	if (state->status_page) {
1128c2ecf20Sopenharmony_ci		status = page_address(state->status_page);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		status->sequence++;
1158c2ecf20Sopenharmony_ci		smp_wmb();
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		status->policyload = seqno;
1188c2ecf20Sopenharmony_ci		status->deny_unknown = !security_get_allow_unknown(state);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci		smp_wmb();
1218c2ecf20Sopenharmony_ci		status->sequence++;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci	mutex_unlock(&state->status_lock);
1248c2ecf20Sopenharmony_ci}
125