diff options
| author | Linus Torvalds <torvalds@woody.osdl.org> | 2006-12-01 19:43:42 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.osdl.org> | 2006-12-01 19:43:42 -0500 |
| commit | b07e3c3a1db0ce399d2a1d04860e1b901927c05e (patch) | |
| tree | 474c17e969b5462a3702f0021249e1d78522ac35 | |
| parent | 5f56bbdf1e35d41b4b3d4c92bdb3e70c63877e4d (diff) | |
| parent | b94c7e677b9d28bd3f9ba4a70df6bfa7942867ca (diff) | |
Merge branch 'for-2.6.20' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/selinux-2.6
* 'for-2.6.20' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/selinux-2.6:
SELinux: validate kernel object classes and permissions
SELinux: ensure keys constant in hashtab_search
SELinux: export object class and permission definitions
SELinux: remove current object class and permission validation mechanism
| -rw-r--r-- | security/selinux/avc.c | 23 | ||||
| -rw-r--r-- | security/selinux/include/avc_ss.h | 24 | ||||
| -rw-r--r-- | security/selinux/ss/hashtab.c | 6 | ||||
| -rw-r--r-- | security/selinux/ss/hashtab.h | 10 | ||||
| -rw-r--r-- | security/selinux/ss/services.c | 203 | ||||
| -rw-r--r-- | 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 a300702da527..74c0319c417e 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c | |||
| @@ -32,12 +32,7 @@ | |||
| 32 | #include "avc.h" | 32 | #include "avc.h" |
| 33 | #include "avc_ss.h" | 33 | #include "avc_ss.h" |
| 34 | 34 | ||
| 35 | static const struct av_perm_to_string | 35 | static const struct av_perm_to_string av_perm_to_string[] = { |
| 36 | { | ||
| 37 | u16 tclass; | ||
| 38 | u32 value; | ||
| 39 | const char *name; | ||
| 40 | } av_perm_to_string[] = { | ||
| 41 | #define S_(c, v, s) { c, v, s }, | 36 | #define S_(c, v, s) { c, v, s }, |
| 42 | #include "av_perm_to_string.h" | 37 | #include "av_perm_to_string.h" |
| 43 | #undef S_ | 38 | #undef S_ |
| @@ -57,17 +52,21 @@ static const char *class_to_string[] = { | |||
| 57 | #undef TE_ | 52 | #undef TE_ |
| 58 | #undef S_ | 53 | #undef S_ |
| 59 | 54 | ||
| 60 | static const struct av_inherit | 55 | static const struct av_inherit av_inherit[] = { |
| 61 | { | ||
| 62 | u16 tclass; | ||
| 63 | const char **common_pts; | ||
| 64 | u32 common_base; | ||
| 65 | } av_inherit[] = { | ||
| 66 | #define S_(c, i, b) { c, common_##i##_perm_to_string, b }, | 56 | #define S_(c, i, b) { c, common_##i##_perm_to_string, b }, |
| 67 | #include "av_inherit.h" | 57 | #include "av_inherit.h" |
| 68 | #undef S_ | 58 | #undef S_ |
| 69 | }; | 59 | }; |
| 70 | 60 | ||
| 61 | const struct selinux_class_perm selinux_class_perm = { | ||
| 62 | av_perm_to_string, | ||
| 63 | ARRAY_SIZE(av_perm_to_string), | ||
| 64 | class_to_string, | ||
| 65 | ARRAY_SIZE(class_to_string), | ||
| 66 | av_inherit, | ||
| 67 | ARRAY_SIZE(av_inherit) | ||
| 68 | }; | ||
| 69 | |||
| 71 | #define AVC_CACHE_SLOTS 512 | 70 | #define AVC_CACHE_SLOTS 512 |
| 72 | #define AVC_DEF_CACHE_THRESHOLD 512 | 71 | #define AVC_DEF_CACHE_THRESHOLD 512 |
| 73 | #define AVC_CACHE_RECLAIM 16 | 72 | #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 @@ | |||
| 10 | 10 | ||
| 11 | int avc_ss_reset(u32 seqno); | 11 | int avc_ss_reset(u32 seqno); |
| 12 | 12 | ||
| 13 | struct av_perm_to_string | ||
| 14 | { | ||
| 15 | u16 tclass; | ||
| 16 | u32 value; | ||
| 17 | const char *name; | ||
| 18 | }; | ||
| 19 | |||
| 20 | struct av_inherit | ||
| 21 | { | ||
| 22 | u16 tclass; | ||
| 23 | const char **common_pts; | ||
| 24 | u32 common_base; | ||
| 25 | }; | ||
| 26 | |||
| 27 | struct selinux_class_perm | ||
| 28 | { | ||
| 29 | const struct av_perm_to_string *av_perm_to_string; | ||
| 30 | u32 av_pts_len; | ||
| 31 | const char **class_to_string; | ||
| 32 | u32 cts_len; | ||
| 33 | const struct av_inherit *av_inherit; | ||
| 34 | u32 av_inherit_len; | ||
| 35 | }; | ||
| 36 | |||
| 13 | #endif /* _SELINUX_AVC_SS_H_ */ | 37 | #endif /* _SELINUX_AVC_SS_H_ */ |
| 14 | 38 | ||
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 @@ | |||
| 8 | #include <linux/errno.h> | 8 | #include <linux/errno.h> |
| 9 | #include "hashtab.h" | 9 | #include "hashtab.h" |
| 10 | 10 | ||
| 11 | struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key), | 11 | struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key), |
| 12 | int (*keycmp)(struct hashtab *h, void *key1, void *key2), | 12 | int (*keycmp)(struct hashtab *h, const void *key1, const void *key2), |
| 13 | u32 size) | 13 | u32 size) |
| 14 | { | 14 | { |
| 15 | struct hashtab *p; | 15 | struct hashtab *p; |
| @@ -71,7 +71,7 @@ int hashtab_insert(struct hashtab *h, void *key, void *datum) | |||
| 71 | return 0; | 71 | return 0; |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | void *hashtab_search(struct hashtab *h, void *key) | 74 | void *hashtab_search(struct hashtab *h, const void *key) |
| 75 | { | 75 | { |
| 76 | u32 hvalue; | 76 | u32 hvalue; |
| 77 | struct hashtab_node *cur; | 77 | 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 { | |||
| 22 | struct hashtab_node **htable; /* hash table */ | 22 | struct hashtab_node **htable; /* hash table */ |
| 23 | u32 size; /* number of slots in hash table */ | 23 | u32 size; /* number of slots in hash table */ |
| 24 | u32 nel; /* number of elements in hash table */ | 24 | u32 nel; /* number of elements in hash table */ |
| 25 | u32 (*hash_value)(struct hashtab *h, void *key); | 25 | u32 (*hash_value)(struct hashtab *h, const void *key); |
| 26 | /* hash function */ | 26 | /* hash function */ |
| 27 | int (*keycmp)(struct hashtab *h, void *key1, void *key2); | 27 | int (*keycmp)(struct hashtab *h, const void *key1, const void *key2); |
| 28 | /* key comparison function */ | 28 | /* key comparison function */ |
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| @@ -39,8 +39,8 @@ struct hashtab_info { | |||
| 39 | * Returns NULL if insufficent space is available or | 39 | * Returns NULL if insufficent space is available or |
| 40 | * the new hash table otherwise. | 40 | * the new hash table otherwise. |
| 41 | */ | 41 | */ |
| 42 | struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key), | 42 | struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key), |
| 43 | int (*keycmp)(struct hashtab *h, void *key1, void *key2), | 43 | int (*keycmp)(struct hashtab *h, const void *key1, const void *key2), |
| 44 | u32 size); | 44 | u32 size); |
| 45 | 45 | ||
| 46 | /* | 46 | /* |
| @@ -59,7 +59,7 @@ int hashtab_insert(struct hashtab *h, void *k, void *d); | |||
| 59 | * Returns NULL if no entry has the specified key or | 59 | * Returns NULL if no entry has the specified key or |
| 60 | * the datum of the entry otherwise. | 60 | * the datum of the entry otherwise. |
| 61 | */ | 61 | */ |
| 62 | void *hashtab_search(struct hashtab *h, void *k); | 62 | void *hashtab_search(struct hashtab *h, const void *k); |
| 63 | 63 | ||
| 64 | /* | 64 | /* |
| 65 | * Destroys the specified hash table. | 65 | * Destroys the specified hash table. |
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index bfe122764c98..408820486af0 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c | |||
| @@ -17,9 +17,13 @@ | |||
| 17 | * | 17 | * |
| 18 | * Added support for NetLabel | 18 | * Added support for NetLabel |
| 19 | * | 19 | * |
| 20 | * Updated: Chad Sellers <csellers@tresys.com> | ||
| 21 | * | ||
| 22 | * Added validation of kernel classes and permissions | ||
| 23 | * | ||
| 20 | * Copyright (C) 2006 Hewlett-Packard Development Company, L.P. | 24 | * Copyright (C) 2006 Hewlett-Packard Development Company, L.P. |
| 21 | * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. | 25 | * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. |
| 22 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC | 26 | * Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC |
| 23 | * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> | 27 | * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> |
| 24 | * This program is free software; you can redistribute it and/or modify | 28 | * This program is free software; you can redistribute it and/or modify |
| 25 | * it under the terms of the GNU General Public License as published by | 29 | * it under the terms of the GNU General Public License as published by |
| @@ -53,6 +57,11 @@ | |||
| 53 | extern void selnl_notify_policyload(u32 seqno); | 57 | extern void selnl_notify_policyload(u32 seqno); |
| 54 | unsigned int policydb_loaded_version; | 58 | unsigned int policydb_loaded_version; |
| 55 | 59 | ||
| 60 | /* | ||
| 61 | * This is declared in avc.c | ||
| 62 | */ | ||
| 63 | extern const struct selinux_class_perm selinux_class_perm; | ||
| 64 | |||
| 56 | static DEFINE_RWLOCK(policy_rwlock); | 65 | static DEFINE_RWLOCK(policy_rwlock); |
| 57 | #define POLICY_RDLOCK read_lock(&policy_rwlock) | 66 | #define POLICY_RDLOCK read_lock(&policy_rwlock) |
| 58 | #define POLICY_WRLOCK write_lock_irq(&policy_rwlock) | 67 | #define POLICY_WRLOCK write_lock_irq(&policy_rwlock) |
| @@ -1019,86 +1028,112 @@ int security_change_sid(u32 ssid, | |||
| 1019 | } | 1028 | } |
| 1020 | 1029 | ||
| 1021 | /* | 1030 | /* |
| 1022 | * Verify that each permission that is defined under the | 1031 | * Verify that each kernel class that is defined in the |
| 1023 | * existing policy is still defined with the same value | 1032 | * policy is correct |
| 1024 | * in the new policy. | ||
| 1025 | */ | 1033 | */ |
| 1026 | static int validate_perm(void *key, void *datum, void *p) | 1034 | static int validate_classes(struct policydb *p) |
| 1027 | { | 1035 | { |
| 1028 | struct hashtab *h; | 1036 | int i, j; |
| 1029 | struct perm_datum *perdatum, *perdatum2; | 1037 | struct class_datum *cladatum; |
| 1030 | int rc = 0; | 1038 | struct perm_datum *perdatum; |
| 1031 | 1039 | u32 nprim, tmp, common_pts_len, perm_val, pol_val; | |
| 1032 | 1040 | u16 class_val; | |
| 1033 | h = p; | 1041 | const struct selinux_class_perm *kdefs = &selinux_class_perm; |
| 1034 | perdatum = datum; | 1042 | const char *def_class, *def_perm, *pol_class; |
| 1035 | 1043 | struct symtab *perms; | |
| 1036 | perdatum2 = hashtab_search(h, key); | 1044 | |
| 1037 | if (!perdatum2) { | 1045 | for (i = 1; i < kdefs->cts_len; i++) { |
| 1038 | printk(KERN_ERR "security: permission %s disappeared", | 1046 | def_class = kdefs->class_to_string[i]; |
| 1039 | (char *)key); | 1047 | if (i > p->p_classes.nprim) { |
| 1040 | rc = -ENOENT; | 1048 | printk(KERN_INFO |
| 1041 | goto out; | 1049 | "security: class %s not defined in policy\n", |
| 1042 | } | 1050 | def_class); |
| 1043 | if (perdatum->value != perdatum2->value) { | 1051 | continue; |
| 1044 | printk(KERN_ERR "security: the value of permission %s changed", | 1052 | } |
| 1045 | (char *)key); | 1053 | pol_class = p->p_class_val_to_name[i-1]; |
| 1046 | rc = -EINVAL; | 1054 | if (strcmp(pol_class, def_class)) { |
| 1047 | } | 1055 | printk(KERN_ERR |
| 1048 | out: | 1056 | "security: class %d is incorrect, found %s but should be %s\n", |
| 1049 | return rc; | 1057 | i, pol_class, def_class); |
| 1050 | } | 1058 | return -EINVAL; |
| 1051 | 1059 | } | |
| 1052 | /* | ||
| 1053 | * Verify that each class that is defined under the | ||
| 1054 | * existing policy is still defined with the same | ||
| 1055 | * attributes in the new policy. | ||
| 1056 | */ | ||
| 1057 | static int validate_class(void *key, void *datum, void *p) | ||
| 1058 | { | ||
| 1059 | struct policydb *newp; | ||
| 1060 | struct class_datum *cladatum, *cladatum2; | ||
| 1061 | int rc; | ||
| 1062 | |||
| 1063 | newp = p; | ||
| 1064 | cladatum = datum; | ||
| 1065 | |||
| 1066 | cladatum2 = hashtab_search(newp->p_classes.table, key); | ||
| 1067 | if (!cladatum2) { | ||
| 1068 | printk(KERN_ERR "security: class %s disappeared\n", | ||
| 1069 | (char *)key); | ||
| 1070 | rc = -ENOENT; | ||
| 1071 | goto out; | ||
| 1072 | } | ||
| 1073 | if (cladatum->value != cladatum2->value) { | ||
| 1074 | printk(KERN_ERR "security: the value of class %s changed\n", | ||
| 1075 | (char *)key); | ||
| 1076 | rc = -EINVAL; | ||
| 1077 | goto out; | ||
| 1078 | } | 1060 | } |
| 1079 | if ((cladatum->comdatum && !cladatum2->comdatum) || | 1061 | for (i = 0; i < kdefs->av_pts_len; i++) { |
| 1080 | (!cladatum->comdatum && cladatum2->comdatum)) { | 1062 | class_val = kdefs->av_perm_to_string[i].tclass; |
| 1081 | printk(KERN_ERR "security: the inherits clause for the access " | 1063 | perm_val = kdefs->av_perm_to_string[i].value; |
| 1082 | "vector definition for class %s changed\n", (char *)key); | 1064 | def_perm = kdefs->av_perm_to_string[i].name; |
| 1083 | rc = -EINVAL; | 1065 | if (class_val > p->p_classes.nprim) |
| 1084 | goto out; | 1066 | continue; |
| 1067 | pol_class = p->p_class_val_to_name[class_val-1]; | ||
| 1068 | cladatum = hashtab_search(p->p_classes.table, pol_class); | ||
| 1069 | BUG_ON(!cladatum); | ||
| 1070 | perms = &cladatum->permissions; | ||
| 1071 | nprim = 1 << (perms->nprim - 1); | ||
| 1072 | if (perm_val > nprim) { | ||
| 1073 | printk(KERN_INFO | ||
| 1074 | "security: permission %s in class %s not defined in policy\n", | ||
| 1075 | def_perm, pol_class); | ||
| 1076 | continue; | ||
| 1077 | } | ||
| 1078 | perdatum = hashtab_search(perms->table, def_perm); | ||
| 1079 | if (perdatum == NULL) { | ||
| 1080 | printk(KERN_ERR | ||
| 1081 | "security: permission %s in class %s not found in policy\n", | ||
| 1082 | def_perm, pol_class); | ||
| 1083 | return -EINVAL; | ||
| 1084 | } | ||
| 1085 | pol_val = 1 << (perdatum->value - 1); | ||
| 1086 | if (pol_val != perm_val) { | ||
| 1087 | printk(KERN_ERR | ||
| 1088 | "security: permission %s in class %s has incorrect value\n", | ||
| 1089 | def_perm, pol_class); | ||
| 1090 | return -EINVAL; | ||
| 1091 | } | ||
| 1085 | } | 1092 | } |
| 1086 | if (cladatum->comdatum) { | 1093 | for (i = 0; i < kdefs->av_inherit_len; i++) { |
| 1087 | rc = hashtab_map(cladatum->comdatum->permissions.table, validate_perm, | 1094 | class_val = kdefs->av_inherit[i].tclass; |
| 1088 | cladatum2->comdatum->permissions.table); | 1095 | if (class_val > p->p_classes.nprim) |
| 1089 | if (rc) { | 1096 | continue; |
| 1090 | printk(" in the access vector definition for class " | 1097 | pol_class = p->p_class_val_to_name[class_val-1]; |
| 1091 | "%s\n", (char *)key); | 1098 | cladatum = hashtab_search(p->p_classes.table, pol_class); |
| 1092 | goto out; | 1099 | BUG_ON(!cladatum); |
| 1100 | if (!cladatum->comdatum) { | ||
| 1101 | printk(KERN_ERR | ||
| 1102 | "security: class %s should have an inherits clause but does not\n", | ||
| 1103 | pol_class); | ||
| 1104 | return -EINVAL; | ||
| 1105 | } | ||
| 1106 | tmp = kdefs->av_inherit[i].common_base; | ||
| 1107 | common_pts_len = 0; | ||
| 1108 | while (!(tmp & 0x01)) { | ||
| 1109 | common_pts_len++; | ||
| 1110 | tmp >>= 1; | ||
| 1111 | } | ||
| 1112 | perms = &cladatum->comdatum->permissions; | ||
| 1113 | for (j = 0; j < common_pts_len; j++) { | ||
| 1114 | def_perm = kdefs->av_inherit[i].common_pts[j]; | ||
| 1115 | if (j >= perms->nprim) { | ||
| 1116 | printk(KERN_INFO | ||
| 1117 | "security: permission %s in class %s not defined in policy\n", | ||
| 1118 | def_perm, pol_class); | ||
| 1119 | continue; | ||
| 1120 | } | ||
| 1121 | perdatum = hashtab_search(perms->table, def_perm); | ||
| 1122 | if (perdatum == NULL) { | ||
| 1123 | printk(KERN_ERR | ||
| 1124 | "security: permission %s in class %s not found in policy\n", | ||
| 1125 | def_perm, pol_class); | ||
| 1126 | return -EINVAL; | ||
| 1127 | } | ||
| 1128 | if (perdatum->value != j + 1) { | ||
| 1129 | printk(KERN_ERR | ||
| 1130 | "security: permission %s in class %s has incorrect value\n", | ||
| 1131 | def_perm, pol_class); | ||
| 1132 | return -EINVAL; | ||
| 1133 | } | ||
| 1093 | } | 1134 | } |
| 1094 | } | 1135 | } |
| 1095 | rc = hashtab_map(cladatum->permissions.table, validate_perm, | 1136 | return 0; |
| 1096 | cladatum2->permissions.table); | ||
| 1097 | if (rc) | ||
| 1098 | printk(" in access vector definition for class %s\n", | ||
| 1099 | (char *)key); | ||
| 1100 | out: | ||
| 1101 | return rc; | ||
| 1102 | } | 1137 | } |
| 1103 | 1138 | ||
| 1104 | /* Clone the SID into the new SID table. */ | 1139 | /* Clone the SID into the new SID table. */ |
| @@ -1243,6 +1278,16 @@ int security_load_policy(void *data, size_t len) | |||
| 1243 | avtab_cache_destroy(); | 1278 | avtab_cache_destroy(); |
| 1244 | return -EINVAL; | 1279 | return -EINVAL; |
| 1245 | } | 1280 | } |
| 1281 | /* Verify that the kernel defined classes are correct. */ | ||
| 1282 | if (validate_classes(&policydb)) { | ||
| 1283 | printk(KERN_ERR | ||
| 1284 | "security: the definition of a class is incorrect\n"); | ||
| 1285 | LOAD_UNLOCK; | ||
| 1286 | sidtab_destroy(&sidtab); | ||
| 1287 | policydb_destroy(&policydb); | ||
| 1288 | avtab_cache_destroy(); | ||
| 1289 | return -EINVAL; | ||
| 1290 | } | ||
| 1246 | policydb_loaded_version = policydb.policyvers; | 1291 | policydb_loaded_version = policydb.policyvers; |
| 1247 | ss_initialized = 1; | 1292 | ss_initialized = 1; |
| 1248 | seqno = ++latest_granting; | 1293 | seqno = ++latest_granting; |
| @@ -1265,10 +1310,10 @@ int security_load_policy(void *data, size_t len) | |||
| 1265 | 1310 | ||
| 1266 | sidtab_init(&newsidtab); | 1311 | sidtab_init(&newsidtab); |
| 1267 | 1312 | ||
| 1268 | /* Verify that the existing classes did not change. */ | 1313 | /* Verify that the kernel defined classes are correct. */ |
| 1269 | if (hashtab_map(policydb.p_classes.table, validate_class, &newpolicydb)) { | 1314 | if (validate_classes(&newpolicydb)) { |
| 1270 | printk(KERN_ERR "security: the definition of an existing " | 1315 | printk(KERN_ERR |
| 1271 | "class changed\n"); | 1316 | "security: the definition of a class is incorrect\n"); |
| 1272 | rc = -EINVAL; | 1317 | rc = -EINVAL; |
| 1273 | goto err; | 1318 | goto err; |
| 1274 | } | 1319 | } |
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 @@ | |||
| 9 | #include <linux/errno.h> | 9 | #include <linux/errno.h> |
| 10 | #include "symtab.h" | 10 | #include "symtab.h" |
| 11 | 11 | ||
| 12 | static unsigned int symhash(struct hashtab *h, void *key) | 12 | static unsigned int symhash(struct hashtab *h, const void *key) |
| 13 | { | 13 | { |
| 14 | char *p, *keyp; | 14 | const char *p, *keyp; |
| 15 | unsigned int size; | 15 | unsigned int size; |
| 16 | unsigned int val; | 16 | unsigned int val; |
| 17 | 17 | ||
| @@ -23,9 +23,9 @@ static unsigned int symhash(struct hashtab *h, void *key) | |||
| 23 | return val & (h->size - 1); | 23 | return val & (h->size - 1); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | static int symcmp(struct hashtab *h, void *key1, void *key2) | 26 | static int symcmp(struct hashtab *h, const void *key1, const void *key2) |
| 27 | { | 27 | { |
| 28 | char *keyp1, *keyp2; | 28 | const char *keyp1, *keyp2; |
| 29 | 29 | ||
| 30 | keyp1 = key1; | 30 | keyp1 = key1; |
| 31 | keyp2 = key2; | 31 | keyp2 = key2; |
