* Sheldon Hearn:

> On Wednesday 07 January 2009 14:04:50 Florian Weimer wrote:
>> Something is wrong because the -3 version is known to the tracker,
>> but not listed in your output.
>
> How would I verify that -3 is known to the tracker?  It's not in
>
> http://secure-testing.debian.net/debian-secure-testing/project/debsecan/release/1/etch

I've got:

squirrelmail,CVE-2007-1262,S  F,2:1.4.10a-1,2:1.4.4-11 2:1.4.9a-2 2:1.4.9a-3
squirrelmail,CVE-2007-2589,SL F,2:1.4.10a-1,2:1.4.4-11 2:1.4.9a-2 2:1.4.9a-3

Perhaps the data has been updated in the meantime?

>> >         If the installed version is the same or greater than any of
>> > the other_versions that have the same upstream version, then it is
>> > not vulnerable.
>>
>> This doesn't work because there might be a -4 version in unstable
>> which hasn't got the fix.  (This is more apparent with the usual
>> -1+etch1 versioning scheme.)
>
> Well, perhaps.  But the debsecan source knows the unstable_version that 
> fixes the vulnerability.  So I don't see how that matters.

I'm a bit in a hurry, so I can't give you a real-world example right
now.  But suppose we've got a vulnerability in a package foo, with the
following versions:

   etch: 1.0-2
   sid/lenny: 1.0-3

sid is fixed by uploading a new upstream version, so the fixed version
is 1.1-1.  For etch, we release 1.0-2+etch1.

Suppose you've installed version 1.0-3, by cherrypicking it from lenny
(debsecan supports this because there was real demand for this
feature).  If I understand your rule correctly, it applies in this
case, and gives the wrong result.  And this is not the result of
temporarily outdated data, it will always give the wrong result, no
matter what additional data we add.

I've attached a very rough draft on the vulnerability versioning
challenges.  Perhaps it's helpful even in this stage, but don't count
on it.

\documentclass[a4paper,11pt]{scrartcl}
\usepackage{array}
\usepackage{amsmath}
\usepackage{listings}

\title{The Debian Security Tracker}
\author{Florian Weimer}
\date{August 2008}

\begin{document}
\maketitle

\let\distro\textsf

\def\nonterm#1{\ensuremath{\langle\mbox{\slshape #1}\rangle}}
\def\terminal#1{\ensuremath{\mbox{\tt #1}}}
\def\opt{\mathrm{opt}}

\lstdefinelanguage{SML}%
  {morekeywords={abstype,and,andalso,as,case,do,datatype,else,end,%
       eqtype,exception,fn,fun,functor,handle,if,in,include,infix,%
       infixr,let,local,nonfix,of,op,open,orelse,raise,rec,sharing,sig,%
       signature,struct,structure,then,type,val,with,withtype,while},%
   sensitive=true,%
   morecomment=[n]{(*}{*)},%
   morestring=[d]",%
   literate=%
   {->}{$\to$ }{1} {=>}{$\Rightarrow$ }{1} {*}{$\star$ }{1}%
  }[keywords,comments,strings]

\section*{Overview}


This report is structured as follows.
\begin{enumerate}
\item Section~\ref{sec:distros} provides a birds-eye view on the
  Debian development and release engineering processes.
\item Section~\ref{sec:users} shows how users typically make use of
  Debian distributions to create system installations.
\end{enumerate}

\section{Debian distributions}
\label{sec:distros}

Debian development happens on different \emph{distributions}.  The
main development effort concentrates on \distro{unstable} (permanently
code-named \distro{sid}).  After some delay, and barring major
problems recorded in the Debian bug tracker, packages propagate from
\distro{testing} (which is coded-named with the next \distro{stable}
release, like \distro{sarge}, \distro{etch}, or \distro{lenny}).

During preparations for a new \distro{stable} release, propagation
from \distro{unstable} to \distro{testing} is gradually frozen, until
\distro{testing} is released as the new \distro{stable}.  When the
freeze period is over, \distro{testing} receives a new code name.

