def test_fingerprint(): """Verify that fingerprint matches what we get from openssl. To get the fingerprint of a certificate with openssl, you can do: $ openssl x509 -fingerprint -in <certfile> """ cert = """ -----BEGIN CERTIFICATE----- MIICqjCCAZKgAwIBAgIILDVF+J1HIWowDQYJKoZIhvcNAQELBQAwFDESMBAGA1UE AwwJbG9jYWxob3N0MCAXDTE0MDgyODE3MTUyM1oYDzk5OTkxMjMxMjM1OTU5WjAU MRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK AoIBAQCVPL4WfLACl/oR+RNolkwxfyxcj8IdTrzrUXmY1zBBncdHhHOPijJZHdP5 vmD4HO2kb793V4uT02gnMq+MiYr8fNui7yRYnInG3GsgwJKQTH/5objHN6JZl505 82r/aas0rDjRRc25WXGSCuPuW+h1u0f2N4PuBwN6iCmv0FDhEz7C3FgDWDSk4heY xLglX2wnkrD4nob4NXWqX7VZ5A9KYBKQPhpOczg+vtv1nd3CnU5JN9GbC3AZ+4wF Zb0xMpLDgn4T5z0TjOhJOwCbDOSsJWdC0q6UQe4ln5GubzKjPaJWVR4VXOsVuqoU q4fLed5vHSsQUc0C6qElvak0xULbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBACVZ H5UJfpvujJ5tV0Mj9Jho0f5Wf/KARTa/5NL6lbM3KPSrrOZGdnH2Hag6me3JMJ+y kpxCc5HQnhF+Ep21Y5tFo0Ex2/FRGSfxZZVL0ERjMYnJpzjbnB4S5VPYW1LB+ctL +kwNc+sc8up986zNZnzxRY5hllvmC82Bn24dCVECzy3fgczVpOSh4pLeF+sXYOA1 2ZT081GtWsEjebCndRoTtInTkqdtsSLHvznAi8YQ7lhtow/sAr5hbUaGwdrROUaq 6Z+dzh7LHrTmfqmefNGGqi+hWKSU+fxGYQ+QDUjCD5J1dfnnueeHgYIeiQ9ZjnyE f3brW92sWUeqydhqD5A= -----END CERTIFICATE----- """ expected_fingerprint = "5E:DF:14:6E:87:0F:2D:15:FD:B2:07:4E:6B:43:9A:21:84:58:F7:70" assert crypto.fingerprint(cert) == expected_fingerprint
def connect(host=C.MANAGER_HOST, port=None, tls_mode=C.MANAGER_TLS): """Connect to LabRAD and return a deferred that fires the protocol object. Args: host (str): The hostname of the manager. port (int): The tcp port of the manager. If None, use the appropriate default value based on the TLS mode. tls_mode (str): The tls mode to use for this connection. See: `labrad.constants.check_tls_mode`. Returns: twisted.internet.defer.Deferred(LabradProtocol): A deferred that will fire with the protocol once the connection is established. """ tls_mode = C.check_tls_mode(tls_mode) if port is None: port = C.MANAGER_PORT_TLS if tls_mode == 'on' else C.MANAGER_PORT if tls_mode == 'on': tls_options = crypto.tls_options(host) p = yield _factory.connectSSL(host, port, tls_options, timeout=C.TIMEOUT) returnValue(p) def do_connect(): return _factory.connectTCP(host, port, timeout=C.TIMEOUT) @inlineCallbacks def start_tls(p, cert_string=None): try: resp = yield p.sendRequest(C.MANAGER_ID, [(1L, ('STARTTLS', host))]) except Exception: raise Exception( 'Failed sending STARTTLS command to server. You should update ' 'the manager and configure it to support encryption or else ' 'disable encryption for clients. See ' 'https://github.com/labrad/pylabrad/blob/master/CONFIG.md') cert = resp[0][1] p.transport.startTLS(crypto.tls_options(host, cert_string=cert_string)) returnValue(cert) def ping(p): return p.sendRequest(C.MANAGER_ID, [(2L, 'PING')]) p = yield do_connect() is_local_connection = util.is_local_connection(p.transport) if ((tls_mode == 'starttls-force') or (tls_mode == 'starttls' and not is_local_connection)): try: cert = yield start_tls(p) except Exception: # TODO: remove this retry. This is a temporary fix to support # compatibility until TLS is fully deployed. print ('STARTTLS failed; will retry without encryption in case we ' 'are connecting to a legacy manager.') p = yield connect(host, port, tls_mode='off') print 'Connected without encryption.' returnValue(p) try: yield ping(p) except Exception: print 'STARTTLS failed due to untrusted server certificate:' print 'SHA1 Fingerprint={}'.format(crypto.fingerprint(cert)) print while True: ans = raw_input( 'Accept server certificate for host "{}"? (accept just ' 'this [O]nce; [S]ave and always accept this cert; ' '[R]eject) '.format(host)) ans = ans.lower() if ans in ['o', 's', 'r']: break else: print 'Invalid input:', ans if ans == 'r': raise p = yield do_connect() yield start_tls(p, cert) yield ping(p) if ans == 's': # save now that we know TLS succeeded, # including hostname verification. crypto.save_cert(host, cert) returnValue(p)
def connect(host=C.MANAGER_HOST, port=None, tls_mode=C.MANAGER_TLS, username=None, password=None, headless=False): """Connect to LabRAD and return a deferred that fires the protocol object. Args: host (str): The hostname of the manager. port (int): The tcp port of the manager. If None, use the appropriate default value based on the TLS mode. tls_mode (str): The tls mode to use for this connection. See: `labrad.constants.check_tls_mode`. username (str | None): The username to use when authenticating. password (str | None): The password to use when authenticating. headless (bool): Whether to use headless OAuth flow if no username or password is configured. Returns: twisted.internet.defer.Deferred(LabradProtocol): A deferred that will fire with the protocol once the connection is established. """ spawn_kw = dict(host=host, port=port, tls_mode=tls_mode, username=username, password=password, headless=headless) tls_mode = C.check_tls_mode(tls_mode) if port is None: port = C.MANAGER_PORT_TLS if tls_mode == 'on' else C.MANAGER_PORT @inlineCallbacks def authenticate(p): yield p.authenticate(username, password, headless) if tls_mode == 'on': tls_options = crypto.tls_options(host) p = yield _factory.connectSSL(host, port, tls_options, timeout=C.TIMEOUT) p.set_address(host, port) p.spawn_kw = spawn_kw yield authenticate(p) returnValue(p) @inlineCallbacks def do_connect(): p = yield _factory.connectTCP(host, port, timeout=C.TIMEOUT) p.set_address(host, port) p.spawn_kw = spawn_kw returnValue(p) @inlineCallbacks def start_tls(p, cert_string=None): try: cert = yield p._sendManagerRequest(1, ('STARTTLS', host)) except Exception: raise Exception( 'Failed sending STARTTLS command to server. You should update ' 'the manager and configure it to support encryption or else ' 'disable encryption for clients. See ' 'https://github.com/labrad/pylabrad/blob/master/CONFIG.md') p.transport.startTLS(crypto.tls_options(host, cert_string=cert_string)) returnValue(cert) @inlineCallbacks def ping(p): resp = yield p._sendManagerRequest(2, 'PING') if isinstance(resp, tuple): manager_features = set(resp[1]) else: manager_features = set() returnValue(manager_features) p = yield do_connect() is_local_connection = util.is_local_connection(p.transport) if ((tls_mode == 'starttls-force') or (tls_mode == 'starttls' and not is_local_connection)): try: cert = yield start_tls(p) except Exception: # TODO: remove this retry. This is a temporary fix to support # compatibility until TLS is fully deployed. print('STARTTLS failed; will retry without encryption in case we ' 'are connecting to a legacy manager.') p = yield connect(host, port, tls_mode='off') print('Connected without encryption.') p.manager_features = set() yield authenticate(p) returnValue(p) try: manager_features = yield ping(p) except Exception: print('STARTTLS failed due to untrusted server certificate:') print('SHA1 Fingerprint={}'.format(crypto.fingerprint(cert))) print() while True: ans = input( 'Accept server certificate for host "{}"? (accept just ' 'this [O]nce; [S]ave and always accept this cert; ' '[R]eject) '.format(host)) ans = ans.lower() if ans in ['o', 's', 'r']: break else: print('Invalid input:', ans) if ans == 'r': raise p = yield do_connect() yield start_tls(p, cert) manager_features = yield ping(p) if ans == 's': # save now that we know TLS succeeded, # including hostname verification. crypto.save_cert(host, cert) else: manager_features = yield ping(p) p.manager_features = manager_features yield authenticate(p) returnValue(p)
def getConnection(host=C.MANAGER_HOST, port=None, name="Python Client", password=None, tls=C.MANAGER_TLS): """Connect to LabRAD and return a deferred that fires the protocol object.""" tls = C.check_tls_mode(tls) if port is None: port = C.MANAGER_PORT_TLS if tls == 'on' else C.MANAGER_PORT if tls == 'on': tls_options = crypto.tls_options(host) p = yield protocol.factory.connectSSL(host, port, tls_options, timeout=C.TIMEOUT) else: def connect(): return protocol.factory.connectTCP(host, port, timeout=C.TIMEOUT) @inlineCallbacks def start_tls(p, cert_string=None): try: resp = yield p.sendRequest(C.MANAGER_ID, [(1L, ('STARTTLS', host))]) except Exception, e: raise Exception( 'Failed sending STARTTLS command to server. You should ' 'update the manager and configure it to support encryption ' 'or else disable encryption for clients. See ' 'https://github.com/labrad/pylabrad/blob/master/CONFIG.md') cert = resp[0][1] p.transport.startTLS(crypto.tls_options(host, cert_string=cert_string)) returnValue(cert) def ping(p): return p.sendRequest(C.MANAGER_ID, [(2L, 'PING')]) p = yield connect() is_local_connection = util.is_local_connection(p.transport) if ((tls == 'starttls-force') or (tls == 'starttls' and not is_local_connection)): try: cert = yield start_tls(p) except Exception: # TODO: remove this retry. This is a temporary fix to support # compatibility until TLS is fully deployed. print ('STARTTLS failed; will retry without encryption in case ' 'we are connecting to a legacy manager.') p = yield getConnection(host, port, name, password, tls='off') print 'Connected without encryption.' returnValue(p) try: yield ping(p) except Exception: print 'STARTTLS failed due to untrusted server certificate:' print 'SHA1 Fingerprint={}'.format(crypto.fingerprint(cert)) print while True: ans = raw_input( "Accept server certificate for host '{}'? " "(accept just this [O]nce; [S]ave and always " "accept this cert; [R]eject) ".format(host)) if ans.lower() in ['o', 's', 'r']: break else: print 'Invalid input:', ans if ans.lower() == 'r': raise p = yield connect() yield start_tls(p, cert) yield ping(p) if ans.lower() == 's': # save now that we know TLS succeeded, # including hostname verification. crypto.save_cert(host, cert)