aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorJann Horn <jannh@google.com>2018-08-06 17:19:32 -0400
committerPaul Moore <paul@paul-moore.com>2018-09-05 17:47:09 -0400
commit95ffe194204ae3cef88d0b59be209204bbe9b3be (patch)
treea31906d241c0bc898d412423cf97ce8905f0347c /security
parent7bb185edb0306bb90029a5fa6b9cff900ffdbf4b (diff)
selinux: refactor mls_context_to_sid() and make it stricter
The intended behavior change for this patch is to reject any MLS strings that contain (trailing) garbage if p->mls_enabled is true. As suggested by Paul Moore, change mls_context_to_sid() so that the two parts of the range are extracted before the rest of the parsing. Because now we don't have to scan for two different separators simultaneously everywhere, we can actually switch to strchr() everywhere instead of the open-coded loops that scan for two separators at once. mls_context_to_sid() used to signal how much of the input string was parsed by updating `*scontext`. However, there is actually no case in which mls_context_to_sid() only parses a subset of the input and still returns a success (other than the buggy case with a second '-' in which it incorrectly claims to have consumed the entire string). Turn `scontext` into a simple pointer argument and stop redundantly checking whether the entire input was consumed in string_to_context_struct(). This also lets us remove the `scontext_len` argument from `string_to_context_struct()`. Signed-off-by: Jann Horn <jannh@google.com> [PM: minor merge fuzz in convert_context()] Signed-off-by: Paul Moore <paul@paul-moore.com>
Diffstat (limited to 'security')
-rw-r--r--security/selinux/ss/mls.c178
-rw-r--r--security/selinux/ss/mls.h2
-rw-r--r--security/selinux/ss/services.c12
3 files changed, 82 insertions, 110 deletions
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index 39475fb455bc..2fe459df3c85 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -218,9 +218,7 @@ int mls_context_isvalid(struct policydb *p, struct context *c)
218/* 218/*
219 * Set the MLS fields in the security context structure 219 * Set the MLS fields in the security context structure
220 * `context' based on the string representation in 220 * `context' based on the string representation in
221 * the string `*scontext'. Update `*scontext' to 221 * the string `scontext'.
222 * point to the end of the string representation of
223 * the MLS fields.
224 * 222 *
225 * This function modifies the string in place, inserting 223 * This function modifies the string in place, inserting
226 * NULL characters to terminate the MLS fields. 224 * NULL characters to terminate the MLS fields.
@@ -235,22 +233,21 @@ int mls_context_isvalid(struct policydb *p, struct context *c)
235 */ 233 */
236int mls_context_to_sid(struct policydb *pol, 234int mls_context_to_sid(struct policydb *pol,
237 char oldc, 235 char oldc,
238 char **scontext, 236 char *scontext,
239 struct context *context, 237 struct context *context,
240 struct sidtab *s, 238 struct sidtab *s,
241 u32 def_sid) 239 u32 def_sid)
242{ 240{
243 241 char *sensitivity, *cur_cat, *next_cat, *rngptr;
244 char delim;
245 char *scontextp, *p, *rngptr;
246 struct level_datum *levdatum; 242 struct level_datum *levdatum;
247 struct cat_datum *catdatum, *rngdatum; 243 struct cat_datum *catdatum, *rngdatum;
248 int l, rc = -EINVAL; 244 int l, rc, i;
245 char *rangep[2];
249 246
250 if (!pol->mls_enabled) { 247 if (!pol->mls_enabled) {
251 if (def_sid != SECSID_NULL && oldc) 248 if ((def_sid != SECSID_NULL && oldc) || (*scontext) == '\0')
252 *scontext += strlen(*scontext) + 1; 249 return 0;
253 return 0; 250 return -EINVAL;
254 } 251 }
255 252
256 /* 253 /*
@@ -261,113 +258,94 @@ int mls_context_to_sid(struct policydb *pol,
261 struct context *defcon; 258 struct context *defcon;
262 259
263 if (def_sid == SECSID_NULL) 260 if (def_sid == SECSID_NULL)
264 goto out; 261 return -EINVAL;
265 262
266 defcon = sidtab_search(s, def_sid); 263 defcon = sidtab_search(s, def_sid);
267 if (!defcon) 264 if (!defcon)
268 goto out; 265 return -EINVAL;
269 266
270 rc = mls_context_cpy(context, defcon); 267 return mls_context_cpy(context, defcon);
271 goto out;
272 } 268 }
273 269
274 /* Extract low sensitivity. */ 270 /*
275 scontextp = p = *scontext; 271 * If we're dealing with a range, figure out where the two parts
276 while (*p && *p != ':' && *p != '-') 272 * of the range begin.
277 p++; 273 */
278 274 rangep[0] = scontext;
279 delim = *p; 275 rangep[1] = strchr(scontext, '-');
280 if (delim != '\0') 276 if (rangep[1]) {
281 *p++ = '\0'; 277 rangep[1][0] = '\0';
278 rangep[1]++;
279 }
282 280
281 /* For each part of the range: */
283 for (l = 0; l < 2; l++) { 282 for (l = 0; l < 2; l++) {
284 levdatum = hashtab_search(pol->p_levels.table, scontextp); 283 /* Split sensitivity and category set. */
285 if (!levdatum) { 284 sensitivity = rangep[l];
286 rc = -EINVAL; 285 if (sensitivity == NULL)
287 goto out; 286 break;
288 } 287 next_cat = strchr(sensitivity, ':');
288 if (next_cat)
289 *(next_cat++) = '\0';
289 290
291 /* Parse sensitivity. */
292 levdatum = hashtab_search(pol->p_levels.table, sensitivity);
293 if (!levdatum)
294 return -EINVAL;
290 context->range.level[l].sens = levdatum->level->sens; 295 context->range.level[l].sens = levdatum->level->sens;
291 296
292 if (delim == ':') { 297 /* Extract category set. */
293 /* Extract category set. */ 298 while (next_cat != NULL) {
294 while (1) { 299 cur_cat = next_cat;
295 scontextp = p; 300 next_cat = strchr(next_cat, ',');
296 while (*p && *p != ',' && *p != '-') 301 if (next_cat != NULL)
297 p++; 302 *(next_cat++) = '\0';
298 delim = *p; 303
299 if (delim != '\0') 304 /* Separate into range if exists */
300 *p++ = '\0'; 305 rngptr = strchr(cur_cat, '.');
301 306 if (rngptr != NULL) {
302 /* Separate into range if exists */ 307 /* Remove '.' */
303 rngptr = strchr(scontextp, '.'); 308 *rngptr++ = '\0';
304 if (rngptr != NULL) { 309 }
305 /* Remove '.' */
306 *rngptr++ = '\0';
307 }
308 310
309 catdatum = hashtab_search(pol->p_cats.table, 311 catdatum = hashtab_search(pol->p_cats.table, cur_cat);
310 scontextp); 312 if (!catdatum)
311 if (!catdatum) { 313 return -EINVAL;
312 rc = -EINVAL;
313 goto out;
314 }
315 314
316 rc = ebitmap_set_bit(&context->range.level[l].cat, 315 rc = ebitmap_set_bit(&context->range.level[l].cat,
317 catdatum->value - 1, 1); 316 catdatum->value - 1, 1);
318 if (rc) 317 if (rc)
319 goto out; 318 return rc;
320 319
321 /* If range, set all categories in range */ 320 /* If range, set all categories in range */
322 if (rngptr) { 321 if (rngptr == NULL)
323 int i; 322 continue;
324 323
325 rngdatum = hashtab_search(pol->p_cats.table, rngptr); 324 rngdatum = hashtab_search(pol->p_cats.table, rngptr);
326 if (!rngdatum) { 325 if (!rngdatum)
327 rc = -EINVAL; 326 return -EINVAL;
328 goto out; 327
329 } 328 if (catdatum->value >= rngdatum->value)
330 329 return -EINVAL;
331 if (catdatum->value >= rngdatum->value) {
332 rc = -EINVAL;
333 goto out;
334 }
335
336 for (i = catdatum->value; i < rngdatum->value; i++) {
337 rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
338 if (rc)
339 goto out;
340 }
341 }
342 330
343 if (delim != ',') 331 for (i = catdatum->value; i < rngdatum->value; i++) {
344 break; 332 rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
333 if (rc)
334 return rc;
345 } 335 }
346 } 336 }
347 if (delim == '-') {
348 /* Extract high sensitivity. */
349 scontextp = p;
350 while (*p && *p != ':')
351 p++;
352
353 delim = *p;
354 if (delim != '\0')
355 *p++ = '\0';
356 } else
357 break;
358 } 337 }
359 338
360 if (l == 0) { 339 /* If we didn't see a '-', the range start is also the range end. */
340 if (rangep[1] == NULL) {
361 context->range.level[1].sens = context->range.level[0].sens; 341 context->range.level[1].sens = context->range.level[0].sens;
362 rc = ebitmap_cpy(&context->range.level[1].cat, 342 rc = ebitmap_cpy(&context->range.level[1].cat,
363 &context->range.level[0].cat); 343 &context->range.level[0].cat);
364 if (rc) 344 if (rc)
365 goto out; 345 return rc;
366 } 346 }
367 *scontext = ++p; 347
368 rc = 0; 348 return 0;
369out:
370 return rc;
371} 349}
372 350
373/* 351/*
@@ -379,21 +357,19 @@ out:
379int mls_from_string(struct policydb *p, char *str, struct context *context, 357int mls_from_string(struct policydb *p, char *str, struct context *context,
380 gfp_t gfp_mask) 358 gfp_t gfp_mask)
381{ 359{
382 char *tmpstr, *freestr; 360 char *tmpstr;
383 int rc; 361 int rc;
384 362
385 if (!p->mls_enabled) 363 if (!p->mls_enabled)
386 return -EINVAL; 364 return -EINVAL;
387 365
388 /* we need freestr because mls_context_to_sid will change 366 tmpstr = kstrdup(str, gfp_mask);
389 the value of tmpstr */
390 tmpstr = freestr = kstrdup(str, gfp_mask);
391 if (!tmpstr) { 367 if (!tmpstr) {
392 rc = -ENOMEM; 368 rc = -ENOMEM;
393 } else { 369 } else {
394 rc = mls_context_to_sid(p, ':', &tmpstr, context, 370 rc = mls_context_to_sid(p, ':', tmpstr, context,
395 NULL, SECSID_NULL); 371 NULL, SECSID_NULL);
396 kfree(freestr); 372 kfree(tmpstr);
397 } 373 }
398 374
399 return rc; 375 return rc;
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h
index 9a3ff7af70ad..67093647576d 100644
--- a/security/selinux/ss/mls.h
+++ b/security/selinux/ss/mls.h
@@ -34,7 +34,7 @@ int mls_level_isvalid(struct policydb *p, struct mls_level *l);
34 34
35int mls_context_to_sid(struct policydb *p, 35int mls_context_to_sid(struct policydb *p,
36 char oldc, 36 char oldc,
37 char **scontext, 37 char *scontext,
38 struct context *context, 38 struct context *context,
39 struct sidtab *s, 39 struct sidtab *s,
40 u32 def_sid); 40 u32 def_sid);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index f3def298a90e..12e414394530 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -1365,7 +1365,6 @@ int security_sid_to_context_force(struct selinux_state *state, u32 sid,
1365static int string_to_context_struct(struct policydb *pol, 1365static int string_to_context_struct(struct policydb *pol,
1366 struct sidtab *sidtabp, 1366 struct sidtab *sidtabp,
1367 char *scontext, 1367 char *scontext,
1368 u32 scontext_len,
1369 struct context *ctx, 1368 struct context *ctx,
1370 u32 def_sid) 1369 u32 def_sid)
1371{ 1370{
@@ -1426,15 +1425,12 @@ static int string_to_context_struct(struct policydb *pol,
1426 1425
1427 ctx->type = typdatum->value; 1426 ctx->type = typdatum->value;
1428 1427
1429 rc = mls_context_to_sid(pol, oldc, &p, ctx, sidtabp, def_sid); 1428 rc = mls_context_to_sid(pol, oldc, p, ctx, sidtabp, def_sid);
1430 if (rc) 1429 if (rc)
1431 goto out; 1430 goto out;
1432 1431
1433 rc = -EINVAL;
1434 if ((p - scontext) < scontext_len)
1435 goto out;
1436
1437 /* Check the validity of the new context. */ 1432 /* Check the validity of the new context. */
1433 rc = -EINVAL;
1438 if (!policydb_context_isvalid(pol, ctx)) 1434 if (!policydb_context_isvalid(pol, ctx))
1439 goto out; 1435 goto out;
1440 rc = 0; 1436 rc = 0;
@@ -1489,7 +1485,7 @@ static int security_context_to_sid_core(struct selinux_state *state,
1489 policydb = &state->ss->policydb; 1485 policydb = &state->ss->policydb;
1490 sidtab = &state->ss->sidtab; 1486 sidtab = &state->ss->sidtab;
1491 rc = string_to_context_struct(policydb, sidtab, scontext2, 1487 rc = string_to_context_struct(policydb, sidtab, scontext2,
1492 scontext_len, &context, def_sid); 1488 &context, def_sid);
1493 if (rc == -EINVAL && force) { 1489 if (rc == -EINVAL && force) {
1494 context.str = str; 1490 context.str = str;
1495 context.len = strlen(str) + 1; 1491 context.len = strlen(str) + 1;
@@ -1958,7 +1954,7 @@ static int convert_context(u32 key,
1958 goto out; 1954 goto out;
1959 1955
1960 rc = string_to_context_struct(args->newp, NULL, s, 1956 rc = string_to_context_struct(args->newp, NULL, s,
1961 c->len, &ctx, SECSID_NULL); 1957 &ctx, SECSID_NULL);
1962 kfree(s); 1958 kfree(s);
1963 if (!rc) { 1959 if (!rc) {
1964 pr_info("SELinux: Context %s became valid (mapped).\n", 1960 pr_info("SELinux: Context %s became valid (mapped).\n",