source: TI12-security/trunk/python/ndg.security.server/ndg/security/server/wsgi/ssl.py @ 5280

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.server/ndg/security/server/wsgi/ssl.py@5280
Revision 5280, 5.3 KB checked in by pjkersha, 10 years ago (diff)

Further improvements to the authorization middleware:

  • PEPFilter no longer explicitly calls the PEPResultHandlerMiddleware (This latter class is the WSGI component which handles the access denied response that the server returns). This is not necessary as it can set a 403 response in order to trigger multiHandlerIntercept callback function set in the MultiHandler? instance. This responds to all 403 type status codes by invoking the PEPResultHandlerMiddleware.
  • ndg.security.common.authz.msi: improvements to the PDP, PIP and Response classes.
  • ndg.security.test.integration.dap: added integration test for secured pyDAP service
Line 
1"""SSL Client Authentication Middleware Module
2
3Apply to SSL client authentication to configured URL paths.
4
5SSL Client certificate is expected to be present in environ as SSL_CLIENT_CERT
6key as set by standard Apache SSL.
7
8NERC Data Grid Project
9
10"""
11__author__ = "P J Kershaw"
12__date__ = "11/12/08"
13__copyright__ = "(C) 2009 Science and Technology Facilities Council"
14__contact__ = "Philip.Kershaw@stfc.ac.uk"
15__revision__ = "$Id$"
16__license__ = "BSD - see top-level directory for LICENSE file"
17import logging
18log = logging.getLogger(__name__)
19import os
20import httplib
21
22from ndg.security.server.wsgi import NDGSecurityPathFilter
23from ndg.security.common.X509 import X509Stack, X509Cert, X509CertError
24
25class SSLClientAuthNMiddleware(NDGSecurityPathFilter):
26    '''Apply to SSL client authentication to configured URL paths.
27   
28    B{This class must be run under Apache mod_wsgi}
29
30    - SSL Client certificate is expected to be present in environ as
31    SSL_CLIENT_CERT key as set by Apache SSL with ExportCertData option to
32    SSLOptions directive enabled.'''
33
34    sslClientCertKeyName = 'SSL_CLIENT_CERT'
35   
36    propertyDefaults = {
37        'caCertFilePathList': []
38    }
39    propertyDefaults.update(NDGSecurityPathFilter.propertyDefaults)
40       
41    _isSSLClientCertSet = lambda self: bool(self.environ.get(
42                                SSLClientAuthNMiddleware.sslClientCertKeyName)) 
43    isSSLClientCertSet = property(fget=_isSSLClientCertSet,
44                                  doc="Check for client cert. set in environ")
45       
46    def _setCACertsFromFileList(self, caCertFilePathList):
47        '''Read CA certificates from file and add them to an X.509 Cert.
48        stack
49       
50        @type caCertFilePathList: list or tuple
51        @param caCertFilePathList: list of file paths for CA certificates to
52        be used to verify certificate used to sign message'''
53       
54        if isinstance(caCertFilePathList, basestring):
55            # Try parsing a space separated list of file paths
56            caCertFilePathList = caCertFilePathList.split()
57           
58        elif not isinstance(caCertFilePathList, (list, tuple)):
59            raise TypeError('Expecting a list or tuple for '
60                            '"caCertFilePathList"')
61
62        self._caCertStack = X509Stack()
63
64        for caCertFilePath in caCertFilePathList:
65            x509Cert = X509Cert.Read(os.path.expandvars(caCertFilePath))
66            self._caCertStack.push(x509Cert)
67       
68    caCertFilePathList = property(fset=_setCACertsFromFileList,
69                                  doc="list of CA certificate file paths - "
70                                      "peer certificate must validate against "
71                                      "one")
72     
73    @NDGSecurityMiddlewareBase.initCall         
74    def __call__(self, environ, start_response):
75       
76        log.debug("Calling SSLClientAuthNMiddleware.__call__ ...")
77       
78        if not self.isSSLRequest:
79            log.debug("ignoring request - assuming non-SSL")
80            return self._setResponse(environ, start_response)
81           
82        if not self.pathMatch:
83            log.debug("ignoring path [%s]" % self.path)
84            return self._setResponse(environ, start_response)
85                   
86        elif not self.isSSLClientCertSet:
87            log.error("No SSL Client path set for request to [%s]" % self.path)
88            return self._setErrorResponse(start_response=start_response,
89                                          msg='No client SSL Certificate set')
90           
91        if self.isValidClientCert(environ):           
92            return self._setResponse(environ, start_response)
93        else:
94            return self._setErrorResponse(start_response=start_response)
95           
96    def _setResponse(self, 
97                     environ, 
98                     start_response,
99                     notFoundMsg='No application set for '
100                                 'SSLClientAuthNMiddleware'):
101        return super(SSLClientAuthNMiddleware, self)._setResponse(environ, 
102                                                    start_response,
103                                                    notFoundMsg=notFoundMsg)
104
105    def _setErrorResponse(self, 
106                          start_response,
107                          msg='Invalid SSL client certificate'):
108        return super(SSLClientAuthNMiddleware, self)._setResponse(
109                                                start_response=start_response,
110                                                msg=msg,
111                                                code=self.errorResponseCode)
112
113    def isValidClientCert(self, environ):
114        sslClientCert = environ[SSLClientAuthNMiddleware.sslClientCertKeyName]
115        x509Cert = X509Cert.Parse(sslClientCert)
116       
117        if len(self._caCertStack) == 0:
118            log.warning("No CA certificates set for Client certificate "
119                        "signature verification")
120        else:
121            try:
122                self._caCertStack.verifyCertChain(x509Cert2Verify=x509Cert)
123
124            except X509CertError, e:
125                log.info("Client certificate verification failed: %s" % e)
126                return False
127           
128            except Exception, e:
129                log.error("Client certificate verification failed with "
130                          "unexpected error: %s" % e)
131                return False
132           
133        return True
Note: See TracBrowser for help on using the repository browser.