Files
Podman/newidmap.go

188 lines
4.9 KiB
Go

package Podman
import (
"bytes"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strconv"
"strings"
"syscall"
moby_user "github.com/moby/sys/user"
"kernel.org/pub/linux/libs/security/libcap/cap"
)
const subidUseFile = false
func NewIDMap(idMapName string) {
flag.Parse()
args := flag.Args()
var (
dir *os.File
err error
)
if len(args) < 4 {
log.Fatalf("%s [<pid>|fd:<pidfd>] <id> <lower id> <count> [ <id> <lower id> <count> ]", filepath.Base(os.Args[0]))
}
pid := args[0]
args = args[1:]
if len(args)%3 != 0 {
log.Fatalf("argument count is not a multiple of 3\n%s [<pid>|fd:<pidfd>] <id> <lower id> <count> [ <id> <lower id> <count> ]", filepath.Base(os.Args[0]))
}
if len(pid) > 3 && strings.HasPrefix(pid, "fd:") {
fd, err := strconv.Atoi(pid[3:])
if err != nil {
log.Fatalf("Invalid fd given: %s", pid)
}
dir = os.NewFile(uintptr(fd), pid[3:])
} else {
dir, err = os.Open(filepath.Join("/proc", pid))
if err != nil {
log.Fatalf("unable to find process %s", pid)
}
}
stat, err := dir.Stat()
if err != nil {
log.Fatalf("unable to find process %s", pid)
}
fstat := stat.Sys().(*syscall.Stat_t)
if os.Geteuid() != int(fstat.Uid) {
log.Fatal("You aren't allowed to do that :-P")
}
if os.Getegid() != int(fstat.Gid) {
log.Fatal("You aren't allowed to do that :-P")
}
var (
user Passwd
subid moby_user.SubID
passwds []Passwd = LoadAllPasswd()
)
subidstart := subidStart
for _, u := range passwds {
if u.UID == int(fstat.Uid) {
user = u
subid = moby_user.SubID{
Name: user.User,
SubID: subidstart,
Count: subidCount,
}
}
subidstart += subidCount
}
if user.User == "" {
log.Fatalf("Unable to find user")
}
if subidUseFile {
if idMapName == "uid_map" {
subid, err = getSubIDs("/etc/subuid", user.User, strconv.Itoa(user.UID))
} else {
subid, err = getSubIDs("/etc/subgid", user.User, strconv.Itoa(user.GID))
}
}
if err != nil {
log.Fatalf("Unable to get subid map for the user: %v", err)
}
idmaps, err := getIDMaps(int64(user.UID), subid, args)
if err != nil {
log.Fatalf("Unable to approve requested subid mappings: %v", err)
}
idMapFd, err := syscall.Openat(int(dir.Fd()), idMapName, os.O_WRONLY, 0o600)
if err != nil {
log.Fatalf("Unable to open process %s: %v", idMapName, err)
}
idMap := os.NewFile(uintptr(idMapFd), idMapName)
defer idMap.Close()
buf := &bytes.Buffer{}
for _, m := range idmaps {
fmt.Fprintf(buf, "%d %d %d\n", m.ID, m.ParentID, m.Count)
}
caps := cap.GetProc()
neededCap := cap.SETUID
if idMapName == "gid_map" {
neededCap = cap.SETGID
}
hasCap, _ := caps.GetFlag(cap.Effective, neededCap)
if !hasCap {
log.Printf("Wrong capability for setting %s: have %v need %v", idMapName, caps, neededCap)
}
// Only one write syscall is allowed
_, err = idMap.Write(buf.Bytes())
if err != nil {
log.Fatalf("Failed to write %s(%s) to process: %v", idMapName, buf.Bytes(), err)
}
}
func WriteSubids(file string) error {
buf := &bytes.Buffer{}
passwds := LoadAllPasswd()
subidstart := subidStart
for _, passwd := range passwds {
fmt.Fprintf(buf, "%s:%d:%d\n", passwd.User, subidstart, subidCount)
subidstart += subidCount
}
err := os.WriteFile(file, buf.Bytes(), 0o644)
if err != nil {
return err
}
return nil
}
func getIDMaps(id int64, subid moby_user.SubID, args []string) ([]moby_user.IDMap, error) {
var err error
idmap := moby_user.IDMap{}
idmaps := []moby_user.IDMap{}
for i, arg := range args {
switch i % 3 {
case 0:
idmap = moby_user.IDMap{}
idmap.ID, err = strconv.ParseInt(arg, 10, 64)
if err != nil {
return idmaps, fmt.Errorf("failed to parse uid as number: %v: %v", arg, err)
}
case 1:
idmap.ParentID, err = strconv.ParseInt(arg, 10, 64)
if err != nil {
return idmaps, fmt.Errorf("failed to parse parent uid as number: %v: %v", arg, err)
}
if idmap.ParentID != id && (idmap.ParentID < subid.SubID || idmap.ParentID >= subid.SubID+subid.Count) {
return idmaps, fmt.Errorf("parent ID: '%v' not in approved range: %v-%v", idmap.ParentID, subid.SubID, subid.SubID+subid.Count)
}
case 2:
idmap.Count, err = strconv.ParseInt(arg, 10, 64)
if err != nil {
return idmaps, fmt.Errorf("failed to parse count as number: %v: %v", arg, err)
}
end := idmap.ParentID + idmap.Count - 1
if end != id && (end < subid.SubID || end >= subid.SubID+subid.Count) {
return idmaps, fmt.Errorf("parent ID: '%v' not in approved range: %v-%v", end, subid.SubID, subid.SubID+subid.Count)
}
idmaps = append(idmaps, idmap)
}
}
return idmaps, nil
}
func getSubIDs(filename string, name string, id string) (moby_user.SubID, error) {
var subid moby_user.SubID
subids, err := moby_user.ParseSubIDFile(filename)
if err != nil {
log.Fatalf("Failed to read subuid file: %v", err)
}
for _, sub := range subids {
if sub.Name == name || sub.Name == id {
subid = sub
break
}
}
if subid.Name == "" {
return subid, fmt.Errorf("no entry in %q for %s", filename, name)
}
return subid, nil
}