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