source: TI12-security/trunk/MyProxyClient/myproxy/utils/openssl.py @ 6837

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/MyProxyClient/myproxy/utils/openssl.py@6837
Revision 6837, 8.5 KB checked in by pjkersha, 11 years ago (diff)

Fixing peer cert verification

  • Property svn:keywords set to Id
Line 
1"""OpenSSL utilities module - contains OpenSSLConfig class for
2parsing OpenSSL configuration files
3
4NERC Data Grid Project
5"""
6__author__ = "P J Kershaw"
7__date__ = "08/02/07"
8__copyright__ = "(C) 2009 Science and Technology Facilities Council"
9__license__ = """BSD - See LICENSE file in top-level directory"""
10__contact__ = "Philip.Kershaw@stfc.ac.uk"
11__revision__ = '$Id:openssl.py 4643 2008-12-15 14:53:53Z pjkersha $'
12import re
13import os
14from ConfigParser import SafeConfigParser
15
16
17class OpenSSLConfigError(Exception):
18    """Exceptions related to OpenSSLConfig class"""   
19
20
21class OpenSSLConfig(SafeConfigParser, object):
22    """Wrapper to OpenSSL Configuration file to allow extraction of
23    required distinguished name used for making certificate requests
24   
25    @type _certReqDNParamName: tuple
26    @cvar _certReqDNParamName: permissable keys for Distinguished Name
27    (not including CN which gets set separately).  This is used in _setReqDN
28    to check input
29   
30    @type _caDirPat: string
31    @cvar _caDirPat: sub-directory path to CA config directory
32    @type _gridCASubDir: string
33    @cvar _gridCASubDir: sub-directory of globus user for CA settings"""
34   
35    _certReqDNParamName = (
36        'countryName',
37        'C',
38        'stateOrProvinceName',
39        'ST', 
40        'localityName',
41        'L'
42        'organizationName',
43        'O',
44        'organizationalUnitName',
45        'OU',
46        'commonName',
47        'CN',
48        'emailAddress'
49    )
50       
51    _caDirPat = re.compile('\$dir')   
52    _gridCASubDir = os.path.join(".globus", "simpleCA")
53
54    def __init__(self, filePath=None, caDir=None):
55        """Initial OpenSSL configuration optionally setting a file path to
56        read from
57       
58        @type filePath: string       
59        @param filePath: path to OpenSSL configuration file
60       
61        @type caDir: string
62        @param caDir: directory for SimpleCA.  This is substituted for $dir
63        in OpenSSL config file where present.  caDir can be left out in
64        which case the substitution is not done"""
65       
66        SafeConfigParser.__init__(self)
67       
68        self._reqDN = None
69        self._setFilePath(filePath)
70
71        # Set-up CA directory
72        self.setCADir(caDir)
73
74           
75    def _setFilePath(self, filePath):
76        """Set property method
77        @type filePath: string
78        @param filePath: path for OpenSSL configuration file"""
79        if filePath is not None:
80            if not isinstance(filePath, basestring):
81                raise OpenSSLConfigError("Input OpenSSL config file path must "
82                                         "be a string")
83
84            try:
85                if not os.access(filePath, os.R_OK):
86                    raise OpenSSLConfigError("Not found or no read access")
87                                         
88            except Exception, e:
89                raise OpenSSLConfigError("OpenSSL config file path is not "
90                                         'valid: "%s": %s' % (filePath, e))
91                   
92        self._filePath = filePath
93                   
94    def _getFilePath(self):
95        """Get property method
96        @rtype: string
97        @return: file path for OpenSSL configuration file"""
98        return self.__filePath
99
100    filePath = property(fget=_getFilePath,
101                        fset=_setFilePath,
102                        doc="file path for configuration file")
103     
104    def setCADir(self, caDir):
105        """Set property method
106        @type caDir: string
107        @param caDir: path for OpenSSL configuration file"""
108        if caDir is None:
109            # Try to set default from 'HOME' env variable
110            homeDir = os.environ.get('HOME')
111            if homeDir:
112                self._caDir = os.path.join(homeDir, self._gridCASubDir)
113            else:
114                self._caDir = None
115        else:
116            if not isinstance(caDir, basestring):
117                raise OpenSSLConfigError("Input OpenSSL CA directory path "
118                                         "must be a string")
119
120            try:
121                if not os.access(caDir, os.R_OK):
122                    raise OpenSSLConfigError("Not found or no read access")
123                                         
124            except Exception, e:
125                raise OpenSSLConfigError("OpenSSL CA directory path is not "
126                                         'valid: "%s": %s' % (filePath, e))
127                   
128        self._caDir = caDir
129                   
130    def _getCADir(self):
131        """Get property method
132        @rtype caDir: string
133        @return caDir: directory path for CA configuration files"""
134        return self._caDir
135
136    caDir = property(fget=_getCADir,
137                     fset=setCADir,
138                     doc="directory path for CA configuration files")
139
140    def _getReqDN(self):
141        """Get property method
142        @rtype reqDN: dict
143        @return reqDN: Distinguished Name for certificate request"""
144        return self._reqDN
145
146    def _setReqDN(self, reqDN):
147        """Set property method
148        @type reqDN: dict
149        @param reqDN: Distinguished Name for certificate request"""
150        if not isinstance(reqDN, dict):
151            raise AttributeError("Distinguished Name must be dict type")
152       
153        invalidKw = [k for k in dict \
154                     if k not in OpenSSLConfig._certReqDNParamName]
155        if invalidKw:
156            raise AttributeError("Invalid certificate request keyword(s): "
157                                 "%s.  Valid keywords are: %s" % 
158                                 (', '.join(invalidKw), 
159                                 ', '.join(OpenSSLConfig._certReqDNParamName)))
160
161        self._reqDN = reqDN
162
163    reqDN = property(fget=_getReqDN,
164                     fset=_setReqDN,
165                     doc="Distinguished Name for certificate request")
166   
167    def read(self):
168        """Override base class version to avoid parsing error with the first
169        'RANDFILE = ...' part of the openssl file.  Also, reformat _sections
170        to allow for the style of SSL config files where section headings can
171        have spaces either side of the brackets e.g.
172        [ sectionName ]
173       
174        and comments can occur on the same line as an option e.g.
175        option = blah # This is option blah
176       
177        Reformat _sections to """
178        try:
179            file = open(self._filePath)
180            fileTxt = file.read()
181        except Exception, e:
182            raise OpenSSLConfigError('Reading OpenSSL config file "%s": %s' % 
183                                                    (self._filePath, e))
184
185        idx = re.search('\[\s*\w*\s*\]', fileTxt).span()[0]
186        file.seek(idx)
187        SafeConfigParser.readfp(self, file)
188       
189        # Filter section names and remove comments from options
190        for section, val in self._sections.items():
191            newSection = section
192            self._sections[newSection.strip()] = \
193                                    dict([(opt, self._filtOptVal(optVal))
194                                          for opt, optVal in val.items()])
195            del self._sections[section]
196       
197        self._setReqDN()
198
199    def _filtOptVal(self, optVal):
200        """For option value, filter out comments and substitute $dir with
201        the CA directory location
202       
203        @type optVal: string
204        @param optVal: option value"""
205        filtVal = optVal.split('#')[0].strip()
206        if self._caDir:
207            # Replace $dir with CA directory path
208            return OpenSSLConfig._caDirPat.sub(self._caDir, filtVal)
209        else:
210            # Leave $dir in place as no CA directory has been set
211            return filtVal   
212
213    def readfp(self, fp):
214        """Set to not implemented as using a file object could be problematic
215        given read() has to seek ahead to the first actual section to avoid
216        parsing errors"""
217        raise NotImplementedError("Use read method instead")
218
219    def _setReqDN(self):
220        """Set Required DN parameters from the configuration file returning
221        them in a dictionary"""
222       
223        # Nb. Match over line boundaries
224        try:
225            self._reqDN = {
226                'O': self.get('req_distinguished_name', 
227                              '0.organizationName_default'),
228                'OU': self.get('req_distinguished_name', 
229                               '0.organizationalUnitName_default')
230            }
231        except Exception, e:
232            raise OpenSSLConfigError('Setting content of Distinguished Name '
233                                     'from file "%s": %s' % (self._filePath, e))
Note: See TracBrowser for help on using the repository browser.