~ubuntu-branches/ubuntu/precise/apparmor/precise-security

« back to all changes in this revision

Viewing changes to .pc/0008-apparmor-lp963756.patch/parser/parser_regex.c

  • Committer: Package Import Robot
  • Author(s): Steve Beattie, Jamie Strandboge, Serge Hallyn, Steve Beattie
  • Date: 2012-04-12 06:17:42 UTC
  • Revision ID: package-import@ubuntu.com-20120412061742-9v75hjko2mjtbewv
Tags: 2.7.102-0ubuntu3
[ Jamie Strandboge ]
* debian/patches/0007-ubuntu-manpage-updates.patch: update apparmor(5)
  to describe Ubuntu's two-stage policy load and how to add utilize it
  when developing policy (LP: #974089)

[ Serge Hallyn ]
* debian/apparmor.init: do nothing in a container.  This can be
  removed once stacked profiles are supported and used by lxc.
  (LP: #978297)

[ Steve Beattie ]
* debian/patches/0008-apparmor-lp963756.patch: Fix permission mapping
  for change_profile onexec (LP: #963756)
* debian/patches/0009-apparmor-lp959560-part1.patch,
  debian/patches/0010-apparmor-lp959560-part2.patch: Update the parser
  to support the 'in' keyword for value lists, and make mount
  operations aware of 'in' keyword so they can affect the flags build
  list (LP: #959560)
* debian/patches/0011-apparmor-lp872446.patch: fix logprof missing
  exec events in complain mode (LP: #872446)
* debian/patches/0012-apparmor-lp978584.patch: allow inet6 access in
  dovecot imap-login profile (LP: #978584)
* debian/patches/0013-apparmor-lp800826.patch: fix libapparmor
  log parsing library from dropping apparmor network events that
  contain ip addresses or ports in them (LP: #800826)
* debian/patches/0014-apparmor-lp979095.patch: document new mount rule
  syntax and usage in apparmor.d(5) manpage (LP: #979095)
* debian/patches/0015-apparmor-lp963756.patch: Fix change_onexec
  for profiles without attachment specification (LP: #963756,
  LP: #978038)
* debian/patches/0016-apparmor-lp968956.patch: Fix protocol error when
  loading policy to kernels without compat patches (LP: #968956)
* debian/patches/0017-apparmor-lp979135.patch: Fix change_profile to
  grant access to /proc/attr api (LP: #979135)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *   Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
 
3
 *   NOVELL (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.
 
16
 */
 
17
 
 
18
#include <stdio.h>
 
19
#include <stdlib.h>
 
20
#include <stdarg.h>
 
21
#include <string.h>
 
22
#include <libintl.h>
 
23
#include <linux/limits.h>
 
24
#define _(s) gettext(s)
 
25
 
 
26
/* #define DEBUG */
 
27
 
 
28
#include "parser.h"
 
29
#include "libapparmor_re/apparmor_re.h"
 
30
#include "libapparmor_re/aare_rules.h"
 
31
#include "mount.h"
 
32
#include "policydb.h"
 
33
 
 
34
enum error_type {
 
35
        e_no_error,
 
36
        e_parse_error,
 
37
        e_buffer_overflow
 
38
};
 
39
 
 
40
/* Filters out multiple slashes (except if the first two are slashes,
 
41
 * that's a distinct namespace in linux) and trailing slashes.
 
42
 * NOTE: modifies in place the contents of the path argument */
 
43
 
 
44
static void filter_slashes(char *path)
 
45
{
 
46
        char *sptr, *dptr;
 
47
        BOOL seen_slash = 0;
 
48
 
 
49
        if (!path || (strlen(path) < 2))
 
50
                return;
 
51
 
 
52
        sptr = dptr = path;
 
53
 
 
54
        /* special case for linux // namespace */
 
55
        if (sptr[0] == '/' && sptr[1] == '/' && sptr[2] != '/') {
 
56
                sptr += 2;
 
57
                dptr += 2;
 
58
        }
 
59
 
 
60
        while (*sptr) {
 
61
                if (*sptr == '/') {
 
62
                        if (seen_slash) {
 
63
                                ++sptr;
 
64
                        } else {
 
65
                                *dptr++ = *sptr++;
 
66
                                seen_slash = TRUE;
 
67
                        }
 
68
                } else {
 
69
                        seen_slash = 0;
 
70
                        if (dptr < sptr) {
 
71
                                *dptr++ = *sptr++;
 
72
                        } else {
 
73
                                dptr++;
 
74
                                sptr++;
 
75
                        }
 
76
                }
 
77
        }
 
78
        *dptr = 0;
 
79
}
 
80
 
 
81
static pattern_t convert_aaregex_to_pcre(const char *aare, int anchor,
 
82
                                         char *pcre, size_t pcre_size,
 
83
                                         int *first_re_pos)
 
84
{
 
85
#define STORE(_src, _dest, _len) \
 
86
        if ((const char*)_dest + _len > (pcre + pcre_size)){ \
 
87
                error = e_buffer_overflow; \
 
88
        } else { \
 
89
                memcpy(_dest, _src, _len); \
 
90
                _dest += _len; \
 
91
        }
 
92
#define update_re_pos(X) if (!(*first_re_pos)) { *first_re_pos = (X); }
 
93
 
 
94
        *first_re_pos = 0;
 
95
 
 
96
        int ret = TRUE;
 
97
        /* flag to indicate input error */
 
98
        enum error_type error;
 
99
 
 
100
        const char *sptr;
 
101
        char *dptr;
 
102
        pattern_t ptype;
 
103
 
 
104
        BOOL bEscape = 0;       /* flag to indicate escape */
 
105
        int ingrouping = 0;     /* flag to indicate {} context */
 
106
        int incharclass = 0;    /* flag to indicate [ ] context */
 
107
 
 
108
        error = e_no_error;
 
109
        ptype = ePatternBasic;  /* assume no regex */
 
110
 
 
111
        sptr = aare;
 
112
        dptr = pcre;
 
113
 
 
114
        if (dfaflags & DFA_DUMP_RULE_EXPR)
 
115
                fprintf(stderr, "aare: %s   ->   ", aare);
 
116
 
 
117
        if (anchor)
 
118
                /* anchor beginning of regular expression */
 
119
                *dptr++ = '^';
 
120
 
 
121
        while (error == e_no_error && *sptr) {
 
122
                switch (*sptr) {
 
123
 
 
124
                case '\\':
 
125
                        /* concurrent escapes are allowed now and
 
126
                         * output as two consecutive escapes so that
 
127
                         * pcre won't interpret them
 
128
                         * \\\{...\\\} will be emitted as \\\{...\\\}
 
129
                         * for pcre matching.  For string matching
 
130
                         * and globbing only one escape is output
 
131
                         * this is done by stripping later
 
132
                         */
 
133
                        if (bEscape) {
 
134
                                STORE("\\\\", dptr, 2);
 
135
                        } else {
 
136
                                bEscape = TRUE;
 
137
                                ++sptr;
 
138
                                continue;       /*skip turning bEscape off */
 
139
                        }       /* bEscape */
 
140
                        break;
 
141
                case '*':
 
142
                        if (bEscape) {
 
143
                                /* '*' is a PCRE special character */
 
144
                                /* We store an escaped *, in case we
 
145
                                 * end up using this regex buffer (i.e another
 
146
                                 * non-escaped regex follows)
 
147
                                 */
 
148
                                STORE("\\*", dptr, 2);
 
149
                        } else {
 
150
                                if ((dptr > pcre) &&  *(dptr - 1) == '/') {
 
151
                                        #if 0
 
152
                                        // handle comment containing use
 
153
                                        // of C comment characters
 
154
                                        // /* /*/ and /** to describe paths
 
155
                                        //
 
156
                                        // modify what is emitted for * and **
 
157
                                        // when used as the only path
 
158
                                        // component
 
159
                                        // ex.
 
160
                                        // /* /*/ /**/ /**
 
161
                                        // this prevents these expressions
 
162
                                        // from matching directories or
 
163
                                        // invalid paths
 
164
                                        // in these case * and ** must
 
165
                                        // match at least 1 character to
 
166
                                        // get a valid path element.
 
167
                                        // ex.
 
168
                                        // /foo/* -> should not match /foo/
 
169
                                        // /foo/*bar -> should match /foo/bar
 
170
                                        // /*/foo -> should not match //foo
 
171
                                        #endif
 
172
                                        const char *s = sptr;
 
173
                                        while (*s == '*')
 
174
                                                s++;
 
175
                                        if (*s == '/' || !*s) {
 
176
                                                STORE("[^/\\x00]", dptr, 8);
 
177
                                        }
 
178
                                }
 
179
                                if (*(sptr + 1) == '*') {
 
180
                                        /* is this the first regex form we
 
181
                                         * have seen and also the end of
 
182
                                         * pattern? If so, we can use
 
183
                                         * optimised tail globbing rather
 
184
                                         * than full regex.
 
185
                                         */
 
186
                                        update_re_pos(sptr - aare);
 
187
                                        if (*(sptr + 2) == '\0' &&
 
188
                                            ptype == ePatternBasic) {
 
189
                                                ptype = ePatternTailGlob;
 
190
                                        } else {
 
191
                                                ptype = ePatternRegex;
 
192
                                        }
 
193
 
 
194
                                        STORE("[^\\x00]*", dptr, 8);
 
195
                                        sptr++;
 
196
                                } else {
 
197
                                        update_re_pos(sptr - aare);
 
198
                                        ptype = ePatternRegex;
 
199
                                        STORE("[^/\\x00]*", dptr, 9);
 
200
                                }       /* *(sptr+1) == '*' */
 
201
                        }       /* bEscape */
 
202
 
 
203
                        break;
 
204
 
 
205
                case '?':
 
206
                        if (bEscape) {
 
207
                                /* ? is not a PCRE regex character
 
208
                                 * so no need to escape, just skip
 
209
                                 * transform
 
210
                                 */
 
211
                                STORE(sptr, dptr, 1);
 
212
                        } else {
 
213
                                update_re_pos(sptr - aare);
 
214
                                ptype = ePatternRegex;
 
215
                                STORE("[^/\\x00]", dptr, 8);
 
216
                        }
 
217
                        break;
 
218
 
 
219
                case '[':
 
220
                        if (bEscape) {
 
221
                                /* [ is a PCRE special character */
 
222
                                STORE("\\[", dptr, 2);
 
223
                        } else {
 
224
                                update_re_pos(sptr - aare);
 
225
                                incharclass = 1;
 
226
                                ptype = ePatternRegex;
 
227
                                STORE(sptr, dptr, 1);
 
228
                        }
 
229
                        break;
 
230
 
 
231
                case ']':
 
232
                        if (bEscape) {
 
233
                                /* ] is a PCRE special character */
 
234
                                STORE("\\]", dptr, 2);
 
235
                        } else {
 
236
                                incharclass = 0;
 
237
                                STORE(sptr, dptr, 1);
 
238
                        }
 
239
                        break;
 
240
 
 
241
                case '{':
 
242
                        if (bEscape) {
 
243
                                /* { is a PCRE special character */
 
244
                                STORE("\\{", dptr, 2);
 
245
                        } else {
 
246
                                if (ingrouping) {
 
247
                                        error = e_parse_error;
 
248
                                        PERROR(_("%s: Illegal open {, nesting groupings not allowed\n"),
 
249
                                               progname);
 
250
                                } else {
 
251
                                        update_re_pos(sptr - aare);
 
252
                                        ingrouping = 1;
 
253
                                        ptype = ePatternRegex;
 
254
                                        STORE("(", dptr, 1);
 
255
                                }
 
256
                        }
 
257
                        break;
 
258
 
 
259
                case '}':
 
260
                        if (bEscape) {
 
261
                                /* { is a PCRE special character */
 
262
                                STORE("\\}", dptr, 2);
 
263
                        } else {
 
264
                                if (ingrouping <= 1) {
 
265
 
 
266
                                        error = e_parse_error;
 
267
 
 
268
                                        if (ingrouping == 1) {
 
269
                                                PERROR(_("%s: Regex grouping error: Invalid number of items between {}\n"),
 
270
                                                       progname);
 
271
 
 
272
                                                ingrouping = 0; /* prevent further errors */
 
273
 
 
274
                                        } else {        /* ingrouping == 0 */
 
275
                                                PERROR(_("%s: Regex grouping error: Invalid close }, no matching open { detected\n"),
 
276
                                                       progname);
 
277
                                        }
 
278
                                } else {        /* ingrouping > 1 */
 
279
                                        ingrouping = 0;
 
280
                                        STORE(")", dptr, 1);
 
281
                                }
 
282
                        }       /* bEscape */
 
283
 
 
284
                        break;
 
285
 
 
286
                case ',':
 
287
                        if (bEscape) {
 
288
                                /* , is not a PCRE regex character
 
289
                                 * so no need to escape, just skip
 
290
                                 * transform
 
291
                                 */
 
292
                                STORE(sptr, dptr, 1);
 
293
                        } else {
 
294
                                if (ingrouping) {
 
295
                                        ++ingrouping;
 
296
                                        STORE("|", dptr, 1);
 
297
                                } else {
 
298
                                        STORE(sptr, dptr, 1);
 
299
                                }
 
300
                        }
 
301
                        break;
 
302
 
 
303
                        /* these are special outside of character
 
304
                         * classes but not in them */
 
305
                case '^':
 
306
                case '$':
 
307
                        if (incharclass) {
 
308
                                STORE(sptr, dptr, 1);
 
309
                        } else {
 
310
                                STORE("\\", dptr, 1);
 
311
                                STORE(sptr, dptr, 1);
 
312
                        }
 
313
                        break;
 
314
 
 
315
                        /*
 
316
                         * Not a subdomain regex, but needs to be
 
317
                         * escaped as it is a pcre metacharacter which
 
318
                         * we don't want to support. We always escape
 
319
                         * these, so no need to check bEscape
 
320
                         */
 
321
                case '.':
 
322
                case '+':
 
323
                case '|':
 
324
                case '(':
 
325
                case ')':
 
326
                        STORE("\\", dptr, 1);
 
327
                        // fall through to default
 
328
 
 
329
                default:
 
330
                        STORE(sptr, dptr, 1);
 
331
                        break;
 
332
                }       /* switch (*sptr) */
 
333
 
 
334
                bEscape = FALSE;
 
335
                ++sptr;
 
336
        }               /* while error == e_no_error && *sptr) */
 
337
 
 
338
        if (ingrouping > 0 || incharclass) {
 
339
                error = e_parse_error;
 
340
 
 
341
                PERROR(_("%s: Regex grouping error: Unclosed grouping or character class, expecting close }\n"),
 
342
                       progname);
 
343
        }
 
344
 
 
345
        /* anchor end and terminate pattern string */
 
346
        if ((error == e_no_error) && anchor) {
 
347
                STORE("$" , dptr, 1);
 
348
        }
 
349
        if (error == e_no_error) {
 
350
                STORE("", dptr, 1);
 
351
        }
 
352
        /* check error  again, as above STORE may have set it */
 
353
        if (error != e_no_error) {
 
354
                if (error == e_buffer_overflow) {
 
355
                        PERROR(_("%s: Internal buffer overflow detected, %d characters exceeded\n"),
 
356
                               progname, PATH_MAX);
 
357
                }
 
358
 
 
359
                PERROR(_("%s: Unable to parse input line '%s'\n"),
 
360
                       progname, aare);
 
361
 
 
362
                ret = FALSE;
 
363
                goto out;
 
364
        }
 
365
 
 
366
out:
 
367
        if (ret == FALSE)
 
368
                ptype = ePatternInvalid;
 
369
 
 
370
        if (dfaflags & DFA_DUMP_RULE_EXPR)
 
371
                fprintf(stderr, "%s\n", pcre);
 
372
 
 
373
        return ptype;
 
374
}
 
375
 
 
376
static const char *local_name(const char *name)
 
377
{
 
378
        const char *t;
 
379
 
 
380
        for (t = strstr(name, "//") ; t ; t = strstr(name, "//"))
 
381
                name = t + 2;
 
382
 
 
383
        return name;
 
384
}
 
385
 
 
386
static int process_profile_name_xmatch(struct codomain *cod)
 
387
{
 
388
        char tbuf[PATH_MAX + 3];        /* +3 for ^, $ and \0 */
 
389
        pattern_t ptype;
 
390
        const char *name;
 
391
 
 
392
        /* don't filter_slashes for profile names */
 
393
        if (cod->attachment)
 
394
                name = cod->attachment;
 
395
        else
 
396
                name = local_name(cod->name);
 
397
        ptype = convert_aaregex_to_pcre(name, 0, tbuf, PATH_MAX + 3,
 
398
                                        &cod->xmatch_len);
 
399
        if (ptype == ePatternBasic)
 
400
                cod->xmatch_len = strlen(name);
 
401
 
 
402
        if (ptype == ePatternInvalid) {
 
403
                PERROR(_("%s: Invalid profile name '%s' - bad regular expression\n"), progname, name);
 
404
                return FALSE;
 
405
        } else if (ptype == ePatternBasic && !(cod->altnames || cod->attachment)) {
 
406
                /* no regex so do not set xmatch */
 
407
                cod->xmatch = NULL;
 
408
                cod->xmatch_len = 0;
 
409
                cod->xmatch_size = 0;
 
410
        } else {
 
411
                /* build a dfa */
 
412
                aare_ruleset_t *rule = aare_new_ruleset(0);
 
413
                if (!rule)
 
414
                        return FALSE;
 
415
                if (!aare_add_rule(rule, tbuf, 0, AA_MAY_EXEC, 0, dfaflags)) {
 
416
                        aare_delete_ruleset(rule);
 
417
                        return FALSE;
 
418
                }
 
419
                if (cod->altnames) {
 
420
                        struct alt_name *alt;
 
421
                        list_for_each(cod->altnames, alt) {
 
422
                                int len;
 
423
                                ptype = convert_aaregex_to_pcre(alt->name, 0,
 
424
                                                                tbuf,
 
425
                                                                PATH_MAX + 3,
 
426
                                                                &len);
 
427
                                if (ptype == ePatternBasic)
 
428
                                        len = strlen(alt->name);
 
429
                                if (len < cod->xmatch_len)
 
430
                                        cod->xmatch_len = len;
 
431
                                if (!aare_add_rule(rule, tbuf, 0, AA_MAY_EXEC, 0, dfaflags)) {
 
432
                                        aare_delete_ruleset(rule);
 
433
                                        return FALSE;
 
434
                                }
 
435
                        }
 
436
                }
 
437
                cod->xmatch = aare_create_dfa(rule, &cod->xmatch_size,
 
438
                                              dfaflags);
 
439
                aare_delete_ruleset(rule);
 
440
                if (!cod->xmatch)
 
441
                        return FALSE;
 
442
        }
 
443
 
 
444
        return TRUE;
 
445
}
 
446
 
 
447
static int process_dfa_entry(aare_ruleset_t *dfarules, struct cod_entry *entry)
 
448
{
 
449
        char tbuf[PATH_MAX + 3];        /* +3 for ^, $ and \0 */
 
450
        pattern_t ptype;
 
451
        int pos;
 
452
 
 
453
        if (!entry)             /* shouldn't happen */
 
454
                return TRUE;
 
455
 
 
456
 
 
457
        if (entry->mode & ~AA_CHANGE_PROFILE)
 
458
                filter_slashes(entry->name);
 
459
        ptype = convert_aaregex_to_pcre(entry->name, 0, tbuf, PATH_MAX+3, &pos);
 
460
        if (ptype == ePatternInvalid)
 
461
                return FALSE;
 
462
 
 
463
        entry->pattern_type = ptype;
 
464
 
 
465
        /* ix implies m but the apparmor module does not add m bit to
 
466
         * dfa states like it does for pcre
 
467
         */
 
468
        if ((entry->mode >> AA_OTHER_SHIFT) & AA_EXEC_INHERIT)
 
469
                entry->mode |= AA_EXEC_MMAP << AA_OTHER_SHIFT;
 
470
        if ((entry->mode >> AA_USER_SHIFT) & AA_EXEC_INHERIT)
 
471
                entry->mode |= AA_EXEC_MMAP << AA_USER_SHIFT;
 
472
 
 
473
        /* relying on ptrace and change_profile not getting merged earlier */
 
474
 
 
475
        /* the link bit on the first pair entry should not get masked
 
476
         * out by a deny rule, as both pieces of the link pair must
 
477
         * match.  audit info for the link is carried on the second
 
478
         * entry of the pair
 
479
         */
 
480
        if (entry->deny && (entry->mode & AA_LINK_BITS)) {
 
481
                if (!aare_add_rule(dfarules, tbuf, entry->deny,
 
482
                                   entry->mode & ~AA_LINK_BITS,
 
483
                                   entry->audit & ~AA_LINK_BITS, dfaflags))
 
484
                        return FALSE;
 
485
        } else if (entry->mode & ~AA_CHANGE_PROFILE) {
 
486
                if (!aare_add_rule(dfarules, tbuf, entry->deny, entry->mode,
 
487
                                   entry->audit, dfaflags))
 
488
                        return FALSE;
 
489
        }
 
490
 
 
491
        if (entry->mode & (AA_LINK_BITS)) {
 
492
                /* add the pair rule */
 
493
                char lbuf[PATH_MAX + 8];
 
494
                int perms = AA_LINK_BITS & entry->mode;
 
495
                char *vec[2];
 
496
                int pos;
 
497
                vec[0] = tbuf;
 
498
                if (entry->link_name) {
 
499
                        ptype = convert_aaregex_to_pcre(entry->link_name, 0, lbuf, PATH_MAX + 8, &pos);
 
500
                        if (ptype == ePatternInvalid)
 
501
                                return FALSE;
 
502
                        if (entry->subset)
 
503
                                perms |= LINK_TO_LINK_SUBSET(perms);
 
504
                        vec[1] = lbuf;
 
505
                } else {
 
506
                        perms |= LINK_TO_LINK_SUBSET(perms);
 
507
                        vec[1] = "/[^/].*";
 
508
                }
 
509
                if (!aare_add_rule_vec(dfarules, entry->deny, perms, entry->audit & AA_LINK_BITS, 2, vec, dfaflags))
 
510
                        return FALSE;
 
511
        }
 
512
        if (entry->mode & AA_CHANGE_PROFILE) {
 
513
                if (entry->namespace) {
 
514
                        char *vec[2];
 
515
                        char lbuf[PATH_MAX + 8];
 
516
                        int pos;
 
517
                        ptype = convert_aaregex_to_pcre(entry->namespace, 0, lbuf, PATH_MAX + 8, &pos);
 
518
                        vec[0] = lbuf;
 
519
                        vec[1] = tbuf;
 
520
                        if (!aare_add_rule_vec(dfarules, 0, AA_CHANGE_PROFILE, 0, 2, vec, dfaflags))
 
521
                            return FALSE;
 
522
                } else {
 
523
                  if (!aare_add_rule(dfarules, tbuf, 0, AA_CHANGE_PROFILE, 0, dfaflags))
 
524
                                return FALSE;
 
525
                }
 
526
        }
 
527
        if (entry->mode & (AA_USER_PTRACE | AA_OTHER_PTRACE)) {
 
528
                int mode = entry->mode & (AA_USER_PTRACE | AA_OTHER_PTRACE);
 
529
                if (entry->namespace) {
 
530
                        char *vec[2];
 
531
                        vec[0] = entry->namespace;
 
532
                        vec[1] = entry->name;
 
533
                        if (!aare_add_rule_vec(dfarules, 0, mode, 0, 2, vec, dfaflags))
 
534
                            return FALSE;
 
535
                } else {
 
536
                  if (!aare_add_rule(dfarules, entry->name, 0, mode, 0, dfaflags))
 
537
                                return FALSE;
 
538
                }
 
539
        }
 
540
        return TRUE;
 
541
}
 
542
 
 
543
int post_process_entries(struct codomain *cod)
 
544
{
 
545
        int ret = TRUE;
 
546
        struct cod_entry *entry;
 
547
        int count = 0;
 
548
 
 
549
        list_for_each(cod->entries, entry) {
 
550
                if (regex_type == AARE_DFA &&
 
551
                    !process_dfa_entry(cod->dfarules, entry))
 
552
                        ret = FALSE;
 
553
                count++;
 
554
        }
 
555
 
 
556
        cod->dfarule_count = count;
 
557
        return ret;
 
558
}
 
559
 
 
560
int process_regex(struct codomain *cod)
 
561
{
 
562
        int error = -1;
 
563
 
 
564
        if (regex_type == AARE_DFA) {
 
565
                if (!process_profile_name_xmatch(cod))
 
566
                        goto out;
 
567
 
 
568
                cod->dfarules = aare_new_ruleset(0);
 
569
                if (!cod->dfarules)
 
570
                        goto out;
 
571
        }
 
572
        if (!post_process_entries(cod))
 
573
                goto out;
 
574
 
 
575
        if (regex_type == AARE_DFA && cod->dfarule_count > 0) {
 
576
                cod->dfa = aare_create_dfa(cod->dfarules, &cod->dfa_size,
 
577
                                           dfaflags);
 
578
                aare_delete_ruleset(cod->dfarules);
 
579
                cod->dfarules = NULL;
 
580
                if (!cod->dfa)
 
581
                        goto out;
 
582
/*
 
583
                if (cod->dfa_size == 0) {
 
584
                        PERROR(_("profile %s: has merged rules (%s) with "
 
585
                                 "multiple x modifiers\n"),
 
586
                               cod->name, (char *) cod->dfa);
 
587
                        free(cod->dfa);
 
588
                        cod->dfa = NULL;
 
589
                        goto out;
 
590
                }
 
591
*/
 
592
        }
 
593
        /*
 
594
         * Post process subdomain(s):
 
595
         *
 
596
         * They are chained from the toplevel subdomain pointer
 
597
         * thru each <codomain> next pointer.
 
598
 
 
599
         * i.e first subdomain is list->subdomain
 
600
         *    second subdomain is list->subdomain->next
 
601
         *
 
602
         * N.B sub-subdomains are not valid so:
 
603
         * if (list->subdomain) {
 
604
         *    assert(list->subdomain->subdomain == NULL)
 
605
         * }
 
606
         */
 
607
        if (process_hat_regex(cod) != 0)
 
608
                goto out;
 
609
 
 
610
        error = 0;
 
611
 
 
612
out:
 
613
        return error;
 
614
}
 
615
 
 
616
static int build_list_val_expr(char *buffer, int size, struct value_list *list)
 
617
{
 
618
        struct value_list *ent;
 
619
        char tmp[PATH_MAX + 3];
 
620
        char *p;
 
621
        int len;
 
622
        pattern_t ptype;
 
623
        int pos;
 
624
 
 
625
        if (!list) {
 
626
                strncpy(buffer, "[^\\000]*", size);
 
627
                return TRUE;
 
628
        }
 
629
 
 
630
        p = buffer;
 
631
        strncpy(p, "(", size - (p - buffer));
 
632
        p++;
 
633
        if (p > buffer + size)
 
634
                goto fail;
 
635
 
 
636
        ptype = convert_aaregex_to_pcre(list->value, 0, tmp, PATH_MAX+3, &pos);
 
637
        if (ptype == ePatternInvalid)
 
638
                goto fail;
 
639
 
 
640
        len = strlen(tmp);
 
641
        if (len > size - (p - buffer))
 
642
                goto fail;
 
643
        strcpy(p, tmp);
 
644
        p += len;
 
645
 
 
646
        list_for_each(list->next, ent) {
 
647
                ptype = convert_aaregex_to_pcre(ent->value, 0, tmp,
 
648
                                                PATH_MAX+3, &pos);
 
649
                if (ptype == ePatternInvalid)
 
650
                        goto fail;
 
651
 
 
652
                strncpy(p, "|", size - (p - buffer));
 
653
                p++;
 
654
                len = strlen(tmp);
 
655
                if (len > size - (p - buffer))
 
656
                        goto fail;
 
657
                strcpy(p, tmp);
 
658
                p += len;
 
659
        }
 
660
        strncpy(p, ")", size - (p - buffer));
 
661
        p++;
 
662
        if (p > buffer + size)
 
663
                goto fail;
 
664
 
 
665
        return TRUE;
 
666
fail:
 
667
        return FALSE;
 
668
}
 
669
 
 
670
static int convert_entry(char *buffer, int size, char *entry)
 
671
{
 
672
        pattern_t ptype;
 
673
        int pos;
 
674
 
 
675
        if (entry) {
 
676
                ptype = convert_aaregex_to_pcre(entry, 0, buffer, size, &pos);
 
677
                if (ptype == ePatternInvalid)
 
678
                        return FALSE;
 
679
        } else {
 
680
                /* match any char except \000 0 or more times */
 
681
                if (size < 8)
 
682
                        return FALSE;
 
683
 
 
684
                strcpy(buffer, "[^\\000]*");
 
685
        }
 
686
        return TRUE;
 
687
}
 
688
 
 
689
static int build_mnt_flags(char *buffer, int size, unsigned int flags,
 
690
                           unsigned int inv_flags)
 
691
{
 
692
        char *p = buffer;
 
693
        int i, len = 0;
 
694
 
 
695
        if (flags == MS_ALL_FLAGS) {
 
696
                /* all flags are optional */
 
697
                len = snprintf(p, size, "[^\\000]*");
 
698
                if (len < 0 || len >= size)
 
699
                        return FALSE;
 
700
                return TRUE;
 
701
        }
 
702
        for (i = 0; i <= 31; ++i) {
 
703
                if ((flags & inv_flags) & (1 << i))
 
704
                        len = snprintf(p, size, "(\\x%02x|)", i + 1);
 
705
                else if (flags & (1 << i))
 
706
                        len = snprintf(p, size, "\\x%02x", i + 1);
 
707
                else    /* no entry = not set */
 
708
                        continue;
 
709
 
 
710
                if (len < 0 || len >= size)
 
711
                        return FALSE;
 
712
                p += len;
 
713
                size -= len;
 
714
        }
 
715
 
 
716
        /* this needs to go once the backend is updated. */
 
717
        if (buffer == p) {
 
718
                /* match nothing - use impossible 254 as regex parser doesn't
 
719
                 * like the empty string
 
720
                 */
 
721
                if (size < 9)
 
722
                        return FALSE;
 
723
 
 
724
                strcpy(p, "(\\xfe|)");
 
725
        }
 
726
 
 
727
        return TRUE;
 
728
}
 
729
 
 
730
static int build_mnt_opts(char *buffer, int size, struct value_list *opts)
 
731
{
 
732
        struct value_list *ent;
 
733
        char tmp[PATH_MAX + 3];
 
734
        char *p;
 
735
        int len;
 
736
        pattern_t ptype;
 
737
        int pos;
 
738
 
 
739
        if (!opts) {
 
740
                if (size < 8)
 
741
                        return FALSE;
 
742
                strncpy(buffer, "[^\\000]*", size);
 
743
                return TRUE;
 
744
        }
 
745
 
 
746
        p = buffer;
 
747
        list_for_each(opts, ent) {
 
748
                ptype = convert_aaregex_to_pcre(ent->value, 0, tmp,
 
749
                                                PATH_MAX+3, &pos);
 
750
                if (ptype == ePatternInvalid)
 
751
                        goto fail;
 
752
 
 
753
                len = strlen(tmp);
 
754
                if (len > size - (p - buffer))
 
755
                        goto fail;
 
756
                strcpy(p, tmp);
 
757
                p += len;
 
758
                if (ent->next && size - (p - buffer) > 1) {
 
759
                        *p++ = ',';
 
760
                        *p = 0;
 
761
                }
 
762
        }
 
763
 
 
764
        return TRUE;
 
765
 
 
766
fail:
 
767
        return FALSE;
 
768
}
 
769
 
 
770
static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
 
771
{
 
772
        char mntbuf[PATH_MAX + 3];
 
773
        char devbuf[PATH_MAX + 3];
 
774
        char typebuf[PATH_MAX + 3];
 
775
        char flagsbuf[PATH_MAX + 3];
 
776
        char optsbuf[PATH_MAX + 3];
 
777
        char *p, *vec[5];
 
778
        int count = 0;
 
779
        unsigned int flags, inv_flags;
 
780
 
 
781
        /* a single mount rule may result in multiple matching rules being
 
782
         * created in the backend to cover all the possible choices
 
783
         */
 
784
 
 
785
        if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_REMOUNT)
 
786
            && !entry->device && !entry->dev_type) {
 
787
                int allow;
 
788
                /* remount can't be conditional on device and type */
 
789
                p = mntbuf;
 
790
                /* rule class single byte header */
 
791
                p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
 
792
                if (entry->mnt_point) {
 
793
                        /* both device && mnt_point or just mnt_point */
 
794
                        if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
 
795
                                goto fail;
 
796
                        vec[0] = mntbuf;
 
797
                } else {
 
798
                        if (!convert_entry(p, PATH_MAX +3, entry->device))
 
799
                                goto fail;
 
800
                        vec[0] = mntbuf;
 
801
                }
 
802
                /* skip device */
 
803
                if (!convert_entry(devbuf, PATH_MAX +3, NULL))
 
804
                        goto fail;
 
805
                vec[1] = devbuf;
 
806
                /* skip type */
 
807
                vec[2] = devbuf;
 
808
 
 
809
                flags = entry->flags;
 
810
                inv_flags = entry->inv_flags;
 
811
                if (flags != MS_ALL_FLAGS)
 
812
                        flags &= MS_REMOUNT_FLAGS;
 
813
                if (inv_flags != MS_ALL_FLAGS)
 
814
                        flags &= MS_REMOUNT_FLAGS;
 
815
                if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags))
 
816
                        goto fail;
 
817
                vec[3] = flagsbuf;
 
818
                if (!build_mnt_opts(optsbuf, PATH_MAX, entry->opts))
 
819
                        goto fail;
 
820
 
 
821
                if (entry->opts)
 
822
                        allow = AA_MATCH_CONT;
 
823
                else
 
824
                        allow = entry->allow;
 
825
 
 
826
                /* rule for match without required data || data MATCH_CONT */
 
827
                if (!aare_add_rule_vec(dfarules, entry->deny, allow,
 
828
                                       entry->audit | AA_AUDIT_MNT_DATA, 4,
 
829
                                       vec, dfaflags))
 
830
                        goto fail;
 
831
                count++;
 
832
 
 
833
                if (entry->opts) {
 
834
                        /* rule with data match required */
 
835
                        if (!build_mnt_opts(optsbuf, PATH_MAX, entry->opts))
 
836
                                goto fail;
 
837
                        vec[4] = optsbuf;
 
838
                        if (!aare_add_rule_vec(dfarules, entry->deny,
 
839
                                               entry->allow,
 
840
                                               entry->audit | AA_AUDIT_MNT_DATA,
 
841
                                               5, vec, dfaflags))
 
842
                                goto fail;
 
843
                        count++;
 
844
                }
 
845
        }
 
846
        if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_BIND)
 
847
            && !entry->dev_type && !entry->opts) {
 
848
                /* bind mount rules can't be conditional on dev_type or data */
 
849
                p = mntbuf;
 
850
                /* rule class single byte header */
 
851
                p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
 
852
                if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
 
853
                        goto fail;
 
854
                vec[0] = mntbuf;
 
855
                if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
 
856
                        goto fail;
 
857
                vec[1] = devbuf;
 
858
                if (!convert_entry(typebuf, PATH_MAX +3, NULL))
 
859
                        goto fail;
 
860
                vec[2] = typebuf;
 
861
 
 
862
                flags = entry->flags;
 
863
                inv_flags = entry->inv_flags;
 
864
                if (flags != MS_ALL_FLAGS)
 
865
                        flags &= MS_BIND_FLAGS;
 
866
                if (inv_flags != MS_ALL_FLAGS)
 
867
                        flags &= MS_BIND_FLAGS;
 
868
                if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags))
 
869
                        goto fail;
 
870
                vec[3] = flagsbuf;
 
871
                if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
 
872
                                       entry->audit, 4, vec, dfaflags))
 
873
                        goto fail;
 
874
                count++;
 
875
        }
 
876
        if ((entry->allow & AA_MAY_MOUNT) &&
 
877
            (entry->flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED))
 
878
            && !entry->device && !entry->dev_type && !entry->opts) {
 
879
                /* change type base rules can not be conditional on device,
 
880
                 * device type or data
 
881
                 */
 
882
                p = mntbuf;
 
883
                /* rule class single byte header */
 
884
                p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
 
885
                if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
 
886
                        goto fail;
 
887
                vec[0] = mntbuf;
 
888
                /* skip device and type */
 
889
                if (!convert_entry(devbuf, PATH_MAX +3, NULL))
 
890
                        goto fail;
 
891
                vec[1] = devbuf;
 
892
                vec[2] = devbuf;
 
893
 
 
894
                flags = entry->flags;
 
895
                inv_flags = entry->inv_flags;
 
896
                if (flags != MS_ALL_FLAGS)
 
897
                        flags &= MS_MAKE_FLAGS;
 
898
                if (inv_flags != MS_ALL_FLAGS)
 
899
                        flags &= MS_MAKE_FLAGS;
 
900
                if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags))
 
901
                        goto fail;
 
902
                vec[3] = flagsbuf;
 
903
                if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
 
904
                                       entry->audit, 4, vec, dfaflags))
 
