//+--------------------------------------------------------------------------- // // Microsoft Windows NT Security // Copyright (C) Microsoft Corporation, 1997 - 1998 // // File: dsctx.cpp // // Contents: Implementation of CDsObjectContext and NT Marta DS object // Functions // // History: 3-31-1999 kirtd Created // //---------------------------------------------------------------------------- #include #pragma hdrstop #include #include //+--------------------------------------------------------------------------- // // Member: CDsObjectContext::CDsObjectContext, public // // Synopsis: Constructor // //---------------------------------------------------------------------------- CDsObjectContext::CDsObjectContext () { m_cRefs = 1; memset( &m_LdapUrlComponents, 0, sizeof( m_LdapUrlComponents ) ); m_pBinding = NULL; } //+--------------------------------------------------------------------------- // // Member: CDsObjectContext::~CDsObjectContext, public // // Synopsis: Destructor // //---------------------------------------------------------------------------- CDsObjectContext::~CDsObjectContext () { LdapFreeBindings( m_pBinding ); LdapFreeUrlComponents( &m_LdapUrlComponents ); assert( m_cRefs == 0 ); } //+--------------------------------------------------------------------------- // // Member: CDsObjectContext::InitializeByName, public // // Synopsis: initialize the context given the name of the lanman share // //---------------------------------------------------------------------------- DWORD CDsObjectContext::InitializeByName (LPCWSTR pObjectName, ACCESS_MASK AccessMask) { DWORD Result = ERROR_SUCCESS; LPWSTR pwszName = NULL; ULONG len = wcslen( pObjectName ); ULONG i, j; if ( _wcsnicmp( pObjectName, LDAP_SCHEME_U, wcslen( LDAP_SCHEME_U ) ) != 0 ) { pwszName = new WCHAR [ len + wcslen( LDAP_SCHEME_UC ) + 2 ]; if ( pwszName != NULL ) { wcscpy( pwszName, LDAP_SCHEME_UC ); wcscat( pwszName, L"/" ); wcscat( pwszName, pObjectName ); } else { Result = ERROR_OUTOFMEMORY; } } else { pwszName = new WCHAR [ len + 1 ]; if ( pwszName != NULL ) { wcscpy( pwszName, pObjectName ); } else { Result = ERROR_OUTOFMEMORY; } } if ( Result == ERROR_SUCCESS ) { for (i = j = 0; i <= len; i++, j++) { if (L'\\' == pwszName[i]) { if (L'/' != pwszName[i+1]) { pwszName[j++] = pwszName[i++]; } else { i++; } } pwszName[j] = pwszName[i]; } } if ( Result == ERROR_SUCCESS ) { if ( LdapCrackUrl( pwszName, &m_LdapUrlComponents ) == FALSE ) { Result = GetLastError(); } } if ( Result == ERROR_SUCCESS ) { if ( LdapGetBindings( m_LdapUrlComponents.pwszHost, m_LdapUrlComponents.Port, 0, 0, &m_pBinding ) == FALSE ) { Result = GetLastError(); } } if ( pwszName != pObjectName ) { delete pwszName; } return( Result ); } //+--------------------------------------------------------------------------- // // Member: CDsObjectContext::AddRef, public // // Synopsis: add a reference to the context // //---------------------------------------------------------------------------- DWORD CDsObjectContext::AddRef () { m_cRefs += 1; return( m_cRefs ); } //+--------------------------------------------------------------------------- // // Member: CDsObjectContext::Release, public // // Synopsis: release a reference to the context // //---------------------------------------------------------------------------- DWORD CDsObjectContext::Release () { m_cRefs -= 1; if ( m_cRefs == 0 ) { delete this; return( 0 ); } return( m_cRefs ); } //+--------------------------------------------------------------------------- // // Member: CDsObjectContext::GetDsObjectProperties, public // // Synopsis: get properties about the context // //---------------------------------------------------------------------------- DWORD CDsObjectContext::GetDsObjectProperties ( PMARTA_OBJECT_PROPERTIES pObjectProperties ) { if ( pObjectProperties->cbSize < sizeof( MARTA_OBJECT_PROPERTIES ) ) { return( ERROR_INVALID_PARAMETER ); } assert( pObjectProperties->dwFlags == 0 ); return( ERROR_SUCCESS ); } //+--------------------------------------------------------------------------- // // Member: CDsObjectContext::GetDsObjectRights, public // // Synopsis: get the DsObject security descriptor // //---------------------------------------------------------------------------- DWORD CDsObjectContext::GetDsObjectRights ( SECURITY_INFORMATION SecurityInfo, PSECURITY_DESCRIPTOR* ppSecurityDescriptor ) { DWORD Result; Result = MartaReadDSObjSecDesc( m_pBinding, m_LdapUrlComponents.pwszDN, SecurityInfo, ppSecurityDescriptor ); return( Result ); } //+--------------------------------------------------------------------------- // // Member: CDsObjectContext::SetDsObjectRights, public // // Synopsis: set the window security descriptor // //---------------------------------------------------------------------------- DWORD CDsObjectContext::SetDsObjectRights ( SECURITY_INFORMATION SecurityInfo, PSECURITY_DESCRIPTOR pSecurityDescriptor ) { DWORD Result; PISECURITY_DESCRIPTOR pisd = NULL; DWORD cb = 0; PSECURITY_DESCRIPTOR psd = NULL; pisd = (PISECURITY_DESCRIPTOR)pSecurityDescriptor; if ( pisd->Control & SE_SELF_RELATIVE ) { cb = GetSecurityDescriptorLength( pSecurityDescriptor ); psd = pSecurityDescriptor; } else { if ( MakeSelfRelativeSD( pSecurityDescriptor, NULL, &cb ) == FALSE ) { if ( cb > 0 ) { psd = new BYTE [ cb ]; if ( psd != NULL ) { if ( MakeSelfRelativeSD( pSecurityDescriptor, psd, &cb ) == FALSE ) { delete psd; return( GetLastError() ); } } else { return( ERROR_OUTOFMEMORY ); } } else { return( GetLastError() ); } } else { assert( FALSE && "Should not get here!" ); return( ERROR_INVALID_PARAMETER ); } } assert( psd != NULL ); Result = MartaStampSD( m_LdapUrlComponents.pwszDN, cb, SecurityInfo, psd, m_pBinding ); if ( psd != pSecurityDescriptor ) { delete psd; } return( Result ); } //+--------------------------------------------------------------------------- // // Member: CDsObjectContext::GetDsObjectGuid, public // // Synopsis: get the object GUID // //---------------------------------------------------------------------------- DWORD CDsObjectContext::GetDsObjectGuid (GUID* pGuid) { return( ERROR_INVALID_PARAMETER ); } // // Functions from Ds.h which dispatch unto the CDsObjectContext class // DWORD MartaAddRefDsObjectContext( IN MARTA_CONTEXT Context ) { return( ( (CDsObjectContext *)Context )->AddRef() ); } DWORD MartaCloseDsObjectContext( IN MARTA_CONTEXT Context ) { return( ( (CDsObjectContext *)Context )->Release() ); } DWORD MartaGetDsObjectProperties( IN MARTA_CONTEXT Context, IN OUT PMARTA_OBJECT_PROPERTIES pProperties ) { return( ( (CDsObjectContext *)Context )->GetDsObjectProperties( pProperties ) ); } DWORD MartaGetDsObjectTypeProperties( IN OUT PMARTA_OBJECT_TYPE_PROPERTIES pProperties ) { if ( pProperties->cbSize < sizeof( MARTA_OBJECT_TYPE_PROPERTIES ) ) { return( ERROR_INVALID_PARAMETER ); } assert( pProperties->dwFlags == 0 ); pProperties->dwFlags = MARTA_OBJECT_TYPE_INHERITANCE_MODEL_PRESENT_FLAG; return( ERROR_SUCCESS ); } DWORD MartaGetDsObjectRights( IN MARTA_CONTEXT Context, IN SECURITY_INFORMATION SecurityInfo, OUT PSECURITY_DESCRIPTOR * ppSecurityDescriptor ) { return( ( (CDsObjectContext *)Context )->GetDsObjectRights( SecurityInfo, ppSecurityDescriptor ) ); } DWORD MartaOpenDsObjectNamedObject( IN LPCWSTR pObjectName, IN ACCESS_MASK AccessMask, OUT PMARTA_CONTEXT pContext ) { DWORD Result; CDsObjectContext* pDsObjectContext; pDsObjectContext = new CDsObjectContext; if ( pDsObjectContext == NULL ) { return( ERROR_OUTOFMEMORY ); } Result = pDsObjectContext->InitializeByName( pObjectName, AccessMask ); if ( Result != ERROR_SUCCESS ) { pDsObjectContext->Release(); return( Result ); } *pContext = pDsObjectContext; return( ERROR_SUCCESS ); } DWORD MartaSetDsObjectRights( IN MARTA_CONTEXT Context, IN SECURITY_INFORMATION SecurityInfo, IN PSECURITY_DESCRIPTOR pSecurityDescriptor ) { return( ( (CDsObjectContext *)Context )->SetDsObjectRights( SecurityInfo, pSecurityDescriptor ) ); } DWORD MartaConvertDsObjectNameToGuid( IN LPCWSTR pObjectName, OUT GUID* pGuid ) { DWORD Result = ERROR_SUCCESS; LPWSTR pwszName = NULL; LDAP_URL_COMPONENTS LdapUrlComponents; DS_NAME_RESULTW* pnameresult; HANDLE hDs = NULL; WCHAR GuidString[ MAX_PATH ]; memset( &LdapUrlComponents, 0, sizeof( LdapUrlComponents ) ); if ( _wcsnicmp( pObjectName, LDAP_SCHEME_U, wcslen( LDAP_SCHEME_U ) ) != 0 ) { pwszName = new WCHAR [ wcslen( pObjectName ) + wcslen( LDAP_SCHEME_U ) + 2 ]; if ( pwszName != NULL ) { wcscpy( pwszName, LDAP_SCHEME_U ); wcscat( pwszName, L"/" ); wcscat( pwszName, pObjectName ); } else { Result = ERROR_OUTOFMEMORY; } } else { pwszName = (LPWSTR)pObjectName; } if ( Result == ERROR_SUCCESS ) { if ( LdapCrackUrl( pwszName, &LdapUrlComponents ) == FALSE ) { Result = GetLastError(); } } if ( Result == ERROR_SUCCESS ) { Result = DsBindW( LdapUrlComponents.pwszHost, NULL, &hDs ); } if ( Result == ERROR_SUCCESS ) { Result = DsCrackNamesW( hDs, DS_NAME_NO_FLAGS, DS_FQDN_1779_NAME, DS_UNIQUE_ID_NAME, 1, &LdapUrlComponents.pwszDN, &pnameresult ); } if ( Result == ERROR_SUCCESS ) { if ( ( pnameresult->cItems > 0 ) && ( pnameresult->rItems[0].status == ERROR_SUCCESS ) ) { Result = IIDFromString( pnameresult->rItems[0].pName, pGuid ); } else { Result = ERROR_INVALID_PARAMETER; } DsFreeNameResultW( pnameresult ); } if ( hDs != NULL ) { DsUnBindW( &hDs ); } LdapFreeUrlComponents( &LdapUrlComponents ); if ( pwszName != pObjectName ) { delete pwszName; } return( Result ); } DWORD MartaConvertGuidToDsName( IN GUID Guid, OUT LPWSTR * ppObjectName ) { DWORD Result; HANDLE hDs = NULL; WCHAR GuidString[ MAX_PATH ]; DS_NAME_RESULTW* pnameresult = NULL; LPWSTR pObjectName = NULL; if ( StringFromGUID2( Guid, GuidString, MAX_PATH ) == 0 ) { return( ERROR_INVALID_PARAMETER ); } Result = DsBindW( NULL, NULL, &hDs ); if ( Result == ERROR_SUCCESS ) { Result = DsCrackNamesW( hDs, DS_NAME_NO_FLAGS, DS_UNIQUE_ID_NAME, DS_FQDN_1779_NAME, 1, (LPCWSTR *)&GuidString, &pnameresult ); } if ( Result == ERROR_SUCCESS ) { if ( ( pnameresult->cItems > 0 ) && ( pnameresult->rItems[0].status == ERROR_SUCCESS ) ) { pObjectName = (LPWSTR)LocalAlloc( LPTR, ( wcslen( pnameresult->rItems[0].pName ) + 1 ) * sizeof( WCHAR ) ); if ( pObjectName != NULL ) { wcscpy( pObjectName, pnameresult->rItems[0].pName ); *ppObjectName = pObjectName; } else { Result = ERROR_OUTOFMEMORY; } } else { Result = ERROR_INVALID_PARAMETER; } DsFreeNameResultW( pnameresult ); } if ( hDs != NULL ) { DsUnBindW( &hDs ); } return( ERROR_SUCCESS ); } //+--------------------------------------------------------------------------- // // Function: MartaReadDSObjSecDesc // // Synopsis: Reads the security descriptor from the specied object via // the open ldap connection // // Arguments: [IN pLDAP] -- The open LDAP connection // [IN SeInfo] -- Parts of the security descriptor to // read. // [IN pwszDSObj] -- The DSObject to get the security // descriptor for // [OUT ppSD] -- Where the security descriptor is // returned // // Returns: ERROR_SUCCESS -- The object is reachable // ERROR_NOT_ENOUGH_MEMORY A memory allocation failed // // Notes: The returned security descriptor must be freed with LocalFree // //---------------------------------------------------------------------------- DWORD MartaReadDSObjSecDesc(IN PLDAP pLDAP, IN LPWSTR pwszObject, IN SECURITY_INFORMATION SeInfo, OUT PSECURITY_DESCRIPTOR *ppSD) { DWORD dwErr = ERROR_SUCCESS; PLDAPMessage pMessage = NULL; LPWSTR rgAttribs[2]; BYTE berValue[8]; // // JohnsonA The BER encoding is current hardcoded. Change this to use // AndyHe's BER_printf package once it's done. // berValue[0] = 0x30; berValue[1] = 0x03; berValue[2] = 0x02; berValue[3] = 0x01; berValue[4] = (BYTE)((ULONG)SeInfo & 0xF); LDAPControlW SeInfoControl = { LDAP_SERVER_SD_FLAGS_OID_W, { 5, (PCHAR)berValue }, TRUE }; PLDAPControlW ServerControls[2] = { &SeInfoControl, NULL }; rgAttribs[0] = SD_PROP_NAME; rgAttribs[1] = NULL; if(dwErr == ERROR_SUCCESS) { dwErr = ldap_search_ext_sW(pLDAP, pwszObject, LDAP_SCOPE_BASE, L"(objectClass=*)", rgAttribs, 0, (PLDAPControlW *)&ServerControls, NULL, NULL, 10000, &pMessage); dwErr = LdapMapErrorToWin32( dwErr ); } if(dwErr == ERROR_SUCCESS) { LDAPMessage *pEntry = NULL; pEntry = ldap_first_entry(pLDAP,pMessage); if(pEntry == NULL) { dwErr = LdapMapErrorToWin32( pLDAP->ld_errno ); if (ERROR_SUCCESS == dwErr) dwErr = ERROR_ACCESS_DENIED; } else { PLDAP_BERVAL *pSize = ldap_get_values_lenW(pLDAP, pMessage, rgAttribs[0]); if(pSize == NULL) { dwErr = LdapMapErrorToWin32( pLDAP->ld_errno ); } else { // // Allocate the security descriptor to return // *ppSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, (*pSize)->bv_len); if(*ppSD == NULL) { dwErr = ERROR_NOT_ENOUGH_MEMORY; } else { memcpy(*ppSD, (PBYTE)(*pSize)->bv_val, (*pSize)->bv_len); } ldap_value_free_len(pSize); } } } if ( pMessage != NULL ) { ldap_msgfree(pMessage); } return(dwErr); } //+--------------------------------------------------------------------------- // // Function: MartaStampSD // // Synopsis: Actually stamps the security descriptor on the object. // // Arguments: [IN pwszObject] -- The object to stamp the SD on // [IN cSDSize] -- The size of the security descriptor // [IN SeInfo] -- SecurityInformation about the security // descriptor // [IN pSD] -- The SD to stamp // [IN pLDAP] -- The LDAP connection to use // // Returns: ERROR_SUCCESS -- Success // ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed // //---------------------------------------------------------------------------- DWORD MartaStampSD(IN LPWSTR pwszObject, IN ULONG cSDSize, IN SECURITY_INFORMATION SeInfo, IN PSECURITY_DESCRIPTOR pSD, IN PLDAP pLDAP) { DWORD dwErr = ERROR_SUCCESS; // // Now, we'll do the write. The security descriptor // we got passed in better not be in the old Ds format, // where the leading 4 bytes are the SECURITY_INFORMATION, which we'll skip // and replace with control information // assert(*(PULONG)pSD > 0xF ); PLDAPModW rgMods[2]; PLDAP_BERVAL pBVals[2]; LDAPModW Mod; LDAP_BERVAL BVal; BYTE ControlBuffer[ 5 ]; LDAPControlW SeInfoControl = { LDAP_SERVER_SD_FLAGS_OID_W, { 5, (PCHAR) &ControlBuffer }, TRUE }; // // !!! Hardcoded for now. Use Andyhe's BER_printf once it's done. // ControlBuffer[0] = 0x30; ControlBuffer[1] = 0x3; ControlBuffer[2] = 0x02; // Denotes an integer; ControlBuffer[3] = 0x01; // Size ControlBuffer[4] = (BYTE)((ULONG)SeInfo & 0xF); PLDAPControlW ServerControls[2] = { &SeInfoControl, NULL }; assert(IsValidSecurityDescriptor( pSD ) ); rgMods[0] = &Mod; rgMods[1] = NULL; pBVals[0] = &BVal; pBVals[1] = NULL; BVal.bv_len = cSDSize; BVal.bv_val = (PCHAR)pSD; Mod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES; Mod.mod_type = SD_PROP_NAME; Mod.mod_values = (LPWSTR *)pBVals; // // Now, we'll do the write... // dwErr = ldap_modify_ext_sW(pLDAP, pwszObject, rgMods, (PLDAPControlW *)&ServerControls, NULL); dwErr = LdapMapErrorToWin32(dwErr); return(dwErr); } DWORD MartaGetDsParentName( IN LPWSTR ObjectName, OUT LPWSTR *pParentName ) /*++ Routine Description: Given the name of a DS object return the name of its parent. The routine allocates memory required to hold the parent name. Arguments: ObjectName - Name of the DS object. pParentName - To return the pointer to the allocated parent name. In case of the root of the tree, we return NULL parent with ERROR_SUCCESS. Return Value: ERROR_SUCCESS in case of success. ERROR_* otherwise --*/ { LPCWSTR pKey = NULL; LPCWSTR pVal = NULL; DWORD ccKey = 0; DWORD ccDN = 0; DWORD ccVal = 0; DWORD Size = 0; DWORD dwErr = ERROR_SUCCESS; LPCWSTR pDN = (LPWSTR) ObjectName; ccDN = wcslen(pDN); *pParentName = NULL; // // The input is empty. There is no parent. Just return. // if (0 == ccDN) { return ERROR_SUCCESS; } // // Do the first pass to get to the next level. At the end of this call, // pDN will point to the next ','. One more call to DsGetRdnW will // return the right result in pKey. // Input: // pDN = "CN=Kedar, DC=NTDEV, DC=Microsoft, DC=com" // Output: // pDN = ", DC=NTDEV, DC=Microsoft, DC=com" // dwErr = DsGetRdnW( &pDN, &ccDN, &pKey, &ccKey, &pVal, &ccVal ); if (ERROR_SUCCESS != dwErr) { return dwErr; } // // This is TRUE when the Object does not have any parent. // if (0 == ccDN) { return ERROR_SUCCESS; } // // Input: // pDN = ", DC=NTDEV, DC=Microsoft, DC=com" // Output: // pKey = "DC=NTDEV, DC=Microsoft, DC=com" // dwErr = DsGetRdnW( &pDN, &ccDN, &pKey, &ccKey, &pVal, &ccVal ); if (ERROR_SUCCESS != dwErr) { return dwErr; } // // We have to distinguish between LDAP://ServerName/ObjectName and // ObjectName. // if (!_wcsnicmp(ObjectName, LDAP_SCHEME_U, wcslen(LDAP_SCHEME_U)) != 0 ) { ULONG HostSize; // // Compute the size of string required to hold "LDAP//ServerName/" in // HostSize. // pDN = ObjectName + sizeof("ldap://"); pDN = wcschr(pDN, L'/'); if (NULL == pDN) { return ERROR_INVALID_PARAMETER; } HostSize = (ULONG) (pDN - ObjectName + 1); Size = (1 + wcslen(pKey) + HostSize) * sizeof(WCHAR); *pParentName = (LPWSTR) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, Size); if (NULL == *pParentName) { return ERROR_NOT_ENOUGH_MEMORY; } // // Copy the name of the parent into allocated memeory. // wcsncpy(*pParentName, ObjectName, HostSize); wcscpy((*pParentName) + HostSize, pKey); } else { Size = (1 + wcslen(pKey)) * sizeof(WCHAR); *pParentName = (LPWSTR) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, Size); if (NULL == *pParentName) { return ERROR_NOT_ENOUGH_MEMORY; } // // Copy the name of the parent into allocated memeory. // wcscpy(*pParentName, pKey); } return ERROR_SUCCESS; }