libwusers provides a POSIX wrapper around Windows users/groups API. It defines struct passwd and struct group
and implements POSIX API functions such as getuid, getgid, getgrgid, getgrnam, getpwnam, getpwuid (the list is not meant to be exhaustive…)
with native Windows API calls. Its goal is to simplify porting of open source software into the Windows ecosystem without touching the [L]GPL tar baby
of Cygwin/Msys. If you just hit "fatal error: pwd.h: No such file or directory", welcome home.
libwusers is being developed with two runtime targets in mind: MinGW and pure Windows API. It means two things in practice:
- Our API dependencies are limited to the intersection of APIs provided by MinGW and native Windows SDKs;
- Our POSIX headers don't conflict with POSIX headers provided by MinGW (IOW, it's safe to install them into
$(PREFIX)/$(TARGET)/include).
The Netapi32.lib interfaces involved are available since Windows 2000.
Since the primary consumer of libwusers is Project Rakko, real world testing (somewhat) ensures at least Windows 8.1+ support
in desktop mode. The need for UWP support is subject to debate (you need tilde expansion in a host-agnostic container? really?);
technically, it should at least be possible to inject simulated user data process-wide or on a per-thread basis. File an issue if you need it (or fork and code it).
Windows APIs are wchar_t*. POSIX APIs are char*. It's possible to specify any code page supported by Windows, including "default for the current user", "default for the current thread" (apparently, it is a thing) and UTF-8; include wusers/wuser_cpage.h for that.
The LM for time is time_t (seconds since the Unix epoch). Therefore time values are returned verbatim.
There is no direct search by RID in Windows API. Instead of guessing the intermediate SID authorities (which would have been extremely fragile), libwusers simply iterates over existing accounts (starting with cached records) looking for a match.
libwusers converts from USER_INFO_3 to struct passwd.
pw_nameis… well, the name. On Windows, it MAY contain spaces; make sure your project won't be confused. Queries by account name are case-insensitive.pw_uidandpw_gidare RIDs of the user and the user's primary group, respectively. WinXP APIs allow requesting SIDs instead of RIDs, but only the last component (=RID) fits within POSIX types.pw_gecosis the user's full name.pw_classis the string representation of the privilege level, i.e. one of the three strings: "User", "Administrator" or "Guest".pw_diris the user's profile folder. SinceNetUserGetInforeturns home directory information only on servers, some second-guessing is applied. First, if the user name matches%USERNAME%,%USERPROFILE%is returned as is. Second, if the user name is different, butdirname(%USERPROFILE)\\%USERNAME%exists and is a directory, it is returned as the best informed guess. Otherwise (or if libwusers is compiled with_WUSER_NO_HEURISTICS), and empty string is returned.- The second guess for
pw_shellis the value of%ComSpec%. - The account expiration time is returned as
pw_expire. - There is no corresponding field for
pw_change(requred password change time). Thereforepw_expireis returned if the password has not expired. If it has, the current time minus 86400 seconds (i.e. same time yesterday) is returned.
Group field translation logic is much more straightforward. gr_name is the group name, gr_mem is a null-terminated char* array initialized from GROUP_USER_INFO_0 values, gr_gid is the RID. gr_passwd has no Windows equivalent; an asterisk (*) is returned.
It may be possible to access more group and group membership information that an unpriviliged process can retrieve using NetGroupGetInfo() and NetGroupGetUsers() by using elevation, or the local group API ("local groups" aren't a subset of "groups", but a different object class), or the WMI API. All of these options can be explored; the question is, as always, the intended use case.
Memory ownership by libwusers is BSD-style, as documented in the respective OpenBSD manual pages: the library owns
- the last (translated) entry returned by non-reentrant APIs, together with its string data;
- the last batch of (untranslated) entries being iterated over with
*entAPI; - (as a courtesy) the last few hundred solitary user names and stringified UIDs.
The only difference is that stayopen in setpassent has no Windows equivalent (there are no files being kept "open",
at least on the surface) and is therefore disrespected.
Some of the memory management logic relies on the fact that pw_shell (the last char* field) is also the last field processed by pw_dup().
If you want to change that, search for reinterpret_cast<char*> to highlight these places in the code.
Semantically constant C-string values (such as * in lieu of passwords, or privilege class names), though syntactically mutable, MAY reside in read-only memory.
libwusers is free as in freedom and released into the public domain (where it rightfully belongs)
with a no-strings-attached CC0 license.
All bets are off. This project is being worked on in our spare time to support another project being worked on in our spare time.
There is no guarantee that someone would even respond in a timely fashion; but nevertheless, feel free and invited to drop a note,
report a bug, suggest a feature or contribute a change. After all, you are a techie, right? libwusers is of no use to you unless you are.
Um… Let's put it this way: don't do things you would be escorted out of a coffee shop for. That is, don't do them here.