Initial commit - from Precise source
[freerdp-ubuntu-pcb-backport.git] / libfreerdp-core / tls.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol Client
3  * Transport Layer Security
4  *
5  * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *               http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #include <freerdp/utils/stream.h>
21 #include <freerdp/utils/memory.h>
22
23 #include "tls.h"
24
25 boolean tls_connect(rdpTls* tls)
26 {
27         int connection_status;
28
29         tls->ctx = SSL_CTX_new(TLSv1_client_method());
30
31         if (tls->ctx == NULL)
32         {
33                 printf("SSL_CTX_new failed\n");
34                 return false;
35         }
36
37         /*
38          * This is necessary, because the Microsoft TLS implementation is not perfect.
39          * SSL_OP_ALL enables a couple of workarounds for buggy TLS implementations,
40          * but the most important workaround being SSL_OP_TLS_BLOCK_PADDING_BUG.
41          * As the size of the encrypted payload may give hints about its contents,
42          * block padding is normally used, but the Microsoft TLS implementation
43          * won't recognize it and will disconnect you after sending a TLS alert.
44          */
45         SSL_CTX_set_options(tls->ctx, SSL_OP_ALL);
46
47         tls->ssl = SSL_new(tls->ctx);
48
49         if (tls->ssl == NULL)
50         {
51                 printf("SSL_new failed\n");
52                 return false;
53         }
54
55         if (SSL_set_fd(tls->ssl, tls->sockfd) < 1)
56         {
57                 printf("SSL_set_fd failed\n");
58                 return false;
59         }
60
61         connection_status = SSL_connect(tls->ssl);
62
63         if (connection_status <= 0)
64         {
65                 if (tls_print_error("SSL_connect", tls->ssl, connection_status))
66                         return false;
67         }
68
69         return true;
70 }
71
72 boolean tls_accept(rdpTls* tls, const char* cert_file, const char* privatekey_file)
73 {
74         int connection_status;
75
76         tls->ctx = SSL_CTX_new(TLSv1_server_method());
77
78         if (tls->ctx == NULL)
79         {
80                 printf("SSL_CTX_new failed\n");
81                 return false;
82         }
83
84         if (SSL_CTX_use_RSAPrivateKey_file(tls->ctx, privatekey_file, SSL_FILETYPE_PEM) <= 0)
85         {
86                 printf("SSL_CTX_use_RSAPrivateKey_file failed\n");
87                 return false;
88         }
89
90         tls->ssl = SSL_new(tls->ctx);
91
92         if (tls->ssl == NULL)
93         {
94                 printf("SSL_new failed\n");
95                 return false;
96         }
97
98         if (SSL_use_certificate_file(tls->ssl, cert_file, SSL_FILETYPE_PEM) <= 0)
99         {
100                 printf("SSL_use_certificate_file failed\n");
101                 return false;
102         }
103
104         if (SSL_set_fd(tls->ssl, tls->sockfd) < 1)
105         {
106                 printf("SSL_set_fd failed\n");
107                 return false;
108         }
109
110         connection_status = SSL_accept(tls->ssl);
111
112         if (connection_status <= 0)
113         {
114                 if (tls_print_error("SSL_accept", tls->ssl, connection_status))
115                         return false;
116         }
117
118         printf("TLS connection accepted\n");
119
120         return true;
121 }
122
123 boolean tls_disconnect(rdpTls* tls)
124 {
125         SSL_shutdown(tls->ssl);
126         return true;
127 }
128
129 int tls_read(rdpTls* tls, uint8* data, int length)
130 {
131         int status;
132
133         status = SSL_read(tls->ssl, data, length);
134
135         switch (SSL_get_error(tls->ssl, status))
136         {
137                 case SSL_ERROR_NONE:
138                         break;
139
140                 case SSL_ERROR_WANT_READ:
141                 case SSL_ERROR_WANT_WRITE:
142                         status = 0;
143                         break;
144
145                 default:
146                         tls_print_error("SSL_read", tls->ssl, status);
147                         status = -1;
148                         break;
149         }
150
151         return status;
152 }
153
154 int tls_write(rdpTls* tls, uint8* data, int length)
155 {
156         int status;
157
158         status = SSL_write(tls->ssl, data, length);
159
160         switch (SSL_get_error(tls->ssl, status))
161         {
162                 case SSL_ERROR_NONE:
163                         break;
164
165                 case SSL_ERROR_WANT_READ:
166                 case SSL_ERROR_WANT_WRITE:
167                         status = 0;
168                         break;
169
170                 default:
171                         tls_print_error("SSL_write", tls->ssl, status);
172                         status = -1;
173                         break;
174         }
175
176         return status;
177 }
178
179 boolean tls_print_error(char* func, SSL* connection, int value)
180 {
181         switch (SSL_get_error(connection, value))
182         {
183                 case SSL_ERROR_ZERO_RETURN:
184                         printf("%s: Server closed TLS connection\n", func);
185                         return true;
186
187                 case SSL_ERROR_WANT_READ:
188                         printf("SSL_ERROR_WANT_READ\n");
189                         return false;
190
191                 case SSL_ERROR_WANT_WRITE:
192                         printf("SSL_ERROR_WANT_WRITE\n");
193                         return false;
194
195                 case SSL_ERROR_SYSCALL:
196                         printf("%s: I/O error\n", func);
197                         return true;
198
199                 case SSL_ERROR_SSL:
200                         printf("%s: Failure in SSL library (protocol error?)\n", func);
201                         return true;
202
203                 default:
204                         printf("%s: Unknown error\n", func);
205                         return true;
206         }
207 }
208
209 CryptoCert tls_get_certificate(rdpTls* tls)
210 {
211         CryptoCert cert;
212         X509* server_cert;
213
214         server_cert = SSL_get_peer_certificate(tls->ssl);
215
216         if (!server_cert)
217         {
218                 printf("ssl_verify: failed to get the server SSL certificate\n");
219                 cert = NULL;
220         }
221         else
222         {
223                 cert = xmalloc(sizeof(*cert));
224                 cert->px509 = server_cert;
225         }
226
227         return cert;
228 }
229
230 boolean tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname)
231 {
232         int match;
233         int index;
234         char* common_name;
235         int common_name_length;
236         char** alt_names;
237         int alt_names_count;
238         int* alt_names_lengths;
239         boolean certificate_status;
240         boolean hostname_match = false;
241         rdpCertificateData* certificate_data;
242
243         /* ignore certificate verification if user explicitly required it (discouraged) */
244         if (tls->settings->ignore_certificate)
245                 return true;  /* success! */
246
247         /* if user explicitly specified a certificate name, use it instead of the hostname */
248         if (tls->settings->certificate_name)
249                 hostname = tls->settings->certificate_name;
250
251         /* attempt verification using OpenSSL and the ~/.freerdp/certs certificate store */
252         certificate_status = x509_verify_certificate(cert, tls->certificate_store->path);
253
254         /* verify certificate name match */
255         certificate_data = crypto_get_certificate_data(cert->px509, hostname);
256
257         /* extra common name and alternative names */
258         common_name = crypto_cert_subject_common_name(cert->px509, &common_name_length);
259         alt_names = crypto_cert_subject_alt_name(cert->px509, &alt_names_count, &alt_names_lengths);
260
261         /* compare against common name */
262
263         if (common_name != NULL)
264         {
265                 if (strlen(hostname) == common_name_length)
266                 {
267                         if (memcmp((void*) hostname, (void*) common_name, common_name_length) == 0)
268                                 hostname_match = true;
269                 }
270         }
271
272         /* compare against alternative names */
273
274         if (alt_names != NULL)
275         {
276                 for (index = 0; index < alt_names_count; index++)
277                 {
278                         if (strlen(hostname) == alt_names_lengths[index])
279                         {
280                                 if (memcmp((void*) hostname, (void*) alt_names[index], alt_names_lengths[index]) == 0)
281                                         hostname_match = true;
282                         }
283                 }
284         }
285
286         /* if the certificate is valid and the certificate name matches, verification succeeds */
287         if (certificate_status && hostname_match)
288                 return true; /* success! */
289
290         /* if the certificate is valid but the certificate name does not match, warn user, do not accept */
291         if (certificate_status && !hostname_match)
292                 tls_print_certificate_name_mismatch_error(hostname, common_name, alt_names, alt_names_count);
293
294         /* verification could not succeed with OpenSSL, use known_hosts file and prompt user for manual verification */
295
296         if (!certificate_status)
297         {
298                 char* issuer;
299                 char* subject;
300                 char* fingerprint;
301                 boolean accept_certificate = false;
302                 boolean verification_status = false;
303
304                 issuer = crypto_cert_issuer(cert->px509);
305                 subject = crypto_cert_subject(cert->px509);
306                 fingerprint = crypto_cert_fingerprint(cert->px509);
307
308                 /* search for matching entry in known_hosts file */
309                 match = certificate_data_match(tls->certificate_store, certificate_data);
310
311                 if (match == 1)
312                 {
313                         /* no entry was found in known_hosts file, prompt user for manual verification */
314
315                         freerdp* instance = (freerdp*) tls->settings->instance;
316
317                         if (!hostname_match)
318                                 tls_print_certificate_name_mismatch_error(hostname, common_name, alt_names, alt_names_count);
319
320                         if (instance->VerifyCertificate)
321                                 accept_certificate = instance->VerifyCertificate(instance, subject, issuer, fingerprint);
322
323                         if (!accept_certificate)
324                         {
325                                 /* user did not accept, abort and do not add entry in known_hosts file */
326                                 verification_status = false;  /* failure! */
327                         }
328                         else
329                         {
330                                 /* user accepted certificate, add entry in known_hosts file */
331                                 certificate_data_print(tls->certificate_store, certificate_data);
332                                 verification_status = true; /* success! */
333                         }
334                 }
335                 else if (match == -1)
336                 {
337                         /* entry was found in known_hosts file, but fingerprint does not match */
338                         tls_print_certificate_error(hostname, fingerprint);
339                         verification_status = false; /* failure! */
340                 }
341                 else if (match == 0)
342                 {
343                         verification_status = true; /* success! */
344                 }
345
346                 xfree(issuer);
347                 xfree(subject);
348                 xfree(fingerprint);
349
350                 return verification_status;
351         }
352
353         return false;
354 }
355
356 void tls_print_certificate_error(char* hostname, char* fingerprint)
357 {
358         printf("The host key for %s has changed\n", hostname);
359         printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
360         printf("@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @\n");
361         printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
362         printf("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\n");
363         printf("Someone could be eavesdropping on you right now (man-in-the-middle attack)!\n");
364         printf("It is also possible that a host key has just been changed.\n");
365         printf("The fingerprint for the host key sent by the remote host is\n%s\n", fingerprint);
366         printf("Please contact your system administrator.\n");
367         printf("Add correct host key in ~/.freerdp/known_hosts to get rid of this message.\n");
368         printf("Host key for %s has changed and you have requested strict checking.\n", hostname);
369         printf("Host key verification failed.\n");
370 }
371
372 void tls_print_certificate_name_mismatch_error(char* hostname, char* common_name, char** alt_names, int alt_names_count)
373 {
374         int index;
375
376         printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
377         printf("@           WARNING: CERTIFICATE NAME MISMATCH!           @\n");
378         printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
379         printf("The hostname used for this connection (%s) \n", hostname);
380
381         if (alt_names_count < 1)
382         {
383                 printf("does not match the name given in the certificate:\n");
384                 printf("%s\n", common_name);
385         }
386         else
387         {
388                 printf("does not match the names given in the certificate:\n");
389                 printf("%s", common_name);
390
391                 for (index = 0; index < alt_names_count; index++)
392                 {
393                         printf(", %s", alt_names[index]);
394                 }
395
396                 printf("\n");
397         }
398
399         printf("A valid certificate for the wrong name should NOT be trusted!\n");
400 }
401
402 rdpTls* tls_new(rdpSettings* settings)
403 {
404         rdpTls* tls;
405
406         tls = (rdpTls*) xzalloc(sizeof(rdpTls));
407
408         if (tls != NULL)
409         {
410                 SSL_load_error_strings();
411                 SSL_library_init();
412
413                 tls->settings = settings;
414                 tls->certificate_store = certificate_store_new(settings);
415         }
416
417         return tls;
418 }
419
420 void tls_free(rdpTls* tls)
421 {
422         if (tls != NULL)
423         {
424                 if (tls->ssl)
425                         SSL_free(tls->ssl);
426
427                 if (tls->ctx)
428                         SSL_CTX_free(tls->ctx);
429
430                 certificate_store_free(tls->certificate_store);
431
432                 xfree(tls);
433         }
434 }