|
| 1 | +''' |
| 2 | +Copyright (c) 2015 Jesse Peterson |
| 3 | +Licensed under the MIT license. See the included LICENSE.txt file for details. |
| 4 | +''' |
| 5 | + |
| 6 | +from flask import Blueprint, Response, render_template, request, make_response |
| 7 | +from flask import redirect |
| 8 | +# Response, request, current_app, abort, make_response |
| 9 | +from .pki.ca import get_ca, PushCertificate |
| 10 | +from .pki.x509 import CertificateRequest |
| 11 | +from .models import (Certificate as DBCertificate, |
| 12 | + PrivateKey as DBPrivateKey, |
| 13 | + CertificateRequest as DBCertificateRequest) |
| 14 | +from .database import db_session |
| 15 | +import json |
| 16 | +from base64 import b64encode |
| 17 | +import urllib2 |
| 18 | +from M2Crypto import BIO, SMIME, X509, m2 |
| 19 | +import datetime |
| 20 | + |
| 21 | +MDMCERT_REQ_URL = 'https://mdmcert.download/api/v1/signrequest' |
| 22 | + |
| 23 | +# PLEASE! Do not take this key and use it for another product/project. It's |
| 24 | +# only for Commandment's use. If you'd like to get your own (free!) key |
| 25 | +# contact the mdmcert.download administrators and get your own key for your |
| 26 | +# own project/product. We're trying to keep statistics on which products are |
| 27 | +# requesting certs (per Apple T&C). Don't force Apple's hand and |
| 28 | +# ruin it for everyone! |
| 29 | +MDMCERT_API_KEY = 'b742461ff981756ca3f924f02db5a12e1f6639a9109db047ead1814aafc058dd' |
| 30 | + |
| 31 | +CERT_REQ_TYPE = 'mdmcert.pushcert' |
| 32 | + |
| 33 | +def submit_mdmcert_request(email, pem_csr, pem_enc): |
| 34 | + mdmcert_dict = { |
| 35 | + 'csr': b64encode(pem_csr), |
| 36 | + 'email': email, |
| 37 | + 'key': MDMCERT_API_KEY, |
| 38 | + 'encrypt': b64encode(pem_enc), |
| 39 | + } |
| 40 | + |
| 41 | + req = urllib2.Request( |
| 42 | + MDMCERT_REQ_URL, |
| 43 | + json.dumps(mdmcert_dict), |
| 44 | + {'Content-Type': 'application/json', |
| 45 | + 'User-Agent': 'coMmanDMent/0.1'}) |
| 46 | + |
| 47 | + f = urllib2.urlopen(req) |
| 48 | + resp = f.read() |
| 49 | + f.close() |
| 50 | + |
| 51 | + return json.loads(resp) |
| 52 | + |
| 53 | + |
| 54 | +class FixedLocationResponse(Response): |
| 55 | + # override Werkzeug default behaviour of "fixing up" once-non-compliant |
| 56 | + # relative location headers. now permitted in rfc7231 sect. 7.1.2 |
| 57 | + autocorrect_location_header = False |
| 58 | + |
| 59 | +admin_mdmcert_app = Blueprint('admin_mdmcert_app', __name__) |
| 60 | + |
| 61 | +@admin_mdmcert_app.route('/') |
| 62 | +def index(): |
| 63 | + return render_template('admin/mdmcert/new.html') |
| 64 | + |
| 65 | +@admin_mdmcert_app.route('/submit', methods=['POST']) |
| 66 | +def submit(): |
| 67 | + mdm_ca = get_ca() |
| 68 | + |
| 69 | + csr, csr_pk = CertificateRequest.with_new_private_key(CN='mdmcert.download') |
| 70 | + |
| 71 | + db_csr = DBCertificateRequest.from_x509(csr, CERT_REQ_TYPE) |
| 72 | + db_pk = DBPrivateKey.from_x509(csr_pk) |
| 73 | + |
| 74 | + db_pk.certificate_requests.append(db_csr) |
| 75 | + |
| 76 | + db_session.add(db_csr) |
| 77 | + db_session.add(db_pk) |
| 78 | + |
| 79 | + db_session.commit() |
| 80 | + |
| 81 | + email = request.form.get('email') |
| 82 | + |
| 83 | + res = submit_mdmcert_request(email, csr.to_pem(), mdm_ca.get_cacert().to_pem()) |
| 84 | + print res.get('result') |
| 85 | + if res.get('result') != 'success': |
| 86 | + return 'Error! ' + res.get('reason', '') |
| 87 | + |
| 88 | + return 'Submitted!' |
| 89 | + |
| 90 | +from binascii import unhexlify |
| 91 | + |
| 92 | +@admin_mdmcert_app.route('/upload_encr', methods=['POST']) |
| 93 | +def upload_encr(): |
| 94 | + mdm_ca = get_ca() |
| 95 | + |
| 96 | + upl_encrypt = unhexlify(request.files['upload_encr_file'].stream.read()) |
| 97 | + |
| 98 | + s = SMIME.SMIME() |
| 99 | + |
| 100 | + bio = BIO.MemoryBuffer(upl_encrypt) |
| 101 | + |
| 102 | + s.load_key_bio( |
| 103 | + BIO.MemoryBuffer(mdm_ca.get_private_key().to_pem()), |
| 104 | + BIO.MemoryBuffer(mdm_ca.get_cacert().to_pem())) |
| 105 | + |
| 106 | + p7 = SMIME.PKCS7(m2.pkcs7_read_bio_der(bio._ptr())) |
| 107 | + |
| 108 | + out = s.decrypt(p7) |
| 109 | + |
| 110 | + response = make_response(out) |
| 111 | + response.headers['Content-Type'] = 'application/octet-stream' |
| 112 | + response.headers['Content-Disposition'] = 'attachment; filename=mdm_signed_request.%s.plist.b64' % datetime.datetime.now().strftime('%Y%m%d_%H%M%S') |
| 113 | + return response |
| 114 | + |
| 115 | +@admin_mdmcert_app.route('/upload_cert', methods=['POST']) |
| 116 | +def upload_cert(): |
| 117 | + pcert = PushCertificate.from_pem(request.files['upload_cert_file'].stream.read()) |
| 118 | + |
| 119 | + cert_modulus = pcert._get_pubkey().get_modulus() |
| 120 | + |
| 121 | + q = db_session.query(DBCertificateRequest).filter(DBCertificateRequest.req_type == CERT_REQ_TYPE) |
| 122 | + for cr in q: |
| 123 | + req = cr.to_x509() |
| 124 | + if req._get_pubkey().get_modulus() == cert_modulus: |
| 125 | + db_cert = DBCertificate.from_x509(pcert, 'mdm.pushcert') |
| 126 | + |
| 127 | + db_pk = cr.privatekeys[0] |
| 128 | + |
| 129 | + db_pk.certificates.append(db_cert) |
| 130 | + |
| 131 | + db_session.add(db_cert) |
| 132 | + |
| 133 | + db_session.commit() |
| 134 | + |
| 135 | + return redirect('/admin/certificates', Response=FixedLocationResponse) |
| 136 | + |
| 137 | + |
| 138 | + return 'no matching CSR found' |
0 commit comments