1 /*
2 * Media contexts backend for labeling system
3 *
4 * Author : Eamon Walsh <[email protected]>
5 */
6
7 #include <sys/stat.h>
8 #include <string.h>
9 #include <stdio.h>
10 #include <stdio_ext.h>
11 #include <ctype.h>
12 #include <errno.h>
13 #include <limits.h>
14 #include "callbacks.h"
15 #include "label_internal.h"
16
17 /*
18 * Internals
19 */
20
21 /* A context specification. */
22 typedef struct spec {
23 struct selabel_lookup_rec lr; /* holds contexts for lookup result */
24 char *key; /* key string */
25 int matches; /* number of matches made during operation */
26 } spec_t;
27
28 struct saved_data {
29 unsigned int nspec;
30 spec_t *spec_arr;
31 };
32
process_line(const char * path,const char * line_buf,int pass,unsigned lineno,struct selabel_handle * rec)33 static int process_line(const char *path, const char *line_buf, int pass,
34 unsigned lineno, struct selabel_handle *rec)
35 {
36 struct saved_data *data = (struct saved_data *)rec->data;
37 int items;
38 const char *buf_p;
39 char *key, *context;
40
41 buf_p = line_buf;
42 while (isspace((unsigned char)*buf_p))
43 buf_p++;
44 /* Skip comment lines and empty lines. */
45 if (*buf_p == '#' || *buf_p == 0)
46 return 0;
47 items = sscanf(line_buf, "%ms %ms ", &key, &context);
48 if (items < 2) {
49 selinux_log(SELINUX_WARNING,
50 "%s: line %u is missing fields, skipping\n", path,
51 lineno);
52 if (items == 1)
53 free(key);
54 return 0;
55 }
56
57 if (pass == 1) {
58 data->spec_arr[data->nspec].key = key;
59 data->spec_arr[data->nspec].lr.ctx_raw = context;
60 }
61
62 data->nspec++;
63 if (pass == 0) {
64 free(key);
65 free(context);
66 }
67 return 0;
68 }
69
init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned n)70 static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
71 unsigned n)
72 {
73 FILE *fp;
74 struct saved_data *data = (struct saved_data *)rec->data;
75 const char *path = NULL;
76 char *line_buf = NULL;
77 size_t line_len = 0;
78 int status = -1;
79 unsigned int lineno, pass, maxnspec;
80 struct stat sb;
81
82 /* Process arguments */
83 while (n) {
84 n--;
85 switch(opts[n].type) {
86 case SELABEL_OPT_PATH:
87 path = opts[n].value;
88 break;
89 case SELABEL_OPT_UNUSED:
90 case SELABEL_OPT_VALIDATE:
91 case SELABEL_OPT_DIGEST:
92 break;
93 default:
94 errno = EINVAL;
95 return -1;
96 }
97 }
98
99 /* Open the specification file. */
100 if (!path)
101 path = selinux_media_context_path();
102 if ((fp = fopen(path, "re")) == NULL)
103 return -1;
104 __fsetlocking(fp, FSETLOCKING_BYCALLER);
105
106 if (fstat(fileno(fp), &sb) < 0)
107 goto finish;
108 if (!S_ISREG(sb.st_mode)) {
109 errno = EINVAL;
110 goto finish;
111 }
112 rec->spec_file = strdup(path);
113
114 /*
115 * Perform two passes over the specification file.
116 * The first pass counts the number of specifications and
117 * performs simple validation of the input. At the end
118 * of the first pass, the spec array is allocated.
119 * The second pass performs detailed validation of the input
120 * and fills in the spec array.
121 */
122 maxnspec = UINT_MAX / sizeof(spec_t);
123 for (pass = 0; pass < 2; pass++) {
124 lineno = 0;
125 data->nspec = 0;
126 while (getline(&line_buf, &line_len, fp) > 0 &&
127 data->nspec < maxnspec) {
128 if (process_line(path, line_buf, pass, ++lineno, rec))
129 goto finish;
130 }
131
132 if (pass == 0) {
133 if (data->nspec == 0) {
134 status = 0;
135 goto finish;
136 }
137 data->spec_arr = malloc(sizeof(spec_t)*data->nspec);
138 if (data->spec_arr == NULL)
139 goto finish;
140 memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
141 maxnspec = data->nspec;
142
143 status = fseek(fp, 0L, SEEK_SET);
144 if (status == -1)
145 goto finish;
146 }
147 }
148
149 status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
150 if (status)
151 goto finish;
152
153 digest_gen_hash(rec->digest);
154
155 finish:
156 free(line_buf);
157 fclose(fp);
158 return status;
159 }
160
161 /*
162 * Backend interface routines
163 */
close(struct selabel_handle * rec)164 static void close(struct selabel_handle *rec)
165 {
166 struct saved_data *data = (struct saved_data *)rec->data;
167 struct spec *spec, *spec_arr;
168 unsigned int i;
169
170 if (!data)
171 return;
172
173 spec_arr = data->spec_arr;
174
175 for (i = 0; i < data->nspec; i++) {
176 spec = &spec_arr[i];
177 free(spec->key);
178 free(spec->lr.ctx_raw);
179 free(spec->lr.ctx_trans);
180 }
181
182 if (spec_arr)
183 free(spec_arr);
184
185 free(data);
186 rec->data = NULL;
187 }
188
lookup(struct selabel_handle * rec,const char * key,int type)189 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
190 const char *key,
191 int type __attribute__((unused)))
192 {
193 struct saved_data *data = (struct saved_data *)rec->data;
194 spec_t *spec_arr = data->spec_arr;
195 unsigned int i;
196
197 for (i = 0; i < data->nspec; i++) {
198 if (!strncmp(spec_arr[i].key, key, strlen(key) + 1))
199 break;
200 if (!strncmp(spec_arr[i].key, "*", 2))
201 break;
202 }
203
204 if (i >= data->nspec) {
205 /* No matching specification. */
206 errno = ENOENT;
207 return NULL;
208 }
209
210 spec_arr[i].matches++;
211 return &spec_arr[i].lr;
212 }
213
stats(struct selabel_handle * rec)214 static void stats(struct selabel_handle *rec)
215 {
216 struct saved_data *data = (struct saved_data *)rec->data;
217 unsigned int i, total = 0;
218
219 for (i = 0; i < data->nspec; i++)
220 total += data->spec_arr[i].matches;
221
222 selinux_log(SELINUX_INFO, "%u entries, %u matches made\n",
223 data->nspec, total);
224 }
225
selabel_media_init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned nopts)226 int selabel_media_init(struct selabel_handle *rec,
227 const struct selinux_opt *opts,
228 unsigned nopts)
229 {
230 struct saved_data *data;
231
232 data = (struct saved_data *)malloc(sizeof(*data));
233 if (!data)
234 return -1;
235 memset(data, 0, sizeof(*data));
236
237 rec->data = data;
238 rec->func_close = &close;
239 rec->func_lookup = &lookup;
240 rec->func_stats = &stats;
241
242 return init(rec, opts, nopts);
243 }
244