Esempio n. 1
0
/******************************************************************
 CaExecSecureObjects - entry point for SecureObjects Custom Action
				   called as Type 1025 CustomAction (deferred binary DLL)

 NOTE: deferred CustomAction since it modifies the machine
 NOTE: CustomActionData == wzObject\twzTable\twzDomain\twzUser\tdwPermissions\twzObject\t...
******************************************************************/
extern "C" UINT __stdcall ExecSecureObjects(
	__in MSIHANDLE hInstall
	)
{
//	AssertSz(FALSE, "debug ExecSecureObjects");
	HRESULT hr = S_OK;
	DWORD er = ERROR_SUCCESS;

	LPWSTR pwz = NULL;
	LPWSTR pwzData = NULL;
	LPWSTR pwzObject = NULL;
	LPWSTR pwzTable = NULL;
	LPWSTR pwzDomain = NULL;
	DWORD dwRevision = 0;
	LPWSTR pwzUser = NULL;
	DWORD dwPermissions = 0;
	LPWSTR pwzAccount = NULL;
	PSID psid = NULL;

	EXPLICIT_ACCESSW ea = {0};
	SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE;
	PSECURITY_DESCRIPTOR psd = NULL;
	SECURITY_DESCRIPTOR_CONTROL sdc = {0};
	SECURITY_INFORMATION si = {0};
	PACL pAclExisting = NULL;   // doesn't get freed
	PACL pAclNew = NULL;

	PMSIHANDLE hActionRec = ::MsiCreateRecord(1);

	//
	// initialize
	//
	hr = WcaInitialize(hInstall, "ExecSecureObjects");
	ExitOnFailure(hr, "failed to initialize");

	hr = WcaGetProperty(L"CustomActionData", &pwzData);
	ExitOnFailure(hr, "failed to get CustomActionData");

	WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %S", pwzData);

	pwz = pwzData;

	//
	// loop through all the passed in data
	//
	while (pwz && *pwz)
	{
		hr = WcaReadStringFromCaData(&pwz, &pwzObject);
		ExitOnFailure(hr, "failed to process CustomActionData");

		hr = WcaReadStringFromCaData(&pwz, &pwzTable);
		ExitOnFailure(hr, "failed to process CustomActionData");
		hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
		ExitOnFailure(hr, "failed to process CustomActionData");
		hr = WcaReadStringFromCaData(&pwz, &pwzUser);
		ExitOnFailure(hr, "failed to process CustomActionData");
		hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwPermissions));
		ExitOnFailure(hr, "failed to processCustomActionData");

		WcaLog(LOGMSG_VERBOSE, "Securing Object: %S Type: %S User: %S", pwzObject, pwzTable, pwzUser);

		//
		// create the appropriate SID
		//

		// figure out the right user to put into the access block
		if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Everyone"))
		{
			hr = AclGetWellKnownSid(WinWorldSid, &psid);
		}
		else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Administrators"))
		{
			hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &psid);
		}
		else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"LocalSystem"))
		{
			hr = AclGetWellKnownSid(WinLocalSystemSid, &psid);
		}
		else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"LocalService"))
		{
			hr = AclGetWellKnownSid(WinLocalServiceSid, &psid);
		}
		else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"NetworkService"))
		{
			hr = AclGetWellKnownSid(WinNetworkServiceSid, &psid);
		}
		else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"AuthenticatedUser"))
		{
			hr = AclGetWellKnownSid(WinAuthenticatedUserSid, &psid);
		}
		else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Guests"))
		{
			hr = AclGetWellKnownSid(WinBuiltinGuestsSid, &psid);
		}
		else if(!*pwzDomain && 0 == lstrcmpW(pwzUser, L"CREATOR OWNER"))
		{
			hr = AclGetWellKnownSid(WinCreatorOwnerSid, &psid);
		}
		else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"INTERACTIVE"))
		{
			hr = AclGetWellKnownSid(WinInteractiveSid, &psid);
		}
		else if(!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Users"))
		{
			hr = AclGetWellKnownSid(WinBuiltinUsersSid, &psid);
		}
		else
		{
			hr = StrAllocFormatted(&pwzAccount, L"%s\\%s", *pwzDomain ? pwzDomain : L".", pwzUser);
			ExitOnFailure(hr, "failed to build domain user name");

			hr = AclGetAccountSid(NULL, pwzAccount, &psid);
		}
		ExitOnFailure3(hr, "failed to get sid for account: %S%S%S", pwzDomain, *pwzDomain ? L"\\" : L"", pwzUser);

		//
		// build up the explicit access
		//
		ea.grfAccessPermissions = dwPermissions;
		ea.grfAccessMode = SET_ACCESS;

		if (0 == lstrcmpW(L"CreateFolder", pwzTable))
		{
			ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
		}
		else
		{
			ea.grfInheritance = NO_INHERITANCE;
		}

		::BuildTrusteeWithSidW(&ea.Trustee, psid);

		if (0 == lstrcmpW(L"ServiceInstall", pwzTable))
		{
			objectType = SE_SERVICE;

			// always add these permissions for services
			// these are basic permissions that are often forgotten
			dwPermissions |= SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_INTERROGATE;
		}
		else if (0 == lstrcmpW(L"CreateFolder", pwzTable) || 0 == lstrcmpW(L"File", pwzTable))
		{
			objectType = SE_FILE_OBJECT;
		}
		else if (0 == lstrcmpW(L"Registry", pwzTable))
		{
			objectType = SE_REGISTRY_KEY;
		}

		if (SE_UNKNOWN_OBJECT_TYPE != objectType)
		{
			er = ::GetNamedSecurityInfoW(pwzObject, objectType, DACL_SECURITY_INFORMATION, NULL, NULL, &pAclExisting, NULL, &psd);
			ExitOnFailure1(hr = HRESULT_FROM_WIN32(er), "failed to get security info for object: %S", pwzObject);

			//Need to see if DACL is protected so getting Descriptor information
			if(!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision))
			{
				ExitOnLastError1(hr, "failed to get security descriptor control for object: %S", pwzObject);
			}

			er = ::SetEntriesInAclW(1, &ea, pAclExisting, &pAclNew);
			ExitOnFailure1(hr = HRESULT_FROM_WIN32(er), "failed to add ACLs for object: %S", pwzObject);

			if (sdc & SE_DACL_PROTECTED)
			{
				si = DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION;
			}
			else
			{
				si = DACL_SECURITY_INFORMATION;
			}
			er = ::SetNamedSecurityInfoW(pwzObject, objectType, si, NULL, NULL, pAclNew, NULL);
			MessageExitOnFailure1(hr = HRESULT_FROM_WIN32(er), msierrSecureObjectsFailedSet, "failed to set security info for object: %S", pwzObject);
		}
		else
		{
			MessageExitOnFailure1(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %S", pwzTable);
		}

		hr = WcaProgressMessage(COST_SECUREOBJECT, FALSE);
		ExitOnFailure(hr, "failed to send progress message");

		objectType = SE_UNKNOWN_OBJECT_TYPE;
	}

