mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-10-15 14:16:18 +00:00
4241 lines
127 KiB
Diff
4241 lines
127 KiB
Diff
Index: linux-2.6/security/apparmor/Makefile
|
|
===================================================================
|
|
--- linux-2.6.orig/security/apparmor/Makefile
|
|
+++ linux-2.6/security/apparmor/Makefile
|
|
@@ -1,6 +1,6 @@
|
|
# Makefile for AppArmor Linux Security Module
|
|
#
|
|
-obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o match/
|
|
+obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
|
|
|
|
apparmor-y := main.o list.o procattr.o lsm.o apparmorfs.o capabilities.o \
|
|
- module_interface.o
|
|
+ module_interface.o match.o
|
|
Index: linux-2.6/security/apparmor/apparmor.h
|
|
===================================================================
|
|
--- linux-2.6.orig/security/apparmor/apparmor.h
|
|
+++ linux-2.6/security/apparmor/apparmor.h
|
|
@@ -17,6 +17,7 @@
|
|
#include <linux/rcupdate.h>
|
|
|
|
#include "shared.h"
|
|
+#include "match.h"
|
|
|
|
/* Control parameters (0 or 1), settable thru module/boot flags or
|
|
* via /sys/kernel/security/apparmor/control */
|
|
@@ -61,56 +62,21 @@ struct flagval {
|
|
int audit;
|
|
};
|
|
|
|
-enum entry_match_type {
|
|
- aa_entry_literal,
|
|
- aa_entry_tailglob,
|
|
- aa_entry_pattern,
|
|
- aa_entry_invalid
|
|
-};
|
|
-
|
|
-/* struct aa_entry - file ACL *
|
|
- * @filename: filename controlled by this ACL
|
|
- * @mode: permissions granted by ACL
|
|
- * @type: type of match to perform against @filename
|
|
- * @extradata: any extra data needed by an extended matching type
|
|
- * @list: list the ACL is on
|
|
- * @listp: permission partitioned lists this ACL is on.
|
|
- *
|
|
- * Each entry describes a file and an allowed access mode.
|
|
- */
|
|
-struct aa_entry {
|
|
- char *filename;
|
|
- int mode; /* mode is 'or' of READ, WRITE, EXECUTE,
|
|
- * INHERIT, UNCONSTRAINED, and LIBRARY
|
|
- * (meaning don't prefetch). */
|
|
-
|
|
- enum entry_match_type type;
|
|
- void *extradata;
|
|
-
|
|
- struct list_head list;
|
|
- struct list_head listp[POS_AA_FILE_MAX + 1];
|
|
-};
|
|
-
|
|
#define AA_MEDIATE_FS (void*)0x00000001
|
|
#define AA_SECURE_EXEC_NEEDED 0x00000001
|
|
|
|
#define AA_EXEC_MODIFIER_MASK(mask) ((mask) & AA_EXEC_MODIFIERS)
|
|
-#define AA_EXEC_MASK(mask) ((mask) & (AA_MAY_EXEC | AA_EXEC_MODIFIERS))
|
|
-#define AA_EXEC_UNSAFE_MASK(mask) ((mask) & (AA_MAY_EXEC | AA_EXEC_MODIFIERS |\
|
|
- AA_EXEC_UNSAFE))
|
|
+#define AA_EXEC_MASK(mask) ((mask) & (AA_EXEC_MODIFIERS | AA_EXEC_UNSAFE))
|
|
|
|
/* struct aaprofile - basic confinement data
|
|
* @parent: non refcounted pointer to parent profile
|
|
* @name: the profiles name
|
|
- * @file_entry: file ACL
|
|
- * @file_entryp: vector of file ACL by permission granted
|
|
+ * @file_rules: dfa containing the profiles file rules
|
|
* @list: list this profile is on
|
|
* @sub: profiles list of subprofiles (HATS)
|
|
* @flags: flags controlling profile behavior
|
|
* @null_profile: if needed per profile learning and null confinement profile
|
|
* @isstale: flag to indicate the profile is stale
|
|
- * @num_file_entries: number of file entries the profile contains
|
|
- * @num_file_pentries: number of file entries for each partitioned list
|
|
* @capabilities: capabilities granted by the process
|
|
* @rcu: rcu head used when freeing the profile
|
|
* @count: reference count of the profile
|
|
@@ -123,17 +89,14 @@ struct aaprofile {
|
|
struct aaprofile *parent;
|
|
char *name;
|
|
|
|
- struct list_head file_entry;
|
|
- struct list_head file_entryp[POS_AA_FILE_MAX + 1];
|
|
+ struct aa_dfa *file_rules;
|
|
+
|
|
struct list_head list;
|
|
struct list_head sub;
|
|
struct flagval flags;
|
|
struct aaprofile *null_profile;
|
|
int isstale;
|
|
|
|
- int num_file_entries;
|
|
- int num_file_pentries[POS_AA_FILE_MAX + 1];
|
|
-
|
|
kernel_cap_t capabilities;
|
|
|
|
struct rcu_head rcu;
|
|
@@ -298,4 +261,12 @@ extern void destroy_apparmorfs(void);
|
|
/* capabilities.c */
|
|
extern const char *capability_to_name(unsigned int cap);
|
|
|
|
+/* match.c */
|
|
+struct aa_dfa *aamatch_alloc(void);
|
|
+void aamatch_free(struct aa_dfa *dfa);
|
|
+int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size);
|
|
+int verify_dfa(struct aa_dfa *dfa);
|
|
+const char *aamatch_features(void);
|
|
+unsigned int aamatch(struct aa_dfa *dfa, const char *pathname);
|
|
+
|
|
#endif /* __APPARMOR_H */
|
|
Index: linux-2.6/security/apparmor/inline.h
|
|
===================================================================
|
|
--- linux-2.6.orig/security/apparmor/inline.h
|
|
+++ linux-2.6/security/apparmor/inline.h
|
|
@@ -199,14 +199,8 @@ static inline struct aaprofile *alloc_aa
|
|
GFP_KERNEL);
|
|
AA_DEBUG("%s(%p)\n", __FUNCTION__, profile);
|
|
if (profile) {
|
|
- int i;
|
|
-
|
|
INIT_LIST_HEAD(&profile->list);
|
|
INIT_LIST_HEAD(&profile->sub);
|
|
- INIT_LIST_HEAD(&profile->file_entry);
|
|
- for (i = 0; i <= POS_AA_FILE_MAX; i++) {
|
|
- INIT_LIST_HEAD(&profile->file_entryp[i]);
|
|
- }
|
|
INIT_RCU_HEAD(&profile->rcu);
|
|
kref_init(&profile->count);
|
|
}
|
|
Index: linux-2.6/security/apparmor/main.c
|
|
===================================================================
|
|
--- linux-2.6.orig/security/apparmor/main.c
|
|
+++ linux-2.6/security/apparmor/main.c
|
|
@@ -14,7 +14,6 @@
|
|
#include <linux/audit.h>
|
|
|
|
#include "apparmor.h"
|
|
-#include "match/match.h"
|
|
|
|
#include "inline.h"
|
|
|
|
@@ -60,7 +59,6 @@ static inline int aa_taskattr_access(con
|
|
*/
|
|
static inline int aa_file_mode(struct aaprofile *profile, const char *name)
|
|
{
|
|
- struct aa_entry *entry;
|
|
int perms = 0;
|
|
|
|
AA_DEBUG("%s: %s\n", __FUNCTION__, name);
|
|
@@ -73,119 +71,11 @@ static inline int aa_file_mode(struct aa
|
|
AA_DEBUG("%s: no profile\n", __FUNCTION__);
|
|
goto out;
|
|
}
|
|
- list_for_each_entry(entry, &profile->file_entry, list) {
|
|
- perms |= aamatch_match(entry, name);
|
|
- }
|
|
-out:
|
|
- return perms;
|
|
-}
|
|
-
|
|
-/**
|
|
- * aa_get_execmode - calculate what qualifier to apply to an exec
|
|
- * @active: profile to search
|
|
- * @name: name of file to exec
|
|
- * @xmod: pointer to a execution mode bit for the rule that was matched
|
|
- * if the rule has no execuition qualifier {pui} then
|
|
- * %AA_MAY_EXEC is returned indicating a naked x
|
|
- * if the has an exec qualifier then only the qualifier bit {pui}
|
|
- * is returned (%AA_MAY_EXEC) is not set.
|
|
- * @unsafe: true if secure_exec should be overriden
|
|
- * Returns %0 (false):
|
|
- * if unable to find profile or there are conflicting pattern matches.
|
|
- * *xmod - is not modified
|
|
- * *unsafe - is not modified
|
|
- *
|
|
- * Returns %1 (true):
|
|
- * if exec rule matched
|
|
- * if the rule has an execution mode qualifier {pui} then
|
|
- * *xmod = the execution qualifier of the rule {pui}
|
|
- * else
|
|
- * *xmod = %AA_MAY_EXEC
|
|
- * unsafe = presence of unsage flag
|
|
- */
|
|
-static inline int aa_get_execmode(struct aaprofile *active, const char *name,
|
|
- int *xmod, int *unsafe)
|
|
-{
|
|
- struct aa_entry *entry;
|
|
- int entry_perms = 0, match_perms = 0;
|
|
- int pattern_match_invalid = 0, rc = 0;
|
|
-
|
|
- /* search list of profiles with 'x' permission
|
|
- * this will also include entries with 'p', 'u' and 'i'
|
|
- * qualifiers.
|
|
- *
|
|
- * If we find a pattern match we will keep looking for an exact match
|
|
- * If we find conflicting pattern matches we will flag (while still
|
|
- * looking for an exact match). If all we have is a conflict, FALSE
|
|
- * is returned.
|
|
- */
|
|
-
|
|
- list_for_each_entry(entry, &active->file_entryp[POS_AA_MAY_EXEC],
|
|
- listp[POS_AA_MAY_EXEC]) {
|
|
- if (!pattern_match_invalid &&
|
|
- entry->type == aa_entry_pattern &&
|
|
- (entry_perms = aamatch_match(entry, name))) {
|
|
- if (match_perms &&
|
|
- AA_EXEC_UNSAFE_MASK(entry_perms) !=
|
|
- AA_EXEC_UNSAFE_MASK(match_perms))
|
|
- pattern_match_invalid = 1;
|
|
- else
|
|
- /* keep searching for an exact match */
|
|
- match_perms = entry_perms;
|
|
- } else if ((entry->type == aa_entry_literal ||
|
|
- (!pattern_match_invalid &&
|
|
- entry->type == aa_entry_tailglob)) &&
|
|
- (entry_perms = aamatch_match(entry, name))) {
|
|
- if (entry->type == aa_entry_literal) {
|
|
- /* got an exact match -- there can be only
|
|
- * one, asserted at profile load time
|
|
- */
|
|
- match_perms = entry_perms;
|
|
- pattern_match_invalid = 0;
|
|
- break;
|
|
- } else {
|
|
- if (match_perms &&
|
|
- AA_EXEC_UNSAFE_MASK(entry_perms) !=
|
|
- AA_EXEC_UNSAFE_MASK(match_perms))
|
|
- pattern_match_invalid = 1;
|
|
- else
|
|
- /* got a tailglob match, keep searching
|
|
- * for an exact match
|
|
- */
|
|
- match_perms = entry_perms;
|
|
- }
|
|
- }
|
|
-
|
|
- }
|
|
-
|
|
- rc = match_perms && !pattern_match_invalid;
|
|
|
|
- if (rc) {
|
|
- int mode = AA_EXEC_MASK(match_perms);
|
|
+ perms = aamatch(profile->file_rules, name);
|
|
|
|
- /* check for qualifiers, if present
|
|
- * we just return the qualifier
|
|
- */
|
|
- if (mode & ~AA_MAY_EXEC)
|
|
- mode = mode & ~AA_MAY_EXEC;
|
|
-
|
|
- *xmod = mode;
|
|
- *unsafe = (match_perms & AA_EXEC_UNSAFE);
|
|
- } else if (!match_perms) {
|
|
- AA_DEBUG("%s: Unable to find execute entry in profile "
|
|
- "for image '%s'\n",
|
|
- __FUNCTION__,
|
|
- name);
|
|
- } else if (pattern_match_invalid) {
|
|
- AA_WARN("%s: Inconsistency in profile %s. "
|
|
- "Two (or more) patterns specify conflicting exec "
|
|
- "qualifiers ('u', 'i' or 'p') for image %s\n",
|
|
- __FUNCTION__,
|
|
- active->name,
|
|
- name);
|
|
- }
|
|
-
|
|
- return rc;
|
|
+out:
|
|
+ return perms;
|
|
}
|
|
|
|
/**
|
|
@@ -242,10 +132,9 @@ static inline void aa_permerror2result(i
|
|
*
|
|
* Return %0 on success, else mask of non-allowed permissions
|
|
*/
|
|
-static unsigned int aa_file_perm(struct aaprofile *active, const char *name,
|
|
- int mask)
|
|
+static int aa_file_perm(struct aaprofile *active, const char *name, int mask)
|
|
{
|
|
- int i, error = 0;
|
|
+ int error = 0;
|
|
int perms;
|
|
|
|
#define PROCPFX "/proc/"
|
|
@@ -265,32 +154,7 @@ static unsigned int aa_file_perm(struct
|
|
aa_taskattr_access(name + PROCLEN))
|
|
goto done;
|
|
|
|
- perms = 0;
|
|
-
|
|
- /* iterate over partition, one permission bit at a time */
|
|
- for (i = 0; i <= POS_AA_FILE_MAX; i++) {
|
|
- struct aa_entry *entry;
|
|
-
|
|
- /* do we have to accumulate this bit?
|
|
- * or have we already accumulated it (shortcut below)? */
|
|
- if (!(mask & (1 << i)) || perms & (1 << i))
|
|
- continue;
|
|
-
|
|
- list_for_each_entry(entry, &active->file_entryp[i],
|
|
- listp[i]) {
|
|
- perms |= aamatch_match(entry, name);
|
|
-
|
|
- /* Mask bits are overloaded
|
|
- * MAY_{EXEC,WRITE,READ,APPEND} are used by
|
|
- * kernel, other values are used locally only.
|
|
- */
|
|
- if ((perms & mask) == mask) {
|
|
- AA_DEBUG("MATCH! %s=0x%x [total mode=0x%x]\n",
|
|
- name, mask, perms);
|
|
- goto done;
|
|
- }
|
|
- }
|
|
- }
|
|
+ perms = aamatch(active->file_rules, name);
|
|
|
|
/* return permissions not satisfied */
|
|
error = mask & ~perms;
|
|
@@ -964,8 +828,8 @@ int aa_register(struct linux_binprm *bpr
|
|
exec_mode = 0,
|
|
find_profile = 0,
|
|
find_profile_mandatory = 0,
|
|
- unsafe_exec = 0,
|
|
- complain = 0;
|
|
+ complain = 0,
|
|
+ unsafe_exec = 0;
|
|
|
|
AA_DEBUG("%s\n", __FUNCTION__);
|
|
|
|
@@ -990,8 +854,11 @@ int aa_register(struct linux_binprm *bpr
|
|
/* Confined task, determine what mode inherit, unconstrained or
|
|
* mandatory to load new profile
|
|
*/
|
|
- if (aa_get_execmode(active, filename, &exec_mode, &unsafe_exec)) {
|
|
- switch (exec_mode) {
|
|
+ exec_mode = AA_EXEC_MASK(aamatch(active->file_rules, filename));
|
|
+ unsafe_exec = exec_mode & AA_EXEC_UNSAFE;
|
|
+
|
|
+ if (exec_mode) {
|
|
+ switch (AA_EXEC_MODIFIER_MASK(exec_mode)) {
|
|
case AA_EXEC_INHERIT:
|
|
/* do nothing - setting of profile
|
|
* already handed in aa_fork
|
|
Index: linux-2.6/security/apparmor/match/Kbuild
|
|
===================================================================
|
|
--- linux-2.6.orig/security/apparmor/match/Kbuild
|
|
+++ /dev/null
|
|
@@ -1,6 +0,0 @@
|
|
-# Makefile for AppArmor aamatch submodule
|
|
-#
|
|
-
|
|
-obj-$(CONFIG_SECURITY_APPARMOR) += aamatch_dfa.o
|
|
-
|
|
-aamatch_dfa-y := match_dfa.o
|
|
Index: linux-2.6/security/apparmor/match/Makefile
|
|
===================================================================
|
|
--- linux-2.6.orig/security/apparmor/match/Makefile
|
|
+++ /dev/null
|
|
@@ -1,5 +0,0 @@
|
|
-# Makefile for AppArmor aamatch submodule
|
|
-#
|
|
-obj-$(CONFIG_SECURITY_APPARMOR) += aamatch_pcre.o
|
|
-
|
|
-aamatch_pcre-y := match_pcre.o pcre_exec.o
|
|
Index: linux-2.6/security/apparmor/match/match.h
|
|
===================================================================
|
|
--- linux-2.6.orig/security/apparmor/match/match.h
|
|
+++ /dev/null
|
|
@@ -1,126 +0,0 @@
|
|
-/*
|
|
- * Copyright (C) 2002-2005 Novell/SUSE
|
|
- *
|
|
- * This program is free software; you can redistribute it and/or
|
|
- * modify it under the terms of the GNU General Public License as
|
|
- * published by the Free Software Foundation, version 2 of the
|
|
- * License.
|
|
- *
|
|
- * AppArmor submodule (match) prototypes
|
|
- */
|
|
-
|
|
-#ifndef __MATCH_H
|
|
-#define __MATCH_H
|
|
-
|
|
-#include "../module_interface.h"
|
|
-#include "../apparmor.h"
|
|
-
|
|
-/* The following functions implement an interface used by the primary
|
|
- * AppArmor module to perform name matching (n.b. "AppArmor" was previously
|
|
- * called "SubDomain").
|
|
-
|
|
- * aamatch_alloc
|
|
- * aamatch_free
|
|
- * aamatch_features
|
|
- * aamatch_serialize
|
|
- * aamatch_match
|
|
- *
|
|
- * The intent is for the primary module to export (via virtual fs entries)
|
|
- * the features provided by the submodule (aamatch_features) so that the
|
|
- * parser may only load policy that can be supported.
|
|
- *
|
|
- * The primary module will call aamatch_serialize to allow the submodule
|
|
- * to consume submodule specific data from parser data stream and will call
|
|
- * aamatch_match to determine if a pathname matches an aa_entry.
|
|
- */
|
|
-
|
|
-typedef int (*aamatch_serializecb)
|
|
- (struct aa_ext *, enum aa_code, void *, const char *);
|
|
-
|
|
-/**
|
|
- * aamatch_alloc: allocate extradata (if necessary)
|
|
- * @type: type of entry being allocated
|
|
- * Return value: NULL indicates no data was allocated (ERR_PTR(x) on error)
|
|
- */
|
|
-extern void* aamatch_alloc(enum entry_match_type type);
|
|
-
|
|
-/**
|
|
- * aamatch_free: release data allocated by aamatch_alloc
|
|
- * @entry_extradata: data previously allocated by aamatch_alloc
|
|
- */
|
|
-extern void aamatch_free(void *entry_extradata);
|
|
-
|
|
-/**
|
|
- * aamatch_features: return match types supported
|
|
- * Return value: space seperated string (of types supported - use type=value
|
|
- * to indicate variants of a type)
|
|
- */
|
|
-extern const char* aamatch_features(void);
|
|
-
|
|
-/**
|
|
- * aamatch_serialize: serialize extradata
|
|
- * @entry_extradata: data previously allocated by aamatch_alloc
|
|
- * @e: input stream
|
|
- * @cb: callback fn (consume incoming data stream)
|
|
- * Return value: 0 success, -ve error
|
|
- */
|
|
-extern int aamatch_serialize(void *entry_extradata, struct aa_ext *e,
|
|
- aamatch_serializecb cb);
|
|
-
|
|
-/**
|
|
- * aamatch_match: determine if pathname matches entry
|
|
- * @entry: rule entry to match against
|
|
- * @pathname: pathname to verify
|
|
- * Return value: permission match, 0 othersise
|
|
- */
|
|
-extern int aamatch_match(struct aa_entry *entry, const char *pathname);
|
|
-
|
|
-/**
|
|
- * sd_getmatch_type - return string representation of entry_match_type
|
|
- * @type: entry match type
|
|
- */
|
|
-static inline const char *sd_getmatch_type(enum entry_match_type type)
|
|
-{
|
|
- const char *names[] = {
|
|
- "aa_entry_literal",
|
|
- "aa_entry_tailglob",
|
|
- "aa_entry_pattern",
|
|
- "aa_entry_invalid"
|
|
- };
|
|
-
|
|
- if (type >= aa_entry_invalid) {
|
|
- type = aa_entry_invalid;
|
|
- }
|
|
-
|
|
- return names[type];
|
|
-}
|
|
-
|
|
-/**
|
|
- * aamatch_match_common - helper function to check if a pathname matches
|
|
- * a literal/tailglob
|
|
- * @path: path requested to search for
|
|
- * @entry_name: name from aa_entry
|
|
- * @type: type of entry
|
|
- */
|
|
-static inline int aamatch_match_common(struct aa_entry *entry,
|
|
- const char *path)
|
|
-{
|
|
- int retval;
|
|
-
|
|
- /* literal, no pattern matching characters */
|
|
- if (entry->type == aa_entry_literal) {
|
|
- retval = (strcmp(entry->filename, path) == 0);
|
|
- /* trailing ** glob pattern */
|
|
- } else if (entry->type == aa_entry_tailglob) {
|
|
- retval = (strncmp(entry->filename, path,
|
|
- strlen(entry->filename) - 2) == 0);
|
|
- } else {
|
|
- AA_WARN("%s: Invalid entry_match_type %d\n",
|
|
- __FUNCTION__, entry->type);
|
|
- retval = 0;
|
|
- }
|
|
-
|
|
- return retval ? entry->mode : 0;
|
|
-}
|
|
-
|
|
-#endif /* __MATCH_H */
|
|
Index: linux-2.6/security/apparmor/match/match_default.c
|
|
===================================================================
|
|
--- linux-2.6.orig/security/apparmor/match/match_default.c
|
|
+++ /dev/null
|
|
@@ -1,56 +0,0 @@
|
|
-/*
|
|
- * Copyright (C) 2002-2005 Novell/SUSE
|
|
- *
|
|
- * This program is free software; you can redistribute it and/or
|
|
- * modify it under the terms of the GNU General Public License as
|
|
- * published by the Free Software Foundation, version 2 of the
|
|
- * License.
|
|
- *
|
|
- * http://forge.novell.com/modules/xfmod/project/?apparmor
|
|
- *
|
|
- * AppArmor default match submodule (literal and tailglob)
|
|
- */
|
|
-
|
|
-#include <linux/module.h>
|
|
-#include "match.h"
|
|
-
|
|
-static const char *features="literal tailglob";
|
|
-
|
|
-void* aamatch_alloc(enum entry_match_type type)
|
|
-{
|
|
- return NULL;
|
|
-}
|
|
-
|
|
-void aamatch_free(void *ptr)
|
|
-{
|
|
-}
|
|
-
|
|
-const char *aamatch_features(void)
|
|
-{
|
|
- return features;
|
|
-}
|
|
-
|
|
-int aamatch_serialize(void *entry_extradata, struct aa_ext *e,
|
|
- aamatch_serializecb cb)
|
|
-{
|
|
- return 0;
|
|
-}
|
|
-
|
|
-int aamatch_match(struct aa_entry *entry, const char *pathname)
|
|
-{
|
|
- int ret;
|
|
-
|
|
- ret = aamatch_match_common(entry, pathname);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-EXPORT_SYMBOL_GPL(aamatch_alloc);
|
|
-EXPORT_SYMBOL_GPL(aamatch_free);
|
|
-EXPORT_SYMBOL_GPL(aamatch_features);
|
|
-EXPORT_SYMBOL_GPL(aamatch_serialize);
|
|
-EXPORT_SYMBOL_GPL(aamatch_match);
|
|
-
|
|
-MODULE_DESCRIPTION("AppArmor match module (aamatch) [default]");
|
|
-MODULE_AUTHOR("Tony Jones <tonyj@suse.de>");
|
|
-MODULE_LICENSE("GPL");
|
|
Index: linux-2.6/security/apparmor/match/match_dfa.c
|
|
===================================================================
|
|
--- linux-2.6.orig/security/apparmor/match/match_dfa.c
|
|
+++ /dev/null
|
|
@@ -1,398 +0,0 @@
|
|
-/*
|
|
- * Copyright (C) 2002-2005 Novell/SUSE
|
|
- *
|
|
- * This program is free software; you can redistribute it and/or
|
|
- * modify it under the terms of the GNU General Public License as
|
|
- * published by the Free Software Foundation, version 2 of the
|
|
- * License.
|
|
- *
|
|
- * http://forge.novell.com/modules/xfmod/project/?apparmor
|
|
- *
|
|
- * AppArmor aamatch submodule (w/ pattern expansion).
|
|
- *
|
|
- */
|
|
-
|
|
-#include <asm/unaligned.h>
|
|
-#include <linux/module.h>
|
|
-#include "match.h"
|
|
-
|
|
-static const char *features="literal tailglob pattern=aadfa";
|
|
-
|
|
-#define YYTH_MAGIC 0x1B5E783D
|
|
-
|
|
-struct table_set_header {
|
|
- u32 th_magic; /* TH_MAGIC */
|
|
- u32 th_hsize;
|
|
- u32 th_ssize;
|
|
- u16 th_flags;
|
|
- char th_version[];
|
|
-};
|
|
-
|
|
-#define YYTD_ID_ACCEPT 1 /* 1 */
|
|
-#define YYTD_ID_BASE 2 /* 2 */
|
|
-#define YYTD_ID_CHK 3 /* 3 */
|
|
-#define YYTD_ID_DEF 4 /* 4 */
|
|
-#define YYTD_ID_EC 5 /* 5 */
|
|
-#define YYTD_ID_NXT 6 /* 8 */
|
|
-#define YYTD_ID_META 7 /* 6 */
|
|
-
|
|
-#define YYTD_DATA8 1
|
|
-#define YYTD_DATA16 2
|
|
-#define YYTD_DATA32 4
|
|
-
|
|
-struct table_header {
|
|
- u16 td_id;
|
|
- u16 td_flags;
|
|
- u32 td_hilen;
|
|
- u32 td_lolen;
|
|
- char td_data[];
|
|
-};
|
|
-
|
|
-#define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF - 1]->td_data))
|
|
-#define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE - 1]->td_data))
|
|
-#define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT - 1]->td_data))
|
|
-#define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK - 1]->td_data))
|
|
-#define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC - 1]->td_data))
|
|
-#define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT - 1]->td_data))
|
|
-
|
|
-struct aa_dfa {
|
|
- struct table_header *tables[YYTD_ID_NXT];
|
|
-
|
|
- struct table_set_header th;
|
|
-};
|
|
-
|
|
-#define ntohb(X) (X)
|
|
-
|
|
-#define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \
|
|
- do { \
|
|
- typeof(LEN) __i; \
|
|
- TYPE *__t = (TYPE *) TABLE; \
|
|
- TYPE *__b = (TYPE *) BLOB; \
|
|
- for (__i = 0; __i < LEN; __i++) { \
|
|
- __t[__i] = NTOHX(__b[__i]); \
|
|
- } \
|
|
- } while (0)
|
|
-
|
|
-static inline size_t pad64(size_t i)
|
|
-{
|
|
- return (i + (size_t)7) & ~(size_t)7;
|
|
-}
|
|
-
|
|
-static inline size_t table_size(size_t len, size_t el_size)
|
|
-{
|
|
- return pad64(sizeof(struct table_header) + len * el_size);
|
|
-}
|
|
-
|
|
-static struct table_header *unpack_table(void *blob, size_t bsize)
|
|
-{
|
|
- struct table_header *table = NULL;
|
|
- struct table_header th;
|
|
- size_t tsize;
|
|
-
|
|
- if (bsize < sizeof(struct table_header))
|
|
- goto out;
|
|
-
|
|
- th.td_id = ntohs(get_unaligned((u16 *) (blob)));
|
|
- th.td_flags = ntohs(get_unaligned((u16 *) (blob + 2)));
|
|
- th.td_lolen = ntohl(get_unaligned((u32 *) (blob + 8)));
|
|
- blob += sizeof(struct table_header);
|
|
-
|
|
- if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 ||
|
|
- th.td_flags == YYTD_DATA8))
|
|
- goto out;
|
|
-
|
|
- tsize = table_size(th.td_lolen, th.td_flags);
|
|
- if (bsize < tsize)
|
|
- goto out;
|
|
-
|
|
- table = kmalloc(tsize, GFP_KERNEL);
|
|
- if (table) {
|
|
- *table = th;
|
|
- if (th.td_flags == YYTD_DATA8)
|
|
- UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
|
|
- u8, ntohb);
|
|
- else if (th.td_flags == YYTD_DATA16)
|
|
- UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
|
|
- u16, ntohs);
|
|
- else
|
|
- UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
|
|
- u32, ntohl);
|
|
- }
|
|
-
|
|
-out:
|
|
- return table;
|
|
-}
|
|
-
|
|
-static int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size)
|
|
-{
|
|
- int i;
|
|
- int error = -ENOMEM;
|
|
-
|
|
- /* get dfa table set header */
|
|
- if (size < sizeof(struct table_set_header))
|
|
- goto fail;
|
|
-
|
|
- dfa->th.th_magic = ntohl(get_unaligned((u32 *) (blob + 0)));
|
|
- dfa->th.th_hsize = ntohl(get_unaligned((u32 *) (blob + 4)));
|
|
- dfa->th.th_ssize = ntohl(get_unaligned((u32 *) (blob + 8)));
|
|
- dfa->th.th_flags = ntohs(get_unaligned((u16 *) (blob + 12)));
|
|
-
|
|
- if (dfa->th.th_magic != YYTH_MAGIC)
|
|
- goto fail;
|
|
-
|
|
- if (size < dfa->th.th_hsize)
|
|
- goto fail;
|
|
-
|
|
- blob += dfa->th.th_hsize;
|
|
- size -= dfa->th.th_hsize;
|
|
-
|
|
- while (size > 0) {
|
|
- struct table_header *table;
|
|
- table = unpack_table(blob, size);
|
|
- if (!table)
|
|
- goto fail;
|
|
-
|
|
- switch(table->td_id) {
|
|
- case YYTD_ID_ACCEPT:
|
|
- case YYTD_ID_BASE:
|
|
- dfa->tables[table->td_id - 1] = table;
|
|
- if (table->td_flags != YYTD_DATA32)
|
|
- goto fail_proto;
|
|
- break;
|
|
- case YYTD_ID_DEF:
|
|
- case YYTD_ID_NXT:
|
|
- case YYTD_ID_CHK:
|
|
- dfa->tables[table->td_id - 1] = table;
|
|
- if (table->td_flags != YYTD_DATA16)
|
|
- goto fail_proto;
|
|
- break;
|
|
- case YYTD_ID_EC:
|
|
- dfa->tables[table->td_id - 1] = table;
|
|
- if (table->td_flags != YYTD_DATA8)
|
|
- goto fail_proto;
|
|
- break;
|
|
- default:
|
|
- kfree(table);
|
|
- goto fail_proto;
|
|
- }
|
|
-
|
|
- blob += table_size(table->td_lolen, table->td_flags);
|
|
- size -= table_size(table->td_lolen, table->td_flags);
|
|
- }
|
|
-
|
|
- error = 0;
|
|
-
|
|
- return error;
|
|
-
|
|
-fail_proto:
|
|
- error = -EPROTO;
|
|
-fail:
|
|
- for (i = 0; i < YYTD_ID_NXT; i++) {
|
|
- if (dfa->tables[i]) {
|
|
- kfree(dfa->tables[i]);
|
|
- dfa->tables[i] = NULL;
|
|
- }
|
|
- }
|
|
- return error;
|
|
-}
|
|
-
|
|
-/**
|
|
- * verify_dfa - verify that all the transitions and states in the dfa tables
|
|
- * are in bounds.
|
|
- * @dfa: dfa to test
|
|
- *
|
|
- * assumes dfa has gone through the verification done by unpacking
|
|
- */
|
|
-static int verify_dfa(struct aa_dfa *dfa)
|
|
-{
|
|
- size_t i, state_count, trans_count;
|
|
- int error = -EPROTO;
|
|
-
|
|
- /* check that required tables exist */
|
|
- if (!(dfa->tables[YYTD_ID_ACCEPT -1 ] &&
|
|
- dfa->tables[YYTD_ID_DEF - 1] &&
|
|
- dfa->tables[YYTD_ID_BASE - 1] &&
|
|
- dfa->tables[YYTD_ID_NXT - 1] &&
|
|
- dfa->tables[YYTD_ID_CHK - 1]))
|
|
- goto out;
|
|
-
|
|
- /* accept.size == default.size == base.size */
|
|
- state_count = dfa->tables[YYTD_ID_BASE - 1]->td_lolen;
|
|
- if (!(state_count == dfa->tables[YYTD_ID_DEF - 1]->td_lolen &&
|
|
- state_count == dfa->tables[YYTD_ID_ACCEPT - 1]->td_lolen))
|
|
- goto out;
|
|
-
|
|
- /* next.size == chk.size */
|
|
- trans_count = dfa->tables[YYTD_ID_NXT - 1]->td_lolen;
|
|
- if (trans_count != dfa->tables[YYTD_ID_CHK - 1]->td_lolen)
|
|
- goto out;
|
|
-
|
|
- /* if equivalence classes then its table must be 256 */
|
|
- if (dfa->tables[YYTD_ID_EC - 1] &&
|
|
- dfa->tables[YYTD_ID_EC - 1]->td_lolen != 256)
|
|
- goto out;
|
|
-
|
|
- for (i = 0; i < state_count; i++) {
|
|
- if (DEFAULT_TABLE(dfa)[i] >= state_count)
|
|
- goto out;
|
|
- if (BASE_TABLE(dfa)[i] >= trans_count + 256)
|
|
- goto out;
|
|
- }
|
|
-
|
|
- for (i = 0; i < trans_count ; i++) {
|
|
- if (NEXT_TABLE(dfa)[i] >= state_count)
|
|
- goto out;
|
|
- if (CHECK_TABLE(dfa)[i] >= state_count)
|
|
- goto out;
|
|
- }
|
|
-
|
|
- error = 0;
|
|
-out:
|
|
- return error;
|
|
-}
|
|
-
|
|
-/**
|
|
- * aadfa_label - return the permissions associated with @state
|
|
- * @dfa: dfa to get state permission from
|
|
- * @state: state in the dfa for which to get a label
|
|
- *
|
|
- * Assumes that state is a valid state of the dfa
|
|
- *
|
|
- * Returns the label associated with @state. 0 indicates the state
|
|
- * is no-accepting/provides no permissions.
|
|
- */
|
|
-inline unsigned int aadfa_label(struct aa_dfa *dfa, int state)
|
|
-{
|
|
- return ACCEPT_TABLE(dfa)[state] & AA_VALID_PERM_MASK;
|
|
-}
|
|
-
|
|
-/**
|
|
- * aadfa_match - match @path against @dfa starting in @state
|
|
- * @dfa: the dfa to match @path against
|
|
- * @state: the state to start matching in
|
|
- * @path: the path to match against the dfa
|
|
- *
|
|
- * aadfa_match will match the full path length and return the state it
|
|
- * finished matching in. The final state returned can be used to
|
|
- * lookup the accepting label or as a starting point to continue matching
|
|
- * with a new string if the path has been broken into multiple components.
|
|
- */
|
|
-static unsigned int aadfa_match(struct aa_dfa *dfa, unsigned int state,
|
|
- const char *path)
|
|
-{
|
|
- u8 *s = (u8 *) path;
|
|
- u16 *def = DEFAULT_TABLE(dfa);
|
|
- u32 *base = BASE_TABLE(dfa);
|
|
- u16 *next = NEXT_TABLE(dfa);
|
|
- u16 *check = CHECK_TABLE(dfa);
|
|
- unsigned int pos;
|
|
-
|
|
- /* current state is <state>, matching character *s */
|
|
- if (dfa->tables[YYTD_ID_EC - 1]) {
|
|
- u8 *equiv = EQUIV_TABLE(dfa);
|
|
- for ( ; *s; ++s) {
|
|
- pos = base[state] + equiv[*s];
|
|
- if (check[pos] == state)
|
|
- state = next[pos];
|
|
- else
|
|
- state = def[state];
|
|
- }
|
|
- } else {
|
|
- for ( ; *s; ++s) {
|
|
- pos = base[state] + *s;
|
|
- if (check[pos] == state)
|
|
- state = next[pos];
|
|
- else
|
|
- state = def[state];
|
|
- }
|
|
- }
|
|
- return state;
|
|
-}
|
|
-
|
|
-void* aamatch_alloc(enum entry_match_type entry_type)
|
|
-{
|
|
- void *ptr=NULL;
|
|
-
|
|
- if (entry_type == aa_entry_pattern) {
|
|
- ptr = kmalloc(sizeof(struct aa_dfa), GFP_KERNEL);
|
|
- if (ptr)
|
|
- memset(ptr, 0, sizeof(struct aa_dfa));
|
|
- else
|
|
- ptr=ERR_PTR(-ENOMEM);
|
|
- } else if (entry_type != aa_entry_literal &&
|
|
- entry_type != aa_entry_tailglob) {
|
|
- ptr = ERR_PTR(-EINVAL);
|
|
- }
|
|
-
|
|
- return ptr;
|
|
-}
|
|
-
|
|
-void aamatch_free(void *ptr)
|
|
-{
|
|
- if (ptr) {
|
|
- int i;
|
|
- struct aa_dfa *dfa = (struct aa_dfa *) ptr;
|
|
- for (i = 0; i < YYTD_ID_NXT; i++) {
|
|
- if (dfa->tables[i])
|
|
- kfree(dfa->tables[i]);
|
|
- }
|
|
- }
|
|
- kfree(ptr);
|
|
-}
|
|
-
|
|
-const char *aamatch_features(void)
|
|
-{
|
|
- return features;
|
|
-}
|
|
-
|
|
-int aamatch_serialize(void *entry_extradata, struct aa_ext *e,
|
|
- aamatch_serializecb cb)
|
|
-{
|
|
- int error = 0;
|
|
- char *blob = NULL;
|
|
- size_t size;
|
|
-
|
|
- struct aa_dfa *dfa = (struct aa_dfa *) entry_extradata;
|
|
- if (dfa == NULL)
|
|
- goto done;
|
|
-
|
|
- error = -EPROTO;
|
|
- size = cb(e, AA_BLOB_LOC, &blob, "aadfa");
|
|
-
|
|
- if (size)
|
|
- error = unpack_dfa(dfa, blob, size);
|
|
-
|
|
- if (!error)
|
|
- error = verify_dfa(dfa);
|
|
-done:
|
|
- return error;
|
|
-}
|
|
-
|
|
-int aamatch_match(struct aa_entry *entry, const char *pathname)
|
|
-{
|
|
- int ret;
|
|
-
|
|
- if (entry->type == aa_entry_pattern) {
|
|
- unsigned int state;
|
|
- struct aa_dfa *dfa = (struct aa_dfa *) entry->extradata;
|
|
-
|
|
- state = aadfa_match(dfa, 1, pathname);
|
|
-
|
|
- /* label returned is the permissions of the matched state */
|
|
- ret = aadfa_label(dfa, state);
|
|
- } else {
|
|
- ret = aamatch_match_common(entry, pathname);
|
|
- }
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-EXPORT_SYMBOL_GPL(aamatch_alloc);
|
|
-EXPORT_SYMBOL_GPL(aamatch_free);
|
|
-EXPORT_SYMBOL_GPL(aamatch_features);
|
|
-EXPORT_SYMBOL_GPL(aamatch_serialize);
|
|
-EXPORT_SYMBOL_GPL(aamatch_match);
|
|
-
|
|
-MODULE_DESCRIPTION("AppArmor aa_match module [dfa]");
|
|
-MODULE_AUTHOR("John Johansen <jjohansen@suse.de>");
|
|
-MODULE_LICENSE("GPL");
|
|
Index: linux-2.6/security/apparmor/match/match_pcre.c
|
|
===================================================================
|
|
--- linux-2.6.orig/security/apparmor/match/match_pcre.c
|
|
+++ /dev/null
|
|
@@ -1,168 +0,0 @@
|
|
-/*
|
|
- * Copyright (C) 2002-2005 Novell/SUSE
|
|
- *
|
|
- * This program is free software; you can redistribute it and/or
|
|
- * modify it under the terms of the GNU General Public License as
|
|
- * published by the Free Software Foundation, version 2 of the
|
|
- * License.
|
|
- *
|
|
- * http://forge.novell.com/modules/xfmod/project/?apparmor
|
|
- *
|
|
- * AppArmor aamatch submodule (w/ pattern expansion).
|
|
- *
|
|
- * This module makes use of a slightly modified version of the PCRE
|
|
- * library developed by Philip Hazel <ph10@cam.ac.uk>. See the files
|
|
- * pcre_* in this directory.
|
|
- */
|
|
-
|
|
-#include <linux/module.h>
|
|
-#include "match.h"
|
|
-#include "pcre_exec.h"
|
|
-#include "pcre_tables.h"
|
|
-
|
|
-static const char *features="literal tailglob pattern=pcre";
|
|
-
|
|
-struct aamatch_entry
|
|
-{
|
|
- char *pattern;
|
|
- pcre *compiled;
|
|
-};
|
|
-
|
|
-void* aamatch_alloc(enum entry_match_type entry_type)
|
|
-{
|
|
-void *ptr=NULL;
|
|
-
|
|
- if (entry_type == aa_entry_pattern) {
|
|
- ptr = kmalloc(sizeof(struct aamatch_entry), GFP_KERNEL);
|
|
- if (ptr)
|
|
- memset(ptr, 0, sizeof(struct aamatch_entry));
|
|
- else
|
|
- ptr=ERR_PTR(-ENOMEM);
|
|
- } else if (entry_type != aa_entry_literal &&
|
|
- entry_type != aa_entry_tailglob) {
|
|
- ptr = ERR_PTR(-EINVAL);
|
|
- }
|
|
-
|
|
- return ptr;
|
|
-}
|
|
-
|
|
-void aamatch_free(void *ptr)
|
|
-{
|
|
- if (ptr) {
|
|
- struct aamatch_entry *ed = (struct aamatch_entry *) ptr;
|
|
- kfree(ed->pattern);
|
|
- kfree(ed->compiled); /* allocated by AA_READ_X */
|
|
- }
|
|
- kfree(ptr);
|
|
-}
|
|
-
|
|
-const char *aamatch_features(void)
|
|
-{
|
|
- return features;
|
|
-}
|
|
-
|
|
-int aamatch_serialize(void *entry_extradata, struct aa_ext *e,
|
|
- aamatch_serializecb cb)
|
|
-{
|
|
-#define AA_READ_X(E, C, D, N) \
|
|
- do { \
|
|
- if (!cb((E), (C), (D), (N))) { \
|
|
- error = -EINVAL; \
|
|
- goto done; \
|
|
- }\
|
|
- } while (0)
|
|
-
|
|
- int error = 0;
|
|
- u32 size, magic, opts;
|
|
- u8 t_char;
|
|
- struct aamatch_entry *ed = (struct aamatch_entry *) entry_extradata;
|
|
-
|
|
- if (ed == NULL)
|
|
- goto done;
|
|
-
|
|
- AA_READ_X(e, AA_DYN_STRING, &ed->pattern, NULL);
|
|
-
|
|
- /* size determines the real size of the pcre struct,
|
|
- it is size_t - sizeof(pcre) on user side.
|
|
- uschar must be the same in user and kernel space */
|
|
- /* check that we are processing the correct structure */
|
|
- AA_READ_X(e, AA_STRUCT, NULL, "pcre");
|
|
- AA_READ_X(e, AA_U32, &size, NULL);
|
|
- AA_READ_X(e, AA_U32, &magic, NULL);
|
|
-
|
|
- /* the allocation of pcre is delayed because it depends on the size
|
|
- * of the pattern */
|
|
- ed->compiled = (pcre *) kmalloc(size + sizeof(pcre), GFP_KERNEL);
|
|
- if (!ed->compiled) {
|
|
- error = -ENOMEM;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- memset(ed->compiled, 0, size + sizeof(pcre));
|
|
- ed->compiled->magic_number = magic;
|
|
- ed->compiled->size = size + sizeof(pcre);
|
|
-
|
|
- AA_READ_X(e, AA_U32, &opts, NULL);
|
|
- ed->compiled->options = opts;
|
|
- AA_READ_X(e, AA_U16, &ed->compiled->top_bracket, NULL);
|
|
- AA_READ_X(e, AA_U16, &ed->compiled->top_backref, NULL);
|
|
- AA_READ_X(e, AA_U8, &t_char, NULL);
|
|
- ed->compiled->first_char = t_char;
|
|
- AA_READ_X(e, AA_U8, &t_char, NULL);
|
|
- ed->compiled->req_char = t_char;
|
|
- AA_READ_X(e, AA_U8, &t_char, NULL);
|
|
- ed->compiled->code[0] = t_char;
|
|
-
|
|
- AA_READ_X(e, AA_STATIC_BLOB, &ed->compiled->code[1], NULL);
|
|
-
|
|
- AA_READ_X(e, AA_STRUCTEND, NULL, NULL);
|
|
-
|
|
- /* stitch in pcre patterns, it was NULLed out by parser
|
|
- * pcre_default_tables defined in pcre_tables.h */
|
|
- ed->compiled->tables = pcre_default_tables;
|
|
-
|
|
-done:
|
|
- if (error != 0 && ed) {
|
|
- kfree(ed->pattern); /* allocated by AA_READ_X */
|
|
- kfree(ed->compiled);
|
|
- ed->pattern = NULL;
|
|
- ed->compiled = NULL;
|
|
- }
|
|
-
|
|
- return error;
|
|
-}
|
|
-
|
|
-int aamatch_match(struct aa_entry *entry, const char *pathname)
|
|
-{
|
|
- int ret;
|
|
-
|
|
- if (entry->type == aa_entry_pattern) {
|
|
- int pcreret;
|
|
- struct aamatch_entry *ed =
|
|
- (struct aamatch_entry *) entry->extradata;
|
|
-
|
|
- pcreret = pcre_exec(ed->compiled, NULL,
|
|
- pathname, strlen(pathname),
|
|
- 0, 0, NULL, 0);
|
|
-
|
|
- ret = (pcreret >= 0) ? entry->mode : 0;
|
|
-
|
|
- // XXX - this needs access to subdomain_debug, hmmm
|
|
- //AA_DEBUG("%s(%d): %s %s %d\n", __FUNCTION__,
|
|
- // ret, pathname, ed->pattern, pcreret);
|
|
- } else {
|
|
- ret = aamatch_match_common(entry, pathname);
|
|
- }
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-EXPORT_SYMBOL_GPL(aamatch_alloc);
|
|
-EXPORT_SYMBOL_GPL(aamatch_free);
|
|
-EXPORT_SYMBOL_GPL(aamatch_features);
|
|
-EXPORT_SYMBOL_GPL(aamatch_serialize);
|
|
-EXPORT_SYMBOL_GPL(aamatch_match);
|
|
-
|
|
-MODULE_DESCRIPTION("AppArmor aa_match module [pcre]");
|
|
-MODULE_AUTHOR("Tony Jones <tonyj@suse.de>");
|
|
-MODULE_LICENSE("GPL");
|
|
Index: linux-2.6/security/apparmor/match/pcre_exec.c
|
|
===================================================================
|
|
--- linux-2.6.orig/security/apparmor/match/pcre_exec.c
|
|
+++ /dev/null
|
|
@@ -1,1945 +0,0 @@
|
|
-/*
|
|
- * This is a modified version of pcre.c containing only the code/data
|
|
- * required to support pcre_exec()
|
|
- */
|
|
-
|
|
-
|
|
-/*************************************************
|
|
-* Perl-Compatible Regular Expressions *
|
|
-*************************************************/
|
|
-
|
|
-/*
|
|
-This is a library of functions to support regular expressions whose syntax
|
|
-and semantics are as close as possible to those of the Perl 5 language. See
|
|
-the file Tech.Notes for some information on the internals.
|
|
-
|
|
-Written by: Philip Hazel <ph10@cam.ac.uk>
|
|
-
|
|
- Copyright (c) 1997-2001 University of Cambridge
|
|
-
|
|
------------------------------------------------------------------------------
|
|
-Permission is granted to anyone to use this software for any purpose on any
|
|
-computer system, and to redistribute it freely, subject to the following
|
|
-restrictions:
|
|
-
|
|
-1. This software is distributed in the hope that it will be useful,
|
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
-
|
|
-2. The origin of this software must not be misrepresented, either by
|
|
- explicit claim or by omission.
|
|
-
|
|
-3. Altered versions must be plainly marked as such, and must not be
|
|
- misrepresented as being the original software.
|
|
-
|
|
-4. If PCRE is embedded in any software that is released under the GNU
|
|
- General Purpose Licence (GPL), then the terms of that licence shall
|
|
- supersede any condition above with which it is incompatible.
|
|
------------------------------------------------------------------------------
|
|
-*/
|
|
-
|
|
-
|
|
-/* Define DEBUG to get debugging output on stdout. */
|
|
-
|
|
-/* #define DEBUG */
|
|
-
|
|
-/* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef
|
|
-inline, and there are *still* stupid compilers about that don't like indented
|
|
-pre-processor statements. I suppose it's only been 10 years... */
|
|
-
|
|
-#ifdef DEBUG
|
|
-#define DPRINTF(p) PCRE_PRINTF p
|
|
-#else
|
|
-#define DPRINTF(p) /*nothing*/
|
|
-#endif
|
|
-
|
|
-/* Include the internals header, which itself includes Standard C headers plus
|
|
-the external pcre header. */
|
|
-
|
|
-#include "pcre_exec.h"
|
|
-
|
|
-
|
|
-/* ---- CODE DELETED ---- */
|
|
-
|
|
-
|
|
-/* Min and max values for the common repeats; for the maxima, 0 => infinity */
|
|
-
|
|
-static const char rep_min[] = { 0, 0, 1, 1, 0, 0 };
|
|
-static const char rep_max[] = { 0, 0, 0, 0, 1, 1 };
|
|
-
|
|
-
|
|
-/* ---- CODE DELETED ---- */
|
|
-
|
|
-
|
|
-/* Structure for building a chain of data that actually lives on the
|
|
- * stack, for holding the values of the subject pointer at the start of each
|
|
- * subpattern, so as to detect when an empty string has been matched by a
|
|
- * subpattern - to break infinite loops. */
|
|
-
|
|
-typedef struct eptrblock {
|
|
- struct eptrblock *prev;
|
|
- const uschar *saved_eptr;
|
|
-} eptrblock;
|
|
-
|
|
-/* Flag bits for the match() function */
|
|
-
|
|
-#define match_condassert 0x01 /* Called to check a condition assertion */
|
|
-#define match_isgroup 0x02 /* Set if start of bracketed group */
|
|
-
|
|
-
|
|
-/* ---- CODE DELETED ---- */
|
|
-
|
|
-
|
|
-/*************************************************
|
|
- * * Global variables *
|
|
- * *************************************************/
|
|
-
|
|
-/* PCRE is thread-clean and doesn't use any global variables in the normal
|
|
- * sense. However, it calls memory allocation and free functions via the two
|
|
- * indirections below, which are can be changed by the caller, but are shared
|
|
- * between all threads. */
|
|
-
|
|
-#ifdef __KERNEL__
|
|
-static void *kern_malloc(size_t sz)
|
|
-{
|
|
- return kmalloc(sz, GFP_KERNEL);
|
|
-}
|
|
-void *(*pcre_malloc)(size_t) = kern_malloc;
|
|
-void (*pcre_free)(const void *) = kfree;
|
|
-#else
|
|
-void *(*pcre_malloc)(size_t) = malloc;
|
|
-void (*pcre_free)(const void *) = free;
|
|
-#endif
|
|
-
|
|
-
|
|
-/*************************************************
|
|
- * * Macros and tables for character handling *
|
|
- * *************************************************/
|
|
-
|
|
-/* When UTF-8 encoding is being used, a character is no longer just a single
|
|
- * byte. The macros for character handling generate simple sequences when used in
|
|
- * byte-mode, and more complicated ones for UTF-8 characters. */
|
|
-
|
|
-#ifndef SUPPORT_UTF8
|
|
-#define GETCHARINC(c, eptr) c = *eptr++;
|
|
-#define GETCHARLEN(c, eptr, len) c = *eptr;
|
|
-#define BACKCHAR(eptr)
|
|
-#endif
|
|
-
|
|
-/* ---- CODE DELETED ---- */
|
|
-
|
|
-#ifdef DEBUG
|
|
-/*************************************************
|
|
-* Debugging function to print chars *
|
|
-*************************************************/
|
|
-
|
|
-/* Print a sequence of chars in printable format, stopping at the end of the
|
|
-subject if the requested.
|
|
-
|
|
-Arguments:
|
|
- p points to characters
|
|
- length number to print
|
|
- is_subject TRUE if printing from within md->start_subject
|
|
- md pointer to matching data block, if is_subject is TRUE
|
|
-
|
|
-Returns: nothing
|
|
-*/
|
|
-
|
|
-static void
|
|
-pchars(const uschar *p, int length, BOOL is_subject, match_data *md)
|
|
-{
|
|
-int c;
|
|
-if (is_subject && length > md->end_subject - p) length = md->end_subject - p;
|
|
-while (length-- > 0)
|
|
- if (isprint(c = *(p++))) PCRE_PRINTF("%c", c); else PCRE_PRINTF("\\x%02x", c);
|
|
-}
|
|
-#endif /* DEBUG */
|
|
-
|
|
-/* ---- CODE DELETED ---- */
|
|
-
|
|
-
|
|
-/*************************************************
|
|
-* Match a back-reference *
|
|
-*************************************************/
|
|
-
|
|
-/* If a back reference hasn't been set, the length that is passed is greater
|
|
-than the number of characters left in the string, so the match fails.
|
|
-
|
|
-Arguments:
|
|
- offset index into the offset vector
|
|
- eptr points into the subject
|
|
- length length to be matched
|
|
- md points to match data block
|
|
- ims the ims flags
|
|
-
|
|
-Returns: TRUE if matched
|
|
-*/
|
|
-
|
|
-static BOOL
|
|
-match_ref(int offset, register const uschar *eptr, int length, match_data *md,
|
|
- unsigned long int ims)
|
|
-{
|
|
-const uschar *p = md->start_subject + md->offset_vector[offset];
|
|
-
|
|
-#ifdef DEBUG
|
|
-if (eptr >= md->end_subject)
|
|
- PCRE_PRINTF("matching subject <null>");
|
|
-else
|
|
- {
|
|
- PCRE_PRINTF("matching subject ");
|
|
- pchars(eptr, length, TRUE, md);
|
|
- }
|
|
-PCRE_PRINTF(" against backref ");
|
|
-pchars(p, length, FALSE, md);
|
|
-PCRE_PRINTF("\n");
|
|
-#endif
|
|
-
|
|
-/* Always fail if not enough characters left */
|
|
-
|
|
-if (length > md->end_subject - eptr) return FALSE;
|
|
-
|
|
-/* Separate the caselesss case for speed */
|
|
-
|
|
-if ((ims & PCRE_CASELESS) != 0)
|
|
- {
|
|
- while (length-- > 0)
|
|
- if (md->lcc[*p++] != md->lcc[*eptr++]) return FALSE;
|
|
- }
|
|
-else
|
|
- { while (length-- > 0) if (*p++ != *eptr++) return FALSE; }
|
|
-
|
|
-return TRUE;
|
|
-}
|
|
-
|
|
-
|
|
-/*************************************************
|
|
-* Match from current position *
|
|
-*************************************************/
|
|
-
|
|
-/* On entry ecode points to the first opcode, and eptr to the first character
|
|
-in the subject string, while eptrb holds the value of eptr at the start of the
|
|
-last bracketed group - used for breaking infinite loops matching zero-length
|
|
-strings.
|
|
-
|
|
-Arguments:
|
|
- eptr pointer in subject
|
|
- ecode position in code
|
|
- offset_top current top pointer
|
|
- md pointer to "static" info for the match
|
|
- ims current /i, /m, and /s options
|
|
- eptrb pointer to chain of blocks containing eptr at start of
|
|
- brackets - for testing for empty matches
|
|
- flags can contain
|
|
- match_condassert - this is an assertion condition
|
|
- match_isgroup - this is the start of a bracketed group
|
|
-
|
|
-Returns: TRUE if matched
|
|
-*/
|
|
-
|
|
-static BOOL
|
|
-match(register const uschar *eptr, register const uschar *ecode,
|
|
- int offset_top, match_data *md, unsigned long int ims, eptrblock *eptrb,
|
|
- int flags)
|
|
-{
|
|
-unsigned long int original_ims = ims; /* Save for resetting on ')' */
|
|
-eptrblock newptrb;
|
|
-
|
|
-/* At the start of a bracketed group, add the current subject pointer to the
|
|
-stack of such pointers, to be re-instated at the end of the group when we hit
|
|
-the closing ket. When match() is called in other circumstances, we don't add to
|
|
-the stack. */
|
|
-
|
|
-if ((flags & match_isgroup) != 0)
|
|
- {
|
|
- newptrb.prev = eptrb;
|
|
- newptrb.saved_eptr = eptr;
|
|
- eptrb = &newptrb;
|
|
- }
|
|
-
|
|
-/* Now start processing the operations. */
|
|
-
|
|
-for (;;)
|
|
- {
|
|
- int op = (int)*ecode;
|
|
- int min, max, ctype;
|
|
- register int i;
|
|
- register int c;
|
|
- BOOL minimize = FALSE;
|
|
-
|
|
- /* Opening capturing bracket. If there is space in the offset vector, save
|
|
- the current subject position in the working slot at the top of the vector. We
|
|
- mustn't change the current values of the data slot, because they may be set
|
|
- from a previous iteration of this group, and be referred to by a reference
|
|
- inside the group.
|
|
-
|
|
- If the bracket fails to match, we need to restore this value and also the
|
|
- values of the final offsets, in case they were set by a previous iteration of
|
|
- the same bracket.
|
|
-
|
|
- If there isn't enough space in the offset vector, treat this as if it were a
|
|
- non-capturing bracket. Don't worry about setting the flag for the error case
|
|
- here; that is handled in the code for KET. */
|
|
-
|
|
- if (op > OP_BRA)
|
|
- {
|
|
- int offset;
|
|
- int number = op - OP_BRA;
|
|
-
|
|
- /* For extended extraction brackets (large number), we have to fish out the
|
|
- number from a dummy opcode at the start. */
|
|
-
|
|
- if (number > EXTRACT_BASIC_MAX) number = (ecode[4] << 8) | ecode[5];
|
|
- offset = number << 1;
|
|
-
|
|
-#ifdef DEBUG
|
|
- PCRE_PRINTF("start bracket %d subject=", number);
|
|
- pchars(eptr, 16, TRUE, md);
|
|
- PCRE_PRINTF("\n");
|
|
-#endif
|
|
-
|
|
- if (offset < md->offset_max)
|
|
- {
|
|
- int save_offset1 = md->offset_vector[offset];
|
|
- int save_offset2 = md->offset_vector[offset+1];
|
|
- int save_offset3 = md->offset_vector[md->offset_end - number];
|
|
-
|
|
- DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3));
|
|
- md->offset_vector[md->offset_end - number] = eptr - md->start_subject;
|
|
-
|
|
- do
|
|
- {
|
|
- if (match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup))
|
|
- return TRUE;
|
|
- ecode += (ecode[1] << 8) + ecode[2];
|
|
- }
|
|
- while (*ecode == OP_ALT);
|
|
-
|
|
- DPRINTF(("bracket %d failed\n", number));
|
|
-
|
|
- md->offset_vector[offset] = save_offset1;
|
|
- md->offset_vector[offset+1] = save_offset2;
|
|
- md->offset_vector[md->offset_end - number] = save_offset3;
|
|
-
|
|
- return FALSE;
|
|
- }
|
|
-
|
|
- /* Insufficient room for saving captured contents */
|
|
-
|
|
- else op = OP_BRA;
|
|
- }
|
|
-
|
|
- /* Other types of node can be handled by a switch */
|
|
-
|
|
- switch(op)
|
|
- {
|
|
- case OP_BRA: /* Non-capturing bracket: optimized */
|
|
- DPRINTF(("start bracket 0\n"));
|
|
- do
|
|
- {
|
|
- if (match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup))
|
|
- return TRUE;
|
|
- ecode += (ecode[1] << 8) + ecode[2];
|
|
- }
|
|
- while (*ecode == OP_ALT);
|
|
- DPRINTF(("bracket 0 failed\n"));
|
|
- return FALSE;
|
|
-
|
|
- /* Conditional group: compilation checked that there are no more than
|
|
- two branches. If the condition is false, skipping the first branch takes us
|
|
- past the end if there is only one branch, but that's OK because that is
|
|
- exactly what going to the ket would do. */
|
|
-
|
|
- case OP_COND:
|
|
- if (ecode[3] == OP_CREF) /* Condition is extraction test */
|
|
- {
|
|
- int offset = (ecode[4] << 9) | (ecode[5] << 1); /* Doubled ref number */
|
|
- return match(eptr,
|
|
- ecode + ((offset < offset_top && md->offset_vector[offset] >= 0)?
|
|
- 6 : 3 + (ecode[1] << 8) + ecode[2]),
|
|
- offset_top, md, ims, eptrb, match_isgroup);
|
|
- }
|
|
-
|
|
- /* The condition is an assertion. Call match() to evaluate it - setting
|
|
- the final argument TRUE causes it to stop at the end of an assertion. */
|
|
-
|
|
- else
|
|
- {
|
|
- if (match(eptr, ecode+3, offset_top, md, ims, NULL,
|
|
- match_condassert | match_isgroup))
|
|
- {
|
|
- ecode += 3 + (ecode[4] << 8) + ecode[5];
|
|
- while (*ecode == OP_ALT) ecode += (ecode[1] << 8) + ecode[2];
|
|
- }
|
|
- else ecode += (ecode[1] << 8) + ecode[2];
|
|
- return match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup);
|
|
- }
|
|
- /* Control never reaches here */
|
|
-
|
|
- /* Skip over conditional reference or large extraction number data if
|
|
- encountered. */
|
|
-
|
|
- case OP_CREF:
|
|
- case OP_BRANUMBER:
|
|
- ecode += 3;
|
|
- break;
|
|
-
|
|
- /* End of the pattern. If PCRE_NOTEMPTY is set, fail if we have matched
|
|
- an empty string - recursion will then try other alternatives, if any. */
|
|
-
|
|
- case OP_END:
|
|
- if (md->notempty && eptr == md->start_match) return FALSE;
|
|
- md->end_match_ptr = eptr; /* Record where we ended */
|
|
- md->end_offset_top = offset_top; /* and how many extracts were taken */
|
|
- return TRUE;
|
|
-
|
|
- /* Change option settings */
|
|
-
|
|
- case OP_OPT:
|
|
- ims = ecode[1];
|
|
- ecode += 2;
|
|
- DPRINTF(("ims set to %02lx\n", ims));
|
|
- break;
|
|
-
|
|
- /* Assertion brackets. Check the alternative branches in turn - the
|
|
- matching won't pass the KET for an assertion. If any one branch matches,
|
|
- the assertion is true. Lookbehind assertions have an OP_REVERSE item at the
|
|
- start of each branch to move the current point backwards, so the code at
|
|
- this level is identical to the lookahead case. */
|
|
-
|
|
- case OP_ASSERT:
|
|
- case OP_ASSERTBACK:
|
|
- do
|
|
- {
|
|
- if (match(eptr, ecode+3, offset_top, md, ims, NULL, match_isgroup)) break;
|
|
- ecode += (ecode[1] << 8) + ecode[2];
|
|
- }
|
|
- while (*ecode == OP_ALT);
|
|
- if (*ecode == OP_KET) return FALSE;
|
|
-
|
|
- /* If checking an assertion for a condition, return TRUE. */
|
|
-
|
|
- if ((flags & match_condassert) != 0) return TRUE;
|
|
-
|
|
- /* Continue from after the assertion, updating the offsets high water
|
|
- mark, since extracts may have been taken during the assertion. */
|
|
-
|
|
- do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT);
|
|
- ecode += 3;
|
|
- offset_top = md->end_offset_top;
|
|
- continue;
|
|
-
|
|
- /* Negative assertion: all branches must fail to match */
|
|
-
|
|
- case OP_ASSERT_NOT:
|
|
- case OP_ASSERTBACK_NOT:
|
|
- do
|
|
- {
|
|
- if (match(eptr, ecode+3, offset_top, md, ims, NULL, match_isgroup))
|
|
- return FALSE;
|
|
- ecode += (ecode[1] << 8) + ecode[2];
|
|
- }
|
|
- while (*ecode == OP_ALT);
|
|
-
|
|
- if ((flags & match_condassert) != 0) return TRUE;
|
|
-
|
|
- ecode += 3;
|
|
- continue;
|
|
-
|
|
- /* Move the subject pointer back. This occurs only at the start of
|
|
- each branch of a lookbehind assertion. If we are too close to the start to
|
|
- move back, this match function fails. When working with UTF-8 we move
|
|
- back a number of characters, not bytes. */
|
|
-
|
|
- case OP_REVERSE:
|
|
-#ifdef SUPPORT_UTF8
|
|
- c = (ecode[1] << 8) + ecode[2];
|
|
- for (i = 0; i < c; i++)
|
|
- {
|
|
- eptr--;
|
|
- BACKCHAR(eptr)
|
|
- }
|
|
-#else
|
|
- eptr -= (ecode[1] << 8) + ecode[2];
|
|
-#endif
|
|
-
|
|
- if (eptr < md->start_subject) return FALSE;
|
|
- ecode += 3;
|
|
- break;
|
|
-
|
|
- /* Recursion matches the current regex, nested. If there are any capturing
|
|
- brackets started but not finished, we have to save their starting points
|
|
- and reinstate them after the recursion. However, we don't know how many
|
|
- such there are (offset_top records the completed total) so we just have
|
|
- to save all the potential data. There may be up to 99 such values, which
|
|
- is a bit large to put on the stack, but using malloc for small numbers
|
|
- seems expensive. As a compromise, the stack is used when there are fewer
|
|
- than 16 values to store; otherwise malloc is used. A problem is what to do
|
|
- if the malloc fails ... there is no way of returning to the top level with
|
|
- an error. Save the top 15 values on the stack, and accept that the rest
|
|
- may be wrong. */
|
|
-
|
|
- case OP_RECURSE:
|
|
- {
|
|
- BOOL rc;
|
|
- int *save;
|
|
- int stacksave[15];
|
|
-
|
|
- c = md->offset_max;
|
|
-
|
|
- if (c < 16) save = stacksave; else
|
|
- {
|
|
- save = (int *)(pcre_malloc)((c+1) * sizeof(int));
|
|
- if (save == NULL)
|
|
- {
|
|
- save = stacksave;
|
|
- c = 15;
|
|
- }
|
|
- }
|
|
-
|
|
- for (i = 1; i <= c; i++)
|
|
- save[i] = md->offset_vector[md->offset_end - i];
|
|
- rc = match(eptr, md->start_pattern, offset_top, md, ims, eptrb,
|
|
- match_isgroup);
|
|
- for (i = 1; i <= c; i++)
|
|
- md->offset_vector[md->offset_end - i] = save[i];
|
|
- if (save != stacksave) (pcre_free)(save);
|
|
- if (!rc) return FALSE;
|
|
-
|
|
- /* In case the recursion has set more capturing values, save the final
|
|
- number, then move along the subject till after the recursive match,
|
|
- and advance one byte in the pattern code. */
|
|
-
|
|
- offset_top = md->end_offset_top;
|
|
- eptr = md->end_match_ptr;
|
|
- ecode++;
|
|
- }
|
|
- break;
|
|
-
|
|
- /* "Once" brackets are like assertion brackets except that after a match,
|
|
- the point in the subject string is not moved back. Thus there can never be
|
|
- a move back into the brackets. Check the alternative branches in turn - the
|
|
- matching won't pass the KET for this kind of subpattern. If any one branch
|
|
- matches, we carry on as at the end of a normal bracket, leaving the subject
|
|
- pointer. */
|
|
-
|
|
- case OP_ONCE:
|
|
- {
|
|
- const uschar *prev = ecode;
|
|
- const uschar *saved_eptr = eptr;
|
|
-
|
|
- do
|
|
- {
|
|
- if (match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup))
|
|
- break;
|
|
- ecode += (ecode[1] << 8) + ecode[2];
|
|
- }
|
|
- while (*ecode == OP_ALT);
|
|
-
|
|
- /* If hit the end of the group (which could be repeated), fail */
|
|
-
|
|
- if (*ecode != OP_ONCE && *ecode != OP_ALT) return FALSE;
|
|
-
|
|
- /* Continue as from after the assertion, updating the offsets high water
|
|
- mark, since extracts may have been taken. */
|
|
-
|
|
- do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT);
|
|
-
|
|
- offset_top = md->end_offset_top;
|
|
- eptr = md->end_match_ptr;
|
|
-
|
|
- /* For a non-repeating ket, just continue at this level. This also
|
|
- happens for a repeating ket if no characters were matched in the group.
|
|
- This is the forcible breaking of infinite loops as implemented in Perl
|
|
- 5.005. If there is an options reset, it will get obeyed in the normal
|
|
- course of events. */
|
|
-
|
|
- if (*ecode == OP_KET || eptr == saved_eptr)
|
|
- {
|
|
- ecode += 3;
|
|
- break;
|
|
- }
|
|
-
|
|
- /* The repeating kets try the rest of the pattern or restart from the
|
|
- preceding bracket, in the appropriate order. We need to reset any options
|
|
- that changed within the bracket before re-running it, so check the next
|
|
- opcode. */
|
|
-
|
|
- if (ecode[3] == OP_OPT)
|
|
- {
|
|
- ims = (ims & ~PCRE_IMS) | ecode[4];
|
|
- DPRINTF(("ims set to %02lx at group repeat\n", ims));
|
|
- }
|
|
-
|
|
- if (*ecode == OP_KETRMIN)
|
|
- {
|
|
- if (match(eptr, ecode+3, offset_top, md, ims, eptrb, 0) ||
|
|
- match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup))
|
|
- return TRUE;
|
|
- }
|
|
- else /* OP_KETRMAX */
|
|
- {
|
|
- if (match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup) ||
|
|
- match(eptr, ecode+3, offset_top, md, ims, eptrb, 0)) return TRUE;
|
|
- }
|
|
- }
|
|
- return FALSE;
|
|
-
|
|
- /* An alternation is the end of a branch; scan along to find the end of the
|
|
- bracketed group and go to there. */
|
|
-
|
|
- case OP_ALT:
|
|
- do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT);
|
|
- break;
|
|
-
|
|
- /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating
|
|
- that it may occur zero times. It may repeat infinitely, or not at all -
|
|
- i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper
|
|
- repeat limits are compiled as a number of copies, with the optional ones
|
|
- preceded by BRAZERO or BRAMINZERO. */
|
|
-
|
|
- case OP_BRAZERO:
|
|
- {
|
|
- const uschar *next = ecode+1;
|
|
- if (match(eptr, next, offset_top, md, ims, eptrb, match_isgroup))
|
|
- return TRUE;
|
|
- do next += (next[1] << 8) + next[2]; while (*next == OP_ALT);
|
|
- ecode = next + 3;
|
|
- }
|
|
- break;
|
|
-
|
|
- case OP_BRAMINZERO:
|
|
- {
|
|
- const uschar *next = ecode+1;
|
|
- do next += (next[1] << 8) + next[2]; while (*next == OP_ALT);
|
|
- if (match(eptr, next+3, offset_top, md, ims, eptrb, match_isgroup))
|
|
- return TRUE;
|
|
- ecode++;
|
|
- }
|
|
- break;
|
|
-
|
|
- /* End of a group, repeated or non-repeating. If we are at the end of
|
|
- an assertion "group", stop matching and return TRUE, but record the
|
|
- current high water mark for use by positive assertions. Do this also
|
|
- for the "once" (not-backup up) groups. */
|
|
-
|
|
- case OP_KET:
|
|
- case OP_KETRMIN:
|
|
- case OP_KETRMAX:
|
|
- {
|
|
- const uschar *prev = ecode - (ecode[1] << 8) - ecode[2];
|
|
- const uschar *saved_eptr = eptrb->saved_eptr;
|
|
-
|
|
- eptrb = eptrb->prev; /* Back up the stack of bracket start pointers */
|
|
-
|
|
- if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT ||
|
|
- *prev == OP_ASSERTBACK || *prev == OP_ASSERTBACK_NOT ||
|
|
- *prev == OP_ONCE)
|
|
- {
|
|
- md->end_match_ptr = eptr; /* For ONCE */
|
|
- md->end_offset_top = offset_top;
|
|
- return TRUE;
|
|
- }
|
|
-
|
|
- /* In all other cases except a conditional group we have to check the
|
|
- group number back at the start and if necessary complete handling an
|
|
- extraction by setting the offsets and bumping the high water mark. */
|
|
-
|
|
- if (*prev != OP_COND)
|
|
- {
|
|
- int offset;
|
|
- int number = *prev - OP_BRA;
|
|
-
|
|
- /* For extended extraction brackets (large number), we have to fish out
|
|
- the number from a dummy opcode at the start. */
|
|
-
|
|
- if (number > EXTRACT_BASIC_MAX) number = (prev[4] << 8) | prev[5];
|
|
- offset = number << 1;
|
|
-
|
|
-#ifdef DEBUG
|
|
- PCRE_PRINTF("end bracket %d", number);
|
|
- PCRE_PRINTF("\n");
|
|
-#endif
|
|
-
|
|
- if (number > 0)
|
|
- {
|
|
- if (offset >= md->offset_max) md->offset_overflow = TRUE; else
|
|
- {
|
|
- md->offset_vector[offset] =
|
|
- md->offset_vector[md->offset_end - number];
|
|
- md->offset_vector[offset+1] = eptr - md->start_subject;
|
|
- if (offset_top <= offset) offset_top = offset + 2;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- /* Reset the value of the ims flags, in case they got changed during
|
|
- the group. */
|
|
-
|
|
- ims = original_ims;
|
|
- DPRINTF(("ims reset to %02lx\n", ims));
|
|
-
|
|
- /* For a non-repeating ket, just continue at this level. This also
|
|
- happens for a repeating ket if no characters were matched in the group.
|
|
- This is the forcible breaking of infinite loops as implemented in Perl
|
|
- 5.005. If there is an options reset, it will get obeyed in the normal
|
|
- course of events. */
|
|
-
|
|
- if (*ecode == OP_KET || eptr == saved_eptr)
|
|
- {
|
|
- ecode += 3;
|
|
- break;
|
|
- }
|
|
-
|
|
- /* The repeating kets try the rest of the pattern or restart from the
|
|
- preceding bracket, in the appropriate order. */
|
|
-
|
|
- if (*ecode == OP_KETRMIN)
|
|
- {
|
|
- if (match(eptr, ecode+3, offset_top, md, ims, eptrb, 0) ||
|
|
- match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup))
|
|
- return TRUE;
|
|
- }
|
|
- else /* OP_KETRMAX */
|
|
- {
|
|
- if (match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup) ||
|
|
- match(eptr, ecode+3, offset_top, md, ims, eptrb, 0)) return TRUE;
|
|
- }
|
|
- }
|
|
- return FALSE;
|
|
-
|
|
- /* Start of subject unless notbol, or after internal newline if multiline */
|
|
-
|
|
- case OP_CIRC:
|
|
- if (md->notbol && eptr == md->start_subject) return FALSE;
|
|
- if ((ims & PCRE_MULTILINE) != 0)
|
|
- {
|
|
- if (eptr != md->start_subject && eptr[-1] != NEWLINE) return FALSE;
|
|
- ecode++;
|
|
- break;
|
|
- }
|
|
- /* ... else fall through */
|
|
-
|
|
- /* Start of subject assertion */
|
|
-
|
|
- case OP_SOD:
|
|
- if (eptr != md->start_subject) return FALSE;
|
|
- ecode++;
|
|
- break;
|
|
-
|
|
- /* Assert before internal newline if multiline, or before a terminating
|
|
- newline unless endonly is set, else end of subject unless noteol is set. */
|
|
-
|
|
- case OP_DOLL:
|
|
- if ((ims & PCRE_MULTILINE) != 0)
|
|
- {
|
|
- if (eptr < md->end_subject) { if (*eptr != NEWLINE) return FALSE; }
|
|
- else { if (md->noteol) return FALSE; }
|
|
- ecode++;
|
|
- break;
|
|
- }
|
|
- else
|
|
- {
|
|
- if (md->noteol) return FALSE;
|
|
- if (!md->endonly)
|
|
- {
|
|
- if (eptr < md->end_subject - 1 ||
|
|
- (eptr == md->end_subject - 1 && *eptr != NEWLINE)) return FALSE;
|
|
-
|
|
- ecode++;
|
|
- break;
|
|
- }
|
|
- }
|
|
- /* ... else fall through */
|
|
-
|
|
- /* End of subject assertion (\z) */
|
|
-
|
|
- case OP_EOD:
|
|
- if (eptr < md->end_subject) return FALSE;
|
|
- ecode++;
|
|
- break;
|
|
-
|
|
- /* End of subject or ending \n assertion (\Z) */
|
|
-
|
|
- case OP_EODN:
|
|
- if (eptr < md->end_subject - 1 ||
|
|
- (eptr == md->end_subject - 1 && *eptr != NEWLINE)) return FALSE;
|
|
- ecode++;
|
|
- break;
|
|
-
|
|
- /* Word boundary assertions */
|
|
-
|
|
- case OP_NOT_WORD_BOUNDARY:
|
|
- case OP_WORD_BOUNDARY:
|
|
- {
|
|
- BOOL prev_is_word = (eptr != md->start_subject) &&
|
|
- ((md->ctypes[eptr[-1]] & ctype_word) != 0);
|
|
- BOOL cur_is_word = (eptr < md->end_subject) &&
|
|
- ((md->ctypes[*eptr] & ctype_word) != 0);
|
|
- if ((*ecode++ == OP_WORD_BOUNDARY)?
|
|
- cur_is_word == prev_is_word : cur_is_word != prev_is_word)
|
|
- return FALSE;
|
|
- }
|
|
- break;
|
|
-
|
|
- /* Match a single character type; inline for speed */
|
|
-
|
|
- case OP_ANY:
|
|
- if ((ims & PCRE_DOTALL) == 0 && eptr < md->end_subject && *eptr == NEWLINE)
|
|
- return FALSE;
|
|
- if (eptr++ >= md->end_subject) return FALSE;
|
|
-#ifdef SUPPORT_UTF8
|
|
- if (md->utf8)
|
|
- while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
|
|
-#endif
|
|
- ecode++;
|
|
- break;
|
|
-
|
|
- case OP_NOT_DIGIT:
|
|
- if (eptr >= md->end_subject ||
|
|
- (md->ctypes[*eptr++] & ctype_digit) != 0)
|
|
- return FALSE;
|
|
- ecode++;
|
|
- break;
|
|
-
|
|
- case OP_DIGIT:
|
|
- if (eptr >= md->end_subject ||
|
|
- (md->ctypes[*eptr++] & ctype_digit) == 0)
|
|
- return FALSE;
|
|
- ecode++;
|
|
- break;
|
|
-
|
|
- case OP_NOT_WHITESPACE:
|
|
- if (eptr >= md->end_subject ||
|
|
- (md->ctypes[*eptr++] & ctype_space) != 0)
|
|
- return FALSE;
|
|
- ecode++;
|
|
- break;
|
|
-
|
|
- case OP_WHITESPACE:
|
|
- if (eptr >= md->end_subject ||
|
|
- (md->ctypes[*eptr++] & ctype_space) == 0)
|
|
- return FALSE;
|
|
- ecode++;
|
|
- break;
|
|
-
|
|
- case OP_NOT_WORDCHAR:
|
|
- if (eptr >= md->end_subject ||
|
|
- (md->ctypes[*eptr++] & ctype_word) != 0)
|
|
- return FALSE;
|
|
- ecode++;
|
|
- break;
|
|
-
|
|
- case OP_WORDCHAR:
|
|
- if (eptr >= md->end_subject ||
|
|
- (md->ctypes[*eptr++] & ctype_word) == 0)
|
|
- return FALSE;
|
|
- ecode++;
|
|
- break;
|
|
-
|
|
- /* Match a back reference, possibly repeatedly. Look past the end of the
|
|
- item to see if there is repeat information following. The code is similar
|
|
- to that for character classes, but repeated for efficiency. Then obey
|
|
- similar code to character type repeats - written out again for speed.
|
|
- However, if the referenced string is the empty string, always treat
|
|
- it as matched, any number of times (otherwise there could be infinite
|
|
- loops). */
|
|
-
|
|
- case OP_REF:
|
|
- {
|
|
- int length;
|
|
- int offset = (ecode[1] << 9) | (ecode[2] << 1); /* Doubled ref number */
|
|
- ecode += 3; /* Advance past item */
|
|
-
|
|
- /* If the reference is unset, set the length to be longer than the amount
|
|
- of subject left; this ensures that every attempt at a match fails. We
|
|
- can't just fail here, because of the possibility of quantifiers with zero
|
|
- minima. */
|
|
-
|
|
- length = (offset >= offset_top || md->offset_vector[offset] < 0)?
|
|
- md->end_subject - eptr + 1 :
|
|
- md->offset_vector[offset+1] - md->offset_vector[offset];
|
|
-
|
|
- /* Set up for repetition, or handle the non-repeated case */
|
|
-
|
|
- switch (*ecode)
|
|
- {
|
|
- case OP_CRSTAR:
|
|
- case OP_CRMINSTAR:
|
|
- case OP_CRPLUS:
|
|
- case OP_CRMINPLUS:
|
|
- case OP_CRQUERY:
|
|
- case OP_CRMINQUERY:
|
|
- c = *ecode++ - OP_CRSTAR;
|
|
- minimize = (c & 1) != 0;
|
|
- min = rep_min[c]; /* Pick up values from tables; */
|
|
- max = rep_max[c]; /* zero for max => infinity */
|
|
- if (max == 0) max = INT_MAX;
|
|
- break;
|
|
-
|
|
- case OP_CRRANGE:
|
|
- case OP_CRMINRANGE:
|
|
- minimize = (*ecode == OP_CRMINRANGE);
|
|
- min = (ecode[1] << 8) + ecode[2];
|
|
- max = (ecode[3] << 8) + ecode[4];
|
|
- if (max == 0) max = INT_MAX;
|
|
- ecode += 5;
|
|
- break;
|
|
-
|
|
- default: /* No repeat follows */
|
|
- if (!match_ref(offset, eptr, length, md, ims)) return FALSE;
|
|
- eptr += length;
|
|
- continue; /* With the main loop */
|
|
- }
|
|
-
|
|
- /* If the length of the reference is zero, just continue with the
|
|
- main loop. */
|
|
-
|
|
- if (length == 0) continue;
|
|
-
|
|
- /* First, ensure the minimum number of matches are present. We get back
|
|
- the length of the reference string explicitly rather than passing the
|
|
- address of eptr, so that eptr can be a register variable. */
|
|
-
|
|
- for (i = 1; i <= min; i++)
|
|
- {
|
|
- if (!match_ref(offset, eptr, length, md, ims)) return FALSE;
|
|
- eptr += length;
|
|
- }
|
|
-
|
|
- /* If min = max, continue at the same level without recursion.
|
|
- They are not both allowed to be zero. */
|
|
-
|
|
- if (min == max) continue;
|
|
-
|
|
- /* If minimizing, keep trying and advancing the pointer */
|
|
-
|
|
- if (minimize)
|
|
- {
|
|
- for (i = min;; i++)
|
|
- {
|
|
- if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
|
|
- return TRUE;
|
|
- if (i >= max || !match_ref(offset, eptr, length, md, ims))
|
|
- return FALSE;
|
|
- eptr += length;
|
|
- }
|
|
- /* Control never gets here */
|
|
- }
|
|
-
|
|
- /* If maximizing, find the longest string and work backwards */
|
|
-
|
|
- else
|
|
- {
|
|
- const uschar *pp = eptr;
|
|
- for (i = min; i < max; i++)
|
|
- {
|
|
- if (!match_ref(offset, eptr, length, md, ims)) break;
|
|
- eptr += length;
|
|
- }
|
|
- while (eptr >= pp)
|
|
- {
|
|
- if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
|
|
- return TRUE;
|
|
- eptr -= length;
|
|
- }
|
|
- return FALSE;
|
|
- }
|
|
- }
|
|
- /* Control never gets here */
|
|
-
|
|
-
|
|
-
|
|
- /* Match a character class, possibly repeatedly. Look past the end of the
|
|
- item to see if there is repeat information following. Then obey similar
|
|
- code to character type repeats - written out again for speed. */
|
|
-
|
|
- case OP_CLASS:
|
|
- {
|
|
- const uschar *data = ecode + 1; /* Save for matching */
|
|
- ecode += 33; /* Advance past the item */
|
|
-
|
|
- switch (*ecode)
|
|
- {
|
|
- case OP_CRSTAR:
|
|
- case OP_CRMINSTAR:
|
|
- case OP_CRPLUS:
|
|
- case OP_CRMINPLUS:
|
|
- case OP_CRQUERY:
|
|
- case OP_CRMINQUERY:
|
|
- c = *ecode++ - OP_CRSTAR;
|
|
- minimize = (c & 1) != 0;
|
|
- min = rep_min[c]; /* Pick up values from tables; */
|
|
- max = rep_max[c]; /* zero for max => infinity */
|
|
- if (max == 0) max = INT_MAX;
|
|
- break;
|
|
-
|
|
- case OP_CRRANGE:
|
|
- case OP_CRMINRANGE:
|
|
- minimize = (*ecode == OP_CRMINRANGE);
|
|
- min = (ecode[1] << 8) + ecode[2];
|
|
- max = (ecode[3] << 8) + ecode[4];
|
|
- if (max == 0) max = INT_MAX;
|
|
- ecode += 5;
|
|
- break;
|
|
-
|
|
- default: /* No repeat follows */
|
|
- min = max = 1;
|
|
- break;
|
|
- }
|
|
-
|
|
- /* First, ensure the minimum number of matches are present. */
|
|
-
|
|
- for (i = 1; i <= min; i++)
|
|
- {
|
|
- if (eptr >= md->end_subject) return FALSE;
|
|
- GETCHARINC(c, eptr) /* Get character; increment eptr */
|
|
-
|
|
-#ifdef SUPPORT_UTF8
|
|
- /* We do not yet support class members > 255 */
|
|
- if (c > 255) return FALSE;
|
|
-#endif
|
|
-
|
|
- if ((data[c/8] & (1 << (c&7))) != 0) continue;
|
|
- return FALSE;
|
|
- }
|
|
-
|
|
- /* If max == min we can continue with the main loop without the
|
|
- need to recurse. */
|
|
-
|
|
- if (min == max) continue;
|
|
-
|
|
- /* If minimizing, keep testing the rest of the expression and advancing
|
|
- the pointer while it matches the class. */
|
|
-
|
|
- if (minimize)
|
|
- {
|
|
- for (i = min;; i++)
|
|
- {
|
|
- if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
|
|
- return TRUE;
|
|
- if (i >= max || eptr >= md->end_subject) return FALSE;
|
|
- GETCHARINC(c, eptr) /* Get character; increment eptr */
|
|
-
|
|
-#ifdef SUPPORT_UTF8
|
|
- /* We do not yet support class members > 255 */
|
|
- if (c > 255) return FALSE;
|
|
-#endif
|
|
- if ((data[c/8] & (1 << (c&7))) != 0) continue;
|
|
- return FALSE;
|
|
- }
|
|
- /* Control never gets here */
|
|
- }
|
|
-
|
|
- /* If maximizing, find the longest possible run, then work backwards. */
|
|
-
|
|
- else
|
|
- {
|
|
- const uschar *pp = eptr;
|
|
- int len = 1;
|
|
- for (i = min; i < max; i++)
|
|
- {
|
|
- if (eptr >= md->end_subject) break;
|
|
- GETCHARLEN(c, eptr, len) /* Get character, set length if UTF-8 */
|
|
-
|
|
-#ifdef SUPPORT_UTF8
|
|
- /* We do not yet support class members > 255 */
|
|
- if (c > 255) break;
|
|
-#endif
|
|
- if ((data[c/8] & (1 << (c&7))) == 0) break;
|
|
- eptr += len;
|
|
- }
|
|
-
|
|
- while (eptr >= pp)
|
|
- {
|
|
- if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
|
|
- return TRUE;
|
|
-
|
|
-#ifdef SUPPORT_UTF8
|
|
- BACKCHAR(eptr)
|
|
-#endif
|
|
- }
|
|
- return FALSE;
|
|
- }
|
|
- }
|
|
- /* Control never gets here */
|
|
-
|
|
- /* Match a run of characters */
|
|
-
|
|
- case OP_CHARS:
|
|
- {
|
|
- register int length = ecode[1];
|
|
- ecode += 2;
|
|
-
|
|
-#ifdef DEBUG /* Sigh. Some compilers never learn. */
|
|
- if (eptr >= md->end_subject)
|
|
- PCRE_PRINTF("matching subject <null> against pattern ");
|
|
- else
|
|
- {
|
|
- PCRE_PRINTF("matching subject ");
|
|
- pchars(eptr, length, TRUE, md);
|
|
- PCRE_PRINTF(" against pattern ");
|
|
- }
|
|
- pchars(ecode, length, FALSE, md);
|
|
- PCRE_PRINTF("\n");
|
|
-#endif
|
|
-
|
|
- if (length > md->end_subject - eptr) return FALSE;
|
|
- if ((ims & PCRE_CASELESS) != 0)
|
|
- {
|
|
- while (length-- > 0)
|
|
- if (md->lcc[*ecode++] != md->lcc[*eptr++])
|
|
- return FALSE;
|
|
- }
|
|
- else
|
|
- {
|
|
- while (length-- > 0) if (*ecode++ != *eptr++) return FALSE;
|
|
- }
|
|
- }
|
|
- break;
|
|
-
|
|
- /* Match a single character repeatedly; different opcodes share code. */
|
|
-
|
|
- case OP_EXACT:
|
|
- min = max = (ecode[1] << 8) + ecode[2];
|
|
- ecode += 3;
|
|
- goto REPEATCHAR;
|
|
-
|
|
- case OP_UPTO:
|
|
- case OP_MINUPTO:
|
|
- min = 0;
|
|
- max = (ecode[1] << 8) + ecode[2];
|
|
- minimize = *ecode == OP_MINUPTO;
|
|
- ecode += 3;
|
|
- goto REPEATCHAR;
|
|
-
|
|
- case OP_STAR:
|
|
- case OP_MINSTAR:
|
|
- case OP_PLUS:
|
|
- case OP_MINPLUS:
|
|
- case OP_QUERY:
|
|
- case OP_MINQUERY:
|
|
- c = *ecode++ - OP_STAR;
|
|
- minimize = (c & 1) != 0;
|
|
- min = rep_min[c]; /* Pick up values from tables; */
|
|
- max = rep_max[c]; /* zero for max => infinity */
|
|
- if (max == 0) max = INT_MAX;
|
|
-
|
|
- /* Common code for all repeated single-character matches. We can give
|
|
- up quickly if there are fewer than the minimum number of characters left in
|
|
- the subject. */
|
|
-
|
|
- REPEATCHAR:
|
|
- if (min > md->end_subject - eptr) return FALSE;
|
|
- c = *ecode++;
|
|
-
|
|
- /* The code is duplicated for the caseless and caseful cases, for speed,
|
|
- since matching characters is likely to be quite common. First, ensure the
|
|
- minimum number of matches are present. If min = max, continue at the same
|
|
- level without recursing. Otherwise, if minimizing, keep trying the rest of
|
|
- the expression and advancing one matching character if failing, up to the
|
|
- maximum. Alternatively, if maximizing, find the maximum number of
|
|
- characters and work backwards. */
|
|
-
|
|
- DPRINTF(("matching %c{%d,%d} against subject %.*s\n", c, min, max,
|
|
- max, eptr));
|
|
-
|
|
- if ((ims & PCRE_CASELESS) != 0)
|
|
- {
|
|
- c = md->lcc[c];
|
|
- for (i = 1; i <= min; i++)
|
|
- if (c != md->lcc[*eptr++]) return FALSE;
|
|
- if (min == max) continue;
|
|
- if (minimize)
|
|
- {
|
|
- for (i = min;; i++)
|
|
- {
|
|
- if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
|
|
- return TRUE;
|
|
- if (i >= max || eptr >= md->end_subject ||
|
|
- c != md->lcc[*eptr++])
|
|
- return FALSE;
|
|
- }
|
|
- /* Control never gets here */
|
|
- }
|
|
- else
|
|
- {
|
|
- const uschar *pp = eptr;
|
|
- for (i = min; i < max; i++)
|
|
- {
|
|
- if (eptr >= md->end_subject || c != md->lcc[*eptr]) break;
|
|
- eptr++;
|
|
- }
|
|
- while (eptr >= pp)
|
|
- if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
|
|
- return TRUE;
|
|
- return FALSE;
|
|
- }
|
|
- /* Control never gets here */
|
|
- }
|
|
-
|
|
- /* Caseful comparisons */
|
|
-
|
|
- else
|
|
- {
|
|
- for (i = 1; i <= min; i++) if (c != *eptr++) return FALSE;
|
|
- if (min == max) continue;
|
|
- if (minimize)
|
|
- {
|
|
- for (i = min;; i++)
|
|
- {
|
|
- if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
|
|
- return TRUE;
|
|
- if (i >= max || eptr >= md->end_subject || c != *eptr++) return FALSE;
|
|
- }
|
|
- /* Control never gets here */
|
|
- }
|
|
- else
|
|
- {
|
|
- const uschar *pp = eptr;
|
|
- for (i = min; i < max; i++)
|
|
- {
|
|
- if (eptr >= md->end_subject || c != *eptr) break;
|
|
- eptr++;
|
|
- }
|
|
- while (eptr >= pp)
|
|
- if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
|
|
- return TRUE;
|
|
- return FALSE;
|
|
- }
|
|
- }
|
|
- /* Control never gets here */
|
|
-
|
|
- /* Match a negated single character */
|
|
-
|
|
- case OP_NOT:
|
|
- if (eptr >= md->end_subject) return FALSE;
|
|
- ecode++;
|
|
- if ((ims & PCRE_CASELESS) != 0)
|
|
- {
|
|
- if (md->lcc[*ecode++] == md->lcc[*eptr++]) return FALSE;
|
|
- }
|
|
- else
|
|
- {
|
|
- if (*ecode++ == *eptr++) return FALSE;
|
|
- }
|
|
- break;
|
|
-
|
|
- /* Match a negated single character repeatedly. This is almost a repeat of
|
|
- the code for a repeated single character, but I haven't found a nice way of
|
|
- commoning these up that doesn't require a test of the positive/negative
|
|
- option for each character match. Maybe that wouldn't add very much to the
|
|
- time taken, but character matching *is* what this is all about... */
|
|
-
|
|
- case OP_NOTEXACT:
|
|
- min = max = (ecode[1] << 8) + ecode[2];
|
|
- ecode += 3;
|
|
- goto REPEATNOTCHAR;
|
|
-
|
|
- case OP_NOTUPTO:
|
|
- case OP_NOTMINUPTO:
|
|
- min = 0;
|
|
- max = (ecode[1] << 8) + ecode[2];
|
|
- minimize = *ecode == OP_NOTMINUPTO;
|
|
- ecode += 3;
|
|
- goto REPEATNOTCHAR;
|
|
-
|
|
- case OP_NOTSTAR:
|
|
- case OP_NOTMINSTAR:
|
|
- case OP_NOTPLUS:
|
|
- case OP_NOTMINPLUS:
|
|
- case OP_NOTQUERY:
|
|
- case OP_NOTMINQUERY:
|
|
- c = *ecode++ - OP_NOTSTAR;
|
|
- minimize = (c & 1) != 0;
|
|
- min = rep_min[c]; /* Pick up values from tables; */
|
|
- max = rep_max[c]; /* zero for max => infinity */
|
|
- if (max == 0) max = INT_MAX;
|
|
-
|
|
- /* Common code for all repeated single-character matches. We can give
|
|
- up quickly if there are fewer than the minimum number of characters left in
|
|
- the subject. */
|
|
-
|
|
- REPEATNOTCHAR:
|
|
- if (min > md->end_subject - eptr) return FALSE;
|
|
- c = *ecode++;
|
|
-
|
|
- /* The code is duplicated for the caseless and caseful cases, for speed,
|
|
- since matching characters is likely to be quite common. First, ensure the
|
|
- minimum number of matches are present. If min = max, continue at the same
|
|
- level without recursing. Otherwise, if minimizing, keep trying the rest of
|
|
- the expression and advancing one matching character if failing, up to the
|
|
- maximum. Alternatively, if maximizing, find the maximum number of
|
|
- characters and work backwards. */
|
|
-
|
|
- DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", c, min, max,
|
|
- max, eptr));
|
|
-
|
|
- if ((ims & PCRE_CASELESS) != 0)
|
|
- {
|
|
- c = md->lcc[c];
|
|
- for (i = 1; i <= min; i++)
|
|
- if (c == md->lcc[*eptr++]) return FALSE;
|
|
- if (min == max) continue;
|
|
- if (minimize)
|
|
- {
|
|
- for (i = min;; i++)
|
|
- {
|
|
- if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
|
|
- return TRUE;
|
|
- if (i >= max || eptr >= md->end_subject ||
|
|
- c == md->lcc[*eptr++])
|
|
- return FALSE;
|
|
- }
|
|
- /* Control never gets here */
|
|
- }
|
|
- else
|
|
- {
|
|
- const uschar *pp = eptr;
|
|
- for (i = min; i < max; i++)
|
|
- {
|
|
- if (eptr >= md->end_subject || c == md->lcc[*eptr]) break;
|
|
- eptr++;
|
|
- }
|
|
- while (eptr >= pp)
|
|
- if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
|
|
- return TRUE;
|
|
- return FALSE;
|
|
- }
|
|
- /* Control never gets here */
|
|
- }
|
|
-
|
|
- /* Caseful comparisons */
|
|
-
|
|
- else
|
|
- {
|
|
- for (i = 1; i <= min; i++) if (c == *eptr++) return FALSE;
|
|
- if (min == max) continue;
|
|
- if (minimize)
|
|
- {
|
|
- for (i = min;; i++)
|
|
- {
|
|
- if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
|
|
- return TRUE;
|
|
- if (i >= max || eptr >= md->end_subject || c == *eptr++) return FALSE;
|
|
- }
|
|
- /* Control never gets here */
|
|
- }
|
|
- else
|
|
- {
|
|
- const uschar *pp = eptr;
|
|
- for (i = min; i < max; i++)
|
|
- {
|
|
- if (eptr >= md->end_subject || c == *eptr) break;
|
|
- eptr++;
|
|
- }
|
|
- while (eptr >= pp)
|
|
- if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
|
|
- return TRUE;
|
|
- return FALSE;
|
|
- }
|
|
- }
|
|
- /* Control never gets here */
|
|
-
|
|
- /* Match a single character type repeatedly; several different opcodes
|
|
- share code. This is very similar to the code for single characters, but we
|
|
- repeat it in the interests of efficiency. */
|
|
-
|
|
- case OP_TYPEEXACT:
|
|
- min = max = (ecode[1] << 8) + ecode[2];
|
|
- minimize = TRUE;
|
|
- ecode += 3;
|
|
- goto REPEATTYPE;
|
|
-
|
|
- case OP_TYPEUPTO:
|
|
- case OP_TYPEMINUPTO:
|
|
- min = 0;
|
|
- max = (ecode[1] << 8) + ecode[2];
|
|
- minimize = *ecode == OP_TYPEMINUPTO;
|
|
- ecode += 3;
|
|
- goto REPEATTYPE;
|
|
-
|
|
- case OP_TYPESTAR:
|
|
- case OP_TYPEMINSTAR:
|
|
- case OP_TYPEPLUS:
|
|
- case OP_TYPEMINPLUS:
|
|
- case OP_TYPEQUERY:
|
|
- case OP_TYPEMINQUERY:
|
|
- c = *ecode++ - OP_TYPESTAR;
|
|
- minimize = (c & 1) != 0;
|
|
- min = rep_min[c]; /* Pick up values from tables; */
|
|
- max = rep_max[c]; /* zero for max => infinity */
|
|
- if (max == 0) max = INT_MAX;
|
|
-
|
|
- /* Common code for all repeated single character type matches */
|
|
-
|
|
- REPEATTYPE:
|
|
- ctype = *ecode++; /* Code for the character type */
|
|
-
|
|
- /* First, ensure the minimum number of matches are present. Use inline
|
|
- code for maximizing the speed, and do the type test once at the start
|
|
- (i.e. keep it out of the loop). Also we can test that there are at least
|
|
- the minimum number of bytes before we start, except when doing '.' in
|
|
- UTF8 mode. Leave the test in in all cases; in the special case we have
|
|
- to test after each character. */
|
|
-
|
|
- if (min > md->end_subject - eptr) return FALSE;
|
|
- if (min > 0) switch(ctype)
|
|
- {
|
|
- case OP_ANY:
|
|
-#ifdef SUPPORT_UTF8
|
|
- if (md->utf8)
|
|
- {
|
|
- for (i = 1; i <= min; i++)
|
|
- {
|
|
- if (eptr >= md->end_subject ||
|
|
- (*eptr++ == NEWLINE && (ims & PCRE_DOTALL) == 0))
|
|
- return FALSE;
|
|
- while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
|
|
- }
|
|
- break;
|
|
- }
|
|
-#endif
|
|
- /* Non-UTF8 can be faster */
|
|
- if ((ims & PCRE_DOTALL) == 0)
|
|
- { for (i = 1; i <= min; i++) if (*eptr++ == NEWLINE) return FALSE; }
|
|
- else eptr += min;
|
|
- break;
|
|
-
|
|
- case OP_NOT_DIGIT:
|
|
- for (i = 1; i <= min; i++)
|
|
- if ((md->ctypes[*eptr++] & ctype_digit) != 0) return FALSE;
|
|
- break;
|
|
-
|
|
- case OP_DIGIT:
|
|
- for (i = 1; i <= min; i++)
|
|
- if ((md->ctypes[*eptr++] & ctype_digit) == 0) return FALSE;
|
|
- break;
|
|
-
|
|
- case OP_NOT_WHITESPACE:
|
|
- for (i = 1; i <= min; i++)
|
|
- if ((md->ctypes[*eptr++] & ctype_space) != 0) return FALSE;
|
|
- break;
|
|
-
|
|
- case OP_WHITESPACE:
|
|
- for (i = 1; i <= min; i++)
|
|
- if ((md->ctypes[*eptr++] & ctype_space) == 0) return FALSE;
|
|
- break;
|
|
-
|
|
- case OP_NOT_WORDCHAR:
|
|
- for (i = 1; i <= min; i++)
|
|
- if ((md->ctypes[*eptr++] & ctype_word) != 0)
|
|
- return FALSE;
|
|
- break;
|
|
-
|
|
- case OP_WORDCHAR:
|
|
- for (i = 1; i <= min; i++)
|
|
- if ((md->ctypes[*eptr++] & ctype_word) == 0)
|
|
- return FALSE;
|
|
- break;
|
|
- }
|
|
-
|
|
- /* If min = max, continue at the same level without recursing */
|
|
-
|
|
- if (min == max) continue;
|
|
-
|
|
- /* If minimizing, we have to test the rest of the pattern before each
|
|
- subsequent match. */
|
|
-
|
|
- if (minimize)
|
|
- {
|
|
- for (i = min;; i++)
|
|
- {
|
|
- if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) return TRUE;
|
|
- if (i >= max || eptr >= md->end_subject) return FALSE;
|
|
-
|
|
- c = *eptr++;
|
|
- switch(ctype)
|
|
- {
|
|
- case OP_ANY:
|
|
- if ((ims & PCRE_DOTALL) == 0 && c == NEWLINE) return FALSE;
|
|
-#ifdef SUPPORT_UTF8
|
|
- if (md->utf8)
|
|
- while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
|
|
-#endif
|
|
- break;
|
|
-
|
|
- case OP_NOT_DIGIT:
|
|
- if ((md->ctypes[c] & ctype_digit) != 0) return FALSE;
|
|
- break;
|
|
-
|
|
- case OP_DIGIT:
|
|
- if ((md->ctypes[c] & ctype_digit) == 0) return FALSE;
|
|
- break;
|
|
-
|
|
- case OP_NOT_WHITESPACE:
|
|
- if ((md->ctypes[c] & ctype_space) != 0) return FALSE;
|
|
- break;
|
|
-
|
|
- case OP_WHITESPACE:
|
|
- if ((md->ctypes[c] & ctype_space) == 0) return FALSE;
|
|
- break;
|
|
-
|
|
- case OP_NOT_WORDCHAR:
|
|
- if ((md->ctypes[c] & ctype_word) != 0) return FALSE;
|
|
- break;
|
|
-
|
|
- case OP_WORDCHAR:
|
|
- if ((md->ctypes[c] & ctype_word) == 0) return FALSE;
|
|
- break;
|
|
- }
|
|
- }
|
|
- /* Control never gets here */
|
|
- }
|
|
-
|
|
- /* If maximizing it is worth using inline code for speed, doing the type
|
|
- test once at the start (i.e. keep it out of the loop). */
|
|
-
|
|
- else
|
|
- {
|
|
- const uschar *pp = eptr;
|
|
- switch(ctype)
|
|
- {
|
|
- case OP_ANY:
|
|
-
|
|
- /* Special code is required for UTF8, but when the maximum is unlimited
|
|
- we don't need it. */
|
|
-
|
|
-#ifdef SUPPORT_UTF8
|
|
- if (md->utf8 && max < INT_MAX)
|
|
- {
|
|
- if ((ims & PCRE_DOTALL) == 0)
|
|
- {
|
|
- for (i = min; i < max; i++)
|
|
- {
|
|
- if (eptr >= md->end_subject || *eptr++ == NEWLINE) break;
|
|
- while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
|
|
- }
|
|
- }
|
|
- else
|
|
- {
|
|
- for (i = min; i < max; i++)
|
|
- {
|
|
- eptr++;
|
|
- while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
|
|
- }
|
|
- }
|
|
- break;
|
|
- }
|
|
-#endif
|
|
- /* Non-UTF8 can be faster */
|
|
- if ((ims & PCRE_DOTALL) == 0)
|
|
- {
|
|
- for (i = min; i < max; i++)
|
|
- {
|
|
- if (eptr >= md->end_subject || *eptr == NEWLINE) break;
|
|
- eptr++;
|
|
- }
|
|
- }
|
|
- else
|
|
- {
|
|
- c = max - min;
|
|
- if (c > md->end_subject - eptr) c = md->end_subject - eptr;
|
|
- eptr += c;
|
|
- }
|
|
- break;
|
|
-
|
|
- case OP_NOT_DIGIT:
|
|
- for (i = min; i < max; i++)
|
|
- {
|
|
- if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) != 0)
|
|
- break;
|
|
- eptr++;
|
|
- }
|
|
- break;
|
|
-
|
|
- case OP_DIGIT:
|
|
- for (i = min; i < max; i++)
|
|
- {
|
|
- if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) == 0)
|
|
- break;
|
|
- eptr++;
|
|
- }
|
|
- break;
|
|
-
|
|
- case OP_NOT_WHITESPACE:
|
|
- for (i = min; i < max; i++)
|
|
- {
|
|
- if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) != 0)
|
|
- break;
|
|
- eptr++;
|
|
- }
|
|
- break;
|
|
-
|
|
- case OP_WHITESPACE:
|
|
- for (i = min; i < max; i++)
|
|
- {
|
|
- if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) == 0)
|
|
- break;
|
|
- eptr++;
|
|
- }
|
|
- break;
|
|
-
|
|
- case OP_NOT_WORDCHAR:
|
|
- for (i = min; i < max; i++)
|
|
- {
|
|
- if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) != 0)
|
|
- break;
|
|
- eptr++;
|
|
- }
|
|
- break;
|
|
-
|
|
- case OP_WORDCHAR:
|
|
- for (i = min; i < max; i++)
|
|
- {
|
|
- if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) == 0)
|
|
- break;
|
|
- eptr++;
|
|
- }
|
|
- break;
|
|
- }
|
|
-
|
|
- while (eptr >= pp)
|
|
- {
|
|
- if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
|
|
- return TRUE;
|
|
-#ifdef SUPPORT_UTF8
|
|
- if (md->utf8)
|
|
- while (eptr > pp && (*eptr & 0xc0) == 0x80) eptr--;
|
|
-#endif
|
|
- }
|
|
- return FALSE;
|
|
- }
|
|
- /* Control never gets here */
|
|
-
|
|
- /* There's been some horrible disaster. */
|
|
-
|
|
- default:
|
|
- DPRINTF(("Unknown opcode %d\n", *ecode));
|
|
- md->errorcode = PCRE_ERROR_UNKNOWN_NODE;
|
|
- return FALSE;
|
|
- }
|
|
-
|
|
- /* Do not stick any code in here without much thought; it is assumed
|
|
- that "continue" in the code above comes out to here to repeat the main
|
|
- loop. */
|
|
-
|
|
- } /* End of main loop */
|
|
-/* Control never reaches here */
|
|
-}
|
|
-
|
|
-
|
|
-/*************************************************
|
|
-* Execute a Regular Expression *
|
|
-*************************************************/
|
|
-
|
|
-/* This function applies a compiled re to a subject string and picks out
|
|
-portions of the string if it matches. Two elements in the vector are set for
|
|
-each substring: the offsets to the start and end of the substring.
|
|
-
|
|
-Arguments:
|
|
- external_re points to the compiled expression
|
|
- external_extra points to "hints" from pcre_study() or is NULL
|
|
- subject points to the subject string
|
|
- length length of subject string (may contain binary zeros)
|
|
- start_offset where to start in the subject string
|
|
- options option bits
|
|
- offsets points to a vector of ints to be filled in with offsets
|
|
- offsetcount the number of elements in the vector
|
|
-
|
|
-Returns: > 0 => success; value is the number of elements filled in
|
|
- = 0 => success, but offsets is not big enough
|
|
- -1 => failed to match
|
|
- < -1 => some kind of unexpected problem
|
|
-*/
|
|
-
|
|
-int
|
|
-pcre_exec(const pcre *external_re, const pcre_extra *external_extra,
|
|
- const char *subject, int length, int start_offset, int options, int *offsets,
|
|
- int offsetcount)
|
|
-{
|
|
-int resetcount, ocount;
|
|
-int first_char = -1;
|
|
-int req_char = -1;
|
|
-int req_char2 = -1;
|
|
-unsigned long int ims = 0;
|
|
-match_data match_block;
|
|
-const uschar *start_bits = NULL;
|
|
-const uschar *start_match = (const uschar *)subject + start_offset;
|
|
-const uschar *end_subject;
|
|
-const uschar *req_char_ptr = start_match - 1;
|
|
-const real_pcre *re = (const real_pcre *)external_re;
|
|
-const real_pcre_extra *extra = (const real_pcre_extra *)external_extra;
|
|
-BOOL using_temporary_offsets = FALSE;
|
|
-BOOL anchored;
|
|
-BOOL startline;
|
|
-
|
|
-if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION;
|
|
-
|
|
-if (re == NULL || subject == NULL ||
|
|
- (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL;
|
|
-if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC;
|
|
-
|
|
-anchored = ((re->options | options) & PCRE_ANCHORED) != 0;
|
|
-startline = (re->options & PCRE_STARTLINE) != 0;
|
|
-
|
|
-match_block.start_pattern = re->code;
|
|
-match_block.start_subject = (const uschar *)subject;
|
|
-match_block.end_subject = match_block.start_subject + length;
|
|
-end_subject = match_block.end_subject;
|
|
-
|
|
-match_block.endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0;
|
|
-match_block.utf8 = (re->options & PCRE_UTF8) != 0;
|
|
-
|
|
-match_block.notbol = (options & PCRE_NOTBOL) != 0;
|
|
-match_block.noteol = (options & PCRE_NOTEOL) != 0;
|
|
-match_block.notempty = (options & PCRE_NOTEMPTY) != 0;
|
|
-
|
|
-match_block.errorcode = PCRE_ERROR_NOMATCH; /* Default error */
|
|
-
|
|
-match_block.lcc = re->tables + lcc_offset;
|
|
-match_block.ctypes = re->tables + ctypes_offset;
|
|
-
|
|
-/* The ims options can vary during the matching as a result of the presence
|
|
-of (?ims) items in the pattern. They are kept in a local variable so that
|
|
-restoring at the exit of a group is easy. */
|
|
-
|
|
-ims = re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL);
|
|
-
|
|
-/* If the expression has got more back references than the offsets supplied can
|
|
-hold, we get a temporary bit of working store to use during the matching.
|
|
-Otherwise, we can use the vector supplied, rounding down its size to a multiple
|
|
-of 3. */
|
|
-
|
|
-ocount = offsetcount - (offsetcount % 3);
|
|
-
|
|
-if (re->top_backref > 0 && re->top_backref >= ocount/3)
|
|
- {
|
|
- ocount = re->top_backref * 3 + 3;
|
|
- match_block.offset_vector = (int *)(pcre_malloc)(ocount * sizeof(int));
|
|
- if (match_block.offset_vector == NULL) return PCRE_ERROR_NOMEMORY;
|
|
- using_temporary_offsets = TRUE;
|
|
- DPRINTF(("Got memory to hold back references\n"));
|
|
- }
|
|
-else match_block.offset_vector = offsets;
|
|
-
|
|
-match_block.offset_end = ocount;
|
|
-match_block.offset_max = (2*ocount)/3;
|
|
-match_block.offset_overflow = FALSE;
|
|
-
|
|
-/* Compute the minimum number of offsets that we need to reset each time. Doing
|
|
-this makes a huge difference to execution time when there aren't many brackets
|
|
-in the pattern. */
|
|
-
|
|
-resetcount = 2 + re->top_bracket * 2;
|
|
-if (resetcount > offsetcount) resetcount = ocount;
|
|
-
|
|
-/* Reset the working variable associated with each extraction. These should
|
|
-never be used unless previously set, but they get saved and restored, and so we
|
|
-initialize them to avoid reading uninitialized locations. */
|
|
-
|
|
-if (match_block.offset_vector != NULL)
|
|
- {
|
|
- register int *iptr = match_block.offset_vector + ocount;
|
|
- register int *iend = iptr - resetcount/2 + 1;
|
|
- while (--iptr >= iend) *iptr = -1;
|
|
- }
|
|
-
|
|
-/* Set up the first character to match, if available. The first_char value is
|
|
-never set for an anchored regular expression, but the anchoring may be forced
|
|
-at run time, so we have to test for anchoring. The first char may be unset for
|
|
-an unanchored pattern, of course. If there's no first char and the pattern was
|
|
-studied, there may be a bitmap of possible first characters. */
|
|
-
|
|
-if (!anchored)
|
|
- {
|
|
- if ((re->options & PCRE_FIRSTSET) != 0)
|
|
- {
|
|
- first_char = re->first_char;
|
|
- if ((ims & PCRE_CASELESS) != 0) first_char = match_block.lcc[first_char];
|
|
- }
|
|
- else
|
|
- if (!startline && extra != NULL &&
|
|
- (extra->options & PCRE_STUDY_MAPPED) != 0)
|
|
- start_bits = extra->start_bits;
|
|
- }
|
|
-
|
|
-/* For anchored or unanchored matches, there may be a "last known required
|
|
-character" set. If the PCRE_CASELESS is set, implying that the match starts
|
|
-caselessly, or if there are any changes of this flag within the regex, set up
|
|
-both cases of the character. Otherwise set the two values the same, which will
|
|
-avoid duplicate testing (which takes significant time). This covers the vast
|
|
-majority of cases. It will be suboptimal when the case flag changes in a regex
|
|
-and the required character in fact is caseful. */
|
|
-
|
|
-if ((re->options & PCRE_REQCHSET) != 0)
|
|
- {
|
|
- req_char = re->req_char;
|
|
- req_char2 = ((re->options & (PCRE_CASELESS | PCRE_ICHANGED)) != 0)?
|
|
- (re->tables + fcc_offset)[req_char] : req_char;
|
|
- }
|
|
-
|
|
-/* Loop for handling unanchored repeated matching attempts; for anchored regexs
|
|
-the loop runs just once. */
|
|
-
|
|
-do
|
|
- {
|
|
- int rc;
|
|
- register int *iptr = match_block.offset_vector;
|
|
- register int *iend = iptr + resetcount;
|
|
-
|
|
- /* Reset the maximum number of extractions we might see. */
|
|
-
|
|
- while (iptr < iend) *iptr++ = -1;
|
|
-
|
|
- /* Advance to a unique first char if possible */
|
|
-
|
|
- if (first_char >= 0)
|
|
- {
|
|
- if ((ims & PCRE_CASELESS) != 0)
|
|
- while (start_match < end_subject &&
|
|
- match_block.lcc[*start_match] != first_char)
|
|
- start_match++;
|
|
- else
|
|
- while (start_match < end_subject && *start_match != first_char)
|
|
- start_match++;
|
|
- }
|
|
-
|
|
- /* Or to just after \n for a multiline match if possible */
|
|
-
|
|
- else if (startline)
|
|
- {
|
|
- if (start_match > match_block.start_subject + start_offset)
|
|
- {
|
|
- while (start_match < end_subject && start_match[-1] != NEWLINE)
|
|
- start_match++;
|
|
- }
|
|
- }
|
|
-
|
|
- /* Or to a non-unique first char after study */
|
|
-
|
|
- else if (start_bits != NULL)
|
|
- {
|
|
- while (start_match < end_subject)
|
|
- {
|
|
- register int c = *start_match;
|
|
- if ((start_bits[c/8] & (1 << (c&7))) == 0) start_match++; else break;
|
|
- }
|
|
- }
|
|
-
|
|
-#ifdef DEBUG /* Sigh. Some compilers never learn. */
|
|
- PCRE_PRINTF(">>>> Match against: ");
|
|
- pchars(start_match, end_subject - start_match, TRUE, &match_block);
|
|
- PCRE_PRINTF("\n");
|
|
-#endif
|
|
-
|
|
- /* If req_char is set, we know that that character must appear in the subject
|
|
- for the match to succeed. If the first character is set, req_char must be
|
|
- later in the subject; otherwise the test starts at the match point. This
|
|
- optimization can save a huge amount of backtracking in patterns with nested
|
|
- unlimited repeats that aren't going to match. We don't know what the state of
|
|
- case matching may be when this character is hit, so test for it in both its
|
|
- cases if necessary. However, the different cased versions will not be set up
|
|
- unless PCRE_CASELESS was given or the casing state changes within the regex.
|
|
- Writing separate code makes it go faster, as does using an autoincrement and
|
|
- backing off on a match. */
|
|
-
|
|
- if (req_char >= 0)
|
|
- {
|
|
- register const uschar *p = start_match + ((first_char >= 0)? 1 : 0);
|
|
-
|
|
- /* We don't need to repeat the search if we haven't yet reached the
|
|
- place we found it at last time. */
|
|
-
|
|
- if (p > req_char_ptr)
|
|
- {
|
|
- /* Do a single test if no case difference is set up */
|
|
-
|
|
- if (req_char == req_char2)
|
|
- {
|
|
- while (p < end_subject)
|
|
- {
|
|
- if (*p++ == req_char) { p--; break; }
|
|
- }
|
|
- }
|
|
-
|
|
- /* Otherwise test for either case */
|
|
-
|
|
- else
|
|
- {
|
|
- while (p < end_subject)
|
|
- {
|
|
- register int pp = *p++;
|
|
- if (pp == req_char || pp == req_char2) { p--; break; }
|
|
- }
|
|
- }
|
|
-
|
|
- /* If we can't find the required character, break the matching loop */
|
|
-
|
|
- if (p >= end_subject) break;
|
|
-
|
|
- /* If we have found the required character, save the point where we
|
|
- found it, so that we don't search again next time round the loop if
|
|
- the start hasn't passed this character yet. */
|
|
-
|
|
- req_char_ptr = p;
|
|
- }
|
|
- }
|
|
-
|
|
- /* When a match occurs, substrings will be set for all internal extractions;
|
|
- we just need to set up the whole thing as substring 0 before returning. If
|
|
- there were too many extractions, set the return code to zero. In the case
|
|
- where we had to get some local store to hold offsets for backreferences, copy
|
|
- those back references that we can. In this case there need not be overflow
|
|
- if certain parts of the pattern were not used. */
|
|
-
|
|
- match_block.start_match = start_match;
|
|
- if (!match(start_match, re->code, 2, &match_block, ims, NULL, match_isgroup))
|
|
- continue;
|
|
-
|
|
- /* Copy the offset information from temporary store if necessary */
|
|
-
|
|
- if (using_temporary_offsets)
|
|
- {
|
|
- if (offsetcount >= 4)
|
|
- {
|
|
- memcpy(offsets + 2, match_block.offset_vector + 2,
|
|
- (offsetcount - 2) * sizeof(int));
|
|
- DPRINTF(("Copied offsets from temporary memory\n"));
|
|
- }
|
|
- if (match_block.end_offset_top > offsetcount)
|
|
- match_block.offset_overflow = TRUE;
|
|
-
|
|
- DPRINTF(("Freeing temporary memory\n"));
|
|
- (pcre_free)(match_block.offset_vector);
|
|
- }
|
|
-
|
|
- rc = match_block.offset_overflow? 0 : match_block.end_offset_top/2;
|
|
-
|
|
- if (offsetcount < 2) rc = 0; else
|
|
- {
|
|
- offsets[0] = start_match - match_block.start_subject;
|
|
- offsets[1] = match_block.end_match_ptr - match_block.start_subject;
|
|
- }
|
|
-
|
|
- DPRINTF((">>>> returning %d\n", rc));
|
|
- return rc;
|
|
- }
|
|
-
|
|
-/* This "while" is the end of the "do" above */
|
|
-
|
|
-while (!anchored &&
|
|
- match_block.errorcode == PCRE_ERROR_NOMATCH &&
|
|
- start_match++ < end_subject);
|
|
-
|
|
-if (using_temporary_offsets)
|
|
- {
|
|
- DPRINTF(("Freeing temporary memory\n"));
|
|
- (pcre_free)(match_block.offset_vector);
|
|
- }
|
|
-
|
|
-DPRINTF((">>>> returning %d\n", match_block.errorcode));
|
|
-
|
|
-return match_block.errorcode;
|
|
-}
|
|
-
|
|
-/* End of pcre.c */
|
|
Index: linux-2.6/security/apparmor/match/pcre_exec.h
|
|
===================================================================
|
|
--- linux-2.6.orig/security/apparmor/match/pcre_exec.h
|
|
+++ /dev/null
|
|
@@ -1,308 +0,0 @@
|
|
-/*
|
|
- * This is a modified header file containing the definitions from
|
|
- * pcre.h and internal.h required to support pcre_exec()
|
|
- */
|
|
-
|
|
-
|
|
-/*************************************************
|
|
-* Perl-Compatible Regular Expressions *
|
|
-*************************************************/
|
|
-
|
|
-/* Copyright (c) 1997-2001 University of Cambridge */
|
|
-
|
|
-#ifndef _PCRE_H
|
|
-#define _PCRE_H
|
|
-
|
|
-/* ----- CODE ADDED ---- */
|
|
-
|
|
-#ifdef __KERNEL__
|
|
-#include <linux/slab.h> // for kmalloc/kfree
|
|
-#endif
|
|
-
|
|
-#ifdef __KERNEL__
|
|
-#define PCRE_PRINTF printk
|
|
-#define isprint(x) ((unsigned char)(x) >= 128 && (unsigned char)(x) <= 255)
|
|
-#else
|
|
-#define PCRE_PRINTF printf
|
|
-#endif
|
|
-
|
|
-/* The value of NEWLINE determines the newline character. The default is to
|
|
- * leave it up to the compiler, but some sites want to force a particular value.
|
|
- * On Unix systems, "configure" can be used to override this default. */
|
|
-
|
|
-#ifndef NEWLINE
|
|
-#define NEWLINE '\n'
|
|
-#endif
|
|
-
|
|
-/* ---- CODE DELETED ---- */
|
|
-
|
|
-/* Options */
|
|
-
|
|
-#define PCRE_CASELESS 0x0001
|
|
-#define PCRE_MULTILINE 0x0002
|
|
-#define PCRE_DOTALL 0x0004
|
|
-#define PCRE_EXTENDED 0x0008
|
|
-#define PCRE_ANCHORED 0x0010
|
|
-#define PCRE_DOLLAR_ENDONLY 0x0020
|
|
-#define PCRE_EXTRA 0x0040
|
|
-#define PCRE_NOTBOL 0x0080
|
|
-#define PCRE_NOTEOL 0x0100
|
|
-#define PCRE_UNGREEDY 0x0200
|
|
-#define PCRE_NOTEMPTY 0x0400
|
|
-#define PCRE_UTF8 0x0800
|
|
-
|
|
-/* Exec-time and get-time error codes */
|
|
-
|
|
-#define PCRE_ERROR_NOMATCH (-1)
|
|
-#define PCRE_ERROR_NULL (-2)
|
|
-#define PCRE_ERROR_BADOPTION (-3)
|
|
-#define PCRE_ERROR_BADMAGIC (-4)
|
|
-#define PCRE_ERROR_UNKNOWN_NODE (-5)
|
|
-#define PCRE_ERROR_NOMEMORY (-6)
|
|
-#define PCRE_ERROR_NOSUBSTRING (-7)
|
|
-
|
|
-/* ---- CODE DELETED ---- */
|
|
-
|
|
-/* Types */
|
|
-
|
|
-struct real_pcre; /* declaration; the definition is private */
|
|
-struct real_pcre_extra; /* declaration; the definition is private */
|
|
-
|
|
-typedef struct real_pcre pcre;
|
|
-typedef struct real_pcre_extra pcre_extra;
|
|
-
|
|
-/* ---- CODE DELETED ---- */
|
|
-
|
|
-extern int pcre_exec(const pcre *, const pcre_extra *,
|
|
- const char *, int, int, int, int *,
|
|
- int);
|
|
-
|
|
-/* ---- CODE ADDED (from internal.h) ---- */
|
|
-
|
|
-/* These are the public options that can change during matching. */
|
|
-
|
|
-#define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL)
|
|
-
|
|
-/* Private options flags start at the most significant end of the four bytes,
|
|
-but skip the top bit so we can use ints for convenience without getting tangled
|
|
-with negative values. The public options defined in pcre.h start at the least
|
|
-significant end. Make sure they don't overlap, though now that we have expanded
|
|
-to four bytes there is plenty of space. */
|
|
-
|
|
-#define PCRE_FIRSTSET 0x40000000 /* first_char is set */
|
|
-#define PCRE_REQCHSET 0x20000000 /* req_char is set */
|
|
-#define PCRE_STARTLINE 0x10000000 /* start after \n for multiline */
|
|
-#define PCRE_ICHANGED 0x04000000 /* i option changes within regex */
|
|
-
|
|
-/* Options for the "extra" block produced by pcre_study(). */
|
|
-
|
|
-#define PCRE_STUDY_MAPPED 0x01 /* a map of starting chars exists */
|
|
-
|
|
-/* Masks for identifying the public options which are permitted at compile
|
|
-time, run time or study time, respectively. */
|
|
-
|
|
-#define PUBLIC_EXEC_OPTIONS \
|
|
- (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY)
|
|
-
|
|
-/* Magic number to provide a small check against being handed junk. */
|
|
-
|
|
-#define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */
|
|
-
|
|
-typedef int BOOL;
|
|
-
|
|
-#define FALSE 0
|
|
-#define TRUE 1
|
|
-
|
|
-/* Opcode table: OP_BRA must be last, as all values >= it are used for brackets
|
|
-that extract substrings. Starting from 1 (i.e. after OP_END), the values up to
|
|
-OP_EOD must correspond in order to the list of escapes immediately above. */
|
|
-
|
|
-enum {
|
|
- OP_END, /* End of pattern */
|
|
-
|
|
- /* Values corresponding to backslashed metacharacters */
|
|
-
|
|
- OP_SOD, /* Start of data: \A */
|
|
- OP_NOT_WORD_BOUNDARY, /* \B */
|
|
- OP_WORD_BOUNDARY, /* \b */
|
|
- OP_NOT_DIGIT, /* \D */
|
|
- OP_DIGIT, /* \d */
|
|
- OP_NOT_WHITESPACE, /* \S */
|
|
- OP_WHITESPACE, /* \s */
|
|
- OP_NOT_WORDCHAR, /* \W */
|
|
- OP_WORDCHAR, /* \w */
|
|
- OP_EODN, /* End of data or \n at end of data: \Z. */
|
|
- OP_EOD, /* End of data: \z */
|
|
-
|
|
- OP_OPT, /* Set runtime options */
|
|
- OP_CIRC, /* Start of line - varies with multiline switch */
|
|
- OP_DOLL, /* End of line - varies with multiline switch */
|
|
- OP_ANY, /* Match any character */
|
|
- OP_CHARS, /* Match string of characters */
|
|
- OP_NOT, /* Match anything but the following char */
|
|
-
|
|
- OP_STAR, /* The maximizing and minimizing versions of */
|
|
- OP_MINSTAR, /* all these opcodes must come in pairs, with */
|
|
- OP_PLUS, /* the minimizing one second. */
|
|
- OP_MINPLUS, /* This first set applies to single characters */
|
|
- OP_QUERY,
|
|
- OP_MINQUERY,
|
|
- OP_UPTO, /* From 0 to n matches */
|
|
- OP_MINUPTO,
|
|
- OP_EXACT, /* Exactly n matches */
|
|
-
|
|
- OP_NOTSTAR, /* The maximizing and minimizing versions of */
|
|
- OP_NOTMINSTAR, /* all these opcodes must come in pairs, with */
|
|
- OP_NOTPLUS, /* the minimizing one second. */
|
|
- OP_NOTMINPLUS, /* This first set applies to "not" single characters */
|
|
- OP_NOTQUERY,
|
|
- OP_NOTMINQUERY,
|
|
- OP_NOTUPTO, /* From 0 to n matches */
|
|
- OP_NOTMINUPTO,
|
|
- OP_NOTEXACT, /* Exactly n matches */
|
|
-
|
|
- OP_TYPESTAR, /* The maximizing and minimizing versions of */
|
|
- OP_TYPEMINSTAR, /* all these opcodes must come in pairs, with */
|
|
- OP_TYPEPLUS, /* the minimizing one second. These codes must */
|
|
- OP_TYPEMINPLUS, /* be in exactly the same order as those above. */
|
|
- OP_TYPEQUERY, /* This set applies to character types such as \d */
|
|
- OP_TYPEMINQUERY,
|
|
- OP_TYPEUPTO, /* From 0 to n matches */
|
|
- OP_TYPEMINUPTO,
|
|
- OP_TYPEEXACT, /* Exactly n matches */
|
|
-
|
|
- OP_CRSTAR, /* The maximizing and minimizing versions of */
|
|
- OP_CRMINSTAR, /* all these opcodes must come in pairs, with */
|
|
- OP_CRPLUS, /* the minimizing one second. These codes must */
|
|
- OP_CRMINPLUS, /* be in exactly the same order as those above. */
|
|
- OP_CRQUERY, /* These are for character classes and back refs */
|
|
- OP_CRMINQUERY,
|
|
- OP_CRRANGE, /* These are different to the three seta above. */
|
|
- OP_CRMINRANGE,
|
|
-
|
|
- OP_CLASS, /* Match a character class */
|
|
- OP_REF, /* Match a back reference */
|
|
- OP_RECURSE, /* Match this pattern recursively */
|
|
-
|
|
- OP_ALT, /* Start of alternation */
|
|
- OP_KET, /* End of group that doesn't have an unbounded repeat */
|
|
- OP_KETRMAX, /* These two must remain together and in this */
|
|
- OP_KETRMIN, /* order. They are for groups the repeat for ever. */
|
|
-
|
|
- /* The assertions must come before ONCE and COND */
|
|
-
|
|
- OP_ASSERT, /* Positive lookahead */
|
|
- OP_ASSERT_NOT, /* Negative lookahead */
|
|
- OP_ASSERTBACK, /* Positive lookbehind */
|
|
- OP_ASSERTBACK_NOT, /* Negative lookbehind */
|
|
- OP_REVERSE, /* Move pointer back - used in lookbehind assertions */
|
|
-
|
|
- /* ONCE and COND must come after the assertions, with ONCE first, as there's
|
|
- a test for >= ONCE for a subpattern that isn't an assertion. */
|
|
-
|
|
- OP_ONCE, /* Once matched, don't back up into the subpattern */
|
|
- OP_COND, /* Conditional group */
|
|
- OP_CREF, /* Used to hold an extraction string number (cond ref) */
|
|
-
|
|
- OP_BRAZERO, /* These two must remain together and in this */
|
|
- OP_BRAMINZERO, /* order. */
|
|
-
|
|
- OP_BRANUMBER, /* Used for extracting brackets whose number is greater
|
|
- than can fit into an opcode. */
|
|
-
|
|
- OP_BRA /* This and greater values are used for brackets that
|
|
- extract substrings up to a basic limit. After that,
|
|
- use is made of OP_BRANUMBER. */
|
|
-};
|
|
-
|
|
-/* The highest extraction number before we have to start using additional
|
|
-bytes. (Originally PCRE didn't have support for extraction counts highter than
|
|
-this number.) The value is limited by the number of opcodes left after OP_BRA,
|
|
-i.e. 255 - OP_BRA. We actually set it a bit lower to leave room for additional
|
|
-opcodes. */
|
|
-
|
|
-#define EXTRACT_BASIC_MAX 150
|
|
-
|
|
-/* All character handling must be done as unsigned characters. Otherwise there
|
|
-are problems with top-bit-set characters and functions such as isspace().
|
|
-However, we leave the interface to the outside world as char *, because that
|
|
-should make things easier for callers. We define a short type for unsigned char
|
|
-to save lots of typing. I tried "uchar", but it causes problems on Digital
|
|
-Unix, where it is defined in sys/types, so use "uschar" instead. */
|
|
-
|
|
-typedef unsigned char uschar;
|
|
-
|
|
-/* The real format of the start of the pcre block; the actual code vector
|
|
-runs on as long as necessary after the end. */
|
|
-
|
|
-typedef struct real_pcre {
|
|
- unsigned long int magic_number;
|
|
- size_t size;
|
|
- const unsigned char *tables;
|
|
- unsigned long int options;
|
|
- unsigned short int top_bracket;
|
|
- unsigned short int top_backref;
|
|
- uschar first_char;
|
|
- uschar req_char;
|
|
- uschar code[1];
|
|
-} real_pcre;
|
|
-
|
|
-/* The real format of the extra block returned by pcre_study(). */
|
|
-
|
|
-typedef struct real_pcre_extra {
|
|
- uschar options;
|
|
- uschar start_bits[32];
|
|
-} real_pcre_extra;
|
|
-
|
|
-/* Structure for passing "static" information around between the functions
|
|
-doing the matching, so that they are thread-safe. */
|
|
-
|
|
-typedef struct match_data {
|
|
- int errorcode; /* As it says */
|
|
- int *offset_vector; /* Offset vector */
|
|
- int offset_end; /* One past the end */
|
|
- int offset_max; /* The maximum usable for return data */
|
|
- const uschar *lcc; /* Points to lower casing table */
|
|
- const uschar *ctypes; /* Points to table of type maps */
|
|
- BOOL offset_overflow; /* Set if too many extractions */
|
|
- BOOL notbol; /* NOTBOL flag */
|
|
- BOOL noteol; /* NOTEOL flag */
|
|
- BOOL utf8; /* UTF8 flag */
|
|
- BOOL endonly; /* Dollar not before final \n */
|
|
- BOOL notempty; /* Empty string match not wanted */
|
|
- const uschar *start_pattern; /* For use when recursing */
|
|
- const uschar *start_subject; /* Start of the subject string */
|
|
- const uschar *end_subject; /* End of the subject string */
|
|
- const uschar *start_match; /* Start of this match attempt */
|
|
- const uschar *end_match_ptr; /* Subject position at end match */
|
|
- int end_offset_top; /* Highwater mark at end of match */
|
|
-} match_data;
|
|
-
|
|
-/* Bit definitions for entries in the pcre_ctypes table. */
|
|
-
|
|
-#define ctype_space 0x01
|
|
-#define ctype_letter 0x02
|
|
-#define ctype_digit 0x04
|
|
-#define ctype_xdigit 0x08
|
|
-#define ctype_word 0x10 /* alphameric or '_' */
|
|
-#define ctype_meta 0x80 /* regexp meta char or zero (end pattern) */
|
|
-
|
|
-/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set
|
|
-of bits for a class map. Some classes are built by combining these tables. */
|
|
-
|
|
-#define cbit_length 320 /* Length of the cbits table */
|
|
-
|
|
-/* Offsets of the various tables from the base tables pointer, and
|
|
-total length. */
|
|
-
|
|
-#define lcc_offset 0
|
|
-#define fcc_offset 256
|
|
-
|
|
-#define fcc_offset 256
|
|
-#define cbits_offset 512
|
|
-#define ctypes_offset (cbits_offset + cbit_length)
|
|
-
|
|
-/* ----- CODE ADDED ---- */
|
|
-
|
|
-#endif // _PCRE_H
|
|
- /* End of pcre.h */
|
|
Index: linux-2.6/security/apparmor/match/pcre_tables.h
|
|
===================================================================
|
|
--- linux-2.6.orig/security/apparmor/match/pcre_tables.h
|
|
+++ /dev/null
|
|
@@ -1,184 +0,0 @@
|
|
-
|
|
-/*************************************************
|
|
-* Perl-Compatible Regular Expressions *
|
|
-*************************************************/
|
|
-
|
|
-/* This file is automatically written by the dftables auxiliary
|
|
-program. If you edit it by hand, you might like to edit the Makefile to
|
|
-prevent its ever being regenerated.
|
|
-
|
|
-This file is #included in the compilation of pcre.c to build the default
|
|
-character tables which are used when no tables are passed to the compile
|
|
-function. */
|
|
-
|
|
-static unsigned char pcre_default_tables[] = {
|
|
-
|
|
-/* This table is a lower casing table. */
|
|
-
|
|
- 0, 1, 2, 3, 4, 5, 6, 7,
|
|
- 8, 9, 10, 11, 12, 13, 14, 15,
|
|
- 16, 17, 18, 19, 20, 21, 22, 23,
|
|
- 24, 25, 26, 27, 28, 29, 30, 31,
|
|
- 32, 33, 34, 35, 36, 37, 38, 39,
|
|
- 40, 41, 42, 43, 44, 45, 46, 47,
|
|
- 48, 49, 50, 51, 52, 53, 54, 55,
|
|
- 56, 57, 58, 59, 60, 61, 62, 63,
|
|
- 64, 97, 98, 99,100,101,102,103,
|
|
- 104,105,106,107,108,109,110,111,
|
|
- 112,113,114,115,116,117,118,119,
|
|
- 120,121,122, 91, 92, 93, 94, 95,
|
|
- 96, 97, 98, 99,100,101,102,103,
|
|
- 104,105,106,107,108,109,110,111,
|
|
- 112,113,114,115,116,117,118,119,
|
|
- 120,121,122,123,124,125,126,127,
|
|
- 128,129,130,131,132,133,134,135,
|
|
- 136,137,138,139,140,141,142,143,
|
|
- 144,145,146,147,148,149,150,151,
|
|
- 152,153,154,155,156,157,158,159,
|
|
- 160,161,162,163,164,165,166,167,
|
|
- 168,169,170,171,172,173,174,175,
|
|
- 176,177,178,179,180,181,182,183,
|
|
- 184,185,186,187,188,189,190,191,
|
|
- 192,193,194,195,196,197,198,199,
|
|
- 200,201,202,203,204,205,206,207,
|
|
- 208,209,210,211,212,213,214,215,
|
|
- 216,217,218,219,220,221,222,223,
|
|
- 224,225,226,227,228,229,230,231,
|
|
- 232,233,234,235,236,237,238,239,
|
|
- 240,241,242,243,244,245,246,247,
|
|
- 248,249,250,251,252,253,254,255,
|
|
-
|
|
-/* This table is a case flipping table. */
|
|
-
|
|
- 0, 1, 2, 3, 4, 5, 6, 7,
|
|
- 8, 9, 10, 11, 12, 13, 14, 15,
|
|
- 16, 17, 18, 19, 20, 21, 22, 23,
|
|
- 24, 25, 26, 27, 28, 29, 30, 31,
|
|
- 32, 33, 34, 35, 36, 37, 38, 39,
|
|
- 40, 41, 42, 43, 44, 45, 46, 47,
|
|
- 48, 49, 50, 51, 52, 53, 54, 55,
|
|
- 56, 57, 58, 59, 60, 61, 62, 63,
|
|
- 64, 97, 98, 99,100,101,102,103,
|
|
- 104,105,106,107,108,109,110,111,
|
|
- 112,113,114,115,116,117,118,119,
|
|
- 120,121,122, 91, 92, 93, 94, 95,
|
|
- 96, 65, 66, 67, 68, 69, 70, 71,
|
|
- 72, 73, 74, 75, 76, 77, 78, 79,
|
|
- 80, 81, 82, 83, 84, 85, 86, 87,
|
|
- 88, 89, 90,123,124,125,126,127,
|
|
- 128,129,130,131,132,133,134,135,
|
|
- 136,137,138,139,140,141,142,143,
|
|
- 144,145,146,147,148,149,150,151,
|
|
- 152,153,154,155,156,157,158,159,
|
|
- 160,161,162,163,164,165,166,167,
|
|
- 168,169,170,171,172,173,174,175,
|
|
- 176,177,178,179,180,181,182,183,
|
|
- 184,185,186,187,188,189,190,191,
|
|
- 192,193,194,195,196,197,198,199,
|
|
- 200,201,202,203,204,205,206,207,
|
|
- 208,209,210,211,212,213,214,215,
|
|
- 216,217,218,219,220,221,222,223,
|
|
- 224,225,226,227,228,229,230,231,
|
|
- 232,233,234,235,236,237,238,239,
|
|
- 240,241,242,243,244,245,246,247,
|
|
- 248,249,250,251,252,253,254,255,
|
|
-
|
|
-/* This table contains bit maps for various character classes.
|
|
-Each map is 32 bytes long and the bits run from the least
|
|
-significant end of each byte. The classes that have their own
|
|
-maps are: space, xdigit, digit, upper, lower, word, graph
|
|
-print, punct, and cntrl. Other classes are built from combinations. */
|
|
-
|
|
- 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
-
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
|
|
- 0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
-
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
-
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
- 0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
-
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
-
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
|
|
- 0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
-
|
|
- 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff,
|
|
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
-
|
|
- 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
|
|
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
-
|
|
- 0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc,
|
|
- 0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
-
|
|
- 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
-
|
|
-/* This table identifies various classes of character by individual bits:
|
|
- 0x01 white space character
|
|
- 0x02 letter
|
|
- 0x04 decimal digit
|
|
- 0x08 hexadecimal digit
|
|
- 0x10 alphanumeric or '_'
|
|
- 0x80 regular expression metacharacter or binary zero
|
|
-*/
|
|
-
|
|
- 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */
|
|
- 0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /* 8- 15 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */
|
|
- 0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /* - ' */
|
|
- 0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /* ( - / */
|
|
- 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */
|
|
- 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /* 8 - ? */
|
|
- 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* @ - G */
|
|
- 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */
|
|
- 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */
|
|
- 0x12,0x12,0x12,0x80,0x00,0x00,0x80,0x10, /* X - _ */
|
|
- 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* ` - g */
|
|
- 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* h - o */
|
|
- 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* p - w */
|
|
- 0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /* x -127 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */
|
|
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */
|
|
-
|
|
-/* End of chartables.c */
|
|
Index: linux-2.6/security/apparmor/module_interface.c
|
|
===================================================================
|
|
--- linux-2.6.orig/security/apparmor/module_interface.c
|
|
+++ linux-2.6/security/apparmor/module_interface.c
|
|
@@ -14,7 +14,6 @@
|
|
#include "apparmor.h"
|
|
#include "inline.h"
|
|
#include "module_interface.h"
|
|
-#include "match/match.h"
|
|
|
|
/* aa_code defined in module_interface.h */
|
|
|
|
@@ -25,40 +24,6 @@ struct aa_taskreplace_data {
|
|
struct aaprofile *new_profile;
|
|
};
|
|
|
|
-/* inlines must be forward of there use in newer version of gcc,
|
|
- just forward declaring with a prototype won't work anymore */
|
|
-
|
|
-static inline void free_aa_entry(struct aa_entry *entry)
|
|
-{
|
|
- if (entry) {
|
|
- kfree(entry->filename);
|
|
- aamatch_free(entry->extradata);
|
|
- kfree(entry);
|
|
- }
|
|
-}
|
|
-
|
|
-/**
|
|
- * alloc_aa_entry - create new empty aa_entry
|
|
- * This routine allocates, initializes, and returns a new aa_entry
|
|
- * file entry structure. Structure is zeroed. Returns new structure on
|
|
- * success, %NULL on failure.
|
|
- */
|
|
-static inline struct aa_entry *alloc_aa_entry(void)
|
|
-{
|
|
- struct aa_entry *entry;
|
|
-
|
|
- AA_DEBUG("%s\n", __FUNCTION__);
|
|
- entry = kzalloc(sizeof(struct aa_entry), GFP_KERNEL);
|
|
- if (entry) {
|
|
- int i;
|
|
- INIT_LIST_HEAD(&entry->list);
|
|
- for (i = 0; i <= POS_AA_FILE_MAX; i++) {
|
|
- INIT_LIST_HEAD(&entry->listp[i]);
|
|
- }
|
|
- }
|
|
- return entry;
|
|
-}
|
|
-
|
|
/**
|
|
* free_aaprofile_rcu - rcu callback for free profiles
|
|
* @head: rcu_head struct of the profile whose reference is being put.
|
|
@@ -392,144 +357,38 @@ fail:
|
|
}
|
|
|
|
/**
|
|
- * aa_activate_file_entry - unpack serialized file entry
|
|
+ * aa_activate_dfa - unpack a file rule dfa
|
|
* @e: serialized data extent information
|
|
*
|
|
- * unpack the information used for a file ACL entry.
|
|
+ * returns dfa or ERR_PTR
|
|
*/
|
|
-static inline struct aa_entry *aa_activate_file_entry(struct aa_ext *e)
|
|
+struct aa_dfa *aa_activate_dfa(struct aa_ext *e)
|
|
{
|
|
- struct aa_entry *entry = NULL;
|
|
-
|
|
- if (!(entry = alloc_aa_entry()))
|
|
- goto fail;
|
|
-
|
|
- AA_READ_X(e, AA_STRUCT, NULL, "fe");
|
|
- AA_READ_X(e, AA_DYN_STRING, &entry->filename, NULL);
|
|
- AA_READ_X(e, AA_U32, &entry->mode, NULL);
|
|
- AA_READ_X(e, AA_U32, &entry->type, NULL);
|
|
-
|
|
- entry->extradata = aamatch_alloc(entry->type);
|
|
- if (IS_ERR(entry->extradata)) {
|
|
- entry->extradata = NULL;
|
|
- goto fail;
|
|
- }
|
|
-
|
|
- if (entry->extradata &&
|
|
- aamatch_serialize(entry->extradata, e, aa_is_nameX) != 0) {
|
|
- goto fail;
|
|
- }
|
|
- AA_READ_X(e, AA_STRUCTEND, NULL, NULL);
|
|
-
|
|
- switch (entry->type) {
|
|
- case aa_entry_literal:
|
|
- AA_DEBUG("%s: %s [no pattern] mode=0x%x\n",
|
|
- __FUNCTION__,
|
|
- entry->filename,
|
|
- entry->mode);
|
|
- break;
|
|
- case aa_entry_tailglob:
|
|
- AA_DEBUG("%s: %s [tailglob] mode=0x%x\n",
|
|
- __FUNCTION__,
|
|
- entry->filename,
|
|
- entry->mode);
|
|
- break;
|
|
- case aa_entry_pattern:
|
|
- AA_DEBUG("%s: %s mode=0x%x\n",
|
|
- __FUNCTION__,
|
|
- entry->filename,
|
|
- entry->mode);
|
|
- break;
|
|
- default:
|
|
- AA_WARN("%s: INVALID entry_match_type %d\n",
|
|
- __FUNCTION__,
|
|
- (int)entry->type);
|
|
- goto fail;
|
|
- }
|
|
-
|
|
- return entry;
|
|
-
|
|
-fail:
|
|
- free_aa_entry(entry);
|
|
- return NULL;
|
|
-}
|
|
-
|
|
-/**
|
|
- * check_rule_and_add - check a file rule is valid and add to a profile
|
|
- * @file_entry: file rule to add
|
|
- * @profile: profile to add the rule to
|
|
- * @message: error message returned if the addition failes.
|
|
- *
|
|
- * perform consistency check to ensure that a file rule entry is valid.
|
|
- * If the rule is valid it is added to the profile.
|
|
- */
|
|
-static inline int check_rule_and_add(struct aa_entry *file_entry,
|
|
- struct aaprofile *profile,
|
|
- const char **message)
|
|
-{
|
|
- /* verify consistency of x, px, ix, ux for entry against
|
|
- possible duplicates for this entry */
|
|
- int mode = AA_EXEC_MODIFIER_MASK(file_entry->mode);
|
|
- int i;
|
|
-
|
|
- if (mode && !(AA_MAY_EXEC & file_entry->mode)) {
|
|
- *message = "inconsistent rule, x modifiers without x";
|
|
- goto out;
|
|
- }
|
|
-
|
|
- /* check that only 1 of the modifiers is set */
|
|
- if (mode && (mode & (mode - 1))) {
|
|
- *message = "inconsistent rule, multiple x modifiers";
|
|
- goto out;
|
|
- }
|
|
+ char *blob = NULL;
|
|
+ size_t size, error = 0;
|
|
+ struct aa_dfa *dfa = NULL;
|
|
+
|
|
+ size = aa_is_nameX(e, AA_BLOB_LOC, &blob, "aadfa");
|
|
+ if (size) {
|
|
+ dfa = aamatch_alloc();
|
|
+ if (dfa) {
|
|
+ error = unpack_dfa(dfa, blob, size);
|
|
+
|
|
+ if (!error)
|
|
+ error = verify_dfa(dfa);
|
|
+ } else {
|
|
+ error = -ENOMEM;
|
|
+ }
|
|
|
|
- /* ix -> m (required so that target exec binary may map itself) */
|
|
- if (mode & AA_EXEC_INHERIT)
|
|
- file_entry->mode |= AA_EXEC_MMAP;
|
|
-
|
|
- list_add(&file_entry->list, &profile->file_entry);
|
|
- profile->num_file_entries++;
|
|
-
|
|
- mode = file_entry->mode;
|
|
-
|
|
- /* Handle partitioned lists
|
|
- * Chain entries onto sublists based on individual
|
|
- * permission bits. This allows more rapid searching.
|
|
- */
|
|
- for (i = 0; i <= POS_AA_FILE_MAX; i++) {
|
|
- if (mode & (1 << i))
|
|
- /* profile->file_entryp[i] initially set to
|
|
- * NULL in alloc_aaprofile() */
|
|
- list_add(&file_entry->listp[i],
|
|
- &profile->file_entryp[i]);
|
|
+ if (error) {
|
|
+ aamatch_free(dfa);
|
|
+ dfa = ERR_PTR(error);
|
|
+ }
|
|
}
|
|
|
|
- return 1;
|
|
-
|
|
-out:
|
|
- free_aa_entry(file_entry);
|
|
- return 0;
|
|
+ return dfa;
|
|
}
|
|
|
|
-#define AA_ENTRY_LIST(NAME) \
|
|
- do { \
|
|
- if (aa_is_nameX(e, AA_LIST, NULL, (NAME))) { \
|
|
- rulename = ""; \
|
|
- error_string = "Invalid file entry"; \
|
|
- while (!aa_is_nameX(e, AA_LISTEND, NULL, NULL)) { \
|
|
- struct aa_entry *file_entry; \
|
|
- file_entry = aa_activate_file_entry(e); \
|
|
- if (!file_entry) \
|
|
- goto fail; \
|
|
- if (!check_rule_and_add(file_entry, profile, \
|
|
- &error_string)) { \
|
|
- rulename = file_entry->filename; \
|
|
- goto fail; \
|
|
- } \
|
|
- } \
|
|
- } \
|
|
- } while (0)
|
|
-
|
|
/**
|
|
* aa_activate_profile - unpack a serialized profile
|
|
* @e: serialized data extent information
|
|
@@ -565,10 +424,14 @@ static struct aaprofile *aa_activate_pro
|
|
error_string = "Invalid capabilities";
|
|
AA_READ_X(e, AA_U32, &(profile->capabilities), NULL);
|
|
|
|
- /* get the file entries. */
|
|
- AA_ENTRY_LIST("pgent"); /* pcre rules */
|
|
- AA_ENTRY_LIST("sgent"); /* simple globs */
|
|
- AA_ENTRY_LIST("fent"); /* regular file entries */
|
|
+ /* get file rules */
|
|
+ profile->file_rules = aa_activate_dfa(e);
|
|
+ if (IS_ERR(profile->file_rules)) {
|
|
+ error_string = "Invalid file rule dfa\n";
|
|
+ *error = PTR_ERR(profile->file_rules);
|
|
+ profile->file_rules = NULL;
|
|
+ goto fail;
|
|
+ }
|
|
|
|
/* get the net entries */
|
|
if (aa_is_nameX(e, AA_LIST, NULL, "net")) {
|
|
@@ -631,7 +494,7 @@ static void *aa_activate_top_profile(str
|
|
}
|
|
|
|
/* check that the interface version is currently supported */
|
|
- if (e->version != 2) {
|
|
+ if (e->version != 3) {
|
|
AA_WARN("%s: unsupported interface version (%d)\n",
|
|
INTERFACE_ID, e->version);
|
|
*error = -EPROTONOSUPPORT;
|
|
@@ -816,7 +679,6 @@ void free_aaprofile_kref(struct kref *kr
|
|
*/
|
|
void free_aaprofile(struct aaprofile *profile)
|
|
{
|
|
- struct aa_entry *ent, *tmp;
|
|
struct aaprofile *p, *ptmp;
|
|
|
|
AA_DEBUG("%s(%p)\n", __FUNCTION__, profile);
|
|
@@ -833,13 +695,7 @@ void free_aaprofile(struct aaprofile *pr
|
|
BUG();
|
|
}
|
|
|
|
- list_for_each_entry_safe(ent, tmp, &profile->file_entry, list) {
|
|
- if (ent->filename)
|
|
- AA_DEBUG("freeing aa_entry: %p %s\n",
|
|
- ent->filename, ent->filename);
|
|
- list_del_init(&ent->list);
|
|
- free_aa_entry(ent);
|
|
- }
|
|
+ aamatch_free(profile->file_rules);
|
|
|
|
/* use free_aaprofile instead of put_aaprofile to destroy the
|
|
* null_profile, because the null_profile use the same reference
|
|
Index: linux-2.6/security/apparmor/apparmorfs.c
|
|
===================================================================
|
|
--- linux-2.6.orig/security/apparmor/apparmorfs.c
|
|
+++ linux-2.6/security/apparmor/apparmorfs.c
|
|
@@ -17,7 +17,6 @@
|
|
|
|
#include "apparmor.h"
|
|
#include "inline.h"
|
|
-#include "match/match.h"
|
|
|
|
#define SECFS_AA "apparmor"
|
|
static struct dentry *aafs_dentry = NULL;
|
|
Index: linux-2.6/security/apparmor/match.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-2.6/security/apparmor/match.c
|
|
@@ -0,0 +1,274 @@
|
|
+/*
|
|
+ * Copyright (C) 2002-2005 Novell/SUSE
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License as
|
|
+ * published by the Free Software Foundation, version 2 of the
|
|
+ * License.
|
|
+ *
|
|
+ * http://forge.novell.com/modules/xfmod/project/?apparmor
|
|
+ *
|
|
+ * AppArmor aamatch submodule (w/ pattern expansion).
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <asm/unaligned.h>
|
|
+#include <linux/module.h>
|
|
+#include "match.h"
|
|
+
|
|
+static const char *features="pattern=aadfa";
|
|
+
|
|
+static struct table_header *unpack_table(void *blob, size_t bsize)
|
|
+{
|
|
+ struct table_header *table = NULL;
|
|
+ struct table_header th;
|
|
+ size_t tsize;
|
|
+
|
|
+ if (bsize < sizeof(struct table_header))
|
|
+ goto out;
|
|
+
|
|
+ th.td_id = ntohs(get_unaligned((u16 *) (blob)));
|
|
+ th.td_flags = ntohs(get_unaligned((u16 *) (blob + 2)));
|
|
+ th.td_lolen = ntohl(get_unaligned((u32 *) (blob + 8)));
|
|
+ blob += sizeof(struct table_header);
|
|
+
|
|
+ if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 ||
|
|
+ th.td_flags == YYTD_DATA8))
|
|
+ goto out;
|
|
+
|
|
+ tsize = table_size(th.td_lolen, th.td_flags);
|
|
+ if (bsize < tsize)
|
|
+ goto out;
|
|
+
|
|
+ table = kmalloc(tsize, GFP_KERNEL);
|
|
+ if (table) {
|
|
+ *table = th;
|
|
+ if (th.td_flags == YYTD_DATA8)
|
|
+ UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
|
|
+ u8, ntohb);
|
|
+ else if (th.td_flags == YYTD_DATA16)
|
|
+ UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
|
|
+ u16, ntohs);
|
|
+ else
|
|
+ UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
|
|
+ u32, ntohl);
|
|
+ }
|
|
+
|
|
+out:
|
|
+ return table;
|
|
+}
|
|
+
|
|
+int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size)
|
|
+{
|
|
+ int i;
|
|
+ int error = -ENOMEM;
|
|
+
|
|
+ /* get dfa table set header */
|
|
+ if (size < sizeof(struct table_set_header))
|
|
+ goto fail;
|
|
+
|
|
+ dfa->th.th_magic = ntohl(get_unaligned((u32 *) (blob + 0)));
|
|
+ dfa->th.th_hsize = ntohl(get_unaligned((u32 *) (blob + 4)));
|
|
+ dfa->th.th_ssize = ntohl(get_unaligned((u32 *) (blob + 8)));
|
|
+ dfa->th.th_flags = ntohs(get_unaligned((u16 *) (blob + 12)));
|
|
+
|
|
+ if (dfa->th.th_magic != YYTH_MAGIC)
|
|
+ goto fail;
|
|
+
|
|
+ if (size < dfa->th.th_hsize)
|
|
+ goto fail;
|
|
+
|
|
+ blob += dfa->th.th_hsize;
|
|
+ size -= dfa->th.th_hsize;
|
|
+
|
|
+ while (size > 0) {
|
|
+ struct table_header *table;
|
|
+ table = unpack_table(blob, size);
|
|
+ if (!table)
|
|
+ goto fail;
|
|
+
|
|
+ switch(table->td_id) {
|
|
+ case YYTD_ID_ACCEPT:
|
|
+ case YYTD_ID_BASE:
|
|
+ dfa->tables[table->td_id - 1] = table;
|
|
+ if (table->td_flags != YYTD_DATA32)
|
|
+ goto fail_proto;
|
|
+ break;
|
|
+ case YYTD_ID_DEF:
|
|
+ case YYTD_ID_NXT:
|
|
+ case YYTD_ID_CHK:
|
|
+ dfa->tables[table->td_id - 1] = table;
|
|
+ if (table->td_flags != YYTD_DATA16)
|
|
+ goto fail_proto;
|
|
+ break;
|
|
+ case YYTD_ID_EC:
|
|
+ dfa->tables[table->td_id - 1] = table;
|
|
+ if (table->td_flags != YYTD_DATA8)
|
|
+ goto fail_proto;
|
|
+ break;
|
|
+ default:
|
|
+ kfree(table);
|
|
+ goto fail_proto;
|
|
+ }
|
|
+
|
|
+ blob += table_size(table->td_lolen, table->td_flags);
|
|
+ size -= table_size(table->td_lolen, table->td_flags);
|
|
+ }
|
|
+
|
|
+ error = 0;
|
|
+
|
|
+ return error;
|
|
+
|
|
+fail_proto:
|
|
+ error = -EPROTO;
|
|
+fail:
|
|
+ for (i = 0; i < YYTD_ID_NXT; i++) {
|
|
+ if (dfa->tables[i]) {
|
|
+ kfree(dfa->tables[i]);
|
|
+ dfa->tables[i] = NULL;
|
|
+ }
|
|
+ }
|
|
+ return error;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * verify_dfa - verify that all the transitions and states in the dfa tables
|
|
+ * are in bounds.
|
|
+ * @dfa: dfa to test
|
|
+ *
|
|
+ * assumes dfa has gone through the verification done by unpacking
|
|
+ */
|
|
+int verify_dfa(struct aa_dfa *dfa)
|
|
+{
|
|
+ size_t i, state_count, trans_count;
|
|
+ int error = -EPROTO;
|
|
+
|
|
+ /* check that required tables exist */
|
|
+ if (!(dfa->tables[YYTD_ID_ACCEPT -1 ] &&
|
|
+ dfa->tables[YYTD_ID_DEF - 1] &&
|
|
+ dfa->tables[YYTD_ID_BASE - 1] &&
|
|
+ dfa->tables[YYTD_ID_NXT - 1] &&
|
|
+ dfa->tables[YYTD_ID_CHK - 1]))
|
|
+ goto out;
|
|
+
|
|
+ /* accept.size == default.size == base.size */
|
|
+ state_count = dfa->tables[YYTD_ID_BASE - 1]->td_lolen;
|
|
+ if (!(state_count == dfa->tables[YYTD_ID_DEF - 1]->td_lolen &&
|
|
+ state_count == dfa->tables[YYTD_ID_ACCEPT - 1]->td_lolen))
|
|
+ goto out;
|
|
+
|
|
+ /* next.size == chk.size */
|
|
+ trans_count = dfa->tables[YYTD_ID_NXT - 1]->td_lolen;
|
|
+ if (trans_count != dfa->tables[YYTD_ID_CHK - 1]->td_lolen)
|
|
+ goto out;
|
|
+
|
|
+ /* if equivalence classes then its table must be 256 */
|
|
+ if (dfa->tables[YYTD_ID_EC - 1] &&
|
|
+ dfa->tables[YYTD_ID_EC - 1]->td_lolen != 256)
|
|
+ goto out;
|
|
+
|
|
+ for (i = 0; i < state_count; i++) {
|
|
+ if (DEFAULT_TABLE(dfa)[i] >= state_count)
|
|
+ goto out;
|
|
+ if (BASE_TABLE(dfa)[i] >= trans_count + 256)
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < trans_count ; i++) {
|
|
+ if (NEXT_TABLE(dfa)[i] >= state_count)
|
|
+ goto out;
|
|
+ if (CHECK_TABLE(dfa)[i] >= state_count)
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ error = 0;
|
|
+out:
|
|
+ return error;
|
|
+}
|
|
+
|
|
+struct aa_dfa *aamatch_alloc(void)
|
|
+{
|
|
+ return kzalloc(sizeof(struct aa_dfa), GFP_KERNEL);
|
|
+}
|
|
+
|
|
+void aamatch_free(struct aa_dfa *dfa)
|
|
+{
|
|
+ if (dfa) {
|
|
+ int i;
|
|
+ for (i = 0; i < YYTD_ID_NXT; i++) {
|
|
+ kfree(dfa->tables[i]);
|
|
+ }
|
|
+ }
|
|
+ kfree(dfa);
|
|
+}
|
|
+
|
|
+const char *aamatch_features(void)
|
|
+{
|
|
+ return features;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * aadfa_label - return the permissions associated with @state
|
|
+ * @dfa: dfa to get state permission from
|
|
+ * @state: state in the dfa for which to get a label
|
|
+ *
|
|
+ * Assumes that state is a valid state of the dfa
|
|
+ *
|
|
+ * Returns the label associated with @state. 0 indicates the state
|
|
+ * is no-accepting/provides no permissions.
|
|
+ */
|
|
+inline unsigned int aadfa_label(struct aa_dfa *dfa, int state)
|
|
+{
|
|
+ return ACCEPT_TABLE(dfa)[state];
|
|
+}
|
|
+
|
|
+/**
|
|
+ * aadfa_match - match @path against @dfa starting in @state
|
|
+ * @dfa: the dfa to match @path against
|
|
+ * @state: the state to start matching in
|
|
+ * @path: the path to match against the dfa
|
|
+ *
|
|
+ * aadfa_match will match the full path length and return the state it
|
|
+ * finished matching in. The final state returned can be used to
|
|
+ * lookup the accepting label or as a starting point to continue matching
|
|
+ * with a new string if the path has been broken into multiple components.
|
|
+ */
|
|
+inline unsigned int aadfa_match(struct aa_dfa *dfa, unsigned int state,
|
|
+ const char *path)
|
|
+{
|
|
+ u8 *s = (u8 *) path;
|
|
+ u16 *def = DEFAULT_TABLE(dfa);
|
|
+ u32 *base = BASE_TABLE(dfa);
|
|
+ u16 *next = NEXT_TABLE(dfa);
|
|
+ u16 *check = CHECK_TABLE(dfa);
|
|
+ unsigned int pos;
|
|
+
|
|
+ /* current state is <state>, matching character *s */
|
|
+ if (dfa->tables[YYTD_ID_EC - 1]) {
|
|
+ u8 *equiv = EQUIV_TABLE(dfa);
|
|
+ for ( ; *s; ++s) {
|
|
+ pos = base[state] + equiv[*s];
|
|
+ if (check[pos] == state)
|
|
+ state = next[pos];
|
|
+ else
|
|
+ state = def[state];
|
|
+ }
|
|
+ } else {
|
|
+ for ( ; *s; ++s) {
|
|
+ pos = base[state] + *s;
|
|
+ if (check[pos] == state)
|
|
+ state = next[pos];
|
|
+ else
|
|
+ state = def[state];
|
|
+ }
|
|
+ }
|
|
+ return state;
|
|
+}
|
|
+
|
|
+unsigned int aamatch(struct aa_dfa *dfa, const char *pathname)
|
|
+{
|
|
+ if (dfa)
|
|
+ return aadfa_label(dfa, aadfa_match(dfa, 1, pathname));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
Index: linux-2.6/security/apparmor/match.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-2.6/security/apparmor/match.h
|
|
@@ -0,0 +1,80 @@
|
|
+/*
|
|
+ * Copyright (C) 2002-2005 Novell/SUSE
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License as
|
|
+ * published by the Free Software Foundation, version 2 of the
|
|
+ * License.
|
|
+ *
|
|
+ * AppArmor submodule (match) prototypes
|
|
+ */
|
|
+
|
|
+#ifndef __MATCH_H
|
|
+#define __MATCH_H
|
|
+
|
|
+#define YYTH_MAGIC 0x1B5E783D
|
|
+
|
|
+struct table_set_header {
|
|
+ u32 th_magic; /* TH_MAGIC */
|
|
+ u32 th_hsize;
|
|
+ u32 th_ssize;
|
|
+ u16 th_flags;
|
|
+ char th_version[];
|
|
+};
|
|
+
|
|
+#define YYTD_ID_ACCEPT 1 /* 1 */
|
|
+#define YYTD_ID_BASE 2 /* 2 */
|
|
+#define YYTD_ID_CHK 3 /* 3 */
|
|
+#define YYTD_ID_DEF 4 /* 4 */
|
|
+#define YYTD_ID_EC 5 /* 5 */
|
|
+#define YYTD_ID_NXT 6 /* 8 */
|
|
+#define YYTD_ID_META 7 /* 6 */
|
|
+
|
|
+#define YYTD_DATA8 1
|
|
+#define YYTD_DATA16 2
|
|
+#define YYTD_DATA32 4
|
|
+
|
|
+struct table_header {
|
|
+ u16 td_id;
|
|
+ u16 td_flags;
|
|
+ u32 td_hilen;
|
|
+ u32 td_lolen;
|
|
+ char td_data[];
|
|
+};
|
|
+
|
|
+#define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF - 1]->td_data))
|
|
+#define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE - 1]->td_data))
|
|
+#define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT - 1]->td_data))
|
|
+#define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK - 1]->td_data))
|
|
+#define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC - 1]->td_data))
|
|
+#define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT - 1]->td_data))
|
|
+
|
|
+struct aa_dfa {
|
|
+ struct table_header *tables[YYTD_ID_NXT];
|
|
+
|
|
+ struct table_set_header th;
|
|
+};
|
|
+
|
|
+#define ntohb(X) (X)
|
|
+
|
|
+#define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \
|
|
+ do { \
|
|
+ typeof(LEN) __i; \
|
|
+ TYPE *__t = (TYPE *) TABLE; \
|
|
+ TYPE *__b = (TYPE *) BLOB; \
|
|
+ for (__i = 0; __i < LEN; __i++) { \
|
|
+ __t[__i] = NTOHX(__b[__i]); \
|
|
+ } \
|
|
+ } while (0)
|
|
+
|
|
+static inline size_t pad64(size_t i)
|
|
+{
|
|
+ return (i + (size_t)7) & ~(size_t)7;
|
|
+}
|
|
+
|
|
+static inline size_t table_size(size_t len, size_t el_size)
|
|
+{
|
|
+ return pad64(sizeof(struct table_header) + len * el_size);
|
|
+}
|
|
+
|
|
+#endif /* __MATCH_H */
|