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 }