Initial implementation of kerberos server autentication with CredSSP,
disabled by default and is enabled using argument --enable-credssp to configure script. git-svn-id: svn://svn.code.sf.net/p/rdesktop/code/rdesktop/trunk@1676 423420c4-83ab-492f-b58f-81f9feb106b5
This commit is contained in:
parent
6d437f8116
commit
51014c91df
11
Makefile.in
11
Makefile.in
@ -26,19 +26,20 @@ LDVNC = @LDVNC@
|
||||
VNCLINK = @VNCLINK@
|
||||
SOUNDOBJ = @SOUNDOBJ@
|
||||
SCARDOBJ = @SCARDOBJ@
|
||||
CREDSSPOBJ = @CREDSSPOBJ@
|
||||
|
||||
RDPOBJ = tcp.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o disk.o parallel.o printercache.o mppc.o pstcache.o lspci.o seamless.o ssl.o
|
||||
RDPOBJ = tcp.o asn.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o disk.o parallel.o printercache.o mppc.o pstcache.o lspci.o seamless.o ssl.o
|
||||
X11OBJ = rdesktop.o xwin.o xkeymap.o ewmhints.o xclip.o cliprdr.o
|
||||
VNCOBJ = vnc/rdp2vnc.o vnc/vnc.o vnc/xkeymap.o vnc/x11stubs.o
|
||||
|
||||
.PHONY: all
|
||||
all: $(TARGETS)
|
||||
|
||||
rdesktop: $(X11OBJ) $(SOUNDOBJ) $(RDPOBJ) $(SCARDOBJ)
|
||||
$(CC) $(CFLAGS) -o rdesktop $(X11OBJ) $(SOUNDOBJ) $(RDPOBJ) $(SCARDOBJ) $(LDFLAGS) -lX11
|
||||
rdesktop: $(X11OBJ) $(SOUNDOBJ) $(RDPOBJ) $(SCARDOBJ) $(CREDSSPOBJ)
|
||||
$(CC) $(CFLAGS) -o rdesktop $(X11OBJ) $(SOUNDOBJ) $(RDPOBJ) $(SCARDOBJ) $(CREDSSPOBJ) $(LDFLAGS) -lX11
|
||||
|
||||
rdp2vnc: $(VNCOBJ) $(SOUNDOBJ) $(RDPOBJ) $(SCARDOBJ)
|
||||
$(VNCLINK) $(CFLAGS) -o rdp2vnc $(VNCOBJ) $(SOUNDOBJ) $(RDPOBJ) $(SCARDOBJ) $(LDFLAGS) $(LDVNC)
|
||||
rdp2vnc: $(VNCOBJ) $(SOUNDOBJ) $(RDPOBJ) $(SCARDOBJ) $(CREDSSPOBJ)
|
||||
$(VNCLINK) $(CFLAGS) -o rdp2vnc $(VNCOBJ) $(SOUNDOBJ) $(RDPOBJ) $(SCARDOBJ) $(CREDSSPOBJ) $(LDFLAGS) $(LDVNC)
|
||||
|
||||
vnc/rdp2vnc.o: rdesktop.c
|
||||
$(CC) $(CFLAGS) $(VNCINC) -DRDP2VNC -o vnc/rdp2vnc.o -c rdesktop.c
|
||||
|
109
asn.c
Normal file
109
asn.c
Normal file
@ -0,0 +1,109 @@
|
||||
/* -*- c-basic-offset: 8 -*-
|
||||
rdesktop: A Remote Desktop Protocol client.
|
||||
ASN.1 utility functions
|
||||
Copyright 2010-2011 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 "rdesktop.h"
|
||||
|
||||
|
||||
/* Parse an ASN.1 BER header */
|
||||
RD_BOOL
|
||||
ber_parse_header(STREAM s, int tagval, int *length)
|
||||
{
|
||||
int tag, len;
|
||||
|
||||
if (tagval > 0xff)
|
||||
{
|
||||
in_uint16_be(s, tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
in_uint8(s, tag);
|
||||
}
|
||||
|
||||
if (tag != tagval)
|
||||
{
|
||||
error("expected tag %d, got %d\n", tagval, tag);
|
||||
return False;
|
||||
}
|
||||
|
||||
in_uint8(s, len);
|
||||
|
||||
if (len & 0x80)
|
||||
{
|
||||
len &= ~0x80;
|
||||
*length = 0;
|
||||
while (len--)
|
||||
next_be(s, *length);
|
||||
}
|
||||
else
|
||||
*length = len;
|
||||
|
||||
return s_check(s);
|
||||
}
|
||||
|
||||
/* Output an ASN.1 BER header */
|
||||
void
|
||||
ber_out_header(STREAM s, int tagval, int length)
|
||||
{
|
||||
if (tagval > 0xff)
|
||||
{
|
||||
out_uint16_be(s, tagval);
|
||||
}
|
||||
else
|
||||
{
|
||||
out_uint8(s, tagval);
|
||||
}
|
||||
|
||||
if (length >= 0x80)
|
||||
{
|
||||
out_uint8(s, 0x82);
|
||||
out_uint16_be(s, length);
|
||||
}
|
||||
else
|
||||
out_uint8(s, length);
|
||||
}
|
||||
|
||||
/* Output an ASN.1 BER integer */
|
||||
void
|
||||
ber_out_integer(STREAM s, int value)
|
||||
{
|
||||
ber_out_header(s, BER_TAG_INTEGER, 2);
|
||||
out_uint16_be(s, value);
|
||||
}
|
||||
|
||||
RD_BOOL
|
||||
ber_in_header(STREAM s, int *tagval, int *decoded_len)
|
||||
{
|
||||
in_uint8(s, *tagval);
|
||||
in_uint8(s, *decoded_len);
|
||||
|
||||
if (*decoded_len < 0x80)
|
||||
return True;
|
||||
else if (*decoded_len == 0x81)
|
||||
{
|
||||
in_uint8(s, *decoded_len);
|
||||
return True;
|
||||
}
|
||||
else if (*decoded_len == 0x82)
|
||||
{
|
||||
in_uint16_be(s, *decoded_len);
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
34
configure.ac
34
configure.ac
@ -120,6 +120,27 @@ else
|
||||
esac
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(credssp,
|
||||
[ --enable-credssp enables CredSSP support (Kerberos only).
|
||||
], [
|
||||
if test -n "$PKG_CONFIG"; then
|
||||
PKG_CHECK_MODULES(GSSGLUE, libgssglue, [
|
||||
CREDSSPOBJ="cssp.o"
|
||||
CFLAGS="$CFLAGS $GSSGLUE_CFLAGS"
|
||||
LIBS="$LIBS $GSSGLUE_LIBS"
|
||||
AC_DEFINE(WITH_CREDSSP)
|
||||
WITH_CREDSSP=1
|
||||
], [
|
||||
AC_MSG_RESULT([Not found])
|
||||
echo
|
||||
echo "ERROR: Could not find gssglue headers/libraries required for CredSSP."
|
||||
exit 1
|
||||
])
|
||||
fi
|
||||
])
|
||||
|
||||
AC_SUBST(CREDSSPOBJ)
|
||||
|
||||
# xrandr
|
||||
if test -n "$PKG_CONFIG"; then
|
||||
PKG_CHECK_MODULES(XRANDR, xrandr, [HAVE_XRANDR=1], [HAVE_XRANDR=0])
|
||||
@ -131,7 +152,7 @@ if test x"$HAVE_XRANDR" = "x1"; then
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(smartcard,
|
||||
[ --enable-smartcard Enables smart-card support.
|
||||
[ --enable-smartcard enables smart-card support.
|
||||
],
|
||||
[
|
||||
case "$OSTYPE" in
|
||||
@ -905,6 +926,17 @@ AC_ARG_WITH(debug-smartcard,
|
||||
fi
|
||||
])
|
||||
|
||||
AC_ARG_WITH(debug-credssp,
|
||||
[ --with-debug-credssp enable debugging of CredSSP code],
|
||||
[
|
||||
if test $withval != "no";
|
||||
then
|
||||
if test x"$WITH_CREDSSP" = "x1"; then
|
||||
AC_DEFINE(WITH_DEBUG_CREDSSP,1)
|
||||
fi
|
||||
fi
|
||||
])
|
||||
|
||||
#
|
||||
# target-specific stuff
|
||||
#
|
||||
|
@ -78,6 +78,10 @@ enum MCS_PDU_TYPE
|
||||
#define BER_TAG_INTEGER 2
|
||||
#define BER_TAG_OCTET_STRING 4
|
||||
#define BER_TAG_RESULT 10
|
||||
#define BER_TAG_SEQUENCE 16
|
||||
#define BER_TAG_CONSTRUCTED 0x20
|
||||
#define BER_TAG_CTXT_SPECIFIC 0x80
|
||||
|
||||
#define MCS_TAG_DOMAIN_PARAMS 0x30
|
||||
|
||||
#define MCS_GLOBAL_CHANNEL 1003
|
||||
|
680
cssp.c
Normal file
680
cssp.c
Normal file
@ -0,0 +1,680 @@
|
||||
#include <gssapi.h>
|
||||
#include <gssapi/gssapi_krb5.h>
|
||||
#include "rdesktop.h"
|
||||
|
||||
static gss_OID_desc _gss_spnego_krb5_mechanism_oid_desc =
|
||||
{ 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
|
||||
|
||||
|
||||
static void
|
||||
s_free(STREAM s)
|
||||
{
|
||||
free(s->data);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static STREAM
|
||||
ber_wrap_hdr_data(int tagval, STREAM in)
|
||||
{
|
||||
STREAM out;
|
||||
int size = s_length(in) + 16;
|
||||
|
||||
out = xmalloc(sizeof(struct stream));
|
||||
|
||||
out->data = xmalloc(size);
|
||||
out->size = size;
|
||||
out->p = out->data;
|
||||
|
||||
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;
|
||||
|
||||
error("GSS error [%d:%d:%d]: %s\n", (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;
|
||||
|
||||
error(" - %s\n", 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 (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)
|
||||
{
|
||||
error("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;
|
||||
}
|
||||
|
||||
#ifdef WITH_DEBUG_CREDSSP
|
||||
void
|
||||
streamsave(STREAM s, char *fn)
|
||||
{
|
||||
FILE *f = fopen(fn, "wb");
|
||||
fwrite(s->data, s_length(s), 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
#endif
|
||||
|
||||
static STREAM
|
||||
cssp_encode_tspasswordcreds(char *username, char *password, char *domain)
|
||||
{
|
||||
int i;
|
||||
STREAM out, h1, h2;
|
||||
struct stream tmp = { 0 };
|
||||
struct stream message = { 0 };
|
||||
|
||||
// allocate local streams
|
||||
tmp.size = 4096;
|
||||
tmp.data = xmalloc(tmp.size);
|
||||
s_reset(&tmp);
|
||||
|
||||
message.size = 4096;
|
||||
message.data = xmalloc(message.size);
|
||||
s_reset(&message);
|
||||
|
||||
// domainName [0]
|
||||
s_reset(&tmp);
|
||||
for (i = 0; i < strlen(domain); i++)
|
||||
out_uint16_le(&tmp, domain[i]);
|
||||
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);
|
||||
out_uint8p(&message, h1->data, s_length(h1));
|
||||
s_free(h2);
|
||||
s_free(h1);
|
||||
|
||||
// userName [1]
|
||||
s_reset(&tmp);
|
||||
for (i = 0; i < strlen(username); i++)
|
||||
out_uint16_le(&tmp, username[i]);
|
||||
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);
|
||||
out_uint8p(&message, h1->data, s_length(h1));
|
||||
s_free(h2);
|
||||
s_free(h1);
|
||||
|
||||
// password [2]
|
||||
s_reset(&tmp);
|
||||
for (i = 0; i < strlen(password); i++)
|
||||
out_uint16_le(&tmp, password[i]);
|
||||
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);
|
||||
out_uint8p(&message, h1->data, s_length(h1));
|
||||
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 };
|
||||
|
||||
// allocate local streams
|
||||
tmp.size = 4096;
|
||||
tmp.data = xmalloc(tmp.size);
|
||||
s_reset(&tmp);
|
||||
|
||||
message.size = 4096;
|
||||
message.data = xmalloc(message.size);
|
||||
s_reset(&message);
|
||||
|
||||
// credType [0]
|
||||
s_reset(&tmp);
|
||||
out_uint8(&tmp, 1); // TSPasswordCreds
|
||||
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);
|
||||
out_uint8p(&message, h1->data, s_length(h1));
|
||||
s_free(h2);
|
||||
s_free(h1);
|
||||
|
||||
// credentials [1]
|
||||
h3 = cssp_encode_tspasswordcreds(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);
|
||||
out_uint8p(&message, h1->data, s_length(h1));
|
||||
s_free(h3);
|
||||
s_free(h2);
|
||||
s_free(h1);
|
||||
|
||||
s_mark_end(&message);
|
||||
|
||||
// Construct ASN.1 message
|
||||
out = ber_wrap_hdr_data(BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED, &message);
|
||||
|
||||
#if WITH_DEBUG_CREDSSP
|
||||
streamsave(out, "tscredentials.raw");
|
||||
printf("Out TSCredentials %ld bytes\n", s_length(out));
|
||||
hexdump(out->data, s_length(out));
|
||||
#endif
|
||||
|
||||
// cleanup
|
||||
free(message.data);
|
||||
free(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 };
|
||||
|
||||
// allocate local streams
|
||||
tmp.size = 4096;
|
||||
tmp.data = xmalloc(tmp.size);
|
||||
s_reset(&tmp);
|
||||
|
||||
message.size = 4096;
|
||||
message.data = xmalloc(message.size);
|
||||
s_reset(&message);
|
||||
|
||||
// version [0]
|
||||
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);
|
||||
out_uint8p(&message, h1->data, s_length(h1));
|
||||
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);
|
||||
|
||||
out_uint8p(&message, h1->data, s_length(h1));
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
out_uint8p(&message, h1->data, s_length(h1));
|
||||
|
||||
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);
|
||||
|
||||
#if WITH_DEBUG_CREDSSP
|
||||
streamsave(s, "tsrequest_out.raw");
|
||||
printf("Out TSRequest %ld bytes\n", s_length(s));
|
||||
hexdump(s->data, s_length(s));
|
||||
#endif
|
||||
|
||||
tcp_send(s);
|
||||
|
||||
// cleanup
|
||||
free(message.data);
|
||||
free(tmp.data);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
RD_BOOL
|
||||
cssp_read_tsrequest(STREAM token, STREAM pubkey)
|
||||
{
|
||||
STREAM s;
|
||||
int length;
|
||||
int tagval;
|
||||
|
||||
s = tcp_recv(NULL, 4);
|
||||
|
||||
if (s == NULL)
|
||||
return False;
|
||||
|
||||
// verify ASN.1 header
|
||||
if (s->p[0] != (BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED))
|
||||
{
|
||||
error("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);
|
||||
|
||||
#if WITH_DEBUG_CREDSSP
|
||||
streamsave(s, "tsrequest_in.raw");
|
||||
printf("In TSRequest token %ld bytes\n", s_length(s));
|
||||
hexdump(s->data, s_length(s));
|
||||
#endif
|
||||
|
||||
// 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;
|
||||
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;
|
||||
|
||||
token->end = token->p = token->data;
|
||||
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)
|
||||
{
|
||||
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))
|
||||
{
|
||||
warning("CredSSP: System doesn't have support for desired authentication mechanism.\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
// Get service name
|
||||
if (!cssp_gss_get_service_name(server, &target_name))
|
||||
{
|
||||
warning("CredSSP: Failed to get target service name.\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
// Establish tls connection to server
|
||||
if (!tcp_tls_connect())
|
||||
{
|
||||
warning("CredSSP: Failed to establish TLS connection.\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
tcp_tls_get_server_pubkey(&pubkey);
|
||||
|
||||
#ifdef WITH_DEBUG_CREDSSP
|
||||
streamsave(&pubkey, "PubKey.raw");
|
||||
#endif
|
||||
|
||||
|
||||
// TODO: Could this be prettier ??
|
||||
token.data = token.p = xmalloc(4096);
|
||||
token.size = 4096;
|
||||
token.end = token.data + token.size;
|
||||
|
||||
// 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)
|
||||
error("CredSSP: Initialize failed, do you have correct kerberos tgt initialized ?\n");
|
||||
else
|
||||
error("CredSSP: Negotiation failed.\n");
|
||||
|
||||
#ifdef WITH_DEBUG_CREDSSP
|
||||
cssp_gss_report_error(GSS_C_GSS_CODE, "CredSSP: SPNEGO negotiation failed.",
|
||||
major_status, minor_status);
|
||||
#endif
|
||||
return False;
|
||||
}
|
||||
|
||||
// validate required services
|
||||
if (!(actual_services & GSS_C_CONF_FLAG))
|
||||
{
|
||||
error("CredSSP: Confidiality service required but is not available.\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
// Send token to server
|
||||
if (output_tok.length != 0)
|
||||
{
|
||||
out_uint8p(&token, output_tok.value, output_tok.length);
|
||||
s_mark_end(&token);
|
||||
|
||||
if (!cssp_send_tsrequest(&token, NULL, NULL))
|
||||
return False;
|
||||
|
||||
(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))
|
||||
return False;
|
||||
|
||||
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))
|
||||
return False;
|
||||
|
||||
if (!cssp_send_tsrequest(NULL, NULL, &blob))
|
||||
return False;
|
||||
|
||||
context_established = 1;
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
}
|
||||
while (!context_established);
|
||||
|
||||
// read tsrequest response and decrypt for public key validation
|
||||
if (!cssp_read_tsrequest(NULL, &blob))
|
||||
return False;
|
||||
|
||||
if (!cssp_gss_unwrap(gss_ctx, &blob, &pubkey_cmp))
|
||||
return False;
|
||||
|
||||
pubkey_cmp.data[0] -= 1;
|
||||
|
||||
// validate public key
|
||||
if (memcmp(pubkey.data, pubkey_cmp.data, s_length(&pubkey)) != 0)
|
||||
{
|
||||
error("CredSSP: Cannot guarantee integrity of server connection, MITM ? "
|
||||
"(public key data mismatch)\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
// Send TSCredentials
|
||||
ts_creds = cssp_encode_tscredentials(user, password, domain);
|
||||
|
||||
if (!cssp_gss_wrap(gss_ctx, ts_creds, &blob))
|
||||
return False;
|
||||
|
||||
s_free(ts_creds);
|
||||
|
||||
if (!cssp_send_tsrequest(NULL, &blob, NULL))
|
||||
return False;
|
||||
|
||||
return True;
|
||||
}
|
20
iso.c
20
iso.c
@ -3,6 +3,7 @@
|
||||
Protocol services - ISO layer
|
||||
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
|
||||
Copyright 2005-2011 Peter Astrand <astrand@cendio.se> for Cendio AB
|
||||
Copyright 2012 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
|
||||
@ -80,7 +81,11 @@ iso_send_connection_request(char *username)
|
||||
out_uint8(s, RDP_NEG_REQ);
|
||||
out_uint8(s, 0);
|
||||
out_uint16(s, 8);
|
||||
#ifdef WITH_CREDSSP
|
||||
out_uint32(s, PROTOCOL_SSL | PROTOCOL_HYBRID);
|
||||
#else
|
||||
out_uint32(s, PROTOCOL_SSL);
|
||||
#endif
|
||||
}
|
||||
|
||||
s_mark_end(s);
|
||||
@ -191,7 +196,8 @@ iso_recv(uint8 * rdpver)
|
||||
|
||||
/* Establish a connection up to the ISO layer */
|
||||
RD_BOOL
|
||||
iso_connect(char *server, char *username, RD_BOOL reconnect, uint32 * selected_protocol)
|
||||
iso_connect(char *server, char *username, char *domain, char *password,
|
||||
RD_BOOL reconnect, uint32 * selected_protocol)
|
||||
{
|
||||
STREAM s;
|
||||
uint8 code;
|
||||
@ -292,10 +298,22 @@ iso_connect(char *server, char *username, RD_BOOL reconnect, uint32 * selected_p
|
||||
tcp_disconnect();
|
||||
return False;
|
||||
}
|
||||
/* do not use encryption when using TLS */
|
||||
g_encryption = False;
|
||||
}
|
||||
#ifdef WITH_CREDSSP
|
||||
else if (data == PROTOCOL_HYBRID)
|
||||
{
|
||||
if (!cssp_connect(server, username, domain, password, s))
|
||||
{
|
||||
tcp_disconnect();
|
||||
return False;
|
||||
}
|
||||
|
||||
/* do not use encryption when using TLS */
|
||||
g_encryption = False;
|
||||
}
|
||||
#endif
|
||||
else if (data != PROTOCOL_RDP)
|
||||
{
|
||||
tcp_disconnect();
|
||||
|
70
mcs.c
70
mcs.c
@ -24,71 +24,6 @@ uint16 g_mcs_userid;
|
||||
extern VCHANNEL g_channels[];
|
||||
extern unsigned int g_num_channels;
|
||||
|
||||
/* Parse an ASN.1 BER header */
|
||||
static RD_BOOL
|
||||
ber_parse_header(STREAM s, int tagval, int *length)
|
||||
{
|
||||
int tag, len;
|
||||
|
||||
if (tagval > 0xff)
|
||||
{
|
||||
in_uint16_be(s, tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
in_uint8(s, tag);
|
||||
}
|
||||
|
||||
if (tag != tagval)
|
||||
{
|
||||
error("expected tag %d, got %d\n", tagval, tag);
|
||||
return False;
|
||||
}
|
||||
|
||||
in_uint8(s, len);
|
||||
|
||||
if (len & 0x80)
|
||||
{
|
||||
len &= ~0x80;
|
||||
*length = 0;
|
||||
while (len--)
|
||||
next_be(s, *length);
|
||||
}
|
||||
else
|
||||
*length = len;
|
||||
|
||||
return s_check(s);
|
||||
}
|
||||
|
||||
/* Output an ASN.1 BER header */
|
||||
static void
|
||||
ber_out_header(STREAM s, int tagval, int length)
|
||||
{
|
||||
if (tagval > 0xff)
|
||||
{
|
||||
out_uint16_be(s, tagval);
|
||||
}
|
||||
else
|
||||
{
|
||||
out_uint8(s, tagval);
|
||||
}
|
||||
|
||||
if (length >= 0x80)
|
||||
{
|
||||
out_uint8(s, 0x82);
|
||||
out_uint16_be(s, length);
|
||||
}
|
||||
else
|
||||
out_uint8(s, length);
|
||||
}
|
||||
|
||||
/* Output an ASN.1 BER integer */
|
||||
static void
|
||||
ber_out_integer(STREAM s, int value)
|
||||
{
|
||||
ber_out_header(s, BER_TAG_INTEGER, 2);
|
||||
out_uint16_be(s, value);
|
||||
}
|
||||
|
||||
/* Output a DOMAIN_PARAMS structure (ASN.1 BER) */
|
||||
static void
|
||||
@ -373,9 +308,10 @@ mcs_recv(uint16 * channel, uint8 * rdpver)
|
||||
}
|
||||
|
||||
RD_BOOL
|
||||
mcs_connect_start(char *server, char *username, RD_BOOL reconnect, uint32 * selected_protocol)
|
||||
mcs_connect_start(char *server, char *username, char *domain, char *password,
|
||||
RD_BOOL reconnect, uint32 * selected_protocol)
|
||||
{
|
||||
return iso_connect(server, username, reconnect, selected_protocol);
|
||||
return iso_connect(server, username, domain, password, reconnect, selected_protocol);
|
||||
}
|
||||
|
||||
RD_BOOL
|
||||
|
18
proto.h
18
proto.h
@ -74,7 +74,8 @@ void ewmh_init(void);
|
||||
STREAM iso_init(int length);
|
||||
void iso_send(STREAM s);
|
||||
STREAM iso_recv(uint8 * rdpver);
|
||||
RD_BOOL iso_connect(char *server, char *username, RD_BOOL reconnect, uint32 * selected_protocol);
|
||||
RD_BOOL iso_connect(char *server, char *username, char *domain, char *password, RD_BOOL reconnect,
|
||||
uint32 * selected_protocol);
|
||||
void iso_disconnect(void);
|
||||
void iso_reset_state(void);
|
||||
/* licence.c */
|
||||
@ -84,8 +85,8 @@ STREAM mcs_init(int length);
|
||||
void mcs_send_to_channel(STREAM s, uint16 channel);
|
||||
void mcs_send(STREAM s);
|
||||
STREAM mcs_recv(uint16 * channel, uint8 * rdpver);
|
||||
RD_BOOL mcs_connect_start(char *server, char *username, RD_BOOL reconnect,
|
||||
uint32 * selected_protocol);
|
||||
RD_BOOL mcs_connect_start(char *server, char *username, char *domain, char *password,
|
||||
RD_BOOL reconnect, uint32 * selected_protocol);
|
||||
RD_BOOL mcs_connect_finalize(STREAM s);
|
||||
void mcs_disconnect(void);
|
||||
void mcs_reset_state(void);
|
||||
@ -191,7 +192,7 @@ void sec_send_to_channel(STREAM s, uint32 flags, uint16 channel);
|
||||
void sec_send(STREAM s, uint32 flags);
|
||||
void sec_process_mcs_data(STREAM s);
|
||||
STREAM sec_recv(uint8 * rdpver);
|
||||
RD_BOOL sec_connect(char *server, char *username, RD_BOOL reconnect);
|
||||
RD_BOOL sec_connect(char *server, char *username, char *domain, char *password, RD_BOOL reconnect);
|
||||
void sec_disconnect(void);
|
||||
void sec_reset_state(void);
|
||||
/* serial.c */
|
||||
@ -207,6 +208,15 @@ RD_BOOL tcp_connect(char *server);
|
||||
void tcp_disconnect(void);
|
||||
char *tcp_get_address(void);
|
||||
void tcp_reset_state(void);
|
||||
RD_BOOL tcp_tls_connect(void);
|
||||
RD_BOOL tcp_tls_get_server_pubkey(STREAM s);
|
||||
|
||||
/* asn.c */
|
||||
RD_BOOL ber_in_header(STREAM s, int *tagval, int *length);
|
||||
void ber_out_header(STREAM s, int tagval, int length);
|
||||
RD_BOOL ber_parse_header(STREAM s, int tagval, int *length);
|
||||
void ber_out_integer(STREAM s, int value);
|
||||
|
||||
/* xclip.c */
|
||||
void ui_clip_format_announce(uint8 * data, uint32 length);
|
||||
void ui_clip_handle_data(uint8 * data, uint32 length);
|
||||
|
2
rdp.c
2
rdp.c
@ -1636,7 +1636,7 @@ rdp_connect(char *server, uint32 flags, char *domain, char *password,
|
||||
RD_BOOL deactivated = False;
|
||||
uint32 ext_disc_reason = 0;
|
||||
|
||||
if (!sec_connect(server, g_username, reconnect))
|
||||
if (!sec_connect(server, g_username, domain, password, reconnect))
|
||||
return False;
|
||||
|
||||
rdp_send_logon_info(flags, domain, g_username, password, command, directory);
|
||||
|
6
secure.c
6
secure.c
@ -881,13 +881,13 @@ sec_recv(uint8 * rdpver)
|
||||
|
||||
/* Establish a secure connection */
|
||||
RD_BOOL
|
||||
sec_connect(char *server, char *username, RD_BOOL reconnect)
|
||||
sec_connect(char *server, char *username, char *domain, char *password, RD_BOOL reconnect)
|
||||
{
|
||||
uint32 selected_proto;
|
||||
struct stream mcs_data;
|
||||
|
||||
/* Start a MCS connect sequence */
|
||||
if (!mcs_connect_start(server, username, reconnect, &selected_proto))
|
||||
if (!mcs_connect_start(server, username, domain, password, reconnect, &selected_proto))
|
||||
return False;
|
||||
|
||||
/* We exchange some RDP data during the MCS-Connect */
|
||||
@ -899,7 +899,7 @@ sec_connect(char *server, char *username, RD_BOOL reconnect)
|
||||
if (!mcs_connect_finalize(&mcs_data))
|
||||
return False;
|
||||
|
||||
/* sec_process_mcs_data(&mcs_data); */
|
||||
/* sec_process_mcs_data(&mcs_data); */
|
||||
if (g_encryption)
|
||||
sec_establish_key();
|
||||
xfree(mcs_data.data);
|
||||
|
Loading…
Reference in New Issue
Block a user