From 1be8de6f5ca4c7e40f5999ce4f960010fc36acb1 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Thu, 28 Aug 2025 00:22:55 +0900 Subject: [PATCH] internal/app: less strict username regex Use the default value of NAME_REGEX from adduser. Should not hurt compatibility while being less strict. Signed-off-by: Ophestra --- internal/app/process_linux.go | 6 +++--- internal/app/seal_linux.go | 19 +++++++------------ internal/app/username.go | 12 ++++++++++++ 3 files changed, 22 insertions(+), 15 deletions(-) create mode 100644 internal/app/username.go diff --git a/internal/app/process_linux.go b/internal/app/process_linux.go index 57b49ee..c7da474 100644 --- a/internal/app/process_linux.go +++ b/internal/app/process_linux.go @@ -44,7 +44,7 @@ func (seal *outcome) Run(rs *RunState) error { defer func() { var revertErr error storeErr := new(StateStoreError) - storeErr.Inner, storeErr.DoErr = store.Do(seal.user.aid.unwrap(), func(c state.Cursor) { + storeErr.Inner, storeErr.DoErr = store.Do(seal.user.identity.unwrap(), func(c state.Cursor) { revertErr = func() error { storeErr.InnerErr = deferredStoreFunc(c) @@ -102,7 +102,7 @@ func (seal *outcome) Run(rs *RunState) error { // passed through to shim by hsu shimEnv + "=" + strconv.Itoa(fd), // interpreted by hsu - "HAKUREI_APP_ID=" + seal.user.aid.String(), + "HAKUREI_APP_ID=" + seal.user.identity.String(), } } @@ -155,7 +155,7 @@ func (seal *outcome) Run(rs *RunState) error { PID: cmd.Process.Pid, Time: *rs.Time, } - earlyStoreErr.Inner, earlyStoreErr.DoErr = store.Do(seal.user.aid.unwrap(), func(c state.Cursor) { + earlyStoreErr.Inner, earlyStoreErr.DoErr = store.Do(seal.user.identity.unwrap(), func(c state.Cursor) { earlyStoreErr.InnerErr = c.Save(&sd, seal.ct) }) } diff --git a/internal/app/seal_linux.go b/internal/app/seal_linux.go index 9572e9f..be18a2a 100644 --- a/internal/app/seal_linux.go +++ b/internal/app/seal_linux.go @@ -10,7 +10,6 @@ import ( "io/fs" "os" "path" - "regexp" "slices" "strconv" "strings" @@ -59,8 +58,6 @@ var ( ErrPulseMode = errors.New("unexpected pulse socket mode") ) -var posixUsername = regexp.MustCompilePOSIX("^[a-z_]([A-Za-z0-9_-]{0,31}|[A-Za-z0-9_-]{0,30}\\$)$") - // outcome stores copies of various parts of [hst.Config] type outcome struct { // copied from initialising [app] @@ -135,8 +132,7 @@ func (share *shareHost) runtime() *container.Absolute { // hsuUser stores post-hsu credentials and metadata type hsuUser struct { - // identity - aid *stringPair[int] + identity *stringPair[int] // target uid resolved by hid:aid uid *stringPair[int] @@ -172,25 +168,24 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co seal.ct = ct } - // allowed aid range 0 to 9999, this is checked again in hsu + // allowed identity range 0 to 9999, this is checked again in hsu if config.Identity < 0 || config.Identity > 9999 { return hlog.WrapErr(ErrIdent, fmt.Sprintf("identity %d out of range", config.Identity)) } seal.user = hsuUser{ - aid: newInt(config.Identity), + identity: newInt(config.Identity), home: config.Home, username: config.Username, } if seal.user.username == "" { seal.user.username = "chronos" - } else if !posixUsername.MatchString(seal.user.username) || - len(seal.user.username) >= sysconf(_SC_LOGIN_NAME_MAX) { + } else if !isValidUsername(seal.user.username) { return hlog.WrapErr(ErrName, fmt.Sprintf("invalid user name %q", seal.user.username)) } - if u, err := sys.Uid(seal.user.aid.unwrap()); err != nil { + if u, err := sys.Uid(seal.user.identity.unwrap()); err != nil { return err } else { seal.user.uid = newInt(u) @@ -318,7 +313,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co runtimeDir := share.sc.SharePath.Append("runtime") seal.sys.Ensure(runtimeDir.String(), 0700) seal.sys.UpdatePermType(system.User, runtimeDir.String(), acl.Execute) - runtimeDirInst := runtimeDir.Append(seal.user.aid.String()) + runtimeDirInst := runtimeDir.Append(seal.user.identity.String()) seal.sys.Ensure(runtimeDirInst.String(), 0700) seal.sys.UpdatePermType(system.User, runtimeDirInst.String(), acl.Read, acl.Write, acl.Execute) seal.container.Tmpfs(container.AbsFHSRunUser, 1<<12, 0755) @@ -329,7 +324,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co tmpdir := share.sc.SharePath.Append("tmpdir") seal.sys.Ensure(tmpdir.String(), 0700) seal.sys.UpdatePermType(system.User, tmpdir.String(), acl.Execute) - tmpdirInst := tmpdir.Append(seal.user.aid.String()) + tmpdirInst := tmpdir.Append(seal.user.identity.String()) seal.sys.Ensure(tmpdirInst.String(), 01700) seal.sys.UpdatePermType(system.User, tmpdirInst.String(), acl.Read, acl.Write, acl.Execute) // mount inner /tmp from share so it shares persistence and storage behaviour of host /tmp diff --git a/internal/app/username.go b/internal/app/username.go new file mode 100644 index 0000000..566daf5 --- /dev/null +++ b/internal/app/username.go @@ -0,0 +1,12 @@ +package app + +import "regexp" + +// nameRegex is the default NAME_REGEX value from adduser. +var nameRegex = regexp.MustCompilePOSIX(`^[a-zA-Z][a-zA-Z0-9_-]*\$?$`) + +// isValidUsername returns whether the argument is a valid username +func isValidUsername(username string) bool { + return len(username) < sysconf(_SC_LOGIN_NAME_MAX) && + nameRegex.MatchString(username) +}