~ubuntu-branches/ubuntu/jaunty/pyca/jaunty

« back to all changes in this revision

Viewing changes to sbin/ca-cycle-pub.py

  • Committer: Bazaar Package Importer
  • Author(s): Lars Bahner
  • Date: 2003-12-02 19:39:35 UTC
  • Revision ID: james.westby@ubuntu.com-20031202193935-fzzt289mntvy6a8q
Tags: upstream-20031118
ImportĀ upstreamĀ versionĀ 20031118

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
 
 
3
########################################################################
 
4
# ca-cycle-pub.py
 
5
# (c) by Michael Stroeder, michael@stroeder.com
 
6
########################################################################
 
7
 
 
8
__version__ = '0.6.6'
 
9
 
 
10
########################################################################
 
11
# This script is typically run by CRON or a similar task manager.
 
12
# It does several jobs (some not implemented yet):
 
13
# - Mark expired certificates in OpenSSL certificate database
 
14
# - Sort in new certificates and inform user via e-mail where to
 
15
#   download his certificate
 
16
# - Spool certificate requests and certificate revocation requests
 
17
# - Remove stale certificate requests from caPendCertReqDir
 
18
########################################################################
 
19
 
 
20
import sys, string, os, smtplib, getopt
 
21
 
 
22
from time import time,gmtime,localtime,strftime,mktime
 
23
 
 
24
def findoption(options,paramname):
 
25
  for i in options:
 
26
    if i[0]==paramname:
 
27
      return i
 
28
  return ()
 
29
 
 
30
def PrintUsage(ErrorMsg='',ErrorCode=1):
 
31
  script_name = string.split(sys.argv[0],os.sep)[-1]
 
32
  sys.stderr.write("""*** %s *** (C) by Michael Stroeder, 1999
 
33
 
 
34
usage: %s [options]
 
35
 
 
36
Options:
 
37
 
 
38
  -h or --help
 
39
        Print out this message
 
40
 
 
41
  --config=[pathname]
 
42
        Pathname of OpenSSL configuration file.
 
43
        You may also use env variable OPENSSL_CONF.
 
44
        Default: /etc/openssl/openssl.cnf
 
45
 
 
46
  --pycalib=[directory]
 
47
        Specify directory containing the pyCA modules
 
48
        Default: /usr/local/pyca/pylib
 
49
 
 
50
""" % (script_name,script_name))
 
51
  if ErrorMsg:
 
52
    sys.stderr.write('Error: %s\n' % ErrorMsg)
 
53
  sys.exit(ErrorCode)
 
54
 
 
55
########################################################################
 
56
#                              Main
 
57
########################################################################
 
58
 
 
59
script_name=sys.argv[0]
 
60
 
 
61
try:
 
62
  options,args=getopt.getopt(sys.argv[1:],'h',['help','config=','pycalib='])
 
63
except getopt.error,e:
 
64
  PrintUsage(str(e))
 
65
 
 
66
if findoption(options,'-h')!=() or findoption(options,'--help')!=():
 
67
  PrintUsage()
 
68
 
 
69
if findoption(options,'--config')!=():
 
70
  opensslcnfname = findoption(options,'--config')[1]
 
71
else:
 
72
  opensslcnfname = os.environ.get('OPENSSL_CONF','/etc/openssl/openssl.cnf')
 
73
 
 
74
if not os.path.isfile(opensslcnfname):
 
75
  PrintUsage('Config file %s not found.' % (opensslcnfname))
 
76
 
 
77
if findoption(options,'--pycalib')!=():
 
78
  pycalib = findoption(options,'--pycalib')[1]
 
79
else:
 
80
  pycalib = os.environ.get('PYCALIB','/usr/local/pyca/pylib')
 
81
 
 
82
if not os.path.exists(pycalib) or not os.path.isdir(pycalib):
 
83
  PrintUsage('Module directory %s not exists or not a directory.' % (pycalib))
 
84
 
 
85
sys.path.append(pycalib)
 
86
 
 
87
try:
 
88
  import openssl,charset
 
89
  from openssl.db import \
 
90
    empty_DN_dict, \
 
91
    DB_type,DB_exp_date,DB_rev_date,DB_serial,DB_file,DB_name,DB_number, \
 
92
    DB_TYPE_REV,DB_TYPE_EXP,DB_TYPE_VAL, \
 
93
    dbtime2tuple,GetEntriesbyDN,SplitDN
 
94
except ImportError:
 
95
  PrintUsage('Required pyCA modules not found in directory %s!' % (pycalib))
 
96
 
 
97
# Read the configuration file
 
