/*++ Copyright (c) 1998 - 1998 Microsoft Corporation Module Name: utils.cpp Abstract: This Module implements the utility routines for dsacls Author: hitesh raigandhi (hiteshr ) Environment:User Mode Revision History: --*/ #include "stdafx.h" #include "utils.h" #include "dsace.h" #include "dsacls.h" /******************************************************************* NAME: GetAccountNameFromSid SYNOPSIS: Convert Sid to Account Name ENTRY: pszServerName: Server name at which to look for pSid : Pointer to Sid EXIT: ppszName : Gets pointer to Account Name RETURNS: ERROR_SUCCESS if Successful ERROR_NOT_ENOUGH_MEMORY NOTES: If LookupAccountName resolve the sid, it is converted in to string and returned HISTORY: hiteshr July-1999 Created ********************************************************************/ DWORD GetAccountNameFromSid( LPWSTR pszServerName, PSID pSid, LPWSTR * ppszName ) { LPWSTR pszAccountName = NULL; LPWSTR pszDomainName = NULL; DWORD cbAccountName = 0 ; DWORD cbDomainName = 0; SID_NAME_USE Use ; DWORD dwErr = ERROR_SUCCESS; *ppszName = NULL; if( LookupAccountSid( pszServerName, // name of local or remote computer pSid, // security identifier NULL, // account name buffer &cbAccountName, NULL , &cbDomainName , &Use ) == FALSE ) { dwErr = GetLastError(); if( dwErr != ERROR_INSUFFICIENT_BUFFER ) { //Convert Sid to String if( !ConvertSidToStringSid( pSid, ppszName ) ) dwErr = GetLastError(); else dwErr = ERROR_SUCCESS; goto FAILURE_RETURN; } else dwErr = ERROR_SUCCESS; } pszAccountName = (LPWSTR)LocalAlloc( LMEM_FIXED, ( cbAccountName +1 ) * sizeof( WCHAR ) ); if( pszAccountName == NULL ) { dwErr = ERROR_NOT_ENOUGH_MEMORY; goto FAILURE_RETURN; } pszDomainName = (LPWSTR)LocalAlloc( LMEM_FIXED, ( cbDomainName + 1 )* sizeof( WCHAR ) ); if( pszDomainName == NULL ) { dwErr = ERROR_NOT_ENOUGH_MEMORY; goto FAILURE_RETURN; } if( LookupAccountSid( pszServerName, // name of local or remote computer pSid, // security identifier pszAccountName, // account name buffer &cbAccountName, pszDomainName , &cbDomainName , &Use ) == FALSE ) { dwErr = GetLastError(); goto FAILURE_RETURN; } *ppszName = (LPWSTR)LocalAlloc( LMEM_FIXED, ( cbAccountName + cbDomainName + 2 ) * sizeof( WCHAR ) ); if( *ppszName == NULL ) { dwErr = ERROR_NOT_ENOUGH_MEMORY; goto FAILURE_RETURN; } *ppszName[0] = NULL; if( cbDomainName ) { wcscpy( *ppszName, pszDomainName ); wcscat( *ppszName, L"\\" ); } wcscat( *ppszName, pszAccountName ); FAILURE_RETURN: if( pszDomainName ) LocalFree( pszDomainName ); if( pszAccountName ) LocalFree( pszAccountName ); return dwErr; } /******************************************************************* NAME: GetSidFromAccountName SYNOPSIS: Converts AccountName into SID ********************************************************************/ DWORD GetSidFromAccountName( LPWSTR pszServerName, PSID *ppSid, LPWSTR pszName ) { LPWSTR pszDomainName = NULL; DWORD cbSid = 0 ; DWORD cbDomainName = 0; SID_NAME_USE Use ; DWORD dwErr = ERROR_SUCCESS; if( LookupAccountName(pszServerName, // name of local or remote computer pszName, // security identifier NULL, // account name buffer &cbSid, NULL , &cbDomainName , &Use ) == FALSE ) { dwErr = GetLastError(); if( dwErr != ERROR_INSUFFICIENT_BUFFER ) goto FAILURE_RETURN; else dwErr = ERROR_SUCCESS; } *ppSid = (PSID)LocalAlloc( LMEM_FIXED, cbSid ); CHECK_NULL( *ppSid, FAILURE_RETURN ); pszDomainName = (LPWSTR)LocalAlloc( LMEM_FIXED, ( cbDomainName + 1 )* sizeof( WCHAR ) ); CHECK_NULL( pszDomainName, FAILURE_RETURN ); if( LookupAccountName( pszServerName, // name of local or remote computer pszName, // security identifier *ppSid, // account name buffer &cbSid, pszDomainName , &cbDomainName , &Use ) == FALSE ) { dwErr = GetLastError(); goto FAILURE_RETURN; } goto SUCCESS_RETURN; FAILURE_RETURN: if( pszDomainName ) LocalFree( pszDomainName ); if( *ppSid ) LocalFree( *ppSid ); SUCCESS_RETURN: return dwErr; } /******************************************************************* NAME: GetAceSid SYNOPSIS: Gets pointer to SID from an ACE ENTRY: pAce - pointer to ACE EXIT: RETURNS: Pointer to SID if successful, NULL otherwise NOTES: HISTORY: JeffreyS 08-Oct-1996 Created ********************************************************************/ PSID GetAceSid(PACE_HEADER pAce) { switch (pAce->AceType) { case ACCESS_ALLOWED_ACE_TYPE: case ACCESS_DENIED_ACE_TYPE: case SYSTEM_AUDIT_ACE_TYPE: case SYSTEM_ALARM_ACE_TYPE: return (PSID)&((PKNOWN_ACE)pAce)->SidStart; case ACCESS_ALLOWED_OBJECT_ACE_TYPE: case ACCESS_DENIED_OBJECT_ACE_TYPE: case SYSTEM_AUDIT_OBJECT_ACE_TYPE: case SYSTEM_ALARM_OBJECT_ACE_TYPE: return RtlObjectAceSid(pAce); } return NULL; } /******************************************************************* NAME: GetGlobalNamingContexts SYNOPSIS: Gets LDAP path for Schema and Extendend-Rights ENTRY: pszServerName, Server to bind to for query EXIT: pszSchemaNamingContext: Schema name in "LDAP:\\cn=schema,cn=..." format pszConfigurationNamingContext: Extendend rights path in "LDAP:\\CN=Extended-Rights,CN=Configuration..formats RETURNS: WIN32 Error Code ********************************************************************/ DWORD GetGlobalNamingContexts( LPWSTR pszServerName, LPWSTR * pszSchemaNamingContext, LPWSTR * pszConfigurationNamingContext ) { HRESULT hr = S_OK; DWORD dwErr = ERROR_SUCCESS; LPWSTR szSNC = NULL; ULONG uLen = 0; IADs *spRootDSE = NULL; LPWSTR pszRootDsePath = NULL; *pszSchemaNamingContext = NULL; *pszConfigurationNamingContext = NULL; if( pszServerName ) uLen = wcslen(L"LDAP://") + wcslen( pszServerName ) + wcslen( L"/RootDSE") + 1; else uLen = wcslen(L"LDAP://RootDSE"); pszRootDsePath = (LPWSTR)LocalAlloc( LMEM_FIXED, uLen * sizeof(WCHAR) ); CHECK_NULL( pszRootDsePath,FAILURE_RETURN ); wcscpy(pszRootDsePath, L"LDAP://"); if( pszServerName ) { wcscat( pszRootDsePath, pszServerName ); wcscat( pszRootDsePath, L"/" ); } wcscat( pszRootDsePath, L"RootDSE" ); /*hr = ::ADsOpenObject( pszRootDsePath, NULL, NULL, ADS_SECURE_AUTHENTICATION, IID_IADs, (void**)&spRootDSE ); */ hr = ::ADsGetObject( pszRootDsePath, IID_IADs, (void**)&spRootDSE ); CHECK_HR( hr, FAILURE_RETURN ); VARIANT varSchemaNamingContext; hr = spRootDSE->Get(L"schemaNamingContext", &varSchemaNamingContext); CHECK_HR( hr, FAILURE_RETURN ); szSNC = (LPWSTR)varSchemaNamingContext.bstrVal; uLen = wcslen( szSNC ) + 8 ; //For "LDAP:// + 1 *pszSchemaNamingContext = (LPWSTR) LocalAlloc( LMEM_FIXED, uLen* sizeof(WCHAR) ); CHECK_NULL( *pszSchemaNamingContext, FAILURE_RETURN ); wcscpy( *pszSchemaNamingContext, L"LDAP://"); wcscat( *pszSchemaNamingContext, szSNC ); hr = spRootDSE->Get(L"configurationNamingContext", &varSchemaNamingContext); CHECK_HR( hr, FAILURE_RETURN ); szSNC = (LPWSTR)varSchemaNamingContext.bstrVal; uLen = wcslen( szSNC ) + 27 + 1 ; *pszConfigurationNamingContext = (LPWSTR) LocalAlloc( LMEM_FIXED, uLen* sizeof(WCHAR) ); CHECK_NULL( *pszConfigurationNamingContext,FAILURE_RETURN ); wcscpy( *pszConfigurationNamingContext, L"LDAP://CN=Extended-Rights,"); wcscat( *pszConfigurationNamingContext, szSNC ); goto SUCCESS_RETURN; FAILURE_RETURN: if( *pszSchemaNamingContext ) LocalFree( *pszSchemaNamingContext ); if( *pszConfigurationNamingContext ) LocalFree( *pszConfigurationNamingContext ); SUCCESS_RETURN: if( spRootDSE ) spRootDSE->Release(); if( pszRootDsePath ) LocalFree( pszRootDsePath ); return dwErr; } /******************************************************************* NAME: FormatStringGUID SYNOPSIS: Given a GUID struct, it returns a GUID in string format, without {} //Function copied from marcoc code ********************************************************************/ BOOL FormatStringGUID(LPWSTR lpszBuf, UINT nBufSize, const GUID* pGuid) { lpszBuf[0] = NULL; // if it is a NULL GUID*, just return an empty string if (pGuid == NULL) { return FALSE; } /* typedef struct _GUID { unsigned long Data1; unsigned short Data2; unsigned short Data3; unsigned char Data4[ 8 ]; } int _snwprintf( wchar_t *buffer, size_t count, const wchar_t *format [, argume nt] ... ); */ return (_snwprintf(lpszBuf, nBufSize, L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", pGuid->Data1, pGuid->Data2, pGuid->Data3, pGuid->Data4[0], pGuid->Data4[1], pGuid->Data4[2], pGuid->Data4[3], pGuid->Data4[4], pGuid->Data4[5], pGuid->Data4[6], pGuid->Data4[7]) > 0); } /* Returns A string with n spaces */ void StringWithNSpace( UINT n, LPWSTR szSpace ) { for( UINT i = 0; i < n ; ++ i ) szSpace[i] = L' '; szSpace[n] = 0; } /* Loads the string from Resource Table and Formats it */ DWORD LoadMessage( IN DWORD MessageId, LPWSTR *ppszLoadString,...) { va_list ArgList; DWORD dwErr = ERROR_SUCCESS; va_start( ArgList, ppszLoadString ); WCHAR szBuffer[1024]; if( LoadString( g_hInstance, MessageId, szBuffer, 1023 ) == 0 ) { dwErr = GetLastError(); goto CLEAN_RETURN; } if( FormatMessage( FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, szBuffer, MessageId, 0, ( PWSTR )ppszLoadString, 0, &ArgList ) == 0 ) { dwErr = GetLastError(); goto CLEAN_RETURN; } CLEAN_RETURN: va_end( ArgList ); return dwErr; } /******************************************************************* NAME: DisplayString SYNOPSIS: Displays a string after inserting nIdent spaces ********************************************************************/ VOID DisplayString( UINT nIdent, LPWSTR pszDisplay ) { for ( UINT i = 0; i < nIdent; i++ ) wprintf( L" " ); wprintf(L"%s",pszDisplay); } VOID DisplayStringWithNewLine( UINT nIdent, LPWSTR pszDisplay ) { DisplayString( nIdent, pszDisplay ); wprintf(L"\n"); } VOID DisplayNewLine() { wprintf(L"\n"); } /******************************************************************* NAME: DisplayMessageEx SYNOPSIS: Loads Message from Resource and Formats its IN Indent - Number of tabs to indent MessageId - Id of the message to load ... - Optional list of parameters RETURNS: NONE ********************************************************************/ DWORD DisplayMessageEx( DWORD nIndent, IN DWORD MessageId,...) { va_list ArgList; LPWSTR pszLoadString = NULL; va_start( ArgList, MessageId ); WCHAR szBuffer[1024]; if( LoadString( g_hInstance, MessageId, szBuffer, 1023 ) == 0 ) { va_end( ArgList ); return GetLastError(); } if( FormatMessage( FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, szBuffer, MessageId, 0, ( PWSTR )&pszLoadString, 0, &ArgList ) == 0 ) { va_end( ArgList ); return GetLastError(); } DisplayStringWithNewLine( nIndent, pszLoadString ); LocalFree( pszLoadString ); return ERROR_SUCCESS; } BOOL GuidFromString(GUID* pGuid, LPCWSTR lpszGuidString) { ZeroMemory(pGuid, sizeof(GUID)); if (lpszGuidString == NULL) { return FALSE; } int nLen = lstrlen(lpszGuidString); // the string length should be 36 if (nLen != 36) return FALSE; // add the braces to call the Win32 API LPWSTR lpszWithBraces = (LPWSTR)LocalAlloc(LMEM_FIXED,((nLen+1+2)*sizeof(WCHAR)) ); // NULL plus {} if(!lpszWithBraces) return FALSE; wsprintf(lpszWithBraces, L"{%s}", lpszGuidString); return SUCCEEDED(::CLSIDFromString(lpszWithBraces, pGuid)); } /******************************************************************* NAME: GetServerName SYNOPSIS: Get the name of the server. If Obeject Path is in form \\ADSERVER\CN=John..., then it gets the server name from Object Path and changes Object Path to CN=John... ********************************************************************/ DWORD GetServerName( IN LPWSTR ObjectPath, OUT LPWSTR * ppszServerName ) { DWORD Win32Err = ERROR_SUCCESS; PWSTR Separator = NULL; PDOMAIN_CONTROLLER_INFO DcInfo = NULL; PDOMAIN_CONTROLLER_INFO DcInfo1 = NULL; PWSTR Path = NULL; HANDLE DsHandle = NULL; PDS_NAME_RESULT NameRes = NULL; BOOLEAN NamedServer = FALSE; NTSTATUS Status; LPWSTR ServerName = NULL; // // Get a server name // if ( wcslen( ObjectPath ) > 2 && *ObjectPath == L'\\' && *( ObjectPath + 1 ) == L'\\' ) { Separator = wcschr( ObjectPath + 2, L'\\' ); if ( Separator ) { *Separator = L'\0'; Path = Separator + 1; } else return ERROR_INVALID_PARAMETER; ServerName = ObjectPath + 2; *ppszServerName = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(WCHAR) * (wcslen(ServerName) + 1) ); if( *ppszServerName == NULL ) { if( Separator ) *Separator = L'\\'; return ERROR_NOT_ENOUGH_MEMORY; } wcscpy( *ppszServerName, ServerName ); //Remove server name from object path memmove( ObjectPath, Path, ( wcslen(Path) + 1) * sizeof(WCHAR) ); return ERROR_SUCCESS; } else { Path = ObjectPath; Win32Err = DsGetDcName( NULL, NULL, NULL, NULL, DS_DIRECTORY_SERVICE_REQUIRED, &DcInfo ); if ( Win32Err == ERROR_SUCCESS ) { ServerName = DcInfo[ 0 ].DomainControllerName + 2; } } // // Do the bind and crack // if ( Win32Err == ERROR_SUCCESS ) { Win32Err = DsBind( ServerName, NULL, &DsHandle ); if ( Win32Err == ERROR_SUCCESS ) { Win32Err = DsCrackNames( DsHandle, DS_NAME_NO_FLAGS, DS_FQDN_1779_NAME, DS_FQDN_1779_NAME, 1, &Path, &NameRes ); if ( Win32Err == ERROR_SUCCESS ) { if ( NameRes->cItems != 0 && NameRes->rItems[ 0 ].status == DS_NAME_ERROR_DOMAIN_ONLY ) { Win32Err = DsGetDcNameW( NULL, NameRes->rItems[ 0 ].pDomain, NULL, NULL, DS_DIRECTORY_SERVICE_REQUIRED, &DcInfo1 ); if ( Win32Err == ERROR_SUCCESS ) { ServerName = DcInfo1->DomainControllerName + 2; } if( Win32Err == ERROR_INVALID_DOMAINNAME || Win32Err == ERROR_NO_SUCH_DOMAIN ) ServerName = NULL; } } } } if( ServerName ) { *ppszServerName = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(WCHAR) * (wcslen(ServerName) + 1) ); if( *ppszServerName == NULL ) return ERROR_NOT_ENOUGH_MEMORY; wcscpy( *ppszServerName, ServerName ); Win32Err = ERROR_SUCCESS; } if( DcInfo ) NetApiBufferFree( DcInfo ); if( DcInfo1 ) NetApiBufferFree( DcInfo1 ); if( DsHandle ) DsUnBindW( &DsHandle ); if ( NameRes ) DsFreeNameResult( NameRes ); return Win32Err; } /******************************************************************* NAME: DisplayMessage SYNOPSIS: Loads Message from Message Table and Formats its IN Indent - Number of tabs to indent MessageId - Id of the message to load ... - Optional list of parameters RETURNS: NONE ********************************************************************/ VOID DisplayMessage( IN DWORD Indent, IN DWORD MessageId, ... ) /*++ Routine Description: Loads the resource out of the executable and displays it. Arguments: Indent - Number of tabs to indent MessageId - Id of the message to load ... - Optional list of parameters Return Value: VOID --*/ { PWSTR MessageDisplayString; va_list ArgList; ULONG Length, i; va_start( ArgList, MessageId ); Length = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, MessageId, 0, ( PWSTR )&MessageDisplayString, 0, &ArgList ); if ( Length != 0 ) { for ( i = 0; i < Indent; i++ ) { printf( "\t" ); } printf( "%ws", MessageDisplayString ); LocalFree( MessageDisplayString ); } va_end( ArgList ); } /******************************************************************* NAME: DisplayErrorMessage SYNOPSIS: Displays Error Message corresponding to Error RETURNS: NONE ********************************************************************/ VOID DisplayErrorMessage( IN DWORD Error ) { ULONG Size = 0; PWSTR DisplayString; ULONG Options = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM; Size = FormatMessage( Options, NULL, Error, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), ( LPTSTR )&DisplayString, 0, NULL ); if ( Size != 0 ) { printf( "%ws\n", DisplayString ); LocalFree( DisplayString ); } } /******************************************************************* NAME: ConvertStringAToStringW SYNOPSIS: Converts MBYTE stirng to UNICODE RETURNS: ERROR_SUCCESS if success ERROR_NOT_ENOUGH_MEMORY ********************************************************************/ DWORD ConvertStringAToStringW ( IN PSTR AString, OUT PWSTR *WString ) { DWORD Win32Err = ERROR_SUCCESS, Length; if ( AString == NULL ) { *WString = NULL; } else { Length = strlen( AString ); *WString = ( PWSTR )LocalAlloc( LMEM_FIXED, ( mbstowcs( NULL, AString, Length + 1 ) + 1 ) * sizeof( WCHAR ) ); if(*WString != NULL ) { mbstowcs( *WString, AString, Length + 1); } else { Win32Err = ERROR_NOT_ENOUGH_MEMORY; } } return( Win32Err ); } /******************************************************************* NAME: CopyUnicodeString SYNOPSIS: Copy Unicode string from Source to Destination RETURNS: ERROR_SUCCESS if success ERROR_NOT_ENOUGH_MEMORY ********************************************************************/ DWORD CopyUnicodeString( LPWSTR * strDst, LPWSTR strSrc ) { *strDst = (LPWSTR)LocalAlloc( LMEM_FIXED , ( wcslen(strSrc) + 1 ) * sizeof(WCHAR ) ); if ( !*strDst ) { return ERROR_NOT_ENOUGH_MEMORY; } wcscpy( *strDst, strSrc ); return ERROR_SUCCESS; } /******************************************************************* NAME: GetProtection SYNOPSIS: Sets PROTECTED_DACL_SECURITY_INFORMATION in pSI, if SE_DACL_PROTECTED is set pSD RETURNS: ERROR_SUCCESS if success ********************************************************************/ DWORD GetProtection( PSECURITY_DESCRIPTOR pSD, SECURITY_INFORMATION * pSI ) { SECURITY_DESCRIPTOR_CONTROL wSDControl = 0; DWORD dwRevision; // ; if( !GetSecurityDescriptorControl(pSD, &wSDControl, &dwRevision) ) { return GetLastError(); } if ( wSDControl & SE_DACL_PROTECTED ) *pSI |= PROTECTED_DACL_SECURITY_INFORMATION; return ERROR_SUCCESS; } /******************************************************************* NAME: BuildLdapPath SYNOPSIS: Builds a LDAP path using servername and path RETURNS: ERROR_SUCCESS if success ********************************************************************/ DWORD BuildLdapPath( LPWSTR * ppszLdapPath, LPWSTR pszServerName, LPWSTR pszPath ) { ULONG uLen = 0; if( pszServerName ) uLen = wcslen( pszServerName ) + wcslen( pszPath ); else uLen = wcslen( pszPath ); uLen += 9; //LDAP://ServerName/path *ppszLdapPath = (LPWSTR)LocalAlloc( LMEM_FIXED, uLen * sizeof(WCHAR) ); if( NULL == *ppszLdapPath ) return ERROR_NOT_ENOUGH_MEMORY; wcscpy( * ppszLdapPath, L"LDAP://" ); if( pszServerName ) { wcscat( * ppszLdapPath, pszServerName ); wcscat( * ppszLdapPath, L"/"); } wcscat(* ppszLdapPath, pszPath ); return ERROR_SUCCESS; }