#!/usr/local/bin/perl

# Auto hacker script. You specify a host and the script will follow
# login/nis/nfs relationships in order to locate possible points of
# entrance.  By default, it operates on pre-collected host information.
# Specify the -u option to collect data on hosts that haven't been
# examined yet.

do 'yagrip.pl' ||
  die "can't do yagrip.pl";

$options = "bd:k:lf:t:uvw";
$usage = "usage: attack [-b] [-d depth] [-k host] [-l] [-f datadir] [-t timeout] [-u] [-v] [-w] host";

#
# Defaults for the -d and -f options. By default, the host data is kept
# in files named hosts/fully.qualified.host.name, and only plans up to
# three levels deep are considered when hosts are being probed in real
# time.
#
$data_dir = "rip_data";
$depth_limit = 3;

#
# addto - add plan to list
#
sub addto {
    local($host, $user, $plan) = @_;

    #
    # If this host is in the list of known hosts we have found a path to
    # break in.
    #
    if (defined($plans_known{$host})) {
	print "Plan:\n $plan\n\n";
	return;
    }
    #
    # Check to see if it's a duplicate - if so, don't need to do anything.
    #
    if (defined($plans_pending{"$user@$host"})) {
	return;
    }
    #
    # Add to pending list 
    #
    $plans_pending{"$user@$host"} = $plan;

    #
    # Add to next plan list
    #
    $plans_new{"$user@$host"} = $plan;

    if (defined($opt_v)) {
	printf "addto: $user@$host $plan\n";
    }
}

#
# Initializations...
#
sub init_attack {

    %plans_new = ();

    #
    # Deal with args...
    #

    (&getopt($options) && ($#ARGV == 0)) ||
      die $usage;

    $host = $ARGV[0];
    &addto($host, 2, "grant 2@$host.");

    if (defined($opt_f)) {
	$data_dir = $opt_f;
    }
    #
    # Limit search when probing hosts in real time.
    #
    if (defined($opt_u)) {
	if (defined($opt_d)) {
	    $depth_limit = $opt_d;
	}
    } else {
	$depth_limit = -1;
    }
    if (defined($opt_k)) {
	$plans_known{$opt_k} = 1;
    }
    if (defined($opt_t)) {
	$rip_flags="$rip_flags -t $opt_t";
    }

    #
    # All per-host data is kept below a separate directory.
    #
    foreach $path ("", "/hosts", "/grant") {
	if (! -d "$data_dir$path" && !mkdir("$data_dir$path", 0755)) {
	    die "Unable to initialize the $data_dir$path directory. Giving up."
	}
    }
    #
    # Set up the initial known goal. When some plan is accessible for 
    # known host we have found a path to break in.
    #
    $plans_known{"anyhost"} = 1;
}

#
#----------------------------------------------------------------------
#Main program follows...initialize and loop till we're done.
#

&init_attack();
$depth = 0;

#
# While there's still something to pursue...
#
while($depth != $depth_limit && &sizeof(*plans_new) != 0) {
    %plans_old = %plans_new;
    %plans_new = ();

    foreach $user_host (keys %plans_old) {
	$plan = $plans_old{$user_host};
	($user, $host) = split(/@/, $user_host);
	if (defined($opt_v)) {
	    printf "eval[$depth]: $user@$host `$plan'\n";
	}
	$host_data = "$data_dir/hosts/$host";
	if (! -f $host_data && defined($opt_u)) {
	    system("./rip_host $rip_flags $host >>$host_data");
	}
	if (! -s $host_data) {
	    if (defined($opt_l)) {
		print "Undecided:\n $plan\n";
	    }
	} else {
	    $grant_data = "$data_dir/grant_dst/$host";
	    if (-s $grant_data) {
		open(RIP_DATA, $grant_data);
	    loop:
		while (<RIP_DATA>) {
		    chop;
		    ($post_user, $pre_host, $pre_user, $why) = split(/[|]/);
		    #
		    # Avoid visiting the same host more than once.
		    #
		    if (index($plan, $pre_host) != -1) {
			next loop;
		    }
		    #
		    # For the time being, ignore resricted NFS exports.
		    #
		    if ($why =~ /exports/ && $pre_host ne "anyhost") {
			next loop;
		    }
		    #
		    # Pursue this rule if it gives access to $user@$host.
		    # Otherwise, if we have permission to break root, try
		    # that, but not too often.
		    #
		    if (($user == 2)			# No privs needed
		    || ($user == 1 && $post_user eq 0)	# More privilege
		    || ($user eq $post_user)) {		# Same privilege
			&addto($pre_host, $pre_user, 
			    "grant $pre_user@$pre_host. $why.\n $plan");
		    } elsif (defined($opt_b) && index($plan, "FORCE") == -1) {
			$plans_pending{"$post_user@$host"} = "occupied";
			$t_plan = "grant $post_user@$host FORCE $user.\n $plan";
			&addto($pre_host, $pre_user,
			    "grant $pre_user@$pre_host. $why.\n $t_plan");
		    }
		}
		close(RIP_DATA);
	    }
	}
    }
    $depth++;
}

sub sizeof {
    local(*which) = @_;
    local(@keywords);

    @keywords = keys %which;
    return($#keywords + 1);
}
