From 0dac2c555a0c0e2d0c0153b37524b16351cdd17e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 30 Jun 2011 18:48:48 -0400 Subject: [PATCH] libfreerdp-core: added protocol security negotiation --- libfreerdp-core/nego.c | 314 +++++++++++++++++++++++++++++++++++++++++ libfreerdp-core/nego.h | 67 +++++++++ 2 files changed, 381 insertions(+) create mode 100644 libfreerdp-core/nego.c create mode 100644 libfreerdp-core/nego.h diff --git a/libfreerdp-core/nego.c b/libfreerdp-core/nego.c new file mode 100644 index 000000000..454750466 --- /dev/null +++ b/libfreerdp-core/nego.c @@ -0,0 +1,314 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * RDP Protocol Security Negotiation + * + * Copyright 2011 Marc-Andre Moreau + * + * 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. + */ + +#include "frdp.h" +#include "rdp.h" +#include "tcp.h" +#include "stream.h" +#include + +#include "nego.h" + +/** + * Negotiate protocol security and connect. + * @param nego + * @return + */ + +int nego_connect(NEGO *nego) +{ + if (nego->state == NEGO_STATE_INITIAL) + { + if (nego->enabled_protocols[PROTOCOL_NLA] > 0) + nego->state = NEGO_STATE_NLA; + else if (nego->enabled_protocols[PROTOCOL_TLS] > 0) + nego->state = NEGO_STATE_TLS; + else if (nego->enabled_protocols[PROTOCOL_RDP] > 0) + nego->state = NEGO_STATE_RDP; + else + nego->state = NEGO_STATE_FAIL; + } + + while (nego->state != NEGO_STATE_FINAL) + { + nego_send(nego); + + if (nego->state == NEGO_STATE_FAIL) + { + nego->state = NEGO_STATE_FINAL; + return 0; + } + } + + return 1; +} + +/** + * Connect TCP layer. + * @param nego + * @return + */ + +int nego_tcp_connect(NEGO *nego) +{ + if (nego->tcp_connected == 0) + { + if (tcp_connect(nego->net->tcp, nego->hostname, nego->port) == False) + { + nego->tcp_connected = 0; + return 0; + } + else + { + nego->tcp_connected = 1; + return 1; + } + } + + return 1; +} + +/** + * Disconnect TCP layer. + * @param nego + * @return + */ + +int nego_tcp_disconnect(NEGO *nego) +{ + if (nego->tcp_connected) + tcp_disconnect(nego->net->tcp); + + nego->tcp_connected = 0; + return 1; +} + +/** + * Attempt negotiating NLA + TLS security. + * @param nego + */ + +void nego_attempt_nla(NEGO *nego) +{ + uint8 code; + nego->requested_protocols = PROTOCOL_NLA | PROTOCOL_TLS; + + nego_tcp_connect(nego); + x224_send_connection_request(nego->net->iso); + tpkt_recv(nego->net->iso, &code, NULL); + + if (nego->state != NEGO_STATE_FINAL) + { + nego_tcp_disconnect(nego); + + if (nego->enabled_protocols[PROTOCOL_TLS] > 0) + nego->state = NEGO_STATE_TLS; + else if (nego->enabled_protocols[PROTOCOL_RDP] > 0) + nego->state = NEGO_STATE_RDP; + else + nego->state = NEGO_STATE_FAIL; + } +} + +/** + * Attempt negotiating TLS security. + * @param nego + */ + +void nego_attempt_tls(NEGO *nego) +{ + uint8 code; + nego->requested_protocols = PROTOCOL_TLS; + + nego_tcp_connect(nego); + x224_send_connection_request(nego->net->iso); + tpkt_recv(nego->net->iso, &code, NULL); + + if (nego->state != NEGO_STATE_FINAL) + { + nego_tcp_disconnect(nego); + + if (nego->enabled_protocols[PROTOCOL_RDP] > 0) + nego->state = NEGO_STATE_RDP; + else + nego->state = NEGO_STATE_FAIL; + } +} + +/** + * Attempt negotiating standard RDP security. + * @param nego + */ + +void nego_attempt_rdp(NEGO *nego) +{ + uint8 code; + nego->requested_protocols = PROTOCOL_RDP; + + nego_tcp_connect(nego); + x224_send_connection_request(nego->net->iso); + + if (tpkt_recv(nego->net->iso, &code, NULL) == NULL) + nego->state = NEGO_STATE_FAIL; + else + nego->state = NEGO_STATE_FINAL; +} + +/** + * Receive protocol security negotiation message. + * @param nego + * @param s + */ + +void nego_recv(NEGO *nego, STREAM s) +{ + uint8 type; + in_uint8(s, type); /* Type */ + + switch (type) + { + case TYPE_RDP_NEG_RSP: + nego_process_negotiation_response(nego, s); + break; + case TYPE_RDP_NEG_FAILURE: + nego_process_negotiation_failure(nego, s); + break; + } +} + +/** + * Send protocol security negotiation message. + * @param nego + */ + +void nego_send(NEGO *nego) +{ + if (nego->state == NEGO_STATE_NLA) + nego_attempt_nla(nego); + else if (nego->state == NEGO_STATE_TLS) + nego_attempt_tls(nego); + else if (nego->state == NEGO_STATE_RDP) + nego_attempt_rdp(nego); +} + +/** + * Process Negotiation Response from Connection Confirm message. + * @param nego + * @param s + */ + +void nego_process_negotiation_response(NEGO *nego, STREAM s) +{ + uint8 flags; + uint16 length; + uint32 selectedProtocol; + + in_uint8(s, flags); + in_uint16_le(s, length); + in_uint32_le(s, selectedProtocol); + + if (selectedProtocol == PROTOCOL_NLA) + nego->selected_protocol = PROTOCOL_NLA; + else if (selectedProtocol == PROTOCOL_TLS) + nego->selected_protocol = PROTOCOL_TLS; + else if (selectedProtocol == PROTOCOL_RDP) + nego->selected_protocol = PROTOCOL_RDP; + + nego->state = NEGO_STATE_FINAL; +} + +/** + * Process Negotiation Failure from Connection Confirm message. + * @param nego + * @param s + */ + +void nego_process_negotiation_failure(NEGO *nego, STREAM s) +{ + uint8 flags; + uint16 length; + uint32 failureCode; + + in_uint8(s, flags); + in_uint16_le(s, length); + in_uint32_le(s, failureCode); + + switch (failureCode) + { + case SSL_REQUIRED_BY_SERVER: + //printf("Error: SSL_REQUIRED_BY_SERVER\n"); + break; + case SSL_NOT_ALLOWED_BY_SERVER: + //printf("Error: SSL_NOT_ALLOWED_BY_SERVER\n"); + break; + case SSL_CERT_NOT_ON_SERVER: + //printf("Error: SSL_CERT_NOT_ON_SERVER\n"); + break; + case INCONSISTENT_FLAGS: + //printf("Error: INCONSISTENT_FLAGS\n"); + break; + case HYBRID_REQUIRED_BY_SERVER: + //printf("Error: HYBRID_REQUIRED_BY_SERVER\n"); + break; + default: + printf("Error: Unknown protocol security error %d\n", failureCode); + break; + } +} + +/** + * Create a new NEGO state machine instance. + * @param iso + * @return + */ + +NEGO* nego_new(struct rdp_network * net) +{ + NEGO *nego = (NEGO*) xmalloc(sizeof(NEGO)); + + if (nego != NULL) + { + memset(nego, '\0', sizeof(NEGO)); + nego->net = net; + nego_init(nego); + } + + return nego; +} + +/** + * Initialize NEGO state machine. + * @param nego + */ + +void nego_init(NEGO *nego) +{ + nego->state = NEGO_STATE_INITIAL; + nego->requested_protocols = PROTOCOL_RDP; +} + +/** + * Free NEGO state machine. + * @param nego + */ + +void nego_free(NEGO *nego) +{ + xfree(nego); +} diff --git a/libfreerdp-core/nego.h b/libfreerdp-core/nego.h new file mode 100644 index 000000000..da1dd9a62 --- /dev/null +++ b/libfreerdp-core/nego.h @@ -0,0 +1,67 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * RDP Protocol Security Negotiation + * + * Copyright 2011 Marc-Andre Moreau + * + * 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 __NEGO_H +#define __NEGO_H + +#include "frdp.h" +#include "stream.h" +#include "network.h" + +enum _NEGO_STATE +{ + NEGO_STATE_INITIAL, + NEGO_STATE_NLA, /* Network Level Authentication (TLS implicit) */ + NEGO_STATE_TLS, /* TLS Encryption without NLA */ + NEGO_STATE_RDP, /* Standard Legacy RDP Encryption */ + NEGO_STATE_FAIL, /* Negotiation failure */ + NEGO_STATE_FINAL +}; +typedef enum _NEGO_STATE NEGO_STATE; + +struct _NEGO +{ + int port; + char *hostname; + NEGO_STATE state; + int tcp_connected; + struct rdp_network * net; + uint32 selected_protocol; + uint32 requested_protocols; + uint8 enabled_protocols[3]; +}; +typedef struct _NEGO NEGO; + +int nego_connect(NEGO *nego); + +void nego_attempt_nla(NEGO *nego); +void nego_attempt_tls(NEGO *nego); +void nego_attempt_rdp(NEGO *nego); + +void nego_send(NEGO *nego); +void nego_recv(NEGO *nego, STREAM s); + +void nego_process_negotiation_response(NEGO *nego, STREAM s); +void nego_process_negotiation_failure(NEGO *nego, STREAM s); + +NEGO* nego_new(struct rdp_network * net); +void nego_init(NEGO *nego); +void nego_free(NEGO *nego); + +#endif /* __NEGO_H */