NTLM AcceptSecurityContext fails on remote machine, but works locally

  • Thread starter Thread starter Manustone
  • Start date Start date
M

Manustone

Guest
I am trying to implement NTLM authentication in C++ using Windows API

InitializeSecurityContext
AcquireCredentialsHandle
AcceptSecurityContext

I implemented a server and a client which exchange the token generated using these functions. Everything works fine when I run server and client on the same machine, ... BUT as soon as I run the apps in 2 different machines ON THE SAME domain it does not work anymore.

To be precise the server call of <code>AcceptSecurityContext</code> fails with error <code>SEC_E_INVALID_TOKEN</code>.

Do you know what am I doing wrong?

Part of this code is taken from a WebServer. If I use INTERNET EXPLORER as client to connect from a remote machine works fin.

Is there anything I should check like Firewall or Networks security which prevents NTLM across 2 machines to work properly?

I am using <code>boost::asio</code> to exchange the tokens through the networks

Below The full listing.

Once build is finished use command line 's' to start the server and 'c' to stat the client




#pragma warning( disable: 4996 )

#include <boost/asio.hpp>
#include <boost/assert.hpp>
#include <thread>

#include <Windows.h>

#define SECURITY_WIN32
#include <Security.h>
#include <cassert>
#include <iostream>

#define TARGET_INIT_SECURITY_CONTEXT "TEST/192.168.10.92"

#define BYTES_MAX_MESSAGE 12000
#define ASIO_BUFFER_SIZE 1024

#define CLIENT_SECURITY_CONTEXT "NTLM"
#define CLIENT_SECURITY_REQUIREMENTS ISC_REQ_CONNECTION

#define SERVER_SECURITY_CONTEXT "NTLM"
#define SERVER_SECURITY_REQUIREMENETS ASC_REQ_CONNECTION


void cs_base64_encode(const unsigned char *src, int src_len, char *dst);
int cs_base64_decode(const unsigned char *s, int len, char *dst, int *dec_len);

std::string base64_decode(std::string const& encoded_string);
std::string base64_encode(char const* bytes_to_encode, unsigned int in_len);


class NTLMServer
{
public:
std::string first_call(std::string clientToken_string)
{
SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL,
SERVER_SECURITY_CONTEXT,
SECPKG_CRED_INBOUND,
NULL,
NULL,
NULL,
NULL,
&serverCredentials_,
NULL);

BOOST_ASSERT_MSG(status == SEC_E_OK, "AcquireCredentialsHandle failed");

BYTE *pOut = new BYTE[BYTES_MAX_MESSAGE];
std::memset(pOut, 0, sizeof(BYTE) * BYTES_MAX_MESSAGE);

SecBuffer serverOutBuffer_;
SecBufferDesc serverToken_;

serverOutBuffer_.BufferType = SECBUFFER_TOKEN;
serverOutBuffer_.cbBuffer = BYTES_MAX_MESSAGE;
serverOutBuffer_.pvBuffer = pOut;

serverToken_.cBuffers = 1;
serverToken_.pBuffers = &serverOutBuffer_;
serverToken_.ulVersion = SECBUFFER_VERSION;

std::string out;

SecBufferDesc clientToken;
SecBuffer clientOutBuffer;

{
std::string strToken = base64_decode(clientToken_string);

BYTE *pOut = new BYTE[BYTES_MAX_MESSAGE];
std::memset(pOut, 0, sizeof(BYTE) * BYTES_MAX_MESSAGE);
strToken.copy(reinterpret_cast<char*>(pOut), strToken.size());

clientToken.cBuffers = 1;
clientToken.pBuffers = &clientOutBuffer;
clientToken.ulVersion = SECBUFFER_VERSION;

clientOutBuffer.BufferType = SECBUFFER_TOKEN;
clientOutBuffer.cbBuffer = strToken.size();
clientOutBuffer.pvBuffer = pOut;

ULONG ContextAttributes = 0;

SECURITY_STATUS status = AcceptSecurityContext(
&serverCredentials_,
NULL,
&clientToken,
SERVER_SECURITY_REQUIREMENETS,
SECURITY_NATIVE_DREP,
&hContext_,
&serverToken_,
&ContextAttributes,
NULL);


BOOST_ASSERT_MSG(status == SEC_I_CONTINUE_NEEDED || status == SEC_E_OK, "AcceptSecurityContext failed");

int bufferSize = static_cast< int >(serverOutBuffer_.cbBuffer);
out = base64_encode(reinterpret_cast< const char *> (serverToken_.pBuffers->pvBuffer), bufferSize);
}

return out;
}

