source: TI12-security/trunk/python/ndg.security.common/ndg/security/common/m2CryptoSSLUtility.py @ 2931

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.common/ndg/security/common/m2CryptoSSLUtility.py@2931
Revision 2931, 7.6 KB checked in by pjkersha, 13 years ago (diff)

Updated Attribute Authority adding new info to the map configuration:

  • The DN of Attribute Authority cert
  • the cert DN of the Login Service https server
  • the cert DN for the https server making REQUESTS to the Login Service. This enables the latter to validate requests and prevent phishing attacks.

Also added a new WSDL operation getAllHostsInfo. This combines getHostInfo and getTrustedHostInfo simplifying code for the WAYF.

ndg.security.common/ndg/security/common/m2CryptoSSLUtility.py: HostCheck? class can now accept multiple peerCertDNs to validate against.

  • Property svn:keywords set to Id
Line 
1"""Extend M2Crypto SSL functionality for cert verification and custom
2timeout settings.
3
4NERC Data Grid Project"""
5__author__ = "P J Kershaw"
6__date__ = "02/07/07"
7__copyright__ = "(C) 2007 STFC & NERC"
8__license__ = \
9"""This software may be distributed under the terms of the Q Public
10License, version 1.0 or later."""
11__contact__ = "P.J.Kershaw@rl.ac.uk"
12__revision__ = '$Id$'
13
14import httplib
15import socket
16
17from M2Crypto import SSL, X509
18from M2Crypto.httpslib import HTTPSConnection as _HTTPSConnection
19
20from ndg.security.common.X509 import X509Cert, X509Stack
21
22class InvalidCertSignature(SSL.Checker.SSLVerificationError):
23    """Raise if verification against CA cert public key fails"""
24   
25
26class HostCheck(SSL.Checker.Checker, object):
27    """Override SSL.Checker.Checker to enable alternate Common Name
28    setting match for peer cert"""
29
30    def __init__(self, 
31                 peerCertDN=None, 
32                 peerCertCN=None, 
33                 caCertList=[],
34                 caCertFilePathList=[], 
35                 **kw):
36        """Override parent class __init__ to enable setting of myProxyServerDN
37        setting
38       
39        @type peerCertDN: string/list
40        @param peerCertDN: Set the expected Distinguished Name of the
41        server to avoid errors matching hostnames.  This is useful
42        where the hostname is not fully qualified.  Alternatively set a list
43        of acceptable DNs.  This enables validation where the expected DN is
44        could be one a number of different identities.
45       
46        @type peerCertCN: string
47        @param peerCertCN: enable alternate Common Name to peer
48        hostname
49       
50        @type caCertList: list type of M2Crypto.X509.X509 types
51        @param caCertList: CA X.509 certificates - if set the peer cert's
52        CA signature is verified against one of these.  At least one must
53        verify
54       
55        @type caCertFilePathList: list string types
56        @param caCertFilePathList: same as caCertList except input as list
57        of CA cert file paths"""
58       
59        SSL.Checker.Checker.__init__(self, **kw)
60       
61        self.peerCertDN = peerCertDN
62        self.peerCertCN = peerCertCN
63        if caCertList:
64            self.caCertList = caCertList
65        elif caCertFilePathList:
66            self.caCertFilePathList = caCertFilePathList
67           
68       
69    def __call__(self, peerCert, host=None):
70        """Carry out checks on server ID
71        @param peerCert: MyProxy server host certificate as M2Crypto.X509.X509
72        instance
73        @param host: name of host to check
74        """
75       
76        try:
77            SSL.Checker.Checker.__call__(self, peerCert, host=self.peerCertCN)
78           
79        except SSL.Checker.WrongHost, e:
80            # Try match against peerCertDN set
81            # file setting
82            peerCertDN='/'+peerCert.get_subject().as_text().replace(', ', '/')
83           
84            if isinstance(self.peerCertDN, list):
85                # At least one match should be found in the list
86                if not len([dn for dn in self.peerCertDN if peerCertDN==dn]):
87                    raise e
88            else:
89                if peerCertDN != self.peerCertDN:
90                    raise e
91
92        if len(self.__caCertStack) > 0:
93            try:
94                self.__caCertStack.verifyCertChain(\
95                           x509Cert2Verify=X509Cert(m2CryptoX509=peerCert))
96            except Exception, e:
97                raise InvalidCertSignature, \
98            "Peer certificate verification against CA cert failed: "+str(e) 
99             
100        # They match - drop the exception and return all OK instead         
101        return True
102   
103   
104    def __setCACertList(self, caCertList):
105        """Set list of CA certs - peer cert must validate against at least one
106        of these"""
107        self.__caCertStack = X509Stack()
108        for caCert in caCertList:
109            self.__caCertStack.push(caCert)
110
111    caCertList = property(fset=__setCACertList,
112              doc="list of CA certs - peer cert must validate against one")
113
114
115    #_________________________________________________________________________
116    def __setCACertsFromFileList(self, caCertFilePathList):
117        '''Read CA certificates from file and add them to the X.509
118        stack
119       
120        @type caCertFilePathList: list or tuple
121        @param caCertFilePathList: list of file paths for CA certificates to
122        be used to verify certificate used to sign message'''
123       
124        if not isinstance(caCertFilePathList, list) and \
125           not isinstance(caCertFilePathList, tuple):
126            raise AttributeError, \
127                        'Expecting a list or tuple for "caCertFilePathList"'
128
129        self.__caCertStack = X509Stack()
130
131        for caCertFilePath in caCertFilePathList:
132            self.__caCertStack.push(X509.load_cert(caCertFilePath))
133       
134    caCertFilePathList = property(fset=__setCACertsFromFileList,
135    doc="list of CA cert file paths - peer cert must validate against one")
136
137
138class HTTPSConnection(_HTTPSConnection):
139    """Modified version of M2Crypto equivalent to enable custom checks with
140    the peer and timeout settings
141   
142    @type defReadTimeout: M2Crypto.SSL.timeout
143    @cvar defReadTimeout: default timeout for read operations
144    @type defWriteTimeout: M2Crypto.SSL.timeout
145    @cvar defWriteTimeout: default timeout for write operations"""   
146    defReadTimeout = SSL.timeout(sec=20.)
147    defWriteTimeout = SSL.timeout(sec=20.)
148   
149    def __init__(self, *args, **kw):
150        '''Overload to enable setting of post connection check
151        callback to SSL.Connection
152       
153        type *args: tuple
154        param *args: args which apply to M2Crypto.httpslib.HTTPSConnection
155        type **kw: dict
156        param **kw: additional keywords
157        @type postConnectionCheck: SSL.Checker.Checker derivative
158        @keyword postConnectionCheck: set class for checking peer
159        @type readTimeout: M2Crypto.SSL.timeout
160        @keyword readTimeout: readTimeout - set timeout for read
161        @type writeTimeout: M2Crypto.SSL.timeout
162        @keyword writeTimeout: similar to read timeout'''
163       
164        if 'postConnectionCheck' in kw:
165            self._postConnectionCheck = kw['postConnectionCheck']
166            del kw['postConnectionCheck']
167        else:
168            self._postConnectionCheck = SSL.Checker.Checker
169       
170        if 'readTimeout' in kw:
171            if not isinstance(readTimeout, SSL.timeout):
172                raise AttributeError, "readTimeout must be of type " + \
173                                      "M2Crypto.SSL.timeout" 
174            self.readTimeout = readTimeout
175            del kw['readTimeout']
176        else:
177            self.readTimeout = HTTPSConnection.defReadTimeout
178             
179        if 'writeTimeout' in kw:
180            if not isinstance(writeTimeout, SSL.timeout):
181                raise AttributeError, "writeTimeout must be of type " + \
182                                      "M2Crypto.SSL.timeout" 
183            self.writeTimeout = writeTimeout
184            del kw['writeTimeout']
185        else:
186            self.writeTimeout = HTTPSConnection.defWriteTimeout
187           
188        _HTTPSConnection.__init__(self, *args, **kw)
189       
190       
191    def connect(self):
192        '''Overload M2Crypto.httpslib.HTTPSConnection to enable
193        custom post connection check of peer certificate and socket timeout'''
194        self.sock = SSL.Connection(self.ssl_ctx)
195        self.sock.set_post_connection_check_callback(
196                                                 self._postConnectionCheck)
197
198        self.sock.set_socket_read_timeout(self.readTimeout)
199        self.sock.set_socket_write_timeout(self.writeTimeout)
200
201        self.sock.connect((self.host, self.port))
Note: See TracBrowser for help on using the repository browser.