def _read(fdptr, data, dataLength): """ The SecureTransport SSL read callback. @param fdptr: reference value added via SSLSetConnection @type fdptr: ffi(int *) @param data: buffer to read data into @type data: ffi(void *) @param dataLength: length of the buffer to read into, and on return the actual length of data read @type dataLength: ffi(size_t *) """ # Get the associated L{Connection} object engine_id = ffi.cast("int *", fdptr)[0] engine = Connection.engines[engine_id] # If there are bytes pending in the L{Connection}s buffer, read as much # of those as possible, or return errSSLWouldBlock dlen = dataLength[0] rdata = ffi.buffer(data, dlen) if engine.getBytes(rdata, dlen) is None: dataLength[0] = 0 return security.errSSLWouldBlock return 0
def connect(self): """ Create the SecureTransport SSLContextRef object and initialize it. """ self.ctx = security.SSLCreateContext(ffi.NULL, security.kSSLClientSide if self.is_client else security.kSSLServerSide, security.kSSLStreamType) minVersion = None for option, minValue in ( (OP_NO_SSLv2, security.kSSLProtocol3), (OP_NO_SSLv3, security.kTLSProtocol1), (OP_NO_TLSv1, security.kTLSProtocol11), (OP_NO_TLSv1_1, security.kTLSProtocol12), (OP_NO_TLSv1_2, security.kTLSProtocol12), # TLS1.2 is the highest supported right now ): if option in self.context.options: minVersion = minValue if minVersion is not None: security.SSLSetProtocolVersionMin(self.ctx, minVersion) # Make sure we have a reference back to this L{Connection} in the SecureTransport callbacks self.connref = ffi.new("int *", self.engine_id) err = security.SSLSetConnection(self.ctx, ffi.cast("SSLConnectionRef", self.connref)) if err: self.shutdown() raise Error(err) # Setup the actual SecureTransport callbacks err = security.SSLSetIOFuncs(self.ctx, self._read, self._write) if err: self.shutdown() raise Error(err) # Must have a certificate identity if we are a server if not self.is_client and self.context.identity is None: self.shutdown() raise Error("No certificate") # Must have a peer name if we are a client if self.is_client and not self.context.peerName: self.shutdown() raise Error("No peer name set with client connection.") elif self.is_client: # Always set the client peer name for proper certificate validation err = security.SSLSetPeerDomainName(self.ctx, self.context.peerName, len(self.context.peerName)) if err: self.shutdown() raise Error(err) # Add the certificate if self.context.identity is not None: certs = CFArrayRef.fromList([self.context.identity]) err = security.SSLSetCertificate(self.ctx, certs.ref()) if err: self.shutdown() raise Error(err)
def _write(fdptr, data, dataLength): """ The SecureTransport SSL write callback. @param fdptr: reference value added via SSLSetConnection @type fdptr: ffi(int *) @param data: buffer to write @type data: ffi(const void *) @param dataLength: length of the data to write, and on return the actual length of data written @type dataLength: ffi(size_t *) """ # Get the associated L{Connection} object engine_id = ffi.cast("int *", fdptr)[0] engine = Connection.engines[engine_id] # We always write everything in one go dlen = dataLength[0] wdata = ffi.buffer(data, dlen) engine.protocol.transport.write(wdata[:]) return 0