windows-nt/Source/XPSP1/NT/ds/adsi/winnt/cuser.cxx
2020-09-26 16:20:57 +08:00

2013 lines
44 KiB
C++

//---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995
//
// File: cuser.cxx
//
// Contents: Host user object code
//
// History: 11-1-95 krishnag Created.
//
//----------------------------------------------------------------------------
#include "winnt.hxx"
#pragma hdrstop
// Class CWinNTUser
DEFINE_IDispatch_ExtMgr_Implementation(CWinNTUser)
DEFINE_IADsExtension_ExtMgr_Implementation(CWinNTUser)
DEFINE_IADs_TempImplementation(CWinNTUser)
DEFINE_IADs_PutGetImplementation(CWinNTUser,UserClass,gdwUserTableSize)
DEFINE_IADsPropertyList_Implementation(CWinNTUser,UserClass,gdwUserTableSize)
CWinNTUser::CWinNTUser():
_pDispMgr(NULL),
_pExtMgr(NULL),
_pPropertyCache(NULL),
_ParentType(0),
_DomainName(NULL),
_ServerName(NULL),
_fPasswordSet(FALSE),
_dwRasPermissions(0),
_pCCredentialsPwdHolder(NULL),
_fUseCacheForAcctLocked(TRUE),
_fComputerAcct(FALSE)
{
ENLIST_TRACKING(CWinNTUser);
}
HRESULT
CWinNTUser::CreateUser(
BSTR Parent,
ULONG ParentType,
BSTR DomainName,
BSTR ServerName,
BSTR UserName,
DWORD dwObjectState,
DWORD *pdwUserFlags, // OPTIONAL
LPWSTR szFullName, // OPTIONAL
LPWSTR szDescription, // OPTIONAL
PSID pSid, // OPTIONAL
REFIID riid,
CWinNTCredentials& Credentials,
void **ppvObj
)
{
CWinNTUser FAR * pUser = NULL;
HRESULT hr = S_OK;
hr = AllocateUserObject(&pUser);
BAIL_ON_FAILURE(hr);
ADsAssert(pUser->_pDispMgr);
hr = pUser->InitializeCoreObject(
Parent,
UserName,
USER_CLASS_NAME,
USER_SCHEMA_NAME,
CLSID_WinNTUser,
dwObjectState
);
BAIL_ON_FAILURE(hr);
pUser->_Credentials = Credentials;
//
// The server name will be NULL only when we create a user
// by SID - WinNT://S-1-321-231-231. In this case we should
// not ref the server. Parent type is used as an extra check.
//
if (!((ParentType == WINNT_COMPUTER_ID)
&& !ServerName)) {
hr = pUser->_Credentials.Ref(ServerName, DomainName, ParentType);
if (hr == HRESULT_FROM_WIN32(ERROR_BAD_ARGUMENTS)) {
//
// We had a rebind error.
// This will happen in the case where the credentials
// ref the current server which is a bdc, the users is
// a member of a global group we are going through and
// we end up trying to ref the PDC when we already have
// a connection to this comp.
hr = S_OK;
}
}
BAIL_ON_FAILURE(hr);
hr = ADsAllocString(DomainName, &pUser->_DomainName);
BAIL_ON_FAILURE(hr);
if (ParentType == WINNT_DOMAIN_ID)
{
pUser->_ParentType = WINNT_DOMAIN_ID;
ADsAssert(DomainName && DomainName[0]!=L'\0');
}
else
{
pUser->_ParentType = WINNT_COMPUTER_ID;
hr = ADsAllocString(ServerName, &pUser->_ServerName);
BAIL_ON_FAILURE(hr);
}
//
// Load ext mgr and extensions
//
hr = ADSILoadExtensionManager(
USER_CLASS_NAME,
(IADsUser *) pUser,
pUser->_pDispMgr,
Credentials,
&pUser->_pExtMgr
);
BAIL_ON_FAILURE(hr);
ADsAssert(pUser->_pExtMgr);
//
// Prepopulate the object
//
hr = pUser->Prepopulate(TRUE,
pdwUserFlags,
szFullName,
szDescription,
pSid);
BAIL_ON_FAILURE(hr);
// check if the call is from UMI
if(Credentials.GetFlags() & ADS_AUTH_RESERVED) {
//
// we do not pass riid to InitUmiObject below. This is because UMI object
// does not support IDispatch. There are several places in ADSI code where
// riid passed into this function is defaulted to IID_IDispatch -
// IADsContainer::Create for example. To handle these cases, we always
// request IID_IUnknown from the UMI object. Subsequent code within UMI
// will QI for the appropriate interface.
//
if(3 == pUser->_dwNumComponents) {
pUser->_CompClasses[0] = L"Domain";
pUser->_CompClasses[1] = L"Computer";
pUser->_CompClasses[2] = L"User";
}
else if(2 == pUser->_dwNumComponents) {
if(NULL == DomainName) {
pUser->_CompClasses[0] = L"Computer";
pUser->_CompClasses[1] = L"User";
}
else if(NULL == ServerName) {
pUser->_CompClasses[0] = L"Domain";
pUser->_CompClasses[1] = L"User";
}
else
BAIL_ON_FAILURE(hr = UMI_E_FAIL);
}
else
BAIL_ON_FAILURE(hr = UMI_E_FAIL);
hr = pUser->InitUmiObject(
pUser->_Credentials,
UserClass,
gdwUserTableSize,
pUser->_pPropertyCache,
(IUnknown *) (INonDelegatingUnknown *) pUser,
pUser->_pExtMgr,
IID_IUnknown,
ppvObj
);
BAIL_ON_FAILURE(hr);
//
// UMI object was created and the interface was obtained successfully.
// UMI object now has a reference to the inner unknown of IADs, since
// the call to Release() below is not going to be made in this case.
//
RRETURN(hr);
}
hr = pUser->QueryInterface(riid, ppvObj);
BAIL_ON_FAILURE(hr);
pUser->Release();
RRETURN(hr);
error:
delete pUser;
RRETURN_EXP_IF_ERR(hr);
}
HRESULT
CWinNTUser::CreateUser(
BSTR Parent,
ULONG ParentType,
BSTR DomainName,
BSTR ServerName,
BSTR UserName,
DWORD dwObjectState,
REFIID riid,
CWinNTCredentials& Credentials,
void **ppvObj
)
{
HRESULT hr = S_OK;
hr = CWinNTUser::CreateUser(
Parent,
ParentType,
DomainName,
ServerName,
UserName,
dwObjectState,
NULL,
NULL,
NULL,
NULL,
riid,
Credentials,
ppvObj
);
RRETURN_EXP_IF_ERR(hr);
}
CWinNTUser::~CWinNTUser( )
{
ADsFreeString(_DomainName);
ADsFreeString(_ServerName);
delete _pExtMgr; // created last, destroyed first
delete _pDispMgr;
delete _pPropertyCache;
if (_pCCredentialsPwdHolder) {
delete _pCCredentialsPwdHolder;
}
}
//----------------------------------------------------------------------------
// Function: QueryInterface
//
// Synopsis: If this object is aggregated within another object, then
// all calls will delegate to the outer object. Otherwise, the
// non-delegating QI is called
//
// Arguments:
//
// iid interface requested
// ppInterface Returns pointer to interface requested. NULL if interface
// is not supported.
//
// Returns: S_OK on success. Error code otherwise.
//
// Modifies: *ppInterface to return interface pointer
//
//----------------------------------------------------------------------------
STDMETHODIMP CWinNTUser::QueryInterface(
REFIID iid,
LPVOID *ppInterface
)
{
if(_pUnkOuter != NULL)
RRETURN(_pUnkOuter->QueryInterface(
iid,
ppInterface
));
RRETURN(NonDelegatingQueryInterface(
iid,
ppInterface
));
}
//----------------------------------------------------------------------------
// Function: AddRef
//
// Synopsis: IUnknown::AddRef. If this object is aggregated within
// another, all calls will delegate to the outer object.
// Otherwise, the non-delegating AddRef is called
//
// Arguments:
//
// None
//
// Returns: New reference count
//
// Modifies: Nothing
//
//----------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CWinNTUser::AddRef(void)
{
if(_pUnkOuter != NULL)
RRETURN(_pUnkOuter->AddRef());
RRETURN(NonDelegatingAddRef());
}
//----------------------------------------------------------------------------
// Function: Release
//
// Synopsis: IUnknown::Release. If this object is aggregated within
// another, all calls will delegate to the outer object.
// Otherwise, the non-delegating Release is called
//
// Arguments:
//
// None
//
// Returns: New reference count
//
// Modifies: Nothing
//
//----------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CWinNTUser::Release(void)
{
if(_pUnkOuter != NULL)
RRETURN(_pUnkOuter->Release());
RRETURN(NonDelegatingRelease());
}
//----------------------------------------------------------------------------
STDMETHODIMP
CWinNTUser::NonDelegatingQueryInterface(
REFIID iid,
LPVOID FAR* ppv
)
{
HRESULT hr = S_OK;
if (ppv == NULL) {
RRETURN(E_POINTER);
}
if (IsEqualIID(iid, IID_IUnknown))
{
*ppv = (IADsUser FAR *) this;
}
else if (IsEqualIID(iid, IID_IADsUser))
{
*ppv = (IADsUser FAR *) this;
}
else if (IsEqualIID(iid, IID_IADs))
{
*ppv = (IADsUser FAR *) this;
}
else if (IsEqualIID(iid, IID_IADsPropertyList))
{
*ppv = (IADsPropertyList FAR *) this;
}
else if (IsEqualIID(iid, IID_IDispatch))
{
*ppv = (IADsUser FAR *) this;
}
else if (IsEqualIID(iid, IID_ISupportErrorInfo))
{
*ppv = (ISupportErrorInfo FAR *) this;
}
else if( (_pDispatch != NULL) &&
IsEqualIID(iid, IID_IADsExtension) )
{
*ppv = (IADsExtension *) this;
}
else if (_pExtMgr)
{
RRETURN(_pExtMgr->QueryInterface(iid,ppv));
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
AddRef();
return NOERROR;
}
/* ISupportErrorInfo method */
STDMETHODIMP
CWinNTUser::InterfaceSupportsErrorInfo(
THIS_ REFIID riid
)
{
if (IsEqualIID(riid, IID_IADs) ||
IsEqualIID(riid, IID_IADsPropertyList) ||
IsEqualIID(riid, IID_IADsUser)) {
RRETURN(S_OK);
} else {
RRETURN(S_FALSE);
}
}
/* IADs methods */
STDMETHODIMP
CWinNTUser::SetInfo(THIS)
{
HRESULT hr = S_OK;
NET_API_STATUS nasStatus;
WCHAR szPDCName[MAX_PATH];
WCHAR *pszPassword = NULL;
WCHAR *pszServerName = _ServerName;
//
// objects associated with invalid SIDs have neither a
// corresponding server nor domain
//
if ((!_DomainName) && (!_ServerName)) {
BAIL_ON_FAILURE(hr = E_ADS_INVALID_USER_OBJECT);
}
if (GetObjectState() == ADS_OBJECT_UNBOUND) {
if (_ParentType == WINNT_DOMAIN_ID) {
hr = WinNTGetCachedDCName(
_DomainName,
szPDCName,
_Credentials.GetFlags()
);
BAIL_ON_FAILURE(hr);
//
// + 2 skips the backslashes when calling create
//
pszServerName = szPDCName + 2;
}
if (!_fPasswordSet) {
hr = WinNTCreateUser(
pszServerName,
_Name
);
} else {
hr = getPrivatePassword(&pszPassword);
BAIL_ON_FAILURE(hr);
hr = WinNTCreateUser(
pszServerName,
_Name,
pszPassword
);
}
BAIL_ON_FAILURE(hr);
SetObjectState(ADS_OBJECT_BOUND);
}
hr = SetInfo(3);
if(SUCCEEDED(hr))
_pPropertyCache->ClearModifiedFlags();
error:
if (pszPassword) {
FreeADsStr(pszPassword);
}
RRETURN_EXP_IF_ERR(hr);
}
STDMETHODIMP
CWinNTUser::GetInfo(THIS)
{
HRESULT hr;
_pPropertyCache->flushpropcache();
// USER_INFO
//
hr = GetInfo(
3,
TRUE
);
BAIL_ON_FAILURE(hr);
//
// USER_MODAL_INFO0
//
hr = GetInfo(
10,
TRUE
);
BAIL_ON_FAILURE(hr);
//
// USER_MODAL_INFO3
//
hr = GetInfo(
13,
TRUE
);
BAIL_ON_FAILURE(hr);
if(FALSE == _fComputerAcct) {
#ifndef WIN95
//
// Get the RAS permissions. Do this only for user accounts and
// not for machine accounts.
//
//
hr = GetInfo(21, TRUE);
BAIL_ON_FAILURE(hr);
#endif
}
//
// objectSid. LookupAccountName fails for machine accounts on NT4, but
// works on Win2K. In order for an explicit GetInfo to succeed against NT4
// systems we do not check the error code returned below. If this call
// fails, a subsequent Get("ObjectSid") will return
// E_ADS_PROPERTY_NOT_FOUND.
//
GetInfo(
20,
TRUE
);
error :
RRETURN(hr);
}
STDMETHODIMP
CWinNTUser::ImplicitGetInfo(THIS)
{
HRESULT hr;
#ifndef WIN95
//
// Get the RAS permissions first
//
//
hr = GetInfo(21, FALSE);
BAIL_ON_FAILURE(hr);
#endif
// USER_INFO
//
hr = GetInfo(
3,
FALSE
);
BAIL_ON_FAILURE(hr);
//
// USER_MODAL_INFO0
//
hr = GetInfo(
10,
FALSE
);
BAIL_ON_FAILURE(hr);
//
// USER_MODAL_INFO3
//
hr = GetInfo(
13,
FALSE
);
BAIL_ON_FAILURE(hr);
//
// objectSid
//
hr = GetInfo(
20,
FALSE
);
error :
RRETURN(hr);
}
HRESULT
CWinNTUser::AllocateUserObject(
CWinNTUser ** ppUser
)
{
CWinNTUser FAR * pUser = NULL;
CAggregatorDispMgr FAR * pDispMgr = NULL;
CPropertyCache FAR * pPropertyCache = NULL;
HRESULT hr = S_OK;
pUser = new CWinNTUser();
if (pUser == NULL) {
hr = E_OUTOFMEMORY;
}
BAIL_ON_FAILURE(hr);
pDispMgr = new CAggregatorDispMgr;
if (pDispMgr == NULL) {
hr = E_OUTOFMEMORY;
}
BAIL_ON_FAILURE(hr);
hr = LoadTypeInfoEntry(
pDispMgr,
LIBID_ADs,
IID_IADsUser,
(IADsUser *)pUser,
DISPID_REGULAR
);
BAIL_ON_FAILURE(hr);
hr = LoadTypeInfoEntry(
pDispMgr,
LIBID_ADs,
IID_IADsPropertyList,
(IADsPropertyList *)pUser,
DISPID_VALUE
);
BAIL_ON_FAILURE(hr);
hr = CPropertyCache::createpropertycache(
UserClass,
gdwUserTableSize,
(CCoreADsObject *)pUser,
&pPropertyCache
);
BAIL_ON_FAILURE(hr);
pDispMgr->RegisterPropertyCache(
pPropertyCache
);
pUser->_pPropertyCache = pPropertyCache;
pUser->_pDispMgr = pDispMgr;
*ppUser = pUser;
RRETURN(hr);
error:
delete pDispMgr;
delete pPropertyCache;
delete pUser;
RRETURN(hr);
}
//
// For current implementation in clocgroup:
// If this function is called as a public function (ie. by another
// modual/class), fExplicit must be FALSE since the cache is NOT
// flushed in this function.
//
// External functions should ONLY call GetInfo(no param) for explicit
// GetInfo. This will flush the cache properly.
//
STDMETHODIMP
CWinNTUser::GetInfo(
THIS_ DWORD dwApiLevel,
BOOL fExplicit
)
{
HRESULT hr = S_OK;
switch (dwApiLevel) {
// GetInfo(1 or 2, fExplicit) in ADSI codes should be modified
// to GetInfo(3, fExplicit) to minimize calls on wire.
case 3:
hr = GetStandardInfo(3, fExplicit);
RRETURN_EXP_IF_ERR(hr);
case 10:
hr = GetModalInfo(0, fExplicit);
RRETURN_EXP_IF_ERR(hr);
case 13:
hr = GetModalInfo(3, fExplicit);
RRETURN_EXP_IF_ERR(hr);
case 20:
hr = GetSidInfo(fExplicit);
RRETURN_EXP_IF_ERR(hr);
#ifndef WIN95
case 21:
hr = GetRasInfo(fExplicit);
RRETURN_EXP_IF_ERR(hr);
#endif
default:
RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER);
}
}
HRESULT
CWinNTUser::GetStandardInfo(
THIS_ DWORD dwApiLevel,
BOOL fExplicit
)
{
NET_API_STATUS nasStatus;
LPBYTE lpBuffer = NULL;
HRESULT hr;
WCHAR szHostServerName[MAX_PATH];
if (GetObjectState() == ADS_OBJECT_UNBOUND) {
RRETURN_EXP_IF_ERR(E_ADS_OBJECT_UNBOUND);
}
//
// objects associated with invalid SIDs have neither a
// corresponding server nor domain
//
if ((!_DomainName) && (!_ServerName)) {
BAIL_ON_FAILURE(hr = E_ADS_INVALID_USER_OBJECT);
}
if (_ParentType == WINNT_DOMAIN_ID) {
hr = WinNTGetCachedDCName(
_DomainName,
szHostServerName,
_Credentials.GetFlags()
);
BAIL_ON_FAILURE(hr);
}else {
hr = MakeUncName(
_ServerName,
szHostServerName
);
}
nasStatus = NetUserGetInfo(
szHostServerName,
_Name,
dwApiLevel,
&lpBuffer
);
hr = HRESULT_FROM_WIN32(nasStatus);
BAIL_ON_FAILURE(hr);
hr = UnMarshall(
lpBuffer,
dwApiLevel,
fExplicit
);
BAIL_ON_FAILURE(hr);
error:
if (lpBuffer) {
NetApiBufferFree(lpBuffer);
}
RRETURN_EXP_IF_ERR(hr);
}
HRESULT
CWinNTUser::UnMarshall(
LPBYTE lpBuffer,
DWORD dwApiLevel,
BOOL fExplicit
)
{
VARIANT_BOOL fBool;
BSTR bstrData = NULL;
LONG lnData = 0L;
VARIANT vaData;
DATE daDate = 0;
ADsAssert(lpBuffer);
switch (dwApiLevel) {
// GetStandardInfo currently only be called with dwApiLevel=3. If
// dwApiLevel = 1 or 2 is used, modify ADSI codes to 3.
case 3:
RRETURN(UnMarshall_Level3(fExplicit, (LPUSER_INFO_3)lpBuffer));
break;
default:
RRETURN(E_FAIL);
}
}
HRESULT
CWinNTUser::UnMarshall_Level3(
BOOL fExplicit,
LPUSER_INFO_3 pUserInfo3
)
{
HRESULT hr = S_OK;
//
// Begin Account Restrictions Properties
//
hr = SetDWORDPropertyInCache(
_pPropertyCache,
TEXT("UserFlags"),
pUserInfo3->usri3_flags,
fExplicit
);
if(SUCCEEDED(hr)) {
_fUseCacheForAcctLocked = TRUE;
}
if( (pUserInfo3->usri3_flags & UF_WORKSTATION_TRUST_ACCOUNT) ||
(pUserInfo3->usri3_flags & UF_SERVER_TRUST_ACCOUNT) ||
(pUserInfo3->usri3_flags & UF_INTERDOMAIN_TRUST_ACCOUNT) ) {
_fComputerAcct = TRUE;
}
//
// If usri3_acct_expires == TIMEQ_FOREVER, it means we need
// to ignore the acct expiration date, the account
// can never expire.
//
if (pUserInfo3->usri3_acct_expires != TIMEQ_FOREVER) {
hr = SetDATE70PropertyInCache(
_pPropertyCache,
TEXT("AccountExpirationDate"),
pUserInfo3->usri3_acct_expires,
fExplicit
);
}
hr = SetDelimitedStringPropertyInCache(
_pPropertyCache,
TEXT("LoginWorkstations"),
pUserInfo3->usri3_workstations,
fExplicit
);
hr = SetDWORDPropertyInCache(
_pPropertyCache,
TEXT("MaxStorage"),
pUserInfo3->usri3_max_storage,
fExplicit
);
hr = SetDWORDPropertyInCache(
_pPropertyCache,
TEXT("PasswordAge"),
pUserInfo3->usri3_password_age,
fExplicit
);
hr = SetDWORDPropertyInCache(
_pPropertyCache,
TEXT("PasswordExpired"),
pUserInfo3->usri3_password_expired,
fExplicit
);
hr = SetOctetPropertyInCache(
_pPropertyCache,
TEXT("LoginHours"),
pUserInfo3->usri3_logon_hours,
21,
fExplicit
);
//
// Begin Business Info Properties
//
hr = SetLPTSTRPropertyInCache(
_pPropertyCache,
TEXT("FullName"),
pUserInfo3->usri3_full_name,
fExplicit
);
hr = SetLPTSTRPropertyInCache(
_pPropertyCache,
TEXT("Description"),
pUserInfo3->usri3_comment,
fExplicit
);
//
// Begin Account Statistics Properties
//
hr = SetDWORDPropertyInCache(
_pPropertyCache,
TEXT("BadPasswordAttempts"),
pUserInfo3->usri3_bad_pw_count,
fExplicit
);
//
// lasg_logon/off == 0 means user never logon/off or logon/off time unknown.
//
if (pUserInfo3->usri3_last_logon!=0) {
hr = SetDATE70PropertyInCache(
_pPropertyCache,
TEXT("LastLogin"),
pUserInfo3->usri3_last_logon,
fExplicit
);
}
if (pUserInfo3->usri3_last_logoff!=0) {
hr = SetDATE70PropertyInCache(
_pPropertyCache,
TEXT("LastLogoff"),
pUserInfo3->usri3_last_logoff,
fExplicit
);
}
//
// Begin Other Info Properties
//
hr = SetLPTSTRPropertyInCache(
_pPropertyCache,
TEXT("HomeDirectory"),
pUserInfo3->usri3_home_dir,
fExplicit
);
hr = SetLPTSTRPropertyInCache(
_pPropertyCache,
TEXT("LoginScript"),
pUserInfo3->usri3_script_path,
fExplicit
);
hr = SetLPTSTRPropertyInCache(
_pPropertyCache,
TEXT("Profile"),
pUserInfo3->usri3_profile,
fExplicit
);
hr = SetLPTSTRPropertyInCache(
_pPropertyCache,
TEXT("HomeDirDrive"),
pUserInfo3->usri3_home_dir_drive,
fExplicit
);
hr = SetLPTSTRPropertyInCache(
_pPropertyCache,
TEXT("Parameters"),
pUserInfo3->usri3_parms,
fExplicit
);
hr = SetDWORDPropertyInCache(
_pPropertyCache,
TEXT("PrimaryGroupID"),
pUserInfo3->usri3_primary_group_id,
fExplicit
);
hr = SetLPTSTRPropertyInCache(
_pPropertyCache,
TEXT("Name"),
_Name,
fExplicit
);
RRETURN(S_OK);
}
HRESULT
CWinNTUser::Prepopulate(
BOOL fExplicit,
DWORD *pdwUserFlags, // OPTIONAL
LPWSTR szFullName, // OPTIONAL
LPWSTR szDescription, // OPTIONAL
PSID pSid // OPTIONAL
)
{
HRESULT hr = S_OK;
DWORD dwErr = 0;
DWORD dwSidLength = 0;
//
// Prepopulate the object with supplied info,
// if available
//
if (pdwUserFlags) {
hr = SetDWORDPropertyInCache(
_pPropertyCache,
TEXT("UserFlags"),
*pdwUserFlags,
TRUE
);
BAIL_ON_FAILURE(hr);
//
// see comment on _fUseCacheForAcctLocked in cuser.hxx
//
_fUseCacheForAcctLocked = FALSE;
}
if (szFullName) {
hr = SetLPTSTRPropertyInCache(
_pPropertyCache,
TEXT("FullName"),
szFullName,
TRUE
);
BAIL_ON_FAILURE(hr);
}
if (szDescription) {
hr = SetLPTSTRPropertyInCache(
_pPropertyCache,
TEXT("Description"),
szDescription,
TRUE
);
BAIL_ON_FAILURE(hr);
}
if (pSid) {
//
// On NT4 for some reason GetLengthSID does not set lasterror to 0
//
SetLastError(NO_ERROR);
dwSidLength = GetLengthSid(pSid);
//
// This is an extra check to make sure that we have the
// correct length.
//
dwErr = GetLastError();
if (dwErr != NO_ERROR) {
hr = HRESULT_FROM_WIN32(dwErr);
BAIL_ON_FAILURE(hr);
}
hr = SetOctetPropertyInCache(
_pPropertyCache,
TEXT("objectSid"),
(PBYTE) pSid,
dwSidLength,
TRUE
);
BAIL_ON_FAILURE(hr);
}
error:
RRETURN(hr);
}
HRESULT
CWinNTUser::GetSidInfo(
IN BOOL fExplicit
)
{
HRESULT hr = E_FAIL;
WCHAR szHostServerName[MAX_PATH];
//
// objects associated with invalid SIDs have neither a
// corresponding server nor domain
//
if ((!_DomainName) && (!_ServerName)) {
BAIL_ON_FAILURE(hr = E_ADS_INVALID_USER_OBJECT);
}
//
// Get Server Name
//
if (_ParentType == WINNT_DOMAIN_ID) {
hr = WinNTGetCachedDCName(
_DomainName,
szHostServerName,
_Credentials.GetFlags()
);
BAIL_ON_FAILURE(hr);
}else {
hr = MakeUncName(
_ServerName,
szHostServerName
);
}
//
// Get Sid of this user account and store in cache if fExplicit.
//
hr = GetSidIntoCache(
szHostServerName,
_Name,
_pPropertyCache,
fExplicit
);
BAIL_ON_FAILURE(hr);
error:
RRETURN(hr);
}
HRESULT
CWinNTUser::GetRasInfo(
IN BOOL fExplicit
)
{
#ifdef WIN95
RRETURN(E_NOTIMPL);
#else
HRESULT hr = E_FAIL;
WCHAR szHostServerName[MAX_PATH];
RAS_USER_0 RasUser0;
DWORD nasStatus = 0;
if (GetObjectState() == ADS_OBJECT_UNBOUND) {
RRETURN_EXP_IF_ERR(E_ADS_OBJECT_UNBOUND);
}
//
// objects associated with invalid SIDs have neither a
// corresponding server nor domain
//
if ((!_DomainName) && (!_ServerName)) {
BAIL_ON_FAILURE(hr = E_ADS_INVALID_USER_OBJECT);
}
//
// Get Server Name
//
if (_ParentType == WINNT_DOMAIN_ID) {
hr = WinNTGetCachedDCName(
_DomainName,
szHostServerName,
_Credentials.GetFlags()
);
BAIL_ON_FAILURE(hr);
}else {
hr = MakeUncName(
_ServerName,
szHostServerName
);
}
//
// Make Ras call to get permissions.
//
nasStatus = RasAdminUserGetInfo(
szHostServerName,
_Name,
&RasUser0
);
if (nasStatus) {
BAIL_ON_FAILURE(hr = HRESULT_FROM_WIN32(nasStatus));
}
_dwRasPermissions = RasUser0.bfPrivilege;
hr = SetDWORDPropertyInCache(
_pPropertyCache,
TEXT("RasPermissions"),
RasUser0.bfPrivilege,
fExplicit
);
error:
RRETURN(hr);
#endif
}
HRESULT
CWinNTUser::SetInfo(THIS_ DWORD dwApiLevel)
{
NET_API_STATUS nasStatus;
HRESULT hr;
LPBYTE lpBuffer = NULL;
DWORD dwParamErr = 0;
WCHAR szHostServerName[MAX_PATH];
#ifndef WIN95
RAS_USER_0 RasUser0;
DWORD dwRasPerms = 0;
#endif
//
// objects associated with invalid SIDs have neither a
// corresponding server nor domain
//
if ((!_DomainName) && (!_ServerName)) {
BAIL_ON_FAILURE(hr = E_ADS_INVALID_USER_OBJECT);
}
if (_ParentType == WINNT_DOMAIN_ID) {
hr = WinNTGetCachedDCName(
_DomainName,
szHostServerName,
_Credentials.GetFlags()
);
BAIL_ON_FAILURE(hr);
}else {
hr = MakeUncName(
_ServerName,
szHostServerName
);
BAIL_ON_FAILURE(hr);
}
#ifndef WIN95
//
// Since Ras stuff is the new addition do this first.
// There is a chance that we may fail after creating the
// user but I do not see anyway to avoid this.
// Check to see if the value is set in the cace and only
// if it is set and is different from the value we have
// do we try and change it.
//
hr = GetDWORDPropertyFromCache(
_pPropertyCache,
TEXT("RasPermissions"),
&dwRasPerms
);
if (SUCCEEDED(hr) && (dwRasPerms != _dwRasPermissions)) {
//
// Get the permissions and then set privelege as we
// do not want to change the callback number param.
//
nasStatus = RasAdminUserGetInfo(
szHostServerName,
_Name,
&RasUser0
);
if (nasStatus) {
BAIL_ON_FAILURE(hr = HRESULT_FROM_WIN32(nasStatus));
}
RasUser0.bfPrivilege = (BYTE) dwRasPerms;
nasStatus = RasAdminUserSetInfo(
szHostServerName,
_Name,
&RasUser0
);
if (nasStatus) {
BAIL_ON_FAILURE(hr = HRESULT_FROM_WIN32(nasStatus));
}
} // Ras info.
#endif
nasStatus = NetUserGetInfo(
szHostServerName,
_Name,
dwApiLevel,
&lpBuffer
);
hr = HRESULT_FROM_WIN32(nasStatus);
BAIL_ON_FAILURE(hr);
hr = MarshallAndSet(szHostServerName, lpBuffer, dwApiLevel);
BAIL_ON_FAILURE(hr);
error:
if (lpBuffer) {
NetApiBufferFree(lpBuffer);
}
RRETURN_EXP_IF_ERR(hr);
}
HRESULT
CWinNTUser::MarshallAndSet(
LPWSTR szHostServerName,
LPBYTE lpBuffer,
DWORD dwApiLevel
)
{
ADsAssert(lpBuffer);
switch (dwApiLevel) {
//
// dwApiLevel = 1 or 2 should change to 3 in caller codes to min
// calls on wire
case 3:
RRETURN(Marshall_Set_Level3(szHostServerName, (LPUSER_INFO_3)lpBuffer));
break;
//
// caae 10:
// case 13:
// USER_MODAL_INFO should be set at domain level,
// Not at user level
//
//
// case 20:
// objectSid not writable
//
default:
RRETURN(E_FAIL);
}
}
HRESULT
CWinNTUser::Marshall_Set_Level3(
LPWSTR szHostServerName,
LPUSER_INFO_3 pUserInfo3
)
{
HRESULT hr;
DWORD dwFlags = 0;
DWORD dwAcctExpDate = 0;
LPWSTR pszDescription = NULL;
LPWSTR pszFullName = NULL;
DWORD dwBadPwCount = 0;
DWORD dwLastLogin = 0;
DWORD dwLastLogoff = 0;
LPWSTR pszHomeDir = NULL;
LPWSTR pszScript = NULL;
LPWSTR pszProfile = NULL;
LPWSTR pszLoginWorkstations = NULL;
DWORD dwMaxStorage = 0;
LPWSTR pszHomeDirDrive = NULL;
LPWSTR pszParameters = NULL;
DWORD dwPrimaryGroupId = 0;
DWORD dwPasswordExpired = 0;
OctetString octetString;
DWORD dwParmErr = 0;
NET_API_STATUS nasStatus;
hr = GetDWORDPropertyFromCache(
_pPropertyCache,
TEXT("UserFlags"),
&dwFlags
);
if(SUCCEEDED(hr)){
pUserInfo3->usri3_flags = dwFlags;
}
hr = GetDATE70PropertyFromCache(
_pPropertyCache,
TEXT("AccountExpirationDate"),
&dwAcctExpDate
);
if(SUCCEEDED(hr)){
//
// Pick an easy to remeber date to represent "account never expires" :
// 1/1/70 at 0:00. (Range <= 86400 and >= 0xffffffff-86400 is +/- one
// day from 1/1/70 at 0:00 to take time localization into account.)
//
if (dwAcctExpDate <= 86400 || dwAcctExpDate >= (0xffffffff-86400)) {
pUserInfo3->usri3_acct_expires = TIMEQ_FOREVER;
}
else {
pUserInfo3->usri3_acct_expires = dwAcctExpDate;
}
}
hr = GetDWORDPropertyFromCache(
_pPropertyCache,
TEXT("PasswordExpired"),
&dwPasswordExpired
);
if(SUCCEEDED(hr)){
pUserInfo3->usri3_password_expired = dwPasswordExpired;
}
hr = GetDWORDPropertyFromCache(
_pPropertyCache,
TEXT("MaxStorage"),
&dwMaxStorage
);
if(SUCCEEDED(hr)){
pUserInfo3->usri3_max_storage = dwMaxStorage;
}
hr = GetDelimitedStringPropertyFromCache(
_pPropertyCache,
TEXT("LoginWorkstations"),
&pszLoginWorkstations
);
if(SUCCEEDED(hr)){
pUserInfo3->usri3_workstations = pszLoginWorkstations;
}
//
// Begin Business Information Properties
//
hr = GetLPTSTRPropertyFromCache(
_pPropertyCache,
TEXT("Description"),
&pszDescription
);
if(SUCCEEDED(hr)){
pUserInfo3->usri3_comment = pszDescription;
}
hr = GetLPTSTRPropertyFromCache(
_pPropertyCache,
TEXT("FullName"),
&pszFullName
);
if(SUCCEEDED(hr)){
pUserInfo3->usri3_full_name = pszFullName;
}
hr = GetOctetPropertyFromCache(
_pPropertyCache,
TEXT("LoginHours"),
&octetString
);
if(SUCCEEDED(hr)){
memcpy(pUserInfo3->usri3_logon_hours,
octetString.pByte,
octetString.dwSize);
FreeADsMem(octetString.pByte);
}
/*
//
// Begin Account Statistics Properties - should not be writable.
//
hr = GetDWORDPropertyFromCache(
_pPropertyCache,
TEXT("BadPasswordAttempts"),
&dwBadPwCount
);
if(SUCCEEDED(hr)){
pUserInfo3->usri3_bad_pw_count = dwBadPwCount;
}
hr = GetDATE70PropertyFromCache(
_pPropertyCache,
TEXT("LastLogin"),
&dwLastLogin
);
if(SUCCEEDED(hr)){
pUserInfo3->usri3_last_logon = dwLastLogin;
}
hr = GetDATE70PropertyFromCache(
_pPropertyCache,
TEXT("LastLogoff"),
&dwLastLogoff
);
if(SUCCEEDED(hr)){
pUserInfo3->usri3_last_logoff = dwLastLogoff;
}
*/
//
// Begin Other Info Properties
//
hr = GetLPTSTRPropertyFromCache(
_pPropertyCache,
TEXT("HomeDirectory"),
&pszHomeDir
);
if(SUCCEEDED(hr)){
pUserInfo3->usri3_home_dir = pszHomeDir;
}
hr = GetLPTSTRPropertyFromCache(
_pPropertyCache,
TEXT("LoginScript"),
&pszScript
);
if(SUCCEEDED(hr)){
pUserInfo3->usri3_script_path = pszScript;
}
hr = GetLPTSTRPropertyFromCache(
_pPropertyCache,
TEXT("Profile"),
&pszProfile
);
if(SUCCEEDED(hr)){
pUserInfo3->usri3_profile = pszProfile;
}
hr = GetLPTSTRPropertyFromCache(
_pPropertyCache,
TEXT("HomeDirDrive"),
&pszHomeDirDrive
);
if(SUCCEEDED(hr)){
pUserInfo3->usri3_home_dir_drive = pszHomeDirDrive;
}
hr = GetLPTSTRPropertyFromCache(
_pPropertyCache,
TEXT("Parameters"),
&pszParameters
);
if(SUCCEEDED(hr)){
pUserInfo3->usri3_parms = pszParameters;
}
hr = GetDWORDPropertyFromCache(
_pPropertyCache,
TEXT("PrimaryGroupID"),
&dwPrimaryGroupId
);
if(SUCCEEDED(hr)){
pUserInfo3->usri3_primary_group_id = dwPrimaryGroupId;
}
//
// Now perform the Set call.
//
nasStatus = NetUserSetInfo(
szHostServerName,
_Name,
3,
(LPBYTE)pUserInfo3,
&dwParmErr
);
hr = HRESULT_FROM_WIN32(nasStatus);
BAIL_ON_FAILURE(hr);
error:
if (pszDescription) {
FreeADsStr(pszDescription);
}
if (pszFullName) {
FreeADsStr(pszFullName);
}
if (pszHomeDir) {
FreeADsStr(pszHomeDir);
}
if (pszScript) {
FreeADsStr(pszScript);
}
if (pszProfile) {
FreeADsStr(pszProfile);
}
if (pszLoginWorkstations) {
FreeADsStr(pszLoginWorkstations);
}
if (pszParameters) {
FreeADsStr(pszParameters);
}
if (pszHomeDirDrive) {
FreeADsStr(pszHomeDirDrive);
}
RRETURN(hr);
}
HRESULT
CWinNTUser::Marshall_Create_Level1(
LPWSTR szHostServerName,
LPUSER_INFO_1 pUserInfo1
)
{
HRESULT hr = S_OK;
NET_API_STATUS nasStatus;
DWORD dwParmErr;
pUserInfo1->usri1_name = _Name;
pUserInfo1->usri1_password = NULL;
pUserInfo1->usri1_password_age = DEF_MAX_PWAGE;
pUserInfo1->usri1_priv = 1;
pUserInfo1->usri1_home_dir = NULL;
pUserInfo1->usri1_comment = NULL;
pUserInfo1->usri1_flags = 0x00000201;
pUserInfo1->usri1_script_path = NULL;
nasStatus = NetUserAdd(
szHostServerName,
1,
(LPBYTE)pUserInfo1,
&dwParmErr
);
hr = HRESULT_FROM_WIN32(nasStatus);
RRETURN(hr);
}
HRESULT
CWinNTUser::GetModalInfo(
THIS_ DWORD dwApiLevel,
BOOL fExplicit
)
{
NET_API_STATUS nasStatus;
LPBYTE lpBuffer = NULL;
HRESULT hr;
WCHAR szPDCName[MAX_PATH];
//
// objects associated with invalid SIDs have neither a
// corresponding server nor domain
//
if ((!_DomainName) && (!_ServerName)) {
BAIL_ON_FAILURE(hr = E_ADS_INVALID_USER_OBJECT);
}
if (_ParentType == WINNT_DOMAIN_ID) {
hr = WinNTGetCachedDCName(
_DomainName,
szPDCName,
_Credentials.GetFlags()
);
BAIL_ON_FAILURE(hr);
}else {
hr = MakeUncName(
_ServerName,
szPDCName
);
BAIL_ON_FAILURE(hr);
}
nasStatus = NetUserModalsGet(
szPDCName,
dwApiLevel,
&lpBuffer
);
hr = HRESULT_FROM_WIN32(nasStatus);
BAIL_ON_FAILURE(hr);
hr = UnMarshallModalInfo(lpBuffer, dwApiLevel, fExplicit);
BAIL_ON_FAILURE(hr);
error:
if (lpBuffer) {
NetApiBufferFree(lpBuffer);
}
RRETURN(hr);
}
HRESULT
CWinNTUser::UnMarshallModalInfo(
LPBYTE lpBuffer,
DWORD dwApiLevel,
BOOL fExplicit
)
{
ADsAssert(lpBuffer);
switch (dwApiLevel) {
case 0:
RRETURN(UnMarshall_ModalLevel0(fExplicit, (LPUSER_MODALS_INFO_0)lpBuffer));
break;
case 2:
RRETURN(UnMarshall_ModalLevel2(fExplicit, (LPUSER_MODALS_INFO_2)lpBuffer));
break;
case 3:
RRETURN(UnMarshall_ModalLevel3(fExplicit, (LPUSER_MODALS_INFO_3)lpBuffer));
break;
default:
RRETURN(E_FAIL);
}
}
HRESULT
CWinNTUser::UnMarshall_ModalLevel0(
BOOL fExplicit,
LPUSER_MODALS_INFO_0 pUserInfo0
)
{
HRESULT hr = S_OK;
hr = SetDWORDPropertyInCache(
_pPropertyCache,
TEXT("MinPasswordLength"),
pUserInfo0->usrmod0_min_passwd_len,
fExplicit
);
hr = SetDWORDPropertyInCache(
_pPropertyCache,
TEXT("MaxPasswordAge"),
pUserInfo0->usrmod0_max_passwd_age,
fExplicit
);
hr = SetDWORDPropertyInCache(
_pPropertyCache,
TEXT("MinPasswordAge"),
pUserInfo0->usrmod0_min_passwd_age,
fExplicit
);
hr = SetDWORDPropertyInCache(
_pPropertyCache,
TEXT("PasswordHistoryLength"),
pUserInfo0->usrmod0_password_hist_len,
fExplicit
);
RRETURN(S_OK);
}
HRESULT
CWinNTUser::UnMarshall_ModalLevel2(
BOOL fExplicit,
LPUSER_MODALS_INFO_2 pUserInfo2
)
{
RRETURN(S_OK);
}
HRESULT
CWinNTUser::UnMarshall_ModalLevel3(
BOOL fExplicit,
LPUSER_MODALS_INFO_3 pUserInfo3
)
{
HRESULT hr = S_OK;
hr = SetDWORDPropertyInCache(
_pPropertyCache,
TEXT("AutoUnlockInterval"),
pUserInfo3->usrmod3_lockout_duration,
fExplicit
);
hr = SetDWORDPropertyInCache(
_pPropertyCache,
TEXT("LockoutObservationInterval"),
pUserInfo3->usrmod3_lockout_observation_window,
fExplicit
);
hr = SetDWORDPropertyInCache(
_pPropertyCache,
TEXT("MaxBadPasswordsAllowed"),
pUserInfo3->usrmod3_lockout_threshold,
fExplicit
);
RRETURN(S_OK);
}
//
// This method is meant to set the password, so that new users
// can be created, their password set and then SetInfo can be
// called. This is necessary to allow creation of users when there
// are restrictions such as passwd should be present.
//
HRESULT
CWinNTUser::setPrivatePassword(
PWSTR pszNewPassword
)
{
HRESULT hr = S_OK;
// CCred safely stores password for us
if (_pCCredentialsPwdHolder) {
hr = _pCCredentialsPwdHolder->SetPassword(pszNewPassword);
BAIL_ON_FAILURE(hr);
} else
_pCCredentialsPwdHolder = new CCredentials(NULL, pszNewPassword, 0);
if (!_pCCredentialsPwdHolder) {
hr = E_OUTOFMEMORY;
} else
_fPasswordSet = TRUE;
error:
RRETURN(hr);
}
//
// This method is meant to set the password, so that new users
// can be created, their password set and then SetInfo can be
// called. This is necessary to allow creation of users when there
// are restrictions such as passwd should be present.
//
HRESULT
CWinNTUser::getPrivatePassword(
PWSTR * ppszPassword
)
{
HRESULT hr = S_OK;
if (_pCCredentialsPwdHolder && _fPasswordSet) {
hr = _pCCredentialsPwdHolder->GetPassword(ppszPassword);
} else
hr = E_FAIL;
RRETURN(hr);
}
HRESULT
CWinNTUser::GetUserFlags(
DWORD *pdwUserFlags
)
{
HRESULT hr = S_OK;
NET_API_STATUS nasStatus;
LPBYTE lpBuffer = NULL;
WCHAR szHostServerName[MAX_PATH];
ADsAssert(pdwUserFlags != NULL);
if (GetObjectState() == ADS_OBJECT_UNBOUND) {
RRETURN_EXP_IF_ERR(E_ADS_OBJECT_UNBOUND);
}
//
// objects associated with invalid SIDs have neither a
// corresponding server nor domain
//
if ((!_DomainName) && (!_ServerName)) {
BAIL_ON_FAILURE(hr = E_ADS_INVALID_USER_OBJECT);
}
if (_ParentType == WINNT_DOMAIN_ID) {
hr = WinNTGetCachedDCName(
_DomainName,
szHostServerName,
_Credentials.GetFlags()
);
BAIL_ON_FAILURE(hr);
}else {
hr = MakeUncName(
_ServerName,
szHostServerName
);
}
nasStatus = NetUserGetInfo(
szHostServerName,
_Name,
3,
&lpBuffer
);
hr = HRESULT_FROM_WIN32(nasStatus);
BAIL_ON_FAILURE(hr);
*pdwUserFlags = ((LPUSER_INFO_3)lpBuffer)->usri3_flags;
error:
if (lpBuffer) {
NetApiBufferFree(lpBuffer);
}
RRETURN(hr);
}