blob: ac411d472314401670d3e9c00278655dd57598c8 [file] [log] [blame]
// Copyright (c) 2017 Baidu, Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Baidu, Inc., nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//! # Intel Protected File System API
use sgx_types::*;
use sgx_trts::error::errno;
use sgx_trts::c_str::CStr;
use core::cmp;
fn max_len() -> usize {
u32::max_value() as usize
}
unsafe fn rsgx_fopen(filename: &CStr, mode: &CStr, key: &sgx_key_128bit_t) -> SysResult<SGX_FILE> {
let file = sgx_fopen(filename.as_ptr(), mode.as_ptr(), key as * const sgx_key_128bit_t);
if file.is_null() {
Err(errno())
} else {
Ok(file)
}
}
unsafe fn rsgx_fopen_auto_key(filename: &CStr, mode: &CStr) -> SysResult<SGX_FILE> {
let file = sgx_fopen_auto_key(filename.as_ptr(), mode.as_ptr());
if file.is_null() {
Err(errno())
} else {
Ok(file)
}
}
unsafe fn rsgx_fwrite(stream: SGX_FILE, buf: &[u8]) -> SysResult<usize> {
if stream.is_null() || buf.len() == 0 {
return Err(EINVAL);
}
let write_size = cmp::min(buf.len(), max_len());
let ret_size = sgx_fwrite(buf.as_ptr() as * const c_void, 1, write_size, stream);
if ret_size != write_size {
Err(rsgx_ferror(stream))
} else {
Ok(ret_size)
}
}
unsafe fn rsgx_fread(stream: SGX_FILE, buf: &mut [u8]) -> SysResult<usize> {
if stream.is_null() || buf.len() == 0 {
return Err(EINVAL);
}
let read_size = cmp::min(buf.len(), max_len());
let ret_size = sgx_fread(buf.as_mut_ptr() as * mut c_void, 1, read_size, stream);
if ret_size != read_size {
let is_eof = try!(rsgx_feof(stream));
if is_eof == true {
Ok(ret_size)
} else {
Err(rsgx_ferror(stream))
}
} else {
Ok(ret_size)
}
}
unsafe fn rsgx_ftell(stream: SGX_FILE) -> SysResult<i64> {
if stream.is_null() {
return Err(EINVAL);
}
let pos = sgx_ftell(stream);
if pos == -1 {
Err(errno())
} else {
Ok(pos)
}
}
unsafe fn rsgx_fseek(stream: SGX_FILE, offset: i64, origin: i32) -> SysError {
if stream.is_null() {
return Err(EINVAL);
}
let ret = sgx_fseek(stream, offset, origin);
if ret == 0 {
Ok(())
} else {
Err(rsgx_ferror(stream))
}
}
unsafe fn rsgx_fflush(stream: SGX_FILE) -> SysError {
let ret = sgx_fflush(stream);
if ret == 0 {
Ok(())
} else if ret == EOF {
Err(rsgx_ferror(stream))
} else {
Err(ret)
}
}
unsafe fn rsgx_ferror(stream: SGX_FILE) -> i32 {
let mut err = sgx_ferror(stream);
if err == -1 {
err = EINVAL;
}
err
}
unsafe fn rsgx_feof(stream: SGX_FILE) -> SysResult<bool> {
if stream.is_null() {
return Err(EINVAL);
}
if sgx_feof(stream) == 1 {
Ok(true)
} else {
Ok(false)
}
}
unsafe fn rsgx_clearerr(stream: SGX_FILE) {
sgx_clearerr(stream)
}
unsafe fn rsgx_fclose(stream: SGX_FILE) -> SysError {
if stream.is_null() {
return Err(EINVAL);
}
let ret = sgx_fclose(stream);
if ret == 0 {
Ok(())
} else {
Err(EIO)
}
}
unsafe fn rsgx_fclear_cache(stream: SGX_FILE) -> SysError {
if stream.is_null() {
return Err(EINVAL);
}
let ret = sgx_fclear_cache(stream);
if ret == 0 {
Ok(())
} else {
Err(rsgx_ferror(stream))
}
}
unsafe fn rsgx_remove(filename: &CStr) -> SysError {
let ret = sgx_remove(filename.as_ptr());
if ret == 0 {
Ok(())
} else {
Err(errno())
}
}
unsafe fn rsgx_fexport_auto_key(filename: &CStr, key: &mut sgx_key_128bit_t) -> SysError {
let ret = sgx_fexport_auto_key(filename.as_ptr(), key as * mut sgx_key_128bit_t);
if ret == 0 {
Ok(())
} else {
Err(errno())
}
}
unsafe fn rsgx_fimport_auto_key(filename: &CStr, key: &sgx_key_128bit_t) -> SysError {
let ret = sgx_fimport_auto_key(filename.as_ptr(), key as * const sgx_key_128bit_t);
if ret == 0 {
Ok(())
} else {
Err(errno())
}
}
pub struct SgxFileStream {
stream: SGX_FILE
}
impl SgxFileStream {
///
/// The open function creates or opens a protected file.
///
/// # Description
///
/// open is similar to the C file API fopen. It creates a new Protected File or opens an existing
/// Protected File created with a previous call to open. Regular files cannot be opened with this API.
///
/// # Parameters
///
/// **filename**
///
/// The name of the file to be created or opened.
///
/// **mode**
///
/// The file open mode string. Allowed values are any combination of ‘r’, ‘w’ or ‘a’, with possible ‘+’
/// and possible ‘b’ (since string functions are currently not sup- ported, ‘b’ is meaningless).
///
/// **key**
///
/// The encryption key of the file. This key is used as a key derivation key, used for deriving encryption
/// keys for the file. If the file is created with open, you should protect this key and provide it as
/// input every time the file is opened.
///
/// # Requirements
///
/// Header: sgx_tprotected_fs.edl
///
/// Library: libsgx_tprotected_fs.a
///
/// # Return value
///
/// If the function succeeds, it returns a valid file pointer, which can be used by all the other functions
/// in the Protected FS API, otherwise, error code is returned.
///
pub fn open(filename: &CStr, mode: &CStr, key: &sgx_key_128bit_t) -> SysResult<SgxFileStream> {
unsafe {
rsgx_fopen(filename, mode, key).map(|f| SgxFileStream{ stream: f})
}
}
///
/// The open_auto_key function creates or opens a protected file.
///
/// # Description
///
/// open_auto_key is similar to the C file API fopen. It creates a new Protected File or opens an existing
/// Protected File created with a previous call to open_auto_key. Regular files cannot be opened with this API.
///
/// # Parameters
///
/// **filename**
///
/// The name of the file to be created or opened.
///
/// **mode**
///
/// The file open mode string. Allowed values are any combination of ‘r’, ‘w’ or ‘a’, with possible ‘+’
/// and possible ‘b’ (since string functions are currently not sup- ported, ‘b’ is meaningless).
///
/// # Requirements
///
/// Header: sgx_tprotected_fs.edl
///
/// Library: libsgx_tprotected_fs.a
///
/// # Return value
///
/// If the function succeeds, it returns a valid file pointer, which can be used by all the other functions
/// in the Protected FS API, otherwise, error code is returned.
///
pub fn open_auto_key(filename: &CStr, mode: &CStr) -> SysResult<SgxFileStream> {
unsafe {
rsgx_fopen_auto_key(filename, mode).map(|f| SgxFileStream{ stream: f})
}
}
///
/// The read function reads the requested amount of data from the file, and extends the file pointer by that amount.
///
/// # Description
///
/// read is similar to the file API fread. In case of an error, error can be called to get the error code.
///
/// # Parameters
///
/// **buf**
///
/// A pointer to a buffer to receive the data read from the file.
///
/// # Requirements
///
/// Header: sgx_tprotected_fs.edl
///
/// Library: libsgx_tprotected_fs.a
///
/// # Return value
///
/// If the function succeeds, the number of bytes read is returned (zero indicates end of file).
/// otherwise, error code is returned.
///
pub fn read(&self, buf: &mut [u8]) -> SysResult<usize> {
unsafe { rsgx_fread(self.stream, buf) }
}
///
/// The write function writes the given amount of data to the file, and extends the file pointer by that amount.
///
/// # Description
///
/// write is similar to the file API fwrite. In case of an error, error can be called to get the error code.
///
/// # Parameters
///
/// **buf**
///
/// A pointer to a buffer, that contains the data to write to the file.
///
/// # Requirements
///
/// Header: sgx_tprotected_fs.edl
///
/// Library: libsgx_tprotected_fs.a
///
/// # Return value
///
/// If the function succeeds, the number of bytes written is returned (zero indicates nothing was written).
/// otherwise, error code is returned.
///
pub fn write(&self, buf: &[u8]) -> SysResult<usize> {
unsafe { rsgx_fwrite(self.stream, buf) }
}
///
/// The tell function obtains the current value of the file position indicator for the stream pointed to by stream.
///
/// # Description
///
/// tell is similar to the C file API ftell.
///
/// # Requirements
///
/// Header: sgx_tprotected_fs.edl
///
/// Library: libsgx_tprotected_fs.a
///
/// # Return value
///
/// If the function succeeds, it returns the current value of the position indicator of the file.
/// otherwise, error code is returned.
///
pub fn tell(&self) -> SysResult<i64> {
unsafe { rsgx_ftell(self.stream) }
}
///
/// The seek function sets the current value of the position indicator of the file.
///
/// # Description
///
/// seek is similar to the C file API fseek.
///
/// # Parameters
///
/// **offset**
///
/// The new required value, relative to the origin parameter.
///
/// **origin**
///
/// The origin from which to calculate the offset (Start, ENd or Current).
///
/// # Requirements
///
/// Header: sgx_tprotected_fs.edl
///
/// Library: libsgx_tprotected_fs.a
///
/// # Return value
///
/// If the function failed, error code is returned.
///
pub fn seek(&self, offset: i64, origin: SeekFrom) -> SysError {
let whence = match origin {
SeekFrom::Start => SEEK_SET,
SeekFrom::End => SEEK_END,
SeekFrom::Current => SEEK_CUR,
};
unsafe { rsgx_fseek(self.stream, offset, whence) }
}
///
/// The flush function forces a cache flush, and if it returns successfully, it is guaranteed
/// that your changes are committed to a file on the disk.
///
/// # Description
///
/// flush is similar to the C file API fflush. This function flushes all the modified data from
/// the cache and writes it to a file on the disk. In case of an error, error can be called to
/// get the error code. Note that this function does not clear the cache, but only flushes the
/// changes to the actual file on the disk. Flushing also happens automatically when the cache
/// is full and page eviction is required.
///
/// # Requirements
///
/// Header: sgx_tprotected_fs.edl
///
/// Library: libsgx_tprotected_fs.a
///
/// # Return value
///
/// If the function failed, error code is returned.
///
pub fn flush(&self) -> SysError {
unsafe { rsgx_fflush(self.stream) }
}
///
/// The error function returns the latest operation error code.
///
/// # Description
///
/// error is similar to the C file API ferror. In case the latest operation failed because
/// the file is in a bad state, SGX_ERROR_FILE_BAD_STATUS will be returned.
///
/// # Requirements
///
/// Header: sgx_tprotected_fs.edl
///
/// Library: libsgx_tprotected_fs.a
///
/// # Return value
///
/// The latest operation error code is returned. 0 indicates that no errors occurred.
///
pub fn error(&self) -> i32 {
unsafe{ rsgx_ferror(self.stream) }
}
///
/// The is_eof function tells the caller if the file's position indicator hit the end of the
/// file in a previous read operation.
///
/// # Description
///
/// is_eof is similar to the C file API feof.
///
/// # Requirements
///
/// Header: sgx_tprotected_fs.edl
///
/// Library: libsgx_tprotected_fs.a
///
/// # Return value
///
/// true - End of file was not reached. false - End of file was reached.
///
pub fn is_eof(&self) -> bool {
unsafe { rsgx_feof(self.stream).unwrap() }
}
///
/// The clearerr function attempts to repair a bad file status, and also clears the end-of-file flag.
///
/// # Description
///
/// clearerr is similar to the C file API clearerr. This function attempts to repair errors resulted
/// from the underlying file system, like write errors to the disk (resulting in a full cache thats
/// cannot be emptied). Call error or is_eof after a call to this function to learn if it was successful
/// or not.
///
/// clearerr does not repair errors resulting from a corrupted file, like decryption errors, or from
/// memory corruption, etc.
///
/// # Requirements
///
/// Header: sgx_tprotected_fs.edl
///
/// Library: libsgx_tprotected_fs.a
///
/// # Return value
///
/// None
///
pub fn clearerr(&self) {
unsafe { rsgx_clearerr(self.stream) }
}
///
/// The clear_cache function is used for clearing the internal file cache. The function scrubs all
/// the data from the cache, and releases all the allocated cache memory.
///
/// # Description
///
/// clear_cache is used to scrub all the data from the cache and release all the allocated cache
/// memory. If modified data is found in the cache, it will be written to the file on disk before
/// being scrubbed.
///
/// This function is especially useful if you do not trust parts of your own enclave (for example,
/// external libraries you linked against, etc.) and want to make sure there is as little sensitive
/// data in the memory as possible before transferring control to the code they do not trust.
/// Note, however, that the SGX_FILE structure itself still holds sensitive data. To remove all
/// such data related to the file from memory completely, you should close the file handle.
///
/// # Requirements
///
/// Header: sgx_tprotected_fs.edl
///
/// Library: libsgx_tprotected_fs.a
///
/// # Return value
///
/// If the function failed, error code is returned.
///
pub fn clear_cache(&self) -> SysError {
unsafe { rsgx_fclear_cache(self.stream) }
}
}
///
/// The remove function deletes a file from the file system.
///
/// # Description
///
/// remove is similar to the C file API remove.
///
/// # Parameters
///
/// **filename**
///
/// The name of the file to delete.
///
/// # Requirements
///
/// Header: sgx_tprotected_fs.edl
///
/// Library: libsgx_tprotected_fs.a
///
/// # Return value
///
/// If the function failed, error code is returned.
///
pub fn remove(filename: &CStr) -> SysError {
unsafe { rsgx_remove(filename) }
}
///
/// The export_auto_key function is used for exporting the latest key used for the file encryption.
///
/// # Description
///
/// export_auto_key is used to export the last key that was used in the encryption of the file.
/// With this key you can import the file in a different enclave or system.
///
/// # Parameters
///
/// **filename**
///
/// The name of the file to be exported. This should be the name of a file created with the open_auto_key API.
///
/// # Requirements
///
/// Header: sgx_tprotected_fs.edl
///
/// Library: libsgx_tprotected_fs.a
///
/// # Return value
///
/// If the function succeeds, it returns the latest encryption key.
/// otherwise, error code is returned.
///
pub fn export_auto_key(filename: &CStr) -> SysResult<sgx_key_128bit_t> {
let mut key: sgx_key_128bit_t = Default::default();
unsafe {
rsgx_fexport_auto_key(filename, &mut key).map(|_| key)
}
}
///
/// The import_auto_key function is used for importing a Protected FS auto key file created on
/// a different enclave or platform.
///
/// # Description
///
/// import_auto_key is used for importing a Protected FS file. After this call returns successfully,
/// the file can be opened normally with export_auto_key.
///
/// # Parameters
///
/// **filename**
///
/// The name of the file to be imported. This should be the name of a file created with the
/// open_auto_key API, on a different enclave or system.
///
/// **key**
///
/// he encryption key, exported with a call to export_auto_key in the source enclave or system.
///
/// # Requirements
///
/// Header: sgx_tprotected_fs.edl
///
/// Library: libsgx_tprotected_fs.a
///
/// # Return value
///
/// otherwise, error code is returned.
///
pub fn import_auto_key(filename: &CStr, key: &sgx_key_128bit_t) -> SysError {
unsafe { rsgx_fimport_auto_key(filename, key) }
}
impl Drop for SgxFileStream {
fn drop(&mut self) {
// Note that errors are ignored when closing a file descriptor. The
// reason for this is that if an error occurs we don't actually know if
// the file descriptor was closed or not, and if we retried (for
// something like EINTR), we might close another valid file descriptor
// (opened after we closed ours.
let _ = unsafe { rsgx_fclose(self.stream) };
}
}
/// Enumeration of possible methods to seek within an I/O object.
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
pub enum SeekFrom {
/// Set the offset to the provided number of bytes.
Start,
/// Set the offset to the size of this object plus the specified number of
/// bytes.
///
/// It is possible to seek beyond the end of an object, but it's an error to
/// seek before byte 0.
End,
/// Set the offset to the current position plus the specified number of
/// bytes.
///
/// It is possible to seek beyond the end of an object, but it's an error to
/// seek before byte 0.
Current,
}