/*++ Copyright (c) 1997-1999 Microsoft Corporation Module Name: rnduser.cpp Abstract: This module contains implementation of CUser object. Author: Rajeevb, Modification history: Mu Han (muhan) 12-5-1997 --*/ #include "stdafx.h" #include "rnduser.h" const WCHAR * const UserAttributeNames[] = { L"SamAccountName", // ZoltanS: was "cn" -- we need SamAccountName for ntds. L"telephoneNumber", L"IPPhone" }; #define INC_ACCESS_ACL_SIZE(_SIZE_, _SID_) \ _SIZE_ += (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(_SID_)); #define BAIL_ON_BOOLFAIL(_FN_) \ if ( !_FN_ ) \ { \ hr = HRESULT_FROM_WIN32(GetLastError()); \ goto failed; \ } #define ACCESS_READ 0x10 #define ACCESS_WRITE 0x20 #define ACCESS_MODIFY (ACCESS_WRITE | WRITE_DAC) #define ACCESS_DELETE DELETE #define ACCESS_ALL (ACCESS_READ | ACCESS_MODIFY | ACCESS_DELETE) HRESULT ConvertStringToSid( IN PWSTR string, OUT PSID *sid, OUT PDWORD pdwSidSize, OUT PWSTR *end ); HRESULT ConvertACLToVariant( PACL pACL, LPVARIANT pvarACL ); ///////////////////////////////////////////////////////////////////////////// // non-interface class methods ///////////////////////////////////////////////////////////////////////////// HRESULT CUser::Init(BSTR bName) { HRESULT hr; hr = SetSingleValue(UA_USERNAME, bName); BAIL_IF_FAIL(hr, "can't set user name"); hr = SetDefaultSD(); BAIL_IF_FAIL(hr, "Init the security descriptor"); return hr; } HRESULT CUser::GetSingleValueBstr( IN OBJECT_ATTRIBUTE Attribute, OUT BSTR * AttributeValue ) { LOG((MSP_INFO, "CUser::GetSingleValueBstr - entered")); BAIL_IF_BAD_WRITE_PTR(AttributeValue, E_POINTER); if (!ValidUserAttribute(Attribute)) { LOG((MSP_ERROR, "Invalid Attribute, %d", Attribute)); return E_FAIL; } CLock Lock(m_lock); if(!m_Attributes[UserAttrIndex(Attribute)]) { LOG((MSP_ERROR, "Attribute %S is not found", UserAttributeName(Attribute))); return E_FAIL; } *AttributeValue = SysAllocString(m_Attributes[UserAttrIndex(Attribute)]); if (*AttributeValue == NULL) { return E_OUTOFMEMORY; } LOG((MSP_INFO, "CUser::get %S: %S", UserAttributeName(Attribute), *AttributeValue)); return S_OK; } HRESULT CUser::SetSingleValue( IN OBJECT_ATTRIBUTE Attribute, IN WCHAR * AttributeValue ) { LOG((MSP_INFO, "CUser::SetSingleValue - entered")); if (!ValidUserAttribute(Attribute)) { LOG((MSP_ERROR, "Invalid Attribute, %d", Attribute)); return E_FAIL; } if (AttributeValue != NULL) { BAIL_IF_BAD_READ_PTR(AttributeValue, E_POINTER); } CLock Lock(m_lock); if (!m_Attributes[UserAttrIndex(Attribute)].set(AttributeValue)) { LOG((MSP_ERROR, "Can not add attribute %S", UserAttributeName(Attribute))); return E_OUTOFMEMORY; } LOG((MSP_INFO, "CUser::set %S to %S", UserAttributeName(Attribute), AttributeValue)); return S_OK; } /*++ Routine Description: Set the right security descriptor for the conference. Arguments: Return Value: HRESULT. --*/ HRESULT CUser::SetDefaultSD() { LOG((MSP_INFO, "CConference::SetDefaultSD - entered")); // // The security descriptor // IADsSecurityDescriptor* pSecDesc = NULL; HRESULT hr = S_OK; bool bOwner = false, bWorld = false; PACL pACL = NULL; PSID pSidWorld = NULL; DWORD dwAclSize = sizeof(ACL), dwTemp; BSTR bstrTemp = NULL; LPWSTR pszTemp = NULL; HANDLE hToken; UCHAR *pInfoBuffer = NULL; DWORD cbInfoBuffer = 512; // // Try to get the thread or process token // if( !OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken) ) { // // If there was a sever error we exit // if( GetLastError() != ERROR_NO_TOKEN ) { LOG((MSP_ERROR, "CConference::SetDefaultSD - exit E_FAIL " "OpenThreadToken failed!")); return E_FAIL; } // // Attempt to open the process token, since no thread token exists // if( !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) ) { LOG((MSP_ERROR, "CConference::SetDefaultSD - exit E_FAIL " "OpenProcessToken failed")); return E_FAIL; } } // // Loop until we have a large enough structure // while ( (pInfoBuffer = new UCHAR[cbInfoBuffer]) != NULL ) { if ( !GetTokenInformation(hToken, TokenUser, pInfoBuffer, cbInfoBuffer, &cbInfoBuffer) ) { delete pInfoBuffer; pInfoBuffer = NULL; if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER ) return E_FAIL; } else { break; } } CloseHandle(hToken); // // Did we get the owner ACL? // if ( pInfoBuffer ) { INC_ACCESS_ACL_SIZE( dwAclSize, ((PTOKEN_USER) pInfoBuffer)->User.Sid ); bOwner = true; } // // Make SID for "Everyone" // SysReAllocString( &bstrTemp, L"S-1-1-0" ); hr = ConvertStringToSid( bstrTemp, &pSidWorld, &dwTemp, &pszTemp ); if ( SUCCEEDED(hr) ) { INC_ACCESS_ACL_SIZE( dwAclSize, pSidWorld ); bWorld = true; } // // Create a security descriptor // hr = CoCreateInstance( CLSID_SecurityDescriptor, NULL, CLSCTX_INPROC_SERVER, IID_IADsSecurityDescriptor, (void **)&pSecDesc ); if( FAILED(hr) ) { LOG((MSP_ERROR, "CConference::SetDefaultSD - exit 0x%08x " "Create security descriptor failed!", hr)); goto failed; } // // Create the ACL containing the Owner and World ACEs // pACL = (PACL) new BYTE[dwAclSize]; if ( pACL ) { BAIL_ON_BOOLFAIL( InitializeAcl(pACL, dwAclSize, ACL_REVISION) ); // Add World Rights if ( bWorld ) { if ( bOwner ) { BAIL_ON_BOOLFAIL( AddAccessAllowedAce(pACL, ACL_REVISION, ACCESS_READ, pSidWorld) ); } else { BAIL_ON_BOOLFAIL( AddAccessAllowedAce(pACL, ACL_REVISION, ACCESS_ALL , pSidWorld) ); } } // Add Creator rights if ( bOwner ) BAIL_ON_BOOLFAIL( AddAccessAllowedAce(pACL, ACL_REVISION, ACCESS_ALL, ((PTOKEN_USER) pInfoBuffer)->User.Sid) ); // Set the DACL onto our security descriptor VARIANT varDACL; VariantInit( &varDACL ); if ( SUCCEEDED(hr = ConvertACLToVariant((PACL) pACL, &varDACL)) ) { if ( SUCCEEDED(hr = pSecDesc->put_DaclDefaulted(FALSE)) ) { hr = pSecDesc->put_DiscretionaryAcl( V_DISPATCH(&varDACL) ); if( SUCCEEDED(hr) ) { hr = put_SecurityDescriptor((IDispatch*)pSecDesc); if( SUCCEEDED(hr) ) { hr = this->put_SecurityDescriptorIsModified(TRUE); } } } } VariantClear( &varDACL ); } else { hr = E_OUTOFMEMORY; } // Clean up failed: SysFreeString( bstrTemp ); if ( pACL ) delete pACL; if ( pSidWorld ) delete pSidWorld; if ( pInfoBuffer ) delete pInfoBuffer; if( pSecDesc ) pSecDesc->Release(); LOG((MSP_INFO, "CConference::SetDefaultSD - exit 0x%08x", hr)); return hr; } ///////////////////////////////////////////////////////////////////////////// // ITDirectoryObject ///////////////////////////////////////////////////////////////////////////// STDMETHODIMP CUser::get_Name(BSTR * ppVal) { return GetSingleValueBstr(UA_USERNAME, ppVal); } STDMETHODIMP CUser::put_Name(BSTR pVal) { return SetSingleValue(UA_USERNAME, pVal); } STDMETHODIMP CUser::get_DialableAddrs( IN long dwAddressTypes, //defined in tapi.h OUT VARIANT * pVariant ) { BAIL_IF_BAD_WRITE_PTR(pVariant, E_POINTER); HRESULT hr; BSTR *Addresses = new BSTR[1]; // only one for now. BAIL_IF_NULL(Addresses, E_OUTOFMEMORY); switch (dwAddressTypes) { case LINEADDRESSTYPE_DOMAINNAME: hr = GetSingleValueBstr(UA_IPPHONE_PRIMARY, &Addresses[0]); break; case LINEADDRESSTYPE_IPADDRESS: { BSTR pDomainName; DWORD dwIP; hr = GetSingleValueBstr(UA_IPPHONE_PRIMARY, &pDomainName); if ( SUCCEEDED(hr) ) { hr = ResolveHostName(0, pDomainName, NULL, &dwIP); SysFreeString(pDomainName); if ( SUCCEEDED(hr) ) { WCHAR wszIP[20]; ipAddressToStringW(wszIP, dwIP); Addresses[0] = SysAllocString(wszIP); if ( Addresses[0] == NULL ) { hr = E_OUTOFMEMORY; } } } } break; case LINEADDRESSTYPE_PHONENUMBER: hr = GetSingleValueBstr(UA_TELEPHONE_NUMBER, &Addresses[0]); break; default: hr = E_FAIL; break; } DWORD dwCount = (FAILED(hr)) ? 0 : 1; hr = ::CreateBstrCollection(dwCount, // count &Addresses[0], // begin pointer &Addresses[dwCount], // end pointer pVariant, // return value AtlFlagTakeOwnership); // flags // the collection will destroy the Addresses array eventually. // no need to free anything here. Even if we tell it to hand // out zero objects, it will delete the array on construction. // (ZoltanS verified.) return hr; } STDMETHODIMP CUser::EnumerateDialableAddrs( IN DWORD dwAddressTypes, //defined in tapi.h OUT IEnumDialableAddrs ** ppEnumDialableAddrs ) { BAIL_IF_BAD_WRITE_PTR(ppEnumDialableAddrs, E_POINTER); HRESULT hr; BSTR *Addresses = new BSTR[1]; // only one for now. BAIL_IF_NULL(Addresses, E_OUTOFMEMORY); switch (dwAddressTypes) { case LINEADDRESSTYPE_IPADDRESS: { BSTR pDomainName; DWORD dwIP; hr = GetSingleValueBstr(UA_IPPHONE_PRIMARY, &pDomainName); if ( SUCCEEDED(hr) ) { hr = ResolveHostName(0, pDomainName, NULL, &dwIP); SysFreeString(pDomainName); if ( SUCCEEDED(hr) ) { WCHAR wszIP[20]; ipAddressToStringW(wszIP, dwIP); Addresses[0] = SysAllocString(wszIP); if ( Addresses[0] == NULL ) { hr = E_OUTOFMEMORY; } } } } break; case LINEADDRESSTYPE_DOMAINNAME: hr = GetSingleValueBstr(UA_IPPHONE_PRIMARY, &Addresses[0]); break; case LINEADDRESSTYPE_PHONENUMBER: hr = GetSingleValueBstr(UA_TELEPHONE_NUMBER, &Addresses[0]); break; default: hr = E_FAIL; break; } DWORD dwCount = (FAILED(hr)) ? 0 : 1; hr = ::CreateDialableAddressEnumerator( &Addresses[0], &Addresses[dwCount], ppEnumDialableAddrs ); // the enumerator will destroy the Addresses array eventually, // so no need to free anything here. Even if we tell it to hand // out zero objects, it will delete the array on destruction. // (ZoltanS verified.) return hr; } STDMETHODIMP CUser::GetAttribute( IN OBJECT_ATTRIBUTE Attribute, OUT BSTR * ppAttributeValue ) { return GetSingleValueBstr(Attribute, ppAttributeValue); } STDMETHODIMP CUser::SetAttribute( IN OBJECT_ATTRIBUTE Attribute, IN BSTR pAttributeValue ) { return SetSingleValue(Attribute, pAttributeValue); } STDMETHODIMP CUser::GetTTL( OUT DWORD * pdwTTL ) { BAIL_IF_BAD_WRITE_PTR(pdwTTL, E_POINTER); *pdwTTL = 0; return S_OK; } ///////////////////////////////////////////////////////////////////////////// // ITDirectoryObjectUser ///////////////////////////////////////////////////////////////////////////// STDMETHODIMP CUser::get_IPPhonePrimary( OUT BSTR *pVal ) { return GetSingleValueBstr(UA_IPPHONE_PRIMARY, pVal); } STDMETHODIMP CUser::put_IPPhonePrimary( IN BSTR newVal ) { // ZoltanS: we now need to check the BSTR, as the ResolveHostName // call below doesn't check it before using it. // Second argument is the maximum length of string to check -- we want // to check the whole thing, so we say (UINT) -1, which is about 2^32. if ( IsBadStringPtr(newVal, (UINT) -1) ) { LOG((MSP_ERROR, "CUser::put_IPPhonePrimary: bad BSTR")); return E_POINTER; } // ZoltanS: We shouldn't let the user set an IPPhonePrimary value that // doesn't resolve to a known host / IP. Check here. char * pchFullDNSName = NULL; // we don't really care what we get DWORD dwIp = 0; // we don't really care what we get // This is our utility function from rndutil.cpp. HRESULT hr = ResolveHostName(0, newVal, &pchFullDNSName, &dwIp); if (FAILED(hr)) { LOG((MSP_ERROR, "CUser::put_IPPhonePrimary: unresolvable name")); return hr; } // Now actually set it. return SetSingleValue(UA_IPPHONE_PRIMARY, newVal); } typedef IDispatchImpl, &IID_ITDirectoryObjectUser, &LIBID_RENDLib> CTDirObjUser; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++= // // CUser::GetIDsOfNames // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++= STDMETHODIMP CUser::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid ) { LOG((MSP_TRACE, "CUser::GetIDsOfNames[%p] - enter. Name [%S]",this, *rgszNames)); HRESULT hr = DISP_E_UNKNOWNNAME; // // See if the requsted method belongs to the default interface // hr = CTDirObjUser::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); if (SUCCEEDED(hr)) { LOG((MSP_TRACE, "CUser::GetIDsOfNames - found %S on CTDirObjUser", *rgszNames)); rgdispid[0] |= IDISPDIROBJUSER; return hr; } // // If not, then try the CDirectoryObject base class // hr = CDirectoryObject::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); if (SUCCEEDED(hr)) { LOG((MSP_TRACE, "CUser::GetIDsOfNames - found %S on CDirectoryObject", *rgszNames)); rgdispid[0] |= IDISPDIROBJECT; return hr; } LOG((MSP_ERROR, "CUser::GetIDsOfNames[%p] - finish. didn't find %S on our iterfaces",*rgszNames)); return hr; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++= // // CUser::Invoke // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++= STDMETHODIMP CUser::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr ) { LOG((MSP_TRACE, "CUser::Invoke[%p] - enter. dispidMember %lx",this, dispidMember)); HRESULT hr = DISP_E_MEMBERNOTFOUND; DWORD dwInterface = (dispidMember & INTERFACEMASK); // // Call invoke for the required interface // switch (dwInterface) { case IDISPDIROBJUSER: { hr = CTDirObjUser::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr ); LOG((MSP_TRACE, "CUser::Invoke - ITDirectoryObjectUser")); break; } case IDISPDIROBJECT: { hr = CDirectoryObject::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr ); LOG((MSP_TRACE, "CUser::Invoke - ITDirectoryObject")); break; } } // end switch (dwInterface) LOG((MSP_TRACE, "CUser::Invoke[%p] - finish. hr = %lx", hr)); return hr; }