LExit:
	ReleaseStr(pwzUser);
	ReleaseStr(pwzDomain);
	ReleaseStr(pwzTable);
	ReleaseStr(pwzObject);
	ReleaseStr(pwzData);
	ReleaseStr(pwzAccount);

	if (pAclNew)
		::LocalFree(pAclNew);
	if (psd)
		::LocalFree(psd);
	if (psid)
		AclFreeSid(psid);

	if (FAILED(hr))
		er = ERROR_INSTALL_FAILURE;
	return WcaFinalize(er);
}
Esempio n. 2
0
/* ****************************************************************
ScaUserExecute - Schedules user account creation or removal based on
component state.

******************************************************************/
HRESULT ScaUserExecute(
    __in SCA_USER *psuList
    )
{
    HRESULT hr = S_OK;
    DWORD er = 0;
    PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;

    USER_INFO_0 *pUserInfo = NULL;
    LPWSTR pwzActionData = NULL;
    LPWSTR pwzRollbackData = NULL;

    for (SCA_USER *psu = psuList; psu; psu = psu->psuNext)
    {
        USER_EXISTS ueUserExists = USER_EXISTS_INDETERMINATE;

        // Always put the User Name and Domain plus Attributes on the front of the CustomAction
        // data.  Sometimes we'll add more data.
        Assert(psu->wzName);
        hr = WcaWriteStringToCaData(psu->wzName, &pwzActionData);
        ExitOnFailure1(hr, "Failed to add user name to custom action data: %ls", psu->wzName);
        hr = WcaWriteStringToCaData(psu->wzDomain, &pwzActionData);
        ExitOnFailure1(hr, "Failed to add user domain to custom action data: %ls", psu->wzDomain);
        hr = WcaWriteIntegerToCaData(psu->iAttributes, &pwzActionData);
        ExitOnFailure1(hr, "failed to add user attributes to custom action data for user: %ls", psu->wzKey);

        // Check to see if the user already exists since we have to be very careful when adding
        // and removing users.  Note: MSDN says that it is safe to call these APIs from any
        // user, so we should be safe calling it during immediate mode.
        er = ::NetApiBufferAllocate(sizeof(USER_INFO_0), reinterpret_cast<LPVOID*>(&pUserInfo));
        hr = HRESULT_FROM_WIN32(er);
        ExitOnFailure1(hr, "Failed to allocate memory to check existence of user: %ls", psu->wzName);

        LPCWSTR wzDomain = psu->wzDomain;
        if (wzDomain && *wzDomain)
        {
            er = ::DsGetDcNameW(NULL, wzDomain, NULL, NULL, NULL, &pDomainControllerInfo);
            if (HRESULT_FROM_WIN32(er) == RPC_S_SERVER_UNAVAILABLE)
            {
                // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag
                er = ::DsGetDcNameW(NULL, wzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo);
            }
            if (ERROR_SUCCESS == er)
            {
                wzDomain = pDomainControllerInfo->DomainControllerName + 2;  //Add 2 so that we don't get the \\ prefix
            }
        }

        er = ::NetUserGetInfo(wzDomain, psu->wzName, 0, reinterpret_cast<LPBYTE*>(pUserInfo));
        if (NERR_Success == er)
        {
            ueUserExists = USER_EXISTS_YES;
        }
        else if (NERR_UserNotFound == er)
        {
            ueUserExists = USER_EXISTS_NO;
        }
        else
        {
            ueUserExists = USER_EXISTS_INDETERMINATE;
            hr = HRESULT_FROM_WIN32(er);
            WcaLog(LOGMSG_VERBOSE, "Failed to check existence of domain: %ls, user: %ls (error code 0x%x) - continuing", wzDomain, psu->wzName, hr);
        }

        if (WcaIsInstalling(psu->isInstalled, psu->isAction))
        {
            // If the user exists, check to see if we are supposed to fail if user the exists before
            // the install.
            if (USER_EXISTS_YES == ueUserExists)
            {
                // Reinstalls will always fail if we don't remove the check for "fail if exists".
                if (WcaIsReInstalling(psu->isInstalled, psu->isAction))
                {
                    psu->iAttributes &= ~SCAU_FAIL_IF_EXISTS;
                }

                if ((SCAU_FAIL_IF_EXISTS & (psu->iAttributes)) && !(SCAU_UPDATE_IF_EXISTS & (psu->iAttributes)))
                {
                    hr = HRESULT_FROM_WIN32(NERR_UserExists);
                    MessageExitOnFailure1(hr, msierrUSRFailedUserCreateExists, "Failed to create user: %ls because user already exists.", psu->wzName);
                }
            }

            // Rollback only if the user already exists, we couldn't determine if the user exists, or we are going to create the user
            if ((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists) || !(psu->iAttributes & SCAU_DONT_CREATE_USER))
            {
                INT iRollbackUserAttributes = psu->iAttributes;

                // If the user already exists, ensure this is accounted for in rollback
                if (USER_EXISTS_YES == ueUserExists)
                {
                    iRollbackUserAttributes |= SCAU_DONT_CREATE_USER;
                }
                else
                {
                    iRollbackUserAttributes &= ~SCAU_DONT_CREATE_USER;
                }

                hr = WcaWriteStringToCaData(psu->wzName, &pwzRollbackData);
                ExitOnFailure1(hr, "Failed to add user name to rollback custom action data: %ls", psu->wzName);
                hr = WcaWriteStringToCaData(psu->wzDomain, &pwzRollbackData);
                ExitOnFailure1(hr, "Failed to add user domain to rollback custom action data: %ls", psu->wzDomain);
                hr = WcaWriteIntegerToCaData(iRollbackUserAttributes, &pwzRollbackData);
                ExitOnFailure1(hr, "failed to add user attributes to rollback custom action data for user: %ls", psu->wzKey);

                // If the user already exists, add relevant group information to rollback data
                if (USER_EXISTS_YES == ueUserExists || USER_EXISTS_INDETERMINATE == ueUserExists)
                {
                    hr = WriteGroupRollbackInfo(psu->wzName, psu->wzDomain, psu->psgGroups, &pwzRollbackData);
                    ExitOnFailure(hr, "failed to add group information to rollback custom action data");
                }

                hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"CreateUserRollback"), pwzRollbackData, COST_USER_DELETE);
                ExitOnFailure(hr, "failed to schedule CreateUserRollback");
            }

            //
            // Schedule the creation now.
            //
            hr = WcaWriteStringToCaData(psu->wzPassword, &pwzActionData);
            ExitOnFailure1(hr, "failed to add user password to custom action data for user: %ls", psu->wzKey);

            // Add user's group information to custom action data
            hr = WriteGroupInfo(psu->psgGroups, &pwzActionData);
            ExitOnFailure(hr, "failed to add group information to custom action data");

            hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"CreateUser"), pwzActionData, COST_USER_ADD);
            ExitOnFailure(hr, "failed to schedule CreateUser");
        }
        else if (((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists)) && WcaIsUninstalling(psu->isInstalled, psu->isAction) && !(psu->iAttributes & SCAU_DONT_REMOVE_ON_UNINSTALL))
        {
            // Add user's group information - this will ensure the user can be removed from any groups they were added to, if the user isn't be deleted
            hr = WriteGroupInfo(psu->psgGroups, &pwzActionData);
            ExitOnFailure(hr, "failed to add group information to custom action data");

            //
            // Schedule the removal because the user exists and we don't have any flags set
            // that say, don't remove the user on uninstall.
            //
            // Note: We can't rollback the removal of a user which is why RemoveUser is a commit 
            // CustomAction.
            hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RemoveUser"), pwzActionData, COST_USER_DELETE);
            ExitOnFailure(hr, "failed to schedule RemoveUser");
        }

        ReleaseNullStr(pwzActionData);
        ReleaseNullStr(pwzRollbackData);
        if (pUserInfo)
        {
            ::NetApiBufferFree(static_cast<LPVOID>(pUserInfo));
            pUserInfo = NULL;
        }
        if (pDomainControllerInfo)
        {
            ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
            pDomainControllerInfo = NULL;
        }
    }

