blob: 93e9c8db796ab00a8340a30247116261bd3d92fe [file] [log] [blame]
James Feist3909dc82020-04-03 10:58:55 -07001#pragma once
2
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08003#include "app.hpp"
4#include "common.hpp"
5#include "forward_unauthorized.hpp"
6#include "http_request.hpp"
7#include "http_response.hpp"
8#include "http_utility.hpp"
9#include "pam_authenticate.hpp"
James Feist3909dc82020-04-03 10:58:55 -070010#include "webroutes.hpp"
11
James Feist3909dc82020-04-03 10:58:55 -070012#include <boost/container/flat_set.hpp>
James Feist3909dc82020-04-03 10:58:55 -070013
14#include <random>
Ed Tanousb5a76932020-09-29 16:16:58 -070015#include <utility>
James Feist3909dc82020-04-03 10:58:55 -070016
17namespace crow
18{
19
Nan Zhoud055a342022-05-25 01:15:34 +000020namespace authentication
James Feist3909dc82020-04-03 10:58:55 -070021{
22
Ed Tanous02cad962022-06-30 16:50:15 -070023static void cleanupTempSession(const Request& req)
James Feist3909dc82020-04-03 10:58:55 -070024{
25 // TODO(ed) THis should really be handled by the persistent data
26 // middleware, but because it is upstream, it doesn't have access to the
27 // session information. Should the data middleware persist the current
28 // user session?
29 if (req.session != nullptr &&
30 req.session->persistence ==
Ed Tanous52cc1122020-07-18 13:51:21 -070031 persistent_data::PersistenceType::SINGLE_REQUEST)
James Feist3909dc82020-04-03 10:58:55 -070032 {
33 persistent_data::SessionStore::getInstance().removeSession(req.session);
34 }
35}
36
Alan Kuof16f6262020-12-08 19:29:59 +080037#ifdef BMCWEB_ENABLE_BASIC_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -070038static std::shared_ptr<persistent_data::UserSession>
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -050039 performBasicAuth(const boost::asio::ip::address& clientIp,
Ed Tanous81ce6092020-12-17 16:54:55 +000040 std::string_view authHeader)
James Feist3909dc82020-04-03 10:58:55 -070041{
42 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Basic authentication";
43
Ed Tanous11ba3972022-07-11 09:50:41 -070044 if (!authHeader.starts_with("Basic "))
John Edward Broadbent97a056a2021-09-10 14:56:19 -070045 {
46 return nullptr;
47 }
48
Ed Tanous81ce6092020-12-17 16:54:55 +000049 std::string_view param = authHeader.substr(strlen("Basic "));
John Edward Broadbent97a056a2021-09-10 14:56:19 -070050 std::string authData;
51
James Feist3909dc82020-04-03 10:58:55 -070052 if (!crow::utility::base64Decode(param, authData))
53 {
54 return nullptr;
55 }
56 std::size_t separator = authData.find(':');
57 if (separator == std::string::npos)
58 {
59 return nullptr;
60 }
61
62 std::string user = authData.substr(0, separator);
63 separator += 1;
64 if (separator > authData.size())
65 {
66 return nullptr;
67 }
68 std::string pass = authData.substr(separator);
69
70 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Authenticating user: " << user;
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -050071 BMCWEB_LOG_DEBUG << "[AuthMiddleware] User IPAddress: "
72 << clientIp.to_string();
James Feist3909dc82020-04-03 10:58:55 -070073
74 int pamrc = pamAuthenticateUser(user, pass);
75 bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
76 if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
77 {
78 return nullptr;
79 }
80
81 // TODO(ed) generateUserSession is a little expensive for basic
82 // auth, as it generates some random identifiers that will never be
83 // used. This should have a "fast" path for when user tokens aren't
84 // needed.
85 // This whole flow needs to be revisited anyway, as we can't be
86 // calling directly into pam for every request
87 return persistent_data::SessionStore::getInstance().generateUserSession(
Ed Tanousbb759e32022-08-02 17:07:54 -070088 user, clientIp, std::nullopt,
Sunitha Harishd3239222021-02-24 15:33:29 +053089 persistent_data::PersistenceType::SINGLE_REQUEST, isConfigureSelfOnly);
James Feist3909dc82020-04-03 10:58:55 -070090}
Alan Kuof16f6262020-12-08 19:29:59 +080091#endif
James Feist3909dc82020-04-03 10:58:55 -070092
Alan Kuof16f6262020-12-08 19:29:59 +080093#ifdef BMCWEB_ENABLE_SESSION_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -070094static std::shared_ptr<persistent_data::UserSession>
Ed Tanous81ce6092020-12-17 16:54:55 +000095 performTokenAuth(std::string_view authHeader)
James Feist3909dc82020-04-03 10:58:55 -070096{
97 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Token authentication";
Ed Tanous11ba3972022-07-11 09:50:41 -070098 if (!authHeader.starts_with("Token "))
John Edward Broadbent97a056a2021-09-10 14:56:19 -070099 {
100 return nullptr;
101 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000102 std::string_view token = authHeader.substr(strlen("Token "));
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700103 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700104 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700105 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700106}
Alan Kuof16f6262020-12-08 19:29:59 +0800107#endif
James Feist3909dc82020-04-03 10:58:55 -0700108
Alan Kuof16f6262020-12-08 19:29:59 +0800109#ifdef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700110static std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700111 performXtokenAuth(const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700112{
113 BMCWEB_LOG_DEBUG << "[AuthMiddleware] X-Auth-Token authentication";
114
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700115 std::string_view token = reqHeader["X-Auth-Token"];
James Feist3909dc82020-04-03 10:58:55 -0700116 if (token.empty())
117 {
118 return nullptr;
119 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700120 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700121 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700122 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700123}
Alan Kuof16f6262020-12-08 19:29:59 +0800124#endif
James Feist3909dc82020-04-03 10:58:55 -0700125
Alan Kuof16f6262020-12-08 19:29:59 +0800126#ifdef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700127static std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700128 performCookieAuth(boost::beast::http::verb method,
129 const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700130{
131 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Cookie authentication";
132
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700133 std::string_view cookieValue = reqHeader["Cookie"];
James Feist3909dc82020-04-03 10:58:55 -0700134 if (cookieValue.empty())
135 {
136 return nullptr;
137 }
138
139 auto startIndex = cookieValue.find("SESSION=");
140 if (startIndex == std::string::npos)
141 {
142 return nullptr;
143 }
144 startIndex += sizeof("SESSION=") - 1;
Ed Tanous81ce6092020-12-17 16:54:55 +0000145 auto endIndex = cookieValue.find(';', startIndex);
James Feist3909dc82020-04-03 10:58:55 -0700146 if (endIndex == std::string::npos)
147 {
148 endIndex = cookieValue.size();
149 }
150 std::string_view authKey =
151 cookieValue.substr(startIndex, endIndex - startIndex);
152
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700153 std::shared_ptr<persistent_data::UserSession> sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700154 persistent_data::SessionStore::getInstance().loginSessionByToken(
155 authKey);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700156 if (sessionOut == nullptr)
James Feist3909dc82020-04-03 10:58:55 -0700157 {
158 return nullptr;
159 }
160#ifndef BMCWEB_INSECURE_DISABLE_CSRF_PREVENTION
161 // RFC7231 defines methods that need csrf protection
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700162 if (method != boost::beast::http::verb::get)
James Feist3909dc82020-04-03 10:58:55 -0700163 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700164 std::string_view csrf = reqHeader["X-XSRF-TOKEN"];
James Feist3909dc82020-04-03 10:58:55 -0700165 // Make sure both tokens are filled
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700166 if (csrf.empty() || sessionOut->csrfToken.empty())
James Feist3909dc82020-04-03 10:58:55 -0700167 {
168 return nullptr;
169 }
170
Ed Tanous52cc1122020-07-18 13:51:21 -0700171 if (csrf.size() != persistent_data::sessionTokenSize)
James Feist3909dc82020-04-03 10:58:55 -0700172 {
173 return nullptr;
174 }
175 // Reject if csrf token not available
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700176 if (!crow::utility::constantTimeStringCompare(csrf,
177 sessionOut->csrfToken))
James Feist3909dc82020-04-03 10:58:55 -0700178 {
179 return nullptr;
180 }
181 }
182#endif
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700183 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700184}
Alan Kuof16f6262020-12-08 19:29:59 +0800185#endif
James Feist3909dc82020-04-03 10:58:55 -0700186
Alexander Filippov96457602020-09-29 14:19:38 +0300187#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700188static std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700189 performTLSAuth(Response& res,
190 const boost::beast::http::header<true>& reqHeader,
Ed Tanousb5a76932020-09-29 16:16:58 -0700191 const std::weak_ptr<persistent_data::UserSession>& session)
James Feist6964c982020-07-28 16:10:23 -0700192{
James Feist6964c982020-07-28 16:10:23 -0700193 if (auto sp = session.lock())
194 {
195 // set cookie only if this is req from the browser.
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700196 if (reqHeader["User-Agent"].empty())
James Feist6964c982020-07-28 16:10:23 -0700197 {
198 BMCWEB_LOG_DEBUG << " TLS session: " << sp->uniqueId
199 << " will be used for this request.";
200 return sp;
201 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700202 std::string_view cookieValue = reqHeader["Cookie"];
Ed Tanous3174e4d2020-10-07 11:41:22 -0700203 if (cookieValue.empty() ||
204 cookieValue.find("SESSION=") == std::string::npos)
James Feist6964c982020-07-28 16:10:23 -0700205 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700206 // TODO: change this to not switch to cookie auth
Gunnar Mills636be392021-03-15 12:47:07 -0500207 res.addHeader(
208 "Set-Cookie",
209 "XSRF-TOKEN=" + sp->csrfToken +
210 "; SameSite=Strict; Secure\r\nSet-Cookie: SESSION=" +
211 sp->sessionToken +
212 "; SameSite=Strict; Secure; HttpOnly\r\nSet-Cookie: "
213 "IsAuthenticated=true; Secure");
Ed Tanous3174e4d2020-10-07 11:41:22 -0700214 BMCWEB_LOG_DEBUG << " TLS session: " << sp->uniqueId
215 << " with cookie will be used for this request.";
216 return sp;
James Feist6964c982020-07-28 16:10:23 -0700217 }
218 }
James Feist6964c982020-07-28 16:10:23 -0700219 return nullptr;
220}
Alexander Filippov96457602020-09-29 14:19:38 +0300221#endif
James Feist6964c982020-07-28 16:10:23 -0700222
James Feist3909dc82020-04-03 10:58:55 -0700223// checks if request can be forwarded without authentication
Nan Zhou8682c5a2021-11-13 11:00:07 -0800224[[maybe_unused]] static bool isOnAllowlist(std::string_view url,
225 boost::beast::http::verb method)
James Feist3909dc82020-04-03 10:58:55 -0700226{
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700227 if (boost::beast::http::verb::get == method)
James Feist3909dc82020-04-03 10:58:55 -0700228 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700229 if (url == "/redfish/v1" || url == "/redfish/v1/" ||
230 url == "/redfish" || url == "/redfish/" ||
231 url == "/redfish/v1/odata" || url == "/redfish/v1/odata/")
James Feist3909dc82020-04-03 10:58:55 -0700232 {
233 return true;
234 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700235 if (crow::webroutes::routes.find(std::string(url)) !=
Ed Tanousd4d25792020-09-29 15:15:03 -0700236 crow::webroutes::routes.end())
James Feist3909dc82020-04-03 10:58:55 -0700237 {
238 return true;
239 }
240 }
241
242 // it's allowed to POST on session collection & login without
243 // authentication
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700244 if (boost::beast::http::verb::post == method)
James Feist3909dc82020-04-03 10:58:55 -0700245 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700246 if ((url == "/redfish/v1/SessionService/Sessions") ||
247 (url == "/redfish/v1/SessionService/Sessions/") ||
Ed Tanouse76cd862022-03-14 09:12:00 -0700248 (url == "/redfish/v1/SessionService/Sessions/Members") ||
249 (url == "/redfish/v1/SessionService/Sessions/Members/") ||
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700250 (url == "/login"))
James Feist3909dc82020-04-03 10:58:55 -0700251 {
252 return true;
253 }
254 }
255
256 return false;
257}
258
Nan Zhou8682c5a2021-11-13 11:00:07 -0800259[[maybe_unused]] static std::shared_ptr<persistent_data::UserSession>
260 authenticate(
Ed Tanous02cad962022-06-30 16:50:15 -0700261 const boost::asio::ip::address& ipAddress [[maybe_unused]],
Nan Zhou3acced22022-07-12 23:21:02 +0000262 Response& res [[maybe_unused]],
263 boost::beast::http::verb method [[maybe_unused]],
Nan Zhou8682c5a2021-11-13 11:00:07 -0800264 const boost::beast::http::header<true>& reqHeader,
265 [[maybe_unused]] const std::shared_ptr<persistent_data::UserSession>&
266 session)
James Feist3909dc82020-04-03 10:58:55 -0700267{
Ed Tanous52cc1122020-07-18 13:51:21 -0700268 const persistent_data::AuthConfigMethods& authMethodsConfig =
269 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
James Feist3909dc82020-04-03 10:58:55 -0700270
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700271 std::shared_ptr<persistent_data::UserSession> sessionOut = nullptr;
Alexander Filippov96457602020-09-29 14:19:38 +0300272#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700273 if (authMethodsConfig.tls)
James Feist6964c982020-07-28 16:10:23 -0700274 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700275 sessionOut = performTLSAuth(res, reqHeader, session);
James Feist6964c982020-07-28 16:10:23 -0700276 }
Alexander Filippov96457602020-09-29 14:19:38 +0300277#endif
Alan Kuof16f6262020-12-08 19:29:59 +0800278#ifdef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700279 if (sessionOut == nullptr && authMethodsConfig.xtoken)
James Feist3909dc82020-04-03 10:58:55 -0700280 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700281 sessionOut = performXtokenAuth(reqHeader);
James Feist3909dc82020-04-03 10:58:55 -0700282 }
Alan Kuof16f6262020-12-08 19:29:59 +0800283#endif
284#ifdef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700285 if (sessionOut == nullptr && authMethodsConfig.cookie)
James Feist3909dc82020-04-03 10:58:55 -0700286 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700287 sessionOut = performCookieAuth(method, reqHeader);
James Feist3909dc82020-04-03 10:58:55 -0700288 }
Alan Kuof16f6262020-12-08 19:29:59 +0800289#endif
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700290 std::string_view authHeader = reqHeader["Authorization"];
Andrew Geissler1c801e12021-10-08 14:36:01 -0500291 BMCWEB_LOG_DEBUG << "authHeader=" << authHeader;
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700292
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700293 if (sessionOut == nullptr && authMethodsConfig.sessionToken)
James Feist3909dc82020-04-03 10:58:55 -0700294 {
Alan Kuof16f6262020-12-08 19:29:59 +0800295#ifdef BMCWEB_ENABLE_SESSION_AUTHENTICATION
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700296 sessionOut = performTokenAuth(authHeader);
Alan Kuof16f6262020-12-08 19:29:59 +0800297#endif
James Feist3909dc82020-04-03 10:58:55 -0700298 }
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700299 if (sessionOut == nullptr && authMethodsConfig.basic)
300 {
301#ifdef BMCWEB_ENABLE_BASIC_AUTHENTICATION
302 sessionOut = performBasicAuth(ipAddress, authHeader);
303#endif
304 }
305 if (sessionOut != nullptr)
306 {
307 return sessionOut;
308 }
309
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700310 return nullptr;
James Feist3909dc82020-04-03 10:58:55 -0700311}
312
Nan Zhoud055a342022-05-25 01:15:34 +0000313} // namespace authentication
James Feist3909dc82020-04-03 10:58:55 -0700314} // namespace crow