blob: f8e74b9af029910f0cea1014adc3a8f13adac909 [file] [log] [blame]
James Feist3909dc82020-04-03 10:58:55 -07001#pragma once
2
3#include "webroutes.hpp"
4
Ed Tanous04e438c2020-10-03 08:06:26 -07005#include <app.hpp>
James Feist3909dc82020-04-03 10:58:55 -07006#include <boost/algorithm/string/predicate.hpp>
7#include <boost/container/flat_set.hpp>
Ed Tanous04e438c2020-10-03 08:06:26 -07008#include <common.hpp>
Ed Tanousd4b6c662021-03-10 13:29:30 -08009#include <forward_unauthorized.hpp>
Ed Tanous04e438c2020-10-03 08:06:26 -070010#include <http_request.hpp>
11#include <http_response.hpp>
James Feist3909dc82020-04-03 10:58:55 -070012#include <http_utility.hpp>
13#include <pam_authenticate.hpp>
James Feist3909dc82020-04-03 10:58:55 -070014
15#include <random>
Ed Tanousb5a76932020-09-29 16:16:58 -070016#include <utility>
James Feist3909dc82020-04-03 10:58:55 -070017
18namespace crow
19{
20
21namespace authorization
22{
23
24static void cleanupTempSession(Request& req)
25{
26 // TODO(ed) THis should really be handled by the persistent data
27 // middleware, but because it is upstream, it doesn't have access to the
28 // session information. Should the data middleware persist the current
29 // user session?
30 if (req.session != nullptr &&
31 req.session->persistence ==
Ed Tanous52cc1122020-07-18 13:51:21 -070032 persistent_data::PersistenceType::SINGLE_REQUEST)
James Feist3909dc82020-04-03 10:58:55 -070033 {
34 persistent_data::SessionStore::getInstance().removeSession(req.session);
35 }
36}
37
Alan Kuof16f6262020-12-08 19:29:59 +080038#ifdef BMCWEB_ENABLE_BASIC_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -070039static std::shared_ptr<persistent_data::UserSession>
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -050040 performBasicAuth(const boost::asio::ip::address& clientIp,
Ed Tanous81ce6092020-12-17 16:54:55 +000041 std::string_view authHeader)
James Feist3909dc82020-04-03 10:58:55 -070042{
43 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Basic authentication";
44
John Edward Broadbent97a056a2021-09-10 14:56:19 -070045 if (!boost::starts_with(authHeader, "Basic "))
46 {
47 return nullptr;
48 }
49
Ed Tanous81ce6092020-12-17 16:54:55 +000050 std::string_view param = authHeader.substr(strlen("Basic "));
John Edward Broadbent97a056a2021-09-10 14:56:19 -070051 std::string authData;
52
James Feist3909dc82020-04-03 10:58:55 -070053 if (!crow::utility::base64Decode(param, authData))
54 {
55 return nullptr;
56 }
57 std::size_t separator = authData.find(':');
58 if (separator == std::string::npos)
59 {
60 return nullptr;
61 }
62
63 std::string user = authData.substr(0, separator);
64 separator += 1;
65 if (separator > authData.size())
66 {
67 return nullptr;
68 }
69 std::string pass = authData.substr(separator);
70
71 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Authenticating user: " << user;
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -050072 BMCWEB_LOG_DEBUG << "[AuthMiddleware] User IPAddress: "
73 << clientIp.to_string();
James Feist3909dc82020-04-03 10:58:55 -070074
75 int pamrc = pamAuthenticateUser(user, pass);
76 bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
77 if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
78 {
79 return nullptr;
80 }
81
82 // TODO(ed) generateUserSession is a little expensive for basic
83 // auth, as it generates some random identifiers that will never be
84 // used. This should have a "fast" path for when user tokens aren't
85 // needed.
86 // This whole flow needs to be revisited anyway, as we can't be
87 // calling directly into pam for every request
Ed Tanouse05aec52022-01-25 10:28:56 -080088 std::string unsupportedClientId;
James Feist3909dc82020-04-03 10:58:55 -070089 return persistent_data::SessionStore::getInstance().generateUserSession(
Jiaqing Zhao41d61c82021-12-07 13:21:47 +080090 user, clientIp, unsupportedClientId,
Sunitha Harishd3239222021-02-24 15:33:29 +053091 persistent_data::PersistenceType::SINGLE_REQUEST, isConfigureSelfOnly);
James Feist3909dc82020-04-03 10:58:55 -070092}
Alan Kuof16f6262020-12-08 19:29:59 +080093#endif
James Feist3909dc82020-04-03 10:58:55 -070094
Alan Kuof16f6262020-12-08 19:29:59 +080095#ifdef BMCWEB_ENABLE_SESSION_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -070096static std::shared_ptr<persistent_data::UserSession>
Ed Tanous81ce6092020-12-17 16:54:55 +000097 performTokenAuth(std::string_view authHeader)
James Feist3909dc82020-04-03 10:58:55 -070098{
99 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Token authentication";
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700100 if (!boost::starts_with(authHeader, "Token "))
101 {
102 return nullptr;
103 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000104 std::string_view token = authHeader.substr(strlen("Token "));
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700105 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700106 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700107 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700108}
Alan Kuof16f6262020-12-08 19:29:59 +0800109#endif
James Feist3909dc82020-04-03 10:58:55 -0700110
Alan Kuof16f6262020-12-08 19:29:59 +0800111#ifdef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700112static std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700113 performXtokenAuth(const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700114{
115 BMCWEB_LOG_DEBUG << "[AuthMiddleware] X-Auth-Token authentication";
116
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700117 std::string_view token = reqHeader["X-Auth-Token"];
James Feist3909dc82020-04-03 10:58:55 -0700118 if (token.empty())
119 {
120 return nullptr;
121 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700122 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700123 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700124 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700125}
Alan Kuof16f6262020-12-08 19:29:59 +0800126#endif
James Feist3909dc82020-04-03 10:58:55 -0700127
Alan Kuof16f6262020-12-08 19:29:59 +0800128#ifdef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700129static std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700130 performCookieAuth(boost::beast::http::verb method,
131 const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700132{
133 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Cookie authentication";
134
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700135 std::string_view cookieValue = reqHeader["Cookie"];
James Feist3909dc82020-04-03 10:58:55 -0700136 if (cookieValue.empty())
137 {
138 return nullptr;
139 }
140
141 auto startIndex = cookieValue.find("SESSION=");
142 if (startIndex == std::string::npos)
143 {
144 return nullptr;
145 }
146 startIndex += sizeof("SESSION=") - 1;
Ed Tanous81ce6092020-12-17 16:54:55 +0000147 auto endIndex = cookieValue.find(';', startIndex);
James Feist3909dc82020-04-03 10:58:55 -0700148 if (endIndex == std::string::npos)
149 {
150 endIndex = cookieValue.size();
151 }
152 std::string_view authKey =
153 cookieValue.substr(startIndex, endIndex - startIndex);
154
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700155 std::shared_ptr<persistent_data::UserSession> sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700156 persistent_data::SessionStore::getInstance().loginSessionByToken(
157 authKey);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700158 if (sessionOut == nullptr)
James Feist3909dc82020-04-03 10:58:55 -0700159 {
160 return nullptr;
161 }
162#ifndef BMCWEB_INSECURE_DISABLE_CSRF_PREVENTION
163 // RFC7231 defines methods that need csrf protection
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700164 if (method != boost::beast::http::verb::get)
James Feist3909dc82020-04-03 10:58:55 -0700165 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700166 std::string_view csrf = reqHeader["X-XSRF-TOKEN"];
James Feist3909dc82020-04-03 10:58:55 -0700167 // Make sure both tokens are filled
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700168 if (csrf.empty() || sessionOut->csrfToken.empty())
James Feist3909dc82020-04-03 10:58:55 -0700169 {
170 return nullptr;
171 }
172
Ed Tanous52cc1122020-07-18 13:51:21 -0700173 if (csrf.size() != persistent_data::sessionTokenSize)
James Feist3909dc82020-04-03 10:58:55 -0700174 {
175 return nullptr;
176 }
177 // Reject if csrf token not available
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700178 if (!crow::utility::constantTimeStringCompare(csrf,
179 sessionOut->csrfToken))
James Feist3909dc82020-04-03 10:58:55 -0700180 {
181 return nullptr;
182 }
183 }
184#endif
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700185 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700186}
Alan Kuof16f6262020-12-08 19:29:59 +0800187#endif
James Feist3909dc82020-04-03 10:58:55 -0700188
Alexander Filippov96457602020-09-29 14:19:38 +0300189#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700190static std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700191 performTLSAuth(Response& res,
192 const boost::beast::http::header<true>& reqHeader,
Ed Tanousb5a76932020-09-29 16:16:58 -0700193 const std::weak_ptr<persistent_data::UserSession>& session)
James Feist6964c982020-07-28 16:10:23 -0700194{
James Feist6964c982020-07-28 16:10:23 -0700195 if (auto sp = session.lock())
196 {
197 // set cookie only if this is req from the browser.
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700198 if (reqHeader["User-Agent"].empty())
James Feist6964c982020-07-28 16:10:23 -0700199 {
200 BMCWEB_LOG_DEBUG << " TLS session: " << sp->uniqueId
201 << " will be used for this request.";
202 return sp;
203 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700204 std::string_view cookieValue = reqHeader["Cookie"];
Ed Tanous3174e4d2020-10-07 11:41:22 -0700205 if (cookieValue.empty() ||
206 cookieValue.find("SESSION=") == std::string::npos)
James Feist6964c982020-07-28 16:10:23 -0700207 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700208 // TODO: change this to not switch to cookie auth
Gunnar Mills636be392021-03-15 12:47:07 -0500209 res.addHeader(
210 "Set-Cookie",
211 "XSRF-TOKEN=" + sp->csrfToken +
212 "; SameSite=Strict; Secure\r\nSet-Cookie: SESSION=" +
213 sp->sessionToken +
214 "; SameSite=Strict; Secure; HttpOnly\r\nSet-Cookie: "
215 "IsAuthenticated=true; Secure");
Ed Tanous3174e4d2020-10-07 11:41:22 -0700216 BMCWEB_LOG_DEBUG << " TLS session: " << sp->uniqueId
217 << " with cookie will be used for this request.";
218 return sp;
James Feist6964c982020-07-28 16:10:23 -0700219 }
220 }
James Feist6964c982020-07-28 16:10:23 -0700221 return nullptr;
222}
Alexander Filippov96457602020-09-29 14:19:38 +0300223#endif
James Feist6964c982020-07-28 16:10:23 -0700224
James Feist3909dc82020-04-03 10:58:55 -0700225// checks if request can be forwarded without authentication
Nan Zhou8682c5a2021-11-13 11:00:07 -0800226[[maybe_unused]] static bool isOnAllowlist(std::string_view url,
227 boost::beast::http::verb method)
James Feist3909dc82020-04-03 10:58:55 -0700228{
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700229 if (boost::beast::http::verb::get == method)
James Feist3909dc82020-04-03 10:58:55 -0700230 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700231 if (url == "/redfish/v1" || url == "/redfish/v1/" ||
232 url == "/redfish" || url == "/redfish/" ||
233 url == "/redfish/v1/odata" || url == "/redfish/v1/odata/")
James Feist3909dc82020-04-03 10:58:55 -0700234 {
235 return true;
236 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700237 if (crow::webroutes::routes.find(std::string(url)) !=
Ed Tanousd4d25792020-09-29 15:15:03 -0700238 crow::webroutes::routes.end())
James Feist3909dc82020-04-03 10:58:55 -0700239 {
240 return true;
241 }
242 }
243
244 // it's allowed to POST on session collection & login without
245 // authentication
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700246 if (boost::beast::http::verb::post == method)
James Feist3909dc82020-04-03 10:58:55 -0700247 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700248 if ((url == "/redfish/v1/SessionService/Sessions") ||
249 (url == "/redfish/v1/SessionService/Sessions/") ||
250 (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(
261 boost::asio::ip::address& ipAddress [[maybe_unused]],
262 Response& res [[maybe_unused]], boost::beast::http::verb method,
263 const boost::beast::http::header<true>& reqHeader,
264 [[maybe_unused]] const std::shared_ptr<persistent_data::UserSession>&
265 session)
James Feist3909dc82020-04-03 10:58:55 -0700266{
Ed Tanous52cc1122020-07-18 13:51:21 -0700267 const persistent_data::AuthConfigMethods& authMethodsConfig =
268 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
James Feist3909dc82020-04-03 10:58:55 -0700269
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700270 std::shared_ptr<persistent_data::UserSession> sessionOut = nullptr;
Alexander Filippov96457602020-09-29 14:19:38 +0300271#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700272 if (authMethodsConfig.tls)
James Feist6964c982020-07-28 16:10:23 -0700273 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700274 sessionOut = performTLSAuth(res, reqHeader, session);
James Feist6964c982020-07-28 16:10:23 -0700275 }
Alexander Filippov96457602020-09-29 14:19:38 +0300276#endif
Alan Kuof16f6262020-12-08 19:29:59 +0800277#ifdef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700278 if (sessionOut == nullptr && authMethodsConfig.xtoken)
James Feist3909dc82020-04-03 10:58:55 -0700279 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700280 sessionOut = performXtokenAuth(reqHeader);
James Feist3909dc82020-04-03 10:58:55 -0700281 }
Alan Kuof16f6262020-12-08 19:29:59 +0800282#endif
283#ifdef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700284 if (sessionOut == nullptr && authMethodsConfig.cookie)
James Feist3909dc82020-04-03 10:58:55 -0700285 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700286 sessionOut = performCookieAuth(method, reqHeader);
James Feist3909dc82020-04-03 10:58:55 -0700287 }
Alan Kuof16f6262020-12-08 19:29:59 +0800288#endif
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700289 std::string_view authHeader = reqHeader["Authorization"];
Andrew Geissler1c801e12021-10-08 14:36:01 -0500290 BMCWEB_LOG_DEBUG << "authHeader=" << authHeader;
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700291
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700292 if (sessionOut == nullptr && authMethodsConfig.sessionToken)
James Feist3909dc82020-04-03 10:58:55 -0700293 {
Alan Kuof16f6262020-12-08 19:29:59 +0800294#ifdef BMCWEB_ENABLE_SESSION_AUTHENTICATION
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700295 sessionOut = performTokenAuth(authHeader);
Alan Kuof16f6262020-12-08 19:29:59 +0800296#endif
James Feist3909dc82020-04-03 10:58:55 -0700297 }
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700298 if (sessionOut == nullptr && authMethodsConfig.basic)
299 {
300#ifdef BMCWEB_ENABLE_BASIC_AUTHENTICATION
301 sessionOut = performBasicAuth(ipAddress, authHeader);
302#endif
303 }
304 if (sessionOut != nullptr)
305 {
306 return sessionOut;
307 }
308
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700309 return nullptr;
James Feist3909dc82020-04-03 10:58:55 -0700310}
311
312} // namespace authorization
313} // namespace crow