In addition to the regular release process, packages enter
\distro{testing} and \distro{unstable} through to additional
distributions, \distro{-proposed-updates} and \distro{-security}.  The
former is for critical bug fixes, the latter is for security fixes.

The official terminology in this area is somewhat unclear.  Usually,
Debian itself is called a GNU/Linux distribution, so other names like
\emph{suite} or \emph{branch} have been proposed, the latter following
terminology that is well-established in the software version control
context.  In addition, \distro{stable-security} and their likes are
sometimes not considered full distributions because they are not
closed under package dependencies.

\begin{table}
  \centering
  \begin{tabular}{>{\sf}l|c|l}
    \rmfamily\textbf{Distribution} 
      & \textbf{Upload} & \textbf{Packages propagate from} \\\hline
    unstable & yes & n/a \\
    testing & no & \distro{unstable} \\
    \quad testing-proposed-updates & yes & n/a \\
    \quad testing-security & moderated & n/a \\
    stable & no & \distro{testing}, \distro{stable-proposed-updates} \\
    \quad stable-proposed-updates & moderated & \distro{stable-security} \\
    \quad stable-security & moderated & n/a \\
    volatile & yes & \distro{unstable} (but manually)
  \end{tabular}
  \caption{Debian development branches}
  \label{tab:distros}
\end{table}

Table~\ref{tab:distros} summarizes these processes.  The \emph{upload}
column indicates if it is possible for Debian Developers to directly
upload packages to this distribution.  The entry ``moderated'' means
that an upload is possible, but that further, manual action is
required before the new package version is published by Debian.

In this context, \distro{volatile} is somewhat special.  It mostly
consists of packages from \distro{unstable} recompiled on the
\distro{stable} distribution, with minimal changes to the package
(creating a \emph{backport}).  Nowadays, backports to \distro{stable}
use version numbers which are less then the version number from
\distro{unstable} they are based one, to ease upgrades once the
version from \distro{unstable} is available from \distro{stable} after
the next release.

\section{Composing distributions to create Debian installations}
\label{sec:users}

Within the limits imposed by package dependencies, end users can mix
packages from different distributions.  The package metadata recorded
by the system does not contain information about the distribution the
package came.

% FIXME
There is an official repository of backports called
\distro{volatile}, but there are others like \texttt{backports.org}.


\section{Strategy for security updates}

There are two different ways to approach security updates for
distributions other than \distro{unstable} (or more, generally
speaking, development branches besides the head branch):
\begin{itemize}
\item {\sfb Minimal changes.} A minimal change addressing the vulnerability
  is applied to the package version in \distro{stable}.  This is the
  approach adopted by Debian.
\item {\sfb Updates to the unstable version.} The security update is
  applied to the package version in \distro{unstable}.  Essentially
  the same version is used for \distro{stable-security} or
  \distro{testing-security} (potentially with backports).
\end{itemize}

The first approach may require some effort to extract the minimal
change from an upstream security update.  A predictable build
environment is a must as well.  However, in many cases, considerably
less testing is required because the changes are confined to a small
portion of the package.  The downside is that this approach does not
interact well with most version control systems which are in use
today.

The second approach is popular with some other vendors.  It interacts
in a well-defined manner with version control.  Often, it buries the
security-relevant change in unrelated fixes, which makes it more
difficult to reverse engineer the vulnerability from the update in
case no source code is available.  Obviously, this is less of a
concern to Debian.  But more importantly, Debian lacks a strict policy
against backwards-incompatbile changes in the \distro{unstable}
distribution\footnote{In the author's opinion, this is generally a
  good thing because it allows to correct past mistakes with
  reasonable effort on the developer's side.  However, other vendors
  follow a completely different philosophy.}---for instance, the
default configuration settings or ABIs and APIs could have changed
since the last \distro{stable} release.  This means that as a general
policy, this strategy does not work for Debian.

In some cases, the second approach is the only possibility,
particularly if the original API (and hence ABI) itself is insecure.
But in general, the Debian security team tries to follow the minimal
changes approach.


\section{Version-based vulnerability tracking}