LExit:
    ReleaseStr(pwzActionData);
    ReleaseStr(pwzRollbackData);
    if (pUserInfo)
    {
        ::NetApiBufferFree(static_cast<LPVOID>(pUserInfo));
    }
    if (pDomainControllerInfo)
    {
        ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
    }

    return hr;
}
Esempio n. 3
0
HRESULT CpiApplicationRolesVerifyInstall(
    CPI_APPLICATION_ROLE_LIST* pList
    )
{
    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;

    ICatalogObject* piRoleObj = NULL;

    for (CPI_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
    {
        // referenced locaters or roles that are being installed
        if (!pItm->fReferencedForInstall && !(pItm->fHasComponent && WcaIsInstalling(pItm->isInstalled, pItm->isAction)))
            continue;

        // if the role is referensed and is not a locater, it must be installed
        if (pItm->fReferencedForInstall && pItm->fHasComponent && !CpiWillBeInstalled(pItm->isInstalled, pItm->isAction))
            MessageExitOnFailure1(hr = E_FAIL, msierrComPlusApplicationRoleDependency, "An application role is used by another entity being installed, but is not installed itself, key: %S", pItm->wzKey);

        // role is a locater
        if (!pItm->fHasComponent)
        {
            // get collection object for role
            hr = FindObjectForApplicationRole(pItm, &piRoleObj);
            ExitOnFailure(hr, "Failed to find collection object for role");

            // if the role was not found
            if (S_FALSE == hr)
                MessageExitOnFailure1(hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND), msierrComPlusApplicationRoleNotFound, "An application role required by this installation was not found, key: %S", pItm->wzKey);
        }

        // role is supposed to be created
        else if (!CpiIsInstalled(pItm->isInstalled))
        {
            do {
                // find roles with conflicting name or id
                hr = FindObjectForApplicationRole(pItm, NULL);
                ExitOnFailure(hr, "Failed to find collection object for role");

                if (S_OK == hr)
                {
                    er = WcaErrorMessage(msierrComPlusApplicationRoleConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0);
                    switch (er)
                    {
                    case IDABORT:
                        ExitOnFailure1(hr = E_FAIL, "An application with a conflictiong name exists, key: %S", pItm->wzKey);
                        break;
                    case IDRETRY:
                        break;
                    case IDIGNORE:
                    default:
                        hr = S_FALSE; // indicate that this is not a conflict
                    }
                }
            } while (S_OK == hr); // hr = S_FALSE if we don't have any conflicts
        }

        // clean up
        ReleaseNullObject(piRoleObj);
    }

    hr = S_OK;

LExit:
    // clean up
    ReleaseObject(piRoleObj);

    return hr;
}
Esempio n. 4
0
/******************************************************************
 ExecXmlConfig - entry point for XmlConfig Custom Action

*******************************************************************/
extern "C" UINT __stdcall ExecXmlConfig(
    __in MSIHANDLE hInstall
    )
{
    //AssertSz(FALSE, "debug ExecXmlConfig");
    HRESULT hr = S_OK;
    HRESULT hrOpenFailure = S_OK;
    UINT er = ERROR_SUCCESS;

    BOOL fIsWow64Process = FALSE;
    BOOL fIsFSRedirectDisabled = FALSE;
    BOOL fPreserveDate = FALSE;

    LPWSTR pwzCustomActionData = NULL;
    LPWSTR pwzData = NULL;
    LPWSTR pwzFile = NULL;
    LPWSTR pwzElementPath = NULL;
    LPWSTR pwzVerifyPath = NULL;
    LPWSTR pwzName = NULL;
    LPWSTR pwzValue = NULL;
    LPWSTR pwz = NULL;
    int cAdditionalChanges = 0;

    IXMLDOMDocument* pixd = NULL;
    IXMLDOMNode* pixn = NULL;
    IXMLDOMNode* pixnVerify = NULL;
    IXMLDOMNode* pixnNewNode = NULL;
    IXMLDOMNode* pixnRemovedChild = NULL;

    IXMLDOMDocument* pixdNew = NULL;
    IXMLDOMElement* pixeNew = NULL;

    FILETIME ft;

    int id = IDRETRY;

    eXmlAction xa;
    eXmlPreserveDate xd;

    // initialize
    hr = WcaInitialize(hInstall, "ExecXmlConfig");
    ExitOnFailure(hr, "failed to initialize");

    hr = XmlInitialize();
    ExitOnFailure(hr, "failed to initialize xml utilities");

    hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
    ExitOnFailure(hr, "failed to get CustomActionData");

    WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);

    pwz = pwzCustomActionData;

    hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
    ExitOnFailure(hr, "failed to process CustomActionData");

    // Initialize the Wow64 API - store the result in fWow64APIPresent
    // If it fails, this doesn't warrant an error yet, because we only need the Wow64 API in some cases
    WcaInitializeWow64();
    fIsWow64Process = WcaIsWow64Process();

    if (xaOpenFile != xa && xaOpenFilex64 != xa)
    {
        ExitOnFailure(hr = E_INVALIDARG, "invalid custom action data");
    }

    // loop through all the passed in data
    while (pwz && *pwz)
    {
        hr = WcaReadStringFromCaData(&pwz, &pwzFile);
        ExitOnFailure(hr, "failed to read file name from custom action data");

        // Default to not preserve date, preserve it if any modifications require us to
        fPreserveDate = FALSE;

        // Open the file
        ReleaseNullObject(pixd);

        if (xaOpenFilex64 == xa)
        {
            if (!fIsWow64Process)
            {
                hr = E_NOTIMPL;
                ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but the custom action process is not running in WOW.");
            }

            hr = WcaDisableWow64FSRedirection();
            ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API.");

            fIsFSRedirectDisabled = TRUE;
        }

        hr = XmlLoadDocumentFromFileEx(pwzFile, XML_LOAD_PRESERVE_WHITESPACE, &pixd);
        if (FAILED(hr))
        {
            // Ignore the return code for now.  If they try to add something, we'll fail the install.  If all they do is remove stuff then it doesn't matter.
            hrOpenFailure = hr;
            hr = S_OK;
        }
        else
        {
            hrOpenFailure = S_OK;
        }

        WcaLog(LOGMSG_VERBOSE, "Configuring Xml File: %ls", pwzFile);

        while (pwz && *pwz)
        {
            // If we skip past an element that has additional changes we need to strip them off the stream before
            // moving on to the next element. Do that now and then restart the outer loop.
            if (cAdditionalChanges > 0)
            {
                while (cAdditionalChanges > 0)
                {
                    hr = WcaReadStringFromCaData(&pwz, &pwzName);
                    ExitOnFailure(hr, "failed to process CustomActionData");
                    hr = WcaReadStringFromCaData(&pwz, &pwzValue);
                    ExitOnFailure(hr, "failed to process CustomActionData");

                    cAdditionalChanges--;
                }
                continue;
            }

            hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
            ExitOnFailure(hr, "failed to process CustomActionData");

            // Break if we need to move on to a different file
            if (xaOpenFile == xa || xaOpenFilex64 == xa)
            {
                break;
            }

            hr = WcaReadIntegerFromCaData(&pwz, (int*) &xd);
            ExitOnFailure(hr, "failed to process CustomActionData");

            if (xdPreserve == xd)
            {
                fPreserveDate = TRUE;
            }

            // Get path, name, and value to be written
            hr = WcaReadStringFromCaData(&pwz, &pwzElementPath);
            ExitOnFailure(hr, "failed to process CustomActionData");
            hr = WcaReadStringFromCaData(&pwz, &pwzVerifyPath);
            ExitOnFailure(hr, "failed to process CustomActionData");
            hr = WcaReadStringFromCaData(&pwz, &pwzName);
            ExitOnFailure(hr, "failed to process CustomActionData");
            hr = WcaReadStringFromCaData(&pwz, &pwzValue);
            ExitOnFailure(hr, "failed to process CustomActionData");
            hr = WcaReadIntegerFromCaData(&pwz, &cAdditionalChanges);
            ExitOnFailure(hr, "failed to process CustomActionData");

            // If we failed to open the file and we're adding something to the file, we've got a problem.  Otherwise, just continue on since the file's already gone.
            if (FAILED(hrOpenFailure))
            {
                if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa)
                {
                    MessageExitOnFailure1(hr = hrOpenFailure, msierrXmlConfigFailedOpen, "failed to load XML file: %ls", pwzFile);
                }
                else
                {
                    continue;
                }
            }

            // Select the node we're about to modify
            ReleaseNullObject(pixn);

            hr = XmlSelectSingleNode(pixd, pwzElementPath, &pixn);

            // If we failed to find the node that we are going to add to, we've got a problem. Otherwise, just continue since the node's already gone.
            if (S_FALSE == hr)
            {
                if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa)
                {
                    hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
                }
                else
                {
                    hr = S_OK;
                    continue;
                }
            }

            MessageExitOnFailure2(hr, msierrXmlConfigFailedSelect, "failed to find node: %ls in XML file: %ls", pwzElementPath, pwzFile);

            // Make the modification
            switch (xa)
            {
            case xaWriteValue:
                if (pwzName && *pwzName)
                {
                    // We're setting an attribute
                    hr = XmlSetAttribute(pixn, pwzName, pwzValue);
                    ExitOnFailure2(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
                }
                else
                {
                    // We're setting the text of the node
                    hr = XmlSetText(pixn, pwzValue);
                    ExitOnFailure2(hr, "failed to set text to: %ls for element %ls.  Make sure that XPath points to an element.", pwzValue, pwzElementPath);
                }
                break;
            case xaWriteDocument:
                if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
                {
                    hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
                    if (S_OK == hr)
                    {
                        // We found the verify path which means we have no further work to do
                        continue;
                    }
                    ExitOnFailure1(hr, "failed to query verify path: %ls", pwzVerifyPath);
                }

                hr = XmlLoadDocumentEx(pwzValue, XML_LOAD_PRESERVE_WHITESPACE, &pixdNew);
                ExitOnFailure(hr, "Failed to load value as document.");

                hr = pixdNew->get_documentElement(&pixeNew);
                ExitOnFailure(hr, "Failed to get document element.");

                hr = pixn->appendChild(pixeNew, NULL);
                ExitOnFailure(hr, "Failed to append document element on to parent element.");

                ReleaseNullObject(pixeNew);
                ReleaseNullObject(pixdNew);
                break;

            case xaCreateElement:
                if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
                {
                    hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
                    if (S_OK == hr)
                    {
                        // We found the verify path which means we have no further work to do
                        continue;
                    }
                    ExitOnFailure1(hr, "failed to query verify path: %ls", pwzVerifyPath);
                }

                hr = XmlCreateChild(pixn, pwzName, &pixnNewNode);
                ExitOnFailure1(hr, "failed to create child element: %ls", pwzName);

                if (pwzValue && *pwzValue)
                {
                    hr = XmlSetText(pixnNewNode, pwzValue);
                    ExitOnFailure2(hr, "failed to set text to: %ls for node: %ls", pwzValue, pwzName);
                }

                while (cAdditionalChanges > 0)
                {
                    hr = WcaReadStringFromCaData(&pwz, &pwzName);
                    ExitOnFailure(hr, "failed to process CustomActionData");
                    hr = WcaReadStringFromCaData(&pwz, &pwzValue);
                    ExitOnFailure(hr, "failed to process CustomActionData");

                    // Set the additional attribute
                    hr = XmlSetAttribute(pixnNewNode, pwzName, pwzValue);
                    ExitOnFailure2(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);

                    cAdditionalChanges--;
                }

                ReleaseNullObject(pixnNewNode);
                break;
            case xaDeleteValue:
                if (pwzName && *pwzName)
                {
                    // Delete the attribute
                    hr = XmlRemoveAttribute(pixn, pwzName);
                    ExitOnFailure1(hr, "failed to remove attribute: %ls", pwzName);
                }
                else
                {
                    // Clear the text value for the node
                    hr = XmlSetText(pixn, L"");
                    ExitOnFailure(hr, "failed to clear text value");
                }
                break;
            case xaDeleteElement:
                if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
                {
                    hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
                    if (S_OK == hr)
                    {
                        hr = pixn->removeChild(pixnVerify, &pixnRemovedChild);
                        ExitOnFailure(hr, "failed to remove created child element");

                        ReleaseNullObject(pixnRemovedChild);
                    }
                    else
                    {
                        WcaLog(LOGMSG_VERBOSE, "Failed to select path %ls for deleting.  Skipping...", pwzVerifyPath);
                        hr = S_OK;
                    }
                }
                else
                {
                    // TODO: This requires a VerifyPath to delete an element.  Should we support not having one?
                    WcaLog(LOGMSG_VERBOSE, "No VerifyPath specified for delete element of ID: %ls", pwzElementPath);
                }
                break;
            default:
                ExitOnFailure(hr = E_UNEXPECTED, "Invalid modification specified in custom action data");
                break;
            }
        }


        // Now that we've made all of the changes to this file, save it and move on to the next
        if (S_OK == hrOpenFailure)
        {
            if (fPreserveDate)
            {
                hr = FileGetTime(pwzFile, NULL, NULL, &ft);
                ExitOnFailure1(hr, "failed to get modified time of file : %ls", pwzFile);
            }

            int iSaveAttempt = 0;

            do
            {
                hr = XmlSaveDocument(pixd, pwzFile);
                if (FAILED(hr))
                {
                    id = WcaErrorMessage(msierrXmlConfigFailedSave, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 1, pwzFile);
                    switch (id)
                    {
                    case IDABORT:
                        ExitOnFailure1(hr, "Failed to save changes to XML file: %ls", pwzFile);
                    case IDRETRY:
                        hr = S_FALSE;   // hit me, baby, one more time
                        break;
                    case IDIGNORE:
                        hr = S_OK;  // pretend everything is okay and bail
                        break;
                    case 0: // No UI case, MsiProcessMessage returns 0
                        if (STIERR_SHARING_VIOLATION == hr)
                        {
                            // Only in case of sharing violation do we retry 30 times, once a second.
                            if (iSaveAttempt < 30)
                            {
                                hr = S_FALSE;
                                ++iSaveAttempt;
                                WcaLog(LOGMSG_VERBOSE, "Unable to save changes to XML file: %ls, retry attempt: %x", pwzFile, iSaveAttempt);
                                Sleep(1000);
                            }
                            else
                            {
                                ExitOnFailure1(hr, "Failed to save changes to XML file: %ls", pwzFile);
                            }
                        }
                        break;
                    default: // Unknown error
                        ExitOnFailure1(hr, "Failed to save changes to XML file: %ls", pwzFile);
                    }
                }
            } while (S_FALSE == hr);

            if (fPreserveDate)
            {
                hr = FileSetTime(pwzFile, NULL, NULL, &ft);
                ExitOnFailure1(hr, "failed to set modified time of file : %ls", pwzFile);
            }

            if (fIsFSRedirectDisabled)
            {
                fIsFSRedirectDisabled = FALSE;
                WcaRevertWow64FSRedirection();
            }
        }
    }

