github.com/apptainer/singularity@v3.1.1+incompatible/internal/app/singularity/capability_manage_linux.go (about) 1 // Copyright (c) 2018-2019, Sylabs Inc. All rights reserved. 2 // This software is licensed under a 3-clause BSD license. Please consult the 3 // LICENSE.md file distributed with the sources of this project regarding your 4 // rights to use or distribute this software. 5 6 package singularity 7 8 import ( 9 "fmt" 10 "os" 11 "syscall" 12 13 "github.com/sylabs/singularity/internal/pkg/sylog" 14 "github.com/sylabs/singularity/internal/pkg/util/user" 15 "github.com/sylabs/singularity/pkg/util/capabilities" 16 ) 17 18 // CapManageConfig specifies what capability set to edit in the capability file 19 type CapManageConfig struct { 20 Caps string 21 User string 22 Group string 23 } 24 25 type manageType struct { 26 UserFn func(*capabilities.Config, string, []string) error 27 GroupFn func(*capabilities.Config, string, []string) error 28 } 29 30 // CapabilityAdd adds the specified capability set to the capability file 31 func CapabilityAdd(capFile string, c CapManageConfig) error { 32 addType := manageType{ 33 UserFn: func(c *capabilities.Config, a string, b []string) error { 34 return c.AddUserCaps(a, b) 35 }, 36 GroupFn: func(c *capabilities.Config, a string, b []string) error { 37 return c.AddGroupCaps(a, b) 38 }, 39 } 40 41 return manageCaps(capFile, c, addType) 42 } 43 44 // CapabilityDrop drops the specified capability set from the capability file 45 func CapabilityDrop(capFile string, c CapManageConfig) error { 46 dropType := manageType{ 47 UserFn: func(c *capabilities.Config, a string, b []string) error { 48 return c.DropUserCaps(a, b) 49 }, 50 GroupFn: func(c *capabilities.Config, a string, b []string) error { 51 return c.DropGroupCaps(a, b) 52 }, 53 } 54 55 return manageCaps(capFile, c, dropType) 56 } 57 58 func manageCaps(capFile string, c CapManageConfig, t manageType) error { 59 if os.Getuid() != 0 { 60 return fmt.Errorf("while managing capability file: only root user can manage capabilities") 61 } 62 63 oldmask := syscall.Umask(0) 64 defer syscall.Umask(oldmask) 65 66 file, err := os.OpenFile(capFile, os.O_RDWR|os.O_CREATE, 0644) 67 if err != nil { 68 return fmt.Errorf("while opening capability config file: %s", err) 69 } 70 defer file.Close() 71 72 capConfig, err := capabilities.ReadFrom(file) 73 if err != nil { 74 return fmt.Errorf("while parsing capability config data: %s", err) 75 } 76 77 caps, ign := capabilities.Split(c.Caps) 78 if len(ign) > 0 { 79 sylog.Warningf("Ignoring unknown capabilities: %s", ign) 80 } 81 82 if c.User == "" && c.Group == "" { 83 return fmt.Errorf("no user or group specified") 84 } 85 86 if c.User != "" { 87 if !userExists(c.User) { 88 return fmt.Errorf("while setting capabilities for user %s: user does not exist", c.User) 89 } 90 91 if err := t.UserFn(capConfig, c.User, caps); err != nil { 92 return fmt.Errorf("while setting capabilities for user %s: %s", c.User, err) 93 } 94 } 95 96 if c.Group != "" { 97 if !groupExists(c.Group) { 98 return fmt.Errorf("while setting capabilities for group %s: group does not exist", c.Group) 99 } 100 101 if err := t.GroupFn(capConfig, c.Group, caps); err != nil { 102 return fmt.Errorf("while setting capabilities for group %s: %s", c.Group, err) 103 } 104 } 105 106 if err := file.Truncate(0); err != nil { 107 return fmt.Errorf("while truncating capability config file: %s", err) 108 } 109 110 if n, err := file.Seek(0, os.SEEK_SET); err != nil || n != 0 { 111 return fmt.Errorf("failed to reset %s cursor: %s", file.Name(), err) 112 } 113 114 if _, err := capConfig.WriteTo(file); err != nil { 115 return fmt.Errorf("while writing capability data to file: %s", err) 116 } 117 118 if err := file.Sync(); err != nil { 119 return fmt.Errorf("failed to flush capability config file %s: %s", file.Name(), err) 120 } 121 122 return nil 123 } 124 125 func userExists(usr string) bool { 126 if _, err := user.GetPwNam(usr); err != nil { 127 return false 128 } 129 return true 130 } 131 132 func groupExists(group string) bool { 133 if _, err := user.GetGrNam(group); err != nil { 134 return false 135 } 136 return true 137 }