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

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

#941: MyProxyClient egg

  • re-arranging main package structure.
  • 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$'
14
15import re, os
16from ConfigParser import SafeConfigParser
17from M2Crypto.X509 import X509_Name
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
84    def _getFilePath(self):
85        """Get property method
86        @rtype: string
87        @return: file path for OpenSSL configuration file"""
88        return self.__filePath
89
90    filePath = property(fget=_getFilePath,
91                        fset=_setFilePath,
92                        doc="file path for configuration file")
93
94           
95    def setCADir(self, caDir):
96        """Set property method
97        @type caDir: string
98        @param caDir: path for OpenSSL configuration file"""
99        if caDir is None:
100            # Try to set default from 'HOME' env variable
101            homeDir = os.environ.get('HOME')
102            if homeDir:
103                self._caDir=os.path.join(os.environ['HOME'],self._gridCASubDir)
104            else:
105                self._caDir = None
106        else:
107            if not isinstance(caDir, basestring):
108                raise OpenSSLConfigError("Input OpenSSL CA directory path "
109                                         "must be a string")
110
111            try:
112                if not os.access(caDir, os.R_OK):
113                    raise OpenSSLConfigError("Not found or no read access")
114                                         
115            except Exception, e:
116                raise OpenSSLConfigError("OpenSSL CA directory path is not "
117                                         'valid: "%s": %s' % (filePath, e))
118                   
119        self._caDir = caDir
120                   
121
122    def _getCADir(self):
123        """Get property method
124        @rtype caDir: string
125        @return caDir: directory path for CA configuration files"""
126        return self._caDir
127
128    caDir = property(fget=_getCADir,
129                     fset=setCADir,
130                     doc="directory path for CA configuration files")
131
132
133    def _getReqDN(self):
134        """Get property method
135        @rtype reqDN: dict
136        @return reqDN: Distinguished Name for certificate request"""
137        return self._reqDN
138
139
140    def _setReqDN(self, reqDN):
141        """Set property method
142        @type reqDN: dict
143        @param reqDN: Distinguished Name for certificate request"""
144        if not isinstance(reqDN, dict):
145            raise AttributeError("Distinguished Name must be dict type")
146       
147        invalidKw = [k for k in dict \
148                     if k not in OpenSSLConfig._certReqDNParamName]
149        if invalidKw:
150            raise AttributeError("Invalid certificate request keyword(s): "
151                                 "%s.  Valid keywords are: %s" % 
152                                 (', '.join(invalidKw), 
153                                 ', '.join(OpenSSLConfig._certReqDNParamName)))
154
155        self._reqDN = reqDN
156
157
158    reqDN = property(fget=_getReqDN,
159                     fset=_setReqDN,
160                     doc="Distinguished Name for certificate request")
161   
162    def read(self):
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 """
173        try:
174            file = open(self._filePathfilePath)
175            fileTxt = file.read()
176        except Exception, e:
177            raise OpenSSLConfigError('Reading OpenSSL config file "%s": %s' % 
178                                                    (self._filePath, e))
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
197        the CA directory location
198       
199        @type optVal: string
200        @param optVal: option value"""
201        filtVal = optVal.split('#')[0].strip()
202        if self._caDir:
203            # Replace $dir with CA directory path
204            return OpenSSLConfig._caDirPat.sub(self._caDir, filtVal)
205        else:
206            # Leave $dir in place as no CA directory has been set
207            return filtVal
208       
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"""
214        raise NotImplementedError("Use read method instead")
215
216
217    def _setReqDN(self):
218        """Set Required DN parameters from the configuration file returning
219        them in a dictionary"""
220       
221        # Nb. Match over line boundaries
222        try:
223            self._reqDN = {
224                'O': self.get('req_distinguished_name', 
225                              '0.organizationName_default'),
226                'OU': self.get('req_distinguished_name', 
227                               '0.organizationalUnitName_default')
228            }
229        except Exception, e:
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.