905
                        goto fail;
 
906
                count++;
 
907
        }
 
908
        if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_MOVE)
 
909
            && !entry->dev_type && !entry->opts) {
 
910
                /* mount move rules can not be conditional on dev_type,
 
911
                 * or data
 
912
                 */
 
913
                p = mntbuf;
 
914
                /* rule class single byte header */
 
915
                p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
 
916
                if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
 
917
                        goto fail;
 
918
                vec[0] = mntbuf;
 
919
                if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
 
920
                        goto fail;
 
921
                vec[1] = devbuf;
 
922
                /* skip type */
 
923
                if (!convert_entry(typebuf, PATH_MAX +3, NULL))
 
924
                        goto fail;
 
925
                vec[2] = typebuf;
 
926
 
 
927
                flags = entry->flags;
 
928
                inv_flags = entry->inv_flags;
 
929
                if (flags != MS_ALL_FLAGS)
 
930
                        flags &= MS_MOVE_FLAGS;
 
931
                if (inv_flags != MS_ALL_FLAGS)
 
932
                        flags &= MS_MOVE_FLAGS;
 
933
                if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags))
 
934
                        goto fail;
 
935
                vec[3] = flagsbuf;
 
936
                if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
 
937
                                       entry->audit, 4, vec, dfaflags))
 
