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