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

« back to all changes in this revision

Viewing changes to libfreerdp/tls.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
   FreeRDP: A Remote Desktop Protocol client.
 
3
   Transport Layer Security (TLS) encryption
 
4
 
 
5
   Copyright (C) Marc-Andre Moreau <marcandre.moreau@gmail.com> 2010
 
6
 
 
7
   This program is free software; you can redistribute it and/or modify
 
8
   it under the terms of the GNU General Public License as published by
 
9
   the Free Software Foundation; either version 2 of the License, or
 
10
   (at your option) any later version.
 
11
 
 
12
   This program is distributed in the hope that it will be useful,
 
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
   GNU General Public License for more details.
 
16
 
 
17
   You should have received a copy of the GNU General Public License
 
18
   along with this program; if not, write to the Free Software
 
19
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
20
*/
 
21
 
 
22
#include "frdp.h"
 
23
#include "ssl.h"
 
24
#include "secure.h"
 
25
#include "mcs.h"
 
26
#include "iso.h"
 
27
#include "tcp.h"
 
28
#include "mem.h"
 
29
 
 
30
#include <openssl/ssl.h>
 
31
#include <openssl/err.h>
 
32
#include <openssl/rc4.h>
 
33
#include <openssl/md5.h>
 
34
#include <openssl/sha.h>
 
35
#include <openssl/bn.h>
 
36
#include <openssl/x509v3.h>
 
37
 
 
38
#include "tls.h"
 
39
 
 
40
/* TODO: Implement SSL verify enforcement, disconnect when verify fails */
 
41
 
 
42
/* check the identity in a certificate against a hostname */
 
43
static RD_BOOL
 
44
tls_verify_peer_identity(X509 *cert, const char *peer)
 
45
{
 
46
        X509_NAME *subject_name = NULL;
 
47
        X509_NAME_ENTRY *entry = NULL;
 
48
        ASN1_STRING *asn1str = NULL;
 
49
        //GENERAL_NAMES *subjectAltNames = NULL;
 
50
        unsigned char *ustr = NULL;
 
51
        char *str = NULL;
 
52
        int i, len;
 
53
 
 
54
#if 0
 
55
        /* Check cert for subjectAltName extensions */
 
56
        /* things to check: ipv4/ipv6 address, hostname in normal form and in DC= form */
 
57
        i = -1;
 
58
        for (;;)
 
59
        {
 
60
                subjectAltNames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
 
61
                if (ext == NULL)
 
62
                        break;
 
63
        }n
 
64
 
 
65
        /* Check against ip address of server */
 
66
#endif
 
67
        /* Check commonName */
 
68
        subject_name = X509_get_subject_name(cert);
 
69
        if (!subject_name)
 
70
        {
 
71
                printf("ssl_verify_peer_identity: failed to get subject name\n");
 
72
                goto exit;
 
73
        }
 
74
 
 
75
        /* try to find a common name that matches the peer hostname */
 
76
        /* TODO: cn migth also be in DC=www,DC=redhat,DC=com format? */
 
77
        i = -1;
 
78
        for (;;)
 
79
        {
 
80
                entry = NULL;
 
81
                i = X509_NAME_get_index_by_NID(subject_name, NID_commonName, i);
 
82
                if (i == -1)
 
83
                        break;
 
84
                entry = X509_NAME_get_entry(subject_name, i);
 
85
                asn1str = X509_NAME_ENTRY_get_data(entry);
 
86
                len = ASN1_STRING_to_UTF8(&ustr, asn1str);
 
87
                str = (char *)ustr;
 
88
                if (strcmp(str, peer) == 0)
 
89
                        break;  /* found a match */
 
90
        }
 
91
 
 
92
        if (!entry)
 
93
        {
 
94
                printf("ssl_verify_peer_identity: certificate belongs to %s, but connection is to %s\n", str ? str : "unknown id", peer);
 
95
                return False;
 
96
        }
 
97
exit:
 
98
        return True;
 
99
}
 
100
 
 
101
/* verify SSL/TLS connection integrity. 2 checks are carried out. First make sure that the
 
102
 * certificate is assigned to the server we're connected to, and second make sure that the
 
103
 * certificate is signed by a trusted certification authority
 
104
 */
 
105
 
 
106
static RD_BOOL
 
