github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/interfaces/builtin/netlink_driver.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2021 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package builtin
    21  
    22  import (
    23  	"fmt"
    24  	"regexp"
    25  
    26  	"github.com/snapcore/snapd/interfaces"
    27  	"github.com/snapcore/snapd/interfaces/seccomp"
    28  	"github.com/snapcore/snapd/snap"
    29  )
    30  
    31  const netlinkDriverSummary = `allows operating a kernel driver module exposing itself via a netlink protocol family`
    32  
    33  const netlinkDriverBaseDeclarationSlots = `
    34    netlink-driver:
    35      allow-installation:
    36        slot-snap-type:
    37          - core
    38          - gadget
    39      deny-auto-connection: true
    40  `
    41  
    42  // netlinkDriverInterface type
    43  type netlinkDriverInterface struct {
    44  	commonInterface
    45  }
    46  
    47  const netlinkDriverConnectedPlugApparmor = `
    48  # allow accessing the Linux kernel custom netlink protocol
    49  # this allows all netlink protocol communication - further 
    50  # confinement for particular families/protocols is 
    51  # implemented via seccomp filtering
    52  network netlink,
    53  
    54  # CAP_NET_ADMIN required per 'man 7 netlink'
    55  capability net_admin,
    56  `
    57  
    58  // regex for family-name must match:
    59  // * at least 2 characters long
    60  // * must start with letter
    61  // * must not end in a hyphen
    62  // * can contain numbers, letters and hyphens for all character positions except
    63  //   as described above
    64  var familyNameRegexp = regexp.MustCompile(`^[a-z]+[a-z0-9-]*[^\-]$`)
    65  
    66  // BeforePrepareSlot checks the slot definition is valid
    67  func (iface *netlinkDriverInterface) BeforePrepareSlot(slot *snap.SlotInfo) error {
    68  	// Must have a protocol number identified as family
    69  	number, ok := slot.Attrs["family"]
    70  	if !ok {
    71  		return fmt.Errorf("netlink-driver slot must have a family number attribute")
    72  	}
    73  
    74  	// Valid values of number
    75  	if _, ok := number.(int64); !ok {
    76  		return fmt.Errorf("netlink-driver slot family number attribute must be an int")
    77  	}
    78  
    79  	// must also have a family-name, used for identifying plug <-> slot
    80  	return validateFamilyNameAttr(slot, "slot")
    81  }
    82  
    83  func validateFamilyNameAttr(a interfaces.Attrer, side string) error {
    84  	name, ok := a.Lookup("family-name")
    85  	if !ok {
    86  		return fmt.Errorf("netlink-driver %s must have a family-name attribute", side)
    87  	}
    88  
    89  	nameStr, ok := name.(string)
    90  	if !ok {
    91  		return fmt.Errorf("netlink-driver %s family-name attribute must be a string", side)
    92  	}
    93  
    94  	// ensure it matches the regex
    95  	if !familyNameRegexp.MatchString(nameStr) {
    96  		return fmt.Errorf("netlink-driver %s family-name %q is invalid", side, nameStr)
    97  	}
    98  
    99  	// attribute is good
   100  	return nil
   101  
   102  }
   103  
   104  // BeforePreparePlug checks the plug definition is valid
   105  func (iface *netlinkDriverInterface) BeforePreparePlug(plug *snap.PlugInfo) error {
   106  	return validateFamilyNameAttr(plug, "plug")
   107  }
   108  
   109  func (iface *netlinkDriverInterface) SecCompConnectedPlug(spec *seccomp.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
   110  	var familyNum int64
   111  	if err := slot.Attr("family", &familyNum); err != nil {
   112  		return err
   113  	}
   114  
   115  	var familyName string
   116  	if err := slot.Attr("family-name", &familyName); err != nil {
   117  		return err
   118  	}
   119  
   120  	spec.AddSnippet(fmt.Sprintf(`# Description: Can access the Linux kernel custom netlink protocol
   121  # for family %s
   122  socket AF_NETLINK - %d
   123  bind`, familyName, familyNum))
   124  	return nil
   125  }
   126  
   127  func (iface *netlinkDriverInterface) AutoConnect(plug *snap.PlugInfo, slot *snap.SlotInfo) bool {
   128  	// ensure that the family name on the plug side matches the family name
   129  	// on the slot side
   130  
   131  	var slotFamily, plugFamily string
   132  	if err := plug.Attr("family-name", &plugFamily); err != nil {
   133  		return false
   134  	}
   135  
   136  	if err := slot.Attr("family-name", &slotFamily); err != nil {
   137  		return false
   138  	}
   139  
   140  	return slotFamily == plugFamily
   141  }
   142  
   143  func init() {
   144  	registerIface(&netlinkDriverInterface{
   145  		commonInterface: commonInterface{
   146  			name:                  "netlink-driver",
   147  			summary:               netlinkDriverSummary,
   148  			baseDeclarationSlots:  netlinkDriverBaseDeclarationSlots,
   149  			connectedPlugAppArmor: netlinkDriverConnectedPlugApparmor,
   150  		},
   151  	})
   152  }