1 #include <unistd.h>
2 #include <errno.h>
3 #include <stdio.h>
4 #include <stdio_ext.h>
5 #include <stdint.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <ctype.h>
9 #include <pwd.h>
10 #include "selinux_internal.h"
11 #include "context_internal.h"
12 #include "get_context_list_internal.h"
13
get_default_context_with_role(const char * user,const char * role,const char * fromcon,char ** newcon)14 int get_default_context_with_role(const char *user,
15 const char *role,
16 const char *fromcon,
17 char ** newcon)
18 {
19 char **conary;
20 char **ptr;
21 context_t con;
22 const char *role2;
23 int rc;
24
25 rc = get_ordered_context_list(user, fromcon, &conary);
26 if (rc <= 0)
27 return -1;
28
29 for (ptr = conary; *ptr; ptr++) {
30 con = context_new(*ptr);
31 if (!con)
32 continue;
33 role2 = context_role_get(con);
34 if (role2 && !strcmp(role, role2)) {
35 context_free(con);
36 break;
37 }
38 context_free(con);
39 }
40
41 rc = -1;
42 if (!(*ptr)) {
43 errno = EINVAL;
44 goto out;
45 }
46 *newcon = strdup(*ptr);
47 if (!(*newcon))
48 goto out;
49 rc = 0;
50 out:
51 freeconary(conary);
52 return rc;
53 }
54
55
get_default_context_with_rolelevel(const char * user,const char * role,const char * level,const char * fromcon,char ** newcon)56 int get_default_context_with_rolelevel(const char *user,
57 const char *role,
58 const char *level,
59 const char *fromcon,
60 char ** newcon)
61 {
62
63 int rc;
64 char *backup_fromcon = NULL;
65 context_t con;
66 const char *newfromcon;
67
68 if (!level)
69 return get_default_context_with_role(user, role, fromcon,
70 newcon);
71
72 if (!fromcon) {
73 rc = getcon(&backup_fromcon);
74 if (rc < 0)
75 return rc;
76 fromcon = backup_fromcon;
77 }
78
79 rc = -1;
80 con = context_new(fromcon);
81 if (!con)
82 goto out;
83
84 if (context_range_set(con, level))
85 goto out;
86
87 newfromcon = context_str(con);
88 if (!newfromcon)
89 goto out;
90
91 rc = get_default_context_with_role(user, role, newfromcon, newcon);
92
93 out:
94 context_free(con);
95 freecon(backup_fromcon);
96 return rc;
97
98 }
99
get_default_context(const char * user,const char * fromcon,char ** newcon)100 int get_default_context(const char *user,
101 const char *fromcon, char ** newcon)
102 {
103 char **conary;
104 int rc;
105
106 rc = get_ordered_context_list(user, fromcon, &conary);
107 if (rc <= 0)
108 return -1;
109
110 *newcon = strdup(conary[0]);
111 freeconary(conary);
112 if (!(*newcon))
113 return -1;
114 return 0;
115 }
116
is_in_reachable(char ** reachable,const char * usercon_str)117 static int is_in_reachable(char **reachable, const char *usercon_str)
118 {
119 if (!reachable)
120 return 0;
121
122 for (; *reachable != NULL; reachable++) {
123 if (strcmp(*reachable, usercon_str) == 0) {
124 return 1;
125 }
126 }
127 return 0;
128 }
129
get_context_user(FILE * fp,const char * fromcon,const char * user,char *** reachable,unsigned int * nreachable)130 static int get_context_user(FILE * fp,
131 const char * fromcon,
132 const char * user,
133 char ***reachable,
134 unsigned int *nreachable)
135 {
136 char *start, *end = NULL;
137 char *line = NULL;
138 size_t line_len = 0, usercon_len;
139 size_t user_len = strlen(user);
140 ssize_t len;
141 int found = 0;
142 const char *fromrole, *fromtype, *fromlevel;
143 char *linerole, *linetype;
144 char **new_reachable = NULL;
145 char *usercon_str;
146 const char *usercon_str2;
147 context_t con;
148 context_t usercon;
149
150 int rc;
151
152 errno = EINVAL;
153
154 /* Extract the role and type of the fromcon for matching.
155 User identity and MLS range can be variable. */
156 con = context_new(fromcon);
157 if (!con)
158 return -1;
159 fromrole = context_role_get(con);
160 fromtype = context_type_get(con);
161 fromlevel = context_range_get(con);
162 if (!fromrole || !fromtype) {
163 context_free(con);
164 return -1;
165 }
166
167 while ((len = getline(&line, &line_len, fp)) > 0) {
168 if (line[len - 1] == '\n')
169 line[len - 1] = 0;
170
171 /* Skip leading whitespace. */
172 start = line;
173 while (*start && isspace(*start))
174 start++;
175 if (!(*start))
176 continue;
177
178 /* Find the end of the (partial) fromcon in the line. */
179 end = start;
180 while (*end && !isspace(*end))
181 end++;
182 if (!(*end))
183 continue;
184
185 /* Check for a match. */
186 linerole = start;
187 while (*start && !isspace(*start) && *start != ':')
188 start++;
189 if (*start != ':')
190 continue;
191 *start = 0;
192 linetype = ++start;
193 while (*start && !isspace(*start) && *start != ':')
194 start++;
195 if (!(*start))
196 continue;
197 *start = 0;
198 if (!strcmp(fromrole, linerole) && !strcmp(fromtype, linetype)) {
199 found = 1;
200 break;
201 }
202 }
203
204 if (!found) {
205 errno = ENOENT;
206 rc = -1;
207 goto out;
208 }
209
210 start = ++end;
211 while (*start) {
212 /* Skip leading whitespace */
213 while (*start && isspace(*start))
214 start++;
215 if (!(*start))
216 break;
217
218 /* Find the end of this partial context. */
219 end = start;
220 while (*end && !isspace(*end))
221 end++;
222 if (*end)
223 *end++ = 0;
224
225 /* Check whether a new context is valid */
226 if (SIZE_MAX - user_len < strlen(start) + 2) {
227 fprintf(stderr, "%s: one of partial contexts is too big\n", __FUNCTION__);
228 errno = EINVAL;
229 rc = -1;
230 goto out;
231 }
232 usercon_len = user_len + strlen(start) + 2;
233 usercon_str = malloc(usercon_len);
234 if (!usercon_str) {
235 rc = -1;
236 goto out;
237 }
238
239 /* set range from fromcon in the new usercon */
240 snprintf(usercon_str, usercon_len, "%s:%s", user, start);
241 usercon = context_new(usercon_str);
242 if (!usercon) {
243 if (errno != EINVAL) {
244 free(usercon_str);
245 rc = -1;
246 goto out;
247 }
248 fprintf(stderr,
249 "%s: can't create a context from %s, skipping\n",
250 __FUNCTION__, usercon_str);
251 free(usercon_str);
252 start = end;
253 continue;
254 }
255 free(usercon_str);
256 if (context_range_set(usercon, fromlevel) != 0) {
257 context_free(usercon);
258 rc = -1;
259 goto out;
260 }
261 usercon_str2 = context_str(usercon);
262 if (!usercon_str2) {
263 context_free(usercon);
264 rc = -1;
265 goto out;
266 }
267
268 /* check whether usercon is already in reachable */
269 if (is_in_reachable(*reachable, usercon_str2)) {
270 context_free(usercon);
271 start = end;
272 continue;
273 }
274 if (security_check_context(usercon_str2) == 0) {
275 new_reachable = realloc(*reachable, (*nreachable + 2) * sizeof(char *));
276 if (!new_reachable) {
277 context_free(usercon);
278 rc = -1;
279 goto out;
280 }
281 *reachable = new_reachable;
282 new_reachable[*nreachable] = strdup(usercon_str2);
283 if (new_reachable[*nreachable] == NULL) {
284 context_free(usercon);
285 rc = -1;
286 goto out;
287 }
288 new_reachable[*nreachable + 1] = 0;
289 *nreachable += 1;
290 }
291 context_free(usercon);
292 start = end;
293 }
294 rc = 0;
295
296 out:
297 context_free(con);
298 free(line);
299 return rc;
300 }
301
get_failsafe_context(const char * user,char ** newcon)302 static int get_failsafe_context(const char *user, char ** newcon)
303 {
304 FILE *fp;
305 char buf[255], *ptr;
306 size_t plen, nlen;
307 int rc;
308
309 fp = fopen(selinux_failsafe_context_path(), "re");
310 if (!fp)
311 return -1;
312
313 ptr = fgets_unlocked(buf, sizeof buf, fp);
314 fclose(fp);
315
316 if (!ptr)
317 return -1;
318 plen = strlen(ptr);
319 if (buf[plen - 1] == '\n')
320 buf[plen - 1] = 0;
321
322 nlen = strlen(user) + 1 + plen + 1;
323 *newcon = malloc(nlen);
324 if (!(*newcon))
325 return -1;
326 rc = snprintf(*newcon, nlen, "%s:%s", user, ptr);
327 if (rc < 0 || (size_t) rc >= nlen) {
328 free(*newcon);
329 *newcon = 0;
330 return -1;
331 }
332
333 /* If possible, check the context to catch
334 errors early rather than waiting until the
335 caller tries to use setexeccon on the context.
336 But this may not always be possible, e.g. if
337 selinuxfs isn't mounted. */
338 if (security_check_context(*newcon) && errno != ENOENT) {
339 free(*newcon);
340 *newcon = 0;
341 return -1;
342 }
343
344 return 0;
345 }
346
get_ordered_context_list_with_level(const char * user,const char * level,const char * fromcon,char *** list)347 int get_ordered_context_list_with_level(const char *user,
348 const char *level,
349 const char *fromcon,
350 char *** list)
351 {
352 int rc;
353 char *backup_fromcon = NULL;
354 context_t con;
355 const char *newfromcon;
356
357 if (!level)
358 return get_ordered_context_list(user, fromcon, list);
359
360 if (!fromcon) {
361 rc = getcon(&backup_fromcon);
362 if (rc < 0)
363 return rc;
364 fromcon = backup_fromcon;
365 }
366
367 rc = -1;
368 con = context_new(fromcon);
369 if (!con)
370 goto out;
371
372 if (context_range_set(con, level))
373 goto out;
374
375 newfromcon = context_str(con);
376 if (!newfromcon)
377 goto out;
378
379 rc = get_ordered_context_list(user, newfromcon, list);
380
381 out:
382 context_free(con);
383 freecon(backup_fromcon);
384 return rc;
385 }
386
387
get_default_context_with_level(const char * user,const char * level,const char * fromcon,char ** newcon)388 int get_default_context_with_level(const char *user,
389 const char *level,
390 const char *fromcon,
391 char ** newcon)
392 {
393 char **conary;
394 int rc;
395
396 rc = get_ordered_context_list_with_level(user, level, fromcon, &conary);
397 if (rc <= 0)
398 return -1;
399
400 *newcon = strdup(conary[0]);
401 freeconary(conary);
402 if (!(*newcon))
403 return -1;
404 return 0;
405 }
406
get_ordered_context_list(const char * user,const char * fromcon,char *** list)407 int get_ordered_context_list(const char *user,
408 const char *fromcon,
409 char *** list)
410 {
411 char **reachable = NULL;
412 int rc = 0;
413 unsigned nreachable = 0;
414 char *backup_fromcon = NULL;
415 FILE *fp;
416 char *fname = NULL;
417 size_t fname_len;
418 const char *user_contexts_path = selinux_user_contexts_path();
419
420 if (!fromcon) {
421 /* Get the current context and use it for the starting context */
422 rc = getcon(&backup_fromcon);
423 if (rc < 0)
424 return rc;
425 fromcon = backup_fromcon;
426 }
427
428 /* Determine the ordering to apply from the optional per-user config
429 and from the global config. */
430 fname_len = strlen(user_contexts_path) + strlen(user) + 2;
431 fname = malloc(fname_len);
432 if (!fname)
433 goto failsafe;
434 snprintf(fname, fname_len, "%s%s", user_contexts_path, user);
435 fp = fopen(fname, "re");
436 if (fp) {
437 __fsetlocking(fp, FSETLOCKING_BYCALLER);
438 rc = get_context_user(fp, fromcon, user, &reachable, &nreachable);
439
440 fclose(fp);
441 if (rc < 0 && errno != ENOENT) {
442 fprintf(stderr,
443 "%s: error in processing configuration file %s\n",
444 __FUNCTION__, fname);
445 /* Fall through, try global config */
446 }
447 }
448 free(fname);
449 fp = fopen(selinux_default_context_path(), "re");
450 if (fp) {
451 __fsetlocking(fp, FSETLOCKING_BYCALLER);
452 rc = get_context_user(fp, fromcon, user, &reachable, &nreachable);
453 fclose(fp);
454 if (rc < 0 && errno != ENOENT) {
455 fprintf(stderr,
456 "%s: error in processing configuration file %s\n",
457 __FUNCTION__, selinux_default_context_path());
458 /* Fall through */
459 }
460 }
461
462 if (!nreachable)
463 goto failsafe;
464
465 out:
466 if (nreachable > 0) {
467 *list = reachable;
468 rc = nreachable;
469 }
470 else
471 freeconary(reachable);
472
473 freecon(backup_fromcon);
474
475 return rc;
476
477 failsafe:
478 /* Unable to determine a reachable context list, try to fall back to
479 the "failsafe" context to at least permit root login
480 for emergency recovery if possible. */
481 freeconary(reachable);
482 reachable = malloc(2 * sizeof(char *));
483 if (!reachable) {
484 rc = -1;
485 goto out;
486 }
487 reachable[0] = reachable[1] = 0;
488 rc = get_failsafe_context(user, &reachable[0]);
489 if (rc < 0) {
490 freeconary(reachable);
491 reachable = NULL;
492 goto out;
493 }
494 nreachable = 1; /* one context in the list */
495 goto out;
496 }
497
498