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