diff --git a/go.mod b/go.mod index 50e8ab744..1e376b6ac 100644 --- a/go.mod +++ b/go.mod @@ -161,7 +161,7 @@ require ( github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/crewjam/httperr v0.2.0 // indirect - github.com/crewjam/saml v0.4.13 // indirect + github.com/crewjam/saml v0.4.14 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set v1.8.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect diff --git a/go.sum b/go.sum index f93b6652a..1de9e7efa 100644 --- a/go.sum +++ b/go.sum @@ -1011,15 +1011,14 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo= github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4= -github.com/crewjam/saml v0.4.13 h1:TYHggH/hwP7eArqiXSJUvtOPNzQDyQ7vwmwEqlFWhMc= -github.com/crewjam/saml v0.4.13/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA= +github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c= +github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME= github.com/cs3org/reva/v2 v2.16.1-0.20231012102459-2b27cd47ab72 h1:53M+ldLYQSxl/iJokKfOUmY0ntMhnATQu9cBZE1X53k= github.com/cs3org/reva/v2 v2.16.1-0.20231012102459-2b27cd47ab72/go.mod h1:6M5k4UvGUgZh31t4r70RwbesW+w2EM/gd/gpuQZxAPg= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dchest/uniuri v1.2.0/go.mod h1:fSzm4SLHzNZvWLvWJew423PhAzkpNQYq+uNLq4kxhkY= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= github.com/deepmap/oapi-codegen v1.3.11/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0= @@ -1264,7 +1263,6 @@ github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptG github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= @@ -1892,7 +1890,6 @@ github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= -github.com/russellhaering/goxmldsig v1.2.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= github.com/russellhaering/goxmldsig v1.4.0 h1:8UcDh/xGyQiyrW+Fq5t8f+l2DLB1+zlhYzkPUJ7Qhys= github.com/russellhaering/goxmldsig v1.4.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -2038,7 +2035,6 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= -github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go-micro.dev/v4 v4.9.0 h1:pd1CpqMT9hA47jSmX8mfdGK865PkMh95Rwj5RdfqPqE= go-micro.dev/v4 v4.9.0/go.mod h1:Ju8HrZ5hQSF+QguZ2QUs9Kbe42MHP1tJa/fpP5g07Cs= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -2131,7 +2127,6 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= @@ -2973,7 +2968,6 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/github.com/crewjam/saml/.golangci.yml b/vendor/github.com/crewjam/saml/.golangci.yml index 4fbc0405d..23f37cbf5 100644 --- a/vendor/github.com/crewjam/saml/.golangci.yml +++ b/vendor/github.com/crewjam/saml/.golangci.yml @@ -7,41 +7,35 @@ linters: enable: + - bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false] + - errcheck # Inspects source code for security problems [fast: true, auto-fix: false] + - gocritic # The most opinionated Go source code linter [fast: true, auto-fix: false] + - gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false] - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true] - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports [fast: true, auto-fix: true] - gosec # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: true, auto-fix: false] - - misspell # Finds commonly misspelled English words in comments [fast: true, auto-fix: true] - - deadcode # Finds unused code [fast: true, auto-fix: false] - - revive # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes [fast: true, auto-fix: false] - - unconvert # Remove unnecessary type conversions [fast: true, auto-fix: false] - - disable: - # TODO(ross): fix errors reported by these checkers and enable them - - bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false] - - depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false] - - dupl # Tool for code clone detection [fast: true, auto-fix: false] - - errcheck # Inspects source code for security problems [fast: true, auto-fix: false] - - gochecknoglobals # Checks that no globals are present in Go code [fast: true, auto-fix: false] - - gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false] - - goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false] - - gocritic # The most opinionated Go source code linter [fast: true, auto-fix: false] - - gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false] - gosimple # Linter for Go source code that specializes in simplifying a code [fast: false, auto-fix: false] - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string [fast: false, auto-fix: false] - ineffassign # Detects when assignments to existing variables are not used [fast: true, auto-fix: false] - - interfacer # Linter that suggests narrower interface types [fast: false, auto-fix: false] - - lll # Reports long lines [fast: true, auto-fix: false] - - maligned # Tool to detect Go structs that would take less memory if their fields were sorted [fast: true, auto-fix: false] + - misspell # Finds commonly misspelled English words in comments [fast: true, auto-fix: true] - nakedret # Finds naked returns in functions greater than a specified function length [fast: true, auto-fix: false] - prealloc # Finds slice declarations that could potentially be preallocated [fast: true, auto-fix: false] - - scopelint # Scopelint checks for unpinned variables in go programs [fast: true, auto-fix: false] + - revive # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes [fast: true, auto-fix: false] - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks [fast: false, auto-fix: false] - - structcheck # Finds unused struct fields [fast: true, auto-fix: false] - stylecheck # Stylecheck is a replacement for golint [fast: false, auto-fix: false] - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code [fast: true, auto-fix: false] + - unconvert # Remove unnecessary type conversions [fast: true, auto-fix: false] - unparam # Reports unused function parameters [fast: false, auto-fix: false] - unused # Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false] - - varcheck # Finds unused global variables and constants [fast: true, auto-fix: false] + + disable: + # TODO(ross): fix errors reported by these checkers and enable them + - dupl # Tool for code clone detection [fast: true, auto-fix: false] + - gochecknoglobals # Checks that no globals are present in Go code [fast: true, auto-fix: false] + - gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false] + - goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false] + - lll # Reports long lines [fast: true, auto-fix: false] + - depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false] linters-settings: goimports: local-prefixes: github.com/crewjam/saml diff --git a/vendor/github.com/crewjam/saml/identity_provider.go b/vendor/github.com/crewjam/saml/identity_provider.go index bcea5828f..abaaad68e 100644 --- a/vendor/github.com/crewjam/saml/identity_provider.go +++ b/vendor/github.com/crewjam/saml/identity_provider.go @@ -9,7 +9,6 @@ import ( "encoding/xml" "fmt" "io" - "io/ioutil" "net/http" "net/url" "os" @@ -96,6 +95,7 @@ type AssertionMaker interface { // and password). type IdentityProvider struct { Key crypto.PrivateKey + Signer crypto.Signer Logger logger.Interface Certificate *x509.Certificate Intermediates []*x509.Certificate @@ -196,10 +196,13 @@ func (idp *IdentityProvider) Handler() http.Handler { } // ServeMetadata is an http.HandlerFunc that serves the IDP metadata -func (idp *IdentityProvider) ServeMetadata(w http.ResponseWriter, r *http.Request) { +func (idp *IdentityProvider) ServeMetadata(w http.ResponseWriter, _ *http.Request) { buf, _ := xml.MarshalIndent(idp.Metadata(), "", " ") w.Header().Set("Content-Type", "application/samlmetadata+xml") - w.Write(buf) + if _, err := w.Write(buf); err != nil { + idp.Logger.Printf("ERROR: %s", err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + } } // ServeSSO handles SAML auth requests. @@ -362,7 +365,7 @@ func NewIdpAuthnRequest(idp *IdentityProvider, r *http.Request) (*IdpAuthnReques if err != nil { return nil, fmt.Errorf("cannot decode request: %s", err) } - req.RequestBuffer, err = ioutil.ReadAll(newSaferFlateReader(bytes.NewReader(compressedRequest))) + req.RequestBuffer, err = io.ReadAll(newSaferFlateReader(bytes.NewReader(compressedRequest))) if err != nil { return nil, fmt.Errorf("cannot decompress request: %s", err) } @@ -716,9 +719,7 @@ func (DefaultAssertionMaker) MakeAssertion(req *IdpAuthnRequest, session *Sessio }) } - for _, ca := range session.CustomAttributes { - attributes = append(attributes, ca) - } + attributes = append(attributes, session.CustomAttributes...) if len(session.Groups) != 0 { groupMemberAttributeValues := []AttributeValue{} @@ -830,24 +831,8 @@ const canonicalizerPrefixList = "" // MakeAssertionEl sets `AssertionEl` to a signed, possibly encrypted, version of `Assertion`. func (req *IdpAuthnRequest) MakeAssertionEl() error { - keyPair := tls.Certificate{ - Certificate: [][]byte{req.IDP.Certificate.Raw}, - PrivateKey: req.IDP.Key, - Leaf: req.IDP.Certificate, - } - for _, cert := range req.IDP.Intermediates { - keyPair.Certificate = append(keyPair.Certificate, cert.Raw) - } - keyStore := dsig.TLSCertKeyStore(keyPair) - - signatureMethod := req.IDP.SignatureMethod - if signatureMethod == "" { - signatureMethod = dsig.RSASHA1SignatureMethod - } - - signingContext := dsig.NewDefaultSigningContext(keyStore) - signingContext.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList(canonicalizerPrefixList) - if err := signingContext.SetSignatureMethod(signatureMethod); err != nil { + signingContext, err := req.signingContext() + if err != nil { return err } @@ -1048,24 +1033,8 @@ func (req *IdpAuthnRequest) MakeResponse() error { // Sign the response element (we've already signed the Assertion element) { - keyPair := tls.Certificate{ - Certificate: [][]byte{req.IDP.Certificate.Raw}, - PrivateKey: req.IDP.Key, - Leaf: req.IDP.Certificate, - } - for _, cert := range req.IDP.Intermediates { - keyPair.Certificate = append(keyPair.Certificate, cert.Raw) - } - keyStore := dsig.TLSCertKeyStore(keyPair) - - signatureMethod := req.IDP.SignatureMethod - if signatureMethod == "" { - signatureMethod = dsig.RSASHA1SignatureMethod - } - - signingContext := dsig.NewDefaultSigningContext(keyStore) - signingContext.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList(canonicalizerPrefixList) - if err := signingContext.SetSignatureMethod(signatureMethod); err != nil { + signingContext, err := req.signingContext() + if err != nil { return err } @@ -1083,3 +1052,44 @@ func (req *IdpAuthnRequest) MakeResponse() error { req.ResponseEl = responseEl return nil } + +// signingContext will create a signing context for the request. +func (req *IdpAuthnRequest) signingContext() (*dsig.SigningContext, error) { + // Create a cert chain based off of the IDP cert and its intermediates. + certificates := [][]byte{req.IDP.Certificate.Raw} + for _, cert := range req.IDP.Intermediates { + certificates = append(certificates, cert.Raw) + } + + var signingContext *dsig.SigningContext + var err error + // If signer is set, use it instead of the private key. + if req.IDP.Signer != nil { + signingContext, err = dsig.NewSigningContext(req.IDP.Signer, certificates) + if err != nil { + return nil, err + } + } else { + keyPair := tls.Certificate{ + Certificate: certificates, + PrivateKey: req.IDP.Key, + Leaf: req.IDP.Certificate, + } + keyStore := dsig.TLSCertKeyStore(keyPair) + + signingContext = dsig.NewDefaultSigningContext(keyStore) + } + + // Default to using SHA1 if the signature method isn't set. + signatureMethod := req.IDP.SignatureMethod + if signatureMethod == "" { + signatureMethod = dsig.RSASHA1SignatureMethod + } + + signingContext.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList(canonicalizerPrefixList) + if err := signingContext.SetSignatureMethod(signatureMethod); err != nil { + return nil, err + } + + return signingContext, nil +} diff --git a/vendor/github.com/crewjam/saml/logger/logger.go b/vendor/github.com/crewjam/saml/logger/logger.go index c211aba60..03bb0bdef 100644 --- a/vendor/github.com/crewjam/saml/logger/logger.go +++ b/vendor/github.com/crewjam/saml/logger/logger.go @@ -1,3 +1,4 @@ +// Package logger provides a logging interface. package logger import ( diff --git a/vendor/github.com/crewjam/saml/metadata.go b/vendor/github.com/crewjam/saml/metadata.go index f7486d380..006a9e67a 100644 --- a/vendor/github.com/crewjam/saml/metadata.go +++ b/vendor/github.com/crewjam/saml/metadata.go @@ -2,6 +2,8 @@ package saml import ( "encoding/xml" + "fmt" + "net/url" "time" "github.com/beevik/etree" @@ -19,6 +21,9 @@ const HTTPArtifactBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" // SOAPBinding is the official URN for the SOAP binding (transport) const SOAPBinding = "urn:oasis:names:tc:SAML:2.0:bindings:SOAP" +// SOAPBindingV1 is the URN for the SOAP binding in SAML 1.0 +const SOAPBindingV1 = "urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding" + // EntitiesDescriptor represents the SAML object of the same name. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.1 @@ -65,7 +70,7 @@ type EntityDescriptor struct { } // MarshalXML implements xml.Marshaler -func (m EntityDescriptor) MarshalXML(e *xml.Encoder, start xml.StartElement) error { +func (m EntityDescriptor) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { type Alias EntityDescriptor aux := &struct { ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"` @@ -188,6 +193,76 @@ type Endpoint struct { ResponseLocation string `xml:"ResponseLocation,attr,omitempty"` } +func checkEndpointLocation(binding string, location string) (string, error) { + // Within the SAML standard, the complex type EndpointType describes a + // SAML protocol binding endpoint at which a SAML entity can be sent + // protocol messages. In particular, the location of an endpoint type is + // defined as follows in the Metadata for the OASIS Security Assertion + // Markup Language (SAML) V2.0 - 2.2.2 Complex Type EndpointType: + // + // Location [Required] A required URI attribute that specifies the + // location of the endpoint. The allowable syntax of this URI depends + // on the protocol binding. + switch binding { + case HTTPPostBinding, + HTTPRedirectBinding, + HTTPArtifactBinding, + SOAPBinding, + SOAPBindingV1: + locationURL, err := url.Parse(location) + if err != nil { + return "", fmt.Errorf("invalid url %q: %w", location, err) + } + switch locationURL.Scheme { + case "http", "https": + // ok + default: + return "", fmt.Errorf("invalid url scheme %q for binding %q", + locationURL.Scheme, binding) + } + default: + // We don't know what form location should take, but the protocol + // requires that we validate its syntax. + // + // In practice, lots of metadata contains random bindings, for example + // "urn:mace:shibboleth:1.0:profiles:AuthnRequest" from our own test suite. + // + // We can't fail, but we also can't allow a location parameter whose syntax we + // cannot verify. The least-bad course of action here is to set location to + // and empty string, and hope the caller doesn't care need it. + location = "" + } + + return location, nil +} + +// UnmarshalXML implements xml.Unmarshaler +func (m *Endpoint) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + type Alias Endpoint + aux := &struct { + *Alias + }{ + Alias: (*Alias)(m), + } + if err := d.DecodeElement(aux, &start); err != nil { + return err + } + + var err error + m.Location, err = checkEndpointLocation(m.Binding, m.Location) + if err != nil { + return err + } + if m.ResponseLocation != "" { + m.ResponseLocation, err = checkEndpointLocation(m.Binding, m.ResponseLocation) + if err != nil { + return err + } + } + + return nil +} + // IndexedEndpoint represents the SAML IndexedEndpointType object. // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.2.3 @@ -199,6 +274,38 @@ type IndexedEndpoint struct { IsDefault *bool `xml:"isDefault,attr"` } +// UnmarshalXML implements xml.Unmarshaler +func (m *IndexedEndpoint) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + type Alias IndexedEndpoint + aux := &struct { + *Alias + }{ + Alias: (*Alias)(m), + } + if err := d.DecodeElement(aux, &start); err != nil { + return err + } + + var err error + m.Location, err = checkEndpointLocation(m.Binding, m.Location) + if err != nil { + return err + } + if m.ResponseLocation != nil { + responseLocation, err := checkEndpointLocation(m.Binding, *m.ResponseLocation) + if err != nil { + return err + } + if responseLocation != "" { + m.ResponseLocation = &responseLocation + } else { + m.ResponseLocation = nil + } + } + + return nil +} + // SSODescriptor represents the SAML complex type SSODescriptor // // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.2 diff --git a/vendor/github.com/crewjam/saml/samlsp/error.go b/vendor/github.com/crewjam/saml/samlsp/error.go index 662bce749..496faccf6 100644 --- a/vendor/github.com/crewjam/saml/samlsp/error.go +++ b/vendor/github.com/crewjam/saml/samlsp/error.go @@ -14,7 +14,7 @@ type ErrorFunction func(w http.ResponseWriter, r *http.Request, err error) // DefaultOnError is the default ErrorFunction implementation. It prints // an message via the standard log package and returns a simple text // "Forbidden" message to the user. -func DefaultOnError(w http.ResponseWriter, r *http.Request, err error) { +func DefaultOnError(w http.ResponseWriter, _ *http.Request, err error) { if parseErr, ok := err.(*saml.InvalidResponseError); ok { log.Printf("WARNING: received invalid saml response: %s (now: %s) %s", parseErr.Response, parseErr.Now, parseErr.PrivateErr) diff --git a/vendor/github.com/crewjam/saml/samlsp/fetch_metadata.go b/vendor/github.com/crewjam/saml/samlsp/fetch_metadata.go index 1ef521ac1..ede3c6b3e 100644 --- a/vendor/github.com/crewjam/saml/samlsp/fetch_metadata.go +++ b/vendor/github.com/crewjam/saml/samlsp/fetch_metadata.go @@ -5,13 +5,15 @@ import ( "context" "encoding/xml" "errors" - "io/ioutil" + "io" "net/http" "net/url" "github.com/crewjam/httperr" xrv "github.com/mattermost/xml-roundtrip-validator" + "github.com/crewjam/saml/logger" + "github.com/crewjam/saml" ) @@ -61,12 +63,16 @@ func FetchMetadata(ctx context.Context, httpClient *http.Client, metadataURL url if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { + if err := resp.Body.Close(); err != nil { + logger.DefaultLogger.Printf("Error while closing response body during fetch metadata: %v", err) + } + }() if resp.StatusCode >= 400 { return nil, httperr.Response(*resp) } - data, err := ioutil.ReadAll(resp.Body) + data, err := io.ReadAll(resp.Body) if err != nil { return nil, err } diff --git a/vendor/github.com/crewjam/saml/samlsp/middleware.go b/vendor/github.com/crewjam/saml/samlsp/middleware.go index 834a79c12..f5eabb16b 100644 --- a/vendor/github.com/crewjam/saml/samlsp/middleware.go +++ b/vendor/github.com/crewjam/saml/samlsp/middleware.go @@ -1,6 +1,7 @@ package samlsp import ( + "bytes" "encoding/xml" "net/http" @@ -65,16 +66,22 @@ func (m *Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // ServeMetadata handles requests for the SAML metadata endpoint. -func (m *Middleware) ServeMetadata(w http.ResponseWriter, r *http.Request) { +func (m *Middleware) ServeMetadata(w http.ResponseWriter, _ *http.Request) { buf, _ := xml.MarshalIndent(m.ServiceProvider.Metadata(), "", " ") w.Header().Set("Content-Type", "application/samlmetadata+xml") - w.Write(buf) - return + if _, err := w.Write(buf); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } // ServeACS handles requests for the SAML ACS endpoint. func (m *Middleware) ServeACS(w http.ResponseWriter, r *http.Request) { - r.ParseForm() + err := r.ParseForm() + if err != nil { + m.OnError(w, r, err) + return + } possibleRequestIDs := []string{} if m.ServiceProvider.AllowIDPInitiated { @@ -93,7 +100,6 @@ func (m *Middleware) ServeACS(w http.ResponseWriter, r *http.Request) { } m.CreateSessionFromAssertion(w, r, assertion, m.ServiceProvider.DefaultRedirectURI) - return } // RequireAccount is HTTP middleware that requires that each request be @@ -114,7 +120,6 @@ func (m *Middleware) RequireAccount(handler http.Handler) http.Handler { } m.OnError(w, r, err) - return }) } @@ -173,9 +178,14 @@ func (m *Middleware) HandleStartAuthFlow(w http.ResponseWriter, r *http.Request) "script-src 'sha256-AjPdJSbZmeWHnEc5ykvJFay8FTWeTeRbs9dutfZ0HqE='; "+ "reflected-xss block; referrer no-referrer;") w.Header().Add("Content-type", "text/html") - w.Write([]byte(``)) - w.Write(authReq.Post(relayState)) - w.Write([]byte(``)) + var buf bytes.Buffer + buf.WriteString(``) + buf.Write(authReq.Post(relayState)) + buf.WriteString(``) + if _, err := w.Write(buf.Bytes()); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } return } panic("not reached") @@ -195,7 +205,10 @@ func (m *Middleware) CreateSessionFromAssertion(w http.ResponseWriter, r *http.R return } } else { - m.RequestTracker.StopTrackingRequest(w, r, trackedRequestIndex) + if err := m.RequestTracker.StopTrackingRequest(w, r, trackedRequestIndex); err != nil { + m.OnError(w, r, err) + return + } redirectURI = trackedRequest.URI } diff --git a/vendor/github.com/crewjam/saml/samlsp/request_tracker_jwt.go b/vendor/github.com/crewjam/saml/samlsp/request_tracker_jwt.go index 625a66e97..0ca472586 100644 --- a/vendor/github.com/crewjam/saml/samlsp/request_tracker_jwt.go +++ b/vendor/github.com/crewjam/saml/samlsp/request_tracker_jwt.go @@ -25,7 +25,7 @@ var _ TrackedRequestCodec = JWTTrackedRequestCodec{} // JWTTrackedRequestClaims represents the JWT claims for a tracked request. type JWTTrackedRequestClaims struct { - jwt.StandardClaims + jwt.RegisteredClaims TrackedRequest SAMLAuthnRequest bool `json:"saml-authn-request"` } @@ -34,12 +34,12 @@ type JWTTrackedRequestClaims struct { func (s JWTTrackedRequestCodec) Encode(value TrackedRequest) (string, error) { now := saml.TimeNow() claims := JWTTrackedRequestClaims{ - StandardClaims: jwt.StandardClaims{ - Audience: s.Audience, - ExpiresAt: now.Add(s.MaxAge).Unix(), - IssuedAt: now.Unix(), + RegisteredClaims: jwt.RegisteredClaims{ + Audience: jwt.ClaimStrings{s.Audience}, + ExpiresAt: jwt.NewNumericDate(now.Add(s.MaxAge)), + IssuedAt: jwt.NewNumericDate(now), Issuer: s.Issuer, - NotBefore: now.Unix(), // TODO(ross): correct for clock skew + NotBefore: jwt.NewNumericDate(now), // TODO(ross): correct for clock skew Subject: value.Index, }, TrackedRequest: value, @@ -67,7 +67,7 @@ func (s JWTTrackedRequestCodec) Decode(signed string) (*TrackedRequest, error) { if !claims.VerifyIssuer(s.Issuer, true) { return nil, fmt.Errorf("expected issuer %q, got %q", s.Issuer, claims.Issuer) } - if claims.SAMLAuthnRequest != true { + if !claims.SAMLAuthnRequest { return nil, fmt.Errorf("expected saml-authn-request") } claims.TrackedRequest.Index = claims.Subject diff --git a/vendor/github.com/crewjam/saml/samlsp/session_jwt.go b/vendor/github.com/crewjam/saml/samlsp/session_jwt.go index c4531fb90..8d801e47f 100644 --- a/vendor/github.com/crewjam/saml/samlsp/session_jwt.go +++ b/vendor/github.com/crewjam/saml/samlsp/session_jwt.go @@ -106,7 +106,7 @@ func (c JWTSessionCodec) Decode(signed string) (Session, error) { if !claims.VerifyIssuer(c.Issuer, true) { return nil, fmt.Errorf("expected issuer %q, got %q", c.Issuer, claims.Issuer) } - if claims.SAMLSession != true { + if !claims.SAMLSession { return nil, errors.New("expected saml-session") } return claims, nil diff --git a/vendor/github.com/crewjam/saml/schema.go b/vendor/github.com/crewjam/saml/schema.go index 13ea2ef60..23cddbcab 100644 --- a/vendor/github.com/crewjam/saml/schema.go +++ b/vendor/github.com/crewjam/saml/schema.go @@ -48,7 +48,7 @@ type AuthnRequest struct { NameIDPolicy *NameIDPolicy `xml:"urn:oasis:names:tc:SAML:2.0:protocol NameIDPolicy"` Conditions *Conditions RequestedAuthnContext *RequestedAuthnContext - //Scoping *Scoping // TODO + // Scoping *Scoping // TODO ForceAuthn *bool `xml:",attr"` IsPassive *bool `xml:",attr"` @@ -108,7 +108,7 @@ func (r *LogoutRequest) Element() *etree.Element { } // MarshalXML implements xml.Marshaler -func (r *LogoutRequest) MarshalXML(e *xml.Encoder, start xml.StartElement) error { +func (r *LogoutRequest) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { type Alias LogoutRequest aux := &struct { IssueInstant RelaxedTime `xml:",attr"` @@ -209,9 +209,9 @@ func (r *AuthnRequest) Element() *etree.Element { if r.RequestedAuthnContext != nil { el.AddChild(r.RequestedAuthnContext.Element()) } - //if r.Scoping != nil { - // el.AddChild(r.Scoping.Element()) - //} + // if r.Scoping != nil { + // el.AddChild(r.Scoping.Element()) + // } if r.ForceAuthn != nil { el.CreateAttr("ForceAuthn", strconv.FormatBool(*r.ForceAuthn)) } @@ -237,7 +237,7 @@ func (r *AuthnRequest) Element() *etree.Element { } // MarshalXML implements xml.Marshaler -func (r *AuthnRequest) MarshalXML(e *xml.Encoder, start xml.StartElement) error { +func (r *AuthnRequest) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { type Alias AuthnRequest aux := &struct { IssueInstant RelaxedTime `xml:",attr"` @@ -374,7 +374,7 @@ func (r *ArtifactResolve) SoapRequest() *etree.Element { } // MarshalXML implements xml.Marshaler -func (r *ArtifactResolve) MarshalXML(e *xml.Encoder, start xml.StartElement) error { +func (r *ArtifactResolve) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { type Alias ArtifactResolve aux := &struct { IssueInstant RelaxedTime `xml:",attr"` @@ -448,7 +448,7 @@ func (r *ArtifactResponse) Element() *etree.Element { } // MarshalXML implements xml.Marshaler -func (r *ArtifactResponse) MarshalXML(e *xml.Encoder, start xml.StartElement) error { +func (r *ArtifactResponse) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { type Alias ArtifactResponse aux := &struct { IssueInstant RelaxedTime `xml:",attr"` @@ -542,7 +542,7 @@ func (r *Response) Element() *etree.Element { } // MarshalXML implements xml.Marshaler -func (r *Response) MarshalXML(e *xml.Encoder, start xml.StartElement) error { +func (r *Response) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { type Alias Response aux := &struct { IssueInstant RelaxedTime `xml:",attr"` @@ -667,7 +667,7 @@ const ( StatusRequestUnsupported = "urn:oasis:names:tc:SAML:2.0:status:RequestUnsupported" // StatusRequestVersionDeprecated means the SAML responder cannot process any requests with the protocol version specified in the request. - StatusRequestVersionDeprecated = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionDeprecated" + StatusRequestVersionDeprecated = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionDeprecated" //nolint:gosec // StatusRequestVersionTooHigh means the SAML responder cannot process the request because the protocol version specified in the request message is a major upgrade from the highest protocol version supported by the responder. StatusRequestVersionTooHigh = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooHigh" @@ -1153,9 +1153,9 @@ func (a *SubjectLocality) Element() *etree.Element { // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.7.2.2 type AuthnContext struct { AuthnContextClassRef *AuthnContextClassRef - //AuthnContextDecl *AuthnContextDecl ... TODO - //AuthnContextDeclRef *AuthnContextDeclRef ... TODO - //AuthenticatingAuthorities []AuthenticatingAuthority... TODO + // AuthnContextDecl *AuthnContextDecl ... TODO + // AuthnContextDeclRef *AuthnContextDeclRef ... TODO + // AuthenticatingAuthorities []AuthenticatingAuthority... TODO } // Element returns an etree.Element representing the object in XML form. @@ -1292,7 +1292,7 @@ func (r *LogoutResponse) Element() *etree.Element { } // MarshalXML implements xml.Marshaler -func (r *LogoutResponse) MarshalXML(e *xml.Encoder, start xml.StartElement) error { +func (r *LogoutResponse) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { type Alias LogoutResponse aux := &struct { IssueInstant RelaxedTime `xml:",attr"` diff --git a/vendor/github.com/crewjam/saml/service_provider.go b/vendor/github.com/crewjam/saml/service_provider.go index 6f6e7f4fc..30b35670d 100644 --- a/vendor/github.com/crewjam/saml/service_provider.go +++ b/vendor/github.com/crewjam/saml/service_provider.go @@ -12,7 +12,7 @@ import ( "errors" "fmt" "html/template" - "io/ioutil" + "io" "net/http" "net/url" "regexp" @@ -23,6 +23,7 @@ import ( dsig "github.com/russellhaering/goxmldsig" "github.com/russellhaering/goxmldsig/etreeutils" + "github.com/crewjam/saml/logger" "github.com/crewjam/saml/xmlenc" ) @@ -189,13 +190,13 @@ func (sp *ServiceProvider) Metadata() *EntityDescriptor { } } - var sloEndpoints []Endpoint - for _, binding := range sp.LogoutBindings { - sloEndpoints = append(sloEndpoints, Endpoint{ + sloEndpoints := make([]Endpoint, len(sp.LogoutBindings)) + for i, binding := range sp.LogoutBindings { + sloEndpoints[i] = Endpoint{ Binding: binding, Location: sp.SloURL.String(), ResponseLocation: sp.SloURL.String(), - }) + } } return &EntityDescriptor{ @@ -245,25 +246,29 @@ func (sp *ServiceProvider) MakeRedirectAuthenticationRequest(relayState string) } // Redirect returns a URL suitable for using the redirect binding with the request -func (req *AuthnRequest) Redirect(relayState string, sp *ServiceProvider) (*url.URL, error) { +func (r *AuthnRequest) Redirect(relayState string, sp *ServiceProvider) (*url.URL, error) { w := &bytes.Buffer{} w1 := base64.NewEncoder(base64.StdEncoding, w) w2, _ := flate.NewWriter(w1, 9) doc := etree.NewDocument() - doc.SetRoot(req.Element()) + doc.SetRoot(r.Element()) if _, err := doc.WriteTo(w2); err != nil { panic(err) } - w2.Close() - w1.Close() + if err := w2.Close(); err != nil { + panic(err) + } + if err := w1.Close(); err != nil { + panic(err) + } - rv, _ := url.Parse(req.Destination) + rv, _ := url.Parse(r.Destination) // We can't depend on Query().set() as order matters for signing query := rv.RawQuery if len(query) > 0 { - query += "&SAMLRequest=" + url.QueryEscape(string(w.Bytes())) + query += "&SAMLRequest=" + url.QueryEscape(w.String()) } else { - query += "SAMLRequest=" + url.QueryEscape(string(w.Bytes())) + query += "SAMLRequest=" + url.QueryEscape(w.String()) } if relayState != "" { @@ -352,11 +357,11 @@ func (sp *ServiceProvider) getIDPSigningCerts() ([]*x509.Certificate, error) { return nil, errors.New("cannot find any signing certificate in the IDP SSO descriptor") } - var certs []*x509.Certificate + certs := make([]*x509.Certificate, len(certStrs)) // cleanup whitespace regex := regexp.MustCompile(`\s+`) - for _, certStr := range certStrs { + for i, certStr := range certStrs { certStr = regex.ReplaceAllString(certStr, "") certBytes, err := base64.StdEncoding.DecodeString(certStr) if err != nil { @@ -367,7 +372,7 @@ func (sp *ServiceProvider) getIDPSigningCerts() ([]*x509.Certificate, error) { if err != nil { return nil, err } - certs = append(certs, parsedCert) + certs[i] = parsedCert } return certs, nil @@ -439,9 +444,9 @@ func GetSigningContext(sp *ServiceProvider) (*dsig.SigningContext, error) { Leaf: sp.Certificate, } // TODO: add intermediates for SP - //for _, cert := range sp.Intermediates { - // keyPair.Certificate = append(keyPair.Certificate, cert.Raw) - //} + // for _, cert := range sp.Intermediates { + // keyPair.Certificate = append(keyPair.Certificate, cert.Raw) + // } keyStore := dsig.TLSCertKeyStore(keyPair) if sp.SignatureMethod != dsig.RSASHA1SignatureMethod && @@ -508,9 +513,9 @@ func (sp *ServiceProvider) MakePostAuthenticationRequest(relayState string) ([]b } // Post returns an HTML form suitable for using the HTTP-POST binding with the request -func (req *AuthnRequest) Post(relayState string) []byte { +func (r *AuthnRequest) Post(relayState string) []byte { doc := etree.NewDocument() - doc.SetRoot(req.Element()) + doc.SetRoot(r.Element()) reqBuf, err := doc.WriteToBytes() if err != nil { panic(err) @@ -530,7 +535,7 @@ func (req *AuthnRequest) Post(relayState string) []byte { SAMLRequest string RelayState string }{ - URL: req.Destination, + URL: r.Destination, SAMLRequest: encodedReqBuf, RelayState: relayState, } @@ -579,7 +584,7 @@ type InvalidResponseError struct { } func (ivr *InvalidResponseError) Error() string { - return fmt.Sprintf("Authentication failed") + return "Authentication failed" } // ErrBadStatus is returned when the assertion provided is valid but the @@ -606,7 +611,7 @@ func (sp *ServiceProvider) handleArtifactRequest(ctx context.Context, artifactID artifactResolveRequest, err := sp.MakeArtifactResolveRequest(artifactID) if err != nil { - retErr.PrivateErr = fmt.Errorf("Cannot generate artifact resolution request: %s", err) + retErr.PrivateErr = fmt.Errorf("cannot generate artifact resolution request: %s", err) return nil, retErr } @@ -633,12 +638,16 @@ func (sp *ServiceProvider) handleArtifactRequest(ctx context.Context, artifactID retErr.PrivateErr = fmt.Errorf("cannot resolve artifact: %s", err) return nil, retErr } - defer response.Body.Close() + defer func() { + if err := response.Body.Close(); err != nil { + logger.DefaultLogger.Printf("Error while closing response body during artifact resolution: %v", err) + } + }() if response.StatusCode != 200 { retErr.PrivateErr = fmt.Errorf("Error during artifact resolution: HTTP status %d (%s)", response.StatusCode, response.Status) return nil, retErr } - responseBody, err := ioutil.ReadAll(response.Body) + responseBody, err := io.ReadAll(response.Body) if err != nil { retErr.PrivateErr = fmt.Errorf("Error during artifact resolution: %s", err) return nil, retErr @@ -753,11 +762,12 @@ func (sp *ServiceProvider) parseArtifactResponse(artifactResponseEl *etree.Eleme var signatureRequirement signatureRequirement sigErr := sp.validateSignature(artifactResponseEl) - if sigErr == nil { + switch sigErr { + case nil: signatureRequirement = signatureNotRequired - } else if sigErr == errSignatureElementNotPresent { + case errSignatureElementNotPresent: signatureRequirement = signatureRequired - } else { + default: retErr.PrivateErr = sigErr return nil, retErr } @@ -888,13 +898,14 @@ func (sp *ServiceProvider) parseResponse(responseEl *etree.Element, possibleRequ } if signatureRequirement == signatureRequired { - if responseSignatureErr == nil { + switch responseSignatureErr { + case nil: // since the request has a signature, none of the Assertions need one signatureRequirement = signatureNotRequired - } else if responseSignatureErr == errSignatureElementNotPresent { + case errSignatureElementNotPresent: // the request has no signature, so assertions must be signed signatureRequirement = signatureRequired // nop - } else { + default: return nil, responseSignatureErr } } @@ -1078,7 +1089,7 @@ func (sp *ServiceProvider) validateAssertion(assertion *Assertion, possibleReque return nil } -var errSignatureElementNotPresent = errors.New("Signature element not present") +var errSignatureElementNotPresent = errors.New("signature element not present") // validateSignature returns nil iff the Signature embedded in the element is valid func (sp *ServiceProvider) validateSignature(el *etree.Element) error { @@ -1154,9 +1165,9 @@ func (sp *ServiceProvider) SignLogoutRequest(req *LogoutRequest) error { Leaf: sp.Certificate, } // TODO: add intermediates for SP - //for _, cert := range sp.Intermediates { - // keyPair.Certificate = append(keyPair.Certificate, cert.Raw) - //} + // for _, cert := range sp.Intermediates { + // keyPair.Certificate = append(keyPair.Certificate, cert.Raw) + // } keyStore := dsig.TLSCertKeyStore(keyPair) if sp.SignatureMethod != dsig.RSASHA1SignatureMethod && @@ -1222,22 +1233,26 @@ func (sp *ServiceProvider) MakeRedirectLogoutRequest(nameID, relayState string) } // Redirect returns a URL suitable for using the redirect binding with the request -func (req *LogoutRequest) Redirect(relayState string) *url.URL { +func (r *LogoutRequest) Redirect(relayState string) *url.URL { w := &bytes.Buffer{} w1 := base64.NewEncoder(base64.StdEncoding, w) w2, _ := flate.NewWriter(w1, 9) doc := etree.NewDocument() - doc.SetRoot(req.Element()) + doc.SetRoot(r.Element()) if _, err := doc.WriteTo(w2); err != nil { panic(err) } - w2.Close() - w1.Close() + if err := w2.Close(); err != nil { + panic(err) + } + if err := w1.Close(); err != nil { + panic(err) + } - rv, _ := url.Parse(req.Destination) + rv, _ := url.Parse(r.Destination) query := rv.Query() - query.Set("SAMLRequest", string(w.Bytes())) + query.Set("SAMLRequest", w.String()) if relayState != "" { query.Set("RelayState", relayState) } @@ -1258,9 +1273,9 @@ func (sp *ServiceProvider) MakePostLogoutRequest(nameID, relayState string) ([]b } // Post returns an HTML form suitable for using the HTTP-POST binding with the request -func (req *LogoutRequest) Post(relayState string) []byte { +func (r *LogoutRequest) Post(relayState string) []byte { doc := etree.NewDocument() - doc.SetRoot(req.Element()) + doc.SetRoot(r.Element()) reqBuf, err := doc.WriteToBytes() if err != nil { panic(err) @@ -1280,7 +1295,7 @@ func (req *LogoutRequest) Post(relayState string) []byte { SAMLRequest string RelayState string }{ - URL: req.Destination, + URL: r.Destination, SAMLRequest: encodedReqBuf, RelayState: relayState, } @@ -1332,22 +1347,26 @@ func (sp *ServiceProvider) MakeRedirectLogoutResponse(logoutRequestID, relayStat } // Redirect returns a URL suitable for using the redirect binding with the LogoutResponse. -func (resp *LogoutResponse) Redirect(relayState string) *url.URL { +func (r *LogoutResponse) Redirect(relayState string) *url.URL { w := &bytes.Buffer{} w1 := base64.NewEncoder(base64.StdEncoding, w) w2, _ := flate.NewWriter(w1, 9) doc := etree.NewDocument() - doc.SetRoot(resp.Element()) + doc.SetRoot(r.Element()) if _, err := doc.WriteTo(w2); err != nil { panic(err) } - w2.Close() - w1.Close() + if err := w2.Close(); err != nil { + panic(err) + } + if err := w1.Close(); err != nil { + panic(err) + } - rv, _ := url.Parse(resp.Destination) + rv, _ := url.Parse(r.Destination) query := rv.Query() - query.Set("SAMLResponse", string(w.Bytes())) + query.Set("SAMLResponse", w.String()) if relayState != "" { query.Set("RelayState", relayState) } @@ -1368,9 +1387,9 @@ func (sp *ServiceProvider) MakePostLogoutResponse(logoutRequestID, relayState st } // Post returns an HTML form suitable for using the HTTP-POST binding with the LogoutResponse. -func (resp *LogoutResponse) Post(relayState string) []byte { +func (r *LogoutResponse) Post(relayState string) []byte { doc := etree.NewDocument() - doc.SetRoot(resp.Element()) + doc.SetRoot(r.Element()) reqBuf, err := doc.WriteToBytes() if err != nil { panic(err) @@ -1390,7 +1409,7 @@ func (resp *LogoutResponse) Post(relayState string) []byte { SAMLResponse string RelayState string }{ - URL: resp.Destination, + URL: r.Destination, SAMLResponse: encodedReqBuf, RelayState: relayState, } @@ -1411,9 +1430,9 @@ func (sp *ServiceProvider) SignLogoutResponse(resp *LogoutResponse) error { Leaf: sp.Certificate, } // TODO: add intermediates for SP - //for _, cert := range sp.Intermediates { - // keyPair.Certificate = append(keyPair.Certificate, cert.Raw) - //} + // for _, cert := range sp.Intermediates { + // keyPair.Certificate = append(keyPair.Certificate, cert.Raw) + // } keyStore := dsig.TLSCertKeyStore(keyPair) if sp.SignatureMethod != dsig.RSASHA1SignatureMethod && @@ -1502,10 +1521,7 @@ func (sp *ServiceProvider) ValidateLogoutResponseForm(postFormData string) error retErr.PrivateErr = err return retErr } - if err := sp.validateLogoutResponse(&resp); err != nil { - return err - } - return nil + return sp.validateLogoutResponse(&resp) } // ValidateLogoutResponseRedirect returns a nil error if the logout response is valid. @@ -1524,7 +1540,7 @@ func (sp *ServiceProvider) ValidateLogoutResponseRedirect(queryParameterData str } retErr.Response = string(rawResponseBuf) - gr, err := ioutil.ReadAll(newSaferFlateReader(bytes.NewBuffer(rawResponseBuf))) + gr, err := io.ReadAll(newSaferFlateReader(bytes.NewBuffer(rawResponseBuf))) if err != nil { retErr.PrivateErr = err return retErr @@ -1550,10 +1566,7 @@ func (sp *ServiceProvider) ValidateLogoutResponseRedirect(queryParameterData str retErr.PrivateErr = err return retErr } - if err := sp.validateLogoutResponse(&resp); err != nil { - return err - } - return nil + return sp.validateLogoutResponse(&resp) } // validateLogoutResponse validates the LogoutResponse fields. Returns a nil error if the LogoutResponse is valid. @@ -1585,6 +1598,7 @@ func firstSet(a, b string) string { // findChildren returns all the elements matching childNS/childTag that are direct children of parentEl. func findChildren(parentEl *etree.Element, childNS string, childTag string) ([]*etree.Element, error) { + //nolint:prealloc // We don't know how many child elements we'll actually put into this array. var rv []*etree.Element for _, childEl := range parentEl.ChildElements() { if childEl.Tag != childTag { diff --git a/vendor/github.com/crewjam/saml/util.go b/vendor/github.com/crewjam/saml/util.go index c9731b1b1..eda053eeb 100644 --- a/vendor/github.com/crewjam/saml/util.go +++ b/vendor/github.com/crewjam/saml/util.go @@ -21,6 +21,7 @@ var Clock *dsig.Clock // rand.Reader, but it can be replaced for testing. var RandReader = rand.Reader +//nolint:unparam // This always receives 20, but we want the option to do more or less if needed. func randomBytes(n int) []byte { rv := make([]byte, n) diff --git a/vendor/github.com/crewjam/saml/xmlenc/cbc.go b/vendor/github.com/crewjam/saml/xmlenc/cbc.go index 77ddb3b2f..991ba1ebd 100644 --- a/vendor/github.com/crewjam/saml/xmlenc/cbc.go +++ b/vendor/github.com/crewjam/saml/xmlenc/cbc.go @@ -31,7 +31,7 @@ func (e CBC) Algorithm() string { // Encrypt encrypts plaintext with key, which should be a []byte of length KeySize(). // It returns an xenc:EncryptedData element. -func (e CBC) Encrypt(key interface{}, plaintext []byte, nonce []byte) (*etree.Element, error) { +func (e CBC) Encrypt(key interface{}, plaintext []byte, _ []byte) (*etree.Element, error) { keyBuf, ok := key.([]byte) if !ok { return nil, ErrIncorrectKeyType("[]byte") diff --git a/vendor/github.com/crewjam/saml/xmlenc/decrypt.go b/vendor/github.com/crewjam/saml/xmlenc/decrypt.go index 93991f9f5..98a575da9 100644 --- a/vendor/github.com/crewjam/saml/xmlenc/decrypt.go +++ b/vendor/github.com/crewjam/saml/xmlenc/decrypt.go @@ -90,6 +90,7 @@ func validateRSAKeyIfPresent(key interface{}, encryptedKey *etree.Element) (*rsa // if the key will work, or let the service provider know which key // to use to decrypt the message. Either way, verification is not // security-critical. + //nolint:revive,staticcheck // Keep the later empty branch so that we know to address this at a later date. if el := encryptedKey.FindElement("./KeyInfo/X509Data/X509Certificate"); el != nil { certPEMbuf := el.Text() certPEMbuf = "-----BEGIN CERTIFICATE-----\n" + certPEMbuf + "\n-----END CERTIFICATE-----\n" diff --git a/vendor/github.com/crewjam/saml/xmlenc/digest.go b/vendor/github.com/crewjam/saml/xmlenc/digest.go index 801347f26..3eaaf7bc7 100644 --- a/vendor/github.com/crewjam/saml/xmlenc/digest.go +++ b/vendor/github.com/crewjam/saml/xmlenc/digest.go @@ -6,6 +6,7 @@ import ( "crypto/sha512" "hash" + //nolint:staticcheck // We should support this for legacy reasons. "golang.org/x/crypto/ripemd160" ) diff --git a/vendor/modules.txt b/vendor/modules.txt index 99368dcc0..1971943bf 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -328,8 +328,8 @@ github.com/cpuguy83/go-md2man/v2/md2man # github.com/crewjam/httperr v0.2.0 ## explicit; go 1.13 github.com/crewjam/httperr -# github.com/crewjam/saml v0.4.13 -## explicit; go 1.16 +# github.com/crewjam/saml v0.4.14 +## explicit; go 1.19 github.com/crewjam/saml github.com/crewjam/saml/logger github.com/crewjam/saml/samlsp