kinetic-c  v0.12.0
Seagate Kinetic Protocol Client Library for C
bus_ssl.c
Go to the documentation of this file.
1 /*
2 * kinetic-c
3 * Copyright (C) 2015 Seagate Technology.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 */
20 #include <poll.h>
21 #include <assert.h>
22 
23 #include "bus_ssl.h"
24 #include "syscall.h"
25 #include "util.h"
26 
27 #define TIMEOUT_MSEC 100
28 #define MAX_TIMEOUT 10000
29 
30 static bool init_client_SSL_CTX(SSL_CTX **ctx_out);
31 static void disable_SSL_compression(void);
32 static void disable_known_bad_ciphers(SSL_CTX *ctx);
33 static bool do_blocking_connection(struct bus *b, SSL *ssl, int fd);
34 
35 /* Initialize the SSL library internals for use by the messaging bus. */
36 bool BusSSL_Init(struct bus *b) {
37  if (!SSL_library_init()) { return false; }
38  SSL_load_error_strings();
39  ERR_load_BIO_strings();
40  OpenSSL_add_ssl_algorithms();
41 
42  SSL_CTX *ctx = NULL;
43  if (!init_client_SSL_CTX(&ctx)) { return false; }
44  b->ssl_ctx = ctx;
45 
46  return true;
47 }
48 
49 /* Do an SSL / TLS handshake for a connection. Blocking.
50  * Returns whether the connection succeeded. */
51 SSL *BusSSL_Connect(struct bus *b, int fd) {
52  SSL *ssl = NULL;
53 
54  ssl = SSL_new(b->ssl_ctx);
55  if (ssl == NULL) {
56  ERR_print_errors_fp(stderr);
57  return NULL;
58  }
59 
60  if (!SSL_set_fd(ssl, fd)) {
61  return NULL;
62  }
63 
64  if (do_blocking_connection(b, ssl, fd)) {
65  return ssl;
66  } else {
67  SSL_free(ssl);
68  return NULL;
69  }
70 }
71 
72 /* Disconnect and free an individual SSL handle. */
73 bool BusSSL_Disconnect(struct bus *b, SSL *ssl) {
74  SSL_free(ssl);
75  (void)b;
76  return true;
77 }
78 
79 /* Free all internal data for using SSL (the SSL_CTX). */
80 void BusSSL_CtxFree(struct bus *b) {
81  if (b && b->ssl_ctx) {
82  SSL_CTX_free(b->ssl_ctx);
83  b->ssl_ctx = NULL;
84  }
85 }
86 
87 static bool init_client_SSL_CTX(SSL_CTX **ctx_out) {
88  SSL_CTX *ctx = NULL;
89  assert(ctx_out);
90 
91  /* Create TLS context */
92  const SSL_METHOD *method = NULL;
93 
94  #if KINETIC_USE_TLS_1_2
95  method = TLSv1_2_client_method();
96  #else
97  method = TLSv1_1_client_method();
98  #endif
99 
100  assert(method);
101  ctx = SSL_CTX_new(method);
102  if (ctx == NULL) {
103  ERR_print_errors_fp(stderr);
104  return false;
105  }
106 
109  *ctx_out = ctx;
110  return true;
111 }
112 
113 static void disable_SSL_compression(void) {
114  STACK_OF(SSL_COMP) *ssl_comp_methods;
115  ssl_comp_methods = SSL_COMP_get_compression_methods();
116  int n = sk_SSL_COMP_num(ssl_comp_methods);
117  for (int i = 0; i < n; i++) {
118  (void) sk_SSL_COMP_pop(ssl_comp_methods);
119  }
120 }
121 
122 static void disable_known_bad_ciphers(SSL_CTX *ctx) {
123  const char CIPHER_LIST_CFG[] =
124  "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:"
125  "ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS";
126  int res = SSL_CTX_set_cipher_list(ctx, CIPHER_LIST_CFG);
127  assert(res == 1);
128 }
129 
130 static bool do_blocking_connection(struct bus *b, SSL *ssl, int fd) {
132  "SSL_Connect handshake for socket %d", fd);
133 
134  struct pollfd fds[1];
135  fds[0].fd = fd;
136  fds[0].events = POLLOUT;
137 
138  bool connected = false;
139  size_t elapsed = 0;
140 
141  while (!connected) {
142  int pres = syscall_poll(fds, 1, TIMEOUT_MSEC);
144  "SSL_Connect handshake for socket %d, poll res %d", fd, pres);
145 
146  if (pres < 0) {
147  if (Util_IsResumableIOError(errno)) {
148  errno = 0;
149  } else {
150  /* */
151  assert(false);
152  }
153  } else if (pres > 0) {
154  if (fds[0].revents & (POLLOUT | POLLIN)) {
155  int connect_res = SSL_connect(ssl);
157  "socket %d: connect_res %d", fd, connect_res);
158 
159  if (connect_res == 1) {
161  "socket %d: successfully connected", fd);
162  connected = true;
163  } else if (connect_res < 0) {
164  int reason = SSL_get_error(ssl, connect_res);
165 
166  switch (reason) {
167  case SSL_ERROR_WANT_WRITE:
168  BUS_LOG(b, 4, LOG_SOCKET_REGISTERED, "WANT_WRITE", b->udata);
169  fds[0].events = POLLOUT;
170  break;
171 
172  case SSL_ERROR_WANT_READ:
173  BUS_LOG(b, 4, LOG_SOCKET_REGISTERED, "WANT_READ", b->udata);
174  fds[0].events = POLLIN;
175  break;
176 
177  case SSL_ERROR_SYSCALL:
178  {
179  if (Util_IsResumableIOError(errno)) {
180  errno = 0;
181  break;
182  } else {
183  unsigned long errval = ERR_get_error();
184  char ebuf[256];
186  "socket %d: ERROR -- %s", fd, ERR_error_string(errval, ebuf));
187  }
188  }
189  break;
190  default:
191  {
192  unsigned long errval = ERR_get_error();
193  char ebuf[256];
195  "socket %d: ERROR %d -- %s", fd, reason, ERR_error_string(errval, ebuf));
196  assert(false);
197  }
198  }
199 
200  } else {
202  "socket %d: unknown state, setting event bask to (POLLIN | POLLOUT)",
203  fd);
204  fds[0].events = (POLLIN | POLLOUT);
205  }
206  } else if (fds[0].revents & POLLHUP) {
208  "SSL_Connect: HUP on %d", fd);
209  return false;
210  } else if (fds[0].revents & POLLERR) {
212  "SSL_Connect: ERR on %d", fd);
213  return false;
214  }
215  } else {
216  BUS_LOG(b, 4, LOG_SOCKET_REGISTERED, "poll timeout", b->udata);
217  elapsed += TIMEOUT_MSEC;
218  if (elapsed > MAX_TIMEOUT) {
219  BUS_LOG(b, 2, LOG_SOCKET_REGISTERED, "timed out", b->udata);
220  return false;
221  }
222  }
223  }
224 
225  return connected;
226 }
bool Util_IsResumableIOError(int errno_)
Definition: util.c:26
static bool do_blocking_connection(struct bus *b, SSL *ssl, int fd)
Definition: bus_ssl.c:130
bool BusSSL_Init(struct bus *b)
Initialize the SSL library internals for use by the messaging bus.
Definition: bus_ssl.c:36
void BusSSL_CtxFree(struct bus *b)
Free all internal data for using SSL (the SSL_CTX).
Definition: bus_ssl.c:80
#define TIMEOUT_MSEC
Definition: bus_ssl.c:27
Message bus.
bool BusSSL_Disconnect(struct bus *b, SSL *ssl)
Disconnect and free an individual SSL handle.
Definition: bus_ssl.c:73
void * udata
User data for callbacks.
#define MAX_TIMEOUT
Definition: bus_ssl.c:28
static void disable_SSL_compression(void)
Definition: bus_ssl.c:113
SSL_CTX * ssl_ctx
SSL context.
static bool init_client_SSL_CTX(SSL_CTX **ctx_out)
Definition: bus_ssl.c:87
#define BUS_LOG_SNPRINTF(B, LEVEL, EVENT_KEY, UDATA, MAX_SZ, FMT,...)
Definition: bus_types.h:59
static void disable_known_bad_ciphers(SSL_CTX *ctx)
Definition: bus_ssl.c:122
SSL * BusSSL_Connect(struct bus *b, int fd)
Do an SSL / TLS shake for a connection.
Definition: bus_ssl.c:51
int syscall_poll(struct pollfd fds[], nfds_t nfds, int timeout)
Wrappers for syscalls, to allow mocking for testing.
Definition: syscall.c:27
#define BUS_LOG(B, LEVEL, EVENT_KEY, MSG, UDATA)
Definition: bus_types.h:45