Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
3 : : *
4 : : * This program is free software; you can redistribute it and/or modify
5 : : * it under the terms of the GNU General Public License as published by
6 : : * the Free Software Foundation, version 2.
7 : : *
8 : : * Author:
9 : : * Casey Schaufler <casey@schaufler-ca.com>
10 : : *
11 : : */
12 : :
13 : : #include <linux/types.h>
14 : : #include <linux/slab.h>
15 : : #include <linux/fs.h>
16 : : #include <linux/sched.h>
17 : : #include "smack.h"
18 : :
19 : : struct smack_known smack_known_huh = {
20 : : .smk_known = "?",
21 : : .smk_secid = 2,
22 : : };
23 : :
24 : : struct smack_known smack_known_hat = {
25 : : .smk_known = "^",
26 : : .smk_secid = 3,
27 : : };
28 : :
29 : : struct smack_known smack_known_star = {
30 : : .smk_known = "*",
31 : : .smk_secid = 4,
32 : : };
33 : :
34 : : struct smack_known smack_known_floor = {
35 : : .smk_known = "_",
36 : : .smk_secid = 5,
37 : : };
38 : :
39 : : struct smack_known smack_known_invalid = {
40 : : .smk_known = "",
41 : : .smk_secid = 6,
42 : : };
43 : :
44 : : struct smack_known smack_known_web = {
45 : : .smk_known = "@",
46 : : .smk_secid = 7,
47 : : };
48 : :
49 : : LIST_HEAD(smack_known_list);
50 : :
51 : : /*
52 : : * The initial value needs to be bigger than any of the
53 : : * known values above.
54 : : */
55 : : static u32 smack_next_secid = 10;
56 : :
57 : : /*
58 : : * what events do we log
59 : : * can be overwritten at run-time by /smack/logging
60 : : */
61 : : int log_policy = SMACK_AUDIT_DENIED;
62 : :
63 : : /**
64 : : * smk_access_entry - look up matching access rule
65 : : * @subject_label: a pointer to the subject's Smack label
66 : : * @object_label: a pointer to the object's Smack label
67 : : * @rule_list: the list of rules to search
68 : : *
69 : : * This function looks up the subject/object pair in the
70 : : * access rule list and returns the access mode. If no
71 : : * entry is found returns -ENOENT.
72 : : *
73 : : * NOTE:
74 : : *
75 : : * Earlier versions of this function allowed for labels that
76 : : * were not on the label list. This was done to allow for
77 : : * labels to come over the network that had never been seen
78 : : * before on this host. Unless the receiving socket has the
79 : : * star label this will always result in a failure check. The
80 : : * star labeled socket case is now handled in the networking
81 : : * hooks so there is no case where the label is not on the
82 : : * label list. Checking to see if the address of two labels
83 : : * is the same is now a reliable test.
84 : : *
85 : : * Do the object check first because that is more
86 : : * likely to differ.
87 : : *
88 : : * Allowing write access implies allowing locking.
89 : : */
90 : 0 : int smk_access_entry(char *subject_label, char *object_label,
91 : : struct list_head *rule_list)
92 : : {
93 : : int may = -ENOENT;
94 : : struct smack_rule *srp;
95 : :
96 [ # # ]: 0 : list_for_each_entry_rcu(srp, rule_list, list) {
97 [ # # ][ # # ]: 0 : if (srp->smk_object == object_label &&
98 : 0 : srp->smk_subject->smk_known == subject_label) {
99 : 0 : may = srp->smk_access;
100 : 0 : break;
101 : : }
102 : : }
103 : :
104 : : /*
105 : : * MAY_WRITE implies MAY_LOCK.
106 : : */
107 [ # # ]: 0 : if ((may & MAY_WRITE) == MAY_WRITE)
108 : 0 : may |= MAY_LOCK;
109 : 0 : return may;
110 : : }
111 : :
112 : : /**
113 : : * smk_access - determine if a subject has a specific access to an object
114 : : * @subject_known: a pointer to the subject's Smack label entry
115 : : * @object_label: a pointer to the object's Smack label
116 : : * @request: the access requested, in "MAY" format
117 : : * @a : a pointer to the audit data
118 : : *
119 : : * This function looks up the subject/object pair in the
120 : : * access rule list and returns 0 if the access is permitted,
121 : : * non zero otherwise.
122 : : *
123 : : * Smack labels are shared on smack_list
124 : : */
125 : 0 : int smk_access(struct smack_known *subject_known, char *object_label,
126 : : int request, struct smk_audit_info *a)
127 : : {
128 : : int may = MAY_NOT;
129 : : int rc = 0;
130 : :
131 : : /*
132 : : * Hardcoded comparisons.
133 : : *
134 : : * A star subject can't access any object.
135 : : */
136 [ # # ]: 0 : if (subject_known == &smack_known_star) {
137 : : rc = -EACCES;
138 : : goto out_audit;
139 : : }
140 : : /*
141 : : * An internet object can be accessed by any subject.
142 : : * Tasks cannot be assigned the internet label.
143 : : * An internet subject can access any object.
144 : : */
145 [ # # ][ # # ]: 0 : if (object_label == smack_known_web.smk_known ||
146 : : subject_known == &smack_known_web)
147 : : goto out_audit;
148 : : /*
149 : : * A star object can be accessed by any subject.
150 : : */
151 [ # # ]: 0 : if (object_label == smack_known_star.smk_known)
152 : : goto out_audit;
153 : : /*
154 : : * An object can be accessed in any way by a subject
155 : : * with the same label.
156 : : */
157 [ # # ]: 0 : if (subject_known->smk_known == object_label)
158 : : goto out_audit;
159 : : /*
160 : : * A hat subject can read any object.
161 : : * A floor object can be read by any subject.
162 : : */
163 [ # # ]: 0 : if ((request & MAY_ANYREAD) == request) {
164 [ # # ]: 0 : if (object_label == smack_known_floor.smk_known)
165 : : goto out_audit;
166 [ # # ]: 0 : if (subject_known == &smack_known_hat)
167 : : goto out_audit;
168 : : }
169 : : /*
170 : : * Beyond here an explicit relationship is required.
171 : : * If the requested access is contained in the available
172 : : * access (e.g. read is included in readwrite) it's
173 : : * good. A negative response from smk_access_entry()
174 : : * indicates there is no entry for this pair.
175 : : */
176 : : rcu_read_lock();
177 : 0 : may = smk_access_entry(subject_known->smk_known, object_label,
178 : : &subject_known->smk_rules);
179 : : rcu_read_unlock();
180 : :
181 [ # # ][ # # ]: 0 : if (may > 0 && (request & may) == request)
182 : : goto out_audit;
183 : :
184 : : rc = -EACCES;
185 : : out_audit:
186 : : #ifdef CONFIG_AUDIT
187 [ # # ]: 0 : if (a)
188 : 0 : smack_log(subject_known->smk_known, object_label, request,
189 : : rc, a);
190 : : #endif
191 : 0 : return rc;
192 : : }
193 : :
194 : : /**
195 : : * smk_curacc - determine if current has a specific access to an object
196 : : * @obj_label: a pointer to the object's Smack label
197 : : * @mode: the access requested, in "MAY" format
198 : : * @a : common audit data
199 : : *
200 : : * This function checks the current subject label/object label pair
201 : : * in the access rule list and returns 0 if the access is permitted,
202 : : * non zero otherwise. It allows that current may have the capability
203 : : * to override the rules.
204 : : */
205 : 0 : int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a)
206 : : {
207 : 0 : struct task_smack *tsp = current_security();
208 : : struct smack_known *skp = smk_of_task(tsp);
209 : : int may;
210 : : int rc;
211 : :
212 : : /*
213 : : * Check the global rule list
214 : : */
215 : 0 : rc = smk_access(skp, obj_label, mode, NULL);
216 [ # # ]: 0 : if (rc == 0) {
217 : : /*
218 : : * If there is an entry in the task's rule list
219 : : * it can further restrict access.
220 : : */
221 : 0 : may = smk_access_entry(skp->smk_known, obj_label,
222 : : &tsp->smk_rules);
223 [ # # ]: 0 : if (may < 0)
224 : : goto out_audit;
225 [ # # ]: 0 : if ((mode & may) == mode)
226 : : goto out_audit;
227 : : rc = -EACCES;
228 : : }
229 : :
230 : : /*
231 : : * Allow for priviliged to override policy.
232 : : */
233 [ # # ][ # # ]: 0 : if (rc != 0 && smack_privileged(CAP_MAC_OVERRIDE))
234 : : rc = 0;
235 : :
236 : : out_audit:
237 : : #ifdef CONFIG_AUDIT
238 [ # # ]: 0 : if (a)
239 : 0 : smack_log(skp->smk_known, obj_label, mode, rc, a);
240 : : #endif
241 : 0 : return rc;
242 : : }
243 : :
244 : : #ifdef CONFIG_AUDIT
245 : : /**
246 : : * smack_str_from_perm : helper to transalate an int to a
247 : : * readable string
248 : : * @string : the string to fill
249 : : * @access : the int
250 : : *
251 : : */
252 : : static inline void smack_str_from_perm(char *string, int access)
253 : : {
254 : : int i = 0;
255 : :
256 [ # # ]: 0 : if (access & MAY_READ)
257 : 0 : string[i++] = 'r';
258 [ # # ]: 0 : if (access & MAY_WRITE)
259 : 0 : string[i++] = 'w';
260 [ # # ]: 0 : if (access & MAY_EXEC)
261 : 0 : string[i++] = 'x';
262 [ # # ]: 0 : if (access & MAY_APPEND)
263 : 0 : string[i++] = 'a';
264 [ # # ]: 0 : if (access & MAY_TRANSMUTE)
265 : 0 : string[i++] = 't';
266 [ # # ]: 0 : if (access & MAY_LOCK)
267 : 0 : string[i++] = 'l';
268 : 0 : string[i] = '\0';
269 : : }
270 : : /**
271 : : * smack_log_callback - SMACK specific information
272 : : * will be called by generic audit code
273 : : * @ab : the audit_buffer
274 : : * @a : audit_data
275 : : *
276 : : */
277 : 0 : static void smack_log_callback(struct audit_buffer *ab, void *a)
278 : : {
279 : : struct common_audit_data *ad = a;
280 : 0 : struct smack_audit_data *sad = ad->smack_audit_data;
281 [ # # ]: 0 : audit_log_format(ab, "lsm=SMACK fn=%s action=%s",
282 : : ad->smack_audit_data->function,
283 : 0 : sad->result ? "denied" : "granted");
284 : 0 : audit_log_format(ab, " subject=");
285 : 0 : audit_log_untrustedstring(ab, sad->subject);
286 : 0 : audit_log_format(ab, " object=");
287 : 0 : audit_log_untrustedstring(ab, sad->object);
288 : 0 : audit_log_format(ab, " requested=%s", sad->request);
289 : 0 : }
290 : :
291 : : /**
292 : : * smack_log - Audit the granting or denial of permissions.
293 : : * @subject_label : smack label of the requester
294 : : * @object_label : smack label of the object being accessed
295 : : * @request: requested permissions
296 : : * @result: result from smk_access
297 : : * @a: auxiliary audit data
298 : : *
299 : : * Audit the granting or denial of permissions in accordance
300 : : * with the policy.
301 : : */
302 : 0 : void smack_log(char *subject_label, char *object_label, int request,
303 : : int result, struct smk_audit_info *ad)
304 : : {
305 : : char request_buffer[SMK_NUM_ACCESS_TYPE + 1];
306 : : struct smack_audit_data *sad;
307 : 0 : struct common_audit_data *a = &ad->a;
308 : :
309 : : /* check if we have to log the current event */
310 [ # # ][ # # ]: 0 : if (result != 0 && (log_policy & SMACK_AUDIT_DENIED) == 0)
311 : 0 : return;
312 [ # # ][ # # ]: 0 : if (result == 0 && (log_policy & SMACK_AUDIT_ACCEPT) == 0)
313 : : return;
314 : :
315 : 0 : sad = a->smack_audit_data;
316 : :
317 [ # # ]: 0 : if (sad->function == NULL)
318 : 0 : sad->function = "unknown";
319 : :
320 : : /* end preparing the audit data */
321 : : smack_str_from_perm(request_buffer, request);
322 : 0 : sad->subject = subject_label;
323 : 0 : sad->object = object_label;
324 : 0 : sad->request = request_buffer;
325 : 0 : sad->result = result;
326 : :
327 : 0 : common_lsm_audit(a, smack_log_callback, NULL);
328 : : }
329 : : #else /* #ifdef CONFIG_AUDIT */
330 : : void smack_log(char *subject_label, char *object_label, int request,
331 : : int result, struct smk_audit_info *ad)
332 : : {
333 : : }
334 : : #endif
335 : :
336 : : DEFINE_MUTEX(smack_known_lock);
337 : :
338 : : struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
339 : :
340 : : /**
341 : : * smk_insert_entry - insert a smack label into a hash map,
342 : : *
343 : : * this function must be called under smack_known_lock
344 : : */
345 : 0 : void smk_insert_entry(struct smack_known *skp)
346 : : {
347 : : unsigned int hash;
348 : : struct hlist_head *head;
349 : :
350 : 0 : hash = full_name_hash(skp->smk_known, strlen(skp->smk_known));
351 : 0 : head = &smack_known_hash[hash & (SMACK_HASH_SLOTS - 1)];
352 : :
353 : 0 : hlist_add_head_rcu(&skp->smk_hashed, head);
354 : 0 : list_add_rcu(&skp->list, &smack_known_list);
355 : 0 : }
356 : :
357 : : /**
358 : : * smk_find_entry - find a label on the list, return the list entry
359 : : * @string: a text string that might be a Smack label
360 : : *
361 : : * Returns a pointer to the entry in the label list that
362 : : * matches the passed string.
363 : : */
364 : 0 : struct smack_known *smk_find_entry(const char *string)
365 : : {
366 : : unsigned int hash;
367 : : struct hlist_head *head;
368 : : struct smack_known *skp;
369 : :
370 : 0 : hash = full_name_hash(string, strlen(string));
371 : 0 : head = &smack_known_hash[hash & (SMACK_HASH_SLOTS - 1)];
372 : :
373 [ # # ][ # # ]: 0 : hlist_for_each_entry_rcu(skp, head, smk_hashed)
[ # # ]
374 [ # # ]: 0 : if (strcmp(skp->smk_known, string) == 0)
375 : : return skp;
376 : :
377 : : return NULL;
378 : : }
379 : :
380 : : /**
381 : : * smk_parse_smack - parse smack label from a text string
382 : : * @string: a text string that might contain a Smack label
383 : : * @len: the maximum size, or zero if it is NULL terminated.
384 : : *
385 : : * Returns a pointer to the clean label, or NULL
386 : : */
387 : 0 : char *smk_parse_smack(const char *string, int len)
388 : : {
389 : : char *smack;
390 : : int i;
391 : :
392 [ # # ]: 0 : if (len <= 0)
393 : 0 : len = strlen(string) + 1;
394 : :
395 : : /*
396 : : * Reserve a leading '-' as an indicator that
397 : : * this isn't a label, but an option to interfaces
398 : : * including /smack/cipso and /smack/cipso2
399 : : */
400 [ # # ]: 0 : if (string[0] == '-')
401 : : return NULL;
402 : :
403 [ # # ]: 0 : for (i = 0; i < len; i++)
404 [ # # ][ # # ]: 0 : if (string[i] > '~' || string[i] <= ' ' || string[i] == '/' ||
405 [ # # ][ # # ]: 0 : string[i] == '"' || string[i] == '\\' || string[i] == '\'')
406 : : break;
407 : :
408 [ # # ]: 0 : if (i == 0 || i >= SMK_LONGLABEL)
409 : : return NULL;
410 : :
411 : 0 : smack = kzalloc(i + 1, GFP_KERNEL);
412 [ # # ]: 0 : if (smack != NULL) {
413 : 0 : strncpy(smack, string, i + 1);
414 : 0 : smack[i] = '\0';
415 : : }
416 : 0 : return smack;
417 : : }
418 : :
419 : : /**
420 : : * smk_netlbl_mls - convert a catset to netlabel mls categories
421 : : * @catset: the Smack categories
422 : : * @sap: where to put the netlabel categories
423 : : *
424 : : * Allocates and fills attr.mls
425 : : * Returns 0 on success, error code on failure.
426 : : */
427 : 0 : int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap,
428 : : int len)
429 : : {
430 : : unsigned char *cp;
431 : : unsigned char m;
432 : : int cat;
433 : : int rc;
434 : : int byte;
435 : :
436 : 0 : sap->flags |= NETLBL_SECATTR_MLS_CAT;
437 : 0 : sap->attr.mls.lvl = level;
438 : 0 : sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
439 [ # # ]: 0 : if (!sap->attr.mls.cat)
440 : : return -ENOMEM;
441 : 0 : sap->attr.mls.cat->startbit = 0;
442 : :
443 [ # # ]: 0 : for (cat = 1, cp = catset, byte = 0; byte < len; cp++, byte++)
444 [ # # ]: 0 : for (m = 0x80; m != 0; m >>= 1, cat++) {
445 [ # # ]: 0 : if ((m & *cp) == 0)
446 : 0 : continue;
447 : 0 : rc = netlbl_secattr_catmap_setbit(sap->attr.mls.cat,
448 : : cat, GFP_ATOMIC);
449 [ # # ]: 0 : if (rc < 0) {
450 : 0 : netlbl_secattr_catmap_free(sap->attr.mls.cat);
451 : : return rc;
452 : : }
453 : : }
454 : :
455 : : return 0;
456 : : }
457 : :
458 : : /**
459 : : * smk_import_entry - import a label, return the list entry
460 : : * @string: a text string that might be a Smack label
461 : : * @len: the maximum size, or zero if it is NULL terminated.
462 : : *
463 : : * Returns a pointer to the entry in the label list that
464 : : * matches the passed string, adding it if necessary.
465 : : */
466 : 0 : struct smack_known *smk_import_entry(const char *string, int len)
467 : : {
468 : : struct smack_known *skp;
469 : : char *smack;
470 : : int slen;
471 : : int rc;
472 : :
473 : 0 : smack = smk_parse_smack(string, len);
474 [ # # ]: 0 : if (smack == NULL)
475 : : return NULL;
476 : :
477 : 0 : mutex_lock(&smack_known_lock);
478 : :
479 : 0 : skp = smk_find_entry(smack);
480 [ # # ]: 0 : if (skp != NULL)
481 : : goto freeout;
482 : :
483 : : skp = kzalloc(sizeof(*skp), GFP_KERNEL);
484 [ # # ]: 0 : if (skp == NULL)
485 : : goto freeout;
486 : :
487 : 0 : skp->smk_known = smack;
488 : 0 : skp->smk_secid = smack_next_secid++;
489 : 0 : skp->smk_netlabel.domain = skp->smk_known;
490 : 0 : skp->smk_netlabel.flags =
491 : : NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
492 : : /*
493 : : * If direct labeling works use it.
494 : : * Otherwise use mapped labeling.
495 : : */
496 : 0 : slen = strlen(smack);
497 [ # # ]: 0 : if (slen < SMK_CIPSOLEN)
498 : 0 : rc = smk_netlbl_mls(smack_cipso_direct, skp->smk_known,
499 : : &skp->smk_netlabel, slen);
500 : : else
501 : 0 : rc = smk_netlbl_mls(smack_cipso_mapped, (char *)&skp->smk_secid,
502 : : &skp->smk_netlabel, sizeof(skp->smk_secid));
503 : :
504 [ # # ]: 0 : if (rc >= 0) {
505 : 0 : INIT_LIST_HEAD(&skp->smk_rules);
506 : 0 : mutex_init(&skp->smk_rules_lock);
507 : : /*
508 : : * Make sure that the entry is actually
509 : : * filled before putting it on the list.
510 : : */
511 : 0 : smk_insert_entry(skp);
512 : 0 : goto unlockout;
513 : : }
514 : : /*
515 : : * smk_netlbl_mls failed.
516 : : */
517 : 0 : kfree(skp);
518 : : skp = NULL;
519 : : freeout:
520 : 0 : kfree(smack);
521 : : unlockout:
522 : 0 : mutex_unlock(&smack_known_lock);
523 : :
524 : 0 : return skp;
525 : : }
526 : :
527 : : /**
528 : : * smk_import - import a smack label
529 : : * @string: a text string that might be a Smack label
530 : : * @len: the maximum size, or zero if it is NULL terminated.
531 : : *
532 : : * Returns a pointer to the label in the label list that
533 : : * matches the passed string, adding it if necessary.
534 : : */
535 : 0 : char *smk_import(const char *string, int len)
536 : : {
537 : : struct smack_known *skp;
538 : :
539 : : /* labels cannot begin with a '-' */
540 [ # # ]: 0 : if (string[0] == '-')
541 : : return NULL;
542 : 0 : skp = smk_import_entry(string, len);
543 [ # # ]: 0 : if (skp == NULL)
544 : : return NULL;
545 : 0 : return skp->smk_known;
546 : : }
547 : :
548 : : /**
549 : : * smack_from_secid - find the Smack label associated with a secid
550 : : * @secid: an integer that might be associated with a Smack label
551 : : *
552 : : * Returns a pointer to the appropriate Smack label entry if there is one,
553 : : * otherwise a pointer to the invalid Smack label.
554 : : */
555 : 0 : struct smack_known *smack_from_secid(const u32 secid)
556 : : {
557 : : struct smack_known *skp;
558 : :
559 : : rcu_read_lock();
560 [ # # ]: 0 : list_for_each_entry_rcu(skp, &smack_known_list, list) {
561 [ # # ]: 0 : if (skp->smk_secid == secid) {
562 : : rcu_read_unlock();
563 : 0 : return skp;
564 : : }
565 : : }
566 : :
567 : : /*
568 : : * If we got this far someone asked for the translation
569 : : * of a secid that is not on the list.
570 : : */
571 : : rcu_read_unlock();
572 : 0 : return &smack_known_invalid;
573 : : }
574 : :
575 : : /**
576 : : * smack_to_secid - find the secid associated with a Smack label
577 : : * @smack: the Smack label
578 : : *
579 : : * Returns the appropriate secid if there is one,
580 : : * otherwise 0
581 : : */
582 : 0 : u32 smack_to_secid(const char *smack)
583 : : {
584 : 0 : struct smack_known *skp = smk_find_entry(smack);
585 : :
586 [ # # ]: 0 : if (skp == NULL)
587 : : return 0;
588 : 0 : return skp->smk_secid;
589 : : }
|