|  | # Copyright 2015 The Chromium Authors. All rights reserved. | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  |  | 
|  | """Utility library to add SSL support to the RPC server.""" | 
|  |  | 
|  | import logging | 
|  | import ssl | 
|  | import subprocess | 
|  | import tempfile | 
|  |  | 
|  | from legion.lib import common_lib | 
|  | from legion.lib.rpc import jsonrpclib | 
|  | from legion.lib.rpc import SimpleJSONRPCServer | 
|  |  | 
|  |  | 
|  | class Error(Exception): | 
|  | pass | 
|  |  | 
|  |  | 
|  | def CreateKeyFile(): | 
|  | """Creates an SSL keyfile and returns the path.""" | 
|  | keyfile = tempfile.mkstemp()[1] | 
|  | cmd = [ | 
|  | 'openssl', | 
|  | 'genrsa', | 
|  | '-out', keyfile, | 
|  | '2048' | 
|  | ] | 
|  | _RunCommand(cmd) | 
|  | return keyfile | 
|  |  | 
|  |  | 
|  | def CreateCsrFile(keyfile): | 
|  | """Creates an SSL CSR file and returns the path.""" | 
|  | csrfile = tempfile.mkstemp()[1] | 
|  | cmd = [ | 
|  | 'openssl', | 
|  | 'req', | 
|  | '-new', | 
|  | '-key', keyfile, | 
|  | '-out', csrfile, | 
|  | '-subj', '/C=NA/ST=NA/L=NA/O=Chromium/OU=Test/CN=chromium.org' | 
|  | ] | 
|  | _RunCommand(cmd) | 
|  | return csrfile | 
|  |  | 
|  |  | 
|  | def CreateCrtFile(keyfile, csrfile): | 
|  | """Creates an SSL CRT file and returns the path.""" | 
|  | crtfile = tempfile.mkstemp()[1] | 
|  | cmd = [ | 
|  | 'openssl', | 
|  | 'x509', | 
|  | '-req', | 
|  | '-days', '1', | 
|  | '-in', csrfile, | 
|  | '-signkey', keyfile, | 
|  | '-out', crtfile | 
|  | ] | 
|  | _RunCommand(cmd) | 
|  | return crtfile | 
|  |  | 
|  |  | 
|  | def CreatePemFile(): | 
|  | """Creates an SSL PEM file and returns the path.""" | 
|  | keyfile = CreateKeyFile() | 
|  | csrfile = CreateCsrFile(keyfile) | 
|  | crtfile = CreateCrtFile(keyfile, csrfile) | 
|  | pemfile = tempfile.mkstemp()[1] | 
|  | with open(keyfile) as k: | 
|  | with open(crtfile) as c: | 
|  | with open(pemfile, 'wb') as p: | 
|  | p.write('%s\n%s' % (k.read(), c.read())) | 
|  | return pemfile | 
|  |  | 
|  |  | 
|  | def _RunCommand(cmd): | 
|  | try: | 
|  | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | 
|  | except OSError as e: | 
|  | raise Error('Failed to run %s: %s' % (' '.join(cmd), e)) | 
|  | out, err = p.communicate() | 
|  | if p.returncode != 0: | 
|  | raise Error(err) | 
|  | return out | 
|  |  | 
|  |  | 
|  | class SslRpcServer(SimpleJSONRPCServer.SimpleJSONRPCServer): | 
|  | """Class to add SSL support to the RPC server.""" | 
|  |  | 
|  | def __init__(self, *args, **kwargs): | 
|  | SimpleJSONRPCServer.SimpleJSONRPCServer.__init__(self, *args, **kwargs) | 
|  | self.socket = ssl.wrap_socket(self.socket, certfile=CreatePemFile(), | 
|  | server_side=True) | 
|  |  | 
|  | @staticmethod | 
|  | def Connect(server, port=common_lib.SERVER_PORT): | 
|  | """Creates and returns a connection to an SSL RPC server.""" | 
|  | addr = 'https://%s:%d' % (server, port) | 
|  | logging.debug('Connecting to RPC server at %s', addr) | 
|  | return jsonrpclib.ServerProxy(addr, allow_none=True) |