Blame view
security/selinux/xfrm.c
11.1 KB
d28d1e080 [LSM-IPSec]: Per-... |
1 2 3 4 5 6 7 8 |
/* * NSA Security-Enhanced Linux (SELinux) security module * * This file contains the SELinux XFRM hook function implementations. * * Authors: Serge Hallyn <sergeh@us.ibm.com> * Trent Jaeger <jaegert@us.ibm.com> * |
e0d1caa7b [MLSXFRM]: Flow b... |
9 10 11 12 |
* Updated: Venkat Yekkirala <vyekkirala@TrustedCS.com> * * Granular IPSec Associations for use in MLS environments. * |
d28d1e080 [LSM-IPSec]: Per-... |
13 |
* Copyright (C) 2005 International Business Machines Corporation |
e0d1caa7b [MLSXFRM]: Flow b... |
14 |
* Copyright (C) 2006 Trusted Computer Solutions, Inc. |
d28d1e080 [LSM-IPSec]: Per-... |
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation. */ /* * USAGE: * NOTES: * 1. Make sure to enable the following options in your kernel config: * CONFIG_SECURITY=y * CONFIG_SECURITY_NETWORK=y * CONFIG_SECURITY_NETWORK_XFRM=y * CONFIG_SECURITY_SELINUX=m/y * ISSUES: * 1. Caching packets, so they are not dropped during negotiation * 2. Emulating a reasonable SO_PEERSEC across machines * 3. Testing addition of sk_policy's with security context via setsockopt */ |
d28d1e080 [LSM-IPSec]: Per-... |
34 35 36 37 |
#include <linux/kernel.h> #include <linux/init.h> #include <linux/security.h> #include <linux/types.h> |
5a0e3ad6a include cleanup: ... |
38 |
#include <linux/slab.h> |
d28d1e080 [LSM-IPSec]: Per-... |
39 40 41 42 43 44 45 |
#include <linux/ip.h> #include <linux/tcp.h> #include <linux/skbuff.h> #include <linux/xfrm.h> #include <net/xfrm.h> #include <net/checksum.h> #include <net/udp.h> |
60063497a atomic: use <linu... |
46 |
#include <linux/atomic.h> |
d28d1e080 [LSM-IPSec]: Per-... |
47 48 49 50 |
#include "avc.h" #include "objsec.h" #include "xfrm.h" |
d621d35e5 SELinux: Enable d... |
51 52 |
/* Labeled XFRM instance counter */ atomic_t selinux_xfrm_refcount = ATOMIC_INIT(0); |
d28d1e080 [LSM-IPSec]: Per-... |
53 54 |
/* |
4baabeec2 selinux: cleanup ... |
55 |
* Returns true if the context is an LSM/SELinux context. |
d28d1e080 [LSM-IPSec]: Per-... |
56 57 58 59 60 61 62 63 64 |
*/ static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx) { return (ctx && (ctx->ctx_doi == XFRM_SC_DOI_LSM) && (ctx->ctx_alg == XFRM_SC_ALG_SELINUX)); } /* |
4baabeec2 selinux: cleanup ... |
65 |
* Returns true if the xfrm contains a security blob for SELinux. |
d28d1e080 [LSM-IPSec]: Per-... |
66 67 68 69 70 71 72 |
*/ static inline int selinux_authorizable_xfrm(struct xfrm_state *x) { return selinux_authorizable_ctx(x->security); } /* |
2e5aa8660 lsm: split the xf... |
73 74 75 76 |
* Allocates a xfrm_sec_state and populates it using the supplied security * xfrm_user_sec_ctx context. */ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp, |
52a4c6404 selinux: add gfp ... |
77 78 |
struct xfrm_user_sec_ctx *uctx, gfp_t gfp) |
2e5aa8660 lsm: split the xf... |
79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
{ int rc; const struct task_security_struct *tsec = current_security(); struct xfrm_sec_ctx *ctx = NULL; u32 str_len; if (ctxp == NULL || uctx == NULL || uctx->ctx_doi != XFRM_SC_DOI_LSM || uctx->ctx_alg != XFRM_SC_ALG_SELINUX) return -EINVAL; str_len = uctx->ctx_len; if (str_len >= PAGE_SIZE) return -ENOMEM; |
52a4c6404 selinux: add gfp ... |
93 |
ctx = kmalloc(sizeof(*ctx) + str_len + 1, gfp); |
2e5aa8660 lsm: split the xf... |
94 95 96 97 98 99 100 101 |
if (!ctx) return -ENOMEM; ctx->ctx_doi = XFRM_SC_DOI_LSM; ctx->ctx_alg = XFRM_SC_ALG_SELINUX; ctx->ctx_len = str_len; memcpy(ctx->ctx_str, &uctx[1], str_len); ctx->ctx_str[str_len] = '\0'; |
52a4c6404 selinux: add gfp ... |
102 |
rc = security_context_to_sid(ctx->ctx_str, str_len, &ctx->ctx_sid, gfp); |
2e5aa8660 lsm: split the xf... |
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
if (rc) goto err; rc = avc_has_perm(tsec->sid, ctx->ctx_sid, SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL); if (rc) goto err; *ctxp = ctx; atomic_inc(&selinux_xfrm_refcount); return 0; err: kfree(ctx); return rc; } /* |
ccf17cc4b selinux: cleanup ... |
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
* Free the xfrm_sec_ctx structure. */ static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx) { if (!ctx) return; atomic_dec(&selinux_xfrm_refcount); kfree(ctx); } /* * Authorize the deletion of a labeled SA or policy rule. */ static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx) { const struct task_security_struct *tsec = current_security(); if (!ctx) return 0; return avc_has_perm(tsec->sid, ctx->ctx_sid, SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL); } /* |
4baabeec2 selinux: cleanup ... |
148 149 |
* LSM hook implementation that authorizes that a flow can use a xfrm policy * rule. |
d28d1e080 [LSM-IPSec]: Per-... |
150 |
*/ |
03e1ad7b5 LSM: Make the Lab... |
151 |
int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir) |
d28d1e080 [LSM-IPSec]: Per-... |
152 |
{ |
5b368e61c IPsec: correct se... |
153 |
int rc; |
d28d1e080 [LSM-IPSec]: Per-... |
154 |
|
96484348a selinux: cleanup ... |
155 156 157 |
/* All flows should be treated as polmatch'ing an otherwise applicable * "non-labeled" policy. This would prevent inadvertent "leaks". */ if (!ctx) |
5b368e61c IPsec: correct se... |
158 |
return 0; |
d28d1e080 [LSM-IPSec]: Per-... |
159 |
|
96484348a selinux: cleanup ... |
160 161 162 |
/* Context sid is either set to label or ANY_ASSOC */ if (!selinux_authorizable_ctx(ctx)) return -EINVAL; |
5b368e61c IPsec: correct se... |
163 |
|
96484348a selinux: cleanup ... |
164 165 166 |
rc = avc_has_perm(fl_secid, ctx->ctx_sid, SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, NULL); return (rc == -EACCES ? -ESRCH : rc); |
d28d1e080 [LSM-IPSec]: Per-... |
167 168 169 |
} /* |
e0d1caa7b [MLSXFRM]: Flow b... |
170 171 172 |
* LSM hook implementation that authorizes that a state matches * the given policy, flow combo. */ |
96484348a selinux: cleanup ... |
173 174 175 |
int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp, const struct flowi *fl) |
e0d1caa7b [MLSXFRM]: Flow b... |
176 177 |
{ u32 state_sid; |
e0d1caa7b [MLSXFRM]: Flow b... |
178 |
|
67f83cbf0 SELinux: Fix SA s... |
179 |
if (!xp->security) |
5b368e61c IPsec: correct se... |
180 181 182 183 184 185 |
if (x->security) /* unlabeled policy and labeled SA can't match */ return 0; else /* unlabeled policy and unlabeled SA match all flows */ return 1; |
5b368e61c IPsec: correct se... |
186 |
else |
67f83cbf0 SELinux: Fix SA s... |
187 188 |
if (!x->security) /* unlabeled SA and labeled policy can't match */ |
5b368e61c IPsec: correct se... |
189 |
return 0; |
67f83cbf0 SELinux: Fix SA s... |
190 191 192 193 |
else if (!selinux_authorizable_xfrm(x)) /* Not a SELinux-labeled SA */ return 0; |
5b368e61c IPsec: correct se... |
194 |
|
67f83cbf0 SELinux: Fix SA s... |
195 |
state_sid = x->security->ctx_sid; |
e0d1caa7b [MLSXFRM]: Flow b... |
196 |
|
1d28f42c1 net: Put flowi_* ... |
197 |
if (fl->flowi_secid != state_sid) |
67f83cbf0 SELinux: Fix SA s... |
198 |
return 0; |
e0d1caa7b [MLSXFRM]: Flow b... |
199 |
|
96484348a selinux: cleanup ... |
200 201 202 203 204 205 |
/* We don't need a separate SA Vs. policy polmatch check since the SA * is now of the same label as the flow and a flow Vs. policy polmatch * check had already happened in selinux_xfrm_policy_lookup() above. */ return (avc_has_perm(fl->flowi_secid, state_sid, SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, NULL) ? 0 : 1); |
e0d1caa7b [MLSXFRM]: Flow b... |
206 |
} |
817eff718 selinux: look for... |
207 |
static u32 selinux_xfrm_skb_sid_egress(struct sk_buff *skb) |
e0d1caa7b [MLSXFRM]: Flow b... |
208 |
{ |
817eff718 selinux: look for... |
209 210 211 212 213 214 215 216 |
struct dst_entry *dst = skb_dst(skb); struct xfrm_state *x; if (dst == NULL) return SECSID_NULL; x = dst->xfrm; if (x == NULL || !selinux_authorizable_xfrm(x)) return SECSID_NULL; |
e0d1caa7b [MLSXFRM]: Flow b... |
217 |
|
817eff718 selinux: look for... |
218 219 220 221 222 223 224 225 |
return x->security->ctx_sid; } static int selinux_xfrm_skb_sid_ingress(struct sk_buff *skb, u32 *sid, int ckall) { u32 sid_session = SECSID_NULL; struct sec_path *sp = skb->sp; |
e0d1caa7b [MLSXFRM]: Flow b... |
226 |
|
e0d1caa7b [MLSXFRM]: Flow b... |
227 |
if (sp) { |
e21936958 selinux: cleanup ... |
228 |
int i; |
e0d1caa7b [MLSXFRM]: Flow b... |
229 |
|
e21936958 selinux: cleanup ... |
230 |
for (i = sp->len - 1; i >= 0; i--) { |
e0d1caa7b [MLSXFRM]: Flow b... |
231 232 233 |
struct xfrm_state *x = sp->xvec[i]; if (selinux_authorizable_xfrm(x)) { struct xfrm_sec_ctx *ctx = x->security; |
e21936958 selinux: cleanup ... |
234 235 |
if (sid_session == SECSID_NULL) { sid_session = ctx->ctx_sid; |
beb8d13be [MLSXFRM]: Add fl... |
236 |
if (!ckall) |
e21936958 selinux: cleanup ... |
237 238 239 |
goto out; } else if (sid_session != ctx->ctx_sid) { *sid = SECSID_NULL; |
e0d1caa7b [MLSXFRM]: Flow b... |
240 |
return -EINVAL; |
e21936958 selinux: cleanup ... |
241 |
} |
e0d1caa7b [MLSXFRM]: Flow b... |
242 243 244 |
} } } |
e21936958 selinux: cleanup ... |
245 246 |
out: *sid = sid_session; |
e0d1caa7b [MLSXFRM]: Flow b... |
247 248 249 250 |
return 0; } /* |
817eff718 selinux: look for... |
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
* LSM hook implementation that checks and/or returns the xfrm sid for the * incoming packet. */ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) { if (skb == NULL) { *sid = SECSID_NULL; return 0; } return selinux_xfrm_skb_sid_ingress(skb, sid, ckall); } int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid) { int rc; rc = selinux_xfrm_skb_sid_ingress(skb, sid, 0); if (rc == 0 && *sid == SECSID_NULL) *sid = selinux_xfrm_skb_sid_egress(skb); return rc; } /* |
4baabeec2 selinux: cleanup ... |
275 |
* LSM hook implementation that allocs and transfers uctx spec to xfrm_policy. |
d28d1e080 [LSM-IPSec]: Per-... |
276 |
*/ |
03e1ad7b5 LSM: Make the Lab... |
277 |
int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp, |
52a4c6404 selinux: add gfp ... |
278 279 |
struct xfrm_user_sec_ctx *uctx, gfp_t gfp) |
d28d1e080 [LSM-IPSec]: Per-... |
280 |
{ |
52a4c6404 selinux: add gfp ... |
281 |
return selinux_xfrm_alloc_user(ctxp, uctx, gfp); |
d28d1e080 [LSM-IPSec]: Per-... |
282 |
} |
d28d1e080 [LSM-IPSec]: Per-... |
283 |
/* |
4baabeec2 selinux: cleanup ... |
284 285 |
* LSM hook implementation that copies security data structure from old to new * for policy cloning. |
d28d1e080 [LSM-IPSec]: Per-... |
286 |
*/ |
03e1ad7b5 LSM: Make the Lab... |
287 288 |
int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx, struct xfrm_sec_ctx **new_ctxp) |
d28d1e080 [LSM-IPSec]: Per-... |
289 |
{ |
03e1ad7b5 LSM: Make the Lab... |
290 |
struct xfrm_sec_ctx *new_ctx; |
d28d1e080 [LSM-IPSec]: Per-... |
291 |
|
ccf17cc4b selinux: cleanup ... |
292 293 |
if (!old_ctx) return 0; |
7d1db4b24 selinux: Use kmem... |
294 295 |
new_ctx = kmemdup(old_ctx, sizeof(*old_ctx) + old_ctx->ctx_len, GFP_ATOMIC); |
ccf17cc4b selinux: cleanup ... |
296 297 |
if (!new_ctx) return -ENOMEM; |
ccf17cc4b selinux: cleanup ... |
298 299 |
atomic_inc(&selinux_xfrm_refcount); *new_ctxp = new_ctx; |
d28d1e080 [LSM-IPSec]: Per-... |
300 |
|
d28d1e080 [LSM-IPSec]: Per-... |
301 302 303 304 |
return 0; } /* |
03e1ad7b5 LSM: Make the Lab... |
305 |
* LSM hook implementation that frees xfrm_sec_ctx security information. |
d28d1e080 [LSM-IPSec]: Per-... |
306 |
*/ |
03e1ad7b5 LSM: Make the Lab... |
307 |
void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx) |
d28d1e080 [LSM-IPSec]: Per-... |
308 |
{ |
ccf17cc4b selinux: cleanup ... |
309 |
selinux_xfrm_free(ctx); |
d28d1e080 [LSM-IPSec]: Per-... |
310 311 312 |
} /* |
c8c05a8ee [LSM-IPsec]: SELi... |
313 314 |
* LSM hook implementation that authorizes deletion of labeled policies. */ |
03e1ad7b5 LSM: Make the Lab... |
315 |
int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx) |
c8c05a8ee [LSM-IPsec]: SELi... |
316 |
{ |
ccf17cc4b selinux: cleanup ... |
317 |
return selinux_xfrm_delete(ctx); |
c8c05a8ee [LSM-IPsec]: SELi... |
318 319 320 |
} /* |
2e5aa8660 lsm: split the xf... |
321 322 |
* LSM hook implementation that allocates a xfrm_sec_state, populates it using * the supplied security context, and assigns it to the xfrm_state. |
d28d1e080 [LSM-IPSec]: Per-... |
323 |
*/ |
2e5aa8660 lsm: split the xf... |
324 325 |
int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx) |
d28d1e080 [LSM-IPSec]: Per-... |
326 |
{ |
52a4c6404 selinux: add gfp ... |
327 |
return selinux_xfrm_alloc_user(&x->security, uctx, GFP_KERNEL); |
2e5aa8660 lsm: split the xf... |
328 |
} |
d28d1e080 [LSM-IPSec]: Per-... |
329 |
|
2e5aa8660 lsm: split the xf... |
330 331 332 333 334 335 336 337 338 339 340 |
/* * LSM hook implementation that allocates a xfrm_sec_state and populates based * on a secid. */ int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x, struct xfrm_sec_ctx *polsec, u32 secid) { int rc; struct xfrm_sec_ctx *ctx; char *ctx_str = NULL; int str_len; |
d28d1e080 [LSM-IPSec]: Per-... |
341 |
|
2e5aa8660 lsm: split the xf... |
342 343 344 345 346 347 348 349 350 351 352 |
if (!polsec) return 0; if (secid == 0) return -EINVAL; rc = security_sid_to_context(secid, &ctx_str, &str_len); if (rc) return rc; ctx = kmalloc(sizeof(*ctx) + str_len, GFP_ATOMIC); |
0af901643 selinux: fix poss... |
353 354 355 356 |
if (!ctx) { rc = -ENOMEM; goto out; } |
2e5aa8660 lsm: split the xf... |
357 358 359 360 361 362 |
ctx->ctx_doi = XFRM_SC_DOI_LSM; ctx->ctx_alg = XFRM_SC_ALG_SELINUX; ctx->ctx_sid = secid; ctx->ctx_len = str_len; memcpy(ctx->ctx_str, ctx_str, str_len); |
2e5aa8660 lsm: split the xf... |
363 364 365 |
x->security = ctx; atomic_inc(&selinux_xfrm_refcount); |
0af901643 selinux: fix poss... |
366 367 368 |
out: kfree(ctx_str); return rc; |
d28d1e080 [LSM-IPSec]: Per-... |
369 370 371 372 373 374 375 |
} /* * LSM hook implementation that frees xfrm_state security information. */ void selinux_xfrm_state_free(struct xfrm_state *x) { |
ccf17cc4b selinux: cleanup ... |
376 |
selinux_xfrm_free(x->security); |
d28d1e080 [LSM-IPSec]: Per-... |
377 |
} |
4baabeec2 selinux: cleanup ... |
378 379 380 |
/* * LSM hook implementation that authorizes deletion of labeled SAs. */ |
c8c05a8ee [LSM-IPsec]: SELi... |
381 382 |
int selinux_xfrm_state_delete(struct xfrm_state *x) { |
ccf17cc4b selinux: cleanup ... |
383 |
return selinux_xfrm_delete(x->security); |
c8c05a8ee [LSM-IPsec]: SELi... |
384 |
} |
2c7946a7b [SECURITY]: TCP/U... |
385 |
/* |
d28d1e080 [LSM-IPSec]: Per-... |
386 387 388 389 390 391 |
* LSM hook that controls access to unlabelled packets. If * a xfrm_state is authorizable (defined by macro) then it was * already authorized by the IPSec process. If not, then * we need to check for unlabelled access since this may not have * gone thru the IPSec process. */ |
eef9b4162 selinux: cleanup ... |
392 393 |
int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb, struct common_audit_data *ad) |
d28d1e080 [LSM-IPSec]: Per-... |
394 |
{ |
eef9b4162 selinux: cleanup ... |
395 396 397 |
int i; struct sec_path *sp = skb->sp; u32 peer_sid = SECINITSID_UNLABELED; |
d28d1e080 [LSM-IPSec]: Per-... |
398 399 |
if (sp) { |
d28d1e080 [LSM-IPSec]: Per-... |
400 |
for (i = 0; i < sp->len; i++) { |
676447263 [SELINUX] Fix bui... |
401 |
struct xfrm_state *x = sp->xvec[i]; |
d28d1e080 [LSM-IPSec]: Per-... |
402 |
|
e0d1caa7b [MLSXFRM]: Flow b... |
403 404 |
if (x && selinux_authorizable_xfrm(x)) { struct xfrm_sec_ctx *ctx = x->security; |
eef9b4162 selinux: cleanup ... |
405 |
peer_sid = ctx->ctx_sid; |
e0d1caa7b [MLSXFRM]: Flow b... |
406 407 |
break; } |
d28d1e080 [LSM-IPSec]: Per-... |
408 409 |
} } |
eef9b4162 selinux: cleanup ... |
410 411 412 413 414 |
/* This check even when there's no association involved is intended, * according to Trent Jaeger, to make sure a process can't engage in * non-IPsec communication unless explicitly allowed by policy. */ return avc_has_perm(sk_sid, peer_sid, SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, ad); |
d28d1e080 [LSM-IPSec]: Per-... |
415 416 417 418 419 420 421 |
} /* * POSTROUTE_LAST hook's XFRM processing: * If we have no security association, then we need to determine * whether the socket is allowed to send to an unlabelled destination. * If we do have a authorizable security association, then it has already been |
67f83cbf0 SELinux: Fix SA s... |
422 |
* checked in the selinux_xfrm_state_pol_flow_match hook above. |
d28d1e080 [LSM-IPSec]: Per-... |
423 |
*/ |
eef9b4162 selinux: cleanup ... |
424 425 |
int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb, struct common_audit_data *ad, u8 proto) |
d28d1e080 [LSM-IPSec]: Per-... |
426 427 |
{ struct dst_entry *dst; |
d28d1e080 [LSM-IPSec]: Per-... |
428 |
|
67f83cbf0 SELinux: Fix SA s... |
429 430 431 432 |
switch (proto) { case IPPROTO_AH: case IPPROTO_ESP: case IPPROTO_COMP: |
eef9b4162 selinux: cleanup ... |
433 434 435 436 |
/* We should have already seen this packet once before it * underwent xfrm(s). No need to subject it to the unlabeled * check. */ return 0; |
67f83cbf0 SELinux: Fix SA s... |
437 438 439 |
default: break; } |
eef9b4162 selinux: cleanup ... |
440 441 442 |
dst = skb_dst(skb); if (dst) { struct dst_entry *iter; |
67f83cbf0 SELinux: Fix SA s... |
443 |
|
eef9b4162 selinux: cleanup ... |
444 445 446 447 448 449 450 451 452 453 454 455 456 |
for (iter = dst; iter != NULL; iter = iter->child) { struct xfrm_state *x = iter->xfrm; if (x && selinux_authorizable_xfrm(x)) return 0; } } /* This check even when there's no association involved is intended, * according to Trent Jaeger, to make sure a process can't engage in * non-IPsec communication unless explicitly allowed by policy. */ return avc_has_perm(sk_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, ad); |
d28d1e080 [LSM-IPSec]: Per-... |
457 |
} |