libfreerdp-core: refactor NTLM over HTTP authentication

This commit is contained in:
Marc-André Moreau 2012-11-27 22:03:05 -05:00
parent 83a9af128f
commit 4da0c0daa9
5 changed files with 313 additions and 177 deletions

View File

@ -29,6 +29,8 @@ include_directories(".")
include_directories(${${MODULE_PREFIX}_GATEWAY_DIR})
set(${MODULE_PREFIX}_GATEWAY_SRCS
${${MODULE_PREFIX}_GATEWAY_DIR}/rpc.c
${${MODULE_PREFIX}_GATEWAY_DIR}/rpc.h
${${MODULE_PREFIX}_GATEWAY_DIR}/tsg.c
${${MODULE_PREFIX}_GATEWAY_DIR}/tsg.h
${${MODULE_PREFIX}_GATEWAY_DIR}/rts.c
@ -37,8 +39,8 @@ set(${MODULE_PREFIX}_GATEWAY_SRCS
${${MODULE_PREFIX}_GATEWAY_DIR}/ntlm.h
${${MODULE_PREFIX}_GATEWAY_DIR}/http.c
${${MODULE_PREFIX}_GATEWAY_DIR}/http.h
${${MODULE_PREFIX}_GATEWAY_DIR}/rpc.c
${${MODULE_PREFIX}_GATEWAY_DIR}/rpc.h)
${${MODULE_PREFIX}_GATEWAY_DIR}/ncacn_http.c
${${MODULE_PREFIX}_GATEWAY_DIR}/ncacn_http.h)
set(${MODULE_PREFIX}_SRCS
activation.c

View File

@ -0,0 +1,229 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RPC over HTTP (ncacn_http)
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "ncacn_http.h"
#include <winpr/crt.h>
#include <winpr/tchar.h>
#include <winpr/dsparse.h>
#include <openssl/rand.h>
STREAM* rpc_ntlm_http_request(rdpRpc* rpc, SecBuffer* ntlm_token, int content_length, TSG_CHANNEL channel)
{
STREAM* s;
char* base64_ntlm_token;
HttpContext* http_context;
HttpRequest* http_request;
http_request = http_request_new();
base64_ntlm_token = crypto_base64_encode(ntlm_token->pvBuffer, ntlm_token->cbBuffer);
if (channel == TSG_CHANNEL_IN)
{
http_context = rpc->NtlmHttpIn->context;
http_request_set_method(http_request, "RPC_IN_DATA");
}
else if (channel == TSG_CHANNEL_OUT)
{
http_context = rpc->NtlmHttpOut->context;
http_request_set_method(http_request, "RPC_OUT_DATA");
}
else
{
return NULL;
}
http_request->ContentLength = content_length;
http_request_set_uri(http_request, http_context->URI);
http_request_set_auth_scheme(http_request, "NTLM");
http_request_set_auth_param(http_request, base64_ntlm_token);
s = http_request_write(http_context, http_request);
http_request_free(http_request);
free(base64_ntlm_token);
return s;
}
int rpc_ncacn_http_send_in_channel_request(rdpRpc* rpc)
{
STREAM* s;
int content_length;
BOOL continue_needed;
rdpNtlm* ntlm = rpc->NtlmHttpIn->ntlm;
continue_needed = ntlm_authenticate(ntlm);
content_length = (continue_needed) ? 0 : 0x40000000;
s = rpc_ntlm_http_request(rpc, &ntlm->outputBuffer, content_length, TSG_CHANNEL_IN);
DEBUG_RPC("\n%s", s->data);
rpc_in_write(rpc, s->data, s->size);
stream_free(s);
return 0;
}
int rpc_ncacn_http_recv_in_channel_response(rdpRpc* rpc)
{
int ntlm_token_length;
BYTE* ntlm_token_data;
HttpResponse* http_response;
rdpNtlm* ntlm = rpc->NtlmHttpIn->ntlm;
http_response = http_response_recv(rpc->TlsIn);
ntlm_token_data = NULL;
crypto_base64_decode((BYTE*) http_response->AuthParam, strlen(http_response->AuthParam),
&ntlm_token_data, &ntlm_token_length);
ntlm->inputBuffer.pvBuffer = ntlm_token_data;
ntlm->inputBuffer.cbBuffer = ntlm_token_length;
http_response_free(http_response);
return 0;
}
int rpc_ncacn_http_ntlm_init(rdpRpc* rpc, TSG_CHANNEL channel)
{
rdpNtlm* ntlm = NULL;
rdpSettings* settings;
settings = rpc->settings;
if (channel == TSG_CHANNEL_IN)
ntlm = rpc->NtlmHttpIn->ntlm;
else if (channel == TSG_CHANNEL_OUT)
ntlm = rpc->NtlmHttpOut->ntlm;
if (settings->GatewayUseSameCredentials)
{
ntlm_client_init(ntlm, TRUE, settings->Username,
settings->Domain, settings->Password);
}
else
{
ntlm_client_init(ntlm, TRUE, settings->GatewayUsername,
settings->GatewayDomain, settings->GatewayPassword);
}
ntlm_client_make_spn(ntlm, NULL, settings->GatewayHostname);
//ntlm_client_make_spn(ntlm, _T("HTTP"), settings->GatewayHostname);
return 0;
}
BOOL rpc_ntlm_http_in_connect(rdpRpc* rpc)
{
rdpNtlm* ntlm = rpc->NtlmHttpIn->ntlm;
rpc_ncacn_http_ntlm_init(rpc, TSG_CHANNEL_IN);
/* Send IN Channel Request */
rpc_ncacn_http_send_in_channel_request(rpc);
/* Receive IN Channel Response */
rpc_ncacn_http_recv_in_channel_response(rpc);
/* Send IN Channel Request */
rpc_ncacn_http_send_in_channel_request(rpc);
ntlm_client_uninit(ntlm);
ntlm_free(ntlm);
return TRUE;
}
int rpc_ncacn_http_send_out_channel_request(rdpRpc* rpc)
{
STREAM* s;
int content_length;
BOOL continue_needed;
rdpNtlm* ntlm = rpc->NtlmHttpOut->ntlm;
continue_needed = ntlm_authenticate(ntlm);
content_length = (continue_needed) ? 0 : 76;
s = rpc_ntlm_http_request(rpc, &ntlm->outputBuffer, content_length, TSG_CHANNEL_OUT);
DEBUG_RPC("\n%s", s->data);
rpc_out_write(rpc, s->data, s->size);
stream_free(s);
return 0;
}
int rpc_ncacn_http_recv_out_channel_response(rdpRpc* rpc)
{
int ntlm_token_length;
BYTE* ntlm_token_data;
HttpResponse* http_response;
rdpNtlm* ntlm = rpc->NtlmHttpOut->ntlm;
http_response = http_response_recv(rpc->TlsOut);
ntlm_token_data = NULL;
crypto_base64_decode((BYTE*) http_response->AuthParam, strlen(http_response->AuthParam),
&ntlm_token_data, &ntlm_token_length);
ntlm->inputBuffer.pvBuffer = ntlm_token_data;
ntlm->inputBuffer.cbBuffer = ntlm_token_length;
http_response_free(http_response);
return 0;
}
BOOL rpc_ntlm_http_out_connect(rdpRpc* rpc)
{
rdpNtlm* ntlm = rpc->NtlmHttpOut->ntlm;
rpc_ncacn_http_ntlm_init(rpc, TSG_CHANNEL_OUT);
/* Send OUT Channel Request */
rpc_ncacn_http_send_out_channel_request(rpc);
/* Receive OUT Channel Response */
rpc_ncacn_http_recv_out_channel_response(rpc);
/* Send OUT Channel Request */
rpc_ncacn_http_send_out_channel_request(rpc);
ntlm_client_uninit(ntlm);
ntlm_free(ntlm);
return TRUE;
}

View File

@ -0,0 +1,39 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RPC over HTTP (ncacn_http)
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CORE_NCACN_HTTP_H
#define FREERDP_CORE_NCACN_HTTP_H
#include <freerdp/types.h>
#include <freerdp/settings.h>
#include <freerdp/crypto/tls.h>
#include <freerdp/crypto/crypto.h>
#include <freerdp/utils/sleep.h>
#include <freerdp/utils/debug.h>
#include <freerdp/utils/stream.h>
#include <freerdp/utils/hexdump.h>
#include "rpc.h"
STREAM* rpc_ntlm_http_request(rdpRpc* rpc, SecBuffer* ntlm_token, int content_length, TSG_CHANNEL channel);
BOOL rpc_ntlm_http_out_connect(rdpRpc* rpc);
BOOL rpc_ntlm_http_in_connect(rdpRpc* rpc);
#endif

View File

@ -153,6 +153,44 @@ BOOL ntlm_client_make_spn(rdpNtlm* ntlm, LPCTSTR ServiceClass, char* hostname)
return TRUE;
}
/**
* SSPI Client Ceremony
*
* --------------
* ( Client Begin )
* --------------
* |
* |
* \|/
* -----------+--------------
* | AcquireCredentialsHandle |
* --------------------------
* |
* |
* \|/
* -------------+--------------
* +---------------> / InitializeSecurityContext /
* | ----------------------------
* | |
* | |
* | \|/
* --------------------------- ---------+------------- ----------------------
* / Receive blob from server / < Received security blob? > --Yes-> / Send blob to server /
* -------------+------------- ----------------------- ----------------------
* /|\ | |
* | No |
* Yes \|/ |
* | ------------+----------- |
* +---------------- < Received Continue Needed > <-----------------+
* ------------------------
* |
* No
* \|/
* ------+-------
* ( Client End )
* --------------
*/
BOOL ntlm_authenticate(rdpNtlm* ntlm)
{
SECURITY_STATUS status;
@ -188,7 +226,7 @@ BOOL ntlm_authenticate(rdpNtlm* ntlm)
if (ntlm->table->QueryContextAttributes(&ntlm->context, SECPKG_ATTR_SIZES, &ntlm->ContextSizes) != SEC_E_OK)
{
printf("QueryContextAttributes SECPKG_ATTR_SIZES failure\n");
return FALSE ;
return FALSE;
}
if (status == SEC_I_COMPLETE_NEEDED)
@ -200,7 +238,7 @@ BOOL ntlm_authenticate(rdpNtlm* ntlm)
ntlm->haveInputBuffer = TRUE;
ntlm->haveContext = TRUE;
return TRUE;
return (status == SEC_I_CONTINUE_NEEDED) ? TRUE : FALSE;
}
void ntlm_client_uninit(rdpNtlm* ntlm)

