diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | main.c | 62 | ||||
-rw-r--r-- | main.cc | 214 |
3 files changed, 216 insertions, 64 deletions
@@ -1,5 +1,5 @@ -insecuresuexec: main.c - clang -Wall -Werror -Os -o $@ $+ +insecuresuexec: main.cc + clang++ -Wall -Werror -std=c++11 -Os -g -o $@ $+ clean: rm -f insecuresuexec @@ -1,62 +0,0 @@ -#define _GNU_SOURCE - -#include <assert.h> -#include <errno.h> -#include <grp.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <unistd.h> - -int main( int argc, char *argv[] ) { - - if ( argc < 4 ) { - fprintf( stderr, "Usage: %s user group cmd [args..]\n", argv[0] ); - return 1; - }; - - char *user = argv[1]; - char *group = argv[2]; - char *cmd = argv[3]; - char **args = argv + 3; - - char *end; - unsigned long tmp; - - struct passwd *userpw; - struct group *grouppw; - - tmp = strtoul( user, &end, 10 ); - if ( end != user && ! *end ) { - userpw = getpwuid( tmp ); - } else { - userpw = getpwnam( user ); - }; - assert( userpw != NULL ); - - tmp = strtoul( group, &end, 10 ); - if ( end != user && ! *end ) { - grouppw = getgrgid( tmp ); - } else { - grouppw = getgrnam( group ); - }; - assert( grouppw != NULL ); - - // literally the only security check - assert( grouppw->gr_gid != 0 ); - assert( userpw->pw_uid != 0 ); - - if ( setgroups( 0, NULL ) != 0 ) - assert_perror( errno ); - - if ( setregid( grouppw->gr_gid, grouppw->gr_gid ) != 0 ) - assert_perror( errno ); - - if ( setreuid( userpw->pw_uid, userpw->pw_uid ) != 0 ) - assert_perror( errno ); - - execv( cmd, args ); - assert_perror( errno ); - -} @@ -0,0 +1,214 @@ +#include <assert.h> +#include <errno.h> +#include <grp.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> +#include <utility> +#include <vector> + +uid_t parse_user( const char *user ) { + + char *end; + unsigned long tmp; + + tmp = strtoul( user, &end, 10 ); + if ( end != user && ! *end ) { + return tmp; + } else { + struct passwd *pw = getpwnam( user ); + assert( pw ); + return pw->pw_uid; + }; + +} + +gid_t parse_group( const char *group ) { + + char *end; + unsigned long tmp; + + tmp = strtoul( group, &end, 10 ); + if ( end != group && ! *end ) { + return tmp; + } else { + struct group *gr = getgrnam( group ); + assert( gr ); + return gr->gr_gid; + }; + +} + +class permission { + + uid_t min_uid, max_uid; + gid_t min_gid, max_gid; + +public: + + permission( std::pair< uid_t, uid_t > uids, std::pair< gid_t, gid_t > gids ) + : min_uid( uids.first ) + , max_uid( uids.second ) + , min_gid( gids.first ) + , max_gid( gids.second ) + { + + + } + + const bool allows( uid_t uid, gid_t gid ) { + return uid >= min_uid && uid <= max_uid && gid >= min_gid && gid <= max_gid; + } + +}; + +template< typename thing > std::pair< thing, thing > parse_pair( const char *line, thing (*parse_one)( const char *part ) ) { + + std::pair< thing, thing > ret( 0, -1 ); + + size_t part_len = strlen( line ); + assert( part_len > 0 ); + + char part[part_len + 1]; + strncpy( part, line, part_len ); + part[part_len] = 0; + + size_t comma = strcspn( line, "," ); + + if ( comma == part_len ) { + + thing it = parse_one( part ); + + ret.first = it; + ret.second = it; + + } else { + + size_t first_len = comma; + size_t second_len = part_len - comma - 1; + + char first[first_len + 1]; + strncpy( first, part, first_len ); + first[first_len] = 0; + + char second[second_len + 1]; + strncpy( second, part + comma + 1, second_len ); + second[second_len] = 0; + + if ( first_len ) + ret.first = parse_one( first ); + + if ( second_len ) + ret.second = parse_one( second ); + + }; + + return ret; + +} + +permission parse_line( const char *line ) { + + size_t line_len = strcspn( line, "\n" ); + assert( line_len > 0 ); + + size_t user_len = strcspn( line, ":" ); + assert( user_len != line_len ); + + size_t group_len = line_len - user_len - 1; + + char user[user_len + 1]; + strncpy( user, line, user_len ); + user[user_len] = 0; + + char group[group_len + 1]; + strncpy( group, line + user_len + 1, group_len ); + group[group_len] = 0; + + auto uid_part = parse_pair< uid_t >( user, parse_user ); + auto gid_part = parse_pair< gid_t >( group, parse_group ); + + return permission( uid_part, gid_part ); + +} + +std::vector< permission > * read_permissions( const char *file ) { + + FILE *fh = fopen( file, "r" ); + if ( not fh ) + assert_perror( errno ); + + auto *ret = new std::vector< permission >( ); + + char *line = 0; + size_t size = 0; + + for ( ; getline( &line, &size, fh ) != -1; ) { + + size_t line_len = strcspn( line, "\n" ); + if ( line_len ) { + ret->push_back( parse_line( line ) ); + }; + + }; + + if ( line ) + free( line ); + + return ret; + +} + +int main( int argc, char *argv[] ) { + + if ( argc < 4 ) { + fprintf( stderr, "Usage: %s user group cmd [args..]\n", argv[0] ); + return 1; + }; + + char *user = argv[1]; + char *group = argv[2]; + char *cmd = argv[3]; + char **args = argv + 3; + + uid_t uid; + gid_t gid; + + uid = parse_user( user ); + gid = parse_group( group ); + + // literally the only hard-coded security check + if ( not uid || not gid ) { + fprintf( stderr, "Neither uid nor gid may be zero!\n" ); + _exit( 1 ); + }; + + auto allowed = read_permissions( "/etc/insecuresuexec/permissions" ); + + // the configurable security checks + bool ok = false; + for ( auto i = allowed->begin( ); i != allowed->end( ); ++i ) { + ok |= i->allows( uid, gid ); + if ( ok ) break; + }; + if ( not ok ) { + fprintf( stderr, "The uid/gid pair %u/%u is not allowed!\n", uid, gid ); + _exit( 1 ); + }; + + if ( setgroups( 0, NULL ) != 0 ) + assert_perror( errno ); + + if ( setregid( gid, gid ) != 0 ) + assert_perror( errno ); + + if ( setreuid( uid, uid ) != 0 ) + assert_perror( errno ); + + execv( cmd, args ); + assert_perror( errno ); + +} |