Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

853 lines
24 KiB

/*++
Copyright (c) 1995-2000 Microsoft Corporation
Module Name:
rpcbind.c
Abstract:
Domain Name System (DNS) Server -- Admin Client API
RPC binding routines for client.
Author:
Jim Gilroy (jamesg) September 1995
Environment:
User Mode Win32
Revision History:
--*/
#include "dnsclip.h"
#include "ws2tcpip.h"
#include <rpcutil.h>
#include <ntdsapi.h>
#include <dnslibp.h>
//
// Allow 45 seconds for RPC connection. This should allow one 30 second
// TCP attempt plus 15 seconds of the second TCP retry.
//
#define DNS_RPC_CONNECT_TIMEOUT ( 45 * 1000 ) // in milliseconds
//
// Local machine name
//
// Keep this as static data to check when attempt to access local
// machine by name.
// Buffer is large enough to hold unicode version of name.
//
static WCHAR wszLocalMachineName[ MAX_COMPUTERNAME_LENGTH + 1 ] = L"";
LPWSTR pwszLocalMachineName = wszLocalMachineName;
LPSTR pszLocalMachineName = ( LPSTR ) wszLocalMachineName;
//
// NT4 uses ANSI\UTF8 string for binding
//
DWORD
FindProtocolToUseNt4(
IN LPSTR pszServerName
)
/*++
Routine Description:
Determine which protocol to use.
This is determined from server name:
- noneexistent or local -> use LPC
- valid IpAddress -> use TCP/IP
- otherwise named pipes
Arguments:
pszServerName -- server name we want to bind to
Return Value:
DNS_RPC_USE_TCPIP
DNS_RPC_USE_NP
DNS_RPC_USE_LPC
--*/
{
DWORD dwComputerNameLength;
DWORD dwIpAddress;
DWORD status;
DNSDBG( RPC, (
"FindProtocolToUseNt4(%s)\n",
pszServerName ));
//
// no address given, use LPC
//
if ( pszServerName == NULL ||
*pszServerName == 0 ||
( *pszServerName == '.' && *( pszServerName + 1 ) == 0 ) )
{
return DNS_RPC_USE_LPC;
}
//
// if valid IP address, use TCP/IP
// - except if loopback address, then use LPC
//
dwIpAddress = inet_addr( pszServerName );
if ( dwIpAddress != INADDR_NONE )
{
if ( strcmp( "127.0.0.1", pszServerName ) == 0 )
{
return DNS_RPC_USE_LPC;
}
return DNS_RPC_USE_TCPIP;
}
//
// DNS name -- use TCP/IP
//
if ( strchr( pszServerName, '.' ) )
{
status = Dns_ValidateName_UTF8(
pszServerName,
DnsNameHostnameFull );
if ( status == ERROR_SUCCESS || status == DNS_ERROR_NON_RFC_NAME )
{
return DNS_RPC_USE_TCPIP;
}
}
//
// pszServerName is netBIOS computer name
//
// check if local machine name -- then use LPC
// - save copy of local computer name if don't have it
//
if ( *pszLocalMachineName == '\0' )
{
dwComputerNameLength = MAX_COMPUTERNAME_LENGTH;
if( !GetComputerName(
pszLocalMachineName,
&dwComputerNameLength ) )
{
*pszLocalMachineName = '\0';
}
}
if ( ( *pszLocalMachineName != '\0' ) )
{
// if the machine has "\\" skip it for name compare.
if ( *pszServerName == '\\' )
{
pszServerName += 2;
}
if ( _stricmp( pszLocalMachineName, pszServerName ) == 0 )
{
return DNS_RPC_USE_LPC;
}
if ( _stricmp( "localhost", pszServerName ) == 0 )
{
return DNS_RPC_USE_LPC;
}
}
//
// remote machine name -- use named pipes
//
return DNS_RPC_USE_NAMED_PIPE;
}
//
// NT5 binding handle is unicode
//
DWORD
FindProtocolToUse(
IN LPWSTR pwszServerName
)
/*++
Routine Description:
Determine which protocol to use.
This is determined from server name:
- noneexistent or local -> use LPC
- valid IpAddress -> use TCP/IP
- otherwise named pipes
Arguments:
pwszServerName -- server name we want to bind to
Return Value:
DNS_RPC_USE_TCPIP
DNS_RPC_USE_NP
DNS_RPC_USE_LPC
--*/
{
DWORD nameLength;
DWORD status;
CHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
DNSDBG( RPC, ( "FindProtocolToUse( %S )\n", pwszServerName ));
//
// If no server name was given, use LPC.
// Special case "." as local machine for convenience in dnscmd.exe.
//
if ( pwszServerName == NULL ||
*pwszServerName == 0 ||
( *pwszServerName == L'.' && * ( pwszServerName + 1 ) == 0 ) )
{
return DNS_RPC_USE_LPC;
}
//
// If the name appears to be an address or a fully qualified
// domain name, check for TCP versus LPC. We want to use LPC in
// all cases where the target is the name of the local machine
// or is a local address.
//
if ( ( wcschr( pwszServerName, L'.' ) ||
wcschr( pwszServerName, L':' ) ) &&
Dns_UnicodeToUtf8(
pwszServerName,
wcslen( pwszServerName ),
nameBuffer,
sizeof( nameBuffer ) ) )
{
struct addrinfo * paddrinfo = NULL;
struct addrinfo hints = { 0 };
//
// Remove trailing dots from nameBuffer so that we can do string
// compares later to see if it matches the local host name.
//
while ( nameBuffer[ 0 ] &&
nameBuffer[ strlen( nameBuffer ) - 1 ] == '.' )
{
nameBuffer[ strlen( nameBuffer ) - 1 ] = '\0';
}
//
// Attempt to convert the string into an address.
//
hints.ai_flags = AI_NUMERICHOST;
if ( getaddrinfo(
nameBuffer,
NULL, // service name
&hints,
&paddrinfo ) == ERROR_SUCCESS &&
paddrinfo )
{
if ( paddrinfo->ai_family == AF_INET )
{
PDNS_ADDR_ARRAY dnsapiArrayIpv4;
BOOL addrIsLocal = FALSE;
if ( strcmp( "127.0.0.1", nameBuffer ) == 0 )
{
return DNS_RPC_USE_LPC;
}
//
// Check IP passed in against local IPv4 address list.
// If we are unable to retrieve local IPv4 address list,
// fail silently and use TCP/IP.
//
dnsapiArrayIpv4 = ( PDNS_ADDR_ARRAY )
DnsQueryConfigAllocEx(
DnsConfigLocalAddrsIp4,
NULL, // adapter name
FALSE ); // local alloc
if ( dnsapiArrayIpv4 )
{
DWORD iaddr;
struct sockaddr_in * psin4;
psin4 = ( struct sockaddr_in * ) paddrinfo->ai_addr;
for ( iaddr = 0; iaddr < dnsapiArrayIpv4->AddrCount; ++iaddr )
{
if ( DnsAddr_GetIp4( &dnsapiArrayIpv4->AddrArray[ iaddr ] ) ==
psin4->sin_addr.s_addr )
{
addrIsLocal = TRUE;
break;
}
}
DnsFreeConfigStructure( dnsapiArrayIpv4, DnsConfigLocalAddrsIp4 );
if ( addrIsLocal )
{
return DNS_RPC_USE_LPC;
}
}
return DNS_RPC_USE_TCPIP;
}
else if ( paddrinfo->ai_family == AF_INET6 )
{
struct sockaddr_in6 * psin6;
psin6 = ( struct sockaddr_in6 * ) paddrinfo->ai_addr;
if ( IN6_IS_ADDR_LOOPBACK( &psin6->sin6_addr ) )
{
return DNS_RPC_USE_LPC;
}
return DNS_RPC_USE_TCPIP;
}
}
status = Dns_ValidateName_UTF8(
nameBuffer,
DnsNameHostnameFull );
if ( status == ERROR_SUCCESS || status == DNS_ERROR_NON_RFC_NAME )
{
//
// Note: assume we will never need a larger buffer, and
// if GetComputerName fails, return TCP/IP always.
//
CHAR szhost[ DNS_MAX_NAME_BUFFER_LENGTH ];
DWORD dwhostsize = DNS_MAX_NAME_BUFFER_LENGTH;
if ( GetComputerNameEx(
ComputerNameDnsFullyQualified,
szhost,
&dwhostsize ) &&
_stricmp( szhost, nameBuffer ) == 0 )
{
return DNS_RPC_USE_LPC;
}
return DNS_RPC_USE_TCPIP;
}
}
//
// pwszServerName is netBIOS computer name
//
// check if local machine name -- then use LPC
// - save copy of local computer name if don't have it
//
if ( *pwszLocalMachineName == 0 )
{
nameLength = MAX_COMPUTERNAME_LENGTH;
if( !GetComputerNameW(
pwszLocalMachineName,
&nameLength ) )
{
*pwszLocalMachineName = 0;
}
}
if ( *pwszLocalMachineName != 0 )
{
// if the machine has "\\" skip it for name compare.
if ( *pwszServerName == L'\\' )
{
pwszServerName += 2;
}
if ( _wcsicmp( pwszLocalMachineName, pwszServerName ) == 0 )
{
return DNS_RPC_USE_LPC;
}
if ( _wcsicmp( L"localhost", pwszServerName ) == 0 )
{
return DNS_RPC_USE_LPC;
}
}
//
// remote machine name -- use named pipes
//
return DNS_RPC_USE_NAMED_PIPE;
}
DNS_STATUS
makeSpn(
IN PWSTR ServiceClass,
IN PWSTR ServiceName,
IN OPTIONAL PWSTR InstanceName,
IN OPTIONAL USHORT InstancePort,
IN OPTIONAL PWSTR Referrer,
OUT PWSTR *Spn
)
/*
Routine Description:
This routine is wrapper around DsMakeSpnWto avoid two calls to this function,
one to find the size of the return value and second to get the actual value.
jwesth: I stole this routine from ds\src\sam\client\wrappers.c.
Arguments:
ServiceClass -- Pointer to a constant null-terminated Unicode string specifying the
class of the service. This parameter may be any string unique to that service;
either the protocol name (for example, ldap) or the string form of a GUID will work.
ServiceName -- Pointer to a constant null-terminated string specifying the DNS name,
NetBIOS name, or distinguished name (DN). This parameter must be non-NULL.
InstanceName -- Pointer to a constant null-terminated Unicode string specifying the DNS name
or IP address of the host for an instance of the service. If ServiceName specifies
the DNS or NetBIOS name of the service's host computer, the InstanceName parameter must be NULL.
InstancePort -- Port number for an instance of the service. Use 0 for the default port.
If this parameter is zero, the SPN does not include a port number.
Referrer -- Pointer to a constant null-terminated Unicode string specifying the DNS name
of the host that gave an IP address referral. This parameter is ignored unless the
ServiceName parameter specifies an IP address.
Spn -- Pointer to a Unicode string that receives the constructed SPN.
The caller must free this value.
Return Value:
STATUS_SUCCESS
Successful
STATUS_NO_MEMORY
not enough memory to complete the task
STATUS_INVALID_PARAMETER
one of the parameters is invalid
STATUS_INTERNAL_ERROR
opps something went wrong!
*/
{
DWORD DwStatus;
NTSTATUS Status = STATUS_SUCCESS;
ULONG SpnLength = 0;
ADDRINFO * paddrinfo = NULL;
ADDRINFO hints = { 0 };
CHAR szname[ DNS_MAX_NAME_LENGTH + 1 ];
PWSTR pwsznamecopy = NULL;
PSTR psznamecopy = NULL;
//
// If ServiceName is an IP address, do DNS lookup on it.
//
hints.ai_flags = AI_NUMERICHOST;
psznamecopy = Dns_StringCopyAllocate(
( PSTR ) ServiceName,
0,
DnsCharSetUnicode,
DnsCharSetUtf8 );
if ( psznamecopy &&
getaddrinfo(
psznamecopy,
NULL,
&hints,
&paddrinfo ) == ERROR_SUCCESS )
{
*szname = L'\0';
if ( getnameinfo(
paddrinfo->ai_addr,
paddrinfo->ai_addrlen,
szname,
DNS_MAX_NAME_LENGTH,
NULL,
0,
0 ) == ERROR_SUCCESS &&
*szname )
{
pwsznamecopy = Dns_StringCopyAllocate(
szname,
0,
DnsCharSetUtf8,
DnsCharSetUnicode );
if ( pwsznamecopy )
{
ServiceName = pwsznamecopy;
}
}
}
freeaddrinfo( paddrinfo );
//
// Construct SPN.
//
*Spn = NULL;
DwStatus = DsMakeSpnW(
ServiceClass,
ServiceName,
NULL,
0,
NULL,
&SpnLength,
NULL );
if ( DwStatus != ERROR_BUFFER_OVERFLOW )
{
ASSERT( !"DwStatus must be INVALID_PARAMETER, so which parameter did we pass wrong?" );
Status = STATUS_INVALID_PARAMETER;
goto Error;
}
*Spn = MIDL_user_allocate( SpnLength * sizeof( WCHAR ) );
if( *Spn == NULL ) {
Status = STATUS_NO_MEMORY;
goto Error;
}
DwStatus = DsMakeSpnW(
ServiceClass,
ServiceName,
NULL,
0,
NULL,
&SpnLength,
*Spn );
if ( DwStatus != ERROR_SUCCESS )
{
ASSERT( !"DwStatus must be INVALID_PARAMETER or BUFFER_OVERFLOW, so what did we do wrong?" );
Status = STATUS_INTERNAL_ERROR;
goto Error;
}
goto Exit;
Error:
ASSERT( !NT_SUCCESS( Status ) );
MIDL_user_free( *Spn );
*Spn = NULL;
Exit:
FREE_HEAP( pwsznamecopy );
FREE_HEAP( psznamecopy );
return Status;
}
handle_t
DNSSRV_RPC_HANDLE_bind(
IN DNSSRV_RPC_HANDLE pszServerName
)
/*++
Routine Description:
Get binding handle to a DNS server.
This routine is called from the DNS client stubs when
it is necessary create an RPC binding to the DNS server.
Arguments:
pszServerName - String containing the name of the server to bind with.
Return Value:
The binding handle if successful.
NULL if bind unsuccessful.
--*/
{
RPC_STATUS status;
LPWSTR binding;
handle_t bindingHandle;
DWORD RpcProtocol;
PWSTR pwszspn = NULL;
RPC_SECURITY_QOS rpcSecurityQOS;
BOOL bW2KBind = dnsrpcGetW2KBindFlag();
//
// Clear thread local W2K bind retry flag for the next attempt.
//
dnsrpcSetW2KBindFlag( FALSE );
//
// Initialize RPC quality of service structure.
//
rpcSecurityQOS.Version = RPC_C_SECURITY_QOS_VERSION;
rpcSecurityQOS.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
rpcSecurityQOS.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;
rpcSecurityQOS.ImpersonationType = RPC_C_IMP_LEVEL_DELEGATE;
//
// Determine protocol from target name (could be short name, long name, or IP).
//
RpcProtocol = FindProtocolToUse( (LPWSTR)pszServerName );
IF_DNSDBG( RPC )
{
DNS_PRINT(( "RPC Protocol = %d\n", RpcProtocol ));
}
if( RpcProtocol == DNS_RPC_USE_LPC )
{
status = RpcStringBindingComposeW(
0,
L"ncalrpc",
NULL,
DNS_RPC_LPC_EP_W,
L"Security=Impersonation Static True",
&binding );
}
else if( RpcProtocol == DNS_RPC_USE_NAMED_PIPE )
{
status = RpcStringBindingComposeW(
0,
L"ncacn_np",
( LPWSTR ) pszServerName,
DNS_RPC_NAMED_PIPE_W,
L"Security=Impersonation Static True",
&binding );
}
else
{
status = RpcStringBindingComposeW(
0,
L"ncacn_ip_tcp",
( LPWSTR ) pszServerName,
DNS_RPC_SERVER_PORT_W,
NULL,
&binding );
}
if ( status != RPC_S_OK )
{
DNS_PRINT((
"ERROR: RpcStringBindingCompose failed for protocol %d\n"
" Status = %d\n",
RpcProtocol,
status ));
goto Cleanup;
}
status = RpcBindingFromStringBindingW(
binding,
&bindingHandle );
if ( status != RPC_S_OK )
{
DNS_PRINT((
"ERROR: RpcBindingFromStringBinding failed\n"
" Status = %d\n",
status ));
goto Cleanup;
}
if ( RpcProtocol == DNS_RPC_USE_TCPIP )
{
//
// Create SPN string
//
if ( !bW2KBind )
{
status = makeSpn(
L"Rpcss",
( PWSTR ) pszServerName,
NULL,
0,
NULL,
&pwszspn );
}
if ( !bW2KBind && status == RPC_S_OK )
{
//
// Set up RPC security.
//
#if DBG
printf( "rpcbind: SPN = %S\n", pwszspn );
#endif
status = RpcBindingSetAuthInfoExW(
bindingHandle, // binding handle
pwszspn, // app name to security provider
RPC_C_AUTHN_LEVEL_CONNECT, // auth level
RPC_C_AUTHN_GSS_NEGOTIATE, // auth package ID
NULL, // client auth info, NULL specified logon info.
0, // auth service
&rpcSecurityQOS ); // RPC security quality of service
if ( status != RPC_S_OK )
{
DNS_PRINT((
"ERROR: RpcBindingSetAuthInfo failed\n"
" Status = %d\n",
status ));
goto Cleanup;
}
}
else
{
#if DBG
printf( "rpcbind: SPN = %s\n", DNS_RPC_SECURITY );
#endif
//
// No SPN is available, so make the call that we used in W2K.
// This seems to have a beneficial effect even though it is
// not really correct. If the target is an IP address and there
// is no reverse lookup zone, without the call below we do not
// get a working RPC session.
//
if ( bW2KBind )
{
status = RpcBindingSetAuthInfoA(
bindingHandle, // binding handle
DNS_RPC_SECURITY, // app name to security provider
RPC_C_AUTHN_LEVEL_CONNECT, // auth level
RPC_C_AUTHN_WINNT, // auth package ID
NULL, // client auth info, NULL specified logon info.
0 ); // auth service
}
else
{
status = RpcBindingSetAuthInfoExA(
bindingHandle, // binding handle
DNS_RPC_SECURITY, // app name to security provider
RPC_C_AUTHN_LEVEL_CONNECT, // auth level
RPC_C_AUTHN_GSS_NEGOTIATE, // auth package ID
NULL, // client auth info, NULL specified logon info.
0, // auth service
&rpcSecurityQOS ); // RPC security quality of service
}
if ( status != RPC_S_OK )
{
DNS_PRINT((
"ERROR: RpcBindingSetAuthInfo failed\n"
" Status = %d\n",
status ));
goto Cleanup;
}
}
}
#if 0
//
// Set RPC connection timeout. The default timeout is very long. If
// the remote IP is unreachable we don't really need to wait that long.
//
// Can't do this. It's a nice idea but RPC uses this timeout for the
// entire call, which means that long running RPC calls will return
// RPC_S_CALL_CANCELLED to the client after 45 seconds. What I want
// is a timeout option for connection only. RPC does not provide this.
//
RpcBindingSetOption(
bindingHandle,
RPC_C_OPT_CALL_TIMEOUT,
DNS_RPC_CONNECT_TIMEOUT );
#endif
Cleanup:
RpcStringFreeW( &binding );
MIDL_user_free( pwszspn );
if ( status != RPC_S_OK )
{
SetLastError( status );
return NULL;
}
return bindingHandle;
}
void
DNSSRV_RPC_HANDLE_unbind(
IN DNSSRV_RPC_HANDLE pszServerName,
IN handle_t BindHandle
)
/*++
Routine Description:
Unbind from DNS server.
Called from the DNS client stubs when it is necessary to unbind
from a server.
Arguments:
pszServerName - This is the name of the server from which to unbind.
BindingHandle - This is the binding handle that is to be closed.
Return Value:
None.
--*/
{
UNREFERENCED_PARAMETER(pszServerName);
DNSDBG( RPC, ( "RpcBindingFree()\n" ));
RpcBindingFree( &BindHandle );
}
//
// End rpcbind.c
//