blob: 7110034cf2e24bc75122e8b857c9623a31dbadb7 [file] [log] [blame]
James Feist3909dc82020-04-03 10:58:55 -07001#pragma once
2
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08003#include "common.hpp"
4#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
Alan Kuof16f6262020-12-08 19:29:59 +080036#ifdef BMCWEB_ENABLE_BASIC_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -070037static std::shared_ptr<persistent_data::UserSession>
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -050038 performBasicAuth(const boost::asio::ip::address& clientIp,
Ed Tanous81ce6092020-12-17 16:54:55 +000039 std::string_view authHeader)
James Feist3909dc82020-04-03 10:58:55 -070040{
41 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Basic authentication";
42
Ed Tanous11ba3972022-07-11 09:50:41 -070043 if (!authHeader.starts_with("Basic "))
John Edward Broadbent97a056a2021-09-10 14:56:19 -070044 {
45 return nullptr;
46 }
47
Ed Tanous81ce6092020-12-17 16:54:55 +000048 std::string_view param = authHeader.substr(strlen("Basic "));
John Edward Broadbent97a056a2021-09-10 14:56:19 -070049 std::string authData;
50
James Feist3909dc82020-04-03 10:58:55 -070051 if (!crow::utility::base64Decode(param, authData))
52 {
53 return nullptr;
54 }
55 std::size_t separator = authData.find(':');
56 if (separator == std::string::npos)
57 {
58 return nullptr;
59 }
60
61 std::string user = authData.substr(0, separator);
62 separator += 1;
63 if (separator > authData.size())
64 {
65 return nullptr;
66 }
67 std::string pass = authData.substr(separator);
68
69 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Authenticating user: " << user;
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -050070 BMCWEB_LOG_DEBUG << "[AuthMiddleware] User IPAddress: "
71 << clientIp.to_string();
James Feist3909dc82020-04-03 10:58:55 -070072
73 int pamrc = pamAuthenticateUser(user, pass);
74 bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
75 if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
76 {
77 return nullptr;
78 }
79
80 // TODO(ed) generateUserSession is a little expensive for basic
81 // auth, as it generates some random identifiers that will never be
82 // used. This should have a "fast" path for when user tokens aren't
83 // needed.
84 // This whole flow needs to be revisited anyway, as we can't be
85 // calling directly into pam for every request
86 return persistent_data::SessionStore::getInstance().generateUserSession(
Ed Tanousbb759e32022-08-02 17:07:54 -070087 user, clientIp, std::nullopt,
Sunitha Harishd3239222021-02-24 15:33:29 +053088 persistent_data::PersistenceType::SINGLE_REQUEST, isConfigureSelfOnly);
James Feist3909dc82020-04-03 10:58:55 -070089}
Alan Kuof16f6262020-12-08 19:29:59 +080090#endif
James Feist3909dc82020-04-03 10:58:55 -070091
Alan Kuof16f6262020-12-08 19:29:59 +080092#ifdef BMCWEB_ENABLE_SESSION_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -070093static std::shared_ptr<persistent_data::UserSession>
Ed Tanous81ce6092020-12-17 16:54:55 +000094 performTokenAuth(std::string_view authHeader)
James Feist3909dc82020-04-03 10:58:55 -070095{
96 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Token authentication";
Ed Tanous11ba3972022-07-11 09:50:41 -070097 if (!authHeader.starts_with("Token "))
John Edward Broadbent97a056a2021-09-10 14:56:19 -070098 {
99 return nullptr;
100 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000101 std::string_view token = authHeader.substr(strlen("Token "));
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700102 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700103 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700104 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700105}
Alan Kuof16f6262020-12-08 19:29:59 +0800106#endif
James Feist3909dc82020-04-03 10:58:55 -0700107
Alan Kuof16f6262020-12-08 19:29:59 +0800108#ifdef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700109static std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700110 performXtokenAuth(const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700111{
112 BMCWEB_LOG_DEBUG << "[AuthMiddleware] X-Auth-Token authentication";
113
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700114 std::string_view token = reqHeader["X-Auth-Token"];
James Feist3909dc82020-04-03 10:58:55 -0700115 if (token.empty())
116 {
117 return nullptr;
118 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700119 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700120 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700121 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700122}
Alan Kuof16f6262020-12-08 19:29:59 +0800123#endif
James Feist3909dc82020-04-03 10:58:55 -0700124
Alan Kuof16f6262020-12-08 19:29:59 +0800125#ifdef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700126static std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700127 performCookieAuth(boost::beast::http::verb method,
128 const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700129{
130 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Cookie authentication";
131
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700132 std::string_view cookieValue = reqHeader["Cookie"];
James Feist3909dc82020-04-03 10:58:55 -0700133 if (cookieValue.empty())
134 {
135 return nullptr;
136 }
137
138 auto startIndex = cookieValue.find("SESSION=");
139 if (startIndex == std::string::npos)
140 {
141 return nullptr;
142 }
143 startIndex += sizeof("SESSION=") - 1;
Ed Tanous81ce6092020-12-17 16:54:55 +0000144 auto endIndex = cookieValue.find(';', startIndex);
James Feist3909dc82020-04-03 10:58:55 -0700145 if (endIndex == std::string::npos)
146 {
147 endIndex = cookieValue.size();
148 }
149 std::string_view authKey =
150 cookieValue.substr(startIndex, endIndex - startIndex);
151
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700152 std::shared_ptr<persistent_data::UserSession> sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700153 persistent_data::SessionStore::getInstance().loginSessionByToken(
154 authKey);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700155 if (sessionOut == nullptr)
James Feist3909dc82020-04-03 10:58:55 -0700156 {
157 return nullptr;
158 }
159#ifndef BMCWEB_INSECURE_DISABLE_CSRF_PREVENTION
160 // RFC7231 defines methods that need csrf protection
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700161 if (method != boost::beast::http::verb::get)
James Feist3909dc82020-04-03 10:58:55 -0700162 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700163 std::string_view csrf = reqHeader["X-XSRF-TOKEN"];
James Feist3909dc82020-04-03 10:58:55 -0700164 // Make sure both tokens are filled
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700165 if (csrf.empty() || sessionOut->csrfToken.empty())
James Feist3909dc82020-04-03 10:58:55 -0700166 {
167 return nullptr;
168 }
169
Ed Tanous52cc1122020-07-18 13:51:21 -0700170 if (csrf.size() != persistent_data::sessionTokenSize)
James Feist3909dc82020-04-03 10:58:55 -0700171 {
172 return nullptr;
173 }
174 // Reject if csrf token not available
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700175 if (!crow::utility::constantTimeStringCompare(csrf,
176 sessionOut->csrfToken))
James Feist3909dc82020-04-03 10:58:55 -0700177 {
178 return nullptr;
179 }
180 }
181#endif
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700182 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700183}
Alan Kuof16f6262020-12-08 19:29:59 +0800184#endif
James Feist3909dc82020-04-03 10:58:55 -0700185
Alexander Filippov96457602020-09-29 14:19:38 +0300186#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700187static std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700188 performTLSAuth(Response& res,
189 const boost::beast::http::header<true>& reqHeader,
Ed Tanousb5a76932020-09-29 16:16:58 -0700190 const std::weak_ptr<persistent_data::UserSession>& session)
James Feist6964c982020-07-28 16:10:23 -0700191{
James Feist6964c982020-07-28 16:10:23 -0700192 if (auto sp = session.lock())
193 {
194 // set cookie only if this is req from the browser.
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700195 if (reqHeader["User-Agent"].empty())
James Feist6964c982020-07-28 16:10:23 -0700196 {
197 BMCWEB_LOG_DEBUG << " TLS session: " << sp->uniqueId
198 << " will be used for this request.";
199 return sp;
200 }
Karol Niczyjade2fe72022-04-27 18:04:58 +0200201 // TODO: change this to not switch to cookie auth
202 res.addHeader("Set-Cookie",
203 "XSRF-TOKEN=" + sp->csrfToken +
204 "; SameSite=Strict; Secure\r\nSet-Cookie: SESSION=" +
205 sp->sessionToken +
206 "; SameSite=Strict; Secure; HttpOnly\r\nSet-Cookie: "
207 "IsAuthenticated=true; Secure");
208 BMCWEB_LOG_DEBUG << " TLS session: " << sp->uniqueId
209 << " with cookie will be used for this request.";
210 return sp;
James Feist6964c982020-07-28 16:10:23 -0700211 }
James Feist6964c982020-07-28 16:10:23 -0700212 return nullptr;
213}
Alexander Filippov96457602020-09-29 14:19:38 +0300214#endif
James Feist6964c982020-07-28 16:10:23 -0700215
James Feist3909dc82020-04-03 10:58:55 -0700216// checks if request can be forwarded without authentication
Nan Zhou8682c5a2021-11-13 11:00:07 -0800217[[maybe_unused]] static bool isOnAllowlist(std::string_view url,
218 boost::beast::http::verb method)
James Feist3909dc82020-04-03 10:58:55 -0700219{
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700220 if (boost::beast::http::verb::get == method)
James Feist3909dc82020-04-03 10:58:55 -0700221 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700222 if (url == "/redfish/v1" || url == "/redfish/v1/" ||
223 url == "/redfish" || url == "/redfish/" ||
224 url == "/redfish/v1/odata" || url == "/redfish/v1/odata/")
James Feist3909dc82020-04-03 10:58:55 -0700225 {
226 return true;
227 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700228 if (crow::webroutes::routes.find(std::string(url)) !=
Ed Tanousd4d25792020-09-29 15:15:03 -0700229 crow::webroutes::routes.end())
James Feist3909dc82020-04-03 10:58:55 -0700230 {
231 return true;
232 }
233 }
234
235 // it's allowed to POST on session collection & login without
236 // authentication
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700237 if (boost::beast::http::verb::post == method)
James Feist3909dc82020-04-03 10:58:55 -0700238 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700239 if ((url == "/redfish/v1/SessionService/Sessions") ||
240 (url == "/redfish/v1/SessionService/Sessions/") ||
Ed Tanouse76cd862022-03-14 09:12:00 -0700241 (url == "/redfish/v1/SessionService/Sessions/Members") ||
242 (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
Nan Zhou8682c5a2021-11-13 11:00:07 -0800252[[maybe_unused]] static std::shared_ptr<persistent_data::UserSession>
253 authenticate(
Ed Tanous02cad962022-06-30 16:50:15 -0700254 const boost::asio::ip::address& ipAddress [[maybe_unused]],
Nan Zhou3acced22022-07-12 23:21:02 +0000255 Response& res [[maybe_unused]],
256 boost::beast::http::verb method [[maybe_unused]],
Nan Zhou8682c5a2021-11-13 11:00:07 -0800257 const boost::beast::http::header<true>& reqHeader,
258 [[maybe_unused]] const std::shared_ptr<persistent_data::UserSession>&
259 session)
James Feist3909dc82020-04-03 10:58:55 -0700260{
Ed Tanous52cc1122020-07-18 13:51:21 -0700261 const persistent_data::AuthConfigMethods& authMethodsConfig =
262 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
James Feist3909dc82020-04-03 10:58:55 -0700263
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700264 std::shared_ptr<persistent_data::UserSession> sessionOut = nullptr;
Alexander Filippov96457602020-09-29 14:19:38 +0300265#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700266 if (authMethodsConfig.tls)
James Feist6964c982020-07-28 16:10:23 -0700267 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700268 sessionOut = performTLSAuth(res, reqHeader, session);
James Feist6964c982020-07-28 16:10:23 -0700269 }
Alexander Filippov96457602020-09-29 14:19:38 +0300270#endif
Alan Kuof16f6262020-12-08 19:29:59 +0800271#ifdef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700272 if (sessionOut == nullptr && authMethodsConfig.xtoken)
James Feist3909dc82020-04-03 10:58:55 -0700273 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700274 sessionOut = performXtokenAuth(reqHeader);
James Feist3909dc82020-04-03 10:58:55 -0700275 }
Alan Kuof16f6262020-12-08 19:29:59 +0800276#endif
277#ifdef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700278 if (sessionOut == nullptr && authMethodsConfig.cookie)
James Feist3909dc82020-04-03 10:58:55 -0700279 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700280 sessionOut = performCookieAuth(method, reqHeader);
James Feist3909dc82020-04-03 10:58:55 -0700281 }
Alan Kuof16f6262020-12-08 19:29:59 +0800282#endif
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700283 std::string_view authHeader = reqHeader["Authorization"];
Andrew Geissler1c801e12021-10-08 14:36:01 -0500284 BMCWEB_LOG_DEBUG << "authHeader=" << authHeader;
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700285
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700286 if (sessionOut == nullptr && authMethodsConfig.sessionToken)
James Feist3909dc82020-04-03 10:58:55 -0700287 {
Alan Kuof16f6262020-12-08 19:29:59 +0800288#ifdef BMCWEB_ENABLE_SESSION_AUTHENTICATION
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700289 sessionOut = performTokenAuth(authHeader);
Alan Kuof16f6262020-12-08 19:29:59 +0800290#endif
James Feist3909dc82020-04-03 10:58:55 -0700291 }
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700292 if (sessionOut == nullptr && authMethodsConfig.basic)
293 {
294#ifdef BMCWEB_ENABLE_BASIC_AUTHENTICATION
295 sessionOut = performBasicAuth(ipAddress, authHeader);
296#endif
297 }
298 if (sessionOut != nullptr)
299 {
300 return sessionOut;
301 }
302
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700303 return nullptr;
James Feist3909dc82020-04-03 10:58:55 -0700304}
305
Nan Zhoud055a342022-05-25 01:15:34 +0000306} // namespace authentication
James Feist3909dc82020-04-03 10:58:55 -0700307} // namespace crow