new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / os_regex / os_regex_compile.c
1 /* Copyright (C) 2009 Trend Micro Inc.
2  * All right reserved.
3  *
4  * This program is a free software; you can redistribute it
5  * and/or modify it under the terms of the GNU General Public
6  * License (version 2) as published by the FSF - Free Software
7  * Foundation
8  */
9
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
13
14 #include "os_regex.h"
15
16 const char *OSRegex_Execute_pcre2_match(const char *str, OSRegex *reg);
17 const char *OSRegex_Execute_strncmp(const char *subject, OSRegex *reg);
18 const char *OSRegex_Execute_strrcmp(const char *subject, OSRegex *reg);
19 const char *OSRegex_Execute_strcasecmp(const char *subject, OSRegex *reg);
20 const char *OSRegex_Execute_strncasecmp(const char *subject, OSRegex *reg);
21 const char *OSRegex_Execute_strrcasecmp(const char *subject, OSRegex *reg);
22 const char *OSRegex_Execute_strcmp(const char *subject, OSRegex *reg);
23 int OSRegex_CouldBeOptimized(const char *pattern2check);
24
25 /* Compile a regular expression to be used later
26  * Allowed flags are:
27  *      - OS_CASE_SENSITIVE
28  *      - OS_RETURN_SUBSTRING
29  * Returns 1 on success or 0 on error
30  * The error code is set on reg->error
31  */
32 int OSRegex_Compile(const char *pattern, OSRegex *reg, int flags)
33 {
34     char *pattern_pcre2 = NULL;
35     int flags_compile = 0;
36     int error = 0;
37     PCRE2_SIZE erroroffset = 0;
38     size_t pattern_len = 0UL;
39     char first_char, last_char;
40     uint32_t count, i;
41
42     /* Check for references not initialized */
43     if (reg == NULL) {
44         return (0);
45     }
46
47     /* Initialize OSRegex structure */
48     reg->error = 0;
49     reg->sub_strings = NULL;
50     reg->regex = NULL;
51     reg->match_data = NULL;
52     reg->pattern_len = 0UL;
53     reg->pattern = NULL;
54     reg->exec_function = NULL;
55
56     /* The pattern can't be null */
57     if (pattern == NULL) {
58         reg->error = OS_REGEX_PATTERN_NULL;
59         goto compile_error;
60     }
61
62     /* Maximum size of the pattern */
63     pattern_len = strlen(pattern);
64     if (pattern_len > OS_PATTERN_MAXSIZE) {
65         reg->error = OS_REGEX_MAXSIZE;
66         goto compile_error;
67     }
68
69     if (OSRegex_CouldBeOptimized(pattern)) {
70         first_char = pattern[0];
71         last_char = pattern[pattern_len - 1];
72
73         if (first_char == '^') {
74             if (last_char == '$') {
75                 reg->pattern = strdup(&pattern[1]);
76                 reg->pattern_len = pattern_len - 2;
77                 reg->pattern[reg->pattern_len] = '\0';
78                 if (flags & OS_CASE_SENSITIVE) {
79                     reg->exec_function = OSRegex_Execute_strcmp;
80                 } else {
81                     reg->exec_function = OSRegex_Execute_strcasecmp;
82                 }
83                 return (1);
84             } else {
85                 reg->pattern = strdup(&pattern[1]);
86                 reg->pattern_len = pattern_len - 1;
87                 if (flags & OS_CASE_SENSITIVE) {
88                     reg->exec_function = OSRegex_Execute_strncmp;
89                 } else {
90                     reg->exec_function = OSRegex_Execute_strncasecmp;
91                 }
92                 return (1);
93             }
94         } else {
95             if (last_char == '$') {
96                 reg->pattern = strdup(pattern);
97                 reg->pattern_len = pattern_len - 1;
98                 reg->pattern[reg->pattern_len] = '\0';
99                 if (flags & OS_CASE_SENSITIVE) {
100                     reg->exec_function = OSRegex_Execute_strrcmp;
101                 } else {
102                     reg->exec_function = OSRegex_Execute_strrcasecmp;
103                 }
104                 return (1);
105             }
106         }
107     }
108
109     reg->exec_function = OSRegex_Execute_pcre2_match;
110
111     /* Ossec pattern conversion */
112     if (OSRegex_Convert(pattern, &pattern_pcre2, OS_CONVERT_REGEX) == 0) {
113         reg->error = OS_REGEX_BADREGEX;
114         goto compile_error;
115     }
116
117     flags_compile |= PCRE2_UTF;
118     flags_compile |= PCRE2_NO_UTF_CHECK;
119     flags_compile |= (flags & OS_CASE_SENSITIVE) ? 0 : PCRE2_CASELESS;
120     reg->regex = pcre2_compile((PCRE2_SPTR)pattern_pcre2, PCRE2_ZERO_TERMINATED, flags_compile,
121                                &error, &erroroffset, NULL);
122     if (reg->regex == NULL) {
123         reg->error = OS_REGEX_BADREGEX;
124         goto compile_error;
125     }
126
127     reg->match_data = pcre2_match_data_create_from_pattern(reg->regex, NULL);
128     if (reg->match_data == NULL) {
129         reg->error = OS_REGEX_OUTOFMEMORY;
130         goto compile_error;
131     }
132
133 #ifdef USE_PCRE2_JIT
134     /* Just In Time compilation for faster execution */
135     if (pcre2_jit_compile(reg->regex, PCRE2_JIT_COMPLETE) != 0) {
136         reg->error = OS_REGEX_NO_JIT;
137         goto compile_error;
138     }
139 #endif
140
141     if (flags & OS_RETURN_SUBSTRING) {
142         pcre2_pattern_info(reg->regex, PCRE2_INFO_CAPTURECOUNT, (void *)&count);
143         count++; // to store NULL pointer at the end
144         reg->sub_strings = calloc(count, sizeof(char *));
145         if (reg->sub_strings == NULL) {
146             reg->error = OS_REGEX_OUTOFMEMORY;
147             goto compile_error;
148         }
149         for (i = 0; i < count; i++) {
150             reg->sub_strings[i] = NULL;
151         }
152     }
153
154     free(pattern_pcre2);
155
156     return (1);
157
158 compile_error:
159     /* Error handling */
160
161     if (pattern_pcre2) {
162         free(pattern_pcre2);
163     }
164
165     OSRegex_FreePattern(reg);
166
167     return (0);
168 }
169
170 int OSRegex_CouldBeOptimized(const char *pattern2check) {
171     return OS_Pcre2("^\\^?[A-Za-z0-9 !\"#%&',/:;<=>@_`~-]*\\$?$", pattern2check);
172 }