Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

785 lines
15 KiB

// Copyright (C) 1997 Microsoft Corporation
//
// ADSI wrapper
//
// 9-22-97 sburns
#include "headers.hxx"
#include "adsi.hpp"
const String ADSI::PROVIDER(L"WinNT");
const String ADSI::PROVIDER_ROOT(L"WinNT://");
const String ADSI::CLASS_User(USER_CLASS_NAME);
const String ADSI::CLASS_Group(GROUP_CLASS_NAME);
const String ADSI::CLASS_Computer(COMPUTER_CLASS_NAME);
const String ADSI::PATH_SEP(L"/");
const String ADSI::PROPERTY_PasswordExpired(L"PasswordExpired");
const String ADSI::PROPERTY_UserFlags(L"UserFlags");
const String ADSI::PROPERTY_LocalDrive(L"HomeDirDrive");
const String ADSI::PROPERTY_UserParams(L"Parameters");
const String ADSI::PROPERTY_ObjectSID(L"ObjectSID");
const String ADSI::PROPERTY_GroupType(L"groupType");
// InetOrgPerson needs to be supported as if it was a user.
// The WINNT provider always returns inetOrgPerson objects
// as users but the LDAP provider returns them as inetOrgPerson.
// This string is used for the comparison
// NTRAID#NTBUG9-436314-2001/07/16-jeffjon
const String ADSI::CLASS_InetOrgPerson(L"inetOrgPerson");
static const String COMPUTER_SUFFIX(L"," COMPUTER_CLASS_NAME);
static
HRESULT
buildEnumerator(
SmartInterface<IADsContainer>& container,
IEnumVARIANT*& enumVariant);
static
HRESULT
buildEnumerator(IADsMembers& members, IEnumVARIANT*& enumVariant);
static
HRESULT
enumerateNext(
IEnumVARIANT& enumVariant,
int numberElementsToRead,
_variant_t destinationBuf[],
int& elementsRead);
static
HRESULT
freeEnumerator(IEnumVARIANT& enumerator);
static
HRESULT
buildEnumerator(
SmartInterface<IADsContainer>& container,
IEnumVARIANT*& enumVariant)
{
LOG_FUNCTION(buildEnumerator);
return ::ADsBuildEnumerator(container, &enumVariant);
}
HRESULT
buildEnumerator(IADsMembers& members, IEnumVARIANT*& enumVariant)
{
LOG_FUNCTION(buildEnumerator);
// this fast-and-loose cast is from ADSI sample code...
return ::ADsBuildEnumerator((IADsContainer*) &members, &enumVariant);
}
HRESULT
freeEnumerator(IEnumVARIANT& enumerator)
{
LOG_FUNCTION(freeEnumerator);
return ::ADsFreeEnumerator(&enumerator);
}
HRESULT
enumerateNext(
IEnumVARIANT& enumVariant,
int numberElementsToRead,
_variant_t destinationBuf[],
int& elementsRead)
{
LOG_FUNCTION(enumerateNext);
ASSERT(numberElementsToRead);
ULONG n = 0;
HRESULT hr =
::ADsEnumerateNext(
&enumVariant,
numberElementsToRead,
destinationBuf,
&n);
// this cast is safe, as we never ask for more elements than can be
// counted with an int.
elementsRead = static_cast<int>(n);
return hr;
}
HRESULT
walkArray(
_variant_t varray[],
int arraySize,
ADSI::ObjectVisitor& visitor)
{
LOG_FUNCTION(walkArray);
HRESULT hr = S_OK;
for (int i = 0; i < arraySize; i++ )
{
IDispatch* dispatch = varray[i].pdispVal;
ASSERT(dispatch);
if (dispatch)
{
IADs* object = 0;
hr = dispatch->QueryInterface(QI_PARAMS(IADs, &object));
BREAK_ON_FAILED_HRESULT(hr);
ASSERT(object);
if (object)
{
SmartInterface<IADs> so(0);
so.Acquire(object);
visitor.Visit(so);
}
}
// we do not release dispatch, as this is done when the array is
// destroyed: _variant_t::~_variant_t calls VariantClear. As each
// element has type VT_DISPATCH, VariantClear releases the interface for
// us.
}
return hr;
}
HRESULT
walkEnumeration(IEnumVARIANT& enumerator, ADSI::ObjectVisitor& visitor)
{
LOG_FUNCTION(walkEnumeration);
// read 100 values at a time.
static const int MAX_ADS_ENUM = 100;
HRESULT hr = S_OK;
bool done = false;
do
{
_variant_t varray[MAX_ADS_ENUM];
int objects_fetched = 0;
hr =
enumerateNext(
enumerator,
MAX_ADS_ENUM,
varray,
objects_fetched);
BREAK_ON_FAILED_HRESULT(hr);
done = (hr == S_FALSE) ? true : false;
hr = walkArray(varray, objects_fetched, visitor);
BREAK_ON_FAILED_HRESULT(hr);
}
while (!done);
return hr;
}
buildVarArrayStr(
const StringVector& strings,
_variant_t& v)
{
LOG_FUNCTION(buildVarArrayStr);
ASSERT(strings.size());
PWSTR* a = new PWSTR[strings.size()];
for (int i = 0; i < strings.size(); i++)
{
a[i] = const_cast<wchar_t*>(strings[i].c_str());
}
HRESULT hr =
::ADsBuildVarArrayStr(a, static_cast<DWORD>(strings.size()), &v);
delete[] a;
return hr;
}
HRESULT
ADSI::VisitChildren(
const String& containerPath,
const String& objectADSIClass,
ADSI::ObjectVisitor& visitor)
{
LOG_FUNCTION2(
ADSI::VisitChildren,
String::format(
L"container: %1 class: %2",
containerPath.c_str(),
objectADSIClass.c_str()));
ASSERT(!containerPath.empty());
ASSERT(!objectADSIClass.empty());
HRESULT hr = S_OK;
// This interface must be released
IEnumVARIANT* enumerator = 0;
do
{
hr = CoInitialize(0);
BREAK_ON_FAILED_HRESULT(hr);
SmartInterface<IADsContainer> container(0);
hr = ADSI::GetContainer(containerPath, container);
// failure => not a valid container
BREAK_ON_FAILED_HRESULT(hr);
_variant_t filter;
PWSTR a = const_cast<wchar_t*>(objectADSIClass.c_str());
hr = ::ADsBuildVarArrayStr(&a, 1, &filter);
BREAK_ON_FAILED_HRESULT(hr);
hr = container->put_Filter(filter);
BREAK_ON_FAILED_HRESULT(hr);
hr = buildEnumerator(container, enumerator);
BREAK_ON_FAILED_HRESULT(hr);
hr = walkEnumeration(*enumerator, visitor);
BREAK_ON_FAILED_HRESULT(hr);
}
while (0);
if (enumerator)
{
freeEnumerator(*enumerator);
}
return hr;
}
HRESULT
ADSI::VisitMembers(
const SmartInterface<IADsGroup>& group,
ADSI::ObjectVisitor& visitor)
{
LOG_FUNCTION(ADSI::VisitMembers);
HRESULT hr = S_OK;
// these must be released when we're done
IADsMembers* members = 0;
IEnumVARIANT* enumerator = 0;
do
{
hr = group->Members(&members);
BREAK_ON_FAILED_HRESULT(hr);
hr = buildEnumerator(*members, enumerator);
BREAK_ON_FAILED_HRESULT(hr);
hr = walkEnumeration(*enumerator, visitor);
BREAK_ON_FAILED_HRESULT(hr);
}
while (0);
if (enumerator)
{
freeEnumerator(*enumerator);
}
if (members)
{
members->Release();
}
return hr;
}
String
ADSI::ExtractObjectName(const String& ADSIPath)
{
LOG_FUNCTION2(ADSI::ExtractObjectName, ADSIPath);
ASSERT(!ADSIPath.empty());
PathCracker cracker(ADSIPath);
return cracker.leaf();
}
String
ADSI::ComposeMachineContainerPath(const String& computerName)
{
return
PROVIDER_ROOT
+ computerName
+ COMPUTER_SUFFIX;
}
HRESULT
ADSI::RenameObject(
const String& containerADSIPath,
const String& objectADSIPath,
const String& newName)
{
LOG_FUNCTION2(
ADSI::RenameObject,
String::format(
L"container: %1 object: %2, new name: %3",
containerADSIPath.c_str(),
objectADSIPath.c_str(),
newName.c_str()));
ASSERT(!containerADSIPath.empty());
ASSERT(!objectADSIPath.empty());
ASSERT(!newName.empty());
HRESULT hr = S_OK;
do
{
SmartInterface<IADsContainer> container(0);
hr = ADSI::GetContainer(containerADSIPath, container);
BREAK_ON_FAILED_HRESULT(hr);
IDispatch* object = 0;
hr =
container->MoveHere(
AutoBstr(objectADSIPath),
AutoBstr(newName),
&object);
BREAK_ON_FAILED_HRESULT(hr);
ASSERT(object);
}
while (0);
return hr;
}
HRESULT
ADSI::DeleteObject(
const String& containerADSIPath,
const String& objectRelativeName,
const String& objectClass)
{
LOG_FUNCTION2(
ADSI::DeleteObject,
String::format(
L"container: %1 object: %2 class: %3",
containerADSIPath.c_str(),
objectRelativeName.c_str(),
objectClass.c_str()));
ASSERT(!containerADSIPath.empty());
ASSERT(!objectRelativeName.empty());
ASSERT(!objectClass.empty());
HRESULT hr = S_OK;
do
{
SmartInterface<IADsContainer> container(0);
hr = ADSI::GetContainer(containerADSIPath, container);
BREAK_ON_FAILED_HRESULT(hr);
hr =
container->Delete(
AutoBstr(objectClass),
AutoBstr(objectRelativeName));
BREAK_ON_FAILED_HRESULT(hr);
}
while (0);
return hr;
}
HRESULT
ADSI::IsComputerAccessible(const String& machine)
{
LOG_FUNCTION2(ADSI::IsComputerAccessible, machine);
ASSERT(!machine.empty());
String path = ADSI::ComposeMachineContainerPath(machine);
SmartInterface<IADsContainer> container(0);
return ADSI::GetContainer(path, container);
}
HRESULT
ADSI::VisitGroups(
const SmartInterface<IADsUser>& user,
ADSI::ObjectVisitor& visitor)
{
LOG_FUNCTION(ADSI::VisitGroups);
HRESULT hr = S_OK;
// these must be released when we're done
IADsMembers* members = 0;
IEnumVARIANT* enumerator = 0;
do
{
hr = user->Groups(&members);
BREAK_ON_FAILED_HRESULT(hr);
hr = buildEnumerator(*members, enumerator);
BREAK_ON_FAILED_HRESULT(hr);
hr = walkEnumeration(*enumerator, visitor);
BREAK_ON_FAILED_HRESULT(hr);
}
while (0);
if (enumerator)
{
freeEnumerator(*enumerator);
}
if (members)
{
members->Release();
}
return hr;
}
// e.g. DOMAIN\account
String
ADSI::ExtractDomainObjectName(const String& ADSIPath)
{
LOG_FUNCTION2(ADSI::ExtractDomainObjectName, ADSIPath);
ASSERT(!ADSIPath.empty());
String result(ADSIPath);
size_t pos = ADSIPath.find_last_of(ADSI::PATH_SEP);
if (pos != String::npos)
{
// the last '/' marks the user name, just prior to that is the machine
// name (or the domain name), so find the 2nd to last '/'
pos = ADSIPath.find_last_of(ADSI::PATH_SEP, pos - 1);
if (pos != String::npos)
{
String s = ADSIPath.substr(pos + 1);
result = s.replace(ADSI::PATH_SEP, L"\\");
}
}
else
{
// there's something weird about the path
ASSERT(false);
}
LOG(result);
return result;
}
void
ADSI::FreeSid(SID* sid)
{
ASSERT(sid);
delete[] reinterpret_cast<BYTE*>(sid);
}
// allocates memory (with ::operator new) to hold a copy of the SID inside the
// variant. returns a pointer to that memory as a SID*, which the caller
// should free with ADSI::FreeSid.
HRESULT
VariantToSid(VARIANT* var, SID*& result)
{
LOG_FUNCTION(VariantToSid);
ASSERT(var);
ASSERT(V_VT(var) == (VT_ARRAY | VT_UI1));
result = 0;
HRESULT hr = S_OK;
SAFEARRAY* psa = V_ARRAY(var);
bool safeArrayAccessed = false;
do
{
ASSERT(psa);
ASSERT(psa != (SAFEARRAY*)-1);
if (!psa or psa == (SAFEARRAY*)-1)
{
LOG(L"variant not safe array");
hr = E_INVALIDARG;
break;
}
if (::SafeArrayGetDim(psa) != 1)
{
LOG(L"safe array: wrong number of dimensions");
hr = E_INVALIDARG;
break;
}
if (::SafeArrayGetElemsize(psa) != 1)
{
LOG(L"safe array: wrong element type");
hr = E_INVALIDARG;
break;
}
PSID sid = 0;
hr = ::SafeArrayAccessData(psa, reinterpret_cast<void**>(&sid));
BREAK_ON_FAILED_HRESULT(hr);
safeArrayAccessed = true;
if (!::IsValidSid(sid))
{
LOG(L"sid not valid");
hr = E_INVALIDARG;
break;
}
// caller frees the result with ADSI::FreeSid
DWORD bufSize = ::GetLengthSid(sid);
BYTE* buffer = new BYTE[bufSize];
::ZeroMemory(buffer, bufSize);
result = reinterpret_cast<SID*>(buffer);
hr = Win::CopySid(bufSize, result, sid);
BREAK_ON_FAILED_HRESULT(hr);
}
while (0);
if (safeArrayAccessed)
{
HRESULT unused = ::SafeArrayUnaccessData(psa);
ASSERT(SUCCEEDED(unused));
}
if (FAILED(hr))
{
ADSI::FreeSid(result);
result = 0;
}
LOG_HRESULT(hr);
return hr;
}
// Converts a VARIANT containing a safe array of bytes to a WinNT SID-style
// path (WinNT://S-x-x...)
HRESULT
ADSI::VariantToSidPath(VARIANT* var, String& result)
{
LOG_FUNCTION(ADSI::VariantToSidPath);
ASSERT(var);
ASSERT(V_VT(var) == (VT_ARRAY | VT_UI1));
result.erase();
HRESULT hr = S_OK;
SID* sid = 0;
do
{
hr = VariantToSid(var, sid);
BREAK_ON_FAILED_HRESULT(hr);
String sidString;
hr = Win::ConvertSidToStringSid(sid, sidString);
BREAK_ON_FAILED_HRESULT(hr);
result = ADSI::PROVIDER_ROOT + sidString;
}
while (0);
ADSI::FreeSid(sid);
LOG_HRESULT(hr);
LOG(result);
return hr;
}
HRESULT
ADSI::GetSid(const String& adsiPath, SID*& result)
{
LOG_FUNCTION2(ADSI::GetSid, adsiPath);
ASSERT(!adsiPath.empty());
ASSERT(IsWinNTPath(adsiPath));
HRESULT hr = S_OK;
result = 0;
do
{
SmartInterface<IADs> iads(0);
IADs* p = 0;
hr =
::ADsGetObject(
adsiPath.c_str(),
QI_PARAMS(IADs, &p));
BREAK_ON_FAILED_HRESULT(hr);
iads.Acquire(p);
hr = ADSI::GetSid(iads, result);
BREAK_ON_FAILED_HRESULT(hr);
}
while (0);
return hr;
}
HRESULT
ADSI::GetSid(const SmartInterface<IADs>& iads, SID*& result)
{
LOG_FUNCTION(ADSI::GetSid);
ASSERT(iads);
result = 0;
HRESULT hr = S_OK;
do
{
// get the account SID
_variant_t variant;
hr = iads->Get(AutoBstr(ADSI::PROPERTY_ObjectSID), &variant);
BREAK_ON_FAILED_HRESULT(hr);
hr = VariantToSid(&variant, result);
BREAK_ON_FAILED_HRESULT(hr);
}
while (0);
return hr;
}
HRESULT
ADSI::GetSidPath(const SmartInterface<IADs>& iads, String& result)
{
LOG_FUNCTION(ADSI::GetSidPath);
ASSERT(iads);
result.erase();
HRESULT hr = S_OK;
do
{
// get the account SID
_variant_t variant;
hr = iads->Get(AutoBstr(ADSI::PROPERTY_ObjectSID), &variant);
BREAK_ON_FAILED_HRESULT(hr);
hr = ADSI::VariantToSidPath(&variant, result);
BREAK_ON_FAILED_HRESULT(hr);
}
while (0);
return hr;
}
HRESULT
ADSI::GetSidPath(const String& adsiPath, String& result)
{
LOG_FUNCTION2(ADSI::GetSidPath, adsiPath);
ASSERT(!adsiPath.empty());
ASSERT(IsWinNTPath(adsiPath));
result.erase();
HRESULT hr = S_OK;
do
{
SmartInterface<IADs> iads(0);
IADs* p = 0;
hr =
::ADsGetObject(
adsiPath.c_str(),
QI_PARAMS(IADs, &p));
BREAK_ON_FAILED_HRESULT(hr);
iads.Acquire(p);
hr = ADSI::GetSidPath(iads, result);
BREAK_ON_FAILED_HRESULT(hr);
}
while (0);
return hr;
}
bool
ADSI::IsWinNTPath(const String& path)
{
LOG_FUNCTION2(ADSI::IsWinNTPath, path);
bool result = false;
if (path.find(ADSI::PROVIDER_ROOT) == 0)
{
result = true;
}
LOG(result ? L"true" : L"false");
return result;
}