From 8cced27d17ef4139e8c777fe06abafada3561699 Mon Sep 17 00:00:00 2001 From: Vic Lee Date: Thu, 18 Aug 2011 23:15:28 +0800 Subject: [PATCH] libfreerdp/peer: initial protocol negotiation. --- libfreerdp-core/connection.h | 6 ++ libfreerdp-core/nego.c | 108 +++++++++++++++++++++++++++++++++++ libfreerdp-core/nego.h | 5 ++ libfreerdp-core/peer.c | 87 ++++++++++++++++++++++++++-- libfreerdp-core/peer.h | 3 +- libfreerdp-core/tpdu.c | 34 +++++++++++ libfreerdp-core/tpdu.h | 2 + libfreerdp-core/transport.c | 5 ++ libfreerdp-core/transport.h | 1 + server/test/freerdp_server.c | 4 +- 10 files changed, 248 insertions(+), 7 deletions(-) diff --git a/libfreerdp-core/connection.h b/libfreerdp-core/connection.h index 6011bd0a3..10b72899d 100644 --- a/libfreerdp-core/connection.h +++ b/libfreerdp-core/connection.h @@ -31,6 +31,12 @@ #include #include +enum CONNECTION_STATE +{ + CONNECTION_STATE_INITIAL = 0, + CONNECTION_STATE_NEGO +}; + boolean rdp_client_connect(rdpRdp* rdp); #endif /* __CONNECTION_H */ diff --git a/libfreerdp-core/nego.c b/libfreerdp-core/nego.c index 8b6776345..0f48cff18 100644 --- a/libfreerdp-core/nego.c +++ b/libfreerdp-core/nego.c @@ -269,6 +269,59 @@ int nego_recv(rdpTransport* transport, STREAM* s, void* extra) return 0; } +/** + * Receive protocol security negotiation request message.\n + * @param nego + * @param s stream + */ + +boolean nego_recv_request(rdpNego* nego, STREAM* s) +{ + uint8 li; + uint8 c; + uint8 type; + + tpkt_read_header(s); + li = tpdu_read_connection_request(s); + if (li != stream_get_left(s) + 6) + { + printf("Incorrect TPDU length indicator.\n"); + return False; + } + + if (stream_get_left(s) > 8) + { + /* Optional routingToken or cookie, ending with CR+LF */ + while (stream_get_left(s) > 0) + { + stream_read_uint8(s, c); + if (c != '\x0D') + continue; + stream_peek_uint8(s, c); + if (c != '\x0A') + continue; + + break; + } + } + + if (stream_get_left(s) >= 8) + { + /* rdpNegData (optional) */ + + stream_read_uint8(s, type); /* Type */ + if (type != TYPE_RDP_NEG_REQ) + { + printf("Incorrect negotiation request type %d\n", type); + return False; + } + + nego_process_negotiation_request(nego, s); + } + + return True; +} + /** * Send protocol security negotiation message. * @param nego @@ -339,6 +392,26 @@ void nego_send_negotiation_request(rdpNego* nego) transport_write(nego->transport, s); } +/** + * Process Negotiation Request from Connection Request message. + * @param nego + * @param s + */ + +void nego_process_negotiation_request(rdpNego* nego, STREAM* s) +{ + uint8 flags; + uint16 length; + + DEBUG_NEGO("RDP_NEG_REQ"); + + stream_read_uint8(s, flags); + stream_read_uint16(s, length); + stream_read_uint32(s, nego->requested_protocols); + + nego->state = NEGO_STATE_FINAL; +} + /** * Process Negotiation Response from Connection Confirm message. * @param nego @@ -402,6 +475,41 @@ void nego_process_negotiation_failure(rdpNego* nego, STREAM* s) nego->state = NEGO_STATE_FAIL; } +/** + * Send RDP Negotiation Response (RDP_NEG_RSP).\n + * @param nego + */ + +void nego_send_negotiation_response(rdpNego* nego) +{ + STREAM* s; + int length; + uint8 *bm, *em; + + s = stream_new(64); + length = TPDU_CONNECTION_CONFIRM_LENGTH; + stream_get_mark(s, bm); + stream_seek(s, length); + + if (nego->selected_protocol > PROTOCOL_RDP) + { + /* RDP_NEG_DATA must be present for TLS and NLA */ + stream_write_uint8(s, TYPE_RDP_NEG_RSP); + stream_write_uint8(s, EXTENDED_CLIENT_DATA_SUPPORTED); /* flags */ + stream_write_uint16(s, 8); /* RDP_NEG_DATA length (8) */ + stream_write_uint32(s, nego->selected_protocol); /* selectedProtocol */ + length += 8; + } + + stream_get_mark(s, em); + stream_set_mark(s, bm); + tpkt_write_header(s, length); + tpdu_write_connection_confirm(s, length - 5); + stream_set_mark(s, em); + + transport_write(nego->transport, s); +} + /** * Initialize NEGO state machine. * @param nego diff --git a/libfreerdp-core/nego.h b/libfreerdp-core/nego.h index fcafbd0d0..9db30dd64 100644 --- a/libfreerdp-core/nego.h +++ b/libfreerdp-core/nego.h @@ -65,6 +65,8 @@ enum RDP_NEG_MSG TYPE_RDP_NEG_FAILURE = 0x3 }; +#define EXTENDED_CLIENT_DATA_SUPPORTED 0x01 + extern char NEGO_STATE_STRINGS[6][25]; extern char PROTOCOL_SECURITY_STRINGS[3][4]; @@ -92,10 +94,13 @@ void nego_attempt_rdp(rdpNego* nego); void nego_send(rdpNego* nego); int nego_recv(rdpTransport* transport, STREAM* s, void* extra); void nego_recv_response(rdpNego* nego); +boolean nego_recv_request(rdpNego* nego, STREAM* s); void nego_send_negotiation_request(rdpNego* nego); +void nego_process_negotiation_request(rdpNego* nego, STREAM* s); void nego_process_negotiation_response(rdpNego* nego, STREAM* s); void nego_process_negotiation_failure(rdpNego* nego, STREAM* s); +void nego_send_negotiation_response(rdpNego* nego); rdpNego* nego_new(struct rdp_transport * transport); void nego_free(rdpNego* nego); diff --git a/libfreerdp-core/peer.c b/libfreerdp-core/peer.c index 38decd9d1..d5624cdb1 100644 --- a/libfreerdp-core/peer.c +++ b/libfreerdp-core/peer.c @@ -21,6 +21,10 @@ static boolean freerdp_peer_initialize(freerdp_peer* client) { + rdpPeer* peer = (rdpPeer*)client->peer; + + peer->state = CONNECTION_STATE_INITIAL; + return True; } @@ -28,7 +32,7 @@ static boolean freerdp_peer_get_fds(freerdp_peer* client, void** rfds, int* rcou { rdpPeer* peer = (rdpPeer*)client->peer; - rfds[*rcount] = (void*)(long)(peer->sockfd); + rfds[*rcount] = (void*)(long)(peer->rdp->transport->tcp->sockfd); (*rcount)++; return True; @@ -36,9 +40,78 @@ static boolean freerdp_peer_get_fds(freerdp_peer* client, void** rfds, int* rcou static boolean freerdp_peer_check_fds(freerdp_peer* client) { + rdpPeer* peer = (rdpPeer*)client->peer; + rdpRdp* rdp; + int status; + + rdp = (rdpRdp*) peer->rdp; + + status = rdp_check_fds(rdp); + if (status < 0) + return False; + return True; } +static int peer_process_connection_nego(rdpPeer* peer, STREAM* s) +{ + if (!nego_recv_request(peer->rdp->nego, s)) + return -1; + if (peer->rdp->nego->requested_protocols == PROTOCOL_RDP) + { + printf("Standard RDP encryption is not supported.\n"); + return -1; + } + + printf("Requested protocols:"); + if ((peer->rdp->nego->requested_protocols | PROTOCOL_TLS)) + { + printf(" TLS"); + if (peer->rdp->settings->tls_security) + { + printf("(Y)"); + peer->rdp->nego->selected_protocol |= PROTOCOL_TLS; + } + else + printf("(n)"); + } + if ((peer->rdp->nego->requested_protocols | PROTOCOL_NLA)) + { + printf(" NLA"); + if (peer->rdp->settings->nla_security) + { + printf("(Y)"); + peer->rdp->nego->selected_protocol |= PROTOCOL_NLA; + } + else + printf("(n)"); + } + printf("\n"); + + nego_send_negotiation_response(peer->rdp->nego); + + peer->state = CONNECTION_STATE_NEGO; + + return 1; +} + +static int peer_recv_callback(rdpTransport* transport, STREAM* s, void* extra) +{ + rdpPeer* peer = (rdpPeer*)extra; + + switch (peer->state) + { + case CONNECTION_STATE_INITIAL: + return peer_process_connection_nego(peer, s); + + default: + printf("Invalid state %d\n", peer->state); + return -1; + } + + return 1; +} + static void freerdp_peer_disconnect(freerdp_peer* client) { } @@ -50,7 +123,6 @@ freerdp_peer* freerdp_peer_new(int sockfd) client = xnew(freerdp_peer); - client->settings = settings_new(); client->Initialize = freerdp_peer_initialize; client->GetFileDescriptor = freerdp_peer_get_fds; client->CheckFileDescriptor = freerdp_peer_check_fds; @@ -58,9 +130,16 @@ freerdp_peer* freerdp_peer_new(int sockfd) peer = xnew(rdpPeer); peer->client = client; - peer->sockfd = sockfd; + peer->rdp = rdp_new(NULL); client->peer = (void*)peer; + client->settings = peer->rdp->settings; + + transport_attach(peer->rdp->transport, sockfd); + + peer->rdp->transport->recv_callback = peer_recv_callback; + peer->rdp->transport->recv_extra = peer; + transport_set_blocking_mode(peer->rdp->transport, False); return client; } @@ -69,8 +148,8 @@ void freerdp_peer_free(freerdp_peer* client) { rdpPeer* peer = (rdpPeer*)client->peer; + rdp_free(peer->rdp); xfree(peer); - settings_free(client->settings); xfree(client); } diff --git a/libfreerdp-core/peer.h b/libfreerdp-core/peer.h index bb12ff78d..b18e97ce6 100644 --- a/libfreerdp-core/peer.h +++ b/libfreerdp-core/peer.h @@ -29,7 +29,8 @@ struct rdp_peer { freerdp_peer* client; - int sockfd; + rdpRdp* rdp; + int state; }; #endif /* __PEER */ diff --git a/libfreerdp-core/tpdu.c b/libfreerdp-core/tpdu.c index 630b61e0a..c1e0ffac3 100644 --- a/libfreerdp-core/tpdu.c +++ b/libfreerdp-core/tpdu.c @@ -111,6 +111,28 @@ tpdu_write_header(STREAM* s, uint16 length, uint8 code) } } +/** + * Read Connection Request TPDU + * @param s stream + * @return length indicator (LI) + */ + +uint8 tpdu_read_connection_request(STREAM* s) +{ + uint8 li; + uint8 code; + + li = tpdu_read_header(s, &code); + + if (code != X224_TPDU_CONNECTION_REQUEST) + { + printf("Error: expected X224_TPDU_CONNECTION_REQUEST\n"); + return 0; + } + + return li; +} + /** * Write Connection Request TPDU. * @param s stream @@ -146,6 +168,18 @@ tpdu_read_connection_confirm(STREAM* s) return li; } +/** + * Write Connection Confirm TPDU. + * @param s stream + * @param length TPDU length + */ + +void +tpdu_write_connection_confirm(STREAM* s, uint16 length) +{ + tpdu_write_header(s, length, X224_TPDU_CONNECTION_CONFIRM); +} + /** * Write Disconnect Request TPDU. * @param s stream diff --git a/libfreerdp-core/tpdu.h b/libfreerdp-core/tpdu.h index a240315c3..c85273c9d 100644 --- a/libfreerdp-core/tpdu.h +++ b/libfreerdp-core/tpdu.h @@ -43,8 +43,10 @@ enum X224_TPDU_TYPE uint8 tpdu_read_header(STREAM* s, uint8* code); void tpdu_write_header(STREAM* s, uint16 length, uint8 code); +uint8 tpdu_read_connection_request(STREAM* s); void tpdu_write_connection_request(STREAM* s, uint16 length); uint8 tpdu_read_connection_confirm(STREAM* s); +void tpdu_write_connection_confirm(STREAM* s, uint16 length); void tpdu_write_disconnect_request(STREAM* s, uint16 length); uint16 tpdu_read_data(STREAM* s); void tpdu_write_data(STREAM* s); diff --git a/libfreerdp-core/transport.c b/libfreerdp-core/transport.c index 3631a82d8..ccf3cf65a 100644 --- a/libfreerdp-core/transport.c +++ b/libfreerdp-core/transport.c @@ -63,6 +63,11 @@ boolean transport_connect(rdpTransport* transport, const char* hostname, uint16 return transport->tcp->connect(transport->tcp, hostname, port); } +void transport_attach(rdpTransport* transport, int sockfd) +{ + transport->tcp->sockfd = sockfd; +} + boolean transport_disconnect(rdpTransport* transport) { return transport->tcp->disconnect(transport->tcp); diff --git a/libfreerdp-core/transport.h b/libfreerdp-core/transport.h index 9b02426ce..65b851110 100644 --- a/libfreerdp-core/transport.h +++ b/libfreerdp-core/transport.h @@ -69,6 +69,7 @@ struct rdp_transport STREAM* transport_recv_stream_init(rdpTransport* transport, int size); STREAM* transport_send_stream_init(rdpTransport* transport, int size); boolean transport_connect(rdpTransport* transport, const char* hostname, uint16 port); +void transport_attach(rdpTransport* transport, int sockfd); boolean transport_disconnect(rdpTransport* transport); boolean transport_connect_rdp(rdpTransport* transport); boolean transport_connect_tls(rdpTransport* transport); diff --git a/server/test/freerdp_server.c b/server/test/freerdp_server.c index ffc585189..f302dfb1d 100644 --- a/server/test/freerdp_server.c +++ b/server/test/freerdp_server.c @@ -85,11 +85,11 @@ static void* test_peer_mainloop(void* arg) break; } + printf("Client %s disconnected.\n", client->settings->hostname); + client->Disconnect(client); freerdp_peer_free(client); - printf("Client %s disconnected.\n", client->settings->hostname); - return NULL; }