Windows-Server-2003/net/tapi/server/dspub.cpp

2598 lines
63 KiB
C++

/*++ BUILD Version: 0000 // Increment this if a change has global effects
Copyright (c) 2000 - 2002 Microsoft Corporation
Module Name:
dspub.cpp
Abstract:
Src module for tapi server DS publishing
Author:
Xiaohai Zhang (xzhang) 10-March-2000
Revision History:
--*/
#include "windows.h"
#include "objbase.h"
#include "winbase.h"
#include "sddl.h"
#include "iads.h"
#include "activeds.h"
#include "tapi.h"
#include "tspi.h"
#include "utils.h"
#include "client.h"
#include "server.h"
#include "private.h"
#include "tchar.h"
#define SECURITY_WIN32
#include "sspi.h"
#include "secext.h"
#include "psapi.h"
extern "C" {
extern const TCHAR gszRegKeyTelephony[];
extern DWORD gdwTapiSCPTTL;
extern const TCHAR gszRegTapisrvSCPGuid[];
extern CRITICAL_SECTION gSCPCritSec;
}
const TCHAR gszTapisrvBindingInfo[] = TEXT("E{\\pipe\\tapsrv}P{ncacn_np}C{%s}A{%s}S{%s}TTL{%s}");
const TCHAR gszVenderMS[] = TEXT("Microsoft");
const TCHAR gszMSGuid[] = TEXT("937924B8-AA44-11d2-81F1-00C04FB9624E");
const WCHAR gwszTapisrvRDN[] = L"CN=Telephony Service";
const TCHAR gszTapisrvProdName[] = TEXT("Telephony Service");
// gszTapisrvGuid needs to be consistant with remotesp\dslookup.cpp
const TCHAR gszTapisrvGuid[] = TEXT("B1A37774-E3F7-488E-ADBFD4DB8A4AB2E5");
const TCHAR gwszProxyRDN[] = L"cn=TAPI Proxy Server";
const TCHAR gszProxyProdName[] = TEXT("TAPI Proxy Server");
const TCHAR gszProxyGuid[] = TEXT("A2657445-3E27-400B-851A-456C41666E37");
const TCHAR gszRegProxySCPGuid[] = TEXT("PROXYSCPGUID");
typedef struct _PROXY_SCP_ENTRY {
// A valid CLSID requires 38 chars
TCHAR szClsid[40];
// A binding GUID is of format
// LDAP://<GUID={B1A37774-E3F7-488E-ADBFD4DB8A4AB2E5}>
// required size is 38+14=52 chars
TCHAR szObjGuid[56];
// Ref count for this entry
DWORD dwRefCount;
} PROXY_SCP_ENTRY, *PPROXY_SCP_ENTRY;
typedef struct _PROXY_SCPS {
DWORD dwTotalEntries;
DWORD dwUsedEntries;
PROXY_SCP_ENTRY * aEntries;
} PROXY_SCPS, *PPROXY_SCPS;
PROXY_SCPS gProxyScps;
#define MAX_SD 2048
//
// GetTokenUser
//
// Based on hAccessToken, call GetTokenInformation
// to retrieve TokenUser info
//
HRESULT
GetTokenUser (HANDLE hAccessToken, PTOKEN_USER * ppUser)
{
HRESULT hr = S_OK;
DWORD dwInfoSize = 0;
PTOKEN_USER ptuUser = NULL;
DWORD ntsResult;
if (!GetTokenInformation(
hAccessToken,
TokenUser,
NULL,
0,
&dwInfoSize
))
{
ntsResult = GetLastError();
if (ntsResult != ERROR_INSUFFICIENT_BUFFER)
{
hr = HRESULT_FROM_WIN32 (ntsResult);
goto ExitHere;
}
}
ptuUser = (PTOKEN_USER) ServerAlloc (dwInfoSize);
if (ptuUser == NULL)
{
hr = LINEERR_NOMEM;
goto ExitHere;
}
if (!GetTokenInformation(
hAccessToken,
TokenUser,
ptuUser,
dwInfoSize,
&dwInfoSize
))
{
ServerFree (ptuUser);
hr = HRESULT_FROM_WIN32 (GetLastError());
goto ExitHere;
}
*ppUser = ptuUser;
ExitHere:
return hr;
}
//
// IsLocalSystem
//
// This function makes the determination if the given process token
// is running as LocalSystem or LocalService or NetworkService
// Returns S_OK if it is, S_FALSE if it is not LocalSystem.
//
HRESULT
IsLocalSystem(HANDLE hAccessToken)
{
HRESULT hr = S_OK;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
PSID pLocalSid = NULL;
PSID pLocalServiceSid = NULL;
PSID pNetworkServiceSid = NULL;
PTOKEN_USER ptuUser = NULL;
hr = GetTokenUser (hAccessToken, &ptuUser);
if (FAILED(hr))
{
goto ExitHere;
}
if (!AllocateAndInitializeSid (
&NtAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&pLocalSid) ||
!AllocateAndInitializeSid (
&NtAuthority,
1,
SECURITY_LOCAL_SERVICE_RID,
0, 0, 0, 0, 0, 0, 0,
&pLocalServiceSid) ||
!AllocateAndInitializeSid (
&NtAuthority,
1,
SECURITY_NETWORK_SERVICE_RID,
0, 0, 0, 0, 0, 0, 0,
&pNetworkServiceSid)
)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto ExitHere;
}
if (!EqualSid(pLocalSid, ptuUser->User.Sid) &&
!EqualSid(pLocalServiceSid, ptuUser->User.Sid) &&
!EqualSid(pNetworkServiceSid, ptuUser->User.Sid))
{
hr = S_FALSE;
}
ExitHere:
if (NULL != ptuUser)
{
ServerFree (ptuUser);
}
if (NULL != pLocalSid)
{
FreeSid(pLocalSid);
}
if (NULL != pLocalServiceSid)
{
FreeSid (pLocalServiceSid);
}
if (NULL != pNetworkServiceSid)
{
FreeSid (pNetworkServiceSid);
}
return hr;
}
//
// IsCurrentLocalSystem
//
// IsCurrentLocalSystem checks to see if current thread/process
// runs in LocalSystem account
//
HRESULT
IsCurrentLocalSystem ()
{
HRESULT hr = S_OK;
HANDLE hToken = NULL;
if (!OpenThreadToken(
GetCurrentThread(),
TOKEN_QUERY,
FALSE,
&hToken))
{
if(!OpenProcessToken(
GetCurrentProcess(),
TOKEN_QUERY,
&hToken
))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto ExitHere;
}
}
hr = IsLocalSystem (hToken);
CloseHandle (hToken);
ExitHere:
return hr;
}
HRESULT
SetPrivilege(
HANDLE hToken, // token handle
LPCTSTR Privilege, // Privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
HRESULT hr = S_OK;
TOKEN_PRIVILEGES tp;
LUID luid;
TOKEN_PRIVILEGES tpPrevious;
DWORD cbPrevious=sizeof(TOKEN_PRIVILEGES);
DWORD dwErr;
if(!LookupPrivilegeValue( NULL, Privilege, &luid ))
{
hr = HRESULT_FROM_WIN32 (GetLastError ());
goto ExitHere;
}
//
// first pass. get current privilege setting
//
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = 0;
AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
&tpPrevious,
&cbPrevious
);
dwErr = GetLastError ();
if (dwErr != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32 (dwErr);
goto ExitHere;
}
//
// second pass. set privilege based on previous setting
//
tpPrevious.PrivilegeCount = 1;
tpPrevious.Privileges[0].Luid = luid;
if(bEnablePrivilege)
{
tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
}
else
{
tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED &
tpPrevious.Privileges[0].Attributes);
}
AdjustTokenPrivileges(
hToken,
FALSE,
&tpPrevious,
cbPrevious,
NULL,
NULL
);
dwErr = GetLastError ();
if (dwErr != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32 (dwErr);
goto ExitHere;
}
ExitHere:
return hr;
}
HRESULT
SetCurrentPrivilege (
LPCTSTR Privilege, // Privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
HRESULT hr = S_OK;
HANDLE hToken = NULL;
if (!OpenThreadToken(
GetCurrentThread(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
FALSE,
&hToken))
{
if(!OpenProcessToken(
GetCurrentProcess(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
&hToken
))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto ExitHere;
}
}
hr = SetPrivilege(hToken, Privilege, bEnablePrivilege);
ExitHere:
if (hToken)
{
CloseHandle(hToken);
}
return hr;
}
//
// SetSidOnAcl
//
BOOL
SetSidOnAcl(
PSID pSid,
PACL pAclSource,
PACL *pAclDestination,
DWORD AccessMask,
BYTE AceFlags,
BOOL bAddSid
)
{
HRESULT hr = S_OK;
ACL_SIZE_INFORMATION AclInfo;
DWORD dwNewAclSize, dwErr;
LPVOID pAce;
DWORD AceCounter;
//
// If we were given a NULL Acl, just provide a NULL Acl
//
*pAclDestination = NULL;
if(pAclSource == NULL || !IsValidSid(pSid))
{
hr = E_INVALIDARG;
goto ExitHere;
}
if(!GetAclInformation(
pAclSource,
&AclInfo,
sizeof(ACL_SIZE_INFORMATION),
AclSizeInformation
))
{
hr = HRESULT_FROM_WIN32 (GetLastError ());
goto ExitHere;
}
//
// compute size for new Acl, based on addition or subtraction of Ace
//
if(bAddSid)
{
dwNewAclSize=AclInfo.AclBytesInUse +
sizeof(ACCESS_ALLOWED_ACE) +
GetLengthSid(pSid) -
sizeof(DWORD) ;
}
else
{
dwNewAclSize=AclInfo.AclBytesInUse -
sizeof(ACCESS_ALLOWED_ACE) -
GetLengthSid(pSid) +
sizeof(DWORD) ;
}
*pAclDestination = (PACL)ServerAlloc(dwNewAclSize);
if(*pAclDestination == NULL) {
hr = LINEERR_NOMEM;
goto ExitHere;
}
//
// initialize new Acl
//
if(!InitializeAcl(
*pAclDestination,
dwNewAclSize,
ACL_REVISION
))
{
hr = HRESULT_FROM_WIN32 (GetLastError ());
goto ExitHere;
}
//
// if appropriate, add ace representing pSid
//
if(bAddSid)
{
PACCESS_ALLOWED_ACE pNewAce;
if(!AddAccessAllowedAce(
*pAclDestination,
ACL_REVISION,
AccessMask,
pSid
))
{
hr = HRESULT_FROM_WIN32 (GetLastError ());
goto ExitHere;
}
//
// get pointer to ace we just added, so we can change the AceFlags
//
if(!GetAce(
*pAclDestination,
0, // this is the first ace in the Acl
(void**) &pNewAce
))
{
hr = HRESULT_FROM_WIN32 (GetLastError ());
goto ExitHere;
}
pNewAce->Header.AceFlags = AceFlags;
}
//
// copy existing aces to new Acl
//
for(AceCounter = 0 ; AceCounter < AclInfo.AceCount ; AceCounter++) {
//
// fetch existing ace
//
if(!GetAce(pAclSource, AceCounter, &pAce))
{
hr = HRESULT_FROM_WIN32 (GetLastError ());
goto ExitHere;
}
//
// check to see if we are removing the Ace
//
if(!bAddSid) {
//
// we only care about ACCESS_ALLOWED aces
//
if((((PACE_HEADER)pAce)->AceType) == ACCESS_ALLOWED_ACE_TYPE)
{
PSID pTempSid=(PSID)&((PACCESS_ALLOWED_ACE)pAce)->SidStart;
//
// if the Sid matches, skip adding this Sid
//
if(EqualSid(pSid, pTempSid))
{
continue;
}
}
}
//
// append ace to Acl
//
if(!AddAce(
*pAclDestination,
ACL_REVISION,
MAXDWORD, // maintain Ace order
pAce,
((PACE_HEADER)pAce)->AceSize
))
{
hr = HRESULT_FROM_WIN32 (GetLastError ());
goto ExitHere;
}
}
ExitHere:
//
// free memory if an error occurred
//
if(hr) {
if(*pAclDestination != NULL)
{
ServerFree(*pAclDestination);
}
}
return hr;
}
//
// AddSIDToKernelObject()
//
// This function takes a given SID and dwAccess and adds it to a given token.
//
// ** Be sure to restore old kernel object
// ** using call to GetKernelObjectSecurity()
//
HRESULT
AddSIDToKernelObjectDacl(
PSID pSid,
DWORD dwAccess,
HANDLE OriginalToken,
PSECURITY_DESCRIPTOR* ppSDOld)
{
HRESULT hr = S_OK;
PSECURITY_DESCRIPTOR pSD = NULL;
SECURITY_DESCRIPTOR sdNew;
DWORD cbByte = MAX_SD, cbNeeded = 0, dwErr = 0;
PACL pOldDacl = NULL, pNewDacl = NULL;
BOOL fDaclPresent, fDaclDefaulted, fRet = FALSE;
pSD = (PSECURITY_DESCRIPTOR) ServerAlloc(cbByte);
if (NULL == pSD)
{
hr = LINEERR_NOMEM;
goto ExitHere;
}
if (!InitializeSecurityDescriptor(
&sdNew,
SECURITY_DESCRIPTOR_REVISION
))
{
hr = HRESULT_FROM_WIN32 (GetLastError());
goto ExitHere;
}
if (!GetKernelObjectSecurity(
OriginalToken,
DACL_SECURITY_INFORMATION,
pSD,
cbByte,
&cbNeeded
))
{
dwErr = GetLastError();
if (cbNeeded > MAX_SD && dwErr == ERROR_MORE_DATA)
{
ServerFree(pSD);
pSD = (PSECURITY_DESCRIPTOR) ServerAlloc(cbNeeded);
if (NULL == pSD)
{
hr = LINEERR_NOMEM;
goto ExitHere;
}
if (!GetKernelObjectSecurity(
OriginalToken,
DACL_SECURITY_INFORMATION,
pSD,
cbNeeded,
&cbNeeded
))
{
hr = HRESULT_FROM_WIN32 (GetLastError());
goto ExitHere;
}
dwErr = 0;
}
if (dwErr != 0)
{
hr = HRESULT_FROM_WIN32 (dwErr);
goto ExitHere;
}
}
if (!GetSecurityDescriptorDacl(
pSD,
&fDaclPresent,
&pOldDacl,
&fDaclDefaulted
))
{
hr = HRESULT_FROM_WIN32 (GetLastError());
goto ExitHere;
}
hr = SetSidOnAcl(
pSid,
pOldDacl,
&pNewDacl,
dwAccess,
0,
TRUE
);
if (hr)
{
goto ExitHere;
}
if (!SetSecurityDescriptorDacl(
&sdNew,
TRUE,
pNewDacl,
FALSE
))
{
hr = HRESULT_FROM_WIN32 (GetLastError());
goto ExitHere;
}
if (!SetKernelObjectSecurity(
OriginalToken,
DACL_SECURITY_INFORMATION,
&sdNew
))
{
hr = HRESULT_FROM_WIN32 (GetLastError());
goto ExitHere;
}
*ppSDOld = pSD;
ExitHere:
if (NULL != pNewDacl)
{
ServerFree(pNewDacl);
}
if (hr)
{
if (NULL != pSD)
{
ServerFree(pSD);
*ppSDOld = NULL;
}
}
return hr;
}
//
// SetTokenDefaultDacl
//
// This function makes pSidUser and LocalSystem account
// have full access in the default DACL of the access token
// this is necessary for CreateThread to succeed without
// an assert in checked build
//
HRESULT
SetTokenDefaultDacl(HANDLE hAccessToken, PSID pSidUser)
{
HRESULT hr = S_OK;
SID_IDENTIFIER_AUTHORITY IDAuthorityNT = SECURITY_NT_AUTHORITY;
PSID pLocalSid = NULL;
TOKEN_DEFAULT_DACL defDACL = {0};
DWORD cbDACL;
if (!AllocateAndInitializeSid(
&IDAuthorityNT,
1,
SECURITY_LOCAL_SYSTEM_RID,
0,0,0,0,0,0,0,
&pLocalSid
))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto ExitHere;
}
cbDACL = sizeof(ACL) +
sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid (pLocalSid) +
sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid (pSidUser);
defDACL.DefaultDacl = (PACL) ServerAlloc (cbDACL);
if (defDACL.DefaultDacl == NULL)
{
hr = LINEERR_NOMEM;
goto ExitHere;
}
if (!InitializeAcl (
defDACL.DefaultDacl,
cbDACL,
ACL_REVISION
))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto ExitHere;
}
if (!AddAccessAllowedAce (
defDACL.DefaultDacl,
ACL_REVISION,
GENERIC_ALL,
pLocalSid
) ||
!AddAccessAllowedAce (
defDACL.DefaultDacl,
ACL_REVISION,
GENERIC_ALL,
pSidUser
))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto ExitHere;
}
if (!SetTokenInformation (
hAccessToken,
TokenDefaultDacl,
&defDACL,
sizeof(TOKEN_DEFAULT_DACL)
))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto ExitHere;
}
ExitHere:
if (NULL != pLocalSid)
{
FreeSid(pLocalSid);
}
if (defDACL.DefaultDacl != NULL)
{
ServerFree (defDACL.DefaultDacl);
}
return hr;
}
//
// GetLocalSystemToken
//
// This function grabs a process token from a LocalSystem process and uses it
// to impersonate when ncessary
//
HRESULT
GetLocalSystemToken(HANDLE* phRet)
{
HRESULT hr = S_OK;
DWORD rgDefPIDs[128];
DWORD * rgPIDs = rgDefPIDs;
DWORD cbPIDs = sizeof(rgDefPIDs), cbNeeded;
DWORD i;
HANDLE hProcess = NULL;
HANDLE hPToken = NULL, hPDupToken = NULL, hPTokenNew = NULL;
HANDLE hToken;
PTOKEN_USER ptuUser = NULL;
BOOL fSet = FALSE;
PSECURITY_DESCRIPTOR pSD = NULL;
//
// Set up necessary privilege for follow-on security operation
//
if(hr = SetCurrentPrivilege(SE_DEBUG_NAME, TRUE))
{
goto ExitHere;
}
if(hr = SetCurrentPrivilege(SE_TAKE_OWNERSHIP_NAME, TRUE))
{
goto ExitHere;
}
//
// Get the current thread/process token user info
//
if (!OpenThreadToken(
GetCurrentThread(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
FALSE,
&hToken))
{
if(!OpenProcessToken(
GetCurrentProcess(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
&hToken
))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto ExitHere;
}
}
hr = GetTokenUser (hToken, &ptuUser);
CloseHandle (hToken);
if (hr)
{
goto ExitHere;
}
//
// Get the list of process IDs in the system
//
while (1)
{
if (!EnumProcesses (
rgPIDs,
cbPIDs,
&cbNeeded
))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto ExitHere;
}
// Break out if we have large enough buf
if (cbNeeded < cbPIDs)
{
break;
}
// Otherwise, alloc larger buffer
if (rgPIDs != rgDefPIDs)
{
ServerFree (rgPIDs);
}
cbPIDs += 256;
rgPIDs = (DWORD *)ServerAlloc (cbPIDs);
if (rgPIDs == NULL)
{
hr = LINEERR_NOMEM;
goto ExitHere;
}
}
//
// Walk processes until we find one that's running as
// local system
//
for (i = 1; i < (cbNeeded / sizeof(DWORD)); i++)
{
hProcess = OpenProcess(
PROCESS_ALL_ACCESS,
FALSE,
rgPIDs[i]
);
if (NULL == hProcess)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto ExitHere;
}
if (!OpenProcessToken(
hProcess,
READ_CONTROL | WRITE_DAC,
&hPToken
))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto ExitHere;
}
//
// We have got the process token, but in general
// we do not have TOKEN_DUPLICATE access. So we
// go ahead and whack the DACL of the object to
// grant us the access right.
// IMPORTANT: need to restore the original SD
//
if (hr = AddSIDToKernelObjectDacl(
ptuUser->User.Sid,
TOKEN_DUPLICATE,
hPToken,
&pSD
))
{
goto ExitHere;
}
fSet = TRUE;
if (!OpenProcessToken(
hProcess,
TOKEN_DUPLICATE,
&hPTokenNew
))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto ExitHere;
}
//
// Duplicate the token
//
if (!DuplicateTokenEx(
hPTokenNew,
TOKEN_ALL_ACCESS,
NULL,
SecurityImpersonation,
TokenPrimary,
&hPDupToken
))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto ExitHere;
}
if (IsLocalSystem (hPDupToken) == S_OK &&
SetTokenDefaultDacl (
hPDupToken,
ptuUser->User.Sid) == S_OK)
{
break;
}
//
// Loop cleanup
//
if (!SetKernelObjectSecurity(
hPToken,
DACL_SECURITY_INFORMATION,
pSD
))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto ExitHere;
}
fSet = FALSE;
if (hPDupToken)
{
CloseHandle (hPDupToken);
hPDupToken = NULL;
}
if (hPTokenNew)
{
CloseHandle (hPTokenNew);
hPTokenNew = NULL;
}
if (pSD != NULL)
{
ServerFree (pSD);
pSD = NULL;
}
if (hPToken)
{
CloseHandle (hPToken);
hPToken = NULL;
}
if (hProcess)
{
CloseHandle (hProcess);
hProcess = NULL;
}
}
if (i >= cbNeeded / sizeof(DWORD))
{
hr = S_FALSE;
}
ExitHere:
if (fSet)
{
if (!SetKernelObjectSecurity(
hPToken,
DACL_SECURITY_INFORMATION,
pSD
))
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
if (hPTokenNew)
{
CloseHandle (hPTokenNew);
}
if (hPToken)
{
CloseHandle (hPToken);
}
if (pSD != NULL)
{
ServerFree (pSD);
}
if (hProcess)
{
CloseHandle (hProcess);
}
if (rgPIDs != rgDefPIDs)
{
ServerFree (rgPIDs);
}
if (ptuUser)
{
ServerFree (ptuUser);
}
if (hr)
{
*phRet = NULL;
if (hPDupToken)
{
CloseHandle (hPDupToken);
}
}
else
{
*phRet = hPDupToken;
}
return hr;
}
//
// ImpersonateLocalSystem
//
HRESULT ImpersonateLocalSystem ()
{
HRESULT hr = S_OK;
HANDLE hTokenLocalSys;
hr = GetLocalSystemToken (&hTokenLocalSys);
if (FAILED (hr))
{
goto ExitHere;
}
if (!ImpersonateLoggedOnUser(
hTokenLocalSys
))
{
hr = HRESULT_FROM_WIN32 (GetLastError ());
goto ExitHere;
}
ExitHere:
if (hTokenLocalSys)
{
CloseHandle (hTokenLocalSys);
}
return hr;
}
//
// RevertLocalSystemImp
//
// Revert the LocalSystem account impersonation
//
HRESULT RevertLocalSystemImp ()
{
HRESULT hr;
if (!RevertToSelf())
{
hr = HRESULT_FROM_WIN32 (GetLastError ());
}
else
{
hr = S_OK;
}
return hr;
}
//
// AllowAccessToScpProperties
//
// The ACEs grant read/write access to the computer account
// under which the TAPI service instance will be running
//
HRESULT AllowAccessToScpProperties(
IADs *pSCPObject // IADs pointer to the SCP object.
)
{
HRESULT hr = S_OK;
VARIANT varSD;
LPOLESTR szAttribute = L"nTSecurityDescriptor";
DWORD dwLen;
IADsSecurityDescriptor *pSD = NULL;
IADsAccessControlList *pACL = NULL;
IDispatch *pDisp = NULL;
IADsAccessControlEntry *pACE1 = NULL;
IADsAccessControlEntry *pACE2 = NULL;
IDispatch *pDispACE = NULL;
long lFlags = 0L;
SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY;
PSID pSid = NULL;
LPOLESTR szTrustee = NULL;
SID_IDENTIFIER_AUTHORITY siaAll = SECURITY_WORLD_SID_AUTHORITY;
PSID pSidAll = NULL;
LPOLESTR szTrusteeAll = NULL;
//
// Give tapi server service logon account full control
//
if(!AllocateAndInitializeSid(
&sia,
1,
SECURITY_SERVICE_RID,
0, 0, 0, 0, 0, 0, 0,
&pSid
) ||
!ConvertSidToStringSidW (pSid, &szTrustee))
{
hr = HRESULT_FROM_NT(GetLastError());
goto ExitHere;
}
//
// Give everyone read access
//
if(!AllocateAndInitializeSid(
&siaAll,
1,
SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&pSidAll
) ||
!ConvertSidToStringSidW (pSidAll, &szTrusteeAll))
{
hr = HRESULT_FROM_NT(GetLastError());
goto ExitHere;
}
//
// Now get the nTSecurityDescriptor
//
VariantClear(&varSD);
hr = pSCPObject->Get(szAttribute, &varSD);
if (FAILED(hr) || (varSD.vt!=VT_DISPATCH)) {
LOG((TL_ERROR, "Get nTSecurityDescriptor failed: 0x%x\n", hr));
goto ExitHere;
}
//
// Use the V_DISPATCH macro to get the IDispatch pointer from VARIANT
// structure and QueryInterface for an IADsSecurityDescriptor pointer.
//
hr = V_DISPATCH( &varSD )->QueryInterface(
IID_IADsSecurityDescriptor,
(void**)&pSD
);
if (FAILED(hr)) {
LOG((TL_ERROR, "Couldn't get IADsSecurityDescriptor: 0x%x\n", hr));
goto ExitHere;
}
// Get an IADsAccessControlList pointer to the security descriptor's DACL.
hr = pSD->get_DiscretionaryAcl(&pDisp);
if (SUCCEEDED(hr))
hr = pDisp->QueryInterface(IID_IADsAccessControlList,(void**)&pACL);
if (FAILED(hr)) {
LOG((TL_ERROR, "Couldn't get DACL: 0x%x\n", hr));
goto ExitHere;
}
// Create the COM object for the first ACE.
hr = CoCreateInstance(
CLSID_AccessControlEntry,
NULL,
CLSCTX_INPROC_SERVER,
IID_IADsAccessControlEntry,
(void **)&pACE1
);
// Create the COM object for the second ACE.
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(
CLSID_AccessControlEntry,
NULL,
CLSCTX_INPROC_SERVER,
IID_IADsAccessControlEntry,
(void **)&pACE2
);
}
if (FAILED(hr)) {
LOG((TL_ERROR, "Couldn't create ACEs: 0x%x\n", hr));
goto ExitHere;
}
//
// Set the properties of the two ACEs.
//
// Set the trustee
hr = pACE1->put_Trustee( szTrustee );
hr = pACE2->put_Trustee( szTrusteeAll );
//
// Set the access rights
//
// Full access for service logon account
hr = pACE1->put_AccessMask(
ADS_RIGHT_DELETE | ADS_RIGHT_READ_CONTROL |
ADS_RIGHT_WRITE_DAC | ADS_RIGHT_WRITE_OWNER |
ADS_RIGHT_SYNCHRONIZE | ADS_RIGHT_ACCESS_SYSTEM_SECURITY |
ADS_RIGHT_GENERIC_READ | ADS_RIGHT_GENERIC_WRITE |
ADS_RIGHT_GENERIC_EXECUTE | ADS_RIGHT_GENERIC_ALL |
ADS_RIGHT_DS_CREATE_CHILD | ADS_RIGHT_DS_DELETE_CHILD |
ADS_RIGHT_ACTRL_DS_LIST | ADS_RIGHT_DS_SELF |
ADS_RIGHT_DS_READ_PROP | ADS_RIGHT_DS_WRITE_PROP |
ADS_RIGHT_DS_DELETE_TREE | ADS_RIGHT_DS_LIST_OBJECT |
ADS_RIGHT_DS_CONTROL_ACCESS
);
// Read access for everyone
hr = pACE2->put_AccessMask(
ADS_RIGHT_DS_READ_PROP | ADS_RIGHT_READ_CONTROL |
ADS_RIGHT_GENERIC_READ | ADS_RIGHT_ACTRL_DS_LIST |
ADS_RIGHT_DS_LIST_OBJECT
);
// Set the ACE type.
hr = pACE1->put_AceType( ADS_ACETYPE_ACCESS_ALLOWED_OBJECT );
hr = pACE2->put_AceType( ADS_ACETYPE_ACCESS_ALLOWED_OBJECT );
// Set AceFlags to zero because ACE is not inheritable.
hr = pACE1->put_AceFlags( 0 );
hr = pACE2->put_AceFlags( 0 );
// Set Flags to indicate an ACE that protects a specified object.
hr = pACE1->put_Flags( 0 );
hr = pACE2->put_Flags( 0 );
// Set ObjectType to the schemaIDGUID of the attribute.
hr = pACE1->put_ObjectType( NULL );
hr = pACE2->put_ObjectType( NULL );
// Add the ACEs to the DACL. Need an IDispatch pointer for each ACE
// to pass to the AddAce method.
hr = pACE1->QueryInterface(IID_IDispatch,(void**)&pDispACE);
if (SUCCEEDED(hr))
{
hr = pACL->AddAce(pDispACE);
}
if (FAILED(hr)) {
LOG((TL_ERROR, "Couldn't add first ACE: 0x%x\n", hr));
goto ExitHere;
}
else
{
if (pDispACE)
pDispACE->Release();
pDispACE = NULL;
}
// Do it again for the second ACE.
hr = pACE2->QueryInterface(IID_IDispatch, (void**)&pDispACE);
if (SUCCEEDED(hr))
{
hr = pACL->AddAce(pDispACE);
}
if (FAILED(hr)) {
LOG((TL_ERROR, "Couldn't add second ACE: 0x%x\n", hr));
goto ExitHere;
}
// Write the modified DACL back to the security descriptor.
hr = pSD->put_DiscretionaryAcl(pDisp);
if (SUCCEEDED(hr))
{
// Write the ntSecurityDescriptor property to the property cache.
hr = pSCPObject->Put(szAttribute, varSD);
if (SUCCEEDED(hr))
{
// SetInfo updates the SCP object in the directory.
hr = pSCPObject->SetInfo();
}
}
ExitHere:
if (pDispACE)
pDispACE->Release();
if (pACE1)
pACE1->Release();
if (pACE2)
pACE2->Release();
if (pACL)
pACL->Release();
if (pDisp)
pDisp->Release();
if (pSD)
pSD->Release();
if (szTrustee)
LocalFree (szTrustee);
if (pSid)
FreeSid (pSid);
if (szTrusteeAll)
LocalFree (szTrusteeAll);
if (pSidAll)
FreeSid (pSidAll);
VariantClear(&varSD);
return hr;
}
/**********************************************************
* SCP Creation
*********************************************************/
//
// CreateSCP
//
// Creates a server Service Connection Point object
// under the local host computer object
//
// Parameters:
// wszRDN - RDN
// szProductName - A member of "keywords" property
// szProductGuid - A member of "keywords" property
// szExtraKey - An extra member of "keywords" property
// szBindingInfo - value of property "serviceBindingInformation"
// szObjGuidVlueName
// - The value name to store the SCP object GUID
// under HKLM\Software\Microsoft\Windows\
// CurrentVersion\Telephony\
// if this value is NULL, we don't cache it in registry
// ppBindByGuidStr - For returning the SCP object GUID in the
// format of LPTSTR, if this is NULL, the GUID is
// not returned
//
HRESULT CreateSCP (
LPWSTR wszRDN,
LPTSTR szProductName,
LPTSTR szProductGuid,
LPTSTR szExtraKey,
LPTSTR szBindingInfo,
LPTSTR szObjGuidValueName,
LPTSTR * ppBindByGuidStr
)
{
DWORD dwStat, dwAttr, dwLen;
HRESULT hr = S_OK;
IDispatch *pDisp = NULL; // returned dispinterface of new object
IDirectoryObject *pComp = NULL; // Computer object; parent of SCP
IADs *pIADsSCP = NULL; // IADs interface on new object
BOOL bCoInited = FALSE;
BOOL bRevert = FALSE;
//
// Values for SCPs keywords attribute. Tapisrv product GUID is defined
// in server.h, vendor GUID is from MSDN
//
DWORD dwNumKeywords = 4;
TCHAR *KwVal[5]={
(LPTSTR) gszMSGuid, // Vendor GUID
(LPTSTR) szProductGuid, // Product GUID
(LPTSTR) gszVenderMS, // Vendor Name
(LPTSTR) szProductName, // Product Name
NULL
};
if (szExtraKey != NULL && szExtraKey[0] != 0)
{
KwVal[4] = szExtraKey;
++dwNumKeywords;
}
TCHAR szServer[MAX_PATH];
TCHAR szBinding[128];
TCHAR szDn[MAX_PATH];
TCHAR szAdsPath[MAX_PATH];
HKEY hReg = NULL;
DWORD dwDisp;
ADSVALUE cn,objclass,keywords[5],binding,
classname,dnsname,nametype;
//
// SCP attributes to set during creation of SCP.
//
ADS_ATTR_INFO ScpAttribs[] = {
{TEXT("cn"), ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, &cn, 1},
{TEXT("objectClass"), ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING,
&objclass, 1},
{TEXT("keywords"), ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING,
keywords, dwNumKeywords},
{TEXT("serviceDNSName"), ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING,
&dnsname, 1},
{TEXT("serviceDNSNameType"), ADS_ATTR_UPDATE,ADSTYPE_CASE_IGNORE_STRING,
&nametype, 1},
{TEXT("serviceClassName"), ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING,
&classname, 1},
{TEXT("serviceBindingInformation"), ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING,
&binding, 1},
};
// A binding GUID is of format
// LDAP:<GUID=B1A37774-E3F7-488E-ADBFD4DB8A4AB2E5>
BSTR bstrGuid = NULL;
TCHAR szBindByGuidStr[64];
//
// Do CoInitializeEx
//
hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
if (FAILED (hr))
{
goto ExitHere;
}
bCoInited = TRUE;
//
// Do all the operation in LocalSystem account
//
if (IsCurrentLocalSystem () != S_OK)
{
hr = ImpersonateLocalSystem ();
if (hr)
{
goto ExitHere;
}
bRevert = TRUE;
}
//
// Get the DNS name of the local computer
//
dwLen = sizeof(szServer);
if (!GetComputerNameEx(
ComputerNameDnsFullyQualified,
szServer,
&dwLen
))
{
hr = HRESULT_FROM_NT(GetLastError());
LOG((TL_ERROR, "GetComputerNameEx: %s\n", szServer));
goto ExitHere;
}
//
// Fill in the attribute values to be stored in the SCP.
//
cn.dwType = ADSTYPE_CASE_IGNORE_STRING;
cn.CaseIgnoreString = wszRDN + 3; // 3 is the size of "CN="
objclass.dwType = ADSTYPE_CASE_IGNORE_STRING;
objclass.CaseIgnoreString = TEXT("serviceConnectionPoint");
keywords[0].dwType = ADSTYPE_CASE_IGNORE_STRING;
keywords[1].dwType = ADSTYPE_CASE_IGNORE_STRING;
keywords[2].dwType = ADSTYPE_CASE_IGNORE_STRING;
keywords[3].dwType = ADSTYPE_CASE_IGNORE_STRING;
keywords[4].dwType = ADSTYPE_CASE_IGNORE_STRING;
keywords[0].CaseIgnoreString=KwVal[0];
keywords[1].CaseIgnoreString=KwVal[1];
keywords[2].CaseIgnoreString=KwVal[2];
keywords[3].CaseIgnoreString=KwVal[3];
keywords[4].CaseIgnoreString=KwVal[4];
dnsname.dwType = ADSTYPE_CASE_IGNORE_STRING;
dnsname.CaseIgnoreString = szServer;
nametype.dwType = ADSTYPE_CASE_IGNORE_STRING;
nametype.CaseIgnoreString = TEXT("A");
classname.dwType = ADSTYPE_CASE_IGNORE_STRING;
classname.CaseIgnoreString = szProductName;
binding.dwType = ADSTYPE_CASE_IGNORE_STRING;
binding.CaseIgnoreString = szBindingInfo;
//
// Get the distinguished name of the computer object for the local computer
//
dwLen = sizeof(szDn);
if (!GetComputerObjectName(NameFullyQualifiedDN, szDn, &dwLen))
{
hr = HRESULT_FROM_NT(GetLastError());
LOG((TL_ERROR, "GetComputerObjectName: %s\n", szDn));
goto ExitHere;
}
//
// Compose the ADSpath and bind to the computer object for the local computer
//
_tcscpy(szAdsPath,TEXT("LDAP://"));
_tcscat(szAdsPath,szDn);
hr = ADsGetObject(szAdsPath, IID_IDirectoryObject, (void **)&pComp);
if (FAILED(hr)) {
LOG((TL_ERROR, "Failed to bind Computer Object.",hr));
goto ExitHere;
}
//*******************************************************************
// Publish the SCP as a child of the computer object
//*******************************************************************
// Figure out attribute count.
dwAttr = sizeof(ScpAttribs)/sizeof(ADS_ATTR_INFO);
// Do the Deed!
hr = pComp->CreateDSObject(
wszRDN,
ScpAttribs,
dwAttr,
&pDisp
);
if (FAILED(hr)) {
LOG((TL_ERROR, "Failed to create SCP: 0x%x\n", hr));
if (HRESULT_CODE(hr) == ERROR_OBJECT_ALREADY_EXISTS)
{
hr = HRESULT_FROM_NT (TAPIERR_SCP_ALREADY_EXISTS);
}
goto ExitHere;
}
// Query for an IADs pointer on the SCP object.
hr = pDisp->QueryInterface(IID_IADs,(void **)&pIADsSCP);
if (FAILED(hr)) {
LOG((TL_ERROR, "Failed to QI for IADs: 0x%x\n",hr));
goto ExitHere;
}
// Set ACEs on SCP so service can modify it.
hr = AllowAccessToScpProperties(
pIADsSCP // IADs pointer to the SCP object.
);
if (FAILED(hr)) {
LOG((TL_ERROR, "Failed to set ACEs on SCP DACL: 0x%x\n", hr));
goto ExitHere;
}
// Retrieve the SCP's objectGUID in format suitable for binding.
hr = pIADsSCP->get_GUID(&bstrGuid);
if (FAILED(hr)) {
LOG((TL_ERROR, "Failed to get GUID: 0x%x\n", hr));
goto ExitHere;
}
// Build a string for binding to the object by GUID
_tcscpy(szBindByGuidStr, TEXT("LDAP://<GUID="));
_tcscat(szBindByGuidStr, bstrGuid);
_tcscat(szBindByGuidStr, TEXT(">"));
LOG((TL_INFO, "GUID binding string: %S\n", szBindByGuidStr));
// Set the returning BindByGuidStr if any
if (ppBindByGuidStr)
{
*ppBindByGuidStr = (LPTSTR) ServerAlloc (
(_tcslen (szBindByGuidStr) + 1) * sizeof(TCHAR)
);
if (*ppBindByGuidStr == NULL)
{
hr = LINEERR_NOMEM;
goto ExitHere;
}
_tcscpy (*ppBindByGuidStr, szBindByGuidStr);
}
// Create a registry key under
// HKEY_LOCAL_MACHINE\SOFTWARE\Vendor\Product.
if (szObjGuidValueName)
{
dwStat = RegCreateKeyEx(
HKEY_LOCAL_MACHINE,
gszRegKeyTelephony,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE,
NULL,
&hReg,
&dwDisp);
if (dwStat != NO_ERROR) {
hr = HRESULT_FROM_NT(GetLastError());
LOG((TL_ERROR, "RegCreateKeyEx failed: 0x%x\n", hr));
return hr;
}
// Cache the GUID binding string under the registry key.
dwStat = RegSetValueEx(
hReg,
szObjGuidValueName,
0,
REG_SZ,
(const BYTE *)szBindByGuidStr,
sizeof(TCHAR)*(_tcslen(szBindByGuidStr))
);
if (dwStat != NO_ERROR) {
hr = HRESULT_FROM_NT(GetLastError());
LOG((TL_ERROR, "RegSetValueEx failed: 0x%x\n", hr));
// goto ExitHere;
}
}
ExitHere:
if (pDisp)
{
pDisp->Release();
}
if (pIADsSCP)
{
pIADsSCP->Release();
}
if (hReg)
{
RegCloseKey(hReg);
}
if (bstrGuid)
{
SysFreeString (bstrGuid);
}
if (pComp)
{
if (FAILED(hr))
{
pComp->DeleteDSObject (wszRDN);
}
pComp->Release();
}
if (bRevert)
{
RevertLocalSystemImp ();
}
if (bCoInited)
{
CoUninitialize ();
}
return hr;
}
//
// CreateTapiSCP
//
// Creates the TAPI server Service Connection Point object
// under the local host computer object
//
// Parameters:
// pGuidAssoc - GUID of the line/user association objecct GUID
// currently NULL
// pGuidCluster - The cluster object GUID this server belonging to
//
HRESULT CreateTapiSCP (
GUID * pGuidAssoc,
GUID * pGuidCluster
)
{
DWORD dwStat, dwAttr, dwLen;
HRESULT hr = S_OK;
TCHAR szGUIDCluster[40];
TCHAR szGUIDAssoc[40];
TCHAR szBinding[128];
EnterCriticalSection(&gSCPCritSec);
// Construct the binding information
if (pGuidCluster != NULL)
{
StringFromGUID2 (
*pGuidCluster,
szGUIDCluster,
sizeof(szGUIDCluster) / sizeof(TCHAR)
);
}
else
{
szGUIDCluster[0] = 0;
}
if (pGuidAssoc != NULL)
{
StringFromGUID2 (
*pGuidAssoc,
szGUIDAssoc,
sizeof(szGUIDAssoc) / sizeof(TCHAR)
);
}
else
{
szGUIDAssoc[0] = 0;
}
wsprintf(
szBinding,
gszTapisrvBindingInfo,
szGUIDCluster,
szGUIDAssoc,
TEXT("Inactive"),
TEXT("")
);
hr = CreateSCP (
(LPWSTR) gwszTapisrvRDN,
(LPTSTR) gszTapisrvProdName,
(LPTSTR) gszTapisrvGuid,
(pGuidCluster == NULL) ? NULL : ((LPTSTR)szGUIDCluster),
(LPTSTR) szBinding,
(LPTSTR) gszRegTapisrvSCPGuid,
NULL
);
LeaveCriticalSection(&gSCPCritSec);
return hr;
}
//
// CreateProxySCP
//
// Creates the TAPI proxy server Service Connection Point object
// under the local host computer object
//
// Parameters:
// szClsid - class ID of the proxy server object for DCOM invokation
// ppBindByGuidStr
// - where to return the BindByGuid string pointer
//
HRESULT CreateProxySCP (
LPTSTR szClsid,
LPTSTR * ppBindByGuidStr
)
{
HRESULT hr = S_OK;
// RDN size include "cn=TAPI Proxy Server" + szClsid(38ch)
WCHAR wszRDN[128];
WCHAR *psz;
wcscpy (wszRDN, gwszProxyRDN);
wcscat (wszRDN, L"{");
psz = wszRDN + wcslen(wszRDN);
#ifndef UNICODE
if (MultiByteToWideChar (
CP_ACP,
MB_PRECOMPOSED,
szClsid,
-1,
psz,
(sizeof(wszRDN) - sizeof(gwszProxyRDN)) / sizeof(WCHAR) - 3
// 3 is to compensate for the two brace
) == 0)
{
hr = HRESULT_FROM_NT(GetLastError());
goto ExitHere;
}
#else
wcscat (wszRDN, szClsid);
#endif
wcscat (wszRDN, L"}");
hr = CreateSCP (
(LPWSTR) wszRDN,
(LPTSTR) gszProxyProdName,
(LPTSTR) gszProxyGuid,
NULL,
(LPTSTR) szClsid,
NULL,
ppBindByGuidStr
);
return hr;
}
/**********************************************************
* SCP Update
*********************************************************/
//
// UpdateSCP
//
// Update a general SCP properties when necessary to keep every
// piece ofthe information up to date. The following will be checked:
// 1. Check the serviceDNSName property against current computer
// DNS name to ensure consistancy
// 2. Check the binding information with the information given
//
// Parameters:
// szAdsPath - The ADs path of the SCP object
// szBinding - The binding information to compare with
//
HRESULT UpdateSCP (
LPTSTR szAdsPath,
LPTSTR szBinding
)
{
HRESULT hr = S_OK;
DWORD dwAttrs;
int i;
ADSVALUE dnsname,binding;
DWORD dwLen;
BOOL bUpdate=FALSE;
BOOL bCoInited = FALSE;
BOOL bRevert = FALSE;
IDirectoryObject *pObj = NULL;
PADS_ATTR_INFO pAttribs = NULL;
TCHAR szServer[MAX_PATH];
TCHAR *pszAttrs[]={
TEXT("serviceDNSName"),
TEXT("serviceBindingInformation")
};
ADS_ATTR_INFO Attribs[]={
{TEXT("serviceDNSName"),ADS_ATTR_UPDATE,ADSTYPE_CASE_IGNORE_STRING,&
dnsname,1},
{TEXT("serviceBindingInformation"),ADS_ATTR_UPDATE,
ADSTYPE_CASE_IGNORE_STRING,&binding,1},
};
//
// Do CoInitializeEx
//
hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
if (FAILED (hr))
{
goto ExitHere;
}
bCoInited = TRUE;
//
// Do all the operation in LocalSystem account
//
if (IsCurrentLocalSystem() != S_OK)
{
hr = ImpersonateLocalSystem ();
if (hr)
{
goto ExitHere;
}
bRevert = TRUE;
}
// Get the DNS name of the host server.
dwLen = sizeof(szServer)/sizeof(TCHAR);
if (!GetComputerNameEx(ComputerNameDnsFullyQualified, szServer, &dwLen))
{
hr = HRESULT_FROM_NT(GetLastError());
goto ExitHere;
}
// Bind to the SCP.
hr = ADsGetObject(szAdsPath, IID_IDirectoryObject, (void **)&pObj);
if (FAILED(hr))
{
LOG((TL_ERROR,
"ADsGetObject failed to bind to GUID (bind string: %S): ",
szAdsPath
));
goto ExitHere;
}
// Retrieve attributes from the SCP.
hr = pObj->GetObjectAttributes(pszAttrs, 2, &pAttribs, &dwAttrs);
if (FAILED(hr)) {
LOG((TL_ERROR, "GetObjectAttributes failed"));
goto ExitHere;
}
// Check if we got the correct attribute type
if (pAttribs->dwADsType != ADSTYPE_CASE_IGNORE_STRING ||
(pAttribs+1)->dwADsType != ADSTYPE_CASE_IGNORE_STRING)
{
LOG((TL_ERROR,
"GetObjectAttributes returned dwADsType (%d,%d) instead of CASE_IGNORE_STRING",
pAttribs->dwADsType,
(pAttribs+1)->dwADsType
));
goto ExitHere;
}
// Compare the current DNS name and port to the values retrieved from
// the SCP. Update the SCP only if something has changed.
for (i=0; i<(LONG)dwAttrs; i++)
{
if (_tcsicmp(TEXT("serviceDNSName"), pAttribs[i].pszAttrName)==0)
{
if (_tcsicmp(szServer, pAttribs[i].pADsValues->CaseIgnoreString) != 0)
{
LOG((TL_TRACE, "serviceDNSName being updated", 0));
bUpdate = TRUE;
}
else
{
LOG((TL_TRACE, "serviceDNSName okay", 0));
}
}
else if (_tcsicmp(
TEXT("serviceBindingInformation"),
pAttribs[i].pszAttrName
)==0)
{
if (_tcsicmp(szBinding, pAttribs[i].pADsValues->CaseIgnoreString) != 0)
{
LOG((TL_TRACE, "serviceBindingInformation being updated", 0));
bUpdate = TRUE;
}
else
{
LOG((TL_TRACE, "serviceBindingInformation okay"));
}
}
}
// The binding information or server name have changed,
// so update the SCP values.
if (bUpdate)
{
dnsname.dwType = ADSTYPE_CASE_IGNORE_STRING;
dnsname.CaseIgnoreString = szServer;
binding.dwType = ADSTYPE_CASE_IGNORE_STRING;
binding.CaseIgnoreString = szBinding;
hr = pObj->SetObjectAttributes(Attribs, 2, &dwAttrs);
if (FAILED(hr))
{
LOG((TL_ERROR, "ScpUpdate: Failed to set SCP values. 0x%x", hr));
goto ExitHere;
}
}
ExitHere:
if (pAttribs)
{
FreeADsMem(pAttribs);
}
if (pObj)
{
pObj->Release();
}
if (bRevert)
{
RevertLocalSystemImp ();
}
if (bCoInited)
{
CoUninitialize ();
}
return hr;
}
//
// UpdateTapiSCP
//
// Update TAPI server SCP properties when necessary to keep every
// piece ofthe information up to date. The following will be checked:
//
// Parameters:
// pGuidAssoc - The line/user association guid
// pGuidCluster - The cluster GUID
//
HRESULT UpdateTapiSCP (
BOOL bActive,
GUID * pGuidAssoc,
GUID * pGuidCluster
)
{
HRESULT hr = S_OK;
TCHAR szGUIDCluster[40];
TCHAR szGUIDAssoc[40];
TCHAR szBinding[128];
DWORD dwStat, dwType, dwLen;
HKEY hReg = NULL;
TCHAR szAdsPath[MAX_PATH];
LOG((TL_TRACE, "UpdateTapiSCP: enter, bActive: %d", bActive));
EnterCriticalSection(&gSCPCritSec);
// Open the service's registry key.
dwStat = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
gszRegKeyTelephony,
0,
KEY_QUERY_VALUE,
&hReg
);
if (dwStat != NO_ERROR)
{
// Probably because the SCP never published, CreateTapiSCP
LOG((TL_ERROR, "RegOpenKeyEx failed", dwStat));
hr = HRESULT_FROM_NT(dwStat);
goto ExitHere;
}
// Get the GUID binding string used to bind to the service's SCP.
dwLen = sizeof(szAdsPath);
dwStat = RegQueryValueEx(
hReg,
gszRegTapisrvSCPGuid,
0,
&dwType,
(LPBYTE)szAdsPath,
&dwLen
);
if (dwStat != NO_ERROR)
{
LOG((TL_ERROR, "UpdateTapiSCP: RegQueryValueEx TapisrvSCPGuid failed, error 0x%x", dwStat));
if (TapiGlobals.dwFlags & TAPIGLOBALS_SERVER)
{
if (FAILED(hr = CreateTapiSCP (pGuidAssoc, pGuidCluster)))
{
LOG((TL_ERROR, "UpdateTapiSCP: CreateTapiSCP failed"));
goto ExitHere;
}
// CreateTapiSCP succeeded, need to read the guid
dwLen = sizeof(szAdsPath);
dwStat = RegQueryValueEx(
hReg,
gszRegTapisrvSCPGuid,
0,
&dwType,
(LPBYTE)szAdsPath,
&dwLen
);
if (dwStat != NO_ERROR)
{
LOG((TL_ERROR, "UpdateTapiSCP: CreateTapiSCP succeeded but cannot read the guid"));
hr = HRESULT_FROM_NT(dwStat);
goto ExitHere;
}
}
else
{
LOG((TL_TRACE, "UpdateTapiSCP: Telephony server is not enabled"));
hr = HRESULT_FROM_NT(dwStat);
goto ExitHere;
}
}
// Format to generate desired binding information
if (pGuidCluster != NULL)
{
StringFromGUID2 (
*pGuidCluster,
szGUIDCluster,
sizeof(szGUIDCluster) / sizeof(TCHAR)
);
}
else
{
szGUIDCluster[0] = 0;
}
if (pGuidAssoc != NULL)
{
StringFromGUID2 (
*pGuidAssoc,
szGUIDAssoc,
sizeof(szGUIDAssoc) / sizeof(TCHAR)
);
}
else
{
szGUIDAssoc[0] = 0;
}
//
// Now construct the serviceBindingInformation based on the
// service status
//
if (bActive)
{
TCHAR szTTL[64];
FILETIME ftCur;
ULONGLONG ullInc, ullTime;
SYSTEMTIME stExp;
// Get current time
GetSystemTimeAsFileTime (&ftCur);
CopyMemory (&ullTime, &ftCur, sizeof(ULONGLONG));
// Get the time increment for gdwTapiSCPTTL minutes
// FILETIME is in the unit of 100 nanoseconds
ullInc = ((ULONGLONG)gdwTapiSCPTTL) * 60 * 10000000;
// Get the record expiration time
ullTime += ullInc;
CopyMemory (&ftCur, &ullTime, sizeof(FILETIME));
//
// Convert the expiration time to system time and
// format the string
//
// The current TTL string is the concatenation of
// Year, Month, Date, Hour, Minute, Second, Milliseconds
// There are 5 digits allocated for year, 3 digits for
// milliseconds, and 2 digits fro the remaining fields
// all the numbers are zero padded to fill the extra space
//
// Format here needs to be consistant with \
// sp\remotesp\dslookup.cpp
//
FileTimeToSystemTime (&ftCur, &stExp);
wsprintf (
szTTL,
TEXT("%05d%02d%02d%02d%02d%02d%03d"),
stExp.wYear,
stExp.wMonth,
stExp.wDay,
stExp.wHour,
stExp.wMinute,
stExp.wSecond,
stExp.wMilliseconds
);
wsprintf(
szBinding,
gszTapisrvBindingInfo,
szGUIDCluster,
szGUIDAssoc,
TEXT("Active"),
szTTL
);
}
else
{
wsprintf(
szBinding,
gszTapisrvBindingInfo,
szGUIDCluster,
szGUIDAssoc,
TEXT("Inactive"),
TEXT("")
);
}
hr = UpdateSCP (
szAdsPath,
szBinding
);
ExitHere:
if (hReg)
{
RegCloseKey (hReg);
}
LeaveCriticalSection(&gSCPCritSec);
return hr;
}
//
// Proxy server exists only if TAPI server are alive
// the DS information not likely to change when TAPI server is
// alive, so no SCP updating routine for Proxy server
//
/**********************************************************
* SCP Removal
*********************************************************/
//
// RemoveSCP
//
// Removes a Service Connection Point object from the
// local host computer object.
//
// Parameters:
// wszRDN - the RDN of the SCP to delete
// szRegNameToDel
// - The registery value name to be deleted
// If this value is NULL, no registry del
//
HRESULT RemoveSCP (
LPWSTR wszRDN,
LPTSTR szRegNameToDel
)
{
HRESULT hr = S_OK;
TCHAR szServer[MAX_PATH];
TCHAR szAdsPath[MAX_PATH];
DWORD dwLen, dwStat;
HKEY hReg;
IDirectoryObject * pComp = NULL;
BOOL bCoInited = FALSE;
BOOL bRevert = FALSE;
LOG((TL_TRACE, "RemoveSCP %S %S", wszRDN, szRegNameToDel));
//
// Do CoInitializeEx
//
hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
if (FAILED (hr))
{
goto ExitHere;
}
bCoInited = TRUE;
//
// Do all the operation in LocalSystem account
//
if (IsCurrentLocalSystem() != S_OK)
{
hr = ImpersonateLocalSystem ();
if (hr)
{
goto ExitHere;
}
bRevert = TRUE;
}
// Get the DNS name of the host server.
dwLen = sizeof(szServer);
if (!GetComputerObjectName(NameFullyQualifiedDN, szServer, &dwLen))
{
hr = HRESULT_FROM_NT(GetLastError());
goto ExitHere;
}
//
// Compose the ADSpath and bind to the computer object for the local computer
//
_tcscpy(szAdsPath,TEXT("LDAP://"));
_tcscat(szAdsPath,szServer);
hr = ADsGetObject(szAdsPath, IID_IDirectoryObject, (void **)&pComp);
if (FAILED(hr)) {
LOG((TL_ERROR, "Failed (%x) to bind Computer Object.",hr));
goto ExitHere;
}
hr = pComp->DeleteDSObject (wszRDN);
if (FAILED (hr))
{
LOG((TL_ERROR, "Failed (%x) to Delete Tapisrv Object.",hr));
if (HRESULT_CODE(hr) == ERROR_DS_NO_SUCH_OBJECT)
{
hr = HRESULT_FROM_NT (TAPIERR_SCP_DOES_NOT_EXIST);
}
goto ExitHere;
}
// Open the service's registry key.
if (szRegNameToDel)
{
dwStat = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
gszRegKeyTelephony,
0,
KEY_QUERY_VALUE | KEY_WRITE,
&hReg);
if (dwStat == NO_ERROR)
{
RegDeleteValue (
hReg,
szRegNameToDel
);
RegCloseKey (hReg);
}
else
{
LOG((TL_ERROR, "RegOpenKeyEx failed", dwStat));
hr = HRESULT_FROM_NT(GetLastError());
// goto ExitHere;
}
}
ExitHere:
if (pComp)
pComp->Release();
if (bRevert)
{
RevertLocalSystemImp ();
}
if (bCoInited)
{
CoUninitialize ();
}
return hr;
}
//
// RemoveTapiSCP
//
// Removes the TAPI server Service Connection Point object from the
// local host computer object. This happens if a TAPI server machine
// retires from service.
//
HRESULT RemoveTapiSCP (
)
{
DWORD dwResult;
EnterCriticalSection(&gSCPCritSec);
dwResult = RemoveSCP (
(LPWSTR) gwszTapisrvRDN,
(LPTSTR) gszRegTapisrvSCPGuid
);
LeaveCriticalSection(&gSCPCritSec);
return dwResult;
}
//
// RemoveProxySCP
//
// Removes the proxy server Service Connection Point object from the
// local host computer object. This happens if the last line is closed
// from a certain proxy server (CLSID)
//
HRESULT RemoveProxySCP (
LPTSTR szClsid
)
{
HRESULT hr = S_OK;
// Construct the RDN
// RDN size include "cn=TAPI Proxy Server" + szClsid(38ch)
WCHAR wszRDN[128];
WCHAR *psz;
wcscpy (wszRDN, gwszProxyRDN);
wcscat (wszRDN, L"{");
psz = wszRDN + wcslen(wszRDN);
#ifndef UNICODE
if (MultiByteToWideChar (
CP_ACP,
MB_PRECOMPOSED,
szClsid,
-1,
psz,
(sizeof(wszRDN) - sizeof(gwszProxyRDN)) / sizeof(WCHAR) - 3
// 3 is to compensate for the two brace
) == 0)
{
hr = HRESULT_FROM_NT(GetLastError());
goto ExitHere;
}
#else
wcscat (wszRDN, szClsid);
#endif
wcscat (wszRDN, L"}");
// Call RemoveSCP
hr = RemoveSCP (
wszRDN,
NULL
);
//ExitHere:
return hr;
}
/**********************************************************
* Proxy Server SCP management
*********************************************************/
//
// The Rules:
// 1. An array of created SCP objects and their corresponding CLSID
// is maintained in a global data structure PROXY_SCPS
// 2. ServerInit calls OnProxySCPInit & ServerShutdown calls
// OnProxySCPShutdown
// 3. Every LOpen with proxy privilege will call OnProxyLineOpen with
// the proxy server CLSID as the input parameter.
// An SCP object will be created for a new CLSID, subsequent LOpen
// with the same CLSID will only increment the ref count
// 4. Every LClose with on a line (opened with proxy privilege) will call
// OnProxyLineClose with the proxy server CLSID as the input parameter
// The ref count is decemented every time for the SCP with the CLSID,
// if the ref count goes to zero, the SCP object will be deleted.
//
HRESULT OnProxySCPInit (
)
{
TapiEnterCriticalSection (&TapiGlobals.CritSec);
ZeroMemory (&gProxyScps, sizeof(gProxyScps));
TapiLeaveCriticalSection (&TapiGlobals.CritSec);
return S_OK;
}
HRESULT OnProxySCPShutdown (
)
{
DWORD i;
TapiEnterCriticalSection (&TapiGlobals.CritSec);
for (i = 0; i < gProxyScps.dwUsedEntries; ++i)
{
RemoveProxySCP (gProxyScps.aEntries[i].szClsid);
}
if (gProxyScps.aEntries != NULL)
{
ServerFree (gProxyScps.aEntries);
}
ZeroMemory (&gProxyScps, sizeof(gProxyScps));
TapiLeaveCriticalSection (&TapiGlobals.CritSec);
return S_OK;
}
HRESULT OnProxyLineOpen (
LPTSTR szClsid
)
{
HRESULT hr = S_OK;
BOOL fExists = FALSE;
DWORD i;
// Skip beginning/trailing white space
while (*szClsid == TEXT(' ') || *szClsid == TEXT('\t'))
++ szClsid;
// A valid CLSID string should only contain 38 chars
if (_tcslen (szClsid) > 40)
{
hr = E_INVALIDARG;
goto ExitHere;
}
TapiEnterCriticalSection (&TapiGlobals.CritSec);
// Is SCP for this szClsid already created (in array)?
for (i = 0; i < gProxyScps.dwUsedEntries; ++i)
{
if (_tcsicmp (
gProxyScps.aEntries[i].szClsid,
szClsid
) == 0)
{
fExists = TRUE;
break;
}
}
// If already exists, inc the ref count
if (fExists)
{
gProxyScps.aEntries[i].dwRefCount++;
}
// If not exists, create the new SCP and cache it
else
{
LPTSTR pBindByGuidStr;
hr = CreateProxySCP (szClsid, &pBindByGuidStr);
if (FAILED (hr))
{
TapiLeaveCriticalSection (&TapiGlobals.CritSec);
goto ExitHere;
}
if (gProxyScps.dwUsedEntries >= gProxyScps.dwTotalEntries)
{
// Increase the size
PROXY_SCP_ENTRY * pNew;
pNew = (PPROXY_SCP_ENTRY) ServerAlloc (
sizeof(PROXY_SCP_ENTRY) * (gProxyScps.dwTotalEntries + 16)
);
if (pNew == NULL)
{
hr = LINEERR_NOMEM;
ServerFree (pBindByGuidStr);
TapiLeaveCriticalSection (&TapiGlobals.CritSec);
goto ExitHere;
}
CopyMemory (
pNew,
gProxyScps.aEntries,
sizeof(PROXY_SCP_ENTRY) * gProxyScps.dwTotalEntries
);
ServerFree (gProxyScps.aEntries);
gProxyScps.aEntries = pNew;
gProxyScps.dwTotalEntries += 16;
}
i = gProxyScps.dwUsedEntries++;
_tcscpy (gProxyScps.aEntries[i].szClsid, szClsid);
_tcscpy (gProxyScps.aEntries[i].szObjGuid, pBindByGuidStr);
gProxyScps.aEntries[i].dwRefCount = 1;
ServerFree (pBindByGuidStr);
}
TapiLeaveCriticalSection (&TapiGlobals.CritSec);
ExitHere:
return hr;
}
HRESULT OnProxyLineClose (
LPTSTR szClsid
)
{
HRESULT hr = S_OK;
BOOL fExists = FALSE;
DWORD i;
// Skip beginning/trailing white space
while (*szClsid == TEXT(' ') || *szClsid == TEXT('\t'))
++ szClsid;
// A valid CLSID string should only contain 38 chars
if (_tcslen (szClsid) > 40)
{
hr = E_INVALIDARG;
goto ExitHere;
}
TapiEnterCriticalSection (&TapiGlobals.CritSec);
// Is SCP for this szClsid already created (in array)?
for (i = 0; i < gProxyScps.dwUsedEntries; ++i)
{
if (_tcsicmp (
gProxyScps.aEntries[i].szClsid,
szClsid
) == 0)
{
fExists = TRUE;
break;
}
}
if (fExists)
{
--gProxyScps.aEntries[i].dwRefCount;
// If ref count goes to zero, remove the SCP
if (gProxyScps.aEntries[i].dwRefCount == 0)
{
hr = RemoveProxySCP (gProxyScps.aEntries[i].szClsid);
if (i < gProxyScps.dwUsedEntries - 1)
{
MoveMemory (
gProxyScps.aEntries + i,
gProxyScps.aEntries + i + 1,
sizeof(PROXY_SCP_ENTRY) * (gProxyScps.dwUsedEntries - 1 - i)
);
}
--gProxyScps.dwUsedEntries;
}
}
TapiLeaveCriticalSection (&TapiGlobals.CritSec);
ExitHere:
return hr;
}