blob: ecbdca04c0cf481865a29da0682f2bc00564ef92 [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
45 std::string authData;
Ed Tanous81ce6092020-12-17 16:54:55 +000046 std::string_view param = authHeader.substr(strlen("Basic "));
James Feist3909dc82020-04-03 10:58:55 -070047 if (!crow::utility::base64Decode(param, authData))
48 {
49 return nullptr;
50 }
51 std::size_t separator = authData.find(':');
52 if (separator == std::string::npos)
53 {
54 return nullptr;
55 }
56
57 std::string user = authData.substr(0, separator);
58 separator += 1;
59 if (separator > authData.size())
60 {
61 return nullptr;
62 }
63 std::string pass = authData.substr(separator);
64
65 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Authenticating user: " << user;
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -050066 BMCWEB_LOG_DEBUG << "[AuthMiddleware] User IPAddress: "
67 << clientIp.to_string();
James Feist3909dc82020-04-03 10:58:55 -070068
69 int pamrc = pamAuthenticateUser(user, pass);
70 bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
71 if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
72 {
73 return nullptr;
74 }
75
76 // TODO(ed) generateUserSession is a little expensive for basic
77 // auth, as it generates some random identifiers that will never be
78 // used. This should have a "fast" path for when user tokens aren't
79 // needed.
80 // This whole flow needs to be revisited anyway, as we can't be
81 // calling directly into pam for every request
Sunitha Harishd3239222021-02-24 15:33:29 +053082 std::string unsupportedClientId = "";
James Feist3909dc82020-04-03 10:58:55 -070083 return persistent_data::SessionStore::getInstance().generateUserSession(
Sunitha Harishd3239222021-02-24 15:33:29 +053084 user, clientIp.to_string(), unsupportedClientId,
85 persistent_data::PersistenceType::SINGLE_REQUEST, isConfigureSelfOnly);
James Feist3909dc82020-04-03 10:58:55 -070086}
Alan Kuof16f6262020-12-08 19:29:59 +080087#endif
James Feist3909dc82020-04-03 10:58:55 -070088
Alan Kuof16f6262020-12-08 19:29:59 +080089#ifdef BMCWEB_ENABLE_SESSION_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -070090static 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{
93 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Token authentication";
94
Ed Tanous81ce6092020-12-17 16:54:55 +000095 std::string_view token = authHeader.substr(strlen("Token "));
John Edward Broadbent59b98b22021-07-13 15:36:32 -070096 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -070097 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -070098 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -070099}
Alan Kuof16f6262020-12-08 19:29:59 +0800100#endif
James Feist3909dc82020-04-03 10:58:55 -0700101
Alan Kuof16f6262020-12-08 19:29:59 +0800102#ifdef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700103static std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700104 performXtokenAuth(const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700105{
106 BMCWEB_LOG_DEBUG << "[AuthMiddleware] X-Auth-Token authentication";
107
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700108 std::string_view token = reqHeader["X-Auth-Token"];
James Feist3909dc82020-04-03 10:58:55 -0700109 if (token.empty())
110 {
111 return nullptr;
112 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700113 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700114 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700115 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700116}
Alan Kuof16f6262020-12-08 19:29:59 +0800117#endif
James Feist3909dc82020-04-03 10:58:55 -0700118
Alan Kuof16f6262020-12-08 19:29:59 +0800119#ifdef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700120static std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700121 performCookieAuth(boost::beast::http::verb method,
122 const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700123{
124 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Cookie authentication";
125
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700126 std::string_view cookieValue = reqHeader["Cookie"];
James Feist3909dc82020-04-03 10:58:55 -0700127 if (cookieValue.empty())
128 {
129 return nullptr;
130 }
131
132 auto startIndex = cookieValue.find("SESSION=");
133 if (startIndex == std::string::npos)
134 {
135 return nullptr;
136 }
137 startIndex += sizeof("SESSION=") - 1;
Ed Tanous81ce6092020-12-17 16:54:55 +0000138 auto endIndex = cookieValue.find(';', startIndex);
James Feist3909dc82020-04-03 10:58:55 -0700139 if (endIndex == std::string::npos)
140 {
141 endIndex = cookieValue.size();
142 }
143 std::string_view authKey =
144 cookieValue.substr(startIndex, endIndex - startIndex);
145
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700146 std::shared_ptr<persistent_data::UserSession> sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700147 persistent_data::SessionStore::getInstance().loginSessionByToken(
148 authKey);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700149 if (sessionOut == nullptr)
James Feist3909dc82020-04-03 10:58:55 -0700150 {
151 return nullptr;
152 }
153#ifndef BMCWEB_INSECURE_DISABLE_CSRF_PREVENTION
154 // RFC7231 defines methods that need csrf protection
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700155 if (method != boost::beast::http::verb::get)
James Feist3909dc82020-04-03 10:58:55 -0700156 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700157 std::string_view csrf = reqHeader["X-XSRF-TOKEN"];
James Feist3909dc82020-04-03 10:58:55 -0700158 // Make sure both tokens are filled
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700159 if (csrf.empty() || sessionOut->csrfToken.empty())
James Feist3909dc82020-04-03 10:58:55 -0700160 {
161 return nullptr;
162 }
163
Ed Tanous52cc1122020-07-18 13:51:21 -0700164 if (csrf.size() != persistent_data::sessionTokenSize)
James Feist3909dc82020-04-03 10:58:55 -0700165 {
166 return nullptr;
167 }
168 // Reject if csrf token not available
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700169 if (!crow::utility::constantTimeStringCompare(csrf,
170 sessionOut->csrfToken))
James Feist3909dc82020-04-03 10:58:55 -0700171 {
172 return nullptr;
173 }
174 }
175#endif
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700176 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700177}
Alan Kuof16f6262020-12-08 19:29:59 +0800178#endif
James Feist3909dc82020-04-03 10:58:55 -0700179
Alexander Filippov96457602020-09-29 14:19:38 +0300180#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700181static std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700182 performTLSAuth(Response& res,
183 const boost::beast::http::header<true>& reqHeader,
Ed Tanousb5a76932020-09-29 16:16:58 -0700184 const std::weak_ptr<persistent_data::UserSession>& session)
James Feist6964c982020-07-28 16:10:23 -0700185{
James Feist6964c982020-07-28 16:10:23 -0700186 if (auto sp = session.lock())
187 {
188 // set cookie only if this is req from the browser.
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700189 if (reqHeader["User-Agent"].empty())
James Feist6964c982020-07-28 16:10:23 -0700190 {
191 BMCWEB_LOG_DEBUG << " TLS session: " << sp->uniqueId
192 << " will be used for this request.";
193 return sp;
194 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700195 std::string_view cookieValue = reqHeader["Cookie"];
Ed Tanous3174e4d2020-10-07 11:41:22 -0700196 if (cookieValue.empty() ||
197 cookieValue.find("SESSION=") == std::string::npos)
James Feist6964c982020-07-28 16:10:23 -0700198 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700199 // TODO: change this to not switch to cookie auth
Gunnar Mills636be392021-03-15 12:47:07 -0500200 res.addHeader(
201 "Set-Cookie",
202 "XSRF-TOKEN=" + sp->csrfToken +
203 "; SameSite=Strict; Secure\r\nSet-Cookie: SESSION=" +
204 sp->sessionToken +
205 "; SameSite=Strict; Secure; HttpOnly\r\nSet-Cookie: "
206 "IsAuthenticated=true; Secure");
Ed Tanous3174e4d2020-10-07 11:41:22 -0700207 BMCWEB_LOG_DEBUG << " TLS session: " << sp->uniqueId
208 << " with cookie will be used for this request.";
209 return sp;
James Feist6964c982020-07-28 16:10:23 -0700210 }
211 }
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
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700217static bool isOnWhitelist(std::string_view url, boost::beast::http::verb method)
James Feist3909dc82020-04-03 10:58:55 -0700218{
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700219 if (boost::beast::http::verb::get == method)
James Feist3909dc82020-04-03 10:58:55 -0700220 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700221 if (url == "/redfish/v1" || url == "/redfish/v1/" ||
222 url == "/redfish" || url == "/redfish/" ||
223 url == "/redfish/v1/odata" || url == "/redfish/v1/odata/")
James Feist3909dc82020-04-03 10:58:55 -0700224 {
225 return true;
226 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700227 if (crow::webroutes::routes.find(std::string(url)) !=
Ed Tanousd4d25792020-09-29 15:15:03 -0700228 crow::webroutes::routes.end())
James Feist3909dc82020-04-03 10:58:55 -0700229 {
230 return true;
231 }
232 }
233
234 // it's allowed to POST on session collection & login without
235 // authentication
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700236 if (boost::beast::http::verb::post == method)
James Feist3909dc82020-04-03 10:58:55 -0700237 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700238 if ((url == "/redfish/v1/SessionService/Sessions") ||
239 (url == "/redfish/v1/SessionService/Sessions/") ||
240 (url == "/login"))
James Feist3909dc82020-04-03 10:58:55 -0700241 {
242 return true;
243 }
244 }
245
246 return false;
247}
248
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700249static std::shared_ptr<persistent_data::UserSession> authenticate(
250 std::string_view url, boost::asio::ip::address& ipAddress, Response& res,
251 boost::beast::http::verb method,
252 const boost::beast::http::header<true>& reqHeader,
253 [[maybe_unused]] const std::shared_ptr<persistent_data::UserSession>&
254 session)
James Feist3909dc82020-04-03 10:58:55 -0700255{
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700256 if (isOnWhitelist(url, method))
James Feist3909dc82020-04-03 10:58:55 -0700257 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700258 return nullptr;
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;
Alexander Filippov96457602020-09-29 14:19:38 +0300264#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700265 if (authMethodsConfig.tls)
James Feist6964c982020-07-28 16:10:23 -0700266 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700267 sessionOut = performTLSAuth(res, reqHeader, session);
James Feist6964c982020-07-28 16:10:23 -0700268 }
Alexander Filippov96457602020-09-29 14:19:38 +0300269#endif
Alan Kuof16f6262020-12-08 19:29:59 +0800270#ifdef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700271 if (sessionOut == nullptr && authMethodsConfig.xtoken)
James Feist3909dc82020-04-03 10:58:55 -0700272 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700273 sessionOut = performXtokenAuth(reqHeader);
James Feist3909dc82020-04-03 10:58:55 -0700274 }
Alan Kuof16f6262020-12-08 19:29:59 +0800275#endif
276#ifdef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700277 if (sessionOut == nullptr && authMethodsConfig.cookie)
James Feist3909dc82020-04-03 10:58:55 -0700278 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700279 sessionOut = performCookieAuth(method, reqHeader);
James Feist3909dc82020-04-03 10:58:55 -0700280 }
Alan Kuof16f6262020-12-08 19:29:59 +0800281#endif
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700282 std::string_view authHeader = reqHeader["Authorization"];
283
284 if (!authHeader.empty())
James Feist3909dc82020-04-03 10:58:55 -0700285 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700286 // Reject any kind of auth other than basic or token
287 if (boost::starts_with(authHeader, "Token ") &&
288 authMethodsConfig.sessionToken)
James Feist3909dc82020-04-03 10:58:55 -0700289 {
Alan Kuof16f6262020-12-08 19:29:59 +0800290#ifdef BMCWEB_ENABLE_SESSION_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700291 sessionOut = performTokenAuth(authHeader);
Alan Kuof16f6262020-12-08 19:29:59 +0800292#endif
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700293 }
294 else if (boost::starts_with(authHeader, "Basic ") &&
295 authMethodsConfig.basic)
296 {
Alan Kuof16f6262020-12-08 19:29:59 +0800297#ifdef BMCWEB_ENABLE_BASIC_AUTHENTICATION
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700298 sessionOut = performBasicAuth(ipAddress, authHeader);
Alan Kuof16f6262020-12-08 19:29:59 +0800299#endif
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700300 }
301 if (sessionOut != nullptr)
302 {
303 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700304 }
305 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700306 BMCWEB_LOG_WARNING << "[AuthMiddleware] authorization failed";
307 forward_unauthorized::sendUnauthorized(url, reqHeader["User-Agent"],
308 reqHeader["accept"], res);
309 res.end();
310 return nullptr;
James Feist3909dc82020-04-03 10:58:55 -0700311}
312
313} // namespace authorization
314} // namespace crow