Skip to content

Commit 70cce1a

Browse files
Merge #923
923: Fix control message *decoding* and add support for `ScmCredentials` r=asomers a=jonas-schievink While #918 fixed the *encoding*, the *decoding* done by the `CmsgIterator` still remained broken when multiple messages are received. However, since nix didn't support any control message that could reliably be sent twice in one `sendmsg` call, this couldn't be tested properly. This PR addresses this by adding `SCM_CREDENTIALS` support and testing all of this by passing both an `SCM_CREDENTIALS` and a `SCM_RIGHTS` message at the same time. I've also verified that the resulting encoding is the same as for roughly equivalent C code. Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
2 parents 237ec7b + 9f0af44 commit 70cce1a

File tree

5 files changed

+295
-90
lines changed

5 files changed

+295
-90
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1010
([#922](https://github.com/nix-rust/nix/pull/922))
1111
- Support the `SO_PEERCRED` socket option and the `UnixCredentials` type on all Linux and Android targets.
1212
([#921](https://github.com/nix-rust/nix/pull/921))
13+
- Added support for `SCM_CREDENTIALS`, allowing to send process credentials over Unix sockets.
14+
([#923](https://github.com/nix-rust/nix/pull/923))
1315

1416
### Changed
1517

src/sys/socket/mod.rs

+124-90
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,18 @@ cfg_if! {
205205
}
206206
impl Eq for UnixCredentials {}
207207

208+
impl From<libc::ucred> for UnixCredentials {
209+
fn from(cred: libc::ucred) -> Self {
210+
UnixCredentials(cred)
211+
}
212+
}
213+
214+
impl Into<libc::ucred> for UnixCredentials {
215+
fn into(self) -> libc::ucred {
216+
self.0
217+
}
218+
}
219+
208220
impl fmt::Debug for UnixCredentials {
209221
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210222
f.debug_struct("UnixCredentials")
@@ -359,7 +371,7 @@ impl<T> CmsgSpace<T> {
359371
}
360372
}
361373

362-
#[allow(missing_debug_implementations)]
374+
#[derive(Debug)]
363375
pub struct RecvMsg<'a> {
364376
// The number of bytes received.
365377
pub bytes: usize,
@@ -374,15 +386,14 @@ impl<'a> RecvMsg<'a> {
374386
pub fn cmsgs(&self) -> CmsgIterator {
375387
CmsgIterator {
376388
buf: self.cmsg_buffer,
377-
next: 0
378389
}
379390
}
380391
}
381392

382-
#[allow(missing_debug_implementations)]
393+
#[derive(Debug)]
383394
pub struct CmsgIterator<'a> {
395+
/// Control message buffer to decode from. Must adhere to cmsg alignment.
384396
buf: &'a [u8],
385-
next: usize,
386397
}
387398

388399
impl<'a> Iterator for CmsgIterator<'a> {
@@ -392,53 +403,27 @@ impl<'a> Iterator for CmsgIterator<'a> {
392403
// although we handle the invariants in slightly different places to
393404
// get a better iterator interface.
394405
fn next(&mut self) -> Option<ControlMessage<'a>> {
395-
let sizeof_cmsghdr = mem::size_of::<cmsghdr>();
396-
if self.buf.len() < sizeof_cmsghdr {
406+
if self.buf.len() == 0 {
407+
// The iterator assumes that `self.buf` always contains exactly the
408+
// bytes we need, so we're at the end when the buffer is empty.
397409
return None;
398410
}
399-
let cmsg: &'a cmsghdr = unsafe { &*(self.buf.as_ptr() as *const cmsghdr) };
400411

401-
// This check is only in the glibc implementation of CMSG_NXTHDR
402-
// (although it claims the kernel header checks this), but such
403-
// a structure is clearly invalid, either way.
404-
let cmsg_len = cmsg.cmsg_len as usize;
405-
if cmsg_len < sizeof_cmsghdr {
406-
return None;
407-
}
408-
let len = cmsg_len - sizeof_cmsghdr;
409-
let aligned_cmsg_len = if self.next == 0 {
410-
// CMSG_FIRSTHDR
411-
cmsg_len
412-
} else {
413-
// CMSG_NXTHDR
414-
cmsg_align(cmsg_len)
412+
// Safe if: `self.buf` is `cmsghdr`-aligned.
413+
let cmsg: &'a cmsghdr = unsafe {
414+
&*(self.buf[..mem::size_of::<cmsghdr>()].as_ptr() as *const cmsghdr)
415415
};
416416

417+
let cmsg_len = cmsg.cmsg_len as usize;
418+
417419
// Advance our internal pointer.
418-
if aligned_cmsg_len > self.buf.len() {
419-
return None;
420-
}
421-
let cmsg_data = &self.buf[cmsg_align(sizeof_cmsghdr)..cmsg_len];
422-
self.buf = &self.buf[aligned_cmsg_len..];
423-
self.next += 1;
424-
425-
match (cmsg.cmsg_level, cmsg.cmsg_type) {
426-
(libc::SOL_SOCKET, libc::SCM_RIGHTS) => unsafe {
427-
Some(ControlMessage::ScmRights(
428-
slice::from_raw_parts(cmsg_data.as_ptr() as *const _,
429-
cmsg_data.len() / mem::size_of::<RawFd>())))
430-
},
431-
(libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => unsafe {
432-
Some(ControlMessage::ScmTimestamp(
433-
&*(cmsg_data.as_ptr() as *const _)))
434-
},
435-
(_, _) => unsafe {
436-
Some(ControlMessage::Unknown(UnknownCmsg(
437-
cmsg,
438-
slice::from_raw_parts(
439-
cmsg_data.as_ptr() as *const _,
440-
len))))
441-
}
420+
let cmsg_data = &self.buf[cmsg_align(mem::size_of::<cmsghdr>())..cmsg_len];
421+
self.buf = &self.buf[cmsg_align(cmsg_len)..];
422+
423+
// Safe if: `cmsg_data` contains the expected (amount of) content. This
424+
// is verified by the kernel.
425+
unsafe {
426+
Some(ControlMessage::decode_from(cmsg, cmsg_data))
442427
}
443428
}
444429
}
@@ -459,6 +444,20 @@ pub enum ControlMessage<'a> {
459444
/// or fail with `EINVAL`. Instead, you can put all fds to be passed into a single `ScmRights`
460445
/// message.
461446
ScmRights(&'a [RawFd]),
447+
/// A message of type `SCM_CREDENTIALS`, containing the pid, uid and gid of
448+
/// a process connected to the socket.
449+
///
450+
/// This is similar to the socket option `SO_PEERCRED`, but requires a
451+
/// process to explicitly send its credentials. A process running as root is
452+
/// allowed to specify any credentials, while credentials sent by other
453+
/// processes are verified by the kernel.
454+
///
455+
/// For further information, please refer to the
456+
/// [`unix(7)`](http://man7.org/linux/man-pages/man7/unix.7.html) man page.
457+
// FIXME: When `#[repr(transparent)]` is stable, use it on `UnixCredentials`
458+
// and put that in here instead of a raw ucred.
459+
#[cfg(any(target_os = "android", target_os = "linux"))]
460+
ScmCredentials(&'a libc::ucred),
462461
/// A message of type `SCM_TIMESTAMP`, containing the time the
463462
/// packet was received by the kernel.
464463
///
@@ -527,6 +526,7 @@ pub enum ControlMessage<'a> {
527526
/// nix::unistd::close(in_socket).unwrap();
528527
/// ```
529528
ScmTimestamp(&'a TimeVal),
529+
/// Catch-all variant for unimplemented cmsg types.
530530
#[doc(hidden)]
531531
Unknown(UnknownCmsg<'a>),
532532
}
@@ -558,6 +558,10 @@ impl<'a> ControlMessage<'a> {
558558
ControlMessage::ScmRights(fds) => {
559559
mem::size_of_val(fds)
560560
},
561+
#[cfg(any(target_os = "android", target_os = "linux"))]
562+
ControlMessage::ScmCredentials(creds) => {
563+
mem::size_of_val(creds)
564+
}
561565
ControlMessage::ScmTimestamp(t) => {
562566
mem::size_of_val(t)
563567
},
@@ -567,57 +571,87 @@ impl<'a> ControlMessage<'a> {
567571
}
568572
}
569573

574+
/// Returns the value to put into the `cmsg_type` field of the header.
575+
fn cmsg_type(&self) -> libc::c_int {
576+
match *self {
577+
ControlMessage::ScmRights(_) => libc::SCM_RIGHTS,
578+
#[cfg(any(target_os = "android", target_os = "linux"))]
579+
ControlMessage::ScmCredentials(_) => libc::SCM_CREDENTIALS,
580+
ControlMessage::ScmTimestamp(_) => libc::SCM_TIMESTAMP,
581+
ControlMessage::Unknown(ref cmsg) => cmsg.0.cmsg_type,
582+
}
583+
}
584+
570585
// Unsafe: start and end of buffer must be cmsg_align'd. Updates
571586
// the provided slice; panics if the buffer is too small.
572587
unsafe fn encode_into(&self, buf: &mut [u8]) {
573-
match *self {
574-
ControlMessage::ScmRights(fds) => {
575-
let cmsg = cmsghdr {
576-
cmsg_len: self.len() as _,
577-
cmsg_level: libc::SOL_SOCKET,
578-
cmsg_type: libc::SCM_RIGHTS,
579-
..mem::uninitialized()
580-
};
581-
let buf = copy_bytes(&cmsg, buf);
582-
583-
let padlen = cmsg_align(mem::size_of_val(&cmsg)) -
584-
mem::size_of_val(&cmsg);
585-
let buf = pad_bytes(padlen, buf);
586-
587-
let buf = copy_bytes(fds, buf);
588-
589-
let padlen = self.space() - self.len();
590-
pad_bytes(padlen, buf);
591-
},
592-
ControlMessage::ScmTimestamp(t) => {
593-
let cmsg = cmsghdr {
594-
cmsg_len: self.len() as _,
595-
cmsg_level: libc::SOL_SOCKET,
596-
cmsg_type: libc::SCM_TIMESTAMP,
597-
..mem::uninitialized()
598-
};
599-
let buf = copy_bytes(&cmsg, buf);
600-
601-
let padlen = cmsg_align(mem::size_of_val(&cmsg)) -
602-
mem::size_of_val(&cmsg);
603-
let buf = pad_bytes(padlen, buf);
604-
605-
let buf = copy_bytes(t, buf);
606-
607-
let padlen = self.space() - self.len();
608-
pad_bytes(padlen, buf);
609-
},
610-
ControlMessage::Unknown(UnknownCmsg(orig_cmsg, bytes)) => {
611-
let buf = copy_bytes(orig_cmsg, buf);
588+
let final_buf = if let ControlMessage::Unknown(ref cmsg) = *self {
589+
let &UnknownCmsg(orig_cmsg, bytes) = cmsg;
590+
591+
let buf = copy_bytes(orig_cmsg, buf);
612592

613-
let padlen = cmsg_align(mem::size_of_val(&orig_cmsg)) -
614-
mem::size_of_val(&orig_cmsg);
615-
let buf = pad_bytes(padlen, buf);
593+
let padlen = cmsg_align(mem::size_of_val(&orig_cmsg)) -
594+
mem::size_of_val(&orig_cmsg);
595+
let buf = pad_bytes(padlen, buf);
616596

617-
let buf = copy_bytes(bytes, buf);
597+
copy_bytes(bytes, buf)
598+
} else {
599+
let cmsg = cmsghdr {
600+
cmsg_len: self.len() as _,
601+
cmsg_level: libc::SOL_SOCKET,
602+
cmsg_type: self.cmsg_type(),
603+
..mem::zeroed() // zero out platform-dependent padding fields
604+
};
605+
let buf = copy_bytes(&cmsg, buf);
606+
607+
let padlen = cmsg_align(mem::size_of_val(&cmsg)) -
608+
mem::size_of_val(&cmsg);
609+
let buf = pad_bytes(padlen, buf);
610+
611+
match *self {
612+
ControlMessage::ScmRights(fds) => {
613+
copy_bytes(fds, buf)
614+
},
615+
#[cfg(any(target_os = "android", target_os = "linux"))]
616+
ControlMessage::ScmCredentials(creds) => {
617+
copy_bytes(creds, buf)
618+
}
619+
ControlMessage::ScmTimestamp(t) => {
620+
copy_bytes(t, buf)
621+
},
622+
ControlMessage::Unknown(_) => unreachable!(),
623+
}
624+
};
618625

619-
let padlen = self.space() - self.len();
620-
pad_bytes(padlen, buf);
626+
let padlen = self.space() - self.len();
627+
pad_bytes(padlen, final_buf);
628+
}
629+
630+
/// Decodes a `ControlMessage` from raw bytes.
631+
///
632+
/// This is only safe to call if the data is correct for the message type
633+
/// specified in the header. Normally, the kernel ensures that this is the
634+
/// case. "Correct" in this case includes correct length, alignment and
635+
/// actual content.
636+
unsafe fn decode_from(header: &'a cmsghdr, data: &'a [u8]) -> ControlMessage<'a> {
637+
match (header.cmsg_level, header.cmsg_type) {
638+
(libc::SOL_SOCKET, libc::SCM_RIGHTS) => {
639+
ControlMessage::ScmRights(
640+
slice::from_raw_parts(data.as_ptr() as *const _,
641+
data.len() / mem::size_of::<RawFd>()))
642+
},
643+
#[cfg(any(target_os = "android", target_os = "linux"))]
644+
(libc::SOL_SOCKET, libc::SCM_CREDENTIALS) => {
645+
ControlMessage::ScmCredentials(
646+
&*(data.as_ptr() as *const _)
647+
)
648+
}
649+
(libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => {
650+
ControlMessage::ScmTimestamp(
651+
&*(data.as_ptr() as *const _))
652+
},
653+
(_, _) => {
654+
ControlMessage::Unknown(UnknownCmsg(header, data))
621655
}
622656
}
623657
}

src/sys/socket/sockopt.rs

+2
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,8 @@ sockopt_impl!(Both, BindAny, libc::SOL_SOCKET, libc::SO_BINDANY, bool);
255255
sockopt_impl!(Both, BindAny, libc::IPPROTO_IP, libc::IP_BINDANY, bool);
256256
#[cfg(target_os = "linux")]
257257
sockopt_impl!(Both, Mark, libc::SOL_SOCKET, libc::SO_MARK, u32);
258+
#[cfg(any(target_os = "android", target_os = "linux"))]
259+
sockopt_impl!(Both, PassCred, libc::SOL_SOCKET, libc::SO_PASSCRED, bool);
258260

259261
/*
260262
*

src/unistd.rs

+15
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ impl Uid {
4848
pub fn is_root(&self) -> bool {
4949
*self == ROOT
5050
}
51+
52+
/// Get the raw `uid_t` wrapped by `self`.
53+
pub fn as_raw(&self) -> uid_t {
54+
self.0
55+
}
5156
}
5257

5358
impl From<Uid> for uid_t {
@@ -87,6 +92,11 @@ impl Gid {
8792
pub fn effective() -> Self {
8893
getegid()
8994
}
95+
96+
/// Get the raw `gid_t` wrapped by `self`.
97+
pub fn as_raw(&self) -> gid_t {
98+
self.0
99+
}
90100
}
91101

92102
impl From<Gid> for gid_t {
@@ -123,6 +133,11 @@ impl Pid {
123133
pub fn parent() -> Self {
124134
getppid()
125135
}
136+
137+
/// Get the raw `pid_t` wrapped by `self`.
138+
pub fn as_raw(&self) -> pid_t {
139+
self.0
140+
}
126141
}
127142

128143
impl From<Pid> for pid_t {

0 commit comments

Comments
 (0)