Projects STRLCPY ebpfguard Commits d5cd64e5
🤬
  • ebpf: Refactor `socket_connect` function

    Split it into multiple functions, make part of them generic, provide
    `IpAddrs` trait.
  • Loading...
  • Michal Rostecki committed with vadorovsky 1 year ago
    d5cd64e5
    1 parent 8f97c919
  • ■ ■ ■ ■ ■
    guardctl/src/policy/socket_connect.rs
    skipped 5 lines
    6 6  use aya::{maps::HashMap, Bpf};
    7 7  use cli_table::{Cell, Style, Table, TableStruct};
    8 8  use guardity::policy::engine::INODE_WILDCARD;
     9 +use guardity_common::IpAddrs;
    9 10   
    10 11  enum Addresses {
    11 12   All,
    skipped 128 lines
  • ■ ■ ■ ■ ■
    guardity-common/src/lib.rs
    skipped 190 lines
    191 191   }
    192 192  }
    193 193   
     194 +pub trait IpAddrs<T, const U: usize> {
     195 + fn all(&self) -> bool;
     196 + fn addrs(&self) -> [T; U];
     197 +}
     198 + 
    194 199  #[repr(C)]
    195 200  #[derive(Copy, Clone)]
    196 201  pub struct Ipv4Addrs {
    skipped 10 lines
    207 212   addrs: [0; MAX_IPV4ADDRS],
    208 213   }
    209 214   }
     215 +}
    210 216   
     217 +impl IpAddrs<u32, MAX_IPV4ADDRS> for Ipv4Addrs {
    211 218   #[inline(always)]
    212  - pub fn all(&self) -> bool {
     219 + fn all(&self) -> bool {
    213 220   self.addrs[0] == 0
    214 221   }
     222 + 
     223 + #[inline(always)]
     224 + fn addrs(&self) -> [u32; MAX_IPV4ADDRS] {
     225 + self.addrs
     226 + }
    215 227  }
    216 228   
    217 229  #[repr(C)]
    skipped 12 lines
    230 242   addrs: [[0; 16]; MAX_IPV4ADDRS],
    231 243   }
    232 244   }
     245 +}
    233 246   
     247 +impl IpAddrs<[u8; 16], MAX_IPV6ADDRS> for Ipv6Addrs {
    234 248   #[inline(always)]
    235  - pub fn all(&self) -> bool {
     249 + fn all(&self) -> bool {
    236 250   self.addrs[0] == [0; 16]
     251 + }
     252 + 
     253 + #[inline(always)]
     254 + fn addrs(&self) -> [[u8; 16]; MAX_IPV6ADDRS] {
     255 + self.addrs
    237 256   }
    238 257  }
    239 258   
    skipped 14 lines
  • ■ ■ ■ ■
    guardity-ebpf/src/main.rs
    skipped 39 lines
    40 40  #[lsm(name = "socket_connect")]
    41 41  pub fn prog_socket_connect(ctx: LsmContext) -> i32 {
    42 42   match socket_connect(ctx) {
    43  - Ok(ret) => ret,
     43 + Ok(ret) => ret.into(),
    44 44   Err(_) => 0,
    45 45   }
    46 46  }
    skipped 6 lines
  • ■ ■ ■ ■ ■ ■
    guardity-ebpf/src/socket_connect.rs
    1  -use aya_bpf::{cty::c_long, helpers::bpf_probe_read_kernel, programs::LsmContext, BpfContext};
    2  -use guardity_common::{AlertSocketConnect, MAX_IPV4ADDRS, MAX_IPV6ADDRS};
     1 +use aya_bpf::{
     2 + cty::c_long, helpers::bpf_probe_read_kernel, maps::HashMap, programs::LsmContext, BpfContext,
     3 +};
     4 +use guardity_common::{AlertSocketConnect, IpAddrs, Ipv4Addrs, Ipv6Addrs};
    3 5   
    4 6  use crate::{
    5 7   binprm::current_binprm_inode,
    skipped 3 lines
    9 11   DENIED_SOCKET_CONNECT_V4, DENIED_SOCKET_CONNECT_V6,
    10 12   },
    11 13   vmlinux::{sockaddr, sockaddr_in, sockaddr_in6},
     14 + Action, Mode,
    12 15  };
    13 16   
    14 17  /// Inspects the context of `socket_connect` LSM hook and decides whether to
    15 18  /// allow or deny the operation based on the state of the
    16 19  /// `ALLOWED_SOCKET_CONNECT_V4`/`ALLOWED_SOCKET_CONNECT_V6` and
    17 20  /// `DENIED_SOCKET_CONNECT_V4`/`DENIED_SOCKET_CONNECT_V6` maps.
    18  -pub fn socket_connect(ctx: LsmContext) -> Result<i32, c_long> {
     21 +///
     22 +/// # Example
     23 +///
     24 +/// ```rust
     25 +/// use aya_bpf::{macros::lsm, programs::LsmContext};
     26 +/// use guardity_ebpf::socket_connect;
     27 +///
     28 +/// #[lsm(name = "my_program")]
     29 +/// pub fn my_program(ctx: LsmContext) -> i32 {
     30 +/// match socket_connect(ctx) {
     31 +/// Ok(ret) => ret.into(),
     32 +/// Err(_) => 0,
     33 +/// }
     34 +/// }
     35 +/// ```
     36 +pub fn socket_connect(ctx: LsmContext) -> Result<Action, c_long> {
    19 37   let sockaddr: *const sockaddr = unsafe { ctx.arg(1) };
    20 38   let sa_family = unsafe { (*sockaddr).sa_family };
    21 39   
    22 40   match sa_family {
    23 41   AF_INET => socket_connect_v4(ctx, sockaddr),
    24 42   AF_INET6 => socket_connect_v6(ctx, sockaddr),
    25  - _ => Ok(0),
     43 + _ => Ok(Action::Allow),
    26 44   }
    27 45  }
    28 46   
    29 47  #[inline(always)]
    30  -fn socket_connect_v4(ctx: LsmContext, sockaddr: *const sockaddr) -> Result<i32, c_long> {
     48 +fn socket_connect_v4(ctx: LsmContext, sockaddr: *const sockaddr) -> Result<Action, c_long> {
    31 49   let sockaddr_in: *const sockaddr_in = sockaddr as *const sockaddr_in;
    32 50   let addr = u32::from_be(unsafe { (*sockaddr_in).sin_addr.s_addr });
    33 51   
    skipped 1 lines
    35 53   
    36 54   if let Some(addrs) = unsafe { ALLOWED_SOCKET_CONNECT_V4.get(&INODE_WILDCARD) } {
    37 55   if addrs.all() {
    38  - if let Some(addrs) = unsafe { DENIED_SOCKET_CONNECT_V4.get(&INODE_WILDCARD) } {
    39  - if addrs.all() {
    40  - ALERT_SOCKET_CONNECT.output(
    41  - &ctx,
    42  - &AlertSocketConnect::new_ipv4(ctx.pid(), binprm_inode, addr),
    43  - 0,
    44  - );
    45  - return Ok(-1);
    46  - }
    47  - if addrs.addrs[..MAX_IPV4ADDRS].contains(&addr) {
    48  - ALERT_SOCKET_CONNECT.output(
    49  - &ctx,
    50  - &AlertSocketConnect::new_ipv4(ctx.pid(), binprm_inode, addr),
    51  - 0,
    52  - );
    53  - return Ok(-1);
    54  - }
    55  - }
    56  - 
    57  - if let Some(addrs) = unsafe { DENIED_SOCKET_CONNECT_V4.get(&binprm_inode) } {
    58  - if addrs.all() {
    59  - ALERT_SOCKET_CONNECT.output(
    60  - &ctx,
    61  - &AlertSocketConnect::new_ipv4(ctx.pid(), binprm_inode, addr),
    62  - 0,
    63  - );
    64  - return Ok(-1);
    65  - }
    66  - if addrs.addrs[..MAX_IPV4ADDRS].contains(&addr) {
    67  - ALERT_SOCKET_CONNECT.output(
    68  - &ctx,
    69  - &AlertSocketConnect::new_ipv4(ctx.pid(), binprm_inode, addr),
    70  - 0,
    71  - );
    72  - return Ok(-1);
    73  - }
    74  - }
    75  - } else {
    76  - if addrs.addrs[..MAX_IPV4ADDRS].contains(&addr) {
    77  - return Ok(0);
    78  - }
     56 + return Ok(check_conditions_and_alert_v4(
     57 + &ctx,
     58 + &DENIED_SOCKET_CONNECT_V4,
     59 + addr,
     60 + binprm_inode,
     61 + Mode::Denylist,
     62 + ));
    79 63   }
    80 64   }
    81 65   
    82 66   if let Some(addrs) = unsafe { DENIED_SOCKET_CONNECT_V4.get(&INODE_WILDCARD) } {
    83 67   if addrs.all() {
    84  - if let Some(addrs) = unsafe { ALLOWED_SOCKET_CONNECT_V4.get(&INODE_WILDCARD) } {
    85  - if addrs.all() {
    86  - return Ok(0);
    87  - }
    88  - if addrs.addrs[..MAX_IPV4ADDRS].contains(&addr) {
    89  - return Ok(0);
    90  - }
    91  - }
    92  - 
    93  - if let Some(addrs) = unsafe { ALLOWED_SOCKET_CONNECT_V4.get(&binprm_inode) } {
    94  - if addrs.all() {
    95  - return Ok(0);
    96  - }
    97  - if addrs.addrs[..MAX_IPV4ADDRS].contains(&addr) {
    98  - return Ok(0);
    99  - }
    100  - }
    101  - 
    102  - ALERT_SOCKET_CONNECT.output(
     68 + return Ok(check_conditions_and_alert_v4(
    103 69   &ctx,
    104  - &AlertSocketConnect::new_ipv4(ctx.pid(), binprm_inode, addr),
    105  - 0,
    106  - );
    107  - return Ok(-1);
    108  - } else {
    109  - if addrs.addrs[..MAX_IPV4ADDRS].contains(&addr) {
    110  - ALERT_SOCKET_CONNECT.output(
    111  - &ctx,
    112  - &AlertSocketConnect::new_ipv4(ctx.pid(), binprm_inode, addr),
    113  - 0,
    114  - );
    115  - return Ok(-1);
    116  - }
     70 + &ALLOWED_SOCKET_CONNECT_V4,
     71 + addr,
     72 + binprm_inode,
     73 + Mode::Allowlist,
     74 + ));
    117 75   }
    118 76   }
    119 77   
    120  - Ok(0)
     78 + Ok(Action::Allow)
    121 79  }
    122 80   
    123 81  #[inline(always)]
    124  -fn socket_connect_v6(ctx: LsmContext, sockaddr: *const sockaddr) -> Result<i32, c_long> {
     82 +fn socket_connect_v6(ctx: LsmContext, sockaddr: *const sockaddr) -> Result<Action, c_long> {
    125 83   let sockaddr_in6: *const sockaddr_in6 = sockaddr as *const sockaddr_in6;
    126 84   
    127 85   let sockaddr_in6: sockaddr_in6 = unsafe { bpf_probe_read_kernel(sockaddr_in6)? };
    skipped 3 lines
    131 89   
    132 90   if let Some(addrs) = unsafe { ALLOWED_SOCKET_CONNECT_V6.get(&INODE_WILDCARD) } {
    133 91   if addrs.all() {
    134  - if let Some(addrs) = unsafe { DENIED_SOCKET_CONNECT_V6.get(&INODE_WILDCARD) } {
    135  - if addrs.all() {
    136  - ALERT_SOCKET_CONNECT.output(
    137  - &ctx,
    138  - &AlertSocketConnect::new_ipv6(ctx.pid(), binprm_inode, addr),
    139  - 0,
    140  - );
    141  - return Ok(-1);
    142  - }
    143  - if addrs.addrs[..MAX_IPV6ADDRS].contains(&addr) {
    144  - ALERT_SOCKET_CONNECT.output(
    145  - &ctx,
    146  - &AlertSocketConnect::new_ipv6(ctx.pid(), binprm_inode, addr),
    147  - 0,
    148  - );
    149  - return Ok(-1);
    150  - }
    151  - }
    152  - 
    153  - if let Some(addrs) = unsafe { DENIED_SOCKET_CONNECT_V6.get(&binprm_inode) } {
    154  - if addrs.all() {
    155  - ALERT_SOCKET_CONNECT.output(
    156  - &ctx,
    157  - &AlertSocketConnect::new_ipv6(ctx.pid(), binprm_inode, addr),
    158  - 0,
    159  - );
    160  - return Ok(-1);
    161  - }
    162  - if addrs.addrs[..MAX_IPV6ADDRS].contains(&addr) {
    163  - ALERT_SOCKET_CONNECT.output(
    164  - &ctx,
    165  - &AlertSocketConnect::new_ipv6(ctx.pid(), binprm_inode, addr),
    166  - 0,
    167  - );
    168  - return Ok(-1);
    169  - }
    170  - }
    171  - } else {
    172  - if addrs.addrs[..MAX_IPV6ADDRS].contains(&addr) {
    173  - return Ok(0);
    174  - }
     92 + return Ok(check_conditions_and_alert_v6(
     93 + &ctx,
     94 + &DENIED_SOCKET_CONNECT_V6,
     95 + addr,
     96 + binprm_inode,
     97 + Mode::Denylist,
     98 + ));
    175 99   }
    176 100   }
    177 101   
    178 102   if let Some(addrs) = unsafe { DENIED_SOCKET_CONNECT_V6.get(&INODE_WILDCARD) } {
    179 103   if addrs.all() {
    180  - if let Some(addrs) = unsafe { ALLOWED_SOCKET_CONNECT_V6.get(&INODE_WILDCARD) } {
    181  - if addrs.all() {
    182  - return Ok(0);
    183  - }
    184  - if addrs.addrs[..MAX_IPV6ADDRS].contains(&addr) {
    185  - return Ok(0);
    186  - }
    187  - }
     104 + return Ok(check_conditions_and_alert_v6(
     105 + &ctx,
     106 + &ALLOWED_SOCKET_CONNECT_V6,
     107 + addr,
     108 + binprm_inode,
     109 + Mode::Allowlist,
     110 + ));
     111 + }
     112 + }
    188 113   
    189  - if let Some(addrs) = unsafe { ALLOWED_SOCKET_CONNECT_V6.get(&binprm_inode) } {
    190  - if addrs.all() {
    191  - return Ok(0);
    192  - }
    193  - if addrs.addrs[..MAX_IPV6ADDRS].contains(&addr) {
    194  - return Ok(0);
    195  - }
    196  - }
     114 + Ok(Action::Allow)
     115 +}
    197 116   
     117 +#[inline(always)]
     118 +fn check_conditions_and_alert_v4(
     119 + ctx: &LsmContext,
     120 + map: &HashMap<u64, Ipv4Addrs>,
     121 + addr: u32,
     122 + binprm_inode: u64,
     123 + mode: Mode,
     124 +) -> Action {
     125 + match check_conditions(map, addr, binprm_inode, mode) {
     126 + Action::Deny => {
    198 127   ALERT_SOCKET_CONNECT.output(
    199  - &ctx,
     128 + ctx,
     129 + &AlertSocketConnect::new_ipv4(ctx.pid(), binprm_inode, addr),
     130 + 0,
     131 + );
     132 + Action::Deny
     133 + }
     134 + action => action,
     135 + }
     136 +}
     137 + 
     138 +#[inline(always)]
     139 +fn check_conditions_and_alert_v6(
     140 + ctx: &LsmContext,
     141 + map: &HashMap<u64, Ipv6Addrs>,
     142 + addr: [u8; 16],
     143 + binprm_inode: u64,
     144 + mode: Mode,
     145 +) -> Action {
     146 + match check_conditions(map, addr, binprm_inode, mode) {
     147 + Action::Deny => {
     148 + ALERT_SOCKET_CONNECT.output(
     149 + ctx,
    200 150   &AlertSocketConnect::new_ipv6(ctx.pid(), binprm_inode, addr),
    201 151   0,
    202 152   );
    203  - return Ok(-1);
    204  - } else {
    205  - if addrs.addrs[..MAX_IPV6ADDRS].contains(&addr) {
    206  - ALERT_SOCKET_CONNECT.output(
    207  - &ctx,
    208  - &AlertSocketConnect::new_ipv6(ctx.pid(), binprm_inode, addr),
    209  - 0,
    210  - );
    211  - return Ok(-1);
    212  - }
     153 + Action::Deny
    213 154   }
     155 + action => action,
    214 156   }
     157 +}
    215 158   
    216  - Ok(0)
     159 +#[inline(always)]
     160 +fn check_conditions<T, U, const V: usize>(
     161 + map: &HashMap<u64, T>,
     162 + addr: U,
     163 + binprm_inode: u64,
     164 + mode: Mode,
     165 +) -> Action
     166 +where
     167 + T: IpAddrs<U, V>,
     168 + U: Copy + PartialEq,
     169 +{
     170 + if let Some(addrs) = unsafe { map.get(&INODE_WILDCARD) } {
     171 + if let Some(action) = check_addresses(addrs, addr, &mode) {
     172 + return action;
     173 + }
     174 + }
     175 + 
     176 + if let Some(addrs) = unsafe { map.get(&binprm_inode) } {
     177 + if let Some(action) = check_addresses(addrs, addr, &mode) {
     178 + return action;
     179 + }
     180 + }
     181 + 
     182 + match mode {
     183 + Mode::Allowlist => Action::Deny,
     184 + Mode::Denylist => Action::Allow,
     185 + }
     186 +}
     187 + 
     188 +#[inline(always)]
     189 +fn check_addresses<T, U, const V: usize>(addrs: &T, addr: U, mode: &Mode) -> Option<Action>
     190 +where
     191 + T: IpAddrs<U, V>,
     192 + U: Copy + PartialEq,
     193 +{
     194 + if addrs.all() {
     195 + return Some(match mode {
     196 + Mode::Allowlist => Action::Allow,
     197 + Mode::Denylist => Action::Deny,
     198 + });
     199 + }
     200 + 
     201 + if addrs.addrs()[..V].contains(&addr) {
     202 + return Some(match mode {
     203 + Mode::Allowlist => Action::Allow,
     204 + Mode::Denylist => Action::Deny,
     205 + });
     206 + }
     207 + 
     208 + None
    217 209  }
    218 210   
Please wait...
Page is in error, reload to recover