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

move configuratoin parameters into config file; add extra sanity checks

This commit is contained in:
Seth Schoen
2012-07-19 23:19:39 -07:00
parent 8cd2b1e66d
commit 90f4b4daeb
3 changed files with 50 additions and 15 deletions

28
server-ca/CONFIG.py Normal file
View File

@@ -0,0 +1,28 @@
# The name that the server expects to be referred to by.
chocolate_server_name = "ca.theobroma.info"
# The shortest length in bits of an acceptable RSA modulus.
min_keysize = 2048
# The number of bits of hashcash that a client must provide with
# a new request.
difficulty = 23
# The number of seconds that the server asks the client to wait, at
# a time, when a request is still being processed.
polldelay = 4
# The maximum number of subject names in a request.
max_names = 20
# The maximum size in bytes of a CSR.
max_csr_size = 20480
# The expiry times of sessions, challenges, and hashcash, in seconds.
maximum_session_age = 100
maximum_challenge_age = 600
hashcash_expiry = 60*60
# Extra names that the CA refuses to issue for, apart from those in
# the blacklist table in the database.
extra_name_blacklist = ["eff.org", "www.eff.org"]

View File

@@ -14,6 +14,8 @@ import hashlib
import blacklists
# we can use temp() to get tempfiles to pass to OpenSSL subprocesses.
from CONFIG import min_key_size
forbidden_moduli = blacklists.forbidden_moduli()
forbidden_names = blacklists.forbidden_names()
@@ -45,7 +47,7 @@ def goodkey(key):
"""Does this public key comply with our CA policy?"""
key = str(key)
bits = modulusbits(key)
if bits and bits >= 2000 and not blacklisted(key):
if bits and bits >= min_key_size and not blacklisted(key):
return True
else:
return False

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python
import web, redis, time, binascii
import web, redis, time, binascii, re
import CSR
import hashcash
from CSR import M2Crypto
@@ -8,11 +8,9 @@ from Crypto import Random
from chocolate_protocol_pb2 import chocolatemessage
from google.protobuf.message import DecodeError
MaximumSessionAge = 100 # seconds, to demonstrate session timeout
MaximumChallengeAge = 600 # to demonstrate challenge timeout
HashcashExpiry = 60*60
difficulty = 23 # bits of hashcash required with new requests
from CONFIG import chocolate_server_name, min_keysize, difficulty, polldelay
from CONFIG import max_names, max_csr_size, maximum_session_age
from CONFIG import maximum_challenge_age, hashcash_expiry, extra_name_blacklist
try:
chocolate_server_name = open("SERVERNAME").read().rstrip()
@@ -36,10 +34,11 @@ def safe(what, s):
return False
base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
csr_ok = base64 + " =-"
# if what == "nonce":
# return s.isalnum()
if what == "recipient" or what == "hostname":
return all(c.isalnum() or c in "-." for c in s)
# This rejects domain names which don't contain ".". Although there
# are some of these which are valid Internet FQDNs, none of them
# should be subjects or recipients of Chocolate signing requests.
return re.match("^[A-Za-z0-9][A-Za-z0-9-]*(\.[A-Za-z0-9][A-Za-z0-9-]*)+$", s) is not None
elif what == "csr":
return all(all(c in csr_ok for c in line) for line in s.split("\n"))
# Note that this implies CSRs must have LF for end-of-line, not CRLF
@@ -136,7 +135,7 @@ class session(object):
def check_hashcash(self, h):
"""Is the hashcash string h valid for a request to this server?"""
if hashcash.check(stamp=h, resource=chocolate_server_name, \
bits=difficulty, check_expiration=HashcashExpiry):
bits=difficulty, check_expiration=hashcash_expiry):
# sessions.sadd returns True upon adding to a set and
# False if the item was already in the set.
return sessions.sadd("spent-hashcash", h)
@@ -186,7 +185,7 @@ class session(object):
if not (self.exists() and self.live()):
# Don't need to, or can't, kill nonexistent/already dead session
r.failure.cause = r.StaleRequest
elif self.age() > MaximumSessionAge:
elif self.age() > maximum_session_age:
# TODO: Sessions in state "done" should probably not be killed by timeout
# because they have already resulted in issuance of a cert and no further
# issuance can occur. At least, their timeout should probably be extended
@@ -236,6 +235,9 @@ class session(object):
if time.time() - timestamp > 100:
self.die(r, r.BadRequest, uri="https://ca.example.com/failures/past")
return
if len(csr) > max_csr_size:
self.die(r, r.BadCSR, uri="https://ca.example.com/failures/longcsr")
return
if not CSR.parse(csr):
self.die(r, r.BadCSR)
return
@@ -250,8 +252,11 @@ class session(object):
if len(names) == 0:
self.die(r, r.BadCSR)
return
if len(names) > max_names:
self.die(r, r.BadCSR, uri="https://ca.example.com/failures/toomanynames")
return
for san in names: # includes CN as well as SANs
if not safe("hostname", san) or not CSR.can_sign(san):
if not safe("hostname", san) or not CSR.can_sign(san) or san in extra_name_blacklist:
# TODO: Is there a problem including client-supplied data in the URL?
self.die(r, r.CannotIssueThatName, uri="https://ca.example.com/failures/name?%s" % san)
return
@@ -262,7 +267,7 @@ class session(object):
# do what the daemon does, and then return the challenges instead
# of returning proceed.
r.proceed.timestamp = int(time.time())
r.proceed.polldelay = 4
r.proceed.polldelay = polldelay
def handleexistingsession(self, m, r):
if m.request.IsInitialized():
@@ -278,7 +283,7 @@ class session(object):
# If we're in makechallenge or issue, tell the client to come back later.
if state == "makechallenge" or state == "issue":
r.proceed.timestamp = int(time.time())
r.proceed.polldelay = 4
r.proceed.polldelay = polldelay
return
# If we're in testchallenge, tell the client about the challenges and their
# current status.