107
tls_verify(SSL *connection, const char *server)
 
108
{
 
109
        /* TODO: Check for eku extension with server authentication purpose */
 
110
 
 
111
        RD_BOOL verified = False;
 
112
        X509 *server_cert = NULL;
 
113
        int ret;
 
114
 
 
115
        server_cert = SSL_get_peer_certificate(connection);
 
116
        if (!server_cert)
 
117
        {
 
118
                printf("ssl_verify: failed to get the server SSL certificate\n");
 
119
                goto exit;
 
120
        }
 
121
 
 
122
        ret = SSL_get_verify_result(connection);
 
123
        if (ret != X509_V_OK)
 
124
        {
 
125
                printf("ssl_verify: error %d (see 'man 1 verify' for more information)\n", ret);
 
126
                printf("certificate details:\n  Subject:\n");
 
127
                X509_NAME_print_ex_fp(stdout, X509_get_subject_name(server_cert), 4,
 
128
                                XN_FLAG_MULTILINE);
 
129
                printf("\n  Issued by:\n");
 
130
                X509_NAME_print_ex_fp(stdout, X509_get_issuer_name(server_cert), 4,
 
131
                                XN_FLAG_MULTILINE);
 
132
                printf("\n");
 
133
 
 
134
        }
 
135
        else
 
136
        {
 
137
                verified = tls_verify_peer_identity(server_cert, server);
 
138
        }
 
139
 
 
140
exit:
 
141
        if (!verified)
 
142
                printf("The server could not be authenticated. Connection security may be compromised!\n");
 
143
 
 
144
        if (server_cert)
 
145
        {
 
146
                X509_free(server_cert);
 
147
                server_cert = NULL;
 
148
        }
 
149
 
 
150
        return verified;
 
151
}
 
152
 
 
153
/* Handle an SSL error and returns True if the caller should abort (error was fatal) */
 
154
/* TODO: Use openssl error description functions */
 
155
static RD_BOOL
 
156
tls_printf(char *func, SSL *connection, int val)
 
157
{
 
158
        switch (SSL_get_error(connection, val))
 
159
        {
 
160
                case SSL_ERROR_ZERO_RETURN:
 
161
                        printf("%s: Server closed TLS connection\n", func);
 
162
                        return True;
 
163
                case SSL_ERROR_WANT_READ:
 
164
                        printf("SSL_ERROR_WANT_READ\n");
 
165
                        //if (!ui_select(SSL_get_fd(connection)))
 
166
                                /* User quit */
 
167
                                //return True;
 
168
                        return False;
 
169
                case SSL_ERROR_WANT_WRITE:
 
170
                        //tcp_can_send(SSL_get_fd(connection), 100);
 
171
                        printf("SSL_ERROR_WANT_WRITE\n");
 
172
                        return False;
 
173
                case SSL_ERROR_SYSCALL:
 
174
                        printf("%s: I/O error\n", func);
 
175
                        return True;
 
176
                case SSL_ERROR_SSL:
 
177
                        printf("%s: Failure in SSL library (protocol error?)\n", func);
 
178
                        return True;
 
179
                default:
 
180
                        printf("%s: Unknown error\n", func);
 
181
                        return True;
 
182
        }
 
183
}
 
184
 
 
185
/* Create TLS context */
 
186
SSL_CTX*
 
187
tls_create_context()
 
188
{
 
189
        SSL_CTX* ctx;
 
190
 
 
191
        SSL_load_error_strings();
 
192
        SSL_library_init();
 
193
 
 
194
        ctx = SSL_CTX_new(TLSv1_client_method());
 
195
 
 
196
        if (ctx == NULL)
 
197
        {
 
198
                printf("SSL_CTX_new failed\n");
 
199
                return NULL;
 
200
        }
 
201
 
 
202
        /*
 
203
         * This is necessary, because the Microsoft TLS implementation is not perfect.
 
204
         * SSL_OP_ALL enables a couple of workarounds for buggy TLS implementations,
 
205
         * but the most important workaround being SSL_OP_TLS_BLOCK_PADDING_BUG.
 
206
         * As the size of the encrypted payload may give hints about its contents,
 
207
         * block padding is normally used, but the Microsoft TLS implementation
 
208
         * won't recognize it and will disconnect you after sending a TLS alert.
 
209
         */
 
210
 
 
211
        SSL_CTX_set_options(ctx, SSL_OP_ALL);
 
212
 
 
213
        return ctx;
 
214
}
 
