From fc5d81e69d15c65ca20d9e5b4e242690e3e9c27d Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Mon, 27 Nov 2006 15:16:48 +0900 Subject: selinux: fix dentry_open() error check The return value of dentry_open() shoud be checked by IS_ERR(). Cc: Stephen Smalley Cc: James Morris Signed-off-by: Akinobu Mita Signed-off-by: James Morris --- security/selinux/hooks.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 8ab5679..28ee187 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1754,7 +1754,8 @@ static inline void flush_unauthorized_files(struct files_struct * files) get_file(devnull); } else { devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR); - if (!devnull) { + if (IS_ERR(devnull)) { + devnull = NULL; put_unused_fd(fd); fput(file); continue; -- cgit v1.1 From 5a64d4438ed1e759ccd30d9e90842bf360f19298 Mon Sep 17 00:00:00 2001 From: Chad Sellers Date: Mon, 6 Nov 2006 12:38:15 -0500 Subject: SELinux: remove current object class and permission validation mechanism Removes the current SELinux object class and permission validation code, as the current code makes it impossible to change or remove object classes and permissions on a running system. Additionally, the current code does not actually validate that the classes and permissions are correct, but instead merely validates that they do not change between policy reloads. Signed-off-by: Chad Sellers Acked-by: Stephen Smalley Signed-off-by: James Morris --- security/selinux/ss/services.c | 91 ------------------------------------------ 1 file changed, 91 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index bfe1227..33ae102 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1018,89 +1018,6 @@ int security_change_sid(u32 ssid, return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid); } -/* - * Verify that each permission that is defined under the - * existing policy is still defined with the same value - * in the new policy. - */ -static int validate_perm(void *key, void *datum, void *p) -{ - struct hashtab *h; - struct perm_datum *perdatum, *perdatum2; - int rc = 0; - - - h = p; - perdatum = datum; - - perdatum2 = hashtab_search(h, key); - if (!perdatum2) { - printk(KERN_ERR "security: permission %s disappeared", - (char *)key); - rc = -ENOENT; - goto out; - } - if (perdatum->value != perdatum2->value) { - printk(KERN_ERR "security: the value of permission %s changed", - (char *)key); - rc = -EINVAL; - } -out: - return rc; -} - -/* - * Verify that each class that is defined under the - * existing policy is still defined with the same - * attributes in the new policy. - */ -static int validate_class(void *key, void *datum, void *p) -{ - struct policydb *newp; - struct class_datum *cladatum, *cladatum2; - int rc; - - newp = p; - cladatum = datum; - - cladatum2 = hashtab_search(newp->p_classes.table, key); - if (!cladatum2) { - printk(KERN_ERR "security: class %s disappeared\n", - (char *)key); - rc = -ENOENT; - goto out; - } - if (cladatum->value != cladatum2->value) { - printk(KERN_ERR "security: the value of class %s changed\n", - (char *)key); - rc = -EINVAL; - goto out; - } - if ((cladatum->comdatum && !cladatum2->comdatum) || - (!cladatum->comdatum && cladatum2->comdatum)) { - printk(KERN_ERR "security: the inherits clause for the access " - "vector definition for class %s changed\n", (char *)key); - rc = -EINVAL; - goto out; - } - if (cladatum->comdatum) { - rc = hashtab_map(cladatum->comdatum->permissions.table, validate_perm, - cladatum2->comdatum->permissions.table); - if (rc) { - printk(" in the access vector definition for class " - "%s\n", (char *)key); - goto out; - } - } - rc = hashtab_map(cladatum->permissions.table, validate_perm, - cladatum2->permissions.table); - if (rc) - printk(" in access vector definition for class %s\n", - (char *)key); -out: - return rc; -} - /* Clone the SID into the new SID table. */ static int clone_sid(u32 sid, struct context *context, @@ -1265,14 +1182,6 @@ int security_load_policy(void *data, size_t len) sidtab_init(&newsidtab); - /* Verify that the existing classes did not change. */ - if (hashtab_map(policydb.p_classes.table, validate_class, &newpolicydb)) { - printk(KERN_ERR "security: the definition of an existing " - "class changed\n"); - rc = -EINVAL; - goto err; - } - /* Clone the SID table. */ sidtab_shutdown(&sidtab); if (sidtab_map(&sidtab, clone_sid, &newsidtab)) { -- cgit v1.1 From 5c45899879e8caadb78f04c9c639f4c2025b9f00 Mon Sep 17 00:00:00 2001 From: Chad Sellers Date: Mon, 6 Nov 2006 12:38:16 -0500 Subject: SELinux: export object class and permission definitions Moves the definition of the 3 structs containing object class and permission definitions from avc.c to avc_ss.h so that the security server can access them for validation on policy load. This also adds a new struct type, defined_classes_perms_t, suitable for allowing the security server to access these data structures from the avc. Signed-off-by: Chad Sellers Acked-by: Stephen Smalley Signed-off-by: James Morris --- security/selinux/avc.c | 23 +++++++++++------------ security/selinux/include/avc_ss.h | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 12 deletions(-) (limited to 'security') diff --git a/security/selinux/avc.c b/security/selinux/avc.c index a300702..74c0319 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -32,12 +32,7 @@ #include "avc.h" #include "avc_ss.h" -static const struct av_perm_to_string -{ - u16 tclass; - u32 value; - const char *name; -} av_perm_to_string[] = { +static const struct av_perm_to_string av_perm_to_string[] = { #define S_(c, v, s) { c, v, s }, #include "av_perm_to_string.h" #undef S_ @@ -57,17 +52,21 @@ static const char *class_to_string[] = { #undef TE_ #undef S_ -static const struct av_inherit -{ - u16 tclass; - const char **common_pts; - u32 common_base; -} av_inherit[] = { +static const struct av_inherit av_inherit[] = { #define S_(c, i, b) { c, common_##i##_perm_to_string, b }, #include "av_inherit.h" #undef S_ }; +const struct selinux_class_perm selinux_class_perm = { + av_perm_to_string, + ARRAY_SIZE(av_perm_to_string), + class_to_string, + ARRAY_SIZE(class_to_string), + av_inherit, + ARRAY_SIZE(av_inherit) +}; + #define AVC_CACHE_SLOTS 512 #define AVC_DEF_CACHE_THRESHOLD 512 #define AVC_CACHE_RECLAIM 16 diff --git a/security/selinux/include/avc_ss.h b/security/selinux/include/avc_ss.h index 450a283..ff869e8 100644 --- a/security/selinux/include/avc_ss.h +++ b/security/selinux/include/avc_ss.h @@ -10,5 +10,29 @@ int avc_ss_reset(u32 seqno); +struct av_perm_to_string +{ + u16 tclass; + u32 value; + const char *name; +}; + +struct av_inherit +{ + u16 tclass; + const char **common_pts; + u32 common_base; +}; + +struct selinux_class_perm +{ + const struct av_perm_to_string *av_perm_to_string; + u32 av_pts_len; + const char **class_to_string; + u32 cts_len; + const struct av_inherit *av_inherit; + u32 av_inherit_len; +}; + #endif /* _SELINUX_AVC_SS_H_ */ -- cgit v1.1 From bb242497474da317a7169cc939c741ccf2e79e8c Mon Sep 17 00:00:00 2001 From: Chad Sellers Date: Mon, 6 Nov 2006 12:38:17 -0500 Subject: SELinux: ensure keys constant in hashtab_search Makes the key argument passed into hashtab_search and all the functions it calls constant. These functions include hash table function pointers hash_value and keycmp. The only implementations of these currently are symhash and symcmp, which do not modify the key. The key parameter should never be changed by any of these, so it should be const. This is necessary to allow calling these functions with keys found in kernel object class and permission definitions. Signed-off-by: Chad Sellers Acked-by: Stephen Smalley Signed-off-by: James Morris --- security/selinux/ss/hashtab.c | 6 +++--- security/selinux/ss/hashtab.h | 10 +++++----- security/selinux/ss/symtab.c | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index 24e5ec9..77b530c 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -8,8 +8,8 @@ #include #include "hashtab.h" -struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key), - int (*keycmp)(struct hashtab *h, void *key1, void *key2), +struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key), + int (*keycmp)(struct hashtab *h, const void *key1, const void *key2), u32 size) { struct hashtab *p; @@ -71,7 +71,7 @@ int hashtab_insert(struct hashtab *h, void *key, void *datum) return 0; } -void *hashtab_search(struct hashtab *h, void *key) +void *hashtab_search(struct hashtab *h, const void *key) { u32 hvalue; struct hashtab_node *cur; diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h index 4cc8581..7e2ff3e 100644 --- a/security/selinux/ss/hashtab.h +++ b/security/selinux/ss/hashtab.h @@ -22,9 +22,9 @@ struct hashtab { struct hashtab_node **htable; /* hash table */ u32 size; /* number of slots in hash table */ u32 nel; /* number of elements in hash table */ - u32 (*hash_value)(struct hashtab *h, void *key); + u32 (*hash_value)(struct hashtab *h, const void *key); /* hash function */ - int (*keycmp)(struct hashtab *h, void *key1, void *key2); + int (*keycmp)(struct hashtab *h, const void *key1, const void *key2); /* key comparison function */ }; @@ -39,8 +39,8 @@ struct hashtab_info { * Returns NULL if insufficent space is available or * the new hash table otherwise. */ -struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key), - int (*keycmp)(struct hashtab *h, void *key1, void *key2), +struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key), + int (*keycmp)(struct hashtab *h, const void *key1, const void *key2), u32 size); /* @@ -59,7 +59,7 @@ int hashtab_insert(struct hashtab *h, void *k, void *d); * Returns NULL if no entry has the specified key or * the datum of the entry otherwise. */ -void *hashtab_search(struct hashtab *h, void *k); +void *hashtab_search(struct hashtab *h, const void *k); /* * Destroys the specified hash table. diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c index 24a10d3..837658a 100644 --- a/security/selinux/ss/symtab.c +++ b/security/selinux/ss/symtab.c @@ -9,9 +9,9 @@ #include #include "symtab.h" -static unsigned int symhash(struct hashtab *h, void *key) +static unsigned int symhash(struct hashtab *h, const void *key) { - char *p, *keyp; + const char *p, *keyp; unsigned int size; unsigned int val; @@ -23,9 +23,9 @@ static unsigned int symhash(struct hashtab *h, void *key) return val & (h->size - 1); } -static int symcmp(struct hashtab *h, void *key1, void *key2) +static int symcmp(struct hashtab *h, const void *key1, const void *key2) { - char *keyp1, *keyp2; + const char *keyp1, *keyp2; keyp1 = key1; keyp2 = key2; -- cgit v1.1 From b94c7e677b9d28bd3f9ba4a70df6bfa7942867ca Mon Sep 17 00:00:00 2001 From: Chad Sellers Date: Mon, 6 Nov 2006 12:38:18 -0500 Subject: SELinux: validate kernel object classes and permissions This is a new object class and permission validation scheme that validates against the defined kernel headers. This scheme allows extra classes and permissions that do not conflict with the kernel definitions to be added to the policy. This validation is now done for all policy loads, not just subsequent loads after the first policy load. The implementation walks the three structrures containing the defined object class and permission values and ensures their values are the same in the policy being loaded. This includes verifying the object classes themselves, the permissions they contain, and the permissions they inherit from commons. Classes or permissions that are present in the kernel but missing from the policy cause a warning (printed to KERN_INFO) to be printed, but do not stop the policy from loading, emulating current behavior. Any other inconsistencies cause the load to fail. Signed-off-by: Chad Sellers Acked-by: Stephen Smalley Signed-off-by: James Morris --- security/selinux/ss/services.c | 138 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 33ae102..4088204 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -17,9 +17,13 @@ * * Added support for NetLabel * + * Updated: Chad Sellers + * + * Added validation of kernel classes and permissions + * * Copyright (C) 2006 Hewlett-Packard Development Company, L.P. * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. - * Copyright (C) 2003 - 2004 Tresys Technology, LLC + * Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC * Copyright (C) 2003 Red Hat, Inc., James Morris * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -53,6 +57,11 @@ extern void selnl_notify_policyload(u32 seqno); unsigned int policydb_loaded_version; +/* + * This is declared in avc.c + */ +extern const struct selinux_class_perm selinux_class_perm; + static DEFINE_RWLOCK(policy_rwlock); #define POLICY_RDLOCK read_lock(&policy_rwlock) #define POLICY_WRLOCK write_lock_irq(&policy_rwlock) @@ -1018,6 +1027,115 @@ int security_change_sid(u32 ssid, return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid); } +/* + * Verify that each kernel class that is defined in the + * policy is correct + */ +static int validate_classes(struct policydb *p) +{ + int i, j; + struct class_datum *cladatum; + struct perm_datum *perdatum; + u32 nprim, tmp, common_pts_len, perm_val, pol_val; + u16 class_val; + const struct selinux_class_perm *kdefs = &selinux_class_perm; + const char *def_class, *def_perm, *pol_class; + struct symtab *perms; + + for (i = 1; i < kdefs->cts_len; i++) { + def_class = kdefs->class_to_string[i]; + if (i > p->p_classes.nprim) { + printk(KERN_INFO + "security: class %s not defined in policy\n", + def_class); + continue; + } + pol_class = p->p_class_val_to_name[i-1]; + if (strcmp(pol_class, def_class)) { + printk(KERN_ERR + "security: class %d is incorrect, found %s but should be %s\n", + i, pol_class, def_class); + return -EINVAL; + } + } + for (i = 0; i < kdefs->av_pts_len; i++) { + class_val = kdefs->av_perm_to_string[i].tclass; + perm_val = kdefs->av_perm_to_string[i].value; + def_perm = kdefs->av_perm_to_string[i].name; + if (class_val > p->p_classes.nprim) + continue; + pol_class = p->p_class_val_to_name[class_val-1]; + cladatum = hashtab_search(p->p_classes.table, pol_class); + BUG_ON(!cladatum); + perms = &cladatum->permissions; + nprim = 1 << (perms->nprim - 1); + if (perm_val > nprim) { + printk(KERN_INFO + "security: permission %s in class %s not defined in policy\n", + def_perm, pol_class); + continue; + } + perdatum = hashtab_search(perms->table, def_perm); + if (perdatum == NULL) { + printk(KERN_ERR + "security: permission %s in class %s not found in policy\n", + def_perm, pol_class); + return -EINVAL; + } + pol_val = 1 << (perdatum->value - 1); + if (pol_val != perm_val) { + printk(KERN_ERR + "security: permission %s in class %s has incorrect value\n", + def_perm, pol_class); + return -EINVAL; + } + } + for (i = 0; i < kdefs->av_inherit_len; i++) { + class_val = kdefs->av_inherit[i].tclass; + if (class_val > p->p_classes.nprim) + continue; + pol_class = p->p_class_val_to_name[class_val-1]; + cladatum = hashtab_search(p->p_classes.table, pol_class); + BUG_ON(!cladatum); + if (!cladatum->comdatum) { + printk(KERN_ERR + "security: class %s should have an inherits clause but does not\n", + pol_class); + return -EINVAL; + } + tmp = kdefs->av_inherit[i].common_base; + common_pts_len = 0; + while (!(tmp & 0x01)) { + common_pts_len++; + tmp >>= 1; + } + perms = &cladatum->comdatum->permissions; + for (j = 0; j < common_pts_len; j++) { + def_perm = kdefs->av_inherit[i].common_pts[j]; + if (j >= perms->nprim) { + printk(KERN_INFO + "security: permission %s in class %s not defined in policy\n", + def_perm, pol_class); + continue; + } + perdatum = hashtab_search(perms->table, def_perm); + if (perdatum == NULL) { + printk(KERN_ERR + "security: permission %s in class %s not found in policy\n", + def_perm, pol_class); + return -EINVAL; + } + if (perdatum->value != j + 1) { + printk(KERN_ERR + "security: permission %s in class %s has incorrect value\n", + def_perm, pol_class); + return -EINVAL; + } + } + } + return 0; +} + /* Clone the SID into the new SID table. */ static int clone_sid(u32 sid, struct context *context, @@ -1160,6 +1278,16 @@ int security_load_policy(void *data, size_t len) avtab_cache_destroy(); return -EINVAL; } + /* Verify that the kernel defined classes are correct. */ + if (validate_classes(&policydb)) { + printk(KERN_ERR + "security: the definition of a class is incorrect\n"); + LOAD_UNLOCK; + sidtab_destroy(&sidtab); + policydb_destroy(&policydb); + avtab_cache_destroy(); + return -EINVAL; + } policydb_loaded_version = policydb.policyvers; ss_initialized = 1; seqno = ++latest_granting; @@ -1182,6 +1310,14 @@ int security_load_policy(void *data, size_t len) sidtab_init(&newsidtab); + /* Verify that the kernel defined classes are correct. */ + if (validate_classes(&newpolicydb)) { + printk(KERN_ERR + "security: the definition of a class is incorrect\n"); + rc = -EINVAL; + goto err; + } + /* Clone the SID table. */ sidtab_shutdown(&sidtab); if (sidtab_map(&sidtab, clone_sid, &newsidtab)) { -- cgit v1.1 From c1a856c9640c9ff3d70bbd8214b6a0974609eef8 Mon Sep 17 00:00:00 2001 From: Venkat Yekkirala Date: Wed, 8 Nov 2006 17:03:44 -0600 Subject: SELinux: Various xfrm labeling fixes Since the upstreaming of the mlsxfrm modification a few months back, testing has resulted in the identification of the following issues/bugs that are resolved in this patch set. 1. Fix the security context used in the IKE negotiation to be the context of the socket as opposed to the context of the SPD rule. 2. Fix SO_PEERSEC for tcp sockets to return the security context of the peer as opposed to the source. 3. Fix the selection of an SA for an outgoing packet to be at the same context as the originating socket/flow. The following would be the result of applying this patchset: - SO_PEERSEC will now correctly return the peer's context. - IKE deamons will receive the context of the source socket/flow as opposed to the SPD rule's context so that the negotiated SA will be at the same context as the source socket/flow. - The SELinux policy will require one or more of the following for a socket to be able to communicate with/without SAs: 1. To enable a socket to communicate without using labeled-IPSec SAs: allow socket_t unlabeled_t:association { sendto recvfrom } 2. To enable a socket to communicate with labeled-IPSec SAs: allow socket_t self:association { sendto }; allow socket_t peer_sa_t:association { recvfrom }; This Patch: Pass correct security context to IKE for use in negotiation Fix the security context passed to IKE for use in negotiation to be the context of the socket as opposed to the context of the SPD rule so that the SA carries the label of the originating socket/flow. Signed-off-by: Venkat Yekkirala Signed-off-by: James Morris --- security/dummy.c | 4 ++-- security/selinux/include/xfrm.h | 4 ++-- security/selinux/xfrm.c | 35 +++++++++-------------------------- 3 files changed, 13 insertions(+), 30 deletions(-) (limited to 'security') diff --git a/security/dummy.c b/security/dummy.c index 43874c1..838d844 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -836,7 +836,7 @@ static inline void dummy_req_classify_flow(const struct request_sock *req, #ifdef CONFIG_SECURITY_NETWORK_XFRM static int dummy_xfrm_policy_alloc_security(struct xfrm_policy *xp, - struct xfrm_user_sec_ctx *sec_ctx, struct sock *sk) + struct xfrm_user_sec_ctx *sec_ctx) { return 0; } @@ -856,7 +856,7 @@ static int dummy_xfrm_policy_delete_security(struct xfrm_policy *xp) } static int dummy_xfrm_state_alloc_security(struct xfrm_state *x, - struct xfrm_user_sec_ctx *sec_ctx, struct xfrm_sec_ctx *pol, u32 secid) + struct xfrm_user_sec_ctx *sec_ctx, u32 secid) { return 0; } diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 526b280..8e329dd 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -8,12 +8,12 @@ #define _SELINUX_XFRM_H_ int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, - struct xfrm_user_sec_ctx *sec_ctx, struct sock *sk); + struct xfrm_user_sec_ctx *sec_ctx); int selinux_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new); void selinux_xfrm_policy_free(struct xfrm_policy *xp); int selinux_xfrm_policy_delete(struct xfrm_policy *xp); int selinux_xfrm_state_alloc(struct xfrm_state *x, - struct xfrm_user_sec_ctx *sec_ctx, struct xfrm_sec_ctx *pol, u32 secid); + struct xfrm_user_sec_ctx *sec_ctx, u32 secid); void selinux_xfrm_state_free(struct xfrm_state *x); int selinux_xfrm_state_delete(struct xfrm_state *x); int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir); diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index 675b995..4d5a043 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -226,16 +226,15 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) * CTX does not have a meaningful value on input */ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, - struct xfrm_user_sec_ctx *uctx, struct xfrm_sec_ctx *pol, u32 sid) + struct xfrm_user_sec_ctx *uctx, u32 sid) { int rc = 0; struct task_security_struct *tsec = current->security; struct xfrm_sec_ctx *ctx = NULL; char *ctx_str = NULL; u32 str_len; - u32 ctx_sid; - BUG_ON(uctx && pol); + BUG_ON(uctx && sid); if (!uctx) goto not_from_user; @@ -279,15 +278,7 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, return rc; not_from_user: - if (pol) { - rc = security_sid_mls_copy(pol->ctx_sid, sid, &ctx_sid); - if (rc) - goto out; - } - else - ctx_sid = sid; - - rc = security_sid_to_context(ctx_sid, &ctx_str, &str_len); + rc = security_sid_to_context(sid, &ctx_str, &str_len); if (rc) goto out; @@ -302,7 +293,7 @@ not_from_user: ctx->ctx_doi = XFRM_SC_DOI_LSM; ctx->ctx_alg = XFRM_SC_ALG_SELINUX; - ctx->ctx_sid = ctx_sid; + ctx->ctx_sid = sid; ctx->ctx_len = str_len; memcpy(ctx->ctx_str, ctx_str, @@ -323,22 +314,14 @@ out2: * xfrm_policy. */ int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, - struct xfrm_user_sec_ctx *uctx, struct sock *sk) + struct xfrm_user_sec_ctx *uctx) { int err; - u32 sid; BUG_ON(!xp); - BUG_ON(uctx && sk); - - if (sk) { - struct sk_security_struct *ssec = sk->sk_security; - sid = ssec->sid; - } - else - sid = SECSID_NULL; + BUG_ON(!uctx); - err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx, NULL, sid); + err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx, 0); return err; } @@ -399,13 +382,13 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp) * xfrm_state. */ int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx, - struct xfrm_sec_ctx *pol, u32 secid) + u32 secid) { int err; BUG_ON(!x); - err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, pol, secid); + err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, secid); return err; } -- cgit v1.1 From 6b877699c6f1efede4545bcecc367786a472eedb Mon Sep 17 00:00:00 2001 From: Venkat Yekkirala Date: Wed, 8 Nov 2006 17:04:09 -0600 Subject: SELinux: Return correct context for SO_PEERSEC Fix SO_PEERSEC for tcp sockets to return the security context of the peer (as represented by the SA from the peer) as opposed to the SA used by the local/source socket. Signed-off-by: Venkat Yekkirala Signed-off-by: James Morris --- security/dummy.c | 6 ++++++ security/selinux/hooks.c | 21 +++++++++++++++++---- security/selinux/include/xfrm.h | 12 ++++++------ security/selinux/xfrm.c | 40 +++------------------------------------- 4 files changed, 32 insertions(+), 47 deletions(-) (limited to 'security') diff --git a/security/dummy.c b/security/dummy.c index 838d844..0148d15 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -828,6 +828,11 @@ static inline void dummy_inet_csk_clone(struct sock *newsk, { } +static inline void dummy_inet_conn_established(struct sock *sk, + struct sk_buff *skb) +{ +} + static inline void dummy_req_classify_flow(const struct request_sock *req, struct flowi *fl) { @@ -1108,6 +1113,7 @@ void security_fixup_ops (struct security_operations *ops) set_to_dummy_if_null(ops, sock_graft); set_to_dummy_if_null(ops, inet_conn_request); set_to_dummy_if_null(ops, inet_csk_clone); + set_to_dummy_if_null(ops, inet_conn_established); set_to_dummy_if_null(ops, req_classify_flow); #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_NETWORK_XFRM diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 28ee187..5bbd599 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3535,8 +3535,10 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op } else if (isec->sclass == SECCLASS_TCP_SOCKET) { peer_sid = selinux_netlbl_socket_getpeersec_stream(sock); - if (peer_sid == SECSID_NULL) - peer_sid = selinux_socket_getpeer_stream(sock->sk); + if (peer_sid == SECSID_NULL) { + ssec = sock->sk->sk_security; + peer_sid = ssec->peer_sid; + } if (peer_sid == SECSID_NULL) { err = -ENOPROTOOPT; goto out; @@ -3647,11 +3649,11 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, return 0; } - err = selinux_xfrm_decode_session(skb, &peersid, 0); - BUG_ON(err); + selinux_skb_xfrm_sid(skb, &peersid); if (peersid == SECSID_NULL) { req->secid = sksec->sid; + req->peer_secid = 0; return 0; } @@ -3660,6 +3662,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, return err; req->secid = newsid; + req->peer_secid = peersid; return 0; } @@ -3669,6 +3672,7 @@ static void selinux_inet_csk_clone(struct sock *newsk, struct sk_security_struct *newsksec = newsk->sk_security; newsksec->sid = req->secid; + newsksec->peer_sid = req->peer_secid; /* NOTE: Ideally, we should also get the isec->sid for the new socket in sync, but we don't have the isec available yet. So we will wait until sock_graft to do it, by which @@ -3677,6 +3681,14 @@ static void selinux_inet_csk_clone(struct sock *newsk, selinux_netlbl_sk_security_init(newsksec, req->rsk_ops->family); } +static void selinux_inet_conn_established(struct sock *sk, + struct sk_buff *skb) +{ + struct sk_security_struct *sksec = sk->sk_security; + + selinux_skb_xfrm_sid(skb, &sksec->peer_sid); +} + static void selinux_req_classify_flow(const struct request_sock *req, struct flowi *fl) { @@ -4739,6 +4751,7 @@ static struct security_operations selinux_ops = { .sock_graft = selinux_sock_graft, .inet_conn_request = selinux_inet_conn_request, .inet_csk_clone = selinux_inet_csk_clone, + .inet_conn_established = selinux_inet_conn_established, .req_classify_flow = selinux_req_classify_flow, #ifdef CONFIG_SECURITY_NETWORK_XFRM diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 8e329dd..2750236 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -39,7 +39,6 @@ int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb, struct avc_audit_data *ad); int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, struct avc_audit_data *ad); -u32 selinux_socket_getpeer_stream(struct sock *sk); u32 selinux_socket_getpeer_dgram(struct sk_buff *skb); int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall); #else @@ -55,11 +54,6 @@ static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, return 0; } -static inline int selinux_socket_getpeer_stream(struct sock *sk) -{ - return SECSID_NULL; -} - static inline int selinux_socket_getpeer_dgram(struct sk_buff *skb) { return SECSID_NULL; @@ -71,4 +65,10 @@ static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int } #endif +static inline void selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid) +{ + int err = selinux_xfrm_decode_session(skb, sid, 0); + BUG_ON(err); +} + #endif /* _SELINUX_XFRM_H_ */ diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index 4d5a043..8fef742 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -184,7 +184,8 @@ int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm, } /* - * LSM hook implementation that determines the sid for the session. + * LSM hook implementation that checks and/or returns the xfrm sid for the + * incoming packet. */ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) @@ -403,43 +404,8 @@ void selinux_xfrm_state_free(struct xfrm_state *x) } /* - * SELinux internal function to retrieve the context of a connected - * (sk->sk_state == TCP_ESTABLISHED) TCP socket based on its security - * association used to connect to the remote socket. - * - * Retrieve via getsockopt SO_PEERSEC. - */ -u32 selinux_socket_getpeer_stream(struct sock *sk) -{ - struct dst_entry *dst, *dst_test; - u32 peer_sid = SECSID_NULL; - - if (sk->sk_state != TCP_ESTABLISHED) - goto out; - - dst = sk_dst_get(sk); - if (!dst) - goto out; - - for (dst_test = dst; dst_test != 0; - dst_test = dst_test->child) { - struct xfrm_state *x = dst_test->xfrm; - - if (x && selinux_authorizable_xfrm(x)) { - struct xfrm_sec_ctx *ctx = x->security; - peer_sid = ctx->ctx_sid; - break; - } - } - dst_release(dst); - -out: - return peer_sid; -} - -/* * SELinux internal function to retrieve the context of a UDP packet - * based on its security association used to connect to the remote socket. + * based on its security association. * * Retrieve via setsockopt IP_PASSSEC and recvmsg with control message * type SCM_SECURITY. -- cgit v1.1 From 67f83cbf081a70426ff667e8d14f94e13ed3bdca Mon Sep 17 00:00:00 2001 From: Venkat Yekkirala Date: Wed, 8 Nov 2006 17:04:26 -0600 Subject: SELinux: Fix SA selection semantics Fix the selection of an SA for an outgoing packet to be at the same context as the originating socket/flow. This eliminates the SELinux policy's ability to use/sendto SAs with contexts other than the socket's. With this patch applied, the SELinux policy will require one or more of the following for a socket to be able to communicate with/without SAs: 1. To enable a socket to communicate without using labeled-IPSec SAs: allow socket_t unlabeled_t:association { sendto recvfrom } 2. To enable a socket to communicate with labeled-IPSec SAs: allow socket_t self:association { sendto }; allow socket_t peer_sa_t:association { recvfrom }; Signed-off-by: Venkat Yekkirala Signed-off-by: James Morris --- security/dummy.c | 7 --- security/selinux/hooks.c | 26 +++++++---- security/selinux/include/xfrm.h | 7 +-- security/selinux/xfrm.c | 101 +++++++++++++++++++--------------------- 4 files changed, 68 insertions(+), 73 deletions(-) (limited to 'security') diff --git a/security/dummy.c b/security/dummy.c index 0148d15..558795b 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -886,12 +886,6 @@ static int dummy_xfrm_state_pol_flow_match(struct xfrm_state *x, return 1; } -static int dummy_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm, - struct xfrm_policy *xp) -{ - return 1; -} - static int dummy_xfrm_decode_session(struct sk_buff *skb, u32 *fl, int ckall) { return 0; @@ -1126,7 +1120,6 @@ void security_fixup_ops (struct security_operations *ops) set_to_dummy_if_null(ops, xfrm_state_delete_security); set_to_dummy_if_null(ops, xfrm_policy_lookup); set_to_dummy_if_null(ops, xfrm_state_pol_flow_match); - set_to_dummy_if_null(ops, xfrm_flow_state_match); set_to_dummy_if_null(ops, xfrm_decode_session); #endif /* CONFIG_SECURITY_NETWORK_XFRM */ #ifdef CONFIG_KEYS diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5bbd599..956137b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2889,7 +2889,8 @@ static void selinux_task_to_inode(struct task_struct *p, } /* Returns error only if unable to parse addresses */ -static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad) +static int selinux_parse_skb_ipv4(struct sk_buff *skb, + struct avc_audit_data *ad, u8 *proto) { int offset, ihlen, ret = -EINVAL; struct iphdr _iph, *ih; @@ -2907,6 +2908,9 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad ad->u.net.v4info.daddr = ih->daddr; ret = 0; + if (proto) + *proto = ih->protocol; + switch (ih->protocol) { case IPPROTO_TCP: { struct tcphdr _tcph, *th; @@ -2950,7 +2954,8 @@ out: #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) /* Returns error only if unable to parse addresses */ -static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad) +static int selinux_parse_skb_ipv6(struct sk_buff *skb, + struct avc_audit_data *ad, u8 *proto) { u8 nexthdr; int ret = -EINVAL, offset; @@ -2971,6 +2976,9 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad if (offset < 0) goto out; + if (proto) + *proto = nexthdr; + switch (nexthdr) { case IPPROTO_TCP: { struct tcphdr _tcph, *th; @@ -3007,13 +3015,13 @@ out: #endif /* IPV6 */ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, - char **addrp, int *len, int src) + char **addrp, int *len, int src, u8 *proto) { int ret = 0; switch (ad->u.net.family) { case PF_INET: - ret = selinux_parse_skb_ipv4(skb, ad); + ret = selinux_parse_skb_ipv4(skb, ad, proto); if (ret || !addrp) break; *len = 4; @@ -3023,7 +3031,7 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) case PF_INET6: - ret = selinux_parse_skb_ipv6(skb, ad); + ret = selinux_parse_skb_ipv6(skb, ad, proto); if (ret || !addrp) break; *len = 16; @@ -3494,7 +3502,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ad.u.net.netif = skb->dev ? skb->dev->name : "[unknown]"; ad.u.net.family = family; - err = selinux_parse_skb(skb, &ad, &addrp, &len, 1); + err = selinux_parse_skb(skb, &ad, &addrp, &len, 1, NULL); if (err) goto out; @@ -3820,6 +3828,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, struct avc_audit_data ad; struct net_device *dev = (struct net_device *)out; struct sk_security_struct *sksec; + u8 proto; sk = skb->sk; if (!sk) @@ -3831,7 +3840,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, ad.u.net.netif = dev->name; ad.u.net.family = family; - err = selinux_parse_skb(skb, &ad, &addrp, &len, 0); + err = selinux_parse_skb(skb, &ad, &addrp, &len, 0, &proto); if (err) goto out; @@ -3845,7 +3854,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, if (err) goto out; - err = selinux_xfrm_postroute_last(sksec->sid, skb, &ad); + err = selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto); out: return err ? NF_DROP : NF_ACCEPT; } @@ -4764,7 +4773,6 @@ static struct security_operations selinux_ops = { .xfrm_state_delete_security = selinux_xfrm_state_delete, .xfrm_policy_lookup = selinux_xfrm_policy_lookup, .xfrm_state_pol_flow_match = selinux_xfrm_state_pol_flow_match, - .xfrm_flow_state_match = selinux_xfrm_flow_state_match, .xfrm_decode_session = selinux_xfrm_decode_session, #endif diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 2750236..ebd7246 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -19,9 +19,6 @@ int selinux_xfrm_state_delete(struct xfrm_state *x); int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir); int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp, struct flowi *fl); -int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm, - struct xfrm_policy *xp); - /* * Extract the security blob from the sock (it's actually on the socket) @@ -38,7 +35,7 @@ static inline struct inode_security_struct *get_sock_isec(struct sock *sk) int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb, struct avc_audit_data *ad); int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, - struct avc_audit_data *ad); + struct avc_audit_data *ad, u8 proto); u32 selinux_socket_getpeer_dgram(struct sk_buff *skb); int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall); #else @@ -49,7 +46,7 @@ static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb, } static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, - struct avc_audit_data *ad) + struct avc_audit_data *ad, u8 proto) { return 0; } diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index 8fef742..9b77714 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -115,71 +115,40 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy * struct flowi *fl) { u32 state_sid; - u32 pol_sid; - int err; + int rc; - if (xp->security) { - if (!x->security) - /* unlabeled SA and labeled policy can't match */ - return 0; - else - state_sid = x->security->ctx_sid; - pol_sid = xp->security->ctx_sid; - } else + if (!xp->security) if (x->security) /* unlabeled policy and labeled SA can't match */ return 0; else /* unlabeled policy and unlabeled SA match all flows */ return 1; - - err = avc_has_perm(state_sid, pol_sid, SECCLASS_ASSOCIATION, - ASSOCIATION__POLMATCH, - NULL); - - if (err) - return 0; - - err = avc_has_perm(fl->secid, state_sid, SECCLASS_ASSOCIATION, - ASSOCIATION__SENDTO, - NULL)? 0:1; - - return err; -} - -/* - * LSM hook implementation that authorizes that a particular outgoing flow - * can use a given security association. - */ - -int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm, - struct xfrm_policy *xp) -{ - int rc = 0; - u32 sel_sid = SECINITSID_UNLABELED; - struct xfrm_sec_ctx *ctx; - - if (!xp->security) - if (!xfrm->security) - return 1; - else - return 0; else - if (!xfrm->security) + if (!x->security) + /* unlabeled SA and labeled policy can't match */ return 0; + else + if (!selinux_authorizable_xfrm(x)) + /* Not a SELinux-labeled SA */ + return 0; - /* Context sid is either set to label or ANY_ASSOC */ - if ((ctx = xfrm->security)) { - if (!selinux_authorizable_ctx(ctx)) - return 0; + state_sid = x->security->ctx_sid; - sel_sid = ctx->ctx_sid; - } + if (fl->secid != state_sid) + return 0; - rc = avc_has_perm(fl->secid, sel_sid, SECCLASS_ASSOCIATION, + rc = avc_has_perm(fl->secid, state_sid, SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, NULL)? 0:1; + /* + * We don't need a separate SA Vs. policy polmatch check + * since the SA is now of the same label as the flow and + * a flow Vs. policy polmatch check had already happened + * in selinux_xfrm_policy_lookup() above. + */ + return rc; } @@ -481,6 +450,13 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb, } } + /* + * This check even when there's no association involved is + * intended, according to Trent Jaeger, to make sure a + * process can't engage in non-ipsec communication unless + * explicitly allowed by policy. + */ + rc = avc_has_perm(isec_sid, sel_sid, SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, ad); @@ -492,10 +468,10 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb, * If we have no security association, then we need to determine * whether the socket is allowed to send to an unlabelled destination. * If we do have a authorizable security association, then it has already been - * checked in xfrm_policy_lookup hook. + * checked in the selinux_xfrm_state_pol_flow_match hook above. */ int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, - struct avc_audit_data *ad) + struct avc_audit_data *ad, u8 proto) { struct dst_entry *dst; int rc = 0; @@ -514,6 +490,27 @@ int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, } } + switch (proto) { + case IPPROTO_AH: + case IPPROTO_ESP: + case IPPROTO_COMP: + /* + * We should have already seen this packet once before + * it underwent xfrm(s). No need to subject it to the + * unlabeled check. + */ + goto out; + default: + break; + } + + /* + * This check even when there's no association involved is + * intended, according to Trent Jaeger, to make sure a + * process can't engage in non-ipsec communication unless + * explicitly allowed by policy. + */ + rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, ad); out: -- cgit v1.1 From 2ee92d46c6cabedd50edf6f273fa8cf84f707618 Mon Sep 17 00:00:00 2001 From: James Morris Date: Mon, 13 Nov 2006 16:09:01 -0800 Subject: [SELinux]: Add support for DCCP This patch implements SELinux kernel support for DCCP (http://linux-net.osdl.org/index.php/DCCP), which is similar in operation to TCP in terms of connected state between peers. The SELinux support for DCCP is thus modeled on existing handling of TCP. A new DCCP socket class is introduced, to allow protocol differentation. The permissions for this class inherit all of the socket permissions, as well as the current TCP permissions (node_bind, name_bind etc). IPv4 and IPv6 are supported, although labeled networking is not, at this stage. Patches for SELinux userspace are at: http://people.redhat.com/jmorris/selinux/dccp/user/ I've performed some basic testing, and it seems to be working as expected. Adding policy support is similar to TCP, the only real difference being that it's a different protocol. Acked-by: Stephen Smalley Signed-off-by: James Morris Signed-off-by: David S. Miller --- security/selinux/hooks.c | 66 ++++++++++++++++++++++++---- security/selinux/include/av_inherit.h | 1 + security/selinux/include/av_perm_to_string.h | 8 ++++ security/selinux/include/av_permissions.h | 32 ++++++++++++++ security/selinux/include/class_to_string.h | 2 + security/selinux/include/flask.h | 2 + 6 files changed, 103 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 956137b..0cf9874 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include /* for Unix socket types */ #include /* for Unix socket types */ @@ -751,6 +752,8 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc return SECCLASS_UDP_SOCKET; else return SECCLASS_RAWIP_SOCKET; + case SOCK_DCCP: + return SECCLASS_DCCP_SOCKET; default: return SECCLASS_RAWIP_SOCKET; } @@ -2944,6 +2947,22 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, break; } + case IPPROTO_DCCP: { + struct dccp_hdr _dccph, *dh; + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + offset += ihlen; + dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph); + if (dh == NULL) + break; + + ad->u.net.sport = dh->dccph_sport; + ad->u.net.dport = dh->dccph_dport; + break; + } + default: break; } @@ -3004,6 +3023,18 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, break; } + case IPPROTO_DCCP: { + struct dccp_hdr _dccph, *dh; + + dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph); + if (dh == NULL) + break; + + ad->u.net.sport = dh->dccph_sport; + ad->u.net.dport = dh->dccph_dport; + break; + } + /* includes fragments */ default: break; @@ -3188,7 +3219,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in case SECCLASS_UDP_SOCKET: node_perm = UDP_SOCKET__NODE_BIND; break; - + + case SECCLASS_DCCP_SOCKET: + node_perm = DCCP_SOCKET__NODE_BIND; + break; + default: node_perm = RAWIP_SOCKET__NODE_BIND; break; @@ -3226,16 +3261,17 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, return err; /* - * If a TCP socket, check name_connect permission for the port. + * If a TCP or DCCP socket, check name_connect permission for the port. */ isec = SOCK_INODE(sock)->i_security; - if (isec->sclass == SECCLASS_TCP_SOCKET) { + if (isec->sclass == SECCLASS_TCP_SOCKET || + isec->sclass == SECCLASS_DCCP_SOCKET) { struct sock *sk = sock->sk; struct avc_audit_data ad; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; unsigned short snum; - u32 sid; + u32 sid, perm; if (sk->sk_family == PF_INET) { addr4 = (struct sockaddr_in *)address; @@ -3254,11 +3290,13 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, if (err) goto out; + perm = (isec->sclass == SECCLASS_TCP_SOCKET) ? + TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; + AVC_AUDIT_DATA_INIT(&ad,NET); ad.u.net.dport = htons(snum); ad.u.net.family = sk->sk_family; - err = avc_has_perm(isec->sid, sid, isec->sclass, - TCP_SOCKET__NAME_CONNECT, &ad); + err = avc_has_perm(isec->sid, sid, isec->sclass, perm, &ad); if (err) goto out; } @@ -3446,7 +3484,13 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, node_perm = NODE__TCP_RECV; recv_perm = TCP_SOCKET__RECV_MSG; break; - + + case SECCLASS_DCCP_SOCKET: + netif_perm = NETIF__DCCP_RECV; + node_perm = NODE__DCCP_RECV; + recv_perm = DCCP_SOCKET__RECV_MSG; + break; + default: netif_perm = NETIF__RAWIP_RECV; node_perm = NODE__RAWIP_RECV; @@ -3777,7 +3821,13 @@ static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device * node_perm = NODE__TCP_SEND; send_perm = TCP_SOCKET__SEND_MSG; break; - + + case SECCLASS_DCCP_SOCKET: + netif_perm = NETIF__DCCP_SEND; + node_perm = NODE__DCCP_SEND; + send_perm = DCCP_SOCKET__SEND_MSG; + break; + default: netif_perm = NETIF__RAWIP_SEND; node_perm = NODE__RAWIP_SEND; diff --git a/security/selinux/include/av_inherit.h b/security/selinux/include/av_inherit.h index a68fdd5..8377a4b 100644 --- a/security/selinux/include/av_inherit.h +++ b/security/selinux/include/av_inherit.h @@ -30,3 +30,4 @@ S_(SECCLASS_NETLINK_DNRT_SOCKET, socket, 0x00400000UL) S_(SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET, socket, 0x00400000UL) S_(SECCLASS_APPLETALK_SOCKET, socket, 0x00400000UL) + S_(SECCLASS_DCCP_SOCKET, socket, 0x00400000UL) diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h index 09fc8a2..ad9fb2d 100644 --- a/security/selinux/include/av_perm_to_string.h +++ b/security/selinux/include/av_perm_to_string.h @@ -35,12 +35,16 @@ S_(SECCLASS_NODE, NODE__RAWIP_RECV, "rawip_recv") S_(SECCLASS_NODE, NODE__RAWIP_SEND, "rawip_send") S_(SECCLASS_NODE, NODE__ENFORCE_DEST, "enforce_dest") + S_(SECCLASS_NODE, NODE__DCCP_RECV, "dccp_recv") + S_(SECCLASS_NODE, NODE__DCCP_SEND, "dccp_send") S_(SECCLASS_NETIF, NETIF__TCP_RECV, "tcp_recv") S_(SECCLASS_NETIF, NETIF__TCP_SEND, "tcp_send") S_(SECCLASS_NETIF, NETIF__UDP_RECV, "udp_recv") S_(SECCLASS_NETIF, NETIF__UDP_SEND, "udp_send") S_(SECCLASS_NETIF, NETIF__RAWIP_RECV, "rawip_recv") S_(SECCLASS_NETIF, NETIF__RAWIP_SEND, "rawip_send") + S_(SECCLASS_NETIF, NETIF__DCCP_RECV, "dccp_recv") + S_(SECCLASS_NETIF, NETIF__DCCP_SEND, "dccp_send") S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__CONNECTTO, "connectto") S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__NEWCONN, "newconn") S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__ACCEPTFROM, "acceptfrom") @@ -252,3 +256,7 @@ S_(SECCLASS_KEY, KEY__LINK, "link") S_(SECCLASS_KEY, KEY__SETATTR, "setattr") S_(SECCLASS_KEY, KEY__CREATE, "create") + S_(SECCLASS_CONTEXT, CONTEXT__TRANSLATE, "translate") + S_(SECCLASS_CONTEXT, CONTEXT__CONTAINS, "contains") + S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NODE_BIND, "node_bind") + S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect") diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h index 81f4f52..2de4b5f 100644 --- a/security/selinux/include/av_permissions.h +++ b/security/selinux/include/av_permissions.h @@ -312,6 +312,8 @@ #define NODE__RAWIP_RECV 0x00000010UL #define NODE__RAWIP_SEND 0x00000020UL #define NODE__ENFORCE_DEST 0x00000040UL +#define NODE__DCCP_RECV 0x00000080UL +#define NODE__DCCP_SEND 0x00000100UL #define NETIF__TCP_RECV 0x00000001UL #define NETIF__TCP_SEND 0x00000002UL @@ -319,6 +321,8 @@ #define NETIF__UDP_SEND 0x00000008UL #define NETIF__RAWIP_RECV 0x00000010UL #define NETIF__RAWIP_SEND 0x00000020UL +#define NETIF__DCCP_RECV 0x00000040UL +#define NETIF__DCCP_SEND 0x00000080UL #define NETLINK_SOCKET__IOCTL 0x00000001UL #define NETLINK_SOCKET__READ 0x00000002UL @@ -970,3 +974,31 @@ #define KEY__LINK 0x00000010UL #define KEY__SETATTR 0x00000020UL #define KEY__CREATE 0x00000040UL + +#define CONTEXT__TRANSLATE 0x00000001UL +#define CONTEXT__CONTAINS 0x00000002UL + +#define DCCP_SOCKET__IOCTL 0x00000001UL +#define DCCP_SOCKET__READ 0x00000002UL +#define DCCP_SOCKET__WRITE 0x00000004UL +#define DCCP_SOCKET__CREATE 0x00000008UL +#define DCCP_SOCKET__GETATTR 0x00000010UL +#define DCCP_SOCKET__SETATTR 0x00000020UL +#define DCCP_SOCKET__LOCK 0x00000040UL +#define DCCP_SOCKET__RELABELFROM 0x00000080UL +#define DCCP_SOCKET__RELABELTO 0x00000100UL +#define DCCP_SOCKET__APPEND 0x00000200UL +#define DCCP_SOCKET__BIND 0x00000400UL +#define DCCP_SOCKET__CONNECT 0x00000800UL +#define DCCP_SOCKET__LISTEN 0x00001000UL +#define DCCP_SOCKET__ACCEPT 0x00002000UL +#define DCCP_SOCKET__GETOPT 0x00004000UL +#define DCCP_SOCKET__SETOPT 0x00008000UL +#define DCCP_SOCKET__SHUTDOWN 0x00010000UL +#define DCCP_SOCKET__RECVFROM 0x00020000UL +#define DCCP_SOCKET__SENDTO 0x00040000UL +#define DCCP_SOCKET__RECV_MSG 0x00080000UL +#define DCCP_SOCKET__SEND_MSG 0x00100000UL +#define DCCP_SOCKET__NAME_BIND 0x00200000UL +#define DCCP_SOCKET__NODE_BIND 0x00400000UL +#define DCCP_SOCKET__NAME_CONNECT 0x00800000UL diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h index 24303b6..9f3ebb1 100644 --- a/security/selinux/include/class_to_string.h +++ b/security/selinux/include/class_to_string.h @@ -61,3 +61,5 @@ S_("appletalk_socket") S_("packet") S_("key") + S_("context") + S_("dccp_socket") diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h index 95887ae..67cef37 100644 --- a/security/selinux/include/flask.h +++ b/security/selinux/include/flask.h @@ -63,6 +63,8 @@ #define SECCLASS_APPLETALK_SOCKET 56 #define SECCLASS_PACKET 57 #define SECCLASS_KEY 58 +#define SECCLASS_CONTEXT 59 +#define SECCLASS_DCCP_SOCKET 60 /* * Security identifier indices for initial entities -- cgit v1.1 From 6051e2f4fb68fc8e5343db58fa680ece376f405c Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 14 Nov 2006 19:54:19 -0800 Subject: [IPv6] prefix: Convert RTM_NEWPREFIX notifications to use the new netlink api RTM_GETPREFIX is completely unused and is thus removed. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- security/selinux/nlmsgtab.c | 1 - 1 file changed, 1 deletion(-) (limited to 'security') diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index b8f4d25..ccfe875 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -60,7 +60,6 @@ static struct nlmsg_perm nlmsg_route_perms[] = { RTM_DELACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, { RTM_GETACTION, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_NEWPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_GETMULTICAST, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_GETANYCAST, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_GETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_READ }, -- cgit v1.1 From 701a90bad99b8081a824cca52c178c8fc8f46bb2 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 17 Nov 2006 17:38:46 -0500 Subject: NetLabel: make netlbl_lsm_secattr struct easier/quicker to understand The existing netlbl_lsm_secattr struct required the LSM to check all of the fields to determine if any security attributes were present resulting in a lot of work in the common case of no attributes. This patch adds a 'flags' field which is used to indicate which attributes are present in the structure; this should allow the LSM to do a quick comparison to determine if the structure holds any security attributes. Example: if (netlbl_lsm_secattr->flags) /* security attributes present */ else /* NO security attributes present */ Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/ss/services.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 4088204..1f5bbb2 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2254,8 +2254,6 @@ static void selinux_netlbl_cache_add(struct sk_buff *skb, struct context *ctx) cache = kzalloc(sizeof(*cache), GFP_ATOMIC); if (cache == NULL) goto netlbl_cache_add_return; - secattr.cache->free = selinux_netlbl_cache_free; - secattr.cache->data = (void *)cache; cache->type = NETLBL_CACHE_T_MLS; if (ebitmap_cpy(&cache->data.mls_label.level[0].cat, @@ -2268,6 +2266,10 @@ static void selinux_netlbl_cache_add(struct sk_buff *skb, struct context *ctx) cache->data.mls_label.level[0].sens = ctx->range.level[0].sens; cache->data.mls_label.level[1].sens = ctx->range.level[0].sens; + secattr.cache->free = selinux_netlbl_cache_free; + secattr.cache->data = (void *)cache; + secattr.flags = NETLBL_SECATTR_CACHE; + netlbl_cache_add(skb, &secattr); netlbl_cache_add_return: @@ -2313,7 +2315,7 @@ static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb, POLICY_RDLOCK; - if (secattr->cache) { + if (secattr->flags & NETLBL_SECATTR_CACHE) { cache = NETLBL_CACHE(secattr->cache->data); switch (cache->type) { case NETLBL_CACHE_T_SID: @@ -2346,7 +2348,7 @@ static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb, default: goto netlbl_secattr_to_sid_return; } - } else if (secattr->mls_lvl_vld) { + } else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) { ctx = sidtab_search(&sidtab, base_sid); if (ctx == NULL) goto netlbl_secattr_to_sid_return; @@ -2355,7 +2357,7 @@ static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb, ctx_new.role = ctx->role; ctx_new.type = ctx->type; mls_import_lvl(&ctx_new, secattr->mls_lvl, secattr->mls_lvl); - if (secattr->mls_cat) { + if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { if (mls_import_cat(&ctx_new, secattr->mls_cat, secattr->mls_cat_len, @@ -2414,11 +2416,13 @@ static int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, netlbl_secattr_init(&secattr); rc = netlbl_skbuff_getattr(skb, &secattr); - if (rc == 0) + if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) rc = selinux_netlbl_secattr_to_sid(skb, &secattr, base_sid, sid); + else + *sid = SECSID_NULL; netlbl_secattr_destroy(&secattr); return rc; @@ -2455,7 +2459,6 @@ static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) secattr.domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1], GFP_ATOMIC); mls_export_lvl(ctx, &secattr.mls_lvl, NULL); - secattr.mls_lvl_vld = 1; rc = mls_export_cat(ctx, &secattr.mls_cat, &secattr.mls_cat_len, @@ -2464,6 +2467,10 @@ static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) if (rc != 0) goto netlbl_socket_setsid_return; + secattr.flags |= NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL; + if (secattr.mls_cat) + secattr.flags |= NETLBL_SECATTR_MLS_CAT; + rc = netlbl_socket_setattr(sock, &secattr); if (rc == 0) sksec->nlbl_state = NLBL_LABELED; @@ -2564,6 +2571,7 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) netlbl_secattr_init(&secattr); if (netlbl_sock_getattr(sk, &secattr) == 0 && + secattr.flags != NETLBL_SECATTR_NONE && selinux_netlbl_secattr_to_sid(NULL, &secattr, SECINITSID_UNLABELED, @@ -2756,7 +2764,7 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, sksec->nlbl_state == NLBL_LABELED) { netlbl_secattr_init(&secattr); rc = netlbl_socket_getattr(sock, &secattr); - if (rc == 0 && (secattr.cache || secattr.mls_lvl_vld)) + if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) rc = -EACCES; netlbl_secattr_destroy(&secattr); } -- cgit v1.1 From 9f2ad66509b182b399a5b03de487f45bde623524 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 17 Nov 2006 17:38:53 -0500 Subject: NetLabel: SELinux cleanups This patch does a lot of cleanup in the SELinux NetLabel support code. A summary of the changes include: * Use RCU locking for the NetLabel state variable in the skk_security_struct instead of using the inode_security_struct mutex. * Remove unnecessary parameters in selinux_netlbl_socket_post_create(). * Rename selinux_netlbl_sk_clone_security() to selinux_netlbl_sk_security_clone() to better fit the other NetLabel sk_security functions. * Improvements to selinux_netlbl_inode_permission() to help reduce the cost of the common case. Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/hooks.c | 10 +-- security/selinux/include/objsec.h | 2 + security/selinux/include/selinux_netlabel.h | 21 +++--- security/selinux/ss/services.c | 107 +++++++++++++++++----------- 4 files changed, 86 insertions(+), 54 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 0cf9874..975c0df 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3140,9 +3140,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, if (sock->sk) { sksec = sock->sk->sk_security; sksec->sid = isec->sid; - err = selinux_netlbl_socket_post_create(sock, - family, - isec->sid); + err = selinux_netlbl_socket_post_create(sock); } return err; @@ -3661,7 +3659,7 @@ static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk) newssec->sid = ssec->sid; newssec->peer_sid = ssec->peer_sid; - selinux_netlbl_sk_clone_security(ssec, newssec); + selinux_netlbl_sk_security_clone(ssec, newssec); } static void selinux_sk_getsecid(struct sock *sk, u32 *secid) @@ -3730,7 +3728,9 @@ static void selinux_inet_csk_clone(struct sock *newsk, So we will wait until sock_graft to do it, by which time it will have been created and available. */ - selinux_netlbl_sk_security_init(newsksec, req->rsk_ops->family); + /* We don't need to take any sort of lock here as we are the only + * thread with access to newsksec */ + selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family); } static void selinux_inet_conn_established(struct sock *sk, diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index ef2267f..91b88f0 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "flask.h" #include "avc.h" @@ -108,6 +109,7 @@ struct sk_security_struct { NLBL_REQUIRE, NLBL_LABELED, } nlbl_state; + spinlock_t nlbl_lock; /* protects nlbl_state */ #endif }; diff --git a/security/selinux/include/selinux_netlabel.h b/security/selinux/include/selinux_netlabel.h index 9de10cc2..57943f4 100644 --- a/security/selinux/include/selinux_netlabel.h +++ b/security/selinux/include/selinux_netlabel.h @@ -38,9 +38,7 @@ #ifdef CONFIG_NETLABEL void selinux_netlbl_cache_invalidate(void); -int selinux_netlbl_socket_post_create(struct socket *sock, - int sock_family, - u32 sid); +int selinux_netlbl_socket_post_create(struct socket *sock); void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock); u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, u32 sock_sid); int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, @@ -48,9 +46,11 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, struct avc_audit_data *ad); u32 selinux_netlbl_socket_getpeersec_stream(struct socket *sock); u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb); +void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec, + int family); void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, int family); -void selinux_netlbl_sk_clone_security(struct sk_security_struct *ssec, +void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, struct sk_security_struct *newssec); int selinux_netlbl_inode_permission(struct inode *inode, int mask); int selinux_netlbl_socket_setsockopt(struct socket *sock, @@ -62,9 +62,7 @@ static inline void selinux_netlbl_cache_invalidate(void) return; } -static inline int selinux_netlbl_socket_post_create(struct socket *sock, - int sock_family, - u32 sid) +static inline int selinux_netlbl_socket_post_create(struct socket *sock) { return 0; } @@ -98,6 +96,13 @@ static inline u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb) return SECSID_NULL; } +static inline void selinux_netlbl_sk_security_reset( + struct sk_security_struct *ssec, + int family) +{ + return; +} + static inline void selinux_netlbl_sk_security_init( struct sk_security_struct *ssec, int family) @@ -105,7 +110,7 @@ static inline void selinux_netlbl_sk_security_init( return; } -static inline void selinux_netlbl_sk_clone_security( +static inline void selinux_netlbl_sk_security_clone( struct sk_security_struct *ssec, struct sk_security_struct *newssec) { diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 1f5bbb2..b66b454 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -2435,7 +2436,9 @@ static int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, * * Description: * Attempt to label a socket using the NetLabel mechanism using the given - * SID. Returns zero values on success, negative values on failure. + * SID. Returns zero values on success, negative values on failure. The + * caller is responsibile for calling rcu_read_lock() before calling this + * this function and rcu_read_unlock() after this function returns. * */ static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) @@ -2472,8 +2475,11 @@ static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) secattr.flags |= NETLBL_SECATTR_MLS_CAT; rc = netlbl_socket_setattr(sock, &secattr); - if (rc == 0) + if (rc == 0) { + spin_lock(&sksec->nlbl_lock); sksec->nlbl_state = NLBL_LABELED; + spin_unlock(&sksec->nlbl_lock); + } netlbl_socket_setsid_return: POLICY_RDUNLOCK; @@ -2482,6 +2488,25 @@ netlbl_socket_setsid_return: } /** + * selinux_netlbl_sk_security_reset - Reset the NetLabel fields + * @ssec: the sk_security_struct + * @family: the socket family + * + * Description: + * Called when the NetLabel state of a sk_security_struct needs to be reset. + * The caller is responsibile for all the NetLabel sk_security_struct locking. + * + */ +void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec, + int family) +{ + if (family == PF_INET) + ssec->nlbl_state = NLBL_REQUIRE; + else + ssec->nlbl_state = NLBL_UNSET; +} + +/** * selinux_netlbl_sk_security_init - Setup the NetLabel fields * @ssec: the sk_security_struct * @family: the socket family @@ -2494,14 +2519,13 @@ netlbl_socket_setsid_return: void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, int family) { - if (family == PF_INET) - ssec->nlbl_state = NLBL_REQUIRE; - else - ssec->nlbl_state = NLBL_UNSET; + /* No locking needed, we are the only one who has access to ssec */ + selinux_netlbl_sk_security_reset(ssec, family); + spin_lock_init(&ssec->nlbl_lock); } /** - * selinux_netlbl_sk_clone_security - Copy the NetLabel fields + * selinux_netlbl_sk_security_clone - Copy the NetLabel fields * @ssec: the original sk_security_struct * @newssec: the cloned sk_security_struct * @@ -2510,41 +2534,41 @@ void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, * @newssec. * */ -void selinux_netlbl_sk_clone_security(struct sk_security_struct *ssec, +void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, struct sk_security_struct *newssec) { + /* We don't need to take newssec->nlbl_lock because we are the only + * thread with access to newssec, but we do need to take the RCU read + * lock as other threads could have access to ssec */ + rcu_read_lock(); + selinux_netlbl_sk_security_reset(newssec, ssec->sk->sk_family); newssec->sclass = ssec->sclass; - if (ssec->nlbl_state != NLBL_UNSET) - newssec->nlbl_state = NLBL_REQUIRE; - else - newssec->nlbl_state = NLBL_UNSET; + rcu_read_unlock(); } /** * selinux_netlbl_socket_post_create - Label a socket using NetLabel * @sock: the socket to label - * @sock_family: the socket family - * @sid: the SID to use * * Description: * Attempt to label a socket using the NetLabel mechanism using the given * SID. Returns zero values on success, negative values on failure. * */ -int selinux_netlbl_socket_post_create(struct socket *sock, - int sock_family, - u32 sid) +int selinux_netlbl_socket_post_create(struct socket *sock) { + int rc = 0; struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; struct sk_security_struct *sksec = sock->sk->sk_security; sksec->sclass = isec->sclass; - if (sock_family != PF_INET) - return 0; + rcu_read_lock(); + if (sksec->nlbl_state == NLBL_REQUIRE) + rc = selinux_netlbl_socket_setsid(sock, sksec->sid); + rcu_read_unlock(); - sksec->nlbl_state = NLBL_REQUIRE; - return selinux_netlbl_socket_setsid(sock, sid); + return rc; } /** @@ -2566,8 +2590,12 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) sksec->sclass = isec->sclass; - if (sk->sk_family != PF_INET) + rcu_read_lock(); + + if (sksec->nlbl_state != NLBL_REQUIRE) { + rcu_read_unlock(); return; + } netlbl_secattr_init(&secattr); if (netlbl_sock_getattr(sk, &secattr) == 0 && @@ -2579,12 +2607,12 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) sksec->peer_sid = nlbl_peer_sid; netlbl_secattr_destroy(&secattr); - sksec->nlbl_state = NLBL_REQUIRE; - /* Try to set the NetLabel on the socket to save time later, if we fail * here we will pick up the pieces in later calls to * selinux_netlbl_inode_permission(). */ selinux_netlbl_socket_setsid(sock, sksec->sid); + + rcu_read_unlock(); } /** @@ -2625,25 +2653,24 @@ u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, u32 sock_sid) int selinux_netlbl_inode_permission(struct inode *inode, int mask) { int rc; - struct inode_security_struct *isec; struct sk_security_struct *sksec; struct socket *sock; - if (!S_ISSOCK(inode->i_mode)) + if (!S_ISSOCK(inode->i_mode) || + ((mask & (MAY_WRITE | MAY_APPEND)) == 0)) return 0; - sock = SOCKET_I(inode); - isec = inode->i_security; sksec = sock->sk->sk_security; - mutex_lock(&isec->lock); - if (unlikely(sksec->nlbl_state == NLBL_REQUIRE && - (mask & (MAY_WRITE | MAY_APPEND)))) { - lock_sock(sock->sk); - rc = selinux_netlbl_socket_setsid(sock, sksec->sid); - release_sock(sock->sk); - } else - rc = 0; - mutex_unlock(&isec->lock); + + rcu_read_lock(); + if (sksec->nlbl_state != NLBL_REQUIRE) { + rcu_read_unlock(); + return 0; + } + lock_sock(sock->sk); + rc = selinux_netlbl_socket_setsid(sock, sksec->sid); + release_sock(sock->sk); + rcu_read_unlock(); return rc; } @@ -2754,12 +2781,10 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, int optname) { int rc = 0; - struct inode *inode = SOCK_INODE(sock); struct sk_security_struct *sksec = sock->sk->sk_security; - struct inode_security_struct *isec = inode->i_security; struct netlbl_lsm_secattr secattr; - mutex_lock(&isec->lock); + rcu_read_lock(); if (level == IPPROTO_IP && optname == IP_OPTIONS && sksec->nlbl_state == NLBL_LABELED) { netlbl_secattr_init(&secattr); @@ -2768,7 +2793,7 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, rc = -EACCES; netlbl_secattr_destroy(&secattr); } - mutex_unlock(&isec->lock); + rcu_read_unlock(); return rc; } -- cgit v1.1 From 3de4bab5b9f8848a0c16a4b1ffe0452f0d670237 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 17 Nov 2006 17:38:54 -0500 Subject: SELinux: peer secid consolidation for external network labeling Now that labeled IPsec makes use of the peer_sid field in the sk_security_struct we can remove a lot of the special cases between labeled IPsec and NetLabel. In addition, create a new function, security_skb_extlbl_sid(), which we can use in several places to get the security context of the packet's external label which allows us to further simplify the code in a few places. Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/hooks.c | 43 ++++---------- security/selinux/include/security.h | 3 + security/selinux/include/selinux_netlabel.h | 28 +++------ security/selinux/include/xfrm.h | 5 -- security/selinux/ss/services.c | 90 +++++++++-------------------- security/selinux/xfrm.c | 33 ----------- 6 files changed, 52 insertions(+), 150 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 975c0df..a29d78d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3574,27 +3574,16 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op u32 scontext_len; struct sk_security_struct *ssec; struct inode_security_struct *isec; - u32 peer_sid = 0; + u32 peer_sid = SECSID_NULL; isec = SOCK_INODE(sock)->i_security; - /* if UNIX_STREAM check peer_sid, if TCP check dst for labelled sa */ - if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET) { + if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET || + isec->sclass == SECCLASS_TCP_SOCKET) { ssec = sock->sk->sk_security; peer_sid = ssec->peer_sid; } - else if (isec->sclass == SECCLASS_TCP_SOCKET) { - peer_sid = selinux_netlbl_socket_getpeersec_stream(sock); - if (peer_sid == SECSID_NULL) { - ssec = sock->sk->sk_security; - peer_sid = ssec->peer_sid; - } - if (peer_sid == SECSID_NULL) { - err = -ENOPROTOOPT; - goto out; - } - } - else { + if (peer_sid == SECSID_NULL) { err = -ENOPROTOOPT; goto out; } @@ -3626,13 +3615,12 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff * u32 peer_secid = SECSID_NULL; int err = 0; - if (sock && (sock->sk->sk_family == PF_UNIX)) + if (sock && sock->sk->sk_family == PF_UNIX) selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid); - else if (skb) { - peer_secid = selinux_netlbl_socket_getpeersec_dgram(skb); - if (peer_secid == SECSID_NULL) - peer_secid = selinux_socket_getpeer_dgram(skb); - } + else if (skb) + security_skb_extlbl_sid(skb, + SECINITSID_UNLABELED, + &peer_secid); if (peer_secid == SECSID_NULL) err = -EINVAL; @@ -3693,17 +3681,10 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, u32 newsid; u32 peersid; - newsid = selinux_netlbl_inet_conn_request(skb, sksec->sid); - if (newsid != SECSID_NULL) { - req->secid = newsid; - return 0; - } - - selinux_skb_xfrm_sid(skb, &peersid); - + security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peersid); if (peersid == SECSID_NULL) { req->secid = sksec->sid; - req->peer_secid = 0; + req->peer_secid = SECSID_NULL; return 0; } @@ -3738,7 +3719,7 @@ static void selinux_inet_conn_established(struct sock *sk, { struct sk_security_struct *sksec = sk->sk_security; - selinux_skb_xfrm_sid(skb, &sksec->peer_sid); + security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &sksec->peer_sid); } static void selinux_req_classify_flow(const struct request_sock *req, diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 1ef7917..015f697 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -8,6 +8,7 @@ #ifndef _SELINUX_SECURITY_H_ #define _SELINUX_SECURITY_H_ +#include #include "flask.h" #define SECSID_NULL 0x00000000 /* unspecified SID */ @@ -80,6 +81,8 @@ int security_netif_sid(char *name, u32 *if_sid, int security_node_sid(u16 domain, void *addr, u32 addrlen, u32 *out_sid); +void security_skb_extlbl_sid(struct sk_buff *skb, u32 base_sid, u32 *sid); + int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, u16 tclass); diff --git a/security/selinux/include/selinux_netlabel.h b/security/selinux/include/selinux_netlabel.h index 57943f4..2a732c9 100644 --- a/security/selinux/include/selinux_netlabel.h +++ b/security/selinux/include/selinux_netlabel.h @@ -38,14 +38,12 @@ #ifdef CONFIG_NETLABEL void selinux_netlbl_cache_invalidate(void); +int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid); int selinux_netlbl_socket_post_create(struct socket *sock); void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock); -u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, u32 sock_sid); int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, struct sk_buff *skb, struct avc_audit_data *ad); -u32 selinux_netlbl_socket_getpeersec_stream(struct socket *sock); -u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb); void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec, int family); void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, @@ -62,6 +60,14 @@ static inline void selinux_netlbl_cache_invalidate(void) return; } +static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, + u32 base_sid, + u32 *sid) +{ + *sid = SECSID_NULL; + return 0; +} + static inline int selinux_netlbl_socket_post_create(struct socket *sock) { return 0; @@ -73,12 +79,6 @@ static inline void selinux_netlbl_sock_graft(struct sock *sk, return; } -static inline u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, - u32 sock_sid) -{ - return SECSID_NULL; -} - static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, struct sk_buff *skb, struct avc_audit_data *ad) @@ -86,16 +86,6 @@ static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, return 0; } -static inline u32 selinux_netlbl_socket_getpeersec_stream(struct socket *sock) -{ - return SECSID_NULL; -} - -static inline u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb) -{ - return SECSID_NULL; -} - static inline void selinux_netlbl_sk_security_reset( struct sk_security_struct *ssec, int family) diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index ebd7246..161eb57 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -36,7 +36,6 @@ int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb, struct avc_audit_data *ad); int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, struct avc_audit_data *ad, u8 proto); -u32 selinux_socket_getpeer_dgram(struct sk_buff *skb); int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall); #else static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb, @@ -51,10 +50,6 @@ static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, return 0; } -static inline int selinux_socket_getpeer_dgram(struct sk_buff *skb) -{ - return SECSID_NULL; -} static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) { *sid = SECSID_NULL; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index b66b454..b43dd80 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -54,6 +54,7 @@ #include "mls.h" #include "objsec.h" #include "selinux_netlabel.h" +#include "xfrm.h" extern void selnl_notify_policyload(u32 seqno); unsigned int policydb_loaded_version; @@ -2191,6 +2192,32 @@ void selinux_audit_set_callback(int (*callback)(void)) aurule_callback = callback; } +/** + * security_skb_extlbl_sid - Determine the external label of a packet + * @skb: the packet + * @base_sid: the SELinux SID to use as a context for MLS only external labels + * @sid: the packet's SID + * + * Description: + * Check the various different forms of external packet labeling and determine + * the external SID for the packet. + * + */ +void security_skb_extlbl_sid(struct sk_buff *skb, u32 base_sid, u32 *sid) +{ + u32 xfrm_sid; + u32 nlbl_sid; + + selinux_skb_xfrm_sid(skb, &xfrm_sid); + if (selinux_netlbl_skbuff_getsid(skb, + (xfrm_sid == SECSID_NULL ? + base_sid : xfrm_sid), + &nlbl_sid) != 0) + nlbl_sid = SECSID_NULL; + + *sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid); +} + #ifdef CONFIG_NETLABEL /* * This is the structure we store inside the NetLabel cache block. @@ -2408,9 +2435,7 @@ netlbl_secattr_to_sid_return_cleanup: * assign to the packet. Returns zero on success, negative values on failure. * */ -static int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, - u32 base_sid, - u32 *sid) +int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid) { int rc; struct netlbl_lsm_secattr secattr; @@ -2616,29 +2641,6 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) } /** - * selinux_netlbl_inet_conn_request - Handle a new connection request - * @skb: the packet - * @sock_sid: the SID of the parent socket - * - * Description: - * If present, use the security attributes of the packet in @skb and the - * parent sock's SID to arrive at a SID for the new child sock. Returns the - * SID of the connection or SECSID_NULL on failure. - * - */ -u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, u32 sock_sid) -{ - int rc; - u32 peer_sid; - - rc = selinux_netlbl_skbuff_getsid(skb, sock_sid, &peer_sid); - if (rc != 0) - return SECSID_NULL; - - return peer_sid; -} - -/** * selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled * @inode: the file descriptor's inode * @mask: the permission mask @@ -2728,42 +2730,6 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, } /** - * selinux_netlbl_socket_getpeersec_stream - Return the connected peer's SID - * @sock: the socket - * - * Description: - * Examine @sock to find the connected peer's SID. Returns the SID on success - * or SECSID_NULL on error. - * - */ -u32 selinux_netlbl_socket_getpeersec_stream(struct socket *sock) -{ - struct sk_security_struct *sksec = sock->sk->sk_security; - return sksec->peer_sid; -} - -/** - * selinux_netlbl_socket_getpeersec_dgram - Return the SID of a NetLabel packet - * @skb: the packet - * - * Description: - * Examine @skb to find the SID assigned to it by NetLabel. Returns the SID on - * success, SECSID_NULL on error. - * - */ -u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb) -{ - int peer_sid; - - if (selinux_netlbl_skbuff_getsid(skb, - SECINITSID_UNLABELED, - &peer_sid) != 0) - return SECSID_NULL; - - return peer_sid; -} - -/** * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel * @sock: the socket * @level: the socket level or protocol diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index 9b77714..bd8d1ef 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -372,39 +372,6 @@ void selinux_xfrm_state_free(struct xfrm_state *x) kfree(ctx); } -/* - * SELinux internal function to retrieve the context of a UDP packet - * based on its security association. - * - * Retrieve via setsockopt IP_PASSSEC and recvmsg with control message - * type SCM_SECURITY. - */ -u32 selinux_socket_getpeer_dgram(struct sk_buff *skb) -{ - struct sec_path *sp; - - if (skb == NULL) - return SECSID_NULL; - - if (skb->sk->sk_protocol != IPPROTO_UDP) - return SECSID_NULL; - - sp = skb->sp; - if (sp) { - int i; - - for (i = sp->len-1; i >= 0; i--) { - struct xfrm_state *x = sp->xvec[i]; - if (selinux_authorizable_xfrm(x)) { - struct xfrm_sec_ctx *ctx = x->security; - return ctx->ctx_sid; - } - } - } - - return SECSID_NULL; -} - /* * LSM hook implementation that authorizes deletion of labeled SAs. */ -- cgit v1.1 From bb22f58087fdf8b617803c9b65bc86c6d26b5115 Mon Sep 17 00:00:00 2001 From: James Morris Date: Fri, 17 Nov 2006 23:01:03 -0500 Subject: Compile fix for "peer secid consolidation for external network labeling" Use a forward declaration instead of dragging in skbuff.h and related junk. Signed-off-by: James Morris --- security/selinux/include/security.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 015f697..210eec7 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -8,7 +8,6 @@ #ifndef _SELINUX_SECURITY_H_ #define _SELINUX_SECURITY_H_ -#include #include "flask.h" #define SECSID_NULL 0x00000000 /* unspecified SID */ @@ -35,6 +34,8 @@ #define POLICYDB_VERSION_MAX POLICYDB_VERSION_RANGETRANS #endif +struct sk_buff; + extern int selinux_enabled; extern int selinux_mls_enabled; -- cgit v1.1 From 02752760359db6b00a3ffb1acfc13ef8d9eb1e3f Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 29 Nov 2006 13:18:18 -0500 Subject: NetLabel: convert to an extensibile/sparse category bitmap The original NetLabel category bitmap was a straight char bitmap which worked fine for the initial release as it only supported 240 bits due to limitations in the CIPSO restricted bitmap tag (tag type 0x01). This patch converts that straight char bitmap into an extensibile/sparse bitmap in order to lay the foundation for other CIPSO tag types and protocols. This patch also has a nice side effect in that all of the security attributes passed by NetLabel into the LSM are now in a format which is in the host's native byte/bit ordering which makes the LSM specific code much simpler; look at the changes in security/selinux/ss/ebitmap.c as an example. Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/ss/ebitmap.c | 198 ++++++++++++++++++----------------------- security/selinux/ss/ebitmap.h | 26 ++++-- security/selinux/ss/mls.c | 156 +++++++++++--------------------- security/selinux/ss/mls.h | 46 +++++++--- security/selinux/ss/services.c | 23 ++--- 5 files changed, 199 insertions(+), 250 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index d539346..ce492a6 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -6,7 +6,7 @@ /* * Updated: Hewlett-Packard * - * Added ebitmap_export() and ebitmap_import() + * Added support to import/export the NetLabel category bitmap * * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 */ @@ -14,6 +14,7 @@ #include #include #include +#include #include "ebitmap.h" #include "policydb.h" @@ -67,141 +68,120 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src) return 0; } +#ifdef CONFIG_NETLABEL /** - * ebitmap_export - Export an ebitmap to a unsigned char bitmap string - * @src: the ebitmap to export - * @dst: the resulting bitmap string - * @dst_len: length of dst in bytes + * ebitmap_netlbl_export - Export an ebitmap into a NetLabel category bitmap + * @ebmap: the ebitmap to export + * @catmap: the NetLabel category bitmap * * Description: - * Allocate a buffer at least src->highbit bits long and export the extensible - * bitmap into the buffer. The bitmap string will be in little endian format, - * i.e. LSB first. The value returned in dst_len may not the true size of the - * buffer as the length of the buffer is rounded up to a multiple of MAPTYPE. - * The caller must free the buffer when finished. Returns zero on success, - * negative values on failure. + * Export a SELinux extensibile bitmap into a NetLabel category bitmap. + * Returns zero on success, negative values on error. * */ -int ebitmap_export(const struct ebitmap *src, - unsigned char **dst, - size_t *dst_len) +int ebitmap_netlbl_export(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap **catmap) { - size_t bitmap_len; - unsigned char *bitmap; - struct ebitmap_node *iter_node; - MAPTYPE node_val; - size_t bitmap_byte; - unsigned char bitmask; - - if (src->highbit == 0) { - *dst = NULL; - *dst_len = 0; + struct ebitmap_node *e_iter = ebmap->node; + struct netlbl_lsm_secattr_catmap *c_iter; + u32 cmap_idx; + + /* This function is a much simpler because SELinux's MAPTYPE happens + * to be the same as NetLabel's NETLBL_CATMAP_MAPTYPE, if MAPTYPE is + * changed from a u64 this function will most likely need to be changed + * as well. It's not ideal but I think the tradeoff in terms of + * neatness and speed is worth it. */ + + if (e_iter == NULL) { + *catmap = NULL; return 0; } - bitmap_len = src->highbit / 8; - if (src->highbit % 7) - bitmap_len += 1; - - bitmap = kzalloc((bitmap_len & ~(sizeof(MAPTYPE) - 1)) + - sizeof(MAPTYPE), - GFP_ATOMIC); - if (bitmap == NULL) + c_iter = netlbl_secattr_catmap_alloc(GFP_ATOMIC); + if (c_iter == NULL) return -ENOMEM; + *catmap = c_iter; + c_iter->startbit = e_iter->startbit & ~(NETLBL_CATMAP_SIZE - 1); + + while (e_iter != NULL) { + if (e_iter->startbit >= + (c_iter->startbit + NETLBL_CATMAP_SIZE)) { + c_iter->next = netlbl_secattr_catmap_alloc(GFP_ATOMIC); + if (c_iter->next == NULL) + goto netlbl_export_failure; + c_iter = c_iter->next; + c_iter->startbit = e_iter->startbit & + ~(NETLBL_CATMAP_SIZE - 1); + } + cmap_idx = (e_iter->startbit - c_iter->startbit) / + NETLBL_CATMAP_MAPSIZE; + c_iter->bitmap[cmap_idx] = e_iter->map; + e_iter = e_iter->next; + } - iter_node = src->node; - do { - bitmap_byte = iter_node->startbit / 8; - bitmask = 0x80; - node_val = iter_node->map; - do { - if (bitmask == 0) { - bitmap_byte++; - bitmask = 0x80; - } - if (node_val & (MAPTYPE)0x01) - bitmap[bitmap_byte] |= bitmask; - node_val >>= 1; - bitmask >>= 1; - } while (node_val > 0); - iter_node = iter_node->next; - } while (iter_node); - - *dst = bitmap; - *dst_len = bitmap_len; return 0; + +netlbl_export_failure: + netlbl_secattr_catmap_free(*catmap); + return -ENOMEM; } /** - * ebitmap_import - Import an unsigned char bitmap string into an ebitmap - * @src: the bitmap string - * @src_len: the bitmap length in bytes - * @dst: the empty ebitmap + * ebitmap_netlbl_import - Import a NetLabel category bitmap into an ebitmap + * @ebmap: the ebitmap to export + * @catmap: the NetLabel category bitmap * * Description: - * This function takes a little endian bitmap string in src and imports it into - * the ebitmap pointed to by dst. Returns zero on success, negative values on - * failure. + * Import a NetLabel category bitmap into a SELinux extensibile bitmap. + * Returns zero on success, negative values on error. * */ -int ebitmap_import(const unsigned char *src, - size_t src_len, - struct ebitmap *dst) +int ebitmap_netlbl_import(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap *catmap) { - size_t src_off = 0; - size_t node_limit; - struct ebitmap_node *node_new; - struct ebitmap_node *node_last = NULL; - u32 i_byte; - u32 i_bit; - unsigned char src_byte; - - while (src_off < src_len) { - if (src_len - src_off >= sizeof(MAPTYPE)) { - if (*(MAPTYPE *)&src[src_off] == 0) { - src_off += sizeof(MAPTYPE); - continue; - } - node_limit = sizeof(MAPTYPE); - } else { - for (src_byte = 0, i_byte = src_off; - i_byte < src_len && src_byte == 0; - i_byte++) - src_byte |= src[i_byte]; - if (src_byte == 0) - break; - node_limit = src_len - src_off; - } + struct ebitmap_node *e_iter = NULL; + struct ebitmap_node *emap_prev = NULL; + struct netlbl_lsm_secattr_catmap *c_iter = catmap; + u32 c_idx; - node_new = kzalloc(sizeof(*node_new), GFP_ATOMIC); - if (unlikely(node_new == NULL)) { - ebitmap_destroy(dst); - return -ENOMEM; - } - node_new->startbit = src_off * 8; - for (i_byte = 0; i_byte < node_limit; i_byte++) { - src_byte = src[src_off++]; - for (i_bit = i_byte * 8; src_byte != 0; i_bit++) { - if (src_byte & 0x80) - node_new->map |= MAPBIT << i_bit; - src_byte <<= 1; - } - } + /* This function is a much simpler because SELinux's MAPTYPE happens + * to be the same as NetLabel's NETLBL_CATMAP_MAPTYPE, if MAPTYPE is + * changed from a u64 this function will most likely need to be changed + * as well. It's not ideal but I think the tradeoff in terms of + * neatness and speed is worth it. */ - if (node_last != NULL) - node_last->next = node_new; - else - dst->node = node_new; - node_last = node_new; - } + do { + for (c_idx = 0; c_idx < NETLBL_CATMAP_MAPCNT; c_idx++) { + if (c_iter->bitmap[c_idx] == 0) + continue; + + e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC); + if (e_iter == NULL) + goto netlbl_import_failure; + if (emap_prev == NULL) + ebmap->node = e_iter; + else + emap_prev->next = e_iter; + emap_prev = e_iter; - if (likely(node_last != NULL)) - dst->highbit = node_last->startbit + MAPSIZE; + e_iter->startbit = c_iter->startbit + + NETLBL_CATMAP_MAPSIZE * c_idx; + e_iter->map = c_iter->bitmap[c_idx]; + } + c_iter = c_iter->next; + } while (c_iter != NULL); + if (e_iter != NULL) + ebmap->highbit = e_iter->startbit + MAPSIZE; else - ebitmap_init(dst); + ebitmap_destroy(ebmap); return 0; + +netlbl_import_failure: + ebitmap_destroy(ebmap); + return -ENOMEM; } +#endif /* CONFIG_NETLABEL */ int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2) { diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h index da2d465..1270e34 100644 --- a/security/selinux/ss/ebitmap.h +++ b/security/selinux/ss/ebitmap.h @@ -14,6 +14,8 @@ #ifndef _SS_EBITMAP_H_ #define _SS_EBITMAP_H_ +#include + #define MAPTYPE u64 /* portion of bitmap in each node */ #define MAPSIZE (sizeof(MAPTYPE) * 8) /* number of bits in node bitmap */ #define MAPBIT 1ULL /* a bit in the node bitmap */ @@ -69,16 +71,28 @@ static inline int ebitmap_node_get_bit(struct ebitmap_node * n, int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2); int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src); -int ebitmap_export(const struct ebitmap *src, - unsigned char **dst, - size_t *dst_len); -int ebitmap_import(const unsigned char *src, - size_t src_len, - struct ebitmap *dst); int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2); int ebitmap_get_bit(struct ebitmap *e, unsigned long bit); int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value); void ebitmap_destroy(struct ebitmap *e); int ebitmap_read(struct ebitmap *e, void *fp); +#ifdef CONFIG_NETLABEL +int ebitmap_netlbl_export(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap **catmap); +int ebitmap_netlbl_import(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap *catmap); +#else +static inline int ebitmap_netlbl_export(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap **catmap) +{ + return -ENOMEM; +} +static inline int ebitmap_netlbl_import(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap *catmap) +{ + return -ENOMEM; +} +#endif + #endif /* _SS_EBITMAP_H_ */ diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index 2cca8e2..b4f682d 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -13,7 +13,7 @@ /* * Updated: Hewlett-Packard * - * Added support to import/export the MLS label + * Added support to import/export the MLS label from NetLabel * * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 */ @@ -22,6 +22,7 @@ #include #include #include +#include #include "sidtab.h" #include "mls.h" #include "policydb.h" @@ -571,163 +572,108 @@ int mls_compute_sid(struct context *scontext, return -EINVAL; } +#ifdef CONFIG_NETLABEL /** - * mls_export_lvl - Export the MLS sensitivity levels + * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel * @context: the security context - * @low: the low sensitivity level - * @high: the high sensitivity level + * @secattr: the NetLabel security attributes * * Description: - * Given the security context copy the low MLS sensitivity level into lvl_low - * and the high sensitivity level in lvl_high. The MLS levels are only - * exported if the pointers are not NULL, if they are NULL then that level is - * not exported. + * Given the security context copy the low MLS sensitivity level into the + * NetLabel MLS sensitivity level field. * */ -void mls_export_lvl(const struct context *context, u32 *low, u32 *high) +void mls_export_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) { if (!selinux_mls_enabled) return; - if (low != NULL) - *low = context->range.level[0].sens - 1; - if (high != NULL) - *high = context->range.level[1].sens - 1; + secattr->mls_lvl = context->range.level[0].sens - 1; + secattr->flags |= NETLBL_SECATTR_MLS_LVL; } /** - * mls_import_lvl - Import the MLS sensitivity levels + * mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels * @context: the security context - * @low: the low sensitivity level - * @high: the high sensitivity level + * @secattr: the NetLabel security attributes * * Description: - * Given the security context and the two sensitivty levels, set the MLS levels - * in the context according the two given as parameters. Returns zero on - * success, negative values on failure. + * Given the security context and the NetLabel security attributes, copy the + * NetLabel MLS sensitivity level into the context. * */ -void mls_import_lvl(struct context *context, u32 low, u32 high) +void mls_import_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) { if (!selinux_mls_enabled) return; - context->range.level[0].sens = low + 1; - context->range.level[1].sens = high + 1; + context->range.level[0].sens = secattr->mls_lvl + 1; + context->range.level[1].sens = context->range.level[0].sens; } /** - * mls_export_cat - Export the MLS categories + * mls_export_netlbl_cat - Export the MLS categories to NetLabel * @context: the security context - * @low: the low category - * @low_len: length of the cat_low bitmap in bytes - * @high: the high category - * @high_len: length of the cat_high bitmap in bytes + * @secattr: the NetLabel security attributes * * Description: - * Given the security context export the low MLS category bitmap into cat_low - * and the high category bitmap into cat_high. The MLS categories are only - * exported if the pointers are not NULL, if they are NULL then that level is - * not exported. The caller is responsibile for freeing the memory when - * finished. Returns zero on success, negative values on failure. + * Given the security context copy the low MLS categories into the NetLabel + * MLS category field. Returns zero on success, negative values on failure. * */ -int mls_export_cat(const struct context *context, - unsigned char **low, - size_t *low_len, - unsigned char **high, - size_t *high_len) +int mls_export_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) { - int rc = -EPERM; + int rc; - if (!selinux_mls_enabled) { - *low = NULL; - *low_len = 0; - *high = NULL; - *high_len = 0; + if (!selinux_mls_enabled) return 0; - } - if (low != NULL) { - rc = ebitmap_export(&context->range.level[0].cat, - low, - low_len); - if (rc != 0) - goto export_cat_failure; - } - if (high != NULL) { - rc = ebitmap_export(&context->range.level[1].cat, - high, - high_len); - if (rc != 0) - goto export_cat_failure; - } - - return 0; + rc = ebitmap_netlbl_export(&context->range.level[0].cat, + &secattr->mls_cat); + if (rc == 0 && secattr->mls_cat != NULL) + secattr->flags |= NETLBL_SECATTR_MLS_CAT; -export_cat_failure: - if (low != NULL) { - kfree(*low); - *low = NULL; - *low_len = 0; - } - if (high != NULL) { - kfree(*high); - *high = NULL; - *high_len = 0; - } return rc; } /** - * mls_import_cat - Import the MLS categories + * mls_import_netlbl_cat - Import the MLS categories from NetLabel * @context: the security context - * @low: the low category - * @low_len: length of the cat_low bitmap in bytes - * @high: the high category - * @high_len: length of the cat_high bitmap in bytes + * @secattr: the NetLabel security attributes * * Description: - * Given the security context and the two category bitmap strings import the - * categories into the security context. The MLS categories are only imported - * if the pointers are not NULL, if they are NULL they are skipped. Returns - * zero on success, negative values on failure. + * Copy the NetLabel security attributes into the SELinux context; since the + * NetLabel security attribute only contains a single MLS category use it for + * both the low and high categories of the context. Returns zero on success, + * negative values on failure. * */ -int mls_import_cat(struct context *context, - const unsigned char *low, - size_t low_len, - const unsigned char *high, - size_t high_len) +int mls_import_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) { - int rc = -EPERM; + int rc; if (!selinux_mls_enabled) return 0; - if (low != NULL) { - rc = ebitmap_import(low, - low_len, - &context->range.level[0].cat); - if (rc != 0) - goto import_cat_failure; - } - if (high != NULL) { - if (high == low) - rc = ebitmap_cpy(&context->range.level[1].cat, - &context->range.level[0].cat); - else - rc = ebitmap_import(high, - high_len, - &context->range.level[1].cat); - if (rc != 0) - goto import_cat_failure; - } + rc = ebitmap_netlbl_import(&context->range.level[0].cat, + secattr->mls_cat); + if (rc != 0) + goto import_netlbl_cat_failure; + + rc = ebitmap_cpy(&context->range.level[1].cat, + &context->range.level[0].cat); + if (rc != 0) + goto import_netlbl_cat_failure; return 0; -import_cat_failure: +import_netlbl_cat_failure: ebitmap_destroy(&context->range.level[0].cat); ebitmap_destroy(&context->range.level[1].cat); return rc; } +#endif /* CONFIG_NETLABEL */ diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h index df6032c6..661d6fc 100644 --- a/security/selinux/ss/mls.h +++ b/security/selinux/ss/mls.h @@ -13,7 +13,7 @@ /* * Updated: Hewlett-Packard * - * Added support to import/export the MLS label + * Added support to import/export the MLS label from NetLabel * * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 */ @@ -69,19 +69,37 @@ int mls_compute_sid(struct context *scontext, int mls_setup_user_range(struct context *fromcon, struct user_datum *user, struct context *usercon); -void mls_export_lvl(const struct context *context, u32 *low, u32 *high); -void mls_import_lvl(struct context *context, u32 low, u32 high); - -int mls_export_cat(const struct context *context, - unsigned char **low, - size_t *low_len, - unsigned char **high, - size_t *high_len); -int mls_import_cat(struct context *context, - const unsigned char *low, - size_t low_len, - const unsigned char *high, - size_t high_len); +#ifdef CONFIG_NETLABEL +void mls_export_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr); +void mls_import_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr); +int mls_export_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr); +int mls_import_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr); +#else +static inline void mls_export_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return; +} +static inline void mls_import_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return; +} +static inline int mls_export_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return -ENOMEM; +} +static inline int mls_import_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return -ENOMEM; +} +#endif #endif /* _SS_MLS_H */ diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index b43dd80..bdb7070 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -55,6 +55,7 @@ #include "objsec.h" #include "selinux_netlabel.h" #include "xfrm.h" +#include "ebitmap.h" extern void selnl_notify_policyload(u32 seqno); unsigned int policydb_loaded_version; @@ -2384,13 +2385,10 @@ static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb, ctx_new.user = ctx->user; ctx_new.role = ctx->role; ctx_new.type = ctx->type; - mls_import_lvl(&ctx_new, secattr->mls_lvl, secattr->mls_lvl); + mls_import_netlbl_lvl(&ctx_new, secattr); if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { - if (mls_import_cat(&ctx_new, - secattr->mls_cat, - secattr->mls_cat_len, - NULL, - 0) != 0) + if (ebitmap_netlbl_import(&ctx_new.range.level[0].cat, + secattr->mls_cat) != 0) goto netlbl_secattr_to_sid_return; ctx_new.range.level[1].cat.highbit = ctx_new.range.level[0].cat.highbit; @@ -2486,19 +2484,12 @@ static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) secattr.domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1], GFP_ATOMIC); - mls_export_lvl(ctx, &secattr.mls_lvl, NULL); - rc = mls_export_cat(ctx, - &secattr.mls_cat, - &secattr.mls_cat_len, - NULL, - NULL); + secattr.flags |= NETLBL_SECATTR_DOMAIN; + mls_export_netlbl_lvl(ctx, &secattr); + rc = mls_export_netlbl_cat(ctx, &secattr); if (rc != 0) goto netlbl_socket_setsid_return; - secattr.flags |= NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL; - if (secattr.mls_cat) - secattr.flags |= NETLBL_SECATTR_MLS_CAT; - rc = netlbl_socket_setattr(sock, &secattr); if (rc == 0) { spin_lock(&sksec->nlbl_lock); -- cgit v1.1 From 6cbda6b6e2e2a0a84c0fcda8ea262c16d7a63fc8 Mon Sep 17 00:00:00 2001 From: James Morris Date: Wed, 29 Nov 2006 16:50:27 -0500 Subject: Rename class_destroy to avoid namespace conflicts. We're seeing increasing namespace conflicts between the global class_destroy() function declared in linux/device.h, and the private function in the SELinux core code. This patch renames the SELinux function to cls_destroy() to avoid this conflict. Acked-by: Stephen Smalley Signed-off-by: James Morris --- security/selinux/ss/policydb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index ba48961..cd79c63 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -468,7 +468,7 @@ static int common_destroy(void *key, void *datum, void *p) return 0; } -static int class_destroy(void *key, void *datum, void *p) +static int cls_destroy(void *key, void *datum, void *p) { struct class_datum *cladatum; struct constraint_node *constraint, *ctemp; @@ -566,7 +566,7 @@ static int cat_destroy(void *key, void *datum, void *p) static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) = { common_destroy, - class_destroy, + cls_destroy, role_destroy, type_destroy, user_destroy, @@ -1124,7 +1124,7 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) out: return rc; bad: - class_destroy(key, cladatum, NULL); + cls_destroy(key, cladatum, NULL); goto out; } -- cgit v1.1