diff options
Diffstat (limited to 'security/selinux/ss/services.c')
-rw-r--r-- | security/selinux/ss/services.c | 437 |
1 files changed, 236 insertions, 201 deletions
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index bfe122764c98..bdb7070dd3dc 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c | |||
@@ -17,9 +17,13 @@ | |||
17 | * | 17 | * |
18 | * Added support for NetLabel | 18 | * Added support for NetLabel |
19 | * | 19 | * |
20 | * Updated: Chad Sellers <csellers@tresys.com> | ||
21 | * | ||
22 | * Added validation of kernel classes and permissions | ||
23 | * | ||
20 | * Copyright (C) 2006 Hewlett-Packard Development Company, L.P. | 24 | * Copyright (C) 2006 Hewlett-Packard Development Company, L.P. |
21 | * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. | 25 | * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. |
22 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC | 26 | * Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC |
23 | * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> | 27 | * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> |
24 | * This program is free software; you can redistribute it and/or modify | 28 | * This program is free software; you can redistribute it and/or modify |
25 | * it under the terms of the GNU General Public License as published by | 29 | * it under the terms of the GNU General Public License as published by |
@@ -29,6 +33,7 @@ | |||
29 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
30 | #include <linux/string.h> | 34 | #include <linux/string.h> |
31 | #include <linux/spinlock.h> | 35 | #include <linux/spinlock.h> |
36 | #include <linux/rcupdate.h> | ||
32 | #include <linux/errno.h> | 37 | #include <linux/errno.h> |
33 | #include <linux/in.h> | 38 | #include <linux/in.h> |
34 | #include <linux/sched.h> | 39 | #include <linux/sched.h> |
@@ -49,10 +54,17 @@ | |||
49 | #include "mls.h" | 54 | #include "mls.h" |
50 | #include "objsec.h" | 55 | #include "objsec.h" |
51 | #include "selinux_netlabel.h" | 56 | #include "selinux_netlabel.h" |
57 | #include "xfrm.h" | ||
58 | #include "ebitmap.h" | ||
52 | 59 | ||
53 | extern void selnl_notify_policyload(u32 seqno); | 60 | extern void selnl_notify_policyload(u32 seqno); |
54 | unsigned int policydb_loaded_version; | 61 | unsigned int policydb_loaded_version; |
55 | 62 | ||
63 | /* | ||
64 | * This is declared in avc.c | ||
65 | */ | ||
66 | extern const struct selinux_class_perm selinux_class_perm; | ||
67 | |||
56 | static DEFINE_RWLOCK(policy_rwlock); | 68 | static DEFINE_RWLOCK(policy_rwlock); |
57 | #define POLICY_RDLOCK read_lock(&policy_rwlock) | 69 | #define POLICY_RDLOCK read_lock(&policy_rwlock) |
58 | #define POLICY_WRLOCK write_lock_irq(&policy_rwlock) | 70 | #define POLICY_WRLOCK write_lock_irq(&policy_rwlock) |
@@ -1019,86 +1031,112 @@ int security_change_sid(u32 ssid, | |||
1019 | } | 1031 | } |
1020 | 1032 | ||
1021 | /* | 1033 | /* |
1022 | * Verify that each permission that is defined under the | 1034 | * Verify that each kernel class that is defined in the |
1023 | * existing policy is still defined with the same value | 1035 | * policy is correct |
1024 | * in the new policy. | ||
1025 | */ | ||
1026 | static int validate_perm(void *key, void *datum, void *p) | ||
1027 | { | ||
1028 | struct hashtab *h; | ||
1029 | struct perm_datum *perdatum, *perdatum2; | ||
1030 | int rc = 0; | ||
1031 | |||
1032 | |||
1033 | h = p; | ||
1034 | perdatum = datum; | ||
1035 | |||
1036 | perdatum2 = hashtab_search(h, key); | ||
1037 | if (!perdatum2) { | ||
1038 | printk(KERN_ERR "security: permission %s disappeared", | ||
1039 | (char *)key); | ||
1040 | rc = -ENOENT; | ||
1041 | goto out; | ||
1042 | } | ||
1043 | if (perdatum->value != perdatum2->value) { | ||
1044 | printk(KERN_ERR "security: the value of permission %s changed", | ||
1045 | (char *)key); | ||
1046 | rc = -EINVAL; | ||
1047 | } | ||
1048 | out: | ||
1049 | return rc; | ||
1050 | } | ||
1051 | |||
1052 | /* | ||
1053 | * Verify that each class that is defined under the | ||
1054 | * existing policy is still defined with the same | ||
1055 | * attributes in the new policy. | ||
1056 | */ | 1036 | */ |
1057 | static int validate_class(void *key, void *datum, void *p) | 1037 | static int validate_classes(struct policydb *p) |
1058 | { | 1038 | { |
1059 | struct policydb *newp; | 1039 | int i, j; |
1060 | struct class_datum *cladatum, *cladatum2; | 1040 | struct class_datum *cladatum; |
1061 | int rc; | 1041 | struct perm_datum *perdatum; |
1062 | 1042 | u32 nprim, tmp, common_pts_len, perm_val, pol_val; | |
1063 | newp = p; | 1043 | u16 class_val; |
1064 | cladatum = datum; | 1044 | const struct selinux_class_perm *kdefs = &selinux_class_perm; |
1065 | 1045 | const char *def_class, *def_perm, *pol_class; | |
1066 | cladatum2 = hashtab_search(newp->p_classes.table, key); | 1046 | struct symtab *perms; |
1067 | if (!cladatum2) { | 1047 | |
1068 | printk(KERN_ERR "security: class %s disappeared\n", | 1048 | for (i = 1; i < kdefs->cts_len; i++) { |
1069 | (char *)key); | 1049 | def_class = kdefs->class_to_string[i]; |
1070 | rc = -ENOENT; | 1050 | if (i > p->p_classes.nprim) { |
1071 | goto out; | 1051 | printk(KERN_INFO |
1072 | } | 1052 | "security: class %s not defined in policy\n", |
1073 | if (cladatum->value != cladatum2->value) { | 1053 | def_class); |
1074 | printk(KERN_ERR "security: the value of class %s changed\n", | 1054 | continue; |
1075 | (char *)key); | 1055 | } |
1076 | rc = -EINVAL; | 1056 | pol_class = p->p_class_val_to_name[i-1]; |
1077 | goto out; | 1057 | if (strcmp(pol_class, def_class)) { |
1058 | printk(KERN_ERR | ||
1059 | "security: class %d is incorrect, found %s but should be %s\n", | ||
1060 | i, pol_class, def_class); | ||
1061 | return -EINVAL; | ||
1062 | } | ||
1078 | } | 1063 | } |
1079 | if ((cladatum->comdatum && !cladatum2->comdatum) || | 1064 | for (i = 0; i < kdefs->av_pts_len; i++) { |
1080 | (!cladatum->comdatum && cladatum2->comdatum)) { | 1065 | class_val = kdefs->av_perm_to_string[i].tclass; |
1081 | printk(KERN_ERR "security: the inherits clause for the access " | 1066 | perm_val = kdefs->av_perm_to_string[i].value; |
1082 | "vector definition for class %s changed\n", (char *)key); | 1067 | def_perm = kdefs->av_perm_to_string[i].name; |
1083 | rc = -EINVAL; | 1068 | if (class_val > p->p_classes.nprim) |
1084 | goto out; | 1069 | continue; |
1070 | pol_class = p->p_class_val_to_name[class_val-1]; | ||
1071 | cladatum = hashtab_search(p->p_classes.table, pol_class); | ||
1072 | BUG_ON(!cladatum); | ||
1073 | perms = &cladatum->permissions; | ||
1074 | nprim = 1 << (perms->nprim - 1); | ||
1075 | if (perm_val > nprim) { | ||
1076 | printk(KERN_INFO | ||
1077 | "security: permission %s in class %s not defined in policy\n", | ||
1078 | def_perm, pol_class); | ||
1079 | continue; | ||
1080 | } | ||
1081 | perdatum = hashtab_search(perms->table, def_perm); | ||
1082 | if (perdatum == NULL) { | ||
1083 | printk(KERN_ERR | ||
1084 | "security: permission %s in class %s not found in policy\n", | ||
1085 | def_perm, pol_class); | ||
1086 | return -EINVAL; | ||
1087 | } | ||
1088 | pol_val = 1 << (perdatum->value - 1); | ||
1089 | if (pol_val != perm_val) { | ||
1090 | printk(KERN_ERR | ||
1091 | "security: permission %s in class %s has incorrect value\n", | ||
1092 | def_perm, pol_class); | ||
1093 | return -EINVAL; | ||
1094 | } | ||
1085 | } | 1095 | } |
1086 | if (cladatum->comdatum) { | 1096 | for (i = 0; i < kdefs->av_inherit_len; i++) { |
1087 | rc = hashtab_map(cladatum->comdatum->permissions.table, validate_perm, | 1097 | class_val = kdefs->av_inherit[i].tclass; |
1088 | cladatum2->comdatum->permissions.table); | 1098 | if (class_val > p->p_classes.nprim) |
1089 | if (rc) { | 1099 | continue; |
1090 | printk(" in the access vector definition for class " | 1100 | pol_class = p->p_class_val_to_name[class_val-1]; |
1091 | "%s\n", (char *)key); | 1101 | cladatum = hashtab_search(p->p_classes.table, pol_class); |
1092 | goto out; | 1102 | BUG_ON(!cladatum); |
1103 | if (!cladatum->comdatum) { | ||
1104 | printk(KERN_ERR | ||
1105 | "security: class %s should have an inherits clause but does not\n", | ||
1106 | pol_class); | ||
1107 | return -EINVAL; | ||
1108 | } | ||
1109 | tmp = kdefs->av_inherit[i].common_base; | ||
1110 | common_pts_len = 0; | ||
1111 | while (!(tmp & 0x01)) { | ||
1112 | common_pts_len++; | ||
1113 | tmp >>= 1; | ||
1114 | } | ||
1115 | perms = &cladatum->comdatum->permissions; | ||
1116 | for (j = 0; j < common_pts_len; j++) { | ||
1117 | def_perm = kdefs->av_inherit[i].common_pts[j]; | ||
1118 | if (j >= perms->nprim) { | ||
1119 | printk(KERN_INFO | ||
1120 | "security: permission %s in class %s not defined in policy\n", | ||
1121 | def_perm, pol_class); | ||
1122 | continue; | ||
1123 | } | ||
1124 | perdatum = hashtab_search(perms->table, def_perm); | ||
1125 | if (perdatum == NULL) { | ||
1126 | printk(KERN_ERR | ||
1127 | "security: permission %s in class %s not found in policy\n", | ||
1128 | def_perm, pol_class); | ||
1129 | return -EINVAL; | ||
1130 | } | ||
1131 | if (perdatum->value != j + 1) { | ||
1132 | printk(KERN_ERR | ||
1133 | "security: permission %s in class %s has incorrect value\n", | ||
1134 | def_perm, pol_class); | ||
1135 | return -EINVAL; | ||
1136 | } | ||
1093 | } | 1137 | } |
1094 | } | 1138 | } |
1095 | rc = hashtab_map(cladatum->permissions.table, validate_perm, | 1139 | return 0; |
1096 | cladatum2->permissions.table); | ||
1097 | if (rc) | ||
1098 | printk(" in access vector definition for class %s\n", | ||
1099 | (char *)key); | ||
1100 | out: | ||
1101 | return rc; | ||
1102 | } | 1140 | } |
1103 | 1141 | ||
1104 | /* Clone the SID into the new SID table. */ | 1142 | /* Clone the SID into the new SID table. */ |
@@ -1243,6 +1281,16 @@ int security_load_policy(void *data, size_t len) | |||
1243 | avtab_cache_destroy(); | 1281 | avtab_cache_destroy(); |
1244 | return -EINVAL; | 1282 | return -EINVAL; |
1245 | } | 1283 | } |
1284 | /* Verify that the kernel defined classes are correct. */ | ||
1285 | if (validate_classes(&policydb)) { | ||
1286 | printk(KERN_ERR | ||
1287 | "security: the definition of a class is incorrect\n"); | ||
1288 | LOAD_UNLOCK; | ||
1289 | sidtab_destroy(&sidtab); | ||
1290 | policydb_destroy(&policydb); | ||
1291 | avtab_cache_destroy(); | ||
1292 | return -EINVAL; | ||
1293 | } | ||
1246 | policydb_loaded_version = policydb.policyvers; | 1294 | policydb_loaded_version = policydb.policyvers; |
1247 | ss_initialized = 1; | 1295 | ss_initialized = 1; |
1248 | seqno = ++latest_granting; | 1296 | seqno = ++latest_granting; |
@@ -1265,10 +1313,10 @@ int security_load_policy(void *data, size_t len) | |||
1265 | 1313 | ||
1266 | sidtab_init(&newsidtab); | 1314 | sidtab_init(&newsidtab); |
1267 | 1315 | ||
1268 | /* Verify that the existing classes did not change. */ | 1316 | /* Verify that the kernel defined classes are correct. */ |
1269 | if (hashtab_map(policydb.p_classes.table, validate_class, &newpolicydb)) { | 1317 | if (validate_classes(&newpolicydb)) { |
1270 | printk(KERN_ERR "security: the definition of an existing " | 1318 | printk(KERN_ERR |
1271 | "class changed\n"); | 1319 | "security: the definition of a class is incorrect\n"); |
1272 | rc = -EINVAL; | 1320 | rc = -EINVAL; |
1273 | goto err; | 1321 | goto err; |
1274 | } | 1322 | } |
@@ -2145,6 +2193,32 @@ void selinux_audit_set_callback(int (*callback)(void)) | |||
2145 | aurule_callback = callback; | 2193 | aurule_callback = callback; |
2146 | } | 2194 | } |
2147 | 2195 | ||
2196 | /** | ||
2197 | * security_skb_extlbl_sid - Determine the external label of a packet | ||
2198 | * @skb: the packet | ||
2199 | * @base_sid: the SELinux SID to use as a context for MLS only external labels | ||
2200 | * @sid: the packet's SID | ||
2201 | * | ||
2202 | * Description: | ||
2203 | * Check the various different forms of external packet labeling and determine | ||
2204 | * the external SID for the packet. | ||
2205 | * | ||
2206 | */ | ||
2207 | void security_skb_extlbl_sid(struct sk_buff *skb, u32 base_sid, u32 *sid) | ||
2208 | { | ||
2209 | u32 xfrm_sid; | ||
2210 | u32 nlbl_sid; | ||
2211 | |||
2212 | selinux_skb_xfrm_sid(skb, &xfrm_sid); | ||
2213 | if (selinux_netlbl_skbuff_getsid(skb, | ||
2214 | (xfrm_sid == SECSID_NULL ? | ||
2215 | base_sid : xfrm_sid), | ||
2216 | &nlbl_sid) != 0) | ||
2217 | nlbl_sid = SECSID_NULL; | ||
2218 | |||
2219 | *sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid); | ||
2220 | } | ||
2221 | |||
2148 | #ifdef CONFIG_NETLABEL | 2222 | #ifdef CONFIG_NETLABEL |
2149 | /* | 2223 | /* |
2150 | * This is the structure we store inside the NetLabel cache block. | 2224 | * This is the structure we store inside the NetLabel cache block. |
@@ -2209,8 +2283,6 @@ static void selinux_netlbl_cache_add(struct sk_buff *skb, struct context *ctx) | |||
2209 | cache = kzalloc(sizeof(*cache), GFP_ATOMIC); | 2283 | cache = kzalloc(sizeof(*cache), GFP_ATOMIC); |
2210 | if (cache == NULL) | 2284 | if (cache == NULL) |
2211 | goto netlbl_cache_add_return; | 2285 | goto netlbl_cache_add_return; |
2212 | secattr.cache->free = selinux_netlbl_cache_free; | ||
2213 | secattr.cache->data = (void *)cache; | ||
2214 | 2286 | ||
2215 | cache->type = NETLBL_CACHE_T_MLS; | 2287 | cache->type = NETLBL_CACHE_T_MLS; |
2216 | if (ebitmap_cpy(&cache->data.mls_label.level[0].cat, | 2288 | if (ebitmap_cpy(&cache->data.mls_label.level[0].cat, |
@@ -2223,6 +2295,10 @@ static void selinux_netlbl_cache_add(struct sk_buff *skb, struct context *ctx) | |||
2223 | cache->data.mls_label.level[0].sens = ctx->range.level[0].sens; | 2295 | cache->data.mls_label.level[0].sens = ctx->range.level[0].sens; |
2224 | cache->data.mls_label.level[1].sens = ctx->range.level[0].sens; | 2296 | cache->data.mls_label.level[1].sens = ctx->range.level[0].sens; |
2225 | 2297 | ||
2298 | secattr.cache->free = selinux_netlbl_cache_free; | ||
2299 | secattr.cache->data = (void *)cache; | ||
2300 | secattr.flags = NETLBL_SECATTR_CACHE; | ||
2301 | |||
2226 | netlbl_cache_add(skb, &secattr); | 2302 | netlbl_cache_add(skb, &secattr); |
2227 | 2303 | ||
2228 | netlbl_cache_add_return: | 2304 | netlbl_cache_add_return: |
@@ -2268,7 +2344,7 @@ static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb, | |||
2268 | 2344 | ||
2269 | POLICY_RDLOCK; | 2345 | POLICY_RDLOCK; |
2270 | 2346 | ||
2271 | if (secattr->cache) { | 2347 | if (secattr->flags & NETLBL_SECATTR_CACHE) { |
2272 | cache = NETLBL_CACHE(secattr->cache->data); | 2348 | cache = NETLBL_CACHE(secattr->cache->data); |
2273 | switch (cache->type) { | 2349 | switch (cache->type) { |
2274 | case NETLBL_CACHE_T_SID: | 2350 | case NETLBL_CACHE_T_SID: |
@@ -2301,7 +2377,7 @@ static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb, | |||
2301 | default: | 2377 | default: |
2302 | goto netlbl_secattr_to_sid_return; | 2378 | goto netlbl_secattr_to_sid_return; |
2303 | } | 2379 | } |
2304 | } else if (secattr->mls_lvl_vld) { | 2380 | } else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) { |
2305 | ctx = sidtab_search(&sidtab, base_sid); | 2381 | ctx = sidtab_search(&sidtab, base_sid); |
2306 | if (ctx == NULL) | 2382 | if (ctx == NULL) |
2307 | goto netlbl_secattr_to_sid_return; | 2383 | goto netlbl_secattr_to_sid_return; |
@@ -2309,13 +2385,10 @@ static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb, | |||
2309 | ctx_new.user = ctx->user; | 2385 | ctx_new.user = ctx->user; |
2310 | ctx_new.role = ctx->role; | 2386 | ctx_new.role = ctx->role; |
2311 | ctx_new.type = ctx->type; | 2387 | ctx_new.type = ctx->type; |
2312 | mls_import_lvl(&ctx_new, secattr->mls_lvl, secattr->mls_lvl); | 2388 | mls_import_netlbl_lvl(&ctx_new, secattr); |
2313 | if (secattr->mls_cat) { | 2389 | if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { |
2314 | if (mls_import_cat(&ctx_new, | 2390 | if (ebitmap_netlbl_import(&ctx_new.range.level[0].cat, |
2315 | secattr->mls_cat, | 2391 | secattr->mls_cat) != 0) |
2316 | secattr->mls_cat_len, | ||
2317 | NULL, | ||
2318 | 0) != 0) | ||
2319 | goto netlbl_secattr_to_sid_return; | 2392 | goto netlbl_secattr_to_sid_return; |
2320 | ctx_new.range.level[1].cat.highbit = | 2393 | ctx_new.range.level[1].cat.highbit = |
2321 | ctx_new.range.level[0].cat.highbit; | 2394 | ctx_new.range.level[0].cat.highbit; |
@@ -2360,20 +2433,20 @@ netlbl_secattr_to_sid_return_cleanup: | |||
2360 | * assign to the packet. Returns zero on success, negative values on failure. | 2433 | * assign to the packet. Returns zero on success, negative values on failure. |
2361 | * | 2434 | * |
2362 | */ | 2435 | */ |
2363 | static int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, | 2436 | int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid) |
2364 | u32 base_sid, | ||
2365 | u32 *sid) | ||
2366 | { | 2437 | { |
2367 | int rc; | 2438 | int rc; |
2368 | struct netlbl_lsm_secattr secattr; | 2439 | struct netlbl_lsm_secattr secattr; |
2369 | 2440 | ||
2370 | netlbl_secattr_init(&secattr); | 2441 | netlbl_secattr_init(&secattr); |
2371 | rc = netlbl_skbuff_getattr(skb, &secattr); | 2442 | rc = netlbl_skbuff_getattr(skb, &secattr); |
2372 | if (rc == 0) | 2443 | if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) |
2373 | rc = selinux_netlbl_secattr_to_sid(skb, | 2444 | rc = selinux_netlbl_secattr_to_sid(skb, |
2374 | &secattr, | 2445 | &secattr, |
2375 | base_sid, | 2446 | base_sid, |
2376 | sid); | 2447 | sid); |
2448 | else | ||
2449 | *sid = SECSID_NULL; | ||
2377 | netlbl_secattr_destroy(&secattr); | 2450 | netlbl_secattr_destroy(&secattr); |
2378 | 2451 | ||
2379 | return rc; | 2452 | return rc; |
@@ -2386,7 +2459,9 @@ static int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, | |||
2386 | * | 2459 | * |
2387 | * Description: | 2460 | * Description: |
2388 | * Attempt to label a socket using the NetLabel mechanism using the given | 2461 | * Attempt to label a socket using the NetLabel mechanism using the given |
2389 | * SID. Returns zero values on success, negative values on failure. | 2462 | * SID. Returns zero values on success, negative values on failure. The |
2463 | * caller is responsibile for calling rcu_read_lock() before calling this | ||
2464 | * this function and rcu_read_unlock() after this function returns. | ||
2390 | * | 2465 | * |
2391 | */ | 2466 | */ |
2392 | static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) | 2467 | static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) |
@@ -2409,19 +2484,18 @@ static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) | |||
2409 | 2484 | ||
2410 | secattr.domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1], | 2485 | secattr.domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1], |
2411 | GFP_ATOMIC); | 2486 | GFP_ATOMIC); |
2412 | mls_export_lvl(ctx, &secattr.mls_lvl, NULL); | 2487 | secattr.flags |= NETLBL_SECATTR_DOMAIN; |
2413 | secattr.mls_lvl_vld = 1; | 2488 | mls_export_netlbl_lvl(ctx, &secattr); |
2414 | rc = mls_export_cat(ctx, | 2489 | rc = mls_export_netlbl_cat(ctx, &secattr); |
2415 | &secattr.mls_cat, | ||
2416 | &secattr.mls_cat_len, | ||
2417 | NULL, | ||
2418 | NULL); | ||
2419 | if (rc != 0) | 2490 | if (rc != 0) |
2420 | goto netlbl_socket_setsid_return; | 2491 | goto netlbl_socket_setsid_return; |
2421 | 2492 | ||
2422 | rc = netlbl_socket_setattr(sock, &secattr); | 2493 | rc = netlbl_socket_setattr(sock, &secattr); |
2423 | if (rc == 0) | 2494 | if (rc == 0) { |
2495 | spin_lock(&sksec->nlbl_lock); | ||
2424 | sksec->nlbl_state = NLBL_LABELED; | 2496 | sksec->nlbl_state = NLBL_LABELED; |
2497 | spin_unlock(&sksec->nlbl_lock); | ||
2498 | } | ||
2425 | 2499 | ||
2426 | netlbl_socket_setsid_return: | 2500 | netlbl_socket_setsid_return: |
2427 | POLICY_RDUNLOCK; | 2501 | POLICY_RDUNLOCK; |
@@ -2430,6 +2504,25 @@ netlbl_socket_setsid_return: | |||
2430 | } | 2504 | } |
2431 | 2505 | ||
2432 | /** | 2506 | /** |
2507 | * selinux_netlbl_sk_security_reset - Reset the NetLabel fields | ||
2508 | * @ssec: the sk_security_struct | ||
2509 | * @family: the socket family | ||
2510 | * | ||
2511 | * Description: | ||
2512 | * Called when the NetLabel state of a sk_security_struct needs to be reset. | ||
2513 | * The caller is responsibile for all the NetLabel sk_security_struct locking. | ||
2514 | * | ||
2515 | */ | ||
2516 | void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec, | ||
2517 | int family) | ||
2518 | { | ||
2519 | if (family == PF_INET) | ||
2520 | ssec->nlbl_state = NLBL_REQUIRE; | ||
2521 | else | ||
2522 | ssec->nlbl_state = NLBL_UNSET; | ||
2523 | } | ||
2524 | |||
2525 | /** | ||
2433 | * selinux_netlbl_sk_security_init - Setup the NetLabel fields | 2526 | * selinux_netlbl_sk_security_init - Setup the NetLabel fields |
2434 | * @ssec: the sk_security_struct | 2527 | * @ssec: the sk_security_struct |
2435 | * @family: the socket family | 2528 | * @family: the socket family |
@@ -2442,14 +2535,13 @@ netlbl_socket_setsid_return: | |||
2442 | void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, | 2535 | void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, |
2443 | int family) | 2536 | int family) |
2444 | { | 2537 | { |
2445 | if (family == PF_INET) | 2538 | /* No locking needed, we are the only one who has access to ssec */ |
2446 | ssec->nlbl_state = NLBL_REQUIRE; | 2539 | selinux_netlbl_sk_security_reset(ssec, family); |
2447 | else | 2540 | spin_lock_init(&ssec->nlbl_lock); |
2448 | ssec->nlbl_state = NLBL_UNSET; | ||
2449 | } | 2541 | } |
2450 | 2542 | ||
2451 | /** | 2543 | /** |
2452 | * selinux_netlbl_sk_clone_security - Copy the NetLabel fields | 2544 | * selinux_netlbl_sk_security_clone - Copy the NetLabel fields |
2453 | * @ssec: the original sk_security_struct | 2545 | * @ssec: the original sk_security_struct |
2454 | * @newssec: the cloned sk_security_struct | 2546 | * @newssec: the cloned sk_security_struct |
2455 | * | 2547 | * |
@@ -2458,41 +2550,41 @@ void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, | |||
2458 | * @newssec. | 2550 | * @newssec. |
2459 | * | 2551 | * |
2460 | */ | 2552 | */ |
2461 | void selinux_netlbl_sk_clone_security(struct sk_security_struct *ssec, | 2553 | void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, |
2462 | struct sk_security_struct *newssec) | 2554 | struct sk_security_struct *newssec) |
2463 | { | 2555 | { |
2556 | /* We don't need to take newssec->nlbl_lock because we are the only | ||
2557 | * thread with access to newssec, but we do need to take the RCU read | ||
2558 | * lock as other threads could have access to ssec */ | ||
2559 | rcu_read_lock(); | ||
2560 | selinux_netlbl_sk_security_reset(newssec, ssec->sk->sk_family); | ||
2464 | newssec->sclass = ssec->sclass; | 2561 | newssec->sclass = ssec->sclass; |
2465 | if (ssec->nlbl_state != NLBL_UNSET) | 2562 | rcu_read_unlock(); |
2466 | newssec->nlbl_state = NLBL_REQUIRE; | ||
2467 | else | ||
2468 | newssec->nlbl_state = NLBL_UNSET; | ||
2469 | } | 2563 | } |
2470 | 2564 | ||
2471 | /** | 2565 | /** |
2472 | * selinux_netlbl_socket_post_create - Label a socket using NetLabel | 2566 | * selinux_netlbl_socket_post_create - Label a socket using NetLabel |
2473 | * @sock: the socket to label | 2567 | * @sock: the socket to label |
2474 | * @sock_family: the socket family | ||
2475 | * @sid: the SID to use | ||
2476 | * | 2568 | * |
2477 | * Description: | 2569 | * Description: |
2478 | * Attempt to label a socket using the NetLabel mechanism using the given | 2570 | * Attempt to label a socket using the NetLabel mechanism using the given |
2479 | * SID. Returns zero values on success, negative values on failure. | 2571 | * SID. Returns zero values on success, negative values on failure. |
2480 | * | 2572 | * |
2481 | */ | 2573 | */ |
2482 | int selinux_netlbl_socket_post_create(struct socket *sock, | 2574 | int selinux_netlbl_socket_post_create(struct socket *sock) |
2483 | int sock_family, | ||
2484 | u32 sid) | ||
2485 | { | 2575 | { |
2576 | int rc = 0; | ||
2486 | struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; | 2577 | struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; |
2487 | struct sk_security_struct *sksec = sock->sk->sk_security; | 2578 | struct sk_security_struct *sksec = sock->sk->sk_security; |
2488 | 2579 | ||
2489 | sksec->sclass = isec->sclass; | 2580 | sksec->sclass = isec->sclass; |
2490 | 2581 | ||
2491 | if (sock_family != PF_INET) | 2582 | rcu_read_lock(); |
2492 | return 0; | 2583 | if (sksec->nlbl_state == NLBL_REQUIRE) |
2584 | rc = selinux_netlbl_socket_setsid(sock, sksec->sid); | ||
2585 | rcu_read_unlock(); | ||
2493 | 2586 | ||
2494 | sksec->nlbl_state = NLBL_REQUIRE; | 2587 | return rc; |
2495 | return selinux_netlbl_socket_setsid(sock, sid); | ||
2496 | } | 2588 | } |
2497 | 2589 | ||
2498 | /** | 2590 | /** |
@@ -2514,11 +2606,16 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) | |||
2514 | 2606 | ||
2515 | sksec->sclass = isec->sclass; | 2607 | sksec->sclass = isec->sclass; |
2516 | 2608 | ||
2517 | if (sk->sk_family != PF_INET) | 2609 | rcu_read_lock(); |
2610 | |||
2611 | if (sksec->nlbl_state != NLBL_REQUIRE) { | ||
2612 | rcu_read_unlock(); | ||
2518 | return; | 2613 | return; |
2614 | } | ||
2519 | 2615 | ||
2520 | netlbl_secattr_init(&secattr); | 2616 | netlbl_secattr_init(&secattr); |
2521 | if (netlbl_sock_getattr(sk, &secattr) == 0 && | 2617 | if (netlbl_sock_getattr(sk, &secattr) == 0 && |
2618 | secattr.flags != NETLBL_SECATTR_NONE && | ||
2522 | selinux_netlbl_secattr_to_sid(NULL, | 2619 | selinux_netlbl_secattr_to_sid(NULL, |
2523 | &secattr, | 2620 | &secattr, |
2524 | SECINITSID_UNLABELED, | 2621 | SECINITSID_UNLABELED, |
@@ -2526,35 +2623,12 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) | |||
2526 | sksec->peer_sid = nlbl_peer_sid; | 2623 | sksec->peer_sid = nlbl_peer_sid; |
2527 | netlbl_secattr_destroy(&secattr); | 2624 | netlbl_secattr_destroy(&secattr); |
2528 | 2625 | ||
2529 | sksec->nlbl_state = NLBL_REQUIRE; | ||
2530 | |||
2531 | /* Try to set the NetLabel on the socket to save time later, if we fail | 2626 | /* Try to set the NetLabel on the socket to save time later, if we fail |
2532 | * here we will pick up the pieces in later calls to | 2627 | * here we will pick up the pieces in later calls to |
2533 | * selinux_netlbl_inode_permission(). */ | 2628 | * selinux_netlbl_inode_permission(). */ |
2534 | selinux_netlbl_socket_setsid(sock, sksec->sid); | 2629 | selinux_netlbl_socket_setsid(sock, sksec->sid); |
2535 | } | ||
2536 | |||
2537 | /** | ||
2538 | * selinux_netlbl_inet_conn_request - Handle a new connection request | ||
2539 | * @skb: the packet | ||
2540 | * @sock_sid: the SID of the parent socket | ||
2541 | * | ||
2542 | * Description: | ||
2543 | * If present, use the security attributes of the packet in @skb and the | ||
2544 | * parent sock's SID to arrive at a SID for the new child sock. Returns the | ||
2545 | * SID of the connection or SECSID_NULL on failure. | ||
2546 | * | ||
2547 | */ | ||
2548 | u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, u32 sock_sid) | ||
2549 | { | ||
2550 | int rc; | ||
2551 | u32 peer_sid; | ||
2552 | |||
2553 | rc = selinux_netlbl_skbuff_getsid(skb, sock_sid, &peer_sid); | ||
2554 | if (rc != 0) | ||
2555 | return SECSID_NULL; | ||
2556 | 2630 | ||
2557 | return peer_sid; | 2631 | rcu_read_unlock(); |
2558 | } | 2632 | } |
2559 | 2633 | ||
2560 | /** | 2634 | /** |
@@ -2572,25 +2646,24 @@ u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, u32 sock_sid) | |||
2572 | int selinux_netlbl_inode_permission(struct inode *inode, int mask) | 2646 | int selinux_netlbl_inode_permission(struct inode *inode, int mask) |
2573 | { | 2647 | { |
2574 | int rc; | 2648 | int rc; |
2575 | struct inode_security_struct *isec; | ||
2576 | struct sk_security_struct *sksec; | 2649 | struct sk_security_struct *sksec; |
2577 | struct socket *sock; | 2650 | struct socket *sock; |
2578 | 2651 | ||
2579 | if (!S_ISSOCK(inode->i_mode)) | 2652 | if (!S_ISSOCK(inode->i_mode) || |
2653 | ((mask & (MAY_WRITE | MAY_APPEND)) == 0)) | ||
2580 | return 0; | 2654 | return 0; |
2581 | |||
2582 | sock = SOCKET_I(inode); | 2655 | sock = SOCKET_I(inode); |
2583 | isec = inode->i_security; | ||
2584 | sksec = sock->sk->sk_security; | 2656 | sksec = sock->sk->sk_security; |
2585 | mutex_lock(&isec->lock); | 2657 | |
2586 | if (unlikely(sksec->nlbl_state == NLBL_REQUIRE && | 2658 | rcu_read_lock(); |
2587 | (mask & (MAY_WRITE | MAY_APPEND)))) { | 2659 | if (sksec->nlbl_state != NLBL_REQUIRE) { |
2588 | lock_sock(sock->sk); | 2660 | rcu_read_unlock(); |
2589 | rc = selinux_netlbl_socket_setsid(sock, sksec->sid); | 2661 | return 0; |
2590 | release_sock(sock->sk); | 2662 | } |
2591 | } else | 2663 | lock_sock(sock->sk); |
2592 | rc = 0; | 2664 | rc = selinux_netlbl_socket_setsid(sock, sksec->sid); |
2593 | mutex_unlock(&isec->lock); | 2665 | release_sock(sock->sk); |
2666 | rcu_read_unlock(); | ||
2594 | 2667 | ||
2595 | return rc; | 2668 | return rc; |
2596 | } | 2669 | } |
@@ -2648,42 +2721,6 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, | |||
2648 | } | 2721 | } |
2649 | 2722 | ||
2650 | /** | 2723 | /** |
2651 | * selinux_netlbl_socket_getpeersec_stream - Return the connected peer's SID | ||
2652 | * @sock: the socket | ||
2653 | * | ||
2654 | * Description: | ||
2655 | * Examine @sock to find the connected peer's SID. Returns the SID on success | ||
2656 | * or SECSID_NULL on error. | ||
2657 | * | ||
2658 | */ | ||
2659 | u32 selinux_netlbl_socket_getpeersec_stream(struct socket *sock) | ||
2660 | { | ||
2661 | struct sk_security_struct *sksec = sock->sk->sk_security; | ||
2662 | return sksec->peer_sid; | ||
2663 | } | ||
2664 | |||
2665 | /** | ||
2666 | * selinux_netlbl_socket_getpeersec_dgram - Return the SID of a NetLabel packet | ||
2667 | * @skb: the packet | ||
2668 | * | ||
2669 | * Description: | ||
2670 | * Examine @skb to find the SID assigned to it by NetLabel. Returns the SID on | ||
2671 | * success, SECSID_NULL on error. | ||
2672 | * | ||
2673 | */ | ||
2674 | u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb) | ||
2675 | { | ||
2676 | int peer_sid; | ||
2677 | |||
2678 | if (selinux_netlbl_skbuff_getsid(skb, | ||
2679 | SECINITSID_UNLABELED, | ||
2680 | &peer_sid) != 0) | ||
2681 | return SECSID_NULL; | ||
2682 | |||
2683 | return peer_sid; | ||
2684 | } | ||
2685 | |||
2686 | /** | ||
2687 | * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel | 2724 | * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel |
2688 | * @sock: the socket | 2725 | * @sock: the socket |
2689 | * @level: the socket level or protocol | 2726 | * @level: the socket level or protocol |
@@ -2701,21 +2738,19 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, | |||
2701 | int optname) | 2738 | int optname) |
2702 | { | 2739 | { |
2703 | int rc = 0; | 2740 | int rc = 0; |
2704 | struct inode *inode = SOCK_INODE(sock); | ||
2705 | struct sk_security_struct *sksec = sock->sk->sk_security; | 2741 | struct sk_security_struct *sksec = sock->sk->sk_security; |
2706 | struct inode_security_struct *isec = inode->i_security; | ||
2707 | struct netlbl_lsm_secattr secattr; | 2742 | struct netlbl_lsm_secattr secattr; |
2708 | 2743 | ||
2709 | mutex_lock(&isec->lock); | 2744 | rcu_read_lock(); |
2710 | if (level == IPPROTO_IP && optname == IP_OPTIONS && | 2745 | if (level == IPPROTO_IP && optname == IP_OPTIONS && |
2711 | sksec->nlbl_state == NLBL_LABELED) { | 2746 | sksec->nlbl_state == NLBL_LABELED) { |
2712 | netlbl_secattr_init(&secattr); | 2747 | netlbl_secattr_init(&secattr); |
2713 | rc = netlbl_socket_getattr(sock, &secattr); | 2748 | rc = netlbl_socket_getattr(sock, &secattr); |
2714 | if (rc == 0 && (secattr.cache || secattr.mls_lvl_vld)) | 2749 | if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) |
2715 | rc = -EACCES; | 2750 | rc = -EACCES; |
2716 | netlbl_secattr_destroy(&secattr); | 2751 | netlbl_secattr_destroy(&secattr); |
2717 | } | 2752 | } |
2718 | mutex_unlock(&isec->lock); | 2753 | rcu_read_unlock(); |
2719 | 2754 | ||
2720 | return rc; | 2755 | return rc; |
2721 | } | 2756 | } |