938
                        goto fail;
 
939
                count++;
 
940
        }
 
941
        if ((entry->allow & AA_MAY_MOUNT) &&
 
942
            (entry->flags | entry->inv_flags) & ~MS_CMDS) {
 
943
                int allow;
 
944
                /* generic mount if flags are set that are not covered by
 
945
                 * above commands
 
946
                 */
 
947
                p = mntbuf;
 
948
                /* rule class single byte header */
 
949
                p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
 
950
                if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
 
951
                        goto fail;
 
952
                vec[0] = mntbuf;
 
953
                if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
 
954
                        goto fail;
 
955
                vec[1] = devbuf;
 
956
                if (!build_list_val_expr(typebuf, PATH_MAX+2, entry->dev_type))
 
957
                        goto fail;
 
958
                vec[2] = typebuf;
 
959
 
 
960
                flags = entry->flags;
 
961
                inv_flags = entry->inv_flags;
 
962
                if (flags != MS_ALL_FLAGS)
 
963
                        flags &= ~MS_CMDS;
 
964
                if (inv_flags != MS_ALL_FLAGS)
 
965
                        flags &= ~MS_CMDS;
 
966
                if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags))
 
967
                        goto fail;
 
968
                vec[3] = flagsbuf;
 
969
 
 
970
                if (entry->opts)
 
