1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
|
package idmap
import (
"fmt"
"strconv"
"strings"
)
// Entry is a single idmap entry (line).
type Entry struct {
IsUID bool `json:"Isuid"`
IsGID bool `json:"Isgid"`
HostID int64 `json:"Hostid"` // id as seen on the host - i.e. 100000
NSID int64 `json:"Nsid"` // id as seen in the ns - i.e. 0
MapRange int64 `json:"Maprange"`
}
// ToLXCString converts an Entry into its LXC representation.
func (e *Entry) ToLXCString() []string {
if e.IsUID && e.IsGID {
return []string{
fmt.Sprintf("u %d %d %d", e.NSID, e.HostID, e.MapRange),
fmt.Sprintf("g %d %d %d", e.NSID, e.HostID, e.MapRange),
}
}
if e.IsUID {
return []string{fmt.Sprintf("u %d %d %d", e.NSID, e.HostID, e.MapRange)}
}
return []string{fmt.Sprintf("g %d %d %d", e.NSID, e.HostID, e.MapRange)}
}
// HostIDsIntersect checks whether the provided entry intersects with the host IDs of the existing one.
func (e *Entry) HostIDsIntersect(i Entry) bool {
if (e.IsUID && i.IsUID) || (e.IsGID && i.IsGID) {
switch {
case isBetween(e.HostID, i.HostID, i.HostID+i.MapRange):
return true
case isBetween(i.HostID, e.HostID, e.HostID+e.MapRange):
return true
case isBetween(e.HostID+e.MapRange, i.HostID, i.HostID+i.MapRange):
return true
case isBetween(i.HostID+i.MapRange, e.HostID, e.HostID+e.MapRange):
return true
}
}
return false
}
// Intersects checks whether the provided entry intersects with the existing one.
func (e *Entry) Intersects(i Entry) bool {
if (e.IsUID && i.IsUID) || (e.IsGID && i.IsGID) {
switch {
case isBetween(e.HostID, i.HostID, i.HostID+i.MapRange-1):
return true
case isBetween(i.HostID, e.HostID, e.HostID+e.MapRange-1):
return true
case isBetween(e.HostID+e.MapRange-1, i.HostID, i.HostID+i.MapRange-1):
return true
case isBetween(i.HostID+i.MapRange-1, e.HostID, e.HostID+e.MapRange-1):
return true
case isBetween(e.NSID, i.NSID, i.NSID+i.MapRange-1):
return true
case isBetween(i.NSID, e.NSID, e.NSID+e.MapRange-1):
return true
case isBetween(e.NSID+e.MapRange-1, i.NSID, i.NSID+i.MapRange-1):
return true
case isBetween(i.NSID+i.MapRange-1, e.NSID, e.NSID+e.MapRange-1):
return true
}
}
return false
}
// HostIDsCoveredBy returns whether or not the entry is covered by the supplied host UID and GID ID maps.
// If e.IsUID is true then host IDs must be covered by an entry in allowedHostUIDs, and if e.IsGID is true then
// host IDs must be covered by an entry in allowedHostGIDs.
func (e *Entry) HostIDsCoveredBy(allowedHostUIDs []Entry, allowedHostGIDs []Entry) bool {
if !e.IsUID && !e.IsGID {
return false // This is an invalid idmap entry.
}
isUIDAllowed := false
if e.IsUID {
for _, allowedIDMap := range allowedHostUIDs {
if !allowedIDMap.IsUID {
continue
}
if e.HostID >= allowedIDMap.HostID && (e.HostID+e.MapRange) <= (allowedIDMap.HostID+allowedIDMap.MapRange) {
isUIDAllowed = true
break
}
}
}
isGIDAllowed := false
if e.IsGID {
for _, allowedIDMap := range allowedHostGIDs {
if !allowedIDMap.IsGID {
continue
}
if e.HostID >= allowedIDMap.HostID && (e.HostID+e.MapRange) <= (allowedIDMap.HostID+allowedIDMap.MapRange) {
isGIDAllowed = true
break
}
}
}
return e.IsUID == isUIDAllowed && e.IsGID == isGIDAllowed
}
// Usable checks whether the entry is usable on this system.
func (e *Entry) Usable() error {
kernelIdmap, err := NewSetFromCurrentProcess()
if err != nil {
return err
}
kernelRanges, err := kernelIdmap.ValidRanges()
if err != nil {
return err
}
// Validate the uid map.
if e.IsUID {
valid := false
for _, kernelRange := range kernelRanges {
if !kernelRange.IsUID {
continue
}
if kernelRange.Contains(e.HostID) && kernelRange.Contains(e.HostID+e.MapRange-1) {
valid = true
break
}
}
if !valid {
return fmt.Errorf("The '%s' map can't work in the current user namespace", e.ToLXCString())
}
}
// Validate the gid map.
if e.IsGID {
valid := false
for _, kernelRange := range kernelRanges {
if !kernelRange.IsGID {
continue
}
if kernelRange.Contains(e.HostID) && kernelRange.Contains(e.HostID+e.MapRange-1) {
valid = true
break
}
}
if !valid {
return fmt.Errorf("The '%s' map can't work in the current user namespace", e.ToLXCString())
}
}
return nil
}
// Clone gets a distinct copy of the entry.
func (e *Entry) Clone() *Entry {
return &Entry{e.IsUID, e.IsGID, e.HostID, e.NSID, e.MapRange}
}
func (e *Entry) parse(s string) error {
split := strings.Split(s, ":")
var err error
if len(split) != 4 {
return fmt.Errorf("Bad idmap: %q", s)
}
switch split[0] {
case "u":
e.IsUID = true
case "g":
e.IsGID = true
case "b":
e.IsUID = true
e.IsGID = true
default:
return fmt.Errorf("Bad idmap type in %q", s)
}
nsid, err := strconv.ParseUint(split[1], 10, 32)
if err != nil {
return err
}
e.NSID = int64(nsid)
hostid, err := strconv.ParseUint(split[2], 10, 32)
if err != nil {
return err
}
e.HostID = int64(hostid)
maprange, err := strconv.ParseUint(split[3], 10, 32)
if err != nil {
return err
}
e.MapRange = int64(maprange)
// Wrap around.
if e.HostID+e.MapRange < e.HostID || e.NSID+e.MapRange < e.NSID {
return fmt.Errorf("Bad mapping: id wraparound")
}
return nil
}
// Shift a uid from the host into the container
// I.e. 0 -> 1000 -> 101000.
func (e *Entry) shiftIntoNS(id int64) (int64, error) {
if id < e.NSID || id >= e.NSID+e.MapRange {
// This mapping doesn't apply.
return 0, fmt.Errorf("ID mapping doesn't apply")
}
return id - e.NSID + e.HostID, nil
}
// Shift a uid from the container back to the host
// I.e. 101000 -> 1000.
func (e *Entry) shiftFromNS(id int64) (int64, error) {
if id < e.HostID || id >= e.HostID+e.MapRange {
// This mapping doesn't apply.
return 0, fmt.Errorf("ID mapping doesn't apply")
}
return id - e.HostID + e.NSID, nil
}
|