Code Monkey home page Code Monkey logo

rust-native-tls's Introduction

rust-native-tls

Documentation

An abstraction over platform-specific TLS implementations.

Specifically, this crate uses SChannel on Windows (via the schannel crate), Secure Transport on macOS (via the security-framework crate), and OpenSSL (via the openssl crate) on all other platforms.

Installation

# Cargo.toml
[dependencies]
native-tls = "0.2"

Usage

An example client looks like:

extern crate native_tls;

use native_tls::TlsConnector;
use std::io::{Read, Write};
use std::net::TcpStream;

fn main() {
    let connector = TlsConnector::new().unwrap();

    let stream = TcpStream::connect("google.com:443").unwrap();
    let mut stream = connector.connect("google.com", stream).unwrap();

    stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
    let mut res = vec![];
    stream.read_to_end(&mut res).unwrap();
    println!("{}", String::from_utf8_lossy(&res));
}

To accept connections as a server from remote clients:

extern crate native_tls;

use native_tls::{Identity, TlsAcceptor, TlsStream};
use std::fs::File;
use std::io::{Read};
use std::net::{TcpListener, TcpStream};
use std::sync::Arc;
use std::thread;

fn main() {
    let mut file = File::open("identity.pfx").unwrap();
    let mut identity = vec![];
    file.read_to_end(&mut identity).unwrap();
    let identity = Identity::from_pkcs12(&identity, "hunter2").unwrap();

    let acceptor = TlsAcceptor::new(identity).unwrap();
    let acceptor = Arc::new(acceptor);

    let listener = TcpListener::bind("0.0.0.0:8443").unwrap();

    fn handle_client(stream: TlsStream<TcpStream>) {
        // ...
    }

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                let acceptor = acceptor.clone();
                thread::spawn(move || {
                    let stream = acceptor.accept(stream).unwrap();
                    handle_client(stream);
                });
            }
            Err(e) => { /* connection failed */ }
        }
    }
}

License

rust-native-tls is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0), with portions covered by various BSD-like licenses.

See LICENSE-APACHE, and LICENSE-MIT for details.

rust-native-tls's People

Contributors

alexcrichton avatar algermissen avatar azdlowry avatar b8591340 avatar blackbeam avatar blameomar avatar blyxxyz avatar byron avatar cyang1 avatar daxpedda avatar edelangh avatar fanatid avatar jbtrystram avatar jonhoo avatar kanerogers avatar kazk avatar kornelski avatar lesnyrumcajs avatar marnixkuijs avatar metajack avatar nickbabcock avatar rust1248 avatar scottschroeder avatar sfackler avatar sgasse avatar sgg avatar simlay avatar sstecko avatar tamird avatar vladikoff avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rust-native-tls's Issues

Don't create a temporary keychain to decode Pkcs12

Keychains, other than the login one automatically become locked after a set period of time by default. If the binary is still running, the user will be presented with a password dialogue that they didn't expect for a keychain they didn't remember creating. It's possible to decode Pkcs12 without interacting with the system keychain at all using SecPKCS12Import.

Allow trusted certificates to be registered

This is straighforward for OpenSSL and Secure Transport, but I'm not sure how to do it with SChannel. We'll need to support both the addition of certs to the existing set as well as replacing the trusted set entirely.

Getting the cert out of PEM into a CERT_CONTEXT looks to be doable via CryptStringToBinary and CertCreateCertificateContext, but I still need to figure it out from there.

cc @retep998 - do you know how SChannel would do this?

(OpenSSL) Server rejects client: "tlsv1 alert unknown ca"

I'm a little stuck. I've tried a million different things, but I keep getting this error on linux (using openssl 1.0.2 in Docker with debian:jessie-backports, and also fails in travis-ci).

rust-openssl: v0.9.7
native-tls: v0.1.1

The error is this:

Failure(Ssl(ErrorStack([Error { code: 336151576, library: "SSL routines", function: "SSL3_READ_BYTES", reason: "tlsv1 alert unknown ca" }])))

I've tried a whole bunch of different things like:

let pkcs12 = native_tls::Pkcs12::from_der(&server_pkcs12_der, "mypassword").expect("Pkcs12::from_der");
    let mut tls = TlsAcceptor::builder(pkcs12).expect("build with pkcs12 failed");

    #[cfg(target_os = "linux")]
    {
      let mut openssl_builder = tls.builder_mut();
      let mut openssl_ctx_builder = openssl_builder.builder_mut();
      let mut mode = openssl::ssl::SslVerifyMode::empty();
      // mode.insert(SSL_VERIFY_PEER);
      // mode.insert(SSL_VERIFY_FAIL_IF_NO_PEER_CERT);

      openssl_ctx_builder.set_verify(mode);
      openssl_ctx_builder.set_default_verify_paths().unwrap();
      openssl_ctx_builder.cert_store_mut().set_default_paths().unwrap();
    }

    let tls = tls.build().expect("tls build failed");

