Merge pull request #6 from mullvad/support-security-attributes-on-win
Support security attributes on Windows
This commit is contained in:
commit
9d213dc4ac
|
@ -11,9 +11,9 @@ tokio-core = "0.1"
|
|||
tokio-io = "0.1"
|
||||
rand = "0.3"
|
||||
mio-named-pipes = { git = "https://github.com/alexcrichton/mio-named-pipes" }
|
||||
miow = "0.2"
|
||||
miow = "0.3.2"
|
||||
log = "*"
|
||||
bytes = "0.4"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["winbase"] }
|
||||
winapi = { version = "0.3", features = ["winbase", "winnt", "accctrl", "aclapi", "securitybaseapi", "minwinbase", "winbase"] }
|
||||
|
|
175
src/lib.rs
175
src/lib.rs
|
@ -30,6 +30,17 @@ use bytes::{BufMut, Buf};
|
|||
#[cfg(windows)]
|
||||
use tokio_named_pipes::NamedPipe;
|
||||
|
||||
#[cfg(windows)]
|
||||
mod win_permissions;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub use win_permissions::SecurityAttributes;
|
||||
|
||||
#[cfg(unix)]
|
||||
mod unix_permissions;
|
||||
#[cfg(unix)]
|
||||
pub use unix_permissions::SecurityAttributes;
|
||||
|
||||
/// For testing/examples
|
||||
pub fn dummy_endpoint() -> String {
|
||||
extern crate rand;
|
||||
|
@ -57,55 +68,80 @@ pub fn dummy_endpoint() -> String {
|
|||
///
|
||||
/// fn main() {
|
||||
/// let core = Core::new().unwrap();
|
||||
/// let endpoint = Endpoint::new(dummy_endpoint(), &core.handle()).unwrap();
|
||||
/// endpoint.incoming().for_each(|(stream, _)| {
|
||||
/// println!("Connection received");
|
||||
/// future::ok(())
|
||||
/// let endpoint = Endpoint::new(dummy_endpoint());
|
||||
/// endpoint.incoming(core.handle())
|
||||
/// .expect("failed to open a pipe")
|
||||
/// .for_each(|(stream, _)| {
|
||||
/// println!("Connection received");
|
||||
/// future::ok(())
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Endpoint {
|
||||
_path: String,
|
||||
_handle: Handle,
|
||||
#[cfg(not(windows))]
|
||||
inner: tokio_uds::UnixListener,
|
||||
#[cfg(windows)]
|
||||
inner: NamedPipe,
|
||||
path: String,
|
||||
security_attributes: SecurityAttributes,
|
||||
}
|
||||
|
||||
impl Endpoint {
|
||||
/// Stream of incoming connections
|
||||
#[cfg(not(windows))]
|
||||
pub fn incoming(self) -> Incoming {
|
||||
Incoming { inner: self.inner.incoming() }
|
||||
pub fn incoming(self, handle: Handle) -> io::Result<Incoming> {
|
||||
Ok(
|
||||
Incoming { inner: self.inner(&handle)?.incoming() }
|
||||
)
|
||||
}
|
||||
|
||||
/// Stream of incoming connections
|
||||
#[cfg(windows)]
|
||||
pub fn incoming(self) -> Incoming {
|
||||
Incoming { inner: NamedPipeSupport { path: self._path, handle: self._handle.remote().clone(), pipe: self.inner } }
|
||||
pub fn incoming(mut self, handle: Handle) -> io::Result<Incoming> {
|
||||
let pipe = self.inner(&handle)?;
|
||||
Ok(
|
||||
Incoming { inner: NamedPipeSupport { path: self.path, handle: handle.remote().clone(), pipe: pipe, security_attributes: self.security_attributes} }
|
||||
)
|
||||
}
|
||||
|
||||
/// Inner platform-dependant state of the endpoint
|
||||
#[cfg(windows)]
|
||||
fn inner(p: &str, handle: &Handle) -> io::Result<NamedPipe> {
|
||||
NamedPipe::new(p, handle)
|
||||
fn inner(&mut self, handle: &Handle) -> io::Result<NamedPipe> {
|
||||
extern crate mio_named_pipes;
|
||||
use std::os::windows::io::*;
|
||||
use miow::pipe::NamedPipeBuilder;
|
||||
|
||||
let raw_handle = unsafe { NamedPipeBuilder::new(&self.path)
|
||||
.first(true)
|
||||
.inbound(true)
|
||||
.outbound(true)
|
||||
.out_buffer_size(65536)
|
||||
.in_buffer_size(65536)
|
||||
.with_security_attributes(self.security_attributes.as_ptr())?
|
||||
.into_raw_handle()};
|
||||
|
||||
let mio_pipe = unsafe { mio_named_pipes::NamedPipe::from_raw_handle(raw_handle) };
|
||||
NamedPipe::from_pipe(mio_pipe, handle)
|
||||
}
|
||||
|
||||
/// Inner platform-dependant state of the endpoint
|
||||
#[cfg(not(windows))]
|
||||
fn inner(p: &str, handle: &Handle) -> io::Result<tokio_uds::UnixListener> {
|
||||
tokio_uds::UnixListener::bind(p, handle)
|
||||
fn inner(&self, handle: &Handle) -> io::Result<tokio_uds::UnixListener> {
|
||||
tokio_uds::UnixListener::bind(&self.path, handle)
|
||||
}
|
||||
|
||||
pub fn set_security_attributes(&mut self, security_attributes: SecurityAttributes) {
|
||||
self.security_attributes = security_attributes;
|
||||
}
|
||||
|
||||
/// Returns the path of the endpoint.
|
||||
pub fn path(&self) -> &str {
|
||||
&self.path
|
||||
}
|
||||
|
||||
|
||||
/// New IPC endpoint at the given path
|
||||
/// Endpoint ready to accept connections immediately
|
||||
pub fn new(path: String, handle: &Handle) -> io::Result<Self> {
|
||||
Ok(Endpoint {
|
||||
inner: Self::inner(&path, handle)?,
|
||||
_path: path,
|
||||
_handle: handle.clone(),
|
||||
})
|
||||
pub fn new(path: String) -> Self {
|
||||
Endpoint {
|
||||
path: path,
|
||||
security_attributes: SecurityAttributes::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,7 +152,35 @@ pub struct RemoteId;
|
|||
struct NamedPipeSupport {
|
||||
path: String,
|
||||
handle: tokio_core::reactor::Remote,
|
||||
pipe: NamedPipe,
|
||||
pipe: NamedPipe,
|
||||
security_attributes: SecurityAttributes,
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl NamedPipeSupport {
|
||||
fn replacement_pipe(&mut self) -> io::Result<NamedPipe> {
|
||||
extern crate mio_named_pipes;
|
||||
|
||||
use std::os::windows::io::*;
|
||||
use miow::pipe::NamedPipeBuilder;
|
||||
|
||||
let ev_handle = &self.handle.handle().ok_or(
|
||||
io::Error::new(io::ErrorKind::Other, "Cannot spawn event loop handle")
|
||||
)?;
|
||||
|
||||
|
||||
let raw_handle = unsafe { NamedPipeBuilder::new(&self.path)
|
||||
.first(false)
|
||||
.inbound(true)
|
||||
.outbound(true)
|
||||
.out_buffer_size(65536)
|
||||
.in_buffer_size(65536)
|
||||
.with_security_attributes(self.security_attributes.as_ptr())?
|
||||
.into_raw_handle()};
|
||||
|
||||
let mio_pipe = unsafe { mio_named_pipes::NamedPipe::from_raw_handle(raw_handle) };
|
||||
NamedPipe::from_pipe(mio_pipe, ev_handle)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stream of incoming connections
|
||||
|
@ -128,27 +192,6 @@ pub struct Incoming {
|
|||
inner: NamedPipeSupport,
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn replacement_pipe(path: &str, handle: &Handle) -> io::Result<NamedPipe> {
|
||||
extern crate mio_named_pipes;
|
||||
|
||||
use std::os::windows::io::*;
|
||||
use miow::pipe::NamedPipeBuilder;
|
||||
|
||||
let raw_handle = NamedPipeBuilder::new(path)
|
||||
.first(false)
|
||||
.inbound(true)
|
||||
.outbound(true)
|
||||
.out_buffer_size(65536)
|
||||
.in_buffer_size(65536)
|
||||
.create()?
|
||||
.into_raw_handle();
|
||||
|
||||
let mio_pipe = unsafe { mio_named_pipes::NamedPipe::from_raw_handle(raw_handle) };
|
||||
|
||||
NamedPipe::from_pipe(mio_pipe, handle)
|
||||
}
|
||||
|
||||
impl Stream for Incoming {
|
||||
type Item = (IpcConnection, RemoteId);
|
||||
type Error = io::Error;
|
||||
|
@ -160,21 +203,19 @@ impl Stream for Incoming {
|
|||
Async::Ready(None) => Async::Ready(None),
|
||||
Async::NotReady => Async::NotReady,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, io::Error> {
|
||||
match self.inner.pipe.connect() {
|
||||
Ok(()) => {
|
||||
trace!("Incoming connection polled successfully");
|
||||
let handle = &self.inner.handle.handle().ok_or(
|
||||
io::Error::new(io::ErrorKind::Other, "Cannot spawn event loop handle")
|
||||
)?;
|
||||
let new_listener = self.inner.replacement_pipe()?;
|
||||
Ok(Async::Ready(Some((
|
||||
IpcConnection {
|
||||
inner: ::std::mem::replace(
|
||||
&mut self.inner.pipe,
|
||||
replacement_pipe(&self.inner.path, &handle)?,
|
||||
new_listener,
|
||||
)
|
||||
},
|
||||
RemoteId,
|
||||
|
@ -293,6 +334,8 @@ mod tests {
|
|||
|
||||
use super::Endpoint;
|
||||
use super::IpcConnection;
|
||||
#[cfg(windows)]
|
||||
use super::SecurityAttributes;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn random_pipe_path() -> String {
|
||||
|
@ -311,10 +354,10 @@ mod tests {
|
|||
let (ok_signal, ok_rx) = oneshot::channel();
|
||||
thread::spawn(|| {
|
||||
let mut core = Core::new().expect("failed to spawn an event loop");
|
||||
let endpoint = Endpoint::new(path, &core.handle()).expect("failed to open endpoint");
|
||||
let endpoint = Endpoint::new(path);
|
||||
let connections = endpoint.incoming(core.handle()).expect("failed to open up a new pipe/socket");
|
||||
ok_signal.send(()).expect("failed to send ok");
|
||||
let srv = endpoint.incoming()
|
||||
.for_each(|(stream, _)| {
|
||||
let srv = connections.for_each(|(stream, _)| {
|
||||
let (reader, writer) = stream.split();
|
||||
let buf = [0u8; 5];
|
||||
io::read_exact(reader,buf).and_then(move |(_reader, buf)| {
|
||||
|
@ -339,7 +382,7 @@ mod tests {
|
|||
let handle = core.handle();
|
||||
|
||||
let client = IpcConnection::connect(&path, &handle).expect("failed to open a client");
|
||||
let other_client = IpcConnection::connect(&path, &handle).expect("failed to open a client");
|
||||
let other_client = IpcConnection::connect(&path, &handle).expect("failed to open a client again");
|
||||
let msg = b"hello";
|
||||
|
||||
let mut rx_buf = vec![0u8; msg.len()];
|
||||
|
@ -356,4 +399,24 @@ mod tests {
|
|||
assert_eq!(rx_msg, msg);
|
||||
assert_eq!(other_rx_msg, msg);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn create_pipe_with_permissions(attr: SecurityAttributes) -> ::std::io::Result<()> {
|
||||
let mut core = Core::new().expect("Failed to spawn an event loop");
|
||||
let path = random_pipe_path();
|
||||
|
||||
let mut endpoint = Endpoint::new(path);
|
||||
endpoint.set_security_attributes(attr);
|
||||
endpoint.incoming(core.handle()).map(|_| ())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn test_pipe_permissions() {
|
||||
create_pipe_with_permissions(SecurityAttributes::empty()).expect("failed with no attributes");
|
||||
create_pipe_with_permissions(SecurityAttributes::allow_everyone_create().unwrap())
|
||||
.expect("failed with attributes for creating");
|
||||
create_pipe_with_permissions(SecurityAttributes::allow_everyone_connect().unwrap())
|
||||
.expect("failed with attributes for connecting");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
/// A NOOP struct for bringing the API between Windows and Unix up to parity. To set permissions
|
||||
/// properly on Unix, you can just use `std::os::unix::fs::PermissionsExt`.
|
||||
pub struct SecurityAttributes;
|
||||
|
||||
impl SecurityAttributes {
|
||||
pub fn empty() -> Self { SecurityAttributes }
|
||||
pub fn allow_everyone_connect() -> Self { SecurityAttributes }
|
||||
pub fn allow_everyone_create() -> Self { SecurityAttributes }
|
||||
}
|
|
@ -0,0 +1,265 @@
|
|||
use winapi::um::winnt::*;
|
||||
use winapi::um::accctrl::*;
|
||||
use winapi::um::aclapi::*;
|
||||
use winapi::um::securitybaseapi::*;
|
||||
use winapi::um::minwinbase::{LPTR, SECURITY_ATTRIBUTES, PSECURITY_ATTRIBUTES};
|
||||
use winapi::um::winbase::{LocalAlloc, LocalFree};
|
||||
use winapi::shared::winerror::ERROR_SUCCESS;
|
||||
|
||||
use std::ptr;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::marker;
|
||||
|
||||
pub struct SecurityAttributes {
|
||||
attributes: Option<InnerAttributes>,
|
||||
}
|
||||
|
||||
impl SecurityAttributes {
|
||||
pub fn empty() -> SecurityAttributes {
|
||||
SecurityAttributes { attributes: None }
|
||||
}
|
||||
|
||||
pub fn allow_everyone_connect() -> io::Result<SecurityAttributes> {
|
||||
let attributes = Some(InnerAttributes::allow_everyone(GENERIC_READ | FILE_WRITE_DATA)?);
|
||||
Ok(SecurityAttributes { attributes })
|
||||
}
|
||||
|
||||
pub fn allow_everyone_create() -> io::Result<SecurityAttributes> {
|
||||
let attributes = Some(InnerAttributes::allow_everyone(GENERIC_READ | GENERIC_WRITE)?);
|
||||
Ok(SecurityAttributes { attributes })
|
||||
}
|
||||
|
||||
pub unsafe fn as_ptr(&mut self) -> PSECURITY_ATTRIBUTES {
|
||||
match self.attributes.as_mut() {
|
||||
Some(attributes) => attributes.as_ptr(),
|
||||
None => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for SecurityAttributes {}
|
||||
|
||||
|
||||
struct Sid {
|
||||
sid_ptr: PSID
|
||||
}
|
||||
|
||||
impl Sid {
|
||||
fn everyone_sid() -> io::Result<Sid> {
|
||||
let mut sid_ptr = ptr::null_mut();
|
||||
let result = unsafe {
|
||||
AllocateAndInitializeSid(
|
||||
SECURITY_WORLD_SID_AUTHORITY.as_mut_ptr() as *mut _, 1,
|
||||
SECURITY_WORLD_RID,
|
||||
0, 0, 0, 0, 0, 0, 0,
|
||||
&mut sid_ptr)
|
||||
};
|
||||
if result == 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(Sid{sid_ptr})
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe - the returned pointer is only valid for the lifetime of self.
|
||||
unsafe fn as_ptr(&self) -> PSID {
|
||||
self.sid_ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Sid {
|
||||
fn drop(&mut self) {
|
||||
if !self.sid_ptr.is_null() {
|
||||
unsafe{ FreeSid(self.sid_ptr); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct AceWithSid<'a> {
|
||||
explicit_access: EXPLICIT_ACCESS_W,
|
||||
_marker: marker::PhantomData<&'a Sid>,
|
||||
}
|
||||
|
||||
impl<'a> AceWithSid<'a> {
|
||||
fn new(sid: &'a Sid, trustee_type: u32) -> AceWithSid<'a> {
|
||||
let mut explicit_access = unsafe { mem::zeroed::<EXPLICIT_ACCESS_W>() };
|
||||
explicit_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||||
explicit_access.Trustee.TrusteeType = trustee_type;
|
||||
explicit_access.Trustee.ptstrName = unsafe { sid.as_ptr() as *mut _ };
|
||||
|
||||
AceWithSid{
|
||||
explicit_access,
|
||||
_marker: marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_access_mode(&mut self, access_mode: u32) -> &mut Self {
|
||||
self.explicit_access.grfAccessMode = access_mode;
|
||||
self
|
||||
}
|
||||
|
||||
fn set_access_permissions(&mut self, access_permissions: u32) -> &mut Self {
|
||||
self.explicit_access.grfAccessPermissions = access_permissions;
|
||||
self
|
||||
}
|
||||
|
||||
fn allow_inheritance(&mut self, inheritance_flags: u32) -> &mut Self {
|
||||
self.explicit_access.grfInheritance = inheritance_flags;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
struct Acl {
|
||||
acl_ptr: PACL,
|
||||
}
|
||||
|
||||
impl Acl {
|
||||
fn empty() -> io::Result<Acl> {
|
||||
Self::new(&mut [])
|
||||
}
|
||||
|
||||
fn new(entries: &mut [AceWithSid]) -> io::Result<Acl> {
|
||||
let mut acl_ptr = ptr::null_mut();
|
||||
let result = unsafe {
|
||||
SetEntriesInAclW(entries.len() as u32,
|
||||
entries.as_mut_ptr() as *mut _,
|
||||
ptr::null_mut(), &mut acl_ptr)
|
||||
};
|
||||
|
||||
if result != ERROR_SUCCESS {
|
||||
return Err(io::Error::from_raw_os_error(result as i32));
|
||||
}
|
||||
|
||||
Ok(Acl{acl_ptr})
|
||||
}
|
||||
|
||||
unsafe fn as_ptr(&self) -> PACL {
|
||||
self.acl_ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Acl {
|
||||
fn drop(&mut self) {
|
||||
if !self.acl_ptr.is_null() {
|
||||
unsafe { LocalFree(self.acl_ptr as *mut _) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SecurityDescriptor {
|
||||
descriptor_ptr: PSECURITY_DESCRIPTOR,
|
||||
}
|
||||
|
||||
impl SecurityDescriptor{
|
||||
fn new() -> io::Result<Self> {
|
||||
let descriptor_ptr = unsafe {
|
||||
LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH)
|
||||
};
|
||||
if descriptor_ptr.is_null() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other,
|
||||
"Failed to allocate security descriptor"));
|
||||
}
|
||||
|
||||
if unsafe { InitializeSecurityDescriptor(
|
||||
descriptor_ptr,
|
||||
SECURITY_DESCRIPTOR_REVISION) == 0 }
|
||||
{
|
||||
return Err(io::Error::last_os_error());
|
||||
};
|
||||
|
||||
Ok(SecurityDescriptor{descriptor_ptr})
|
||||
}
|
||||
|
||||
fn set_dacl(&mut self, acl: &Acl) -> io::Result<()> {
|
||||
if unsafe {
|
||||
SetSecurityDescriptorDacl(
|
||||
self.descriptor_ptr,
|
||||
true as i32, acl.as_ptr(),
|
||||
false as i32) == 0
|
||||
}{
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn as_ptr(&self) -> PSECURITY_DESCRIPTOR {
|
||||
self.descriptor_ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SecurityDescriptor {
|
||||
fn drop(&mut self) {
|
||||
if !self.descriptor_ptr.is_null() {
|
||||
unsafe { LocalFree(self.descriptor_ptr) };
|
||||
self.descriptor_ptr = ptr::null_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct InnerAttributes {
|
||||
descriptor: SecurityDescriptor,
|
||||
acl: Acl,
|
||||
attrs: SECURITY_ATTRIBUTES,
|
||||
}
|
||||
|
||||
|
||||
impl InnerAttributes {
|
||||
|
||||
fn empty() -> io::Result<InnerAttributes> {
|
||||
let descriptor = SecurityDescriptor::new()?;
|
||||
let mut attrs = unsafe { mem::zeroed::<SECURITY_ATTRIBUTES>() };
|
||||
attrs.nLength = mem::size_of::<SECURITY_ATTRIBUTES>() as u32;
|
||||
attrs.lpSecurityDescriptor = unsafe {descriptor.as_ptr()};
|
||||
attrs.bInheritHandle = false as i32;
|
||||
|
||||
let acl = Acl::empty().expect("this should never fail");
|
||||
|
||||
Ok(InnerAttributes{
|
||||
acl,
|
||||
descriptor,
|
||||
attrs,
|
||||
})
|
||||
}
|
||||
|
||||
fn allow_everyone(permissions: u32) -> io::Result<InnerAttributes> {
|
||||
let mut attributes = Self::empty()?;
|
||||
let sid = Sid::everyone_sid()?;
|
||||
println!("pisec");
|
||||
|
||||
let mut everyone_ace = AceWithSid::new(&sid, TRUSTEE_IS_WELL_KNOWN_GROUP);
|
||||
everyone_ace.set_access_mode(SET_ACCESS)
|
||||
.set_access_permissions(permissions)
|
||||
.allow_inheritance(false as u32);
|
||||
|
||||
|
||||
let mut entries = vec![everyone_ace];
|
||||
attributes.acl = Acl::new(&mut entries)?;
|
||||
attributes.descriptor.set_dacl(&attributes.acl)?;
|
||||
|
||||
Ok(attributes)
|
||||
}
|
||||
|
||||
unsafe fn as_ptr(&mut self) -> PSECURITY_ATTRIBUTES {
|
||||
&mut self.attrs as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::SecurityAttributes;
|
||||
|
||||
#[test]
|
||||
fn test_allow_everyone_everything() {
|
||||
SecurityAttributes::allow_everyone_create()
|
||||
.expect("failed to create security attributes that allow everyone to create a pipe");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_allow_eveyone_read_write() {
|
||||
SecurityAttributes::allow_everyone_connect()
|
||||
.expect("failed to create security attributes that allow everyone to read and write to/from a pipe");
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue