new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / os_regex / os_pcre2_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 <ctype.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include "os_regex.h"
16
17 const char *OSPcre2_Execute_pcre2_match(const char *str, OSPcre2 *reg);
18 const char *OSPcre2_Execute_strncmp(const char *subject, OSPcre2 *reg);
19 const char *OSPcre2_Execute_strrcmp(const char *subject, OSPcre2 *reg);
20 const char *OSPcre2_Execute_strcasecmp(const char *subject, OSPcre2 *reg);
21 const char *OSPcre2_Execute_strncasecmp(const char *subject, OSPcre2 *reg);
22 const char *OSPcre2_Execute_strrcasecmp(const char *subject, OSPcre2 *reg);
23 const char *OSPcre2_Execute_strcmp(const char *subject, OSPcre2 *reg);
24 int OSPcre2_CouldBeOptimized(const char *pattern);
25
26 int OSPcre2_Compile(const char *pattern, OSPcre2 *reg, int flags)
27 {
28     int error = 0;
29     PCRE2_SIZE erroroffset;
30     size_t pattern_len = 0UL;
31     char first_char, last_char;
32     uint32_t count, i;
33
34     /* Check for references not initialized */
35     if (reg == NULL) {
36         return (0);
37     }
38
39     /* Initialize OSRegex structure */
40     reg->error = 0;
41     reg->sub_strings = NULL;
42     reg->regex = NULL;
43     reg->match_data = NULL;
44     reg->pattern_len = 0UL;
45     reg->pattern = NULL;
46     reg->exec_function = NULL;
47
48     /* The pattern can't be null */
49     if (pattern == NULL) {
50         reg->error = OS_REGEX_PATTERN_NULL;
51         goto compile_error;
52     }
53
54     /* Maximum size of the pattern */
55     pattern_len = strlen(pattern);
56 #if 0
57     if (pattern_len > OS_PATTERN_MAXSIZE) {
58         reg->error = OS_REGEX_MAXSIZE;
59         goto compile_error;
60     }
61 #endif
62
63     if (OSPcre2_CouldBeOptimized(pattern)) {
64         first_char = pattern[0];
65         last_char = pattern[pattern_len - 1];
66
67         if (first_char == '^') {
68             if (last_char == '$') {
69                 reg->pattern = strdup(&pattern[1]);
70                 reg->pattern_len = pattern_len - 2;
71                 reg->pattern[reg->pattern_len] = '\0';
72                 if (flags & PCRE2_CASELESS) {
73                     reg->exec_function = OSPcre2_Execute_strcasecmp;
74                 } else {
75                     reg->exec_function = OSPcre2_Execute_strcmp;
76                 }
77                 return (1);
78             } else {
79                 reg->pattern = strdup(&pattern[1]);
80                 reg->pattern_len = pattern_len - 1;
81                 if (flags & PCRE2_CASELESS) {
82                     reg->exec_function = OSPcre2_Execute_strncasecmp;
83                 } else {
84                     reg->exec_function = OSPcre2_Execute_strncmp;
85                 }
86                 return (1);
87             }
88         } else {
89             if (last_char == '$') {
90                 reg->pattern = strdup(pattern);
91                 reg->pattern_len = pattern_len - 1;
92                 reg->pattern[reg->pattern_len] = '\0';
93                 if (flags & PCRE2_CASELESS) {
94                     reg->exec_function = OSPcre2_Execute_strrcasecmp;
95                 } else {
96                     reg->exec_function = OSPcre2_Execute_strrcmp;
97                 }
98                 return (1);
99             }
100         }
101     }
102
103     reg->exec_function = OSPcre2_Execute_pcre2_match;
104
105     reg->regex = pcre2_compile((PCRE2_SPTR)pattern, pattern_len, flags, &error, &erroroffset, NULL);
106     if (reg->regex == NULL) {
107         reg->error = OS_REGEX_BADREGEX;
108         goto compile_error;
109     }
110
111     reg->match_data = pcre2_match_data_create_from_pattern(reg->regex, NULL);
112     if (reg->match_data == NULL) {
113         reg->error = OS_REGEX_OUTOFMEMORY;
114         goto compile_error;
115     }
116
117     pcre2_pattern_info(reg->regex, PCRE2_INFO_CAPTURECOUNT, (void *)&count);
118     count++; // to store NULL pointer at the end
119     reg->sub_strings = calloc(count, sizeof(char *));
120     if (reg->sub_strings == NULL) {
121         reg->error = OS_REGEX_OUTOFMEMORY;
122         goto compile_error;
123     }
124     for (i = 0; i < count; i++) {
125         reg->sub_strings[i] = NULL;
126     }
127
128 #ifdef USE_PCRE2_JIT
129     /* Just In Time compilation for faster execution */
130     if (pcre2_jit_compile(reg->regex, PCRE2_JIT_COMPLETE) != 0) {
131         reg->error = OS_REGEX_NO_JIT;
132         goto compile_error;
133     }
134 #endif
135
136     return (1);
137
138 compile_error:
139     /* Error handling */
140
141     OSPcre2_FreePattern(reg);
142
143     return (0);
144 }
145
146 int OSPcre2_CouldBeOptimized(const char *pattern)
147 {
148     pcre2_code *preg = NULL;
149     pcre2_match_data *md = NULL;
150     PCRE2_SPTR re = (PCRE2_SPTR) "^\\^?[a-zA-Z0-9 !\"#%&',/:;<=>@_`~-]*\\$?$";
151     int error = 0;
152     PCRE2_SIZE erroroffset = 0;
153     int should_be_optimized = 0;
154
155     preg = pcre2_compile(re, PCRE2_ZERO_TERMINATED, PCRE2_UTF | PCRE2_NO_UTF_CHECK, &error,
156                          &erroroffset, NULL);
157     md = pcre2_match_data_create_from_pattern(preg, NULL);
158
159     if (pcre2_match(preg, (PCRE2_SPTR)pattern, strlen(pattern), 0, 0, md, NULL) >= 0) {
160         should_be_optimized = 1;
161     }
162
163     pcre2_match_data_free(md);
164     pcre2_code_free(preg);
165
166     return should_be_optimized;
167 }