%
% Copyright 1995, Michal Jaegermann
%
% You may print and reprint this article, as long as the author
% will be acknowledged. 
%
\ifx\documentclass\undefined
\documentstyle[11pt]{article}
\else
\documentclass[11pt]{article}
\fi

\newcommand{\command}[1]{\mbox{\tt #1}}
\newcommand{\path}[1]{{\tt #1}}

\begin{document}
\title{Making MakeTeXPK safer for Unix installations}
\author{Micha{\l} Jaegermann\\
{\small\tt michal\char`\@ellpspace.math.ualberta.ca,}\\
{\small\tt michal\char`\@gortel.phys.ualberta.ca}}
\date{14 February 1995}
\maketitle

\begin{abstract}
  Leaving directories open for writing by anybody in the world 
  in order to ensure that \command{MakeTeXPK} will work is often a
  security concern. This note describes how to avoid that on Unix
  systems without loosing functionality.
\end{abstract}
\renewcommand{\thefootnote}{}
\footnote{Unix is a registered trademark of AT\&T}
\footnote{\TeX{} is a trademark of the American Mathematical Society}
\footnote{Metafont is a trademark of the Addison--Wesley Publishing Company}

If you run \TeX{} on Unix, or any other Unix-like, system and you are
using very convenient and popular setup with an automatic bitmap
generation then you undoubtedly noticed that this requires directories
with \command{'write'} permissions to everybody.  In theory this is
not much different than having your \path{/tmp} directory opened that
way.  In practice, though, especially when your programs were compiled
with \command{kpathsea} library with ensuing multiple font
directories, this may be some security and administrative headache.

For the first and rather feeble line of defense, if your variant of
Unix supports that, you should set 'sticky' bit on directories in
question like this: \command{chmod a+t pk}.  That way files can be
removed or overwritten only by their owner(s).  Unfortunately this
does not prevent a ``denial of service'' attack when a perpetrator
may fill out directories with a garbage (like empty) files with
proper names.  No big harm is done on a long run, but repeated pranks
like that may turn into a major nuisance.

If you were thinking about running \command{MakeTeXPK} ``suid''
(that means --- in a mode which gives a program all privileges of
its {\em owner\/}) then you should drop the idea immediately;
especially if \command{MakeTeXPK} is owned by \command{root}.
This is a {\em shell script} and for many reasons running shell
scripts in suid mode is one of the biggest security holes you may
think of.  Because of that many Unix variants simply disable such
scripts, thus saving you from yourself; but even if this is not the
case on some particular system do {\bf not} do that.  Problems with
``world writable'' directories pale in a comparison.

Fortunately not everything is lost as long as you have a compiler and
an editor.  \command{MakeTeXPK} and relatives (\command{MakeTeXTFM},
\command{MakeTEXMF}, \ldots) may be executed indirectly, by a small
compiled ``wrapper'' program and the last one can be safely made
``suid''.

This note describes below steps needed to perform such modification.
Details and accompanying sample files are for one particular
distribution (some version of \command{teTeX} for Linux) but
modifications for other installations should be rather straightforward.
It goes without saying that you need a \command{root} access for all
(ok, most of) these steps.

\begin{itemize}
\item Create new user account, say \command{tex}, on your system
  and include it some innocuous group, e.g.~\command{tex}.
\end{itemize}
The only purpose of of this ``user'' will be to own common \TeX{}
files. You may have already some suitable group, like
\command{nogroup} or \command{nobody}, so instead you may include your
new user there.
\begin{itemize}
\item Give \command{tex} account \path{/bin/false} for a login shell
  and disable password by putting \command{*} in the corresponding
  field.  Home directory is not terribly important.  Nobody will be
  ever logging into this account.
\end{itemize}
An entry in your password file will look after this somewhat like
that:
\begin{verbatim}
tex:*:117:65535:Owner of TeX files:/usr/local/tex:/bin/false
\end{verbatim}
\begin{itemize}
\item Close a loophole in some mail delivery programs by creating
  an empty file \path{/usr/spool/mail/tex} executing
  \command{touch /usr/spool/mail/tex}.
  You may find the file already
  in place made by some yours system administration utility.
  Change owner and a group of this file to those of root
  (\command{chown root:root /usr/spool/mail/tex})
  and remove all read and write permissions on it
  (\command{chmod 000 /usr/spool/mail/tex}).
\item Make sure that \command{MakeTeXPK}, and similar scripts you want
  to execute the same way, are {\bf not} in your \$PATH, or at least not
  earlier then an intended location of your ``wrapper'' program(s).
\end{itemize}
For \command{teTeX} distribution, version 0.2, it is enough to delete
links in a directory \path{/usr/local/bin}.  Real scripts
\command{MakeTeXPK}, \command{MakeTeXMF} and \command{MakeTeXTFM}
reside in \path{/usr/local/tex/scripts-0.2/bin/} and we may leave them
there.
\begin{itemize}
\item Edit all scripts in question to replace in invocations programs
  by their absolute locations.  Do not forget to perform this task
  in other scripts which may be called by our \command{MakeTEX...}
  scripts (this is \command{append\_db} in \command{teTeX}
  distribution). Change mode used when creating new files to 444
  and to 755 for directories.
\end{itemize}
A proper way to do that is start a script with a series of shell
variable definitions similar to \command{MF="/usr/local/bin/mf"} and
replace all later occurences of \command{mf} in the script by
\command{\$MF}.  That way, if you later move your {\sc Metafont}
executables to some other place, script editing will be limited to one
place.  Similar for other programs.  Absolute locations are required
since, for security reason, we will limit \command{\$PATH} only to
\path{"/bin:/usr/bin"}. This means that in theory you may leave things
like \command{test}, \command{echo} or \command{rm} alone.  In the
later case, for ``dangerous'' commands like \command{rm -f} is still a
good idea to replace them with definitions similar to
\command{RM="/bin/rm -f"} to get a better control on what you are
really executing.  As a side effect you will ensure that this is a
``real'' system \command{rm} and not some funky user redefinition
which asks a lot of questions before doing anything and confuses
\command{MakeTeXPK} in the process.

Depending on a level of your mistrust you may use a similar approach
to \command{echo} and \command{test} as well, but in bundled sample
files I have chosen to be more relaxed.  Besides they may anyway be
``built-ins'' for some shells.
\begin{itemize}
\item Edit, to adjust to your system, and compile a program like this
  one below.
\end{itemize}
%
\begin{verbatim}
/****************************************************/
/*                                                  */
/*  Executable wrapper for MakeTeX... programs.     */
/*  Calls its namesake from TOOLS directory.        */
/*  Provide links with different names to make it   */
/*  multipurpose.                                   */
/*                                                  */
/*  Michal Jaegermann, Feb 11 1995                  */
/*                                                  */
/****************************************************/

#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#define VERSION_S "0.2"
/*
 * If you do not have ANSI compiler you may use
 * an "explicit"single string in TOOLS define; this
 * is just a way to make future modifications easier.
 */
#define TOOLS   "/usr/local/tex/scripts-" VERSION_S "/bin/"
#define ASIZE  120

/*
 * This is a list of names under which we are willing
 * to execute. It has be NULL terminated.
 */
const char *accepted[] = {
    "MakeTeXPK",
    "MakeTeXTFM",
    "MakeTeXMF",
    NULL
};

int
main(int argc, char **argv)
{
    char doer[ASIZE] = TOOLS;
    int idx = 0;
    /*
     * If your compiler is broken and the construction below
     * does not work then "tail = strchr(doer, '\0');", or
     * equivalent, will serve as well.
     */
    char *tail = doer + (sizeof(TOOLS) - 1);
    char *start;

    /* find our base name */
    start = (start = strrchr(argv[0], '/')) ?
                               (start + 1) : argv[0];
    /* check if we are on the list */
    while (1) {
        if (NULL == accepted[idx])
            exit(1);          /* not on the list - bye-bye */
        if (0 == strcmp(accepted[idx], start))
            break;            /* this is ours */
        idx += 1;
    }
    /*
     * Set pretty bland, but hopefuly secure environment;
     * we intend to run this program 'suid'.
     */
    setenv("PATH", "/bin:/usr/bin", 1);
    setenv("IFS", " ", 1);
    /*
     * You may want/need some other calls to setenv().
     * For example, if your system has an environment
     * variable pointing to shared libraries it should
     * be set here.
     */

    /*
     * Attach our name at the end of a directory string.
     * This assumes that real scripts in TOOLS directory
     * will be called by their own names (but indirectly)
     */
    strcpy(tail, start);
    setuid(geteuid());          /* we want priviledges of
                                   an owner of this program */
    return execv(doer, argv);   /* do it and tell results   */
}
\end{verbatim}
\begin{itemize}
\item Install results of the compilation somewhere in your
  \command{\$PATH}.  A directory \path{/usr/local/bin} is
  usually a good place.  Go there and name your program
  \command{MakeTeXPK}.
   Change its ownership and group to that of user \command{tex} by
  typing \command{chmod tex:tex MakeTeXPK}.  (Depending on your
  variant of Unix you may have to use dot instead of colon to separate
  user and its group name, or you may have to do that in two steps,
  using also another command, \command{chgrp}, to acomplish the above.
  Use a group to which you assigned your \command{tex} user.  This is
  only an example).  The program needs ``execute'' and ``set uid''
  priviledges (\command{chmod 4755 MakeTeXPK}).  Also for your other
  \command{MakeTeX...} scripts provide corresponding ``call points''
  with their names via file links (\command{ln MakeTeXPK MakeTeXTFM};
  \command{ln MakeTeXPK MakeTeXMF}).
\item Change ownership of all your font files to \command{tex}.
  Actually you may make \command{tex} an owner of whole directory
  trees in \TeX{} system files.  Assuming that all you want to asign
  that way is in a tree rooted in \path{texmf} you may accomplish that
  by doing \command{chown -R tex:tex texmf}.  If your \command{chown}
  does not understand \command{-R} (recursive) flag, then something
  similar to the following should do:
\begin{verbatim}
    find texmf -print | xargs -n1 chown tex:tex
\end{verbatim}
  See also \command{chown} caveats in the previous item.
\end{itemize}
\begin{itemize}
\item Remove ``write'' permissions for anybody but owner on all
  directories in question.  A command like this should
  accomplish that task (careful, you do not want to change
  non-directories):
\begin{verbatim}
    find texmf -type d -print | xargs -n1 chmod 755
\end{verbatim}
\end{itemize}
You are done. Now, when \command{MakeTeXPK} will be called, directly
from a command line, or by some other program like \command{dvips},
your ``wrapper'' program will be executed instead.  It will call in
turn a ``real'' script but already with an id of an owner of your font
directories.


\subsubsection*{Concluding remarks}

The presented solution is not entirely without problems.  Due to ``out
of sync'' ownership and permissions \command{kpathsea} library
functions may fail, depending on a moment this happened, when trying
to write \path{missfont.log} file in cases when font making was not
successful.
This can likely be hard to resolve without modifying the library
itself.  If you will really encounter this, then a simple workaround
would be to create an empty \path{missfont.log}, owned by you, and to
give on it write permissions for everybody \\(\command{touch missfont.log;}
\command{chmod a+w missfont.log}).  When you are done simply change
permissions back to an original state.

Another possible trouble spot will occur when you have your
``private'' fonts, because you are conducting some font making
experiments, for example, and you would like to have bitmaps created
in places owned by you and not by \command{tex}.  Otherwise you will
not be able to remove results of failed tests.  In that case make
yourself a private, executable, copy of \command{MakeTeXPK} script,
edit it accordingly and make sure that it can be found {\em earlier}
in your \command{\$PATH} than a system program with the same name.
You will not able to deposit anything in system directories, but this
is likely what you want anyway.  Your script does not have to run
``suid'', so repeating all of the above is not necessary.

Last, but not least, there is possible somewhat another approach to
the whole problem.  There are only a few commands (\command{mv},
\command{mkdir}, \command{chmod}) which have to modify \TeX{}
``system'' directories.  Instead of running the whole
\command{MakeTeX} in ``suid'' mode you may write special versions
(\command{texmv}, \command{texmkdir}, \command{texchmod}) of these,
which would operate ``suid'' \command{tex}, and use them as
replacements in \command{MakeTeXPK} script whenever needed.  If this
is a better idea depends entirely on your situation and 
security requirements.

\end{document}

%%% Local Variables: 
%%% mode: latex
%%% TeX-master: t
%%% End: 