In order to cut down the number of version annotations that are
required for each vulnerability, we make the following simplifying
assumption.
\begin{quote}
  {\sfb Assumption:} Suppose that a vulnerability in a package was
  fixed in version $v$, and that $v'$ is another version of the same
  package.  If $v'\geq v$, the vulnerability is fixed in version $v'$
  as well.
\end{quote}
This assumption, together with propagation from distribution to
distribution, means that there is a cone of fixed versions once the
fixed version in \distro{unstable} has been identified.  As a result, it
is not necessary to record new vulnerability information if a new
development branch is created, which reduces maintenance effort
considerably.

Of course, the assumption is not universally true.  Regressions
happen, and vulnerabilities are sometimes reintroduced.  However, when
this happens, they can be treated as a new, distinct vulnerability.
This also makes sense from an operational perspective because in those
rare cases, a new security update must be rolled out, with a different
set of affected package versions.

Note that the assumption does not state an equivalence.  There are
several reasons why the converse, that is, $v' < v$ and $v'$ is
vulnerable, might not hold:
\begin{enumerate}
\item $v'$ may be sufficiently old, and the vulnerable code had not
  yet been introduced at this stage.
\item $v$ and $v'$ belong to different distributions $D$ and $D'$,
  there is propagation from $D$ to $D'$, but $v'$ was fixed
  indepedently.  For example, $v$ is the correction in
  \distro{unstable}, and $v'$ is the one for \distro{stable-security}.
\item $v$ and $v'$ belong to different distributions $D$ and $D'$,
  there is propagation from $D$ to $D'$, but the vulnerability was
  never present in $D$.  (Perhaps it was introduced during another
  security update.)
\end{enumerate}

In order to deal with these shortcomings, we introduce
distribution-specific annotations.  In the first case, we mark the
distribution as not vulnerable (that is, the vulnerability was fixed
in version ``\texttt{0}''). 
% This ignores ~, versions are no longer well-founded.
In the second case, $v'$ is used as the
fixed version for distribution $D'$.

The third case is the problematic one.  It means that sometimes, the
cone model mentioned at the beginning is violated in a very
fundamental way.

% Problems:
% lack of sync between -security and -proposed-updates
% latent vulnerability
% backports/volatile

\section{Vulnerability description file syntax}

\subsection{Basic syntax}

This section describes the syntax of the input data.  There are
certain validity constraints that are not expressed in the syntax;
these are given in section~\ref{sec:validity}.

\subsubsection{Syntax notation}

The syntax is describung using a BNF variant. Non-terminals are
written \nonterm{non-terminal}, terminals are written
\texttt{terminal}.  Alternatives are denoted by
$\nonterm{item-1}\mid\nonterm{item-2}$.  Repetition (one more more
times) is denoted by $\nonterm{item}^+$.  Optional elements are
written $\nonterm{item}_\opt$.  By extension, repetition of one or
more times is denoted by $\nonterm{item}_\opt^+$.  Grouping is
performed using parentheses, as in $(\nonterm{item})$.

The grammar is not very regular and does not suggest a clear
lexer/parser destinction.  The reason is that the notation grew
organically over several years, and started with a text file which was
only partly intended for automated processing.  Nevertheless,
reasonably efficient parsers can be constructed using regular
expressions or parsing expression grammars (PEGs).

\subsubsection{Primitives}

The non-terminal \nonterm{package-string} denotes a valid Debian
package name, and \nonterm{version-string} a valid Debian version
(both strings are always non-empty).\footnote{When implementing
  software which is used outside Debian, it makes sense to make the
  parser more liberal than what is permitted by the Debian Policy.
  % FIXME: reference Debian policy
} A (possibly empty) sequence of arbitrary characters (excluding line
feeds and other control characters) is denoted by \nonterm{free-text}.
A non-empty sequence of characters which are either space or
horizontal tabulators is given by \nonterm{ws} (``whitespace'').  A
non-empty sequence of characters from the set \terminal{a} to
\terminal{z} is given by \nonterm{alpha}. In addition, define
\begin{align*}
  \nonterm{digit} &:= \terminal{0} \mid \terminal{1} \mid \terminal{2} \mid 
  \terminal{3} \mid \terminal{4} \mid \terminal{5} \mid \terminal{6} \mid 
  \terminal{7} \mid \terminal{8} \mid \terminal{9}\\
  \nonterm{digits} &:= \nonterm{digit}^{+}
