From: mdw Date: Mon, 21 Jul 1997 13:47:55 +0000 (+0000) Subject: Initial revision X-Git-Tag: 1.3.3~148 X-Git-Url: https://git.distorted.org.uk/~mdw/become/commitdiff_plain/c4f2d992e4a0fc068281376d89ec38de56dc2f58 Initial revision --- c4f2d992e4a0fc068281376d89ec38de56dc2f58 diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..36534f2 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,263 @@ +# -*-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 diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..e5f2d23 --- /dev/null +++ b/config.h.in @@ -0,0 +1,132 @@ +/* -*-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 diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..7dc5995 --- /dev/null +++ b/configure.in @@ -0,0 +1,103 @@ +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 -------------------------------------------------- diff --git a/manual/become.tex b/manual/become.tex new file mode 100644 index 0000000..b3cc061 --- /dev/null +++ b/manual/become.tex @@ -0,0 +1,888 @@ +%%% -*-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' \[ \[ \< \> \] \] +\end{syntdiag} +and +\begin{syntdiag} + `become' `-c' +\end{syntdiag} +The first variant allows you to execute any \ as user \, +as limited by your site's configuration. The second variant simply passes +the \ to \path{/bin/sh}, so you must have permission to +execute the shell as \. 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" "/bin/sh -c" }', 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' + \[ `[' `]' \] + `->' \[ \] + \[ `:' \] + `;' +\]] + +\end{grammar} +The items \, \ and \ 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} + + ::= \[[ + \[ \< \\ `,' \> \] + \]] + + ::= \[[ + \[ \< \\ `-' \> \] +\]] + + ::= \[[ + \[ \< \\ `|' \> \] +\]] + + ::= \[[ + \[ \< \\ `&' \> \] +\]] + + ::= \[[ + \( `(' `)' \\ \\ \) +\]] + +\end{grammar} + +\subsubsection{Naming classes} + +To save repetition, you can give names to classes, using one of the three +assignment statements: +\begin{grammar} + + ::= \[[ `user' `=' `;' \]] + + ::= \[[ `host' `=' `;' \]] + + ::= \[[ `command' `=' `;' \]] + +\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' \\ `--output' \] \> +\end{syntdiag} +If you don't specify a \ of bits, a default of 128 random bits are +generated, which is correct for IDEA keys. If you omit the \, 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" ";"}' 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" ";"}' 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} + \[ `:' \] +\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} + + ::= \[[ + `#' \< \tok{any character other than } \> +\]] + + ::= \[[ + \tok{letter or `_'} \< \( \tok{letter or `_'} \\ \tok{digit} \) \> +\]] + + ::= \[[ \< \tok{digit} \> \]] + + ::= \[[ + `"' \< \( \tok{any character other than `"', or `\\'} \\ + `\\' \tok{any character other than } \) \> `"' +\]] + +\end{grammar} + +All \s and whitespace are ignored entirely. What's left is parsed +as follows: + +\begin{grammar} + + ::= \[[ \< \> \]] + + ::= \[[ + \( \( `user' \\ `command' \\ `host' \) `=' `;' \\ + `allow' `;' \\ + `port' `;' \\ + `key' `;' \) +\]] + + ::= \[[ + \[ `[' `]' \] + `->' \[ \] + \[ `:' \] +\]] + + ::= \[[ + \[ \< \\ `,' \> \] + \]] + + ::= \[[ + \[ \< \\ `-' \> \] +\]] + + ::= \[[ + \[ \< \\ `|' \> \] +\]] + + ::= \[[ + \[ \< \\ `&' \> \] +\]] + + ::= \[[ + \( `(' `)' \\ \\ \\ \) +\]] + +\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: diff --git a/src/become.c b/src/become.c new file mode 100644 index 0000000..c2d8bcf --- /dev/null +++ b/src/become.c @@ -0,0 +1,480 @@ +/* -*-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 +#include +#include +#include +#include + +/* --- Unix headers --- */ + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +/* --- 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 \n" + " $ [ []...]\n" + " $ -d [-p ] [-f ]\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 -------------------------------------------------*/ diff --git a/src/blowfish-sbox.h b/src/blowfish-sbox.h new file mode 100644 index 0000000..0baf11c --- /dev/null +++ b/src/blowfish-sbox.h @@ -0,0 +1,320 @@ +/* -*-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 -------------------------------------------------*/ diff --git a/src/blowfish.c b/src/blowfish.c new file mode 100644 index 0000000..ddf7b26 --- /dev/null +++ b/src/blowfish.c @@ -0,0 +1,608 @@ +/* -*-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 + +/* --- 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 -------------------------------------------------*/ diff --git a/src/blowfish.h b/src/blowfish.h new file mode 100644 index 0000000..23519a6 --- /dev/null +++ b/src/blowfish.h @@ -0,0 +1,110 @@ +/* -*-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 diff --git a/src/check.c b/src/check.c new file mode 100644 index 0000000..9ed0b6f --- /dev/null +++ b/src/check.c @@ -0,0 +1,286 @@ +/* -*-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 +#include +#include +#include +#include +#include + +/* --- Unix headers --- */ + +#include +#include +#include + +#include + +#include + +#include +#include + +/* --- 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 -------------------------------------------------*/ diff --git a/src/check.h b/src/check.h new file mode 100644 index 0000000..24f5cb9 --- /dev/null +++ b/src/check.h @@ -0,0 +1,70 @@ +/* -*-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 + diff --git a/src/class.c b/src/class.c new file mode 100644 index 0000000..d2f5e4b --- /dev/null +++ b/src/class.c @@ -0,0 +1,325 @@ +/* -*-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 +#include +#include + +/* --- Unix headers --- */ + +#include +#include + +#include + +#include + +#include + +/* --- 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 -------------------------------------------------*/ diff --git a/src/class.h b/src/class.h new file mode 100644 index 0000000..cbe3929 --- /dev/null +++ b/src/class.h @@ -0,0 +1,175 @@ +/* -*-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 +#include +#include +#include + +#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 + diff --git a/src/crypt.c b/src/crypt.c new file mode 100644 index 0000000..86ed605 --- /dev/null +++ b/src/crypt.c @@ -0,0 +1,566 @@ +/* -*-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 +#include +#include +#include +#include +#include + +/* --- Unix headers --- */ + +#include +#include +#include +#include + +/* --- 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 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 -------------------------------------------------*/ diff --git a/src/crypt.h b/src/crypt.h new file mode 100644 index 0000000..b508b45 --- /dev/null +++ b/src/crypt.h @@ -0,0 +1,181 @@ +/* -*-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 + +#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 diff --git a/src/daemon.c b/src/daemon.c new file mode 100644 index 0000000..eb36b39 --- /dev/null +++ b/src/daemon.c @@ -0,0 +1,419 @@ +/* -*-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 +#include +#include +#include +#include +#include + +/* --- Unix headers --- */ + +#include +#include +#include + +#include + +#include + +#include +#include +#include + +/* --- 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 -------------------------------------------------*/ diff --git a/src/daemon.h b/src/daemon.h new file mode 100644 index 0000000..ed3c05c --- /dev/null +++ b/src/daemon.h @@ -0,0 +1,86 @@ +/* -*-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 diff --git a/src/dbutils.h b/src/dbutils.h new file mode 100644 index 0000000..70f24ee --- /dev/null +++ b/src/dbutils.h @@ -0,0 +1,69 @@ +/* -*-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 +# include +# 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 diff --git a/src/icrypt.c b/src/icrypt.c new file mode 100644 index 0000000..828ce1e --- /dev/null +++ b/src/icrypt.c @@ -0,0 +1,317 @@ +/* -*-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 +#include +#include + +/* --- 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 +#include +#include +#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 -------------------------------------------------*/ diff --git a/src/icrypt.h b/src/icrypt.h new file mode 100644 index 0000000..38deed9 --- /dev/null +++ b/src/icrypt.h @@ -0,0 +1,150 @@ +/* -*-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 + +#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 diff --git a/src/idea.c b/src/idea.c new file mode 100644 index 0000000..e1bacfe --- /dev/null +++ b/src/idea.c @@ -0,0 +1,501 @@ +/* -*-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 + +#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 -------------------------------------------------*/ diff --git a/src/idea.h b/src/idea.h new file mode 100644 index 0000000..79ca601 --- /dev/null +++ b/src/idea.h @@ -0,0 +1,126 @@ +/* -*-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 diff --git a/src/keygen.c b/src/keygen.c new file mode 100644 index 0000000..17bf93c --- /dev/null +++ b/src/keygen.c @@ -0,0 +1,577 @@ +/* -*-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 +#include +#include +#include +#include +#include + +/* --- Unix headers --- */ + +#include +#include +#include + +#include +#include + +/* --- 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 -------------------------------------------------*/ diff --git a/src/lexer.h b/src/lexer.h new file mode 100644 index 0000000..603d382 --- /dev/null +++ b/src/lexer.h @@ -0,0 +1,83 @@ +/* -*-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 + +/*----- 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 diff --git a/src/lexer.l b/src/lexer.l new file mode 100644 index 0000000..597690a --- /dev/null +++ b/src/lexer.l @@ -0,0 +1,173 @@ +/* -*-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 +#include +#include + +#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 --- */ + +{ + "#".*\n lex_line++; + + /* --- Whitespace --- */ + + [ \t] /* munch */ + \n lex_line++; +} + + /* --- Keywords --- */ + +{ + 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 --- */ + +{ + [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. + */ + +{ + \\. { + 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 | + <> { + 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 -------------------------------------------------*/ diff --git a/src/md5.c b/src/md5.c new file mode 100644 index 0000000..80cadf2 --- /dev/null +++ b/src/md5.c @@ -0,0 +1,496 @@ +/* -*-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 +#include + +#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 -------------------------------------------------*/ diff --git a/src/md5.h b/src/md5.h new file mode 100644 index 0000000..a105630 --- /dev/null +++ b/src/md5.h @@ -0,0 +1,146 @@ +/* -*-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 + +#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 diff --git a/src/mdwopt.c b/src/mdwopt.c new file mode 100644 index 0000000..af30491 --- /dev/null +++ b/src/mdwopt.c @@ -0,0 +1,766 @@ +/* -*-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 +#include +#include +#include + +#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 + * `%%@.{"-"}%%', 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 -------------------------------------------------*/ diff --git a/src/mdwopt.h b/src/mdwopt.h new file mode 100644 index 0000000..38e89e4 --- /dev/null +++ b/src/mdwopt.h @@ -0,0 +1,421 @@ +/* -*-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 + * `%%@.{"-"}%%', 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 diff --git a/src/name.c b/src/name.c new file mode 100644 index 0000000..faa06ab --- /dev/null +++ b/src/name.c @@ -0,0 +1,248 @@ +/* -*-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 +#include +#include + +/* --- Unix headers --- */ + +#include +#include + +/* --- 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 diff --git a/src/name.h b/src/name.h new file mode 100644 index 0000000..8d94ed5 --- /dev/null +++ b/src/name.h @@ -0,0 +1,123 @@ +/* -*-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 diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 0000000..cd64a67 --- /dev/null +++ b/src/parser.h @@ -0,0 +1,79 @@ +/* -*-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 diff --git a/src/parser.y b/src/parser.y new file mode 100644 index 0000000..0a8462f --- /dev/null +++ b/src/parser.y @@ -0,0 +1,507 @@ +/* -*-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 +#include +#include + +/* --- Unix headers --- */ + +#include + +#include +#include + +/* --- 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 INT +%token WORD +%token STRING +%token ARROW + +%left ',' +%left '-' +%left '|' +%left '&' + +/* --- Rules --- */ + +%type user_class command_class host_class +%type user_class_opt command_class_opt host_class_opt +%type 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 -------------------------------------------------*/ diff --git a/src/rule.c b/src/rule.c new file mode 100644 index 0000000..000c290 --- /dev/null +++ b/src/rule.c @@ -0,0 +1,158 @@ +/* -*-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 +#include +#include + +/* --- 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 -------------------------------------------------*/ diff --git a/src/rule.h b/src/rule.h new file mode 100644 index 0000000..7e337f6 --- /dev/null +++ b/src/rule.h @@ -0,0 +1,115 @@ +/* -*-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 +#include +#include +#include + +#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 diff --git a/src/set.c b/src/set.c new file mode 100644 index 0000000..7c36bb7 --- /dev/null +++ b/src/set.c @@ -0,0 +1,161 @@ +/* -*-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 +#include +#include + +/* --- 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 -------------------------------------------------*/ diff --git a/src/set.h b/src/set.h new file mode 100644 index 0000000..62b15c5 --- /dev/null +++ b/src/set.h @@ -0,0 +1,114 @@ +/* -*-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 diff --git a/src/sym.c b/src/sym.c new file mode 100644 index 0000000..d882f5f --- /dev/null +++ b/src/sym.c @@ -0,0 +1,681 @@ +/* -*-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 +#include +#include + +/* --- 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 +#include + +#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 -------------------------------------------------*/ diff --git a/src/sym.h b/src/sym.h new file mode 100644 index 0000000..8f708e1 --- /dev/null +++ b/src/sym.h @@ -0,0 +1,190 @@ +/* -*-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 + +/*----- 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 diff --git a/src/tx.c b/src/tx.c new file mode 100644 index 0000000..b17456a --- /dev/null +++ b/src/tx.c @@ -0,0 +1,178 @@ +/* -*-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 +#include +#include +#include + +/* --- 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 -------------------------------------------------*/ diff --git a/src/tx.h b/src/tx.h new file mode 100644 index 0000000..4aaaaee --- /dev/null +++ b/src/tx.h @@ -0,0 +1,84 @@ +/* -*-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 + +/*----- 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 diff --git a/src/userdb.c b/src/userdb.c new file mode 100644 index 0000000..bcefc85 --- /dev/null +++ b/src/userdb.c @@ -0,0 +1,784 @@ +/* -*-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 +#include +#include +#include +#include + +/* --- Unix headers --- */ + +#include + +#include +#include +#include + +/* --- 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 -------------------------------------------------*/ diff --git a/src/userdb.h b/src/userdb.h new file mode 100644 index 0000000..91038d7 --- /dev/null +++ b/src/userdb.h @@ -0,0 +1,235 @@ +/* -*-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 +#include + +#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 diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..a46a310 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,199 @@ +/* -*-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 +#include +#include + +/* --- 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 -------------------------------------------------*/ diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..09ee6d2 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,176 @@ +/* -*-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 +#include + +/*----- 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