Rust is a modern programing language which is claimed to be blazingly fast and memory-efficient. It syntactically similar to C++, but is designed to provide better memory safety while maintaining high performance and productivity:
Enough theory, now come the problem: I currently develop a daemon network application (server) in Rust for an embedded project, and for security purpose, at some point in the program, after reading all configurations from a file, the application need to drop user privileges of the current process to another configured user. How can one do such a thing in Rust ?
Basically, in Unix-like system, user privileges dropping can be performed via some libc functions:
// get group struct from group name
struct group *getgrnam(const char *name);
// Drop the group privileges of the current process
int setgid( gid_t gid);
// get user struct from user name
struct passwd *getpwnam(const char *name);
// Drop user privileges
int setuid(uid_t uid);
These function need to be called from Rust to perform the task. This requires performing some FFI calls to external library (libc in this case). Luckily, we can use the libc crate, a libc wrapper for Rust, for this purpose.
Using external library in Rust involves the use of unsafe feature in Rust. Basically, Rust is compile time memory safe, but when accessing external C library which is not managed by the Rust's compiler, the memory safety of these calls are unknown at compile time. In this case, you must use the unsafe feature to tell the compiler that it can count you, and that you do it with your own risk. The downside: if you use unsafe code incorrectly, problems due to memory unsafety, such as null pointers, dangling pointers, or data races, can occur.
The following snippet shows how to use unsafe code to perform user privileges dropping via FFI calls to underlying libc library, unsafe calls are denoted by the unsafe
keyword:
extern crate libc;
use std::ffi::{CStr, CString};
use std::io::prelude::*;
use std::io::{Error, ErrorKind};
use std::ptr;
/// Drop user privileges
///
/// This function drop the privileges of the current user
/// to another inferior privileges user.
pub fn privdrop(user: &str, group: &str) -> Result<(), Error> {
// the group id need to be set first, otherwise,
// when the user privileges drop, it is unnable to
// drop the group privileges
// get the gid from username
if let Ok(cstr) = CString::new(group.as_bytes()) {
let p = unsafe { libc::getgrnam(cstr.as_ptr()) };
if p.is_null() {
eprintln!("privdrop: Unable to getgrnam of group: {}", group);
return Err(Error::last_os_error());
}
if unsafe { libc::setgid((*p).gr_gid) } != 0 {
eprintln!("privdrop: Unable to setgid of group: {}", group);
return Err(Error::last_os_error());
}
} else {
return Err(Error::new(
ErrorKind::Other,
"Cannot create CString from String (group)!",
));
}
// drop the user privileges
// get the uid from username
if let Ok(cstr) = CString::new(user.as_bytes()) {
let p = unsafe { libc::getpwnam(cstr.as_ptr()) };
if p.is_null() {
eprintln!("privdrop: Unable to getpwnam of user: {}", user);
return Err(Error::last_os_error());
}
if unsafe { libc::setuid((*p).pw_uid) } != 0 {
eprintln!("privdrop: Unable to setuid of user: {}", user);
return Err(Error::last_os_error());
}
} else {
return Err(Error::new(
ErrorKind::Other,
"Cannot create CString from String (user)!",
));
}
Ok(())
}