blob: 9106764b1deadd9f6fe6abdc76e239f0ccb0d30e [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
Ed Tanous7045c8d2017-04-03 10:04:37 -07003#pragma once
Ed Tanous75312982021-02-11 14:26:02 -08004#include "bmcweb_config.h"
Adriana Kobylak0e1cf262019-12-05 13:57:57 -06005
Ed Tanousd093c992023-01-19 19:01:49 -08006#include "async_resp.hpp"
Nan Zhoud055a342022-05-25 01:15:34 +00007#include "authentication.hpp"
Ed Tanoused5f8952023-06-22 14:06:22 -07008#include "complete_response_fields.hpp"
Ed Tanousfca2cbe2021-01-28 14:49:59 -08009#include "http2_connection.hpp"
Ed Tanousb2896142024-01-31 15:25:47 -080010#include "http_body.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070011#include "http_response.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070012#include "http_utility.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070013#include "logging.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080014#include "mutual_tls.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080015#include "ssl_key_handler.hpp"
Ed Tanous18f8f602023-07-18 10:07:23 -070016#include "str_utility.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070017#include "utility.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070018
Ed Tanous8f626352018-12-19 14:51:54 -080019#include <boost/asio/io_context.hpp>
Ed Tanous3112a142018-11-29 15:45:10 -080020#include <boost/asio/ip/tcp.hpp>
Ed Tanousd43cd0c2020-09-30 20:46:53 -070021#include <boost/asio/ssl/stream.hpp>
Ed Tanous5dfb5b22021-12-03 11:24:53 -080022#include <boost/asio/steady_timer.hpp>
Ed Tanous4fa45df2023-09-01 14:20:50 -070023#include <boost/beast/_experimental/test/stream.hpp>
Ed Tanous4d698612024-02-06 14:57:24 -080024#include <boost/beast/core/buffers_generator.hpp>
Ed Tanous3112a142018-11-29 15:45:10 -080025#include <boost/beast/core/flat_static_buffer.hpp>
Myung Baea4326fe2023-01-10 14:29:24 -060026#include <boost/beast/http/error.hpp>
Ed Tanous4d698612024-02-06 14:57:24 -080027#include <boost/beast/http/message_generator.hpp>
Ed Tanous918ef252022-05-25 10:40:41 -070028#include <boost/beast/http/parser.hpp>
29#include <boost/beast/http/read.hpp>
Ed Tanous918ef252022-05-25 10:40:41 -070030#include <boost/beast/http/write.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050031#include <boost/beast/websocket.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050032
Manojkiran Eda44250442020-06-16 12:51:38 +053033#include <atomic>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050034#include <chrono>
Jonathan Doman102a4cd2024-04-15 16:56:23 -070035#include <memory>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050036#include <vector>
37
Ed Tanous1abe55e2018-09-05 08:30:59 -070038namespace crow
39{
Ed Tanous257f5792018-03-17 14:40:09 -070040
Ed Tanouscf9e4172022-12-21 09:30:16 -080041// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous6fbdbca2021-12-06 14:36:06 -080042static int connectionCount = 0;
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070043
Ed Tanous25b54db2024-04-17 15:40:31 -070044// request body limit size set by the BMCWEB_HTTP_BODY_LIMIT option
45constexpr uint64_t httpReqBodyLimit = 1024UL * 1024UL * BMCWEB_HTTP_BODY_LIMIT;
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070046
Ed Tanous1d1d7782024-04-09 12:54:08 -070047constexpr uint64_t loggedOutPostBodyLimit = 4096U;
James Feist3909dc82020-04-03 10:58:55 -070048
Ed Tanous1d1d7782024-04-09 12:54:08 -070049constexpr uint32_t httpHeaderLimit = 8192U;
James Feist3909dc82020-04-03 10:58:55 -070050
Ed Tanous4fa45df2023-09-01 14:20:50 -070051template <typename>
52struct IsTls : std::false_type
53{};
54
55template <typename T>
Ed Tanous003301a2024-04-16 09:59:19 -070056struct IsTls<boost::asio::ssl::stream<T>> : std::true_type
Ed Tanous4fa45df2023-09-01 14:20:50 -070057{};
58
Ed Tanous52cc1122020-07-18 13:51:21 -070059template <typename Adaptor, typename Handler>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050060class Connection :
Ed Tanous52cc1122020-07-18 13:51:21 -070061 public std::enable_shared_from_this<Connection<Adaptor, Handler>>
Ed Tanous1abe55e2018-09-05 08:30:59 -070062{
Ed Tanous7c8e0642022-02-21 12:11:14 -080063 using self_type = Connection<Adaptor, Handler>;
64
Ed Tanous1abe55e2018-09-05 08:30:59 -070065 public:
Ed Tanous5dfb5b22021-12-03 11:24:53 -080066 Connection(Handler* handlerIn, boost::asio::steady_timer&& timerIn,
Ed Tanous81ce6092020-12-17 16:54:55 +000067 std::function<std::string()>& getCachedDateStrF,
Ed Tanous3281bcf2024-06-25 16:02:05 -070068 Adaptor&& adaptorIn) :
Patrick Williamsbd79bce2024-08-16 15:22:20 -040069 adaptor(std::move(adaptorIn)), handler(handlerIn),
70 timer(std::move(timerIn)), getCachedDateStr(getCachedDateStrF)
Ed Tanous1abe55e2018-09-05 08:30:59 -070071 {
Ed Tanous1d1d7782024-04-09 12:54:08 -070072 initParser();
Kowalski, Kamil55e43f62019-07-10 13:12:57 +020073
Ed Tanous40aa0582021-07-14 13:24:40 -070074 connectionCount++;
Ed Tanous6fbdbca2021-12-06 14:36:06 -080075
Ed Tanous1d1d7782024-04-09 12:54:08 -070076 BMCWEB_LOG_DEBUG("{} Connection created, total {}", logPtr(this),
Ed Tanous62598e32023-07-17 17:06:25 -070077 connectionCount);
Ed Tanous40aa0582021-07-14 13:24:40 -070078 }
79
80 ~Connection()
81 {
Ed Tanous1d1d7782024-04-09 12:54:08 -070082 res.releaseCompleteRequestHandler();
Ed Tanous40aa0582021-07-14 13:24:40 -070083 cancelDeadlineTimer();
Ed Tanous6fbdbca2021-12-06 14:36:06 -080084
Ed Tanous40aa0582021-07-14 13:24:40 -070085 connectionCount--;
Ed Tanous62598e32023-07-17 17:06:25 -070086 BMCWEB_LOG_DEBUG("{} Connection closed, total {}", logPtr(this),
87 connectionCount);
Ed Tanous40aa0582021-07-14 13:24:40 -070088 }
89
Ed Tanousecd6a3a2022-01-07 09:18:40 -080090 Connection(const Connection&) = delete;
91 Connection(Connection&&) = delete;
92 Connection& operator=(const Connection&) = delete;
93 Connection& operator=(Connection&&) = delete;
94
Ed Tanous7c8e0642022-02-21 12:11:14 -080095 bool tlsVerifyCallback(bool preverified,
96 boost::asio::ssl::verify_context& ctx)
97 {
Ed Tanous3281bcf2024-06-25 16:02:05 -070098 BMCWEB_LOG_DEBUG("{} tlsVerifyCallback called with preverified {}",
99 logPtr(this), preverified);
Ed Tanous7c8e0642022-02-21 12:11:14 -0800100 if (preverified)
101 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700102 mtlsSession = verifyMtlsUser(ip, ctx);
Boleslaw Ogonczyk Makowskib4963072023-02-06 09:59:58 +0100103 if (mtlsSession)
Ed Tanous7c8e0642022-02-21 12:11:14 -0800104 {
Ed Tanous3281bcf2024-06-25 16:02:05 -0700105 BMCWEB_LOG_DEBUG("{} Generated TLS session: {}", logPtr(this),
Ed Tanous62598e32023-07-17 17:06:25 -0700106 mtlsSession->uniqueId);
Ed Tanous7c8e0642022-02-21 12:11:14 -0800107 }
108 }
Ed Tanous3281bcf2024-06-25 16:02:05 -0700109 const persistent_data::AuthConfigMethods& c =
110 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
111 if (c.tlsStrict)
112 {
Ed Tanous463a0e32024-10-14 11:21:48 -0700113 BMCWEB_LOG_DEBUG(
114 "{} TLS is in strict mode, returning preverified as is.",
115 logPtr(this));
Ed Tanous3281bcf2024-06-25 16:02:05 -0700116 return preverified;
117 }
118 // If tls strict mode is disabled
119 // We always return true to allow full auth flow for resources that
120 // don't require auth
Ed Tanous7c8e0642022-02-21 12:11:14 -0800121 return true;
122 }
123
Ed Tanous3281bcf2024-06-25 16:02:05 -0700124 bool prepareMutualTls()
Ed Tanous40aa0582021-07-14 13:24:40 -0700125 {
Ed Tanous4fa45df2023-09-01 14:20:50 -0700126 if constexpr (IsTls<Adaptor>::value)
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100127 {
Ed Tanous3281bcf2024-06-25 16:02:05 -0700128 BMCWEB_LOG_DEBUG("prepareMutualTls");
Zbigniew Kurzynski009c2a42019-11-14 13:37:15 +0100129
Ed Tanous3281bcf2024-06-25 16:02:05 -0700130 constexpr std::string_view id = "bmcweb";
131
132 const char* idPtr = id.data();
133 const auto* idCPtr = std::bit_cast<const unsigned char*>(idPtr);
134 auto idLen = static_cast<unsigned int>(id.length());
135 int ret = SSL_set_session_id_context(adaptor.native_handle(),
136 idCPtr, idLen);
137 if (ret == 0)
138 {
139 BMCWEB_LOG_ERROR("{} failed to set SSL id", logPtr(this));
140 return false;
Ed Tanous4fa45df2023-09-01 14:20:50 -0700141 }
142
Ed Tanous3281bcf2024-06-25 16:02:05 -0700143 BMCWEB_LOG_DEBUG("set_verify_callback");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700144
Ed Tanous3281bcf2024-06-25 16:02:05 -0700145 boost::system::error_code ec;
146 adaptor.set_verify_callback(
147 std::bind_front(&self_type::tlsVerifyCallback, this), ec);
148 if (ec)
149 {
150 BMCWEB_LOG_ERROR("Failed to set verify callback {}", ec);
151 return false;
152 }
153 }
154
155 return true;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700156 }
157
Ed Tanous1abe55e2018-09-05 08:30:59 -0700158 void start()
159 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700160 BMCWEB_LOG_DEBUG("{} Connection started, total {}", logPtr(this),
161 connectionCount);
Gunnar Mills4f63be02023-10-25 09:14:07 -0500162 if (connectionCount >= 200)
Ed Tanous6fbdbca2021-12-06 14:36:06 -0800163 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700164 BMCWEB_LOG_CRITICAL("{} Max connection count exceeded.",
Ed Tanous62598e32023-07-17 17:06:25 -0700165 logPtr(this));
Ed Tanous6fbdbca2021-12-06 14:36:06 -0800166 return;
167 }
168
Ed Tanous3281bcf2024-06-25 16:02:05 -0700169 if constexpr (BMCWEB_MUTUAL_TLS_AUTH)
170 {
171 if (!prepareMutualTls())
172 {
173 BMCWEB_LOG_ERROR("{} Failed to prepare mTLS", logPtr(this));
174 return;
175 }
176 }
177
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800178 startDeadline();
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500179
Ed Tanous1d1d7782024-04-09 12:54:08 -0700180 readClientIp();
181
Ed Tanousceac6f72018-12-02 11:58:47 -0800182 // TODO(ed) Abstract this to a more clever class with the idea of an
183 // asynchronous "start"
Ed Tanous4fa45df2023-09-01 14:20:50 -0700184 if constexpr (IsTls<Adaptor>::value)
Ed Tanousceac6f72018-12-02 11:58:47 -0800185 {
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000186 adaptor.async_handshake(boost::asio::ssl::stream_base::server,
187 [this, self(shared_from_this())](
188 const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400189 if (ec)
190 {
191 return;
192 }
193 afterSslHandshake();
194 });
Ed Tanousceac6f72018-12-02 11:58:47 -0800195 }
196 else
197 {
198 doReadHeaders();
199 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700200 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700201
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800202 void afterSslHandshake()
203 {
204 // If http2 is enabled, negotiate the protocol
Ed Tanous25b54db2024-04-17 15:40:31 -0700205 if constexpr (BMCWEB_EXPERIMENTAL_HTTP2)
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800206 {
207 const unsigned char* alpn = nullptr;
208 unsigned int alpnlen = 0;
209 SSL_get0_alpn_selected(adaptor.native_handle(), &alpn, &alpnlen);
210 if (alpn != nullptr)
211 {
212 std::string_view selectedProtocol(
213 std::bit_cast<const char*>(alpn), alpnlen);
Ed Tanous62598e32023-07-17 17:06:25 -0700214 BMCWEB_LOG_DEBUG("ALPN selected protocol \"{}\" len: {}",
215 selectedProtocol, alpnlen);
Ed Tanousfca2cbe2021-01-28 14:49:59 -0800216 if (selectedProtocol == "h2")
217 {
218 auto http2 =
219 std::make_shared<HTTP2Connection<Adaptor, Handler>>(
220 std::move(adaptor), handler, getCachedDateStr);
221 http2->start();
222 return;
223 }
224 }
225 }
226
227 doReadHeaders();
228 }
229
Ed Tanous1d1d7782024-04-09 12:54:08 -0700230 void initParser()
231 {
232 boost::beast::http::request_parser<bmcweb::HttpBody>& instance =
Ed Tanous38afdb92024-12-11 23:57:53 -0800233 parser.emplace();
Ed Tanous1d1d7782024-04-09 12:54:08 -0700234
235 // reset header limit for newly created parser
236 instance.header_limit(httpHeaderLimit);
237
238 // Initially set no body limit. We don't yet know if the user is
239 // authenticated.
240 instance.body_limit(boost::none);
241 }
242
Ed Tanous1abe55e2018-09-05 08:30:59 -0700243 void handle()
244 {
Ed Tanousf79b7a52021-09-22 19:04:29 -0700245 std::error_code reqEc;
Ed Tanouse01d0c32023-06-30 13:21:32 -0700246 if (!parser)
247 {
248 return;
249 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700250 req = std::make_shared<crow::Request>(parser->release(), reqEc);
Ed Tanousf79b7a52021-09-22 19:04:29 -0700251 if (reqEc)
252 {
Ed Tanous62598e32023-07-17 17:06:25 -0700253 BMCWEB_LOG_DEBUG("Request failed to construct{}", reqEc.message());
Gunnar Mills262f1152022-12-20 15:18:47 -0600254 res.result(boost::beast::http::status::bad_request);
255 completeRequest(res);
Ed Tanousf79b7a52021-09-22 19:04:29 -0700256 return;
257 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700258 req->session = userSession;
Ed Tanous89cda632024-04-16 08:45:54 -0700259 accept = req->getHeaderValue("Accept");
Ivan Mikhaylovf65b0be2021-04-19 10:05:30 +0000260 // Fetch the client IP address
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700261 req->ipAddress = ip;
Ivan Mikhaylovf65b0be2021-04-19 10:05:30 +0000262
Ed Tanous1abe55e2018-09-05 08:30:59 -0700263 // Check for HTTP version 1.1.
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700264 if (req->version() == 11)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700265 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700266 if (req->getHeaderValue(boost::beast::http::field::host).empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700267 {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700268 res.result(boost::beast::http::status::bad_request);
Nan Zhou72374eb2022-01-27 17:06:51 -0800269 completeRequest(res);
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700270 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700271 }
272 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700273
Ed Tanous62598e32023-07-17 17:06:25 -0700274 BMCWEB_LOG_INFO("Request: {} HTTP/{}.{} {} {} {}", logPtr(this),
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700275 req->version() / 10, req->version() % 10,
276 req->methodString(), req->target(),
277 req->ipAddress.to_string());
Ed Tanousd32c4fa2021-09-14 13:16:51 -0700278
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700279 req->ioService = static_cast<decltype(req->ioService)>(
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700280 &adaptor.get_executor().context());
Kowalski, Kamil55e43f62019-07-10 13:12:57 +0200281
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700282 if (res.completed)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700283 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800284 completeRequest(res);
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700285 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700286 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700287 keepAlive = req->keepAlive();
Ed Tanous4fa45df2023-09-01 14:20:50 -0700288 if constexpr (!std::is_same_v<Adaptor, boost::beast::test::stream>)
Ed Tanous1d3c14a2021-09-22 18:54:40 -0700289 {
Ed Tanous83328312024-05-09 15:48:09 -0700290 if constexpr (!BMCWEB_INSECURE_DISABLE_AUTH)
Ed Tanous4fa45df2023-09-01 14:20:50 -0700291 {
Ed Tanous83328312024-05-09 15:48:09 -0700292 if (!crow::authentication::isOnAllowlist(req->url().path(),
293 req->method()) &&
294 req->session == nullptr)
295 {
296 BMCWEB_LOG_WARNING("Authentication failed");
297 forward_unauthorized::sendUnauthorized(
298 req->url().encoded_path(),
299 req->getHeaderValue("X-Requested-With"),
300 req->getHeaderValue("Accept"), res);
301 completeRequest(res);
302 return;
303 }
Ed Tanous4fa45df2023-09-01 14:20:50 -0700304 }
Ed Tanous4fa45df2023-09-01 14:20:50 -0700305 }
Nan Zhou72374eb2022-01-27 17:06:51 -0800306 auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
Ed Tanous62598e32023-07-17 17:06:25 -0700307 BMCWEB_LOG_DEBUG("Setting completion handler");
Nan Zhou72374eb2022-01-27 17:06:51 -0800308 asyncResp->res.setCompleteRequestHandler(
309 [self(shared_from_this())](crow::Response& thisRes) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400310 self->completeRequest(thisRes);
311 });
Ed Tanous6fde95f2023-06-01 07:33:34 -0700312 bool isSse =
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700313 isContentTypeAllowed(req->getHeaderValue("Accept"),
Ed Tanous6fde95f2023-06-01 07:33:34 -0700314 http_helpers::ContentType::EventStream, false);
Ed Tanous18f8f602023-07-18 10:07:23 -0700315 std::string_view upgradeType(
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700316 req->getHeaderValue(boost::beast::http::field::upgrade));
317 if ((req->isUpgrade() &&
Ed Tanous18f8f602023-07-18 10:07:23 -0700318 bmcweb::asciiIEquals(upgradeType, "websocket")) ||
Ed Tanous6fde95f2023-06-01 07:33:34 -0700319 isSse)
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700320 {
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530321 asyncResp->res.setCompleteRequestHandler(
322 [self(shared_from_this())](crow::Response& thisRes) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400323 if (thisRes.result() != boost::beast::http::status::ok)
324 {
325 // When any error occurs before handle upgradation,
326 // the result in response will be set to respective
327 // error. By default the Result will be OK (200),
328 // which implies successful handle upgrade. Response
329 // needs to be sent over this connection only on
330 // failure.
331 self->completeRequest(thisRes);
332 return;
333 }
334 });
Ed Tanous52e31622024-01-23 16:31:11 -0800335 handler->handleUpgrade(req, asyncResp, std::move(adaptor));
Ed Tanous6c7f01d2021-08-25 13:42:35 -0700336 return;
337 }
Ed Tanous291d7092022-04-13 12:34:57 -0700338 std::string_view expected =
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700339 req->getHeaderValue(boost::beast::http::field::if_none_match);
Ed Tanous291d7092022-04-13 12:34:57 -0700340 if (!expected.empty())
341 {
342 res.setExpectedHash(expected);
343 }
Ed Tanous52e31622024-01-23 16:31:11 -0800344 handler->handle(req, asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700345 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700346
Ed Tanous1d1d7782024-04-09 12:54:08 -0700347 void hardClose()
Ed Tanouse278c182019-03-13 16:23:37 -0700348 {
Ed Tanous3281bcf2024-06-25 16:02:05 -0700349 if (mtlsSession != nullptr)
350 {
351 BMCWEB_LOG_DEBUG("{} Removing TLS session: {}", logPtr(this),
352 mtlsSession->uniqueId);
353 persistent_data::SessionStore::getInstance().removeSession(
354 mtlsSession);
355 }
Ed Tanous1d1d7782024-04-09 12:54:08 -0700356 BMCWEB_LOG_DEBUG("{} Closing socket", logPtr(this));
357 boost::beast::get_lowest_layer(adaptor).close();
358 }
359
360 void tlsShutdownComplete(const std::shared_ptr<self_type>& self,
361 const boost::system::error_code& ec)
362 {
363 if (ec)
364 {
365 BMCWEB_LOG_WARNING("{} Failed to shut down TLS cleanly {}",
366 logPtr(self.get()), ec);
367 }
368 self->hardClose();
369 }
370
371 void gracefulClose()
372 {
373 BMCWEB_LOG_DEBUG("{} Socket close requested", logPtr(this));
Ed Tanous3281bcf2024-06-25 16:02:05 -0700374
Ed Tanous4fa45df2023-09-01 14:20:50 -0700375 if constexpr (IsTls<Adaptor>::value)
Ed Tanouse278c182019-03-13 16:23:37 -0700376 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700377 adaptor.async_shutdown(std::bind_front(
378 &self_type::tlsShutdownComplete, this, shared_from_this()));
Ed Tanouse278c182019-03-13 16:23:37 -0700379 }
380 else
381 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700382 hardClose();
Ed Tanouse278c182019-03-13 16:23:37 -0700383 }
384 }
385
Nan Zhou72374eb2022-01-27 17:06:51 -0800386 void completeRequest(crow::Response& thisRes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700387 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800388 res = std::move(thisRes);
Ed Tanous4cdc2e82023-01-13 10:03:22 -0800389 res.keepAlive(keepAlive);
Ed Tanous5ae6f922023-01-09 10:45:53 -0800390
Ed Tanous89cda632024-04-16 08:45:54 -0700391 completeResponseFields(accept, res);
Ed Tanous998e0cb2023-09-06 13:57:30 -0700392 res.addHeader(boost::beast::http::field::date, getCachedDateStr());
Ed Tanous291d7092022-04-13 12:34:57 -0700393
Ed Tanous52e31622024-01-23 16:31:11 -0800394 doWrite();
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000395
396 // delete lambda with self shared_ptr
397 // to enable connection destruction
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700398 res.setCompleteRequestHandler(nullptr);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700399 }
400
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500401 void readClientIp()
402 {
403 boost::system::error_code ec;
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500404
Ed Tanous4fa45df2023-09-01 14:20:50 -0700405 if constexpr (!std::is_same_v<Adaptor, boost::beast::test::stream>)
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500406 {
Ed Tanous4fa45df2023-09-01 14:20:50 -0700407 boost::asio::ip::tcp::endpoint endpoint =
408 boost::beast::get_lowest_layer(adaptor).remote_endpoint(ec);
409
410 if (ec)
411 {
412 // If remote endpoint fails keep going. "ClientOriginIPAddress"
413 // will be empty.
414 BMCWEB_LOG_ERROR(
415 "Failed to get the client's IP Address. ec : {}", ec);
Ed Tanous1d1d7782024-04-09 12:54:08 -0700416 return;
Ed Tanous4fa45df2023-09-01 14:20:50 -0700417 }
418 ip = endpoint.address();
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500419 }
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500420 }
421
Ed Tanous1abe55e2018-09-05 08:30:59 -0700422 private:
Ed Tanous1d1d7782024-04-09 12:54:08 -0700423 uint64_t getContentLengthLimit()
424 {
Ed Tanous83328312024-05-09 15:48:09 -0700425 if constexpr (!BMCWEB_INSECURE_DISABLE_AUTH)
Ed Tanous1d1d7782024-04-09 12:54:08 -0700426 {
Ed Tanous83328312024-05-09 15:48:09 -0700427 if (userSession == nullptr)
428 {
429 return loggedOutPostBodyLimit;
430 }
Ed Tanous1d1d7782024-04-09 12:54:08 -0700431 }
Ed Tanous1d1d7782024-04-09 12:54:08 -0700432
433 return httpReqBodyLimit;
434 }
435
436 // Returns true if content length was within limits
437 // Returns false if content length error has been returned
438 bool handleContentLengthError()
439 {
440 if (!parser)
441 {
Manojkiran Edaefff2b52024-06-18 18:01:46 +0530442 BMCWEB_LOG_CRITICAL("Parser was null");
Ed Tanous1d1d7782024-04-09 12:54:08 -0700443 return false;
444 }
445 const boost::optional<uint64_t> contentLength =
446 parser->content_length();
447 if (!contentLength)
448 {
449 BMCWEB_LOG_DEBUG("{} No content length available", logPtr(this));
450 return true;
451 }
452
453 uint64_t maxAllowedContentLength = getContentLengthLimit();
454
455 if (*contentLength > maxAllowedContentLength)
456 {
457 // If the users content limit is between the logged in
458 // and logged out limits They probably just didn't log
459 // in
460 if (*contentLength > loggedOutPostBodyLimit &&
461 *contentLength < httpReqBodyLimit)
462 {
463 BMCWEB_LOG_DEBUG(
464 "{} Content length {} valid, but greater than logged out"
465 " limit of {}. Setting unauthorized",
466 logPtr(this), *contentLength, loggedOutPostBodyLimit);
467 res.result(boost::beast::http::status::unauthorized);
468 }
469 else
470 {
471 // Otherwise they're over both limits, so inform
472 // them
473 BMCWEB_LOG_DEBUG(
474 "{} Content length {} was greater than global limit {}."
475 " Setting payload too large",
476 logPtr(this), *contentLength, httpReqBodyLimit);
477 res.result(boost::beast::http::status::payload_too_large);
478 }
479
480 keepAlive = false;
481 doWrite();
482 return false;
483 }
484
485 return true;
486 }
487
Ed Tanous116370d2024-10-08 10:37:28 -0700488 void afterReadHeaders(const std::shared_ptr<self_type>& /*self*/,
489 const boost::system::error_code& ec,
490 std::size_t bytesTransferred)
491 {
492 BMCWEB_LOG_DEBUG("{} async_read_header {} Bytes", logPtr(this),
493 bytesTransferred);
494
495 if (ec)
496 {
497 cancelDeadlineTimer();
498
499 if (ec == boost::beast::http::error::header_limit)
500 {
501 BMCWEB_LOG_ERROR("{} Header field too large, closing",
502 logPtr(this), ec.message());
503
504 res.result(boost::beast::http::status::
505 request_header_fields_too_large);
506 keepAlive = false;
507 doWrite();
508 return;
509 }
510 if (ec == boost::beast::http::error::end_of_stream)
511 {
512 BMCWEB_LOG_WARNING("{} End of stream, closing {}", logPtr(this),
513 ec);
514 hardClose();
515 return;
516 }
517
518 BMCWEB_LOG_DEBUG("{} Closing socket due to read error {}",
519 logPtr(this), ec.message());
520 gracefulClose();
521
522 return;
523 }
524
525 if (!parser)
526 {
527 BMCWEB_LOG_ERROR("Parser was unexpectedly null");
528 return;
529 }
530
531 constexpr bool isTest =
532 std::is_same_v<Adaptor, boost::beast::test::stream>;
533
534 if constexpr (!BMCWEB_INSECURE_DISABLE_AUTH && !isTest)
535 {
536 boost::beast::http::verb method = parser->get().method();
537 userSession = crow::authentication::authenticate(
538 ip, res, method, parser->get().base(), mtlsSession);
539 }
540
541 std::string_view expect =
542 parser->get()[boost::beast::http::field::expect];
543 if (bmcweb::asciiIEquals(expect, "100-continue"))
544 {
545 res.result(boost::beast::http::status::continue_);
546 doWrite();
547 return;
548 }
549
550 if (!handleContentLengthError())
551 {
552 return;
553 }
554
555 parser->body_limit(getContentLengthLimit());
556
557 if (parser->is_done())
558 {
559 handle();
560 return;
561 }
562
563 doRead();
564 }
565
Ed Tanous1abe55e2018-09-05 08:30:59 -0700566 void doReadHeaders()
567 {
Ed Tanous62598e32023-07-17 17:06:25 -0700568 BMCWEB_LOG_DEBUG("{} doReadHeaders", logPtr(this));
Ed Tanouse01d0c32023-06-30 13:21:32 -0700569 if (!parser)
570 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700571 BMCWEB_LOG_CRITICAL("Parser was not initialized.");
Ed Tanouse01d0c32023-06-30 13:21:32 -0700572 return;
573 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700574 // Clean up any previous Connection.
575 boost::beast::http::async_read_header(
Ed Tanousceac6f72018-12-02 11:58:47 -0800576 adaptor, buffer, *parser,
Ed Tanous116370d2024-10-08 10:37:28 -0700577 std::bind_front(&self_type::afterReadHeaders, this,
578 shared_from_this()));
579 }
Ed Tanous52e31622024-01-23 16:31:11 -0800580
Ed Tanous116370d2024-10-08 10:37:28 -0700581 void afterRead(const std::shared_ptr<self_type>& /*self*/,
582 const boost::system::error_code& ec,
583 std::size_t bytesTransferred)
584 {
585 BMCWEB_LOG_DEBUG("{} async_read_some {} Bytes", logPtr(this),
586 bytesTransferred);
587
588 if (ec)
589 {
590 BMCWEB_LOG_ERROR("{} Error while reading: {}", logPtr(this),
591 ec.message());
592 if (ec == boost::beast::http::error::body_limit)
593 {
594 if (handleContentLengthError())
Ed Tanous1d1d7782024-04-09 12:54:08 -0700595 {
Ed Tanous116370d2024-10-08 10:37:28 -0700596 BMCWEB_LOG_CRITICAL("Body length limit reached, "
597 "but no content-length "
598 "available? Should never happen");
599 res.result(
600 boost::beast::http::status::internal_server_error);
601 keepAlive = false;
Ed Tanous1d1d7782024-04-09 12:54:08 -0700602 doWrite();
Ed Tanous1d1d7782024-04-09 12:54:08 -0700603 }
Ed Tanous116370d2024-10-08 10:37:28 -0700604 return;
605 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400606
Ed Tanous116370d2024-10-08 10:37:28 -0700607 gracefulClose();
608 return;
609 }
Ed Tanous1d1d7782024-04-09 12:54:08 -0700610
Ed Tanous116370d2024-10-08 10:37:28 -0700611 // If the user is logged in, allow them to send files
612 // incrementally one piece at a time. If authentication is
613 // disabled then there is no user session hence always allow to
614 // send one piece at a time.
615 if (userSession != nullptr)
616 {
617 cancelDeadlineTimer();
618 }
Ed Tanous1d1d7782024-04-09 12:54:08 -0700619
Ed Tanous116370d2024-10-08 10:37:28 -0700620 if (!parser)
621 {
622 BMCWEB_LOG_ERROR("Parser was unexpectedly null");
623 return;
624 }
625 if (!parser->is_done())
626 {
627 doRead();
628 return;
629 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700630
Ed Tanous116370d2024-10-08 10:37:28 -0700631 cancelDeadlineTimer();
632 handle();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633 }
634
635 void doRead()
636 {
Ed Tanous62598e32023-07-17 17:06:25 -0700637 BMCWEB_LOG_DEBUG("{} doRead", logPtr(this));
Ed Tanouse01d0c32023-06-30 13:21:32 -0700638 if (!parser)
639 {
640 return;
641 }
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800642 startDeadline();
Ed Tanous7d243eb2023-01-23 15:57:41 -0800643 boost::beast::http::async_read_some(
644 adaptor, buffer, *parser,
Ed Tanous116370d2024-10-08 10:37:28 -0700645 std::bind_front(&self_type::afterRead, this, shared_from_this()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700646 }
647
Ed Tanous27b0cf92023-08-07 12:02:40 -0700648 void afterDoWrite(const std::shared_ptr<self_type>& /*self*/,
649 const boost::system::error_code& ec,
650 std::size_t bytesTransferred)
651 {
Ed Tanous0242baf2024-05-16 19:52:47 -0700652 BMCWEB_LOG_DEBUG("{} async_write wrote {} bytes, ec={}", logPtr(this),
Ed Tanous1d1d7782024-04-09 12:54:08 -0700653 bytesTransferred, ec);
Ed Tanous27b0cf92023-08-07 12:02:40 -0700654
655 cancelDeadlineTimer();
656
Ed Tanous0242baf2024-05-16 19:52:47 -0700657 if (ec == boost::system::errc::operation_would_block ||
658 ec == boost::system::errc::resource_unavailable_try_again)
659 {
660 doWrite();
661 return;
662 }
Ed Tanous27b0cf92023-08-07 12:02:40 -0700663 if (ec)
664 {
665 BMCWEB_LOG_DEBUG("{} from write(2)", logPtr(this));
666 return;
667 }
Ed Tanous1d1d7782024-04-09 12:54:08 -0700668
669 if (res.result() == boost::beast::http::status::continue_)
670 {
671 // Reset the result to ok
672 res.result(boost::beast::http::status::ok);
673 doRead();
674 return;
675 }
676
Ed Tanous27b0cf92023-08-07 12:02:40 -0700677 if (!keepAlive)
678 {
Ed Tanous1d1d7782024-04-09 12:54:08 -0700679 BMCWEB_LOG_DEBUG("{} keepalive not set. Closing socket",
680 logPtr(this));
681
682 gracefulClose();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700683 return;
684 }
685
686 BMCWEB_LOG_DEBUG("{} Clearing response", logPtr(this));
687 res.clear();
Ed Tanous1d1d7782024-04-09 12:54:08 -0700688 initParser();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700689
690 userSession = nullptr;
691
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700692 req->clear();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700693 doReadHeaders();
694 }
695
Ed Tanous52e31622024-01-23 16:31:11 -0800696 void doWrite()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700697 {
Ed Tanous62598e32023-07-17 17:06:25 -0700698 BMCWEB_LOG_DEBUG("{} doWrite", logPtr(this));
Ed Tanous52e31622024-01-23 16:31:11 -0800699 res.preparePayload();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700700
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800701 startDeadline();
Ed Tanous4d698612024-02-06 14:57:24 -0800702 boost::beast::async_write(
703 adaptor,
704 boost::beast::http::message_generator(std::move(res.response)),
Ed Tanous52e31622024-01-23 16:31:11 -0800705 std::bind_front(&self_type::afterDoWrite, this,
706 shared_from_this()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700707 }
708
Ed Tanous1abe55e2018-09-05 08:30:59 -0700709 void cancelDeadlineTimer()
710 {
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800711 timer.cancel();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700712 }
713
Ed Tanous116370d2024-10-08 10:37:28 -0700714 void afterTimerWait(const std::weak_ptr<self_type>& weakSelf,
715 const boost::system::error_code& ec)
716 {
717 // Note, we are ignoring other types of errors here; If the timer
718 // failed for any reason, we should still close the connection
719 std::shared_ptr<Connection<Adaptor, Handler>> self = weakSelf.lock();
720 if (!self)
721 {
722 if (ec == boost::asio::error::operation_aborted)
723 {
724 BMCWEB_LOG_DEBUG(
725 "{} Timer canceled on connection being destroyed",
726 logPtr(self.get()));
727 }
728 else
729 {
730 BMCWEB_LOG_CRITICAL("{} Failed to capture connection",
731 logPtr(self.get()));
732 }
733 return;
734 }
735
736 self->timerStarted = false;
737
738 if (ec)
739 {
740 if (ec == boost::asio::error::operation_aborted)
741 {
742 BMCWEB_LOG_DEBUG("{} Timer canceled", logPtr(self.get()));
743 return;
744 }
745 BMCWEB_LOG_CRITICAL("{} Timer failed {}", logPtr(self.get()), ec);
746 }
747
748 BMCWEB_LOG_WARNING("{} Connection timed out, hard closing",
749 logPtr(self.get()));
750
751 self->hardClose();
752 }
753
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800754 void startDeadline()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700755 {
Ed Tanous7d243eb2023-01-23 15:57:41 -0800756 // Timer is already started so no further action is required.
757 if (timerStarted)
758 {
759 return;
760 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700761
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800762 std::chrono::seconds timeout(15);
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800763
764 std::weak_ptr<Connection<Adaptor, Handler>> weakSelf = weak_from_this();
765 timer.expires_after(timeout);
Ed Tanous116370d2024-10-08 10:37:28 -0700766 timer.async_wait(std::bind_front(&self_type::afterTimerWait, this,
767 weak_from_this()));
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800768
Ed Tanous7d243eb2023-01-23 15:57:41 -0800769 timerStarted = true;
Ed Tanous62598e32023-07-17 17:06:25 -0700770 BMCWEB_LOG_DEBUG("{} timer started", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700771 }
772
Ed Tanous1abe55e2018-09-05 08:30:59 -0700773 Adaptor adaptor;
774 Handler* handler;
Ed Tanous1d1d7782024-04-09 12:54:08 -0700775
776 boost::asio::ip::address ip;
777
Ed Tanousa24526d2018-12-10 15:17:59 -0800778 // Making this a std::optional allows it to be efficiently destroyed and
Ed Tanous1abe55e2018-09-05 08:30:59 -0700779 // re-created on Connection reset
Ed Tanousb2896142024-01-31 15:25:47 -0800780 std::optional<boost::beast::http::request_parser<bmcweb::HttpBody>> parser;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700781
Ed Tanous3112a142018-11-29 15:45:10 -0800782 boost::beast::flat_static_buffer<8192> buffer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700783
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700784 std::shared_ptr<crow::Request> req;
Ed Tanous89cda632024-04-16 08:45:54 -0700785 std::string accept;
786
Ed Tanous1abe55e2018-09-05 08:30:59 -0700787 crow::Response res;
Ed Tanous52cc1122020-07-18 13:51:21 -0700788
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700789 std::shared_ptr<persistent_data::UserSession> userSession;
Boleslaw Ogonczyk Makowskib4963072023-02-06 09:59:58 +0100790 std::shared_ptr<persistent_data::UserSession> mtlsSession;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700791
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800792 boost::asio::steady_timer timer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700793
Ed Tanous4cdc2e82023-01-13 10:03:22 -0800794 bool keepAlive = true;
795
Ed Tanous7d243eb2023-01-23 15:57:41 -0800796 bool timerStarted = false;
797
Ed Tanous1abe55e2018-09-05 08:30:59 -0700798 std::function<std::string()>& getCachedDateStr;
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000799
800 using std::enable_shared_from_this<
Ed Tanous52cc1122020-07-18 13:51:21 -0700801 Connection<Adaptor, Handler>>::shared_from_this;
Ed Tanous5dfb5b22021-12-03 11:24:53 -0800802
803 using std::enable_shared_from_this<
804 Connection<Adaptor, Handler>>::weak_from_this;
Ed Tanous3112a142018-11-29 15:45:10 -0800805};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700806} // namespace crow