~ubuntu-branches/ubuntu/hardy/pyca/hardy

« back to all changes in this revision

Viewing changes to sbin/ca-certreq-mail.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-certreq-mail.py
 
5
(c) by Michael Stroeder, michael@stroeder.com
 
6
 
 
7
This script is intended to handle the confirmation mail for a
 
8
cert request.
 
9
It receives the mail on stdin and moves the certificate request file
 
10
from pend_reqs_dir to new_reqs_dir. If the senders from:
 
11
address is different of the one in the certificate request the sender
 
12
is notified about this by e-mail.
 
13
If an error occurs the exit code is still zero to prevent the
 
14
mail system from generating a bounce revealing internal informations.
 
15
"""
 
16
 
 
17
__version__ = '0.6.6'
 
18
 
 
19
import sys, os, shutil, time, string, smtplib, rfc822, getopt
 
20
from mimify import mime_decode_header, mime_encode_header
 
21
 
 
22
# Einen Datensatz in Protokolldatei schreiben
 
23
# log           Filehandle von bereits geoeffneter Protokolldatei
 
24
# Kategorie     des Eintrags, z.B. 'Error:'
 
25
# Mail          Mailheader
 
26
# Kommentar     klar
 
27
def LogWrite(log,Kategorie,Mail,Kommentar):
 
28
  log.write('%s %s: ' % (time.strftime('%d.%m.%Y %X',time.localtime(time.time())),Kategorie))
 
29
  if Mail:
 
30
    for i in ['from','subject','message-id']:
 
31
      if Mail.has_key(i):
 
32
        log.write('%s ' % (Mail[i]))
 
33
  log.write('%s\n' % (Kommentar))
 
34
  return
 
35
 
 
36
def findoption(options,paramname):
 
37
  for i in options:
 
38
    if i[0]==paramname:
 
39
      return i
 
40
  return ()
 
41
 
 
42
 
 
43
########################################################################
 
44
#                              Main
 
45
########################################################################
 
46
 
 
47
script_name=sys.argv[0]
 
48
 
 
49
 
 
50
# The log file can only be stderr as long as the config is not read
 
51
logfile = sys.stderr
 
52
 
 
53
# Parse command-line options
 
54
try:
 
55
  options,args=getopt.getopt(sys.argv[1:],'h',['help','config=','pycalib='])
 
56
except getopt.error,e:
 
57
  LogWrite(logfile,'Error',None,str(e))
 
58
 
 
59
# Try to find modules directory
 
60
if findoption(options,'--pycalib')!=():
 
61
  pycalib = findoption(options,'--pycalib')[1]
 
62
else:
 
63
  pycalib = os.environ.get('PYCALIB','/usr/local/pyca/pylib')
 
64
if not os.path.exists(pycalib) or not os.path.isdir(pycalib):
 
65
  LogWrite(logfile,'Error',None,'Module directory %s not exists or not a directory.' % (pycalib))
 
66
sys.path.append(pycalib)
 
67
 
 
68
if findoption(options,'--config')!=():
 
69
  opensslcnfname = findoption(options,'--config')[1]
 
70
else:
 
71
  opensslcnfname = os.environ.get('OPENSSL_CONF','/etc/openssl/openssl.cnf')
 
72
 
 
73
if not os.path.isfile(opensslcnfname):
 
74
  LogWrite(logfile,'Error',None,'Config file %s not found.' % (opensslcnfname))
 
75
 
 
76
import openssl,charset
 
77
from openssl.db import \
 
78
  empty_DN_dict, \
 
79
  DB_type,DB_exp_date,DB_rev_date,DB_serial,DB_file,DB_name,DB_number, \
 
80
  DB_TYPE_REV,DB_TYPE_EXP,DB_TYPE_VAL, \
 
81
  dbtime2tuple,GetEntriesbyDN,SplitDN
 
82
 
 
83
# Read the configuration file
 
84
if os.path.isfile('%s.pickle' % (opensslcnfname)):
 
85
  # Try to read OpenSSL's config file from a pickled copy
 
86
  f=open('%s.pickle' % (opensslcnfname),'rb')
 
87
  try:
 
88
    # first try to use the faster cPickle module
 
89
    from cPickle import load
 
90
  except ImportError:
 
91
    from pickle import load
 
92
  opensslcnf=load(f)
 
93
  f.close()
 
94
else:
 
95
  # Parse OpenSSL's config file from source
 
96
  opensslcnf=openssl.cnf.OpenSSLConfigClass(opensslcnfname)
 
97
 
 
98
pyca_section = opensslcnf.data.get('pyca',{})
 
99
 
 
100
logfile_name = pyca_section.get('caCertConfirmReqLog','/var/log/pyca/ca-certreq-mail.out')
 
101
logfile = open(logfile_name,'a')
 
102
 
 
103
openssl.bin_filename = pyca_section.get('OpenSSLExec','/usr/local/ssl/bin/openssl')
 
104
if not os.path.isfile(openssl.bin_filename):
 
105
  LogWrite(logfile,'Error',None,'Did not find OpenSSL executable %s.' % (openssl.bin_filename))
 
106
 
 
107
ca_names = opensslcnf.sectionkeys.get('ca',[])
 
108
MailRelay           = pyca_section.get('MailRelay','localhost')
 
109
caCertReqMailAdr    = pyca_section.get('caCertReqMailAdr','')
 
110
caInternalCertTypes = pyca_section.get('caInternalCertTypes',[])
 
111
caInternalDomains   = pyca_section.get('caInternalDomains','')
 
112
if type(caInternalDomains)!=type([]):
 
113
  caInternalDomains = [caInternalDomains]
 
114
 
 
115
#############################################################
 
116
# Hauptprogramm
 
117
#############################################################
 
118
 
 
119
m=rfc822.Message(sys.stdin)
 
120
 
 
121
#############################################################
 
122
# Format von Subject und Body ueberpruefen
 
123
# Wirkt recht paranoid, soll aber gegen Muellmails schuetzen
 
124
#############################################################
 
125
 
 
126
# Ueberlange Subjects verbieten
 
127
if len(m["subject"])>80:
 
128
  LogWrite(logfile,'Error',m,'Subject too long.')
 
129
  sys.exit(0)
 
130
 
 
131
if m.has_key('from'):
 
132
  from_addr = mime_decode_header(string.strip(m["from"]))
 
133
  from_name, from_mail = rfc822.AddressList(from_addr).addresslist[0]
 
134
else:
 
135
  from_mail = ''
 
136
 
 
137
subject = string.strip(m["subject"])
 
138
subjectstart = string.find(subject,'cert-req-')
 
139
 
 
140
# Format dreiteilig?
 
141
try:
 
142
  prefix,ca_name,caChallengeId = string.split(subject[subjectstart:len(subject)],'.',2)
 
143
except ValueError:
 
144
  LogWrite(logfile,'Error',m,'Subject has wrong format.')
 
145
  sys.exit(0)
 
146
 
 
147
# Prefix richtig?
 
148
if prefix=='cert-req-SPKAC':
 
149
  request_filenamesuffix = 'spkac'
 
150
elif prefix=='cert-req-PKCS10':
 
151
  request_filenamesuffix = 'pem'
 
152
else:
 
153
  LogWrite(logfile,'Error',m,'Subject has wrong format.')
 
154
  sys.exit(0)
 
155
 
 
156
# ChallengeID nicht zu lang?
 
157
if len(caChallengeId)>30:
 
158
  LogWrite(logfile,'Error',m,'caChallengeId %s has bad format.' % (caChallengeId))
 
159
  sys.exit(0)
 
160
 
 
161
# CA Name gueltig?
 
162
if not (ca_name in ca_names):
 
163
  LogWrite(logfile,'Error',m,'ca_name "%s" wrong.' % (ca_name))
 
164
  sys.exit(0)
 
165
 
 
166
ca = opensslcnf.getcadata(ca_name)
 
167
 
 
168
# Eine Benutzerantwort ist eingetroffen
 
169
request_filename = os.path.join(ca.pend_reqs_dir,'%s.%s.%s' % (prefix,ca_name,caChallengeId))
 
170
 
 
171
pubkey_filename = '%s.%s' % (request_filename,request_filenamesuffix)
 
172
 
 
173
# Existieren die benoetigten Dateien?
 
174
if not os.path.isfile(pubkey_filename):
 
175
  LogWrite(logfile,'Error',m,'Certificate request file %s not found.' % (pubkey_filename))
 
176
  sys.exit(0)
 
177
 
 
178
# Hier sind jetzt alle Angaben gueltig, soweit pruefbar
 
179
 
 
180
newrequest_filename = os.path.join(ca.new_reqs_dir,'%s.%s.%s' % (prefix,ca_name,caChallengeId))
 
181
target_pubkey_filename = '%s.%s' % (newrequest_filename,request_filenamesuffix)
 
182
 
 
183
# Now copy files to target directory, use copy to get new ownership
 
184
try:
 
185
  shutil.copyfile(pubkey_filename,target_pubkey_filename)
 
186
  os.chmod(target_pubkey_filename,0440)
 
187
except IOError:
 
188
  LogWrite(logfile,'Error',m,'Copying %s to %s failed.' % (pubkey_filename,target_pubkey_filename))
 
189
  sys.exit(0)
 
190
else:
 
191
  try:
 
192
    os.remove(pubkey_filename)
 
193
  except IOError:
 
194
    LogWrite(logfile,'Error',m,'Removing %s failed.' % (pubkey_filename))
 
195
    sys.exit(0)
 
196
 
 
197
LogWrite(logfile,'Challenge',m,'Request challenge: %s Id=%s' % (ca_name,caChallengeId))
 
198
 
 
199
# FIX ME! We also would like to look into PKCS10 requests!
 
200
if prefix!='cert-req-SPKAC':
 
201
  sys.exit(0)
 
202
 
 
203
# Read the certificate request file
 
204
certreq = openssl.cert.SPKACClass(target_pubkey_filename)
 
205
 
 
206
certreq_name_attr = certreq.data.get('commonName','')
 
207
certreq_mail_attr = certreq.data.get('emailAddress','')
 
208
 
 
209
if (certreq_name_attr and from_name!=certreq_name_attr) or \
 
210
   (certreq_mail_attr and from_mail!=certreq_mail_attr):
 
211
 
 
212
  cacert = openssl.cert.X509CertificateClass(ca.certificate)
 
213
  ca_from_addr = cacert.subject.get('Email',pyca_section.get('caAdminMailAdr',''))
 
214
  mail_msg = """From: %s <%s>
 
