diff options
Diffstat (limited to 'security/selinux/netif.c')
-rw-r--r-- | security/selinux/netif.c | 254 |
1 files changed, 145 insertions, 109 deletions
diff --git a/security/selinux/netif.c b/security/selinux/netif.c index e87ab948104c..ee49a7382875 100644 --- a/security/selinux/netif.c +++ b/security/selinux/netif.c | |||
@@ -7,6 +7,8 @@ | |||
7 | * Author: James Morris <jmorris@redhat.com> | 7 | * Author: James Morris <jmorris@redhat.com> |
8 | * | 8 | * |
9 | * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> | 9 | * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> |
10 | * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. | ||
11 | * Paul Moore <paul.moore@hp.com> | ||
10 | * | 12 | * |
11 | * This program is free software; you can redistribute it and/or modify | 13 | * This program is free software; you can redistribute it and/or modify |
12 | * it under the terms of the GNU General Public License version 2, | 14 | * it under the terms of the GNU General Public License version 2, |
@@ -29,14 +31,6 @@ | |||
29 | #define SEL_NETIF_HASH_SIZE 64 | 31 | #define SEL_NETIF_HASH_SIZE 64 |
30 | #define SEL_NETIF_HASH_MAX 1024 | 32 | #define SEL_NETIF_HASH_MAX 1024 |
31 | 33 | ||
32 | #undef DEBUG | ||
33 | |||
34 | #ifdef DEBUG | ||
35 | #define DEBUGP printk | ||
36 | #else | ||
37 | #define DEBUGP(format, args...) | ||
38 | #endif | ||
39 | |||
40 | struct sel_netif | 34 | struct sel_netif |
41 | { | 35 | { |
42 | struct list_head list; | 36 | struct list_head list; |
@@ -49,174 +43,217 @@ static LIST_HEAD(sel_netif_list); | |||
49 | static DEFINE_SPINLOCK(sel_netif_lock); | 43 | static DEFINE_SPINLOCK(sel_netif_lock); |
50 | static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE]; | 44 | static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE]; |
51 | 45 | ||
52 | static inline u32 sel_netif_hasfn(struct net_device *dev) | 46 | /** |
47 | * sel_netif_hashfn - Hashing function for the interface table | ||
48 | * @ifindex: the network interface | ||
49 | * | ||
50 | * Description: | ||
51 | * This is the hashing function for the network interface table, it returns the | ||
52 | * bucket number for the given interface. | ||
53 | * | ||
54 | */ | ||
55 | static inline u32 sel_netif_hashfn(int ifindex) | ||
53 | { | 56 | { |
54 | return (dev->ifindex & (SEL_NETIF_HASH_SIZE - 1)); | 57 | return (ifindex & (SEL_NETIF_HASH_SIZE - 1)); |
55 | } | 58 | } |
56 | 59 | ||
57 | /* | 60 | /** |
58 | * All of the devices should normally fit in the hash, so we optimize | 61 | * sel_netif_find - Search for an interface record |
59 | * for that case. | 62 | * @ifindex: the network interface |
63 | * | ||
64 | * Description: | ||
65 | * Search the network interface table and return the record matching @ifindex. | ||
66 | * If an entry can not be found in the table return NULL. | ||
67 | * | ||
60 | */ | 68 | */ |
61 | static inline struct sel_netif *sel_netif_find(struct net_device *dev) | 69 | static inline struct sel_netif *sel_netif_find(int ifindex) |
62 | { | 70 | { |
63 | struct list_head *pos; | 71 | int idx = sel_netif_hashfn(ifindex); |
64 | int idx = sel_netif_hasfn(dev); | 72 | struct sel_netif *netif; |
65 | 73 | ||
66 | __list_for_each_rcu(pos, &sel_netif_hash[idx]) { | 74 | list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list) |
67 | struct sel_netif *netif = list_entry(pos, | 75 | /* all of the devices should normally fit in the hash, so we |
68 | struct sel_netif, list); | 76 | * optimize for that case */ |
69 | if (likely(netif->nsec.dev == dev)) | 77 | if (likely(netif->nsec.ifindex == ifindex)) |
70 | return netif; | 78 | return netif; |
71 | } | 79 | |
72 | return NULL; | 80 | return NULL; |
73 | } | 81 | } |
74 | 82 | ||
83 | /** | ||
84 | * sel_netif_insert - Insert a new interface into the table | ||
85 | * @netif: the new interface record | ||
86 | * | ||
87 | * Description: | ||
88 | * Add a new interface record to the network interface hash table. Returns | ||
89 | * zero on success, negative values on failure. | ||
90 | * | ||
91 | */ | ||
75 | static int sel_netif_insert(struct sel_netif *netif) | 92 | static int sel_netif_insert(struct sel_netif *netif) |
76 | { | 93 | { |
77 | int idx, ret = 0; | 94 | int idx; |
78 | 95 | ||
79 | if (sel_netif_total >= SEL_NETIF_HASH_MAX) { | 96 | if (sel_netif_total >= SEL_NETIF_HASH_MAX) |
80 | ret = -ENOSPC; | 97 | return -ENOSPC; |
81 | goto out; | ||
82 | } | ||
83 | 98 | ||
84 | idx = sel_netif_hasfn(netif->nsec.dev); | 99 | idx = sel_netif_hashfn(netif->nsec.ifindex); |
85 | list_add_rcu(&netif->list, &sel_netif_hash[idx]); | 100 | list_add_rcu(&netif->list, &sel_netif_hash[idx]); |
86 | sel_netif_total++; | 101 | sel_netif_total++; |
87 | out: | 102 | |
88 | return ret; | 103 | return 0; |
89 | } | 104 | } |
90 | 105 | ||
106 | /** | ||
107 | * sel_netif_free - Frees an interface entry | ||
108 | * @p: the entry's RCU field | ||
109 | * | ||
110 | * Description: | ||
111 | * This function is designed to be used as a callback to the call_rcu() | ||
112 | * function so that memory allocated to a hash table interface entry can be | ||
113 | * released safely. | ||
114 | * | ||
115 | */ | ||
91 | static void sel_netif_free(struct rcu_head *p) | 116 | static void sel_netif_free(struct rcu_head *p) |
92 | { | 117 | { |
93 | struct sel_netif *netif = container_of(p, struct sel_netif, rcu_head); | 118 | struct sel_netif *netif = container_of(p, struct sel_netif, rcu_head); |
94 | |||
95 | DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name); | ||
96 | kfree(netif); | 119 | kfree(netif); |
97 | } | 120 | } |
98 | 121 | ||
122 | /** | ||
123 | * sel_netif_destroy - Remove an interface record from the table | ||
124 | * @netif: the existing interface record | ||
125 | * | ||
126 | * Description: | ||
127 | * Remove an existing interface record from the network interface table. | ||
128 | * | ||
129 | */ | ||
99 | static void sel_netif_destroy(struct sel_netif *netif) | 130 | static void sel_netif_destroy(struct sel_netif *netif) |
100 | { | 131 | { |
101 | DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name); | ||
102 | |||
103 | list_del_rcu(&netif->list); | 132 | list_del_rcu(&netif->list); |
104 | sel_netif_total--; | 133 | sel_netif_total--; |
105 | call_rcu(&netif->rcu_head, sel_netif_free); | 134 | call_rcu(&netif->rcu_head, sel_netif_free); |
106 | } | 135 | } |
107 | 136 | ||
108 | static struct sel_netif *sel_netif_lookup(struct net_device *dev) | 137 | /** |
138 | * sel_netif_sid_slow - Lookup the SID of a network interface using the policy | ||
139 | * @ifindex: the network interface | ||
140 | * @sid: interface SID | ||
141 | * | ||
142 | * Description: | ||
143 | * This function determines the SID of a network interface by quering the | ||
144 | * security policy. The result is added to the network interface table to | ||
145 | * speedup future queries. Returns zero on success, negative values on | ||
146 | * failure. | ||
147 | * | ||
148 | */ | ||
149 | static int sel_netif_sid_slow(int ifindex, u32 *sid) | ||
109 | { | 150 | { |
110 | int ret; | 151 | int ret; |
111 | struct sel_netif *netif, *new; | 152 | struct sel_netif *netif; |
112 | struct netif_security_struct *nsec; | 153 | struct sel_netif *new = NULL; |
154 | struct net_device *dev; | ||
113 | 155 | ||
114 | netif = sel_netif_find(dev); | 156 | /* NOTE: we always use init's network namespace since we don't |
115 | if (likely(netif != NULL)) | 157 | * currently support containers */ |
116 | goto out; | ||
117 | |||
118 | new = kzalloc(sizeof(*new), GFP_ATOMIC); | ||
119 | if (!new) { | ||
120 | netif = ERR_PTR(-ENOMEM); | ||
121 | goto out; | ||
122 | } | ||
123 | |||
124 | nsec = &new->nsec; | ||
125 | 158 | ||
126 | ret = security_netif_sid(dev->name, &nsec->if_sid, &nsec->msg_sid); | 159 | dev = dev_get_by_index(&init_net, ifindex); |
127 | if (ret < 0) { | 160 | if (dev == NULL) |
128 | kfree(new); | 161 | return -ENOENT; |
129 | netif = ERR_PTR(ret); | ||
130 | goto out; | ||
131 | } | ||
132 | 162 | ||
133 | nsec->dev = dev; | ||
134 | |||
135 | spin_lock_bh(&sel_netif_lock); | 163 | spin_lock_bh(&sel_netif_lock); |
136 | 164 | netif = sel_netif_find(ifindex); | |
137 | netif = sel_netif_find(dev); | 165 | if (netif != NULL) { |
138 | if (netif) { | 166 | *sid = netif->nsec.sid; |
139 | spin_unlock_bh(&sel_netif_lock); | 167 | ret = 0; |
140 | kfree(new); | ||
141 | goto out; | 168 | goto out; |
142 | } | 169 | } |
143 | 170 | new = kzalloc(sizeof(*new), GFP_ATOMIC); | |
144 | ret = sel_netif_insert(new); | 171 | if (new == NULL) { |
145 | spin_unlock_bh(&sel_netif_lock); | 172 | ret = -ENOMEM; |
146 | |||
147 | if (ret) { | ||
148 | kfree(new); | ||
149 | netif = ERR_PTR(ret); | ||
150 | goto out; | 173 | goto out; |
151 | } | 174 | } |
175 | ret = security_netif_sid(dev->name, &new->nsec.sid); | ||
176 | if (ret != 0) | ||
177 | goto out; | ||
178 | new->nsec.ifindex = ifindex; | ||
179 | ret = sel_netif_insert(new); | ||
180 | if (ret != 0) | ||
181 | goto out; | ||
182 | *sid = new->nsec.sid; | ||
152 | 183 | ||
153 | netif = new; | ||
154 | |||
155 | DEBUGP("new: ifindex=%u name=%s if_sid=%u msg_sid=%u\n", dev->ifindex, dev->name, | ||
156 | nsec->if_sid, nsec->msg_sid); | ||
157 | out: | 184 | out: |
158 | return netif; | 185 | spin_unlock_bh(&sel_netif_lock); |
159 | } | 186 | dev_put(dev); |
160 | 187 | if (ret != 0) | |
161 | static void sel_netif_assign_sids(u32 if_sid_in, u32 msg_sid_in, u32 *if_sid_out, u32 *msg_sid_out) | 188 | kfree(new); |
162 | { | ||
163 | if (if_sid_out) | ||
164 | *if_sid_out = if_sid_in; | ||
165 | if (msg_sid_out) | ||
166 | *msg_sid_out = msg_sid_in; | ||
167 | } | ||
168 | |||
169 | static int sel_netif_sids_slow(struct net_device *dev, u32 *if_sid, u32 *msg_sid) | ||
170 | { | ||
171 | int ret = 0; | ||
172 | u32 tmp_if_sid, tmp_msg_sid; | ||
173 | |||
174 | ret = security_netif_sid(dev->name, &tmp_if_sid, &tmp_msg_sid); | ||
175 | if (!ret) | ||
176 | sel_netif_assign_sids(tmp_if_sid, tmp_msg_sid, if_sid, msg_sid); | ||
177 | return ret; | 189 | return ret; |
178 | } | 190 | } |
179 | 191 | ||
180 | int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid) | 192 | /** |
193 | * sel_netif_sid - Lookup the SID of a network interface | ||
194 | * @ifindex: the network interface | ||
195 | * @sid: interface SID | ||
196 | * | ||
197 | * Description: | ||
198 | * This function determines the SID of a network interface using the fastest | ||
199 | * method possible. First the interface table is queried, but if an entry | ||
200 | * can't be found then the policy is queried and the result is added to the | ||
201 | * table to speedup future queries. Returns zero on success, negative values | ||
202 | * on failure. | ||
203 | * | ||
204 | */ | ||
205 | int sel_netif_sid(int ifindex, u32 *sid) | ||
181 | { | 206 | { |
182 | int ret = 0; | ||
183 | struct sel_netif *netif; | 207 | struct sel_netif *netif; |
184 | 208 | ||
185 | rcu_read_lock(); | 209 | rcu_read_lock(); |
186 | netif = sel_netif_lookup(dev); | 210 | netif = sel_netif_find(ifindex); |
187 | if (IS_ERR(netif)) { | 211 | if (likely(netif != NULL)) { |
212 | *sid = netif->nsec.sid; | ||
188 | rcu_read_unlock(); | 213 | rcu_read_unlock(); |
189 | ret = sel_netif_sids_slow(dev, if_sid, msg_sid); | 214 | return 0; |
190 | goto out; | ||
191 | } | 215 | } |
192 | sel_netif_assign_sids(netif->nsec.if_sid, netif->nsec.msg_sid, if_sid, msg_sid); | ||
193 | rcu_read_unlock(); | 216 | rcu_read_unlock(); |
194 | out: | 217 | |
195 | return ret; | 218 | return sel_netif_sid_slow(ifindex, sid); |
196 | } | 219 | } |
197 | 220 | ||
198 | static void sel_netif_kill(struct net_device *dev) | 221 | /** |
222 | * sel_netif_kill - Remove an entry from the network interface table | ||
223 | * @ifindex: the network interface | ||
224 | * | ||
225 | * Description: | ||
226 | * This function removes the entry matching @ifindex from the network interface | ||
227 | * table if it exists. | ||
228 | * | ||
229 | */ | ||
230 | static void sel_netif_kill(int ifindex) | ||
199 | { | 231 | { |
200 | struct sel_netif *netif; | 232 | struct sel_netif *netif; |
201 | 233 | ||
202 | spin_lock_bh(&sel_netif_lock); | 234 | spin_lock_bh(&sel_netif_lock); |
203 | netif = sel_netif_find(dev); | 235 | netif = sel_netif_find(ifindex); |
204 | if (netif) | 236 | if (netif) |
205 | sel_netif_destroy(netif); | 237 | sel_netif_destroy(netif); |
206 | spin_unlock_bh(&sel_netif_lock); | 238 | spin_unlock_bh(&sel_netif_lock); |
207 | } | 239 | } |
208 | 240 | ||
241 | /** | ||
242 | * sel_netif_flush - Flush the entire network interface table | ||
243 | * | ||
244 | * Description: | ||
245 | * Remove all entries from the network interface table. | ||
246 | * | ||
247 | */ | ||
209 | static void sel_netif_flush(void) | 248 | static void sel_netif_flush(void) |
210 | { | 249 | { |
211 | int idx; | 250 | int idx; |
251 | struct sel_netif *netif; | ||
212 | 252 | ||
213 | spin_lock_bh(&sel_netif_lock); | 253 | spin_lock_bh(&sel_netif_lock); |
214 | for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) { | 254 | for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) |
215 | struct sel_netif *netif; | ||
216 | |||
217 | list_for_each_entry(netif, &sel_netif_hash[idx], list) | 255 | list_for_each_entry(netif, &sel_netif_hash[idx], list) |
218 | sel_netif_destroy(netif); | 256 | sel_netif_destroy(netif); |
219 | } | ||
220 | spin_unlock_bh(&sel_netif_lock); | 257 | spin_unlock_bh(&sel_netif_lock); |
221 | } | 258 | } |
222 | 259 | ||
@@ -239,7 +276,7 @@ static int sel_netif_netdev_notifier_handler(struct notifier_block *this, | |||
239 | return NOTIFY_DONE; | 276 | return NOTIFY_DONE; |
240 | 277 | ||
241 | if (event == NETDEV_DOWN) | 278 | if (event == NETDEV_DOWN) |
242 | sel_netif_kill(dev); | 279 | sel_netif_kill(dev->ifindex); |
243 | 280 | ||
244 | return NOTIFY_DONE; | 281 | return NOTIFY_DONE; |
245 | } | 282 | } |
@@ -250,10 +287,10 @@ static struct notifier_block sel_netif_netdev_notifier = { | |||
250 | 287 | ||
251 | static __init int sel_netif_init(void) | 288 | static __init int sel_netif_init(void) |
252 | { | 289 | { |
253 | int i, err = 0; | 290 | int i, err; |
254 | 291 | ||
255 | if (!selinux_enabled) | 292 | if (!selinux_enabled) |
256 | goto out; | 293 | return 0; |
257 | 294 | ||
258 | for (i = 0; i < SEL_NETIF_HASH_SIZE; i++) | 295 | for (i = 0; i < SEL_NETIF_HASH_SIZE; i++) |
259 | INIT_LIST_HEAD(&sel_netif_hash[i]); | 296 | INIT_LIST_HEAD(&sel_netif_hash[i]); |
@@ -265,7 +302,6 @@ static __init int sel_netif_init(void) | |||
265 | if (err) | 302 | if (err) |
266 | panic("avc_add_callback() failed, error %d\n", err); | 303 | panic("avc_add_callback() failed, error %d\n", err); |
267 | 304 | ||
268 | out: | ||
269 | return err; | 305 | return err; |
270 | } | 306 | } |
271 | 307 | ||