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