new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / os_regex / os_converter.c
1 #include <stdbool.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 #include "debug_op.h"
7 #include "os_regex.h"
8
9 #define OSREGEX_TO_PCRE2_FIX                                                                       \
10     ("(?<!\\\\)"     /* not preceed by a '\' */                                                    \
11      "([+*]) \\?"    /* '+' or '*' (capture group 1) followed by '?' */                            \
12      "("             /* start capture group 2 the */                                               \
13      "\\)*"          /* Any number of closing parenthese ')' */                                    \
14      "(?: [|$] | $)" /* Either '|', '$', or end of string */                                       \
15      ")"             /* end of capture group 2 */                                                  \
16     )
17
18 /* determine the length of an array */
19 #define ARRAY_LENGTH(_A) (sizeof(_A) / sizeof(_A[0]))
20
21 /* definition of a remplacement pattern */
22 typedef struct _replacement_pattern {
23     const char *old;
24     const char *new;
25     size_t new_sz;
26     size_t old_sz;
27 } replacement_pattern;
28
29 /* macro to define an entry in a replacement map */
30 #define REPLACEMENT_ENTRY(_old, _new)                                                              \
31     { .old = _old, .new = _new, .new_sz = sizeof(_new) - 1, .old_sz = sizeof(_old) - 1 }
32
33 /* replacement map for ossec regex to perl6 expressions */
34 const replacement_pattern _replacement_map_regex[] = {
35     REPLACEMENT_ENTRY("\\p", "[()*+,.:;\\<=>?\\[\\]!\"'#%&$|{}-]"),
36     REPLACEMENT_ENTRY("\\s", "[ ]"),
37     REPLACEMENT_ENTRY("\\W", "[^A-Za-z0-9@_-]"),
38     REPLACEMENT_ENTRY("\\w", "[A-Za-z0-9@_-]"),
39     REPLACEMENT_ENTRY("\\d", "[0-9]"),
40     REPLACEMENT_ENTRY("\\D", "[^0-9]"),
41     REPLACEMENT_ENTRY("\\S", "[^ ]"),
42     REPLACEMENT_ENTRY("\\.", "(?s:.)"),
43     REPLACEMENT_ENTRY("$", "(?!\\n)$"),
44     REPLACEMENT_ENTRY(".", "\\."),
45     REPLACEMENT_ENTRY("[", "\\["),
46     REPLACEMENT_ENTRY("]", "\\]"),
47     REPLACEMENT_ENTRY("{", "\\{"),
48     REPLACEMENT_ENTRY("}", "\\}"),
49     REPLACEMENT_ENTRY("+", "\\+"),
50     REPLACEMENT_ENTRY("?", "\\?"),
51     REPLACEMENT_ENTRY("*", "\\*")};
52
53 /* replacement map for match expressions */
54 const replacement_pattern _replacement_map_match[] = {
55     REPLACEMENT_ENTRY(".", "\\."), REPLACEMENT_ENTRY("[", "\\["),  REPLACEMENT_ENTRY("]", "\\]"),
56     REPLACEMENT_ENTRY("{", "\\{"), REPLACEMENT_ENTRY("}", "\\}"),  REPLACEMENT_ENTRY("+", "\\+"),
57     REPLACEMENT_ENTRY("?", "\\?"), REPLACEMENT_ENTRY("*", "\\*"),  REPLACEMENT_ENTRY("(", "\\("),
58     REPLACEMENT_ENTRY(")", "\\)"), REPLACEMENT_ENTRY("\\", "\\\\")};
59
60 int OSRegex_ConvertRegex(const char *pattern, char **converted_pattern_ptr);
61 int OSRegex_ConvertMatch(const char *pattern, char **converted_pattern_ptr);
62
63 int OSRegex_Convert(const char *pattern, char **converted_pattern_ptr, uint32_t map)
64 {
65     *converted_pattern_ptr = NULL;
66     switch (map) {
67         case OS_CONVERT_REGEX:
68             return OSRegex_ConvertRegex(pattern, converted_pattern_ptr);
69         case OS_CONVERT_MATCH:
70             return OSRegex_ConvertMatch(pattern, converted_pattern_ptr);
71             break;
72         default:
73             return (0);
74     }
75 }
76
77 int OSRegex_ConvertRegex(const char *pattern, char **converted_pattern_ptr)
78 {
79     char *converted_pattern = NULL;
80     size_t converted_pattern_size = 0UL;
81     size_t converted_pattern_offset = 0UL;
82     size_t pattern_offset = 0UL;
83     size_t pattern_size = strlen(pattern);
84     const char *replacement = NULL;
85     size_t replacement_size = 0UL;
86     const size_t map_size = ARRAY_LENGTH(_replacement_map_regex);
87     const replacement_pattern *map = _replacement_map_regex;
88     size_t i;
89     const char *p = NULL;
90     const char *star_ungreedy = "*?";
91     const char *plus_ungreedy = "+?";
92     pcre2_code *preg = NULL;
93     int error = 0;
94     PCRE2_SIZE erroroffset = 0;
95     PCRE2_UCHAR *final_converted_pattern = NULL;
96     PCRE2_SIZE final_converted_pattern_len = 0;
97
98     for (pattern_offset = 0UL; pattern_offset < pattern_size; pattern_offset++) {
99         p = &pattern[pattern_offset];
100         replacement = NULL;
101         replacement_size = 0UL;
102
103         if (pattern_offset >= 2 && pattern[pattern_offset - 2] == '\\') {
104             switch (p[0]) {
105                 case '*':
106                     replacement = star_ungreedy;
107                     replacement_size = 2UL;
108                     break;
109                 case '+':
110                     replacement = plus_ungreedy;
111                     replacement_size = 2UL;
112                     break;
113                 default:
114                     break;
115             }
116         }
117
118         if (!replacement) {
119             for (i = 0; i < map_size; i++) {
120                 if (map[i].old_sz + pattern_offset <= pattern_size &&
121                     strncmp(map[i].old, p, map[i].old_sz) == 0) {
122                     replacement = map[i].new;
123                     replacement_size = map[i].new_sz;
124                     pattern_offset += map[i].old_sz - 1;
125                     break;
126                 }
127             }
128         }
129
130         if (!replacement) {
131             /* If it is a special class for ossec */
132             if (p[0] == '\\' && pattern_offset + 1 < pattern_size) {
133                 switch (p[1]) {
134                     case 't':
135                     case '$':
136                     case '(':
137                     case ')':
138                     case '{':
139                     case '}':
140                     case '[':
141                     case ']':
142                     case '\\':
143                     case '|':
144                     case '<':
145                         /* case '>': Only the '<' can be escaped, possibly to not interfer
146                          * with XML parsing if nothing to backslash we copy the two current
147                          * input pattern characters and move the cursor to the next next char
148                          */
149                         replacement = p;
150                         pattern_offset++;
151                         replacement_size = 2UL;
152                         break;
153                     default:
154                         goto conversion_error;
155                 }
156             } else {
157                 replacement = p;
158                 replacement_size = 1UL;
159             }
160         }
161
162         if (converted_pattern_offset + replacement_size >= converted_pattern_size) {
163             converted_pattern_size += OS_PATTERN_MAXSIZE;
164             converted_pattern = (char *)realloc(converted_pattern, converted_pattern_size);
165             if (!converted_pattern) {
166                 return (0);
167             }
168         }
169
170         converted_pattern_offset += sprintf(&converted_pattern[converted_pattern_offset], "%.*s",
171                                             (int)replacement_size, replacement);
172     }
173
174     /*
175      * We should remove the '?' for non-greediness when it is the last one => m/([+*])\?(\)*[|]?)$/
176      * Because Ossec is only greedy with the last occurencies modifier
177      */
178     preg = pcre2_compile((PCRE2_SPTR)OSREGEX_TO_PCRE2_FIX, PCRE2_ZERO_TERMINATED, PCRE2_EXTENDED,
179                          &error, &erroroffset, NULL);
180     if (preg == NULL) {
181         goto conversion_error;
182     }
183     final_converted_pattern_len = converted_pattern_size;
184     final_converted_pattern = malloc(final_converted_pattern_len);
185     if (pcre2_substitute(preg, (PCRE2_SPTR)converted_pattern, converted_pattern_offset, 0,
186                          PCRE2_SUBSTITUTE_GLOBAL, NULL, NULL, (PCRE2_SPTR) "$1$2", 4,
187                          final_converted_pattern, &final_converted_pattern_len) > 0) {
188         free(converted_pattern);
189         *converted_pattern_ptr = (char *)final_converted_pattern;
190     } else {
191         free(final_converted_pattern);
192         *converted_pattern_ptr = converted_pattern;
193     }
194     pcre2_code_free(preg);
195
196     return (1);
197
198 conversion_error:
199     if (converted_pattern) {
200         free(converted_pattern);
201     }
202
203     if (preg) {
204         pcre2_code_free(preg);
205     }
206
207     return (0);
208 }
209
210 int OSRegex_ConvertMatch(const char *pattern, char **converted_pattern_ptr)
211 {
212     char *converted_pattern = NULL;
213     size_t converted_pattern_size = 0UL;
214     size_t converted_pattern_offset = 0UL;
215     size_t pattern_offset = 0UL;
216     size_t pattern_size = strlen(pattern);
217     const char *replacement = NULL;
218     size_t replacement_size = 0UL;
219     const size_t map_size = ARRAY_LENGTH(_replacement_map_match);
220     const replacement_pattern *map = _replacement_map_match;
221     size_t i;
222     const char *p = NULL;
223
224     for (pattern_offset = 0UL; pattern_offset < pattern_size; pattern_offset++) {
225         p = &pattern[pattern_offset];
226         replacement = NULL;
227         replacement_size = 0UL;
228         for (i = 0; i < map_size; i++) {
229             if (map[i].old_sz + pattern_offset <= pattern_size &&
230                 strncmp(map[i].old, p, map[i].old_sz) == 0) {
231                 replacement = map[i].new;
232                 replacement_size = map[i].new_sz;
233                 pattern_offset += map[i].old_sz - 1;
234                 break;
235             }
236         }
237         if (!replacement) {
238             replacement = p;
239             replacement_size = 1UL;
240         }
241
242         if (converted_pattern_offset + replacement_size >= converted_pattern_size) {
243             converted_pattern_size += OS_PATTERN_MAXSIZE;
244             converted_pattern = (char *)realloc(converted_pattern, converted_pattern_size);
245             if (!converted_pattern) {
246                 return (0);
247             }
248         }
249
250         converted_pattern_offset += sprintf(&converted_pattern[converted_pattern_offset], "%.*s",
251                                             (int)replacement_size, replacement);
252     }
253
254     *converted_pattern_ptr = converted_pattern;
255
256     return (1);
257 }