~ubuntu-branches/ubuntu/raring/freerdp/raring

« back to all changes in this revision

Viewing changes to libfreerdp/iso.c

  • Committer: Bazaar Package Importer
  • Author(s): Otavio Salvador
  • Date: 2010-06-23 21:39:09 UTC
  • Revision ID: james.westby@ubuntu.com-20100623213909-bb9pvvv03913tdv6
Tags: upstream-0.7.1
ImportĀ upstreamĀ versionĀ 0.7.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- c-basic-offset: 8 -*-
 
2
   rdesktop: A Remote Desktop Protocol client.
 
3
   Protocol services - ISO layer
 
4
   Copyright (C) Matthew Chapman 1999-2008
 
5
 
 
6
   This program is free software; you can redistribute it and/or modify
 
7
   it under the terms of the GNU General Public License as published by
 
8
   the Free Software Foundation; either version 2 of the License, or
 
9
   (at your option) any later version.
 
10
 
 
11
   This program is distributed in the hope that it will be useful,
 
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
   GNU General Public License for more details.
 
15
 
 
16
   You should have received a copy of the GNU General Public License
 
17
   along with this program; if not, write to the Free Software
 
18
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
19
 */
 
20
 
 
21
#include "tcp.h"
 
22
#include "iso.h"
 
23
#include "mcs.h"
 
24
#include "secure.h"
 
25
#include "credssp.h"
 
26
#include "rdp.h"
 
27
#include "mem.h"
 
28
 
 
29
/* TPKT from T123 - aka ISO DP 8073 */
 
30
 
 
31
/* Output TPKT header for length length.
 
32
 * Length should include the TPKT header (4 bytes) */
 
33
static void
 
34
tpkt_output_header(STREAM s, int length)
 
35
{
 
36
        out_uint8(s, 3);        /* version */
 
37
        out_uint8(s, 0);        /* reserved */
 
38
        out_uint16_be(s, length);       /* length */
 
39
}
 
40
 
 
41
/* Try to read TPKT header for X.224 from stream and return length
 
42
 * (including the 4 bytes TPKT header already read).
 
43
 * If not possible then return untouched stream and length -1. */
 
44
static int
 
45
tpkt_input_header(STREAM s)
 
46
{
 
47
        if (*s->p == 3) /* Peeking is less ugly than rewinding */
 
48
        {
 
49
                uint8 version;
 
50
                uint8 reserved;
 
51
                uint16 length;
 
52
 
 
53
                in_uint8(s, version);
 
54
                in_uint8(s, reserved);
 
55
                in_uint16_be(s, length);
 
56
                return length;
 
57
        }
 
58
        return -1;      /* Probably Fast-Path */
 
59
}
 
60
 
 
61
/* Output and send 7 bytes X.224 headers for
 
62
 * Client X.224 Connection Request TPDU (X224_TPDU_CONNECTION_REQUEST)
 
63
 * Server X.224 Connection Confirm TPDU
 
64
 * FIXME: is this also suitable for X224_TPDU_DISCONNECT_REQUEST ??? */
 
65
static void
 
66
x224_send_dst_src_class(rdpIso * iso, uint8 code)
 
67
{
 
68
        STREAM s;
 
69
 
 
70
        s = tcp_init(iso->tcp, 11);
 
71
 
 
72
        tpkt_output_header(s, 11);
 
73
 
 
74
        out_uint8(s, 6);        /* length indicator */
 
75
        out_uint8(s, code);
 
76
        out_uint16_le(s, 0);    /* dst_ref */
 
77
        out_uint16_le(s, 0);    /* src_ref */
 
78
        out_uint8(s, 0);        /* class */
 
79
 
 
80
        s_mark_end(s);
 
81
        tcp_send(iso->tcp, s);
 
82
}
 
83
 
 
84
/* Output and send X.224 Connection Request TPDU with routing for username */
 
85
static void
 
86
x224_send_connection_request(rdpIso * iso, char *username)
 