LExit:
    // Make sure we revert FS Redirection if necessary before exiting
    if (fIsFSRedirectDisabled)
    {
        fIsFSRedirectDisabled = FALSE;
        WcaRevertWow64FSRedirection();
    }
    WcaFinalizeWow64();

    ReleaseStr(pwzCustomActionData);
    ReleaseStr(pwzData);
    ReleaseStr(pwzFile);
    ReleaseStr(pwzElementPath);
    ReleaseStr(pwzVerifyPath);
    ReleaseStr(pwzName);
    ReleaseStr(pwzValue);

    ReleaseObject(pixeNew);
    ReleaseObject(pixdNew);

    ReleaseObject(pixn);
    ReleaseObject(pixd);
    ReleaseObject(pixnNewNode);
    ReleaseObject(pixnRemovedChild);

    XmlUninitialize();

    if (FAILED(hr))
    {
        er = ERROR_INSTALL_FAILURE;
    }
    return WcaFinalize(er);
}
Esempio n. 5
0
static HRESULT StoreACLRollbackInfo(
    __in LPWSTR pwzObject,
    __in LPCWSTR pwzTable
    )
{
    HRESULT hr = S_OK;
    DWORD er = ERROR_SUCCESS;
    PSECURITY_DESCRIPTOR psd = NULL;
    SECURITY_DESCRIPTOR_CONTROL sdc = {0};
    DWORD dwRevision = 0;
    LPWSTR pwzCustomActionData = NULL;
    LPWSTR pwzSecurityInfo = NULL;
    
    Assert(pwzObject && pwzTable);

    SE_OBJECT_TYPE objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable));

    if (SE_UNKNOWN_OBJECT_TYPE != objectType)
    {
        er = ::GetNamedSecurityInfoW(pwzObject, objectType, DACL_SECURITY_INFORMATION, NULL, NULL, NULL, NULL, &psd);
        if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er || ERROR_SERVICE_DOES_NOT_EXIST == HRESULT_CODE(er))
        {
            // If the file, path or service doesn't exist yet, skip rollback without a message
            hr = HRESULT_FROM_WIN32(er);
            ExitFunction();
        }

        ExitOnFailure1(hr = HRESULT_FROM_WIN32(er), "Unable to schedule rollback for object: %ls", pwzObject);

        //Need to see if DACL is protected so getting Descriptor information
        if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision))
        {
            ExitOnLastError1(hr, "Unable to schedule rollback for object (failed to get security descriptor control): %ls", pwzObject);
        }

        // Convert the security information to a string, and write this to the custom action data
        if (!::ConvertSecurityDescriptorToStringSecurityDescriptorW(psd,SDDL_REVISION_1,DACL_SECURITY_INFORMATION,&pwzSecurityInfo,NULL))
        {
            hr = E_UNEXPECTED;
            ExitOnFailure1(hr, "Unable to schedule rollback for object (failed to convert security descriptor to a valid security descriptor string): %ls", pwzObject);
        }

        hr = WcaWriteStringToCaData(pwzObject, &pwzCustomActionData);
        ExitOnFailure(hr, "failed to add object data to rollback CustomActionData");

        hr = WcaWriteStringToCaData(pwzTable, &pwzCustomActionData);
        ExitOnFailure(hr, "failed to add table name to rollback CustomActionData");

        hr = WcaWriteStringToCaData(pwzSecurityInfo, &pwzCustomActionData);
        ExitOnFailure(hr, "failed to add security info data to rollback CustomActionData");

        // Write a 1 if DACL is protected, 0 otherwise
        if (sdc & SE_DACL_PROTECTED)
        {
            hr = WcaWriteIntegerToCaData(1,&pwzCustomActionData);
            ExitOnFailure(hr, "failed to add data to rollbackCustomActionData");
        }
        else
        {
            hr = WcaWriteIntegerToCaData(0,&pwzCustomActionData);
            ExitOnFailure(hr, "failed to add data to rollback CustomActionData");
        }

        hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"ExecSecureObjectsRollback"), pwzCustomActionData, COST_SECUREOBJECT);
        ExitOnFailure2(hr, "failed to schedule ExecSecureObjectsRollback for item: %ls of type: %ls", pwzObject, pwzTable);

        ReleaseStr(pwzCustomActionData);
        pwzCustomActionData = NULL;

    }
    else
    {
        MessageExitOnFailure1(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable);
    }