void second_call(const std::string& clientToken_string)
{
SecBufferDesc clientToken;
SecBuffer clientOutBuffer;

std::string strToken = base64_decode(clientToken_string);

BYTE *pIn = new BYTE[BYTES_MAX_MESSAGE];
std::memset(pIn, 0, sizeof(BYTE) * BYTES_MAX_MESSAGE);
strToken.copy(reinterpret_cast<char*>(pIn), strToken.size());

clientToken.cBuffers = 1;
clientToken.pBuffers = &clientOutBuffer;
clientToken.ulVersion = SECBUFFER_VERSION;

clientOutBuffer.BufferType = SECBUFFER_TOKEN;
clientOutBuffer.cbBuffer = strToken.size();
clientOutBuffer.pvBuffer = pIn;

BYTE *pOut = new BYTE[BYTES_MAX_MESSAGE];
std::memset(pOut, 0, sizeof(BYTE) * BYTES_MAX_MESSAGE);

SecBuffer serverOutBuffer_;
SecBufferDesc serverToken_;

serverOutBuffer_.BufferType = SECBUFFER_TOKEN;
serverOutBuffer_.cbBuffer = BYTES_MAX_MESSAGE;
serverOutBuffer_.pvBuffer = pOut;

serverToken_.cBuffers = 1;
serverToken_.pBuffers = &serverOutBuffer_;
serverToken_.ulVersion = SECBUFFER_VERSION;

ULONG ContextAttributes = 0;

SECURITY_STATUS status = AcceptSecurityContext(
&serverCredentials_,
&hContext_,
&clientToken,
SERVER_SECURITY_REQUIREMENETS,
SECURITY_NATIVE_DREP,
&hContext_,
&serverToken_,
&ContextAttributes,
NULL);

BOOST_ASSERT_MSG(status != SEC_E_INVALID_TOKEN, "Invalid Token!?!?!?");
BOOST_ASSERT_MSG(status == SEC_E_OK, "NOT OK?!?!?" );
}

CredHandle serverCredentials_;
CtxtHandle hContext_;
};

class NTLMClient
{
public:
std::string first_call()
{
ULONG ContextAttributes = 0;
SECURITY_STATUS status = AcquireCredentialsHandle(
NULL,
CLIENT_SECURITY_CONTEXT,
SECPKG_CRED_OUTBOUND,
NULL,
NULL,
NULL,
NULL,
&hCred_,
NULL);



BOOST_ASSERT_MSG(status == SEC_E_OK, "AcquireCredentialsHandle failed");

SecBuffer OutSecBuff;

BYTE *pIn = NULL;

BYTE *pOut = new BYTE[BYTES_MAX_MESSAGE];
std::memset(pOut, 0, sizeof(BYTE) * BYTES_MAX_MESSAGE);

BOOL fDone = FALSE;
BOOL *pfDone = &fDone;

DWORD cbOut = BYTES_MAX_MESSAGE;
DWORD *pcbOut = &cbOut;

OutDesc_.ulVersion = 0;
OutDesc_.cBuffers = 1;
OutDesc_.pBuffers = &OutSecBuff;

OutSecBuff.cbBuffer = *pcbOut;
OutSecBuff.BufferType = SECBUFFER_TOKEN;
OutSecBuff.pvBuffer = pOut;

status = InitializeSecurityContext(
&hCred_,
NULL,
TARGET_INIT_SECURITY_CONTEXT,
CLIENT_SECURITY_REQUIREMENTS,
0,
SECURITY_NATIVE_DREP,
NULL,
0,
&hContext_,
&OutDesc_,
&ContextAttributes,
NULL);



BOOST_ASSERT_MSG(status == SEC_I_CONTINUE_NEEDED, "InitializeSecurityContext failed");

*pcbOut = OutSecBuff.cbBuffer;
*pfDone = !((SEC_I_CONTINUE_NEEDED == status) ||
(SEC_I_COMPLETE_AND_CONTINUE == status));



std::string output;
output.resize(cbOut * 2);

cs_base64_encode(pOut, cbOut, &output[0]);
output.erase(output.find_first_of('\0')); // remove the string

return output;
}

std::string second_call(const std::string& server_response)
{
SecBuffer OutSecBuff;

BYTE *pIn;
BYTE *pOut;

DWORD cbOut = 0;
DWORD *pcbOut = &cbOut;

BOOL done;
DWORD cbIn;

pIn = new BYTE[BYTES_MAX_MESSAGE];
std::memset(pIn, 0, sizeof(BYTE) * BYTES_MAX_MESSAGE);

pOut = new BYTE[BYTES_MAX_MESSAGE];
std::memset(pOut, 0, sizeof(BYTE) * BYTES_MAX_MESSAGE);

int what_is_this = cs_base64_decode(reinterpret_cast<const unsigned char *>(server_response.c_str()),
server_response.size(),
reinterpret_cast<char *>(pIn),
NULL);

cbIn = what_is_this;
cbOut = BYTES_MAX_MESSAGE;

SecBuffer InSecBuff;

OutDesc_.ulVersion = 0;
OutDesc_.cBuffers = 1;
OutDesc_.pBuffers = &OutSecBuff;

OutSecBuff.cbBuffer = *pcbOut;
OutSecBuff.BufferType = SECBUFFER_TOKEN;
OutSecBuff.pvBuffer = pOut;

ServerTokenDesc_.ulVersion = 0;
ServerTokenDesc_.cBuffers = 1;
ServerTokenDesc_.pBuffers = &InSecBuff;

InSecBuff.cbBuffer = cbIn;
InSecBuff.BufferType = SECBUFFER_TOKEN;
InSecBuff.pvBuffer = pIn;


ULONG ContextAttributes = 0;
SECURITY_STATUS status = InitializeSecurityContext(
&hCred_,
&hContext_,
TARGET_INIT_SECURITY_CONTEXT,
CLIENT_SECURITY_REQUIREMENTS,
0,
SECURITY_NATIVE_DREP,
&ServerTokenDesc_,
0,
&hContext_,
&OutDesc_,
&ContextAttributes,
NULL);

*pcbOut = OutSecBuff.cbBuffer;
done = !((SEC_I_CONTINUE_NEEDED == status) ||
(SEC_I_COMPLETE_AND_CONTINUE == status));

BOOST_ASSERT_MSG(status == SEC_E_OK, "InitializeSecurityContext failed");



std::string output;
output.resize(cbOut * 2);

cs_base64_encode(pOut, cbOut, &output[0]);
output.erase(output.find_first_of('\0')); // remove the string


return output;
}

private:
CredHandle hCred_;
CtxtHandle hContext_;
SecBufferDesc ServerTokenDesc_;
SecBufferDesc OutDesc_;
};

