samplecode/tls/tlsserver/app/src/main.rs (478 lines of code) (raw):
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License..
#![allow(dead_code)]
#![allow(unused_assignments)]
extern crate sgx_types;
extern crate sgx_urts;
use sgx_types::*;
use sgx_urts::SgxEnclave;
extern crate mio;
use mio::tcp::{TcpListener, TcpStream, Shutdown};
use std::os::unix::io::AsRawFd;
use std::ffi::CString;
use std::net;
use std::str;
use std::io::{self, Read, Write};
use std::collections::HashMap;
const BUFFER_SIZE: usize = 1024;
static ENCLAVE_FILE: &'static str = "enclave.signed.so";
extern {
fn tls_server_new(eid: sgx_enclave_id_t, retval: *mut size_t,
fd: c_int, cert: *const c_char, key: *const c_char) -> sgx_status_t;
fn tls_server_read(eid: sgx_enclave_id_t, retval: *mut c_int,
session_id: size_t, buf: *mut c_void, cnt: c_int) -> sgx_status_t;
fn tls_server_write(eid: sgx_enclave_id_t, retval: *mut c_int,
session_id: size_t, buf: *const c_void, cnt: c_int) -> sgx_status_t;
fn tls_server_wants_read(eid: sgx_enclave_id_t, retval: *mut c_int,
session_id: size_t) -> sgx_status_t;
fn tls_server_wants_write(eid: sgx_enclave_id_t, retval: *mut c_int,
session_id: size_t) -> sgx_status_t;
fn tls_server_close(eid: sgx_enclave_id_t,
session_id: size_t) -> sgx_status_t;
fn tls_server_send_close(edi: sgx_enclave_id_t,
session_id: size_t) -> sgx_status_t;
}
fn init_enclave() -> SgxResult<SgxEnclave> {
let mut launch_token: sgx_launch_token_t = [0; 1024];
let mut launch_token_updated: i32 = 0;
// call sgx_create_enclave to initialize an enclave instance
// Debug Support: set 2nd parameter to 1
let debug = 1;
let mut misc_attr = sgx_misc_attribute_t {secs_attr: sgx_attributes_t { flags:0, xfrm:0}, misc_select:0};
SgxEnclave::create(ENCLAVE_FILE,
debug,
&mut launch_token,
&mut launch_token_updated,
&mut misc_attr)
}
// Token for our listening socket.
const LISTENER: mio::Token = mio::Token(0);
// Which mode the server operates in.
#[derive(Clone)]
enum ServerMode {
/// Write back received bytes
Echo,
/// Do one read, then write a bodged HTTP response and
/// cleanly close the connection.
Http,
/// Forward traffic to/from given port on localhost.
Forward(u16),
}
/// This binds together a TCP listening socket, some outstanding
/// connections, and a TLS server configuration.
struct TlsServer {
enclave_id: sgx_enclave_id_t,
server: TcpListener,
cert: CString,
key: CString,
mode: ServerMode,
connections: HashMap<mio::Token, Connection>,
next_id: usize,
}
impl TlsServer {
fn new(enclave_id: sgx_enclave_id_t, server: TcpListener, mode: ServerMode, cert: CString, key: CString) -> TlsServer {
println!("[+] TlsServer new {:?} {:?}", cert, key);
TlsServer {
enclave_id: enclave_id,
server: server,
cert: cert,
key: key,
mode: mode,
connections: HashMap::new(),
next_id: 2
}
}
fn accept(&mut self, poll: &mut mio::Poll) -> bool {
match self.server.accept() {
Ok((socket, addr)) => {
println!("Accepting new connection from {:?}", addr);
let mut tlsserver_id: usize = 0xFFFF_FFFF_FFFF_FFFF;
let retval = unsafe {
tls_server_new(self.enclave_id,
&mut tlsserver_id,
socket.as_raw_fd(),
self.cert.as_bytes_with_nul().as_ptr() as * const c_char,
self.key.as_bytes_with_nul().as_ptr() as * const c_char)
};
if retval != sgx_status_t::SGX_SUCCESS {
println!("[-] ECALL Enclave [tls_server_new] Failed {}!", retval);
return false;
}
if tlsserver_id == 0xFFFF_FFFF_FFFF_FFFF {
println!("[-] New enclave tlsserver error");
return false;
}
let mode = self.mode.clone();
let token = mio::Token(self.next_id);
self.next_id += 1;
self.connections.insert(token, Connection::new(self.enclave_id,
socket,
token,
mode,
tlsserver_id));
self.connections[&token].register(poll);
true
}
Err(e) => {
println!("encountered error while accepting connection; err={:?}", e);
false
}
}
}
fn conn_event(&mut self, poll: &mut mio::Poll, event: &mio::Event) {
let token = event.token();
if self.connections.contains_key(&token) {
self.connections
.get_mut(&token)
.unwrap()
.ready(poll, event);
if self.connections[&token].is_closed() {
self.connections.remove(&token);
}
}
}
}
/// This is a connection which has been accepted by the server,
/// and is currently being served.
///
/// It has a TCP-level stream, a TLS-level session, and some
/// other state/metadata.
struct Connection {
enclave_id: sgx_enclave_id_t,
socket: TcpStream,
token: mio::Token,
closing: bool,
mode: ServerMode,
tlsserver_id: usize,
back: Option<TcpStream>,
sent_http_response: bool,
}
/// Open a plaintext TCP-level connection for forwarded connections.
fn open_back(mode: &ServerMode) -> Option<TcpStream> {
match *mode {
ServerMode::Forward(ref port) => {
let addr = net::SocketAddrV4::new(net::Ipv4Addr::new(127, 0, 0, 1), *port);
let conn = TcpStream::connect(&net::SocketAddr::V4(addr)).unwrap();
Some(conn)
}
_ => None,
}
}
/// This used to be conveniently exposed by mio: map EWOULDBLOCK
/// errors to something less-errory.
fn try_read(r: io::Result<usize>) -> io::Result<Option<usize>> {
match r {
Ok(len) => Ok(Some(len)),
Err(e) => {
if e.kind() == io::ErrorKind::WouldBlock {
Ok(None)
} else {
Err(e)
}
}
}
}
impl Connection {
fn new(enclave_id: sgx_enclave_id_t,
socket: TcpStream,
token: mio::Token,
mode: ServerMode,
tlsserver_id: usize)
-> Connection {
let back = open_back(&mode);
Connection {
enclave_id: enclave_id,
socket: socket,
token: token,
closing: false,
mode: mode,
tlsserver_id: tlsserver_id,
back: back,
sent_http_response: false,
}
}
fn read_tls(&self, buf: &mut [u8]) -> isize {
let mut retval = -1;
let result = unsafe {
tls_server_read(self.enclave_id,
&mut retval,
self.tlsserver_id,
buf.as_ptr() as * mut c_void,
buf.len() as c_int)
};
match result {
sgx_status_t::SGX_SUCCESS => { retval as isize },
_ => {
println!("[-] ECALL Enclave [tls_server_wants_read] Failed {}!", result);
return -1;
},
}
}
fn write_tls(&self, buf: &[u8]) -> isize {
let mut retval = -1;
let result = unsafe {
tls_server_write(self.enclave_id,
&mut retval,
self.tlsserver_id,
buf.as_ptr() as * const c_void,
buf.len() as c_int)
};
match result {
sgx_status_t::SGX_SUCCESS => { retval as isize },
_ => {
println!("[-] ECALL Enclave [tls_server_wants_read] Failed {}!", result);
return -1;
},
}
}
fn wants_read(&self) -> bool {
let mut retval = -1;
let result = unsafe {
tls_server_wants_read(self.enclave_id,
&mut retval,
self.tlsserver_id)
};
match result {
sgx_status_t::SGX_SUCCESS => {},
_ => {
println!("[-] ECALL Enclave [tls_server_wants_read] Failed {}!", result);
return false;
},
}
match retval {
0 => false,
_ => true
}
}
fn wants_write(&self) -> bool {
let mut retval = -1 ;
let result = unsafe {
tls_server_wants_write(self.enclave_id,
&mut retval,
self.tlsserver_id)
};
match result {
sgx_status_t::SGX_SUCCESS => {},
_ => {
println!("[-] ECALL Enclave [tls_server_wants_write] Failed {}!", result);
return false;
},
}
match retval {
0 => false,
_ => true
}
}
fn tls_close(&self) {
unsafe {
tls_server_close(self.enclave_id, self.tlsserver_id)
};
}
fn send_close_notify(&self) {
unsafe {
tls_server_send_close(self.enclave_id, self.tlsserver_id);
}
}
/// We're a connection, and we have something to do.
fn ready(&mut self, poll: &mut mio::Poll, ev: &mio::Event) {
// If we're readable: read some TLS. Then
// see if that yielded new plaintext. Then
// see if the backend is readable too.
if ev.readiness().is_readable() {
self.do_tls_read();
self.try_plain_read();
self.try_back_read();
}
if ev.readiness().is_writable() {
self.do_tls_write();
}
if self.closing {
self.tls_close();
let _ = self.socket.shutdown(Shutdown::Both);
self.close_back();
} else {
self.reregister(poll);
}
}
/// Close the backend connection for forwarded sessions.
fn close_back(&mut self) {
if self.back.is_some() {
let back = self.back.as_mut().unwrap();
back.shutdown(Shutdown::Both).unwrap();
}
self.back = None;
}
fn do_tls_read(&mut self) {
// Read some TLS data.
println!("Read some TLS data.");
let mut buf = Vec::new();
let rc = self.read_tls(buf.as_mut_slice());
if rc == -1 {
println!("read error {:?}", rc);
self.closing = true;
return;
}
}
fn try_plain_read(&mut self) {
// Read and process all available plaintext.
let mut buf = vec![0; BUFFER_SIZE];
let rc = self.read_tls(buf.as_mut_slice());
if rc == -1 {
println!("plaintext read failed: {:?}", rc);
self.closing = true;
return;
}
buf.resize(rc as usize, 0);
if !buf.is_empty() {
println!("plaintext read {:?} {:? }", buf.len(), buf);
self.incoming_plaintext(&buf);
}
}
fn try_back_read(&mut self) {
if self.back.is_none() {
return;
}
println!("try back read");
// Try a non-blocking read.
let mut buf = [0u8; BUFFER_SIZE];
let maybe_len = {
let back = self.back.as_mut().unwrap();
let rc = try_read(back.read(&mut buf));
if rc.is_err() {
println!("backend read failed: {:?}", rc);
self.closing = true;
return;
}
rc.unwrap()
};
// If we have a successful but empty read, that's an EOF.
// Otherwise, we shove the data into the TLS session.
match maybe_len {
Some(len) if len == 0 => {
println!("back eof");
self.closing = true;
}
Some(len) => {
self.write_all(&buf[..len]).unwrap();
}
None => {}
};
}
/// Process some amount of received plaintext.
fn incoming_plaintext(&mut self, buf: &[u8]) {
match self.mode {
ServerMode::Echo => {
self.write_all(buf).unwrap();
}
ServerMode::Http => {
self.send_http_response_once();
}
ServerMode::Forward(_) => {
self.back.as_mut().unwrap().write_all(buf).unwrap();
}
}
}
fn send_http_response_once(&mut self) {
println!("send_http_response_once");
let response = b"HTTP/1.0 200 OK\r\nConnection: close\r\n\r\nHello world from rustls tlsserver\r\n";
if !self.sent_http_response {
self.write_all(response)
.unwrap();
self.sent_http_response = true;
self.send_close_notify();
}
}
fn do_tls_write(&mut self) {
let buf = Vec::new();
let rc = self.write_tls(buf.as_slice());
if rc == -1 {
println!("write failed {:?}", rc);
self.closing = true;
return;
}
}
fn register(&self, poll: &mut mio::Poll) {
poll.register(&self.socket,
self.token,
self.event_set(),
mio::PollOpt::level() | mio::PollOpt::oneshot())
.unwrap();
if self.back.is_some() {
poll.register(self.back.as_ref().unwrap(),
self.token,
mio::Ready::readable(),
mio::PollOpt::level() | mio::PollOpt::oneshot())
.unwrap();
}
}
fn reregister(&self, poll: &mut mio::Poll) {
poll.reregister(&self.socket,
self.token,
self.event_set(),
mio::PollOpt::level() | mio::PollOpt::oneshot())
.unwrap();
if self.back.is_some() {
poll.reregister(self.back.as_ref().unwrap(),
self.token,
mio::Ready::readable(),
mio::PollOpt::level() | mio::PollOpt::oneshot())
.unwrap();
}
}
/// What IO events we're currently waiting for,
/// based on wants_read/wants_write.
fn event_set(&self) -> mio::Ready {
let rd = self.wants_read();
let wr = self.wants_write();
if rd && wr {
mio::Ready::readable() | mio::Ready::writable()
} else if wr {
mio::Ready::writable()
} else {
mio::Ready::readable()
}
}
fn is_closed(&self) -> bool {
self.closing
}
}
/// We implement `io::Write` and pass through to the TLS session
impl io::Write for Connection {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
Ok(self.write_tls(bytes) as usize)
}
// unused
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl io::Read for Connection {
fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
Ok(self.read_tls(bytes) as usize)
}
}
fn main() {
let enclave = match init_enclave() {
Ok(r) => {
println!("[+] Init Enclave Successful {}!", r.geteid());
r
},
Err(x) => {
println!("[-] Init Enclave Failed {}!", x.as_str());
return;
},
};
println!("[+] Test tlsclient in enclave, start!");
let addr: net::SocketAddr = "0.0.0.0:8443".parse().unwrap();
let listener = TcpListener::bind(&addr).expect("cannot listen on port");
let cert = CString::new("end.fullchain").unwrap();
let key = CString::new("end.rsa").unwrap();
let mut poll = mio::Poll::new().unwrap();
poll.register(&listener,
LISTENER,
mio::Ready::readable(),
mio::PollOpt::level()).unwrap();
let mut tlsserv = TlsServer::new(enclave.geteid(), listener, ServerMode::Echo, cert, key);
println!("[+] TlsServer new success!");
let mut events = mio::Events::with_capacity(256);
'outer: loop {
poll.poll(&mut events, None)
.unwrap();
for event in events.iter() {
match event.token() {
LISTENER => {
if !tlsserv.accept(&mut poll) {
break 'outer;
}
}
_ => tlsserv.conn_event(&mut poll, &event)
}
}
}
println!("[+] Test tlsServer in enclave, done!");
enclave.destroy();
}