447 lines
11 KiB
C++
447 lines
11 KiB
C++
// LsaStuff.cpp
|
|
//
|
|
// LSA-dependent code
|
|
//
|
|
// HISTORY
|
|
// 09-Jul-97 jonn Creation.
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "DynamLnk.h" // DynamicDLL
|
|
|
|
extern "C"
|
|
{
|
|
#define NTSTATUS LONG
|
|
#define PNTSTATUS NTSTATUS*
|
|
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
|
|
#define SE_SHUTDOWN_PRIVILEGE (19L)
|
|
|
|
// stuff taken from ntdef.h
|
|
typedef struct _UNICODE_STRING {
|
|
USHORT Length;
|
|
USHORT MaximumLength;
|
|
#ifdef MIDL_PASS
|
|
[size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;
|
|
#else // MIDL_PASS
|
|
PWSTR Buffer;
|
|
#endif // MIDL_PASS
|
|
} UNICODE_STRING;
|
|
typedef UNICODE_STRING *PUNICODE_STRING;
|
|
typedef struct _OBJECT_ATTRIBUTES {
|
|
ULONG Length;
|
|
HANDLE RootDirectory;
|
|
PUNICODE_STRING ObjectName;
|
|
ULONG Attributes;
|
|
PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
|
|
PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
|
|
} OBJECT_ATTRIBUTES;
|
|
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
|
|
#define InitializeObjectAttributes( p, n, a, r, s ) { \
|
|
(p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
|
|
(p)->RootDirectory = r; \
|
|
(p)->Attributes = a; \
|
|
(p)->ObjectName = n; \
|
|
(p)->SecurityDescriptor = s; \
|
|
(p)->SecurityQualityOfService = NULL; \
|
|
}
|
|
#define _NTDEF
|
|
|
|
// from ntstatus.h
|
|
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
|
|
|
|
// from lmaccess.h
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
NetUserModalsGet (
|
|
IN LPCWSTR servername OPTIONAL,
|
|
IN DWORD level,
|
|
OUT LPBYTE *bufptr
|
|
);
|
|
typedef struct _USER_MODALS_INFO_1 {
|
|
DWORD usrmod1_role;
|
|
LPWSTR usrmod1_primary;
|
|
}USER_MODALS_INFO_1, *PUSER_MODALS_INFO_1, *LPUSER_MODALS_INFO_1;
|
|
//
|
|
// UAS role manifests under NETLOGON
|
|
//
|
|
#define UAS_ROLE_STANDALONE 0
|
|
#define UAS_ROLE_MEMBER 1
|
|
#define UAS_ROLE_BACKUP 2
|
|
#define UAS_ROLE_PRIMARY 3
|
|
|
|
#include <ntlsa.h>
|
|
}
|
|
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
|
|
typedef enum _Netapi32ApiIndex
|
|
{
|
|
BUFFERFREE_ENUM = 0,
|
|
USERMODALSGET_ENUM
|
|
};
|
|
|
|
// not subject to localization
|
|
static LPCSTR g_apchNetapi32FunctionNames[] = {
|
|
"NetApiBufferFree",
|
|
"NetUserModalsGet",
|
|
NULL
|
|
};
|
|
|
|
typedef NET_API_STATUS (*BUFFERFREEPROC)(LPVOID);
|
|
typedef NET_API_STATUS (*USERMODALSGETPROC)(LPCWSTR, DWORD, LPBYTE*);
|
|
|
|
// not subject to localization
|
|
DynamicDLL g_LSASTUFF_Netapi32DLL( _T("NETAPI32.DLL"), g_apchNetapi32FunctionNames );
|
|
|
|
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: ::FillUnicodeString
|
|
|
|
SYNOPSIS: Standalone method for filling in a UNICODE_STRING
|
|
|
|
ENTRY: punistr - Unicode string to be filled in.
|
|
nls - Source for filling the unistr
|
|
|
|
EXIT:
|
|
|
|
NOTES: punistr->Buffer is allocated here and must be deallocated
|
|
by the caller using FreeUnicodeString.
|
|
|
|
HISTORY:
|
|
jonn 07/09/97 copied from net\ui\common\src\lmobj\lmobj\uintmem.cxx
|
|
|
|
********************************************************************/
|
|
VOID FillUnicodeString( LSA_UNICODE_STRING * punistr, LPCWSTR psz )
|
|
{
|
|
ASSERT( NULL != punistr && NULL != psz );
|
|
size_t cTchar = ::wcslen(psz);
|
|
// Length and MaximumLength are counts of bytes.
|
|
punistr->Length = (USHORT)(cTchar * sizeof(WCHAR));
|
|
punistr->MaximumLength = punistr->Length + sizeof(WCHAR);
|
|
punistr->Buffer = new WCHAR[cTchar + 1];
|
|
ASSERT( NULL != punistr->Buffer );
|
|
::wcscpy( punistr->Buffer, psz );
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: ::FreeUnicodeString
|
|
|
|
SYNOPSIS: Standalone method for freeing in a UNICODE_STRING
|
|
|
|
ENTRY: unistr - Unicode string whose Buffer is to be freed.
|
|
|
|
EXIT:
|
|
|
|
HISTORY:
|
|
jonn 07/09/97 copied from net\ui\common\src\lmobj\lmobj\uintmem.cxx
|
|
|
|
********************************************************************/
|
|
VOID FreeUnicodeString( LSA_UNICODE_STRING * punistr )
|
|
{
|
|
ASSERT( punistr != NULL );
|
|
delete punistr->Buffer;
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: InitObjectAttributes
|
|
|
|
SYNOPSIS:
|
|
|
|
This function initializes the given Object Attributes structure, including
|
|
Security Quality Of Service. Memory must be allcated for both
|
|
ObjectAttributes and Security QOS by the caller.
|
|
|
|
ENTRY:
|
|
|
|
poa - Pointer to Object Attributes to be initialized.
|
|
|
|
psqos - Pointer to Security QOS to be initialized.
|
|
|
|
EXIT:
|
|
|
|
NOTES:
|
|
|
|
HISTORY:
|
|
jonn 07/09/97 copied from net\ui\common\src\lmobj\lmobj\uintlsa.cxx
|
|
|
|
********************************************************************/
|
|
VOID InitObjectAttributes( PLSA_OBJECT_ATTRIBUTES poa,
|
|
PSECURITY_QUALITY_OF_SERVICE psqos )
|
|
|
|
{
|
|
ASSERT( poa != NULL );
|
|
ASSERT( psqos != NULL );
|
|
|
|
psqos->Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
|
psqos->ImpersonationLevel = SecurityImpersonation;
|
|
psqos->ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
|
psqos->EffectiveOnly = FALSE;
|
|
|
|
//
|
|
// Set up the object attributes prior to opening the LSA.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
poa,
|
|
NULL,
|
|
0L,
|
|
NULL,
|
|
NULL );
|
|
|
|
//
|
|
// The InitializeObjectAttributes macro presently stores NULL for
|
|
// the psqos field, so we must manually copy that
|
|
// structure for now.
|
|
//
|
|
|
|
poa->SecurityQualityOfService = psqos;
|
|
}
|
|
|
|
|
|
BOOL
|
|
I_CheckLSAAccount( LSA_UNICODE_STRING* punistrServerName,
|
|
LPCTSTR pszLogOnAccountName,
|
|
DWORD* pdwMsgID ) // *pdsMsgID is always set if this fails
|
|
{
|
|
ASSERT( NULL != pdwMsgID );
|
|
|
|
BOOL fSuccess = FALSE;
|
|
LSA_HANDLE hlsa = NULL;
|
|
LSA_HANDLE hlsaAccount = NULL;
|
|
PSID psidAccount = NULL;
|
|
|
|
do { // false loop
|
|
|
|
//
|
|
// Determine whether the target machine is a BDC, and if so, get the PDC
|
|
//
|
|
|
|
// if an error occurs now, it is a read error
|
|
*pdwMsgID = IDS_LSAERR_READ_FAILED;
|
|
|
|
//
|
|
// Get LSA_POLICY handle
|
|
//
|
|
LSA_OBJECT_ATTRIBUTES oa;
|
|
SECURITY_QUALITY_OF_SERVICE sqos;
|
|
InitObjectAttributes( &oa, &sqos );
|
|
|
|
NTSTATUS ntstatus = ::LsaOpenPolicy(
|
|
punistrServerName,
|
|
&oa,
|
|
POLICY_ALL_ACCESS, // CODEWORK ??
|
|
&hlsa );
|
|
if ( !NT_SUCCESS(ntstatus) )
|
|
break;
|
|
|
|
//
|
|
// Remove ".\" or "thismachine\" from the head of the account name if it is present
|
|
//
|
|
CString strAccountName = pszLogOnAccountName;
|
|
int iBackslash = strAccountName.Find( _T('\\') );
|
|
if ( -1 < iBackslash )
|
|
{
|
|
CString strPrefix = strAccountName.Left(iBackslash);
|
|
if ( !lstrcmp(strPrefix, _T(".")) || IsLocalComputername(strPrefix) )
|
|
{
|
|
strAccountName = strAccountName.Mid(iBackslash+1);
|
|
}
|
|
}
|
|
|
|
//
|
|
// determine the SID of the account
|
|
//
|
|
PLSA_REFERENCED_DOMAIN_LIST plsardl = NULL;
|
|
PLSA_TRANSLATED_SID plsasid = NULL;
|
|
LSA_UNICODE_STRING unistrAccountName;
|
|
::FillUnicodeString( &unistrAccountName, strAccountName );
|
|
ntstatus = ::LsaLookupNames(
|
|
hlsa,
|
|
1,
|
|
&unistrAccountName,
|
|
&plsardl,
|
|
&plsasid );
|
|
::FreeUnicodeString( &unistrAccountName );
|
|
if ( !NT_SUCCESS(ntstatus) )
|
|
break;
|
|
|
|
//
|
|
// Build the SID of the account by taking the SID of the domain
|
|
// and adding at the end the RID of the account
|
|
//
|
|
PSID psidDomain = plsardl->Domains[0].Sid;
|
|
DWORD ridAccount = plsasid[0].RelativeId;
|
|
DWORD cbNewSid = ::GetLengthSid(psidDomain)+sizeof(ridAccount);
|
|
psidAccount = (PSID) new BYTE[cbNewSid];
|
|
ASSERT( NULL != psidAccount );
|
|
(void) ::CopySid( cbNewSid, psidAccount, psidDomain );
|
|
UCHAR* pcSubAuthorities = ::GetSidSubAuthorityCount( psidAccount ) ;
|
|
(*pcSubAuthorities)++;
|
|
DWORD* pdwSubAuthority = ::GetSidSubAuthority(
|
|
psidAccount, (*pcSubAuthorities)-1 );
|
|
*pdwSubAuthority = ridAccount;
|
|
|
|
(void) ::LsaFreeMemory( plsardl );
|
|
(void) ::LsaFreeMemory( plsasid );
|
|
|
|
//
|
|
// Determine whether this LSA account exists, create it if not
|
|
//
|
|
ntstatus = ::LsaOpenAccount( hlsa,
|
|
psidAccount,
|
|
ACCOUNT_ALL_ACCESS | DELETE, // CODEWORK
|
|
&hlsaAccount );
|
|
ULONG ulSystemAccessCurrent = 0;
|
|
if (STATUS_OBJECT_NAME_NOT_FOUND == ntstatus)
|
|
{
|
|
// handle account-not-found case
|
|
|
|
// if an error occurs now, it is a write error
|
|
*pdwMsgID = IDS_LSAERR_WRITE_FAILED;
|
|
ntstatus = ::LsaCreateAccount( hlsa,
|
|
psidAccount,
|
|
ACCOUNT_ALL_ACCESS | DELETE,
|
|
&hlsaAccount );
|
|
// presumably the account is created without POLICY_MODE_SERVICE privilege
|
|
}
|
|
else
|
|
{
|
|
ntstatus = ::LsaGetSystemAccessAccount( hlsaAccount, &ulSystemAccessCurrent );
|
|
}
|
|
if ( !NT_SUCCESS(ntstatus) )
|
|
break;
|
|
|
|
//
|
|
// Determine whether this LSA account has POLICY_MODE_SERVICE privilege,
|
|
// grant it if not
|
|
//
|
|
if ( POLICY_MODE_SERVICE != (ulSystemAccessCurrent & POLICY_MODE_SERVICE ) )
|
|
{
|
|
// if an error occurs now, it is a write error
|
|
*pdwMsgID = IDS_LSAERR_WRITE_FAILED;
|
|
|
|
ntstatus = ::LsaSetSystemAccessAccount(
|
|
hlsaAccount,
|
|
ulSystemAccessCurrent | POLICY_MODE_SERVICE );
|
|
if ( !NT_SUCCESS(ntstatus) )
|
|
break; // CODEWORK could check for STATUS_BACKUP_CONTROLLER
|
|
|
|
// display the write-succeeded message
|
|
*pdwMsgID = IDS_LSAERR_WRITE_SUCCEEDED;
|
|
}
|
|
else
|
|
{
|
|
*pdwMsgID = 0;
|
|
}
|
|
|
|
fSuccess = TRUE;
|
|
|
|
} while (FALSE); // false loop
|
|
|
|
// CODEWORK should check for special error code for NT5 non-DC
|
|
// using local policy object
|
|
|
|
if (NULL != hlsa)
|
|
{
|
|
::LsaClose( hlsa );
|
|
}
|
|
if (NULL != hlsaAccount)
|
|
{
|
|
::LsaClose( hlsaAccount );
|
|
}
|
|
if (NULL != psidAccount)
|
|
{
|
|
delete psidAccount;
|
|
}
|
|
|
|
return fSuccess;
|
|
|
|
} // I_CheckLSAAccount()
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// FCheckLSAAccount()
|
|
//
|
|
VOID
|
|
CServicePropertyData::FCheckLSAAccount()
|
|
{
|
|
LSA_UNICODE_STRING unistrServerName;
|
|
PLSA_UNICODE_STRING punistrServerName = NULL ;
|
|
USER_MODALS_INFO_1* pum1 = NULL;
|
|
DWORD dwMsgID = 0;
|
|
|
|
TRACE1("INFO: Checking LSA permissions for account %s...\n",
|
|
(LPCTSTR)m_strLogOnAccountName);
|
|
|
|
if ( !m_strMachineName.IsEmpty() )
|
|
{
|
|
::FillUnicodeString( &unistrServerName, m_strMachineName );
|
|
punistrServerName = &unistrServerName;
|
|
}
|
|
|
|
do // false loop
|
|
{
|
|
// check on the local machine
|
|
// this will always set dwMsgID if it fails
|
|
if (I_CheckLSAAccount(punistrServerName, m_strLogOnAccountName, &dwMsgID))
|
|
break; // this succeeded, we can stop now
|
|
|
|
// check whether this is a Backup Domain Controller
|
|
if ( !g_LSASTUFF_Netapi32DLL.LoadFunctionPointers() )
|
|
{
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
DWORD err = ((USERMODALSGETPROC)g_LSASTUFF_Netapi32DLL[USERMODALSGET_ENUM])(
|
|
(m_strMachineName.IsEmpty()) ? NULL : const_cast<LPTSTR>((LPCTSTR)m_strMachineName),
|
|
1,
|
|
reinterpret_cast<LPBYTE*>(&pum1) );
|
|
if (NERR_Success != err)
|
|
break;
|
|
ASSERT( NULL != pum1 );
|
|
if (UAS_ROLE_BACKUP != pum1->usrmod1_role)
|
|
break; // not a backup controller
|
|
if (NULL == pum1->usrmod1_primary )
|
|
{
|
|
ASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
// Try it on the PDC
|
|
(void) I_CheckLSAAccount(punistrServerName, pum1->usrmod1_primary, &dwMsgID);
|
|
|
|
} while (FALSE); // false loop
|
|
|
|
if ( NULL != punistrServerName )
|
|
{
|
|
::FreeUnicodeString( punistrServerName );
|
|
}
|
|
|
|
if ( NULL != pum1 )
|
|
{
|
|
if ( !g_LSASTUFF_Netapi32DLL.LoadFunctionPointers() )
|
|
{
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
((BUFFERFREEPROC)g_LSASTUFF_Netapi32DLL[BUFFERFREE_ENUM])( pum1 );
|
|
}
|
|
|
|
if (0 != dwMsgID)
|
|
{
|
|
DoServicesErrMsgBox( GetActiveWindow(), MB_OK | MB_ICONEXCLAMATION, 0, dwMsgID, (LPCTSTR)m_strLogOnAccountName );
|
|
}
|
|
|
|
} // FCheckLSAAccount()
|