--- /dev/null
+# -*-makefile-*-
+#
+# $Id: Makefile.in,v 1.1 1997/07/21 13:47:55 mdw Exp $
+#
+# Makefile for `become'
+#
+# (c) 1997 EBI
+#
+
+#----- Licencing notice -----------------------------------------------------
+#
+# This file is part of `become'
+#
+# `Become' 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.
+#
+# `Become' 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 `become'; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#----- Revision history -----------------------------------------------------
+#
+# $Log: Makefile.in,v $
+# Revision 1.1 1997/07/21 13:47:55 mdw
+# Initial revision
+#
+
+#----- Configuration section ------------------------------------------------
+
+# --- Miscellaneous stuff ---
+
+SHELL= /bin/sh
+VERSION= @VERSION@
+
+# --- Finding source code ---
+
+srcdir= @srcdir@
+VPATH= @srcdir@
+
+# --- Where to put things ---
+
+prefix= @prefix@
+exec_prefix= @exec_prefix@
+bindir= @bindir@
+libdir= @libdir@
+etcdir= @sysconfdir@
+
+# --- Compiling ---
+
+CC= @CC@
+CFLAGS= @CFLAGS@
+CPPFLAGS= @CPPFLAGS@ -I$(srcdir) -I.
+TEST_DEFINES= -DTEST_RIG
+
+YACC= @YACC@
+YFLAGS= -d -v
+
+LEX= @LEX@
+LEXFLAGS=
+
+DEPEND= gcc -MM -MG -x c
+
+# --- Linking ---
+
+LD= @CC@
+LIBS= @LIBS@ @LEXLIB@
+LDFLAGS= @LDFLAGS@
+AR= @AR@
+RANLIB= @RANLIB@
+
+# --- Installation ---
+
+INSTALL= @INSTALL@
+INSTALL_PROG= @INSTALL_PROGRAM@
+INSTALL_DATA= @INSTALL_DATA@
+
+# --- Other useful things ---
+
+SED= sed
+MV= mv -f
+RM= rm -f
+ETAGS= etags
+
+#----- Interesting files ----------------------------------------------------
+
+# --- Library of common things ---
+
+BCMLIB= libbecome.a
+BCMLIBSRCF= idea.c md5.c icrypt.c crypt.c tx.c \
+ class.c name.c rule.c set.c sym.c userdb.c \
+ check.c daemon.c \
+ parser.y lexer.l \
+ mdwopt.c utils.c
+BCMLIBSRC= $(addprefix $(srcdir)/, $(BCMLIBSRCF))
+BCMLIBOBJ= $(addsuffix .o, $(basename $(BCMLIBSRCF)))
+
+# --- The `become' program ---
+
+BECOME= become
+BECOMESRCF= become.c
+BECOMESRC= $(addprefix $(srcdir)/, $(BECOMESRCF))
+BECOMEOBJ= $(addsuffix .o, $(basename $(BECOMESRCF)))
+
+# --- The `keygen' program ---
+
+KEYGEN= keygen
+KEYGENSRCF= keygen.c
+KEYGENSRC= $(addprefix $(srcdir)/, $(KEYGENSRCF))
+KEYGENOBJ= $(addsuffix .o, $(basename $(KEYGENSRCF)))
+
+# --- Various tests for some important modules ---
+
+TESTS= crypt-test icrypt-test idea-test md5-test \
+ name-test sym-test userdb-test \
+ become-test
+TESTOBJ= $(addsuffix .o, $(TESTS))
+
+# --- Everything ---
+
+ALLSRC= $(BCMLIBSRC) $(BECOMESRC) $(KEYGENSRC)
+ALLOBJ= $(BCMLIBOBJ) $(BCMLIB) $(BECOMEOBJ) $(KEYGENOBJ) $(TESTOBJ)
+
+# --- Generated source files ---
+
+GENSRCF= parser.c parser.tab.h lexer.c
+GENSRC= $(addprefix $(srcdir)/, $(GENSRCF))
+
+# --- Installed files ---
+
+INSTALLABLES= $(bindir)/$(BECOME) $(bindir)/$(KEYGEN)
+
+#----- Parser tokens header file --------------------------------------------
+
+%.tab.h: %.c
+ $(MV) y.tab.h $@
+
+#----- Main target ----------------------------------------------------------
+
+all: $(BECOME) $(KEYGEN)
+
+#----- Compiling the code ---------------------------------------------------
+
+$(BCMLIB): $(BCMLIBOBJ)
+ $(AR) rc $(BCMLIB) $?
+ $(RANLIB) $(BCMLIB)
+
+$(BECOME): $(BECOMEOBJ) $(BCMLIB)
+ $(LD) $(LDFLAGS) $^ $(LIBS) -o $(BECOME)
+
+$(KEYGEN): $(KEYGENOBJ) $(BCMLIB)
+ $(LD) $(LDFLAGS) $^ $(LIBS) -o $(KEYGEN)
+
+#----- Installation ---------------------------------------------------------
+
+# --- Installing ---
+
+install: $(INSTALLABLES)
+
+$(bindir):; $(srcdir)/mkinstalldirs $(bindir)
+
+$(bindir)/%: % $(bindir)
+ $(INSTALL_PROG) $< $(bindir)
+
+# --- Uninstalling ---
+
+uninstall:; $(RM) $(INSTALLABLES)
+
+#----- Environmental luxuries -----------------------------------------------
+
+tags: $(srcdir)/TAGS
+$(srcdir)/TAGS: $(ALLSRC)
+ $(ETAGS) $(ALLSRC) -o $(srcdir)/TAGS
+
+depend: $(srcdir)/depend
+$(srcdir)/depend: $(ALLSRC)
+ $(SED) \
+ -e '/^# \[Generated dependencies]/ q' \
+ $(srcdir)/Makefile.in >$(srcdir)/new-Makefile.in
+ $(DEPEND) $(ALLSRC) | \
+ sed -e 's/'$(srcdir)'\///g' \
+ >>$(srcdir)/new-Makefile.in
+ $(MV) $(srcdir)/new-Makefile.in $(srcdir)/Makefile.in
+ touch $(srcdir)/depend
+
+#----- Distribution building ------------------------------------------------
+
+dist: $(GENSRC)
+ $(srcdir)/mkdist -p $(BECOME) -v $(VERSION) \
+ -m $(srcdir)/MANIFEST -i $(srcdir)
+
+#----- Cleanliness ----------------------------------------------------------
+
+mostlyclean:; $(RM) \
+ $(BECOME) $(KEYGEN) $(TESTS) $(ALLOBJ) \
+ y.output \
+ $(srcdir)/core $(srcdir)/\#* $(srcdir)/*~ core \#* ~*
+
+clean: mostlyclean
+
+distclean: clean
+ $(RM) Makefile \
+ config.status config.cache config.h config.log
+
+maintainer-clean: distclean
+ $(RM) $(srcdir)/TAGS $(srcdir)/depend $(GENSRC) \
+
+#----- Test rigs for various pieces of code ---------------------------------
+
+test: $(TESTS)
+
+%-test.o: %.c config.h
+ $(CC) $(CFLAGS) $(CPPFLAGS) $(TEST_DEFINES) -c $< -o $@
+
+%-test: %-test.o $(BCMLIB)
+ $(LD) $(LDFLAGS) $^ $(LIBS) -o $@
+
+#----- Administrivia --------------------------------------------------------
+
+.PHONY: all tags install instdirs uninstall tests depend \
+ mostlyclean clean distclean maintainer-clean
+
+#----- Automatic dependencies -----------------------------------------------
+#
+# [Generated dependencies]
+idea.o: idea.c config.h idea.h config.h utils.h
+md5.o: md5.c config.h md5.h config.h utils.h
+icrypt.o: icrypt.c config.h icrypt.h config.h idea.h config.h
+crypt.o: crypt.c config.h crypt.h become.h config.h config.h \
+ icrypt.h config.h idea.h config.h md5.h config.h tx.h \
+ utils.h
+tx.o: tx.c config.h tx.h
+class.o: class.c class.h sym.h set.h utils.h
+name.o: name.c class.h sym.h name.h userdb.h utils.h
+rule.o: rule.c become.h config.h class.h sym.h rule.h \
+ utils.h
+set.o: set.c set.h sym.h utils.h
+sym.o: sym.c sym.h utils.h
+userdb.o: userdb.c config.h sym.h userdb.h utils.h
+check.o: check.c become.h config.h config.h crypt.h config.h \
+ idea.h config.h lexer.h name.h class.h sym.h rule.h \
+ parser.h parser.tab.h tx.h utils.h
+daemon.o: daemon.c become.h config.h config.h crypt.h \
+ config.h daemon.h idea.h config.h lexer.h name.h \
+ class.h sym.h parser.h parser.tab.h rule.h tx.h \
+ userdb.h utils.h
+parser.y.o: parser.y class.h sym.h daemon.h lexer.h \
+ name.h rule.h become.h config.h set.h userdb.h \
+ utils.h
+lexer.l.o: lexer.l become.h config.h lexer.h parser.h \
+ class.h sym.h name.h parser.tab.h utils.h
+mdwopt.o: mdwopt.c mdwopt.h
+utils.o: utils.c config.h utils.h
+become.o: become.c become.h config.h config.h check.h \
+ daemon.h lexer.h mdwopt.h name.h class.h sym.h \
+ parser.h parser.tab.h rule.h utils.h
+keygen.o: keygen.c config.h tx.h mdwopt.h utils.h
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: config.h.in,v 1.1 1997/07/21 13:47:52 mdw Exp $
+ *
+ * Default settings for `become' config.h
+ *
+ * (c) 1997 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: config.h.in,v $
+ * Revision 1.1 1997/07/21 13:47:52 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+/*----- Configuration time ------------------------------------------------*/
+
+/* --- My version number --- */
+#define VERSION ???
+
+/* --- Where to find things --- */
+
+/* The `etcdir' contains configuration and state information */
+#define ETCDIR "/usr/local/etc"
+
+/* Other filenames based on the above */
+#define file_KEY (ETCDIR "/become.key")
+#define file_PID (ETCDIR "/become.pid")
+#define file_RANDSEED (ETCDIR "/become.random")
+#define file_RULES (ETCDIR "/become.conf")
+#define file_SERVER (ETCDIR "/become.server")
+
+/* --- Endianness (for transfer and cryptography) --- */
+
+/* If defined, we're big-endian */
+#undef WORDS_BIGENDIAN
+
+/* --- Swap endianness if necessary (don't alter this lot) --- */
+
+#define END_SWAP(n) \
+ do { \
+ register unsigned long x; \
+ x = n; \
+ x = ((x<<24) & 0xFF000000ul) | \
+ ((x<< 8) & 0x00FF0000ul) | \
+ ((x>> 8) & 0x0000FF00ul) | \
+ ((x>>24) & 0x000000FFul); \
+ n = x; \
+ } while (0);
+
+#ifdef WORDS_BIGENDIAN
+# define TO_BIG_END(x) ((void)0)
+# define FROM_BIG_END(x) ((void)0)
+# define TO_LITTLE_END(x) END_SWAP(x)
+# define FROM_LITTLE_END(x) END_SWAP(x)
+#else
+# define TO_BIG_END(x) END_SWAP(x)
+# define FROM_BIG_END(x) END_SWAP(x)
+# define TO_LITTLE_END(x) ((void)0)
+# define FROM_LITTLE_END(x) ((void)0)
+#endif
+
+/* --- Find a suitable 32-bit type --- */
+
+/* Define to be the size of an int */
+#define SIZEOF_INT 4
+
+#if SIZEOF_INT < 4
+ typedef unsigned long uint_32;
+#else
+ typedef unsigned int uint_32;
+#endif
+
+/* --- Path separator character --- */
+
+/* This is replaced by `/' by `configure' -- leave alone for DOSness */
+#define PATHSEP '\\'
+
+/* --- Whether to do debugging --- */
+
+#ifndef TEST_RIG
+#undef NDEBUG
+#endif
+
+#ifdef NDEBUG
+# define D(x) /* empty */
+#else
+# define D(x) x
+#endif
+
+/* --- YP support --- */
+
+#undef HAVE_YP
+
+/* --- Useful types --- */
+
+/* Type representing a user id */
+#undef uid_t
+
+/* Type representing a group id */
+#undef gid_t
+
+/* Type representing a process id */
+#undef pid_t
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#endif
--- /dev/null
+dnl -*-fundamental-*-
+dnl
+dnl $Id: configure.in,v 1.1 1997/07/21 13:47:51 mdw Exp $
+dnl
+dnl Source for auto configuration for `become'
+dnl
+dnl (c) 1997 Mark Wooding
+dnl
+
+dnl----- Licencing notice ---------------------------------------------------
+dnl
+dnl This file is part of `become'
+dnl
+dnl `Become' is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl `Become' is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+dnl GNU General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with `become'; if not, write to the Free Software
+dnl Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dnl----- Revision history ---------------------------------------------------
+dnl
+dnl $Log: configure.in,v $
+dnl Revision 1.1 1997/07/21 13:47:51 mdw
+dnl Initial revision
+dnl
+
+AC_INIT(icrypt.c)
+AC_CONFIG_HEADER(config.h)
+VERSION=1.1 AC_SUBST(VERSION)
+AC_DEFINE(VERSION, "1.1 (22 February 1997)")
+
+dnl --- Check for compilers and things ---
+
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_LEX
+AC_CHECK_PROG(AR, ar, ar)
+AC_PROG_RANLIB
+AC_PROG_YACC
+if test "$ac_cv_prog_gcc" = "yes"; then
+ CFLAGS="$CFLAGS -pedantic -Wall"
+fi
+
+dnl --- Libraries ---
+
+AC_CHECK_LIB(socket, socket)
+MDW_LIB_RESOLVER
+
+dnl --- Types ---
+
+AC_TYPE_PID_T
+AC_TYPE_UID_T
+
+dnl --- Check on endianness ---
+
+AC_C_BIGENDIAN(yes)
+
+dnl --- Check on type sizes ---
+
+AC_CHECK_SIZEOF(int)
+
+dnl --- Set the path separator ---
+
+AC_DEFINE(PATHSEP, '/')
+
+dnl --- Debugging stuff ---
+
+AC_ARG_ENABLE(debugging,
+ [--enable-debugging spews vast swathes of useless information],
+ [if test "$enableval" = "no"; then
+ AC_DEFINE(NDEBUG, 1)
+ fi],
+ AC_DEFINE(NDEBUG, 1))
+
+dnl --- Yell^H^H^H^HNetwork Information System ---
+
+AC_ARG_ENABLE(yp,
+ [--enable-yp read user names using ypcat],
+ [if test "$enableval" != "no"; then
+ AC_DEFINE(HAVE_YP, 1)
+ fi],
+ MDW_CHECK_YP)
+
+dnl --- Define where things get put ---
+
+mdw_save_prefix="$prefix"
+test "x$prefix" = "xNONE" && prefix="$ac_default_prefix"
+AC_DEFINE_UNQUOTED(ETCDIR, "`eval echo ${sysconfdir}`")
+prefix="$mdw_save_prefix"
+
+dnl --- Done ---
+
+AC_OUTPUT(Makefile)
+
+dnl----- That's all, folks --------------------------------------------------
--- /dev/null
+%%% -*-LaTeX-*-
+%%%
+%%% $Id: become.tex,v 1.1 1997/07/21 13:47:54 mdw Exp $
+%%%
+%%% Documentation for `become'
+%%%
+%%% (c) 1997 EBI
+%%%
+
+%%%----- Licencing notice ---------------------------------------------------
+%%%
+%%% This file is part of `become'
+%%%
+%%% `Become' 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.
+%%%
+%%% `Become' 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 `become'; if not, write to the Free Software
+%%% Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+%%%----- Revision history ---------------------------------------------------
+%%%
+%%% $Log: become.tex,v $
+%%% Revision 1.1 1997/07/21 13:47:54 mdw
+%%% Initial revision
+%%%
+
+%%%----- Document preamble --------------------------------------------------
+
+
+%% --- Document class and packages ---
+
+\documentclass[a4paper, 10pt]{article}
+\usepackage{array, tabularx}
+\usepackage[rounded]{syntax}
+
+\newif\ifxypic
+% \IfFileExists{xy.sty}{\usepackage[all]{xy}\xypictrue}{\xypicfalse}
+
+\IfFileExists{mdwfonts.sty}{\usepackage{mdwfonts}}{}
+
+
+%% --- Macros and things ---
+
+\newcommand{\become}{\textsf{become}}
+\newcommand{\path}[1]{\texttt{#1}}
+\def\<#1>{\synt{#1}}
+\newcommand{\xor}{\oplus}
+\newcommand{\ror}{\mathbin{>\mskip-6mu>\mskip-6mu>}}
+
+
+%% --- eqalign, from Plain TeX, LaTeXified ---
+
+\makeatletter
+\def\eqalign{%
+ \null\,\vcenter\bgroup\openup\jot\m@th%
+ \ialign\bgroup%
+ \strut\hfil$\displaystyle{##}$&$\displaystyle{{}##}$\hfil\crcr%
+}
+\def\endeqalign{
+ \crcr%
+ \egroup\egroup%
+ \,%
+}
+\makeatother
+
+
+%% --- Other layout preferences ---
+
+\setlength{\grammarindent}{1in}
+\renewcommand{\arraystretch}{1.2}
+\addtolength{\textwidth}{0.6in}
+\addtolength{\oddsidemargin}{-0.3in}
+\sloppy
+
+\begin{document}
+
+
+%%%----- Introductory matter ------------------------------------------------
+
+
+%% --- Title and some `brief' acknowledgements ---
+
+\title{The \become\ program}
+\author{Mark Wooding\thanks{
+ The program contains nontrivial pieces of code owned by the European
+ Bioinformatics Institute, Mark Wooding and Straylight (even though I
+ actually wrote it all). Thanks also to the Free Software Foundation for
+ Autoconf; to Ron Rivest for the MD5 message digest algorithm; to Xuekia
+ Lai and James Massey for the IDEA cipher; and to Bruce Schneier, for
+ writing \emph{Applied Cryptography}, which explained why I'd written the
+ first version of all this code wrong.} \\
+ \texttt{mdw@ebi.ac.uk}}
+
+\maketitle
+
+
+%% --- Abstract ---
+
+\begin{abstract}
+ This document describes a system for allowing users to `become' other users
+ in a secure and controlled way under Unix. The idea is to allow users to
+ maintain programs and other resources which require their own accounts
+ while removing the need for such accounts to have passwords (which can be
+ disclosed, forgotten or otherwise abused in ways that passwords for user
+ accounts don't tend to be).
+
+ The \become\ program will look up the user's identity, the identity of the
+ user he or she wishes to `become', the name of the program which is to be
+ executed, and the identity of the current host, consult a configuration
+ file, and decide whether the request is permitted before granting it. The
+ novel idea is that the table doesn't need to be on the local machine --
+ \become\ will send a request to a server, asking it for permission,
+ allowing the information to be held centrally, and making maintenance more
+ convenient. Cryptographic protocols are used to ensure the authenticity of
+ the server's responses.
+\end{abstract}
+
+
+%% --- Contents ---
+
+\tableofcontents
+
+
+%%%--------------------------------------------------------------------------
+\section{User guide}
+
+
+\subsection{Introduction}
+
+Running \become\ lets you `become' another user. What this really means is
+that it lets you execute a process with the permissions of another user.
+Which users you're allowed to `become', and exactly what processes you're
+allowed to execute as those users is determined by the people that installed
+\become.
+
+
+\subsection{Invoking \become}
+
+Invoking the \become\ program is impressively simple. There are essentially
+two forms of invocation:
+\begin{syntdiag}
+ `become' <user-name> \[ <program> \[ \< <argument> \> \] \]
+\end{syntdiag}
+and
+\begin{syntdiag}
+ `become' `-c' <shell-command> <user-name>
+\end{syntdiag}
+The first variant allows you to execute any \<program> as user \<user-name>,
+as limited by your site's configuration. The second variant simply passes
+the \<shell-command> to \path{/bin/sh}, so you must have permission to
+execute the shell as \<user-name>. The latter form doesn't actually allow
+you to do anything the former doesn't: it is in fact entirely equivalent to
+saying `\syntax{"become" <user-name> "/bin/sh -c" <shell-command>}', but
+slightly more compact.
+
+
+%%%--------------------------------------------------------------------------
+\section{Installation and configuration}
+
+
+\subsection{Unpacking and compiling}
+
+Before you begin, there are some prerequisites you should check:
+\begin{itemize}
+\item You need the Free Software Foundation's gzip program to unpack the
+ distribution archive.
+\item You need an ANSI-conformant C~compiler and library to correctly compile
+ the source code. No effort at all has been made to support nonstandard
+ C~implementations.\footnote{
+ The ANSI~C standard was ratified in 1989. It is now 1997. If your
+ system vendor hasn't bothered to comply with an eight year old standard,
+ you ought to firstly complain to your vendor, and secondly install the
+ GNU~C compiler.}
+\item You need a copy of GNU make to build the software. This can be
+ obtained from any GNU software archive.
+\item You'll probably need GNU flex and bison to rebuild the scanner and
+ parser. However, prebuilt C~source for these components is provided, and
+ will probably work.
+\item A recent version of \LaTeX\ is required for formatting the manual. For
+ those without \LaTeX, a formatted version of the manual is supplied, in
+ PostScript form.
+\end{itemize}
+
+The \become\ software is distributed as a gzipped tape archive: saying
+\begin{verbatim}
+ $ gunzip -c become-1.1.tar.gz | tar xvf -
+\end{verbatim}
+will decompress and unpack the source code into a directory
+\path{become-1.1}.
+
+The software must be configured prior to compilation. The author has used
+the Free Software Foundation's Autoconf system which will (with luck)
+configure \become\ correctly for the host platform. The simplest way to do
+this is to change into the unpacked source directory and say
+\begin{verbatim}
+ $ ./configure
+\end{verbatim}
+If you're compiling for several architectures, you can keep the object files
+for each in a separate directory. To do this, create a directory for each
+one, e.g., by saying something like
+\begin{verbatim}
+ $ mkdir linux solaris
+\end{verbatim}
+Then make the appropriate directory current, and run the configure script
+from the parent directory:
+\begin{verbatim}
+ $ cd linux
+ $ ../configure
+\end{verbatim}
+
+Without any arguments, the configure script will attempt to deduce all it
+needs to know about your platform, and it will choose default places to
+install files. You can change the configure script's ideas about where to
+put the files by passing it command line arguments. By default, all of
+\become's files are placed relative to a \emph{prefix} directory (so binaries
+go in \emph{prefix}\path{/bin} and so on). The prefix directory is usually
+\path{/usr/local}, although you can change this by using the
+\texttt{--prefix} option, e.g.,
+\begin{verbatim}
+ $ ./configure --prefix=/usr/local/become-1.1
+\end{verbatim}
+(This will keep all of \become's files in a subdirectory of
+\path{/usr/local}, which you may find makes maintenance easier.)
+
+You can also choose different locations for various types of file. Most
+importantly, \become's configuration files are put into a `system
+configuration' directory, which by default is \emph{prefix}\path{/etc}. You
+can change it using the \texttt{--sysconfdir} option, e.g.,
+\begin{verbatim}
+ $ ./configure --sysconfdir=/etc/become
+\end{verbatim}
+If you're planning to use \become\ in a centralised installation (see
+section~\ref{sec:become.inst-type} on page~\pageref{sec:become.inst-type})
+then the system configuration directory \emph{must not} be on a remote
+filesystem because cryptographic keys are stored in this directory and
+putting them on a remote filesystem will make them visible on the network.
+
+A complete list of options accepted by the configure script may be displayed
+by passing the \texttt{--help} option:
+\begin{verbatim}
+ $ ./configure --help
+\end{verbatim}
+
+You can now build the programs by invoking GNU make. Whether simply typing
+\texttt{make} is sufficient to run GNU make depends on your site: ask around
+if you don't know how to invoke GNU make.
+
+Finally, you can install the various files to their correct directories by
+saying
+\begin{verbatim}
+ $ make install
+\end{verbatim}
+(again, using GNU make, so maybe it's not called `\texttt{make}' at your
+site).
+
+Congratulations: \become\ is now compiled. The easy part is now done.
+
+
+\subsection{Different installation types}
+\label{sec:become.inst-type}
+
+There are two types of installation for \become, and which one you choose
+depends on how you want to maintain the configuration file, which contains
+the rules describing who is allowed to become whom:
+\begin{itemize}
+\item a \emph{standalone} installation, where the configuration is stored
+ locally, and
+\item a \emph{centralised} installation, where the configuration is stored on
+ a central (and trusted) server.
+\end{itemize}
+The difference is basically how you want to maintain the configuration. In
+the standalone case, you have to ensure that the configuration file is copied
+to each participating host each time it gets changed. In the centralised
+case, you only have one copy of the configuration file, and have a different
+problem concerning key distribution.
+
+
+\subsection{The configuration file}
+
+The configuration file for \become is called \path{become.conf}, and it's
+stored in the system configuration directory you set up when compiling the
+program. It defines a set of records, each containing four fields:
+\begin{itemize}
+\item a \emph{from} field, identifying a class of users;
+\item a \emph{to} field, identifying a (possibly different) class of users;
+\item a \emph{commands} field, describing a class of commands; and
+\item a \emph{hosts} field, describing a class of hosts.
+\end{itemize}
+Such a record permits any user in the \emph{from} class to run a command
+contained in the \emph{commands} class as any user in the \emph{to} class, on
+any host in the \emph{hosts} class. If any class fails to match, permission
+is denied.
+
+The configuration file can contain comments, which start with a \lit{\#}
+character and extend to the end of the line; this is the only time when
+newlines are significant in the configuration file.
+
+\subsubsection{Allow records}
+
+A record like the one described above is represented in the configuration
+file by an \emph{allow record}. It has the following syntax:
+\begin{grammar}
+
+<allow> ::= \[[
+ `allow'
+ \[ `[' <host-class> `]' \]
+ <user-class> `->' \[ <user-class> \]
+ \[ `:' <command-class> \]
+ `;'
+\]]
+
+\end{grammar}
+The items \<host-class>, \<user-class> and \<command-class> are all
+\emph{class expressions}. If you omit one of the classes, then it will match
+all requests. So saying
+\begin{verbatim}
+ allow EVILHACKER -> ;
+\end{verbatim}
+allows anyone in the `EVILHACKER' class to become anyone they like,
+everywhere, and do anything.
+
+\subsubsection{Class expressions}
+
+Class expressions allow you to define classes of users, hosts and commands
+conveniently.
+
+All class expressions have the same high-level syntax, and it's fairly easy
+to understand. It looks a little bit like set notation: you can obtain the
+union of two classes using the \lit{|} character (or), take intersections
+using \lit{\&} (and), and subtract classes using \lit{-}. Finally, you can
+list several classes by separating them with commas \lit{,}. The order of
+precedence, from lowest to highest, is \lit{,}, \lit{-}, \lit{|} and
+\lit{\&}.\footnote{
+ Actually, the \lit{,} and \lit{|} operators do exactly the same thing. The
+ only difference is their relative precedence. It probably helps if you
+ think of them as being conceptually different, though.}
+You can override the precedence rules by using parentheses.
+
+The whole syntax looks like this:
+\begin{grammar}
+
+<class-expr> ::= \[[
+ \[ \< <class-minus-expr> \\ `,' \> \] <class-minus-expr>
+ \]]
+
+<class-minus-expr> ::= \[[
+ \[ \< <class-or-expr> \\ `-' \> \] <class-or-expr>
+\]]
+
+<class-or-expr> ::= \[[
+ \[ \< <class-and-expr> \\ `|' \> \] <class-and-expr>
+\]]
+
+<class-and-expr> ::= \[[
+ \[ \< <class-primary> \\ `&' \> \] <class-primary>
+\]]
+
+<class-primary> ::= \[[
+ \( `(' <class-expr> `)' \\ <class-name> \\ <explicit-item> \)
+\]]
+
+\end{grammar}
+
+\subsubsection{Naming classes}
+
+To save repetition, you can give names to classes, using one of the three
+assignment statements:
+\begin{grammar}
+
+<user-assign> ::= \[[ `user' <name> `=' <user-class> `;' \]]
+
+<host-assign> ::= \[[ `host' <name> `=' <host-class> `;' \]]
+
+<command-assign> ::= \[[ `command' <name> `=' <command-class> `;' \]]
+
+\end{grammar}
+
+Classes can be defined in terms of themselves: saying
+\begin{verbatim}
+ user HACKERS = HACKERS | "mdw";
+\end{verbatim}
+says to add `mdw' to the class of hackers, for example. The configuration
+file is read strictly top-to-bottom, and an allow record already given
+doesn't change its meaning just because you later redefine of the classes it
+refers to.
+
+\subsubsection{Naming users, hosts and commands}
+
+Right: you now know how to define classes in terms of other classes, but
+you've got to start somewhere. Each type of class has its own way of
+identifying members.
+\begin{itemize}
+\item A user may be identified either by writing the user's name in double
+ quotes (e.g, \texttt{"mdw"}) or by giving the integer user id (e.g.,
+ \texttt{272}).
+\item A host may be specified by giving, in quotes, either the host's
+ \emph{fully qualified} name (e.g., \texttt{"excessus.hacker.org"}), or its
+ IP~address, (e.g., \texttt{"158.152.170.219"}). Note that the IP~address
+ must be quoted too: this is slightly unusual. Either form may contain
+ wildcards: \lit{?} matches any character, and \lit{*} matches zero or more
+ characters. For example, I can name all hosts at hacker.org by saying
+ \texttt{"*.hacker.org"}. \emergencystretch=10pt
+\item A command may be specified by giving its \emph{full pathname} in quotes
+ (e.g., \texttt{"/sbin/shutdown"}). Again, wildcards can be used to specify
+ lots of commands at the same time.
+\end{itemize}
+
+Also, note that lots of user classes come \emph{predefined}. A class is
+defined for every user, named with the user's name, and containing that user.
+Also, for every group, there is a class, named with the group's name, which
+contains all users who are members of that group. You can redefine these
+classes if you like: they're meant to be a convenience, but if you don't like
+them, you don't have to use them.
+
+A warning is in order: some systems have group names which are also user
+names: in this case, a class is defined containing the named user \emph{and}
+all the members of the group. That's probably not desirable.
+
+
+\subsection{Configuring standalone installations}
+
+That's it, really. Make sure that \become\ can find the configuration file
+on each host. If \become\ can't find a server to talk to (which it can't
+because you haven't configured one) it will parse the local configuration
+file and decide for itself whether to grant the user's request.
+
+If you're only interested in setting up a standalone installation, then
+you're finished, and can get on with doing something interesting.
+Alternatively, read on, and see all the work you don't have to do.
+
+
+\subsection{Keys and random numbers}
+
+Because \become\ uses cryptographic methods for communicating with its
+server, you must set up some encryption keys for it to use. You need to set
+up two files, both in \become's system configuration directory:
+\begin{itemize}
+\item The file \path{become.key} contains \become's `master' key. Someone
+ who knows the master key can fake responses from the server, and grant
+ themselves any privileges they like.
+\item The file \path{become.random} contains a `random number seed' which is
+ used (together with the master key) to generate random numbers (e.g.,
+ session keys). Someone who knows the random numbers can fake responses
+ from the server, and grant themselves any privileges they like. It's
+ difficult to actually predict random numbers given the random number seed
+ file, although it's not a good idea to leave the seed lying around.
+\end{itemize}
+Both of these files should be stored on a local filesystem, and they should
+be readable only by the super-user.
+
+Each of the two files, the key and the random number file, contain a 128-bit
+number, written in hexadecimal. To make the thing more readable, you may
+insert dashes in the number between each chunk of eight digits.\footnote{
+ Actually, you can insert dashes wherever you like in the number, but
+ this is only because the parser is rather primitive. The author recommends
+ that you stick with every eight digits.}
+Here's an example of a possible key file:
+\begin{verbatim}
+ 4fda99b0-fcbd8bcb-d1bcf951-e1ed04c9
+\end{verbatim}
+
+You should generate 128 genuinely random bits for each file. It is
+\emph{not} good enough to use a computer random number generator. A program
+will be supplied later which will examine key timings as a source for random
+numbers. Also, don't use the number printed above. That would be really
+silly.
+
+To help you do this, a program `keygen' is provided. It uses timings of
+keypresses to generate random numbers. To use the program to generate (for
+example) the random number seed file, type
+\begin{verbatim}
+ $ keygen -o become.random
+\end{verbatim}
+Keygen will report the number of bits which still need to be generated. Keep
+typing until it says `done'. The program automatically ensures that its
+output file, if it doesn't already exist, is readable only by its owner. The
+command line arguments to keygen are simple:
+\begin{syntdiag}
+`keygen' \< \[ `--bits' <number> \\ `--output' <file-name> \] \>
+\end{syntdiag}
+If you don't specify a \<number> of bits, a default of 128 random bits are
+generated, which is correct for IDEA keys. If you omit the \<file-name>, the
+random key is written to standard output.
+
+
+\subsection{Setting up the server and clients}
+
+You don't need a separate program to run as a \become\ server: the normal
+\become\ is quite capable of behaving as a server all by itself. However,
+before you start the server up, you need to decide on a port to which it will
+listen. The author uses port 9876 for testing purposes, and there's not much
+reason why you couldn't do the same.
+
+There are three ways you can inform \become\ of your choice of port:
+\begin{itemize}
+\item You can pass the port number on the command line, using the
+ \texttt{--port} option.
+\item You can add a line saying `\syntax{"port" <number> ";"}' to the
+ configuration file.
+\item You can add an entry to your \path{/etc/services} file (or NIS map),
+ binding your chosen port number to the name `become'.\footnote{
+ Actually, \become\ searches for the port using the filename with which
+ it was invoked, so if you call the \become\ binary \path{splat}, then
+ \become will look for a service labelled `splat'.}
+\end{itemize}
+If \become\ still has no idea which port to use, it refuses to start up as a
+server and reports an error message to you.
+
+You can also choose a different key file to use, by writing a line of the
+form `\syntax{"key" <filename> ";"}' in \path{become.conf}. The client won't
+listen to this -- only the server does that.
+
+To make \become\ run as a server, say
+\begin{verbatim}
+ $ become --daemon
+\end{verbatim}
+(or to use an explicit port number, say something like
+\begin{verbatim}
+ $ become --daemon --port=9876
+\end{verbatim}
+replacing \texttt{9876} in the example with your chosen port). You can also
+run the daemon with a different configuration file, by using the
+\texttt{--config-file} option, e.g.,
+\begin{verbatim}
+ $ become --daemon --config-file=/etc/become/server.conf
+\end{verbatim}
+
+Now to configure the clients. All they need is a file saying where to find
+the server. All this contains is a single line of the form
+\begin{syntdiag}
+ <host-name> \[ `:' <port-number> \]
+\end{syntdiag}
+If you omit the port number (or it's otherwise incomprehensible) then
+\become\ looks at the services table (again using the name under which it was
+invoked) to find a port. If it still can't find a port to use, then \become\
+complains.
+
+The server wakes up every five minutes to rescan its configuration and
+encryption key. Thus, it should react fairly quickly to changes to the user
+database or to its configuration. However, you can always force the server
+to refetch its configuration files by sending it a SIGHUP signal. To help
+you do this, the server stores its process id in a file \path{become.pid}
+within its system configuration directory.
+
+
+\subsection{Maintaining \become}
+
+There's not much to it really, apart from updating the configuration file
+when your requirements change.
+
+The only other thing you really ought to do is to periodically change the
+master key. This should be done about once a week, I'd suspect. The
+difficult part is distributing the keys over the network: you don't really
+want to trust the old keys. I'd recommend that you investigate `ssh' for key
+distribution.
+
+
+\subsection{Summary of \become\ configuration}
+
+\subsubsection{Table of the configuration files}
+
+\begin{tabularx}{\textwidth}{@{} >{\ttfamily}l X @{}}
+ \multicolumn{1}{@{}l}{\textbf{File name}} &
+ \multicolumn{1}{l@{}}{\textbf{Contents}} \\
+ become.conf & Main configuration file. See the syntax below for the
+ complete reference \\
+ become.key & Master encryption key. Should contain a 128-bit random
+ number. \\
+ become.pid & Server's process id (so that you can kill it). The server
+ creates this file all by itself. \\
+ become.random & Random number seed for generating session keys. Should
+ also contain a 128-bit random number. Don't be surprised
+ if the number keeps changing -- it's meant to. \\
+ become.server & Tells the \become\ client where to find the server. \\
+\end{tabularx}
+
+\subsubsection{Definitive syntax for \path{become.conf}}
+
+The syntax for \path{become.conf} files is shown below. This mainly reprises
+the syntax shown earlier, but in a different order, and without all the
+explanatory text getting in the way.
+
+Firstly, the lexical grammar is as follows:
+
+\begin{grammar}
+
+<comment> ::= \[[
+ `#' \< \tok{any character other than <new-line>} \> <new-line>
+\]]
+
+<name> ::= \[[
+ \tok{letter or `_'} \< \( \tok{letter or `_'} \\ \tok{digit} \) \>
+\]]
+
+<integer> ::= \[[ \< \tok{digit} \> \]]
+
+<string> ::= \[[
+ `"' \< \( \tok{any character other than `"', <new-line> or `\\'} \\
+ `\\' \tok{any character other than <new-line>} \) \> `"'
+\]]
+
+\end{grammar}
+
+All \<comment>s and whitespace are ignored entirely. What's left is parsed
+as follows:
+
+\begin{grammar}
+
+<become-conf> ::= \[[ \< <statement> \> \]]
+
+<statement> ::= \[[
+ \( \( `user' \\ `command' \\ `host' \) <name> `=' <class-expr> `;' \\
+ `allow' <allow-spec> `;' \\
+ `port' <integer> `;' \\
+ `key' <string> `;' \)
+\]]
+
+<allow-spec> ::= \[[
+ \[ `[' <host-class> `]' \]
+ <user-class> `->' \[ <user-class> \]
+ \[ `:' <command-class> \]
+\]]
+
+<class-expr> ::= \[[
+ \[ \< <class-minus-expr> \\ `,' \> \] <class-minus-expr>
+ \]]
+
+<class-minus-expr> ::= \[[
+ \[ \< <class-or-expr> \\ `-' \> \] <class-or-expr>
+\]]
+
+<class-or-expr> ::= \[[
+ \[ \< <class-and-expr> \\ `|' \> \] <class-and-expr>
+\]]
+
+<class-and-expr> ::= \[[
+ \[ \< <class-primary> \\ `&' \> \] <class-primary>
+\]]
+
+<class-primary> ::= \[[
+ \( `(' <class-expr> `)' \\ <name> \\ <integer> \\ <string> \)
+\]]
+
+\end{grammar}
+
+
+%%%--------------------------------------------------------------------------
+\section{Cryptographic trivia}
+
+
+\subsection{Design requirements}
+
+The way the system works is that the \become\ client program builds a
+\emph{request block} containing all the information needed to decide whether
+the user's request is valid. It then sends this to a server, asking it
+whether this request should be granted. If the server replies `yes', then
+\become\ changes its uid, and runs the user's program.
+
+The really important point is that the client must be able to trust the
+responses it gets from the server: the final decision over whether to grant
+the request lies only with the client. The server doesn't really need to
+worry too much about whether it trusts a request -- it's not going to do
+anything with them anyway except send a reply back.
+
+
+\subsection{Notation}
+
+Some slightly weird mathematical notation is used in the following sections.
+\begin{description}
+\item [$a \xor b$] denotes the exclusive-or (XOR) operation (bitwise addition
+ mod 2).
+\item [$(a, b, c)$] denotes concatenation of the quantities $a$, $b$ and $c$.
+\item [$a[x : y]$] denotes bits $x$ up to $y$ of $a$, including bit~$x$ but
+ \emph{not} bit~$y$. For example, $a[32:64]$ is a 32-bit quantity. The
+ bits are labelled starting from the left at zero, and increasing to the
+ right.
+\item [$E_{k, IV}(a)$] denotes encryption of $a$, using the key $k$ and
+ initialisation vector $IV$.
+\item [$D_{k, IV}(a)$] denotes decryption of $a$, using the key $k$ and
+ initialisation vector $IV$.
+\end{description}
+Encryption is performed using the IDEA algorithm, in 64-bit ciphertext
+feedback mode.
+
+
+\subsection{The actual protocol}
+
+The protocol \become\ uses to communicate with the server is as follows:
+\begin{enumerate}
+
+\item The client and server share a secret key~$k$.
+
+\item The client calculates the following:
+ \begin{description}
+ \item [$F$] is the `from' user id;
+ \item [$T$] is the `to' user id;
+ \item [$C$] is the command the user wishes to execute;
+ \item [$t$] is the current time, as returned from \texttt{time}(2); and
+ \item [$p$] is the client's process id.
+ \end{description}
+ The fields $t$ and~$p$ are to ensure that the client doesn't get confused
+ by replies to the wrong requests.
+
+\item The client generates a session key~$s$ and initialisation vector~$IV$.
+ It then calculates a checksum
+ \[ X = MD5(F, T, C, t, p)[0:32] \]
+ and sends the server a message
+ \[ \bigl(IV, E_{k, IV}(s), E_{s, IV'}(F, T, C, t, p, X)\bigr) \]
+ where $IV'$ is $E_{k, IV}(s)[64:128]$ (i.e., the last block of ciphertext
+ after encrypting the session key, so the whole message is encrypted as one
+ ciphertext feedback job, with a key change part-way).
+
+\item The server decrypts the message, and checks it to make sure it's valid:
+ \begin{itemize}
+ \item It checks that $X$ is the correct checksum.
+ \item It ensures that the difference between $t$ and the true time is
+ acceptable. (The current implementation allows $t$ to be 15 seconds
+ out.)
+ \end{itemize}
+ If either of these checks fails, the request is rejected without
+ acknowledgement.
+
+\item The server decides whether to grant the request. If it gives its
+ permission, it sets $a = 1$; otherwise it sets $a = 0$. It calculates a
+ checksum
+ \[ Y = MD5(t, p, a)[0:32] \]
+ and sends the client a message
+ \[ \bigl(IV'', E_{s, IV''}(t, p, a, Y)\bigr) \]
+ where $IV''$ is the last 64~bits of ciphertext received from the client,
+ continuing the ciphertext feedback again. (Later versions of \become\
+ might use a different method for deciding on the initialisation vector.)
+
+\item The client decrypts the reply, and verifies it:
+ \begin{itemize}
+ \item It checks that $Y$ is a valid checksum.
+ \item It checks that the $t$ and $p$ values received match the ones in the
+ original request.
+ \end{itemize}
+ If either fail to match, the reply is discarded, and the client continues
+ to wait for a valid reply (possibly timing out).
+
+\item The client accepts the reply. If $a = 1$ it changes uid and executes
+ the named process~$C$.
+
+\end{enumerate}
+
+The encryption makes it hard for an attacker to alter the data being
+transmitted in any meaningful way; the 32-bit checksum means that an altered
+message has a $2^{-32}$ probability of not being noticed.
+
+The use of ciphertext feedback mode attempts to prevent chosen-plaintext
+attacks, even though the user can make the client send arbitrary messages.
+
+
+\subsection{The random number generator}
+
+The random number generator is used to generate initialisation vectors and
+session keys for the cryptographic protocol above. The random number
+generator might well change in later versions of \become.
+
+The current implementation maintains an $n$-bit random number seed~$R_i$. It
+generates a 128-bit session key~$s$, an initialisation vector~$IV$ and a new
+seed~$R_{i+1}$.
+\[
+ \begin{eqalign}
+ s &= MD5(R_i, t, p, k) \cr
+ IV &= MD5(R_i, t, p, s)[0:64] \cr
+ R_{i + 1} &= (R_i[n - 128 : n] \xor s, R_i[0 : n - 128]) \cr
+ \end{eqalign}
+\]
+\ifxypic
+ See figure~\ref{fig:become.randgen} for a diagrammatic representation of
+ the generator. It's
+\else
+ This is
+\fi
+really just a weird sort of feedback shift register, generating 128~bits of
+data at a time.
+
+\ifxypic
+\begin{figure}
+
+ \xymatrix{
+
+ }
+
+ \caption{The \become\ random number generator}
+ \label{fig:become.randgen}
+\end{figure}
+\fi
+
+
+The use of the secret key~$k$ helps to ensure that even if the random number
+seed is compromised, an attacker still needs to know $k$ before he can
+predict session keys. Of course, if the attacker knows $k$, he has no need
+to predict session keys: he can just decrypt them from the messages. The use
+of the values $t$ and $p$ attempts to add a small quantity of randomness to
+the seed in each iteration.
+
+
+\subsection{The `keygen' program}
+
+The `keygen' program attempts to take advantage of the variations in time
+between your keystrokes to generate random numbers. It's not perfect. It
+may help a little if you know exactly how it works.
+
+Keygen keeps track of the interval between keypresses. It exclusive-ors
+adjacent interval times together, and strips off leading and trailing
+sequences of one- or zero-bits. What's left is shifted into the accumulator.
+The aim of all this complexity is to measure the variation in key timings,
+and then discard any uninteresting bits from the result.
+
+This method works best on machines with very high-resolution clocks
+(preferably with microsecond granularity), although even on the author's
+Linux machine, which uses a clock with centisecond granularity, the number of
+keystrokes required is acceptable.
+
+
+\subsection{How to break \become's security}
+
+The author can't see any obvious weaknesses in the protocol used. Here are
+some possibilities which might occur to an attacker, though:
+\begin{itemize}
+
+\item Forge a server reply packet and send it to the client. Intercept the
+ request packet and discard it before it reaches the real server. The
+ required contents of the reply packet can be guessed. However, encrypting
+ it requires knowledge of the session key sent by the client. Obtaining
+ this means you need to break the IDEA cipher, which (to the author's
+ knowledge) isn't practical.
+
+\item Send another packet to the server at the same time, altering the sender
+ address so that the server replies to the wrong host or port. This won't
+ work, because the client will attempt to decrypt the fake reply with the
+ wrong session key and will reject the packet when it finds that the
+ checksum is incorrect.
+
+\item Find some other back door into the client host, to become root. Read
+ the secret key file, and use that to decrypt requests and send back
+ replies. If you can already become the super-user, why bother cracking
+ \become?
+
+\item Feed the client program bad input to overflow a fixed-size buffer. The
+ bad input contains executable code which gives the attacker a privileged
+ shell. The author isn't aware of any buffers which might overflow as a
+ result of user-supplied data.
+
+\end{itemize}
+
+The above assumes that \become\ has been set up correctly. The following
+attacks rely on misconfiguration:
+\begin{itemize}
+
+\item Watch new secret keys being transmitted over the network when the
+ administrator replaces them. Now you can decrypt request packets and send
+ back replies. Make sure that the original server's responses are
+ corrupted so that the client rejects them.
+
+\item Watch the client or server reading the secret key from a remote
+ filesystem.
+
+\item Clobber the configuration file when the server re-reads it from a
+ remote filesystem, so that it gives your user account permission to become
+ anyone.
+
+\end{itemize}
+
+
+%%%----- Licencing conditions -----------------------------------------------
+
+\input{gpl}
+
+%%%----- That's all, folks --------------------------------------------------
+
+\end{document}
+
+%%% Local Variables:
+%%% mode: latex
+%%% TeX-master: t
+%%% End:
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: become.c,v 1.1 1997/07/21 13:47:54 mdw Exp $
+ *
+ * Main code for `become'
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: become.c,v $
+ * Revision 1.1 1997/07/21 13:47:54 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Unix headers --- */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <unistd.h>
+
+/* --- Local headers --- */
+
+#include "become.h"
+#include "config.h"
+#include "check.h"
+#include "daemon.h"
+#include "lexer.h"
+#include "mdwopt.h"
+#include "name.h"
+#include "parser.h"
+#include "rule.h"
+#include "utils.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @bc__write@ --- *
+ *
+ * Arguments: @FILE *fp@ = pointer to a stream to write on
+ * @const char *p@ = pointer to a string
+ *
+ * Returns: ---
+ *
+ * Use: Writes the string to the stream, substituting the program
+ * name (as returned by @quis@) for each occurrence of the
+ * character `$'.
+ */
+
+static void bc__write(FILE *fp, const char *p)
+{
+ const char *n = quis();
+ size_t l;
+ size_t nl = strlen(n);
+
+ /* --- Try to be a little efficient --- *
+ *
+ * Gather up non-`$' characters using @strcpn@ and spew them out really
+ * quickly.
+ */
+
+ for (;;) {
+ l = strcspn(p, "$");
+ if (l)
+ fwrite(p, l, 1, fp);
+ p += l;
+ if (!*p)
+ break;
+ fwrite(n, nl, 1, fp);
+ p++;
+ }
+}
+
+/* --- @bc__banner@ --- *
+ *
+ * Arguments: @FILE *fp@ = stream to write on
+ *
+ * Returns: ---
+ *
+ * Use: Writes a banner containing copyright information.
+ */
+
+static void bc__banner(FILE *fp)
+{
+ bc__write(fp, "$ version " VERSION "\n");
+}
+
+/* --- @bc__usage@ --- *
+ *
+ * Arguments: @FILE *fp@ = stream to write on
+ *
+ * Returns: ---
+ *
+ * Use: Writes a terse reminder of command line syntax.
+ */
+
+static void bc__usage(FILE *fp)
+{
+ bc__write(fp,
+ "Usage: \n"
+ " $ -c <shell-command> <user>\n"
+ " $ <user> [<command> [<arguments>]...]\n"
+ " $ -d [-p <port>] [-f <config-file>]\n");
+}
+
+/* --- @bc__help@ --- *
+ *
+ * Arguments: @FILE *fp@ = stream to write on
+ *
+ * Returns: ---
+ *
+ * Use: Displays a help message for this excellent piece of software.
+ */
+
+static void bc__help(FILE *fp)
+{
+ bc__banner(fp);
+ putc('\n', fp);
+ bc__usage(fp);
+ putc('\n', fp);
+ bc__write(fp,
+"The `$' program allows you to run a process as another user.\n"
+"If a command name is given, this is the process executed. If the `-c'\n"
+"option is used, the process is assumed to be `/bin/sh'. If no command is\n"
+"given, your default login shell is used.\n"
+"\n"
+"Your user id, the user id you wish to become, the name of the process\n"
+"you wish to run, and the identity of the current host are looked up to\n"
+"ensure that you have permission to do this.\n"
+"\n"
+"Note that logs are kept of all uses of this program.\n"
+"\n"
+"Options available are:\n"
+"\n"
+"-h, --help Display this help text\n"
+"-v, --version Display the version number of this copy of $\n"
+"-c, --command=CMD Run the (Bourne) shell command CMD\n"
+"-d, --daemon Start up a daemon, to accept requests from clients\n"
+"-p, --port=PORT In daemon mode, listen on PORT\n"
+"-f, --config-file=FILE In daemon mode, read config from FILE\n"
+"--yacc-debug Dump lots of parser diagnostics (boring)\n");
+}
+
+/* --- @main@ --- *
+ *
+ * Arguments: @int argc@ = number of command line arguments
+ * @char *argv[]@ = pointer to the various arguments
+ *
+ * Returns: Zero if successful.
+ *
+ * Use: Allows a user to change UID.
+ */
+
+int main(int argc, char *argv[])
+{
+ char *cmd = 0;
+ static char *shell[] = { "/bin/sh", "-c", 0, 0 };
+ char **todo;
+ request rq;
+ char buff[CMDLEN_MAX];
+ char *conffile = file_RULES;
+ int port = -1;
+
+ enum {
+ f_daemon = 1,
+ f_duff = 2
+ };
+
+ unsigned flags = 0;
+
+ /* --- Set up the program name --- */
+
+ ego(argv[0]);
+
+ /* --- Parse some command line arguments --- */
+
+ for (;;) {
+ int i;
+ struct option opts[] = {
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "command", gFlag_argReq, 0, 'c' },
+ { "daemon", 0, 0, 'd' },
+ { "port", gFlag_argReq, 0, 'p' },
+ { "config-file", gFlag_argReq, 0, 'f' },
+ { "yacc-debug", 0, 0, 'Y' },
+ { 0, 0, 0, 0 }
+ };
+
+ i = mdwopt(argc, argv, "hvc:p:df:", opts, 0, 0, 0);
+ if (i < 0)
+ break;
+
+ switch (i) {
+ case 'h':
+ bc__help(stdout);
+ exit(0);
+ break;
+ case 'v':
+ bc__banner(stdout);
+ exit(0);
+ break;
+ case 'c':
+ cmd = optarg;
+ break;
+ case 'p':
+ port = atoi(optarg);
+ break;
+ case 'd':
+ flags |= f_daemon;
+ break;
+ case 'f':
+ conffile = optarg;
+ break;
+ case 'Y':
+ if (getuid() == geteuid())
+ yydebug = 1;
+ else
+ moan("won't set debugging mode when running setuid");
+ break;
+ case '?':
+ flags |= f_duff;
+ break;
+ }
+ }
+ if (flags & f_duff) {
+ bc__usage(stderr);
+ exit(1);
+ }
+
+ /* --- Switch to daemon mode if requested --- */
+
+ if (flags & f_daemon) {
+ daemon_init(conffile, port);
+ exit(0);
+ }
+
+ /* --- Open a syslog --- */
+
+ openlog(quis(), 0, LOG_AUTH);
+
+ /* --- Pick out the uid --- */
+
+ {
+ const char *u;
+ struct passwd *pw;
+ if (optind >= argc) {
+ bc__usage(stderr);
+ exit(1);
+ }
+ u = argv[optind++];
+ pw = getpwnam(u);
+ if (!pw && isdigit(u[0]))
+ pw = getpwuid(atoi(u));
+ if (!pw)
+ die("unknown user `%s'", u);
+ rq.to = pw->pw_uid;
+ }
+
+ /* --- Fill in the easy bits of the request --- */
+
+ rq.from = getuid();
+
+ /* --- Find the local host address --- */
+
+ {
+ struct utsname u;
+ struct hostent *he;
+ uname(&u);
+ if ((he = gethostbyname(u.nodename)) == 0)
+ die("who am I? (can't resolve `%s')", u.nodename);
+ memcpy(&rq.host, he->h_addr, sizeof(struct in_addr));
+ }
+
+ /* --- Figure out what command to use --- */
+
+ if (cmd) {
+ shell[2] = cmd;
+ todo = shell;
+ } else if (optind < argc) {
+ todo = argv + optind;
+ } else {
+ struct passwd *pw = getpwuid(rq.from);
+ if (!pw)
+ die("who are you? (can't find uid %li in database)", (long)rq.from);
+ shell[0] = pw->pw_shell;
+ shell[1] = 0;
+ todo = shell;
+ }
+
+ /* --- If necessary, resolve the path to the command --- */
+
+ if (!strchr(todo[0], '/')) {
+ char *path;
+ char *p;
+ struct stat st;
+ size_t sz;
+
+ if ((p = getenv("PATH")) == 0)
+ p = "/bin:/usr/bin";
+ sz = strlen(p) + 1;
+ memcpy(path = xmalloc(sz), p, sz);
+
+ for (p = strtok(path, ":"); (p = strtok(0, ":")) != 0; ) {
+
+ /* --- SECURITY: check length of string before copying --- */
+
+ if (strlen(p) + strlen(todo[0]) + 2 > sizeof(buff))
+ continue;
+
+ /* --- Now build the pathname and check it --- */
+
+ sprintf(buff, "%s/%s", p, todo[0]);
+ if (stat(buff, &st) == 0 && /* Check it exists */
+ st.st_mode & 0111 && /* Check it's executable */
+ (st.st_mode & S_IFMT) == S_IFREG) /* Check it's a file */
+ break;
+ }
+
+ if (!p)
+ die("couldn't find `%s' in path", todo[0]);
+ todo[0] = buff;
+ free(path);
+ }
+
+ /* --- Canonicalise the path string, if necessary --- */
+
+ {
+ char b[CMDLEN_MAX];
+ char *p;
+ const char *q;
+
+ /* --- Insert current directory name if path not absolute --- */
+
+ p = b;
+ q = todo[0];
+ if (*q != '/') {
+ if (!getcwd(b, sizeof(b)))
+ die("couldn't read current directory: %s", strerror(errno));
+ p += strlen(p);
+ *p++ = '/';
+ }
+
+ /* --- Now copy over characters from the path string --- */
+
+ while (*q) {
+
+ /* --- SECURITY: check for buffer overflows here --- *
+ *
+ * I write at most one byte per iteration so this is OK. Remember to
+ * allow one for the null byte.
+ */
+
+ if (p >= b + sizeof(b) - 1)
+ die("buffer overflow -- bad things happened");
+
+ /* --- Reduce multiple slashes to just one --- */
+
+ if (*q == '/') {
+ while (*q == '/')
+ q++;
+ *p++ = '/';
+ }
+
+ /* --- Handle dots in filenames --- *
+ *
+ * @p[-1]@ is valid here, because if @*q@ is not a `/' then either
+ * we've just stuck the current directory on the end of the buffer,
+ * or we've just put something else on the end.
+ */
+
+ else if (*q == '.' && p[-1] == '/') {
+
+ /* --- A simple `./' just gets removed --- */
+
+ if (q[1] == 0 || q[1] == '/') {
+ q++;
+ p--;
+ continue;
+ }
+
+ /* --- A `../' needs to be peeled back to the previous `/' --- */
+
+ if (q[1] == '.' && (q[2] == 0 || q[2] == '/')) {
+ q += 2;
+ p--;
+ while (p > b && p[-1] != '/')
+ p--;
+ if (p > b)
+ p--;
+ continue;
+ }
+ } else
+ *p++ = *q++;
+ }
+
+ *p++ = 0;
+ strcpy(rq.cmd, b);
+ }
+
+ /* --- Run the check --- */
+
+ {
+ int a = check(&rq);
+ char from[16], to[16];
+ struct passwd *pw;
+
+ if ((pw = getpwuid(rq.from)) != 0)
+ sprintf(from, "%.15s", pw->pw_name);
+ else
+ sprintf(from, "user %lu", (unsigned long)rq.from);
+
+ if ((pw = getpwuid(rq.to)) != 0)
+ sprintf(to, "%.15s", pw->pw_name);
+ else
+ sprintf(to, "user %lu", (unsigned long)rq.to);
+
+ syslog(LOG_INFO,
+ "permission %s for %s to become %s to run `%s'",
+ a ? "granted" : "denied", from, to, rq.cmd);
+
+ if (!a)
+ die("permission denied");
+ }
+
+ /* --- Now do the job --- */
+
+#ifdef TEST_RIG
+ printf("ok\n");
+ return (0);
+#else
+ if (setuid(rq.to) == -1 || seteuid(rq.to) == -1)
+ die("couldn't set uid: %s", strerror(errno));
+ execv(rq.cmd, todo);
+ die("couldn't exec `%s': %s", rq.cmd, strerror(errno));
+ return (127);
+#endif
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: blowfish-sbox.h,v 1.1 1997/07/21 13:47:54 mdw Exp $
+ *
+ * Blowfish encryption routines
+ *
+ * (c) 1997 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: blowfish-sbox.h,v $
+ * Revision 1.1 1997/07/21 13:47:54 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Blowfish initialisation tables ------------------------------------*/
+
+/* --- These are just digits of %$\pi$% --- */
+
+static const blowfish_key blowfish__init = {
+
+ /* --- P-array of round-specific subkeys --- */
+
+ { 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+ 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+ 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+ 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+ 0x9216d5d9, 0x8979fb1b },
+
+ /* --- First S-box --- */
+
+ { 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+ 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+ 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+ 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+ 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+ 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+ 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+ 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+ 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+ 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+ 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+ 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+ 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+ 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+ 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+ 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+ 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+ 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+ 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+ 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+ 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+ 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+ 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+ 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+ 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+ 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+ 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+ 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+ 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+ 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+ 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+ 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+ 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+ 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+ 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+ 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+ 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+ 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+ 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+ 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+ 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+ 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+ 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+ 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+ 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+ 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+ 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+ 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+ 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+ 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+ 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+ 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+ 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+ 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+ 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+ 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+ 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+ 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+ 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+ 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+ 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+ 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+ 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+ 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a },
+
+ /* --- Second S-box --- */
+
+ { 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+ 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+ 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+ 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+ 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+ 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+ 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+ 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+ 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+ 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+ 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+ 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+ 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+ 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+ 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+ 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+ 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+ 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+ 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+ 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+ 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+ 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+ 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+ 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+ 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+ 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+ 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+ 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+ 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+ 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+ 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+ 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+ 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+ 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+ 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+ 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+ 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+ 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+ 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+ 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+ 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+ 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+ 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+ 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+ 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+ 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+ 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+ 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+ 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+ 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+ 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+ 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+ 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+ 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+ 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+ 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+ 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+ 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+ 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+ 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+ 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+ 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+ 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+ 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 },
+
+ /* --- Third S-box --- */
+
+ { 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+ 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+ 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+ 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+ 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+ 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+ 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+ 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+ 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+ 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+ 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+ 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+ 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+ 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+ 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+ 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+ 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+ 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+ 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+ 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+ 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+ 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+ 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+ 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+ 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+ 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+ 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+ 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+ 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+ 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+ 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+ 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+ 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+ 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+ 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+ 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+ 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+ 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+ 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+ 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+ 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+ 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+ 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+ 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+ 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+ 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+ 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+ 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+ 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+ 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+ 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+ 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+ 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+ 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+ 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+ 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+ 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+ 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+ 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+ 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+ 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+ 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+ 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+ 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 },
+
+ /* --- Fourth and final S-box --- */
+
+ { 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+ 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+ 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+ 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+ 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+ 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+ 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+ 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+ 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+ 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+ 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+ 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+ 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+ 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+ 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+ 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+ 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+ 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+ 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+ 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+ 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+ 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+ 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+ 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+ 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+ 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+ 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+ 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+ 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+ 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+ 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+ 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+ 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+ 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+ 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+ 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+ 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+ 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+ 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+ 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+ 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+ 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+ 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+ 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+ 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+ 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+ 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+ 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+ 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+ 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+ 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+ 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+ 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+ 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+ 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+ 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+ 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+ 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+ 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+ 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+ 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+ 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+ 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+ 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 }
+};
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: blowfish.c,v 1.1 1997/07/21 13:47:53 mdw Exp $
+ *
+ * Blowfish encryption routines
+ *
+ * (c) 1997 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: blowfish.c,v $
+ * Revision 1.1 1997/07/21 13:47:53 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <stdio.h>
+
+/* --- Local headers --- */
+
+#include "config.h"
+#include "blowfish.h"
+#include "utils.h"
+
+/*----- Define the initial S-box values -----------------------------------*/
+
+#include "blowfish-sbox.h"
+
+/*----- Useful macros -----------------------------------------------------*/
+
+/* --- The Blowfish round function --- *
+ *
+ * This is why I like this cipher. The round function is microscopic. And
+ * very fast.
+ */
+
+#define ROUND(L, R, K) \
+ ((L) ^= k->p[K], \
+ (R) ^= ((((k->s0[((L) >> 24) & 0xFF]) + \
+ k->s1[((L) >> 16) & 0xFF]) ^ \
+ k->s2[((L) >> 8) & 0xFF]) + \
+ k->s3[((L) >> 0) & 0xFF]))
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @blowfish_encrypt@ --- *
+ *
+ * Arguments: @const blowfish_key *k@ = pointer to key block
+ * @const void *from@ = block to encrypt from
+ * @void *to@ = block to encrypt to
+ *
+ * Returns: ---
+ *
+ * Use: Encrypts a block using the Blowfish algorithm.
+ */
+
+void blowfish_encrypt(const blowfish_key *k, const void *from, void *to)
+{
+ uint_32 l, r;
+ const unsigned char *f = from;
+ unsigned char *t = to;
+
+ /* --- Extract left and right block halves --- */
+
+ l = load32(f + 0);
+ r = load32(f + 4);
+
+ /* --- Now run the round function on these values --- */
+
+ ROUND(l, r, 0);
+ ROUND(r, l, 1);
+ ROUND(l, r, 2);
+ ROUND(r, l, 3);
+ ROUND(l, r, 4);
+ ROUND(r, l, 5);
+ ROUND(l, r, 6);
+ ROUND(r, l, 7);
+ ROUND(l, r, 8);
+ ROUND(r, l, 9);
+ ROUND(l, r, 10);
+ ROUND(r, l, 11);
+ ROUND(l, r, 12);
+ ROUND(r, l, 13);
+ ROUND(l, r, 14);
+ ROUND(r, l, 15);
+
+ /* --- Final transformation --- */
+
+ l ^= k->p[16];
+ r ^= k->p[17];
+
+ /* --- Store the encrypted value --- */
+
+ store32(t + 0, r);
+ store32(t + 4, l);
+}
+
+/* --- @blowfish_decrypt@ --- *
+ *
+ * Arguments: @const blowfish_key *k@ = pointer to key block
+ * @const void *from@ = block to decrypt from
+ * @void *to@ = block to decrypt to
+ *
+ * Returns: ---
+ *
+ * Use: Decrypts a block using the Blowfish algorithm.
+ */
+
+void blowfish_decrypt(const blowfish_key *k, const void *from, void *to)
+{
+ uint_32 l, r;
+ const unsigned char *f = from;
+ unsigned char *t = to;
+
+ /* --- Extract left and right block halves --- */
+
+ l = load32(f + 0);
+ r = load32(f + 4);
+
+ /* --- Now run the round function on these values --- */
+
+ ROUND(l, r, 17);
+ ROUND(r, l, 16);
+ ROUND(l, r, 15);
+ ROUND(r, l, 14);
+ ROUND(l, r, 13);
+ ROUND(r, l, 12);
+ ROUND(l, r, 11);
+ ROUND(r, l, 10);
+ ROUND(l, r, 9);
+ ROUND(r, l, 8);
+ ROUND(l, r, 7);
+ ROUND(r, l, 6);
+ ROUND(l, r, 5);
+ ROUND(r, l, 4);
+ ROUND(l, r, 3);
+ ROUND(r, l, 2);
+
+ /* --- Final transformation --- */
+
+ l ^= k->p[1];
+ r ^= k->p[0];
+
+ /* --- Store the decrypted value --- */
+
+ store32(t + 0, r);
+ store32(t + 4, l);
+}
+
+/* --- @blowfish__qcrypt@ --- *
+ *
+ * Arguments: @const blowfish_key *k@ = pointer to a key block
+ * @uint_32 *p@ = pointer to block to mangle
+ *
+ * Returns: ---
+ *
+ * Use: Mangles a block using the Blowfish algorithm.
+ */
+
+static void blowfish__qcrypt(blowfish_key *k, uint_32 *p)
+{
+ uint_32 l = p[0], r = p[1];
+
+ /* --- Run the round function --- */
+
+ ROUND(l, r, 0);
+ ROUND(r, l, 1);
+ ROUND(l, r, 2);
+ ROUND(r, l, 3);
+ ROUND(l, r, 4);
+ ROUND(r, l, 5);
+ ROUND(l, r, 6);
+ ROUND(r, l, 7);
+ ROUND(l, r, 8);
+ ROUND(r, l, 9);
+ ROUND(l, r, 10);
+ ROUND(r, l, 11);
+ ROUND(l, r, 12);
+ ROUND(r, l, 13);
+ ROUND(l, r, 14);
+ ROUND(r, l, 15);
+
+ /* --- Output transformation --- */
+
+ l ^= k->p[16];
+ r ^= k->p[17];
+
+ /* --- Store the new values --- */
+
+ p[0] = r;
+ p[1] = l;
+}
+
+/* --- @blowfish__buildKey@ --- *
+ *
+ * Arguments: @blowfish_key *k@ = pointer to a key block to set up
+ *
+ * Returns: ---
+ *
+ * Use: Sets up the P-array and S-boxes once a key has been mixed
+ * into the P-array. Use a local copy of the Blowfish
+ * encryption routine, to avoid penalising the main code too
+ * much with having to veneer onto a general args-in-words
+ * function, and to avoid me messing about with transforming
+ * values backwards and forwards between char arrays and
+ * integers.
+ */
+
+static void blowfish__buildKey(blowfish_key *k)
+{
+ uint_32 b[2] = { 0, 0 };
+ int i;
+
+ /* --- First, run through the P-array --- */
+
+ for (i = 0; i < 18; i += 2) {
+ blowfish__qcrypt(k, b);
+ k->p[i] = b[0];
+ k->p[i + 1] = b[1];
+ }
+
+ /* --- Now do the S-boxes --- */
+
+ for (i = 0; i < 256; i += 2) {
+ blowfish__qcrypt(k, b);
+ k->s0[i] = b[0];
+ k->s0[i + 1] = b[1];
+ }
+
+ for (i = 0; i < 256; i += 2) {
+ blowfish__qcrypt(k, b);
+ k->s1[i] = b[0];
+ k->s1[i + 1] = b[1];
+ }
+
+ for (i = 0; i < 256; i += 2) {
+ blowfish__qcrypt(k, b);
+ k->s2[i] = b[0];
+ k->s2[i + 1] = b[1];
+ }
+
+ for (i = 0; i < 256; i += 2) {
+ blowfish__qcrypt(k, b);
+ k->s3[i] = b[0];
+ k->s3[i + 1] = b[1];
+ }
+}
+
+/* --- @blowfish_setKey@ --- *
+ *
+ * Arguments: @blowfish_key *kb@ = pointer to key block to fill
+ * @void *k@ = pointer to key data
+ * @size_t sz@ = length of data in bytes
+ *
+ * Returns: ---
+ *
+ * Use: Expands a key which isn't represented as a number of whole
+ * words. This is a nonstandard extension, although it can be
+ * used to support 40-bit keys, which some governments might
+ * find more palatable than 160-bit (or 448-bit!) keys.
+ */
+
+void blowfish_setKey(blowfish_key *kb, const void *k, size_t sz)
+{
+ int i, j, l;
+ const unsigned char *p = k;
+ uint_32 a;
+
+ memcpy(kb, &blowfish__init, sizeof(blowfish__init));
+
+ j = 0;
+ for (i = 0; i < 18; i++) {
+ a = 0;
+ for (l = 0; l < 4; l++) {
+ a = (a << 8) | p[j];
+ j++;
+ if (j >= sz)
+ j = 0;
+ }
+ kb->p[i] ^= a;
+ }
+
+ blowfish__buildKey(kb);
+}
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+int main(void)
+{
+ /* --- Stage one: ECB tests --- */
+
+ {
+ static struct {
+ uint_32 k[2];
+ uint_32 p[2];
+ uint_32 c[2];
+ } table[] = {
+ { { 0x00000000u, 0x00000000u },
+ { 0x00000000u, 0x00000000u },
+ { 0x4EF99745u, 0x6198DD78u } },
+
+ { { 0xFFFFFFFFu, 0xFFFFFFFFu },
+ { 0xFFFFFFFFu, 0xFFFFFFFFu },
+ { 0x51866FD5u, 0xB85ECB8Au } },
+
+ { { 0x30000000u, 0x00000000u },
+ { 0x10000000u, 0x00000001u },
+ { 0x7D856F9Au, 0x613063F2u } },
+
+ { { 0x11111111u, 0x11111111u },
+ { 0x11111111u, 0x11111111u },
+ { 0x2466DD87u, 0x8B963C9Du } },
+
+ { { 0x01234567u, 0x89ABCDEFu },
+ { 0x11111111u, 0x11111111u },
+ { 0x61F9C380u, 0x2281B096u } },
+
+ { { 0x11111111u, 0x11111111u },
+ { 0x01234567u, 0x89ABCDEFu },
+ { 0x7D0CC630u, 0xAFDA1EC7u } },
+
+ { { 0x00000000u, 0x00000000u },
+ { 0x00000000u, 0x00000000u },
+ { 0x4EF99745u, 0x6198DD78u } },
+
+ { { 0xFEDCBA98u, 0x76543210u },
+ { 0x01234567u, 0x89ABCDEFu },
+ { 0x0ACEAB0Fu, 0xC6A0A28Du } },
+
+ { { 0x7CA11045u, 0x4A1A6E57u },
+ { 0x01A1D6D0u, 0x39776742u },
+ { 0x59C68245u, 0xEB05282Bu } },
+
+ { { 0x0131D961u, 0x9DC1376Eu },
+ { 0x5CD54CA8u, 0x3DEF57DAu },
+ { 0xB1B8CC0Bu, 0x250F09A0u } },
+
+ { { 0x07A1133Eu, 0x4A0B2686u },
+ { 0x0248D438u, 0x06F67172u },
+ { 0x1730E577u, 0x8BEA1DA4u } },
+
+ { { 0x3849674Cu, 0x2602319Eu },
+ { 0x51454B58u, 0x2DDF440Au },
+ { 0xA25E7856u, 0xCF2651EBu } },
+
+ { { 0x04B915BAu, 0x43FEB5B6u },
+ { 0x42FD4430u, 0x59577FA2u },
+ { 0x353882B1u, 0x09CE8F1Au } },
+
+ { { 0x0113B970u, 0xFD34F2CEu },
+ { 0x059B5E08u, 0x51CF143Au },
+ { 0x48F4D088u, 0x4C379918u } },
+
+ { { 0x0170F175u, 0x468FB5E6u },
+ { 0x0756D8E0u, 0x774761D2u },
+ { 0x432193B7u, 0x8951FC98u } },
+
+ { { 0x43297FADu, 0x38E373FEu },
+ { 0x762514B8u, 0x29BF486Au },
+ { 0x13F04154u, 0xD69D1AE5u } },
+
+ { { 0x07A71370u, 0x45DA2A16u },
+ { 0x3BDD1190u, 0x49372802u },
+ { 0x2EEDDA93u, 0xFFD39C79u } },
+
+ { { 0x04689104u, 0xC2FD3B2Fu },
+ { 0x26955F68u, 0x35AF609Au },
+ { 0xD887E039u, 0x3C2DA6E3u } },
+
+ { { 0x37D06BB5u, 0x16CB7546u },
+ { 0x164D5E40u, 0x4F275232u },
+ { 0x5F99D04Fu, 0x5B163969u } },
+
+ { { 0x1F08260Du, 0x1AC2465Eu },
+ { 0x6B056E18u, 0x759F5CCAu },
+ { 0x4A057A3Bu, 0x24D3977Bu } },
+
+ { { 0x58402364u, 0x1ABA6176u },
+ { 0x004BD6EFu, 0x09176062u },
+ { 0x452031C1u, 0xE4FADA8Eu } },
+
+ { { 0x02581616u, 0x4629B007u },
+ { 0x480D3900u, 0x6EE762F2u },
+ { 0x7555AE39u, 0xF59B87BDu } },
+
+ { { 0x49793EBCu, 0x79B3258Fu },
+ { 0x437540C8u, 0x698F3CFAu },
+ { 0x53C55F9Cu, 0xB49FC019u } },
+
+ { { 0x4FB05E15u, 0x15AB73A7u },
+ { 0x072D43A0u, 0x77075292u },
+ { 0x7A8E7BFAu, 0x937E89A3u } },
+
+ { { 0x49E95D6Du, 0x4CA229BFu },
+ { 0x02FE5577u, 0x8117F12Au },
+ { 0xCF9C5D7Au, 0x4986ADB5u } },
+
+ { { 0x018310DCu, 0x409B26D6u },
+ { 0x1D9D5C50u, 0x18F728C2u },
+ { 0xD1ABB290u, 0x658BC778u } },
+
+ { { 0x1C587F1Cu, 0x13924FEFu },
+ { 0x30553228u, 0x6D6F295Au },
+ { 0x55CB3774u, 0xD13EF201u } },
+
+ { { 0x01010101u, 0x01010101u },
+ { 0x01234567u, 0x89ABCDEFu },
+ { 0xFA34EC48u, 0x47B268B2u } },
+
+ { { 0x1F1F1F1Fu, 0x0E0E0E0Eu },
+ { 0x01234567u, 0x89ABCDEFu },
+ { 0xA7907951u, 0x08EA3CAEu } },
+
+ { { 0xE0FEE0FEu, 0xF1FEF1FEu },
+ { 0x01234567u, 0x89ABCDEFu },
+ { 0xC39E072Du, 0x9FAC631Du } },
+
+ { { 0x00000000u, 0x00000000u },
+ { 0xFFFFFFFFu, 0xFFFFFFFFu },
+ { 0x014933E0u, 0xCDAFF6E4u } },
+
+ { { 0xFFFFFFFFu, 0xFFFFFFFFu },
+ { 0x00000000u, 0x00000000u },
+ { 0xF21E9A77u, 0xB71C49BCu } },
+
+ { { 0x01234567u, 0x89ABCDEFu },
+ { 0x00000000u, 0x00000000u },
+ { 0x24594688u, 0x5754369Au } },
+
+ { { 0xFEDCBA98u, 0x76543210u },
+ { 0xFFFFFFFFu, 0xFFFFFFFFu },
+ { 0x6B5C5A9Cu, 0x5D9E0A5Au } }
+ };
+
+ int f = 1;
+ int i;
+
+ printf("*** stage one: ");
+ fflush(stdout);
+
+ for (i = 0; i < sizeof(table) / sizeof(table[0]); i++) {
+ char kb[8], p[8], c[8];
+ blowfish_key k;
+
+ store32(kb + 0, table[i].k[0]);
+ store32(kb + 4, table[i].k[1]);
+ blowfish_setKey(&k, kb, 8);
+
+ store32(p + 0, table[i].p[0]);
+ store32(p + 4, table[i].p[1]);
+ blowfish_encrypt(&k, p, c);
+
+ if (load32(c + 0) != table[i].c[0] ||
+ load32(c + 4) != table[i].c[1]) {
+ printf("\n"
+ "!!! bad encryption\n"
+ " key = %08lx-%08lx\n"
+ " plaintext = %08lx-%08lx\n"
+ " expected ciphertext = %08lx-%08lx\n"
+ " calculated ciphertext = %08lx-%08lx\n",
+ (unsigned long)table[i].k[0],
+ (unsigned long)table[i].k[1],
+ (unsigned long)table[i].p[0],
+ (unsigned long)table[i].p[1],
+ (unsigned long)table[i].c[0],
+ (unsigned long)table[i].c[1],
+ (unsigned long)load32(c + 0),
+ (unsigned long)load32(c + 4));
+ f = 0;
+ }
+
+ blowfish_decrypt(&k, c, p);
+ if (load32(p + 0) != table[i].p[0] ||
+ load32(p + 4) != table[i].p[1]) {
+ printf("\n"
+ "!!! bad decryption\n"
+ " key = %08lx-%08lx\n"
+ " ciphertext = %08lx-%08lx\n"
+ " expected plaintext = %08lx-%08lx\n"
+ " calculated plaintext = %08lx-%08lx\n",
+ (unsigned long)table[i].k[0],
+ (unsigned long)table[i].k[1],
+ (unsigned long)table[i].c[0],
+ (unsigned long)table[i].c[1],
+ (unsigned long)table[i].p[0],
+ (unsigned long)table[i].p[1],
+ (unsigned long)load32(p + 0),
+ (unsigned long)load32(p + 4));
+ f = 0;
+ }
+
+ putchar('.');
+ fflush(stdout);
+ }
+ putchar('\n');
+ if (f)
+ printf("*** stage one ok\n");
+ }
+
+ /* --- Stage 2: key scheduling --- */
+
+ {
+ static struct {
+ uint_32 c[2];
+ } table[] = {
+ {{ 0xF9AD597Cu, 0x49DB005Eu }},
+ {{ 0xE91D21C1u, 0xD961A6D6u }},
+ {{ 0xE9C2B70Au, 0x1BC65CF3u }},
+ {{ 0xBE1E6394u, 0x08640F05u }},
+ {{ 0xB39E4448u, 0x1BDB1E6Eu }},
+ {{ 0x9457AA83u, 0xB1928C0Du }},
+ {{ 0x8BB77032u, 0xF960629Du }},
+ {{ 0xE87A244Eu, 0x2CC85E82u }},
+ {{ 0x15750E7Au, 0x4F4EC577u }},
+ {{ 0x122BA70Bu, 0x3AB64AE0u }},
+ {{ 0x3A833C9Au, 0xFFC537F6u }},
+ {{ 0x9409DA87u, 0xA90F6BF2u }},
+ {{ 0x884F8062u, 0x5060B8B4u }},
+ {{ 0x1F85031Cu, 0x19E11968u }},
+ {{ 0x79D9373Au, 0x714CA34Fu }},
+ {{ 0x93142887u, 0xEE3BE15Cu }},
+ {{ 0x03429E83u, 0x8CE2D14Bu }},
+ {{ 0xA4299E27u, 0x469FF67Bu }},
+ {{ 0xAFD5AED1u, 0xC1BC96A8u }},
+ {{ 0x10851C0Eu, 0x3858DA9Fu }},
+ {{ 0xE6F51ED7u, 0x9B9DB21Fu }},
+ {{ 0x64A6E14Au, 0xFD36B46Fu }},
+ {{ 0x80C7D7D4u, 0x5A5479ADu }},
+ {{ 0x05044B62u, 0xFA52D080u }},
+ };
+
+ unsigned char kk[] = {
+ 0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87,
+ 0x78, 0x69, 0x5A, 0x4B, 0x3C, 0x2D, 0x1E, 0x0F,
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
+ };
+
+ int i;
+ int f = 1;
+
+ printf("*** stage two: ");
+ fflush(stdout);
+
+ for (i = 0; i < sizeof(kk); i++) {
+ blowfish_key k;
+ unsigned char p[8] = { 0xFE, 0xDC, 0xBA, 0x98,
+ 0x76, 0x54, 0x32, 0x10 };
+
+ blowfish_setKey(&k, kk, i + 1);
+ blowfish_encrypt(&k, p, p);
+
+ if (load32(p + 0) != table[i].c[0] ||
+ load32(p + 4) != table[i].c[1]) {
+ printf("!!! bad encryption\n"
+ " key length = %i\n"
+ " expected = %08lx-%08lx\n"
+ " calculated = %08lx-%08lx\n",
+ i + 1,
+ (unsigned long)table[i].c[0],
+ (unsigned long)table[i].c[1],
+ (unsigned long)load32(p + 0),
+ (unsigned long)load32(p + 4));
+ f = 0;
+ }
+
+ putchar('.');
+ fflush(stdout);
+ }
+
+ putchar('\n');
+
+ if (f)
+ printf("*** stage two ok\n");
+ }
+
+ return (0);
+
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: blowfish.h,v 1.1 1997/07/21 13:47:53 mdw Exp $
+ *
+ * Blowfish encryption routines
+ *
+ * (c) 1997 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: blowfish.h,v $
+ * Revision 1.1 1997/07/21 13:47:53 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef BLOWFISH_H
+#define BLOWFISH_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Type definitions --------------------------------------------------*/
+
+/* --- A blowfish expanded key --- */
+
+typedef struct blowfish_key {
+ uint_32 p[18];
+ uint_32 s0[256];
+ uint_32 s1[256];
+ uint_32 s2[256];
+ uint_32 s3[256];
+} blowfish_key;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @blowfish_encrypt@ --- *
+ *
+ * Arguments: @const blowfish_key *k@ = pointer to key block
+ * @const voic *from@ = block to encrypt from
+ * @void *to@ = block to encrypt to
+ *
+ * Returns: ---
+ *
+ * Use: Encrypts a block using the Blowfish algorithm.
+ */
+
+extern void blowfish_encrypt(const blowfish_key */*k*/,
+ const void */*from*/, void */*to*/);
+
+
+/* --- @blowfish_decrypt@ --- *
+ *
+ * Arguments: @const blowfish_key *k@ = pointer to key block
+ * @const void *from@ = block to decrypt from
+ * @void *to@ = block to decrypt to
+ *
+ * Returns: ---
+ *
+ * Use: Decrypts a block using the Blowfish algorithm.
+ */
+
+extern void blowfish_decrypt(const blowfish_key */*k*/,
+ const void */*from*/, void */*to*/);
+
+/* --- @blowfish_setKey@ --- *
+ *
+ * Arguments: @blowfish_key *kb@ = pointer to key block to fill
+ * @void *k@ = pointer to key data
+ * @size_t sz@ = length of data in bytes
+ *
+ * Returns: ---
+ *
+ * Use: Expands a key which isn't represented as a number of whole
+ * words. This is a nonstandard extension, although it can be
+ * used to support 40-bit keys, which some governments might
+ * find more palatable than 160-bit (or 448-bit!) keys.
+ */
+
+extern void blowfish_setKey(blowfish_key */*kb*/,
+ const void */*k*/, size_t /*sz*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: check.c,v 1.1 1997/07/21 13:47:53 mdw Exp $
+ *
+ * Check validity of requests
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: check.c,v $
+ * Revision 1.1 1997/07/21 13:47:53 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+/* --- Unix headers --- */
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <unistd.h>
+
+/* --- Local headers --- */
+
+#include "become.h"
+#include "config.h"
+#include "crypt.h"
+#include "idea.h"
+#include "lexer.h"
+#include "name.h"
+#include "rule.h"
+#include "parser.h"
+#include "tx.h"
+#include "utils.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @check__client@ --- *
+ *
+ * Arguments: @request *rq@ = pointer to request buffer
+ * @const char *serv@ = pointer to the server
+ * @int port@ = port number to use
+ *
+ * Returns: Nonzero if OK, zero if forbidden
+ *
+ * Use: Contacts a server to decide whether the request is OK.
+ */
+
+static int check__client(request *rq, const char *serv, int port)
+{
+ int fd;
+ struct sockaddr_in ssin;
+ unsigned char crq[crq_size];
+ unsigned char k[IDEA_KEYSIZE];
+ unsigned char sk[IDEA_KEYSIZE];
+ time_t t;
+ pid_t pid;
+
+ /* --- Create my socket --- */
+
+ if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
+ die("couldn't create socket: %s", strerror(errno));
+
+ /* --- Bind myself to some address --- */
+
+ {
+ struct sockaddr_in sin;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = 0;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ die("couldn't bind socket to address: %s", strerror(errno));
+ }
+
+ /* --- Find the server's address --- */
+
+ {
+ struct hostent *he;
+
+ /* --- Resolve the server's name --- */
+
+ if ((he = gethostbyname(serv)) == 0)
+ die("couldn't find server host `%s'", serv);
+
+ /* --- Build the address block --- */
+
+ ssin.sin_family = AF_INET;
+ ssin.sin_port = htons(port);
+ memcpy(&ssin.sin_addr, he->h_addr, sizeof(struct in_addr));
+ }
+
+ /* --- Read in the encryption key --- */
+
+ {
+ FILE *fp;
+
+ if ((fp = fopen(file_KEY, "r")) == 0) {
+ die("couldn't open key file `%s': %s", file_KEY,
+ strerror(errno));
+ }
+ tx_getBits(k, 128, fp);
+ }
+
+ /* --- Now build a request packet --- */
+
+ t = time(0);
+ pid = getpid();
+ crypt_packRequest(rq, crq, t, pid, k, sk);
+
+ /* --- Send the packet to the server --- */
+
+ if (sendto(fd, (char *)crq, sizeof(crq), 0,
+ (struct sockaddr *)&ssin, sizeof(ssin)) < 0) {
+ burn(k);
+ die("couldn't send request to server: %s", strerror(errno));
+ }
+ burn(k);
+
+ /* --- Now wait for a reply --- */
+
+ {
+ fd_set fds;
+ struct timeval when, now, tv;
+
+ gettimeofday(&when, 0);
+ when.tv_sec += 10;
+
+ for (;;) {
+ int i;
+
+ /* --- Sort out when to return --- */
+
+ gettimeofday(&now, 0);
+ if (now.tv_usec > when.tv_usec) {
+ now.tv_usec -= 1000000;
+ now.tv_sec += 1;
+ }
+ tv.tv_sec = when.tv_sec - now.tv_sec;
+ tv.tv_usec = when.tv_usec - now.tv_usec;
+
+ /* --- Sort out file descriptors to watch --- */
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ /* --- Wait for them --- */
+
+ i = select(FD_SETSIZE, &fds, 0, 0, &tv);
+ if (i == 0)
+ die("no answer from server");
+ if (i < 0)
+ die("error waiting for reply: %s", strerror(errno));
+
+ /* --- A reply should be waiting now --- */
+
+ {
+ struct sockaddr_in sin;
+ int slen = sizeof(sin);
+ unsigned char buff[256];
+ int answer;
+
+ /* --- Read the reply data --- */
+
+ if (recvfrom(fd, (char *)buff, sizeof(buff), 0,
+ (struct sockaddr *)&sin, &slen) < 0)
+ die("error reading server's reply: %s", strerror(errno));
+
+ /* --- Verify the sender --- */
+
+ if (sin.sin_addr.s_addr != ssin.sin_addr.s_addr ||
+ sin.sin_port != ssin.sin_port)
+ continue;
+
+ /* --- Unpack and verify the response --- */
+
+ answer = crypt_unpackReply(buff, sk, t, pid);
+ if (answer < 0)
+ continue;
+ return (answer);
+ }
+ }
+ }
+
+ die("internal error: can't get here in check_client");
+ return (0);
+}
+
+/* --- @check@ --- *
+ *
+ * Arguments: @request *rq@ = pointer to request buffer
+ *
+ * Returns: Nonzero if OK, zero if forbidden
+ *
+ * Use: Checks to see if the request is acceptable.
+ */
+
+int check(request *rq)
+{
+ FILE *fp;
+
+ /* --- Check if we need to talk to a server --- */
+
+ if ((fp = fopen(file_SERVER, "r")) != 0) {
+ char buff[64];
+ int port;
+ int ch;
+
+ if (fscanf(fp, " %63[^: \t] ", buff) < 1)
+ die("error in `%s'", file_SERVER);
+ ch = getc(fp);
+ if (ch == ':') {
+ char b[64];
+ struct servent *se;
+
+ fscanf(fp, "%s", b);
+ if ((se = getservbyname(b, 0)) != 0)
+ port = ntohs(se->s_port);
+ else if ((port = atoi(b)) == 0)
+ die("error in `%s'", file_SERVER);
+ } else {
+ struct servent *se;
+
+ if ((se = getservbyname(quis(), "udp")) == 0)
+ die("no idea which port to use");
+ port = ntohs(se->s_port);
+ }
+ fclose(fp);
+ return (check__client(rq, buff, port));
+ }
+
+ /* --- Read the configuration in and go --- */
+
+ if ((fp = fopen(file_RULES, "r")) == 0) {
+ die("couldn't read configuration file `%s': %s",
+ file_RULES, strerror(errno));
+ }
+
+ name_init();
+ rule_init();
+ lexer_scan(fp);
+ yyparse();
+
+ return (rule_check(rq));
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: check.h,v 1.1 1997/07/21 13:47:52 mdw Exp $
+ *
+ * Check validity of requests
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: check.h,v $
+ * Revision 1.1 1997/07/21 13:47:52 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef CHECK_H
+#define CHECK_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#ifndef BECOME_H
+# include "become.h"
+#endif
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @check@ --- *
+ *
+ * Arguments: @request *rq@ = pointer to request buffer
+ *
+ * Returns: Nonzero if OK, zero if forbidden
+ *
+ * Use: Checks to see if the request is acceptable.
+ */
+
+extern int check(request */*rq*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
+
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: class.c,v 1.1 1997/07/21 13:47:52 mdw Exp $
+ *
+ * Handling classes of things nicely
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: class.c,v $
+ * Revision 1.1 1997/07/21 13:47:52 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Unix headers --- */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <netdb.h>
+
+/* --- Local headers --- */
+
+#include "class.h"
+#include "set.h"
+#include "sym.h"
+#include "utils.h"
+
+/*----- Global variables --------------------------------------------------*/
+
+static classdef class__all = { clType_all, -1, 0 };
+classdef *class_all = &class__all;
+
+/*----- Wildcard matching -------------------------------------------------*/
+
+/* --- @class__wildMatch@ --- *
+ *
+ * Arguments: @const char *pat@ = pointer to pattern string
+ * @const char *p@ = pointer to target string
+ *
+ * Returns: Zero if no match, nonzero if match.
+ *
+ * Use: Wildcard-matches the pattern against the target string.
+ */
+
+static int class__wildMatch(const char *pat, const char *p)
+{
+ for (;;) {
+ if (*pat == 0 && *p == 0)
+ return (42); /* For sadism's sake */
+ else if (*pat == '*') {
+ while (*pat == '*')
+ pat++;
+ do {
+ if (class__wildMatch(pat, p))
+ return (27); /* Nyahaha */
+ p++;
+ } while (*p);
+ return (0);
+ } else if (*pat == '?' || *pat == *p)
+ p++, pat++;
+ else
+ return (0);
+ }
+}
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @class_create@ --- *
+ *
+ * Arguments: @unsigned type@ = type of the class to create
+ * @sym_table *t@ = pointer to symbol table which stores info
+ *
+ * Returns: Pointer to a @classdef@ which maintains the class's info.
+ *
+ * Use: Creates a new class definition. The new definition has one
+ * reference attached to it (which is the one you get returned).
+ */
+
+classdef *class_create(unsigned type, sym_table *t)
+{
+ classdef *c = xmalloc(sizeof(*c));
+ c->type = type;
+ c->ref = 1;
+ c->t = t;
+ return (c);
+}
+
+/* --- @class_inc@ --- *
+ *
+ * Arguments: @classdef *c@ = pointer to a class block
+ *
+ * Returns: ---
+ *
+ * Use: Adds a reference to the class definition.
+ */
+
+void class_inc(classdef *c)
+{
+ if (c != class_all)
+ c->ref++;
+}
+
+/* --- @class_dec@ --- *
+ *
+ * Arguments: @classdef *c@ = pointer to a class block
+ *
+ * Returns: ---
+ *
+ * Use: Removes a reference to a class block.
+ */
+
+void class_dec(classdef *c)
+{
+ if (c != class_all && !--c->ref) {
+ sym_destroyTable(c->t);
+ free(c);
+ }
+}
+
+/* --- @class_userMatch@ --- *
+ *
+ * Arguments: @classdef *c@ = pointer to a class block
+ * @int u@ = uid number to check against
+ *
+ * Returns: Zero if no match, nonzero if it matched.
+ *
+ * Use: Looks to see if a user is in a group.
+ */
+
+int class_userMatch(classdef *c, int u)
+{
+ if (~c->type & clType_user)
+ return (0);
+ else if (c == class_all)
+ return (1);
+ else
+ return (sym_find(c->t, (char *)&u, sizeof(u), 0, 0) != 0);
+}
+
+/* --- @class_commandMatch@ --- *
+ *
+ * Arguments: @classdef *c@ = pointer to a class block
+ * @const char *p@ = pointer to command string to match
+ *
+ * Returns: Zero for no match, nonzero if it matched.
+ *
+ * Use: Tries to match a string against the wildcard patterns in the
+ * given class. Note that this involves examining all the
+ * items, so it's not too quick. Then again, you don't have
+ * big command classes, do you...?
+ */
+
+int class_commandMatch(classdef *c, const char *p)
+{
+ sym_iter i;
+ sym_base *s;
+
+ if (~c->type & clType_command)
+ return (0);
+ else if (c == class_all)
+ return (1);
+ else {
+ for (sym_createIter(&i, c->t); (s = sym_next(&i)) != 0; ) {
+ if (class__wildMatch(s->name, p))
+ return (1);
+ }
+ return (0);
+ }
+}
+
+/* --- @class_hostMatch@ --- *
+ *
+ * Arguments: @classdef *c@ = pointer to class block
+ * @struct in_addr addr@ = IP address to match
+ *
+ * Returns: Zero for no match, nonzero if it matched.
+ *
+ * Use: Tries to match an IP address against the patterns and masks
+ * in the given class.
+ */
+
+int class_hostMatch(classdef *c, struct in_addr addr)
+{
+ sym_iter i;
+ sym_base *s;
+ const char *a;
+ struct hostent *he;
+ char **p;
+
+ if (~c->type & clType_host)
+ return (0);
+ else if (c == class_all)
+ return (1);
+ else {
+
+ /* --- Get the dotted-quad and other names for the address --- */
+
+ if ((a = inet_ntoa(addr)) == 0)
+ return (0);
+ if ((he = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == 0)
+ return (0);
+
+ /* --- Now search the list for a match --- *
+ *
+ * This is almost infinitely slow, I'm afraid.
+ */
+
+ for (sym_createIter(&i, c->t); (s = sym_next(&i)) != 0; ) {
+
+ /* --- Check the dotted-quad name first --- */
+
+ if (class__wildMatch(s->name, a))
+ return (1);
+
+ /* --- Now try the host's main name --- */
+
+ if (class__wildMatch(s->name, he->h_name))
+ return (1);
+
+ /* --- Now go through all the names --- */
+
+ for (p = he->h_aliases; *p; p++) {
+ if (class__wildMatch(s->name, *p))
+ return (1);
+ }
+ }
+ return (0);
+ }
+}
+
+/* --- @class_dump@ --- *
+ *
+ * Arguments: @classdef *c@ = pointer to a class block
+ * @FILE *fp@ = pointer to a stream object
+ *
+ * Returns: ---
+ *
+ * Use: Dumps the contents of a class to a stream.
+ */
+
+void class_dump(classdef *c, FILE *fp)
+{
+ sym_iter i;
+ sym_base *s;
+
+ /* --- Write a little header block --- */
+
+ {
+ const char *typetbl[] = {
+ "bad class",
+ "user",
+ "command",
+ "user/command",
+ "host",
+ "host/user",
+ "host/command",
+ "all",
+ };
+
+ fprintf(fp,
+ "*** Type: %s\n"
+ "*** Refs: %u\n"
+ "*** \n"
+ "*** Items:\n",
+ c->type < clType__limit ? typetbl[c->type] : "bad class",
+ c->ref);
+ }
+
+ /* --- Dump the table --- */
+
+ if (c->type != clType_all) {
+ for (sym_createIter(&i, c->t); (s = sym_next(&i)) != 0; ) {
+ switch (c->type) {
+ case clType_user:
+ fprintf(fp, "*** %i\n", *(int *)s->name);
+ break;
+ case clType_command:
+ case clType_host:
+ fputs("*** ", fp);
+ fwrite(s->name, s->len, 1, fp);
+ putc('\n', fp);
+ break;
+ }
+ }
+ }
+
+ putc('\n', fp);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: class.h,v 1.1 1997/07/21 13:47:52 mdw Exp $
+ *
+ * Handling classes of things nicely
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: class.h,v $
+ * Revision 1.1 1997/07/21 13:47:52 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef CLASS_H
+#define CLASS_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifndef SYM_H
+# include "sym.h"
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* --- Class types --- */
+
+enum {
+ clType_user = 1, /* Class of users */
+ clType_command = 2, /* Class of command strings */
+ clType_host = 4, /* Class of host names */
+ clType_all = 7, /* All of the above (maintain me) */
+ clType__limit /* First undefined class type */
+};
+
+/* --- Class block --- */
+
+typedef struct classdef {
+ unsigned type; /* Type of this class */
+ unsigned ref; /* Reference count */
+ sym_table *t; /* Symbol table for this class */
+} classdef;
+
+/*----- Global variables --------------------------------------------------*/
+
+extern classdef *class_all; /* The match-everything class */
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @class_create@ --- *
+ *
+ * Arguments: @unsigned type@ = type of the class to create
+ * @sym_table *t@ = pointer to symbol table which stores info
+ *
+ * Returns: Pointer to a @classdef@ which maintains the class's info.
+ *
+ * Use: Creates a new class definition. The new definition has one
+ * reference attached to it (which is the one you get returned).
+ */
+
+extern classdef *class_create(unsigned /*type*/, sym_table */*t*/);
+
+/* --- @class_inc@ --- *
+ *
+ * Arguments: @classdef *c@ = pointer to a class block
+ *
+ * Returns: ---
+ *
+ * Use: Adds a reference to the class definition.
+ */
+
+extern void class_inc(classdef */*c*/);
+
+/* --- @class_dec@ --- *
+ *
+ * Arguments: @classdef *c@ = pointer to a class block
+ *
+ * Returns: ---
+ *
+ * Use: Removes a reference to a class block.
+ */
+
+extern void class_dec(classdef */*c*/);
+
+/* --- @class_userMatch@ --- *
+ *
+ * Arguments: @classdef *c@ = pointer to a class block
+ * @int u@ = uid number to check against
+ *
+ * Returns: Zero if no match, nonzero if it matched.
+ *
+ * Use: Looks to see if a user is in a group.
+ */
+
+extern int class_userMatch(classdef */*c*/, int /*u*/);
+
+/* --- @class_commandMatch@ --- *
+ *
+ * Arguments: @classdef *c@ = pointer to a class block
+ * @const char *p@ = pointer to command string to match
+ *
+ * Returns: Zero for no match, nonzero if it matched.
+ *
+ * Use: Tries to match a string against the wildcard patterns in the
+ * given class. Note that this involves examining all the
+ * items, so it's not too quick. Then again, you don't have
+ * big command classes, do you...?
+ */
+
+extern int class_commandMatch(classdef */*c*/, const char */*p*/);
+
+/* --- @class_hostMatch@ --- *
+ *
+ * Arguments: @classdef *c@ = pointer to class block
+ * @struct in_addr addr@ = IP address to match
+ *
+ * Returns: Zero for no match, nonzero if it matched.
+ *
+ * Use: Tries to match an IP address against the patterns and masks
+ * in the given class.
+ */
+
+extern int class_hostMatch(classdef */*c*/, struct in_addr /*addr*/);
+
+/* --- @class_dump@ --- *
+ *
+ * Arguments: @classdef *c@ = pointer to a class block
+ * @FILE *fp@ = pointer to a stream object
+ *
+ * Returns: ---
+ *
+ * Use: Dumps the contents of a class to a stream.
+ */
+
+extern void class_dump(classdef */*c*/, FILE */*fp*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
+
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: crypt.c,v 1.1 1997/07/21 13:47:51 mdw Exp $
+ *
+ * Cryptographic transfer of `become' requests
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: crypt.c,v $
+ * Revision 1.1 1997/07/21 13:47:51 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+/* --- Unix headers --- */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+
+/* --- Local headers --- */
+
+#include "config.h"
+#include "crypt.h"
+#include "icrypt.h"
+#include "idea.h"
+#include "md5.h"
+#include "tx.h"
+#include "utils.h"
+
+/*----- Magic numbers -----------------------------------------------------*/
+
+#define crypt__timeError 15 /* Seconds error to permit */
+#define crypt__seedBits 512 /* Number of random seed bits */
+
+/*----- Dump a block of data ----------------------------------------------*/
+
+/* --- @crypt__dump@ --- *
+ *
+ * Arguments: @char *p@ = a string to display at the top of the message
+ * @unsigned char *buf@ = pointer to a buffer to display
+ * @size_t sz@ = size of the buffer
+ * @FILE *fp@ = file to dump on
+ *
+ * Returns: --
+ *
+ * Use: Dumps a block of data to the terminal. This allows a
+ * programmer to examine the traffic between client and server,
+ * and possibly locate bugs.
+ */
+
+#ifndef NDEBUG
+
+static void crypt__dump(const char *p, const unsigned char *buf,
+ int sz, FILE *fp)
+{
+ int i;
+ fprintf(stderr,"+++ %s\n",p);
+ while (sz>0)
+ {
+ fprintf(stderr,"+++ ");
+ for (i=0;i<8;i++)
+ {
+ if (i<sz)
+ fprintf(stderr,"%02x ",buf[i] & 0xff);
+ else
+ fputs("** ",stderr);
+ }
+ fputs(": ",stderr);
+ for (i=0;i<8;i++)
+ putc(i<sz ? (isprint(buf[i]) ? buf[i] : '.') : '*',stderr);
+ putc('\n',stderr);
+ buf+=8;
+ sz-=8;
+ }
+ putc('\n',stderr);
+}
+
+#else
+
+#define crypt__dump(p, buf, sz, fp) ((void)0)
+
+#endif
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @crypt__sessionKey@ --- *
+ *
+ * Arguments: @const char *seedfile@ = pointer to name of seed file
+ * @unsigned char *k@ = our secret key
+ * @time_t t@ = the current time
+ * @pid_t pid@ = our process id
+ * @unsigned *sk@ = where to store the session key
+ * @unsigned char *iv@ = where to store the IV
+ *
+ * Returns: ---
+ *
+ * Use: Decides on a random session key and initialisation vector.
+ */
+
+static void crypt__sessionKey(const char *seedfile, unsigned char *k,
+ time_t t, pid_t pid,
+ unsigned char *sk, unsigned char *iv)
+{
+ FILE *fp; /* File handle for reading */
+ unsigned char s[crypt__seedBits / 8]; /* Random seed block */
+ unsigned char b[4 + 4];
+ /* For building the hash block */
+
+ /* --- Read the old seed in --- *
+ *
+ * Interlock with other processes locking this file.
+ */
+
+ {
+#ifndef TEST_RIG
+ struct flock l;
+#endif
+
+ if ((fp = fopen(seedfile, "r+")) == 0)
+ die("couldn't open random seed file `%s': %s",
+ seedfile, strerror(errno));
+
+#ifndef TEST_RIG
+ l.l_type = F_WRLCK;
+ l.l_whence = SEEK_SET;
+ l.l_start = 0;
+ l.l_len = 0;
+ if (fcntl(fileno(fp), F_SETLKW, &l) < 0)
+ die("couldn't lock random number file: %s", strerror(errno));
+#endif
+
+ tx_getBits(s, crypt__seedBits, fp);
+ }
+
+ crypt__dump("Initial seed", s, crypt__seedBits / 8, stdout);
+
+ /* --- Build a block of data to hash --- */
+
+ store32(b + 0, t);
+ store32(b + 4, pid);
+
+ /* --- Work out the session key --- */
+
+ {
+ md5 md;
+
+ md5_init(&md);
+ md5_buffer(&md, s, sizeof(s));
+ md5_buffer(&md, b, sizeof(b));
+ md5_buffer(&md, k, IDEA_KEYSIZE);
+ md5_final(&md, sk);
+
+ crypt__dump("Session key", sk, 128 / 8, stdout);
+ burn(md);
+ }
+
+ /* --- Build the new seed and write it back again --- */
+
+ if (crypt__seedBits > 128) {
+ unsigned char tb[MD5_HASHSIZE];
+ int i;
+
+ memcpy(tb, s + crypt__seedBits / 8 - sizeof(tb), sizeof(tb));
+ memmove(s + sizeof(tb), s, crypt__seedBits / 8 - sizeof(tb));
+ memcpy(s, tb, sizeof(tb));
+ for (i = 0; i < sizeof(sk); i++)
+ s[i] ^= sk[i];
+ burn(tb);
+ }
+
+ /* --- Take the seed we have and hash it again to get an IV --- */
+
+ {
+ unsigned char mdv[MD5_HASHSIZE];
+ md5 md;
+
+ md5_init(&md);
+ md5_buffer(&md, b, sizeof(b));
+ md5_buffer(&md, k, IDEA_KEYSIZE);
+ md5_buffer(&md, s, sizeof(s));
+ md5_final(&md, mdv);
+ memcpy(iv, mdv, IDEA_BLKSIZE);
+ crypt__dump("IV", iv, IDEA_BLKSIZE, stdout);
+ burn(md); burn(mdv);
+ }
+
+
+ /* --- Lock the file again --- *
+ *
+ * We're closing the file after we've finished, so we don't need to
+ * unlock it afterwards.
+ */
+
+ crypt__dump("Final seed", s, crypt__seedBits / 8, stdout);
+
+ rewind(fp);
+ tx_putBits(s, crypt__seedBits, fp);
+ fclose(fp);
+
+ /* --- Destroy sensitive data --- */
+
+ burn(b); burn(s);
+}
+
+/* --- @crypt_packRequest@ --- *
+ *
+ * Arguments: @request *rq@ = pointer to request block
+ * @unsigned char *buff@ = pointer to a buffer
+ * @time_t t@ = the current time
+ * @pid_t pid@ = my process ID
+ * @unsigned char *k@ = pointer to 128-bit key
+ * @unsigned char *sk@ = where to put the session key
+ *
+ * Returns: ---
+ *
+ * Use: Packs a request block into a buffer. The buffer should have
+ * space for at least @crq_size@ bytes. The buffer comes back
+ * encrypted and ready to send.
+ */
+
+void crypt_packRequest(request *rq, unsigned char *buff,
+ time_t t, pid_t pid,
+ unsigned char *k, unsigned char *sk)
+{
+ /* --- First, build the easy stuff in the block --- */
+
+ buff[crq_cryptType] = cryptType_idea;
+ store32(buff + crq_time, t);
+ store32(buff + crq_pid, pid);
+ store32(buff + crq_from, rq->from);
+ store32(buff + crq_to, rq->to);
+
+ /* --- The string causes a few problems --- *
+ *
+ * There's a good chance that the string will be a good deal shorter than
+ * the space allowed for it. This will probably mean lots of zeroes, and a
+ * very easy known-plaintext job for a potential attacker. (An early
+ * version of this code used @strncpy@ which is even worse!)
+ *
+ * I'll fill the block with random (from @rand@(3) -- nothing too
+ * elaborate) and then encrypt it using IDEA in CFB mode, using the first
+ * few bytes as the key. This should provide a sufficiently unpredictable
+ * background for the block.
+ */
+
+ {
+ icrypt_job j;
+ unsigned char *p;
+ unsigned u;
+
+ /* --- Initialise the buffer with junk --- */
+
+ srand((unsigned int)(t ^ pid)); /* Seed the (bad) RNG */
+ for (p = buff + crq_cmd; p < buff + crq_cmd + CMDLEN_MAX; p++) {
+ u = rand();
+ *p = u ^ (u >> 8);
+ }
+
+ /* --- Now make the junk a whole lot harder to predict --- */
+
+ p = buff + crq_cmd;
+ icrypt_init(&j, p, 0);
+ icrypt_encrypt(&j, p, p, CMDLEN_MAX);
+ burn(j);
+
+ /* --- Copy the string into here --- */
+
+ strcpy((char *)buff + crq_cmd, rq->cmd);
+ }
+
+ /* --- Generate a session key --- */
+
+ {
+ crypt__sessionKey(file_RANDSEED, k, t, pid, sk, buff + crq_iv);
+ memcpy(buff + crq_session, sk, IDEA_KEYSIZE);
+ }
+
+ /* --- Checksum the finished data --- */
+
+ {
+ md5 md;
+ unsigned char mdv[MD5_HASHSIZE];
+
+ md5_init(&md);
+ md5_buffer(&md, buff + crq_cipher, crq_check - crq_cipher);
+ md5_final(&md, mdv);
+ memcpy(buff + crq_check, mdv, 4);
+ burn(md); burn(mdv);
+ }
+
+ /* --- Encrypt the block --- *
+ *
+ * First, encrypt the session key using the master key. Since the session
+ * key is effectively random, this makes cracking the master key much
+ * harder. The rest of the block is then encrypted with the session key,
+ * using the IV left over from encrypting the session key.
+ */
+
+ {
+ icrypt_job j;
+
+ crypt__dump("request, before encryption", buff, crq_size, stdout);
+
+ icrypt_init(&j, k, buff + crq_iv);
+ icrypt_encrypt(&j, buff + crq_session, buff + crq_session, IDEA_KEYSIZE);
+ icrypt_reset(&j, sk, 0);
+ icrypt_encrypt(&j, buff + crq_cipher,
+ buff + crq_cipher, crq_size - crq_cipher);
+ burn(j);
+
+ crypt__dump("request, after encryption", buff, crq_size, stdout);
+ }
+}
+
+/* --- @crypt_unpackRequest@ --- *
+ *
+ * Arguments: @reqest *rq@ = pointer to destination request block
+ * @unsigned char *buff@ = pointer to source buffer
+ * @unsigned char *k@ = pointer to encryption key
+ * @unsigned char *sk@ = pointer to where to store session key
+ * @unsigned char *rpl@ = where to start building reply
+ *
+ * Returns: Nonzero if it was decrypted OK
+ *
+ * Use: Decrypts and unpacks a request buffer.
+ */
+
+int crypt_unpackRequest(request *rq, unsigned char *buff,
+ unsigned char *k, unsigned char *sk,
+ unsigned char *rpl)
+{
+ {
+ /* --- Check the encryption format --- */
+
+ if (buff[crq_cryptType] != cryptType_idea)
+ return (0);
+ }
+
+ {
+ /* --- First things first: decrypt the block --- */
+
+ icrypt_job j;
+
+ crypt__dump("request, before decryption", buff, crq_size, stdout);
+
+ icrypt_init(&j, k, buff + crq_iv);
+ icrypt_decrypt(&j, buff + crq_session, buff + crq_session, IDEA_KEYSIZE);
+ memcpy(sk, buff + crq_session, IDEA_KEYSIZE);
+ icrypt_reset(&j, sk, 0);
+ icrypt_decrypt(&j, buff + crq_cipher,
+ buff + crq_cipher, crq_size - crq_cipher);
+ icrypt_saveIV(&j, rpl + crp_iv);
+
+ memset(buff + crq_session, 0, IDEA_KEYSIZE); /* Burn, baby, burn */
+ burn(j);
+
+ crypt__dump("request, after decryption", buff, crq_size, stdout);
+ }
+
+ {
+ /* --- Check the validity of the data therein --- */
+
+ md5 md;
+ unsigned char mdv[MD5_HASHSIZE];
+
+ md5_init(&md);
+ md5_buffer(&md, buff + crq_cipher, crq_check - crq_cipher);
+ md5_final(&md, mdv);
+ if (memcmp(mdv, buff + crq_check, 4) != 0) {
+ syslog(LOG_INFO, "packet rejected: bad checksum");
+ return (0);
+ }
+ burn(md); burn(mdv);
+ }
+
+ {
+ /* --- Extract fields from the block --- */
+
+ rq->from = load32(buff + crq_from);
+ rq->to = load32(buff + crq_to);
+ memcpy(rq->cmd, buff + crq_cmd, CMDLEN_MAX);
+ }
+
+ {
+ /* --- Fill in bits of the reply block --- */
+
+ long t = (long)time(0);
+ long u = (long)load32(buff + crq_time);
+
+ if (t - u > crypt__timeError || u - t > crypt__timeError) {
+ syslog(LOG_INFO, "packet rejected: bad time");
+ return (0);
+ }
+ memcpy(rpl + crp_time, buff + crq_time, 8);
+ }
+
+ /* --- Done --- */
+
+ return (1);
+}
+
+/* --- @crypt_packReply@ --- *
+ *
+ * Arguments: @char *buff@ = pointer to reply block
+ * @unsigned char *sk@ = pointer to session key
+ * @int answer@ = yes or no
+ *
+ * Returns: ---
+ *
+ * Use: Packs and encrypts a reply block.
+ */
+
+void crypt_packReply(unsigned char *buff, unsigned char *sk, int answer)
+{
+ {
+ /* --- Store the answer --- */
+
+ buff[crp_answer] = (answer != 0);
+ }
+
+ {
+ /* --- Build the checksum --- */
+
+ md5 md;
+ unsigned char mdv[MD5_HASHSIZE];
+
+ md5_init(&md);
+ md5_buffer(&md, buff + crp_cipher, crp_check - crp_cipher);
+ md5_final(&md, mdv);
+ memcpy(buff + crp_check, mdv, 4);
+ burn(md); burn(mdv);
+ }
+
+ {
+ /* --- Encrypt the buffer --- */
+
+ icrypt_job j;
+ icrypt_init(&j, sk, buff + crp_iv);
+ crypt__dump("reply, before encryption", buff, crp_size, stdout);
+ icrypt_encrypt(&j, buff + crp_cipher,
+ buff + crp_cipher, crp_size - crp_cipher);
+ crypt__dump("reply, after encryption", buff, crp_size, stdout);
+ burn(j);
+ }
+}
+
+/* --- @crypt_unpackReply@ --- *
+ *
+ * Arguments: @unsigned char *buff@ = pointer to reply buffer
+ * @unsigned char *sk@ = pointer to session key
+ * @time_t t@ = time at which request was sent
+ * @pid_t pid@ = my process ID
+ *
+ * Returns: >0 if request granted, zero if denied, <0 if reply rejected
+ *
+ * Use: Unpacks a reply block, and informs the caller of the outcome.
+ */
+
+int crypt_unpackReply(unsigned char *buff, unsigned char *sk,
+ time_t t, pid_t pid)
+{
+ {
+ /* --- Decrypt my reply block --- */
+
+ icrypt_job j;
+ icrypt_init(&j, sk, buff + crp_iv);
+ crypt__dump("reply, before decryption", buff, crp_size, stdout);
+ icrypt_decrypt(&j, buff + crp_cipher,
+ buff + crp_cipher, crp_size - crp_cipher);
+ crypt__dump("reply, after decryption", buff, crp_size, stdout);
+ burn(j);
+ }
+
+ {
+ /* --- Check validity --- */
+
+ md5 md;
+ unsigned char mdv[MD5_HASHSIZE];
+ char b[8];
+
+ /* --- Check the checksum --- */
+
+ md5_init(&md);
+ md5_buffer(&md, buff + crp_cipher, crp_check - crp_cipher);
+ md5_final(&md, mdv);
+ if (memcmp(buff + crp_check, mdv, 4) != 0) {
+ syslog(LOG_INFO, "reply rejected: bad checksum");
+ return (-1);
+ }
+
+ /* --- Check the identifier --- */
+
+ store32(b + 0, t); store32(b + 4, pid);
+ if (memcmp(b, buff + crp_time, sizeof(b)) != 0) {
+ syslog(LOG_INFO, "reply rejected: bad identification marker");
+ return (-1);
+ }
+ }
+
+ /* --- Return the value --- */
+
+ return (buff[crp_answer]);
+}
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+int main(int argc, char *argv[])
+{
+ time_t t = time(0);
+ pid_t pid = getpid();
+ unsigned char buff[8];
+ unsigned char sk[IDEA_KEYSIZE], k[IDEA_KEYSIZE];
+ FILE *fp;
+
+ ego(argv[0]);
+ if (argc < 3)
+ die("bad args");
+ fp = fopen(argv[1], "r");
+ if (!fp)
+ die("fopen: %s", strerror(errno));
+ tx_getBits(k, 128, fp);
+ fclose(fp);
+ crypt__sessionKey(argv[2], k, t, pid, sk, buff);
+ return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: crypt.h,v 1.1 1997/07/21 13:47:51 mdw Exp $
+ *
+ * Cryptographic transfer of `become' requests
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: crypt.h,v $
+ * Revision 1.1 1997/07/21 13:47:51 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef CRYPT_H
+#define CRYPT_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#include <string.h>
+
+#ifndef BECOME_H
+# include "become.h"
+#endif
+
+#ifndef CONFIG_H
+# include "config.h"
+#endif
+
+/*----- Type definitions and data structures ------------------------------*/
+
+/* --- Encryption formats --- */
+
+enum {
+ cryptType_idea, /* Symmetric IDEA encryption */
+ cryptType_rsa /* Public key RSA (later project) */
+};
+
+/* --- Encrypted buffer format --- *
+ *
+ * C structures are no good here. Time for some explicit offsets.
+ */
+
+enum {
+ crq_cryptType = 0, /* Encryption type (1 byte) */
+ crq_iv = crq_cryptType + 1, /* Plaintext IV (8 bytes) */
+ crq_session = crq_iv + 8, /* IDEA session key (16 bytes) */
+ crq_cipher = crq_session + 16, /* Where to start encrypting */
+ crq_time = crq_cipher, /* Time stamp (4 bytes) */
+ crq_pid = crq_time + 4, /* Process ID (4 bytes) */
+ crq_from = crq_pid + 4, /* From user id (4 bytes) */
+ crq_to = crq_from + 4, /* To user id (4 bytes) */
+ crq_cmd = crq_to + 4, /* Command string (lots of bytes) */
+ crq_check = crq_cmd + CMDLEN_MAX, /* Checksum for request (4 bytes) */
+ crq_size = crq_check + 4 /* Size of encrypted request */
+};
+
+/* --- Encrypted result format --- */
+
+enum {
+ crp_iv = 0, /* Plaintext IV (8 bytes) */
+ crp_cipher = crp_iv + 8, /* Where to start encrypting */
+ crp_time = crp_cipher, /* Time of request (4 bytes) */
+ crp_pid = crp_time + 4, /* Process ID of client (4 bytes) */
+ crp_answer = crp_pid + 4, /* Answer (1 or 0) (1 byte) */
+ crp_check = crp_answer + 1, /* Checksum for reply (4 bytes) */
+ crp_size = crp_check + 4 /* Size of encrypted reply */
+};
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- @burn@ --- *
+ *
+ * Arguments: @obj@ = some object
+ *
+ * Use: Writes zero bytes over the object.
+ */
+
+#define burn(obj) ((void)memset(&obj, 0, sizeof(obj)))
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @crypt_packRequest@ --- *
+ *
+ * Arguments: @request *rq@ = pointer to request block
+ * @unsigned char *buff@ = pointer to a buffer
+ * @time_t t@ = the current time
+ * @pid_t pid@ = my process ID
+ * @unsigned char *k@ = pointer to 128-bit key
+ * @unsigned char *sk@ = where to put the session key
+ *
+ * Returns: The number of bytes written.
+ *
+ * Use: Packs a request block into a buffer. The buffer should have
+ * space for at least @crq_size@ bytes. The buffer comes back
+ * encrypted and ready to send.
+ */
+
+extern void crypt_packRequest(request */*rq*/, unsigned char */*buff*/,
+ time_t /*t*/, pid_t /*pid*/,
+ unsigned char */*k*/, unsigned char */*sk*/);
+
+/* --- @crypt_unpackRequest@ --- *
+ *
+ * Arguments: @reqest *rq@ = pointer to destination request block
+ * @unsigned char *buff@ = pointer to source buffer
+ * @unsigned char *k@ = pointer to encryption key
+ * @unsigned char *sk@ = pointer to where to store session key
+ * @unsigned char *rpl@ = where to start building reply
+ *
+ * Returns: ---
+ *
+ * Use: Decrypts and unpacks a request buffer.
+ */
+
+extern int crypt_unpackRequest(request */*rq*/, unsigned char */*buff*/,
+ unsigned char */*k*/, unsigned char */*sk*/,
+ unsigned char */*rpl*/);
+
+/* --- @crypt_packReply@ --- *
+ *
+ * Arguments: @unsigned char *buff@ = pointer to reply block
+ * @unsigned char *sk@ = pointer to session key
+ * @int answer@ = yes or no
+ *
+ * Returns: ---
+ *
+ * Use: Packs and encrypts a reply block.
+ */
+
+extern void crypt_packReply(unsigned char */*buff*/, unsigned char */*sk*/,
+ int /*answer*/);
+
+/* --- @crypt_unpackReply@ --- *
+ *
+ * Arguments: @unsigned char *buff@ = pointer to reply buffer
+ * @unsigned char *sk@ = pointer to session key
+ * @time_t t@ = time at which request was sent
+ * @pid_t pid@ = my process ID
+ *
+ * Returns: >0 if request granted, zero if denied, <0 if reply rejected
+ *
+ * Use: Unpacks a reply block, and informs the caller of the outcome.
+ */
+
+extern int crypt_unpackReply(unsigned char */*buff*/, unsigned char */*sk*/,
+ time_t /*t*/, pid_t /*pid*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: daemon.c,v 1.1 1997/07/21 13:47:50 mdw Exp $
+ *
+ * Running a `become' daemon
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: daemon.c,v $
+ * Revision 1.1 1997/07/21 13:47:50 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <errno.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Unix headers --- */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <syslog.h>
+#include <unistd.h>
+
+/* --- Local headers --- */
+
+#include "become.h"
+#include "config.h"
+#include "crypt.h"
+#include "daemon.h"
+#include "idea.h"
+#include "lexer.h"
+#include "name.h"
+#include "parser.h"
+#include "rule.h"
+#include "tx.h"
+#include "userdb.h"
+#include "utils.h"
+
+/*----- Arbitrary constants -----------------------------------------------*/
+
+#define daemon__awakeEvery (5 * 60) /* Awaken this often to rescan */
+
+/*----- Static variables --------------------------------------------------*/
+
+static int daemon__running = 0; /* Am I running as a daemon? */
+static int daemon__port = -1; /* No particular port yet */
+static volatile sig_atomic_t daemon__rescan = 0; /* Rescan as soon as poss */
+#define daemon__signum daemon__rescan /* Alias for readbility */
+static int daemon__readKey = 0; /* Have I read a key? */
+static unsigned char daemon__key[IDEA_KEYSIZE]; /* encryption key */
+static jmp_buf daemon__dieBuf; /* Jump here to kill the daemon */
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @daemon_usePort@ --- *
+ *
+ * Arguments: @int port@ = port to use, please
+ *
+ * Returns: ---
+ *
+ * Use: Instructs the daemon to listen to the given port.
+ */
+
+void daemon_usePort(int port)
+{
+ daemon__port = port;
+}
+
+/* --- @daemon_readKey@ --- *
+ *
+ * Arguments: @const char *kf@ = name of file containing key
+ *
+ * Returns: ---
+ *
+ * Use: Instructs the daemon to read the named key file.
+ */
+
+void daemon_readKey(const char *kf)
+{
+ FILE *fp;
+
+ if (!daemon__running)
+ return;
+
+ if ((fp = fopen(kf, "r")) == 0) {
+ syslog(LOG_WARNING, "couldn't read key file: %e");
+ return;
+ }
+ tx_getBits(daemon__key, 128, fp);
+ fclose(fp);
+ daemon__readKey = 1;
+ return;
+}
+
+/* --- @daemon__readConfig@ --- *
+ *
+ * Arguments: @const char *cf@ = pointer to configuration file to use
+ *
+ * Returns: Zero if it worked, nonzero if it hurt...
+ *
+ * Use: Reads the configuration file, and other things.
+ */
+
+static int daemon__readConfig(const char *cf)
+{
+ FILE *fp;
+
+ daemon__readKey = 0;
+ if ((fp = fopen(cf, "r")) == 0)
+ return (-1);
+ lexer_scan(fp);
+ yyparse();
+ fclose(fp);
+ if (!daemon__readKey)
+ daemon_readKey(file_KEY);
+ daemon__rescan = 0;
+ return (0);
+}
+
+/* --- @daemon__restart@ --- *
+ *
+ * Arguments: @int sig@ = the signal number
+ *
+ * Returns: ---
+ *
+ * Use: Handles signals. Causes the configuration file to be reread.
+ */
+
+static void daemon__restart(int sig)
+{
+ daemon__rescan = 1;
+ signal(sig, daemon__restart);
+}
+
+/* --- @daemon__die@ --- *
+ *
+ * Arguments: @int sig@ = the signal number
+ *
+ * Returns: via @longjmp@
+ *
+ * Use: Handles other signals. Causes the daemon to die.
+ */
+
+static void daemon__die(int sig)
+{
+ daemon__signum = sig;
+ longjmp(daemon__dieBuf, 1);
+}
+
+/* --- @daemon__read@ --- *
+ *
+ * Arguments: @int fd@ = socket handle
+ *
+ * Returns: ---
+ *
+ * Use: Examines a buffer, and returns a response.
+ */
+
+void daemon__read(int fd)
+{
+ unsigned char buff[65536]; /* Buffer for incoming packets */
+ unsigned char rpl[crp_size]; /* Buffer for outgoing replies */
+ struct sockaddr_in sin; /* Address of packet sender */
+ char sender[64]; /* Sender's hostname (resolved) */
+ unsigned char sk[IDEA_KEYSIZE]; /* Session key for reply */
+ request rq; /* Request buffer for verification */
+
+ /* --- Read the message --- */
+
+ {
+ int slen = sizeof(sin);
+
+ if (recvfrom(fd, (char *)buff, sizeof(buff), 0,
+ (struct sockaddr *)&sin, &slen) < 0) {
+ syslog(LOG_INFO, "duff packet received: %e");
+ return;
+ }
+ }
+
+ /* --- Resolve the host name --- */
+
+ {
+ struct hostent *he = gethostbyaddr((char *)&sin.sin_addr,
+ sizeof(sin.sin_addr),
+ AF_INET);
+ sender[0] = 0;
+ strncat(sender,
+ he ? he->h_name : inet_ntoa(sin.sin_addr),
+ sizeof(sender));
+ syslog(LOG_DEBUG, "packet received from %s", sender);
+ }
+
+ /* --- Unpack the block --- */
+
+ if (crypt_unpackRequest(&rq, buff, daemon__key, sk, rpl) == 0) {
+ burn(buff);
+ syslog(LOG_INFO, "packet from %s rejected", sender);
+ return;
+ }
+ burn(buff);
+
+ /* --- Fill in the sender's address in the request block --- */
+
+ rq.host = sin.sin_addr;
+
+ /* --- Build a reply block --- */
+
+ {
+ int answer = rule_check(&rq);
+ syslog(LOG_INFO, "request from %s for %i to become %i to run %s %s",
+ sender, rq.from, rq.to, rq.cmd, answer ? "granted" : "denied");
+ crypt_packReply(rpl, sk, answer);
+ burn(sk);
+ }
+
+ /* --- Send the reply off --- */
+
+ sendto(fd, (char *)rpl, crp_size, 0, (struct sockaddr *)&sin, sizeof(sin));
+ burn(rpl);
+}
+
+/* --- @daemon_init@ --- *
+ *
+ * Arguments: @const char *cf@ = pointer to name of configuration file
+ * @int port@ = port to listen to, or %$-1$% for default
+ *
+ * Returns: Never.
+ *
+ * Use: Starts `become' up in daemon mode.
+ */
+
+void daemon_init(const char *cf, int port)
+{
+ int s;
+
+ /* --- Remove my root privileges --- *
+ *
+ * Just in case there's anything dodgy in my configuration file, or the
+ * user wants me to start on a funny port.
+ */
+
+ seteuid(getuid());
+
+ /* --- Initialise bits of the program --- */
+
+ daemon__running = 1;
+ daemon__port = port;
+ userdb_init();
+ userdb_local();
+ userdb_yp();
+ name_init();
+ rule_init();
+ openlog(quis(), 0, LOG_DAEMON);
+ syslog(LOG_NOTICE, "starting up");
+
+ if (daemon__readConfig(cf))
+ die("couldn't read configuration file");
+
+ /* --- Decide on a port to use --- *
+ *
+ * If I don't have a port yet (e.g., from the configuration file) then
+ * look it up in /etc/services under whatever name I was started as.
+ */
+
+ if (daemon__port <= 0) {
+ struct servent *se = getservbyname(quis(), "udp");
+ if (!se)
+ die("no idea which port to use");
+ daemon__port = ntohs(se->s_port);
+ }
+
+ /* --- Now set up a socket --- */
+
+ {
+ struct sockaddr_in sin;
+
+ if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
+ die("couldn't create socket: %s", strerror(errno));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(daemon__port);
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (bind(s, (struct sockaddr *)&sin, sizeof(sin)))
+ die("couldn't bind socket to port: %s", strerror(errno));
+ }
+
+ /* --- Fork off into the sunset --- */
+
+#ifdef NDEBUG
+ {
+ int pid = fork();
+ FILE *fp;
+
+ /* --- Make a background process --- */
+
+ if (pid == -1)
+ die("couldn't fork daemon: %s", strerror(errno));
+ else if (pid != 0)
+ return;
+
+ /* --- Disconnect from the terminal --- */
+
+ setsid();
+
+ /* --- Write my process id to a file --- */
+
+ if ((fp = fopen(file_PID, "w")) != 0) {
+ fprintf(fp, "%lu\n", (unsigned long)getpid());
+ fclose(fp);
+ }
+ }
+#endif
+
+ /* --- Program in daemon death mode --- */
+
+ if (setjmp(daemon__dieBuf)) {
+ syslog(LOG_NOTICE, "killed by signal type %i", daemon__signum);
+ remove(file_PID);
+ exit(0);
+ }
+
+ /* --- Set signal handlers --- */
+
+ signal(SIGHUP, daemon__restart);
+ signal(SIGQUIT, daemon__restart);
+ signal(SIGINT, daemon__die);
+ signal(SIGTERM, daemon__die);
+ signal(SIGSEGV, daemon__die);
+ signal(SIGFPE, daemon__die);
+ signal(SIGBUS, daemon__die);
+
+ /* --- Now wait for something exciting to happen --- *
+ *
+ * Actually, every so often (5 minutes, perhaps) I need to wake up and
+ * rescan the users to see whether they've changed. Time to play with
+ * @select@.
+ */
+
+ {
+ time_t when;
+
+ /* --- Find when I am, and thus when I need to be awoken again --- */
+
+ when = time(0) + daemon__awakeEvery;
+
+ for (;;) {
+ fd_set fds;
+ int i;
+
+ /* --- Set up the file descriptor tables --- */
+
+ FD_ZERO(&fds);
+ FD_SET(s, &fds);
+
+ /* --- Now wait for something interesting --- */
+
+ i = select(FD_SETSIZE, &fds, 0, 0, 0);
+
+ /* --- Now, see if I need to rescan the config --- */
+
+ if (daemon__rescan || time(0) - when > 0) {
+ daemon__rescan = 0;
+ syslog(LOG_INFO, "rescanning configuration file");
+ userdb_reinit();
+ userdb_local();
+ userdb_yp();
+ rule_reinit();
+ name_reinit();
+ if (daemon__readConfig(cf))
+ syslog(LOG_ERR, "error reading configuration file");
+ when = time(0) + daemon__awakeEvery;
+ }
+
+ /* --- Read the data from the request --- */
+
+ if (i > 0)
+ daemon__read(s);
+ }
+ }
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: daemon.h,v 1.1 1997/07/21 13:47:50 mdw Exp $
+ *
+ * Running a `become' daemon
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: daemon.h,v $
+ * Revision 1.1 1997/07/21 13:47:50 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef DAEMON_H
+#define DAEMON_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @daemon_usePort@ --- *
+ *
+ * Arguments: @int port@ = port to use, please
+ *
+ * Returns: ---
+ *
+ * Use: Instructs the daemon to listen to the given port.
+ */
+
+extern void daemon_usePort(int /*port*/);
+
+/* --- @daemon_readKey@ --- *
+ *
+ * Arguments: @const char *kf@ = name of file containing key
+ *
+ * Returns: ---
+ *
+ * Use: Instructs the daemon to read the named key file.
+ */
+
+extern void daemon_readKey(const char */*kf*/);
+
+/* --- @daemon_init@ --- *
+ *
+ * Arguments: @const char *cf@ = pointer to name of configuration file
+ * @int port@ = port to listen to, or %$-1$% for default
+ *
+ * Returns: Never.
+ *
+ * Use: Starts `become' up in daemon mode.
+ */
+
+extern void daemon_init(const char */*cf*/, int /*port*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: dbutils.h,v 1.1 1997/07/21 13:47:50 mdw Exp $
+ *
+ * Debugging things
+ *
+ * (c) 1996 Straylight
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: dbutils.h,v $
+ * Revision 1.1 1997/07/21 13:47:50 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef DBUTILS_H
+#define DBUTILS_H
+
+/* --- Note --- *
+ *
+ * This is most certainly not portable. I'm using VT100-specific control
+ * codes to clear the screen and @select@ to pause for a bit between
+ * allocations to stop the scree flicker driving me up the wall.
+ *
+ * The main routine just allocates and frees blocks randomly, and displays
+ */
+
+#if defined(__unix)
+# include <sys/time.h>
+# include <unistd.h>
+# define cls fputs("\33[H\33[2J", stderr) /* VT100 specific */
+# define pause do { \
+ struct timeval tv = { 0, 10000 }; \
+ select(0, 0, 0, 0, &tv); \
+ } while (0)
+#elif defined(__riscos)
+# include "swiv.h"
+# include "swis.h"
+# define cls _swi(OS_WriteC, _in(0), 12)
+# define pause _swi(OS_Byte, _in(0), 19)
+#else
+# define cls /* Tough. Write these yourself. */
+# define pause
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: icrypt.c,v 1.1 1997/07/21 13:47:49 mdw Exp $
+ *
+ * Higher level IDEA encryption
+ *
+ * (c) 1997 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: icrypt.c,v $
+ * Revision 1.1 1997/07/21 13:47:49 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* --- Local headers --- */
+
+#include "config.h"
+#include "icrypt.h"
+#include "idea.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @icrypt_init@ --- *
+ *
+ * Arguments: @icrypt_job *j@ = pointer to job context block
+ * @unsigned char *k@ = pointer to key data
+ * @const unsigned char *iv@ = pointer to IV
+ *
+ * Returns: ---
+ *
+ * Use: Primes the context block ready for encryption.
+ */
+
+void icrypt_init(icrypt_job *j, unsigned char *k, const unsigned char *iv)
+{
+ idea_ekeys(&j->k, k);
+ if (iv)
+ memcpy(j->iv, iv, IDEA_BLKSIZE);
+ else
+ memset(j->iv, 0, IDEA_BLKSIZE);
+ j->i = 8;
+}
+
+/* --- @icrypt_encrypt@ --- *
+ *
+ * Arguments: @icrypt_job *j@ = job handle
+ * @const void *src@ = pointer to source buffer
+ * @void *dest@ = pointer to destination buffer
+ * @size_t sz@ = size of buffers to handle
+ *
+ * Returns: ---
+ *
+ * Use: Encrypts data from the source to the destination, using the
+ * key attached to the job handle.
+ */
+
+void icrypt_encrypt(icrypt_job *j, const void *src, void *dest, size_t sz)
+{
+ const char *s = src;
+ char *d = dest;
+ int i;
+
+ /* --- First, use up bytes in the buffer --- */
+
+ while (j->i < IDEA_BLKSIZE && sz > 0) {
+ *d++ = j->iv[j->i++] ^= *s++;
+ sz--;
+ }
+ if (!sz) return;
+
+ /* --- Now encrypt larger chunks at a time --- */
+
+ while (sz >= IDEA_BLKSIZE) {
+
+ /* --- Freshen the IV --- */
+
+ idea_encrypt(&j->k, j->iv, j->iv);
+
+ /* --- Now encrypt some more bytes --- */
+
+ for (i = 0; i < IDEA_BLKSIZE; i++) {
+ *d++ = j->iv[i] ^= *s++;
+ }
+ sz -= IDEA_BLKSIZE;
+ }
+ if (!sz) return;
+
+ /* --- Do the tail-end bits --- */
+
+ idea_encrypt(&j->k, j->iv, j->iv);
+ j->i = 0;
+ while (sz) {
+ *d++ = j->iv[j->i++] ^= *s++;
+ sz--;
+ }
+}
+
+/* --- @icrypt_decrypt@ --- *
+ *
+ * Arguments: @icrypt_job *j@ = job handle
+ * @const void *src@ = pointer to source buffer
+ * @void *dest@ = pointer to destination buffer
+ * @size_t sz@ = size of buffers to handle
+ *
+ * Returns: ---
+ *
+ * Use: Decrypts data from the source to the destination, using
+ * the key attached to the job handle.
+ */
+
+void icrypt_decrypt(icrypt_job *j, const void *src, void *dest, size_t sz)
+{
+ const char *s = src;
+ char *d = dest;
+ int i;
+ char c;
+
+ /* --- First, use up bytes in the buffer --- */
+
+ while (j->i < IDEA_BLKSIZE && sz > 0) {
+ c = *s++;
+ *d++ = j->iv[j->i] ^ c;
+ j->iv[j->i++] = c;
+ sz--;
+ }
+ if (!sz) return;
+
+ /* --- Now encrypt larger chunks at a time --- */
+
+ while (sz >= IDEA_BLKSIZE) {
+
+ /* --- Freshen the IV --- */
+
+ idea_encrypt(&j->k, j->iv, j->iv);
+
+ /* --- Now encrypt some more bytes --- */
+
+ for (i = 0; i < IDEA_BLKSIZE; i++) {
+ c = *s++;
+ *d++ = j->iv[i] ^ c;
+ j->iv[i] = c;
+ }
+ sz -= IDEA_BLKSIZE;
+ }
+ if (!sz) return;
+
+ /* --- Do the tail-end bits --- */
+
+ idea_encrypt(&j->k, j->iv, j->iv);
+ j->i = 0;
+ while (sz) {
+ c = *s++;
+ *d++ = j->iv[j->i] ^ c;
+ j->iv[j->i++] = c;
+ sz--;
+ }
+}
+
+/* --- @icrypt_reset@ --- *
+ *
+ * Arguments: @icrypt_job *j@ = pointer to job context block
+ * @unsigned char *k@ = pointer to key data, or zero for
+ * no change
+ * @const unsigned char *iv@ = pointer to IV, or zero
+ *
+ * Returns: ---
+ *
+ * Use: Alters the context block. This can be used after recovery
+ * of a session key, for example.
+ */
+
+void icrypt_reset(icrypt_job *j, unsigned char *k, const unsigned char *iv)
+{
+ if (k)
+ idea_ekeys(&j->k, k);
+ if (iv)
+ memcpy(j->iv, iv, IDEA_BLKSIZE);
+ else {
+ unsigned char b[IDEA_BLKSIZE];
+ int n = j->i, o = IDEA_BLKSIZE - j->i;
+
+ memcpy(b, j->iv, sizeof(b));
+ memcpy(j->iv, b + n, o);
+ memcpy(j->iv + o, b, n);
+ }
+ j->i = 8;
+}
+
+/* --- @icrypt_saveIV@ --- *
+ *
+ * Arguments: @icrypt_job *j@ = pointer to job context block
+ * @unsigned char *iv@ = where to store the IV
+ *
+ * Returns: ---
+ *
+ * Use: Writes out the job's IV after munging it a little.
+ */
+
+void icrypt_saveIV(icrypt_job *j, unsigned char *iv)
+{
+ int n = j->i, o = IDEA_BLKSIZE - j->i;
+
+ memcpy(j->iv, iv + n, o);
+ memcpy(j->iv + o, iv, n);
+ idea_encrypt(&j->k, iv, iv);
+}
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+#include <errno.h>
+#include <pwd.h>
+#include <unistd.h>
+#include "mdwopt.h"
+#include "md5.h"
+#include "utils.h"
+
+void icrypt(icrypt_job *j,
+ void (*proc)(icrypt_job *j,
+ const void *src,
+ void *dest,
+ size_t sz),
+ FILE *fp)
+{
+ char buff[71];
+ size_t r;
+
+ for (;;) {
+ r = fread(buff, 1, sizeof(buff), fp);
+ if (!r) break;
+ proc(j, buff, buff, r);
+ fwrite(buff, 1, r, stdout);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ icrypt_job j;
+ md5 md;
+ void (*proc)(icrypt_job *j,
+ const void *src,
+ void *dest,
+ size_t sz) = icrypt_encrypt;
+
+ ego(argv[0]);
+
+ for (;;) {
+ int i = getopt(argc, argv, "d");
+ if (i < 0)
+ break;
+ if (i == 'd')
+ proc = icrypt_decrypt;
+ }
+
+ {
+ char *pass = getpass("Password: ");
+ md5_init(&md);
+ md5_buffer(&md, pass, strlen(pass));
+ memset(pass, 0, strlen(pass));
+ md5_final(&md);
+
+ icrypt_init(&j, md.val, 0);
+ }
+
+ if (optind >= argc)
+ icrypt(&j, proc, stdin);
+
+ while (optind < argc) {
+ char *p = argv[optind++];
+ if (strcmp(p, "-") == 0)
+ icrypt(&j, proc, stdin);
+ else {
+ FILE *fp = fopen(p, "rb");
+ if (!fp)
+ die("couldn't open `%s': %s", p, strerror(errno));
+ icrypt(&j, proc, fp);
+ fclose(fp);
+ }
+ }
+
+ return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: icrypt.h,v 1.1 1997/07/21 13:47:49 mdw Exp $
+ *
+ * Higher level IDEA encryption
+ *
+ * (c) 1997 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: icrypt.h,v $
+ * Revision 1.1 1997/07/21 13:47:49 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef ICRYPT_H
+#define ICRYPT_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#include <stddef.h>
+
+#ifndef CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef IDEA_H
+# include "idea.h"
+#endif
+
+/*----- Type definitions --------------------------------------------------*/
+
+/* --- @icrypt_job@ --- */
+
+typedef struct icrypt_job {
+ idea_key k; /* IDEA key for en/decrypting */
+ int i; /* Index into the IV buffer */
+ char iv[IDEA_BLKSIZE]; /* IV bytes for encrypting */
+} icrypt_job;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @icrypt_init@ --- *
+ *
+ * Arguments: @icrypt_job *j@ = pointer to job context block
+ * @unsigned char *k@ = pointer to key data
+ * @const unsigned char *iv@ = pointer to IV
+ *
+ * Returns: ---
+ *
+ * Use: Primes the context block ready for encryption.
+ */
+
+extern void icrypt_init(icrypt_job */*j*/,
+ unsigned char */*k*/,
+ const unsigned char */*iv*/);
+
+/* --- @icrypt_encrypt@ --- *
+ *
+ * Arguments: @icrypt_job *j@ = job handle
+ * @const void *src@ = pointer to source buffer
+ * @void *dest@ = pointer to destination buffer
+ * @size_t sz@ = size of buffers to handle
+ *
+ * Returns: ---
+ *
+ * Use: Encrypts data from the source to the destination, using the
+ * key attached to the job handle.
+ */
+
+extern void icrypt_encrypt(icrypt_job */*j*/, const void */*src*/,
+ void */*dest*/, size_t /*sz*/);
+
+/* --- @icrypt_decrypt@ --- *
+ *
+ * Arguments: @icrypt_job *j@ = job handle
+ * @const void *src@ = pointer to source buffer
+ * @void *dest@ = pointer to destination buffer
+ * @size_t sz@ = size of buffers to handle
+ *
+ * Returns: ---
+ *
+ * Use: Decrypts data from the source to the destination, using
+ * the key attached to the job handle.
+ */
+
+extern void icrypt_decrypt(icrypt_job */*j*/, const void */*src*/,
+ void */*dest*/, size_t /*sz*/);
+
+/* --- @icrypt_reset@ --- *
+ *
+ * Arguments: @icrypt_job *j@ = pointer to job context block
+ * @unsigned char *k@ = pointer to key data, or zero for
+ * no change
+ * @const unsigned char *iv@ = pointer to IV, or zero
+ *
+ * Returns: ---
+ *
+ * Use: Alters the context block. This can be used after recovery
+ * of a session key, for example.
+ */
+
+extern void icrypt_reset(icrypt_job */*j*/,
+ unsigned char */*k*/,
+ const unsigned char */*iv*/);
+
+/* --- @icrypt_saveIV@ --- *
+ *
+ * Arguments: @icrypt_job *j@ = pointer to job context block
+ * @unsigned char *iv@ = where to store the IV
+ *
+ * Returns: ---
+ *
+ * Use: Writes out the job's IV after munging it a little.
+ */
+
+extern void icrypt_saveIV(icrypt_job */*j*/, unsigned char */*iv*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: idea.c,v 1.1 1997/07/21 13:47:49 mdw Exp $
+ *
+ * IDEA encryption routines
+ * Based on Straylight ARM assembler routines
+ *
+ * (c) 1996, 1997 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: idea.c,v $
+ * Revision 1.1 1997/07/21 13:47:49 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Notes -------------------------------------------------------------*
+ *
+ * This code is optimised for 32-bit processors with reasonable numbers of
+ * registers. Hopefully it should still work on a Spectrum, although rather
+ * slowly. I do assume two's complement arithmetic.
+ *
+ * Since this is actually /decompiled/, by hand, from some existing assembler
+ * code, you can expect some parts to be a little strange.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdio.h>
+
+#include "config.h"
+#include "idea.h"
+#include "utils.h"
+
+/*----- Low-level support functions ---------------------------------------*/
+
+/* --- @idea__inv@ --- *
+ *
+ * Arguments: @int n@ = number to invert
+ *
+ * Returns: Multiplicative inverse of n, mod 2^{16} + 1
+ */
+
+static int idea__inv(int n)
+{
+ long m, a, b, q, r, t;
+
+ /* --- Check the easy case --- */
+
+ if (!n)
+ return (0);
+
+ /* --- Start off the loop --- */
+
+ m = 0x10001L;
+ a = 1;
+ b = 0;
+ for (;;) {
+ q = m / n, r = m % n;
+ if (!r)
+ break;
+ m = n, n = r;
+ t = a, a = b - q * a, b = t;
+ }
+
+ /* --- Get return value in range --- */
+
+ if (a < 0)
+ a += 1;
+ return ((int) a & 0xFFFF);
+}
+
+/* --- @_mul@ --- *
+ *
+ * An evil macro to do multiplication. Satan lives here.
+ */
+
+#define _mul(x, y) \
+ (void)( \
+ x &= ffff, x ? \
+ ( y &= ffff, y ? \
+ ((y = x * y), x = y & ffff, y = y >> 16, x < y ? \
+ (x = x - y + 1) : (x = x - y)) : \
+ (x = 1 - x)) : \
+ (x = 1 - y) \
+ )
+
+/*----- Key unpacking functions -------------------------------------------*/
+
+/* --- @idea_ekeys@ --- *
+ *
+ * Arguments: @idea_key *k@ = the expanded key buffer
+ * @const unsigned char *key@ = the user's key encryption key
+ *
+ * Returns: ---
+ *
+ * Use: Unpacks an encryption key.
+ */
+
+void idea_ekeys(idea_key *k, const unsigned char *key)
+{
+ /* --- Convince compiler to do this properly --- */
+
+ register const int ffff = 0xFFFF;
+
+ uint_32 ka, kb, kc, kd;
+ int count;
+ int *p = k->k;
+
+ /* --- Load the 4 words from the block --- *
+ *
+ * Don't ask.
+ */
+
+ ka = load32(key + 0);
+ kb = load32(key + 4);
+ kc = load32(key + 8);
+ kd = load32(key + 12);
+
+ for (count = 48; count > 0; count -= 8) {
+
+ /* --- Unpack halfwords into the block --- */
+
+ *p++ = (ka >> 16) & ffff;
+ *p++ = ka & ffff;
+ *p++ = (kb >> 16) & ffff;
+ *p++ = kb & ffff;
+ *p++ = (kc >> 16) & ffff;
+ *p++ = kc & ffff;
+ *p++ = (kd >> 16) & ffff;
+ *p++ = kd & ffff;
+
+ /* --- Now rotate the 128-bit key --- */
+
+ {
+ uint_32 kx = ka;
+ ka = ((ka << 25) | (kb >> 7)) & 0xffffffffu;
+ kb = ((kb << 25) | (kc >> 7)) & 0xffffffffu;
+ kc = ((kc << 25) | (kd >> 7)) & 0xffffffffu;
+ kd = ((kd << 25) | (kx >> 7)) & 0xffffffffu;
+ }
+ }
+
+ /* --- Write the tail-enders over --- */
+
+ *p++ = (ka >> 16) & ffff;
+ *p++ = ka & ffff;
+ *p++ = (kb >> 16) & ffff;
+ *p++ = kb & ffff;
+}
+
+/* --- @idea_invertKey@ --- *
+ *
+ * Arguments: @const idea_key *in@ = pointer to input expanded key buffer
+ * @idea_key *out@ = pointer to output expanded key buffer
+ *
+ * Returns: ---
+ *
+ * Use: Computes the inverse (decryption) key given an expanded
+ * IDEA encryption key.
+ */
+
+void idea_invertKey(const idea_key *in, idea_key *out)
+{
+ int i;
+ unsigned a, b, c, d, e, f;
+ int *ibuf = in->k, *obuf = out->k;
+
+ /* --- Deal with identical input and output buffers --- */
+
+ if (in == out) {
+ idea_key t;
+ memcpy(&t, in, sizeof(t));
+ idea_invertKey(&t, out);
+ return;
+ }
+
+ /* --- Do the real work --- */
+
+ ibuf += IDEA_EXPKEYSIZE;
+ for (i = 8; i; i--) {
+ ibuf -= 6;
+ a = ibuf[0];
+ b = ibuf[1];
+ c = ibuf[2];
+ d = ibuf[3];
+ e = ibuf[4];
+ f = ibuf[5];
+
+ c = idea__inv(c);
+ f = idea__inv(f);
+ d = 0x10000 - d;
+ e = 0x10000 - e;
+
+ if (i < 8)
+ d ^= e, e ^= d, d ^= e;
+
+ obuf[0] = c;
+ obuf[1] = d;
+ obuf[2] = e;
+ obuf[3] = f;
+ obuf[4] = a;
+ obuf[5] = b;
+ obuf += 6;
+ }
+
+ /* --- Deal with the tail-enders --- */
+
+ ibuf -= 4;
+ c = ibuf[0];
+ d = ibuf[1];
+ e = ibuf[2];
+ f = ibuf[3];
+
+ c = idea__inv(c);
+ f = idea__inv(f);
+ d = 0x10000 - d;
+ e = 0x10000 - e;
+
+ obuf[0] = c;
+ obuf[1] = d;
+ obuf[2] = e;
+ obuf[3] = f;
+}
+
+/* --- @idea_dkeys@ --- *
+ *
+ * Arguments: @idea_key *k@ = the expanded key buffer
+ * @const unsigned char *key@ = the user's key encryption key
+ *
+ * Returns: ---
+ *
+ * Use: Unpacks a decryption key.
+ */
+
+void idea_dkeys(idea_key *k, const unsigned char *key)
+{
+ idea_key t;
+ idea_ekeys(&t, key);
+ idea_invertKey(&t, k);
+}
+
+/*----- Main IDEA cipher --------------------------------------------------*/
+
+/* --- @idea_encrypt@ --- *
+ *
+ * Arguments: @const idea_key *k@ = key to use
+ * @const void *src@ = block to encrypt
+ * @void *dest@ = where to store the result
+ *
+ * Returns: ---
+ *
+ * Use: Encrypts (or decrypts) a block, using the IDEA cryptosystem.
+ * Since the decryption operation is the same as encryption
+ * except that a different key buffer is used, this is all we
+ * need to complete the simple bits.
+ *
+ * For people following this at home: I've been very sloppy
+ * about chopping off excess bits from the ints here. Most of
+ * the time it doesn't matter, and when it does, in the
+ * multiplication stage, the macro does this for us.
+ *
+ * Our @register const int ffff@ makes another appearance. This
+ * might suggest to compilers that having this constant
+ * available would be beneficial.
+ *
+ * Registers are in short supply here. So is legibility.
+ */
+
+#if defined(TEST_RIG) && defined(DUMPROUNDS)
+# define _dump(a,b,c,d) \
+ printf(" %5lu %5lu %5lu %5lu\n", \
+ a & ffff, b & ffff, c & ffff, d & ffff)
+#else
+# define _dump(a,b,c,d) ((void)0)
+#endif
+
+#define _round(a, b, c, d) do { \
+ _dump(a, b, c, d); \
+ u = kp[0]; v = kp[1]; w = kp[2]; x = kp[3]; y = kp[4]; z = kp[5]; \
+ kp += 6; \
+ _mul(a, u); b += v; c += w; _mul(d, x); \
+ u = a ^ c; v = b ^ d; _mul(u, y); v += u; _mul(v, z); u += v; \
+ a ^= v; b ^= u; c ^= v; d ^= u; \
+ _dump(a, b, c, d); \
+} while (0) \
+
+void idea_encrypt(const idea_key *k, const void *src, void *dest)
+{
+ register const int ffff = 0xFFFF;
+ const unsigned char *usrc = src;
+ unsigned char *udest = dest;
+ int *kp = k->k;
+
+ uint_32 a, b, c, d;
+ uint_32 u, v, w, x, y, z;
+
+ /* --- Unpack next block into registers --- */
+
+ a = (usrc[0] << 8) | usrc[1];
+ b = (usrc[2] << 8) | usrc[3];
+ c = (usrc[4] << 8) | usrc[5];
+ d = (usrc[6] << 8) | usrc[7];
+
+ /* --- Now run the block through the eight rounds --- *
+ *
+ * Notice how the arguments swap around so as I don't have to move the
+ * values about.
+ */
+
+ _round(a, b, c, d);
+ _round(a, c, b, d);
+ _round(a, b, c, d);
+ _round(a, c, b, d);
+
+ _round(a, b, c, d);
+ _round(a, c, b, d);
+ _round(a, b, c, d);
+ _round(a, c, b, d);
+
+ /* --- Do the output transformation --- */
+
+ u = kp[0];
+ v = kp[1];
+ w = kp[2];
+ x = kp[3];
+ _mul(a, u);
+ b += w;
+ c += v;
+ _mul(d, x);
+
+ /* --- Repack and store the block --- */
+
+ udest[0] = (a >> 8) & 0xFF; udest[1] = a & 0xFF;
+ udest[2] = (c >> 8) & 0xFF; udest[3] = c & 0xFF;
+ udest[4] = (b >> 8) & 0xFF; udest[5] = b & 0xFF;
+ udest[6] = (d >> 8) & 0xFF; udest[7] = d & 0xFF;
+}
+
+/*----- Debugging driver --------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+#define TESTENCRYPTION
+
+void dumpbuf(int *k)
+{
+ int i;
+ printf("Round ");
+ for (i = 1; i <= 6; i++)
+ printf("%5i ", i);
+ for (i = 0; i < 52; i++) {
+ if (i % 6 == 0)
+ printf("\n %i ", i / 6 + 1);
+ printf("%5i ", *k++);
+ }
+ printf("\n\n");
+}
+
+void dumpblk(char *bb)
+{
+ unsigned char *b = (unsigned char *)bb;
+ printf("++ %5u %5u %5u %5u\n",
+ (b[0]<<8)|b[1],
+ (b[2]<<8)|b[3],
+ (b[4]<<8)|b[5],
+ (b[6]<<8)|b[7]);
+}
+
+int main(void)
+{
+
+#ifdef TESTMULTIPLY
+ {
+ unsigned int i, j;
+ char buf[256];
+ int ffff = 0xFFFF;
+ for (;;) {
+ gets(buf);
+ if (!buf[0])
+ break;
+ sscanf(buf, "%u%u", &i, &j);
+ _mul(i, j);
+ printf("%u\n", i);
+ }
+ }
+#endif
+
+#ifdef TESTENCRYPTION
+ {
+ int i;
+ int f;
+
+ unsigned char k[] = { 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8 };
+ idea_key e, d;
+ unsigned char b[] = { 0, 0, 0, 1, 0, 2, 0, 3 };
+
+ static idea_key correct_e = { {
+ 1, 2, 3, 4, 5, 6,
+ 7, 8, 1024, 1536, 2048, 2560,
+ 3072, 3584, 4096, 512, 16, 20,
+ 24, 28, 32, 4, 8, 12,
+ 10240, 12288, 14336, 16384, 2048, 4096,
+ 6144, 8192, 112, 128, 16, 32,
+ 48, 64, 80, 96, 0, 8192,
+ 16384, 24576, 32768, 40960, 49152, 57345,
+ 128, 192, 256, 320
+ } };
+
+ static idea_key correct_d = { {
+ 65025, 65344, 65280, 26010, 49152, 57345,
+ 65533, 32768, 40960, 52428, 0, 8192,
+ 42326, 65456, 65472, 21163, 16, 32,
+ 21835, 65424, 57344, 65025, 2048, 4096,
+ 13101, 51200, 53248, 65533, 8, 12,
+ 19115, 65504, 65508, 49153, 16, 20,
+ 43670, 61440, 61952, 65409, 2048, 2560,
+ 18725, 64512, 65528, 21803, 5, 6,
+ 1, 65534, 65533, 49153
+ } };
+
+ static unsigned char correct_encrypt[] = {
+ 4603 / 256, 4603 % 256,
+ 60715 / 256, 60715 % 256,
+ 408 / 256, 408 % 256,
+ 28133 / 256, 28133 % 256
+ };
+
+ static unsigned char correct_decrypt[] = {
+ 0, 0, 0, 1, 0, 2, 0, 3
+ };
+
+ idea_ekeys(&e, k);
+ dumpbuf(e.k);
+
+ f = 1;
+ for (i = 0; i < IDEA_EXPKEYSIZE; i++) {
+ if (e.k[i] != correct_e.k[i]) {
+ f = 0;
+ printf("!!! bad encryption key values!\n\n");
+ }
+ }
+ if (f)
+ printf("*** expanded encryption key correct\n\n");
+
+ idea_dkeys(&d, k);
+ dumpbuf(d.k);
+
+ f = 1;
+ for (i = 0; i < IDEA_EXPKEYSIZE; i++) {
+ if (d.k[i] != correct_d.k[i]) {
+ f = 0;
+ printf("!!! bad decryption key values!\n\n");
+ }
+ }
+ if (f)
+ printf("*** expanded decryption key correct\n\n");
+
+ idea_encrypt(&e, b, b);
+ dumpblk(b);
+ if (memcmp(b, correct_encrypt, 8) == 0)
+ printf("*** correct encipherment\n\n");
+ else
+ printf("!!! bad encipherment\n\n");
+
+ idea_encrypt(&d, b, b);
+ dumpblk(b);
+ if (memcmp(b, correct_decrypt, 8) == 0)
+ printf("*** correct decipherment\n");
+ else
+ printf("!!! bad decipherment\n");
+ }
+#endif
+
+ return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: idea.h,v 1.1 1997/07/21 13:47:48 mdw Exp $
+ *
+ * IDEA encryption routines
+ * Based on Straylight ARM assembler routines
+ *
+ * (c) 1996, 1997 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: idea.h,v $
+ * Revision 1.1 1997/07/21 13:47:48 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef IDEA_H
+#define IDEA_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#ifndef CONFIG_H
+# include "config.h"
+#endif
+
+/*----- Useful constants --------------------------------------------------*/
+
+#define IDEA_BLKSIZE (8u) /* Number of bytes in IDEA block */
+#define IDEA_KEYSIZE (16u) /* Number of bytes in IDEA key */
+#define IDEA_EXPKEYSIZE (52u) /* Number of ints in expanded key */
+
+/*----- Type definitions --------------------------------------------------*/
+
+typedef struct idea_key {
+ int k[IDEA_EXPKEYSIZE]; /* Subkey array */
+} idea_key;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @idea_ekeys@ --- *
+ *
+ * Arguments: @idea_key *k@ = the expanded key buffer
+ * @const unsigned char *key@ = the user's key encryption key
+ *
+ * Returns: ---
+ *
+ * Use: Unpacks an encryption key.
+ */
+
+extern void idea_ekeys(idea_key */*k*/, const unsigned char */*key*/);
+
+/* --- @idea_invertKey@ --- *
+ *
+ * Arguments: @const idea_key *in@ = pointer to input expanded key buffer
+ * @idea_key *out@ = pointer to output expanded key buffer
+ *
+ * Returns: ---
+ *
+ * Use: Computes the inverse (decryption) key given an expanded
+ * IDEA encryption key.
+ */
+
+extern void idea_invertKey(const idea_key */*in*/, idea_key */*out*/);
+
+/* --- @idea_dkeys@ --- *
+ *
+ * Arguments: @idea_key *k@ = the expanded key buffer
+ * @const unsigned char *key@ = the user's key encryption key
+ *
+ * Returns: ---
+ *
+ * Use: Unpacks a decryption key.
+ */
+
+extern void idea_dkeys(idea_key */*k*/, const unsigned char */*key*/);
+
+/* --- @idea_encrypt@ --- *
+ *
+ * Arguments: @const idea_key *k@ = key to use
+ * @const void *src@ = block to encrypt
+ * @void *dest@ = where to store the result
+ *
+ *
+ * Returns: ---
+ *
+ * Use: Encrypts (or decrypts) a block, using the IDEA cryptosystem.
+ * Since the decryption operation is the same as encryption
+ * except that a different key buffer is used, this is all we
+ * need to complete the simple bits.
+ */
+
+extern void idea_encrypt(const idea_key */*k*/,
+ const void */*src*/, void */*dest*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: keygen.c,v 1.1 1997/07/21 13:47:48 mdw Exp $
+ *
+ * Key generation
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: keygen.c,v $
+ * Revision 1.1 1997/07/21 13:47:48 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Unix headers --- */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <fcntl.h>
+
+#include <termios.h>
+#include <unistd.h>
+
+/* --- Local headers --- */
+
+#include "config.h"
+#include "tx.h"
+#include "mdwopt.h"
+#include "utils.h"
+
+/*----- Static variables --------------------------------------------------*/
+
+static struct termios kg__raw, kg__old; /* Terminal settings */
+static int kg__tty; /* File handle for the terminal */
+static FILE *kg__ttyfp; /* Stream pointer for terminal */
+static unsigned int kg__flags; /* Various interesting flags */
+
+enum {
+ kgFlag__cbreak = 1 /* Terminal is in cbreak mode */
+};
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @kg__cbreak@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Makes the terminal return characters as soon as they're
+ * asked for.
+ */
+
+void kg__cbreak(void)
+{
+ /* --- Don't do this if I don't have to --- */
+
+ if (kg__flags & kgFlag__cbreak)
+ return;
+
+ /* --- Fetch the old attributes, and remember them --- */
+
+ if (tcgetattr(kg__tty, &kg__old))
+ die("couldn't read terminal attributes: %s", strerror(errno));
+ memcpy(&kg__raw, &kg__old, sizeof(kg__raw));
+
+ /* --- Now modify them for raw mode --- */
+
+ kg__raw.c_lflag &= ~(ICANON | ECHO);
+ kg__raw.c_cc[VTIME] = 0;
+ kg__raw.c_cc[VMIN] = 1;
+
+ /* --- Remember the new state, and away we go --- */
+
+ kg__flags |= kgFlag__cbreak;
+ if (tcsetattr(kg__tty, TCSAFLUSH, &kg__raw))
+ die("couldn't set terminal attributes: %s", strerror(errno));
+}
+
+/* --- @kg__crepair@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Unbreaks a cbroken tty. Obvious, innit?
+ */
+
+static void kg__crepair(void)
+{
+ /* --- Don't do this if I don't have to --- */
+
+ if (~kg__flags & kgFlag__cbreak)
+ return;
+
+ /* --- Reset the old attributes --- */
+
+ tcsetattr(kg__tty, TCSAFLUSH, &kg__old);
+ kg__flags &= ~kgFlag__cbreak;
+}
+
+/* --- @kg__signal@ --- *
+ *
+ * Arguments: @int sig@ = signal number
+ *
+ * Returns: ---
+ *
+ * Use: Tidies up if I get a signal.
+ */
+
+static void kg__signal(int sig)
+{
+ kg__crepair();
+ signal(sig, SIG_DFL);
+ raise(sig);
+}
+
+/* --- @kgFmt__binary@ --- *
+ *
+ * Arguments: @unsigned char *k@ = pointer to key buffer
+ * @size_t bits@ = number of bits to write
+ * @FILE *fp@ = stream to write on
+ *
+ * Returns: ---
+ *
+ * Use: Writes bits on a stream.
+ */
+
+static void kgFmt__binary(unsigned char *k, size_t bits, FILE *fp)
+{
+ fwrite(k, 1, bits / 8, fp);
+}
+
+/* --- @kgFmt__base64@ --- *
+ *
+ * Arguments: @unsigned char *k@ = pointer to key buffer
+ * @size_t bits@ = number of bits to write
+ * @FILE *fp@ = stream to write on
+ *
+ * Returns: ---
+ *
+ * Use: Writes bits on a stream in an encoded way.
+ */
+
+static void kgFmt__base64(unsigned char *k, size_t bits, FILE *fp)
+{
+ static const char xlt[64] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/" };
+ unsigned long b;
+ int ll = 0;
+
+ while (bits > 24) {
+ b = ((k[0] & 0xffu) << 16) | ((k[1] & 0xffu) << 8) | (k[2] & 0xffu);
+ k += 3;
+ bits -= 24;
+ putc(xlt[(b >> 18) & 0x3fu], fp);
+ putc(xlt[(b >> 12) & 0x3fu], fp);
+ putc(xlt[(b >> 6) & 0x3fu], fp);
+ putc(xlt[(b >> 0) & 0x3fu], fp);
+ if ((ll += 4) > 70) {
+ putc('\n', fp);
+ ll = 0;
+ }
+ }
+
+ b = 0;
+ switch (bits) {
+ case 24:
+ b = *k++ & 0xffu;
+ case 16:
+ b = (b << 8) | (*k++ & 0xffu);
+ case 8:
+ b = (b << 8) | (*k++ & 0xffu);
+ }
+ b <<= (24 - bits);
+ putc(xlt[(b >> 18) & 0x3fu], fp);
+ putc(xlt[(b >> 12) & 0x3fu], fp);
+ switch (bits) {
+ case 24:
+ putc(xlt[(b >> 6) & 0x3fu], fp);
+ putc(xlt[(b >> 0) & 0x3fu], fp);
+ break;
+ case 16:
+ putc(xlt[(b >> 6) & 0x3fu], fp);
+ putc('=', fp);
+ break;
+ case 8:
+ fputs("==", fp);
+ break;
+ }
+
+ putc('\n', fp);
+}
+
+/* --- @kg__gen@ --- *
+ *
+ * Arguments: @unsigned char *ui@ = pointer to array to fill in
+ * @size_t sz@ = number of bits to generate
+ *
+ * Returns: ---
+ *
+ * Use: Uses key timings to generate random numbers. Maybe.
+ */
+
+static void kg__gen(unsigned char *ui, size_t sz)
+{
+ int bits = 32 < sz ? 32 : sz;
+ size_t wsz = (sz + 31u) & ~31u;
+ unsigned long last, ldiff = 0;
+ unsigned long a = 0;
+ unsigned long fact = 1000000 / CLOCKS_PER_SEC;
+
+ fprintf(kg__ttyfp,
+"I need to get %i random bits; I'll do this by timing your keypresses.\n"
+"Please type some arbitrary text until I say `done'.\n",
+ sz);
+
+ {
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ last = tv.tv_usec / fact + tv.tv_sec * fact;
+ }
+
+ while (sz) {
+ int useful;
+ uint_32 orr, xor;
+
+ /* --- Print current status --- */
+
+ fprintf(kg__ttyfp, "\r%5i...", sz);
+ fflush(kg__ttyfp);
+
+ /* --- Read the next character --- */
+
+ {
+ char buff[16];
+ if (read(kg__tty, buff, sizeof(buff)) < 0)
+ die("couldn't read from terminal: %s", strerror(errno));
+ }
+
+ /* --- Fiddle with times --- *
+ *
+ * Read the time now. Turn it into 32 bits of useful information, and
+ * find the difference between that and the previous time. Compare this
+ * with the difference between the previous pair of keypresses.
+ */
+
+ {
+ struct timeval tv;
+ uint_32 n, nd;
+
+ gettimeofday(&tv, 0);
+ n = tv.tv_usec / fact + tv.tv_sec * fact;
+ if (!ldiff) {
+ ldiff = n - last;
+ last = n;
+ continue;
+ }
+ nd = n - last;
+ orr = nd | ldiff;
+ xor = nd ^ ldiff;
+ D( printf("\nlast = %08lx, next = %08lx, ldiff = %08lx, nd = %08lx\n",
+ (unsigned long)last, (unsigned long)n,
+ (unsigned long)ldiff, (unsigned long)nd);
+ printf("xor = %08lx\n", (unsigned long)xor); )
+ ldiff = nd;
+ last = n;
+ }
+
+ /* --- Find the useful bits in this value --- *
+ *
+ * Find the least significant set bit in @bowl@ and chop it off. Then
+ * find the most significant set bit and chop that off two. The rest is
+ * probably interesting.
+ */
+
+ {
+ unsigned long i;
+
+ if (!orr || !xor)
+ continue; /* erk! */
+
+ useful = 30;
+
+ i = 0x80000000ul;
+ if (xor & i) {
+ while (i && (xor & i) != 0) {
+ i >>= 1;
+ useful--;
+ }
+ } else {
+ while (i && (xor & i) == 0) {
+ i >>= 1;
+ useful--;
+ }
+ }
+ xor &= ~-i;
+
+ while ((orr & 1) == 0) {
+ useful--;
+ orr >>= 1;
+ xor >>= 1;
+ }
+ xor >>= 1;
+
+ if (useful < 1)
+ continue;
+ }
+
+ /* --- Now add the bits in the mixing bowl to my stash --- *
+ *
+ * There are two cases:
+ *
+ * 1. I have more bits than will fit into the accumulator. Then I must
+ * put as many bits into the accumulator as will fit, store the
+ * accumulator, and remove the spent bits.
+ *
+ * 2. I have too few bits to fit in the accumulator. Then shift them
+ * in and return.
+ */
+
+ while (sz && useful) {
+
+ D( printf("got %i bits, need %i/%i: %8lx\n",
+ useful, bits, sz, (unsigned long)xor); )
+
+ if (useful >= bits) {
+
+ D( printf("shifted acc = %08lx\n"
+ " new bits = %08lx\n"
+ " result = %08lx\n",
+ (unsigned long)(a << bits),
+ (unsigned long)(xor >> (useful - bits)),
+ (unsigned long)((a << bits) | (xor >> (useful - bits)))); )
+
+ a = (a << bits) | (xor >> (useful - bits));
+
+ sz -= bits;
+ wsz -= bits;
+ useful -= bits;
+
+ if (sz == 0) {
+ a <<= wsz;
+ bits = 32 - wsz;
+ } else
+ bits = 32;
+
+ while (bits > 0) {
+ D( printf("writing %02x\n", (a >> 24) & 0xffu); )
+ *ui++ = (a >> 24) & 0xffu;
+ a <<= 8;
+ bits -= 8;
+ }
+ a = 0;
+
+ bits = 32 < sz ? 32 : sz;
+
+ } else {
+
+ D( printf("shifted acc = %08lx\n"
+ " new bits = %08lx\n"
+ " result = %08lx\n",
+ (unsigned long)(a << useful),
+ (unsigned long)(xor),
+ (unsigned long)((a << useful) | (xor))); )
+ a = (a << useful) | xor;
+ bits -= useful;
+ sz -= useful;
+ wsz -= useful;
+ useful = 0;
+ }
+ }
+ }
+
+ fputs("\rDone! \n", kg__ttyfp);
+ putc('\a', kg__ttyfp); fflush(kg__ttyfp);
+ sleep(1);
+}
+
+/* --- @main@ --- *
+ *
+ * Arguments: @int argc@ = number of arguments
+ * @char *argv[]@ = array of arguments
+ *
+ * Returns: Zero if it worked, nonzero if it didn't.
+ *
+ * Use: Generates random numbers from the keyboard.
+ */
+
+int main(int argc, char *argv[])
+{
+ unsigned char *uip;
+ size_t sz = 128;
+ const char *file = 0;
+ FILE *fp;
+
+ /* --- Formats table --- */
+
+ static struct {
+ const char *name;
+ void (*proc)(unsigned char *k, size_t bits, FILE *fp);
+ } format[] = {
+ { "tx", tx_putBits },
+ { "hex", tx_putBits },
+ { "binary", kgFmt__binary },
+ { "base64", kgFmt__base64 },
+ { 0, 0 }
+ };
+
+ void (*fmt)(unsigned char *, size_t, FILE *) = tx_putBits;
+
+ /* --- Explain who I am --- */
+
+ ego(argv[0]);
+
+ /* --- Read arguments --- */
+
+ for (;;) {
+ static struct option opts[] = {
+ { "help", 0, 0, 'h' },
+ { "bits", gFlag_argReq, 0, 'b' },
+ { "output", gFlag_argReq, 0, 'o' },
+ { "format", gFlag_argReq, 0, 'f' },
+ { 0, 0, 0, 0 }
+ };
+
+ int i = mdwopt(argc, argv, "hb:o:f:", opts, 0, 0, 0);
+
+ if (i < 0)
+ break;
+ switch (i) {
+ case 'h':
+ printf(""
+"Usage: %s [--bits=BITS] [--output=FILE] [--format=FORMAT]\n"
+"\n"
+"Generates BITS (by default, 128) random bits by timing keypresses. The\n"
+"resulting number is written to FILE, or standard output in the given\n"
+"format. Formats currently supported are:\n"
+"\n"
+"tx, hex Hexadecimal, with punctuating dashes.\n"
+"binary Raw binary.\n"
+"base64 Base64-encoded binary; the result is printable ASCII.\n",
+ quis());
+ exit(0);
+ break;
+ case 'b':
+ sz = atoi(optarg);
+ if (sz == 0)
+ die("bad number of bits (illegible or zero)");
+ if (sz % 8)
+ die("can only generate a whole number of 8-bit bytes");
+ break;
+ case 'o':
+ file = optarg;
+ break;
+ case 'f':
+ fmt = 0;
+ for (i = 0; format[i].name; i++) {
+ const char *p = format[i].name, *q = optarg;
+ for (;;) {
+ if (*q == 0)
+ break;
+ if (*p != *q)
+ break;
+ p++, q++;
+ }
+ if (!*q) {
+ if (fmt)
+ die("ambiguous format name: `%s'", optarg);
+ fmt = format[i].proc;
+ }
+ }
+ if (!fmt)
+ die("unknown format name: `%s'", optarg);
+ break;
+ case '?':
+ exit(1);
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr, "Usage: %s [--bits=BITS] [--output=FILE]\n", quis());
+ exit(1);
+ }
+
+ /* --- Allocate memory --- */
+
+ uip = xmalloc(sz / 8);
+
+ /* --- Open the terminal --- *
+ *
+ * I'd like to be able to @fprintf@ to the terminal, so use @fopen@.
+ */
+
+ if ((kg__ttyfp = fopen("/dev/tty", "r+")) == 0)
+ die("couldn't open terminal: %s", strerror(errno));
+ kg__tty = fileno(kg__ttyfp);
+
+ /* --- Open the output file, if one is specified --- */
+
+ if (file) {
+ int fd;
+
+ /* --- Open the file oddly --- *
+ *
+ * There's a good reason for this. I want to be able to @fprintf@ (for
+ * the benefit of @tx_putWords@ mainly) but I also want to ensure that
+ * only the user has read permissions for the file. So I'll use @open@
+ * with an appropriate mode and then @fdopen@ the file descriptor to get
+ * a stream. Yuk.
+ */
+
+ if ((fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
+ die("couldn't open output file: %s", strerror(errno));
+ if ((fp = fdopen(fd, "w")) == 0)
+ die("couldn't attach stream to output file: %s", strerror(errno));
+ } else
+ fp = stdout;
+
+ /* --- Tidy up nicely if I die --- */
+
+ signal(SIGINT, kg__signal);
+ signal(SIGTERM, kg__signal);
+ atexit(kg__crepair);
+
+ /* --- Put the terminal into cbreak, read the key, and restore --- */
+
+ kg__cbreak();
+ kg__gen(uip, sz);
+ kg__crepair();
+
+ /* --- Now write the number and exit --- */
+
+ D( fputs("*** ", fp); tx_putBits(uip, sz, stdout); )
+ fmt(uip, sz, fp);
+ if (file)
+ fclose(fp);
+ memset(uip, 0, sz / 8); /* Burn temporary buffer */
+ return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: lexer.h,v 1.1 1997/07/21 13:47:48 mdw Exp $
+ *
+ * Lexical analyser for `become.conf' files
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: lexer.h,v $
+ * Revision 1.1 1997/07/21 13:47:48 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef LEXER_H
+#define LEXER_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#include <stdio.h>
+
+/*----- Global variables --------------------------------------------------*/
+
+extern int lex_line; /* Current line number */
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @yylex@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: The next token, or zero for EOF.
+ *
+ * Use: Scans the input file.
+ */
+
+extern int yylex(void);
+
+/* --- @lexer_scan@ --- *
+ *
+ * Arguments: @FILE *fp@ = pointer to a stream object to scan
+ *
+ * Returns: ---
+ *
+ * Use: Initialises the scanner ready to parse from the given
+ * stream.
+ */
+
+extern void lexer_scan(FILE */*fp*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: lexer.l,v 1.1 1997/07/21 13:47:48 mdw Exp $
+ *
+ * Lexical analyser for `become.conf' files
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: lexer.l,v $
+ * Revision 1.1 1997/07/21 13:47:48 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Declarations section ----------------------------------------------*/
+
+/* --- Header files --- */
+
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "become.h"
+#include "lexer.h"
+#include "parser.h"
+#include "utils.h"
+%}
+
+/* --- Start conditions --- */
+
+%x s_KEYWORD
+%x s_NORMAL
+%x s_STRING
+
+/* --- A handy static buffer --- */
+
+ static char lex__buff[4096];
+ static char *lex__ptr;
+
+/* --- Line number --- */
+
+ int lex_line;
+
+%%
+
+ /*---- Main scanner definition -------------------------------------------*/
+
+ /* --- Comments --- */
+
+<INITIAL,s_KEYWORD,s_NORMAL>{
+ "#".*\n lex_line++;
+
+ /* --- Whitespace --- */
+
+ [ \t] /* munch */
+ \n lex_line++;
+}
+
+ /* --- Keywords --- */
+
+<INITIAL,s_KEYWORD>{
+ user BEGIN(s_NORMAL); return (USER);
+ command BEGIN(s_NORMAL); return (COMMAND);
+ host BEGIN(s_NORMAL); return (HOST);
+ allow BEGIN(s_NORMAL); return (ALLOW);
+ port BEGIN(s_NORMAL); return (PORT);
+ keyfile BEGIN(s_NORMAL); return (KEYFILE);
+ . BEGIN(s_NORMAL); return (BADTOKEN);
+}
+ /* --- Other sorts of tokens --- */
+
+<s_NORMAL>{
+ [0-9]* yylval.i = atoi(yytext); return (INT);
+ [a-zA-Z_][a-zA-Z_0-9]* yylval.s = yytext; return (WORD);
+ \" BEGIN(s_STRING); lex__ptr = lex__buff;
+ ";" BEGIN(s_KEYWORD); return (';');
+ "->" return (ARROW);
+ . return (yytext[0]);
+}
+
+ /* --- Strings and things --- *
+ *
+ * Be a little careful about buffer overflows here.
+ */
+
+<s_STRING>{
+ \\. {
+ if (lex__ptr >
+ lex__buff + sizeof(lex__buff) - 8) {
+ moan("string too long at line %i",
+ lex_line);
+ *lex__ptr++ = 0;
+ yylval.s = lex__buff;
+ BEGIN(s_NORMAL);
+ return (STRING);
+ }
+ *lex__ptr++ = yytext[1];
+ }
+ \" {
+ *lex__ptr++ = 0;
+ yylval.s = lex__buff;
+ BEGIN(s_NORMAL);
+ return (STRING);
+ }
+ \n |
+ <<EOF>> {
+ moan("missing `\"', inserted at line %i",
+ lex_line);
+ lex_line++;
+ *lex__ptr++ = 0;
+ yylval.s = lex__buff;
+ BEGIN(s_NORMAL);
+ return (STRING);
+ }
+ . {
+ if (lex__ptr >
+ lex__buff + sizeof(lex__buff) - 8) {
+ moan("string too long at line %i",
+ lex_line);
+ *lex__ptr++ = 0;
+ yylval.s = lex__buff;
+ BEGIN(s_NORMAL);
+ return (STRING);
+ }
+ *lex__ptr++ = yytext[0];
+ }
+}
+
+%%
+
+/*----- Support routines --------------------------------------------------*/
+
+/* --- @lexer_scan@ --- *
+ *
+ * Arguments: @FILE *fp@ = pointer to a stream object to scan
+ *
+ * Returns: ---
+ *
+ * Use: Initialises the scanner ready to parse from the given
+ * stream.
+ */
+
+void lexer_scan(FILE *fp)
+{
+ yyin = fp;
+ lex_line = 1;
+ BEGIN(INITIAL);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: md5.c,v 1.1 1997/07/21 13:47:47 mdw Exp $
+ *
+ * MD-5 secure hash routines
+ * Based on RSA MD-5 code
+ *
+ * (c) 1996, 1997 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: md5.c,v $
+ * Revision 1.1 1997/07/21 13:47:47 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "config.h"
+#include "md5.h"
+#include "utils.h"
+
+/*----- Define MD-5 transform ---------------------------------------------*/
+
+/* --- Low-level nonlinear functions --- */
+
+#define f(x,y,z) (((x) & (y)) | ((~x) & (z)))
+#define g(x,y,z) (((x) & (z)) | ((y) & (~z)))
+#define h(x,y,z) ((x) ^ (y) ^ (z))
+#define i(x,y,z) ((y) ^ ((x) | (~z)))
+
+#define rol(x,b) (((x) << (b)) | ((x) >> (32-(b))))
+
+/* --- Rotations applied at various stages --- */
+
+#define S11 7u
+#define S12 12u
+#define S13 17
+#define S14 22u
+#define S21 5u
+#define S22 9u
+#define S23 14u
+#define S24 20u
+#define S31 4u
+#define S32 11u
+#define S33 16u
+#define S34 23u
+#define S41 6u
+#define S42 10u
+#define S43 15u
+#define S44 21u
+
+/* --- Higher level nonlinear functions --- */
+
+#define ff(a, b, c, d, x, s, m) ( a += f(b, c, d) + x + m, \
+ a = rol(a, s), a += b )
+
+#define gg(a, b, c, d, x, s, m) ( a += g(b, c, d) + x + m, \
+ a = rol(a, s), a += b )
+
+#define hh(a, b, c, d, x, s, m) ( a += h(b, c, d) + x + m, \
+ a = rol(a, s), a += b )
+
+#define ii(a, b, c, d, x, s, m) ( a += i(b, c, d) + x + m, \
+ a = rol(a, s), a += b )
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @md5__trans@ --- *
+ *
+ * Arguments: @uint_32 *v@ = pointer to chaining variables (updated)
+ * @const unsigned char *buf@ = pointer to a 64-byte block
+ *
+ * Returns: ---
+ *
+ * Use: Performs the main MD5 transform on a block of data.
+ */
+
+static void md5__trans(uint_32 *v, const unsigned char *buf)
+{
+ uint_32 a, b, c, d;
+ uint_32 ib[16];
+ size_t i;
+
+ /* --- Initialise my internal registers --- */
+
+ a = v[0], b = v[1], c = v[2], d = v[3];
+
+ /* --- Turn the buffer into 32-bit words --- */
+
+ for (i = 0; i < 16; i++)
+ ib[i] = load32_l(buf + (i << 2));
+
+ /* --- Round one --- */
+
+ ff(a, b, c, d, ib[ 0], S11, 0xd76aa478); /* 1 */
+ ff(d, a, b, c, ib[ 1], S12, 0xe8c7b756); /* 2 */
+ ff(c, d, a, b, ib[ 2], S13, 0x242070db); /* 3 */
+ ff(b, c, d, a, ib[ 3], S14, 0xc1bdceee); /* 4 */
+ ff(a, b, c, d, ib[ 4], S11, 0xf57c0faf); /* 5 */
+ ff(d, a, b, c, ib[ 5], S12, 0x4787c62a); /* 6 */
+ ff(c, d, a, b, ib[ 6], S13, 0xa8304613); /* 7 */
+ ff(b, c, d, a, ib[ 7], S14, 0xfd469501); /* 8 */
+ ff(a, b, c, d, ib[ 8], S11, 0x698098d8); /* 9 */
+ ff(d, a, b, c, ib[ 9], S12, 0x8b44f7af); /* 10 */
+ ff(c, d, a, b, ib[10], S13, 0xffff5bb1); /* 11 */
+ ff(b, c, d, a, ib[11], S14, 0x895cd7be); /* 12 */
+ ff(a, b, c, d, ib[12], S11, 0x6b901122); /* 13 */
+ ff(d, a, b, c, ib[13], S12, 0xfd987193); /* 14 */
+ ff(c, d, a, b, ib[14], S13, 0xa679438e); /* 15 */
+ ff(b, c, d, a, ib[15], S14, 0x49b40821); /* 16 */
+
+ /* --- Round two --- */
+
+ gg(a, b, c, d, ib[ 1], S21, 0xf61e2562); /* 17 */
+ gg(d, a, b, c, ib[ 6], S22, 0xc040b340); /* 18 */
+ gg(c, d, a, b, ib[11], S23, 0x265e5a51); /* 19 */
+ gg(b, c, d, a, ib[ 0], S24, 0xe9b6c7aa); /* 20 */
+ gg(a, b, c, d, ib[ 5], S21, 0xd62f105d); /* 21 */
+ gg(d, a, b, c, ib[10], S22, 0x02441453); /* 22 */
+ gg(c, d, a, b, ib[15], S23, 0xd8a1e681); /* 23 */
+ gg(b, c, d, a, ib[ 4], S24, 0xe7d3fbc8); /* 24 */
+ gg(a, b, c, d, ib[ 9], S21, 0x21e1cde6); /* 25 */
+ gg(d, a, b, c, ib[14], S22, 0xc33707d6); /* 26 */
+ gg(c, d, a, b, ib[ 3], S23, 0xf4d50d87); /* 27 */
+ gg(b, c, d, a, ib[ 8], S24, 0x455a14ed); /* 28 */
+ gg(a, b, c, d, ib[13], S21, 0xa9e3e905); /* 29 */
+ gg(d, a, b, c, ib[ 2], S22, 0xfcefa3f8); /* 30 */
+ gg(c, d, a, b, ib[ 7], S23, 0x676f02d9); /* 31 */
+ gg(b, c, d, a, ib[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* --- Round three --- */
+
+ hh(a, b, c, d, ib[ 5], S31, 0xfffa3942); /* 33 */
+ hh(d, a, b, c, ib[ 8], S32, 0x8771f681); /* 34 */
+ hh(c, d, a, b, ib[11], S33, 0x6d9d6122); /* 35 */
+ hh(b, c, d, a, ib[14], S34, 0xfde5380c); /* 36 */
+ hh(a, b, c, d, ib[ 1], S31, 0xa4beea44); /* 37 */
+ hh(d, a, b, c, ib[ 4], S32, 0x4bdecfa9); /* 38 */
+ hh(c, d, a, b, ib[ 7], S33, 0xf6bb4b60); /* 39 */
+ hh(b, c, d, a, ib[10], S34, 0xbebfbc70); /* 40 */
+ hh(a, b, c, d, ib[13], S31, 0x289b7ec6); /* 41 */
+ hh(d, a, b, c, ib[ 0], S32, 0xeaa127fa); /* 42 */
+ hh(c, d, a, b, ib[ 3], S33, 0xd4ef3085); /* 43 */
+ hh(b, c, d, a, ib[ 6], S34, 0x04881d05); /* 44 */
+ hh(a, b, c, d, ib[ 9], S31, 0xd9d4d039); /* 45 */
+ hh(d, a, b, c, ib[12], S32, 0xe6db99e5); /* 46 */
+ hh(c, d, a, b, ib[15], S33, 0x1fa27cf8); /* 47 */
+ hh(b, c, d, a, ib[ 2], S34, 0xc4ac5665); /* 48 */
+
+ /* --- Round four --- */
+
+ ii(a, b, c, d, ib[ 0], S41, 0xf4292244); /* 49 */
+ ii(d, a, b, c, ib[ 7], S42, 0x432aff97); /* 50 */
+ ii(c, d, a, b, ib[14], S43, 0xab9423a7); /* 51 */
+ ii(b, c, d, a, ib[ 5], S44, 0xfc93a039); /* 52 */
+ ii(a, b, c, d, ib[12], S41, 0x655b59c3); /* 53 */
+ ii(d, a, b, c, ib[ 3], S42, 0x8f0ccc92); /* 54 */
+ ii(c, d, a, b, ib[10], S43, 0xffeff47d); /* 55 */
+ ii(b, c, d, a, ib[ 1], S44, 0x85845dd1); /* 56 */
+ ii(a, b, c, d, ib[ 8], S41, 0x6fa87e4f); /* 57 */
+ ii(d, a, b, c, ib[15], S42, 0xfe2ce6e0); /* 58 */
+ ii(c, d, a, b, ib[ 6], S43, 0xa3014314); /* 59 */
+ ii(b, c, d, a, ib[13], S44, 0x4e0811a1); /* 60 */
+ ii(a, b, c, d, ib[ 4], S41, 0xf7537e82); /* 61 */
+ ii(d, a, b, c, ib[11], S42, 0xbd3af235); /* 62 */
+ ii(c, d, a, b, ib[ 2], S43, 0x2ad7d2bb); /* 63 */
+ ii(b, c, d, a, ib[ 9], S44, 0xeb86d391); /* 64 */
+
+ /* --- Update the context --- */
+
+ v[0] += a, v[1] += b, v[2] += c, v[3] += d;
+}
+
+/* --- @md5_trans@ --- *
+ *
+ * Arguments: @unsigned char *v@ = pointer to chaining block (updated)
+ * @const unsigned char *buf@ = pointer to input buffer
+ *
+ * Returns: ---
+ *
+ * Use: Performs the MD5 transformation on a chunk of data. This may
+ * be useful for using MD5 in MDC-type cipher constructions.
+ */
+
+void md5_trans(unsigned char *v, const unsigned char *buf)
+{
+ uint_32 vv[4];
+
+ vv[0] = load32_l(v + 0);
+ vv[1] = load32_l(v + 4);
+ vv[2] = load32_l(v + 8);
+ vv[3] = load32_l(v + 12);
+ md5__trans(vv, buf);
+ store32_l(v + 0, vv[0]);
+ store32_l(v + 4, vv[1]);
+ store32_l(v + 8, vv[2]);
+ store32_l(v + 12, vv[3]);
+}
+
+/* --- @md5_buffer@ --- *
+ *
+ * Arguments: @md5 *m@ = pointer to an MD5 context
+ * @const void *buff@ = pointer to buffer of data
+ * @size_t size@ = size of buffer
+ *
+ * Returns: ---
+ *
+ * Use: Hashes the buffer of data. You can call @md5_buffer@
+ * lots of times during an MD5 job, to allow big files to be
+ * split into little ones.
+ */
+
+void md5_buffer(md5 *m, const void *buff, size_t size)
+{
+ long int s = size;
+ const unsigned char *b = buff;
+ unsigned long x, y;
+
+ /* --- Maybe there's some data already in the buffer --- */
+
+ if (x = m->size & 63, x) {
+ y = 64 - x;
+ if (y > s) {
+ memcpy(x + m->buf, b, s);
+ m->size += s;
+ return;
+ }
+ memcpy(x + m->buf, b, y);
+ md5__trans(m->val, m->buf);
+ s -= y, b += y;
+ }
+
+ /* --- Now do whole buffers-full --- */
+
+ while (s >= 64) {
+ md5__trans(m->val, b);
+ s -= 64, b += 64;
+ }
+
+ /* --- Tidy up the tail end --- */
+
+ if (s)
+ memcpy(m->buf, b, s);
+ m->size += size;
+}
+
+/* --- @md5_key@ --- *
+ *
+ * Arguments: @md5 *m@ = pointer to an MD5 context
+ * @const unsigned char *k@ = pointer to a 4-word `key'
+ *
+ * Returns: ---
+ *
+ * Use: Initialises a context buffer, with a chosen initialisation
+ * string (instead of the standard MD5 value). This allows you
+ * to use NMAC message authentication, should the urge take you.
+ */
+
+void md5_key(md5 *m, const unsigned char *k)
+{
+ m->size = 0;
+ m->val[0] = load32_l(k + 0);
+ m->val[0] = load32_l(k + 4);
+ m->val[0] = load32_l(k + 8);
+ m->val[0] = load32_l(k + 12);
+}
+
+/* --- @md5_init@ --- *
+ *
+ * Arguments: @md5 *m@ = pointer to an MD5 context
+ *
+ * Returns: ---
+ *
+ * Use: Initialises the context buffer, so that you can do an
+ * MD5 job.
+ */
+
+void md5_init(md5 *m)
+{
+ m->size = 0;
+ m->val[0] = 0x67452301;
+ m->val[1] = 0xefcdab89;
+ m->val[2] = 0x98badcfe;
+ m->val[3] = 0x10325476;
+}
+
+/* --- @md5_final@ --- *
+ *
+ * Arguments: @md5 *m@ = pointer to context buffer
+ * @unsigned char *v@ = where to store the value
+ *
+ * Returns: ---
+ *
+ * Use: Finalises an MD5 buffer, so that you can use the result.
+ */
+
+void md5_final(md5 *m, unsigned char *v)
+{
+ int s = m->size;
+
+ /* --- Pad out the block --- */
+
+ {
+ const static unsigned char pad[64] = { 0x80 };
+ int p = m->size & 63;
+ p = (p < 56) ? (p = 56 - p) : (p = 120 - p);
+ md5_buffer(m, pad, p);
+ }
+
+ /* --- Append the length --- */
+
+ {
+ unsigned char b[8];
+
+ store32_l(b + 0, s << 3);
+ store32_l(b + 4, s >> 29);
+ md5_buffer(m, b, 8);
+ }
+
+ /* --- Write out the value --- */
+
+ if (v) {
+ store32_l(v + 0, m->val[0]);
+ store32_l(v + 4, m->val[1]);
+ store32_l(v + 8, m->val[2]);
+ store32_l(v + 12, m->val[3]);
+ }
+}
+
+/*----- Test driver -------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+int main(int argc, char *argv[])
+{
+ if (argc > 1 && strcmp(argv[1], "-x") == 0) {
+
+ static struct {
+ const char *string;
+ uint_32 md[4];
+ } table[] = {
+ "", { 0xd98c1dd4, 0x04b2008f, 0x980980e9, 0x7e42f8ec },
+ "a", { 0xb975c10c, 0xa8b6f1c0, 0xe299c331, 0x61267769 },
+ "abc", { 0x98500190, 0xb04fd23c, 0x7d3f96d6, 0x727fe128 },
+ "message digest", { 0x7d696bf9, 0x8d93b77c, 0x312f5a52, 0xd061f1aa },
+ "abcdefghijklmnopqrstuvwxyz",
+ { 0xd7d3fcc3, 0x00e49261, 0x6c49fb7d, 0x3be167ca },
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ { 0x98ab74d1, 0xf5d977d2, 0x2c1c61a5, 0x9f9d419f },
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n",
+ { 0xbaa7652b, 0x5e10cd4a, 0xde9acbf2, 0xfa0b9fbd }
+ };
+ int steptbl[] = { 3, 18, 32, 37, 63, 64, 65, 256, 1024, -1 };
+
+ md5 m;
+ int i, j;
+ const char *p;
+ size_t sz, a, d;
+ int f = 1;
+
+ for (i = 0; i < sizeof(table) / sizeof(table[0]); i++) {
+ sz = strlen(table[i].string);
+ for (j = 0; steptbl[j] < sz && steptbl[j] != -1; j++) {
+ p = table[i].string;
+ d = steptbl[j];
+ md5_init(&m);
+ for (a = sz; a > d; a -= d) {
+ md5_buffer(&m, p, d);
+ p += d;
+ }
+ md5_buffer(&m, p, a);
+ md5_final(&m, 0);
+ if (m.val[0] != table[i].md[0] ||
+ m.val[1] != table[i].md[1] ||
+ m.val[2] != table[i].md[2] ||
+ m.val[3] != table[i].md[3]) {
+ printf("!!! bad value, string == `%s', step size == %i\n"
+ "!!! expected %08lx-%08lx-%08lx-%08lx, found "
+ "%08lx-%08lx-%08lx-%08lx\n\n",
+ table[i].string, d,
+ (unsigned long)table[i].md[0],
+ (unsigned long)table[i].md[1],
+ (unsigned long)table[i].md[2],
+ (unsigned long)table[i].md[3],
+ (unsigned long)m.val[0],
+ (unsigned long)m.val[1],
+ (unsigned long)m.val[2],
+ (unsigned long)m.val[3]);
+ f = 0;
+ }
+ }
+ md5_init(&m);
+ md5_buffer(&m, table[i].string, sz);
+ md5_final(&m, 0);
+ if (m.val[0] != table[i].md[0] ||
+ m.val[1] != table[i].md[1] ||
+ m.val[2] != table[i].md[2] ||
+ m.val[3] != table[i].md[3]) {
+ printf("!!! bad value, string == `%s', step size == entire string\n"
+ "!!! expected %08lx-%08lx-%08lx-%08lx, found "
+ "%08lx-%08lx-%08lx-%08lx\n\n",
+ table[i].string,
+ (unsigned long)table[i].md[0],
+ (unsigned long)table[i].md[1],
+ (unsigned long)table[i].md[2],
+ (unsigned long)table[i].md[3],
+ (unsigned long)m.val[0],
+ (unsigned long)m.val[1],
+ (unsigned long)m.val[2],
+ (unsigned long)m.val[3]);
+ f = 0;
+ } else {
+ printf("`%s' => %08lx-%08lx-%08lx-%08lx\n",
+ table[i].string,
+ (unsigned long)m.val[0],
+ (unsigned long)m.val[1],
+ (unsigned long)m.val[2],
+ (unsigned long)m.val[3]);
+ }
+ }
+
+ } else {
+
+ char buff[4096];
+ md5 m;
+ int i;
+ int read;
+
+ md5_init(&m);
+ while (read = fread(buff, 1, 4096, stdin), read)
+ md5_buffer(&m, buff, read);
+ md5_final(&m, 0);
+
+ for (i = 0; i < 4; i++)
+ printf("%02lx%02lx%02lx%02lx",
+ (unsigned long)( (m.val[i] & 0x000000FF) >> 0 ),
+ (unsigned long)( (m.val[i] & 0x0000FF00) >> 8 ),
+ (unsigned long)( (m.val[i] & 0x00FF0000) >> 16 ),
+ (unsigned long)( (m.val[i] & 0xFF000000) >> 24 ));
+ putc('\n', stdout);
+
+ }
+
+ return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: md5.h,v 1.1 1997/07/21 13:47:47 mdw Exp $
+ *
+ * MD-5 secure hash routines
+ * Based on RSA MD-5 code
+ *
+ * (c) 1996, 1997 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: md5.h,v $
+ * Revision 1.1 1997/07/21 13:47:47 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#include <stddef.h>
+
+#ifndef CONFIG_H
+# include "config.h"
+#endif
+
+/*----- Useful constants --------------------------------------------------*/
+
+#define MD5_HASHSIZE (16u) /* Size of an MD5 hash */
+
+/*----- Type definitions --------------------------------------------------*/
+
+/* --- MD5 context buffer --- */
+
+typedef struct {
+ uint_32 val[4]; /* Result of the hash function */
+ unsigned long size; /* Current size of data in bytes */
+ unsigned char buf[64]; /* Buffer accumulating next block */
+} md5;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @md5_trans@ --- *
+ *
+ * Arguments: @unsigned char *v@ = pointer to chaining block (updated)
+ * @const unsigned char *buf@ = pointer to input buffer
+ *
+ * Returns: ---
+ *
+ * Use: Performs the MD5 transformation on a chunk of data. This may
+ * be useful for using MD5 in MDC-type cipher constructions.
+ */
+
+extern void md5_trans(unsigned char */*v*/, const unsigned char */*buf*/);
+
+/* --- @md5_buffer@ --- *
+ *
+ * Arguments: @md5 *m@ = pointer to an MD5 context
+ * @const void *buff@ = pointer to buffer of data
+ * @size_t size@ = size of buffer
+ *
+ * Returns: ---
+ *
+ * Use: Hashes the buffer of data. You can call md5_buffer
+ * lots of times during an MD5 job, to allow big files to be
+ * split into little ones.
+ *
+ * This routine could be improved lots, to compare with the
+ * Straylight ARM assembler implementation, although that
+ * requires lots of work. Remember that the ARM version
+ * doesn't need to do endianness-fiddling.
+ */
+
+extern void md5_buffer(md5 */*m*/, const void */*buff*/, size_t /*size*/);
+
+/* --- @md5_key@ --- *
+ *
+ * Arguments: @md5 *m@ = pointer to an MD5 context
+ * @const unsigned char *k@ = pointer to a 4-word `key'
+ *
+ * Returns: ---
+ *
+ * Use: Initialises a context buffer, with a chosen initialisation
+ * string (instead of the standard MD5 value). This allows you
+ * to use NMAC message authentication, should the urge take you.
+ */
+
+extern void md5_key(md5 */*m*/, const unsigned char */*k*/);
+
+/* --- @md5_init@ --- *
+ *
+ * Arguments: @md5 *m@ = pointer to an MD5 context
+ *
+ * Returns: ---
+ *
+ * Use: Initialises the context buffer, so that you can do an
+ * MD5 job.
+ */
+
+extern void md5_init(md5 */*m*/);
+
+/* --- @md5_final@ --- *
+ *
+ * Arguments: @md5 *m@ = pointer to context buffer
+ * @unsigned char *v@ = where to store the value
+ *
+ * Returns: ---
+ *
+ * Use: Finalises an MD5 buffer, so that you can use the result.
+ */
+
+extern void md5_final(md5 */*m*/, unsigned char */*v*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: mdwopt.c,v 1.1 1997/07/21 13:47:47 mdw Exp $
+ *
+ * Options parsing, similar to GNU @getopt_long@
+ *
+ * (c) 1996 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of many programs.
+ *
+ * `mdwopt' 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.
+ *
+ * `mdwopt' 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 `mdwopt'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: mdwopt.c,v $
+ * Revision 1.1 1997/07/21 13:47:47 mdw
+ * Initial revision
+ *
+ * Revision 1.3 1997/02/26 00:41:10 mdw
+ * Added GPL notice to the top. Slight formatting changes.
+ *
+ * Revision 1.2 1996/10/28 13:12:13 mdw
+ * Fixed calls to ctype.h routines. Arguments are cast to unsigned char
+ * to avoid invoking undefined behaviour caused by signedness of chars.
+ *
+ * Revision 1.1 1996/09/24 18:01:28 mdw
+ * Initial revision
+ *
+ */
+
+/*----- External dependencies ---------------------------------------------*/
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mdwopt.h"
+
+/*----- Configuration things ----------------------------------------------*/
+
+#if defined(__riscos)
+# define PATHSEP '.'
+#elif defined(__OS2__) || defined(__MSDOS__)
+# define PATHSEP '\\'
+#else /* Assume a sane filing system */
+# define PATHSEP '/'
+#endif
+
+/*----- Global variables --------------------------------------------------*/
+
+mdwopt_data mdwopt_global = {0, 0, 0, 1, 0, 0, 0, 0, 0};
+
+enum {
+ ord__permute = 0, /* Permute the options (default) */
+ ord__return = 1, /* Return non-option things */
+ ord__posix = 2, /* Do POSIX-type hacking */
+ ord__negate = 4 /* Magic negate-next-thing flag */
+};
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @mo__nextWord@ --- *
+ *
+ * Arguments: @int argc@ = number of command line options
+ * @char *argv[]@ = pointer to command line options
+ * @mdwopt_data *data@ = pointer to persistent state
+ *
+ * Returns: Pointer to the next word to handle, or 0
+ *
+ * Use: Extracts the next word from the command line or environment
+ * variable.
+ */
+
+static char *mo__nextWord(int argc, char *const *argv, mdwopt_data *data)
+{
+ if (data->ind == -1) {
+ char *p = data->env;
+ char *q;
+ while (isspace((unsigned char)*p))
+ p++;
+ q = p;
+ while (*p && !isspace((unsigned char)*p))
+ p++;
+ *p = 0;
+ data->env = p + 1;
+ if (p != q)
+ return (q);
+ free(data->estart);
+ data->env = 0;
+ data->ind = 1;
+ }
+
+ if (data->next == argc)
+ return (0);
+ return (argv[data->next++]);
+}
+
+/* --- @mo__permute@ --- *
+ *
+ * Arguments: @char *argv[]@ = pointer to command line arguments
+ * @mdwopt_data *data@ = pointer to persistent data
+ *
+ * Returns: --
+ *
+ * Use: Moves a command line option into the right place.
+ */
+
+static void mo__permute(char *const *argv, mdwopt_data *data)
+{
+ char **v = (char **)argv;
+ if (data->ind != -1) {
+ int i = data->next - 1;
+ char *p = v[i];
+ while (i > data->ind) {
+ v[i] = v[i - 1];
+ i--;
+ }
+ v[i] = p;
+ data->ind++;
+ }
+}
+
+/* --- @mo__findOpt@ --- *
+ *
+ * Arguments: @int o@ = which option to search for
+ * @const char *shortopt@ = short options string to search
+ * @mdwopt_data *data@ = pointer to persistant state
+ *
+ * Returns: Pointer to rest of short options string (including magic
+ * characters)
+ *
+ * Use: Looks up a short option in the given string.
+ */
+
+static const char *mo__findOpt(int o, const char *shortopt,
+ mdwopt_data *data)
+{
+ const char *p = shortopt; /* Point to short opts table */
+ for (;;) {
+ if (!*p) /* No more options left */
+ return (0);
+
+ if (o != *p || (p[1] != '+' && data->order & ord__negate)) {
+ p++; /* Skip this option entry */
+ while (*p == '+') /* Jump a `%|+|%' sign */
+ p++;
+ while (*p == ':') /* And jump any `%|:|%' characters */
+ p++; /* Just in case there are any */
+ }
+ else
+ return (p + 1);
+ }
+}
+
+/* --- @mdwopt@ --- *
+ *
+ * Arguments: @int argc@ = number of command line arguments
+ * @char * const *argv@ = pointer to command line arguments
+ * @const char *shortopt@ = pointer to short options information
+ * @const struct option *longopts@ = pointer to long opts info
+ * @int *longind@ = where to store matched longopt
+ * @mdwopt_data *data@ = persistent state for the parser
+ * @int flags@ = various useful flags
+ *
+ * Returns: Value of option found next, or an error character, or
+ * @EOF@ for the last thing.
+ *
+ * Use: Reads options. The routine should be more-or-less compatible
+ * with standard getopts, although it provides many more
+ * features even than the standard GNU implementation.
+ *
+ * The precise manner of options parsing is determined by
+ * various flag settings, which are described below. By setting
+ * flag values appropriately, you can achieve behaviour very
+ * similar to most other getopt routines.
+ *
+ *
+ * How options parsing appears to users
+ *
+ * A command line consists of a number of `words' (which may
+ * contain spaces, according to various shell quoting
+ * conventions). A word may be an option, an argument to an
+ * option, or a non-option. An option begins with a special
+ * character, usually `%|-|%', although `%|+|%' is also used
+ * sometimes. As special exceptions, the word containing only a
+ * `%|-|%' is considered to be a non-option, since it usually
+ * represents standard input or output as a filename, and the
+ * word containing a double-dash `%|--|%' is used to mark all
+ * following words as being non-options regardless of their
+ * initial character.
+ *
+ * Traditionally, all words after the first non-option have been
+ * considered to be non-options automatically, so that options
+ * must be specified before filenames. However, this
+ * implementation can extract all the options from the command
+ * line regardless of their position. This can usually be
+ * disabled by setting one of the environment variables
+ * `%|POSIXLY_CORRECT|%' or `%|_POSIX_OPTION_ORDER|%'.
+ *
+ * There are two different styles of options: `short' and
+ * `long'.
+ *
+ * Short options are the sort which Unix has known for ages: an
+ * option is a single letter, preceded by a `%|-|%'. Short
+ * options can be joined together to save space (and possibly to
+ * make silly words): e.g., instead of giving options
+ * `%|-x -y|%', a user could write `%|-xy|%'. Some short
+ * options can have arguments, which appear after the option
+ * letter, either immediately following, or in the next `word'
+ * (so an option with an argument could be written as
+ * `%|-o foo|%' or as `%|-ofoo|%'). Note that options with
+ * optional arguments must be written in the second style.
+ *
+ * When a short option controls a flag setting, it is sometimes
+ * possible to explicitly turn the flag off, as well as turning
+ * it on, (usually to override default options). This is
+ * usually done by using a `%|+|%' instead of a `%|-|%' to
+ * introduce the option.
+ *
+ * Long options, as popularised by the GNU utilities, are given
+ * long-ish memorable names, preceded by a double-dash `%|--|%'.
+ * Since their names are more than a single character, long
+ * options can't be combined in the same way as short options.
+ * Arguments to long options may be given either in the same
+ * `word', separated from the option name by an equals sign, or
+ * in the following `word'.
+ *
+ * Long option names can be abbreviated if necessary, as long
+ * as the abbreviation is unique. This means that options can
+ * have sensible and memorable names but still not require much
+ * typing from an experienced user.
+ *
+ * Like short options, long options can control flag settings.
+ * The options to manipulate these settings come in pairs: an
+ * option of the form `%|--set-flag|%' might set the flag, while
+ * an option of the form `%|--no-set-flag|%' might clear it.
+ *
+ * It is usual for applications to provide both short and long
+ * options with identical behaviour. Some applications with
+ * lots of options may only provide long options (although they
+ * will often be only two or three characters long). In this
+ * case, long options can be preceded with a single `%|-|%'
+ * character, and negated by a `%|+|%' character.
+ *
+ * Finally, some (older) programs accept arguments of the form
+ * `%%@.{"-"<number>}%%', to set some numerical parameter,
+ * typically a line count of some kind.
+ *
+ *
+ * How programs parse options
+ *
+ * An application parses its options by calling mdwopt
+ * repeatedly. Each time it is called, mdwopt returns a value
+ * describing the option just read, and stores information about
+ * the option in a data block. The value %$-1$% is returned
+ * when there are no more options to be read. The `%|?|%'
+ * character is returned when an error is encountered.
+ *
+ * Before starting to parse options, the value @data->ind@ must
+ * be set to 0 or 1. The value of @data->err@ can also be set,
+ * to choose whether errors are reported by mdwopt.
+ *
+ * The program's `@argc@' and `@argv@' arguments are passed to
+ * the options parser, so that it can read the command line. A
+ * flags word is also passed, allowing the program fine control
+ * over parsing. The flags are described above.
+ *
+ * Short options are described by a string, which once upon a
+ * time just contained the permitted option characters. Now the
+ * options string begins with a collection of flag characters,
+ * and various flag characters can be put after options
+ * characters to change their properties.
+ *
+ * If the first character of the short options string is
+ * `%|+|%', `%|-|%' or `%|!|%', the order in which options are
+ * read is modified, as follows:
+ *
+ * `%|+|%' forces the POSIX order to be used. As soon as a non-
+ * option is found, mdwopt returns %$-1$%.
+ *
+ * `%|-|%' makes mdwopt treat non-options as being `special'
+ * sorts of option. When a non-option word is found, the
+ * value 0 is returned, and the actual text of the word
+ * is stored as being the option's argument.
+ *
+ * `%|!|%' forces the default order to be used. The entire
+ * command line is scanned for options, which are
+ * returned in order. However, during this process,
+ * the options are moved in the @argv@ array, so that
+ * they appear before the non- options.
+ *
+ * A `%|:|%' character may be placed after the ordering flag (or
+ * at the very beginning if no ordering flag is given) which
+ * indicates that the character `%|:|%', rather than `%|?|%',
+ * should be returned if a missing argument error is detected.
+ *
+ * Each option in the string can be followed by a `%|+|%' sign,
+ * indicating that it can be negated, a `%|:|%' sign indicating
+ * that it requires an argument, or a `%|::|%' string,
+ * indicating an optional argument. Both `%|+|%' and `%|:|%' or
+ * `%|::|%' may be given, although the `%|+|%' must come first.
+ *
+ * If an option is found, the option character is returned to
+ * the caller. A pointer to an argument is stored in
+ * @data->arg@, or @NULL@ is stored if there was no argument.
+ * If a negated option was found, the option character is
+ * returned ORred with @gFlag_negated@ (bit 8 set).
+ *
+ * Long options are described in a table. Each entry in the
+ * table is of type @struct option@, and the table is terminated
+ * by an entry whose @name@ field is null. Each option has
+ * a flags word which, due to historical reasons, is called
+ * @has_arg@. This describes various properties of the option,
+ * such as what sort of argument it takes, and whether it can
+ * be negated.
+ *
+ * When mdwopt finds a long option, it looks the name up in the
+ * table. The index of the matching entry is stored in the
+ * @longind@ variable, passed to mdwopt (unless @longind@ is 0):
+ * a value of %$-1$% indicates that no long option was
+ * found. The behaviour is then dependent on the values in the
+ * table entry. If @flag@ is nonzero, it points to an integer
+ * to be modified by mdwopt. Usually the value in the @val@
+ * field is simply stored in the @flag@ variable. If the flag
+ * @gFlag_switch@ is set, however, the value is combined with
+ * the existing value of the flags using a bitwise OR. If
+ * @gFlag_negate@ is set, then the flag bit will be cleared if a
+ * matching negated long option is found. The value 0 is
+ * returned.
+ *
+ * If @flag@ is zero, the value in @val@ is returned by mdwopt,
+ * possibly with bit 8 set if the option was negated.
+ *
+ * Arguments for long options are stored in @data->arg@, as
+ * before.
+ *
+ * Numeric options, if enabled, cause the value `%|#|%' to be
+ * returned, and the numeric value to be stored in @data->opt@.
+ *
+ * If the flag @gFlag_envVar@ is set on entry, options will be
+ * extracted from an environment variable whose name is built by
+ * capitalising all the letters of the program's name. (This
+ * allows a user to have different default settings for a
+ * program, by calling it through different symbolic links.) */
+
+int mdwopt(int argc, char *const *argv,
+ const char *shortopt,
+ const struct option *longopts, int *longind,
+ mdwopt_data *data, int flags)
+{
+ /* --- Local variables --- */
+
+ char *p, *q, *r; /* Some useful things to have */
+ char *prefix; /* Prefix from this option */
+ int i; /* Always useful */
+ char noarg = '?'; /* Standard missing-arg char */
+
+ /* --- Sort out our data --- */
+
+ if (!data) /* If default data requested */
+ data = &mdwopt_global; /* Then use the global stuff */
+
+ /* --- See if this is the first time --- */
+
+ if (data->ind == 0 || (data->ind == 1 && ~flags & gFlag_noProgName)) {
+
+ /* --- Sort out default returning order --- */
+
+ if (getenv("_POSIX_OPTION_ORDER") || /* Examine environment for opts */
+ getenv("POSIXLY_CORRECT")) /* To see if we disable features */
+ data->order = ord__posix; /* If set, use POSIX ordering */
+ else
+ data->order = ord__permute; /* Otherwise mangle the options */
+
+ /* --- Now see what the caller actually wants --- */
+
+ switch (shortopt[0]) { /* Look at the first character */
+ case '-': /* `%|-|%' turns on in-orderness */
+ data->order = ord__return;
+ break;
+ case '+': /* `%|+|%' turns on POSIXness */
+ data->order = ord__posix;
+ break;
+ case '!': /* `%|!|%' ignores POSIXness */
+ data->order = ord__permute;
+ break;
+ }
+
+ /* --- Now decide on the program's name --- */
+
+ if (~flags & gFlag_noProgName) {
+ p = q = (char *)argv[0];
+ while (*p) {
+ if (*p++ == PATHSEP)
+ q = p;
+ }
+ data->prog = q;
+
+ data->ind = data->next = 1;
+ data->list = 0;
+
+ /* --- See about environment variables --- *
+ *
+ * Be careful. The program may be setuid, and an attacker might have
+ * given us a long name in @argv[0]@. If the name is very long, don't
+ * support this option.
+ */
+
+ if (flags & gFlag_envVar && strlen(data->prog) < 48) {
+
+ char buf[64];
+
+ /* --- For RISC OS, support a different format --- *
+ *
+ * Acorn's RISC OS tends to put settings in variables named
+ * `App$Options' rather than `APP'. Under RISC OS, I'll support
+ * both methods, just to avoid confuddlement.
+ */
+
+#ifdef __riscos
+ sprintf(buf, "%s$Options", data->prog);
+ p = getenv(buf);
+ if (!p) {
+#endif
+
+ p = buf; /* Point to a buffer */
+ q = data->prog; /* Point to program name */
+ while (*q) /* While characters left here */
+ *p++ = toupper(*q++); /* Copy and uppercase */
+ *p++ = 0; /* Terminate my copy of this */
+ p = getenv(buf); /* Get the value of the variable */
+
+#ifdef __riscos
+ }
+#endif
+
+ /* --- Copy the options string into a buffer --- */
+
+ if (p) { /* If it is defined */
+ q = malloc(strlen(p) + 1); /* Allocate space for a copy */
+ if (!q) { /* If that failed */
+ fprintf(stderr, /* Report a nice error */
+ "%s: Not enough memory to read settings in "
+ "environment variable\n",
+ data->prog);
+ } else { /* Otherwise */
+ strcpy(q, p); /* Copy the text over */
+ data->ind = -1; /* Mark that we're parsing envvar */
+ data->env = data->estart = q; /* And store the pointer away */
+ }
+ }
+
+ }
+ }
+ else
+ data->ind = data->next = 0;
+ }
+
+ /* --- Do some initial bodgery --- *
+ *
+ * The @shortopt@ string can have some interesting characters at the
+ * beginning. We'll skip past them.
+ */
+
+ switch (shortopt[0]) {
+ case '+':
+ case '-':
+ case '!':
+ shortopt++;
+ break;
+ }
+
+ if (shortopt[0] == ':') {
+ noarg = shortopt[0];
+ shortopt++;
+ }
+
+ if (longind) /* Allow longind to be null */
+ *longind = -1; /* Clear this to avoid confusion */
+ data->opt = -1; /* And this too */
+ data->arg = 0; /* No option set up here */
+
+ /* --- Now go off and search for an option --- */
+
+ if (!data->list || !*data->list) {
+ data->order &= 3; /* Clear negation flag */
+
+ /* --- Now we need to find the next option --- *
+ *
+ * Exactly how we do this depends on the settings of the order variable.
+ * We identify options as being things starting with `%|-|%', and which
+ * aren't equal to `%|-|%' or `%|--|%'. We'll look for options until:
+ *
+ * * We find something which isn't an option AND @order == ord__posix@
+ * * We find a `%|--|%'
+ * * We reach the end of the list
+ *
+ * There are some added little wrinkles, which we'll meet as we go.
+ */
+
+ for (;;) { /* Keep looping for a while */
+ p = mo__nextWord(argc, argv, data); /* Get the next word out */
+ if (!p) /* If there's no next word */
+ return (EOF); /* There's no more now */
+
+ /* --- See if we've found an option --- */
+
+ if ((p[0] == '-' || (p[0] == '+' && flags & gFlag_negation)) &&
+ p[1] != 0) {
+ if (strcmp(p, "--") == 0) { /* If this is the magic marker */
+ mo__permute(argv, data); /* Stow the magic marker item */
+ return (EOF); /* There's nothing else to do */
+ }
+ break; /* We've found something! */
+ }
+
+ /* --- Figure out how to proceed --- */
+
+ switch (data->order & 3) {
+ case ord__posix: /* POSIX option order */
+ return (EOF); /* This is easy */
+ break;
+ case ord__permute: /* Permute the option order */
+ break;
+ case ord__return: /* Return each argument */
+ mo__permute(argv, data); /* Insert word in same place */
+ data->arg = p; /* Point to the argument */
+ return (0); /* Return the value */
+ }
+ }
+
+ /* --- We found an option --- */
+
+ mo__permute(argv, data); /* Do any permuting necessary */
+
+ /* --- Check for a numeric option --- *
+ *
+ * We only check the first character (or the second if the first is a
+ * sign). This ought to be enough.
+ */
+
+ if (flags & gFlag_numbers && (p[0] == '-' || flags & gFlag_negNumber)) {
+ if (((p[1] == '+' || p[1] == '-') && isdigit((unsigned char)p[2])) ||
+ isdigit((unsigned char)p[1])) {
+ data->opt = strtol(p + 1, &data->arg, 10);
+ while (isspace((unsigned char)data->arg[0]))
+ data->arg++;
+ if (!data->arg[0])
+ data->arg = 0;
+ return (p[0] == '-' ? '#' : '#' | gFlag_negated);
+ }
+ }
+
+ /* --- Check for a long option --- */
+
+ if (p[0] == '+')
+ data->order |= ord__negate;
+
+ if (((p[0] == '-' && p[1] == '-') ||
+ (flags & gFlag_noShorts && !mo__findOpt(p[1], shortopt, data))) &&
+ (~flags & gFlag_noLongs)) /* Is this a long option? */
+ {
+ int match = -1; /* Count matches as we go */
+
+ if (p[0] == '+') { /* If it's negated */
+ data->order |= ord__negate; /* Set the negate flag */
+ p++; /* Point to the main text */
+ prefix = "+"; /* Set the prefix string up */
+ } else if (p[1] == '-') { /* If this is a `%|--|%' option */
+ if ((flags & gFlag_negation) && strncmp(p + 2, "no-", 3) == 0) {
+ p += 5; /* Point to main text */
+ prefix = "--no-"; /* And set the prefix */
+ data->order |= ord__negate; /* Set the negatedness flag */
+ } else {
+ p += 2; /* Point to the main text */
+ prefix = "--"; /* Remember the prefix string */
+ }
+ } else {
+ if ((flags & gFlag_negation) && strncmp(p + 1, "no-", 3) == 0) {
+ p += 4; /* Find the text */
+ prefix = "-no-"; /* Set the prefix */
+ data->order |= ord__negate; /* Set negatedness flag */
+ } else {
+ p++; /* Otherwise find the text */
+ prefix = "-"; /* And remember the prefix */
+ }
+ }
+
+ for (i = 0; longopts[i].name; i++) { /* Loop through the options */
+ if ((data->order & ord__negate) &&
+ (~longopts[i].has_arg & gFlag_negate))
+ continue; /* If neg and opt doesn't allow */
+
+ r = (char *) longopts[i].name; /* Point to the name string */
+ q = p; /* Point to the string start */
+ for (;;) { /* Do a loop here */
+ if (*q == 0 || *q == '=') { /* End of the option string? */
+ if (*r == 0) { /* If end of other string */
+ match = i; /* This is the match */
+ goto botched; /* And exit the loop now */
+ }
+ if (match == -1) { /* If no match currently */
+ match = i; /* Then this is it, here */
+ break; /* Stop looking now */
+ } else {
+ match = -1; /* Else it's ambiguous */
+ goto botched; /* So give up right now */
+ }
+ }
+ else if (*q != *r) /* Otherwise if mismatch */
+ break; /* Abort this loop */
+ q++, r++; /* Increment the counters */
+ }
+ }
+
+ botched:
+ if (match == -1) { /* If we couldn't find a match */
+ if (data->err) {
+ fprintf(stderr, "%s: unrecognised option `%s%s'\n",
+ data->prog,
+ prefix, p);
+ }
+ return ('?');
+ }
+
+ if (longind) /* Allow longind to be null */
+ *longind = match; /* Store the match away */
+
+ /* --- Handle argument behaviour --- */
+
+ while (*p != 0 && *p != '=') /* Find the argument string */
+ p++;
+ p = (*p ? p + 1 : 0); /* Sort out argument presence */
+ q = (char *) longopts[match].name; /* Remember the name here */
+
+ switch (longopts[match].has_arg & 3) {
+ case no_argument:
+ if (p) {
+ if (data->err) {
+ fprintf(stderr,
+ "%s: option `%s%s' does not accept arguments\n",
+ data->prog,
+ prefix, q);
+ }
+ return ('?');
+ }
+ break;
+
+ case required_argument:
+ if (!p) { /* If no argument given */
+ p = mo__nextWord(argc, argv, data);
+
+ if (!p) { /* If no more arguments */
+ if (data->err) {
+ fprintf(stderr, "%s: option `%s%s' requires an argument\n",
+ data->prog,
+ prefix, q);
+ }
+ return (noarg);
+ }
+
+ mo__permute(argv, data);
+ }
+ break;
+
+ case optional_argument:
+ /* Who cares? */
+ break;
+ }
+ data->arg = p;
+
+ /* --- Do correct things now we have a match --- */
+
+ if (longopts[match].flag) { /* If he has a @flag@ argument */
+ if (longopts[match].has_arg & gFlag_switch) {
+ if (data->order & ord__negate)
+ *longopts[match].flag &= ~longopts[match].val;
+ else
+ *longopts[match].flag |= longopts[match].val;
+ } else {
+ if (data->order & ord__negate)
+ *longopts[match].flag = 0;
+ else
+ *longopts[match].flag = longopts[match].val;
+ }
+ return (0); /* And return something */
+ } else {
+ if (data->order & ord__negate)
+ return (longopts[match].val | gFlag_negated);
+ else
+ return (longopts[match].val);
+ }
+ }
+
+ /* --- Do short options things --- */
+
+ else {
+ if (p[0] == '+') /* If starts with a `%|+|%' */
+ data->order |= ord__negate;
+ data->list = p + 1; /* Omit leading `%|-|%'/`%|+|%' */
+ }
+ }
+
+ /* --- Now process the short options --- */
+
+ i = *data->list++; /* Get the next option letter */
+ data->opt = i; /* Store this away nicely */
+
+ p = (char *) mo__findOpt(i, shortopt, data);
+ if (!p) { /* No more options left */
+ if (data->err) {
+ fprintf(stderr, "%s: unknown option `%c%c'\n",
+ data->prog,
+ data->order & ord__negate ? '+' : '-',
+ i);
+ }
+ return ('?');
+ }
+
+ data->opt = i; /* Store this for the caller */
+
+ /* --- Sort out an argument, if we expect one --- */
+
+ if (p[0] == ':') { /* If we expect an option */
+ q = (data->list[0] ? data->list : 0); /* If argument expected, use it */
+ data->list = 0; /* Kill the remaining options */
+ if (p[1] != ':' && !q) { /* If no arg, and not optional */
+
+ /* --- Same code as before --- */
+
+ q = mo__nextWord(argc, argv, data); /* Read the next word */
+ if (!q) { /* If no more arguments */
+ if (data->err) {
+ fprintf(stderr, "%s: option `%c%c' requires an argument\n",
+ data->prog,
+ data->order & ord__negate ? '+' : '-',
+ i);
+ }
+ return (noarg);
+ }
+ mo__permute(argv, data);
+ }
+
+ data->arg = q;
+ }
+ return ((data->order & ord__negate) ? i | gFlag_negated : i);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: mdwopt.h,v 1.1 1997/07/21 13:47:47 mdw Exp $
+ *
+ * Options parsing, similar to GNU @getopt_long@
+ *
+ * (c) 1996 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of many programs.
+ *
+ * `mdwopt' 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.
+ *
+ * `mdwopt' 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 `mdwopt'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: mdwopt.h,v $
+ * Revision 1.1 1997/07/21 13:47:47 mdw
+ * Initial revision
+ *
+ * Revision 1.4 1997/02/26 00:39:55 mdw
+ * Added GPL notice to the top. Slight formatting changes. Commented out
+ * arguments to functions.
+ *
+ * Revision 1.3 1996/12/31 19:41:33 mdw
+ * Formatting changes.
+ *
+ * Revision 1.2 1996/11/23 00:47:25 mdw
+ * Added `MdwOpt' object from the `anagram' source code.
+ *
+ * Revision 1.1 1996/09/24 18:01:43 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef MDWOPT_H
+#define MDWOPT_H
+
+/*----- Options handling structures ---------------------------------------*/
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* --- @mdwopt_data@ --- *
+ *
+ * Contains all the information needed by the @mdwopt@ routine to do its
+ * work.
+ */
+
+typedef struct {
+ /* --- Public variables --- */
+
+ char *arg; /* Arg of current option, or 0 */
+ int opt; /* Value of current option */
+ int ind; /* 0 for init, index when done */
+ int err; /* Set nonzero for error messages */
+ char *prog; /* Program name (from @argv[0]@) */
+
+ /* --- Private variables --- *
+ *
+ * Don't play with these, please.
+ */
+
+ char *list; /* Current short options pointer */
+ int next; /* Next argument, unpermuted */
+ int order; /* Ordering of options, flags */
+ char *env; /* Where we are in the env var */
+ char *estart; /* Pointer to env var buffer */
+}
+mdwopt_data;
+
+/*----- Global variables --------------------------------------------------*/
+
+extern mdwopt_data mdwopt_global; /* The default global data */
+
+/* --- For compatibility with older programs (and prettiness) --- *
+ *
+ * The macros here access the global structure defined above. I consider it
+ * to be perfectly acceptable to use these macros in new code, because it
+ * looks nicer than playing with @mdwopt_global@.
+ */
+
+#define optarg (mdwopt_global.arg) /* Argument of current option */
+#define optopt (mdwopt_global.opt) /* Code of current option */
+#define opterr (mdwopt_global.err) /* Zero to report error messages */
+#define optind (mdwopt_global.ind) /* Index of first non-option */
+#define optprog (mdwopt_global.prog) /* Pointer to program name */
+
+/*----- Type definitions --------------------------------------------------*/
+
+/* --- Long options definition table --- */
+
+struct option {
+ const char *name; /* Name of the long option */
+ int has_arg; /* Does it have an argument? */
+ int *flag; /* Address of flag variable */
+ int val; /* Value to store/return */
+};
+
+/* --- Old-style names for argument flags in long options table --- */
+
+enum {
+ no_argument, /* No argument required */
+ required_argument, /* User must specify argument */
+ optional_argument /* Argument is optional */
+};
+
+/* --- New style flag names --- */
+
+enum {
+ gFlag_argReq = 1, /* Required argument */
+ gFlag_argOpt = 2, /* Optional argument */
+ gFlag_switch = 4, /* OR val into flag, don't store */
+ gFlag_negate = 8, /* Allow long option to be negated */
+ gFlag__last_long_opt_flag = 0 /* Dummy value */
+};
+
+enum {
+ gFlag_noLongs = 1, /* Don't read long options */
+ gFlag_noShorts = 2, /* Don't read short options */
+ gFlag_numbers = 4, /* Read numeric options */
+ gFlag_negation = 8, /* Allow `%|+|%' for negations */
+ gFlag_envVar = 16, /* Parse options from env var */
+ gFlag_noProgName = 32, /* Don't set @optprog@ */
+ gFlag_negNumber = 64, /* Allow negated number options */
+ gFlag__last_mdwopt_flag = 0 /* Dummy value */
+};
+
+enum {
+ gFlag_negated = 256, /* Option flag was negated by user */
+ gFlag__last_return_flag = 0 /* Dummy value */
+};
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @mdwopt@ --- *
+ *
+ * Arguments: @int argc@ = number of command line arguments
+ * @char * const *argv@ = pointer to command line arguments
+ * @const char *shortopt@ = pointer to short options information
+ * @const struct option *longopts@ = pointer to long opts info
+ * @int *longind@ = where to store matched longopt
+ * @mdwopt_data *data@ = persistent state for the parser
+ * @int flags@ = various useful flags
+ *
+ * Returns: Value of option found next, or an error character, or
+ * @EOF@ for the last thing.
+ *
+ * Use: Reads options. The routine should be more-or-less compatible
+ * with standard getopts, although it provides many more
+ * features even than the standard GNU implementation.
+ *
+ * The precise manner of options parsing is determined by
+ * various flag settings, which are described below. By setting
+ * flag values appropriately, you can achieve behaviour very
+ * similar to most other getopt routines.
+ *
+ *
+ * How options parsing appears to users
+ *
+ * A command line consists of a number of `words' (which may
+ * contain spaces, according to various shell quoting
+ * conventions). A word may be an option, an argument to an
+ * option, or a non-option. An option begins with a special
+ * character, usually `%|-|%', although `%|+|%' is also used
+ * sometimes. As special exceptions, the word containing only a
+ * `%|-|%' is considered to be a non-option, since it usually
+ * represents standard input or output as a filename, and the
+ * word containing a double-dash `%|--|%' is used to mark all
+ * following words as being non-options regardless of their
+ * initial character.
+ *
+ * Traditionally, all words after the first non-option have been
+ * considered to be non-options automatically, so that options
+ * must be specified before filenames. However, this
+ * implementation can extract all the options from the command
+ * line regardless of their position. This can usually be
+ * disabled by setting one of the environment variables
+ * `%|POSIXLY_CORRECT|%' or `%|_POSIX_OPTION_ORDER|%'.
+ *
+ * There are two different styles of options: `short' and
+ * `long'.
+ *
+ * Short options are the sort which Unix has known for ages: an
+ * option is a single letter, preceded by a `%|-|%'. Short
+ * options can be joined together to save space (and possibly to
+ * make silly words): e.g., instead of giving options
+ * `%|-x.-y|%', a user could write `%|-xy|%'. Some short
+ * options can have arguments, which appear after the option
+ * letter, either immediately following, or in the next `word'
+ * (so an option with an argument could be written as
+ * `%|-o foo|%' or as `%|-ofoo|%'). Note that options with
+ * optional arguments must be written in the second style.
+ *
+ * When a short option controls a flag setting, it is sometimes
+ * possible to explicitly turn the flag off, as well as turning
+ * it on, (usually to override default options). This is
+ * usually done by using a `%|+|%' instead of a `%|-|%' to
+ * introduce the option.
+ *
+ * Long options, as popularised by the GNU utilities, are given
+ * long-ish memorable names, preceded by a double-dash `%|--|%'.
+ * Since their names are more than a single character, long
+ * options can't be combined in the same way as short options.
+ * Arguments to long options may be given either in the same
+ * `word', separated from the option name by an equals sign, or
+ * in the following `word'.
+ *
+ * Long option names can be abbreviated if necessary, as long
+ * as the abbreviation is unique. This means that options can
+ * have sensible and memorable names but still not require much
+ * typing from an experienced user.
+ *
+ * Like short options, long options can control flag settings.
+ * The options to manipulate these settings come in pairs: an
+ * option of the form `%|--set-flag|%' might set the flag, while
+ * an option of the form `%|--no-set-flag|%' might clear it.
+ *
+ * It is usual for applications to provide both short and long
+ * options with identical behaviour. Some applications with
+ * lots of options may only provide long options (although they
+ * will often be only two or three characters long). In this
+ * case, long options can be preceded with a single `%|-|%'
+ * character, and negated by a `%|+|%' character.
+ *
+ * Finally, some (older) programs accept arguments of the form
+ * `%%@.{"-"<number>}%%', to set some numerical parameter,
+ * typically a line count of some kind.
+ *
+ *
+ * How programs parse options
+ *
+ * An application parses its options by calling mdwopt
+ * repeatedly. Each time it is called, mdwopt returns a value
+ * describing the option just read, and stores information about
+ * the option in a data block. The value %$-1$% is returned
+ * when there are no more options to be read. The `%|?|%'
+ * character is returned when an error is encountered.
+ *
+ * Before starting to parse options, the value @data->ind@ must
+ * be set to 0 or 1. The value of @data->err@ can also be set,
+ * to choose whether errors are reported by mdwopt.
+ *
+ * The program's `@argc@' and `@argv@' arguments are passed to
+ * the options parser, so that it can read the command line. A
+ * flags word is also passed, allowing the program fine control
+ * over parsing. The flags are described above.
+ *
+ * Short options are described by a string, which once upon a
+ * time just contained the permitted option characters. Now the
+ * options string begins with a collection of flag characters,
+ * and various flag characters can be put after options
+ * characters to change their properties.
+ *
+ * If the first character of the short options string is
+ * `%|+|%', `%|-|%' or `%|!|%', the order in which options are
+ * read is modified, as follows:
+ *
+ * `%|+|%' forces the POSIX order to be used. As soon as a non-
+ * option is found, mdwopt returns %$-1$%.
+ *
+ * `%|-|%' makes mdwopt treat non-options as being `special'
+ * sorts of option. When a non-option word is found, the
+ * value 0 is returned, and the actual text of the word
+ * is stored as being the option's argument.
+ *
+ * `%|!|%' forces the default order to be used. The entire
+ * command line is scanned for options, which are
+ * returned in order. However, during this process,
+ * the options are moved in the @argv@ array, so that
+ * they appear before the non- options.
+ *
+ * A `%|:|%' character may be placed after the ordering flag (or
+ * at the very beginning if no ordering flag is given) which
+ * indicates that the character `%|:|%', rather than `%|?|%',
+ * should be returned if a missing argument error is detected.
+ *
+ * Each option in the string can be followed by a `%|+|%' sign,
+ * indicating that it can be negated, a `%|:|%' sign indicating
+ * that it requires an argument, or a `%|::|%' string,
+ * indicating an optional argument. Both `%|+|%' and `%|:|%' or
+ * `%|::|%' may be given, although the `%|+|%' must come first.
+ *
+ * If an option is found, the option character is returned to
+ * the caller. A pointer to an argument is stored in
+ * @data->arg@, or @NULL@ is stored if there was no argument.
+ * If a negated option was found, the option character is
+ * returned ORred with @gFlag_negated@ (bit 8 set).
+ *
+ * Long options are described in a table. Each entry in the
+ * table is of type @struct option@, and the table is terminated
+ * by an entry whose @name@ field is null. Each option has
+ * a flags word which, due to historical reasons, is called
+ * @has_arg@. This describes various properties of the option,
+ * such as what sort of argument it takes, and whether it can
+ * be negated.
+ *
+ * When mdwopt finds a long option, it looks the name up in the
+ * table. The index of the matching entry is stored in the
+ * @longind@ variable, passed to mdwopt (unless @longind@ is 0):
+ * a value of %$-1$% indicates that no long option was
+ * found. The behaviour is then dependent on the values in the
+ * table entry. If @flag@ is nonzero, it points to an integer
+ * to be modified by mdwopt. Usually the value in the @val@
+ * field is simply stored in the @flag@ variable. If the flag
+ * @gFlag_switch@ is set, however, the value is combined with
+ * the existing value of the flags using a bitwise OR. If
+ * @gFlag_negate@ is set, then the flag bit will be cleared if a
+ * matching negated long option is found. The value 0 is
+ * returned.
+ *
+ * If @flag@ is zero, the value in @val@ is returned by mdwopt,
+ * possibly with bit 8 set if the option was negated.
+ *
+ * Arguments for long options are stored in @data->arg@, as
+ * before.
+ *
+ * Numeric options, if enabled, cause the value `%|#|%' to be
+ * returned, and the numeric value to be stored in @data->opt@.
+ *
+ * If the flag @gFlag_envVar@ is set on entry, options will be
+ * extracted from an environment variable whose name is built by
+ * capitalising all the letters of the program's name. (This
+ * allows a user to have different default settings for a
+ * program, by calling it through different symbolic links.) */
+
+extern int mdwopt(int /*argc*/, char *const */*argv*/,
+ const char */*shortopt*/,
+ const struct option */*longopts*/, int */*longind*/,
+ mdwopt_data */*data*/, int /*flags*/);
+
+/* --- Macros for more commonly used routines --- */
+
+#define getopt(c, v, o) mdwopt(c, v, o, 0, 0, 0, gFlag_noLongs)
+#define getopt_long(c, v, o, l, li) mdwopt(c, v, o, l, li, 0, 0)
+#define getopt_long_only(c, v, o, l, li) \
+ mdwopt(c, v, o, l, li, 0, gFlag_noShorts)
+
+#ifdef __cplusplus
+}
+#endif
+
+/*----- C++ wrapper class -------------------------------------------------*/
+
+#ifdef __cplusplus
+
+/* --- Class: @MdwOpt@ --- *
+ *
+ * Parent: ---
+ *
+ * Methods: @MdwOpt@ -- construct a new mdwopt object with the given
+ * arguments. These are remembered for later use.
+ * @arg@ -- return the argument of the current option
+ * arguments. These are remembered for later use.
+ * @arg@ -- return the argument of the current option
+ * @opt@ -- return the value of the current option
+ * @ind@ -- return the index of the next unread argument
+ * @longind@ -- return index of current long option in table
+ * @errors@ -- return or set whether we report errors to the
+ * user
+ * @prog@ -- return program name from @argv[0]@
+ * @next@ -- return next option read from the table
+ *
+ * Use: A simple C++ class for encapsulating the options parser.
+ * The methods are all nice and simple, and extremely similar
+ * to the normal C interface described above.
+ */
+
+class MdwOpt {
+ protected:
+ int argc;
+ char * const *argv;
+ const char *shortopts;
+ const struct option *longopts;
+ int long_ind;
+ int flags;
+
+ mdwopt_data data;
+
+ public:
+ MdwOpt(int c, char * const *v, const char *so,
+ const struct option *lo, int f=0) :
+ argc(c), argv(v), shortopts(so), longopts(lo), flags(f) {
+ data.ind = 0;
+ data.err = 1;
+ }
+
+ const char *arg(void) const { return (data.arg); }
+ int opt(void) const { return (data.opt); }
+ int errors(void) const { return (data.err); }
+ int errors(int e) { int oe = data.err; data.err = e; return (oe); }
+ int ind(void) const { return (data.ind); }
+ int longind(void) const { return (long_ind); }
+ const char *prog(void) const { return (data.prog); }
+
+ int next(void) {
+ return (mdwopt(argc, argv, shortopts,
+ longopts, &long_ind, &data, flags));
+ }
+};
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: name.c,v 1.1 1997/07/21 13:47:46 mdw Exp $
+ *
+ * Looking up of names in symbol tables
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: name.c,v $
+ * Revision 1.1 1997/07/21 13:47:46 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Unix headers --- */
+
+#include <grp.h>
+#include <pwd.h>
+
+/* --- Local headers --- */
+
+#include "class.h"
+#include "name.h"
+#include "sym.h"
+#include "userdb.h"
+#include "utils.h"
+
+/*----- Static variables --------------------------------------------------*/
+
+static sym_table name__table; /* Symbol table for everything */
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @name_init@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Initialises the name table. Requires the user database to
+ * be populated (see @userdb_local@ and @userdb_yp@).
+ */
+
+void name_init(void)
+{
+ /* --- Initialise the name table --- */
+
+ sym_createTable(&name__table);
+
+ /* --- Insert all the users and groups into the table --- */
+
+ {
+ struct passwd *pw;
+ struct group *gr;
+
+ userdb_iterateUsers();
+ while ((pw = userdb_nextUser()) != 0) {
+ unsigned f;
+ name *n;
+ int u;
+
+ /* --- First, add the user to the table --- */
+
+ n = sym_find(&name__table, pw->pw_name, -1, sizeof(name), &f);
+ if (!f) {
+ sym_table *t = xmalloc(sizeof(*t));
+ sym_createTable(t);
+ n->c = class_create(clType_user, t);
+ }
+ u = pw->pw_uid;
+ sym_find(n->c->t, (char *)&u, sizeof(u), sizeof(sym_base), 0);
+
+ /* --- Now handle the user's default group --- */
+
+ if ((gr = userdb_groupById(pw->pw_gid)) != 0) {
+ n = sym_find(&name__table, gr->gr_name, -1, sizeof(name), &f);
+ if (!f) {
+ sym_table *t = xmalloc(sizeof(*t));
+ sym_createTable(t);
+ n->c = class_create(clType_user, t);
+ }
+ sym_find(n->c->t, (char *)&u, sizeof(u), sizeof(sym_base), 0);
+ }
+ }
+ }
+
+ /* --- Now get the subsidiary groups --- */
+
+ {
+ struct group *gr;
+ struct passwd *pw;
+ char **p;
+
+ userdb_iterateGroups();
+ while ((gr = userdb_nextGroup()) != 0) {
+ unsigned f;
+ name *n;
+ int u;
+
+ n = sym_find(&name__table, gr->gr_name, -1, sizeof(name), &f);
+ if (!f) {
+ sym_table *t = xmalloc(sizeof(*t));
+ sym_createTable(t);
+ n->c = class_create(clType_user, t);
+ }
+
+ for (p = gr->gr_mem; *p; p++) {
+ if ((pw = userdb_userByName(*p)) != 0) {
+ u = pw->pw_uid;
+ sym_find(n->c->t, (char *)&u, sizeof(u), sizeof(sym_base), 0);
+ }
+ }
+ }
+ }
+
+ /* --- Finally add in the `all' class --- *
+ *
+ * Do that now, to prevent it being overwritten by the above.
+ */
+
+ {
+ name *n;
+ unsigned f;
+
+ n = sym_find(&name__table, "all", -1, sizeof(name), &f);
+ if (f)
+ class_dec(n->c);
+ n->c = class_all;
+ }
+}
+
+/* --- @name_reinit@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Reinitialises the names table. It's cleared and then
+ * initialised with the current user and group ids as for
+ * @name_init@ above.
+ */
+
+void name_reinit(void)
+{
+ /* --- Empty the symbol table --- */
+
+ {
+ sym_iter i;
+ name *n;
+
+ for (sym_createIter(&i, &name__table); (n = sym_next(&i)) != 0; ) {
+ if (n->c)
+ class_dec(n->c);
+ }
+ }
+
+ /* --- Destroy and recreate the table --- */
+
+ sym_destroyTable(&name__table);
+ name_init();
+}
+
+/* --- @name_find@ --- *
+ *
+ * Arguments: @const char *p@ = pointer to name to look up
+ * @unsigned create@ = whether to create the item
+ * @unsigned *f@ = whether the item was created
+ *
+ * Returns: Pointer to a @name@ block containing the symbol, or
+ * zero if it wasn't found and we didn't want to create a
+ * new one.
+ *
+ * Use: Looks up a name in the symbol table and returns the
+ * item so located.
+ */
+
+name *name_find(const char *p, unsigned create, unsigned *f)
+{
+ /* --- This is a trivial veneer onto @sym_find@ --- */
+
+ return (sym_find(&name__table, p, -1, create ? sizeof(name) : 0, f));
+}
+
+/* --- @name_dump@ --- *
+ *
+ * Arguments: @FILE *fp@ = stream to dump on
+ *
+ * Returns: ---
+ *
+ * Use: Dumps a complete listing of the symbol table.
+ */
+
+void name_dump(FILE *fp)
+{
+ sym_iter i;
+ name *n;
+
+ for (sym_createIter(&i, &name__table); (n = sym_next(&i)) != 0; ) {
+ fprintf(fp, "\n--- name `%s'\n", n->base.name);
+ class_dump(n->c, fp);
+ }
+}
+
+/*----- Test driver -------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+int main(void)
+{
+ userdb_init();
+ userdb_local();
+ userdb_yp();
+ name_init();
+ name_dump(stdout);
+ return (0);
+}
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: name.h,v 1.1 1997/07/21 13:47:46 mdw Exp $
+ *
+ * Looking up of names in symbol tables
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: name.h,v $
+ * Revision 1.1 1997/07/21 13:47:46 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef NAMES_H
+#define NAMES_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#ifndef CLASS_H
+# include "class.h"
+#endif
+
+#ifndef SYM_H
+# include "sym.h"
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct name {
+ sym_base base; /* Base block for symbol table */
+ classdef *c; /* Base class pointer */
+} name;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @name_init@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Initialises the name table. Requires the user database to
+ * be populated (see @userdb_local@ and @userdb_yp@).
+ */
+
+extern void name_init(void);
+
+/* --- @name_reinit@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Reinitialises the names table. It's cleared and then
+ * initialised with the current user and group ids as for
+ * @name_init@ above.
+ */
+
+extern void name_reinit(void);
+
+/* --- @name_find@ --- *
+ *
+ * Arguments: @const char *p@ = pointer to name to look up
+ * @unsigned create@ = whether to create the item
+ * @unsigned *f@ = whether the item was created
+ *
+ * Returns: Pointer to a @name@ block containing the symbol, or
+ * zero if it wasn't found and we didn't want to create a
+ * new one.
+ *
+ * Use: Looks up a name in the symbol table and returns the
+ * item so located.
+ */
+
+extern name *name_find(const char */*p*/,
+ unsigned /*create*/,
+ unsigned */*f*/);
+
+/* --- @name_dump@ --- *
+ *
+ * Arguments: @FILE *fp@ = stream to dump on
+ *
+ * Returns: ---
+ *
+ * Use: Dumps a complete listing of the symbol table.
+ */
+
+extern void name_dump(FILE */*fp*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: parser.h,v 1.1 1997/07/21 13:47:46 mdw Exp $
+ *
+ * Parser for `become.conf' files
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: parser.h,v $
+ * Revision 1.1 1997/07/21 13:47:46 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef PARSER_H
+#define PARSER_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#ifndef CLASS_H
+# include "class.h"
+#endif
+
+#ifndef NAME_H
+# include "name.h"
+#endif
+
+#include "parser.tab.h" /* Token definitions and things */
+
+/*----- Global variables --------------------------------------------------*/
+
+extern int yydebug; /* Debugging flag for parser */
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @yyparse@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: Zero if it worked, nonzero if it didn't.
+ *
+ * Use: Parses configuration files.
+ */
+
+extern int yyparse(void);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: parser.y,v 1.1 1997/07/21 13:47:45 mdw Exp $
+ *
+ * Parser for `become.conf' files
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: parser.y,v $
+ * Revision 1.1 1997/07/21 13:47:45 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+%{
+
+/* --- ANSI headers --- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Unix headers --- */
+
+#include <sys/types.h>
+
+#include <pwd.h>
+#include <unistd.h>
+
+/* --- Local headers --- */
+
+#include "class.h"
+#include "daemon.h"
+#include "lexer.h"
+#include "name.h"
+#include "rule.h"
+#include "set.h"
+#include "sym.h"
+#include "userdb.h"
+#include "utils.h"
+
+%}
+/*----- Stack type --------------------------------------------------------*/
+
+%union {
+ long i;
+ char *s;
+ name *n;
+ classdef *c;
+}
+
+/*----- Token and rule declarations ---------------------------------------*/
+
+/* --- Tokens --- */
+
+%token BADTOKEN
+%token USER
+%token COMMAND
+%token HOST
+%token ALLOW
+%token PORT
+%token KEYFILE
+%token <i> INT
+%token <s> WORD
+%token <s> STRING
+%token ARROW
+
+%left ','
+%left '-'
+%left '|'
+%left '&'
+
+/* --- Rules --- */
+
+%type <c> user_class command_class host_class
+%type <c> user_class_opt command_class_opt host_class_opt
+%type <n> name
+
+/*----- Error reporting ---------------------------------------------------*/
+%{
+
+#define YYDEBUG 1
+#define YYERROR_VERBOSE
+
+/* --- @yyprint@ --- *
+ *
+ * Arguments: @FILE *fp@ = pointer to stream to write on
+ * @int type@ = pointer to token type
+ * @YYSTYPE v@ = token value
+ *
+ * Returns: ---
+ *
+ * Use: Displays the semantic value of a token.
+ */
+
+#define YYPRINT(fp, type, value) yyprint(fp, type, value)
+
+static void yyprint(FILE *fp, int type, YYSTYPE v)
+{
+ switch (type) {
+ case INT:
+ fprintf(fp, " %li", v.i);
+ break;
+ case WORD:
+ case STRING:
+ fprintf(fp, " `%s'", v.s);
+ break;
+ }
+}
+
+/* --- @yyerror@ --- *
+ *
+ * Arguments: @const char *msg@ = pointer to error message
+ *
+ * Returns: ---
+ *
+ * Use: Reports parse errors.
+ */
+
+static void yyerror(const char *msg)
+{
+ moan("%s at line %i", msg, lex_line);
+}
+
+%}
+%%
+/*----- The actual grammar ------------------------------------------------*/
+
+/* --- Simple driver things --- */
+
+file : /* empty */
+ | file statement
+ ;
+
+statement : user_spec
+ | command_spec
+ | host_spec
+ | allow_spec
+ | port_spec
+ | key_spec
+ | error ';'
+ ;
+
+/* --- Main statement types --- */
+
+user_spec : USER name '=' user_class ';' {
+ if ($2->c)
+ class_dec($2->c);
+ $2->c = $4;
+ }
+ ;
+
+command_spec : COMMAND name '=' command_class ';' {
+ if ($2->c)
+ class_dec($2->c);
+ $2->c = $4;
+ }
+ ;
+
+host_spec : HOST name '=' host_class ';' {
+ if ($2->c)
+ class_dec($2->c);
+ $2->c = $4;
+ }
+ ;
+
+port_spec : PORT INT ';' { daemon_usePort($2); }
+ ;
+
+key_spec : KEYFILE STRING ';' { daemon_readKey($2); }
+
+/* --- Parsing allow specifications --- */
+
+allow_spec : ALLOW host_class_opt user_class ARROW
+ user_class_opt command_class_opt ';' {
+ rule_add($2, $3, $5, $6);
+ }
+
+host_class_opt : /* empty */ { $$ = class_all; }
+ | '[' host_class ']' { $$ = $2; }
+ ;
+
+user_class_opt : /* empty */ { $$ = class_all; }
+ | user_class { $$ = $1; }
+ ;
+
+command_class_opt : /* empty */ { $$ = class_all; }
+ | ':' command_class { $$ = $2; }
+ ;
+
+/* --- Names get translated into symbols quickly --- */
+
+name : WORD {
+ unsigned f;
+ name *n = name_find($1, 1, &f);
+ if (!f)
+ n->c = 0;
+ $$ = n;
+ }
+
+/*----- Various class expression types ------------------------------------*
+ *
+ * Unfortunately, all these need to handle token types slightly differently
+ * and I can't be bothered to remember the current state.
+ */
+
+/* --- User class expressions --- */
+
+user_class : user_class ',' user_class {
+ if ($1->type != $3->type) {
+ yyerror("type mismatch");
+ class_dec($1);
+ class_dec($3);
+ YYERROR;
+ } else {
+ if ($1 == class_all)
+ $$ = class_all;
+ else
+ $$ = class_create($1->type,
+ set_union($1->t, $3->t));
+ class_dec($1);
+ class_dec($3);
+ }
+ }
+ | user_class '-' user_class {
+ if ($1->type != $3->type) {
+ yyerror("type mismatch");
+ class_dec($1);
+ class_dec($3);
+ YYERROR;
+ } else {
+ $$ = class_create($1->type,
+ set_subtract($1->t, $3->t));
+ class_dec($1);
+ class_dec($3);
+ }
+ }
+ | user_class '&' user_class {
+ if ($1->type != $3->type) {
+ yyerror("type mismatch");
+ class_dec($1);
+ class_dec($3);
+ YYERROR;
+ } else {
+ if ($1 == class_all)
+ $$ = class_all;
+ else
+ $$ = class_create($1->type,
+ set_intersect($1->t, $3->t));
+ class_dec($1);
+ class_dec($3);
+ }
+ }
+ | user_class '|' user_class {
+ if ($1->type != $3->type) {
+ yyerror("type mismatch");
+ class_dec($1);
+ class_dec($3);
+ YYERROR;
+ } else {
+ if ($1 == class_all)
+ $$ = class_all;
+ else
+ $$ = class_create($1->type,
+ set_union($1->t, $3->t));
+ class_dec($1);
+ class_dec($3);
+ }
+ }
+ | INT {
+ sym_table *t = xmalloc(sizeof(*t));
+ int u = $1;
+ sym_createTable(t);
+ sym_find(t, (char *)&u, sizeof(u),
+ sizeof(sym_base), 0);
+ $$ = class_create(clType_user, t);
+ }
+ | STRING {
+ struct passwd *pw;
+ sym_table *t;
+ int u;
+ if ((pw = userdb_userByName($1)) == 0) {
+ moan("user `%s' not known at line %i",
+ $1, lex_line);
+ YYERROR;
+ } else {
+ t = xmalloc(sizeof(*t));
+ u = pw->pw_uid;
+ sym_createTable(t);
+ sym_find(t, (char *)&u, sizeof(u),
+ sizeof(sym_base), 0);
+ $$ = class_create(clType_user, t);
+ }
+ }
+ | WORD {
+ name *n = name_find($1, 0, 0);
+ if (!n || !n->c) {
+ moan("class `%s' not found at line %i",
+ $1, lex_line);
+ YYERROR;
+ } else if (~n->c->type & clType_user) {
+ yyerror("type mismatch");
+ YYERROR;
+ } else {
+ $$ = n->c;
+ class_inc(n->c);
+ }
+ }
+ | '(' user_class ')' { $$ = $2; }
+ ;
+
+/* --- Command class expressions --- */
+
+command_class : command_class ',' command_class {
+ if ($1->type != $3->type) {
+ yyerror("type mismatch");
+ class_dec($1);
+ class_dec($3);
+ YYERROR;
+ } else {
+ if ($1 == class_all)
+ $$ = class_all;
+ else
+ $$ = class_create($1->type,
+ set_union($1->t, $3->t));
+ class_dec($1);
+ class_dec($3);
+ }
+ }
+ | command_class '-' command_class {
+ if ($1->type != $3->type) {
+ yyerror("type mismatch");
+ class_dec($1);
+ class_dec($3);
+ YYERROR;
+ } else {
+ $$ = class_create($1->type,
+ set_subtract($1->t, $3->t));
+ class_dec($1);
+ class_dec($3);
+ }
+ }
+ | command_class '&' command_class {
+ if ($1->type != $3->type) {
+ yyerror("type mismatch");
+ class_dec($1);
+ class_dec($3);
+ YYERROR;
+ } else {
+ if ($1 == class_all)
+ $$ = class_all;
+ else
+ $$ = class_create($1->type,
+ set_intersect($1->t, $3->t));
+ class_dec($1);
+ class_dec($3);
+ }
+ }
+ | command_class '|' command_class {
+ if ($1->type != $3->type) {
+ yyerror("type mismatch");
+ class_dec($1);
+ class_dec($3);
+ YYERROR;
+ } else {
+ if ($1 == class_all)
+ $$ = class_all;
+ else
+ $$ = class_create($1->type,
+ set_union($1->t, $3->t));
+ class_dec($1);
+ class_dec($3);
+ }
+ }
+ | STRING {
+ sym_table *t = xmalloc(sizeof(*t));
+ sym_createTable(t);
+ sym_find(t, $1, -1, sizeof(sym_base), 0);
+ $$ = class_create(clType_command, t);
+ }
+ | WORD {
+ name *n = name_find($1, 0, 0);
+ if (!n || !n->c) {
+ moan("class `%s' not found at line %i",
+ $1, lex_line);
+ YYERROR;
+ } else if (~n->c->type & clType_command) {
+ yyerror("type mismatch");
+ YYERROR;
+ } else {
+ $$ = n->c;
+ class_inc(n->c);
+ }
+ }
+ | '(' command_class ')' { $$ = $2; }
+ ;
+
+/* --- Host class expressions --- */
+
+host_class : host_class ',' host_class {
+ if ($1->type != $3->type) {
+ yyerror("type mismatch");
+ class_dec($1);
+ class_dec($3);
+ YYERROR;
+ } else {
+ if ($1 == class_all)
+ $$ = class_all;
+ else
+ $$ = class_create($1->type,
+ set_union($1->t, $3->t));
+ class_dec($1);
+ class_dec($3);
+ }
+ }
+ | host_class '-' host_class {
+ if ($1->type != $3->type) {
+ yyerror("type mismatch");
+ class_dec($1);
+ class_dec($3);
+ YYERROR;
+ } else {
+ $$ = class_create($1->type,
+ set_subtract($1->t, $3->t));
+ class_dec($1);
+ class_dec($3);
+ }
+ }
+ | host_class '&' host_class {
+ if ($1->type != $3->type) {
+ yyerror("type mismatch");
+ class_dec($1);
+ class_dec($3);
+ YYERROR;
+ } else {
+ if ($1 == class_all)
+ $$ = class_all;
+ else
+ $$ = class_create($1->type,
+ set_intersect($1->t, $3->t));
+ class_dec($1);
+ class_dec($3);
+ }
+ }
+ | host_class '|' host_class {
+ if ($1->type != $3->type) {
+ yyerror("type mismatch");
+ class_dec($1);
+ class_dec($3);
+ YYERROR;
+ } else {
+ if ($1 == class_all)
+ $$ = class_all;
+ else
+ $$ = class_create($1->type,
+ set_union($1->t, $3->t));
+ class_dec($1);
+ class_dec($3);
+ }
+ }
+ | STRING {
+ sym_table *t = xmalloc(sizeof(*t));
+ sym_createTable(t);
+ sym_find(t, $1, -1, sizeof(sym_base), 0);
+ $$ = class_create(clType_host, t);
+ }
+ | WORD {
+ name *n = name_find($1, 0, 0);
+ if (!n || !n->c) {
+ moan("class `%s' not found at line %i",
+ $1, lex_line);
+ YYERROR;
+ } else if (~n->c->type & clType_host) {
+ yyerror("type mismatch");
+ YYERROR;
+ } else {
+ $$ = n->c;
+ class_inc(n->c);
+ }
+ }
+ | '(' host_class ')' { $$ = $2; }
+ ;
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: rule.c,v 1.1 1997/07/21 13:47:45 mdw Exp $
+ *
+ * Managing rule sets
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: rule.c,v $
+ * Revision 1.1 1997/07/21 13:47:45 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Local headers --- */
+
+#include "become.h"
+#include "class.h"
+#include "rule.h"
+#include "utils.h"
+
+/*----- Type definitions --------------------------------------------------*/
+
+/* --- Rule block --- */
+
+typedef struct rule {
+ struct rule *next; /* Next rule in the list */
+ classdef *host; /* Hosts this rule applies to */
+ classdef *from; /* From users in this class */
+ classdef *to; /* To users in this class */
+ classdef *cmd; /* To run commands in this class */
+} rule;
+
+/*----- Static variables --------------------------------------------------*/
+
+static rule *rule__list; /* List of rules */
+static rule *rule__tail; /* Pointer to last rule item */
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @rule_init@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Intialises the rule database.
+ */
+
+void rule_init(void)
+{
+ rule__list = 0;
+ rule__tail = (rule *)&rule__list;
+}
+
+/* --- @rule_reinit@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Reinitialises the rule database.
+ */
+
+void rule_reinit(void)
+{
+ rule *r = rule__list;
+ rule *rr;
+
+ while (r) {
+ rr = r->next;
+ free(r);
+ r = rr;
+ }
+
+ rule_init();
+}
+
+/* --- @rule_add@ --- *
+ *
+ * Arguments: @classdef *host@ = class of hosts this rule applies to
+ * @classdef *from@ = class of users allowed to change
+ * @classdef *to@ = class of users allowed to be changed to
+ * @classdef *cmd@ = class of commands allowed
+ *
+ * Returns: ---
+ *
+ * Use: Registers another rule.
+ */
+
+void rule_add(classdef *host, classdef *from, classdef *to, classdef *cmd)
+{
+ rule *r = xmalloc(sizeof(*r));
+
+ r->host = host;
+ r->from = from;
+ r->to = to;
+ r->cmd = cmd;
+ r->next = 0;
+ rule__tail->next = r;
+ rule__tail = r;
+}
+
+/* --- @rule_check@ --- *
+ *
+ * Arguments: @request *r@ = pointer to a request block
+ *
+ * Returns: Zero if disallowed, nonzero if allowed.
+ *
+ * Use: Checks a request to see if it's allowed.
+ */
+
+int rule_check(request *r)
+{
+ rule *rr = rule__list;
+
+ while (rr) {
+ if (class_userMatch(rr->from, r->from) &&
+ class_userMatch(rr->to, r->to) &&
+ class_commandMatch(rr->cmd, r->cmd) &&
+ class_hostMatch(rr->host, r->host))
+ return (1);
+ rr = rr->next;
+ }
+ return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: rule.h,v 1.1 1997/07/21 13:47:45 mdw Exp $
+ *
+ * Managing rule sets
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: rule.h,v $
+ * Revision 1.1 1997/07/21 13:47:45 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef RULE_H
+#define RULE_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifndef BECOME_H
+# include "become.h"
+#endif
+
+#ifndef CLASS_H
+# include "class.h"
+#endif
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @rule_init@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Intialises the rule database.
+ */
+
+extern void rule_init(void);
+
+/* --- @rule_reinit@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Reinitialises the rule database.
+ */
+
+extern void rule_reinit(void);
+
+/* --- @rule_add@ --- *
+ *
+ * Arguments: @classdef *host@ = class of hosts this rule applies to
+ * @classdef *from@ = class of users allowed to change
+ * @classdef *to@ = class of users allowed to be changed to
+ * @classdef *cmd@ = class of commands allowed
+ *
+ * Returns: ---
+ *
+ * Use: Registers another rule.
+ */
+
+extern void rule_add(classdef */*host*/, classdef */*from*/,
+ classdef */*to*/, classdef */*cmd*/);
+
+/* --- @rule_check@ --- *
+ *
+ * Arguments: @request *r@ = pointer to a request block
+ *
+ * Returns: Zero if disallowed, nonzero if allowed.
+ *
+ * Use: Checks a request to see if it's allowed.
+ */
+
+extern int rule_check(request */*r*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: set.c,v 1.1 1997/07/21 13:47:44 mdw Exp $
+ *
+ * Management of sets (for the use of the class expression handler)
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: set.c,v $
+ * Revision 1.1 1997/07/21 13:47:44 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Local headers --- */
+
+#include "set.h"
+#include "sym.h"
+#include "utils.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @set_subtract@ --- *
+ *
+ * Arguments: @sym_table *a@ = pointer to table @A@
+ * @sym_table *b@ = pointer to table @B@
+ *
+ * Returns: A symbol table containing all the elements in @A@ which don't
+ * appear in @B@.
+ *
+ * Use: Subtracts a symbol table from another symbol table. Assumes
+ * that there's no data following the actual @sym_base@ block
+ * for each item in the table.
+ */
+
+sym_table *set_subtract(sym_table *a, sym_table *b)
+{
+ sym_table *c = xmalloc(sizeof(*c));
+ sym_iter i;
+ sym_base *s;
+ unsigned f;
+
+ sym_createTable(c);
+ for (sym_createIter(&i, a); (s = sym_next(&i)) != 0; ) {
+ if (!sym_find(b, s->name, s->len, 0, &f))
+ sym_find(c, s->name, s->len, sizeof(sym_base), 0);
+ }
+
+ return (c);
+}
+
+/* --- @set_intersect@ --- *
+ *
+ * Arguments: @sym_table *a@ = pointer to table @A@
+ * @sym_table *b@ = pointer to table @B@
+ *
+ * Returns: A symbol table containing all the elements in @A@ which also
+ * appear in @B@.
+ *
+ * Use: Constructs the intersection of two symbol tables. Assumes
+ * that there's no data following the actual @sym_base@ block
+ * for each item in the table.
+ */
+
+sym_table *set_intersect(sym_table *a, sym_table *b)
+{
+ sym_table *c = xmalloc(sizeof(*c));
+ sym_iter i;
+ sym_base *s;
+ unsigned f;
+
+ sym_createTable(c);
+ for (sym_createIter(&i, a); (s = sym_next(&i)) != 0; ) {
+ if (sym_find(b, s->name, s->len, 0, &f))
+ sym_find(c, s->name, s->len, sizeof(sym_base), 0);
+ }
+
+ return (c);
+}
+
+/* --- @set_union@ --- *
+ *
+ * Arguments: @sym_table *a@ = pointer to table @A@
+ * @sym_table *b@ = pointer to table @B@
+ *
+ * Returns: A symbol table containing all the elements in @A@ and those
+ * in @B@.
+ *
+ * Use: Constructs the union of two symbol tables. Assumes that
+ * there's no data following the actual @sym_base@ block for
+ * each item in the table.
+ */
+
+sym_table *set_union(sym_table *a, sym_table *b)
+{
+ sym_table *c = xmalloc(sizeof(*c));
+ sym_iter i;
+ sym_base *s;
+
+ sym_createTable(c);
+ for (sym_createIter(&i, a); (s = sym_next(&i)) != 0; )
+ sym_find(c, s->name, s->len, sizeof(sym_base), 0);
+
+ for (sym_createIter(&i, b); (s = sym_next(&i)) != 0; )
+ sym_find(c, s->name, s->len, sizeof(sym_base), 0);
+
+ return (c);
+}
+
+/* --- @set_copy@ --- *
+ *
+ * Arguments: @sym_table *a@ = pointer to table
+ *
+ * Returns: A copy of the symbol table.
+ *
+ * Use: Copies a symbol table. Same assumptions again...
+ */
+
+sym_table *set_copy(sym_table *a)
+{
+ sym_table *c = xmalloc(sizeof(*c));
+ sym_iter i;
+ sym_base *s;
+
+ for (sym_createIter(&i, a); (s = sym_next(&i)) != 0; )
+ sym_find(c, s->name, s->len, sizeof(sym_base), 0);
+
+ return (c);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: set.h,v 1.1 1997/07/21 13:47:44 mdw Exp $
+ *
+ * Management of sets (for the use of the class expression handler)
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: set.h,v $
+ * Revision 1.1 1997/07/21 13:47:44 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef SET_H
+#define SET_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#ifndef SYM_H
+# include "sym.h"
+#endif
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @set_subtract@ --- *
+ *
+ * Arguments: @sym_table *a@ = pointer to table @A@
+ * @sym_table *b@ = pointer to table @B@
+ *
+ * Returns: A symbol table containing all the elements in @A@ which don't
+ * appear in @B@.
+ *
+ * Use: Subtracts a symbol table from another symbol table. Assumes
+ * that there's no data following the actual @sym_base@ block
+ * for each item in the table.
+ */
+
+extern sym_table *set_subtract(sym_table */*a*/, sym_table */*b*/);
+
+/* --- @set_intersect@ --- *
+ *
+ * Arguments: @sym_table *a@ = pointer to table @A@
+ * @sym_table *b@ = pointer to table @B@
+ *
+ * Returns: A symbol table containing all the elements in @A@ which also
+ * appear in @B@.
+ *
+ * Use: Constructs the intersection of two symbol tables. Assumes
+ * that there's no data following the actual @sym_base@ block
+ * for each item in the table.
+ */
+
+sym_table *set_intersect(sym_table */*a*/, sym_table */*b*/);
+
+/* --- @set_union@ --- *
+ *
+ * Arguments: @sym_table *a@ = pointer to table @A@
+ * @sym_table *b@ = pointer to table @B@
+ *
+ * Returns: A symbol table containing all the elements in @A@ and those
+ * in @B@.
+ *
+ * Use: Constructs the union of two symbol tables. Assumes that
+ * there's no data following the actual @sym_base@ block for
+ * each item in the table.
+ */
+
+sym_table *set_union(sym_table */*a*/, sym_table */*b*/);
+
+/* --- @set_copy@ --- *
+ *
+ * Arguments: @sym_table *a@ = pointer to table
+ *
+ * Returns: A copy of the symbol table.
+ *
+ * Use: Copies a symbol table. Same assumptions again...
+ */
+
+sym_table *set_copy(sym_table */*a*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: sym.c,v 1.1 1997/07/21 13:47:44 mdw Exp $
+ *
+ * Symbol table management
+ *
+ * (c) 1996 Straylight
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: sym.c,v $
+ * Revision 1.1 1997/07/21 13:47:44 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Local headers --- */
+
+#include "sym.h"
+#include "utils.h"
+
+/*----- Tuning parameters -------------------------------------------------*/
+
+/* --- Initial hash table size --- *
+ *
+ * This is the initial @mask@ value. It must be of the form %$2^n - 1$%,
+ * so that it can be used to mask of the bottom bits of a hash value.
+ */
+
+#define sym__iSize 255 /* Size of a new hash table */
+
+/* --- Maximum load factor --- *
+ *
+ * This parameter controls how much the table has to be loaded before the
+ * table is extended. The number of elements %$n$%, the number of bins %$b$%
+ * and the limit %$l$% satisfy the relation %$n < bl$%; if a new item is
+ * added to the table and this relation is found to be false, the table is
+ * doubled in size.
+ *
+ * The parameter @sym__limFactor@ is the value of %$l$% multiplied by 256;
+ * fixed point arithmetic is used to calculate the allowable load. Note
+ * that the exact calculation of this criterion isn't important; what's more
+ * significant is that the parameter allows tuning of the rehashing process.
+ *
+ * The current value is %$3 \over 4$%, which appears to be reasonable on the
+ * face of things.
+ */
+
+#define sym__limFactor 0x0C0 /* Load factor for growing table */
+
+/*----- CRC table ---------------------------------------------------------*/
+
+/*****************************************************************/
+/* */
+/* CRC LOOKUP TABLE */
+/* ================ */
+/* */
+/* The following CRC lookup table was generated automagically */
+/* by `crcgen', which is based on the Rocksoft^tm Model CRC */
+/* Algorithm Table Generation Program. The model parameters */
+/* supplied to `crcgen' were: */
+/* */
+/* Width : 32 bits */
+/* Poly : 0x04C11DB7 */
+/* Init : 0xFFFFFFFF */
+/* XorOut : 0xFFFFFFFF */
+/* Reverse : Yes */
+/* Check : 0xCBF43926 */
+/* */
+/* For more information on the Rocksoft^tm Model CRC Algorithm, */
+/* see the document titled `A Painless Guide to CRC Error */
+/* Detection Algorithms' by Ross Williams */
+/* (ross@@guest.adelaide.edu.au.). This document is likely to be */
+/* in the FTP archive `ftp.adelaide.edu.au/pub/rocksoft'. */
+/* */
+/*****************************************************************/
+
+static unsigned long sym__crcTable[256] = {
+ 0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,
+ 0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
+ 0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L,
+ 0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L,
+ 0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL,
+ 0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L,
+ 0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL,
+ 0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L,
+ 0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L,
+ 0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL,
+ 0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L,
+ 0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L,
+ 0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L,
+ 0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL,
+ 0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L,
+ 0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL,
+ 0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL,
+ 0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L,
+ 0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L,
+ 0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L,
+ 0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL,
+ 0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L,
+ 0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL,
+ 0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L,
+ 0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L,
+ 0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL,
+ 0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L,
+ 0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L,
+ 0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L,
+ 0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL,
+ 0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L,
+ 0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL,
+ 0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL,
+ 0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L,
+ 0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L,
+ 0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L,
+ 0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL,
+ 0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L,
+ 0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL,
+ 0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L,
+ 0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L,
+ 0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL,
+ 0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L,
+ 0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L,
+ 0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L,
+ 0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL,
+ 0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L,
+ 0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL,
+ 0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL,
+ 0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L,
+ 0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L,
+ 0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L,
+ 0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL,
+ 0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L,
+ 0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL,
+ 0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L,
+ 0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L,
+ 0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL,
+ 0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L,
+ 0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L,
+ 0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L,
+ 0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL,
+ 0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L,
+ 0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL,
+};
+
+/*****************************************************************/
+/* End of CRC Lookup Table */
+/*****************************************************************/
+
+/* --- Work out the CRC of a bytestring --- */
+
+#define sym__crc(s_, l_, hv_) do { \
+ unsigned long h_ = 0xFFFFFFFFL; \
+ const char *p_ = s_; \
+ const char *e_ = (s_) + (l_); \
+ \
+ while (p_ < e_) \
+ h_ = (h_ >> 8) ^ (sym__crcTable[(*p_++ ^ h_) & 0xFF]); \
+ hv_ = ~h_ & 0xFFFFFFFFL; \
+} while (0)
+
+/*----- Main code ---------------------------------------------------------*/
+
+#ifndef DEBUG_CRC
+
+/* --- @sym_createTable@ --- *
+ *
+ * Arguments: @sym_table *t@ = symbol table to initialise
+ *
+ * Returns: ---
+ *
+ * Use: Initialises the given symbol table.
+ */
+
+void sym_createTable(sym_table *t)
+{
+ size_t i;
+
+ t->mask = sym__iSize;
+ t->c = (sym__iSize * sym__limFactor) >> 8;
+ t->a = xmalloc((t->mask + 1) * sizeof(sym_base *));
+
+ for (i = 0; i < sym__iSize + 1; i++)
+ t->a[i] = 0;
+}
+
+/* --- @sym_destroyTable@ --- *
+ *
+ * Arguments: @sym_table *t@ = pointer to symbol table in question
+ *
+ * Returns: ---
+ *
+ * Use: Destroys a symbol table, freeing all the memory it used to
+ * occupy.
+ */
+
+void sym_destroyTable(sym_table *t)
+{
+ size_t i;
+ sym_base *p, *q;
+
+ for (i = 0; i <= t->mask; i++) {
+ p = t->a[i];
+ while (p) {
+ q = p->next;
+ free(p);
+ p = q;
+ }
+ }
+}
+
+/* --- @sym_find@ --- *
+ *
+ * Arguments: @sym_table *t@ = pointer to symbol table in question
+ * @const char *n@ = pointer to symbol table to look up
+ * @long l@ = length of the name string or negative to measure
+ * @size_t sz@ = size of desired symbol object, or zero
+ * @unsigned *f@ = pointer to a flag, or null.
+ *
+ * Returns: The address of a @sym_base@ structure, or null if not found
+ * and @sz@ is zero.
+ *
+ * Use: Looks up a symbol in a given symbol table. The name is
+ * passed by the address of its first character. The length
+ * may be given, in which case the name may contain arbitrary
+ * binary data, or it may be given as a negative number, in
+ * which case the length of the name is calculated as
+ * @strlen(n)@.
+ *
+ * The return value is the address of a pointer to a @sym_base@
+ * block (which may have other things on the end, as above). If
+ * the symbol could be found, the return value points to the
+ * symbol block. If the symbol wasn't there, then if @sz@ is
+ * nonzero, a new symbol is created and its address is returned;
+ * otherwise a null pointer is returned.
+ *
+ * The value of @*f@ indicates whether a new symbol entry was
+ * created: a nonzero value indicates that an old value was
+ * found.
+ */
+
+void *sym_find(sym_table *t, const char *n, long l, size_t sz, unsigned *f)
+{
+ unsigned long hash; /* Hash value for user's name */
+ size_t len = l < 0 ? strlen(n) + 1 : l; /* Find length of user's name */
+ sym_base *bin; /* Bin containing our item */
+ sym_base *p, *q; /* Pointer wandering through list */
+
+ /* --- Find the correct bin --- */
+
+ sym__crc(n, len, hash); /* Find hash value for this name */
+ bin = p = (sym_base *)(t->a + (hash & t->mask));
+
+ /* --- Search the bin list --- */
+
+ while (p->next) {
+ if (hash == p->next->hash && /* Check hash values first */
+ len == p->next->len && /* Then compare string lengths */
+ !memcmp(n, p->next->name, len)) /* And finally compare the strings */
+ {
+ /* --- Found a match --- *
+ *
+ * As a minor, and probably pointless, tweak, move the item to the
+ * front of its bin list.
+ */
+
+ q = p->next; /* Find the actual symbol block */
+ p->next = q->next; /* Extract block from bin list */
+ q->next = bin->next; /* Set up symbol's next pointer */
+ bin->next = q; /* And reinsert the block */
+
+ /* --- Return the block --- */
+
+ if (f) *f = 1; /* Didn't fail to find the item */
+ return (q); /* And return the block */
+ }
+
+ p = p->next; /* Move onto the next item */
+ }
+
+ /* --- Couldn't find the item there --- */
+
+ if (f) *f = 0; /* Failed to find the block */
+ if (!sz) return (0); /* Return zero if not creating */
+
+ /* --- Create a new symbol block and initialise it --- */
+
+ p = xmalloc(sz); /* Create a new symbol block */
+ p->next = bin->next; /* Set up the next pointer */
+ p->hash = hash; /* Set up the hash value too */
+ p->name = xmalloc(len); /* Allocate a block for the name */
+ p->len = len; /* And set up the string length */
+ memcpy(p->name, n, len); /* And copy the string over */
+
+ bin->next = p; /* Put the pointer into the bin */
+
+ /* --- Consider growing the array --- */
+
+ if (!--t->c) {
+ unsigned long m = t->mask + 1; /* Next set bit in has word */
+ sym_base *p, *q, *r; /* More useful pointers */
+ size_t i, lim; /* Loop counter and limit */
+
+ /* --- Update values in the anchor block --- */
+
+ t->c = ((t->mask + 1) * sym__limFactor) >> 8; /* Set load value */
+ t->mask = (t->mask + 1) * 2 - 1; /* Set the new mask value */
+ t->a = xrealloc(t->a, (t->mask + 1) * sizeof(sym_base *));
+
+ /* --- Now wander through the table rehashing things --- *
+ *
+ * This loop is very careful to avoid problems with aliasing. The items
+ * are dealt with from the end backwards to avoid overwriting bins before
+ * they've been processed.
+ */
+
+ lim = (t->mask + 1) >> 1;
+ for (i = 0; i < lim; i++) {
+ /* --- Some initialisation --- */
+
+ r = t->a[i]; /* Find the list we're dissecting */
+ p = (sym_base *)(t->a + i); /* Find bit-clear list */
+ q = (sym_base *)(t->a + i + lim); /* And the bit-set lsit */
+
+ /* --- Now go through the @r@ list --- */
+
+ while (r) {
+ if (r->hash & m) /* Is the next bit set? */
+ q = q->next = r; /* Yes, so fit up previous link */
+ else
+ p = p->next = r; /* No, so fit up previous link */
+ r = r->next; /* Move onto the next item */
+ }
+ p->next = q->next = 0; /* Null terminate the new lists */
+ }
+ }
+
+ /* --- Finished that, so return the new symbol block --- */
+
+ return (p);
+}
+
+/* --- @sym_remove@ --- *
+ *
+ * Arguments: @sym_table *i@ = pointer to a symbol table object
+ * @void *b@ = pointer to symbol table entry
+ *
+ * Returns: ---
+ *
+ * Use: Removes the object from the symbol table. The space occupied
+ * by the object and its name is freed; anything else attached
+ * to the entry should already be gone by this point.
+ */
+
+void sym_remove(sym_table *t, void *b)
+{
+ /* --- A quick comment --- *
+ *
+ * Since the @sym_base@ block contains the hash, finding the element in the
+ * bin list is really quick -- it's not worth bothering with things like
+ * doubly linked lists.
+ */
+
+ sym_base *p = b;
+ sym_base *bin = (sym_base *)(t->a + (p->hash & t->mask));
+
+ /* --- Find the item in the bin list --- */
+
+ while (bin->next) {
+ if (bin->next == p)
+ break;
+ bin = bin->next;
+ }
+ if (!bin->next)
+ return;
+
+ /* --- Now just remove the item from the list and free it --- *
+ *
+ * Oh, and bump the load counter.
+ */
+
+ bin->next = p->next;
+ free(p->name);
+ free(p);
+ t->c++;
+}
+
+/* --- @sym_createIter@ --- *
+ *
+ * Arguments: @sym_iter *i@ = pointer to an iterator object
+ * @sym_table *t@ = pointer to a symbol table object
+ *
+ * Returns: ---
+ *
+ * Use: Creates a new symbol table iterator which may be used to
+ * iterate through a symbol table.
+ */
+
+void sym_createIter(sym_iter *i, sym_table *t)
+{
+ i->t = t;
+ i->i = 0;
+ i->n = 0;
+}
+
+/* --- @sym_next@ --- *
+ *
+ * Arguments: @sym_iter *i@ = pointer to iterator object
+ *
+ * Returns: Pointer to the next symbol found, or null when finished.
+ *
+ * Use: Returns the next symbol from the table. Symbols are not
+ * returned in any particular order.
+ */
+
+void *sym_next(sym_iter *i)
+{
+ sym_base *p;
+
+ /* --- Find the next item --- */
+
+ while (!i->n) {
+ if (i->i > i->t->mask)
+ return (0);
+ i->n = i->t->a[i->i++];
+ }
+
+ /* --- Update the iterator block --- */
+
+ p = i->n;
+ i->n = p->next;
+
+ /* --- Done --- */
+
+ return (p);
+}
+
+#endif
+
+/*----- CRC test code -----------------------------------------------------*/
+
+#ifdef DEBUG_CRC
+
+int main(void)
+{
+ unsigned long h;
+ sym__crc("123456789", 9, h);
+ printf("crc check == %04x\n", h);
+ return (0);
+}
+
+#endif
+
+/*----- Symbol table test code --------------------------------------------*/
+
+#ifdef TEST_RIG
+
+#include <errno.h>
+#include <time.h>
+
+#include "dbutils.h"
+
+typedef struct sym_word {
+ sym_base base;
+ size_t i;
+} sym_word;
+
+
+/* --- What it does --- *
+ *
+ * Reads the file /usr/dict/words (change to some other file full of
+ * interesting and appropriate bits of text to taste) into a big buffer and
+ * picks apart into lines. Then picks lines at random and enters them into
+ * the symbol table.
+ */
+
+int main(void)
+{
+ char *buff, *p, *lim;
+ size_t sz, done;
+ FILE *fp;
+ int i;
+ char **line;
+ sym_word **flag;
+ sym_table tbl;
+ int entries;
+
+ /* --- Initialise for reading the file --- */
+
+ sz = BUFSIZ;
+ buff = xmalloc(sz + 1);
+ done = 0;
+
+ if ((fp = fopen("/usr/dict/words", "r")) == 0)
+ fprintf(stderr, "buggered ;-( (%s)\n", strerror(errno));
+
+ /* --- Read buffers of text --- *
+ *
+ * Read a buffer. If more to come, double the buffer size and try again.
+ * This is the method I recommended to comp.lang.c, so I may as well try
+ * it.
+ */
+
+ for (;;) {
+ i = fread(buff + done, 1, sz - done, fp);
+ done += i;
+ if (done != sz)
+ break;
+ sz <<= 1;
+ buff = xrealloc(buff, sz + 1);
+ }
+
+ /* --- Count the lines --- */
+
+ lim = buff + done;
+
+ sz = 1;
+ for (p = buff; p < lim; p++)
+ if (*p == '\n') sz++;
+
+ /* --- Build a table of line starts --- */
+
+ line = xmalloc(sz * sizeof(char *));
+ i = 0;
+ line[i++] = buff;
+ for (p = buff; p < lim; p++)
+ if (*p == '\n') *p = 0, line[i++] = p + 1;
+ *lim = 0;
+
+ /* --- Build a table of lines --- *
+ *
+ * This reverses the mapping which the symbol table performs, so that its
+ * accuracy can be tested.
+ */
+
+ flag = xmalloc(sz * sizeof(sym_word *));
+ for (i = 0; i < sz; i++)
+ flag[i] = 0;
+ entries = 0;
+
+ sym_createTable(&tbl);
+
+ for (;;) {
+ i = (unsigned)rand() % sz;
+
+ switch (rand() % 5)
+ {
+ case 0: {
+ sym_word *w;
+
+ printf("find `%s'\n", line[i]);
+
+ w = sym_find(&tbl, line[i], -1, 0, 0);
+ if (w != flag[i])
+ printf("*** error: find `%s' gave %p not %p\n",
+ line[i], (void *)w, (void *)flag[i]);
+ else if (w && w->i != i)
+ printf("*** error: find sym for `%s' gives index %i not %i\n",
+ line[i], w->i, i);
+ } break;
+
+ case 1: {
+ unsigned f;
+ sym_word *w;
+
+ printf("create `%s'\n", line[i]);
+
+ w = sym_find(&tbl, line[i], -1, sizeof(sym_word), &f);
+ if (f)
+ {
+ if (w != flag[i])
+ printf("*** error: create `%s' gave %p not %p\n",
+ line[i], (void *)w, (void *)flag[i]);
+ else if (w && w->i != i)
+ printf("*** error: create sym for `%s' gives index %i not %i\n",
+ line[i], w->i, i);
+ }
+ else
+ {
+ if (flag[i])
+ printf("*** error: create `%s' gave new block, should be %p\n",
+ line[i], (void *)flag[i]);
+ else {
+ flag[i] = w;
+ w->i = i;
+ entries++;
+ }
+ }
+ } break;
+
+ case 2: {
+ sym_iter it;
+ sym_word *w, **ntbl;
+
+ printf("iterate\n");
+
+ ntbl = xmalloc(sz * sizeof(sym_word *));
+ memcpy(ntbl, flag, sz * sizeof(sym_word *));
+ sym_createIter(&it, &tbl);
+
+ while ((w = sym_next(&it)) != 0) {
+ if (ntbl[w->i] == 0)
+ printf("*** error: iterate returned duff item %i\n", w->i);
+ else
+ ntbl[w->i] = 0;
+ }
+
+ for (i = 0; i < sz; i++)
+ if (ntbl[i]) printf("*** error: iterate didn't return item %i\n",
+ i);
+ free(ntbl);
+ } break;
+
+ case 3: {
+ sym_base *b;
+ int v = rand() & 255 ? 0 : 1;
+
+ printf("dump\n");
+
+ for (i = 0; i <= tbl.mask; i++) {
+ if (!tbl.a[i]) continue;
+ if (v) printf(" %i: ", i);
+ b = tbl.a[i];
+ while (b) {
+ if ((b->hash & tbl.mask) != i)
+ printf("*** error: bad hash value found");
+ if (v) printf("`%s'(%08lx:%lu) ",
+ line[((sym_word *)b)->i],
+ b->hash,
+ b->hash & tbl.mask);
+ b = b->next;
+ }
+ if (v) putchar('\n');
+ }
+ } break;
+
+ case 4: {
+ if (flag[i]) {
+ printf("remove `%s'\n", flag[i]->base.name);
+ sym_remove(&tbl, flag[i]);
+ flag[i] = 0;
+ entries--;
+ }
+ } break;
+ }
+
+ }
+
+ return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: sym.h,v 1.1 1997/07/21 13:47:43 mdw Exp $
+ *
+ * Symbol table management
+ *
+ * (c) 1996 Straylight
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: sym.h,v $
+ * Revision 1.1 1997/07/21 13:47:43 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef SYM_H
+#define SYM_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#include <stddef.h>
+
+/*----- Type definitions --------------------------------------------------*/
+
+/* --- Symbol table --- *
+ *
+ * A @sym_table@ contains the information needed to manage a symbol table.
+ * Users shouldn't fiddle with this information directly, but it needs to be
+ * here so that objects of the correct type can be created.
+ */
+
+typedef struct sym_table {
+ unsigned long mask; /* Bit mask for hashing purposes */
+ size_t c; /* Down counter for growing table */
+ struct sym_base **a; /* Array of hash bins */
+} sym_table;
+
+/* --- A symbol table entry --- *
+ *
+ * I don't care what actually gets stored in symbol entries because I don't
+ * create them: that's the responsibility of my client. All I care about
+ * here is that whatever gets passed to me is a structure whose first member
+ * is a @sym_base@. The ANSI guarantees about structure layout are
+ * sufficient to allow me to manipulate such objects.
+ */
+
+typedef struct sym_base {
+ struct sym_base *next; /* Next symbol in hash bin */
+ unsigned long hash; /* Hash value for symbol's name */
+ char *name; /* Name of this symbol */
+ size_t len; /* Length of the symbol's name */
+} sym_base;
+
+/* --- An iterator block --- */
+
+typedef struct sym_iter {
+ sym_table *t; /* Symbol table being iterated */
+ sym_base *n; /* Address of next item to return */
+ size_t i; /* Index of next hash bin to use */
+} sym_iter;
+
+/*----- External functions ------------------------------------------------*/
+
+/* --- @sym_createTable@ --- *
+ *
+ * Arguments: @sym_table *t@ = symbol table to initialise
+ *
+ * Returns: ---
+ *
+ * Use: Initialises the given symbol table.
+ */
+
+extern void sym_createTable(sym_table */*t*/);
+
+/* --- @sym_destroyTable@ --- *
+ *
+ * Arguments: @sym_table *t@ = pointer to symbol table in question
+ *
+ * Returns: ---
+ *
+ * Use: Destroys a symbol table, freeing all the memory it used to
+ * occupy.
+ */
+
+extern void sym_destroyTable(sym_table */*t*/);
+
+/* --- @sym_find@ --- *
+ *
+ * Arguments: @sym_table *t@ = pointer to symbol table in question
+ * @const char *n@ = pointer to symbol table to look up
+ * @long l@ = length of the name string or negative to measure
+ * @size_t sz@ = size of desired symbol object, or zero
+ * @unsigned *f@ = pointer to a flag, or null.
+ *
+ * Returns: The address of a @sym_base@ structure, or null if not found
+ * and @sz@ is zero.
+ *
+ * Use: Looks up a symbol in a given symbol table. The name is
+ * passed by the address of its first character. The length
+ * may be given, in which case the name may contain arbitrary
+ * binary data, or it may be given as a negative number, in
+ * which case the length of the name is calculated as
+ * @strlen(n)@.
+ *
+ * The return value is the address of a pointer to a @sym_base@
+ * block (which may have other things on the end, as above). If
+ * the symbol could be found, the return value points to the
+ * symbol block. If the symbol wasn't there, then if @sz@ is
+ * nonzero, a new symbol is created and its address is returned;
+ * otherwise a null pointer is returned.
+ *
+ * The value of @*f@ indicates whether a new symbol entry was
+ * created: a nonzero value indicates that an old value was
+ * found.
+ */
+
+extern void *sym_find(sym_table */*t*/, const char */*n*/, long /*l*/,
+ size_t /*sz*/, unsigned */*f*/);
+
+/* --- @sym_remove@ --- *
+ *
+ * Arguments: @sym_table *i@ = pointer to a symbol table object
+ * @void *b@ = pointer to symbol table entry
+ *
+ * Returns: ---
+ *
+ * Use: Removes the object from the symbol table. The space occupied
+ * by the object and its name is freed; anything else attached
+ * to the entry should already be gone by this point.
+ */
+
+extern void sym_remove(sym_table */*t*/, void */*b*/);
+
+/* --- @sym_createIter@ --- *
+ *
+ * Arguments: @sym_iter *i@ = pointer to an iterator object
+ * @sym_table *t@ = pointer to a symbol table object
+ *
+ * Returns: ---
+ *
+ * Use: Creates a new symbol table iterator which may be used to
+ * iterate through a symbol table.
+ */
+
+extern void sym_createIter(sym_iter */*i*/, sym_table */*t*/);
+
+/* --- @sym_next@ --- *
+ *
+ * Arguments: @sym_iter *i@ = pointer to iterator object
+ *
+ * Returns: Pointer to the next symbol found, or null when finished.
+ *
+ * Use: Returns the next symbol from the table. Symbols are not
+ * returned in any particular order.
+ */
+
+extern void *sym_next(sym_iter */*i*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: tx.c,v 1.1 1997/07/21 13:47:43 mdw Exp $
+ *
+ * Transfer for keys and other large integers
+ *
+ * (c) 1997 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: tx.c,v $
+ * Revision 1.1 1997/07/21 13:47:43 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Local headers --- */
+
+#include "config.h"
+#include "tx.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @tx_getBits@ --- *
+ *
+ * Arguments: @unsigned char *k@ = pointer to key array to unpack into
+ * @size_t sz@ = number of bits to read (elements in array)
+ * @FILE *fp@ = stream to read from
+ *
+ * Returns: ---
+ *
+ * Use: Reads a number of bits into an array. The least significant
+ * bits of the final word are cleared to zero.
+ */
+
+void tx_getBits(unsigned char *k, size_t sz, FILE *fp)
+{
+ int i = 0;
+ unsigned char a = 0;
+ unsigned int ch;
+ size_t wsz = (size_t)((sz + 7ul) & ~7ul);
+ int t;
+
+ while ((t = getc(fp)) != EOF) {
+
+ /* --- Allow separators for readbility --- */
+
+ if (t == '-')
+ continue;
+
+ /* --- Standard converting-from-hex-digit code --- *
+ *
+ * Assumes that 'a'--'f' and 'A'--'F' are contiguous and in order, in
+ * addition to the guarantee that '0'--'9' are like this. The assumption
+ * is true in ASCII (and character sets based thereon) and EBCDIC.
+ */
+
+ ch = (unsigned)t;
+ ch -= '0';
+ if (ch > 9) ch -= 'A' - '0' - 10;
+ if (ch > 15) ch -= 'a' - 'A';
+ if (ch > 15) break;
+
+ /* --- Accumulate and maybe store --- */
+
+ a = (a << 4) | ch;
+ i++;
+ sz -= 4, wsz -= 4;
+ if ((i & 1) == 0)
+ *k++ = a, a = 0;
+ if (!sz)
+ break;
+ }
+
+ /* --- Pad the rest out with zeros --- */
+
+ while (wsz) {
+ a <<= 4;
+ i++;
+ wsz -= 4;
+ if ((i & 1) == 0)
+ *k++ = a, a = 0;
+ }
+}
+
+/* --- @tx_putBits@ --- *
+ *
+ * Arguments: @unsigned char *k@ = pointer to key block
+ * @size_t sz@ = number of bits to write
+ * @FILE *fp@ = pointer to stream to write on
+ *
+ * Returns: ---
+ *
+ * Use: Complements @tx_getBits@ above. Writes a number of bits
+ * to a file in an easy-to-read and transportable format (hex!)
+ */
+
+void tx_putBits(unsigned char *k, size_t sz, FILE *fp)
+{
+ const static char hex[16] = "0123456789abcdef";
+ size_t dash;
+ size_t d;
+ unsigned char i;
+
+ /* --- Don't do anything unless we have to --- */
+
+ if (!sz)
+ return;
+
+ /* --- Now decide where to `dash' the output --- */
+
+ if (sz % 32 == 0)
+ dash = 4;
+ else if (sz % 40 == 0)
+ dash = 5;
+ else
+ dash = 0;
+
+ /* --- Start writing values out --- */
+
+ d = dash;
+
+ for (;;) {
+
+ /* --- Write next byte out --- */
+
+ i = *k++;
+ putc(hex[(i >> 4) & 0x0fu], fp);
+ putc(hex[(i >> 0) & 0x0fu], fp);
+
+ /* --- If done, stop now --- */
+
+ if (sz -= 8, sz == 0)
+ break;
+
+ /* --- If need a dash, print one --- */
+
+ if (!--d) {
+ putc('-', fp);
+ d = dash;
+ }
+ }
+
+ /* --- Print the final newline --- */
+
+ fputc('\n', fp);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: tx.h,v 1.1 1997/07/21 13:47:43 mdw Exp $
+ *
+ * Transfer for keys and other large integers
+ *
+ * (c) 1997 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: tx.h,v $
+ * Revision 1.1 1997/07/21 13:47:43 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef TX_H
+#define TX_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#include <stddef.h>
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @tx_getBits@ --- *
+ *
+ * Arguments: @unsigned char *k@ = pointer to key array to unpack into
+ * @size_t sz@ = number of bits to read (elements in array)
+ * @FILE *fp@ = stream to read from
+ *
+ * Returns: ---
+ *
+ * Use: Reads a number of bits into an array. The least significant
+ * bits of the final word are cleared to zero.
+ */
+
+extern void tx_getBits(unsigned char */*k*/, size_t /*sz*/, FILE */*fp*/);
+
+/* --- @tx_putBits@ --- *
+ *
+ * Arguments: @unsigned char *k@ = pointer to key block
+ * @size_t sz@ = number of bits to write
+ * @FILE *fp@ = pointer to stream to write on
+ *
+ * Returns: ---
+ *
+ * Use: Complements @tx_getBits@ above. Writes a number of bits
+ * to a file in an easy-to-read and transportable format (hex!)
+ */
+
+extern void tx_putBits(unsigned char */*k*/, size_t /*sz*/, FILE */*fp*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: userdb.c,v 1.1 1997/07/21 13:47:43 mdw Exp $
+ *
+ * User database management
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: userdb.c,v $
+ * Revision 1.1 1997/07/21 13:47:43 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Unix headers --- */
+
+#include <sys/types.h>
+
+#include <grp.h>
+#include <pwd.h>
+#include <unistd.h>
+
+/* --- Local headers --- */
+
+#include "config.h"
+#include "sym.h"
+#include "userdb.h"
+#include "utils.h"
+
+/*----- Type definitions --------------------------------------------------*/
+
+/* --- A map link --- */
+
+typedef struct userdb__node {
+ struct userdb__node *next;
+ void *rec;
+} userdb__node;
+
+/* --- A reference to a real record --- */
+
+typedef struct userdb__sym {
+ sym_base _base;
+ void *rec;
+} userdb__sym;
+
+/* --- A name- and number-mapping --- */
+
+typedef struct userdb__map {
+ sym_table nmap;
+ sym_table idmap;
+ userdb__node *list;
+} userdb__map;
+
+/*----- Static variables --------------------------------------------------*/
+
+static userdb__map userdb__users; /* Map of user info blocks */
+static sym_iter userdb__useri; /* Iterator for users */
+static userdb__map userdb__groups; /* Map of group info blocks */
+static sym_iter userdb__groupi; /* Iterator for groups */
+
+/*----- Map management functions ------------------------------------------*/
+
+/* --- @userdb__createMap@ --- *
+ *
+ * Arguments: @userdb__map *m@ = pointer to a map block
+ *
+ * Returns: ---
+ *
+ * Use: Initialises a map table.
+ */
+
+static void userdb__createMap(userdb__map *m)
+{
+ sym_createTable(&m->nmap);
+ sym_createTable(&m->idmap);
+ m->list = 0;
+}
+
+/* --- @userdb__addToMap@ --- *
+ *
+ * Arguments: @userdb__map *m@ = pointer to the map block
+ * @const char *name@ = pointer to the item's name
+ * @int id@ = the item's id number
+ * @void *rec@ = pointer to the actual record
+ *
+ * Returns: ---
+ *
+ * Use: Adds an item to the given map.
+ */
+
+static void userdb__addToMap(userdb__map *m,
+ const char *name,
+ int id, void *rec)
+{
+ unsigned f;
+ userdb__sym *s;
+ userdb__node *n;
+
+ s = sym_find(&m->nmap, name, -1, sizeof(*s), &f);
+ if (!f)
+ s->rec = rec;
+
+ s = sym_find(&m->idmap, (char *)&id, sizeof(id), sizeof(*s), &f);
+ if (!f)
+ s->rec = rec;
+
+ n = xmalloc(sizeof(*n));
+ n->rec = rec;
+ n->next = m->list;
+ m->list = n;
+}
+
+/* --- @userdb__byName@ --- *
+ *
+ * Arguments: @userdb__map *m@ = pointer to a map block
+ * @const char *name@ = name to look up
+ *
+ * Returns: A pointer to the appropriate block, or zero if not found.
+ *
+ * Use: Looks up a name in a mapping and returns the result.
+ */
+
+static void *userdb__byName(userdb__map *m, const char *name)
+{
+ userdb__sym *s = sym_find(&m->nmap, name, -1, 0, 0);
+ return (s ? s->rec : 0);
+}
+
+/* --- @userdb__byId@ --- *
+ *
+ * Arguments: @userdb__map *m@ = pointer to a map block
+ * @int id@ = id number to find
+ *
+ * Returns: A pointer to the appropriate block, or zero if not found.
+ *
+ * Use: Looks up an ID in a mapping, and returns the result.
+ */
+
+static void *userdb__byId(userdb__map *m, int id)
+{
+ userdb__sym *s = sym_find(&m->idmap, (char *)&id, sizeof(id), 0, 0);
+ return (s ? s->rec : 0);
+}
+
+/* --- @userdb__clearMap@ --- *
+ *
+ * Arguments: @userdb__map *m@ = pointer to a map block
+ * @void (*freerec)(void *rec)@ = pointer to a free-record proc
+ *
+ * Returns: ---
+ *
+ * Use: Clears a map, emptying it and releasing the memory it
+ * occupied.
+ */
+
+static void userdb__clearMap(userdb__map *m, void (*freerec)(void *rec))
+{
+ userdb__node *n, *t;
+
+ sym_destroyTable(&m->nmap);
+ sym_destroyTable(&m->idmap);
+
+ for (n = m->list; n; n = t) {
+ t = n->next;
+ freerec(n->rec);
+ free(n);
+ }
+}
+
+/*----- User and group block management -----------------------------------*/
+
+/* --- @userdb__dumpUser@ --- *
+ *
+ * Arguments: @const struct passwd *pw@ = pointer to a user block
+ * @FILE *fp@ = pointer to stream to write on
+ *
+ * Returns: ---
+ *
+ * Use: Writes a user's informationt to a stream.
+ */
+
+#ifndef NDEBUG
+
+static void userdb__dumpUser(const struct passwd *pw, FILE *fp)
+{
+ printf("\n"
+ "*** name == %s\n"
+ "*** passwd == %s\n"
+ "*** uid == %i\n"
+ "*** gid == %i\n"
+ "*** gecos == %s\n"
+ "*** home == %s\n"
+ "*** shell == %s\n",
+ pw->pw_name, pw->pw_passwd, (int)pw->pw_uid, (int)pw->pw_gid,
+ pw->pw_gecos, pw->pw_dir, pw->pw_shell);
+}
+
+#else
+
+#define userdb__dumpUser(pw, fp) ((void)0)
+
+#endif
+
+/* --- @userdb_copyUser@ --- *
+ *
+ * Arguments: @struct passwd *pw@ = pointer to block to copy
+ *
+ * Returns: Pointer to the copy.
+ *
+ * Use: Copies a user block. The copy is `deep' so all the strings
+ * are copied too. Free the copy with @userdb_freeUser@ when
+ * you don't want it any more.
+ */
+
+struct passwd *userdb_copyUser(struct passwd *pw)
+{
+ struct passwd *npw;
+
+ if (!pw)
+ return (0);
+
+ npw = xmalloc(sizeof(*npw));
+
+ npw->pw_name = xstrdup(pw->pw_name);
+ npw->pw_passwd = xstrdup(pw->pw_passwd);
+ npw->pw_uid = pw->pw_uid;
+ npw->pw_gid = pw->pw_gid;
+ npw->pw_gecos = xstrdup(pw->pw_gecos);
+ npw->pw_dir = xstrdup(pw->pw_dir);
+ npw->pw_shell = xstrdup(pw->pw_shell);
+
+ return (npw);
+}
+
+/* --- @userdb__buildUser@ --- *
+ *
+ * Arguments: @char *s@ = pointer to user string
+ *
+ * Returns: Pointer to a user block.
+ *
+ * Use: Converts a line from a user file into a password entry.
+ * Note that the string is corrupted by @strtok@ while it gets
+ * parsed.
+ */
+
+static struct passwd *userdb__buildUser(char *s)
+{
+ struct passwd *pw = xmalloc(sizeof(*pw));
+
+ s = strtok(s, ":"); if (!s) goto tidy_0; pw->pw_name = xstrdup(s);
+ s = strtok(0, ":"); if (!s) goto tidy_1; pw->pw_passwd = xstrdup(s);
+ s = strtok(0, ":"); if (!s) goto tidy_2; pw->pw_uid = atoi(s);
+ s = strtok(0, ":"); if (!s) goto tidy_2; pw->pw_gid = atoi(s);
+ s = strtok(0, ":"); if (!s) goto tidy_2; pw->pw_gecos = xstrdup(s);
+ s = strtok(0, ":"); if (!s) goto tidy_3; pw->pw_dir = xstrdup(s);
+ s = strtok(0, ":"); if (!s) goto tidy_4; pw->pw_shell = xstrdup(s);
+ return (pw);
+
+ /* --- Error handling --- */
+
+tidy_4:
+ free(pw->pw_dir);
+tidy_3:
+ free(pw->pw_gecos);
+tidy_2:
+ free(pw->pw_passwd);
+tidy_1:
+ free(pw->pw_name);
+tidy_0:
+ free(pw);
+
+ return (0);
+}
+
+/* --- @userdb_freeUser@ --- *
+ *
+ * Arguments: @void *rec@ = pointer to a user record
+ *
+ * Returns: ---
+ *
+ * Use: Frees a user record.
+ */
+
+void userdb_freeUser(void *rec)
+{
+ struct passwd *pw;
+
+ if (!rec)
+ return;
+
+ pw = rec;
+ free(pw->pw_name);
+ free(pw->pw_passwd);
+ free(pw->pw_gecos);
+ free(pw->pw_dir);
+ free(pw->pw_shell);
+ free(pw);
+}
+
+/* --- @userdb__dumpGroup@ --- *
+ *
+ * Arguments: @const struct group *gr@ = pointer to a group block
+ * @FILE *fp@ = pointer to stream to write on
+ *
+ * Returns: ---
+ *
+ * Use: Writes a group's information to a stream.
+ */
+
+#ifndef NDEBUG
+
+static void userdb__dumpGroup(const struct group *gr, FILE *fp)
+{
+ char *const *p;
+
+ printf("\n"
+ "*** name == %s\n"
+ "*** passwd == %s\n"
+ "*** gid == %i\n"
+ "*** members...\n",
+ gr->gr_name, gr->gr_passwd, (int)gr->gr_gid);
+ for (p = gr->gr_mem; *p; p++)
+ printf("*** %s\n", *p);
+}
+
+#else
+
+#define userdb__dumpUser(pw, fp) ((void)0)
+
+#endif
+
+/* --- @userdb_copyGroup@ --- *
+ *
+ * Arguments: @struct group *gr@ = pointer to group block
+ *
+ * Returns: Pointer to copied block
+ *
+ * Use: Copies a group block. The copy is `deep' so all the strings
+ * are copied too. Free the copy with @userdb_freeGroup@ when
+ * you don't want it any more.
+ */
+
+struct group *userdb_copyGroup(struct group *gr)
+{
+ struct group *ngr;
+ int i, max;
+
+ if (!gr)
+ return (0);
+
+ ngr = xmalloc(sizeof(*ngr));
+
+ ngr->gr_name = xstrdup(gr->gr_name);
+ ngr->gr_passwd = xstrdup(gr->gr_passwd);
+ ngr->gr_gid = gr->gr_gid;
+
+ for (max = 0; gr->gr_mem[max]; max++)
+ ;
+ ngr->gr_mem = xmalloc((max + 1) * sizeof(char *));
+ for (i = 0; i < max; i++)
+ ngr->gr_mem[i] = xstrdup(gr->gr_mem[i]);
+ ngr->gr_mem[max] = 0;
+
+ return (ngr);
+}
+
+/* --- @userdb__buildGroup@ --- *
+ *
+ * Arguments: @char *s@ = pointer to group line string
+ *
+ * Returns: Pointer to a group block
+ *
+ * Use: Parses an entry in the groups file. The string is garbled
+ * by @strtok@ as we go.
+ */
+
+static struct group *userdb__buildGroup(char *s)
+{
+ struct group *gr = xmalloc(sizeof(*gr));
+ char *p;
+ int i;
+
+ /* --- Do the easy bits --- */
+
+ s = strtok(s, ":"); if (!s) goto tidy_0; gr->gr_name = xstrdup(s);
+ s = strtok(0, ":"); if (!s) goto tidy_1; gr->gr_passwd = xstrdup(s);
+ s = strtok(0, ":"); if (!s) goto tidy_2; gr->gr_gid = atoi(s);
+
+ /* --- Find the start of the member list --- */
+
+ s = strtok(0, "");
+ if (!s)
+ goto tidy_2;
+
+ /* --- Count the number of members --- */
+
+ p = s;
+ i = 0;
+ for (;;) {
+ i++;
+ if ((p = strpbrk(p, ",")) == 0)
+ break;
+ p++;
+ }
+
+ /* --- Allocate the block and fill it --- */
+
+ gr->gr_mem = xmalloc((i + 1) * sizeof(char *));
+ i = 0;
+ s = strtok(s, ",");
+ do {
+ gr->gr_mem[i++] = xstrdup(s);
+ s = strtok(0, ",");
+ } while (s);
+ gr->gr_mem[i] = 0;
+
+ return (gr);
+
+ /* --- Various tidying-up things --- */
+
+tidy_2:
+ free(gr->gr_passwd);
+tidy_1:
+ free(gr->gr_name);
+tidy_0:
+ free(gr);
+
+ return (0);
+}
+
+/* --- @userdb_freeGroup@ --- *
+ *
+ * Arguments: @void *rec@ = pointer to a group record
+ *
+ * Returns: ---
+ *
+ * Use: Frees a group record.
+ */
+
+void userdb_freeGroup(void *rec)
+{
+ struct group *gr;
+ char **p;
+
+ if (!rec)
+ return;
+
+ gr = rec;
+ free(gr->gr_name);
+ free(gr->gr_passwd);
+ for (p = gr->gr_mem; *p; p++)
+ free(*p);
+ free(gr->gr_mem);
+ free(gr);
+}
+
+/*----- Higher-level functions --------------------------------------------*/
+
+/* --- @userdb_local@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Reads the local list of users into the maps.
+ */
+
+void userdb_local(void)
+{
+ D( printf("adding local users...\n"); )
+
+ /* --- Fetch users first --- */
+
+ {
+ struct passwd *pw;
+
+ setpwent();
+ while ((pw = getpwent()) != 0) {
+ D( userdb__dumpUser(pw, stdout); )
+ if (!userdb__byName(&userdb__users, pw->pw_name))
+ userdb__addToMap(&userdb__users, pw->pw_name, pw->pw_uid,
+ userdb_copyUser(pw));
+ }
+ endpwent();
+ }
+
+ /* --- Then fetch groups --- */
+
+ {
+ struct group *gr;
+
+ setgrent();
+ while ((gr = getgrent()) != 0) {
+ D( userdb__dumpGroup(gr, stdout); )
+ if (!userdb__byName(&userdb__groups, gr->gr_name))
+ userdb__addToMap(&userdb__groups, gr->gr_name, gr->gr_gid,
+ userdb_copyGroup(gr));
+ }
+ endgrent();
+ }
+}
+
+/* --- @userdb__getLine@ --- *
+ *
+ * Arguments: @char *buff@ = pointer to buffer to read into
+ * @size_t sz@ = size of the buffer
+ * @FILE *fp@ = pointer to stream to read on
+ *
+ * Returns: Zero if something didn't work.
+ *
+ * Use: Reads a line into the buffer. If the line didn't fit, bits
+ * of it are thrown away. The newline character is not
+ * included.
+ */
+
+static char *userdb__getLine(char *buff, size_t sz, FILE *fp)
+{
+ if ((buff = fgets(buff, sz, fp)) == 0)
+ return (0);
+ sz = strlen(buff) - 1;
+ if (buff[sz] == '\n')
+ buff[sz] = 0;
+ else for (;;) {
+ int ch = getc(fp);
+ if (ch == '\n' || ch == EOF)
+ break;
+ }
+ return (buff);
+}
+
+/* --- @userdb_yp@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Fetches the YP database of users.
+ */
+
+void userdb_yp(void)
+{
+
+#ifdef HAVE_YP
+
+ char line[1024];
+ FILE *fp;
+
+ D( printf("adding nis users\n"); )
+
+ /* --- First, users --- */
+
+ if ((fp = popen("ypcat passwd", "r")) != 0) {
+ while (userdb__getLine(line, sizeof(line), fp)) {
+ struct passwd *pw;
+
+ if ((pw = userdb__buildUser(line)) != 0) {
+ D( userdb__dumpUser(pw, stdout); )
+ if (userdb__byName(&userdb__users, pw->pw_name))
+ userdb_freeUser(pw);
+ else
+ userdb__addToMap(&userdb__users, pw->pw_name, pw->pw_uid, pw);
+ }
+ }
+ pclose(fp);
+ }
+
+ /* --- Next, groups --- */
+
+
+ if ((fp = popen("ypcat group", "r")) != 0) {
+ while (userdb__getLine(line, sizeof(line), fp)) {
+ struct group *gr;
+
+ if ((gr = userdb__buildGroup(line)) != 0) {
+ D( userdb__dumpGroup(gr, stdout); )
+ if (userdb__byName(&userdb__groups, gr->gr_name))
+ userdb_freeGroup(gr);
+ else
+ userdb__addToMap(&userdb__groups, gr->gr_name, gr->gr_gid, gr);
+ }
+ }
+ pclose(fp);
+ }
+
+#endif
+
+}
+
+/* --- @userdb_userByName@, @userdb_userById@ --- *
+ *
+ * Arguments: @const char *name@ = pointer to user's name
+ * @uid_t id@ = user id to find
+ *
+ * Returns: Pointer to user block, or zero if not found.
+ *
+ * Use: Looks up a user by name or id.
+ */
+
+struct passwd *userdb_userByName(const char *name)
+{ return (userdb__byName(&userdb__users, name)); }
+
+struct passwd *userdb_userById(uid_t id)
+{ return (userdb__byId(&userdb__users, id)); }
+
+/* --- @userdb_iterateUsers@, @userdb_iterateUsers_r@ --- *
+ *
+ * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator object
+ *
+ * Returns: ---
+ *
+ * Use: Initialises an iteration for the user database.
+ */
+
+void userdb_iterateUsers(void)
+{ userdb_iterateUsers_r(&userdb__useri); }
+
+void userdb_iterateUsers_r(userdb_iter *i)
+{ sym_createIter(i, &userdb__users.nmap); }
+
+/* --- @userdb_nextUser@, @userdb_nextUser_r@ --- *
+ *
+ * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator oject
+ *
+ * Returns: Pointer to the next user block, or null.
+ *
+ * Use: Returns another user block.
+ */
+
+struct passwd *userdb_nextUser(void)
+{ return (userdb_nextUser_r(&userdb__useri)); }
+
+struct passwd *userdb_nextUser_r(userdb_iter *i)
+{
+ userdb__sym *s = sym_next(i);
+ return (s ? s->rec : 0);
+}
+
+/* --- @userdb_groupByName@, @userdb_groupById@ --- *
+ *
+ * Arguments: @const char *name@ = pointer to group's name
+ * @gid_t id@ = group id to find
+ *
+ * Returns: Pointer to group block, or zero if not found.
+ *
+ * Use: Looks up a group by name or id.
+ */
+
+struct group *userdb_groupByName(const char *name)
+{ return (userdb__byName(&userdb__groups, name)); }
+
+struct group *userdb_groupById(gid_t id)
+{ return (userdb__byId(&userdb__groups, id)); }
+
+/* --- @userdb_iterateGroups@, @userdb_iterateGroups_r@ --- *
+ *
+ * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator object
+ *
+ * Returns: ---
+ *
+ * Use: Initialises an iteration for the group database.
+ */
+
+void userdb_iterateGroups(void)
+{ userdb_iterateGroups_r(&userdb__groupi); }
+
+void userdb_iterateGroups_r(userdb_iter *i)
+{ sym_createIter(i, &userdb__groups.nmap); }
+
+/* --- @userdb_nextGroup@, @userdb_nextGroup_r@ --- *
+ *
+ * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator oject
+ *
+ * Returns: Pointer to the next group block, or null.
+ *
+ * Use: Returns another group block.
+ */
+
+struct group *userdb_nextGroup(void)
+{ return (userdb_nextGroup_r(&userdb__groupi)); }
+
+struct group *userdb_nextGroup_r(userdb_iter *i)
+{
+ userdb__sym *s = sym_next(i);
+ return (s ? s->rec : 0);
+}
+
+/* --- @userdb_init@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Initialises the user database.
+ */
+
+void userdb_init(void)
+{
+ userdb__createMap(&userdb__users);
+ userdb__createMap(&userdb__groups);
+}
+
+/* --- @userdb_reinit@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Reinitialises the user database.
+ */
+
+void userdb_reinit(void)
+{
+ userdb__clearMap(&userdb__users, userdb_freeUser);
+ userdb__clearMap(&userdb__groups, userdb_freeGroup);
+ userdb_init();
+}
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+void dumpit(const char *msg)
+{
+ printf("\n\n$$$ %s\n", msg);
+
+ {
+ struct passwd *pw;
+ for (userdb_iterateUsers(); (pw = userdb_nextUser()) != 0; )
+ userdb__dumpUser(pw, stdout);
+ }
+
+ {
+ struct group *gr;
+ for (userdb_iterateGroups(); (gr = userdb_nextGroup()) != 0; )
+ userdb__dumpGroup(gr, stdout);
+ }
+}
+
+int main(void)
+{
+ userdb_init();
+ dumpit("cleared");
+ userdb_local();
+ dumpit("local");
+ userdb_yp();
+ dumpit("yp");
+ return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id*
+ *
+ * User database management
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: userdb.h,v $
+ * Revision 1.1 1997/07/21 13:47:42 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef USERDB_H
+#define USERDB_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#include <grp.h>
+#include <pwd.h>
+
+#ifndef SYM_H
+# include "sym.h"
+#endif
+
+/*----- Type definitions --------------------------------------------------*/
+
+/* --- Iterator objects --- */
+
+typedef sym_iter userdb_iter;
+
+/*----- User and group block manipulation ---------------------------------*/
+
+/* --- @userdb_copyUser@ --- *
+ *
+ * Arguments: @struct passwd *pw@ = pointer to block to copy
+ *
+ * Returns: Pointer to the copy.
+ *
+ * Use: Copies a user block. The copy is `deep' so all the strings
+ * are copied too. Free the copy with @userdb_freeUser@ when
+ * you don't want it any more.
+ */
+
+extern struct passwd *userdb_copyUser(struct passwd */*pw*/);
+
+/* --- @userdb_freeUser@ --- *
+ *
+ * Arguments: @void *rec@ = pointer to a user record
+ *
+ * Returns: ---
+ *
+ * Use: Frees a user record.
+ */
+
+extern void userdb_freeUser(void */*rec*/);
+
+/* --- @userdb_copyGroup@ --- *
+ *
+ * Arguments: @struct group *gr@ = pointer to group block
+ *
+ * Returns: Pointer to copied block
+ *
+ * Use: Copies a group block. The copy is `deep' so all the strings
+ * are copied too. Free the copy with @userdb_freeGroup@ when
+ * you don't want it any more.
+ */
+
+extern struct group *userdb_copyGroup(struct group */*gr*/);
+
+/* --- @userdb_freeGroup@ --- *
+ *
+ * Arguments: @void *rec@ = pointer to a group record
+ *
+ * Returns: ---
+ *
+ * Use: Frees a group record.
+ */
+
+extern void userdb_freeGroup(void */*rec*/);
+
+/*----- Internal user database mapping ------------------------------------*/
+
+/* --- @userdb_local@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Reads the local list of users into the maps.
+ */
+
+extern void userdb_local(void);
+
+/* --- @userdb_yp@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Fetches the YP database of users.
+ */
+
+extern void userdb_yp(void);
+
+/* --- @userdb_userByName@, @userdb_userById@ --- *
+ *
+ * Arguments: @const char *name@ = pointer to user's name
+ * @uid_t id@ = user id to find
+ *
+ * Returns: Pointer to user block, or zero if not found.
+ *
+ * Use: Looks up a user by name or id.
+ */
+
+extern struct passwd *userdb_userByName(const char */*name*/);
+extern struct passwd *userdb_userById(uid_t /*id*/);
+
+/* --- @userdb_iterateUsers@, @userdb_iterateUsers_r@ --- *
+ *
+ * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator object
+ *
+ * Returns: ---
+ *
+ * Use: Initialises an iteration for the user database.
+ */
+
+extern void userdb_iterateUsers(void);
+extern void userdb_iterateUsers_r(userdb_iter */*i*/);
+
+/* --- @userdb_nextUser@, @userdb_nextUser_r@ --- *
+ *
+ * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator oject
+ *
+ * Returns: Pointer to the next user block, or null.
+ *
+ * Use: Returns another user block.
+ */
+
+extern struct passwd *userdb_nextUser(void);
+extern struct passwd *userdb_nextUser_r(userdb_iter */*i*/);
+
+/* --- @userdb_groupByName@, @userdb_groupById@ --- *
+ *
+ * Arguments: @const char *name@ = pointer to group's name
+ * @gid_t id@ = group id to find
+ *
+ * Returns: Pointer to group block, or zero if not found.
+ *
+ * Use: Looks up a group by name or id.
+ */
+
+extern struct group *userdb_groupByName(const char */*name*/);
+extern struct group *userdb_groupById(gid_t /*id*/);
+
+/* --- @userdb_iterateGroups@, @userdb_iterateGroups_r@ --- *
+ *
+ * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator object
+ *
+ * Returns: ---
+ *
+ * Use: Initialises an iteration for the group database.
+ */
+
+extern void userdb_iterateGroups(void);
+extern void userdb_iterateGroups_r(userdb_iter */*i*/);
+
+/* --- @userdb_nextGroup@, @userdb_nextGroup_r@ --- *
+ *
+ * Arguments: @userdb_iter *i@ = pointer to a symbol table iterator oject
+ *
+ * Returns: Pointer to the next group block, or null.
+ *
+ * Use: Returns another group block.
+ */
+
+extern struct group *userdb_nextGroup(void);
+extern struct group *userdb_nextGroup_r(userdb_iter */*i*/);
+
+/* --- @userdb_init@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Initialises the user database.
+ */
+
+extern void userdb_init(void);
+
+/* --- @userdb_reinit@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Reinitialises the user database.
+ */
+
+extern void userdb_reinit(void);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: utils.c,v 1.1 1997/07/21 13:47:42 mdw Exp $
+ *
+ * Miscellaneous useful bits of code.
+ *
+ * (c) 1997 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: utils.c,v $
+ * Revision 1.1 1997/07/21 13:47:42 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* --- Local headers --- */
+
+#include "config.h"
+#include "utils.h"
+
+/*----- Static data -------------------------------------------------------*/
+
+static const char *myname = 0; /* What's my name? */
+
+/*----- Program name handling ---------------------------------------------*/
+
+/* --- @quis@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: Pointer to the program name.
+ *
+ * Use: Returns the program name.
+ */
+
+const char *quis(void)
+{
+ return (myname);
+}
+
+/* --- @ego@ --- *
+ *
+ * Arguments: @const char *p@ = pointer to program name
+ *
+ * Returns: ---
+ *
+ * Use: Tells the utils library what the program's name is.
+ */
+
+#ifndef PATHSEP
+# if defined(__riscos)
+# define PATHSEP '.'
+# elif defined(__unix) || defined(unix)
+# define PATHSEP '/'
+# else
+# define PATHSEP '\\'
+# endif
+#endif
+
+void ego(const char *p)
+{
+ const char *q = p;
+ while (*q) {
+ if (*q++ == PATHSEP)
+ p = q;
+ }
+ myname = p;
+}
+
+/*----- Error reporting ---------------------------------------------------*/
+
+/* --- @moan@ --- *
+ *
+ * Arguments: @const char *f@ = a @printf@-style format string
+ * @...@ = other arguments
+ *
+ * Returns: ---
+ *
+ * Use: Reports an error.
+ */
+
+void moan(const char *f, ...)
+{
+ va_list ap;
+ va_start(ap, f);
+ fprintf(stderr, "%s: ", myname);
+ vfprintf(stderr, f, ap);
+ va_end(ap);
+ putc('\n', stderr);
+}
+
+/* --- @die@ --- *
+ *
+ * Arguments: @const char *f@ = a @printf@-style format string
+ * @...@ = other arguments
+ *
+ * Returns: Never.
+ *
+ * Use: Reports an error and hari-kiris. Like @moan@ above, only
+ * more permanent.
+ */
+
+void die(const char *f, ...)
+{
+ va_list ap;
+ va_start(ap, f);
+ fprintf(stderr, "%s: ", myname);
+ vfprintf(stderr, f, ap);
+ va_end(ap);
+ putc('\n', stderr);
+ exit(EXIT_FAILURE);
+}
+
+/*----- Memory management functions ---------------------------------------*/
+
+/* --- @xmalloc@ --- *
+ *
+ * Arguments: @size_t sz@ = size of block to allocate
+ *
+ * Returns: Pointer to allocated block.
+ *
+ * Use: Allocates memory. If the memory isn't available, we don't
+ * hang aroung long enough for a normal function return.
+ */
+
+void *xmalloc(size_t sz)
+{
+ void *p = malloc(sz);
+ if (!p)
+ die("not enough memory");
+ return (p);
+}
+
+/* --- @xstrdup@ --- *
+ *
+ * Arguments: @const char *s@ = pointer to a string
+ *
+ * Returns: Pointer to a copy of the string.
+ *
+ * Use: Copies a string (like @strdup@ would, if it existed).
+ */
+
+char *xstrdup(const char *s)
+{
+ size_t sz = strlen(s) + 1;
+ char *p = xmalloc(sz);
+ memcpy(p, s, sz);
+ return (p);
+}
+
+/* --- @xrealloc@ --- *
+ *
+ * Arguments: @void *p@ = pointer to a block of memory
+ * @size_t sz@ = new size desired for the block
+ *
+ * Returns: Pointer to the resized memory block (which is almost
+ * certainly not in the same place any more).
+ *
+ * Use: Resizes a memory block.
+ */
+
+void *xrealloc(void *p, size_t sz)
+{
+ p = realloc(p, sz);
+ if (!p)
+ die("not enough memory");
+ return (p);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: utils.h,v 1.1 1997/07/21 13:47:42 mdw Exp $
+ *
+ * Miscellaneous useful bits of code.
+ *
+ * (c) 1997 Mark Wooding
+ */
+
+/*----- Licencing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' 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.
+ *
+ * `Become' 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 `become'; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: utils.h,v $
+ * Revision 1.1 1997/07/21 13:47:42 mdw
+ * Initial revision
+ *
+ */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Required header files ---------------------------------------------*/
+
+#include <stddef.h>
+#include <stdlib.h>
+
+/*----- Storing and retrieving numbers ------------------------------------*
+ *
+ * These use big-endian conventions, because they seem more usual in network
+ * applications. I actually think that little-endian is more sensible...
+ */
+
+#define load32(p) \
+ ((((unsigned char)(p)[0] & 0xFF) << 24) | \
+ (((unsigned char)(p)[1] & 0xFF) << 16) | \
+ (((unsigned char)(p)[2] & 0xFF) << 8) | \
+ (((unsigned char)(p)[3] & 0xFF) << 0))
+
+#define store32(p, v) \
+ ((p)[0] = ((unsigned long)(v) >> 24) & 0xFF, \
+ (p)[1] = ((unsigned long)(v) >> 16) & 0xFF, \
+ (p)[2] = ((unsigned long)(v) >> 8) & 0xFF, \
+ (p)[3] = ((unsigned long)(v) >> 0) & 0xFF)
+
+/* --- Little-endian versions (for MD5) --- */
+
+#define load32_l(p) \
+ ((((unsigned char)(p)[0] & 0xFF) << 0) | \
+ (((unsigned char)(p)[1] & 0xFF) << 8) | \
+ (((unsigned char)(p)[2] & 0xFF) << 16) | \
+ (((unsigned char)(p)[3] & 0xFF) << 24))
+
+#define store32_l(p, v) \
+ ((p)[0] = ((unsigned long)(v) >> 0) & 0xFF, \
+ (p)[1] = ((unsigned long)(v) >> 8) & 0xFF, \
+ (p)[2] = ((unsigned long)(v) >> 16) & 0xFF, \
+ (p)[3] = ((unsigned long)(v) >> 24) & 0xFF)
+
+/*----- Program name handling ---------------------------------------------*/
+
+/* --- @quis@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: Pointer to the program name.
+ *
+ * Use: Returns the program name.
+ */
+
+extern const char *quis(void);
+
+/* --- @ego@ --- *
+ *
+ * Arguments: @const char *p@ = pointer to program name
+ *
+ * Returns: ---
+ *
+ * Use: Tells the utils library what the program's name is.
+ */
+
+extern void ego(const char */*p*/);
+
+/*----- Error reporting ---------------------------------------------------*/
+
+/* --- @moan@ --- *
+ *
+ * Arguments: @const char *f@ = a @printf@-style format string
+ * @...@ = other arguments
+ *
+ * Returns: ---
+ *
+ * Use: Reports an error.
+ */
+
+extern void moan(const char */*f*/, ...);
+
+/* --- @die@ --- *
+ *
+ * Arguments: @const char *f@ = a @printf@-style format string
+ * @...@ = other arguments
+ *
+ * Returns: Never.
+ *
+ * Use: Reports an error and hari-kiris. Like @moan@ above, only
+ * more permanent.
+ */
+
+extern void die(const char */*f*/, ...);
+
+/*----- Memory management functions ---------------------------------------*/
+
+/* --- @xmalloc@ --- *
+ *
+ * Arguments: @size_t sz@ = size of block to allocate
+ *
+ * Returns: Pointer to allocated block.
+ *
+ * Use: Allocates memory. If the memory isn't available, we don't
+ * hang aroung long enough for a normal function return.
+ */
+
+extern void *xmalloc(size_t /*sz*/);
+
+/* --- @xstrdup@ --- *
+ *
+ * Arguments: @const char *s@ = pointer to a string
+ *
+ * Returns: Pointer to a copy of the string.
+ *
+ * Use: Copies a string (like @strdup@ would, if it existed).
+ */
+
+extern char *xstrdup(const char */*s*/);
+
+/* --- @xrealloc@ --- *
+ *
+ * Arguments: @void *p@ = pointer to a block of memory
+ * @size_t sz@ = new size desired for the block
+ *
+ * Returns: Pointer to the resized memory block (which is almost
+ * certainly not in the same place any more).
+ *
+ * Use: Resizes a memory block.
+ */
+
+extern void *xrealloc(void */*p*/, size_t /*sz*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif