]>
Commit | Line | Data |
---|---|---|
cdff2642 JJ |
1 | /* |
2 | * AppArmor security module | |
3 | * | |
4 | * This file contains basic common functions used in AppArmor | |
5 | * | |
6 | * Copyright (C) 1998-2008 Novell/SUSE | |
7 | * Copyright 2009-2010 Canonical Ltd. | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU General Public License as | |
11 | * published by the Free Software Foundation, version 2 of the | |
12 | * License. | |
13 | */ | |
14 | ||
3b0aaf58 | 15 | #include <linux/ctype.h> |
b7f080cf | 16 | #include <linux/mm.h> |
cdff2642 JJ |
17 | #include <linux/slab.h> |
18 | #include <linux/string.h> | |
19 | #include <linux/vmalloc.h> | |
20 | ||
21 | #include "include/audit.h" | |
32c3df63 | 22 | #include "include/apparmor.h" |
12557dcb | 23 | #include "include/lib.h" |
fc7e0b26 | 24 | #include "include/perms.h" |
fe6bb31f | 25 | #include "include/policy.h" |
cdff2642 | 26 | |
aa9aeea8 JJ |
27 | struct aa_perms allperms = { .allow = ALL_PERMS_MASK, |
28 | .quiet = ALL_PERMS_MASK, | |
29 | .hide = ALL_PERMS_MASK }; | |
30 | ||
cdff2642 JJ |
31 | /** |
32 | * aa_split_fqname - split a fqname into a profile and namespace name | |
33 | * @fqname: a full qualified name in namespace profile format (NOT NULL) | |
34 | * @ns_name: pointer to portion of the string containing the ns name (NOT NULL) | |
35 | * | |
36 | * Returns: profile name or NULL if one is not specified | |
37 | * | |
38 | * Split a namespace name from a profile name (see policy.c for naming | |
39 | * description). If a portion of the name is missing it returns NULL for | |
40 | * that portion. | |
41 | * | |
42 | * NOTE: may modify the @fqname string. The pointers returned point | |
43 | * into the @fqname string. | |
44 | */ | |
45 | char *aa_split_fqname(char *fqname, char **ns_name) | |
46 | { | |
47 | char *name = strim(fqname); | |
48 | ||
49 | *ns_name = NULL; | |
50 | if (name[0] == ':') { | |
51 | char *split = strchr(&name[1], ':'); | |
04ccd53f | 52 | *ns_name = skip_spaces(&name[1]); |
cdff2642 JJ |
53 | if (split) { |
54 | /* overwrite ':' with \0 */ | |
2654bfbc JJ |
55 | *split++ = 0; |
56 | if (strncmp(split, "//", 2) == 0) | |
57 | split += 2; | |
58 | name = skip_spaces(split); | |
cdff2642 JJ |
59 | } else |
60 | /* a ns name without a following profile is allowed */ | |
61 | name = NULL; | |
cdff2642 JJ |
62 | } |
63 | if (name && *name == 0) | |
64 | name = NULL; | |
65 | ||
66 | return name; | |
67 | } | |
68 | ||
3b0aaf58 JJ |
69 | /** |
70 | * skipn_spaces - Removes leading whitespace from @str. | |
71 | * @str: The string to be stripped. | |
72 | * | |
73 | * Returns a pointer to the first non-whitespace character in @str. | |
74 | * if all whitespace will return NULL | |
75 | */ | |
76 | ||
b91deb9d | 77 | const char *skipn_spaces(const char *str, size_t n) |
3b0aaf58 JJ |
78 | { |
79 | for (; n && isspace(*str); --n) | |
80 | ++str; | |
81 | if (n) | |
82 | return (char *)str; | |
83 | return NULL; | |
84 | } | |
85 | ||
86 | const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, | |
87 | size_t *ns_len) | |
88 | { | |
89 | const char *end = fqname + n; | |
90 | const char *name = skipn_spaces(fqname, n); | |
91 | ||
92 | if (!name) | |
93 | return NULL; | |
94 | *ns_name = NULL; | |
95 | *ns_len = 0; | |
96 | if (name[0] == ':') { | |
97 | char *split = strnchr(&name[1], end - &name[1], ':'); | |
98 | *ns_name = skipn_spaces(&name[1], end - &name[1]); | |
99 | if (!*ns_name) | |
100 | return NULL; | |
101 | if (split) { | |
102 | *ns_len = split - *ns_name; | |
103 | if (*ns_len == 0) | |
104 | *ns_name = NULL; | |
105 | split++; | |
106 | if (end - split > 1 && strncmp(split, "//", 2) == 0) | |
107 | split += 2; | |
108 | name = skipn_spaces(split, end - split); | |
109 | } else { | |
110 | /* a ns name without a following profile is allowed */ | |
111 | name = NULL; | |
112 | *ns_len = end - *ns_name; | |
113 | } | |
114 | } | |
115 | if (name && *name == 0) | |
116 | name = NULL; | |
117 | ||
118 | return name; | |
119 | } | |
120 | ||
cdff2642 JJ |
121 | /** |
122 | * aa_info_message - log a none profile related status message | |
123 | * @str: message to log | |
124 | */ | |
125 | void aa_info_message(const char *str) | |
126 | { | |
127 | if (audit_enabled) { | |
ef88a7ac JJ |
128 | DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL); |
129 | ||
130 | aad(&sa)->info = str; | |
cdff2642 JJ |
131 | aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); |
132 | } | |
133 | printk(KERN_INFO "AppArmor: %s\n", str); | |
134 | } | |
135 | ||
e53cfe6c JJ |
136 | const char aa_file_perm_chrs[] = "xwracd km l "; |
137 | const char *aa_file_perm_names[] = { | |
138 | "exec", | |
139 | "write", | |
140 | "read", | |
141 | "append", | |
142 | ||
143 | "create", | |
144 | "delete", | |
145 | "open", | |
146 | "rename", | |
147 | ||
148 | "setattr", | |
149 | "getattr", | |
150 | "setcred", | |
151 | "getcred", | |
152 | ||
153 | "chmod", | |
154 | "chown", | |
155 | "chgrp", | |
156 | "lock", | |
157 | ||
158 | "mmap", | |
159 | "mprot", | |
160 | "link", | |
161 | "snapshot", | |
162 | ||
163 | "unknown", | |
164 | "unknown", | |
165 | "unknown", | |
166 | "unknown", | |
167 | ||
168 | "unknown", | |
169 | "unknown", | |
170 | "unknown", | |
171 | "unknown", | |
172 | ||
173 | "stack", | |
174 | "change_onexec", | |
175 | "change_profile", | |
176 | "change_hat", | |
177 | }; | |
178 | ||
179 | /** | |
180 | * aa_perm_mask_to_str - convert a perm mask to its short string | |
181 | * @str: character buffer to store string in (at least 10 characters) | |
182 | * @mask: permission mask to convert | |
183 | */ | |
184 | void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask) | |
185 | { | |
186 | unsigned int i, perm = 1; | |
187 | ||
188 | for (i = 0; i < 32; perm <<= 1, i++) { | |
189 | if (mask & perm) | |
190 | *str++ = chrs[i]; | |
191 | } | |
192 | *str = '\0'; | |
193 | } | |
194 | ||
aa9aeea8 JJ |
195 | void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask) |
196 | { | |
197 | const char *fmt = "%s"; | |
198 | unsigned int i, perm = 1; | |
199 | bool prev = false; | |
200 | ||
201 | for (i = 0; i < 32; perm <<= 1, i++) { | |
202 | if (mask & perm) { | |
203 | audit_log_format(ab, fmt, names[i]); | |
204 | if (!prev) { | |
205 | prev = true; | |
206 | fmt = " %s"; | |
207 | } | |
208 | } | |
209 | } | |
210 | } | |
211 | ||
212 | void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, | |
213 | u32 chrsmask, const char **names, u32 namesmask) | |
214 | { | |
215 | char str[33]; | |
216 | ||
217 | audit_log_format(ab, "\""); | |
218 | if ((mask & chrsmask) && chrs) { | |
219 | aa_perm_mask_to_str(str, chrs, mask & chrsmask); | |
220 | mask &= ~chrsmask; | |
221 | audit_log_format(ab, "%s", str); | |
222 | if (mask & namesmask) | |
223 | audit_log_format(ab, " "); | |
224 | } | |
225 | if ((mask & namesmask) && names) | |
226 | aa_audit_perm_names(ab, names, mask & namesmask); | |
227 | audit_log_format(ab, "\""); | |
228 | } | |
229 | ||
230 | /** | |
231 | * aa_apply_modes_to_perms - apply namespace and profile flags to perms | |
232 | * @profile: that perms where computed from | |
233 | * @perms: perms to apply mode modifiers to | |
234 | * | |
235 | * TODO: split into profile and ns based flags for when accumulating perms | |
236 | */ | |
237 | void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms) | |
238 | { | |
239 | switch (AUDIT_MODE(profile)) { | |
240 | case AUDIT_ALL: | |
241 | perms->audit = ALL_PERMS_MASK; | |
242 | /* fall through */ | |
243 | case AUDIT_NOQUIET: | |
244 | perms->quiet = 0; | |
245 | break; | |
246 | case AUDIT_QUIET: | |
247 | perms->audit = 0; | |
248 | /* fall through */ | |
249 | case AUDIT_QUIET_DENIED: | |
250 | perms->quiet = ALL_PERMS_MASK; | |
251 | break; | |
252 | } | |
253 | ||
254 | if (KILL_MODE(profile)) | |
255 | perms->kill = ALL_PERMS_MASK; | |
256 | else if (COMPLAIN_MODE(profile)) | |
257 | perms->complain = ALL_PERMS_MASK; | |
258 | /* | |
259 | * TODO: | |
260 | * else if (PROMPT_MODE(profile)) | |
261 | * perms->prompt = ALL_PERMS_MASK; | |
262 | */ | |
263 | } | |
264 | ||
265 | static u32 map_other(u32 x) | |
266 | { | |
267 | return ((x & 0x3) << 8) | /* SETATTR/GETATTR */ | |
268 | ((x & 0x1c) << 18) | /* ACCEPT/BIND/LISTEN */ | |
269 | ((x & 0x60) << 19); /* SETOPT/GETOPT */ | |
270 | } | |
271 | ||
272 | void aa_compute_perms(struct aa_dfa *dfa, unsigned int state, | |
273 | struct aa_perms *perms) | |
274 | { | |
275 | perms->deny = 0; | |
276 | perms->kill = perms->stop = 0; | |
277 | perms->complain = perms->cond = 0; | |
278 | perms->hide = 0; | |
279 | perms->prompt = 0; | |
280 | perms->allow = dfa_user_allow(dfa, state); | |
281 | perms->audit = dfa_user_audit(dfa, state); | |
282 | perms->quiet = dfa_user_quiet(dfa, state); | |
283 | ||
284 | /* for v5 perm mapping in the policydb, the other set is used | |
285 | * to extend the general perm set | |
286 | */ | |
287 | perms->allow |= map_other(dfa_other_allow(dfa, state)); | |
288 | perms->audit |= map_other(dfa_other_audit(dfa, state)); | |
289 | perms->quiet |= map_other(dfa_other_quiet(dfa, state)); | |
290 | // perms->xindex = dfa_user_xindex(dfa, state); | |
291 | } | |
292 | ||
fe6bb31f JJ |
293 | /** |
294 | * aa_policy_init - initialize a policy structure | |
295 | * @policy: policy to initialize (NOT NULL) | |
296 | * @prefix: prefix name if any is required. (MAYBE NULL) | |
297 | * @name: name of the policy, init will make a copy of it (NOT NULL) | |
298 | * | |
299 | * Note: this fn creates a copy of strings passed in | |
300 | * | |
301 | * Returns: true if policy init successful | |
302 | */ | |
303 | bool aa_policy_init(struct aa_policy *policy, const char *prefix, | |
d102d895 | 304 | const char *name, gfp_t gfp) |
fe6bb31f JJ |
305 | { |
306 | /* freed by policy_free */ | |
307 | if (prefix) { | |
308 | policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, | |
d102d895 | 309 | gfp); |
fe6bb31f | 310 | if (policy->hname) |
bbe4a7c8 | 311 | sprintf((char *)policy->hname, "%s//%s", prefix, name); |
fe6bb31f | 312 | } else |
d102d895 | 313 | policy->hname = kstrdup(name, gfp); |
fe6bb31f | 314 | if (!policy->hname) |
b9c42ac7 | 315 | return false; |
fe6bb31f | 316 | /* base.name is a substring of fqname */ |
d102d895 | 317 | policy->name = basename(policy->hname); |
fe6bb31f JJ |
318 | INIT_LIST_HEAD(&policy->list); |
319 | INIT_LIST_HEAD(&policy->profiles); | |
320 | ||
b9c42ac7 | 321 | return true; |
fe6bb31f JJ |
322 | } |
323 | ||
324 | /** | |
325 | * aa_policy_destroy - free the elements referenced by @policy | |
326 | * @policy: policy that is to have its elements freed (NOT NULL) | |
327 | */ | |
328 | void aa_policy_destroy(struct aa_policy *policy) | |
329 | { | |
5fd1b95f JJ |
330 | AA_BUG(on_list_rcu(&policy->profiles)); |
331 | AA_BUG(on_list_rcu(&policy->list)); | |
fe6bb31f JJ |
332 | |
333 | /* don't free name as its a subset of hname */ | |
334 | kzfree(policy->hname); | |
335 | } |