87
{
 
88
        STREAM s;
 
89
        int length = 30 + strlen(username);
 
90
 
 
91
        if (iso->mcs->sec->tls)
 
92
                length += 8;
 
93
 
 
94
        /*
 
95
         * negotiation_state is used so that tcp_recv() will be able to make
 
96
         * the distinction between an unexpected disconnection and a
 
97
         * disconnection that is to be expected if negotation fails, such as
 
98
         * when the client claims to support TLS with NLA but the server only
 
99
         * supports the legacy encryption.
 
100
         */
 
101
 
 
102
        /* FIXME: Use x224_send_dst_src_class */
 
103
        s = tcp_init(iso->tcp, length);
 
104
 
 
105
        tpkt_output_header(s, length);
 
106
 
 
107
        /* X.224 Connection Request (CR) TPDU */
 
108
        out_uint8(s, length - 5);       /* length indicator */
 
109
        out_uint8(s, X224_TPDU_CONNECTION_REQUEST);
 
110
        out_uint16_le(s, 0);    /* dst_ref */
 
111
        out_uint16_le(s, 0);    /* src_ref */
 
112
        out_uint8(s, 0);        /* class */
 
113
 
 
114
        /* cookie */
 
115
        out_uint8p(s, "Cookie: mstshash=", strlen("Cookie: mstshash="));
 
116
        out_uint8p(s, username, strlen(username));
 
117
 
 
118
        /* routingToken */
 
119
        out_uint8(s, 0x0D);     /* CR */
 
120
        out_uint8(s, 0x0A);     /* LF */
 
121
 
 
122
        if (iso->mcs->sec->tls)
 
123
        {
 
124
                /* When using TLS + NLA, the RDP_NEG_DATA field should be present */
 
125
                out_uint8(s, TYPE_RDP_NEG_REQ);
 
126
                out_uint8(s, 0x00);     /* flags, must be set to zero */
 
127
                out_uint16_le(s, 8);    /* RDP_NEG_DATA length (8) */
 
128
                out_uint32_le(s, PROTOCOL_HYBRID | PROTOCOL_SSL);       /* requestedProtocols */
 
129
        }
 
130
 
 
131
        s_mark_end(s);
 
132
        tcp_send(iso->tcp, s);
 
133
}
 
134
 
 
135
/* Process Negotiation Response from Connection Confirm payload
 
136
 * Return selected protocol */
 
137
static uint32 /* or enum RDP_NEG_PROTOCOLS */
 
138
rdp_process_negotiation_response(rdpIso * iso, STREAM s)
 
139
{
 
140
        uint8 flags;
 
141
        uint16 length;
 
142
        uint32 selectedProtocol;
 
143
 
 
144
        in_uint8(s, flags);
 
145
        in_uint16_le(s, length);
 
146
        in_uint32_le(s, selectedProtocol);
 
147
 
 
148
        if (iso->mcs->sec->tls)
 
149
        {
 
150
                switch (selectedProtocol)
 
151
                {
 
152
                        case PROTOCOL_RDP:
 
153
                                printf("Selected PROTOCOL_RDP Security\n");
 
154
                                break;
 
155
                        case PROTOCOL_SSL:
 
156
                                printf("Selected PROTOCOL_SSL Security\n");
 
157
                                break;
 
158
                        case PROTOCOL_HYBRID:
 
159
                                printf("Selected PROTOCOL_HYBRID Security\n");
 
160
                                break;
 
161
                        default:
 
162
                                printf("Error: Unknown protocol security\n");
 
163
                                break;
 
164
                }
 
165
        }
 
166
 
 
167
        return selectedProtocol;
 
168
}
 
169
 
 
170
/* Process Negotiation Failure from Connection Confirm payload */
 
171
static void
 
172
rdp_process_negotiation_failure(rdpIso * iso, STREAM s)
 
173
{
 
174
        uint8 flags;
 
175
        uint16 length;
 
176
        uint32 failureCode;
 
177
 
 
178
        in_uint8(s, flags);
 
179
        in_uint16_le(s, length);
 
180
        in_uint32_le(s, failureCode);
 
181
 
 
182
        if (iso->mcs->sec->tls)
 
183
        {
 
184
                switch (failureCode)
 
185
                {
 
186
                        case SSL_REQUIRED_BY_SERVER:
 
187
                                printf("Error: SSL_REQUIRED_BY_SERVER\n");
 
188
                                break;
 
189
                        case SSL_NOT_ALLOWED_BY_SERVER:
 
190
                                printf("Error: SSL_NOT_ALLOWED_BY_SERVER\n");
 
191
                                break;
 
192
                        case SSL_CERT_NOT_ON_SERVER:
 
193
                                printf("Error: SSL_CERT_NOT_ON_SERVER\n");
 
194
                                break;
 
195
                        case INCONSISTENT_FLAGS:
 
196
                                printf("Error: INCONSISTENT_FLAGS\n");
 
197
                                break;
 
198
                        case HYBRID_REQUIRED_BY_SERVER:
 
199
                                printf("Error: HYBRID_REQUIRED_BY_SERVER\n");
 
200
                                break;
 
201
                        default:
 
202
                                printf("Error: Unknown protocol security error %d\n", failureCode);
 
203
                                break;
 
204
                }
 
205
        }
 
206
}
 
