1 |
/* |
2 |
* fs/ccsecurity/umount.c |
3 |
* |
4 |
* Copyright (C) 2005-2009 NTT DATA CORPORATION |
5 |
* |
6 |
* Version: 1.7.0-pre 2009/07/03 |
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/slab.h> |
14 |
#include <linux/version.h> |
15 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) |
16 |
#include <linux/mount.h> |
17 |
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) |
18 |
#include <linux/namespace.h> |
19 |
#endif |
20 |
#include "internal.h" |
21 |
#include <linux/ccsecurity.h> |
22 |
|
23 |
/** |
24 |
* ccs_audit_umount_log - Audit unmount log. |
25 |
* |
26 |
* @r: Pointer to "struct ccs_request_info". |
27 |
* @dir: Mount point. |
28 |
* @is_granted: True if this is a granted log. |
29 |
* |
30 |
* Returns 0 on success, negative value otherwise. |
31 |
*/ |
32 |
static int ccs_audit_umount_log(struct ccs_request_info *r, |
33 |
const char *dir, const bool is_granted) |
34 |
{ |
35 |
if (!is_granted && ccs_verbose_mode(r->domain)) |
36 |
printk(KERN_WARNING "SAKURA-%s: umount %s denied for %s\n", |
37 |
ccs_get_msg(r->mode == 3), dir, |
38 |
ccs_get_last_name(r->domain)); |
39 |
return ccs_write_audit_log(is_granted, r, KEYWORD_ALLOW_UNMOUNT |
40 |
"%s\n", dir); |
41 |
} |
42 |
|
43 |
/** |
44 |
* ccs_update_umount_acl - Update "struct ccs_umount_acl_record" list. |
45 |
* |
46 |
* @dir: The name of directrory. |
47 |
* @is_delete: True if it is a delete request. |
48 |
* |
49 |
* Returns 0 on success, negative value otherwise. |
50 |
*/ |
51 |
static int ccs_update_umount_acl(const char *dir, |
52 |
struct ccs_domain_info *domain, |
53 |
struct ccs_condition *condition, |
54 |
const bool is_delete) |
55 |
{ |
56 |
struct ccs_umount_acl_record *entry = NULL; |
57 |
struct ccs_acl_info *ptr; |
58 |
const struct ccs_path_info *saved_dir; |
59 |
int error = is_delete ? -ENOENT : -ENOMEM; |
60 |
if (!ccs_is_correct_path(dir, 1, 0, 1)) |
61 |
return -EINVAL; |
62 |
saved_dir = ccs_get_name(dir); |
63 |
if (!saved_dir) |
64 |
return -ENOMEM; |
65 |
if (!is_delete) |
66 |
entry = kzalloc(sizeof(*entry), GFP_KERNEL); |
67 |
mutex_lock(&ccs_policy_lock); |
68 |
list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { |
69 |
struct ccs_umount_acl_record *acl; |
70 |
if (ccs_acl_type1(ptr) != TYPE_UMOUNT_ACL) |
71 |
continue; |
72 |
if (ptr->cond != condition) |
73 |
continue; |
74 |
acl = container_of(ptr, struct ccs_umount_acl_record, head); |
75 |
if (acl->dir != saved_dir) |
76 |
continue; |
77 |
if (is_delete) |
78 |
error = ccs_del_domain_acl(ptr); |
79 |
else |
80 |
error = ccs_add_domain_acl(NULL, ptr); |
81 |
break; |
82 |
} |
83 |
if (!is_delete && error && ccs_memory_ok(entry, sizeof(*entry))) { |
84 |
entry->head.type = TYPE_UMOUNT_ACL; |
85 |
entry->head.cond = condition; |
86 |
entry->dir = saved_dir; |
87 |
saved_dir = NULL; |
88 |
error = ccs_add_domain_acl(domain, &entry->head); |
89 |
entry = NULL; |
90 |
} |
91 |
mutex_unlock(&ccs_policy_lock); |
92 |
ccs_put_name(saved_dir); |
93 |
kfree(entry); |
94 |
return error; |
95 |
} |
96 |
|
97 |
/** |
98 |
* ccs_may_umount2 - Check permission for unmount. |
99 |
* |
100 |
* @mnt: Pointer to "struct vfsmount". |
101 |
* |
102 |
* Returns 0 on success, negative value otherwise. |
103 |
* |
104 |
* Caller holds ccs_read_lock(). |
105 |
*/ |
106 |
static int ccs_may_umount2(struct vfsmount *mnt) |
107 |
{ |
108 |
struct ccs_request_info r; |
109 |
int error; |
110 |
const char *dir0; |
111 |
bool is_enforce; |
112 |
struct ccs_acl_info *ptr; |
113 |
struct ccs_path_info dir; |
114 |
ccs_check_read_lock(); |
115 |
if (!ccs_can_sleep()) |
116 |
return 0; |
117 |
ccs_init_request_info(&r, NULL, CCS_MAC_FOR_NAMESPACE); |
118 |
is_enforce = (r.mode == 3); |
119 |
if (!r.mode) |
120 |
return 0; |
121 |
retry: |
122 |
error = -EPERM; |
123 |
dir0 = ccs_realpath_from_dentry(mnt->mnt_root, mnt); |
124 |
if (!dir0) |
125 |
goto out; |
126 |
dir.name = dir0; |
127 |
ccs_fill_path_info(&dir); |
128 |
list_for_each_entry_rcu(ptr, &r.domain->acl_info_list, list) { |
129 |
struct ccs_umount_acl_record *acl; |
130 |
if (ccs_acl_type2(ptr) != TYPE_UMOUNT_ACL) |
131 |
continue; |
132 |
acl = container_of(ptr, struct ccs_umount_acl_record, head); |
133 |
if (!ccs_path_matches_pattern(&dir, acl->dir) || |
134 |
!ccs_check_condition(&r, ptr)) |
135 |
continue; |
136 |
r.cond = ptr->cond; |
137 |
error = 0; |
138 |
break; |
139 |
} |
140 |
ccs_audit_umount_log(&r, dir0, !error); |
141 |
if (!error) |
142 |
goto out; |
143 |
if (is_enforce) |
144 |
error = ccs_check_supervisor(&r, KEYWORD_ALLOW_UNMOUNT "%s", |
145 |
dir0); |
146 |
else if (ccs_domain_quota_ok(&r)) |
147 |
ccs_update_umount_acl(dir0, r.domain, NULL, false); |
148 |
out: |
149 |
kfree(dir0); |
150 |
if (!is_enforce) |
151 |
error = 0; |
152 |
if (error == 1) |
153 |
goto retry; |
154 |
return error; |
155 |
} |
156 |
|
157 |
/** |
158 |
* ccs_may_umount - Check permission for unmount. |
159 |
* |
160 |
* @mnt: Pointer to "struct vfsmount". |
161 |
* |
162 |
* Returns 0 on success, negative value otherwise. |
163 |
*/ |
164 |
int ccs_may_umount(struct vfsmount *mnt) |
165 |
{ |
166 |
const int idx = ccs_read_lock(); |
167 |
const int error = ccs_may_umount2(mnt); |
168 |
ccs_read_unlock(idx); |
169 |
return error; |
170 |
} |
171 |
|
172 |
/** |
173 |
* ccs_write_umount_policy - Write "struct ccs_umount_acl_record" list. |
174 |
* |
175 |
* @data: String to parse. |
176 |
* @domain: Pointer to "struct ccs_domain_info". |
177 |
* @condition: Pointer to "struct ccs_condition". May be NULL. |
178 |
* @is_delete: True if it is a delete request. |
179 |
* |
180 |
* Returns 0 on sucess, negative value otherwise. |
181 |
*/ |
182 |
int ccs_write_umount_policy(char *data, struct ccs_domain_info *domain, |
183 |
struct ccs_condition *condition, |
184 |
const bool is_delete) |
185 |
{ |
186 |
return ccs_update_umount_acl(data, domain, condition, is_delete); |
187 |
} |