From f7020c0d16baf2b960e7123e233e20c519f6a340 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 15 Dec 2018 21:46:30 -0600 Subject: Import implementation of UtilCA from old repo's WixCA/scasched/scaexec. (#3) --- src/ca/scaexec.cpp | 807 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 807 insertions(+) create mode 100644 src/ca/scaexec.cpp (limited to 'src/ca/scaexec.cpp') diff --git a/src/ca/scaexec.cpp b/src/ca/scaexec.cpp new file mode 100644 index 00000000..ab9e6599 --- /dev/null +++ b/src/ca/scaexec.cpp @@ -0,0 +1,807 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +/******************************************************************** + * CreateSmb - CUSTOM ACTION ENTRY POINT for creating fileshares + * + * Input: deferred CustomActionData - + * wzFsKey\twzShareDesc\twzFullPath\tfIntegratedAuth\twzUserName\tnPermissions\twzUserName\tnPermissions... + * + * ****************************************************************/ +extern "C" UINT __stdcall CreateSmb(MSIHANDLE hInstall) +{ +//AssertSz(0, "debug CreateSmb"); + UINT er = ERROR_SUCCESS; + HRESULT hr = S_OK; + + LPWSTR pwzData = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzFsKey = NULL; + LPWSTR pwzShareDesc = NULL; + LPWSTR pwzDirectory = NULL; + int iAccessMode = 0; + DWORD nExPermissions = 0; + BOOL fIntegratedAuth; + LPWSTR pwzExUser = NULL; + SCA_SMBP ssp = {0}; + DWORD dwExUserPerms = 0; + DWORD dwCounter = 0; + SCA_SMBP_USER_PERMS* pUserPermsList = NULL; + + hr = WcaInitialize(hInstall, "CreateSmb"); + ExitOnFailure(hr, "failed to initialize"); + + hr = WcaGetProperty( L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); + + pwz = pwzData; + hr = WcaReadStringFromCaData(&pwz, &pwzFsKey); // share name + ExitOnFailure(hr, "failed to read share name"); + hr = WcaReadStringFromCaData(&pwz, &pwzShareDesc); // share description + ExitOnFailure(hr, "failed to read share name"); + hr = WcaReadStringFromCaData(&pwz, &pwzDirectory); // full path to share + ExitOnFailure(hr, "failed to read share name"); + hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast(&fIntegratedAuth)); + ExitOnFailure(hr, "failed to read integrated authentication"); + + hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast(&dwExUserPerms)); + ExitOnFailure(hr, "failed to read count of permissions to set"); + if(dwExUserPerms > 0) + { + pUserPermsList = static_cast(MemAlloc(sizeof(SCA_SMBP_USER_PERMS)*dwExUserPerms, TRUE)); + ExitOnNull(pUserPermsList, hr, E_OUTOFMEMORY, "failed to allocate memory for permissions structure"); + + //Pull out all of the ExUserPerm strings + for (dwCounter = 0; dwCounter < dwExUserPerms; ++dwCounter) + { + hr = WcaReadStringFromCaData(&pwz, &pwzExUser); // user account + ExitOnFailure(hr, "failed to read user account"); + pUserPermsList[dwCounter].wzUser = pwzExUser; + pwzExUser = NULL; + + hr = WcaReadIntegerFromCaData(&pwz, &iAccessMode); + ExitOnFailure(hr, "failed to read access mode"); + pUserPermsList[dwCounter].accessMode = (ACCESS_MODE)iAccessMode; + iAccessMode = 0; + + hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast(&nExPermissions)); + ExitOnFailure(hr, "failed to read count of permissions"); + pUserPermsList[dwCounter].nPermissions = nExPermissions; + nExPermissions = 0; + } + } + + ssp.wzKey = pwzFsKey; + ssp.wzDescription = pwzShareDesc; + ssp.wzDirectory = pwzDirectory; + ssp.fUseIntegratedAuth = fIntegratedAuth; + ssp.dwUserPermissionCount = dwExUserPerms; + ssp.pUserPerms = pUserPermsList; + + hr = ScaEnsureSmbExists(&ssp); + MessageExitOnFailure(hr, msierrSMBFailedCreate, "failed to create share: '%ls'", pwzFsKey); + + hr = WcaProgressMessage(COST_SMB_CREATESMB, FALSE); + +LExit: + ReleaseStr(pwzFsKey); + ReleaseStr(pwzShareDesc); + ReleaseStr(pwzDirectory); + ReleaseStr(pwzData); + + if (pUserPermsList) + { + MemFree(pUserPermsList); + } + + if (FAILED(hr)) + { + er = ERROR_INSTALL_FAILURE; + } + return WcaFinalize(er); +} + + + +/******************************************************************** + DropSmb - CUSTOM ACTION ENTRY POINT for creating fileshares + + Input: deferred CustomActionData - wzFsKey\twzShareDesc\twzFullPath\tnPermissions\tfIntegratedAuth\twzUserName\twzPassword + + * ****************************************************************/ +extern "C" UINT __stdcall DropSmb(MSIHANDLE hInstall) +{ + //AssertSz(0, "debug DropSmb"); + UINT er = ERROR_SUCCESS; + HRESULT hr = S_OK; + + LPWSTR pwzData = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzFsKey = NULL; + SCA_SMBP ssp = {0}; + + hr = WcaInitialize(hInstall, "DropSmb"); + ExitOnFailure(hr, "failed to initialize"); + + hr = WcaGetProperty( L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); + + pwz = pwzData; + hr = WcaReadStringFromCaData(&pwz, &pwzFsKey); // share name + ExitOnFailure(hr, "failed to read share name"); + + ssp.wzKey = pwzFsKey; + + hr = ScaDropSmb(&ssp); + MessageExitOnFailure(hr, msierrSMBFailedDrop, "failed to delete share: '%ls'", pwzFsKey); + + hr = WcaProgressMessage(COST_SMB_DROPSMB, FALSE); + +LExit: + ReleaseStr(pwzFsKey); + ReleaseStr(pwzData); + + if (FAILED(hr)) + { + er = ERROR_INSTALL_FAILURE; + } + return WcaFinalize(er); +} + + +static HRESULT AddUserToGroup( + __in LPWSTR wzUser, + __in LPCWSTR wzUserDomain, + __in LPCWSTR wzGroup, + __in LPCWSTR wzGroupDomain + ) +{ + Assert(wzUser && *wzUser && wzUserDomain && wzGroup && *wzGroup && wzGroupDomain); + + HRESULT hr = S_OK; + IADsGroup *pGroup = NULL; + BSTR bstrUser = NULL; + BSTR bstrGroup = NULL; + LPCWSTR wz = NULL; + LPWSTR pwzUser = NULL; + LOCALGROUP_MEMBERS_INFO_3 lgmi; + + if (*wzGroupDomain) + { + wz = wzGroupDomain; + } + + // Try adding it to the global group first + UINT ui = ::NetGroupAddUser(wz, wzGroup, wzUser); + if (NERR_GroupNotFound == ui) + { + // Try adding it to the local group + if (wzUserDomain) + { + hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzUserDomain, wzUser); + ExitOnFailure(hr, "failed to allocate user domain string"); + } + + lgmi.lgrmi3_domainandname = (NULL == pwzUser ? wzUser : pwzUser); + ui = ::NetLocalGroupAddMembers(wz, wzGroup, 3 , reinterpret_cast(&lgmi), 1); + } + hr = HRESULT_FROM_WIN32(ui); + if (HRESULT_FROM_WIN32(ERROR_MEMBER_IN_ALIAS) == hr) // if they're already a member of the group don't report an error + hr = S_OK; + + // + // If we failed, try active directory + // + if (FAILED(hr)) + { + WcaLog(LOGMSG_VERBOSE, "Failed to add user: %ls, domain %ls to group: %ls, domain: %ls with error 0x%x. Attempting to use Active Directory", wzUser, wzUserDomain, wzGroup, wzGroupDomain, hr); + + hr = UserCreateADsPath(wzUserDomain, wzUser, &bstrUser); + ExitOnFailure(hr, "failed to create user ADsPath for user: %ls domain: %ls", wzUser, wzUserDomain); + + hr = UserCreateADsPath(wzGroupDomain, wzGroup, &bstrGroup); + ExitOnFailure(hr, "failed to create group ADsPath for group: %ls domain: %ls", wzGroup, wzGroupDomain); + + hr = ::ADsGetObject(bstrGroup,IID_IADsGroup, reinterpret_cast(&pGroup)); + ExitOnFailure(hr, "Failed to get group '%ls'.", reinterpret_cast(bstrGroup) ); + + hr = pGroup->Add(bstrUser); + if ((HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS) == hr) || (HRESULT_FROM_WIN32(ERROR_MEMBER_IN_ALIAS) == hr)) + hr = S_OK; + + ExitOnFailure(hr, "Failed to add user %ls to group '%ls'.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); + } + +LExit: + ReleaseObject(pGroup); + ReleaseBSTR(bstrUser); + ReleaseBSTR(bstrGroup); + + return hr; +} + +static HRESULT RemoveUserFromGroup( + __in LPWSTR wzUser, + __in LPCWSTR wzUserDomain, + __in LPCWSTR wzGroup, + __in LPCWSTR wzGroupDomain + ) +{ + Assert(wzUser && *wzUser && wzUserDomain && wzGroup && *wzGroup && wzGroupDomain); + + HRESULT hr = S_OK; + IADsGroup *pGroup = NULL; + BSTR bstrUser = NULL; + BSTR bstrGroup = NULL; + LPCWSTR wz = NULL; + LPWSTR pwzUser = NULL; + LOCALGROUP_MEMBERS_INFO_3 lgmi; + + if (*wzGroupDomain) + { + wz = wzGroupDomain; + } + + // Try removing it from the global group first + UINT ui = ::NetGroupDelUser(wz, wzGroup, wzUser); + if (NERR_GroupNotFound == ui) + { + // Try removing it from the local group + if (wzUserDomain) + { + hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzUserDomain, wzUser); + ExitOnFailure(hr, "failed to allocate user domain string"); + } + + lgmi.lgrmi3_domainandname = (NULL == pwzUser ? wzUser : pwzUser); + ui = ::NetLocalGroupDelMembers(wz, wzGroup, 3 , reinterpret_cast(&lgmi), 1); + } + hr = HRESULT_FROM_WIN32(ui); + + // + // If we failed, try active directory + // + if (FAILED(hr)) + { + WcaLog(LOGMSG_VERBOSE, "Failed to remove user: %ls, domain %ls from group: %ls, domain: %ls with error 0x%x. Attempting to use Active Directory", wzUser, wzUserDomain, wzGroup, wzGroupDomain, hr); + + hr = UserCreateADsPath(wzUserDomain, wzUser, &bstrUser); + ExitOnFailure(hr, "failed to create user ADsPath in order to remove user: %ls domain: %ls from a group", wzUser, wzUserDomain); + + hr = UserCreateADsPath(wzGroupDomain, wzGroup, &bstrGroup); + ExitOnFailure(hr, "failed to create group ADsPath in order to remove user from group: %ls domain: %ls", wzGroup, wzGroupDomain); + + hr = ::ADsGetObject(bstrGroup,IID_IADsGroup, reinterpret_cast(&pGroup)); + ExitOnFailure(hr, "Failed to get group '%ls'.", reinterpret_cast(bstrGroup) ); + + hr = pGroup->Remove(bstrUser); + ExitOnFailure(hr, "Failed to remove user %ls from group '%ls'.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); + } + +LExit: + ReleaseObject(pGroup); + ReleaseBSTR(bstrUser); + ReleaseBSTR(bstrGroup); + + return hr; +} + + +static HRESULT ModifyUserLocalServiceRight( + __in_opt LPCWSTR wzDomain, + __in LPCWSTR wzName, + __in BOOL fAdd + ) +{ + HRESULT hr = S_OK; + NTSTATUS nt = 0; + + LPWSTR pwzUser = NULL; + PSID psid = NULL; + LSA_HANDLE hPolicy = NULL; + LSA_OBJECT_ATTRIBUTES ObjectAttributes = { 0 }; + LSA_UNICODE_STRING lucPrivilege = { 0 }; + + if (wzDomain && *wzDomain) + { + hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName); + ExitOnFailure(hr, "Failed to allocate user with domain string"); + } + else + { + hr = StrAllocString(&pwzUser, wzName, 0); + ExitOnFailure(hr, "Failed to allocate string from user name."); + } + + hr = AclGetAccountSid(NULL, pwzUser, &psid); + ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser); + + nt = ::LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy); + hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt)); + ExitOnFailure(hr, "Failed to open LSA policy store."); + + lucPrivilege.Buffer = L"SeServiceLogonRight"; + lucPrivilege.Length = static_cast(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR)); + lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR); + + if (fAdd) + { + nt = ::LsaAddAccountRights(hPolicy, psid, &lucPrivilege, 1); + hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt)); + ExitOnFailure(hr, "Failed to add 'logon as service' bit to user: %ls", pwzUser); + } + else + { + nt = ::LsaRemoveAccountRights(hPolicy, psid, FALSE, &lucPrivilege, 1); + hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt)); + ExitOnFailure(hr, "Failed to remove 'logon as service' bit from user: %ls", pwzUser); + } + +LExit: + if (hPolicy) + { + ::LsaClose(hPolicy); + } + + ReleaseSid(psid); + ReleaseStr(pwzUser); + return hr; +} + + +static HRESULT ModifyUserLocalBatchRight( + __in_opt LPCWSTR wzDomain, + __in LPCWSTR wzName, + __in BOOL fAdd + ) +{ + HRESULT hr = S_OK; + NTSTATUS nt = 0; + + LPWSTR pwzUser = NULL; + PSID psid = NULL; + LSA_HANDLE hPolicy = NULL; + LSA_OBJECT_ATTRIBUTES ObjectAttributes = { 0 }; + LSA_UNICODE_STRING lucPrivilege = { 0 }; + + if (wzDomain && *wzDomain) + { + hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName); + ExitOnFailure(hr, "Failed to allocate user with domain string"); + } + else + { + hr = StrAllocString(&pwzUser, wzName, 0); + ExitOnFailure(hr, "Failed to allocate string from user name."); + } + + hr = AclGetAccountSid(NULL, pwzUser, &psid); + ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser); + + nt = ::LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy); + hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt)); + ExitOnFailure(hr, "Failed to open LSA policy store."); + + lucPrivilege.Buffer = L"SeBatchLogonRight"; + lucPrivilege.Length = static_cast(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR)); + lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR); + + if (fAdd) + { + nt = ::LsaAddAccountRights(hPolicy, psid, &lucPrivilege, 1); + hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt)); + ExitOnFailure(hr, "Failed to add 'logon as batch job' bit to user: %ls", pwzUser); + } + else + { + nt = ::LsaRemoveAccountRights(hPolicy, psid, FALSE, &lucPrivilege, 1); + hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt)); + ExitOnFailure(hr, "Failed to remove 'logon as batch job' bit from user: %ls", pwzUser); + } + + LExit: + if (hPolicy) + { + ::LsaClose(hPolicy); + } + + ReleaseSid(psid); + ReleaseStr(pwzUser); + return hr; +} + +static void SetUserPasswordAndAttributes( + __in USER_INFO_1* puserInfo, + __in LPWSTR wzPassword, + __in int iAttributes + ) +{ + Assert(puserInfo); + + // Set the User's password + puserInfo->usri1_password = wzPassword; + + // Apply the Attributes + if (SCAU_DONT_EXPIRE_PASSWRD & iAttributes) + { + puserInfo->usri1_flags |= UF_DONT_EXPIRE_PASSWD; + } + else + { + puserInfo->usri1_flags &= ~UF_DONT_EXPIRE_PASSWD; + } + + if (SCAU_PASSWD_CANT_CHANGE & iAttributes) + { + puserInfo->usri1_flags |= UF_PASSWD_CANT_CHANGE; + } + else + { + puserInfo->usri1_flags &= ~UF_PASSWD_CANT_CHANGE; + } + + if (SCAU_DISABLE_ACCOUNT & iAttributes) + { + puserInfo->usri1_flags |= UF_ACCOUNTDISABLE; + } + else + { + puserInfo->usri1_flags &= ~UF_ACCOUNTDISABLE; + } + + if (SCAU_PASSWD_CHANGE_REQD_ON_LOGIN & iAttributes) // TODO: for some reason this doesn't work + { + puserInfo->usri1_flags |= UF_PASSWORD_EXPIRED; + } + else + { + puserInfo->usri1_flags &= ~UF_PASSWORD_EXPIRED; + } +} + + +/******************************************************************** + CreateUser - CUSTOM ACTION ENTRY POINT for creating users + + Input: deferred CustomActionData - UserName\tDomain\tPassword\tAttributes\tGroupName\tDomain\tGroupName\tDomain... + * *****************************************************************/ +extern "C" UINT __stdcall CreateUser( + __in MSIHANDLE hInstall + ) +{ + //AssertSz(0, "Debug CreateUser"); + + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzData = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzName = NULL; + LPWSTR pwzDomain = NULL; + LPWSTR pwzPassword = NULL; + LPWSTR pwzGroup = NULL; + LPWSTR pwzGroupDomain = NULL; + PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL; + int iAttributes = 0; + BOOL fInitializedCom = FALSE; + + USER_INFO_1 userInfo; + USER_INFO_1* puserInfo = NULL; + DWORD dw; + LPCWSTR wz = NULL; + + hr = WcaInitialize(hInstall, "CreateUser"); + ExitOnFailure(hr, "failed to initialize"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "failed to initialize COM"); + fInitializedCom = TRUE; + + hr = WcaGetProperty( L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); + + // + // Read in the CustomActionData + // + pwz = pwzData; + hr = WcaReadStringFromCaData(&pwz, &pwzName); + ExitOnFailure(hr, "failed to read user name from custom action data"); + + hr = WcaReadStringFromCaData(&pwz, &pwzDomain); + ExitOnFailure(hr, "failed to read domain from custom action data"); + + hr = WcaReadIntegerFromCaData(&pwz, &iAttributes); + ExitOnFailure(hr, "failed to read attributes from custom action data"); + + hr = WcaReadStringFromCaData(&pwz, &pwzPassword); + ExitOnFailure(hr, "failed to read password from custom action data"); + + if (!(SCAU_DONT_CREATE_USER & iAttributes)) + { + ::ZeroMemory(&userInfo, sizeof(USER_INFO_1)); + userInfo.usri1_name = pwzName; + userInfo.usri1_priv = USER_PRIV_USER; + userInfo.usri1_flags = UF_SCRIPT; + userInfo.usri1_home_dir = NULL; + userInfo.usri1_comment = NULL; + userInfo.usri1_script_path = NULL; + + SetUserPasswordAndAttributes(&userInfo, pwzPassword, iAttributes); + + // + // Create the User + // + if (pwzDomain && *pwzDomain) + { + er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, NULL, &pDomainControllerInfo ); + if (RPC_S_SERVER_UNAVAILABLE == er) + { + // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag + er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo ); + } + if (ERROR_SUCCESS == er) + { + wz = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix + } + else + { + wz = pwzDomain; + } + } + + er = ::NetUserAdd(wz, 1, reinterpret_cast(&userInfo), &dw); + if (NERR_UserExists == er) + { + if (SCAU_UPDATE_IF_EXISTS & iAttributes) + { + er = ::NetUserGetInfo(wz, pwzName, 1, reinterpret_cast(&puserInfo)); + if (NERR_Success == er) + { + // Change the existing user's password and attributes again then try + // to update user with this new data + SetUserPasswordAndAttributes(puserInfo, pwzPassword, iAttributes); + + er = ::NetUserSetInfo(wz, pwzName, 1, reinterpret_cast(puserInfo), &dw); + } + } + else if (!(SCAU_FAIL_IF_EXISTS & iAttributes)) + { + er = NERR_Success; + } + } + else if (NERR_PasswordTooShort == er || NERR_PasswordTooLong == er) + { + MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrUSRFailedUserCreatePswd, "failed to create user: %ls due to invalid password.", pwzName); + } + MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrUSRFailedUserCreate, "failed to create user: %ls", pwzName); + } + + if (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes) + { + hr = ModifyUserLocalServiceRight(pwzDomain, pwzName, TRUE); + MessageExitOnFailure(hr, msierrUSRFailedGrantLogonAsService, "Failed to grant logon as service rights to user: %ls", pwzName); + } + + if (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes) + { + hr = ModifyUserLocalBatchRight(pwzDomain, pwzName, TRUE); + MessageExitOnFailure(hr, msierrUSRFailedGrantLogonAsService, "Failed to grant logon as batch job rights to user: %ls", pwzName); + } + + // + // Add the users to groups + // + while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup))) + { + hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain); + ExitOnFailure(hr, "failed to get domain for group: %ls", pwzGroup); + + hr = AddUserToGroup(pwzName, pwzDomain, pwzGroup, pwzGroupDomain); + MessageExitOnFailure(hr, msierrUSRFailedUserGroupAdd, "failed to add user: %ls to group %ls", pwzName, pwzGroup); + } + if (E_NOMOREITEMS == hr) // if there are no more items, all is well + { + hr = S_OK; + } + ExitOnFailure(hr, "failed to get next group in which to include user:%ls", pwzName); + +LExit: + if (puserInfo) + { + ::NetApiBufferFree((LPVOID)puserInfo); + } + + if (pDomainControllerInfo) + { + ::NetApiBufferFree((LPVOID)pDomainControllerInfo); + } + + ReleaseStr(pwzData); + ReleaseStr(pwzName); + ReleaseStr(pwzDomain); + ReleaseStr(pwzPassword); + ReleaseStr(pwzGroup); + ReleaseStr(pwzGroupDomain); + + if (fInitializedCom) + { + ::CoUninitialize(); + } + + if (SCAU_NON_VITAL & iAttributes) + { + er = ERROR_SUCCESS; + } + else if (FAILED(hr)) + { + er = ERROR_INSTALL_FAILURE; + } + + return WcaFinalize(er); +} + + +/******************************************************************** + RemoveUser - CUSTOM ACTION ENTRY POINT for removing users + + Input: deferred CustomActionData - Name\tDomain + * *****************************************************************/ +extern "C" UINT __stdcall RemoveUser( + MSIHANDLE hInstall + ) +{ + //AssertSz(0, "Debug RemoveAccount"); + + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzData = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzName = NULL; + LPWSTR pwzDomain= NULL; + LPWSTR pwzGroup = NULL; + LPWSTR pwzGroupDomain = NULL; + int iAttributes = 0; + LPCWSTR wz = NULL; + PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL; + BOOL fInitializedCom = FALSE; + + hr = WcaInitialize(hInstall, "RemoveUser"); + ExitOnFailure(hr, "failed to initialize"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "failed to initialize COM"); + fInitializedCom = TRUE; + + hr = WcaGetProperty(L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); + + // + // Read in the CustomActionData + // + pwz = pwzData; + hr = WcaReadStringFromCaData(&pwz, &pwzName); + ExitOnFailure(hr, "failed to read name from custom action data"); + + hr = WcaReadStringFromCaData(&pwz, &pwzDomain); + ExitOnFailure(hr, "failed to read domain from custom action data"); + + hr = WcaReadIntegerFromCaData(&pwz, &iAttributes); + ExitOnFailure(hr, "failed to read attributes from custom action data"); + + // + // Remove the logon as service privilege. + // + if (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes) + { + hr = ModifyUserLocalServiceRight(pwzDomain, pwzName, FALSE); + if (FAILED(hr)) + { + WcaLogError(hr, "Failed to remove logon as service right from user, continuing..."); + hr = S_OK; + } + } + + if (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes) + { + hr = ModifyUserLocalBatchRight(pwzDomain, pwzName, FALSE); + if (FAILED(hr)) + { + WcaLogError(hr, "Failed to remove logon as batch job right from user, continuing..."); + hr = S_OK; + } + } + + // + // Remove the User Account if the user was created by us. + // + if (!(SCAU_DONT_CREATE_USER & iAttributes)) + { + if (pwzDomain && *pwzDomain) + { + er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, NULL, &pDomainControllerInfo ); + if (RPC_S_SERVER_UNAVAILABLE == er) + { + // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag + er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo ); + } + if (ERROR_SUCCESS == er) + { + wz = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix + } + else + { + wz = pwzDomain; + } + } + + er = ::NetUserDel(wz, pwzName); + if (NERR_UserNotFound == er) + { + er = NERR_Success; + } + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to delete user account: %ls", pwzName); + } + else + { + // + // Remove the user from the groups + // + while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup))) + { + hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain); + + if (FAILED(hr)) + { + WcaLogError(hr, "failed to get domain for group: %ls, continuing anyway.", pwzGroup); + } + else + { + hr = RemoveUserFromGroup(pwzName, pwzDomain, pwzGroup, pwzGroupDomain); + if (FAILED(hr)) + { + WcaLogError(hr, "failed to remove user: %ls from group %ls, continuing anyway.", pwzName, pwzGroup); + } + } + } + + if (E_NOMOREITEMS == hr) // if there are no more items, all is well + { + hr = S_OK; + } + + ExitOnFailure(hr, "failed to get next group from which to remove user:%ls", pwzName); + } + +LExit: + if (pDomainControllerInfo) + { + ::NetApiBufferFree(static_cast(pDomainControllerInfo)); + } + + ReleaseStr(pwzData); + ReleaseStr(pwzName); + ReleaseStr(pwzDomain); + + if (fInitializedCom) + { + ::CoUninitialize(); + } + + if (FAILED(hr)) + { + er = ERROR_INSTALL_FAILURE; + } + + return WcaFinalize(er); +} -- cgit v1.2.3-55-g6feb