207
 
 
208
/* Receive an X.224 TPDU */
 
209
static STREAM
 
210
x224_recv(rdpIso * iso, STREAM s, int length, uint8 * pcode)
 
211
{
 
212
        uint8 lengthIndicator;
 
213
        uint8 code;
 
214
        uint8 subcode;
 
215
        uint8 type;
 
216
 
 
217
        s = tcp_recv(iso->tcp, s, length - 4);
 
218
 
 
219
        if (s == NULL)
 
220
                return NULL;
 
221
 
 
222
        /* X.224 TPDU Header */
 
223
        in_uint8(s, lengthIndicator);
 
224
        in_uint8(s, code);
 
225
 
 
226
        subcode = code & 0x0F;  /* get the lower nibble */
 
227
        code &= 0xF0;   /* take out lower nibble */
 
228
 
 
229
        *pcode = code;
 
230
 
 
231
        if (code == X224_TPDU_DATA)
 
232
        {
 
233
                in_uint8s(s, 1);        /* EOT */
 
234
                return s;
 
235
        }
 
236
 
 
237
        /* dst-ref (2 bytes) */
 
238
        /* src-ref (2 bytes) */
 
239
        /* class option (1 byte) */
 
240
        in_uint8s(s, 5);
 
241
 
 
242
        switch (code)
 
243
        {
 
244
                /* Connection Request */
 
245
                case X224_TPDU_CONNECTION_REQUEST:
 
246
                        printf("X224_TPDU_CONNECTION_REQUEST\n");
 
247
                        break;
 
248
 
 
249
                /* Connection Confirm */
 
250
                case X224_TPDU_CONNECTION_CONFIRM:
 
251
                        printf("X224_TPDU_CONNECTION_CONFIRM\n");
 
252
                        break;
 
253
 
 
254
                /* Disconnect Request */
 
255
                case X224_TPDU_DISCONNECT_REQUEST:
 
256
                        printf("X224_TPDU_DISCONNECT_REQUEST\n");
 
257
                        break;
 
258
 
 
259
                /* Data */
 
260
                case X224_TPDU_DATA:
 
261
                        printf("X224_TPDU_DATA\n");
 
262
                        break;
 
263
 
 
264
                /* Error */
 
265
                case X224_TPDU_ERROR:
 
266
                        printf("X224_TPDU_ERROR\n");
 
267
                        break;
 
268
        }
 
269
 
 
270
        /* According to X.224 13.4 and [MS-RDPBCGR] 2.2.1.2, the rdpNegData field is optional
 
271
           and its length is included in the X.224 length indicator */
 
272
        if (lengthIndicator > 6)
 
273
        {
 
274
                in_uint8(s, type);      /* Type */
 
275
                switch (type)
 
276
                {
 
277
                        case TYPE_RDP_NEG_RSP:
 
278
                                printf("TYPE_RDP_NEG_RSP\n");
 
279
                                rdp_process_negotiation_response(iso, s);
 
280
                                break;
 
281
                        case TYPE_RDP_NEG_FAILURE:
 
282
                                printf("TYPE_RDP_NEG_FAILURE\n");
 
283
                                rdp_process_negotiation_failure(iso, s);
 
284
                                break;
 
285
                }
 
286
        }
 
287
 
 
288
        return s;
 
289
}
 
290
 
 
291
/* Receive a packet from tcp and return stream.
 
292
 * If no ptype then only TPKT header with X.224 is accepted.
 
293
 * If ptype then Fast-Path packets are accepted too.
 
294
 * Return NULL on error. */
 
295
static STREAM
 
296
tpkt_recv(rdpIso * iso, uint8 * pcode, isoRecvType * ptype)
 
297
{
 
298
        STREAM s;
 
299
        int length;
 
300
 
 
301
        s = tcp_recv(iso->tcp, NULL, 4);
 
302
 
 
303
        if (s == NULL)
 
304
                return NULL;
 
305
 
 
306
        length = tpkt_input_header(s);
 
307
 
 
308
        if (length >= 0)
 
309
        {
 
310
                /* Valid TPKT header, payload is X.224 TPDU */
 
311
                if (ptype != NULL)
 
312
                        *ptype = ISO_RECV_X224;
 
313
 
 
314
                return x224_recv(iso, s, length, pcode);
 
315
        }
 
316
        else if (ptype != NULL)
 
317
        {
 
318
                /* Fast-Path header */
 
319
                uint8 fpInputHeader;
 
320
 
 
321
                in_uint8(s, fpInputHeader);
 
322
                *ptype = (fpInputHeader & 0x80) ? ISO_RECV_FAST_PATH_ENCRYPTED : ISO_RECV_FAST_PATH;
 
323
 
 
324
                in_uint8(s, length);
 
325
                if (length & 0x80)
 
326
                {
 
327
                        length &= ~0x80;
 
328
                        next_be(s, length);
 
329
                }
 
330
                s = tcp_recv(iso->tcp, s, length - 4);
 
331
                return s;
 
332
        }
 
333
        return NULL;    /* Fast-Path not allowed */
 
334
}
 
