source: TI12-security/trunk/python/ndg.security.common/ndg/security/common/openssl.py @ 2735

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

setup.py,
ndg.security.client/setup.py,
ndg.security.server/setup.py,
ndg.security.test/setup.py,
ndg.security.common/setup.py:

  • incremented to 0.8.3 for latest NDG beta release.

setup.cfg,
ndg.security.client/setup.cfg,
ndg.security.server/setup.cfg,
ndg.security.test/setup.cfg,
ndg.security.common/setup.cfg:

  • changed back to _beta release for DEWS

ndg.security.common/ndg/security/common/openssl.py:

  • OpenSSLConfig._filtOptVal - only apply CA dir substitution for $dir if CA

dir is set.

Line 
1"""OpenSSL utilities module - contains OpenSSLConfig class for
2parsing OpenSSL configuration files
3
4NERC Data Grid Project
5
6@author P J Kershaw 08/02/07
7
8@copyright (C) 2007 CCLRC & NERC
9
10@license This software may be distributed under the terms of the Q Public
11License, version 1.0 or later.
12"""
13__revision__ = '$Id:$'
14
15import re, os
16from ConfigParser import SafeConfigParser
17
18#_____________________________________________________________________________       
19class OpenSSLConfigError(Exception):
20    """Exceptions related to OpenSSLConfig class"""   
21
22
23#_____________________________________________________________________________       
24class OpenSSLConfig(SafeConfigParser, object):
25    """Wrapper to OpenSSL Configuration file to allow extraction of
26    required distinguished name used for making certificate requests
27   
28    @type _certReqDNParamName: tuple
29    @cvar _certReqDNParamName: permissable keys for Distinguished Name
30    (not including CN which gets set separately).  This is used in __setReqDN
31    to check input
32   
33    @type _caDirPat: string
34    @cvar _caDirPat: sub-directory path to CA config directory
35    @type __gridCASubDir: string
36    @cvar __gridCASubDir: sub-directory of globus user for CA settings"""
37   
38    _certReqDNParamName = ('O', 'OU', '0.organizationName',
39                            '0.organizationalUnitName')
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        @keyword filePath: path to OpenSSL configuration file"""
52       
53        SafeConfigParser.__init__(self)
54       
55        self.__reqDN = None
56        self.__setFilePath(filePath)
57
58        # Set-up CA directory
59        self.setCADir(caDir)
60
61           
62    def __setFilePath(self, filePath):
63        """Set property method
64        @type filePath: string
65        @param filePath: path for OpenSSL configuration file"""
66        if filePath is not None:
67            if not isinstance(filePath, basestring):
68                raise OpenSSLConfigError, \
69                    "Input OpenSSL config file path must be a string"
70
71            try:
72                if not os.access(filePath, os.R_OK):
73                    raise OpenSSLConfigError, "not found or no read access"
74                                         
75            except Exception, e:
76                raise OpenSSLConfigError, \
77                    "OpenSSL config file path is not valid: \"%s\": %s" % \
78                    (filePath, str(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'], 
104                                            self.__gridCASubDir)
105            else:
106                self.__caDir = None
107        else:
108            if not isinstance(caDir, basestring):
109                raise OpenSSLConfigError, \
110                    "Input OpenSSL CA directory path 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, \
118                    "OpenSSL CA directory path is not valid: \"%s\": %s" % \
119                    (filePath, str(e))
120                   
121        self.__caDir = caDir
122                   
123
124    def __getCADir(self):
125        """Get property method
126        @rtype caDir: string
127        @return caDir: directory path for CA configuration files"""
128        return self.__caDir
129
130    caDir = property(fget=__getCADir,
131                     fset=setCADir,
132                     doc="directory path for CA configuration files")
133
134
135    def __getReqDN(self):
136        """Get property method
137        @rtype reqDN: dict
138        @return reqDN: Distinguished Name for certificate request"""
139        return self.__reqDN
140
141
142    def __setReqDN(self, reqDN):
143        """Set property method
144        @type reqDN: dict
145        @param reqDN: Distinguished Name for certificate request"""
146        if not isinstance(reqDN, dict):
147            raise AttributeError, "Distinguished Name must be dict type"
148       
149        invalidKw = [k for k in dict \
150                     if k not in self.__class__._certReqDNParamName]
151        if invalidKw:
152            raise AttributeError, \
153    "Invalid certificate request keyword(s): %s.  Valid keywords are: %s" % \
154    (', '.join(invalidKw), ', '.join(self.__class__._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, \
179                "Error reading OpenSSL config file \"%s\": %s" % \
180                                                    (self.__filePath, str(e))
181
182        idx = re.search('\[\s*\w*\s*\]', fileTxt).span()[0]
183        file.seek(idx)
184        SafeConfigParser.readfp(self, file)
185       
186        # Filter section names and reomve comments from options
187        for section, val in self._sections.items():
188            newSection = section
189            self._sections[newSection.strip()] = \
190                                    dict([(opt, self._filtOptVal(optVal))
191                                          for opt, optVal in val.items()])
192            del self._sections[section]
193       
194        self._setReqDN()
195
196   
197    def _filtOptVal(self, optVal):
198        """For option value, filter out comments and substitute $dir with
199        the CA directory location
200       
201        @type optVal: string
202        @param optVal: option value"""
203        filtVal = optVal.split('#')[0].strip()
204        if self.__caDir:
205            # Replace $dir with CA directory path
206            return self.__class__._caDirPat.sub(self.__caDir, filtVal)
207        else:
208            # Leave $dir in place as no CA directory has been set
209            return filtVal
210       
211
212    def readfp(self, fp):
213        """Set to not implemented as using a file object could be problematic
214        given read() has to seek ahead to the first actual section to avoid
215        parsing errors"""
216        raise NotImplementedError, "Use read method instead"
217        self._parseReqDN()
218
219
220    def _setReqDN(self):
221        """Set Required DN parameters from the configuration file returning
222        them in a dictionary"""
223       
224        # Nb. Match over line boundaries
225        try:
226            self.__reqDN = \
227            {
228                'O': self.get('req_distinguished_name', 
229                              '0.organizationName_default'),
230                'OU': self.get('req_distinguished_name', 
231                               '0.organizationalUnitName_default')
232            }
233        except Exception, e:
234            raise OpenSSLConfigError, \
235            'Error setting content of Distinguished Name from file "%s": %s'%\
236                                                    (self.__filePath, str(e))
Note: See TracBrowser for help on using the repository browser.