xref: /aosp_15_r20/external/minijail/config_parser.c (revision 4b9c6d91573e8b3a96609339b46361b5476dd0f9)
1*4b9c6d91SCole Faust /* Copyright 2021 The ChromiumOS Authors
2*4b9c6d91SCole Faust  * Use of this source code is governed by a BSD-style license that can be
3*4b9c6d91SCole Faust  * found in the LICENSE file.
4*4b9c6d91SCole Faust  */
5*4b9c6d91SCole Faust 
6*4b9c6d91SCole Faust #include <errno.h>
7*4b9c6d91SCole Faust #include <stdbool.h>
8*4b9c6d91SCole Faust #include <stdio.h>
9*4b9c6d91SCole Faust #include <stdlib.h>
10*4b9c6d91SCole Faust #include <string.h>
11*4b9c6d91SCole Faust 
12*4b9c6d91SCole Faust #include "config_parser.h"
13*4b9c6d91SCole Faust 
14*4b9c6d91SCole Faust #include "util.h"
15*4b9c6d91SCole Faust 
16*4b9c6d91SCole Faust #define LIST_DEFAULT_SIZE (100)
17*4b9c6d91SCole Faust 
new_config_entry_list(void)18*4b9c6d91SCole Faust struct config_entry_list *new_config_entry_list(void)
19*4b9c6d91SCole Faust {
20*4b9c6d91SCole Faust 	/*
21*4b9c6d91SCole Faust 	 * There are <100 CLI options, configuration file will likely have
22*4b9c6d91SCole Faust 	 * a similar number of config entries.
23*4b9c6d91SCole Faust 	 */
24*4b9c6d91SCole Faust 	struct config_entry *entries =
25*4b9c6d91SCole Faust 	    calloc(LIST_DEFAULT_SIZE, sizeof(struct config_entry));
26*4b9c6d91SCole Faust 	if (!entries)
27*4b9c6d91SCole Faust 		return NULL;
28*4b9c6d91SCole Faust 
29*4b9c6d91SCole Faust 	struct config_entry_list *list =
30*4b9c6d91SCole Faust 	    calloc(1, sizeof(struct config_entry_list));
31*4b9c6d91SCole Faust 	if (!list) {
32*4b9c6d91SCole Faust 		free(entries);
33*4b9c6d91SCole Faust 		return NULL;
34*4b9c6d91SCole Faust 	}
35*4b9c6d91SCole Faust 	list->entries = entries;
36*4b9c6d91SCole Faust 	list->num_allocated_ = LIST_DEFAULT_SIZE;
37*4b9c6d91SCole Faust 	list->num_entries = 0;
38*4b9c6d91SCole Faust 	return list;
39*4b9c6d91SCole Faust }
40*4b9c6d91SCole Faust 
clear_config_entry(struct config_entry * entry)41*4b9c6d91SCole Faust void clear_config_entry(struct config_entry *entry)
42*4b9c6d91SCole Faust {
43*4b9c6d91SCole Faust 	free((char *)entry->key);
44*4b9c6d91SCole Faust 	free((char *)entry->value);
45*4b9c6d91SCole Faust }
46*4b9c6d91SCole Faust 
free_config_entry_list(struct config_entry_list * list)47*4b9c6d91SCole Faust void free_config_entry_list(struct config_entry_list *list)
48*4b9c6d91SCole Faust {
49*4b9c6d91SCole Faust 	if (!list)
50*4b9c6d91SCole Faust 		return;
51*4b9c6d91SCole Faust 	for (size_t i = 0; i < list->num_entries; i++) {
52*4b9c6d91SCole Faust 		clear_config_entry(&list->entries[i]);
53*4b9c6d91SCole Faust 	}
54*4b9c6d91SCole Faust 	free(list->entries);
55*4b9c6d91SCole Faust 	free(list);
56*4b9c6d91SCole Faust }
57*4b9c6d91SCole Faust 
parse_config_line(const char * config_line,struct config_entry * entry)58*4b9c6d91SCole Faust bool parse_config_line(const char *config_line, struct config_entry *entry)
59*4b9c6d91SCole Faust {
60*4b9c6d91SCole Faust 	/* Parsing will modify |config_line| in place, so make a copy. */
61*4b9c6d91SCole Faust 	attribute_cleanup_str char *line = strdup(config_line);
62*4b9c6d91SCole Faust 	if (!line)
63*4b9c6d91SCole Faust 		return false;
64*4b9c6d91SCole Faust 	char *value = line;
65*4b9c6d91SCole Faust 
66*4b9c6d91SCole Faust 	/* After tokenize call, |value| will point to a substring after '='.
67*4b9c6d91SCole Faust 	 * If there is no '=' in the string, |key| will contain the entire
68*4b9c6d91SCole Faust 	 * string while |value| will be NULL.
69*4b9c6d91SCole Faust 	 */
70*4b9c6d91SCole Faust 	char *key = tokenize(&value, "=");
71*4b9c6d91SCole Faust 	if (key)
72*4b9c6d91SCole Faust 		key = strip(key);
73*4b9c6d91SCole Faust 	if (value)
74*4b9c6d91SCole Faust 		value = strip(value);
75*4b9c6d91SCole Faust 	if (!key || key[0] == '\0' || (value && value[0] == '\0')) {
76*4b9c6d91SCole Faust 		warn("unable to parse %s", config_line);
77*4b9c6d91SCole Faust 		return false;
78*4b9c6d91SCole Faust 	}
79*4b9c6d91SCole Faust 	entry->key = strdup(key);
80*4b9c6d91SCole Faust 	entry->value = value ? strdup(value) : NULL;
81*4b9c6d91SCole Faust 	if (!entry->key || (value && !entry->value)) {
82*4b9c6d91SCole Faust 		clear_config_entry(entry);
83*4b9c6d91SCole Faust 		return false;
84*4b9c6d91SCole Faust 	}
85*4b9c6d91SCole Faust 	return true;
86*4b9c6d91SCole Faust }
87*4b9c6d91SCole Faust 
match_special_directive(const char * line)88*4b9c6d91SCole Faust static bool match_special_directive(const char *line)
89*4b9c6d91SCole Faust {
90*4b9c6d91SCole Faust 	return streq(line, "% minijail-config-file v0\n");
91*4b9c6d91SCole Faust }
92*4b9c6d91SCole Faust 
parse_config_file(FILE * config_file,struct config_entry_list * list)93*4b9c6d91SCole Faust bool parse_config_file(FILE *config_file, struct config_entry_list *list)
94*4b9c6d91SCole Faust {
95*4b9c6d91SCole Faust 	attribute_cleanup_str char *line = NULL;
96*4b9c6d91SCole Faust 	size_t len = 0;
97*4b9c6d91SCole Faust 
98*4b9c6d91SCole Faust 	/* The first line must match the special directive */
99*4b9c6d91SCole Faust 	if (getline(&line, &len, config_file) == -1 ||
100*4b9c6d91SCole Faust 	    !match_special_directive(line))
101*4b9c6d91SCole Faust 		return false;
102*4b9c6d91SCole Faust 	while (getmultiline(&line, &len, config_file) != -1) {
103*4b9c6d91SCole Faust 		char *stripped_line = strip(line);
104*4b9c6d91SCole Faust 		/*
105*4b9c6d91SCole Faust 		 * Skip blank lines and all comments. Comment lines start with
106*4b9c6d91SCole Faust 		 * '#'.
107*4b9c6d91SCole Faust 		 */
108*4b9c6d91SCole Faust 		if (stripped_line[0] == '\0' || stripped_line[0] == '#')
109*4b9c6d91SCole Faust 			continue;
110*4b9c6d91SCole Faust 
111*4b9c6d91SCole Faust 		/*
112*4b9c6d91SCole Faust 		 * Check if the list is full, and reallocate with doubled
113*4b9c6d91SCole Faust 		 * capacity if so.
114*4b9c6d91SCole Faust 		 */
115*4b9c6d91SCole Faust 		if (list->num_entries >= list->num_allocated_) {
116*4b9c6d91SCole Faust 			list->num_allocated_ = list->num_allocated_ * 2;
117*4b9c6d91SCole Faust 			list->entries = realloc(
118*4b9c6d91SCole Faust 			    list->entries,
119*4b9c6d91SCole Faust 			    list->num_allocated_ * sizeof(struct config_entry));
120*4b9c6d91SCole Faust 			if (list->entries == NULL) {
121*4b9c6d91SCole Faust 				return false;
122*4b9c6d91SCole Faust 			}
123*4b9c6d91SCole Faust 		}
124*4b9c6d91SCole Faust 
125*4b9c6d91SCole Faust 		struct config_entry *entry = &list->entries[list->num_entries];
126*4b9c6d91SCole Faust 		if (!parse_config_line(stripped_line, entry)) {
127*4b9c6d91SCole Faust 			return false;
128*4b9c6d91SCole Faust 		}
129*4b9c6d91SCole Faust 		++list->num_entries;
130*4b9c6d91SCole Faust 	}
131*4b9c6d91SCole Faust 	/*
132*4b9c6d91SCole Faust 	 * getmultiline() behaves similarly with getline(3). It returns -1
133*4b9c6d91SCole Faust 	 * when read into EOF or the following errors.
134*4b9c6d91SCole Faust 	 * Caveat: EINVAL may happen when EOF is encountered in a valid stream.
135*4b9c6d91SCole Faust 	 */
136*4b9c6d91SCole Faust 	if ((errno == EINVAL && config_file == NULL) || errno == ENOMEM) {
137*4b9c6d91SCole Faust 		return false;
138*4b9c6d91SCole Faust 	}
139*4b9c6d91SCole Faust 
140*4b9c6d91SCole Faust 	/* Shrink the list to save memory. */
141*4b9c6d91SCole Faust 	if (list->num_entries < list->num_allocated_) {
142*4b9c6d91SCole Faust 		list->entries =
143*4b9c6d91SCole Faust 		    realloc(list->entries,
144*4b9c6d91SCole Faust 			    list->num_entries * sizeof(struct config_entry));
145*4b9c6d91SCole Faust 		list->num_allocated_ = list->num_entries;
146*4b9c6d91SCole Faust 	}
147*4b9c6d91SCole Faust 
148*4b9c6d91SCole Faust 	return true;
149*4b9c6d91SCole Faust }
150