#!/usr/bin/perl

# idea by Lukasz Wojtow <lw@wszia.edu.pl>
# code by Rafal Wijata http://www.wijata.com

# for 0.9.5

$devpath = "/proc/erup";
$modprobe = "/sbin/modprobe";
$modprobe = `which modprobe` unless (-x $modprobe);

#allow cmd: show something
$ARGV[0] .= $ARGV[1] if ($ARGV[0] eq "show");


if ($ARGV[0] eq "showuser") {
	$grep = "\\+u:";

} elsif ($ARGV[0] eq "showgroup") {
	$grep = "\\+g:";

} elsif ($ARGV[0] eq "showgroups") {
	$grep = "\\+s:";

} elsif ($ARGV[0] eq "showchr") {
	$grep = "\\+c:";

} elsif ($ARGV[0] eq "showquota") {
	$grep = "\\+q:";

} elsif ($ARGV[0] eq "showinfo") {
	$grep = "\\=";

} elsif ($ARGV[0] eq "showall") {
	$grep = ".";

} elsif ($ARGV[0] eq "add" and $ARGV[1] eq "user" and defined($ARGV[4])) {
	(undef, undef, $uid) = getpwnam($ARGV[2]);
	$uid = $ARGV[2] unless (defined $uid);

	(undef, undef, $from) = getpwnam($ARGV[3]);
	$from = $ARGV[3] unless (defined $from);

	(undef, undef, $to) = getpwnam($ARGV[4]);
	$to = $ARGV[4] unless (defined $to);

        $cmd = "+u:$uid:$from:$to:";

} elsif ($ARGV[0] eq "add" and $ARGV[1] eq "group" and defined($ARGV[4])) {
	(undef, undef, $uid) = getpwnam($ARGV[2]);
	$uid = $ARGV[2] unless (defined $uid);

	(undef, undef, $from) = getgrnam($ARGV[3]);
	$from = $ARGV[3] unless (defined $from);

        (undef, undef, $to) = getgrnam($ARGV[4]);
	$to = $ARGV[4] unless (defined $to);

        $cmd = "+g:$uid:$from:$to:";

} elsif ($ARGV[0] eq "add" and $ARGV[1] eq "groups" and defined($ARGV[4])) {
	(undef, undef, $uid) = getpwnam($ARGV[2]);
	$uid = $ARGV[2] unless (defined $uid);

        $cmd = "+s:$uid:$ARGV[3]:$ARGV[4]:";

} elsif ($ARGV[0] eq "add" and $ARGV[1] eq "chr" and -d $ARGV[3]) {
	(undef, undef, $uid) = getpwnam($ARGV[2]);
	$uid = $ARGV[2] unless (defined $uid);
	#strip ending slashes
	@path = split(/\//, $ARGV[3]);
	$path = "";
	while( defined($_ = shift(@path)) ) {
	    next unless($_);
	    $path .= "/$_";
	};
	$path = "/" unless ($path);
	$cmd = "+c:$uid:$path:";

} elsif ($ARGV[0] eq "add" and $ARGV[1] eq "quota" and defined($ARGV[4])) {
	(undef, undef, $uid) = getpwnam($ARGV[2]);
	$uid = $ARGV[2] unless (defined $uid);

        $cmd = "+q:$uid:$ARGV[3]:$ARGV[4]:";

} elsif ($ARGV[0] eq "del" and $ARGV[1] eq "all") {
	foreach $cmd ("user", "group", "groups", "chr", "quota") {
	    system("$0 del $cmd all");
	};
	exit(0);

} elsif ($ARGV[0] eq "del" and defined($ARGV[2])) {
	$ARGV[2] = "0" if ($ARGV[2] eq "all");
	(undef, undef, $uid) = getpwnam($ARGV[2]);
	$uid = $ARGV[2] unless (defined $uid);
	$cmd = "-u:$uid:" if ($ARGV[1] eq "user");
	$cmd = "-g:$uid:" if ($ARGV[1] eq "group");
	$cmd = "-s:$uid:" if ($ARGV[1] eq "groups");
	$cmd = "-c:$uid:" if ($ARGV[1] eq "chr");
	$cmd = "-q:$uid:" if ($ARGV[1] eq "quota");

} else {
	&usage();
	exit(1);
};

#open device
if (! open(DEV, "+<$devpath")) {
    #try load module
    system("$modprobe erup");
    open(DEV, "+<$devpath")	or die ("open($devpath): $!\n");
};

#turn autoflush on
select DEV;
$| = 1;
select STDOUT;

if ($cmd) {	#give command
    if (syswrite(DEV, $cmd, length($cmd))) {
	print(STDERR "syswrite($cmd): OK\n");
    } else {
	print(STDERR "syswrite($cmd): $!\n");
    };
} elsif ($grep) {
    while (<DEV>) {
	chop;
	#only if match $grep
	next unless (/^$grep/o);
	#uid/list etc. only if line starts with +
	unless (/^\+/) { print("$_\n"); next; };
	($cmd, $uid, $rest) = split(/:/, $_, 3);
	$uid = (getpwuid($uid) || $uid);
	if    ($cmd eq "+u") { $cmd = "user"; }
	elsif ($cmd eq "+g") { $cmd = "group"; }
	elsif ($cmd eq "+c") { $cmd = "chroot"; }
	elsif ($cmd eq "+s") { $cmd = "groupS";
	                       $rest =~ m/(\d):(\d)/;
			       $rest = ($1==1?"clear:":"-:").($2==2?"set":"-"); }
	elsif ($cmd eq "+q") { $cmd = "quota";
	                       $rest =~ m/(\d):(\d)/;
			       $rest = ($1==1?"read:":"-:").($2==2?"set":"-"); }
	
	printf("%-7s:%-9s:$rest\n", $cmd, $uid);
    };
} else {
    &usage();
};

close(DEV);
exit 0;

##########################################################################
sub usage {	# always TODO
    print << "EOF";
usage:
    $0 add {user|group} ID min_id max_id
    $0 add chr ID directory
    $0 add groups ID clear set
    $0 add quota ID read set
    $0 del {user|group|groups|chr|quota} ID
    $0 del all		- revoke all privileges on all ilsts
	add/del		- grant or revoke privileges
	user/group/groups/chr/quota
			- manage setuid() or setgid() or setgroups()
			    or chroot() or quotactl() privileges
	ID		- name or numeric ID of modified user, if cmd is del
			  and ID is all|root|0, revokes privileges for all
	min_id, max_id	- grant switching uid/gid to min_id <= x <= max_id
	directory	- grant chroot only to directory (and deeper unless
			  strict path checking compiled in)
	read, clear, set
			- integer value saying whether user is allowed read/set
			  others quota; clear/set his supplementary groups
or:
    $0 show {user|group|groups|chr|quota|info}
	show list of privileges of desired type
or:
    $0 log
	switch logging flag, show value after switch
examples:
$0 add user foo 1 100
    - user foo can swich uid to (1..100)
$0 add group foo sys sys
    - user, whom foo is primary group can switch gid to (sys..sys)
$0 add chr foo /usr
    - user foo can chroot(/usr), but also chroot(/usr/any/dir) if no
      strict checking mode was compiled in
$0 add quota foo 1 0
    - user foo can read quota values of others, but not set
$0 add groups foo 1 1
    - user foo can clear and set its supplementary groups
$0 del user foo
    - user foo cant switch to any other user
$0 del chr 0
    - no user can do chroot() any longer (except root ;)
$0 show user
    - lists granted privileges on setuid
note:
    When granting chroot to dir, dir is handled differently depending on
    compiled in options. Check system log or run $0 show info
    to find what options was compiled in
EOF
};

#########################################################################
#obsolete code, but I'll hold it for a while
#    #try read up to 64K
#    $size = 1024;
#    while ( ($size <= 65536) && (! (sysread(DEV, $res, $size))) ) {
#	$size *= 4;
#    };
#    if ($!) {
#	print(STDERR "sysread(): $!\n");
#    } else {
#	foreach $_ (split(/\n/, $res)) {
#	    print("$_\n") if (/^$grep/o);
#	};
#    };
