package Engine;
use strict;

# initialise this one for later filling out by
this routine. If we don't
# initialise it, then the mainline will be using
an unitialised value where
# no successful exploits were
found.
@Engine::successful_exploits = ();

# find_match()
#   Find a match
if one exists. There will be two sorts of matches. The
#   first type, and
easiest to find, is an exact match where the given 
#   string matches
another string exactly. The second type is where the 
#   given string
comprises a wildcard, which will match with another string.
#
# Returns: 1
if a match is found, 0 otherwise.
# Expects: a CO string and the name of an
asociative array to lookup.
# Called By:  addto()
# Side Effects: none.
#
External References: The associative array passed as the second argument.
#
Errors: None looked for.
sub find_match {
    my($co) = $_[0];

my($name) = $_[1];
    my($assoc_r) = $_[2];

    my($key);


KuangPlus::pdebug "find_match(): Matching for \"$co\" in \"$name\"", 9.3;

if ( defined $$assoc_r{$co} ) {
        KuangPlus::pdebug "find_match():
simple, it was defined.", 9.3;
        return 1;
    }
    foreach $key
(keys(%$assoc_r)) {
        KuangPlus::pdebug "find_match(): test: \"$co\"
=~ \"$key\"\?",9.3;
        if ( "$co" =~ /^$key$/ ) {

KuangPlus::pdebug "find_match(): regex match found.", 9.3;

return 1;
        }
    }
    return 0;
} # end of find_match()

#
addto()
#    selectively adds and evaluates plans with a view to getting
access to the 
#    superuser account. Popluates
@Engine::successful_exploits as they 
#    are found.
#
# Calls:
KuangPlus::debug() 
# Called by:
# Expects: a list of at least 2
elements.
# Returns:
# Side effects:
# External references:
# Errors: looks
out for plans which are the same as the objective.
sub addto {
    my($op)
= $_[0];
    my($ob) = $_[1];
    my($plan) = $_[2];

    @Addto::plan =
$plan ? split(' ',$plan) : ();
    my($co) = "$op $ob";
    my($i);


KuangPlus::pdebug 
        "addto(op: \"$op\", ob: \"$ob\"
plan[".@Addto::plan."]: ".
        "\"@Addto::plan\")",
        3.3;
    

# check to see if $op and $ob is "u 0". If the plan is empty, then 
    #
its ok to pursue this (i.e. its the initial goal). However, if 
    # the
plan is not-empty, then it is silly to pursue it because we can 
    # do
what ever we like if we have root access.
    if ( $op eq "u" && $ob eq "0"
&& $#Addto::plan >= 0 ) {
        KuangPlus::pdebug "addto: silly to
pursue: \"$co\"", 3.3;
        return;
    }

    # The
%KuangPlus::known_facts associative array is populated with what 
    #
access we have at the moment and other "known" facts such as from 
    #
the "uname" call. If the $co is amongst the things we "know" then 
    # we
have a chain of controlling operation pairs (a plan) which 
    # will take
us backward from the goal to a known fact i.e. SUCCESS

KuangPlus::pdebug "addto(): Checking known_facts for \"$co\"", 3.3;
    if
( Engine::find_match($co,"known_facts",\%KuangPlus::known_facts) ) {

push(@Engine::successful_exploits,"@Addto::plan $co");

KuangPlus::pdebug 
            "addto(): added \"@Addto::plan $co\" to
successful_exploits", 3.3;
    }

    # loop checker - make sure that the
"$co" we're looking at doesn't
    # already appear in the plan we're
looking at.
    for ( $i = 0; $i <= $#Addto::plan; $i += 2 ) {

my($entry) = "@Addto::plan[$i .. $i+1]";
        KuangPlus::pdebug 

"addto(): loop checker: comparing \"$co\" to \"$entry\"", 3.3;

if ($entry eq $co ) {
            KuangPlus::pdebug 

"addto(): loop found \"$entry\" eq \"$co\". ", 3.3;

KuangPlus::pdebug 
                "addto(): loop: \"$co\" is in
\"@Addto::plan\" already.", 3.3;
            return;
        }
    }

    #
add this $co to the existing @plan because it is not "known" and is not 

# a loop. It will be later loaded as a new goal if we haven't already

# looked at this @plan
    push(@Addto::plan, "$co");

    # check to see
if the plan has been done
    if ( defined($Engine::beendone{"@Addto::plan"}
) ) {
        KuangPlus::pdebug 
            "addto(): This plan,
\"@Addto::plan\", has been done.", 3.3;
        return;
    }

    # This
plan hasn't been done already, so store it in the "beendone"
    # array
...
    $Engine::beendone{"@Addto::plan"} = 1;
    # and load it to "new"
as it is worth pursuing further.
    push(@Engine::new, "@Addto::plan");

KuangPlus::pdebug 
        "addto(): Adding \"@Addto::plan\" to
\@Engine::new plan array.", 3.3;
} # end of addto()

