| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| */ |
| |
| #include "config.h" |
| |
| #include "socket-ssl.h" |
| |
| #include <poll.h> |
| #include <stdlib.h> |
| |
| #include <guacamole/error.h> |
| #include <guacamole/socket.h> |
| #include <openssl/ssl.h> |
| |
| static ssize_t __guac_socket_ssl_read_handler(guac_socket* socket, |
| void* buf, size_t count) { |
| |
| /* Read from socket */ |
| guac_socket_ssl_data* data = (guac_socket_ssl_data*) socket->data; |
| int retval; |
| |
| retval = SSL_read(data->ssl, buf, count); |
| |
| /* Record errors in guac_error */ |
| if (retval <= 0) { |
| guac_error = GUAC_STATUS_SEE_ERRNO; |
| guac_error_message = "Error reading data from secure socket"; |
| } |
| |
| return retval; |
| |
| } |
| |
| static ssize_t __guac_socket_ssl_write_handler(guac_socket* socket, |
| const void* buf, size_t count) { |
| |
| /* Write data to socket */ |
| guac_socket_ssl_data* data = (guac_socket_ssl_data*) socket->data; |
| int retval; |
| |
| retval = SSL_write(data->ssl, buf, count); |
| |
| /* Record errors in guac_error */ |
| if (retval <= 0) { |
| guac_error = GUAC_STATUS_SEE_ERRNO; |
| guac_error_message = "Error writing data to secure socket"; |
| } |
| |
| return retval; |
| |
| } |
| |
| static int __guac_socket_ssl_select_handler(guac_socket* socket, int usec_timeout) { |
| |
| guac_socket_ssl_data* data = (guac_socket_ssl_data*) socket->data; |
| |
| int retval; |
| |
| /* Initialize with single underlying file descriptor */ |
| struct pollfd fds[1] = {{ |
| .fd = data->fd, |
| .events = POLLIN, |
| .revents = 0, |
| }}; |
| |
| /* No timeout if usec_timeout is negative */ |
| if (usec_timeout < 0) |
| retval = poll(fds, 1, -1); |
| |
| /* Handle timeout if specified, rounding up to poll()'s granularity */ |
| else |
| retval = poll(fds, 1, (usec_timeout + 999) / 1000); |
| |
| /* Properly set guac_error */ |
| if (retval < 0) { |
| guac_error = GUAC_STATUS_SEE_ERRNO; |
| guac_error_message = "Error while waiting for data on secure socket"; |
| } |
| |
| if (retval == 0) { |
| guac_error = GUAC_STATUS_TIMEOUT; |
| guac_error_message = "Timeout while waiting for data on secure socket"; |
| } |
| |
| return retval; |
| |
| } |
| |
| static int __guac_socket_ssl_free_handler(guac_socket* socket) { |
| |
| /* Shutdown SSL */ |
| guac_socket_ssl_data* data = (guac_socket_ssl_data*) socket->data; |
| SSL_shutdown(data->ssl); |
| |
| /* Close file descriptor */ |
| close(data->fd); |
| |
| free(data); |
| return 0; |
| } |
| |
| guac_socket* guac_socket_open_secure(SSL_CTX* context, int fd) { |
| |
| /* Allocate socket and associated data */ |
| guac_socket* socket = guac_socket_alloc(); |
| guac_socket_ssl_data* data = malloc(sizeof(guac_socket_ssl_data)); |
| |
| /* Init SSL */ |
| data->context = context; |
| data->ssl = SSL_new(context); |
| SSL_set_fd(data->ssl, fd); |
| |
| /* Accept SSL connection, handle errors */ |
| if (SSL_accept(data->ssl) <= 0) { |
| |
| guac_error = GUAC_STATUS_INTERNAL_ERROR; |
| guac_error_message = "SSL accept failed"; |
| |
| free(data); |
| guac_socket_free(socket); |
| return NULL; |
| } |
| |
| /* Store file descriptor as socket data */ |
| data->fd = fd; |
| socket->data = data; |
| |
| /* Set read/write handlers */ |
| socket->read_handler = __guac_socket_ssl_read_handler; |
| socket->write_handler = __guac_socket_ssl_write_handler; |
| socket->select_handler = __guac_socket_ssl_select_handler; |
| socket->free_handler = __guac_socket_ssl_free_handler; |
| |
| return socket; |
| |
| } |
| |