2007-06-11 13:01:04 +01:00
|
|
|
/*
|
|
|
|
* Scyther : An automatic verifier for security protocols.
|
2012-04-24 12:56:51 +01:00
|
|
|
* Copyright (C) 2007-2012 Cas Cremers
|
2007-06-11 13:01:04 +01:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
2004-05-15 13:33:01 +01:00
|
|
|
/**
|
2004-07-24 16:05:20 +01:00
|
|
|
* @file system.c
|
|
|
|
* \brief system related logic.
|
2004-05-15 13:33:01 +01:00
|
|
|
*/
|
2004-04-23 11:58:43 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <limits.h>
|
2004-07-24 20:07:29 +01:00
|
|
|
#include "term.h"
|
|
|
|
#include "termlist.h"
|
2004-04-23 11:58:43 +01:00
|
|
|
#include "knowledge.h"
|
2004-07-24 16:08:35 +01:00
|
|
|
#include "system.h"
|
2004-04-23 11:58:43 +01:00
|
|
|
#include "debug.h"
|
2007-01-06 14:45:29 +00:00
|
|
|
#include "error.h"
|
2004-07-24 20:07:29 +01:00
|
|
|
#include "role.h"
|
2005-06-02 13:14:28 +01:00
|
|
|
#include "mgu.h"
|
2005-06-07 16:02:27 +01:00
|
|
|
#include "switches.h"
|
|
|
|
#include "binding.h"
|
2006-02-26 15:00:58 +00:00
|
|
|
#include "depend.h"
|
2005-06-16 15:10:07 +01:00
|
|
|
#include "specialterm.h"
|
2004-04-23 11:58:43 +01:00
|
|
|
|
2004-08-27 11:08:03 +01:00
|
|
|
//! Global count of protocols
|
|
|
|
int protocolCount;
|
|
|
|
|
2006-02-27 16:08:17 +00:00
|
|
|
//! External
|
|
|
|
extern Protocol INTRUDER;
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Switch for indent or not.
|
2004-04-23 11:58:43 +01:00
|
|
|
static int indentState = 0;
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Current indent depth.
|
2004-04-23 11:58:43 +01:00
|
|
|
static int indentDepth = 0;
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Initialise a system structure.
|
|
|
|
/**
|
|
|
|
*@return A system structure pointer with initial values.
|
|
|
|
*/
|
2004-04-23 11:58:43 +01:00
|
|
|
System
|
|
|
|
systemInit ()
|
|
|
|
{
|
2006-03-08 13:58:46 +00:00
|
|
|
System sys = (System) malloc (sizeof (struct system));
|
2004-04-23 11:58:43 +01:00
|
|
|
|
|
|
|
/* initially, no trace ofcourse */
|
|
|
|
sys->step = 0;
|
|
|
|
sys->shortestattack = INT_MAX;
|
|
|
|
sys->maxtracelength = INT_MAX;
|
|
|
|
|
|
|
|
/* init rundefs */
|
|
|
|
sys->maxruns = 0;
|
|
|
|
sys->runs = NULL;
|
|
|
|
/* no protocols yet */
|
2004-08-27 11:08:03 +01:00
|
|
|
protocolCount = 0;
|
2004-04-23 11:58:43 +01:00
|
|
|
sys->protocols = NULL;
|
|
|
|
sys->locals = NULL;
|
|
|
|
sys->variables = NULL;
|
2006-07-27 12:55:24 +01:00
|
|
|
sys->agentnames = NULL;
|
2004-04-23 11:58:43 +01:00
|
|
|
sys->untrusted = NULL;
|
2006-02-21 20:29:05 +00:00
|
|
|
sys->globalconstants = NULL;
|
|
|
|
sys->hidden = NULL;
|
2004-04-23 11:58:43 +01:00
|
|
|
sys->secrets = NULL; // list of claimed secrets
|
2004-07-24 21:30:00 +01:00
|
|
|
sys->synchronising_labels = NULL;
|
2004-06-14 23:08:47 +01:00
|
|
|
/* no protocols => no protocol preprocessed */
|
2004-06-16 11:39:13 +01:00
|
|
|
sys->rolecount = 0;
|
|
|
|
sys->roleeventmax = 0;
|
2004-06-14 23:08:47 +01:00
|
|
|
sys->claimlist = NULL;
|
2004-08-27 12:52:43 +01:00
|
|
|
sys->labellist = NULL;
|
2006-07-31 12:08:51 +01:00
|
|
|
sys->knowledgedefined = false; // currently, we have backwards compatibility for empty role knowledge defs (disabling well-formedness rules)
|
2005-06-07 14:40:56 +01:00
|
|
|
sys->attackid = 0; // First attack will have id 1, because the counter is increased before any attacks are displayed.
|
2004-04-23 11:58:43 +01:00
|
|
|
|
2007-01-16 17:22:51 +00:00
|
|
|
/* arachne assist */
|
2006-03-08 13:58:46 +00:00
|
|
|
bindingInit (sys);
|
2005-05-02 14:38:45 +01:00
|
|
|
sys->bindings = NULL;
|
|
|
|
sys->current_claim = NULL;
|
2007-01-16 17:22:51 +00:00
|
|
|
sys->trustedRoles = NULL;
|
2008-01-28 14:23:40 +00:00
|
|
|
sys->hasUntypedVariable = false;
|
2005-05-02 14:38:45 +01:00
|
|
|
|
2004-04-23 11:58:43 +01:00
|
|
|
/* reset global counters */
|
|
|
|
systemReset (sys);
|
|
|
|
|
|
|
|
return sys;
|
|
|
|
}
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Reset a system state after some exploration.
|
|
|
|
/**
|
2004-06-13 21:58:54 +01:00
|
|
|
*@param sys A system structure pointer.
|
2004-05-15 15:47:19 +01:00
|
|
|
*@return Counter values have been reset.
|
|
|
|
*/
|
2004-04-23 11:58:43 +01:00
|
|
|
void
|
|
|
|
systemReset (const System sys)
|
|
|
|
{
|
2004-06-14 23:08:47 +01:00
|
|
|
Claimlist cl;
|
|
|
|
|
2004-04-23 11:58:43 +01:00
|
|
|
/* some initial counters */
|
2004-07-21 15:26:28 +01:00
|
|
|
sys->states = statesIncrease (STATES0); //!< Initial state is not explored, so start counting at 1
|
2004-08-09 11:05:58 +01:00
|
|
|
sys->interval = sys->states; //!< To keep in line with the states
|
2004-07-21 11:35:39 +01:00
|
|
|
sys->claims = STATES0;
|
|
|
|
sys->failed = STATES0;
|
2004-04-23 11:58:43 +01:00
|
|
|
sys->explore = 1; // do explore the space
|
2004-06-14 23:08:47 +01:00
|
|
|
cl = sys->claimlist;
|
|
|
|
while (cl != NULL)
|
|
|
|
{
|
2004-07-21 15:26:28 +01:00
|
|
|
cl->count = STATES0;
|
|
|
|
cl->failed = STATES0;
|
2004-06-14 23:08:47 +01:00
|
|
|
cl = cl->next;
|
|
|
|
}
|
2004-08-09 11:05:58 +01:00
|
|
|
|
2004-04-23 11:58:43 +01:00
|
|
|
termlistDestroy (sys->secrets); // remove old secrets list
|
|
|
|
sys->secrets = NULL; // list of claimed secrets
|
2006-04-02 13:29:02 +01:00
|
|
|
sys->num_intruder_runs = 0; // number of intruder runs
|
|
|
|
sys->num_regular_runs = 0; // number of regular runs
|
2004-04-23 11:58:43 +01:00
|
|
|
|
|
|
|
/* transfer switches */
|
2005-06-07 16:02:27 +01:00
|
|
|
sys->maxtracelength = switches.maxtracelength;
|
2004-07-28 12:39:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//! Initialize runtime system (according to cut traces, limited runs)
|
|
|
|
void
|
|
|
|
systemRuns (const System sys)
|
|
|
|
{
|
|
|
|
int run;
|
|
|
|
|
|
|
|
sys->lastChooseRun = -1;
|
|
|
|
for (run = 0; run < sys->maxruns; run++)
|
|
|
|
{
|
|
|
|
Roledef rd;
|
|
|
|
|
|
|
|
rd = runPointerGet (sys, run);
|
2004-08-09 11:05:58 +01:00
|
|
|
if (rd != NULL && rd->internal && rd->type == READ)
|
2004-07-28 12:39:08 +01:00
|
|
|
{
|
|
|
|
/* increasing run traversal, so this yields max */
|
|
|
|
sys->lastChooseRun = run;
|
|
|
|
}
|
|
|
|
}
|
2004-04-23 11:58:43 +01:00
|
|
|
}
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Delete a system structure and clear used memory for all buffers.
|
|
|
|
/**
|
|
|
|
* Is more thorough than systemDestroy().
|
|
|
|
*\sa systemDestroy()
|
|
|
|
*/
|
2004-04-23 11:58:43 +01:00
|
|
|
void
|
2004-07-20 13:41:56 +01:00
|
|
|
systemDone (const System sys)
|
2004-04-23 11:58:43 +01:00
|
|
|
{
|
|
|
|
int s;
|
|
|
|
|
|
|
|
/* clear globals, which were defined in systemStart */
|
|
|
|
|
|
|
|
s = sys->maxtracelength + 1;
|
2006-03-08 13:58:46 +00:00
|
|
|
free (sys->traceEvent);
|
|
|
|
free (sys->traceRun);
|
|
|
|
free (sys->traceKnow);
|
|
|
|
free (sys->traceNode);
|
2004-04-23 11:58:43 +01:00
|
|
|
|
|
|
|
/* clear roledefs */
|
2004-08-10 16:02:37 +01:00
|
|
|
while (sys->maxruns > 0)
|
|
|
|
{
|
|
|
|
roleInstanceDestroy (sys);
|
|
|
|
}
|
2004-04-23 11:58:43 +01:00
|
|
|
|
2006-02-26 15:00:58 +00:00
|
|
|
/* undo bindings (for arachne) */
|
|
|
|
|
2006-03-08 13:58:46 +00:00
|
|
|
bindingDone ();
|
2006-02-26 15:00:58 +00:00
|
|
|
|
2004-04-23 11:58:43 +01:00
|
|
|
/* clear substructures */
|
|
|
|
termlistDestroy (sys->secrets);
|
|
|
|
|
|
|
|
/* clear main system */
|
|
|
|
systemDestroy (sys);
|
|
|
|
}
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Destroy a system memory block and system::runs
|
|
|
|
/**
|
|
|
|
* Ignores any other substructes.
|
|
|
|
*\sa systemDone()
|
|
|
|
*/
|
2004-04-23 11:58:43 +01:00
|
|
|
void
|
2004-07-20 13:41:56 +01:00
|
|
|
systemDestroy (const System sys)
|
2004-04-23 11:58:43 +01:00
|
|
|
{
|
2006-03-08 13:58:46 +00:00
|
|
|
free (sys->runs);
|
|
|
|
free (sys);
|
2004-04-23 11:58:43 +01:00
|
|
|
}
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Ensures that a run can be added to the system.
|
|
|
|
/**
|
|
|
|
* Allocates memory to allow a run to be added, if needed.
|
|
|
|
* This is meant to be used before using runPointerSet().
|
|
|
|
*/
|
2004-04-23 11:58:43 +01:00
|
|
|
|
|
|
|
void
|
2004-07-20 13:41:56 +01:00
|
|
|
ensureValidRun (const System sys, int run)
|
2004-04-23 11:58:43 +01:00
|
|
|
{
|
|
|
|
int i, oldsize;
|
|
|
|
|
|
|
|
if (run < sys->maxruns)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* this amount of memory was not allocated yet */
|
|
|
|
/* (re)allocate space */
|
|
|
|
/* Note, this is never explicitly freed, because it is never
|
|
|
|
copied */
|
|
|
|
/* update size parameter */
|
|
|
|
oldsize = sys->maxruns;
|
|
|
|
sys->maxruns = run + 1;
|
2006-03-08 13:58:46 +00:00
|
|
|
sys->runs = (Run) realloc (sys->runs, sizeof (struct run) * (sys->maxruns));
|
2004-04-23 11:58:43 +01:00
|
|
|
|
|
|
|
/* create runs, set the new pointer(s) to NULL */
|
|
|
|
for (i = oldsize; i < sys->maxruns; i++)
|
|
|
|
{
|
|
|
|
/* init run */
|
2006-03-15 21:30:19 +00:00
|
|
|
sys->runs[i].protocol = NULL;
|
|
|
|
sys->runs[i].role = NULL;
|
|
|
|
sys->runs[i].step = 0;
|
|
|
|
sys->runs[i].rolelength = 0;
|
|
|
|
|
|
|
|
sys->runs[i].index = NULL;
|
|
|
|
sys->runs[i].start = NULL;
|
|
|
|
sys->runs[i].know = NULL;
|
|
|
|
|
|
|
|
sys->runs[i].rho = NULL;
|
|
|
|
sys->runs[i].sigma = NULL;
|
|
|
|
sys->runs[i].constants = NULL;
|
|
|
|
|
|
|
|
sys->runs[i].locals = NULL;
|
|
|
|
sys->runs[i].artefacts = NULL;
|
|
|
|
sys->runs[i].substitutions = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
sys->runs[i].prevSymmRun = -1;
|
|
|
|
sys->runs[i].firstNonAgentRead = -1;
|
|
|
|
sys->runs[i].firstReal = 0;
|
2004-04-23 11:58:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Print a run.
|
2004-04-23 11:58:43 +01:00
|
|
|
void
|
|
|
|
runPrint (Roledef rd)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
indent ();
|
|
|
|
i = 0;
|
|
|
|
while (rd != NULL)
|
|
|
|
{
|
2004-10-28 13:33:57 +01:00
|
|
|
eprintf ("%i: ", i);
|
2004-04-23 11:58:43 +01:00
|
|
|
roledefPrint (rd);
|
2004-10-28 13:33:57 +01:00
|
|
|
eprintf ("\n");
|
2004-04-23 11:58:43 +01:00
|
|
|
i++;
|
|
|
|
rd = rd->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Print all runs in the system structure.
|
2004-04-23 11:58:43 +01:00
|
|
|
void
|
2004-07-20 13:41:56 +01:00
|
|
|
runsPrint (const System sys)
|
2004-04-23 11:58:43 +01:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
indent ();
|
2004-10-28 13:33:57 +01:00
|
|
|
eprintf ("[ Run definitions ]\n");
|
2004-04-23 11:58:43 +01:00
|
|
|
for (i = 0; i < (sys->maxruns); i++)
|
|
|
|
{
|
|
|
|
indent ();
|
2004-10-28 13:33:57 +01:00
|
|
|
eprintf ("Run definition %i:\n", i);
|
2004-04-23 11:58:43 +01:00
|
|
|
runPrint (runPointerGet (sys, i));
|
2004-10-28 13:33:57 +01:00
|
|
|
eprintf ("\n");
|
2004-04-23 11:58:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-08-31 15:31:06 +01:00
|
|
|
//! Determine whether a term is sent or claimed, but not read first in a roledef
|
2004-10-12 17:58:29 +01:00
|
|
|
/**
|
|
|
|
* @returns True iff the term occurs, and is sent/claimed first. If this returns true,
|
|
|
|
* we have to prefix a read.
|
|
|
|
*/
|
2004-09-20 13:40:01 +01:00
|
|
|
int
|
|
|
|
not_read_first (const Roledef rdstart, const Term t)
|
2004-08-31 15:31:06 +01:00
|
|
|
{
|
|
|
|
Roledef rd;
|
|
|
|
|
|
|
|
rd = rdstart;
|
|
|
|
while (rd != NULL)
|
|
|
|
{
|
|
|
|
if (termSubTerm (rd->message, t))
|
|
|
|
{
|
|
|
|
return (rd->type != READ);
|
|
|
|
}
|
|
|
|
rd = rd->next;
|
|
|
|
}
|
2004-10-12 17:58:29 +01:00
|
|
|
/* this term is not read or sent explicitly, which is no problem */
|
|
|
|
/* So we signal we don't have to prefix a read */
|
2004-08-31 15:31:06 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Yield the agent name term in a role, for a run in the system.
|
|
|
|
/**
|
|
|
|
*@param sys The system.
|
|
|
|
*@param run The run in which we are interested.
|
|
|
|
*@param role The role of which we want to know the agent.
|
2004-04-23 11:58:43 +01:00
|
|
|
*/
|
|
|
|
Term
|
|
|
|
agentOfRunRole (const System sys, const int run, const Term role)
|
|
|
|
{
|
2006-03-08 13:58:46 +00:00
|
|
|
Termlist agents;
|
2005-05-01 14:32:50 +01:00
|
|
|
|
2006-03-08 13:58:46 +00:00
|
|
|
// Agent variables have the same symbol as the role names, so
|
|
|
|
// we can scan for this.
|
2006-03-15 21:30:19 +00:00
|
|
|
agents = sys->runs[run].rho;
|
2006-03-08 13:58:46 +00:00
|
|
|
while (agents != NULL)
|
|
|
|
{
|
|
|
|
Term agent;
|
2005-02-21 15:12:59 +00:00
|
|
|
|
2006-03-08 13:58:46 +00:00
|
|
|
agent = agents->term;
|
|
|
|
if (TermSymb (role) == TermSymb (agent))
|
2004-04-23 11:58:43 +01:00
|
|
|
{
|
2006-03-08 13:58:46 +00:00
|
|
|
return agent;
|
2004-04-23 11:58:43 +01:00
|
|
|
}
|
2005-02-21 15:12:59 +00:00
|
|
|
else
|
|
|
|
{
|
2006-03-08 13:58:46 +00:00
|
|
|
agents = agents->next;
|
2005-02-21 15:12:59 +00:00
|
|
|
}
|
2004-04-23 11:58:43 +01:00
|
|
|
}
|
2006-03-08 13:58:46 +00:00
|
|
|
return NULL;
|
2004-04-23 11:58:43 +01:00
|
|
|
}
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Yield the actor agent of a run in the system.
|
|
|
|
/**
|
|
|
|
*@param sys The system.
|
|
|
|
*@param run The run in which we are interested.
|
2004-04-23 11:58:43 +01:00
|
|
|
*/
|
|
|
|
Term
|
|
|
|
agentOfRun (const System sys, const int run)
|
|
|
|
{
|
2004-08-09 11:05:58 +01:00
|
|
|
return agentOfRunRole (sys, run, sys->runs[run].role->nameterm);
|
2004-04-23 11:58:43 +01:00
|
|
|
}
|
|
|
|
|
2004-07-15 12:04:15 +01:00
|
|
|
//! Determine first read with variables besides agents
|
|
|
|
/**
|
|
|
|
*@todo For now, we assume it is simply the first read after the choose, if there is one.
|
|
|
|
*/
|
2004-08-09 11:05:58 +01:00
|
|
|
int
|
|
|
|
firstNonAgentRead (const System sys, int rid)
|
2004-07-15 12:04:15 +01:00
|
|
|
{
|
|
|
|
int step;
|
|
|
|
Roledef rd;
|
|
|
|
|
|
|
|
if (sys->runs[rid].prevSymmRun == -1)
|
|
|
|
{
|
|
|
|
/* if there is no symmetrical run, then this doesn't apply at all */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
rd = sys->runs[rid].start;
|
|
|
|
step = 0;
|
|
|
|
while (rd != NULL && rd->internal && rd->type == READ) // assumes lazy LR eval
|
|
|
|
{
|
|
|
|
rd = rd->next;
|
|
|
|
step++;
|
|
|
|
}
|
2004-08-09 11:05:58 +01:00
|
|
|
if (rd != NULL && !rd->internal && rd->type == READ) // assumes lazy LR eval
|
2004-07-15 12:04:15 +01:00
|
|
|
{
|
2004-07-17 22:11:35 +01:00
|
|
|
#ifdef DEBUG
|
2004-08-09 11:05:58 +01:00
|
|
|
warning
|
|
|
|
("First read %i with dependency on symmetrical found in run %i.",
|
|
|
|
step, rid);
|
2004-07-17 22:11:35 +01:00
|
|
|
#endif
|
2004-07-15 12:04:15 +01:00
|
|
|
return step;
|
|
|
|
}
|
|
|
|
/* no such read */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2004-07-14 08:31:01 +01:00
|
|
|
|
2004-09-20 13:40:01 +01:00
|
|
|
/*************************************************
|
|
|
|
*
|
|
|
|
* Support code for roleInstance
|
|
|
|
*
|
|
|
|
*************************************************/
|
|
|
|
|
|
|
|
//! Prefix a read before a given run.
|
|
|
|
/**
|
|
|
|
* Maybe this simply needs integration in the role definitions. However, in practice it
|
|
|
|
* depends on the specific scenario. For Arachne it can thus depend on the roledef.
|
|
|
|
*
|
|
|
|
* Stores the (new) rd pointer in start and index
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
run_prefix_read (const System sys, const int run, Roledef rd,
|
|
|
|
const Term extterm)
|
|
|
|
{
|
|
|
|
/* prefix a read for such reads. TODO: this should also cover any external stuff */
|
|
|
|
if (extterm != NULL)
|
|
|
|
{
|
|
|
|
Roledef rdnew;
|
|
|
|
|
|
|
|
rdnew = roledefInit (READ, NULL, NULL, NULL, extterm, NULL);
|
|
|
|
/* this is an internal action! */
|
|
|
|
rdnew->internal = 1;
|
|
|
|
/* Store this new pointer */
|
|
|
|
rdnew->next = rd;
|
|
|
|
rd = rdnew;
|
|
|
|
/* mark the first real action */
|
|
|
|
sys->runs[run].firstReal++;
|
|
|
|
}
|
|
|
|
/* possibly shifted rd */
|
|
|
|
sys->runs[run].start = rd;
|
|
|
|
sys->runs[run].index = rd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Localize run
|
|
|
|
/**
|
|
|
|
* Takes a run roledef list and substitutes fromlist into tolist terms.
|
|
|
|
* Furthermore, localizes all substitutions occurring in this, which termLocal
|
|
|
|
* does not. Any localized substitutions are stored as well in a list.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
run_localize (const System sys, const int rid, Termlist fromlist,
|
|
|
|
Termlist tolist, Termlist substlist)
|
|
|
|
{
|
|
|
|
Roledef rd;
|
|
|
|
|
|
|
|
rd = sys->runs[rid].start;
|
|
|
|
while (rd != NULL)
|
|
|
|
{
|
2004-12-08 16:25:27 +00:00
|
|
|
rd->from = termLocal (rd->from, fromlist, tolist);
|
|
|
|
rd->to = termLocal (rd->to, fromlist, tolist);
|
|
|
|
rd->message = termLocal (rd->message, fromlist, tolist);
|
2004-09-20 13:40:01 +01:00
|
|
|
rd = rd->next;
|
|
|
|
}
|
|
|
|
|
2004-12-08 16:25:27 +00:00
|
|
|
// Substlist is NULL currently? No usage of this last stuff now
|
|
|
|
// TODO
|
|
|
|
if (substlist != NULL)
|
|
|
|
{
|
|
|
|
error ("Substlist should be NULL in run_localize");
|
|
|
|
}
|
2004-09-20 13:40:01 +01:00
|
|
|
sys->runs[rid].substitutions = NULL;
|
|
|
|
while (substlist != NULL)
|
|
|
|
{
|
|
|
|
Term t;
|
|
|
|
|
|
|
|
t = substlist->term;
|
|
|
|
if (t->subst != NULL)
|
|
|
|
{
|
2004-12-08 16:25:27 +00:00
|
|
|
t->subst = termLocal (t->subst, fromlist, tolist);
|
2004-09-20 13:40:01 +01:00
|
|
|
sys->runs[rid].substitutions =
|
|
|
|
termlistAdd (sys->runs[rid].substitutions, t);
|
|
|
|
}
|
|
|
|
substlist = substlist->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Instantiate a role by making a new run for Arachne
|
2004-05-15 15:47:19 +01:00
|
|
|
/**
|
|
|
|
* This involves creation of a new run(id).
|
|
|
|
* Copy & subst of Roledef, Agent knowledge.
|
|
|
|
* Tolist might contain type constants.
|
2004-04-23 11:58:43 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2004-09-20 13:40:01 +01:00
|
|
|
roleInstanceArachne (const System sys, const Protocol protocol,
|
|
|
|
const Role role, const Termlist paramlist,
|
|
|
|
Termlist substlist)
|
2004-04-23 11:58:43 +01:00
|
|
|
{
|
|
|
|
int rid;
|
|
|
|
Run runs;
|
|
|
|
Roledef rd;
|
2005-05-01 14:32:50 +01:00
|
|
|
Termlist fromlist = NULL; // deleted at the end
|
|
|
|
Termlist tolist = NULL; // -> .locals
|
|
|
|
Term extterm = NULL; // construction thing (will go to artefacts)
|
2004-04-23 11:58:43 +01:00
|
|
|
|
2006-03-15 21:30:19 +00:00
|
|
|
void createLocal (Term oldt, int isvariable, int isrole)
|
|
|
|
{
|
|
|
|
Term newt;
|
2004-09-20 13:40:01 +01:00
|
|
|
|
2006-03-15 21:30:19 +00:00
|
|
|
// Create new term with the same symbol
|
|
|
|
if (isvariable)
|
|
|
|
{
|
|
|
|
// Force variable
|
|
|
|
newt = makeTermType (VARIABLE, TermSymb (oldt), rid);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Force local (weirdly enough called global)
|
|
|
|
newt = makeTermType (GLOBAL, TermSymb (oldt), rid);
|
|
|
|
}
|
|
|
|
newt->stype = oldt->stype; // copy list of types
|
|
|
|
newt->roleVar = isrole; // set role status
|
|
|
|
|
|
|
|
// Add to copy list
|
|
|
|
TERMLISTADD (fromlist, oldt);
|
|
|
|
TERMLISTADD (tolist, newt);
|
|
|
|
|
|
|
|
// Add to registration lists
|
|
|
|
// Everything to destructor list
|
|
|
|
TERMLISTADD (runs[rid].artefacts, newt);
|
|
|
|
// Variable / Constant?
|
|
|
|
if (isvariable)
|
|
|
|
{
|
|
|
|
TERMLISTADD (sys->variables, newt);
|
|
|
|
if (isrole)
|
|
|
|
{
|
|
|
|
// role variable
|
|
|
|
/*
|
|
|
|
* We use append to make sure the order is
|
|
|
|
* consistent with the role names list.
|
|
|
|
*/
|
|
|
|
TERMLISTAPPEND (runs[rid].rho, newt);
|
|
|
|
if (!role->initiator)
|
|
|
|
{
|
|
|
|
// For non-initiators, we prepend the reading of the role names
|
|
|
|
|
|
|
|
// XXX disabled for now TODO [x] [cc]
|
|
|
|
if (0 == 1 && not_read_first (rd, oldt))
|
|
|
|
{
|
|
|
|
/* this term is forced as a choose, or it does not occur in the (first) read event */
|
|
|
|
if (extterm == NULL)
|
|
|
|
{
|
|
|
|
extterm = newt;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
extterm = makeTermTuple (newt, extterm);
|
|
|
|
// NOTE: don't these get double deleted? By roledefdestroy?
|
|
|
|
TERMLISTAPPEND (runs[rid].artefacts, extterm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// normal variable
|
|
|
|
TERMLISTAPPEND (runs[rid].sigma, newt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// local constant
|
|
|
|
TERMLISTADD (runs[rid].constants, newt);
|
|
|
|
}
|
|
|
|
}
|
2004-09-20 13:40:01 +01:00
|
|
|
|
2007-05-18 13:06:29 +01:00
|
|
|
//! The next function makes locals for all in the list. Flags denote whether it is a variable or role.
|
2006-03-15 21:30:19 +00:00
|
|
|
void createLocals (Termlist list, int isvariable, int isrole)
|
|
|
|
{
|
|
|
|
while (list != NULL)
|
|
|
|
{
|
|
|
|
createLocal (list->term, isvariable, isrole);
|
|
|
|
list = list->next;
|
|
|
|
}
|
|
|
|
}
|
2004-04-23 11:58:43 +01:00
|
|
|
|
2007-01-06 18:01:36 +00:00
|
|
|
/* claim runid, allocate space */
|
|
|
|
rid = sys->maxruns;
|
|
|
|
ensureValidRun (sys, rid); // creates a new block
|
|
|
|
runs = sys->runs; // simple structure pointer transfer (shortcut)
|
|
|
|
|
|
|
|
/* duplicate roledef in buffer rd */
|
|
|
|
/* Notice that it is not stored (yet) in the run structure,
|
|
|
|
* and that termDuplicate is used internally
|
|
|
|
*/
|
|
|
|
rd = roledefDuplicate (role->roledef);
|
|
|
|
|
|
|
|
/* set parameters */
|
|
|
|
/* generic setup of inherited stuff */
|
|
|
|
runs[rid].protocol = protocol;
|
|
|
|
runs[rid].role = role;
|
|
|
|
runs[rid].step = 0;
|
|
|
|
runs[rid].firstReal = 0;
|
|
|
|
|
|
|
|
/* Now we need to create local terms corresponding to rho, sigma, and any local constants.
|
|
|
|
*
|
|
|
|
* We maintain our stuff in a from/to list.
|
|
|
|
*/
|
|
|
|
|
2006-03-15 21:30:19 +00:00
|
|
|
// Create rho, sigma, constants
|
|
|
|
createLocals (protocol->rolenames, true, true);
|
|
|
|
createLocals (role->declaredvars, true, false);
|
|
|
|
createLocals (role->declaredconsts, false, false);
|
2004-08-12 10:14:31 +01:00
|
|
|
|
2004-12-08 19:30:26 +00:00
|
|
|
/* Now we prefix the read before rd, if extterm is not NULL. Even if
|
|
|
|
* extterm is NULL, rd is still set as the start and the index pointer of
|
|
|
|
* the run.
|
|
|
|
*/
|
2004-09-20 13:40:01 +01:00
|
|
|
run_prefix_read (sys, rid, rd, extterm);
|
2004-08-12 10:14:31 +01:00
|
|
|
|
2004-09-20 13:40:01 +01:00
|
|
|
/* TODO this is not what we want yet, also local knowledge. The local
|
|
|
|
* knowledge (list?) also needs to be substituted on invocation. */
|
|
|
|
runs[rid].know = NULL;
|
2004-08-31 15:31:06 +01:00
|
|
|
|
2004-09-20 13:40:01 +01:00
|
|
|
/* now adjust the local run copy */
|
|
|
|
run_localize (sys, rid, fromlist, tolist, substlist);
|
|
|
|
|
|
|
|
termlistDelete (fromlist);
|
|
|
|
runs[rid].locals = tolist;
|
|
|
|
|
|
|
|
/* erase any substitutions in the role definition, as they are now copied */
|
|
|
|
termlistSubstReset (role->variables);
|
2006-02-26 15:00:58 +00:00
|
|
|
|
|
|
|
/* length */
|
|
|
|
runs[rid].rolelength = roledef_length (runs[rid].start);
|
2006-02-26 17:18:59 +00:00
|
|
|
/* [[[ Hack ]]] this length is minimally 3 (to help the construction of the encryptors/decryptors from bare roledefs */
|
|
|
|
if (runs[rid].rolelength < 3)
|
|
|
|
{
|
|
|
|
runs[rid].rolelength = 3;
|
|
|
|
}
|
2006-02-26 15:00:58 +00:00
|
|
|
|
|
|
|
/* new graph to create */
|
|
|
|
dependPushRun (sys);
|
2004-09-20 13:40:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Instantiate a role by making a new run
|
|
|
|
/**
|
2006-03-08 13:58:46 +00:00
|
|
|
* Just forwards to Arachne version.
|
2004-09-20 13:40:01 +01:00
|
|
|
*
|
|
|
|
* This involves creation of a new run(id).
|
|
|
|
* Copy & subst of Roledef, Agent knowledge.
|
|
|
|
* Tolist might contain type constants.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
roleInstance (const System sys, const Protocol protocol, const Role role,
|
|
|
|
const Termlist paramlist, Termlist substlist)
|
|
|
|
{
|
2006-03-08 13:58:46 +00:00
|
|
|
roleInstanceArachne (sys, protocol, role, paramlist, substlist);
|
2004-09-20 13:40:01 +01:00
|
|
|
}
|
2004-04-23 11:58:43 +01:00
|
|
|
|
2004-08-10 16:02:37 +01:00
|
|
|
//! Destroy roleInstance
|
|
|
|
/**
|
|
|
|
* Destroys the run with the highest index number
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
roleInstanceDestroy (const System sys)
|
|
|
|
{
|
2004-08-13 15:35:22 +01:00
|
|
|
if (sys->maxruns > 0)
|
2004-08-10 16:02:37 +01:00
|
|
|
{
|
2004-08-13 15:35:22 +01:00
|
|
|
int runid;
|
2004-08-10 16:02:37 +01:00
|
|
|
struct run myrun;
|
2004-08-15 20:58:26 +01:00
|
|
|
Termlist substlist;
|
2004-08-10 16:02:37 +01:00
|
|
|
|
2004-08-13 15:35:22 +01:00
|
|
|
runid = sys->maxruns - 1;
|
2004-08-10 16:02:37 +01:00
|
|
|
myrun = sys->runs[runid];
|
|
|
|
|
2006-02-26 15:00:58 +00:00
|
|
|
// Reset graph
|
2006-03-08 13:58:46 +00:00
|
|
|
dependPopRun ();
|
2006-02-26 15:00:58 +00:00
|
|
|
|
2004-08-10 16:02:37 +01:00
|
|
|
// Destroy roledef
|
|
|
|
roledefDestroy (myrun.start);
|
2004-08-12 10:14:31 +01:00
|
|
|
|
2004-08-10 16:02:37 +01:00
|
|
|
// Destroy artefacts
|
2005-10-08 20:53:10 +01:00
|
|
|
//
|
2006-03-15 21:30:19 +00:00
|
|
|
termlistDelete (myrun.rho);
|
|
|
|
termlistDelete (myrun.sigma);
|
|
|
|
termlistDelete (myrun.constants);
|
|
|
|
|
2005-10-08 20:53:10 +01:00
|
|
|
// sys->variables might contain locals from the run: remove them
|
|
|
|
{
|
|
|
|
Termlist tl;
|
|
|
|
|
|
|
|
tl = sys->variables;
|
|
|
|
while (tl != NULL)
|
|
|
|
{
|
|
|
|
Term t;
|
|
|
|
|
|
|
|
t = tl->term;
|
|
|
|
if (realTermLeaf (t) && TermRunid (t) == runid)
|
|
|
|
{
|
|
|
|
Termlist tlnext;
|
|
|
|
|
|
|
|
tlnext = tl->next;
|
|
|
|
// remove from list; return pointer to head
|
|
|
|
sys->variables = termlistDelTerm (tl);
|
|
|
|
tl = tlnext;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// proceed
|
|
|
|
tl = tl->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-08-15 20:58:26 +01:00
|
|
|
/**
|
|
|
|
* Undo the local copies of the substitutions. We cannot restore them however, so this might
|
|
|
|
* prove a problem. We assume that the substlist fixes this at roleInstance time; it should be exact.
|
|
|
|
*/
|
|
|
|
substlist = myrun.substitutions;
|
|
|
|
while (substlist != NULL)
|
|
|
|
{
|
|
|
|
Term t;
|
|
|
|
|
|
|
|
t = substlist->term;
|
|
|
|
if (t->subst != NULL)
|
|
|
|
{
|
|
|
|
termDelete (t->subst);
|
|
|
|
t->subst = NULL;
|
|
|
|
}
|
|
|
|
substlist = substlist->next;
|
|
|
|
}
|
2005-05-01 14:32:50 +01:00
|
|
|
termlistDelete (myrun.substitutions);
|
2004-12-08 16:25:27 +00:00
|
|
|
|
2006-03-15 21:30:19 +00:00
|
|
|
/*
|
|
|
|
* Artefact removal can only be done if knowledge sets are empty, as with Arachne
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
Termlist artefacts;
|
|
|
|
// Remove artefacts
|
|
|
|
artefacts = myrun.artefacts;
|
|
|
|
while (artefacts != NULL)
|
|
|
|
{
|
|
|
|
free (artefacts->term);
|
|
|
|
artefacts = artefacts->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-12-08 16:25:27 +00:00
|
|
|
// remove lists
|
2004-08-12 10:14:31 +01:00
|
|
|
termlistDelete (myrun.artefacts);
|
|
|
|
termlistDelete (myrun.locals);
|
2004-12-08 16:25:27 +00:00
|
|
|
|
2004-08-10 16:02:37 +01:00
|
|
|
// Destroy run struct allocation in array using realloc
|
|
|
|
// Reduce run count
|
2004-12-08 16:25:27 +00:00
|
|
|
sys->maxruns = sys->maxruns - 1;
|
2005-05-01 14:32:50 +01:00
|
|
|
sys->runs =
|
2006-03-08 13:58:46 +00:00
|
|
|
(Run) realloc (sys->runs, sizeof (struct run) * (sys->maxruns));
|
2004-08-10 16:02:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Initialise the second system phase.
|
|
|
|
/**
|
|
|
|
* Allocates memory for traces.
|
|
|
|
* The number of runs has to be known for this procedure.
|
|
|
|
*\sa systemInit()
|
|
|
|
*/
|
2004-04-23 11:58:43 +01:00
|
|
|
|
|
|
|
void
|
2004-07-20 13:41:56 +01:00
|
|
|
systemStart (const System sys)
|
2004-04-23 11:58:43 +01:00
|
|
|
{
|
|
|
|
int i, s;
|
|
|
|
Roledef rd;
|
|
|
|
|
|
|
|
s = 0;
|
|
|
|
for (i = 0; i < sys->maxruns; i++)
|
|
|
|
{
|
|
|
|
rd = runPointerGet (sys, i);
|
|
|
|
while (rd != NULL)
|
|
|
|
{
|
|
|
|
s++;
|
|
|
|
rd = rd->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* this is the maximum trace length */
|
|
|
|
if (sys->maxtracelength > s)
|
|
|
|
sys->maxtracelength = s;
|
|
|
|
|
|
|
|
/* trace gets one added entry for buffer */
|
|
|
|
s = sys->maxtracelength + 1;
|
|
|
|
|
|
|
|
/* freed in systemDone */
|
2006-03-08 13:58:46 +00:00
|
|
|
sys->traceEvent = malloc (s * sizeof (Roledef));
|
|
|
|
sys->traceRun = malloc (s * sizeof (int));
|
|
|
|
sys->traceKnow = malloc (s * sizeof (Knowledge));
|
|
|
|
sys->traceNode = malloc (s * sizeof (states_t));
|
2004-04-23 11:58:43 +01:00
|
|
|
|
|
|
|
/* clear, for niceties */
|
|
|
|
for (i = 0; i < s; i++)
|
|
|
|
{
|
|
|
|
sys->traceEvent[i] = NULL;
|
|
|
|
sys->traceRun[i] = 0;
|
|
|
|
sys->traceKnow[i] = NULL;
|
2004-07-21 11:35:39 +01:00
|
|
|
sys->traceNode[i] = STATES0;
|
2004-04-23 11:58:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Print the prefix of a line suitable for the current indent level.
|
2004-04-23 11:58:43 +01:00
|
|
|
void
|
|
|
|
indent ()
|
|
|
|
{
|
|
|
|
int i = indentDepth;
|
|
|
|
int j = 0;
|
|
|
|
while (i > 0)
|
|
|
|
{
|
2004-10-28 13:33:57 +01:00
|
|
|
eprintf ("%i ", j);
|
2004-04-23 11:58:43 +01:00
|
|
|
i--;
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Create an empty protocol structure with a name.
|
2004-04-23 11:58:43 +01:00
|
|
|
Protocol
|
|
|
|
protocolCreate (Term name)
|
|
|
|
{
|
|
|
|
Protocol p;
|
|
|
|
|
2006-03-08 13:58:46 +00:00
|
|
|
p = malloc (sizeof (struct protocol));
|
2004-04-23 11:58:43 +01:00
|
|
|
p->nameterm = name;
|
|
|
|
p->roles = NULL;
|
2004-08-12 14:22:49 +01:00
|
|
|
p->rolenames = NULL;
|
2004-04-23 11:58:43 +01:00
|
|
|
p->locals = NULL;
|
2004-08-12 14:22:49 +01:00
|
|
|
p->next = NULL;
|
2007-01-29 17:20:45 +00:00
|
|
|
p->lineno = 0;
|
2004-04-23 11:58:43 +01:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Print all local terms in a term list.
|
2004-07-24 16:05:20 +01:00
|
|
|
//@todo What is this doing here? This should be in termlists.c!
|
2004-04-23 11:58:43 +01:00
|
|
|
void
|
|
|
|
locVarPrint (Termlist tl)
|
|
|
|
{
|
|
|
|
if (tl == NULL)
|
|
|
|
{
|
2004-10-28 13:33:57 +01:00
|
|
|
eprintf ("No local terms.\n");
|
2004-04-23 11:58:43 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-10-28 13:33:57 +01:00
|
|
|
eprintf ("Local terms: ");
|
|
|
|
eprintf ("[");
|
2004-04-23 11:58:43 +01:00
|
|
|
while (tl != NULL)
|
|
|
|
{
|
|
|
|
termPrint (tl->term);
|
|
|
|
if (tl->term->stype != NULL)
|
|
|
|
{
|
2004-10-28 13:33:57 +01:00
|
|
|
eprintf (":");
|
2004-04-23 11:58:43 +01:00
|
|
|
termlistPrint (tl->term->stype);
|
|
|
|
}
|
|
|
|
tl = tl->next;
|
|
|
|
if (tl != NULL)
|
2004-10-28 13:33:57 +01:00
|
|
|
eprintf (",");
|
2004-04-23 11:58:43 +01:00
|
|
|
}
|
2004-10-28 13:33:57 +01:00
|
|
|
eprintf ("]");
|
|
|
|
eprintf ("\n");
|
2004-04-23 11:58:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Print a protocol.
|
2004-04-23 11:58:43 +01:00
|
|
|
void
|
|
|
|
protocolPrint (Protocol p)
|
|
|
|
{
|
|
|
|
if (p == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
indent ();
|
2004-10-28 13:33:57 +01:00
|
|
|
eprintf ("[[Protocol : ");
|
2004-04-23 11:58:43 +01:00
|
|
|
termPrint (p->nameterm);
|
2004-10-28 13:33:57 +01:00
|
|
|
eprintf (" (");
|
2004-04-23 11:58:43 +01:00
|
|
|
termlistPrint (p->rolenames);
|
2004-10-28 13:33:57 +01:00
|
|
|
eprintf (")]]\n");
|
2004-04-23 11:58:43 +01:00
|
|
|
locVarPrint (p->locals);
|
|
|
|
rolesPrint (p->roles);
|
|
|
|
}
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Print a list of protocols.
|
2004-04-23 11:58:43 +01:00
|
|
|
void
|
|
|
|
protocolsPrint (Protocol p)
|
|
|
|
{
|
|
|
|
while (p != NULL)
|
|
|
|
{
|
|
|
|
protocolPrint (p);
|
|
|
|
p = p->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-07-01 14:25:54 +01:00
|
|
|
//! Determine whether an agent term is trusted
|
|
|
|
/**
|
|
|
|
* 1 (True) means trusted, 0 is untrusted
|
|
|
|
*/
|
2005-08-15 13:49:32 +01:00
|
|
|
int
|
2005-07-01 14:25:54 +01:00
|
|
|
isAgentTrusted (const System sys, Term agent)
|
|
|
|
{
|
|
|
|
agent = deVar (agent);
|
|
|
|
if (!realTermVariable (agent) && inTermlist (sys->untrusted, agent))
|
|
|
|
{
|
|
|
|
// Untrusted agent in the list
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2004-05-15 15:47:19 +01:00
|
|
|
//! Determine whether there is an untrusted agent.
|
|
|
|
/**
|
2005-07-01 14:25:54 +01:00
|
|
|
*@return True iff all agent in the list are trusted.
|
2004-05-15 15:47:19 +01:00
|
|
|
*/
|
2004-04-23 11:58:43 +01:00
|
|
|
int
|
2005-07-01 14:25:54 +01:00
|
|
|
isAgentlistTrusted (const System sys, Termlist agents)
|
2004-04-23 11:58:43 +01:00
|
|
|
{
|
|
|
|
while (agents != NULL)
|
|
|
|
{
|
2005-07-01 14:25:54 +01:00
|
|
|
if (!isAgentTrusted (sys, agents->term))
|
2004-04-23 11:58:43 +01:00
|
|
|
{
|
2005-07-01 14:25:54 +01:00
|
|
|
return 0;
|
2004-04-23 11:58:43 +01:00
|
|
|
}
|
2005-07-01 14:25:54 +01:00
|
|
|
agents = agents->next;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Determine whether all agents of a run are trusted
|
|
|
|
/**
|
|
|
|
* Returns 0 (False) if they are not trusted, otherwise 1 (True)
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
isRunTrusted (const System sys, const int run)
|
|
|
|
{
|
|
|
|
if (run >= 0 && run < sys->maxruns)
|
|
|
|
{
|
2006-03-15 21:30:19 +00:00
|
|
|
if (!isAgentlistTrusted (sys, sys->runs[run].rho))
|
2004-04-23 11:58:43 +01:00
|
|
|
{
|
2005-07-01 14:25:54 +01:00
|
|
|
return 0;
|
2004-04-23 11:58:43 +01:00
|
|
|
}
|
|
|
|
}
|
2005-07-01 14:25:54 +01:00
|
|
|
return 1;
|
2004-04-23 11:58:43 +01:00
|
|
|
}
|
|
|
|
|
2004-07-26 13:43:19 +01:00
|
|
|
void
|
2005-06-07 16:02:27 +01:00
|
|
|
commandlinePrint (FILE * stream)
|
2004-07-26 13:43:19 +01:00
|
|
|
{
|
|
|
|
/* print command line */
|
|
|
|
int i;
|
|
|
|
|
2005-06-07 16:02:27 +01:00
|
|
|
for (i = 0; i < switches.argc; i++)
|
|
|
|
fprintf (stream, " %s", switches.argv[i]);
|
2004-07-26 13:43:19 +01:00
|
|
|
}
|
|
|
|
|
2004-06-16 11:39:13 +01:00
|
|
|
//! Get the number of roles in the system.
|
2004-08-09 11:05:58 +01:00
|
|
|
int
|
|
|
|
compute_rolecount (const System sys)
|
2004-06-16 11:39:13 +01:00
|
|
|
{
|
|
|
|
Protocol pr;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
pr = sys->protocols;
|
|
|
|
while (pr != NULL)
|
|
|
|
{
|
2004-08-09 11:05:58 +01:00
|
|
|
n = n + termlistLength (pr->rolenames);
|
2004-06-16 11:39:13 +01:00
|
|
|
pr = pr->next;
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Compute the maximum number of events in a single role in the system.
|
2004-08-09 11:05:58 +01:00
|
|
|
int
|
|
|
|
compute_roleeventmax (const System sys)
|
2004-06-16 11:39:13 +01:00
|
|
|
{
|
|
|
|
Protocol pr;
|
|
|
|
int maxev;
|
|
|
|
|
|
|
|
maxev = 0;
|
|
|
|
pr = sys->protocols;
|
|
|
|
while (pr != NULL)
|
|
|
|
{
|
|
|
|
Role r;
|
|
|
|
|
|
|
|
r = pr->roles;
|
|
|
|
while (r != NULL)
|
|
|
|
{
|
|
|
|
Roledef rd;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
rd = r->roledef;
|
|
|
|
n = 0;
|
|
|
|
while (rd != NULL)
|
|
|
|
{
|
|
|
|
n++;
|
|
|
|
rd = rd->next;
|
|
|
|
}
|
2004-08-09 11:05:58 +01:00
|
|
|
if (n > maxev)
|
|
|
|
maxev = n;
|
2004-06-16 11:39:13 +01:00
|
|
|
r = r->next;
|
|
|
|
}
|
|
|
|
pr = pr->next;
|
|
|
|
}
|
|
|
|
return maxev;
|
|
|
|
}
|
2004-07-28 12:39:08 +01:00
|
|
|
|
2005-08-15 13:49:32 +01:00
|
|
|
//! Determine whether we don't need any more attacks
|
|
|
|
/**
|
2006-11-23 10:49:28 +00:00
|
|
|
* Returns 1 (true) iff no more attacks are needed for this claim.
|
2005-08-15 13:49:32 +01:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
enoughAttacks (const System sys)
|
|
|
|
{
|
2006-11-23 10:40:10 +00:00
|
|
|
// Only if we are outputting more than one
|
|
|
|
if (switches.prune == 0)
|
2005-08-15 13:49:32 +01:00
|
|
|
{
|
2006-11-23 10:40:10 +00:00
|
|
|
if (switches.maxAttacks != 0)
|
2005-08-15 13:49:32 +01:00
|
|
|
{
|
2006-11-23 10:49:28 +00:00
|
|
|
// Note: we're comparing states (some type of large integer) to regular
|
|
|
|
// integers, and hope that the compiler solves it :)
|
|
|
|
if (sys->current_claim->failed >= switches.maxAttacks)
|
2006-11-23 10:40:10 +00:00
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
2005-08-15 13:49:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2006-02-27 16:08:17 +00:00
|
|
|
|
|
|
|
//! Iterate over runs.
|
|
|
|
/**
|
|
|
|
* Callback should return true in order to continue.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iterateRuns (const System sys, int (*callback) (int r))
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
for (r = 0; r < sys->maxruns; r++)
|
|
|
|
{
|
|
|
|
if (!callback (r))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Iterate over non-intruder runs.
|
|
|
|
/**
|
|
|
|
* Callback should return true in order to continue.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iterateRegularRuns (const System sys, int (*callback) (int r))
|
|
|
|
{
|
|
|
|
int regular (int r)
|
|
|
|
{
|
|
|
|
if (sys->runs[r].protocol != INTRUDER)
|
|
|
|
{
|
|
|
|
return callback (r);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return iterateRuns (sys, regular);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Iterate over events in a certain run (increasing through role)
|
|
|
|
int
|
|
|
|
iterateEvents (const System sys, const int run,
|
|
|
|
int (*callback) (Roledef rd, int ev))
|
|
|
|
{
|
|
|
|
int e;
|
|
|
|
Roledef rd;
|
|
|
|
|
|
|
|
rd = sys->runs[run].start;
|
|
|
|
for (e = 0; e < sys->runs[run].step; e++)
|
|
|
|
{
|
|
|
|
if (!callback (rd, e))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
rd = rd->next;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-05-26 12:27:05 +01:00
|
|
|
//! Iterate over all events in all runs
|
|
|
|
/**
|
|
|
|
* This includes intruder as well as regular events
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iterateAllEvents (const System sys,
|
|
|
|
int (*callback) (int run, Roledef rd, int ev))
|
|
|
|
{
|
|
|
|
int run;
|
|
|
|
|
|
|
|
int callwrapper (Roledef rd, int ev)
|
|
|
|
{
|
|
|
|
return callback (run, rd, ev);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (run = 0; run < sys->maxruns; run++)
|
|
|
|
{
|
|
|
|
if (!iterateEvents (sys, run, callwrapper))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Iterate over event type in a certain run (increasing through role)
|
2006-02-28 15:33:12 +00:00
|
|
|
/**
|
|
|
|
* If evtype == ANYEVENT then it does not matter.
|
|
|
|
*/
|
2006-02-27 16:08:17 +00:00
|
|
|
int
|
|
|
|
iterateEventsType (const System sys, const int run, const int evtype,
|
|
|
|
int (*callback) (Roledef rd, int ev))
|
|
|
|
{
|
|
|
|
int selectEvent (Roledef rd, int e)
|
|
|
|
{
|
2006-02-28 15:33:12 +00:00
|
|
|
if (evtype == ANYEVENT || rd->type == evtype)
|
2006-02-27 16:08:17 +00:00
|
|
|
{
|
|
|
|
return callback (rd, e);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return iterateEvents (sys, run, selectEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Iterate over all 'others': local variables of a run that are instantiated and contain some term of another run.
|
|
|
|
int
|
|
|
|
iterateLocalToOther (const System sys, const int myrun,
|
2006-02-27 22:27:09 +00:00
|
|
|
int (*callback) (Term tlocal))
|
2006-02-27 16:08:17 +00:00
|
|
|
{
|
|
|
|
Termlist tlo, tls;
|
2006-02-28 13:41:36 +00:00
|
|
|
int flag;
|
2006-02-27 16:08:17 +00:00
|
|
|
|
|
|
|
int addOther (Term t)
|
|
|
|
{
|
|
|
|
tlo = termlistAddNew (tlo, t);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-02-28 13:41:36 +00:00
|
|
|
flag = true;
|
2006-02-27 16:08:17 +00:00
|
|
|
tlo = NULL;
|
|
|
|
// construct all others occuring in the reads
|
2006-03-15 21:30:19 +00:00
|
|
|
for (tls = sys->runs[myrun].sigma; tls != NULL; tls = tls->next)
|
2006-02-27 16:08:17 +00:00
|
|
|
{
|
2006-02-28 13:41:36 +00:00
|
|
|
Term tt;
|
|
|
|
|
|
|
|
tt = tls->term;
|
|
|
|
if (realTermVariable (tt) && tt->subst != NULL);
|
|
|
|
{
|
|
|
|
iterateTermOther (myrun, tt->subst, addOther);
|
|
|
|
}
|
2006-02-27 16:08:17 +00:00
|
|
|
}
|
|
|
|
// now iterate over all of them
|
2006-02-28 13:41:36 +00:00
|
|
|
for (tls = tlo; flag && (tls != NULL); tls = tls->next)
|
2006-02-27 16:08:17 +00:00
|
|
|
{
|
|
|
|
if (!callback (tls->term))
|
|
|
|
{
|
2006-02-28 13:41:36 +00:00
|
|
|
flag = false;
|
2006-02-27 16:08:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// clean up
|
|
|
|
termlistDelete (tlo);
|
2006-02-28 13:41:36 +00:00
|
|
|
return flag;
|
2006-02-27 16:08:17 +00:00
|
|
|
}
|
|
|
|
|
2006-07-27 11:44:12 +01:00
|
|
|
//! Iterate over all roles
|
|
|
|
int
|
|
|
|
iterateRoles (const System sys, int (*callback) (Protocol p, Role r))
|
|
|
|
{
|
|
|
|
Protocol p;
|
|
|
|
|
|
|
|
p = sys->protocols;
|
|
|
|
while (p != NULL)
|
|
|
|
{
|
|
|
|
Role r;
|
|
|
|
|
|
|
|
r = p->roles;
|
|
|
|
while (r != NULL)
|
|
|
|
{
|
|
|
|
if (!callback (p, r))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
r = r->next;
|
|
|
|
}
|
|
|
|
p = p->next;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-02-27 16:08:17 +00:00
|
|
|
//! Get first read/send occurrence (event index) of term t in run r
|
|
|
|
int
|
|
|
|
firstOccurrence (const System sys, const int r, Term t, int evtype)
|
|
|
|
{
|
|
|
|
int firste;
|
|
|
|
|
|
|
|
int checkOccurs (Roledef rd, int e)
|
|
|
|
{
|
|
|
|
if (termSubTerm (rd->message, t) || termSubTerm (rd->from, t)
|
|
|
|
|| termSubTerm (rd->to, t))
|
|
|
|
{
|
|
|
|
firste = e;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-02-27 22:27:09 +00:00
|
|
|
firste = -1;
|
|
|
|
iterateEventsType (sys, r, evtype, checkOccurs);
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (DEBUGL (3))
|
2006-02-27 16:08:17 +00:00
|
|
|
{
|
2006-02-27 22:27:09 +00:00
|
|
|
if (firste == -1)
|
|
|
|
{
|
|
|
|
globalError++;
|
|
|
|
eprintf ("Warning: Desired term ");
|
|
|
|
termPrint (t);
|
|
|
|
eprintf (" does not occur");
|
|
|
|
eprintf (" in run %i in event type %i.\n", r, evtype);
|
|
|
|
runPrint (sys->runs[r].start);
|
|
|
|
eprintf ("\n");
|
|
|
|
globalError--;
|
|
|
|
}
|
2006-02-27 16:08:17 +00:00
|
|
|
}
|
2006-02-27 22:27:09 +00:00
|
|
|
#endif
|
2006-02-27 16:08:17 +00:00
|
|
|
return firste;
|
|
|
|
}
|
2006-03-08 15:12:58 +00:00
|
|
|
|
|
|
|
//! Get the roledef of an event
|
|
|
|
Roledef
|
|
|
|
eventRoledef (const System sys, const int run, const int ev)
|
|
|
|
{
|
|
|
|
return roledef_shift (sys->runs[run].start, ev);
|
|
|
|
}
|
2006-03-28 15:45:02 +01:00
|
|
|
|
2006-04-02 13:07:25 +01:00
|
|
|
//! count the number of initiators
|
|
|
|
int
|
|
|
|
countInitiators (const System sys)
|
|
|
|
{
|
|
|
|
int run;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
count = 0;
|
|
|
|
for (run = 0; run < sys->maxruns; run++)
|
|
|
|
{
|
|
|
|
if (sys->runs[run].role->initiator)
|
|
|
|
{
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2011-04-06 09:50:54 +01:00
|
|
|
//! Iterate over regular non-helper runs
|
|
|
|
/**
|
|
|
|
* stopval is the stopping value, defval is the default value.
|
|
|
|
* if stopval == -2, we increment defval for each true result and return the total.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iterateRegularNonHelper (const System sys,
|
|
|
|
int (*callback) (const System sys, int rid),
|
|
|
|
int defval, int stopval)
|
|
|
|
{
|
|
|
|
int res, run;
|
|
|
|
|
|
|
|
res = defval;
|
|
|
|
for (run = 0; run < sys->maxruns; run++)
|
|
|
|
{
|
|
|
|
if ((!isHelperProtocol (sys->runs[run].protocol))
|
|
|
|
&& (sys->runs[run].protocol != INTRUDER))
|
|
|
|
{
|
|
|
|
int tmp;
|
|
|
|
|
|
|
|
tmp = callback (sys, run);
|
|
|
|
if (stopval == -2)
|
|
|
|
{
|
|
|
|
if (tmp == true)
|
|
|
|
{
|
|
|
|
res++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (tmp == stopval)
|
|
|
|
{
|
|
|
|
return stopval;
|
|
|
|
}
|
|
|
|
res = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-31 09:24:41 +01:00
|
|
|
//! determine whether a run talks to itself
|
|
|
|
int
|
|
|
|
selfSession (const System sys, const int run)
|
|
|
|
{
|
|
|
|
int self_session;
|
|
|
|
Termlist agents;
|
|
|
|
Termlist seen;
|
|
|
|
|
|
|
|
if (sys->runs[run].protocol == INTRUDER)
|
|
|
|
{
|
|
|
|
// Intruder has no self sessions
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
self_session = false;
|
|
|
|
|
|
|
|
agents = sys->runs[run].rho;
|
|
|
|
seen = NULL;
|
|
|
|
while (agents != NULL)
|
|
|
|
{
|
|
|
|
Term agent;
|
|
|
|
|
|
|
|
agent = deVar (agents->term);
|
|
|
|
if (inTermlist (seen, agent))
|
|
|
|
{
|
|
|
|
// This agent was already in the seen list
|
|
|
|
self_session = true;
|
2006-03-31 11:12:58 +01:00
|
|
|
break;
|
2006-03-31 09:24:41 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
seen = termlistAdd (seen, agent);
|
|
|
|
}
|
|
|
|
agents = agents->next;
|
|
|
|
}
|
|
|
|
termlistDelete (seen);
|
|
|
|
|
|
|
|
return self_session;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! determine whether a run is a so-called self-responder
|
2006-03-28 15:45:02 +01:00
|
|
|
/**
|
2006-03-31 09:24:41 +01:00
|
|
|
* Alice starting a run with Bob, Charlie, Bob is also counted as self-response.
|
2006-03-28 15:45:02 +01:00
|
|
|
*/
|
|
|
|
int
|
2006-03-31 09:24:41 +01:00
|
|
|
selfResponder (const System sys, const int run)
|
2006-03-28 15:45:02 +01:00
|
|
|
{
|
2011-04-06 09:50:54 +01:00
|
|
|
if (sys->runs[run].role->initiator)
|
2006-03-28 15:45:02 +01:00
|
|
|
{
|
2011-04-06 09:50:54 +01:00
|
|
|
return false;
|
2006-03-31 09:24:41 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return selfSession (sys, run);
|
|
|
|
}
|
|
|
|
}
|
2006-03-28 15:45:02 +01:00
|
|
|
|
2006-03-31 09:24:41 +01:00
|
|
|
//! Count the number of any self-responders
|
|
|
|
int
|
|
|
|
selfResponders (const System sys)
|
|
|
|
{
|
2011-04-06 09:50:54 +01:00
|
|
|
return iterateRegularNonHelper (sys, selfResponder, 0, -2);
|
2006-03-31 09:24:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//! determine whether a run is a so-called self-initiator
|
|
|
|
/**
|
|
|
|
* Alice starting a run with Bob, Charlie, Bob is also counted as self-initiation.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
selfInitiator (const System sys, const int run)
|
|
|
|
{
|
2011-04-06 09:50:54 +01:00
|
|
|
if (sys->runs[run].role->initiator)
|
2006-03-31 09:24:41 +01:00
|
|
|
{
|
2011-04-06 09:50:54 +01:00
|
|
|
return selfSession (sys, run);
|
2006-03-31 09:24:41 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
2006-03-28 15:45:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Count the number of any self-initiators
|
|
|
|
int
|
|
|
|
selfInitiators (const System sys)
|
|
|
|
{
|
2011-04-06 09:50:54 +01:00
|
|
|
return iterateRegularNonHelper (sys, selfInitiator, 0, -2);
|
2006-03-28 15:45:02 +01:00
|
|
|
}
|
2011-04-01 14:23:20 +01:00
|
|
|
|
|
|
|
//! Check a protocol for being a helper protocol.
|
|
|
|
/**
|
|
|
|
* Special helper protocols start with an '@' conform the usage in Gijs
|
|
|
|
* Hollestelle's work.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
isHelperProtocol (Protocol p)
|
|
|
|
{
|
|
|
|
if (p != NULL)
|
|
|
|
{
|
|
|
|
if (p->nameterm != NULL)
|
|
|
|
{
|
|
|
|
if (TermSymb (p->nameterm)->text[0] == '@')
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2011-04-01 14:24:14 +01:00
|
|
|
|
|
|
|
//! Check whether agents are performing unique roles
|
|
|
|
int
|
|
|
|
agentsUniqueRoles (const System sys)
|
|
|
|
{
|
|
|
|
Termlist roles;
|
|
|
|
Termlist agents;
|
|
|
|
int run;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
roles = NULL;
|
|
|
|
agents = NULL;
|
|
|
|
res = true;
|
|
|
|
for (run = 0; run < sys->maxruns; run++)
|
|
|
|
{
|
|
|
|
if (!isHelperProtocol (sys->runs[run].protocol))
|
|
|
|
{
|
|
|
|
Term agentname;
|
|
|
|
Term rolename;
|
|
|
|
Termlist tla, tlr;
|
|
|
|
|
|
|
|
rolename = sys->runs[run].role->nameterm;
|
|
|
|
agentname = agentOfRunRole (sys, run, rolename);
|
|
|
|
|
|
|
|
// Find out whether agent name was already mapped to a role
|
|
|
|
tlr = roles;
|
|
|
|
tla = agents;
|
|
|
|
while ((tlr != NULL) && (tla != NULL))
|
|
|
|
{
|
|
|
|
if (isTermEqual (tla->term, agentname))
|
|
|
|
{
|
|
|
|
// okay so this matches. Then so should the second.
|
|
|
|
if (!isTermEqual (tlr->term, rolename))
|
|
|
|
{
|
|
|
|
res = false;
|
|
|
|
// full abort
|
|
|
|
tlr = NULL;
|
|
|
|
run = sys->maxruns;
|
|
|
|
}
|
|
|
|
// stop anyway, because the agent is fine, should not occur twice
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tlr = tlr->next;
|
|
|
|
tla = tla->next;
|
|
|
|
}
|
|
|
|
// Not in there yet, add.
|
|
|
|
agents = termlistAdd (agents, agentname);
|
|
|
|
roles = termlistAdd (roles, rolename);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
termlistDelete (agents);
|
|
|
|
termlistDelete (roles);
|
|
|
|
return res;
|
|
|
|
}
|