github.com/rigado/snapd@v2.42.5-go-mod+incompatible/interfaces/builtin/hidraw.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2017 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 "path/filepath" 25 "regexp" 26 "strings" 27 28 "github.com/snapcore/snapd/interfaces" 29 "github.com/snapcore/snapd/interfaces/apparmor" 30 "github.com/snapcore/snapd/interfaces/udev" 31 "github.com/snapcore/snapd/snap" 32 ) 33 34 const hidrawSummary = `allows access to specific hidraw device` 35 36 const hidrawBaseDeclarationSlots = ` 37 hidraw: 38 allow-installation: 39 slot-snap-type: 40 - core 41 - gadget 42 deny-auto-connection: true 43 ` 44 45 // hidrawInterface is the type for hidraw interfaces. 46 type hidrawInterface struct{} 47 48 // Name of the hidraw interface. 49 func (iface *hidrawInterface) Name() string { 50 return "hidraw" 51 } 52 53 func (iface *hidrawInterface) StaticInfo() interfaces.StaticInfo { 54 return interfaces.StaticInfo{ 55 Summary: hidrawSummary, 56 BaseDeclarationSlots: hidrawBaseDeclarationSlots, 57 } 58 } 59 60 func (iface *hidrawInterface) String() string { 61 return iface.Name() 62 } 63 64 // Pattern to match allowed hidraw device nodes, path attributes will be 65 // compared to this for validity when not using udev identification 66 var hidrawDeviceNodePattern = regexp.MustCompile("^/dev/hidraw[0-9]{1,3}$") 67 68 // Pattern that is considered valid for the udev symlink to the hidraw device, 69 // path attributes will be compared to this for validity when usb vid and pid 70 // are also specified 71 var hidrawUDevSymlinkPattern = regexp.MustCompile("^/dev/hidraw-[a-z0-9]+$") 72 73 // BeforePrepareSlot checks validity of the defined slot 74 func (iface *hidrawInterface) BeforePrepareSlot(slot *snap.SlotInfo) error { 75 // Check slot has a path attribute identify hidraw device 76 path, ok := slot.Attrs["path"].(string) 77 if !ok || path == "" { 78 return fmt.Errorf("hidraw slots must have a path attribute") 79 } 80 81 // Clean the path before further checks 82 path = filepath.Clean(path) 83 84 if iface.hasUsbAttrs(slot) { 85 // Must be path attribute where symlink will be placed and usb vendor and product identifiers 86 // Check the path attribute is in the allowable pattern 87 if !hidrawUDevSymlinkPattern.MatchString(path) { 88 return fmt.Errorf("hidraw path attribute specifies invalid symlink location") 89 } 90 91 usbVendor, vOk := slot.Attrs["usb-vendor"].(int64) 92 if !vOk { 93 return fmt.Errorf("hidraw slot failed to find usb-vendor attribute") 94 } 95 if (usbVendor < 0x1) || (usbVendor > 0xFFFF) { 96 return fmt.Errorf("hidraw usb-vendor attribute not valid: %d", usbVendor) 97 } 98 99 usbProduct, pOk := slot.Attrs["usb-product"].(int64) 100 if !pOk { 101 return fmt.Errorf("hidraw slot failed to find usb-product attribute") 102 } 103 if (usbProduct < 0x0) || (usbProduct > 0xFFFF) { 104 return fmt.Errorf("hidraw usb-product attribute not valid: %d", usbProduct) 105 } 106 } else { 107 // Just a path attribute - must be a valid usb device node 108 // Check the path attribute is in the allowable pattern 109 if !hidrawDeviceNodePattern.MatchString(path) { 110 return fmt.Errorf("hidraw path attribute must be a valid device node") 111 } 112 } 113 return nil 114 } 115 116 func (iface *hidrawInterface) UDevPermanentSlot(spec *udev.Specification, slot *snap.SlotInfo) error { 117 usbVendor, ok := slot.Attrs["usb-vendor"].(int64) 118 if !ok { 119 return nil 120 } 121 usbProduct, ok := slot.Attrs["usb-product"].(int64) 122 if !ok { 123 return nil 124 } 125 path, ok := slot.Attrs["path"].(string) 126 if !ok || path == "" { 127 return nil 128 } 129 spec.AddSnippet(fmt.Sprintf(`# hidraw 130 IMPORT{builtin}="usb_id" 131 SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="%04x", ATTRS{idProduct}=="%04x", SYMLINK+="%s"`, 132 usbVendor, usbProduct, strings.TrimPrefix(path, "/dev/"))) 133 return nil 134 } 135 136 func (iface *hidrawInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 137 if iface.hasUsbAttrs(slot) { 138 // This apparmor rule must match hidrawDeviceNodePattern 139 // UDev tagging and device cgroups will restrict down to the specific device 140 spec.AddSnippet("/dev/hidraw[0-9]{,[0-9],[0-9][0-9]} rw,") 141 return nil 142 } 143 144 // Path to fixed device node 145 var path string 146 if err := slot.Attr("path", &path); err != nil { 147 return err 148 } 149 cleanedPath := filepath.Clean(path) 150 spec.AddSnippet(fmt.Sprintf("%s rw,", cleanedPath)) 151 return nil 152 153 } 154 155 func (iface *hidrawInterface) UDevConnectedPlug(spec *udev.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 156 hasOnlyPath := true 157 if iface.hasUsbAttrs(slot) { 158 hasOnlyPath = false 159 } 160 161 var usbVendor int64 162 var usbProduct int64 163 var path string 164 165 err := slot.Attr("usb-vendor", &usbVendor) 166 if err != nil && !hasOnlyPath { 167 return nil 168 } 169 err = slot.Attr("usb-product", &usbProduct) 170 if err != nil && !hasOnlyPath { 171 return nil 172 } 173 174 err = slot.Attr("path", &path) 175 if err != nil && hasOnlyPath { 176 return nil 177 } 178 179 if hasOnlyPath { 180 spec.TagDevice(fmt.Sprintf(`SUBSYSTEM=="hidraw", KERNEL=="%s"`, strings.TrimPrefix(path, "/dev/"))) 181 } else { 182 spec.TagDevice(fmt.Sprintf(`IMPORT{builtin}="usb_id" 183 SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="%04x", ATTRS{idProduct}=="%04x"`, usbVendor, usbProduct)) 184 } 185 return nil 186 } 187 188 func (iface *hidrawInterface) AutoConnect(*snap.PlugInfo, *snap.SlotInfo) bool { 189 // allow what declarations allowed 190 return true 191 } 192 193 func (iface *hidrawInterface) hasUsbAttrs(attrs interfaces.Attrer) bool { 194 var v int64 195 if err := attrs.Attr("usb-vendor", &v); err == nil { 196 return true 197 } 198 if err := attrs.Attr("usb-product", &v); err == nil { 199 return true 200 } 201 return false 202 } 203 204 func init() { 205 registerIface(&hidrawInterface{}) 206 }