windows-nt/Source/XPSP1/NT/net/dhcp/server/dhcpds/dsauth.c
2020-09-26 16:20:57 +08:00

871 lines
22 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
ds.c
Abstract:
This file contains all the routines used in interfacing with the DS. We go
to the DS go make sure we are not a Rogue server, and also to retrieve all
configuration information.
Author:
Shirish Koti (koti) 26-May-1997
Environment:
User Mode - Win32
Revision History:
--*/
//
// System Includes
//
#define INC_OLE2
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "activeds.h"
#include "adsi.h"
#if DBG
#define DBGPRINT printf
#else
#define DBGPRINT
#endif
#include <netlib.h>
#include <lmapibuf.h>
#include <dsgetdc.h>
#include <dnsapi.h>
#include "adsi.h"
#include <dsauth.h>
#include <dhcpapi.h>
#include <dhcpds.h>
DWORD
ValidateService(
LPWSTR lpwDomain, // domain name
LPWSTR lpwUserName, // usually NULL
LPWSTR lpwPassword, // usually NULL
DWORD dwAuthFlags, // ??
LPWSTR lpwObjectPath, // where our things are stored (e.g. DHCP)
LPWSTR lpwSrchFilter, // objects we're looking for (objectClass==DHCP)
LPWSTR *ppwAttrName, // name of the attribute we're looking for
LPWSTR lpwAttrVal // value of the attribute we're looking for
);
LPWSTR lpwGlbNamingContextString = DHCPDS_NAMING_CONTEXT;
DWORD
DhcpDSGetDomainAndRoot(
LPWSTR * ppwDomainName,
LPWSTR * ppwRootName,
BOOLEAN * fIsStandAlone
)
/*++
Routine Description:
This function gets the DS Domain and the root of the enterprise that this
machine is a member of.
NOTE: Memory is allocated to hold strings pointed to by *ppwDomainName and
*ppwRootName, and the caller must free that memory.
One of ppwDomainName or ppwRootName can be NULL, to indicate the caller
doesn't want that info. (e.g. only root is needed, not domain)
Arguments:
ppwDomainName - pointer to a string that will hold domain name
ppwRootName - pointer to a string that will hold the root of enterprise
fIsStandAlone - pointer to a boolean: TRUE if this server is Standalone
Return Value:
Result of the operation
--*/
{
DWORD dwError;
LPTSTR NetbiosDomNamePtr=NULL;
LPTSTR pDomain=NULL;
LPWSTR lpwRetDomainName=NULL;
LPWSTR lpwRetRootName=NULL;
BOOLEAN IsWorkgroup=TRUE;
PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;
// initialize, in case we bail out
if (ppwDomainName)
{
*ppwDomainName = NULL;
}
if (ppwRootName)
{
*ppwRootName = NULL;
}
*fIsStandAlone = FALSE;
//
// first, find out which domain we are a member of.
//
dwError = NetpGetDomainNameExEx(
&NetbiosDomNamePtr,
&pDomain,
&IsWorkgroup);
//
// if NetpGetDomainNameExEx failed, we shouldn't proceed
//
if (dwError != NO_ERROR)
{
DBGPRINT("DhcpGetDSDomain: NetpGetDomainNameExEx failed (%ld)\n",dwError);
return(dwError);
}
// we don't care about this domain name: free the buffer
if (NetbiosDomNamePtr)
{
NetApiBufferFree(NetbiosDomNamePtr);
}
// if this is a standalone server, we're done here
if (IsWorkgroup)
{
DBGPRINT("DhcpGetDSDomain: server is part of a workgroup\n");
*fIsStandAlone = TRUE;
if (pDomain)
{
NetApiBufferFree(pDomain);
}
return(NO_ERROR);
}
// if pDomain is NULL, it means that the DNS domain isn't configured.
// For the purpose of rogue-detection, we will assume it to be a workgroup
// though in the strictest sense that's not true.
if (pDomain == NULL)
{
DBGPRINT("DhcpGetDSDomain: pDomain is NULL: assuming part of workgroup\n");
*fIsStandAlone = TRUE;
return(NO_ERROR);
}
//
// if the caller wants DomainName, allocate space, and copy it in
//
if (ppwDomainName)
{
lpwRetDomainName = (LPWSTR)LocalAlloc(
LPTR,
(wcslen(pDomain)+1)*sizeof(WCHAR) );
if (lpwRetDomainName == NULL)
{
DBGPRINT("DhcpGetDSDomain: malloc 1 failed (%ld)\n",
(wcslen(pDomain)+1)*sizeof(WCHAR));
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto DhcpGetDSDomain_ErrExit;
}
// copy the name in
wcscpy(lpwRetDomainName, pDomain);
*ppwDomainName = lpwRetDomainName;
}
//
// See if the caller wants DS Root as well
// get the info about this domain controller (the only thing we want
// from all the good info in pDCInfo is the DS root name (DnsForestName field)
//
if (ppwRootName)
{
dwError = DsGetDcName(
NULL, // server name
pDomain, // domain name
NULL, // domain guid
NULL, // site name
DS_IS_DNS_NAME,
&pDCInfo);
if (dwError != NO_ERROR)
{
DBGPRINT("DhcpGetDSDomain: DsGetDcName failed %ld\n",dwError);
goto DhcpGetDSDomain_ErrExit;
}
//
// RootName: allocate space, and copy the root name in
//
lpwRetRootName = (LPWSTR)LocalAlloc(
LPTR,
(wcslen(pDCInfo->DnsForestName)+1)*sizeof(WCHAR) );
if (lpwRetRootName == NULL)
{
DBGPRINT("DhcpGetDSDomain: malloc 2 failed (%ld)\n",
(wcslen(pDCInfo->DnsForestName)+1)*sizeof(WCHAR));
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto DhcpGetDSDomain_ErrExit;
}
// copy the name in
wcscpy(lpwRetRootName, pDCInfo->DnsForestName);
*ppwRootName = lpwRetRootName;
NetApiBufferFree(pDCInfo);
}
NetApiBufferFree(pDomain);
return(NO_ERROR);
DhcpGetDSDomain_ErrExit:
DBGPRINT("DhcpGetDSDomain: executing error path, dwError = %ld\n",dwError);
//
// free the things that were given to us (or we took!)
//
if (pDomain)
{
NetApiBufferFree(pDomain);
}
if (pDCInfo)
{
NetApiBufferFree(pDCInfo);
}
if (lpwRetDomainName)
{
LocalFree(lpwRetDomainName);
}
if (lpwRetRootName)
{
LocalFree(lpwRetRootName);
}
if (ppwDomainName)
{
*ppwDomainName = NULL;
}
if (ppwRootName)
{
*ppwRootName = NULL;
}
return(dwError);
}
//BeginExport(function)
//DOC DhcpDSValidateServer validates the server in the DS by looking for the server
//DOC object and checking to see if the given IpAddress is present in the DS.
//DOC This calls the other helper routine in DhcpDs.dll to do the real work.
//DOC Returns one of
//DOC DHCPDSERR_DS_OPERATION_FAILED
//DOC DHCPDSERR_ENTRY_NOT_FOUND
//DOC DHCPDSERR_ENTRY_FOUND
//DOC DHCPDSERR_SERVER_IS_STANDALONE
DWORD
DhcpDSValidateServer( // validate in DS
IN LPWSTR lpwDomain, // OPTIONAL NULL ==> Default domain
IN DWORD *lpIpAddress, // one of the IpAddresses that must exist in DS
IN ULONG dwIpAddressCount, // # of ip addresses supplied..
IN LPWSTR lpwUserName, // OPTIONAL NULL ==> m/c account
IN LPWSTR lpwPassword, // OPTIONAL password to use
IN DWORD dwAuthFlags // MUST BE ZERO?
) //EndExport(function)
{
DWORD Error;
BOOL Found;
BOOL IsStandAlone;
IsStandAlone = FALSE;
Found = FALSE;
Error = DhcpDsValidateService // check to validate for dhcp
(
lpwDomain,
lpIpAddress,
dwIpAddressCount,
lpwUserName,
lpwPassword,
dwAuthFlags,
&Found,
&IsStandAlone
);
if( ERROR_SUCCESS != Error )
return DHCPDSERR_DS_OPERATION_FAILED;
if( IsStandAlone ) return DHCPDSERR_SERVER_IS_STANDALONE;
return Found? DHCPDSERR_ENTRY_FOUND : DHCPDSERR_ENTRY_NOT_FOUND;
}
DWORD
DhcpDSMapDomainToCNPath(
LPWSTR lpwDomain,
LPWSTR lpwUserName,
LPWSTR lpwPassword,
DWORD dwAuthFlags,
LPWSTR *ppwCNPath
)
/*++
Routine Description:
This function retrieves the full path to the Configuration Container, given
the name of the domain. If the domain name is not specified, server's domain
name is assumed.
NOTE: memory is allocated for ppwCNPath on successful return. Caller must
free this memory.
Arguments:
lpwDomain - domain name in which to validate the service. Can be NULL to
indicate the server's domain
lpwUserName - account name to use during DS lookup. Usually, NULL (default)
lpwPassword - password for the above user
dwAuthFlags - flags to use
ppwCNPath - pointer to string containing the path, on return.
Return Value:
Result of the operation
--*/
{
HRESULT hr=S_OK;
LPWSTR pCanonName;
LPWSTR lpwLocalDomain=NULL;
LPWSTR lpwDomainToContact=NULL;
LPWSTR lpwDCName;
HANDLE handle = NULL;
PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;
DWORD dwCanonNameLen;
PADS_ATTR_INFO pAttributeEntries=NULL;
DWORD dwNumAttributesReturned=0;
BOOLEAN fIsStandAlone;
BOOLEAN fCNStringFound;
DWORD dwRetCode;
DWORD index;
*ppwCNPath = NULL;
lpwDomainToContact = lpwDomain;
//
// if domain name is not supplied, find local domain first
//
if (!lpwDomain)
{
dwRetCode = DhcpDSGetDomainAndRoot(
&lpwLocalDomain,
NULL,
&fIsStandAlone);
if (dwRetCode != NO_ERROR)
{
DBGPRINT("DhcpDSMap..CNPath: GetLocalDom.. failed (0x%x)\n",dwRetCode);
return(dwRetCode);
}
//
// are we a standalone server? if so, return error since there is no
// local domain (and the caller didn't specify a domain)
//
if (fIsStandAlone)
{
DBGPRINT("DhcpDSMap..CNPath: standalone has no domain!\n");
return(DHCPDSERR_SERVER_IS_STANDALONE);
}
lpwDomainToContact = lpwLocalDomain;
}
//
// first, find a DC for this domain
//
dwRetCode = DsGetDcName(
NULL, // server name
lpwDomainToContact, // domain name
NULL, // domain guid
NULL, // site name
DS_IS_DNS_NAME, // what flag(s) to use?
&pDCInfo);
// if we first obtained local domain, free it here
if (lpwLocalDomain != NULL)
{
LocalFree((PCHAR)lpwLocalDomain);
}
if (dwRetCode != NO_ERROR)
{
DBGPRINT("MapDomainToCNPath: DsGetDcName failed %ld\n",dwRetCode);
return(dwRetCode);
}
lpwDCName = pDCInfo->DomainControllerName;
// skip the two leading '\'
lpwDCName += 2;
// also, skip the trailing '.'
lpwDCName[wcslen(lpwDCName)-1] = 0;
dwCanonNameLen = sizeof(DHCPDS_ROOTDSE_PRE) +
(wcslen(lpwDCName)+1)*sizeof(WCHAR) +
sizeof(DHCPDS_ROOTDSE_POST) +
sizeof(WCHAR);
pCanonName = (LPWSTR)LocalAlloc(LPTR, dwCanonNameLen);
if (pCanonName == NULL)
{
DBGPRINT("DhcpDSMapDomainToCNPath: malloc 1 failed (%ld)\n",dwCanonNameLen);
NetApiBufferFree(pDCInfo);
return(ERROR_NOT_ENOUGH_MEMORY);
}
// copy the LDAP:// string
wcscpy(pCanonName, DHCPDS_ROOTDSE_PRE);
// append the server name
wcscat(pCanonName, lpwDCName);
// then append the RootDSE part
wcscat(pCanonName, DHCPDS_ROOTDSE_POST);
// don't need this nomore: free it now
NetApiBufferFree(pDCInfo);
// now, open the RootDSE "object"
hr = ADSIOpenDSObject(
pCanonName,
lpwUserName,
lpwPassword,
dwAuthFlags,
&handle
);
// free this first: don't need this nomore
LocalFree((PCHAR)pCanonName);
if (FAILED(hr))
{
DBGPRINT("ADSIOpenDSObject (RootDSE) failed with 0x%x\n",hr);
return(DHCPDSERR_DS_OPERATION_FAILED);
}
// now, get all the attributes on this object
hr = ADSIGetObjectAttributes(
handle,
&lpwGlbNamingContextString,
1,
&pAttributeEntries,
&dwNumAttributesReturned);
if (FAILED(hr))
{
DBGPRINT("ADSIGetObjectAttributes (RootDSE) failed with 0x%x\n",hr);
dwRetCode = DHCPDSERR_DS_OPERATION_FAILED;
goto DhcpDSMapDomainToCNPath_Exit;
}
fCNStringFound = FALSE;
//
// now, of all the values returned, find the one that we are
// interested in (the one starts with CN=Configuration,)
//
for (index=0; index<pAttributeEntries->dwNumValues; index++)
{
if (pAttributeEntries->pADsValues[index].dwType != ADSTYPE_CASE_IGNORE_STRING)
{
continue;
}
if (wcsncmp(pAttributeEntries->pADsValues[index].CaseIgnoreString,
DHCPDS_CN_STRING,
wcslen(DHCPDS_CN_STRING)) == 0)
{
fCNStringFound = TRUE;
break;
}
}
if (!fCNStringFound)
{
DBGPRINT("MapDomainToCNPath: %d values returned, but not ours\n",
pAttributeEntries->dwNumValues);
dwRetCode = DHCPDSERR_DS_OPERATION_FAILED;
goto DhcpDSMapDomainToCNPath_Exit;
}
dwCanonNameLen = (wcslen(pAttributeEntries->pADsValues[index].CaseIgnoreString) +
1) * sizeof(WCHAR);
(*ppwCNPath) = (LPWSTR)LocalAlloc(LPTR, dwCanonNameLen);
if ((*ppwCNPath) == NULL)
{
DBGPRINT("MapDomainToCNPath: malloc 2 failed (%ld)\n",dwCanonNameLen);
dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
goto DhcpDSMapDomainToCNPath_Exit;
}
// now, copy the-path-to-Configuration string
wcscpy((*ppwCNPath),pAttributeEntries->pADsValues[index].CaseIgnoreString);
dwRetCode = 0;
DhcpDSMapDomainToCNPath_Exit:
// free pAttributeEntries
if (pAttributeEntries)
{
FreeADsMem(pAttributeEntries);
}
ADSICloseDSObject( handle );
return(dwRetCode);
}
DWORD
ValidateService(
LPWSTR lpwDomain,
LPWSTR lpwUserName,
LPWSTR lpwPassword,
DWORD dwAuthFlags,
LPWSTR lpwObjectPath,
LPWSTR lpwSrchFilter,
LPWSTR *ppwAttrName,
LPWSTR lpwAttrVal
)
/*++
Routine Description:
This function determines if the given attribute (ipaddress in case of
DHCP) is present in the list of authorized entities (DHCP Servers) on the DS
enterprise.
Arguments:
lpwDomain - domain name in which to validate the service. Can be NULL to
indicate the server's domain
lpwUserName - account name to use during DS lookup. Usually, NULL (default)
lpwPassword - password for the above user
dwAuthFlags - flags to use
lpwObjectPath - path to where the "entities" are stored
lpwSrchFilter - the filter to use to get our "entities" (e.g.(objectClass==DHCPClass)
ppwAttrName - name of the attribute we're looking for (e.g. IpAddress)
lpwAttrVal - value of the attribute (e.g. the server's ipaddress to validate)
Return Value:
Result of the operation
--*/
{
HRESULT hr=S_OK;
HANDLE handle = NULL;
ADS_SEARCH_HANDLE hSearchHandle=NULL;
ADS_SEARCH_COLUMN Column;
DWORD dwRetCode;
DWORD i;
LPWSTR lpwCNPath=NULL;
LPWSTR lpwFullDSPath=NULL;
DWORD dwLen;
BOOLEAN fObjectFound=FALSE;
dwRetCode = DHCPDSERR_ENTRY_NOT_FOUND;
//
// first, we need to get the full ads path to our container within the
// configuration container, from the domain name
//
dwRetCode = DhcpDSMapDomainToCNPath(
lpwDomain,
lpwUserName,
lpwPassword,
dwAuthFlags,
&lpwCNPath);
if (dwRetCode != 0)
{
DBGPRINT("ValidateService: MapDomainToCNPath failed with 0x%x\n",dwRetCode);
goto ValidateService_Exit;
}
dwLen = (wcslen(lpwObjectPath)+1)*sizeof(WCHAR) +
(wcslen(lpwCNPath)+1)*sizeof(WCHAR);
lpwFullDSPath = (LPWSTR)LocalAlloc(LPTR, dwLen);
if (lpwFullDSPath == NULL)
{
DBGPRINT("ValidateService: malloc failed (%ld)\n",dwLen);
dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
goto ValidateService_Exit;
}
// copy the lpwObjectPath (e.g. LDAP://CN=DHCP) string
wcscpy(lpwFullDSPath, lpwObjectPath);
// now, concatenate the path to the configuration container
wcscat(lpwFullDSPath, lpwCNPath);
hr = ADSIOpenDSObject(
lpwFullDSPath,
lpwUserName,
lpwPassword,
dwAuthFlags,
&handle
);
if (FAILED(hr))
{
DBGPRINT("ValidateService: ADSIOpenDSObject failed with 0x%x\n",hr);
dwRetCode = DHCPDSERR_DS_OPERATION_FAILED;
goto ValidateService_Exit;
}
hr = ADSIExecuteSearch(
handle,
lpwSrchFilter,
ppwAttrName,
-1, // ok to always pass this?
&hSearchHandle
);
if (FAILED(hr))
{
DBGPRINT("ADSIExecuteSearch failed with 0x%x\n",hr);
dwRetCode = DHCPDSERR_DS_OPERATION_FAILED;
goto ValidateService_Exit;
}
hr = ADSIGetNextRow(
handle,
hSearchHandle
);
if (FAILED(hr))
{
DBGPRINT("ADSIGetNextRow failed with 0x%x\n",hr);
dwRetCode = DHCPDSERR_DS_OPERATION_FAILED;
goto ValidateService_Exit;
}
while (hr != S_ADS_NOMORE_ROWS && !fObjectFound)
{
//
// get the values for the attribute requested (e.g. ipaddress)
//
hr = ADSIGetColumn(
handle,
hSearchHandle,
ppwAttrName[0], // for now, only first one is needed
&Column
);
if (SUCCEEDED(hr))
{
//
// if the attribute is multivalued, compare all the values to see
// if we find the one we want
//
for (i=0; i < Column.dwNumValues; i++)
{
if (_wcsnicmp(
Column.pADsValues[i].CaseIgnoreString,
lpwAttrVal,
wcslen(lpwAttrVal)) == 0)
{
fObjectFound = TRUE;
break;
}
}
// free the column
ADSIFreeColumn(
handle,
&Column);
}
//
// it's strange if this attribute is not defined for this object since
// it's one of our objects. But, nevertheless, just skip it
//
else
{
DBGPRINT("ADSIGetColumn failed with hr = 0x%x\n",hr);
}
// move to the next object
hr = ADSIGetNextRow(
handle,
hSearchHandle
);
}
dwRetCode = (fObjectFound)? DHCPDSERR_ENTRY_FOUND : DHCPDSERR_ENTRY_NOT_FOUND;
ValidateService_Exit:
if (hSearchHandle)
{
ADSICloseSearchHandle(
handle,
hSearchHandle);
}
if (handle)
{
ADSICloseDSObject( handle );
}
if (lpwCNPath)
{
LocalFree((PCHAR)lpwCNPath);
}
if (lpwFullDSPath)
{
LocalFree((PCHAR)lpwFullDSPath);
}
return(dwRetCode) ;
}
DWORD
DhcpDSSameEnterprise(
LPWSTR lpwDomainName,
LPWSTR lpwRootName,
BOOLEAN *fIsSameEnterprise
)
/*++
Routine Description:
This function checks if the given domain is part of the given DS Tree (in other
words, we want to find out if this domain belongs to another enterprise)
Arguments:
lpwDomainName - name of the domain in question
lpwRootName - root of the DS Tree to test against
fIsSameEnterprise - on return, TRUE or FALSE
Return Value:
result of the operation
--*/
{
PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;
DWORD dwRetCode;
//
// until we can definitely verify that the root of the given domain is
// the same as the one that is supplied, we return FALSE.
//
(*fIsSameEnterprise) = FALSE;
if (!lpwRootName)
{
DBGPRINT("DhcpDSSameEnterprise: C'mon, don't pass me a NULL!\n");
return(0);
}
dwRetCode = DsGetDcName(
NULL, // server name
lpwDomainName, // domain name
NULL, // domain guid
NULL, // site name
DS_IS_DNS_NAME, // what flag(s) to use?
&pDCInfo);
if (dwRetCode != NO_ERROR)
{
DBGPRINT("DhcpDSSameEnterprise: DsGetDcName failed (%ld)\n",dwRetCode);
return(dwRetCode);
}
// moment of truth: are the two roots the same?
(*fIsSameEnterprise) = (DnsCompareName(lpwRootName, pDCInfo->DnsForestName) != 0);
NetApiBufferFree(pDCInfo);
return(0);
}