69 lines
2.0 KiB
Go
69 lines
2.0 KiB
Go
// Package execuser resolves user overrides for guest exec processes.
|
|
package execuser
|
|
|
|
import (
|
|
"fmt"
|
|
userpkg "os/user"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
)
|
|
|
|
// Resolve parses a user override in user[:group] form.
|
|
//
|
|
// User and group components may be names or numeric IDs.
|
|
func Resolve(spec string) (*syscall.Credential, error) {
|
|
// Split the override into user and optional group
|
|
userPart, groupPart, _ := strings.Cut(spec, ":")
|
|
if userPart == "" {
|
|
return nil, fmt.Errorf("invalid user override %q", spec)
|
|
}
|
|
|
|
// Resolve the user by numeric ID or name
|
|
var user *userpkg.User
|
|
var err error
|
|
|
|
if _, err = strconv.ParseUint(userPart, 10, 32); err == nil {
|
|
user, err = userpkg.LookupId(userPart)
|
|
} else {
|
|
user, err = userpkg.Lookup(userPart)
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to resolve user %q: %w", userPart, err)
|
|
}
|
|
|
|
// User resolution yields strings, so we need to parse them first
|
|
uid, err := strconv.ParseUint(user.Uid, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse UID %q: %w", user.Uid, err)
|
|
}
|
|
|
|
gid, err := strconv.ParseUint(user.Gid, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse GID %q: %w", user.Gid, err)
|
|
}
|
|
|
|
// Keep the user's primary GID when no group override is provided
|
|
if groupPart == "" {
|
|
return &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}, nil
|
|
}
|
|
|
|
// When group override is provided, and it's numeric, use it directly
|
|
if groupGID, err := strconv.ParseUint(groupPart, 10, 32); err == nil {
|
|
return &syscall.Credential{Uid: uint32(uid), Gid: uint32(groupGID)}, nil
|
|
}
|
|
|
|
// Otherwise, resolve named group override through the system user database
|
|
group, err := userpkg.LookupGroup(groupPart)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to resolve group %q: %w", groupPart, err)
|
|
}
|
|
|
|
groupGID, err := strconv.ParseUint(group.Gid, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse GID %q: %w", group.Gid, err)
|
|
}
|
|
|
|
return &syscall.Credential{Uid: uint32(uid), Gid: uint32(groupGID)}, nil
|
|
}
|