diff options
author | Narayan Desai <desai@mcs.anl.gov> | 2009-05-19 01:40:51 +0000 |
---|---|---|
committer | Narayan Desai <desai@mcs.anl.gov> | 2009-05-19 01:40:51 +0000 |
commit | 260f66263dbb3104eabb930f86bf1fff02389509 (patch) | |
tree | 08bfa8a8fd95f65ae4c5c7424988c4ba0bd34918 | |
parent | 229c86e87a277fac5756dd9c33267b4780113198 (diff) | |
download | bcfg2-260f66263dbb3104eabb930f86bf1fff02389509.tar.gz bcfg2-260f66263dbb3104eabb930f86bf1fff02389509.tar.bz2 bcfg2-260f66263dbb3104eabb930f86bf1fff02389509.zip |
SSL: Implememt client-side server cN verification
git-svn-id: https://svn.mcs.anl.gov/repos/bcfg/trunk/bcfg2@5244 ce84e21b-d406-0410-9b95-82705330c041
-rw-r--r-- | src/lib/Options.py | 6 | ||||
-rw-r--r-- | src/lib/Proxy.py | 27 | ||||
-rwxr-xr-x | src/sbin/bcfg2 | 5 |
3 files changed, 31 insertions, 7 deletions
diff --git a/src/lib/Options.py b/src/lib/Options.py index add106869..a2d9d46ca 100644 --- a/src/lib/Options.py +++ b/src/lib/Options.py @@ -217,6 +217,11 @@ CLIENT_CERT = Option('Path to SSL certificate', default=None, cmd="--ssl-cert", CLIENT_CA = Option('Path to SSL CA Cert', default=None, cmd="--ca-cert", cf=('communication', 'ca'), odesc='<ca cert>', long_arg=True) +CLIENT_SCNS = Option('list of server commonNames', default=None, cmd="--ssl-cns", + cf=('communication', 'serverCommonNames'), + odesc='<commonName1:commonName2>', cook=list_split, + long_arg=True) + SERVER_PASSWORD = Option('Communication Password', cmd='-x', odesc='<password>', cf=('communication', 'password'), default=False) INSTALL_PREFIX = Option('Installation location', cf=('server', 'prefix'), @@ -276,6 +281,7 @@ LOGGING_FILE_PATH = Option('Set path of file log', default=None, CLIENT_SERVICE_MODE = Option('Set client service mode', default='default', cmd='-s', odesc='<default|disabled|build>') + class OptionParser(OptionSet): ''' OptionParser bootstraps option parsing, diff --git a/src/lib/Proxy.py b/src/lib/Proxy.py index 2dd1b0065..3595b1099 100644 --- a/src/lib/Proxy.py +++ b/src/lib/Proxy.py @@ -27,6 +27,10 @@ has_py26 = map(int, version) >= [2, 6] __all__ = ["ComponentProxy", "RetryMethod", "SSLHTTPConnection", "XMLRPCTransport"] +class CertificateError(Exception): + def __init__(self, commonName): + self.commonName = commonName + class RetryMethod(_Method): """Method with error handling and retries built in""" log = logging.getLogger('xmlrpc') @@ -45,6 +49,10 @@ class RetryMethod(_Method): if retry == 3: self.log.error("Server failure: %s" % err) raise xmlrpclib.Fault(20, err) + except CertificateError, ce: + self.log.error("Got unallowed commonName %s from server" \ + % ce.commonName) + break except: self.log.error("Unknown failure", exc_info=1) break @@ -56,7 +64,7 @@ xmlrpclib._Method = RetryMethod class SSLHTTPConnection(httplib.HTTPConnection): def __init__(self, host, port=None, strict=None, timeout=90, key=None, - cert=None, ca=None): + cert=None, ca=None, scns=None): if not has_py26: httplib.HTTPConnection.__init__(self, host, port, strict) else: @@ -64,6 +72,7 @@ class SSLHTTPConnection(httplib.HTTPConnection): self.key = key self.cert = cert self.ca = ca + self.scns = scns if self.ca: self.ca_mode = ssl.CERT_REQUIRED else: @@ -77,20 +86,27 @@ class SSLHTTPConnection(httplib.HTTPConnection): ca_certs=self.ca, suppress_ragged_eofs=True, keyfile=self.key, certfile=self.cert) self.sock.connect((self.host, self.port)) + pc = self.sock.getpeercert() + if pc and self.scns: + scn = [x[0][1] for x in pc['subject'] if x[0][0] == 'commonName'][0] + if scn not in self.scns: + raise CertificateError, scn self.sock.closeSocket = True class XMLRPCTransport(xmlrpclib.Transport): - def __init__(self, key=None, cert=None, ca=None, use_datetime=0): + def __init__(self, key=None, cert=None, ca=None, scns=None, use_datetime=0): if hasattr(xmlrpclib.Transport, '__init__'): xmlrpclib.Transport.__init__(self, use_datetime) self.key = key self.cert = cert self.ca = ca + self.scns = scns def make_connection(self, host): host = self.get_host_info(host)[0] - http = SSLHTTPConnection(host, key=self.key, cert=self.cert, ca=self.ca) + http = SSLHTTPConnection(host, key=self.key, cert=self.cert, ca=self.ca, + scns=self.scns) https = httplib.HTTP() https._setup(http) return https @@ -134,7 +150,8 @@ class XMLRPCTransport(xmlrpclib.Transport): return u.close() -def ComponentProxy (url, user=None, password=None, key=None, cert=None, ca=None): +def ComponentProxy (url, user=None, password=None, key=None, cert=None, ca=None, + allowedServerCNs=None): """Constructs proxies to components. @@ -149,6 +166,6 @@ def ComponentProxy (url, user=None, password=None, key=None, cert=None, ca=None) newurl = "%s://%s:%s@%s" % (method, user, password, path) else: newurl = url - ssl_trans = XMLRPCTransport(key, cert, ca) + ssl_trans = XMLRPCTransport(key, cert, ca, allowedServerCNs) return xmlrpclib.ServerProxy(newurl, allow_none=True, transport=ssl_trans) diff --git a/src/sbin/bcfg2 b/src/sbin/bcfg2 index 070561dd9..337bdb888 100755 --- a/src/sbin/bcfg2 +++ b/src/sbin/bcfg2 @@ -46,7 +46,6 @@ class Client: 'quick': Bcfg2.Options.CLIENT_QUICK, 'debug': Bcfg2.Options.DEBUG, 'drivers': Bcfg2.Options.CLIENT_DRIVERS, - 'fingerprint': Bcfg2.Options.SERVER_FINGERPRINT, 'dryrun': Bcfg2.Options.CLIENT_DRYRUN, 'paranoid': Bcfg2.Options.CLIENT_PARANOID, 'bundle': Bcfg2.Options.CLIENT_BUNDLE, @@ -72,6 +71,7 @@ class Client: 'key' : Bcfg2.Options.CLIENT_KEY, 'certificate' : Bcfg2.Options.CLIENT_CERT, 'ca' : Bcfg2.Options.CLIENT_CA, + 'serverCN' : Bcfg2.Options.CLIENT_SCNS, } self.setup = Bcfg2.Options.OptionParser(optinfo) @@ -161,7 +161,8 @@ class Client: self.setup['password'], key = self.setup['key'], cert = self.setup['certificate'], - ca = self.setup['ca']) + ca = self.setup['ca'], + allowedServerCNs = self.setup['serverCN']) if self.setup['profile']: try: |