@@ -204,6 +204,7 @@ func (sp *SAMLServiceProvider) validateElementSignature(el *etree.Element) (*etr
204204 return sp .validationContext ().Validate (el )
205205}
206206
207+ // deprecated
207208func (sp * SAMLServiceProvider ) validateAssertionSignatures (el * etree.Element ) error {
208209 signedAssertions := 0
209210 unsignedAssertions := 0
@@ -264,55 +265,131 @@ func (sp *SAMLServiceProvider) ValidateEncodedResponse(encodedResponse string) (
264265 }
265266
266267 // Parse the raw response
267- doc , el , err := parseResponse (raw , sp .MaximumDecompressedBodySize )
268+ doc , unverifiedResponse , err := parseResponse (raw , sp .MaximumDecompressedBodySize )
268269 if err != nil {
269270 return nil , err
270271 }
271272
272273 var responseSignatureValidated bool
273- if ! sp .SkipSignatureValidation {
274- el , err = sp .validateElementSignature (el )
275- if err == dsig .ErrMissingSignature {
276- // Unfortunately we just blew away our Response
277- el = doc .Root ()
278- } else if err != nil {
274+ // storing our final response to return back
275+ decodedResponse := & types.Response {}
276+ // user has decided to skip signature verification
277+ // just unmarshal the untrusted el
278+
279+ if sp .SkipSignatureValidation {
280+ err = xmlUnmarshalElement (unverifiedResponse , decodedResponse )
281+ if err != nil {
282+ return nil , fmt .Errorf ("unable to unmarshal response: %v" , err )
283+ }
284+
285+ decodedResponse .SignatureValidated = false
286+ err := sp .Validate (decodedResponse )
287+ if err != nil {
279288 return nil , err
280- } else if el == nil {
281- return nil , fmt .Errorf ("missing transformed response" )
282- } else {
283- responseSignatureValidated = true
284289 }
290+ return decodedResponse , nil
285291 }
286292
287- err = sp .decryptAssertions (el )
288- if err != nil {
293+ // first get SignedResponse, if any
294+ signedResponseEl , err := sp .validateElementSignature (unverifiedResponse )
295+
296+ // continue for unsigned Response, maybe individual Assertions are still signed
297+ if err == dsig .ErrMissingSignature {
298+ // Unfortunately we just blew away our Response
299+ unverifiedResponse = doc .Root ()
300+ } else if err != nil {
289301 return nil , err
290- }
302+ } else if signedResponseEl == nil {
303+ return nil , fmt .Errorf ("missing transformed response" )
304+ } else {
305+ // good case, no errors when verifying signature
306+ // 1. Response is signed
307+ // optionally decrypt each assertion
308+ err = sp .decryptAssertions (signedResponseEl )
309+ if err != nil {
310+ return nil , err
311+ }
291312
292- var assertionSignaturesValidated bool
293- if ! sp .SkipSignatureValidation {
294- err = sp .validateAssertionSignatures (el )
295- if err == dsig .ErrMissingSignature {
296- if ! responseSignatureValidated {
297- return nil , fmt .Errorf ("response and/or assertions must be signed" )
298- }
299- } else if err != nil {
313+ responseSignatureValidated = true
314+
315+ err = xmlUnmarshalElement (signedResponseEl , decodedResponse )
316+ if err != nil {
317+ return nil , fmt .Errorf ("unable to unmarshal response: %v" , err )
318+ }
319+ decodedResponse .SignatureValidated = responseSignatureValidated
320+
321+ err := sp .Validate (decodedResponse )
322+ if err != nil {
300323 return nil , err
301- } else {
302- assertionSignaturesValidated = true
303324 }
325+ return decodedResponse , nil
304326 }
305327
306- decodedResponse := & types.Response {}
307- err = xmlUnmarshalElement (el , decodedResponse )
328+ // now we have a tricky case,
329+ // unsigned response but have some signed Assertions
330+ // unmarshal into decodedResponse,
331+
332+ err = xmlUnmarshalElement (unverifiedResponse , decodedResponse )
308333 if err != nil {
309- return nil , fmt . Errorf ( "unable to unmarshal response: %v" , err )
334+ return nil , err
310335 }
311- decodedResponse .SignatureValidated = responseSignatureValidated
312- if assertionSignaturesValidated {
313- for idx := 0 ; idx < len (decodedResponse .Assertions ); idx ++ {
314- decodedResponse .Assertions [idx ].SignatureValidated = true
336+
337+ // keep in mind anything inside the Response is technically untrusted
338+ // however, we have to keep the relevant details such as StatusCode
339+ // We reset the underlying assertions & encrypted assertions to []
340+
341+ decodedResponse .SignatureValidated = false
342+ decodedResponse .Assertions = []types.Assertion {}
343+ decodedResponse .EncryptedAssertions = []types.EncryptedAssertion {}
344+
345+ // first decrypt all assertions
346+ err = sp .decryptAssertions (unverifiedResponse )
347+ if err != nil {
348+ return nil , err
349+ }
350+
351+ // iterate through each Assertion inside our etree unverifiedResponse
352+ addSignedAssertion := func (ctx etreeutils.NSContext , unverifiedAssertion * etree.Element ) error {
353+ parent := unverifiedAssertion .Parent ()
354+ if parent == nil {
355+ return fmt .Errorf ("parent is nil" )
315356 }
357+ if parent != unverifiedResponse {
358+ return fmt .Errorf ("found assertion with unexpected parent element: %s" , unverifiedAssertion .Parent ().Tag )
359+ }
360+
361+ detached , err := etreeutils .NSDetatch (ctx , unverifiedAssertion ) // make a detached copy
362+ if err != nil {
363+ return fmt .Errorf ("unable to detach unverified assertion: %v" , err )
364+ }
365+
366+ // signedAssertion after checking for errors
367+ signedAssertion , err := sp .validationContext ().Validate (detached )
368+
369+ if err != nil {
370+ return err // return any errors including unsignedAssertions
371+ }
372+
373+ decodedAssertion := & types.Assertion {}
374+
375+ err = xmlUnmarshalElement (signedAssertion , decodedAssertion )
376+ if err != nil {
377+ return fmt .Errorf ("unable to unmarshal assertion: %v" , err )
378+ }
379+
380+ decodedAssertion .SignatureValidated = true
381+
382+ // now add it to decodedResponse
383+ decodedResponse .Assertions = append (decodedResponse .Assertions , * decodedAssertion )
384+
385+ return nil
386+ }
387+
388+ // iterate through each Assertion through our unverified Response
389+ // our decodedResponse contains a empty list of Assertions
390+ // throughout iteration, we will add signed assertions to the decodedResponse
391+ if err := etreeutils .NSFindIterate (unverifiedResponse , SAMLAssertionNamespace , AssertionTag , addSignedAssertion ); err != nil {
392+ return nil , err
316393 }
317394
318395 err = sp .Validate (decodedResponse )
0 commit comments