blob: ba4fb44bb72018dffbb99e64933238101bae2707 [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
Ed Tanousd7857202025-01-28 15:32:26 -08005#include "bmcweb_config.h"
6
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08007#include "http_response.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -08008#include "logging.hpp"
9#include "ossl_random.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080010#include "pam_authenticate.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080011#include "sessions.hpp"
12#include "utility.hpp"
13#include "utils/ip_utils.hpp"
James Feist3909dc82020-04-03 10:58:55 -070014#include "webroutes.hpp"
15
Ed Tanousd7857202025-01-28 15:32:26 -080016#include <security/_pam_types.h>
17
18#include <boost/asio/ip/address.hpp>
19#include <boost/beast/http/field.hpp>
20#include <boost/beast/http/message.hpp>
21#include <boost/beast/http/verb.hpp>
James Feist3909dc82020-04-03 10:58:55 -070022#include <boost/container/flat_set.hpp>
James Feist3909dc82020-04-03 10:58:55 -070023
Ed Tanousd7857202025-01-28 15:32:26 -080024#include <cstddef>
25#include <cstring>
26#include <memory>
27#include <optional>
28#include <string>
29#include <string_view>
Ed Tanousb5a76932020-09-29 16:16:58 -070030#include <utility>
James Feist3909dc82020-04-03 10:58:55 -070031
32namespace crow
33{
34
Nan Zhoud055a342022-05-25 01:15:34 +000035namespace authentication
James Feist3909dc82020-04-03 10:58:55 -070036{
37
Patrick Williamsbd79bce2024-08-16 15:22:20 -040038inline std::shared_ptr<persistent_data::UserSession> performBasicAuth(
39 const boost::asio::ip::address& clientIp, std::string_view authHeader)
James Feist3909dc82020-04-03 10:58:55 -070040{
Ed Tanous62598e32023-07-17 17:06:25 -070041 BMCWEB_LOG_DEBUG("[AuthMiddleware] Basic authentication");
James Feist3909dc82020-04-03 10:58:55 -070042
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
Ed Tanous62598e32023-07-17 17:06:25 -070069 BMCWEB_LOG_DEBUG("[AuthMiddleware] Authenticating user: {}", user);
70 BMCWEB_LOG_DEBUG("[AuthMiddleware] User IPAddress: {}",
71 clientIp.to_string());
James Feist3909dc82020-04-03 10:58:55 -070072
Ravi Teja2ccce1f2024-08-10 04:05:36 -050073 int pamrc = pamAuthenticateUser(user, pass, std::nullopt);
James Feist3909dc82020-04-03 10:58:55 -070074 bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
75 if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
76 {
77 return nullptr;
78 }
79
Ed Tanous89cda632024-04-16 08:45:54 -070080 // Attempt to locate an existing Basic Auth session from the same ip address
81 // and user
82 for (auto& session :
83 persistent_data::SessionStore::getInstance().getSessions())
84 {
85 if (session->sessionType != persistent_data::SessionType::Basic)
86 {
87 continue;
88 }
89 if (session->clientIp != redfish::ip_util::toString(clientIp))
90 {
91 continue;
92 }
93 if (session->username != user)
94 {
95 continue;
96 }
97 return session;
98 }
99
James Feist3909dc82020-04-03 10:58:55 -0700100 return persistent_data::SessionStore::getInstance().generateUserSession(
Ed Tanous89cda632024-04-16 08:45:54 -0700101 user, clientIp, std::nullopt, persistent_data::SessionType::Basic,
102 isConfigureSelfOnly);
James Feist3909dc82020-04-03 10:58:55 -0700103}
104
Patrick Williams504af5a2025-02-03 14:29:03 -0500105inline std::shared_ptr<persistent_data::UserSession> performTokenAuth(
106 std::string_view authHeader)
James Feist3909dc82020-04-03 10:58:55 -0700107{
Ed Tanous62598e32023-07-17 17:06:25 -0700108 BMCWEB_LOG_DEBUG("[AuthMiddleware] Token authentication");
Ed Tanous11ba3972022-07-11 09:50:41 -0700109 if (!authHeader.starts_with("Token "))
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700110 {
111 return nullptr;
112 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000113 std::string_view token = authHeader.substr(strlen("Token "));
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700114 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700115 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700116 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700117}
118
Patrick Williams504af5a2025-02-03 14:29:03 -0500119inline std::shared_ptr<persistent_data::UserSession> performXtokenAuth(
120 const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700121{
Ed Tanous62598e32023-07-17 17:06:25 -0700122 BMCWEB_LOG_DEBUG("[AuthMiddleware] X-Auth-Token authentication");
James Feist3909dc82020-04-03 10:58:55 -0700123
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700124 std::string_view token = reqHeader["X-Auth-Token"];
James Feist3909dc82020-04-03 10:58:55 -0700125 if (token.empty())
126 {
127 return nullptr;
128 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700129 auto sessionOut =
James Feist3909dc82020-04-03 10:58:55 -0700130 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700131 return sessionOut;
James Feist3909dc82020-04-03 10:58:55 -0700132}
133
Patrick Williams504af5a2025-02-03 14:29:03 -0500134inline std::shared_ptr<persistent_data::UserSession> performCookieAuth(
135 boost::beast::http::verb method [[maybe_unused]],
136 const boost::beast::http::header<true>& reqHeader)
James Feist3909dc82020-04-03 10:58:55 -0700137{
Ed Tanous9f217c22024-04-19 17:22:43 -0700138 using headers = boost::beast::http::header<true>;
139 std::pair<headers::const_iterator, headers::const_iterator> cookies =
140 reqHeader.equal_range(boost::beast::http::field::cookie);
James Feist3909dc82020-04-03 10:58:55 -0700141
Ed Tanous9f217c22024-04-19 17:22:43 -0700142 for (auto it = cookies.first; it != cookies.second; it++)
James Feist3909dc82020-04-03 10:58:55 -0700143 {
Ed Tanous9f217c22024-04-19 17:22:43 -0700144 std::string_view cookieValue = it->value();
145 BMCWEB_LOG_DEBUG("Checking cookie {}", cookieValue);
146 auto startIndex = cookieValue.find("SESSION=");
147 if (startIndex == std::string::npos)
148 {
149 BMCWEB_LOG_DEBUG(
150 "Cookie was present, but didn't look like a session {}",
151 cookieValue);
152 continue;
153 }
154 startIndex += sizeof("SESSION=") - 1;
155 auto endIndex = cookieValue.find(';', startIndex);
156 if (endIndex == std::string::npos)
157 {
158 endIndex = cookieValue.size();
159 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400160 std::string_view authKey =
161 cookieValue.substr(startIndex, endIndex - startIndex);
James Feist3909dc82020-04-03 10:58:55 -0700162
Ed Tanous9f217c22024-04-19 17:22:43 -0700163 std::shared_ptr<persistent_data::UserSession> sessionOut =
164 persistent_data::SessionStore::getInstance().loginSessionByToken(
165 authKey);
166 if (sessionOut == nullptr)
167 {
168 return nullptr;
169 }
170 sessionOut->cookieAuth = true;
James Feist3909dc82020-04-03 10:58:55 -0700171
Ed Tanous576db692024-05-10 16:37:56 -0700172 if constexpr (!BMCWEB_INSECURE_DISABLE_CSRF)
Ed Tanous25b54db2024-04-17 15:40:31 -0700173 {
174 // RFC7231 defines methods that need csrf protection
175 if (method != boost::beast::http::verb::get)
Ed Tanous9f217c22024-04-19 17:22:43 -0700176 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700177 std::string_view csrf = reqHeader["X-XSRF-TOKEN"];
178 // Make sure both tokens are filled
179 if (csrf.empty() || sessionOut->csrfToken.empty())
180 {
181 return nullptr;
182 }
183
184 if (csrf.size() != persistent_data::sessionTokenSize)
185 {
186 return nullptr;
187 }
188 // Reject if csrf token not available
Ed Tanous724985f2024-06-05 09:19:06 -0700189 if (!bmcweb::constantTimeStringCompare(csrf,
190 sessionOut->csrfToken))
Ed Tanous25b54db2024-04-17 15:40:31 -0700191 {
192 return nullptr;
193 }
Ed Tanous9f217c22024-04-19 17:22:43 -0700194 }
James Feist3909dc82020-04-03 10:58:55 -0700195 }
Ed Tanous3eaecac2024-05-08 16:26:24 -0700196 return sessionOut;
Ed Tanous9f217c22024-04-19 17:22:43 -0700197 }
198 return nullptr;
James Feist3909dc82020-04-03 10:58:55 -0700199}
200
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400201inline std::shared_ptr<persistent_data::UserSession> performTLSAuth(
202 Response& res, const std::shared_ptr<persistent_data::UserSession>& session)
James Feist6964c982020-07-28 16:10:23 -0700203{
Ed Tanous3281bcf2024-06-25 16:02:05 -0700204 if (session != nullptr)
James Feist6964c982020-07-28 16:10:23 -0700205 {
Ed Tanous994fd862023-06-06 13:37:03 -0700206 res.addHeader(boost::beast::http::field::set_cookie,
207 "IsAuthenticated=true; Secure");
Ed Tanous62598e32023-07-17 17:06:25 -0700208 BMCWEB_LOG_DEBUG(
209 " TLS session: {} with cookie will be used for this request.",
Ed Tanous3281bcf2024-06-25 16:02:05 -0700210 session->uniqueId);
James Feist6964c982020-07-28 16:10:23 -0700211 }
Ed Tanous3281bcf2024-06-25 16:02:05 -0700212
213 return session;
James Feist6964c982020-07-28 16:10:23 -0700214}
215
James Feist3909dc82020-04-03 10:58:55 -0700216// checks if request can be forwarded without authentication
Ed Tanous25b54db2024-04-17 15:40:31 -0700217inline bool isOnAllowlist(std::string_view url, boost::beast::http::verb method)
James Feist3909dc82020-04-03 10:58:55 -0700218{
Ed Tanous38221502024-06-03 12:19:38 -0700219 // Handle the case where the router registers routes as both ending with /
220 // and not.
VinceChang66378f5df132024-06-14 17:13:16 +0800221 if (url.ends_with('/') && url != "/")
Ed Tanous38221502024-06-03 12:19:38 -0700222 {
223 url.remove_suffix(1);
224 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700225 if (boost::beast::http::verb::get == method)
James Feist3909dc82020-04-03 10:58:55 -0700226 {
Patrick Williams504af5a2025-02-03 14:29:03 -0500227 if ((url == "/redfish") || //
228 (url == "/redfish/v1") || //
Ed Tanous38221502024-06-03 12:19:38 -0700229 (url == "/redfish/v1/odata") || //
230 (url == "/redfish/v1/$metadata"))
James Feist3909dc82020-04-03 10:58:55 -0700231 {
232 return true;
233 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700234 if (crow::webroutes::routes.find(std::string(url)) !=
Ed Tanousd4d25792020-09-29 15:15:03 -0700235 crow::webroutes::routes.end())
James Feist3909dc82020-04-03 10:58:55 -0700236 {
237 return true;
238 }
239 }
240
241 // it's allowed to POST on session collection & login without
242 // authentication
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700243 if (boost::beast::http::verb::post == method)
James Feist3909dc82020-04-03 10:58:55 -0700244 {
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700245 if ((url == "/redfish/v1/SessionService/Sessions") ||
Ed Tanouse76cd862022-03-14 09:12:00 -0700246 (url == "/redfish/v1/SessionService/Sessions/Members") ||
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700247 (url == "/login"))
James Feist3909dc82020-04-03 10:58:55 -0700248 {
249 return true;
250 }
251 }
252
253 return false;
254}
255
Ed Tanous25b54db2024-04-17 15:40:31 -0700256inline std::shared_ptr<persistent_data::UserSession> authenticate(
257 const boost::asio::ip::address& ipAddress [[maybe_unused]],
258 Response& res [[maybe_unused]],
259 boost::beast::http::verb method [[maybe_unused]],
260 const boost::beast::http::header<true>& reqHeader,
261 [[maybe_unused]] const std::shared_ptr<persistent_data::UserSession>&
262 session)
James Feist3909dc82020-04-03 10:58:55 -0700263{
Ed Tanous52cc1122020-07-18 13:51:21 -0700264 const persistent_data::AuthConfigMethods& authMethodsConfig =
265 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
James Feist3909dc82020-04-03 10:58:55 -0700266
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700267 std::shared_ptr<persistent_data::UserSession> sessionOut = nullptr;
Ed Tanous25b54db2024-04-17 15:40:31 -0700268 if constexpr (BMCWEB_MUTUAL_TLS_AUTH)
James Feist6964c982020-07-28 16:10:23 -0700269 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700270 if (authMethodsConfig.tls)
271 {
Ed Tanous3281bcf2024-06-25 16:02:05 -0700272 sessionOut = performTLSAuth(res, session);
Ed Tanous25b54db2024-04-17 15:40:31 -0700273 }
James Feist6964c982020-07-28 16:10:23 -0700274 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700275 if constexpr (BMCWEB_XTOKEN_AUTH)
James Feist3909dc82020-04-03 10:58:55 -0700276 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700277 if (sessionOut == nullptr && authMethodsConfig.xtoken)
278 {
279 sessionOut = performXtokenAuth(reqHeader);
280 }
James Feist3909dc82020-04-03 10:58:55 -0700281 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700282 if constexpr (BMCWEB_COOKIE_AUTH)
James Feist3909dc82020-04-03 10:58:55 -0700283 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700284 if (sessionOut == nullptr && authMethodsConfig.cookie)
285 {
286 sessionOut = performCookieAuth(method, reqHeader);
287 }
James Feist3909dc82020-04-03 10:58:55 -0700288 }
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700289 std::string_view authHeader = reqHeader["Authorization"];
Ed Tanous62598e32023-07-17 17:06:25 -0700290 BMCWEB_LOG_DEBUG("authHeader={}", authHeader);
Ed Tanous25b54db2024-04-17 15:40:31 -0700291 if constexpr (BMCWEB_SESSION_AUTH)
James Feist3909dc82020-04-03 10:58:55 -0700292 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700293 if (sessionOut == nullptr && authMethodsConfig.sessionToken)
294 {
295 sessionOut = performTokenAuth(authHeader);
296 }
James Feist3909dc82020-04-03 10:58:55 -0700297 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700298 if constexpr (BMCWEB_BASIC_AUTH)
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700299 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700300 if (sessionOut == nullptr && authMethodsConfig.basic)
301 {
302 sessionOut = performBasicAuth(ipAddress, authHeader);
303 }
John Edward Broadbent97a056a2021-09-10 14:56:19 -0700304 }
305 if (sessionOut != nullptr)
306 {
307 return sessionOut;
308 }
309
John Edward Broadbent59b98b22021-07-13 15:36:32 -0700310 return nullptr;
James Feist3909dc82020-04-03 10:58:55 -0700311}
312
Nan Zhoud055a342022-05-25 01:15:34 +0000313} // namespace authentication
James Feist3909dc82020-04-03 10:58:55 -0700314} // namespace crow