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.
2274 lines
69 KiB
2274 lines
69 KiB
|
|
/*++
|
|
|
|
Copyright (c) 1997-1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ds.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code to process OS Chooser message
|
|
for the BINL server.
|
|
|
|
Author:
|
|
|
|
Adam Barr (adamba) 9-Jul-1997
|
|
Geoff Pease (gpease) 10-Nov-1997
|
|
|
|
Environment:
|
|
|
|
User Mode - Win32
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "binl.h"
|
|
#pragma hdrstop
|
|
|
|
#include <math.h> // pow() function
|
|
|
|
#include <riname.h>
|
|
|
|
#include <riname.c>
|
|
|
|
DWORD
|
|
OscGetUserDetails (
|
|
PCLIENT_STATE clientState
|
|
)
|
|
//
|
|
// This function fills in USERDOMAIN, USERFIRSTNAME, USERLASTNAME, USEROU in
|
|
// the client state. Also fills in ROOTDOMAIN for root of enterprise.
|
|
//
|
|
{
|
|
DWORD Error = ERROR_SUCCESS;
|
|
DWORD Count;
|
|
|
|
LPWSTR pszUserName = OscFindVariableW( clientState, "USERNAME" );
|
|
LPWSTR pUserDomain = OscFindVariableW( clientState, "USERDOMAIN" );
|
|
LPWSTR pUserOU = OscFindVariableW( clientState, "USEROU" );
|
|
LPWSTR pUserFullName = OscFindVariableW( clientState, "USERFULLNAME" );
|
|
|
|
PLDAP LdapHandle;
|
|
PLDAPMessage LdapMessage = NULL;
|
|
PWSTR Filter = NULL;
|
|
PWCHAR ldapAttributes[5];
|
|
BOOLEAN impersonating = FALSE;
|
|
PLDAPMessage ldapEntry;
|
|
PWCHAR *ldapConfigContainer = NULL;
|
|
PWCHAR *ldapDomain = NULL;
|
|
PWCHAR *ldapFirstName = NULL;
|
|
PWCHAR *ldapLastName = NULL;
|
|
PWCHAR *ldapDisplayName = NULL;
|
|
PWCHAR *ldapAccountName = NULL;
|
|
BOOLEAN allocatedContainer = FALSE;
|
|
PWCHAR configContainer = NULL;
|
|
BOOLEAN firstNameValid = FALSE;
|
|
BOOLEAN lastNameValid = FALSE;
|
|
|
|
BOOLEAN userFullNameSet = FALSE;
|
|
|
|
PLDAPControlW controlArray[2];
|
|
LDAPControlW controlNoReferrals;
|
|
ULONG noReferralsPlease;
|
|
|
|
PWCHAR ldapUserDN = NULL;
|
|
PWCHAR *explodedDN = NULL;
|
|
PWCHAR dnUsersOU = NULL;
|
|
|
|
TraceFunc( "OscGetUserDetails( )\n" );
|
|
if ( pszUserName[0] == L'\0' ) {
|
|
OscAddVariableA( clientState, "SUBERROR", "USERNAME" );
|
|
return ERROR_BINL_MISSING_VARIABLE;
|
|
}
|
|
|
|
//
|
|
// If the USERFULLNAME variable already exists, we won't change it below.
|
|
// But if it came back as an empty string, that might actually mean
|
|
// that the variable doesn't exist. In such a case, when SearchAndReplace
|
|
// processes the .SIF file for the client, it will leave occurrences of
|
|
// "%USERFULLNAME%" alone -- it won't replace them with "". We don't want
|
|
// "%USERFULLNAME% to hang around, so we explicitly set it to an empty
|
|
// string if it doesn't already exist or is an empty string. We do the
|
|
// same thing with USERFIRSTNAME, USERLASTNAME, and USERDISPLAYNAME.
|
|
//
|
|
|
|
if (pUserFullName[0] != L'\0') {
|
|
userFullNameSet = TRUE;
|
|
} else {
|
|
OscAddVariableW( clientState, "USERFULLNAME", L"" );
|
|
}
|
|
|
|
{
|
|
LPWSTR name;
|
|
name = OscFindVariableW( clientState, "USERFIRSTNAME" );
|
|
if (name[0] == L'\0') {
|
|
OscAddVariableW( clientState, "USERFIRSTNAME", L"" );
|
|
}
|
|
name = OscFindVariableW( clientState, "USERLASTNAME" );
|
|
if (name[0] == L'\0') {
|
|
OscAddVariableW( clientState, "USERLASTNAME", L"" );
|
|
}
|
|
name = OscFindVariableW( clientState, "USERDISPLAYNAME" );
|
|
if (name[0] == L'\0') {
|
|
OscAddVariableW( clientState, "USERDISPLAYNAME", L"" );
|
|
}
|
|
}
|
|
|
|
if ( pUserOU[0] != L'\0' ) {
|
|
|
|
//
|
|
// if we've already found this user's info, bail here with success.
|
|
//
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// if the users domain and the servers domain don't match,
|
|
// then try connecting to the DC for the new domain. If we
|
|
// don't do this, then we won't necessarily be able to get
|
|
// the correct information about the user. By connecting to
|
|
// the new DC, we get the clientState to cache some information
|
|
// about the new domain.
|
|
//
|
|
if (pUserDomain[0] != L'\0' ) {
|
|
PWSTR CrossDC = OscFindVariableW( clientState, "DCNAME" );
|
|
if ( (CrossDC[0] == L'\0') &&
|
|
(_wcsicmp(pUserDomain, BinlGlobalOurDomainName) != 0)) {
|
|
HANDLE hDC;
|
|
PSTR pUserDomainA = OscFindVariableA( clientState, "USERDOMAIN" );
|
|
Error = MyGetDcHandle(clientState, pUserDomainA,&hDC);
|
|
if (Error == ERROR_SUCCESS) {
|
|
DsUnBindA(&hDC);
|
|
}
|
|
}
|
|
}
|
|
|
|
Error = OscImpersonate(clientState);
|
|
if (Error != ERROR_SUCCESS) {
|
|
BinlPrintDbg((DEBUG_ERRORS,
|
|
"OscGetUserDetails: OscImpersonate failed %lx\n", Error));
|
|
return Error;
|
|
}
|
|
impersonating = TRUE;
|
|
|
|
BinlAssert( clientState->AuthenticatedDCLdapHandle != NULL );
|
|
|
|
LdapHandle = clientState->AuthenticatedDCLdapHandle;
|
|
|
|
//
|
|
// we first look up the configuration and default container, we'll need
|
|
// one or the other, based on whether we have a domain name or not.
|
|
//
|
|
|
|
ldapAttributes[0] = L"configurationNamingContext";
|
|
ldapAttributes[1] = L"rootDomainNamingContext";
|
|
ldapAttributes[2] = NULL;
|
|
|
|
Error = ldap_search_ext_sW(LdapHandle,
|
|
NULL,
|
|
LDAP_SCOPE_BASE,
|
|
L"(objectClass=*)",
|
|
ldapAttributes,
|
|
FALSE,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
0,
|
|
&LdapMessage);
|
|
|
|
if (Error == LDAP_SUCCESS) {
|
|
|
|
Count = ldap_count_entries( LdapHandle, LdapMessage );
|
|
|
|
if (Count > 0) {
|
|
|
|
ldapEntry = ldap_first_entry( LdapHandle, LdapMessage );
|
|
|
|
if (ldapEntry != NULL) {
|
|
|
|
ldapConfigContainer = ldap_get_valuesW( LdapHandle,
|
|
ldapEntry,
|
|
L"configurationNamingContext" );
|
|
|
|
ldapDomain = ldap_get_valuesW( LdapHandle,
|
|
ldapEntry,
|
|
L"rootDomainNamingContext" );
|
|
|
|
if (ldapDomain != NULL &&
|
|
*ldapDomain != NULL &&
|
|
**ldapDomain != L'\0') {
|
|
|
|
OscAddVariableW( clientState, "ROOTDOMAIN", *ldapDomain );
|
|
}
|
|
}
|
|
} else {
|
|
LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR,
|
|
LdapGetLastError(),
|
|
LdapHandle
|
|
);
|
|
}
|
|
} else {
|
|
LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR,
|
|
LdapGetLastError(),
|
|
LdapHandle
|
|
);
|
|
}
|
|
if (LdapMessage) {
|
|
ldap_msgfree( LdapMessage );
|
|
LdapMessage = NULL;
|
|
}
|
|
|
|
//
|
|
// we either have the config container or the default domain DN. If
|
|
// we only have the config container, go get the correct domain DN.
|
|
//
|
|
|
|
if ( pUserDomain[0] != L'\0' ) {
|
|
|
|
//
|
|
// Since the user specified a domain, remove the defaulting to the same domain
|
|
// as the RIS server.
|
|
//
|
|
ldapDomain = NULL;
|
|
|
|
//
|
|
// if a domain was specified, then we look it up to find the baseDN
|
|
//
|
|
// we fail if we didn't get the config container
|
|
//
|
|
|
|
if (ldapConfigContainer == NULL ||
|
|
*ldapConfigContainer == NULL ||
|
|
**ldapConfigContainer == L'\0') {
|
|
|
|
if (Error == LDAP_SUCCESS) {
|
|
Error = LDAP_NO_SUCH_ATTRIBUTE;
|
|
}
|
|
BinlPrintDbg((DEBUG_ERRORS,
|
|
"OscGetUserDetails: get config container failed %lx\n", Error));
|
|
Error = LdapMapErrorToWin32( Error );
|
|
goto exitGetUserDetails;
|
|
}
|
|
|
|
//
|
|
// we then tack on "CN=Partitions," to search the partitions container
|
|
//
|
|
// sizeof contains the \0 in it.
|
|
//
|
|
Count = wcslen( *ldapConfigContainer ) + (sizeof( L"CN=Partitions,")/sizeof(WCHAR));
|
|
|
|
configContainer = BinlAllocateMemory( Count * sizeof(WCHAR) );
|
|
|
|
if (configContainer == NULL) {
|
|
|
|
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
goto exitGetUserDetails;
|
|
}
|
|
|
|
if (_snwprintf(configContainer,
|
|
Count,
|
|
L"CN=Partitions,%ws",
|
|
*ldapConfigContainer) < 0) {
|
|
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
goto exitGetUserDetails; // this frees configContainer
|
|
}
|
|
configContainer[Count-1] = L'\0';
|
|
|
|
//
|
|
// then we find the correct partition, we ignore the enterprise and
|
|
// enterprise schema entries by specifying a non-empty netbios name.
|
|
//
|
|
|
|
ldapAttributes[0] = L"NCName";
|
|
ldapAttributes[1] = NULL;
|
|
|
|
Filter = BinlAllocateMemory(
|
|
((wcslen(pUserDomain) * 2) +
|
|
(sizeof(L"(&(objectClass=CrossRef)(netbiosName=*)(|(dnsRoot=)(cn=)))")/sizeof(WCHAR)))
|
|
* sizeof(WCHAR) );
|
|
if (!Filter) {
|
|
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
goto exitGetUserDetails;
|
|
}
|
|
|
|
wsprintf(Filter,
|
|
L"(&(objectClass=CrossRef)(netbiosName=*)(|(dnsRoot=%s)(cn=%s)))",
|
|
pUserDomain,
|
|
pUserDomain);
|
|
|
|
|
|
Error = ldap_search_ext_sW(LdapHandle,
|
|
configContainer,
|
|
LDAP_SCOPE_ONELEVEL,
|
|
Filter,
|
|
ldapAttributes,
|
|
FALSE,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
0,
|
|
&LdapMessage);
|
|
|
|
if (Error == LDAP_SUCCESS) {
|
|
|
|
Count = ldap_count_entries( LdapHandle, LdapMessage );
|
|
|
|
if (Count > 0) {
|
|
|
|
PWCHAR *ldapDomainFromPartition = NULL;
|
|
|
|
ldapEntry = ldap_first_entry( LdapHandle,
|
|
LdapMessage );
|
|
|
|
if (ldapEntry != NULL) {
|
|
|
|
ldapDomainFromPartition = ldap_get_valuesW( LdapHandle,
|
|
ldapEntry,
|
|
L"NCName" );
|
|
if (ldapDomainFromPartition != NULL) {
|
|
|
|
//
|
|
// if we read a valid DN from the partitions container,
|
|
// we free the default one and switch over to the
|
|
// one we just found.
|
|
//
|
|
|
|
if (*ldapDomainFromPartition != NULL &&
|
|
**ldapDomainFromPartition != L'\0') {
|
|
|
|
ldap_value_free( ldapDomain );
|
|
ldapDomain = ldapDomainFromPartition;
|
|
|
|
} else {
|
|
|
|
ldap_value_free( ldapDomainFromPartition );
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR, LdapGetLastError(), LdapHandle);
|
|
|
|
}
|
|
} else {
|
|
LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR, LdapGetLastError(), LdapHandle);
|
|
}
|
|
|
|
if (LdapMessage) {
|
|
ldap_msgfree( LdapMessage );
|
|
LdapMessage = NULL;
|
|
}
|
|
|
|
} else if ((ldapDomain != NULL) && (*ldapDomain != NULL) && (**ldapDomain != L'\0')) {
|
|
|
|
//
|
|
// Add the user's domain as a variable to the client state.
|
|
//
|
|
OscAddVariableW( clientState, "USERDOMAIN", *ldapDomain );
|
|
pUserDomain = OscFindVariableW( clientState, "USERDOMAIN" );
|
|
}
|
|
|
|
if (ldapDomain == NULL ||
|
|
*ldapDomain == NULL ||
|
|
**ldapDomain == L'\0') {
|
|
|
|
if (Error == LDAP_SUCCESS) {
|
|
Error = LDAP_NO_SUCH_ATTRIBUTE;
|
|
}
|
|
BinlPrintDbg((DEBUG_ERRORS,
|
|
"OscGetUserDetails: get default domain failed %lx\n", Error));
|
|
Error = LdapMapErrorToWin32( Error );
|
|
goto exitGetUserDetails;
|
|
}
|
|
|
|
|
|
//
|
|
// go find the user's first name, last name, display name,
|
|
// and account name from the DS.
|
|
//
|
|
|
|
ldapAttributes[0] = &L"givenName";
|
|
ldapAttributes[1] = &L"sn";
|
|
ldapAttributes[2] = &L"displayName";
|
|
ldapAttributes[3] = &L"cn";
|
|
ldapAttributes[4] = NULL;
|
|
|
|
BinlFreeMemory( Filter );
|
|
Filter = BinlAllocateMemory(
|
|
(wcslen( pszUserName ) + (sizeof(L"(&(objectClass=user)(samAccountName=))")/sizeof(WCHAR)))
|
|
* sizeof(WCHAR));
|
|
|
|
if (!Filter) {
|
|
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
goto exitGetUserDetails;
|
|
}
|
|
|
|
wsprintf( Filter, L"(&(objectClass=user)(samAccountName=%s))", pszUserName );
|
|
|
|
//
|
|
// we really don't want it to go chasing referrals over the entire
|
|
// enterprise since we know what the domain is but we do want to chase
|
|
// externals.
|
|
//
|
|
|
|
noReferralsPlease = (ULONG)((ULONG_PTR)LDAP_CHASE_EXTERNAL_REFERRALS);
|
|
controlNoReferrals.ldctl_oid = LDAP_CONTROL_REFERRALS_W;
|
|
controlNoReferrals.ldctl_value.bv_len = sizeof(ULONG);
|
|
controlNoReferrals.ldctl_value.bv_val = (PCHAR) &noReferralsPlease;
|
|
controlNoReferrals.ldctl_iscritical = FALSE;
|
|
|
|
controlArray[0] = &controlNoReferrals;
|
|
controlArray[1] = NULL;
|
|
|
|
Error = ldap_search_ext_sW(LdapHandle,
|
|
*ldapDomain,
|
|
LDAP_SCOPE_SUBTREE,
|
|
Filter,
|
|
ldapAttributes,
|
|
FALSE,
|
|
NULL,
|
|
&controlArray[0],
|
|
0,
|
|
1,
|
|
&LdapMessage);
|
|
|
|
if (Error == LDAP_SUCCESS) {
|
|
|
|
Count = ldap_count_entries( LdapHandle, LdapMessage );
|
|
|
|
if (Count > 0) {
|
|
|
|
ldapEntry = ldap_first_entry( LdapHandle, LdapMessage );
|
|
|
|
if (ldapEntry != NULL) {
|
|
|
|
ldapFirstName = ldap_get_valuesW( LdapHandle,
|
|
ldapEntry,
|
|
L"givenName" );
|
|
|
|
if (ldapFirstName != NULL &&
|
|
*ldapFirstName != NULL &&
|
|
**ldapFirstName != L'\0') {
|
|
|
|
OscAddVariableW( clientState, "USERFIRSTNAME", *ldapFirstName );
|
|
firstNameValid = TRUE;
|
|
}
|
|
|
|
ldapLastName = ldap_get_valuesW( LdapHandle,
|
|
ldapEntry,
|
|
L"sn" );
|
|
if (ldapLastName != NULL &&
|
|
*ldapLastName != NULL &&
|
|
**ldapLastName != L'\0') {
|
|
|
|
OscAddVariableW( clientState, "USERLASTNAME", *ldapLastName );
|
|
lastNameValid = TRUE;
|
|
}
|
|
|
|
//
|
|
// Now that we have first and last name, set the USERFULLNAME
|
|
// if either is not empty.
|
|
//
|
|
|
|
if ((firstNameValid || lastNameValid) && (userFullNameSet == FALSE)) {
|
|
|
|
ULONG userFullNameLength = 0;
|
|
PWCHAR userFullName;
|
|
|
|
if (firstNameValid) {
|
|
userFullNameLength = (wcslen(*ldapFirstName) + 1) * sizeof(WCHAR);
|
|
}
|
|
if (lastNameValid) {
|
|
if (firstNameValid) {
|
|
userFullNameLength += sizeof(WCHAR); // for the space
|
|
}
|
|
userFullNameLength += (wcslen(*ldapLastName) + 1) * sizeof(WCHAR);
|
|
}
|
|
|
|
userFullName = BinlAllocateMemory(userFullNameLength);
|
|
if (userFullName != NULL) {
|
|
userFullName[0] = L'\0';
|
|
if (firstNameValid) {
|
|
wcscat(userFullName, *ldapFirstName);
|
|
}
|
|
if (lastNameValid) {
|
|
if (firstNameValid) {
|
|
wcscat(userFullName, L" ");
|
|
}
|
|
wcscat(userFullName, *ldapLastName);
|
|
}
|
|
OscAddVariableW( clientState, "USERFULLNAME", userFullName);
|
|
BinlFreeMemory(userFullName);
|
|
userFullNameSet = TRUE;
|
|
}
|
|
}
|
|
|
|
ldapDisplayName = ldap_get_valuesW( LdapHandle,
|
|
ldapEntry,
|
|
L"displayName" );
|
|
if (ldapDisplayName != NULL &&
|
|
*ldapDisplayName != NULL &&
|
|
**ldapDisplayName != L'\0') {
|
|
|
|
OscAddVariableW( clientState, "USERDISPLAYNAME", *ldapDisplayName );
|
|
if (!userFullNameSet) {
|
|
OscAddVariableW( clientState, "USERFULLNAME", *ldapDisplayName );
|
|
userFullNameSet = TRUE;
|
|
}
|
|
}
|
|
|
|
ldapAccountName = ldap_get_valuesW( LdapHandle,
|
|
ldapEntry,
|
|
L"cn" );
|
|
if (ldapAccountName != NULL &&
|
|
*ldapAccountName != NULL &&
|
|
**ldapAccountName != L'\0') {
|
|
|
|
OscAddVariableW( clientState, "USERACCOUNTNAME", *ldapAccountName );
|
|
if (!userFullNameSet) {
|
|
OscAddVariableW( clientState, "USERFULLNAME", *ldapAccountName );
|
|
userFullNameSet = TRUE;
|
|
}
|
|
}
|
|
|
|
ldapUserDN = ldap_get_dnW( LdapHandle, ldapEntry );
|
|
|
|
if (ldapUserDN != NULL) {
|
|
|
|
explodedDN = ldap_explode_dnW( ldapUserDN, 0);
|
|
|
|
if (explodedDN != NULL &&
|
|
*explodedDN != NULL &&
|
|
*(explodedDN+1) != NULL ) {
|
|
|
|
//
|
|
// if there's less than two components, we can't do
|
|
// anything with this DN.
|
|
//
|
|
|
|
PWCHAR component;
|
|
ULONG requiredSize = 1; // 1 for null terminator
|
|
|
|
//
|
|
// we now have an array of strings, each of which
|
|
// is a component of the DN. This is the safe and
|
|
// correct way to chop off the first element.
|
|
//
|
|
|
|
Count = 1;
|
|
while ((component = explodedDN[Count++]) != NULL) {
|
|
|
|
requiredSize += wcslen( component ) + 1; // 1 for the comma
|
|
}
|
|
|
|
dnUsersOU = BinlAllocateMemory( requiredSize * sizeof(WCHAR) );
|
|
|
|
if (dnUsersOU != NULL) {
|
|
|
|
wcscpy( dnUsersOU, explodedDN[1] );
|
|
Count = 2;
|
|
while ((component = explodedDN[Count++]) != NULL) {
|
|
|
|
wcscat( dnUsersOU, L"," );
|
|
wcscat( dnUsersOU, component );
|
|
}
|
|
|
|
OscAddVariableW( clientState, "USEROU", dnUsersOU );
|
|
|
|
} else {
|
|
|
|
BinlPrintDbg((DEBUG_ERRORS,
|
|
"OscGetUserDetails: unable to allocate %lx for user OU\n",
|
|
requiredSize * sizeof(WCHAR)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR,
|
|
LdapGetLastError(),
|
|
LdapHandle
|
|
);
|
|
}
|
|
} else {
|
|
LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR,
|
|
LdapGetLastError(),
|
|
LdapHandle
|
|
);
|
|
}
|
|
|
|
if (LdapMessage) {
|
|
ldap_msgfree( LdapMessage );
|
|
LdapMessage = NULL;
|
|
}
|
|
|
|
Error = ERROR_SUCCESS;
|
|
|
|
exitGetUserDetails:
|
|
|
|
if (dnUsersOU != NULL) {
|
|
BinlFreeMemory( dnUsersOU );
|
|
}
|
|
if (explodedDN != NULL) {
|
|
ldap_value_free( explodedDN );
|
|
}
|
|
if (ldapUserDN != NULL) {
|
|
ldap_memfree( ldapUserDN );
|
|
}
|
|
if (ldapConfigContainer != NULL) {
|
|
ldap_value_free( ldapConfigContainer );
|
|
}
|
|
if (ldapDomain != NULL) {
|
|
ldap_value_free( ldapDomain );
|
|
}
|
|
if (ldapFirstName != NULL) {
|
|
ldap_value_free( ldapFirstName );
|
|
}
|
|
if (ldapLastName != NULL) {
|
|
ldap_value_free( ldapLastName );
|
|
}
|
|
if (ldapDisplayName != NULL) {
|
|
ldap_value_free( ldapDisplayName );
|
|
}
|
|
if (ldapAccountName != NULL) {
|
|
ldap_value_free( ldapAccountName );
|
|
}
|
|
if (impersonating) {
|
|
OscRevert( clientState );
|
|
}
|
|
|
|
if (Filter != NULL) {
|
|
BinlFreeMemory( Filter );
|
|
}
|
|
|
|
if (configContainer != NULL) {
|
|
BinlFreeMemory( configContainer );
|
|
}
|
|
return Error;
|
|
}
|
|
|
|
DWORD
|
|
OscCreateAccount(
|
|
PCLIENT_STATE clientState,
|
|
PCREATE_DATA CreateData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates an account for the client specified by
|
|
RequestContext and writes the response in CreateData, which
|
|
will be sent down to the client.
|
|
|
|
It also creates the client's base image directory.
|
|
|
|
Arguments:
|
|
|
|
clientState - client state information
|
|
|
|
CreateData - The block of data that will be sent down to the
|
|
client if the account is successfully created.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DWORD Error;
|
|
PWSTR pMachineName;
|
|
PWSTR pMachineDN = NULL;
|
|
PWSTR pMachineOU;
|
|
PWSTR pServerName;
|
|
PWSTR pInstallPath;
|
|
|
|
WCHAR SetupPath[MAX_PATH];
|
|
PWSTR pNameDollarSign;
|
|
ULONG HostNameSize;
|
|
UINT uSize;
|
|
LPSTR pGuid;
|
|
PWCHAR pStrings[3];
|
|
|
|
MACHINE_INFO MachineInfo = { 0 };
|
|
|
|
TraceFunc("OscCreateAccount( )\n");
|
|
|
|
pMachineName = OscFindVariableW( clientState, "MACHINENAME" );
|
|
pNameDollarSign = OscFindVariableW( clientState, "NETBIOSNAME" );
|
|
|
|
//
|
|
// Convert the GUID
|
|
//
|
|
pGuid = OscFindVariableA( clientState, "GUID" );
|
|
Error = OscGuidToBytes( pGuid, MachineInfo.Guid );
|
|
if ( Error != ERROR_SUCCESS )
|
|
goto e0;
|
|
|
|
if (clientState->fCreateNewAccount) {
|
|
|
|
//
|
|
// Create client's FQDN(DS)
|
|
//
|
|
pMachineOU = OscFindVariableW( clientState, "MACHINEOU" );
|
|
BinlAssert( pMachineOU[0] != L'\0' );
|
|
uSize = wcslen( pMachineName ) * sizeof(WCHAR)
|
|
+ wcslen( pMachineOU ) * sizeof(WCHAR)
|
|
+ sizeof(L"CN=,"); // includes terminating NULL char
|
|
pMachineDN = (PWCHAR) BinlAllocateMemory( uSize );
|
|
if ( !pMachineDN ) {
|
|
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
goto e0;
|
|
}
|
|
wsprintf( pMachineDN, L"CN=%ws,%ws", pMachineName, pMachineOU );
|
|
OscAddVariableW( clientState, "MACHINEDN", pMachineDN );
|
|
|
|
} else {
|
|
|
|
pMachineDN = OscFindVariableW( clientState, "MACHINEDN" );
|
|
}
|
|
|
|
//
|
|
// Create the full setup path
|
|
//
|
|
pServerName = OscFindVariableW( clientState, "SERVERNAME");
|
|
pInstallPath = OscFindVariableW( clientState, "INSTALLPATH");
|
|
|
|
if (!pServerName || !pInstallPath ||
|
|
(_snwprintf(SetupPath,
|
|
MAX_PATH,
|
|
L"\\\\%ws\\REMINST\\%ws",
|
|
pServerName ,
|
|
pInstallPath) < 0 )) {
|
|
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
goto e0;
|
|
}
|
|
SetupPath[MAX_PATH-1] = L'\0';
|
|
|
|
EnterCriticalSection( &gcsParameters );
|
|
MachineInfo.HostName = BinlStrDupW(BinlGlobalOurDnsName );
|
|
LeaveCriticalSection( &gcsParameters );
|
|
|
|
if ( !MachineInfo.HostName ) {
|
|
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
goto e0;
|
|
}
|
|
|
|
//
|
|
// Fill in the rest of the MachineInfo structure
|
|
//
|
|
MachineInfo.Name = pMachineName;
|
|
MachineInfo.MachineDN = pMachineDN;
|
|
#if 1
|
|
//
|
|
// Don't store BOOTFILE in the cache/DS, since BOOTFILE points to setupldr
|
|
// and we want the cache entry to point to oschooser. If we store an
|
|
// empty string in the cache/DS, then GetBootParametersExt() will replace
|
|
// that with the path to oschooser.
|
|
//
|
|
MachineInfo.BootFileName = L"";
|
|
#else
|
|
MachineInfo.BootFileName = OscFindVariableW( clientState, "BOOTFILE" );
|
|
#endif
|
|
MachineInfo.SetupPath = SetupPath;
|
|
MachineInfo.SamName = pNameDollarSign;
|
|
MachineInfo.Password = clientState->MachineAccountPassword;
|
|
MachineInfo.PasswordLength = clientState->MachineAccountPasswordLength;
|
|
MachineInfo.dwFlags = MI_NAME
|
|
| MI_HOSTNAME
|
|
| MI_BOOTFILENAME
|
|
| MI_SETUPPATH
|
|
| MI_SAMNAME
|
|
| MI_PASSWORD
|
|
| MI_MACHINEDN
|
|
| MI_GUID;
|
|
|
|
//
|
|
// Create the MAO in the DS
|
|
//
|
|
Error = UpdateAccount( clientState,
|
|
&MachineInfo,
|
|
clientState->fCreateNewAccount ); // create it
|
|
if ( Error ) {
|
|
goto e0;
|
|
}
|
|
|
|
//
|
|
// Create the response to the client
|
|
//
|
|
Error = OscConstructSecret(
|
|
clientState,
|
|
clientState->MachineAccountPassword,
|
|
clientState->MachineAccountPasswordLength,
|
|
CreateData );
|
|
if ( Error != ERROR_SUCCESS ) {
|
|
OscCreateWin32SubError( clientState, Error );
|
|
Error = ERROR_BINL_FAILED_TO_INITIALIZE_CLIENT;
|
|
goto e0;
|
|
}
|
|
|
|
BinlPrint(( DEBUG_OSC, "Successfully created account for <%ws>\n", pMachineName ));
|
|
pStrings[0] = pMachineName;
|
|
pStrings[1] = OscFindVariableW( clientState, "USERNAME" );
|
|
BinlReportEventW( EVENT_COMPUTER_ACCOUNT_CREATED_SUCCESSFULLY,
|
|
EVENTLOG_INFORMATION_TYPE,
|
|
2,
|
|
0,
|
|
pStrings,
|
|
0 );
|
|
|
|
e0:
|
|
// No need to call FreeMachineInfo() since all the information
|
|
// in it is either allocated on the stack or is referenced
|
|
// by the clientState, but we do need to free the HostName
|
|
// since it is allocated above.
|
|
if ( MachineInfo.HostName ) {
|
|
BinlFreeMemory( MachineInfo.HostName );
|
|
}
|
|
|
|
if ( pMachineDN && clientState->fCreateNewAccount ) {
|
|
BinlFreeMemory( pMachineDN );
|
|
}
|
|
return Error;
|
|
}
|
|
|
|
|
|
//
|
|
// CheckForDuplicateMachineName( )
|
|
//
|
|
DWORD
|
|
CheckForDuplicateMachineName(
|
|
PCLIENT_STATE clientState,
|
|
LPWSTR pszMachineName )
|
|
{
|
|
DWORD Error = ERROR_SUCCESS;
|
|
PLDAPMessage LdapMessage = NULL;
|
|
WCHAR Filter[128];
|
|
DWORD count;
|
|
PWCHAR ComputerAttrs[2];
|
|
LPWSTR pDomain = OscFindVariableW( clientState, "MACHINEOU" );
|
|
PWCHAR BaseDN;
|
|
PLDAP LdapHandle;
|
|
ULONG ldapRetryLimit = 0;
|
|
PWCHAR *gcBase;
|
|
|
|
PLDAPControlW controlArray[2];
|
|
LDAPControlW controlNoReferrals;
|
|
ULONG noReferralsPlease;
|
|
|
|
//
|
|
// see if binl is already in the process of registering the name with the DS
|
|
//
|
|
if (IsQueuedDSName(pszMachineName)) {
|
|
Error = -1; // signal multiple accounts
|
|
goto exitCheck;
|
|
}
|
|
|
|
ComputerAttrs[0] = &L"cn";
|
|
ComputerAttrs[1] = NULL;
|
|
|
|
TraceFunc( "CheckForDuplicateMachineName( )\n" );
|
|
|
|
if (pDomain[0] == L'\0') {
|
|
|
|
pDomain = OscFindVariableW( clientState, "USERDOMAIN" );
|
|
BinlPrintDbg((DEBUG_ERRORS, "CheckforDupMachine: couldn't find root domain, using user's domain %ws\n.", pDomain));
|
|
}
|
|
|
|
BaseDN = StrStrIW( pDomain, L"DC=" );
|
|
|
|
if (BaseDN == NULL) {
|
|
BaseDN = pDomain;
|
|
}
|
|
|
|
LdapHandle = clientState->AuthenticatedDCLdapHandle;
|
|
|
|
BinlAssert( LdapHandle != NULL );
|
|
|
|
//
|
|
// According to the DS guys, it's not necessarily the case that CN is
|
|
// equal to SamAccountName and the latter is the important one. It has
|
|
// a dollar sign at the end, so we'll tack that on.
|
|
//
|
|
|
|
if (_snwprintf( Filter,
|
|
sizeof(Filter)/sizeof(Filter[0]),
|
|
L"(&(objectClass=Computer)(samAccountName=%ws$))",
|
|
pszMachineName) < 0) {
|
|
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
goto exitCheck;
|
|
}
|
|
Filter[(sizeof(Filter)/sizeof(Filter[0]))-1] = L'\0';
|
|
|
|
//
|
|
// we really don't want it to go chasing subordinate referrals over
|
|
// the entire enterprise since we know what the domain is, therefore
|
|
// limit it to only external referrals (for child domains).
|
|
//
|
|
|
|
noReferralsPlease = (ULONG)((ULONG_PTR) LDAP_CHASE_EXTERNAL_REFERRALS);
|
|
controlNoReferrals.ldctl_oid = LDAP_CONTROL_REFERRALS_W;
|
|
controlNoReferrals.ldctl_value.bv_len = sizeof(ULONG);
|
|
controlNoReferrals.ldctl_value.bv_val = (PCHAR) &noReferralsPlease;
|
|
controlNoReferrals.ldctl_iscritical = FALSE;
|
|
|
|
controlArray[0] = &controlNoReferrals;
|
|
controlArray[1] = NULL;
|
|
|
|
Retry:
|
|
Error = ldap_search_ext_s(LdapHandle,
|
|
BaseDN,
|
|
LDAP_SCOPE_SUBTREE,
|
|
Filter,
|
|
ComputerAttrs,
|
|
FALSE,
|
|
NULL,
|
|
&controlArray[0],
|
|
0,
|
|
1,
|
|
&LdapMessage);
|
|
switch ( Error )
|
|
{
|
|
case LDAP_SUCCESS:
|
|
break;
|
|
|
|
case LDAP_BUSY:
|
|
if (++ldapRetryLimit < LDAP_BUSY_LIMIT) {
|
|
Sleep( LDAP_BUSY_DELAY );
|
|
goto Retry;
|
|
}
|
|
|
|
// lack of break is on purpose.
|
|
|
|
default:
|
|
OscCreateLDAPSubError( clientState, Error );
|
|
LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR,
|
|
Error,
|
|
LdapHandle
|
|
);
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "!!LdapError 0x%08x - Failed search to create machine name.\n", Error ));
|
|
goto exitCheck;
|
|
}
|
|
|
|
count = ldap_count_entries( LdapHandle, LdapMessage );
|
|
if ( count != 0 ) {
|
|
Error = -1; // signal multiple accounts
|
|
goto exitCheck;
|
|
}
|
|
|
|
ldap_msgfree( LdapMessage );
|
|
LdapMessage = NULL;
|
|
|
|
//
|
|
// now we go check the GC.
|
|
//
|
|
|
|
gcBase = NULL;
|
|
|
|
Error = InitializeConnection( TRUE, &LdapHandle, &gcBase );
|
|
if ( Error != ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// if no GC is present or available, we'll let this call succeed.
|
|
// Reasoning here is GCs can be flaky creatures.
|
|
//
|
|
Error = ERROR_SUCCESS;
|
|
goto exitCheck;
|
|
}
|
|
|
|
ldapRetryLimit = 0;
|
|
|
|
RetryGC:
|
|
Error = ldap_search_ext_s(LdapHandle,
|
|
*gcBase,
|
|
LDAP_SCOPE_SUBTREE,
|
|
Filter,
|
|
ComputerAttrs,
|
|
FALSE,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
1,
|
|
&LdapMessage);
|
|
switch ( Error )
|
|
{
|
|
case LDAP_SUCCESS:
|
|
break;
|
|
|
|
case LDAP_BUSY:
|
|
if (++ldapRetryLimit < LDAP_BUSY_LIMIT) {
|
|
Sleep( LDAP_BUSY_DELAY );
|
|
goto RetryGC;
|
|
}
|
|
|
|
// lack of break is on purpose.
|
|
|
|
default:
|
|
|
|
OscCreateLDAPSubError( clientState, Error );
|
|
|
|
HandleLdapFailure( Error,
|
|
EVENT_WARNING_LDAP_SEARCH_ERROR,
|
|
TRUE,
|
|
&LdapHandle,
|
|
FALSE ); // don't have the lock
|
|
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "!!LdapError 0x%08x - Failed search to create machine name.\n", Error ));
|
|
goto exitCheck;
|
|
}
|
|
|
|
count = ldap_count_entries( LdapHandle, LdapMessage );
|
|
|
|
if ( count != 0 ) {
|
|
|
|
Error = -1; // signal multiple accounts
|
|
|
|
} else {
|
|
|
|
Error = ERROR_SUCCESS;
|
|
}
|
|
|
|
exitCheck:
|
|
|
|
if (LdapMessage) {
|
|
ldap_msgfree( LdapMessage );
|
|
}
|
|
return Error;
|
|
}
|
|
|
|
//
|
|
// GenerateMachineName( )
|
|
//
|
|
|
|
DWORD
|
|
GenerateMachineName(
|
|
PCLIENT_STATE clientState
|
|
)
|
|
{
|
|
DWORD Error = ERROR_SUCCESS;
|
|
GENNAME_VARIABLES variables;
|
|
WCHAR szMachineName[ DNS_MAX_LABEL_BUFFER_LENGTH ];
|
|
DWORD Count = 1;
|
|
LPWSTR missingVariable;
|
|
BOOL usedCounter;
|
|
|
|
LPWSTR pszUserName;
|
|
LPWSTR pszFirstName;
|
|
LPWSTR pszLastName;
|
|
LPWSTR pUserOU;
|
|
LPWSTR pszMAC;
|
|
|
|
TraceFunc( "GenerateMachineName( )\n" );
|
|
|
|
pszUserName = OscFindVariableW( clientState, "USERNAME" );
|
|
|
|
if ( pszUserName[0] == L'\0' ) {
|
|
OscAddVariableA( clientState, "SUBERROR", "USERNAME" );
|
|
return ERROR_BINL_MISSING_VARIABLE;
|
|
}
|
|
|
|
Error = OscGetUserDetails( clientState );
|
|
|
|
if (Error != ERROR_SUCCESS) {
|
|
BinlPrintDbg((DEBUG_OSC_ERROR,
|
|
"GenerateMachineName: OscGetUserDetails failed %lx\n", Error));
|
|
return Error;
|
|
}
|
|
|
|
pszFirstName = OscFindVariableW( clientState, "USERFIRSTNAME" );
|
|
pszLastName = OscFindVariableW( clientState, "USERLASTNAME" );
|
|
pUserOU = OscFindVariableW( clientState, "USEROU" );
|
|
pszMAC = OscFindVariableW( clientState, "MAC" );
|
|
|
|
variables.UserName = pszUserName;
|
|
variables.FirstName = pszFirstName;
|
|
variables.LastName = pszLastName;
|
|
variables.MacAddress = pszMAC;
|
|
variables.AllowCounterTruncation = FALSE;
|
|
|
|
TryAgain:
|
|
|
|
variables.Counter = ++clientState->nCreateAccountCounter;
|
|
|
|
EnterCriticalSection( &gcsParameters );
|
|
|
|
Error = GenerateNameFromTemplate(
|
|
NewMachineNamingPolicy,
|
|
&variables,
|
|
szMachineName,
|
|
DNS_MAX_LABEL_BUFFER_LENGTH,
|
|
&missingVariable,
|
|
&usedCounter,
|
|
NULL
|
|
);
|
|
|
|
LeaveCriticalSection( &gcsParameters );
|
|
|
|
if ( (Error != GENNAME_NO_ERROR) && (Error != GENNAME_NAME_TOO_LONG) ) {
|
|
if ( Error == GENNAME_VARIABLE_MISSING ) {
|
|
OscAddVariableW( clientState, "SUBERROR", missingVariable );
|
|
clientState->nCreateAccountCounter = 0;
|
|
return ERROR_BINL_MISSING_VARIABLE;
|
|
}
|
|
BinlAssert( (Error == GENNAME_COUNTER_TOO_HIGH) || (Error == GENNAME_TEMPLATE_INVALID) );
|
|
clientState->nCreateAccountCounter = 0;
|
|
return ERROR_BINL_UNABLE_TO_GENERATE_MACHINE_NAME;
|
|
}
|
|
|
|
BinlPrint(( DEBUG_OSC, "Generated MachineName = %ws\n", szMachineName ));
|
|
|
|
//
|
|
// grab the global lock so that we can synchronously find a unique name.
|
|
// once we find a unique name, we then add it to the queued DS name list
|
|
// to prevent another thread from attempting to register the same name
|
|
//
|
|
EnterCriticalSection( &gcsParameters );
|
|
|
|
Error = CheckForDuplicateMachineName( clientState, szMachineName );
|
|
|
|
if ( Error == -1 ) {
|
|
if ( usedCounter ) {
|
|
LeaveCriticalSection( &gcsParameters );
|
|
goto TryAgain;
|
|
}
|
|
Error = ERROR_BINL_DUPLICATE_MACHINE_NAME_FOUND;
|
|
LeaveCriticalSection( &gcsParameters );
|
|
} else if ( Error == LDAP_SIZELIMIT_EXCEEDED ) {
|
|
BinlPrint(( DEBUG_OSC, "MachineName '%s' has mutliple accounts already.\n", szMachineName ));
|
|
if ( usedCounter ) {
|
|
LeaveCriticalSection( &gcsParameters );
|
|
goto TryAgain;
|
|
}
|
|
LeaveCriticalSection( &gcsParameters );
|
|
} else if ( Error != LDAP_SUCCESS ) {
|
|
Error = ERROR_BINL_UNABLE_TO_GENERATE_MACHINE_NAME;
|
|
LeaveCriticalSection( &gcsParameters );
|
|
} else {
|
|
|
|
//
|
|
// insert name into queued name list so another thread
|
|
// fails the CheckForDuplicateMachineName if they try
|
|
// to use the same name
|
|
//
|
|
Error = AddQueuedDSName(szMachineName);
|
|
|
|
LeaveCriticalSection( &gcsParameters );
|
|
|
|
if (Error == ERROR_SUCCESS) {
|
|
|
|
BinlPrintDbg(( DEBUG_OSC, "MachineName: '%ws'\n", szMachineName ));
|
|
|
|
Error = OscAddVariableW( clientState, "MACHINENAME", szMachineName );
|
|
|
|
if ( Error == ERROR_SUCCESS ) {
|
|
|
|
WCHAR NameDollarSign[17]; // MACHINENAME(15)+'$'+'\0'
|
|
UINT uSize;
|
|
|
|
clientState->fAutomaticMachineName = TRUE;
|
|
|
|
uSize = sizeof(NameDollarSign);
|
|
// DnsHostnameToComputerNameW takes BYTEs in and returns the # of WCHARs out.
|
|
if ( !DnsHostnameToComputerNameW( szMachineName, NameDollarSign, &uSize ) ) {
|
|
// if this fails(?), default to truncating machine name and
|
|
// add '$' to the end
|
|
BinlPrintDbg((DEBUG_OSC_ERROR, "!! Error 0x%08x - DnsHostnameToComputerNameW failed.\n", GetLastError() ));
|
|
BinlPrintDbg((DEBUG_OSC, "WARNING: Truncating machine name to 15 characters to generated NETBIOS name.\n" ));
|
|
memset( NameDollarSign, 0, sizeof(NameDollarSign) );
|
|
wcsncpy( NameDollarSign, szMachineName, 15 );
|
|
}
|
|
wcscat( NameDollarSign, L"$");
|
|
Error = OscAddVariableW( clientState, "NETBIOSNAME", NameDollarSign );
|
|
}
|
|
}
|
|
}
|
|
|
|
clientState->nCreateAccountCounter = 0;
|
|
|
|
return Error;
|
|
|
|
|
|
}
|
|
|
|
DWORD
|
|
OscCheckMachineDN(
|
|
PCLIENT_STATE clientState
|
|
)
|
|
//
|
|
// Ensure that the client name, OU, and domain are setup correctly. If there
|
|
// are duplicate records in the DS with this same guid, we'll return
|
|
// ERROR_BINL_DUPLICATE_MACHINE_NAME_FOUND and set %SUBERROR% string to
|
|
// those DNs and return an error.
|
|
//
|
|
{
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
PWCHAR pwc; // parsing pointer
|
|
WCHAR wch; // temp wide char
|
|
PWCHAR pMachineName; // Pointer to Machine Name variable value
|
|
PWCHAR pMachineOU; // Pointer to where the MAO will be created
|
|
PWCHAR pDomain; // Pointer to Domain variable name
|
|
PCHAR pGuid; // Pointer to Guid variable name
|
|
WCHAR NameDollarSign[17]; // MACHINENAME(15)+'$'+'\0'
|
|
WCHAR Path[MAX_PATH]; // general purpose path buffer
|
|
ULONG i; // general counter
|
|
BOOL b; // general purpose BOOLean.
|
|
UINT uSize;
|
|
UCHAR Guid[ BINL_GUID_LENGTH ];
|
|
PMACHINE_INFO pMachineInfo = NULL;
|
|
USHORT SystemArchitecture;
|
|
DWORD DupRecordCount;
|
|
|
|
TraceFunc("OscCheckMachineDN( )\n");
|
|
|
|
if ( clientState->fHaveSetupMachineDN ) {
|
|
|
|
// we've been through this logic before, just exit here with success.
|
|
dwErr = ERROR_SUCCESS;
|
|
goto e0;
|
|
}
|
|
|
|
dwErr = OscGetUserDetails( clientState );
|
|
|
|
if (dwErr != ERROR_SUCCESS) {
|
|
BinlPrintDbg((DEBUG_OSC_ERROR,
|
|
"OscCheckMachineDN: OscGetUserDetails failed %lx\n", dwErr));
|
|
goto e0;
|
|
}
|
|
|
|
pGuid = OscFindVariableA( clientState, "GUID" );
|
|
if ( pGuid[0] == '\0' ) {
|
|
OscAddVariableA( clientState, "SUBERROR", "GUID" );
|
|
dwErr = ERROR_BINL_MISSING_VARIABLE;
|
|
goto e0;
|
|
}
|
|
|
|
dwErr = OscGuidToBytes( pGuid, Guid );
|
|
if ( dwErr != ERROR_SUCCESS ) {
|
|
goto e0;
|
|
}
|
|
|
|
// Do we have a machine name yet?
|
|
clientState->fCreateNewAccount = TRUE;
|
|
pMachineName = OscFindVariableW( clientState, "MACHINENAME" );
|
|
if ( pMachineName[0] == L'\0' ) {
|
|
|
|
clientState->CustomInstall = FALSE;
|
|
|
|
} else {
|
|
|
|
clientState->CustomInstall = TRUE;
|
|
}
|
|
|
|
clientState->fHaveSetupMachineDN = TRUE;
|
|
|
|
|
|
SystemArchitecture = OscPlatformToArchitecture(clientState);
|
|
|
|
|
|
//
|
|
// See if the client already has an account with a matching GUID
|
|
//
|
|
dwErr = GetBootParameters( Guid,
|
|
&pMachineInfo,
|
|
MI_NAME | MI_DOMAIN | MI_MACHINEDN,
|
|
SystemArchitecture,
|
|
FALSE );
|
|
|
|
if (( dwErr == ERROR_SUCCESS ) &&
|
|
( !clientState->CustomInstall )) {
|
|
|
|
PWCHAR pszOU;
|
|
|
|
//
|
|
// Since we asked for these, they should be set.
|
|
//
|
|
ASSERT ( pMachineInfo->dwFlags & MI_NAME );
|
|
ASSERT ( pMachineInfo->dwFlags & MI_MACHINEDN );
|
|
|
|
//
|
|
// if this is an automatic install, then we simply set the
|
|
// account info to the account we found.
|
|
//
|
|
|
|
// skip the comma
|
|
pszOU = wcschr( pMachineInfo->MachineDN, L',' );
|
|
if (pszOU) {
|
|
pszOU++;
|
|
OscAddVariableW( clientState, "MACHINEOU", pszOU );
|
|
}
|
|
|
|
OscAddVariableW( clientState, "MACHINEDN", pMachineInfo->MachineDN );
|
|
|
|
dwErr = OscAddVariableW( clientState, "MACHINENAME", pMachineInfo->Name );
|
|
if ( dwErr != ERROR_SUCCESS ) {
|
|
BinlPrintDbg((DEBUG_OSC_ERROR,
|
|
"!!Error 0x%08x - OscCheckMachineDN: Unable to add MACHINENAME variable\n", dwErr ));
|
|
goto e0;
|
|
}
|
|
clientState->fCreateNewAccount = FALSE;
|
|
|
|
if ( pMachineInfo->dwFlags & MI_DOMAIN ) {
|
|
OscAddVariableW( clientState, "MACHINEDOMAIN", pMachineInfo->Domain );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Do we have an OU yet?
|
|
//
|
|
pMachineOU = OscFindVariableW( clientState, "MACHINEOU" );
|
|
if ( pMachineOU[0] == L'\0' ) {
|
|
|
|
//
|
|
// Here's how we determine the OU...
|
|
//
|
|
// if this is an auto, then MACHINEOU shouldn't already have
|
|
// been set by now. If it's custom, then MACHINEOU may be empty
|
|
// or it may be set to what the user wants it set to.
|
|
//
|
|
// if it's not already set, then we look at BinlGlobalDefaultContainer.
|
|
//
|
|
// if this value is equal to the server's DN, then we set it to the
|
|
// default for this domain.
|
|
//
|
|
// if BinlGlobalDefaultContainer is empty, then we set it to the
|
|
// user's OU.
|
|
//
|
|
|
|
if ( BinlGlobalServerDN == NULL ) {
|
|
|
|
dwErr = ERROR_BINL_NO_DN_AVAILABLE_FOR_SERVER;
|
|
BinlPrintDbg((DEBUG_OSC_ERROR,
|
|
"!!Error - OscCheckMachineDN: BinlGlobalServerDN is null\n", dwErr ));
|
|
goto e0;
|
|
}
|
|
|
|
EnterCriticalSection( &gcsParameters );
|
|
|
|
if ( BinlGlobalServerDN &&
|
|
_wcsicmp( BinlGlobalDefaultContainer, BinlGlobalServerDN ) == 0) {
|
|
|
|
//
|
|
// If the machine's OU is the same as this server's OU, then we set
|
|
// it to the default for this server's domain.
|
|
//
|
|
|
|
PWCHAR pDomainDefault = StrStrIW( BinlGlobalServerDN, L"DC=" );
|
|
ULONG dwErrGetDefault;
|
|
if ( pDomainDefault ) {
|
|
|
|
dwErrGetDefault = OscGetDefaultContainerForDomain( clientState, pDomainDefault );
|
|
|
|
//
|
|
// spit out an error but keep going, we'll try the user's OU in a bit.
|
|
//
|
|
if (dwErrGetDefault != ERROR_SUCCESS) {
|
|
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "Could not get default MACHINEOU, 0x%x\n",dwErrGetDefault));
|
|
}
|
|
}
|
|
} else {
|
|
|
|
dwErr = OscAddVariableW( clientState, "MACHINEOU", BinlGlobalDefaultContainer );
|
|
if ( dwErr != ERROR_SUCCESS ) {
|
|
LeaveCriticalSection( &gcsParameters );
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "!!Error 0x%08x - Could not add MACHINEOU\n", dwErr ));
|
|
goto e0;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection( &gcsParameters );
|
|
|
|
pMachineOU = OscFindVariableW( clientState, "MACHINEOU" );
|
|
if ( pMachineOU[0] == L'\0' ) {
|
|
|
|
LPWSTR pUserOU = OscFindVariableW( clientState, "USEROU" );
|
|
//
|
|
// the machine OU isn't already specified, that means we set it to
|
|
// the same as the user's OU.
|
|
//
|
|
|
|
if ( pUserOU[0] == L'\0' ) {
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "Missing UserOU variable\n" ));
|
|
OscAddVariableA( clientState, "SUBERROR", "USEROU" );
|
|
dwErr = ERROR_BINL_MISSING_VARIABLE;
|
|
goto e0;
|
|
}
|
|
|
|
dwErr = OscAddVariableW( clientState, "MACHINEOU", pUserOU );
|
|
if ( dwErr != ERROR_SUCCESS ) {
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "!!Error 0x%08x - Could not add MACHINEOU\n", dwErr ));
|
|
goto e0;
|
|
}
|
|
|
|
pMachineOU = OscFindVariableW( clientState, "MACHINEOU" );
|
|
}
|
|
}
|
|
|
|
//
|
|
// We need to generate the MACHINENAME after MACHINEOU because we need
|
|
// to know MACHINEOU to know which domain to check for duplicate
|
|
// machine names.
|
|
//
|
|
|
|
pMachineName = OscFindVariableW( clientState, "MACHINENAME" );
|
|
|
|
if ( pMachineName[0] == L'\0' ) {
|
|
|
|
dwErr = GenerateMachineName( clientState );
|
|
if ( dwErr != ERROR_SUCCESS ) {
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "!!Error 0x%08x - Failed to generate machine name\n" ));
|
|
goto e0;
|
|
}
|
|
// now we should have one
|
|
pMachineName = OscFindVariableW( clientState, "MACHINENAME" );
|
|
}
|
|
BinlAssertMsg( pMachineName[0] != L'\0', "Missing MACHINENAME" );
|
|
|
|
uSize = sizeof(NameDollarSign);
|
|
// DnsHostnameToComputerNameW takes BYTEs in and returns the # of WCHARs out.
|
|
if ( !DnsHostnameToComputerNameW( pMachineName, NameDollarSign, &uSize ) )
|
|
{
|
|
// if this fails(?), default to truncating machine name and
|
|
// add '$' to the end
|
|
BinlPrintDbg((DEBUG_OSC_ERROR, "!! Error 0x%08x - DnsHostnameToComputerNameW failed.\n", GetLastError( ) ));
|
|
BinlPrintDbg((DEBUG_OSC, "WARNING: Truncating machine name to 15 characters to generated NETBIOS name.\n" ));
|
|
memset( NameDollarSign, 0, sizeof(NameDollarSign) );
|
|
wcsncpy( NameDollarSign, pMachineName, 15 );
|
|
// don't return the error...
|
|
}
|
|
wcscat( NameDollarSign, L"$");
|
|
OscAddVariableW( clientState, "NETBIOSNAME", NameDollarSign );
|
|
|
|
// Do we have a domain yet?
|
|
pDomain = OscFindVariableW( clientState, "MACHINEDOMAIN" );
|
|
if ( pDomain[0] == L'\0' ) {
|
|
|
|
// skip to the first "DC="
|
|
pDomain = StrStrIW( pMachineOU, L"DC=" );
|
|
if ( pDomain ) {
|
|
|
|
PDS_NAME_RESULTW pResults;
|
|
|
|
dwErr = DsCrackNames( INVALID_HANDLE_VALUE,
|
|
DS_NAME_FLAG_SYNTACTICAL_ONLY,
|
|
DS_FQDN_1779_NAME,
|
|
DS_CANONICAL_NAME,
|
|
1,
|
|
&pDomain,
|
|
&pResults );
|
|
BinlAssertMsg( dwErr == ERROR_SUCCESS, "Error in DsCrackNames" );
|
|
|
|
if ( dwErr == ERROR_SUCCESS ) {
|
|
if ( pResults->cItems == 1
|
|
&& pResults->rItems[0].status == DS_NAME_NO_ERROR
|
|
&& pResults->rItems[0].pName ) { // paranoid
|
|
pResults->rItems[0].pName[wcslen(pResults->rItems[0].pName)-1] = L'\0';
|
|
OscAddVariableW( clientState, "MACHINEDOMAIN", pResults->rItems[0].pName );
|
|
}
|
|
|
|
DsFreeNameResult( pResults );
|
|
|
|
pDomain = OscFindVariableW( clientState, "MACHINEDOMAIN" );
|
|
} else {
|
|
pDomain = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// All else fails default to the servers
|
|
if ( !pDomain || pDomain[0] == '\0' )
|
|
{
|
|
OscAddVariableW( clientState,
|
|
"MACHINEDOMAIN",
|
|
OscFindVariableW( clientState, "SERVERDOMAIN" ) );
|
|
}
|
|
|
|
//
|
|
// check for duplicate accounts in the ds. fail if we find any, though
|
|
// we only fail after we have everything setup in case the user on
|
|
// custom install wants to ignore the error. For automatic, it's
|
|
// currently a fatal error but this could be changed in the osc screens.
|
|
//
|
|
|
|
if (( pMachineInfo != NULL ) &&
|
|
( pMachineInfo->dwFlags & MI_MACHINEDN )) {
|
|
|
|
PDUP_GUID_DN dupDN;
|
|
PLIST_ENTRY listEntry;
|
|
|
|
if (( pMachineInfo->dwFlags & MI_NAME ) &&
|
|
( clientState->CustomInstall )) {
|
|
|
|
//
|
|
// if this is a custom install, then we compare the account
|
|
// the user entered with all the existing accounts we found.
|
|
// We want to match both machine namd and OU (this is really
|
|
// just the DN but we have not necessarily constructed that
|
|
// yet).
|
|
//
|
|
// First we try the main entry in the cache, then all of
|
|
// the rest in the DNsWithSameGuid list.
|
|
//
|
|
|
|
// skip the comma
|
|
ULONG err;
|
|
PWCHAR MachineDNToUse;
|
|
PWCHAR pszOU = wcschr( pMachineInfo->MachineDN, L',' );
|
|
if (pszOU) {
|
|
pszOU++;
|
|
}
|
|
|
|
//
|
|
// See if the main machine name and OU in the cache
|
|
// entry match.
|
|
//
|
|
|
|
if ((CompareStringW(
|
|
LOCALE_SYSTEM_DEFAULT,
|
|
NORM_IGNORECASE,
|
|
pMachineName,
|
|
-1,
|
|
pMachineInfo->Name,
|
|
-1
|
|
) != 2)
|
|
||
|
|
((pszOU == NULL) && (pMachineOU[0] != L'\0'))
|
|
||
|
|
((pszOU != NULL) &&
|
|
(CompareStringW(
|
|
LOCALE_SYSTEM_DEFAULT,
|
|
NORM_IGNORECASE,
|
|
pMachineOU,
|
|
-1,
|
|
pszOU,
|
|
-1
|
|
) != 2))) {
|
|
|
|
//
|
|
// We did not match the main entry in the cache, so
|
|
// keep looking.
|
|
//
|
|
|
|
for (listEntry = pMachineInfo->DNsWithSameGuid.Flink;
|
|
listEntry != &pMachineInfo->DNsWithSameGuid;
|
|
listEntry = listEntry->Flink) {
|
|
|
|
dupDN = CONTAINING_RECORD(listEntry, DUP_GUID_DN, ListEntry);
|
|
|
|
pszOU = wcschr( &dupDN->DuplicateName[dupDN->DuplicateDNOffset], L',' );
|
|
if (pszOU) {
|
|
pszOU++;
|
|
}
|
|
|
|
if ((CompareStringW(
|
|
LOCALE_SYSTEM_DEFAULT,
|
|
NORM_IGNORECASE,
|
|
pMachineName,
|
|
-1,
|
|
dupDN->DuplicateName,
|
|
-1
|
|
) != 2)
|
|
||
|
|
((pszOU == NULL) && (pMachineOU[0] != L'\0'))
|
|
||
|
|
((pszOU != NULL) &&
|
|
(CompareStringW(
|
|
LOCALE_SYSTEM_DEFAULT,
|
|
NORM_IGNORECASE,
|
|
pMachineOU,
|
|
-1,
|
|
pszOU,
|
|
-1
|
|
) != 2))) {
|
|
|
|
//
|
|
// No match on this one.
|
|
//
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We found a match. Note which DN to use for
|
|
// this account.
|
|
//
|
|
|
|
MachineDNToUse = &dupDN->DuplicateName[dupDN->DuplicateDNOffset];
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we got to the end of our list with no match, jump to
|
|
// the error case.
|
|
//
|
|
|
|
if (listEntry == &pMachineInfo->DNsWithSameGuid) {
|
|
goto exitWithDupError;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// The main cache entry matched.
|
|
//
|
|
|
|
MachineDNToUse = pMachineInfo->MachineDN;
|
|
}
|
|
|
|
//
|
|
// We didn't jump to exitWithDupError above, so we found a match.
|
|
// we know that the client is using an existing account, let's
|
|
// mark the client state as such. this is the custom case.
|
|
//
|
|
clientState->fCreateNewAccount = FALSE;
|
|
|
|
OscAddVariableW( clientState, "MACHINEDN", MachineDNToUse );
|
|
|
|
if ( pMachineInfo->dwFlags & MI_DOMAIN ) {
|
|
OscAddVariableW( clientState, "MACHINEDOMAIN", pMachineInfo->Domain );
|
|
}
|
|
}
|
|
|
|
if (!IsListEmpty(&pMachineInfo->DNsWithSameGuid)) {
|
|
|
|
//
|
|
// if there's more than one account, we fill in SUBERROR
|
|
// with a list of the duplicates and return an error.
|
|
//
|
|
|
|
PWCHAR dnList;
|
|
ULONG requiredSize = 1; // 1 for null terminator
|
|
BOOL FreeDnList;
|
|
|
|
exitWithDupError:
|
|
//
|
|
// since we tack a <BR> to the end of each string, we'll account
|
|
// for it when we allocate the string as +4 from what we need.
|
|
//
|
|
#define MAX_DUPLICATE_RECORDS_TO_DISPLAY 4
|
|
|
|
requiredSize += wcslen( pMachineInfo->Name ) + (sizeof(L"<BR>")/sizeof(WCHAR));
|
|
listEntry = pMachineInfo->DNsWithSameGuid.Flink;
|
|
|
|
DupRecordCount = 0;
|
|
while (listEntry != &pMachineInfo->DNsWithSameGuid) {
|
|
|
|
dupDN = CONTAINING_RECORD(listEntry, DUP_GUID_DN, ListEntry);
|
|
listEntry = listEntry->Flink;
|
|
DupRecordCount += 1;
|
|
|
|
if (DupRecordCount <= MAX_DUPLICATE_RECORDS_TO_DISPLAY) {
|
|
requiredSize += wcslen( &dupDN->DuplicateName[0] ) + sizeof("<BR>");
|
|
} else if (DupRecordCount == MAX_DUPLICATE_RECORDS_TO_DISPLAY+1) {
|
|
requiredSize += sizeof( "..." ) + sizeof("<BR>");
|
|
}
|
|
}
|
|
|
|
dnList = BinlAllocateMemory( requiredSize * sizeof(WCHAR) );
|
|
|
|
DupRecordCount = 0;
|
|
if (dnList != NULL) {
|
|
|
|
ULONG nameLength;
|
|
|
|
nameLength = wcslen(pMachineInfo->Name);
|
|
FreeDnList = TRUE;
|
|
|
|
//
|
|
// The Name field should not end in a '$'.
|
|
//
|
|
|
|
ASSERT (!((nameLength > 1) && (pMachineInfo->Name[nameLength-1] == L'$')));
|
|
|
|
wcscpy( dnList, pMachineInfo->Name );
|
|
wcscat( dnList, L"<BR>" );
|
|
|
|
listEntry = pMachineInfo->DNsWithSameGuid.Flink;
|
|
|
|
while (listEntry != &pMachineInfo->DNsWithSameGuid) {
|
|
|
|
dupDN = CONTAINING_RECORD(listEntry, DUP_GUID_DN, ListEntry);
|
|
listEntry = listEntry->Flink;
|
|
|
|
DupRecordCount += 1;
|
|
|
|
if (DupRecordCount <= MAX_DUPLICATE_RECORDS_TO_DISPLAY) {
|
|
|
|
nameLength = wcslen(dupDN->DuplicateName);
|
|
|
|
//
|
|
// The DuplicateName field should not have the '$' either
|
|
//
|
|
|
|
ASSERT (!((nameLength > 1) && (dupDN->DuplicateName[nameLength-1] == L'$')));
|
|
|
|
wcscat( dnList, dupDN->DuplicateName );
|
|
wcscat( dnList, L"<BR>" );
|
|
|
|
} else if (DupRecordCount == MAX_DUPLICATE_RECORDS_TO_DISPLAY + 1) {
|
|
wcscat( dnList, L"..." );
|
|
wcscat( dnList, L"<BR>" );
|
|
}
|
|
}
|
|
} else {
|
|
FreeDnList = FALSE;
|
|
dnList = pMachineInfo->MachineDN;
|
|
}
|
|
|
|
OscAddVariableW( clientState, "SUBERROR", dnList );
|
|
if (FreeDnList) {
|
|
BinlFreeMemory( dnList );
|
|
}
|
|
dwErr = ERROR_BINL_DUPLICATE_MACHINE_NAME_FOUND;
|
|
}
|
|
} else {
|
|
//
|
|
// We must not exist in the DS yet so there cannot be a duplicate.
|
|
// set the error to successand return.
|
|
//
|
|
dwErr = ERROR_SUCCESS;
|
|
}
|
|
|
|
e0:
|
|
if ( pMachineInfo ) {
|
|
BinlDoneWithCacheEntry( pMachineInfo, FALSE );
|
|
}
|
|
return dwErr;
|
|
}
|
|
|
|
DWORD
|
|
OscGetDefaultContainerForDomain (
|
|
PCLIENT_STATE clientState,
|
|
PWCHAR DomainDN
|
|
)
|
|
{
|
|
PLDAP LdapHandle;
|
|
PLDAPMessage LdapMessage = NULL;
|
|
PWCHAR ldapAttributes[2];
|
|
BOOLEAN impersonating = FALSE;
|
|
PLDAPMessage ldapEntry;
|
|
PWCHAR *ldapWellKnownObjectValues = NULL;
|
|
PWCHAR objectValue;
|
|
PWCHAR guidEnd;
|
|
WCHAR savedChar;
|
|
ULONG Error = LDAP_NO_SUCH_ATTRIBUTE;
|
|
ULONG Count;
|
|
|
|
if (clientState->AuthenticatedDCLdapHandle == NULL) {
|
|
|
|
Error = OscImpersonate(clientState);
|
|
if (Error != ERROR_SUCCESS) {
|
|
BinlPrintDbg((DEBUG_ERRORS,
|
|
"OscGetDefaultContainer: OscImpersonate failed %lx\n", Error));
|
|
return Error;
|
|
}
|
|
impersonating = TRUE;
|
|
BinlAssert( clientState->AuthenticatedDCLdapHandle != NULL );
|
|
}
|
|
|
|
LdapHandle = clientState->AuthenticatedDCLdapHandle;
|
|
|
|
//
|
|
// we look up the wellKnownObjects in the root of the domain
|
|
//
|
|
|
|
ldapAttributes[0] = L"wellKnownObjects";
|
|
ldapAttributes[1] = NULL;
|
|
|
|
Error = ldap_search_ext_sW(LdapHandle,
|
|
DomainDN,
|
|
LDAP_SCOPE_BASE,
|
|
L"objectclass=*",
|
|
ldapAttributes,
|
|
FALSE,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
0,
|
|
&LdapMessage);
|
|
|
|
if (Error == LDAP_SUCCESS) {
|
|
Count = ldap_count_entries( LdapHandle, LdapMessage );
|
|
} else {
|
|
Count = 0;
|
|
}
|
|
|
|
Error = LDAP_NO_SUCH_ATTRIBUTE;
|
|
|
|
if (Count == 0) {
|
|
|
|
BinlPrintDbg((DEBUG_ERRORS,
|
|
"OscGetDefaultContainer: get default domain failed with no records found\n"));
|
|
LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR,
|
|
Error,
|
|
LdapHandle
|
|
);
|
|
goto exitGetDefaultContainer;
|
|
}
|
|
|
|
ldapEntry = ldap_first_entry( LdapHandle, LdapMessage );
|
|
|
|
if (ldapEntry == NULL) {
|
|
|
|
BinlPrintDbg((DEBUG_ERRORS,
|
|
"OscGetDefaultContainer: get first entry failed\n"));
|
|
goto exitGetDefaultContainer;
|
|
}
|
|
|
|
ldapWellKnownObjectValues = ldap_get_valuesW( LdapHandle,
|
|
ldapEntry,
|
|
L"wellKnownObjects" );
|
|
if (ldapWellKnownObjectValues == NULL) {
|
|
|
|
BinlPrintDbg((DEBUG_ERRORS,"OscGetDefaultContainer: get value failed\n"));
|
|
goto exitGetDefaultContainer;
|
|
}
|
|
|
|
Count = 0;
|
|
objectValue = NULL;
|
|
while (1) {
|
|
|
|
objectValue = ldapWellKnownObjectValues[Count++];
|
|
|
|
if (objectValue == NULL) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// the structure of this particular field is :
|
|
// L"B:32:GUID:DN" where GUID is AA312825768811D1ADED00C04FD8D5CD
|
|
//
|
|
|
|
if (wcslen( objectValue ) <
|
|
(sizeof( COMPUTER_DEFAULT_CONTAINER_IN_B32_FORM )/sizeof(WCHAR)) -1 ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// see if it matches "B:32:specialGuid:" then DN follows
|
|
//
|
|
|
|
guidEnd = objectValue + (sizeof( COMPUTER_DEFAULT_CONTAINER_IN_B32_FORM )/sizeof(WCHAR))-1;
|
|
savedChar = *guidEnd;
|
|
*guidEnd = L'\0';
|
|
|
|
if (_wcsicmp( objectValue, COMPUTER_DEFAULT_CONTAINER_IN_B32_FORM) != 0) {
|
|
|
|
*guidEnd = savedChar;
|
|
continue;
|
|
}
|
|
|
|
*guidEnd = savedChar; // this is the first character of the DN
|
|
|
|
//
|
|
// we have our value, now copy it off.
|
|
//
|
|
|
|
OscAddVariableW( clientState, "MACHINEOU", guidEnd );
|
|
|
|
Error = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
exitGetDefaultContainer:
|
|
|
|
if (ldapWellKnownObjectValues) {
|
|
ldap_value_free( ldapWellKnownObjectValues );
|
|
}
|
|
if (LdapMessage) {
|
|
ldap_msgfree( LdapMessage );
|
|
}
|
|
if (impersonating) {
|
|
OscRevert( clientState );
|
|
}
|
|
return Error;
|
|
}
|
|
|
|
VOID
|
|
LogLdapError (
|
|
ULONG LdapEvent,
|
|
ULONG LdapError,
|
|
PLDAP LdapHandle OPTIONAL
|
|
)
|
|
{
|
|
PWCHAR Server = NULL;
|
|
|
|
if (LdapError != LDAP_SUCCESS) {
|
|
|
|
if (LdapHandle != NULL) {
|
|
|
|
ldap_get_option( LdapHandle, LDAP_OPT_HOST_NAME, &Server );
|
|
}
|
|
|
|
if (++BinlGlobalLdapErrorCount <= BinlGlobalMaxLdapErrorsLogged) {
|
|
|
|
PWCHAR strings[2];
|
|
|
|
if (Server) {
|
|
strings[0] = Server;
|
|
} else {
|
|
strings[0] = L"?";
|
|
}
|
|
strings[1] = NULL;
|
|
|
|
BinlReportEventW( LdapEvent,
|
|
EVENTLOG_WARNING_TYPE,
|
|
(Server != NULL) ? 1 : 0,
|
|
sizeof(LdapError),
|
|
(Server != NULL) ? strings : NULL,
|
|
&LdapError
|
|
);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
DWORD
|
|
MyGetDcHandle(
|
|
PCLIENT_STATE clientState,
|
|
PCSTR DomainName,
|
|
PHANDLE Handle
|
|
)
|
|
{
|
|
DWORD Error;
|
|
HANDLE hDC;
|
|
PDOMAIN_CONTROLLER_INFOA DCI = NULL;
|
|
DWORD impersonateError = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
|
|
BinlPrintDbg((
|
|
DEBUG_OSC,
|
|
"Attempting discovery of DC in %s domain.\n",
|
|
DomainName ));
|
|
|
|
Error = DsGetDcNameA(
|
|
NULL,
|
|
DomainName,
|
|
NULL,
|
|
NULL,
|
|
DS_IS_DNS_NAME | DS_RETURN_DNS_NAME,
|
|
&DCI);
|
|
|
|
if (Error == ERROR_SUCCESS) {
|
|
|
|
BinlPrintDbg((
|
|
DEBUG_OSC,
|
|
"DC is %s, attempting bind.\n",
|
|
DCI->DomainControllerName ));
|
|
|
|
impersonateError = OscImpersonate(clientState);
|
|
|
|
Error = DsBindA(DCI->DomainControllerName, NULL, &hDC);
|
|
if (Error != ERROR_SUCCESS) {
|
|
BinlPrintDbg((
|
|
DEBUG_OSC_ERROR,
|
|
"DsBind failed, ec = %d.\n",
|
|
Error ));
|
|
} else {
|
|
PSTR p = DCI->DomainControllerName;
|
|
|
|
*Handle = hDC;
|
|
|
|
//
|
|
// if it's got '\\' in the front, then strip those
|
|
// off because ldap_init hates them
|
|
//
|
|
while (*p == '\\') {
|
|
p = p + 1;
|
|
}
|
|
|
|
OscAddVariableA( clientState, "DCNAME", p );
|
|
}
|
|
|
|
NetApiBufferFree(DCI);
|
|
|
|
} else {
|
|
BinlPrintDbg((
|
|
DEBUG_OSC_ERROR,
|
|
"DsGetDcNameA failed, ec = %d.\n",
|
|
Error ));
|
|
}
|
|
|
|
if (impersonateError == ERROR_SUCCESS) {
|
|
OscRevert(clientState);
|
|
}
|
|
|
|
return(Error);
|
|
|
|
}
|
|
|
|
DWORD
|
|
AddQueuedDSName(
|
|
PWCHAR Name
|
|
)
|
|
/*++
|
|
|
|
description:
|
|
|
|
this routine ADDS a name to the queued ds name list.
|
|
|
|
args:
|
|
|
|
Name - queued DS Name to add
|
|
|
|
return:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
DWORD Error = ERROR_SUCCESS;
|
|
PQUEUED_DS_NAME_NODE queuedDSName;
|
|
PLIST_ENTRY ListEntry;
|
|
ULONG ListEntrySize;
|
|
|
|
do {
|
|
|
|
//
|
|
//
|
|
//
|
|
ListEntrySize = sizeof(QUEUED_DS_NAME_NODE);
|
|
ListEntrySize += (wcslen(Name) + 1) * sizeof(WCHAR);
|
|
|
|
ListEntry = BinlAllocateMemory( ListEntrySize );
|
|
|
|
if (ListEntry == NULL) {
|
|
|
|
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
queuedDSName = CONTAINING_RECORD(ListEntry, QUEUED_DS_NAME_NODE, ListEntry);
|
|
|
|
wcscpy(queuedDSName->Name, Name);
|
|
|
|
//
|
|
//
|
|
//
|
|
EnterCriticalSection( &QueuedDSNamesCriticalSection );
|
|
|
|
InsertTailList(&QueuedDSNamesList, ListEntry);
|
|
|
|
LeaveCriticalSection( &QueuedDSNamesCriticalSection );
|
|
|
|
} while ( FALSE );
|
|
|
|
if (Error != ERROR_SUCCESS) {
|
|
if (ListEntry) {
|
|
BinlFreeMemory(ListEntry);
|
|
}
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
PLIST_ENTRY
|
|
FindQueuedDSName(
|
|
PWCHAR Name
|
|
)
|
|
/*++
|
|
|
|
description:
|
|
|
|
this routine attempts to find a name in the queued ds name list.
|
|
|
|
note: this routine assumes that the global list lock is held
|
|
by the caller
|
|
|
|
args:
|
|
|
|
Name - queued DS Name to find
|
|
|
|
return:
|
|
|
|
if found, pointer to listEntry is returned.
|
|
else NULL
|
|
|
|
--*/
|
|
{
|
|
PQUEUED_DS_NAME_NODE queuedDSName;
|
|
PLIST_ENTRY listEntry;
|
|
int compare;
|
|
BOOL found;
|
|
|
|
found = FALSE;
|
|
|
|
for (listEntry = QueuedDSNamesList.Flink;
|
|
listEntry != &QueuedDSNamesList;
|
|
listEntry = listEntry->Flink) {
|
|
|
|
queuedDSName = CONTAINING_RECORD(listEntry, QUEUED_DS_NAME_NODE, ListEntry);
|
|
|
|
compare = CompareStringW(
|
|
LOCALE_SYSTEM_DEFAULT,
|
|
NORM_IGNORECASE,
|
|
Name,
|
|
-1,
|
|
queuedDSName->Name,
|
|
-1
|
|
);
|
|
|
|
if (compare == CSTR_EQUAL) {
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
listEntry = NULL;
|
|
}
|
|
|
|
return listEntry;
|
|
}
|
|
|
|
DWORD
|
|
RemoveQueuedDSName(
|
|
PWCHAR Name
|
|
)
|
|
/*++
|
|
|
|
description:
|
|
|
|
this routine REMOVES a name to the queued ds name list.
|
|
|
|
args:
|
|
|
|
Name - queued DS Name to remove
|
|
|
|
return:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
DWORD Error = ERROR_SUCCESS;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
do {
|
|
|
|
//
|
|
//
|
|
//
|
|
EnterCriticalSection( &QueuedDSNamesCriticalSection );
|
|
|
|
ListEntry = FindQueuedDSName(Name);
|
|
|
|
if (ListEntry == NULL) {
|
|
Error = ERROR_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
RemoveEntryList(ListEntry);
|
|
|
|
} while ( FALSE );
|
|
|
|
LeaveCriticalSection( &QueuedDSNamesCriticalSection );
|
|
|
|
//
|
|
//
|
|
//
|
|
if (Error == ERROR_SUCCESS) {
|
|
|
|
BinlAssert( ListEntry != NULL );
|
|
|
|
BinlFreeMemory(ListEntry);
|
|
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
BOOL
|
|
IsQueuedDSName(
|
|
PWCHAR Name
|
|
)
|
|
/*++
|
|
|
|
description:
|
|
|
|
this routine determines if a name exits in the queued ds name list.
|
|
|
|
args:
|
|
|
|
Name - queued DS Name to find
|
|
|
|
return:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
BOOL bFound;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
bFound = FALSE;
|
|
|
|
//
|
|
//
|
|
//
|
|
EnterCriticalSection( &QueuedDSNamesCriticalSection );
|
|
|
|
ListEntry = FindQueuedDSName(Name);
|
|
|
|
if (ListEntry != NULL) {
|
|
bFound = TRUE;
|
|
}
|
|
|
|
LeaveCriticalSection( &QueuedDSNamesCriticalSection );
|
|
|
|
return bFound;
|
|
}
|
|
|
|
|
|
VOID
|
|
FreeQueuedDSNameList(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
description:
|
|
|
|
this routine any entries in the Queued DS Name list
|
|
|
|
args:
|
|
|
|
None
|
|
|
|
return:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY listEntry;
|
|
|
|
EnterCriticalSection( &QueuedDSNamesCriticalSection );
|
|
|
|
while(! IsListEmpty(&QueuedDSNamesList)) {
|
|
|
|
listEntry = RemoveHeadList(&QueuedDSNamesList);
|
|
|
|
BinlFreeMemory(listEntry);
|
|
|
|
}
|
|
|
|
BinlAssert(IsListEmpty(&QueuedDSNamesList));
|
|
|
|
LeaveCriticalSection( &QueuedDSNamesCriticalSection );
|
|
|
|
}
|
|
|