but nothing seems to change this. Any tips? The default code on macOS (i.e. without that linux block) works without a problem. So I don't think is key format issues or anything. Full code is here:

https://github.com/bluejekyll/trust-dns/blob/dns_over_tls/client/src/tls/tls_stream.rs#L236

recent build failure for reference:

https://travis-ci.org/bluejekyll/trust-dns/jobs/200925570

Certificate::from_pem()?

Is there a deeper reason that I am not seeing behind Certificate not implementing from_pem() but just from_der()?

pub fn from_der(buf: &[u8]) -> Result<Certificate, Error> {

To me it looks like a matter of just adding the couple of lines analog from_der() - so I think I might be missing something??

Support cipher suite selection

OpenSSL's the most flexible here, and SChannel is the least. The cross-platform solution will probably look something like an SChannel style enum of algorithms that will be unioned:

pub enum Algorithm {
    Sha256,
    Aes,
    Dhe,
    Rsa,
    ...
}

If you then call

client_builder.supported_algorithms(&[Algorithm::Rsa, Algorithm::Aes, Algorithm::Sha256, Algorithm::Dhe])

would mean all ciphers that use RSA or DHE for key exchange, AES as a bulk cipher, and SHA256 as a hash algorithm would be enabled. That'd translate in SChannel to

cred_builder.supported_algorithms(&[Algorithm::Rsa, Algorithm::Aes, Algorithm::Sha256. Algorithm::Dhe])

and in OpenSSL to (note that we have to generate the cartesian product of key exchange, bulk cipher and hash algorithms)

ctx.set_cipher_suite("RSA+AES+SHA256:DHE+AES+SHA256:@STRENGTH")

and in Secure Transport to a somewhat complicated dance where we load supported ciphers and filter them through the provided algorithms:

let supported = ctx.supported_ciphers();
let enabled = supported.into_iter().filter(|c| suite_supported(c, algorithms)).collect();
ctx.set_enabled_ciphers(&enabled);

One interesting question is what to do if no algorithm in a category is specified (e.g. just &[Algorithm::Aes, Algorithm::Dhe] is passed). Should that mean that no suites match and no connections will work, or that all algorithms of that type can be used?

An alternative design would be to have separate enums for each algorithm type:

client_builder.supported_algorithms(&[KeyExchange::Rsa, KeyExchange::Dhe], &[BulkCipher::Aes], &[Hash::Sha256]);

It would be a bit more clear, but more verbose as well.

Couldn't add root certificate on ubuntu

Hi. add_root_certificate(ca) method call is working on mac but not on linux. It's failing with this error

ErrorStack([Error { code: 185057381, library: "x509 certificate routines", function: "X509_STORE_add_cert", reason: "cert already in hash table", file: "x509_lu.c", line: 361

get client's certificate from server's TlsStream?

Is there a way to get the client's certificate from a native_tls::TlsStream on the server's side? I couldn't find a method to do so. It seems like there should be a 'peer_certificate' accessor or something like that as there is on openssl::SslRef (available via openssl::SslStream::ssl).

release 0.2

current released native-tls has a dependency on openssl ^0.9.23, which is too old for openssl 1.1 and tls 1.3. any chance for an update?

Implement Debug for TlsConnector

This is just a minor detail, but besides allowing derive(Debug) on things holding a connector, it could also be useful to see whether some danger options have been set, and possibly any certs/identities.

Add support for construction TlsStream from raw stream

Is it possible to add a function to convert a raw stream into a TlsStream without performing a handshake.
Since TlsStream is not clone, we can use something like from_raw() to convert an already established connection to a TlsStream.

Thanks.

Library has no ciphers

I am trying to connect to a server with a self signed certificate. Whilst doing so from a C++/Qt application everything works as expected. I can connect to the server and call several functions.

I use the same API from a Apache2.4/PHP5.6 backend. When I try to simply connect to the same server using the same certificate on the client side I always get
The OpenSSL library reported an error: error:140A90A1:SSL routines:SSL_CTX_new:library has no ciphers:../ssl/ssl_lib.c:2519:

Additoinally: everything runs on the same host: the application server, the C++/Qt application and the Apache2.4/PHP5.6 backend.

I have read that calling SSL_library_init(); might help, but from my point of view we are not even able to call this method. I also couldn't find an equivalent in openssl* functions in PHP.

access to certificates used in a TlsStream

Once a TlsStream is set up, there is no way to get at the information about the handshake. Access to the certificates and cyphers used would be useful, e.g. in a system where tracking of the certificates that are used a requirement.

Support protocol selection

OpenSSL makes this a bit annoying since the options to disable TLSv1.1 and TLSv1.2 in modern OpenSSL have the same value as some options to enable some weird internal testing mode on OpenSSL 0.9.8.

2 variables "stream"

    let connector = TlsConnector::builder().unwrap().build().unwrap();

    let stream = TcpStream::connect("google.com:443").unwrap();
    let mut stream = connector.connect("google.com", stream).unwrap();

    stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();

why stream 2 times, what's that supposed to mean?

Is it possible to use this crate with static MUSL?

I know that it sounds stupid, since the idea of this crate is to use the native TLS, but I just want to have a confirmation. Is it possible to use with with static MUSL? The reason why I am asking is that Reqwest uses this crate, and if the answer is no, I'm unable to use Reqwest with my crate.

Unable to build git master on Linux (OpenSSL binding)

On a clean Ubuntu 16.04.4 32-bit (the problem seems to happen on 64-bit Linux as well) VM I installed:

$ sudo apt install gcc pkg-config libssl-dev

Project:

# Cargo.toml
...
[dependencies]
native-tls = { git = "https://github.com/sfackler/rust-native-tls" }

main.rs is populated with the example client from the native-tls README.

The build cannot find SslOptions::NO_SSL_V2 (Did you mean NO_SSLV2) or SslOptions:NO_TLSV1_0 (Did you mean NO_TLSV1_1)?

OpenSSL version:

$ openssl version
OpenSSL 1.0.2g  1 Mar 2016

Confirmed the problem exists on stable Rust 1.26.0 and nightly 2018-05-25.

ALPN support

At the recent all-hands we discussed TLS-in-Rust a bit and one of the nice features to add to this library would be ALPN support, especially in support of HTTP/2. For the backends native-tls has:

  • openssl crate already has ALPN support
  • security-framework does not have ALPN support. This may be available in the upcoming high sierra OSX though? And this may be available through private APIs in historical releases of OSX? It may be possible to write a shim that works with all this.
  • schannel doesn't have ALPN support but it's suspected that it does indeed exist in Windows, we just need to bind it.

server closed session with no notification

Hi, this is probably a very noob question and might be an error from openSSL and not neccesarily this crate, but I don't know where else to look for help.

I'm building a web server using hyper v0.11, tokio and i decided to add TLS to it. Code below

fn spawnReactors(RouteService: ArcRouter) -> io::Result<Vec<ReactorAlias>> {
	let mut reactors = Vec::new(); // ignore this.
	let routeService = Arc::new(RouteService);  // ignore this.
	
	for _ in 0..num_cpus::get() * 2 {  // ignore this.
		let reactor = Reactor::new();  // ignore this.
		reactors.push(reactor.clone());  // ignore this.
		let routeService = routeService.clone();   // ignore this.

		thread::spawn(move || { // ignore this.
			let mut core = Core::new().expect("Could not start event loop");// tokio_core::reactor::Core;
			let handle = core.handle();
			let http: Http<Chunk> = Http::new(); //hyper::server::Http;
			let http = Arc::new(http);

			core.run(ReactorHandler {
				handler: || {
					let mut reactor = reactor.lock().unwrap();
					for (socket, _peerAddr) in reactor.peers.drain(..) { // socket is of type tokio_tls::AcceptAsync<tokio_core::net::TcpStream>
						let future = futureFactory(socket, http.clone(), routeService.clone());
						handle.spawn(future); // run future on the event loop.
					}
					reactor.taskHandle = Some(task::current());
				},
			}).expect("Could not spawn threads!");
		});
	}

	Ok(reactors)
}

fn futureFactory(io: AcceptAsync<TcpStream>, http: Arc<Http<Chunk>>, s: Arc<ArcRouter>) -> impl Future<Item=(), Error=()>
{
	io.map(move |socket: tokio::net::TcpStream| {
		http.serve_connection(socket, s.clone()) // i suspect that this is the problem? perhaps `serve_connection` is closing the socket abruptly for some unkown reason?
	})
		.map(|_| ())
		.map_err(|err| {
			println!("{}", err) // whenever a request comes in this prints "server closed session with no notification" 
		})
}

I'm not entirely sure why i am getting this error and i know its probably not from this crate, but please any help would be greatly appreciated. Thanks ๐Ÿ˜„

a debug version of the error is

Error { code: -9816, message: "server closed session with no notification" }

Certificates from .pem

X509 certificates can currently be created using the function x509::X509::from_der(). I propose adding a similar method to create an X509 certificate from a .pem file.

I would be willing to submit a PR for this functionality, if someone could give me a little direction on which way to proceed.

I think we would use this method from the openssl crate. However, the current code in rust-native-tls uses rust-security-framework, which uses the native macOS stack in preference to openssl.

My questions are

  1. Is this functionality something that is desirable in rust-native-tls, from your perspective?
  2. Which code path is invoked on non-macOS platforms? Which of these need to be changed?

Thanks

Krishna

No way to detect connection closure on the remote side?

I'm reading std::io::Read and native_tls::TlsStream documentation and I'm confused about detecting connection closure on the remote side.

It seems to me that I can't rely on read() return value being Ok(0) to consider that the connection is closed, because it may be possible that bytes were read but they were used for maintaining the TLS stream rather than the the actual data sent by the remote process using the TLS stream. In which case read() would return Ok(0) because the documentation says n <= buf.len() should hold, and buf won't be filled at all because received data is used for maintaining the TLS stream.

If this is true, then there's no way to detect when a connection is closed on the remote side using TlsStream.

Arguably, Reads design is not great here. Because of n <= buf.len() invariant read() can't report "data received but used to maintain the underlying stream" (e.g. TLS lifecycle).

Am I missing anything? Would it be possible to add another method to TlsStream for checking if the connection is closed? (of course this would only work if the underlying stream returns Ok(0) in this case which is not always the case but works with TcpStream)

[Question] confused about HandshakeError::Interrupted

Sorry this is not really an issue but a question..

I'm trying to understand how to use native-tls with non-blocking sockets (more specifically, TcpStream). TlsConnector::connect returns HandshakeError::Interrupted, but it doesn't say what it needs to do, reading or writing. So I'm not sure what signal to wait for (i.e. readiness event).

Assuming I'm not the only one it may be a good idea to add a word or two about this in the documentation..

Thanks..

Switch `TlsAcceptor::builder` input from PKCS#12 to PKCS#8 + cert list

For background, see https://unmitigatedrisk.com/?p=543 and https://www.cs.auckland.ac.nz/~pgut001/cryptlib/faq.html#Q5.

Although it seems simple for rust-native-tls to use PKCS#12 as its standard format for server key/certificate configuration, it is actually quite problematic.

First, PKCS#12 cryptography is stone-aged (3DES, RC2, etc.). This is a huge burden on any crypto library that itself wasn't forged in the stone ages of cryptography. Also, it is common for implementations to use way too few rounds of PBKDF2 (or PKBDF1), making the password-based encryption limited value unless the password itself is a strong random key.

Secondly, the interop is limited due to poor defaults and lack of following recommendations. For example, the AES-CBC encryption that the latest IETF spec recommends isn't supported by most (all?) versions of Windows. As another example, many versions of OpenSSL (and, IIRC, NSS) use RC2 as the default (in some cases only) encryption format.

Thirdly, it is pretty rare for private keys and certificate chains to be in PKCS#12 format. Usually they are in a collection of unencrypted PEM files. Even in the case where the TLS server does want to encrypt the private key key, usually that encryption is done using a key management system that (in my experience) isn't based on PKCS#12. For example, the ACME protocol (Let's Encrypt's certificate distribution protocol) defaults to PEM-based formats; see https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-6.4.2.

Fourth, PKCS#12 is unnecessarily difficult to implement correctly and safely, and difficult to test and verify.

I propose instead that the constructor for TlsAcceptor be changed to accept an unencrypted PKCS#8 private key and a list of certificates (maybe just a list of DER-encoded certificates in &[u8] slices). Then support for encrypted key/cert management, including PKCS#12, and also hopefully including better systems than PKCS#12, can be provided by separate crates.

Allow Backend Customization

Let's say I've created an openssl SslAcceptor because I want customizations (initialize from pem format, use mozilla_modern/intermediate, etc). I still want to use this crate's TlsAcceptor because this abstraction is used in higher level crates like tokio-tls -- and it's a good abstraction. The only problem is that it looks like rust_native_tls::imp::openssl::TlsAcceptor, which as far as I know is what I'm looking for, isn't exposed to users.

Is there a way to inject customizations depending on the backend?

Error in macOS 10.12.2 for p12 cert which works in Win/Linux

Hi,

I'm using this with great success on Win and Linux. So, thanks! Unfortunately though, on macOS Sierra, the p12 identity file I've created with the documented openssl command just gives me:

Tls(Error { code: -25264, message: "MAC verification failed during PKCS12 import (wrong password?)" })

I tried recreating the .p12 using the current macOS openssl (0.9.8zh), just in case it was a compatibility problem, but while the file hashes differed, I got exactly the same error message.

I'm using a self-signed cert, no intermediate chain, and an empty password. I'm using it via hyper-native-tls and my code looks like:

let ssl = match NativeTlsServer::new("devkey/certificate.pfx", "") {
    Ok(server) => server,
    Err(e) => { println!("{:?}", e); panic!("Error loading ssl cert") }
};

My lock file says hyper-native-tls 0.2.1 and native-tls 0.1.1. Let me know if I can provide you with any more info, or run any further tests for you โ€“ I can test on all platforms.

Thanks,
Matt.

Memory leak in TlsConnectorBuilder::add_root_certificate

I think I found a memory leak. Code to reproduce:

extern crate native_tls;

use std::fs::File;
use std::io::Read;
use std::path::Path;
use native_tls::{Certificate, TlsConnector};

fn main() {
    println!("Hello, world!");

    let mut cert_bytes = Vec::new();
    File::open(&Path::new("cert.der"))
        .expect("Could not open cert.der")
        .read_to_end(&mut cert_bytes)
        .expect("Could not read cert.der");

    let cert = Certificate::from_der(&cert_bytes).unwrap();

    let mut builder = TlsConnector::builder().unwrap();
    builder.add_root_certificate(cert).unwrap();
    let connector = builder.build();

    // All resources should be dropped here
}

Place a DER formatted certificate called cert.der in your current directory. Build with --release, then run valgrind on it:

memtest$ cargo build --release
    Finished release [optimized] target(s) in 0.0 secs
memtest$ valgrind --leak-check=full target/release/memtest 
==10181== Memcheck, a memory error detector
==10181== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==10181== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==10181== Command: target/release/memtest
==10181== 
Hello, world!
==10181== 
==10181== HEAP SUMMARY:
==10181==     in use at exit: 2,344 bytes in 48 blocks
==10181==   total heap usage: 27,300 allocs, 27,252 frees, 2,134,572 bytes allocated
==10181== 
==10181== 2,344 (328 direct, 2,016 indirect) bytes in 1 blocks are definitely lost in loss record 32 of 32
==10181==    at 0x4C2CEDF: malloc (vg_replace_malloc.c:299)
==10181==    by 0x51FD639: CRYPTO_zalloc (in /usr/lib/libcrypto.so.1.1)
==10181==    by 0x5131D07: ??? (in /usr/lib/libcrypto.so.1.1)
==10181==    by 0x512F772: ??? (in /usr/lib/libcrypto.so.1.1)
==10181==    by 0x51302BA: ASN1_item_ex_d2i (in /usr/lib/libcrypto.so.1.1)
==10181==    by 0x513033B: ASN1_item_d2i (in /usr/lib/libcrypto.so.1.1)
==10181==    by 0x110E78: openssl::x509::X509::from_der (in /tmp/memtest/target/release/memtest)
==10181==    by 0x110B31: native_tls::Certificate::from_der (in /tmp/memtest/target/release/memtest)
==10181==    by 0x110436: memtest::main (in /tmp/memtest/target/release/memtest)
==10181==    by 0x110772: std::rt::lang_start::{{closure}} (in /tmp/memtest/target/release/memtest)
==10181==    by 0x113227: {{closure}} (rt.rs:59)
==10181==    by 0x113227: _ZN3std9panicking3try7do_call17hc2d31ff468fce3beE.llvm.A6DD7AAE (panicking.rs:479)
==10181==    by 0x12895E: __rust_maybe_catch_panic (lib.rs:102)
==10181== 
==10181== LEAK SUMMARY:
==10181==    definitely lost: 328 bytes in 1 blocks
==10181==    indirectly lost: 2,016 bytes in 47 blocks
==10181==      possibly lost: 0 bytes in 0 blocks
==10181==    still reachable: 0 bytes in 0 blocks
==10181==         suppressed: 0 bytes in 0 blocks
==10181== 
==10181== For counts of detected and suppressed errors, rerun with: -v
==10181== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

The leak does not occur if I comment out the add_root_certificate line.

Not sure if this is in the native-tls or the openssl crate.

Add ability to disable verification

I know, I know, it's horrible. It is also a reality that many people deal with this, and so it would be useful to add a way to disable verification. It can and probably should be a terrifying long name, very_insecure_disable_verification or what-have-you.

cc seanmonstar/reqwest#15

invalid purpose

When using NativeTLS for STARTTLS within SMTP, I've been getting OpenSSL "certificate validation failed" errors from well known mail servers (like gmail, outlook, and fastmail). See lettre/lettre#198. Is it possible that NativeTLS is using OpenSSL in a way that presumes the purpose such that SMTP/STARTTLS would not validate?

Could Certificate be clonable?

If I need to set up multiple connectors that all use the same CA certificate that I provide, it feels icky to parse the certificate from a .der file over and over again. I don't know the insides of the Certificate, but I think that if it could, it should implement Clone.

Would it be possible to support TlsStream<TcpStream>::try_clone?

Hello! At Faraday, we've been using native-tls in a number of applications, and we're very happy with it. We've been working on a pull request for the Rust amqp crate which would switch it from using OpenSSL to native-tls.

However, in one critical place, amqp relies on TcpStream::try_clone(), or the OpenSSL equivalent. It would probably require an overhaul to avoid this. In particular, we need to be able to send writes while another thread waits on reads.

I notice that there is no try_clone method or equivalent for TlsStream<TcpStream>. Is this because of Windows or Mac portability restrictions? How much work would it take to implement this? We're happy to spend some time working on a PR, but we figured we should ask first. :-)

Thank you for any advice you can provide, and for native-tls!

How to deserialize Pkcs12 from openssl::Pkcs12's serialization?

I have TLS working on my server where I've built a Pkcs12 (.pfx) file from the key/cert generated by Let's Encrypt. That works fine.

But now I also want my server to be able to generate its own signed cert in memory on server boot. I use rust-openssl to generate a key/cert pair and then DER-serialize it in memory, but I'm having trouble deserializing it to native_tls::Pkcs12 so that I can initialize my serve with it.

  • I'm on OSX.
  • I took the key/cert generation in generate_pair() from the docs' example. Ideally I would change it so that it uses the same parameters as CLI openssl's keygen, but I'm still researching that.
  • Why doesn't openssl::Pkcs12::from_der() take a password as an argument? It decodes the archive without one.

Anyways, this snippet demonstrates that native_tls is unable to deserialize the pfx file that rust-openssl generated.

Alternatively, is there a better solution for get the server to generate and use its own self-signed cert?

extern crate native_tls;
extern crate openssl;

use openssl::x509::{self, X509, X509Name, X509Ref};
use openssl::pkey::{PKey, PKeyRef};
use openssl::hash::MessageDigest;
use openssl::rsa::Rsa;

use openssl::pkcs12::Pkcs12Builder;
use openssl::pkcs12::Pkcs12;

// Generates a key/cert pair
fn generate_pair() -> (PKey, x509::X509) {
    let rsa = Rsa::generate(2048).unwrap();
    let key = PKey::from_rsa(rsa).unwrap();

    let mut name = X509Name::builder().unwrap();
    name.append_entry_by_nid(openssl::nid::COMMONNAME, "example.com").unwrap();
    let name = name.build();

    let mut builder = X509::builder().unwrap();
    builder.set_version(2).unwrap();
    builder.set_subject_name(&name).unwrap();
    builder.set_issuer_name(&name).unwrap();
    builder.set_pubkey(&key).unwrap();
    builder.sign(&key, MessageDigest::sha256()).unwrap();

    let cert: openssl::x509::X509 = builder.build();
    
    (key, cert)
}

fn main() {
    let (key, cert) = generate_pair();

    // Let's turn out key/cert pair into a pkcs12 archive with a password of "secret"
    const PASSWORD: &str = "secret";

    let pkcs12 = openssl::pkcs12::Pkcs12::builder()
        .build(password, "friendly_name", &*key, &cert)
        .unwrap();

    // The DER-encoded bytes of the archive
    let der = pkcs12.to_der().unwrap();

    // openssl's Pkcs12 struct can deserialize the round-trip.
    let pkcs12 = openssl::pkcs12::Pkcs12::from_der(&der).unwrap();

    // But native_tls' Pkcs12 cannot.
    let native_tls_pfx = native_tls::Pkcs12::from_der(&der, PASSWORD).unwrap();
    // (Fails)
}

On OSX, the error is:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error { code: -25257, message: "Unknown format in import." }'

On Linux, the error is:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Ssl(ErrorStack([Error { code: 218570907, library: "asn1 encoding routines", function: "ASN1_get_object", reason: "too long", file: "asn1_lib.c", line: 147 }, Error { code: 218529894, library: "asn1 encoding routines", function: "ASN1_CHECK_TLEN", reason: "bad object header", file: "tasn_dec.c", line: 1185 }, Error { code: 218595386, library: "asn1 encoding routines", function: "ASN1_ITEM_EX_D2I", reason: "nested asn1 error", file: "tasn_dec.c", line: 219, data: "Type=ASN1_TIME" }, Error { code: 218640442, library: "asn1 encoding routines", function: "ASN1_TEMPLATE_NOEXP_D2I", reason: "nested asn1 error", file: "tasn_dec.c", line: 697, data: "Field=notBefore, Type=X509_VAL" }, Error { code: 218640442, library: "asn1 encoding routines", function: "ASN1_TEMPLATE_NOEXP_D2I", reason: "nested asn1 error", file: "tasn_dec.c", line: 697, data: "Field=validity, Type=X509_CINF" }, Error { code: 218640442, library: "asn1 encoding routines", function: "ASN1_TEMPLATE_NOEXP_D2I", reason: "nested asn1 error", file: "tasn_dec.c", line: 697, data: "Field=cert_info, Type=X509" }, Error { code: 218919022, library: "asn1 encoding routines", function: "ASN1_item_unpack", reason: "decode error", file: "asn_pack.c", line: 205 }, Error { code: 587686002, library: "PKCS12 routines", function: "PKCS12_parse", reason: "parse error", file: "p12_kiss.c", line: 132 }]))', /checkout/src/libcore/result.rs:916:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Parallelism causes server unit tests to occasionally flake on OSX

Pretty stumped here..

If I add an additional unit test on a fresh checkout of master, I start getting two test failures:

---- test::server stdout ----
	thread 'test::server' panicked at 'Failure(Error { code: -9806, message: "connection closed via error" })', src/test.rs:76
stack backtrace:
   1:        0x109c13cbc - std::sys::imp::backtrace::tracing::imp::write::hb7ff6d5abd996ef4
   2:        0x109c16f9e - std::panicking::default_hook::{{closure}}::h53094adb345230b0
   3:        0x109c16b43 - std::panicking::default_hook::h5acf019c6eaf66af
   4:        0x109c17457 - std::panicking::rust_panic_with_hook::h8943f907023b7818
   5:        0x109c172b4 - std::panicking::begin_panic::h946820df60a04b85
   6:        0x109c17222 - std::panicking::begin_panic_fmt::h5face8a00b51b583
   7:        0x109b94c66 - native_tls::test::server::h3847131659cdb976
   8:        0x109bc70ce - <F as test::FnBox<T>>::call_box::h5c3bbb3b38d2930a
   9:        0x109c183ba - __rust_maybe_catch_panic
  10:        0x109bbb5c8 - std::panicking::try::do_call::h988206980d2b27fc
  11:        0x109c183ba - __rust_maybe_catch_panic
  12:        0x109bc230e - <F as alloc::boxed::FnBox<A>>::call_box::h2654ce5ce2ab7b8e
  13:        0x109c16664 - std::sys::imp::thread::Thread::new::thread_start::h4ad0b40513420e9c
  14:     0x7fff94e8aaaa - _pthread_body
  15:     0x7fff94e8a9f6 - _pthread_start

---- test::server_tls11_only stdout ----
	thread 'test::server_tls11_only' panicked at 'Failure(Error { code: -9806, message: "connection closed via error" })', src/test.rs:116

Removing the test causes things to pass again. Reproduced on 10.12.3.

This is the test I added for posterity:

fn connect_bad_hostname_ignored2() {
    let builder = p!(TlsConnector::builder());
    let builder = p!(builder.build());
    let s = p!(TcpStream::connect("google.com:443"));
    builder.danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication(s).unwrap();
}

(identical to an existing test but with a 2 in its name)

Building for iOS includes OpenSSL

Steps to reproduce (on an iOS machine):

  1. Setup
$ cargo new iostest
$ cd iostest
$ echo 'native-tls = "*"' >> Cargo.toml
$ echo '[lib]' >> Cargo.toml
$ echo 'crate-type = ["cdylib", "staticlib"]' >> Cargo.toml
  1. Building for Mac OS
$ cargo build

This works and includes security-framework-sys.

  1. Building for iOS
$ cargo install cargo-lipo
$ cargo lipo --release --targets armv7-apple-ios
   Compiling foreign-types-shared v0.1.1
   Compiling cc v1.0.4
   Compiling libc v0.2.35
   Compiling openssl v0.9.23
   Compiling bitflags v0.9.1
   Compiling pkg-config v0.3.9
   Compiling lazy_static v1.0.0
   Compiling foreign-types v0.3.2
   Compiling openssl-sys v0.9.24
error: failed to run custom build command for `openssl-sys v0.9.24`
process didn't exit successfully: `/Users/jenkins/Projects/iostest/target/release/build/openssl-sys-f2b704e6ad58beb2/build-script-build` (exit code: 101)
--- stdout
cargo:rerun-if-env-changed=ARMV7_APPLE_IOS_OPENSSL_LIB_DIR
cargo:rerun-if-env-changed=OPENSSL_LIB_DIR
cargo:rerun-if-env-changed=ARMV7_APPLE_IOS_OPENSSL_INCLUDE_DIR
cargo:rerun-if-env-changed=OPENSSL_INCLUDE_DIR
cargo:rerun-if-env-changed=ARMV7_APPLE_IOS_OPENSSL_DIR
cargo:rerun-if-env-changed=OPENSSL_DIR
run pkg_config fail: "Cross compilation detected. Use PKG_CONFIG_ALLOW_CROSS=1 to override"

--- stderr
thread 'main' panicked at '

Could not find directory of OpenSSL installation, and this `-sys` crate cannot
proceed without this knowledge. If OpenSSL is installed and this crate had
trouble finding it,  you can set the `OPENSSL_DIR` environment variable for the
compilation process.

If you're in a situation where you think the directory *should* be found
automatically, please open a bug at https://github.com/sfackler/rust-openssl
and include information about your system as well as this message.

    $HOST = x86_64-apple-darwin
    $TARGET = armv7-apple-ios
    openssl-sys = 0.9.24

', /Users/jenkins/.cargo/registry/src/github.com-1ecc6299db9ec823/openssl-sys-0.9.24/build.rs:210:4
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Cargo exited unsuccessfully: exit code: 101

Somehow here it tries to include OpenSSL. Any idea why this line seems to be ignored? Or is it another target_os?

In rust-sodium, the detection is done like this:

https://github.com/maidsafe/rust_sodium/blob/93907c8a2221664b113c8cef15a3139105c2a437/rust_sodium-sys/build.rs#L561-L570

CC @king6cong @azdlowry

Support LibreSSL

There are some distros which do not have packages for OpenSSL at all, but which do provide LibreSSL. LibreSSL is a fork of OpenSSL in which much legacy code has been removed and which has been reorganized to be easier to maintain. According to this Reddit thread some versions of LibreSSL would work due to some amount of compatibility with OpenSSL, but that is not guaranteed.

Since it looks like LibreSSL is the "native tls" on certain platforms, like Void Linux, it would probably be useful to explicitly support and test it.

Alternatively, providing a "non-native TLS" option, like rustls might make it possible for users who don't have any of the native TLS options to use projects that depend on this, without having to have each downstream project add support for two different TLS crates.

feature = "dummy"

Can rust-native-tls crate have feature = "dummy"? When this option is enabled, client code can be compiled with native_tls crate, but any call results in error.

Why.

HTTP client or server implementation may have optional TLS. Even if it is enabled, there's runtime switch that enables TLS.

Some people want to avoid compiling OpenSSL (or whateven implementation native-tls uses), because they don't use TLS.

So I need to provide a feature in my library to disable TLS.

Currently I need to have a hundred conditionals in the library code and two builds to verify that code works with and without TLS.

If native-tls crate had an option to effectively disable TLS, I'd just forward that feature flag to native-tls crate.

Automatically run openssl-probe?

The location of the trust root isn't consistent across distributions. A distro's own OpenSSL is configured to look in the right place but if you're statically linking to make a portable binary things aren't going to automatically work. You can use openssl-probe to track down the right location but that isn't done automatically in native-tls right now.

We could unconditionally run it but it seems like it could produce some weird behavior when you are dynamically linking to the system OpenSSL. We could maybe make it a Cargo feature or a function you can call optionally?

cc @jwilm

How to check if `native_tls::Error` caused by WouldBlock?

In a native_tls application we're getting an error with description "The underlying stream reported an error" and cause description "operation would block". Looking at the source code, it seems like we're hitting https://github.com/sfackler/rust-openssl/blob/master/openssl/src/ssl/error.rs#L48 . I need to somehow check for this case and but native_tls abstracts over TLS library error types and as far as I can see it's not currently possible to check if a native_tls::Error is caused by a WouldBlock error.

I'm wondering if it's there's a way to do that that I'm missing, or if it makes sense to add such a method to native_tls::Error. Any ideas?

about iOS support

To enable security-framework working on iOS : #30

unfortunately, this commit 304b00e breaks for iOS because of:
use self::security_framework::os::macos::keychain::{self, KeychainSettings};
which is only available on macOS.

So I created a PR: kornelski/rust-security-framework#41 to make rust-native-tls working on iOS.

There seems a lack of concern for iOS in rust-native-tls, I wonder the status of support for iOS in both rust-native-tls and rust-security-framework. I hope we can make iOS a first-class citizen that is supported.

And I don't know the difference between security-framework on iOS and macOS, but only found a macOS module here: https://github.com/sfackler/rust-security-framework/tree/master/security-framework/src/os

Thanks ๐Ÿ˜ƒ

Example code/tests for mutual authentication.

I want to use native tls for client authentication by providing the

  1. client cert
  2. client key, and
  3. a root ca-chain.cert.pem (the root+intermediate) certificate.
    I combined 1,2 and 3 with the command mentioned on https://docs.rs/native-tls/0.1.2/native_tls/struct.Pkcs12.html#method.from_der, and used identity method on Pkcs12 to add it. But my example code fails with certificate verify failed.
on an `Err` value: Error { repr: Custom(Custom { kind: Other, error: StringError("Ssl(ErrorStack([Error { code: 336134278, library: \"SSL routines\", function: \"ssl3_get_server_certificate\", reason: \"certificate verify failed\", file: \"s3_clnt.c\", line: 1264 }]))") }) }', /checkout/src/libcore/result.rs:860
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Do i also have to use add_root_certificate method ?

The server is an nginx server with verify client settings configured. I have tested my certs and the tls setup with other library and they are working fine.

It would be good to have code example or test case for mutual authentication, i.e., both server and client verifying each other.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.