#pragma once #include "cookies.hpp" #include "forward_unauthorized.hpp" #include "http_request.hpp" #include "http_response.hpp" #include "http_utility.hpp" #include "pam_authenticate.hpp" #include "webroutes.hpp" #include #include #include namespace crow { namespace authentication { inline std::shared_ptr performBasicAuth( const boost::asio::ip::address& clientIp, std::string_view authHeader) { BMCWEB_LOG_DEBUG("[AuthMiddleware] Basic authentication"); if (!authHeader.starts_with("Basic ")) { return nullptr; } std::string_view param = authHeader.substr(strlen("Basic ")); std::string authData; if (!crow::utility::base64Decode(param, authData)) { return nullptr; } std::size_t separator = authData.find(':'); if (separator == std::string::npos) { return nullptr; } std::string user = authData.substr(0, separator); separator += 1; if (separator > authData.size()) { return nullptr; } std::string pass = authData.substr(separator); BMCWEB_LOG_DEBUG("[AuthMiddleware] Authenticating user: {}", user); BMCWEB_LOG_DEBUG("[AuthMiddleware] User IPAddress: {}", clientIp.to_string()); int pamrc = pamAuthenticateUser(user, pass, std::nullopt); bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD; if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly) { return nullptr; } // Attempt to locate an existing Basic Auth session from the same ip address // and user for (auto& session : persistent_data::SessionStore::getInstance().getSessions()) { if (session->sessionType != persistent_data::SessionType::Basic) { continue; } if (session->clientIp != redfish::ip_util::toString(clientIp)) { continue; } if (session->username != user) { continue; } return session; } return persistent_data::SessionStore::getInstance().generateUserSession( user, clientIp, std::nullopt, persistent_data::SessionType::Basic, isConfigureSelfOnly); } inline std::shared_ptr performTokenAuth(std::string_view authHeader) { BMCWEB_LOG_DEBUG("[AuthMiddleware] Token authentication"); if (!authHeader.starts_with("Token ")) { return nullptr; } std::string_view token = authHeader.substr(strlen("Token ")); auto sessionOut = persistent_data::SessionStore::getInstance().loginSessionByToken(token); return sessionOut; } inline std::shared_ptr performXtokenAuth(const boost::beast::http::header& reqHeader) { BMCWEB_LOG_DEBUG("[AuthMiddleware] X-Auth-Token authentication"); std::string_view token = reqHeader["X-Auth-Token"]; if (token.empty()) { return nullptr; } auto sessionOut = persistent_data::SessionStore::getInstance().loginSessionByToken(token); return sessionOut; } inline std::shared_ptr performCookieAuth(boost::beast::http::verb method [[maybe_unused]], const boost::beast::http::header& reqHeader) { using headers = boost::beast::http::header; std::pair cookies = reqHeader.equal_range(boost::beast::http::field::cookie); for (auto it = cookies.first; it != cookies.second; it++) { std::string_view cookieValue = it->value(); BMCWEB_LOG_DEBUG("Checking cookie {}", cookieValue); auto startIndex = cookieValue.find("SESSION="); if (startIndex == std::string::npos) { BMCWEB_LOG_DEBUG( "Cookie was present, but didn't look like a session {}", cookieValue); continue; } startIndex += sizeof("SESSION=") - 1; auto endIndex = cookieValue.find(';', startIndex); if (endIndex == std::string::npos) { endIndex = cookieValue.size(); } std::string_view authKey = cookieValue.substr(startIndex, endIndex - startIndex); std::shared_ptr sessionOut = persistent_data::SessionStore::getInstance().loginSessionByToken( authKey); if (sessionOut == nullptr) { return nullptr; } sessionOut->cookieAuth = true; if constexpr (!BMCWEB_INSECURE_DISABLE_CSRF) { // RFC7231 defines methods that need csrf protection if (method != boost::beast::http::verb::get) { std::string_view csrf = reqHeader["X-XSRF-TOKEN"]; // Make sure both tokens are filled if (csrf.empty() || sessionOut->csrfToken.empty()) { return nullptr; } if (csrf.size() != persistent_data::sessionTokenSize) { return nullptr; } // Reject if csrf token not available if (!bmcweb::constantTimeStringCompare(csrf, sessionOut->csrfToken)) { return nullptr; } } } return sessionOut; } return nullptr; } inline std::shared_ptr performTLSAuth( Response& res, const std::shared_ptr& session) { if (session != nullptr) { res.addHeader(boost::beast::http::field::set_cookie, "IsAuthenticated=true; Secure"); BMCWEB_LOG_DEBUG( " TLS session: {} with cookie will be used for this request.", session->uniqueId); } return session; } // checks if request can be forwarded without authentication inline bool isOnAllowlist(std::string_view url, boost::beast::http::verb method) { // Handle the case where the router registers routes as both ending with / // and not. if (url.ends_with('/') && url != "/") { url.remove_suffix(1); } if (boost::beast::http::verb::get == method) { if ((url == "/redfish") || // (url == "/redfish/v1") || // (url == "/redfish/v1/odata") || // (url == "/redfish/v1/$metadata")) { return true; } if (crow::webroutes::routes.find(std::string(url)) != crow::webroutes::routes.end()) { return true; } } // it's allowed to POST on session collection & login without // authentication if (boost::beast::http::verb::post == method) { if ((url == "/redfish/v1/SessionService/Sessions") || (url == "/redfish/v1/SessionService/Sessions/Members") || (url == "/login")) { return true; } } return false; } inline std::shared_ptr authenticate( const boost::asio::ip::address& ipAddress [[maybe_unused]], Response& res [[maybe_unused]], boost::beast::http::verb method [[maybe_unused]], const boost::beast::http::header& reqHeader, [[maybe_unused]] const std::shared_ptr& session) { const persistent_data::AuthConfigMethods& authMethodsConfig = persistent_data::SessionStore::getInstance().getAuthMethodsConfig(); std::shared_ptr sessionOut = nullptr; if constexpr (BMCWEB_MUTUAL_TLS_AUTH) { if (authMethodsConfig.tls) { sessionOut = performTLSAuth(res, session); } } if constexpr (BMCWEB_XTOKEN_AUTH) { if (sessionOut == nullptr && authMethodsConfig.xtoken) { sessionOut = performXtokenAuth(reqHeader); } } if constexpr (BMCWEB_COOKIE_AUTH) { if (sessionOut == nullptr && authMethodsConfig.cookie) { sessionOut = performCookieAuth(method, reqHeader); } } std::string_view authHeader = reqHeader["Authorization"]; BMCWEB_LOG_DEBUG("authHeader={}", authHeader); if constexpr (BMCWEB_SESSION_AUTH) { if (sessionOut == nullptr && authMethodsConfig.sessionToken) { sessionOut = performTokenAuth(authHeader); } } if constexpr (BMCWEB_BASIC_AUTH) { if (sessionOut == nullptr && authMethodsConfig.basic) { sessionOut = performBasicAuth(ipAddress, authHeader); } } if (sessionOut != nullptr) { return sessionOut; } return nullptr; } } // namespace authentication } // namespace crow