Skip to content

Commit b07b16c

Browse files
authored
Merge pull request from GHSA-267v-3v32-g6q5
1 parent 5f6e5ab commit b07b16c

3 files changed

+116
-0
lines changed

metadata.go

+107
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package saml
22

33
import (
44
"encoding/xml"
5+
"fmt"
6+
"net/url"
57
"time"
68

79
"github.com/beevik/etree"
@@ -19,6 +21,9 @@ const HTTPArtifactBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
1921
// SOAPBinding is the official URN for the SOAP binding (transport)
2022
const SOAPBinding = "urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
2123

24+
// SOAPBindingV1 is the URN for the SOAP binding in SAML 1.0
25+
const SOAPBindingV1 = "urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding"
26+
2227
// EntitiesDescriptor represents the SAML object of the same name.
2328
//
2429
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.1
@@ -188,6 +193,76 @@ type Endpoint struct {
188193
ResponseLocation string `xml:"ResponseLocation,attr,omitempty"`
189194
}
190195

196+
func checkEndpointLocation(binding string, location string) (string, error) {
197+
// Within the SAML standard, the complex type EndpointType describes a
198+
// SAML protocol binding endpoint at which a SAML entity can be sent
199+
// protocol messages. In particular, the location of an endpoint type is
200+
// defined as follows in the Metadata for the OASIS Security Assertion
201+
// Markup Language (SAML) V2.0 - 2.2.2 Complex Type EndpointType:
202+
//
203+
// Location [Required] A required URI attribute that specifies the
204+
// location of the endpoint. The allowable syntax of this URI depends
205+
// on the protocol binding.
206+
switch binding {
207+
case HTTPPostBinding,
208+
HTTPRedirectBinding,
209+
HTTPArtifactBinding,
210+
SOAPBinding,
211+
SOAPBindingV1:
212+
locationURL, err := url.Parse(location)
213+
if err != nil {
214+
return "", fmt.Errorf("invalid url %q: %w", location, err)
215+
}
216+
switch locationURL.Scheme {
217+
case "http", "https":
218+
// ok
219+
default:
220+
return "", fmt.Errorf("invalid url scheme %q for binding %q",
221+
locationURL.Scheme, binding)
222+
}
223+
default:
224+
// We don't know what form location should take, but the protocol
225+
// requires that we validate its syntax.
226+
//
227+
// In practice, lots of metadata contains random bindings, for example
228+
// "urn:mace:shibboleth:1.0:profiles:AuthnRequest" from our own test suite.
229+
//
230+
// We can't fail, but we also can't allow a location parameter whose syntax we
231+
// cannot verify. The least-bad course of action here is to set location to
232+
// and empty string, and hope the caller doesn't care need it.
233+
location = ""
234+
}
235+
236+
return location, nil
237+
}
238+
239+
// UnmarshalXML implements xml.Unmarshaler
240+
func (m *Endpoint) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
241+
type Alias Endpoint
242+
aux := &struct {
243+
*Alias
244+
}{
245+
Alias: (*Alias)(m),
246+
}
247+
if err := d.DecodeElement(aux, &start); err != nil {
248+
return err
249+
}
250+
251+
var err error
252+
m.Location, err = checkEndpointLocation(m.Binding, m.Location)
253+
if err != nil {
254+
return err
255+
}
256+
if m.ResponseLocation != "" {
257+
m.ResponseLocation, err = checkEndpointLocation(m.Binding, m.ResponseLocation)
258+
if err != nil {
259+
return err
260+
}
261+
}
262+
263+
return nil
264+
}
265+
191266
// IndexedEndpoint represents the SAML IndexedEndpointType object.
192267
//
193268
// 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 {
199274
IsDefault *bool `xml:"isDefault,attr"`
200275
}
201276

277+
// UnmarshalXML implements xml.Unmarshaler
278+
func (m *IndexedEndpoint) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
279+
type Alias IndexedEndpoint
280+
aux := &struct {
281+
*Alias
282+
}{
283+
Alias: (*Alias)(m),
284+
}
285+
if err := d.DecodeElement(aux, &start); err != nil {
286+
return err
287+
}
288+
289+
var err error
290+
m.Location, err = checkEndpointLocation(m.Binding, m.Location)
291+
if err != nil {
292+
return err
293+
}
294+
if m.ResponseLocation != nil {
295+
responseLocation, err := checkEndpointLocation(m.Binding, *m.ResponseLocation)
296+
if err != nil {
297+
return err
298+
}
299+
if responseLocation != "" {
300+
m.ResponseLocation = &responseLocation
301+
} else {
302+
m.ResponseLocation = nil
303+
}
304+
}
305+
306+
return nil
307+
}
308+
202309
// SSODescriptor represents the SAML complex type SSODescriptor
203310
//
204311
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.2

metadata_test.go

+8
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,11 @@ cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==`,
165165
assert.Check(t, err)
166166
golden.Assert(t, string(buf), "TestCanProduceSPMetadata_expected")
167167
}
168+
169+
func TestMetadataValidatesUrlSchemeForProtocolBinding(t *testing.T) {
170+
buf := golden.Get(t, "TestMetadataValidatesUrlSchemeForProtocolBinding_metadata.xml")
171+
172+
metadata := EntityDescriptor{}
173+
err := xml.Unmarshal(buf, &metadata)
174+
assert.Error(t, err, "invalid url scheme \"javascript\" for binding \"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"")
175+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<?xml version='1.0' encoding='UTF-8'?><md:EntityDescriptor ID='_af805d1c-c2e3-444e-9cf5-efc664eeace6' entityID='https://dev.aa.kndr.org/users/auth/saml/metadata' validUntil='2001-02-03T04:05:06.789' cacheDuration='PT1H' xmlns:md='urn:oasis:names:tc:SAML:2.0:metadata' xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'><md:SPSSODescriptor AuthnRequestsSigned='false' WantAssertionsSigned='false' protocolSupportEnumeration='urn:oasis:names:tc:SAML:2.0:protocol'><md:AssertionConsumerService Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' Location='javascript:alert(true)' index='0' isDefault='true'/><md:AttributeConsumingService index='1' isDefault='true'><md:ServiceName xml:lang='en'>Required attributes</md:ServiceName><md:RequestedAttribute FriendlyName='Email address' Name='email' NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic'/><md:RequestedAttribute FriendlyName='Full name' Name='name' NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic'/><md:RequestedAttribute FriendlyName='Given name' Name='first_name' NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic'/><md:RequestedAttribute FriendlyName='Family name' Name='last_name' NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic'/></md:AttributeConsumingService></md:SPSSODescriptor></md:EntityDescriptor>

0 commit comments

Comments
 (0)