Skip to content

Commit 2261306

Browse files
bowerscd-corppcmoore
authored andcommitted
ipe: add userspace interface
As is typical with LSMs, IPE uses securityfs as its interface with userspace. for a complete list of the interfaces and the respective inputs/outputs, please see the documentation under admin-guide/LSM/ipe.rst Signed-off-by: Deven Bowers <[email protected]> Signed-off-by: Fan Wu <[email protected]> Signed-off-by: Paul Moore <[email protected]>
1 parent 7138679 commit 2261306

File tree

8 files changed

+727
-0
lines changed

8 files changed

+727
-0
lines changed

security/ipe/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
obj-$(CONFIG_SECURITY_IPE) += \
99
eval.o \
1010
hooks.o \
11+
fs.o \
1112
ipe.o \
1213
policy.o \
14+
policy_fs.o \
1315
policy_parser.o \

security/ipe/fs.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
4+
*/
5+
6+
#include <linux/dcache.h>
7+
#include <linux/security.h>
8+
9+
#include "ipe.h"
10+
#include "fs.h"
11+
#include "policy.h"
12+
13+
static struct dentry *np __ro_after_init;
14+
static struct dentry *root __ro_after_init;
15+
struct dentry *policy_root __ro_after_init;
16+
17+
/**
18+
* new_policy() - Write handler for the securityfs node, "ipe/new_policy".
19+
* @f: Supplies a file structure representing the securityfs node.
20+
* @data: Supplies a buffer passed to the write syscall.
21+
* @len: Supplies the length of @data.
22+
* @offset: unused.
23+
*
24+
* Return:
25+
* * Length of buffer written - Success
26+
* * %-EPERM - Insufficient permission
27+
* * %-ENOMEM - Out of memory (OOM)
28+
* * %-EBADMSG - Policy is invalid
29+
* * %-ERANGE - Policy version number overflow
30+
* * %-EINVAL - Policy version parsing error
31+
* * %-EEXIST - Same name policy already deployed
32+
*/
33+
static ssize_t new_policy(struct file *f, const char __user *data,
34+
size_t len, loff_t *offset)
35+
{
36+
struct ipe_policy *p = NULL;
37+
char *copy = NULL;
38+
int rc = 0;
39+
40+
if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
41+
return -EPERM;
42+
43+
copy = memdup_user_nul(data, len);
44+
if (IS_ERR(copy))
45+
return PTR_ERR(copy);
46+
47+
p = ipe_new_policy(NULL, 0, copy, len);
48+
if (IS_ERR(p)) {
49+
rc = PTR_ERR(p);
50+
goto out;
51+
}
52+
53+
rc = ipe_new_policyfs_node(p);
54+
55+
out:
56+
if (rc < 0)
57+
ipe_free_policy(p);
58+
kfree(copy);
59+
return (rc < 0) ? rc : len;
60+
}
61+
62+
static const struct file_operations np_fops = {
63+
.write = new_policy,
64+
};
65+
66+
/**
67+
* ipe_init_securityfs() - Initialize IPE's securityfs tree at fsinit.
68+
*
69+
* Return: %0 on success. If an error occurs, the function will return
70+
* the -errno.
71+
*/
72+
static int __init ipe_init_securityfs(void)
73+
{
74+
int rc = 0;
75+
76+
if (!ipe_enabled)
77+
return -EOPNOTSUPP;
78+
79+
root = securityfs_create_dir("ipe", NULL);
80+
if (IS_ERR(root)) {
81+
rc = PTR_ERR(root);
82+
goto err;
83+
}
84+
85+
policy_root = securityfs_create_dir("policies", root);
86+
if (IS_ERR(policy_root)) {
87+
rc = PTR_ERR(policy_root);
88+
goto err;
89+
}
90+
91+
np = securityfs_create_file("new_policy", 0200, root, NULL, &np_fops);
92+
if (IS_ERR(np)) {
93+
rc = PTR_ERR(np);
94+
goto err;
95+
}
96+
97+
return 0;
98+
err:
99+
securityfs_remove(np);
100+
securityfs_remove(policy_root);
101+
securityfs_remove(root);
102+
return rc;
103+
}
104+
105+
fs_initcall(ipe_init_securityfs);

