def main(config_file): logging.basicConfig( level=logging.INFO, format='%(asctime)s.%(msecs)03d example: %(levelname)s: %(message)s', datefmt='%Y-%m-%dT%H:%M:%S', ) config = ConfigParser.RawConfigParser() config_path = os.path.expanduser(config_file) config_path = os.path.abspath(config_path) with open(config_path) as f: config.readfp(f) host = config.get('app', 'host') port = config.get('app', 'port') port = int(port) settings = dict() settings['assertion_consumer_service_url'] = config.get( 'saml', 'assertion_consumer_service_url' ) settings['issuer'] = config.get( 'saml', 'issuer' ) settings['name_identifier_format'] = config.get( 'saml', 'name_identifier_format' ) settings['idp_sso_target_url'] = config.get( 'saml', 'idp_sso_target_url' ) settings['idp_cert_file'] = config.get( 'saml', 'idp_cert_file' ) settings['idp_cert_fingerprint'] = config.get( 'saml', 'idp_cert_fingerprint' ) cert_file = settings.pop('idp_cert_file', None) # idp_cert_file has priority over idp_cert_fingerprint if cert_file: cert_path = os.path.expanduser(cert_file) cert_path = os.path.abspath(cert_path) with open(cert_path) as f: cert = f.read() fingerprint = calculate_x509_fingerprint(cert) if fingerprint: settings['idp_cert_fingerprint'] = fingerprint else: formated = format_finger_print(settings['idp_cert_fingerprint']) settings['idp_cert_fingerprint'] = formated parts = urlparse.urlparse(settings['assertion_consumer_service_url']) SampleAppHTTPRequestHandler.protocol_version = 'HTTP/1.0' SampleAppHTTPRequestHandler.settings = settings SampleAppHTTPRequestHandler.saml_post_path = parts.path httpd = HTTPServer( (host, port), SampleAppHTTPRequestHandler, ) socket_name = httpd.socket.getsockname() log.info( 'Serving HTTP on {host} port {port} ...'.format( host=socket_name[0], port=socket_name[1], ) ) httpd.serve_forever()
def verify(document, signature, _etree=None, _tempfile=None, _subprocess=None, _os=None): """ Verify that signature contained in the samlp:Response is valid when checked against the provided signature. Return True if valid, otherwise False Arguments: document -- lxml.etree.XML object containing the samlp:Response signature -- The fingerprint to check the samlp:Response against """ if _etree is None: _etree = etree if _tempfile is None: _tempfile = tempfile if _subprocess is None: _subprocess = subprocess if _os is None: _os = os signatureNodes = document.xpath("//ds:Signature", namespaces={'ds': 'http://www.w3.org/2000/09/xmldsig#'}) parent_id_container = 'urn:oasis:names:tc:SAML:2.0:assertion:Assertion' if signatureNodes and signatureNodes[0].getparent().tag == '{urn:oasis:names:tc:SAML:2.0:protocol}Response': parent_id_container = 'urn:oasis:names:tc:SAML:2.0:protocol:Response' certificateNodes = document.xpath("//ds:X509Certificate", namespaces={'ds': 'http://www.w3.org/2000/09/xmldsig#'}) if not certificateNodes or calculate_x509_fingerprint(certificateNodes[0].text) != signature: return False else: # use the x509 cert instead of fingerprint required by xmlsec signature = format_cert(certificateNodes[0].text) xmlsec_bin = _get_xmlsec_bin() verified = False cert_filename = None xml_filename = None # Windows hack: Without the delete=False parameter in NamedTemporaryFile # xmlsec.exe will get an IO Permission Denied error. try: with _tempfile.NamedTemporaryFile(delete=False) as xml_fp: doc_str = _etree.tostring(document) xml_fp.write(doc_str) xml_fp.seek(0) with _tempfile.NamedTemporaryFile(delete=False) as cert_fp: cert_fp.write(signature) cert_fp.seek(0) cert_filename = cert_fp.name xml_filename = xml_fp.name # We cannot use xmlsec python bindings to verify here because # that would require a call to libxml2.xmlAddID. The libxml2 # python bindings do not yet provide this function. # http://www.aleksey.com/xmlsec/faq.html Section 3.2 cmds = [ xmlsec_bin, '--verify', '--pubkey-cert-pem', cert_filename, '--id-attr:ID', parent_id_container, xml_filename, ] proc = _subprocess.Popen( cmds, stderr=_subprocess.PIPE, stdout=_subprocess.PIPE, ) proc.wait() verified = _parse_stderr(proc) finally: if cert_filename is not None: _os.remove(cert_filename) if xml_filename is not None: _os.remove(xml_filename) return verified
def verify(document, signature, _etree=None, _tempfile=None, _subprocess=None, _os=None): """ Verify that signature contained in the samlp:Response is valid when checked against the provided signature. Return True if valid, otherwise False Arguments: document -- lxml.etree.XML object containing the samlp:Response signature -- The fingerprint to check the samlp:Response against """ if _etree is None: _etree = etree if _tempfile is None: _tempfile = tempfile if _subprocess is None: _subprocess = subprocess if _os is None: _os = os signatureNodes = document.xpath("//ds:Signature", namespaces={"ds": "http://www.w3.org/2000/09/xmldsig#"}) parent_id_container = "urn:oasis:names:tc:SAML:2.0:assertion:Assertion" if signatureNodes and signatureNodes[0].getparent().tag == "{urn:oasis:names:tc:SAML:2.0:protocol}Response": parent_id_container = "urn:oasis:names:tc:SAML:2.0:protocol:Response" certificateNodes = document.xpath("//ds:X509Certificate", namespaces={"ds": "http://www.w3.org/2000/09/xmldsig#"}) if not certificateNodes or calculate_x509_fingerprint(certificateNodes[0].text) != signature: return False else: # use the x509 cert instead of fingerprint required by xmlsec signature = format_cert(certificateNodes[0].text) xmlsec_bin = _get_xmlsec_bin() verified = False cert_filename = None xml_filename = None # Windows hack: Without the delete=False parameter in NamedTemporaryFile # xmlsec.exe will get an IO Permission Denied error. try: with _tempfile.NamedTemporaryFile(delete=False) as xml_fp: doc_str = _etree.tostring(document) xml_fp.write(doc_str) xml_fp.seek(0) with _tempfile.NamedTemporaryFile(delete=False) as cert_fp: cert_fp.write(signature) cert_fp.seek(0) cert_filename = cert_fp.name xml_filename = xml_fp.name # We cannot use xmlsec python bindings to verify here because # that would require a call to libxml2.xmlAddID. The libxml2 # python bindings do not yet provide this function. # http://www.aleksey.com/xmlsec/faq.html Section 3.2 cmds = [ xmlsec_bin, "--verify", "--pubkey-cert-pem", cert_filename, "--id-attr:ID", parent_id_container, xml_filename, ] proc = _subprocess.Popen(cmds, stderr=_subprocess.PIPE, stdout=_subprocess.PIPE) proc.wait() verified = _parse_stderr(proc) finally: if cert_filename is not None: _os.remove(cert_filename) if xml_filename is not None: _os.remove(xml_filename) return verified
def main(config_file): logging.basicConfig( level=logging.INFO, format='%(asctime)s.%(msecs)03d example: %(levelname)s: %(message)s', datefmt='%Y-%m-%dT%H:%M:%S', ) config = ConfigParser.RawConfigParser() config_path = os.path.expanduser(config_file) config_path = os.path.abspath(config_path) with open(config_path) as f: config.readfp(f) host = config.get('app', 'host') port = config.get('app', 'port') port = int(port) settings = dict() settings['assertion_consumer_service_url'] = config.get( 'saml', 'assertion_consumer_service_url') settings['issuer'] = config.get('saml', 'issuer') settings['name_identifier_format'] = config.get('saml', 'name_identifier_format') settings['idp_sso_target_url'] = config.get('saml', 'idp_sso_target_url') settings['idp_cert_file'] = config.get('saml', 'idp_cert_file') settings['idp_cert_fingerprint'] = config.get('saml', 'idp_cert_fingerprint') cert_file = settings.pop('idp_cert_file', None) # idp_cert_file has priority over idp_cert_fingerprint if cert_file: cert_path = os.path.expanduser(cert_file) cert_path = os.path.abspath(cert_path) with open(cert_path) as f: cert = f.read() fingerprint = calculate_x509_fingerprint(cert) if fingerprint: settings['idp_cert_fingerprint'] = fingerprint else: formated = format_finger_print(settings['idp_cert_fingerprint']) settings['idp_cert_fingerprint'] = formated parts = urlparse.urlparse(settings['assertion_consumer_service_url']) SampleAppHTTPRequestHandler.protocol_version = 'HTTP/1.0' SampleAppHTTPRequestHandler.settings = settings SampleAppHTTPRequestHandler.saml_post_path = parts.path httpd = HTTPServer( (host, port), SampleAppHTTPRequestHandler, ) socket_name = httpd.socket.getsockname() log.info('Serving HTTP on {host} port {port} ...'.format( host=socket_name[0], port=socket_name[1], )) httpd.serve_forever()