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

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

#941: Completed MyProxyClient egg

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