\end{align*}

\subsection{Vulnerability references}

CVE names follow the official MITRE format.
\begin{align*}
  \nonterm{cve-year} &:= \nonterm{digit}\nonterm{digit}\nonterm{digit}\nonterm{digit}\\
  \nonterm{cve-name} &:= \terminal{CVE-} \nonterm{cve-year} \terminal{-}
  \nonterm{digits}
\end{align*}
In addition, it is possible to reference Debian advisories by their name.
\begin{align*}
  \nonterm{dsa-name} &:= \terminal{DSA-}\nonterm{digits}\terminal-\nonterm{digits}\\
  \nonterm{dtsa-name} &:= \terminal{DTSA-}\nonterm{digits}\terminal-\nonterm{digits}
\end{align*}
Finally, $\nonterm{name} := \nonterm{cve-name} \mid \nonterm{dsa-name} \mid \nonterm{dtsa-name}$.

\subsubsection{Vulnerability metadata}

For the purposes of pure vulnerability tracking, the following items
are ignored.  The difference between a \terminal{TODO} and a
\terminal{NOTE} is that only the former causes the vulnerability to
appear in the automatically generated list of to-do items.
\begin{displaymath}
  \nonterm{freetext-note} := 
  \nonterm{ws} 
  (\terminal{NOTE}\mid\terminal{TODO}\mid\terminal{NOT-FOR-US})
  \terminal{:} \nonterm{ws} \nonterm{free-text}
\end{displaymath}
\terminal{NOT-FOR-US} is used to give a short reason why a particular
CVE name does not affect Debian.

Vulnerability annotations often have decators, which indicate the
severity:
\begin{displaymath}
  \nonterm{urgency} 
  := \terminal{unknown} \mid \terminal{low} \mid \terminal{medium} \mid \terminal{high}
