e1a7b36d8edba06d1fbbc4e1d6e109f2f059b5f6
1 # FIXME: more intelligent fault raises
6 Copyright 2007, Red Hat, Inc
9 This software may be freely redistributed under the terms of the GNU
10 general public license.
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 import SimpleXMLRPCServer
22 from OpenSSL
import crypto
31 from config
import read_config
32 from commonconfig
import CMConfig
34 CERTMASTER_LISTEN_PORT
= 51235
35 CERTMASTER_CONFIG
= "/etc/certmaster/certmaster.conf"
37 class CertMaster(object):
38 def __init__(self
, conf_file
=CERTMASTER_CONFIG
):
39 self
.cfg
= read_config(conf_file
, CMConfig
)
41 usename
= utils
.get_hostname(talk_to_certmaster
=False)
43 mycn
= '%s-CA-KEY' % usename
44 self
.ca_key_file
= '%s/certmaster.key' % self
.cfg
.cadir
45 self
.ca_cert_file
= '%s/certmaster.crt' % self
.cfg
.cadir
47 if not os
.path
.exists(self
.cfg
.cadir
):
48 os
.makedirs(self
.cfg
.cadir
)
49 if not os
.path
.exists(self
.ca_key_file
) and not os
.path
.exists(self
.ca_cert_file
):
50 certs
.create_ca(CN
=mycn
, ca_key_file
=self
.ca_key_file
, ca_cert_file
=self
.ca_cert_file
)
51 except (IOError, OSError), e
:
52 print 'Cannot make certmaster certificate authority keys/certs, aborting: %s' % e
56 # open up the cakey and cacert so we have them available
57 self
.cakey
= certs
.retrieve_key_from_file(self
.ca_key_file
)
58 self
.cacert
= certs
.retrieve_cert_from_file(self
.ca_cert_file
)
60 for dirpath
in [self
.cfg
.cadir
, self
.cfg
.certroot
, self
.cfg
.csrroot
]:
61 if not os
.path
.exists(dirpath
):
66 'wait_for_cert': self
.wait_for_cert
,
69 def _dispatch(self
, method
, params
):
70 if method
== 'trait_names' or method
== '_getAttributeNames':
71 return self
.handlers
.keys()
73 if method
in self
.handlers
.keys():
74 return self
.handlers
[method
](*params
)
76 raise codes
.InvalidMethodException
78 def _sanitize_cn(self
, commonname
):
79 commonname
= commonname
.replace('/', '')
80 commonname
= commonname
.replace('\\', '')
83 def wait_for_cert(self
, csrbuf
):
86 returns True, caller_cert, ca_cert
91 csrreq
= crypto
.load_certificate_request(crypto
.FILETYPE_PEM
, csrbuf
)
92 except crypto
.Error
, e
:
93 #XXX need to raise a fault here and document it - but false is just as good
96 requesting_host
= self
._sanitize
_cn
(csrreq
.get_subject().CN
)
98 # get rid of dodgy characters in the filename we're about to make
100 certfile
= '%s/%s.cert' % (self
.cfg
.certroot
, requesting_host
)
101 csrfile
= '%s/%s.csr' % (self
.cfg
.csrroot
, requesting_host
)
103 # check for old csr on disk
104 # if we have it - compare the two - if they are not the same - raise a fault
105 if os
.path
.exists(csrfile
):
106 oldfo
= open(csrfile
)
107 oldcsrbuf
= oldfo
.read()
109 oldsha
.update(oldcsrbuf
)
110 olddig
= oldsha
.hexdigest()
112 newsha
.update(csrbuf
)
113 newdig
= newsha
.hexdigest()
114 if not newdig
== olddig
:
115 # XXX raise a proper fault
119 # if we have it, then return True, etc, etc
120 if os
.path
.exists(certfile
):
121 slavecert
= certs
.retrieve_cert_from_file(certfile
)
122 cert_buf
= crypto
.dump_certificate(crypto
.FILETYPE_PEM
, slavecert
)
123 cacert_buf
= crypto
.dump_certificate(crypto
.FILETYPE_PEM
, self
.cacert
)
124 return True, cert_buf
, cacert_buf
126 # if we don't have a cert then:
127 # if we're autosign then sign it, write out the cert and return True, etc, etc
128 # else write out the csr
130 if self
.cfg
.autosign
:
131 cert_fn
= self
.sign_this_csr(csrreq
)
132 cert
= certs
.retrieve_cert_from_file(cert_fn
)
133 cert_buf
= crypto
.dump_certificate(crypto
.FILETYPE_PEM
, cert
)
134 cacert_buf
= crypto
.dump_certificate(crypto
.FILETYPE_PEM
, self
.cacert
)
135 return True, cert_buf
, cacert_buf
138 # write the csr out to a file to be dealt with by the admin
139 destfo
= open(csrfile
, 'w')
140 destfo
.write(crypto
.dump_certificate_request(crypto
.FILETYPE_PEM
, csrreq
))
147 def get_csrs_waiting(self
):
149 csrglob
= '%s/*.csr' % self
.cfg
.csrroot
150 csr_list
= glob
.glob(csrglob
)
152 hn
= os
.path
.basename(f
)
157 def remove_this_cert(self
, hn
):
158 """ removes cert for hostname using unlink """
160 csrglob
= '%s/%s.csr' % (cm
.cfg
.csrroot
, hn
)
161 csrs
= glob
.glob(csrglob
)
162 certglob
= '%s/%s.cert' % (cm
.cfg
.certroot
, hn
)
163 certs
= glob
.glob(certglob
)
164 if not csrs
and not certs
:
165 # FIXME: should be an exception?
166 print 'No match for %s to clean up' % hn
168 for fn
in csrs
+ certs
:
169 print 'Cleaning out %s for host matching %s' % (fn
, hn
)
172 def sign_this_csr(self
, csr
):
173 """returns the path to the signed cert file"""
174 csr_unlink_file
= None
176 if type(csr
) is type(''):
177 if csr
.startswith('/') and os
.path
.exists(csr
): # we have a full path to the file
179 csr_buf
= csrfo
.read()
180 csr_unlink_file
= csr
182 elif os
.path
.exists('%s/%s' % (self
.cfg
.csrroot
, csr
)): # we have a partial path?
183 csrfo
= open('%s/%s' % (self
.cfg
.csrroot
, csr
))
184 csr_buf
= csrfo
.read()
185 csr_unlink_file
= '%s/%s' % (self
.cfg
.csrroot
, csr
)
187 # we have a string of some kind
192 csrreq
= crypto
.load_certificate_request(crypto
.FILETYPE_PEM
, csr_buf
)
193 except crypto
.Error
, e
:
194 raise exceptions
.Exception("Bad CSR: %s" % csr
)
196 else: # assume we got a bare csr req
198 requesting_host
= self
._sanitize
_cn
(csrreq
.get_subject().CN
)
200 certfile
= '%s/%s.cert' % (self
.cfg
.certroot
, requesting_host
)
201 thiscert
= certs
.create_slave_certificate(csrreq
, self
.cakey
, self
.cacert
, self
.cfg
.cadir
)
202 destfo
= open(certfile
, 'w')
203 destfo
.write(crypto
.dump_certificate(crypto
.FILETYPE_PEM
, thiscert
))
206 if csr_unlink_file
and os
.path
.exists(csr_unlink_file
):
207 os
.unlink(csr_unlink_file
)
212 class CertmasterXMLRPCServer(SimpleXMLRPCServer
.SimpleXMLRPCServer
):
213 def __init__(self
, args
):
214 self
.allow_reuse_address
= True
215 SimpleXMLRPCServer
.SimpleXMLRPCServer
.__init
__(self
, args
)
218 def serve(xmlrpcinstance
):
221 Code for starting the XMLRPC service.
224 server
= CertmasterXMLRPCServer((xmlrpcinstance
.cfg
.listen_addr
, CERTMASTER_LISTEN_PORT
))
225 server
.logRequests
= 0 # don't print stuff to console
226 server
.register_instance(xmlrpcinstance
)
227 server
.serve_forever()
232 cm
= CertMaster('/etc/certmaster/certmaster.conf')
234 if "daemon" in argv
or "--daemon" in argv
:
235 utils
.daemonize("/var/run/certmaster.pid")
240 # just let exceptions bubble up for now
244 if __name__
== "__main__":
245 #textdomain(I18N_DOMAIN)