rdesktop/cssp.c
Pierre Ossman 3e340f2f20 Add explicit STREAM allocation function
Avoids mistakes by making sure everyone allocates these the same
way.

The smart card code still has manual allocation because it has it's
own magical memory management.
2019-05-06 14:33:05 +02:00

861 lines
21 KiB
C

/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
CredSSP layer and Kerberos support.
Copyright 2012-2017 Henrik Andersson <hean01@cendio.se> for Cendio AB
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <gssapi/gssapi.h>
#include "rdesktop.h"
extern RD_BOOL g_use_password_as_pin;
extern char *g_sc_csp_name;
extern char *g_sc_reader_name;
extern char *g_sc_card_name;
extern char *g_sc_container_name;
static gss_OID_desc _gss_spnego_krb5_mechanism_oid_desc =
{ 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
static STREAM
ber_wrap_hdr_data(int tagval, STREAM in)
{
STREAM out;
int size = s_length(in) + 16;
out = s_alloc(size);
ber_out_header(out, tagval, s_length(in));
out_uint8p(out, in->data, s_length(in));
s_mark_end(out);
return out;
}
static void
cssp_gss_report_error(OM_uint32 code, char *str, OM_uint32 major_status, OM_uint32 minor_status)
{
OM_uint32 msgctx = 0, ms;
gss_buffer_desc status_string;
logger(Core, Debug, "GSS error [%d:%d:%d]: %s", (major_status & 0xff000000) >> 24, // Calling error
(major_status & 0xff0000) >> 16, // Routine error
major_status & 0xffff, // Supplementary info bits
str);
do
{
ms = gss_display_status(&minor_status, major_status,
code, GSS_C_NULL_OID, &msgctx, &status_string);
if (ms != GSS_S_COMPLETE)
continue;
logger(Core, Debug, " - %s", status_string.value);
}
while (ms == GSS_S_COMPLETE && msgctx);
}
static RD_BOOL
cssp_gss_mech_available(gss_OID mech)
{
int mech_found;
OM_uint32 major_status, minor_status;
gss_OID_set mech_set;
mech_found = 0;
if (mech == GSS_C_NO_OID)
return True;
major_status = gss_indicate_mechs(&minor_status, &mech_set);
if (!mech_set)
return False;
if (GSS_ERROR(major_status))
{
cssp_gss_report_error(GSS_C_GSS_CODE, "Failed to get available mechs on system",
major_status, minor_status);
return False;
}
gss_test_oid_set_member(&minor_status, mech, mech_set, &mech_found);
if (GSS_ERROR(major_status))
{
cssp_gss_report_error(GSS_C_GSS_CODE, "Failed to match mechanism in set",
major_status, minor_status);
return False;
}
if (!mech_found)
return False;
return True;
}
static RD_BOOL
cssp_gss_get_service_name(char *server, gss_name_t * name)
{
gss_buffer_desc output;
OM_uint32 major_status, minor_status;
const char service_name[] = "TERMSRV";
gss_OID type = (gss_OID) GSS_C_NT_HOSTBASED_SERVICE;
int size = (strlen(service_name) + 1 + strlen(server) + 1);
output.value = malloc(size);
snprintf(output.value, size, "%s@%s", service_name, server);
output.length = strlen(output.value) + 1;
major_status = gss_import_name(&minor_status, &output, type, name);
if (GSS_ERROR(major_status))
{
cssp_gss_report_error(GSS_C_GSS_CODE, "Failed to create service principal name",
major_status, minor_status);
return False;
}
gss_release_buffer(&minor_status, &output);
return True;
}
static RD_BOOL
cssp_gss_wrap(gss_ctx_id_t ctx, STREAM in, STREAM out)
{
int conf_state;
OM_uint32 major_status;
OM_uint32 minor_status;
gss_buffer_desc inbuf, outbuf;
inbuf.value = in->data;
inbuf.length = s_length(in);
major_status = gss_wrap(&minor_status, ctx, True,
GSS_C_QOP_DEFAULT, &inbuf, &conf_state, &outbuf);
if (major_status != GSS_S_COMPLETE)
{
cssp_gss_report_error(GSS_C_GSS_CODE, "Failed to encrypt and sign message",
major_status, minor_status);
return False;
}
if (!conf_state)
{
logger(Core, Error,
"cssp_gss_wrap(), GSS Confidentiality failed, no encryption of message performed.");
return False;
}
// write enc data to out stream
out->data = out->p = xmalloc(outbuf.length);
out->size = outbuf.length;
out_uint8p(out, outbuf.value, outbuf.length);
s_mark_end(out);
gss_release_buffer(&minor_status, &outbuf);
return True;
}
static RD_BOOL
cssp_gss_unwrap(gss_ctx_id_t ctx, STREAM in, STREAM out)
{
OM_uint32 major_status;
OM_uint32 minor_status;
gss_qop_t qop_state;
gss_buffer_desc inbuf, outbuf;
int conf_state;
inbuf.value = in->data;
inbuf.length = s_length(in);
major_status = gss_unwrap(&minor_status, ctx, &inbuf, &outbuf, &conf_state, &qop_state);
if (major_status != GSS_S_COMPLETE)
{
cssp_gss_report_error(GSS_C_GSS_CODE, "Failed to decrypt message",
major_status, minor_status);
return False;
}
out->data = out->p = xmalloc(outbuf.length);
out->size = outbuf.length;
out_uint8p(out, outbuf.value, outbuf.length);
s_mark_end(out);
gss_release_buffer(&minor_status, &outbuf);
return True;
}
static STREAM
cssp_encode_tspasswordcreds(char *username, char *password, char *domain)
{
STREAM out, h1, h2;
struct stream tmp = { 0 };
struct stream message = { 0 };
memset(&tmp, 0, sizeof(tmp));
memset(&message, 0, sizeof(message));
s_realloc(&tmp, 512 * 4);
// domainName [0]
s_reset(&tmp);
out_utf16s(&tmp, domain);
s_mark_end(&tmp);
h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 0, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h2);
s_free(h1);
// userName [1]
s_reset(&tmp);
out_utf16s(&tmp, username);
s_mark_end(&tmp);
h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 1, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h2);
s_free(h1);
// password [2]
s_reset(&tmp);
out_utf16s(&tmp, password);
s_mark_end(&tmp);
h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 2, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h2);
s_free(h1);
// build message
out = ber_wrap_hdr_data(BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED, &message);
// cleanup
xfree(tmp.data);
xfree(message.data);
return out;
}
/* KeySpecs from wincrypt.h */
#define AT_KEYEXCHANGE 1
#define AT_SIGNATURE 2
static STREAM
cssp_encode_tscspdatadetail(unsigned char keyspec, char *card, char *reader, char *container,
char *csp)
{
STREAM out;
STREAM h1, h2;
struct stream tmp = { 0 };
struct stream message = { 0 };
s_realloc(&tmp, 512 * 4);
// keySpec [0]
s_reset(&tmp);
out_uint8(&tmp, keyspec);
s_mark_end(&tmp);
h2 = ber_wrap_hdr_data(BER_TAG_INTEGER, &tmp);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 0, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h2);
s_free(h1);
// cardName [1]
if (card)
{
s_reset(&tmp);
out_utf16s(&tmp, card);
s_mark_end(&tmp);
h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 1, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h2);
s_free(h1);
}
// readerName [2]
if (reader)
{
s_reset(&tmp);
out_utf16s(&tmp, reader);
s_mark_end(&tmp);
h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 2, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h2);
s_free(h1);
}
// containerName [3]
if (container)
{
s_reset(&tmp);
out_utf16s(&tmp, container);
s_mark_end(&tmp);
h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 3, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h2);
s_free(h1);
}
// cspName [4]
if (csp)
{
s_reset(&tmp);
out_utf16s(&tmp, csp);
s_mark_end(&tmp);
h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 4, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h2);
s_free(h1);
}
s_mark_end(&message);
// build message
out = ber_wrap_hdr_data(BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED, &message);
// cleanup
free(tmp.data);
free(message.data);
return out;
}
static STREAM
cssp_encode_tssmartcardcreds(char *username, char *password, char *domain)
{
STREAM out, h1, h2;
struct stream tmp = { 0 };
struct stream message = { 0 };
s_realloc(&tmp, 512 * 4);
// pin [0]
s_reset(&tmp);
out_utf16s(&tmp, password);
s_mark_end(&tmp);
h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 0, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h2);
s_free(h1);
// cspData [1]
h2 = cssp_encode_tscspdatadetail(AT_KEYEXCHANGE, g_sc_card_name, g_sc_reader_name,
g_sc_container_name, g_sc_csp_name);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 1, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h2);
s_free(h1);
// userHint [2]
if (username && strlen(username))
{
s_reset(&tmp);
out_utf16s(&tmp, username);
s_mark_end(&tmp);
h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 2, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h2);
s_free(h1);
}
// domainHint [3]
if (domain && strlen(domain))
{
s_reset(&tmp);
out_utf16s(&tmp, domain);
s_mark_end(&tmp);
h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, &tmp);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 3, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h2);
s_free(h1);
}
s_mark_end(&message);
// build message
out = ber_wrap_hdr_data(BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED, &message);
// cleanup
free(tmp.data);
free(message.data);
return out;
}
STREAM
cssp_encode_tscredentials(char *username, char *password, char *domain)
{
STREAM out;
STREAM h1, h2, h3;
struct stream tmp = { 0 };
struct stream message = { 0 };
// credType [0]
s_realloc(&tmp, sizeof(uint8));
s_reset(&tmp);
if (g_use_password_as_pin == False)
{
out_uint8(&tmp, 1); // TSPasswordCreds
}
else
{
out_uint8(&tmp, 2); // TSSmartCardCreds
}
s_mark_end(&tmp);
h2 = ber_wrap_hdr_data(BER_TAG_INTEGER, &tmp);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 0, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h2);
s_free(h1);
// credentials [1]
if (g_use_password_as_pin == False)
{
h3 = cssp_encode_tspasswordcreds(username, password, domain);
}
else
{
h3 = cssp_encode_tssmartcardcreds(username, password, domain);
}
h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, h3);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 1, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h3);
s_free(h2);
s_free(h1);
// Construct ASN.1 message
out = ber_wrap_hdr_data(BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED, &message);
// cleanup
xfree(message.data);
xfree(tmp.data);
return out;
}
RD_BOOL
cssp_send_tsrequest(STREAM token, STREAM auth, STREAM pubkey)
{
STREAM s;
STREAM h1, h2, h3, h4, h5;
struct stream tmp = { 0 };
struct stream message = { 0 };
memset(&message, 0, sizeof(message));
memset(&tmp, 0, sizeof(tmp));
// version [0]
s_realloc(&tmp, sizeof(uint8));
s_reset(&tmp);
out_uint8(&tmp, 2);
s_mark_end(&tmp);
h2 = ber_wrap_hdr_data(BER_TAG_INTEGER, &tmp);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 0, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h2);
s_free(h1);
// negoToken [1]
if (token && s_length(token))
{
h5 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, token);
h4 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 0, h5);
h3 = ber_wrap_hdr_data(BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED, h4);
h2 = ber_wrap_hdr_data(BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED, h3);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 1, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h5);
s_free(h4);
s_free(h3);
s_free(h2);
s_free(h1);
}
// authInfo [2]
if (auth && s_length(auth))
{
h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, auth);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 2, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_free(h2);
s_free(h1);
}
// pubKeyAuth [3]
if (pubkey && s_length(pubkey))
{
h2 = ber_wrap_hdr_data(BER_TAG_OCTET_STRING, pubkey);
h1 = ber_wrap_hdr_data(BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 3, h2);
s_realloc(&message, s_length(&message) + s_length(h1));
out_uint8p(&message, h1->data, s_length(h1));
s_mark_end(&message);
s_free(h2);
s_free(h1);
}
s_mark_end(&message);
// Construct ASN.1 Message
// Todo: can h1 be send directly instead of tcp_init() approach
h1 = ber_wrap_hdr_data(BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED, &message);
s = tcp_init(s_length(h1));
out_uint8p(s, h1->data, s_length(h1));
s_mark_end(s);
s_free(h1);
tcp_send(s);
// cleanup
xfree(message.data);
xfree(tmp.data);
return True;
}
RD_BOOL
cssp_read_tsrequest(STREAM token, STREAM pubkey)
{
STREAM s;
int length;
int tagval;
struct stream packet;
s = tcp_recv(NULL, 4);
if (s == NULL)
return False;
// verify ASN.1 header
if (s->p[0] != (BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED))
{
logger(Protocol, Error,
"cssp_read_tsrequest(), expected BER_TAG_SEQUENCE|BER_TAG_CONSTRUCTED, got %x",
s->p[0]);
return False;
}
// peek at first 4 bytes to get full message length
if (s->p[1] < 0x80)
length = s->p[1] - 2;
else if (s->p[1] == 0x81)
length = s->p[2] - 1;
else if (s->p[1] == 0x82)
length = (s->p[2] << 8) | s->p[3];
else
return False;
// receive the remainings of message
s = tcp_recv(s, length);
packet = *s;
// parse the response and into nego token
if (!ber_in_header(s, &tagval, &length) ||
tagval != (BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED))
return False;
// version [0]
if (!ber_in_header(s, &tagval, &length) ||
tagval != (BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 0))
return False;
if (!s_check_rem(s, length))
{
rdp_protocol_error("consume of version from stream would overrun",
&packet);
}
in_uint8s(s, length);
// negoToken [1]
if (token)
{
if (!ber_in_header(s, &tagval, &length)
|| tagval != (BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 1))
return False;
if (!ber_in_header(s, &tagval, &length)
|| tagval != (BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED))
return False;
if (!ber_in_header(s, &tagval, &length)
|| tagval != (BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED))
return False;
if (!ber_in_header(s, &tagval, &length)
|| tagval != (BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 0))
return False;
if (!ber_in_header(s, &tagval, &length) || tagval != BER_TAG_OCTET_STRING)
return False;
if (!s_check_rem(s, length))
{
rdp_protocol_error("consume of token from stream would overrun",
&packet);
}
s_realloc(token, length);
s_reset(token);
out_uint8p(token, s->p, length);
s_mark_end(token);
}
// pubKey [3]
if (pubkey)
{
if (!ber_in_header(s, &tagval, &length)
|| tagval != (BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 3))
return False;
if (!ber_in_header(s, &tagval, &length) || tagval != BER_TAG_OCTET_STRING)
return False;
pubkey->data = pubkey->p = s->p;
pubkey->end = pubkey->data + length;
pubkey->size = length;
}
return True;
}
RD_BOOL
cssp_connect(char *server, char *user, char *domain, char *password, STREAM s)
{
UNUSED(s);
OM_uint32 actual_time;
gss_cred_id_t cred;
gss_buffer_desc input_tok, output_tok;
gss_name_t target_name;
OM_uint32 major_status, minor_status;
int context_established = 0;
gss_ctx_id_t gss_ctx;
gss_OID desired_mech = &_gss_spnego_krb5_mechanism_oid_desc;
STREAM ts_creds;
struct stream token = { 0 };
struct stream pubkey = { 0 };
struct stream pubkey_cmp = { 0 };
// Verify that system gss support spnego
if (!cssp_gss_mech_available(desired_mech))
{
logger(Core, Debug,
"cssp_connect(), system doesn't have support for desired authentication mechanism");
return False;
}
// Get service name
if (!cssp_gss_get_service_name(server, &target_name))
{
logger(Core, Debug, "cssp_connect(), failed to get target service name");
return False;
}
// Establish TLS connection to server
if (!tcp_tls_connect())
{
logger(Core, Debug, "cssp_connect(), failed to establish TLS connection");
return False;
}
tcp_tls_get_server_pubkey(&pubkey);
// Enter the spnego loop
OM_uint32 actual_services;
gss_OID actual_mech;
struct stream blob = { 0 };
gss_ctx = GSS_C_NO_CONTEXT;
cred = GSS_C_NO_CREDENTIAL;
input_tok.length = 0;
output_tok.length = 0;
minor_status = 0;
int i = 0;
do
{
major_status = gss_init_sec_context(&minor_status,
cred,
&gss_ctx,
target_name,
desired_mech,
GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG,
GSS_C_INDEFINITE,
GSS_C_NO_CHANNEL_BINDINGS,
&input_tok,
&actual_mech,
&output_tok, &actual_services, &actual_time);
if (GSS_ERROR(major_status))
{
if (i == 0)
logger(Core, Notice,
"Failed to initialize NLA, do you have correct Kerberos TGT initialized ?");
else
logger(Core, Error, "cssp_connect(), negotiation failed");
cssp_gss_report_error(GSS_C_GSS_CODE, "cssp_connect(), negotiation failed.",
major_status, minor_status);
goto bail_out;
}
// validate required services
if (!(actual_services & GSS_C_CONF_FLAG))
{
logger(Core, Error,
"cssp_connect(), confidentiality service required but is not available");
goto bail_out;
}
// Send token to server
if (output_tok.length != 0)
{
if (output_tok.length > token.size)
s_realloc(&token, output_tok.length);
s_reset(&token);
out_uint8p(&token, output_tok.value, output_tok.length);
s_mark_end(&token);
if (!cssp_send_tsrequest(&token, NULL, NULL))
goto bail_out;
(void) gss_release_buffer(&minor_status, &output_tok);
}
// Read token from server
if (major_status & GSS_S_CONTINUE_NEEDED)
{
(void) gss_release_buffer(&minor_status, &input_tok);
if (!cssp_read_tsrequest(&token, NULL))
goto bail_out;
input_tok.value = token.data;
input_tok.length = s_length(&token);
}
else
{
// Send encrypted pubkey for verification to server
context_established = 1;
if (!cssp_gss_wrap(gss_ctx, &pubkey, &blob))
goto bail_out;
if (!cssp_send_tsrequest(NULL, NULL, &blob))
goto bail_out;
context_established = 1;
}
i++;
}
while (!context_established);
// read tsrequest response and decrypt for public key validation
if (!cssp_read_tsrequest(NULL, &blob))
goto bail_out;
if (!cssp_gss_unwrap(gss_ctx, &blob, &pubkey_cmp))
goto bail_out;
pubkey_cmp.data[0] -= 1;
// validate public key
if (memcmp(pubkey.data, pubkey_cmp.data, s_length(&pubkey)) != 0)
{
logger(Core, Error,
"cssp_connect(), public key mismatch, cannot guarantee integrity of server connection");
goto bail_out;
}
// Send TSCredentials
ts_creds = cssp_encode_tscredentials(user, password, domain);
if (!cssp_gss_wrap(gss_ctx, ts_creds, &blob))
goto bail_out;
s_free(ts_creds);
if (!cssp_send_tsrequest(NULL, &blob, NULL))
goto bail_out;
return True;
bail_out:
xfree(token.data);
return False;
}