Merge pull request #1857 from tych0/add-ima-project

projects: add IMA namespacing project
This commit is contained in:
Justin Cormack 2017-05-18 22:12:02 +01:00 committed by GitHub
commit ac31862c42
22 changed files with 6406 additions and 0 deletions

View File

@ -22,6 +22,8 @@ If you want to create a project, please submit a pull request to create a new di
- [Logging](logging/) Experimental logging tools
- [etcd cluster](etcd/) etcd cluster demo from DockerCon'17
- [kernel-config](kernel-config/) an experiment on how to manage kernel config
- [IMA-namespace](ima-namespace/) patches for supporting per-mount-namespace
IMA policies
## Current projects not yet documented
- VMWare support (VMWare)

View File

@ -0,0 +1,10 @@
.PHONY: run
run: ima-namespace-kernel
../../bin/linuxkit run ima-namespace
ima-namespace-kernel: ima-namespace.yml
../../bin/moby build ima-namespace
.PHONY: clean
clean:
-rm *-cmdline *-kernel *.img

View File

@ -0,0 +1,55 @@
## IMA
IMA stands for Integrity Management Architecture. The basic idea is to prevent
userspace from even *opening* files that have been mutated, by tracking file
content via a hash in the `security.ima` extended attribute. IMA supports
keeping track of these hashes and signing the result via the TPM, and a host of
other features.
Today, this is not namespace aware, so there is no way to differentiate in
IMA's appraisal output between files in one mount namespace vs another, which
makes this not particularly useful for container engines. The goal of this
patchset is to make IMA namespace aware.
## IMA namespace patches
These are draft patches for an implementation of IMA namespacing. They are
currently a rebased version of the v1 set posted here [1].
### Usage
Let's suppose you have some sensitive files owned by a particular user that you
want to keep secure:
sensitive=/tmp/foo
user=71452
mkdir -p $(dirname $sensitive) && echo "hello" > $sensitive
chown $user $sensitive
To use IMA in the per-namespace mode, you need ima\_appraise=enforce\_ns on the
kernel CLI (this is done in the yaml file). Then, the userspace interface looks
something like this:
# create a new mount namespace
unshare -m
# enable per-ns policy for this new namespace
nsid=$(readlink /proc/self/ns/mnt | cut -c '6-15')
echo ${nsid} > /sys/kernel/security/ima/namespaces
# set the policy (we use tmpfs magic here since that's all that linuxkit
# has available to write to for this example)
TMPFS_MAGIC=0x01021994
printf "appraise fsmagic=$TMPFS_MAGIC fowner=$user\nappraise func=MODULE_CHECK" > /sys/kernel/security/ima/$nsid/policy
hash=$(echo -e "\x4$(openssl dgst -sha256 -binary $sensitive)")
setfattr -n security.ima -v "${hash}" $sensitive
And now you should be able to see things failing:
moby:/# echo foo > /tmp/foo
moby:/# cat /tmp/foo
[ 3233.681544] audit: type=1800 audit(1495131746.610:29): pid=384 uid=0 auid=4294967295 ses=4294967295 op="appraise_data" cause="invalid-hash" comm="cat" name="/tmp/foo" mnt_ns=4026532208 dev="tmpfs" ino=13105 res=0
cat: can't open '/tmp/foo': Permission denied
[1]: https://lkml.org/lkml/2017/5/11/699

View File

@ -0,0 +1,60 @@
kernel:
image: "linuxkit/kernel-ima:4.11.1-"
cmdline: "console=ttyS0 console=tty0 page_poison=1 ima_appraise=enforce_ns"
init:
- linuxkit/init:b3740303f3d1e5689a84c87b7dfb48fd2a40a192
- linuxkit/runc:47b1c38d63468c0f3078f8b1b055d07965a1895d
- linuxkit/containerd:cf2614f5a96c569a0bd4bd54e054a65ba17d167f
- linuxkit/ca-certificates:3344cdca1bc59fdfa17bd7f0fcbf491b9dbaa288
- linuxkit/ima-utils:fe119c7dac08884f4144cd106dc279ddd8b37517
onboot:
- name: sysctl
image: "linuxkit/sysctl:2cf2f9d5b4d314ba1bfc22b2fe931924af666d8c"
net: host
pid: host
ipc: host
capabilities:
- CAP_SYS_ADMIN
readonly: true
- name: binfmt
image: "linuxkit/binfmt:131026c0cf6084467316395fed3b358f64bda00c"
binds:
- /proc/sys/fs/binfmt_misc:/binfmt_misc
readonly: true
- name: dhcpcd
image: "linuxkit/dhcpcd:2def74ab3f9233b4c09ebb196ba47c27c08b0ed8"
binds:
- /var:/var
- /tmp/etc:/etc
capabilities:
- CAP_NET_ADMIN
- CAP_NET_BIND_SERVICE
- CAP_NET_RAW
net: host
command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"]
services:
- name: rngd
image: "linuxkit/rngd:61a07ced77a9747708223ca16a4aec621eacf518"
capabilities:
- CAP_SYS_ADMIN
oomScoreAdj: -800
readonly: true
- name: nginx
image: "nginx:alpine"
capabilities:
- CAP_NET_BIND_SERVICE
- CAP_CHOWN
- CAP_SETUID
- CAP_SETGID
- CAP_DAC_OVERRIDE
net: host
files:
- path: etc/docker/daemon.json
contents: '{"debug": true}'
trust:
image:
- linuxkit/kernel
- linuxkit/binfmt
- linuxkit/rngd
outputs:
- format: kernel+initrd

View File

@ -0,0 +1,9 @@
FROM alpine:edge as utils
RUN apk add --no-cache attr openssl
FROM scratch
ENTRYPOINT []
CMD []
WORKDIR /
COPY --from=utils /usr/bin/openssl /usr/bin/setfattr /usr/bin/
COPY --from=utils /lib/libattr.so* /lib/libssl.so* /lib/libcrypto.so* /lib/

View File

@ -0,0 +1,14 @@
.PHONY: tag push
default: push
IMAGE=ima-utils
DEPS=Dockerfile
HASH?=$(shell git ls-tree HEAD -- ../$(notdir $(CURDIR)) | awk '{print $$3}')
tag: $(DEPS)
docker build --no-cache -t linuxkit/$(IMAGE):$(HASH) .
push: tag
docker pull linuxkit/$(IMAGE):$(HASH) || \
docker push linuxkit/$(IMAGE):$(HASH)

View File

@ -0,0 +1,6 @@
x86_64/
etc/
lib/
usr/
sbin/
bzImage

View File