# ------------------

# init_kuang()
# Creates other data structures as required.
#
# Calls: 
#
Called by: evaluate_plans()
# Expects: Nothing.
# Returns:
# Global
references: sets $KuangPlus::goal
# Errors: prints to STDERR if plan
doesn't have the correct ingredients.
#
sub init_kuang {
    my($plans_r) =
$_[0];

    KuangPlus::pdebug "init_kuang: plans are ".keys(%$plans_r),
4.2;
    $KuangPlus::goal = "u 0";
    # load pre-generated plans to search
space. The pregenerated plans 
    # come from the scripts in the "rules/"
subdirectory. By loading the 
    # plans into a new "search_space" array,
we get the opportunity to 
    # discard anything that doesn't appear to be
sensible. In this context,
    # a rule which contains the goal on the RHS
is silly to pursue i.e. we
    # are looking for exploits to achieve the
goal so it makes no sense to
    # pursue a rule which requires the goal as
a pre-existing condition.
    foreach (keys(%$plans_r)) {
        # The
"next" line is only useful if we are reading from a file.
        # next if
( /^#/ || /^$/ ); # ignore blank lines and comments
        if ( $_ =~
/^[ugrw] [^ \t]+ $KuangPlus::goal$/ ) {
            KuangPlus::pdebug 

"Goal is on RHS of plan. Skipping \"$_\"", 4.6;
        } else
{
            KuangPlus::pdebug 
                "init_kuang: loading
\"$_\" to search space", 4.2;
            push(@Engine::search_space,$_);

}
    }
    KuangPlus::pdebug 
        "init_kuang: search_space is
\"@Engine::search_space\"", 4.3;
}

# ------------------ 
#
evaluate_plans()
#	Main component of Engine
#
# Calls: init_kuang(),
KuangPlus::pdebug()
# Called by: KuangPlus::
# Expects:
# Returns:
nothing
# Side effects: prints out successful attacks if encountered.
#
External references: uses $KuangPlus::goal.
# Errors:
#
sub evaluate_plans
{
    my($plans_r) = $_[0];

    my($s_plan,$o_plan);
    my(@old);


init_kuang($plans_r);

    KuangPlus::pdebug "goal: \"$KuangPlus::goal\"",
5.3;
    if ( $KuangPlus::goal =~ /^([ugrw]) ([^ \t]+)$/ ) {
        #
initial setup for a new goal
        @old = ();
        @Engine::new = ();

%Engine::beendone = ();
        # As a side effect of the next
"addto()" call, the "@Engine::new"
        # variable is set to the goal.
Note that $1 and $2 are from the 
        # regular expression match in the
"if" above.
        addto($1, $2);
        # The following "while" loop
will continue trying to match the RHS
        # of the goal(s) with the LHS
of plans in the search space.
        while ( $#Engine::new >= 0 ) {

KuangPlus::pdebug 
                "Starting search.".@Engine::new."
goals: \"@Engine::new\"", 5.3;
            @old = @Engine::new;

@Engine::new = ();
            foreach $o_plan (@old) {

my(@o_plan) = split(' ',$o_plan);
                my($o_op) =
$o_plan[$#o_plan-1];
                my($o_ob) = $o_plan[$#o_plan];

KuangPlus::pdebug "* matching for goal \"$o_plan\" ...", 5.3;

foreach $s_plan ( @Engine::search_space ) {

my(@s_plan) = split(' ',$s_plan);
                    my($s_op) =
$s_plan[0];
                    my($s_ob) = $s_plan[1];

KuangPlus::pdebug "** search space:\"$s_plan\"", 5.3;

KuangPlus::pdebug "** goal:\"$o_plan\"", 5.3;
                    # in
order to make the regexp work, the regexp has to
                    # be
on the RHS of the test. However, there is a chance
                    # it
will appear on either side so we have to do two
          #
tests, where the second has the LHS and RHS
                    #
transposed in relation to the first. Note the use of
                    #
start '^' and end '$' characters on RHS to stop
                    #
sub-string matches.
                    if (( "$o_op $o_ob" =~ /^$s_op
$s_ob$/ ) ||
                        ( "$s_op $s_ob" =~ /^$o_op $o_ob$/ ))
{
                        KuangPlus::pdebug 

"*** found a match, calling addto()", 5.3;

my($s_lop) = $s_plan[$#s_plan-1];
                        my($s_lob) =
$s_plan[$#s_plan];
                        addto($s_lop,$s_lob,"@o_plan");

}
                }
            }
        }

return @Engine::successful_exploits;
    } else {
        print STDERR "bad
goal \"$KuangPlus::goal\"";
    }
}

1;