98
if os.path.isfile('%s.pickle' % (opensslcnfname)):
 
99
  # Try to read OpenSSL's config file from a pickled copy
 
100
  f=open('%s.pickle' % (opensslcnfname),'rb')
 
101
  try:
 
102
    # first try to use the faster cPickle module
 
103
    from cPickle import load
 
104
  except ImportError:
 
105
    from pickle import load
 
106
  opensslcnf=load(f)
 
107
  f.close()
 
108
else:
 
109
  # Parse OpenSSL's config file from source
 
110
  opensslcnf=openssl.cnf.OpenSSLConfigClass(opensslcnfname)
 
111
 
 
112
pyca_section = opensslcnf.data.get('pyca',{})
 
113
 
 
114
openssl.bin_filename = pyca_section.get('OpenSSLExec','/usr/local/ssl/bin/openssl')
 
115
if not os.path.isfile(openssl.bin_filename):
 
116
  PrintUsage('Did not find OpenSSL executable %s.' % (openssl.bin_filename))
 
117
 
 
118
MailRelay          = pyca_section.get('MailRelay','localhost')
 
119
nsGetCertUrl       = pyca_section.get('nsGetCertUrl','cgi-bin/get-cert.py')
 
120
nsViewCertUrl      = pyca_section.get('nsViewCertUrl','cgi-bin/view-cert.py')
 
121
caPendCertReqValid = 3600*string.atoi(pyca_section.get('caPendCertReqValid','0'))
 
122
 
 
123
newcert_mailtext = r"""From: %s
 
124
To: %s
 
125
Subject: Your certificate %d
 
126
 
 
127
The certificate you requested has been issued and is valid
 
128
from %s until %s.
 
129
 
 
130
You can retrieve your certificate from here:
 
131
 
 
132
  %s%s/%s/%s.crt?%x
 
133
 
 
134
Please use the same web browser on the same machine, with same
 
135
login and configuration data as when you created the certificate
 
136
request. Otherwise your software will likely refuse to install
 
137
the certificate.
 
138
 
 
139
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
140
! It is highly recommended to make a backup copy of your !
 
141
! private key and certificate right after installing it. !
 
142
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
143
 
 
144
If you have further questions simply reply to this e-mail.
 
145
 
 
146
----------------------------------------------------------
 
147
 
 
148
%s
 
149
 
 
150
Detail view of your certificate:
 
151
 
 
152
  %s%s/%s/%s?%x
 
153
"""
 
154
 
 
155
gmt = time()
 
156
gmtstr = strftime('%Y%m%d%H%M%S',gmtime(gmt))
 
157
 
 
158
######################################################################
 
159
# Spool certificates and certificate revocation lists
 
160
# from system holding the private keys
 
161
######################################################################
 
162
 
 
163
pass
 
164
 
 
165
######################################################################
 
166
# CA specific actions
 
167
######################################################################
 
168
 
 
169
ca_names = opensslcnf.sectionkeys.get('ca',[])
 
170
 
 
171
# Lists of processed files and dirs to avoid double-processing
 
172
processed_ca_databases = []
 
173
processed_ca_crls = []
 
174
processed_pend_reqs_dirs = []
 
175
processed_new_reqs_dirs = []
 
176
processed_new_certs_dirs = []
 
177
 
 
178
for ca_name in ca_names:
 
179
 
 
180
  sys.stdout.write('\n\nProcessing certificate authority "%s"\n' % (ca_name))
 
181
 
 
182
  ca = opensslcnf.getcadata(ca_name)
 
183
 
 
184
 
 
185
  ######################################################################
 
186
  # Processing certificate database
 
187
  ######################################################################
 
188
 
 
189
  if not ca.database in processed_ca_databases:
 
190
 
 
191
    if os.path.isfile(ca.database):
 
192
 
 
193
      processed_ca_databases.append(ca.database)
 
194
      # Certificate database not processed up to now
 
195
      ca_db = openssl.db.OpenSSLcaDatabaseClass(ca.database)
 
196
 
 
197
      # Mark expired certificates in OpenSSL certificate database
 
198
      expired_db_entries = ca_db.Expire()
 
199
      if expired_db_entries:
 
200
        sys.stdout.write('The following entries were marked as expired:\n')
 
201
        for db_entry in expired_db_entries:
 
202
          sys.stdout.write('%s\n' % (charset.asn12iso(db_entry[DB_name])))
 
203
 
 
204
      # Mark expired certificates in OpenSSL certificate database
 
205
      expire_treshold=7*86400
 
206
      expired_db_entries = ca_db.ExpireWarning(expire_treshold)
 
