blob: ad9759bf49c40cd976d44b70767557cbe8cd06e9 [file] [log] [blame]
James Feist3909dc82020-04-03 10:58:55 -07001#pragma once
2
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08003#include "forward_unauthorized.hpp"
4#include "http_request.hpp"
5#include "http_response.hpp"
6#include "http_utility.hpp"
7#include "pam_authenticate.hpp"
James Feist3909dc82020-04-03 10:58:55 -07008#include "webroutes.hpp"
9
James Feist3909dc82020-04-03 10:58:55 -070010#include <boost/container/flat_set.hpp>
James Feist3909dc82020-04-03 10:58:55 -070011
12#include <random>
Ed Tanousb5a76932020-09-29 16:16:58 -070013#include <utility>
James Feist3909dc82020-04-03 10:58:55 -070014
15namespace crow
16{
17
Nan Zhoud055a342022-05-25 01:15:34 +000018namespace authentication
James Feist3909dc82020-04-03 10:58:55 -070019{
20
Ed Tanousb1d736f2023-02-10 12:24:00 -080021inline void cleanupTempSession(const Request& req)
James Feist3909dc82020-04-03 10:58:55 -070022{
23 // TODO(ed) THis should really be handled by the persistent data
24 // middleware, but because it is upstream, it doesn't have access to the
25 // session information. Should the data middleware persist the current
26 // user session?
27 if (req.session != nullptr &&
28 req.session->persistence ==
Ed Tanous52cc1122020-07-18 13:51:21 -070029 persistent_data::PersistenceType::SINGLE_REQUEST)
James Feist3909dc82020-04-03 10:58:55 -070030 {
31 persistent_data::SessionStore::getInstance().removeSession(req.session);
32 }
33}
34
Alan Kuof16f6262020-12-08 19:29:59 +080035#ifdef BMCWEB_ENABLE_BASIC_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -070036static std::shared_ptr<persistent_data::UserSession>
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -050037 performBasicAuth(const boost::asio::ip::address& clientIp,
Ed Tanous81ce6092020-12-17 16:54:55 +000038 std::string_view authHeader)
James Feist3909dc82020-04-03 10:58:55 -070039{
Ed Tanous62598e32023-07-17 17:06:25 -070040 BMCWEB_LOG_DEBUG("[AuthMiddleware] Basic authentication");
James Feist3909dc82020-04-03 10:58:55 -070041
Ed Tanous11ba3972022-07-11 09:50:41 -070042 if (!authHeader.starts_with("Basic "))
John Edward Broadbent97a056a2021-09-10 14:56:19 -070043 {
44 return nullptr;
45 }
46
Ed Tanous81ce6092020-12-17 16:54:55 +000047 std::string_view param = authHeader.substr(strlen("Basic "));
John Edward Broadbent97a056a2021-09-10 14:56:19 -070048 std::string authData;
49
James Feist3909dc82020-04-03 10:58:55 -070050 if (!crow::utility::base64Decode(param, authData))
51 {
52 return nullptr;
53 }
54 std::size_t separator = authData.find(':');
55 if (separator == std::string::npos)
56 {
57 return nullptr;
58 }
59
60 std::string user = authData.substr(0, separator);
61 separator += 1;
62 if (separator > authData.size())
63 {
64 return nullptr;
65 }
66 std::string pass = authData.substr(separator);
67
Ed Tanous62598e32023-07-17 17:06:25 -070068 BMCWEB_LOG_DEBUG("[AuthMiddleware] Authenticating user: {}", user);
69 BMCWEB_LOG_DEBUG("[AuthMiddleware] User IPAddress: {}",
70 clientIp.to_string());
James Feist3909dc82020-04-03 10:58:55 -070071
72 int pamrc = pamAuthenticateUser(user, pass);
73 bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
74 if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
75 {
76 return nullptr;
77 }
78
79 // TODO(ed) generateUserSession is a little expensive for basic
80 // auth, as it generates some random identifiers that will never be
81 // used. This should have a "fast" path for when user tokens aren't
82 // needed.
83 // This whole flow needs to be revisited anyway, as we can't be
84 // calling directly into pam for every request
85 return persistent_data::SessionStore::getInstance().generateUserSession(
Ed Tanousbb759e32022-08-02 17:07:54 -070086 user, clientIp, std::nullopt,
Sunitha Harishd3239222021-02-24 15:33:29 +053087 persistent_data::PersistenceType::SINGLE_REQUEST, isConfigureSelfOnly);
James Feist3909dc82020-04-03 10:58:55 -070088}
Alan Kuof16f6262020-12-08 19:29:59 +080089#endif
James Feist3909dc82020-04-03 10:58:55 -070090
Alan Kuof16f6262020-12-08 19:29:59 +080091#ifdef BMCWEB_ENABLE_SESSION_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -070092static std::shared_ptr<persistent_data::UserSession>
Ed Tanous81ce6092020-12-17 16:54:55 +000093 performTokenAuth(std::string_view authHeader)
James Feist3909dc82020-04-03 10:58:55 -070094{
Ed Tanous62598e32023-07-17 17:06:25 -070095 BMCWEB_LOG_DEBUG("[AuthMiddleware] Token authentication");
Ed Tanous11ba3972022-07-11 09:50:41 -070096 if (!authHeader.starts_with("Token "))
John Edward Broadbent97a056a2021-09-10 14:56:19 -070097 {
98 return nullptr;
99 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000100 std::string_view token = authHeader.substr(strlen("Token "));
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700101 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700102 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700103 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700104}
Alan Kuof16f6262020-12-08 19:29:59 +0800105#endif
James Feist3909dc82020-04-03 10:58:55 -0700106
Alan Kuof16f6262020-12-08 19:29:59 +0800107#ifdef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700108static std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700109 performXtokenAuth(const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700110{
Ed Tanous62598e32023-07-17 17:06:25 -0700111 BMCWEB_LOG_DEBUG("[AuthMiddleware] X-Auth-Token authentication");
James Feist3909dc82020-04-03 10:58:55 -0700112
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700113 std::string_view token = reqHeader["X-Auth-Token"];
James Feist3909dc82020-04-03 10:58:55 -0700114 if (token.empty())
115 {
116 return nullptr;
117 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700118 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700119 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700120 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700121}
Alan Kuof16f6262020-12-08 19:29:59 +0800122#endif
James Feist3909dc82020-04-03 10:58:55 -0700123
Alan Kuof16f6262020-12-08 19:29:59 +0800124#ifdef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700125static std::shared_ptr<persistent_data::UserSession>
Ed Tanous1d869602022-12-19 13:48:12 -0800126 performCookieAuth(boost::beast::http::verb method [[maybe_unused]],
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700127 const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700128{
Ed Tanous9f217c22024-04-19 17:22:43 -0700129 using headers = boost::beast::http::header<true>;
130 std::pair<headers::const_iterator, headers::const_iterator> cookies =
131 reqHeader.equal_range(boost::beast::http::field::cookie);
James Feist3909dc82020-04-03 10:58:55 -0700132
Ed Tanous9f217c22024-04-19 17:22:43 -0700133 for (auto it = cookies.first; it != cookies.second; it++)
James Feist3909dc82020-04-03 10:58:55 -0700134 {
Ed Tanous9f217c22024-04-19 17:22:43 -0700135 std::string_view cookieValue = it->value();
136 BMCWEB_LOG_DEBUG("Checking cookie {}", cookieValue);
137 auto startIndex = cookieValue.find("SESSION=");
138 if (startIndex == std::string::npos)
139 {
140 BMCWEB_LOG_DEBUG(
141 "Cookie was present, but didn't look like a session {}",
142 cookieValue);
143 continue;
144 }
145 startIndex += sizeof("SESSION=") - 1;
146 auto endIndex = cookieValue.find(';', startIndex);
147 if (endIndex == std::string::npos)
148 {
149 endIndex = cookieValue.size();
150 }
151 std::string_view authKey = cookieValue.substr(startIndex,
152 endIndex - startIndex);
James Feist3909dc82020-04-03 10:58:55 -0700153
Ed Tanous9f217c22024-04-19 17:22:43 -0700154 std::shared_ptr<persistent_data::UserSession> sessionOut =
155 persistent_data::SessionStore::getInstance().loginSessionByToken(
156 authKey);
157 if (sessionOut == nullptr)
158 {
159 return nullptr;
160 }
161 sessionOut->cookieAuth = true;
James Feist3909dc82020-04-03 10:58:55 -0700162#ifndef BMCWEB_INSECURE_DISABLE_CSRF_PREVENTION
Ed Tanous9f217c22024-04-19 17:22:43 -0700163 // RFC7231 defines methods that need csrf protection
164 if (method != boost::beast::http::verb::get)
James Feist3909dc82020-04-03 10:58:55 -0700165 {
Ed Tanous9f217c22024-04-19 17:22:43 -0700166 std::string_view csrf = reqHeader["X-XSRF-TOKEN"];
167 // Make sure both tokens are filled
168 if (csrf.empty() || sessionOut->csrfToken.empty())
169 {
170 return nullptr;
171 }
James Feist3909dc82020-04-03 10:58:55 -0700172
Ed Tanous9f217c22024-04-19 17:22:43 -0700173 if (csrf.size() != persistent_data::sessionTokenSize)
174 {
175 return nullptr;
176 }
177 // Reject if csrf token not available
178 if (!crow::utility::constantTimeStringCompare(
179 csrf, sessionOut->csrfToken))
180 {
181 return nullptr;
182 }
James Feist3909dc82020-04-03 10:58:55 -0700183 }
James Feist3909dc82020-04-03 10:58:55 -0700184#endif
Ed Tanous9f217c22024-04-19 17:22:43 -0700185 return sessionOut;
186 }
187 return nullptr;
James Feist3909dc82020-04-03 10:58:55 -0700188}
Alan Kuof16f6262020-12-08 19:29:59 +0800189#endif
James Feist3909dc82020-04-03 10:58:55 -0700190
Alexander Filippov96457602020-09-29 14:19:38 +0300191#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700192static std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700193 performTLSAuth(Response& res,
194 const boost::beast::http::header<true>& reqHeader,
Ed Tanousb5a76932020-09-29 16:16:58 -0700195 const std::weak_ptr<persistent_data::UserSession>& session)
James Feist6964c982020-07-28 16:10:23 -0700196{
James Feist6964c982020-07-28 16:10:23 -0700197 if (auto sp = session.lock())
198 {
199 // set cookie only if this is req from the browser.
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700200 if (reqHeader["User-Agent"].empty())
James Feist6964c982020-07-28 16:10:23 -0700201 {
Ed Tanous62598e32023-07-17 17:06:25 -0700202 BMCWEB_LOG_DEBUG(" TLS session: {} will be used for this request.",
203 sp->uniqueId);
James Feist6964c982020-07-28 16:10:23 -0700204 return sp;
205 }
Karol Niczyjade2fe72022-04-27 18:04:58 +0200206 // TODO: change this to not switch to cookie auth
Ed Tanous994fd862023-06-06 13:37:03 -0700207 res.addHeader(boost::beast::http::field::set_cookie,
Karol Niczyjade2fe72022-04-27 18:04:58 +0200208 "XSRF-TOKEN=" + sp->csrfToken +
Ed Tanous994fd862023-06-06 13:37:03 -0700209 "; SameSite=Strict; Secure");
210 res.addHeader(boost::beast::http::field::set_cookie,
211 "SESSION=" + sp->sessionToken +
212 "; SameSite=Strict; Secure; HttpOnly");
213 res.addHeader(boost::beast::http::field::set_cookie,
214 "IsAuthenticated=true; Secure");
Ed Tanous62598e32023-07-17 17:06:25 -0700215 BMCWEB_LOG_DEBUG(
216 " TLS session: {} with cookie will be used for this request.",
217 sp->uniqueId);
Karol Niczyjade2fe72022-04-27 18:04:58 +0200218 return sp;
James Feist6964c982020-07-28 16:10:23 -0700219 }
James Feist6964c982020-07-28 16:10:23 -0700220 return nullptr;
221}
Alexander Filippov96457602020-09-29 14:19:38 +0300222#endif
James Feist6964c982020-07-28 16:10:23 -0700223
James Feist3909dc82020-04-03 10:58:55 -0700224// checks if request can be forwarded without authentication
Nan Zhou8682c5a2021-11-13 11:00:07 -0800225[[maybe_unused]] static bool isOnAllowlist(std::string_view url,
226 boost::beast::http::verb method)
James Feist3909dc82020-04-03 10:58:55 -0700227{
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700228 if (boost::beast::http::verb::get == method)
James Feist3909dc82020-04-03 10:58:55 -0700229 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700230 if (url == "/redfish/v1" || url == "/redfish/v1/" ||
231 url == "/redfish" || url == "/redfish/" ||
232 url == "/redfish/v1/odata" || url == "/redfish/v1/odata/")
James Feist3909dc82020-04-03 10:58:55 -0700233 {
234 return true;
235 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700236 if (crow::webroutes::routes.find(std::string(url)) !=
Ed Tanousd4d25792020-09-29 15:15:03 -0700237 crow::webroutes::routes.end())
James Feist3909dc82020-04-03 10:58:55 -0700238 {
239 return true;
240 }
241 }
242
243 // it's allowed to POST on session collection & login without
244 // authentication
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700245 if (boost::beast::http::verb::post == method)
James Feist3909dc82020-04-03 10:58:55 -0700246 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700247 if ((url == "/redfish/v1/SessionService/Sessions") ||
248 (url == "/redfish/v1/SessionService/Sessions/") ||
Ed Tanouse76cd862022-03-14 09:12:00 -0700249 (url == "/redfish/v1/SessionService/Sessions/Members") ||
250 (url == "/redfish/v1/SessionService/Sessions/Members/") ||
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700251 (url == "/login"))
James Feist3909dc82020-04-03 10:58:55 -0700252 {
253 return true;
254 }
255 }
256
257 return false;
258}
259
Nan Zhou8682c5a2021-11-13 11:00:07 -0800260[[maybe_unused]] static std::shared_ptr<persistent_data::UserSession>
261 authenticate(
Ed Tanous02cad962022-06-30 16:50:15 -0700262 const boost::asio::ip::address& ipAddress [[maybe_unused]],
Nan Zhou3acced22022-07-12 23:21:02 +0000263 Response& res [[maybe_unused]],
264 boost::beast::http::verb method [[maybe_unused]],
Nan Zhou8682c5a2021-11-13 11:00:07 -0800265 const boost::beast::http::header<true>& reqHeader,
266 [[maybe_unused]] const std::shared_ptr<persistent_data::UserSession>&
267 session)
James Feist3909dc82020-04-03 10:58:55 -0700268{
Ed Tanous52cc1122020-07-18 13:51:21 -0700269 const persistent_data::AuthConfigMethods& authMethodsConfig =
270 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
James Feist3909dc82020-04-03 10:58:55 -0700271
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700272 std::shared_ptr<persistent_data::UserSession> sessionOut = nullptr;
Alexander Filippov96457602020-09-29 14:19:38 +0300273#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700274 if (authMethodsConfig.tls)
James Feist6964c982020-07-28 16:10:23 -0700275 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700276 sessionOut = performTLSAuth(res, reqHeader, session);
James Feist6964c982020-07-28 16:10:23 -0700277 }
Alexander Filippov96457602020-09-29 14:19:38 +0300278#endif
Alan Kuof16f6262020-12-08 19:29:59 +0800279#ifdef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700280 if (sessionOut == nullptr && authMethodsConfig.xtoken)
James Feist3909dc82020-04-03 10:58:55 -0700281 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700282 sessionOut = performXtokenAuth(reqHeader);
James Feist3909dc82020-04-03 10:58:55 -0700283 }
Alan Kuof16f6262020-12-08 19:29:59 +0800284#endif
285#ifdef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700286 if (sessionOut == nullptr && authMethodsConfig.cookie)
James Feist3909dc82020-04-03 10:58:55 -0700287 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700288 sessionOut = performCookieAuth(method, reqHeader);
James Feist3909dc82020-04-03 10:58:55 -0700289 }
Alan Kuof16f6262020-12-08 19:29:59 +0800290#endif
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700291 std::string_view authHeader = reqHeader["Authorization"];
Ed Tanous62598e32023-07-17 17:06:25 -0700292 BMCWEB_LOG_DEBUG("authHeader={}", authHeader);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700293
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700294 if (sessionOut == nullptr && authMethodsConfig.sessionToken)
James Feist3909dc82020-04-03 10:58:55 -0700295 {
Alan Kuof16f6262020-12-08 19:29:59 +0800296#ifdef BMCWEB_ENABLE_SESSION_AUTHENTICATION
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700297 sessionOut = performTokenAuth(authHeader);
Alan Kuof16f6262020-12-08 19:29:59 +0800298#endif
James Feist3909dc82020-04-03 10:58:55 -0700299 }
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700300 if (sessionOut == nullptr && authMethodsConfig.basic)
301 {
302#ifdef BMCWEB_ENABLE_BASIC_AUTHENTICATION
303 sessionOut = performBasicAuth(ipAddress, authHeader);
304#endif
305 }
306 if (sessionOut != nullptr)
307 {
308 return sessionOut;
309 }
310
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700311 return nullptr;
James Feist3909dc82020-04-03 10:58:55 -0700312}
313
Nan Zhoud055a342022-05-25 01:15:34 +0000314} // namespace authentication
James Feist3909dc82020-04-03 10:58:55 -0700315} // namespace crow