1 /*
2 * Media contexts backend for labeling system
3 *
4 * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
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,char * line_buf,int pass,unsigned lineno,struct selabel_handle * rec)33 static int process_line(const char *path, 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 char *buf_p;
39 char *key, *context;
40
41 buf_p = line_buf;
42 while (isspace(*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 switch(opts[n].type) {
85 case SELABEL_OPT_PATH:
86 path = opts[n].value;
87 break;
88 }
89
90 /* Open the specification file. */
91 if (!path)
92 path = selinux_media_context_path();
93 if ((fp = fopen(path, "r")) == NULL)
94 return -1;
95 __fsetlocking(fp, FSETLOCKING_BYCALLER);
96
97 if (fstat(fileno(fp), &sb) < 0)
98 return -1;
99 if (!S_ISREG(sb.st_mode)) {
100 errno = EINVAL;
101 return -1;
102 }
103 rec->spec_file = strdup(path);
104
105 /*
106 * Perform two passes over the specification file.
107 * The first pass counts the number of specifications and
108 * performs simple validation of the input. At the end
109 * of the first pass, the spec array is allocated.
110 * The second pass performs detailed validation of the input
111 * and fills in the spec array.
112 */
113 maxnspec = UINT_MAX / sizeof(spec_t);
114 for (pass = 0; pass < 2; pass++) {
115 lineno = 0;
116 data->nspec = 0;
117 while (getline(&line_buf, &line_len, fp) > 0 &&
118 data->nspec < maxnspec) {
119 if (process_line(path, line_buf, pass, ++lineno, rec))
120 goto finish;
121 }
122 lineno = 0;
123
124 if (pass == 0) {
125 if (data->nspec == 0) {
126 status = 0;
127 goto finish;
128 }
129 data->spec_arr = malloc(sizeof(spec_t)*data->nspec);
130 if (data->spec_arr == NULL)
131 goto finish;
132 memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
133 maxnspec = data->nspec;
134 rewind(fp);
135 }
136 }
137 free(line_buf);
138
139 status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
140 if (status)
141 goto finish;
142
143 digest_gen_hash(rec->digest);
144
145 finish:
146 fclose(fp);
147 return status;
148 }
149
150 /*
151 * Backend interface routines
152 */
close(struct selabel_handle * rec)153 static void close(struct selabel_handle *rec)
154 {
155 struct saved_data *data = (struct saved_data *)rec->data;
156 struct spec *spec, *spec_arr = data->spec_arr;
157 unsigned int i;
158
159 for (i = 0; i < data->nspec; i++) {
160 spec = &spec_arr[i];
161 free(spec->key);
162 free(spec->lr.ctx_raw);
163 free(spec->lr.ctx_trans);
164 }
165
166 if (spec_arr)
167 free(spec_arr);
168
169 free(data);
170 }
171
lookup(struct selabel_handle * rec,const char * key,int type)172 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
173 const char *key,
174 int type __attribute__((unused)))
175 {
176 struct saved_data *data = (struct saved_data *)rec->data;
177 spec_t *spec_arr = data->spec_arr;
178 unsigned int i;
179
180 for (i = 0; i < data->nspec; i++) {
181 if (!strncmp(spec_arr[i].key, key, strlen(key) + 1))
182 break;
183 if (!strncmp(spec_arr[i].key, "*", 2))
184 break;
185 }
186
187 if (i >= data->nspec) {
188 /* No matching specification. */
189 errno = ENOENT;
190 return NULL;
191 }
192
193 spec_arr[i].matches++;
194 return &spec_arr[i].lr;
195 }
196
stats(struct selabel_handle * rec)197 static void stats(struct selabel_handle *rec)
198 {
199 struct saved_data *data = (struct saved_data *)rec->data;
200 unsigned int i, total = 0;
201
202 for (i = 0; i < data->nspec; i++)
203 total += data->spec_arr[i].matches;
204
205 selinux_log(SELINUX_INFO, "%u entries, %u matches made\n",
206 data->nspec, total);
207 }
208
selabel_media_init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned nopts)209 int selabel_media_init(struct selabel_handle *rec,
210 const struct selinux_opt *opts,
211 unsigned nopts)
212 {
213 struct saved_data *data;
214
215 data = (struct saved_data *)malloc(sizeof(*data));
216 if (!data)
217 return -1;
218 memset(data, 0, sizeof(*data));
219
220 rec->data = data;
221 rec->func_close = &close;
222 rec->func_lookup = &lookup;
223 rec->func_stats = &stats;
224
225 return init(rec, opts, nopts);
226 }
227