From 1c2d315354fe1939adb9f292c8557eb25d113e07 Mon Sep 17 00:00:00 2001 From: Martin Fleisz Date: Fri, 18 Mar 2016 13:32:13 +0100 Subject: [PATCH] core: Add support for CredSSP version 3 --- libfreerdp/core/nla.c | 105 ++++++++++++++++++++++++++++++++++------------- libfreerdp/core/nla.h | 2 + libfreerdp/crypto/ber.c | 13 ++++++ winpr/include/winpr/nt.h | 8 ++++ 4 files changed, 99 insertions(+), 29 deletions(-) diff --git a/libfreerdp/core/nla.c b/libfreerdp/core/nla.c index 22c0138..471f1dc 100644 --- a/libfreerdp/core/nla.c +++ b/libfreerdp/core/nla.c @@ -5,6 +5,7 @@ * Copyright 2010-2012 Marc-Andre Moreau * Copyright 2015 Thincast Technologies GmbH * Copyright 2015 DI (FH) Martin Haimberger + * Copyright 2016 Martin Fleisz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,7 +55,8 @@ * version [0] INTEGER, * negoTokens [1] NegoData OPTIONAL, * authInfo [2] OCTET STRING OPTIONAL, - * pubKeyAuth [3] OCTET STRING OPTIONAL + * pubKeyAuth [3] OCTET STRING OPTIONAL, + * errorCode [4] INTEGER OPTIONAL * } * * NegoData ::= SEQUENCE OF NegoDataItem @@ -548,7 +550,10 @@ int nla_client_authenticate(rdpNla* nla) } if (nla_client_begin(nla) < 1) + { + Stream_Free(s, TRUE); return -1; + } while (nla->state < NLA_STATE_AUTH_INFO) { @@ -566,7 +571,13 @@ int nla_client_authenticate(rdpNla* nla) status = nla_recv_pdu(nla, s); if (status < 0) + { + if (nla->errorCode) + WLog_ERR(TAG, "SPNEGO failed with NTSTATUS: %08X", nla->errorCode); + + Stream_Free(s, TRUE); return -1; + } } Stream_Free(s, TRUE); @@ -780,8 +791,28 @@ int nla_server_authenticate(rdpNla* nla) if ((nla->status != SEC_E_OK) && (nla->status != SEC_I_CONTINUE_NEEDED)) { + /* Special handling of these specific error codes as NTSTATUS_FROM_WIN32 + unfortunately does not map directly to the corresponding NTSTATUS values + */ + switch (GetLastError()) + { + case ERROR_PASSWORD_MUST_CHANGE: + nla->errorCode = STATUS_PASSWORD_MUST_CHANGE; + break; + case ERROR_PASSWORD_EXPIRED: + nla->errorCode = STATUS_PASSWORD_EXPIRED; + break; + case ERROR_ACCOUNT_DISABLED: + nla->errorCode = STATUS_ACCOUNT_DISABLED; + break; + default: + nla->errorCode = NTSTATUS_FROM_WIN32(GetLastError()); + break; + } + WLog_ERR(TAG, "AcceptSecurityContext status %s [%08X]", GetSecurityStatusString(nla->status), nla->status); + nla_send(nla); return -1; /* Access Denied */ } @@ -1381,14 +1412,24 @@ BOOL nla_send(rdpNla* nla) wStream* s; int length; int ts_request_length; - int nego_tokens_length; - int pub_key_auth_length; - int auth_info_length; - - nego_tokens_length = (nla->negoToken.cbBuffer > 0) ? nla_sizeof_nego_tokens(nla->negoToken.cbBuffer) : 0; - pub_key_auth_length = (nla->pubKeyAuth.cbBuffer > 0) ? nla_sizeof_pub_key_auth(nla->pubKeyAuth.cbBuffer) : 0; - auth_info_length = (nla->authInfo.cbBuffer > 0) ? nla_sizeof_auth_info(nla->authInfo.cbBuffer) : 0; - length = nego_tokens_length + pub_key_auth_length + auth_info_length; + int nego_tokens_length = 0; + int pub_key_auth_length = 0; + int auth_info_length = 0; + int error_code_context_length = 0; + int error_code_length = 0; + + if (nla->version < 3 || nla->errorCode == 0) + { + nego_tokens_length = (nla->negoToken.cbBuffer > 0) ? nla_sizeof_nego_tokens(nla->negoToken.cbBuffer) : 0; + pub_key_auth_length = (nla->pubKeyAuth.cbBuffer > 0) ? nla_sizeof_pub_key_auth(nla->pubKeyAuth.cbBuffer) : 0; + auth_info_length = (nla->authInfo.cbBuffer > 0) ? nla_sizeof_auth_info(nla->authInfo.cbBuffer) : 0; + } + else + { + error_code_length = ber_sizeof_integer(nla->errorCode); + error_code_context_length = ber_sizeof_contextual_tag(error_code_length); + } + length = nego_tokens_length + pub_key_auth_length + auth_info_length + error_code_context_length + error_code_length; ts_request_length = nla_sizeof_ts_request(length); s = Stream_New(NULL, ber_sizeof_sequence(ts_request_length)); @@ -1404,7 +1445,7 @@ BOOL nla_send(rdpNla* nla) ber_write_sequence_tag(s, ts_request_length); /* SEQUENCE */ /* [0] version */ ber_write_contextual_tag(s, 0, 3, TRUE); - ber_write_integer(s, 2); /* INTEGER */ + ber_write_integer(s, nla->version); /* INTEGER */ /* [1] negoTokens (NegoData) */ if (nego_tokens_length > 0) @@ -1430,6 +1471,13 @@ BOOL nla_send(rdpNla* nla) length -= ber_write_sequence_octet_string(s, 3, nla->pubKeyAuth.pvBuffer, nla->pubKeyAuth.cbBuffer); } + /* [4] errorCode (INTEGER) */ + if (error_code_length > 0) + { + ber_write_contextual_tag(s, 4, error_code_length, TRUE); + ber_write_integer(s, nla->errorCode); + } + Stream_SealLength(s); transport_write(nla->transport, s); Stream_Free(s, TRUE); @@ -1439,14 +1487,12 @@ BOOL nla_send(rdpNla* nla) int nla_decode_ts_request(rdpNla* nla, wStream* s) { int length; - UINT32 version; /* TSRequest */ if (!ber_read_sequence_tag(s, &length) || !ber_read_contextual_tag(s, 0, &length, TRUE) || - !ber_read_integer(s, &version)) + !ber_read_integer(s, &nla->version)) { - Stream_Free(s, TRUE); return -1; } @@ -1459,15 +1505,12 @@ int nla_decode_ts_request(rdpNla* nla, wStream* s) !ber_read_octet_string_tag(s, &length) || /* OCTET STRING */ ((int) Stream_GetRemainingLength(s)) < length) { - Stream_Free(s, TRUE); return -1; } if (!sspi_SecBufferAlloc(&nla->negoToken, length)) - { - Stream_Free(s, TRUE); return -1; - } + Stream_Read(s, nla->negoToken.pvBuffer, length); nla->negoToken.cbBuffer = length; } @@ -1477,16 +1520,11 @@ int nla_decode_ts_request(rdpNla* nla, wStream* s) { if (!ber_read_octet_string_tag(s, &length) || /* OCTET STRING */ ((int) Stream_GetRemainingLength(s)) < length) - { - Stream_Free(s, TRUE); return -1; - } if (!sspi_SecBufferAlloc(&nla->authInfo, length)) - { - Stream_Free(s, TRUE); return -1; - } + Stream_Read(s, nla->authInfo.pvBuffer, length); nla->authInfo.cbBuffer = length; } @@ -1496,20 +1534,25 @@ int nla_decode_ts_request(rdpNla* nla, wStream* s) { if (!ber_read_octet_string_tag(s, &length) || /* OCTET STRING */ ((int) Stream_GetRemainingLength(s)) < length) - { - Stream_Free(s, TRUE); return -1; - } if (!sspi_SecBufferAlloc(&nla->pubKeyAuth, length)) - { - Stream_Free(s, TRUE); return -1; - } + Stream_Read(s, nla->pubKeyAuth.pvBuffer, length); nla->pubKeyAuth.cbBuffer = length; } + /* [4] errorCode (INTEGER) */ + if (nla->version >= 3) + { + if (ber_read_contextual_tag(s, 4, &length, TRUE) != FALSE) + { + if (!ber_read_integer(s, &nla->errorCode)) + return -1; + } + } + return 1; } @@ -1547,7 +1590,10 @@ int nla_recv(rdpNla* nla) } if (nla_decode_ts_request(nla, s) < 1) + { + Stream_Free(s, TRUE); return -1; + } Stream_Free(s, TRUE); return 1; @@ -1667,6 +1713,7 @@ rdpNla* nla_new(freerdp* instance, rdpTransport* transport, rdpSettings* setting nla->transport = transport; nla->sendSeqNum = 0; nla->recvSeqNum = 0; + nla->version = 3; ZeroMemory(&nla->negoToken, sizeof(SecBuffer)); ZeroMemory(&nla->pubKeyAuth, sizeof(SecBuffer)); diff --git a/libfreerdp/core/nla.h b/libfreerdp/core/nla.h index b1afeba..7489fc7 100644 --- a/libfreerdp/core/nla.h +++ b/libfreerdp/core/nla.h @@ -58,6 +58,8 @@ struct rdp_nla rdpSettings* settings; rdpTransport* transport; UINT32 cbMaxToken; + UINT32 version; + UINT32 errorCode; ULONG fContextReq; ULONG pfContextAttr; BOOL haveContext; diff --git a/libfreerdp/crypto/ber.c b/libfreerdp/crypto/ber.c index 9e54232..86b965f 100644 --- a/libfreerdp/crypto/ber.c +++ b/libfreerdp/crypto/ber.c @@ -451,6 +451,14 @@ int ber_write_integer(wStream* s, UINT32 value) Stream_Write_UINT32_BE(s, value); return 6; } + else + { + /* treat as signed integer i.e. NT/HRESULT error codes */ + ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE); + ber_write_length(s, 4); + Stream_Write_UINT32_BE(s, value); + return 6; + } return 0; } @@ -473,6 +481,11 @@ int ber_sizeof_integer(UINT32 value) { return 6; } + else + { + /* treat as signed integer i.e. NT/HRESULT error codes */ + return 6; + } return 0; } diff --git a/winpr/include/winpr/nt.h b/winpr/include/winpr/nt.h index 68bccc6..5224e1d 100644 --- a/winpr/include/winpr/nt.h +++ b/winpr/include/winpr/nt.h @@ -1270,6 +1270,14 @@ /* Defined in winternl.h, always define since we do not include this header */ +/* defined in ntstatus.h */ +#if !defined(NTSTATUS_FROM_WIN32) && !defined(INLINE_NTSTATUS_FROM_WIN32) +INLINE NTSTATUS NTSTATUS_FROM_WIN32(long x) +{ + return x <= 0 ? (NTSTATUS)x : (NTSTATUS) (((x) & 0x0000FFFF) | (0x7 << 16) | 0xC0000000); +} +#endif + #ifdef _WIN32 /** -- 2.7.4