class TCPServer
{
public:
void start()
{
// STAT LISTENING AND READ
//
using boost::asio::ip::tcp;
tcp::acceptor a(io_context, tcp::endpoint(tcp::v4(), 32111));
tcp::socket sock(io_context);
a.accept(sock);

char data[ASIO_BUFFER_SIZE];
std::memset(data, 0, ASIO_BUFFER_SIZE);
size_t length;

boost::system::error_code error;
length = sock.read_some(boost::asio::buffer(data), error);

if (error == boost::asio::error::eof)
return; // Connection closed cleanly by peer.

std::string client_token(data, length);
auto first_response = ntlm_server_.first_call(client_token);

std::memset(data, 0, ASIO_BUFFER_SIZE);
first_response.copy(data, first_response.size());
boost::asio::write(sock, boost::asio::buffer(data, first_response.size()));

std::memset(data, 0, ASIO_BUFFER_SIZE);
length = sock.read_some(boost::asio::buffer(data), error);
if (error == boost::asio::error::eof)
return; // Connection closed cleanly by peer.

client_token.assign(data, length);
ntlm_server_.second_call(client_token);
}

private:
NTLMServer ntlm_server_;
boost::asio::io_service io_context;
};

class TCPClient
{
public:
void start()
{
// Connect
using boost::asio::ip::tcp;
tcp::resolver resolver(io_context);
tcp::socket s(io_context);
s.connect( boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string("192.168.10.92"), 32111));

// Generate client token
const std::string msg = ntlm_client_.first_call();

char request[ASIO_BUFFER_SIZE];
std::memset(request, 0, ASIO_BUFFER_SIZE);
msg.copy(request, ASIO_BUFFER_SIZE);
size_t reply_length;

boost::asio::write(s, boost::asio::buffer(request, msg.size()));

char reply[ASIO_BUFFER_SIZE];
reply_length = boost::asio::read(s, boost::asio::buffer(reply, 256));
std::string first_server_response(reply, reply_length);

auto second_msg = ntlm_client_.second_call(first_server_response);
std::memset(request, 0, ASIO_BUFFER_SIZE);
second_msg.copy(request, second_msg.size());

boost::asio::write(s, boost::asio::buffer(request, second_msg.size()));

std::this_thread::sleep_for(std::chrono::seconds(3));
}
private:
NTLMClient ntlm_client_;
boost::asio::io_service io_context;
};

