gitee.com/mysnapcore/mysnapd@v0.1.0/interfaces/apparmor/spec.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017-2018 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 apparmor 21 22 import ( 23 "bytes" 24 "fmt" 25 "path/filepath" 26 "sort" 27 "strings" 28 29 "gitee.com/mysnapcore/mysnapd/interfaces" 30 "gitee.com/mysnapcore/mysnapd/snap" 31 "gitee.com/mysnapcore/mysnapd/strutil" 32 ) 33 34 // Specification assists in collecting apparmor entries associated with an interface. 35 type Specification struct { 36 // scope for various Add{...}Snippet functions 37 securityTags []string 38 39 // snippets are indexed by security tag and describe parts of apparmor policy 40 // for snap application and hook processes. The security tag encodes the identity 41 // of the application or hook. 42 snippets map[string][]string 43 44 // dedupSnippets are just like snippets but are added only once to the 45 // resulting policy in an effort to avoid certain expensive to de-duplicate 46 // rules by apparmor_parser. 47 dedupSnippets map[string]*strutil.OrderedSet 48 49 // parametricSnippets are like snippets but are further parametrized where 50 // one template is instantiated with multiple values that end up producing 51 // a single apparmor rule that is computationally cheaper than naive 52 // repetition of the template alone. The first map index is the security 53 // tag, the second map index is the template. The final map value is the 54 // set of strings that the template is instantiated with across all the 55 // interfaces. 56 // 57 // As a simple example, it can be used to craft rules like 58 // "/sys/**/foo{1,2,3}/** r,", which do not triggering the exponential 59 // cost of parsing "/sys/**/foo1/** r,", followed by two similar rules for 60 // "2" and "3". 61 parametricSnippets map[string]map[string]*strutil.OrderedSet 62 63 // updateNS describe parts of apparmor policy for snap-update-ns executing 64 // on behalf of a given snap. 65 updateNS strutil.OrderedSet 66 67 // AppArmor deny rules cannot be undone by allow rules which makes 68 // deny rules difficult to work with arbitrary combinations of 69 // interfaces. Sometimes it is useful to suppress noisy denials and 70 // because that can currently only be done with explicit deny rules, 71 // adding explicit deny rules unconditionally makes it difficult for 72 // interfaces to be used in combination. Define the suppressPtraceTrace 73 // to allow an interface to request suppression and define 74 // usesPtraceTrace to omit the explicit deny rules such that: 75 // if suppressPtraceTrace && !usesPtraceTrace { 76 // add 'deny ptrace (trace),' 77 // } 78 suppressPtraceTrace bool 79 usesPtraceTrace bool 80 81 // Same as the above, but for the sys_module capability 82 suppressSysModuleCapability bool 83 usesSysModuleCapability bool 84 85 // The home interface typically should have 'ix' as part of its rules, 86 // but specifying certain change_profile rules with these rules cases 87 // a 'conflicting x modifiers' parser error. Allow interfaces that 88 // require this type of change_profile rule to suppress 'ix' so that 89 // the calling interface can be used with the home interface. Ideally, 90 // we would not need this, but we currently do (LP: #1797786) 91 suppressHomeIx bool 92 } 93 94 // setScope sets the scope of subsequent AddSnippet family functions. 95 // The returned function resets the scope to an empty scope. 96 func (spec *Specification) setScope(securityTags []string) (restore func()) { 97 spec.securityTags = securityTags 98 return func() { 99 spec.securityTags = nil 100 } 101 } 102 103 // AddSnippet adds a new apparmor snippet to all applications and hooks using the interface. 104 func (spec *Specification) AddSnippet(snippet string) { 105 if len(spec.securityTags) == 0 { 106 return 107 } 108 if spec.snippets == nil { 109 spec.snippets = make(map[string][]string) 110 } 111 for _, tag := range spec.securityTags { 112 spec.snippets[tag] = append(spec.snippets[tag], snippet) 113 sort.Strings(spec.snippets[tag]) 114 } 115 } 116 117 // AddDeduplicatedSnippet adds a new apparmor snippet to all applications and hooks using the interface. 118 // 119 // Certain combinations of snippets may be computationally expensive for 120 // apparmor_parser in its de-duplication step. This function lets snapd 121 // perform de-duplication of identical rules at the potential cost of a 122 // somewhat more complex auditing process of the text of generated 123 // apparmor profile. Identical mount rules should typically use this, but 124 // this function can also be used to avoid repeated rules that inhibit 125 // auditability. 126 func (spec *Specification) AddDeduplicatedSnippet(snippet string) { 127 if len(spec.securityTags) == 0 { 128 return 129 } 130 if spec.dedupSnippets == nil { 131 spec.dedupSnippets = make(map[string]*strutil.OrderedSet) 132 } 133 for _, tag := range spec.securityTags { 134 bag := spec.dedupSnippets[tag] 135 if bag == nil { 136 bag = &strutil.OrderedSet{} 137 spec.dedupSnippets[tag] = bag 138 } 139 bag.Put(snippet) 140 } 141 } 142 143 // AddParametricSnippet adds a new apparmor snippet both de-duplicated and optimized for the parser. 144 // 145 // Conceptually the function takes a parametric template and a single value to 146 // remember. The resulting snippet text is a single entry resulting from the 147 // expanding the template and all the unique values observed, in the order they 148 // were observed. 149 // 150 // The template is expressed as a slice of strings, with the parameter 151 // automatically injected between any two of them, or in the special case of 152 // only one fragment, after that fragment. 153 // 154 // The resulting expansion depends on the number of values seen. If only one 155 // value is seen the resulting snippet is just the plain string one would 156 // expect if no parametric optimization had taken place. If more than one 157 // distinct value was seen then the resulting apparmor rule uses alternation 158 // syntax {param1,param2,...,paramN} which has better compilation time and 159 // memory complexity as compared to a set of naive expansions of the full 160 // snippet one after another. 161 // 162 // For example the code: 163 // 164 // AddParametricSnippet([]string{"/dev/", "rw,"}, "sda1") 165 // AddParametricSnippet([]string{"/dev/", "rw,"}, "sda3") 166 // AddParametricSnippet([]string{"/dev/", "rw,"}, "sdb2") 167 // 168 // Results in a single apparmor rule: 169 // 170 // "/dev/{sda1,sda3,sdb2} rw," 171 // 172 // This function should be used whenever the apparmor template features more 173 // than one use of "**" syntax (which represent arbitrary many directories or 174 // files) and a variable component, like a device name or similar. Repeated 175 // instances of this pattern require exponential memory when compiled with 176 // apparmor_parser -O no-expr-simplify. 177 func (spec *Specification) AddParametricSnippet(templateFragment []string, value string) { 178 if len(spec.securityTags) == 0 { 179 return 180 } 181 182 // We need to build a template string from the templateFragment. 183 // 184 // If only a single fragment is given we just append our "###PARM###": 185 // []string{"prefix"} becomes -> "prefix###PARAM###" 186 // 187 // Otherwise we join the strings: 188 // []string{"pre","post"} becomes -> "pre###PARAM###post" 189 // 190 // This seems to be the most natural way of doing this. 191 var template string 192 switch len(templateFragment) { 193 case 0: 194 return 195 case 1: 196 template = templateFragment[0] + "###PARAM###" 197 default: 198 template = strings.Join(templateFragment, "###PARAM###") 199 } 200 201 // Expand the spec's parametric snippets, initializing each 202 // part of the map as needed 203 if spec.parametricSnippets == nil { 204 spec.parametricSnippets = make(map[string]map[string]*strutil.OrderedSet) 205 } 206 for _, tag := range spec.securityTags { 207 expansions := spec.parametricSnippets[tag] 208 if expansions == nil { 209 expansions = make(map[string]*strutil.OrderedSet) 210 spec.parametricSnippets[tag] = expansions 211 } 212 values := expansions[template] 213 if values == nil { 214 values = &strutil.OrderedSet{} 215 expansions[template] = values 216 } 217 // Now that everything is initialized, insert value into the 218 // spec.parametricSnippets[<tag>][<template>]'s OrderedSet. 219 values.Put(value) 220 } 221 } 222 223 // AddUpdateNS adds a new apparmor snippet for the snap-update-ns program. 224 func (spec *Specification) AddUpdateNS(snippet string) { 225 spec.updateNS.Put(snippet) 226 } 227 228 // AddUpdateNSf formats and adds a new apparmor snippet for the snap-update-ns program. 229 func (spec *Specification) AddUpdateNSf(f string, args ...interface{}) { 230 spec.AddUpdateNS(fmt.Sprintf(f, args...)) 231 } 232 233 // UpdateNSIndexOf returns the index of a previously added snippet. 234 func (spec *Specification) UpdateNSIndexOf(snippet string) (idx int, ok bool) { 235 return spec.updateNS.IndexOf(snippet) 236 } 237 238 func (spec *Specification) emitLayout(si *snap.Info, layout *snap.Layout) { 239 emit := spec.AddUpdateNSf 240 241 emit(" # Layout %s\n", layout.String()) 242 path := si.ExpandSnapVariables(layout.Path) 243 switch { 244 case layout.Bind != "": 245 bind := si.ExpandSnapVariables(layout.Bind) 246 // Allow bind mounting the layout element. 247 emit(" mount options=(rbind, rw) \"%s/\" -> \"%s/\",\n", bind, path) 248 emit(" mount options=(rprivate) -> \"%s/\",\n", path) 249 emit(" umount \"%s/\",\n", path) 250 // Allow constructing writable mimic in both bind-mount source and mount point. 251 GenWritableProfile(emit, path, 2) // At least / and /some-top-level-directory 252 GenWritableProfile(emit, bind, 4) // At least /, /snap/, /snap/$SNAP_NAME and /snap/$SNAP_NAME/$SNAP_REVISION 253 case layout.BindFile != "": 254 bindFile := si.ExpandSnapVariables(layout.BindFile) 255 // Allow bind mounting the layout element. 256 emit(" mount options=(bind, rw) \"%s\" -> \"%s\",\n", bindFile, path) 257 emit(" mount options=(rprivate) -> \"%s\",\n", path) 258 emit(" umount \"%s\",\n", path) 259 // Allow constructing writable mimic in both bind-mount source and mount point. 260 GenWritableFileProfile(emit, path, 2) // At least / and /some-top-level-directory 261 GenWritableFileProfile(emit, bindFile, 4) // At least /, /snap/, /snap/$SNAP_NAME and /snap/$SNAP_NAME/$SNAP_REVISION 262 case layout.Type == "tmpfs": 263 emit(" mount fstype=tmpfs tmpfs -> \"%s/\",\n", path) 264 emit(" mount options=(rprivate) -> \"%s/\",\n", path) 265 emit(" umount \"%s/\",\n", path) 266 // Allow constructing writable mimic to mount point. 267 GenWritableProfile(emit, path, 2) // At least / and /some-top-level-directory 268 case layout.Symlink != "": 269 // Allow constructing writable mimic to symlink parent directory. 270 emit(" \"%s\" rw,\n", path) 271 GenWritableProfile(emit, path, 2) // At least / and /some-top-level-directory 272 } 273 } 274 275 // AddLayout adds apparmor snippets based on the layout of the snap. 276 // 277 // The per-snap snap-update-ns profiles are composed via a template and 278 // snippets for the snap. The snippets may allow (depending on the snippet): 279 // - mount profiles via the content interface 280 // - creating missing mount point directories under $SNAP* (the 'tree' 281 // of permissions is needed for SecureMkDirAll that uses 282 // open(..., O_NOFOLLOW) and mkdirat() using the resulting file descriptor) 283 // - creating a placeholder directory in /tmp/.snap/ in the per-snap mount 284 // namespace to support writable mimic which uses tmpfs and bind mount to 285 // poke holes in arbitrary read-only locations 286 // - mounting/unmounting any part of $SNAP into placeholder directory 287 // - mounting/unmounting tmpfs over the original $SNAP/** location 288 // - mounting/unmounting from placeholder back to $SNAP/** (for reconstructing 289 // the data) 290 // Importantly, the above mount operations are happening within the per-snap 291 // mount namespace. 292 func (spec *Specification) AddLayout(snapInfo *snap.Info) { 293 if len(snapInfo.Layout) == 0 { 294 return 295 } 296 297 // Walk the layout elements in deterministic order, by mount point name. 298 paths := make([]string, 0, len(snapInfo.Layout)) 299 for path := range snapInfo.Layout { 300 paths = append(paths, path) 301 } 302 sort.Strings(paths) 303 304 // Get tags describing all apps and hooks. 305 tags := make([]string, 0, len(snapInfo.Apps)+len(snapInfo.Hooks)) 306 for _, app := range snapInfo.Apps { 307 tags = append(tags, app.SecurityTag()) 308 } 309 for _, hook := range snapInfo.Hooks { 310 tags = append(tags, hook.SecurityTag()) 311 } 312 313 // Append layout snippets to all tags; the layout applies equally to the 314 // entire snap as the entire snap uses one mount namespace. 315 if spec.snippets == nil { 316 spec.snippets = make(map[string][]string) 317 } 318 for _, tag := range tags { 319 for _, path := range paths { 320 snippet := snippetFromLayout(snapInfo.Layout[path]) 321 spec.snippets[tag] = append(spec.snippets[tag], snippet) 322 } 323 sort.Strings(spec.snippets[tag]) 324 } 325 326 // Append update-ns snippets that allow constructing the layout. 327 for _, path := range paths { 328 layout := snapInfo.Layout[path] 329 spec.emitLayout(snapInfo, layout) 330 } 331 } 332 333 // AddExtraLayouts adds additional apparmor snippets based on the provided layouts. 334 // The function is in part identical to AddLayout, except that it considers only the 335 // layouts passed as parameters instead of those declared in the snap.Info structure. 336 // XXX: Should we just combine this into AddLayout instead of this separate 337 // function? 338 func (spec *Specification) AddExtraLayouts(si *snap.Info, layouts []snap.Layout) { 339 for _, layout := range layouts { 340 spec.emitLayout(si, &layout) 341 } 342 } 343 344 // AddOvername adds AppArmor snippets allowing remapping of snap 345 // directories for parallel installed snaps 346 // 347 // Specifically snap-update-ns will apply the following bind mounts 348 // - /snap/foo_bar -> /snap/foo 349 // - /var/snap/foo_bar -> /var/snap/foo 350 // - /home/joe/snap/foo_bar -> /home/joe/snap/foo 351 func (spec *Specification) AddOvername(si *snap.Info) { 352 if si.InstanceKey == "" { 353 return 354 } 355 var buf bytes.Buffer 356 357 // /snap/foo_bar -> /snap/foo 358 fmt.Fprintf(&buf, " # Allow parallel instance snap mount namespace adjustments\n") 359 fmt.Fprintf(&buf, " mount options=(rw rbind) /snap/%s/ -> /snap/%s/,\n", si.InstanceName(), si.SnapName()) 360 // /var/snap/foo_bar -> /var/snap/foo 361 fmt.Fprintf(&buf, " mount options=(rw rbind) /var/snap/%s/ -> /var/snap/%s/,\n", si.InstanceName(), si.SnapName()) 362 spec.AddUpdateNS(buf.String()) 363 } 364 365 // isProbably writable returns true if the path is probably representing writable area. 366 func isProbablyWritable(path string) bool { 367 return strings.HasPrefix(path, "/var/snap/") || strings.HasPrefix(path, "/home/") || strings.HasPrefix(path, "/root/") 368 } 369 370 // isProbablyPresent returns true if the path is probably already present. 371 // 372 // This is used as a simple hint to not inject writable path rules for things 373 // that we don't expect to create as they are already present in the skeleton 374 // file-system tree. 375 func isProbablyPresent(path string) bool { 376 return path == "/" || path == "/snap" || path == "/var" || path == "/var/snap" || path == "/tmp" || path == "/usr" || path == "/etc" 377 } 378 379 // GenWritableMimicProfile generates apparmor rules for a writable mimic at the given path. 380 func GenWritableMimicProfile(emit func(f string, args ...interface{}), path string, assumedPrefixDepth int) { 381 emit(" # Writable mimic %s\n", path) 382 383 iter, err := strutil.NewPathIterator(path) 384 if err != nil { 385 panic(err) 386 } 387 388 // Handle the prefix that is assumed to exist first. 389 emit(" # .. permissions for traversing the prefix that is assumed to exist\n") 390 for iter.Next() { 391 if iter.Depth() < assumedPrefixDepth { 392 emit(" \"%s\" r,\n", iter.CurrentPath()) 393 } 394 } 395 396 // Rewind the iterator and handle the part that needs to be created. 397 iter.Rewind() 398 for iter.Next() { 399 if iter.Depth() < assumedPrefixDepth { 400 continue 401 } 402 // Assume that the mimic needs to be created at the given prefix of the 403 // full mimic path. This is called a mimic "variant". Both of the paths 404 // must end with a slash as this is important for apparmor file vs 405 // directory path semantics. 406 mimicPath := filepath.Join(iter.CurrentBase(), iter.CurrentCleanName()) + "/" 407 mimicAuxPath := filepath.Join("/tmp/.snap", iter.CurrentPath()) + "/" 408 emit(" # .. variant with mimic at %s\n", mimicPath) 409 emit(" # Allow reading the mimic directory, it must exist in the first place.\n") 410 emit(" \"%s\" r,\n", mimicPath) 411 emit(" # Allow setting the read-only directory aside via a bind mount.\n") 412 emit(" \"%s\" rw,\n", mimicAuxPath) 413 emit(" mount options=(rbind, rw) \"%s\" -> \"%s\",\n", mimicPath, mimicAuxPath) 414 emit(" # Allow mounting tmpfs over the read-only directory.\n") 415 emit(" mount fstype=tmpfs options=(rw) tmpfs -> \"%s\",\n", mimicPath) 416 emit(" # Allow creating empty files and directories for bind mounting things\n" + 417 " # to reconstruct the now-writable parent directory.\n") 418 emit(" \"%s*/\" rw,\n", mimicAuxPath) 419 emit(" \"%s*/\" rw,\n", mimicPath) 420 emit(" mount options=(rbind, rw) \"%s*/\" -> \"%s*/\",\n", mimicAuxPath, mimicPath) 421 emit(" \"%s*\" rw,\n", mimicAuxPath) 422 emit(" \"%s*\" rw,\n", mimicPath) 423 emit(" mount options=(bind, rw) \"%s*\" -> \"%s*\",\n", mimicAuxPath, mimicPath) 424 emit(" # Allow unmounting the auxiliary directory.\n" + 425 " # TODO: use fstype=tmpfs here for more strictness (LP: #1613403)\n") 426 emit(" mount options=(rprivate) -> \"%s\",\n", mimicAuxPath) 427 emit(" umount \"%s\",\n", mimicAuxPath) 428 emit(" # Allow unmounting the destination directory as well as anything\n" + 429 " # inside. This lets us perform the undo plan in case the writable\n" + 430 " # mimic fails.\n") 431 emit(" mount options=(rprivate) -> \"%s\",\n", mimicPath) 432 emit(" mount options=(rprivate) -> \"%s*\",\n", mimicPath) 433 emit(" mount options=(rprivate) -> \"%s*/\",\n", mimicPath) 434 emit(" umount \"%s\",\n", mimicPath) 435 emit(" umount \"%s*\",\n", mimicPath) 436 emit(" umount \"%s*/\",\n", mimicPath) 437 } 438 } 439 440 // GenWritableFileProfile writes a profile for snap-update-ns for making given file writable. 441 func GenWritableFileProfile(emit func(f string, args ...interface{}), path string, assumedPrefixDepth int) { 442 if path == "/" { 443 return 444 } 445 if isProbablyWritable(path) { 446 emit(" # Writable file %s\n", path) 447 emit(" \"%s\" rw,\n", path) 448 for p := parent(path); !isProbablyPresent(p); p = parent(p) { 449 emit(" \"%s/\" rw,\n", p) 450 } 451 } else { 452 parentPath := parent(path) 453 GenWritableMimicProfile(emit, parentPath, assumedPrefixDepth) 454 } 455 } 456 457 // GenWritableProfile generates a profile for snap-update-ns for making given directory writable. 458 func GenWritableProfile(emit func(f string, args ...interface{}), path string, assumedPrefixDepth int) { 459 if path == "/" { 460 return 461 } 462 if isProbablyWritable(path) { 463 emit(" # Writable directory %s\n", path) 464 for p := path; !isProbablyPresent(p); p = parent(p) { 465 emit(" \"%s/\" rw,\n", p) 466 } 467 } else { 468 parentPath := parent(path) 469 GenWritableMimicProfile(emit, parentPath, assumedPrefixDepth) 470 } 471 } 472 473 // parent returns the parent directory of a given path. 474 func parent(path string) string { 475 result, _ := filepath.Split(path) 476 result = filepath.Clean(result) 477 return result 478 } 479 480 // Snippets returns a deep copy of all the added application snippets. 481 func (spec *Specification) Snippets() map[string][]string { 482 tags := spec.SecurityTags() 483 snippets := make(map[string][]string, len(tags)) 484 for _, tag := range tags { 485 snippets[tag] = spec.snippetsForTag(tag) 486 } 487 return snippets 488 } 489 490 // SnippetForTag returns a combined snippet for given security tag with 491 // individual snippets joined with the newline character. Empty string is 492 // returned for non-existing security tag. 493 func (spec *Specification) SnippetForTag(tag string) string { 494 return strings.Join(spec.snippetsForTag(tag), "\n") 495 } 496 497 // SecurityTags returns a list of security tags which have a snippet. 498 func (spec *Specification) SecurityTags() []string { 499 var tags []string 500 seen := make(map[string]bool, len(spec.snippets)) 501 for t := range spec.snippets { 502 tags = append(tags, t) 503 seen[t] = true 504 } 505 for t := range spec.dedupSnippets { 506 if !seen[t] { 507 tags = append(tags, t) 508 } 509 } 510 for t := range spec.parametricSnippets { 511 if !seen[t] { 512 tags = append(tags, t) 513 } 514 } 515 sort.Strings(tags) 516 return tags 517 } 518 519 func (spec *Specification) snippetsForTag(tag string) []string { 520 snippets := append([]string(nil), spec.snippets[tag]...) 521 // First add any deduplicated snippets 522 if bag := spec.dedupSnippets[tag]; bag != nil { 523 snippets = append(snippets, bag.Items()...) 524 } 525 templates := make([]string, 0, len(spec.parametricSnippets[tag])) 526 // Then add any parametric snippets 527 for template := range spec.parametricSnippets[tag] { 528 templates = append(templates, template) 529 } 530 sort.Strings(templates) 531 for _, template := range templates { 532 bag := spec.parametricSnippets[tag][template] 533 if bag != nil { 534 values := bag.Items() 535 switch len(values) { 536 case 0: 537 /* no values, nothing to do */ 538 case 1: 539 snippet := strings.Replace(template, "###PARAM###", values[0], -1) 540 snippets = append(snippets, snippet) 541 default: 542 snippet := strings.Replace(template, "###PARAM###", 543 fmt.Sprintf("{%s}", strings.Join(values, ",")), -1) 544 snippets = append(snippets, snippet) 545 } 546 } 547 } 548 return snippets 549 } 550 551 // UpdateNS returns a deep copy of all the added snap-update-ns snippets. 552 func (spec *Specification) UpdateNS() []string { 553 return spec.updateNS.Items() 554 } 555 556 func snippetFromLayout(layout *snap.Layout) string { 557 mountPoint := layout.Snap.ExpandSnapVariables(layout.Path) 558 if layout.Bind != "" || layout.Type == "tmpfs" { 559 return fmt.Sprintf("# Layout path: %s\n\"%s{,/**}\" mrwklix,", mountPoint, mountPoint) 560 } else if layout.BindFile != "" { 561 return fmt.Sprintf("# Layout path: %s\n\"%s\" mrwklix,", mountPoint, mountPoint) 562 } 563 return fmt.Sprintf("# Layout path: %s\n# (no extra permissions required for symlink)", mountPoint) 564 } 565 566 // Implementation of methods required by interfaces.Specification 567 568 // AddConnectedPlug records apparmor-specific side-effects of having a connected plug. 569 func (spec *Specification) AddConnectedPlug(iface interfaces.Interface, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 570 type definer interface { 571 AppArmorConnectedPlug(spec *Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error 572 } 573 if iface, ok := iface.(definer); ok { 574 restore := spec.setScope(plug.SecurityTags()) 575 defer restore() 576 return iface.AppArmorConnectedPlug(spec, plug, slot) 577 } 578 return nil 579 } 580 581 // AddConnectedSlot records apparmor-specific side-effects of having a connected slot. 582 func (spec *Specification) AddConnectedSlot(iface interfaces.Interface, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 583 type definer interface { 584 AppArmorConnectedSlot(spec *Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error 585 } 586 if iface, ok := iface.(definer); ok { 587 restore := spec.setScope(slot.SecurityTags()) 588 defer restore() 589 return iface.AppArmorConnectedSlot(spec, plug, slot) 590 } 591 return nil 592 } 593 594 // AddPermanentPlug records apparmor-specific side-effects of having a plug. 595 func (spec *Specification) AddPermanentPlug(iface interfaces.Interface, plug *snap.PlugInfo) error { 596 type definer interface { 597 AppArmorPermanentPlug(spec *Specification, plug *snap.PlugInfo) error 598 } 599 if iface, ok := iface.(definer); ok { 600 restore := spec.setScope(plug.SecurityTags()) 601 defer restore() 602 return iface.AppArmorPermanentPlug(spec, plug) 603 } 604 return nil 605 } 606 607 // AddPermanentSlot records apparmor-specific side-effects of having a slot. 608 func (spec *Specification) AddPermanentSlot(iface interfaces.Interface, slot *snap.SlotInfo) error { 609 type definer interface { 610 AppArmorPermanentSlot(spec *Specification, slot *snap.SlotInfo) error 611 } 612 if iface, ok := iface.(definer); ok { 613 restore := spec.setScope(slot.SecurityTags()) 614 defer restore() 615 return iface.AppArmorPermanentSlot(spec, slot) 616 } 617 return nil 618 } 619 620 // SetUsesPtraceTrace records when to omit explicit ptrace deny rules. 621 func (spec *Specification) SetUsesPtraceTrace() { 622 spec.usesPtraceTrace = true 623 } 624 625 // UsesPtraceTrace returns whether ptrace is being used by any of the interfaces 626 // in the spec. 627 func (spec *Specification) UsesPtraceTrace() bool { 628 return spec.usesPtraceTrace 629 } 630 631 // SetSuppressPtraceTrace to request explicit ptrace deny rules 632 func (spec *Specification) SetSuppressPtraceTrace() { 633 spec.suppressPtraceTrace = true 634 } 635 636 // SuppressPtraceTrace returns whether ptrace should be suppressed as dictated 637 // by any of the interfaces in the spec. 638 func (spec *Specification) SuppressPtraceTrace() bool { 639 return spec.suppressPtraceTrace 640 } 641 642 // SetUsesSysModuleCapability records that some interface has granted the 643 // sys_module capability 644 func (spec *Specification) SetUsesSysModuleCapability() { 645 spec.usesSysModuleCapability = true 646 } 647 648 // UsesSysModuleCapability returns whether the sys_module capability is being 649 // used by any of the interfaces in the spec. 650 func (spec *Specification) UsesSysModuleCapability() bool { 651 return spec.usesSysModuleCapability 652 } 653 654 // SetSuppressSysModuleCapability to request explicit denial of the sys_module 655 // capability 656 func (spec *Specification) SetSuppressSysModuleCapability() { 657 spec.suppressSysModuleCapability = true 658 } 659 660 // SuppressSysModuleCapability returns whether any interface has asked the 661 // sys_module capability to be explicitly denied 662 func (spec *Specification) SuppressSysModuleCapability() bool { 663 return spec.suppressSysModuleCapability 664 } 665 666 // SetSuppressHomeIx records suppression of the ix rules for the home 667 // interface. 668 func (spec *Specification) SetSuppressHomeIx() { 669 spec.suppressHomeIx = true 670 } 671 672 // SuppressHomeIx returns whether the ix rules of the home interface should be 673 // suppressed. 674 func (spec *Specification) SuppressHomeIx() bool { 675 return spec.suppressHomeIx 676 }