Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1642 lines
52 KiB

//#pragma title ("SidCache.hpp -- Cache, Tree of SIDs")
/*
Copyright (c) 1995-1998, Mission Critical Software, Inc. All rights reserved.
===============================================================================
Module - sidcache.cpp
System - SDResolve
Author - Christy Boles
Created - 97/06/27
Description - Cache of SIDs. Implemented using TNode derived classes, Cache is
organized as a tree, sorted by Domain B RID. Each node contains
Domain A RID, Domain B RID, Account Name, and counters for stats.
Updates -
===============================================================================
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "stdafx.h"
#include <malloc.h>
#include <winbase.h>
#include <lm.h>
#include <lmaccess.h>
#include <assert.h>
#include "common.hpp"
#include "ErrDct.hpp"
#include "Ustring.hpp"
#include "sidcache.hpp"
#include "CommaLog.hpp"
#include "TxtSid.h"
#include "ResStr.h"
#include "EaLen.hpp"
#include "GetDcName.h"
// from sdresolve.hpp
extern BOOL BuiltinRid(DWORD rid);
extern DWORD OpenDomain(WCHAR const * domain);
extern TErrorDct err;
extern TErrorDct errAlt;
extern bool useErrAlt;
extern bool silent;
/***************************************************************************************************/
/* vRidComp is used as a compare function for TSidNode Trees
// It searches for v1 in the ridA field. The Tree must be sorted with RidComp before using this
// search fn.
// Return values: 0 tn->ridA == v1
// 1 tn->ridA < v1
// -1 tn->ridA > v1
//
/**************************************************************************************************/
int
vRidComp(
const TNode * tn, // in -TSidNode
const void * v1 // in -DWORD ridA value to look for
)
{
DWORD rid1;
DWORD rid2;
TRidNode * n2;
int retval;
assert( tn ); // we should always be given valid inputs
assert( v1 );
n2 = (TRidNode *)tn;
rid1 = n2->SrcRid();
rid2 = *(DWORD *)v1;
if ( rid1 < rid2 )
{
retval = -1;
}
if (rid1 > rid2)
{
retval = 1;
}
if ( rid1 == rid2) // ( rid1 == rid2 )
{
retval = 0;
}
return retval;
}
/***************************************************************************************************/
/* RidComp: used as a compare function for TSidNode Trees
It compares the ridA fields of SIDTNodes
Return Values:
0 t1->ridA == t2->ridA
1 t1->ridA > t2->ridA
-1 t1->ridA < t2->ridA
/***************************************************************************************************/
int RidComp(
const TNode * t1, //in -first node to compare
const TNode * t2 //in -second node to compare
)
{
assert( t1 );
assert( t2 );
TRidNode * st1 = (TRidNode *) t1;
TRidNode * st2 = (TRidNode *) t2;
DWORD rid1 = st1->SrcRid();
DWORD rid2 = st2->SrcRid();
int retval;
if ( rid1 < rid2 )
{
retval = -1;
}
if (rid1 > rid2)
{
retval = 1;
}
if ( rid1 == rid2 ) // (rid1 == rid2)
{
retval = 0;
}
return retval;
}
/***************************************************************************************************
vNameComp: used as a compare function for TSidNode trees
It compares a UNICODE string, with the acct_name field in the node
Return Values:
0 tn->acct_name == actname
1 tn->acct_name < actname
-1 tn->acct_name > actname
/***************************************************************************************************/
int
vNameComp(
const TNode * tn, //in -tree node
const void * actname //in -name to look for
)
{
assert( tn );
assert( actname );
LPWSTR str1 = ((TRidNode *)tn)->GetAcctName();
LPWSTR str2 = (LPWSTR) actname;
return UStrICmp(str1,str2);
}
/***************************************************************************************************/
/* CompN: used as a compare function for TSidNode Trees
It compares the acct_name fields of SIDTNodes
Return Values:
0 t1->acct_name == t2->acct_name
1 t1->acct_name > t2->acct_name
-1 t1->acct_name < t2->acct_name
Error Handling:
if given bad inputs, CompN displays an error message and returns 0
/***************************************************************************************************/
int
CompN(
const TNode * v1, //in -first node to compare
const TNode * v2 //in -second node to compare
)
{
assert( v1 );
assert( v2 );
TRidNode * t1 = (TRidNode *)v1;
TRidNode * t2 = (TRidNode *)v2;
return UStrICmp(t1->GetAcctName(),t2->GetAcctName());
}
int
vTargetNameComp(
const TNode * tn, //in -tree node
const void * actname //in -name to look for
)
{
assert( tn );
assert( actname );
LPWSTR str1 = ((TRidNode *)tn)->GetTargetAcctName();
LPWSTR str2 = (LPWSTR) actname;
return UStrICmp(str1,str2);
}
int
CompTargetN(
const TNode * v1, //in -first node to compare
const TNode * v2 //in -second node to compare
)
{
assert( v1 );
assert( v2 );
TRidNode * t1 = (TRidNode *)v1;
TRidNode * t2 = (TRidNode *)v2;
return UStrICmp(t1->GetTargetAcctName(),t2->GetTargetAcctName());
}
int
TSidCompare(
PSID const sid1, // in - first sid to compare
PSID const sid2 // in - second sid to compare
)
{
DWORD len1,
len2;
int retval = 0;
len1 = GetLengthSid(sid1);
len2 = GetLengthSid(sid2);
if ( len1 < len2 )
{
retval = -1;
}
if ( len1 > len2 )
{
retval = 1;
}
if ( len1 == len2 )
{
retval = memcmp(sid1,sid2,len1);
}
return retval;
}
/**************************************************************************************************
vSidComp: used as a compare function for TSidNode trees
It compares a UNICODE string, with the acct_name field in the node
Return Values:
0 tn->acct_name == actname
1 tn->acct_name < actname
-1 tn->acct_name > actname
/***************************************************************************************************/
int
vSidComp(
const TNode * tn, //in -tree node
const void * val //in -sid to look for
)
{
PSID sid1 = ((TGeneralSidNode *)tn)->SrcSid();
PSID sid2 = (PSID)val;
return TSidCompare(sid1,sid2);
}
int
nSidComp(
const TNode * v1, //in -first node to compare
const TNode * v2 //in -second node to compare
)
{
TGeneralSidNode * t1 = (TGeneralSidNode *)v1;
TGeneralSidNode * t2 = (TGeneralSidNode *)v2;
return TSidCompare(t1->SrcSid(), t2->SrcSid());
}
/*******************************************************************************************************/
// TSidNode Implementation
/*******************************************************************************************************/
TGeneralCache::TGeneralCache()
{
CompareSet(&nSidComp);
TypeSetTree();
}
TGeneralCache::~TGeneralCache()
{
DeleteAllListItems(TGeneralSidNode);
}
void * TRidNode::operator new(size_t sz, const LPWSTR oldname, const LPWSTR newname)
{
int nlen = UStrLen(newname) + UStrLen(oldname) + 1;
void * node = malloc(sz + nlen * (sizeof WCHAR) );
return node;
}
TAcctNode::TAcctNode()
{
owner_changes = 0;
group_changes = 0;
ace_changes = 0;
sace_changes = 0;
}
WCHAR * // ret- domain part of name
GetDomainName(
WCHAR * name // in - domain\\account name
)
{
assert (name);
int i;
int len = UStrLen(name);
WCHAR * domain;
for (i = 2 ; name[i] != '\\' && i < len ; i++ )
;
if ( i < len )
{
domain = new WCHAR[i+1];
if (!domain)
return NULL;
UStrCpy(domain,name,i);
}
else
{
domain = NULL;
}
return domain;
}
TGeneralSidNode::TGeneralSidNode(
const LPWSTR name1, // in - account name on source domain
const LPWSTR name2 // in - account name on target domain
)
{
assert (name1 && name2);
assert (*name1 && *name2);
WCHAR * domname = NULL;
memset(&ownerStats,0,(sizeof ownerStats));
memset(&groupStats,0,(sizeof ownerStats));
memset(&daclStats,0,(sizeof ownerStats));
memset(&saclStats,0,(sizeof ownerStats));
src_acct_name = new WCHAR[UStrLen(name1)+1];
if (!src_acct_name)
return;
safecopy(src_acct_name,name1);
tgt_acct_name = new WCHAR[UStrLen(name2) + 1];
if (!tgt_acct_name)
return;
safecopy(tgt_acct_name,name2);
SDRDomainInfo info;
domname = GetDomainName(name1);
if (!domname)
return;
SetDomainInfoStruct(domname,&info);
if ( info.valid )
{
src_domain = new WCHAR[UStrLen(info.domain_name)];
if (!src_domain)
return;
safecopy(src_domain, info.domain_name);
// src_dc = info.dc_name;
src_nsubs = info.nsubs;
src_sid = info.domain_sid;
}
else
{
err.MsgWrite(ErrE,DCT_MSG_DOMAIN_NOT_FOUND_S,domname);
src_domain = NULL;
// src_dc = NULL;
src_nsubs = 0;
src_sid = NULL;
}
if(domname)
{
delete [] domname;
}
domname = NULL;
domname = GetDomainName(name2);
if (!domname)
return;
SetDomainInfoStruct(domname,&info);
if ( info.valid )
{
tgt_domain = new WCHAR[UStrLen(info.domain_name)];
if (!tgt_domain)
return;
safecopy(tgt_domain, info.domain_name);
tgt_nsubs = info.nsubs;
tgt_sid = info.domain_sid;
}
else
{
err.MsgWrite(ErrE,DCT_MSG_DOMAIN_NOT_FOUND_S,domname);
tgt_domain = NULL;
tgt_nsubs = 0;
tgt_sid = NULL;
}
sizediff = GetSidLengthRequired(tgt_nsubs) - GetSidLengthRequired(src_nsubs);
if(domname)
{
delete [] domname;
}
}
WCHAR * // ret- textual representation of sid
BuildSidString(
PSID pSid // in - sid
)
{
WCHAR * buf = NULL;
DWORD bufLen = 0;
GetTextualSid(pSid,NULL,&bufLen);
buf = new WCHAR[bufLen+1];
if (!buf)
return NULL;
if ( ! GetTextualSid(pSid,buf,&bufLen) )
{
wcscpy(buf,L"<Unknown>");
}
return buf;
}
TGeneralSidNode::TGeneralSidNode(
const PSID pSid1, // in - source domain sid
const PSID pSid2 // in - target domain sid
)
{
WCHAR domain[LEN_Domain];
WCHAR account[LEN_Account];
DWORD lenDomain = DIM(domain);
DWORD lenAccount = DIM(account);
SID_NAME_USE snu;
DWORD nBytes;
memset(&ownerStats,0,(sizeof ownerStats));
memset(&groupStats,0,(sizeof ownerStats));
memset(&daclStats,0,(sizeof ownerStats));
memset(&saclStats,0,(sizeof ownerStats));
// Source domain
if ( pSid1 )
{
// Make a copy of the SID
src_nsubs = *GetSidSubAuthorityCount(pSid1);
nBytes = GetSidLengthRequired(src_nsubs);
src_sid = new BYTE[nBytes];
if (!src_sid)
return;
CopySid(nBytes,src_sid,pSid1);
// Look up name for source SID
if ( LookupAccountSid(NULL,pSid1,account,&lenAccount,domain,&lenDomain,&snu) )
{
if ( lenAccount == 0 && snu == SidTypeDeletedAccount )
{
WCHAR * buf = BuildSidString(pSid1);
if (!buf)
return;
swprintf(account,L"<Deleted Account (%s)>",buf);
delete [] buf;
}
src_acct_name = new WCHAR[UStrLen(domain) + 1 + UStrLen(account)+1];
if (!src_acct_name)
return;
swprintf(src_acct_name,L"%s\\%s",domain,account);
src_domain = NULL;
}
else
{
src_acct_name = BuildSidString(pSid1);
if (!src_acct_name)
return;
src_domain = NULL;
}
}
else
{
src_nsubs = 0;
src_sid = NULL;
src_acct_name = NULL;
src_domain = NULL;
}
// Target domain
if ( pSid2 )
{
tgt_nsubs = *GetSidSubAuthorityCount(pSid2);
nBytes = GetSidLengthRequired(tgt_nsubs);
tgt_sid = new BYTE[nBytes];
if (!tgt_sid)
return;
CopySid(nBytes,tgt_sid,pSid2);
if ( LookupAccountSid(NULL,pSid2,account,&lenAccount,domain,&lenDomain,&snu) )
{
tgt_acct_name = new WCHAR[UStrLen(domain) + 1 + UStrLen(account)+1];
if (!tgt_acct_name)
return;
swprintf(tgt_acct_name,L"%s\\%s",domain,account);
tgt_domain = NULL;
}
else
{
tgt_acct_name = NULL;
tgt_domain = NULL;
}
}
else
{
tgt_nsubs = 0;
tgt_sid = NULL;
tgt_acct_name = NULL;
tgt_domain = NULL;
}
sizediff = GetSidLengthRequired(src_nsubs) - GetSidLengthRequired(tgt_nsubs);
}
TGeneralSidNode::~TGeneralSidNode()
{
if ( src_acct_name )
delete [] src_acct_name;
if ( tgt_acct_name )
delete [] tgt_acct_name;
if ( src_sid )
delete [] src_sid;
if ( tgt_sid )
delete [] tgt_sid;
if ( src_domain )
delete [] src_domain;
if ( tgt_domain )
delete [] tgt_domain;
}
TRidNode::TRidNode(
const LPWSTR oldacctname, // in -source account name
const LPWSTR newacctname // in -target account name
)
{
assert(tgtDomSid.c_str() == NULL);
srcRid = 0;
tgtRid = 0;
status = DEFAULT;
if ( ! newacctname )
{
acct_len = -1;
swprintf(acct_name,L"%s",oldacctname);
acct_name[UStrLen(acct_name)+1] = 0;
}
else
{
acct_len = UStrLen(oldacctname);
swprintf(acct_name,L"%s:%s",oldacctname,newacctname);
acct_name[acct_len] = 0;
}
}
TRidNode::~TRidNode()
{
}
/*******************************************************************************************************/
// TSidCache Implementation
/*******************************************************************************************************/
void
TSDRidCache::ReportAccountReferences(
WCHAR const * filename // in - filename to record account references
)
{
if ( m_otherAccounts )
{
CommaDelimitedLog resultLog;
if ( resultLog.LogOpen(filename,FALSE) )
{
TGeneralSidNode * gnode;
TNodeTreeEnum tEnum;
for ( gnode = (TGeneralSidNode *)tEnum.OpenFirst(m_otherAccounts) ; gnode ; gnode = (TGeneralSidNode*)tEnum.Next() )
{
TSDFileDirCell * pOwner = gnode->GetOwnerStats();
TSDFileDirCell * pGroup = gnode->GetGroupStats();
TSDFileDirCell * pDacl = gnode->GetDaclStats();
TSDFileDirCell * pSacl = gnode->GetSaclStats();
WCHAR * sAccountSid = BuildSidString(gnode->SrcSid());
if (!sAccountSid)
return;
resultLog.MsgWrite(L"%s,%s,%s,%ld,%ld,%ld,%ld",gnode->GetAcctName(),
sAccountSid,
GET_STRING(IDS_STReference_File),
pOwner->file,
pGroup->file,
pDacl->file,
pSacl->file );
resultLog.MsgWrite(L"%s,%s,%s,%ld,%ld,%ld,%ld",gnode->GetAcctName(),
sAccountSid,
GET_STRING(IDS_STReference_Dir),
pOwner->dir,
pGroup->dir,
pDacl->dir,
pSacl->dir );
resultLog.MsgWrite(L"%s,%s,%s,%ld,%ld,%ld,%ld",gnode->GetAcctName(),
sAccountSid,
GET_STRING(IDS_STReference_Share),
pOwner->share,
pGroup->share,
pDacl->share,
pSacl->share );
resultLog.MsgWrite(L"%s,%s,%s,%ld,%ld,%ld,%ld",gnode->GetAcctName(),
sAccountSid,
GET_STRING(IDS_STReference_Mailbox),
pOwner->mailbox,
pGroup->mailbox,
pDacl->mailbox,
pSacl->mailbox );
resultLog.MsgWrite(L"%s,%s,%s,%ld,%ld,%ld,%ld",gnode->GetAcctName(),
sAccountSid,
GET_STRING(IDS_STReference_Container),
pOwner->container,
pGroup->container,
pDacl->container,
pSacl->container );
resultLog.MsgWrite(L"%s,%s,%s,%ld,%ld,%ld,%ld",gnode->GetAcctName(),
sAccountSid,
GET_STRING(IDS_STReference_Member),
pOwner->member,
pGroup->member,
pDacl->member,
pSacl->member );
resultLog.MsgWrite(L"%s,%s,%s,%ld,%ld,%ld,%ld",gnode->GetAcctName(),
sAccountSid,
GET_STRING(IDS_STReference_UserRight),
pOwner->userright,
pGroup->userright,
pDacl->userright,
pSacl->userright );
resultLog.MsgWrite(L"%s,%s,%s,%ld,%ld,%ld,%ld",gnode->GetAcctName(),
sAccountSid,
GET_STRING(IDS_STReference_RegKey),
pOwner->regkey,
pGroup->regkey,
pDacl->regkey,
pSacl->regkey );
resultLog.MsgWrite(L"%s,%s,%s,%ld,%ld,%ld,%ld",gnode->GetAcctName(),
sAccountSid,
GET_STRING(IDS_STReference_Printer),
pOwner->printer,
pGroup->printer,
pDacl->printer,
pSacl->printer );
if (sAccountSid)
delete sAccountSid;
}
tEnum.Close();
resultLog.LogClose();
}
else
{
err.MsgWrite(ErrE,DCT_MSG_COULD_NOT_OPEN_RESULT_FILE_S,filename);
}
}
}
//----------------------------------------------------------------------------
// Function: VerifyTargetSids
//
// Synopsis: This function checks all target sids in the cache. If a target sid is not valid,
// the particular TRidNode will be marked with TRidNode::TARGETSIDISINVALID.
// We only concern about the case where the target domain sid is defined in
// the TSDRidCache.
//
// Arguments: none
//
// Returns: none
//
// Modifies: mark the TRidNode status to TRidNode::TARGETSIDISINVALID
// if the target sid is not valid
//
//----------------------------------------------------------------------------
void TSDRidCache::VerifyTargetSids()
{
TNodeListEnum cacheEnumerator;
TRidNode* aRidNode;
// We only concern about the case where the target domain sid is defined in TSDRidCache
// If it is not defined, TRidNode's are probably inserted with InsertLastWithSid, which
// means the sid has been verified to be valid already (sid mapping file).
if (to_sid != NULL)
{
DWORD dwSidSize = GetSidLengthRequired(to_nsubs);
PSID targetSid = (PSID) new byte[dwSidSize];
if (targetSid == NULL)
_com_issue_error(E_OUTOFMEMORY);
CopySid(dwSidSize, targetSid, to_sid); // copy the target domain sid
PDWORD rid = GetSidSubAuthority(targetSid,to_nsubs -1);
// go through each Node to check the target sid
for (aRidNode = (TRidNode *) cacheEnumerator.OpenFirst(this);
aRidNode != NULL && aRidNode->TgtRid() != 0;
aRidNode = (TRidNode *) cacheEnumerator.Next())
{
// make sure that the TRidNode object is not using its own target domain sid
// if it is using its own, we assume that the target sid has been verified
// if target domain sid string is not empty, the TRidNode object uses its own target domain sid
PCWSTR pszRidNodeOwnTgtDomSid = aRidNode->GetTgtDomSid();
if (pszRidNodeOwnTgtDomSid != NULL && *pszRidNodeOwnTgtDomSid != L'\0')
continue;
(*rid) = aRidNode->TgtRid(); // replace last sub with this node's RID
// look up the sid
BOOL bIsTargetSidValid = TRUE;
if (!aRidNode->IsValidOnTgt())
{
// if target rid is 0 or already verified before, we don't need to verify it again
bIsTargetSidValid = FALSE;
}
else
{
WCHAR name[MAX_PATH];
WCHAR domainName[MAX_PATH];
DWORD cbName = MAX_PATH;
DWORD cbDomainName = MAX_PATH;
SID_NAME_USE sid_use;
if (!LookupAccountSid(NULL, targetSid, name, &cbName, domainName, &cbDomainName, &sid_use))
{
bIsTargetSidValid = FALSE;
aRidNode->SetStatus(aRidNode->GetStatus() | TRidNode::TARGETSIDISINVALID);
}
}
}
if (targetSid)
delete[] ((byte*)targetSid);
}
}
void
TSDRidCache::ReportToVarSet(
IVarSet * pVarSet, // in -varset to write information to
bool summary, // in -flag: whether to print summary information
bool detail // in -flag: whether to print detailed stats
)
{
TNodeTreeEnum tEnum;
TRidNode * tnode;
long users=0;
long lgroups=0;
long ggroups=0;
long other=0;
long unres_users=0;
long unres_lg=0;
long unres_gg=0;
long unres_other=0;
long total=0;
long n = 0;
// sort the cache by names before printing the report
if (IsTree())
{
ToSorted();
}
SortedToScrambledTree();
Sort(&CompN);
Balance();
if ( detail )
{
for ( tnode = (TRidNode *)tEnum.OpenFirst(this) ; tnode ; tnode = (TRidNode *)tEnum.Next() )
{
if( tnode->ReportToVarSet(pVarSet,n) )
{
n++;
}
switch ( tnode->Type() )
{
case EA_AccountGroup: ggroups++; break;
case EA_AccountGGroup: ggroups++; break;
case EA_AccountLGroup: lgroups++; break;
case EA_AccountUser: users++; break;
default:
other++;
err.MsgWrite(0,DCT_MSG_BAD_ACCOUNT_TYPE_SD,tnode->GetAcctName(),tnode->Type() );
}
}
tEnum.Close();
if ( m_otherAccounts )
{
TGeneralSidNode * gnode;
for ( gnode = (TGeneralSidNode *)tEnum.OpenFirst(m_otherAccounts) ; gnode ; gnode = (TGeneralSidNode*)tEnum.Next() )
{
if( gnode->ReportToVarSet(pVarSet,n) )
{
n++;
}
other++;
}
}
total = users+lgroups+ggroups+other + unres_gg + unres_lg + unres_users + unres_other;
pVarSet->put(GET_BSTR(DCTVS_Stats_Accounts_NumUsers),users);
pVarSet->put(GET_BSTR(DCTVS_Stats_Accounts_NumGlobalGroups),ggroups);
pVarSet->put(GET_BSTR(DCTVS_Stats_Accounts_NumLocalGroups),lgroups);
pVarSet->put(GET_BSTR(DCTVS_Stats_Accounts_NumOther),other);
}
// re-sort by rid after printing the report
if (IsTree())
{
ToSorted();
}
SortedToScrambledTree();
Sort(&RidComp);
Balance();
}
/***************************************************************************************************/
/* TSidCache::Display: Displays the summary information, and/or contents of the TSidCache tree
/***************************************************************************************************/
void
TSDRidCache::Display(
bool summary, // in -flag: whether to print summary information
bool detail // in -flag: whether to print detailed stats
)
{
TNodeTreeEnum tEnum;
TRidNode * tnode;
long users=0;
long lgroups=0;
long ggroups=0;
long other=0;
long unres_users=0;
long unres_lg=0;
long unres_gg=0;
long unres_other=0;
long total=0;
//
// sort the cache by names before printing the report
//
// Note that the tree generated during the Sort method may become grossly un-balanced if
// the data is already in the same order that the Sort method sorts the data. A stack
// overflow may occur in the Balance method when it tries to convert the un-balanced tree
// into a linked list before generating a balanced tree.
//
// Therefore it is necessary to generate a random 'scrambled' tree before re-sorting. If
// already a tree the data must be converted to a list before generating the random tree.
//
if (IsTree())
{
ToSorted();
}
SortedToScrambledTree();
Sort(&CompN);
Balance();
if ( detail )
{
err.MsgWrite(0,DCT_MSG_ACCOUNT_DETAIL_HEADER);
err.MsgWrite(0, DCT_MSG_ACCOUNT_DETAIL_FORMAT);
for ( tnode = (TRidNode *)tEnum.OpenFirst(this) ; tnode ; tnode = (TRidNode *)tEnum.Next() )
{
tnode->DisplayStats();
switch ( tnode->Type() )
{
case EA_AccountGroup: ggroups++; break;
case EA_AccountGGroup: ggroups++; break;
case EA_AccountLGroup: lgroups++; break;
case EA_AccountUser: users++; break;
default:
other++;
err.MsgWrite(0,DCT_MSG_BAD_ACCOUNT_TYPE_SD,tnode->GetAcctName(),tnode->Type() );
}
}
total = users+lgroups+ggroups+other + unres_gg + unres_lg + unres_users + unres_other;
err.MsgWrite(0,DCT_MSG_ACCOUNT_DETAIL_FOOTER);
}
if ( summary )
{
err.MsgWrite(0,DCT_MSG_ACCOUNT_USER_GROUP_COUNT_DD,users+unres_users,ggroups+unres_gg+lgroups+unres_lg);
err.MsgWrite(0,DCT_MSG_ACCOUNT_STATUS_COUNT_DDD,accts,accts_resolved,accts - accts_resolved);
}
// re-sort by rid after printing the report
if (IsTree())
{
ToSorted();
}
SortedToScrambledTree();
Sort(&RidComp);
Balance();
}
/***************************************************************************************************/
/* GetSidB:
Builds a sid containing the Identifier Authority from the target-domain SID, along with the
RID from the ridB field of the supplied node.
/***************************************************************************************************/
PSID // ret -the domain B sid for the account referenced in tnode
TSDRidCache::GetTgtSid(
const TAcctNode * anode // in -node to copy RID from
)
{
TRidNode * tnode = (TRidNode *)anode;
assert( tnode );
assert( tnode->TgtRid() != 0);
assert( to_sid ); // we can't resolve if we don't have domain B sid
PDWORD rid;
DWORD sidsize = GetSidLengthRequired(to_nsubs);
PSID newsid = malloc(sidsize);
if (newsid)
{
CopySid(sidsize,newsid,to_sid); // copy the domain B sid
rid = GetSidSubAuthority(newsid,to_nsubs -1);
assert( (*rid) == -1 ); // FillCache makes sure to_sid always ends with -1(f...f)
(*rid)=tnode->TgtRid(); // replace last sub with this node's RID
}
return newsid;
}
// GetTgtSidWODomain:
// Returns the target sid for this node without having the target domain information
// filled in (like when we reACl using a sID mapping file).
PSID // ret -the domain B sid for the account referenced in tnode
TSDRidCache::GetTgtSidWODomain(
const TAcctNode * anode // in -node to copy RID from
)
{
TRidNode * tnode = (TRidNode *)anode;
assert( tnode );
assert( tnode->TgtRid() != 0);
PDWORD rid;
//get and convert the target domain sid stored in the node to a PSID
PSID pTgtSid = MallocedSidFromString(tnode->GetTgtDomSid());
if (pTgtSid)
{
PUCHAR pCount = GetSidSubAuthorityCount(pTgtSid);
DWORD nSub = (DWORD)(*pCount) - 1;
rid = GetSidSubAuthority(pTgtSid,nSub);
assert( (*rid) == -1 ); // FillCache makes sure to_sid always ends with -1(f...f)
(*rid)=tnode->TgtRid(); // replace last sub with this node's RID
}
return pTgtSid;
}
// GetTgtSidWODomain:
// Returns the target sid for this node without having the target domain information
// filled in (like when we reACl using a sID mapping file).
PSID
TSDRidCache::GetTgtSidWODomain(
const PSID psid // in - the source sid
)
{
TAcctNode* tn = LookupWODomain(psid);
if (tn)
return GetTgtSidWODomain(tn);
else
return NULL;
}
/***************************************************************************************************/
/* Display sid - Displays the contents of a SID.
The sid given is assumed to be a valid SID
/***************************************************************************************************/
void
DisplaySid(
const PSID ps // in -pointer to the sid to display
)
{
assert( ps );
PUCHAR ch = GetSidSubAuthorityCount(ps);
DWORD nsubs = *ch;
DWORD i;
PSID_IDENTIFIER_AUTHORITY ida = GetSidIdentifierAuthority(ps);
for ( i = 0 ; i < 6 ; i++ ) // 6 value fields in IA
{
printf("%ld, ",ida->Value[i]);
}
printf("\n%ld Subs: ",nsubs);
for ( i = 0 ; i < nsubs ; i++ ) // print subauthority values
{
printf("\nS[%d]= %ld ",i,*GetSidSubAuthority(ps,i));
}
printf("\n");
}
/***************************************************************************************************/
/* DisplaySid: If the sid is found in the cache, display the associated name, otherwise display
the sid contents.
ps and C are assumed to be valid.
/***************************************************************************************************/
void
DisplaySid(
const PSID ps, // in- sid to display
TAccountCache * C // in- TSidCache to look for sid in
)
{
assert ( ps );
if ( !C )
{
DisplaySid(ps);
}
else
{
WCHAR * name = C->GetName(ps);
if ( name )
{
err.MsgWrite(0,DCT_MSG_GENERIC_S,name);
}
else
{
DisplaySid(ps);
}
}
}
/***************************************************************************************************/
//DispSidInfo: Displays contents of the TSidNode
/***************************************************************************************************/
void
TRidNode::DisplaySidInfo() const
{
err.DbgMsgWrite(0,L"\nRid A= %ld \nName= %S \nRid B= %ld\n",srcRid,acct_name,tgtRid);
err.DbgMsgWrite(0,L"Owner changes: %ld\n",owner_changes);
err.DbgMsgWrite(0,L"Group changes: %ld\n",group_changes);
err.DbgMsgWrite(0,L"ACE changes: %ld\n",ace_changes);
err.DbgMsgWrite(0,L"SACE changes: %ld\n",sace_changes);
if ( !IsValidOnTgt() )
err.DbgMsgWrite(0,L"Target RID is not valid\n");
}
void
TAcctNode::DisplayStats() const
{
LPWSTR res;
if ( IsValidOnTgt() )
res = L"";
else
res = (WCHAR*)GET_BSTR(IDS_UNRESOLVED);
if (owner_changes || group_changes || ace_changes || sace_changes)
err.MsgWrite(0,DCT_MSG_ACCOUNT_REFS_DATA_SDDDDS,owner_changes,group_changes,ace_changes,sace_changes,res);
}
void
TRidNode::DisplayStats() const
{
LPWSTR res;
if ( IsValidOnTgt() )
res = L"";
else
res = (WCHAR*)GET_BSTR(IDS_UNRESOLVED);
#ifndef _DEBUG
if (owner_changes || group_changes || ace_changes || sace_changes )
err.MsgWrite(0,DCT_MSG_ACCOUNT_REFS_DATA_SDDDDS,acct_name,owner_changes,group_changes,ace_changes,sace_changes,res);
#else
if (owner_changes || group_changes || ace_changes || sace_changes || true)
err.DbgMsgWrite(0,L"%-35ls (%ld, %ld, %ld, %ld) %ls [%ld,%ld]",acct_name,owner_changes,group_changes,ace_changes,sace_changes,res,srcRid,tgtRid);
#endif
}
BOOL // ret- TRUE if details reported, FALSE if skipped blank record
TAcctNode::ReportToVarSet(
IVarSet * pVarSet ,// in - VarSet to write data to
DWORD n // in - index of account in varset
)
{
BOOL bRecorded = FALSE;
if ( owner_changes || group_changes || ace_changes || sace_changes )
{
WCHAR key[200];
swprintf(key,L"Stats.Accounts.%ld.Name",n);
pVarSet->put(key,GetAcctName());
swprintf(key,L"Stats.Accounts.%ld.Owners",n);
pVarSet->put(key,(LONG)owner_changes);
swprintf(key,L"Stats.Accounts.%ld.Groups",n);
pVarSet->put(key,(LONG)group_changes);
swprintf(key,L"Stats.Accounts.%ld.ACEs",n);
pVarSet->put(key,(LONG)ace_changes);
swprintf(key,L"Stats.Accounts.%ld.SACEs",n);
pVarSet->put(key,(LONG)sace_changes);
swprintf(key,L"Stats.Accounts.%ld.Resolved",n);
if ( IsValidOnTgt() )
{
pVarSet->put(key,L"Yes");
}
else
{
pVarSet->put(key,L"No");
}
bRecorded = TRUE;
}
return bRecorded;
}
/****************************************************************************************************/
/* SIDTCache Implementation */
/***************************************************************************************************/
TSDRidCache::TSDRidCache()
{
from_sid = NULL;
to_sid = NULL;
from_domain[0] = 0;
to_domain[0] = 0;
from_dc[0] = 0;
to_dc[0] = 0;
from_nsubs = 0;
to_nsubs = 0;
accts = 0;
accts_resolved = 0;
m_otherAccounts = NULL;
CompareSet(&CompN); //start with an empty tree, to be sorted by acct_name
TypeSetTree();
}
void
TSDRidCache::CopyDomainInfo(
TSDRidCache const * other
)
{
from_nsubs = other->from_nsubs;
to_nsubs = other->to_nsubs;
from_sid = NULL;
to_sid = NULL;
if ( other->from_sid )
from_sid = malloc(GetSidLengthRequired(from_nsubs));
if ( other->to_sid )
to_sid = malloc(GetSidLengthRequired(to_nsubs));
if ( from_sid )
CopySid(GetSidLengthRequired(from_nsubs),from_sid,other->from_sid);
if ( to_sid )
CopySid(GetSidLengthRequired(to_nsubs),to_sid,other->to_sid);
safecopy(from_domain,other->from_domain);
safecopy(to_domain,other->to_domain);
safecopy(from_dc,other->from_dc);
safecopy(to_dc,other->to_dc);
}
void
TSDRidCache::Clear()
{
TRidNode * node;
for ( node = (TRidNode *)Head() ; node ; Remove(node) , free(node), node = (TRidNode *)Head() )
;
accts = 0;
accts_resolved = 0;
}
TSDRidCache::~TSDRidCache()
{
if ( from_sid )
{
free(from_sid);
from_sid = NULL;
}
if ( to_sid )
{
free(to_sid);
to_sid = NULL;
}
// empty the list, and free each node
TRidNode * node;
for ( node = (TRidNode *)Head() ; node ; Remove(node) , free(node), node = (TRidNode *)Head() )
;
if ( m_otherAccounts )
delete m_otherAccounts;
}
/***************************************************************************************************/
/* SizeDiff:
Returns (Length of Domain B SID) - (Length of Domain A SID)
if Domain B sids are longer, otherwise returns 0
This is used to figure out how much space to allocate for new SIDs in the ACEs
This function assumes that from_sid and to_sid (from_nsubs and to_nsubs) are valid
/***************************************************************************************************/
DWORD
TSDRidCache::SizeDiff(
const TAcctNode * tnode // in -this parameter is not used for TSDRidCache
) const
{
assert( from_sid ); // not having from_sid or to_sid should abort the program
assert( to_sid );
DWORD fromsize = GetSidLengthRequired(from_nsubs);
DWORD tosize = GetSidLengthRequired(to_nsubs);
DWORD retval;
if ( fromsize >= tosize )
{
retval = 0;
}
else
{
retval = tosize - fromsize;
}
return retval;
}
/*****************************************************************************************************/
/* DomainizeSidFst:
Takes a domain sid, and verifies that its last subauthority value is -1. If the RID is not
-1, DomainizeSid adds a -1 to the end.
/*****************************************************************************************************/
PSID // ret -the sid with RID = -1
DomainizeSidFst(
PSID psid, // in -sid to check and possibly fix
BOOL freeOldSid // in -whether to free the old sid
)
{
assert (psid);
UCHAR len = (* GetSidSubAuthorityCount(psid));
PDWORD sub = GetSidSubAuthority(psid,len-1);
if ( *sub != -1 )
{
DWORD sdsize = GetSidLengthRequired(len+1); // sid doesn't already have -1 as rid
PSID newsid = (SID *)malloc(sdsize); // copy the sid, and add -1 to the end
if (!newsid)
{
assert(false);
return psid;
}
if ( ! InitializeSid(newsid,GetSidIdentifierAuthority(psid),len+1) ) // make a bigger sid w/same IA
{
err.SysMsgWrite(ErrU,GetLastError(),DCT_MSG_INITIALIZE_SID_FAILED_D,GetLastError());
assert (false);
}
for ( DWORD i = 0 ; i < len ; i++ )
{
sub = GetSidSubAuthority(newsid,i); // copy subauthorities
(*sub) = (*GetSidSubAuthority(psid,i));
}
sub = GetSidSubAuthority(newsid,len);
*sub = -1; // set rid =-1
if ( freeOldSid )
{
free(psid);
}
psid = newsid;
len++;
}
return psid;
}
void
SetDomainInfoStructFromSid(
PSID pSid, // in -sid for domain
SDRDomainInfo * info // out-struct containing information
)
{
// if ( (pSid) )
if ( IsValidSid(pSid) )
{
info->domain_name[0] = 0;
info->dc_name[0] = 0;
info->domain_sid = DomainizeSidFst(pSid,FALSE/*don't free old sid*/);
info->nsubs = *GetSidSubAuthorityCount(info->domain_sid);
info->valid = TRUE;
}
else
{
// info->domain_name[0] = 0;
// info->dc_name[0] = 0;
// info->valid = TRUE;
err.MsgWrite(ErrE,DCT_MSG_INVALID_DOMAIN_SID);
info->valid = FALSE;
}
}
void
SetDomainInfoStruct(
WCHAR const * domname, // in -name of domain
SDRDomainInfo * info // in -struct to put info into
)
{
DWORD rc = 0;
WCHAR domain[LEN_Computer];
BOOL found = FALSE;
_bstr_t computer;
safecopy(domain,domname);
info->valid = FALSE;
safecopy(info->domain_name, domname);
// get the domain controller name
rc = GetAnyDcName4(domain, computer);
if ( rc == ERROR_SUCCESS )
{
safecopy(info->dc_name,(PCWSTR)computer);
}
if ( ! rc )
{
// Get the SID for the domain
WCHAR strDomain[LEN_Domain];
DWORD lenStrDomain = DIM(strDomain);
SID_NAME_USE snu;
BYTE sid[200];
DWORD lenSid = DIM(sid);
if ( LookupAccountName(info->dc_name,info->domain_name,sid,&lenSid,strDomain,&lenStrDomain,&snu) )
{
info->domain_sid = DomainizeSidFst(sid, FALSE /*don't free sid*/);
info->nsubs = *GetSidSubAuthorityCount(info->domain_sid);
info->valid = TRUE;
found = TRUE;
}
else
{
rc = GetLastError();
}
}
if ( rc )
{
err.SysMsgWrite(ErrE,rc,DCT_MSG_DOMAIN_GET_INFO_FAILED_S,domain);
}
}
int
TSDRidCache::SetDomainInfoWithSid(
WCHAR const * strDomain, // in -domain name
WCHAR const * strSid, // in -textual representation of sid
bool firstdom // in -flag: (true => source domain), (false => target domain)
)
{
SDRDomainInfo info;
PSID pSid = SidFromString(strSid);
SetDomainInfoStructFromSid(pSid,&info);
if ( info.valid )
{
if ( firstdom )
{
safecopy(from_domain,strDomain);
from_sid = info.domain_sid;
from_dc[0] = 0;
from_nsubs = info.nsubs;
}
else
{
safecopy(to_domain,strDomain);
to_sid = info.domain_sid;
to_dc[0] =0;
to_nsubs = info.nsubs;
}
}
FreeSid(pSid);
return info.valid;
}
/*****************************************************************************************************/
/* SetDomainInfo:
sets either ( from_domain, from_sid, from_dc, from_nsubs) if ( firstdom )
or ( to_domain, to_sid, to_dc, to_nsubs) if ( ! firstdom)
/*****************************************************************************************************/
int // ret -true if members were set, false otherwise
TSDRidCache::SetDomainInfo(
WCHAR const * domname, // in -name of domain
bool firstdom // in -flag: (true => source domain), (false => target domain)
)
{
SDRDomainInfo info;
SetDomainInfoStruct(domname,&info);
if ( info.valid ) // we have good information to store
{
if ( firstdom )
{
safecopy(from_domain,info.domain_name);
from_sid = info.domain_sid;
safecopy(from_dc,info.dc_name);
from_nsubs = info.nsubs;
}
else
{
safecopy(to_domain,info.domain_name);
to_sid = info.domain_sid;
safecopy(to_dc,info.dc_name);
to_nsubs = info.nsubs;
}
}
return info.valid;
}
LPWSTR
TGeneralCache::GetName(
const PSID psid // in - SID to lookup account name for
)
{
TGeneralSidNode * tn = (TGeneralSidNode*)Lookup(psid);
if ( tn )
return tn->GetAcctName();
else
return NULL;
}
TAcctNode *
TGeneralCache::Lookup(
const PSID psid // in - SID to lookup account name for
)
{
TGeneralSidNode * tn = (TGeneralSidNode*)Find(&vSidComp,psid);
return (TAcctNode *)tn;
}
/***************************************************************************************************/
/* Lookup: takes a sid, checks whether it came from domain A. If so, it finds the corresponding entry
in the cache, and returns that node.
Returns: Pointer to TSidNode whose domain A rid matches asid's rid,
or NULL if not a domain A sid, or not found in the cache
/***************************************************************************************************/
TAcctNode *
TSDRidCache::Lookup(
const PSID psid // in -sid to search for
)
{
TRidNode * tn = NULL;
DWORD rid = 0;
BOOL bFromSourceDomain;
UCHAR * pNsubs;
DWORD nsubs;
TAcctNode * anode = NULL;
assert( IsValidSid(psid) );
assert ( IsValidSid(from_sid) );
pNsubs = GetSidSubAuthorityCount(psid);
if ( pNsubs )
{
nsubs = (*pNsubs);
}
else
{
assert(false);
return NULL;
}
rid = (* GetSidSubAuthority(psid,nsubs - 1) );
// if ((!from_sid) || (EqualPrefixSid(psid,from_sid))) // first check whether asid matches the from-domain
if ( EqualPrefixSid(psid,from_sid) ) // first check whether asid matches the from-domain
{
bFromSourceDomain = TRUE;
tn = (TRidNode *)Find(&vRidComp,&rid);
anode = tn;
}
else
{
bFromSourceDomain = FALSE;
}
if (! tn )
{
tn = (TRidNode *)-1;
if ( AddIfNotFound() && ! BuiltinRid(rid) ) // Don't lookup builtin accounts
{
if ( ! m_otherAccounts )
{
m_otherAccounts = new TGeneralCache();
if (!m_otherAccounts)
{
assert(false);
return NULL;
}
}
TGeneralSidNode * sn = (TGeneralSidNode *)m_otherAccounts->Lookup(psid);
if ( ! sn )
{
sn = new TGeneralSidNode(psid,NULL);
if (!sn)
{
assert(false);
return NULL;
}
m_otherAccounts->TreeInsert(sn);
}
anode = (TAcctNode*)sn;
}
}
return anode;
}
/***************************************************************************************************/
/* GetName: Calls SidCache::Lookup, and returns the acct name from the resulting node
/***************************************************************************************************/
LPWSTR // ret -acct_name, or NULL if not found
TSDRidCache::GetName(
const PSID psid // in -sid to look for
)
{
TAcctNode * tn = Lookup(psid);
LPWSTR retval;
if ( tn )
retval = tn->GetAcctName();
else
retval = NULL;
return retval;
}
/***************************************************************************************************/
/* LookupWODomain: takes a sid, checks whether it came from domain A. If so, it finds the corresponding entry
in the cache, and returns that node. This lookup function is used if the
src domain sid has not been recorded (like in the case of using a sID mapping file).
Returns: Pointer to TSidNode whose domain A rid matches asid's rid,
or NULL if not a domain A sid, or not found in the cache
/***************************************************************************************************/
TAcctNode* TSDRidCache::LookupWODomain(const PSID psid)
{
TAcctNode* pAcctNode = NULL;
// if map is empty then construct rid to node map
if (m_mapRidToNode.empty())
{
TNodeTreeEnum nte(this);
for (TRidNode* pNode = (TRidNode*)nte.First(); pNode; pNode = (TRidNode*)nte.Next())
{
m_mapRidToNode.insert(CRidToNodeMap::value_type(pNode->SrcRid(), pNode));
}
}
// retrieve RID from given SID
assert(IsValidSid(psid));
PUCHAR pCount = GetSidSubAuthorityCount(psid);
if (pCount)
{
DWORD rid = (*GetSidSubAuthority(psid, *pCount - 1));
// retrieve range of nodes with matching RID
CRidToNodeMap::_Pairii pairii = m_mapRidToNode.equal_range(rid);
// for each node in range...
for (CRidToNodeMap::iterator it = pairii.first; it != pairii.second; it++)
{
// retrieve RID node and compare domain SIDs
TRidNode* pNode = it->second;
if (pNode)
{
PSID psidSrc = SidFromString(pNode->GetSrcDomSid());
if (psidSrc)
{
// if domain SIDs are equal...
if (EqualPrefixSid(psid, psidSrc))
{
// a match has been found
pAcctNode = pNode;
}
FreeSid(psidSrc);
if (pAcctNode)
{
break;
}
}
}
}
}
return pAcctNode;
}