mirror of
https://github.com/evilsocket/opensnitch.git
synced 2025-03-04 08:34:40 +01:00
Lookup inode and uid via netlink
It has some advantages over parsing /proc, like performance and reliability.
This commit is contained in:
parent
4a30cc5b84
commit
d8ad8de6ef
3 changed files with 246 additions and 1 deletions
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/gustavo-iniguez-goya/opensnitch/daemon/netstat"
|
||||
"github.com/gustavo-iniguez-goya/opensnitch/daemon/procmon"
|
||||
"github.com/gustavo-iniguez-goya/opensnitch/daemon/ui/protocol"
|
||||
"github.com/gustavo-iniguez-goya/opensnitch/daemon/netlink"
|
||||
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
@ -75,11 +76,22 @@ func newConnectionImpl(nfp *netfilter.Packet, c *Connection) (cr *Connection, er
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// 0. lookup uid and inode via netlink
|
||||
// 1. lookup uid and inode using /proc/net/(udp|tcp)
|
||||
// 2. lookup pid by inode
|
||||
// 3. if this is coming from us, just accept
|
||||
// 4. lookup process info by pid
|
||||
if c.Entry = netstat.FindEntry(c.Protocol, c.SrcIP, c.SrcPort, c.DstIP, c.DstPort); c.Entry == nil {
|
||||
if _uid, _inode := netlink.GetSocketInfo(c.Protocol, c.SrcIP, c.SrcPort, c.DstIP, c.DstPort); _uid != -1 && _inode != -1 {
|
||||
c.Entry = &netstat.Entry {
|
||||
Proto: c.Protocol,
|
||||
SrcIP: c.SrcIP,
|
||||
SrcPort: c.SrcPort,
|
||||
DstIP: c.DstIP,
|
||||
DstPort: c.DstPort,
|
||||
UserId: _uid,
|
||||
INode: _inode,
|
||||
}
|
||||
}else if c.Entry = netstat.FindEntry(c.Protocol, c.SrcIP, c.SrcPort, c.DstIP, c.DstPort); c.Entry == nil {
|
||||
return nil, fmt.Errorf("Could not find netstat entry for: %s", c)
|
||||
}
|
||||
if c.Entry.UserId == -1 {
|
||||
|
|
36
daemon/netlink/socket.go
Normal file
36
daemon/netlink/socket.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package netlink
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"net"
|
||||
)
|
||||
|
||||
func GetSocketInfo(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint) (uid, inode int) {
|
||||
family := uint8(syscall.AF_INET)
|
||||
ipproto := uint8(syscall.IPPROTO_TCP)
|
||||
protoLen := len(proto)
|
||||
if proto[protoLen-1:protoLen] == "6" {
|
||||
family = syscall.AF_INET6
|
||||
}
|
||||
|
||||
var s *Socket
|
||||
var err error
|
||||
if proto[:3] == "udp" {
|
||||
ipproto = syscall.IPPROTO_UDP
|
||||
if protoLen >=7 && proto[:7] == "udplite" {
|
||||
ipproto = syscall.IPPROTO_UDPLITE
|
||||
}
|
||||
srcAddr := &net.UDPAddr{ IP: srcIP, Port: int(srcPort), }
|
||||
dstAddr := &net.UDPAddr{ IP: dstIP, Port: int(dstPort), }
|
||||
s, err = SocketGet(family, ipproto, srcAddr, dstAddr)
|
||||
} else {
|
||||
srcAddr := &net.TCPAddr{ IP: srcIP, Port: int(srcPort), }
|
||||
dstAddr := &net.TCPAddr{ IP: dstIP, Port: int(dstPort), }
|
||||
s, err = SocketGet(family, ipproto, srcAddr, dstAddr)
|
||||
}
|
||||
if err == nil && s.INode != 0xffffffff {
|
||||
return int(s.UID), int(s.INode)
|
||||
}
|
||||
|
||||
return -1, -1
|
||||
}
|
197
daemon/netlink/socket_linux.go
Normal file
197
daemon/netlink/socket_linux.go
Normal file
|
@ -0,0 +1,197 @@
|
|||
package netlink
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// This is a copy of https://github.com/vishvananda/netlink socket_linux.go
|
||||
// which adds support for query UDP, UDPLITE and IPv6 sockets
|
||||
|
||||
const (
|
||||
sizeofSocketID = 0x30
|
||||
sizeofSocketRequest = sizeofSocketID + 0x8
|
||||
sizeofSocket = sizeofSocketID + 0x18
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotImplemented = errors.New("not implemented")
|
||||
native = nl.NativeEndian()
|
||||
networkOrder = binary.BigEndian
|
||||
)
|
||||
|
||||
type SocketID struct {
|
||||
SourcePort uint16
|
||||
DestinationPort uint16
|
||||
Source net.IP
|
||||
Destination net.IP
|
||||
Interface uint32
|
||||
Cookie [2]uint32
|
||||
}
|
||||
|
||||
// Socket represents a netlink socket.
|
||||
type Socket struct {
|
||||
Family uint8
|
||||
State uint8
|
||||
Timer uint8
|
||||
Retrans uint8
|
||||
ID SocketID
|
||||
Expires uint32
|
||||
RQueue uint32
|
||||
WQueue uint32
|
||||
UID uint32
|
||||
INode uint32
|
||||
}
|
||||
|
||||
type socketRequest struct {
|
||||
Family uint8
|
||||
Protocol uint8
|
||||
Ext uint8
|
||||
pad uint8
|
||||
States uint32
|
||||
ID SocketID
|
||||
}
|
||||
|
||||
type writeBuffer struct {
|
||||
Bytes []byte
|
||||
pos int
|
||||
}
|
||||
|
||||
func (b *writeBuffer) Write(c byte) {
|
||||
b.Bytes[b.pos] = c
|
||||
b.pos++
|
||||
}
|
||||
|
||||
func (b *writeBuffer) Next(n int) []byte {
|
||||
s := b.Bytes[b.pos : b.pos+n]
|
||||
b.pos += n
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *socketRequest) Serialize() []byte {
|
||||
b := writeBuffer{Bytes: make([]byte, sizeofSocketRequest)}
|
||||
b.Write(r.Family)
|
||||
b.Write(r.Protocol)
|
||||
b.Write(r.Ext)
|
||||
b.Write(r.pad)
|
||||
native.PutUint32(b.Next(4), r.States)
|
||||
networkOrder.PutUint16(b.Next(2), r.ID.SourcePort)
|
||||
networkOrder.PutUint16(b.Next(2), r.ID.DestinationPort)
|
||||
copy(b.Next(4), r.ID.Source.To4())
|
||||
b.Next(12)
|
||||
copy(b.Next(4), r.ID.Destination.To4())
|
||||
b.Next(12)
|
||||
native.PutUint32(b.Next(4), r.ID.Interface)
|
||||
native.PutUint32(b.Next(4), r.ID.Cookie[0])
|
||||
native.PutUint32(b.Next(4), r.ID.Cookie[1])
|
||||
return b.Bytes
|
||||
}
|
||||
|
||||
func (r *socketRequest) Len() int { return sizeofSocketRequest }
|
||||
|
||||
type readBuffer struct {
|
||||
Bytes []byte
|
||||
pos int
|
||||
}
|
||||
|
||||
func (b *readBuffer) Read() byte {
|
||||
c := b.Bytes[b.pos]
|
||||
b.pos++
|
||||
return c
|
||||
}
|
||||
|
||||
func (b *readBuffer) Next(n int) []byte {
|
||||
s := b.Bytes[b.pos : b.pos+n]
|
||||
b.pos += n
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Socket) deserialize(b []byte) error {
|
||||
if len(b) < sizeofSocket {
|
||||
return fmt.Errorf("socket data short read (%d); want %d", len(b), sizeofSocket)
|
||||
}
|
||||
rb := readBuffer{Bytes: b}
|
||||
s.Family = rb.Read()
|
||||
s.State = rb.Read()
|
||||
s.Timer = rb.Read()
|
||||
s.Retrans = rb.Read()
|
||||
s.ID.SourcePort = networkOrder.Uint16(rb.Next(2))
|
||||
s.ID.DestinationPort = networkOrder.Uint16(rb.Next(2))
|
||||
s.ID.Source = net.IPv4(rb.Read(), rb.Read(), rb.Read(), rb.Read())
|
||||
rb.Next(12)
|
||||
s.ID.Destination = net.IPv4(rb.Read(), rb.Read(), rb.Read(), rb.Read())
|
||||
rb.Next(12)
|
||||
s.ID.Interface = native.Uint32(rb.Next(4))
|
||||
s.ID.Cookie[0] = native.Uint32(rb.Next(4))
|
||||
s.ID.Cookie[1] = native.Uint32(rb.Next(4))
|
||||
s.Expires = native.Uint32(rb.Next(4))
|
||||
s.RQueue = native.Uint32(rb.Next(4))
|
||||
s.WQueue = native.Uint32(rb.Next(4))
|
||||
s.UID = native.Uint32(rb.Next(4))
|
||||
s.INode = native.Uint32(rb.Next(4))
|
||||
return nil
|
||||
}
|
||||
|
||||
// SocketGet returns the Socket identified by its local and remote addresses.
|
||||
func SocketGet(family uint8, proto uint8, local, remote net.Addr) (*Socket, error) {
|
||||
_Id := SocketID{}
|
||||
|
||||
if proto == unix.IPPROTO_UDP || proto == unix.IPPROTO_UDPLITE {
|
||||
localUDP, ok := local.(*net.UDPAddr)
|
||||
if !ok {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
remoteUDP, _ := remote.(*net.UDPAddr)
|
||||
_Id = SocketID{
|
||||
SourcePort: uint16(localUDP.Port),
|
||||
DestinationPort: uint16(remoteUDP.Port),
|
||||
Source: localUDP.IP.To4(),
|
||||
Destination: remoteUDP.IP.To4(),
|
||||
Cookie: [2]uint32{nl.TCPDIAG_NOCOOKIE, nl.TCPDIAG_NOCOOKIE},
|
||||
}
|
||||
} else {
|
||||
localTCP, _ := local.(*net.TCPAddr)
|
||||
remoteTCP, _ := remote.(*net.TCPAddr)
|
||||
_Id = SocketID{
|
||||
SourcePort: uint16(localTCP.Port),
|
||||
DestinationPort: uint16(remoteTCP.Port),
|
||||
Source: localTCP.IP.To4(),
|
||||
Destination: remoteTCP.IP.To4(),
|
||||
Cookie: [2]uint32{nl.TCPDIAG_NOCOOKIE, nl.TCPDIAG_NOCOOKIE},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
s, err := nl.Subscribe(unix.NETLINK_INET_DIAG)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer s.Close()
|
||||
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, 0)
|
||||
req.AddData(&socketRequest{
|
||||
Family: family,
|
||||
Protocol: proto,
|
||||
ID: _Id,
|
||||
})
|
||||
s.Send(req)
|
||||
msgs, err := s.Receive()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(msgs) == 0 {
|
||||
return nil, errors.New("no message nor error from netlink")
|
||||
}
|
||||
if len(msgs) > 2 {
|
||||
return nil, fmt.Errorf("multiple (%d) matching sockets", len(msgs))
|
||||
}
|
||||
sock := &Socket{}
|
||||
if err := sock.deserialize(msgs[0].Data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sock, nil
|
||||
}
|
Loading…
Add table
Reference in a new issue