819 lines
18 KiB
C
819 lines
18 KiB
C
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include "selinux_internal.h"
|
|
#include <stdio.h>
|
|
#include <stdio_ext.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <regex.h>
|
|
#include <stdarg.h>
|
|
#include "policy.h"
|
|
#include "context_internal.h"
|
|
|
|
static void
|
|
#ifdef __GNUC__
|
|
__attribute__ ((format (printf, 1, 2)))
|
|
#endif
|
|
default_printf(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/* If MLS is disabled, strip any MLS level field from the context.
|
|
This allows file_contexts with MLS levels to be processed on
|
|
a non-MLS system that otherwise has the same policy. */
|
|
static inline int STRIP_LEVEL(char **context, int mls_enabled)
|
|
{
|
|
char *str;
|
|
context_t con;
|
|
int rc = -1;
|
|
|
|
if (mls_enabled)
|
|
return 0;
|
|
|
|
con = context_new(*context);
|
|
if (!con)
|
|
return rc;
|
|
|
|
if (context_range_set(con,NULL))
|
|
goto out;
|
|
|
|
str = context_str(con);
|
|
if (!str)
|
|
goto out;
|
|
|
|
str = strdup(str);
|
|
if (!str)
|
|
goto out;
|
|
|
|
free(*context);
|
|
*context = str;
|
|
rc = 0;
|
|
out:
|
|
context_free(con);
|
|
return rc;
|
|
}
|
|
|
|
static void (*myprintf)(const char *fmt, ...) = &default_printf;
|
|
|
|
void set_matchpathcon_printf(void (*f)(const char *fmt, ...))
|
|
{
|
|
if (f)
|
|
myprintf = f;
|
|
else
|
|
myprintf = &default_printf;
|
|
}
|
|
|
|
static int default_invalidcon(const char *path, unsigned lineno, char *context)
|
|
{
|
|
if (security_check_context(context) < 0 && errno != ENOENT) {
|
|
myprintf("%s: line %u has invalid context %s\n", path, lineno, context);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int (*myinvalidcon)(const char *p, unsigned l, char *c) = &default_invalidcon;
|
|
|
|
void set_matchpathcon_invalidcon(int (*f)(const char *p, unsigned l, char *c))
|
|
{
|
|
if (f)
|
|
myinvalidcon = f;
|
|
else
|
|
myinvalidcon = &default_invalidcon;
|
|
}
|
|
|
|
static unsigned int myflags;
|
|
|
|
void set_matchpathcon_flags(unsigned int flags)
|
|
{
|
|
myflags = flags;
|
|
}
|
|
|
|
/*
|
|
* A file security context specification.
|
|
*/
|
|
typedef struct spec {
|
|
char *regex_str; /* regular expession string for diagnostic messages */
|
|
char *type_str; /* type string for diagnostic messages */
|
|
char *context; /* context string */
|
|
regex_t regex; /* compiled regular expression */
|
|
mode_t mode; /* mode format value */
|
|
int matches; /* number of matching pathnames */
|
|
int hasMetaChars; /* indicates whether the RE has
|
|
any meta characters.
|
|
0 = no meta chars
|
|
1 = has one or more meta chars */
|
|
int stem_id; /* indicates which of the stem-compression
|
|
* items it matches */
|
|
} spec_t;
|
|
|
|
typedef struct stem {
|
|
char *buf;
|
|
int len;
|
|
} stem_t;
|
|
|
|
static stem_t *stem_arr = NULL;
|
|
static int num_stems = 0;
|
|
static int alloc_stems = 0;
|
|
|
|
static const char * const regex_chars = ".^$?*+|[({";
|
|
|
|
/* Return the length of the text that can be considered the stem, returns 0
|
|
* if there is no identifiable stem */
|
|
static int get_stem_from_spec(const char * const buf)
|
|
{
|
|
const char *tmp = strchr(buf + 1, '/');
|
|
const char *ind;
|
|
|
|
if(!tmp)
|
|
return 0;
|
|
|
|
for(ind = buf; ind < tmp; ind++)
|
|
{
|
|
if(strchr(regex_chars, (int)*ind))
|
|
return 0;
|
|
}
|
|
return tmp - buf;
|
|
}
|
|
|
|
/* return the length of the text that is the stem of a file name */
|
|
static int get_stem_from_file_name(const char * const buf)
|
|
{
|
|
const char *tmp = strchr(buf + 1, '/');
|
|
|
|
if(!tmp)
|
|
return 0;
|
|
return tmp - buf;
|
|
}
|
|
|
|
/* find the stem of a file spec, returns the index into stem_arr for a new
|
|
* or existing stem, (or -1 if there is no possible stem - IE for a file in
|
|
* the root directory or a regex that is too complex for us). Makes buf
|
|
* point to the text AFTER the stem. */
|
|
static int find_stem_from_spec(const char **buf)
|
|
{
|
|
int i;
|
|
int stem_len = get_stem_from_spec(*buf);
|
|
|
|
if(!stem_len)
|
|
return -1;
|
|
for(i = 0; i < num_stems; i++)
|
|
{
|
|
if(stem_len == stem_arr[i].len && !strncmp(*buf, stem_arr[i].buf, stem_len))
|
|
{
|
|
*buf += stem_len;
|
|
return i;
|
|
}
|
|
}
|
|
if(num_stems == alloc_stems)
|
|
{
|
|
stem_t *tmp_arr;
|
|
alloc_stems = alloc_stems * 2 + 16;
|
|
tmp_arr = realloc(stem_arr, sizeof(stem_t) * alloc_stems);
|
|
if(!tmp_arr)
|
|
return -1;
|
|
stem_arr = tmp_arr;
|
|
}
|
|
stem_arr[num_stems].len = stem_len;
|
|
stem_arr[num_stems].buf = malloc(stem_len + 1);
|
|
if(!stem_arr[num_stems].buf)
|
|
return -1;
|
|
memcpy(stem_arr[num_stems].buf, *buf, stem_len);
|
|
stem_arr[num_stems].buf[stem_len] = '\0';
|
|
num_stems++;
|
|
*buf += stem_len;
|
|
return num_stems - 1;
|
|
}
|
|
|
|
/* find the stem of a file name, returns the index into stem_arr (or -1 if
|
|
* there is no match - IE for a file in the root directory or a regex that is
|
|
* too complex for us). Makes buf point to the text AFTER the stem. */
|
|
static int find_stem_from_file(const char **buf)
|
|
{
|
|
int i;
|
|
int stem_len = get_stem_from_file_name(*buf);
|
|
|
|
if(!stem_len)
|
|
return -1;
|
|
for(i = 0; i < num_stems; i++)
|
|
{
|
|
if(stem_len == stem_arr[i].len && !strncmp(*buf, stem_arr[i].buf, stem_len))
|
|
{
|
|
*buf += stem_len;
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* The array of specifications, initially in the
|
|
* same order as in the specification file.
|
|
* Sorting occurs based on hasMetaChars
|
|
*/
|
|
static spec_t *spec_arr;
|
|
static unsigned int nspec;
|
|
|
|
/*
|
|
* An association between an inode and a
|
|
* specification.
|
|
*/
|
|
typedef struct file_spec {
|
|
ino_t ino; /* inode number */
|
|
int specind; /* index of specification in spec */
|
|
char *file; /* full pathname for diagnostic messages about conflicts */
|
|
struct file_spec *next; /* next association in hash bucket chain */
|
|
} file_spec_t;
|
|
|
|
/*
|
|
* The hash table of associations, hashed by inode number.
|
|
* Chaining is used for collisions, with elements ordered
|
|
* by inode number in each bucket. Each hash bucket has a dummy
|
|
* header.
|
|
*/
|
|
#define HASH_BITS 16
|
|
#define HASH_BUCKETS (1 << HASH_BITS)
|
|
#define HASH_MASK (HASH_BUCKETS-1)
|
|
static file_spec_t *fl_head;
|
|
|
|
/*
|
|
* Try to add an association between an inode and
|
|
* a specification. If there is already an association
|
|
* for the inode and it conflicts with this specification,
|
|
* then use the specification that occurs later in the
|
|
* specification array.
|
|
*/
|
|
int matchpathcon_filespec_add(ino_t ino, int specind, const char *file)
|
|
{
|
|
file_spec_t *prevfl, *fl;
|
|
int h, no_conflict, ret;
|
|
struct stat sb;
|
|
|
|
if (!fl_head) {
|
|
fl_head = malloc(sizeof(file_spec_t)*HASH_BUCKETS);
|
|
if (!fl_head)
|
|
goto oom;
|
|
memset(fl_head, 0, sizeof(file_spec_t)*HASH_BUCKETS);
|
|
}
|
|
|
|
h = (ino + (ino >> HASH_BITS)) & HASH_MASK;
|
|
for (prevfl = &fl_head[h], fl = fl_head[h].next; fl;
|
|
prevfl = fl, fl = fl->next) {
|
|
if (ino == fl->ino) {
|
|
ret = lstat(fl->file, &sb);
|
|
if (ret < 0 || sb.st_ino != ino) {
|
|
fl->specind = specind;
|
|
free(fl->file);
|
|
fl->file = malloc(strlen(file) + 1);
|
|
if (!fl->file)
|
|
goto oom;
|
|
strcpy(fl->file, file);
|
|
return fl->specind;
|
|
|
|
}
|
|
|
|
no_conflict = (strcmp(spec_arr[fl->specind].context,spec_arr[specind].context) == 0);
|
|
if (no_conflict)
|
|
return fl->specind;
|
|
|
|
myprintf("%s: conflicting specifications for %s and %s, using %s.\n",
|
|
__FUNCTION__, file, fl->file,
|
|
((specind > fl->specind) ? spec_arr[specind].
|
|
context : spec_arr[fl->specind].context));
|
|
fl->specind =
|
|
(specind >
|
|
fl->specind) ? specind : fl->specind;
|
|
free(fl->file);
|
|
fl->file = malloc(strlen(file) + 1);
|
|
if (!fl->file)
|
|
goto oom;
|
|
strcpy(fl->file, file);
|
|
return fl->specind;
|
|
}
|
|
|
|
if (ino > fl->ino)
|
|
break;
|
|
}
|
|
|
|
fl = malloc(sizeof(file_spec_t));
|
|
if (!fl)
|
|
goto oom;
|
|
fl->ino = ino;
|
|
fl->specind = specind;
|
|
fl->file = malloc(strlen(file) + 1);
|
|
if (!fl->file)
|
|
goto oom_freefl;
|
|
strcpy(fl->file, file);
|
|
fl->next = prevfl->next;
|
|
prevfl->next = fl;
|
|
return fl->specind;
|
|
oom_freefl:
|
|
free(fl);
|
|
oom:
|
|
myprintf("%s: insufficient memory for file label entry for %s\n",
|
|
__FUNCTION__, file);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Evaluate the association hash table distribution.
|
|
*/
|
|
void matchpathcon_filespec_eval(void)
|
|
{
|
|
file_spec_t *fl;
|
|
int h, used, nel, len, longest;
|
|
|
|
if (!fl_head)
|
|
return;
|
|
|
|
used = 0;
|
|
longest = 0;
|
|
nel = 0;
|
|
for (h = 0; h < HASH_BUCKETS; h++) {
|
|
len = 0;
|
|
for (fl = fl_head[h].next; fl; fl = fl->next) {
|
|
len++;
|
|
}
|
|
if (len)
|
|
used++;
|
|
if (len > longest)
|
|
longest = len;
|
|
nel += len;
|
|
}
|
|
|
|
myprintf
|
|
("%s: hash table stats: %d elements, %d/%d buckets used, longest chain length %d\n",
|
|
__FUNCTION__, nel, used, HASH_BUCKETS, longest);
|
|
}
|
|
|
|
/*
|
|
* Destroy the association hash table.
|
|
*/
|
|
void matchpathcon_filespec_destroy(void)
|
|
{
|
|
file_spec_t *fl, *tmp;
|
|
int h;
|
|
|
|
if (!fl_head)
|
|
return;
|
|
|
|
for (h = 0; h < HASH_BUCKETS; h++) {
|
|
fl = fl_head[h].next;
|
|
while (fl) {
|
|
tmp = fl;
|
|
fl = fl->next;
|
|
free(tmp->file);
|
|
free(tmp);
|
|
}
|
|
fl_head[h].next = NULL;
|
|
}
|
|
free(fl_head);
|
|
fl_head = NULL;
|
|
}
|
|
|
|
/*
|
|
* Warn about duplicate specifications.
|
|
*/
|
|
static void nodups_specs(const char *path)
|
|
{
|
|
unsigned int ii, jj;
|
|
struct spec *curr_spec;
|
|
|
|
for (ii = 0; ii < nspec; ii++) {
|
|
curr_spec = &spec_arr[ii];
|
|
for (jj = ii + 1; jj < nspec; jj++) {
|
|
if ((!strcmp(spec_arr[jj].regex_str, curr_spec->regex_str))
|
|
&&
|
|
(!spec_arr[jj].mode || !curr_spec->mode
|
|
|| spec_arr[jj].mode == curr_spec->mode)) {
|
|
if (strcmp(spec_arr[jj].context, curr_spec->context)) {
|
|
myprintf(
|
|
"%s: Multiple different specifications for %s (%s and %s).\n",
|
|
path, curr_spec->regex_str,
|
|
spec_arr[jj].context,
|
|
curr_spec->context);
|
|
}
|
|
else {
|
|
myprintf(
|
|
"%s: Multiple same specifications for %s.\n",
|
|
path, curr_spec->regex_str);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Determine if the regular expression specification has any meta characters. */
|
|
static void spec_hasMetaChars(struct spec *spec)
|
|
{
|
|
char *c;
|
|
int len;
|
|
char *end;
|
|
|
|
c = spec->regex_str;
|
|
len = strlen(spec->regex_str);
|
|
end = c + len;
|
|
|
|
spec->hasMetaChars = 0;
|
|
|
|
/* Look at each character in the RE specification string for a
|
|
* meta character. Return when any meta character reached. */
|
|
while (c != end) {
|
|
switch(*c) {
|
|
case '.':
|
|
case '^':
|
|
case '$':
|
|
case '?':
|
|
case '*':
|
|
case '+':
|
|
case '|':
|
|
case '[':
|
|
case '(':
|
|
case '{':
|
|
spec->hasMetaChars = 1;
|
|
return;
|
|
case '\\': /* skip the next character */
|
|
c++;
|
|
break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
c++;
|
|
}
|
|
return;
|
|
}
|
|
static int process_line( const char *path, char *line_buf, int pass, unsigned lineno, int mls_enabled) {
|
|
int items, len, regerr;
|
|
char *buf_p;
|
|
char *regex, *type, *context;
|
|
char *anchored_regex;
|
|
len = strlen(line_buf);
|
|
if (line_buf[len - 1] == '\n')
|
|
line_buf[len - 1] = 0;
|
|
buf_p = line_buf;
|
|
while (isspace(*buf_p))
|
|
buf_p++;
|
|
/* Skip comment lines and empty lines. */
|
|
if (*buf_p == '#' || *buf_p == 0)
|
|
return 0;
|
|
items =
|
|
sscanf(line_buf, "%as %as %as", ®ex, &type,
|
|
&context);
|
|
if (items < 2) {
|
|
myprintf("%s: line %d is missing fields\n, skipping", path, lineno);
|
|
return 0;
|
|
} else if (items == 2) {
|
|
/* The type field is optional. */
|
|
free(context);
|
|
context = type;
|
|
type = 0;
|
|
}
|
|
|
|
if (pass == 1) {
|
|
/* On the second pass, compile and store the specification in spec. */
|
|
const char *reg_buf = regex;
|
|
char *cp;
|
|
spec_arr[nspec].stem_id = find_stem_from_spec(®_buf);
|
|
spec_arr[nspec].regex_str = regex;
|
|
|
|
/* Anchor the regular expression. */
|
|
len = strlen(reg_buf);
|
|
cp = anchored_regex = malloc(len + 3);
|
|
if (!anchored_regex)
|
|
return -1;
|
|
/* Create ^...$ regexp. */
|
|
*cp++ = '^';
|
|
cp = mempcpy(cp, reg_buf, len);
|
|
*cp++ = '$';
|
|
*cp = '\0';
|
|
|
|
/* Compile the regular expression. */
|
|
regerr =
|
|
regcomp(&spec_arr[nspec].regex,
|
|
anchored_regex,
|
|
REG_EXTENDED | REG_NOSUB);
|
|
if (regerr != 0) {
|
|
size_t errsz = 0;
|
|
char *errbuf = NULL;
|
|
errsz = regerror(regerr, &spec_arr[nspec].regex,
|
|
errbuf, errsz);
|
|
if (errsz)
|
|
errbuf = malloc(errsz);
|
|
if (errbuf)
|
|
(void) regerror(regerr,
|
|
&spec_arr[nspec].regex,
|
|
errbuf, errsz);
|
|
myprintf("%s: line %d has invalid regex %s: %s\n", path, lineno, anchored_regex, (errbuf ? errbuf : "out of memory"));
|
|
free(anchored_regex);
|
|
return 0;
|
|
}
|
|
free(anchored_regex);
|
|
|
|
/* Convert the type string to a mode format */
|
|
spec_arr[nspec].type_str = type;
|
|
spec_arr[nspec].mode = 0;
|
|
if (!type)
|
|
goto skip_type;
|
|
len = strlen(type);
|
|
if (type[0] != '-' || len != 2) {
|
|
myprintf("%s: line %d has invalid file type %s\n", path, lineno, type);
|
|
return 0;
|
|
}
|
|
switch (type[1]) {
|
|
case 'b':
|
|
spec_arr[nspec].mode = S_IFBLK;
|
|
break;
|
|
case 'c':
|
|
spec_arr[nspec].mode = S_IFCHR;
|
|
break;
|
|
case 'd':
|
|
spec_arr[nspec].mode = S_IFDIR;
|
|
break;
|
|
case 'p':
|
|
spec_arr[nspec].mode = S_IFIFO;
|
|
break;
|
|
case 'l':
|
|
spec_arr[nspec].mode = S_IFLNK;
|
|
break;
|
|
case 's':
|
|
spec_arr[nspec].mode = S_IFSOCK;
|
|
break;
|
|
case '-':
|
|
spec_arr[nspec].mode = S_IFREG;
|
|
break;
|
|
default:
|
|
myprintf("%s: line %d has invalid file type %s\n", path, lineno, type);
|
|
return 0;
|
|
}
|
|
|
|
skip_type:
|
|
if (strcmp(context, "<<none>>")) {
|
|
if (context_translations) {
|
|
if (raw_to_trans_context(context,
|
|
&spec_arr[nspec].context)) {
|
|
myprintf("%s: line %u has invalid "
|
|
"context %s\n",
|
|
path, lineno, context);
|
|
return 0;
|
|
}
|
|
free(context);
|
|
context = spec_arr[nspec].context;
|
|
} else {
|
|
if (STRIP_LEVEL(&context, mls_enabled))
|
|
return -1;
|
|
}
|
|
|
|
if (myinvalidcon(path, lineno, context))
|
|
return 0;
|
|
}
|
|
|
|
spec_arr[nspec].context = context;
|
|
|
|
/* Determine if specification has
|
|
* any meta characters in the RE */
|
|
spec_hasMetaChars(&spec_arr[nspec]);
|
|
}
|
|
|
|
nspec++;
|
|
if (pass == 0) {
|
|
free(regex);
|
|
if (type)
|
|
free(type);
|
|
free(context);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int matchpathcon_init(const char *path)
|
|
{
|
|
FILE *fp;
|
|
FILE *localfp = NULL;
|
|
FILE *homedirfp = NULL;
|
|
char local_path[PATH_MAX + 1];
|
|
char homedir_path[PATH_MAX + 1];
|
|
char *line_buf = NULL;
|
|
size_t line_len = 0;
|
|
unsigned int lineno, pass, i, j, maxnspec;
|
|
spec_t *spec_copy=NULL;
|
|
int status=-1;
|
|
int mls_enabled=is_selinux_mls_enabled();
|
|
|
|
/* Open the specification file. */
|
|
if (!path)
|
|
path = selinux_file_context_path();
|
|
if ((fp = fopen(path, "r")) == NULL)
|
|
return -1;
|
|
__fsetlocking(fp, FSETLOCKING_BYCALLER);
|
|
|
|
if ((myflags & MATCHPATHCON_BASEONLY) == 0) {
|
|
snprintf(homedir_path, sizeof(homedir_path), "%s.homedirs", path);
|
|
homedirfp = fopen(homedir_path, "r");
|
|
if (homedirfp != NULL)
|
|
__fsetlocking(homedirfp, FSETLOCKING_BYCALLER);
|
|
|
|
snprintf(local_path, sizeof(local_path), "%s.local", path);
|
|
localfp = fopen(local_path, "r");
|
|
if (localfp != NULL)
|
|
__fsetlocking(localfp, FSETLOCKING_BYCALLER);
|
|
}
|
|
|
|
/*
|
|
* Perform two passes over the specification file.
|
|
* The first pass counts the number of specifications and
|
|
* performs simple validation of the input. At the end
|
|
* of the first pass, the spec array is allocated.
|
|
* The second pass performs detailed validation of the input
|
|
* and fills in the spec array.
|
|
*/
|
|
maxnspec = UINT_MAX / sizeof(spec_t);
|
|
for (pass = 0; pass < 2; pass++) {
|
|
lineno = 0;
|
|
nspec = 0;
|
|
while (getline(&line_buf, &line_len, fp) > 0 && nspec < maxnspec) {
|
|
if (process_line(path, line_buf, pass, ++lineno, mls_enabled) != 0)
|
|
goto finish;
|
|
}
|
|
lineno = 0;
|
|
if (homedirfp)
|
|
while (getline(&line_buf, &line_len, homedirfp) > 0 && nspec < maxnspec) {
|
|
if (process_line(homedir_path, line_buf, pass, ++lineno, mls_enabled) != 0)
|
|
goto finish;
|
|
}
|
|
|
|
lineno = 0;
|
|
if (localfp)
|
|
while (getline(&line_buf, &line_len, localfp) > 0 && nspec < maxnspec) {
|
|
if (process_line(local_path, line_buf, pass, ++lineno, mls_enabled) != 0)
|
|
goto finish;
|
|
}
|
|
|
|
if (pass == 0) {
|
|
if (nspec == 0) {
|
|
status = 0;
|
|
goto finish;
|
|
}
|
|
if ((spec_arr = malloc(sizeof(spec_t) * nspec)) ==
|
|
NULL)
|
|
goto finish;
|
|
memset(spec_arr, '\0', sizeof(spec_t) * nspec);
|
|
maxnspec = nspec;
|
|
rewind(fp);
|
|
if (homedirfp) rewind(homedirfp);
|
|
if (localfp) rewind(localfp);
|
|
}
|
|
}
|
|
free(line_buf);
|
|
|
|
/* Move exact pathname specifications to the end. */
|
|
spec_copy = malloc(sizeof(spec_t) * nspec);
|
|
if (!spec_copy)
|
|
goto finish;
|
|
j = 0;
|
|
for (i = 0; i < nspec; i++) {
|
|
if (spec_arr[i].hasMetaChars)
|
|
memcpy(&spec_copy[j++], &spec_arr[i], sizeof(spec_t));
|
|
}
|
|
for (i = 0; i < nspec; i++) {
|
|
if (!spec_arr[i].hasMetaChars)
|
|
memcpy(&spec_copy[j++], &spec_arr[i], sizeof(spec_t));
|
|
}
|
|
free(spec_arr);
|
|
spec_arr = spec_copy;
|
|
|
|
nodups_specs(path);
|
|
|
|
status = 0;
|
|
finish:
|
|
fclose(fp);
|
|
if (spec_arr != spec_copy) free(spec_arr);
|
|
if (homedirfp) fclose(homedirfp);
|
|
if (localfp) fclose(localfp);
|
|
return status;
|
|
}
|
|
hidden_def(matchpathcon_init)
|
|
|
|
static int matchpathcon_common(const char *name,
|
|
mode_t mode)
|
|
{
|
|
int i, ret, file_stem;
|
|
const char *buf = name;
|
|
|
|
if (!nspec) {
|
|
ret = matchpathcon_init(NULL);
|
|
if (ret < 0)
|
|
return ret;
|
|
if (!nspec) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
file_stem = find_stem_from_file(&buf);
|
|
|
|
mode &= S_IFMT;
|
|
|
|
/*
|
|
* Check for matching specifications in reverse order, so that
|
|
* the last matching specification is used.
|
|
*/
|
|
for (i = nspec - 1; i >= 0; i--)
|
|
{
|
|
/* if the spec in question matches no stem or has the same
|
|
* stem as the file AND if the spec in question has no mode
|
|
* specified or if the mode matches the file mode then we do
|
|
* a regex check */
|
|
if( (spec_arr[i].stem_id == -1 || spec_arr[i].stem_id == file_stem)
|
|
&& (!mode || !spec_arr[i].mode || ( (mode & S_IFMT) == spec_arr[i].mode ) ) )
|
|
{
|
|
if(spec_arr[i].stem_id == -1)
|
|
ret = regexec(&spec_arr[i].regex, name, 0, NULL, 0);
|
|
else
|
|
ret = regexec(&spec_arr[i].regex, buf, 0, NULL, 0);
|
|
if (ret == 0)
|
|
break;
|
|
|
|
if (ret == REG_NOMATCH)
|
|
continue;
|
|
/* else it's an error */
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (i < 0) {
|
|
/* No matching specification. */
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
spec_arr[i].matches++;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
int matchpathcon(const char *name,
|
|
mode_t mode,
|
|
security_context_t *con)
|
|
{
|
|
int i = matchpathcon_common(name, mode);
|
|
|
|
if (i < 0)
|
|
return -1;
|
|
|
|
if (strcmp(spec_arr[i].context, "<<none>>") == 0) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
*con = strdup(spec_arr[i].context);
|
|
if (!(*con))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int matchpathcon_index(const char *name,
|
|
mode_t mode,
|
|
security_context_t *con)
|
|
{
|
|
int i = matchpathcon_common(name, mode);
|
|
|
|
if (i < 0)
|
|
return -1;
|
|
|
|
*con = strdup(spec_arr[i].context);
|
|
if (!(*con))
|
|
return -1;
|
|
|
|
return i;
|
|
}
|
|
|
|
void matchpathcon_checkmatches(char *str)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < nspec; i++) {
|
|
if (spec_arr[i].matches == 0) {
|
|
if (spec_arr[i].type_str) {
|
|
myprintf
|
|
("%s: Warning! No matches for (%s, %s, %s)\n",
|
|
str, spec_arr[i].regex_str,
|
|
spec_arr[i].type_str, spec_arr[i].context);
|
|
} else {
|
|
myprintf
|
|
("%s: Warning! No matches for (%s, %s)\n",
|
|
str, spec_arr[i].regex_str,
|
|
spec_arr[i].context);
|
|
}
|
|
}
|
|
}
|
|
}
|