535 lines
10 KiB
C
535 lines
10 KiB
C
/*
|
|
* Scyther : An automatic verifier for security protocols.
|
|
* Copyright (C) 2007 Cas Cremers
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/**
|
|
* @file role.c
|
|
* \brief role related logic.
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include "term.h"
|
|
#include "termlist.h"
|
|
#include "knowledge.h"
|
|
#include "system.h"
|
|
#include "debug.h"
|
|
#include "error.h"
|
|
#include "role.h"
|
|
|
|
extern int protocolCount; // from system.c
|
|
|
|
//! Allocate memory the size of a roledef struct.
|
|
Roledef
|
|
makeRoledef ()
|
|
{
|
|
return (Roledef) malloc (sizeof (struct roledef));
|
|
}
|
|
|
|
//! Print a role event.
|
|
/**
|
|
* If print_actor is true, the actor is included (OS version), otherwise it is left out (short stuff)
|
|
*/
|
|
void
|
|
roledefPrintGeneric (Roledef rd, int print_actor)
|
|
{
|
|
if (rd == NULL)
|
|
{
|
|
eprintf ("[Empty roledef]");
|
|
return;
|
|
}
|
|
if (rd->type == READ && rd->internal)
|
|
{
|
|
/* special case: internal read == choose ! */
|
|
eprintf ("CHOOSE(");
|
|
termPrint (rd->message);
|
|
eprintf (")");
|
|
return;
|
|
}
|
|
if (rd->type == READ)
|
|
eprintf ("RECV");
|
|
if (rd->type == SEND)
|
|
eprintf ("SEND");
|
|
if (rd->type == CLAIM)
|
|
eprintf ("CLAIM");
|
|
if (rd->label != NULL)
|
|
{
|
|
//! Print label
|
|
Term label;
|
|
|
|
/* Old version: sometimes prints protocol stuff (really unique labels)
|
|
label = deVar (rd->label);
|
|
if (protocolCount < 2 && realTermTuple (label))
|
|
{
|
|
// Only one protocol, so we don't need to show the extra label info
|
|
label = TermOp2 (label);
|
|
}
|
|
*/
|
|
label = deVar (rd->label);
|
|
if (realTermTuple (label))
|
|
{
|
|
label = TermOp2 (label);
|
|
}
|
|
|
|
eprintf ("_");
|
|
termPrint (label);
|
|
}
|
|
eprintf ("(");
|
|
if (!(rd->from == NULL && rd->to == NULL))
|
|
{
|
|
if (print_actor || rd->type == READ)
|
|
{
|
|
termPrint (rd->from);
|
|
eprintf (",");
|
|
}
|
|
if (rd->type == CLAIM)
|
|
eprintf (" ");
|
|
if (print_actor || rd->type != READ)
|
|
{
|
|
termPrint (rd->to);
|
|
eprintf (", ");
|
|
}
|
|
}
|
|
termPrint (rd->message);
|
|
eprintf (" )");
|
|
}
|
|
|
|
//! Print a roledef
|
|
void
|
|
roledefPrint (Roledef rd)
|
|
{
|
|
roledefPrintGeneric (rd, 1);
|
|
}
|
|
|
|
//! Print a roledef, but shorten it
|
|
void
|
|
roledefPrintShort (Roledef rd)
|
|
{
|
|
roledefPrintGeneric (rd, 0);
|
|
}
|
|
|
|
|
|
//! Duplicate a single role event node.
|
|
/**
|
|
*\sa roledefDelete()
|
|
*/
|
|
Roledef
|
|
roledefDuplicate1 (const Roledef rd)
|
|
{
|
|
Roledef newrd;
|
|
|
|
if (rd == NULL)
|
|
return NULL;
|
|
newrd = makeRoledef ();
|
|
memcpy (newrd, rd, sizeof (struct roledef));
|
|
newrd->next = NULL;
|
|
return newrd;
|
|
}
|
|
|
|
//! Duplicate a role event list.
|
|
/**
|
|
*\sa roledefDelete()
|
|
*/
|
|
Roledef
|
|
roledefDuplicate (Roledef rd)
|
|
{
|
|
Roledef newrd;
|
|
|
|
if (rd == NULL)
|
|
return NULL;
|
|
newrd = roledefDuplicate1 (rd);
|
|
newrd->next = roledefDuplicate (rd->next);
|
|
return newrd;
|
|
}
|
|
|
|
//! Delete a role event or event list.
|
|
/**
|
|
*\sa roledefDuplicate()
|
|
*/
|
|
void
|
|
roledefDelete (Roledef rd)
|
|
{
|
|
if (rd == NULL)
|
|
return;
|
|
roledefDelete (rd->next);
|
|
free (rd);
|
|
return;
|
|
}
|
|
|
|
//! Destroy a role event or event list.
|
|
void
|
|
roledefDestroy (Roledef rd)
|
|
{
|
|
if (rd == NULL)
|
|
return;
|
|
roledefDestroy (rd->next);
|
|
termDelete (rd->from);
|
|
termDelete (rd->to);
|
|
termDelete (rd->message);
|
|
free (rd);
|
|
return;
|
|
}
|
|
|
|
//! Make a new role event with the specified parameters.
|
|
/**
|
|
*@return A pointer to a new role event with the given parameters.
|
|
*/
|
|
Roledef
|
|
roledefInit (int type, Term label, Term from, Term to, Term msg, Claimlist cl)
|
|
{
|
|
Roledef newEvent;
|
|
|
|
newEvent = makeRoledef ();
|
|
newEvent->internal = 0;
|
|
newEvent->type = type;
|
|
newEvent->label = label;
|
|
newEvent->from = from;
|
|
newEvent->to = to;
|
|
newEvent->message = msg;
|
|
newEvent->forbidden = NULL; // no forbidden stuff
|
|
newEvent->knowPhase = -1; // we haven't explored any knowledge yet
|
|
newEvent->claiminfo = cl; // only for claims
|
|
if (type == READ)
|
|
newEvent->bound = 0; // bound goal (Used for arachne only). Technically involves choose events as well.
|
|
else
|
|
newEvent->bound = 1; // other stuff does not need to be bound
|
|
newEvent->next = NULL;
|
|
newEvent->lineno = 0;
|
|
return newEvent;
|
|
}
|
|
|
|
//! Add a role event to an existing list, with the given parameters.
|
|
/**
|
|
*\sa roledefInit()
|
|
*/
|
|
Roledef
|
|
roledefAdd (Roledef rd, int type, Term label, Term from, Term to, Term msg,
|
|
Claimlist cl)
|
|
{
|
|
Roledef scan;
|
|
|
|
if (rd == NULL)
|
|
return roledefInit (type, label, from, to, msg, cl);
|
|
|
|
scan = rd;
|
|
while (scan->next != NULL)
|
|
scan = scan->next;
|
|
scan->next = roledefInit (type, label, from, to, msg, cl);
|
|
return rd;
|
|
}
|
|
|
|
//! Create an empty role structure with a name.
|
|
Role
|
|
roleCreate (Term name)
|
|
{
|
|
Role r;
|
|
|
|
r = malloc (sizeof (struct role));
|
|
r->nameterm = name;
|
|
r->roledef = NULL;
|
|
r->locals = NULL;
|
|
r->variables = NULL;
|
|
r->declaredvars = NULL;
|
|
r->declaredconsts = NULL;
|
|
r->initiator = 1; //! Will be determined later, if a read is the first action (in compiler.c)
|
|
r->singular = false; // by default, a role is not singular
|
|
r->next = NULL;
|
|
r->knows = NULL;
|
|
r->lineno = 0;
|
|
return r;
|
|
}
|
|
|
|
//! Print a role.
|
|
void
|
|
rolePrint (Role r)
|
|
{
|
|
Roledef rd;
|
|
|
|
if (r == NULL)
|
|
return;
|
|
|
|
indent ();
|
|
eprintf ("[[Role : ");
|
|
termPrint (r->nameterm);
|
|
eprintf ("]]\n");
|
|
locVarPrint (r->locals);
|
|
|
|
rd = r->roledef;
|
|
while (rd != NULL)
|
|
{
|
|
roledefPrint (rd);
|
|
eprintf ("\n");
|
|
rd = rd->next;
|
|
}
|
|
}
|
|
|
|
//! Print a list of roles.
|
|
void
|
|
rolesPrint (Role r)
|
|
{
|
|
if (r == NULL)
|
|
{
|
|
eprintf ("Empty role.");
|
|
}
|
|
else
|
|
{
|
|
while (r != NULL)
|
|
{
|
|
rolePrint (r);
|
|
r = r->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
//! Iterate over the events in a roledef list
|
|
/**
|
|
* Function gets roledef pointer
|
|
*/
|
|
int
|
|
roledef_iterate_events (Roledef rd, int (*func) ())
|
|
{
|
|
while (rd != NULL)
|
|
{
|
|
if (!func (rd))
|
|
return 0;
|
|
rd = rd->next;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//! Roledef length
|
|
/**
|
|
* Would be faster hard-coded,
|
|
* but this just shows the use of the iteration.
|
|
*/
|
|
int
|
|
roledef_length (const Roledef rd)
|
|
{
|
|
int count = 0;
|
|
int countplus (Roledef rd)
|
|
{
|
|
count++;
|
|
return 1;
|
|
}
|
|
roledef_iterate_events (rd, countplus);
|
|
return count;
|
|
}
|
|
|
|
//! Yield roledef pointer for a given index
|
|
Roledef
|
|
roledef_shift (Roledef rd, int i)
|
|
{
|
|
while (i > 0 && rd != NULL)
|
|
{
|
|
rd = rd->next;
|
|
i--;
|
|
}
|
|
return rd;
|
|
}
|
|
|
|
//! Check whether a term is a subterm of a roledef
|
|
int
|
|
roledefSubTerm (Roledef rd, Term tsub)
|
|
{
|
|
if (rd == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return (termSubTerm (rd->from, tsub) ||
|
|
termSubTerm (rd->to, tsub) || termSubTerm (rd->message, tsub));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Some stuff directly from the semantics
|
|
*/
|
|
|
|
//! Is a term readable (from some knowledge set)
|
|
/**
|
|
* Returns value of predicate
|
|
*/
|
|
int
|
|
Readable (Knowledge know, Term t)
|
|
{
|
|
if (isTermVariable (t))
|
|
{
|
|
// Variable pattern
|
|
return true;
|
|
}
|
|
if (!isTermLeaf (t))
|
|
{
|
|
if (isTermTuple (t))
|
|
{
|
|
// Tuple pattern
|
|
Knowledge knowalt;
|
|
int both;
|
|
|
|
both = false;
|
|
knowalt = knowledgeDuplicate (know);
|
|
knowledgeAddTerm (knowalt, TermOp2 (t));
|
|
if (Readable (knowalt, TermOp1 (t)))
|
|
{
|
|
// Yes, left half works
|
|
knowledgeDelete (knowalt);
|
|
knowalt = knowledgeDuplicate (know);
|
|
knowledgeAddTerm (knowalt, TermOp1 (t));
|
|
if (Readable (knowalt, TermOp2 (t)))
|
|
{
|
|
both = true;
|
|
}
|
|
}
|
|
knowledgeDelete (knowalt);
|
|
return both;
|
|
}
|
|
else
|
|
{
|
|
// Encryption pattern
|
|
// But we exclude functions
|
|
if (getTermFunction (t) == NULL)
|
|
{
|
|
// Real encryption pattern
|
|
Term inv;
|
|
int either;
|
|
|
|
// Left disjunct
|
|
if (inKnowledge (know, t))
|
|
{
|
|
return true;
|
|
}
|
|
// Right disjunct
|
|
inv = inverseKey (know->inverses, TermKey (t));
|
|
either = false;
|
|
if (inKnowledge (know, inv))
|
|
{
|
|
if (Readable (know, TermOp (t)))
|
|
{
|
|
either = true;
|
|
}
|
|
}
|
|
termDelete (inv);
|
|
return either;
|
|
}
|
|
}
|
|
}
|
|
return inKnowledge (know, t);
|
|
}
|
|
|
|
//! Well-formed error reporting.
|
|
void
|
|
wfeError (Knowledge know, Roledef rd, char *errorstring, Term was,
|
|
Term shouldbe)
|
|
{
|
|
globalError++;
|
|
eprintf ("Well-formedness error.\n");
|
|
roledefPrint (rd);
|
|
eprintf ("\nKnowing ");
|
|
knowledgePrintShort (know);
|
|
eprintf ("\n");
|
|
if (was != NULL || shouldbe != NULL)
|
|
{
|
|
eprintf ("while parsing ");
|
|
termPrint (was);
|
|
if (shouldbe != NULL)
|
|
{
|
|
eprintf (" which should have been ");
|
|
termPrint (shouldbe);
|
|
}
|
|
eprintf ("\n");
|
|
}
|
|
globalError--;
|
|
error (errorstring);
|
|
}
|
|
|
|
|
|
//! Is an event well-formed
|
|
/**
|
|
* Returns the new knowledge or NULL if it was not well-formed.
|
|
*/
|
|
Knowledge
|
|
WellFormedEvent (Term role, Knowledge know, Roledef rd)
|
|
{
|
|
if (rd == NULL)
|
|
{
|
|
return know;
|
|
}
|
|
if (rd->type == READ)
|
|
{
|
|
// Read
|
|
if (!isTermEqual (role, rd->to))
|
|
{
|
|
wfeError (know, rd, "Receiving role incorrect.", rd->to, role);
|
|
return NULL;
|
|
}
|
|
if (!inKnowledge (know, rd->from))
|
|
{
|
|
wfeError (know, rd, "Unknown sender role.", rd->from, NULL);
|
|
return NULL;
|
|
|
|
}
|
|
if (!Readable (know, rd->message))
|
|
{
|
|
wfeError (know, rd, "Cannot read message pattern.", rd->message,
|
|
NULL);
|
|
return NULL;
|
|
}
|
|
knowledgeAddTerm (know, rd->message);
|
|
return know;
|
|
}
|
|
if (rd->type == SEND)
|
|
{
|
|
// Send
|
|
if (!isTermEqual (role, rd->from))
|
|
{
|
|
wfeError (know, rd, "Sending role incorrect.", rd->from, role);
|
|
return NULL;
|
|
}
|
|
if (!inKnowledge (know, rd->to))
|
|
{
|
|
wfeError (know, rd, "Unknown receiving role.", rd->to, NULL);
|
|
return NULL;
|
|
|
|
}
|
|
if (!inKnowledge (know, rd->message))
|
|
{
|
|
wfeError (know, rd, "Unable to construct message.", rd->message,
|
|
NULL);
|
|
return NULL;
|
|
}
|
|
return know;
|
|
}
|
|
if (rd->type == CLAIM)
|
|
{
|
|
// Claim
|
|
if (!isTermEqual (role, rd->from))
|
|
{
|
|
wfeError (know, rd, "Claiming role incorrect.", rd->from, role);
|
|
return NULL;
|
|
}
|
|
return know;
|
|
}
|
|
// Unknown, false
|
|
globalError++;
|
|
roledefPrint (rd);
|
|
globalError--;
|
|
error ("I don't know this event");
|
|
return NULL;
|
|
}
|