335
 
 
336
static RD_BOOL
 
337
iso_negotiate_encryption(rdpIso * iso, char *username)
 
338
{
 
339
        uint8 code;
 
340
 
 
341
        if (iso->mcs->sec->tls == 0)
 
342
        {
 
343
                /* We do no use TLS + NLA, so we won't attempt to negotiate */
 
344
 
 
345
                iso->mcs->sec->negotiation_state = 2;
 
346
                x224_send_connection_request(iso, username);
 
347
 
 
348
                /* Receive negotiation response */
 
349
                if (tpkt_recv(iso, &code, NULL) == NULL)
 
350
                        return False;
 
351
        }
 
352
        else
 
353
        {
 
354
                /* first negotiation attempt */
 
355
                iso->mcs->sec->negotiation_state = 1;
 
356
 
 
357
                x224_send_connection_request(iso, username);
 
358
 
 
359
                /* Attempt to receive negotiation response */
 
360
                if (tpkt_recv(iso, &code, NULL) == NULL)
 
361
                {
 
362
                        if (iso->mcs->sec->negotiation_state == -1)
 
363
                        {
 
364
                                /* Negotiation failure, downgrade encryption and try again */
 
365
 
 
366
                                iso->mcs->sec->tls = 0;
 
367
 
 
368
                                /* second negotiation attempt */
 
369
                                iso->mcs->sec->negotiation_state = 2;
 
370
 
 
371
                                x224_send_connection_request(iso, username);
 
372
 
 
373
                                /* Receive negotiation response */
 
374
                                if (tpkt_recv(iso, &code, NULL) == NULL)
 
375
                                        return False;
 
376
                        }
 
377
                }
 
378
        }
 
379
 
 
380
        return True;
 
381
}
 
382
 
 
383
/* Receive a message on the ISO layer, return code */
 
384
static STREAM
 
385
iso_recv_msg(rdpIso * iso, uint8 * code, isoRecvType * ptype)
 
386
{
 
387
        return tpkt_recv(iso, code, ptype);
 
388
}
 
389
 
 
390
/* Initialise ISO transport data packet */
 
391
STREAM
 
392
iso_init(rdpIso * iso, int length)
 
393
{
 
394
        STREAM s;
 
395
 
 
396
        s = tcp_init(iso->tcp, length + 7);
 
397
        s_push_layer(s, iso_hdr, 7);
 
398
 
 
399
        return s;
 
400
}
 
401
 
 
402
/* Initialise fast path data packet */
 
403
STREAM
 
404
iso_fp_init(rdpIso * iso, int length)
 
405
{
 
406
        STREAM s;
 
407
 
 
408
        s = tcp_init(iso->tcp, length + 3);
 
409
        s_push_layer(s, iso_hdr, 3);
 
410
        return s;
 
411
}
 
412
 
 
413
/* Send an ISO data PDU */
 
414
void
 
415
iso_send(rdpIso * iso, STREAM s)
 
416
{
 
417
        uint16 length;
 
418
 
 
419
        s_pop_layer(s, iso_hdr);
 
420
        length = s->end - s->p;
 
421
 
 
422
        out_uint8(s, 3);        /* version */
 
423
        out_uint8(s, 0);        /* reserved */
 
424
        out_uint16_be(s, length);
 
425
 
 
426
        out_uint8(s, 2);        /* hdrlen */
 
427
        out_uint8(s, X224_TPDU_DATA);   /* code */
 
428
        out_uint8(s, 0x80);     /* eot */
 
429
 
 
430
        tcp_send(iso->tcp, s);
 
431
}
 
432
 
 
433
/* Send an fast path data PDU */
 
434
void
 
435
iso_fp_send(rdpIso * iso, STREAM s, uint32 flags)
 
436
{
 
437
        int fp_flags;
 
438
        int len;
 
439
        int index;
 
440
 
 
441
        fp_flags = (1 << 2) | 0;        /* one event, fast path */
 
442
        if (flags & SEC_ENCRYPT)
 
443
        {
 
444
                fp_flags |= 2 << 6;     /* FASTPATH_INPUT_ENCRYPTED */
 
445
        }
 
446
        s_pop_layer(s, iso_hdr);
 
447
        len = (int) (s->end - s->p);
 
448
        out_uint8(s, fp_flags);
 
449
        if (len >= 128)
 
450
        {
 
451
                out_uint16_be(s, len | 0x8000);
 
452
        }
 
453
        else
 
454
        {
 
455
                /* copy the bits up to pack and save 1 byte */
 
456
                for (index = 3; index < len; index++)
 
457
                {
 
458
                        s->data[index - 1] = s->data[index];
 
459
                }
 
460
                len--;
 
461
                s->end--;
 
462
                out_uint8(s, len);
 
463
        }
 
464
        tcp_send(iso->tcp, s);
 
465
}
 
466
 
 
467
/* Receive ISO transport data packet
 
468
 * If ptype is NULL then only X224 is accepted */
 
469
STREAM
 
470
iso_recv(rdpIso * iso, isoRecvType * ptype)
 
471
{
 
472
        STREAM s;
 
473
        uint8 code = 0;
 
474
 
 
475
        s = iso_recv_msg(iso, &code, ptype);
 
476
 
 
477
        if (s == NULL)
 
478
                return NULL;
 
479
 
 
480
        if ((ptype != NULL) &&
 
481
                (*ptype == ISO_RECV_X224) &&
 
482
                (code != X224_TPDU_DATA))
 
483
        {
 
484
                ui_error(iso->mcs->sec->rdp->inst, "expected X224_TPDU_DATA, got 0x%x\n", code);
 
485
                return NULL;
 
486
        }
 
487
 
 
488
        return s;
 
489
}
 
490
 
 
491
/* Establish a connection up to the ISO layer */
 
492
RD_BOOL
 
493
iso_connect(rdpIso * iso, char *server, char *username, int port)
 
494
{
 
495
        if (!tcp_connect(iso->tcp, server, port))
 
496
                return False;
 
497
 
 
498
        return iso_negotiate_encryption(iso, username);
 
499
}
 
500
 
 
501
/* Establish a reconnection up to the ISO layer */
 
502
RD_BOOL
 
503
iso_reconnect(rdpIso * iso, char *server, int port)
 
504
{
 
505
        uint8 code = 0;
 
506
 
 
507
        if (!tcp_connect(iso->tcp, server, port))
 
508
                return False;
 
509
 
 
510
        x224_send_dst_src_class(iso, X224_TPDU_CONNECTION_REQUEST);
 
511
 
 
512
        if (iso_recv_msg(iso, &code, NULL) == NULL)
 
513
                return False;
 
514
 
 
515
        if (code != X224_TPDU_CONNECTION_CONFIRM)
 
516
        {
 
517
                ui_error(iso->mcs->sec->rdp->inst,
 
518
                         "expected X224_TPDU_CONNECTION_CONFIRM, got 0x%x\n", code);
 
519
                tcp_disconnect(iso->tcp);
 
520
                return False;
 
521
        }
 
522
 
 
523
        return True;
 
524
}
 
525
 
 
526
/* Disconnect from the ISO layer */
 
527
void
 
528
iso_disconnect(rdpIso * iso)
 
529
{
 
530
        x224_send_dst_src_class(iso, X224_TPDU_DISCONNECT_REQUEST);
 
531
        tcp_disconnect(iso->tcp);
 
532
}
 
533
 
 
534
/* reset the state to support reconnecting */
 
535
void
 
536
iso_reset_state(rdpIso * iso)
 
537
{
 
538
        tcp_reset_state(iso->tcp);
 
539
}
 
540
 
 
541
rdpIso *
 
542
iso_new(struct rdp_mcs *mcs)
 
543
{
 
544
        rdpIso *self;
 
545
 
 
546
        self = (rdpIso *) xmalloc(sizeof(rdpIso));
 
547
 
 
548
        if (self != NULL)
 
549
        {
 
550
                memset(self, 0, sizeof(rdpIso));
 
551
                self->mcs = mcs;
 
552
                self->tcp = tcp_new(self);
 
553
        }
 
554
 
 
555
        return self;
 
556
}
 
557
 
 
558
void
 
559
iso_free(rdpIso * iso)
 
560
{
 
561
        if (iso != NULL)
 
562
        {
 
563
                tcp_free(iso->tcp);
 
564
                xfree(iso);
 
565
        }
 
566
}