aboutsummaryrefslogtreecommitdiffstats
path: root/src/util.rs
blob: e48d7814c5403ec51446506efa7ef7226b61be47 (plain) (blame)
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
use std::cmp::Ordering;
use std::fmt;
use std::process::exit;

use libc::{c_char, chdir, chroot, fork, getpwnam, getuid, setgid, setsid, setuid, umask};

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub enum DaemonizeError {
    Fork,
    SetSession,
    SetGroup,
    SetUser,
    Chroot,
    Chdir,
}

impl fmt::Display for DaemonizeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            DaemonizeError::Fork => "unable to fork",
            DaemonizeError::SetSession => "unable to create new process session",
            DaemonizeError::SetGroup => "unable to set group (drop privileges)",
            DaemonizeError::SetUser => "unable to set user (drop privileges)",
            DaemonizeError::Chroot => "unable to enter chroot jail",
            DaemonizeError::Chdir => "failed to change directory",
        }
        .fmt(f)
    }
}

fn fork_and_exit() -> Result<(), DaemonizeError> {
    let pid = unsafe { fork() };
    match pid.cmp(&0) {
        Ordering::Less => Err(DaemonizeError::Fork),
        Ordering::Equal => Ok(()),
        Ordering::Greater => exit(0),
    }
}

pub fn daemonize() -> Result<(), DaemonizeError> {
    // fork from the original parent
    fork_and_exit()?;

    // avoid killing the child when this parent dies
    if unsafe { setsid() } < 0 {
        return Err(DaemonizeError::SetSession);
    }

    // fork again to create orphan
    fork_and_exit()
}

pub fn drop_privileges() -> Result<(), DaemonizeError> {
    // retrieve nobody's uid & gid
    let usr = unsafe { getpwnam("nobody\x00".as_ptr() as *const c_char) };
    if usr.is_null() {
        return Err(DaemonizeError::SetGroup);
    }

    // change root directory
    let uid = unsafe { getuid() };
    if uid == 0 && unsafe { chroot("/tmp\x00".as_ptr() as *const c_char) } != 0 {
        return Err(DaemonizeError::Chroot);
    }

    // set umask for files
    unsafe { umask(0) };

    // change directory
    if unsafe { chdir("/\x00".as_ptr() as *const c_char) } != 0 {
        return Err(DaemonizeError::Chdir);
    }

    // set group id to nobody
    if unsafe { setgid((*usr).pw_gid) } != 0 {
        return Err(DaemonizeError::SetGroup);
    }

    // set user id to nobody
    if unsafe { setuid((*usr).pw_uid) } != 0 {
        Err(DaemonizeError::SetUser)
    } else {
        Ok(())
    }
}