blob: b1837c44f7a7b8db96655faf093d442e05ec713e [file] [log] [blame]
Stephen Smalley35b01082012-04-04 10:06:13 -04001/*
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. */
17typedef 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 */
23struct 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
32static 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 */
50static 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
83static 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
131 data->nspec = ++nspec;
132 return 0;
133}
134
Stephen Smalleya2e47cd2012-06-11 13:35:34 -0400135static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
Stephen Smalley35b01082012-04-04 10:06:13 -0400136 unsigned n)
137{
138 struct saved_data *data = (struct saved_data *)rec->data;
139 const char *path = NULL;
140 FILE *fp;
141 char line_buf[BUFSIZ];
142 unsigned int lineno = 0, maxnspec, pass;
143 int status = -1;
144 struct stat sb;
145
146 /* Process arguments */
147 while (n--)
148 switch (opts[n].type) {
149 case SELABEL_OPT_PATH:
150 path = opts[n].value;
151 break;
152 default:
153 selinux_log(SELINUX_WARNING,
154 "Argument type (%d) not recognized. Skipping\n", opts[n].type);
155 break;
156 }
157
158 /* Open the specification file. */
159 if ((fp = fopen(path, "r")) == NULL)
160 return -1;
161
162 if (fstat(fileno(fp), &sb) < 0)
163 return -1;
164 if (!S_ISREG(sb.st_mode)) {
165 errno = EINVAL;
166 return -1;
167 }
168
169 /*
170 * Two passes of the specification file. First is to get the size.
171 * After the first pass, the spec array is malloced to the appropriate
172 * size. Second pass is to populate the spec array and check for
173 * dups.
174 */
175 maxnspec = UINT_MAX / sizeof(spec_t);
176 for (pass = 0; pass < 2; pass++) {
177 data->nspec = 0;
178
179 while (fgets(line_buf, sizeof line_buf - 1, fp)
180 && data->nspec < maxnspec) {
181 if (process_line(rec, path, line_buf, pass, ++lineno) != 0) {
182 goto finish;
183 }
184 }
185
186 if (pass == 1) {
187 status = nodups_specs(data, path);
188
189 if (status)
190 goto finish;
191 }
192
193 if (pass == 0) {
194
195 if (data->nspec == 0) {
196 status = 0;
197 goto finish;
198 }
199
200 if (NULL == (data->spec_arr =
201 malloc(sizeof(spec_t) * data->nspec)))
202 goto finish;
203
204 memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
205 maxnspec = data->nspec;
206 rewind(fp);
207 }
208 }
209
210 qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
211
212 status = 0;
213finish:
214 fclose(fp);
215 return status;
216}
217
218/*
219 * Backend interface routines
220 */
221static void closef(struct selabel_handle *rec)
222{
223 struct saved_data *data = (struct saved_data *)rec->data;
224 struct spec *spec;
225 unsigned int i;
226
227 for (i = 0; i < data->nspec; i++) {
228 spec = &data->spec_arr[i];
229 free(spec->property_key);
230 free(spec->lr.ctx_raw);
231 free(spec->lr.ctx_trans);
232 }
233
234 if (data->spec_arr)
235 free(data->spec_arr);
236
237 free(data);
238}
239
240static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
241 const char *key,
242 int __attribute__((unused)) type)
243{
244 struct saved_data *data = (struct saved_data *)rec->data;
245 spec_t *spec_arr = data->spec_arr;
246 unsigned int i;
247 struct selabel_lookup_rec *ret = NULL;
248
249 if (!data->nspec) {
250 errno = ENOENT;
251 goto finish;
252 }
253
254 for (i = 0; i < data->nspec; i++) {
255 if (strncmp(spec_arr[i].property_key, key,
256 strlen(spec_arr[i].property_key)) == 0) {
257 break;
258 }
259 if (strncmp(spec_arr[i].property_key, "*", 1) == 0)
260 break;
261 }
262
263 if (i >= data->nspec) {
264 /* No matching specification. */
265 errno = ENOENT;
266 goto finish;
267 }
268
269 ret = &spec_arr[i].lr;
270
271finish:
272 return ret;
273}
274
275static void stats(struct selabel_handle __attribute__((unused)) *rec)
276{
277 selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n");
278}
279
Stephen Smalleya2e47cd2012-06-11 13:35:34 -0400280int selabel_property_init(struct selabel_handle *rec,
281 const struct selinux_opt *opts,
282 unsigned nopts)
Stephen Smalley35b01082012-04-04 10:06:13 -0400283{
284 struct saved_data *data;
285
286 data = (struct saved_data *)malloc(sizeof(*data));
287 if (!data)
288 return -1;
289 memset(data, 0, sizeof(*data));
290
291 rec->data = data;
292 rec->func_close = &closef;
293 rec->func_stats = &stats;
294 rec->func_lookup = &lookup;
295
296 return init(rec, opts, nopts);
297}