mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-03 14:18:47 +00:00
Add support to core:windows to add/delete users.
main :: proc() {
using fmt;
using windows;
username := "testuser";
password := "testpass";
ok := add_user("", username, password);
fmt.printf("add_user: %v\n", ok);
pi := windows.PROCESS_INFORMATION{};
ok2, path := windows.add_user_profile(username);
fmt.printf("add_user_profile: %v, %v\n", ok2, path);
ok3 := windows.delete_user_profile(username);
fmt.printf("delete_user_profile: %v\n", ok3);
ok4 := windows.delete_user("", username);
fmt.printf("delete_user: %v\n", ok4);
// Has optional bool to not wait on the process before returning.
b := run_as_user(username, password, "C:\\Repro\\repro.exe", "Hellope!", &pi);
fmt.printf("run_as_user: %v %v\n", b, pi);
}
This commit is contained in:
@@ -10,3 +10,57 @@ foreign advapi32 {
|
|||||||
DesiredAccess: DWORD,
|
DesiredAccess: DWORD,
|
||||||
TokenHandle: ^HANDLE) -> BOOL ---
|
TokenHandle: ^HANDLE) -> BOOL ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Necessary to create a token to impersonate a user with for CreateProcessAsUser
|
||||||
|
@(default_calling_convention="stdcall")
|
||||||
|
foreign advapi32 {
|
||||||
|
LogonUserW :: proc(
|
||||||
|
lpszUsername: LPCWSTR,
|
||||||
|
lpszDomain: LPCWSTR,
|
||||||
|
lpszPassword: LPCWSTR,
|
||||||
|
dwLogonType: Logon32_Type,
|
||||||
|
dwLogonProvider: Logon32_Provider,
|
||||||
|
phToken: ^HANDLE,
|
||||||
|
) -> BOOL ---
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-lookupaccountnamew
|
||||||
|
// To look up the SID to use with DeleteProfileW.
|
||||||
|
LookupAccountNameW :: proc(
|
||||||
|
lpSystemName: wstring,
|
||||||
|
lpAccountName: wstring,
|
||||||
|
Sid: ^SID,
|
||||||
|
cbSid: ^DWORD,
|
||||||
|
ReferencedDomainName: wstring,
|
||||||
|
cchReferencedDomainName: ^DWORD,
|
||||||
|
peUse: ^SID_TYPE,
|
||||||
|
) -> BOOL ---
|
||||||
|
|
||||||
|
CreateProcessWithLogonW :: proc(
|
||||||
|
lpUsername: wstring,
|
||||||
|
lpDomain: wstring,
|
||||||
|
lpPassword: wstring,
|
||||||
|
dwLogonFlags: DWORD,
|
||||||
|
lpApplicationName: wstring,
|
||||||
|
lpCommandLine: wstring,
|
||||||
|
dwCreationFlags: DWORD,
|
||||||
|
lpEnvironment: LPVOID,
|
||||||
|
lpCurrentDirectory: wstring,
|
||||||
|
lpStartupInfo: LPSTARTUPINFO,
|
||||||
|
lpProcessInformation: LPPROCESS_INFORMATION,
|
||||||
|
) -> BOOL ---
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasuserw
|
||||||
|
CreateProcessAsUserW :: proc(
|
||||||
|
hToken: HANDLE,
|
||||||
|
lpApplicationName: wstring,
|
||||||
|
lpCommandLine: wstring,
|
||||||
|
lpProcessAttributes: LPSECURITY_ATTRIBUTES,
|
||||||
|
lpThreadAttributes: LPSECURITY_ATTRIBUTES,
|
||||||
|
bInheritHandles: BOOL,
|
||||||
|
dwCreationFlags: DWORD,
|
||||||
|
lpEnvironment: LPVOID,
|
||||||
|
lpCurrentDirectory: wstring,
|
||||||
|
lpStartupInfo: LPSTARTUPINFO,
|
||||||
|
lpProcessInformation: LPPROCESS_INFORMATION,
|
||||||
|
) -> BOOL ---;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package sys_windows
|
||||||
|
|
||||||
|
foreign import netapi32 "system:Netapi32.lib"
|
||||||
|
|
||||||
|
@(default_calling_convention="stdcall")
|
||||||
|
foreign netapi32 {
|
||||||
|
NetUserAdd :: proc(
|
||||||
|
servername: wstring,
|
||||||
|
level: DWORD,
|
||||||
|
user_info: ^USER_INFO_1, // Perhaps make this a #raw_union with USER_INFO1..4 when we need the other levels.
|
||||||
|
parm_err: ^DWORD,
|
||||||
|
) -> NET_API_STATUS ---;
|
||||||
|
NetUserDel :: proc(
|
||||||
|
servername: wstring,
|
||||||
|
username: wstring,
|
||||||
|
) -> NET_API_STATUS ---;
|
||||||
|
NetUserGetInfo :: proc(
|
||||||
|
servername: wstring,
|
||||||
|
username: wstring,
|
||||||
|
level: DWORD,
|
||||||
|
user_info: ^USER_INFO_1,
|
||||||
|
) -> NET_API_STATUS ---;
|
||||||
|
NetLocalGroupAddMembers :: proc(
|
||||||
|
servername: wstring,
|
||||||
|
groupname: wstring,
|
||||||
|
level: DWORD,
|
||||||
|
group_members_info: ^LOCALGROUP_MEMBERS_INFO_0, // Actually a variably sized array of these.
|
||||||
|
totalentries: DWORD,
|
||||||
|
) -> NET_API_STATUS ---;
|
||||||
|
NetLocalGroupDelMembers :: proc(
|
||||||
|
servername: wstring,
|
||||||
|
groupname: wstring,
|
||||||
|
level: DWORD,
|
||||||
|
group_members_info: ^LOCALGROUP_MEMBERS_INFO_0, // Actually a variably sized array of these.
|
||||||
|
totalentries: DWORD,
|
||||||
|
) -> NET_API_STATUS ---;
|
||||||
|
}
|
||||||
+448
-2
@@ -570,7 +570,8 @@ PROCESS_INFORMATION :: struct {
|
|||||||
dwThreadId: DWORD,
|
dwThreadId: DWORD,
|
||||||
}
|
}
|
||||||
|
|
||||||
STARTUPINFO :: struct {
|
// FYI: This is STARTUPINFOW, not STARTUPINFOA
|
||||||
|
STARTUPINFO :: struct #packed {
|
||||||
cb: DWORD,
|
cb: DWORD,
|
||||||
lpReserved: LPWSTR,
|
lpReserved: LPWSTR,
|
||||||
lpDesktop: LPWSTR,
|
lpDesktop: LPWSTR,
|
||||||
@@ -580,7 +581,7 @@ STARTUPINFO :: struct {
|
|||||||
dwXSize: DWORD,
|
dwXSize: DWORD,
|
||||||
dwYSize: DWORD,
|
dwYSize: DWORD,
|
||||||
dwXCountChars: DWORD,
|
dwXCountChars: DWORD,
|
||||||
dwYCountCharts: DWORD,
|
dwYCountChars: DWORD,
|
||||||
dwFillAttribute: DWORD,
|
dwFillAttribute: DWORD,
|
||||||
dwFlags: DWORD,
|
dwFlags: DWORD,
|
||||||
wShowWindow: WORD,
|
wShowWindow: WORD,
|
||||||
@@ -788,3 +789,448 @@ OSVERSIONINFOEXW :: struct {
|
|||||||
wProductType: UCHAR,
|
wProductType: UCHAR,
|
||||||
wReserved: UCHAR,
|
wReserved: UCHAR,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-quota_limits
|
||||||
|
// Used in LogonUserExW
|
||||||
|
PQUOTA_LIMITS :: struct {
|
||||||
|
PagedPoolLimit: SIZE_T,
|
||||||
|
NonPagedPoolLimit: SIZE_T,
|
||||||
|
MinimumWorkingSetSize: SIZE_T,
|
||||||
|
MaximumWorkingSetSize: SIZE_T,
|
||||||
|
PagefileLimit: SIZE_T,
|
||||||
|
TimeLimit: LARGE_INTEGER,
|
||||||
|
};
|
||||||
|
|
||||||
|
Logon32_Type :: enum DWORD {
|
||||||
|
INTERACTIVE = 2,
|
||||||
|
NETWORK = 3,
|
||||||
|
BATCH = 4,
|
||||||
|
SERVICE = 5,
|
||||||
|
UNLOCK = 7,
|
||||||
|
NETWORK_CLEARTEXT = 8,
|
||||||
|
NEW_CREDENTIALS = 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
Logon32_Provider :: enum DWORD {
|
||||||
|
DEFAULT = 0,
|
||||||
|
WINNT35 = 1,
|
||||||
|
WINNT40 = 2,
|
||||||
|
WINNT50 = 3,
|
||||||
|
VIRTUAL = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/profinfo/ns-profinfo-profileinfow
|
||||||
|
// Used in LoadUserProfileW
|
||||||
|
|
||||||
|
PROFILEINFOW :: struct {
|
||||||
|
dwSize: DWORD,
|
||||||
|
dwFlags: DWORD,
|
||||||
|
lpUserName: LPWSTR,
|
||||||
|
lpProfilePath: LPWSTR,
|
||||||
|
lpDefaultPath: LPWSTR,
|
||||||
|
lpServerName: LPWSTR,
|
||||||
|
lpPolicyPath: LPWSTR,
|
||||||
|
hProfile: HANDLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used in LookupAccountNameW
|
||||||
|
SID_NAME_USE :: distinct DWORD;
|
||||||
|
|
||||||
|
SID_TYPE :: enum SID_NAME_USE {
|
||||||
|
User = 1,
|
||||||
|
Group,
|
||||||
|
Domain,
|
||||||
|
Alias,
|
||||||
|
WellKnownGroup,
|
||||||
|
DeletedAccount,
|
||||||
|
Invalid,
|
||||||
|
Unknown,
|
||||||
|
Computer,
|
||||||
|
Label,
|
||||||
|
LogonSession
|
||||||
|
}
|
||||||
|
|
||||||
|
SECURITY_MAX_SID_SIZE :: 68;
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid
|
||||||
|
SID :: struct #packed {
|
||||||
|
Revision: byte,
|
||||||
|
SubAuthorityCount: byte,
|
||||||
|
IdentifierAuthority: SID_IDENTIFIER_AUTHORITY,
|
||||||
|
SubAuthority: [15]DWORD, // Array of DWORDs
|
||||||
|
};
|
||||||
|
#assert(size_of(SID) == SECURITY_MAX_SID_SIZE);
|
||||||
|
|
||||||
|
SID_IDENTIFIER_AUTHORITY :: struct #packed {
|
||||||
|
Value: [6]u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
// For NetAPI32
|
||||||
|
// https://github.com/tpn/winsdk-10/blob/master/Include/10.0.14393.0/shared/lmerr.h
|
||||||
|
// https://github.com/tpn/winsdk-10/blob/master/Include/10.0.14393.0/shared/LMaccess.h
|
||||||
|
|
||||||
|
UNLEN :: 256; // Maximum user name length
|
||||||
|
LM20_UNLEN :: 20; // LM 2.0 Maximum user name length
|
||||||
|
|
||||||
|
GNLEN :: UNLEN; // Group name
|
||||||
|
LM20_GNLEN :: LM20_UNLEN; // LM 2.0 Group name
|
||||||
|
|
||||||
|
PWLEN :: 256; // Maximum password length
|
||||||
|
LM20_PWLEN :: 14; // LM 2.0 Maximum password length
|
||||||
|
|
||||||
|
USER_PRIV :: enum DWORD {
|
||||||
|
Guest = 0,
|
||||||
|
User = 1,
|
||||||
|
Admin = 2,
|
||||||
|
Mask = 0x3,
|
||||||
|
}
|
||||||
|
|
||||||
|
USER_INFO_FLAG :: enum DWORD {
|
||||||
|
Script = 0, // 1 << 0: 0x0001,
|
||||||
|
AccountDisable = 1, // 1 << 1: 0x0002,
|
||||||
|
HomeDir_Required = 3, // 1 << 3: 0x0008,
|
||||||
|
Lockout = 4, // 1 << 4: 0x0010,
|
||||||
|
Passwd_NotReqd = 5, // 1 << 5: 0x0020,
|
||||||
|
Passwd_Cant_Change = 6, // 1 << 6: 0x0040,
|
||||||
|
Encrypted_Text_Password_Allowed = 7, // 1 << 7: 0x0080,
|
||||||
|
|
||||||
|
Temp_Duplicate_Account = 8, // 1 << 8: 0x0100,
|
||||||
|
Normal_Account = 9, // 1 << 9: 0x0200,
|
||||||
|
InterDomain_Trust_Account = 11, // 1 << 11: 0x0800,
|
||||||
|
Workstation_Trust_Account = 12, // 1 << 12: 0x1000,
|
||||||
|
Server_Trust_Account = 13, // 1 << 13: 0x2000,
|
||||||
|
}
|
||||||
|
USER_INFO_FLAGS :: distinct bit_set[USER_INFO_FLAG];
|
||||||
|
|
||||||
|
USER_INFO_1 :: struct #packed {
|
||||||
|
name: LPWSTR,
|
||||||
|
password: LPWSTR, // Max password length is defined in LM20_PWLEN.
|
||||||
|
password_age: DWORD,
|
||||||
|
priv: USER_PRIV,
|
||||||
|
home_dir: LPWSTR,
|
||||||
|
comment: LPWSTR,
|
||||||
|
flags: USER_INFO_FLAGS,
|
||||||
|
script_path: LPWSTR,
|
||||||
|
};
|
||||||
|
#assert(size_of(USER_INFO_1) == 50);
|
||||||
|
|
||||||
|
LOCALGROUP_MEMBERS_INFO_0 :: struct #packed {
|
||||||
|
sid: ^SID,
|
||||||
|
};
|
||||||
|
|
||||||
|
NET_API_STATUS :: enum DWORD {
|
||||||
|
Success = 0,
|
||||||
|
ERROR_ACCESS_DENIED = 5,
|
||||||
|
MemberInAlias = 1378,
|
||||||
|
NetNotStarted = 2102,
|
||||||
|
UnknownServer = 2103,
|
||||||
|
ShareMem = 2104,
|
||||||
|
NoNetworkResource = 2105,
|
||||||
|
RemoteOnly = 2106,
|
||||||
|
DevNotRedirected = 2107,
|
||||||
|
ServerNotStarted = 2114,
|
||||||
|
ItemNotFound = 2115,
|
||||||
|
UnknownDevDir = 2116,
|
||||||
|
RedirectedPath = 2117,
|
||||||
|
DuplicateShare = 2118,
|
||||||
|
NoRoom = 2119,
|
||||||
|
TooManyItems = 2121,
|
||||||
|
InvalidMaxUsers = 2122,
|
||||||
|
BufTooSmall = 2123,
|
||||||
|
RemoteErr = 2127,
|
||||||
|
LanmanIniError = 2131,
|
||||||
|
NetworkError = 2136,
|
||||||
|
WkstaInconsistentState = 2137,
|
||||||
|
WkstaNotStarted = 2138,
|
||||||
|
BrowserNotStarted = 2139,
|
||||||
|
InternalError = 2140,
|
||||||
|
BadTransactConfig = 2141,
|
||||||
|
InvalidAPI = 2142,
|
||||||
|
BadEventName = 2143,
|
||||||
|
DupNameReboot = 2144,
|
||||||
|
CfgCompNotFound = 2146,
|
||||||
|
CfgParamNotFound = 2147,
|
||||||
|
LineTooLong = 2149,
|
||||||
|
QNotFound = 2150,
|
||||||
|
JobNotFound = 2151,
|
||||||
|
DestNotFound = 2152,
|
||||||
|
DestExists = 2153,
|
||||||
|
QExists = 2154,
|
||||||
|
QNoRoom = 2155,
|
||||||
|
JobNoRoom = 2156,
|
||||||
|
DestNoRoom = 2157,
|
||||||
|
DestIdle = 2158,
|
||||||
|
DestInvalidOp = 2159,
|
||||||
|
ProcNoRespond = 2160,
|
||||||
|
SpoolerNotLoaded = 2161,
|
||||||
|
DestInvalidState = 2162,
|
||||||
|
QInvalidState = 2163,
|
||||||
|
JobInvalidState = 2164,
|
||||||
|
SpoolNoMemory = 2165,
|
||||||
|
DriverNotFound = 2166,
|
||||||
|
DataTypeInvalid = 2167,
|
||||||
|
ProcNotFound = 2168,
|
||||||
|
ServiceTableLocked = 2180,
|
||||||
|
ServiceTableFull = 2181,
|
||||||
|
ServiceInstalled = 2182,
|
||||||
|
ServiceEntryLocked = 2183,
|
||||||
|
ServiceNotInstalled = 2184,
|
||||||
|
BadServiceName = 2185,
|
||||||
|
ServiceCtlTimeout = 2186,
|
||||||
|
ServiceCtlBusy = 2187,
|
||||||
|
BadServiceProgName = 2188,
|
||||||
|
ServiceNotCtrl = 2189,
|
||||||
|
ServiceKillProc = 2190,
|
||||||
|
ServiceCtlNotValid = 2191,
|
||||||
|
NotInDispatchTbl = 2192,
|
||||||
|
BadControlRecv = 2193,
|
||||||
|
ServiceNotStarting = 2194,
|
||||||
|
AlreadyLoggedOn = 2200,
|
||||||
|
NotLoggedOn = 2201,
|
||||||
|
BadUsername = 2202,
|
||||||
|
BadPassword = 2203,
|
||||||
|
UnableToAddName_W = 2204,
|
||||||
|
UnableToAddName_F = 2205,
|
||||||
|
UnableToDelName_W = 2206,
|
||||||
|
UnableToDelName_F = 2207,
|
||||||
|
LogonsPaused = 2209,
|
||||||
|
LogonServerConflict = 2210,
|
||||||
|
LogonNoUserPath = 2211,
|
||||||
|
LogonScriptError = 2212,
|
||||||
|
StandaloneLogon = 2214,
|
||||||
|
LogonServerNotFound = 2215,
|
||||||
|
LogonDomainExists = 2216,
|
||||||
|
NonValidatedLogon = 2217,
|
||||||
|
ACFNotFound = 2219,
|
||||||
|
GroupNotFound = 2220,
|
||||||
|
UserNotFound = 2221,
|
||||||
|
ResourceNotFound = 2222,
|
||||||
|
GroupExists = 2223,
|
||||||
|
UserExists = 2224,
|
||||||
|
ResourceExists = 2225,
|
||||||
|
NotPrimary = 2226,
|
||||||
|
ACFNotLoaded = 2227,
|
||||||
|
ACFNoRoom = 2228,
|
||||||
|
ACFFileIOFail = 2229,
|
||||||
|
ACFTooManyLists = 2230,
|
||||||
|
UserLogon = 2231,
|
||||||
|
ACFNoParent = 2232,
|
||||||
|
CanNotGrowSegment = 2233,
|
||||||
|
SpeGroupOp = 2234,
|
||||||
|
NotInCache = 2235,
|
||||||
|
UserInGroup = 2236,
|
||||||
|
UserNotInGroup = 2237,
|
||||||
|
AccountUndefined = 2238,
|
||||||
|
AccountExpired = 2239,
|
||||||
|
InvalidWorkstation = 2240,
|
||||||
|
InvalidLogonHours = 2241,
|
||||||
|
PasswordExpired = 2242,
|
||||||
|
PasswordCantChange = 2243,
|
||||||
|
PasswordHistConflict = 2244,
|
||||||
|
PasswordTooShort = 2245,
|
||||||
|
PasswordTooRecent = 2246,
|
||||||
|
InvalidDatabase = 2247,
|
||||||
|
DatabaseUpToDate = 2248,
|
||||||
|
SyncRequired = 2249,
|
||||||
|
UseNotFound = 2250,
|
||||||
|
BadAsgType = 2251,
|
||||||
|
DeviceIsShared = 2252,
|
||||||
|
SameAsComputerName = 2253,
|
||||||
|
NoComputerName = 2270,
|
||||||
|
MsgAlreadyStarted = 2271,
|
||||||
|
MsgInitFailed = 2272,
|
||||||
|
NameNotFound = 2273,
|
||||||
|
AlreadyForwarded = 2274,
|
||||||
|
AddForwarded = 2275,
|
||||||
|
AlreadyExists = 2276,
|
||||||
|
TooManyNames = 2277,
|
||||||
|
DelComputerName = 2278,
|
||||||
|
LocalForward = 2279,
|
||||||
|
GrpMsgProcessor = 2280,
|
||||||
|
PausedRemote = 2281,
|
||||||
|
BadReceive = 2282,
|
||||||
|
NameInUse = 2283,
|
||||||
|
MsgNotStarted = 2284,
|
||||||
|
NotLocalName = 2285,
|
||||||
|
NoForwardName = 2286,
|
||||||
|
RemoteFull = 2287,
|
||||||
|
NameNotForwarded = 2288,
|
||||||
|
TruncatedBroadcast = 2289,
|
||||||
|
InvalidDevice = 2294,
|
||||||
|
WriteFault = 2295,
|
||||||
|
DuplicateName = 2297,
|
||||||
|
DeleteLater = 2298,
|
||||||
|
IncompleteDel = 2299,
|
||||||
|
MultipleNets = 2300,
|
||||||
|
NetNameNotFound = 2310,
|
||||||
|
DeviceNotShared = 2311,
|
||||||
|
ClientNameNotFound = 2312,
|
||||||
|
FileIdNotFound = 2314,
|
||||||
|
ExecFailure = 2315,
|
||||||
|
TmpFile = 2316,
|
||||||
|
TooMuchData = 2317,
|
||||||
|
DeviceShareConflict = 2318,
|
||||||
|
BrowserTableIncomplete = 2319,
|
||||||
|
NotLocalDomain = 2320,
|
||||||
|
IsDfsShare = 2321,
|
||||||
|
DevInvalidOpCode = 2331,
|
||||||
|
DevNotFound = 2332,
|
||||||
|
DevNotOpen = 2333,
|
||||||
|
BadQueueDevString = 2334,
|
||||||
|
BadQueuePriority = 2335,
|
||||||
|
NoCommDevs = 2337,
|
||||||
|
QueueNotFound = 2338,
|
||||||
|
BadDevString = 2340,
|
||||||
|
BadDev = 2341,
|
||||||
|
InUseBySpooler = 2342,
|
||||||
|
CommDevInUse = 2343,
|
||||||
|
InvalidComputer = 2351,
|
||||||
|
MaxLenExceeded = 2354,
|
||||||
|
BadComponent = 2356,
|
||||||
|
CantType = 2357,
|
||||||
|
TooManyEntries = 2362,
|
||||||
|
ProfileFileTooBig = 2370,
|
||||||
|
ProfileOffset = 2371,
|
||||||
|
ProfileCleanup = 2372,
|
||||||
|
ProfileUnknownCmd = 2373,
|
||||||
|
ProfileLoadErr = 2374,
|
||||||
|
ProfileSaveErr = 2375,
|
||||||
|
LogOverflow = 2377,
|
||||||
|
LogFileChanged = 2378,
|
||||||
|
LogFileCorrupt = 2379,
|
||||||
|
SourceIsDir = 2380,
|
||||||
|
BadSource = 2381,
|
||||||
|
BadDest = 2382,
|
||||||
|
DifferentServers = 2383,
|
||||||
|
RunSrvPaused = 2385,
|
||||||
|
ErrCommRunSrv = 2389,
|
||||||
|
ErrorExecingGhost = 2391,
|
||||||
|
ShareNotFound = 2392,
|
||||||
|
InvalidLana = 2400,
|
||||||
|
OpenFiles = 2401,
|
||||||
|
ActiveConns = 2402,
|
||||||
|
BadPasswordCore = 2403,
|
||||||
|
DevInUse = 2404,
|
||||||
|
LocalDrive = 2405,
|
||||||
|
AlertExists = 2430,
|
||||||
|
TooManyAlerts = 2431,
|
||||||
|
NoSuchAlert = 2432,
|
||||||
|
BadRecipient = 2433,
|
||||||
|
AcctLimitExceeded = 2434,
|
||||||
|
InvalidLogSeek = 2440,
|
||||||
|
BadUasConfig = 2450,
|
||||||
|
InvalidUASOp = 2451,
|
||||||
|
LastAdmin = 2452,
|
||||||
|
DCNotFound = 2453,
|
||||||
|
LogonTrackingError = 2454,
|
||||||
|
NetlogonNotStarted = 2455,
|
||||||
|
CanNotGrowUASFile = 2456,
|
||||||
|
TimeDiffAtDC = 2457,
|
||||||
|
PasswordMismatch = 2458,
|
||||||
|
NoSuchServer = 2460,
|
||||||
|
NoSuchSession = 2461,
|
||||||
|
NoSuchConnection = 2462,
|
||||||
|
TooManyServers = 2463,
|
||||||
|
TooManySessions = 2464,
|
||||||
|
TooManyConnections = 2465,
|
||||||
|
TooManyFiles = 2466,
|
||||||
|
NoAlternateServers = 2467,
|
||||||
|
TryDownLevel = 2470,
|
||||||
|
UPSDriverNotStarted = 2480,
|
||||||
|
UPSInvalidConfig = 2481,
|
||||||
|
UPSInvalidCommPort = 2482,
|
||||||
|
UPSSignalAsserted = 2483,
|
||||||
|
UPSShutdownFailed = 2484,
|
||||||
|
BadDosRetCode = 2500,
|
||||||
|
ProgNeedsExtraMem = 2501,
|
||||||
|
BadDosFunction = 2502,
|
||||||
|
RemoteBootFailed = 2503,
|
||||||
|
BadFileCheckSum = 2504,
|
||||||
|
NoRplBootSystem = 2505,
|
||||||
|
RplLoadrNetBiosErr = 2506,
|
||||||
|
RplLoadrDiskErr = 2507,
|
||||||
|
ImageParamErr = 2508,
|
||||||
|
TooManyImageParams = 2509,
|
||||||
|
NonDosFloppyUsed = 2510,
|
||||||
|
RplBootRestart = 2511,
|
||||||
|
RplSrvrCallFailed = 2512,
|
||||||
|
CantConnectRplSrvr = 2513,
|
||||||
|
CantOpenImageFile = 2514,
|
||||||
|
CallingRplSrvr = 2515,
|
||||||
|
StartingRplBoot = 2516,
|
||||||
|
RplBootServiceTerm = 2517,
|
||||||
|
RplBootStartFailed = 2518,
|
||||||
|
RPL_CONNECTED = 2519,
|
||||||
|
BrowserConfiguredToNotRun = 2550,
|
||||||
|
RplNoAdaptersStarted = 2610,
|
||||||
|
RplBadRegistry = 2611,
|
||||||
|
RplBadDatabase = 2612,
|
||||||
|
RplRplfilesShare = 2613,
|
||||||
|
RplNotRplServer = 2614,
|
||||||
|
RplCannotEnum = 2615,
|
||||||
|
RplWkstaInfoCorrupted = 2616,
|
||||||
|
RplWkstaNotFound = 2617,
|
||||||
|
RplWkstaNameUnavailable = 2618,
|
||||||
|
RplProfileInfoCorrupted = 2619,
|
||||||
|
RplProfileNotFound = 2620,
|
||||||
|
RplProfileNameUnavailable = 2621,
|
||||||
|
RplProfileNotEmpty = 2622,
|
||||||
|
RplConfigInfoCorrupted = 2623,
|
||||||
|
RplConfigNotFound = 2624,
|
||||||
|
RplAdapterInfoCorrupted = 2625,
|
||||||
|
RplInternal = 2626,
|
||||||
|
RplVendorInfoCorrupted = 2627,
|
||||||
|
RplBootInfoCorrupted = 2628,
|
||||||
|
RplWkstaNeedsUserAcct = 2629,
|
||||||
|
RplNeedsRPLUSERAcct = 2630,
|
||||||
|
RplBootNotFound = 2631,
|
||||||
|
RplIncompatibleProfile = 2632,
|
||||||
|
RplAdapterNameUnavailable = 2633,
|
||||||
|
RplConfigNotEmpty = 2634,
|
||||||
|
RplBootInUse = 2635,
|
||||||
|
RplBackupDatabase = 2636,
|
||||||
|
RplAdapterNotFound = 2637,
|
||||||
|
RplVendorNotFound = 2638,
|
||||||
|
RplVendorNameUnavailable = 2639,
|
||||||
|
RplBootNameUnavailable = 2640,
|
||||||
|
RplConfigNameUnavailable = 2641,
|
||||||
|
DfsInternalCorruption = 2660,
|
||||||
|
DfsVolumeDataCorrupt = 2661,
|
||||||
|
DfsNoSuchVolume = 2662,
|
||||||
|
DfsVolumeAlreadyExists = 2663,
|
||||||
|
DfsAlreadyShared = 2664,
|
||||||
|
DfsNoSuchShare = 2665,
|
||||||
|
DfsNotALeafVolume = 2666,
|
||||||
|
DfsLeafVolume = 2667,
|
||||||
|
DfsVolumeHasMultipleServers = 2668,
|
||||||
|
DfsCantCreateJunctionPoint = 2669,
|
||||||
|
DfsServerNotDfsAware = 2670,
|
||||||
|
DfsBadRenamePath = 2671,
|
||||||
|
DfsVolumeIsOffline = 2672,
|
||||||
|
DfsNoSuchServer = 2673,
|
||||||
|
DfsCyclicalName = 2674,
|
||||||
|
DfsNotSupportedInServerDfs = 2675,
|
||||||
|
DfsDuplicateService = 2676,
|
||||||
|
DfsCantRemoveLastServerShare = 2677,
|
||||||
|
DfsVolumeIsInterDfs = 2678,
|
||||||
|
DfsInconsistent = 2679,
|
||||||
|
DfsServerUpgraded = 2680,
|
||||||
|
DfsDataIsIdentical = 2681,
|
||||||
|
DfsCantRemoveDfsRoot = 2682,
|
||||||
|
DfsChildOrParentInDfs = 2683,
|
||||||
|
DfsInternalError = 2690,
|
||||||
|
SetupAlreadyJoined = 2691,
|
||||||
|
SetupNotJoined = 2692,
|
||||||
|
SetupDomainController = 2693,
|
||||||
|
DefaultJoinRequired = 2694,
|
||||||
|
InvalidWorkgroupName = 2695,
|
||||||
|
NameUsesIncompatibleCodePage = 2696,
|
||||||
|
ComputerAccountNotFound = 2697,
|
||||||
|
PersonalSku = 2698,
|
||||||
|
SetupCheckDNSConfig = 2699,
|
||||||
|
PasswordMustChange = 2701,
|
||||||
|
AccountLockedOut = 2702,
|
||||||
|
PasswordTooLong = 2703,
|
||||||
|
PasswordNotComplexEnough = 2704,
|
||||||
|
PasswordFilterError = 2705,
|
||||||
|
}
|
||||||
@@ -7,4 +7,32 @@ foreign userenv {
|
|||||||
GetUserProfileDirectoryW :: proc(hToken: HANDLE,
|
GetUserProfileDirectoryW :: proc(hToken: HANDLE,
|
||||||
lpProfileDir: LPWSTR,
|
lpProfileDir: LPWSTR,
|
||||||
lpcchSize: ^DWORD) -> BOOL ---
|
lpcchSize: ^DWORD) -> BOOL ---
|
||||||
|
LoadUserProfileW :: proc(
|
||||||
|
hToken: HANDLE,
|
||||||
|
lpProfileInfo: ^PROFILEINFOW,
|
||||||
|
) -> BOOL ---
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-createprofile
|
||||||
|
// The caller must have administrator privileges to call this function.
|
||||||
|
CreateProfile :: proc(
|
||||||
|
pszUserSid: LPCWSTR,
|
||||||
|
pszUserName: LPCWSTR,
|
||||||
|
pszProfilePath: wstring,
|
||||||
|
cchProfilePath: DWORD,
|
||||||
|
) -> u32 ---
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-deleteprofilew
|
||||||
|
// The caller must have administrative privileges to delete a user's profile.
|
||||||
|
DeleteProfileW :: proc(
|
||||||
|
lpSidString: LPCWSTR,
|
||||||
|
lpProfilePath: LPCWSTR,
|
||||||
|
lpComputerName: LPCWSTR,
|
||||||
|
) -> BOOL ---
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertsidtostringsida
|
||||||
|
// To turn a SID into a string SID to use with CreateProfile & DeleteProfileW.
|
||||||
|
ConvertSidToStringSidW :: proc(
|
||||||
|
Sid: ^SID,
|
||||||
|
StringSid: ^LPCWSTR,
|
||||||
|
) -> BOOL ---
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package sys_windows
|
package sys_windows
|
||||||
|
|
||||||
|
import "core:strings"
|
||||||
|
import "core:runtime"
|
||||||
|
import "core:sys/win32"
|
||||||
|
|
||||||
LOWORD :: #force_inline proc "contextless" (x: DWORD) -> WORD {
|
LOWORD :: #force_inline proc "contextless" (x: DWORD) -> WORD {
|
||||||
return WORD(x & 0xffff);
|
return WORD(x & 0xffff);
|
||||||
}
|
}
|
||||||
@@ -81,3 +85,375 @@ utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> string {
|
|||||||
return wstring_to_utf8(raw_data(s), len(s), allocator);
|
return wstring_to_utf8(raw_data(s), len(s), allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AdvAPI32, NetAPI32 and UserENV helpers.
|
||||||
|
|
||||||
|
allowed_username :: proc(username: string) -> bool {
|
||||||
|
/*
|
||||||
|
User account names are limited to 20 characters and group names are limited to 256 characters.
|
||||||
|
In addition, account names cannot be terminated by a period and they cannot include commas or any of the following printable characters:
|
||||||
|
", /, , [, ], :, |, <, >, +, =, ;, ?, *. Names also cannot include characters in the range 1-31, which are nonprintable.
|
||||||
|
*/
|
||||||
|
|
||||||
|
_DISALLOWED :: "\"/ []:|<>+=;?*,";
|
||||||
|
|
||||||
|
if len(username) > LM20_UNLEN || len(username) == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if username[len(username)-1] == '.' {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for r in username {
|
||||||
|
if r > 0 && r < 32 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.contains_any(username, _DISALLOWED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns .Success on success.
|
||||||
|
_add_user :: proc(servername: string, username: string, password: string) -> (ok: NET_API_STATUS) {
|
||||||
|
|
||||||
|
servername_w: wstring;
|
||||||
|
username_w: []u16;
|
||||||
|
password_w: []u16;
|
||||||
|
|
||||||
|
if len(servername) == 0 {
|
||||||
|
// Create account on this computer
|
||||||
|
servername_w = nil;
|
||||||
|
} else {
|
||||||
|
server := utf8_to_utf16(servername, context.temp_allocator);
|
||||||
|
servername_w = &server[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(username) == 0 || len(username) > LM20_UNLEN {
|
||||||
|
return .BadUsername;
|
||||||
|
}
|
||||||
|
if !allowed_username(username) {
|
||||||
|
return .BadUsername;
|
||||||
|
}
|
||||||
|
if len(password) == 0 || len(password) > LM20_PWLEN {
|
||||||
|
return .BadPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
username_w = utf8_to_utf16(username, context.temp_allocator);
|
||||||
|
password_w = utf8_to_utf16(password, context.temp_allocator);
|
||||||
|
|
||||||
|
|
||||||
|
level := DWORD(1);
|
||||||
|
parm_err: DWORD;
|
||||||
|
|
||||||
|
user_info := USER_INFO_1{
|
||||||
|
name = &username_w[0],
|
||||||
|
password = &password_w[0], // Max password length is defined in LM20_PWLEN.
|
||||||
|
password_age = 0, // Ignored
|
||||||
|
priv = .User,
|
||||||
|
home_dir = nil, // We'll set it later
|
||||||
|
comment = nil,
|
||||||
|
flags = {.Script, .Normal_Account},
|
||||||
|
script_path = nil,
|
||||||
|
};
|
||||||
|
|
||||||
|
ok = NetUserAdd(
|
||||||
|
servername_w,
|
||||||
|
level,
|
||||||
|
&user_info,
|
||||||
|
&parm_err,
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_computer_name_and_account_sid :: proc(username: string) -> (computer_name: string, sid := SID{}, ok: bool) {
|
||||||
|
|
||||||
|
username_w := utf8_to_utf16(username, context.temp_allocator);
|
||||||
|
cbsid: DWORD;
|
||||||
|
computer_name_size: DWORD;
|
||||||
|
pe_use := SID_TYPE.User;
|
||||||
|
|
||||||
|
res := LookupAccountNameW(
|
||||||
|
nil, // Look on this computer first
|
||||||
|
&username_w[0],
|
||||||
|
&sid,
|
||||||
|
&cbsid,
|
||||||
|
nil,
|
||||||
|
&computer_name_size,
|
||||||
|
&pe_use,
|
||||||
|
);
|
||||||
|
if computer_name_size == 0 {
|
||||||
|
// User didn't exist, or we'd have a size here.
|
||||||
|
return "", {}, false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cname_w := make([]u16, min(computer_name_size, 1), context.temp_allocator);
|
||||||
|
|
||||||
|
res = LookupAccountNameW(
|
||||||
|
nil,
|
||||||
|
&username_w[0],
|
||||||
|
&sid,
|
||||||
|
&cbsid,
|
||||||
|
&cname_w[0],
|
||||||
|
&computer_name_size,
|
||||||
|
&pe_use,
|
||||||
|
);
|
||||||
|
|
||||||
|
if !res {
|
||||||
|
return "", {}, false;
|
||||||
|
}
|
||||||
|
computer_name = utf16_to_utf8(cname_w, context.temp_allocator);
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_sid :: proc(username: string, sid: ^SID) -> (ok: bool) {
|
||||||
|
|
||||||
|
username_w := utf8_to_utf16(username, context.temp_allocator);
|
||||||
|
cbsid: DWORD;
|
||||||
|
computer_name_size: DWORD;
|
||||||
|
pe_use := SID_TYPE.User;
|
||||||
|
|
||||||
|
res := LookupAccountNameW(
|
||||||
|
nil, // Look on this computer first
|
||||||
|
&username_w[0],
|
||||||
|
sid,
|
||||||
|
&cbsid,
|
||||||
|
nil,
|
||||||
|
&computer_name_size,
|
||||||
|
&pe_use,
|
||||||
|
);
|
||||||
|
if computer_name_size == 0 {
|
||||||
|
// User didn't exist, or we'd have a size here.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cname_w := make([]u16, min(computer_name_size, 1), context.temp_allocator);
|
||||||
|
|
||||||
|
res = LookupAccountNameW(
|
||||||
|
nil,
|
||||||
|
&username_w[0],
|
||||||
|
sid,
|
||||||
|
&cbsid,
|
||||||
|
&cname_w[0],
|
||||||
|
&computer_name_size,
|
||||||
|
&pe_use,
|
||||||
|
);
|
||||||
|
|
||||||
|
if !res {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_user_to_group :: proc(sid: ^SID, group: string) -> (ok: NET_API_STATUS) {
|
||||||
|
group_member := LOCALGROUP_MEMBERS_INFO_0{
|
||||||
|
sid = sid,
|
||||||
|
};
|
||||||
|
group_name := utf8_to_utf16(group, context.temp_allocator);
|
||||||
|
ok = NetLocalGroupAddMembers(
|
||||||
|
nil,
|
||||||
|
&group_name[0],
|
||||||
|
0,
|
||||||
|
&group_member,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_del_from_group :: proc(sid: ^SID, group: string) -> (ok: NET_API_STATUS) {
|
||||||
|
group_member := LOCALGROUP_MEMBERS_INFO_0{
|
||||||
|
sid = sid,
|
||||||
|
};
|
||||||
|
group_name := utf8_to_utf16(group, context.temp_allocator);
|
||||||
|
ok = NetLocalGroupDelMembers(
|
||||||
|
nil,
|
||||||
|
&group_name[0],
|
||||||
|
0,
|
||||||
|
&group_member,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_user_profile :: proc(username: string) -> (ok: bool, profile_path: string) {
|
||||||
|
username_w := utf8_to_utf16(username, context.temp_allocator);
|
||||||
|
|
||||||
|
sid := SID{};
|
||||||
|
ok = get_sid(username, &sid);
|
||||||
|
if ok == false {
|
||||||
|
return false, "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sb: wstring;
|
||||||
|
res := ConvertSidToStringSidW(&sid, &sb);
|
||||||
|
if res == false {
|
||||||
|
return false, "";
|
||||||
|
}
|
||||||
|
defer win32.local_free(sb);
|
||||||
|
|
||||||
|
pszProfilePath := make([]u16, 257, context.temp_allocator);
|
||||||
|
cchProfilePath: DWORD;
|
||||||
|
res2 := CreateProfile(
|
||||||
|
sb,
|
||||||
|
&username_w[0],
|
||||||
|
&pszProfilePath[0],
|
||||||
|
257,
|
||||||
|
);
|
||||||
|
if res2 != 0 {
|
||||||
|
return false, "";
|
||||||
|
}
|
||||||
|
profile_path = wstring_to_utf8(&pszProfilePath[0], 257);
|
||||||
|
|
||||||
|
return true, profile_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
delete_user_profile :: proc(username: string) -> (ok: bool) {
|
||||||
|
sid := SID{};
|
||||||
|
ok = get_sid(username, &sid);
|
||||||
|
if ok == false {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb: wstring;
|
||||||
|
res := ConvertSidToStringSidW(&sid, &sb);
|
||||||
|
if res == false {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
defer win32.local_free(sb);
|
||||||
|
|
||||||
|
res2 := DeleteProfileW(
|
||||||
|
sb,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
);
|
||||||
|
return bool(res2);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_user :: proc(servername: string, username: string, password: string) -> (ok: bool) {
|
||||||
|
/*
|
||||||
|
Convenience function that creates a new user, adds it to the group Users and creates a profile directory for it.
|
||||||
|
Requires elevated privileges (run as administrator).
|
||||||
|
|
||||||
|
TODO: Add a bool that governs whether to delete the user if adding to group and/or creating profile fail?
|
||||||
|
TODO: SecureZeroMemory the password after use.
|
||||||
|
*/
|
||||||
|
|
||||||
|
res := _add_user(servername, username, password);
|
||||||
|
if res != .Success {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab the SID to add the user to the Users group.
|
||||||
|
sid: SID;
|
||||||
|
ok2 := get_sid(username, &sid);
|
||||||
|
if ok2 == false {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok3 := add_user_to_group(&sid, "Users");
|
||||||
|
if ok3 != .Success {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_user :: proc(servername: string, username: string) -> (ok: bool) {
|
||||||
|
/*
|
||||||
|
Convenience function that deletes a user.
|
||||||
|
Requires elevated privileges (run as administrator).
|
||||||
|
|
||||||
|
TODO: Add a bool that governs whether to delete the profile from this wrapper?
|
||||||
|
*/
|
||||||
|
|
||||||
|
servername_w: wstring;
|
||||||
|
if len(servername) == 0 {
|
||||||
|
// Delete account on this computer
|
||||||
|
servername_w = nil;
|
||||||
|
} else {
|
||||||
|
server := utf8_to_utf16(servername, context.temp_allocator);
|
||||||
|
servername_w = &server[0];
|
||||||
|
}
|
||||||
|
username_w := utf8_to_utf16(username);
|
||||||
|
|
||||||
|
res := NetUserDel(
|
||||||
|
servername_w,
|
||||||
|
&username_w[0],
|
||||||
|
);
|
||||||
|
if res != .Success {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_as_user :: proc(username, password, application, commandline: string, pi: ^PROCESS_INFORMATION, wait := true) -> (ok: bool) {
|
||||||
|
/*
|
||||||
|
Needs to be run as an account which has the "Replace a process level token" privilege.
|
||||||
|
This can be added to an account from: Control Panel -> Administrative Tools -> Local Security Policy.
|
||||||
|
The path to this policy is as follows: Local Policies -> User Rights Assignment -> Replace a process level token.
|
||||||
|
A reboot may be required for this change to take effect and impersonating a user to work.
|
||||||
|
|
||||||
|
TODO: SecureZeroMemory the password after use.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
username_w := utf8_to_utf16(username);
|
||||||
|
domain_w := utf8_to_utf16(".");
|
||||||
|
password_w := utf8_to_utf16(password);
|
||||||
|
app_w := utf8_to_utf16(application);
|
||||||
|
|
||||||
|
commandline_w: []u16 = {0};
|
||||||
|
if len(commandline) > 0 {
|
||||||
|
commandline_w = utf8_to_utf16(commandline);
|
||||||
|
}
|
||||||
|
|
||||||
|
user_token: HANDLE;
|
||||||
|
|
||||||
|
ok = bool(LogonUserW(
|
||||||
|
lpszUsername = &username_w[0],
|
||||||
|
lpszDomain = &domain_w[0],
|
||||||
|
lpszPassword = &password_w[0],
|
||||||
|
dwLogonType = .NEW_CREDENTIALS,
|
||||||
|
dwLogonProvider = .WINNT50,
|
||||||
|
phToken = &user_token,
|
||||||
|
));
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return false;
|
||||||
|
// err := GetLastError();
|
||||||
|
// fmt.printf("GetLastError: %v\n", err);
|
||||||
|
}
|
||||||
|
si := STARTUPINFO{};
|
||||||
|
si.cb = size_of(STARTUPINFO);
|
||||||
|
pi := pi;
|
||||||
|
|
||||||
|
ok = bool(CreateProcessAsUserW(
|
||||||
|
user_token,
|
||||||
|
&app_w[0],
|
||||||
|
&commandline_w[0],
|
||||||
|
nil, // lpProcessAttributes,
|
||||||
|
nil, // lpThreadAttributes,
|
||||||
|
false, // bInheritHandles,
|
||||||
|
0, // creation flags
|
||||||
|
nil, // environment,
|
||||||
|
nil, // current directory: inherit from parent if nil
|
||||||
|
&si,
|
||||||
|
pi,
|
||||||
|
));
|
||||||
|
if ok {
|
||||||
|
if wait {
|
||||||
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||||
|
CloseHandle(pi.hProcess);
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user