libfreerdp-crypto: make distinction between TLS connection error and user cancellation

This commit is contained in:
Marc-André Moreau 2014-04-01 16:23:27 -04:00
parent 046a33ba9f
commit feea87b42f
3 changed files with 101 additions and 37 deletions

View File

@ -84,7 +84,7 @@ struct rdp_tls
int alertDescription; int alertDescription;
}; };
FREERDP_API BOOL tls_connect(rdpTls* tls); FREERDP_API int tls_connect(rdpTls* tls);
FREERDP_API BOOL tls_accept(rdpTls* tls, const char* cert_file, const char* privatekey_file); FREERDP_API BOOL tls_accept(rdpTls* tls, const char* cert_file, const char* privatekey_file);
FREERDP_API BOOL tls_disconnect(rdpTls* tls); FREERDP_API BOOL tls_disconnect(rdpTls* tls);

View File

@ -224,6 +224,7 @@ BIO_METHOD* BIO_s_tsg(void)
BOOL transport_connect_tls(rdpTransport* transport) BOOL transport_connect_tls(rdpTransport* transport)
{ {
int tls_status;
freerdp* instance; freerdp* instance;
rdpContext* context; rdpContext* context;
@ -245,13 +246,23 @@ BOOL transport_connect_tls(rdpTransport* transport)
if (transport->TsgTls->port == 0) if (transport->TsgTls->port == 0)
transport->TsgTls->port = 3389; transport->TsgTls->port = 3389;
if (!tls_connect(transport->TsgTls)) tls_status = tls_connect(transport->TsgTls);
if (tls_status < 1)
{
if (tls_status < 0)
{ {
if (!connectErrorCode) if (!connectErrorCode)
connectErrorCode = TLSCONNECTERROR; connectErrorCode = TLSCONNECTERROR;
if (!freerdp_get_last_error(context)) if (!freerdp_get_last_error(context))
freerdp_set_last_error(context, FREERDP_ERROR_TLS_CONNECT_FAILED); freerdp_set_last_error(context, FREERDP_ERROR_TLS_CONNECT_FAILED);
}
else
{
if (!freerdp_get_last_error(context))
freerdp_set_last_error(context, FREERDP_ERROR_CONNECT_CANCELLED);
}
tls_free(transport->TsgTls); tls_free(transport->TsgTls);
transport->TsgTls = NULL; transport->TsgTls = NULL;
@ -277,13 +288,23 @@ BOOL transport_connect_tls(rdpTransport* transport)
if (transport->TlsIn->port == 0) if (transport->TlsIn->port == 0)
transport->TlsIn->port = 3389; transport->TlsIn->port = 3389;
if (!tls_connect(transport->TlsIn)) tls_status = tls_connect(transport->TlsIn);
if (tls_status < 1)
{
if (tls_status < 0)
{ {
if (!connectErrorCode) if (!connectErrorCode)
connectErrorCode = TLSCONNECTERROR; connectErrorCode = TLSCONNECTERROR;
if (!freerdp_get_last_error(context)) if (!freerdp_get_last_error(context))
freerdp_set_last_error(context, FREERDP_ERROR_TLS_CONNECT_FAILED); freerdp_set_last_error(context, FREERDP_ERROR_TLS_CONNECT_FAILED);
}
else
{
if (!freerdp_get_last_error(context))
freerdp_set_last_error(context, FREERDP_ERROR_CONNECT_CANCELLED);
}
tls_free(transport->TlsIn); tls_free(transport->TlsIn);
@ -355,6 +376,13 @@ BOOL transport_connect_nla(rdpTransport* transport)
BOOL transport_tsg_connect(rdpTransport* transport, const char* hostname, UINT16 port) BOOL transport_tsg_connect(rdpTransport* transport, const char* hostname, UINT16 port)
{ {
int tls_status;
freerdp* instance;
rdpContext* context;
instance = (freerdp*) transport->settings->instance;
context = instance->context;
rdpTsg* tsg = tsg_new(transport); rdpTsg* tsg = tsg_new(transport);
tsg->transport = transport; tsg->transport = transport;
@ -381,11 +409,41 @@ BOOL transport_tsg_connect(rdpTransport* transport, const char* hostname, UINT16
if (transport->TlsOut->port == 0) if (transport->TlsOut->port == 0)
transport->TlsOut->port = 443; transport->TlsOut->port = 443;
if (!tls_connect(transport->TlsIn)) tls_status = tls_connect(transport->TlsIn);
return FALSE;
if (tls_status < 1)
{
if (tls_status < 0)
{
if (!freerdp_get_last_error(context))
freerdp_set_last_error(context, FREERDP_ERROR_TLS_CONNECT_FAILED);
}
else
{
if (!freerdp_get_last_error(context))
freerdp_set_last_error(context, FREERDP_ERROR_CONNECT_CANCELLED);
}
if (!tls_connect(transport->TlsOut))
return FALSE; return FALSE;
}
tls_status = tls_connect(transport->TlsOut);
if (tls_status < 1)
{
if (tls_status < 0)
{
if (!freerdp_get_last_error(context))
freerdp_set_last_error(context, FREERDP_ERROR_TLS_CONNECT_FAILED);
}
else
{
if (!freerdp_get_last_error(context))
freerdp_set_last_error(context, FREERDP_ERROR_CONNECT_CANCELLED);
}
return FALSE;
}
if (!tsg_connect(tsg, hostname, port)) if (!tsg_connect(tsg, hostname, port))
return FALSE; return FALSE;

View File

@ -105,10 +105,11 @@ static void tls_ssl_info_callback(const SSL* ssl, int type, int val)
} }
} }
BOOL tls_connect(rdpTls* tls) int tls_connect(rdpTls* tls)
{ {
CryptoCert cert; CryptoCert cert;
long options = 0; long options = 0;
int verify_status;
int connection_status; int connection_status;
tls->ctx = SSL_CTX_new(TLSv1_client_method()); tls->ctx = SSL_CTX_new(TLSv1_client_method());
@ -116,7 +117,7 @@ BOOL tls_connect(rdpTls* tls)
if (!tls->ctx) if (!tls->ctx)
{ {
fprintf(stderr, "SSL_CTX_new failed\n"); fprintf(stderr, "SSL_CTX_new failed\n");
return FALSE; return -1;
} }
//SSL_CTX_set_mode(tls->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE); //SSL_CTX_set_mode(tls->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE);
@ -157,7 +158,7 @@ BOOL tls_connect(rdpTls* tls)
if (!tls->ssl) if (!tls->ssl)
{ {
fprintf(stderr, "SSL_new failed\n"); fprintf(stderr, "SSL_new failed\n");
return FALSE; return -1;
} }
if (tls->tsg) if (tls->tsg)
@ -167,7 +168,7 @@ BOOL tls_connect(rdpTls* tls)
if (!tls->bio) if (!tls->bio)
{ {
fprintf(stderr, "BIO_new failed\n"); fprintf(stderr, "BIO_new failed\n");
return FALSE; return -1;
} }
tls->bio->ptr = tls->tsg; tls->bio->ptr = tls->tsg;
@ -181,7 +182,7 @@ BOOL tls_connect(rdpTls* tls)
if (SSL_set_fd(tls->ssl, tls->sockfd) < 1) if (SSL_set_fd(tls->ssl, tls->sockfd) < 1)
{ {
fprintf(stderr, "SSL_set_fd failed\n"); fprintf(stderr, "SSL_set_fd failed\n");
return FALSE; return -1;
} }
} }
@ -191,7 +192,7 @@ BOOL tls_connect(rdpTls* tls)
{ {
if (tls_print_error("SSL_connect", tls->ssl, connection_status)) if (tls_print_error("SSL_connect", tls->ssl, connection_status))
{ {
return FALSE; return -1;
} }
} }
@ -200,7 +201,7 @@ BOOL tls_connect(rdpTls* tls)
if (!cert) if (!cert)
{ {
fprintf(stderr, "tls_connect: tls_get_certificate failed to return the server certificate.\n"); fprintf(stderr, "tls_connect: tls_get_certificate failed to return the server certificate.\n");
return FALSE; return -1;
} }
tls->Bindings = tls_get_channel_bindings(cert->px509); tls->Bindings = tls_get_channel_bindings(cert->px509);
@ -209,20 +210,22 @@ BOOL tls_connect(rdpTls* tls)
{ {
fprintf(stderr, "tls_connect: crypto_cert_get_public_key failed to return the server public key.\n"); fprintf(stderr, "tls_connect: crypto_cert_get_public_key failed to return the server public key.\n");
tls_free_certificate(cert); tls_free_certificate(cert);
return FALSE; return -1;
} }
if (!tls_verify_certificate(tls, cert, tls->hostname, tls->port)) verify_status = tls_verify_certificate(tls, cert, tls->hostname, tls->port);
if (verify_status < 1)
{ {
fprintf(stderr, "tls_connect: certificate not trusted, aborting.\n"); fprintf(stderr, "tls_connect: certificate not trusted, aborting.\n");
tls_disconnect(tls); tls_disconnect(tls);
tls_free_certificate(cert); tls_free_certificate(cert);
return FALSE; return verify_status;
} }
tls_free_certificate(cert); tls_free_certificate(cert);
return TRUE; return (verify_status == 0) ? 0 : 1;
} }
BOOL tls_accept(rdpTls* tls, const char* cert_file, const char* privatekey_file) BOOL tls_accept(rdpTls* tls, const char* cert_file, const char* privatekey_file)
@ -612,7 +615,7 @@ BOOL tls_match_hostname(char *pattern, int pattern_length, char *hostname)
return FALSE; return FALSE;
} }
BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int port) int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int port)
{ {
int match; int match;
int index; int index;
@ -644,7 +647,7 @@ BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int po
if (!bio) if (!bio)
{ {
fprintf(stderr, "tls_verify_certificate: BIO_new() failure\n"); fprintf(stderr, "tls_verify_certificate: BIO_new() failure\n");
return FALSE; return -1;
} }
status = PEM_write_bio_X509(bio, cert->px509); status = PEM_write_bio_X509(bio, cert->px509);
@ -652,7 +655,7 @@ BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int po
if (status < 0) if (status < 0)
{ {
fprintf(stderr, "tls_verify_certificate: PEM_write_bio_X509 failure: %d\n", status); fprintf(stderr, "tls_verify_certificate: PEM_write_bio_X509 failure: %d\n", status);
return FALSE; return -1;
} }
offset = 0; offset = 0;
@ -664,7 +667,7 @@ BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int po
if (status < 0) if (status < 0)
{ {
fprintf(stderr, "tls_verify_certificate: failed to read certificate\n"); fprintf(stderr, "tls_verify_certificate: failed to read certificate\n");
return FALSE; return -1;
} }
offset += status; offset += status;
@ -685,7 +688,7 @@ BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int po
if (status < 0) if (status < 0)
{ {
fprintf(stderr, "tls_verify_certificate: failed to read certificate\n"); fprintf(stderr, "tls_verify_certificate: failed to read certificate\n");
return FALSE; return -1;
} }
length = offset; length = offset;
@ -704,12 +707,15 @@ BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int po
free(pemCert); free(pemCert);
BIO_free(bio); BIO_free(bio);
return (status < 0) ? FALSE : TRUE; if (status < 0)
return -1;
return (status == 0) ? 0 : 1;
} }
/* ignore certificate verification if user explicitly required it (discouraged) */ /* ignore certificate verification if user explicitly required it (discouraged) */
if (tls->settings->IgnoreCertificate) if (tls->settings->IgnoreCertificate)
return TRUE; /* success! */ return 1; /* success! */
/* if user explicitly specified a certificate name, use it instead of the hostname */ /* if user explicitly specified a certificate name, use it instead of the hostname */
if (tls->settings->CertificateName) if (tls->settings->CertificateName)
@ -727,7 +733,7 @@ BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int po
/* compare against common name */ /* compare against common name */
if (common_name != NULL) if (common_name)
{ {
if (tls_match_hostname(common_name, common_name_length, hostname)) if (tls_match_hostname(common_name, common_name_length, hostname))
hostname_match = TRUE; hostname_match = TRUE;
@ -735,7 +741,7 @@ BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int po
/* compare against alternative names */ /* compare against alternative names */
if (alt_names != NULL) if (alt_names)
{ {
for (index = 0; index < alt_names_count; index++) for (index = 0; index < alt_names_count; index++)
{ {
@ -851,7 +857,7 @@ BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int po
crypto_cert_subject_alt_name_free(alt_names_count, alt_names_lengths, crypto_cert_subject_alt_name_free(alt_names_count, alt_names_lengths,
alt_names); alt_names);
return verification_status; return (verification_status == 0) ? 0 : 1;
} }
void tls_print_certificate_error(char* hostname, char* fingerprint, char *hosts_file) void tls_print_certificate_error(char* hostname, char* fingerprint, char *hosts_file)