1 #ifndef _SELABEL_FILE_H_
2 #define _SELABEL_FILE_H_
3
4 #include <sys/stat.h>
5
6 #include "label_internal.h"
7
8 #define SELINUX_MAGIC_COMPILED_FCONTEXT 0xf97cff8a
9 #define SELINUX_COMPILED_FCONTEXT_NOPCRE_VERS 1
10 #define SELINUX_COMPILED_FCONTEXT_PCRE_VERS 2
11 #define SELINUX_COMPILED_FCONTEXT_MAX_VERS 2
12
13 /* Prior to verison 8.20, libpcre did not have pcre_free_study() */
14 #if (PCRE_MAJOR < 8 || (PCRE_MAJOR == 8 && PCRE_MINOR < 20))
15 #define pcre_free_study pcre_free
16 #endif
17
18 /* A file security context specification. */
19 struct spec {
20 struct selabel_lookup_rec lr; /* holds contexts for lookup result */
21 char *regex_str; /* regular expession string for diagnostics */
22 char *type_str; /* type string for diagnostic messages */
23 pcre *regex; /* compiled regular expression */
24 union {
25 pcre_extra *sd; /* pointer to extra compiled stuff */
26 pcre_extra lsd; /* used to hold the mmap'd version */
27 };
28 mode_t mode; /* mode format value */
29 int matches; /* number of matching pathnames */
30 int stem_id; /* indicates which stem-compression item */
31 char hasMetaChars; /* regular expression has meta-chars */
32 char regcomp; /* regex_str has been compiled to regex */
33 char from_mmap; /* this spec is from an mmap of the data */
34 };
35
36 /* A regular expression stem */
37 struct stem {
38 char *buf;
39 int len;
40 char from_mmap;
41 };
42
43 /* Where we map the file in during selabel_open() */
44 struct mmap_area {
45 void *addr;
46 size_t len;
47 struct mmap_area *next;
48 };
49
50 /* Our stored configuration */
51 struct saved_data {
52 /*
53 * The array of specifications, initially in the same order as in
54 * the specification file. Sorting occurs based on hasMetaChars.
55 */
56 struct spec *spec_arr;
57 unsigned int nspec;
58 unsigned int alloc_specs;
59
60 /*
61 * The array of regular expression stems.
62 */
63 struct stem *stem_arr;
64 int num_stems;
65 int alloc_stems;
66 struct mmap_area *mmap_areas;
67 };
68
get_pcre_extra(struct spec * spec)69 static inline pcre_extra *get_pcre_extra(struct spec *spec)
70 {
71 if (spec->from_mmap)
72 return &spec->lsd;
73 else
74 return spec->sd;
75 }
76
string_to_mode(char * mode)77 static inline mode_t string_to_mode(char *mode)
78 {
79 size_t len;
80
81 if (!mode)
82 return 0;
83 len = strlen(mode);
84 if (mode[0] != '-' || len != 2)
85 return -1;
86 switch (mode[1]) {
87 case 'b':
88 return S_IFBLK;
89 case 'c':
90 return S_IFCHR;
91 case 'd':
92 return S_IFDIR;
93 case 'p':
94 return S_IFIFO;
95 case 'l':
96 return S_IFLNK;
97 case 's':
98 return S_IFSOCK;
99 case '-':
100 return S_IFREG;
101 default:
102 return -1;
103 }
104 /* impossible to get here */
105 return 0;
106 }
107
grow_specs(struct saved_data * data)108 static inline int grow_specs(struct saved_data *data)
109 {
110 struct spec *specs;
111 size_t new_specs, total_specs;
112
113 if (data->nspec < data->alloc_specs)
114 return 0;
115
116 new_specs = data->nspec + 16;
117 total_specs = data->nspec + new_specs;
118
119 specs = realloc(data->spec_arr, total_specs * sizeof(*specs));
120 if (!specs) {
121 perror("realloc");
122 return -1;
123 }
124
125 /* blank the new entries */
126 memset(&specs[data->nspec], 0, new_specs * sizeof(*specs));
127
128 data->spec_arr = specs;
129 data->alloc_specs = total_specs;
130 return 0;
131 }
132
133 /* Determine if the regular expression specification has any meta characters. */
spec_hasMetaChars(struct spec * spec)134 static inline void spec_hasMetaChars(struct spec *spec)
135 {
136 char *c;
137 int len;
138 char *end;
139
140 c = spec->regex_str;
141 len = strlen(spec->regex_str);
142 end = c + len;
143
144 spec->hasMetaChars = 0;
145
146 /* Look at each character in the RE specification string for a
147 * meta character. Return when any meta character reached. */
148 while (c < end) {
149 switch (*c) {
150 case '.':
151 case '^':
152 case '$':
153 case '?':
154 case '*':
155 case '+':
156 case '|':
157 case '[':
158 case '(':
159 case '{':
160 spec->hasMetaChars = 1;
161 return;
162 case '\\': /* skip the next character */
163 c++;
164 break;
165 default:
166 break;
167
168 }
169 c++;
170 }
171 return;
172 }
173
174 /* Move exact pathname specifications to the end. */
sort_specs(struct saved_data * data)175 static inline int sort_specs(struct saved_data *data)
176 {
177 struct spec *spec_copy;
178 struct spec spec;
179 unsigned int i;
180 int front, back;
181 size_t len = sizeof(*spec_copy);
182
183 spec_copy = malloc(len * data->nspec);
184 if (!spec_copy)
185 return -1;
186
187 /* first move the exact pathnames to the back */
188 front = 0;
189 back = data->nspec - 1;
190 for (i = 0; i < data->nspec; i++) {
191 if (data->spec_arr[i].hasMetaChars)
192 memcpy(&spec_copy[front++], &data->spec_arr[i], len);
193 else
194 memcpy(&spec_copy[back--], &data->spec_arr[i], len);
195 }
196
197 /*
198 * now the exact pathnames are at the end, but they are in the reverse order.
199 * since 'front' is now the first of the 'exact' we can run that part of the
200 * array switching the front and back element.
201 */
202 back = data->nspec - 1;
203 while (front < back) {
204 /* save the front */
205 memcpy(&spec, &spec_copy[front], len);
206 /* move the back to the front */
207 memcpy(&spec_copy[front], &spec_copy[back], len);
208 /* put the old front in the back */
209 memcpy(&spec_copy[back], &spec, len);
210 front++;
211 back--;
212 }
213
214 free(data->spec_arr);
215 data->spec_arr = spec_copy;
216
217 return 0;
218 }
219
220 /* Return the length of the text that can be considered the stem, returns 0
221 * if there is no identifiable stem */
get_stem_from_spec(const char * const buf)222 static inline int get_stem_from_spec(const char *const buf)
223 {
224 const char *tmp = strchr(buf + 1, '/');
225 const char *ind;
226
227 if (!tmp)
228 return 0;
229
230 for (ind = buf; ind < tmp; ind++) {
231 if (strchr(".^$?*+|[({", (int)*ind))
232 return 0;
233 }
234 return tmp - buf;
235 }
236
237 /*
238 * return the stemid given a string and a length
239 */
find_stem(struct saved_data * data,const char * buf,int stem_len)240 static inline int find_stem(struct saved_data *data, const char *buf, int stem_len)
241 {
242 int i;
243
244 for (i = 0; i < data->num_stems; i++) {
245 if (stem_len == data->stem_arr[i].len &&
246 !strncmp(buf, data->stem_arr[i].buf, stem_len))
247 return i;
248 }
249
250 return -1;
251 }
252
253 /* returns the index of the new stored object */
store_stem(struct saved_data * data,char * buf,int stem_len)254 static inline int store_stem(struct saved_data *data, char *buf, int stem_len)
255 {
256 int num = data->num_stems;
257
258 if (data->alloc_stems == num) {
259 struct stem *tmp_arr;
260
261 data->alloc_stems = data->alloc_stems * 2 + 16;
262 tmp_arr = realloc(data->stem_arr,
263 sizeof(*tmp_arr) * data->alloc_stems);
264 if (!tmp_arr)
265 return -1;
266 data->stem_arr = tmp_arr;
267 }
268 data->stem_arr[num].len = stem_len;
269 data->stem_arr[num].buf = buf;
270 data->num_stems++;
271
272 return num;
273 }
274
275 /* find the stem of a file spec, returns the index into stem_arr for a new
276 * or existing stem, (or -1 if there is no possible stem - IE for a file in
277 * the root directory or a regex that is too complex for us). */
find_stem_from_spec(struct saved_data * data,const char * buf)278 static inline int find_stem_from_spec(struct saved_data *data, const char *buf)
279 {
280 int stem_len = get_stem_from_spec(buf);
281 int stemid;
282 char *stem;
283
284 if (!stem_len)
285 return -1;
286
287 stemid = find_stem(data, buf, stem_len);
288 if (stemid >= 0)
289 return stemid;
290
291 /* not found, allocate a new one */
292 stem = strndup(buf, stem_len);
293 if (!stem)
294 return -1;
295
296 return store_stem(data, stem, stem_len);
297 }
298
299 #endif /* _SELABEL_FILE_H_ */
300