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

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

add openssl module.

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