package host import ( "bytes" "crypto/sha256" "encoding/hex" "encoding/json" "io" "os" "path/filepath" "slices" "strings" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/instancectl" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/rootstate" ) type claimUnitProgram struct { Schema int Host string Args, Env []string } func readClaimUnitProgram(shape claimUnitShape) (claimUnitProgram, error) { if !provision.ValidHostShape(shape.Host) { return claimUnitProgram{}, os.ErrPermission } f, err := rootstate.OpenAuthorityFile(filepath.Join("/etc/carlos/claim-units", shape.Host+".json")) if err != nil { return claimUnitProgram{}, err } defer f.Close() raw, err := io.ReadAll(io.LimitReader(f, rootstate.MaxRecord+1)) if err != nil { return claimUnitProgram{}, err } return decodeClaimUnitProgram(raw, shape) } func decodeClaimUnitProgram(raw []byte, shape claimUnitShape) (claimUnitProgram, error) { var p claimUnitProgram sum := sha256.Sum256(raw) dec := json.NewDecoder(bytes.NewReader(raw)) dec.DisallowUnknownFields() if len(raw) > rootstate.MaxRecord || hex.EncodeToString(sum[:]) != shape.Program || dec.Decode(&p) != nil || dec.Decode(new(any)) != io.EOF || p.Schema != 1 || p.Host != shape.Host || len(p.Args) > 256 || len(p.Env) > 1024 { return p, os.ErrPermission } for _, arg := range p.Args { if strings.ContainsRune(arg, 0) { return p, os.ErrPermission } } seen := map[string]bool{} for _, env := range p.Env { key, _, ok := strings.Cut(env, "=") if !ok || key == "" || strings.ContainsRune(env, 0) || seen[key] { return p, os.ErrPermission } seen[key] = true } return p, nil } func (h *ClaimLaunchRoot) boundLaunchSpec(q hostbox.RoleLaunchRequest) (instancectl.Launch, bool, error) { var launch instancectl.Launch host := q.Reservation.Target.Host unit, _, err := claimLaunchPlacement(h.Units, host) if err != nil { return launch, false, err } if unit == instancectl.UnitName(instancectl.RoleRun, host) { return launch, false, nil } if h.unitBindings == nil { return launch, true, os.ErrPermission } b, err := h.unitBindings.read(host) if err != nil { return launch, true, err } // Older bindings only describe placement and retain the ordinary isolated // launch contract. Production configuration requires a program digest. if b.Shape.Program == "" { return launch, false, nil } if b.Retiring || b.Retired || b.Shape.Unit != unit || h.unitProgram == nil { return launch, true, os.ErrPermission } p, err := h.unitProgram(b.Shape) if err != nil { return launch, true, err } if q.Spec.Identity.Config != b.Shape.Program || !slices.Equal(p.Args, q.Spec.Args) || !slices.Equal(p.Env, q.Spec.Env) { return launch, true, os.ErrPermission } return instancectl.Launch{Schema: instancectl.LaunchSchema, Host: host, Argv: append([]string{filepath.Join(instancectl.LiveDir, host)}, p.Args...), Env: append([]string(nil), p.Env...)}, true, nil }