My Project
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros
sslops.c
Go to the documentation of this file.
1 /*
2 START OF LICENSE STUB
3  DeDOS: Declarative Dispersion-Oriented Software
4  Copyright (C) 2017 University of Pennsylvania, Georgetown University
5 
6  This program is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18 END OF LICENSE STUB
19 */
20 #include "webserver/sslops.h"
21 #include "webserver/webserver.h"
22 #include "logging.h"
23 #include <pthread.h>
24 #include <openssl/err.h>
25 
26 #ifdef __GNUC__
27 #define UNUSED __attribute__ ((unused))
28 #else
29 #define UNUSED
30 #endif
31 
32 //#define PRINT_REQUEST_ERRORS
33 
34 #ifdef PRINT_REQUEST_ERRORS
35 #define print_request_error(fmt, ...)\
36  log_error(fmt, ##__VA_ARGS__);
37 #else
38 #define print_request_error(...)
39 #endif
40 
44 //FIXME: calling ERR_get_error in the case SSL_ERROR_SSL breaks consistency of this function
45 // (which otherwise reads the stack and doesn't pop it)
46 #ifdef PRINT_REQUEST_ERRORS
47 #define print_ssl_error(ssl, rtn) { \
48  int _err = SSL_get_error(ssl, rtn); \
49  const char* UNUSED error_str = ERR_error_string(_err, NULL); \
50  switch (_err) { \
51  case SSL_ERROR_ZERO_RETURN: { \
52  print_request_error("SSL_ERROR_ZERO_RETURN (%d): %s\n", _err, error_str); \
53  break; \
54  } \
55  case SSL_ERROR_WANT_READ: { \
56  print_request_error("SSL_ERROR_WANT_READ (%d): %s\n", _err, error_str);\
57  break;\
58  }\
59  case SSL_ERROR_WANT_WRITE: {\
60  print_request_error("SSL_ERROR_WANT_WRITE (%d): %s\n", _err, error_str);\
61  break;\
62  }\
63  case SSL_ERROR_WANT_CONNECT: {\
64  print_request_error("SSL_ERROR_WANT_CONNECT (%d): %s\n", _err, error_str);\
65  break;\
66  }\
67  case SSL_ERROR_WANT_ACCEPT: {\
68  print_request_error("SSL_ERROR_WANT_ACCEPT (%d): %s\n", _err, error_str);\
69  break;\
70  }\
71  case SSL_ERROR_WANT_X509_LOOKUP: {\
72  print_request_error("SSL_ERROR_WANT_X509_LOOKUP (%d): %s\n", _err, error_str);\
73  break;\
74  }\
75  case SSL_ERROR_SYSCALL: {\
76  print_request_error("SSL_ERROR_SYSCALL (%d): %s\n", _err, error_str);\
77  break;\
78  }\
79  case SSL_ERROR_SSL: {\
80  unsigned long UNUSED err2 = ERR_get_error(); \
81  print_request_error("SSL_ERROR_SSL (%d, %lx): %s\n", _err, err2, error_str);\
82  break;\
83  }\
84  default:\
85  break;\
86  }\
87 }
88 #else
89 #define print_ssl_error(ssl, rtn)
90 #endif
91 
92 static pthread_mutex_t *lockarray;
93 
94 static void crypto_locking_callback(int mode, int n,
95  const char UNUSED *file, int UNUSED line) {
96  if (mode & CRYPTO_LOCK) {
97  pthread_mutex_lock(&(lockarray[n]));
98  } else {
99  pthread_mutex_unlock(&(lockarray[n]));
100  }
101 }
102 
103 static unsigned long crypto_id_function(void) {
104  return (unsigned long)pthread_self();
105 }
106 
107 void init_ssl_locks(void) {
108 #if defined(OPENSSL_THREADS)
109 #else
110  log_error("no OPENSSL thread support!");
111 #endif
112  lockarray = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(*lockarray));
113  for (int i=0; i < CRYPTO_num_locks(); i++) {
114  pthread_mutex_init(&(lockarray[i]), NULL);
115  }
116  CRYPTO_set_id_callback(crypto_id_function);
117  CRYPTO_set_locking_callback(crypto_locking_callback);
118 }
119 
120 SSL_CTX *ssl_ctx = NULL;
121 
122 void kill_ssl_locks(void) {
123  CRYPTO_set_locking_callback(NULL);
124  for (int i=0; i<CRYPTO_num_locks(); i++) {
125  pthread_mutex_destroy(&(lockarray[i]));
126  }
127  if (ssl_ctx != NULL) {
128  SSL_CTX_free(ssl_ctx);
129  ssl_ctx = NULL;
130  }
131 }
132 
133 
134 int init_ssl_context(void) {
135  if (ssl_ctx != NULL) {
136  log_warn("SSL context already initialized. Not initializing again.");
137  return 1;
138  }
139 
140  SSL_library_init();
141  OpenSSL_add_all_algorithms();
142  SSL_load_error_strings();
143 
144  /* Create new SSL context */
145  const SSL_METHOD *method = TLSv1_2_server_method();
146  ssl_ctx = SSL_CTX_new(method);
147  SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_OFF);
148  SSL_CTX_set_options(ssl_ctx,
149  SSL_OP_NO_SSLv3 | SSL_OP_NO_TICKET | SSL_OP_SINGLE_DH_USE);
150 
151  int rtn = SSL_CTX_set_cipher_list(ssl_ctx, "HIGH:!aNULL:!MD5:!RC4");
152  if ( rtn <= 0 ) {
153  log_error("Could not set cipger list for SSL context");
154  return -1;
155  }
156 
157  return 0;
158 }
159 
160 int load_ssl_certificate(char *cert_file, char *key_file) {
161  /* set the local certificate from CertFile */
162  if ( SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) <= 0 ) {
163  return -1;
164  }
165  /* set the private key from KeyFile (may be the same as CertFile) */
166  if ( SSL_CTX_use_PrivateKey_file(ssl_ctx, cert_file, SSL_FILETYPE_PEM) <= 0 ) {
167  return -1;
168  }
169  /* verify private key */
170  if ( !SSL_CTX_check_private_key(ssl_ctx) ) {
171  return -1;
172  }
173  return 0;
174 }
175 
176 SSL *init_ssl_connection(int fd) {
177  SSL *ssl = SSL_new(ssl_ctx);
178  SSL_set_fd(ssl, fd);
179  return ssl;
180 }
181 
182 #define CHECK_SSL_WANTS(ssl, rtn) \
183  do { \
184  int err = SSL_get_error(ssl, rtn); \
185  if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { \
186  print_ssl_error(ssl, rtn); \
187  return WS_ERROR; \
188  } else if ( err == SSL_ERROR_WANT_READ) { \
189  return WS_INCOMPLETE_READ; \
190  } else if ( err == SSL_ERROR_WANT_WRITE) { \
191  return WS_INCOMPLETE_WRITE; \
192  } \
193  } while (0);
194 
195 int accept_ssl(SSL *ssl) {
196  ERR_clear_error();
197  int rtn = SSL_accept(ssl);
198  if (rtn == 0) {
199  log_warn("The TLS/SSL handshake was not successful "
200  "but was shut down controlled and by the "
201  "specifications of the TLS/SSL protocol");
202  print_ssl_error(ssl, rtn);
203  return -1;
204  }
205  if (rtn < 0) {
206  CHECK_SSL_WANTS(ssl, rtn);
207  }
208  return WS_COMPLETE;
209 }
210 
211 int read_ssl(SSL *ssl, char *buf, int *buf_size) {
212  ERR_clear_error();
213  int num_bytes = SSL_read(ssl, buf, *buf_size);
214  if (num_bytes > 0) {
215  *buf_size = num_bytes;
216  return WS_COMPLETE;
217  } else {
218  CHECK_SSL_WANTS(ssl, num_bytes);
219  }
220  log_warn("Shouldn't ever get here...");
221  return WS_ERROR;
222 }
223 
224 int write_ssl(SSL *ssl, char *buf, int *buf_size) {
225  ERR_clear_error();
226  int num_bytes = SSL_write(ssl, buf, *buf_size);
227  if (num_bytes > 0) {
228  if (num_bytes != *buf_size) {
229  log_warn("Didn't write the requested amount (written: %d, requested: %d)...",
230  num_bytes, *buf_size);
231  }
232  *buf_size = num_bytes;
233  return WS_COMPLETE;
234  } else {
235  CHECK_SSL_WANTS(ssl, num_bytes);
236  }
237  log_warn("Shouldn't ever get here...");
238  return WS_ERROR;
239 }
240 
241 int close_ssl(SSL *ssl) {
242  ERR_clear_error();
243  int rtn = SSL_get_shutdown(ssl);
244  if (rtn >= 0) {
245  rtn = SSL_shutdown(ssl);
246  if (rtn == -1) {
247  log(WS_ERROR, "Error shutting down SSL connection");
248  SSL_free(ssl);
249  return WS_ERROR;
250  }
251  if (rtn == 0) {
252  //See the man page to understand this way of handling error on SSL_shutdown()
253  //TODO: need too handle WANT_READ and WANT_WRITE on BOTH calls
254  //see https://stackoverflow.com/questions/28056056/handling-ssl-shutdown-correctly
255  rtn = SSL_shutdown(ssl);
256  if (rtn == -1) {
257  // log_error("Error shutting down SSL connection");
258  SSL_free(ssl);
259  return WS_ERROR;
260  }
261  }
262  }
263 
264  SSL_free(ssl);
265  return WS_COMPLETE;
266 }
void kill_ssl_locks(void)
Definition: sslops.c:122
void init_ssl_locks(void)
Definition: sslops.c:107
SSL * init_ssl_connection(int fd)
Definition: sslops.c:176
int accept_ssl(SSL *ssl)
Definition: sslops.c:195
#define UNUSED
Definition: sslops.c:29
int read_ssl(SSL *ssl, char *buf, int *buf_size)
Definition: sslops.c:211
Logging of status messages to the terminal.
int load_ssl_certificate(char *cert_file, char *key_file)
Definition: sslops.c:160
#define log_error(fmt,...)
Definition: logging.h:101
SSL_CTX * ssl_ctx
Definition: sslops.c:120
#define print_ssl_error(ssl, rtn)
Explains an SSL error.
Definition: sslops.c:89
static unsigned long crypto_id_function(void)
Definition: sslops.c:103
int init_ssl_context(void)
Definition: sslops.c:134
int write_ssl(SSL *ssl, char *buf, int *buf_size)
Definition: sslops.c:224
#define WS_COMPLETE
Definition: webserver.h:24
#define CHECK_SSL_WANTS(ssl, rtn)
Definition: sslops.c:182
#define WS_ERROR
Definition: webserver.h:27
#define log(level, fmt,...)
Log at a custom level.
Definition: logging.h:147
static pthread_mutex_t * lockarray
Definition: sslops.c:92
#define log_warn(fmt,...)
Definition: logging.h:113
static void crypto_locking_callback(int mode, int n, const char *file, int line)
Definition: sslops.c:94
int close_ssl(SSL *ssl)
Definition: sslops.c:241