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