207
      if expired_db_entries:
 
208
        sys.stdout.write('The following entries will expire soon:\n')
 
209
        for db_entry in expired_db_entries:
 
210
          sys.stdout.write('%s, %s, %s\n' % (
 
211
              db_entry[DB_serial],
 
212
              strftime('%Y-%m-%d %H:%M',localtime(mktime(dbtime2tuple(db_entry[DB_exp_date])))),
 
213
              charset.asn12iso(db_entry[DB_name])
 
214
            )
 
215
          )
 
216
 
 
217
    else:
 
218
      sys.stderr.write('Warning: CA database file %s not found.\n' % (ca.database))
 
219
 
 
220
 
 
221
  ######################################################################
 
222
  # Move expired CRLs to archive
 
223
  ######################################################################
 
224
 
 
225
  if not ca.crl in processed_ca_crls:
 
226
 
 
227
    if os.path.isfile(ca.crl):
 
228
 
 
229
      processed_ca_crls.append(ca.crl)
 
230
      ca_crl = openssl.cert.CRLClass(ca.crl)
 
231
 
 
232
      if ca_crl.nextUpdate_secs<gmt:
 
233
 
 
234
        ca_archived_crl = os.path.join(ca.crl_dir,os.path.basename('%s-%s.pem' % (os.path.splitext(ca.crl)[0],gmtstr)))
 
235
        # Archive the copy in the preferred format
 
236
        os.rename(ca.crl,ca_archived_crl)
 
237
        sys.stdout.write('Archived expired CRL file %s.\n' % (ca_archived_crl))
 
238
 
 
239
    else:
 
240
      sys.stderr.write('Warning: CRL file %s not found.\n' % (ca.crl))
 
241
 
 
242
 
 
243
  ######################################################################
 
244
  # Remove stale certificate requests
 
245
  ######################################################################
 
246
 
 
247
  if caPendCertReqValid and \
 
248
     ca.pend_reqs_dir and \
 
249
     (not ca.pend_reqs_dir in processed_pend_reqs_dirs):
 
250
 
 
251
    processed_pend_reqs_dirs.append(ca.pend_reqs_dir)
 
252
    # pend_certs_dir not processed up to now
 
253
    if os.path.isdir(ca.pend_reqs_dir):
 
254
 
 
255
      pendcertfilenames = os.listdir(ca.pend_reqs_dir)
 
256
      stalecerts = 0
 
257
      for reqfilename in pendcertfilenames:
 
258
        reqpathname = os.path.join(ca.pend_reqs_dir,reqfilename)
 
259
        if gmt-caPendCertReqValid > os.path.getmtime(reqpathname):
 
260
          os.remove(reqpathname)
 
261
          stalecerts = stalecerts+1
 
262
 
 
263
      if stalecerts:
 
264
        sys.stdout.write('Removed %d stale certificate requests from %s.\n' % (stalecerts,ca.pend_reqs_dir))
 
265
 
 
266
    else:
 
267
      pendcertfilenames = []
 
268
      sys.stderr.write('Directory %s not found!\n' % (ca.pend_reqs_dir))
 
269
 
 
270
 
 
271
  ######################################################################
 
272
  # New certificate requests
 
273
  ######################################################################
 
274
 
 
275
  if ca.new_reqs_dir and \
 
276
     (not ca.new_reqs_dir in processed_new_reqs_dirs):
 
277
 
 
278
    processed_new_reqs_dirs.append(ca.new_reqs_dir)
 
279
    # new_certs_dir not processed up to now
 
280
    if os.path.isdir(ca.new_reqs_dir):
 
281
      newreqfilenames = os.listdir(ca.new_reqs_dir)
 
282
      newreqcounter = 0
 
283
      for reqfilename in newreqfilenames:
 
284
        if os.path.splitext(reqfilename)[1] in ['.spkac','.pem']:
 
285
          newreqcounter = newreqcounter+1
 
286
 
 
287
      if newreqcounter:
 
288
        sys.stdout.write('%d valid certificate requests in %s.\n' % (newreqcounter,ca.new_reqs_dir))
 
289
 
 
290
    else:
 
291
      newcertfilenames = []
 
292
      sys.stderr.write('Directory %s not found!\n' % (ca.new_reqs_dir))
 
293
 
 
294
 
 
295
  ######################################################################
 
296
  # Publish new client certificates and inform user via e-mail where to
 
297
  # download his certificate
 
298
  ######################################################################
 
299
 
 
300
  if (not ca.new_certs_dir in processed_new_certs_dirs) and \
 
301
     os.path.isfile(ca.certificate):
 
