aboutsummaryrefslogtreecommitdiff
path: root/src/zenhttp/security/passwordsecurityfilter.cpp
blob: 0428b5437fe879cc7d4786c6c702e8bfd99e8756 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// Copyright Epic Games, Inc. All Rights Reserved.

#include "zenhttp/security/passwordsecurityfilter.h"

#include <zencore/base64.h>
#include <zencore/compactbinaryutil.h>
#include <zencore/fmtutils.h>

namespace zen {

using namespace std::literals;

PasswordHttpFilter::Configuration
PasswordHttpFilter::ReadConfiguration(CbObjectView Config)
{
	Configuration Result;
	if (CbObjectView PasswordType = Config["basic"sv].AsObjectView(); PasswordType)
	{
		Result.AuthenticationTypeString	  = "Basic ";
		std::string_view Username		  = PasswordType["username"sv].AsString();
		std::string_view Password		  = PasswordType["password"sv].AsString();
		std::string		 UsernamePassword = fmt::format("{}:{}", Username, Password);
		Result.PasswordConfig.Password.resize(Base64::GetEncodedDataSize(uint32_t(UsernamePassword.length())));
		Base64::Encode(reinterpret_cast<const uint8_t*>(UsernamePassword.data()),
					   uint32_t(UsernamePassword.size()),
					   const_cast<char*>(Result.PasswordConfig.Password.data()));
	}
	Result.PasswordConfig.ProtectMachineLocalRequests = Config["protect-machine-local-requests"sv].AsBool();
	Result.PasswordConfig.UnprotectedUris			  = compactbinary_helpers::ReadArray<std::string>("unprotected-uris"sv, Config);
	return Result;
}

IHttpRequestFilter::Result
PasswordHttpFilter::FilterRequest(HttpServerRequest& Request)
{
	std::string_view Password;
	std::string_view AuthorizationHeader = Request.GetAuthorizationHeader();

	// Look for the configured scheme prefix (e.g. "Basic ") case-insensitively.
	// Only extract a candidate credential when the scheme prefix matches and the
	// remainder passes two defense-in-depth checks:
	//   * trim any trailing LWS (SP / HTAB) from the credential, so a
	//     well-behaved but "Authorization: Basic <b64> " style header still
	//     works instead of producing a silent 403;
	//   * reject the header outright if the credential contains any control
	//     byte (< 0x20) or DEL (0x7F) — valid Base64 never does, and passing
	//     such bytes downstream is a log-injection / smuggling vector.
	if (AuthorizationHeader.length() > m_AuthenticationTypeString.length() &&
		StrCaseCompare(AuthorizationHeader.data(), m_AuthenticationTypeString.c_str(), m_AuthenticationTypeString.length()) == 0)
	{
		std::string_view Candidate = AuthorizationHeader.substr(m_AuthenticationTypeString.length());

		while (!Candidate.empty() && (Candidate.back() == ' ' || Candidate.back() == '\t'))
		{
			Candidate.remove_suffix(1);
		}

		bool HasControlByte = false;
		for (char C : Candidate)
		{
			const uint8_t B = static_cast<uint8_t>(C);
			if (B < 0x20 || B == 0x7F)
			{
				HasControlByte = true;
				break;
			}
		}

		if (!HasControlByte)
		{
			Password = Candidate;
		}
	}

	bool IsAllowed =
		m_PasswordSecurity.IsAllowed(Password, Request.Service().BaseUri(), Request.RelativeUri(), Request.IsLocalMachineRequest());
	if (IsAllowed)
	{
		return Result::Accepted;
	}
	return Result::Forbidden;
}

}  // namespace zen