From: Markus Triska Date: Sun, 7 Jun 2020 16:12:29 +0000 (+0200) Subject: ADDED: Encrypted client connections in library(sockets) via new option tls/1. X-Git-Tag: v0.8.127~51^2 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=7b30daf88d9ab80e43e154aff3a591a77a19774d;p=scryer-prolog.git ADDED: Encrypted client connections in library(sockets) via new option tls/1. Use tls(true) to negotiate an encrypted network connection via TLS. --- diff --git a/Cargo.toml b/Cargo.toml index 13acfe34..4abfc25a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,3 +42,4 @@ ripemd160 = "0.8.0" sha3 = "0.8.2" blake2 = "0.8.1" openssl = { version = "0.10.29", features = ["vendored"] } +native-tls = "0.2.4" diff --git a/README.md b/README.md index 022cc560..2a631129 100644 --- a/README.md +++ b/README.md @@ -409,6 +409,8 @@ The modules that ship with Scryer Prolog are also called Probabilistic predicates and random number generators. * [`sockets`](src/prolog/lib/sockets.pl) Predicates for opening and accepting TCP connections as streams. + TLS negotiation is performed via the option `tls(true)` in + `socket_client_open/3`, yielding secure encrypted connections. * [`crypto`](src/prolog/lib/crypto.pl) Cryptographically secure random numbers and hashes, HMAC-based key derivation (HKDF), password-based key derivation (PBKDF2), diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index fe816cc0..1d901068 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -635,7 +635,7 @@ impl SystemClauseType { ("$set_seed", 1) => Some(SystemClauseType::SetSeed), ("$skip_max_list", 4) => Some(SystemClauseType::SkipMaxList), ("$sleep", 1) => Some(SystemClauseType::Sleep), - ("$socket_client_open", 7) => Some(SystemClauseType::SocketClientOpen), + ("$socket_client_open", 8) => Some(SystemClauseType::SocketClientOpen), ("$socket_server_open", 3) => Some(SystemClauseType::SocketServerOpen), ("$socket_server_accept", 7) => Some(SystemClauseType::SocketServerAccept), ("$socket_server_close", 1) => Some(SystemClauseType::SocketServerClose), diff --git a/src/prolog/lib/sockets.pl b/src/prolog/lib/sockets.pl index a006d6a2..108ac9b9 100644 --- a/src/prolog/lib/sockets.pl +++ b/src/prolog/lib/sockets.pl @@ -6,7 +6,16 @@ current_hostname/1]). :- use_module(library(error)). +:- use_module(library(lists)). +parse_socket_options_(tls(TLS), tls-TLS) :- + must_be(boolean, TLS), !. +parse_socket_options_(Option, OptionPair) :- + builtins:parse_stream_options_(Option, OptionPair). + +parse_socket_options(Options, OptionValues, Stub) :- + DefaultOptions = [alias-[], eof_action-eof_code, reposition-false, tls-false, type-text], + builtins:parse_options_list(Options, parse_socket_options_, DefaultOptions, OptionValues, Stub). socket_client_open(Addr, Stream, Options) :- ( var(Addr) -> @@ -23,10 +32,10 @@ socket_client_open(Addr, Stream, Options) :- ; throw(error(type_error(socket_address, Addr), socket_client_open/3)) ), - builtins:parse_stream_options(Options, - [Alias, EOFAction, Reposition, Type], - socket_client_open/3), - '$socket_client_open'(Address, Port, Stream, Alias, EOFAction, Reposition, Type). + parse_socket_options(Options, + [Alias, EOFAction, Reposition, TLS, Type], + socket_client_open/3), + '$socket_client_open'(Address, Port, Stream, Alias, EOFAction, Reposition, Type, TLS). socket_server_open(Addr, ServerSocket) :- diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index 6308017d..1dcbe145 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -17,6 +17,8 @@ use std::net::{Shutdown, TcpStream}; use std::ops::DerefMut; use std::rc::Rc; +use native_tls::TlsStream; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum StreamType { Binary, @@ -101,6 +103,7 @@ pub enum StreamInstance { ReadlineStream(ReadlineStream), Stdout, TcpStream(ClauseName, TcpStream), + TlsStream(ClauseName, TlsStream) } impl Drop for StreamInstance { @@ -109,6 +112,9 @@ impl Drop for StreamInstance { StreamInstance::TcpStream(_, ref mut tcp_stream) => { discard_result!(tcp_stream.shutdown(Shutdown::Both)); } + StreamInstance::TlsStream(_, ref mut tls_stream) => { + discard_result!(tls_stream.shutdown()); + } _ => { } } @@ -131,6 +137,8 @@ impl fmt::Debug for StreamInstance { &StreamInstance::Stdout => write!(fmt, "Stdout"), &StreamInstance::TcpStream(_, ref tcp_stream) => write!(fmt, "TcpStream({:?})", tcp_stream), + &StreamInstance::TlsStream(_, ref tls_stream) => + write!(fmt, "TlsStream({:?})", tls_stream), } } } @@ -410,7 +418,8 @@ impl Stream { StreamInstance::InputFile(..) => { "read" } - StreamInstance::TcpStream(..) => { + StreamInstance::TcpStream(..) | + StreamInstance::TlsStream(..) => { "read_append" } StreamInstance::OutputFile(_, _, true) => { @@ -449,6 +458,12 @@ impl Stream { Stream::from_inst(StreamInstance::TcpStream(address, tcp_stream)) } + #[inline] + pub(crate) + fn from_tls_stream(address: ClauseName, tls_stream: TlsStream) -> Self { + Stream::from_inst(StreamInstance::TlsStream(address, tls_stream)) + } + #[inline] pub(crate) fn from_file_as_output(name: ClauseName, file: File, in_append_mode: bool) -> Self { @@ -510,6 +525,7 @@ impl Stream { match self.stream_inst.0.borrow().1 { // StreamInstance::Stdin | StreamInstance::TcpStream(..) | + StreamInstance::TlsStream(..) | StreamInstance::Bytes(_) | StreamInstance::ReadlineStream(_) | StreamInstance::DynReadSource(_) | @@ -528,6 +544,7 @@ impl Stream { match self.stream_inst.0.borrow().1 { StreamInstance::Stdout | StreamInstance::TcpStream(..) + | StreamInstance::TlsStream(..) | StreamInstance::Bytes(_) | StreamInstance::OutputFile(..) => { true @@ -1036,6 +1053,9 @@ impl Read for Stream { StreamInstance::TcpStream(_, ref mut tcp_stream) => { tcp_stream.read(buf) } + StreamInstance::TlsStream(_, ref mut tls_stream) => { + tls_stream.read(buf) + } StreamInstance::ReadlineStream(ref mut rl_stream) => { rl_stream.read(buf) } @@ -1069,6 +1089,9 @@ impl Write for Stream { StreamInstance::TcpStream(_, ref mut tcp_stream) => { tcp_stream.write(buf) } + StreamInstance::TlsStream(_, ref mut tls_stream) => { + tls_stream.write(buf) + } StreamInstance::Bytes(ref mut cursor) => { cursor.write(buf) } @@ -1093,6 +1116,9 @@ impl Write for Stream { StreamInstance::TcpStream(_, ref mut tcp_stream) => { tcp_stream.flush() } + StreamInstance::TlsStream(_, ref mut tls_stream) => { + tls_stream.flush() + } StreamInstance::Bytes(ref mut cursor) => { cursor.flush() } diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index bb470f0e..7d0592bb 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -49,6 +49,8 @@ use openssl::ec::{EcGroup, EcPoint}; use openssl::bn::{BigNum, BigNumContext}; use openssl::nid::Nid; +use native_tls::TlsConnector; + pub fn get_key() -> KeyEvent { let key; enable_raw_mode().expect("failed to enable raw mode"); @@ -4517,7 +4519,30 @@ impl MachineState { Ok(tcp_stream) => { let socket_addr = clause_name!(socket_addr, indices.atom_tbl.clone()); - let mut stream = Stream::from_tcp_stream(socket_addr, tcp_stream); + + let mut stream = + { let tls = match self.store(self.deref(self[temp_v!(8)])) { + Addr::Con(h) if self.heap.atom_at(h) => { + if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] { + atom.as_str() + } else { + unreachable!() + } + } + _ => { + unreachable!() + } + }; + match tls { + "false" => { Stream::from_tcp_stream(socket_addr, tcp_stream) } + "true" => { let connector = TlsConnector::new().unwrap(); + let stream = connector.connect(socket_atom.as_str(), tcp_stream).unwrap(); + + Stream::from_tls_stream(socket_addr, stream) + } + _ => { unreachable!() } + } + }; stream.options = options; if let Some(ref alias) = &stream.options.alias {