GIT 7bef63925dd7d6812682b136d48e5f6e59fd3d56 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/jmorris/selinux-2.6.20.git commit Author: Chad Sellers Date: Mon Nov 6 12:38:18 2006 -0500 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 commit b3350a7c6bd6dcdf62408b2644ac45a1850466f5 Author: Chad Sellers Date: Mon Nov 6 12:38:17 2006 -0500 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 commit a6f25ba8a71851ffcf3fb9d4fbd28d1be68ca32f Author: Chad Sellers Date: Mon Nov 6 12:38:16 2006 -0500 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 commit 2726d189211769cd266897321c188b852c3f1417 Author: Chad Sellers Date: Mon Nov 6 12:38:15 2006 -0500 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/avc.c | 23 ++-- security/selinux/include/avc_ss.h | 24 ++++ security/selinux/ss/hashtab.c | 6 + security/selinux/ss/hashtab.h | 10 +- security/selinux/ss/services.c | 203 +++++++++++++++++++++++-------------- security/selinux/ss/symtab.c | 8 + 6 files changed, 171 insertions(+), 103 deletions(-) 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 #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 @@ #undef TB_ #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 @@ #include "flask.h" 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_ */ 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 #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, vo 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, vo * 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/services.c b/security/selinux/ss/services.c index bfe1227..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 @@ #include "selinux_netlabel.h" 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) @@ -1019,86 +1028,112 @@ int security_change_sid(u32 ssid, } /* - * Verify that each permission that is defined under the - * existing policy is still defined with the same value - * in the new policy. + * Verify that each kernel class that is defined in the + * policy is correct */ -static int validate_perm(void *key, void *datum, void *p) +static int validate_classes(struct policydb *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; + 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; + } } - 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; + 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; + } } - 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; + 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; + } } } - 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; + return 0; } /* Clone the SID into the new SID table. */ @@ -1243,6 +1278,16 @@ int security_load_policy(void *data, siz 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; @@ -1265,10 +1310,10 @@ #endif 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"); + /* 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; } 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 #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 hasht 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;