Click here to Skip to main content
12,695,044 members (31,795 online)
Click here to Skip to main content

Stats

165.5K views
6K downloads
98 bookmarked
Posted

The Windows Access Control Model Part 4

, 7 Sep 2005
The final article in the access control series presents a guide to the access control editor and its associated ISecurityInformation interface.
/**
*	This code is by OShah. all code will have the following licence.
*	Copyright Shexec32. All code bears the following licence:
**/

/**
*	Copyright Shexec32 2004-2005. All rights reserved.
*
*	THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
*	ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
*	TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
*	PARTICULAR PURPOSE.
**/

#include "SecurityCore.h"
#include "resource2.h"
/* Setting the security is a major topic in itself. */


STDMETHODIMP
CObjSecurity::SetSecurity(SECURITY_INFORMATION psi, PSECURITY_DESCRIPTOR pSD)
{/** This function is Non reentrant. You must exit this function before reentering it.
*	However, it keeps to this class, therefore, has the same scope as the class.
**/
	DWORD dwErr = S_OK;

	/* We're an API: validate. */
	if(::IsValidSecurityDescriptor(pSD) != TRUE)
	{
		return E_POINTER;
	}

	/* If this is read only, don't do anything */
	if(this->m_dwSIFlags & SI_READONLY)
		return E_ACCESSDENIED;


	/** Enter the critical section AFTER we enabled the privileges, to avoid deadlocks.
	*	This critical section is run because the ACLAPI functions are not thread safe.
	*
	*	UNDONE: Removed the critical section. The race condition described in SetNamedSecurityInfo
	*	is simply the classic FS race condition inherent in all multitasking OSes (program S
	*	reads a file, then program T writes to the file in the middle of the read; what's
	*	program S going to do now)? Putting in a critical section doesn't solve the problem, it just
	*	increases the risk of a deadlock.
	**/
	{
		int MBoxRes = 0;
		if(this->KeepExplicit == 2)
		{/* Only ask this once, and store "don't ask again" in a global (member) class variable. */
			if((psi & SI_RESET_DACL_TREE || psi & SI_RESET_SACL_TREE))
			{/* If the user has specified SI_RECURSE, we can ask two questions at once here. */
				MBoxRes = ::MessageBox(this->get_currenthWnd(),
					_T("By default, FilePermsBox removes the explicit ACLs of the child objects, which ")
					_T("is the same way as Windows behaves. Would you like to keep the explicit ACLs of ")
					_T("the child objects? [Select No to mimic Windows behaviour]"),
					_T("FilePermsBox - Keep Explicit ACLs?"), MB_YESNOCANCEL | MB_ICONQUESTION | MB_DEFBUTTON2);

				switch(MBoxRes)
				{/* Translate the Answer. */
					case IDYES:
						KeepExplicit = TRUE;
						break;
					case IDNO:
						KeepExplicit = FALSE;
						break;
					default:
					case IDCANCEL:/* Exit at this point. */
						return S_OK;
				}
			}
			else
			{/* We'll just ask the "Are you sure?" question. */
				MBoxRes = ::MessageBox(this->get_currenthWnd(),
					_T("Are you sure you want to change the security descriptor for the selected object[s]?"),
					_T("Change File Security?"), MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2);
				if(MBoxRes != IDYES)
				{
					return S_OK;
				}
			}
		}
	}

	/* Hourglass the mouse. */
	HANDLE hCursor = ::LoadImage(NULL, MAKEINTRESOURCE(OCR_APPSTARTING), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
	if(hCursor != NULL)/* If we couldn't load the cursor, just ignore it. It's not important. */
		SetClassLongPtr(this->get_currenthWnd(), GCLP_HCURSOR, LONG_PTR2(hCursor));

	std::list< const std::basic_string<TCHAR> > DirectoryCollection;

	for(std::list< sized_array<TCHAR> >::iterator ObjIter = this->m_pszObjectName.begin();
		ObjIter != this->m_pszObjectName.end(); ObjIter++)
	{/* For each object in the collection, get their security descriptor */
		DirectoryCollection.push_back(ObjIter->get());
	}
	
	/* DirectoryCollection now contains the entire list of files we need to set the security for. */

	try
	{/* Now we are at the point of no return. Beyond here, we need cleanup */
		this->CProgressObj.InitProgressDialog(this->get_currenthWnd());
		{/* Estimate the total number of files. */
			std::list< const std::basic_string<TCHAR> > DirectoryCollection2 = DirectoryCollection;
			this->GetListOfFiles(DirectoryCollection2, TRUE);
			this->CProgressObj.set_Count(2 * DirectoryCollection2.size());
			/* We have n empty separators (one for each primary item) that we need to normalise */
		}

		/* Set the security for all objects in the collection. */
		::SetLastError(ERROR_SUCCESS);
		dwErr = this->SetSecurityForCollection(DirectoryCollection, psi, pSD);

		/* finally, reset KeepExplicit to Uninitialized. */
	} catch(...) {/* finally */
		dwErr = ERROR_INTERNAL_ERROR;
	}

	this->KeepExplicit = 2;
	this->CProgressObj.Finish();
	if(dwErr == ERROR_SUCCESS)
	{/* Inform the shell of the change (like rshx32 does). */
		dwErr = this->RefreshShell(this->m_dwSIFlags & SI_CONTAINER);
	}

	/* And put the cursor to normal */
	hCursor = ::LoadImage(NULL, MAKEINTRESOURCE(OCR_NORMAL), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
	if(hCursor != NULL)
		SetClassLongPtr(this->get_currenthWnd(), GCLP_HCURSOR, LONG_PTR2(hCursor));


	return HRESULT_FROM_WIN32(dwErr);
}



DWORD
CObjSecurity::SetSecurityForCollection(const std::list<const std::basic_string<TCHAR> > &DirectoryCollection, SECURITY_INFORMATION psi, PSECURITY_DESCRIPTOR pSD)
{
	DWORD dwErr = ERROR_SUCCESS;

	PrivMgr SaclPriv(SE_SECURITY_NAME, &this->PrivClassImpl);
	PrivMgr OwnerPriv(SE_TAKE_OWNERSHIP_NAME, &this->PrivClassImpl);
	/* We may need these privileges. They will be disabled once we've reset security. */
	if(psi & SACL_SECURITY_INFORMATION)
	{
		SaclPriv.TurnOn();
	}
	if((psi & SI_OWNER_RECURSE))
	{/* In fact, we do need the privilege */
		OwnerPriv.TurnOn();
	}


	for(std::list< const std::basic_string<TCHAR> >::const_iterator m_ObjectName = DirectoryCollection.begin();
		m_ObjectName != DirectoryCollection.end(); m_ObjectName++)
	{/* Walk each object manually, and set the security for each object. */

		sized_array<TCHAR> FinalName(*m_ObjectName);
		this->CProgressObj.IncProgressBar(m_ObjectName->c_str());

		dwErr = this->SetSecurityForObject(FinalName.get(), psi, pSD);
		if(dwErr != ERROR_SUCCESS)
		{
			break;
		}
	}
	return dwErr;
}




STDMETHODIMP
CObjSecurity::SetSecurityForObject(TCHAR *m_ObjectName,
	const SECURITY_INFORMATION psi2, const PSECURITY_DESCRIPTOR pSD)
{/* This custom method sets the security for the object specified in m_ObjectName. */
	DWORD dwErr = ERROR_INVALID_FUNCTION;
	SECURITY_INFORMATION psi = psi2;
	PSECURITY_DESCRIPTOR ppSD = NULL;
	PACL NewSAcl = NULL, NewDAcl = NULL;

	try {

		/* We need to get the old security descriptor. */
		PSID SidOwner = NULL, SidGroup = NULL;
		PACL OldSAcl = NULL, OldDAcl = NULL;

		dwErr = ::GetNamedSecurityInfo(m_ObjectName, this->SeObjectType, psi, &SidOwner, 
			&SidGroup, &OldDAcl, &OldSAcl, &ppSD);
		/* Convert result from winerror.h to bool */
		::SetLastError(dwErr);
		if(dwErr == ERROR_SUCCESS) dwErr = TRUE;
		else dwErr = FALSE;

		/* Convert ppSD to Absolute Format. */
		if(dwErr == TRUE)
		{
			PACL AbsDAcl = NULL, AbsSAcl = NULL;
			PSID AbsOwner = NULL, AbsGroup = NULL;
			DWORD dwSize = 0, dwSizes[5] = {0};
			PSECURITY_DESCRIPTOR ppSDRel = ppSD;/* Transfer ppSD into this variable. */

			dwErr = ::MakeAbsoluteSD(ppSDRel, NULL, &dwSizes[0], NULL, &dwSizes[4], NULL, &dwSizes[3],
				NULL, &dwSizes[2], NULL, &dwSizes[1]);
			for(size_t i = 0; i < 5; i++) dwSize += dwSizes[i];
			ppSD = reinterpret_cast<PSECURITY_DESCRIPTOR>
				( ::LocalAlloc(LPTR, 2 * sizeof(ACL) + 2 * sizeof(SID) + dwSize) );
			if(ppSDRel == NULL) throw STD(_T("Could not allocate memory"));
			/* HACKHACK: Allocate a 2D array with one LocalAlloc */

			/* Now set the pointers to the appropriate offsets. */
			dwSize = dwSizes[0];
			AbsOwner = reinterpret_cast<PSID>(reinterpret_cast<BYTE *>(ppSD) + dwSize);
			dwSize += sizeof(SID) + dwSizes[1];
			AbsGroup = reinterpret_cast<PSID>(reinterpret_cast<BYTE *>(ppSD) + dwSize);
			dwSize += sizeof(SID) + dwSizes[2];
			AbsSAcl = reinterpret_cast<PACL>(reinterpret_cast<BYTE *>(ppSD) + dwSize);
			dwSize += sizeof(ACL) + dwSizes[3];
			AbsDAcl = reinterpret_cast<PACL>(reinterpret_cast<BYTE *>(ppSD) + dwSize);

			::SetLastError(ERROR_SUCCESS);
			dwErr = ::MakeAbsoluteSD(ppSDRel, ppSD, &dwSizes[0], AbsDAcl, &dwSizes[4], AbsSAcl,
				&dwSizes[3], AbsOwner, &dwSizes[2], AbsGroup, &dwSizes[1]);
			::LocalFree(ppSDRel); ppSDRel = NULL;
		}

		if(dwErr == TRUE)
		{/* If we could not get the old Acl, just use the SD supplied (skip this block). */
			::SetLastError(ERROR_SUCCESS);
			BOOL AclPresent = FALSE, AclDefaulted = FALSE;

			{/** If the new SD requires us to not use Inheritance, we must switch psi to
			*	PROTECTED_DACL_SECURITY_INFORMATION (apparently). The SD specifies Inheritance through
			*	the control flags.
			**/
				SECURITY_DESCRIPTOR_CONTROL pSDControl = {0};
				DWORD SDRevision = 0;
				dwErr = ::GetSecurityDescriptorControl(pSD, &pSDControl, &SDRevision);
				if(psi & DACL_SECURITY_INFORMATION)
				{
					if(pSDControl & SE_DACL_PROTECTED)
					{/* The DACL must not inherit. Switch psi to PROTECTED_DACL_INFORMATION */
						psi |= PROTECTED_DACL_SECURITY_INFORMATION;
						dwErr = ::SetSecurityDescriptorControl(ppSD, SE_DACL_PROTECTED, SE_DACL_PROTECTED);
					}
					if(pSDControl & SE_DACL_AUTO_INHERIT_REQ)
					{/* We must toggle the DACL's inheritance. */
						psi |= UNPROTECTED_DACL_SECURITY_INFORMATION;
						dwErr = ::SetSecurityDescriptorControl(ppSD, SE_DACL_AUTO_INHERIT_REQ, SE_DACL_AUTO_INHERIT_REQ);
					}
				}
				if(psi & SACL_SECURITY_INFORMATION)
				{
					if(pSDControl & SE_SACL_PROTECTED)
					{/* ...And SACL */
						psi |= PROTECTED_SACL_SECURITY_INFORMATION;
						dwErr = ::SetSecurityDescriptorControl(ppSD, SE_SACL_PROTECTED, SE_SACL_PROTECTED);
					}
					if(pSDControl & SE_SACL_AUTO_INHERIT_REQ)
					{/* ...And SACL. */
						psi |= UNPROTECTED_SACL_SECURITY_INFORMATION;
						dwErr = ::SetSecurityDescriptorControl(ppSD, SE_SACL_AUTO_INHERIT_REQ, SE_SACL_AUTO_INHERIT_REQ);
					}
				}
			}

			if(psi & DACL_SECURITY_INFORMATION)
			{/* Get the new DACL. */
				dwErr = ::GetSecurityDescriptorDacl(pSD, &AclPresent, &NewDAcl, &AclDefaulted);

				/* NewDAcl should now hold the new ACL List. */
				dwErr = ::SetSecurityDescriptorDacl(ppSD, AclPresent, NewDAcl, AclDefaulted);
			}
			if(psi & SACL_SECURITY_INFORMATION)
			{/* Get the new SACL. */
				::GetSecurityDescriptorSacl(pSD, &AclPresent, &NewSAcl, &AclDefaulted);
				/* Merge the two Acls. */

				::SetSecurityDescriptorSacl(ppSD, AclPresent, NewSAcl, AclDefaulted);
			}
			if(psi & GROUP_SECURITY_INFORMATION)
			{/* Get the New Group SID */
				::GetSecurityDescriptorGroup(pSD, &SidGroup, &AclDefaulted);
				/* Replace the SidGroup with this SID. */
				::SetSecurityDescriptorGroup(ppSD, SidGroup, AclDefaulted);
			}
			if(psi & OWNER_SECURITY_INFORMATION)
			{/* Get the New Owner SID */
				::GetSecurityDescriptorOwner(pSD, &SidOwner, &AclDefaulted);
				/* Replace the SidGroup with this SID. */
				::SetSecurityDescriptorOwner(ppSD, SidOwner, AclDefaulted);
			}
		}
		if((psi & SI_OWNER_RECURSE || psi & SI_RESET_DACL_TREE || psi & SI_RESET_SACL_TREE) && this->IsContainer(m_ObjectName))
		{/* Call the XP provided function. */
			try {
				if(this->pfnTreeResetNamedSecurityInfo)
				{
					psi = psi &~ ( SI_OWNER_RECURSE | SI_RESET_DACL_TREE | SI_RESET_SACL_TREE);
					dwErr = this->pfnTreeResetNamedSecurityInfo(m_ObjectName, this->SeObjectType, psi, SidOwner, SidGroup, NewDAcl,
						NewSAcl, this->KeepExplicit,
						reinterpret_cast<FN_PROGRESS>(reinterpret_cast<void *>(TreeCallBackFunc)),
						ProgressInvokeEveryObject, reinterpret_cast<void *>(this));
				}
				else
				{
					dwErr = this->TreeResetWin2k(m_ObjectName, ppSD, psi);
				}
			} catch (...) {
				::MessageBox(this->get_currenthWnd(),
					_T("A fatal error occurred trying to set the security for the object tree. No further objects will be processed."),
					_T("FilePermsBox - Fatal Error"), MB_OK | MB_ICONEXCLAMATION);
				::LocalFree(ppSD); ppSD = NULL;
				return E_UNEXPECTED;
			}
			::SetLastError(dwErr);
		}
		else
		{/* WalkAndSetSecurity has already walked the directory for us and all we need to do is set the security. */
#if (!defined _DEBUG || !defined PRETEND_TO_DO_IT)
			psi = psi &~ ( SI_OWNER_RECURSE | SI_RESET_DACL_TREE | SI_RESET_SACL_TREE);
			::SetLastError(ERROR_SUCCESS);
			dwErr = ::SetNamedSecurityInfo(m_ObjectName, this->SeObjectType, psi, SidOwner,
				SidGroup, NewDAcl, NewSAcl);
			::SetLastError(dwErr);
#else /* DEBUG builds only: with a recompile, instead of actually writing the results, you can see the resultant security descriptor in the debugger */
			psi = psi &~ ( SI_OWNER_RECURSE | SI_RESET_DACL_TREE | SI_RESET_SACL_TREE);
			LPTSTR sppSD = NULL, sppSD2 = NULL;
			::ConvertSecurityDescriptorToStringSecurityDescriptor(ppSD, SDDL_REVISION_1, psi, &sppSD, NULL);


			PSECURITY_DESCRIPTOR ppSDCurrent = NULL;
			dwErr = this->GetSecurity(psi, &ppSDCurrent, FALSE);
			::ConvertSecurityDescriptorToStringSecurityDescriptor(ppSDCurrent, SDDL_REVISION_1, psi, &sppSD2, NULL);

			std::basic_stringstream<TCHAR> TError;
			TError << std::hex << sppSD2 << _T(" ==> ") << _T("Set(\"") << m_ObjectName << _T("\", ") << psi <<
				_T(", ") << sppSD << _T(");") << std::endl << std::dec << std::ends;
			::OutputDebugString(TError.str().c_str());

			::LocalFree(sppSD2); sppSD2 = NULL;
			::LocalFree(sppSD); sppSD = NULL;
			::LocalFree(ppSDCurrent); ppSDCurrent = NULL;
#endif /* PRETEND_TO_DO_IT */
		}


	} catch (const std::basic_string<TCHAR> &) {/* We threw an exception, Don't alter Security. */
		dwErr = ::GetLastError();	/* send the error to ACLUI */
	}
	::LocalFree(ppSD); ppSD = NULL;
	return dwErr;
}



DWORD
CObjSecurity::TreeResetWin2k(TCHAR *m_ObjectName, PSECURITY_DESCRIPTOR ppSD, SECURITY_INFORMATION psi)
{/**	An implementation of the TreeResetNamedSecurityInfo function. Win2k doesn't have this function,
*	So we need to emulate it. Since we know we're dealing with a CObjSecurity, we can make shortcuts regarding
*	the parameters to pass in. This is especially true with the TreeCallBackFunc.
*	For this function it is important to know the features of TreeResetNamedSecurityInfo, and how it behaves.
*
*	<quote>
*	The TreeResetNamedSecurityInfo function resets specified security information in the security descriptor of
*	a specified tree of objects. This function allows a specified discretionary access control list (DACL).
*	This function supports a callback function to track the progress of the tree operation.
*	</quote>
**/

	DWORD dwErr = 0, SecuritySet = 0;
	SECURITY_INFORMATION psi2 = psi &~ ( SI_OWNER_RECURSE | SI_RESET_DACL_TREE | SI_RESET_SACL_TREE);
	std::list<const std::basic_string<TCHAR> > ChildCollection;
	ChildCollection.push_back(m_ObjectName);

	if(((psi & SI_RESET_DACL_TREE) || (psi & SI_RESET_SACL_TREE)) && this->KeepExplicit)
	{
		psi2 = psi2 &~ (PROTECTED_DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION);
	}

	/* The default security descriptor needs to be applied to all children of this object. */
	PSECURITY_DESCRIPTOR pSD2 = NULL;
	if(psi & DACL_SECURITY_INFORMATION || psi & SACL_SECURITY_INFORMATION)
		dwErr = this->GetSecurity(psi, &pSD2, TRUE);
	if(pSD2 == NULL)
	{/* If default security cannot be used, use a COPY of the security descriptor. */
		LPTSTR lpszSD = NULL;
		::SetLastError(ERROR_SUCCESS);
		if(::ConvertSecurityDescriptorToStringSecurityDescriptor(ppSD, SDDL_REVISION_1,
			psi, &lpszSD, NULL) != TRUE)
		{
			return ::GetLastError();
		}
		/** pSD2 = ppSD causes duplicate reference bugs. BuildSecurityDescriptor/CreatePrivateObjectSecurity
		*	Requires complete security descriptors (which we don't have).
		**/
		dwErr = ::ConvertStringSecurityDescriptorToSecurityDescriptor(lpszSD, SDDL_REVISION_1,
			&pSD2, NULL);
		::LocalFree(lpszSD); lpszSD = NULL;
		if(dwErr != TRUE)
		{
			dwErr = ::GetLastError();
			::LocalFree(pSD2); pSD2 = NULL;
			return dwErr;
		}
		/* pSD2 will be freed later. */
	}

	/**
	*	Here is a summary of the algorithm:
	*
	*	!isContainer ?	-> Do direct :
	*	GetFiles before
	*	Reverse list.
	*	Recurse all children
	*	Do This
	*	GetFiles after
	*	Eliminate all entries already set
	*	recurse into resultant list.
	*
	*	Now add in PROG_INVOKE_SETTING support, and progress dialog support.
	**/


	this->GetListOfFiles(ChildCollection, FALSE);
	ChildCollection.pop_front();
	ChildCollection.reverse();
	std::list<const std::basic_string<TCHAR> >::iterator Iter;
	for(Iter = ChildCollection.begin(); Iter != ChildCollection.end();)
	{
		/** Since we need to need retry support, Iter++ does not occur. There is at least one case where
		*	Iter++ would be incorrect.
		**/
		dwErr = ERROR_SUCCESS;
		sized_array<TCHAR> childObjectName = *Iter;

		if(this->pInvokeSetting == ::ProgressRetryOperation)
		{/* On a retry, set the security on this object and Do not walk. */
			this->pInvokeSetting = ::ProgressInvokeEveryObject;
			dwErr = this->SetSecurityForObject(childObjectName.get(), psi2, pSD2);
		}
		else
		{
			dwErr = this->SetSecurityForObject(childObjectName.get(), psi, pSD2);
		}

		/* Inform the call back of the result */
		SecuritySet = (this->pInvokeSetting == ProgressInvokeNever) ? (TRUE) : (dwErr == ERROR_SUCCESS);
		TreeCallBackFunc(childObjectName.get(), dwErr, &pInvokeSetting, reinterpret_cast<void *>(this),
			SecuritySet);
		/** Even if the user has asked us never to show the error dialog again, we still need to enter the
		*	CallBackFunc to update the progress dialog. However, if we enter TreeCallBackFunc in an error,
		*	The error dialog will appear, and the user will be most unhappy. Therefore, for ProgressInvokeNever,
		*	always pretend we succeeded.
		**/
		switch(pInvokeSetting)
		{
			case ::ProgressRetryOperation:
			{
				continue;
			}
			case ::ProgressCancelOperation:
			{
				::LocalFree(pSD2); pSD2 = NULL;
				return dwErr;
			}

			default:
			{
				dwErr = ERROR_SUCCESS;
				break;
			}
		}
		Iter++;
	}

	for(;;)
	{/* goto-less Retry */
		dwErr = this->SetSecurityForObject(m_ObjectName, psi2, ppSD);

		/* Inform the call back of the result */
		SecuritySet = (this->pInvokeSetting == ProgressInvokeNever) ? (TRUE) : (dwErr == ERROR_SUCCESS);
		TreeCallBackFunc(m_ObjectName, dwErr, &this->pInvokeSetting, reinterpret_cast<void *>(this), SecuritySet);
		switch(pInvokeSetting)
		{
			case ::ProgressRetryOperation:
			{
				/* continue: now do you see why I have that redundant for() loop? */
				continue;
			}
			case ::ProgressCancelOperation:
			{
				::LocalFree(pSD2); pSD2 = NULL;
				return dwErr;
			}

			default:
			{
				dwErr = ERROR_SUCCESS;
				break;
			}
		}
		break;
	}

	/* Now that the folder is operational, start getting the list of child objects */
	if(dwErr == ERROR_SUCCESS)
	{/* Our new security descriptor has opened the door to new entries. Get them. */
		std::list<const std::basic_string<TCHAR> > ChildCollection2;
		ChildCollection2.push_back(m_ObjectName);
		this->GetListOfFiles(ChildCollection2, FALSE);
		ChildCollection2.pop_front();
		for(Iter = ChildCollection2.begin(); Iter != ChildCollection2.end();)
		{
			/* For the same reason as above, iteration takes place at the end. */
			dwErr = ERROR_SUCCESS;
			sized_array<TCHAR> childObjectName = *Iter;
			if(!this->IsDuplicate(*Iter, ChildCollection))
			{
				if(this->pInvokeSetting == ::ProgressRetryOperation)
				{/* On a retry, set the security on this object and Do not walk. */
					this->pInvokeSetting = ::ProgressInvokeEveryObject;
					dwErr = this->SetSecurityForObject(childObjectName.get(), psi2, pSD2);
				}
				else
				{
					dwErr = this->SetSecurityForObject(childObjectName.get(), psi, pSD2);
				}
			}
			SecuritySet = (this->pInvokeSetting == ProgressInvokeNever) ? (TRUE) : (dwErr == ERROR_SUCCESS);
			TreeCallBackFunc(childObjectName.get(), dwErr, &pInvokeSetting, reinterpret_cast<void *>(this),
				SecuritySet);
			/* This is a new file, that we have yet to set security on. */
			switch(pInvokeSetting)
			{
				case ::ProgressRetryOperation:
				{
					/* continue: now do you see why I have that redundant for() loop? */
					continue;
				}
				case ::ProgressCancelOperation:
				{
					::LocalFree(pSD2); pSD2 = NULL;
					return dwErr;
				}

				default:
				{
					dwErr = ERROR_SUCCESS;
					break;
				}
			}

			Iter++;
		}
	}


	::LocalFree(pSD2); pSD2 = NULL;
	return dwErr;
}


By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

oshah
Web Developer
United States United States
Mr. Shah is a reclusive C++/C# developer lurking somewhere in the depths of the city of London. He learnt physics at Kings' College London and obtained a Master in Science there. Having earned an MCAD, he teeters on the brink of transitioning from C++ to C#, unsure of which language to jump to. Fortunately, he also knows how to use .NET interop to merge code between the two languages (which means he won't have to make the choice anytime soon).

His interests (apart from programming) are walking, football (the real one!), philosophy, history, retro-gaming, strategy gaming, and any good game in general.

He maintains a website / blog / FAQ / junk at shexec32.serveftp.net, where he places the best answers he's written to the questions you've asked. If you can find him, maybe you can hire Mr. Shah to help you with anything C++[/CLI]/C#/.NET related Smile | :) .

You may also be interested in...

Pro
Pro
| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.170118.1 | Last Updated 7 Sep 2005
Article Copyright 2005 by oshah
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid