2 * FreeRDP: A Remote Desktop Protocol Client
3 * Transport Layer Security
5 * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #include <freerdp/utils/stream.h>
21 #include <freerdp/utils/memory.h>
25 boolean tls_connect(rdpTls* tls)
27 int connection_status;
29 tls->ctx = SSL_CTX_new(TLSv1_client_method());
33 printf("SSL_CTX_new failed\n");
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.
45 SSL_CTX_set_options(tls->ctx, SSL_OP_ALL);
47 tls->ssl = SSL_new(tls->ctx);
51 printf("SSL_new failed\n");
55 if (SSL_set_fd(tls->ssl, tls->sockfd) < 1)
57 printf("SSL_set_fd failed\n");
61 connection_status = SSL_connect(tls->ssl);
63 if (connection_status <= 0)
65 if (tls_print_error("SSL_connect", tls->ssl, connection_status))
72 boolean tls_accept(rdpTls* tls, const char* cert_file, const char* privatekey_file)
74 int connection_status;
76 tls->ctx = SSL_CTX_new(TLSv1_server_method());
80 printf("SSL_CTX_new failed\n");
84 if (SSL_CTX_use_RSAPrivateKey_file(tls->ctx, privatekey_file, SSL_FILETYPE_PEM) <= 0)
86 printf("SSL_CTX_use_RSAPrivateKey_file failed\n");
90 tls->ssl = SSL_new(tls->ctx);
94 printf("SSL_new failed\n");
98 if (SSL_use_certificate_file(tls->ssl, cert_file, SSL_FILETYPE_PEM) <= 0)
100 printf("SSL_use_certificate_file failed\n");
104 if (SSL_set_fd(tls->ssl, tls->sockfd) < 1)
106 printf("SSL_set_fd failed\n");
110 connection_status = SSL_accept(tls->ssl);
112 if (connection_status <= 0)
114 if (tls_print_error("SSL_accept", tls->ssl, connection_status))
118 printf("TLS connection accepted\n");
123 boolean tls_disconnect(rdpTls* tls)
125 SSL_shutdown(tls->ssl);
129 int tls_read(rdpTls* tls, uint8* data, int length)
133 status = SSL_read(tls->ssl, data, length);
135 switch (SSL_get_error(tls->ssl, status))
140 case SSL_ERROR_WANT_READ:
141 case SSL_ERROR_WANT_WRITE:
146 tls_print_error("SSL_read", tls->ssl, status);
154 int tls_write(rdpTls* tls, uint8* data, int length)
158 status = SSL_write(tls->ssl, data, length);
160 switch (SSL_get_error(tls->ssl, status))
165 case SSL_ERROR_WANT_READ:
166 case SSL_ERROR_WANT_WRITE:
171 tls_print_error("SSL_write", tls->ssl, status);
179 boolean tls_print_error(char* func, SSL* connection, int value)
181 switch (SSL_get_error(connection, value))
183 case SSL_ERROR_ZERO_RETURN:
184 printf("%s: Server closed TLS connection\n", func);
187 case SSL_ERROR_WANT_READ:
188 printf("SSL_ERROR_WANT_READ\n");
191 case SSL_ERROR_WANT_WRITE:
192 printf("SSL_ERROR_WANT_WRITE\n");
195 case SSL_ERROR_SYSCALL:
196 printf("%s: I/O error\n", func);
200 printf("%s: Failure in SSL library (protocol error?)\n", func);
204 printf("%s: Unknown error\n", func);
209 CryptoCert tls_get_certificate(rdpTls* tls)
214 server_cert = SSL_get_peer_certificate(tls->ssl);
218 printf("ssl_verify: failed to get the server SSL certificate\n");
223 cert = xmalloc(sizeof(*cert));
224 cert->px509 = server_cert;
230 boolean tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname)
235 int common_name_length;
238 int* alt_names_lengths;
239 boolean certificate_status;
240 boolean hostname_match = false;
241 rdpCertificateData* certificate_data;
243 /* ignore certificate verification if user explicitly required it (discouraged) */
244 if (tls->settings->ignore_certificate)
245 return true; /* success! */
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;
251 /* attempt verification using OpenSSL and the ~/.freerdp/certs certificate store */
252 certificate_status = x509_verify_certificate(cert, tls->certificate_store->path);
254 /* verify certificate name match */
255 certificate_data = crypto_get_certificate_data(cert->px509, hostname);
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);
261 /* compare against common name */
263 if (common_name != NULL)
265 if (strlen(hostname) == common_name_length)
267 if (memcmp((void*) hostname, (void*) common_name, common_name_length) == 0)
268 hostname_match = true;
272 /* compare against alternative names */
274 if (alt_names != NULL)
276 for (index = 0; index < alt_names_count; index++)
278 if (strlen(hostname) == alt_names_lengths[index])
280 if (memcmp((void*) hostname, (void*) alt_names[index], alt_names_lengths[index]) == 0)
281 hostname_match = true;
286 /* if the certificate is valid and the certificate name matches, verification succeeds */
287 if (certificate_status && hostname_match)
288 return true; /* success! */
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);
294 /* verification could not succeed with OpenSSL, use known_hosts file and prompt user for manual verification */
296 if (!certificate_status)
301 boolean accept_certificate = false;
302 boolean verification_status = false;
304 issuer = crypto_cert_issuer(cert->px509);
305 subject = crypto_cert_subject(cert->px509);
306 fingerprint = crypto_cert_fingerprint(cert->px509);
308 /* search for matching entry in known_hosts file */
309 match = certificate_data_match(tls->certificate_store, certificate_data);
313 /* no entry was found in known_hosts file, prompt user for manual verification */
315 freerdp* instance = (freerdp*) tls->settings->instance;
318 tls_print_certificate_name_mismatch_error(hostname, common_name, alt_names, alt_names_count);
320 if (instance->VerifyCertificate)
321 accept_certificate = instance->VerifyCertificate(instance, subject, issuer, fingerprint);
323 if (!accept_certificate)
325 /* user did not accept, abort and do not add entry in known_hosts file */
326 verification_status = false; /* failure! */
330 /* user accepted certificate, add entry in known_hosts file */
331 certificate_data_print(tls->certificate_store, certificate_data);
332 verification_status = true; /* success! */
335 else if (match == -1)
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! */
343 verification_status = true; /* success! */
350 return verification_status;
356 void tls_print_certificate_error(char* hostname, char* fingerprint)
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");
372 void tls_print_certificate_name_mismatch_error(char* hostname, char* common_name, char** alt_names, int alt_names_count)
376 printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
377 printf("@ WARNING: CERTIFICATE NAME MISMATCH! @\n");
378 printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
379 printf("The hostname used for this connection (%s) \n", hostname);
381 if (alt_names_count < 1)
383 printf("does not match the name given in the certificate:\n");
384 printf("%s\n", common_name);
388 printf("does not match the names given in the certificate:\n");
389 printf("%s", common_name);
391 for (index = 0; index < alt_names_count; index++)
393 printf(", %s", alt_names[index]);
399 printf("A valid certificate for the wrong name should NOT be trusted!\n");
402 rdpTls* tls_new(rdpSettings* settings)
406 tls = (rdpTls*) xzalloc(sizeof(rdpTls));
410 SSL_load_error_strings();
413 tls->settings = settings;
414 tls->certificate_store = certificate_store_new(settings);
420 void tls_free(rdpTls* tls)
428 SSL_CTX_free(tls->ctx);
430 certificate_store_free(tls->certificate_store);