302
 
 
303
    processed_new_certs_dirs.append(ca.new_certs_dir)
 
304
 
 
305
    # new_certs_dir not processed up to now
 
306
    newcertfilenames = os.listdir(ca.new_certs_dir)
 
307
 
 
308
    if newcertfilenames:
 
309
      sys.stdout.write('Publish %d new certificates in %s.\n' % (len(newcertfilenames),ca.new_certs_dir))
 
310
      if ca.isservercert():
 
311
        certtype = 'server'
 
312
      elif ca.isclientcert():
 
313
        certtype = 'user'
 
314
      else:
 
315
        certtype = 'user'
 
316
 
 
317
    for certfilename in newcertfilenames:
 
318
 
 
319
      newcertpathname = os.path.join(ca.new_certs_dir,certfilename)
 
320
      cert = openssl.cert.X509CertificateClass(newcertpathname)
 
321
 
 
322
      if openssl.db.GetEntrybySerial(ca.database,cert.serial):
 
323
 
 
324
        certpathname = os.path.join(ca.certs,certfilename)
 
325
 
 
326
        if not os.path.isfile(certpathname):
 
327
 
 
328
          import mimify
 
329
 
 
330
          issuername = charset.asn12iso(cert.issuer.get('CN',''))
 
331
          issueremail = cert.issuer.get('Email','root@localhost')
 
332
          subjectname = charset.asn12iso(cert.subject.get('CN',''))
 
333
          subjectemail = cert.subject.get('Email','')
 
334
          from_addr = mimify.mime_encode_header('%s <%s>' % (issuername,issueremail))
 
335
          if subjectemail:
 
336
            to_name,to_email = subjectname,subjectemail
 
337
          else:
 
338
            to_name,to_email = issuername,issueremail
 
339
          to_addr = mimify.mime_encode_header('%s <%s>' % (to_name,to_email))
 
340
 
 
341
          # Mailbody
 
342
          mail_msg = newcert_mailtext % (
 
343
                       from_addr,
 
344
                       to_addr,
 
345
                       cert.serial,
 
346
                       strftime('%Y-%m-%d %H:%M',gmtime(cert.notBefore_secs)),
 
347
                       strftime('%Y-%m-%d %H:%M',gmtime(cert.notAfter_secs)),
 
348
                       ca.nsBaseUrl,nsGetCertUrl,ca_name,certtype,cert.serial,
 
349
                       cert.asciiprint(),
 
350
                       ca.nsBaseUrl,nsViewCertUrl,ca_name,certtype,cert.serial,
 
351
                     )
 
352
 
 
353
          smtpconn=smtplib.SMTP(MailRelay)
 
354
          smtpconn.set_debuglevel(0)
 
355
          try:
 
356
            smtpconn.sendmail(issueremail,to_email,mail_msg)
 
357
            sys.stderr.write('Sent e-mail to %s <%s>.\n' % (to_name,to_email))
 
358
          except:
 
359
            sys.stderr.write('Unable to send an e-mail to %s <%s>.\n' % (to_name,to_email))
 
360
          smtpconn.quit()
 
361
 
 
362
          # Move file from newcerts to certs dir
 
363
          os.rename(newcertpathname,certpathname)
 
364
 
 
365
        else:
 
366
          sys.stderr.write('Target file %s already exists. Maybe something went wrong?\n' % (certfilename))
 
367
 
 
368
      else:
 
369
        sys.stderr.write('Did not find certificate with serial number %d in file %s! Certificate database up to date?\n' % (cert.serial,ca.database))
 
370
 
 
371
 
 
372
######################################################################
 
373
# Spool certificate requests and certificate revocation requests
 
374
# to system holding the private keys
 
375
######################################################################
 
376
 
 
377
pass
 
378
 
 
379
 
 
380
######################################################################
 
381
# Check again if everything is in place and give out summary report
 
382
######################################################################
 
383
 
 
384
sys.stdout.write('\n\n##### Summary report #####\n\n')
 
385
 
 
386
for ca_name in ca_names:
 
387
 
 
388
  ca = opensslcnf.getcadata(ca_name)
 
389
 
 
390
  sys.stdout.write('CA definition "%s":\n' % (ca_name))
 
391
  if not os.path.isfile(ca.crl):
 
392
    sys.stderr.write('Warning: No valid CRL file %s after processing.\n' % (ca.crl))
 
393
  else:
 
394
    ca_crl = openssl.cert.CRLClass(ca.crl)
 
395
    sys.stdout.write('CRL file %s valid from %s until %s.\n' % (ca.crl,ca_crl.lastUpdate,ca_crl.nextUpdate))
 
396