Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/security/smack/smack_access.c
10814 views
1
/*
2
* Copyright (C) 2007 Casey Schaufler <[email protected]>
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 <[email protected]>
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
}
548
549