1/*
2 * Copyright 2021 gRPC authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package rbac
18
19import (
20	"errors"
21	"fmt"
22	"net"
23	"regexp"
24
25	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
26	v3rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3"
27	v3route_componentspb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
28	v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
29	internalmatcher "google.golang.org/grpc/internal/xds/matcher"
30)
31
32// matcher is an interface that takes data about incoming RPC's and returns
33// whether it matches with whatever matcher implements this interface.
34type matcher interface {
35	match(data *rpcData) bool
36}
37
38// policyMatcher helps determine whether an incoming RPC call matches a policy.
39// A policy is a logical role (e.g. Service Admin), which is comprised of
40// permissions and principals. A principal is an identity (or identities) for a
41// downstream subject which are assigned the policy (role), and a permission is
42// an action(s) that a principal(s) can take. A policy matches if both a
43// permission and a principal match, which will be determined by the child or
44// permissions and principal matchers. policyMatcher implements the matcher
45// interface.
46type policyMatcher struct {
47	permissions *orMatcher
48	principals  *orMatcher
49}
50
51func newPolicyMatcher(policy *v3rbacpb.Policy) (*policyMatcher, error) {
52	permissions, err := matchersFromPermissions(policy.Permissions)
53	if err != nil {
54		return nil, err
55	}
56	principals, err := matchersFromPrincipals(policy.Principals)
57	if err != nil {
58		return nil, err
59	}
60	return &policyMatcher{
61		permissions: &orMatcher{matchers: permissions},
62		principals:  &orMatcher{matchers: principals},
63	}, nil
64}
65
66func (pm *policyMatcher) match(data *rpcData) bool {
67	// A policy matches if and only if at least one of its permissions match the
68	// action taking place AND at least one if its principals match the
69	// downstream peer.
70	return pm.permissions.match(data) && pm.principals.match(data)
71}
72
73// matchersFromPermissions takes a list of permissions (can also be
74// a single permission, e.g. from a not matcher which is logically !permission)
75// and returns a list of matchers which correspond to that permission. This will
76// be called in many instances throughout the initial construction of the RBAC
77// engine from the AND and OR matchers and also from the NOT matcher.
78func matchersFromPermissions(permissions []*v3rbacpb.Permission) ([]matcher, error) {
79	var matchers []matcher
80	for _, permission := range permissions {
81		switch permission.GetRule().(type) {
82		case *v3rbacpb.Permission_AndRules:
83			mList, err := matchersFromPermissions(permission.GetAndRules().Rules)
84			if err != nil {
85				return nil, err
86			}
87			matchers = append(matchers, &andMatcher{matchers: mList})
88		case *v3rbacpb.Permission_OrRules:
89			mList, err := matchersFromPermissions(permission.GetOrRules().Rules)
90			if err != nil {
91				return nil, err
92			}
93			matchers = append(matchers, &orMatcher{matchers: mList})
94		case *v3rbacpb.Permission_Any:
95			matchers = append(matchers, &alwaysMatcher{})
96		case *v3rbacpb.Permission_Header:
97			m, err := newHeaderMatcher(permission.GetHeader())
98			if err != nil {
99				return nil, err
100			}
101			matchers = append(matchers, m)
102		case *v3rbacpb.Permission_UrlPath:
103			m, err := newURLPathMatcher(permission.GetUrlPath())
104			if err != nil {
105				return nil, err
106			}
107			matchers = append(matchers, m)
108		case *v3rbacpb.Permission_DestinationIp:
109			m, err := newDestinationIPMatcher(permission.GetDestinationIp())
110			if err != nil {
111				return nil, err
112			}
113			matchers = append(matchers, m)
114		case *v3rbacpb.Permission_DestinationPort:
115			matchers = append(matchers, newPortMatcher(permission.GetDestinationPort()))
116		case *v3rbacpb.Permission_NotRule:
117			mList, err := matchersFromPermissions([]*v3rbacpb.Permission{{Rule: permission.GetNotRule().Rule}})
118			if err != nil {
119				return nil, err
120			}
121			matchers = append(matchers, &notMatcher{matcherToNot: mList[0]})
122		case *v3rbacpb.Permission_Metadata:
123			// Not supported in gRPC RBAC currently - a permission typed as
124			// Metadata in the initial config will be a no-op.
125		case *v3rbacpb.Permission_RequestedServerName:
126			// Not supported in gRPC RBAC currently - a permission typed as
127			// requested server name in the initial config will be a no-op.
128		}
129	}
130	return matchers, nil
131}
132
133func matchersFromPrincipals(principals []*v3rbacpb.Principal) ([]matcher, error) {
134	var matchers []matcher
135	for _, principal := range principals {
136		switch principal.GetIdentifier().(type) {
137		case *v3rbacpb.Principal_AndIds:
138			mList, err := matchersFromPrincipals(principal.GetAndIds().Ids)
139			if err != nil {
140				return nil, err
141			}
142			matchers = append(matchers, &andMatcher{matchers: mList})
143		case *v3rbacpb.Principal_OrIds:
144			mList, err := matchersFromPrincipals(principal.GetOrIds().Ids)
145			if err != nil {
146				return nil, err
147			}
148			matchers = append(matchers, &orMatcher{matchers: mList})
149		case *v3rbacpb.Principal_Any:
150			matchers = append(matchers, &alwaysMatcher{})
151		case *v3rbacpb.Principal_Authenticated_:
152			authenticatedMatcher, err := newAuthenticatedMatcher(principal.GetAuthenticated())
153			if err != nil {
154				return nil, err
155			}
156			matchers = append(matchers, authenticatedMatcher)
157		case *v3rbacpb.Principal_DirectRemoteIp:
158			m, err := newSourceIPMatcher(principal.GetDirectRemoteIp())
159			if err != nil {
160				return nil, err
161			}
162			matchers = append(matchers, m)
163		case *v3rbacpb.Principal_Header:
164			// Do we need an error here?
165			m, err := newHeaderMatcher(principal.GetHeader())
166			if err != nil {
167				return nil, err
168			}
169			matchers = append(matchers, m)
170		case *v3rbacpb.Principal_UrlPath:
171			m, err := newURLPathMatcher(principal.GetUrlPath())
172			if err != nil {
173				return nil, err
174			}
175			matchers = append(matchers, m)
176		case *v3rbacpb.Principal_NotId:
177			mList, err := matchersFromPrincipals([]*v3rbacpb.Principal{{Identifier: principal.GetNotId().Identifier}})
178			if err != nil {
179				return nil, err
180			}
181			matchers = append(matchers, &notMatcher{matcherToNot: mList[0]})
182		case *v3rbacpb.Principal_SourceIp:
183			// The source ip principal identifier is deprecated. Thus, a
184			// principal typed as a source ip in the identifier will be a no-op.
185			// The config should use DirectRemoteIp instead.
186		case *v3rbacpb.Principal_RemoteIp:
187			// Not supported in gRPC RBAC currently - a principal typed as
188			// Remote Ip in the initial config will be a no-op.
189		case *v3rbacpb.Principal_Metadata:
190			// Not supported in gRPC RBAC currently - a principal typed as
191			// Metadata in the initial config will be a no-op.
192		}
193	}
194	return matchers, nil
195}
196
197// orMatcher is a matcher where it successfully matches if one of it's
198// children successfully match. It also logically represents a principal or
199// permission, but can also be it's own entity further down the tree of
200// matchers. orMatcher implements the matcher interface.
201type orMatcher struct {
202	matchers []matcher
203}
204
205func (om *orMatcher) match(data *rpcData) bool {
206	// Range through child matchers and pass in data about incoming RPC, and
207	// only one child matcher has to match to be logically successful.
208	for _, m := range om.matchers {
209		if m.match(data) {
210			return true
211		}
212	}
213	return false
214}
215
216// andMatcher is a matcher that is successful if every child matcher
217// matches. andMatcher implements the matcher interface.
218type andMatcher struct {
219	matchers []matcher
220}
221
222func (am *andMatcher) match(data *rpcData) bool {
223	for _, m := range am.matchers {
224		if !m.match(data) {
225			return false
226		}
227	}
228	return true
229}
230
231// alwaysMatcher is a matcher that will always match. This logically
232// represents an any rule for a permission or a principal. alwaysMatcher
233// implements the matcher interface.
234type alwaysMatcher struct {
235}
236
237func (am *alwaysMatcher) match(data *rpcData) bool {
238	return true
239}
240
241// notMatcher is a matcher that nots an underlying matcher. notMatcher
242// implements the matcher interface.
243type notMatcher struct {
244	matcherToNot matcher
245}
246
247func (nm *notMatcher) match(data *rpcData) bool {
248	return !nm.matcherToNot.match(data)
249}
250
251// headerMatcher is a matcher that matches on incoming HTTP Headers present
252// in the incoming RPC. headerMatcher implements the matcher interface.
253type headerMatcher struct {
254	matcher internalmatcher.HeaderMatcher
255}
256
257func newHeaderMatcher(headerMatcherConfig *v3route_componentspb.HeaderMatcher) (*headerMatcher, error) {
258	var m internalmatcher.HeaderMatcher
259	switch headerMatcherConfig.HeaderMatchSpecifier.(type) {
260	case *v3route_componentspb.HeaderMatcher_ExactMatch:
261		m = internalmatcher.NewHeaderExactMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetExactMatch())
262	case *v3route_componentspb.HeaderMatcher_SafeRegexMatch:
263		regex, err := regexp.Compile(headerMatcherConfig.GetSafeRegexMatch().Regex)
264		if err != nil {
265			return nil, err
266		}
267		m = internalmatcher.NewHeaderRegexMatcher(headerMatcherConfig.Name, regex)
268	case *v3route_componentspb.HeaderMatcher_RangeMatch:
269		m = internalmatcher.NewHeaderRangeMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetRangeMatch().Start, headerMatcherConfig.GetRangeMatch().End)
270	case *v3route_componentspb.HeaderMatcher_PresentMatch:
271		m = internalmatcher.NewHeaderPresentMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetPresentMatch())
272	case *v3route_componentspb.HeaderMatcher_PrefixMatch:
273		m = internalmatcher.NewHeaderPrefixMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetPrefixMatch())
274	case *v3route_componentspb.HeaderMatcher_SuffixMatch:
275		m = internalmatcher.NewHeaderSuffixMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetSuffixMatch())
276	case *v3route_componentspb.HeaderMatcher_ContainsMatch:
277		m = internalmatcher.NewHeaderContainsMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetContainsMatch())
278	default:
279		return nil, errors.New("unknown header matcher type")
280	}
281	if headerMatcherConfig.InvertMatch {
282		m = internalmatcher.NewInvertMatcher(m)
283	}
284	return &headerMatcher{matcher: m}, nil
285}
286
287func (hm *headerMatcher) match(data *rpcData) bool {
288	return hm.matcher.Match(data.md)
289}
290
291// urlPathMatcher matches on the URL Path of the incoming RPC. In gRPC, this
292// logically maps to the full method name the RPC is calling on the server side.
293// urlPathMatcher implements the matcher interface.
294type urlPathMatcher struct {
295	stringMatcher internalmatcher.StringMatcher
296}
297
298func newURLPathMatcher(pathMatcher *v3matcherpb.PathMatcher) (*urlPathMatcher, error) {
299	stringMatcher, err := internalmatcher.StringMatcherFromProto(pathMatcher.GetPath())
300	if err != nil {
301		return nil, err
302	}
303	return &urlPathMatcher{stringMatcher: stringMatcher}, nil
304}
305
306func (upm *urlPathMatcher) match(data *rpcData) bool {
307	return upm.stringMatcher.Match(data.fullMethod)
308}
309
310// sourceIPMatcher and destinationIPMatcher both are matchers that match against
311// a CIDR Range. Two different matchers are needed as the source and ip address
312// come from different parts of the data about incoming RPC's passed in.
313// Matching a CIDR Range means to determine whether the IP Address falls within
314// the CIDR Range or not. They both implement the matcher interface.
315type sourceIPMatcher struct {
316	// ipNet represents the CidrRange that this matcher was configured with.
317	// This is what will source and destination IP's will be matched against.
318	ipNet *net.IPNet
319}
320
321func newSourceIPMatcher(cidrRange *v3corepb.CidrRange) (*sourceIPMatcher, error) {
322	// Convert configuration to a cidrRangeString, as Go standard library has
323	// methods that parse cidr string.
324	cidrRangeString := fmt.Sprintf("%s/%d", cidrRange.AddressPrefix, cidrRange.PrefixLen.Value)
325	_, ipNet, err := net.ParseCIDR(cidrRangeString)
326	if err != nil {
327		return nil, err
328	}
329	return &sourceIPMatcher{ipNet: ipNet}, nil
330}
331
332func (sim *sourceIPMatcher) match(data *rpcData) bool {
333	return sim.ipNet.Contains(net.IP(net.ParseIP(data.peerInfo.Addr.String())))
334}
335
336type destinationIPMatcher struct {
337	ipNet *net.IPNet
338}
339
340func newDestinationIPMatcher(cidrRange *v3corepb.CidrRange) (*destinationIPMatcher, error) {
341	cidrRangeString := fmt.Sprintf("%s/%d", cidrRange.AddressPrefix, cidrRange.PrefixLen.Value)
342	_, ipNet, err := net.ParseCIDR(cidrRangeString)
343	if err != nil {
344		return nil, err
345	}
346	return &destinationIPMatcher{ipNet: ipNet}, nil
347}
348
349func (dim *destinationIPMatcher) match(data *rpcData) bool {
350	return dim.ipNet.Contains(net.IP(net.ParseIP(data.destinationAddr.String())))
351}
352
353// portMatcher matches on whether the destination port of the RPC matches the
354// destination port this matcher was instantiated with. portMatcher
355// implements the matcher interface.
356type portMatcher struct {
357	destinationPort uint32
358}
359
360func newPortMatcher(destinationPort uint32) *portMatcher {
361	return &portMatcher{destinationPort: destinationPort}
362}
363
364func (pm *portMatcher) match(data *rpcData) bool {
365	return data.destinationPort == pm.destinationPort
366}
367
368// authenticatedMatcher matches on the name of the Principal. If set, the URI
369// SAN or DNS SAN in that order is used from the certificate, otherwise the
370// subject field is used. If unset, it applies to any user that is
371// authenticated. authenticatedMatcher implements the matcher interface.
372type authenticatedMatcher struct {
373	stringMatcher *internalmatcher.StringMatcher
374}
375
376func newAuthenticatedMatcher(authenticatedMatcherConfig *v3rbacpb.Principal_Authenticated) (*authenticatedMatcher, error) {
377	// Represents this line in the RBAC documentation = "If unset, it applies to
378	// any user that is authenticated" (see package-level comments).
379	if authenticatedMatcherConfig.PrincipalName == nil {
380		return &authenticatedMatcher{}, nil
381	}
382	stringMatcher, err := internalmatcher.StringMatcherFromProto(authenticatedMatcherConfig.PrincipalName)
383	if err != nil {
384		return nil, err
385	}
386	return &authenticatedMatcher{stringMatcher: &stringMatcher}, nil
387}
388
389func (am *authenticatedMatcher) match(data *rpcData) bool {
390	// Represents this line in the RBAC documentation = "If unset, it applies to
391	// any user that is authenticated" (see package-level comments). An
392	// authenticated downstream in a stateful TLS connection will have to
393	// provide a certificate to prove their identity. Thus, you can simply check
394	// if there is a certificate present.
395	if am.stringMatcher == nil {
396		return len(data.certs) != 0
397	}
398	// No certificate present, so will never successfully match.
399	if len(data.certs) == 0 {
400		return false
401	}
402	cert := data.certs[0]
403	// The order of matching as per the RBAC documentation (see package-level comments)
404	// is as follows: URI SANs, DNS SANs, and then subject name.
405	for _, uriSAN := range cert.URIs {
406		if am.stringMatcher.Match(uriSAN.String()) {
407			return true
408		}
409	}
410	for _, dnsSAN := range cert.DNSNames {
411		if am.stringMatcher.Match(dnsSAN) {
412			return true
413		}
414	}
415	return am.stringMatcher.Match(cert.Subject.String())
416}
417