View File

@ -35,6 +35,7 @@
#include "http.h"
#include "ntlm.h"
#include "ncacn_http.h"
#include "rpc.h"
@ -263,179 +264,6 @@ void rpc_pdu_header_print(rpcconn_hdr_t* header)
}
}
STREAM* rpc_ntlm_http_request(rdpRpc* rpc, SecBuffer* ntlm_token, int content_length, TSG_CHANNEL channel)
{
STREAM* s;
char* base64_ntlm_token;
HttpContext* http_context;
HttpRequest* http_request;
http_request = http_request_new();
base64_ntlm_token = crypto_base64_encode(ntlm_token->pvBuffer, ntlm_token->cbBuffer);
if (channel == TSG_CHANNEL_IN)
{
http_context = rpc->NtlmHttpIn->context;
http_request_set_method(http_request, "RPC_IN_DATA");
}
else if (channel == TSG_CHANNEL_OUT)
{
http_context = rpc->NtlmHttpOut->context;
http_request_set_method(http_request, "RPC_OUT_DATA");
}
else
{
return NULL;
}
http_request->ContentLength = content_length;
http_request_set_uri(http_request, http_context->URI);
http_request_set_auth_scheme(http_request, "NTLM");
http_request_set_auth_param(http_request, base64_ntlm_token);
s = http_request_write(http_context, http_request);
http_request_free(http_request);
free(base64_ntlm_token);
return s;
}
BOOL rpc_ntlm_http_out_connect(rdpRpc* rpc)
{
STREAM* s;
rdpSettings* settings;
int ntlm_token_length;
BYTE* ntlm_token_data;
HttpResponse* http_response;
rdpNtlm* ntlm = rpc->NtlmHttpOut->ntlm;
settings = rpc->settings;
if (settings->GatewayUseSameCredentials)
{
ntlm_client_init(ntlm, TRUE, settings->Username,
settings->Domain, settings->Password);
ntlm_client_make_spn(ntlm, NULL, settings->GatewayHostname);
//ntlm_client_make_spn(ntlm, _T("HTTP"), settings->GatewayHostname);
}
else
{
ntlm_client_init(ntlm, TRUE, settings->GatewayUsername,
settings->GatewayDomain, settings->GatewayPassword);
ntlm_client_make_spn(ntlm, NULL, settings->GatewayHostname);
//ntlm_client_make_spn(ntlm, _T("HTTP"), settings->GatewayHostname);
}
ntlm_authenticate(ntlm);
s = rpc_ntlm_http_request(rpc, &ntlm->outputBuffer, 0, TSG_CHANNEL_OUT);
/* Send OUT Channel Request */
DEBUG_RPC("\n%s", s->data);
rpc_out_write(rpc, s->data, s->size);
stream_free(s);
/* Receive OUT Channel Response */
http_response = http_response_recv(rpc->TlsOut);
ntlm_token_data = NULL;
crypto_base64_decode((BYTE*) http_response->AuthParam, strlen(http_response->AuthParam),
&ntlm_token_data, &ntlm_token_length);
ntlm->inputBuffer.pvBuffer = ntlm_token_data;
ntlm->inputBuffer.cbBuffer = ntlm_token_length;
ntlm_authenticate(ntlm);
http_response_free(http_response);
s = rpc_ntlm_http_request(rpc, &ntlm->outputBuffer, 76, TSG_CHANNEL_OUT);
/* Send OUT Channel Request */
DEBUG_RPC("\n%s", s->data);
rpc_out_write(rpc, s->data, s->size);
stream_free(s);
ntlm_client_uninit(ntlm);
ntlm_free(ntlm);
return TRUE;
}
BOOL rpc_ntlm_http_in_connect(rdpRpc* rpc)
{
STREAM* s;
rdpSettings* settings;
int ntlm_token_length;
BYTE* ntlm_token_data;
HttpResponse* http_response;
rdpNtlm* ntlm = rpc->NtlmHttpIn->ntlm;
settings = rpc->settings;
if (settings->GatewayUseSameCredentials)
{
ntlm_client_init(ntlm, TRUE, settings->Username,
settings->Domain, settings->Password);
ntlm_client_make_spn(ntlm, NULL, settings->GatewayHostname);
//ntlm_client_make_spn(ntlm, _T("HTTP"), settings->GatewayHostname);
}
else
{
ntlm_client_init(ntlm, TRUE, settings->GatewayUsername,
settings->GatewayDomain, settings->GatewayPassword);
ntlm_client_make_spn(ntlm, NULL, settings->GatewayHostname);
//ntlm_client_make_spn(ntlm, _T("HTTP"), settings->GatewayHostname);
}
ntlm_authenticate(ntlm);
s = rpc_ntlm_http_request(rpc, &ntlm->outputBuffer, 0, TSG_CHANNEL_IN);
/* Send IN Channel Request */
DEBUG_RPC("\n%s", s->data);
rpc_in_write(rpc, s->data, s->size);
stream_free(s);
/* Receive IN Channel Response */
http_response = http_response_recv(rpc->TlsIn);
ntlm_token_data = NULL;
crypto_base64_decode((BYTE*) http_response->AuthParam, strlen(http_response->AuthParam),
&ntlm_token_data, &ntlm_token_length);
ntlm->inputBuffer.pvBuffer = ntlm_token_data;
ntlm->inputBuffer.cbBuffer = ntlm_token_length;
ntlm_authenticate(ntlm);
http_response_free(http_response);
s = rpc_ntlm_http_request(rpc, &ntlm->outputBuffer, 0x40000000, TSG_CHANNEL_IN);
/* Send IN Channel Request */
DEBUG_RPC("\n%s", s->data);
rpc_in_write(rpc, s->data, s->size);
stream_free(s);
ntlm_client_uninit(ntlm);
ntlm_free(ntlm);
return TRUE;
}
void rpc_pdu_header_init(rdpRpc* rpc, rpcconn_hdr_t* header)
{
header->common.rpc_vers = rpc->rpc_vers;