blob: 2c3a08a1564ab4323f2ae65397b2473813280c73 [file] [log] [blame]
James Feist3909dc82020-04-03 10:58:55 -07001#pragma once
2
Paul Fertser29aab242024-06-12 19:28:47 +00003#include "cookies.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08004#include "forward_unauthorized.hpp"
5#include "http_request.hpp"
6#include "http_response.hpp"
7#include "http_utility.hpp"
8#include "pam_authenticate.hpp"
James Feist3909dc82020-04-03 10:58:55 -07009#include "webroutes.hpp"
10
James Feist3909dc82020-04-03 10:58:55 -070011#include <boost/container/flat_set.hpp>
James Feist3909dc82020-04-03 10:58:55 -070012
13#include <random>
Ed Tanousb5a76932020-09-29 16:16:58 -070014#include <utility>
James Feist3909dc82020-04-03 10:58:55 -070015
16namespace crow
17{
18
Nan Zhoud055a342022-05-25 01:15:34 +000019namespace authentication
James Feist3909dc82020-04-03 10:58:55 -070020{
21
Ed Tanousb1d736f2023-02-10 12:24:00 -080022inline void cleanupTempSession(const Request& req)
James Feist3909dc82020-04-03 10:58:55 -070023{
24 // TODO(ed) THis should really be handled by the persistent data
25 // middleware, but because it is upstream, it doesn't have access to the
26 // session information. Should the data middleware persist the current
27 // user session?
28 if (req.session != nullptr &&
29 req.session->persistence ==
Ed Tanous52cc1122020-07-18 13:51:21 -070030 persistent_data::PersistenceType::SINGLE_REQUEST)
James Feist3909dc82020-04-03 10:58:55 -070031 {
32 persistent_data::SessionStore::getInstance().removeSession(req.session);
33 }
34}
35
Ed Tanous25b54db2024-04-17 15:40:31 -070036inline 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}
89
Ed Tanous25b54db2024-04-17 15:40:31 -070090inline std::shared_ptr<persistent_data::UserSession>
Ed Tanous81ce6092020-12-17 16:54:55 +000091 performTokenAuth(std::string_view authHeader)
James Feist3909dc82020-04-03 10:58:55 -070092{
Ed Tanous62598e32023-07-17 17:06:25 -070093 BMCWEB_LOG_DEBUG("[AuthMiddleware] Token authentication");
Ed Tanous11ba3972022-07-11 09:50:41 -070094 if (!authHeader.starts_with("Token "))
John Edward Broadbent97a056a2021-09-10 14:56:19 -070095 {
96 return nullptr;
97 }
Ed Tanous81ce6092020-12-17 16:54:55 +000098 std::string_view token = authHeader.substr(strlen("Token "));
John Edward Broadbent59b98b22021-07-13 15:36:32 -070099 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700100 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700101 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700102}
103
Ed Tanous25b54db2024-04-17 15:40:31 -0700104inline std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700105 performXtokenAuth(const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700106{
Ed Tanous62598e32023-07-17 17:06:25 -0700107 BMCWEB_LOG_DEBUG("[AuthMiddleware] X-Auth-Token authentication");
James Feist3909dc82020-04-03 10:58:55 -0700108
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700109 std::string_view token = reqHeader["X-Auth-Token"];
James Feist3909dc82020-04-03 10:58:55 -0700110 if (token.empty())
111 {
112 return nullptr;
113 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700114 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700115 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700116 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700117}
118
Ed Tanous25b54db2024-04-17 15:40:31 -0700119inline std::shared_ptr<persistent_data::UserSession>
Ed Tanous1d869602022-12-19 13:48:12 -0800120 performCookieAuth(boost::beast::http::verb method [[maybe_unused]],
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700121 const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700122{
Ed Tanous9f217c22024-04-19 17:22:43 -0700123 using headers = boost::beast::http::header<true>;
124 std::pair<headers::const_iterator, headers::const_iterator> cookies =
125 reqHeader.equal_range(boost::beast::http::field::cookie);
James Feist3909dc82020-04-03 10:58:55 -0700126
Ed Tanous9f217c22024-04-19 17:22:43 -0700127 for (auto it = cookies.first; it != cookies.second; it++)
James Feist3909dc82020-04-03 10:58:55 -0700128 {
Ed Tanous9f217c22024-04-19 17:22:43 -0700129 std::string_view cookieValue = it->value();
130 BMCWEB_LOG_DEBUG("Checking cookie {}", cookieValue);
131 auto startIndex = cookieValue.find("SESSION=");
132 if (startIndex == std::string::npos)
133 {
134 BMCWEB_LOG_DEBUG(
135 "Cookie was present, but didn't look like a session {}",
136 cookieValue);
137 continue;
138 }
139 startIndex += sizeof("SESSION=") - 1;
140 auto endIndex = cookieValue.find(';', startIndex);
141 if (endIndex == std::string::npos)
142 {
143 endIndex = cookieValue.size();
144 }
145 std::string_view authKey = cookieValue.substr(startIndex,
146 endIndex - startIndex);
James Feist3909dc82020-04-03 10:58:55 -0700147
Ed Tanous9f217c22024-04-19 17:22:43 -0700148 std::shared_ptr<persistent_data::UserSession> sessionOut =
149 persistent_data::SessionStore::getInstance().loginSessionByToken(
150 authKey);
151 if (sessionOut == nullptr)
152 {
153 return nullptr;
154 }
155 sessionOut->cookieAuth = true;
James Feist3909dc82020-04-03 10:58:55 -0700156
Ed Tanous576db692024-05-10 16:37:56 -0700157 if constexpr (!BMCWEB_INSECURE_DISABLE_CSRF)
Ed Tanous25b54db2024-04-17 15:40:31 -0700158 {
159 // RFC7231 defines methods that need csrf protection
160 if (method != boost::beast::http::verb::get)
Ed Tanous9f217c22024-04-19 17:22:43 -0700161 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700162 std::string_view csrf = reqHeader["X-XSRF-TOKEN"];
163 // Make sure both tokens are filled
164 if (csrf.empty() || sessionOut->csrfToken.empty())
165 {
166 return nullptr;
167 }
168
169 if (csrf.size() != persistent_data::sessionTokenSize)
170 {
171 return nullptr;
172 }
173 // Reject if csrf token not available
174 if (!crow::utility::constantTimeStringCompare(
175 csrf, sessionOut->csrfToken))
176 {
177 return nullptr;
178 }
Ed Tanous9f217c22024-04-19 17:22:43 -0700179 }
James Feist3909dc82020-04-03 10:58:55 -0700180 }
Ed Tanous3eaecac2024-05-08 16:26:24 -0700181 return sessionOut;
Ed Tanous9f217c22024-04-19 17:22:43 -0700182 }
183 return nullptr;
James Feist3909dc82020-04-03 10:58:55 -0700184}
185
Ed Tanous25b54db2024-04-17 15:40:31 -0700186inline std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700187 performTLSAuth(Response& res,
188 const boost::beast::http::header<true>& reqHeader,
Ed Tanousb5a76932020-09-29 16:16:58 -0700189 const std::weak_ptr<persistent_data::UserSession>& session)
James Feist6964c982020-07-28 16:10:23 -0700190{
James Feist6964c982020-07-28 16:10:23 -0700191 if (auto sp = session.lock())
192 {
193 // set cookie only if this is req from the browser.
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700194 if (reqHeader["User-Agent"].empty())
James Feist6964c982020-07-28 16:10:23 -0700195 {
Ed Tanous62598e32023-07-17 17:06:25 -0700196 BMCWEB_LOG_DEBUG(" TLS session: {} will be used for this request.",
197 sp->uniqueId);
James Feist6964c982020-07-28 16:10:23 -0700198 return sp;
199 }
Karol Niczyjade2fe72022-04-27 18:04:58 +0200200 // TODO: change this to not switch to cookie auth
Paul Fertser29aab242024-06-12 19:28:47 +0000201 bmcweb::setSessionCookies(res, *sp);
Ed Tanous994fd862023-06-06 13:37:03 -0700202 res.addHeader(boost::beast::http::field::set_cookie,
203 "IsAuthenticated=true; Secure");
Ed Tanous62598e32023-07-17 17:06:25 -0700204 BMCWEB_LOG_DEBUG(
205 " TLS session: {} with cookie will be used for this request.",
206 sp->uniqueId);
Karol Niczyjade2fe72022-04-27 18:04:58 +0200207 return sp;
James Feist6964c982020-07-28 16:10:23 -0700208 }
James Feist6964c982020-07-28 16:10:23 -0700209 return nullptr;
210}
211
James Feist3909dc82020-04-03 10:58:55 -0700212// checks if request can be forwarded without authentication
Ed Tanous25b54db2024-04-17 15:40:31 -0700213inline bool isOnAllowlist(std::string_view url, boost::beast::http::verb method)
James Feist3909dc82020-04-03 10:58:55 -0700214{
Ed Tanous38221502024-06-03 12:19:38 -0700215 // Handle the case where the router registers routes as both ending with /
216 // and not.
VinceChang66378f5df132024-06-14 17:13:16 +0800217 if (url.ends_with('/') && url != "/")
Ed Tanous38221502024-06-03 12:19:38 -0700218 {
219 url.remove_suffix(1);
220 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700221 if (boost::beast::http::verb::get == method)
James Feist3909dc82020-04-03 10:58:55 -0700222 {
Ed Tanous38221502024-06-03 12:19:38 -0700223 if ((url == "/redfish") || //
224 (url == "/redfish/v1") || //
225 (url == "/redfish/v1/odata") || //
226 (url == "/redfish/v1/$metadata"))
James Feist3909dc82020-04-03 10:58:55 -0700227 {
228 return true;
229 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700230 if (crow::webroutes::routes.find(std::string(url)) !=
Ed Tanousd4d25792020-09-29 15:15:03 -0700231 crow::webroutes::routes.end())
James Feist3909dc82020-04-03 10:58:55 -0700232 {
233 return true;
234 }
235 }
236
237 // it's allowed to POST on session collection & login without
238 // authentication
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700239 if (boost::beast::http::verb::post == method)
James Feist3909dc82020-04-03 10:58:55 -0700240 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700241 if ((url == "/redfish/v1/SessionService/Sessions") ||
Ed Tanouse76cd862022-03-14 09:12:00 -0700242 (url == "/redfish/v1/SessionService/Sessions/Members") ||
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700243 (url == "/login"))
James Feist3909dc82020-04-03 10:58:55 -0700244 {
245 return true;
246 }
247 }
248
249 return false;
250}
251
Ed Tanous25b54db2024-04-17 15:40:31 -0700252inline std::shared_ptr<persistent_data::UserSession> authenticate(
253 const boost::asio::ip::address& ipAddress [[maybe_unused]],
254 Response& res [[maybe_unused]],
255 boost::beast::http::verb method [[maybe_unused]],
256 const boost::beast::http::header<true>& reqHeader,
257 [[maybe_unused]] const std::shared_ptr<persistent_data::UserSession>&
258 session)
James Feist3909dc82020-04-03 10:58:55 -0700259{
Ed Tanous52cc1122020-07-18 13:51:21 -0700260 const persistent_data::AuthConfigMethods& authMethodsConfig =
261 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
James Feist3909dc82020-04-03 10:58:55 -0700262
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700263 std::shared_ptr<persistent_data::UserSession> sessionOut = nullptr;
Ed Tanous25b54db2024-04-17 15:40:31 -0700264 if constexpr (BMCWEB_MUTUAL_TLS_AUTH)
James Feist6964c982020-07-28 16:10:23 -0700265 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700266 if (authMethodsConfig.tls)
267 {
268 sessionOut = performTLSAuth(res, reqHeader, session);
269 }
James Feist6964c982020-07-28 16:10:23 -0700270 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700271 if constexpr (BMCWEB_XTOKEN_AUTH)
James Feist3909dc82020-04-03 10:58:55 -0700272 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700273 if (sessionOut == nullptr && authMethodsConfig.xtoken)
274 {
275 sessionOut = performXtokenAuth(reqHeader);
276 }
James Feist3909dc82020-04-03 10:58:55 -0700277 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700278 if constexpr (BMCWEB_COOKIE_AUTH)
James Feist3909dc82020-04-03 10:58:55 -0700279 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700280 if (sessionOut == nullptr && authMethodsConfig.cookie)
281 {
282 sessionOut = performCookieAuth(method, reqHeader);
283 }
James Feist3909dc82020-04-03 10:58:55 -0700284 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700285 std::string_view authHeader = reqHeader["Authorization"];
Ed Tanous62598e32023-07-17 17:06:25 -0700286 BMCWEB_LOG_DEBUG("authHeader={}", authHeader);
Ed Tanous25b54db2024-04-17 15:40:31 -0700287 if constexpr (BMCWEB_SESSION_AUTH)
James Feist3909dc82020-04-03 10:58:55 -0700288 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700289 if (sessionOut == nullptr && authMethodsConfig.sessionToken)
290 {
291 sessionOut = performTokenAuth(authHeader);
292 }
James Feist3909dc82020-04-03 10:58:55 -0700293 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700294 if constexpr (BMCWEB_BASIC_AUTH)
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700295 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700296 if (sessionOut == nullptr && authMethodsConfig.basic)
297 {
298 sessionOut = performBasicAuth(ipAddress, authHeader);
299 }
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700300 }
301 if (sessionOut != nullptr)
302 {
303 return sessionOut;
304 }
305
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700306 return nullptr;
James Feist3909dc82020-04-03 10:58:55 -0700307}
308
Nan Zhoud055a342022-05-25 01:15:34 +0000309} // namespace authentication
James Feist3909dc82020-04-03 10:58:55 -0700310} // namespace crow