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