linux/security/lockdown/lockdown.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Lock down the kernel
   3 *
   4 * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
   5 * Written by David Howells (dhowells@redhat.com)
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public Licence
   9 * as published by the Free Software Foundation; either version
  10 * 2 of the Licence, or (at your option) any later version.
  11 */
  12
  13#include <linux/security.h>
  14#include <linux/export.h>
  15#include <linux/lsm_hooks.h>
  16
  17static enum lockdown_reason kernel_locked_down;
  18
  19static const enum lockdown_reason lockdown_levels[] = {LOCKDOWN_NONE,
  20                                                 LOCKDOWN_INTEGRITY_MAX,
  21                                                 LOCKDOWN_CONFIDENTIALITY_MAX};
  22
  23/*
  24 * Put the kernel into lock-down mode.
  25 */
  26static int lock_kernel_down(const char *where, enum lockdown_reason level)
  27{
  28        if (kernel_locked_down >= level)
  29                return -EPERM;
  30
  31        kernel_locked_down = level;
  32        pr_notice("Kernel is locked down from %s; see man kernel_lockdown.7\n",
  33                  where);
  34        return 0;
  35}
  36
  37static int __init lockdown_param(char *level)
  38{
  39        if (!level)
  40                return -EINVAL;
  41
  42        if (strcmp(level, "integrity") == 0)
  43                lock_kernel_down("command line", LOCKDOWN_INTEGRITY_MAX);
  44        else if (strcmp(level, "confidentiality") == 0)
  45                lock_kernel_down("command line", LOCKDOWN_CONFIDENTIALITY_MAX);
  46        else
  47                return -EINVAL;
  48
  49        return 0;
  50}
  51
  52early_param("lockdown", lockdown_param);
  53
  54/**
  55 * lockdown_is_locked_down - Find out if the kernel is locked down
  56 * @what: Tag to use in notice generated if lockdown is in effect
  57 */
  58static int lockdown_is_locked_down(enum lockdown_reason what)
  59{
  60        if (WARN(what >= LOCKDOWN_CONFIDENTIALITY_MAX,
  61                 "Invalid lockdown reason"))
  62                return -EPERM;
  63
  64        if (kernel_locked_down >= what) {
  65                if (lockdown_reasons[what])
  66                        pr_notice("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
  67                                  current->comm, lockdown_reasons[what]);
  68                return -EPERM;
  69        }
  70
  71        return 0;
  72}
  73
  74static struct security_hook_list lockdown_hooks[] __lsm_ro_after_init = {
  75        LSM_HOOK_INIT(locked_down, lockdown_is_locked_down),
  76};
  77
  78static int __init lockdown_lsm_init(void)
  79{
  80#if defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY)
  81        lock_kernel_down("Kernel configuration", LOCKDOWN_INTEGRITY_MAX);
  82#elif defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY)
  83        lock_kernel_down("Kernel configuration", LOCKDOWN_CONFIDENTIALITY_MAX);
  84#endif
  85        security_add_hooks(lockdown_hooks, ARRAY_SIZE(lockdown_hooks),
  86                           "lockdown");
  87        return 0;
  88}
  89
  90static ssize_t lockdown_read(struct file *filp, char __user *buf, size_t count,
  91                             loff_t *ppos)
  92{
  93        char temp[80];
  94        int i, offset = 0;
  95
  96        for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) {
  97                enum lockdown_reason level = lockdown_levels[i];
  98
  99                if (lockdown_reasons[level]) {
 100                        const char *label = lockdown_reasons[level];
 101
 102                        if (kernel_locked_down == level)
 103                                offset += sprintf(temp+offset, "[%s] ", label);
 104                        else
 105                                offset += sprintf(temp+offset, "%s ", label);
 106                }
 107        }
 108
 109        /* Convert the last space to a newline if needed. */
 110        if (offset > 0)
 111                temp[offset-1] = '\n';
 112
 113        return simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
 114}
 115
 116static ssize_t lockdown_write(struct file *file, const char __user *buf,
 117                              size_t n, loff_t *ppos)
 118{
 119        char *state;
 120        int i, len, err = -EINVAL;
 121
 122        state = memdup_user_nul(buf, n);
 123        if (IS_ERR(state))
 124                return PTR_ERR(state);
 125
 126        len = strlen(state);
 127        if (len && state[len-1] == '\n') {
 128                state[len-1] = '\0';
 129                len--;
 130        }
 131
 132        for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) {
 133                enum lockdown_reason level = lockdown_levels[i];
 134                const char *label = lockdown_reasons[level];
 135
 136                if (label && !strcmp(state, label))
 137                        err = lock_kernel_down("securityfs", level);
 138        }
 139
 140        kfree(state);
 141        return err ? err : n;
 142}
 143
 144static const struct file_operations lockdown_ops = {
 145        .read  = lockdown_read,
 146        .write = lockdown_write,
 147};
 148
 149static int __init lockdown_secfs_init(void)
 150{
 151        struct dentry *dentry;
 152
 153        dentry = securityfs_create_file("lockdown", 0644, NULL, NULL,
 154                                        &lockdown_ops);
 155        return PTR_ERR_OR_ZERO(dentry);
 156}
 157
 158core_initcall(lockdown_secfs_init);
 159
 160#ifdef CONFIG_SECURITY_LOCKDOWN_LSM_EARLY
 161DEFINE_EARLY_LSM(lockdown) = {
 162#else
 163DEFINE_LSM(lockdown) = {
 164#endif
 165        .name = "lockdown",
 166        .init = lockdown_lsm_init,
 167};
 168