2016-10-16 11:35:29 +00:00
# include "LinuxDropPrivileges.hpp"
# include <linux/capability.h>
# include <linux/securebits.h>
# include <sys/prctl.h>
# include <sys/stat.h>
# include <sys/syscall.h>
# include <sys/types.h>
# include <sys/wait.h>
# include <pwd.h>
# include <stdlib.h>
# include <unistd.h>
namespace ZeroTier {
# ifndef PR_CAP_AMBIENT
// if we are on old libc, dropPrivileges is nop
void dropPrivileges ( std : : string homeDir ) { }
# else
const char * TARGET_USER_NAME = " zerotier-one " ;
struct cap_header_struct {
__u32 version ;
int pid ;
} ;
struct cap_data_struct {
__u32 effective ;
__u32 permitted ;
__u32 inheritable ;
} ;
// libc doesn't export capset, it is instead located in libcap
// We ignore libcap and call it manually.
int capset ( cap_header_struct * hdrp , cap_data_struct * datap ) {
return syscall ( SYS_capset , hdrp , datap ) ;
}
void notDropping ( std : : string homeDir ) {
struct stat buf ;
if ( lstat ( homeDir . c_str ( ) , & buf ) < 0 ) {
if ( buf . st_uid ! = 0 | | buf . st_gid ! = 0 ) {
fprintf ( stderr , " ERROR: failed to drop privileges. Refusing to run as root, because %s was already used in nonprivileged mode. \n " , homeDir . c_str ( ) ) ;
exit ( 1 ) ;
}
}
fprintf ( stderr , " WARNING: failed to drop privileges, running as root \n " ) ;
}
int setCapabilities ( int flags ) {
cap_header_struct capheader = { _LINUX_CAPABILITY_VERSION_1 , 0 } ;
cap_data_struct capdata ;
capdata . inheritable = capdata . permitted = capdata . effective = flags ;
return capset ( & capheader , & capdata ) ;
}
void createOwnedHomedir ( std : : string homeDir , struct passwd * targetUser ) {
struct stat buf ;
if ( lstat ( homeDir . c_str ( ) , & buf ) < 0 ) {
if ( errno = = ENOENT ) {
mkdir ( homeDir . c_str ( ) , 0755 ) ;
} else {
perror ( " cannot access home directory " ) ;
exit ( 1 ) ;
}
}
if ( buf . st_uid ! = 0 | | buf . st_gid ! = 0 ) {
// should be already owned by zerotier-one
if ( targetUser - > pw_uid ! = buf . st_uid ) {
fprintf ( stderr , " ERROR: %s not owned by zerotier-one or root \n " , homeDir . c_str ( ) ) ;
exit ( 1 ) ;
}
return ;
}
// Change homedir owner to zerotier-one user. This is safe, because this directory is writable only by root, so no one could have created malicious hardlink.
long p = ( long ) fork ( ) ;
int exitcode = - 1 ;
if ( p > 0 ) {
waitpid ( p , & exitcode , 0 ) ;
} else if ( p = = 0 ) {
std : : string ownerString = std : : to_string ( targetUser - > pw_uid ) + " : " + std : : to_string ( targetUser - > pw_gid ) ;
execlp ( " chown " , " chown " , " -R " , ownerString . c_str ( ) , " -- " , homeDir . c_str ( ) , NULL ) ;
_exit ( - 1 ) ;
}
if ( exitcode ! = 0 ) {
fprintf ( stderr , " failed to change owner of %s to %s \n " , homeDir . c_str ( ) , targetUser - > pw_name ) ;
exit ( 1 ) ;
}
}
void dropPrivileges ( std : : string homeDir ) {
// dropPrivileges switches to zerotier-one user while retaining CAP_NET_ADMIN
// and CAP_NET_RAW capabilities.
struct passwd * targetUser = getpwnam ( TARGET_USER_NAME ) ;
if ( targetUser = = NULL ) {
// zerotier-one user not configured by package
return ;
}
2016-11-08 14:50:32 +00:00
createOwnedHomedir ( homeDir , targetUser ) ;
2016-10-16 11:35:29 +00:00
if ( prctl ( PR_CAP_AMBIENT , PR_CAP_AMBIENT_IS_SET , CAP_NET_RAW , 0 , 0 ) < 0 ) {
// Kernel has no support for ambient capabilities.
notDropping ( homeDir ) ;
return ;
}
if ( prctl ( PR_SET_SECUREBITS , SECBIT_KEEP_CAPS | SECBIT_NOROOT ) < 0 ) {
notDropping ( homeDir ) ;
return ;
}
if ( setCapabilities ( ( 1 < < CAP_NET_ADMIN ) | ( 1 < < CAP_NET_RAW ) | ( 1 < < CAP_SETUID ) | ( 1 < < CAP_SETGID ) ) < 0 ) {
fprintf ( stderr , " ERROR: failed to set capabilities (not running as real root?) \n " ) ;
exit ( 1 ) ;
}
int oldDumpable = prctl ( PR_GET_DUMPABLE ) ;
if ( prctl ( PR_SET_DUMPABLE , 0 ) < 0 ) {
// Disable ptracing. Otherwise there is a small window when previous
// compromised ZeroTier process could ptrace us, when we still have CAP_SETUID.
// (this is mitigated anyway on most distros by ptrace_scope=1)
perror ( " prctl(PR_SET_DUMPABLE) " ) ;
exit ( 1 ) ;
}
if ( setgid ( targetUser - > pw_gid ) < 0 ) {
perror ( " setgid " ) ;
exit ( 1 ) ;
}
if ( setuid ( targetUser - > pw_uid ) < 0 ) {
perror ( " setuid " ) ;
exit ( 1 ) ;
}
if ( setCapabilities ( ( 1 < < CAP_NET_ADMIN ) | ( 1 < < CAP_NET_RAW ) ) < 0 ) {
perror ( " could not drop capabilities after setuid " ) ;
exit ( 1 ) ;
}
if ( prctl ( PR_SET_DUMPABLE , oldDumpable ) < 0 ) {
perror ( " could not restore dumpable flag " ) ;
exit ( 1 ) ;
}
if ( prctl ( PR_CAP_AMBIENT , PR_CAP_AMBIENT_RAISE , CAP_NET_ADMIN , 0 , 0 ) < 0 ) {
perror ( " could not raise ambient CAP_NET_ADMIN " ) ;
exit ( 1 ) ;
}
if ( prctl ( PR_CAP_AMBIENT , PR_CAP_AMBIENT_RAISE , CAP_NET_RAW , 0 , 0 ) < 0 ) {
perror ( " could not raise ambient CAP_NET_RAW " ) ;
exit ( 1 ) ;
}
}
# endif
}