971
                        allow = AA_MATCH_CONT;
 
972
                else
 
973
                        allow = entry->allow;
 
974
 
 
975
                /* rule for match without required data || data MATCH_CONT */
 
976
                if (!aare_add_rule_vec(dfarules, entry->deny, allow,
 
977
                                       entry->audit | AA_AUDIT_MNT_DATA, 4,
 
978
                                       vec, dfaflags))
 
979
                        goto fail;
 
980
                count++;
 
981
 
 
982
                if (entry->opts) {
 
983
                        /* rule with data match required */
 
984
                        if (!build_mnt_opts(optsbuf, PATH_MAX, entry->opts))
 
985
                                goto fail;
 
986
                        vec[4] = optsbuf;
 
987
                        if (!aare_add_rule_vec(dfarules, entry->deny,
 
988
                                               entry->allow,
 
989
                                               entry->audit | AA_AUDIT_MNT_DATA,
 
990
                                               5, vec, dfaflags))
 
991
                                goto fail;
 
992
                        count++;
 
993
                }
 
994
        }
 
995
        if (entry->allow & AA_MAY_UMOUNT) {
 
996
                p = mntbuf;
 
997
                /* rule class single byte header */
 
998
                p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
 
999
                if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
 
1000
                        goto fail;
 
1001
                vec[0] = mntbuf;
 
1002
                if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
 
1003
                                       entry->audit, 1, vec, dfaflags))
 
