blob: b7861751bbb51a1bdb309aab8f7b2f8f09681748 [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 }
Zbigniew Kurzynski09d02f82020-03-30 13:41:42 +0200377 ASN1_BIT_STRING_free(usage);
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100378
379 // Determine that ExtendedKeyUsage includes Client Auth
380
381 stack_st_ASN1_OBJECT* extUsage = static_cast<stack_st_ASN1_OBJECT*>(
382 X509_get_ext_d2i(peerCert, NID_ext_key_usage, NULL, NULL));
383
384 if (extUsage == nullptr)
385 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100386 BMCWEB_LOG_DEBUG << this << " TLS extUsage is null";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100387 return true;
388 }
389
390 bool isExKeyUsageClientAuth = false;
391 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
392 {
393 if (NID_client_auth ==
394 OBJ_obj2nid(sk_ASN1_OBJECT_value(extUsage, i)))
395 {
396 isExKeyUsageClientAuth = true;
397 break;
398 }
399 }
Zbigniew Kurzynski09d02f82020-03-30 13:41:42 +0200400 sk_ASN1_OBJECT_free(extUsage);
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100401
402 // Certificate has to have proper key usages set
403 if (!isExKeyUsageClientAuth)
404 {
405 BMCWEB_LOG_DEBUG << this
406 << " Certificate ExtendedKeyUsage does "
407 "not allow provided certificate to "
408 "be used for user authentication";
409 return true;
410 }
411 std::string sslUser;
412 // Extract username contained in CommonName
413 sslUser.resize(256, '\0');
414
415 int status = X509_NAME_get_text_by_NID(
416 X509_get_subject_name(peerCert), NID_commonName, sslUser.data(),
417 static_cast<int>(sslUser.size()));
418
419 if (status == -1)
420 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100421 BMCWEB_LOG_DEBUG
422 << this << " TLS cannot get username to create session";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100423 return true;
424 }
425
426 size_t lastChar = sslUser.find('\0');
427 if (lastChar == std::string::npos || lastChar == 0)
428 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100429 BMCWEB_LOG_DEBUG << this << " Invalid TLS user name";
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100430 return true;
431 }
432 sslUser.resize(lastChar);
433
434 session = persistent_data::SessionStore::getInstance()
435 .generateUserSession(
436 sslUser,
437 crow::persistent_data::PersistenceType::TIMEOUT);
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100438 if (auto sp = session.lock())
439 {
440 BMCWEB_LOG_DEBUG << this
441 << " Generating TLS session: " << sp->uniqueId;
442 }
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100443 return true;
444 });
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200445#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
446
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700447#ifdef BMCWEB_ENABLE_DEBUG
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 connectionCount++;
449 BMCWEB_LOG_DEBUG << this << " Connection open, total "
450 << connectionCount;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700451#endif
Ed Tanous1abe55e2018-09-05 08:30:59 -0700452 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700453
Ed Tanous1abe55e2018-09-05 08:30:59 -0700454 ~Connection()
455 {
456 res.completeRequestHandler = nullptr;
457 cancelDeadlineTimer();
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700458#ifdef BMCWEB_ENABLE_DEBUG
Ed Tanous1abe55e2018-09-05 08:30:59 -0700459 connectionCount--;
460 BMCWEB_LOG_DEBUG << this << " Connection closed, total "
461 << connectionCount;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700462#endif
Ed Tanous7045c8d2017-04-03 10:04:37 -0700463 }
464
Ed Tanousceac6f72018-12-02 11:58:47 -0800465 Adaptor& socket()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700466 {
Ed Tanousceac6f72018-12-02 11:58:47 -0800467 return adaptor;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700468 }
469
Ed Tanous1abe55e2018-09-05 08:30:59 -0700470 void start()
471 {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700472
Ed Tanousceac6f72018-12-02 11:58:47 -0800473 startDeadline();
474 // TODO(ed) Abstract this to a more clever class with the idea of an
475 // asynchronous "start"
476 if constexpr (std::is_same_v<Adaptor,
477 boost::beast::ssl_stream<
478 boost::asio::ip::tcp::socket>>)
479 {
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000480 adaptor.async_handshake(boost::asio::ssl::stream_base::server,
481 [this, self(shared_from_this())](
482 const boost::system::error_code& ec) {
483 if (ec)
484 {
485 return;
486 }
487 doReadHeaders();
488 });
Ed Tanousceac6f72018-12-02 11:58:47 -0800489 }
490 else
491 {
492 doReadHeaders();
493 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700494 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700495
Ed Tanous1abe55e2018-09-05 08:30:59 -0700496 void handle()
497 {
498 cancelDeadlineTimer();
499 bool isInvalidRequest = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700500
Ed Tanous1abe55e2018-09-05 08:30:59 -0700501 // Check for HTTP version 1.1.
502 if (req->version() == 11)
503 {
504 if (req->getHeaderValue(boost::beast::http::field::host).empty())
505 {
506 isInvalidRequest = true;
Ed Tanousde5c9f32019-03-26 09:17:55 -0700507 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700508 }
509 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700510
Ed Tanouse278c182019-03-13 16:23:37 -0700511 BMCWEB_LOG_INFO << "Request: "
512 << " " << this << " HTTP/" << req->version() / 10 << "."
513 << req->version() % 10 << ' ' << req->methodString()
514 << " " << req->target();
Ed Tanous7045c8d2017-04-03 10:04:37 -0700515
Ed Tanous1abe55e2018-09-05 08:30:59 -0700516 needToCallAfterHandlers = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700517
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 if (!isInvalidRequest)
519 {
520 res.completeRequestHandler = [] {};
Ed Tanouse278c182019-03-13 16:23:37 -0700521 res.isAliveHelper = [this]() -> bool { return isAlive(); };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700522
Ed Tanous1abe55e2018-09-05 08:30:59 -0700523 ctx = detail::Context<Middlewares...>();
Ed Tanouse278c182019-03-13 16:23:37 -0700524 req->middlewareContext = static_cast<void*>(&ctx);
525 req->ioService = static_cast<decltype(req->ioService)>(
526 &adaptor.get_executor().context());
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200527
528#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
529 if (auto sp = session.lock())
530 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100531 // set cookie only if this is req from the browser.
532 if (req->getHeaderValue("User-Agent").empty())
533 {
534 BMCWEB_LOG_DEBUG << this << " TLS session: " << sp->uniqueId
535 << " will be used for this request.";
536 req->session = sp;
537 }
538 else
539 {
540 std::string_view cookieValue =
541 req->getHeaderValue("Cookie");
542 if (cookieValue.empty() ||
543 cookieValue.find("SESSION=") == std::string::npos)
544 {
545 res.addHeader("Set-Cookie",
546 "XSRF-TOKEN=" + sp->csrfToken +
547 "; Secure\r\nSet-Cookie: SESSION=" +
548 sp->sessionToken +
Zbigniew Kurzynski26139a52019-12-11 19:11:18 +0100549 "; Secure; HttpOnly\r\nSet-Cookie: "
550 "IsAuthenticated=true; Secure");
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100551 BMCWEB_LOG_DEBUG
552 << this << " TLS session: " << sp->uniqueId
553 << " with cookie will be used for this request.";
554 req->session = sp;
555 }
556 }
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200557 }
558#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
559
Ed Tanous1abe55e2018-09-05 08:30:59 -0700560 detail::middlewareCallHelper<
Ed Tanous271584a2019-07-09 16:24:22 -0700561 0U, decltype(ctx), decltype(*middlewares), Middlewares...>(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700562 *middlewares, *req, res, ctx);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700563
Ed Tanous1abe55e2018-09-05 08:30:59 -0700564 if (!res.completed)
565 {
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000566 needToCallAfterHandlers = true;
567 res.completeRequestHandler = [self(shared_from_this())] {
568 self->completeRequest();
569 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700570 if (req->isUpgrade() &&
571 boost::iequals(
572 req->getHeaderValue(boost::beast::http::field::upgrade),
573 "websocket"))
574 {
575 handler->handleUpgrade(*req, res, std::move(adaptor));
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000576 // delete lambda with self shared_ptr
577 // to enable connection destruction
578 res.completeRequestHandler = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700579 return;
580 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700581 handler->handle(*req, res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 }
583 else
584 {
585 completeRequest();
586 }
587 }
588 else
589 {
590 completeRequest();
591 }
592 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700593
Ed Tanouse278c182019-03-13 16:23:37 -0700594 bool isAlive()
595 {
596
597 if constexpr (std::is_same_v<Adaptor,
598 boost::beast::ssl_stream<
599 boost::asio::ip::tcp::socket>>)
600 {
601 return adaptor.next_layer().is_open();
602 }
603 else
604 {
605 return adaptor.is_open();
606 }
607 }
608 void close()
609 {
Ed Tanouse278c182019-03-13 16:23:37 -0700610 if constexpr (std::is_same_v<Adaptor,
611 boost::beast::ssl_stream<
612 boost::asio::ip::tcp::socket>>)
613 {
614 adaptor.next_layer().close();
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200615#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
616 if (auto sp = session.lock())
617 {
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100618 BMCWEB_LOG_DEBUG << this
619 << " Removing TLS session: " << sp->uniqueId;
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200620 persistent_data::SessionStore::getInstance().removeSession(sp);
621 }
622#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanouse278c182019-03-13 16:23:37 -0700623 }
624 else
625 {
626 adaptor.close();
627 }
628 }
629
Ed Tanous1abe55e2018-09-05 08:30:59 -0700630 void completeRequest()
631 {
632 BMCWEB_LOG_INFO << "Response: " << this << ' ' << req->url << ' '
633 << res.resultInt() << " keepalive=" << req->keepAlive();
Ed Tanous7045c8d2017-04-03 10:04:37 -0700634
Ed Tanous1abe55e2018-09-05 08:30:59 -0700635 if (needToCallAfterHandlers)
636 {
637 needToCallAfterHandlers = false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700638
Ed Tanous1abe55e2018-09-05 08:30:59 -0700639 // call all afterHandler of middlewares
Ed Tanous271584a2019-07-09 16:24:22 -0700640 detail::afterHandlersCallHelper<sizeof...(Middlewares) - 1,
Ed Tanousb01bf292019-03-25 19:25:26 +0000641 decltype(ctx),
642 decltype(*middlewares)>(
643 *middlewares, ctx, *req, res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700644 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700645
Ed Tanouse278c182019-03-13 16:23:37 -0700646 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700647 {
648 // BMCWEB_LOG_DEBUG << this << " delete (socket is closed) " <<
649 // isReading
650 // << ' ' << isWriting;
651 // delete this;
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000652
653 // delete lambda with self shared_ptr
654 // to enable connection destruction
655 res.completeRequestHandler = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700656 return;
657 }
658 if (res.body().empty() && !res.jsonValue.empty())
659 {
660 if (http_helpers::requestPrefersHtml(*req))
661 {
662 prettyPrintJson(res);
663 }
664 else
665 {
666 res.jsonMode();
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700667 res.body() = res.jsonValue.dump(2, ' ', true);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700668 }
669 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700670
Ed Tanous1abe55e2018-09-05 08:30:59 -0700671 if (res.resultInt() >= 400 && res.body().empty())
672 {
673 res.body() = std::string(res.reason());
674 }
Ed Tanous6295bec2019-09-03 10:11:01 -0700675
676 if (res.result() == boost::beast::http::status::no_content)
677 {
678 // Boost beast throws if content is provided on a no-content
679 // response. Ideally, this would never happen, but in the case that
680 // it does, we don't want to throw.
681 BMCWEB_LOG_CRITICAL
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100682 << this << " Response content provided but code was no-content";
Ed Tanous6295bec2019-09-03 10:11:01 -0700683 res.body().clear();
684 }
685
Ed Tanous1abe55e2018-09-05 08:30:59 -0700686 res.addHeader(boost::beast::http::field::server, serverName);
687 res.addHeader(boost::beast::http::field::date, getCachedDateStr());
688
689 res.keepAlive(req->keepAlive());
690
691 doWrite();
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000692
693 // delete lambda with self shared_ptr
694 // to enable connection destruction
695 res.completeRequestHandler = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700696 }
697
698 private:
699 void doReadHeaders()
700 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700701 BMCWEB_LOG_DEBUG << this << " doReadHeaders";
702
703 // Clean up any previous Connection.
704 boost::beast::http::async_read_header(
Ed Tanousceac6f72018-12-02 11:58:47 -0800705 adaptor, buffer, *parser,
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000706 [this,
707 self(shared_from_this())](const boost::system::error_code& ec,
708 std::size_t bytes_transferred) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700709 BMCWEB_LOG_ERROR << this << " async_read_header "
710 << bytes_transferred << " Bytes";
711 bool errorWhileReading = false;
712 if (ec)
713 {
714 errorWhileReading = true;
715 BMCWEB_LOG_ERROR
716 << this << " Error while reading: " << ec.message();
717 }
718 else
719 {
720 // if the adaptor isn't open anymore, and wasn't handed to a
721 // websocket, treat as an error
Ed Tanouse278c182019-03-13 16:23:37 -0700722 if (!isAlive() && !req->isUpgrade())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700723 {
724 errorWhileReading = true;
725 }
726 }
727
728 if (errorWhileReading)
729 {
730 cancelDeadlineTimer();
Ed Tanouse278c182019-03-13 16:23:37 -0700731 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700732 BMCWEB_LOG_DEBUG << this << " from read(1)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700733 return;
734 }
735
736 // Compute the url parameters for the request
737 req->url = req->target();
738 std::size_t index = req->url.find("?");
Ed Tanous39e77502019-03-04 17:35:53 -0800739 if (index != std::string_view::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700740 {
Jason M. Bills43fcbe52018-10-16 15:19:20 -0700741 req->url = req->url.substr(0, index);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700742 }
743 req->urlParams = QueryString(std::string(req->target()));
744 doRead();
745 });
746 }
747
748 void doRead()
749 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700750 BMCWEB_LOG_DEBUG << this << " doRead";
751
752 boost::beast::http::async_read(
Ed Tanousceac6f72018-12-02 11:58:47 -0800753 adaptor, buffer, *parser,
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000754 [this,
755 self(shared_from_this())](const boost::system::error_code& ec,
756 std::size_t bytes_transferred) {
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100757 BMCWEB_LOG_DEBUG << this << " async_read " << bytes_transferred
Ed Tanous1abe55e2018-09-05 08:30:59 -0700758 << " Bytes";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700759
760 bool errorWhileReading = false;
761 if (ec)
762 {
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100763 BMCWEB_LOG_ERROR
764 << this << " Error while reading: " << ec.message();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700765 errorWhileReading = true;
766 }
767 else
768 {
Ed Tanouse278c182019-03-13 16:23:37 -0700769 if (!isAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700770 {
771 errorWhileReading = true;
772 }
773 }
774 if (errorWhileReading)
775 {
776 cancelDeadlineTimer();
Ed Tanouse278c182019-03-13 16:23:37 -0700777 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700778 BMCWEB_LOG_DEBUG << this << " from read(1)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700779 return;
780 }
781 handle();
782 });
783 }
784
785 void doWrite()
786 {
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100787 BMCWEB_LOG_DEBUG << this << " doWrite";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700788 res.preparePayload();
789 serializer.emplace(*res.stringResponse);
790 boost::beast::http::async_write(
Ed Tanousceac6f72018-12-02 11:58:47 -0800791 adaptor, *serializer,
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000792 [this,
793 self(shared_from_this())](const boost::system::error_code& ec,
794 std::size_t bytes_transferred) {
Zbigniew Kurzynski2658d982019-11-19 18:01:08 +0100795 BMCWEB_LOG_DEBUG << this << " async_write " << bytes_transferred
Ed Tanous1abe55e2018-09-05 08:30:59 -0700796 << " bytes";
797
798 if (ec)
799 {
800 BMCWEB_LOG_DEBUG << this << " from write(2)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700801 return;
802 }
Ed Tanousceac6f72018-12-02 11:58:47 -0800803 if (!res.keepAlive())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700804 {
Ed Tanouse278c182019-03-13 16:23:37 -0700805 close();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700806 BMCWEB_LOG_DEBUG << this << " from write(1)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700807 return;
808 }
809
810 serializer.reset();
811 BMCWEB_LOG_DEBUG << this << " Clearing response";
812 res.clear();
813 parser.emplace(std::piecewise_construct, std::make_tuple());
814 parser->body_limit(httpReqBodyLimit); // reset body limit for
815 // newly created parser
816 buffer.consume(buffer.size());
817
818 req.emplace(parser->get());
819 doReadHeaders();
820 });
821 }
822
Ed Tanous1abe55e2018-09-05 08:30:59 -0700823 void cancelDeadlineTimer()
824 {
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000825 if (timerCancelKey)
826 {
827 BMCWEB_LOG_DEBUG << this << " timer cancelled: " << &timerQueue
828 << ' ' << *timerCancelKey;
829 timerQueue.cancel(*timerCancelKey);
830 timerCancelKey.reset();
831 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700832 }
833
James Feistf0af8592020-03-27 16:28:59 -0700834 void startDeadline(size_t timerIterations = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700835 {
James Feistf0af8592020-03-27 16:28:59 -0700836 // drop all connections after 1 minute, this time limit was chosen
837 // arbitrarily and can be adjusted later if needed
838 constexpr const size_t maxReadAttempts =
839 (60 / detail::timerQueueTimeoutSeconds);
840
Ed Tanous1abe55e2018-09-05 08:30:59 -0700841 cancelDeadlineTimer();
842
James Feistf0af8592020-03-27 16:28:59 -0700843 timerCancelKey = timerQueue.add([this, self(shared_from_this()),
844 readCount{parser->get().body().size()},
845 timerIterations{timerIterations + 1}] {
846 // Mark timer as not active to avoid canceling it during
847 // Connection destructor which leads to double free issue
848 timerCancelKey.reset();
849 if (!isAlive())
850 {
851 return;
852 }
Jan Sowinski2b5e08e2020-01-09 17:16:02 +0100853
James Feistf0af8592020-03-27 16:28:59 -0700854 // Restart timer if read is in progress.
855 // With threshold can be used to drop slow connections
856 // to protect against slow-rate DoS attack
857 if ((parser->get().body().size() > readCount) &&
858 (timerIterations < maxReadAttempts))
859 {
860 BMCWEB_LOG_DEBUG << this << " restart timer - read in progress";
861 startDeadline(timerIterations);
862 return;
863 }
Jan Sowinski2b5e08e2020-01-09 17:16:02 +0100864
James Feistf0af8592020-03-27 16:28:59 -0700865 close();
866 });
James Feistcb6cb492020-04-03 13:36:17 -0700867
868 if (!timerCancelKey)
869 {
870 close();
871 return;
872 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700873 BMCWEB_LOG_DEBUG << this << " timer added: " << &timerQueue << ' '
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000874 << *timerCancelKey;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700875 }
876
877 private:
878 Adaptor adaptor;
879 Handler* handler;
880
Ed Tanousa24526d2018-12-10 15:17:59 -0800881 // Making this a std::optional allows it to be efficiently destroyed and
Ed Tanous1abe55e2018-09-05 08:30:59 -0700882 // re-created on Connection reset
Ed Tanousa24526d2018-12-10 15:17:59 -0800883 std::optional<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700884 boost::beast::http::request_parser<boost::beast::http::string_body>>
885 parser;
886
Ed Tanous3112a142018-11-29 15:45:10 -0800887 boost::beast::flat_static_buffer<8192> buffer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700888
Ed Tanousa24526d2018-12-10 15:17:59 -0800889 std::optional<boost::beast::http::response_serializer<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700890 boost::beast::http::string_body>>
891 serializer;
892
Ed Tanousa24526d2018-12-10 15:17:59 -0800893 std::optional<crow::Request> req;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700894 crow::Response res;
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200895#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
896 std::weak_ptr<crow::persistent_data::UserSession> session;
897#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanous1abe55e2018-09-05 08:30:59 -0700898
899 const std::string& serverName;
900
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000901 std::optional<size_t> timerCancelKey;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700902
Ed Tanous1abe55e2018-09-05 08:30:59 -0700903 bool needToCallAfterHandlers{};
904 bool needToStartReadAfterComplete{};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700905
906 std::tuple<Middlewares...>* middlewares;
907 detail::Context<Middlewares...> ctx;
908
909 std::function<std::string()>& getCachedDateStr;
910 detail::TimerQueue& timerQueue;
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000911
912 using std::enable_shared_from_this<
913 Connection<Adaptor, Handler, Middlewares...>>::shared_from_this;
Ed Tanous3112a142018-11-29 15:45:10 -0800914};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700915} // namespace crow