/* * 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. */ /* * type.c * * Code to check the consistency of types, in the presence of type flaw stuff etc. */ #include #include #include "term.h" #include "termlist.h" #include "system.h" #include "debug.h" #include "switches.h" #include "specialterm.h" extern Protocol INTRUDER; extern Termlist TERMLISTERROR; //! Report a bad substitution, if needed void reportBadSubst (const Term tvar, const Term tsubst) { #ifdef DEBUG if (DEBUGL (5)) { Term tbuf; tbuf = tvar->subst; tvar->subst = NULL; eprintf ("Substitution fails on "); termPrint (tvar); eprintf (" -/-> "); termPrint (tsubst); eprintf (", maybe because type: \n"); termlistPrint (tvar->stype); eprintf (" does not contain "); termlistPrint (tsubst->stype); eprintf ("\n"); tvar->subst = tbuf; } #endif } //! Say whether a typelist is 'generic' or should be taken per item. int isTypelistGeneric (const Termlist typelist) { if (typelist == NULL) { return 1; } else { return inTermlist (typelist, TERM_Ticket); } } //! Say whether this variable can contain tuples and/or encryptions /** * Precondition: tvar should be a variable. * * This function is specifically used for detecting the problematic Athena case. */ int isOpenVariable (const Term tvar) { return isTypelistGeneric (tvar->stype); } //! Check whether a single variable term is instantiated correctly. /** * Check whether a single variable term is instantiated correctly in this * system. This takes the matching parameter into account, and is aware of the * 'ticket' type. * * Non-variables etc. imply true. */ int checkTypeTerm (const Term tvar) { // Checks are only needed for match < 2 etc. if (switches.match < 2 && tvar != NULL && realTermVariable (tvar)) { // Non-instantiated terms are fine. if (tvar->subst != NULL) { if (!isTypelistGeneric (tvar->stype)) { // So there is a specific (non-ticket) type, and the var is instantiated, match mode 0 or 1 // Is it really a leaf? Term tsubst; tsubst = deVar (tvar); if (!realTermLeaf (tsubst)) { // Then it's definitively false reportBadSubst (tvar, tsubst); return false; } else { // It is a leaf if (switches.match == 0) { /* Types must match exactly. Thus, one of the variable type should match a type of the constant. */ Termlist tl; tl = tvar->stype; while (tl != NULL) { if (inTermlist (tsubst->stype, tl->term)) { // One type matches return true; } tl = tl->next; } // No type matches. reportBadSubst (tvar, tsubst); return false; } } } } } return true; } //! Check whether a typelist is a strict agent type list int isAgentType (Termlist typelist) { return (termlistLength (typelist) == 1 && inTermlist (typelist, TERM_Agent)); } //! Helper function to determine whether a list is compatible with an agent var. int agentCompatible (Termlist tl) { if (isTypelistGeneric (tl)) { return 1; } else { return inTermlist (tl, TERM_Agent); } } //! Check whether two type lists are compatible for variables /** * Depends on some input: * * agentcheck true if agent type is always restrictive * * returns the new type list (needs to be deleted!) or TERMLISTERROR (should * not be deleted!) */ Termlist typelistConjunct (Termlist typelist1, Termlist typelist2, const int agentcheck) { if (typelist1 != TERMLISTERROR && typelist2 != TERMLISTERROR) { /* In the restricted agent case, we check whether agent list occurs in * either set. If so, the result can only be an agent-restrictive list. */ if (agentcheck) { if (isAgentType (typelist1) || isAgentType (typelist2)) { /* Now, the result must surely accept agents. Thus, it must be * NULL or accept agents. */ if (switches.match == 0) { // only if we are doing matching, otherwise it is always agent-compatible if (! (agentCompatible (typelist1) && agentCompatible (typelist2))) { // Not good: one of them cannot return TERMLISTERROR; } } // Good: but because an agent is involved, the type reduces to the simple Agent type only. return termlistAdd (NULL, TERM_Agent); } else { /* Not the simple agent variable case. Now other things come in to play. */ if (switches.match == 0) { /* * Strict match: (-m0) conjunct of the types must be non-empty. */ // Generic exceptions if (isTypelistGeneric (typelist1)) { // Copy for later deletion return termlistShallow (typelist2); } else { if (isTypelistGeneric (typelist2)) { // Copy for later deletion return termlistShallow (typelist1); } else { /* Apparently neither is generic, and we can take the real conjunct. * However, this conjunct must not be empty (because that implies that the types cannot match). */ Termlist conjunct; conjunct = termlistConjunct (typelist1, typelist2); if (conjunct == NULL) { // Empty, and thus the variables cannot be instantiated without causing a type flaw. return TERMLISTERROR; } else { // Non-empty, which is good in this case. return conjunct; } } } } else { /* * Not so strict: (-m1 or -m2) * * Because the variable is not bound, there is certainly no * binding yet to tuples or crypted terms, and thus any * typing will do. */ return NULL; } } } } return TERMLISTERROR; } //! Check a single ground variable /** * Check whether all variables that map to this one have a possible valid substitution */ int checkGroundVariable (const System sys, const Term groundvar) { int allvalid; allvalid = 1; if (switches.match < 2) { if (realTermVariable (groundvar)) { Termlist tl; Termlist typelist; // Check typelist = termlistShallow (groundvar->stype); tl = sys->variables; while (allvalid == 1 && tl != NULL) { Term term; term = tl->term; // Not actually the same, of course if (term != groundvar) { // Does this term map to the same variable? if (isTermEqual (term, groundvar)) { Termlist tlprev; // Maps to same ground term // Take conjunct tlprev = typelist; typelist = typelistConjunct (tlprev, term->stype, switches.agentTypecheck); termlistDelete (tlprev); if (typelist == TERMLISTERROR) { // And this is not valid... allvalid = 0; } } } tl = tl->next; } if (typelist != TERMLISTERROR) { termlistDelete (typelist); } } } return allvalid; } //! Global check of the system, for all variables. /** * Returns true if all variables are okay. * * This version checks all substitutions, and thus takes over all the functions * that are now in mgu.c. However, we have left them in for speed purposes, * because pruning early is good. */ int checkAllSubstitutions (const System sys) { int allvalid; Termlist groundvars; Termlist tl; /* We scan all unbound variables for so called 'ground variables', which are * the root of a substitution tree. Such variables are checked for * satisfyability, i.e. the conjunction of their type lists must not be * empty. * * At the same time, we check also bound variables for their mappings. */ groundvars = NULL; allvalid = 1; tl = sys->variables; while ((allvalid == 1) && (tl != NULL)) { Term tvar; tvar = tl->term; if (realTermVariable (tvar)) { Term tsubst; tsubst = deVar (tvar); if (tvar != tsubst) { // Substitution going on, check if (realTermVariable (tsubst)) { /* Variable -> Variable (unbound) * * Check whether we already scanned for this one, because we * don't want to scan for this ground variable more than once. */ if (!inTermlist (groundvars, tsubst)) { /* Not done before, add now. */ groundvars = termlistAdd (groundvars, tsubst); /* Check whether there exists a valid substitution for * everything mapping to this ground variable. */ allvalid = allvalid && checkGroundVariable (sys, tsubst); } } else { /* Variable -> term (bound) */ if (switches.agentTypecheck && isAgentType (tvar->stype)) { // *Must* include agent type, regardless of match allvalid = allvalid && agentCompatible (tsubst->stype); } else { // Consider match allvalid = allvalid && checkTypeTerm (tvar); } } } } tl = tl->next; } termlistDelete (groundvars); return allvalid; } //! Check this variables whether it is a good agent type /** * Checks for leaf/etc and correct agent type */ int goodAgentType (Term agent) { agent = deVar (agent); if (!realTermLeaf (agent)) { // not a leaf return false; } else { // real leaf if (isTermVariable (agent)) { // Variable: check type consistency (should have a solution) // Not yet: depends on matching mode also } else { // Constant: allow only exact type if (!inTermlist (agent->stype, TERM_Agent)) { return false; } } } return true; }