1 /*
2 * Property Service contexts backend for labeling Android
3 * property keys
4 */
5
6 #include <stdarg.h>
7 #include <string.h>
8 #include <ctype.h>
9 #include <errno.h>
10 #include <limits.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include "callbacks.h"
14 #include "label_internal.h"
15
16 /* A property security context specification. */
17 typedef struct spec {
18 struct selabel_lookup_rec lr; /* holds contexts for lookup result */
19 char *property_key; /* property key string */
20 } spec_t;
21
22 /* Our stored configuration */
23 struct saved_data {
24 /*
25 * The array of specifications is sorted for longest
26 * prefix match
27 */
28 spec_t *spec_arr;
29 unsigned int nspec; /* total number of specifications */
30 };
31
cmp(const void * A,const void * B)32 static int cmp(const void *A, const void *B)
33 {
34 const struct spec *sp1 = A, *sp2 = B;
35
36 if (strncmp(sp1->property_key, "*", 1) == 0)
37 return 1;
38 if (strncmp(sp2->property_key, "*", 1) == 0)
39 return -1;
40
41 size_t L1 = strlen(sp1->property_key);
42 size_t L2 = strlen(sp2->property_key);
43
44 return (L1 < L2) - (L1 > L2);
45 }
46
47 /*
48 * Warn about duplicate specifications.
49 */
nodups_specs(struct saved_data * data)50 static int nodups_specs(struct saved_data *data)
51 {
52 int rc = 0;
53 unsigned int ii, jj;
54 struct spec *curr_spec, *spec_arr = data->spec_arr;
55
56 for (ii = 0; ii < data->nspec; ii++) {
57 curr_spec = &spec_arr[ii];
58 for (jj = ii + 1; jj < data->nspec; jj++) {
59 if (!strcmp(spec_arr[jj].property_key,
60 curr_spec->property_key)) {
61 rc = -1;
62 errno = EINVAL;
63 if (strcmp(spec_arr[jj].lr.ctx_raw,
64 curr_spec->lr.ctx_raw)) {
65 selinux_log
66 (SELINUX_ERROR,
67 "Multiple different specifications for %s (%s and %s).\n",
68 curr_spec->property_key,
69 spec_arr[jj].lr.ctx_raw,
70 curr_spec->lr.ctx_raw);
71 } else {
72 selinux_log
73 (SELINUX_ERROR,
74 "Multiple same specifications for %s.\n",
75 curr_spec->property_key);
76 }
77 }
78 }
79 }
80 return rc;
81 }
82
process_line(struct selabel_handle * rec,const char * path,char * line_buf,int pass,unsigned lineno)83 static int process_line(struct selabel_handle *rec,
84 const char *path, char *line_buf,
85 int pass, unsigned lineno)
86 {
87 int items;
88 char *prop = NULL, *context = NULL;
89 struct saved_data *data = (struct saved_data *)rec->data;
90 spec_t *spec_arr = data->spec_arr;
91 unsigned int nspec = data->nspec;
92 const char *errbuf = NULL;
93
94 items = read_spec_entries(line_buf, &errbuf, 2, &prop, &context);
95 if (items < 0) {
96 items = errno;
97 selinux_log(SELINUX_ERROR,
98 "%s: line %u error due to: %s\n", path,
99 lineno, errbuf ?: strerror(errno));
100 errno = items;
101 return -1;
102 }
103
104 if (items == 0)
105 return items;
106
107 if (items != 2) {
108 selinux_log(SELINUX_ERROR,
109 "%s: line %u is missing fields\n", path,
110 lineno);
111 free(prop);
112 errno = EINVAL;
113 return -1;
114 }
115
116 if (pass == 0) {
117 free(prop);
118 free(context);
119 } else if (pass == 1) {
120 /* On the second pass, process and store the specification in spec. */
121 spec_arr[nspec].property_key = prop;
122 spec_arr[nspec].lr.ctx_raw = context;
123
124 if (rec->validating) {
125 if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) {
126 selinux_log(SELINUX_ERROR,
127 "%s: line %u has invalid context %s\n",
128 path, lineno, spec_arr[nspec].lr.ctx_raw);
129 errno = EINVAL;
130 return -1;
131 }
132 }
133
134 data->nspec = ++nspec;
135 }
136
137 return 0;
138 }
139
process_file(struct selabel_handle * rec,const char * path)140 static int process_file(struct selabel_handle *rec, const char *path)
141 {
142 struct saved_data *data = (struct saved_data *)rec->data;
143 char line_buf[BUFSIZ];
144 unsigned int lineno, maxnspec, pass;
145 struct stat sb;
146 FILE *fp;
147 int status = -1;
148 unsigned int nspec;
149 spec_t *spec_arr;
150
151 /* Open the specification file. */
152 if ((fp = fopen(path, "re")) == NULL)
153 return -1;
154
155 if (fstat(fileno(fp), &sb) < 0)
156 goto finish;
157
158 errno = EINVAL;
159
160 if (!S_ISREG(sb.st_mode))
161 goto finish;
162
163 /*
164 * Two passes per specification file. First is to get the size.
165 * After the first pass, the spec array is malloced / realloced to
166 * the appropriate size. Second pass is to populate the spec array.
167 */
168 maxnspec = UINT_MAX / sizeof(spec_t);
169 for (pass = 0; pass < 2; pass++) {
170 nspec = 0;
171 lineno = 0;
172
173 while (fgets(line_buf, sizeof(line_buf) - 1, fp) &&
174 nspec < maxnspec) {
175 if (process_line(rec, path, line_buf, pass, ++lineno))
176 goto finish;
177 nspec++;
178 }
179
180 if (pass == 0) {
181 if (nspec == 0) {
182 status = 0;
183 goto finish;
184 }
185
186 /* grow spec array if required */
187 spec_arr = realloc(data->spec_arr,
188 (data->nspec + nspec) * sizeof(spec_t));
189 if (spec_arr == NULL)
190 goto finish;
191
192 memset(&spec_arr[data->nspec], 0, nspec * sizeof(spec_t));
193 data->spec_arr = spec_arr;
194 maxnspec = nspec;
195 rewind(fp);
196 }
197 }
198
199 status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
200
201 finish:
202 fclose(fp);
203 return status;
204 }
205
206 static void closef(struct selabel_handle *rec);
207
init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned n)208 static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
209 unsigned n)
210 {
211 struct saved_data *data = (struct saved_data *)rec->data;
212 char **paths = NULL;
213 size_t num_paths = 0;
214 int status = -1;
215 size_t i;
216
217 /* Process arguments */
218 i = n;
219 while (i--) {
220 switch (opts[i].type) {
221 case SELABEL_OPT_PATH:
222 num_paths++;
223 break;
224 }
225 }
226
227 if (!num_paths)
228 return -1;
229
230 paths = calloc(num_paths, sizeof(*paths));
231 if (!paths)
232 return -1;
233
234 rec->spec_files = paths;
235 rec->spec_files_len = num_paths;
236
237 i = n;
238 while (i--) {
239 switch(opts[i].type) {
240 case SELABEL_OPT_PATH:
241 *paths = strdup(opts[i].value);
242 if (*paths == NULL)
243 goto finish;
244 paths++;
245 }
246 }
247
248 for (i = 0; i < num_paths; i++) {
249 status = process_file(rec, rec->spec_files[i]);
250 if (status)
251 goto finish;
252 }
253
254 /* warn about duplicates after all files have been processed. */
255 status = nodups_specs(data);
256 if (status)
257 goto finish;
258
259 qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
260
261 digest_gen_hash(rec->digest);
262
263 finish:
264 if (status)
265 closef(rec);
266
267 return status;
268 }
269
270 /*
271 * Backend interface routines
272 */
closef(struct selabel_handle * rec)273 static void closef(struct selabel_handle *rec)
274 {
275 struct saved_data *data = (struct saved_data *)rec->data;
276 struct spec *spec;
277 unsigned int i;
278
279 if (data->spec_arr) {
280 for (i = 0; i < data->nspec; i++) {
281 spec = &data->spec_arr[i];
282 free(spec->property_key);
283 free(spec->lr.ctx_raw);
284 free(spec->lr.ctx_trans);
285 }
286
287 free(data->spec_arr);
288 }
289
290 free(data);
291 }
292
property_lookup(struct selabel_handle * rec,const char * key,int type)293 static struct selabel_lookup_rec *property_lookup(struct selabel_handle *rec,
294 const char *key,
295 int __attribute__((unused)) type)
296 {
297 struct saved_data *data = (struct saved_data *)rec->data;
298 spec_t *spec_arr = data->spec_arr;
299 unsigned int i;
300 struct selabel_lookup_rec *ret = NULL;
301
302 if (!data->nspec) {
303 errno = ENOENT;
304 goto finish;
305 }
306
307 for (i = 0; i < data->nspec; i++) {
308 if (strncmp(spec_arr[i].property_key, key,
309 strlen(spec_arr[i].property_key)) == 0) {
310 break;
311 }
312 if (strncmp(spec_arr[i].property_key, "*", 1) == 0)
313 break;
314 }
315
316 if (i >= data->nspec) {
317 /* No matching specification. */
318 errno = ENOENT;
319 goto finish;
320 }
321
322 ret = &spec_arr[i].lr;
323
324 finish:
325 return ret;
326 }
327
service_lookup(struct selabel_handle * rec,const char * key,int type)328 static struct selabel_lookup_rec *service_lookup(struct selabel_handle *rec,
329 const char *key, int __attribute__((unused)) type)
330 {
331 struct saved_data *data = (struct saved_data *)rec->data;
332 spec_t *spec_arr = data->spec_arr;
333 unsigned int i;
334 struct selabel_lookup_rec *ret = NULL;
335
336 if (!data->nspec) {
337 errno = ENOENT;
338 goto finish;
339 }
340
341 for (i = 0; i < data->nspec; i++) {
342 if (strcmp(spec_arr[i].property_key, key) == 0)
343 break;
344 if (strcmp(spec_arr[i].property_key, "*") == 0)
345 break;
346 }
347
348 if (i >= data->nspec) {
349 /* No matching specification. */
350 errno = ENOENT;
351 goto finish;
352 }
353
354 ret = &spec_arr[i].lr;
355
356 finish:
357 return ret;
358 }
359
stats(struct selabel_handle * rec)360 static void stats(struct selabel_handle __attribute__((unused)) *rec)
361 {
362 selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n");
363 }
364
selabel_property_init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned nopts)365 int selabel_property_init(struct selabel_handle *rec,
366 const struct selinux_opt *opts,
367 unsigned nopts)
368 {
369 struct saved_data *data;
370
371 data = (struct saved_data *)calloc(1, sizeof(*data));
372 if (!data)
373 return -1;
374
375 rec->data = data;
376 rec->func_close = &closef;
377 rec->func_stats = &stats;
378 rec->func_lookup = &property_lookup;
379
380 return init(rec, opts, nopts);
381 }
382
selabel_service_init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned nopts)383 int selabel_service_init(struct selabel_handle *rec,
384 const struct selinux_opt *opts, unsigned nopts)
385 {
386 struct saved_data *data;
387
388 data = (struct saved_data *)calloc(1, sizeof(*data));
389 if (!data)
390 return -1;
391
392 rec->data = data;
393 rec->func_close = &closef;
394 rec->func_stats = &stats;
395 rec->func_lookup = &service_lookup;
396
397 return init(rec, opts, nopts);
398 }
399