1 /* Copyright 2012 The ChromiumOS Authors
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6 #define _GNU_SOURCE
7
8 #include "util.h"
9
10 #include <ctype.h>
11 #include <errno.h>
12 #include <limits.h>
13 #include <stdarg.h>
14 #include <stdbool.h>
15 #include <stdint.h>
16 #include <stdio.h>
17 #include <string.h>
18
19 #include "libconstants.h"
20 #include "libsyscalls.h"
21
22 /*
23 * These are syscalls used by the syslog() C library call. You can find them
24 * by running a simple test program. See below for x86_64 behavior:
25 * $ cat test.c
26 * #include <syslog.h>
27 * main() { syslog(0, "foo"); }
28 * $ gcc test.c -static
29 * $ strace ./a.out
30 * ...
31 * socket(PF_FILE, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 3 <- look for socket connection
32 * connect(...) <- important
33 * sendto(...) <- important
34 * exit_group(0) <- finish!
35 */
36 const char *const log_syscalls[] = {
37 #if defined(__x86_64__)
38 #if defined(__ANDROID__)
39 "socket",
40 "connect",
41 "fcntl",
42 "writev",
43 #else
44 "socket",
45 "connect",
46 "sendto",
47 "writev",
48 #endif
49 #elif defined(__i386__)
50 #if defined(__ANDROID__)
51 "socketcall",
52 "writev",
53 "fcntl64",
54 "clock_gettime",
55 #else
56 "socketcall",
57 "time",
58 "writev",
59 #endif
60 #elif defined(__arm__)
61 #if defined(__ANDROID__)
62 "clock_gettime", "connect", "fcntl64", "socket", "writev",
63 #else
64 "socket", "connect", "gettimeofday", "send", "writev",
65 #endif
66 #elif defined(__aarch64__)
67 #if defined(__ANDROID__)
68 "connect", "fcntl", "sendto", "socket", "writev",
69 #else
70 "socket",
71 "connect",
72 "send",
73 "writev",
74 #endif
75 #elif defined(__hppa__) || defined(__ia64__) || defined(__mips__) || \
76 defined(__powerpc__) || defined(__sparc__)
77 "socket",
78 "connect",
79 "send",
80 #elif defined(__riscv)
81 #if defined(__ANDROID__)
82 "connect", "fcntl", "sendto", "socket", "writev",
83 #else
84 "socket",
85 "connect",
86 "sendto",
87 #endif
88 #else
89 #error "Unsupported platform"
90 #endif
91 };
92
93 const size_t log_syscalls_len = ARRAY_SIZE(log_syscalls);
94
95 /*
96 * These syscalls are globally allowed. ChromeOS devs: Do **not** add to this
97 * list without approval from the security team.
98 *
99 * This list should be made empty (and mostly remain so) after a better
100 * mechanism is implemented: b/393353891
101 */
102 const char *const libc_compatibility_syscalls[] = {
103 "fstat",
104 #if defined(__arm__)
105 "fstat64",
106 #endif
107 };
108
109 const size_t libc_compatibility_syscalls_len =
110 ARRAY_SIZE(libc_compatibility_syscalls);
111
112 /* clang-format off */
113 static struct logging_config_t {
114 /* The logging system to use. The default is syslog. */
115 enum logging_system_t logger;
116
117 /* File descriptor to log to. Only used when logger is LOG_TO_FD. */
118 int fd;
119
120 /* Minimum priority to log. Only used when logger is LOG_TO_FD. */
121 int min_priority;
122 } logging_config = {
123 .logger = LOG_TO_SYSLOG,
124 };
125 /* clang-format on */
126
127 #if defined(USE_EXIT_ON_DIE)
128 #define do_abort() exit(1)
129 #else
130 #define do_abort() abort()
131 #endif
132
133 #if defined(__clang__)
134 #define attribute_no_optimize __attribute__((optnone))
135 #else
136 #define attribute_no_optimize __attribute__((__optimize__(0)))
137 #endif
138
139 /* Forces the compiler to perform no optimizations on |var|. */
alias(const void * var)140 static void attribute_no_optimize alias(const void *var)
141 {
142 (void)var;
143 }
144
do_fatal_log(int priority,const char * format,...)145 void do_fatal_log(int priority, const char *format, ...)
146 {
147 va_list args, stack_args;
148 va_start(args, format);
149 va_copy(stack_args, args);
150 if (logging_config.logger == LOG_TO_SYSLOG) {
151 vsyslog(priority, format, args);
152 } else {
153 vdprintf(logging_config.fd, format, args);
154 dprintf(logging_config.fd, "\n");
155 }
156 va_end(args);
157
158 /*
159 * Write another copy of the first few characters of the message into a
160 * stack-based buffer so that it can appear in minidumps. Choosing a
161 * small-ish buffer size since breakpad will only pick up the first few
162 * kilobytes of each stack, so that will prevent this buffer from
163 * kicking out other stack frames.
164 */
165 char log_line[512];
166 vsnprintf(log_line, sizeof(log_line), format, stack_args);
167 va_end(stack_args);
168 alias(log_line);
169 do_abort();
170 }
171
do_log(int priority,const char * format,...)172 void do_log(int priority, const char *format, ...)
173 {
174 if (logging_config.logger == LOG_TO_SYSLOG) {
175 va_list args;
176 va_start(args, format);
177 vsyslog(priority, format, args);
178 va_end(args);
179 return;
180 }
181
182 if (logging_config.min_priority < priority)
183 return;
184
185 va_list args;
186 va_start(args, format);
187 vdprintf(logging_config.fd, format, args);
188 va_end(args);
189 dprintf(logging_config.fd, "\n");
190 }
191
192 /*
193 * Returns the syscall nr and optionally populates the index in the pointer
194 * |ind| if it is non-NULL.
195 */
lookup_syscall(const char * name,size_t * ind)196 int lookup_syscall(const char *name, size_t *ind)
197 {
198 size_t ind_tmp = 0;
199 const struct syscall_entry *entry = syscall_table;
200 for (; entry->name && entry->nr >= 0; ++entry) {
201 if (streq(entry->name, name)) {
202 if (ind != NULL)
203 *ind = ind_tmp;
204 return entry->nr;
205 }
206 ind_tmp++;
207 }
208 if (ind != NULL)
209 *ind = -1;
210 return -1;
211 }
212
lookup_syscall_name(long nr)213 const char *lookup_syscall_name(long nr)
214 {
215 const struct syscall_entry *entry = syscall_table;
216 for (; entry->name && entry->nr >= 0; ++entry)
217 if (entry->nr == nr)
218 return entry->name;
219 return NULL;
220 }
221
parse_single_constant(char * constant_str,char ** endptr)222 long int parse_single_constant(char *constant_str, char **endptr)
223 {
224 const struct constant_entry *entry = constant_table;
225 long int res = 0;
226 for (; entry->name; ++entry) {
227 if (streq(entry->name, constant_str)) {
228 *endptr = constant_str + strlen(constant_str);
229 return entry->value;
230 }
231 }
232
233 errno = 0;
234 res = strtol(constant_str, endptr, 0);
235 if (errno == ERANGE) {
236 if (res == LONG_MAX) {
237 /* See if the constant fits in an unsigned long int. */
238 errno = 0;
239 res = strtoul(constant_str, endptr, 0);
240 if (errno == ERANGE) {
241 /*
242 * On unsigned overflow, use the same convention
243 * as when strtol(3) finds no digits: set
244 * |*endptr| to |constant_str| and return 0.
245 */
246 warn("unsigned overflow: '%s'", constant_str);
247 *endptr = constant_str;
248 return 0;
249 }
250 } else if (res == LONG_MIN) {
251 /*
252 * Same for signed underflow: set |*endptr| to
253 * |constant_str| and return 0.
254 */
255 warn("signed underflow: '%s'", constant_str);
256 *endptr = constant_str;
257 return 0;
258 }
259 }
260 if (**endptr != '\0') {
261 warn("trailing garbage after constant: '%s'", constant_str);
262 *endptr = constant_str;
263 return 0;
264 }
265 return res;
266 }
267
tokenize_parenthesized_expression(char ** stringp)268 static char *tokenize_parenthesized_expression(char **stringp)
269 {
270 char *ret = NULL, *found = NULL;
271 size_t paren_count = 1;
272
273 /* If the string is NULL, there are no parens to be found. */
274 if (stringp == NULL || *stringp == NULL)
275 return NULL;
276
277 /* If the string is not on an open paren, the results are undefined. */
278 if (**stringp != '(')
279 return NULL;
280
281 for (found = *stringp + 1; *found; ++found) {
282 switch (*found) {
283 case '(':
284 ++paren_count;
285 break;
286 case ')':
287 --paren_count;
288 if (!paren_count) {
289 *found = '\0';
290 ret = *stringp + 1;
291 *stringp = found + 1;
292 return ret;
293 }
294 break;
295 }
296 }
297
298 /* We got to the end without finding the closing paren. */
299 warn("unclosed parenthesis: '%s'", *stringp);
300 return NULL;
301 }
302
parse_constant(char * constant_str_nonnull,char ** endptr)303 long int parse_constant(char *constant_str_nonnull, char **endptr)
304 {
305 long int value = 0, current_value;
306 /*
307 * The function API says both inputs have to be non-NULL. The code
308 * happens to handle NULL pointers because it resuses the input pointer
309 * as it tokenizes/walks it until the tokenize functions sets it to
310 * NULL. But because of the attributes on the function arguments, the
311 * compiler incorrectly assumes the variable can't become NULL in here,
312 * so we have to create another variable to effectively cast it away.
313 */
314 char *constant_str = constant_str_nonnull;
315 char *group, *lastpos = constant_str;
316
317 /*
318 * If |endptr| is provided, parsing errors are signaled as |endptr|
319 * pointing to |constant_str|.
320 */
321 if (endptr)
322 *endptr = constant_str;
323
324 /*
325 * Try to parse constant expressions. Valid constant expressions are:
326 *
327 * - A number that can be parsed with strtol(3).
328 * - A named constant expression.
329 * - A parenthesized, valid constant expression.
330 * - A valid constant expression prefixed with the unary bitwise
331 * complement operator ~.
332 * - A series of valid constant expressions separated by pipes. Note
333 * that since |constant_str| is an atom, there can be no spaces
334 * between the constant and the pipe.
335 *
336 * If there is an error parsing any of the constants, the whole process
337 * fails.
338 */
339 while (constant_str && *constant_str) {
340 bool negate = false;
341 if (*constant_str == '~') {
342 negate = true;
343 ++constant_str;
344 }
345 if (*constant_str == '(') {
346 group =
347 tokenize_parenthesized_expression(&constant_str);
348 if (group == NULL)
349 return 0;
350 char *end = group;
351 /* Recursively parse the parenthesized subexpression. */
352 current_value = parse_constant(group, &end);
353 if (end == group)
354 return 0;
355 if (constant_str && *constant_str) {
356 /*
357 * If this is not the end of the atom, there
358 * should be another | followed by more stuff.
359 */
360 if (*constant_str != '|') {
361 warn("unterminated constant "
362 "expression: '%s'",
363 constant_str);
364 return 0;
365 }
366 ++constant_str;
367 if (*constant_str == '\0') {
368 warn("unterminated constant "
369 "expression: '%s'",
370 constant_str);
371 return 0;
372 }
373 }
374 lastpos = end;
375 } else {
376 group = tokenize(&constant_str, "|");
377 char *end = group;
378 current_value = parse_single_constant(group, &end);
379 if (end == group)
380 return 0;
381 lastpos = end;
382 }
383 if (negate)
384 current_value = ~current_value;
385 value |= current_value;
386 }
387 if (endptr)
388 *endptr = lastpos;
389 return value;
390 }
391
parse_size(uint64_t * result,const char * sizespec)392 int parse_size(uint64_t *result, const char *sizespec)
393 {
394 uint64_t size;
395 unsigned long long parsed;
396 char *end;
397
398 /* strtoull supports leading whitespace, -, and + signs. */
399 if (sizespec[0] < '0' || sizespec[0] > '9')
400 return -EINVAL;
401
402 /* Clear+check errno so we handle ULLONG_MAX correctly. */
403 errno = 0;
404 parsed = strtoull(sizespec, &end, 10);
405 if (errno)
406 return -errno;
407 size = parsed;
408
409 /* See if there's a suffix. */
410 if (*end != '\0') {
411 static const char suffixes[] = "KMGTPE";
412 size_t i;
413
414 /* Only allow 1 suffix. */
415 if (end[1] != '\0')
416 return -EINVAL;
417
418 for (i = 0; i < sizeof(suffixes) - 1; ++i) {
419 if (*end == suffixes[i]) {
420 /* Make sure we don't overflow. */
421 const int scale = (i + 1) * 10;
422 uint64_t mask =
423 ~((UINT64_C(1) << (64 - scale)) - 1);
424 if (size & mask)
425 return -ERANGE;
426 size <<= scale;
427 break;
428 }
429 }
430
431 /* Unknown suffix. */
432 if (i == sizeof(suffixes) - 1)
433 return -EINVAL;
434 }
435
436 *result = size;
437 return 0;
438 }
439
strip(char * s)440 char *strip(char *s)
441 {
442 char *end;
443 while (*s && isblank(*s))
444 s++;
445 end = s + strlen(s) - 1;
446 while (end >= s && *end && (isblank(*end) || *end == '\n'))
447 end--;
448 *(end + 1) = '\0';
449 return s;
450 }
451
tokenize(char ** stringp,const char * delim)452 char *tokenize(char **stringp, const char *delim)
453 {
454 char *ret = NULL;
455
456 /* If the string is NULL, there are no tokens to be found. */
457 if (stringp == NULL || *stringp == NULL)
458 return NULL;
459
460 /*
461 * If the delimiter is NULL or empty,
462 * the full string makes up the only token.
463 */
464 if (delim == NULL || *delim == '\0') {
465 ret = *stringp;
466 *stringp = NULL;
467 return ret;
468 }
469
470 char *found = strstr(*stringp, delim);
471 if (!found) {
472 /*
473 * The delimiter was not found, so the full string
474 * makes up the only token, and we're done.
475 */
476 ret = *stringp;
477 *stringp = NULL;
478 } else {
479 /* There's a token here, possibly empty. That's OK. */
480 *found = '\0';
481 ret = *stringp;
482 *stringp = found + strlen(delim);
483 }
484
485 return ret;
486 }
487
path_join(const char * external_path,const char * internal_path)488 char *path_join(const char *external_path, const char *internal_path)
489 {
490 char *path = NULL;
491 return asprintf(&path, "%s/%s", external_path, internal_path) < 0
492 ? NULL
493 : path;
494 }
495
path_is_parent(const char * parent,const char * child)496 bool path_is_parent(const char *parent, const char *child)
497 {
498 /*
499 * -Make sure |child| starts with |parent|.
500 * -Make sure that if |child| is longer than |parent|, either:
501 * --the last character in |parent| is a path separator, or
502 * --the character immediately following |parent| in |child| is a path
503 * separator.
504 */
505 size_t parent_len = strlen(parent);
506 return strncmp(parent, child, parent_len) == 0 &&
507 (strlen(child) > parent_len ? (parent[parent_len - 1] == '/' ||
508 child[parent_len] == '/')
509 : false);
510 }
511
consumebytes(size_t length,char ** buf,size_t * buflength)512 void *consumebytes(size_t length, char **buf, size_t *buflength)
513 {
514 char *p = *buf;
515 if (length > *buflength)
516 return NULL;
517 *buf += length;
518 *buflength -= length;
519 return p;
520 }
521
consumestr(char ** buf,size_t * buflength)522 char *consumestr(char **buf, size_t *buflength)
523 {
524 size_t len = strnlen(*buf, *buflength);
525 if (len == *buflength)
526 /* There's no null-terminator. */
527 return NULL;
528 return consumebytes(len + 1, buf, buflength);
529 }
530
init_logging(enum logging_system_t logger,int fd,int min_priority)531 void init_logging(enum logging_system_t logger, int fd, int min_priority)
532 {
533 logging_config.logger = logger;
534 logging_config.fd = fd;
535 logging_config.min_priority = min_priority;
536 }
537
minijail_free_env(char ** env)538 void minijail_free_env(char **env)
539 {
540 if (!env)
541 return;
542
543 for (char **entry = env; *entry; ++entry) {
544 free(*entry);
545 }
546
547 free(env);
548 }
549
minijail_copy_env(char * const * env)550 char **minijail_copy_env(char *const *env)
551 {
552 if (!env)
553 return calloc(1, sizeof(char *));
554
555 int len = 0;
556 while (env[len])
557 ++len;
558
559 char **copy = calloc(len + 1, sizeof(char *));
560 if (!copy)
561 return NULL;
562
563 for (char **entry = copy; *env; ++env, ++entry) {
564 *entry = strdup(*env);
565 if (!*entry) {
566 minijail_free_env(copy);
567 return NULL;
568 }
569 }
570
571 return copy;
572 }
573
574 /*
575 * Utility function used by minijail_setenv, minijail_unsetenv and
576 * minijail_getenv, returns true if |name| is found, false if not.
577 * If found, |*i| is |name|'s index. If not, |*i| is the length of |envp|.
578 */
getenv_index(char ** envp,const char * name,int * i)579 static bool getenv_index(char **envp, const char *name, int *i)
580 {
581 if (!envp || !name || !i)
582 return false;
583
584 size_t name_len = strlen(name);
585 for (*i = 0; envp[*i]; ++(*i)) {
586 /*
587 * If we find a match the size of |name|, we must check
588 * that the next character is a '=', indicating that
589 * the full varname of envp[i] is exactly |name| and
590 * not just happening to start with |name|.
591 */
592 if (!strncmp(envp[*i], name, name_len) &&
593 (envp[*i][name_len] == '=')) {
594 return true;
595 }
596 }
597 /* No match found, |*i| contains the number of elements in |envp|. */
598 return false;
599 }
600
minijail_setenv(char *** env,const char * name,const char * value,int overwrite)601 int minijail_setenv(char ***env, const char *name, const char *value,
602 int overwrite)
603 {
604 if (!env || !*env || !name || !*name || !value)
605 return EINVAL;
606
607 char **dest = NULL;
608 int i;
609
610 /* Look in env to check if this var name already exists. */
611 if (getenv_index(*env, name, &i)) {
612 if (!overwrite)
613 return 0;
614 dest = &(*env)[i];
615 }
616
617 char *new_entry = NULL;
618 if (asprintf(&new_entry, "%s=%s", name, value) == -1)
619 return ENOMEM;
620
621 if (dest) {
622 free(*dest);
623 *dest = new_entry;
624 return 0;
625 }
626
627 /* getenv_index has set |i| to the length of |env|. */
628 ++i;
629 char **new_env = realloc(*env, (i + 1) * sizeof(char *));
630 if (!new_env) {
631 free(new_entry);
632 return ENOMEM;
633 }
634
635 new_env[i - 1] = new_entry;
636 new_env[i] = NULL;
637 *env = new_env;
638 return 0;
639 }
640
641 /*
642 * This is like getline() but supports line wrapping with \.
643 */
getmultiline(char ** lineptr,size_t * n,FILE * stream)644 ssize_t getmultiline(char **lineptr, size_t *n, FILE *stream)
645 {
646 ssize_t ret = getline(lineptr, n, stream);
647 if (ret < 0)
648 return ret;
649
650 char *line = *lineptr;
651 /* Eat the newline to make processing below easier. */
652 if (ret > 0 && line[ret - 1] == '\n')
653 line[--ret] = '\0';
654
655 /* If the line doesn't end in a backslash, we're done. */
656 if (ret <= 0 || line[ret - 1] != '\\')
657 return ret;
658
659 /* This line ends in a backslash. Get the nextline. */
660 line[--ret] = '\0';
661 size_t next_n = 0;
662 attribute_cleanup_str char *next_line = NULL;
663 ssize_t next_ret = getmultiline(&next_line, &next_n, stream);
664 if (next_ret == -1) {
665 /* We couldn't fully read the line, so return an error. */
666 return -1;
667 }
668
669 /* Merge the lines. */
670 *n = ret + next_ret + 2;
671 line = realloc(line, *n);
672 if (!line)
673 return -1;
674 line[ret] = ' ';
675 memcpy(&line[ret + 1], next_line, next_ret + 1);
676 *lineptr = line;
677 return *n - 1;
678 }
679
minijail_getenv(char ** envp,const char * name)680 char *minijail_getenv(char **envp, const char *name)
681 {
682 if (!envp || !name)
683 return NULL;
684
685 int i;
686 if (!getenv_index(envp, name, &i))
687 return NULL;
688
689 /* Return a ptr to the value after the '='. */
690 return envp[i] + strlen(name) + 1;
691 }
692
minijail_unsetenv(char ** envp,const char * name)693 bool minijail_unsetenv(char **envp, const char *name)
694 {
695 if (!envp || !name)
696 return false;
697
698 int i;
699 if (!getenv_index(envp, name, &i))
700 return false;
701
702 /* We found a match, replace it by the last entry of the array. */
703 int last;
704 for (last = i; envp[last]; ++last)
705 continue;
706 --last;
707 envp[i] = envp[last];
708 envp[last] = NULL;
709
710 return true;
711 }
712