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 context_t con;
147 context_t usercon;
148
149 int rc;
150
151 errno = EINVAL;
152
153 /* Extract the role and type of the fromcon for matching.
154 User identity and MLS range can be variable. */
155 con = context_new(fromcon);
156 if (!con)
157 return -1;
158 fromrole = context_role_get(con);
159 fromtype = context_type_get(con);
160 fromlevel = context_range_get(con);
161 if (!fromrole || !fromtype) {
162 context_free(con);
163 return -1;
164 }
165
166 while ((len = getline(&line, &line_len, fp)) > 0) {
167 if (line[len - 1] == '\n')
168 line[len - 1] = 0;
169
170 /* Skip leading whitespace. */
171 start = line;
172 while (*start && isspace(*start))
173 start++;
174 if (!(*start))
175 continue;
176
177 /* Find the end of the (partial) fromcon in the line. */
178 end = start;
179 while (*end && !isspace(*end))
180 end++;
181 if (!(*end))
182 continue;
183
184 /* Check for a match. */
185 linerole = start;
186 while (*start && !isspace(*start) && *start != ':')
187 start++;
188 if (*start != ':')
189 continue;
190 *start = 0;
191 linetype = ++start;
192 while (*start && !isspace(*start) && *start != ':')
193 start++;
194 if (!(*start))
195 continue;
196 *start = 0;
197 if (!strcmp(fromrole, linerole) && !strcmp(fromtype, linetype)) {
198 found = 1;
199 break;
200 }
201 }
202
203 if (!found) {
204 errno = ENOENT;
205 rc = -1;
206 goto out;
207 }
208
209 start = ++end;
210 while (*start) {
211 /* Skip leading whitespace */
212 while (*start && isspace(*start))
213 start++;
214 if (!(*start))
215 break;
216
217 /* Find the end of this partial context. */
218 end = start;
219 while (*end && !isspace(*end))
220 end++;
221 if (*end)
222 *end++ = 0;
223
224 /* Check whether a new context is valid */
225 if (SIZE_MAX - user_len < strlen(start) + 2) {
226 fprintf(stderr, "%s: one of partial contexts is too big\n", __FUNCTION__);
227 errno = EINVAL;
228 rc = -1;
229 goto out;
230 }
231 usercon_len = user_len + strlen(start) + 2;
232 usercon_str = malloc(usercon_len);
233 if (!usercon_str) {
234 rc = -1;
235 goto out;
236 }
237
238 /* set range from fromcon in the new usercon */
239 snprintf(usercon_str, usercon_len, "%s:%s", user, start);
240 usercon = context_new(usercon_str);
241 if (!usercon) {
242 if (errno != EINVAL) {
243 free(usercon_str);
244 rc = -1;
245 goto out;
246 }
247 fprintf(stderr,
248 "%s: can't create a context from %s, skipping\n",
249 __FUNCTION__, usercon_str);
250 free(usercon_str);
251 start = end;
252 continue;
253 }
254 free(usercon_str);
255 if (context_range_set(usercon, fromlevel) != 0) {
256 context_free(usercon);
257 rc = -1;
258 goto out;
259 }
260 usercon_str = context_str(usercon);
261 if (!usercon_str) {
262 context_free(usercon);
263 rc = -1;
264 goto out;
265 }
266
267 /* check whether usercon is already in reachable */
268 if (is_in_reachable(*reachable, usercon_str)) {
269 context_free(usercon);
270 start = end;
271 continue;
272 }
273 if (security_check_context(usercon_str) == 0) {
274 new_reachable = realloc(*reachable, (*nreachable + 2) * sizeof(char *));
275 if (!new_reachable) {
276 context_free(usercon);
277 rc = -1;
278 goto out;
279 }
280 *reachable = new_reachable;
281 new_reachable[*nreachable] = strdup(usercon_str);
282 if (new_reachable[*nreachable] == NULL) {
283 context_free(usercon);
284 rc = -1;
285 goto out;
286 }
287 new_reachable[*nreachable + 1] = 0;
288 *nreachable += 1;
289 }
290 context_free(usercon);
291 start = end;
292 }
293 rc = 0;
294
295 out:
296 context_free(con);
297 free(line);
298 return rc;
299 }
300
get_failsafe_context(const char * user,char ** newcon)301 static int get_failsafe_context(const char *user, char ** newcon)
302 {
303 FILE *fp;
304 char buf[255], *ptr;
305 size_t plen, nlen;
306 int rc;
307
308 fp = fopen(selinux_failsafe_context_path(), "re");
309 if (!fp)
310 return -1;
311
312 ptr = fgets_unlocked(buf, sizeof buf, fp);
313 fclose(fp);
314
315 if (!ptr)
316 return -1;
317 plen = strlen(ptr);
318 if (buf[plen - 1] == '\n')
319 buf[plen - 1] = 0;
320
321 nlen = strlen(user) + 1 + plen + 1;
322 *newcon = malloc(nlen);
323 if (!(*newcon))
324 return -1;
325 rc = snprintf(*newcon, nlen, "%s:%s", user, ptr);
326 if (rc < 0 || (size_t) rc >= nlen) {
327 free(*newcon);
328 *newcon = 0;
329 return -1;
330 }
331
332 /* If possible, check the context to catch
333 errors early rather than waiting until the
334 caller tries to use setexeccon on the context.
335 But this may not always be possible, e.g. if
336 selinuxfs isn't mounted. */
337 if (security_check_context(*newcon) && errno != ENOENT) {
338 free(*newcon);
339 *newcon = 0;
340 return -1;
341 }
342
343 return 0;
344 }
345
get_ordered_context_list_with_level(const char * user,const char * level,const char * fromcon,char *** list)346 int get_ordered_context_list_with_level(const char *user,
347 const char *level,
348 const char *fromcon,
349 char *** list)
350 {
351 int rc;
352 char *backup_fromcon = NULL;
353 context_t con;
354 const char *newfromcon;
355
356 if (!level)
357 return get_ordered_context_list(user, fromcon, list);
358
359 if (!fromcon) {
360 rc = getcon(&backup_fromcon);
361 if (rc < 0)
362 return rc;
363 fromcon = backup_fromcon;
364 }
365
366 rc = -1;
367 con = context_new(fromcon);
368 if (!con)
369 goto out;
370
371 if (context_range_set(con, level))
372 goto out;
373
374 newfromcon = context_str(con);
375 if (!newfromcon)
376 goto out;
377
378 rc = get_ordered_context_list(user, newfromcon, list);
379
380 out:
381 context_free(con);
382 freecon(backup_fromcon);
383 return rc;
384 }
385
386
get_default_context_with_level(const char * user,const char * level,const char * fromcon,char ** newcon)387 int get_default_context_with_level(const char *user,
388 const char *level,
389 const char *fromcon,
390 char ** newcon)
391 {
392 char **conary;
393 int rc;
394
395 rc = get_ordered_context_list_with_level(user, level, fromcon, &conary);
396 if (rc <= 0)
397 return -1;
398
399 *newcon = strdup(conary[0]);
400 freeconary(conary);
401 if (!(*newcon))
402 return -1;
403 return 0;
404 }
405
get_ordered_context_list(const char * user,const char * fromcon,char *** list)406 int get_ordered_context_list(const char *user,
407 const char *fromcon,
408 char *** list)
409 {
410 char **reachable = NULL;
411 int rc = 0;
412 unsigned nreachable = 0;
413 char *backup_fromcon = NULL;
414 FILE *fp;
415 char *fname = NULL;
416 size_t fname_len;
417 const char *user_contexts_path = selinux_user_contexts_path();
418
419 if (!fromcon) {
420 /* Get the current context and use it for the starting context */
421 rc = getcon(&backup_fromcon);
422 if (rc < 0)
423 return rc;
424 fromcon = backup_fromcon;
425 }
426
427 /* Determine the ordering to apply from the optional per-user config
428 and from the global config. */
429 fname_len = strlen(user_contexts_path) + strlen(user) + 2;
430 fname = malloc(fname_len);
431 if (!fname)
432 goto failsafe;
433 snprintf(fname, fname_len, "%s%s", user_contexts_path, user);
434 fp = fopen(fname, "re");
435 if (fp) {
436 __fsetlocking(fp, FSETLOCKING_BYCALLER);
437 rc = get_context_user(fp, fromcon, user, &reachable, &nreachable);
438
439 fclose(fp);
440 if (rc < 0 && errno != ENOENT) {
441 fprintf(stderr,
442 "%s: error in processing configuration file %s\n",
443 __FUNCTION__, fname);
444 /* Fall through, try global config */
445 }
446 }
447 free(fname);
448 fp = fopen(selinux_default_context_path(), "re");
449 if (fp) {
450 __fsetlocking(fp, FSETLOCKING_BYCALLER);
451 rc = get_context_user(fp, fromcon, user, &reachable, &nreachable);
452 fclose(fp);
453 if (rc < 0 && errno != ENOENT) {
454 fprintf(stderr,
455 "%s: error in processing configuration file %s\n",
456 __FUNCTION__, selinux_default_context_path());
457 /* Fall through */
458 }
459 }
460
461 if (!nreachable)
462 goto failsafe;
463
464 out:
465 if (nreachable > 0) {
466 *list = reachable;
467 rc = nreachable;
468 }
469 else
470 freeconary(reachable);
471
472 freecon(backup_fromcon);
473
474 return rc;
475
476 failsafe:
477 /* Unable to determine a reachable context list, try to fall back to
478 the "failsafe" context to at least permit root login
479 for emergency recovery if possible. */
480 freeconary(reachable);
481 reachable = malloc(2 * sizeof(char *));
482 if (!reachable) {
483 rc = -1;
484 goto out;
485 }
486 reachable[0] = reachable[1] = 0;
487 rc = get_failsafe_context(user, &reachable[0]);
488 if (rc < 0) {
489 freeconary(reachable);
490 reachable = NULL;
491 goto out;
492 }
493 nreachable = 1; /* one context in the list */
494 goto out;
495 }
496
497