1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30#include <linux/spinlock.h>
31#include <linux/rcupdate.h>
32#include <linux/gfp.h>
33#include <linux/ip.h>
34#include <linux/ipv6.h>
35#include <net/sock.h>
36#include <net/netlabel.h>
37#include <net/ip.h>
38#include <net/ipv6.h>
39
40#include "objsec.h"
41#include "security.h"
42#include "netlabel.h"
43
44
45
46
47
48
49
50
51
52
53
54
55
56static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
57 u16 family,
58 struct netlbl_lsm_secattr *secattr,
59 u32 *sid)
60{
61 int rc;
62
63 rc = security_netlbl_secattr_to_sid(secattr, sid);
64 if (rc == 0 &&
65 (secattr->flags & NETLBL_SECATTR_CACHEABLE) &&
66 (secattr->flags & NETLBL_SECATTR_CACHE))
67 netlbl_cache_add(skb, family, secattr);
68
69 return rc;
70}
71
72
73
74
75
76
77
78
79
80
81
82static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
83{
84 int rc;
85 struct sk_security_struct *sksec = sk->sk_security;
86 struct netlbl_lsm_secattr *secattr;
87
88 if (sksec->nlbl_secattr != NULL)
89 return sksec->nlbl_secattr;
90
91 secattr = netlbl_secattr_alloc(GFP_ATOMIC);
92 if (secattr == NULL)
93 return NULL;
94 rc = security_netlbl_sid_to_secattr(sksec->sid, secattr);
95 if (rc != 0) {
96 netlbl_secattr_free(secattr);
97 return NULL;
98 }
99 sksec->nlbl_secattr = secattr;
100
101 return secattr;
102}
103
104
105
106
107
108
109
110
111
112
113static struct netlbl_lsm_secattr *selinux_netlbl_sock_getattr(
114 const struct sock *sk,
115 u32 sid)
116{
117 struct sk_security_struct *sksec = sk->sk_security;
118 struct netlbl_lsm_secattr *secattr = sksec->nlbl_secattr;
119
120 if (secattr == NULL)
121 return NULL;
122
123 if ((secattr->flags & NETLBL_SECATTR_SECID) &&
124 (secattr->attr.secid == sid))
125 return secattr;
126
127 return NULL;
128}
129
130
131
132
133
134
135
136
137void selinux_netlbl_cache_invalidate(void)
138{
139 netlbl_cache_invalidate();
140}
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155void selinux_netlbl_err(struct sk_buff *skb, u16 family, int error, int gateway)
156{
157 netlbl_skbuff_err(skb, family, error, gateway);
158}
159
160
161
162
163
164
165
166
167
168void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec)
169{
170 if (sksec->nlbl_secattr != NULL)
171 netlbl_secattr_free(sksec->nlbl_secattr);
172}
173
174
175
176
177
178
179
180
181
182
183
184void selinux_netlbl_sk_security_reset(struct sk_security_struct *sksec)
185{
186 sksec->nlbl_state = NLBL_UNSET;
187}
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
203 u16 family,
204 u32 *type,
205 u32 *sid)
206{
207 int rc;
208 struct netlbl_lsm_secattr secattr;
209
210 if (!netlbl_enabled()) {
211 *sid = SECSID_NULL;
212 return 0;
213 }
214
215 netlbl_secattr_init(&secattr);
216 rc = netlbl_skbuff_getattr(skb, family, &secattr);
217 if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
218 rc = selinux_netlbl_sidlookup_cached(skb, family,
219 &secattr, sid);
220 else
221 *sid = SECSID_NULL;
222 *type = secattr.type;
223 netlbl_secattr_destroy(&secattr);
224
225 return rc;
226}
227
228
229
230
231
232
233
234
235
236
237
238
239int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
240 u16 family,
241 u32 sid)
242{
243 int rc;
244 struct netlbl_lsm_secattr secattr_storage;
245 struct netlbl_lsm_secattr *secattr = NULL;
246 struct sock *sk;
247
248
249
250 sk = skb_to_full_sk(skb);
251 if (sk != NULL) {
252 struct sk_security_struct *sksec = sk->sk_security;
253 if (sksec->nlbl_state != NLBL_REQSKB)
254 return 0;
255 secattr = selinux_netlbl_sock_getattr(sk, sid);
256 }
257 if (secattr == NULL) {
258 secattr = &secattr_storage;
259 netlbl_secattr_init(secattr);
260 rc = security_netlbl_sid_to_secattr(sid, secattr);
261 if (rc != 0)
262 goto skbuff_setsid_return;
263 }
264
265 rc = netlbl_skbuff_setattr(skb, family, secattr);
266
267skbuff_setsid_return:
268 if (secattr == &secattr_storage)
269 netlbl_secattr_destroy(secattr);
270 return rc;
271}
272
273
274
275
276
277
278
279
280
281
282
283
284int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family)
285{
286 int rc;
287 struct netlbl_lsm_secattr secattr;
288
289 if (family != PF_INET && family != PF_INET6)
290 return 0;
291
292 netlbl_secattr_init(&secattr);
293 rc = security_netlbl_sid_to_secattr(req->secid, &secattr);
294 if (rc != 0)
295 goto inet_conn_request_return;
296 rc = netlbl_req_setattr(req, &secattr);
297inet_conn_request_return:
298 netlbl_secattr_destroy(&secattr);
299 return rc;
300}
301
302
303
304
305
306
307
308
309
310
311
312void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
313{
314 struct sk_security_struct *sksec = sk->sk_security;
315
316 if (family == PF_INET)
317 sksec->nlbl_state = NLBL_LABELED;
318 else
319 sksec->nlbl_state = NLBL_UNSET;
320}
321
322
323
324
325
326
327
328
329
330
331
332int selinux_netlbl_socket_post_create(struct sock *sk, u16 family)
333{
334 int rc;
335 struct sk_security_struct *sksec = sk->sk_security;
336 struct netlbl_lsm_secattr *secattr;
337
338 if (family != PF_INET && family != PF_INET6)
339 return 0;
340
341 secattr = selinux_netlbl_sock_genattr(sk);
342 if (secattr == NULL)
343 return -ENOMEM;
344 rc = netlbl_sock_setattr(sk, family, secattr);
345 switch (rc) {
346 case 0:
347 sksec->nlbl_state = NLBL_LABELED;
348 break;
349 case -EDESTADDRREQ:
350 sksec->nlbl_state = NLBL_REQSKB;
351 rc = 0;
352 break;
353 }
354
355 return rc;
356}
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
372 struct sk_buff *skb,
373 u16 family,
374 struct common_audit_data *ad)
375{
376 int rc;
377 u32 nlbl_sid;
378 u32 perm;
379 struct netlbl_lsm_secattr secattr;
380
381 if (!netlbl_enabled())
382 return 0;
383
384 netlbl_secattr_init(&secattr);
385 rc = netlbl_skbuff_getattr(skb, family, &secattr);
386 if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
387 rc = selinux_netlbl_sidlookup_cached(skb, family,
388 &secattr, &nlbl_sid);
389 else
390 nlbl_sid = SECINITSID_UNLABELED;
391 netlbl_secattr_destroy(&secattr);
392 if (rc != 0)
393 return rc;
394
395 switch (sksec->sclass) {
396 case SECCLASS_UDP_SOCKET:
397 perm = UDP_SOCKET__RECVFROM;
398 break;
399 case SECCLASS_TCP_SOCKET:
400 perm = TCP_SOCKET__RECVFROM;
401 break;
402 default:
403 perm = RAWIP_SOCKET__RECVFROM;
404 }
405
406 rc = avc_has_perm(sksec->sid, nlbl_sid, sksec->sclass, perm, ad);
407 if (rc == 0)
408 return 0;
409
410 if (nlbl_sid != SECINITSID_UNLABELED)
411 netlbl_skbuff_err(skb, family, rc, 0);
412 return rc;
413}
414
415
416
417
418
419
420
421
422
423
424static inline int selinux_netlbl_option(int level, int optname)
425{
426 return (level == IPPROTO_IP && optname == IP_OPTIONS) ||
427 (level == IPPROTO_IPV6 && optname == IPV6_HOPOPTS);
428}
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443int selinux_netlbl_socket_setsockopt(struct socket *sock,
444 int level,
445 int optname)
446{
447 int rc = 0;
448 struct sock *sk = sock->sk;
449 struct sk_security_struct *sksec = sk->sk_security;
450 struct netlbl_lsm_secattr secattr;
451
452 if (selinux_netlbl_option(level, optname) &&
453 (sksec->nlbl_state == NLBL_LABELED ||
454 sksec->nlbl_state == NLBL_CONNLABELED)) {
455 netlbl_secattr_init(&secattr);
456 lock_sock(sk);
457
458
459
460 rc = netlbl_sock_getattr(sk, &secattr);
461 release_sock(sk);
462 if (rc == 0)
463 rc = -EACCES;
464 else if (rc == -ENOMSG)
465 rc = 0;
466 netlbl_secattr_destroy(&secattr);
467 }
468
469 return rc;
470}
471
472
473
474
475
476
477
478
479
480
481
482int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
483{
484 int rc;
485 struct sk_security_struct *sksec = sk->sk_security;
486 struct netlbl_lsm_secattr *secattr;
487
488 if (sksec->nlbl_state != NLBL_REQSKB &&
489 sksec->nlbl_state != NLBL_CONNLABELED)
490 return 0;
491
492 lock_sock(sk);
493
494
495
496
497 if (addr->sa_family == AF_UNSPEC) {
498 netlbl_sock_delattr(sk);
499 sksec->nlbl_state = NLBL_REQSKB;
500 rc = 0;
501 goto socket_connect_return;
502 }
503 secattr = selinux_netlbl_sock_genattr(sk);
504 if (secattr == NULL) {
505 rc = -ENOMEM;
506 goto socket_connect_return;
507 }
508 rc = netlbl_conn_setattr(sk, addr, secattr);
509 if (rc == 0)
510 sksec->nlbl_state = NLBL_CONNLABELED;
511
512socket_connect_return:
513 release_sock(sk);
514 return rc;
515}
516