Line data Source code
1 : #define _GNU_SOURCE 2 : #include "fd_sys_util.h" 3 : 4 : #include <pwd.h> 5 : #include <errno.h> 6 : #include <stdlib.h> /* getenv */ 7 : #include <time.h> 8 : #include <unistd.h> 9 : #include <sys/syscall.h> 10 : #include <sys/mman.h> 11 : #include <sys/wait.h> 12 : 13 : void __attribute__((noreturn)) 14 0 : fd_sys_util_exit_group( int code ) { 15 0 : syscall( SYS_exit_group, code ); 16 0 : for(;;); 17 0 : } 18 : 19 : int 20 : fd_sys_util_nanosleep( uint secs, 21 0 : uint nanos ) { 22 0 : struct timespec ts = { .tv_sec = secs, .tv_nsec = nanos }; 23 0 : struct timespec rem; 24 0 : while( FD_UNLIKELY( -1==nanosleep( &ts, &rem ) ) ) { 25 0 : if( FD_LIKELY( errno==EINTR ) ) ts = rem; 26 0 : else return -1; 27 0 : } 28 0 : return 0; 29 0 : } 30 : 31 : char const * 32 3 : fd_sys_util_login_user( void ) { 33 3 : char * name = getenv( "SUDO_USER" ); 34 3 : if( FD_UNLIKELY( name ) ) return name; 35 : 36 0 : name = getenv( "LOGNAME" ); 37 0 : if( FD_LIKELY( name ) ) return name; 38 : 39 0 : name = getenv( "USER" ); 40 0 : if( FD_LIKELY( name ) ) return name; 41 : 42 0 : name = getenv( "LNAME" ); 43 0 : if( FD_LIKELY( name ) ) return name; 44 : 45 0 : name = getenv( "USERNAME" ); 46 0 : if( FD_LIKELY( name ) ) return name; 47 : 48 0 : name = getlogin(); 49 0 : if( FD_UNLIKELY( !name && (errno==ENXIO || errno==ENOTTY) ) ) return NULL; 50 0 : else if( FD_UNLIKELY( !name ) ) FD_LOG_ERR(( "getlogin failed (%i-%s)", errno, fd_io_strerror( errno ) )); 51 0 : return name; 52 0 : } 53 : 54 : int 55 : fd_sys_util_user_to_uid( char const * user, 56 : uint * uid, 57 3 : uint * gid ) { 58 3 : uint * results = mmap( NULL, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0 ); 59 3 : if( FD_UNLIKELY( results==MAP_FAILED ) ) return -1; 60 : 61 3 : results[ 0 ] = UINT_MAX; 62 3 : results[ 1 ] = UINT_MAX; 63 : 64 : /* This is extremely unfortunate. We just want to call getpwnam but 65 : on various glibc it can open `/var/lib/sss/mc/passwd` and then not 66 : close it. We could go and find this file descriptor and close it 67 : for the library, but that is a bit of a hack. Instead we fork a 68 : new process to call getpwnam and then exit. 69 : 70 : We could try just reading /etc/passwd here instead, but the glibc 71 : getpwnam implementation does a lot of things we need, including 72 : potentially reading from NCSD or SSSD. */ 73 : 74 3 : pid_t pid = fork(); 75 3 : if( FD_UNLIKELY( -1==pid ) ) { 76 0 : munmap( results, 4096 ); 77 0 : return -1; 78 0 : } 79 : 80 3 : if( FD_LIKELY( !pid ) ) { 81 0 : char buf[ 16384 ]; 82 0 : struct passwd pwd; 83 0 : struct passwd * result; 84 0 : int error = getpwnam_r( user, &pwd, buf, sizeof(buf), &result ); 85 0 : if( FD_UNLIKELY( error ) ) { 86 0 : if( FD_LIKELY( error==ENOENT || error==ESRCH ) ) { 87 0 : FD_LOG_WARNING(( "configuration file wants firedancer to run as user `%s` but it does not exist", user )); 88 0 : fd_sys_util_exit_group( 1 ); 89 0 : } else { 90 0 : FD_LOG_WARNING(( "could not get user id for `%s` (%i-%s)", user, errno, fd_io_strerror( errno ) )); 91 0 : fd_sys_util_exit_group( 1 ); 92 0 : } 93 0 : } 94 0 : if( FD_UNLIKELY( !result ) ) { 95 0 : FD_LOG_WARNING(( "configuration file wants firedancer to run as user `%s` but it does not exist", user )); 96 0 : fd_sys_util_exit_group( 1 ); 97 0 : } 98 : 99 0 : results[ 0 ] = pwd.pw_uid; 100 0 : results[ 1 ] = pwd.pw_gid; 101 0 : fd_sys_util_exit_group( 0 ); 102 3 : } else { 103 3 : int wstatus; 104 3 : if( FD_UNLIKELY( -1==waitpid( pid, &wstatus, 0 ) ) ) return -1; 105 3 : if( FD_UNLIKELY( WIFSIGNALED( wstatus ) ) ) { 106 0 : FD_LOG_WARNING(( "uid fetch process terminated by signal %i-%s", WTERMSIG( wstatus ), fd_io_strsignal( WTERMSIG( wstatus ) ) )); 107 0 : munmap( results, 4096 ); 108 0 : errno = EINTR; 109 0 : return -1; 110 0 : } 111 3 : if( FD_UNLIKELY( WEXITSTATUS( wstatus ) ) ) { 112 0 : FD_LOG_WARNING(( "uid fetch process exited with status %i", WEXITSTATUS( wstatus ) )); 113 0 : munmap( results, 4096 ); 114 0 : errno = EINTR; 115 0 : return -1; 116 0 : } 117 3 : } 118 : 119 3 : if( FD_UNLIKELY( results[ 0 ]==UINT_MAX || results[ 1 ]==UINT_MAX ) ) { 120 0 : munmap( results, 4096 ); 121 0 : errno = ENOENT; 122 0 : return -1; 123 0 : } 124 : 125 3 : *uid = results[ 0 ]; 126 3 : *gid = results[ 1 ]; 127 : 128 3 : if( FD_UNLIKELY( -1==munmap( results, 4096 ) ) ) return -1; 129 : 130 3 : return 0; 131 3 : }