1004
                        goto fail;
 
1005
                count++;
 
1006
        }
 
1007
        if (entry->allow & AA_MAY_PIVOTROOT) {
 
1008
                p = mntbuf;
 
1009
                /* rule class single byte header */
 
1010
                p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
 
1011
                if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
 
1012
                        goto fail;
 
1013
                vec[0] = mntbuf;
 
1014
                if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
 
1015
                        goto fail;
 
1016
                vec[1] = devbuf;
 
1017
                if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
 
1018
                                       entry->audit, 2, vec, dfaflags))
 
1019
                        goto fail;
 
1020
                count++;
 
1021
        }
 
1022
 
 
1023
        if (!count)
 
1024
                /* didn't actually encode anything */
 
1025
                goto fail;
 
1026
 
 
1027
        return TRUE;
 
1028
 
 
1029
fail:
 
1030
        PERROR("Enocoding of mount rule failed\n");
 
1031
        return FALSE;
 
1032
}
 
1033
 
 
1034
 
 
1035
int post_process_policydb_ents(struct codomain *cod)
 
1036
{
 
1037
        int ret = TRUE;
 
1038
        int count = 0;
 
1039
 
 
1040
        /* Add fns for rules that should be added to policydb here */
 
1041
        if (cod->mnt_ents && kernel_supports_mount) {
 
1042
                struct mnt_entry *entry;
 
1043
                list_for_each(cod->mnt_ents, entry) {
 
1044
                        if (regex_type == AARE_DFA &&
 
1045
                            !process_mnt_entry(cod->policy_rules, entry))
 
1046
                                ret = FALSE;
 
1047
                        count++;
 
1048
                }
 
1049
        } else if (cod->mnt_ents && !kernel_supports_mount)
 
1050
                pwarn("profile %s mount rules not enforced\n", cod->name);
 
1051
 
 
1052
        cod->policy_rule_count = count;
 
1053
        return ret;
 
1054
}
 