@ -0,0 +1,73 @@
FROM linuxkit/kernel-compile:1b396c221af673757703258159ddc8539843b02b@sha256:6b32d205bfc6407568324337b707d195d027328dbfec554428ea93e7b0a8299b AS kernel-build
ARG KERNEL_VERSION
ARG KERNEL_SERIES
ARG DEBUG
ENV KERNEL_SOURCE=https://www.kernel.org/pub/linux/kernel/v4.x/linux-${KERNEL_VERSION}.tar.xz
RUN curl -fsSL -o linux-${KERNEL_VERSION}.tar.xz ${KERNEL_SOURCE}
RUN cat linux-${KERNEL_VERSION}.tar.xz | tar --absolute-names -xJ && mv /linux-${KERNEL_VERSION} /linux
COPY kernel_config-${KERNEL_SERIES} /linux/arch/x86/configs/x86_64_defconfig
COPY kernel_config.debug /linux/debug_config
RUN if [ -n "${DEBUG}" ]; then \
sed -i 's/CONFIG_PANIC_ON_OOPS=y/# CONFIG_PANIC_ON_OOPS is not set/' /linux/arch/x86/configs/x86_64_defconfig; \
cat /linux/debug_config >> /linux/arch/x86/configs/x86_64_defconfig; \
fi
# Apply local patches
COPY patches-${KERNEL_SERIES} /patches
WORKDIR /linux
RUN set -e && for patch in /patches/*.patch; do \
echo "Applying $patch"; \
patch -p1 < "$patch"; \
done
RUN mkdir /out
# Kernel
RUN make defconfig && \
make oldconfig && \
make -j "$(getconf _NPROCESSORS_ONLN)" KCFLAGS="-fno-pie" && \
cp arch/x86_64/boot/bzImage /out/kernel && \
cp System.map /out && \
([ -n "${DEBUG}" ] && cp vmlinux /out || true)
# Modules
RUN make INSTALL_MOD_PATH=/tmp/kernel-modules modules_install && \
( DVER=$(basename $(find /tmp/kernel-modules/lib/modules/ -mindepth 1 -maxdepth 1)) && \
cd /tmp/kernel-modules/lib/modules/$DVER && \
rm build source && \
ln -s /usr/src/linux-headers-$DVER build ) && \
( cd /tmp/kernel-modules && tar cf /out/kernel.tar lib )
# Headers (userspace API)
RUN mkdir -p /tmp/kernel-headers/usr && \
make INSTALL_HDR_PATH=/tmp/kernel-headers/usr headers_install && \
( cd /tmp/kernel-headers && tar cf /out/kernel-headers.tar usr )
# Headers (kernel development)
RUN DVER=$(basename $(find /tmp/kernel-modules/lib/modules/ -mindepth 1 -maxdepth 1)) && \
dir=/tmp/usr/src/linux-headers-$DVER && \
mkdir -p $dir && \
cp /linux/.config $dir && \
cp /linux/Module.symvers $dir && \
find . -path './include/*' -prune -o \
-path './arch/*/include' -prune -o \
-path './scripts/*' -prune -o \
-type f \( -name 'Makefile*' -o -name 'Kconfig*' -o -name 'Kbuild*' -o \
-name '*.lds' -o -name '*.pl' -o -name '*.sh' \) | \
tar cf - -T - | (cd $dir; tar xf -) && \
( cd /tmp && tar cf /out/kernel-dev.tar usr/src )
RUN printf "KERNEL_SOURCE=${KERNEL_SOURCE}\n" > /out/kernel-source-info
FROM linuxkit/toybox-media:b396a375852e5dffc002389d95e0658c8de72914@sha256:a317cc378946ee48cc011cdfc5aa08f0229f5bf10ff70e3690d8f60b36700033
ENTRYPOINT []
CMD []
WORKDIR /
COPY --from=kernel-build /out/* /

View File

@ -0,0 +1,66 @@
# This builds the supported LinuxKit kernels. Kernels are wrapped up
# in a minimal toybox container, which contains the bzImage, a tar
# ball with modules and the kernel source.
#
# Each kernel is pushed to hub twice, once as
# linuxkit/kernel:<kernel>.<major>.<minor>-<hash> and once as
# inuxkit/kernel:<kernel>.<major>.x. The <hash> is the git tree hash
# of the current directory. The build will only rebuild the kernel
# image if the git tree hash changed.
# Git tree hash of this directory. Override to force build
HASH?=$(shell git ls-tree HEAD -- ../$(notdir $(CURDIR)) | awk '{print $$3}')
# Name and Org on Hub
ORG?=linuxkit
IMAGE:=kernel-ima
.PHONY: check tag push sign
# Targets:
# build: builds all kernels
# push: pushes all tagged kernel images to hub
# sign: sign and push all kernel images to hub
build:
push:
sign:
# A template for defining kernel build
# Arguments:
# $1: Full kernel version, e.g., 4.9.22
# $2: Kernel "series", e.g., 4.9.x
# $3: Build a debug kernel (used as suffix for image)
# This defines targets like:
# build_4.9.x, push_4.9.x and sign_4.9.x and adds them as dependencies
# to the global targets
# Set $3 to "_dbg", to build debug kernels. This defines targets like
# build_4.9.x_dbg and adds "_dbg" to the hub image name.
define kernel
build_$(2)$(3): Dockerfile Makefile $(wildcard patches-$(2)/*) kernel_config-$(2) kernel_config.debug
docker pull $(ORG)/$(IMAGE):$(1)$(3)-$(HASH) || \
docker build \
--build-arg KERNEL_VERSION=$(1) \
--build-arg KERNEL_SERIES=$(2) \
--build-arg DEBUG=$(3) \
--no-cache -t $(ORG)/$(IMAGE):$(1)$(3)-$(HASH) .
push_$(2)$(3): build_$(2)$(3)
docker pull $(ORG)/$(IMAGE):$(1)$(3)-$(HASH) || \
(docker push $(ORG)/$(IMAGE):$(1)$(3)-$(HASH) && \
docker tag $(ORG)/$(IMAGE):$(1)$(3)-$(HASH) $(ORG)/$(IMAGE):$(2)$(3) && \
docker push $(ORG)/$(IMAGE):$(2)$(3))
sign_$(2)$(3): build_$(2)$(3)
DOCKER_CONTENT_TRUST=1 docker pull $(ORG)/$(IMAGE):$(1)$(3)-$(HASH) || \
(DOCKER_CONTENT_TRUST=1 docker push $(ORG)/$(IMAGE):$(1)$(3)-$(HASH) && \
docker tag $(ORG)/$(IMAGE):$(1)$(3)-$(HASH) $(ORG)/$(IMAGE):$(2)$(3) && \
DOCKER_CONTENT_TRUST=1 docker push $(ORG)/$(IMAGE):$(2)$(3))
build: build_$(2)$(3)
push: push_$(2)$(3)
sign: sign_$(2)$(3)
endef
#
# Build Targets
# Debug targets only for latest stable and LTS stable
#
$(eval $(call kernel,4.11.1,4.11.x))

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
## LinuxKit DEBUG OPTIONS ##
CONFIG_LOCKDEP=y
CONFIG_FRAME_POINTER=y
CONFIG_LOCKUP_DETECTOR=y
CONFIG_DETECT_HUNG_TASK=y
CONFIG_DEBUG_TIMEKEEPING=y
CONFIG_DEBUG_RT_MUTEXES=y
CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y
CONFIG_DEBUG_LOCK_ALLOC=y
CONFIG_PROVE_LOCKING=y
CONFIG_LOCK_STAT=y
CONFIG_DEBUG_ATOMIC_SLEEP=y
CONFIG_DEBUG_LIST=y
CONFIG_DEBUG_NOTIFIERS=y
CONFIG_PROVE_RCU=y
CONFIG_RCU_TRACE=y
CONFIG_KGDB=y
CONFIG_KGDB_SERIAL_CONSOLE=y
CONFIG_KGDBOC=y
CONFIG_DEBUG_RODATA_TEST=y
CONFIG_DEBUG_WX=y

View File

@ -0,0 +1,44 @@
From 9d601ff5f8a643bf2d9995e073fa5ba2f5a5e9db Mon Sep 17 00:00:00 2001
From: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
Date: Tue, 9 May 2017 10:34:10 -0300
Subject: [PATCH 01/11] ima: qualify pathname in audit info record
Adding new field (mount namespace id, along with already existent file
inode and device name) to uniquely identify a pathname considering
different mount namespaces. The file inode on a given device is unique
and these fields are required to identify a namespace id since this
id can be released and later reused by a different namespace.
Signed-off-by: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
---
security/integrity/integrity_audit.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/security/integrity/integrity_audit.c b/security/integrity/integrity_audit.c
index 90987d1..e675e42 100644
--- a/security/integrity/integrity_audit.c
+++ b/security/integrity/integrity_audit.c
@@ -13,6 +13,7 @@
#include <linux/fs.h>
#include <linux/gfp.h>
#include <linux/audit.h>
+#include <linux/proc_ns.h>
#include "integrity.h"
static int integrity_audit_info;
@@ -52,8 +53,12 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,
audit_log_format(ab, " comm=");
audit_log_untrustedstring(ab, get_task_comm(name, current));
if (fname) {
+ struct ns_common *ns;
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, fname);
+ ns = mntns_operations.get(current);
+ audit_log_format(ab, " mnt_ns=%u", ns->inum);
+ mntns_operations.put(ns);
}
if (inode) {
audit_log_format(ab, " dev=");
--
2.9.3

View File

@ -0,0 +1,52 @@
From 9eb525d553c6aa296f9396e192a37e453b30d5f9 Mon Sep 17 00:00:00 2001
From: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
Date: Tue, 9 May 2017 10:36:16 -0300
Subject: [PATCH 02/11] ima: qualify pathname in audit measurement record
Adding new fields (mount namespace id, file inode and device name) to
uniquely identify a pathname considering different mount namespaces.
The file inode on a given device is unique and these fields are
required to identify a namespace id since this id can be released
and later reused by a different namespace.
Signed-off-by: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
---
security/integrity/ima/ima_api.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index c2edba8..b05c1fd 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -18,6 +18,7 @@
#include <linux/fs.h>
#include <linux/xattr.h>
#include <linux/evm.h>
+#include <linux/proc_ns.h>
#include "ima.h"
@@ -293,6 +294,7 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
char hash[(iint->ima_hash->length * 2) + 1];
const char *algo_name = hash_algo_name[iint->ima_hash->algo];
char algo_hash[sizeof(hash) + strlen(algo_name) + 2];
+ struct ns_common *ns;
int i;
if (iint->flags & IMA_AUDITED)
@@ -312,6 +314,12 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
audit_log_format(ab, " hash=");
snprintf(algo_hash, sizeof(algo_hash), "%s:%s", algo_name, hash);
audit_log_untrustedstring(ab, algo_hash);
+ ns = mntns_operations.get(current);
+ audit_log_format(ab, " mnt_ns=%u", ns->inum);
+ mntns_operations.put(ns);
+ audit_log_format(ab, " dev=");
+ audit_log_untrustedstring(ab, iint->inode->i_sb->s_id);
+ audit_log_format(ab, " ino=%lu", iint->inode->i_ino);
audit_log_task_info(ab, current);
audit_log_end(ab);
--
2.9.3

View File

@ -0,0 +1,210 @@
From e96e10b725a11c2d563caa3907212b149355b70f Mon Sep 17 00:00:00 2001
From: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
Date: Tue, 9 May 2017 11:09:04 -0300
Subject: [PATCH 03/11] ima: qualify pathname in measurement file
Adding new fields (mount namespace id, file inode and device name) to
uniquely identify a pathname in the measurement file considering
multiple mount namespaces. The file inode on a given device is unique
and these fields are required to identify a namespace id since this
id can be released and later reused by a different namespace.
These new fields are added to all measurement templates if
CONFIG_IMA_PER_NAMESPACE is defined.
There will still be one single measurement file even with multiple
namespaces, since for the remote attestion a single and complete list
is required.
Signed-off-by: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
---
security/integrity/ima/Kconfig | 8 ++++
security/integrity/ima/ima.h | 12 ++++++
security/integrity/ima/ima_template.c | 10 ++++-
security/integrity/ima/ima_template_lib.c | 70 +++++++++++++++++++++++++++++++
security/integrity/ima/ima_template_lib.h | 13 ++++++
5 files changed, 111 insertions(+), 2 deletions(-)
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 370eb2f..7331ff6 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -219,3 +219,11 @@ config IMA_APPRAISE_SIGNED_INIT
default n
help
This option requires user-space init to be signed.
+
+config IMA_PER_NAMESPACE
+ bool "Enable per mount-namespace handling of IMA policy."
+ depends on IMA
+ default n
+ help
+ This option enables another API in securityfs allowing IMA policies to
+ be defined per mount namespace.
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index b563fbd..42fb91ba 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -47,7 +47,19 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
#define IMA_TEMPLATE_NUM_FIELDS_MAX 15
#define IMA_TEMPLATE_IMA_NAME "ima"
+#define IMA_TEMPLATE_IMA_NG_NAME "ima-ng"
+#define IMA_TEMPLATE_IMA_SIG_NAME "ima-sig"
+
+#ifndef CONFIG_IMA_PER_NAMESPACE
#define IMA_TEMPLATE_IMA_FMT "d|n"
+#define IMA_TEMPLATE_IMA_NG_FMT "d-ng|n-ng"
+#define IMA_TEMPLATE_IMA_SIG_FMT "d-ng|n-ng|sig"
+#else
+#define IMA_TEMPLATE_IMA_FMT "nid|fi|dev|d|n"
+#define IMA_TEMPLATE_IMA_NG_FMT "nid|fi|dev|d-ng|n-ng"
+#define IMA_TEMPLATE_IMA_SIG_FMT "nid|fi|dev|d-ng|n-ng|sig"
+#endif
+
/* current content of the policy */
extern int ima_policy_flag;
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index cebb37c..db65c09 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -21,8 +21,8 @@
static struct ima_template_desc builtin_templates[] = {
{.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT},
- {.name = "ima-ng", .fmt = "d-ng|n-ng"},
- {.name = "ima-sig", .fmt = "d-ng|n-ng|sig"},
+ {.name = IMA_TEMPLATE_IMA_NG_NAME, .fmt = IMA_TEMPLATE_IMA_NG_FMT},
+ {.name = IMA_TEMPLATE_IMA_SIG_NAME, .fmt = IMA_TEMPLATE_IMA_SIG_FMT},
{.name = "", .fmt = ""}, /* placeholder for a custom format */
};
@@ -40,6 +40,12 @@ static struct ima_template_field supported_fields[] = {
.field_show = ima_show_template_string},
{.field_id = "sig", .field_init = ima_eventsig_init,
.field_show = ima_show_template_sig},
+ {.field_id = "nid", .field_init = ima_namespaceid_init,
+ .field_show = ima_show_namespaceid},
+ {.field_id = "fi", .field_init = ima_filei_init,
+ .field_show = ima_show_filei},
+ {.field_id = "dev", .field_init = ima_dev_init,
+ .field_show = ima_show_dev},
};
#define MAX_TEMPLATE_NAME_LEN 15
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index f9ba37b..50cde10 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -14,6 +14,8 @@
*/
#include "ima_template_lib.h"
+#include <linux/proc_ns.h>
+#include <linux/types.h>
static bool ima_template_hash_algo_allowed(u8 algo)
{
@@ -330,3 +332,71 @@ int ima_eventsig_init(struct ima_event_data *event_data,
out:
return rc;
}
+
+int ima_namespaceid_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ u8 tmpbuf[64];
+ struct ns_common *ns;
+
+ ns = mntns_operations.get(current);
+ snprintf(tmpbuf, sizeof(tmpbuf), "mnt-ns=%u", ns->inum);
+ mntns_operations.put(ns);
+
+ return ima_write_template_field_data(tmpbuf, strlen(tmpbuf), DATA_FMT_STRING, field_data);
+}
+
+void ima_show_namespaceid(struct seq_file *m, enum ima_show_type show,
+ struct ima_field_data *field_data)
+{
+ ima_show_template_field_data(m, show, DATA_FMT_STRING, field_data);
+}
+
+int ima_filei_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ u8 tmpbuf[64];
+ struct inode *inode;
+ int rc = 0;
+
+ if (event_data->file) {
+ inode = file_inode(event_data->file);
+ snprintf(tmpbuf, sizeof(tmpbuf), "inode=%lu", inode->i_ino);
+ rc = ima_write_template_field_data(tmpbuf, strlen(tmpbuf), DATA_FMT_STRING, field_data);
+ } else {
+ pr_info("IMA: event file is NULL\n");
+ }
+
+ return rc;
+}
+
+void ima_show_filei(struct seq_file *m, enum ima_show_type show,
+ struct ima_field_data *field_data)
+{
+ ima_show_template_field_data(m, show, DATA_FMT_STRING, field_data);
+}
+
+int ima_dev_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ u8 tmpbuf[64];
+ struct inode *inode;
+ int rc = 0;
+
+ if (event_data->file) {
+ inode = file_inode(event_data->file);
+ snprintf(tmpbuf, sizeof(tmpbuf), "dev=%s", inode->i_sb->s_id); //TODO: check untrusted string? see audit_log_n_untrustedstring()
+ tmpbuf[sizeof(tmpbuf) - 1] = 0;
+ rc = ima_write_template_field_data(tmpbuf, strlen(tmpbuf), DATA_FMT_STRING, field_data);
+ } else {
+ pr_info("IMA: event file is NULL\n");
+ }
+
+ return rc;
+}
+
+void ima_show_dev(struct seq_file *m, enum ima_show_type show,
+ struct ima_field_data *field_data)
+{
+ ima_show_template_field_data(m, show, DATA_FMT_STRING, field_data);
+}
diff --git a/security/integrity/ima/ima_template_lib.h b/security/integrity/ima/ima_template_lib.h
index c344530..cf6a6c7 100644
--- a/security/integrity/ima/ima_template_lib.h
+++ b/security/integrity/ima/ima_template_lib.h
@@ -26,6 +26,12 @@ void ima_show_template_string(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
+void ima_show_namespaceid(struct seq_file *m, enum ima_show_type show,
+ struct ima_field_data *field_data);
+void ima_show_filei(struct seq_file *m, enum ima_show_type show,
+ struct ima_field_data *field_data);
+void ima_show_dev(struct seq_file *m, enum ima_show_type show,
+ struct ima_field_data *field_data);
int ima_eventdigest_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventname_init(struct ima_event_data *event_data,
@@ -36,4 +42,11 @@ int ima_eventname_ng_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
+int ima_namespaceid_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
+int ima_filei_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
+int ima_dev_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
+
#endif /* __LINUX_IMA_TEMPLATE_LIB_H */
--
2.9.3

View File

@ -0,0 +1,262 @@
From 3966bcc59bbb0d43b4b2bf8b005788cad75cf65d Mon Sep 17 00:00:00 2001
From: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
Date: Tue, 9 May 2017 13:41:24 -0300
Subject: [PATCH 04/11] ima: add support to namespace securityfs file
Creating the namespace securityfs file under ima folder. When a mount
namespace id is written to the namespace file, a new folder is created and
with a policy file for that specified namespace. Then, user defined policy
for namespaces may be set by writing rules to this namespace policy file.
With this interface, there is no need to give visibility for the securityfs
inside mount namespaces or containers in userspace.
Signed-off-by: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
---
security/integrity/ima/ima.h | 4 +
security/integrity/ima/ima_fs.c | 183 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 187 insertions(+)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 42fb91ba..6e8ca8e 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -326,4 +326,8 @@ static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
#define POLICY_FILE_FLAGS S_IWUSR
#endif /* CONFIG_IMA_WRITE_POLICY */
+#ifdef CONFIG_IMA_PER_NAMESPACE
+#define NAMESPACES_FILE_FLAGS S_IWUSR
+#endif
+
#endif /* __LINUX_IMA_H */
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index ca303e5..6456407 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -23,6 +23,8 @@
#include <linux/rcupdate.h>
#include <linux/parser.h>
#include <linux/vmalloc.h>
+#include <linux/proc_ns.h>
+#include <linux/radix-tree.h>
#include "ima.h"
@@ -272,6 +274,40 @@ static const struct file_operations ima_ascii_measurements_ops = {
.release = seq_release,
};
+#ifdef CONFIG_IMA_PER_NAMESPACE
+/*
+ * check_mntns: check a mount namespace is valid
+ *
+ * @ns_id: namespace id to be checked
+ * Returns 0 if the namespace is valid.
+ *
+ * Note: a better way to implement this check is needed. There are
+ * cases where the namespace id is valid but not in use by any process
+ * and then this implementation misses this case. Could we use an
+ * interface similar to what setns implements?
+ */
+static int check_mntns(unsigned int ns_id)
+{
+ struct task_struct *p;
+ int result = 1;
+ struct ns_common *ns;
+
+ rcu_read_lock();
+ for_each_process(p) {
+ ns = mntns_operations.get(p);
+ if (ns->inum == ns_id) {
+ result = 0;
+ mntns_operations.put(ns);
+ break;
+ }
+ mntns_operations.put(ns);
+ }
+ rcu_read_unlock();
+
+ return result;
+}
+#endif
+
static ssize_t ima_read_policy(char *path)
{
void *data;
@@ -366,6 +402,9 @@ static struct dentry *ascii_runtime_measurements;
static struct dentry *runtime_measurements_count;
static struct dentry *violations;
static struct dentry *ima_policy;
+#ifdef CONFIG_IMA_PER_NAMESPACE
+static struct dentry *ima_namespaces;
+#endif
enum ima_fs_flags {
IMA_FS_BUSY,
@@ -451,6 +490,139 @@ static const struct file_operations ima_measure_policy_ops = {
.llseek = generic_file_llseek,
};
+#ifdef CONFIG_IMA_PER_NAMESPACE
+/*
+ * Assumes namespace id is in use by some process and this mapping
+ * does not exist in the map table.
+ */
+static int create_mnt_ns_directory(unsigned int ns_id)
+{
+ int result;
+ struct dentry *ns_dir, *ns_policy;
+ char dir_name[64];
+
+ snprintf(dir_name, sizeof(dir_name), "%u", ns_id);
+
+ ns_dir = securityfs_create_dir(dir_name, ima_dir);
+ if (IS_ERR(ns_dir)) {
+ result = PTR_ERR(ns_dir);
+ goto out;
+ }
+
+ ns_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
+ ns_dir, NULL,
+ &ima_measure_policy_ops);
+ if (IS_ERR(ns_policy)) {
+ result = PTR_ERR(ns_policy);
+ securityfs_remove(ns_dir);
+ goto out;
+ }
+
+ result = 0;
+
+out:
+ return result;
+}
+
+static ssize_t handle_new_namespace_policy(const char *data, size_t datalen)
+{
+ unsigned int ns_id;
+ ssize_t result;
+
+ result = -EINVAL;
+
+ if (sscanf(data, "%u", &ns_id) != 1) {
+ pr_err("IMA: invalid namespace id: %s\n", data);
+ goto out;
+ }
+
+ if (check_mntns(ns_id)) {
+ result = -ENOENT;
+ pr_err("IMA: unused namespace id %u\n", ns_id);
+ goto out;
+ }
+
+ result = create_mnt_ns_directory(ns_id);
+ if (result != 0) {
+ pr_err("IMA: namespace id %u directory creation failed\n", ns_id);
+ goto out;
+ }
+
+ result = datalen;
+ pr_info("IMA: directory created for namespace id %u\n", ns_id);
+
+out:
+ return result;
+}
+
+static ssize_t ima_write_namespaces(struct file *file, const char __user *buf,
+ size_t datalen, loff_t *ppos)
+{
+ char *data;
+ ssize_t result;
+
+ if (datalen >= PAGE_SIZE)
+ datalen = PAGE_SIZE - 1;
+
+ /* No partial writes. */
+ result = -EINVAL;
+ if (*ppos != 0)
+ goto out;
+
+ result = -ENOMEM;
+ data = kmalloc(datalen + 1, GFP_KERNEL);
+ if (!data)
+ goto out;
+
+ *(data + datalen) = '\0';
+
+ result = -EFAULT;
+ if (copy_from_user(data, buf, datalen))
+ goto out_free;
+
+ result = mutex_lock_interruptible(&ima_write_mutex);
+ if (result < 0)
+ goto out_free;
+
+ result = handle_new_namespace_policy(data, datalen);
+
+ mutex_unlock(&ima_write_mutex);
+
+out_free:
+ kfree(data);
+out:
+ return result;
+}
+
+static int ima_open_namespaces(struct inode *inode, struct file *filp)
+{
+ if (!(filp->f_flags & O_WRONLY))
+ return -EACCES;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags))
+ return -EBUSY;
+ return 0;
+}
+
+static int ima_release_namespaces(struct inode *inode, struct file *file)
+{
+ clear_bit(IMA_FS_BUSY, &ima_fs_flags);
+
+ return 0;
+}
+
+static const struct file_operations ima_namespaces_ops = {
+ .open = ima_open_namespaces,
+ .write = ima_write_namespaces,
+ .read = seq_read,
+ .release = ima_release_namespaces,
+ .llseek = generic_file_llseek,
+};
+#endif
+
int __init ima_fs_init(void)
{
ima_dir = securityfs_create_dir("ima", NULL);
@@ -490,6 +662,14 @@ int __init ima_fs_init(void)
if (IS_ERR(ima_policy))
goto out;
+#ifdef CONFIG_IMA_PER_NAMESPACE
+ ima_namespaces = securityfs_create_file("namespaces", NAMESPACES_FILE_FLAGS,
+ ima_dir, NULL,
+ &ima_namespaces_ops);
+ if (IS_ERR(ima_namespaces))
+ goto out;
+#endif
+
return 0;
out:
securityfs_remove(violations);
@@ -498,5 +678,8 @@ int __init ima_fs_init(void)
securityfs_remove(binary_runtime_measurements);
securityfs_remove(ima_dir);
securityfs_remove(ima_policy);
+#ifdef CONFIG_IMA_PER_NAMESPACE
+ securityfs_remove(ima_namespaces);
+#endif
return -1;
}
--
2.9.3

View File

@ -0,0 +1,301 @@
From 9f1840db5abfabeaeb7835bc277a75ac23c4b188 Mon Sep 17 00:00:00 2001
From: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
Date: Tue, 9 May 2017 16:24:05 -0300
Subject: [PATCH 05/11] ima: store new namespace policy structure in a radix
tree
New ima_ns_policy structure to describe IMA policy data per namespace.
Using a radix tree to map namespace ids to a respective ima_ns_policy
structure.
When it is needed to retrieve IMA policy rules/flags, the target
ima_ns_policy structure is retrieved from the radix tree by getting the
namespace id from the current context.
Signed-off-by: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
---
security/integrity/ima/ima.h | 37 +++++++++++++++++
security/integrity/ima/ima_fs.c | 79 ++++++++++++++++++++++++++++++++++---
security/integrity/ima/ima_init.c | 2 +
security/integrity/ima/ima_policy.c | 29 +++++++++-----
4 files changed, 133 insertions(+), 14 deletions(-)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 6e8ca8e..1c5c875 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -140,6 +140,21 @@ static inline void ima_load_kexec_buffer(void) {}
*/
extern bool ima_canonical_fmt;
+/* Namespace policy globals */
+struct ima_ns_policy {
+ struct dentry *policy_dentry;
+ struct dentry *ns_dentry;
+ struct list_head *ima_rules;
+ struct list_head ima_policy_rules;
+ int ima_policy_flag;
+ int ima_appraise;
+};
+
+#ifdef CONFIG_IMA_PER_NAMESPACE
+extern spinlock_t ima_ns_policy_lock;
+extern struct radix_tree_root ima_ns_policy_mapping;
+#endif
+
/* Internal IMA function definitions */
int ima_init(void);
int ima_fs_init(void);
@@ -166,6 +181,27 @@ int ima_measurements_show(struct seq_file *m, void *v);
unsigned long ima_get_binary_runtime_size(void);
int ima_init_template(void);
void ima_init_template_list(void);
+#ifdef CONFIG_IMA_PER_NAMESPACE
+static inline void ima_namespace_lock_init(void) {
+ spin_lock_init(&ima_ns_policy_lock);
+}
+static inline void ima_namespace_lock(void) {
+ spin_lock(&ima_ns_policy_lock);
+}
+static inline void ima_namespace_unlock(void) {
+ spin_unlock(&ima_ns_policy_lock);
+}
+#else
+static inline void ima_namespace_lock_init(void) {
+ return;
+}
+static inline void ima_namespace_lock(void) {
+ return;
+}
+static inline void ima_namespace_unlock(void) {
+ return;
+}
+#endif
/*
* used to protect h_table and sha_table
@@ -226,6 +262,7 @@ void ima_update_policy(void);
void ima_update_policy_flag(void);
ssize_t ima_parse_add_rule(char *);
void ima_delete_rules(void);
+void ima_free_policy_rules(struct list_head *policy_rules);
int ima_check_policy(void);
void *ima_policy_start(struct seq_file *m, loff_t *pos);
void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 6456407..ce6dcdf 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -275,6 +275,48 @@ static const struct file_operations ima_ascii_measurements_ops = {
};
#ifdef CONFIG_IMA_PER_NAMESPACE
+/* used for namespace policy rules initialization */
+static LIST_HEAD(empty_policy);
+
+static int allocate_namespace_policy(struct ima_ns_policy **ins,
+ struct dentry *policy_dentry, struct dentry *ns_dentry)
+{
+ int result;
+ struct ima_ns_policy *p;
+
+ p = kmalloc(sizeof(struct ima_ns_policy), GFP_KERNEL);
+ if (!p) {
+ result = -ENOMEM;
+ goto out;
+ }
+
+ p->policy_dentry = policy_dentry;
+ p->ns_dentry = ns_dentry;
+ p->ima_appraise = 0;
+ p->ima_policy_flag = 0;
+ INIT_LIST_HEAD(&p->ima_policy_rules);
+ /* namespace starts with empty rules and not pointing to
+ * ima_policy_rules */
+ p->ima_rules = &empty_policy;
+
+ result = 0;
+ *ins = p;
+
+out:
+ return result;
+}
+
+static void free_namespace_policy(struct ima_ns_policy *ins)
+{
+ if (ins->policy_dentry)
+ securityfs_remove(ins->policy_dentry);
+ securityfs_remove(ins->ns_dentry);
+
+ ima_free_policy_rules(&ins->ima_policy_rules);
+
+ kfree(ins);
+}
+
/*
* check_mntns: check a mount namespace is valid
*
@@ -476,9 +518,11 @@ static int ima_release_policy(struct inode *inode, struct file *file)
#ifndef CONFIG_IMA_WRITE_POLICY
securityfs_remove(ima_policy);
ima_policy = NULL;
-#else
- clear_bit(IMA_FS_BUSY, &ima_fs_flags);
#endif
+
+ /* always clear the busy flag so other namespaces can use it */
+ clear_bit(IMA_FS_BUSY, &ima_fs_flags);
+
return 0;
}
@@ -500,11 +544,14 @@ static int create_mnt_ns_directory(unsigned int ns_id)
int result;
struct dentry *ns_dir, *ns_policy;
char dir_name[64];
+ struct ima_ns_policy *ins;
snprintf(dir_name, sizeof(dir_name), "%u", ns_id);
ns_dir = securityfs_create_dir(dir_name, ima_dir);
if (IS_ERR(ns_dir)) {
+ /* TODO: handle EEXIST error, remove the folder and
+ continue the procedure */
result = PTR_ERR(ns_dir);
goto out;
}
@@ -518,7 +565,15 @@ static int create_mnt_ns_directory(unsigned int ns_id)
goto out;
}
- result = 0;
+ result = allocate_namespace_policy(&ins, ns_policy, ns_dir);
+ if (!result) {
+ result = radix_tree_insert(&ima_ns_policy_mapping, ns_id, ins);
+ if (result)
+ free_namespace_policy(ins);
+ } else {
+ securityfs_remove(ns_policy);
+ securityfs_remove(ns_dir);
+ }
out:
return result;
@@ -528,6 +583,7 @@ static ssize_t handle_new_namespace_policy(const char *data, size_t datalen)
{
unsigned int ns_id;
ssize_t result;
+ struct ima_ns_policy *ins;
result = -EINVAL;
@@ -536,21 +592,34 @@ static ssize_t handle_new_namespace_policy(const char *data, size_t datalen)
goto out;
}
+ rcu_read_lock();
+ ins = radix_tree_lookup(&ima_ns_policy_mapping, ns_id);
+ rcu_read_unlock();
+ if (ins) {
+ pr_info("IMA: directory for namespace id %u already created\n", ns_id);
+ result = datalen;
+ goto out;
+ }
+
+ ima_namespace_lock();
if (check_mntns(ns_id)) {
result = -ENOENT;
pr_err("IMA: unused namespace id %u\n", ns_id);
- goto out;
+ goto out_unlock;
}
result = create_mnt_ns_directory(ns_id);
if (result != 0) {
pr_err("IMA: namespace id %u directory creation failed\n", ns_id);
- goto out;
+ goto out_unlock;
}
result = datalen;
pr_info("IMA: directory created for namespace id %u\n", ns_id);
+out_unlock:
+ ima_namespace_unlock();
+
out:
return result;
}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 2967d49..b557ee3 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -135,6 +135,8 @@ int __init ima_init(void)
if (rc != 0)
return rc;
+ ima_namespace_lock_init();
+
ima_init_policy();
return ima_fs_init();
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index aed47b7..2e8c3b7 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -47,6 +47,12 @@
int ima_policy_flag;
static int temp_ima_appraise;
+#ifdef CONFIG_IMA_PER_NAMESPACE
+/* policy namespace map entries except the initial namespace policy */
+RADIX_TREE(ima_ns_policy_mapping, GFP_ATOMIC);
+spinlock_t ima_ns_policy_lock;
+#endif
+
#define MAX_LSM_RULES 6
enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
@@ -863,19 +869,12 @@ ssize_t ima_parse_add_rule(char *rule)
return len;
}
-/**
- * ima_delete_rules() called to cleanup invalid in-flight policy.
- * We don't need locking as we operate on the temp list, which is
- * different from the active one. There is also only one user of
- * ima_delete_rules() at a time.
- */
-void ima_delete_rules(void)
+void ima_free_policy_rules(struct list_head *policy_rules)
{
struct ima_rule_entry *entry, *tmp;
int i;
- temp_ima_appraise = 0;
- list_for_each_entry_safe(entry, tmp, &ima_temp_rules, list) {
+ list_for_each_entry_safe(entry, tmp, policy_rules, list) {
for (i = 0; i < MAX_LSM_RULES; i++)
kfree(entry->lsm[i].args_p);
@@ -884,6 +883,18 @@ void ima_delete_rules(void)
}
}
+/**
+ * ima_delete_rules() called to cleanup invalid in-flight policy.
+ * We don't need locking as we operate on the temp list, which is
+ * different from the active one. There is also only one user of
+ * ima_delete_rules() at a time.
+ */
+void ima_delete_rules(void)
+{
+ temp_ima_appraise = 0;
+ ima_free_policy_rules(&ima_temp_rules);
+}
+
#ifdef CONFIG_IMA_READ_POLICY
enum {
mask_exec = 0, mask_write, mask_read, mask_append
--
2.9.3

View File

@ -0,0 +1,104 @@
From 49c15686df3676edcc354a02c666f66b81bbb348 Mon Sep 17 00:00:00 2001
From: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
Date: Tue, 9 May 2017 16:50:01 -0300
Subject: [PATCH 06/11] ima, fs: release namespace policy resources
Release all namespace IMA policy resources when the mount namespace is
released.
This is the suggested mechanism to release namespace policy resources,
but we still can discuss other methods to avoid cross-component changes.
Signed-off-by: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
---
fs/namespace.c | 4 ++++
include/linux/integrity.h | 9 +++++++++
security/integrity/ima/ima_fs.c | 26 ++++++++++++++++++++++++++
3 files changed, 39 insertions(+)
diff --git a/fs/namespace.c b/fs/namespace.c
index cc1375ef..80940998 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -15,6 +15,7 @@
#include <linux/user_namespace.h>
#include <linux/namei.h>
#include <linux/security.h>
+#include <linux/integrity.h>
#include <linux/cred.h>
#include <linux/idr.h>
#include <linux/init.h> /* init_rootfs */
@@ -3283,6 +3284,9 @@ void put_mnt_ns(struct mnt_namespace *ns)
{
if (!atomic_dec_and_test(&ns->count))
return;
+
+ ima_mnt_namespace_dying(ns->ns.inum);
+
drop_collected_mounts(&ns->root->mnt);
free_mnt_ns(ns);
}
diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index c2d6082..034d082 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -43,4 +43,13 @@ static inline void integrity_load_keys(void)
}
#endif /* CONFIG_INTEGRITY */
+#ifdef CONFIG_IMA_PER_NAMESPACE
+extern void ima_mnt_namespace_dying(unsigned int ns_id);
+#else
+static inline void ima_mnt_namespace_dying(unsigned int ns_id)
+{
+ return;
+}
+#endif /* CONFIG_IMA_PER_NAMESPACE */
+
#endif /* _LINUX_INTEGRITY_H */
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index ce6dcdf..56ba0ff 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -423,6 +423,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
"policy_update", "signed policy required",
1, 0);
+
if (ima_appraise & IMA_APPRAISE_ENFORCE)
result = -EACCES;
} else {
@@ -579,6 +580,31 @@ static int create_mnt_ns_directory(unsigned int ns_id)
return result;
}
+/*
+ * ima_mnt_namespace_dying - releases all namespace policy resources
+ * It is called automatically when the namespace is released.
+ * @ns_id namespace id to be released
+ *
+ * Note: This function is called by put_mnt_ns() in the context
+ * of a namespace release. We need to make sure that a lock on
+ * this path is allowed.
+ */
+void ima_mnt_namespace_dying(unsigned int ns_id)
+{
+ struct ima_ns_policy *p;
+
+ spin_lock(&ima_ns_policy_lock);
+ p = radix_tree_delete(&ima_ns_policy_mapping, ns_id);
+
+ if (!p) {
+ spin_unlock(&ima_ns_policy_lock);
+ return;
+ }
+
+ free_namespace_policy(p);
+ spin_unlock(&ima_ns_policy_lock);
+}
+
static ssize_t handle_new_namespace_policy(const char *data, size_t datalen)
{
unsigned int ns_id;
--
2.9.3

View File

@ -0,0 +1,313 @@
From ffefef30c066f49ca03045aeb0f92635e998c385 Mon Sep 17 00:00:00 2001
From: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
Date: Tue, 9 May 2017 17:04:38 -0300
Subject: [PATCH 07/11] ima: new namespace policy structure to track initial
namespace policy data
Adding the global ima_initial_namespace_policy which will be used when the
initial namespace IMA policy data must be referred or when
CONFIG_IMA_PER_NAMESPACE is not defined.
New functions which will be used to retrieve the correct namespace IMA
policy data from the radix tree map or from the ima_initial_namespace_policy.
If the given namespace has not yet defined a private IMA policy, the IMA
policy for that namespace falls back to the initial IMA policy by using
ima_initial_namespace_policy.
Signed-off-by: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
---
security/integrity/ima/ima.h | 6 ++
security/integrity/ima/ima_fs.c | 112 +++++++++++++++++++++++++++++-------
security/integrity/ima/ima_policy.c | 72 +++++++++++++++++++++++
3 files changed, 170 insertions(+), 20 deletions(-)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 1c5c875..20b927e 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -150,6 +150,7 @@ struct ima_ns_policy {
int ima_appraise;
};
+extern struct ima_ns_policy ima_initial_namespace_policy;
#ifdef CONFIG_IMA_PER_NAMESPACE
extern spinlock_t ima_ns_policy_lock;
extern struct radix_tree_root ima_ns_policy_mapping;
@@ -203,6 +204,11 @@ static inline void ima_namespace_unlock(void) {
}
#endif
+/* IMA namespace function definitions */
+struct ima_ns_policy *ima_get_current_namespace_policy(void);
+struct ima_ns_policy *ima_get_namespace_policy_from_inode(struct inode *inode);
+struct ima_ns_policy *ima_get_policy_from_namespace(unsigned int ns_id);
+
/*
* used to protect h_table and sha_table
*/
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 56ba0ff..61f8da1 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -274,6 +274,22 @@ static const struct file_operations ima_ascii_measurements_ops = {
.release = seq_release,
};
+static struct dentry *ima_dir;
+static struct dentry *binary_runtime_measurements;
+static struct dentry *ascii_runtime_measurements;
+static struct dentry *runtime_measurements_count;
+static struct dentry *violations;
+static struct dentry *ima_policy_initial_ns;
+#ifdef CONFIG_IMA_PER_NAMESPACE
+static struct dentry *ima_namespaces;
+#endif
+
+enum ima_fs_flags {
+ IMA_FS_BUSY,
+};
+
+static unsigned long ima_fs_flags;
+
#ifdef CONFIG_IMA_PER_NAMESPACE
/* used for namespace policy rules initialization */
static LIST_HEAD(empty_policy);
@@ -348,6 +364,76 @@ static int check_mntns(unsigned int ns_id)
return result;
}
+
+/*
+ * ima_find_namespace_id_from_inode
+ * @policy_inode: the inode of the securityfs policy file for a given
+ * namespace
+ *
+ * Return 0 if the namespace id is not found in ima_ns_policy_mapping
+ */
+static unsigned int find_namespace_id_from_inode(struct inode *policy_inode)
+{
+ unsigned int ns_id = 0;
+#ifdef CONFIG_IMA_PER_NAMESPACE
+ struct ima_ns_policy *ins;
+ void **slot;
+ struct radix_tree_iter iter;
+
+ rcu_read_lock();
+ radix_tree_for_each_slot(slot, &ima_ns_policy_mapping, &iter, 0) {
+ ins = radix_tree_deref_slot(slot);
+ if (unlikely(!ins))
+ continue;
+ if (radix_tree_deref_retry(ins)) {
+ slot = radix_tree_iter_retry(&iter);
+ continue;
+ }
+
+ if (ins->policy_dentry && ins->policy_dentry->d_inode == policy_inode) {
+ ns_id = iter.index;
+ break;
+ }
+ }
+ rcu_read_unlock();
+#endif
+
+ return ns_id;
+}
+
+/*
+ * get_namespace_policy_from_inode - Finds namespace mapping from
+ * securityfs policy file
+ * It is called to get the namespace policy reference when a seurityfs
+ * file such as the namespace or policy files are read or written.
+ * @inode: inode of the securityfs policy file under a namespace
+ * folder
+ * Expects the ima_ns_policy_lock already held
+ *
+ * Returns NULL if the namespace policy reference is not reliable once it
+ * probably was already released after a concurrent namespace release.
+ * Otherwise, the namespace policy reference is returned.
+ */
+struct ima_ns_policy *ima_get_namespace_policy_from_inode(struct inode *inode)
+{
+ unsigned int ns_id;
+ struct ima_ns_policy *ins;
+
+ ns_id = find_namespace_id_from_inode(inode);
+#ifdef CONFIG_IMA_PER_NAMESPACE
+ if (ns_id == 0 &&
+ (!ima_policy_initial_ns || inode != ima_policy_initial_ns->d_inode)) {
+ /* ns_id == 0 refers to initial namespace, but inode refers to a
+ * namespaced policy file. It might be a race condition with
+ * namespace release, return invalid reference. */
+ return NULL;
+ }
+#endif
+
+ ins = ima_get_policy_from_namespace(ns_id);
+
+ return ins;
+}
#endif
static ssize_t ima_read_policy(char *path)
@@ -439,22 +525,6 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
return result;
}
-static struct dentry *ima_dir;
-static struct dentry *binary_runtime_measurements;
-static struct dentry *ascii_runtime_measurements;
-static struct dentry *runtime_measurements_count;
-static struct dentry *violations;
-static struct dentry *ima_policy;
-#ifdef CONFIG_IMA_PER_NAMESPACE
-static struct dentry *ima_namespaces;
-#endif
-
-enum ima_fs_flags {
- IMA_FS_BUSY,
-};
-
-static unsigned long ima_fs_flags;
-
#ifdef CONFIG_IMA_READ_POLICY
static const struct seq_operations ima_policy_seqops = {
.start = ima_policy_start,
@@ -517,7 +587,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
ima_update_policy();
#ifndef CONFIG_IMA_WRITE_POLICY
- securityfs_remove(ima_policy);
+ securityfs_remove(ima_policy_initial_ns);
ima_policy = NULL;
#endif
@@ -539,6 +609,8 @@ static const struct file_operations ima_measure_policy_ops = {
/*
* Assumes namespace id is in use by some process and this mapping
* does not exist in the map table.
+ * @ns_id namespace id
+ * Expects ima_ns_policy_lock already held
*/
static int create_mnt_ns_directory(unsigned int ns_id)
{
@@ -751,10 +823,10 @@ int __init ima_fs_init(void)
if (IS_ERR(violations))
goto out;
- ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
+ ima_policy_initial_ns = securityfs_create_file("policy", POLICY_FILE_FLAGS,
ima_dir, NULL,
&ima_measure_policy_ops);
- if (IS_ERR(ima_policy))
+ if (IS_ERR(ima_policy_initial_ns))
goto out;
#ifdef CONFIG_IMA_PER_NAMESPACE
@@ -772,7 +844,7 @@ int __init ima_fs_init(void)
securityfs_remove(ascii_runtime_measurements);
securityfs_remove(binary_runtime_measurements);
securityfs_remove(ima_dir);
- securityfs_remove(ima_policy);
+ securityfs_remove(ima_policy_initial_ns);
#ifdef CONFIG_IMA_PER_NAMESPACE
securityfs_remove(ima_namespaces);
#endif
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 2e8c3b7..8c0d4c9 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -20,6 +20,8 @@
#include <linux/rculist.h>
#include <linux/genhd.h>
#include <linux/seq_file.h>
+#include <linux/radix-tree.h>
+#include <linux/proc_ns.h>
#include "ima.h"
@@ -53,6 +55,17 @@ RADIX_TREE(ima_ns_policy_mapping, GFP_ATOMIC);
spinlock_t ima_ns_policy_lock;
#endif
+/* initial namespace map entry, not added to the ima_ns_policy_mapping
+ * Used as policy fallback for namespaces without policy settings */
+struct ima_ns_policy ima_initial_namespace_policy = {
+ .policy_dentry = NULL,
+ .ns_dentry = NULL,
+ .ima_rules = NULL,
+ .ima_policy_rules = LIST_HEAD_INIT(ima_initial_namespace_policy.ima_policy_rules),
+ .ima_policy_flag = 0,
+ .ima_appraise = 0
+ };
+
#define MAX_LSM_RULES 6
enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
@@ -191,6 +204,65 @@ static int __init default_appraise_policy_setup(char *str)
__setup("ima_appraise_tcb", default_appraise_policy_setup);
/*
+ * ima_get_policy_from_namespace - Finds the ns_id mapping to namespace
+ * policy structure
+ * @ns_id: mount namespace id to look for in the policy mapping tree
+ *
+ * Returns either the given namespace policy data if mapped or the initial
+ * namespace data instead.
+ *
+ * Note that if a namespace has not a specific policy defined, it will
+ * fall back to the initial namespace policy.
+ */
+struct ima_ns_policy *ima_get_policy_from_namespace(unsigned int ns_id)
+{
+ struct ima_ns_policy *ins;
+
+#ifdef CONFIG_IMA_PER_NAMESPACE
+ rcu_read_lock();
+ ins = radix_tree_lookup(&ima_ns_policy_mapping, ns_id);
+ rcu_read_unlock();
+
+ if (!ins) {
+ ins = &ima_initial_namespace_policy;
+ }
+#else
+ ins = &ima_initial_namespace_policy;
+#endif
+
+ return ins;
+}
+
+/*
+ * ima_get_current_namespace_policy - Finds the namespace policy mapping
+ * for the current task
+ * This function is called on the context of a syscall and then the namespace
+ * in use will not be released during this context.
+ */
+struct ima_ns_policy *ima_get_current_namespace_policy(void)
+{
+ struct ima_ns_policy *ins = NULL;
+#ifdef CONFIG_IMA_PER_NAMESPACE
+ struct ns_common *ns;
+
+ ns = mntns_operations.get(current);
+ if (ns) {
+ ins = ima_get_policy_from_namespace(ns->inum);
+ mntns_operations.put(ns);
+ }
+ if (!ins || (ins->ima_rules != &ins->ima_policy_rules)) {
+ /* if current namespace has no IMA policy, get the
+ * initial namespace policy */
+ ins = &ima_initial_namespace_policy;
+ }
+#else
+ ins = &ima_initial_namespace_policy;
+#endif
+
+ return ins;
+}
+
+/*
* The LSM policy can be reloaded, leaving the IMA LSM based rules referring
* to the old, stale LSM policy. Update the IMA LSM based rules to reflect
* the reloaded LSM policy. We assume the rules still exist; and BUG_ON() if
--
2.9.3

View File

@ -0,0 +1,61 @@
From 0a3ac1bcf03b07940dff18ce29cd05ced91155c0 Mon Sep 17 00:00:00 2001
From: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
Date: Tue, 9 May 2017 17:19:57 -0300
Subject: [PATCH 08/11] ima: block initial namespace id on the namespace policy
interface
The initial namespace policy is set through the existent interface
in the ima/policy securityfs file. Block the initial namespace
id when it is written to the ima/namespace securityfs file.
Signed-off-by: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
---
security/integrity/ima/ima_fs.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 61f8da1..65c43e7 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -365,6 +365,16 @@ static int check_mntns(unsigned int ns_id)
return result;
}
+static unsigned int initial_mntns_id;
+static void get_initial_mntns_id(void)
+{
+ struct ns_common *ns;
+
+ ns = mntns_operations.get(&init_task);
+ initial_mntns_id = ns->inum;
+ mntns_operations.put(ns);
+}
+
/*
* ima_find_namespace_id_from_inode
* @policy_inode: the inode of the securityfs policy file for a given
@@ -699,6 +709,12 @@ static ssize_t handle_new_namespace_policy(const char *data, size_t datalen)
goto out;
}
+ if (ns_id == initial_mntns_id) {
+ pr_err("IMA: invalid use of the initial mount namespace\n");
+ result = -EINVAL;
+ goto out;
+ }
+
ima_namespace_lock();
if (check_mntns(ns_id)) {
result = -ENOENT;
@@ -835,6 +851,8 @@ int __init ima_fs_init(void)
&ima_namespaces_ops);
if (IS_ERR(ima_namespaces))
goto out;
+
+ get_initial_mntns_id();
#endif
return 0;
--
2.9.3

View File

@ -0,0 +1,70 @@
From 825422c8d524f3500b7a09cda2cf39e28b4999ea Mon Sep 17 00:00:00 2001
From: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
Date: Tue, 9 May 2017 17:31:22 -0300
Subject: [PATCH 09/11] ima: delete namespace policy securityfs file in
write-once mode
When policy file is written and write-once is enabled, the policy file
must be deleted. Select the namespace policy structure to get the correct
policy file descriptor.
Signed-off-by: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
---
security/integrity/ima/ima_fs.c | 27 +++++++++++++++++++++++++--
1 file changed, 25 insertions(+), 2 deletions(-)
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 65c43e7..94e89fe 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -575,6 +575,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
static int ima_release_policy(struct inode *inode, struct file *file)
{
const char *cause = valid_policy ? "completed" : "failed";
+ struct ima_ns_policy *ins;
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
return seq_release(inode, file);
@@ -595,15 +596,37 @@ static int ima_release_policy(struct inode *inode, struct file *file)
return 0;
}
+ /* get the namespace id from file->inode (policy file inode).
+ * We also need to synchronize this operation with concurrent namespace
+ * releasing. */
+ ima_namespace_lock();
+ ins = ima_get_namespace_policy_from_inode(inode);
+ if (!ins) {
+ /* the namespace is not valid anymore, discard new policy
+ * rules and exit */
+ ima_delete_rules();
+ valid_policy = 1;
+ clear_bit(IMA_FS_BUSY, &ima_fs_flags);
+ ima_namespace_unlock();
+ return 0;
+ }
+
ima_update_policy();
#ifndef CONFIG_IMA_WRITE_POLICY
- securityfs_remove(ima_policy_initial_ns);
- ima_policy = NULL;
+ if (ins == &ima_initial_namespace_policy) {
+ securityfs_remove(ima_policy_initial_ns);
+ ima_policy_initial_ns = NULL;
+ } else {
+ securityfs_remove(ins->policy_dentry);
+ ins->policy_dentry = NULL;
+ }
#endif
/* always clear the busy flag so other namespaces can use it */
clear_bit(IMA_FS_BUSY, &ima_fs_flags);
+ ima_namespace_unlock();
+
return 0;
}
--
2.9.3

View File

@ -0,0 +1,689 @@
From c424a95ccee13d974fa54e92ca50cec5b19a9e94 Mon Sep 17 00:00:00 2001
From: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
Date: Tue, 9 May 2017 18:00:55 -0300
Subject: [PATCH 10/11] ima: handling all policy flags per namespace using
ima_ns_policy structure
Global ima_appraise still saves the initial appraise mode and it is used to
initialize namespace.ima_appraise flag when a user defined policy is set.
Globals moved into ima_ns_policy structure (namespace IMA policy private data):
- ima_policy_flag
- ima_appraise
- ima_rules
- ima_policy_rules
Functions changed to take as parameter the correct ima_ns_policy structure.
ima_initial_namespace_policy is initialized in ima_init_policy and stores the
initial namespace IMA policy data.
Replacing direct uses of ima_ns_policy lock with the ima_namespace_lock() and
ima_namespace_unlock() functions.
Signed-off-by: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
---
security/integrity/ima/ima.h | 17 +++---
security/integrity/ima/ima_api.c | 6 +-
security/integrity/ima/ima_appraise.c | 21 +++++--
security/integrity/ima/ima_fs.c | 26 ++++++---
security/integrity/ima/ima_init.c | 11 +++-
security/integrity/ima/ima_main.c | 36 ++++++++----
security/integrity/ima/ima_policy.c | 100 +++++++++++++++++++++++++---------
7 files changed, 150 insertions(+), 67 deletions(-)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 20b927e..fd5cfe9 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -61,9 +61,6 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
#endif
-/* current content of the policy */
-extern int ima_policy_flag;
-
/* set during initialization */
extern int ima_initialized;
extern int ima_used_chip;
@@ -149,7 +146,6 @@ struct ima_ns_policy {
int ima_policy_flag;
int ima_appraise;
};
-
extern struct ima_ns_policy ima_initial_namespace_policy;
#ifdef CONFIG_IMA_PER_NAMESPACE
extern spinlock_t ima_ns_policy_lock;
@@ -241,7 +237,7 @@ enum ima_hooks {
/* LIM API function definitions */
int ima_get_action(struct inode *inode, int mask,
- enum ima_hooks func, int *pcr);
+ enum ima_hooks func, int *pcr, struct ima_ns_policy *ins);
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
int ima_collect_measurement(struct integrity_iint_cache *iint,
struct file *file, void *buf, loff_t size,
@@ -262,10 +258,10 @@ const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
/* IMA policy related functions */
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
- int flags, int *pcr);
+ int flags, int *pcr, struct ima_ns_policy *ins);
void ima_init_policy(void);
-void ima_update_policy(void);
-void ima_update_policy_flag(void);
+void ima_update_policy(struct ima_ns_policy *ins);
+void ima_update_policy_flag(struct ima_ns_policy *ins);
ssize_t ima_parse_add_rule(char *);
void ima_delete_rules(void);
void ima_free_policy_rules(struct list_head *policy_rules);
@@ -283,12 +279,13 @@ int ima_policy_show(struct seq_file *m, void *v);
#define IMA_APPRAISE_FIRMWARE 0x10
#define IMA_APPRAISE_POLICY 0x20
+
#ifdef CONFIG_IMA_APPRAISE
int ima_appraise_measurement(enum ima_hooks func,
struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len, int opened);
+ int xattr_len, int opened, struct ima_ns_policy *ins);
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func);
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
@@ -304,7 +301,7 @@ static inline int ima_appraise_measurement(enum ima_hooks func,
struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len, int opened)
+ int xattr_len, int opened, struct ima_ns_policy *ins)
{
return INTEGRITY_UNKNOWN;
}
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index b05c1fd..9aba542 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -173,13 +173,13 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
* Returns IMA_MEASURE, IMA_APPRAISE mask.
*
*/
-int ima_get_action(struct inode *inode, int mask, enum ima_hooks func, int *pcr)
+int ima_get_action(struct inode *inode, int mask, enum ima_hooks func, int *pcr, struct ima_ns_policy *ins)
{
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE;
- flags &= ima_policy_flag;
+ flags &= ins->ima_policy_flag;
- return ima_match_policy(inode, func, mask, flags, pcr);
+ return ima_match_policy(inode, func, mask, flags, pcr, ins);
}
/*
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 1fd9539..510bb2f 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -26,6 +26,7 @@ static int __init default_appraise_setup(char *str)
ima_appraise = IMA_APPRAISE_LOG;
else if (strncmp(str, "fix", 3) == 0)
ima_appraise = IMA_APPRAISE_FIX;
+
return 1;
}
@@ -38,10 +39,12 @@ __setup("ima_appraise=", default_appraise_setup);
*/
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
{
- if (!ima_appraise)
+ struct ima_ns_policy *ins = ima_get_current_namespace_policy();
+
+ if (!ins->ima_appraise)
return 0;
- return ima_match_policy(inode, func, mask, IMA_APPRAISE, NULL);
+ return ima_match_policy(inode, func, mask, IMA_APPRAISE, NULL, ins);
}
static int ima_fix_xattr(struct dentry *dentry,
@@ -189,7 +192,7 @@ int ima_appraise_measurement(enum ima_hooks func,
struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len, int opened)
+ int xattr_len, int opened, struct ima_ns_policy *ins)
{
static const char op[] = "appraise_data";
char *cause = "unknown";
@@ -273,7 +276,7 @@ int ima_appraise_measurement(enum ima_hooks func,
out:
if (status != INTEGRITY_PASS) {
- if ((ima_appraise & IMA_APPRAISE_FIX) &&
+ if ((ins->ima_appraise & IMA_APPRAISE_FIX) &&
(!xattr_value ||
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
if (!ima_fix_xattr(dentry, iint))
@@ -326,8 +329,11 @@ void ima_inode_post_setattr(struct dentry *dentry)
struct inode *inode = d_backing_inode(dentry);
struct integrity_iint_cache *iint;
int must_appraise;
+ struct ima_ns_policy *ins;
- if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
+ ins = ima_get_current_namespace_policy();
+
+ if (!(ins->ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
|| !(inode->i_opflags & IOP_XATTR))
return;
@@ -363,8 +369,11 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
static void ima_reset_appraise_flags(struct inode *inode, int digsig)
{
struct integrity_iint_cache *iint;
+ struct ima_ns_policy *ins;
+
+ ins = ima_get_current_namespace_policy();
- if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode))
+ if (!(ins->ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode))
return;
iint = integrity_iint_find(inode);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 94e89fe..bc18722 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -308,7 +308,7 @@ static int allocate_namespace_policy(struct ima_ns_policy **ins,
p->policy_dentry = policy_dentry;
p->ns_dentry = ns_dentry;
- p->ima_appraise = 0;
+ p->ima_appraise = ima_appraise;
p->ima_policy_flag = 0;
INIT_LIST_HEAD(&p->ima_policy_rules);
/* namespace starts with empty rules and not pointing to
@@ -488,6 +488,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
{
char *data;
ssize_t result;
+ struct ima_ns_policy *ins;
if (datalen >= PAGE_SIZE)
datalen = PAGE_SIZE - 1;
@@ -512,19 +513,30 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
if (result < 0)
goto out_free;
+ ima_namespace_lock();
+ ins = ima_get_namespace_policy_from_inode(file->f_inode);
+ if (!ins) {
+ /* the namespace is not valid anymore, indicate the error
+ * and exit */
+ result = -EINVAL;
+ goto out_unlock;
+ }
+
if (data[0] == '/') {
result = ima_read_policy(data);
- } else if (ima_appraise & IMA_APPRAISE_POLICY) {
+ } else if (ins->ima_appraise & IMA_APPRAISE_POLICY) {
pr_err("IMA: signed policy file (specified as an absolute pathname) required\n");
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
"policy_update", "signed policy required",
1, 0);
- if (ima_appraise & IMA_APPRAISE_ENFORCE)
+ if (ins->ima_appraise & IMA_APPRAISE_ENFORCE)
result = -EACCES;
} else {
result = ima_parse_add_rule(data);
}
+out_unlock:
+ ima_namespace_unlock();
mutex_unlock(&ima_write_mutex);
out_free:
kfree(data);
@@ -611,7 +623,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
return 0;
}
- ima_update_policy();
+ ima_update_policy(ins);
#ifndef CONFIG_IMA_WRITE_POLICY
if (ins == &ima_initial_namespace_policy) {
securityfs_remove(ima_policy_initial_ns);
@@ -698,16 +710,16 @@ void ima_mnt_namespace_dying(unsigned int ns_id)
{
struct ima_ns_policy *p;
- spin_lock(&ima_ns_policy_lock);
+ ima_namespace_lock();
p = radix_tree_delete(&ima_ns_policy_mapping, ns_id);
if (!p) {
- spin_unlock(&ima_ns_policy_lock);
+ ima_namespace_unlock();
return;
}
free_namespace_policy(p);
- spin_unlock(&ima_ns_policy_lock);
+ ima_namespace_unlock();
}
static ssize_t handle_new_namespace_policy(const char *data, size_t datalen)
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index b557ee3..f0bb196 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -96,11 +96,16 @@ static int __init ima_add_boot_aggregate(void)
#ifdef CONFIG_IMA_LOAD_X509
void __init ima_load_x509(void)
{
- int unset_flags = ima_policy_flag & IMA_APPRAISE;
+ int unset_flags;
+ struct ima_ns_policy *ins;
- ima_policy_flag &= ~unset_flags;
+ ins = ima_get_current_namespace_policy();
+
+ unset_flags = ins->ima_policy_flag & IMA_APPRAISE;
+
+ ins->ima_policy_flag &= ~unset_flags;
integrity_load_x509(INTEGRITY_KEYRING_IMA, CONFIG_IMA_X509_PATH);
- ima_policy_flag |= unset_flags;
+ ins->ima_policy_flag |= unset_flags;
}
#endif
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 2aebb79..1b995bb 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -30,6 +30,7 @@
int ima_initialized;
#ifdef CONFIG_IMA_APPRAISE
+/* Used during IMA initialization only */
int ima_appraise = IMA_APPRAISE_ENFORCE;
#else
int ima_appraise;
@@ -144,9 +145,13 @@ void ima_file_free(struct file *file)
{
struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint;
+ struct ima_ns_policy *ins;
- if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+ ins = ima_get_current_namespace_policy();
+
+ if (!ins->ima_policy_flag || !S_ISREG(inode->i_mode)) {
return;
+ }
iint = integrity_iint_find(inode);
if (!iint)
@@ -170,17 +175,20 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
int xattr_len = 0;
bool violation_check;
enum hash_algo hash_algo;
+ struct ima_ns_policy *ins;
- if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+ ins = ima_get_current_namespace_policy();
+
+ if (!ins->ima_policy_flag || !S_ISREG(inode->i_mode))
return 0;
/* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action
* bitmask based on the appraise/audit/measurement policy.
* Included is the appraise submask.
*/
- action = ima_get_action(inode, mask, func, &pcr);
+ action = ima_get_action(inode, mask, func, &pcr, ins);
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
- (ima_policy_flag & IMA_MEASURE));
+ (ins->ima_policy_flag & IMA_MEASURE));
if (!action && !violation_check)
return 0;
@@ -249,7 +257,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
xattr_value, xattr_len, pcr);
if (action & IMA_APPRAISE_SUBMASK)
rc = ima_appraise_measurement(func, iint, file, pathname,
- xattr_value, xattr_len, opened);
+ xattr_value, xattr_len, opened, ins);
if (action & IMA_AUDIT)
ima_audit_measurement(iint, pathname);
@@ -263,7 +271,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
__putname(pathbuf);
out:
inode_unlock(inode);
- if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))
+ if ((rc && must_appraise) && (ins->ima_appraise & IMA_APPRAISE_ENFORCE))
return -EACCES;
return 0;
}
@@ -361,8 +369,10 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id)
{
if (!file && read_id == READING_MODULE) {
#ifndef CONFIG_MODULE_SIG_FORCE
- if ((ima_appraise & IMA_APPRAISE_MODULES) &&
- (ima_appraise & IMA_APPRAISE_ENFORCE))
+ struct ima_ns_policy *ins;
+ ins = ima_get_current_namespace_policy();
+ if ((ins->ima_appraise & IMA_APPRAISE_MODULES) &&
+ (ins->ima_appraise & IMA_APPRAISE_ENFORCE))
return -EACCES; /* INTEGRITY_UNKNOWN */
#endif
return 0; /* We rely on module signature checking */
@@ -395,10 +405,13 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
enum kernel_read_file_id read_id)
{
enum ima_hooks func;
+ struct ima_ns_policy *ins;
+
+ ins = ima_get_current_namespace_policy();
if (!file && read_id == READING_FIRMWARE) {
- if ((ima_appraise & IMA_APPRAISE_FIRMWARE) &&
- (ima_appraise & IMA_APPRAISE_ENFORCE))
+ if ((ins->ima_appraise & IMA_APPRAISE_FIRMWARE) &&
+ (ins->ima_appraise & IMA_APPRAISE_ENFORCE))
return -EACCES; /* INTEGRITY_UNKNOWN */
return 0;
}
@@ -407,7 +420,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
return 0;
if (!file || !buf || size == 0) { /* should never happen */
- if (ima_appraise & IMA_APPRAISE_ENFORCE)
+ if (ins->ima_appraise & IMA_APPRAISE_ENFORCE)
return -EACCES;
return 0;
}
@@ -425,7 +438,6 @@ static int __init init_ima(void)
error = ima_init();
if (!error) {
ima_initialized = 1;
- ima_update_policy_flag();
}
return error;
}
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 8c0d4c9..4ffb4ad 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -46,7 +46,7 @@
#define INVALID_PCR(a) (((a) < 0) || \
(a) >= (FIELD_SIZEOF(struct integrity_iint_cache, measured_pcrs) * 8))
-int ima_policy_flag;
+/* used only during policy initialization and policy change */
static int temp_ima_appraise;
#ifdef CONFIG_IMA_PER_NAMESPACE
@@ -66,6 +66,7 @@ struct ima_ns_policy ima_initial_namespace_policy = {
.ima_appraise = 0
};
+
#define MAX_LSM_RULES 6
enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
@@ -166,11 +167,12 @@ static struct ima_rule_entry default_appraise_rules[] = {
#endif
};
+/* used only during policy setup of the initial namespace */
static LIST_HEAD(ima_default_rules);
-static LIST_HEAD(ima_policy_rules);
+/* used during policy setting and cleaned up for the next policy setting */
static LIST_HEAD(ima_temp_rules);
-static struct list_head *ima_rules;
+/* only used during setup of the initial namespace policy */
static int ima_policy __initdata;
static int __init default_measure_policy_setup(char *str)
@@ -268,13 +270,14 @@ struct ima_ns_policy *ima_get_current_namespace_policy(void)
* the reloaded LSM policy. We assume the rules still exist; and BUG_ON() if
* they don't.
*/
-static void ima_lsm_update_rules(void)
+static void ima_lsm_update_rules(struct ima_ns_policy *ins)
{
struct ima_rule_entry *entry;
int result;
int i;
- list_for_each_entry(entry, &ima_policy_rules, list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(entry, &ins->ima_policy_rules, list) {
for (i = 0; i < MAX_LSM_RULES; i++) {
if (!entry->lsm[i].rule)
continue;
@@ -285,6 +288,7 @@ static void ima_lsm_update_rules(void)
BUG_ON(!entry->lsm[i].rule);
}
}
+ rcu_read_unlock();
}
/**
@@ -297,7 +301,7 @@ static void ima_lsm_update_rules(void)
* Returns true on rule match, false on failure.
*/
static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
- enum ima_hooks func, int mask)
+ enum ima_hooks func, int mask, struct ima_ns_policy *ins)
{
struct task_struct *tsk = current;
const struct cred *cred = current_cred();
@@ -365,7 +369,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
}
if ((rc < 0) && (!retried)) {
retried = 1;
- ima_lsm_update_rules();
+ ima_lsm_update_rules(ins);
goto retry;
}
if (!rc)
@@ -412,18 +416,18 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
* than writes so ima_match_policy() is classical RCU candidate.
*/
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
- int flags, int *pcr)
+ int flags, int *pcr, struct ima_ns_policy *ins)
{
struct ima_rule_entry *entry;
int action = 0, actmask = flags | (flags << 1);
rcu_read_lock();
- list_for_each_entry_rcu(entry, ima_rules, list) {
+ list_for_each_entry_rcu(entry, ins->ima_rules, list) {
if (!(entry->action & actmask))
continue;
- if (!ima_match_rules(entry, inode, func, mask))
+ if (!ima_match_rules(entry, inode, func, mask, ins))
continue;
action |= entry->flags & IMA_ACTION_FLAGS;
@@ -454,18 +458,20 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
* out of a function or not call the function in the first place
* can be made earlier.
*/
-void ima_update_policy_flag(void)
+void ima_update_policy_flag(struct ima_ns_policy *ins)
{
struct ima_rule_entry *entry;
- list_for_each_entry(entry, ima_rules, list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(entry, ins->ima_rules, list) {
if (entry->action & IMA_DO_MASK)
- ima_policy_flag |= entry->action;
+ ins->ima_policy_flag |= entry->action;
}
+ rcu_read_unlock();
- ima_appraise |= temp_ima_appraise;
- if (!ima_appraise)
- ima_policy_flag &= ~IMA_APPRAISE;
+ ins->ima_appraise |= temp_ima_appraise;
+ if (!ins->ima_appraise)
+ ins->ima_policy_flag &= ~IMA_APPRAISE;
}
/**
@@ -477,6 +483,7 @@ void ima_update_policy_flag(void)
void __init ima_init_policy(void)
{
int i, measure_entries, appraise_entries;
+ struct ima_ns_policy *ins;
/* if !ima_policy set entries = 0 so we load NO default rules */
measure_entries = ima_policy ? ARRAY_SIZE(dont_measure_rules) : 0;
@@ -507,8 +514,13 @@ void __init ima_init_policy(void)
temp_ima_appraise |= IMA_APPRAISE_POLICY;
}
- ima_rules = &ima_default_rules;
- ima_update_policy_flag();
+ ins = &ima_initial_namespace_policy;
+
+ ins->ima_rules = &ima_default_rules;
+ ins->ima_appraise = ima_appraise;
+
+ ima_update_policy_flag(ins);
+ temp_ima_appraise = 0;
}
/* Make sure we have a valid policy, at least containing some rules. */
@@ -530,14 +542,14 @@ int ima_check_policy(void)
* Policy rules are never deleted so ima_policy_flag gets zeroed only once when
* we switch from the default policy to user defined.
*/
-void ima_update_policy(void)
+void ima_update_policy(struct ima_ns_policy *ins)
{
struct list_head *first, *last, *policy;
/* append current policy with the new rules */
first = (&ima_temp_rules)->next;
last = (&ima_temp_rules)->prev;
- policy = &ima_policy_rules;
+ policy = &ins->ima_policy_rules;
synchronize_rcu();
@@ -549,11 +561,14 @@ void ima_update_policy(void)
/* prepare for the next policy rules addition */
INIT_LIST_HEAD(&ima_temp_rules);
- if (ima_rules != policy) {
- ima_policy_flag = 0;
- ima_rules = policy;
+ if (ins->ima_rules != policy) {
+ ins->ima_policy_flag = 0;
+ ins->ima_rules = policy;
+ ins->ima_appraise = ima_appraise;
}
- ima_update_policy_flag();
+
+ ima_update_policy_flag(ins);
+ temp_ima_appraise = 0;
}
enum {
@@ -964,6 +979,7 @@ void ima_free_policy_rules(struct list_head *policy_rules)
void ima_delete_rules(void)
{
temp_ima_appraise = 0;
+
ima_free_policy_rules(&ima_temp_rules);
}
@@ -1002,28 +1018,49 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos)
{
loff_t l = *pos;
struct ima_rule_entry *entry;
+ struct ima_ns_policy *ins;
+
+ ima_namespace_lock();
+ ins = ima_get_namespace_policy_from_inode(m->file->f_inode);
+ if (!ins) {
+ ima_namespace_unlock();
+ return NULL;
+ }
rcu_read_lock();
- list_for_each_entry_rcu(entry, ima_rules, list) {
+ list_for_each_entry_rcu(entry, ins->ima_rules, list) {
if (!l--) {
rcu_read_unlock();
+ ima_namespace_unlock();
return entry;
}
}
rcu_read_unlock();
+ ima_namespace_unlock();
return NULL;
}
void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
{
struct ima_rule_entry *entry = v;
+ struct ima_ns_policy *ins;
+ void *p;
+
+ ima_namespace_lock();
+ ins = ima_get_namespace_policy_from_inode(m->file->f_inode);
+ if (!ins) {
+ ima_namespace_unlock();
+ return NULL;
+ }
rcu_read_lock();
entry = list_entry_rcu(entry->list.next, struct ima_rule_entry, list);
rcu_read_unlock();
(*pos)++;
- return (&entry->list == ima_rules) ? NULL : entry;
+ p = (&entry->list == ins->ima_rules) ? NULL : entry;
+ ima_namespace_unlock();
+ return p;
}
void ima_policy_stop(struct seq_file *m, void *v)
@@ -1082,6 +1119,16 @@ int ima_policy_show(struct seq_file *m, void *v)
struct ima_rule_entry *entry = v;
int i;
char tbuf[64] = {0,};
+ struct ima_ns_policy *ins;
+
+ ima_namespace_lock();
+ ins = ima_get_namespace_policy_from_inode(m->file->f_inode);
+ if (!ins) {
+ /* this namespace was release and the policy entry is not valid
+ * anymore */
+ ima_namespace_unlock();
+ return 0;
+ }
rcu_read_lock();
@@ -1184,6 +1231,7 @@ int ima_policy_show(struct seq_file *m, void *v)
seq_puts(m, "permit_directio ");
rcu_read_unlock();
seq_puts(m, "\n");
+ ima_namespace_unlock();
return 0;
}
#endif /* CONFIG_IMA_READ_POLICY */
--
2.9.3

View File

@ -0,0 +1,141 @@
From c2ef1411f1671f0b5121392e69954977b7a76789 Mon Sep 17 00:00:00 2001
From: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
Date: Tue, 9 May 2017 18:04:16 -0300
Subject: [PATCH 11/11] ima: appraise mode per namespace with new enforce_ns
appraise mode
Global ima_appraise renamed to ima_appraise_mode and it saves the initial
appraise mode. It is used to initialize the ima_appraise ima_ns_policy field
when the policy is defined by user the first time for a namespace.
New 'enforce_ns' appraise mode created. On this new appraise mode, the initial
appraise mode works in 'enforce' mode, but for new namespaces the appraise
mode is set to 'fix' until a policy is defined for the new namespace and then
the appraise mode is automatically set to 'enforce'.
This new mode is useful to keep the initial namespace appraise mode clearly in
'enforce' mode while namespaces can set their appraise modes separatedly.
Signed-off-by: Guilherme Magalhaes <guilherme.magalhaes@hpe.com>
---
security/integrity/ima/ima.h | 6 +++++-
security/integrity/ima/ima_appraise.c | 11 +++++++----
security/integrity/ima/ima_fs.c | 7 ++++++-
security/integrity/ima/ima_main.c | 4 ++--
security/integrity/ima/ima_policy.c | 13 +++++++++++--
5 files changed, 31 insertions(+), 10 deletions(-)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index fd5cfe9..9d451fd 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -65,7 +65,7 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
extern int ima_initialized;
extern int ima_used_chip;
extern int ima_hash_algo;
-extern int ima_appraise;
+extern int ima_appraise_mode;
/* IMA event related data */
struct ima_event_data {
@@ -278,6 +278,10 @@ int ima_policy_show(struct seq_file *m, void *v);
#define IMA_APPRAISE_MODULES 0x08
#define IMA_APPRAISE_FIRMWARE 0x10
#define IMA_APPRAISE_POLICY 0x20
+#ifdef CONFIG_IMA_PER_NAMESPACE
+#define IMA_APPRAISE_NAMESPACE 0x40
+#define IMA_APPRAISE_ENFORCE_NS (IMA_APPRAISE_ENFORCE | IMA_APPRAISE_NAMESPACE)
+#endif
#ifdef CONFIG_IMA_APPRAISE
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 510bb2f..4b94c2a 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -21,12 +21,15 @@
static int __init default_appraise_setup(char *str)
{
if (strncmp(str, "off", 3) == 0)
- ima_appraise = 0;
+ ima_appraise_mode = 0;
else if (strncmp(str, "log", 3) == 0)
- ima_appraise = IMA_APPRAISE_LOG;
+ ima_appraise_mode = IMA_APPRAISE_LOG;
else if (strncmp(str, "fix", 3) == 0)
- ima_appraise = IMA_APPRAISE_FIX;
-
+ ima_appraise_mode = IMA_APPRAISE_FIX;
+#ifdef CONFIG_IMA_PER_NAMESPACE
+ else if (strncmp(str, "enforce_ns", 10) == 0)
+ ima_appraise_mode = IMA_APPRAISE_ENFORCE_NS;
+#endif
return 1;
}
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index bc18722..91cafb5 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -308,7 +308,12 @@ static int allocate_namespace_policy(struct ima_ns_policy **ins,
p->policy_dentry = policy_dentry;
p->ns_dentry = ns_dentry;
- p->ima_appraise = ima_appraise;
+ if (ima_appraise_mode == IMA_APPRAISE_ENFORCE_NS)
+ /* For now, on the enforce_ns mode, a new namespace starts in
+ * fix mode */
+ p->ima_appraise = IMA_APPRAISE_FIX;
+ else
+ p->ima_appraise = ima_appraise_mode;
p->ima_policy_flag = 0;
INIT_LIST_HEAD(&p->ima_policy_rules);
/* namespace starts with empty rules and not pointing to
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 1b995bb..1938c74 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -31,9 +31,9 @@ int ima_initialized;
#ifdef CONFIG_IMA_APPRAISE
/* Used during IMA initialization only */
-int ima_appraise = IMA_APPRAISE_ENFORCE;
+int ima_appraise_mode = IMA_APPRAISE_ENFORCE;
#else
-int ima_appraise;
+int ima_appraise_mode;
#endif
int ima_hash_algo = HASH_ALGO_SHA1;
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 4ffb4ad..bd67a08 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -517,7 +517,7 @@ void __init ima_init_policy(void)
ins = &ima_initial_namespace_policy;
ins->ima_rules = &ima_default_rules;
- ins->ima_appraise = ima_appraise;
+ ins->ima_appraise = ima_appraise_mode;
ima_update_policy_flag(ins);
temp_ima_appraise = 0;
@@ -564,7 +564,16 @@ void ima_update_policy(struct ima_ns_policy *ins)
if (ins->ima_rules != policy) {
ins->ima_policy_flag = 0;
ins->ima_rules = policy;
- ins->ima_appraise = ima_appraise;
+ ins->ima_appraise = ima_appraise_mode;
+#ifdef CONFIG_IMA_PER_NAMESPACE
+ if (ins != &ima_initial_namespace_policy &&
+ ima_appraise_mode == IMA_APPRAISE_ENFORCE_NS) {
+ /* For now, on the enforce_ns mode, switch to enforce mode
+ * when new policy is set for a namespace and for the first
+ * time */
+ ins->ima_appraise = IMA_APPRAISE_ENFORCE;
+ }
+#endif
}
ima_update_policy_flag(ins);
--
2.9.3