215
To: %s
 
216
Subject: Your confirmation e-mail with ID %s
 
217
 
 
218
We received the correct confirmation e-mail for your
 
219
certificate request.
 
220
 
 
221
However the from: field of your confirmation e-mail
 
222
 
 
223
  From: %s
 
224
 
 
225
did not match the attributes
 
226
 
 
227
  commonName   = %s
 
228
  emailAddress = %s
 
229
 
 
230
given in your certificate request. Your certificate request will
 
231
be processed anyway. But if you intend to use the requested
 
232
certificate for signing e-mails you might want to adjust the from
 
233
address in your mail clients preferences / options menu to avoid
 
234
trouble with other mail users reporting invalid signatures.
 
235
 
 
236
If you have further questions simply reply to this e-mail.
 
237
""" % (
 
238
  mime_encode_header(
 
239
    charset.t612iso(
 
240
      cacert.subject.get('CN','CA administrator'))
 
241
    ),
 
242
    ca_from_addr,
 
243
  certreq_mail_attr,
 
244
  caChallengeId,
 
245
  from_addr,
 
246
  certreq_name_attr,certreq_mail_attr
 
247
)
 
248
 
 
249
  try:
 
250
    smtpconn=smtplib.SMTP(MailRelay)
 
251
    try:
 
252
      try:
 
253
        smtpconn.set_debuglevel(0)
 
254
        smtpconn.sendmail(ca_from_addr,certreq_mail_attr,mail_msg)
 
255
      finally:
 
256
        smtpconn.quit()
 
257
    except:
 
258
      LogWrite(logfile,'Error',m,'Unable to send an e-mail to %s!\n' % (certreq_mail_attr))
 
259
    else:
 
260
      LogWrite(logfile,'Error',m,'Sent from address warning to %s!\n' % (certreq_mail_attr))
 
261
  except socket.error:
 
262
    LogWrite(logfile,'Error',m,'Unable to contact default mail relay %s!\n' % (MailRelay))
 
263
 
 
264
sys.exit(0)