diff options
Diffstat (limited to 'security/selinux')
-rw-r--r-- | security/selinux/hooks.c | 2 | ||||
-rw-r--r-- | security/selinux/include/security.h | 2 | ||||
-rw-r--r-- | security/selinux/ss/mls.c | 24 | ||||
-rw-r--r-- | security/selinux/ss/mls.h | 3 | ||||
-rw-r--r-- | security/selinux/ss/policydb.c | 61 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 222 | ||||
-rw-r--r-- | security/selinux/ss/services.h | 2 | ||||
-rw-r--r-- | security/selinux/ss/sidtab.c | 609 | ||||
-rw-r--r-- | security/selinux/ss/sidtab.h | 96 |
9 files changed, 625 insertions, 396 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index a67459eb62d5..0f27db6d94a9 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -2934,7 +2934,7 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data) | |||
2934 | return rc; | 2934 | return rc; |
2935 | 2935 | ||
2936 | /* Allow all mounts performed by the kernel */ | 2936 | /* Allow all mounts performed by the kernel */ |
2937 | if (flags & MS_KERNMOUNT) | 2937 | if (flags & (MS_KERNMOUNT | MS_SUBMOUNT)) |
2938 | return 0; | 2938 | return 0; |
2939 | 2939 | ||
2940 | ad.type = LSM_AUDIT_DATA_DENTRY; | 2940 | ad.type = LSM_AUDIT_DATA_DENTRY; |
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 23e762d529fa..ba8eedf42b90 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h | |||
@@ -81,7 +81,7 @@ enum { | |||
81 | }; | 81 | }; |
82 | #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1) | 82 | #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1) |
83 | 83 | ||
84 | extern char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX]; | 84 | extern const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX]; |
85 | 85 | ||
86 | /* | 86 | /* |
87 | * type_datum properties | 87 | * type_datum properties |
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index b7efa2296969..5e05f5b902d7 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c | |||
@@ -440,16 +440,17 @@ int mls_setup_user_range(struct policydb *p, | |||
440 | 440 | ||
441 | /* | 441 | /* |
442 | * Convert the MLS fields in the security context | 442 | * Convert the MLS fields in the security context |
443 | * structure `c' from the values specified in the | 443 | * structure `oldc' from the values specified in the |
444 | * policy `oldp' to the values specified in the policy `newp'. | 444 | * policy `oldp' to the values specified in the policy `newp', |
445 | * storing the resulting context in `newc'. | ||
445 | */ | 446 | */ |
446 | int mls_convert_context(struct policydb *oldp, | 447 | int mls_convert_context(struct policydb *oldp, |
447 | struct policydb *newp, | 448 | struct policydb *newp, |
448 | struct context *c) | 449 | struct context *oldc, |
450 | struct context *newc) | ||
449 | { | 451 | { |
450 | struct level_datum *levdatum; | 452 | struct level_datum *levdatum; |
451 | struct cat_datum *catdatum; | 453 | struct cat_datum *catdatum; |
452 | struct ebitmap bitmap; | ||
453 | struct ebitmap_node *node; | 454 | struct ebitmap_node *node; |
454 | int l, i; | 455 | int l, i; |
455 | 456 | ||
@@ -459,28 +460,25 @@ int mls_convert_context(struct policydb *oldp, | |||
459 | for (l = 0; l < 2; l++) { | 460 | for (l = 0; l < 2; l++) { |
460 | levdatum = hashtab_search(newp->p_levels.table, | 461 | levdatum = hashtab_search(newp->p_levels.table, |
461 | sym_name(oldp, SYM_LEVELS, | 462 | sym_name(oldp, SYM_LEVELS, |
462 | c->range.level[l].sens - 1)); | 463 | oldc->range.level[l].sens - 1)); |
463 | 464 | ||
464 | if (!levdatum) | 465 | if (!levdatum) |
465 | return -EINVAL; | 466 | return -EINVAL; |
466 | c->range.level[l].sens = levdatum->level->sens; | 467 | newc->range.level[l].sens = levdatum->level->sens; |
467 | 468 | ||
468 | ebitmap_init(&bitmap); | 469 | ebitmap_for_each_positive_bit(&oldc->range.level[l].cat, |
469 | ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) { | 470 | node, i) { |
470 | int rc; | 471 | int rc; |
471 | 472 | ||
472 | catdatum = hashtab_search(newp->p_cats.table, | 473 | catdatum = hashtab_search(newp->p_cats.table, |
473 | sym_name(oldp, SYM_CATS, i)); | 474 | sym_name(oldp, SYM_CATS, i)); |
474 | if (!catdatum) | 475 | if (!catdatum) |
475 | return -EINVAL; | 476 | return -EINVAL; |
476 | rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1); | 477 | rc = ebitmap_set_bit(&newc->range.level[l].cat, |
478 | catdatum->value - 1, 1); | ||
477 | if (rc) | 479 | if (rc) |
478 | return rc; | 480 | return rc; |
479 | |||
480 | cond_resched(); | ||
481 | } | 481 | } |
482 | ebitmap_destroy(&c->range.level[l].cat); | ||
483 | c->range.level[l].cat = bitmap; | ||
484 | } | 482 | } |
485 | 483 | ||
486 | return 0; | 484 | return 0; |
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h index 67093647576d..7954b1e60b64 100644 --- a/security/selinux/ss/mls.h +++ b/security/selinux/ss/mls.h | |||
@@ -46,7 +46,8 @@ int mls_range_set(struct context *context, struct mls_range *range); | |||
46 | 46 | ||
47 | int mls_convert_context(struct policydb *oldp, | 47 | int mls_convert_context(struct policydb *oldp, |
48 | struct policydb *newp, | 48 | struct policydb *newp, |
49 | struct context *context); | 49 | struct context *oldc, |
50 | struct context *newc); | ||
50 | 51 | ||
51 | int mls_compute_sid(struct policydb *p, | 52 | int mls_compute_sid(struct policydb *p, |
52 | struct context *scontext, | 53 | struct context *scontext, |
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index f4eadd3f7350..a50d625e7946 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c | |||
@@ -909,13 +909,21 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s) | |||
909 | if (!c->context[0].user) { | 909 | if (!c->context[0].user) { |
910 | pr_err("SELinux: SID %s was never defined.\n", | 910 | pr_err("SELinux: SID %s was never defined.\n", |
911 | c->u.name); | 911 | c->u.name); |
912 | sidtab_destroy(s); | ||
913 | goto out; | ||
914 | } | ||
915 | if (c->sid[0] == SECSID_NULL || c->sid[0] > SECINITSID_NUM) { | ||
916 | pr_err("SELinux: Initial SID %s out of range.\n", | ||
917 | c->u.name); | ||
918 | sidtab_destroy(s); | ||
912 | goto out; | 919 | goto out; |
913 | } | 920 | } |
914 | 921 | ||
915 | rc = sidtab_insert(s, c->sid[0], &c->context[0]); | 922 | rc = sidtab_set_initial(s, c->sid[0], &c->context[0]); |
916 | if (rc) { | 923 | if (rc) { |
917 | pr_err("SELinux: unable to load initial SID %s.\n", | 924 | pr_err("SELinux: unable to load initial SID %s.\n", |
918 | c->u.name); | 925 | c->u.name); |
926 | sidtab_destroy(s); | ||
919 | goto out; | 927 | goto out; |
920 | } | 928 | } |
921 | } | 929 | } |
@@ -2108,6 +2116,7 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, | |||
2108 | { | 2116 | { |
2109 | int i, j, rc; | 2117 | int i, j, rc; |
2110 | u32 nel, len; | 2118 | u32 nel, len; |
2119 | __be64 prefixbuf[1]; | ||
2111 | __le32 buf[3]; | 2120 | __le32 buf[3]; |
2112 | struct ocontext *l, *c; | 2121 | struct ocontext *l, *c; |
2113 | u32 nodebuf[8]; | 2122 | u32 nodebuf[8]; |
@@ -2217,21 +2226,30 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, | |||
2217 | goto out; | 2226 | goto out; |
2218 | break; | 2227 | break; |
2219 | } | 2228 | } |
2220 | case OCON_IBPKEY: | 2229 | case OCON_IBPKEY: { |
2221 | rc = next_entry(nodebuf, fp, sizeof(u32) * 4); | 2230 | u32 pkey_lo, pkey_hi; |
2231 | |||
2232 | rc = next_entry(prefixbuf, fp, sizeof(u64)); | ||
2233 | if (rc) | ||
2234 | goto out; | ||
2235 | |||
2236 | /* we need to have subnet_prefix in CPU order */ | ||
2237 | c->u.ibpkey.subnet_prefix = be64_to_cpu(prefixbuf[0]); | ||
2238 | |||
2239 | rc = next_entry(buf, fp, sizeof(u32) * 2); | ||
2222 | if (rc) | 2240 | if (rc) |
2223 | goto out; | 2241 | goto out; |
2224 | 2242 | ||
2225 | c->u.ibpkey.subnet_prefix = be64_to_cpu(*((__be64 *)nodebuf)); | 2243 | pkey_lo = le32_to_cpu(buf[0]); |
2244 | pkey_hi = le32_to_cpu(buf[1]); | ||
2226 | 2245 | ||
2227 | if (nodebuf[2] > 0xffff || | 2246 | if (pkey_lo > U16_MAX || pkey_hi > U16_MAX) { |
2228 | nodebuf[3] > 0xffff) { | ||
2229 | rc = -EINVAL; | 2247 | rc = -EINVAL; |
2230 | goto out; | 2248 | goto out; |
2231 | } | 2249 | } |
2232 | 2250 | ||
2233 | c->u.ibpkey.low_pkey = le32_to_cpu(nodebuf[2]); | 2251 | c->u.ibpkey.low_pkey = pkey_lo; |
2234 | c->u.ibpkey.high_pkey = le32_to_cpu(nodebuf[3]); | 2252 | c->u.ibpkey.high_pkey = pkey_hi; |
2235 | 2253 | ||
2236 | rc = context_read_and_validate(&c->context[0], | 2254 | rc = context_read_and_validate(&c->context[0], |
2237 | p, | 2255 | p, |
@@ -2239,7 +2257,10 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, | |||
2239 | if (rc) | 2257 | if (rc) |
2240 | goto out; | 2258 | goto out; |
2241 | break; | 2259 | break; |
2242 | case OCON_IBENDPORT: | 2260 | } |
2261 | case OCON_IBENDPORT: { | ||
2262 | u32 port; | ||
2263 | |||
2243 | rc = next_entry(buf, fp, sizeof(u32) * 2); | 2264 | rc = next_entry(buf, fp, sizeof(u32) * 2); |
2244 | if (rc) | 2265 | if (rc) |
2245 | goto out; | 2266 | goto out; |
@@ -2249,12 +2270,13 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, | |||
2249 | if (rc) | 2270 | if (rc) |
2250 | goto out; | 2271 | goto out; |
2251 | 2272 | ||
2252 | if (buf[1] > 0xff || buf[1] == 0) { | 2273 | port = le32_to_cpu(buf[1]); |
2274 | if (port > U8_MAX || port == 0) { | ||
2253 | rc = -EINVAL; | 2275 | rc = -EINVAL; |
2254 | goto out; | 2276 | goto out; |
2255 | } | 2277 | } |
2256 | 2278 | ||
2257 | c->u.ibendport.port = le32_to_cpu(buf[1]); | 2279 | c->u.ibendport.port = port; |
2258 | 2280 | ||
2259 | rc = context_read_and_validate(&c->context[0], | 2281 | rc = context_read_and_validate(&c->context[0], |
2260 | p, | 2282 | p, |
@@ -2262,7 +2284,8 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, | |||
2262 | if (rc) | 2284 | if (rc) |
2263 | goto out; | 2285 | goto out; |
2264 | break; | 2286 | break; |
2265 | } | 2287 | } /* end case */ |
2288 | } /* end switch */ | ||
2266 | } | 2289 | } |
2267 | } | 2290 | } |
2268 | rc = 0; | 2291 | rc = 0; |
@@ -3105,6 +3128,7 @@ static int ocontext_write(struct policydb *p, struct policydb_compat_info *info, | |||
3105 | { | 3128 | { |
3106 | unsigned int i, j, rc; | 3129 | unsigned int i, j, rc; |
3107 | size_t nel, len; | 3130 | size_t nel, len; |
3131 | __be64 prefixbuf[1]; | ||
3108 | __le32 buf[3]; | 3132 | __le32 buf[3]; |
3109 | u32 nodebuf[8]; | 3133 | u32 nodebuf[8]; |
3110 | struct ocontext *c; | 3134 | struct ocontext *c; |
@@ -3192,12 +3216,17 @@ static int ocontext_write(struct policydb *p, struct policydb_compat_info *info, | |||
3192 | return rc; | 3216 | return rc; |
3193 | break; | 3217 | break; |
3194 | case OCON_IBPKEY: | 3218 | case OCON_IBPKEY: |
3195 | *((__be64 *)nodebuf) = cpu_to_be64(c->u.ibpkey.subnet_prefix); | 3219 | /* subnet_prefix is in CPU order */ |
3220 | prefixbuf[0] = cpu_to_be64(c->u.ibpkey.subnet_prefix); | ||
3196 | 3221 | ||
3197 | nodebuf[2] = cpu_to_le32(c->u.ibpkey.low_pkey); | 3222 | rc = put_entry(prefixbuf, sizeof(u64), 1, fp); |
3198 | nodebuf[3] = cpu_to_le32(c->u.ibpkey.high_pkey); | 3223 | if (rc) |
3224 | return rc; | ||
3199 | 3225 | ||
3200 | rc = put_entry(nodebuf, sizeof(u32), 4, fp); | 3226 | buf[0] = cpu_to_le32(c->u.ibpkey.low_pkey); |
3227 | buf[1] = cpu_to_le32(c->u.ibpkey.high_pkey); | ||
3228 | |||
3229 | rc = put_entry(buf, sizeof(u32), 2, fp); | ||
3201 | if (rc) | 3230 | if (rc) |
3202 | return rc; | 3231 | return rc; |
3203 | rc = context_write(p, &c->context[0], fp); | 3232 | rc = context_write(p, &c->context[0], fp); |
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 12e414394530..dd44126c8d14 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c | |||
@@ -71,7 +71,7 @@ | |||
71 | #include "audit.h" | 71 | #include "audit.h" |
72 | 72 | ||
73 | /* Policy capability names */ | 73 | /* Policy capability names */ |
74 | char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = { | 74 | const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = { |
75 | "network_peer_controls", | 75 | "network_peer_controls", |
76 | "open_perms", | 76 | "open_perms", |
77 | "extended_socket_class", | 77 | "extended_socket_class", |
@@ -776,7 +776,7 @@ static int security_compute_validatetrans(struct selinux_state *state, | |||
776 | read_lock(&state->ss->policy_rwlock); | 776 | read_lock(&state->ss->policy_rwlock); |
777 | 777 | ||
778 | policydb = &state->ss->policydb; | 778 | policydb = &state->ss->policydb; |
779 | sidtab = &state->ss->sidtab; | 779 | sidtab = state->ss->sidtab; |
780 | 780 | ||
781 | if (!user) | 781 | if (!user) |
782 | tclass = unmap_class(&state->ss->map, orig_tclass); | 782 | tclass = unmap_class(&state->ss->map, orig_tclass); |
@@ -876,7 +876,7 @@ int security_bounded_transition(struct selinux_state *state, | |||
876 | read_lock(&state->ss->policy_rwlock); | 876 | read_lock(&state->ss->policy_rwlock); |
877 | 877 | ||
878 | policydb = &state->ss->policydb; | 878 | policydb = &state->ss->policydb; |
879 | sidtab = &state->ss->sidtab; | 879 | sidtab = state->ss->sidtab; |
880 | 880 | ||
881 | rc = -EINVAL; | 881 | rc = -EINVAL; |
882 | old_context = sidtab_search(sidtab, old_sid); | 882 | old_context = sidtab_search(sidtab, old_sid); |
@@ -1034,7 +1034,7 @@ void security_compute_xperms_decision(struct selinux_state *state, | |||
1034 | goto allow; | 1034 | goto allow; |
1035 | 1035 | ||
1036 | policydb = &state->ss->policydb; | 1036 | policydb = &state->ss->policydb; |
1037 | sidtab = &state->ss->sidtab; | 1037 | sidtab = state->ss->sidtab; |
1038 | 1038 | ||
1039 | scontext = sidtab_search(sidtab, ssid); | 1039 | scontext = sidtab_search(sidtab, ssid); |
1040 | if (!scontext) { | 1040 | if (!scontext) { |
@@ -1123,7 +1123,7 @@ void security_compute_av(struct selinux_state *state, | |||
1123 | goto allow; | 1123 | goto allow; |
1124 | 1124 | ||
1125 | policydb = &state->ss->policydb; | 1125 | policydb = &state->ss->policydb; |
1126 | sidtab = &state->ss->sidtab; | 1126 | sidtab = state->ss->sidtab; |
1127 | 1127 | ||
1128 | scontext = sidtab_search(sidtab, ssid); | 1128 | scontext = sidtab_search(sidtab, ssid); |
1129 | if (!scontext) { | 1129 | if (!scontext) { |
@@ -1177,7 +1177,7 @@ void security_compute_av_user(struct selinux_state *state, | |||
1177 | goto allow; | 1177 | goto allow; |
1178 | 1178 | ||
1179 | policydb = &state->ss->policydb; | 1179 | policydb = &state->ss->policydb; |
1180 | sidtab = &state->ss->sidtab; | 1180 | sidtab = state->ss->sidtab; |
1181 | 1181 | ||
1182 | scontext = sidtab_search(sidtab, ssid); | 1182 | scontext = sidtab_search(sidtab, ssid); |
1183 | if (!scontext) { | 1183 | if (!scontext) { |
@@ -1315,7 +1315,7 @@ static int security_sid_to_context_core(struct selinux_state *state, | |||
1315 | } | 1315 | } |
1316 | read_lock(&state->ss->policy_rwlock); | 1316 | read_lock(&state->ss->policy_rwlock); |
1317 | policydb = &state->ss->policydb; | 1317 | policydb = &state->ss->policydb; |
1318 | sidtab = &state->ss->sidtab; | 1318 | sidtab = state->ss->sidtab; |
1319 | if (force) | 1319 | if (force) |
1320 | context = sidtab_search_force(sidtab, sid); | 1320 | context = sidtab_search_force(sidtab, sid); |
1321 | else | 1321 | else |
@@ -1483,7 +1483,7 @@ static int security_context_to_sid_core(struct selinux_state *state, | |||
1483 | } | 1483 | } |
1484 | read_lock(&state->ss->policy_rwlock); | 1484 | read_lock(&state->ss->policy_rwlock); |
1485 | policydb = &state->ss->policydb; | 1485 | policydb = &state->ss->policydb; |
1486 | sidtab = &state->ss->sidtab; | 1486 | sidtab = state->ss->sidtab; |
1487 | rc = string_to_context_struct(policydb, sidtab, scontext2, | 1487 | rc = string_to_context_struct(policydb, sidtab, scontext2, |
1488 | &context, def_sid); | 1488 | &context, def_sid); |
1489 | if (rc == -EINVAL && force) { | 1489 | if (rc == -EINVAL && force) { |
@@ -1668,7 +1668,7 @@ static int security_compute_sid(struct selinux_state *state, | |||
1668 | } | 1668 | } |
1669 | 1669 | ||
1670 | policydb = &state->ss->policydb; | 1670 | policydb = &state->ss->policydb; |
1671 | sidtab = &state->ss->sidtab; | 1671 | sidtab = state->ss->sidtab; |
1672 | 1672 | ||
1673 | scontext = sidtab_search(sidtab, ssid); | 1673 | scontext = sidtab_search(sidtab, ssid); |
1674 | if (!scontext) { | 1674 | if (!scontext) { |
@@ -1880,19 +1880,6 @@ int security_change_sid(struct selinux_state *state, | |||
1880 | out_sid, false); | 1880 | out_sid, false); |
1881 | } | 1881 | } |
1882 | 1882 | ||
1883 | /* Clone the SID into the new SID table. */ | ||
1884 | static int clone_sid(u32 sid, | ||
1885 | struct context *context, | ||
1886 | void *arg) | ||
1887 | { | ||
1888 | struct sidtab *s = arg; | ||
1889 | |||
1890 | if (sid > SECINITSID_NUM) | ||
1891 | return sidtab_insert(s, sid, context); | ||
1892 | else | ||
1893 | return 0; | ||
1894 | } | ||
1895 | |||
1896 | static inline int convert_context_handle_invalid_context( | 1883 | static inline int convert_context_handle_invalid_context( |
1897 | struct selinux_state *state, | 1884 | struct selinux_state *state, |
1898 | struct context *context) | 1885 | struct context *context) |
@@ -1920,101 +1907,84 @@ struct convert_context_args { | |||
1920 | 1907 | ||
1921 | /* | 1908 | /* |
1922 | * Convert the values in the security context | 1909 | * Convert the values in the security context |
1923 | * structure `c' from the values specified | 1910 | * structure `oldc' from the values specified |
1924 | * in the policy `p->oldp' to the values specified | 1911 | * in the policy `p->oldp' to the values specified |
1925 | * in the policy `p->newp'. Verify that the | 1912 | * in the policy `p->newp', storing the new context |
1926 | * context is valid under the new policy. | 1913 | * in `newc'. Verify that the context is valid |
1914 | * under the new policy. | ||
1927 | */ | 1915 | */ |
1928 | static int convert_context(u32 key, | 1916 | static int convert_context(struct context *oldc, struct context *newc, void *p) |
1929 | struct context *c, | ||
1930 | void *p) | ||
1931 | { | 1917 | { |
1932 | struct convert_context_args *args; | 1918 | struct convert_context_args *args; |
1933 | struct context oldc; | ||
1934 | struct ocontext *oc; | 1919 | struct ocontext *oc; |
1935 | struct mls_range *range; | ||
1936 | struct role_datum *role; | 1920 | struct role_datum *role; |
1937 | struct type_datum *typdatum; | 1921 | struct type_datum *typdatum; |
1938 | struct user_datum *usrdatum; | 1922 | struct user_datum *usrdatum; |
1939 | char *s; | 1923 | char *s; |
1940 | u32 len; | 1924 | u32 len; |
1941 | int rc = 0; | 1925 | int rc; |
1942 | |||
1943 | if (key <= SECINITSID_NUM) | ||
1944 | goto out; | ||
1945 | 1926 | ||
1946 | args = p; | 1927 | args = p; |
1947 | 1928 | ||
1948 | if (c->str) { | 1929 | if (oldc->str) { |
1949 | struct context ctx; | 1930 | s = kstrdup(oldc->str, GFP_KERNEL); |
1950 | |||
1951 | rc = -ENOMEM; | ||
1952 | s = kstrdup(c->str, GFP_KERNEL); | ||
1953 | if (!s) | 1931 | if (!s) |
1954 | goto out; | 1932 | return -ENOMEM; |
1955 | 1933 | ||
1956 | rc = string_to_context_struct(args->newp, NULL, s, | 1934 | rc = string_to_context_struct(args->newp, NULL, s, |
1957 | &ctx, SECSID_NULL); | 1935 | newc, SECSID_NULL); |
1958 | kfree(s); | 1936 | if (rc == -EINVAL) { |
1959 | if (!rc) { | ||
1960 | pr_info("SELinux: Context %s became valid (mapped).\n", | ||
1961 | c->str); | ||
1962 | /* Replace string with mapped representation. */ | ||
1963 | kfree(c->str); | ||
1964 | memcpy(c, &ctx, sizeof(*c)); | ||
1965 | goto out; | ||
1966 | } else if (rc == -EINVAL) { | ||
1967 | /* Retain string representation for later mapping. */ | 1937 | /* Retain string representation for later mapping. */ |
1968 | rc = 0; | 1938 | context_init(newc); |
1969 | goto out; | 1939 | newc->str = s; |
1970 | } else { | 1940 | newc->len = oldc->len; |
1941 | return 0; | ||
1942 | } | ||
1943 | kfree(s); | ||
1944 | if (rc) { | ||
1971 | /* Other error condition, e.g. ENOMEM. */ | 1945 | /* Other error condition, e.g. ENOMEM. */ |
1972 | pr_err("SELinux: Unable to map context %s, rc = %d.\n", | 1946 | pr_err("SELinux: Unable to map context %s, rc = %d.\n", |
1973 | c->str, -rc); | 1947 | oldc->str, -rc); |
1974 | goto out; | 1948 | return rc; |
1975 | } | 1949 | } |
1950 | pr_info("SELinux: Context %s became valid (mapped).\n", | ||
1951 | oldc->str); | ||
1952 | return 0; | ||
1976 | } | 1953 | } |
1977 | 1954 | ||
1978 | rc = context_cpy(&oldc, c); | 1955 | context_init(newc); |
1979 | if (rc) | ||
1980 | goto out; | ||
1981 | 1956 | ||
1982 | /* Convert the user. */ | 1957 | /* Convert the user. */ |
1983 | rc = -EINVAL; | 1958 | rc = -EINVAL; |
1984 | usrdatum = hashtab_search(args->newp->p_users.table, | 1959 | usrdatum = hashtab_search(args->newp->p_users.table, |
1985 | sym_name(args->oldp, SYM_USERS, c->user - 1)); | 1960 | sym_name(args->oldp, |
1961 | SYM_USERS, oldc->user - 1)); | ||
1986 | if (!usrdatum) | 1962 | if (!usrdatum) |
1987 | goto bad; | 1963 | goto bad; |
1988 | c->user = usrdatum->value; | 1964 | newc->user = usrdatum->value; |
1989 | 1965 | ||
1990 | /* Convert the role. */ | 1966 | /* Convert the role. */ |
1991 | rc = -EINVAL; | 1967 | rc = -EINVAL; |
1992 | role = hashtab_search(args->newp->p_roles.table, | 1968 | role = hashtab_search(args->newp->p_roles.table, |
1993 | sym_name(args->oldp, SYM_ROLES, c->role - 1)); | 1969 | sym_name(args->oldp, SYM_ROLES, oldc->role - 1)); |
1994 | if (!role) | 1970 | if (!role) |
1995 | goto bad; | 1971 | goto bad; |
1996 | c->role = role->value; | 1972 | newc->role = role->value; |
1997 | 1973 | ||
1998 | /* Convert the type. */ | 1974 | /* Convert the type. */ |
1999 | rc = -EINVAL; | 1975 | rc = -EINVAL; |
2000 | typdatum = hashtab_search(args->newp->p_types.table, | 1976 | typdatum = hashtab_search(args->newp->p_types.table, |
2001 | sym_name(args->oldp, SYM_TYPES, c->type - 1)); | 1977 | sym_name(args->oldp, |
1978 | SYM_TYPES, oldc->type - 1)); | ||
2002 | if (!typdatum) | 1979 | if (!typdatum) |
2003 | goto bad; | 1980 | goto bad; |
2004 | c->type = typdatum->value; | 1981 | newc->type = typdatum->value; |
2005 | 1982 | ||
2006 | /* Convert the MLS fields if dealing with MLS policies */ | 1983 | /* Convert the MLS fields if dealing with MLS policies */ |
2007 | if (args->oldp->mls_enabled && args->newp->mls_enabled) { | 1984 | if (args->oldp->mls_enabled && args->newp->mls_enabled) { |
2008 | rc = mls_convert_context(args->oldp, args->newp, c); | 1985 | rc = mls_convert_context(args->oldp, args->newp, oldc, newc); |
2009 | if (rc) | 1986 | if (rc) |
2010 | goto bad; | 1987 | goto bad; |
2011 | } else if (args->oldp->mls_enabled && !args->newp->mls_enabled) { | ||
2012 | /* | ||
2013 | * Switching between MLS and non-MLS policy: | ||
2014 | * free any storage used by the MLS fields in the | ||
2015 | * context for all existing entries in the sidtab. | ||
2016 | */ | ||
2017 | mls_context_destroy(c); | ||
2018 | } else if (!args->oldp->mls_enabled && args->newp->mls_enabled) { | 1988 | } else if (!args->oldp->mls_enabled && args->newp->mls_enabled) { |
2019 | /* | 1989 | /* |
2020 | * Switching between non-MLS and MLS policy: | 1990 | * Switching between non-MLS and MLS policy: |
@@ -2032,38 +2002,30 @@ static int convert_context(u32 key, | |||
2032 | " the initial SIDs list\n"); | 2002 | " the initial SIDs list\n"); |
2033 | goto bad; | 2003 | goto bad; |
2034 | } | 2004 | } |
2035 | range = &oc->context[0].range; | 2005 | rc = mls_range_set(newc, &oc->context[0].range); |
2036 | rc = mls_range_set(c, range); | ||
2037 | if (rc) | 2006 | if (rc) |
2038 | goto bad; | 2007 | goto bad; |
2039 | } | 2008 | } |
2040 | 2009 | ||
2041 | /* Check the validity of the new context. */ | 2010 | /* Check the validity of the new context. */ |
2042 | if (!policydb_context_isvalid(args->newp, c)) { | 2011 | if (!policydb_context_isvalid(args->newp, newc)) { |
2043 | rc = convert_context_handle_invalid_context(args->state, | 2012 | rc = convert_context_handle_invalid_context(args->state, oldc); |
2044 | &oldc); | ||
2045 | if (rc) | 2013 | if (rc) |
2046 | goto bad; | 2014 | goto bad; |
2047 | } | 2015 | } |
2048 | 2016 | ||
2049 | context_destroy(&oldc); | 2017 | return 0; |
2050 | |||
2051 | rc = 0; | ||
2052 | out: | ||
2053 | return rc; | ||
2054 | bad: | 2018 | bad: |
2055 | /* Map old representation to string and save it. */ | 2019 | /* Map old representation to string and save it. */ |
2056 | rc = context_struct_to_string(args->oldp, &oldc, &s, &len); | 2020 | rc = context_struct_to_string(args->oldp, oldc, &s, &len); |
2057 | if (rc) | 2021 | if (rc) |
2058 | return rc; | 2022 | return rc; |
2059 | context_destroy(&oldc); | 2023 | context_destroy(newc); |
2060 | context_destroy(c); | 2024 | newc->str = s; |
2061 | c->str = s; | 2025 | newc->len = len; |
2062 | c->len = len; | ||
2063 | pr_info("SELinux: Context %s became invalid (unmapped).\n", | 2026 | pr_info("SELinux: Context %s became invalid (unmapped).\n", |
2064 | c->str); | 2027 | newc->str); |
2065 | rc = 0; | 2028 | return 0; |
2066 | goto out; | ||
2067 | } | 2029 | } |
2068 | 2030 | ||
2069 | static void security_load_policycaps(struct selinux_state *state) | 2031 | static void security_load_policycaps(struct selinux_state *state) |
@@ -2103,11 +2065,11 @@ static int security_preserve_bools(struct selinux_state *state, | |||
2103 | int security_load_policy(struct selinux_state *state, void *data, size_t len) | 2065 | int security_load_policy(struct selinux_state *state, void *data, size_t len) |
2104 | { | 2066 | { |
2105 | struct policydb *policydb; | 2067 | struct policydb *policydb; |
2106 | struct sidtab *sidtab; | 2068 | struct sidtab *oldsidtab, *newsidtab; |
2107 | struct policydb *oldpolicydb, *newpolicydb; | 2069 | struct policydb *oldpolicydb, *newpolicydb; |
2108 | struct sidtab oldsidtab, newsidtab; | ||
2109 | struct selinux_mapping *oldmapping; | 2070 | struct selinux_mapping *oldmapping; |
2110 | struct selinux_map newmap; | 2071 | struct selinux_map newmap; |
2072 | struct sidtab_convert_params convert_params; | ||
2111 | struct convert_context_args args; | 2073 | struct convert_context_args args; |
2112 | u32 seqno; | 2074 | u32 seqno; |
2113 | int rc = 0; | 2075 | int rc = 0; |
@@ -2121,27 +2083,37 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len) | |||
2121 | newpolicydb = oldpolicydb + 1; | 2083 | newpolicydb = oldpolicydb + 1; |
2122 | 2084 | ||
2123 | policydb = &state->ss->policydb; | 2085 | policydb = &state->ss->policydb; |
2124 | sidtab = &state->ss->sidtab; | 2086 | |
2087 | newsidtab = kmalloc(sizeof(*newsidtab), GFP_KERNEL); | ||
2088 | if (!newsidtab) { | ||
2089 | rc = -ENOMEM; | ||
2090 | goto out; | ||
2091 | } | ||
2125 | 2092 | ||
2126 | if (!state->initialized) { | 2093 | if (!state->initialized) { |
2127 | rc = policydb_read(policydb, fp); | 2094 | rc = policydb_read(policydb, fp); |
2128 | if (rc) | 2095 | if (rc) { |
2096 | kfree(newsidtab); | ||
2129 | goto out; | 2097 | goto out; |
2098 | } | ||
2130 | 2099 | ||
2131 | policydb->len = len; | 2100 | policydb->len = len; |
2132 | rc = selinux_set_mapping(policydb, secclass_map, | 2101 | rc = selinux_set_mapping(policydb, secclass_map, |
2133 | &state->ss->map); | 2102 | &state->ss->map); |
2134 | if (rc) { | 2103 | if (rc) { |
2104 | kfree(newsidtab); | ||
2135 | policydb_destroy(policydb); | 2105 | policydb_destroy(policydb); |
2136 | goto out; | 2106 | goto out; |
2137 | } | 2107 | } |
2138 | 2108 | ||
2139 | rc = policydb_load_isids(policydb, sidtab); | 2109 | rc = policydb_load_isids(policydb, newsidtab); |
2140 | if (rc) { | 2110 | if (rc) { |
2111 | kfree(newsidtab); | ||
2141 | policydb_destroy(policydb); | 2112 | policydb_destroy(policydb); |
2142 | goto out; | 2113 | goto out; |
2143 | } | 2114 | } |
2144 | 2115 | ||
2116 | state->ss->sidtab = newsidtab; | ||
2145 | security_load_policycaps(state); | 2117 | security_load_policycaps(state); |
2146 | state->initialized = 1; | 2118 | state->initialized = 1; |
2147 | seqno = ++state->ss->latest_granting; | 2119 | seqno = ++state->ss->latest_granting; |
@@ -2154,13 +2126,11 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len) | |||
2154 | goto out; | 2126 | goto out; |
2155 | } | 2127 | } |
2156 | 2128 | ||
2157 | #if 0 | ||
2158 | sidtab_hash_eval(sidtab, "sids"); | ||
2159 | #endif | ||
2160 | |||
2161 | rc = policydb_read(newpolicydb, fp); | 2129 | rc = policydb_read(newpolicydb, fp); |
2162 | if (rc) | 2130 | if (rc) { |
2131 | kfree(newsidtab); | ||
2163 | goto out; | 2132 | goto out; |
2133 | } | ||
2164 | 2134 | ||
2165 | newpolicydb->len = len; | 2135 | newpolicydb->len = len; |
2166 | /* If switching between different policy types, log MLS status */ | 2136 | /* If switching between different policy types, log MLS status */ |
@@ -2169,10 +2139,11 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len) | |||
2169 | else if (!policydb->mls_enabled && newpolicydb->mls_enabled) | 2139 | else if (!policydb->mls_enabled && newpolicydb->mls_enabled) |
2170 | pr_info("SELinux: Enabling MLS support...\n"); | 2140 | pr_info("SELinux: Enabling MLS support...\n"); |
2171 | 2141 | ||
2172 | rc = policydb_load_isids(newpolicydb, &newsidtab); | 2142 | rc = policydb_load_isids(newpolicydb, newsidtab); |
2173 | if (rc) { | 2143 | if (rc) { |
2174 | pr_err("SELinux: unable to load the initial SIDs\n"); | 2144 | pr_err("SELinux: unable to load the initial SIDs\n"); |
2175 | policydb_destroy(newpolicydb); | 2145 | policydb_destroy(newpolicydb); |
2146 | kfree(newsidtab); | ||
2176 | goto out; | 2147 | goto out; |
2177 | } | 2148 | } |
2178 | 2149 | ||
@@ -2186,12 +2157,7 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len) | |||
2186 | goto err; | 2157 | goto err; |
2187 | } | 2158 | } |
2188 | 2159 | ||
2189 | /* Clone the SID table. */ | 2160 | oldsidtab = state->ss->sidtab; |
2190 | sidtab_shutdown(sidtab); | ||
2191 | |||
2192 | rc = sidtab_map(sidtab, clone_sid, &newsidtab); | ||
2193 | if (rc) | ||
2194 | goto err; | ||
2195 | 2161 | ||
2196 | /* | 2162 | /* |
2197 | * Convert the internal representations of contexts | 2163 | * Convert the internal representations of contexts |
@@ -2200,7 +2166,12 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len) | |||
2200 | args.state = state; | 2166 | args.state = state; |
2201 | args.oldp = policydb; | 2167 | args.oldp = policydb; |
2202 | args.newp = newpolicydb; | 2168 | args.newp = newpolicydb; |
2203 | rc = sidtab_map(&newsidtab, convert_context, &args); | 2169 | |
2170 | convert_params.func = convert_context; | ||
2171 | convert_params.args = &args; | ||
2172 | convert_params.target = newsidtab; | ||
2173 | |||
2174 | rc = sidtab_convert(oldsidtab, &convert_params); | ||
2204 | if (rc) { | 2175 | if (rc) { |
2205 | pr_err("SELinux: unable to convert the internal" | 2176 | pr_err("SELinux: unable to convert the internal" |
2206 | " representation of contexts in the new SID" | 2177 | " representation of contexts in the new SID" |
@@ -2210,12 +2181,11 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len) | |||
2210 | 2181 | ||
2211 | /* Save the old policydb and SID table to free later. */ | 2182 | /* Save the old policydb and SID table to free later. */ |
2212 | memcpy(oldpolicydb, policydb, sizeof(*policydb)); | 2183 | memcpy(oldpolicydb, policydb, sizeof(*policydb)); |
2213 | sidtab_set(&oldsidtab, sidtab); | ||
2214 | 2184 | ||
2215 | /* Install the new policydb and SID table. */ | 2185 | /* Install the new policydb and SID table. */ |
2216 | write_lock_irq(&state->ss->policy_rwlock); | 2186 | write_lock_irq(&state->ss->policy_rwlock); |
2217 | memcpy(policydb, newpolicydb, sizeof(*policydb)); | 2187 | memcpy(policydb, newpolicydb, sizeof(*policydb)); |
2218 | sidtab_set(sidtab, &newsidtab); | 2188 | state->ss->sidtab = newsidtab; |
2219 | security_load_policycaps(state); | 2189 | security_load_policycaps(state); |
2220 | oldmapping = state->ss->map.mapping; | 2190 | oldmapping = state->ss->map.mapping; |
2221 | state->ss->map.mapping = newmap.mapping; | 2191 | state->ss->map.mapping = newmap.mapping; |
@@ -2225,7 +2195,8 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len) | |||
2225 | 2195 | ||
2226 | /* Free the old policydb and SID table. */ | 2196 | /* Free the old policydb and SID table. */ |
2227 | policydb_destroy(oldpolicydb); | 2197 | policydb_destroy(oldpolicydb); |
2228 | sidtab_destroy(&oldsidtab); | 2198 | sidtab_destroy(oldsidtab); |
2199 | kfree(oldsidtab); | ||
2229 | kfree(oldmapping); | 2200 | kfree(oldmapping); |
2230 | 2201 | ||
2231 | avc_ss_reset(state->avc, seqno); | 2202 | avc_ss_reset(state->avc, seqno); |
@@ -2239,7 +2210,8 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len) | |||
2239 | 2210 | ||
2240 | err: | 2211 | err: |
2241 | kfree(newmap.mapping); | 2212 | kfree(newmap.mapping); |
2242 | sidtab_destroy(&newsidtab); | 2213 | sidtab_destroy(newsidtab); |
2214 | kfree(newsidtab); | ||
2243 | policydb_destroy(newpolicydb); | 2215 | policydb_destroy(newpolicydb); |
2244 | 2216 | ||
2245 | out: | 2217 | out: |
@@ -2276,7 +2248,7 @@ int security_port_sid(struct selinux_state *state, | |||
2276 | read_lock(&state->ss->policy_rwlock); | 2248 | read_lock(&state->ss->policy_rwlock); |
2277 | 2249 | ||
2278 | policydb = &state->ss->policydb; | 2250 | policydb = &state->ss->policydb; |
2279 | sidtab = &state->ss->sidtab; | 2251 | sidtab = state->ss->sidtab; |
2280 | 2252 | ||
2281 | c = policydb->ocontexts[OCON_PORT]; | 2253 | c = policydb->ocontexts[OCON_PORT]; |
2282 | while (c) { | 2254 | while (c) { |
@@ -2322,7 +2294,7 @@ int security_ib_pkey_sid(struct selinux_state *state, | |||
2322 | read_lock(&state->ss->policy_rwlock); | 2294 | read_lock(&state->ss->policy_rwlock); |
2323 | 2295 | ||
2324 | policydb = &state->ss->policydb; | 2296 | policydb = &state->ss->policydb; |
2325 | sidtab = &state->ss->sidtab; | 2297 | sidtab = state->ss->sidtab; |
2326 | 2298 | ||
2327 | c = policydb->ocontexts[OCON_IBPKEY]; | 2299 | c = policydb->ocontexts[OCON_IBPKEY]; |
2328 | while (c) { | 2300 | while (c) { |
@@ -2368,7 +2340,7 @@ int security_ib_endport_sid(struct selinux_state *state, | |||
2368 | read_lock(&state->ss->policy_rwlock); | 2340 | read_lock(&state->ss->policy_rwlock); |
2369 | 2341 | ||
2370 | policydb = &state->ss->policydb; | 2342 | policydb = &state->ss->policydb; |
2371 | sidtab = &state->ss->sidtab; | 2343 | sidtab = state->ss->sidtab; |
2372 | 2344 | ||
2373 | c = policydb->ocontexts[OCON_IBENDPORT]; | 2345 | c = policydb->ocontexts[OCON_IBENDPORT]; |
2374 | while (c) { | 2346 | while (c) { |
@@ -2414,7 +2386,7 @@ int security_netif_sid(struct selinux_state *state, | |||
2414 | read_lock(&state->ss->policy_rwlock); | 2386 | read_lock(&state->ss->policy_rwlock); |
2415 | 2387 | ||
2416 | policydb = &state->ss->policydb; | 2388 | policydb = &state->ss->policydb; |
2417 | sidtab = &state->ss->sidtab; | 2389 | sidtab = state->ss->sidtab; |
2418 | 2390 | ||
2419 | c = policydb->ocontexts[OCON_NETIF]; | 2391 | c = policydb->ocontexts[OCON_NETIF]; |
2420 | while (c) { | 2392 | while (c) { |
@@ -2479,7 +2451,7 @@ int security_node_sid(struct selinux_state *state, | |||
2479 | read_lock(&state->ss->policy_rwlock); | 2451 | read_lock(&state->ss->policy_rwlock); |
2480 | 2452 | ||
2481 | policydb = &state->ss->policydb; | 2453 | policydb = &state->ss->policydb; |
2482 | sidtab = &state->ss->sidtab; | 2454 | sidtab = state->ss->sidtab; |
2483 | 2455 | ||
2484 | switch (domain) { | 2456 | switch (domain) { |
2485 | case AF_INET: { | 2457 | case AF_INET: { |
@@ -2579,7 +2551,7 @@ int security_get_user_sids(struct selinux_state *state, | |||
2579 | read_lock(&state->ss->policy_rwlock); | 2551 | read_lock(&state->ss->policy_rwlock); |
2580 | 2552 | ||
2581 | policydb = &state->ss->policydb; | 2553 | policydb = &state->ss->policydb; |
2582 | sidtab = &state->ss->sidtab; | 2554 | sidtab = state->ss->sidtab; |
2583 | 2555 | ||
2584 | context_init(&usercon); | 2556 | context_init(&usercon); |
2585 | 2557 | ||
@@ -2681,7 +2653,7 @@ static inline int __security_genfs_sid(struct selinux_state *state, | |||
2681 | u32 *sid) | 2653 | u32 *sid) |
2682 | { | 2654 | { |
2683 | struct policydb *policydb = &state->ss->policydb; | 2655 | struct policydb *policydb = &state->ss->policydb; |
2684 | struct sidtab *sidtab = &state->ss->sidtab; | 2656 | struct sidtab *sidtab = state->ss->sidtab; |
2685 | int len; | 2657 | int len; |
2686 | u16 sclass; | 2658 | u16 sclass; |
2687 | struct genfs *genfs; | 2659 | struct genfs *genfs; |
@@ -2767,7 +2739,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb) | |||
2767 | read_lock(&state->ss->policy_rwlock); | 2739 | read_lock(&state->ss->policy_rwlock); |
2768 | 2740 | ||
2769 | policydb = &state->ss->policydb; | 2741 | policydb = &state->ss->policydb; |
2770 | sidtab = &state->ss->sidtab; | 2742 | sidtab = state->ss->sidtab; |
2771 | 2743 | ||
2772 | c = policydb->ocontexts[OCON_FSUSE]; | 2744 | c = policydb->ocontexts[OCON_FSUSE]; |
2773 | while (c) { | 2745 | while (c) { |
@@ -2973,7 +2945,7 @@ int security_sid_mls_copy(struct selinux_state *state, | |||
2973 | u32 sid, u32 mls_sid, u32 *new_sid) | 2945 | u32 sid, u32 mls_sid, u32 *new_sid) |
2974 | { | 2946 | { |
2975 | struct policydb *policydb = &state->ss->policydb; | 2947 | struct policydb *policydb = &state->ss->policydb; |
2976 | struct sidtab *sidtab = &state->ss->sidtab; | 2948 | struct sidtab *sidtab = state->ss->sidtab; |
2977 | struct context *context1; | 2949 | struct context *context1; |
2978 | struct context *context2; | 2950 | struct context *context2; |
2979 | struct context newcon; | 2951 | struct context newcon; |
@@ -3064,7 +3036,7 @@ int security_net_peersid_resolve(struct selinux_state *state, | |||
3064 | u32 *peer_sid) | 3036 | u32 *peer_sid) |
3065 | { | 3037 | { |
3066 | struct policydb *policydb = &state->ss->policydb; | 3038 | struct policydb *policydb = &state->ss->policydb; |
3067 | struct sidtab *sidtab = &state->ss->sidtab; | 3039 | struct sidtab *sidtab = state->ss->sidtab; |
3068 | int rc; | 3040 | int rc; |
3069 | struct context *nlbl_ctx; | 3041 | struct context *nlbl_ctx; |
3070 | struct context *xfrm_ctx; | 3042 | struct context *xfrm_ctx; |
@@ -3425,7 +3397,7 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, | |||
3425 | goto out; | 3397 | goto out; |
3426 | } | 3398 | } |
3427 | 3399 | ||
3428 | ctxt = sidtab_search(&state->ss->sidtab, sid); | 3400 | ctxt = sidtab_search(state->ss->sidtab, sid); |
3429 | if (unlikely(!ctxt)) { | 3401 | if (unlikely(!ctxt)) { |
3430 | WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n", | 3402 | WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n", |
3431 | sid); | 3403 | sid); |
@@ -3588,7 +3560,7 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state, | |||
3588 | u32 *sid) | 3560 | u32 *sid) |
3589 | { | 3561 | { |
3590 | struct policydb *policydb = &state->ss->policydb; | 3562 | struct policydb *policydb = &state->ss->policydb; |
3591 | struct sidtab *sidtab = &state->ss->sidtab; | 3563 | struct sidtab *sidtab = state->ss->sidtab; |
3592 | int rc; | 3564 | int rc; |
3593 | struct context *ctx; | 3565 | struct context *ctx; |
3594 | struct context ctx_new; | 3566 | struct context ctx_new; |
@@ -3666,7 +3638,7 @@ int security_netlbl_sid_to_secattr(struct selinux_state *state, | |||
3666 | read_lock(&state->ss->policy_rwlock); | 3638 | read_lock(&state->ss->policy_rwlock); |
3667 | 3639 | ||
3668 | rc = -ENOENT; | 3640 | rc = -ENOENT; |
3669 | ctx = sidtab_search(&state->ss->sidtab, sid); | 3641 | ctx = sidtab_search(state->ss->sidtab, sid); |
3670 | if (ctx == NULL) | 3642 | if (ctx == NULL) |
3671 | goto out; | 3643 | goto out; |
3672 | 3644 | ||
diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h index 24c7bdcc8075..9a36de860368 100644 --- a/security/selinux/ss/services.h +++ b/security/selinux/ss/services.h | |||
@@ -24,7 +24,7 @@ struct selinux_map { | |||
24 | }; | 24 | }; |
25 | 25 | ||
26 | struct selinux_ss { | 26 | struct selinux_ss { |
27 | struct sidtab sidtab; | 27 | struct sidtab *sidtab; |
28 | struct policydb policydb; | 28 | struct policydb policydb; |
29 | rwlock_t policy_rwlock; | 29 | rwlock_t policy_rwlock; |
30 | u32 latest_granting; | 30 | u32 latest_granting; |
diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c index fd75a12fa8fc..e63a90ff2728 100644 --- a/security/selinux/ss/sidtab.c +++ b/security/selinux/ss/sidtab.c | |||
@@ -2,108 +2,164 @@ | |||
2 | /* | 2 | /* |
3 | * Implementation of the SID table type. | 3 | * Implementation of the SID table type. |
4 | * | 4 | * |
5 | * Author : Stephen Smalley, <sds@tycho.nsa.gov> | 5 | * Original author: Stephen Smalley, <sds@tycho.nsa.gov> |
6 | * Author: Ondrej Mosnacek, <omosnacek@gmail.com> | ||
7 | * | ||
8 | * Copyright (C) 2018 Red Hat, Inc. | ||
6 | */ | 9 | */ |
10 | #include <linux/errno.h> | ||
7 | #include <linux/kernel.h> | 11 | #include <linux/kernel.h> |
8 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
13 | #include <linux/sched.h> | ||
9 | #include <linux/spinlock.h> | 14 | #include <linux/spinlock.h> |
10 | #include <linux/errno.h> | 15 | #include <linux/atomic.h> |
11 | #include "flask.h" | 16 | #include "flask.h" |
12 | #include "security.h" | 17 | #include "security.h" |
13 | #include "sidtab.h" | 18 | #include "sidtab.h" |
14 | 19 | ||
15 | #define SIDTAB_HASH(sid) \ | ||
16 | (sid & SIDTAB_HASH_MASK) | ||
17 | |||
18 | int sidtab_init(struct sidtab *s) | 20 | int sidtab_init(struct sidtab *s) |
19 | { | 21 | { |
20 | int i; | 22 | u32 i; |
21 | 23 | ||
22 | s->htable = kmalloc_array(SIDTAB_SIZE, sizeof(*s->htable), GFP_ATOMIC); | 24 | memset(s->roots, 0, sizeof(s->roots)); |
23 | if (!s->htable) | 25 | |
24 | return -ENOMEM; | 26 | for (i = 0; i < SIDTAB_RCACHE_SIZE; i++) |
25 | for (i = 0; i < SIDTAB_SIZE; i++) | 27 | atomic_set(&s->rcache[i], -1); |
26 | s->htable[i] = NULL; | 28 | |
27 | s->nel = 0; | 29 | for (i = 0; i < SECINITSID_NUM; i++) |
28 | s->next_sid = 1; | 30 | s->isids[i].set = 0; |
29 | s->shutdown = 0; | 31 | |
32 | atomic_set(&s->count, 0); | ||
33 | |||
34 | s->convert = NULL; | ||
35 | |||
30 | spin_lock_init(&s->lock); | 36 | spin_lock_init(&s->lock); |
31 | return 0; | 37 | return 0; |
32 | } | 38 | } |
33 | 39 | ||
34 | int sidtab_insert(struct sidtab *s, u32 sid, struct context *context) | 40 | int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context) |
35 | { | 41 | { |
36 | int hvalue; | 42 | struct sidtab_isid_entry *entry; |
37 | struct sidtab_node *prev, *cur, *newnode; | 43 | int rc; |
38 | |||
39 | if (!s) | ||
40 | return -ENOMEM; | ||
41 | |||
42 | hvalue = SIDTAB_HASH(sid); | ||
43 | prev = NULL; | ||
44 | cur = s->htable[hvalue]; | ||
45 | while (cur && sid > cur->sid) { | ||
46 | prev = cur; | ||
47 | cur = cur->next; | ||
48 | } | ||
49 | 44 | ||
50 | if (cur && sid == cur->sid) | 45 | if (sid == 0 || sid > SECINITSID_NUM) |
51 | return -EEXIST; | 46 | return -EINVAL; |
52 | 47 | ||
53 | newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC); | 48 | entry = &s->isids[sid - 1]; |
54 | if (!newnode) | ||
55 | return -ENOMEM; | ||
56 | 49 | ||
57 | newnode->sid = sid; | 50 | rc = context_cpy(&entry->context, context); |
58 | if (context_cpy(&newnode->context, context)) { | 51 | if (rc) |
59 | kfree(newnode); | 52 | return rc; |
60 | return -ENOMEM; | ||
61 | } | ||
62 | 53 | ||
63 | if (prev) { | 54 | entry->set = 1; |
64 | newnode->next = prev->next; | 55 | return 0; |
65 | wmb(); | 56 | } |
66 | prev->next = newnode; | 57 | |
67 | } else { | 58 | static u32 sidtab_level_from_count(u32 count) |
68 | newnode->next = s->htable[hvalue]; | 59 | { |
69 | wmb(); | 60 | u32 capacity = SIDTAB_LEAF_ENTRIES; |
70 | s->htable[hvalue] = newnode; | 61 | u32 level = 0; |
62 | |||
63 | while (count > capacity) { | ||
64 | capacity <<= SIDTAB_INNER_SHIFT; | ||
65 | ++level; | ||
71 | } | 66 | } |
67 | return level; | ||
68 | } | ||
72 | 69 | ||
73 | s->nel++; | 70 | static int sidtab_alloc_roots(struct sidtab *s, u32 level) |
74 | if (sid >= s->next_sid) | 71 | { |
75 | s->next_sid = sid + 1; | 72 | u32 l; |
73 | |||
74 | if (!s->roots[0].ptr_leaf) { | ||
75 | s->roots[0].ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE, | ||
76 | GFP_ATOMIC); | ||
77 | if (!s->roots[0].ptr_leaf) | ||
78 | return -ENOMEM; | ||
79 | } | ||
80 | for (l = 1; l <= level; ++l) | ||
81 | if (!s->roots[l].ptr_inner) { | ||
82 | s->roots[l].ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE, | ||
83 | GFP_ATOMIC); | ||
84 | if (!s->roots[l].ptr_inner) | ||
85 | return -ENOMEM; | ||
86 | s->roots[l].ptr_inner->entries[0] = s->roots[l - 1]; | ||
87 | } | ||
76 | return 0; | 88 | return 0; |
77 | } | 89 | } |
78 | 90 | ||
79 | static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force) | 91 | static struct context *sidtab_do_lookup(struct sidtab *s, u32 index, int alloc) |
80 | { | 92 | { |
81 | int hvalue; | 93 | union sidtab_entry_inner *entry; |
82 | struct sidtab_node *cur; | 94 | u32 level, capacity_shift, leaf_index = index / SIDTAB_LEAF_ENTRIES; |
95 | |||
96 | /* find the level of the subtree we need */ | ||
97 | level = sidtab_level_from_count(index + 1); | ||
98 | capacity_shift = level * SIDTAB_INNER_SHIFT; | ||
83 | 99 | ||
84 | if (!s) | 100 | /* allocate roots if needed */ |
101 | if (alloc && sidtab_alloc_roots(s, level) != 0) | ||
85 | return NULL; | 102 | return NULL; |
86 | 103 | ||
87 | hvalue = SIDTAB_HASH(sid); | 104 | /* lookup inside the subtree */ |
88 | cur = s->htable[hvalue]; | 105 | entry = &s->roots[level]; |
89 | while (cur && sid > cur->sid) | 106 | while (level != 0) { |
90 | cur = cur->next; | 107 | capacity_shift -= SIDTAB_INNER_SHIFT; |
91 | 108 | --level; | |
92 | if (force && cur && sid == cur->sid && cur->context.len) | 109 | |
93 | return &cur->context; | 110 | entry = &entry->ptr_inner->entries[leaf_index >> capacity_shift]; |
94 | 111 | leaf_index &= ((u32)1 << capacity_shift) - 1; | |
95 | if (!cur || sid != cur->sid || cur->context.len) { | 112 | |
96 | /* Remap invalid SIDs to the unlabeled SID. */ | 113 | if (!entry->ptr_inner) { |
97 | sid = SECINITSID_UNLABELED; | 114 | if (alloc) |
98 | hvalue = SIDTAB_HASH(sid); | 115 | entry->ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE, |
99 | cur = s->htable[hvalue]; | 116 | GFP_ATOMIC); |
100 | while (cur && sid > cur->sid) | 117 | if (!entry->ptr_inner) |
101 | cur = cur->next; | 118 | return NULL; |
102 | if (!cur || sid != cur->sid) | 119 | } |
120 | } | ||
121 | if (!entry->ptr_leaf) { | ||
122 | if (alloc) | ||
123 | entry->ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE, | ||
124 | GFP_ATOMIC); | ||
125 | if (!entry->ptr_leaf) | ||
103 | return NULL; | 126 | return NULL; |
104 | } | 127 | } |
128 | return &entry->ptr_leaf->entries[index % SIDTAB_LEAF_ENTRIES].context; | ||
129 | } | ||
130 | |||
131 | static struct context *sidtab_lookup(struct sidtab *s, u32 index) | ||
132 | { | ||
133 | u32 count = (u32)atomic_read(&s->count); | ||
134 | |||
135 | if (index >= count) | ||
136 | return NULL; | ||
137 | |||
138 | /* read entries after reading count */ | ||
139 | smp_rmb(); | ||
140 | |||
141 | return sidtab_do_lookup(s, index, 0); | ||
142 | } | ||
143 | |||
144 | static struct context *sidtab_lookup_initial(struct sidtab *s, u32 sid) | ||
145 | { | ||
146 | return s->isids[sid - 1].set ? &s->isids[sid - 1].context : NULL; | ||
147 | } | ||
148 | |||
149 | static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force) | ||
150 | { | ||
151 | struct context *context; | ||
152 | |||
153 | if (sid != 0) { | ||
154 | if (sid > SECINITSID_NUM) | ||
155 | context = sidtab_lookup(s, sid - (SECINITSID_NUM + 1)); | ||
156 | else | ||
157 | context = sidtab_lookup_initial(s, sid); | ||
158 | if (context && (!context->len || force)) | ||
159 | return context; | ||
160 | } | ||
105 | 161 | ||
106 | return &cur->context; | 162 | return sidtab_lookup_initial(s, SECINITSID_UNLABELED); |
107 | } | 163 | } |
108 | 164 | ||
109 | struct context *sidtab_search(struct sidtab *s, u32 sid) | 165 | struct context *sidtab_search(struct sidtab *s, u32 sid) |
@@ -116,191 +172,324 @@ struct context *sidtab_search_force(struct sidtab *s, u32 sid) | |||
116 | return sidtab_search_core(s, sid, 1); | 172 | return sidtab_search_core(s, sid, 1); |
117 | } | 173 | } |
118 | 174 | ||
119 | int sidtab_map(struct sidtab *s, | 175 | static int sidtab_find_context(union sidtab_entry_inner entry, |
120 | int (*apply) (u32 sid, | 176 | u32 *pos, u32 count, u32 level, |
121 | struct context *context, | 177 | struct context *context, u32 *index) |
122 | void *args), | ||
123 | void *args) | ||
124 | { | 178 | { |
125 | int i, rc = 0; | 179 | int rc; |
126 | struct sidtab_node *cur; | 180 | u32 i; |
127 | 181 | ||
128 | if (!s) | 182 | if (level != 0) { |
129 | goto out; | 183 | struct sidtab_node_inner *node = entry.ptr_inner; |
184 | |||
185 | i = 0; | ||
186 | while (i < SIDTAB_INNER_ENTRIES && *pos < count) { | ||
187 | rc = sidtab_find_context(node->entries[i], | ||
188 | pos, count, level - 1, | ||
189 | context, index); | ||
190 | if (rc == 0) | ||
191 | return 0; | ||
192 | i++; | ||
193 | } | ||
194 | } else { | ||
195 | struct sidtab_node_leaf *node = entry.ptr_leaf; | ||
130 | 196 | ||
131 | for (i = 0; i < SIDTAB_SIZE; i++) { | 197 | i = 0; |
132 | cur = s->htable[i]; | 198 | while (i < SIDTAB_LEAF_ENTRIES && *pos < count) { |
133 | while (cur) { | 199 | if (context_cmp(&node->entries[i].context, context)) { |
134 | rc = apply(cur->sid, &cur->context, args); | 200 | *index = *pos; |
135 | if (rc) | 201 | return 0; |
136 | goto out; | 202 | } |
137 | cur = cur->next; | 203 | (*pos)++; |
204 | i++; | ||
138 | } | 205 | } |
139 | } | 206 | } |
140 | out: | 207 | return -ENOENT; |
141 | return rc; | ||
142 | } | 208 | } |
143 | 209 | ||
144 | static void sidtab_update_cache(struct sidtab *s, struct sidtab_node *n, int loc) | 210 | static void sidtab_rcache_update(struct sidtab *s, u32 index, u32 pos) |
145 | { | 211 | { |
146 | BUG_ON(loc >= SIDTAB_CACHE_LEN); | 212 | while (pos > 0) { |
147 | 213 | atomic_set(&s->rcache[pos], atomic_read(&s->rcache[pos - 1])); | |
148 | while (loc > 0) { | 214 | --pos; |
149 | s->cache[loc] = s->cache[loc - 1]; | ||
150 | loc--; | ||
151 | } | 215 | } |
152 | s->cache[0] = n; | 216 | atomic_set(&s->rcache[0], (int)index); |
153 | } | 217 | } |
154 | 218 | ||
155 | static inline u32 sidtab_search_context(struct sidtab *s, | 219 | static void sidtab_rcache_push(struct sidtab *s, u32 index) |
156 | struct context *context) | ||
157 | { | 220 | { |
158 | int i; | 221 | sidtab_rcache_update(s, index, SIDTAB_RCACHE_SIZE - 1); |
159 | struct sidtab_node *cur; | ||
160 | |||
161 | for (i = 0; i < SIDTAB_SIZE; i++) { | ||
162 | cur = s->htable[i]; | ||
163 | while (cur) { | ||
164 | if (context_cmp(&cur->context, context)) { | ||
165 | sidtab_update_cache(s, cur, SIDTAB_CACHE_LEN - 1); | ||
166 | return cur->sid; | ||
167 | } | ||
168 | cur = cur->next; | ||
169 | } | ||
170 | } | ||
171 | return 0; | ||
172 | } | 222 | } |
173 | 223 | ||
174 | static inline u32 sidtab_search_cache(struct sidtab *s, struct context *context) | 224 | static int sidtab_rcache_search(struct sidtab *s, struct context *context, |
225 | u32 *index) | ||
175 | { | 226 | { |
176 | int i; | 227 | u32 i; |
177 | struct sidtab_node *node; | ||
178 | 228 | ||
179 | for (i = 0; i < SIDTAB_CACHE_LEN; i++) { | 229 | for (i = 0; i < SIDTAB_RCACHE_SIZE; i++) { |
180 | node = s->cache[i]; | 230 | int v = atomic_read(&s->rcache[i]); |
181 | if (unlikely(!node)) | 231 | |
232 | if (v < 0) | ||
233 | continue; | ||
234 | |||
235 | if (context_cmp(sidtab_do_lookup(s, (u32)v, 0), context)) { | ||
236 | sidtab_rcache_update(s, (u32)v, i); | ||
237 | *index = (u32)v; | ||
182 | return 0; | 238 | return 0; |
183 | if (context_cmp(&node->context, context)) { | ||
184 | sidtab_update_cache(s, node, i); | ||
185 | return node->sid; | ||
186 | } | 239 | } |
187 | } | 240 | } |
188 | return 0; | 241 | return -ENOENT; |
189 | } | 242 | } |
190 | 243 | ||
191 | int sidtab_context_to_sid(struct sidtab *s, | 244 | static int sidtab_reverse_lookup(struct sidtab *s, struct context *context, |
192 | struct context *context, | 245 | u32 *index) |
193 | u32 *out_sid) | ||
194 | { | 246 | { |
195 | u32 sid; | ||
196 | int ret = 0; | ||
197 | unsigned long flags; | 247 | unsigned long flags; |
248 | u32 count = (u32)atomic_read(&s->count); | ||
249 | u32 count_locked, level, pos; | ||
250 | struct sidtab_convert_params *convert; | ||
251 | struct context *dst, *dst_convert; | ||
252 | int rc; | ||
253 | |||
254 | rc = sidtab_rcache_search(s, context, index); | ||
255 | if (rc == 0) | ||
256 | return 0; | ||
257 | |||
258 | level = sidtab_level_from_count(count); | ||
259 | |||
260 | /* read entries after reading count */ | ||
261 | smp_rmb(); | ||
262 | |||
263 | pos = 0; | ||
264 | rc = sidtab_find_context(s->roots[level], &pos, count, level, | ||
265 | context, index); | ||
266 | if (rc == 0) { | ||
267 | sidtab_rcache_push(s, *index); | ||
268 | return 0; | ||
269 | } | ||
198 | 270 | ||
199 | *out_sid = SECSID_NULL; | 271 | /* lock-free search failed: lock, re-search, and insert if not found */ |
272 | spin_lock_irqsave(&s->lock, flags); | ||
200 | 273 | ||
201 | sid = sidtab_search_cache(s, context); | 274 | convert = s->convert; |
202 | if (!sid) | 275 | count_locked = (u32)atomic_read(&s->count); |
203 | sid = sidtab_search_context(s, context); | 276 | level = sidtab_level_from_count(count_locked); |
204 | if (!sid) { | 277 | |
205 | spin_lock_irqsave(&s->lock, flags); | 278 | /* if count has changed before we acquired the lock, then catch up */ |
206 | /* Rescan now that we hold the lock. */ | 279 | while (count < count_locked) { |
207 | sid = sidtab_search_context(s, context); | 280 | if (context_cmp(sidtab_do_lookup(s, count, 0), context)) { |
208 | if (sid) | 281 | sidtab_rcache_push(s, count); |
209 | goto unlock_out; | 282 | *index = count; |
210 | /* No SID exists for the context. Allocate a new one. */ | 283 | rc = 0; |
211 | if (s->next_sid == UINT_MAX || s->shutdown) { | 284 | goto out_unlock; |
212 | ret = -ENOMEM; | ||
213 | goto unlock_out; | ||
214 | } | 285 | } |
215 | sid = s->next_sid++; | 286 | ++count; |
216 | if (context->len) | ||
217 | pr_info("SELinux: Context %s is not valid (left unmapped).\n", | ||
218 | context->str); | ||
219 | ret = sidtab_insert(s, sid, context); | ||
220 | if (ret) | ||
221 | s->next_sid--; | ||
222 | unlock_out: | ||
223 | spin_unlock_irqrestore(&s->lock, flags); | ||
224 | } | 287 | } |
225 | 288 | ||
226 | if (ret) | 289 | /* insert context into new entry */ |
227 | return ret; | 290 | rc = -ENOMEM; |
291 | dst = sidtab_do_lookup(s, count, 1); | ||
292 | if (!dst) | ||
293 | goto out_unlock; | ||
294 | |||
295 | rc = context_cpy(dst, context); | ||
296 | if (rc) | ||
297 | goto out_unlock; | ||
298 | |||
299 | /* | ||
300 | * if we are building a new sidtab, we need to convert the context | ||
301 | * and insert it there as well | ||
302 | */ | ||
303 | if (convert) { | ||
304 | rc = -ENOMEM; | ||
305 | dst_convert = sidtab_do_lookup(convert->target, count, 1); | ||
306 | if (!dst_convert) { | ||
307 | context_destroy(dst); | ||
308 | goto out_unlock; | ||
309 | } | ||
228 | 310 | ||
229 | *out_sid = sid; | 311 | rc = convert->func(context, dst_convert, convert->args); |
230 | return 0; | 312 | if (rc) { |
313 | context_destroy(dst); | ||
314 | goto out_unlock; | ||
315 | } | ||
316 | |||
317 | /* at this point we know the insert won't fail */ | ||
318 | atomic_set(&convert->target->count, count + 1); | ||
319 | } | ||
320 | |||
321 | if (context->len) | ||
322 | pr_info("SELinux: Context %s is not valid (left unmapped).\n", | ||
323 | context->str); | ||
324 | |||
325 | sidtab_rcache_push(s, count); | ||
326 | *index = count; | ||
327 | |||
328 | /* write entries before writing new count */ | ||
329 | smp_wmb(); | ||
330 | |||
331 | atomic_set(&s->count, count + 1); | ||
332 | |||
333 | rc = 0; | ||
334 | out_unlock: | ||
335 | spin_unlock_irqrestore(&s->lock, flags); | ||
336 | return rc; | ||
231 | } | 337 | } |
232 | 338 | ||
233 | void sidtab_hash_eval(struct sidtab *h, char *tag) | 339 | int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid) |
234 | { | 340 | { |
235 | int i, chain_len, slots_used, max_chain_len; | 341 | int rc; |
236 | struct sidtab_node *cur; | 342 | u32 i; |
237 | 343 | ||
238 | slots_used = 0; | 344 | for (i = 0; i < SECINITSID_NUM; i++) { |
239 | max_chain_len = 0; | 345 | struct sidtab_isid_entry *entry = &s->isids[i]; |
240 | for (i = 0; i < SIDTAB_SIZE; i++) { | ||
241 | cur = h->htable[i]; | ||
242 | if (cur) { | ||
243 | slots_used++; | ||
244 | chain_len = 0; | ||
245 | while (cur) { | ||
246 | chain_len++; | ||
247 | cur = cur->next; | ||
248 | } | ||
249 | 346 | ||
250 | if (chain_len > max_chain_len) | 347 | if (entry->set && context_cmp(context, &entry->context)) { |
251 | max_chain_len = chain_len; | 348 | *sid = i + 1; |
349 | return 0; | ||
252 | } | 350 | } |
253 | } | 351 | } |
254 | 352 | ||
255 | pr_debug("%s: %d entries and %d/%d buckets used, longest " | 353 | rc = sidtab_reverse_lookup(s, context, sid); |
256 | "chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE, | 354 | if (rc) |
257 | max_chain_len); | 355 | return rc; |
356 | *sid += SECINITSID_NUM + 1; | ||
357 | return 0; | ||
258 | } | 358 | } |
259 | 359 | ||
260 | void sidtab_destroy(struct sidtab *s) | 360 | static int sidtab_convert_tree(union sidtab_entry_inner *edst, |
361 | union sidtab_entry_inner *esrc, | ||
362 | u32 *pos, u32 count, u32 level, | ||
363 | struct sidtab_convert_params *convert) | ||
261 | { | 364 | { |
262 | int i; | 365 | int rc; |
263 | struct sidtab_node *cur, *temp; | 366 | u32 i; |
264 | 367 | ||
265 | if (!s) | 368 | if (level != 0) { |
266 | return; | 369 | if (!edst->ptr_inner) { |
267 | 370 | edst->ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE, | |
268 | for (i = 0; i < SIDTAB_SIZE; i++) { | 371 | GFP_KERNEL); |
269 | cur = s->htable[i]; | 372 | if (!edst->ptr_inner) |
270 | while (cur) { | 373 | return -ENOMEM; |
271 | temp = cur; | 374 | } |
272 | cur = cur->next; | 375 | i = 0; |
273 | context_destroy(&temp->context); | 376 | while (i < SIDTAB_INNER_ENTRIES && *pos < count) { |
274 | kfree(temp); | 377 | rc = sidtab_convert_tree(&edst->ptr_inner->entries[i], |
378 | &esrc->ptr_inner->entries[i], | ||
379 | pos, count, level - 1, | ||
380 | convert); | ||
381 | if (rc) | ||
382 | return rc; | ||
383 | i++; | ||
384 | } | ||
385 | } else { | ||
386 | if (!edst->ptr_leaf) { | ||
387 | edst->ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE, | ||
388 | GFP_KERNEL); | ||
389 | if (!edst->ptr_leaf) | ||
390 | return -ENOMEM; | ||
391 | } | ||
392 | i = 0; | ||
393 | while (i < SIDTAB_LEAF_ENTRIES && *pos < count) { | ||
394 | rc = convert->func(&esrc->ptr_leaf->entries[i].context, | ||
395 | &edst->ptr_leaf->entries[i].context, | ||
396 | convert->args); | ||
397 | if (rc) | ||
398 | return rc; | ||
399 | (*pos)++; | ||
400 | i++; | ||
275 | } | 401 | } |
276 | s->htable[i] = NULL; | 402 | cond_resched(); |
277 | } | 403 | } |
278 | kfree(s->htable); | 404 | return 0; |
279 | s->htable = NULL; | ||
280 | s->nel = 0; | ||
281 | s->next_sid = 1; | ||
282 | } | 405 | } |
283 | 406 | ||
284 | void sidtab_set(struct sidtab *dst, struct sidtab *src) | 407 | int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params) |
285 | { | 408 | { |
286 | unsigned long flags; | 409 | unsigned long flags; |
287 | int i; | 410 | u32 count, level, pos; |
288 | 411 | int rc; | |
289 | spin_lock_irqsave(&src->lock, flags); | 412 | |
290 | dst->htable = src->htable; | 413 | spin_lock_irqsave(&s->lock, flags); |
291 | dst->nel = src->nel; | 414 | |
292 | dst->next_sid = src->next_sid; | 415 | /* concurrent policy loads are not allowed */ |
293 | dst->shutdown = 0; | 416 | if (s->convert) { |
294 | for (i = 0; i < SIDTAB_CACHE_LEN; i++) | 417 | spin_unlock_irqrestore(&s->lock, flags); |
295 | dst->cache[i] = NULL; | 418 | return -EBUSY; |
296 | spin_unlock_irqrestore(&src->lock, flags); | 419 | } |
420 | |||
421 | count = (u32)atomic_read(&s->count); | ||
422 | level = sidtab_level_from_count(count); | ||
423 | |||
424 | /* allocate last leaf in the new sidtab (to avoid race with | ||
425 | * live convert) | ||
426 | */ | ||
427 | rc = sidtab_do_lookup(params->target, count - 1, 1) ? 0 : -ENOMEM; | ||
428 | if (rc) { | ||
429 | spin_unlock_irqrestore(&s->lock, flags); | ||
430 | return rc; | ||
431 | } | ||
432 | |||
433 | /* set count in case no new entries are added during conversion */ | ||
434 | atomic_set(¶ms->target->count, count); | ||
435 | |||
436 | /* enable live convert of new entries */ | ||
437 | s->convert = params; | ||
438 | |||
439 | /* we can safely do the rest of the conversion outside the lock */ | ||
440 | spin_unlock_irqrestore(&s->lock, flags); | ||
441 | |||
442 | pr_info("SELinux: Converting %u SID table entries...\n", count); | ||
443 | |||
444 | /* convert all entries not covered by live convert */ | ||
445 | pos = 0; | ||
446 | rc = sidtab_convert_tree(¶ms->target->roots[level], | ||
447 | &s->roots[level], &pos, count, level, params); | ||
448 | if (rc) { | ||
449 | /* we need to keep the old table - disable live convert */ | ||
450 | spin_lock_irqsave(&s->lock, flags); | ||
451 | s->convert = NULL; | ||
452 | spin_unlock_irqrestore(&s->lock, flags); | ||
453 | } | ||
454 | return rc; | ||
297 | } | 455 | } |
298 | 456 | ||
299 | void sidtab_shutdown(struct sidtab *s) | 457 | static void sidtab_destroy_tree(union sidtab_entry_inner entry, u32 level) |
300 | { | 458 | { |
301 | unsigned long flags; | 459 | u32 i; |
302 | 460 | ||
303 | spin_lock_irqsave(&s->lock, flags); | 461 | if (level != 0) { |
304 | s->shutdown = 1; | 462 | struct sidtab_node_inner *node = entry.ptr_inner; |
305 | spin_unlock_irqrestore(&s->lock, flags); | 463 | |
464 | if (!node) | ||
465 | return; | ||
466 | |||
467 | for (i = 0; i < SIDTAB_INNER_ENTRIES; i++) | ||
468 | sidtab_destroy_tree(node->entries[i], level - 1); | ||
469 | kfree(node); | ||
470 | } else { | ||
471 | struct sidtab_node_leaf *node = entry.ptr_leaf; | ||
472 | |||
473 | if (!node) | ||
474 | return; | ||
475 | |||
476 | for (i = 0; i < SIDTAB_LEAF_ENTRIES; i++) | ||
477 | context_destroy(&node->entries[i].context); | ||
478 | kfree(node); | ||
479 | } | ||
480 | } | ||
481 | |||
482 | void sidtab_destroy(struct sidtab *s) | ||
483 | { | ||
484 | u32 i, level; | ||
485 | |||
486 | for (i = 0; i < SECINITSID_NUM; i++) | ||
487 | if (s->isids[i].set) | ||
488 | context_destroy(&s->isids[i].context); | ||
489 | |||
490 | level = SIDTAB_MAX_LEVEL; | ||
491 | while (level && !s->roots[level].ptr_inner) | ||
492 | --level; | ||
493 | |||
494 | sidtab_destroy_tree(s->roots[level], level); | ||
306 | } | 495 | } |
diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h index a1a1d2617b6f..bbd5c0d1f3bd 100644 --- a/security/selinux/ss/sidtab.h +++ b/security/selinux/ss/sidtab.h | |||
@@ -1,56 +1,96 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | /* | 2 | /* |
3 | * A security identifier table (sidtab) is a hash table | 3 | * A security identifier table (sidtab) is a lookup table |
4 | * of security context structures indexed by SID value. | 4 | * of security context structures indexed by SID value. |
5 | * | 5 | * |
6 | * Author : Stephen Smalley, <sds@tycho.nsa.gov> | 6 | * Original author: Stephen Smalley, <sds@tycho.nsa.gov> |
7 | * Author: Ondrej Mosnacek, <omosnacek@gmail.com> | ||
8 | * | ||
9 | * Copyright (C) 2018 Red Hat, Inc. | ||
7 | */ | 10 | */ |
8 | #ifndef _SS_SIDTAB_H_ | 11 | #ifndef _SS_SIDTAB_H_ |
9 | #define _SS_SIDTAB_H_ | 12 | #define _SS_SIDTAB_H_ |
10 | 13 | ||
14 | #include <linux/spinlock_types.h> | ||
15 | #include <linux/log2.h> | ||
16 | |||
11 | #include "context.h" | 17 | #include "context.h" |
12 | 18 | ||
13 | struct sidtab_node { | 19 | struct sidtab_entry_leaf { |
14 | u32 sid; /* security identifier */ | 20 | struct context context; |
15 | struct context context; /* security context structure */ | 21 | }; |
16 | struct sidtab_node *next; | 22 | |
23 | struct sidtab_node_inner; | ||
24 | struct sidtab_node_leaf; | ||
25 | |||
26 | union sidtab_entry_inner { | ||
27 | struct sidtab_node_inner *ptr_inner; | ||
28 | struct sidtab_node_leaf *ptr_leaf; | ||
29 | }; | ||
30 | |||
31 | /* align node size to page boundary */ | ||
32 | #define SIDTAB_NODE_ALLOC_SHIFT PAGE_SHIFT | ||
33 | #define SIDTAB_NODE_ALLOC_SIZE PAGE_SIZE | ||
34 | |||
35 | #define size_to_shift(size) ((size) == 1 ? 1 : (const_ilog2((size) - 1) + 1)) | ||
36 | |||
37 | #define SIDTAB_INNER_SHIFT \ | ||
38 | (SIDTAB_NODE_ALLOC_SHIFT - size_to_shift(sizeof(union sidtab_entry_inner))) | ||
39 | #define SIDTAB_INNER_ENTRIES ((size_t)1 << SIDTAB_INNER_SHIFT) | ||
40 | #define SIDTAB_LEAF_ENTRIES \ | ||
41 | (SIDTAB_NODE_ALLOC_SIZE / sizeof(struct sidtab_entry_leaf)) | ||
42 | |||
43 | #define SIDTAB_MAX_BITS 31 /* limited to INT_MAX due to atomic_t range */ | ||
44 | #define SIDTAB_MAX (((u32)1 << SIDTAB_MAX_BITS) - 1) | ||
45 | /* ensure enough tree levels for SIDTAB_MAX entries */ | ||
46 | #define SIDTAB_MAX_LEVEL \ | ||
47 | DIV_ROUND_UP(SIDTAB_MAX_BITS - size_to_shift(SIDTAB_LEAF_ENTRIES), \ | ||
48 | SIDTAB_INNER_SHIFT) | ||
49 | |||
50 | struct sidtab_node_leaf { | ||
51 | struct sidtab_entry_leaf entries[SIDTAB_LEAF_ENTRIES]; | ||
17 | }; | 52 | }; |
18 | 53 | ||
19 | #define SIDTAB_HASH_BITS 7 | 54 | struct sidtab_node_inner { |
20 | #define SIDTAB_HASH_BUCKETS (1 << SIDTAB_HASH_BITS) | 55 | union sidtab_entry_inner entries[SIDTAB_INNER_ENTRIES]; |
21 | #define SIDTAB_HASH_MASK (SIDTAB_HASH_BUCKETS-1) | 56 | }; |
22 | 57 | ||
23 | #define SIDTAB_SIZE SIDTAB_HASH_BUCKETS | 58 | struct sidtab_isid_entry { |
59 | int set; | ||
60 | struct context context; | ||
61 | }; | ||
62 | |||
63 | struct sidtab_convert_params { | ||
64 | int (*func)(struct context *oldc, struct context *newc, void *args); | ||
65 | void *args; | ||
66 | struct sidtab *target; | ||
67 | }; | ||
68 | |||
69 | #define SIDTAB_RCACHE_SIZE 3 | ||
24 | 70 | ||
25 | struct sidtab { | 71 | struct sidtab { |
26 | struct sidtab_node **htable; | 72 | union sidtab_entry_inner roots[SIDTAB_MAX_LEVEL + 1]; |
27 | unsigned int nel; /* number of elements */ | 73 | atomic_t count; |
28 | unsigned int next_sid; /* next SID to allocate */ | 74 | struct sidtab_convert_params *convert; |
29 | unsigned char shutdown; | ||
30 | #define SIDTAB_CACHE_LEN 3 | ||
31 | struct sidtab_node *cache[SIDTAB_CACHE_LEN]; | ||
32 | spinlock_t lock; | 75 | spinlock_t lock; |
76 | |||
77 | /* reverse lookup cache */ | ||
78 | atomic_t rcache[SIDTAB_RCACHE_SIZE]; | ||
79 | |||
80 | /* index == SID - 1 (no entry for SECSID_NULL) */ | ||
81 | struct sidtab_isid_entry isids[SECINITSID_NUM]; | ||
33 | }; | 82 | }; |
34 | 83 | ||
35 | int sidtab_init(struct sidtab *s); | 84 | int sidtab_init(struct sidtab *s); |
36 | int sidtab_insert(struct sidtab *s, u32 sid, struct context *context); | 85 | int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context); |
37 | struct context *sidtab_search(struct sidtab *s, u32 sid); | 86 | struct context *sidtab_search(struct sidtab *s, u32 sid); |
38 | struct context *sidtab_search_force(struct sidtab *s, u32 sid); | 87 | struct context *sidtab_search_force(struct sidtab *s, u32 sid); |
39 | 88 | ||
40 | int sidtab_map(struct sidtab *s, | 89 | int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params); |
41 | int (*apply) (u32 sid, | ||
42 | struct context *context, | ||
43 | void *args), | ||
44 | void *args); | ||
45 | 90 | ||
46 | int sidtab_context_to_sid(struct sidtab *s, | 91 | int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid); |
47 | struct context *context, | ||
48 | u32 *sid); | ||
49 | 92 | ||
50 | void sidtab_hash_eval(struct sidtab *h, char *tag); | ||
51 | void sidtab_destroy(struct sidtab *s); | 93 | void sidtab_destroy(struct sidtab *s); |
52 | void sidtab_set(struct sidtab *dst, struct sidtab *src); | ||
53 | void sidtab_shutdown(struct sidtab *s); | ||
54 | 94 | ||
55 | #endif /* _SS_SIDTAB_H_ */ | 95 | #endif /* _SS_SIDTAB_H_ */ |
56 | 96 | ||