security/ipe/fs.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
4+
*/
5+
6+
#ifndef _IPE_FS_H
7+
#define _IPE_FS_H
8+
9+
#include "policy.h"
10+
11+
extern struct dentry *policy_root __ro_after_init;
12+
13+
int ipe_new_policyfs_node(struct ipe_policy *p);
14+
void ipe_del_policyfs_node(struct ipe_policy *p);
15+
16+
#endif /* _IPE_FS_H */

security/ipe/ipe.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include "eval.h"
99
#include "hooks.h"
1010

11+
bool ipe_enabled;
12+
1113
static struct lsm_blob_sizes ipe_blobs __ro_after_init = {
1214
.lbs_superblock = sizeof(struct ipe_superblock),
1315
};
@@ -45,6 +47,7 @@ static struct security_hook_list ipe_hooks[] __ro_after_init = {
4547
static int __init ipe_init(void)
4648
{
4749
security_add_hooks(ipe_hooks, ARRAY_SIZE(ipe_hooks), &ipe_lsmid);
50+
ipe_enabled = true;
4851

4952
return 0;
5053
}

security/ipe/ipe.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@
1414
#include <linux/lsm_hooks.h>
1515
struct ipe_superblock *ipe_sb(const struct super_block *sb);
1616

17+
extern bool ipe_enabled;
18+
1719
#endif /* _IPE_H */

security/ipe/policy.c

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,36 @@
77
#include <linux/verification.h>
88

99
#include "ipe.h"
10+
#include "eval.h"
11+
#include "fs.h"
1012
#include "policy.h"
1113
#include "policy_parser.h"
1214

15+
/* lock for synchronizing writers across ipe policy */
16+
DEFINE_MUTEX(ipe_policy_lock);
17+
18+
/**
19+
* ver_to_u64() - Convert an internal ipe_policy_version to a u64.
20+
* @p: Policy to extract the version from.
21+
*
22+
* Bits (LSB is index 0):
23+
* [48,32] -> Major
24+
* [32,16] -> Minor
25+
* [16, 0] -> Revision
26+
*
27+
* Return: u64 version of the embedded version structure.
28+
*/
29+
static inline u64 ver_to_u64(const struct ipe_policy *const p)
30+
{
31+
u64 r;
32+
33+
r = (((u64)p->parsed->version.major) << 32)
34+
| (((u64)p->parsed->version.minor) << 16)
35+
| ((u64)(p->parsed->version.rev));
36+
37+
return r;
38+
}
39+
1340
/**
1441
* ipe_free_policy() - Deallocate a given IPE policy.
1542
* @p: Supplies the policy to free.
@@ -21,6 +48,7 @@ void ipe_free_policy(struct ipe_policy *p)
2148
if (IS_ERR_OR_NULL(p))
2249
return;
2350

51+
ipe_del_policyfs_node(p);
2452
ipe_free_parsed_policy(p->parsed);
2553
/*
2654
* p->text is allocated only when p->pkcs7 is not NULL
@@ -43,6 +71,66 @@ static int set_pkcs7_data(void *ctx, const void *data, size_t len,
4371
return 0;
4472
}
4573

74+
/**
75+
* ipe_update_policy() - parse a new policy and replace old with it.
76+
* @root: Supplies a pointer to the securityfs inode saved the policy.
77+
* @text: Supplies a pointer to the plain text policy.
78+
* @textlen: Supplies the length of @text.
79+
* @pkcs7: Supplies a pointer to a buffer containing a pkcs7 message.
80+
* @pkcs7len: Supplies the length of @pkcs7len.
81+
*
82+
* @text/@textlen is mutually exclusive with @pkcs7/@pkcs7len - see
83+
* ipe_new_policy.
84+
*
85+
* Context: Requires root->i_rwsem to be held.
86+
* Return: %0 on success. If an error occurs, the function will return
87+
* the -errno.
88+
*/
89+
int ipe_update_policy(struct inode *root, const char *text, size_t textlen,
90+
const char *pkcs7, size_t pkcs7len)
91+
{
92+
struct ipe_policy *old, *ap, *new = NULL;
93+
int rc = 0;
94+
95+
old = (struct ipe_policy *)root->i_private;
96+
if (!old)
97+
return -ENOENT;
98+
99+
new = ipe_new_policy(text, textlen, pkcs7, pkcs7len);
100+
if (IS_ERR(new))
101+
return PTR_ERR(new);
102+
103+
if (strcmp(new->parsed->name, old->parsed->name)) {
104+
rc = -EINVAL;
105+
goto err;
106+
}
107+
108+
if (ver_to_u64(old) > ver_to_u64(new)) {
109+
rc = -EINVAL;
110+
goto err;
111+
}
112+
113+
root->i_private = new;
114+
swap(new->policyfs, old->policyfs);
115+
116+
mutex_lock(&ipe_policy_lock);
117+
ap = rcu_dereference_protected(ipe_active_policy,
118+
lockdep_is_held(&ipe_policy_lock));
119+
if (old == ap) {
120+
rcu_assign_pointer(ipe_active_policy, new);
121+
mutex_unlock(&ipe_policy_lock);
122+
} else {
123+
mutex_unlock(&ipe_policy_lock);
124+
}
125+
synchronize_rcu();
126+
ipe_free_policy(old);
127+
128+
return 0;
129+
err:
130+
ipe_free_policy(new);
131+
return rc;
132+
}
133+
46134
/**
47135
* ipe_new_policy() - Allocate and parse an ipe_policy structure.
48136
*
@@ -101,3 +189,35 @@ struct ipe_policy *ipe_new_policy(const char *text, size_t textlen,
101189
ipe_free_policy(new);
102190
return ERR_PTR(rc);
103191
}
192+
193+
/**
194+
* ipe_set_active_pol() - Make @p the active policy.
195+
* @p: Supplies a pointer to the policy to make active.
196+
*
197+
* Context: Requires root->i_rwsem, which i_private has the policy, to be held.
198+
* Return:
199+
* * %0 - Success
200+
* * %-EINVAL - New active policy version is invalid
201+
*/
202+
int ipe_set_active_pol(const struct ipe_policy *p)
203+
{
204+
struct ipe_policy *ap = NULL;
205+
206+
mutex_lock(&ipe_policy_lock);
207+
208+
ap = rcu_dereference_protected(ipe_active_policy,
209+
lockdep_is_held(&ipe_policy_lock));
210+
if (ap == p) {
211+
mutex_unlock(&ipe_policy_lock);
212+
return 0;
213+
}
214+
if (ap && ver_to_u64(ap) > ver_to_u64(p)) {
215+
mutex_unlock(&ipe_policy_lock);
216+
return -EINVAL;
217+
}
218+
219+
rcu_assign_pointer(ipe_active_policy, p);
220+
mutex_unlock(&ipe_policy_lock);
221+
222+
return 0;
223+
}

security/ipe/policy.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <linux/list.h>
99
#include <linux/types.h>
10+
#include <linux/fs.h>
1011

1112
enum ipe_op_type {
1213
IPE_OP_EXEC = 0,
@@ -76,10 +77,16 @@ struct ipe_policy {
7677
size_t textlen;
7778

7879
struct ipe_parsed_policy *parsed;
80+
81+
struct dentry *policyfs;
7982
};
8083

8184
struct ipe_policy *ipe_new_policy(const char *text, size_t textlen,
8285
const char *pkcs7, size_t pkcs7len);
8386
void ipe_free_policy(struct ipe_policy *pol);
87+
int ipe_update_policy(struct inode *root, const char *text, size_t textlen,
88+
const char *pkcs7, size_t pkcs7len);
89+
int ipe_set_active_pol(const struct ipe_policy *p);
90+
extern struct mutex ipe_policy_lock;
8491

8592
#endif /* _IPE_POLICY_H */

0 commit comments

Comments
 (0)