Skip to content

Commit b7db51f

Browse files
JguerIevaVasiljeva
andcommitted
implement review feedback
- Return error if signature is unsupported - wrap errors Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>
1 parent 5e6dd29 commit b7db51f

File tree

3 files changed

+44
-7
lines changed

3 files changed

+44
-7
lines changed

service_provider.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,8 +1478,9 @@ func (sp *ServiceProvider) nameIDFormat() string {
14781478

14791479
// ValidateLogoutResponseRequest validates the LogoutResponse content from the request
14801480
func (sp *ServiceProvider) ValidateLogoutResponseRequest(req *http.Request) error {
1481-
if data := req.URL.Query().Get("SAMLResponse"); data != "" {
1482-
return sp.ValidateLogoutResponseRedirect(req.URL.Query())
1481+
query := req.URL.Query()
1482+
if data := query.Get("SAMLResponse"); data != "" {
1483+
return sp.ValidateLogoutResponseRedirect(query)
14831484
}
14841485

14851486
err := req.ParseForm()

service_provider_signed.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ var (
2626
// ErrInvalidQuerySignature is returned when the query signature is invalid
2727
ErrInvalidQuerySignature = errors.New("invalid query signature")
2828
// ErrNoQuerySignature is returned when the query does not contain a signature
29-
ErrNoQuerySignature = errors.New("no query signature present")
29+
ErrNoQuerySignature = errors.New("query Signature or SigAlg not found")
3030
)
3131

3232
// Sign Query with the SP private key.
@@ -80,7 +80,9 @@ func (sp *ServiceProvider) validateQuerySig(query url.Values) error {
8080
return fmt.Errorf("No SAMLResponse or SAMLRequest found in query")
8181
}
8282

83-
// Encode Query as standard demands. query.Encode() is not standard compliant
83+
// Encode Query as standard demands.
84+
// query.Encode() is not standard compliant
85+
// as query encoding order matters
8486
res := respType + "=" + url.QueryEscape(query.Get(respType))
8587

8688
relayState := query.Get("RelayState")
@@ -93,7 +95,7 @@ func (sp *ServiceProvider) validateQuerySig(query url.Values) error {
9395
// Signature is base64 encoded
9496
sigBytes, err := base64.StdEncoding.DecodeString(sig)
9597
if err != nil {
96-
return err
98+
return fmt.Errorf("failed to decode signature: %w", err)
9799
}
98100

99101
var (
@@ -119,6 +121,8 @@ func (sp *ServiceProvider) validateQuerySig(query url.Values) error {
119121
hashed = hashed1[:]
120122
hashAlg = crypto.SHA1
121123
sigAlg = x509.SHA1WithRSA
124+
default:
125+
return fmt.Errorf("unsupported signature algorithm: %s", alg)
122126
}
123127

124128
// validate signature

service_provider_signed_test.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"crypto/rsa"
55
"encoding/base64"
66
"encoding/xml"
7-
"fmt"
87
"net/url"
98
"testing"
109

@@ -17,23 +16,28 @@ import (
1716
// Using same Cert for SP and IdP in order to test
1817
func TestSigningAndValidation(t *testing.T) {
1918
type testCase struct {
19+
desc string
2020
relayState string
2121
requestType reqType
22+
wantErr bool
2223
wantRawQuery string
2324
}
2425

2526
testCases := []testCase{
2627
{
28+
desc: "validate signature of SAMLRequest with relayState",
2729
relayState: "AAAAAAAAAAAA",
2830
requestType: samlRequest,
2931
wantRawQuery: "SAMLRequest=PHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBJRD0iaWQtMDAwMjA0MDYwODBhMGMwZTEwMTIxNDE2MTgxYTFjMWUyMDIyMjQyNiIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTUtMTItMDFUMDE6NTc6MDlaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9pZHAuZXhhbXBsZS5jb20vc2FtbC9zc28iIEFzc2VydGlvbkNvbnN1bWVyU2VydmljZVVSTD0iaHR0cHM6Ly9zcC5leGFtcGxlLmNvbS9zYW1sMi9hY3MiIFByb3RvY29sQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUE9TVCI%2BPHNhbWw6SXNzdWVyIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5Ij5odHRwczovL3NwLmV4YW1wbGUuY29tL3NhbWwyL21ldGFkYXRhPC9zYW1sOklzc3Vlcj48c2FtbHA6TmFtZUlEUG9saWN5IEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6dHJhbnNpZW50IiBBbGxvd0NyZWF0ZT0idHJ1ZSIvPjwvc2FtbHA6QXV0aG5SZXF1ZXN0Pg%3D%3D&RelayState=AAAAAAAAAAAA&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=zWAF4S%2FIs7tfmEriOsT5Fm8EFOGS3iCq6OxP5i7hM%2BMPwAoXwdDz6fKH8euS1gQ3sGOZBdHD588FZLvnO1OeCxLaEsxHMVKsAZSZFLBmPPwqB6e%2B84cCwX2szOeoMROaR%2B36mdoBDRQz36JIvyBBG%2FND9x41k%2FGQuAuwk%2B9fkuE%3D",
3032
},
3133
{
34+
desc: "validate signature of SAML request without relay state",
3235
relayState: "",
3336
requestType: samlRequest,
3437
wantRawQuery: "SAMLRequest=PHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBJRD0iaWQtMDAwMjA0MDYwODBhMGMwZTEwMTIxNDE2MTgxYTFjMWUyMDIyMjQyNiIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTUtMTItMDFUMDE6NTc6MDlaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9pZHAuZXhhbXBsZS5jb20vc2FtbC9zc28iIEFzc2VydGlvbkNvbnN1bWVyU2VydmljZVVSTD0iaHR0cHM6Ly9zcC5leGFtcGxlLmNvbS9zYW1sMi9hY3MiIFByb3RvY29sQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUE9TVCI%2BPHNhbWw6SXNzdWVyIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5Ij5odHRwczovL3NwLmV4YW1wbGUuY29tL3NhbWwyL21ldGFkYXRhPC9zYW1sOklzc3Vlcj48c2FtbHA6TmFtZUlEUG9saWN5IEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6dHJhbnNpZW50IiBBbGxvd0NyZWF0ZT0idHJ1ZSIvPjwvc2FtbHA6QXV0aG5SZXF1ZXN0Pg%3D%3D&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=HDdoHJSdkYh9%2BmE7RZ1LXcsAWIMJ6LuzKJgwLxH%2BQ4sKFlh8b5moFuQ%2B7rPEwoTcg9SjgCGV5rW9v8PrSU7WGKcLfAbeVwXWyU94ghjFZHEj%2BFCDpsfTD750ZPAPVnhVr0GogFZZ7c%2BEWX4NAqL4CYxDvsg56o%2BpOjw62G%2FyPDc%3D",
3538
},
3639
{
40+
desc: "validate signature of SAML response with relay state",
3741
relayState: "AAAAAAAAAAAA",
3842
requestType: samlResponse,
3943
wantRawQuery: "SAMLResponse=PHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBJRD0iaWQtMDAwMjA0MDYwODBhMGMwZTEwMTIxNDE2MTgxYTFjMWUyMDIyMjQyNiIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTUtMTItMDFUMDE6NTc6MDlaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9pZHAuZXhhbXBsZS5jb20vc2FtbC9zc28iIEFzc2VydGlvbkNvbnN1bWVyU2VydmljZVVSTD0iaHR0cHM6Ly9zcC5leGFtcGxlLmNvbS9zYW1sMi9hY3MiIFByb3RvY29sQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUE9TVCI%2BPHNhbWw6SXNzdWVyIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5Ij5odHRwczovL3NwLmV4YW1wbGUuY29tL3NhbWwyL21ldGFkYXRhPC9zYW1sOklzc3Vlcj48c2FtbHA6TmFtZUlEUG9saWN5IEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6dHJhbnNpZW50IiBBbGxvd0NyZWF0ZT0idHJ1ZSIvPjwvc2FtbHA6QXV0aG5SZXF1ZXN0Pg%3D%3D&RelayState=AAAAAAAAAAAA&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=JDeiWfLgV7SZqgqU64wgtAHS%2FqtF2c3c%2B9g1vdfRHn03tm5jrgsvJtIYg1BD8HoejCoyruH3xgDz1i2qqecVcUiAdaVgVvhn0JWJ%2BzeN9YpUFTEQ4Ah1pwezlSArzuz5esgYzSkemViox313HePWZ%2Fd0FAmtdXuGHA8O0Lp%2F4Ws%3D",
@@ -61,7 +65,7 @@ func TestSigningAndValidation(t *testing.T) {
6165
reqString := base64.StdEncoding.EncodeToString(req)
6266

6367
for _, tc := range testCases {
64-
t.Run(fmt.Sprintf("%s_%s", tc.relayState, tc.requestType), func(t *testing.T) {
68+
t.Run(tc.desc, func(t *testing.T) {
6569
relayState := tc.relayState
6670

6771
rawQuery := string(tc.requestType) + "=" + url.QueryEscape(reqString)
@@ -83,3 +87,31 @@ func TestSigningAndValidation(t *testing.T) {
8387
})
8488
}
8589
}
90+
91+
// Given a raw query with an unsupported signature method, the signature should be rejected.
92+
func TestInvalidSignatureAlgorithm(t *testing.T) {
93+
rawQuery := "SAMLRequest=PHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBJRD0iaWQtMDAwMjA0MDYwODBhMGMwZTEwMTIxNDE2MTgxYTFjMWUyMDIyMjQyNiIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTUtMTItMDFUMDE6NTc6MDlaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9pZHAuZXhhbXBsZS5jb20vc2FtbC9zc28iIEFzc2VydGlvbkNvbnN1bWVyU2VydmljZVVSTD0iaHR0cHM6Ly9zcC5leGFtcGxlLmNvbS9zYW1sMi9hY3MiIFByb3RvY29sQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUE9TVCI%2BPHNhbWw6SXNzdWVyIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5Ij5odHRwczovL3NwLmV4YW1wbGUuY29tL3NhbWwyL21ldGFkYXRhPC9zYW1sOklzc3Vlcj48c2FtbHA6TmFtZUlEUG9saWN5IEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6dHJhbnNpZW50IiBBbGxvd0NyZWF0ZT0idHJ1ZSIvPjwvc2FtbHA6QXV0aG5SZXF1ZXN0Pg%3D%3D&RelayState=AAAAAAAAAAAA&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha384&Signature=zWAF4S%2FIs7tfmEriOsT5Fm8EFOGS3iCq6OxP5i7hM%2BMPwAoXwdDz6fKH8euS1gQ3sGOZBdHD588FZLvnO1OeCxLaEsxHMVKsAZSZFLBmPPwqB6e%2B84cCwX2szOeoMROaR%2B36mdoBDRQz36JIvyBBG%2FND9x41k%2FGQuAuwk%2B9fkuE%3D"
94+
95+
idpMetadata := golden.Get(t, "SP_IDPMetadata_signing")
96+
s := ServiceProvider{
97+
Key: mustParsePrivateKey(golden.Get(t, "idp_key.pem")).(*rsa.PrivateKey),
98+
Certificate: mustParseCertificate(golden.Get(t, "idp_cert.pem")),
99+
MetadataURL: mustParseURL("https://15661444.ngrok.io/saml2/metadata"),
100+
AcsURL: mustParseURL("https://15661444.ngrok.io/saml2/acs"),
101+
SignatureMethod: dsig.RSASHA1SignatureMethod,
102+
}
103+
104+
err := xml.Unmarshal(idpMetadata, &s.IDPMetadata)
105+
idpCert, err := s.getIDPSigningCerts()
106+
107+
assert.Check(t, err == nil)
108+
assert.Check(t,
109+
s.Certificate.Issuer.CommonName == idpCert[0].Issuer.CommonName, "expected %s, got %s",
110+
s.Certificate.Issuer.CommonName, idpCert[0].Issuer.CommonName)
111+
112+
query, err := url.ParseQuery(rawQuery)
113+
assert.NilError(t, err, "error parsing query: %s", err)
114+
115+
err = s.validateQuerySig(query)
116+
assert.Error(t, err, "unsupported signature algorithm: http://www.w3.org/2000/09/xmldsig#rsa-sha384")
117+
}

0 commit comments

Comments
 (0)