/** * xrdp: A Remote Desktop Protocol server. * * Copyright (C) Idan Freiberg 2013-2014 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * transport layer security */ #include #include #include #include #include "os_calls.h" #include "trans.h" #include "ssl_calls.h" /*****************************************************************************/ struct xrdp_tls * APP_CC xrdp_tls_create(struct trans *trans, const char *key, const char *cert) { struct xrdp_tls *self; self = (struct xrdp_tls *) g_malloc(sizeof(struct xrdp_tls), 1); if (self != NULL) { self->trans = trans; self->cert = (char *) cert; self->key = (char *) key; } return self; } /*****************************************************************************/ int APP_CC xrdp_tls_print_error(char *func, SSL *connection, int value) { switch (SSL_get_error(connection, value)) { case SSL_ERROR_ZERO_RETURN: g_writeln("xrdp_tls_print_error: %s: Server closed TLS connection", func); return 1; case SSL_ERROR_WANT_READ: g_writeln("xrdp_tls_print_error: SSL_ERROR_WANT_READ"); return 0; case SSL_ERROR_WANT_WRITE: g_writeln("xrdp_tls_print_error: SSL_ERROR_WANT_WRITE"); return 0; case SSL_ERROR_SYSCALL: g_writeln("xrdp_tls_print_error: %s: I/O error", func); return 1; case SSL_ERROR_SSL: g_writeln("xrdp_tls_print_error: %s: Failure in SSL library (protocol error?)", func); return 1; default: g_writeln("xrdp_tls_print_error: %s: Unknown error", func); return 1; } } /*****************************************************************************/ int APP_CC xrdp_tls_accept(struct xrdp_tls *self) { int connection_status; long options = 0; /** * SSL_OP_NO_SSLv2: * * We only want SSLv3 and TLSv1, so disable SSLv2. * SSLv3 is used by, eg. Microsoft RDC for Mac OS X. */ options |= SSL_OP_NO_SSLv2; #if defined(SSL_OP_NO_COMPRESSION) /** * SSL_OP_NO_COMPRESSION: * * The Microsoft RDP server does not advertise support * for TLS compression, but alternative servers may support it. * This was observed between early versions of the FreeRDP server * and the FreeRDP client, and caused major performance issues, * which is why we're disabling it. */ options |= SSL_OP_NO_COMPRESSION; #endif /** * SSL_OP_TLS_BLOCK_PADDING_BUG: * * The Microsoft RDP server does *not* support TLS padding. * It absolutely needs to be disabled otherwise it won't work. */ options |= SSL_OP_TLS_BLOCK_PADDING_BUG; /** * SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: * * Just like TLS padding, the Microsoft RDP server does not * support empty fragments. This needs to be disabled. */ options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; self->ctx = SSL_CTX_new(SSLv23_server_method()); /* set context options */ SSL_CTX_set_mode(self->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_options(self->ctx, options); SSL_CTX_set_read_ahead(self->ctx, 1); if (self->ctx == NULL) { g_writeln("xrdp_tls_accept: SSL_CTX_new failed"); return 1; } if (SSL_CTX_use_RSAPrivateKey_file(self->ctx, self->key, SSL_FILETYPE_PEM) <= 0) { g_writeln("xrdp_tls_accept: SSL_CTX_use_RSAPrivateKey_file failed"); return 1; } self->ssl = SSL_new(self->ctx); if (self->ssl == NULL) { g_writeln("xrdp_tls_accept: SSL_new failed"); return 1; } if (SSL_use_certificate_file(self->ssl, self->cert, SSL_FILETYPE_PEM) <= 0) { g_writeln("xrdp_tls_accept: SSL_use_certificate_file failed"); return 1; } if (SSL_set_fd(self->ssl, self->trans->sck) < 1) { g_writeln("xrdp_tls_accept: SSL_set_fd failed"); return 1; } connection_status = SSL_accept(self->ssl); if (connection_status <= 0) { if (xrdp_tls_print_error("SSL_accept", self->ssl, connection_status)) { return 1; } } g_writeln("xrdp_tls_accept: TLS connection accepted"); return 0; } /*****************************************************************************/ int APP_CC xrdp_tls_disconnect(struct xrdp_tls *self) { int status = SSL_shutdown(self->ssl); while (status != 1) { status = SSL_shutdown(self->ssl); if (status <= 0) { if (xrdp_tls_print_error("SSL_shutdown", self->ssl, status)) { return 1; } } } return 0; } /*****************************************************************************/ void APP_CC xrdp_tls_delete(struct xrdp_tls *self) { if (self != NULL) { if (self->ssl) SSL_free(self->ssl); if (self->ctx) SSL_CTX_free(self->ctx); g_free(self); } } /*****************************************************************************/ int APP_CC xrdp_tls_read(struct xrdp_tls *tls, char *data, int length) { int status; status = SSL_read(tls->ssl, data, length); switch (SSL_get_error(tls->ssl, status)) { case SSL_ERROR_NONE: break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: status = 0; break; default: xrdp_tls_print_error("SSL_read", tls->ssl, status); status = -1; break; } return status; } /*****************************************************************************/ int APP_CC xrdp_tls_write(struct xrdp_tls *tls, char *data, int length) { int status; status = SSL_write(tls->ssl, data, length); switch (SSL_get_error(tls->ssl, status)) { case SSL_ERROR_NONE: break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: status = 0; break; default: xrdp_tls_print_error("SSL_write", tls->ssl, status); status = -1; break; } return status; }