/proc/self/cwd/source/extensions/filters/common/rbac/matchers.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include "source/extensions/filters/common/rbac/matchers.h" |
2 | | |
3 | | #include "envoy/config/rbac/v3/rbac.pb.h" |
4 | | #include "envoy/upstream/upstream.h" |
5 | | |
6 | | #include "source/common/config/utility.h" |
7 | | #include "source/extensions/filters/common/rbac/matcher_extension.h" |
8 | | |
9 | | namespace Envoy { |
10 | | namespace Extensions { |
11 | | namespace Filters { |
12 | | namespace Common { |
13 | | namespace RBAC { |
14 | | |
15 | | MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Permission& permission, |
16 | 11.9k | ProtobufMessage::ValidationVisitor& validation_visitor) { |
17 | 11.9k | switch (permission.rule_case()) { |
18 | 1.54k | case envoy::config::rbac::v3::Permission::RuleCase::kAndRules: |
19 | 1.54k | return std::make_shared<const AndMatcher>(permission.and_rules(), validation_visitor); |
20 | 867 | case envoy::config::rbac::v3::Permission::RuleCase::kOrRules: |
21 | 867 | return std::make_shared<const OrMatcher>(permission.or_rules(), validation_visitor); |
22 | 34 | case envoy::config::rbac::v3::Permission::RuleCase::kHeader: |
23 | 34 | return std::make_shared<const HeaderMatcher>(permission.header()); |
24 | 26 | case envoy::config::rbac::v3::Permission::RuleCase::kDestinationIp: |
25 | 26 | return std::make_shared<const IPMatcher>(permission.destination_ip(), |
26 | 26 | IPMatcher::Type::DownstreamLocal); |
27 | 5.51k | case envoy::config::rbac::v3::Permission::RuleCase::kDestinationPort: |
28 | 5.51k | return std::make_shared<const PortMatcher>(permission.destination_port()); |
29 | 241 | case envoy::config::rbac::v3::Permission::RuleCase::kDestinationPortRange: |
30 | 241 | return std::make_shared<const PortRangeMatcher>(permission.destination_port_range()); |
31 | 1.76k | case envoy::config::rbac::v3::Permission::RuleCase::kAny: |
32 | 1.76k | return std::make_shared<const AlwaysMatcher>(); |
33 | 115 | case envoy::config::rbac::v3::Permission::RuleCase::kMetadata: |
34 | 115 | return std::make_shared<const MetadataMatcher>(permission.metadata()); |
35 | 851 | case envoy::config::rbac::v3::Permission::RuleCase::kNotRule: |
36 | 851 | return std::make_shared<const NotMatcher>(permission.not_rule(), validation_visitor); |
37 | 889 | case envoy::config::rbac::v3::Permission::RuleCase::kRequestedServerName: |
38 | 889 | return std::make_shared<const RequestedServerNameMatcher>(permission.requested_server_name()); |
39 | 97 | case envoy::config::rbac::v3::Permission::RuleCase::kUrlPath: |
40 | 97 | return std::make_shared<const PathMatcher>(permission.url_path()); |
41 | 0 | case envoy::config::rbac::v3::Permission::RuleCase::kMatcher: { |
42 | 0 | auto& factory = |
43 | 0 | Config::Utility::getAndCheckFactory<MatcherExtensionFactory>(permission.matcher()); |
44 | 0 | return factory.create(permission.matcher(), validation_visitor); |
45 | 0 | } |
46 | 0 | case envoy::config::rbac::v3::Permission::RuleCase::RULE_NOT_SET: |
47 | 0 | break; // Fall through to PANIC. |
48 | 11.9k | } |
49 | 0 | PANIC_DUE_TO_CORRUPT_ENUM; |
50 | 0 | } |
51 | | |
52 | 14.7k | MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Principal& principal) { |
53 | 14.7k | switch (principal.identifier_case()) { |
54 | 512 | case envoy::config::rbac::v3::Principal::IdentifierCase::kAndIds: |
55 | 512 | return std::make_shared<const AndMatcher>(principal.and_ids()); |
56 | 1.78k | case envoy::config::rbac::v3::Principal::IdentifierCase::kOrIds: |
57 | 1.78k | return std::make_shared<const OrMatcher>(principal.or_ids()); |
58 | 4.16k | case envoy::config::rbac::v3::Principal::IdentifierCase::kAuthenticated: |
59 | 4.16k | return std::make_shared<const AuthenticatedMatcher>(principal.authenticated()); |
60 | 452 | case envoy::config::rbac::v3::Principal::IdentifierCase::kSourceIp: |
61 | 452 | return std::make_shared<const IPMatcher>(principal.source_ip(), |
62 | 452 | IPMatcher::Type::ConnectionRemote); |
63 | 302 | case envoy::config::rbac::v3::Principal::IdentifierCase::kDirectRemoteIp: |
64 | 302 | return std::make_shared<const IPMatcher>(principal.direct_remote_ip(), |
65 | 302 | IPMatcher::Type::DownstreamDirectRemote); |
66 | 518 | case envoy::config::rbac::v3::Principal::IdentifierCase::kRemoteIp: |
67 | 518 | return std::make_shared<const IPMatcher>(principal.remote_ip(), |
68 | 518 | IPMatcher::Type::DownstreamRemote); |
69 | 34 | case envoy::config::rbac::v3::Principal::IdentifierCase::kHeader: |
70 | 34 | return std::make_shared<const HeaderMatcher>(principal.header()); |
71 | 4.95k | case envoy::config::rbac::v3::Principal::IdentifierCase::kAny: |
72 | 4.95k | return std::make_shared<const AlwaysMatcher>(); |
73 | 300 | case envoy::config::rbac::v3::Principal::IdentifierCase::kMetadata: |
74 | 300 | return std::make_shared<const MetadataMatcher>(principal.metadata()); |
75 | 1.59k | case envoy::config::rbac::v3::Principal::IdentifierCase::kNotId: |
76 | 1.59k | return std::make_shared<const NotMatcher>(principal.not_id()); |
77 | 158 | case envoy::config::rbac::v3::Principal::IdentifierCase::kUrlPath: |
78 | 158 | return std::make_shared<const PathMatcher>(principal.url_path()); |
79 | 17 | case envoy::config::rbac::v3::Principal::IdentifierCase::kFilterState: |
80 | 17 | return std::make_shared<const FilterStateMatcher>(principal.filter_state()); |
81 | 0 | case envoy::config::rbac::v3::Principal::IdentifierCase::IDENTIFIER_NOT_SET: |
82 | 0 | break; // Fall through to PANIC. |
83 | 14.7k | } |
84 | 0 | PANIC_DUE_TO_CORRUPT_ENUM; |
85 | 0 | } |
86 | | |
87 | | AndMatcher::AndMatcher(const envoy::config::rbac::v3::Permission::Set& set, |
88 | 1.54k | ProtobufMessage::ValidationVisitor& validation_visitor) { |
89 | 2.60k | for (const auto& rule : set.rules()) { |
90 | 2.60k | matchers_.push_back(Matcher::create(rule, validation_visitor)); |
91 | 2.60k | } |
92 | 1.54k | } |
93 | | |
94 | 512 | AndMatcher::AndMatcher(const envoy::config::rbac::v3::Principal::Set& set) { |
95 | 1.84k | for (const auto& id : set.ids()) { |
96 | 1.84k | matchers_.push_back(Matcher::create(id)); |
97 | 1.84k | } |
98 | 512 | } |
99 | | |
100 | | bool AndMatcher::matches(const Network::Connection& connection, |
101 | | const Envoy::Http::RequestHeaderMap& headers, |
102 | 1.47k | const StreamInfo::StreamInfo& info) const { |
103 | 2.08k | for (const auto& matcher : matchers_) { |
104 | 2.08k | if (!matcher->matches(connection, headers, info)) { |
105 | 684 | return false; |
106 | 684 | } |
107 | 2.08k | } |
108 | | |
109 | 786 | return true; |
110 | 1.47k | } |
111 | | |
112 | | OrMatcher::OrMatcher(const Protobuf::RepeatedPtrField<envoy::config::rbac::v3::Permission>& rules, |
113 | 5.59k | ProtobufMessage::ValidationVisitor& validation_visitor) { |
114 | 8.49k | for (const auto& rule : rules) { |
115 | 8.49k | matchers_.push_back(Matcher::create(rule, validation_visitor)); |
116 | 8.49k | } |
117 | 5.59k | } |
118 | | |
119 | 6.50k | OrMatcher::OrMatcher(const Protobuf::RepeatedPtrField<envoy::config::rbac::v3::Principal>& ids) { |
120 | 11.3k | for (const auto& id : ids) { |
121 | 11.3k | matchers_.push_back(Matcher::create(id)); |
122 | 11.3k | } |
123 | 6.50k | } |
124 | | |
125 | | bool OrMatcher::matches(const Network::Connection& connection, |
126 | | const Envoy::Http::RequestHeaderMap& headers, |
127 | 31.5k | const StreamInfo::StreamInfo& info) const { |
128 | 37.5k | for (const auto& matcher : matchers_) { |
129 | 37.5k | if (matcher->matches(connection, headers, info)) { |
130 | 28.7k | return true; |
131 | 28.7k | } |
132 | 37.5k | } |
133 | | |
134 | 2.85k | return false; |
135 | 31.5k | } |
136 | | |
137 | | bool NotMatcher::matches(const Network::Connection& connection, |
138 | | const Envoy::Http::RequestHeaderMap& headers, |
139 | 5.42k | const StreamInfo::StreamInfo& info) const { |
140 | 5.42k | return !matcher_->matches(connection, headers, info); |
141 | 5.42k | } |
142 | | |
143 | | bool HeaderMatcher::matches(const Network::Connection&, |
144 | | const Envoy::Http::RequestHeaderMap& headers, |
145 | 49 | const StreamInfo::StreamInfo&) const { |
146 | 49 | return Envoy::Http::HeaderUtility::matchHeaders(headers, header_); |
147 | 49 | } |
148 | | |
149 | | bool IPMatcher::matches(const Network::Connection& connection, const Envoy::Http::RequestHeaderMap&, |
150 | 1.89k | const StreamInfo::StreamInfo& info) const { |
151 | 1.89k | Envoy::Network::Address::InstanceConstSharedPtr ip; |
152 | 1.89k | switch (type_) { |
153 | 857 | case ConnectionRemote: |
154 | 857 | ip = connection.connectionInfoProvider().remoteAddress(); |
155 | 857 | break; |
156 | 24 | case DownstreamLocal: |
157 | 24 | ip = info.downstreamAddressProvider().localAddress(); |
158 | 24 | break; |
159 | 715 | case DownstreamDirectRemote: |
160 | 715 | ip = info.downstreamAddressProvider().directRemoteAddress(); |
161 | 715 | break; |
162 | 295 | case DownstreamRemote: |
163 | 295 | ip = info.downstreamAddressProvider().remoteAddress(); |
164 | 295 | break; |
165 | 1.89k | } |
166 | 1.89k | return range_.isInRange(*ip.get()); |
167 | 1.89k | } |
168 | | |
169 | | bool PortMatcher::matches(const Network::Connection&, const Envoy::Http::RequestHeaderMap&, |
170 | 6.94k | const StreamInfo::StreamInfo& info) const { |
171 | 6.94k | const Envoy::Network::Address::Ip* ip = |
172 | 6.94k | info.downstreamAddressProvider().localAddress().get()->ip(); |
173 | 6.94k | return ip && ip->port() == port_; |
174 | 6.94k | } |
175 | | |
176 | | PortRangeMatcher::PortRangeMatcher(const ::envoy::type::v3::Int32Range& range) |
177 | 241 | : start_(range.start()), end_(range.end()) { |
178 | 241 | auto start = range.start(); |
179 | 241 | auto end = range.end(); |
180 | 241 | if (start < 0 || start > 65536) { |
181 | 1 | throw EnvoyException(fmt::format("range start {} is out of bounds", start)); |
182 | 1 | } |
183 | 240 | if (end < 0 || end > 65536) { |
184 | 3 | throw EnvoyException(fmt::format("range end {} is out of bounds", end)); |
185 | 3 | } |
186 | 237 | if (start >= end) { |
187 | 5 | throw EnvoyException( |
188 | 5 | fmt::format("range start {} cannot be greater or equal than range end {}", start, end)); |
189 | 5 | } |
190 | 237 | } |
191 | | |
192 | | bool PortRangeMatcher::matches(const Network::Connection&, const Envoy::Http::RequestHeaderMap&, |
193 | 1.46k | const StreamInfo::StreamInfo& info) const { |
194 | 1.46k | const Envoy::Network::Address::Ip* ip = |
195 | 1.46k | info.downstreamAddressProvider().localAddress().get()->ip(); |
196 | 1.46k | if (ip) { |
197 | 25 | const auto port = ip->port(); |
198 | 25 | return start_ <= port && port < end_; |
199 | 1.43k | } else { |
200 | 1.43k | return false; |
201 | 1.43k | } |
202 | 1.46k | } |
203 | | |
204 | | bool AuthenticatedMatcher::matches(const Network::Connection& connection, |
205 | | const Envoy::Http::RequestHeaderMap&, |
206 | 1.36k | const StreamInfo::StreamInfo&) const { |
207 | 1.36k | const auto& ssl = connection.ssl(); |
208 | 1.36k | if (!ssl) { // connection was not authenticated |
209 | 1.36k | return false; |
210 | 1.36k | } else if (!matcher_.has_value()) { // matcher allows any subject |
211 | 0 | return true; |
212 | 0 | } |
213 | | |
214 | | // If set, The URI SAN or DNS SAN in that order is used as Principal, otherwise the subject field |
215 | | // is used. |
216 | 0 | if (!ssl->uriSanPeerCertificate().empty()) { |
217 | 0 | for (const std::string& uri : ssl->uriSanPeerCertificate()) { |
218 | 0 | if (matcher_.value().match(uri)) { |
219 | 0 | return true; |
220 | 0 | } |
221 | 0 | } |
222 | 0 | } |
223 | 0 | if (!ssl->dnsSansPeerCertificate().empty()) { |
224 | 0 | for (const std::string& dns : ssl->dnsSansPeerCertificate()) { |
225 | 0 | if (matcher_.value().match(dns)) { |
226 | 0 | return true; |
227 | 0 | } |
228 | 0 | } |
229 | 0 | } |
230 | 0 | return matcher_.value().match(ssl->subjectPeerCertificate()); |
231 | 0 | } |
232 | | |
233 | | bool MetadataMatcher::matches(const Network::Connection&, const Envoy::Http::RequestHeaderMap&, |
234 | 295 | const StreamInfo::StreamInfo& info) const { |
235 | 295 | return matcher_.match(info.dynamicMetadata()); |
236 | 295 | } |
237 | | |
238 | | bool FilterStateMatcher::matches(const Network::Connection&, const Envoy::Http::RequestHeaderMap&, |
239 | 15 | const StreamInfo::StreamInfo& info) const { |
240 | 15 | return matcher_.match(info.filterState()); |
241 | 15 | } |
242 | | |
243 | | bool PolicyMatcher::matches(const Network::Connection& connection, |
244 | | const Envoy::Http::RequestHeaderMap& headers, |
245 | 15.0k | const StreamInfo::StreamInfo& info) const { |
246 | 15.0k | return permissions_.matches(connection, headers, info) && |
247 | 15.0k | principals_.matches(connection, headers, info) && |
248 | 15.0k | (expr_ == nullptr ? true : Expr::matches(*expr_, info, headers)); |
249 | 15.0k | } |
250 | | |
251 | | bool RequestedServerNameMatcher::matches(const Network::Connection& connection, |
252 | | const Envoy::Http::RequestHeaderMap&, |
253 | 569 | const StreamInfo::StreamInfo&) const { |
254 | 569 | return match(connection.requestedServerName()); |
255 | 569 | } |
256 | | |
257 | | bool PathMatcher::matches(const Network::Connection&, const Envoy::Http::RequestHeaderMap& headers, |
258 | 183 | const StreamInfo::StreamInfo&) const { |
259 | 183 | if (headers.Path() == nullptr) { |
260 | 0 | return false; |
261 | 0 | } |
262 | 183 | return path_matcher_.match(headers.getPathValue()); |
263 | 183 | } |
264 | | |
265 | | } // namespace RBAC |
266 | | } // namespace Common |
267 | | } // namespace Filters |
268 | | } // namespace Extensions |
269 | | } // namespace Envoy |