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

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.server/ndg/security/server/wsgi/ssl.py@4863
Revision 4863, 5.4 KB checked in by pjkersha, 11 years ago (diff)
  • added initialisation decorator for use with derived classes if ndg.security.server.wsgi.NDGSecurityMiddlewareBase.call
  • fixed SSO Client Middleware - client interface for using Single Sign On Service from Pylons or other app
  • Added capability to pass in args to class to be instantiated from instantiateClass factory function in classfactory module
  • Modified SSO wayf template to enable login from this page if the user is at their home site - saves an extra login step
  • Added Signin interface plugin for OpenID Relying Party middleware. The plugin is itself middleware so that it can include other middleware filters such StaticURLParser to include static content used in template.
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(environ, 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(environ, 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                          environ, 
107                          start_response,
108                          msg='Invalid SSL client certificate'):
109        return super(SSLClientAuthNMiddleware, self)._setResponse(environ, 
110                                                   start_response,
111                                                   msg=msg,
112                                                   code=self.errorResponseCode)
113
114    def isValidClientCert(self, environ):
115        sslClientCert = environ[SSLClientAuthNMiddleware.sslClientCertKeyName]
116        x509Cert = X509Cert.Parse(sslClientCert)
117       
118        if len(self._caCertStack) == 0:
119            log.warning("No CA certificates set for Client certificate "
120                        "signature verification")
121        else:
122            try:
123                self._caCertStack.verifyCertChain(x509Cert2Verify=x509Cert)
124
125            except X509CertError, e:
126                log.info("Client certificate verification failed: %s" % e)
127                return False
128           
129            except Exception, e:
130                log.error("Client certificate verification failed with "
131                          "unexpected error: %s" % e)
132                return False
133           
134        return True
Note: See TracBrowser for help on using the repository browser.