diff options
Diffstat (limited to 'security/selinux/netif.c')
-rw-r--r-- | security/selinux/netif.c | 263 |
1 files changed, 154 insertions, 109 deletions
diff --git a/security/selinux/netif.c b/security/selinux/netif.c index e87ab948104..013d3117a86 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,226 @@ 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; |
113 | 154 | struct net_device *dev; | |
114 | netif = sel_netif_find(dev); | 155 | |
115 | if (likely(netif != NULL)) | 156 | /* NOTE: we always use init's network namespace since we don't |
116 | goto out; | 157 | * currently support containers */ |
117 | 158 | ||
118 | new = kzalloc(sizeof(*new), GFP_ATOMIC); | 159 | dev = dev_get_by_index(&init_net, ifindex); |
119 | if (!new) { | 160 | if (unlikely(dev == NULL)) { |
120 | netif = ERR_PTR(-ENOMEM); | 161 | printk(KERN_WARNING |
121 | goto out; | 162 | "SELinux: failure in sel_netif_sid_slow()," |
163 | " invalid network interface (%d)\n", ifindex); | ||
164 | return -ENOENT; | ||
122 | } | 165 | } |
123 | |||
124 | nsec = &new->nsec; | ||
125 | 166 | ||
126 | ret = security_netif_sid(dev->name, &nsec->if_sid, &nsec->msg_sid); | 167 | spin_lock_bh(&sel_netif_lock); |
127 | if (ret < 0) { | 168 | netif = sel_netif_find(ifindex); |
128 | kfree(new); | 169 | if (netif != NULL) { |
129 | netif = ERR_PTR(ret); | 170 | *sid = netif->nsec.sid; |
171 | ret = 0; | ||
130 | goto out; | 172 | goto out; |
131 | } | 173 | } |
132 | 174 | new = kzalloc(sizeof(*new), GFP_ATOMIC); | |
133 | nsec->dev = dev; | 175 | if (new == NULL) { |
134 | 176 | ret = -ENOMEM; | |
135 | spin_lock_bh(&sel_netif_lock); | ||
136 | |||
137 | netif = sel_netif_find(dev); | ||
138 | if (netif) { | ||
139 | spin_unlock_bh(&sel_netif_lock); | ||
140 | kfree(new); | ||
141 | goto out; | 177 | goto out; |
142 | } | 178 | } |
143 | 179 | ret = security_netif_sid(dev->name, &new->nsec.sid); | |
180 | if (ret != 0) | ||
181 | goto out; | ||
182 | new->nsec.ifindex = ifindex; | ||
144 | ret = sel_netif_insert(new); | 183 | ret = sel_netif_insert(new); |
145 | spin_unlock_bh(&sel_netif_lock); | 184 | if (ret != 0) |
146 | |||
147 | if (ret) { | ||
148 | kfree(new); | ||
149 | netif = ERR_PTR(ret); | ||
150 | goto out; | 185 | goto out; |
151 | } | 186 | *sid = new->nsec.sid; |
152 | 187 | ||
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: | 188 | out: |
158 | return netif; | 189 | spin_unlock_bh(&sel_netif_lock); |
159 | } | 190 | dev_put(dev); |
160 | 191 | if (unlikely(ret)) { | |
161 | static void sel_netif_assign_sids(u32 if_sid_in, u32 msg_sid_in, u32 *if_sid_out, u32 *msg_sid_out) | 192 | printk(KERN_WARNING |
162 | { | 193 | "SELinux: failure in sel_netif_sid_slow()," |
163 | if (if_sid_out) | 194 | " unable to determine network interface label (%d)\n", |
164 | *if_sid_out = if_sid_in; | 195 | ifindex); |
165 | if (msg_sid_out) | 196 | kfree(new); |
166 | *msg_sid_out = msg_sid_in; | 197 | } |
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; | 198 | return ret; |
178 | } | 199 | } |
179 | 200 | ||
180 | int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid) | 201 | /** |
202 | * sel_netif_sid - Lookup the SID of a network interface | ||
203 | * @ifindex: the network interface | ||
204 | * @sid: interface SID | ||
205 | * | ||
206 | * Description: | ||
207 | * This function determines the SID of a network interface using the fastest | ||
208 | * method possible. First the interface table is queried, but if an entry | ||
209 | * can't be found then the policy is queried and the result is added to the | ||
210 | * table to speedup future queries. Returns zero on success, negative values | ||
211 | * on failure. | ||
212 | * | ||
213 | */ | ||
214 | int sel_netif_sid(int ifindex, u32 *sid) | ||
181 | { | 215 | { |
182 | int ret = 0; | ||
183 | struct sel_netif *netif; | 216 | struct sel_netif *netif; |
184 | 217 | ||
185 | rcu_read_lock(); | 218 | rcu_read_lock(); |
186 | netif = sel_netif_lookup(dev); | 219 | netif = sel_netif_find(ifindex); |
187 | if (IS_ERR(netif)) { | 220 | if (likely(netif != NULL)) { |
221 | *sid = netif->nsec.sid; | ||
188 | rcu_read_unlock(); | 222 | rcu_read_unlock(); |
189 | ret = sel_netif_sids_slow(dev, if_sid, msg_sid); | 223 | return 0; |
190 | goto out; | ||
191 | } | 224 | } |
192 | sel_netif_assign_sids(netif->nsec.if_sid, netif->nsec.msg_sid, if_sid, msg_sid); | ||
193 | rcu_read_unlock(); | 225 | rcu_read_unlock(); |
194 | out: | 226 | |
195 | return ret; | 227 | return sel_netif_sid_slow(ifindex, sid); |
196 | } | 228 | } |
197 | 229 | ||
198 | static void sel_netif_kill(struct net_device *dev) | 230 | /** |
231 | * sel_netif_kill - Remove an entry from the network interface table | ||
232 | * @ifindex: the network interface | ||
233 | * | ||
234 | * Description: | ||
235 | * This function removes the entry matching @ifindex from the network interface | ||
236 | * table if it exists. | ||
237 | * | ||
238 | */ | ||
239 | static void sel_netif_kill(int ifindex) | ||
199 | { | 240 | { |
200 | struct sel_netif *netif; | 241 | struct sel_netif *netif; |
201 | 242 | ||
202 | spin_lock_bh(&sel_netif_lock); | 243 | spin_lock_bh(&sel_netif_lock); |
203 | netif = sel_netif_find(dev); | 244 | netif = sel_netif_find(ifindex); |
204 | if (netif) | 245 | if (netif) |
205 | sel_netif_destroy(netif); | 246 | sel_netif_destroy(netif); |
206 | spin_unlock_bh(&sel_netif_lock); | 247 | spin_unlock_bh(&sel_netif_lock); |
207 | } | 248 | } |
208 | 249 | ||
250 | /** | ||
251 | * sel_netif_flush - Flush the entire network interface table | ||
252 | * | ||
253 | * Description: | ||
254 | * Remove all entries from the network interface table. | ||
255 | * | ||
256 | */ | ||
209 | static void sel_netif_flush(void) | 257 | static void sel_netif_flush(void) |
210 | { | 258 | { |
211 | int idx; | 259 | int idx; |
260 | struct sel_netif *netif; | ||
212 | 261 | ||
213 | spin_lock_bh(&sel_netif_lock); | 262 | spin_lock_bh(&sel_netif_lock); |
214 | for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) { | 263 | 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) | 264 | list_for_each_entry(netif, &sel_netif_hash[idx], list) |
218 | sel_netif_destroy(netif); | 265 | sel_netif_destroy(netif); |
219 | } | ||
220 | spin_unlock_bh(&sel_netif_lock); | 266 | spin_unlock_bh(&sel_netif_lock); |
221 | } | 267 | } |
222 | 268 | ||
@@ -239,7 +285,7 @@ static int sel_netif_netdev_notifier_handler(struct notifier_block *this, | |||
239 | return NOTIFY_DONE; | 285 | return NOTIFY_DONE; |
240 | 286 | ||
241 | if (event == NETDEV_DOWN) | 287 | if (event == NETDEV_DOWN) |
242 | sel_netif_kill(dev); | 288 | sel_netif_kill(dev->ifindex); |
243 | 289 | ||
244 | return NOTIFY_DONE; | 290 | return NOTIFY_DONE; |
245 | } | 291 | } |
@@ -250,10 +296,10 @@ static struct notifier_block sel_netif_netdev_notifier = { | |||
250 | 296 | ||
251 | static __init int sel_netif_init(void) | 297 | static __init int sel_netif_init(void) |
252 | { | 298 | { |
253 | int i, err = 0; | 299 | int i, err; |
254 | 300 | ||
255 | if (!selinux_enabled) | 301 | if (!selinux_enabled) |
256 | goto out; | 302 | return 0; |
257 | 303 | ||
258 | for (i = 0; i < SEL_NETIF_HASH_SIZE; i++) | 304 | for (i = 0; i < SEL_NETIF_HASH_SIZE; i++) |
259 | INIT_LIST_HEAD(&sel_netif_hash[i]); | 305 | INIT_LIST_HEAD(&sel_netif_hash[i]); |
@@ -265,7 +311,6 @@ static __init int sel_netif_init(void) | |||
265 | if (err) | 311 | if (err) |
266 | panic("avc_add_callback() failed, error %d\n", err); | 312 | panic("avc_add_callback() failed, error %d\n", err); |
267 | 313 | ||
268 | out: | ||
269 | return err; | 314 | return err; |
270 | } | 315 | } |
271 | 316 | ||