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