~ubuntu-branches/ubuntu/quantal/nova/quantal-proposed

« back to all changes in this revision

Viewing changes to nova/crypto.py

  • Committer: Bazaar Package Importer
  • Author(s): Chuck Short
  • Date: 2011-01-21 11:48:06 UTC
  • mto: This revision was merged to the branch mainline in revision 9.
  • Revision ID: james.westby@ubuntu.com-20110121114806-v8fvnnl6az4m4ohv
Tags: upstream-2011.1~bzr597
ImportĀ upstreamĀ versionĀ 2011.1~bzr597

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
Wrappers around standard crypto data elements.
20
20
 
21
21
Includes root and intermediate CAs, SSH key_pairs and x509 certificates.
22
 
 
23
22
"""
24
23
 
25
24
import base64
 
25
import gettext
26
26
import hashlib
27
 
import logging
28
27
import os
29
28
import shutil
30
29
import struct
34
33
 
35
34
import M2Crypto
36
35
 
37
 
from nova import exception
 
36
gettext.install('nova', unicode=1)
 
37
 
 
38
from nova import context
 
39
from nova import db
38
40
from nova import flags
39
 
 
40
 
 
 
41
from nova import log as logging
 
42
 
 
43
 
 
44
LOG = logging.getLogger("nova.crypto")
41
45
FLAGS = flags.FLAGS
42
 
flags.DEFINE_string('ca_file', 'cacert.pem', 'Filename of root CA')
 
46
flags.DEFINE_string('ca_file', 'cacert.pem', _('Filename of root CA'))
 
47
flags.DEFINE_string('key_file',
 
48
                    os.path.join('private', 'cakey.pem'),
 
49
                    _('Filename of private key'))
 
50
flags.DEFINE_string('crl_file', 'crl.pem',
 
51
                    _('Filename of root Certificate Revokation List'))
43
52
flags.DEFINE_string('keys_path', '$state_path/keys',
44
 
                    'Where we keep our keys')
 
53
                    _('Where we keep our keys'))
45
54
flags.DEFINE_string('ca_path', '$state_path/CA',
46
 
                    'Where we keep our root CA')
47
 
flags.DEFINE_boolean('use_intermediate_ca', False,
48
 
                     'Should we use intermediate CAs for each project?')
49
 
 
50
 
 
51
 
def ca_path(project_id):
52
 
    if project_id:
53
 
        return "%s/INTER/%s/cacert.pem" % (FLAGS.ca_path, project_id)
54
 
    return "%s/cacert.pem" % (FLAGS.ca_path)
 
55
                    _('Where we keep our root CA'))
 
56
flags.DEFINE_boolean('use_project_ca', False,
 
57
                     _('Should we use a CA for each project?'))
 
58
flags.DEFINE_string('user_cert_subject',
 
59
                    '/C=US/ST=California/L=MountainView/O=AnsoLabs/'
 
60
                    'OU=NovaDev/CN=%s-%s-%s',
 
61
                    _('Subject for certificate for users, '
 
62
                    '%s for project, user, timestamp'))
 
63
flags.DEFINE_string('project_cert_subject',
 
64
                    '/C=US/ST=California/L=MountainView/O=AnsoLabs/'
 
65
                    'OU=NovaDev/CN=project-ca-%s-%s',
 
66
                    _('Subject for certificate for projects, '
 
67
                    '%s for project, timestamp'))
 
68
flags.DEFINE_string('vpn_cert_subject',
 
69
                    '/C=US/ST=California/L=MountainView/O=AnsoLabs/'
 
70
                    'OU=NovaDev/CN=project-vpn-%s-%s',
 
71
                    _('Subject for certificate for vpns, '
 
72
                    '%s for project, timestamp'))
 
73
 
 
74
 
 
75
def ca_folder(project_id=None):
 
76
    if FLAGS.use_project_ca and project_id:
 
77
        return os.path.join(FLAGS.ca_path, 'projects', project_id)
 
78
    return FLAGS.ca_path
 
79
 
 
80
 
 
81
def ca_path(project_id=None):
 
82
    return os.path.join(ca_folder(project_id), FLAGS.ca_file)
 
83
 
 
84
 
 
85
def key_path(project_id=None):
 
86
    return os.path.join(ca_folder(project_id), FLAGS.key_file)
55
87
 
56
88
 
57
89
def fetch_ca(project_id=None, chain=True):
58
 
    if not FLAGS.use_intermediate_ca:
 
90
    if not FLAGS.use_project_ca:
59
91
        project_id = None
60
92
    buffer = ""
61
93
    if project_id:
92
124
 
93
125
 
94
126
def ssl_pub_to_ssh_pub(ssl_public_key, name='root', suffix='nova'):
95
 
    pub_key_buffer = M2Crypto.BIO.MemoryBuffer(ssl_public_key)
96
 
    rsa_key = M2Crypto.RSA.load_pub_key_bio(pub_key_buffer)
 
127
    buf = M2Crypto.BIO.MemoryBuffer(ssl_public_key)
 
128
    rsa_key = M2Crypto.RSA.load_pub_key_bio(buf)
97
129
    e, n = rsa_key.pub()
98
130
 
99
131
    key_type = 'ssh-rsa'
106
138
    return '%s %s %s@%s\n' % (key_type, b64_blob, name, suffix)
107
139
 
108
140
 
109
 
def generate_x509_cert(subject, bits=1024):
 
141
def revoke_cert(project_id, file_name):
 
142
    """Revoke a cert by file name"""
 
143
    start = os.getcwd()
 
144
    os.chdir(ca_folder(project_id))
 
145
    # NOTE(vish): potential race condition here
 
146
    utils.execute("openssl ca -config ./openssl.cnf -revoke '%s'" % file_name)
 
147
    utils.execute("openssl ca -gencrl -config ./openssl.cnf -out '%s'" %
 
148
                  FLAGS.crl_file)
 
149
    os.chdir(start)
 
150
 
 
151
 
 
152
def revoke_certs_by_user(user_id):
 
153
    """Revoke all user certs"""
 
154
    admin = context.get_admin_context()
 
155
    for cert in db.certificate_get_all_by_user(admin, user_id):
 
156
        revoke_cert(cert['project_id'], cert['file_name'])
 
157
 
 
158
 
 
159
def revoke_certs_by_project(project_id):
 
160
    """Revoke all project certs"""
 
161
    # NOTE(vish): This is somewhat useless because we can just shut down
 
162
    #             the vpn.
 
163
    admin = context.get_admin_context()
 
164
    for cert in db.certificate_get_all_by_project(admin, project_id):
 
165
        revoke_cert(cert['project_id'], cert['file_name'])
 
166
 
 
167
 
 
168
def revoke_certs_by_user_and_project(user_id, project_id):
 
169
    """Revoke certs for user in project"""
 
170
    admin = context.get_admin_context()
 
171
    for cert in db.certificate_get_all_by_user(admin, user_id, project_id):
 
172
        revoke_cert(cert['project_id'], cert['file_name'])
 
173
 
 
174
 
 
175
def _project_cert_subject(project_id):
 
176
    """Helper to generate user cert subject"""
 
177
    return FLAGS.project_cert_subject % (project_id, utils.isotime())
 
178
 
 
179
 
 
180
def _vpn_cert_subject(project_id):
 
181
    """Helper to generate user cert subject"""
 
182
    return FLAGS.vpn_cert_subject % (project_id, utils.isotime())
 
183
 
 
184
 
 
185
def _user_cert_subject(user_id, project_id):
 
186
    """Helper to generate user cert subject"""
 
187
    return FLAGS.user_cert_subject % (project_id, user_id, utils.isotime())
 
188
 
 
189
 
 
190
def generate_x509_cert(user_id, project_id, bits=1024):
 
191
    """Generate and sign a cert for user in project"""
 
192
    subject = _user_cert_subject(user_id, project_id)
110
193
    tmpdir = tempfile.mkdtemp()
111
194
    keyfile = os.path.abspath(os.path.join(tmpdir, 'temp.key'))
112
195
    csrfile = os.path.join(tmpdir, 'temp.csr')
113
 
    logging.debug("openssl genrsa -out %s %s" % (keyfile, bits))
114
 
    utils.runthis("Generating private key: %s",
115
 
                  "openssl genrsa -out %s %s" % (keyfile, bits))
116
 
    utils.runthis("Generating CSR: %s",
117
 
                  "openssl req -new -key %s -out %s -batch -subj %s" %
 
196
    utils.execute("openssl genrsa -out %s %s" % (keyfile, bits))
 
197
    utils.execute("openssl req -new -key %s -out %s -batch -subj %s" %
118
198
                  (keyfile, csrfile, subject))
119
199
    private_key = open(keyfile).read()
120
200
    csr = open(csrfile).read()
121
201
    shutil.rmtree(tmpdir)
122
 
    return (private_key, csr)
123
 
 
124
 
 
125
 
def sign_csr(csr_text, intermediate=None):
126
 
    if not FLAGS.use_intermediate_ca:
127
 
        intermediate = None
128
 
    if not intermediate:
129
 
        return _sign_csr(csr_text, FLAGS.ca_path)
130
 
    user_ca = "%s/INTER/%s" % (FLAGS.ca_path, intermediate)
131
 
    if not os.path.exists(user_ca):
 
202
    (serial, signed_csr) = sign_csr(csr, project_id)
 
203
    fname = os.path.join(ca_folder(project_id), "newcerts/%s.pem" % serial)
 
204
    cert = {'user_id': user_id,
 
205
            'project_id': project_id,
 
206
            'file_name': fname}
 
207
    db.certificate_create(context.get_admin_context(), cert)
 
208
    return (private_key, signed_csr)
 
209
 
 
210
 
 
211
def _ensure_project_folder(project_id):
 
212
    if not os.path.exists(ca_path(project_id)):
132
213
        start = os.getcwd()
133
 
        os.chdir(FLAGS.ca_path)
134
 
        utils.runthis("Generating intermediate CA: %s",
135
 
                      "sh geninter.sh %s" % (intermediate))
 
214
        os.chdir(ca_folder())
 
215
        utils.execute("sh geninter.sh %s %s" %
 
216
                      (project_id, _project_cert_subject(project_id)))
136
217
        os.chdir(start)
137
 
    return _sign_csr(csr_text, user_ca)
 
218
 
 
219
 
 
220
def generate_vpn_files(project_id):
 
221
    project_folder = ca_folder(project_id)
 
222
    csr_fn = os.path.join(project_folder, "server.csr")
 
223
    crt_fn = os.path.join(project_folder, "server.crt")
 
224
 
 
225
    if os.path.exists(crt_fn):
 
226
        return
 
227
    _ensure_project_folder(project_id)
 
228
    start = os.getcwd()
 
229
    os.chdir(ca_folder())
 
230
    # TODO(vish): the shell scripts could all be done in python
 
231
    utils.execute("sh genvpn.sh %s %s" %
 
232
                  (project_id, _vpn_cert_subject(project_id)))
 
233
    with open(csr_fn, "r") as csrfile:
 
234
        csr_text = csrfile.read()
 
235
    (serial, signed_csr) = sign_csr(csr_text, project_id)
 
236
    with open(crt_fn, "w") as crtfile:
 
237
        crtfile.write(signed_csr)
 
238
    os.chdir(start)
 
239
 
 
240
 
 
241
def sign_csr(csr_text, project_id=None):
 
242
    if not FLAGS.use_project_ca:
 
243
        project_id = None
 
244
    if not project_id:
 
245
        return _sign_csr(csr_text, ca_folder())
 
246
    _ensure_project_folder(project_id)
 
247
    project_folder = ca_folder(project_id)
 
248
    return _sign_csr(csr_text, ca_folder(project_id))
138
249
 
139
250
 
140
251
def _sign_csr(csr_text, ca_folder):
141
252
    tmpfolder = tempfile.mkdtemp()
142
 
    csrfile = open("%s/inbound.csr" % (tmpfolder), "w")
 
253
    inbound = os.path.join(tmpfolder, "inbound.csr")
 
254
    outbound = os.path.join(tmpfolder, "outbound.csr")
 
255
    csrfile = open(inbound, "w")
143
256
    csrfile.write(csr_text)
144
257
    csrfile.close()
145
 
    logging.debug("Flags path: %s" % ca_folder)
 
258
    LOG.debug(_("Flags path: %s"), ca_folder)
146
259
    start = os.getcwd()
147
260
    # Change working dir to CA
148
261
    os.chdir(ca_folder)
149
 
    utils.runthis("Signing cert: %s",
150
 
                  "openssl ca -batch -out %s/outbound.crt "
151
 
                  "-config ./openssl.cnf -infiles %s/inbound.csr" %
152
 
                  (tmpfolder, tmpfolder))
 
262
    utils.execute("openssl ca -batch -out %s -config "
 
263
                  "./openssl.cnf -infiles %s" % (outbound, inbound))
 
264
    out, _err = utils.execute("openssl x509 -in %s -serial -noout" % outbound)
 
265
    serial = out.rpartition("=")[2]
153
266
    os.chdir(start)
154
 
    with open("%s/outbound.crt" % (tmpfolder), "r") as crtfile:
155
 
        return crtfile.read()
 
267
    with open(outbound, "r") as crtfile:
 
268
        return (serial, crtfile.read())
156
269
 
157
270
 
158
271
def mkreq(bits, subject="foo", ca=0):
160
273
    req = M2Crypto.X509.Request()
161
274
    rsa = M2Crypto.RSA.gen_key(bits, 65537, callback=lambda: None)
162
275
    pk.assign_rsa(rsa)
163
 
    # Should not be freed here
164
 
    rsa = None
 
276
    rsa = None  # should not be freed here
165
277
    req.set_pubkey(pk)
166
278
    req.set_subject(subject)
167
279
    req.sign(pk, 'sha512')
225
337
# IN THE SOFTWARE.
226
338
# http://code.google.com/p/boto
227
339
 
228
 
 
229
340
def compute_md5(fp):
230
341
    """
231
342
    :type fp: file