blob: 9bb0351069343058acbabe77343c2649855ac397 [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
James Feist3909dc82020-04-03 10:58:55 -07003#pragma once
4
Paul Fertser29aab242024-06-12 19:28:47 +00005#include "cookies.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08006#include "forward_unauthorized.hpp"
7#include "http_request.hpp"
8#include "http_response.hpp"
9#include "http_utility.hpp"
10#include "pam_authenticate.hpp"
James Feist3909dc82020-04-03 10:58:55 -070011#include "webroutes.hpp"
12
James Feist3909dc82020-04-03 10:58:55 -070013#include <boost/container/flat_set.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
Nan Zhoud055a342022-05-25 01:15:34 +000021namespace authentication
James Feist3909dc82020-04-03 10:58:55 -070022{
23
Patrick Williamsbd79bce2024-08-16 15:22:20 -040024inline std::shared_ptr<persistent_data::UserSession> performBasicAuth(
25 const boost::asio::ip::address& clientIp, std::string_view authHeader)
James Feist3909dc82020-04-03 10:58:55 -070026{
Ed Tanous62598e32023-07-17 17:06:25 -070027 BMCWEB_LOG_DEBUG("[AuthMiddleware] Basic authentication");
James Feist3909dc82020-04-03 10:58:55 -070028
Ed Tanous11ba3972022-07-11 09:50:41 -070029 if (!authHeader.starts_with("Basic "))
John Edward Broadbent97a056a2021-09-10 14:56:19 -070030 {
31 return nullptr;
32 }
33
Ed Tanous81ce6092020-12-17 16:54:55 +000034 std::string_view param = authHeader.substr(strlen("Basic "));
John Edward Broadbent97a056a2021-09-10 14:56:19 -070035 std::string authData;
36
James Feist3909dc82020-04-03 10:58:55 -070037 if (!crow::utility::base64Decode(param, authData))
38 {
39 return nullptr;
40 }
41 std::size_t separator = authData.find(':');
42 if (separator == std::string::npos)
43 {
44 return nullptr;
45 }
46
47 std::string user = authData.substr(0, separator);
48 separator += 1;
49 if (separator > authData.size())
50 {
51 return nullptr;
52 }
53 std::string pass = authData.substr(separator);
54
Ed Tanous62598e32023-07-17 17:06:25 -070055 BMCWEB_LOG_DEBUG("[AuthMiddleware] Authenticating user: {}", user);
56 BMCWEB_LOG_DEBUG("[AuthMiddleware] User IPAddress: {}",
57 clientIp.to_string());
James Feist3909dc82020-04-03 10:58:55 -070058
Ravi Teja2ccce1f2024-08-10 04:05:36 -050059 int pamrc = pamAuthenticateUser(user, pass, std::nullopt);
James Feist3909dc82020-04-03 10:58:55 -070060 bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
61 if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
62 {
63 return nullptr;
64 }
65
Ed Tanous89cda632024-04-16 08:45:54 -070066 // Attempt to locate an existing Basic Auth session from the same ip address
67 // and user
68 for (auto& session :
69 persistent_data::SessionStore::getInstance().getSessions())
70 {
71 if (session->sessionType != persistent_data::SessionType::Basic)
72 {
73 continue;
74 }
75 if (session->clientIp != redfish::ip_util::toString(clientIp))
76 {
77 continue;
78 }
79 if (session->username != user)
80 {
81 continue;
82 }
83 return session;
84 }
85
James Feist3909dc82020-04-03 10:58:55 -070086 return persistent_data::SessionStore::getInstance().generateUserSession(
Ed Tanous89cda632024-04-16 08:45:54 -070087 user, clientIp, std::nullopt, persistent_data::SessionType::Basic,
88 isConfigureSelfOnly);
James Feist3909dc82020-04-03 10:58:55 -070089}
90
Ed Tanous25b54db2024-04-17 15:40:31 -070091inline std::shared_ptr<persistent_data::UserSession>
Ed Tanous81ce6092020-12-17 16:54:55 +000092 performTokenAuth(std::string_view authHeader)
James Feist3909dc82020-04-03 10:58:55 -070093{
Ed Tanous62598e32023-07-17 17:06:25 -070094 BMCWEB_LOG_DEBUG("[AuthMiddleware] Token authentication");
Ed Tanous11ba3972022-07-11 09:50:41 -070095 if (!authHeader.starts_with("Token "))
John Edward Broadbent97a056a2021-09-10 14:56:19 -070096 {
97 return nullptr;
98 }
Ed Tanous81ce6092020-12-17 16:54:55 +000099 std::string_view token = authHeader.substr(strlen("Token "));
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700100 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700101 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700102 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700103}
104
Ed Tanous25b54db2024-04-17 15:40:31 -0700105inline std::shared_ptr<persistent_data::UserSession>
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700106 performXtokenAuth(const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700107{
Ed Tanous62598e32023-07-17 17:06:25 -0700108 BMCWEB_LOG_DEBUG("[AuthMiddleware] X-Auth-Token authentication");
James Feist3909dc82020-04-03 10:58:55 -0700109
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700110 std::string_view token = reqHeader["X-Auth-Token"];
James Feist3909dc82020-04-03 10:58:55 -0700111 if (token.empty())
112 {
113 return nullptr;
114 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700115 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700116 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700117 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700118}
119
Ed Tanous25b54db2024-04-17 15:40:31 -0700120inline std::shared_ptr<persistent_data::UserSession>
Ed Tanous1d869602022-12-19 13:48:12 -0800121 performCookieAuth(boost::beast::http::verb method [[maybe_unused]],
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700122 const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700123{
Ed Tanous9f217c22024-04-19 17:22:43 -0700124 using headers = boost::beast::http::header<true>;
125 std::pair<headers::const_iterator, headers::const_iterator> cookies =
126 reqHeader.equal_range(boost::beast::http::field::cookie);
James Feist3909dc82020-04-03 10:58:55 -0700127
Ed Tanous9f217c22024-04-19 17:22:43 -0700128 for (auto it = cookies.first; it != cookies.second; it++)
James Feist3909dc82020-04-03 10:58:55 -0700129 {
Ed Tanous9f217c22024-04-19 17:22:43 -0700130 std::string_view cookieValue = it->value();
131 BMCWEB_LOG_DEBUG("Checking cookie {}", cookieValue);
132 auto startIndex = cookieValue.find("SESSION=");
133 if (startIndex == std::string::npos)
134 {
135 BMCWEB_LOG_DEBUG(
136 "Cookie was present, but didn't look like a session {}",
137 cookieValue);
138 continue;
139 }
140 startIndex += sizeof("SESSION=") - 1;
141 auto endIndex = cookieValue.find(';', startIndex);
142 if (endIndex == std::string::npos)
143 {
144 endIndex = cookieValue.size();
145 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400146 std::string_view authKey =
147 cookieValue.substr(startIndex, endIndex - startIndex);
James Feist3909dc82020-04-03 10:58:55 -0700148
Ed Tanous9f217c22024-04-19 17:22:43 -0700149 std::shared_ptr<persistent_data::UserSession> sessionOut =
150 persistent_data::SessionStore::getInstance().loginSessionByToken(
151 authKey);
152 if (sessionOut == nullptr)
153 {
154 return nullptr;
155 }
156 sessionOut->cookieAuth = true;
James Feist3909dc82020-04-03 10:58:55 -0700157
Ed Tanous576db692024-05-10 16:37:56 -0700158 if constexpr (!BMCWEB_INSECURE_DISABLE_CSRF)
Ed Tanous25b54db2024-04-17 15:40:31 -0700159 {
160 // RFC7231 defines methods that need csrf protection
161 if (method != boost::beast::http::verb::get)
Ed Tanous9f217c22024-04-19 17:22:43 -0700162 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700163 std::string_view csrf = reqHeader["X-XSRF-TOKEN"];
164 // Make sure both tokens are filled
165 if (csrf.empty() || sessionOut->csrfToken.empty())
166 {
167 return nullptr;
168 }
169
170 if (csrf.size() != persistent_data::sessionTokenSize)
171 {
172 return nullptr;
173 }
174 // Reject if csrf token not available
Ed Tanous724985f2024-06-05 09:19:06 -0700175 if (!bmcweb::constantTimeStringCompare(csrf,
176 sessionOut->csrfToken))
Ed Tanous25b54db2024-04-17 15:40:31 -0700177 {
178 return nullptr;
179 }
Ed Tanous9f217c22024-04-19 17:22:43 -0700180 }
James Feist3909dc82020-04-03 10:58:55 -0700181 }
Ed Tanous3eaecac2024-05-08 16:26:24 -0700182 return sessionOut;
Ed Tanous9f217c22024-04-19 17:22:43 -0700183 }
184 return nullptr;
James Feist3909dc82020-04-03 10:58:55 -0700185}
186
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400187inline std::shared_ptr<persistent_data::UserSession> performTLSAuth(
188 Response& res, const std::shared_ptr<persistent_data::UserSession>& session)
James Feist6964c982020-07-28 16:10:23 -0700189{
Ed Tanous3281bcf2024-06-25 16:02:05 -0700190 if (session != nullptr)
James Feist6964c982020-07-28 16:10:23 -0700191 {
Ed Tanous994fd862023-06-06 13:37:03 -0700192 res.addHeader(boost::beast::http::field::set_cookie,
193 "IsAuthenticated=true; Secure");
Ed Tanous62598e32023-07-17 17:06:25 -0700194 BMCWEB_LOG_DEBUG(
195 " TLS session: {} with cookie will be used for this request.",
Ed Tanous3281bcf2024-06-25 16:02:05 -0700196 session->uniqueId);
James Feist6964c982020-07-28 16:10:23 -0700197 }
Ed Tanous3281bcf2024-06-25 16:02:05 -0700198
199 return session;
James Feist6964c982020-07-28 16:10:23 -0700200}
201
James Feist3909dc82020-04-03 10:58:55 -0700202// checks if request can be forwarded without authentication
Ed Tanous25b54db2024-04-17 15:40:31 -0700203inline bool isOnAllowlist(std::string_view url, boost::beast::http::verb method)
James Feist3909dc82020-04-03 10:58:55 -0700204{
Ed Tanous38221502024-06-03 12:19:38 -0700205 // Handle the case where the router registers routes as both ending with /
206 // and not.
VinceChang66378f5df132024-06-14 17:13:16 +0800207 if (url.ends_with('/') && url != "/")
Ed Tanous38221502024-06-03 12:19:38 -0700208 {
209 url.remove_suffix(1);
210 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700211 if (boost::beast::http::verb::get == method)
James Feist3909dc82020-04-03 10:58:55 -0700212 {
Myung Bae41868c62024-10-09 16:36:49 -0700213 if ((url == "/redfish") || //
214 (url == "/redfish/v1") || //
Ed Tanous38221502024-06-03 12:19:38 -0700215 (url == "/redfish/v1/odata") || //
216 (url == "/redfish/v1/$metadata"))
James Feist3909dc82020-04-03 10:58:55 -0700217 {
218 return true;
219 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700220 if (crow::webroutes::routes.find(std::string(url)) !=
Ed Tanousd4d25792020-09-29 15:15:03 -0700221 crow::webroutes::routes.end())
James Feist3909dc82020-04-03 10:58:55 -0700222 {
223 return true;
224 }
225 }
226
227 // it's allowed to POST on session collection & login without
228 // authentication
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700229 if (boost::beast::http::verb::post == method)
James Feist3909dc82020-04-03 10:58:55 -0700230 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700231 if ((url == "/redfish/v1/SessionService/Sessions") ||
Ed Tanouse76cd862022-03-14 09:12:00 -0700232 (url == "/redfish/v1/SessionService/Sessions/Members") ||
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700233 (url == "/login"))
James Feist3909dc82020-04-03 10:58:55 -0700234 {
235 return true;
236 }
237 }
238
239 return false;
240}
241
Ed Tanous25b54db2024-04-17 15:40:31 -0700242inline std::shared_ptr<persistent_data::UserSession> authenticate(
243 const boost::asio::ip::address& ipAddress [[maybe_unused]],
244 Response& res [[maybe_unused]],
245 boost::beast::http::verb method [[maybe_unused]],
246 const boost::beast::http::header<true>& reqHeader,
247 [[maybe_unused]] const std::shared_ptr<persistent_data::UserSession>&
248 session)
James Feist3909dc82020-04-03 10:58:55 -0700249{
Ed Tanous52cc1122020-07-18 13:51:21 -0700250 const persistent_data::AuthConfigMethods& authMethodsConfig =
251 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
James Feist3909dc82020-04-03 10:58:55 -0700252
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700253 std::shared_ptr<persistent_data::UserSession> sessionOut = nullptr;
Ed Tanous25b54db2024-04-17 15:40:31 -0700254 if constexpr (BMCWEB_MUTUAL_TLS_AUTH)
James Feist6964c982020-07-28 16:10:23 -0700255 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700256 if (authMethodsConfig.tls)
257 {
Ed Tanous3281bcf2024-06-25 16:02:05 -0700258 sessionOut = performTLSAuth(res, session);
Ed Tanous25b54db2024-04-17 15:40:31 -0700259 }
James Feist6964c982020-07-28 16:10:23 -0700260 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700261 if constexpr (BMCWEB_XTOKEN_AUTH)
James Feist3909dc82020-04-03 10:58:55 -0700262 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700263 if (sessionOut == nullptr && authMethodsConfig.xtoken)
264 {
265 sessionOut = performXtokenAuth(reqHeader);
266 }
James Feist3909dc82020-04-03 10:58:55 -0700267 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700268 if constexpr (BMCWEB_COOKIE_AUTH)
James Feist3909dc82020-04-03 10:58:55 -0700269 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700270 if (sessionOut == nullptr && authMethodsConfig.cookie)
271 {
272 sessionOut = performCookieAuth(method, reqHeader);
273 }
James Feist3909dc82020-04-03 10:58:55 -0700274 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700275 std::string_view authHeader = reqHeader["Authorization"];
Ed Tanous62598e32023-07-17 17:06:25 -0700276 BMCWEB_LOG_DEBUG("authHeader={}", authHeader);
Ed Tanous25b54db2024-04-17 15:40:31 -0700277 if constexpr (BMCWEB_SESSION_AUTH)
James Feist3909dc82020-04-03 10:58:55 -0700278 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700279 if (sessionOut == nullptr && authMethodsConfig.sessionToken)
280 {
281 sessionOut = performTokenAuth(authHeader);
282 }
James Feist3909dc82020-04-03 10:58:55 -0700283 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700284 if constexpr (BMCWEB_BASIC_AUTH)
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700285 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700286 if (sessionOut == nullptr && authMethodsConfig.basic)
287 {
288 sessionOut = performBasicAuth(ipAddress, authHeader);
289 }
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700290 }
291 if (sessionOut != nullptr)
292 {
293 return sessionOut;
294 }
295
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700296 return nullptr;
James Feist3909dc82020-04-03 10:58:55 -0700297}
298
Nan Zhoud055a342022-05-25 01:15:34 +0000299} // namespace authentication
James Feist3909dc82020-04-03 10:58:55 -0700300} // namespace crow