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

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

Updates to licence info and README for unit tests.

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