ThingWorx C SDK
twOpenSSL.h
Go to the documentation of this file.
1 /***************************************
2  * Copyright (C) 2015 ThingWorx Inc. *
3  ***************************************/
4 
10 #ifndef TW_OPENSSL_H
11 #define TW_OPENSSL_H
12 
13 #include "twOSPort.h"
14 #include "twLogger.h"
15 #include "stdio.h"
16 #include "string.h"
17 #include "stringUtils.h"
18 
19 #include <openssl/bio.h>
20 #include <openssl/ssl.h>
21 #include <openssl/err.h>
22 #include <openssl/pem.h>
23 #include <openssl/x509.h>
24 #include <openssl/x509_vfy.h>
25 #include <openssl/sha.h>
26 #include <openssl/md5.h>
27 
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31 
32 #define TW_SSL_CTX SSL_CTX
33 #define TW_SSL SSL
34 #define TW_SSL_SESSION_ID_SIZE sizeof(void *)
35 #define TW_SSL_SESSION_ID(a) SSL_get1_session(a)
36 #define TW_GET_CERT_SIZE ssl_get_config(SSL_MAX_CERT_CFG_OFFSET)
37 #define TW_GET_CA_CERT_SIZE ssl_get_config(SSL_MAX_CA_CERT_CFG_OFFSET)
38 #define TW_HANDSHAKE_SUCCEEDED(a) (a && SSL_get_state(a) == SSL_ST_OK)
39 #define TW_SSL_FREE(a) SSL_free(a)
40 #define TW_SSL_CTX_FREE(a) SSL_CTX_free(a)
41 #define TW_USE_CERT_FILE(a,b,c) SSL_CTX_use_PrivateKey_file(a, b, SSL_FILETYPE_PEM)
42 #define TW_USE_CERT_CHAIN_FILE(a,b,c) SSL_CTX_load_verify_locations(a, b, NULL)
43 #define TW_SET_CLIENT_CA_LIST(a,b) SSL_CTX_use_certificate_chain_file(a,b)
44 #define DATA_AVAILABLE(a,b,c) (twSocket_WaitFor(a, b) || SSL_pending(c))
45 
46 /*********
47 * Neither SHA1 nor MD5 are FIPS approved, but we don't use them
48 * for any crypto/communications functions, however the FIPS library
49 * will exit the program if we try to use them.
50 **********/
51 #if TLS != FIPS
52 #define TW_SHA1_CTX SHA_CTX
53 #define TW_SHA1_INIT(a) SHA1_Init(a)
54 #define TW_SHA1_UPDATE(a,b,c) SHA1_Update(a,b,c)
55 #define TW_SHA1_FINAL(a,b) SHA1_Final(a,b)
56 
57 #define TW_MD5_CTX MD5_CTX
58 #define TW_MD5_INIT(a) MD5_Init(a)
59 #define TW_MD5_UPDATE(a,b,c) MD5_Update(a,b,c)
60 #define TW_MD5_FINAL(a,b) MD5_Final(a,b)
61 #endif
62 
63 static char fips_enabled = FALSE;
72 static INLINE int TW_ENABLE_FIPS_MODE(TW_SSL_CTX * ctx) {
73  int ret = 0;
74  if (!fips_enabled) {
75  ret = FIPS_mode_set(1); /* FIPS mode *on*/
76  if(ret != 1) {
77  TW_LOG(TW_ERROR, "TW_NEW_SSL_CTX_FUNC: FIPS_mode_set failed: %s.", ERR_error_string(ERR_get_error(), NULL));
78  fips_enabled = FALSE;
79  return TW_ENABLE_FIPS_MODE_FAILED;
80  }
81  fips_enabled = TRUE;
82  }
83  return TW_OK;
84 }
85 
102 static INLINE TW_SSL * TW_NEW_SSL_CLIENT(TW_SSL_CTX * ctx, twSocket * sock, void * session_id, int session_size) {
103  TW_SSL * ssl = NULL;
104  BIO * bio = NULL;
105  signed int res = 0;
106  ssl=SSL_new(ctx);
107  if (!ssl) {
108  TW_LOG(TW_ERROR, "TW_NEW_SSL_CLIENT: Error creating SSL session. Error: %s", ERR_error_string(ERR_get_error(), NULL));
109  return NULL;
110  }
111  if (session_id) SSL_set_session(ssl, (SSL_SESSION *)session_id);
112  bio=BIO_new_socket(sock->sock,BIO_NOCLOSE);
113  if (!bio) {
114  TW_LOG(TW_ERROR, "TW_NEW_SSL_CLIENT: Error creating SSL BIO. Error: %s.", ERR_error_string(ERR_get_error(), NULL));
115  TW_SSL_FREE(ssl);
116  return NULL;
117  }
118  SSL_set_bio(ssl,bio,bio);
119  res = SSL_connect(ssl);
120  if ( res != 1 ) {
121  const char * tmp = NULL;
122  int index = 0;
123  TW_LOG(TW_ERROR,"TW_NEW_SSL_CLIENT: SSL handshake error. Error: %s.", ERR_error_string(ERR_get_error(), NULL));
124  do {
125  tmp = SSL_get_cipher_list(ssl,index);
126  if (tmp != NULL) {
127  TW_LOG(TW_TRACE,"TW_NEW_SSL_CLIENT: Ciphers Supported: %s", tmp);
128  index++;
129  }
130  }
131  while (tmp != NULL);
132  TW_SSL_FREE(ssl);
133  return NULL;
134  }
135  return ssl;
136 }
137 
138 extern unsigned char default_private_key[];
139 extern int default_private_key_len;
140 extern unsigned char default_certificate[];
141 extern int default_certificate_len;
142 
143 static const unsigned char s_server_session_id_context[SSL_MAX_SSL_SESSION_ID_LENGTH] = {""};
157 static INLINE TW_SSL * TW_NEW_SERVER(TW_SSL_CTX * ctx, twSocket * sock) {
158  TW_SSL * ssl = NULL;
159  BIO * bio = NULL;
160  RSA * r = NULL;
161  X509 * x = NULL;
162  unsigned char * p = NULL;
163  if (!ctx || !sock) return NULL;
164  /* We need to load a default cert and key */
165  p = default_private_key;
166  r = d2i_RSAPrivateKey(NULL, (const unsigned char **)&p, default_private_key_len);
167  p = default_certificate;
168  x = d2i_X509(NULL, (const unsigned char **)&p, default_certificate_len);
169  SSL_CTX_use_RSAPrivateKey(ctx, r);
170  SSL_CTX_use_certificate(ctx, x);
171  SSL_CTX_set_session_id_context(ctx, s_server_session_id_context, sizeof s_server_session_id_context);
172  ssl=SSL_new(ctx);
173  if (!ssl) {
174  TW_LOG(TW_ERROR, "TW_NEW_SERVER: Error creating SSL session. Error: %s", ERR_error_string(ERR_get_error(), NULL));
175  return NULL;
176  }
177  bio=BIO_new_socket(sock->sock,BIO_NOCLOSE);
178  if (!bio) {
179  TW_LOG(TW_ERROR, "TW_NEW_SERVER: Error creating SSL BIO. Error: %s.", ERR_error_string(ERR_get_error(), NULL));
180  TW_SSL_FREE(ssl);
181  return NULL;
182  }
183  SSL_set_bio(ssl,bio,bio);
184  return ssl;
185 }
186 
195 static INLINE int TW_SSL_ACCEPT(TW_SSL * s) {
196  int res = SSL_accept(s);
197  if ( res != 1 ) {
198  const char * tmp = NULL;
199  int index = 0;
200  TW_LOG(TW_ERROR,"TW_SSL_ACCEPT: SSL handshake error. Error: %s.", ERR_error_string(ERR_get_error(), NULL));
201  do {
202  tmp = SSL_get_cipher_list(s,index);
203  if (tmp != NULL) {
204  TW_LOG(TW_TRACE," Ciphers Supported: %s", tmp);
205  index++;
206  }
207  }
208  while (tmp != NULL);
209  return -1;
210  }
211  return 0;
212 }
213 
226 static INLINE int TW_USE_KEY_FILE(SSL_CTX * ctx, const char * file, int type, char * passwd) {
227  SSL_CTX_set_default_passwd_cb_userdata(ctx, passwd);
228  if (SSL_CTX_use_PrivateKey_file(ctx, file, type) <= 0) {
229  TW_LOG(TW_ERROR, "TW_USE_KEY_FILE: Error setting the key file.");
230  return -1;
231  }
232  return 0;
233 }
234 
244 static INLINE SSL_CTX * TW_NEW_SSL_CTX_FUNC() {
245  SSL_CTX * ctx = NULL;
246  if (SSL_library_init() != 1) {
247  TW_LOG(TW_ERROR, "TW_NEW_SSL_CTX_FUNC: Error initializing OpenSSL library: %lx.", ERR_get_error());
248  return NULL;
249  }
250  OpenSSL_add_all_algorithms(); /* load encryption & hash algorithms for SSL */
251  SSL_load_error_strings(); /* load the error strings for good error reporting */
252  ERR_load_crypto_strings();
253  ctx = SSL_CTX_new(SSLv23_method());
254  // Limit to TLS only
255  if (ctx) SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
256  return ctx;
257 }
258 #define TW_NEW_SSL_CTX TW_NEW_SSL_CTX_FUNC()
259 
274 static INLINE int TW_SSL_READ(TW_SSL * ssl, char * buf, int len, int32_t timeout) {
275  int32_t ret = SSL_ERROR_NONE;
276  int32_t retries = 0;
277  // Loop until we are unblocked or timeout
278  DATETIME start = twGetSystemTime(TRUE);
279  if (!ssl || !buf) return -1;
280  while (twTimeLessThan(twGetSystemTime(TRUE), twAddMilliseconds(start,twcfg.default_message_timeout)) && retries < 3) {
281  if(!ssl) break;
282  ret = SSL_read(ssl, buf, len);
283  switch (SSL_get_error(ssl,ret)) {
284  case SSL_ERROR_NONE:
285  break;
286  case SSL_ERROR_WANT_WRITE:
287  case SSL_ERROR_WANT_READ:
288  case SSL_ERROR_WANT_X509_LOOKUP:
289  ret = -1;
290  TW_LOG(TW_INFO, "TW_SSL_READ: Read BLOCK on try %d. Error: %s", retries, ERR_error_string(ret, NULL));
291  retries++;
292  twSleepMsec(5);
293  continue;
294  case SSL_ERROR_SYSCALL:
295  case SSL_ERROR_SSL:
296  TW_LOG(TW_ERROR, "TW_SSL_READ: Error reading from SSL stream");
297  break;
298  case SSL_ERROR_ZERO_RETURN:
299  TW_LOG(TW_TRACE, "TW_SSL_READ: Read 0 bytes");
300  break;
301  }
302  break;
303  }
304 
305  // Check for a timeout or error
306  if (ret <= 0 || twTimeGreaterThan(twGetSystemTime(TRUE), twAddMilliseconds(start,twcfg.default_message_timeout))) {
307  TW_LOG(TW_ERROR, "TW_SSL_READ: Timed out or error waiting reading from socket. Error: %s", ERR_error_string(ret, NULL));
308  return TW_TIMEOUT_READING_FROM_SOCKET;
309  }
310  return ret;
311 }
312 
326 static INLINE int TW_SSL_WRITE(TW_SSL * ssl, char * buf, int len) {
327  int retries = 0;
328  int result = 0;
329  while (retries < 3) {
330  result = SSL_write(ssl, buf, len);
331  switch (SSL_get_error(ssl,result)) {
332  case SSL_ERROR_NONE:
333  break;
334  case SSL_ERROR_WANT_WRITE:
335  case SSL_ERROR_WANT_READ:
336  case SSL_ERROR_WANT_X509_LOOKUP:
337  result = 0;
338  retries++;
339  TW_LOG(TW_ERROR, "TW_SSL_WRITE: Write BLOCK. Retry %d in 5 msec", retries);
340  twSleepMsec(5);
341  continue;
342  case SSL_ERROR_SYSCALL:
343  case SSL_ERROR_SSL:
344  TW_LOG(TW_ERROR, "TW_SSL_WRITE: Write failed. Error: %s", ERR_error_string(result, NULL));
345  break;
346  case SSL_ERROR_ZERO_RETURN:
347  TW_LOG(TW_WARN,"TW_SSL_WRITE: Zero bytes written.");
348  break;
349  }
350  break;
351  }
352  return result;
353 }
354 
363 static INLINE int TW_VALIDATE_CERT(TW_SSL * ssl, char selfSignedOk) {
364  int32_t res = SSL_get_verify_result(ssl);
365  if( res != X509_V_OK && !(selfSignedOk && (res == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || res == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN))) {
366  TW_LOG(TW_ERROR, "TW_VALIDATE_CERT: Certificate rejected. Code: %d, Reason = %s",res, X509_verify_cert_error_string(res));
367  return TW_INVALID_SSL_CERT;
368  } else {
369  return 0;
370  }
371 }
372 
385 static INLINE char * TW_GET_X509_FIELD(TW_SSL * ssl, char field) {
386  /* Caller will own the returned pointer */
387  int nid = 0;
388  char tmp[256];
389  X509 * cert = NULL;
390  X509_NAME * name = NULL;
391  if (!ssl) return NULL;
392  cert = SSL_get_peer_certificate(ssl);
393  if (!cert) return NULL;
394  switch (field) {
395  case TW_SUBJECT_CN:
396  nid = OBJ_txt2nid("CN");
397  name = X509_get_subject_name(cert);
398  X509_NAME_get_text_by_NID(name, nid, tmp, 255);
399  break;
400  case TW_SUBJECT_O:
401  nid = OBJ_txt2nid("O");
402  name = X509_get_subject_name(cert);
403  X509_NAME_get_text_by_NID(name, nid, tmp, 255);
404  break;
405  case TW_SUBJECT_OU:
406  nid = OBJ_txt2nid("OU");
407  name = X509_get_subject_name(cert);
408  X509_NAME_get_text_by_NID(name, nid, tmp, 255);
409  break;
410  case TW_ISSUER_CN:
411  nid = OBJ_txt2nid("CN");
412  name = X509_get_issuer_name(cert);
413  X509_NAME_get_text_by_NID(name, nid, tmp, 255);
414  break;
415  case TW_ISSUER_O:
416  nid = OBJ_txt2nid("O");
417  name = X509_get_issuer_name(cert);
418  X509_NAME_get_text_by_NID(name, nid, tmp, 255);
419  break;
420  case TW_ISSUER_OU:
421  nid = OBJ_txt2nid("OU");
422  name = X509_get_issuer_name(cert);
423  X509_NAME_get_text_by_NID(name, nid, tmp, 255);
424  break;
425  default:
426  tmp[0] = 0x00;
427  }
428  return duplicateString(tmp);
429 }
430 
431 #ifdef __cplusplus
432 }
433 #endif
434 
435 #endif /* TW_OPENSSL_H */
static INLINE TW_SSL * TW_NEW_SSL_CLIENT(TW_SSL_CTX *ctx, twSocket *sock, void *session_id, int session_size)
Creates a new #TW_SSL structure for connection with the specified settings (see SSL_new(), SSL_connect()).
Definition: twOpenSSL.h:102
char twTimeGreaterThan(DATETIME t1, DATETIME t2)
Compares two DATETIME variables to see if one is greater.
Definition: twIos.c:31
DATETIME twGetSystemTime(char utc)
Gets the current system time.
Definition: twIos.c:43
TW_SOCKET_TYPE sock
Definition: twOSPort.h:176
static INLINE int TW_SSL_READ(TW_SSL *ssl, char *buf, int len, int32_t timeout)
Reads len bytes of data from ssl into buf (see SSL_read()).
Definition: twOpenSSL.h:274
String utility function prototypes.
Definition: twLogger.h:30
static INLINE char * TW_GET_X509_FIELD(TW_SSL *ssl, char field)
Gets an X509 field of ssl.
Definition: twOpenSSL.h:385
char twTimeLessThan(DATETIME t1, DATETIME t2)
Compares two DATETIME variables to see if one is smaller.
Definition: twIos.c:35
Definition: twLogger.h:31
twSocket base type definition.
Definition: twOSPort.h:175
Wrappers for OS-specific functionality.
static INLINE TW_SSL * TW_NEW_SERVER(TW_SSL_CTX *ctx, twSocket *sock)
Creates a new #TW_SSL connection structure (see SSL_new()).
Definition: twOpenSSL.h:157
static INLINE int TW_SSL_ACCEPT(TW_SSL *s)
Waits for a #TW_SSL client to initiate a handshake with the server. Wrapper function for SSL_accept()...
Definition: twOpenSSL.h:195
Definition: twLogger.h:27
static INLINE int TW_VALIDATE_CERT(TW_SSL *ssl, char selfSignedOk)
Validates the certificate of ssl.
Definition: twOpenSSL.h:363
static INLINE SSL_CTX * TW_NEW_SSL_CTX_FUNC()
Create a new #SSL_CTX stucture as framework for TLS/SSL enabled functions. Wrapper function for SSL_C...
Definition: twOpenSSL.h:244
uint32_t default_message_timeout
Definition: twDefaultSettings.h:175
static INLINE int TW_ENABLE_FIPS_MODE(TW_SSL_CTX *ctx)
Enables FIPS mode for a twTlsClient.
Definition: twOpenSSL.h:72
Definition: twLogger.h:29
DATETIME twAddMilliseconds(DATETIME t1, int32_t msec)
Adds milliseconds to a DATETIME.
Definition: twIos.c:39
static INLINE int TW_USE_KEY_FILE(SSL_CTX *ctx, const char *file, int type, char *passwd)
Loads the certificate authority cert chain used to validate the server's certificate in file into ctx...
Definition: twOpenSSL.h:226
static INLINE int TW_SSL_WRITE(TW_SSL *ssl, char *buf, int len)
Writes len bytes of data in buf to ssl.
Definition: twOpenSSL.h:326
Structure definitions and function prototypes for the ThingWorx logging facility. ...
char * duplicateString(const char *input)
Copies a string.
Definition: stringUtils.c:38