LExit:
    ReleaseStr(pwzCustomActionData);

    if (psd)
    {
        ::LocalFree(psd);
    }

    return hr;
}
Esempio n. 6
0
extern "C" UINT __stdcall ExecSecureObjectsRollback(
    __in MSIHANDLE hInstall
    )
{
//    AssertSz(FALSE, "debug ExecSecureObjectsRollback");
    HRESULT hr = S_OK;
    DWORD er = ERROR_SUCCESS;

    LPWSTR pwz = NULL;
    LPWSTR pwzData = NULL;
    LPWSTR pwzObject = NULL;
    LPWSTR pwzTable = NULL;
    LPWSTR pwzSecurityInfo = NULL;

    SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE;
    PSECURITY_DESCRIPTOR psd = NULL;
    ULONG psdSize;
    SECURITY_DESCRIPTOR_CONTROL sdc = {0};
    SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION;
    PACL pDacl = NULL;
    BOOL bDaclPresent = false;
    BOOL bDaclDefaulted = false;
    DWORD dwRevision = 0;
    int iProtected;

    // initialize
    hr = WcaInitialize(hInstall, "ExecSecureObjectsRollback");
    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, &pwzObject);
    ExitOnFailure(hr, "failed to process CustomActionData");

    hr = WcaReadStringFromCaData(&pwz, &pwzTable);
    ExitOnFailure(hr, "failed to process CustomActionData");

    objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable));

    if (SE_UNKNOWN_OBJECT_TYPE != objectType)
    {
        hr = WcaReadStringFromCaData(&pwz, &pwzSecurityInfo);
        ExitOnFailure(hr, "failed to process CustomActionData");

        hr = WcaReadIntegerFromCaData(&pwz, &iProtected);
        ExitOnFailure(hr, "failed to process CustomActionData");

        if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSecurityInfo,SDDL_REVISION_1,&psd,&psdSize))
        {
            ExitOnLastError(hr, "failed to convert security descriptor string to a valid security descriptor");
        }

        if (!::GetSecurityDescriptorDacl(psd,&bDaclPresent,&pDacl,&bDaclDefaulted))
        {
            hr = E_UNEXPECTED;
            ExitOnFailure2(hr, "failed to get security descriptor's DACL - error code: %d",pwzSecurityInfo,GetLastError());
        }

        // The below situation may always be caught by the above if block - the documentation isn't very clear. To be safe, we're going to test for it.
        if (!bDaclPresent)
        {
            hr = E_UNEXPECTED;
            ExitOnFailure(hr, "security descriptor does not contain a DACL");
        }

        //Need to see if DACL is protected so getting Descriptor information
        if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision))
        {
            ExitOnLastError1(hr, "failed to get security descriptor control for object: %ls", pwzObject);
        }

        // Write a 1 if DACL is protected, 0 otherwise
        switch (iProtected)
        {
        case 0:
            // Unnecessary to do anything - leave si to the default flags
            break;

        case 1:
            si = si | PROTECTED_DACL_SECURITY_INFORMATION;
            break;

        default:
            hr = E_UNEXPECTED;
            ExitOnFailure(hr, "unrecognized value in CustomActionData");
            break;
        }

        er = ::SetNamedSecurityInfoW(pwzObject, objectType, si, NULL, NULL, pDacl, NULL);
        ExitOnFailure2(hr = HRESULT_FROM_WIN32(er), "failed to set security info for object: %ls error code: %d", pwzObject, GetLastError());
    }
    else
    {
        MessageExitOnFailure1(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable);
    }

