Wireshark packet here I would like to ask you a question about implementing multiple authentication with Kerberos, using SSPI and LDAP API. My environment is Windows Server 2019 and the client machine is a member of the domain.
I am using the guidelines described at: How to use ldap_sasl_bind in WinLDAP? and ldap_sasl_bind_s(GSSAPI) - What should be provided in the credentials BERVAL structure
After two days, I finally found out how to use ldap_sasl_bind_s with the GSSAPI mechanism. But I have trouble when combining ldap_sasl_bind_s with ldap_search_s though I can see ldap_sasl_bind_s is successful through wireshark tools.
Below is my code is updated:
void LDAP_SASLBindGSSAPI()
{
LDAP* ld;
int rc = 0;
const int version = LDAP_VERSION3;
SEC_WINNT_AUTH_IDENTITY wincreds;
struct berval* serversp = NULL;
SECURITY_STATUS res;
CredHandle credhandle;
CtxtHandle newhandle;
SecBufferDesc OutBuffDesc;
SecBuffer OutSecBuff;
SecBufferDesc InBuffDesc;
SecBuffer InSecBuff;
unsigned long contextattr;
static PTCHAR lpPackageName = (PTCHAR)"Kerberos";
TimeStamp timeExpired;
ZeroMemory(&wincreds, sizeof(wincreds));
std::string username = "username";
std::string password = "Mypassword";
std::string domain = "TEST.COM";
// Set credential information
wincreds.User = (unsigned char*)username.c_str();
wincreds.UserLength = username.length();
wincreds.Password = (unsigned char*)password.c_str();
wincreds.PasswordLength = password.length();
wincreds.Domain = (unsigned char*)domain.c_str();
wincreds.DomainLength = domain.length();
wincreds.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
res = AcquireCredentialsHandleA(NULL, lpPackageName, SECPKG_CRED_BOTH, NULL, &wincreds, NULL, NULL, &credhandle, &timeExpired);
// Buffer for the output token.
OutBuffDesc.ulVersion = 0;
OutBuffDesc.cBuffers = 1;
OutBuffDesc.pBuffers = &OutSecBuff;
OutSecBuff.BufferType = SECBUFFER_TOKEN;
OutSecBuff.pvBuffer = NULL;
ld = ldap_init((PCHAR)"WIN-CAV3F6MES9M.TEST.COM", LDAP_PORT);
if (ld == nullptr)
{
std::cout << "Initialize ldap failed\n";
}
rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, (void*)&version);
rc = ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
ldap_set_option(ld, LDAP_OPT_SSL, LDAP_OPT_OFF);
ldap_set_option(ld, LDAP_OPT_TLS, LDAP_OPT_OFF);
if (rc != LDAP_SUCCESS)
{
std::cout << "set LDAP_OPT_REFERRALS failed\n";
}
rc = ldap_connect(ld, NULL); // Need to connect before SASL bind!
std::cout << "result: " << rc << std::endl;
if (rc != LDAP_SUCCESS) {
// Connection failed
std::cout << "Connection failed\n";
ldap_unbind(ld);
return;
}
//InitializeBaseLDAP(ld);
do {
if (serversp != NULL)
{
InBuffDesc.ulVersion = 0;
InBuffDesc.cBuffers = 1;
InBuffDesc.pBuffers = &InSecBuff;
InSecBuff.cbBuffer = serversp->bv_len;
InSecBuff.BufferType = SECBUFFER_TOKEN;
InSecBuff.pvBuffer = serversp->bv_val;
res = InitializeSecurityContextA(
&credhandle,
&newhandle,
(PCHAR)"ldap/[email protected]",
ISC_REQ_INTEGRITY | ISC_REQ_MUTUAL_AUTH | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_DELEGATE,
0,
SECURITY_NATIVE_DREP,
&InBuffDesc,
0,
&newhandle,
&OutBuffDesc,
&contextattr,
&timeExpired);
std::cout << "Second InitializeSecurityContextA-Res: " << res << std::endl;
struct berval cred;
cred.bv_len = OutSecBuff.cbBuffer;
cred.bv_val = (char*)OutSecBuff.pvBuffer;
rc = ldap_sasl_bind_sA(ld, (PCHAR)"", (PCHAR)"GSSAPI", &cred, NULL, NULL, &serversp);
std::cout << "rc= " << rc << std::endl;
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &res);
std::cout << "res - second call ldap_sasl_bind_sA: " << res << std::endl;
break;
}
else
{
res = InitializeSecurityContextA(
&credhandle,
NULL,
(PCHAR)"ldap/[email protected]",
ISC_REQ_INTEGRITY | ISC_REQ_MUTUAL_AUTH | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_DELEGATE,
0,
SECURITY_NATIVE_DREP,
NULL,
0,
&newhandle,
&OutBuffDesc,
&contextattr,
&timeExpired);
std::cout << "First InitializeSecurityContextA-Res: " << res << std::endl;
struct berval cred;
cred.bv_len = OutSecBuff.cbBuffer;
cred.bv_val = (char*)OutSecBuff.pvBuffer;
rc = ldap_sasl_bind_sA(ld, (PCHAR)"", (PCHAR)"GSSAPI", &cred, NULL, NULL, &serversp);
std::cout << "rc= " << rc << std::endl;
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &res);
std::cout << "res - first call ldap_sasl_bind_sA: " << res << std::endl;
}
} while (res == LDAP_SASL_BIND_IN_PROGRESS);
if (rc != LDAP_SUCCESS)
{
printf("Bind failed with 0x%x\n", rc);
}
else
{
printf("First and Second Bind succeeded\n");
PrintHexDump(serversp->bv_len, (PBYTE)serversp->bv_val);
SecBufferDesc DecryptBuffDesc;
SecBuffer DecryptSecBuffer[2];
DecryptBuffDesc.ulVersion = 0;
DecryptBuffDesc.cBuffers = 2;
DecryptBuffDesc.pBuffers = DecryptSecBuffer;
DecryptSecBuffer[0].BufferType = SECBUFFER_STREAM;
DecryptSecBuffer[0].cbBuffer = serversp->bv_len;
DecryptSecBuffer[0].pvBuffer = serversp->bv_val;
DecryptSecBuffer[1].BufferType = SECBUFFER_DATA;
ULONG ulQop;
res = DecryptMessage(&newhandle, &DecryptBuffDesc, 0, &ulQop);
std::cout << "res-DecryptMessage: " << res << std::endl;
if (DecryptSecBuffer[1].pvBuffer != NULL)
{
std::cout << "Hexa dump: " << std::endl;
PrintHexDump(DecryptSecBuffer[1].cbBuffer, (PBYTE)DecryptSecBuffer[1].pvBuffer);
unsigned char* ptr = (unsigned char*)DecryptSecBuffer[1].pvBuffer;
int maxsize = (ptr[1] << 16) | (ptr[2] << 8) | (ptr[3]);
std::cout << "maxsize: " << maxsize << std::endl;
ptr = (unsigned char*)malloc(4);
ptr[0] = 0x04;
ptr[1] = maxsize >> 16;
ptr[2] = maxsize >> 8;
ptr[3] = maxsize;
// Encrypt Message
SecBufferDesc EncryptBuffDesc;
SecBuffer EncryptSecBuffer[3];
EncryptBuffDesc.ulVersion = 0;
EncryptBuffDesc.cBuffers = 3;
EncryptBuffDesc.pBuffers = EncryptSecBuffer;
unsigned char* buffer = (unsigned char*)malloc(maxsize);
EncryptSecBuffer[0].BufferType = SECBUFFER_TOKEN;
EncryptSecBuffer[0].cbBuffer = maxsize;
EncryptSecBuffer[0].pvBuffer = buffer;
EncryptSecBuffer[1].cbBuffer = 4;
EncryptSecBuffer[1].BufferType = SECBUFFER_DATA;
EncryptSecBuffer[1].pvBuffer = ptr;
EncryptSecBuffer[2].BufferType = SECBUFFER_PADDING;
res = EncryptMessage(&newhandle, 0, &EncryptBuffDesc, 0);
std::cout << "res-EncryptMessage: " << res << std::endl;
if (EncryptSecBuffer[0].pvBuffer != NULL)
{
std::cout << "EncryptSecBuffer token size: " << EncryptSecBuffer[0].cbBuffer << std::endl;
std::cout << "EncryptSecBuffer data size: " << EncryptSecBuffer[1].cbBuffer << std::endl;
std::cout << "EncryptSecBuffer padding size: " << EncryptSecBuffer[2].cbBuffer << std::endl;
PrintHexDump(EncryptSecBuffer[0].cbBuffer, (PBYTE)EncryptSecBuffer[0].pvBuffer);
}
int cbLastClientBuff = EncryptSecBuffer[0].cbBuffer + EncryptSecBuffer[1].cbBuffer + EncryptSecBuffer[2].cbBuffer;
unsigned char* lastClientBuff = (unsigned char*)malloc(cbLastClientBuff);
int index = 0;
memcpy(lastClientBuff, EncryptSecBuffer[0].pvBuffer, EncryptSecBuffer[0].cbBuffer);
index += EncryptSecBuffer[0].cbBuffer;
memcpy(lastClientBuff + index, EncryptSecBuffer[1].pvBuffer, EncryptSecBuffer[1].cbBuffer);
index += EncryptSecBuffer[1].cbBuffer;
memcpy(lastClientBuff + index, EncryptSecBuffer[2].pvBuffer, EncryptSecBuffer[2].cbBuffer);
struct berval lastClientCred;
lastClientCred.bv_len = cbLastClientBuff;
lastClientCred.bv_val = (char*)lastClientBuff;
rc = ldap_sasl_bind_sA(ld, (PCHAR)"", (PCHAR)"GSSAPI", &lastClientCred , NULL, NULL, &serversp);
std::cout << "rc= " << rc << std::endl;
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &res);
std::cout << "res - last call ldap_sasl_bind_sA: " << res << std::endl;
if (rc == LDAP_SUCCESS && res == LDAP_SUCCESS)
{
std::cout << "ldap_sasl_bind_s successfully\n";
}
}
}
// CAN NOT SEARCH BECAUSE BIND PROCESS IS NOT YET SUCCESSED!!!
PCHAR pMyAttributes[9];
pMyAttributes[0] = (PCHAR)"minPwdLength";
pMyAttributes[1] = (PCHAR)"pwdHistoryLength";
pMyAttributes[2] = (PCHAR)"maxPwdAge";
pMyAttributes[3] = (PCHAR)"minPwdAge";
pMyAttributes[4] = (PCHAR)"pwdProperties";
pMyAttributes[5] = (PCHAR)"lockoutThreshold";
pMyAttributes[6] = (PCHAR)"lockoutDuration";
pMyAttributes[7] = (PCHAR)"lockoutObservationWindow";
pMyAttributes[8] = NULL;
ULONG ldapResult;
LDAPMessage* searchResult = nullptr;
std::string filter = "((objectClass=domain))";
ldapResult = ldap_search_sA(ld, (PCHAR)"DC=TEST,DC=COM", LDAP_SCOPE_SUBTREE, (PCHAR)filter.c_str(), pMyAttributes, 0, &searchResult);
}
I have followed the guidelines as well as read the answer from the link: SASL bind over GSSAPI using Kerberos credentials the with ldap_sasl_bind_s function
But I don't know whether the sasl bind process is finished or not even though I can see sals bind success through the Wireshark tool. The reason I said that I'm not sure sals bind process is done or not due that I can't perform ldap_search_s after that. LDAP server didn't respond to anything when I requested a search.
Thank you in advance!