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.
356 lines
9.0 KiB
356 lines
9.0 KiB
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FILE
|
|
//
|
|
// ldapcxn.cpp
|
|
//
|
|
// SYNOPSIS
|
|
//
|
|
// This file defines the class LDAPConnection.
|
|
//
|
|
// MODIFICATION HISTORY
|
|
//
|
|
// 05/08/1998 Original version.
|
|
// 09/16/1998 Perform access check after bind.
|
|
// 03/23/1999 Set timeout for connect.
|
|
// 04/14/1999 Specify domain and server when opening a connection.
|
|
// 07/09/1999 Disable auto reconnect.
|
|
// 09/14/1999 Always specify timeout for LDAP searches.
|
|
// 01/25/2000 Encrypt LDAP connections.
|
|
// Pass server name (not domain) to ldap_initW.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <ias.h>
|
|
#include <iasutil.h>
|
|
#include <ntldap.h>
|
|
#define SECURITY_WIN32
|
|
#include <security.h>
|
|
#include <new>
|
|
|
|
#include <ldapcxn.h>
|
|
|
|
// Search timeout.
|
|
LDAP_TIMEVAL LDAPConnection::SEARCH_TIMEOUT = { 10, 0 };
|
|
|
|
namespace
|
|
{
|
|
// Timeout for LDAP connects.
|
|
LDAP_TIMEVAL CONNECT_TIMEOUT = { 15, 0 };
|
|
|
|
// RDN for the dummy object used for access checks.
|
|
const WCHAR DUMMY_OBJECT[] =
|
|
L"CN=RAS and IAS Servers Access Check,CN=System,";
|
|
|
|
/* Credentials for Kerberos-only bind.
|
|
SEC_WINNT_AUTH_IDENTITY_EXW BIND_CREDENTIALS =
|
|
{
|
|
SEC_WINNT_AUTH_IDENTITY_VERSION,
|
|
sizeof(SEC_WINNT_AUTH_IDENTITY_EXW),
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
SEC_WINNT_AUTH_IDENTITY_UNICODE,
|
|
L"Kerberos",
|
|
8
|
|
};
|
|
*/
|
|
}
|
|
|
|
void LDAPConnection::Release() throw ()
|
|
{
|
|
if (!InterlockedDecrement(&refCount))
|
|
{
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
DWORD LDAPConnection::createInstance(
|
|
PCWSTR domain,
|
|
PCWSTR server,
|
|
LDAPConnection** cxn
|
|
) throw ()
|
|
{
|
|
DWORD status;
|
|
ULONG opt;
|
|
|
|
// Check the input parameters.
|
|
if (cxn == NULL) { return ERROR_INVALID_PARAMETER; }
|
|
|
|
// Initialize the connection.
|
|
LDAP* ld = ldap_initW(
|
|
const_cast<PWCHAR>(server),
|
|
LDAP_PORT
|
|
);
|
|
if (ld == NULL)
|
|
{
|
|
return LdapMapErrorToWin32(LdapGetLastError());
|
|
}
|
|
|
|
// Set the domain name.
|
|
status = ldap_set_optionW(ld, LDAP_OPT_DNSDOMAIN_NAME, &domain);
|
|
if (status != LDAP_SUCCESS)
|
|
{
|
|
IASTraceLdapFailure("ldap_set_optionW", status, ld);
|
|
ldap_unbind(ld);
|
|
return LdapMapErrorToWin32(status);
|
|
}
|
|
|
|
opt = PtrToUlong(LDAP_OPT_ON);
|
|
status = ldap_set_optionW(ld, LDAP_OPT_AREC_EXCLUSIVE, &opt);
|
|
if (status != LDAP_SUCCESS)
|
|
{
|
|
IASTraceLdapFailure("ldap_set_optionW", status, ld);
|
|
ldap_unbind(ld);
|
|
return LdapMapErrorToWin32(status);
|
|
}
|
|
|
|
// Turn on encryption.
|
|
opt = PtrToUlong(LDAP_OPT_ON);
|
|
status = ldap_set_optionW(ld, LDAP_OPT_ENCRYPT, &opt);
|
|
if (status != LDAP_SUCCESS)
|
|
{
|
|
IASTraceLdapFailure("ldap_set_optionW", status, ld);
|
|
ldap_unbind(ld);
|
|
return LdapMapErrorToWin32(status);
|
|
}
|
|
|
|
// Connect to the server ...
|
|
status = ldap_connect(ld, &CONNECT_TIMEOUT);
|
|
if (status == LDAP_SUCCESS)
|
|
{
|
|
// ... and bind the connection.
|
|
status = ldap_bind_sW(ld, NULL, NULL, LDAP_AUTH_NEGOTIATE);
|
|
if (status != LDAP_SUCCESS)
|
|
{
|
|
IASTraceLdapFailure("ldap_bind_s", status, ld);
|
|
ldap_unbind(ld);
|
|
return LdapMapErrorToWin32(status);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IASTraceLdapFailure("ldap_connect", status, ld);
|
|
ldap_unbind(ld);
|
|
return LdapMapErrorToWin32(status);
|
|
}
|
|
|
|
// Turn off automatic chasing of referrals.
|
|
opt = PtrToUlong(LDAP_OPT_OFF);
|
|
ldap_set_option(ld, LDAP_OPT_REFERRALS, &opt);
|
|
|
|
// Turn off auto reconnect of bad connections.
|
|
opt = PtrToUlong(LDAP_OPT_OFF);
|
|
ldap_set_option(ld, LDAP_OPT_AUTO_RECONNECT, &opt);
|
|
|
|
// Turn on ldap_conn_from_msg.
|
|
opt = PtrToUlong(LDAP_OPT_ON);
|
|
ldap_set_option(ld, LDAP_OPT_REF_DEREF_CONN_PER_MSG, &opt);
|
|
|
|
// Create the LDAPConnection wrapper.
|
|
LDAPConnection* newCxn = new (std::nothrow) LDAPConnection(ld);
|
|
if (newCxn == NULL)
|
|
{
|
|
ldap_unbind(ld);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
// Read the RootDSE.
|
|
status = newCxn->readRootDSE();
|
|
|
|
// Check access permissions.
|
|
if (status == NO_ERROR)
|
|
{
|
|
status = newCxn->checkAccess();
|
|
}
|
|
|
|
// Process the result.
|
|
if (status == NO_ERROR)
|
|
{
|
|
*cxn = newCxn;
|
|
}
|
|
else
|
|
{
|
|
newCxn->Release();
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
//////////
|
|
//
|
|
// NOTE: This doesn't have to be serialized since it's only called from
|
|
// within createInstance.
|
|
//
|
|
//////////
|
|
DWORD LDAPConnection::checkAccess() throw ()
|
|
{
|
|
// Allocate a temporary buffer for the DN.
|
|
size_t len = wcslen(base) * sizeof(WCHAR) + sizeof(DUMMY_OBJECT);
|
|
PWSTR dn = (PWSTR)_alloca(len);
|
|
|
|
// Construct the DN of the dummy object.
|
|
memcpy(dn, DUMMY_OBJECT, sizeof(DUMMY_OBJECT));
|
|
wcscat(dn, base);
|
|
|
|
// Try to read the dummy object.
|
|
PWCHAR attrs[] = { L"CN", NULL };
|
|
LDAPMessage* res = NULL;
|
|
ULONG ldapError = ldap_search_ext_sW(
|
|
connection,
|
|
dn,
|
|
LDAP_SCOPE_BASE,
|
|
L"(objectclass=*)",
|
|
attrs,
|
|
TRUE,
|
|
NULL,
|
|
NULL,
|
|
&SEARCH_TIMEOUT,
|
|
0,
|
|
&res
|
|
);
|
|
|
|
// We have two different error codes.
|
|
if (ldapError == LDAP_SUCCESS)
|
|
{
|
|
ldapError = res->lm_returncode;
|
|
}
|
|
|
|
DWORD status = NO_ERROR;
|
|
|
|
if (ldapError != LDAP_SUCCESS)
|
|
{
|
|
TraceFailure("ldap_search_ext_sW", ldapError);
|
|
status = LdapMapErrorToWin32(ldapError);
|
|
}
|
|
else
|
|
{
|
|
// Get the first attribute from the first entry.
|
|
BerElement* ptr;
|
|
PWCHAR attr = ldap_first_attributeW(
|
|
connection,
|
|
ldap_first_entry(connection, res),
|
|
&ptr
|
|
);
|
|
|
|
// If we couldn't read any attributes, then we must not be a member
|
|
// of the RAS and IAS Servers group.
|
|
if (attr == NULL)
|
|
{
|
|
status = ERROR_ACCESS_DENIED;
|
|
}
|
|
}
|
|
|
|
ldap_msgfree(res);
|
|
|
|
return status;
|
|
}
|
|
|
|
//////////
|
|
//
|
|
// NOTE: This doesn't have to be serialized since it's only called from
|
|
// within createInstance.
|
|
//
|
|
//////////
|
|
DWORD LDAPConnection::readRootDSE() throw ()
|
|
{
|
|
//////////
|
|
// Read the defaultNamingContext from the RootDSE.
|
|
//////////
|
|
|
|
PWCHAR attrs[] = { LDAP_OPATT_DEFAULT_NAMING_CONTEXT_W, NULL };
|
|
LDAPMessage* res = NULL;
|
|
ULONG ldapError = ldap_search_ext_sW(
|
|
connection,
|
|
L"",
|
|
LDAP_SCOPE_BASE,
|
|
L"(objectclass=*)",
|
|
attrs,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&SEARCH_TIMEOUT,
|
|
0,
|
|
&res
|
|
);
|
|
|
|
// We have two different error codes.
|
|
if (ldapError == LDAP_SUCCESS)
|
|
{
|
|
ldapError = res->lm_returncode;
|
|
}
|
|
|
|
if (ldapError != LDAP_SUCCESS)
|
|
{
|
|
TraceFailure("ldap_search_ext_sW", ldapError);
|
|
ldap_msgfree(res);
|
|
return LdapMapErrorToWin32(ldapError);
|
|
}
|
|
|
|
//////////
|
|
// The search succeeded, so get the attribute value.
|
|
//////////
|
|
|
|
PWCHAR* vals = ldap_get_valuesW(
|
|
connection,
|
|
ldap_first_entry(connection, res),
|
|
LDAP_OPATT_DEFAULT_NAMING_CONTEXT_W
|
|
);
|
|
DWORD status;
|
|
|
|
if (vals && *vals)
|
|
{
|
|
// We got something, so save the value.
|
|
base = ias_wcsdup(*vals);
|
|
|
|
status = base ? NO_ERROR : ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
// It's a mandatory attribute but we can't see it, so we must not have
|
|
// sufficient permission.
|
|
status = ERROR_ACCESS_DENIED;
|
|
}
|
|
|
|
ldap_value_freeW(vals);
|
|
|
|
ldap_msgfree(res);
|
|
|
|
return status;
|
|
}
|
|
|
|
void LDAPConnection::TraceFailure(
|
|
PCSTR functionName,
|
|
ULONG errorCode
|
|
)
|
|
{
|
|
IASTraceLdapFailure(functionName, errorCode, connection);
|
|
}
|
|
|
|
void IASTraceLdapFailure(
|
|
PCSTR functionName,
|
|
ULONG errorCode,
|
|
LDAP* cxn
|
|
)
|
|
{
|
|
_ASSERT(functionName != NULL);
|
|
|
|
IASTracePrintf("LDAP ERROR in %s. Code = %d", functionName, errorCode);
|
|
|
|
PWCHAR errorString = NULL;
|
|
ULONG result = ldap_get_optionW(
|
|
cxn,
|
|
LDAP_OPT_SERVER_ERROR,
|
|
(void*) &errorString
|
|
);
|
|
if (result == LDAP_SUCCESS)
|
|
{
|
|
IASTracePrintf("Extended error string: %S", errorString);
|
|
// do what you want with the string here
|
|
ldap_memfree(errorString);
|
|
}
|
|
}
|
|
|