LExit:
    ReleaseStr(pwzData);
    ReleaseStr(pwzObject);
    ReleaseStr(pwzTable);
    ReleaseStr(pwzSecurityInfo);

    if (psd)
    {
        ::LocalFree(psd);
    }

    if (FAILED(hr))
    {
        er = ERROR_INSTALL_FAILURE;
    }
    return WcaFinalize(er);
}
Esempio n. 7
0
HRESULT CpiPartitionsVerifyInstall(
    CPI_PARTITION_LIST* pList
    )
{
    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;

    ICatalogCollection* piPartColl = NULL;
    ICatalogObject* piPartObj = NULL;

    for (CPI_PARTITION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
    {
        // referenced locaters or partitions that are being installed
        if (!pItm->fReferencedForInstall && !(pItm->fHasComponent && WcaIsInstalling(pItm->isInstalled, pItm->isAction)))
            continue;

        // if the partition is referensed and is not a locater, it must be installed
        if (pItm->fReferencedForInstall && pItm->fHasComponent && !CpiWillBeInstalled(pItm->isInstalled, pItm->isAction))
            MessageExitOnFailure1(hr = E_FAIL, msierrComPlusPartitionDependency, "A partition is used by another entity being installed, but is not installed itself, key: %S", pItm->wzKey);

        // get partitions collection
        if (!piPartColl)
        {
            hr = CpiGetPartitionsCollection(&piPartColl);
            ExitOnFailure(hr, "Failed to get partitions collection");
        }

        // partition is supposed to exist
        if (!pItm->fHasComponent || CpiIsInstalled(pItm->isInstalled))
        {
            // get collection object for partition
            hr = CpiFindCollectionObject(piPartColl, pItm->wzID, *pItm->wzID ? NULL : pItm->wzName, &piPartObj);
            ExitOnFailure(hr, "Failed to find collection object for partition");

            // if the partition was found
            if (S_OK == hr)
            {
                // if we don't have an id, copy id from object
                if (!*pItm->wzID)
                {
                    hr = CpiGetKeyForObject(piPartObj, pItm->wzID, countof(pItm->wzID));
                    ExitOnFailure(hr, "Failed to get id");
                }
            }

            // if the partition was not found
            else
            {
                // if the application is a locater, this is an error
                if (!pItm->fHasComponent)
                    MessageExitOnFailure1(hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND), msierrComPlusPartitionNotFound, "A partition required by this installation was not found, key: %S", pItm->wzKey);

                // create a new id if one is missing
                if (!*pItm->wzID)
                {
                    hr = CpiCreateId(pItm->wzID, countof(pItm->wzID));
                    ExitOnFailure(hr, "Failed to create id");
                }
            }
        }

        // partition is supposed to be created
        else
        {
            // check for conflicts
            do {
                if (*pItm->wzID)
                {
                    // find partitions with conflicting id
                    hr = CpiFindCollectionObject(piPartColl, pItm->wzID, NULL, &piPartObj);
                    ExitOnFailure(hr, "Failed to find collection object for partition");

                    if (S_FALSE == hr)
                    {
                        // find partitions with conflicting name
                        hr = CpiFindCollectionObject(piPartColl, NULL, pItm->wzName, &piPartObj);
                        ExitOnFailure(hr, "Failed to find collection object for partition");

                        if (S_OK == hr)
                            // "A partition with a conflictiong name exists. retry cancel"
                            er = WcaErrorMessage(msierrComPlusPartitionNameConflict, hr, INSTALLMESSAGE_ERROR | MB_RETRYCANCEL, 0);
                        else
                            break; // no conflicting entry found, break loop
                    }
                    else
                        // "A partition with a conflicting id exists. abort retry ignore"
                        er = WcaErrorMessage(msierrComPlusPartitionIdConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0);
                }
                else
                {
                    // find partitions with conflicting name
                    hr = CpiFindCollectionObject(piPartColl, NULL, pItm->wzName, &piPartObj);
                    ExitOnFailure(hr, "Failed to find collection object for partition");

                    if (S_OK == hr)
                        // "A partition with a conflictiong name exists. abort retry ignore"
                        er = WcaErrorMessage(msierrComPlusPartitionNameConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0);
                    else
                        break; // no conflicting entry found, break loop
                }

                switch (er)
                {
                case IDCANCEL:
                case IDABORT:
                    ExitOnFailure1(hr = E_FAIL, "A partition with a conflictiong name or id exists, key: %S", pItm->wzKey);
                    break;
                case IDRETRY:
                    break;
                case IDIGNORE:
                default:
                    // if we don't have an id, copy id from object
                    if (!*pItm->wzID)
                    {
                        hr = CpiGetKeyForObject(piPartObj, pItm->wzID, countof(pItm->wzID));
                        ExitOnFailure(hr, "Failed to get id");
                    }
                    hr = S_FALSE; // indicate that this is not a conflict
                }
            } while (S_OK == hr); // hr = S_FALSE if we don't have any conflicts

            // create a new id if one is missing
            if (!*pItm->wzID)
            {
                hr = CpiCreateId(pItm->wzID, countof(pItm->wzID));
                ExitOnFailure(hr, "Failed to create id");
            }
        }

        // clean up
        ReleaseNullObject(piPartObj);
    }

    hr = S_OK;

LExit:
    // clean up
    ReleaseObject(piPartColl);
    ReleaseObject(piPartObj);

    return hr;
}