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
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) 2007 STFC & NERC"
9__license__ = \
10"""This software may be distributed under the terms of the Q Public
11License, version 1.0 or later."""
12__contact__ = "Philip.Kershaw@stfc.ac.uk"
13__revision__ = '$Id:openssl.py 4643 2008-12-15 14:53:53Z pjkersha $'
14
15import re
16import os
17from ConfigParser import SafeConfigParser
18from M2Crypto.X509 import X509_Name
19
20
21class OpenSSLConfigError(Exception):
22    """Exceptions related to OpenSSLConfig class"""   
23
24
25class OpenSSLConfig(SafeConfigParser, object):
26    """Wrapper to OpenSSL Configuration file to allow extraction of
27    required distinguished name used for making certificate requests
28   
29    @type _certReqDNParamName: tuple
30    @cvar _certReqDNParamName: permissable keys for Distinguished Name
31    (not including CN which gets set separately).  This is used in _setReqDN
32    to check input
33   
34    @type _caDirPat: string
35    @cvar _caDirPat: sub-directory path to CA config directory
36    @type _gridCASubDir: string
37    @cvar _gridCASubDir: sub-directory of globus user for CA settings"""
38   
39    _certReqDNParamName = X509_Name.nid.keys()   
40    _caDirPat = re.compile('\$dir')   
41    _gridCASubDir = os.path.join(".globus", "simpleCA")
42
43   
44    def __init__(self, filePath=None, caDir=None):
45        """Initial OpenSSL configuration optionally setting a file path to
46        read from
47       
48        @type filePath: string       
49        @param filePath: path to OpenSSL configuration file
50       
51        @type caDir: string
52        @param caDir: directory for SimpleCA.  This is substituted for $dir
53        in OpenSSL config file where present.  caDir can be left out in
54        which case the substitution is not done"""
55       
56        SafeConfigParser.__init__(self)
57       
58        self._reqDN = None
59        self._setFilePath(filePath)
60
61        # Set-up CA directory
62        self.setCADir(caDir)
63
64           
65    def _setFilePath(self, filePath):
66        """Set property method
67        @type filePath: string
68        @param filePath: path for OpenSSL configuration file"""
69        if filePath is not None:
70            if not isinstance(filePath, basestring):
71                raise OpenSSLConfigError("Input OpenSSL config file path must "
72                                         "be a string")
73
74            try:
75                if not os.access(filePath, os.R_OK):
76                    raise OpenSSLConfigError("Not found or no read access")
77                                         
78            except Exception, e:
79                raise OpenSSLConfigError("OpenSSL config file path is not "
80                                         'valid: "%s": %s' % (filePath, e))
81                   
82        self._filePath = filePath
83                   
84
85    def _getFilePath(self):
86        """Get property method
87        @rtype: string
88        @return: file path for OpenSSL configuration file"""
89        return self.__filePath
90
91    filePath = property(fget=_getFilePath,
92                        fset=_setFilePath,
93                        doc="file path for configuration file")
94
95           
96    def setCADir(self, caDir):
97        """Set property method
98        @type caDir: string
99        @param caDir: path for OpenSSL configuration file"""
100        if caDir is None:
101            # Try to set default from 'HOME' env variable
102            homeDir = os.environ.get('HOME')
103            if homeDir:
104                self._caDir=os.path.join(os.environ['HOME'],self._gridCASubDir)
105            else:
106                self._caDir = None
107        else:
108            if not isinstance(caDir, basestring):
109                raise OpenSSLConfigError("Input OpenSSL CA directory path "
110                                         "must be a string")
111
112            try:
113                if not os.access(caDir, os.R_OK):
114                    raise OpenSSLConfigError("Not found or no read access")
115                                         
116            except Exception, e:
117                raise OpenSSLConfigError("OpenSSL CA directory path is not "
118                                         'valid: "%s": %s' % (filePath, e))
119                   
120        self._caDir = caDir
121                   
122
123    def _getCADir(self):
124        """Get property method
125        @rtype caDir: string
126        @return caDir: directory path for CA configuration files"""
127        return self._caDir
128
129    caDir = property(fget=_getCADir,
130                     fset=setCADir,
131                     doc="directory path for CA configuration files")
132
133
134    def _getReqDN(self):
135        """Get property method
136        @rtype reqDN: dict
137        @return reqDN: Distinguished Name for certificate request"""
138        return self._reqDN
139
140
141    def _setReqDN(self, reqDN):
142        """Set property method
143        @type reqDN: dict
144        @param reqDN: Distinguished Name for certificate request"""
145        if not isinstance(reqDN, dict):
146            raise AttributeError("Distinguished Name must be dict type")
147       
148        invalidKw = [k for k in dict \
149                     if k not in OpenSSLConfig._certReqDNParamName]
150        if invalidKw:
151            raise AttributeError("Invalid certificate request keyword(s): "
152                                 "%s.  Valid keywords are: %s" % 
153                                 (', '.join(invalidKw), 
154                                 ', '.join(OpenSSLConfig._certReqDNParamName)))
155
156        self._reqDN = reqDN
157
158
159    reqDN = property(fget=_getReqDN,
160                     fset=_setReqDN,
161                     doc="Distinguished Name for certificate request")
162   
163    def read(self):
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 """
174        try:
175            file = open(self._filePath)
176            fileTxt = file.read()
177        except Exception, e:
178            raise OpenSSLConfigError('Reading OpenSSL config file "%s": %s' % 
179                                                    (self._filePath, e))
180
181        idx = re.search('\[\s*\w*\s*\]', fileTxt).span()[0]
182        file.seek(idx)
183        SafeConfigParser.readfp(self, file)
184       
185        # Filter section names and remove comments from options
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
198        the CA directory location
199       
200        @type optVal: string
201        @param optVal: option value"""
202        filtVal = optVal.split('#')[0].strip()
203        if self._caDir:
204            # Replace $dir with CA directory path
205            return OpenSSLConfig._caDirPat.sub(self._caDir, filtVal)
206        else:
207            # Leave $dir in place as no CA directory has been set
208            return filtVal
209       
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"""
215        raise NotImplementedError("Use read method instead")
216
217
218    def _setReqDN(self):
219        """Set Required DN parameters from the configuration file returning
220        them in a dictionary"""
221       
222        # Nb. Match over line boundaries
223        try:
224            self._reqDN = {
225                'O': self.get('req_distinguished_name', 
226                              '0.organizationName_default'),
227                'OU': self.get('req_distinguished_name', 
228                               '0.organizationalUnitName_default')
229            }
230        except Exception, e:
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.