b9f4d11d0a
to struct field order.
519 lines
9.5 KiB
C
519 lines
9.5 KiB
C
/**
|
|
*@file arachne.c
|
|
*
|
|
* Introduces a method for proofs akin to the Athena modelchecker
|
|
* http://www.ece.cmu.edu/~dawnsong/athena/
|
|
*
|
|
*/
|
|
|
|
#include "term.h"
|
|
#include "termlist.h"
|
|
#include "role.h"
|
|
#include "system.h"
|
|
#include "compiler.h"
|
|
#include "states.h"
|
|
#include "mgu.h"
|
|
#include "arachne.h"
|
|
|
|
static System sys;
|
|
Protocol INTRUDER; // Pointers, to be set by the Init
|
|
Role I_GOAL; // Same here.
|
|
Role I_TEE;
|
|
Role I_SPLIT;
|
|
Role I_TUPLE;
|
|
Role I_ENCRYPT;
|
|
Role I_DECRYPT;
|
|
|
|
#ifdef DEBUG
|
|
static char *explanation; // Pointer to a string that describes what we just tried to do
|
|
static int e_run;
|
|
static Term e_term1;
|
|
static Term e_term2;
|
|
static Term e_term3;
|
|
static int indentDepth;
|
|
#endif
|
|
|
|
struct goalstruct
|
|
{
|
|
int run;
|
|
int index;
|
|
Roledef rd;
|
|
};
|
|
|
|
typedef struct goalstruct Goal;
|
|
|
|
/**
|
|
* Forward declarations
|
|
*/
|
|
|
|
int iterate ();
|
|
|
|
/**
|
|
* Program code
|
|
*/
|
|
|
|
//! Init Arachne engine
|
|
void
|
|
arachneInit (const System mysys)
|
|
{
|
|
Roledef rd = NULL;
|
|
|
|
void add_event (int event, Term message)
|
|
{
|
|
rd = roledefAdd (rd, event, NULL, NULL, NULL, message, NULL);
|
|
}
|
|
Role add_role (const char *rolenamestring)
|
|
{
|
|
Role r;
|
|
Term rolename;
|
|
|
|
rolename = makeGlobalConstant (rolenamestring);
|
|
r = roleCreate (rolename);
|
|
r->roledef = rd;
|
|
rd = NULL;
|
|
r->next = INTRUDER->roles;
|
|
INTRUDER->roles = r;
|
|
// compute_role_variables (sys, INTRUDER, r);
|
|
return r;
|
|
}
|
|
|
|
sys = mysys; // make sys available for this module as a global
|
|
/*
|
|
* Add intruder protocol roles
|
|
*/
|
|
|
|
INTRUDER = protocolCreate (makeGlobalConstant (" INTRUDER "));
|
|
|
|
add_event (READ, NULL);
|
|
I_GOAL = add_role (" I_GOAL ");
|
|
|
|
return;
|
|
}
|
|
|
|
//! Close Arachne engine
|
|
void
|
|
arachneDone ()
|
|
{
|
|
return;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// Detail
|
|
//------------------------------------------------------------------------
|
|
|
|
/*
|
|
* runs[rid].step is now the number of 'valid' events within the run, but we
|
|
* call it 'length' here.
|
|
*/
|
|
#define INVALID -1
|
|
#define isGoal(rd) (rd->type == READ && !rd->internal)
|
|
#define isBound(rd) (rd->bind_run != INVALID)
|
|
#define length step
|
|
|
|
#ifdef DEBUG
|
|
//! Indent print
|
|
void
|
|
indentPrint ()
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < indentDepth; i++)
|
|
eprintf ("| ");
|
|
}
|
|
#endif
|
|
|
|
//! Iterate but discard the info of the termlist
|
|
int
|
|
mgu_iterate (const Termlist tl)
|
|
{
|
|
return iterate ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// Sub
|
|
//------------------------------------------------------------------------
|
|
|
|
//! Iterate over all send types in the roles (including the intruder ones)
|
|
/**
|
|
* Function is called with (protocol pointer, role pointer, roledef pointer, index)
|
|
* and returns an integer. If it is false, iteration aborts.
|
|
*/
|
|
int
|
|
iterate_role_sends (int (*func) ())
|
|
{
|
|
Protocol p;
|
|
|
|
p = sys->protocols;
|
|
while (p != NULL)
|
|
{
|
|
Role r;
|
|
|
|
r = p->roles;
|
|
while (r != NULL)
|
|
{
|
|
Roledef rd;
|
|
int index;
|
|
|
|
rd = r->roledef;
|
|
index = 0;
|
|
while (rd != NULL)
|
|
{
|
|
if (rd->type == SEND)
|
|
{
|
|
int x;
|
|
|
|
x = 1;
|
|
if (!func (p, r, rd, index))
|
|
return 0;
|
|
}
|
|
index++;
|
|
rd = rd->next;
|
|
}
|
|
r = r->next;
|
|
}
|
|
p = p->next;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//! Generates a new intruder goal, iterates
|
|
/**
|
|
* Sloppy, does not unify term but hardcodes it into the stuff.
|
|
*/
|
|
int
|
|
add_intruder_goal (Goal goal)
|
|
{
|
|
int run;
|
|
int flag;
|
|
Roledef rd;
|
|
|
|
roleInstance (sys, INTRUDER, I_GOAL, NULL);
|
|
run = sys->maxruns - 1;
|
|
goal.rd->bind_run = run;
|
|
goal.rd->bind_index = 0;
|
|
rd = sys->runs[run].start;
|
|
termDelete (rd->message);
|
|
rd->message = termDuplicate (goal.rd->message);
|
|
|
|
#ifdef DEBUG
|
|
explanation = "Adding intruder goal for run";
|
|
e_run = goal.run;
|
|
e_term1 = goal.rd->message;
|
|
#endif
|
|
flag = iterate ();
|
|
|
|
roleInstanceDestroy (sys); // destroy the created run
|
|
goal.rd->bind_run = INVALID;
|
|
return flag;
|
|
}
|
|
|
|
//! Bind a goal to an existing regular run, if possible
|
|
int
|
|
bind_existing_run (const Goal goal, const Protocol p, const Role r,
|
|
const int index)
|
|
{
|
|
int run, flag;
|
|
|
|
flag = 1;
|
|
for (run = 0; run < sys->maxruns; run++)
|
|
{
|
|
if (sys->runs[run].protocol == p && sys->runs[run].role == r)
|
|
{
|
|
int i;
|
|
int old_length;
|
|
Roledef rd;
|
|
|
|
// find roledef entry
|
|
rd = sys->runs[run].start;
|
|
for (i = 0; i < index; i++)
|
|
rd = rd->next;
|
|
|
|
// mgu and iterate
|
|
old_length = sys->runs[run].length;
|
|
if (index >= old_length)
|
|
sys->runs[run].length = index + 1;
|
|
#ifdef DEBUG
|
|
explanation = "Bind existing run";
|
|
e_run = run;
|
|
e_term1 = goal.rd->message;
|
|
#endif
|
|
flag =
|
|
flag & termMguInTerm (goal.rd->message, rd->message, mgu_iterate);
|
|
sys->runs[run].length = old_length;
|
|
}
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
//! Bind a goal to a new run
|
|
int
|
|
bind_new_run (const Goal goal, const Protocol p, const Role r,
|
|
const int index)
|
|
{
|
|
int run;
|
|
int flag;
|
|
Roledef rd;
|
|
|
|
roleInstance (sys, p, r, NULL);
|
|
run = sys->maxruns - 1;
|
|
sys->runs[run].length = index + 1;
|
|
goal.rd->bind_run = run;
|
|
goal.rd->bind_index = index;
|
|
rd = roledef_shift (sys->runs[run].start, index);
|
|
#ifdef DEBUG
|
|
explanation = "Bind new run";
|
|
#endif
|
|
|
|
iterate ();
|
|
|
|
goal.rd->bind_run = INVALID;
|
|
roleInstanceDestroy (sys);
|
|
return flag;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// Larger logical componentents
|
|
//------------------------------------------------------------------------
|
|
|
|
//! Goal selection
|
|
/**
|
|
* Should be ordered to prefer most constrained; for now, it is simply the first one encountered.
|
|
*/
|
|
Goal
|
|
select_goal ()
|
|
{
|
|
Goal goal;
|
|
int run;
|
|
|
|
goal.run = INVALID;
|
|
goal.rd = NULL;
|
|
for (run = 0; run < sys->maxruns; run++)
|
|
{
|
|
Roledef rd;
|
|
int index;
|
|
|
|
index = 0;
|
|
rd = sys->runs[run].start;
|
|
while (rd != NULL && index < sys->runs[run].length)
|
|
{
|
|
if (isGoal (rd) && !isBound (rd))
|
|
{
|
|
// Return this goal
|
|
goal.run = run;
|
|
goal.index = index;
|
|
goal.rd = rd;
|
|
return goal;
|
|
}
|
|
index++;
|
|
rd = rd->next;
|
|
}
|
|
}
|
|
return goal;
|
|
}
|
|
|
|
//! Bind a regular goal
|
|
int
|
|
bind_goal_regular (const Goal goal)
|
|
{
|
|
int flag;
|
|
|
|
/*
|
|
* This is a local function so we have access to goal
|
|
*/
|
|
int bind_this (Protocol p, Role r, Roledef rd, int index)
|
|
{
|
|
int element_f1 (Termlist substlist)
|
|
{
|
|
/**
|
|
* Two options; as this, it is from an existing run,
|
|
* or from a new one.
|
|
*
|
|
* Note that we only bind to regular runs here
|
|
*/
|
|
int flag;
|
|
|
|
if (p == INTRUDER)
|
|
{
|
|
return 1; // don't abort scans
|
|
}
|
|
flag = bind_existing_run (goal, p, r, index);
|
|
if (flag)
|
|
{
|
|
flag = bind_new_run (goal, p, r, index);
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
// Test for interm unification
|
|
return termMguInTerm (goal.rd->message, rd->message, element_f1);
|
|
}
|
|
|
|
// Bind to all possible sends?
|
|
flag = iterate_role_sends (bind_this);
|
|
// Bind to an intruder node?
|
|
if (flag)
|
|
{
|
|
add_intruder_goal (goal); // creates a new run
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
//! Bind an intruder goal
|
|
int
|
|
bind_goal_intruder (const Goal goal)
|
|
{
|
|
//!@todo Fix intruder goal stuff
|
|
}
|
|
|
|
//! Bind a goal in all possible ways
|
|
int
|
|
bind_goal (const Goal goal)
|
|
{
|
|
if (sys->runs[goal.run].protocol == INTRUDER)
|
|
{
|
|
return bind_goal_intruder (goal);
|
|
}
|
|
else
|
|
{
|
|
return bind_goal_regular (goal);
|
|
}
|
|
}
|
|
|
|
//! Prune determination
|
|
/**
|
|
*@returns true iff this state is invalid for some reason
|
|
*/
|
|
int
|
|
prune ()
|
|
{
|
|
if (sys->maxruns > 5)
|
|
{
|
|
// Hardcoded limit on runs
|
|
#ifdef DEBUG
|
|
indentPrint ();
|
|
eprintf ("Pruned because too many runs.\n");
|
|
#endif
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// Main logic core
|
|
//------------------------------------------------------------------------
|
|
|
|
//! Main recursive procedure for Arachne
|
|
int
|
|
iterate ()
|
|
{
|
|
int flag;
|
|
Goal goal;
|
|
|
|
flag = 1;
|
|
#ifdef DEBUG
|
|
indentDepth++;
|
|
#endif
|
|
if (!prune ())
|
|
{
|
|
/**
|
|
* Not pruned: count
|
|
*/
|
|
|
|
sys->states = statesIncrease (sys->states);
|
|
#ifdef DEBUG
|
|
if (explanation != NULL)
|
|
{
|
|
indentPrint ();
|
|
eprintf ("%s ", explanation);
|
|
|
|
if (e_run != INVALID)
|
|
eprintf ("#%i ", e_run);
|
|
if (e_term1 != NULL)
|
|
{
|
|
termPrint (e_term1);
|
|
eprintf (" ");
|
|
}
|
|
if (e_term2 != NULL)
|
|
{
|
|
termPrint (e_term2);
|
|
eprintf (" ");
|
|
}
|
|
if (e_term3 != NULL)
|
|
{
|
|
termPrint (e_term3);
|
|
eprintf (" ");
|
|
}
|
|
eprintf ("\n");
|
|
explanation = NULL;
|
|
e_run = INVALID;
|
|
e_term1 = NULL;
|
|
e_term2 = NULL;
|
|
e_term3 = NULL;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Check whether its a final state (i.e. all goals bound)
|
|
*/
|
|
|
|
goal = select_goal ();
|
|
if (goal.run == INVALID)
|
|
{
|
|
/*
|
|
* all goals bound, check for property
|
|
*/
|
|
//!@todo Property check in Arachne.
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
indentPrint ();
|
|
eprintf ("Trying to bind goal ");
|
|
termPrint (goal.rd->message);
|
|
eprintf (" from run %i, index %i.\n", goal.run, goal.index);
|
|
#endif
|
|
/*
|
|
* bind this goal in all possible ways and iterate
|
|
*/
|
|
flag = bind_goal (goal);
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
indentDepth--;
|
|
#endif
|
|
return flag;
|
|
}
|
|
|
|
//! Main code for Arachne
|
|
/**
|
|
* For this test, we manually set up some stuff.
|
|
*
|
|
* But later, this will just iterate over all claims.
|
|
*/
|
|
int
|
|
arachne ()
|
|
{
|
|
/*
|
|
* set up claim role(s)
|
|
*/
|
|
|
|
if (sys->maxruns > 0)
|
|
{
|
|
sys->runs[0].length = roledef_length (sys->runs[0].start);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
explanation = NULL;
|
|
e_run = INVALID;
|
|
e_term1 = NULL;
|
|
e_term2 = NULL;
|
|
e_term3 = NULL;
|
|
indentDepth = 0;
|
|
#endif
|
|
|
|
/*
|
|
* iterate
|
|
*/
|
|
iterate ();
|
|
}
|