#include #include #include #include #include #include #include #include int main( int argc, char *argv[] ) { int err; openlog( "piny-suid", LOG_ODELAY, LOG_AUTHPRIV ); if ( argc < 3 ) { syslog( LOG_ERR, "Invalid usage" ); fprintf( stderr, "Usage: %s username command [args ...]\n", argv[0] ); return 1; }; regex_t user_reg; // Please note that these regular expressions should duplicate the language for usernames described in Piny::User. if ( ( err = regcomp( &user_reg, "^[a-zA-Z][a-zA-Z0-9_.-]*$", REG_EXTENDED | REG_NOSUB ) ) != 0 ) { size_t sz = regerror( err, &user_reg, NULL, 0 ); char buf[sz]; regerror( err, &user_reg, buf, sz ); syslog( LOG_CRIT, "Internal error; first regex, %s", buf ); fprintf( stderr, "Internal error compiling first regular expression: %s\n", buf ); return 1; }; if ( regexec( &user_reg, argv[1], 0, NULL, 0 ) ) { syslog( LOG_ERR, "Invalid username '%s' (first regex)", argv[1] ); fprintf( stderr, "'%s' does not appear to be a valid username!\n", argv[1] ); return 1; }; regfree( &user_reg ); if ( ( err = regcomp( &user_reg, "^(git|ikiwiki)-", REG_EXTENDED | REG_NOSUB ) ) != 0 ) { size_t sz = regerror( err, &user_reg, NULL, 0 ); char buf[sz]; regerror( err, &user_reg, buf, sz ); syslog( LOG_CRIT, "Internal error: second regex, %s", buf ); fprintf( stderr, "Internal error compiling second regular expression: %s\n", buf ); return 1; }; if ( ! regexec( &user_reg, argv[1], 0, NULL, 0 ) ) { syslog( LOG_ERR, "Invalid username '%s' (second regex)", argv[1] ); fprintf( stderr, "'%s' does not appear to be a valid username!\n", argv[1] ); return 1; }; regfree( &user_reg ); regex_t cmd_reg; if ( ( err = regcomp( &cmd_reg, "/", REG_EXTENDED | REG_NOSUB ) ) != 0 ) { size_t sz = regerror( err, &cmd_reg, NULL, 0 ); char buf[sz]; regerror( err, &cmd_reg, buf, sz ); syslog( LOG_CRIT, "Internal error: third regex, %s", buf ); fprintf( stderr, "Internal error compiling third regular expression: %s\n", buf ); return 1; }; if ( ! regexec( &cmd_reg, argv[2], 0, NULL, 0 ) ) { syslog( LOG_ERR, "Invalid command '%s' (third regex)", argv[2] ); fprintf( stderr, "'%s' does not appear to be a valid command!\n", argv[2] ); return 1; }; regfree( &cmd_reg ); struct passwd *pwd = getpwnam( argv[1] ); if ( ! pwd ) { syslog( LOG_ERR, "Invalid username '%s' (getpwnam)", argv[1] ); fprintf( stderr, "'%s' does not appear to be a valid username!\n", argv[1] ); return 1; }; if ( pwd->pw_uid < 1000 ) { syslog( LOG_ERR, "Invalid username '%s' (uid)", argv[1] ); fprintf( stderr, "'%s' does not appear to be a valid username!\n", argv[1] ); return 1; }; if ( setregid( pwd->pw_gid, pwd->pw_gid ) != 0 ) { err = errno; syslog( LOG_ERR, "Unable to change GID: %s, %s", argv[1], strerror( err ) ); fprintf( stderr, "Unable to change GID: %s\n", strerror( err ) ); return 1; }; if ( setreuid( pwd->pw_uid, pwd->pw_uid ) != 0 ) { err = errno; syslog( LOG_ERR, "Unable to change UID: %s, %s", argv[1], strerror( err ) ); fprintf( stderr, "Unable to change UID: %s\n", strerror( err ) ); return 1; }; size_t sz = snprintf( NULL, 0, "/usr/share/piny-suid/%s", argv[2] ); char buf[sz]; snprintf( buf, sz, "/usr/share/piny-suid/%s", argv[2] ); char * const env[] = { NULL }; syslog( LOG_NOTICE, "Going to exec '%s' as '%s'", buf, argv[1] ); execve( buf, argv + 2, env ); syslog( LOG_ERR, "Invalid command '%s' (fell past exec)", argv[2] ); fprintf( stderr, "'%s' does not appear to be a valid command!\n", argv[2] ); return 1; };