xref: /aosp_15_r20/external/selinux/libselinux/src/label_media.c (revision 2d543d20722ada2425b5bdab9d0d1d29470e7bba)
1 /*
2  * Media contexts backend for labeling system
3  *
4  * Author : Eamon Walsh <[email protected]>
5  */
6 
7 #include <sys/stat.h>
8 #include <string.h>
9 #include <stdio.h>
10 #include <stdio_ext.h>
11 #include <ctype.h>
12 #include <errno.h>
13 #include <limits.h>
14 #include "callbacks.h"
15 #include "label_internal.h"
16 
17 /*
18  * Internals
19  */
20 
21 /* A context specification. */
22 typedef struct spec {
23 	struct selabel_lookup_rec lr;	/* holds contexts for lookup result */
24 	char *key;		/* key string */
25 	int matches;		/* number of matches made during operation */
26 } spec_t;
27 
28 struct saved_data {
29 	unsigned int nspec;
30 	spec_t *spec_arr;
31 };
32 
process_line(const char * path,const char * line_buf,int pass,unsigned lineno,struct selabel_handle * rec)33 static int process_line(const char *path, const char *line_buf, int pass,
34 			unsigned lineno, struct selabel_handle *rec)
35 {
36 	struct saved_data *data = (struct saved_data *)rec->data;
37 	int items;
38 	const char *buf_p;
39 	char *key, *context;
40 
41 	buf_p = line_buf;
42 	while (isspace((unsigned char)*buf_p))
43 		buf_p++;
44 	/* Skip comment lines and empty lines. */
45 	if (*buf_p == '#' || *buf_p == 0)
46 		return 0;
47 	items = sscanf(line_buf, "%ms %ms ", &key, &context);
48 	if (items < 2) {
49 		selinux_log(SELINUX_WARNING,
50 			  "%s:  line %u is missing fields, skipping\n", path,
51 			  lineno);
52 		if (items == 1)
53 			free(key);
54 		return 0;
55 	}
56 
57 	if (pass == 1) {
58 		data->spec_arr[data->nspec].key = key;
59 		data->spec_arr[data->nspec].lr.ctx_raw = context;
60 	}
61 
62 	data->nspec++;
63 	if (pass == 0) {
64 		free(key);
65 		free(context);
66 	}
67 	return 0;
68 }
69 
init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned n)70 static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
71 		unsigned n)
72 {
73 	FILE *fp;
74 	struct saved_data *data = (struct saved_data *)rec->data;
75 	const char *path = NULL;
76 	char *line_buf = NULL;
77 	size_t line_len = 0;
78 	int status = -1;
79 	unsigned int lineno, pass, maxnspec;
80 	struct stat sb;
81 
82 	/* Process arguments */
83 	while (n) {
84 		n--;
85 		switch(opts[n].type) {
86 		case SELABEL_OPT_PATH:
87 			path = opts[n].value;
88 			break;
89 		case SELABEL_OPT_UNUSED:
90 		case SELABEL_OPT_VALIDATE:
91 		case SELABEL_OPT_DIGEST:
92 			break;
93 		default:
94 			errno = EINVAL;
95 			return -1;
96 		}
97 }
98 
99 	/* Open the specification file. */
100 	if (!path)
101 		path = selinux_media_context_path();
102 	if ((fp = fopen(path, "re")) == NULL)
103 		return -1;
104 	__fsetlocking(fp, FSETLOCKING_BYCALLER);
105 
106 	if (fstat(fileno(fp), &sb) < 0)
107 		goto finish;
108 	if (!S_ISREG(sb.st_mode)) {
109 		errno = EINVAL;
110 		goto finish;
111 	}
112 	rec->spec_file = strdup(path);
113 
114 	/*
115 	 * Perform two passes over the specification file.
116 	 * The first pass counts the number of specifications and
117 	 * performs simple validation of the input.  At the end
118 	 * of the first pass, the spec array is allocated.
119 	 * The second pass performs detailed validation of the input
120 	 * and fills in the spec array.
121 	 */
122 	maxnspec = UINT_MAX / sizeof(spec_t);
123 	for (pass = 0; pass < 2; pass++) {
124 		lineno = 0;
125 		data->nspec = 0;
126 		while (getline(&line_buf, &line_len, fp) > 0 &&
127 		       data->nspec < maxnspec) {
128 			if (process_line(path, line_buf, pass, ++lineno, rec))
129 				goto finish;
130 		}
131 
132 		if (pass == 0) {
133 			if (data->nspec == 0) {
134 				status = 0;
135 				goto finish;
136 			}
137 			data->spec_arr = malloc(sizeof(spec_t)*data->nspec);
138 			if (data->spec_arr == NULL)
139 				goto finish;
140 			memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
141 			maxnspec = data->nspec;
142 
143 			status = fseek(fp, 0L, SEEK_SET);
144 			if (status == -1)
145 				goto finish;
146 		}
147 	}
148 
149 	status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
150 	if (status)
151 		goto finish;
152 
153 	digest_gen_hash(rec->digest);
154 
155 finish:
156 	free(line_buf);
157 	fclose(fp);
158 	return status;
159 }
160 
161 /*
162  * Backend interface routines
163  */
close(struct selabel_handle * rec)164 static void close(struct selabel_handle *rec)
165 {
166 	struct saved_data *data = (struct saved_data *)rec->data;
167 	struct spec *spec, *spec_arr;
168 	unsigned int i;
169 
170 	if (!data)
171 		return;
172 
173 	spec_arr = data->spec_arr;
174 
175 	for (i = 0; i < data->nspec; i++) {
176 		spec = &spec_arr[i];
177 		free(spec->key);
178 		free(spec->lr.ctx_raw);
179 		free(spec->lr.ctx_trans);
180 	}
181 
182 	if (spec_arr)
183 	    free(spec_arr);
184 
185 	free(data);
186 	rec->data = NULL;
187 }
188 
lookup(struct selabel_handle * rec,const char * key,int type)189 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
190 					 const char *key,
191 					 int type __attribute__((unused)))
192 {
193 	struct saved_data *data = (struct saved_data *)rec->data;
194 	spec_t *spec_arr = data->spec_arr;
195 	unsigned int i;
196 
197 	for (i = 0; i < data->nspec; i++) {
198 		if (!strncmp(spec_arr[i].key, key, strlen(key) + 1))
199 			break;
200 		if (!strncmp(spec_arr[i].key, "*", 2))
201 			break;
202 	}
203 
204 	if (i >= data->nspec) {
205 		/* No matching specification. */
206 		errno = ENOENT;
207 		return NULL;
208 	}
209 
210 	spec_arr[i].matches++;
211 	return &spec_arr[i].lr;
212 }
213 
stats(struct selabel_handle * rec)214 static void stats(struct selabel_handle *rec)
215 {
216 	struct saved_data *data = (struct saved_data *)rec->data;
217 	unsigned int i, total = 0;
218 
219 	for (i = 0; i < data->nspec; i++)
220 		total += data->spec_arr[i].matches;
221 
222 	selinux_log(SELINUX_INFO, "%u entries, %u matches made\n",
223 		  data->nspec, total);
224 }
225 
selabel_media_init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned nopts)226 int selabel_media_init(struct selabel_handle *rec,
227 				    const struct selinux_opt *opts,
228 				    unsigned nopts)
229 {
230 	struct saved_data *data;
231 
232 	data = (struct saved_data *)malloc(sizeof(*data));
233 	if (!data)
234 		return -1;
235 	memset(data, 0, sizeof(*data));
236 
237 	rec->data = data;
238 	rec->func_close = &close;
239 	rec->func_lookup = &lookup;
240 	rec->func_stats = &stats;
241 
242 	return init(rec, opts, nopts);
243 }
244