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, curr_spec->property_key)) {
60 rc = -1;
61 errno = EINVAL;
62 if (strcmp(spec_arr[jj].lr.ctx_raw, curr_spec->lr.ctx_raw)) {
63 selinux_log(SELINUX_ERROR,
64 "%s: Multiple different specifications for %s (%s and %s).\n",
65 path,
66 curr_spec->property_key,
67 spec_arr[jj].lr.ctx_raw,
68 curr_spec->lr.ctx_raw);
69 } else {
70 selinux_log(SELINUX_ERROR,
71 "%s: Multiple same specifications for %s.\n",
72 path,
73 curr_spec->property_key);
74 }
75 }
76 }
77 }
78 return rc;
79 }
80
process_line(struct selabel_handle * rec,const char * path,char * line_buf,int pass,unsigned lineno)81 static int process_line(struct selabel_handle *rec,
82 const char *path, char *line_buf,
83 int pass, unsigned lineno)
84 {
85 int items, len;
86 char buf1[BUFSIZ], buf2[BUFSIZ];
87 char *buf_p, *prop = buf1, *context = buf2;
88 struct saved_data *data = (struct saved_data *)rec->data;
89 spec_t *spec_arr = data->spec_arr;
90 unsigned int nspec = data->nspec;
91
92 len = strlen(line_buf);
93 if (line_buf[len - 1] == '\n')
94 line_buf[len - 1] = 0;
95 buf_p = line_buf;
96 while (isspace(*buf_p))
97 buf_p++;
98 /* Skip comment lines and empty lines. */
99 if (*buf_p == '#' || *buf_p == 0)
100 return 0;
101 items = sscanf(line_buf, "%255s %255s", prop, context);
102 if (items != 2) {
103 selinux_log(SELINUX_WARNING,
104 "%s: line %u is missing fields, skipping\n", path,
105 lineno);
106 return 0;
107 }
108
109 if (pass == 1) {
110 /* On the second pass, process and store the specification in spec. */
111 spec_arr[nspec].property_key = strdup(prop);
112 if (!spec_arr[nspec].property_key) {
113 selinux_log(SELINUX_WARNING,
114 "%s: out of memory at line %u on prop %s\n",
115 path, lineno, prop);
116 return -1;
117
118 }
119
120 spec_arr[nspec].lr.ctx_raw = strdup(context);
121 if (!spec_arr[nspec].lr.ctx_raw) {
122 selinux_log(SELINUX_WARNING,
123 "%s: out of memory at line %u on context %s\n",
124 path, lineno, context);
125 return -1;
126 }
127
128 if (rec->validating) {
129 if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) {
130 selinux_log(SELINUX_WARNING,
131 "%s: line %u has invalid context %s\n",
132 path, lineno, spec_arr[nspec].lr.ctx_raw);
133 }
134 }
135 }
136
137 data->nspec = ++nspec;
138 return 0;
139 }
140
init(struct selabel_handle * rec,struct selinux_opt * opts,unsigned n)141 static int init(struct selabel_handle *rec, struct selinux_opt *opts,
142 unsigned n)
143 {
144 struct saved_data *data = (struct saved_data *)rec->data;
145 const char *path = NULL;
146 FILE *fp;
147 char line_buf[BUFSIZ];
148 unsigned int lineno = 0, maxnspec, pass;
149 int status = -1;
150 struct stat sb;
151
152 /* Process arguments */
153 while (n--)
154 switch (opts[n].type) {
155 case SELABEL_OPT_PATH:
156 path = opts[n].value;
157 break;
158 }
159
160 if (!path)
161 return -1;
162
163 /* Open the specification file. */
164 if ((fp = fopen(path, "r")) == NULL)
165 return -1;
166
167 if (fstat(fileno(fp), &sb) < 0)
168 goto finish;
169 errno = EINVAL;
170 if (!S_ISREG(sb.st_mode))
171 goto finish;
172
173 /*
174 * Two passes of the specification file. First is to get the size.
175 * After the first pass, the spec array is malloced to the appropriate
176 * size. Second pass is to populate the spec array and check for
177 * dups.
178 */
179 maxnspec = UINT_MAX / sizeof(spec_t);
180 for (pass = 0; pass < 2; pass++) {
181 data->nspec = 0;
182
183 while (fgets(line_buf, sizeof line_buf - 1, fp)
184 && data->nspec < maxnspec) {
185 if (process_line(rec, path, line_buf, pass, ++lineno) != 0)
186 goto finish;
187 }
188
189 if (pass == 1) {
190 status = nodups_specs(data, path);
191
192 if (status)
193 goto finish;
194 }
195
196 if (pass == 0) {
197
198 if (data->nspec == 0) {
199 status = 0;
200 goto finish;
201 }
202
203 if (NULL == (data->spec_arr =
204 malloc(sizeof(spec_t) * data->nspec)))
205 goto finish;
206
207 memset(data->spec_arr, 0, sizeof(spec_t) * data->nspec);
208 maxnspec = data->nspec;
209 rewind(fp);
210 }
211 }
212
213 qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
214
215 status = 0;
216 finish:
217 fclose(fp);
218 return status;
219 }
220
221 /*
222 * Backend interface routines
223 */
closef(struct selabel_handle * rec)224 static void closef(struct selabel_handle *rec)
225 {
226 struct saved_data *data = (struct saved_data *)rec->data;
227 struct spec *spec;
228 unsigned int i;
229
230 for (i = 0; i < data->nspec; i++) {
231 spec = &data->spec_arr[i];
232 free(spec->property_key);
233 free(spec->lr.ctx_raw);
234 free(spec->lr.ctx_trans);
235 }
236
237 if (data->spec_arr)
238 free(data->spec_arr);
239
240 free(data);
241 }
242
lookup(struct selabel_handle * rec,const char * key,int type)243 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
244 const char *key,
245 int __attribute__ ((unused)) type)
246 {
247 struct saved_data *data = (struct saved_data *)rec->data;
248 spec_t *spec_arr = data->spec_arr;
249 unsigned int i;
250 struct selabel_lookup_rec *ret = NULL;
251
252 if (!data->nspec) {
253 errno = ENOENT;
254 goto finish;
255 }
256
257 for (i = 0; i < data->nspec; i++) {
258 if (strncmp(spec_arr[i].property_key, key,
259 strlen(spec_arr[i].property_key)) == 0) {
260 break;
261 }
262 if (strncmp(spec_arr[i].property_key, "*", 1) == 0)
263 break;
264 }
265
266 if (i >= data->nspec) {
267 /* No matching specification. */
268 errno = ENOENT;
269 goto finish;
270 }
271
272 ret = &spec_arr[i].lr;
273
274 finish:
275 return ret;
276 }
277
stats(struct selabel_handle * rec)278 static void stats(struct selabel_handle __attribute__ ((unused)) * rec)
279 {
280 selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n");
281 }
282
selabel_property_init(struct selabel_handle * rec,struct selinux_opt * opts,unsigned nopts)283 int selabel_property_init(struct selabel_handle *rec, struct selinux_opt *opts,
284 unsigned nopts)
285 {
286 struct saved_data *data;
287
288 data = (struct saved_data *)malloc(sizeof(*data));
289 if (!data)
290 return -1;
291 memset(data, 0, sizeof(*data));
292
293 rec->data = data;
294 rec->func_close = &closef;
295 rec->func_stats = &stats;
296 rec->func_lookup = &lookup;
297
298 return init(rec, opts, nopts);
299 }
300