Skip to content

Commit

Permalink
integrate dexidp#1783
Browse files Browse the repository at this point in the history
  • Loading branch information
gertd committed Nov 9, 2021
1 parent 14fe699 commit 6806704
Show file tree
Hide file tree
Showing 13 changed files with 521 additions and 38 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ require (
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/gax-go/v2 v2.1.1 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/huandu/xstrings v1.3.1 // indirect
github.com/imdario/mergo v0.3.11 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ github.com/go-ldap/ldap/v3 v3.4.1/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjR
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
github.com/go-sql-driver/mysql v1.5.1-0.20200311113236-681ffa848bae/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
Expand Down Expand Up @@ -262,6 +263,8 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
Expand Down Expand Up @@ -315,6 +318,7 @@ github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa
github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
Expand Down Expand Up @@ -344,6 +348,7 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
Expand Down Expand Up @@ -512,6 +517,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -714,6 +720,7 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
138 changes: 132 additions & 6 deletions server/oauth2.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ import (
"net"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
"time"

jose "gopkg.in/square/go-jose.v2"

lru "github.com/hashicorp/golang-lru"

"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/server/internal"
"github.com/dexidp/dex/storage"
Expand Down Expand Up @@ -442,7 +445,7 @@ func (s *Server) parseAuthorizationRequest(r *http.Request) (*storage.AuthReques
return nil, newDisplayedErr(http.StatusInternalServerError, "Database error.")
}

if !validateRedirectURI(client, redirectURI) {
if !validateRedirectURI(client, s.wildcardMatcherCache, redirectURI) {
return nil, newDisplayedErr(http.StatusBadRequest, "Unregistered redirect_uri (%q).", redirectURI)
}
if redirectURI == deviceCallbackURI && client.Public {
Expand Down Expand Up @@ -605,17 +608,38 @@ func (s *Server) validateCrossClientTrust(clientID, peerID string) (trusted bool
return false, nil
}

func validateRedirectURI(client storage.Client, redirectURI string) bool {
func validateRedirectURI(client storage.Client, wildcardCache *lru.ARCCache, redirectURI string) bool {
// Allow named RedirectURIs for both public and non-public clients.
// This is required make PKCE-enabled web apps work, when configured as public clients.
for _, uri := range client.RedirectURIs {
if redirectURI == uri {
return true
}
}
// For non-public clients or when RedirectURIs is set, we allow only explicitly named RedirectURIs.
// Otherwise, we check below for special URIs used for desktop or mobile apps.
if !client.Public || len(client.RedirectURIs) > 0 {

// // For non-public clients or when RedirectURIs is set, we allow only explicitly named RedirectURIs.
// // Otherwise, we check below for special URIs used for desktop or mobile apps.
// if !client.Public || len(client.RedirectURIs) > 0 {
// return false
// }

if !client.Public {
for _, uri := range client.RedirectURIs {
if redirectURI == uri {
// exact match is valid
return true
}

if urlRequested, err := url.Parse(redirectURI); err == nil && urlRequested != nil {
// validly-formed url
clobberMatcher := getClobberHostMatcher(uri, wildcardCache)
validClobber := clobberMatcher.HostMatcher != nil && clobberMatcher.ClientRedirectURL != nil
if validClobber && matchesClobber(clobberMatcher, urlRequested) {
// clobber match success
return true
}
}
}
return false
}

Expand All @@ -628,7 +652,7 @@ func validateRedirectURI(client storage.Client, redirectURI string) bool {
if err != nil {
return false
}
if u.Scheme != "http" {
if u.Scheme != "http" && u.Scheme != "https" {
return false
}
if u.Host == "localhost" {
Expand All @@ -638,6 +662,108 @@ func validateRedirectURI(client storage.Client, redirectURI string) bool {
return err == nil && host == "localhost"
}

func matchesClobber(clobberMatcher ClientRedirectClobberMatcher, urlRequested *url.URL) bool {
if clobberMatcher.ClientRedirectURL.Path != urlRequested.Path {
// failed path match
return false
}
if clobberMatcher.ClientRedirectURL.Scheme != urlRequested.Scheme {
// failed scheme match
return false
}
if !clobberMatcher.HostMatcher.MatchString(urlRequested.Host) {
// failed host match
return false
}
return true
}

type HostSplit struct {
HostSuffix string // e.g. host:port of two closest root domains e.g. example.com:8888
SubDomainPrefix string // e.g. dom.abc (from dom.abc.example.com)
}

type ClientRedirectClobberMatcher struct {
ClientRedirectURL *url.URL
HostMatcher *regexp.Regexp
}

// Returns a clobber/wildcard subdomain matcher struct
func createClientRedirectClobberMatcher(clientRedirectURI string) ClientRedirectClobberMatcher {
redirectURL, _ := url.Parse(clientRedirectURI)
if redirectURL == nil {
return ClientRedirectClobberMatcher{}
}
splitResult := splitClobberHTTPRedirectURL(redirectURL)
if splitResult == nil {
// not wildcard or invalid
return ClientRedirectClobberMatcher{}
}

// first replace double-asterisk globber with .+
var subdomain = strings.ReplaceAll(regexp.QuoteMeta(splitResult.SubDomainPrefix), "\\*\\*", ".+")
subdomain = strings.ReplaceAll(subdomain, "\\*", "[^.]+")

hostNameRegExStr := "^" + subdomain + "\\." + regexp.QuoteMeta(splitResult.HostSuffix) + "$"

hostNameRegEx, err := regexp.Compile(hostNameRegExStr)
if err != nil {
// bad pattern - unexpected
return ClientRedirectClobberMatcher{}
}

return ClientRedirectClobberMatcher{
ClientRedirectURL: redirectURL,
HostMatcher: hostNameRegEx,
}
}

// extract the scheme, host and path for wildcard checks.
// If the scheme is not http or https, or does not specify an absolute path beginning with '/'
// the parser will return nil.
func splitClobberHTTPRedirectURL(clientRedirectURLSpec *url.URL) *HostSplit {
host := clientRedirectURLSpec.Host
if !strings.Contains(host, "*") {
// for efficiency, check wildcard before DomainComponents
return nil
}

if strings.Contains(host, "***") {
// a maximum of 2 '*' characters are permitted in sequence
return nil
}

// sub-domain portion is first portion (greedy)
hostSplit := regexp.MustCompile(`^(.+)\.([^.]*[A-Za-z][^.]*\.[^.]*[A-Za-z][^.]+)$`)
hostRes := hostSplit.FindStringSubmatch(host)
if hostRes == nil {
// does not conform to requirements of a subdomain spec followed by two higher order domains
return nil
}

return &HostSplit{
HostSuffix: hostRes[2],
SubDomainPrefix: hostRes[1],
}
}

func getClobberHostMatcher(clientRedirectURI string, wildcardCache *lru.ARCCache) ClientRedirectClobberMatcher {
if wildcardCache == nil {
// unexpected error condition where cache is unavailable
return ClientRedirectClobberMatcher{}
}

if redirectMatcherStruct, ok := wildcardCache.Get(clientRedirectURI); ok {
// cache hit
return redirectMatcherStruct.(ClientRedirectClobberMatcher)
}
// cache miss - calc and add to cache
calcResult := createClientRedirectClobberMatcher(clientRedirectURI)
wildcardCache.Add(clientRedirectURI, calcResult)
// return cached result
return calcResult
}

func validateConnectorID(connectors []storage.Connector, connectorID string) bool {
for _, c := range connectors {
if c.ID == connectorID {
Expand Down
Loading

0 comments on commit 6806704

Please sign in to comment.