blob: b1d1097068e3c5b28fceed11c3b4d3b3eb6798b6 [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 "));
James Feist3909dc82020-04-03 10:58:55 -070096 auto session =
97 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
98 return session;
99}
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>
James Feist3909dc82020-04-03 10:58:55 -0700104 performXtokenAuth(const crow::Request& req)
105{
106 BMCWEB_LOG_DEBUG << "[AuthMiddleware] X-Auth-Token authentication";
107
108 std::string_view token = req.getHeaderValue("X-Auth-Token");
109 if (token.empty())
110 {
111 return nullptr;
112 }
113 auto session =
114 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
115 return session;
116}
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>
James Feist3909dc82020-04-03 10:58:55 -0700121 performCookieAuth(const crow::Request& req)
122{
123 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Cookie authentication";
124
125 std::string_view cookieValue = req.getHeaderValue("Cookie");
126 if (cookieValue.empty())
127 {
128 return nullptr;
129 }
130
131 auto startIndex = cookieValue.find("SESSION=");
132 if (startIndex == std::string::npos)
133 {
134 return nullptr;
135 }
136 startIndex += sizeof("SESSION=") - 1;
Ed Tanous81ce6092020-12-17 16:54:55 +0000137 auto endIndex = cookieValue.find(';', startIndex);
James Feist3909dc82020-04-03 10:58:55 -0700138 if (endIndex == std::string::npos)
139 {
140 endIndex = cookieValue.size();
141 }
142 std::string_view authKey =
143 cookieValue.substr(startIndex, endIndex - startIndex);
144
Ed Tanous3174e4d2020-10-07 11:41:22 -0700145 std::shared_ptr<persistent_data::UserSession> session =
James Feist3909dc82020-04-03 10:58:55 -0700146 persistent_data::SessionStore::getInstance().loginSessionByToken(
147 authKey);
148 if (session == nullptr)
149 {
150 return nullptr;
151 }
152#ifndef BMCWEB_INSECURE_DISABLE_CSRF_PREVENTION
153 // RFC7231 defines methods that need csrf protection
Ed Tanousb41187f2019-10-24 16:30:02 -0700154 if (req.method() != boost::beast::http::verb::get)
James Feist3909dc82020-04-03 10:58:55 -0700155 {
156 std::string_view csrf = req.getHeaderValue("X-XSRF-TOKEN");
157 // Make sure both tokens are filled
158 if (csrf.empty() || session->csrfToken.empty())
159 {
160 return nullptr;
161 }
162
Ed Tanous52cc1122020-07-18 13:51:21 -0700163 if (csrf.size() != persistent_data::sessionTokenSize)
James Feist3909dc82020-04-03 10:58:55 -0700164 {
165 return nullptr;
166 }
167 // Reject if csrf token not available
168 if (!crow::utility::constantTimeStringCompare(csrf, session->csrfToken))
169 {
170 return nullptr;
171 }
172 }
173#endif
174 return session;
175}
Alan Kuof16f6262020-12-08 19:29:59 +0800176#endif
James Feist3909dc82020-04-03 10:58:55 -0700177
Alexander Filippov96457602020-09-29 14:19:38 +0300178#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
Ed Tanous3174e4d2020-10-07 11:41:22 -0700179static std::shared_ptr<persistent_data::UserSession>
James Feist6964c982020-07-28 16:10:23 -0700180 performTLSAuth(const crow::Request& req, Response& res,
Ed Tanousb5a76932020-09-29 16:16:58 -0700181 const std::weak_ptr<persistent_data::UserSession>& session)
James Feist6964c982020-07-28 16:10:23 -0700182{
James Feist6964c982020-07-28 16:10:23 -0700183 if (auto sp = session.lock())
184 {
185 // set cookie only if this is req from the browser.
186 if (req.getHeaderValue("User-Agent").empty())
187 {
188 BMCWEB_LOG_DEBUG << " TLS session: " << sp->uniqueId
189 << " will be used for this request.";
190 return sp;
191 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700192 std::string_view cookieValue = req.getHeaderValue("Cookie");
193 if (cookieValue.empty() ||
194 cookieValue.find("SESSION=") == std::string::npos)
James Feist6964c982020-07-28 16:10:23 -0700195 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700196 // TODO: change this to not switch to cookie auth
Gunnar Mills636be392021-03-15 12:47:07 -0500197 res.addHeader(
198 "Set-Cookie",
199 "XSRF-TOKEN=" + sp->csrfToken +
200 "; SameSite=Strict; Secure\r\nSet-Cookie: SESSION=" +
201 sp->sessionToken +
202 "; SameSite=Strict; Secure; HttpOnly\r\nSet-Cookie: "
203 "IsAuthenticated=true; Secure");
Ed Tanous3174e4d2020-10-07 11:41:22 -0700204 BMCWEB_LOG_DEBUG << " TLS session: " << sp->uniqueId
205 << " with cookie will be used for this request.";
206 return sp;
James Feist6964c982020-07-28 16:10:23 -0700207 }
208 }
James Feist6964c982020-07-28 16:10:23 -0700209 return nullptr;
210}
Alexander Filippov96457602020-09-29 14:19:38 +0300211#endif
James Feist6964c982020-07-28 16:10:23 -0700212
James Feist3909dc82020-04-03 10:58:55 -0700213// checks if request can be forwarded without authentication
214static bool isOnWhitelist(const crow::Request& req)
215{
216 // it's allowed to GET root node without authentication
Ed Tanousb41187f2019-10-24 16:30:02 -0700217 if (boost::beast::http::verb::get == req.method())
James Feist3909dc82020-04-03 10:58:55 -0700218 {
219 if (req.url == "/redfish/v1" || req.url == "/redfish/v1/" ||
220 req.url == "/redfish" || req.url == "/redfish/" ||
221 req.url == "/redfish/v1/odata" || req.url == "/redfish/v1/odata/")
222 {
223 return true;
224 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700225 if (crow::webroutes::routes.find(std::string(req.url)) !=
226 crow::webroutes::routes.end())
James Feist3909dc82020-04-03 10:58:55 -0700227 {
228 return true;
229 }
230 }
231
232 // it's allowed to POST on session collection & login without
233 // authentication
Ed Tanousb41187f2019-10-24 16:30:02 -0700234 if (boost::beast::http::verb::post == req.method())
James Feist3909dc82020-04-03 10:58:55 -0700235 {
236 if ((req.url == "/redfish/v1/SessionService/Sessions") ||
237 (req.url == "/redfish/v1/SessionService/Sessions/") ||
238 (req.url == "/login"))
239 {
240 return true;
241 }
242 }
243
244 return false;
245}
246
Alexander Filippov96457602020-09-29 14:19:38 +0300247static void authenticate(
248 crow::Request& req, Response& res,
Ed Tanousf23b7292020-10-15 09:41:17 -0700249 [[maybe_unused]] const std::weak_ptr<persistent_data::UserSession>& session)
James Feist3909dc82020-04-03 10:58:55 -0700250{
251 if (isOnWhitelist(req))
252 {
253 return;
254 }
255
Ed Tanous52cc1122020-07-18 13:51:21 -0700256 const persistent_data::AuthConfigMethods& authMethodsConfig =
257 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
James Feist3909dc82020-04-03 10:58:55 -0700258
Alexander Filippov96457602020-09-29 14:19:38 +0300259#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
James Feist6964c982020-07-28 16:10:23 -0700260 if (req.session == nullptr && authMethodsConfig.tls)
261 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700262 req.session = performTLSAuth(req, res, session);
James Feist6964c982020-07-28 16:10:23 -0700263 }
Alexander Filippov96457602020-09-29 14:19:38 +0300264#endif
Alan Kuof16f6262020-12-08 19:29:59 +0800265#ifdef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
James Feist3909dc82020-04-03 10:58:55 -0700266 if (req.session == nullptr && authMethodsConfig.xtoken)
267 {
268 req.session = performXtokenAuth(req);
269 }
Alan Kuof16f6262020-12-08 19:29:59 +0800270#endif
271#ifdef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
James Feist3909dc82020-04-03 10:58:55 -0700272 if (req.session == nullptr && authMethodsConfig.cookie)
273 {
274 req.session = performCookieAuth(req);
275 }
Alan Kuof16f6262020-12-08 19:29:59 +0800276#endif
James Feist3909dc82020-04-03 10:58:55 -0700277 if (req.session == nullptr)
278 {
279 std::string_view authHeader = req.getHeaderValue("Authorization");
280 if (!authHeader.empty())
281 {
282 // Reject any kind of auth other than basic or token
283 if (boost::starts_with(authHeader, "Token ") &&
284 authMethodsConfig.sessionToken)
285 {
Alan Kuof16f6262020-12-08 19:29:59 +0800286#ifdef BMCWEB_ENABLE_SESSION_AUTHENTICATION
James Feist3909dc82020-04-03 10:58:55 -0700287 req.session = performTokenAuth(authHeader);
Alan Kuof16f6262020-12-08 19:29:59 +0800288#endif
James Feist3909dc82020-04-03 10:58:55 -0700289 }
290 else if (boost::starts_with(authHeader, "Basic ") &&
291 authMethodsConfig.basic)
292 {
Alan Kuof16f6262020-12-08 19:29:59 +0800293#ifdef BMCWEB_ENABLE_BASIC_AUTHENTICATION
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500294 req.session = performBasicAuth(req.ipAddress, authHeader);
Alan Kuof16f6262020-12-08 19:29:59 +0800295#endif
James Feist3909dc82020-04-03 10:58:55 -0700296 }
297 }
298 }
299
300 if (req.session == nullptr)
301 {
302 BMCWEB_LOG_WARNING << "[AuthMiddleware] authorization failed";
Ed Tanousd4b6c662021-03-10 13:29:30 -0800303 forward_unauthorized::sendUnauthorized(req, res);
James Feist3909dc82020-04-03 10:58:55 -0700304 res.end();
305 return;
306 }
307}
308
309} // namespace authorization
310} // namespace crow