Bump github.com/crewjam/saml from 0.4.13 to 0.4.14

Bumps [github.com/crewjam/saml](https://github.com/crewjam/saml) from 0.4.13 to 0.4.14.
- [Commits](https://github.com/crewjam/saml/compare/v0.4.13...v0.4.14)

---
updated-dependencies:
- dependency-name: github.com/crewjam/saml
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2023-10-17 15:34:48 +00:00
committed by Ralf Haferkamp
parent b5ad0796c4
commit b3a92548b7
18 changed files with 321 additions and 179 deletions

2
go.mod
View File

@@ -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

10
go.sum
View File

@@ -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=

View File

@@ -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

View File

@@ -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
}

View File

@@ -1,3 +1,4 @@
// Package logger provides a logging interface.
package logger
import (

View File

@@ -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

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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(`<!DOCTYPE html><html><body>`))
w.Write(authReq.Post(relayState))
w.Write([]byte(`</body></html>`))
var buf bytes.Buffer
buf.WriteString(`<!DOCTYPE html><html><body>`)
buf.Write(authReq.Post(relayState))
buf.WriteString(`</body></html>`)
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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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"`

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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")

View File

@@ -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"

View File

@@ -6,6 +6,7 @@ import (
"crypto/sha512"
"hash"
//nolint:staticcheck // We should support this for legacy reasons.
"golang.org/x/crypto/ripemd160"
)

4
vendor/modules.txt vendored
View File

@@ -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