1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use super::{Connect, ReadWrite};
use crate::error::{ClientErrorKind, Result};
use std::os::unix::net::UnixStream;
use std::path::PathBuf;
use std::time::Duration;
#[cfg(not(feature = "no-fs-permission-check"))]
use log::error;
#[cfg(not(feature = "no-fs-permission-check"))]
use std::ffi::OsStr;
#[cfg(not(feature = "no-fs-permission-check"))]
use std::fs;
#[cfg(not(feature = "no-fs-permission-check"))]
use std::io::{Error, ErrorKind};
#[cfg(not(feature = "no-fs-permission-check"))]
use std::os::unix::fs::MetadataExt;
const DEFAULT_SOCKET_PATH: &str = "/tmp/parsec/parsec.sock";
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(1);
#[derive(Debug, Clone)]
pub struct Handler {
path: PathBuf,
timeout: Option<Duration>,
}
impl Connect for Handler {
fn connect(&self) -> Result<Box<dyn ReadWrite>> {
#[cfg(not(feature = "no-fs-permission-check"))]
self.secure_parsec_socket_folder()?;
let stream = UnixStream::connect(self.path.clone()).map_err(ClientErrorKind::Ipc)?;
stream
.set_read_timeout(self.timeout)
.map_err(ClientErrorKind::Ipc)?;
stream
.set_write_timeout(self.timeout)
.map_err(ClientErrorKind::Ipc)?;
Ok(Box::from(stream))
}
fn set_timeout(&mut self, timeout: Option<Duration>) {
self.timeout = timeout;
}
}
impl Handler {
pub fn new(path: PathBuf, timeout: Option<Duration>) -> Self {
Handler { path, timeout }
}
#[cfg(not(feature = "no-fs-permission-check"))]
fn secure_parsec_socket_folder(&self) -> Result<()> {
let mut socket_dir = self.path.clone();
if !socket_dir.pop() {
return Err(ClientErrorKind::Ipc(Error::new(
ErrorKind::Other,
"Socket permission checks failed",
))
.into());
}
let meta = fs::metadata(socket_dir).map_err(ClientErrorKind::Ipc)?;
match users::get_user_by_uid(meta.uid()) {
Some(user) => {
if user.name() != OsStr::new("parsec") {
error!("The socket directory must be owned by the parsec user.");
return Err(ClientErrorKind::Ipc(Error::new(
ErrorKind::Other,
"Socket permission checks failed",
))
.into());
}
}
None => {
error!("Can not find socket directory user owner.");
return Err(ClientErrorKind::Ipc(Error::new(
ErrorKind::Other,
"Socket permission checks failed",
))
.into());
}
}
match users::get_group_by_gid(meta.gid()) {
Some(group) => {
if group.name() != OsStr::new("parsec-clients") {
error!("The socket directory must be owned by the parsec-clients group.");
return Err(ClientErrorKind::Ipc(Error::new(
ErrorKind::Other,
"Socket permission checks failed",
))
.into());
}
}
None => {
error!("Can not find socket directory group owner.");
return Err(ClientErrorKind::Ipc(Error::new(
ErrorKind::Other,
"Socket permission checks failed",
))
.into());
}
}
if (meta.mode() & 0o777) != 0o750 {
error!("The permission bits of the folder containing the Parsec socket must be 750.");
return Err(ClientErrorKind::Ipc(Error::new(
ErrorKind::Other,
"Socket permission checks failed",
))
.into());
}
Ok(())
}
}
impl Default for Handler {
fn default() -> Self {
Handler {
path: DEFAULT_SOCKET_PATH.into(),
timeout: Some(DEFAULT_TIMEOUT),
}
}
}