]> nv-tegra.nvidia Code Review - linux-2.6.git/blob - security/smack/smack_access.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
[linux-2.6.git] / security / smack / smack_access.c
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         .smk_cipso      = NULL,
23 };
24
25 struct smack_known smack_known_hat = {
26         .smk_known      = "^",
27         .smk_secid      = 3,
28         .smk_cipso      = NULL,
29 };
30
31 struct smack_known smack_known_star = {
32         .smk_known      = "*",
33         .smk_secid      = 4,
34         .smk_cipso      = NULL,
35 };
36
37 struct smack_known smack_known_floor = {
38         .smk_known      = "_",
39         .smk_secid      = 5,
40         .smk_cipso      = NULL,
41 };
42
43 struct smack_known smack_known_invalid = {
44         .smk_known      = "",
45         .smk_secid      = 6,
46         .smk_cipso      = NULL,
47 };
48
49 struct smack_known smack_known_web = {
50         .smk_known      = "@",
51         .smk_secid      = 7,
52         .smk_cipso      = NULL,
53 };
54
55 LIST_HEAD(smack_known_list);
56
57 /*
58  * The initial value needs to be bigger than any of the
59  * known values above.
60  */
61 static u32 smack_next_secid = 10;
62
63 /*
64  * what events do we log
65  * can be overwritten at run-time by /smack/logging
66  */
67 int log_policy = SMACK_AUDIT_DENIED;
68
69 /**
70  * smk_access_entry - look up matching access rule
71  * @subject_label: a pointer to the subject's Smack label
72  * @object_label: a pointer to the object's Smack label
73  * @rule_list: the list of rules to search
74  *
75  * This function looks up the subject/object pair in the
76  * access rule list and returns the access mode. If no
77  * entry is found returns -ENOENT.
78  *
79  * NOTE:
80  * Even though Smack labels are usually shared on smack_list
81  * labels that come in off the network can't be imported
82  * and added to the list for locking reasons.
83  *
84  * Therefore, it is necessary to check the contents of the labels,
85  * not just the pointer values. Of course, in most cases the labels
86  * will be on the list, so checking the pointers may be a worthwhile
87  * optimization.
88  */
89 int smk_access_entry(char *subject_label, char *object_label,
90                         struct list_head *rule_list)
91 {
92         int may = -ENOENT;
93         struct smack_rule *srp;
94
95         list_for_each_entry_rcu(srp, rule_list, list) {
96                 if (srp->smk_subject == subject_label ||
97                     strcmp(srp->smk_subject, subject_label) == 0) {
98                         if (srp->smk_object == object_label ||
99                             strcmp(srp->smk_object, object_label) == 0) {
100                                 may = srp->smk_access;
101                                 break;
102                         }
103                 }
104         }
105
106         return may;
107 }
108
109 /**
110  * smk_access - determine if a subject has a specific access to an object
111  * @subject_label: a pointer to the subject's Smack label
112  * @object_label: a pointer to the object's Smack label
113  * @request: the access requested, in "MAY" format
114  * @a : a pointer to the audit data
115  *
116  * This function looks up the subject/object pair in the
117  * access rule list and returns 0 if the access is permitted,
118  * non zero otherwise.
119  *
120  * Even though Smack labels are usually shared on smack_list
121  * labels that come in off the network can't be imported
122  * and added to the list for locking reasons.
123  *
124  * Therefore, it is necessary to check the contents of the labels,
125  * not just the pointer values. Of course, in most cases the labels
126  * will be on the list, so checking the pointers may be a worthwhile
127  * optimization.
128  */
129 int smk_access(char *subject_label, char *object_label, int request,
130                struct smk_audit_info *a)
131 {
132         int may = MAY_NOT;
133         int rc = 0;
134
135         /*
136          * Hardcoded comparisons.
137          *
138          * A star subject can't access any object.
139          */
140         if (subject_label == smack_known_star.smk_known ||
141             strcmp(subject_label, smack_known_star.smk_known) == 0) {
142                 rc = -EACCES;
143                 goto out_audit;
144         }
145         /*
146          * An internet object can be accessed by any subject.
147          * Tasks cannot be assigned the internet label.
148          * An internet subject can access any object.
149          */
150         if (object_label == smack_known_web.smk_known ||
151             subject_label == smack_known_web.smk_known ||
152             strcmp(object_label, smack_known_web.smk_known) == 0 ||
153             strcmp(subject_label, smack_known_web.smk_known) == 0)
154                 goto out_audit;
155         /*
156          * A star object can be accessed by any subject.
157          */
158         if (object_label == smack_known_star.smk_known ||
159             strcmp(object_label, smack_known_star.smk_known) == 0)
160                 goto out_audit;
161         /*
162          * An object can be accessed in any way by a subject
163          * with the same label.
164          */
165         if (subject_label == object_label ||
166             strcmp(subject_label, object_label) == 0)
167                 goto out_audit;
168         /*
169          * A hat subject can read any object.
170          * A floor object can be read by any subject.
171          */
172         if ((request & MAY_ANYREAD) == request) {
173                 if (object_label == smack_known_floor.smk_known ||
174                     strcmp(object_label, smack_known_floor.smk_known) == 0)
175                         goto out_audit;
176                 if (subject_label == smack_known_hat.smk_known ||
177                     strcmp(subject_label, smack_known_hat.smk_known) == 0)
178                         goto out_audit;
179         }
180         /*
181          * Beyond here an explicit relationship is required.
182          * If the requested access is contained in the available
183          * access (e.g. read is included in readwrite) it's
184          * good. A negative response from smk_access_entry()
185          * indicates there is no entry for this pair.
186          */
187         rcu_read_lock();
188         may = smk_access_entry(subject_label, object_label, &smack_rule_list);
189         rcu_read_unlock();
190
191         if (may > 0 && (request & may) == request)
192                 goto out_audit;
193
194         rc = -EACCES;
195 out_audit:
196 #ifdef CONFIG_AUDIT
197         if (a)
198                 smack_log(subject_label, object_label, request, rc, a);
199 #endif
200         return rc;
201 }
202
203 /**
204  * smk_curacc - determine if current has a specific access to an object
205  * @obj_label: a pointer to the object's Smack label
206  * @mode: the access requested, in "MAY" format
207  * @a : common audit data
208  *
209  * This function checks the current subject label/object label pair
210  * in the access rule list and returns 0 if the access is permitted,
211  * non zero otherwise. It allows that current may have the capability
212  * to override the rules.
213  */
214 int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a)
215 {
216         struct task_smack *tsp = current_security();
217         char *sp = smk_of_task(tsp);
218         int may;
219         int rc;
220
221         /*
222          * Check the global rule list
223          */
224         rc = smk_access(sp, obj_label, mode, NULL);
225         if (rc == 0) {
226                 /*
227                  * If there is an entry in the task's rule list
228                  * it can further restrict access.
229                  */
230                 may = smk_access_entry(sp, obj_label, &tsp->smk_rules);
231                 if (may < 0)
232                         goto out_audit;
233                 if ((mode & may) == mode)
234                         goto out_audit;
235                 rc = -EACCES;
236         }
237
238         /*
239          * Return if a specific label has been designated as the
240          * only one that gets privilege and current does not
241          * have that label.
242          */
243         if (smack_onlycap != NULL && smack_onlycap != sp)
244                 goto out_audit;
245
246         if (capable(CAP_MAC_OVERRIDE))
247                 rc = 0;
248
249 out_audit:
250 #ifdef CONFIG_AUDIT
251         if (a)
252                 smack_log(sp, obj_label, mode, rc, a);
253 #endif
254         return rc;
255 }
256
257 #ifdef CONFIG_AUDIT
258 /**
259  * smack_str_from_perm : helper to transalate an int to a
260  * readable string
261  * @string : the string to fill
262  * @access : the int
263  *
264  */
265 static inline void smack_str_from_perm(char *string, int access)
266 {
267         int i = 0;
268         if (access & MAY_READ)
269                 string[i++] = 'r';
270         if (access & MAY_WRITE)
271                 string[i++] = 'w';
272         if (access & MAY_EXEC)
273                 string[i++] = 'x';
274         if (access & MAY_APPEND)
275                 string[i++] = 'a';
276         string[i] = '\0';
277 }
278 /**
279  * smack_log_callback - SMACK specific information
280  * will be called by generic audit code
281  * @ab : the audit_buffer
282  * @a  : audit_data
283  *
284  */
285 static void smack_log_callback(struct audit_buffer *ab, void *a)
286 {
287         struct common_audit_data *ad = a;
288         struct smack_audit_data *sad = &ad->smack_audit_data;
289         audit_log_format(ab, "lsm=SMACK fn=%s action=%s",
290                          ad->smack_audit_data.function,
291                          sad->result ? "denied" : "granted");
292         audit_log_format(ab, " subject=");
293         audit_log_untrustedstring(ab, sad->subject);
294         audit_log_format(ab, " object=");
295         audit_log_untrustedstring(ab, sad->object);
296         audit_log_format(ab, " requested=%s", sad->request);
297 }
298
299 /**
300  *  smack_log - Audit the granting or denial of permissions.
301  *  @subject_label : smack label of the requester
302  *  @object_label  : smack label of the object being accessed
303  *  @request: requested permissions
304  *  @result: result from smk_access
305  *  @a:  auxiliary audit data
306  *
307  * Audit the granting or denial of permissions in accordance
308  * with the policy.
309  */
310 void smack_log(char *subject_label, char *object_label, int request,
311                int result, struct smk_audit_info *ad)
312 {
313         char request_buffer[SMK_NUM_ACCESS_TYPE + 1];
314         struct smack_audit_data *sad;
315         struct common_audit_data *a = &ad->a;
316
317         /* check if we have to log the current event */
318         if (result != 0 && (log_policy & SMACK_AUDIT_DENIED) == 0)
319                 return;
320         if (result == 0 && (log_policy & SMACK_AUDIT_ACCEPT) == 0)
321                 return;
322
323         if (a->smack_audit_data.function == NULL)
324                 a->smack_audit_data.function = "unknown";
325
326         /* end preparing the audit data */
327         sad = &a->smack_audit_data;
328         smack_str_from_perm(request_buffer, request);
329         sad->subject = subject_label;
330         sad->object  = object_label;
331         sad->request = request_buffer;
332         sad->result  = result;
333         a->lsm_pre_audit = smack_log_callback;
334
335         common_lsm_audit(a);
336 }
337 #else /* #ifdef CONFIG_AUDIT */
338 void smack_log(char *subject_label, char *object_label, int request,
339                int result, struct smk_audit_info *ad)
340 {
341 }
342 #endif
343
344 static DEFINE_MUTEX(smack_known_lock);
345
346 /**
347  * smk_import_entry - import a label, return the list entry
348  * @string: a text string that might be a Smack label
349  * @len: the maximum size, or zero if it is NULL terminated.
350  *
351  * Returns a pointer to the entry in the label list that
352  * matches the passed string, adding it if necessary.
353  */
354 struct smack_known *smk_import_entry(const char *string, int len)
355 {
356         struct smack_known *skp;
357         char smack[SMK_LABELLEN];
358         int found;
359         int i;
360
361         if (len <= 0 || len > SMK_MAXLEN)
362                 len = SMK_MAXLEN;
363
364         for (i = 0, found = 0; i < SMK_LABELLEN; i++) {
365                 if (found)
366                         smack[i] = '\0';
367                 else if (i >= len || string[i] > '~' || string[i] <= ' ' ||
368                          string[i] == '/' || string[i] == '"' ||
369                          string[i] == '\\' || string[i] == '\'') {
370                         smack[i] = '\0';
371                         found = 1;
372                 } else
373                         smack[i] = string[i];
374         }
375
376         if (smack[0] == '\0')
377                 return NULL;
378
379         mutex_lock(&smack_known_lock);
380
381         found = 0;
382         list_for_each_entry_rcu(skp, &smack_known_list, list) {
383                 if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) {
384                         found = 1;
385                         break;
386                 }
387         }
388
389         if (found == 0) {
390                 skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL);
391                 if (skp != NULL) {
392                         strncpy(skp->smk_known, smack, SMK_MAXLEN);
393                         skp->smk_secid = smack_next_secid++;
394                         skp->smk_cipso = NULL;
395                         spin_lock_init(&skp->smk_cipsolock);
396                         /*
397                          * Make sure that the entry is actually
398                          * filled before putting it on the list.
399                          */
400                         list_add_rcu(&skp->list, &smack_known_list);
401                 }
402         }
403
404         mutex_unlock(&smack_known_lock);
405
406         return skp;
407 }
408
409 /**
410  * smk_import - import a smack label
411  * @string: a text string that might be a Smack label
412  * @len: the maximum size, or zero if it is NULL terminated.
413  *
414  * Returns a pointer to the label in the label list that
415  * matches the passed string, adding it if necessary.
416  */
417 char *smk_import(const char *string, int len)
418 {
419         struct smack_known *skp;
420
421         /* labels cannot begin with a '-' */
422         if (string[0] == '-')
423                 return NULL;
424         skp = smk_import_entry(string, len);
425         if (skp == NULL)
426                 return NULL;
427         return skp->smk_known;
428 }
429
430 /**
431  * smack_from_secid - find the Smack label associated with a secid
432  * @secid: an integer that might be associated with a Smack label
433  *
434  * Returns a pointer to the appropriate Smack label if there is one,
435  * otherwise a pointer to the invalid Smack label.
436  */
437 char *smack_from_secid(const u32 secid)
438 {
439         struct smack_known *skp;
440
441         rcu_read_lock();
442         list_for_each_entry_rcu(skp, &smack_known_list, list) {
443                 if (skp->smk_secid == secid) {
444                         rcu_read_unlock();
445                         return skp->smk_known;
446                 }
447         }
448
449         /*
450          * If we got this far someone asked for the translation
451          * of a secid that is not on the list.
452          */
453         rcu_read_unlock();
454         return smack_known_invalid.smk_known;
455 }
456
457 /**
458  * smack_to_secid - find the secid associated with a Smack label
459  * @smack: the Smack label
460  *
461  * Returns the appropriate secid if there is one,
462  * otherwise 0
463  */
464 u32 smack_to_secid(const char *smack)
465 {
466         struct smack_known *skp;
467
468         rcu_read_lock();
469         list_for_each_entry_rcu(skp, &smack_known_list, list) {
470                 if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) {
471                         rcu_read_unlock();
472                         return skp->smk_secid;
473                 }
474         }
475         rcu_read_unlock();
476         return 0;
477 }
478
479 /**
480  * smack_from_cipso - find the Smack label associated with a CIPSO option
481  * @level: Bell & LaPadula level from the network
482  * @cp: Bell & LaPadula categories from the network
483  * @result: where to put the Smack value
484  *
485  * This is a simple lookup in the label table.
486  *
487  * This is an odd duck as far as smack handling goes in that
488  * it sends back a copy of the smack label rather than a pointer
489  * to the master list. This is done because it is possible for
490  * a foreign host to send a smack label that is new to this
491  * machine and hence not on the list. That would not be an
492  * issue except that adding an entry to the master list can't
493  * be done at that point.
494  */
495 void smack_from_cipso(u32 level, char *cp, char *result)
496 {
497         struct smack_known *kp;
498         char *final = NULL;
499
500         rcu_read_lock();
501         list_for_each_entry(kp, &smack_known_list, list) {
502                 if (kp->smk_cipso == NULL)
503                         continue;
504
505                 spin_lock_bh(&kp->smk_cipsolock);
506
507                 if (kp->smk_cipso->smk_level == level &&
508                     memcmp(kp->smk_cipso->smk_catset, cp, SMK_LABELLEN) == 0)
509                         final = kp->smk_known;
510
511                 spin_unlock_bh(&kp->smk_cipsolock);
512         }
513         rcu_read_unlock();
514         if (final == NULL)
515                 final = smack_known_huh.smk_known;
516         strncpy(result, final, SMK_MAXLEN);
517         return;
518 }
519
520 /**
521  * smack_to_cipso - find the CIPSO option to go with a Smack label
522  * @smack: a pointer to the smack label in question
523  * @cp: where to put the result
524  *
525  * Returns zero if a value is available, non-zero otherwise.
526  */
527 int smack_to_cipso(const char *smack, struct smack_cipso *cp)
528 {
529         struct smack_known *kp;
530         int found = 0;
531
532         rcu_read_lock();
533         list_for_each_entry_rcu(kp, &smack_known_list, list) {
534                 if (kp->smk_known == smack ||
535                     strcmp(kp->smk_known, smack) == 0) {
536                         found = 1;
537                         break;
538                 }
539         }
540         rcu_read_unlock();
541
542         if (found == 0 || kp->smk_cipso == NULL)
543                 return -ENOENT;
544
545         memcpy(cp, kp->smk_cipso, sizeof(struct smack_cipso));
546         return 0;
547 }