215
 
 
216
/* Initiate TLS handshake on socket */
 
217
SSL*
 
218
tls_connect(SSL_CTX *ctx, int sockfd, char *server)
 
219
{
 
220
        SSL *ssl;
 
221
        int connection_status;
 
222
 
 
223
        ssl = SSL_new(ctx);
 
224
 
 
225
        if (ssl == NULL)
 
226
        {
 
227
                printf("SSL_new failed\n");
 
228
                return NULL;
 
229
        }
 
230
 
 
231
        if (SSL_set_fd(ssl, sockfd) < 1)
 
232
        {
 
233
                printf("SSL_set_fd failed\n");
 
234
                return NULL;
 
235
        }
 
236
 
 
237
        do
 
238
        {
 
239
                /* SSL_WANT_READ errors are normal, just try again if it happens */
 
240
                connection_status = SSL_connect(ssl);
 
241
        }
 
242
        while (SSL_get_error(ssl, connection_status) == SSL_ERROR_WANT_READ);
 
243
 
 
244
        if (connection_status < 0)
 
245
        {
 
246
                if (tls_printf("SSL_connect", ssl, connection_status))
 
247
                        return NULL;
 
248
        }
 
249
 
 
250
        tls_verify(ssl, server);
 
251
 
 
252
        printf("TLS connection established\n");
 
253
 
 
254
        return ssl;
 
255
}
 
256
 
 
257
/* Free TLS resources */
 
258
void
 
259
tls_disconnect(SSL *ssl)
 
260
{
 
261
        int ret;
 
262
 
 
263
        if (!ssl)
 
264
                return;
 
265
 
 
266
        while (True)
 
267
        {
 
268
                ret = SSL_shutdown(ssl);
 
269
                if (ret > 0)
 
270
                        break;
 
271
                if (tls_printf("ssl_disconnect", ssl, ret))
 
272
                        break;
 
273
        }
 
274
        SSL_free(ssl);
 
275
        ssl = NULL;
 
276
}
 
277
 
 
278
/* Send data over TLS connection */
 
279
int tls_write(SSL *ssl, char* b, int size)
 
280
{
 
281
        int write_status;
 
282
        int bytesWritten = 0;
 
283
 
 
284
        write_status = SSL_write(ssl, b, size);
 
285
 
 
286
        switch (SSL_get_error(ssl, write_status))
 
287
        {
 
288
                case SSL_ERROR_NONE:
 
289
                        bytesWritten += write_status;
 
290
                        break;
 
291
 
 
292
                default:
 
293
                        tls_printf("SSL_write", ssl, write_status);
 
294
                        break;
 
295
        }
 
296
 
 
297
        if (bytesWritten < size)
 
298
                return bytesWritten += tls_write(ssl, &b[bytesWritten], size - bytesWritten);
 
299
        else
 
300
                return bytesWritten;
 
301
}
 
302
 
 
303
/* Receive data over TLS connection */
 
304
int tls_read(SSL *ssl, char* b, int size)
 
305
{
 
306
        int read_status;
 
307
        int bytesRead = 0;
 
308
 
 
309
        read_status = SSL_read(ssl, b, size);
 
310
 
 
311
        switch (SSL_get_error(ssl, read_status))
 
312
        {
 
313
                case SSL_ERROR_NONE:
 
314
                        bytesRead += read_status;
 
315
                        break;
 
316
 
 
317
                case SSL_ERROR_WANT_READ:
 
318
 
 
319
                        if (size - bytesRead < 128)
 
320
                        {
 
321
                                        /* the buffer is almost full, allocate more memory */
 
322
                                        size += 1024;
 
323
                                        xrealloc(b, size);
 
324
                        }
 
325
 
 
326
                        bytesRead += tls_read(ssl, &b[bytesRead], size - bytesRead);
 
327
                        break;
 
328
 
 
329
                default:
 
330
                        tls_printf("SSL_read", ssl, read_status);
 
331
                        break;
 
332
        }
 
333
 
 
334
        return bytesRead;
 
335
}