~ubuntu-branches/ubuntu/wily/apparmor/wily

« back to all changes in this revision

Viewing changes to libraries/libapparmor/src/features.c

  • Committer: Package Import Robot
  • Author(s): Steve Beattie
  • Date: 2015-07-23 01:57:43 UTC
  • mfrom: (1.1.32)
  • Revision ID: package-import@ubuntu.com-20150723015743-q1nfat496q3d8rcm
Tags: 2.10-0ubuntu1
* Update to apparmor 2.10
  - libapparmor added functions to ease loading profile cache files to
    help support systemd on-demand load of policy (LP: #1385414)
  - apparmor parser: fixed policy generation to allow matching
    embedded NULs in abstract unix socket names (LP: #1413410)
  - aa-status: don't traceback when not permitted to read current
    set of apparmor policy (LP: #1466768)
  - aa-logprof: don't crash on policies that have an #include of a
    directory (LP: #1471425)
  - aa-logprof: fix crash when network rejections occur when file
    operations are performed on network sockets (LP: #1466812)
* dropped reproducible-pdf.patch, incorporated upstream
* debian/patches/tests-fix_sysctl_test.patch: fix sysctl test failure
  with 4.1 kernel and newer.
* debian/control: add alternate dependency on linux-initramfs-tool
  (LP: #1109029)
* debian/libapparmor1.symbols: update symbols file for added symbols
  in libapparmor

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *   Copyright (c) 2014
 
3
 *   Canonical, Ltd. (All rights reserved)
 
4
 *
 
5
 *   This program is free software; you can redistribute it and/or
 
6
 *   modify it under the terms of version 2 of the GNU General Public
 
7
 *   License published by the Free Software Foundation.
 
8
 *
 
9
 *   This program is distributed in the hope that it will be useful,
 
10
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 *   GNU General Public License for more details.
 
13
 *
 
14
 *   You should have received a copy of the GNU General Public License
 
15
 *   along with this program; if not, contact Novell, Inc. or Canonical
 
16
 *   Ltd.
 
17
 */
 
18
 
 
19
#include <errno.h>
 
20
#include <ctype.h>
 
21
#include <dirent.h>
 
22
#include <fcntl.h>
 
23
#include <stdio.h>
 
24
#include <string.h>
 
25
#include <stdarg.h>
 
26
#include <stdlib.h>
 
27
#include <sys/types.h>
 
28
#include <sys/stat.h>
 
29
#include <unistd.h>
 
30
#include <sys/apparmor.h>
 
31
 
 
32
#include "private.h"
 
33
 
 
34
#define FEATURES_FILE "/sys/kernel/security/apparmor/features"
 
35
 
 
36
#define STRING_SIZE 8192
 
37
 
 
38
struct aa_features {
 
39
        unsigned int ref_count;
 
40
        char string[STRING_SIZE];
 
41
};
 
42
 
 
43
struct features_struct {
 
44
        char *buffer;
 
45
        int size;
 
46
        char *pos;
 
47
};
 
48
 
 
49
struct component {
 
50
        const char *str;
 
51
        size_t len;
 
52
};
 
53
 
 
54
static int features_snprintf(struct features_struct *fst, const char *fmt, ...)
 
55
{
 
56
        va_list args;
 
57
        int i, remaining = fst->size - (fst->pos - fst->buffer);
 
58
 
 
59
        if (remaining < 0) {
 
60
                errno = EINVAL;
 
61
                PERROR("Invalid features buffer offset\n");
 
62
                return -1;
 
63
        }
 
64
 
 
65
        va_start(args, fmt);
 
66
        i = vsnprintf(fst->pos, remaining, fmt, args);
 
67
        va_end(args);
 
68
 
 
69
        if (i < 0) {
 
70
                errno = EIO;
 
71
                PERROR("Failed to write to features buffer\n");
 
72
                return -1;
 
73
        } else if (i >= remaining) {
 
74
                errno = ENOBUFS;
 
75
                PERROR("Feature buffer full.");
 
76
                return -1;
 
77
        }
 
78
 
 
79
        fst->pos += i;
 
80
        return 0;
 
81
}
 
82
 
 
83
/* load_features_file - opens and reads a file into @buffer and then NUL-terminates @buffer
 
84
 * @dirfd: a directory file descriptory or AT_FDCWD (see openat(2))
 
85
 * @path: name of the file
 
86
 * @buffer: the buffer to read the features file into (will be NUL-terminated on success)
 
87
 * @size: the size of @buffer
 
88
 *
 
89
 * Returns: The number of bytes copied into @buffer on success (not counting
 
90
 * the NUL-terminator), else -1 and errno is set. Note that @size must be
 
91
 * larger than the size of the file or -1 will be returned with errno set to
 
92
 * ENOBUFS indicating that @buffer was not large enough to contain all of the
 
93
 * file contents.
 
94
 */
 
95
static int load_features_file(int dirfd, const char *path,
 
96
                              char *buffer, size_t size)
 
97
{
 
98
        autoclose int file = -1;
 
99
        char *pos = buffer;
 
100
        ssize_t len;
 
101
 
 
102
        file = openat(dirfd, path, O_RDONLY);
 
103
        if (file < 0) {
 
104
                PDEBUG("Could not open '%s'\n", path);
 
105
                return -1;
 
106
        }
 
107
        PDEBUG("Opened features \"%s\"\n", path);
 
108
 
 
109
        if (!size) {
 
110
                errno = ENOBUFS;
 
111
                return -1;
 
112
        }
 
113
 
 
114
        /* Save room for a NUL-terminator at the end of @buffer */
 
115
        size--;
 
116
 
 
117
        do {
 
118
                len = read(file, pos, size);
 
119
                if (len > 0) {
 
120
                        size -= len;
 
121
                        pos += len;
 
122
                }
 
123
        } while (len > 0 && size);
 
124
 
 
125
        /**
 
126
         * Possible error conditions:
 
127
         *  - len == -1: read failed and errno is already set so return -1
 
128
         *  - len  >  0: read() never returned 0 (EOF) meaning that @buffer was
 
129
         *               too small to contain all of the file contents so set
 
130
         *               errno to ENOBUFS and return -1
 
131
         */
 
132
        if (len) {
 
133
                if (len > 0)
 
134
                        errno = ENOBUFS;
 
135
 
 
136
                PDEBUG("Error reading features file '%s': %m\n", path);
 
137
                return -1;
 
138
        }
 
139
 
 
140
        /* NUL-terminate @buffer */
 
141
        *pos = 0;
 
142
 
 
143
        return pos - buffer;
 
144
}
 
145
 
 
146
static int features_dir_cb(int dirfd, const char *name, struct stat *st,
 
147
                           void *data)
 
148
{
 
149
        struct features_struct *fst = (struct features_struct *) data;
 
150
 
 
151
        /* skip dot files and files with no name */
 
152
        if (*name == '.' || !strlen(name))
 
153
                return 0;
 
154
 
 
155
        if (features_snprintf(fst, "%s {", name) == -1)
 
156
                return -1;
 
157
 
 
158
        if (S_ISREG(st->st_mode)) {
 
159
                int len;
 
160
                int remaining = fst->size - (fst->pos - fst->buffer);
 
161
 
 
162
                len = load_features_file(dirfd, name, fst->pos, remaining);
 
163
                if (len < 0)
 
164
                        return -1;
 
165
 
 
166
                fst->pos += len;
 
167
        } else if (S_ISDIR(st->st_mode)) {
 
168
                if (_aa_dirat_for_each(dirfd, name, fst, features_dir_cb))
 
169
                        return -1;
 
170
        }
 
171
 
 
172
        if (features_snprintf(fst, "}\n") == -1)
 
173
                return -1;
 
174
 
 
175
        return 0;
 
176
}
 
177
 
 
178
static int load_features_dir(int dirfd, const char *path,
 
179
                             char *buffer, int size)
 
180
{
 
181
        struct features_struct fst = { buffer, size, buffer };
 
182
 
 
183
        if (_aa_dirat_for_each(dirfd, path, &fst, features_dir_cb)) {
 
184
                PDEBUG("Failed evaluating %s\n", path);
 
185
                return -1;
 
186
        }
 
187
 
 
188
        return 0;
 
189
}
 
190
 
 
191
static bool islbrace(int c)
 
192
{
 
193
        return c == '{';
 
194
}
 
195
 
 
196
static bool isrbrace(int c)
 
197
{
 
198
        return c == '}';
 
199
}
 
200
 
 
201
static bool isnul(int c)
 
202
{
 
203
        return c == '\0';
 
204
}
 
205
 
 
206
static bool isbrace(int c)
 
207
{
 
208
        return islbrace(c) || isrbrace(c);
 
209
}
 
210
 
 
211
static bool isbrace_or_nul(int c)
 
212
{
 
213
        return isbrace(c) || isnul(c);
 
214
}
 
215
 
 
216
static bool isbrace_space_or_nul(int c)
 
217
{
 
218
        return isbrace(c) || isspace(c) || isnul(c);
 
219
}
 
220
 
 
221
static size_t tokenize_path_components(const char *str,
 
222
                                       struct component *components,
 
223
                                       size_t max_components)
 
224
{
 
225
        size_t i = 0;
 
226
 
 
227
        memset(components, 0, sizeof(*components) * max_components);
 
228
 
 
229
        if (!str)
 
230
                return 0;
 
231
 
 
232
        while (*str && i < max_components) {
 
233
                const char *fwdslash = strchrnul(str, '/');
 
234
 
 
235
                /* Save the token if it is not "/" */
 
236
                if (fwdslash != str) {
 
237
                        components[i].str = str;
 
238
                        components[i].len = fwdslash - str;
 
239
                        i++;
 
240
                }
 
241
 
 
242
                if (isnul(*fwdslash))
 
243
                        break;
 
244
 
 
245
                str = fwdslash + 1;
 
246
        }
 
247
 
 
248
        return i;
 
249
}
 
250
 
 
251
/**
 
252
 * walk_one - walk one component of a features string
 
253
 * @str: a pointer to the current position in a features string
 
254
 * @component: the component to walk
 
255
 * @is_top_level: true if component is a top-level component
 
256
 *
 
257
 * Imagine a features string such as:
 
258
 *
 
259
 *  "feat1 { subfeat1.1 subfeat1.2 } feat2 { subfeat2.1 { subfeat2.1.1 } }"
 
260
 *
 
261
 * You want to know if "feat2/subfeat2.1/subfeat2.8" is valid. It will take 3
 
262
 * invocations of this function to determine if that string is valid. On the
 
263
 * first call, *@str will point to the beginning of the features string,
 
264
 * component->str will be "feat2", and is_top_level will be true since feat2 is
 
265
 * a top level feature. The function will return true and *@str will now point
 
266
 * at the the left brace after "feat2" in the features string. You can call
 
267
 * this function again with component->str being equal to "subfeat2.1" and it
 
268
 * will return true and *@str will now point at the left brace after
 
269
 * "subfeat2.1" in the features string. A third call to the function, with
 
270
 * component->str equal to "subfeat2.8", will return false and *@str will not
 
271
 * be changed.
 
272
 *
 
273
 * Returns true if the walk was successful and false otherwise. If the walk was
 
274
 * successful, *@str will point to the first encountered brace after the walk.
 
275
 * If the walk was unsuccessful, *@str is not updated.
 
276
 */
 
277
static bool walk_one(const char **str, const struct component *component,
 
278
                     bool is_top_level)
 
279
{
 
280
        const char *cur;
 
281
        uint32_t brace_count = 0;
 
282
        size_t i = 0;
 
283
 
 
284
        /* NULL pointers and empty strings are not accepted */
 
285
        if (!str || !*str || !component || !component->str || !component->len)
 
286
                return false;
 
287
 
 
288
        cur = *str;
 
289
 
 
290
        /**
 
291
         * If @component is not top-level, the first character in the string to
 
292
         * search MUST be '{'
 
293
         */
 
294
        if (!is_top_level) {
 
295
                if (!islbrace(*cur))
 
296
                        return false;
 
297
 
 
298
                cur++;
 
299
        }
 
300
 
 
301
        /**
 
302
         * This loop tries to find the @component in *@str. When this loops
 
303
         * completes, cur will either point one character past the end of the
 
304
         * matched @component or to the NUL terminator of *@str.
 
305
         */
 
306
        while(!isnul(*cur) && i < component->len) {
 
307
                if (!isascii(*cur)) {
 
308
                        /* Only ASCII is expected */
 
309
                        return false;
 
310
                } else if (islbrace(*cur)) {
 
311
                        /* There's a limit to the number of left braces */
 
312
                        if (brace_count == UINT32_MAX)
 
313
                                return false;
 
314
 
 
315
                        brace_count++;
 
316
                } else if (isrbrace(*cur)) {
 
317
                        /* Check for unexpected right braces */
 
318
                        if (brace_count == 0)
 
319
                                return false;
 
320
 
 
321
                        brace_count--;
 
322
                }
 
323
 
 
324
                /**
 
325
                 * Move to the next character in @component if we're not inside
 
326
                 * of braces and we have a character match
 
327
                 */
 
328
                if (brace_count == 0 && *cur == component->str[i])
 
329
                        i++;
 
330
                else
 
331
                        i = 0;
 
332
 
 
333
                cur++;
 
334
        }
 
335
 
 
336
        /* Return false if a full match was not found */
 
337
        if (i != component->len) {
 
338
                return false;
 
339
        } else if (!isbrace_space_or_nul(*cur))
 
340
                return false;
 
341
 
 
342
        /**
 
343
         * This loop eats up valid (ASCII) characters until a brace or NUL
 
344
         * character is found so that *@str is properly set to call back into
 
345
         * this function
 
346
         */
 
347
        while (!isbrace_or_nul(*cur)) {
 
348
                if (!isascii(*cur))
 
349
                        return false;
 
350
 
 
351
                cur++;
 
352
        }
 
353
 
 
354
        *str = cur;
 
355
        return true;
 
356
}
 
357
 
 
358
/**
 
359
 * aa_features_new - create a new aa_features object based on a path
 
360
 * @features: will point to the address of an allocated and initialized
 
361
 *            aa_features object upon success
 
362
 * @dirfd: directory file descriptor or AT_FDCWD (see openat(2))
 
363
 * @path: path to a features file or directory
 
364
 *
 
365
 * Returns: 0 on success, -1 on error with errno set and *@features pointing to
 
366
 *          NULL
 
367
 */
 
368
int aa_features_new(aa_features **features, int dirfd, const char *path)
 
369
{
 
370
        struct stat stat_file;
 
371
        aa_features *f;
 
372
        int retval;
 
373
 
 
374
        *features = NULL;
 
375
 
 
376
        if (fstatat(dirfd, path, &stat_file, 0) == -1)
 
377
                return -1;
 
378
 
 
379
        f = calloc(1, sizeof(*f));
 
380
        if (!f) {
 
381
                errno = ENOMEM;
 
382
                return -1;
 
383
        }
 
384
        aa_features_ref(f);
 
385
 
 
386
        retval = S_ISDIR(stat_file.st_mode) ?
 
387
                 load_features_dir(dirfd, path, f->string, STRING_SIZE) :
 
388
                 load_features_file(dirfd, path, f->string, STRING_SIZE);
 
389
        if (retval == -1) {
 
390
                int save = errno;
 
391
 
 
392
                aa_features_unref(f);
 
393
                errno = save;
 
394
                return -1;
 
395
        }
 
396
 
 
397
        *features = f;
 
398
 
 
399
        return 0;
 
400
}
 
401
 
 
402
/**
 
403
 * aa_features_new_from_string - create a new aa_features object based on a string
 
404
 * @features: will point to the address of an allocated and initialized
 
405
 *            aa_features object upon success
 
406
 * @string: a NUL-terminated string representation of features
 
407
 * @size: the size of @string, not counting the NUL-terminator
 
408
 *
 
409
 * Returns: 0 on success, -1 on error with errno set and *@features pointing to
 
410
 *          NULL
 
411
 */
 
412
int aa_features_new_from_string(aa_features **features,
 
413
                                const char *string, size_t size)
 
414
{
 
415
        aa_features *f;
 
416
 
 
417
        *features = NULL;
 
418
 
 
419
        /* Require size to be less than STRING_SIZE so there's room for a NUL */
 
420
        if (size >= STRING_SIZE)
 
421
                return ENOBUFS;
 
422
 
 
423
        f = calloc(1, sizeof(*f));
 
424
        if (!f) {
 
425
                errno = ENOMEM;
 
426
                return -1;
 
427
        }
 
428
        aa_features_ref(f);
 
429
 
 
430
        memcpy(f->string, string, size);
 
431
        f->string[size] = '\0';
 
432
        *features = f;
 
433
 
 
434
        return 0;
 
435
}
 
436
 
 
437
/**
 
438
 * aa_features_new_from_kernel - create a new aa_features object based on the current kernel
 
439
 * @features: will point to the address of an allocated and initialized
 
440
 *            aa_features object upon success
 
441
 *
 
442
 * Returns: 0 on success, -1 on error with errno set and *@features pointing to
 
443
 *          NULL
 
444
 */
 
445
int aa_features_new_from_kernel(aa_features **features)
 
446
{
 
447
        return aa_features_new(features, -1, FEATURES_FILE);
 
448
}
 
449
 
 
450
/**
 
451
 * aa_features_ref - increments the ref count of an aa_features object
 
452
 * @features: the features
 
453
 *
 
454
 * Returns: the features
 
455
 */
 
456
aa_features *aa_features_ref(aa_features *features)
 
457
{
 
458
        atomic_inc(&features->ref_count);
 
459
        return features;
 
460
}
 
461
 
 
462
/**
 
463
 * aa_features_unref - decrements the ref count and frees the aa_features object when 0
 
464
 * @features: the features (can be NULL)
 
465
 */
 
466
void aa_features_unref(aa_features *features)
 
467
{
 
468
        if (features && atomic_dec_and_test(&features->ref_count))
 
469
                free(features);
 
470
}
 
471
 
 
472
/**
 
473
 * aa_features_write_to_file - write a string representation of an aa_features object to a file
 
474
 * @features: the features
 
475
 * @dirfd: directory file descriptor or AT_FDCWD (see openat(2))
 
476
 * @path: the path to write to
 
477
 *
 
478
 * Returns: 0 on success, -1 on error with errno set
 
479
 */
 
480
int aa_features_write_to_file(aa_features *features,
 
481
                              int dirfd, const char *path)
 
482
{
 
483
        autoclose int fd = -1;
 
484
        size_t size;
 
485
        ssize_t retval;
 
486
        char *string;
 
487
 
 
488
        fd = openat(dirfd, path,
 
489
                    O_WRONLY | O_CREAT | O_TRUNC | O_SYNC | O_CLOEXEC,
 
490
                    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
 
491
        if (fd == -1)
 
492
                return -1;
 
493
 
 
494
        string = features->string;
 
495
        size = strlen(string);
 
496
        do {
 
497
                retval = write(fd, string, size);
 
498
                if (retval == -1)
 
499
                        return -1;
 
500
 
 
501
                size -= retval;
 
502
                string += retval;
 
503
        } while (size);
 
504
 
 
505
        return 0;
 
506
}
 
507
 
 
508
/**
 
509
 * aa_features_is_equal - equality test for two aa_features objects
 
510
 * @features1: the first features (can be NULL)
 
511
 * @features2: the second features (can be NULL)
 
512
 *
 
513
 * Returns: true if they're equal, false if they're not or either are NULL
 
514
 */
 
515
bool aa_features_is_equal(aa_features *features1, aa_features *features2)
 
516
{
 
517
        return features1 && features2 &&
 
518
               strcmp(features1->string, features2->string) == 0;
 
519
}
 
520
 
 
521
/**
 
522
 * aa_features_supports - provides aa_features object support status
 
523
 * @features: the features
 
524
 * @str: the string representation of a feature to check
 
525
 *
 
526
 * Example @str values are "dbus/mask/send", "caps/mask/audit_read", and
 
527
 * "policy/versions/v7".
 
528
 *
 
529
 * Returns: a bool specifying the support status of @str feature
 
530
 */
 
531
bool aa_features_supports(aa_features *features, const char *str)
 
532
{
 
533
        const char *features_string = features->string;
 
534
        struct component components[32];
 
535
        size_t i, num_components;
 
536
 
 
537
        /* Empty strings are not accepted. Neither are leading '/' chars. */
 
538
        if (!str || str[0] == '/')
 
539
                return false;
 
540
 
 
541
        /**
 
542
         * Break @str into an array of components. For example,
 
543
         * "mount/mask/mount" would turn into { "mount", 5 } as the first
 
544
         * component, { "mask", 4 } as the second, and { "mount", 5 } as the
 
545
         * third
 
546
         */
 
547
        num_components = tokenize_path_components(str, components,
 
548
                                sizeof(components) / sizeof(*components));
 
549
 
 
550
        /* At least one valid token is required */
 
551
        if (!num_components)
 
552
                return false;
 
553
 
 
554
        /* Ensure that all components are valid and found */
 
555
        for (i = 0; i < num_components; i++) {
 
556
                if (!walk_one(&features_string, &components[i], i == 0))
 
557
                        return false;
 
558
        }
 
559
 
 
560
        return true;
 
561
}