Users and Groups
basaltc’s user database functions live in pwd.rs (~1,400 lines) plus crypt.rs (~440 lines) and sha512.rs (~300 lines).
The implementation reads /etc/passwd, /etc/group, and /etc/shadow through the VFS, parses the colon-separated entries, and exposes the standard POSIX passwd / group / spwd structs.
This page covers the function inventory, the file format expectations, the reentrancy split, and the password hashing surface.
Function Inventory
| Group | Functions |
|---|---|
Password database |
|
Group database |
|
Shadow database |
|
Login |
|
Password hashing |
|
Hashing |
|
File Format Expectations
basaltc reads three files via VFS, parsed line by line:
| File | Format |
|---|---|
|
|
|
|
|
|
basaltc opens each file with posix_open, reads it through stdio line by line with fgets, and parses each line into the appropriate struct.
The strings are allocated from a per-thread or per-call buffer depending on whether the caller is using the reentrant or non-reentrant variant.
getpwnam vs getpwnam_r
The non-reentrant interface returns a pointer to a per-thread static buffer:
pub unsafe extern "C" fn getpwnam(name: *const u8) -> *mut Passwd {
let buf = trona_posix::tls::current_pwd_buf(); // per-thread storage
let mut result: *mut Passwd = core::ptr::null_mut();
if getpwnam_r(name, buf, ..., &mut result) != 0 {
return core::ptr::null_mut();
}
result
}
The reentrant variant takes an explicit buffer:
pub unsafe extern "C" fn getpwnam_r(
name: *const u8,
pwd: *mut Passwd,
buf: *mut u8, buflen: usize,
result: *mut *mut Passwd,
) -> i32 {
// Open /etc/passwd
// Walk lines, looking for the first one whose name matches.
// For the matching line, copy the colon-separated fields into buf,
// populate pwd with pointers into buf,
// store pwd in *result, and return 0.
// On not-found, set *result = NULL and return 0.
// On error, set *result = NULL and return errno (positive).
}
The _r form is preferred for any new code.
Multiple calls to the non-_r form from the same thread overwrite the previous result; multiple calls from different threads do not interfere because the buffer is in TLS.
getpwent (Iteration)
getpwent iterates the password database one entry at a time:
setpwent();
struct passwd *p;
while ((p = getpwent()) != NULL) {
printf("%s : %d\n", p->pw_name, p->pw_uid);
}
endpwent();
basaltc maintains a per-thread FILE* cursor for the open /etc/passwd file (also in TLS).
setpwent opens the file (or rewinds it if already open), getpwent reads the next line, endpwent closes the file.
The same pattern applies to getgrent and getspent.
getgrouplist and initgroups
getgrouplist(user, group, groups, ngroups) enumerates the groups a user belongs to:
pub unsafe extern "C" fn getgrouplist(
user: *const u8, group: GidT,
groups: *mut GidT, ngroups: *mut i32,
) -> i32 {
// Walk /etc/group, collecting GIDs whose member list contains user.
// Always include the primary group (the second argument).
// Write the result into the groups array.
// Return the count, or -1 if the array was too small.
}
initgroups(user, group) calls getgrouplist and then setgroups to install the GID list as the calling process’s supplementary groups.
This is the typical operation a privilege-dropping daemon performs when switching to a non-root user.
Login
getlogin() returns the login name of the controlling terminal’s owner.
On SaltyOS this is the user that posix_getty / posix_login set when starting the session, communicated through a per-process environment variable (LOGNAME) or via a controlling-terminal lookup through posix_ttysrv.
getlogin_r(buf, len) is the reentrant variant taking a caller-supplied buffer.
Crypt and sha512
crypt(key, salt) is the classic Unix password hashing function:
char *hash = crypt("hunter2", "$6$abc1234$");
basaltc supports the SHA-512 crypt format ($6$…) used by modern Linux:
$6$salt$hash
The salt argument identifies the algorithm and the salt value:
| Prefix | Algorithm |
|---|---|
|
MD5 crypt — supported via |
|
SHA-256 crypt — not yet supported |
|
SHA-512 crypt — supported via |
(other) |
Returns NULL with errno = EINVAL |
sha512.rs is a standalone SHA-512 implementation in pure Rust:
pub struct Sha512Ctx { ... }
pub fn sha512_init(ctx: &mut Sha512Ctx);
pub fn sha512_update(ctx: &mut Sha512Ctx, data: &[u8]);
pub fn sha512_final(ctx: &mut Sha512Ctx, hash: &mut [u8; 64]);
The transform is the standard FIPS 180-4 SHA-512 (80 rounds, 64-bit words). basaltc does not use a vectorized implementation — the implementation is straightforward Rust loops, fast enough for password hashing (which is typically called rarely).
crypt_r(key, salt, data) is the reentrant variant taking a caller-supplied state struct.
basaltc supports both forms; the data struct is used to avoid the per-thread static buffer.
Limitations
-
No
/etc/shadowpermission checks — basaltc reads shadow through VFS and respects whatever VFS permissions are in place. There is no explicit check that the caller is root. -
No NSS / LDAP / NIS — only
/etc/passwd,/etc/group,/etc/shadoware consulted. Network-attached user databases would require a separate name-service-switch mechanism. -
No
getpw_*for the optional fields — basaltc populates the standard POSIX fields (pw_name,pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell) but ignores BSD-only fields likepw_class,pw_change,pw_expire. Ports that need these fields will get zero values. -
cryptblocks — there is no async or parallel crypt support. A program callingcryptin a loop is single-threaded for that loop.
Constraints
-
getpwnamand friends issue VFS reads on every call. There is no in-process cache. -
The non-reentrant variants share TLS buffers between functions in the same family — for example,
getpwnamandgetpwuidshareTls::libc_pwd_buf. Calling them in alternation overwrites the result. -
Maximum line length in
/etc/passwdparsing is 1024 bytes. Longer lines are silently truncated.
These match the constraints of typical small-system libcs and are sufficient for the SaltyOS port set.
Related Pages
-
Environment and Arguments —
getloginreads theLOGNAMEenvironment variable -
Files and Directories —
pwd/grpparsing uses the standard file I/O -
trona Boundary — TLS reentrancy buffers and the VFS read path
-
FreeBSD Compatibility — MD5 crypt is in
compat/freebsd/md5.rs