diff options
Diffstat (limited to 'security/selinux/xfrm.c')
-rw-r--r-- | security/selinux/xfrm.c | 216 |
1 files changed, 181 insertions, 35 deletions
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index 6c985ced8102..3e742b850af6 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c | |||
@@ -6,7 +6,12 @@ | |||
6 | * Authors: Serge Hallyn <sergeh@us.ibm.com> | 6 | * Authors: Serge Hallyn <sergeh@us.ibm.com> |
7 | * Trent Jaeger <jaegert@us.ibm.com> | 7 | * Trent Jaeger <jaegert@us.ibm.com> |
8 | * | 8 | * |
9 | * Updated: Venkat Yekkirala <vyekkirala@TrustedCS.com> | ||
10 | * | ||
11 | * Granular IPSec Associations for use in MLS environments. | ||
12 | * | ||
9 | * Copyright (C) 2005 International Business Machines Corporation | 13 | * Copyright (C) 2005 International Business Machines Corporation |
14 | * Copyright (C) 2006 Trusted Computer Solutions, Inc. | ||
10 | * | 15 | * |
11 | * This program is free software; you can redistribute it and/or modify | 16 | * 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, | 17 | * it under the terms of the GNU General Public License version 2, |
@@ -67,10 +72,10 @@ static inline int selinux_authorizable_xfrm(struct xfrm_state *x) | |||
67 | } | 72 | } |
68 | 73 | ||
69 | /* | 74 | /* |
70 | * LSM hook implementation that authorizes that a socket can be used | 75 | * LSM hook implementation that authorizes that a flow can use |
71 | * with the corresponding xfrm_sec_ctx and direction. | 76 | * a xfrm policy rule. |
72 | */ | 77 | */ |
73 | int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir) | 78 | int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir) |
74 | { | 79 | { |
75 | int rc = 0; | 80 | int rc = 0; |
76 | u32 sel_sid = SECINITSID_UNLABELED; | 81 | u32 sel_sid = SECINITSID_UNLABELED; |
@@ -84,27 +89,130 @@ int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir) | |||
84 | sel_sid = ctx->ctx_sid; | 89 | sel_sid = ctx->ctx_sid; |
85 | } | 90 | } |
86 | 91 | ||
87 | rc = avc_has_perm(sk_sid, sel_sid, SECCLASS_ASSOCIATION, | 92 | rc = avc_has_perm(fl_secid, sel_sid, SECCLASS_ASSOCIATION, |
88 | ((dir == FLOW_DIR_IN) ? ASSOCIATION__RECVFROM : | 93 | ASSOCIATION__POLMATCH, |
89 | ((dir == FLOW_DIR_OUT) ? ASSOCIATION__SENDTO : | ||
90 | (ASSOCIATION__SENDTO | ASSOCIATION__RECVFROM))), | ||
91 | NULL); | 94 | NULL); |
92 | 95 | ||
93 | return rc; | 96 | return rc; |
94 | } | 97 | } |
95 | 98 | ||
96 | /* | 99 | /* |
100 | * LSM hook implementation that authorizes that a state matches | ||
101 | * the given policy, flow combo. | ||
102 | */ | ||
103 | |||
104 | int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp, | ||
105 | struct flowi *fl) | ||
106 | { | ||
107 | u32 state_sid; | ||
108 | u32 pol_sid; | ||
109 | int err; | ||
110 | |||
111 | if (x->security) | ||
112 | state_sid = x->security->ctx_sid; | ||
113 | else | ||
114 | state_sid = SECINITSID_UNLABELED; | ||
115 | |||
116 | if (xp->security) | ||
117 | pol_sid = xp->security->ctx_sid; | ||
118 | else | ||
119 | pol_sid = SECINITSID_UNLABELED; | ||
120 | |||
121 | err = avc_has_perm(state_sid, pol_sid, SECCLASS_ASSOCIATION, | ||
122 | ASSOCIATION__POLMATCH, | ||
123 | NULL); | ||
124 | |||
125 | if (err) | ||
126 | return 0; | ||
127 | |||
128 | return selinux_xfrm_flow_state_match(fl, x); | ||
129 | } | ||
130 | |||
131 | /* | ||
132 | * LSM hook implementation that authorizes that a particular outgoing flow | ||
133 | * can use a given security association. | ||
134 | */ | ||
135 | |||
136 | int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm) | ||
137 | { | ||
138 | int rc = 0; | ||
139 | u32 sel_sid = SECINITSID_UNLABELED; | ||
140 | struct xfrm_sec_ctx *ctx; | ||
141 | |||
142 | /* Context sid is either set to label or ANY_ASSOC */ | ||
143 | if ((ctx = xfrm->security)) { | ||
144 | if (!selinux_authorizable_ctx(ctx)) | ||
145 | return 0; | ||
146 | |||
147 | sel_sid = ctx->ctx_sid; | ||
148 | } | ||
149 | |||
150 | rc = avc_has_perm(fl->secid, sel_sid, SECCLASS_ASSOCIATION, | ||
151 | ASSOCIATION__SENDTO, | ||
152 | NULL)? 0:1; | ||
153 | |||
154 | return rc; | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * LSM hook implementation that determines the sid for the session. | ||
159 | */ | ||
160 | |||
161 | int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) | ||
162 | { | ||
163 | struct sec_path *sp; | ||
164 | |||
165 | *sid = SECSID_NULL; | ||
166 | |||
167 | if (skb == NULL) | ||
168 | return 0; | ||
169 | |||
170 | sp = skb->sp; | ||
171 | if (sp) { | ||
172 | int i, sid_set = 0; | ||
173 | |||
174 | for (i = sp->len-1; i >= 0; i--) { | ||
175 | struct xfrm_state *x = sp->xvec[i]; | ||
176 | if (selinux_authorizable_xfrm(x)) { | ||
177 | struct xfrm_sec_ctx *ctx = x->security; | ||
178 | |||
179 | if (!sid_set) { | ||
180 | *sid = ctx->ctx_sid; | ||
181 | sid_set = 1; | ||
182 | |||
183 | if (!ckall) | ||
184 | break; | ||
185 | } | ||
186 | else if (*sid != ctx->ctx_sid) | ||
187 | return -EINVAL; | ||
188 | } | ||
189 | } | ||
190 | } | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | /* | ||
97 | * Security blob allocation for xfrm_policy and xfrm_state | 196 | * Security blob allocation for xfrm_policy and xfrm_state |
98 | * CTX does not have a meaningful value on input | 197 | * CTX does not have a meaningful value on input |
99 | */ | 198 | */ |
100 | static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, struct xfrm_user_sec_ctx *uctx) | 199 | static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, |
200 | struct xfrm_user_sec_ctx *uctx, struct xfrm_sec_ctx *pol, u32 sid) | ||
101 | { | 201 | { |
102 | int rc = 0; | 202 | int rc = 0; |
103 | struct task_security_struct *tsec = current->security; | 203 | struct task_security_struct *tsec = current->security; |
104 | struct xfrm_sec_ctx *ctx; | 204 | struct xfrm_sec_ctx *ctx = NULL; |
205 | char *ctx_str = NULL; | ||
206 | u32 str_len; | ||
207 | u32 ctx_sid; | ||
208 | |||
209 | BUG_ON(uctx && pol); | ||
210 | |||
211 | if (!uctx) | ||
212 | goto not_from_user; | ||
105 | 213 | ||
106 | BUG_ON(!uctx); | 214 | if (uctx->ctx_doi != XFRM_SC_ALG_SELINUX) |
107 | BUG_ON(uctx->ctx_doi != XFRM_SC_ALG_SELINUX); | 215 | return -EINVAL; |
108 | 216 | ||
109 | if (uctx->ctx_len >= PAGE_SIZE) | 217 | if (uctx->ctx_len >= PAGE_SIZE) |
110 | return -ENOMEM; | 218 | return -ENOMEM; |
@@ -141,9 +249,43 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, struct xfrm_us | |||
141 | 249 | ||
142 | return rc; | 250 | return rc; |
143 | 251 | ||
252 | not_from_user: | ||
253 | if (pol) { | ||
254 | rc = security_sid_mls_copy(pol->ctx_sid, sid, &ctx_sid); | ||
255 | if (rc) | ||
256 | goto out; | ||
257 | } | ||
258 | else | ||
259 | ctx_sid = sid; | ||
260 | |||
261 | rc = security_sid_to_context(ctx_sid, &ctx_str, &str_len); | ||
262 | if (rc) | ||
263 | goto out; | ||
264 | |||
265 | *ctxp = ctx = kmalloc(sizeof(*ctx) + | ||
266 | str_len, | ||
267 | GFP_ATOMIC); | ||
268 | |||
269 | if (!ctx) { | ||
270 | rc = -ENOMEM; | ||
271 | goto out; | ||
272 | } | ||
273 | |||
274 | ctx->ctx_doi = XFRM_SC_DOI_LSM; | ||
275 | ctx->ctx_alg = XFRM_SC_ALG_SELINUX; | ||
276 | ctx->ctx_sid = ctx_sid; | ||
277 | ctx->ctx_len = str_len; | ||
278 | memcpy(ctx->ctx_str, | ||
279 | ctx_str, | ||
280 | str_len); | ||
281 | |||
282 | goto out2; | ||
283 | |||
144 | out: | 284 | out: |
145 | *ctxp = NULL; | 285 | *ctxp = NULL; |
146 | kfree(ctx); | 286 | kfree(ctx); |
287 | out2: | ||
288 | kfree(ctx_str); | ||
147 | return rc; | 289 | return rc; |
148 | } | 290 | } |
149 | 291 | ||
@@ -151,13 +293,23 @@ out: | |||
151 | * LSM hook implementation that allocs and transfers uctx spec to | 293 | * LSM hook implementation that allocs and transfers uctx spec to |
152 | * xfrm_policy. | 294 | * xfrm_policy. |
153 | */ | 295 | */ |
154 | int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *uctx) | 296 | int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, |
297 | struct xfrm_user_sec_ctx *uctx, struct sock *sk) | ||
155 | { | 298 | { |
156 | int err; | 299 | int err; |
300 | u32 sid; | ||
157 | 301 | ||
158 | BUG_ON(!xp); | 302 | BUG_ON(!xp); |
303 | BUG_ON(uctx && sk); | ||
159 | 304 | ||
160 | err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx); | 305 | if (sk) { |
306 | struct sk_security_struct *ssec = sk->sk_security; | ||
307 | sid = ssec->sid; | ||
308 | } | ||
309 | else | ||
310 | sid = SECSID_NULL; | ||
311 | |||
312 | err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx, NULL, sid); | ||
161 | return err; | 313 | return err; |
162 | } | 314 | } |
163 | 315 | ||
@@ -217,13 +369,14 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp) | |||
217 | * LSM hook implementation that allocs and transfers sec_ctx spec to | 369 | * LSM hook implementation that allocs and transfers sec_ctx spec to |
218 | * xfrm_state. | 370 | * xfrm_state. |
219 | */ | 371 | */ |
220 | int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx) | 372 | int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx, |
373 | struct xfrm_sec_ctx *pol, u32 secid) | ||
221 | { | 374 | { |
222 | int err; | 375 | int err; |
223 | 376 | ||
224 | BUG_ON(!x); | 377 | BUG_ON(!x); |
225 | 378 | ||
226 | err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx); | 379 | err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, pol, secid); |
227 | return err; | 380 | return err; |
228 | } | 381 | } |
229 | 382 | ||
@@ -329,38 +482,30 @@ int selinux_xfrm_state_delete(struct xfrm_state *x) | |||
329 | * we need to check for unlabelled access since this may not have | 482 | * we need to check for unlabelled access since this may not have |
330 | * gone thru the IPSec process. | 483 | * gone thru the IPSec process. |
331 | */ | 484 | */ |
332 | int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb) | 485 | int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb, |
486 | struct avc_audit_data *ad) | ||
333 | { | 487 | { |
334 | int i, rc = 0; | 488 | int i, rc = 0; |
335 | struct sec_path *sp; | 489 | struct sec_path *sp; |
490 | u32 sel_sid = SECINITSID_UNLABELED; | ||
336 | 491 | ||
337 | sp = skb->sp; | 492 | sp = skb->sp; |
338 | 493 | ||
339 | if (sp) { | 494 | if (sp) { |
340 | /* | ||
341 | * __xfrm_policy_check does not approve unless xfrm_policy_ok | ||
342 | * says that spi's match for policy and the socket. | ||
343 | * | ||
344 | * Only need to verify the existence of an authorizable sp. | ||
345 | */ | ||
346 | for (i = 0; i < sp->len; i++) { | 495 | for (i = 0; i < sp->len; i++) { |
347 | struct xfrm_state *x = sp->xvec[i]; | 496 | struct xfrm_state *x = sp->xvec[i]; |
348 | 497 | ||
349 | if (x && selinux_authorizable_xfrm(x)) | 498 | if (x && selinux_authorizable_xfrm(x)) { |
350 | goto accept; | 499 | struct xfrm_sec_ctx *ctx = x->security; |
500 | sel_sid = ctx->ctx_sid; | ||
501 | break; | ||
502 | } | ||
351 | } | 503 | } |
352 | } | 504 | } |
353 | 505 | ||
354 | /* check SELinux sock for unlabelled access */ | 506 | rc = avc_has_perm(isec_sid, sel_sid, SECCLASS_ASSOCIATION, |
355 | rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION, | 507 | ASSOCIATION__RECVFROM, ad); |
356 | ASSOCIATION__RECVFROM, NULL); | ||
357 | if (rc) | ||
358 | goto drop; | ||
359 | |||
360 | accept: | ||
361 | return 0; | ||
362 | 508 | ||
363 | drop: | ||
364 | return rc; | 509 | return rc; |
365 | } | 510 | } |
366 | 511 | ||
@@ -371,7 +516,8 @@ drop: | |||
371 | * If we do have a authorizable security association, then it has already been | 516 | * If we do have a authorizable security association, then it has already been |
372 | * checked in xfrm_policy_lookup hook. | 517 | * checked in xfrm_policy_lookup hook. |
373 | */ | 518 | */ |
374 | int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb) | 519 | int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, |
520 | struct avc_audit_data *ad) | ||
375 | { | 521 | { |
376 | struct dst_entry *dst; | 522 | struct dst_entry *dst; |
377 | int rc = 0; | 523 | int rc = 0; |
@@ -391,7 +537,7 @@ int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb) | |||
391 | } | 537 | } |
392 | 538 | ||
393 | rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION, | 539 | rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION, |
394 | ASSOCIATION__SENDTO, NULL); | 540 | ASSOCIATION__SENDTO, ad); |
395 | out: | 541 | out: |
396 | return rc; | 542 | return rc; |
397 | } | 543 | } |