summaryrefslogtreecommitdiff
path: root/main.cc
diff options
context:
space:
mode:
Diffstat (limited to 'main.cc')
-rw-r--r--main.cc214
1 files changed, 214 insertions, 0 deletions
diff --git a/main.cc b/main.cc
new file mode 100644
index 0000000..27c7806
--- /dev/null
+++ b/main.cc
@@ -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 );
+
+}