1055
 
 
1056
int process_policydb(struct codomain *cod)
 
1057
{
 
1058
        int error = -1;
 
1059
 
 
1060
        if (regex_type == AARE_DFA) {
 
1061
                cod->policy_rules = aare_new_ruleset(0);
 
1062
                if (!cod->policy_rules)
 
1063
                        goto out;
 
1064
        }
 
1065
 
 
1066
        if (!post_process_policydb_ents(cod))
 
1067
                goto out;
 
1068
 
 
1069
        if (regex_type == AARE_DFA && cod->policy_rule_count > 0) {
 
1070
                cod->policy_dfa = aare_create_dfa(cod->policy_rules,
 
1071
                                                  &cod->policy_dfa_size,
 
1072
                                                  dfaflags);
 
1073
                aare_delete_ruleset(cod->policy_rules);
 
1074
                cod->policy_rules = NULL;
 
1075
                if (!cod->policy_dfa)
 
1076
                        goto out;
 
1077
        }
 
1078
 
 
1079
        aare_reset_matchflags();
 
1080
        if (process_hat_policydb(cod) != 0)
 
1081
                goto out;
 
1082
 
 
1083
        error = 0;
 
1084
 
 
1085
out:
 
1086
        return error;
 
1087
}
 
1088
 
 
1089
void reset_regex(void)
 
