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 8ab5679a37a3..28ee187ed224 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.2.3 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 bfe122764c98..33ae1020091e 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.2.3 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 a300702da527..74c0319c417e 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 450a2831e2e3..ff869e8b6f4a 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.2.3 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 24e5ec957630..77b530c3bbce 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 4cc85816a718..7e2ff3e3c6d2 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 24a10d36d3b6..837658a98a54 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.2.3 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 33ae1020091e..408820486af0 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.2.3 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 43874c1e6e23..838d8442cf3c 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 526b28019aca..8e329ddb5e37 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 675b995a67c3..4d5a043cdfa1 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.2.3 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 838d8442cf3c..0148d1518dd1 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 28ee187ed224..5bbd599a4471 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 8e329ddb5e37..27502365d706 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 4d5a043cdfa1..8fef74271f22 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) @@ -402,44 +403,9 @@ void selinux_xfrm_state_free(struct xfrm_state *x) kfree(ctx); } -/* - * 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.2.3 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 0148d1518dd1..558795b237d6 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 5bbd599a4471..956137baf3e7 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 27502365d706..ebd7246a4be5 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 8fef74271f22..9b777140068f 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.2.3 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 956137baf3e7..0cf98740ddc6 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 a68fdd55597f..8377a4ba3b95 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 09fc8a2345eb..ad9fb2d69b50 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 81f4f526c8b1..2de4b5fe3aa1 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 24303b61309f..9f3ebb1bfae6 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 95887aed2a68..67cef371ee00 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.2.3 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 b8f4d25cf335..ccfe8755735e 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.2.3 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 408820486af0..1f5bbb246d28 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.2.3 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 0cf98740ddc6..975c0dfb5a11 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 ef2267fea8bd..91b88f0ba20c 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 9de10cc2cef2..57943f4a8f90 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 socke