blob: b423af27ed0de0b439a97bfb60b14df2b77a16aa [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
Patrick Williamsbd79bce2024-08-16 15:22:20 -040022inline std::shared_ptr<persistent_data::UserSession> performBasicAuth(
23 const boost::asio::ip::address& clientIp, std::string_view authHeader)
James Feist3909dc82020-04-03 10:58:55 -070024{
Ed Tanous62598e32023-07-17 17:06:25 -070025 BMCWEB_LOG_DEBUG("[AuthMiddleware] Basic authentication");
James Feist3909dc82020-04-03 10:58:55 -070026
Ed Tanous11ba3972022-07-11 09:50:41 -070027 if (!authHeader.starts_with("Basic "))
John Edward Broadbent97a056a2021-09-10 14:56:19 -070028 {
29 return nullptr;
30 }
31
Ed Tanous81ce6092020-12-17 16:54:55 +000032 std::string_view param = authHeader.substr(strlen("Basic "));
John Edward Broadbent97a056a2021-09-10 14:56:19 -070033 std::string authData;
34
James Feist3909dc82020-04-03 10:58:55 -070035 if (!crow::utility::base64Decode(param, authData))
36 {
37 return nullptr;
38 }
39 std::size_t separator = authData.find(':');
40 if (separator == std::string::npos)
41 {
42 return nullptr;
43 }
44
45 std::string user = authData.substr(0, separator);
46 separator += 1;
47 if (separator > authData.size())
48 {
49 return nullptr;
50 }
51 std::string pass = authData.substr(separator);
52
Ed Tanous62598e32023-07-17 17:06:25 -070053 BMCWEB_LOG_DEBUG("[AuthMiddleware] Authenticating user: {}", user);
54 BMCWEB_LOG_DEBUG("[AuthMiddleware] User IPAddress: {}",
55 clientIp.to_string());
James Feist3909dc82020-04-03 10:58:55 -070056
Ravi Teja2ccce1f2024-08-10 04:05:36 -050057 int pamrc = pamAuthenticateUser(user, pass, std::nullopt);
James Feist3909dc82020-04-03 10:58:55 -070058 bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
59 if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
60 {
61 return nullptr;
62 }
63
Ed Tanous89cda632024-04-16 08:45:54 -070064 // Attempt to locate an existing Basic Auth session from the same ip address
65 // and user
66 for (auto& session :
67 persistent_data::SessionStore::getInstance().getSessions())
68 {
69 if (session->sessionType != persistent_data::SessionType::Basic)
70 {
71 continue;
72 }
73 if (session->clientIp != redfish::ip_util::toString(clientIp))
74 {
75 continue;
76 }
77 if (session->username != user)
78 {
79 continue;
80 }
81 return session;
82 }
83
James Feist3909dc82020-04-03 10:58:55 -070084 return persistent_data::SessionStore::getInstance().generateUserSession(
Ed Tanous89cda632024-04-16 08:45:54 -070085 user, clientIp, std::nullopt, persistent_data::SessionType::Basic,
86 isConfigureSelfOnly);
James Feist3909dc82020-04-03 10:58:55 -070087}
88
Ed Tanous25b54db2024-04-17 15:40:31 -070089inline std::shared_ptr<persistent_data::UserSession>
Ed Tanous81ce6092020-12-17 16:54:55 +000090 performTokenAuth(std::string_view authHeader)
James Feist3909dc82020-04-03 10:58:55 -070091{
Ed Tanous62598e32023-07-17 17:06:25 -070092 BMCWEB_LOG_DEBUG("[AuthMiddleware] Token authentication");
Ed Tanous11ba3972022-07-11 09:50:41 -070093 if (!authHeader.starts_with("Token "))
John Edward Broadbent97a056a2021-09-10 14:56:19 -070094 {
95 return nullptr;
96 }
Ed Tanous81ce6092020-12-17 16:54:55 +000097 std::string_view token = authHeader.substr(strlen("Token "));
John Edward Broadbent59b98b22021-07-13 15:36:32 -070098 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -070099 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700100 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700101}
102
Ed Tanous25b54db2024-04-17 15:40:31 -0700103inline 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{
Ed Tanous62598e32023-07-17 17:06:25 -0700106 BMCWEB_LOG_DEBUG("[AuthMiddleware] X-Auth-Token authentication");
James Feist3909dc82020-04-03 10:58:55 -0700107
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}
117
Ed Tanous25b54db2024-04-17 15:40:31 -0700118inline std::shared_ptr<persistent_data::UserSession>
Ed Tanous1d869602022-12-19 13:48:12 -0800119 performCookieAuth(boost::beast::http::verb method [[maybe_unused]],
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700120 const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700121{
Ed Tanous9f217c22024-04-19 17:22:43 -0700122 using headers = boost::beast::http::header<true>;
123 std::pair<headers::const_iterator, headers::const_iterator> cookies =
124 reqHeader.equal_range(boost::beast::http::field::cookie);
James Feist3909dc82020-04-03 10:58:55 -0700125
Ed Tanous9f217c22024-04-19 17:22:43 -0700126 for (auto it = cookies.first; it != cookies.second; it++)
James Feist3909dc82020-04-03 10:58:55 -0700127 {
Ed Tanous9f217c22024-04-19 17:22:43 -0700128 std::string_view cookieValue = it->value();
129 BMCWEB_LOG_DEBUG("Checking cookie {}", cookieValue);
130 auto startIndex = cookieValue.find("SESSION=");
131 if (startIndex == std::string::npos)
132 {
133 BMCWEB_LOG_DEBUG(
134 "Cookie was present, but didn't look like a session {}",
135 cookieValue);
136 continue;
137 }
138 startIndex += sizeof("SESSION=") - 1;
139 auto endIndex = cookieValue.find(';', startIndex);
140 if (endIndex == std::string::npos)
141 {
142 endIndex = cookieValue.size();
143 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400144 std::string_view authKey =
145 cookieValue.substr(startIndex, endIndex - startIndex);
James Feist3909dc82020-04-03 10:58:55 -0700146
Ed Tanous9f217c22024-04-19 17:22:43 -0700147 std::shared_ptr<persistent_data::UserSession> sessionOut =
148 persistent_data::SessionStore::getInstance().loginSessionByToken(
149 authKey);
150 if (sessionOut == nullptr)
151 {
152 return nullptr;
153 }
154 sessionOut->cookieAuth = true;
James Feist3909dc82020-04-03 10:58:55 -0700155
Ed Tanous576db692024-05-10 16:37:56 -0700156 if constexpr (!BMCWEB_INSECURE_DISABLE_CSRF)
Ed Tanous25b54db2024-04-17 15:40:31 -0700157 {
158 // RFC7231 defines methods that need csrf protection
159 if (method != boost::beast::http::verb::get)
Ed Tanous9f217c22024-04-19 17:22:43 -0700160 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700161 std::string_view csrf = reqHeader["X-XSRF-TOKEN"];
162 // Make sure both tokens are filled
163 if (csrf.empty() || sessionOut->csrfToken.empty())
164 {
165 return nullptr;
166 }
167
168 if (csrf.size() != persistent_data::sessionTokenSize)
169 {
170 return nullptr;
171 }
172 // Reject if csrf token not available
Ed Tanous724985f2024-06-05 09:19:06 -0700173 if (!bmcweb::constantTimeStringCompare(csrf,
174 sessionOut->csrfToken))
Ed Tanous25b54db2024-04-17 15:40:31 -0700175 {
176 return nullptr;
177 }
Ed Tanous9f217c22024-04-19 17:22:43 -0700178 }
James Feist3909dc82020-04-03 10:58:55 -0700179 }
Ed Tanous3eaecac2024-05-08 16:26:24 -0700180 return sessionOut;
Ed Tanous9f217c22024-04-19 17:22:43 -0700181 }
182 return nullptr;
James Feist3909dc82020-04-03 10:58:55 -0700183}
184
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400185inline std::shared_ptr<persistent_data::UserSession> performTLSAuth(
186 Response& res, const std::shared_ptr<persistent_data::UserSession>& session)
James Feist6964c982020-07-28 16:10:23 -0700187{
Ed Tanous3281bcf2024-06-25 16:02:05 -0700188 if (session != nullptr)
James Feist6964c982020-07-28 16:10:23 -0700189 {
Ed Tanous994fd862023-06-06 13:37:03 -0700190 res.addHeader(boost::beast::http::field::set_cookie,
191 "IsAuthenticated=true; Secure");
Ed Tanous62598e32023-07-17 17:06:25 -0700192 BMCWEB_LOG_DEBUG(
193 " TLS session: {} with cookie will be used for this request.",
Ed Tanous3281bcf2024-06-25 16:02:05 -0700194 session->uniqueId);
James Feist6964c982020-07-28 16:10:23 -0700195 }
Ed Tanous3281bcf2024-06-25 16:02:05 -0700196
197 return session;
James Feist6964c982020-07-28 16:10:23 -0700198}
199
James Feist3909dc82020-04-03 10:58:55 -0700200// checks if request can be forwarded without authentication
Ed Tanous25b54db2024-04-17 15:40:31 -0700201inline bool isOnAllowlist(std::string_view url, boost::beast::http::verb method)
James Feist3909dc82020-04-03 10:58:55 -0700202{
Ed Tanous38221502024-06-03 12:19:38 -0700203 // Handle the case where the router registers routes as both ending with /
204 // and not.
VinceChang66378f5df132024-06-14 17:13:16 +0800205 if (url.ends_with('/') && url != "/")
Ed Tanous38221502024-06-03 12:19:38 -0700206 {
207 url.remove_suffix(1);
208 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700209 if (boost::beast::http::verb::get == method)
James Feist3909dc82020-04-03 10:58:55 -0700210 {
Myung Bae41868c62024-10-09 16:36:49 -0700211 if ((url == "/redfish") || //
212 (url == "/redfish/v1") || //
Ed Tanous38221502024-06-03 12:19:38 -0700213 (url == "/redfish/v1/odata") || //
214 (url == "/redfish/v1/$metadata"))
James Feist3909dc82020-04-03 10:58:55 -0700215 {
216 return true;
217 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700218 if (crow::webroutes::routes.find(std::string(url)) !=
Ed Tanousd4d25792020-09-29 15:15:03 -0700219 crow::webroutes::routes.end())
James Feist3909dc82020-04-03 10:58:55 -0700220 {
221 return true;
222 }
223 }
224
225 // it's allowed to POST on session collection & login without
226 // authentication
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700227 if (boost::beast::http::verb::post == method)
James Feist3909dc82020-04-03 10:58:55 -0700228 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700229 if ((url == "/redfish/v1/SessionService/Sessions") ||
Ed Tanouse76cd862022-03-14 09:12:00 -0700230 (url == "/redfish/v1/SessionService/Sessions/Members") ||
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700231 (url == "/login"))
James Feist3909dc82020-04-03 10:58:55 -0700232 {
233 return true;
234 }
235 }
236
237 return false;
238}
239
Ed Tanous25b54db2024-04-17 15:40:31 -0700240inline std::shared_ptr<persistent_data::UserSession> authenticate(
241 const boost::asio::ip::address& ipAddress [[maybe_unused]],
242 Response& res [[maybe_unused]],
243 boost::beast::http::verb method [[maybe_unused]],
244 const boost::beast::http::header<true>& reqHeader,
245 [[maybe_unused]] const std::shared_ptr<persistent_data::UserSession>&
246 session)
James Feist3909dc82020-04-03 10:58:55 -0700247{
Ed Tanous52cc1122020-07-18 13:51:21 -0700248 const persistent_data::AuthConfigMethods& authMethodsConfig =
249 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
James Feist3909dc82020-04-03 10:58:55 -0700250
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700251 std::shared_ptr<persistent_data::UserSession> sessionOut = nullptr;
Ed Tanous25b54db2024-04-17 15:40:31 -0700252 if constexpr (BMCWEB_MUTUAL_TLS_AUTH)
James Feist6964c982020-07-28 16:10:23 -0700253 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700254 if (authMethodsConfig.tls)
255 {
Ed Tanous3281bcf2024-06-25 16:02:05 -0700256 sessionOut = performTLSAuth(res, session);
Ed Tanous25b54db2024-04-17 15:40:31 -0700257 }
James Feist6964c982020-07-28 16:10:23 -0700258 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700259 if constexpr (BMCWEB_XTOKEN_AUTH)
James Feist3909dc82020-04-03 10:58:55 -0700260 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700261 if (sessionOut == nullptr && authMethodsConfig.xtoken)
262 {
263 sessionOut = performXtokenAuth(reqHeader);
264 }
James Feist3909dc82020-04-03 10:58:55 -0700265 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700266 if constexpr (BMCWEB_COOKIE_AUTH)
James Feist3909dc82020-04-03 10:58:55 -0700267 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700268 if (sessionOut == nullptr && authMethodsConfig.cookie)
269 {
270 sessionOut = performCookieAuth(method, reqHeader);
271 }
James Feist3909dc82020-04-03 10:58:55 -0700272 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700273 std::string_view authHeader = reqHeader["Authorization"];
Ed Tanous62598e32023-07-17 17:06:25 -0700274 BMCWEB_LOG_DEBUG("authHeader={}", authHeader);
Ed Tanous25b54db2024-04-17 15:40:31 -0700275 if constexpr (BMCWEB_SESSION_AUTH)
James Feist3909dc82020-04-03 10:58:55 -0700276 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700277 if (sessionOut == nullptr && authMethodsConfig.sessionToken)
278 {
279 sessionOut = performTokenAuth(authHeader);
280 }
James Feist3909dc82020-04-03 10:58:55 -0700281 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700282 if constexpr (BMCWEB_BASIC_AUTH)
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700283 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700284 if (sessionOut == nullptr && authMethodsConfig.basic)
285 {
286 sessionOut = performBasicAuth(ipAddress, authHeader);
287 }
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700288 }
289 if (sessionOut != nullptr)
290 {
291 return sessionOut;
292 }
293
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700294 return nullptr;
James Feist3909dc82020-04-03 10:58:55 -0700295}
296
Nan Zhoud055a342022-05-25 01:15:34 +0000297} // namespace authentication
James Feist3909dc82020-04-03 10:58:55 -0700298} // namespace crow