1090
{
 
1091
        aare_reset_matchflags();
 
1092
}
 
1093
 
 
1094
#ifdef UNIT_TEST
 
1095
static int test_filter_slashes(void)
 
1096
{
 
1097
        int rc = 0;
 
1098
        char *test_string;
 
1099
 
 
1100
        test_string = strdup("///foo//////f//oo////////////////");
 
1101
        filter_slashes(test_string);
 
1102
        MY_TEST(strcmp(test_string, "/foo/f/oo/") == 0, "simple tests");
 
1103
 
 
1104
        test_string = strdup("/foo/f/oo");
 
1105
        filter_slashes(test_string);
 
1106
        MY_TEST(strcmp(test_string, "/foo/f/oo") == 0, "simple test for no changes");
 
1107
 
 
1108
        test_string = strdup("/");
 
1109
        filter_slashes(test_string);
 
1110
        MY_TEST(strcmp(test_string, "/") == 0, "simple test for '/'");
 
1111
 
 
1112
        test_string = strdup("");
 
1113
        filter_slashes(test_string);
 
1114
        MY_TEST(strcmp(test_string, "") == 0, "simple test for ''");
 
1115
 
 
1116
        test_string = strdup("//usr");
 
1117
        filter_slashes(test_string);
 
1118
        MY_TEST(strcmp(test_string, "//usr") == 0, "simple test for // namespace");
 
1119
 
 
1120
        test_string = strdup("//");
 
1121
        filter_slashes(test_string);
 
1122
        MY_TEST(strcmp(test_string, "//") == 0, "simple test 2 for // namespace");
 
1123
 
 
1124
        test_string = strdup("///usr");
 
1125
        filter_slashes(test_string);
 
1126
        MY_TEST(strcmp(test_string, "/usr") == 0, "simple test for ///usr");
 
1127
 
 
1128
        test_string = strdup("///");
 
1129
        filter_slashes(test_string);
 
1130
        MY_TEST(strcmp(test_string, "/") == 0, "simple test for ///");
 
1131
 
 
1132
        test_string = strdup("/a/");
 
1133
        filter_slashes(test_string);
 
1134
        MY_TEST(strcmp(test_string, "/a/") == 0, "simple test for /a/");
 
1135
 
 
1136
        return rc;
 
1137
}
 
1138
 
 
1139
int main(void)
 
1140
{
 
1141
        int rc = 0;
 
1142
        int retval;
 
1143
 
 
1144
        retval = test_filter_slashes();
 
1145
        if (retval != 0)
 
1146
                rc = retval;
 
1147
 
 
1148
        return rc;
 
1149
}
 
1150
#endif /* UNIT_TEST */