blob: 54f240267dd6fd8ee53683f41e5d8961f105870f [file] [log] [blame]
Janis Danisevskis50f09102016-09-15 17:14:33 +01001#include <assert.h>
Tom Cherry00548bd2017-07-26 13:02:55 -07002#include <pthread.h>
Janis Danisevskis50f09102016-09-15 17:14:33 +01003#include <stdint.h>
4#include <stdio.h>
5#include <string.h>
6
7#include "regex.h"
8#include "label_file.h"
Tom Cherry00548bd2017-07-26 13:02:55 -07009#include "selinux_internal.h"
Janis Danisevskis50f09102016-09-15 17:14:33 +010010
11#ifdef USE_PCRE2
Janis Danisevskis3b68c6f2016-09-28 11:28:30 +010012#define REGEX_ARCH_SIZE_T PCRE2_SIZE
13#else
14#define REGEX_ARCH_SIZE_T size_t
15#endif
16
17#ifndef __BYTE_ORDER__
Thomas Petazzoni05abcb12017-01-25 22:44:16 +130018
19/* If the compiler doesn't define __BYTE_ORDER__, try to use the C
20 * library <endian.h> header definitions. */
21#include <endian.h>
22#ifndef __BYTE_ORDER
23#error Neither __BYTE_ORDER__ nor __BYTE_ORDER defined. Unable to determine endianness.
24#endif
25
26#define __ORDER_LITTLE_ENDIAN __LITTLE_ENDIAN
27#define __ORDER_BIG_ENDIAN __BIG_ENDIAN
28#define __BYTE_ORDER__ __BYTE_ORDER
29
Janis Danisevskis3b68c6f2016-09-28 11:28:30 +010030#endif
31
32#ifdef USE_PCRE2
Carlo Marcelo Arenas Belón72806f32023-02-02 13:04:17 +090033static pthread_key_t match_data_key;
34static int match_data_key_initialized = -1;
35static pthread_mutex_t key_mutex = PTHREAD_MUTEX_INITIALIZER;
36static __thread char match_data_initialized;
37
Janis Danisevskis3b68c6f2016-09-28 11:28:30 +010038char const *regex_arch_string(void)
39{
40 static char arch_string_buffer[32];
41 static char const *arch_string = "";
42 char const *endianness = NULL;
43 int rc;
44
45 if (arch_string[0] == '\0') {
46 if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
47 endianness = "el";
48 else if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
49 endianness = "eb";
50
51 if (!endianness)
52 return NULL;
53
54 rc = snprintf(arch_string_buffer, sizeof(arch_string_buffer),
55 "%zu-%zu-%s", sizeof(void *),
56 sizeof(REGEX_ARCH_SIZE_T),
57 endianness);
58 if (rc < 0)
59 abort();
60
61 arch_string = &arch_string_buffer[0];
62 }
63 return arch_string;
64}
65
Janis Danisevskis50f09102016-09-15 17:14:33 +010066struct regex_data {
67 pcre2_code *regex; /* compiled regular expression */
Janis Danisevskis50f09102016-09-15 17:14:33 +010068};
69
70int regex_prepare_data(struct regex_data **regex, char const *pattern_string,
71 struct regex_error_data *errordata)
72{
73 memset(errordata, 0, sizeof(struct regex_error_data));
74
75 *regex = regex_data_create();
76 if (!(*regex))
77 return -1;
78
79 (*regex)->regex = pcre2_compile(
80 (PCRE2_SPTR)pattern_string, PCRE2_ZERO_TERMINATED, PCRE2_DOTALL,
81 &errordata->error_code, &errordata->error_offset, NULL);
82 if (!(*regex)->regex) {
83 goto err;
84 }
85
Janis Danisevskis50f09102016-09-15 17:14:33 +010086 return 0;
87
88err:
89 regex_data_free(*regex);
90 *regex = NULL;
91 return -1;
92}
93
94char const *regex_version(void)
95{
96 static char version_buf[256];
97 size_t len = pcre2_config(PCRE2_CONFIG_VERSION, NULL);
98 if (len <= 0 || len > sizeof(version_buf))
99 return NULL;
100
101 pcre2_config(PCRE2_CONFIG_VERSION, version_buf);
102 return version_buf;
103}
104
Janis Danisevskis3b68c6f2016-09-28 11:28:30 +0100105int regex_load_mmap(struct mmap_area *mmap_area, struct regex_data **regex,
Tom Cherry00548bd2017-07-26 13:02:55 -0700106 int do_load_precompregex, bool *regex_compiled)
Janis Danisevskis50f09102016-09-15 17:14:33 +0100107{
108 int rc;
109 uint32_t entry_len;
110
Tom Cherry00548bd2017-07-26 13:02:55 -0700111 *regex_compiled = false;
Janis Danisevskis50f09102016-09-15 17:14:33 +0100112 rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
113 if (rc < 0)
114 return -1;
115
Janis Danisevskis3b68c6f2016-09-28 11:28:30 +0100116 if (entry_len && do_load_precompregex) {
Janis Danisevskis50f09102016-09-15 17:14:33 +0100117 /*
118 * this should yield exactly one because we store one pattern at
119 * a time
120 */
121 rc = pcre2_serialize_get_number_of_codes(mmap_area->next_addr);
122 if (rc != 1)
123 return -1;
124
125 *regex = regex_data_create();
126 if (!*regex)
127 return -1;
128
129 rc = pcre2_serialize_decode(&(*regex)->regex, 1,
130 (PCRE2_SPTR)mmap_area->next_addr,
131 NULL);
132 if (rc != 1)
133 goto err;
134
Tom Cherry00548bd2017-07-26 13:02:55 -0700135 *regex_compiled = true;
Janis Danisevskis50f09102016-09-15 17:14:33 +0100136 }
137
138 /* and skip the decoded bit */
139 rc = next_entry(NULL, mmap_area, entry_len);
140 if (rc < 0)
141 goto err;
142
143 return 0;
144err:
145 regex_data_free(*regex);
146 *regex = NULL;
147 return -1;
148}
149
150int regex_writef(struct regex_data *regex, FILE *fp, int do_write_precompregex)
151{
152 int rc = 0;
153 size_t len;
154 PCRE2_SIZE serialized_size;
155 uint32_t to_write = 0;
156 PCRE2_UCHAR *bytes = NULL;
157
158 if (do_write_precompregex) {
Christian Göttscheb32e85c2023-01-05 18:13:20 +0100159 /* encode the pattern for serialization */
Janis Danisevskis50f09102016-09-15 17:14:33 +0100160 rc = pcre2_serialize_encode((const pcre2_code **)&regex->regex,
161 1, &bytes, &serialized_size, NULL);
162 if (rc != 1) {
163 rc = -1;
164 goto out;
165 }
166 to_write = serialized_size;
167 }
168
169 /* write serialized pattern's size */
170 len = fwrite(&to_write, sizeof(uint32_t), 1, fp);
171 if (len != 1) {
172 rc = -1;
173 goto out;
174 }
175
176 if (do_write_precompregex) {
177 /* write serialized pattern */
178 len = fwrite(bytes, 1, to_write, fp);
179 if (len != to_write)
180 rc = -1;
181 }
182
183out:
184 if (bytes)
185 pcre2_serialize_free(bytes);
186
187 return rc;
188}
189
Carlo Marcelo Arenas Belón72806f32023-02-02 13:04:17 +0900190static void __attribute__((destructor)) match_data_thread_free(void *key)
191{
192 void *value;
193 pcre2_match_data *match_data;
194
195 if (match_data_key_initialized <= 0 || !match_data_initialized)
196 return;
197
198 value = __selinux_getspecific(match_data_key);
199 match_data = value ? value : key;
200
201 pcre2_match_data_free(match_data);
202
203 __pthread_mutex_lock(&key_mutex);
204 if (--match_data_key_initialized == 1) {
205 __selinux_key_delete(match_data_key);
206 match_data_key_initialized = -1;
207 }
208 __pthread_mutex_unlock(&key_mutex);
209}
210
Janis Danisevskis50f09102016-09-15 17:14:33 +0100211void regex_data_free(struct regex_data *regex)
212{
213 if (regex) {
214 if (regex->regex)
215 pcre2_code_free(regex->regex);
Janis Danisevskis50f09102016-09-15 17:14:33 +0100216 free(regex);
217 }
218}
219
220int regex_match(struct regex_data *regex, char const *subject, int partial)
221{
222 int rc;
Carlo Marcelo Arenas Belón72806f32023-02-02 13:04:17 +0900223 bool slow;
224 pcre2_match_data *match_data = NULL;
Inseob Kim30b3e9d2023-01-12 18:14:09 +0900225
Carlo Marcelo Arenas Belón72806f32023-02-02 13:04:17 +0900226 if (match_data_key_initialized > 0) {
227 if (match_data_initialized == 0) {
228 match_data = pcre2_match_data_create(1, NULL);
229 if (match_data) {
230 match_data_initialized = 1;
231 __selinux_setspecific(match_data_key,
232 match_data);
233 __pthread_mutex_lock(&key_mutex);
234 match_data_key_initialized++;
235 __pthread_mutex_unlock(&key_mutex);
236 }
237 } else
238 match_data = __selinux_getspecific(match_data_key);
Inseob Kim30b3e9d2023-01-12 18:14:09 +0900239 }
Carlo Marcelo Arenas Belón72806f32023-02-02 13:04:17 +0900240
241 slow = (match_data_key_initialized <= 0 || match_data == NULL);
242 if (slow) {
243 match_data = pcre2_match_data_create_from_pattern(regex->regex,
244 NULL);
245 if (!match_data)
246 return REGEX_ERROR;
247 }
Inseob Kim30b3e9d2023-01-12 18:14:09 +0900248
Janis Danisevskis50f09102016-09-15 17:14:33 +0100249 rc = pcre2_match(
250 regex->regex, (PCRE2_SPTR)subject, PCRE2_ZERO_TERMINATED, 0,
Inseob Kim30b3e9d2023-01-12 18:14:09 +0900251 partial ? PCRE2_PARTIAL_SOFT : 0, match_data, NULL);
252
Carlo Marcelo Arenas Belón72806f32023-02-02 13:04:17 +0900253 if (slow)
254 pcre2_match_data_free(match_data);
Inseob Kim30b3e9d2023-01-12 18:14:09 +0900255
Carlo Marcelo Arenas Belón72806f32023-02-02 13:04:17 +0900256 if (rc >= 0)
Janis Danisevskis50f09102016-09-15 17:14:33 +0100257 return REGEX_MATCH;
258 switch (rc) {
259 case PCRE2_ERROR_PARTIAL:
260 return REGEX_MATCH_PARTIAL;
261 case PCRE2_ERROR_NOMATCH:
262 return REGEX_NO_MATCH;
263 default:
264 return REGEX_ERROR;
265 }
266}
267
268/*
269 * TODO Replace this compare function with something that actually compares the
270 * regular expressions.
271 * This compare function basically just compares the binary representations of
272 * the automatons, and because this representation contains pointers and
273 * metadata, it can only return a match if regex1 == regex2.
274 * Preferably, this function would be replaced with an algorithm that computes
275 * the equivalence of the automatons systematically.
276 */
277int regex_cmp(struct regex_data *regex1, struct regex_data *regex2)
278{
279 int rc;
280 size_t len1, len2;
281 rc = pcre2_pattern_info(regex1->regex, PCRE2_INFO_SIZE, &len1);
282 assert(rc == 0);
283 rc = pcre2_pattern_info(regex2->regex, PCRE2_INFO_SIZE, &len2);
284 assert(rc == 0);
285 if (len1 != len2 || memcmp(regex1->regex, regex2->regex, len1))
286 return SELABEL_INCOMPARABLE;
287
288 return SELABEL_EQUAL;
289}
290
Tom Cherry00548bd2017-07-26 13:02:55 -0700291struct regex_data *regex_data_create(void)
292{
293 struct regex_data *regex_data =
294 (struct regex_data *)calloc(1, sizeof(struct regex_data));
Jie Lu4c47f922022-11-29 20:00:20 +0800295 if (!regex_data)
296 return NULL;
297
Carlo Marcelo Arenas Belón72806f32023-02-02 13:04:17 +0900298 __pthread_mutex_lock(&key_mutex);
299 if (match_data_key_initialized < 0) {
300 match_data_key_initialized = !__selinux_key_create(
301 &match_data_key,
302 match_data_thread_free);
303 }
304 __pthread_mutex_unlock(&key_mutex);
305
Tom Cherry00548bd2017-07-26 13:02:55 -0700306 return regex_data;
307}
308
Janis Danisevskis50f09102016-09-15 17:14:33 +0100309#else // !USE_PCRE2
Janis Danisevskis3b68c6f2016-09-28 11:28:30 +0100310char const *regex_arch_string(void)
311{
312 return "N/A";
313}
Janis Danisevskis50f09102016-09-15 17:14:33 +0100314
315/* Prior to version 8.20, libpcre did not have pcre_free_study() */
316#if (PCRE_MAJOR < 8 || (PCRE_MAJOR == 8 && PCRE_MINOR < 20))
317#define pcre_free_study pcre_free
318#endif
319
320struct regex_data {
321 int owned; /*
322 * non zero if regex and pcre_extra is owned by this
323 * structure and thus must be freed on destruction.
324 */
325 pcre *regex; /* compiled regular expression */
326 union {
327 pcre_extra *sd; /* pointer to extra compiled stuff */
328 pcre_extra lsd; /* used to hold the mmap'd version */
329 };
330};
331
332int regex_prepare_data(struct regex_data **regex, char const *pattern_string,
333 struct regex_error_data *errordata)
334{
335 memset(errordata, 0, sizeof(struct regex_error_data));
336
337 *regex = regex_data_create();
338 if (!(*regex))
339 return -1;
340
341 (*regex)->regex =
342 pcre_compile(pattern_string, PCRE_DOTALL, &errordata->error_buffer,
343 &errordata->error_offset, NULL);
344 if (!(*regex)->regex)
345 goto err;
346
347 (*regex)->owned = 1;
348
349 (*regex)->sd = pcre_study((*regex)->regex, 0, &errordata->error_buffer);
350 if (!(*regex)->sd && errordata->error_buffer)
351 goto err;
352
353 return 0;
354
355err:
356 regex_data_free(*regex);
357 *regex = NULL;
358 return -1;
359}
360
361char const *regex_version(void)
362{
363 return pcre_version();
364}
365
Janis Danisevskis3b68c6f2016-09-28 11:28:30 +0100366int regex_load_mmap(struct mmap_area *mmap_area, struct regex_data **regex,
Christian Göttschee0570802021-05-03 17:11:26 +0200367 int do_load_precompregex __attribute__((unused)), bool *regex_compiled)
Janis Danisevskis50f09102016-09-15 17:14:33 +0100368{
369 int rc;
370 uint32_t entry_len;
371 size_t info_len;
372
373 rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
374 if (rc < 0 || !entry_len)
375 return -1;
376
377 *regex = regex_data_create();
378 if (!(*regex))
379 return -1;
380
381 (*regex)->owned = 0;
382 (*regex)->regex = (pcre *)mmap_area->next_addr;
383 rc = next_entry(NULL, mmap_area, entry_len);
384 if (rc < 0)
385 goto err;
386
387 /*
388 * Check that regex lengths match. pcre_fullinfo()
389 * also validates its magic number.
390 */
391 rc = pcre_fullinfo((*regex)->regex, NULL, PCRE_INFO_SIZE, &info_len);
392 if (rc < 0 || info_len != entry_len)
393 goto err;
394
395 rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
Stephen Smalley3f99b142018-10-31 10:10:04 -0400396 if (rc < 0)
Janis Danisevskis50f09102016-09-15 17:14:33 +0100397 goto err;
398
399 if (entry_len) {
400 (*regex)->lsd.study_data = (void *)mmap_area->next_addr;
401 (*regex)->lsd.flags |= PCRE_EXTRA_STUDY_DATA;
402 rc = next_entry(NULL, mmap_area, entry_len);
403 if (rc < 0)
404 goto err;
405
406 /* Check that study data lengths match. */
407 rc = pcre_fullinfo((*regex)->regex, &(*regex)->lsd,
408 PCRE_INFO_STUDYSIZE, &info_len);
409 if (rc < 0 || info_len != entry_len)
410 goto err;
411 }
Tom Cherry00548bd2017-07-26 13:02:55 -0700412
413 *regex_compiled = true;
Janis Danisevskis50f09102016-09-15 17:14:33 +0100414 return 0;
415
416err:
417 regex_data_free(*regex);
418 *regex = NULL;
419 return -1;
420}
421
422static inline pcre_extra *get_pcre_extra(struct regex_data *regex)
423{
424 if (!regex) return NULL;
425 if (regex->owned) {
426 return regex->sd;
427 } else if (regex->lsd.study_data) {
428 return &regex->lsd;
429 } else {
430 return NULL;
431 }
432}
433
Stephen Smalley8e776b02016-09-15 13:43:24 -0400434int regex_writef(struct regex_data *regex, FILE *fp,
Christian Göttschee0570802021-05-03 17:11:26 +0200435 int do_write_precompregex __attribute__((unused)))
Janis Danisevskis50f09102016-09-15 17:14:33 +0100436{
437 int rc;
438 size_t len;
439 uint32_t to_write;
440 size_t size;
441 pcre_extra *sd = get_pcre_extra(regex);
442
443 /* determine the size of the pcre data in bytes */
444 rc = pcre_fullinfo(regex->regex, NULL, PCRE_INFO_SIZE, &size);
445 if (rc < 0)
446 return -1;
447
448 /* write the number of bytes in the pcre data */
449 to_write = size;
450 len = fwrite(&to_write, sizeof(uint32_t), 1, fp);
451 if (len != 1)
452 return -1;
453
454 /* write the actual pcre data as a char array */
455 len = fwrite(regex->regex, 1, to_write, fp);
456 if (len != to_write)
457 return -1;
458
459 if (sd) {
460 /* determine the size of the pcre study info */
461 rc =
462 pcre_fullinfo(regex->regex, sd, PCRE_INFO_STUDYSIZE, &size);
463 if (rc < 0)
464 return -1;
465 } else
466 size = 0;
467
468 /* write the number of bytes in the pcre study data */
469 to_write = size;
470 len = fwrite(&to_write, sizeof(uint32_t), 1, fp);
471 if (len != 1)
472 return -1;
473
474 if (sd) {
475 /* write the actual pcre study data as a char array */
476 len = fwrite(sd->study_data, 1, to_write, fp);
477 if (len != to_write)
478 return -1;
479 }
480
481 return 0;
482}
483
484void regex_data_free(struct regex_data *regex)
485{
486 if (regex) {
487 if (regex->owned) {
488 if (regex->regex)
489 pcre_free(regex->regex);
490 if (regex->sd)
491 pcre_free_study(regex->sd);
492 }
493 free(regex);
494 }
495}
496
497int regex_match(struct regex_data *regex, char const *subject, int partial)
498{
499 int rc;
500
501 rc = pcre_exec(regex->regex, get_pcre_extra(regex),
502 subject, strlen(subject), 0,
503 partial ? PCRE_PARTIAL_SOFT : 0, NULL, 0);
504 switch (rc) {
505 case 0:
506 return REGEX_MATCH;
507 case PCRE_ERROR_PARTIAL:
508 return REGEX_MATCH_PARTIAL;
509 case PCRE_ERROR_NOMATCH:
510 return REGEX_NO_MATCH;
511 default:
512 return REGEX_ERROR;
513 }
514}
515
516/*
517 * TODO Replace this compare function with something that actually compares the
518 * regular expressions.
519 * This compare function basically just compares the binary representations of
520 * the automatons, and because this representation contains pointers and
521 * metadata, it can only return a match if regex1 == regex2.
522 * Preferably, this function would be replaced with an algorithm that computes
523 * the equivalence of the automatons systematically.
524 */
525int regex_cmp(struct regex_data *regex1, struct regex_data *regex2)
526{
527 int rc;
528 size_t len1, len2;
529 rc = pcre_fullinfo(regex1->regex, NULL, PCRE_INFO_SIZE, &len1);
530 assert(rc == 0);
531 rc = pcre_fullinfo(regex2->regex, NULL, PCRE_INFO_SIZE, &len2);
532 assert(rc == 0);
533 if (len1 != len2 || memcmp(regex1->regex, regex2->regex, len1))
534 return SELABEL_INCOMPARABLE;
535
536 return SELABEL_EQUAL;
537}
538
Janis Danisevskis50f09102016-09-15 17:14:33 +0100539struct regex_data *regex_data_create(void)
540{
541 return (struct regex_data *)calloc(1, sizeof(struct regex_data));
542}
543
Tom Cherry00548bd2017-07-26 13:02:55 -0700544#endif
545
Janis Danisevskis50f09102016-09-15 17:14:33 +0100546void regex_format_error(struct regex_error_data const *error_data, char *buffer,
547 size_t buf_size)
548{
549 unsigned the_end_length = buf_size > 4 ? 4 : buf_size;
550 char *ptr = &buffer[buf_size - the_end_length];
551 int rc = 0;
552 size_t pos = 0;
553 if (!buffer || !buf_size)
554 return;
555 rc = snprintf(buffer, buf_size, "REGEX back-end error: ");
556 if (rc < 0)
557 /*
558 * If snprintf fails it constitutes a logical error that needs
559 * fixing.
560 */
561 abort();
562
563 pos += rc;
564 if (pos >= buf_size)
565 goto truncated;
566
Nicolas Iooss340da082019-09-20 07:59:54 +0200567 /* Return early if there is no error to format */
568#ifdef USE_PCRE2
569 if (!error_data->error_code) {
570 rc = snprintf(buffer + pos, buf_size - pos, "no error code");
571 if (rc < 0)
572 abort();
573 pos += rc;
574 if (pos >= buf_size)
575 goto truncated;
576 return;
577 }
578#else
579 if (!error_data->error_buffer) {
580 rc = snprintf(buffer + pos, buf_size - pos, "empty error");
581 if (rc < 0)
582 abort();
583 pos += rc;
584 if (pos >= buf_size)
585 goto truncated;
586 return;
587 }
588#endif
589
Janis Danisevskis50f09102016-09-15 17:14:33 +0100590 if (error_data->error_offset > 0) {
591#ifdef USE_PCRE2
592 rc = snprintf(buffer + pos, buf_size - pos, "At offset %zu: ",
593 error_data->error_offset);
594#else
595 rc = snprintf(buffer + pos, buf_size - pos, "At offset %d: ",
596 error_data->error_offset);
597#endif
598 if (rc < 0)
599 abort();
Nicolas Iooss68b2e442019-09-20 07:59:55 +0200600 pos += rc;
601 if (pos >= buf_size)
602 goto truncated;
Janis Danisevskis50f09102016-09-15 17:14:33 +0100603 }
Janis Danisevskis50f09102016-09-15 17:14:33 +0100604
605#ifdef USE_PCRE2
606 rc = pcre2_get_error_message(error_data->error_code,
607 (PCRE2_UCHAR *)(buffer + pos),
608 buf_size - pos);
609 if (rc == PCRE2_ERROR_NOMEMORY)
610 goto truncated;
611#else
612 rc = snprintf(buffer + pos, buf_size - pos, "%s",
613 error_data->error_buffer);
614 if (rc < 0)
615 abort();
616
617 if ((size_t)rc < strlen(error_data->error_buffer))
618 goto truncated;
619#endif
620
621 return;
622
623truncated:
624 /* replace end of string with "..." to indicate that it was truncated */
625 switch (the_end_length) {
626 /* no break statements, fall-through is intended */
627 case 4:
628 *ptr++ = '.';
Stephen Smalleye41ae672017-05-31 16:14:18 -0400629 /* FALLTHRU */
Janis Danisevskis50f09102016-09-15 17:14:33 +0100630 case 3:
631 *ptr++ = '.';
Stephen Smalleye41ae672017-05-31 16:14:18 -0400632 /* FALLTHRU */
Janis Danisevskis50f09102016-09-15 17:14:33 +0100633 case 2:
634 *ptr++ = '.';
Stephen Smalleye41ae672017-05-31 16:14:18 -0400635 /* FALLTHRU */
Janis Danisevskis50f09102016-09-15 17:14:33 +0100636 case 1:
637 *ptr++ = '\0';
Stephen Smalleye41ae672017-05-31 16:14:18 -0400638 /* FALLTHRU */
Janis Danisevskis50f09102016-09-15 17:14:33 +0100639 default:
640 break;
641 }
642 return;
643}