fix(auth): pin native auth storage operations

This commit is contained in:
2026-08-17 14:52:21 +02:00
parent 6d438f4c7e
commit cb0e873ed7
23 changed files with 3016 additions and 1385 deletions
+209 -144
View File
@@ -9,8 +9,6 @@ import (
"encoding/json"
"errors"
"io"
"os"
"path/filepath"
"regexp"
"strings"
"unicode"
@@ -123,21 +121,21 @@ func execute(input request) (response, error) {
return response{Version: protocolVersion, OK: true, Prepared: true}, nil
}
if input.Operation == "read-auth-config" {
root, err := existingPrivateRoot(input.Root)
if err != nil {
return response{}, errInvalid
}
contents, err := safeio.ReadCanonicalPrivateRegular(filepath.Join(root, input.Filename), maximumAuthConfigBytes)
if err != nil {
return response{}, errInvalid
}
return contentResponse(true, contents), nil
return readRootPrivateRegular(input.Root, input.Filename)
}
if input.Operation == "read-local-users" {
return readRootPrivateRegular(input.Root, input.Filename)
}
if !validDirectory(input.Directory) {
return response{}, errInvalid
}
directory, err := storageDirectory(input.Root, input.Directory)
if err != nil {
layout, err := openStorageLayout(input.Root, true)
if err != nil || layout == nil {
return response{}, errInvalid
}
defer layout.Close()
directory := layout.directory(input.Directory)
if directory == nil {
return response{}, errInvalid
}
switch input.Operation {
@@ -162,7 +160,7 @@ func execute(input request) (response, error) {
if err != nil {
return response{}, errInvalid
}
if err := safeio.ReplaceCanonicalRegular(filepath.Join(directory, input.Filename), contents, 0o600); err != nil {
if err := directory.ReplaceRegular(input.Filename, contents); err != nil {
return response{}, errInvalid
}
return response{Version: protocolVersion, OK: true, Replaced: true}, nil
@@ -177,46 +175,32 @@ func execute(input request) (response, error) {
if limit == 0 {
limit = defaultMaximumEntries
}
afterName := ""
if input.Continuation {
page, err := safeio.ListCanonicalPrivateDirectoryPage(
directory,
limit,
input.AfterName,
digestFilename.MatchString,
)
if err != nil {
return response{}, errInvalid
}
more := page.More
return response{Version: protocolVersion, OK: true, Entries: &page.Entries, More: &more}, nil
afterName = input.AfterName
}
entries, err := safeio.ListCanonicalPrivateDirectory(directory, limit)
page, err := directory.ListPage(limit, afterName, recordListName(input.Directory), recordListLinks(input.Directory))
if err != nil {
return response{}, errInvalid
}
for _, entry := range entries {
if !digestFilename.MatchString(entry.Name) && !(input.Directory == "oidc" && (claimFilename.MatchString(entry.Name) || oidcSlotFilename.MatchString(entry.Name))) {
return response{}, errInvalid
}
if input.Continuation {
more := page.More
return response{Version: protocolVersion, OK: true, Entries: &page.Entries, More: &more}, nil
}
return response{Version: protocolVersion, OK: true, Entries: &entries}, nil
if page.More {
return response{}, errInvalid
}
return response{Version: protocolVersion, OK: true, Entries: &page.Entries}, nil
case "claim-consume":
return claimConsume(directory, input.Filename)
case "read-claim":
contents, found, err := safeio.ReadCanonicalPrivateClaim(
filepath.Join(directory, input.Filename),
filepath.Join(directory, asClaimFilename(input.Filename)),
maximumOIDCStateBytes,
)
contents, found, err := directory.ReadClaim(input.Filename, asClaimFilename(input.Filename), maximumOIDCStateBytes)
if err != nil {
return response{}, errInvalid
}
return contentResponse(found, contents), nil
case "remove-claim":
removed, err := safeio.RemoveCanonicalPrivateClaim(
filepath.Join(directory, input.Filename),
filepath.Join(directory, asClaimFilename(input.Filename)),
)
removed, err := directory.RemoveClaim(input.Filename, asClaimFilename(input.Filename))
if err != nil {
return response{}, errInvalid
}
@@ -234,7 +218,7 @@ func validOperationShape(input request) bool {
switch input.Operation {
case "validate-root", "ensure-layout":
return input.Directory == "" && input.Filename == "" && noContents && noMaximumEntries && noAfterName && noContinuation
case "read-auth-config":
case "read-auth-config", "read-local-users":
return input.Directory == "" && authConfigFilename.MatchString(input.Filename) && noContents && noMaximumEntries && noAfterName && noContinuation
case "create", "replace":
return noMaximumEntries && noAfterName && noContinuation && (digestFilename.MatchString(input.Filename) || (input.Operation == "create" && input.Directory == "oidc" && oidcSlotFilename.MatchString(input.Filename)))
@@ -252,78 +236,170 @@ func validOperationShape(input request) bool {
}
}
func preflightRoot(root string) (bool, error) {
if !filepath.IsAbs(root) || filepath.Clean(root) != root || strings.IndexFunc(root, unicode.IsControl) >= 0 {
return false, errInvalid
}
return safeio.PreflightPrivateDirectory(root)
type storageLayout struct {
root safeio.PrivateDirectoryHandle
sessions safeio.PrivateDirectoryHandle
oidc safeio.PrivateDirectoryHandle
}
func existingPrivateRoot(root string) (string, error) {
exists, err := preflightRoot(root)
if err != nil || !exists || safeio.ValidatePrivateDirectory(root) != nil {
return "", errInvalid
func (layout *storageLayout) Close() {
if layout == nil {
return
}
return root, nil
if layout.oidc != nil {
_ = layout.oidc.Close()
layout.oidc = nil
}
if layout.sessions != nil {
_ = layout.sessions.Close()
layout.sessions = nil
}
if layout.root != nil {
_ = layout.root.Close()
layout.root = nil
}
}
func (layout *storageLayout) closeChildren() {
if layout == nil {
return
}
if layout.oidc != nil {
_ = layout.oidc.Close()
layout.oidc = nil
}
if layout.sessions != nil {
_ = layout.sessions.Close()
layout.sessions = nil
}
}
func (layout *storageLayout) directory(name string) safeio.PrivateDirectoryHandle {
if layout == nil {
return nil
}
if name == "sessions" {
return layout.sessions
}
if name == "oidc" {
return layout.oidc
}
return nil
}
func validStorageRoot(root string) bool {
return strings.IndexFunc(root, unicode.IsControl) < 0
}
// openStorageLayout keeps the root descriptor/handle open from its initial canonical validation
// through every child observation. The side-effect-free path performs a second child pass after
// the testable race boundary and retains those final handles; it never validates an earlier,
// discarded exists result.
func openStorageLayout(root string, ensure bool) (*storageLayout, error) {
if !validStorageRoot(root) {
return nil, errInvalid
}
rootHandle, found, err := safeio.OpenPrivateDirectory(root, ensure)
if err != nil {
return nil, errInvalid
}
if !found {
return nil, nil
}
layout := &storageLayout{root: rootHandle}
failed := true
defer func() {
if failed {
layout.Close()
}
}()
safeio.NotifyPrivateDirectoryTestHookForTest("after-auth-root-open")
if err := layout.openChildren(ensure); err != nil {
return nil, errInvalid
}
safeio.NotifyPrivateDirectoryTestHookForTest("after-auth-layout-first-pass")
if !ensure {
layout.closeChildren()
if err := layout.openChildren(false); err != nil {
return nil, errInvalid
}
}
if layout.root.Validate() != nil || (layout.sessions != nil && layout.sessions.Validate() != nil) ||
(layout.oidc != nil && layout.oidc.Validate() != nil) {
return nil, errInvalid
}
failed = false
return layout, nil
}
func (layout *storageLayout) openChildren(ensure bool) error {
if layout == nil || layout.root == nil || layout.root.Validate() != nil {
return errInvalid
}
for _, name := range []string{"sessions", "oidc"} {
child, found, err := layout.root.OpenChild(name, ensure)
if err != nil || (ensure && !found) {
if child != nil {
_ = child.Close()
}
return errInvalid
}
if !found {
continue
}
if child.Validate() != nil {
_ = child.Close()
return errInvalid
}
if name == "sessions" {
layout.sessions = child
} else {
layout.oidc = child
}
}
if layout.root.Validate() != nil {
return errInvalid
}
return nil
}
func validateStorageLayout(root string) error {
exists, err := preflightRoot(root)
layout, err := openStorageLayout(root, false)
if err != nil {
return errInvalid
}
if !exists {
return nil
}
existingChildren := make([]string, 0, 2)
for _, directory := range []string{"sessions", "oidc"} {
path := filepath.Join(root, directory)
if filepath.Dir(path) != root {
return errInvalid
}
childExists, err := safeio.PreflightPrivateDirectory(path)
if err != nil {
return errInvalid
}
if childExists {
existingChildren = append(existingChildren, path)
}
}
// Close permission/identity races between the individual side-effect-free preflights.
if safeio.ValidatePrivateDirectory(root) != nil {
return errInvalid
}
for _, directory := range []string{"sessions", "oidc"} {
if _, err := safeio.PreflightPrivateDirectory(filepath.Join(root, directory)); err != nil {
return errInvalid
}
}
for _, path := range existingChildren {
if safeio.ValidatePrivateDirectory(path) != nil {
return errInvalid
}
if layout != nil {
layout.Close()
}
return nil
}
func ensureStorageLayout(root string) error {
if _, err := preflightRoot(root); err != nil || safeio.EnsurePrivateDirectory(root) != nil {
return errInvalid
}
for _, directory := range []string{"sessions", "oidc"} {
path := filepath.Join(root, directory)
if filepath.Dir(path) != root || safeio.EnsurePrivateDirectory(path) != nil {
return errInvalid
}
}
if safeio.ValidatePrivateDirectory(root) != nil ||
safeio.ValidatePrivateDirectory(filepath.Join(root, "sessions")) != nil ||
safeio.ValidatePrivateDirectory(filepath.Join(root, "oidc")) != nil {
layout, err := openStorageLayout(root, true)
if err != nil || layout == nil {
return errInvalid
}
layout.Close()
return nil
}
func readRootPrivateRegular(root, filename string) (response, error) {
if !validStorageRoot(root) {
return response{}, errInvalid
}
directory, found, err := safeio.OpenPrivateDirectory(root, false)
if err != nil || !found || directory == nil {
return response{}, errInvalid
}
defer directory.Close()
safeio.NotifyPrivateDirectoryTestHookForTest("after-auth-root-open")
contents, found, err := directory.ReadRegular(filename, maximumAuthConfigBytes)
if err != nil || !found || directory.Validate() != nil {
return response{}, errInvalid
}
return contentResponse(true, contents), nil
}
func contentResponse(found bool, contents []byte) response {
if !found {
return response{Version: protocolVersion, OK: true}
@@ -331,17 +407,6 @@ func contentResponse(found bool, contents []byte) response {
return response{Version: protocolVersion, OK: true, Found: true, ContentBase64: base64.StdEncoding.EncodeToString(contents)}
}
func storageDirectory(root, directory string) (string, error) {
if _, err := preflightRoot(root); err != nil || safeio.EnsurePrivateDirectory(root) != nil {
return "", errInvalid
}
path := filepath.Join(root, directory)
if filepath.Dir(path) != root || safeio.EnsurePrivateDirectory(path) != nil {
return "", errInvalid
}
return path, nil
}
func validDirectory(value string) bool {
return value == "sessions" || value == "oidc"
}
@@ -361,67 +426,67 @@ func decodeContents(input request) ([]byte, error) {
return contents, nil
}
func createPrivate(directory, filename string, contents []byte) (bool, error) {
path := filepath.Join(directory, filename)
if _, err := os.Lstat(path); err == nil {
if safeio.ValidatePrivateRegular(path) != nil {
return false, errInvalid
}
return false, nil
} else if !errors.Is(err, os.ErrNotExist) {
func createPrivate(directory safeio.PrivateDirectoryHandle, filename string, contents []byte) (bool, error) {
created, err := directory.CreateRegular(filename, contents)
if err != nil {
return false, errInvalid
}
if err := safeio.WriteCanonicalNewPrivateFile(path, contents, 0o600); err == nil {
return true, nil
}
if safeio.ValidatePrivateRegular(path) == nil {
return false, nil
}
return false, errInvalid
return created, nil
}
func readPrivate(directory, filename string, maximum int64) ([]byte, bool, error) {
path := filepath.Join(directory, filename)
if _, err := os.Lstat(path); errors.Is(err, os.ErrNotExist) {
return nil, false, nil
} else if err != nil {
return nil, false, errInvalid
}
contents, err := safeio.ReadCanonicalPrivateRegular(path, maximum)
func readPrivate(directory safeio.PrivateDirectoryHandle, filename string, maximum int64) ([]byte, bool, error) {
contents, found, err := directory.ReadRegular(filename, maximum)
if err != nil {
return nil, false, errInvalid
}
return contents, true, nil
return contents, found, nil
}
func removePrivate(directory, filename string) (bool, error) {
path := filepath.Join(directory, filename)
if _, err := os.Lstat(path); errors.Is(err, os.ErrNotExist) {
return false, nil
} else if err != nil {
func removePrivate(directory safeio.PrivateDirectoryHandle, filename string) (bool, error) {
removed, err := directory.RemoveRegular(filename)
if err != nil {
return false, errInvalid
}
if err := safeio.RemoveCanonicalPrivateRegular(path); err != nil {
return false, errInvalid
}
return true, nil
return removed, nil
}
func claimConsume(directory, filename string) (response, error) {
source := filepath.Join(directory, filename)
claim := filepath.Join(directory, asClaimFilename(filename))
claimed, err := safeio.ClaimCanonicalPrivateRegular(source, claim)
func recordListName(directory string) func(string) bool {
if directory == "sessions" {
return digestFilename.MatchString
}
return func(name string) bool {
return digestFilename.MatchString(name) || claimFilename.MatchString(name) || oidcSlotFilename.MatchString(name)
}
}
func recordListLinks(directory string) func(string, uint64) bool {
if directory == "sessions" {
return func(name string, links uint64) bool {
return digestFilename.MatchString(name) && links == 1
}
}
return func(name string, links uint64) bool {
if digestFilename.MatchString(name) || claimFilename.MatchString(name) {
return links == 1 || links == 2
}
return oidcSlotFilename.MatchString(name) && links == 1
}
}
func claimConsume(directory safeio.PrivateDirectoryHandle, filename string) (response, error) {
claim := asClaimFilename(filename)
claimed, err := directory.ClaimRegular(filename, claim)
if err != nil {
return response{}, errInvalid
}
if !claimed {
return response{Version: protocolVersion, OK: true}, nil
}
contents, found, err := safeio.ReadCanonicalPrivateClaim(source, claim, maximumOIDCStateBytes)
contents, found, err := directory.ReadClaim(filename, claim, maximumOIDCStateBytes)
if err != nil || !found {
return response{}, errInvalid
}
removed, err := safeio.RemoveCanonicalPrivateClaim(source, claim)
removed, err := directory.RemoveClaim(filename, claim)
if err != nil || !removed {
return response{}, errInvalid
}