diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-11-04 08:00:37 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-11-04 08:24:41 +0900 |
| commit | cb9ebf0e1541c0e49380cda6d4c2d619a0e9933f (patch) | |
| tree | 8bd0bfe3714fd5975dcacb69c0a2afcd2e04c687 /hst | |
| parent | 9a2a7b749f41f73c2c11e4a4557516307838457b (diff) | |
hst/grp_pwd: specify new uid format
This leaves slots available for additional uid ranges in Rosa OS.
This breaks all existing installations! Users are required to fix ownership manually.
Closes #18.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'hst')
| -rw-r--r-- | hst/config.go | 2 | ||||
| -rw-r--r-- | hst/container.go | 5 | ||||
| -rw-r--r-- | hst/grp_pwd.go | 61 | ||||
| -rw-r--r-- | hst/grp_pwd_test.go | 37 |
4 files changed, 99 insertions, 6 deletions
diff --git a/hst/config.go b/hst/config.go index ee31a555..d7078551 100644 --- a/hst/config.go +++ b/hst/config.go @@ -59,7 +59,7 @@ func (config *Config) Validate() error { } // this is checked again in hsu - if config.Identity < IdentityMin || config.Identity > IdentityMax { + if config.Identity < IdentityStart || config.Identity > IdentityEnd { return &AppError{Step: "validate configuration", Err: ErrIdentityBounds, Msg: "identity " + strconv.Itoa(config.Identity) + " out of range"} } diff --git a/hst/container.go b/hst/container.go index 75643913..f9cc95ed 100644 --- a/hst/container.go +++ b/hst/container.go @@ -20,11 +20,6 @@ const ( WaitDelayDefault = 5 * time.Second // WaitDelayMax is used if WaitDelay exceeds its value. WaitDelayMax = 30 * time.Second - - // IdentityMin is the minimum value of [Config.Identity]. This is enforced by cmd/hsu. - IdentityMin = 0 - // IdentityMax is the maximum value of [Config.Identity]. This is enforced by cmd/hsu. - IdentityMax = 9999 ) const ( diff --git a/hst/grp_pwd.go b/hst/grp_pwd.go new file mode 100644 index 00000000..1a5c962e --- /dev/null +++ b/hst/grp_pwd.go @@ -0,0 +1,61 @@ +package hst + +import ( + "fmt" + "strconv" +) + +const ( + // UserOffset is the offset for UID and GID ranges for each user. + UserOffset = 100000 + // RangeSize is the size of each UID and GID range. + RangeSize = UserOffset / 10 + + // IdentityStart is the first [Config.Identity] value. This is enforced in cmd/hsu. + IdentityStart = 0 + // IdentityEnd is the last [Config.Identity] value. This is enforced in cmd/hsu. + IdentityEnd = AppEnd - AppStart + + // AppStart is the first app user UID and GID. + AppStart = RangeSize * 1 + // AppEnd is the last app user UID and GID. + AppEnd = AppStart + RangeSize - 1 + + /* these are for Rosa OS: use the ranges below to determine whether a process is isolated */ + + // IsolatedStart is the start of UID and GID for fully isolated sandboxed processes. + IsolatedStart = RangeSize * 9 + // IsolatedEnd is the end of UID and GID for fully isolated sandboxed processes. + IsolatedEnd = IsolatedStart + RangeSize - 1 +) + +// A UID represents a kernel uid in the init namespace. +type UID uint32 + +// String returns the username corresponding to this uid. +// +// Not safe against untrusted input. +func (uid UID) String() string { + appid := uid % UserOffset + userid := uid / UserOffset + if appid >= IsolatedStart && appid <= IsolatedEnd { + return fmt.Sprintf("u%d_i%d", userid, appid-IsolatedStart) + } else if appid >= AppStart && appid <= AppEnd { + return fmt.Sprintf("u%d_a%d", userid, appid-AppStart) + } else { + return strconv.Itoa(int(uid)) + } +} + +// A GID represents a kernel gid in the init namespace. +type GID uint32 + +// String returns the group name corresponding to this gid. +// +// Not safe against untrusted input. +func (gid GID) String() string { return UID(gid).String() } + +// ToUser returns a [hst.UID] value from userid and appid. +// +// Not safe against untrusted input. +func ToUser[U int | uint32](userid, appid U) U { return userid*UserOffset + AppStart + appid } diff --git a/hst/grp_pwd_test.go b/hst/grp_pwd_test.go new file mode 100644 index 00000000..2d01c122 --- /dev/null +++ b/hst/grp_pwd_test.go @@ -0,0 +1,37 @@ +package hst_test + +import ( + "strconv" + "testing" + + "hakurei.app/hst" +) + +func TestUIDString(t *testing.T) { + t.Parallel() + + testCases := []struct { + val uint32 + want string + }{ + {hst.AppStart + hst.IdentityStart, "u0_a0"}, // uidStart + {hst.ToUser[uint32](hst.RangeSize-1, hst.IdentityEnd), "u9999_a9999"}, // uidEnd + + {hst.IsolatedStart + hst.IdentityStart, "u0_i0"}, // isolatedStart + {(hst.RangeSize-1)*hst.UserOffset + hst.IsolatedEnd, "u9999_i9999"}, // isolatedEnd + + {0, "0"}, // out of bounds + } + for _, tc := range testCases { + t.Run(strconv.Itoa(int(tc.val)), func(t *testing.T) { + t.Parallel() + + if got := hst.UID(tc.val).String(); got != tc.want { + t.Fatalf("UID.String: %q, want %q", got, tc.want) + } + if got := hst.GID(tc.val).String(); got != tc.want { + t.Fatalf("GID.String: %q, want %q", got, tc.want) + } + }) + } +} |
