1
#######################################################################
3
# (c) by Michael Stroeder, michael.stroeder@propack-data.de
4
########################################################################
5
# Modul fuer den Zugriff auf die SSLeay-Konfigurationsdatei
7
########################################################################
9
########################################################################
10
# Ab hier gibt es nix mehr zu konfigurieren
11
########################################################################
13
import sys,types,string,re,charset
17
########################################################################
18
# Funktion GetAllSections()
22
# Dictionary mit Abschnittsnamen als Index und Dictionaries der
23
# einzelnen Abschnitte als Feldelemente
24
# {}, falls Konfigurationsdatei leer
26
# Fuer Konfigurationseintraege ausserhalb eines Abschnittes wird der
27
# Pseudo-Abschnittsnamen '_' gesetzt.
28
########################################################################
30
def GetAllSections(filename):
35
# parameters not within a section will be stored in dummy section "_"
38
# regular exp for a line defining a section name
39
issection_re=re.compile(r'^\s*\[\s*\w+\s*\]\s*(#)*.*')
41
# regular exp for extracting a section name in a section line
42
section_name_re=re.compile(r'\[\s*\w+\s*\]')
44
# regular exp for a syntactically correct line defining a parameter
45
# e.g. name = "parameter1",parameter2 # comment
46
isparamline_re=re.compile(r'^(\d+.)*[\w_]+\s*=.*[\s#]*.*')
48
# regular exp for extracting a parameter [num.]name in a parameter line
49
paramline_numname_re=re.compile(r'(\d+.)*[\w_]+')
51
# regular exp for extracting all parameter values from value incl. comment part
52
onevalue_regex = r'(�.*?�|".*?"|[^:^,^#]*[^:^,^#^\s]|[^:^,^#]+:([^:^,^#]*[^:^,^#^\s]+|".*?"|�.*?�))'
53
paramline_valuepart_re=re.compile(onevalue_regex+r'+(,'+onevalue_regex+')*\s*(#)*')
55
# regular exp for splitting all values from value part
56
paramline_valuesplit_re=re.compile('%s' % (onevalue_regex))
58
# regular exp for testing if param is quoted
59
isquoted_re=re.compile('(%s|%s|%s)' % ('�.*�','".*"',"'.*'"))
62
cnf_file = open(filename,'r')
64
# Read first line from file
65
line = cnf_file.readline()
69
line = string.strip(line)
70
# sys.stderr.write('***%s\n' % (line))
71
if issection_re.search(line)!=None:
73
# Section found => new section in result dict
75
# extract plain section name by searching [ aphanum-string ]
76
# and stripping "[", "]" and white-spaces
77
section_name = string.strip(section_name_re.search(line).group(0)[1:-1])
78
# Create new sub-dict. If there are multiple sections (broken cnf-file)
79
# with the same name, the last one is valid.
81
result[section_name]={}
83
elif isparamline_re.search(line)!=None:
85
# Is valid parameter line
87
# Extract parameter name
88
name = paramline_numname_re.search(line).group(0)
89
# Extract parameter num.name
91
num,name = string.split(name,'.',1)
95
# extract plain value part
96
# and strip "=", "#" and white-spaces
97
line = string.strip(line[string.index(line,'=')+1:])
99
valuepart = string.strip(paramline_valuepart_re.match(line).group(0))
100
valuegroups = paramline_valuesplit_re.findall(valuepart)
105
# sys.stderr.write('***valuegroups=%s\n' % (valuegroups))
106
if len(valuegroups)>1:
107
result[section_name][name] = []
108
for valuetuple in valuegroups:
109
if isquoted_re.search(valuetuple[0]):
110
value = valuetuple[0][1:-1]
112
value = valuetuple[0]
113
# New entry in current section
114
keys[section_name].append(name)
115
# Store value of entry in dict
116
value = string.strip(value)
118
result[section_name][name].append(value)
119
elif len(valuegroups)==1:
120
valuetuple = valuegroups[0]
121
if isquoted_re.search(valuetuple[0]):
122
value = valuetuple[0][1:-1]
124
value = valuetuple[0]
125
# New entry in current section
126
keys[section_name].append(name)
127
# Store value of entry in dict
128
result[section_name][name] = string.strip(value)
130
# Read next line from file
131
line = cnf_file.readline()
138
########################################################################
139
# Objektklasse fuer eine CA-Definition
140
########################################################################
147
# returns 1 if the certificates of CA ca_name are
148
# client certificates (depending on keyUsage and nsCertType).
149
def isclientcert(self):
150
if self.basicConstraints and self.basicConstraints=='CA:true':
153
if type(self.nsCertType)==types.ListType:
155
for i in self.nsCertType:
156
isClientCert = isClientCert or (i in ['email','client','objsign'])
158
isClientCert = self.nsCertType in ['email','client','objsign']
163
# returns 1 if the certificates of CA ca_name are usable for
164
# email (depending on keyUsage and nsCertType).
165
def isemailcert(self):
166
if self.basicConstraints and self.basicConstraints=='CA:true':
168
return (type(self.nsCertType)==types.ListType and ('email' in self.nsCertType)) or \
169
(self.nsCertType=='email') or \
170
(self.nsCertType=='')
172
# returns 1 if the certificates of CA ca_name are usable for
173
# email (depending on keyUsage and nsCertType).
174
def isservercert(self):
175
if self.basicConstraints and self.basicConstraints=='CA:true':
177
return (type(self.nsCertType)==types.ListType and ('server' in self.nsCertType)) or \
178
(self.nsCertType=='server') or \
179
(self.nsCertType=='')
181
########################################################################
182
# Objektklasse fuer eine Konfigurationsdatei
183
########################################################################
185
class OpenSSLConfigClass:
187
def __init__(self,pathname):
188
self.sectionkeys,self.data = GetAllSections(pathname)
190
# FIX ME!!! Look for Netscape specs about key usage determination.
192
# Build tree with CA hierarchy
195
ca_names = self.sectionkeys.get('ca',[])
196
for ca_name in ca_names:
197
signedby = self.data[self.data['ca'][ca_name]].get('signedby','')
199
if catree.has_key(signedby):
200
catree[signedby].append(ca_name)
202
catree[signedby]=[ca_name]
204
catree['.'].append(ca_name)
207
# Get all relevant data of a CA definition and its subsequent sections
208
def getcadata(self,ca_name):
210
pyca_section = self.data.get('pyca',{})
211
ca.sectionname = self.data['ca'][ca_name]
212
ca_section = self.data[ca.sectionname]
214
ca.dir = ca_section.get('dir','')
215
ca.serial = string.replace(ca_section.get('serial','$dir/serial'),'$dir',ca.dir)
216
ca.certificate = string.replace(ca_section.get('certificate','$dir/cacert.pem'),'$dir',ca.dir)
217
ca.private_key = string.replace(ca_section.get('private_key','$dir/private/cakey.pem'),'$dir',ca.dir)
218
ca.database = string.replace(ca_section.get('database','$dir/index.txt'),'$dir',ca.dir)
219
ca.pend_reqs_dir = string.replace(ca_section.get('pend_reqs_dir','$dir/pendreqs'),'$dir',ca.dir)
220
ca.crl = string.replace(ca_section.get('crl','$dir/crl.pem'),'$dir',ca.dir)
221
ca.crl_dir = string.replace(ca_section.get('crl_dir','$dir/crl'),'$dir',ca.dir)
222
ca.new_reqs_dir = string.replace(ca_section.get('new_reqs_dir','$dir/newreqs'),'$dir',ca.dir)
223
ca.old_reqs_dir = string.replace(ca_section.get('old_reqs_dir','$dir/oldreqs'),'$dir',ca.dir)
224
ca.new_certs_dir = string.replace(ca_section.get('new_certs_dir','$dir/newcerts'),'$dir',ca.dir)
225
ca.certs = string.replace(ca_section.get('certs','$dir/certs'),'$dir',ca.dir)
226
ca.req = ca_section.get('req','req')
227
ca.policy = ca_section.get('policy','')
228
ca.signedby = ca_section.get('signedby','')
229
ca.ca_reqfile = ca_section.get('ca_reqfile','')
230
ca.ca_x509_extfile = ca_section.get('ca_x509_extfile','')
231
ca.min_key_size = string.atoi(ca_section.get('min_key_size','0'))
232
ca.default_days = string.atoi(ca_section.get('default_days','0'))
233
ca.crl_days = string.atoi(ca_section.get('crl_days','0'))
234
ca.crl_treshold = string.atoi(ca_section.get('crl_treshold','0'))
236
ca.x509_extensions = ca_section.get('x509_extensions','')
237
x509_extensions_section = self.data.get(ca.x509_extensions,{})
240
ca.basicConstraints = x509_extensions_section.get('basicConstraints','')
241
ca.keyUsage = x509_extensions_section.get('keyUsage','')
242
ca.extendedKeyUsage = x509_extensions_section.get('extendedKeyUsage','')
244
# Netscape attributes
245
ca.nsCertType = x509_extensions_section.get('nsCertType','')
246
ca.nsBaseUrl = x509_extensions_section.get('nsBaseUrl',pyca_section.get('nsBaseUrl',''))
247
ca.nsCaRevocationUrl = x509_extensions_section.get('nsCaRevocationUrl','')
248
ca.nsRevocationUrl = x509_extensions_section.get('nsRevocationUrl','')
249
ca.nsCaPolicyUrl = x509_extensions_section.get('nsCaPolicyUrl','')
250
ca.nsComment = x509_extensions_section.get('nsComment','')
251
if type(ca.nsCertType)==types.ListType:
252
ca.nsCertTypeStr=string.join(ca.nsCertType,'/')
254
ca.nsCertTypeStr=ca.nsCertType
258
# get list of pathnames of all intermediate CA certficates
259
# excluding the self-signed root CA cert
260
def getcacertchain(self,ca_name):
261
ca_section = self.data[self.data['ca'][ca_name]]
263
while ca_section.has_key('signedby'):
264
ca_dir = ca_section.get('dir','')
265
ca_certificate = string.replace(ca_section.get('certificate','$dir/cacert.pem'),'$dir',ca_dir)
266
result.append(ca_certificate)
267
ca_signedby = ca_section['signedby']
268
if not self.data['ca'].has_key(ca_signedby):
269
raise KeyError,'CA name not found'
270
ca_section = self.data[self.data['ca'][ca_signedby]]