int main(int argc, char** argv)
{
if (argv[1][0] == 's')
{
std::cout << "SERVER" << std::endl;
TCPServer server;
server.start();
}

if (argv[1][0] == 'c')
{
std::cout << "CLIENT" << std::endl;
TCPClient client;
client.start();
}

return EXIT_SUCCESS;
}



// UTILITY

#define BASE64_ENCODE_BODY \
static const char *b64 = \
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; \
int i, j, a, b, c; \
\
for (i = j = 0; i < src_len; i += 3) { \
a = src; \
b = i + 1 >= src_len ? 0 : src[i + 1]; \
c = i + 2 >= src_len ? 0 : src[i + 2]; \
\
BASE64_OUT(b64[a >> 2]); \
BASE64_OUT(b64[((a & 3) << 4) | (b >> 4)]); \
if (i + 1 < src_len) { \
BASE64_OUT(b64[(b & 15) << 2 | (c >> 6)]); \
} \
if (i + 2 < src_len) { \
BASE64_OUT(b64[c & 63]); \
} \
} \
\
while (j % 4 != 0) { \
BASE64_OUT('='); \
} \
BASE64_FLUSH()

#define BASE64_OUT(ch) \
do { \
dst[j++] = (ch); \
} while (0)

#define BASE64_FLUSH() \
do { \
dst[j++] = '\0'; \
} while (0)

void cs_base64_encode(const unsigned char *src, int src_len, char *dst) {
BASE64_ENCODE_BODY;
}

const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

bool is_base64(unsigned char c) {
return (std::isalnum(c) || (c == '+') || (c == '/'));
}

std::string base64_decode(std::string const& encoded_string)
{
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;

while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i == 4) {
for (i = 0; i < 4; i++)
char_array_4 = base64_chars.find(char_array_4);

char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

for (i = 0; (i < 3); i++)
ret += char_array_3;
i = 0;
}
}

if (i) {
for (j = i; j < 4; j++)
char_array_4[j] = 0;

for (j = 0; j < 4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);

char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}

return ret;
}

std::string base64_encode(char const* bytes_to_encode, unsigned int in_len)
{
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];

while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;

for (i = 0; (i < 4); i++)
ret += base64_chars[char_array_4];
i = 0;
}
}

if (i)
{
for (j = i; j < 3; j++)
char_array_3[j] = '\0';

char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;

for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];

while ((i++ < 3))
ret += '=';

}

return ret;
}


static unsigned char from_b64(unsigned char ch) {

static const unsigned char tab[128] = {
255, 255, 255, 255,
255, 255, 255, 255, /* 0 */
255, 255, 255, 255,
255, 255, 255, 255, /* 8 */
255, 255, 255, 255,
255, 255, 255, 255, /* 16 */
255, 255, 255, 255,
255, 255, 255, 255, /* 24 */
255, 255, 255, 255,
255, 255, 255, 255, /* 32 */
255, 255, 255, 62,
255, 255, 255, 63, /* 40 */
52, 53, 54, 55,
56, 57, 58, 59, /* 48 */
60, 61, 255, 255,
255, 200, 255, 255, /* 56 '=' is 200, on index 61 */
255, 0, 1, 2,
3, 4, 5, 6, /* 64 */
7, 8, 9, 10,
11, 12, 13, 14, /* 72 */
15, 16, 17, 18,
19, 20, 21, 22, /* 80 */
23, 24, 25, 255,
255, 255, 255, 255, /* 88 */
255, 26, 27, 28,
29, 30, 31, 32, /* 96 */
33, 34, 35, 36,
37, 38, 39, 40, /* 104 */
41, 42, 43, 44,
45, 46, 47, 48, /* 112 */
49, 50, 51, 255,
255, 255, 255, 255, /* 120 */
};
return tab[ch & 127];
}


int cs_base64_decode(const unsigned char *s, int len, char *dst, int *dec_len) {
unsigned char a, b, c, d;
int orig_len = len;
char *orig_dst = dst;
while (len >= 4 && (a = from_b64(s[0])) != 255 &&
(b = from_b64(s[1])) != 255 && (c = from_b64(s[2])) != 255 &&
(d = from_b64(s[3])) != 255) {
s += 4;
len -= 4;
if (a == 200 || b == 200) break; /* '=' can't be there */
*dst++ = a << 2 | b >> 4;
if (c == 200) break;
*dst++ = b << 4 | c >> 2;
if (d == 200) break;
*dst++ = c << 6 | d;
}
*dst = 0;
if (dec_len != NULL) *dec_len = (dst - orig_dst);
return orig_len - len;
}

Continue reading...
 
Back
Top