MinitScript  0.9.31 PRE-BETA
SecureTCPSocket.cpp
Go to the documentation of this file.
2 
3 #include <openssl/x509.h>
4 #include <openssl/ssl.h>
5 #include <openssl/err.h>
6 #include <openssl/pem.h>
7 #include <openssl/rand.h>
8 #include <openssl/ocsp.h>
9 #include <openssl/bn.h>
10 #ifndef OPENSSL_NO_CT
11 #include <openssl/ct.h>
12 #endif
13 
14 #include <string>
15 
24 
26 
27 using std::string;
28 using std::to_string;
29 
37 
38 // see: https://wiki.openssl.org/index.php/SSL/TLS_Client
39 
40 SecureTCPSocket::SecureTCPSocket() {
41 }
42 
44 }
45 
46 size_t SecureTCPSocket::read(void* buf, const size_t bytes) {
47  auto bytesRead = BIO_read(bio, buf, bytes);
48  if (bytesRead == -1) {
49  throw NetworkIOException("Error while reading from socket: " + openSSLGetErrors());
50  } else
51  if (bytesRead == 0) {
52  throw NetworkSocketClosedException("end of stream");
53  }
54  //
55  return (size_t)bytesRead;
56 }
57 
58 size_t SecureTCPSocket::write(void* buf, const size_t bytes) {
59  auto bytesWritten = BIO_write(bio, buf, bytes);
60  if (bytesWritten == -1) {
61  throw NetworkIOException("Error while writing to socket: " + openSSLGetErrors());
62  }
63  //
64  return (size_t)bytesWritten;
65 }
66 
67 void SecureTCPSocket::connect(const string& hostname, const unsigned int port) {
68  // set address
69  this->ip = Network::getIpByHostname(hostname);
70  this->port = port;
71  //
72  const SSL_METHOD* method = SSLv23_method();
73  if (!(nullptr != method))
74  throw NetworkSocketException("Could not connect socket: " + openSSLGetErrors());
75 
76  ctx = SSL_CTX_new(method);
77  if (!(ctx != nullptr))
78  throw NetworkSocketException("Could not connect socket: " + openSSLGetErrors());
79 
80  /* Cannot fail ??? */
81  SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, openSSLVerifyCallback);
82 
83  /* Cannot fail ??? */
84  SSL_CTX_set_verify_depth(ctx, 4);
85 
86  /* Cannot fail ??? */
87  const long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
88  SSL_CTX_set_options(ctx, flags);
89 
90  //
91  long result = 1;
92  // see: https://stackoverflow.com/questions/59017890/where-is-the-certificate-file-for-ssl-ctx-load-verify-locations-in-openssl-locat
93  result = SSL_CTX_load_verify_locations(ctx, (MINITSCRIPT_DATA + "/resources/certs/cacert-2023-08-22.pem").c_str() /* truststore */, "resources/certs");
94  if (!(1 == result))
95  throw NetworkSocketException("Could not connect socket: " + openSSLGetErrors());
96 
97  bio = BIO_new_ssl_connect(ctx);
98  if (!(bio != nullptr))
99  throw NetworkSocketException("Could not connect socket: " + openSSLGetErrors());
100 
101  result = BIO_set_conn_hostname(bio, string(hostname + ":" + to_string(port)).c_str());
102  if (!(1 == result))
103  throw NetworkSocketException("Could not connect socket: " + openSSLGetErrors());
104 
105  BIO_get_ssl(bio, &ssl);
106  if (!(ssl != nullptr))
107  throw NetworkSocketException("Could not connect socket: " + openSSLGetErrors());
108 
109  const char PREFERRED_CIPHERS[] = "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4";
110  result = SSL_set_cipher_list(ssl, PREFERRED_CIPHERS);
111  if (!(1 == result))
112  throw NetworkSocketException("Could not connect socket: " + openSSLGetErrors());
113 
114  result = SSL_set_tlsext_host_name(ssl, hostname.c_str());
115  if (!(1 == result))
116  throw NetworkSocketException("Could not connect socket: " + openSSLGetErrors());
117 
118  out = BIO_new_fp(stdout, BIO_NOCLOSE);
119  if (!(nullptr != out))
120  throw NetworkSocketException("Could not connect socket: " + openSSLGetErrors());
121 
122  result = BIO_do_connect(bio);
123  if (!(1 == result))
124  throw NetworkSocketException("Could not connect socket: " + openSSLGetErrors());
125 
126  result = BIO_do_handshake(bio);
127  if (!(1 == result))
128  throw NetworkSocketException("Could not connect socket: " + openSSLGetErrors());
129 
130  /*
131  // TODO: the symbol "SSL_get_peer_certificate" is missing on StarFive VisionFive2 default Debian
132  // also looks like we dont need this check here, as SecureTCPSocket does still work without it
133  // we can improve this later
134  // Step 1: verify a server certificate was presented during the negotiation
135  X509* cert = SSL_get_peer_certificate(ssl);
136  if (cert) {
137  X509_free(cert);
138  } // Free immediately
139  if (nullptr == cert)
140  throw NetworkSocketException("Could not connect socket: " + openSSLGetErrors());
141  */
142 
143  /* Step 2: verify the result of chain verification */
144  /* Verification performed according to RFC 4158 */
145  result = SSL_get_verify_result(ssl);
146  if (!(X509_V_OK == result))
147  throw NetworkSocketException("Could not connect socket: " + openSSLGetErrors());
148 }
149 
151  if (bio != nullptr) BIO_free_all(bio);
152  if (out != nullptr) BIO_free_all(out);
153  if (ctx != nullptr) SSL_CTX_free(ctx);
154  bio = nullptr;
155  out = nullptr;
156  ctx = nullptr;
157 }
158 
160  if (bio != nullptr) BIO_free_all(bio);
161  if (out != nullptr) BIO_free_all(out);
162  if (ctx != nullptr) SSL_CTX_free(ctx);
163  bio = nullptr;
164  out = nullptr;
165  ctx = nullptr;
166 }
167 
168 int SecureTCPSocket::openSSLVerifyCallback(int preverify, X509_STORE_CTX *x509_ctx) {
169  int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
170  int err = X509_STORE_CTX_get_error(x509_ctx);
171  X509 *cert = X509_STORE_CTX_get_current_cert(x509_ctx);
172  X509_NAME *iname = cert ? X509_get_issuer_name(cert) : nullptr;
173  X509_NAME *sname = cert ? X509_get_subject_name(cert) : nullptr;
174  /*
175  print_cn_name("Issuer (cn)", iname);
176  print_cn_name("Subject (cn)", sname);
177  */
178  if (depth == 0) {
179  /* If depth is 0, its the server's certificate. Print the SANs too */
180  /*
181  print_san_name("Subject (san)", cert);
182  */
183  }
184  return preverify;
185 }
186 
188  string result;
189  int err;
190  while ((err = ERR_get_error()) != 0) {
191  auto errorMessage = ERR_error_string(err, 0);
192  if (errorMessage == nullptr) return result;
193  result+= string(errorMessage) + "\n";
194  }
195  return result;
196 }
Base exception class for network IO exceptions.
Base class of network sockets.
Definition: NetworkSocket.h:17
static const string getIpByHostname(const string &hostname)
Get IP by hostname.
Definition: Network.cpp:51
Class representing a secure TCP socket.
static int openSSLVerifyCallback(int preverify, X509_STORE_CTX *x509_ctx)
OpenSSL verify callback.
size_t read(void *buf, const size_t bytes)
Reads up to "bytes" bytes from socket.
size_t write(void *buf, const size_t bytes)
Writes up to "bytes" bytes to socket.
virtual void close()
Closes the socket.
virtual void shutdown()
shuts socket down for reading and writing
void connect(const string &hostname, const unsigned int port)
Connects a socket to given remote IP and port.
virtual ~SecureTCPSocket()
Public destructor.
Mutex implementation.
Definition: Mutex.h:19
#define MINITSCRIPT_DATA
Definition: minitscript.h:22