NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.5: NDN, CCN, CCNx, content centric networks
API Documentation
print_client_tls.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2016, Peter Thorson. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  * * Redistributions of source code must retain the above copyright
7  * notice, this list of conditions and the following disclaimer.
8  * * Redistributions in binary form must reproduce the above copyright
9  * notice, this list of conditions and the following disclaimer in the
10  * documentation and/or other materials provided with the distribution.
11  * * Neither the name of the WebSocket++ Project nor the
12  * names of its contributors may be used to endorse or promote products
13  * derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
29 #include <websocketpp/client.hpp>
30 
31 #include <iostream>
32 
34 typedef websocketpp::lib::shared_ptr<websocketpp::lib::asio::ssl::context> context_ptr;
35 
36 using websocketpp::lib::placeholders::_1;
37 using websocketpp::lib::placeholders::_2;
38 using websocketpp::lib::bind;
39 
41  std::cout << msg->get_payload() << std::endl;
42 }
43 
45 bool verify_subject_alternative_name(const char * hostname, X509 * cert) {
46  STACK_OF(GENERAL_NAME) * san_names = NULL;
47 
48  san_names = (STACK_OF(GENERAL_NAME) *) X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
49  if (san_names == NULL) {
50  return false;
51  }
52 
53  int san_names_count = sk_GENERAL_NAME_num(san_names);
54 
55  bool result = false;
56 
57  for (int i = 0; i < san_names_count; i++) {
58  const GENERAL_NAME * current_name = sk_GENERAL_NAME_value(san_names, i);
59 
60  if (current_name->type != GEN_DNS) {
61  continue;
62  }
63 
64  char * dns_name = (char *) ASN1_STRING_data(current_name->d.dNSName);
65 
66  // Make sure there isn't an embedded NUL character in the DNS name
67  if (ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) {
68  break;
69  }
70  // Compare expected hostname with the CN
71  result = (strcasecmp(hostname, dns_name) == 0);
72  }
73  sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
74 
75  return result;
76 }
77 
79 bool verify_common_name(const char * hostname, X509 * cert) {
80  // Find the position of the CN field in the Subject field of the certificate
81  int common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name(cert), NID_commonName, -1);
82  if (common_name_loc < 0) {
83  return false;
84  }
85 
86  // Extract the CN field
87  X509_NAME_ENTRY * common_name_entry = X509_NAME_get_entry(X509_get_subject_name(cert), common_name_loc);
88  if (common_name_entry == NULL) {
89  return false;
90  }
91 
92  // Convert the CN field to a C string
93  ASN1_STRING * common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
94  if (common_name_asn1 == NULL) {
95  return false;
96  }
97 
98  char * common_name_str = (char *) ASN1_STRING_data(common_name_asn1);
99 
100  // Make sure there isn't an embedded NUL character in the CN
101  if (ASN1_STRING_length(common_name_asn1) != strlen(common_name_str)) {
102  return false;
103  }
104 
105  // Compare expected hostname with the CN
106  return (strcasecmp(hostname, common_name_str) == 0);
107 }
108 
115 bool verify_certificate(const char * hostname, bool preverified, boost::asio::ssl::verify_context& ctx) {
116  // The verify callback can be used to check whether the certificate that is
117  // being presented is valid for the peer. For example, RFC 2818 describes
118  // the steps involved in doing this for HTTPS. Consult the OpenSSL
119  // documentation for more details. Note that the callback is called once
120  // for each certificate in the certificate chain, starting from the root
121  // certificate authority.
122 
123  // Retrieve the depth of the current cert in the chain. 0 indicates the
124  // actual server cert, upon which we will perform extra validation
125  // (specifically, ensuring that the hostname matches. For other certs we
126  // will use the 'preverified' flag from Asio, which incorporates a number of
127  // non-implementation specific OpenSSL checking, such as the formatting of
128  // certs and the trusted status based on the CA certs we imported earlier.
129  int depth = X509_STORE_CTX_get_error_depth(ctx.native_handle());
130 
131  // if we are on the final cert and everything else checks out, ensure that
132  // the hostname is present on the list of SANs or the common name (CN).
133  if (depth == 0 && preverified) {
134  X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
135 
136  if (verify_subject_alternative_name(hostname, cert)) {
137  return true;
138  } else if (verify_common_name(hostname, cert)) {
139  return true;
140  } else {
141  return false;
142  }
143  }
144 
145  return preverified;
146 }
147 
149 
179  context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);
180 
181  try {
182  ctx->set_options(boost::asio::ssl::context::default_workarounds |
183  boost::asio::ssl::context::no_sslv2 |
184  boost::asio::ssl::context::no_sslv3 |
185  boost::asio::ssl::context::single_dh_use);
186 
187 
188  ctx->set_verify_mode(boost::asio::ssl::verify_peer);
189  ctx->set_verify_callback(bind(&verify_certificate, hostname, ::_1, ::_2));
190 
191  // Here we load the CA certificates of all CA's that this client trusts.
192  ctx->load_verify_file("ca-chain.cert.pem");
193  } catch (std::exception& e) {
194  std::cout << e.what() << std::endl;
195  }
196  return ctx;
197 }
198 
199 int main(int argc, char* argv[]) {
200  client c;
201 
202  std::string hostname = "localhost";
203  std::string port = "9002";
204 
205 
206  if (argc == 3) {
207  hostname = argv[1];
208  port = argv[2];
209  } else {
210  std::cout << "Usage: print_server_tls <hostname> <port>" << std::endl;
211  return 1;
212  }
213 
214  std::string uri = "wss://" + hostname + ":" + port;
215 
216  try {
217  // Set logging to be pretty verbose (everything except message payloads)
221 
222  // Initialize ASIO
223  c.init_asio();
224 
225  // Register our message handler
227  c.set_tls_init_handler(bind(&on_tls_init, hostname.c_str(), ::_1));
228 
229  websocketpp::lib::error_code ec;
230  client::connection_ptr con = c.get_connection(uri, ec);
231  if (ec) {
232  std::cout << "could not create connection because: " << ec.message() << std::endl;
233  return 0;
234  }
235 
236  // Note that connect here only requests a connection. No network messages are
237  // exchanged until the event loop starts running in the next line.
238  c.connect(con);
239 
240  c.get_alog().write(websocketpp::log::alevel::app, "Connecting to " + uri);
241 
242  // Start the ASIO io_service run loop
243  // this will cause a single connection to be made to the server. c.run()
244  // will exit when this connection is closed.
245  c.run();
246  } catch (websocketpp::exception const & e) {
247  std::cout << e.what() << std::endl;
248  }
249 }
static level const all
Special aggregate value representing "all levels".
Definition: levels.hpp:152
connection_type::ptr connection_ptr
Type of a shared pointer to the connections this server will create.
static level const app
Special channel for application specific logs. Not used by the library.
Definition: levels.hpp:143
Client endpoint role based on the given config.
connection_type::message_ptr message_ptr
Type of message pointers that this endpoint uses.
Definition: endpoint.hpp:70
static level const frame_payload
One line per frame, includes the full message payload (warning: chatty)
Definition: levels.hpp:129
lib::weak_ptr< void > connection_hdl
A handle to uniquely identify a connection.
alog_type & get_alog()
Get reference to access logger.
Definition: endpoint.hpp:261
connection_ptr connect(connection_ptr con)
Begin the connection process for the given connection.
void set_access_channels(log::level channels)
Set Access logging channel.
Definition: endpoint.hpp:220
void clear_access_channels(log::level channels)
Clear Access logging channels.
Definition: endpoint.hpp:231
void set_error_channels(log::level channels)
Set Error logging channel.
Definition: endpoint.hpp:242
websocketpp::lib::shared_ptr< boost::asio::ssl::context > context_ptr
void set_message_handler(message_handler h)
Definition: endpoint.hpp:322
virtual char const * what() const
Definition: error.hpp:263
static level const all
Special aggregate value representing "all levels".
Definition: levels.hpp:80
connection_ptr get_connection(uri_ptr location, lib::error_code &ec)
Get a new connection.