1
0
mirror of https://github.com/certbot/certbot.git synced 2026-01-26 07:41:33 +03:00
Files
certbot/client-webserver/client.py

133 lines
3.5 KiB
Python
Executable File

#!/usr/bin/env python
from chocolate_protocol_pb2 import chocolatemessage
import M2Crypto
import urllib2, os, grp, pwd, sys, time, random, sys, hashlib, subprocess
# It is OK to use the upstream M2Crypto here instead of our modified
# version.
difficulty = 23 # bits of hashcash to generate
def sha256(m):
return hashlib.sha256(m).hexdigest()
assert len(sys.argv) > 1 or "CHOCOLATESERVER" in os.environ, "Must specify server via command line or CHOCOLATESERVER environment variable."
if len(sys.argv) > 1:
server = sys.argv[1]
else:
server = os.environ["CHOCOLATESERVER"]
upstream = "https://%s/chocolate.py" % server
if len(sys.argv) > 3:
req_file = sys.argv[2]
key_file = sys.argv[3]
else:
req_file = "req.pem"
key_file = "key.pem"
cert_file = "cert.pem" # we should use getopt to set all of these
def rsa_sign(key, data):
"""
Sign this data with this private key. For client-side use.
@type key: str
@param key: PEM-encoded string of the private key.
@type data: str
@param data: The data to be signed. Will be hashed (sha256) prior to
signing.
@return: binary string of the signature
"""
key = str(key)
data = str(data)
privkey = M2Crypto.RSA.load_key_string(key)
return privkey.sign(hashlib.sha256(data).digest(), 'sha256')
def do(m):
u = urllib2.urlopen(upstream, m.SerializeToString())
return u.read()
def decode(m):
return (chocolatemessage.FromString(m))
def init(m):
m.chocolateversion = 1
m.session = ""
def drop_privs():
nogroup = grp.getgrnam("nogroup").gr_gid
nobody = pwd.getpwnam("nobody").pw_uid
os.setgid(nogroup)
os.setgroups([])
os.setuid(nobody)
def make_request(m, csr):
m.request.recipient = server
m.request.timestamp = int(time.time())
m.request.csr = csr
hashcash_command = "hashcash -P -m -z 12 -b %d -r %s" % (difficulty, server)
hashcash = subprocess.check_output(hashcash_command.split(), preexec_fn=drop_privs, shell=False).rstrip()
if hashcash: m.request.clientpuzzle = hashcash
def sign(key, m):
m.request.sig = rsa_sign(key, ("(%d) (%s) (%s)" % (m.request.timestamp, m.request.recipient, m.request.csr)))
k=chocolatemessage()
m=chocolatemessage()
init(k)
init(m)
make_request(m, csr=open(req_file).read())
sign(open(key_file).read(), m)
print m
r=decode(do(m))
print r
while r.proceed.IsInitialized():
if r.proceed.polldelay > 60: r.proceed.polldelay = 60
print "waiting", r.proceed.polldelay
time.sleep(r.proceed.polldelay)
k.session = r.session
r = decode(do(k))
print r
if r.failure.IsInitialized():
print "Server reported failure."
sys.exit(1)
sni_todo = []
for chall in r.challenge:
print chall
if chall.type == r.DomainValidateSNI:
dvsni_nonce, dvsni_y, dvsni_ext = chall.data
sni_todo.append( (chall.name, dvsni_y, dvsni_nonce, dvsni_ext) )
print sni_todo
import sni_challenge
sni_challenge.perform_sni_cert_challenge(sni_todo, req_file, key_file)
print "waiting", 3
time.sleep(3)
r=decode(do(k))
print r
while r.challenge or r.proceed.IsInitialized():
print "waiting", 5
time.sleep(5)
k.session = r.session
r = decode(do(k))
print r
# TODO: there should be an unperform_sni_cert_challenge() here.
# TODO: there should be a deploy_cert() here.
if r.success.IsInitialized():
with open(cert_file, "w") as f:
f.write(r.success.certificate)
print "Server issued certificate; certificate written to " + cert_file
elif r.failure.IsInitialized():
print "Server reported failure."
sys.exit(1)