Skip to content

Commit 25d0bc5

Browse files
committed
Bugfix for ADFS lowercase signatures
1 parent 3cf2203 commit 25d0bc5

4 files changed

Lines changed: 19 additions & 14 deletions

File tree

src/onelogin/saml2/auth.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def process_response(self, request_id=None):
111111
OneLogin_Saml2_Error.SAML_RESPONSE_NOT_FOUND
112112
)
113113

114-
def process_slo(self, keep_local_session=False, request_id=None, delete_session_cb=None):
114+
def process_slo(self, keep_local_session=False, request_id=None, delete_session_cb=None, lowercase_urlencoding=False):
115115
"""
116116
Process the SAML Logout Response / Logout Request sent by the IdP.
117117
@@ -127,7 +127,7 @@ def process_slo(self, keep_local_session=False, request_id=None, delete_session_
127127

128128
if 'get_data' in self.__request_data and 'SAMLResponse' in self.__request_data['get_data']:
129129
logout_response = OneLogin_Saml2_Logout_Response(self.__settings, self.__request_data['get_data']['SAMLResponse'])
130-
if not logout_response.is_valid(self.__request_data, request_id):
130+
if not logout_response.is_valid(self.__request_data, request_id, lowercase_urlencoding=lowercase_urlencoding):
131131
self.__errors.append('invalid_logout_response')
132132
self.__error_reason = logout_response.get_error()
133133
elif logout_response.get_status() != OneLogin_Saml2_Constants.STATUS_SUCCESS:
@@ -137,7 +137,7 @@ def process_slo(self, keep_local_session=False, request_id=None, delete_session_
137137

138138
elif 'get_data' in self.__request_data and 'SAMLRequest' in self.__request_data['get_data']:
139139
logout_request = OneLogin_Saml2_Logout_Request(self.__settings, self.__request_data['get_data']['SAMLRequest'])
140-
if not logout_request.is_valid(self.__request_data):
140+
if not logout_request.is_valid(self.__request_data, lowercase_urlencoding=lowercase_urlencoding):
141141
self.__errors.append('invalid_logout_request')
142142
self.__error_reason = logout_request.get_error()
143143
else:

src/onelogin/saml2/logout_request.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ def get_session_indexes(request):
251251
session_indexes.append(session_index_node.text)
252252
return session_indexes
253253

254-
def is_valid(self, request_data):
254+
def is_valid(self, request_data, lowercase_urlencoding=False):
255255
"""
256256
Checks if the Logout Request recieved is valid
257257
:param request_data: Request Data
@@ -316,10 +316,10 @@ def is_valid(self, request_data):
316316
else:
317317
sign_alg = get_data['SigAlg']
318318

319-
signed_query = 'SAMLRequest=%s' % OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'SAMLRequest')
319+
signed_query = 'SAMLRequest=%s' % OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'SAMLRequest', lowercase_urlencoding=lowercase_urlencoding)
320320
if 'RelayState' in get_data:
321-
signed_query = '%s&RelayState=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'RelayState'))
322-
signed_query = '%s&SigAlg=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'SigAlg', OneLogin_Saml2_Constants.RSA_SHA1))
321+
signed_query = '%s&RelayState=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'RelayState', lowercase_urlencoding=lowercase_urlencoding))
322+
signed_query = '%s&SigAlg=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'SigAlg', OneLogin_Saml2_Constants.RSA_SHA1, lowercase_urlencoding=lowercase_urlencoding))
323323

324324
if 'x509cert' not in idp_data or idp_data['x509cert'] is None:
325325
raise Exception('In order to validate the sign on the Logout Request, the x509cert of the IdP is required')

src/onelogin/saml2/logout_response.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def get_status(self):
6868
status = entries[0].attrib['Value']
6969
return status
7070

71-
def is_valid(self, request_data, request_id=None):
71+
def is_valid(self, request_data, request_id=None, lowercase_urlencoding=False):
7272
"""
7373
Determines if the SAML LogoutResponse is valid
7474
:param request_id: The ID of the LogoutRequest sent by this SP to the IdP
@@ -119,10 +119,10 @@ def is_valid(self, request_data, request_id=None):
119119
else:
120120
sign_alg = get_data['SigAlg']
121121

122-
signed_query = 'SAMLResponse=%s' % OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'SAMLResponse')
122+
signed_query = 'SAMLResponse=%s' % OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'SAMLResponse', lowercase_urlencoding=lowercase_urlencoding)
123123
if 'RelayState' in get_data:
124-
signed_query = '%s&RelayState=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'RelayState'))
125-
signed_query = '%s&SigAlg=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'SigAlg', OneLogin_Saml2_Constants.RSA_SHA1))
124+
signed_query = '%s&RelayState=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'RelayState', lowercase_urlencoding=lowercase_urlencoding))
125+
signed_query = '%s&SigAlg=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'SigAlg', OneLogin_Saml2_Constants.RSA_SHA1, lowercase_urlencoding=lowercase_urlencoding))
126126

127127
if 'x509cert' not in idp_data or idp_data['x509cert'] is None:
128128
raise Exception('In order to validate the sign on the Logout Response, the x509cert of the IdP is required')

src/onelogin/saml2/utils.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,18 +1127,18 @@ def validate_binary_sign(signed_query, signature, cert=None, algorithm=OneLogin_
11271127
return False
11281128

11291129
@staticmethod
1130-
def get_encoded_parameter(get_data, name, default=None):
1130+
def get_encoded_parameter(get_data, name, default=None, lowercase_urlencoding=False):
11311131
"""Return an url encoded get parameter value
11321132
Prefer to extract the original encoded value directly from query_string since url
11331133
encoding is not canonical. The encoding used by ADFS 3.0 is not compatible with
11341134
python's quote_plus (ADFS produces lower case hex numbers and quote_plus produces
11351135
upper case hex numbers)
11361136
"""
11371137
if name not in get_data:
1138-
return quote_plus(default)
1138+
return case_sensitive_urlencode(default, lowercase_urlencoding)
11391139
if 'query_string' in get_data:
11401140
return OneLogin_Saml2_Utils.extract_raw_query_parameter(get_data['query_string'], name)
1141-
return quote_plus(get_data[name])
1141+
return case_sensitive_urlencode(get_data[name], lowercase_urlencoding)
11421142

11431143
@staticmethod
11441144
def extract_raw_query_parameter(query_string, parameter, default=''):
@@ -1147,3 +1147,8 @@ def extract_raw_query_parameter(query_string, parameter, default=''):
11471147
return m.group(1)
11481148
else:
11491149
return default
1150+
1151+
@staticmethod
1152+
def case_sensitive_urlencode(to_encode, lowercase=False):
1153+
encoded=quote_plus(to_encode)
1154+
return re.sub(r"%[A-F0-9]{2}", lambda m: m.group(0).lower(), encoded) if lowercase else encoded

0 commit comments

Comments
 (0)