github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/trace/capabilities/tracer/tracer.go (about)

     1  // Copyright 2019-2023 The Inspektor Gadget authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  //go:build !withoutebpf
    16  
    17  package tracer
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"os"
    23  	"unsafe"
    24  
    25  	"github.com/cilium/ebpf"
    26  	"github.com/cilium/ebpf/link"
    27  	"github.com/cilium/ebpf/perf"
    28  	"github.com/syndtr/gocapability/capability"
    29  
    30  	gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context"
    31  	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"
    32  	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/capabilities/types"
    33  	eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
    34  	"github.com/inspektor-gadget/inspektor-gadget/pkg/utils/syscalls"
    35  )
    36  
    37  //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target ${TARGET} -cc clang -cflags ${CFLAGS} -type cap_event capabilities ./bpf/capable.bpf.c -- -I./bpf/
    38  
    39  type Config struct {
    40  	MountnsMap *ebpf.Map
    41  	AuditOnly  bool
    42  	Unique     bool
    43  }
    44  
    45  type Tracer struct {
    46  	config        *Config
    47  	objs          capabilitiesObjects
    48  	capEnterLink  link.Link
    49  	capExitLink   link.Link
    50  	tpSysEnter    link.Link
    51  	tpSysExit     link.Link
    52  	reader        *perf.Reader
    53  	enricher      gadgets.DataEnricherByMntNs
    54  	eventCallback func(*types.Event)
    55  }
    56  
    57  var capabilitiesNames = map[int32]string{
    58  	0:  "CHOWN",
    59  	1:  "DAC_OVERRIDE",
    60  	2:  "DAC_READ_SEARCH",
    61  	3:  "FOWNER",
    62  	4:  "FSETID",
    63  	5:  "KILL",
    64  	6:  "SETGID",
    65  	7:  "SETUID",
    66  	8:  "SETPCAP",
    67  	9:  "LINUX_IMMUTABLE",
    68  	10: "NET_BIND_SERVICE",
    69  	11: "NET_BROADCAST",
    70  	12: "NET_ADMIN",
    71  	13: "NET_RAW",
    72  	14: "IPC_LOCK",
    73  	15: "IPC_OWNER",
    74  	16: "SYS_MODULE",
    75  	17: "SYS_RAWIO",
    76  	18: "SYS_CHROOT",
    77  	19: "SYS_PTRACE",
    78  	20: "SYS_PACCT",
    79  	21: "SYS_ADMIN",
    80  	22: "SYS_BOOT",
    81  	23: "SYS_NICE",
    82  	24: "SYS_RESOURCE",
    83  	25: "SYS_TIME",
    84  	26: "SYS_TTY_CONFIG",
    85  	27: "MKNOD",
    86  	28: "LEASE",
    87  	29: "AUDIT_WRITE",
    88  	30: "AUDIT_CONTROL",
    89  	31: "SETFCAP",
    90  	32: "MAC_OVERRIDE",
    91  	33: "MAC_ADMIN",
    92  	34: "SYSLOG",
    93  	35: "WAKE_ALARM",
    94  	36: "BLOCK_SUSPEND",
    95  	37: "AUDIT_READ",
    96  	38: "PERFMON",
    97  	39: "BPF",
    98  	40: "CHECKPOINT_RESTORE",
    99  }
   100  
   101  func NewTracer(c *Config, enricher gadgets.DataEnricherByMntNs,
   102  	eventCallback func(*types.Event),
   103  ) (*Tracer, error) {
   104  	t := &Tracer{
   105  		config:        c,
   106  		enricher:      enricher,
   107  		eventCallback: eventCallback,
   108  	}
   109  
   110  	if err := t.install(); err != nil {
   111  		t.close()
   112  		return nil, err
   113  	}
   114  
   115  	go t.run()
   116  
   117  	return t, nil
   118  }
   119  
   120  // Stop stops the tracer
   121  // TODO: Remove after refactoring
   122  func (t *Tracer) Stop() {
   123  	t.close()
   124  }
   125  
   126  func (t *Tracer) close() {
   127  	t.capEnterLink = gadgets.CloseLink(t.capEnterLink)
   128  	t.capExitLink = gadgets.CloseLink(t.capExitLink)
   129  	t.tpSysEnter = gadgets.CloseLink(t.tpSysEnter)
   130  	t.tpSysExit = gadgets.CloseLink(t.tpSysExit)
   131  
   132  	if t.reader != nil {
   133  		t.reader.Close()
   134  	}
   135  
   136  	t.objs.Close()
   137  }
   138  
   139  func (t *Tracer) install() error {
   140  	spec, err := loadCapabilities()
   141  	if err != nil {
   142  		return fmt.Errorf("loading ebpf program: %w", err)
   143  	}
   144  
   145  	consts := map[string]interface{}{
   146  		"audit_only": t.config.AuditOnly,
   147  		"unique":     t.config.Unique,
   148  	}
   149  
   150  	if err := gadgets.LoadeBPFSpec(t.config.MountnsMap, spec, consts, &t.objs); err != nil {
   151  		return fmt.Errorf("loading ebpf spec: %w", err)
   152  	}
   153  
   154  	tp, err := link.AttachRawTracepoint(link.RawTracepointOptions{
   155  		Name:    "sys_enter",
   156  		Program: t.objs.IgCapSysEnter,
   157  	})
   158  	if err != nil {
   159  		return fmt.Errorf("attaching tracepoint: %w", err)
   160  	}
   161  	t.tpSysEnter = tp
   162  
   163  	tp, err = link.AttachRawTracepoint(link.RawTracepointOptions{
   164  		Name:    "sys_exit",
   165  		Program: t.objs.IgCapSysExit,
   166  	})
   167  	if err != nil {
   168  		return fmt.Errorf("attaching tracepoint: %w", err)
   169  	}
   170  	t.tpSysEnter = tp
   171  
   172  	kprobe, err := link.Kprobe("cap_capable", t.objs.IgTraceCapE, nil)
   173  	if err != nil {
   174  		return fmt.Errorf("attaching kprobe: %w", err)
   175  	}
   176  	t.capEnterLink = kprobe
   177  
   178  	kretprobe, err := link.Kretprobe("cap_capable", t.objs.IgTraceCapX, nil)
   179  	if err != nil {
   180  		return fmt.Errorf("attaching kretprobe: %w", err)
   181  	}
   182  	t.capExitLink = kretprobe
   183  
   184  	reader, err := perf.NewReader(t.objs.capabilitiesMaps.Events, gadgets.PerfBufferPages*os.Getpagesize())
   185  	if err != nil {
   186  		return fmt.Errorf("creating perf ring buffer: %w", err)
   187  	}
   188  	t.reader = reader
   189  
   190  	return nil
   191  }
   192  
   193  func capsNames(capsBitField uint64) (ret []string) {
   194  	// Ensure ret is not nil
   195  	ret = []string{}
   196  	for i := capability.Cap(0); i <= capability.CAP_LAST_CAP; i++ {
   197  		if (1<<uint(i))&capsBitField != 0 {
   198  			ret = append(ret, i.String())
   199  		}
   200  	}
   201  	return
   202  }
   203  
   204  func boolPointer(b bool) *bool {
   205  	return &b
   206  }
   207  
   208  func (t *Tracer) run() {
   209  	for {
   210  		record, err := t.reader.Read()
   211  		if err != nil {
   212  			if errors.Is(err, perf.ErrClosed) {
   213  				// nothing to do, we're done
   214  				return
   215  			}
   216  
   217  			msg := fmt.Sprintf("Error reading perf ring buffer: %s", err)
   218  			t.eventCallback(types.Base(eventtypes.Err(msg)))
   219  			return
   220  		}
   221  
   222  		if record.LostSamples > 0 {
   223  			msg := fmt.Sprintf("lost %d samples", record.LostSamples)
   224  			t.eventCallback(types.Base(eventtypes.Warn(msg)))
   225  			continue
   226  		}
   227  
   228  		if len(record.RawSample) < 1 {
   229  			t.eventCallback(types.Base(eventtypes.Warn("empty record")))
   230  			continue
   231  		}
   232  
   233  		bpfEvent := (*capabilitiesCapEvent)(unsafe.Pointer(&record.RawSample[0]))
   234  
   235  		capability := bpfEvent.Cap
   236  		capabilityName, ok := capabilitiesNames[capability]
   237  		if !ok {
   238  			// If this is printed it may mean a new capability was added to the kernel
   239  			// and capabilitiesNames map needs to be updated.
   240  			capabilityName = fmt.Sprintf("UNKNOWN (%d)", capability)
   241  		}
   242  
   243  		verdict := "Deny"
   244  		if bpfEvent.Ret == 0 {
   245  			verdict = "Allow"
   246  		}
   247  
   248  		syscall, ok := syscalls.GetSyscallNameByNumber(int(bpfEvent.Syscall))
   249  		if !ok {
   250  			syscall = fmt.Sprintf("syscall%d", int(bpfEvent.Syscall))
   251  		}
   252  
   253  		var insetID *bool
   254  		if bpfEvent.Insetid == 0 {
   255  			insetID = boolPointer(false)
   256  		} else if bpfEvent.Insetid > 0 {
   257  			insetID = boolPointer(true)
   258  		}
   259  
   260  		event := types.Event{
   261  			Event: eventtypes.Event{
   262  				Type:      eventtypes.NORMAL,
   263  				Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp),
   264  			},
   265  			WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.Mntnsid},
   266  			TargetUserNs:  bpfEvent.TargetUserns,
   267  			CurrentUserNs: bpfEvent.CurrentUserns,
   268  			Pid:           bpfEvent.Pid,
   269  			Cap:           int(bpfEvent.Cap),
   270  			Uid:           bpfEvent.Uid,
   271  			Gid:           bpfEvent.Gid,
   272  			Audit:         int(bpfEvent.Audit),
   273  			InsetID:       insetID,
   274  			Comm:          gadgets.FromCString(bpfEvent.Task[:]),
   275  			Syscall:       syscall,
   276  			CapName:       capabilityName,
   277  			Verdict:       verdict,
   278  			Caps:          bpfEvent.CapEffective,
   279  			CapsNames:     capsNames(bpfEvent.CapEffective),
   280  		}
   281  
   282  		if t.enricher != nil {
   283  			t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID)
   284  		}
   285  
   286  		t.eventCallback(&event)
   287  	}
   288  }
   289  
   290  // --- Registry changes
   291  
   292  func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error {
   293  	params := gadgetCtx.GadgetParams()
   294  	t.config.Unique = params.Get(ParamUnique).AsBool()
   295  	t.config.AuditOnly = params.Get(ParamAuditOnly).AsBool()
   296  
   297  	defer t.close()
   298  	if err := t.install(); err != nil {
   299  		return fmt.Errorf("installing tracer: %w", err)
   300  	}
   301  
   302  	go t.run()
   303  	gadgetcontext.WaitForTimeoutOrDone(gadgetCtx)
   304  
   305  	return nil
   306  }
   307  
   308  func (t *Tracer) SetMountNsMap(mountnsMap *ebpf.Map) {
   309  	t.config.MountnsMap = mountnsMap
   310  }
   311  
   312  func (t *Tracer) SetEventHandler(handler any) {
   313  	nh, ok := handler.(func(ev *types.Event))
   314  	if !ok {
   315  		panic("event handler invalid")
   316  	}
   317  	t.eventCallback = nh
   318  }
   319  
   320  func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) {
   321  	tracer := &Tracer{
   322  		config: &Config{},
   323  	}
   324  	return tracer, nil
   325  }