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

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

#941: MyProxyClient egg: openssl module moved into myproxy.client.utils

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