Windows-Server-2003/admin/snapin/filemgmt/lsastuff.cpp

463 lines
12 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;
#include <ntlsa.h>
#define _NTDEF_
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)
#include <lmaccess.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 )
{
if ( NULL == punistr || NULL == psz )
{
ASSERT(FALSE);
return;
}
size_t cTchar = ::wcslen(psz);
punistr->Buffer = new WCHAR[cTchar + 1];
ASSERT( NULL != punistr->Buffer );
if ( NULL != punistr->Buffer )
{
::wcscpy( punistr->Buffer, psz );
// Length and MaximumLength are counts of bytes.
punistr->Length = (USHORT)(cTchar * sizeof(WCHAR));
punistr->MaximumLength = punistr->Length + sizeof(WCHAR);
}
else
::ZeroMemory( punistr, sizeof(*punistr) );
}
/*******************************************************************
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 )
{
if ( punistr && punistr->Buffer )
{
delete punistr->Buffer;
::ZeroMemory( punistr, sizeof(*punistr) );
}
}
/*******************************************************************
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 );
// 563140-2002/03/04 JonN POLICY_ALL_ACCESS was too much authority.
// LsaLookupNames only requires POLICY_LOOKUP_NAMES.
// LsaOpenAccount requres no rights (MarkPu).
// LsaCreateAccount requires POLICY_CREATE_ACCOUNT (MarkPu).
NTSTATUS ntstatus = ::LsaOpenPolicy(
punistrServerName,
&oa,
POLICY_LOOKUP_NAMES | POLICY_CREATE_ACCOUNT,
&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;
if ( !plsardl || !plsasid || !plsardl->Domains[0].Sid )
{
ASSERT(FALSE);
ntstatus = E_FAIL;
break;
}
// ISSUE-2002-03-04-JonN There is a potential issue here
// if LsaLookupNames returns an invalid SID. This is a fairly
// remote risk however.
//
// 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 );
// 563140-2002/04/08 JonN POLICY_ALL_ACCESS | DELETE was too much authority.
// CliffV says:
// LsaGetSystemAccessAccount only requires ACCOUNT_VIEW.
// LsaSetSystemAccessAccount only requires ACCOUNT_ADJUST_SYSTEM_ACCESS.
//
// Determine whether this LSA account exists, create it if not
//
ntstatus = ::LsaOpenAccount( hlsa,
psidAccount,
ACCOUNT_VIEW | ACCOUNT_ADJUST_SYSTEM_ACCESS,
&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_ADJUST_SYSTEM_ACCESS,
&hlsaAccount );
// 4/8/02 JonN: CliffV confirms that the account is created without any privileges
}
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()