\end{displaymath}
The decorators can also contain references to Debian bugs.
\begin{displaymath}
  \nonterm{debian-bug}
  := \terminal{bug}\nonterm{ws}\terminal{\#}\nonterm{digits}
\end{displaymath}
A decorator consists of a list of the elements described above.
\begin{align*}
  \nonterm{decorator-1} &:= \nonterm{uragency} \mid \nonterm{debian-bug}\\
  \nonterm{decorator} &:=
  \nonterm{ws} \terminal( 
  \nonterm{decorator-1}
  \bigl(\terminal, \nonterm{ws} \nonterm{decorator-1}\bigr)^+_\opt
  \terminal)
\end{align*}

A list of references to other vulnerabilities can be encoded as
follows.
\begin{displaymath}
  \nonterm{xref} := \terminal\{\nonterm{ws}_\opt
  \nonterm{name}
  \bigl(
  \nonterm{ws}
  \nonterm{name}
  \bigr)^+_\opt
  \nonterm{ws}_\opt
  \terminal\}
\end{displaymath}

A distribution constraint restricts the effect of an annotation to a
specific distribution.
\begin{displaymath}
  \nonterm{distribution} := \terminal{[} (\nonterm{alpha}\mid\terminal{-})^+ \terminal{]} \nonterm{ws}
\end{displaymath}
The distribution must be a valid code name (that is, \terminal{etch}
instead of \terminal{stable}), and the distribution \terminal{sid} is
not allowed.

A package reference is always preceded by a hyphen.
\begin{displaymath}
  \nonterm{package} := \terminal{-} \nonterm{ws} \nonterm{package-string}
\end{displaymath}

\subsubsection{Package Annotations}

Fixed version annotations specify an optional distribution, the
package name, the version when the fix occurred, and an optional
decorator.
\begin{displaymath}
  \nonterm{fixed} := 
  \nonterm{ws}
  \nonterm{distribution}_\opt
  \nonterm{package}
  \nonterm{ws}
  \nonterm{version-string}
  \nonterm{decorator}_\opt
\end{displaymath}

Unfixed annotations are structurally very similar, but use several
pseudo-versions.
\begin{align*}
  \nonterm{unfixed-version} &:= 
  \terminal{<unfixed>}
  \mid \terminal{<itp>}
  \mid \terminal{<removed>}\\
  \nonterm{unfixed} &:= 
  \nonterm{ws}
  \nonterm{distribution}_\opt
  \nonterm{package}
  \nonterm{ws}
  \nonterm{unfixed-version}
  \nonterm{decorator}_\opt
\end{align*}
\terminal{<unfixed>} means that the package is simply unfixed.
\terminal{<removed>} means that the package is unfixed and has been
removed from \distro{unstable}. Vulnerabilities for packages which are
not yet in the archive, but for which an \emph{intent-to-package}
bug\footnote{See \texttt{http://www.debian.org/devel/wnpp/}.}  exists
can be recorded using \terminal{<itp>}.

Some annotations contain free-text fields:
\begin{displaymath}
  \nonterm{reason} := \nonterm{ws} \terminal( \nonterm{free-text} \terminal)
\end{displaymath}

If, for some reason, it was decided not to issue a DSA for a specific
vulnerability, this can be recorded as:
\begin{displaymath}
  \nonterm{no-dsa} := 
  \nonterm{ws}
  \nonterm{distribution}
  \nonterm{package}
  \nonterm{ws}
  \terminal{<no-dsa>}
  \nonterm{reason}
\end{displaymath}
This entry implies that the vulnerability is still present in
\nonterm{distribution}.  If it is fixed in a stable point update, the
\nonterm{no-dsa} annotation is replaced with a \nonterm{fixed}
annotation recording the fixed version.

If a package is not affected by a particular vulnerability, the
following annotation can be used.
\begin{displaymath}
  \nonterm{not-affected} := 
  \nonterm{ws}
  \nonterm{distribution}_\opt
  \nonterm{package}
  \nonterm{ws}
  \terminal{<not-affected>}
  \nonterm{reason}
\end{displaymath}
The relationship with \terminal{NOT-FOR-US} is somewhat unclear at
this point. % FIXME

\subsubsection{CVE specifics}

The main data file contains the CVE-related records.
\begin{align*}
  \nonterm{cve-description} &:= \nonterm{ws} 
  \bigl(\terminal{(} \nonterm{free-text} \terminal{)}
  \mid \terminal{[} \nonterm{free-text} \terminal{]}\bigr)
  \nonterm{ws}\\
  \nonterm{cve-start-regular} & := \nonterm{cve-name}\nonterm{cve-description}_\opt\\
  \nonterm{cve-start-pending} & := \terminal{CVE-}\nonterm{cve-year}\terminal{-XXXX}\nonterm{cve-description}\\
  \nonterm{cve-start} &:= \nonterm{cve-start-regular} \mid \nonterm{cve-start-pending}\\
  \nonterm{cve-flag} &:= \nonterm{ws} (\terminal{RESERVED} \mid \terminal{REJECTED})
\end{align*}
CVE flags are generally ignored.  However, \terminal{REJECTED} CVE
entries should not contain annotations because those CVE name are
considered to be invalid.  (This cannot be a hard error because
\terminal{REJECTED} flags can be introduced by MITRE at any point in
time.)

Entries of the form \nonterm{cve-start-pending} is used to generate a
temporary identifier for an issue which needs to be tracked by Debian,
but has not received a CVE candidate name yet.

\subsection{Validity constraints}
\label{sec:validity}

\begin{itemize}
\item All vulnerability names listed in an \nonterm{xref} must
  actually exist in one of the input files.
\item \nonterm{no-dsa}, \nonterm{not-affected}, \nonterm{unfixed}may
  only appear in the CVE vulnerability list.
\item \nonterm{distribution} is only optional in the CVE vulnerability
  file.
\end{itemize}

\section{Security state descriptor}

This section describes \emph{security state descriptor}, a way to
formally specify the set of package versions a particular
vulnerability affects.  Throughout this section, we assume that a
particular package and vulnerability have been fixed.  If no security
state descriptor exist, the package is deemed to be not affected by
the vulnerability.

Security state descriptors are similar to OVAL definitions.
% FIXME: reference

\subsection{Formal definition}

We use Standard ML to describe the structure of security state
descriptors. % FIXME: reference

\lstset{language=SML,columns=fullflexible,xleftmargin=3em,identifierstyle=\slshape}
\begin{lstlisting}
type version = string
val compareVersion : version * version -> order = ..
fun versionEq lr = compareVersion lr = EQ
val unaffected : version = "0"
\end{lstlisting}
As a simplification, we assume that package versions are represented
as strings.  The comparison function is used below; it's behavior is
specified in the Debian Policy.  The special constant
\lstinline{unaffected} is used as a placeholder to state ``no version
is vulnerable''.  % FIXME: "~0" is smaller?

\begin{lstlisting}
datatype distribution = POTATO | WOODY | SARGE | ETCH | LENNY | ...  
\end{lstlisting}
The distribution.  The fact that this type is a datatype is not
material; any equality type will do.

\begin{lstlisting}
datatype urgency = LOW | MEDIUM | HIGH  
\end{lstlisting}
The urgency.
% FIXME: what to do about unimportant?

\begin{lstlisting}
type unstable_desc {
  fixedIn : version option,
  urgency : urgency option
}
\end{lstlisting}
The security status descriptor for the \distro{unstable} distribution.
For instance, if a medium-urgency vulnerability was fixed in version
1.2-3, this is represented as the expression
\lstinline!{fixedIn = SOME "1.2-3", urgency = SOME MEDIUM}!.
A low-urgency vulnerability that has not been fixed is described by
\lstinline!{fixedIn = NONE, urgency = SOME LOW}!.  Finally,
a vulnerability that does not affect \distro{unstable} for
which no urgency is known is 
\lstinline!{fixedIn = SOME unaffected, urgency = NONE}!.

\begin{lstlisting}
type dist_desc {
  fixed : version list,
  vulnerable : version list,
  urgency : urgency option
}
\end{lstlisting}
% FIXME: remove the #vulnerable field, to document the existing state
% of affairs.
% urgency is useless then,  because it's not possible to figure
% out the branch in question.
The security status descriptor for other distributions than
\distro{unstable}.  Unlike \distro{unstable}, the descriptor contains
an explicit list of versions which are vulnerable, and versions which
have been fixed.

\begin{lstlisting}
type ssd = {
  unstable : unstable_desc,
  distributions : (distribution * dist_desc) list
}
\end{lstlisting}
The combination of the individual security state descriptors of all
distributions.

There are additional constraints on permitted values \lstinline{d} of
type \lstinline{ssd} which are given below.
\begin{itemize}
\item If \lstinline{#unstable (#fixedIn d) = SOME unaffected}, then
  \lstinline{#distributions d <> []}.
\item For all \lstinline{dist_desc} values, at least one of the
  \lstinline{fixed} and \lstinline{vulnerable} lists is not empty.
\end{itemize}

\subsection{Evaluating individual package versions}

\begin{lstlisting}
fun checkDistributions (dists, v) =
  List.exists (fn (_, fixed) =>
                 List.exists (fn fv => fv = v) fixed) dists
\end{lstlisting}
This function returns true if the version \lstinline{v} is among the
fixed versions.
               
\begin{lstlisting}
fun isVulnerable ({unstable, distributions}, v) =
  case #fixedIn unstable of
    SOME fv => (case compareVersions (v, fv) of
                  LT => checkDistributions (distributions, v)
                  EQ | GT => false)
    NONE => checkDistributions (distributions, v)
\end{lstlisting}
The main discriminator is the \distro{unstable} version in which the
vulnerability has been fixed.  If the package is not fixed, we fall
back to the explicit list of fixes in the other distributions.

\end{document}

Reply via email to