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