blob: 4ef3bc6af36c57982403ece2516fbd7bdb1d7c5b [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
Adriana Kobylak0e1cf262019-12-05 13:57:57 -06002#include "config.h"
3
Ed Tanous1abe55e2018-09-05 08:30:59 -07004#include "http_utility.hpp"
5
Ed Tanous7045c8d2017-04-03 10:04:37 -07006#include <atomic>
Ed Tanouse0d918b2018-03-27 17:41:04 -07007#include <boost/algorithm/string.hpp>
Ed Tanous257f5792018-03-17 14:40:09 -07008#include <boost/algorithm/string/predicate.hpp>
Ed Tanous8f626352018-12-19 14:51:54 -08009#include <boost/asio/io_context.hpp>
Ed Tanous3112a142018-11-29 15:45:10 -080010#include <boost/asio/ip/tcp.hpp>
Ed Tanous2f1ebcd2019-02-13 19:39:07 -080011#include <boost/asio/ssl.hpp>
Ed Tanous3112a142018-11-29 15:45:10 -080012#include <boost/beast/core/flat_static_buffer.hpp>
Ed Tanouse278c182019-03-13 16:23:37 -070013#if BOOST_VERSION >= 107000
14#include <boost/beast/ssl/ssl_stream.hpp>
15#else
Ed Tanous2f1ebcd2019-02-13 19:39:07 -080016#include <boost/beast/experimental/core/ssl_stream.hpp>
Ed Tanouse278c182019-03-13 16:23:37 -070017#endif
Ed Tanouse0d918b2018-03-27 17:41:04 -070018#include <boost/beast/http.hpp>
19#include <boost/beast/websocket.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070020#include <chrono>
Ed Tanous1abe55e2018-09-05 08:30:59 -070021#include <vector>
22
Ed Tanousc94ad492019-10-10 15:39:33 -070023#include "http_response.h"
24#include "logging.h"
25#include "middleware_context.h"
26#include "timer_queue.h"
27#include "utility.h"
Ed Tanous7045c8d2017-04-03 10:04:37 -070028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace crow
30{
Ed Tanous257f5792018-03-17 14:40:09 -070031
Ed Tanous1abe55e2018-09-05 08:30:59 -070032inline void prettyPrintJson(crow::Response& res)
33{
Jason M. Bills193ad2f2018-09-26 15:08:52 -070034 std::string value = res.jsonValue.dump(4, ' ', true);
Ed Tanousa29c9972018-11-29 15:54:32 -080035 utility::escapeHtml(value);
36 utility::convertToLinks(value);
Ed Tanous1abe55e2018-09-05 08:30:59 -070037 res.body() = "<html>\n"
38 "<head>\n"
39 "<title>Redfish API</title>\n"
40 "<link rel=\"stylesheet\" type=\"text/css\" "
41 "href=\"/styles/default.css\">\n"
42 "<script src=\"/highlight.pack.js\"></script>"
43 "<script>hljs.initHighlightingOnLoad();</script>"
44 "</head>\n"
45 "<body>\n"
46 "<div style=\"max-width: 576px;margin:0 auto;\">\n"
47 "<img src=\"/DMTF_Redfish_logo_2017.svg\" alt=\"redfish\" "
48 "height=\"406px\" "
49 "width=\"576px\">\n"
50 "<br>\n"
51 "<pre>\n"
52 "<code class=\"json\">" +
53 value +
54 "</code>\n"
55 "</pre>\n"
56 "</div>\n"
57 "</body>\n"
58 "</html>\n";
Ed Tanous93ef5802019-01-03 10:15:41 -080059 res.addHeader("Content-Type", "text/html;charset=UTF-8");
Ed Tanous257f5792018-03-17 14:40:09 -070060}
61
Ed Tanous7045c8d2017-04-03 10:04:37 -070062using namespace boost;
63using tcp = asio::ip::tcp;
64
Ed Tanous1abe55e2018-09-05 08:30:59 -070065namespace detail
66{
67template <typename MW> struct CheckBeforeHandleArity3Const
68{
69 template <typename T,
70 void (T::*)(Request&, Response&, typename MW::Context&) const =
71 &T::beforeHandle>
72 struct Get
73 {
74 };
Ed Tanous7045c8d2017-04-03 10:04:37 -070075};
76
Ed Tanous1abe55e2018-09-05 08:30:59 -070077template <typename MW> struct CheckBeforeHandleArity3
78{
79 template <typename T, void (T::*)(Request&, Response&,
80 typename MW::Context&) = &T::beforeHandle>
81 struct Get
82 {
83 };
Ed Tanous7045c8d2017-04-03 10:04:37 -070084};
85
Ed Tanous1abe55e2018-09-05 08:30:59 -070086template <typename MW> struct CheckAfterHandleArity3Const
87{
88 template <typename T,
89 void (T::*)(Request&, Response&, typename MW::Context&) const =
90 &T::afterHandle>
91 struct Get
92 {
93 };
Ed Tanous7045c8d2017-04-03 10:04:37 -070094};
95
Ed Tanous1abe55e2018-09-05 08:30:59 -070096template <typename MW> struct CheckAfterHandleArity3
97{
98 template <typename T, void (T::*)(Request&, Response&,
99 typename MW::Context&) = &T::afterHandle>
100 struct Get
101 {
102 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700103};
104
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105template <typename T> struct IsBeforeHandleArity3Impl
106{
107 template <typename C>
108 static std::true_type
109 f(typename CheckBeforeHandleArity3Const<T>::template Get<C>*);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700110
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111 template <typename C>
112 static std::true_type
113 f(typename CheckBeforeHandleArity3<T>::template Get<C>*);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700114
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 template <typename C> static std::false_type f(...);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700116
Ed Tanous1abe55e2018-09-05 08:30:59 -0700117 public:
Ed Tanous0c838cf2019-10-24 10:01:46 -0700118 static constexpr bool value = decltype(f<T>(nullptr))::value;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700119};
120
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121template <typename T> struct IsAfterHandleArity3Impl
122{
123 template <typename C>
124 static std::true_type
125 f(typename CheckAfterHandleArity3Const<T>::template Get<C>*);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700126
Ed Tanous1abe55e2018-09-05 08:30:59 -0700127 template <typename C>
128 static std::true_type
129 f(typename CheckAfterHandleArity3<T>::template Get<C>*);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700130
Ed Tanous1abe55e2018-09-05 08:30:59 -0700131 template <typename C> static std::false_type f(...);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700132
Ed Tanous1abe55e2018-09-05 08:30:59 -0700133 public:
Ed Tanous0c838cf2019-10-24 10:01:46 -0700134 static constexpr bool value = decltype(f<T>(nullptr))::value;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700135};
136
137template <typename MW, typename Context, typename ParentContext>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700138typename std::enable_if<!IsBeforeHandleArity3Impl<MW>::value>::type
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 beforeHandlerCall(MW& mw, Request& req, Response& res, Context& ctx,
140 ParentContext& /*parent_ctx*/)
141{
142 mw.beforeHandle(req, res, ctx.template get<MW>(), ctx);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700143}
144
145template <typename MW, typename Context, typename ParentContext>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700146typename std::enable_if<IsBeforeHandleArity3Impl<MW>::value>::type
Ed Tanous1abe55e2018-09-05 08:30:59 -0700147 beforeHandlerCall(MW& mw, Request& req, Response& res, Context& ctx,
148 ParentContext& /*parent_ctx*/)
149{
150 mw.beforeHandle(req, res, ctx.template get<MW>());
Ed Tanous7045c8d2017-04-03 10:04:37 -0700151}
152
153template <typename MW, typename Context, typename ParentContext>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700154typename std::enable_if<!IsAfterHandleArity3Impl<MW>::value>::type
Ed Tanous1abe55e2018-09-05 08:30:59 -0700155 afterHandlerCall(MW& mw, Request& req, Response& res, Context& ctx,
156 ParentContext& /*parent_ctx*/)
157{
158 mw.afterHandle(req, res, ctx.template get<MW>(), ctx);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700159}
160
161template <typename MW, typename Context, typename ParentContext>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700162typename std::enable_if<IsAfterHandleArity3Impl<MW>::value>::type
Ed Tanous1abe55e2018-09-05 08:30:59 -0700163 afterHandlerCall(MW& mw, Request& req, Response& res, Context& ctx,
164 ParentContext& /*parent_ctx*/)
165{
166 mw.afterHandle(req, res, ctx.template get<MW>());
Ed Tanous7045c8d2017-04-03 10:04:37 -0700167}
168
Ed Tanous271584a2019-07-09 16:24:22 -0700169template <size_t N, typename Context, typename Container, typename CurrentMW,
Ed Tanous7045c8d2017-04-03 10:04:37 -0700170 typename... Middlewares>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700171bool middlewareCallHelper(Container& middlewares, Request& req, Response& res,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700172 Context& ctx)
173{
174 using parent_context_t = typename Context::template partial<N - 1>;
175 beforeHandlerCall<CurrentMW, Context, parent_context_t>(
Ed Tanous7045c8d2017-04-03 10:04:37 -0700176 std::get<N>(middlewares), req, res, ctx,
177 static_cast<parent_context_t&>(ctx));
Ed Tanous7045c8d2017-04-03 10:04:37 -0700178
Ed Tanous1abe55e2018-09-05 08:30:59 -0700179 if (res.isCompleted())
180 {
181 afterHandlerCall<CurrentMW, Context, parent_context_t>(
182 std::get<N>(middlewares), req, res, ctx,
183 static_cast<parent_context_t&>(ctx));
184 return true;
185 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700186
Ed Tanous1abe55e2018-09-05 08:30:59 -0700187 if (middlewareCallHelper<N + 1, Context, Container, Middlewares...>(
188 middlewares, req, res, ctx))
189 {
190 afterHandlerCall<CurrentMW, Context, parent_context_t>(
191 std::get<N>(middlewares), req, res, ctx,
192 static_cast<parent_context_t&>(ctx));
193 return true;
194 }
195
196 return false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700197}
198
Ed Tanous271584a2019-07-09 16:24:22 -0700199template <size_t N, typename Context, typename Container>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700200bool middlewareCallHelper(Container& /*middlewares*/, Request& /*req*/,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700201 Response& /*res*/, Context& /*ctx*/)
202{
203 return false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700204}
205
Ed Tanous271584a2019-07-09 16:24:22 -0700206template <size_t N, typename Context, typename Container>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700207typename std::enable_if<(N < 0)>::type
208 afterHandlersCallHelper(Container& /*middlewares*/, Context& /*Context*/,
209 Request& /*req*/, Response& /*res*/)
210{
Ed Tanous7045c8d2017-04-03 10:04:37 -0700211}
212
Ed Tanous271584a2019-07-09 16:24:22 -0700213template <size_t N, typename Context, typename Container>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700214typename std::enable_if<(N == 0)>::type
215 afterHandlersCallHelper(Container& middlewares, Context& ctx, Request& req,
216 Response& res)
217{
218 using parent_context_t = typename Context::template partial<N - 1>;
219 using CurrentMW = typename std::tuple_element<
220 N, typename std::remove_reference<Container>::type>::type;
221 afterHandlerCall<CurrentMW, Context, parent_context_t>(
222 std::get<N>(middlewares), req, res, ctx,
223 static_cast<parent_context_t&>(ctx));
Ed Tanous7045c8d2017-04-03 10:04:37 -0700224}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700225
Ed Tanous271584a2019-07-09 16:24:22 -0700226template <size_t N, typename Context, typename Container>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700227typename std::enable_if<(N > 0)>::type
228 afterHandlersCallHelper(Container& middlewares, Context& ctx, Request& req,
229 Response& res)
230{
231 using parent_context_t = typename Context::template partial<N - 1>;
232 using CurrentMW = typename std::tuple_element<
233 N, typename std::remove_reference<Container>::type>::type;
234 afterHandlerCall<CurrentMW, Context, parent_context_t>(
235 std::get<N>(middlewares), req, res, ctx,
236 static_cast<parent_context_t&>(ctx));
237 afterHandlersCallHelper<N - 1, Context, Container>(middlewares, ctx, req,
238 res);
239}
240} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700241
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700242#ifdef BMCWEB_ENABLE_DEBUG
Ed Tanouse0d918b2018-03-27 17:41:04 -0700243static std::atomic<int> connectionCount;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700244#endif
Jennifer Leeacb7cfb2018-06-07 16:08:15 -0700245
Adriana Kobylak0e1cf262019-12-05 13:57:57 -0600246// request body limit size set by the BMCWEB_HTTP_REQ_BODY_LIMIT_MB option
247constexpr unsigned int httpReqBodyLimit =
248 1024 * 1024 * BMCWEB_HTTP_REQ_BODY_LIMIT_MB;
Jennifer Leeacb7cfb2018-06-07 16:08:15 -0700249
Ed Tanous7045c8d2017-04-03 10:04:37 -0700250template <typename Adaptor, typename Handler, typename... Middlewares>
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000251class Connection : public std::enable_shared_from_this<
252 Connection<Adaptor, Handler, Middlewares...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700253{
254 public:
Ed Tanous271584a2019-07-09 16:24:22 -0700255 Connection(boost::asio::io_context& ioService, Handler* handlerIn,
256 const std::string& ServerNameIn,
257 std::tuple<Middlewares...>* middlewaresIn,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700258 std::function<std::string()>& get_cached_date_str_f,
Ed Tanous271584a2019-07-09 16:24:22 -0700259 detail::TimerQueue& timerQueueIn, Adaptor adaptorIn) :
Ed Tanousceac6f72018-12-02 11:58:47 -0800260 adaptor(std::move(adaptorIn)),
Ed Tanous271584a2019-07-09 16:24:22 -0700261 handler(handlerIn), serverName(ServerNameIn),
262 middlewares(middlewaresIn), getCachedDateStr(get_cached_date_str_f),
263 timerQueue(timerQueueIn)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700264 {
265 parser.emplace(std::piecewise_construct, std::make_tuple());
Adriana Kobylak0e1cf262019-12-05 13:57:57 -0600266 // Temporarily set by the BMCWEB_HTTP_REQ_BODY_LIMIT_MB variable; Need
267 // to modify uploading/authentication mechanism to a better method that
268 // disallows a DOS attack based on a large file size.
Ed Tanous1abe55e2018-09-05 08:30:59 -0700269 parser->body_limit(httpReqBodyLimit);
270 req.emplace(parser->get());
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200271
272#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100273 auto ca_available = !std::filesystem::is_empty(
274 std::filesystem::path(ensuressl::trustStorePath));
275 if (ca_available && crow::persistent_data::SessionStore::getInstance()
276 .getAuthMethodsConfig()
277 .tls)
278 {
279 adaptor.set_verify_mode(boost::asio::ssl::verify_peer);
280 SSL_set_session_id_context(
281 adaptor.native_handle(),
282 reinterpret_cast<const unsigned char*>(serverName.c_str()),
Zbigniew Kurzynskicac94c52019-11-07 12:55:04 +0100283 static_cast<unsigned int>(serverName.length()));
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100284 BMCWEB_LOG_DEBUG << this << " TLS is enabled on this connection.";
285 }
286
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100287 adaptor.set_verify_callback([this](
288 bool preverified,
289 boost::asio::ssl::verify_context& ctx) {
290 // do nothing if TLS is disabled
291 if (!crow::persistent_data::SessionStore::getInstance()
292 .getAuthMethodsConfig()
293 .tls)
294 {
295 BMCWEB_LOG_DEBUG << this << " TLS auth_config is disabled";
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200296 return true;
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100297 }
298
299 // We always return true to allow full auth flow
300 if (!preverified)
301 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100302 BMCWEB_LOG_DEBUG << this << " TLS preverification failed.";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100303 return true;
304 }
305
306 X509_STORE_CTX* cts = ctx.native_handle();
307 if (cts == nullptr)
308 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100309 BMCWEB_LOG_DEBUG << this << " Cannot get native TLS handle.";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100310 return true;
311 }
312
313 // Get certificate
314 X509* peerCert =
315 X509_STORE_CTX_get_current_cert(ctx.native_handle());
316 if (peerCert == nullptr)
317 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100318 BMCWEB_LOG_DEBUG << this
319 << " Cannot get current TLS certificate.";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100320 return true;
321 }
322
323 // Check if certificate is OK
324 int error = X509_STORE_CTX_get_error(cts);
325 if (error != X509_V_OK)
326 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100327 BMCWEB_LOG_INFO << this << " Last TLS error is: " << error;
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100328 return true;
329 }
330 // Check that we have reached final certificate in chain
331 int32_t depth = X509_STORE_CTX_get_error_depth(cts);
332 if (depth != 0)
333
334 {
335 BMCWEB_LOG_DEBUG
336 << this << " Certificate verification in progress (depth "
337 << depth << "), waiting to reach final depth";
338 return true;
339 }
340
341 BMCWEB_LOG_DEBUG << this
342 << " Certificate verification of final depth";
343
344 // Verify KeyUsage
345 bool isKeyUsageDigitalSignature = false;
346 bool isKeyUsageKeyAgreement = false;
347
348 ASN1_BIT_STRING* usage = static_cast<ASN1_BIT_STRING*>(
349 X509_get_ext_d2i(peerCert, NID_key_usage, NULL, NULL));
350
351 if (usage == nullptr)
352 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100353 BMCWEB_LOG_DEBUG << this << " TLS usage is null";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100354 return true;
355 }
356
357 for (int i = 0; i < usage->length; i++)
358 {
359 if (KU_DIGITAL_SIGNATURE & usage->data[i])
360 {
361 isKeyUsageDigitalSignature = true;
362 }
363 if (KU_KEY_AGREEMENT & usage->data[i])
364 {
365 isKeyUsageKeyAgreement = true;
366 }
367 }
368
369 if (!isKeyUsageDigitalSignature || !isKeyUsageKeyAgreement)
370 {
371 BMCWEB_LOG_DEBUG << this
372 << " Certificate ExtendedKeyUsage does "
373 "not allow provided certificate to "
374 "be used for user authentication";
375 return true;
376 }
377
378 // Determine that ExtendedKeyUsage includes Client Auth
379
380 stack_st_ASN1_OBJECT* extUsage = static_cast<stack_st_ASN1_OBJECT*>(
381 X509_get_ext_d2i(peerCert, NID_ext_key_usage, NULL, NULL));
382
383 if (extUsage == nullptr)
384 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100385 BMCWEB_LOG_DEBUG << this << " TLS extUsage is null";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100386 return true;
387 }
388
389 bool isExKeyUsageClientAuth = false;
390 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
391 {
392 if (NID_client_auth ==
393 OBJ_obj2nid(sk_ASN1_OBJECT_value(extUsage, i)))
394 {
395 isExKeyUsageClientAuth = true;
396 break;
397 }
398 }
399
400 // Certificate has to have proper key usages set
401 if (!isExKeyUsageClientAuth)
402 {
403 BMCWEB_LOG_DEBUG << this
404 << " Certificate ExtendedKeyUsage does "
405 "not allow provided certificate to "
406 "be used for user authentication";
407 return true;
408 }
409 std::string sslUser;
410 // Extract username contained in CommonName
411 sslUser.resize(256, '\0');
412
413 int status = X509_NAME_get_text_by_NID(
414 X509_get_subject_name(peerCert), NID_commonName, sslUser.data(),
415 static_cast<int>(sslUser.size()));
416
417 if (status == -1)
418 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100419 BMCWEB_LOG_DEBUG
420 << this << " TLS cannot get username to create session";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100421 return true;
422 }
423
424 size_t lastChar = sslUser.find('\0');
425 if (lastChar == std::string::npos || lastChar == 0)
426 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100427 BMCWEB_LOG_DEBUG << this << " Invalid TLS user name";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100428 return true;
429 }
430 sslUser.resize(lastChar);
431
432 session = persistent_data::SessionStore::getInstance()
433 .generateUserSession(
434 sslUser,
435 crow::persistent_data::PersistenceType::TIMEOUT);
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100436 if (auto sp = session.lock())
437 {
438 BMCWEB_LOG_DEBUG << this
439 << " Generating TLS session: " << sp->uniqueId;
440 }
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100441 return true;
442 });
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200443#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
444
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700445#ifdef BMCWEB_ENABLE_DEBUG
Ed Tanous1abe55e2018-09-05 08:30:59 -0700446 connectionCount++;
447 BMCWEB_LOG_DEBUG << this << " Connection open, total "
448 << connectionCount;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700449#endif
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700451
Ed Tanous1abe55e2018-09-05 08:30:59 -0700452 ~Connection()
453 {
454 res.completeRequestHandler = nullptr;
455 cancelDeadlineTimer();
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700456#ifdef BMCWEB_ENABLE_DEBUG
Ed Tanous1abe55e2018-09-05 08:30:59 -0700457 connectionCount--;
458 BMCWEB_LOG_DEBUG << this << " Connection closed, total "
459 << connectionCount;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700460#endif
Ed Tanous7045c8d2017-04-03 10:04:37 -0700461 }
462
Ed Tanousceac6f72018-12-02 11:58:47 -0800463 Adaptor& socket()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700464 {
Ed Tanousceac6f72018-12-02 11:58:47 -0800465 return adaptor;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700466 }
467
Ed Tanous1abe55e2018-09-05 08:30:59 -0700468 void start()
469 {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700470
Ed Tanousceac6f72018-12-02 11:58:47 -0800471 startDeadline();
472 // TODO(ed) Abstract this to a more clever class with the idea of an
473 // asynchronous "start"
474 if constexpr (std::is_same_v<Adaptor,
475 boost::beast::ssl_stream<
476 boost::asio::ip::tcp::socket>>)
477 {
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000478 adaptor.async_handshake(boost::asio::ssl::stream_base::server,
479 [this, self(shared_from_this())](
480 const boost::system::error_code& ec) {
481 if (ec)
482 {
483 return;
484 }
485 doReadHeaders();
486 });
Ed Tanousceac6f72018-12-02 11:58:47 -0800487 }
488 else
489 {
490 doReadHeaders();
491 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700492 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700493
Ed Tanous1abe55e2018-09-05 08:30:59 -0700494 void handle()
495 {
496 cancelDeadlineTimer();
497 bool isInvalidRequest = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700498
Ed Tanous1abe55e2018-09-05 08:30:59 -0700499 // Check for HTTP version 1.1.
500 if (req->version() == 11)
501 {
502 if (req->getHeaderValue(boost::beast::http::field::host).empty())
503 {
504 isInvalidRequest = true;
Ed Tanousde5c9f32019-03-26 09:17:55 -0700505 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700506 }
507 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700508
Ed Tanouse278c182019-03-13 16:23:37 -0700509 BMCWEB_LOG_INFO << "Request: "
510 << " " << this << " HTTP/" << req->version() / 10 << "."
511 << req->version() % 10 << ' ' << req->methodString()
512 << " " << req->target();
Ed Tanous7045c8d2017-04-03 10:04:37 -0700513
Ed Tanous1abe55e2018-09-05 08:30:59 -0700514 needToCallAfterHandlers = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700515
Ed Tanous1abe55e2018-09-05 08:30:59 -0700516 if (!isInvalidRequest)
517 {
518 res.completeRequestHandler = [] {};
Ed Tanouse278c182019-03-13 16:23:37 -0700519 res.isAliveHelper = [this]() -> bool { return isAlive(); };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700520
Ed Tanous1abe55e2018-09-05 08:30:59 -0700521 ctx = detail::Context<Middlewares...>();
Ed Tanouse278c182019-03-13 16:23:37 -0700522 req->middlewareContext = static_cast<void*>(&ctx);
523 req->ioService = static_cast<decltype(req->ioService)>(
524 &adaptor.get_executor().context());
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200525
526#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
527 if (auto sp = session.lock())
528 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100529 // set cookie only if this is req from the browser.
530 if (req->getHeaderValue("User-Agent").empty())
531 {
532 BMCWEB_LOG_DEBUG << this << " TLS session: " << sp->uniqueId
533 << " will be used for this request.";
534 req->session = sp;
535 }
536 else
537 {
538 std::string_view cookieValue =
539 req->getHeaderValue("Cookie");
540 if (cookieValue.empty() ||
541 cookieValue.find("SESSION=") == std::string::npos)
542 {
543 res.addHeader("Set-Cookie",
544 "XSRF-TOKEN=" + sp->csrfToken +
545 "; Secure\r\nSet-Cookie: SESSION=" +
546 sp->sessionToken +
Zbigniew Kurzynski26139a52019-12-11 19:11:18 +0100547 "; Secure; HttpOnly\r\nSet-Cookie: "
548 "IsAuthenticated=true; Secure");
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100549 BMCWEB_LOG_DEBUG
550 << this << " TLS session: " << sp->uniqueId
551 << " with cookie will be used for this request.";
552 req->session = sp;
553 }
554 }
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200555 }
556#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
557
Ed Tanous1abe55e2018-09-05 08:30:59 -0700558 detail::middlewareCallHelper<
Ed Tanous271584a2019-07-09 16:24:22 -0700559 0U, decltype(ctx), decltype(*middlewares), Middlewares...>(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700560 *middlewares, *req, res, ctx);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700561
Ed Tanous1abe55e2018-09-05 08:30:59 -0700562 if (!res.completed)
563 {
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000564 needToCallAfterHandlers = true;
565 res.completeRequestHandler = [self(shared_from_this())] {
566 self->completeRequest();
567 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700568 if (req->isUpgrade() &&
569 boost::iequals(
570 req->getHeaderValue(boost::beast::http::field::upgrade),
571 "websocket"))
572 {
573 handler->handleUpgrade(*req, res, std::move(adaptor));
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000574 // delete lambda with self shared_ptr
575 // to enable connection destruction
576 res.completeRequestHandler = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700577 return;
578 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700579 handler->handle(*req, res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700580 }
581 else
582 {
583 completeRequest();
584 }
585 }
586 else
587 {
588 completeRequest();
589 }
590 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700591
Ed Tanouse278c182019-03-13 16:23:37 -0700592 bool isAlive()
593 {
594
595 if constexpr (std::is_same_v<Adaptor,
596 boost::beast::ssl_stream<
597 boost::asio::ip::tcp::socket>>)
598 {
599 return adaptor.next_layer().is_open();
600 }
601 else
602 {
603 return adaptor.is_open();
604 }
605 }
606 void close()
607 {
Ed Tanouse278c182019-03-13 16:23:37 -0700608 if constexpr (std::is_same_v<Adaptor,
609 boost::beast::ssl_stream<
610 boost::asio::ip::tcp::socket>>)
611 {
612 adaptor.next_layer().close();
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200613#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
614 if (auto sp = session.lock())
615 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100616 BMCWEB_LOG_DEBUG << this
617 << " Removing TLS session: " << sp->uniqueId;
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200618 persistent_data::SessionStore::getInstance().removeSession(sp);
619 }
620#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanouse278c182019-03-13 16:23:37 -0700621 }
622 else
623 {
624 adaptor.close();
625 }
626 }
627
Ed Tanous1abe55e2018-09-05 08:30:59 -0700628 void completeRequest()
629 {
630 BMCWEB_LOG_INFO << "Response: " << this << ' ' << req->url << ' '
631 << res.resultInt() << " keepalive=" << req->keepAlive();
Ed Tanous7045c8d2017-04-03 10:04:37 -0700632
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633 if (needToCallAfterHandlers)
634 {
635 needToCallAfterHandlers = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700636
Ed Tanous1abe55e2018-09-05 08:30:59 -0700637 // call all afterHandler of middlewares
Ed Tanous271584a2019-07-09 16:24:22 -0700638 detail::afterHandlersCallHelper<sizeof...(Middlewares) - 1,
Ed Tanousb01bf292019-03-25 19:25:26 +0000639 decltype(ctx),
640 decltype(*middlewares)>(
641 *middlewares, ctx, *req, res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700642 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700643
Ed Tanouse278c182019-03-13 16:23:37 -0700644 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700645 {
646 // BMCWEB_LOG_DEBUG << this << " delete (socket is closed) " <<
647 // isReading
648 // << ' ' << isWriting;
649 // delete this;
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000650
651 // delete lambda with self shared_ptr
652 // to enable connection destruction
653 res.completeRequestHandler = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700654 return;
655 }
656 if (res.body().empty() && !res.jsonValue.empty())
657 {
658 if (http_helpers::requestPrefersHtml(*req))
659 {
660 prettyPrintJson(res);
661 }
662 else
663 {
664 res.jsonMode();
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700665 res.body() = res.jsonValue.dump(2, ' ', true);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700666 }
667 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700668
Ed Tanous1abe55e2018-09-05 08:30:59 -0700669 if (res.resultInt() >= 400 && res.body().empty())
670 {
671 res.body() = std::string(res.reason());
672 }
Ed Tanous6295bec2019-09-03 10:11:01 -0700673
674 if (res.result() == boost::beast::http::status::no_content)
675 {
676 // Boost beast throws if content is provided on a no-content
677 // response. Ideally, this would never happen, but in the case that
678 // it does, we don't want to throw.
679 BMCWEB_LOG_CRITICAL
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100680 << this << " Response content provided but code was no-content";
Ed Tanous6295bec2019-09-03 10:11:01 -0700681 res.body().clear();
682 }
683
Ed Tanous1abe55e2018-09-05 08:30:59 -0700684 res.addHeader(boost::beast::http::field::server, serverName);
685 res.addHeader(boost::beast::http::field::date, getCachedDateStr());
686
687 res.keepAlive(req->keepAlive());
688
689 doWrite();
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000690
691 // delete lambda with self shared_ptr
692 // to enable connection destruction
693 res.completeRequestHandler = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700694 }
695
696 private:
697 void doReadHeaders()
698 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700699 BMCWEB_LOG_DEBUG << this << " doReadHeaders";
700
701 // Clean up any previous Connection.
702 boost::beast::http::async_read_header(
Ed Tanousceac6f72018-12-02 11:58:47 -0800703 adaptor, buffer, *parser,
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000704 [this,
705 self(shared_from_this())](const boost::system::error_code& ec,
706 std::size_t bytes_transferred) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700707 BMCWEB_LOG_ERROR << this << " async_read_header "
708 << bytes_transferred << " Bytes";
709 bool errorWhileReading = false;
710 if (ec)
711 {
712 errorWhileReading = true;
713 BMCWEB_LOG_ERROR
714 << this << " Error while reading: " << ec.message();
715 }
716 else
717 {
718 // if the adaptor isn't open anymore, and wasn't handed to a
719 // websocket, treat as an error
Ed Tanouse278c182019-03-13 16:23:37 -0700720 if (!isAlive() && !req->isUpgrade())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700721 {
722 errorWhileReading = true;
723 }
724 }
725
726 if (errorWhileReading)
727 {
728 cancelDeadlineTimer();
Ed Tanouse278c182019-03-13 16:23:37 -0700729 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700730 BMCWEB_LOG_DEBUG << this << " from read(1)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731 return;
732 }
733
734 // Compute the url parameters for the request
735 req->url = req->target();
736 std::size_t index = req->url.find("?");
Ed Tanous39e77502019-03-04 17:35:53 -0800737 if (index != std::string_view::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700738 {
Jason M. Bills43fcbe52018-10-16 15:19:20 -0700739 req->url = req->url.substr(0, index);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700740 }
741 req->urlParams = QueryString(std::string(req->target()));
742 doRead();
743 });
744 }
745
746 void doRead()
747 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700748 BMCWEB_LOG_DEBUG << this << " doRead";
749
750 boost::beast::http::async_read(
Ed Tanousceac6f72018-12-02 11:58:47 -0800751 adaptor, buffer, *parser,
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000752 [this,
753 self(shared_from_this())](const boost::system::error_code& ec,
754 std::size_t bytes_transferred) {
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100755 BMCWEB_LOG_DEBUG << this << " async_read " << bytes_transferred
Ed Tanous1abe55e2018-09-05 08:30:59 -0700756 << " Bytes";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700757
758 bool errorWhileReading = false;
759 if (ec)
760 {
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100761 BMCWEB_LOG_ERROR
762 << this << " Error while reading: " << ec.message();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700763 errorWhileReading = true;
764 }
765 else
766 {
Ed Tanouse278c182019-03-13 16:23:37 -0700767 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700768 {
769 errorWhileReading = true;
770 }
771 }
772 if (errorWhileReading)
773 {
774 cancelDeadlineTimer();
Ed Tanouse278c182019-03-13 16:23:37 -0700775 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700776 BMCWEB_LOG_DEBUG << this << " from read(1)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700777 return;
778 }
779 handle();
780 });
781 }
782
783 void doWrite()
784 {
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100785 BMCWEB_LOG_DEBUG << this << " doWrite";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700786 res.preparePayload();
787 serializer.emplace(*res.stringResponse);
788 boost::beast::http::async_write(
Ed Tanousceac6f72018-12-02 11:58:47 -0800789 adaptor, *serializer,
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000790 [this,
791 self(shared_from_this())](const boost::system::error_code& ec,
792 std::size_t bytes_transferred) {
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100793 BMCWEB_LOG_DEBUG << this << " async_write " << bytes_transferred
Ed Tanous1abe55e2018-09-05 08:30:59 -0700794 << " bytes";
795
796 if (ec)
797 {
798 BMCWEB_LOG_DEBUG << this << " from write(2)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700799 return;
800 }
Ed Tanousceac6f72018-12-02 11:58:47 -0800801 if (!res.keepAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700802 {
Ed Tanouse278c182019-03-13 16:23:37 -0700803 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700804 BMCWEB_LOG_DEBUG << this << " from write(1)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700805 return;
806 }
807
808 serializer.reset();
809 BMCWEB_LOG_DEBUG << this << " Clearing response";
810 res.clear();
811 parser.emplace(std::piecewise_construct, std::make_tuple());
812 parser->body_limit(httpReqBodyLimit); // reset body limit for
813 // newly created parser
814 buffer.consume(buffer.size());
815
816 req.emplace(parser->get());
817 doReadHeaders();
818 });
819 }
820
Ed Tanous1abe55e2018-09-05 08:30:59 -0700821 void cancelDeadlineTimer()
822 {
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000823 if (timerCancelKey)
824 {
825 BMCWEB_LOG_DEBUG << this << " timer cancelled: " << &timerQueue
826 << ' ' << *timerCancelKey;
827 timerQueue.cancel(*timerCancelKey);
828 timerCancelKey.reset();
829 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700830 }
831
832 void startDeadline()
833 {
834 cancelDeadlineTimer();
835
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000836 timerCancelKey = timerQueue.add([this, self(shared_from_this())] {
837 // Mark timer as not active to avoid canceling it during
838 // Connection destructor which leads to double free issue
839 timerCancelKey.reset();
Ed Tanouse278c182019-03-13 16:23:37 -0700840 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700841 {
842 return;
843 }
Ed Tanouse278c182019-03-13 16:23:37 -0700844 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700845 });
846 BMCWEB_LOG_DEBUG << this << " timer added: " << &timerQueue << ' '
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000847 << *timerCancelKey;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700848 }
849
850 private:
851 Adaptor adaptor;
852 Handler* handler;
853
Ed Tanousa24526d2018-12-10 15:17:59 -0800854 // Making this a std::optional allows it to be efficiently destroyed and
Ed Tanous1abe55e2018-09-05 08:30:59 -0700855 // re-created on Connection reset
Ed Tanousa24526d2018-12-10 15:17:59 -0800856 std::optional<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700857 boost::beast::http::request_parser<boost::beast::http::string_body>>
858 parser;
859
Ed Tanous3112a142018-11-29 15:45:10 -0800860 boost::beast::flat_static_buffer<8192> buffer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700861
Ed Tanousa24526d2018-12-10 15:17:59 -0800862 std::optional<boost::beast::http::response_serializer<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700863 boost::beast::http::string_body>>
864 serializer;
865
Ed Tanousa24526d2018-12-10 15:17:59 -0800866 std::optional<crow::Request> req;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700867 crow::Response res;
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200868#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
869 std::weak_ptr<crow::persistent_data::UserSession> session;
870#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanous1abe55e2018-09-05 08:30:59 -0700871
872 const std::string& serverName;
873
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000874 std::optional<size_t> timerCancelKey;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700875
Ed Tanous1abe55e2018-09-05 08:30:59 -0700876 bool needToCallAfterHandlers{};
877 bool needToStartReadAfterComplete{};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700878
879 std::tuple<Middlewares...>* middlewares;
880 detail::Context<Middlewares...> ctx;
881
882 std::function<std::string()>& getCachedDateStr;
883 detail::TimerQueue& timerQueue;
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000884
885 using std::enable_shared_from_this<
886 Connection<Adaptor, Handler, Middlewares...>>::shared_from_this;
Ed Tanous3112a142018-11-29 15:45:10 -0800887};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700888} // namespace crow