1 |
/* |
2 |
* security/ccsecurity/chroot.c |
3 |
* |
4 |
* Copyright (C) 2005-2009 NTT DATA CORPORATION |
5 |
* |
6 |
* Version: 1.7.0-rc 2009/09/01 |
7 |
* |
8 |
* This file is applicable to both 2.4.30 and 2.6.11 and later. |
9 |
* See README.ccs for ChangeLog. |
10 |
* |
11 |
*/ |
12 |
|
13 |
#include <linux/version.h> |
14 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) |
15 |
#include <linux/dcache.h> |
16 |
#include <linux/namei.h> |
17 |
#else |
18 |
#include <linux/fs.h> |
19 |
#endif |
20 |
#include "internal.h" |
21 |
|
22 |
/** |
23 |
* ccs_audit_chroot_log - Audit chroot log. |
24 |
* |
25 |
* @r: Pointer to "struct ccs_request_info". |
26 |
* @root: New root directory. |
27 |
* @is_granted: True if this is a granted log. |
28 |
* |
29 |
* Returns 0 on success, negative value otherwise. |
30 |
*/ |
31 |
static int ccs_audit_chroot_log(struct ccs_request_info *r, |
32 |
const char *root, const bool is_granted) |
33 |
{ |
34 |
if (!is_granted) |
35 |
ccs_warn_log(r, "chroot %s", root); |
36 |
return ccs_write_audit_log(is_granted, r, CCS_KEYWORD_ALLOW_CHROOT |
37 |
"%s\n", root); |
38 |
} |
39 |
|
40 |
/** |
41 |
* ccs_chroot_acl - Check permission for chroot(). |
42 |
* |
43 |
* @path: Pointer to "struct path". |
44 |
* |
45 |
* Returns 0 on success, negative value otherwise. |
46 |
* |
47 |
* Caller holds ccs_read_lock(). |
48 |
*/ |
49 |
static int ccs_chroot_acl(struct path *path) |
50 |
{ |
51 |
struct ccs_request_info r; |
52 |
int error; |
53 |
struct ccs_path_info dir; |
54 |
char *root_name; |
55 |
struct ccs_obj_info obj = { |
56 |
.path1 = *path |
57 |
}; |
58 |
if (ccs_init_request_info(&r, NULL, CCS_MAC_FILE_CHROOT) |
59 |
== CCS_CONFIG_DISABLED) |
60 |
return 0; |
61 |
r.obj = &obj; |
62 |
error = -ENOMEM; |
63 |
root_name = ccs_realpath_from_path(path); |
64 |
if (!root_name) |
65 |
goto out; |
66 |
dir.name = root_name; |
67 |
ccs_fill_path_info(&dir); |
68 |
if (!dir.is_dir) |
69 |
goto out; |
70 |
do { |
71 |
struct ccs_acl_info *ptr; |
72 |
error = -EPERM; |
73 |
list_for_each_entry_rcu(ptr, &r.domain->acl_info_list, list) { |
74 |
struct ccs_chroot_acl *acl; |
75 |
if (ptr->is_deleted || |
76 |
ptr->type != CCS_TYPE_CHROOT_ACL) |
77 |
continue; |
78 |
acl = container_of(ptr, struct ccs_chroot_acl, |
79 |
head); |
80 |
if (!ccs_compare_name_union(&dir, &acl->dir) || |
81 |
!ccs_condition(&r, ptr)) |
82 |
continue; |
83 |
r.cond = ptr->cond; |
84 |
error = 0; |
85 |
break; |
86 |
} |
87 |
ccs_audit_chroot_log(&r, root_name, !error); |
88 |
if (!error) |
89 |
break; |
90 |
error = ccs_supervisor(&r, CCS_KEYWORD_ALLOW_CHROOT |
91 |
"%s\n", ccs_file_pattern(&dir)); |
92 |
} while (error == 1); |
93 |
out: |
94 |
kfree(root_name); |
95 |
if (r.mode != CCS_CONFIG_ENFORCING) |
96 |
error = 0; |
97 |
return error; |
98 |
} |
99 |
|
100 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) |
101 |
#define PATH_or_NAMEIDATA path |
102 |
#else |
103 |
#define PATH_or_NAMEIDATA nameidata |
104 |
#endif |
105 |
|
106 |
/** |
107 |
* ccs_chroot_permission - Check permission for chroot(). |
108 |
* |
109 |
* @path: Pointer to "struct path" (for 2.6.27 and later). |
110 |
* Pointer to "struct nameidata" (for 2.6.26 and earlier). |
111 |
* |
112 |
* Returns 0 on success, negative value otherwise. |
113 |
*/ |
114 |
int ccs_chroot_permission(struct PATH_or_NAMEIDATA *path) |
115 |
{ |
116 |
#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 25) || LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 26) |
117 |
struct path tmp_path = { path->path.mnt, path->path.dentry }; |
118 |
#else |
119 |
struct path tmp_path = { path->mnt, path->dentry }; |
120 |
#endif |
121 |
const int idx = ccs_read_lock(); |
122 |
const int error = ccs_chroot_acl(&tmp_path); |
123 |
ccs_read_unlock(idx); |
124 |
return error; |
125 |
} |
126 |
|
127 |
/** |
128 |
* ccs_write_chroot_policy - Write "struct ccs_chroot_acl" list. |
129 |
* |
130 |
* @data: String to parse. |
131 |
* @domain: Pointer to "struct ccs_domain_info". |
132 |
* @condition: Pointer to "struct ccs_condition". May be NULL. |
133 |
* @is_delete: True if it is a delete request. |
134 |
* |
135 |
* Returns 0 on success, negative value otherwise. |
136 |
*/ |
137 |
int ccs_write_chroot_policy(char *data, struct ccs_domain_info *domain, |
138 |
struct ccs_condition *condition, |
139 |
const bool is_delete) |
140 |
{ |
141 |
struct ccs_chroot_acl *entry = NULL; |
142 |
struct ccs_acl_info *ptr; |
143 |
struct ccs_chroot_acl e = { |
144 |
.head.type = CCS_TYPE_CHROOT_ACL, |
145 |
.head.cond = condition |
146 |
}; |
147 |
int error = is_delete ? -ENOENT : -ENOMEM; |
148 |
if (data[0] != '@' && !ccs_is_correct_path(data, 1, 0, 1)) |
149 |
return -EINVAL; |
150 |
if (!ccs_parse_name_union(data, &e.dir)) |
151 |
return error; |
152 |
if (!is_delete) |
153 |
entry = kmalloc(sizeof(e), GFP_KERNEL); |
154 |
mutex_lock(&ccs_policy_lock); |
155 |
list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { |
156 |
struct ccs_chroot_acl *acl = |
157 |
container_of(ptr, struct ccs_chroot_acl, head); |
158 |
if (ptr->type != CCS_TYPE_CHROOT_ACL || ptr->cond != condition |
159 |
|| memcmp(&acl->dir, &e.dir, sizeof(e.dir))) |
160 |
continue; |
161 |
ptr->is_deleted = is_delete; |
162 |
error = 0; |
163 |
break; |
164 |
} |
165 |
if (!is_delete && error && ccs_commit_ok(entry, &e, sizeof(e))) { |
166 |
ccs_add_domain_acl(domain, &entry->head); |
167 |
entry = NULL; |
168 |
error = 0; |
169 |
} |
170 |
mutex_unlock(&ccs_policy_lock); |
171 |
ccs_put_name_union(&e.dir); |
172 |
kfree(entry); |
173 |
return error; |
174 |
} |