]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
merge to trunk rev 18146
authorKen Raeburn <raeburn@mit.edu>
Thu, 15 Jun 2006 21:23:59 +0000 (21:23 +0000)
committerKen Raeburn <raeburn@mit.edu>
Thu, 15 Jun 2006 21:23:59 +0000 (21:23 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/branches/ldap-integ@18147 dc483132-0cff-0310-8789-dd5450dbe970

111 files changed:
README
doc/admin.texinfo
doc/install.texinfo
doc/krb425.texinfo
doc/procedures.txt
doc/texinfo.tex
doc/user-guide.texinfo
src/appl/bsd/klogind.M
src/appl/bsd/kshd.M
src/appl/bsd/rlogin.M
src/appl/gssftp/ftpd/Makefile.in
src/appl/telnet/telnet/commands.c
src/appl/telnet/telnet/externs.h
src/appl/telnet/telnetd/defs.h
src/clients/kinit/kinit.M
src/clients/kinit/kinit.c
src/config-files/krb5.conf.M
src/config/shlib.conf
src/configure.in
src/include/k5-int.h
src/include/k5-platform.h
src/kdc/Makefile.in
src/kdc/fakeka.M [new file with mode: 0644]
src/kdc/kerberos_v4.c
src/krb5-config.in
src/krb524/Makefile.in
src/krb524/k524init.M [new file with mode: 0644]
src/lib/gssapi/LICENSE [new file with mode: 0644]
src/lib/gssapi/Makefile.in
src/lib/gssapi/generic/Makefile.in
src/lib/gssapi/generic/gssapi.hin
src/lib/gssapi/generic/gssapiP_generic.h
src/lib/gssapi/generic/gssapi_err_generic.et
src/lib/gssapi/generic/oid_ops.c [deleted file]
src/lib/gssapi/generic/util_oid.c [deleted file]
src/lib/gssapi/gss_libinit.c
src/lib/gssapi/krb5/Makefile.in
src/lib/gssapi/krb5/accept_sec_context.c
src/lib/gssapi/krb5/canon_name.c
src/lib/gssapi/krb5/copy_ccache.c
src/lib/gssapi/krb5/get_tkt_flags.c
src/lib/gssapi/krb5/gssapiP_krb5.h
src/lib/gssapi/krb5/gssapi_krb5.c
src/lib/gssapi/krb5/gssapi_krb5.hin
src/lib/gssapi/krb5/indicate_mechs.c
src/lib/gssapi/krb5/init_sec_context.c
src/lib/gssapi/krb5/krb5_gss_glue.c
src/lib/gssapi/krb5/lucid_context.c
src/lib/gssapi/krb5/rel_cred.c
src/lib/gssapi/krb5/rel_oid.c
src/lib/gssapi/krb5/set_allowable_enctypes.c
src/lib/gssapi/libgssapi_krb5.exports
src/lib/gssapi/mechglue/Makefile.in
src/lib/gssapi/mechglue/g_accept_sec_context.c
src/lib/gssapi/mechglue/g_acquire_cred.c
src/lib/gssapi/mechglue/g_canon_name.c [new file with mode: 0644]
src/lib/gssapi/mechglue/g_compare_name.c
src/lib/gssapi/mechglue/g_context_time.c
src/lib/gssapi/mechglue/g_delete_sec_context.c
src/lib/gssapi/mechglue/g_dsp_name.c
src/lib/gssapi/mechglue/g_dsp_status.c
src/lib/gssapi/mechglue/g_dup_name.c [new file with mode: 0644]
src/lib/gssapi/mechglue/g_exp_sec_context.c
src/lib/gssapi/mechglue/g_export_name.c [new file with mode: 0644]
src/lib/gssapi/mechglue/g_glue.c
src/lib/gssapi/mechglue/g_imp_name.c
src/lib/gssapi/mechglue/g_imp_sec_context.c
src/lib/gssapi/mechglue/g_indicate_mechs.c
src/lib/gssapi/mechglue/g_init_sec_context.c
src/lib/gssapi/mechglue/g_initialize.c
src/lib/gssapi/mechglue/g_inq_context.c
src/lib/gssapi/mechglue/g_inq_cred.c
src/lib/gssapi/mechglue/g_inq_names.c
src/lib/gssapi/mechglue/g_mechname.c
src/lib/gssapi/mechglue/g_oid_ops.c
src/lib/gssapi/mechglue/g_process_context.c
src/lib/gssapi/mechglue/g_rel_cred.c
src/lib/gssapi/mechglue/g_rel_name.c
src/lib/gssapi/mechglue/g_rel_oid_set.c
src/lib/gssapi/mechglue/g_seal.c
src/lib/gssapi/mechglue/g_sign.c
src/lib/gssapi/mechglue/g_store_cred.c [new file with mode: 0644]
src/lib/gssapi/mechglue/g_unseal.c
src/lib/gssapi/mechglue/g_userok.c [new file with mode: 0644]
src/lib/gssapi/mechglue/g_utils.c [new file with mode: 0644]
src/lib/gssapi/mechglue/g_verify.c
src/lib/gssapi/mechglue/gssd_pname_to_uid.c
src/lib/gssapi/mechglue/mglueP.h
src/lib/gssapi/mechglue/oid_ops.c
src/lib/gssapi/spnego/Makefile.in [new file with mode: 0644]
src/lib/gssapi/spnego/gssapiP_spnego.h [new file with mode: 0644]
src/lib/gssapi/spnego/spnego_mech.c [new file with mode: 0644]
src/lib/kadm5/clnt/client_rpc.c
src/lib/kadm5/clnt/clnt_privs.c
src/lib/krb4/g_in_tkt.c
src/lib/krb4/krb4int.h
src/lib/krb5/ccache/cc_file.c
src/lib/krb5/keytab/kt_file.c
src/lib/krb5/krb/init_ctx.c
src/lib/krb5/krb/kerrs.c
src/lib/krb5/os/kuserok.c
src/lib/krb5/rcache/rc_base.c
src/lib/krb5/rcache/rc_io.c
src/lib/rpc/svc_auth_gssapi.c
src/lib/rpc/unit-test/rpc_test.0/expire.exp
src/plugins/kdb/db2/libdb2/hash/hash.c
src/slave/kprop.c
src/slave/kprop.h
src/slave/kpropd.c
src/tests/dejagnu/krb-standalone/v4gssftp.exp
src/util/profile/prof_parse.c

diff --git a/README b/README
index 300df13d9a188a43811dd4d214d9cb8f7a85fe08..243af7d7e092f3ac6ffaf7f4c3dd27a64660d191 100644 (file)
--- a/README
+++ b/README
@@ -222,9 +222,76 @@ src/lib/crypto/aes has the following copyright:
  in respect of any properties, including, but not limited to, correctness 
  and fitness for purpose.
 
+--- The implementations of GSSAPI mechglue in GSSAPI-SPNEGO in
+    src/lib/gssapi, including the following files:
+
+lib/gssapi/generic/gssapi_err_generic.et
+lib/gssapi/mechglue/g_accept_sec_context.c
+lib/gssapi/mechglue/g_acquire_cred.c
+lib/gssapi/mechglue/g_canon_name.c
+lib/gssapi/mechglue/g_compare_name.c
+lib/gssapi/mechglue/g_context_time.c
+lib/gssapi/mechglue/g_delete_sec_context.c
+lib/gssapi/mechglue/g_dsp_name.c
+lib/gssapi/mechglue/g_dsp_status.c
+lib/gssapi/mechglue/g_dup_name.c
+lib/gssapi/mechglue/g_exp_sec_context.c
+lib/gssapi/mechglue/g_export_name.c
+lib/gssapi/mechglue/g_glue.c
+lib/gssapi/mechglue/g_imp_name.c
+lib/gssapi/mechglue/g_imp_sec_context.c
+lib/gssapi/mechglue/g_init_sec_context.c
+lib/gssapi/mechglue/g_initialize.c
+lib/gssapi/mechglue/g_inquire_context.c
+lib/gssapi/mechglue/g_inquire_cred.c
+lib/gssapi/mechglue/g_inquire_names.c
+lib/gssapi/mechglue/g_process_context.c
+lib/gssapi/mechglue/g_rel_buffer.c
+lib/gssapi/mechglue/g_rel_cred.c
+lib/gssapi/mechglue/g_rel_name.c
+lib/gssapi/mechglue/g_rel_oid_set.c
+lib/gssapi/mechglue/g_seal.c
+lib/gssapi/mechglue/g_sign.c
+lib/gssapi/mechglue/g_store_cred.c
+lib/gssapi/mechglue/g_unseal.c
+lib/gssapi/mechglue/g_userok.c
+lib/gssapi/mechglue/g_utils.c
+lib/gssapi/mechglue/g_verify.c
+lib/gssapi/mechglue/gssd_pname_to_uid.c
+lib/gssapi/mechglue/mglueP.h
+lib/gssapi/mechglue/oid_ops.c
+lib/gssapi/spnego/gssapiP_spnego.h
+lib/gssapi/spnego/spnego_mech.c
+
+are subject to the following license:
+
+Copyright (c) 2004 Sun Microsystems, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
 Acknowledgements
 ----------------
 
+Thanks to Sun Microsystems for donating their implementations of
+mechglue and SPNEGO.
+
 Thanks to the members of the Kerberos V5 development team at MIT, both
 past and present: Danilo Almeida, Jeffrey Altman, Richard Basch, Jay
 Berkenbilt, Mitch Berger, Andrew Boardman, Joe Calzaretta, John Carr,
index ec20a89d0195f9df6e9c75c172815ae93f9dd1af..4a4a301ceddf544af2edc59b033f66ea6220c055 100644 (file)
 @parskip 6pt plus 6pt
 @end iftex
 
+@dircategory Kerberos
+@direntry
+* krb5-admin: (krb5-admin).             Kerberos V5 Administrator's Guide
+@end direntry
+
 @include definitions.texinfo
 @set EDITION 1.0
 @set UPDATED June 16, 2000
@@ -2443,7 +2448,7 @@ across the realms, they would run the following commands on the KDCs in
 @b{Re-enter password for principal krbtgt/@value{PRIMARYREALM}@@@value{SECONDREALM}:}
 @b{kadmin:} add_princ -requires_preauth krbtgt/@value{SECONDREALM}@@@value{PRIMARYREALM}
 @b{Enter password for principal krbtgt/@value{SECONDREALM}@@@value{PRIMARYREALM}:}
-@b{Enter password for principal krbtgt/@value{SECONDREALM}@@@value{PRIMARYREALML}:}
+@b{Enter password for principal krbtgt/@value{SECONDREALM}@@@value{PRIMARYREALM}:}
 @b{kadmin:}
 @end group
 @end smallexample
index e04e0a45af9e9f27e96aa4df27de4c647eb932d9..ebd64ce9227715bbea56c6c63b592f7b6cbc5583 100644 (file)
 @parskip 6pt plus 6pt
 @end iftex
 
+@dircategory Kerberos
+@direntry
+* krb5-install: (krb5-install).         Kerberos V5 Installation Guide
+@end direntry
+
 @include definitions.texinfo
 @set EDITION 1.1
 
index 7a7a808620ca883be3e798d9606bcea129b98eca..fdeb033c190df8af6b289fa85bc4a99ab325e13c 100644 (file)
 @parskip 6pt plus 6pt
 @end iftex
 
+@dircategory Kerberos
+@direntry
+* krb425: (krb425).                     Upgrading to Kerberos V5 from V4
+@end direntry
+
 @include definitions.texinfo
 @set EDITION 1.0
 @set UPDATED May 22, 2003
index 3c31814c79e10a3a7a3662bffb7c8b355143fcc8..dcc21319a622edcf826b67f802f2edd57f004966 100644 (file)
@@ -16,15 +16,17 @@ an associated RT ticket.  This ticket should have its "target_version"
 keyword set to the release that the change is targeted at, and should
 have the "pullup" tag set.  This ticket should clearly document the
 changes that are to be pulled up to the release branch; the
-recommended way to do this is to ensure the the CVS commit operations
+recommended way to do this is to ensure the the subversion  commit operations
 corresponding to the changes have automatically updated the ticket --
 see the following section.  The release engineer will pull up the
 change to the release branch and set the "version_fixed" keyword on
 the ticket.
 
-USING CVS COMMITS TO CREATE/UPDATE RT TICKETS
+USING Subversion COMMITS TO CREATE/UPDATE RT TICKETS
 =============================================
 
+The following instructions were written for cvs but also work for Subversion.
+
 To: krbdev@mit.edu
 Subject: Important: handling commit interactions with bug database
 Message-Id: <20020917204852.4AEFE151FEF@industrial-algebra.mit.edu>
@@ -57,8 +59,8 @@ Return errno not retval when getpeername fails.
 
 By default when you update a ticket:
 
-* the ticket it assigned to you
-* The ticket it closed
+* the ticket is assigned to you
+* The ticket is closed
 
 If these defaults are not appropriate for your action you can override
 them; see below.
@@ -76,7 +78,7 @@ subject: Add AES support
 Add an implementation of AES to libk5crypto.
 
 In addition to closing the ticket and marking you as the owner of a
-ticket, creating a new ticket makes you the requestor of the ticket.
+ticket, creating a new ticket makes you the requester of the ticket.
 
 OTher Things to Change
 ======================
@@ -91,7 +93,7 @@ The following additional commands are supported:
 * Component: change component of ticket [krb5-libs etc]
 * Version_Reported: 
 * Target_Version:
-* Tags: [enhancement|nochange|noresource]
+* Tags: [enhancement|nochange|noresource|pullup]
 
 You could set version_fixed, but it is wrong to do so.
 
@@ -132,16 +134,16 @@ specified target version.  If we notice a lot of issues we don't have
 time to deal with, we will drop the target versions as the release
 approaches.
 
-Therer are several tags that can be set on a ticket as well.
+There are several tags that can be set on a ticket as well.
 
 The first tag is the enhancement tag; this roughly corresponds to the
 Gnats classification as change-request, distinguished from sw-bug.
 
 
 The next tag is the nochange tag.  This indicates that the ticket has
-been (or will be) closed bwith no code change and thus should not be
+been (or will be) closed with no code change and thus should not be
 processed by the next round of release scripts.  This is appropriate
-for tickets where the requestor is mistaken or where no bug/change
+for tickets where the requester is mistaken or where no bug/change
 exists.
 
 The final tag is the noresource tag; this indicates that MIT does not
index 9d4147edcaec75096ebd6a3f44d80b980ba3a3f3..dddd0140ff02986c27080ec82619bba27e24b6f8 100644 (file)
-%% TeX macros to handle texinfo files
-
-%   Copyright (C) 1985, 86, 88, 90, 91, 92, 93, 1994 Free Software Foundation, Inc.
-
-%This texinfo.tex file 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, or (at
-%your option) any later version.
-
-%This texinfo.tex file is distributed in the hope that it will be
-%useful, but WITHOUT ANY WARRANTY; without even the implied warranty
-%of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%General Public License for more details.
-
-%You should have received a copy of the GNU General Public License
-%along with this texinfo.tex file; see the file COPYING.  If not, write
-%to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
-%USA.
+% texinfo.tex -- TeX macros to handle Texinfo files.
+%
+% Load plain if necessary, i.e., if running under initex.
+\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi
+%
+\def\texinfoversion{2006-02-13.16}
+%
+% Copyright (C) 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995,
+% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free
+% Software Foundation, Inc.
+%
+% This texinfo.tex file 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, or (at
+% your option) any later version.
+%
+% This texinfo.tex file is distributed in the hope that it will be
+% useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+% of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+% General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with this texinfo.tex file; see the file COPYING.  If not, write
+% to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+% Boston, MA 02110-1301, USA.
+%
+% As a special exception, when this file is read by TeX when processing
+% a Texinfo source document, you may use the result without
+% restriction.  (This has been our intent since Texinfo was invented.)
+%
+% Please try the latest version of texinfo.tex before submitting bug
+% reports; you can get the latest version from:
+%   http://www.gnu.org/software/texinfo/ (the Texinfo home page), or
+%   ftp://tug.org/tex/texinfo.tex
+%     (and all CTAN mirrors, see http://www.ctan.org).
+% The texinfo.tex in any given distribution could well be out
+% of date, so if that's what you're using, please check.
+%
+% Send bug reports to bug-texinfo@gnu.org.  Please include including a
+% complete document in each bug report with which we can reproduce the
+% problem.  Patches are, of course, greatly appreciated.
+%
+% To process a Texinfo manual with TeX, it's most reliable to use the
+% texi2dvi shell script that comes with the distribution.  For a simple
+% manual foo.texi, however, you can get away with this:
+%   tex foo.texi
+%   texindex foo.??
+%   tex foo.texi
+%   tex foo.texi
+%   dvips foo.dvi -o  # or whatever; this makes foo.ps.
+% The extra TeX runs get the cross-reference information correct.
+% Sometimes one run after texindex suffices, and sometimes you need more
+% than two; texi2dvi does it as many times as necessary.
+%
+% It is possible to adapt texinfo.tex for other languages, to some
+% extent.  You can get the existing language-specific files from the
+% full Texinfo distribution.
+%
+% The GNU Texinfo home page is http://www.gnu.org/software/texinfo.
 
 
-%In other words, you are welcome to use, share and improve this program.
-%You are forbidden to forbid anyone else to use, share and improve
-%what you give them.   Help stamp out software-hoarding!
+\message{Loading texinfo [version \texinfoversion]:}
 
-% This automatically updates the version number based on RCS.
-\def\deftexinfoversion$#1: #2 ${\def\texinfoversion{#2}}
-\deftexinfoversion$Revision$
-\message{Loading texinfo package [Version \texinfoversion]:}
+% If in a .fmt file, print the version number
+% and turn on active characters that we couldn't do earlier because
+% they might have appeared in the input file name.
+\everyjob{\message{[Texinfo version \texinfoversion]}%
+  \catcode`+=\active \catcode`\_=\active}
 
-% Print the version number if in a .fmt file.
-\everyjob{\message{[Texinfo version \texinfoversion]}\message{}}
+\message{Basics,}
+\chardef\other=12
 
-% Save some parts of plain tex whose names we will redefine.
+% We never want plain's \outer definition of \+ in Texinfo.
+% For @tex, we can use \tabalign.
+\let\+ = \relax
 
-\let\ptextilde=\~
-\let\ptexlbrace=\{
-\let\ptexrbrace=\}
-\let\ptexdots=\dots
-\let\ptexdot=\.
-\let\ptexstar=\*
-\let\ptexend=\end
-\let\ptexbullet=\bullet
+% Save some plain tex macros whose names we will redefine.
 \let\ptexb=\b
+\let\ptexbullet=\bullet
 \let\ptexc=\c
+\let\ptexcomma=\,
+\let\ptexdot=\.
+\let\ptexdots=\dots
+\let\ptexend=\end
+\let\ptexequiv=\equiv
+\let\ptexexclam=\!
+\let\ptexfootnote=\footnote
+\let\ptexgtr=>
+\let\ptexhat=^
 \let\ptexi=\i
+\let\ptexindent=\indent
+\let\ptexinsert=\insert
+\let\ptexlbrace=\{
+\let\ptexless=<
+\let\ptexnewwrite\newwrite
+\let\ptexnoindent=\noindent
+\let\ptexplus=+
+\let\ptexrbrace=\}
+\let\ptexslash=\/
+\let\ptexstar=\*
 \let\ptext=\t
-\let\ptexl=\l
-\let\ptexL=\L
-
-% Be sure we're in horizontal mode when doing a tie, since we make space
-% equivalent to this in @example-like environments. Otherwise, a space
-% at the beginning of a line will start with \penalty -- and
-% since \penalty is valid in vertical mode, we'd end up putting the
-% penalty on the vertical list instead of in the new paragraph.
-{\catcode`@ = 11
- \gdef\tie{\leavevmode\penalty\@M\ }
-}
-\let\~ = \tie                  % And make it available as @~.
-
-\message{Basics,}
-\chardef\other=12
 
 % If this character appears in an error message or help string, it
 % starts a new line in the output.
 \newlinechar = `^^J
 
-% Set up fixed words for English.
-\ifx\putwordChapter\undefined{\gdef\putwordChapter{Chapter}}\fi%
-\def\putwordInfo{Info}%
-\ifx\putwordSee\undefined{\gdef\putwordSee{See}}\fi%
-\ifx\putwordsee\undefined{\gdef\putwordsee{see}}\fi%
-\ifx\putwordfile\undefined{\gdef\putwordfile{file}}\fi%
-\ifx\putwordpage\undefined{\gdef\putwordpage{page}}\fi%
-\ifx\putwordsection\undefined{\gdef\putwordsection{section}}\fi%
-\ifx\putwordSection\undefined{\gdef\putwordSection{Section}}\fi%
-\ifx\putwordTableofContents\undefined{\gdef\putwordTableofContents{Table of Contents}}\fi%
-\ifx\putwordShortContents\undefined{\gdef\putwordShortContents{Short Contents}}\fi%
-\ifx\putwordAppendix\undefined{\gdef\putwordAppendix{Appendix}}\fi%
+% Use TeX 3.0's \inputlineno to get the line number, for better error
+% messages, but if we're using an old version of TeX, don't do anything.
+%
+\ifx\inputlineno\thisisundefined
+  \let\linenumber = \empty % Pre-3.0.
+\else
+  \def\linenumber{l.\the\inputlineno:\space}
+\fi
+
+% Set up fixed words for English if not already set.
+\ifx\putwordAppendix\undefined  \gdef\putwordAppendix{Appendix}\fi
+\ifx\putwordChapter\undefined   \gdef\putwordChapter{Chapter}\fi
+\ifx\putwordfile\undefined      \gdef\putwordfile{file}\fi
+\ifx\putwordin\undefined        \gdef\putwordin{in}\fi
+\ifx\putwordIndexIsEmpty\undefined     \gdef\putwordIndexIsEmpty{(Index is empty)}\fi
+\ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi
+\ifx\putwordInfo\undefined      \gdef\putwordInfo{Info}\fi
+\ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi
+\ifx\putwordMethodon\undefined  \gdef\putwordMethodon{Method on}\fi
+\ifx\putwordNoTitle\undefined   \gdef\putwordNoTitle{No Title}\fi
+\ifx\putwordof\undefined        \gdef\putwordof{of}\fi
+\ifx\putwordon\undefined        \gdef\putwordon{on}\fi
+\ifx\putwordpage\undefined      \gdef\putwordpage{page}\fi
+\ifx\putwordsection\undefined   \gdef\putwordsection{section}\fi
+\ifx\putwordSection\undefined   \gdef\putwordSection{Section}\fi
+\ifx\putwordsee\undefined       \gdef\putwordsee{see}\fi
+\ifx\putwordSee\undefined       \gdef\putwordSee{See}\fi
+\ifx\putwordShortTOC\undefined  \gdef\putwordShortTOC{Short Contents}\fi
+\ifx\putwordTOC\undefined       \gdef\putwordTOC{Table of Contents}\fi
+%
+\ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi
+\ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi
+\ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi
+\ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi
+\ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi
+\ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi
+\ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi
+\ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi
+\ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi
+\ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi
+\ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi
+\ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi
+%
+\ifx\putwordDefmac\undefined    \gdef\putwordDefmac{Macro}\fi
+\ifx\putwordDefspec\undefined   \gdef\putwordDefspec{Special Form}\fi
+\ifx\putwordDefvar\undefined    \gdef\putwordDefvar{Variable}\fi
+\ifx\putwordDefopt\undefined    \gdef\putwordDefopt{User Option}\fi
+\ifx\putwordDeffunc\undefined   \gdef\putwordDeffunc{Function}\fi
+
+% Since the category of space is not known, we have to be careful.
+\chardef\spacecat = 10
+\def\spaceisspace{\catcode`\ =\spacecat}
 
 % Ignore a token.
 %
 \def\gobble#1{}
 
-\hyphenation{ap-pen-dix}
-\hyphenation{mini-buf-fer mini-buf-fers}
-\hyphenation{eshell}
+% The following is used inside several \edef's.
+\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname}
+
+% Hyphenation fixes.
+\hyphenation{
+  Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script
+  ap-pen-dix bit-map bit-maps
+  data-base data-bases eshell fall-ing half-way long-est man-u-script
+  man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm
+  par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces
+  spell-ing spell-ings
+  stand-alone strong-est time-stamp time-stamps which-ever white-space
+  wide-spread wrap-around
+}
 
 % Margin to add to right of even pages, to left of odd pages.
-\newdimen \bindingoffset  \bindingoffset=0pt
-\newdimen \normaloffset   \normaloffset=\hoffset
+\newdimen\bindingoffset
+\newdimen\normaloffset
 \newdimen\pagewidth \newdimen\pageheight
-\pagewidth=\hsize \pageheight=\vsize
+
+% For a final copy, take out the rectangles
+% that mark overfull boxes (in case you have decided
+% that the text looks ok even though it passes the margin).
+%
+\def\finalout{\overfullrule=0pt}
+
+% @| inserts a changebar to the left of the current line.  It should
+% surround any changed text.  This approach does *not* work if the
+% change spans more than two lines of output.  To handle that, we would
+% have adopt a much more difficult approach (putting marks into the main
+% vertical list for the beginning and end of each change).
+%
+\def\|{%
+  % \vadjust can only be used in horizontal mode.
+  \leavevmode
+  %
+  % Append this vertical mode material after the current line in the output.
+  \vadjust{%
+    % We want to insert a rule with the height and depth of the current
+    % leading; that is exactly what \strutbox is supposed to record.
+    \vskip-\baselineskip
+    %
+    % \vadjust-items are inserted at the left edge of the type.  So
+    % the \llap here moves out into the left-hand margin.
+    \llap{%
+      %
+      % For a thicker or thinner bar, change the `1pt'.
+      \vrule height\baselineskip width1pt
+      %
+      % This is the space between the bar and the text.
+      \hskip 12pt
+    }%
+  }%
+}
 
 % Sometimes it is convenient to have everything in the transcript file
 % and nothing on the terminal.  We don't just call \tracingall here,
-% since that produces some useless output on the terminal.
+% since that produces some useless output on the terminal.  We also make
+% some effort to order the tracing commands to reduce output in the log
+% file; cf. trace.sty in LaTeX.
 %
 \def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}%
-\def\loggingall{\tracingcommands2 \tracingstats2
-   \tracingpages1 \tracingoutput1 \tracinglostchars1
-   \tracingmacros2 \tracingparagraphs1 \tracingrestores1
-   \showboxbreadth\maxdimen\showboxdepth\maxdimen
+\def\loggingall{%
+  \tracingstats2
+  \tracingpages1
+  \tracinglostchars2  % 2 gives us more in etex
+  \tracingparagraphs1
+  \tracingoutput1
+  \tracingmacros2
+  \tracingrestores1
+  \showboxbreadth\maxdimen \showboxdepth\maxdimen
+  \ifx\eTeXversion\undefined\else % etex gives us more logging
+    \tracingscantokens1
+    \tracingifs1
+    \tracinggroups1
+    \tracingnesting2
+    \tracingassigns1
+  \fi
+  \tracingcommands3  % 3 gives us more in etex
+  \errorcontextlines16
 }%
 
-%---------------------Begin change-----------------------
+% add check for \lastpenalty to plain's definitions.  If the last thing
+% we did was a \nobreak, we don't want to insert more space.
+%
+\def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount
+  \removelastskip\penalty-50\smallskip\fi\fi}
+\def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount
+  \removelastskip\penalty-100\medskip\fi\fi}
+\def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount
+  \removelastskip\penalty-200\bigskip\fi\fi}
+
+% For @cropmarks command.
+% Do @cropmarks to get crop marks.
 %
-%%%% For @cropmarks command.
-% Dimensions to add cropmarks at corners Added by P. A. MacKay, 12 Nov. 1986
+\newif\ifcropmarks
+\let\cropmarks = \cropmarkstrue
 %
-\newdimen\cornerlong \newdimen\cornerthick
-\newdimen \topandbottommargin
-\newdimen \outerhsize \newdimen \outervsize
-\cornerlong=1pc\cornerthick=.3pt       % These set size of cropmarks
-\outerhsize=7in
-%\outervsize=9.5in
-% Alternative @smallbook page size is 9.25in
-\outervsize=9.25in
-\topandbottommargin=.75in
+% Dimensions to add cropmarks at corners.
+% Added by P. A. MacKay, 12 Nov. 1986
 %
-%---------------------End change-----------------------
+\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines
+\newdimen\cornerlong  \cornerlong=1pc
+\newdimen\cornerthick \cornerthick=.3pt
+\newdimen\topandbottommargin \topandbottommargin=.75in
+
+% Main output routine.
+\chardef\PAGE = 255
+\output = {\onepageout{\pagecontents\PAGE}}
+
+\newbox\headlinebox
+\newbox\footlinebox
 
 % \onepageout takes a vbox as an argument.  Note that \pagecontents
-% does insertions itself, but you have to call it yourself.
-\chardef\PAGE=255  \output={\onepageout{\pagecontents\PAGE}}
-\def\onepageout#1{\hoffset=\normaloffset
-\ifodd\pageno  \advance\hoffset by \bindingoffset
-\else \advance\hoffset by -\bindingoffset\fi
-{\escapechar=`\\\relax % makes sure backslash is used in output files.
-\shipout\vbox{{\let\hsize=\pagewidth \makeheadline} \pagebody{#1}%
-{\let\hsize=\pagewidth \makefootline}}}%
-\advancepageno \ifnum\outputpenalty>-20000 \else\dosupereject\fi}
-
-%%%% For @cropmarks command %%%%
-
-% Here is a modification of the main output routine for Near East Publications
-% This provides right-angle cropmarks at all four corners.
-% The contents of the page are centerlined into the cropmarks,
-% and any desired binding offset is added as an \hskip on either
-% site of the centerlined box.  (P. A. MacKay, 12 November, 1986)
-%
-\def\croppageout#1{\hoffset=0pt % make sure this doesn't mess things up
-{\escapechar=`\\\relax % makes sure backslash is used in output files.
-                \shipout
-                \vbox to \outervsize{\hsize=\outerhsize
-                 \vbox{\line{\ewtop\hfill\ewtop}}
-                 \nointerlineskip
-                 \line{\vbox{\moveleft\cornerthick\nstop}
-                       \hfill
-                       \vbox{\moveright\cornerthick\nstop}}
-                 \vskip \topandbottommargin
-                 \centerline{\ifodd\pageno\hskip\bindingoffset\fi
-                       \vbox{
-                       {\let\hsize=\pagewidth \makeheadline}
-                       \pagebody{#1}
-                       {\let\hsize=\pagewidth \makefootline}}
-                       \ifodd\pageno\else\hskip\bindingoffset\fi}
-                \vskip \topandbottommargin plus1fill minus1fill
-                 \boxmaxdepth\cornerthick
-                 \line{\vbox{\moveleft\cornerthick\nsbot}
-                       \hfill
-                       \vbox{\moveright\cornerthick\nsbot}}
-                 \nointerlineskip
-                 \vbox{\line{\ewbot\hfill\ewbot}}
-       }}
+% does insertions, but you have to call it yourself.
+\def\onepageout#1{%
+  \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi
+  %
+  \ifodd\pageno  \advance\hoffset by \bindingoffset
+  \else \advance\hoffset by -\bindingoffset\fi
+  %
+  % Do this outside of the \shipout so @code etc. will be expanded in
+  % the headline as they should be, not taken literally (outputting ''code).
+  \setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}%
+  \setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}%
+  %
+  {%
+    % Have to do this stuff outside the \shipout because we want it to
+    % take effect in \write's, yet the group defined by the \vbox ends
+    % before the \shipout runs.
+    %
+    \indexdummies         % don't expand commands in the output.
+    \normalturnoffactive  % \ in index entries must not stay \, e.g., if
+               % the page break happens to be in the middle of an example.
+               % We don't want .vr (or whatever) entries like this:
+               % \entry{{\tt \indexbackslash }acronym}{32}{\code {\acronym}}
+               % "\acronym" won't work when it's read back in;
+               % it needs to be 
+               % {\code {{\tt \backslashcurfont }acronym}
+    \shipout\vbox{%
+      % Do this early so pdf references go to the beginning of the page.
+      \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi
+      %
+      \ifcropmarks \vbox to \outervsize\bgroup
+        \hsize = \outerhsize
+        \vskip-\topandbottommargin
+        \vtop to0pt{%
+          \line{\ewtop\hfil\ewtop}%
+          \nointerlineskip
+          \line{%
+            \vbox{\moveleft\cornerthick\nstop}%
+            \hfill
+            \vbox{\moveright\cornerthick\nstop}%
+          }%
+          \vss}%
+        \vskip\topandbottommargin
+        \line\bgroup
+          \hfil % center the page within the outer (page) hsize.
+          \ifodd\pageno\hskip\bindingoffset\fi
+          \vbox\bgroup
+      \fi
+      %
+      \unvbox\headlinebox
+      \pagebody{#1}%
+      \ifdim\ht\footlinebox > 0pt
+        % Only leave this space if the footline is nonempty.
+        % (We lessened \vsize for it in \oddfootingxxx.)
+        % The \baselineskip=24pt in plain's \makefootline has no effect.
+        \vskip 2\baselineskip
+        \unvbox\footlinebox
+      \fi
+      %
+      \ifcropmarks
+          \egroup % end of \vbox\bgroup
+        \hfil\egroup % end of (centering) \line\bgroup
+        \vskip\topandbottommargin plus1fill minus1fill
+        \boxmaxdepth = \cornerthick
+        \vbox to0pt{\vss
+          \line{%
+            \vbox{\moveleft\cornerthick\nsbot}%
+            \hfill
+            \vbox{\moveright\cornerthick\nsbot}%
+          }%
+          \nointerlineskip
+          \line{\ewbot\hfil\ewbot}%
+        }%
+      \egroup % \vbox from first cropmarks clause
+      \fi
+    }% end of \shipout\vbox
+  }% end of group with \indexdummies
   \advancepageno
-  \ifnum\outputpenalty>-20000 \else\dosupereject\fi}
-%
-% Do @cropmarks to get crop marks
-\def\cropmarks{\let\onepageout=\croppageout }
+  \ifnum\outputpenalty>-20000 \else\dosupereject\fi
+}
+
+\newinsert\margin \dimen\margin=\maxdimen
 
 \def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}}
 {\catcode`\@ =11
 \gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi
+% marginal hacks, juha@viisa.uucp (Juha Takala)
+\ifvoid\margin\else % marginal info is present
+  \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi
 \dimen@=\dp#1 \unvbox#1
 \ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi
 \ifr@ggedbottom \kern-\dimen@ \vfil \fi}
 }
 
-%
 % Here are the rules for the cropmarks.  Note that they are
 % offset so that the space between them is truly \outerhsize or \outervsize
 % (P. A. MacKay, 12 November, 1986)
 % the input line (except we remove a trailing comment).  #1 should be a
 % macro which expects an ordinary undelimited TeX argument.
 %
-\def\parsearg#1{%
-  \let\next = #1%
+\def\parsearg{\parseargusing{}}
+\def\parseargusing#1#2{%
+  \def\next{#2}%
   \begingroup
     \obeylines
-    \futurelet\temp\parseargx
-}
-
-% If the next token is an obeyed space (from an @example environment or
-% the like), remove it and recurse.  Otherwise, we're done.
-\def\parseargx{%
-  % \obeyedspace is defined far below, after the definition of \sepspaces.
-  \ifx\obeyedspace\temp
-    \expandafter\parseargdiscardspace
-  \else
-    \expandafter\parseargline
-  \fi
+    \spaceisspace
+    #1%
+    \parseargline\empty% Insert the \empty token, see \finishparsearg below.
 }
 
-% Remove a single space (as the delimiter token to the macro call).
-{\obeyspaces %
- \gdef\parseargdiscardspace {\futurelet\temp\parseargx}}
-
 {\obeylines %
   \gdef\parseargline#1^^M{%
     \endgroup % End of the group started in \parsearg.
-    %
-    % First remove any @c comment, then any @comment.
-    % Result of each macro is put in \toks0.
-    \argremovec #1\c\relax %
-    \expandafter\argremovecomment \the\toks0 \comment\relax %
-    %
-    % Call the caller's macro, saved as \next in \parsearg.
-    \expandafter\next\expandafter{\the\toks0}%
+    \argremovecomment #1\comment\ArgTerm%
   }%
 }
 
-% Since all \c{,omment} does is throw away the argument, we can let TeX
-% do that for us.  The \relax here is matched by the \relax in the call
-% in \parseargline; it could be more or less anything, its purpose is
-% just to delimit the argument to the \c.
-\def\argremovec#1\c#2\relax{\toks0 = {#1}}
-\def\argremovecomment#1\comment#2\relax{\toks0 = {#1}}
+% First remove any @comment, then any @c comment.
+\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm}
+\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm}
 
-% \argremovec{,omment} might leave us with trailing spaces, though; e.g.,
+% Each occurence of `\^^M' or `<space>\^^M' is replaced by a single space.
+%
+% \argremovec might leave us with trailing space, e.g.,
 %    @end itemize  @c foo
-% will have two active spaces as part of the argument with the
-% `itemize'.  Here we remove all active spaces from #1, and assign the
-% result to \toks0.
-%
-% This loses if there are any *other* active characters besides spaces
-% in the argument -- _ ^ +, for example -- since they get expanded.
-% Fortunately, Texinfo does not define any such commands.  (If it ever
-% does, the catcode of the characters in questionwill have to be changed
-% here.)  But this means we cannot call \removeactivespaces as part of
-% \argremovec{,omment}, since @c uses \parsearg, and thus the argument
-% that \parsearg gets might well have any character at all in it.
-%
-\def\removeactivespaces#1{%
-  \begingroup
-    \ignoreactivespaces
-    \edef\temp{#1}%
-    \global\toks0 = \expandafter{\temp}%
-  \endgroup
+% This space token undergoes the same procedure and is eventually removed
+% by \finishparsearg.
+%
+\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M}
+\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M}
+\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{%
+  \def\temp{#3}%
+  \ifx\temp\empty
+    % We cannot use \next here, as it holds the macro to run;
+    % thus we reuse \temp.
+    \let\temp\finishparsearg
+  \else
+    \let\temp\argcheckspaces
+  \fi
+  % Put the space token in:
+  \temp#1 #3\ArgTerm
 }
 
-% Change the active space to expand to nothing.
+% If a _delimited_ argument is enclosed in braces, they get stripped; so
+% to get _exactly_ the rest of the line, we had to prevent such situation.
+% We prepended an \empty token at the very beginning and we expand it now,
+% just before passing the control to \next.
+% (Similarily, we have to think about #3 of \argcheckspacesY above: it is
+% either the null string, or it ends with \^^M---thus there is no danger
+% that a pair of braces would be stripped.
 %
-\begingroup
+% But first, we have to remove the trailing space token.
+%
+\def\finishparsearg#1 \ArgTerm{\expandafter\next\expandafter{#1}}
+
+% \parseargdef\foo{...}
+%      is roughly equivalent to
+% \def\foo{\parsearg\Xfoo}
+% \def\Xfoo#1{...}
+%
+% Actually, I use \csname\string\foo\endcsname, ie. \\foo, as it is my
+% favourite TeX trick.  --kasal, 16nov03
+
+\def\parseargdef#1{%
+  \expandafter \doparseargdef \csname\string#1\endcsname #1%
+}
+\def\doparseargdef#1#2{%
+  \def#2{\parsearg#1}%
+  \def#1##1%
+}
+
+% Several utility definitions with active space:
+{
   \obeyspaces
-  \gdef\ignoreactivespaces{\obeyspaces\let =\empty}
-\endgroup
+  \gdef\obeyedspace{ }
+
+  % Make each space character in the input produce a normal interword
+  % space in the output.  Don't allow a line break at this space, as this
+  % is used only in environments like @example, where each line of input
+  % should produce a line of output anyway.
+  %
+  \gdef\sepspaces{\obeyspaces\let =\tie}
+
+  % If an index command is used in an @example environment, any spaces
+  % therein should become regular spaces in the raw index file, not the
+  % expansion of \tie (\leavevmode \penalty \@M \ ).
+  \gdef\unsepspaces{\let =\space}
+}
 
 
 \def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next}
 
-%% These are used to keep @begin/@end levels from running away
-%% Call \inENV within environments (after a \begingroup)
-\newif\ifENV \ENVfalse \def\inENV{\ifENV\relax\else\ENVtrue\fi}
-\def\ENVcheck{%
-\ifENV\errmessage{Still within an environment.  Type Return to continue.}
-\endgroup\fi} % This is not perfect, but it should reduce lossage
+% Define the framework for environments in texinfo.tex.  It's used like this:
+%
+%   \envdef\foo{...}
+%   \def\Efoo{...}
+%
+% It's the responsibility of \envdef to insert \begingroup before the
+% actual body; @end closes the group after calling \Efoo.  \envdef also
+% defines \thisenv, so the current environment is known; @end checks
+% whether the environment name matches.  The \checkenv macro can also be
+% used to check whether the current environment is the one expected.
+%
+% Non-false conditionals (@iftex, @ifset) don't fit into this, so they
+% are not treated as enviroments; they don't open a group.  (The
+% implementation of @end takes care not to call \endgroup in this
+% special case.)
 
-% @begin foo  is the same as @foo, for now.
-\newhelp\EMsimple{Type <Return> to continue.}
 
-\outer\def\begin{\parsearg\beginxxx}
+% At runtime, environments start with this:
+\def\startenvironment#1{\begingroup\def\thisenv{#1}}
+% initialize
+\let\thisenv\empty
 
-\def\beginxxx #1{%
-\expandafter\ifx\csname #1\endcsname\relax
-{\errhelp=\EMsimple \errmessage{Undefined command @begin #1}}\else
-\csname #1\endcsname\fi}
+% ... but they get defined via ``\envdef\foo{...}'':
+\long\def\envdef#1#2{\def#1{\startenvironment#1#2}}
+\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}}
 
-% @end foo executes the definition of \Efoo.
-%
-\def\end{\parsearg\endxxx}
-\def\endxxx #1{%
-  \removeactivespaces{#1}%
-  \edef\endthing{\the\toks0}%
-  %
-  \expandafter\ifx\csname E\endthing\endcsname\relax
-    \expandafter\ifx\csname \endthing\endcsname\relax
-      % There's no \foo, i.e., no ``environment'' foo.
-      \errhelp = \EMsimple
-      \errmessage{Undefined command `@end \endthing'}%
-    \else
-      \unmatchedenderror\endthing
-    \fi
+% Check whether we're in the right environment:
+\def\checkenv#1{%
+  \def\temp{#1}%
+  \ifx\thisenv\temp
   \else
-    % Everything's ok; the right environment has been started.
-    \csname E\endthing\endcsname
+    \badenverr
   \fi
 }
 
-% There is an environment #1, but it hasn't been started.  Give an error.
-%
-\def\unmatchedenderror#1{%
+% Evironment mismatch, #1 expected:
+\def\badenverr{%
   \errhelp = \EMsimple
-  \errmessage{This `@end #1' doesn't have a matching `@#1'}%
+  \errmessage{This command can appear only \inenvironment\temp,
+    not \inenvironment\thisenv}%
+}
+\def\inenvironment#1{%
+  \ifx#1\empty
+    out of any environment%
+  \else
+    in environment \expandafter\string#1%
+  \fi
 }
 
-% Define the control sequence \E#1 to give an unmatched @end error.
+% @end foo executes the definition of \Efoo.
+% But first, it executes a specialized version of \checkenv
 %
-\def\defineunmatchedend#1{%
-  \expandafter\def\csname E#1\endcsname{\unmatchedenderror{#1}}%
+\parseargdef\end{%
+  \if 1\csname iscond.#1\endcsname
+  \else
+    % The general wording of \badenverr may not be ideal, but... --kasal, 06nov03
+    \expandafter\checkenv\csname#1\endcsname
+    \csname E#1\endcsname
+    \endgroup
+  \fi
 }
 
+\newhelp\EMsimple{Press RETURN to continue.}
 
-% Single-spacing is done by various environments (specifically, in
-% \nonfillstart and \quotations).
-\newskip\singlespaceskip \singlespaceskip = 12.5pt
-\def\singlespace{%
-  % Why was this kern here?  It messes up equalizing space above and below
-  % environments.  --karl, 6may93
-  %{\advance \baselineskip by -\singlespaceskip
-  %\kern \baselineskip}%
-  \setleading \singlespaceskip
-}
 
 %% Simple single-character @ commands
 
 % @@ prints an @
 % Kludge this until the fonts are right (grr).
-\def\@{{\tt \char '100}}
+\def\@{{\tt\char64}}
 
 % This is turned off because it was never documented
 % and you can use @w{...} around a quote to suppress ligatures.
 %\def\'{{'}}
 
 % Used to generate quoted braces.
-
-\def\mylbrace {{\tt \char '173}}
-\def\myrbrace {{\tt \char '175}}
+\def\mylbrace {{\tt\char123}}
+\def\myrbrace {{\tt\char125}}
 \let\{=\mylbrace
 \let\}=\myrbrace
+\begingroup
+  % Definitions to produce \{ and \} commands for indices,
+  % and @{ and @} for the aux/toc files.
+  \catcode`\{ = \other \catcode`\} = \other
+  \catcode`\[ = 1 \catcode`\] = 2
+  \catcode`\! = 0 \catcode`\\ = \other
+  !gdef!lbracecmd[\{]%
+  !gdef!rbracecmd[\}]%
+  !gdef!lbraceatcmd[@{]%
+  !gdef!rbraceatcmd[@}]%
+!endgroup
+
+% @comma{} to avoid , parsing problems.
+\let\comma = ,
+
+% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent
+% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H.
+\let\, = \c
+\let\dotaccent = \.
+\def\ringaccent#1{{\accent23 #1}}
+\let\tieaccent = \t
+\let\ubaraccent = \b
+\let\udotaccent = \d
+
+% Other special characters: @questiondown @exclamdown @ordf @ordm
+% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss.
+\def\questiondown{?`}
+\def\exclamdown{!`}
+\def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}}
+\def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}}
+
+% Dotless i and dotless j, used for accents.
+\def\imacro{i}
+\def\jmacro{j}
+\def\dotless#1{%
+  \def\temp{#1}%
+  \ifx\temp\imacro \ptexi
+  \else\ifx\temp\jmacro \j
+  \else \errmessage{@dotless can be used only with i or j}%
+  \fi\fi
+}
+
+% The \TeX{} logo, as in plain, but resetting the spacing so that a
+% period following counts as ending a sentence.  (Idea found in latex.)
+%
+\edef\TeX{\TeX \spacefactor=1000 }
+
+% @LaTeX{} logo.  Not quite the same results as the definition in
+% latex.ltx, since we use a different font for the raised A; it's most
+% convenient for us to use an explicitly smaller font, rather than using
+% the \scriptstyle font (since we don't reset \scriptstyle and
+% \scriptscriptstyle).
+%
+\def\LaTeX{%
+  L\kern-.36em
+  {\setbox0=\hbox{T}%
+   \vbox to \ht0{\hbox{\selectfonts\lllsize A}\vss}}%
+  \kern-.15em
+  \TeX
+}
+
+% Be sure we're in horizontal mode when doing a tie, since we make space
+% equivalent to this in @example-like environments. Otherwise, a space
+% at the beginning of a line will start with \penalty -- and
+% since \penalty is valid in vertical mode, we'd end up putting the
+% penalty on the vertical list instead of in the new paragraph.
+{\catcode`@ = 11
+ % Avoid using \@M directly, because that causes trouble
+ % if the definition is written into an index file.
+ \global\let\tiepenalty = \@M
+ \gdef\tie{\leavevmode\penalty\tiepenalty\ }
+}
 
 % @: forces normal size whitespace following.
 \def\:{\spacefactor=1000 }
 % @* forces a line break.
 \def\*{\hfil\break\hbox{}\ignorespaces}
 
-% @. is an end-of-sentence period.
-\def\.{.\spacefactor=3000 }
+% @/ allows a line break.
+\let\/=\allowbreak
 
-% @enddots{} is an end-of-sentence ellipsis.
-\gdef\enddots{$\mathinner{\ldotp\ldotp\ldotp\ldotp}$\spacefactor=3000}
+% @. is an end-of-sentence period.
+\def\.{.\spacefactor=\endofsentencespacefactor\space}
 
 % @! is an end-of-sentence bang.
-\gdef\!{!\spacefactor=3000 }
+\def\!{!\spacefactor=\endofsentencespacefactor\space}
 
 % @? is an end-of-sentence query.
-\gdef\?{?\spacefactor=3000 }
+\def\?{?\spacefactor=\endofsentencespacefactor\space}
+
+% @frenchspacing on|off  says whether to put extra space after punctuation.
+% 
+\def\onword{on}
+\def\offword{off}
+%
+\parseargdef\frenchspacing{%
+  \def\temp{#1}%
+  \ifx\temp\onword \plainfrenchspacing
+  \else\ifx\temp\offword \plainnonfrenchspacing
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @frenchspacing option `\temp', must be on/off}%
+  \fi\fi
+}
 
 % @w prevents a word break.  Without the \leavevmode, @w at the
 % beginning of a paragraph, when TeX is still in vertical mode, would
 % therefore, no glue is inserted, and the space between the headline and
 % the text is small, which looks bad.
 %
-\def\group{\begingroup
-  \ifnum\catcode13=\active \else
+% Another complication is that the group might be very large.  This can
+% cause the glue on the previous page to be unduly stretched, because it
+% does not have much material.  In this case, it's better to add an
+% explicit \vfill so that the extra space is at the bottom.  The
+% threshold for doing this is if the group is more than \vfilllimit
+% percent of a page (\vfilllimit can be changed inside of @tex).
+%
+\newbox\groupbox
+\def\vfilllimit{0.7}
+%
+\envdef\group{%
+  \ifnum\catcode`\^^M=\active \else
     \errhelp = \groupinvalidhelp
     \errmessage{@group invalid in context where filling is enabled}%
   \fi
+  \startsavinginserts
   %
-  % The \vtop we start below produces a box with normal height and large
-  % depth; thus, TeX puts \baselineskip glue before it, and (when the
-  % next line of text is done) \lineskip glue after it.  (See p.82 of
-  % the TeXbook.)  Thus, space below is not quite equal to space
-  % above.  But it's pretty close.
-  \def\Egroup{%
-    \egroup           % End the \vtop.
-    \endgroup         % End the \group.
-  }%
-  %
-  \vtop\bgroup
-    % We have to put a strut on the last line in case the @group is in
-    % the midst of an example, rather than completely enclosing it.
-    % Otherwise, the interline space between the last line of the group
-    % and the first line afterwards is too small.  But we can't put the
-    % strut in \Egroup, since there it would be on a line by itself.
-    % Hence this just inserts a strut at the beginning of each line.
-    \everypar = {\strut}%
-    %
-    % Since we have a strut on every line, we don't need any of TeX's
-    % normal interline spacing.
-    \offinterlineskip
-    %
-    % OK, but now we have to do something about blank
-    % lines in the input in @example-like environments, which normally
-    % just turn into \lisppar, which will insert no space now that we've
-    % turned off the interline space.  Simplest is to make them be an
-    % empty paragraph.
-    \ifx\par\lisppar
-      \edef\par{\leavevmode \par}%
-      %
-      % Reset ^^M's definition to new definition of \par.
-      \obeylines
-    \fi
-    %
+  \setbox\groupbox = \vtop\bgroup
     % Do @comment since we are called inside an environment such as
     % @example, where each end-of-line in the input causes an
     % end-of-line in the output.  We don't want the end-of-line after
     \comment
 }
 %
+% The \vtop produces a box with normal height and large depth; thus, TeX puts
+% \baselineskip glue before it, and (when the next line of text is done)
+% \lineskip glue after it.  Thus, space below is not quite equal to space
+% above.  But it's pretty close.
+\def\Egroup{%
+    % To get correct interline space between the last line of the group
+    % and the first line afterwards, we have to propagate \prevdepth.
+    \endgraf % Not \par, as it may have been set to \lisppar.
+    \global\dimen1 = \prevdepth
+  \egroup           % End the \vtop.
+  % \dimen0 is the vertical size of the group's box.
+  \dimen0 = \ht\groupbox  \advance\dimen0 by \dp\groupbox
+  % \dimen2 is how much space is left on the page (more or less).
+  \dimen2 = \pageheight   \advance\dimen2 by -\pagetotal
+  % if the group doesn't fit on the current page, and it's a big big
+  % group, force a page break.
+  \ifdim \dimen0 > \dimen2
+    \ifdim \pagetotal < \vfilllimit\pageheight
+      \page
+    \fi
+  \fi
+  \box\groupbox
+  \prevdepth = \dimen1
+  \checkinserts
+}
+%
 % TeX puts in an \escapechar (i.e., `@') at the beginning of the help
 % message, so this ends up printing `@group can only ...'.
 %
@@ -443,60 +742,60 @@ where each line of input produces a line of output.}
 
 \newdimen\mil  \mil=0.001in
 
-\def\need{\parsearg\needx}
-
 % Old definition--didn't work.
-%\def\needx #1{\par %
+%\parseargdef\need{\par %
 %% This method tries to make TeX break the page naturally
 %% if the depth of the box does not fit.
 %{\baselineskip=0pt%
-%\vtop to #1\mil{\vfil}\kern -#1\mil\penalty 10000
+%\vtop to #1\mil{\vfil}\kern -#1\mil\nobreak
 %\prevdepth=-1000pt
 %}}
 
-\def\needx#1{%
-  % Go into vertical mode, so we don't make a big box in the middle of a
+\parseargdef\need{%
+  % Ensure vertical mode, so we don't make a big box in the middle of a
   % paragraph.
   \par
   %
-  % Don't add any leading before our big empty box, but allow a page
-  % break, since the best break might be right here.
-  \allowbreak
-  \nointerlineskip
-  \vtop to #1\mil{\vfil}%
-  %
-  % TeX does not even consider page breaks if a penalty added to the
-  % main vertical list is 10000 or more.  But in order to see if the
-  % empty box we just added fits on the page, we must make it consider
-  % page breaks.  On the other hand, we don't want to actually break the
-  % page after the empty box.  So we use a penalty of 9999.
-  %
-  % There is an extremely small chance that TeX will actually break the
-  % page at this \penalty, if there are no other feasible breakpoints in
-  % sight.  (If the user is using lots of big @group commands, which
-  % almost-but-not-quite fill up a page, TeX will have a hard time doing
-  % good page breaking, for example.)  However, I could not construct an
-  % example where a page broke at this \penalty; if it happens in a real
-  % document, then we can reconsider our strategy.
-  \penalty9999
-  %
-  % Back up by the size of the box, whether we did a page break or not.
-  \kern -#1\mil
-  %
-  % Do not allow a page break right after this kern.
-  \nobreak
+  % If the @need value is less than one line space, it's useless.
+  \dimen0 = #1\mil
+  \dimen2 = \ht\strutbox
+  \advance\dimen2 by \dp\strutbox
+  \ifdim\dimen0 > \dimen2
+    %
+    % Do a \strut just to make the height of this box be normal, so the
+    % normal leading is inserted relative to the preceding line.
+    % And a page break here is fine.
+    \vtop to #1\mil{\strut\vfil}%
+    %
+    % TeX does not even consider page breaks if a penalty added to the
+    % main vertical list is 10000 or more.  But in order to see if the
+    % empty box we just added fits on the page, we must make it consider
+    % page breaks.  On the other hand, we don't want to actually break the
+    % page after the empty box.  So we use a penalty of 9999.
+    %
+    % There is an extremely small chance that TeX will actually break the
+    % page at this \penalty, if there are no other feasible breakpoints in
+    % sight.  (If the user is using lots of big @group commands, which
+    % almost-but-not-quite fill up a page, TeX will have a hard time doing
+    % good page breaking, for example.)  However, I could not construct an
+    % example where a page broke at this \penalty; if it happens in a real
+    % document, then we can reconsider our strategy.
+    \penalty9999
+    %
+    % Back up by the size of the box, whether we did a page break or not.
+    \kern -#1\mil
+    %
+    % Do not allow a page break right after this kern.
+    \nobreak
+  \fi
 }
 
-% @br   forces paragraph break
+% @br   forces paragraph break (and is undocumented).
 
 \let\br = \par
 
-% @dots{}  output some dots
-
-\def\dots{$\ldots$}
-
-% @page    forces the start of a new page
-
+% @page forces the start of a new page.
+%
 \def\page{\par\vfill\supereject}
 
 % @exdent text....
@@ -507,563 +806,990 @@ where each line of input produces a line of output.}
 \newskip\exdentamount
 
 % This defn is used inside fill environments such as @defun.
-\def\exdent{\parsearg\exdentyyy}
-\def\exdentyyy #1{{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}}
+\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}
 
 % This defn is used inside nofill environments such as @example.
-\def\nofillexdent{\parsearg\nofillexdentyyy}
-\def\nofillexdentyyy #1{{\advance \leftskip by -\exdentamount
-\leftline{\hskip\leftskip{\rm#1}}}}
+\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount
+  \leftline{\hskip\leftskip{\rm#1}}}}
 
-%\hbox{{\rm#1}}\hfil\break}}
+% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current
+% paragraph.  For more general purposes, use the \margin insertion
+% class.  WHICH is `l' or `r'.
+%
+\newskip\inmarginspacing \inmarginspacing=1cm
+\def\strutdepth{\dp\strutbox}
+%
+\def\doinmargin#1#2{\strut\vadjust{%
+  \nobreak
+  \kern-\strutdepth
+  \vtop to \strutdepth{%
+    \baselineskip=\strutdepth
+    \vss
+    % if you have multiple lines of stuff to put here, you'll need to
+    % make the vbox yourself of the appropriate size.
+    \ifx#1l%
+      \llap{\ignorespaces #2\hskip\inmarginspacing}%
+    \else
+      \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}%
+    \fi
+    \null
+  }%
+}}
+\def\inleftmargin{\doinmargin l}
+\def\inrightmargin{\doinmargin r}
+%
+% @inmargin{TEXT [, RIGHT-TEXT]}
+% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right;
+% else use TEXT for both).
+%
+\def\inmargin#1{\parseinmargin #1,,\finish}
+\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing.
+  \setbox0 = \hbox{\ignorespaces #2}%
+  \ifdim\wd0 > 0pt
+    \def\lefttext{#1}%  have both texts
+    \def\righttext{#2}%
+  \else
+    \def\lefttext{#1}%  have only one text
+    \def\righttext{#1}%
+  \fi
+  %
+  \ifodd\pageno
+    \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin
+  \else
+    \def\temp{\inleftmargin\lefttext}%
+  \fi
+  \temp
+}
 
 % @include file    insert text of that file as input.
+%
+\def\include{\parseargusing\filenamecatcodes\includezzz}
+\def\includezzz#1{%
+  \pushthisfilestack
+  \def\thisfile{#1}%
+  {%
+    \makevalueexpandable
+    \def\temp{\input #1 }%
+    \expandafter
+  }\temp
+  \popthisfilestack
+}
+\def\filenamecatcodes{%
+  \catcode`\\=\other
+  \catcode`~=\other
+  \catcode`^=\other
+  \catcode`_=\other
+  \catcode`|=\other
+  \catcode`<=\other
+  \catcode`>=\other
+  \catcode`+=\other
+  \catcode`-=\other
+}
 
-\def\include{\parsearg\includezzz}
-%Use \input\thisfile to avoid blank after \input, which may be an active
-%char (in which case the blank would become the \input argument).
-%The grouping keeps the value of \thisfile correct even when @include
-%is nested.
-\def\includezzz #1{\begingroup
-\def\thisfile{#1}\input\thisfile
-\endgroup}
+\def\pushthisfilestack{%
+  \expandafter\pushthisfilestackX\popthisfilestack\StackTerm
+}
+\def\pushthisfilestackX{%
+  \expandafter\pushthisfilestackY\thisfile\StackTerm
+}
+\def\pushthisfilestackY #1\StackTerm #2\StackTerm {%
+  \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}%
+}
 
-\def\thisfile{}
+\def\popthisfilestack{\errthisfilestackempty}
+\def\errthisfilestackempty{\errmessage{Internal error:
+  the stack of filenames is empty.}}
 
-% @center line   outputs that line, centered
+\def\thisfile{}
 
-\def\center{\parsearg\centerzzz}
-\def\centerzzz #1{{\advance\hsize by -\leftskip
-\advance\hsize by -\rightskip
-\centerline{#1}}}
+% @center line
+% outputs that line, centered.
+%
+\parseargdef\center{%
+  \ifhmode
+    \let\next\centerH
+  \else
+    \let\next\centerV
+  \fi
+  \next{\hfil \ignorespaces#1\unskip \hfil}%
+}
+\def\centerH#1{%
+  {%
+    \hfil\break
+    \advance\hsize by -\leftskip
+    \advance\hsize by -\rightskip
+    \line{#1}%
+    \break
+  }%
+}
+\def\centerV#1{\line{\kern\leftskip #1\kern\rightskip}}
 
 % @sp n   outputs n lines of vertical space
 
-\def\sp{\parsearg\spxxx}
-\def\spxxx #1{\par \vskip #1\baselineskip}
+\parseargdef\sp{\vskip #1\baselineskip}
 
 % @comment ...line which is ignored...
 % @c is the same as @comment
 % @ignore ... @end ignore  is another way to write a comment
 
-\def\comment{\catcode 64=\other \catcode 123=\other \catcode 125=\other%
-\parsearg \commentxxx}
-
-\def\commentxxx #1{\catcode 64=0 \catcode 123=1 \catcode 125=2 }
+\def\comment{\begingroup \catcode`\^^M=\other%
+\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other%
+\commentxxx}
+{\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}}
 
 \let\c=\comment
 
-% Prevent errors for section commands.
-% Used in @ignore and in failing conditionals.
-\def\ignoresections{%
-\let\chapter=\relax
-\let\unnumbered=\relax
-\let\top=\relax
-\let\unnumberedsec=\relax
-\let\unnumberedsection=\relax
-\let\unnumberedsubsec=\relax
-\let\unnumberedsubsection=\relax
-\let\unnumberedsubsubsec=\relax
-\let\unnumberedsubsubsection=\relax
-\let\section=\relax
-\let\subsec=\relax
-\let\subsubsec=\relax
-\let\subsection=\relax
-\let\subsubsection=\relax
-\let\appendix=\relax
-\let\appendixsec=\relax
-\let\appendixsection=\relax
-\let\appendixsubsec=\relax
-\let\appendixsubsection=\relax
-\let\appendixsubsubsec=\relax
-\let\appendixsubsubsection=\relax
-\let\contents=\relax
-\let\smallbook=\relax
-\let\titlepage=\relax
-}
-
-% Used in nested conditionals, where we have to parse the Texinfo source
-% and so want to turn off most commands, in case they are used
-% incorrectly.
-%
-\def\ignoremorecommands{%
-  \let\defcv = \relax
-  \let\deffn = \relax
-  \let\deffnx = \relax
-  \let\defindex = \relax
-  \let\defivar = \relax
-  \let\defmac = \relax
-  \let\defmethod = \relax
-  \let\defop = \relax
-  \let\defopt = \relax
-  \let\defspec = \relax
-  \let\deftp = \relax
-  \let\deftypefn = \relax
-  \let\deftypefun = \relax
-  \let\deftypevar = \relax
-  \let\deftypevr = \relax
-  \let\defun = \relax
-  \let\defvar = \relax
-  \let\defvr = \relax
-  \let\ref = \relax
-  \let\xref = \relax
-  \let\printindex = \relax
-  \let\pxref = \relax
-  \let\settitle = \relax
-  \let\include = \relax
-  \let\lowersections = \relax
-  \let\down = \relax
-  \let\raisesections = \relax
-  \let\up = \relax
-  \let\set = \relax
-  \let\clear = \relax
-  \let\item = \relax
-  \let\message = \relax
-}
-
-% Ignore @ignore ... @end ignore.
-%
-\def\ignore{\doignore{ignore}}
-
-% Also ignore @ifinfo, @ifhtml, @html, @menu, and @direntry text.
+% @paragraphindent NCHARS
+% We'll use ems for NCHARS, close enough.
+% NCHARS can also be the word `asis' or `none'.
+% We cannot feasibly implement @paragraphindent asis, though.
 %
-\def\ifinfo{\doignore{ifinfo}}
-\def\ifhtml{\doignore{ifhtml}}
-\def\html{\doignore{html}}
-\def\menu{\doignore{menu}}
-\def\direntry{\doignore{direntry}}
-
-% Ignore text until a line `@end #1'.
+\def\asisword{asis} % no translation, these are keywords
+\def\noneword{none}
 %
-\def\doignore#1{\begingroup
-  % Don't complain about control sequences we have declared \outer.
-  \ignoresections
-  %
-  % Define a command to swallow text until we reach `@end #1'.
-  \long\def\doignoretext##1\end #1{\enddoignore}%
-  %
-  % Make sure that spaces turn into tokens that match what \doignoretext wants.
-  \catcode32 = 10
-  %
-  % And now expand that command.
-  \doignoretext
-}
-
-% What we do to finish off ignored text.
-%
-\def\enddoignore{\endgroup\ignorespaces}%
-
-\newif\ifwarnedobs\warnedobsfalse
-\def\obstexwarn{%
-  \ifwarnedobs\relax\else
-  % We need to warn folks that they may have trouble with TeX 3.0.
-  % This uses \immediate\write16 rather than \message to get newlines.
-    \immediate\write16{}
-    \immediate\write16{***WARNING*** for users of Unix TeX 3.0!}
-    \immediate\write16{This manual trips a bug in TeX version 3.0 (tex hangs).}
-    \immediate\write16{If you are running another version of TeX, relax.}
-    \immediate\write16{If you are running Unix TeX 3.0, kill this TeX process.}
-    \immediate\write16{  Then upgrade your TeX installation if you can.}
-    \immediate\write16{If you are stuck with version 3.0, run the}
-    \immediate\write16{  script ``tex3patch'' from the Texinfo distribution}
-    \immediate\write16{  to use a workaround.}
-    \immediate\write16{}
-    \warnedobstrue
+\parseargdef\paragraphindent{%
+  \def\temp{#1}%
+  \ifx\temp\asisword
+  \else
+    \ifx\temp\noneword
+      \defaultparindent = 0pt
+    \else
+      \defaultparindent = #1em
     \fi
+  \fi
+  \parindent = \defaultparindent
 }
 
-% **In TeX 3.0, setting text in \nullfont hangs tex.  For a
-% workaround (which requires the file ``dummy.tfm'' to be installed),
-% uncomment the following line:
-%%%%%\font\nullfont=dummy\let\obstexwarn=\relax
-
-% Ignore text, except that we keep track of conditional commands for
-% purposes of nesting, up to an `@end #1' command.
-%
-\def\nestedignore#1{%
-  \obstexwarn
-  % We must actually expand the ignored text to look for the @end
-  % command, so that nested ignore constructs work.  Thus, we put the
-  % text into a \vbox and then do nothing with the result.  To minimize
-  % the change of memory overflow, we follow the approach outlined on
-  % page 401 of the TeXbook: make the current font be a dummy font.
-  %
-  \setbox0 = \vbox\bgroup
-    % Don't complain about control sequences we have declared \outer.
-    \ignoresections
-    %
-    % Define `@end #1' to end the box, which will in turn undefine the
-    % @end command again.
-    \expandafter\def\csname E#1\endcsname{\egroup\ignorespaces}%
-    %
-    % We are going to be parsing Texinfo commands.  Most cause no
-    % trouble when they are used incorrectly, but some commands do
-    % complicated argument parsing or otherwise get confused, so we
-    % undefine them.
-    %
-    % We can't do anything about stray @-signs, unfortunately;
-    % they'll produce `undefined control sequence' errors.
-    \ignoremorecommands
-    %
-    % Set the current font to be \nullfont, a TeX primitive, and define
-    % all the font commands to also use \nullfont.  We don't use
-    % dummy.tfm, as suggested in the TeXbook, because not all sites
-    % might have that installed.  Therefore, math mode will still
-    % produce output, but that should be an extremely small amount of
-    % stuff compared to the main input.
-    %
-    \nullfont
-    \let\tenrm = \nullfont  \let\tenit = \nullfont  \let\tensl = \nullfont
-    \let\tenbf = \nullfont  \let\tentt = \nullfont  \let\smallcaps = \nullfont
-    \let\tensf = \nullfont
-    % Similarly for index fonts (mostly for their use in
-    % smallexample)
-    \let\indrm = \nullfont  \let\indit = \nullfont  \let\indsl = \nullfont
-    \let\indbf = \nullfont  \let\indtt = \nullfont  \let\indsc = \nullfont
-    \let\indsf = \nullfont
-    %
-    % Don't complain when characters are missing from the fonts.
-    \tracinglostchars = 0
-    %
-    % Don't bother to do space factor calculations.
-    \frenchspacing
-    %
-    % Don't report underfull hboxes.
-    \hbadness = 10000
-    %
-    % Do minimal line-breaking.
-    \pretolerance = 10000
-    %
-    % Do not execute instructions in @tex
-    \def\tex{\doignore{tex}}
-}
-
-% @set VAR sets the variable VAR to an empty value.
-% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE.
-%
-% Since we want to separate VAR from REST-OF-LINE (which might be
-% empty), we can't just use \parsearg; we have to insert a space of our
-% own to delimit the rest of the line, and then take it out again if we
-% didn't need it.
-%
-\def\set{\parsearg\setxxx}
-\def\setxxx#1{\setyyy#1 \endsetyyy}
-\def\setyyy#1 #2\endsetyyy{%
-  \def\temp{#2}%
-  \ifx\temp\empty \global\expandafter\let\csname SET#1\endcsname = \empty
-  \else \setzzz{#1}#2\endsetzzz % Remove the trailing space \setxxx inserted.
+% @exampleindent NCHARS
+% We'll use ems for NCHARS like @paragraphindent.
+% It seems @exampleindent asis isn't necessary, but
+% I preserve it to make it similar to @paragraphindent.
+\parseargdef\exampleindent{%
+  \def\temp{#1}%
+  \ifx\temp\asisword
+  \else
+    \ifx\temp\noneword
+      \lispnarrowing = 0pt
+    \else
+      \lispnarrowing = #1em
+    \fi
   \fi
 }
-\def\setzzz#1#2 \endsetzzz{\expandafter\xdef\csname SET#1\endcsname{#2}}
-
-% @clear VAR clears (i.e., unsets) the variable VAR.
-%
-\def\clear{\parsearg\clearxxx}
-\def\clearxxx#1{\global\expandafter\let\csname SET#1\endcsname=\relax}
 
-% @value{foo} gets the text saved in variable foo.
+% @firstparagraphindent WORD
+% If WORD is `none', then suppress indentation of the first paragraph
+% after a section heading.  If WORD is `insert', then do indent at such
+% paragraphs.
 %
-\def\value#1{\expandafter
-               \ifx\csname SET#1\endcsname\relax
-                       {\{No value for ``#1''\}}
-               \else \csname SET#1\endcsname \fi}
-
-% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined
-% with @set.
+% The paragraph indentation is suppressed or not by calling
+% \suppressfirstparagraphindent, which the sectioning commands do.
+% We switch the definition of this back and forth according to WORD.
+% By default, we suppress indentation.
 %
-\def\ifset{\parsearg\ifsetxxx}
-\def\ifsetxxx #1{%
-  \expandafter\ifx\csname SET#1\endcsname\relax
-    \expandafter\ifsetfail
-  \else
-    \expandafter\ifsetsucceed
-  \fi
-}
-\def\ifsetsucceed{\conditionalsucceed{ifset}}
-\def\ifsetfail{\nestedignore{ifset}}
-\defineunmatchedend{ifset}
-
-% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been
-% defined with @set, or has been undefined with @clear.
+\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent}
+\def\insertword{insert}
 %
-\def\ifclear{\parsearg\ifclearxxx}
-\def\ifclearxxx #1{%
-  \expandafter\ifx\csname SET#1\endcsname\relax
-    \expandafter\ifclearsucceed
+\parseargdef\firstparagraphindent{%
+  \def\temp{#1}%
+  \ifx\temp\noneword
+    \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent
+  \else\ifx\temp\insertword
+    \let\suppressfirstparagraphindent = \relax
   \else
-    \expandafter\ifclearfail
-  \fi
+    \errhelp = \EMsimple
+    \errmessage{Unknown @firstparagraphindent option `\temp'}%
+  \fi\fi
 }
-\def\ifclearsucceed{\conditionalsucceed{ifclear}}
-\def\ifclearfail{\nestedignore{ifclear}}
-\defineunmatchedend{ifclear}
 
-% @iftex always succeeds; we read the text following, through @end
-% iftex).  But `@end iftex' should be valid only after an @iftex.
+% Here is how we actually suppress indentation.  Redefine \everypar to
+% \kern backwards by \parindent, and then reset itself to empty.
 %
-\def\iftex{\conditionalsucceed{iftex}}
-\defineunmatchedend{iftex}
-
-% We can't just want to start a group at @iftex (for example) and end it
-% at @end iftex, since then @set commands inside the conditional have no
-% effect (they'd get reverted at the end of the group).  So we must
-% define \Eiftex to redefine itself to be its previous value.  (We can't
-% just define it to fail again with an ``unmatched end'' error, since
-% the @ifset might be nested.)
+% We also make \indent itself not actually do anything until the next
+% paragraph.
 %
-\def\conditionalsucceed#1{%
-  \edef\temp{%
-    % Remember the current value of \E#1.
-    \let\nece{prevE#1} = \nece{E#1}%
-    %
-    % At the `@end #1', redefine \E#1 to be its previous value.
-    \def\nece{E#1}{\let\nece{E#1} = \nece{prevE#1}}%
+\gdef\dosuppressfirstparagraphindent{%
+  \gdef\indent{%
+    \restorefirstparagraphindent
+    \indent
+  }%
+  \gdef\noindent{%
+    \restorefirstparagraphindent
+    \noindent
+  }%
+  \global\everypar = {%
+    \kern -\parindent
+    \restorefirstparagraphindent
   }%
-  \temp
 }
 
-% We need to expand lots of \csname's, but we don't want to expand the
-% control sequences after we've constructed them.
-%
-\def\nece#1{\expandafter\noexpand\csname#1\endcsname}
+\gdef\restorefirstparagraphindent{%
+  \global \let \indent = \ptexindent
+  \global \let \noindent = \ptexnoindent
+  \global \everypar = {}%
+}
+
 
 % @asis just yields its argument.  Used with @table, for example.
 %
 \def\asis#1{#1}
 
-% @math means output in math mode.
-% We don't use $'s directly in the definition of \math because control
-% sequences like \math are expanded when the toc file is written.  Then,
-% we read the toc file back, the $'s will be normal characters (as they
-% should be, according to the definition of Texinfo).  So we must use a
-% control sequence to switch into and out of math mode.
+% @math outputs its argument in math mode.
+%
+% One complication: _ usually means subscripts, but it could also mean
+% an actual _ character, as in @math{@var{some_variable} + 1}.  So make
+% _ active, and distinguish by seeing if the current family is \slfam,
+% which is what @var uses.
+{
+  \catcode`\_ = \active
+  \gdef\mathunderscore{%
+    \catcode`\_=\active
+    \def_{\ifnum\fam=\slfam \_\else\sb\fi}%
+  }
+}
+% Another complication: we want \\ (and @\) to output a \ character.
+% FYI, plain.tex uses \\ as a temporary control sequence (why?), but
+% this is not advertised and we don't care.  Texinfo does not
+% otherwise define @\.
 %
-% This isn't quite enough for @math to work properly in indices, but it
-% seems unlikely it will ever be needed there.
+% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\.
+\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi}
 %
-\let\implicitmath = $
-\def\math#1{\implicitmath #1\implicitmath}
+\def\math{%
+  \tex
+  \mathunderscore
+  \let\\ = \mathbackslash
+  \mathactive
+  $\finishmath
+}
+\def\finishmath#1{#1$\endgroup}  % Close the group opened by \tex.
 
-% @bullet and @minus need the same treatment as @math, just above.
-\def\bullet{\implicitmath\ptexbullet\implicitmath}
-\def\minus{\implicitmath-\implicitmath}
+% Some active characters (such as <) are spaced differently in math.
+% We have to reset their definitions in case the @math was an argument
+% to a command which sets the catcodes (such as @item or @section).
+%
+{
+  \catcode`^ = \active
+  \catcode`< = \active
+  \catcode`> = \active
+  \catcode`+ = \active
+  \gdef\mathactive{%
+    \let^ = \ptexhat
+    \let< = \ptexless
+    \let> = \ptexgtr
+    \let+ = \ptexplus
+  }
+}
 
-\def\node{\ENVcheck\parsearg\nodezzz}
-\def\nodezzz#1{\nodexxx [#1,]}
-\def\nodexxx[#1,#2]{\gdef\lastnode{#1}}
-\let\nwnode=\node
-\let\lastnode=\relax
+% @bullet and @minus need the same treatment as @math, just above.
+\def\bullet{$\ptexbullet$}
+\def\minus{$-$}
 
-\def\donoderef{\ifx\lastnode\relax\else
-\expandafter\expandafter\expandafter\setref{\lastnode}\fi
-\global\let\lastnode=\relax}
+% @dots{} outputs an ellipsis using the current font.
+% We do .5em per period so that it has the same spacing in a typewriter
+% font as three actual period characters.
+%
+\def\dots{%
+  \leavevmode
+  \hbox to 1.5em{%
+    \hskip 0pt plus 0.25fil
+    .\hfil.\hfil.%
+    \hskip 0pt plus 0.5fil
+  }%
+}
 
-\def\unnumbnoderef{\ifx\lastnode\relax\else
-\expandafter\expandafter\expandafter\unnumbsetref{\lastnode}\fi
-\global\let\lastnode=\relax}
+% @enddots{} is an end-of-sentence ellipsis.
+%
+\def\enddots{%
+  \dots
+  \spacefactor=\endofsentencespacefactor
+}
 
-\def\appendixnoderef{\ifx\lastnode\relax\else
-\expandafter\expandafter\expandafter\appendixsetref{\lastnode}\fi
-\global\let\lastnode=\relax}
+% @comma{} is so commas can be inserted into text without messing up
+% Texinfo's parsing.
+%
+\let\comma = ,
 
+% @refill is a no-op.
 \let\refill=\relax
 
+% If working on a large document in chapters, it is convenient to
+% be able to disable indexing, cross-referencing, and contents, for test runs.
+% This is done with @novalidate (before @setfilename).
+%
+\newif\iflinks \linkstrue % by default we want the aux files.
+\let\novalidate = \linksfalse
+
 % @setfilename is done at the beginning of every texinfo file.
 % So open here the files we need to have open while reading the input.
 % This makes it possible to make a .fmt file for texinfo.
 \def\setfilename{%
-   \readauxfile
-   \opencontents
-   \openindices
    \fixbackslash  % Turn off hack to swallow `\input texinfo'.
-   \global\let\setfilename=\comment % Ignore extra @setfilename cmds.
+   \iflinks
+     \tryauxfile
+     % Open the new aux file.  TeX will close it automatically at exit.
+     \immediate\openout\auxfile=\jobname.aux
+   \fi % \openindices needs to do some work in any case.
+   \openindices
+   \let\setfilename=\comment % Ignore extra @setfilename cmds.
+   %
+   % If texinfo.cnf is present on the system, read it.
+   % Useful for site-wide @afourpaper, etc.
+   \openin 1 texinfo.cnf
+   \ifeof 1 \else \input texinfo.cnf \fi
+   \closein 1
+   %
    \comment % Ignore the actual filename.
 }
 
+% Called from \setfilename.
+%
+\def\openindices{%
+  \newindex{cp}%
+  \newcodeindex{fn}%
+  \newcodeindex{vr}%
+  \newcodeindex{tp}%
+  \newcodeindex{ky}%
+  \newcodeindex{pg}%
+}
+
+% @bye.
 \outer\def\bye{\pagealignmacro\tracingstats=1\ptexend}
 
-\def\inforef #1{\inforefzzz #1,,,,**}
-\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}},
-  node \samp{\ignorespaces#1{}}}
+
+\message{pdf,}
+% adobe `portable' document format
+\newcount\tempnum
+\newcount\lnkcount
+\newtoks\filename
+\newcount\filenamelength
+\newcount\pgn
+\newtoks\toksA
+\newtoks\toksB
+\newtoks\toksC
+\newtoks\toksD
+\newbox\boxA
+\newcount\countA
+\newif\ifpdf
+\newif\ifpdfmakepagedest
+
+% when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1
+% can be set).  So we test for \relax and 0 as well as \undefined,
+% borrowed from ifpdf.sty.
+\ifx\pdfoutput\undefined
+\else
+  \ifx\pdfoutput\relax
+  \else
+    \ifcase\pdfoutput
+    \else
+      \pdftrue
+    \fi
+  \fi
+\fi
+
+% PDF uses PostScript string constants for the names of xref targets,
+% for display in the outlines, and in other places.  Thus, we have to
+% double any backslashes.  Otherwise, a name like "\node" will be
+% interpreted as a newline (\n), followed by o, d, e.  Not good.
+% http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html
+% (and related messages, the final outcome is that it is up to the TeX
+% user to double the backslashes and otherwise make the string valid, so
+% that's what we do).
+
+% double active backslashes.
+% 
+{\catcode`\@=0 \catcode`\\=\active
+ @gdef@activebackslashdouble{%
+   @catcode`@\=@active
+   @let\=@doublebackslash}
+}
+
+% To handle parens, we must adopt a different approach, since parens are
+% not active characters.  hyperref.dtx (which has the same problem as
+% us) handles it with this amazing macro to replace tokens.  I've
+% tinkered with it a little for texinfo, but it's definitely from there.
+% 
+% #1 is the tokens to replace.
+% #2 is the replacement.
+% #3 is the control sequence with the string.
+% 
+\def\HyPsdSubst#1#2#3{%
+  \def\HyPsdReplace##1#1##2\END{%
+    ##1%
+    \ifx\\##2\\%
+    \else
+      #2%
+      \HyReturnAfterFi{%
+        \HyPsdReplace##2\END
+      }%
+    \fi
+  }%
+  \xdef#3{\expandafter\HyPsdReplace#3#1\END}%
+}
+\long\def\HyReturnAfterFi#1\fi{\fi#1}
+
+% #1 is a control sequence in which to do the replacements.
+\def\backslashparens#1{%
+  \xdef#1{#1}% redefine it as its expansion; the definition is simply
+             % \lastnode when called from \setref -> \pdfmkdest.
+  \HyPsdSubst{(}{\realbackslash(}{#1}%
+  \HyPsdSubst{)}{\realbackslash)}{#1}%
+}
+
+\ifpdf
+  \input pdfcolor
+  \pdfcatalog{/PageMode /UseOutlines}%
+  \def\dopdfimage#1#2#3{%
+    \def\imagewidth{#2}%
+    \def\imageheight{#3}%
+    % without \immediate, pdftex seg faults when the same image is
+    % included twice.  (Version 3.14159-pre-1.0-unofficial-20010704.)
+    \ifnum\pdftexversion < 14
+      \immediate\pdfimage
+    \else
+      \immediate\pdfximage
+    \fi
+      \ifx\empty\imagewidth\else width \imagewidth \fi
+      \ifx\empty\imageheight\else height \imageheight \fi
+      \ifnum\pdftexversion<13
+         #1.pdf%
+       \else
+         {#1.pdf}%
+       \fi
+    \ifnum\pdftexversion < 14 \else
+      \pdfrefximage \pdflastximage
+    \fi}
+  \def\pdfmkdest#1{{%
+    % We have to set dummies so commands such as @code, and characters
+    % such as \, aren't expanded when present in a section title.
+    \atdummies
+    \activebackslashdouble
+    \def\pdfdestname{#1}%
+    \backslashparens\pdfdestname
+    \pdfdest name{\pdfdestname} xyz%
+  }}%
+  %
+  % used to mark target names; must be expandable.
+  \def\pdfmkpgn#1{#1}%
+  %
+  \let\linkcolor = \Blue  % was Cyan, but that seems light?
+  \def\endlink{\Black\pdfendlink}
+  % Adding outlines to PDF; macros for calculating structure of outlines
+  % come from Petr Olsak
+  \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0%
+    \else \csname#1\endcsname \fi}
+  \def\advancenumber#1{\tempnum=\expnumber{#1}\relax
+    \advance\tempnum by 1
+    \expandafter\xdef\csname#1\endcsname{\the\tempnum}}
+  %
+  % #1 is the section text, which is what will be displayed in the
+  % outline by the pdf viewer.  #2 is the pdf expression for the number
+  % of subentries (or empty, for subsubsections).  #3 is the node text,
+  % which might be empty if this toc entry had no corresponding node.
+  % #4 is the page number
+  %
+  \def\dopdfoutline#1#2#3#4{%
+    % Generate a link to the node text if that exists; else, use the
+    % page number.  We could generate a destination for the section
+    % text in the case where a section has no node, but it doesn't
+    % seem worth the trouble, since most documents are normally structured.
+    \def\pdfoutlinedest{#3}%
+    \ifx\pdfoutlinedest\empty
+      \def\pdfoutlinedest{#4}%
+    \else
+      % Doubled backslashes in the name.
+      {\activebackslashdouble \xdef\pdfoutlinedest{#3}%
+       \backslashparens\pdfoutlinedest}%
+    \fi
+    %
+    % Also double the backslashes in the display string.
+    {\activebackslashdouble \xdef\pdfoutlinetext{#1}%
+     \backslashparens\pdfoutlinetext}%
+    %
+    \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}%
+  }
+  %
+  \def\pdfmakeoutlines{%
+    \begingroup
+      % Thanh's hack / proper braces in bookmarks
+      \edef\mylbrace{\iftrue \string{\else}\fi}\let\{=\mylbrace
+      \edef\myrbrace{\iffalse{\else\string}\fi}\let\}=\myrbrace
+      %
+      % Read toc silently, to get counts of subentries for \pdfoutline.
+      \def\numchapentry##1##2##3##4{%
+       \def\thischapnum{##2}%
+       \def\thissecnum{0}%
+       \def\thissubsecnum{0}%
+      }%
+      \def\numsecentry##1##2##3##4{%
+       \advancenumber{chap\thischapnum}%
+       \def\thissecnum{##2}%
+       \def\thissubsecnum{0}%
+      }%
+      \def\numsubsecentry##1##2##3##4{%
+       \advancenumber{sec\thissecnum}%
+       \def\thissubsecnum{##2}%
+      }%
+      \def\numsubsubsecentry##1##2##3##4{%
+       \advancenumber{subsec\thissubsecnum}%
+      }%
+      \def\thischapnum{0}%
+      \def\thissecnum{0}%
+      \def\thissubsecnum{0}%
+      %
+      % use \def rather than \let here because we redefine \chapentry et
+      % al. a second time, below.
+      \def\appentry{\numchapentry}%
+      \def\appsecentry{\numsecentry}%
+      \def\appsubsecentry{\numsubsecentry}%
+      \def\appsubsubsecentry{\numsubsubsecentry}%
+      \def\unnchapentry{\numchapentry}%
+      \def\unnsecentry{\numsecentry}%
+      \def\unnsubsecentry{\numsubsecentry}%
+      \def\unnsubsubsecentry{\numsubsubsecentry}%
+      \readdatafile{toc}%
+      %
+      % Read toc second time, this time actually producing the outlines.
+      % The `-' means take the \expnumber as the absolute number of
+      % subentries, which we calculated on our first read of the .toc above.
+      %
+      % We use the node names as the destinations.
+      \def\numchapentry##1##2##3##4{%
+        \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}%
+      \def\numsecentry##1##2##3##4{%
+        \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}%
+      \def\numsubsecentry##1##2##3##4{%
+        \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}%
+      \def\numsubsubsecentry##1##2##3##4{% count is always zero
+        \dopdfoutline{##1}{}{##3}{##4}}%
+      %
+      % PDF outlines are displayed using system fonts, instead of
+      % document fonts.  Therefore we cannot use special characters,
+      % since the encoding is unknown.  For example, the eogonek from
+      % Latin 2 (0xea) gets translated to a | character.  Info from
+      % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100.
+      %
+      % xx to do this right, we have to translate 8-bit characters to
+      % their "best" equivalent, based on the @documentencoding.  Right
+      % now, I guess we'll just let the pdf reader have its way.
+      \indexnofonts
+      \setupdatafile
+      \catcode`\\=\active \otherbackslash
+      \input \jobname.toc
+    \endgroup
+  }
+  %
+  \def\skipspaces#1{\def\PP{#1}\def\D{|}%
+    \ifx\PP\D\let\nextsp\relax
+    \else\let\nextsp\skipspaces
+      \ifx\p\space\else\addtokens{\filename}{\PP}%
+        \advance\filenamelength by 1
+      \fi
+    \fi
+    \nextsp}
+  \def\getfilename#1{\filenamelength=0\expandafter\skipspaces#1|\relax}
+  \ifnum\pdftexversion < 14
+    \let \startlink \pdfannotlink
+  \else
+    \let \startlink \pdfstartlink
+  \fi
+  % make a live url in pdf output.
+  \def\pdfurl#1{%
+    \begingroup
+      % it seems we really need yet another set of dummies; have not
+      % tried to figure out what each command should do in the context
+      % of @url.  for now, just make @/ a no-op, that's the only one
+      % people have actually reported a problem with.
+      % 
+      \normalturnoffactive
+      \def\@{@}%
+      \let\/=\empty
+      \makevalueexpandable
+      \leavevmode\Red
+      \startlink attr{/Border [0 0 0]}%
+        user{/Subtype /Link /A << /S /URI /URI (#1) >>}%
+    \endgroup}
+  \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}}
+  \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks}
+  \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks}
+  \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}}
+  \def\maketoks{%
+    \expandafter\poptoks\the\toksA|ENDTOKS|\relax
+    \ifx\first0\adn0
+    \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3
+    \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6
+    \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9
+    \else
+      \ifnum0=\countA\else\makelink\fi
+      \ifx\first.\let\next=\done\else
+        \let\next=\maketoks
+        \addtokens{\toksB}{\the\toksD}
+        \ifx\first,\addtokens{\toksB}{\space}\fi
+      \fi
+    \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+    \next}
+  \def\makelink{\addtokens{\toksB}%
+    {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0}
+  \def\pdflink#1{%
+    \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}}
+    \linkcolor #1\endlink}
+  \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st}
+\else
+  \let\pdfmkdest = \gobble
+  \let\pdfurl = \gobble
+  \let\endlink = \relax
+  \let\linkcolor = \relax
+  \let\pdfmakeoutlines = \relax
+\fi  % \ifx\pdfoutput
+
 
 \message{fonts,}
 
-% Font-change commands.
+% Change the current font style to #1, remembering it in \curfontstyle.
+% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in
+% italics, not bold italics.
+%
+\def\setfontstyle#1{%
+  \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd.
+  \csname ten#1\endcsname  % change the current font
+}
+
+% Select #1 fonts with the current style.
+%
+\def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname}
 
-% Texinfo supports the sans serif font style, which plain TeX does not.
-% So we set up a \sf analogous to plain's \rm, etc.
+\def\rm{\fam=0 \setfontstyle{rm}}
+\def\it{\fam=\itfam \setfontstyle{it}}
+\def\sl{\fam=\slfam \setfontstyle{sl}}
+\def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf}
+\def\tt{\fam=\ttfam \setfontstyle{tt}}
+
+% Texinfo sort of supports the sans serif font style, which plain TeX does not.
+% So we set up a \sf.
 \newfam\sffam
-\def\sf{\fam=\sffam \tensf}
+\def\sf{\fam=\sffam \setfontstyle{sf}}
 \let\li = \sf % Sometimes we call it \li, not \sf.
 
-%% Try out Computer Modern fonts at \magstephalf
-\let\mainmagstep=\magstephalf
+% We don't need math for this font style.
+\def\ttsl{\setfontstyle{ttsl}}
 
-\ifx\bigger\relax
-\let\mainmagstep=\magstep1
-\font\textrm=cmr12
-\font\texttt=cmtt12
-\else
-\font\textrm=cmr10 scaled \mainmagstep
-\font\texttt=cmtt10 scaled \mainmagstep
+% Default leading.
+\newdimen\textleading  \textleading = 13.2pt
+
+% Set the baselineskip to #1, and the lineskip and strut size
+% correspondingly.  There is no deep meaning behind these magic numbers
+% used as factors; they just match (closely enough) what Knuth defined.
+%
+\def\lineskipfactor{.08333}
+\def\strutheightpercent{.70833}
+\def\strutdepthpercent {.29167}
+%
+\def\setleading#1{%
+  \normalbaselineskip = #1\relax
+  \normallineskip = \lineskipfactor\normalbaselineskip
+  \normalbaselines
+  \setbox\strutbox =\hbox{%
+    \vrule width0pt height\strutheightpercent\baselineskip
+                    depth \strutdepthpercent \baselineskip
+  }%
+}
+
+% Set the font macro #1 to the font named #2, adding on the
+% specified font prefix (normally `cm').
+% #3 is the font's design size, #4 is a scale factor
+\def\setfont#1#2#3#4{\font#1=\fontprefix#2#3 scaled #4}
+
+% Use cm as the default font prefix.
+% To specify the font prefix, you must define \fontprefix
+% before you read in texinfo.tex.
+\ifx\fontprefix\undefined
+\def\fontprefix{cm}
 \fi
-% Instead of cmb10, you many want to use cmbx10.
-% cmbx10 is a prettier font on its own, but cmb10
-% looks better when embedded in a line with cmr10.
-\font\textbf=cmb10 scaled \mainmagstep
-\font\textit=cmti10 scaled \mainmagstep
-\font\textsl=cmsl10 scaled \mainmagstep
-\font\textsf=cmss10 scaled \mainmagstep
-\font\textsc=cmcsc10 scaled \mainmagstep
+% Support font families that don't use the same naming scheme as CM.
+\def\rmshape{r}
+\def\rmbshape{bx}               %where the normal face is bold
+\def\bfshape{b}
+\def\bxshape{bx}
+\def\ttshape{tt}
+\def\ttbshape{tt}
+\def\ttslshape{sltt}
+\def\itshape{ti}
+\def\itbshape{bxti}
+\def\slshape{sl}
+\def\slbshape{bxsl}
+\def\sfshape{ss}
+\def\sfbshape{ss}
+\def\scshape{csc}
+\def\scbshape{csc}
+
+% Text fonts (11.2pt, magstep1).
+\def\textnominalsize{11pt}
+\edef\mainmagstep{\magstephalf}
+\setfont\textrm\rmshape{10}{\mainmagstep}
+\setfont\texttt\ttshape{10}{\mainmagstep}
+\setfont\textbf\bfshape{10}{\mainmagstep}
+\setfont\textit\itshape{10}{\mainmagstep}
+\setfont\textsl\slshape{10}{\mainmagstep}
+\setfont\textsf\sfshape{10}{\mainmagstep}
+\setfont\textsc\scshape{10}{\mainmagstep}
+\setfont\textttsl\ttslshape{10}{\mainmagstep}
 \font\texti=cmmi10 scaled \mainmagstep
 \font\textsy=cmsy10 scaled \mainmagstep
 
-% A few fonts for @defun, etc.
-\font\defbf=cmbx10 scaled \magstep1 %was 1314
-\font\deftt=cmtt10 scaled \magstep1
-\def\df{\let\tentt=\deftt \let\tenbf = \defbf \bf}
-
-% Fonts for indices and small examples.
-% We actually use the slanted font rather than the italic,
-% because texinfo normally uses the slanted fonts for that.
-% Do not make many font distinctions in general in the index, since they
-% aren't very useful.
-\font\ninett=cmtt9
-\font\indrm=cmr9
-\font\indit=cmsl9
-\let\indsl=\indit
-\let\indtt=\ninett
-\let\indsf=\indrm
-\let\indbf=\indrm
-\let\indsc=\indrm
-\font\indi=cmmi9
-\font\indsy=cmsy9
-
-% Fonts for headings
-\font\chaprm=cmbx12 scaled \magstep2
-\font\chapit=cmti12 scaled \magstep2
-\font\chapsl=cmsl12 scaled \magstep2
-\font\chaptt=cmtt12 scaled \magstep2
-\font\chapsf=cmss12 scaled \magstep2
+% A few fonts for @defun names and args.
+\setfont\defbf\bfshape{10}{\magstep1}
+\setfont\deftt\ttshape{10}{\magstep1}
+\setfont\defttsl\ttslshape{10}{\magstep1}
+\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf}
+
+% Fonts for indices, footnotes, small examples (9pt).
+\def\smallnominalsize{9pt}
+\setfont\smallrm\rmshape{9}{1000}
+\setfont\smalltt\ttshape{9}{1000}
+\setfont\smallbf\bfshape{10}{900}
+\setfont\smallit\itshape{9}{1000}
+\setfont\smallsl\slshape{9}{1000}
+\setfont\smallsf\sfshape{9}{1000}
+\setfont\smallsc\scshape{10}{900}
+\setfont\smallttsl\ttslshape{10}{900}
+\font\smalli=cmmi9
+\font\smallsy=cmsy9
+
+% Fonts for small examples (8pt).
+\def\smallernominalsize{8pt}
+\setfont\smallerrm\rmshape{8}{1000}
+\setfont\smallertt\ttshape{8}{1000}
+\setfont\smallerbf\bfshape{10}{800}
+\setfont\smallerit\itshape{8}{1000}
+\setfont\smallersl\slshape{8}{1000}
+\setfont\smallersf\sfshape{8}{1000}
+\setfont\smallersc\scshape{10}{800}
+\setfont\smallerttsl\ttslshape{10}{800}
+\font\smalleri=cmmi8
+\font\smallersy=cmsy8
+
+% Fonts for title page (20.4pt):
+\def\titlenominalsize{20pt}
+\setfont\titlerm\rmbshape{12}{\magstep3}
+\setfont\titleit\itbshape{10}{\magstep4}
+\setfont\titlesl\slbshape{10}{\magstep4}
+\setfont\titlett\ttbshape{12}{\magstep3}
+\setfont\titlettsl\ttslshape{10}{\magstep4}
+\setfont\titlesf\sfbshape{17}{\magstep1}
+\let\titlebf=\titlerm
+\setfont\titlesc\scbshape{10}{\magstep4}
+\font\titlei=cmmi12 scaled \magstep3
+\font\titlesy=cmsy10 scaled \magstep4
+\def\authorrm{\secrm}
+\def\authortt{\sectt}
+
+% Chapter (and unnumbered) fonts (17.28pt).
+\def\chapnominalsize{17pt}
+\setfont\chaprm\rmbshape{12}{\magstep2}
+\setfont\chapit\itbshape{10}{\magstep3}
+\setfont\chapsl\slbshape{10}{\magstep3}
+\setfont\chaptt\ttbshape{12}{\magstep2}
+\setfont\chapttsl\ttslshape{10}{\magstep3}
+\setfont\chapsf\sfbshape{17}{1000}
 \let\chapbf=\chaprm
-\font\chapsc=cmcsc10 scaled\magstep3
+\setfont\chapsc\scbshape{10}{\magstep3}
 \font\chapi=cmmi12 scaled \magstep2
 \font\chapsy=cmsy10 scaled \magstep3
 
-\font\secrm=cmbx12 scaled \magstep1
-\font\secit=cmti12 scaled \magstep1
-\font\secsl=cmsl12 scaled \magstep1
-\font\sectt=cmtt12 scaled \magstep1
-\font\secsf=cmss12 scaled \magstep1
-\font\secbf=cmbx12 scaled \magstep1
-\font\secsc=cmcsc10 scaled\magstep2
+% Section fonts (14.4pt).
+\def\secnominalsize{14pt}
+\setfont\secrm\rmbshape{12}{\magstep1}
+\setfont\secit\itbshape{10}{\magstep2}
+\setfont\secsl\slbshape{10}{\magstep2}
+\setfont\sectt\ttbshape{12}{\magstep1}
+\setfont\secttsl\ttslshape{10}{\magstep2}
+\setfont\secsf\sfbshape{12}{\magstep1}
+\let\secbf\secrm
+\setfont\secsc\scbshape{10}{\magstep2}
 \font\seci=cmmi12 scaled \magstep1
 \font\secsy=cmsy10 scaled \magstep2
 
-% \font\ssecrm=cmbx10 scaled \magstep1    % This size an font looked bad.
-% \font\ssecit=cmti10 scaled \magstep1    % The letters were too crowded.
-% \font\ssecsl=cmsl10 scaled \magstep1
-% \font\ssectt=cmtt10 scaled \magstep1
-% \font\ssecsf=cmss10 scaled \magstep1
-
-%\font\ssecrm=cmb10 scaled 1315        % Note the use of cmb rather than cmbx.
-%\font\ssecit=cmti10 scaled 1315       % Also, the size is a little larger than
-%\font\ssecsl=cmsl10 scaled 1315       % being scaled magstep1.
-%\font\ssectt=cmtt10 scaled 1315
-%\font\ssecsf=cmss10 scaled 1315
-
-%\let\ssecbf=\ssecrm
-
-\font\ssecrm=cmbx12 scaled \magstephalf
-\font\ssecit=cmti12 scaled \magstephalf
-\font\ssecsl=cmsl12 scaled \magstephalf
-\font\ssectt=cmtt12 scaled \magstephalf
-\font\ssecsf=cmss12 scaled \magstephalf
-\font\ssecbf=cmbx12 scaled \magstephalf
-\font\ssecsc=cmcsc10 scaled \magstep1
+% Subsection fonts (13.15pt).
+\def\ssecnominalsize{13pt}
+\setfont\ssecrm\rmbshape{12}{\magstephalf}
+\setfont\ssecit\itbshape{10}{1315}
+\setfont\ssecsl\slbshape{10}{1315}
+\setfont\ssectt\ttbshape{12}{\magstephalf}
+\setfont\ssecttsl\ttslshape{10}{1315}
+\setfont\ssecsf\sfbshape{12}{\magstephalf}
+\let\ssecbf\ssecrm
+\setfont\ssecsc\scbshape{10}{1315}
 \font\sseci=cmmi12 scaled \magstephalf
-\font\ssecsy=cmsy10 scaled \magstep1
-% The smallcaps and symbol fonts should actually be scaled \magstep1.5,
-% but that is not a standard magnification.
-
-% Fonts for title page:
-\font\titlerm = cmbx12 scaled \magstep3
-\let\authorrm = \secrm
+\font\ssecsy=cmsy10 scaled 1315
+
+% Reduced fonts for @acro in text (10pt).
+\def\reducednominalsize{10pt}
+\setfont\reducedrm\rmshape{10}{1000}
+\setfont\reducedtt\ttshape{10}{1000}
+\setfont\reducedbf\bfshape{10}{1000}
+\setfont\reducedit\itshape{10}{1000}
+\setfont\reducedsl\slshape{10}{1000}
+\setfont\reducedsf\sfshape{10}{1000}
+\setfont\reducedsc\scshape{10}{1000}
+\setfont\reducedttsl\ttslshape{10}{1000}
+\font\reducedi=cmmi10
+\font\reducedsy=cmsy10
 
 % In order for the font changes to affect most math symbols and letters,
 % we have to define the \textfont of the standard families.  Since
-% texinfo doesn't allow for producing subscripts and superscripts, we
-% don't bother to reset \scriptfont and \scriptscriptfont (which would
-% also require loading a lot more fonts).
+% texinfo doesn't allow for producing subscripts and superscripts except
+% in the main text, we don't bother to reset \scriptfont and
+% \scriptscriptfont (which would also require loading a lot more fonts).
 %
 \def\resetmathfonts{%
-  \textfont0 = \tenrm \textfont1 = \teni \textfont2 = \tensy
-  \textfont\itfam = \tenit \textfont\slfam = \tensl \textfont\bffam = \tenbf
-  \textfont\ttfam = \tentt \textfont\sffam = \tensf
+  \textfont0=\tenrm \textfont1=\teni \textfont2=\tensy
+  \textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf
+  \textfont\ttfam=\tentt \textfont\sffam=\tensf
 }
 
-
 % The font-changing commands redefine the meanings of \tenSTYLE, instead
-% of just \STYLE.  We do this so that font changes will continue to work
-% in math mode, where it is the current \fam that is relevant in most
-% cases, not the current.  Plain TeX does, for example,
-% \def\bf{\fam=\bffam \tenbf}  By redefining \tenbf, we obviate the need
-% to redefine \bf itself.
+% of just \STYLE.  We do this because \STYLE needs to also set the
+% current \fam for math mode.  Our \STYLE (e.g., \rm) commands hardwire
+% \tenSTYLE to set the current font.
+%
+% Each font-changing command also sets the names \lsize (one size lower)
+% and \lllsize (three sizes lower).  These relative commands are used in
+% the LaTeX logo and acronyms.
+%
+% This all needs generalizing, badly.
+%
 \def\textfonts{%
   \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl
   \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc
   \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy
-  \resetmathfonts}
+  \let\tenttsl=\textttsl
+  \def\curfontsize{text}%
+  \def\lsize{reduced}\def\lllsize{smaller}%
+  \resetmathfonts \setleading{\textleading}}
+\def\titlefonts{%
+  \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl
+  \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc
+  \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy
+  \let\tenttsl=\titlettsl
+  \def\curfontsize{title}%
+  \def\lsize{chap}\def\lllsize{subsec}%
+  \resetmathfonts \setleading{25pt}}
+\def\titlefont#1{{\titlefonts\rm #1}}
 \def\chapfonts{%
   \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl
   \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc
   \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy
-  \resetmathfonts}
+  \let\tenttsl=\chapttsl
+  \def\curfontsize{chap}%
+  \def\lsize{sec}\def\lllsize{text}%
+  \resetmathfonts \setleading{19pt}}
 \def\secfonts{%
   \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl
   \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc
   \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy
-  \resetmathfonts}
+  \let\tenttsl=\secttsl
+  \def\curfontsize{sec}%
+  \def\lsize{subsec}\def\lllsize{reduced}%
+  \resetmathfonts \setleading{16pt}}
 \def\subsecfonts{%
   \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl
   \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc
   \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy
-  \resetmathfonts}
-\def\indexfonts{%
-  \let\tenrm=\indrm \let\tenit=\indit \let\tensl=\indsl
-  \let\tenbf=\indbf \let\tentt=\indtt \let\smallcaps=\indsc
-  \let\tensf=\indsf \let\teni=\indi \let\tensy=\indsy
-  \resetmathfonts}
+  \let\tenttsl=\ssecttsl
+  \def\curfontsize{ssec}%
+  \def\lsize{text}\def\lllsize{small}%
+  \resetmathfonts \setleading{15pt}}
+\let\subsubsecfonts = \subsecfonts
+\def\reducedfonts{%
+  \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl
+  \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc
+  \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy
+  \let\tenttsl=\reducedttsl
+  \def\curfontsize{reduced}%
+  \def\lsize{small}\def\lllsize{smaller}%
+  \resetmathfonts \setleading{10.5pt}}
+\def\smallfonts{%
+  \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl
+  \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc
+  \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy
+  \let\tenttsl=\smallttsl
+  \def\curfontsize{small}%
+  \def\lsize{smaller}\def\lllsize{smaller}%
+  \resetmathfonts \setleading{10.5pt}}
+\def\smallerfonts{%
+  \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl
+  \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc
+  \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy
+  \let\tenttsl=\smallerttsl
+  \def\curfontsize{smaller}%
+  \def\lsize{smaller}\def\lllsize{smaller}%
+  \resetmathfonts \setleading{9.5pt}}
+
+% Set the fonts to use with the @small... environments.
+\let\smallexamplefonts = \smallfonts
+
+% About \smallexamplefonts.  If we use \smallfonts (9pt), @smallexample
+% can fit this many characters:
+%   8.5x11=86   smallbook=72  a4=90  a5=69
+% If we use \scriptfonts (8pt), then we can fit this many characters:
+%   8.5x11=90+  smallbook=80  a4=90+  a5=77
+% For me, subjectively, the few extra characters that fit aren't worth
+% the additional smallness of 8pt.  So I'm making the default 9pt.
+%
+% By the way, for comparison, here's what fits with @example (10pt):
+%   8.5x11=71  smallbook=60  a4=75  a5=58
+%
+% I wish the USA used A4 paper.
+% --karl, 24jan03.
+
 
 % Set up the default fonts, so we can use them for creating boxes.
 %
-\textfonts
+\textfonts \rm
+
+% Define these so they can be easily changed for other fonts.
+\def\angleleft{$\langle$}
+\def\angleright{$\rangle$}
 
 % Count depth in font-changes, for error checks
 \newcount\fontdepth \fontdepth=0
 
 % Fonts for short table of contents.
-\font\shortcontrm=cmr12
-\font\shortcontbf=cmbx12
-\font\shortcontsl=cmsl12
+\setfont\shortcontrm\rmshape{12}{1000}
+\setfont\shortcontbf\bfshape{10}{\magstep1}  % no cmb12
+\setfont\shortcontsl\slshape{12}{1000}
+\setfont\shortconttt\ttshape{12}{1000}
 
 %% Add scribe-like font environments, plus @l for inline lisp (usually sans
 %% serif) and @ii for TeX italic
 
 % \smartitalic{ARG} outputs arg in italics, followed by an italic correction
 % unless the following character is such as not to need one.
-\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else\/\fi\fi\fi}
-\def\smartitalic#1{{\sl #1}\futurelet\next\smartitalicx}
+\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else
+                    \ptexslash\fi\fi\fi}
+\def\smartslanted#1{{\ifusingtt\ttsl\sl #1}\futurelet\next\smartitalicx}
+\def\smartitalic#1{{\ifusingtt\ttsl\it #1}\futurelet\next\smartitalicx}
+
+% like \smartslanted except unconditionally uses \ttsl.
+% @var is set to this for defun arguments.
+\def\ttslanted#1{{\ttsl #1}\futurelet\next\smartitalicx}
+
+% like \smartslanted except unconditionally use \sl.  We never want
+% ttsl for book titles, do we?
+\def\cite#1{{\sl #1}\futurelet\next\smartitalicx}
 
 \let\i=\smartitalic
-\let\var=\smartitalic
-\let\dfn=\smartitalic
+\let\slanted=\smartslanted
+\let\var=\smartslanted
+\let\dfn=\smartslanted
 \let\emph=\smartitalic
-\let\cite=\smartitalic
 
+% @b, explicit bold.
 \def\b#1{{\bf #1}}
 \let\strong=\b
 
+% @sansserif, explicit sans.
+\def\sansserif#1{{\sf #1}}
+
 % We can't just use \exhyphenpenalty, because that only has effect at
 % the end of a paragraph.  Restore normal hyphenation at the end of the
 % group within which \nohyphenation is presumably called.
@@ -1071,17 +1797,51 @@ where each line of input produces a line of output.}
 \def\nohyphenation{\hyphenchar\font = -1  \aftergroup\restorehyphenation}
 \def\restorehyphenation{\hyphenchar\font = `- }
 
+% Set sfcode to normal for the chars that usually have another value.
+% Can't use plain's \frenchspacing because it uses the `\x notation, and
+% sometimes \x has an active definition that messes things up.
+%
+\chardef\colonChar = `\:
+\chardef\commaChar = `\,
+\chardef\dotChar   = `\.
+\chardef\exclamChar= `\!
+\chardef\questChar = `\?
+\chardef\semiChar  = `\;
+%
+\catcode`@=11
+  \def\plainfrenchspacing{%
+    \sfcode\dotChar  =\@m \sfcode\questChar=\@m \sfcode\exclamChar=\@m
+    \sfcode\colonChar=\@m \sfcode\semiChar =\@m \sfcode\commaChar =\@m
+    \def\endofsentencespacefactor{1000}% for @. and friends
+  }
+  \def\plainnonfrenchspacing{%
+    \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000
+    \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250
+    \def\endofsentencespacefactor{3000}% for @. and friends
+  }
+\catcode`@=\other
+\def\endofsentencespacefactor{3000}% default
+
 \def\t#1{%
-  {\tt \nohyphenation \rawbackslash \frenchspacing #1}%
+  {\tt \rawbackslash \plainfrenchspacing #1}%
   \null
 }
-\let\ttfont = \t
-%\def\samp #1{`{\tt \rawbackslash \frenchspacing #1}'\null}
-\def\samp #1{`\tclose{#1}'\null}
-\def\key #1{{\tt \nohyphenation \uppercase{#1}}\null}
+\def\samp#1{`\tclose{#1}'\null}
+\setfont\keyrm\rmshape{8}{1000}
+\font\keysy=cmsy9
+\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{%
+  \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{%
+    \vbox{\hrule\kern-0.4pt
+     \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}%
+    \kern-0.4pt\hrule}%
+  \kern-.06em\raise0.4pt\hbox{\angleright}}}}
+% The old definition, with no lozenge:
+%\def\key #1{{\ttsl \nohyphenation \uppercase{#1}}\null}
 \def\ctrl #1{{\tt \rawbackslash \hat}#1}
 
+% @file, @option are the same as @samp.
 \let\file=\samp
+\let\option=\samp
 
 % @code is a modification of @t,
 % which makes spaces the same size as normal in the surrounding text.
@@ -1100,62 +1860,280 @@ where each line of input produces a line of output.}
     \nohyphenation
     %
     \rawbackslash
-    \frenchspacing
+    \plainfrenchspacing
     #1%
   }%
   \null
 }
 
-% We *must* turn on hyphenation at `-' and `_' in \code.
-% Otherwise, it is too hard to avoid overful hboxes
+% We *must* turn on hyphenation at `-' and `_' in @code.
+% Otherwise, it is too hard to avoid overfull hboxes
 % in the Emacs manual, the Library manual, etc.
 
 % Unfortunately, TeX uses one parameter (\hyphenchar) to control
 % both hyphenation at - and hyphenation within words.
 % We must therefore turn them both off (\tclose does that)
-% and arrange explicitly to hyphenate an a dash.
+% and arrange explicitly to hyphenate at a dash.
 %  -- rms.
 {
-\catcode`\-=\active
-\catcode`\_=\active
-\global\def\code{\begingroup \catcode`\-=\active \let-\codedash \catcode`\_=\active \let_\codeunder \codex}
-% The following is used by \doprintindex to insure that long function names
-% wrap around.  It is necessary for - and _ to be active before the index is
-% read from the file, as \entry parses the arguments long before \code is
-% ever called.  -- mycroft
-\global\def\indexbreaks{\catcode`\-=\active \let-\realdash \catcode`\_=\active \let_\realunder}
+  \catcode`\-=\active
+  \catcode`\_=\active
+  %
+  \global\def\code{\begingroup
+    \catcode`\-=\active  \catcode`\_=\active
+    \ifallowcodebreaks
+     \let-\codedash
+     \let_\codeunder
+    \else
+     \let-\realdash
+     \let_\realunder
+    \fi
+    \codex
+  }
 }
+
 \def\realdash{-}
-\def\realunder{_}
 \def\codedash{-\discretionary{}{}{}}
-\def\codeunder{\normalunderscore\discretionary{}{}{}}
+\def\codeunder{%
+  % this is all so @math{@code{var_name}+1} can work.  In math mode, _
+  % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.)
+  % will therefore expand the active definition of _, which is us
+  % (inside @code that is), therefore an endless loop.
+  \ifusingtt{\ifmmode
+               \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_.
+             \else\normalunderscore \fi
+             \discretionary{}{}{}}%
+            {\_}%
+}
 \def\codex #1{\tclose{#1}\endgroup}
 
-%\let\exp=\tclose  %Was temporary
+% An additional complication: the above will allow breaks after, e.g.,
+% each of the four underscores in __typeof__.  This is undesirable in
+% some manuals, especially if they don't have long identifiers in
+% general.  @allowcodebreaks provides a way to control this.
+% 
+\newif\ifallowcodebreaks  \allowcodebreakstrue
+
+\def\keywordtrue{true}
+\def\keywordfalse{false}
+
+\parseargdef\allowcodebreaks{%
+  \def\txiarg{#1}%
+  \ifx\txiarg\keywordtrue
+    \allowcodebreakstrue
+  \else\ifx\txiarg\keywordfalse
+    \allowcodebreaksfalse
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @allowcodebreaks option `\txiarg'}%
+  \fi\fi
+}
 
 % @kbd is like @code, except that if the argument is just one @key command,
 % then @kbd has no effect.
 
+% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always),
+%   `example' (@kbd uses ttsl only inside of @example and friends),
+%   or `code' (@kbd uses normal tty font always).
+\parseargdef\kbdinputstyle{%
+  \def\txiarg{#1}%
+  \ifx\txiarg\worddistinct
+    \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}%
+  \else\ifx\txiarg\wordexample
+    \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}%
+  \else\ifx\txiarg\wordcode
+    \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}%
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @kbdinputstyle option `\txiarg'}%
+  \fi\fi\fi
+}
+\def\worddistinct{distinct}
+\def\wordexample{example}
+\def\wordcode{code}
+
+% Default is `distinct.'
+\kbdinputstyle distinct
+
 \def\xkey{\key}
 \def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}%
 \ifx\one\xkey\ifx\threex\three \key{#2}%
-\else\tclose{\look}\fi
-\else\tclose{\look}\fi}
+\else{\tclose{\kbdfont\look}}\fi
+\else{\tclose{\kbdfont\look}}\fi}
+
+% For @indicateurl, @env, @command quotes seem unnecessary, so use \code.
+\let\indicateurl=\code
+\let\env=\code
+\let\command=\code
+
+% @uref (abbreviation for `urlref') takes an optional (comma-separated)
+% second argument specifying the text to display and an optional third
+% arg as text to display instead of (rather than in addition to) the url
+% itself.  First (mandatory) arg is the url.  Perhaps eventually put in
+% a hypertex \special here.
+%
+\def\uref#1{\douref #1,,,\finish}
+\def\douref#1,#2,#3,#4\finish{\begingroup
+  \unsepspaces
+  \pdfurl{#1}%
+  \setbox0 = \hbox{\ignorespaces #3}%
+  \ifdim\wd0 > 0pt
+    \unhbox0 % third arg given, show only that
+  \else
+    \setbox0 = \hbox{\ignorespaces #2}%
+    \ifdim\wd0 > 0pt
+      \ifpdf
+        \unhbox0             % PDF: 2nd arg given, show only it
+      \else
+        \unhbox0\ (\code{#1})% DVI: 2nd arg given, show both it and url
+      \fi
+    \else
+      \code{#1}% only url given, so show it
+    \fi
+  \fi
+  \endlink
+\endgroup}
+
+% @url synonym for @uref, since that's how everyone uses it.
+%
+\let\url=\uref
+
+% rms does not like angle brackets --karl, 17may97.
+% So now @email is just like @uref, unless we are pdf.
+%
+%\def\email#1{\angleleft{\tt #1}\angleright}
+\ifpdf
+  \def\email#1{\doemail#1,,\finish}
+  \def\doemail#1,#2,#3\finish{\begingroup
+    \unsepspaces
+    \pdfurl{mailto:#1}%
+    \setbox0 = \hbox{\ignorespaces #2}%
+    \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi
+    \endlink
+  \endgroup}
+\else
+  \let\email=\uref
+\fi
+
+% Check if we are currently using a typewriter font.  Since all the
+% Computer Modern typewriter fonts have zero interword stretch (and
+% shrink), and it is reasonable to expect all typewriter fonts to have
+% this property, we can check that font parameter.
+%
+\def\ifmonospace{\ifdim\fontdimen3\font=0pt }
 
 % Typeset a dimension, e.g., `in' or `pt'.  The only reason for the
-% argument is to make the input look right: @dmn{pt} instead of
-% @dmn{}pt.
+% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt.
 %
 \def\dmn#1{\thinspace #1}
 
 \def\kbd#1{\def\look{#1}\expandafter\kbdfoo\look??\par}
 
-\def\l#1{{\li #1}\null}                %
+% @l was never documented to mean ``switch to the Lisp font'',
+% and it is not used as such in any manual I can find.  We need it for
+% Polish suppressed-l.  --karl, 22sep96.
+%\def\l#1{{\li #1}\null}
+
+% Explicit font changes: @r, @sc, undocumented @ii.
+\def\r#1{{\rm #1}}              % roman font
+\def\sc#1{{\smallcaps#1}}       % smallcaps font
+\def\ii#1{{\it #1}}             % italic font
+
+% @acronym for "FBI", "NATO", and the like.
+% We print this one point size smaller, since it's intended for
+% all-uppercase.
+% 
+\def\acronym#1{\doacronym #1,,\finish}
+\def\doacronym#1,#2,#3\finish{%
+  {\selectfonts\lsize #1}%
+  \def\temp{#2}%
+  \ifx\temp\empty \else
+    \space ({\unsepspaces \ignorespaces \temp \unskip})%
+  \fi
+}
+
+% @abbr for "Comput. J." and the like.
+% No font change, but don't do end-of-sentence spacing.
+% 
+\def\abbr#1{\doabbr #1,,\finish}
+\def\doabbr#1,#2,#3\finish{%
+  {\plainfrenchspacing #1}%
+  \def\temp{#2}%
+  \ifx\temp\empty \else
+    \space ({\unsepspaces \ignorespaces \temp \unskip})%
+  \fi
+}
+
+% @pounds{} is a sterling sign, which Knuth put in the CM italic font.
+%
+\def\pounds{{\it\$}}
+
+% @euro{} comes from a separate font, depending on the current style.
+% We use the free feym* fonts from the eurosym package by Henrik
+% Theiling, which support regular, slanted, bold and bold slanted (and
+% "outlined" (blackboard board, sort of) versions, which we don't need).
+% It is available from http://www.ctan.org/tex-archive/fonts/eurosym.
+% 
+% Although only regular is the truly official Euro symbol, we ignore
+% that.  The Euro is designed to be slightly taller than the regular
+% font height.
+% 
+% feymr - regular
+% feymo - slanted
+% feybr - bold
+% feybo - bold slanted
+% 
+% There is no good (free) typewriter version, to my knowledge.
+% A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide.
+% Hmm.
+% 
+% Also doesn't work in math.  Do we need to do math with euro symbols?
+% Hope not.
+% 
+% 
+\def\euro{{\eurofont e}}
+\def\eurofont{%
+  % We set the font at each command, rather than predefining it in
+  % \textfonts and the other font-switching commands, so that
+  % installations which never need the symbol don't have to have the
+  % font installed.
+  % 
+  % There is only one designed size (nominal 10pt), so we always scale
+  % that to the current nominal size.
+  % 
+  % By the way, simply using "at 1em" works for cmr10 and the like, but
+  % does not work for cmbx10 and other extended/shrunken fonts.
+  % 
+  \def\eurosize{\csname\curfontsize nominalsize\endcsname}%
+  %
+  \ifx\curfontstyle\bfstylename 
+    % bold:
+    \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize
+  \else 
+    % regular:
+    \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize
+  \fi
+  \thiseurofont
+}
+
+% @registeredsymbol - R in a circle.  The font for the R should really
+% be smaller yet, but lllsize is the best we can do for now.
+% Adapted from the plain.tex definition of \copyright.
+%
+\def\registeredsymbol{%
+  $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}%
+               \hfil\crcr\Orb}}%
+    }$%
+}
+
+% Laurent Siebenmann reports \Orb undefined with:
+%  Textures 1.7.7 (preloaded format=plain 93.10.14)  (68K)  16 APR 2004 02:38
+% so we'll define it if necessary.
+% 
+\ifx\Orb\undefined
+\def\Orb{\mathhexbox20D}
+\fi
 
-\def\r#1{{\rm #1}}             % roman font
-% Use of \lowercase was suggested.
-\def\sc#1{{\smallcaps#1}}      % smallcaps font
-\def\ii#1{{\it #1}}            % italic font
 
 \message{page headings,}
 
@@ -1163,87 +2141,124 @@ where each line of input produces a line of output.}
 \newskip\titlepagebottomglue \titlepagebottomglue = 2pc
 
 % First the title page.  Must do @settitle before @titlepage.
-\def\titlefont#1{{\titlerm #1}}
-
 \newif\ifseenauthor
 \newif\iffinishedtitlepage
 
-\def\shorttitlepage{\parsearg\shorttitlepagezzz}
-\def\shorttitlepagezzz #1{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}%
-       \endgroup\page\hbox{}\page}
+% Do an implicit @contents or @shortcontents after @end titlepage if the
+% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage.
+%
+\newif\ifsetcontentsaftertitlepage
+ \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue
+\newif\ifsetshortcontentsaftertitlepage
+ \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue
+
+\parseargdef\shorttitlepage{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}%
+        \endgroup\page\hbox{}\page}
 
-\def\titlepage{\begingroup \parindent=0pt \textfonts
-   \let\subtitlerm=\tenrm
-% I deinstalled the following change because \cmr12 is undefined.
-% This change was not in the ChangeLog anyway.  --rms.
-%   \let\subtitlerm=\cmr12
-   \def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}%
-   %
-   \def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines}%
-   %
-   % Leave some space at the very top of the page.
-   \vglue\titlepagetopglue
-   %
-   % Now you can print the title using @title.
-   \def\title{\parsearg\titlezzz}%
-   \def\titlezzz##1{\leftline{\titlefont{##1}}
-                   % print a rule at the page bottom also.
-                   \finishedtitlepagefalse
-                   \vskip4pt \hrule height 4pt width \hsize \vskip4pt}%
-   % No rule at page bottom unless we print one at the top with @title.
-   \finishedtitlepagetrue
-   %
-   % Now you can put text using @subtitle.
-   \def\subtitle{\parsearg\subtitlezzz}%
-   \def\subtitlezzz##1{{\subtitlefont \rightline{##1}}}%
-   %
-   % @author should come last, but may come many times.
-   \def\author{\parsearg\authorzzz}%
-   \def\authorzzz##1{\ifseenauthor\else\vskip 0pt plus 1filll\seenauthortrue\fi
-      {\authorfont \leftline{##1}}}%
-   %
-   % Most title ``pages'' are actually two pages long, with space
-   % at the top of the second.  We don't want the ragged left on the second.
-   \let\oldpage = \page
-   \def\page{%
+\envdef\titlepage{%
+  % Open one extra group, as we want to close it in the middle of \Etitlepage.
+  \begingroup
+    \parindent=0pt \textfonts
+    % Leave some space at the very top of the page.
+    \vglue\titlepagetopglue
+    % No rule at page bottom unless we print one at the top with @title.
+    \finishedtitlepagetrue
+    %
+    % Most title ``pages'' are actually two pages long, with space
+    % at the top of the second.  We don't want the ragged left on the second.
+    \let\oldpage = \page
+    \def\page{%
       \iffinishedtitlepage\else
         \finishtitlepage
       \fi
-      \oldpage
       \let\page = \oldpage
-      \hbox{}}%
-%   \def\page{\oldpage \hbox{}}
+      \page
+      \null
+    }%
 }
 
 \def\Etitlepage{%
-   \iffinishedtitlepage\else
-      \finishtitlepage
-   \fi
-   % It is important to do the page break before ending the group,
-   % because the headline and footline are only empty inside the group.
-   % If we use the new definition of \page, we always get a blank page
-   % after the title page, which we certainly don't want.
-   \oldpage
-   \endgroup
-   \HEADINGSon
-}
-
-\def\finishtitlepage{%
-   \vskip4pt \hrule height 2pt width \hsize
-   \vskip\titlepagebottomglue
-   \finishedtitlepagetrue
-}
-
+    \iffinishedtitlepage\else
+       \finishtitlepage
+    \fi
+    % It is important to do the page break before ending the group,
+    % because the headline and footline are only empty inside the group.
+    % If we use the new definition of \page, we always get a blank page
+    % after the title page, which we certainly don't want.
+    \oldpage
+  \endgroup
+  %
+  % Need this before the \...aftertitlepage checks so that if they are
+  % in effect the toc pages will come out with page numbers.
+  \HEADINGSon
+  %
+  % If they want short, they certainly want long too.
+  \ifsetshortcontentsaftertitlepage
+    \shortcontents
+    \contents
+    \global\let\shortcontents = \relax
+    \global\let\contents = \relax
+  \fi
+  %
+  \ifsetcontentsaftertitlepage
+    \contents
+    \global\let\contents = \relax
+    \global\let\shortcontents = \relax
+  \fi
+}
+
+\def\finishtitlepage{%
+  \vskip4pt \hrule height 2pt width \hsize
+  \vskip\titlepagebottomglue
+  \finishedtitlepagetrue
+}
+
+%%% Macros to be used within @titlepage:
+
+\let\subtitlerm=\tenrm
+\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}
+
+\def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines
+               \let\tt=\authortt}
+
+\parseargdef\title{%
+  \checkenv\titlepage
+  \leftline{\titlefonts\rm #1}
+  % print a rule at the page bottom also.
+  \finishedtitlepagefalse
+  \vskip4pt \hrule height 4pt width \hsize \vskip4pt
+}
+
+\parseargdef\subtitle{%
+  \checkenv\titlepage
+  {\subtitlefont \rightline{#1}}%
+}
+
+% @author should come last, but may come many times.
+% It can also be used inside @quotation.
+%
+\parseargdef\author{%
+  \def\temp{\quotation}%
+  \ifx\thisenv\temp
+    \def\quotationauthor{#1}% printed in \Equotation.
+  \else
+    \checkenv\titlepage
+    \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi
+    {\authorfont \leftline{#1}}%
+  \fi
+}
+
+
 %%% Set up page headings and footings.
 
 \let\thispage=\folio
 
-\newtoks \evenheadline    % Token sequence for heading line of even pages
-\newtoks \oddheadline     % Token sequence for heading line of odd pages
-\newtoks \evenfootline    % Token sequence for footing line of even pages
-\newtoks \oddfootline     % Token sequence for footing line of odd pages
+\newtoks\evenheadline    % headline on even pages
+\newtoks\oddheadline     % headline on odd pages
+\newtoks\evenfootline    % footline on even pages
+\newtoks\oddfootline     % footline on odd pages
 
-% Now make Tex use those variables
+% Now make TeX use those variables
 \headline={{\textfonts\rm \ifodd\pageno \the\oddheadline
                             \else \the\evenheadline \fi}}
 \footline={{\textfonts\rm \ifodd\pageno \the\oddfootline
@@ -1257,56 +2272,51 @@ where each line of input produces a line of output.}
 % @evenfooting @thisfile||
 % @oddfooting ||@thisfile
 
-\def\evenheading{\parsearg\evenheadingxxx}
-\def\oddheading{\parsearg\oddheadingxxx}
-\def\everyheading{\parsearg\everyheadingxxx}
-
-\def\evenfooting{\parsearg\evenfootingxxx}
-\def\oddfooting{\parsearg\oddfootingxxx}
-\def\everyfooting{\parsearg\everyfootingxxx}
 
-{\catcode`\@=0 %
-
-\gdef\evenheadingxxx #1{\evenheadingyyy #1@|@|@|@|\finish}
-\gdef\evenheadingyyy #1@|#2@|#3@|#4\finish{%
+\def\evenheading{\parsearg\evenheadingxxx}
+\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish}
+\def\evenheadingyyy #1\|#2\|#3\|#4\finish{%
 \global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
 
-\gdef\oddheadingxxx #1{\oddheadingyyy #1@|@|@|@|\finish}
-\gdef\oddheadingyyy #1@|#2@|#3@|#4\finish{%
+\def\oddheading{\parsearg\oddheadingxxx}
+\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish}
+\def\oddheadingyyy #1\|#2\|#3\|#4\finish{%
 \global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
 
-\gdef\everyheadingxxx #1{\everyheadingyyy #1@|@|@|@|\finish}
-\gdef\everyheadingyyy #1@|#2@|#3@|#4\finish{%
-\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}
-\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}%
 
-\gdef\evenfootingxxx #1{\evenfootingyyy #1@|@|@|@|\finish}
-\gdef\evenfootingyyy #1@|#2@|#3@|#4\finish{%
+\def\evenfooting{\parsearg\evenfootingxxx}
+\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish}
+\def\evenfootingyyy #1\|#2\|#3\|#4\finish{%
 \global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
 
-\gdef\oddfootingxxx #1{\oddfootingyyy #1@|@|@|@|\finish}
-\gdef\oddfootingyyy #1@|#2@|#3@|#4\finish{%
-\global\oddfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+\def\oddfooting{\parsearg\oddfootingxxx}
+\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish}
+\def\oddfootingyyy #1\|#2\|#3\|#4\finish{%
+  \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}%
+  %
+  % Leave some space for the footline.  Hopefully ok to assume
+  % @evenfooting will not be used by itself.
+  \global\advance\pageheight by -\baselineskip
+  \global\advance\vsize by -\baselineskip
+}
+
+\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}}
 
-\gdef\everyfootingxxx #1{\everyfootingyyy #1@|@|@|@|\finish}
-\gdef\everyfootingyyy #1@|#2@|#3@|#4\finish{%
-\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}
-\global\oddfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
-%
-}% unbind the catcode of @.
 
-% @headings double     turns headings on for double-sided printing.
-% @headings single     turns headings on for single-sided printing.
-% @headings off                turns them off.
-% @headings on         same as @headings double, retained for compatibility.
-% @headings after      turns on double-sided headings after this page.
-% @headings doubleafter        turns on double-sided headings after this page.
+% @headings double      turns headings on for double-sided printing.
+% @headings single      turns headings on for single-sided printing.
+% @headings off         turns them off.
+% @headings on          same as @headings double, retained for compatibility.
+% @headings after       turns on double-sided headings after this page.
+% @headings doubleafter turns on double-sided headings after this page.
 % @headings singleafter turns on single-sided headings after this page.
-% By default, they are off.
+% By default, they are off at the start of a document,
+% and turned `on' after @end titlepage.
 
 \def\headings #1 {\csname HEADINGS#1\endcsname}
 
-\def\HEADINGSoff{
+\def\HEADINGSoff{%
 \global\evenheadline={\hfil} \global\evenfootline={\hfil}
 \global\oddheadline={\hfil} \global\oddfootline={\hfil}}
 \HEADINGSoff
@@ -1315,23 +2325,25 @@ where each line of input produces a line of output.}
 % chapter name on inside top of right hand pages, document
 % title on inside top of left hand pages, and page numbers on outside top
 % edge of all pages.
-\def\HEADINGSdouble{
-%\pagealignmacro
+\def\HEADINGSdouble{%
 \global\pageno=1
 \global\evenfootline={\hfil}
 \global\oddfootline={\hfil}
 \global\evenheadline={\line{\folio\hfil\thistitle}}
 \global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chapoddpage
 }
+\let\contentsalignmacro = \chappager
+
 % For single-sided printing, chapter title goes across top left of page,
 % page number on top right.
-\def\HEADINGSsingle{
-%\pagealignmacro
+\def\HEADINGSsingle{%
 \global\pageno=1
 \global\evenfootline={\hfil}
 \global\oddfootline={\hfil}
 \global\evenheadline={\line{\thischapter\hfil\folio}}
 \global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chappager
 }
 \def\HEADINGSon{\HEADINGSdouble}
 
@@ -1342,6 +2354,7 @@ where each line of input produces a line of output.}
 \global\oddfootline={\hfil}
 \global\evenheadline={\line{\folio\hfil\thistitle}}
 \global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chapoddpage
 }
 
 \def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex}
@@ -1350,43 +2363,32 @@ where each line of input produces a line of output.}
 \global\oddfootline={\hfil}
 \global\evenheadline={\line{\thischapter\hfil\folio}}
 \global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chappager
 }
 
 % Subroutines used in generating headings
-% Produces Day Month Year style of output.
-\def\today{\number\day\space
-\ifcase\month\or
-January\or February\or March\or April\or May\or June\or
-July\or August\or September\or October\or November\or December\fi
-\space\number\year}
-
-% Use this if you want the Month Day, Year style of output.
-%\def\today{\ifcase\month\or
-%January\or February\or March\or April\or May\or June\or
-%July\or August\or September\or October\or November\or December\fi
-%\space\number\day, \number\year}
-
-% @settitle line...  specifies the title of the document, for headings
-% It generates no output of its own
-
-\def\thistitle{No Title}
-\def\settitle{\parsearg\settitlezzz}
-\def\settitlezzz #1{\gdef\thistitle{#1}}
-
-\message{tables,}
-
-% @tabs -- simple alignment
+% This produces Day Month Year style of output.
+% Only define if not already defined, in case a txi-??.tex file has set
+% up a different format (e.g., txi-cs.tex does this).
+\ifx\today\undefined
+\def\today{%
+  \number\day\space
+  \ifcase\month
+  \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr
+  \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug
+  \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec
+  \fi
+  \space\number\year}
+\fi
 
-% These don't work.  For one thing, \+ is defined as outer.
-% So these macros cannot even be defined.
+% @settitle line...  specifies the title of the document, for headings.
+% It generates no output of its own.
+\def\thistitle{\putwordNoTitle}
+\def\settitle{\parsearg{\gdef\thistitle}}
 
-%\def\tabs{\parsearg\tabszzz}
-%\def\tabszzz #1{\settabs\+#1\cr}
-%\def\tabline{\parsearg\tablinezzz}
-%\def\tablinezzz #1{\+#1\cr}
-%\def\&{&}
 
-% Tables -- @table, @ftable, @vtable, @item(x), @kitem(x), @xitem(x).
+\message{tables,}
+% Tables -- @table, @ftable, @vtable, @item(x).
 
 % default indentation of table text
 \newdimen\tableindent \tableindent=.8in
@@ -1398,42 +2400,25 @@ July\or August\or September\or October\or November\or December\fi
 % used internally for \itemindent minus \itemmargin
 \newdimen\itemmax
 
-% Note @table, @vtable, and @vtable define @item, @itemx, etc., with
+% Note @table, @ftable, and @vtable define @item, @itemx, etc., with
 % these defs.
 % They also define \itemindex
 % to index the item name in whatever manner is desired (perhaps none).
 
 \newif\ifitemxneedsnegativevskip
 
-\def\itemxpar{\par\ifitemxneedsnegativevskip\vskip-\parskip\nobreak\fi}
+\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi}
 
 \def\internalBitem{\smallbreak \parsearg\itemzzz}
 \def\internalBitemx{\itemxpar \parsearg\itemzzz}
 
-\def\internalBxitem "#1"{\def\xitemsubtopix{#1} \smallbreak \parsearg\xitemzzz}
-\def\internalBxitemx "#1"{\def\xitemsubtopix{#1} \itemxpar \parsearg\xitemzzz}
-
-\def\internalBkitem{\smallbreak \parsearg\kitemzzz}
-\def\internalBkitemx{\itemxpar \parsearg\kitemzzz}
-
-\def\kitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \lastfunction}}%
-                 \itemzzz {#1}}
-
-\def\xitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \xitemsubtopic}}%
-                 \itemzzz {#1}}
-
 \def\itemzzz #1{\begingroup %
   \advance\hsize by -\rightskip
   \advance\hsize by -\tableindent
-  \setbox0=\hbox{\itemfont{#1}}%
+  \setbox0=\hbox{\itemindicate{#1}}%
   \itemindex{#1}%
   \nobreak % This prevents a break before @itemx.
   %
-  % Be sure we are not still in the middle of a paragraph.
-  %{\parskip = 0in
-  %\par
-  %}%
-  %
   % If the item text does not fit in the space we have, put it on a line
   % by itself, and do not allow a page break either before or after that
   % line.  We do not start a paragraph here because then if the next
@@ -1454,114 +2439,132 @@ July\or August\or September\or October\or November\or December\fi
     % \parskip glue -- logically it's part of the @item we just started.
     \nobreak \vskip-\parskip
     %
-    % Stop a page break at the \parskip glue coming up.  Unfortunately
-    % we can't prevent a possible page break at the following
-    % \baselineskip glue.
-    \nobreak
+    % Stop a page break at the \parskip glue coming up.  However, if
+    % what follows is an environment such as @example, there will be no
+    % \parskip glue; then the negative vskip we just inserted would
+    % cause the example and the item to crash together.  So we use this
+    % bizarre value of 10001 as a signal to \aboveenvbreak to insert
+    % \parskip glue after all.  Section titles are handled this way also.
+    % 
+    \penalty 10001
     \endgroup
     \itemxneedsnegativevskipfalse
   \else
     % The item text fits into the space.  Start a paragraph, so that the
-    % following text (if any) will end up on the same line.  Since that
-    % text will be indented by \tableindent, we make the item text be in
-    % a zero-width box.
+    % following text (if any) will end up on the same line.
     \noindent
-    \rlap{\hskip -\tableindent\box0}\ignorespaces%
-    \endgroup%
-    \itemxneedsnegativevskiptrue%
+    % Do this with kerns and \unhbox so that if there is a footnote in
+    % the item text, it can migrate to the main vertical list and
+    % eventually be printed.
+    \nobreak\kern-\tableindent
+    \dimen0 = \itemmax  \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0
+    \unhbox0
+    \nobreak\kern\dimen0
+    \endgroup
+    \itemxneedsnegativevskiptrue
   \fi
 }
 
-\def\item{\errmessage{@item while not in a table}}
-\def\itemx{\errmessage{@itemx while not in a table}}
-\def\kitem{\errmessage{@kitem while not in a table}}
-\def\kitemx{\errmessage{@kitemx while not in a table}}
-\def\xitem{\errmessage{@xitem while not in a table}}
-\def\xitemx{\errmessage{@xitemx while not in a table}}
-
-%% Contains a kludge to get @end[description] to work
-\def\description{\tablez{\dontindex}{1}{}{}{}{}}
-
-\def\table{\begingroup\inENV\obeylines\obeyspaces\tablex}
-{\obeylines\obeyspaces%
-\gdef\tablex #1^^M{%
-\tabley\dontindex#1        \endtabley}}
-
-\def\ftable{\begingroup\inENV\obeylines\obeyspaces\ftablex}
-{\obeylines\obeyspaces%
-\gdef\ftablex #1^^M{%
-\tabley\fnitemindex#1        \endtabley
-\def\Eftable{\endgraf\afterenvbreak\endgroup}%
-\let\Etable=\relax}}
-
-\def\vtable{\begingroup\inENV\obeylines\obeyspaces\vtablex}
-{\obeylines\obeyspaces%
-\gdef\vtablex #1^^M{%
-\tabley\vritemindex#1        \endtabley
-\def\Evtable{\endgraf\afterenvbreak\endgroup}%
-\let\Etable=\relax}}
-
-\def\dontindex #1{}
-\def\fnitemindex #1{\doind {fn}{\code{#1}}}%
-\def\vritemindex #1{\doind {vr}{\code{#1}}}%
-
-{\obeyspaces %
-\gdef\tabley#1#2 #3 #4 #5 #6 #7\endtabley{\endgroup%
-\tablez{#1}{#2}{#3}{#4}{#5}{#6}}}
-
-\def\tablez #1#2#3#4#5#6{%
-\aboveenvbreak %
-\begingroup %
-\def\Edescription{\Etable}% Neccessary kludge.
-\let\itemindex=#1%
-\ifnum 0#3>0 \advance \leftskip by #3\mil \fi %
-\ifnum 0#4>0 \tableindent=#4\mil \fi %
-\ifnum 0#5>0 \advance \rightskip by #5\mil \fi %
-\def\itemfont{#2}%
-\itemmax=\tableindent %
-\advance \itemmax by -\itemmargin %
-\advance \leftskip by \tableindent %
-\exdentamount=\tableindent
-\parindent = 0pt
-\parskip = \smallskipamount
-\ifdim \parskip=0pt \parskip=2pt \fi%
-\def\Etable{\endgraf\afterenvbreak\endgroup}%
-\let\item = \internalBitem %
-\let\itemx = \internalBitemx %
-\let\kitem = \internalBkitem %
-\let\kitemx = \internalBkitemx %
-\let\xitem = \internalBxitem %
-\let\xitemx = \internalBxitemx %
+\def\item{\errmessage{@item while not in a list environment}}
+\def\itemx{\errmessage{@itemx while not in a list environment}}
+
+% @table, @ftable, @vtable.
+\envdef\table{%
+  \let\itemindex\gobble
+  \tablecheck{table}%
+}
+\envdef\ftable{%
+  \def\itemindex ##1{\doind {fn}{\code{##1}}}%
+  \tablecheck{ftable}%
+}
+\envdef\vtable{%
+  \def\itemindex ##1{\doind {vr}{\code{##1}}}%
+  \tablecheck{vtable}%
+}
+\def\tablecheck#1{%
+  \ifnum \the\catcode`\^^M=\active
+    \endgroup
+    \errmessage{This command won't work in this context; perhaps the problem is
+      that we are \inenvironment\thisenv}%
+    \def\next{\doignore{#1}}%
+  \else
+    \let\next\tablex
+  \fi
+  \next
+}
+\def\tablex#1{%
+  \def\itemindicate{#1}%
+  \parsearg\tabley
+}
+\def\tabley#1{%
+  {%
+    \makevalueexpandable
+    \edef\temp{\noexpand\tablez #1\space\space\space}%
+    \expandafter
+  }\temp \endtablez
 }
+\def\tablez #1 #2 #3 #4\endtablez{%
+  \aboveenvbreak
+  \ifnum 0#1>0 \advance \leftskip by #1\mil \fi
+  \ifnum 0#2>0 \tableindent=#2\mil \fi
+  \ifnum 0#3>0 \advance \rightskip by #3\mil \fi
+  \itemmax=\tableindent
+  \advance \itemmax by -\itemmargin
+  \advance \leftskip by \tableindent
+  \exdentamount=\tableindent
+  \parindent = 0pt
+  \parskip = \smallskipamount
+  \ifdim \parskip=0pt \parskip=2pt \fi
+  \let\item = \internalBitem
+  \let\itemx = \internalBitemx
+}
+\def\Etable{\endgraf\afterenvbreak}
+\let\Eftable\Etable
+\let\Evtable\Etable
+\let\Eitemize\Etable
+\let\Eenumerate\Etable
 
 % This is the counter used by @enumerate, which is really @itemize
 
 \newcount \itemno
 
-\def\itemize{\parsearg\itemizezzz}
+\envdef\itemize{\parsearg\doitemize}
 
-\def\itemizezzz #1{%
-  \begingroup % ended by the @end itemsize
-  \itemizey {#1}{\Eitemize}
+\def\doitemize#1{%
+  \aboveenvbreak
+  \itemmax=\itemindent
+  \advance\itemmax by -\itemmargin
+  \advance\leftskip by \itemindent
+  \exdentamount=\itemindent
+  \parindent=0pt
+  \parskip=\smallskipamount
+  \ifdim\parskip=0pt \parskip=2pt \fi
+  \def\itemcontents{#1}%
+  % @itemize with no arg is equivalent to @itemize @bullet.
+  \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi
+  \let\item=\itemizeitem
 }
 
-\def\itemizey #1#2{%
-\aboveenvbreak %
-\itemmax=\itemindent %
-\advance \itemmax by -\itemmargin %
-\advance \leftskip by \itemindent %
-\exdentamount=\itemindent
-\parindent = 0pt %
-\parskip = \smallskipamount %
-\ifdim \parskip=0pt \parskip=2pt \fi%
-\def#2{\endgraf\afterenvbreak\endgroup}%
-\def\itemcontents{#1}%
-\let\item=\itemizeitem}
-
-% Set sfcode to normal for the chars that usually have another value.
-% These are `.?!:;,'
-\def\frenchspacing{\sfcode46=1000 \sfcode63=1000 \sfcode33=1000
-  \sfcode58=1000 \sfcode59=1000 \sfcode44=1000 }
+% Definition of @item while inside @itemize and @enumerate.
+%
+\def\itemizeitem{%
+  \advance\itemno by 1  % for enumerations
+  {\let\par=\endgraf \smallbreak}% reasonable place to break
+  {%
+   % If the document has an @itemize directly after a section title, a
+   % \nobreak will be last on the list, and \sectionheading will have
+   % done a \vskip-\parskip.  In that case, we don't want to zero
+   % parskip, or the item text will crash with the heading.  On the
+   % other hand, when there is normal text preceding the item (as there
+   % usually is), we do want to zero parskip, or there would be too much
+   % space.  In that case, we won't have a \nobreak before.  At least
+   % that's the theory.
+   \ifnum\lastpenalty<10000 \parskip=0in \fi
+   \noindent
+   \hbox to 0pt{\hss \itemcontents \kern\itemmargin}%
+   \vadjust{\penalty 1200}}% not good to break after first line of item.
+  \flushcr
+}
 
 % \splitoff TOKENS\endmark defines \first to be the first token in
 % TOKENS, and \rest to be the remainder.
@@ -1572,11 +2575,8 @@ July\or August\or September\or October\or November\or December\fi
 % or number, to specify the first label in the enumerated list.  No
 % argument is the same as `1'.
 %
-\def\enumerate{\parsearg\enumeratezzz}
-\def\enumeratezzz #1{\enumeratey #1  \endenumeratey}
+\envparseargdef\enumerate{\enumeratey #1  \endenumeratey}
 \def\enumeratey #1 #2\endenumeratey{%
-  \begingroup % ended by the @end enumerate
-  %
   % If we were given no argument, pretend we were given `1'.
   \def\thearg{#1}%
   \ifx\thearg\empty \def\thearg{1}\fi
@@ -1647,13 +2647,13 @@ July\or August\or September\or October\or November\or December\fi
   }%
 }
 
-% Call itemizey, adding a period to the first argument and supplying the
+% Call \doitemize, adding a period to the first argument and supplying the
 % common last two arguments.  Also subtract one from the initial value in
 % \itemno, since @item increments \itemno.
 %
 \def\startenumeration#1{%
   \advance\itemno by -1
-  \itemizey{#1.}\Eenumerate\flushcr
+  \doitemize{#1.}\flushcr
 }
 
 % @alphaenumerate and @capsenumerate are abbreviations for giving an arg
@@ -1664,22 +2664,12 @@ July\or August\or September\or October\or November\or December\fi
 \def\Ealphaenumerate{\Eenumerate}
 \def\Ecapsenumerate{\Eenumerate}
 
-% Definition of @item while inside @itemize.
-
-\def\itemizeitem{%
-\advance\itemno by 1
-{\let\par=\endgraf \smallbreak}%
-\ifhmode \errmessage{\in hmode at itemizeitem}\fi
-{\parskip=0in \hskip 0pt
-\hbox to 0pt{\hss \itemcontents\hskip \itemmargin}%
-\vadjust{\penalty 1200}}%
-\flushcr}
 
 % @multitable macros
-% Amy Hendrickson, 8/18/94
+% Amy Hendrickson, 8/18/94, 3/6/96
 %
-% @multitable ... @endmultitable will make as many columns as desired.
-% Contents of each column will wrap at width given in preamble. Width
+% @multitable ... @end multitable will make as many columns as desired.
+% Contents of each column will wrap at width given in preamble.  Width
 % can be specified either with sample text given in a template line,
 % or in percent of \hsize, the current width of text on page.
 
@@ -1687,201 +2677,527 @@ July\or August\or September\or October\or November\or December\fi
 
 % To make preamble:
 %
-% Either define widths of columns in terms of percent of \hsize: 
-%   @multitable @percentofhsize .2 .3 .5
+% Either define widths of columns in terms of percent of \hsize:
+%   @multitable @columnfractions .25 .3 .45
 %   @item ...
 %
-%   Numbers following @percentofhsize are the percent of the total
+%   Numbers following @columnfractions are the percent of the total
 %   current hsize to be used for each column. You may use as many
 %   columns as desired.
 
+
 % Or use a template:
 %   @multitable {Column 1 template} {Column 2 template} {Column 3 template}
 %   @item ...
 %   using the widest term desired in each column.
 
-
-% Each new table line starts with @item, each subsequent new column 
+% Each new table line starts with @item, each subsequent new column
 % starts with @tab. Empty columns may be produced by supplying @tab's
 % with nothing between them for as many times as empty columns are needed,
 % ie, @tab@tab@tab will produce two empty columns.
 
-% @item, @tab, @multicolumn or @endmulticolumn do not need to be on their
-% own lines, but it will not hurt if they are.
+% @item, @tab do not need to be on their own lines, but it will not hurt
+% if they are.
 
 % Sample multitable:
 
 %   @multitable {Column 1 template} {Column 2 template} {Column 3 template}
 %   @item first col stuff @tab second col stuff @tab third col
-%   @item 
-%   first col stuff 
-%   @tab 
-%   second col stuff 
-%   @tab 
-%   third col 
-%   @item first col stuff @tab second col stuff 
+%   @item
+%   first col stuff
+%   @tab
+%   second col stuff
+%   @tab
+%   third col
+%   @item first col stuff @tab second col stuff
 %   @tab Many paragraphs of text may be used in any column.
-%     
+%
 %         They will wrap at the width determined by the template.
 %   @item@tab@tab This will be in third column.
-%   @endmultitable
+%   @end multitable
 
 % Default dimensions may be reset by user.
-% @intableparskip will set vertical space between paragraphs in table.
-% @intableparindent will set paragraph indent in table.
-% @spacebetweencols will set horizontal space to be left between columns.
-% @spacebetweenlines will set vertical space to be left between lines.
-
-%%%%
-% Dimensions 
-
-\newdimen\intableparskip
-\newdimen\intableparindent
-\newdimen\spacebetweencols
-\newdimen\spacebetweenlines
-\intableparskip=0pt
-\intableparindent=6pt
-\spacebetweencols=12pt
-\spacebetweenlines=12pt
-
-%%%%
+% @multitableparskip is vertical space between paragraphs in table.
+% @multitableparindent is paragraph indent in table.
+% @multitablecolmargin is horizontal space to be left between columns.
+% @multitablelinespace is space to leave between table items, baseline
+%                                                            to baseline.
+%   0pt means it depends on current normal line spacing.
+%
+\newskip\multitableparskip
+\newskip\multitableparindent
+\newdimen\multitablecolspace
+\newskip\multitablelinespace
+\multitableparskip=0pt
+\multitableparindent=6pt
+\multitablecolspace=12pt
+\multitablelinespace=0pt
+
 % Macros used to set up halign preamble:
+%
 \let\endsetuptable\relax
 \def\xendsetuptable{\endsetuptable}
-\let\percentofhsize\relax
-\def\xpercentofhsize{\percentofhsize}
+\let\columnfractions\relax
+\def\xcolumnfractions{\columnfractions}
 \newif\ifsetpercent
 
+% #1 is the @columnfraction, usually a decimal number like .5, but might
+% be just 1.  We just use it, whatever it is.
+%
+\def\pickupwholefraction#1 {%
+  \global\advance\colcount by 1
+  \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}%
+  \setuptable
+}
+
 \newcount\colcount
-\def\setuptable#1{\def\firstarg{#1}%
-\ifx\firstarg\xendsetuptable\let\go\relax%
-\else
-  \ifx\firstarg\xpercentofhsize\global\setpercenttrue%
+\def\setuptable#1{%
+  \def\firstarg{#1}%
+  \ifx\firstarg\xendsetuptable
+    \let\go = \relax
   \else
-    \ifsetpercent
-       \if#1.\else%
-       \global\advance\colcount by1 %
-       \expandafter\xdef\csname col\the\colcount\endcsname{.#1\hsize}%
-       \fi
+    \ifx\firstarg\xcolumnfractions
+      \global\setpercenttrue
+    \else
+      \ifsetpercent
+         \let\go\pickupwholefraction
+      \else
+         \global\advance\colcount by 1
+         \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a
+                   % separator; typically that is always in the input, anyway.
+         \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}%
+      \fi
+    \fi
+    \ifx\go\pickupwholefraction
+      % Put the argument back for the \pickupwholefraction call, so
+      % we'll always have a period there to be parsed.
+      \def\go{\pickupwholefraction#1}%
     \else
-       \global\advance\colcount by1
-       \setbox0=\hbox{#1}%
-       \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}%
+      \let\go = \setuptable
     \fi%
-  \fi%
-  \let\go\setuptable%
-\fi\go}
-%%%%
-% multitable syntax
-\def\tab{&}
-
-%%%%
-% @multitable ... @endmultitable definitions:
-
-\def\multitable#1\item{\bgroup
-\let\item\cr
-\tolerance=9500
-\hbadness=9500
-\parskip=\intableparskip
-\parindent=\intableparindent
-\overfullrule=0pt
-\global\colcount=0\relax%
-\def\Emultitable{\global\setpercentfalse\global\everycr{}\cr\egroup\egroup}%
- % To parse everything between @multitable and @item :
-\def\one{#1}\expandafter\setuptable\one\endsetuptable
- % Need to reset this to 0 after \setuptable.
-\global\colcount=0\relax% 
- %
- % This preamble sets up a generic column definition, which will
- % be used as many times as user calls for columns.
- % \vtop will set a single line and will also let text wrap and 
- % continue for many paragraphs if desired.
-\halign\bgroup&\global\advance\colcount by 1\relax%
-\vtop{\hsize=\expandafter\csname col\the\colcount\endcsname
- % In order to keep entries from bumping into each other
- % we will add a \leftskip of \spacebetweencols to all columns after
- % the first one.
- %  If a template has been used, we will add \spacebetweencols 
- % to the width of each template entry.
- %  If user has set preamble in terms of percent of \hsize
- % we will use that dimension as the width of the column, and
- % the \leftskip will keep entries from bumping into each other.
- % Table will start at left margin and final column will justify at
- % right margin.
-\ifnum\colcount=1
-\else
-  \ifsetpercent
-  \else
-   % If user has <not> set preamble in terms of percent of \hsize
-   % we will advance \hsize by \spacebetweencols 
-  \advance\hsize by \spacebetweencols
   \fi
- % In either case we will make \leftskip=\spacebetweencols:
-\leftskip=\spacebetweencols
+  \go
+}
+
+% multitable-only commands.
+%
+% @headitem starts a heading row, which we typeset in bold.
+% Assignments have to be global since we are inside the implicit group
+% of an alignment entry.  Note that \everycr resets \everytab.
+\def\headitem{\checkenv\multitable \crcr \global\everytab={\bf}\the\everytab}%
+%
+% A \tab used to include \hskip1sp.  But then the space in a template
+% line is not enough.  That is bad.  So let's go back to just `&' until
+% we encounter the problem it was intended to solve again.
+%                                      --karl, nathan@acm.org, 20apr99.
+\def\tab{\checkenv\multitable &\the\everytab}%
+
+% @multitable ... @end multitable definitions:
+%
+\newtoks\everytab  % insert after every tab.
+%
+\envdef\multitable{%
+  \vskip\parskip
+  \startsavinginserts
+  %
+  % @item within a multitable starts a normal row.
+  % We use \def instead of \let so that if one of the multitable entries
+  % contains an @itemize, we don't choke on the \item (seen as \crcr aka
+  % \endtemplate) expanding \doitemize.
+  \def\item{\crcr}%
+  %
+  \tolerance=9500
+  \hbadness=9500
+  \setmultitablespacing
+  \parskip=\multitableparskip
+  \parindent=\multitableparindent
+  \overfullrule=0pt
+  \global\colcount=0
+  %
+  \everycr = {%
+    \noalign{%
+      \global\everytab={}%
+      \global\colcount=0 % Reset the column counter.
+      % Check for saved footnotes, etc.
+      \checkinserts
+      % Keeps underfull box messages off when table breaks over pages.
+      %\filbreak
+       % Maybe so, but it also creates really weird page breaks when the
+       % table breaks over pages. Wouldn't \vfil be better?  Wait until the
+       % problem manifests itself, so it can be fixed for real --karl.
+    }%
+  }%
+  %
+  \parsearg\domultitable
+}
+\def\domultitable#1{%
+  % To parse everything between @multitable and @item:
+  \setuptable#1 \endsetuptable
+  %
+  % This preamble sets up a generic column definition, which will
+  % be used as many times as user calls for columns.
+  % \vtop will set a single line and will also let text wrap and
+  % continue for many paragraphs if desired.
+  \halign\bgroup &%
+    \global\advance\colcount by 1
+    \multistrut
+    \vtop{%
+      % Use the current \colcount to find the correct column width:
+      \hsize=\expandafter\csname col\the\colcount\endcsname
+      %
+      % In order to keep entries from bumping into each other
+      % we will add a \leftskip of \multitablecolspace to all columns after
+      % the first one.
+      %
+      % If a template has been used, we will add \multitablecolspace
+      % to the width of each template entry.
+      %
+      % If the user has set preamble in terms of percent of \hsize we will
+      % use that dimension as the width of the column, and the \leftskip
+      % will keep entries from bumping into each other.  Table will start at
+      % left margin and final column will justify at right margin.
+      %
+      % Make sure we don't inherit \rightskip from the outer environment.
+      \rightskip=0pt
+      \ifnum\colcount=1
+       % The first column will be indented with the surrounding text.
+       \advance\hsize by\leftskip
+      \else
+       \ifsetpercent \else
+         % If user has not set preamble in terms of percent of \hsize
+         % we will advance \hsize by \multitablecolspace.
+         \advance\hsize by \multitablecolspace
+       \fi
+       % In either case we will make \leftskip=\multitablecolspace:
+      \leftskip=\multitablecolspace
+      \fi
+      % Ignoring space at the beginning and end avoids an occasional spurious
+      % blank line, when TeX decides to break the line at the space before the
+      % box from the multistrut, so the strut ends up on a line by itself.
+      % For example:
+      % @multitable @columnfractions .11 .89
+      % @item @code{#}
+      % @tab Legal holiday which is valid in major parts of the whole country.
+      % Is automatically provided with highlighting sequences respectively
+      % marking characters.
+      \noindent\ignorespaces##\unskip\multistrut
+    }\cr
+}
+\def\Emultitable{%
+  \crcr
+  \egroup % end the \halign
+  \global\setpercentfalse
+}
+
+\def\setmultitablespacing{%
+  \def\multistrut{\strut}% just use the standard line spacing
+  %
+  % Compute \multitablelinespace (if not defined by user) for use in
+  % \multitableparskip calculation.  We used define \multistrut based on
+  % this, but (ironically) that caused the spacing to be off.
+  % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100.
+\ifdim\multitablelinespace=0pt
+\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip
+\global\advance\multitablelinespace by-\ht0
 \fi
-\noindent##}\cr%
- % \everycr will reset column counter, \colcount, at the end of
- % each line. Every column  entry will cause \colcount to advance by one. 
- % The table preamble
- % looks at the current \colcount to find the correct column width.
-\global\everycr{\noalign{\nointerlineskip\vskip\spacebetweenlines
-\filbreak%% keeps underfull box messages off when table breaks over pages.
-\global\colcount=0\relax}}}
+%% Test to see if parskip is larger than space between lines of
+%% table. If not, do nothing.
+%%        If so, set to same dimension as multitablelinespace.
+\ifdim\multitableparskip>\multitablelinespace
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller
+                                      %% than skip between lines in the table.
+\fi%
+\ifdim\multitableparskip=0pt
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller
+                                      %% than skip between lines in the table.
+\fi}
+
+
+\message{conditionals,}
+
+% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext,
+% @ifnotxml always succeed.  They currently do nothing; we don't
+% attempt to check whether the conditionals are properly nested.  But we
+% have to remember that they are conditionals, so that @end doesn't
+% attempt to close an environment group.
+%
+\def\makecond#1{%
+  \expandafter\let\csname #1\endcsname = \relax
+  \expandafter\let\csname iscond.#1\endcsname = 1
+}
+\makecond{iftex}
+\makecond{ifnotdocbook}
+\makecond{ifnothtml}
+\makecond{ifnotinfo}
+\makecond{ifnotplaintext}
+\makecond{ifnotxml}
+
+% Ignore @ignore, @ifhtml, @ifinfo, and the like.
+%
+\def\direntry{\doignore{direntry}}
+\def\documentdescription{\doignore{documentdescription}}
+\def\docbook{\doignore{docbook}}
+\def\html{\doignore{html}}
+\def\ifdocbook{\doignore{ifdocbook}}
+\def\ifhtml{\doignore{ifhtml}}
+\def\ifinfo{\doignore{ifinfo}}
+\def\ifnottex{\doignore{ifnottex}}
+\def\ifplaintext{\doignore{ifplaintext}}
+\def\ifxml{\doignore{ifxml}}
+\def\ignore{\doignore{ignore}}
+\def\menu{\doignore{menu}}
+\def\xml{\doignore{xml}}
+
+% Ignore text until a line `@end #1', keeping track of nested conditionals.
+%
+% A count to remember the depth of nesting.
+\newcount\doignorecount
+
+\def\doignore#1{\begingroup
+  % Scan in ``verbatim'' mode:
+  \obeylines
+  \catcode`\@ = \other
+  \catcode`\{ = \other
+  \catcode`\} = \other
+  %
+  % Make sure that spaces turn into tokens that match what \doignoretext wants.
+  \spaceisspace
+  %
+  % Count number of #1's that we've seen.
+  \doignorecount = 0
+  %
+  % Swallow text until we reach the matching `@end #1'.
+  \dodoignore{#1}%
+}
+
+{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source.
+  \obeylines %
+  %
+  \gdef\dodoignore#1{%
+    % #1 contains the command name as a string, e.g., `ifinfo'.
+    %
+    % Define a command to find the next `@end #1'.
+    \long\def\doignoretext##1^^M@end #1{%
+      \doignoretextyyy##1^^M@#1\_STOP_}%
+    %
+    % And this command to find another #1 command, at the beginning of a
+    % line.  (Otherwise, we would consider a line `@c @ifset', for
+    % example, to count as an @ifset for nesting.)
+    \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}%
+    %
+    % And now expand that command.
+    \doignoretext ^^M%
+  }%
+}
+
+\def\doignoreyyy#1{%
+  \def\temp{#1}%
+  \ifx\temp\empty                      % Nothing found.
+    \let\next\doignoretextzzz
+  \else                                        % Found a nested condition, ...
+    \advance\doignorecount by 1
+    \let\next\doignoretextyyy          % ..., look for another.
+    % If we're here, #1 ends with ^^M\ifinfo (for example).
+  \fi
+  \next #1% the token \_STOP_ is present just after this macro.
+}
+
+% We have to swallow the remaining "\_STOP_".
+%
+\def\doignoretextzzz#1{%
+  \ifnum\doignorecount = 0     % We have just found the outermost @end.
+    \let\next\enddoignore
+  \else                                % Still inside a nested condition.
+    \advance\doignorecount by -1
+    \let\next\doignoretext      % Look for the next @end.
+  \fi
+  \next
+}
+
+% Finish off ignored text.
+{ \obeylines%
+  % Ignore anything after the last `@end #1'; this matters in verbatim
+  % environments, where otherwise the newline after an ignored conditional
+  % would result in a blank line in the output.
+  \gdef\enddoignore#1^^M{\endgroup\ignorespaces}%
+}
+
+
+% @set VAR sets the variable VAR to an empty value.
+% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE.
+%
+% Since we want to separate VAR from REST-OF-LINE (which might be
+% empty), we can't just use \parsearg; we have to insert a space of our
+% own to delimit the rest of the line, and then take it out again if we
+% didn't need it.
+% We rely on the fact that \parsearg sets \catcode`\ =10.
+%
+\parseargdef\set{\setyyy#1 \endsetyyy}
+\def\setyyy#1 #2\endsetyyy{%
+  {%
+    \makevalueexpandable
+    \def\temp{#2}%
+    \edef\next{\gdef\makecsname{SET#1}}%
+    \ifx\temp\empty
+      \next{}%
+    \else
+      \setzzz#2\endsetzzz
+    \fi
+  }%
+}
+% Remove the trailing space \setxxx inserted.
+\def\setzzz#1 \endsetzzz{\next{#1}}
+
+% @clear VAR clears (i.e., unsets) the variable VAR.
+%
+\parseargdef\clear{%
+  {%
+    \makevalueexpandable
+    \global\expandafter\let\csname SET#1\endcsname=\relax
+  }%
+}
+
+% @value{foo} gets the text saved in variable foo.
+\def\value{\begingroup\makevalueexpandable\valuexxx}
+\def\valuexxx#1{\expandablevalue{#1}\endgroup}
+{
+  \catcode`\- = \active \catcode`\_ = \active
+  %
+  \gdef\makevalueexpandable{%
+    \let\value = \expandablevalue
+    % We don't want these characters active, ...
+    \catcode`\-=\other \catcode`\_=\other
+    % ..., but we might end up with active ones in the argument if
+    % we're called from @code, as @code{@value{foo-bar_}}, though.
+    % So \let them to their normal equivalents.
+    \let-\realdash \let_\normalunderscore
+  }
+}
+
+% We have this subroutine so that we can handle at least some @value's
+% properly in indexes (we call \makevalueexpandable in \indexdummies).
+% The command has to be fully expandable (if the variable is set), since
+% the result winds up in the index file.  This means that if the
+% variable's value contains other Texinfo commands, it's almost certain
+% it will fail (although perhaps we could fix that with sufficient work
+% to do a one-level expansion on the result, instead of complete).
+%
+\def\expandablevalue#1{%
+  \expandafter\ifx\csname SET#1\endcsname\relax
+    {[No value for ``#1'']}%
+    \message{Variable `#1', used in @value, is not set.}%
+  \else
+    \csname SET#1\endcsname
+  \fi
+}
+
+% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined
+% with @set.
+%
+% To get special treatment of `@end ifset,' call \makeond and the redefine.
+%
+\makecond{ifset}
+\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}}
+\def\doifset#1#2{%
+  {%
+    \makevalueexpandable
+    \let\next=\empty
+    \expandafter\ifx\csname SET#2\endcsname\relax
+      #1% If not set, redefine \next.
+    \fi
+    \expandafter
+  }\next
+}
+\def\ifsetfail{\doignore{ifset}}
+
+% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been
+% defined with @set, or has been undefined with @clear.
+%
+% The `\else' inside the `\doifset' parameter is a trick to reuse the
+% above code: if the variable is not set, do nothing, if it is set,
+% then redefine \next to \ifclearfail.
+%
+\makecond{ifclear}
+\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}}
+\def\ifclearfail{\doignore{ifclear}}
+
+% @dircategory CATEGORY  -- specify a category of the dir file
+% which this file should belong to.  Ignore this in TeX.
+\let\dircategory=\comment
+
+% @defininfoenclose.
+\let\definfoenclose=\comment
+
 
 \message{indexing,}
 % Index generation facilities
 
 % Define \newwrite to be identical to plain tex's \newwrite
-% except not \outer, so it can be used within \newindex.
-{\catcode`\@=11
-\gdef\newwrite{\alloc@7\write\chardef\sixt@@n}}
+% except not \outer, so it can be used within macros and \if's.
+\edef\newwrite{\makecsname{ptexnewwrite}}
 
 % \newindex {foo} defines an index named foo.
 % It automatically defines \fooindex such that
 % \fooindex ...rest of line... puts an entry in the index foo.
 % It also defines \fooindfile to be the number of the output channel for
-% the file that        accumulates this index.  The file's extension is foo.
+% the file that accumulates this index.  The file's extension is foo.
 % The name of an index should be no more than 2 characters long
 % for the sake of vms.
-
-\def\newindex #1{
-\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file
-\openout \csname#1indfile\endcsname \jobname.#1        % Open the file
-\expandafter\xdef\csname#1index\endcsname{%    % Define \xxxindex
-\noexpand\doindex {#1}}
+%
+\def\newindex#1{%
+  \iflinks
+    \expandafter\newwrite \csname#1indfile\endcsname
+    \openout \csname#1indfile\endcsname \jobname.#1 % Open the file
+  \fi
+  \expandafter\xdef\csname#1index\endcsname{%     % Define @#1index
+    \noexpand\doindex{#1}}
 }
 
 % @defindex foo  ==  \newindex{foo}
-
+%
 \def\defindex{\parsearg\newindex}
 
 % Define @defcodeindex, like @defindex except put all entries in @code.
-
-\def\newcodeindex #1{
-\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file
-\openout \csname#1indfile\endcsname \jobname.#1        % Open the file
-\expandafter\xdef\csname#1index\endcsname{%    % Define \xxxindex
-\noexpand\docodeindex {#1}}
+%
+\def\defcodeindex{\parsearg\newcodeindex}
+%
+\def\newcodeindex#1{%
+  \iflinks
+    \expandafter\newwrite \csname#1indfile\endcsname
+    \openout \csname#1indfile\endcsname \jobname.#1
+  \fi
+  \expandafter\xdef\csname#1index\endcsname{%
+    \noexpand\docodeindex{#1}}%
 }
 
-\def\defcodeindex{\parsearg\newcodeindex}
 
 % @synindex foo bar    makes index foo feed into index bar.
 % Do this instead of @defindex foo if you don't want it as a separate index.
-\def\synindex #1 #2 {%
-\expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname
-\expandafter\let\csname#1indfile\endcsname=\synindexfoo
-\expandafter\xdef\csname#1index\endcsname{%    % Define \xxxindex
-\noexpand\doindex {#2}}%
-}
-
+%
 % @syncodeindex foo bar   similar, but put all entries made for index foo
 % inside @code.
-\def\syncodeindex #1 #2 {%
-\expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname
-\expandafter\let\csname#1indfile\endcsname=\synindexfoo
-\expandafter\xdef\csname#1index\endcsname{%    % Define \xxxindex
-\noexpand\docodeindex {#2}}%
+%
+\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}}
+\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}}
+
+% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo),
+% #3 the target index (bar).
+\def\dosynindex#1#2#3{%
+  % Only do \closeout if we haven't already done it, else we'll end up
+  % closing the target index.
+  \expandafter \ifx\csname donesynindex#2\endcsname \undefined
+    % The \closeout helps reduce unnecessary open files; the limit on the
+    % Acorn RISC OS is a mere 16 files.
+    \expandafter\closeout\csname#2indfile\endcsname
+    \expandafter\let\csname\donesynindex#2\endcsname = 1
+  \fi
+  % redefine \fooindfile:
+  \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname
+  \expandafter\let\csname#2indfile\endcsname=\temp
+  % redefine \fooindex:
+  \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}%
 }
 
 % Define \doindex, the driver for all \fooindex macros.
@@ -1901,175 +3217,402 @@ July\or August\or September\or October\or November\or December\fi
 \def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer}
 \def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}}
 
+% Take care of Texinfo commands that can appear in an index entry.
+% Since there are some commands we want to expand, and others we don't,
+% we have to laboriously prevent expansion for those that we don't.
+%
 \def\indexdummies{%
-% Take care of the plain tex accent commands.
-\def\"{\realbackslash "}%
-\def\`{\realbackslash `}%
-\def\'{\realbackslash '}%
-\def\^{\realbackslash ^}%
-\def\~{\realbackslash ~}%
-\def\={\realbackslash =}%
-\def\b{\realbackslash b}%
-\def\c{\realbackslash c}%
-\def\d{\realbackslash d}%
-\def\u{\realbackslash u}%
-\def\v{\realbackslash v}%
-\def\H{\realbackslash H}%
-% Take care of the plain tex special European modified letters.
-\def\oe{\realbackslash oe}%
-\def\ae{\realbackslash ae}%
-\def\aa{\realbackslash aa}%
-\def\OE{\realbackslash OE}%
-\def\AE{\realbackslash AE}%
-\def\AA{\realbackslash AA}%
-\def\o{\realbackslash o}%
-\def\O{\realbackslash O}%
-\def\l{\realbackslash l}%
-\def\L{\realbackslash L}%
-\def\ss{\realbackslash ss}%
-% Take care of texinfo commands likely to appear in an index entry.
-\def\_{{\realbackslash _}}%
-\def\w{\realbackslash w }%
-\def\bf{\realbackslash bf }%
-\def\rm{\realbackslash rm }%
-\def\sl{\realbackslash sl }%
-\def\sf{\realbackslash sf}%
-\def\tt{\realbackslash tt}%
-\def\gtr{\realbackslash gtr}%
-\def\less{\realbackslash less}%
-\def\hat{\realbackslash hat}%
-\def\char{\realbackslash char}%
-\def\TeX{\realbackslash TeX}%
-\def\dots{\realbackslash dots }%
-\def\copyright{\realbackslash copyright }%
-\def\tclose##1{\realbackslash tclose {##1}}%
-\def\code##1{\realbackslash code {##1}}%
-\def\samp##1{\realbackslash samp {##1}}%
-\def\t##1{\realbackslash r {##1}}%
-\def\r##1{\realbackslash r {##1}}%
-\def\i##1{\realbackslash i {##1}}%
-\def\b##1{\realbackslash b {##1}}%
-\def\cite##1{\realbackslash cite {##1}}%
-\def\key##1{\realbackslash key {##1}}%
-\def\file##1{\realbackslash file {##1}}%
-\def\var##1{\realbackslash var {##1}}%
-\def\kbd##1{\realbackslash kbd {##1}}%
-\def\dfn##1{\realbackslash dfn {##1}}%
-\def\emph##1{\realbackslash emph {##1}}%
-}
-
-% \indexnofonts no-ops all font-change commands.
-% This is used when outputting the strings to sort the index by.
-\def\indexdummyfont#1{#1}
-\def\indexdummytex{TeX}
-\def\indexdummydots{...}
+  \escapechar = `\\     % use backslash in output files.
+  \def\@{@}% change to @@ when we switch to @ as escape char in index files.
+  \def\ {\realbackslash\space }%
+  % Need these in case \tex is in effect and \{ is a \delimiter again.
+  % But can't use \lbracecmd and \rbracecmd because texindex assumes
+  % braces and backslashes are used only as delimiters.
+  \let\{ = \mylbrace
+  \let\} = \myrbrace
+  %
+  % Do the redefinitions.
+  \commondummies
+}
 
-\def\indexnofonts{%
-% Just ignore accents.
-\let\"=\indexdummyfont
-\let\`=\indexdummyfont
-\let\'=\indexdummyfont
-\let\^=\indexdummyfont
-\let\~=\indexdummyfont
-\let\==\indexdummyfont
-\let\b=\indexdummyfont
-\let\c=\indexdummyfont
-\let\d=\indexdummyfont
-\let\u=\indexdummyfont
-\let\v=\indexdummyfont
-\let\H=\indexdummyfont
-% Take care of the plain tex special European modified letters.
-\def\oe{oe}%
-\def\ae{ae}%
-\def\aa{aa}%
-\def\OE{OE}%
-\def\AE{AE}%
-\def\AA{AA}%
-\def\o{o}%
-\def\O{O}%
-\def\l{l}%
-\def\L{L}%
-\def\ss{ss}%
-\let\w=\indexdummyfont
-\let\t=\indexdummyfont
-\let\r=\indexdummyfont
-\let\i=\indexdummyfont
-\let\b=\indexdummyfont
-\let\emph=\indexdummyfont
-\let\strong=\indexdummyfont
-\let\cite=\indexdummyfont
-\let\sc=\indexdummyfont
-%Don't no-op \tt, since it isn't a user-level command
-% and is used in the definitions of the active chars like <, >, |...
-%\let\tt=\indexdummyfont
-\let\tclose=\indexdummyfont
-\let\code=\indexdummyfont
-\let\file=\indexdummyfont
-\let\samp=\indexdummyfont
-\let\kbd=\indexdummyfont
-\let\key=\indexdummyfont
-\let\var=\indexdummyfont
-\let\TeX=\indexdummytex
-\let\dots=\indexdummydots
-}
-
-% To define \realbackslash, we must make \ not be an escape.
-% We must first make another character (@) an escape
-% so we do not become unable to do a definition.
-
-{\catcode`\@=0 \catcode`\\=\other
-@gdef@realbackslash{\}}
+% For the aux and toc files, @ is the escape character.  So we want to
+% redefine everything using @ as the escape character (instead of
+% \realbackslash, still used for index files).  When everything uses @,
+% this will be simpler.
+%
+\def\atdummies{%
+  \def\@{@@}%
+  \def\ {@ }%
+  \let\{ = \lbraceatcmd
+  \let\} = \rbraceatcmd
+  %
+  % Do the redefinitions.
+  \commondummies
+  \otherbackslash
+}
 
-\let\indexbackslash=0  %overridden during \printindex.
+% Called from \indexdummies and \atdummies.
+%
+\def\commondummies{%
+  %
+  % \definedummyword defines \#1 as \string\#1\space, thus effectively
+  % preventing its expansion.  This is used only for control% words,
+  % not control letters, because the \space would be incorrect for
+  % control characters, but is needed to separate the control word
+  % from whatever follows.
+  %
+  % For control letters, we have \definedummyletter, which omits the
+  % space.
+  %
+  % These can be used both for control words that take an argument and
+  % those that do not.  If it is followed by {arg} in the input, then
+  % that will dutifully get written to the index (or wherever).
+  %
+  \def\definedummyword  ##1{\def##1{\string##1\space}}%
+  \def\definedummyletter##1{\def##1{\string##1}}%
+  \let\definedummyaccent\definedummyletter
+  %
+  \commondummiesnofonts
+  %
+  \definedummyletter\_%
+  %
+  % Non-English letters.
+  \definedummyword\AA
+  \definedummyword\AE
+  \definedummyword\L
+  \definedummyword\OE
+  \definedummyword\O
+  \definedummyword\aa
+  \definedummyword\ae
+  \definedummyword\l
+  \definedummyword\oe
+  \definedummyword\o
+  \definedummyword\ss
+  \definedummyword\exclamdown
+  \definedummyword\questiondown
+  \definedummyword\ordf
+  \definedummyword\ordm
+  %
+  % Although these internal commands shouldn't show up, sometimes they do.
+  \definedummyword\bf
+  \definedummyword\gtr
+  \definedummyword\hat
+  \definedummyword\less
+  \definedummyword\sf
+  \definedummyword\sl
+  \definedummyword\tclose
+  \definedummyword\tt
+  %
+  \definedummyword\LaTeX
+  \definedummyword\TeX
+  %
+  % Assorted special characters.
+  \definedummyword\bullet
+  \definedummyword\comma
+  \definedummyword\copyright
+  \definedummyword\registeredsymbol
+  \definedummyword\dots
+  \definedummyword\enddots
+  \definedummyword\equiv
+  \definedummyword\error
+  \definedummyword\euro
+  \definedummyword\expansion
+  \definedummyword\minus
+  \definedummyword\pounds
+  \definedummyword\point
+  \definedummyword\print
+  \definedummyword\result
+  %
+  % We want to disable all macros so that they are not expanded by \write.
+  \macrolist
+  %
+  \normalturnoffactive
+  %
+  % Handle some cases of @value -- where it does not contain any
+  % (non-fully-expandable) commands.
+  \makevalueexpandable
+}
 
-\def\doind #1#2{%
-{\count10=\lastpenalty %
-{\indexdummies % Must do this here, since \bf, etc expand at this stage
-\escapechar=`\\%
-{\let\folio=0% Expand all macros now EXCEPT \folio
-\def\rawbackslashxx{\indexbackslash}% \indexbackslash isn't defined now
-% so it will be output as is; and it will print as backslash in the indx.
-%
-% Now process the index-string once, with all font commands turned off,
-% to get the string to sort the index by.
-{\indexnofonts
-\xdef\temp1{#2}%
-}%
-% Now produce the complete index entry.  We process the index-string again,
-% this time with font commands expanded, to get what to print in the index.
-\edef\temp{%
-\write \csname#1indfile\endcsname{%
-\realbackslash entry {\temp1}{\folio}{#2}}}%
-\temp }%
-}\penalty\count10}}
-
-\def\dosubind #1#2#3{%
-{\count10=\lastpenalty %
-{\indexdummies % Must do this here, since \bf, etc expand at this stage
-\escapechar=`\\%
-{\let\folio=0%
-\def\rawbackslashxx{\indexbackslash}%
-%
-% Now process the index-string once, with all font commands turned off,
-% to get the string to sort the index by.
-{\indexnofonts
-\xdef\temp1{#2 #3}%
-}%
-% Now produce the complete index entry.  We process the index-string again,
-% this time with font commands expanded, to get what to print in the index.
-\edef\temp{%
-\write \csname#1indfile\endcsname{%
-\realbackslash entry {\temp1}{\folio}{#2}{#3}}}%
-\temp }%
-}\penalty\count10}}
+% \commondummiesnofonts: common to \commondummies and \indexnofonts.
+%
+\def\commondummiesnofonts{%
+  % Control letters and accents.
+  \definedummyletter\!%
+  \definedummyaccent\"%
+  \definedummyaccent\'%
+  \definedummyletter\*%
+  \definedummyaccent\,%
+  \definedummyletter\.%
+  \definedummyletter\/%
+  \definedummyletter\:%
+  \definedummyaccent\=%
+  \definedummyletter\?%
+  \definedummyaccent\^%
+  \definedummyaccent\`%
+  \definedummyaccent\~%
+  \definedummyword\u
+  \definedummyword\v
+  \definedummyword\H
+  \definedummyword\dotaccent
+  \definedummyword\ringaccent
+  \definedummyword\tieaccent
+  \definedummyword\ubaraccent
+  \definedummyword\udotaccent
+  \definedummyword\dotless
+  %
+  % Texinfo font commands.
+  \definedummyword\b
+  \definedummyword\i
+  \definedummyword\r
+  \definedummyword\sc
+  \definedummyword\t
+  %
+  % Commands that take arguments.
+  \definedummyword\acronym
+  \definedummyword\cite
+  \definedummyword\code
+  \definedummyword\command
+  \definedummyword\dfn
+  \definedummyword\emph
+  \definedummyword\env
+  \definedummyword\file
+  \definedummyword\kbd
+  \definedummyword\key
+  \definedummyword\math
+  \definedummyword\option
+  \definedummyword\pxref
+  \definedummyword\ref
+  \definedummyword\samp
+  \definedummyword\strong
+  \definedummyword\tie
+  \definedummyword\uref
+  \definedummyword\url
+  \definedummyword\var
+  \definedummyword\verb
+  \definedummyword\w
+  \definedummyword\xref
+}
 
-% The index entry written in the file actually looks like
-%  \entry {sortstring}{page}{topic}
-% or
-%  \entry {sortstring}{page}{topic}{subtopic}
-% The texindex program reads in these files and writes files
-% containing these kinds of lines:
-%  \initial {c}
+% \indexnofonts is used when outputting the strings to sort the index
+% by, and when constructing control sequence names.  It eliminates all
+% control sequences and just writes whatever the best ASCII sort string
+% would be for a given command (usually its argument).
+%
+\def\indexnofonts{%
+  % Accent commands should become @asis.
+  \def\definedummyaccent##1{\let##1\asis}%
+  % We can just ignore other control letters.
+  \def\definedummyletter##1{\let##1\empty}%
+  % Hopefully, all control words can become @asis.
+  \let\definedummyword\definedummyaccent
+  %
+  \commondummiesnofonts
+  %
+  % Don't no-op \tt, since it isn't a user-level command
+  % and is used in the definitions of the active chars like <, >, |, etc.
+  % Likewise with the other plain tex font commands.
+  %\let\tt=\asis
+  %
+  \def\ { }%
+  \def\@{@}%
+  % how to handle braces?
+  \def\_{\normalunderscore}%
+  %
+  % Non-English letters.
+  \def\AA{AA}%
+  \def\AE{AE}%
+  \def\L{L}%
+  \def\OE{OE}%
+  \def\O{O}%
+  \def\aa{aa}%
+  \def\ae{ae}%
+  \def\l{l}%
+  \def\oe{oe}%
+  \def\o{o}%
+  \def\ss{ss}%
+  \def\exclamdown{!}%
+  \def\questiondown{?}%
+  \def\ordf{a}%
+  \def\ordm{o}%
+  %
+  \def\LaTeX{LaTeX}%
+  \def\TeX{TeX}%
+  %
+  % Assorted special characters.
+  % (The following {} will end up in the sort string, but that's ok.)
+  \def\bullet{bullet}%
+  \def\comma{,}%
+  \def\copyright{copyright}%
+  \def\registeredsymbol{R}%
+  \def\dots{...}%
+  \def\enddots{...}%
+  \def\equiv{==}%
+  \def\error{error}%
+  \def\euro{euro}%
+  \def\expansion{==>}%
+  \def\minus{-}%
+  \def\pounds{pounds}%
+  \def\point{.}%
+  \def\print{-|}%
+  \def\result{=>}%
+  %
+  % We need to get rid of all macros, leaving only the arguments (if present).
+  % Of course this is not nearly correct, but it is the best we can do for now.
+  % makeinfo does not expand macros in the argument to @deffn, which ends up
+  % writing an index entry, and texindex isn't prepared for an index sort entry
+  % that starts with \.
+  % 
+  % Since macro invocations are followed by braces, we can just redefine them
+  % to take a single TeX argument.  The case of a macro invocation that
+  % goes to end-of-line is not handled.
+  % 
+  \macrolist
+}
+
+\let\indexbackslash=0  %overridden during \printindex.
+\let\SETmarginindex=\relax % put index entries in margin (undocumented)?
+
+% Most index entries go through here, but \dosubind is the general case.
+% #1 is the index name, #2 is the entry text.
+\def\doind#1#2{\dosubind{#1}{#2}{}}
+
+% Workhorse for all \fooindexes.
+% #1 is name of index, #2 is stuff to put there, #3 is subentry --
+% empty if called from \doind, as we usually are (the main exception
+% is with most defuns, which call us directly).
+%
+\def\dosubind#1#2#3{%
+  \iflinks
+  {%
+    % Store the main index entry text (including the third arg).
+    \toks0 = {#2}%
+    % If third arg is present, precede it with a space.
+    \def\thirdarg{#3}%
+    \ifx\thirdarg\empty \else
+      \toks0 = \expandafter{\the\toks0 \space #3}%
+    \fi
+    %
+    \edef\writeto{\csname#1indfile\endcsname}%
+    %
+    \ifvmode
+      \dosubindsanitize
+    \else
+      \dosubindwrite
+    \fi
+  }%
+  \fi
+}
+
+% Write the entry in \toks0 to the index file:
+%
+\def\dosubindwrite{%
+  % Put the index entry in the margin if desired.
+  \ifx\SETmarginindex\relax\else
+    \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}%
+  \fi
+  %
+  % Remember, we are within a group.
+  \indexdummies % Must do this here, since \bf, etc expand at this stage
+  \def\backslashcurfont{\indexbackslash}% \indexbackslash isn't defined now
+      % so it will be output as is; and it will print as backslash.
+  %
+  % Process the index entry with all font commands turned off, to
+  % get the string to sort by.
+  {\indexnofonts
+   \edef\temp{\the\toks0}% need full expansion
+   \xdef\indexsorttmp{\temp}%
+  }%
+  %
+  % Set up the complete index entry, with both the sort key and
+  % the original text, including any font commands.  We write
+  % three arguments to \entry to the .?? file (four in the
+  % subentry case), texindex reduces to two when writing the .??s
+  % sorted result.
+  \edef\temp{%
+    \write\writeto{%
+      \string\entry{\indexsorttmp}{\noexpand\folio}{\the\toks0}}%
+  }%
+  \temp
+}
+
+% Take care of unwanted page breaks:
+%
+% If a skip is the last thing on the list now, preserve it
+% by backing up by \lastskip, doing the \write, then inserting
+% the skip again.  Otherwise, the whatsit generated by the
+% \write will make \lastskip zero.  The result is that sequences
+% like this:
+% @end defun
+% @tindex whatever
+% @defun ...
+% will have extra space inserted, because the \medbreak in the
+% start of the @defun won't see the skip inserted by the @end of
+% the previous defun.
+%
+% But don't do any of this if we're not in vertical mode.  We
+% don't want to do a \vskip and prematurely end a paragraph.
+%
+% Avoid page breaks due to these extra skips, too.
+%
+% But wait, there is a catch there:
+% We'll have to check whether \lastskip is zero skip.  \ifdim is not
+% sufficient for this purpose, as it ignores stretch and shrink parts
+% of the skip.  The only way seems to be to check the textual
+% representation of the skip.
+%
+% The following is almost like \def\zeroskipmacro{0.0pt} except that
+% the ``p'' and ``t'' characters have catcode \other, not 11 (letter).
+%
+\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname}
+%
+% ..., ready, GO:
+%
+\def\dosubindsanitize{%
+  % \lastskip and \lastpenalty cannot both be nonzero simultaneously.
+  \skip0 = \lastskip
+  \edef\lastskipmacro{\the\lastskip}%
+  \count255 = \lastpenalty
+  %
+  % If \lastskip is nonzero, that means the last item was a
+  % skip.  And since a skip is discardable, that means this
+  % -\skip0 glue we're inserting is preceded by a
+  % non-discardable item, therefore it is not a potential
+  % breakpoint, therefore no \nobreak needed.
+  \ifx\lastskipmacro\zeroskipmacro
+  \else
+    \vskip-\skip0
+  \fi
+  %
+  \dosubindwrite
+  %
+  \ifx\lastskipmacro\zeroskipmacro
+    % If \lastskip was zero, perhaps the last item was a penalty, and
+    % perhaps it was >=10000, e.g., a \nobreak.  In that case, we want
+    % to re-insert the same penalty (values >10000 are used for various
+    % signals); since we just inserted a non-discardable item, any
+    % following glue (such as a \parskip) would be a breakpoint.  For example:
+    % 
+    %   @deffn deffn-whatever
+    %   @vindex index-whatever
+    %   Description.
+    % would allow a break between the index-whatever whatsit
+    % and the "Description." paragraph.
+    \ifnum\count255>9999 \penalty\count255 \fi
+  \else
+    % On the other hand, if we had a nonzero \lastskip,
+    % this make-up glue would be preceded by a non-discardable item
+    % (the whatsit from the \write), so we must insert a \nobreak.
+    \nobreak\vskip\skip0
+  \fi
+}
+
+% The index entry written in the file actually looks like
+%  \entry {sortstring}{page}{topic}
+% or
+%  \entry {sortstring}{page}{topic}{subtopic}
+% The texindex program reads in these files and writes files
+% containing these kinds of lines:
+%  \initial {c}
 %     before the first topic whose initial is c
 %  \entry {topic}{pagelist}
 %     for a topic that is used without subtopics
@@ -2095,134 +3638,170 @@ July\or August\or September\or October\or November\or December\fi
 
 % Define the macros used in formatting output of the sorted index material.
 
-% This is what you call to cause a particular index to get printed.
-% Write
-% @unnumbered Function Index
-% @printindex fn
-
-\def\printindex{\parsearg\doprintindex}
-
-\def\doprintindex#1{%
-  \tex
-  \dobreak \chapheadingskip {10000}
-  \catcode`\%=\other\catcode`\&=\other\catcode`\#=\other
-  \catcode`\$=\other
-  \catcode`\~=\other
-  \indexbreaks
-  %
-  % The following don't help, since the chars were translated
-  % when the raw index was written, and their fonts were discarded
-  % due to \indexnofonts.
-  %\catcode`\"=\active
-  %\catcode`\^=\active
-  %\catcode`\_=\active
-  %\catcode`\|=\active
-  %\catcode`\<=\active
-  %\catcode`\>=\active
-  % %
-  \def\indexbackslash{\rawbackslashxx}
-  \indexfonts\rm \tolerance=9500 \advance\baselineskip -1pt
-  \begindoublecolumns
+% @printindex causes a particular index (the ??s file) to get printed.
+% It does not print any chapter heading (usually an @unnumbered).
+%
+\parseargdef\printindex{\begingroup
+  \dobreak \chapheadingskip{10000}%
+  %
+  \smallfonts \rm
+  \tolerance = 9500
+  \everypar = {}% don't want the \kern\-parindent from indentation suppression.
   %
   % See if the index file exists and is nonempty.
+  % Change catcode of @ here so that if the index file contains
+  % \initial {@}
+  % as its first line, TeX doesn't complain about mismatched braces
+  % (because it thinks @} is a control sequence).
+  \catcode`\@ = 11
   \openin 1 \jobname.#1s
   \ifeof 1
     % \enddoublecolumns gets confused if there is no text in the index,
     % and it loses the chapter title and the aux file entries for the
     % index.  The easiest way to prevent this problem is to make sure
     % there is some text.
-    (Index is nonexistent)
-    \else
+    \putwordIndexNonexistent
+  \else
     %
     % If the index file exists but is empty, then \openin leaves \ifeof
     % false.  We have to make TeX try to read something from the file, so
     % it can discover if there is anything in it.
     \read 1 to \temp
     \ifeof 1
-      (Index is empty)
+      \putwordIndexIsEmpty
     \else
+      % Index files are almost Texinfo source, but we use \ as the escape
+      % character.  It would be better to use @, but that's too big a change
+      % to make right now.
+      \def\indexbackslash{\backslashcurfont}%
+      \catcode`\\ = 0
+      \escapechar = `\\
+      \begindoublecolumns
       \input \jobname.#1s
+      \enddoublecolumns
     \fi
   \fi
   \closein 1
-  \enddoublecolumns
-  \Etex
-}
+\endgroup}
 
 % These macros are used by the sorted index file itself.
 % Change them to control the appearance of the index.
 
-% Same as \bigskipamount except no shrink.
-% \balancecolumns gets confused if there is any shrink.
-\newskip\initialskipamount \initialskipamount 12pt plus4pt
-
-\def\initial #1{%
-{\let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt
-\ifdim\lastskip<\initialskipamount
-\removelastskip \penalty-200 \vskip \initialskipamount\fi
-\line{\secbf#1\hfill}\kern 2pt\penalty10000}}
-
-% This typesets a paragraph consisting of #1, dot leaders, and then #2
-% flush to the right margin.  It is used for index and table of contents
-% entries.  The paragraph is indented by \leftskip.
-%
-\def\entry #1#2{\begingroup
-  %
-  % Start a new paragraph if necessary, so our assignments below can't
-  % affect previous text.
-  \par
-  %
-  % Do not fill out the last line with white space.
-  \parfillskip = 0in
-  %
-  % No extra space above this paragraph.
-  \parskip = 0in
-  %
-  % Do not prefer a separate line ending with a hyphen to fewer lines.
-  \finalhyphendemerits = 0
-  %
-  % \hangindent is only relevant when the entry text and page number
-  % don't both fit on one line.  In that case, bob suggests starting the
-  % dots pretty far over on the line.  Unfortunately, a large
-  % indentation looks wrong when the entry text itself is broken across
-  % lines.  So we use a small indentation and put up with long leaders.
+\def\initial#1{{%
+  % Some minor font changes for the special characters.
+  \let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt
   %
-  % \hangafter is reset to 1 (which is the value we want) at the start
-  % of each paragraph, so we need not do anything with that.
-  \hangindent=2em
+  % Remove any glue we may have, we'll be inserting our own.
+  \removelastskip
   %
-  % When the entry text needs to be broken, just fill out the first line
-  % with blank space.
-  \rightskip = 0pt plus1fil
+  % We like breaks before the index initials, so insert a bonus.
+  \nobreak
+  \vskip 0pt plus 3\baselineskip
+  \penalty 0
+  \vskip 0pt plus -3\baselineskip
   %
-  % Start a ``paragraph'' for the index entry so the line breaking
-  % parameters we've set above will have an effect.
-  \noindent
+  % Typeset the initial.  Making this add up to a whole number of
+  % baselineskips increases the chance of the dots lining up from column
+  % to column.  It still won't often be perfect, because of the stretch
+  % we need before each entry, but it's better.
   %
-  % Insert the text of the index entry.  TeX will do line-breaking on it.
-  #1%
-  % The following is kluged to not output a line of dots in the index if
-  % there are no page numbers.  The next person who breaks this will be
-  % cursed by a Unix daemon.
-  \def\tempa{{\rm }}%
-  \def\tempb{#2}%
-  \edef\tempc{\tempa}%
-  \edef\tempd{\tempb}%
-  \ifx\tempc\tempd\ \else%
+  % No shrink because it confuses \balancecolumns.
+  \vskip 1.67\baselineskip plus .5\baselineskip
+  \leftline{\secbf #1}%
+  % Do our best not to break after the initial.
+  \nobreak
+  \vskip .33\baselineskip plus .1\baselineskip
+}}
+
+% \entry typesets a paragraph consisting of the text (#1), dot leaders, and
+% then page number (#2) flushed to the right margin.  It is used for index
+% and table of contents entries.  The paragraph is indented by \leftskip.
+%
+% A straightforward implementation would start like this:
+%      \def\entry#1#2{...
+% But this frozes the catcodes in the argument, and can cause problems to
+% @code, which sets - active.  This problem was fixed by a kludge---
+% ``-'' was active throughout whole index, but this isn't really right.
+%
+% The right solution is to prevent \entry from swallowing the whole text.
+%                                 --kasal, 21nov03
+\def\entry{%
+  \begingroup
     %
-    % If we must, put the page number on a line of its own, and fill out
-    % this line with blank space.  (The \hfil is overwhelmed with the
-    % fill leaders glue in \indexdotfill if the page number does fit.)
-    \hfil\penalty50
-    \null\nobreak\indexdotfill % Have leaders before the page number.
+    % Start a new paragraph if necessary, so our assignments below can't
+    % affect previous text.
+    \par
     %
-    % The `\ ' here is removed by the implicit \unskip that TeX does as
-    % part of (the primitive) \par.  Without it, a spurious underfull
-    % \hbox ensues.
-    \ #2% The page number ends the paragraph.
-  \fi%
-  \par
-\endgroup}
+    % Do not fill out the last line with white space.
+    \parfillskip = 0in
+    %
+    % No extra space above this paragraph.
+    \parskip = 0in
+    %
+    % Do not prefer a separate line ending with a hyphen to fewer lines.
+    \finalhyphendemerits = 0
+    %
+    % \hangindent is only relevant when the entry text and page number
+    % don't both fit on one line.  In that case, bob suggests starting the
+    % dots pretty far over on the line.  Unfortunately, a large
+    % indentation looks wrong when the entry text itself is broken across
+    % lines.  So we use a small indentation and put up with long leaders.
+    %
+    % \hangafter is reset to 1 (which is the value we want) at the start
+    % of each paragraph, so we need not do anything with that.
+    \hangindent = 2em
+    %
+    % When the entry text needs to be broken, just fill out the first line
+    % with blank space.
+    \rightskip = 0pt plus1fil
+    %
+    % A bit of stretch before each entry for the benefit of balancing
+    % columns.
+    \vskip 0pt plus1pt
+    %
+    % Swallow the left brace of the text (first parameter):
+    \afterassignment\doentry
+    \let\temp =
+}
+\def\doentry{%
+    \bgroup % Instead of the swallowed brace.
+      \noindent
+      \aftergroup\finishentry
+      % And now comes the text of the entry.
+}
+\def\finishentry#1{%
+    % #1 is the page number.
+    %
+    % The following is kludged to not output a line of dots in the index if
+    % there are no page numbers.  The next person who breaks this will be
+    % cursed by a Unix daemon.
+    \def\tempa{{\rm }}%
+    \def\tempb{#1}%
+    \edef\tempc{\tempa}%
+    \edef\tempd{\tempb}%
+    \ifx\tempc\tempd
+      \ %
+    \else
+      %
+      % If we must, put the page number on a line of its own, and fill out
+      % this line with blank space.  (The \hfil is overwhelmed with the
+      % fill leaders glue in \indexdotfill if the page number does fit.)
+      \hfil\penalty50
+      \null\nobreak\indexdotfill % Have leaders before the page number.
+      %
+      % The `\ ' here is removed by the implicit \unskip that TeX does as
+      % part of (the primitive) \par.  Without it, a spurious underfull
+      % \hbox ensues.
+      \ifpdf
+       \pdfgettoks#1.%
+       \ \the\toksA
+      \else
+       \ #1%
+      \fi
+    \fi
+    \par
+  \endgroup
+}
 
 % Like \dotfill except takes at least 1 em.
 \def\indexdotfill{\cleaders
@@ -2231,41 +3810,65 @@ July\or August\or September\or October\or November\or December\fi
 \def\primary #1{\line{#1\hfil}}
 
 \newskip\secondaryindent \secondaryindent=0.5cm
-
-\def\secondary #1#2{
-{\parfillskip=0in \parskip=0in
-\hangindent =1in \hangafter=1
-\noindent\hskip\secondaryindent\hbox{#1}\indexdotfill #2\par
+\def\secondary#1#2{{%
+  \parfillskip=0in
+  \parskip=0in
+  \hangindent=1in
+  \hangafter=1
+  \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill
+  \ifpdf
+    \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph.
+  \else
+    #2
+  \fi
+  \par
 }}
 
-%% Define two-column mode, which is used in indexes.
-%% Adapted from the TeXbook, page 416.
-\catcode `\@=11
+% Define two-column mode, which we use to typeset indexes.
+% Adapted from the TeXbook, page 416, which is to say,
+% the manmac.tex format used to print the TeXbook itself.
+\catcode`\@=11
 
 \newbox\partialpage
-
 \newdimen\doublecolumnhsize
 
-\def\begindoublecolumns{\begingroup
+\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns
   % Grab any single-column material above us.
-  \output = {\global\setbox\partialpage
-    =\vbox{\unvbox255\kern -\topskip \kern \baselineskip}}%
-  \eject
+  \output = {%
+    %
+    % Here is a possibility not foreseen in manmac: if we accumulate a
+    % whole lot of material, we might end up calling this \output
+    % routine twice in a row (see the doublecol-lose test, which is
+    % essentially a couple of indexes with @setchapternewpage off).  In
+    % that case we just ship out what is in \partialpage with the normal
+    % output routine.  Generally, \partialpage will be empty when this
+    % runs and this will be a no-op.  See the indexspread.tex test case.
+    \ifvoid\partialpage \else
+      \onepageout{\pagecontents\partialpage}%
+    \fi
+    %
+    \global\setbox\partialpage = \vbox{%
+      % Unvbox the main output page.
+      \unvbox\PAGE
+      \kern-\topskip \kern\baselineskip
+    }%
+  }%
+  \eject % run that output routine to set \partialpage
   %
-  % Now switch to the double-column output routine.
-  \output={\doublecolumnout}%
+  % Use the double-column output routine for subsequent pages.
+  \output = {\doublecolumnout}%
   %
   % Change the page size parameters.  We could do this once outside this
   % routine, in each of @smallbook, @afourpaper, and the default 8.5x11
   % format, but then we repeat the same computation.  Repeating a couple
   % of assignments once per index is clearly meaningless for the
-  % execution time, so we may as well do it once.
+  % execution time, so we may as well do it in one place.
   %
   % First we halve the line length, less a little for the gutter between
   % the columns.  We compute the gutter based on the line length, so it
   % changes automatically with the paper format.  The magic constant
-  % below is chosen so that the gutter has the same value (well, +- <
-  % 1pt) as it did when we hard-coded it.
+  % below is chosen so that the gutter has the same value (well, +-<1pt)
+  % as it did when we hard-coded it.
   %
   % We put the result in a separate register, \doublecolumhsize, so we
   % can restore it in \pagesofar, after \hsize itself has (potentially)
@@ -2279,111 +3882,156 @@ July\or August\or September\or October\or November\or December\fi
   % Double the \vsize as well.  (We don't need a separate register here,
   % since nobody clobbers \vsize.)
   \vsize = 2\vsize
-  \doublecolumnpagegoal
 }
 
-\def\enddoublecolumns{\eject \endgroup \pagegoal=\vsize \unvbox\partialpage}
-
-\def\doublecolumnsplit{\splittopskip=\topskip \splitmaxdepth=\maxdepth
-  \global\dimen@=\pageheight \global\advance\dimen@ by-\ht\partialpage
-  \global\setbox1=\vsplit255 to\dimen@ \global\setbox0=\vbox{\unvbox1}
-  \global\setbox3=\vsplit255 to\dimen@ \global\setbox2=\vbox{\unvbox3}
-  \ifdim\ht0>\dimen@ \setbox255=\vbox{\unvbox0\unvbox2} \global\setbox255=\copy5 \fi
-  \ifdim\ht2>\dimen@ \setbox255=\vbox{\unvbox0\unvbox2} \global\setbox255=\copy5 \fi
+% The double-column output routine for all double-column pages except
+% the last.
+%
+\def\doublecolumnout{%
+  \splittopskip=\topskip \splitmaxdepth=\maxdepth
+  % Get the available space for the double columns -- the normal
+  % (undoubled) page height minus any material left over from the
+  % previous page.
+  \dimen@ = \vsize
+  \divide\dimen@ by 2
+  \advance\dimen@ by -\ht\partialpage
+  %
+  % box0 will be the left-hand column, box2 the right.
+  \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@
+  \onepageout\pagesofar
+  \unvbox255
+  \penalty\outputpenalty
 }
-\def\doublecolumnpagegoal{%
-  \dimen@=\vsize \advance\dimen@ by-2\ht\partialpage \global\pagegoal=\dimen@
+%
+% Re-output the contents of the output page -- any previous material,
+% followed by the two boxes we just split, in box0 and box2.
+\def\pagesofar{%
+  \unvbox\partialpage
+  %
+  \hsize = \doublecolumnhsize
+  \wd0=\hsize \wd2=\hsize
+  \hbox to\pagewidth{\box0\hfil\box2}%
 }
-\def\pagesofar{\unvbox\partialpage %
-  \hsize=\doublecolumnhsize % have to restore this since output routine
-  \wd0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}}
-\def\doublecolumnout{%
-  \setbox5=\copy255
-  {\vbadness=10000 \doublecolumnsplit}
-  \ifvbox255
-    \setbox0=\vtop to\dimen@{\unvbox0}
-    \setbox2=\vtop to\dimen@{\unvbox2}
-    \onepageout\pagesofar \unvbox255 \penalty\outputpenalty
-  \else
-    \setbox0=\vbox{\unvbox5}
-    \ifvbox0
-      \dimen@=\ht0 \advance\dimen@ by\topskip \advance\dimen@ by-\baselineskip
-      \divide\dimen@ by2 \splittopskip=\topskip \splitmaxdepth=\maxdepth
-      {\vbadness=10000
-       \loop \global\setbox5=\copy0
-          \setbox1=\vsplit5 to\dimen@
-          \setbox3=\vsplit5 to\dimen@
-          \ifvbox5 \global\advance\dimen@ by1pt \repeat
-        \setbox0=\vbox to\dimen@{\unvbox1}
-        \setbox2=\vbox to\dimen@{\unvbox3}
-        \global\setbox\partialpage=\vbox{\pagesofar}
-        \doublecolumnpagegoal
-      }
-    \fi
-  \fi
+%
+% All done with double columns.
+\def\enddoublecolumns{%
+  \output = {%
+    % Split the last of the double-column material.  Leave it on the
+    % current page, no automatic page break.
+    \balancecolumns
+    %
+    % If we end up splitting too much material for the current page,
+    % though, there will be another page break right after this \output
+    % invocation ends.  Having called \balancecolumns once, we do not
+    % want to call it again.  Therefore, reset \output to its normal
+    % definition right away.  (We hope \balancecolumns will never be
+    % called on to balance too much material, but if it is, this makes
+    % the output somewhat more palatable.)
+    \global\output = {\onepageout{\pagecontents\PAGE}}%
+  }%
+  \eject
+  \endgroup % started in \begindoublecolumns
+  %
+  % \pagegoal was set to the doubled \vsize above, since we restarted
+  % the current page.  We're now back to normal single-column
+  % typesetting, so reset \pagegoal to the normal \vsize (after the
+  % \endgroup where \vsize got restored).
+  \pagegoal = \vsize
 }
+%
+% Called at the end of the double column material.
+\def\balancecolumns{%
+  \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120.
+  \dimen@ = \ht0
+  \advance\dimen@ by \topskip
+  \advance\dimen@ by-\baselineskip
+  \divide\dimen@ by 2 % target to split to
+  %debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}%
+  \splittopskip = \topskip
+  % Loop until we get a decent breakpoint.
+  {%
+    \vbadness = 10000
+    \loop
+      \global\setbox3 = \copy0
+      \global\setbox1 = \vsplit3 to \dimen@
+    \ifdim\ht3>\dimen@
+      \global\advance\dimen@ by 1pt
+    \repeat
+  }%
+  %debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}%
+  \setbox0=\vbox to\dimen@{\unvbox1}%
+  \setbox2=\vbox to\dimen@{\unvbox3}%
+  %
+  \pagesofar
+}
+\catcode`\@ = \other
 
-\catcode `\@=\other
-\message{sectioning,}
-% Define chapters, sections, etc.
 
-\newcount \chapno
-\newcount \secno        \secno=0
-\newcount \subsecno     \subsecno=0
-\newcount \subsubsecno  \subsubsecno=0
+\message{sectioning,}
+% Chapters, sections, etc.
+
+% \unnumberedno is an oxymoron, of course.  But we count the unnumbered
+% sections so that we can refer to them unambiguously in the pdf
+% outlines by their "section number".  We avoid collisions with chapter
+% numbers by starting them at 10000.  (If a document ever has 10000
+% chapters, we're in trouble anyway, I'm sure.)
+\newcount\unnumberedno \unnumberedno = 10000
+\newcount\chapno
+\newcount\secno        \secno=0
+\newcount\subsecno     \subsecno=0
+\newcount\subsubsecno  \subsubsecno=0
 
 % This counter is funny since it counts through charcodes of letters A, B, ...
-\newcount \appendixno  \appendixno = `\@
-\def\appendixletter{\char\the\appendixno}
-
-\newwrite \contentsfile
-% This is called from \setfilename.
-\def\opencontents{\openout \contentsfile = \jobname.toc}
+\newcount\appendixno  \appendixno = `\@
+%
+% \def\appendixletter{\char\the\appendixno}
+% We do the following ugly conditional instead of the above simple
+% construct for the sake of pdftex, which needs the actual
+% letter in the expansion, not just typeset.
+%
+\def\appendixletter{%
+  \ifnum\appendixno=`A A%
+  \else\ifnum\appendixno=`B B%
+  \else\ifnum\appendixno=`C C%
+  \else\ifnum\appendixno=`D D%
+  \else\ifnum\appendixno=`E E%
+  \else\ifnum\appendixno=`F F%
+  \else\ifnum\appendixno=`G G%
+  \else\ifnum\appendixno=`H H%
+  \else\ifnum\appendixno=`I I%
+  \else\ifnum\appendixno=`J J%
+  \else\ifnum\appendixno=`K K%
+  \else\ifnum\appendixno=`L L%
+  \else\ifnum\appendixno=`M M%
+  \else\ifnum\appendixno=`N N%
+  \else\ifnum\appendixno=`O O%
+  \else\ifnum\appendixno=`P P%
+  \else\ifnum\appendixno=`Q Q%
+  \else\ifnum\appendixno=`R R%
+  \else\ifnum\appendixno=`S S%
+  \else\ifnum\appendixno=`T T%
+  \else\ifnum\appendixno=`U U%
+  \else\ifnum\appendixno=`V V%
+  \else\ifnum\appendixno=`W W%
+  \else\ifnum\appendixno=`X X%
+  \else\ifnum\appendixno=`Y Y%
+  \else\ifnum\appendixno=`Z Z%
+  % The \the is necessary, despite appearances, because \appendixletter is
+  % expanded while writing the .toc file.  \char\appendixno is not
+  % expandable, thus it is written literally, thus all appendixes come out
+  % with the same letter (or @) in the toc without it.
+  \else\char\the\appendixno
+  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
 
 % Each @chapter defines this as the name of the chapter.
-% page headings and footings can use it.  @section does likewise
-
-\def\thischapter{} \def\thissection{}
-\def\seccheck#1{\if \pageno<0 %
-\errmessage{@#1 not allowed after generating table of contents}\fi
-%
-}
-
-\def\chapternofonts{%
-\let\rawbackslash=\relax%
-\let\frenchspacing=\relax%
-\def\result{\realbackslash result}
-\def\equiv{\realbackslash equiv}
-\def\expansion{\realbackslash expansion}
-\def\print{\realbackslash print}
-\def\TeX{\realbackslash TeX}
-\def\dots{\realbackslash dots}
-\def\copyright{\realbackslash copyright}
-\def\tt{\realbackslash tt}
-\def\bf{\realbackslash bf}
-\def\w{\realbackslash w}
-\def\less{\realbackslash less}
-\def\gtr{\realbackslash gtr}
-\def\hat{\realbackslash hat}
-\def\char{\realbackslash char}
-\def\tclose##1{\realbackslash tclose {##1}}
-\def\code##1{\realbackslash code {##1}}
-\def\samp##1{\realbackslash samp {##1}}
-\def\r##1{\realbackslash r {##1}}
-\def\b##1{\realbackslash b {##1}}
-\def\key##1{\realbackslash key {##1}}
-\def\file##1{\realbackslash file {##1}}
-\def\kbd##1{\realbackslash kbd {##1}}
-% These are redefined because @smartitalic wouldn't work inside xdef.
-\def\i##1{\realbackslash i {##1}}
-\def\cite##1{\realbackslash cite {##1}}
-\def\var##1{\realbackslash var {##1}}
-\def\emph##1{\realbackslash emph {##1}}
-\def\dfn##1{\realbackslash dfn {##1}}
-}
+% page headings and footings can use it.  @section does likewise.
+% However, they are not reliable, because we don't use marks.
+\def\thischapter{}
+\def\thissection{}
 
 \newcount\absseclevel % used to calculate proper heading level
-\newcount\secbase\secbase=0 % @raise/lowersections modify this count
+\newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count
 
 % @raisesections: treat @section as chapter, @subsection as section, etc.
 \def\raisesections{\global\advance\secbase by -1}
@@ -2393,319 +4041,279 @@ July\or August\or September\or October\or November\or December\fi
 \def\lowersections{\global\advance\secbase by 1}
 \let\down=\lowersections % original BFox name
 
-% Choose a numbered-heading macro
-% #1 is heading level if unmodified by @raisesections or @lowersections
-% #2 is text for heading
-\def\numhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1
-\ifcase\absseclevel
-  \chapterzzz{#2}
-\or
-  \seczzz{#2}
-\or
-  \numberedsubseczzz{#2}
-\or
-  \numberedsubsubseczzz{#2}
-\else
-  \ifnum \absseclevel<0
-    \chapterzzz{#2}
+% we only have subsub.
+\chardef\maxseclevel = 3
+%
+% A numbered section within an unnumbered changes to unnumbered too.
+% To achive this, remember the "biggest" unnum. sec. we are currently in:
+\chardef\unmlevel = \maxseclevel
+%
+% Trace whether the current chapter is an appendix or not:
+% \chapheadtype is "N" or "A", unnumbered chapters are ignored.
+\def\chapheadtype{N}
+
+% Choose a heading macro
+% #1 is heading type
+% #2 is heading level
+% #3 is text for heading
+\def\genhead#1#2#3{%
+  % Compute the abs. sec. level:
+  \absseclevel=#2
+  \advance\absseclevel by \secbase
+  % Make sure \absseclevel doesn't fall outside the range:
+  \ifnum \absseclevel < 0
+    \absseclevel = 0
   \else
-    \numberedsubsubseczzz{#2}
+    \ifnum \absseclevel > 3
+      \absseclevel = 3
+    \fi
   \fi
-\fi
-}
-
-% like \numhead, but chooses appendix heading levels
-\def\apphead#1#2{\absseclevel=\secbase\advance\absseclevel by #1
-\ifcase\absseclevel
-  \appendixzzz{#2}
-\or
-  \appendixsectionzzz{#2}
-\or
-  \appendixsubseczzz{#2}
-\or
-  \appendixsubsubseczzz{#2}
-\else
-  \ifnum \absseclevel<0
-    \appendixzzz{#2}
+  % The heading type:
+  \def\headtype{#1}%
+  \if \headtype U%
+    \ifnum \absseclevel < \unmlevel
+      \chardef\unmlevel = \absseclevel
+    \fi
   \else
-    \appendixsubsubseczzz{#2}
+    % Check for appendix sections:
+    \ifnum \absseclevel = 0
+      \edef\chapheadtype{\headtype}%
+    \else
+      \if \headtype A\if \chapheadtype N%
+       \errmessage{@appendix... within a non-appendix chapter}%
+      \fi\fi
+    \fi
+    % Check for numbered within unnumbered:
+    \ifnum \absseclevel > \unmlevel
+      \def\headtype{U}%
+    \else
+      \chardef\unmlevel = 3
+    \fi
   \fi
-\fi
-}
-
-% like \numhead, but chooses numberless heading levels
-\def\unnmhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1
-\ifcase\absseclevel
-  \unnumberedzzz{#2}
-\or
-  \unnumberedseczzz{#2}
-\or
-  \unnumberedsubseczzz{#2}
-\or
-  \unnumberedsubsubseczzz{#2}
-\else
-  \ifnum \absseclevel<0
-    \unnumberedzzz{#2}
+  % Now print the heading:
+  \if \headtype U%
+    \ifcase\absseclevel
+       \unnumberedzzz{#3}%
+    \or \unnumberedseczzz{#3}%
+    \or \unnumberedsubseczzz{#3}%
+    \or \unnumberedsubsubseczzz{#3}%
+    \fi
   \else
-    \unnumberedsubsubseczzz{#2}
+    \if \headtype A%
+      \ifcase\absseclevel
+         \appendixzzz{#3}%
+      \or \appendixsectionzzz{#3}%
+      \or \appendixsubseczzz{#3}%
+      \or \appendixsubsubseczzz{#3}%
+      \fi
+    \else
+      \ifcase\absseclevel
+         \chapterzzz{#3}%
+      \or \seczzz{#3}%
+      \or \numberedsubseczzz{#3}%
+      \or \numberedsubsubseczzz{#3}%
+      \fi
+    \fi
   \fi
-\fi
+  \suppressfirstparagraphindent
 }
 
+% an interface:
+\def\numhead{\genhead N}
+\def\apphead{\genhead A}
+\def\unnmhead{\genhead U}
 
-\def\thischaptername{No Chapter Title}
-\outer\def\chapter{\parsearg\chapteryyy}
-\def\chapteryyy #1{\numhead0{#1}} % normally numhead0 calls chapterzzz
-\def\chapterzzz #1{\seccheck{chapter}%
-\secno=0 \subsecno=0 \subsubsecno=0
-\global\advance \chapno by 1 \message{Chapter \the\chapno}%
-\chapmacro {#1}{\the\chapno}%
-\gdef\thissection{#1}%
-\gdef\thischaptername{#1}%
-% We don't substitute the actual chapter name into \thischapter
-% because we don't want its macros evaluated now.
-\xdef\thischapter{\putwordChapter{} \the\chapno: \noexpand\thischaptername}%
-{\chapternofonts%
-\edef\temp{{\realbackslash chapentry {#1}{\the\chapno}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp  %
-\donoderef %
-\global\let\section = \numberedsec
-\global\let\subsection = \numberedsubsec
-\global\let\subsubsection = \numberedsubsubsec
-}}
-
-\outer\def\appendix{\parsearg\appendixyyy}
-\def\appendixyyy #1{\apphead0{#1}} % normally apphead0 calls appendixzzz
-\def\appendixzzz #1{\seccheck{appendix}%
-\secno=0 \subsecno=0 \subsubsecno=0
-\global\advance \appendixno by 1 \message{Appendix \appendixletter}%
-\chapmacro {#1}{\putwordAppendix{} \appendixletter}%
-\gdef\thissection{#1}%
-\gdef\thischaptername{#1}%
-\xdef\thischapter{\putwordAppendix{} \appendixletter: \noexpand\thischaptername}%
-{\chapternofonts%
-\edef\temp{{\realbackslash chapentry
-  {#1}{\putwordAppendix{} \appendixletter}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp  %
-\appendixnoderef %
-\global\let\section = \appendixsec
-\global\let\subsection = \appendixsubsec
-\global\let\subsubsection = \appendixsubsubsec
-}}
+% @chapter, @appendix, @unnumbered.  Increment top-level counter, reset
+% all lower-level sectioning counters to zero.
+%
+% Also set \chaplevelprefix, which we prepend to @float sequence numbers
+% (e.g., figures), q.v.  By default (before any chapter), that is empty.
+\let\chaplevelprefix = \empty
+%
+\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz
+\def\chapterzzz#1{%
+  % section resetting is \global in case the chapter is in a group, such
+  % as an @include file.
+  \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+    \global\advance\chapno by 1
+  %
+  % Used for \float.
+  \gdef\chaplevelprefix{\the\chapno.}%
+  \resetallfloatnos
+  %
+  \message{\putwordChapter\space \the\chapno}%
+  %
+  % Write the actual heading.
+  \chapmacro{#1}{Ynumbered}{\the\chapno}%
+  %
+  % So @section and the like are numbered underneath this chapter.
+  \global\let\section = \numberedsec
+  \global\let\subsection = \numberedsubsec
+  \global\let\subsubsection = \numberedsubsubsec
+}
 
-\outer\def\top{\parsearg\unnumberedyyy}
-\outer\def\unnumbered{\parsearg\unnumberedyyy}
-\def\unnumberedyyy #1{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz
-\def\unnumberedzzz #1{\seccheck{unnumbered}%
-\secno=0 \subsecno=0 \subsubsecno=0
-%
-% This used to be simply \message{#1}, but TeX fully expands the
-% argument to \message.  Therefore, if #1 contained @-commands, TeX
-% expanded them.  For example, in `@unnumbered The @cite{Book}', TeX
-% expanded @cite (which turns out to cause errors because \cite is meant
-% to be executed, not expanded).
-%
-% Anyway, we don't want the fully-expanded definition of @cite to appear
-% as a result of the \message, we just want `@cite' itself.  We use
-% \the<toks register> to achieve this: TeX expands \the<toks> only once,
-% simply yielding the contents of the <toks register>.
-\toks0 = {#1}\message{(\the\toks0)}%
-%
-\unnumbchapmacro {#1}%
-\gdef\thischapter{#1}\gdef\thissection{#1}%
-{\chapternofonts%
-\edef\temp{{\realbackslash unnumbchapentry {#1}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp  %
-\unnumbnoderef %
-\global\let\section = \unnumberedsec
-\global\let\subsection = \unnumberedsubsec
-\global\let\subsubsection = \unnumberedsubsubsec
-}}
+\outer\parseargdef\appendix{\apphead0{#1}} % normally apphead0 calls appendixzzz
+\def\appendixzzz#1{%
+  \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+    \global\advance\appendixno by 1
+  \gdef\chaplevelprefix{\appendixletter.}%
+  \resetallfloatnos
+  %
+  \def\appendixnum{\putwordAppendix\space \appendixletter}%
+  \message{\appendixnum}%
+  %
+  \chapmacro{#1}{Yappendix}{\appendixletter}%
+  %
+  \global\let\section = \appendixsec
+  \global\let\subsection = \appendixsubsec
+  \global\let\subsubsection = \appendixsubsubsec
+}
 
-\outer\def\numberedsec{\parsearg\secyyy}
-\def\secyyy #1{\numhead1{#1}} % normally calls seczzz
-\def\seczzz #1{\seccheck{section}%
-\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 %
-\gdef\thissection{#1}\secheading {#1}{\the\chapno}{\the\secno}%
-{\chapternofonts%
-\edef\temp{{\realbackslash secentry %
-{#1}{\the\chapno}{\the\secno}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\donoderef %
-\penalty 10000 %
-}}
+\outer\parseargdef\unnumbered{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz
+\def\unnumberedzzz#1{%
+  \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+    \global\advance\unnumberedno by 1
+  %
+  % Since an unnumbered has no number, no prefix for figures.
+  \global\let\chaplevelprefix = \empty
+  \resetallfloatnos
+  %
+  % This used to be simply \message{#1}, but TeX fully expands the
+  % argument to \message.  Therefore, if #1 contained @-commands, TeX
+  % expanded them.  For example, in `@unnumbered The @cite{Book}', TeX
+  % expanded @cite (which turns out to cause errors because \cite is meant
+  % to be executed, not expanded).
+  %
+  % Anyway, we don't want the fully-expanded definition of @cite to appear
+  % as a result of the \message, we just want `@cite' itself.  We use
+  % \the<toks register> to achieve this: TeX expands \the<toks> only once,
+  % simply yielding the contents of <toks register>.  (We also do this for
+  % the toc entries.)
+  \toks0 = {#1}%
+  \message{(\the\toks0)}%
+  %
+  \chapmacro{#1}{Ynothing}{\the\unnumberedno}%
+  %
+  \global\let\section = \unnumberedsec
+  \global\let\subsection = \unnumberedsubsec
+  \global\let\subsubsection = \unnumberedsubsubsec
+}
 
-\outer\def\appenixsection{\parsearg\appendixsecyyy}
-\outer\def\appendixsec{\parsearg\appendixsecyyy}
-\def\appendixsecyyy #1{\apphead1{#1}} % normally calls appendixsectionzzz
-\def\appendixsectionzzz #1{\seccheck{appendixsection}%
-\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 %
-\gdef\thissection{#1}\secheading {#1}{\appendixletter}{\the\secno}%
-{\chapternofonts%
-\edef\temp{{\realbackslash secentry %
-{#1}{\appendixletter}{\the\secno}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\appendixnoderef %
-\penalty 10000 %
-}}
+% @centerchap is like @unnumbered, but the heading is centered.
+\outer\parseargdef\centerchap{%
+  % Well, we could do the following in a group, but that would break
+  % an assumption that \chapmacro is called at the outermost level.
+  % Thus we are safer this way:                --kasal, 24feb04
+  \let\centerparametersmaybe = \centerparameters
+  \unnmhead0{#1}%
+  \let\centerparametersmaybe = \relax
+}
 
-\outer\def\unnumberedsec{\parsearg\unnumberedsecyyy}
-\def\unnumberedsecyyy #1{\unnmhead1{#1}} % normally calls unnumberedseczzz
-\def\unnumberedseczzz #1{\seccheck{unnumberedsec}%
-\plainsecheading {#1}\gdef\thissection{#1}%
-{\chapternofonts%
-\edef\temp{{\realbackslash unnumbsecentry{#1}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\unnumbnoderef %
-\penalty 10000 %
-}}
+% @top is like @unnumbered.
+\let\top\unnumbered
 
-\outer\def\numberedsubsec{\parsearg\numberedsubsecyyy}
-\def\numberedsubsecyyy #1{\numhead2{#1}} % normally calls numberedsubseczzz
-\def\numberedsubseczzz #1{\seccheck{subsection}%
-\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 %
-\subsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}%
-{\chapternofonts%
-\edef\temp{{\realbackslash subsecentry %
-{#1}{\the\chapno}{\the\secno}{\the\subsecno}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\donoderef %
-\penalty 10000 %
-}}
+% Sections.
+\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz
+\def\seczzz#1{%
+  \global\subsecno=0 \global\subsubsecno=0  \global\advance\secno by 1
+  \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}%
+}
 
-\outer\def\appendixsubsec{\parsearg\appendixsubsecyyy}
-\def\appendixsubsecyyy #1{\apphead2{#1}} % normally calls appendixsubseczzz
-\def\appendixsubseczzz #1{\seccheck{appendixsubsec}%
-\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 %
-\subsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}%
-{\chapternofonts%
-\edef\temp{{\realbackslash subsecentry %
-{#1}{\appendixletter}{\the\secno}{\the\subsecno}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\appendixnoderef %
-\penalty 10000 %
-}}
+\outer\parseargdef\appendixsection{\apphead1{#1}} % normally calls appendixsectionzzz
+\def\appendixsectionzzz#1{%
+  \global\subsecno=0 \global\subsubsecno=0  \global\advance\secno by 1
+  \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}%
+}
+\let\appendixsec\appendixsection
 
-\outer\def\unnumberedsubsec{\parsearg\unnumberedsubsecyyy}
-\def\unnumberedsubsecyyy #1{\unnmhead2{#1}} %normally calls unnumberedsubseczzz
-\def\unnumberedsubseczzz #1{\seccheck{unnumberedsubsec}%
-\plainsecheading {#1}\gdef\thissection{#1}%
-{\chapternofonts%
-\edef\temp{{\realbackslash unnumbsubsecentry{#1}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\unnumbnoderef %
-\penalty 10000 %
-}}
+\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} % normally calls unnumberedseczzz
+\def\unnumberedseczzz#1{%
+  \global\subsecno=0 \global\subsubsecno=0  \global\advance\secno by 1
+  \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}%
+}
 
-\outer\def\numberedsubsubsec{\parsearg\numberedsubsubsecyyy}
-\def\numberedsubsubsecyyy #1{\numhead3{#1}} % normally numberedsubsubseczzz
-\def\numberedsubsubseczzz #1{\seccheck{subsubsection}%
-\gdef\thissection{#1}\global\advance \subsubsecno by 1 %
-\subsubsecheading {#1}
-  {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}%
-{\chapternofonts%
-\edef\temp{{\realbackslash subsubsecentry %
-  {#1}
-  {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}
-  {\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\donoderef %
-\penalty 10000 %
-}}
+% Subsections.
+\outer\parseargdef\numberedsubsec{\numhead2{#1}} % normally calls numberedsubseczzz
+\def\numberedsubseczzz#1{%
+  \global\subsubsecno=0  \global\advance\subsecno by 1
+  \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}%
+}
 
-\outer\def\appendixsubsubsec{\parsearg\appendixsubsubsecyyy}
-\def\appendixsubsubsecyyy #1{\apphead3{#1}} % normally appendixsubsubseczzz
-\def\appendixsubsubseczzz #1{\seccheck{appendixsubsubsec}%
-\gdef\thissection{#1}\global\advance \subsubsecno by 1 %
-\subsubsecheading {#1}
-  {\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}%
-{\chapternofonts%
-\edef\temp{{\realbackslash subsubsecentry{#1}%
-  {\appendixletter}
-  {\the\secno}{\the\subsecno}{\the\subsubsecno}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\appendixnoderef %
-\penalty 10000 %
-}}
+\outer\parseargdef\appendixsubsec{\apphead2{#1}} % normally calls appendixsubseczzz
+\def\appendixsubseczzz#1{%
+  \global\subsubsecno=0  \global\advance\subsecno by 1
+  \sectionheading{#1}{subsec}{Yappendix}%
+                 {\appendixletter.\the\secno.\the\subsecno}%
+}
 
-\outer\def\unnumberedsubsubsec{\parsearg\unnumberedsubsubsecyyy}
-\def\unnumberedsubsubsecyyy #1{\unnmhead3{#1}} %normally unnumberedsubsubseczzz
-\def\unnumberedsubsubseczzz #1{\seccheck{unnumberedsubsubsec}%
-\plainsecheading {#1}\gdef\thissection{#1}%
-{\chapternofonts%
-\edef\temp{{\realbackslash unnumbsubsubsecentry{#1}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\unnumbnoderef %
-\penalty 10000 %
-}}
+\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} %normally calls unnumberedsubseczzz
+\def\unnumberedsubseczzz#1{%
+  \global\subsubsecno=0  \global\advance\subsecno by 1
+  \sectionheading{#1}{subsec}{Ynothing}%
+                 {\the\unnumberedno.\the\secno.\the\subsecno}%
+}
 
-% These are variants which are not "outer", so they can appear in @ifinfo.
-% Actually, they should now be obsolete; ordinary section commands should work.
-\def\infotop{\parsearg\unnumberedzzz}
-\def\infounnumbered{\parsearg\unnumberedzzz}
-\def\infounnumberedsec{\parsearg\unnumberedseczzz}
-\def\infounnumberedsubsec{\parsearg\unnumberedsubseczzz}
-\def\infounnumberedsubsubsec{\parsearg\unnumberedsubsubseczzz}
+% Subsubsections.
+\outer\parseargdef\numberedsubsubsec{\numhead3{#1}} % normally numberedsubsubseczzz
+\def\numberedsubsubseczzz#1{%
+  \global\advance\subsubsecno by 1
+  \sectionheading{#1}{subsubsec}{Ynumbered}%
+                 {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
 
-\def\infoappendix{\parsearg\appendixzzz}
-\def\infoappendixsec{\parsearg\appendixseczzz}
-\def\infoappendixsubsec{\parsearg\appendixsubseczzz}
-\def\infoappendixsubsubsec{\parsearg\appendixsubsubseczzz}
+\outer\parseargdef\appendixsubsubsec{\apphead3{#1}} % normally appendixsubsubseczzz
+\def\appendixsubsubseczzz#1{%
+  \global\advance\subsubsecno by 1
+  \sectionheading{#1}{subsubsec}{Yappendix}%
+                 {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
 
-\def\infochapter{\parsearg\chapterzzz}
-\def\infosection{\parsearg\sectionzzz}
-\def\infosubsection{\parsearg\subsectionzzz}
-\def\infosubsubsection{\parsearg\subsubsectionzzz}
+\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} %normally unnumberedsubsubseczzz
+\def\unnumberedsubsubseczzz#1{%
+  \global\advance\subsubsecno by 1
+  \sectionheading{#1}{subsubsec}{Ynothing}%
+                 {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
 
 % These macros control what the section commands do, according
 % to what kind of chapter we are in (ordinary, appendix, or unnumbered).
 % Define them by default for a numbered chapter.
-\global\let\section = \numberedsec
-\global\let\subsection = \numberedsubsec
-\global\let\subsubsection = \numberedsubsubsec
+\let\section = \numberedsec
+\let\subsection = \numberedsubsec
+\let\subsubsection = \numberedsubsubsec
 
 % Define @majorheading, @heading and @subheading
 
-% NOTE on use of \vbox for chapter headings, section headings, and
-% such:
-%      1) We use \vbox rather than the earlier \line to permit
-%         overlong headings to fold.
-%      2) \hyphenpenalty is set to 10000 because hyphenation in a
-%         heading is obnoxious; this forbids it.
+% NOTE on use of \vbox for chapter headings, section headings, and such:
+%       1) We use \vbox rather than the earlier \line to permit
+%          overlong headings to fold.
+%       2) \hyphenpenalty is set to 10000 because hyphenation in a
+%          heading is obnoxious; this forbids it.
 %       3) Likewise, headings look best if no \parindent is used, and
 %          if justification is not attempted.  Hence \raggedright.
 
 
-\def\majorheading{\parsearg\majorheadingzzz}
-\def\majorheadingzzz #1{%
-{\advance\chapheadingskip by 10pt \chapbreak }%
-{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
-                  \parindent=0pt\raggedright
-                  \rm #1\hfill}}\bigskip \par\penalty 200}
-
-\def\chapheading{\parsearg\chapheadingzzz}
-\def\chapheadingzzz #1{\chapbreak %
-{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
-                  \parindent=0pt\raggedright
-                  \rm #1\hfill}}\bigskip \par\penalty 200}
-
-\def\heading{\parsearg\secheadingi}
+\def\majorheading{%
+  {\advance\chapheadingskip by 10pt \chapbreak }%
+  \parsearg\chapheadingzzz
+}
 
-\def\subheading{\parsearg\subsecheadingi}
+\def\chapheading{\chapbreak \parsearg\chapheadingzzz}
+\def\chapheadingzzz#1{%
+  {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+                    \parindent=0pt\raggedright
+                    \rm #1\hfill}}%
+  \bigskip \par\penalty 200\relax
+  \suppressfirstparagraphindent
+}
 
-\def\subsubheading{\parsearg\subsubsecheadingi}
+% @heading, @subheading, @subsubheading.
+\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{}
+  \suppressfirstparagraphindent}
+\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{}
+  \suppressfirstparagraphindent}
+\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{}
+  \suppressfirstparagraphindent}
 
 % These macros generate a chapter, section, etc. heading only
 % (including whitespace, linebreaking, etc. around it),
@@ -2714,12 +4322,10 @@ July\or August\or September\or October\or November\or December\fi
 %%% Args are the skip and penalty (usually negative)
 \def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi}
 
-\def\setchapterstyle #1 {\csname CHAPF#1\endcsname}
-
 %%% Define plain chapter starts, and page on/off switching for it
 % Parameter controlling skip before chapter headings (if needed)
 
-\newskip \chapheadingskip \chapheadingskip = 30pt plus 8pt minus 4pt
+\newskip\chapheadingskip
 
 \def\chapbreak{\dobreak \chapheadingskip {-4000}}
 \def\chappager{\par\vfill\supereject}
@@ -2727,253 +4333,474 @@ July\or August\or September\or October\or November\or December\fi
 
 \def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname}
 
-\def\CHAPPAGoff{
+\def\CHAPPAGoff{%
+\global\let\contentsalignmacro = \chappager
 \global\let\pchapsepmacro=\chapbreak
 \global\let\pagealignmacro=\chappager}
 
-\def\CHAPPAGon{
+\def\CHAPPAGon{%
+\global\let\contentsalignmacro = \chappager
 \global\let\pchapsepmacro=\chappager
 \global\let\pagealignmacro=\chappager
 \global\def\HEADINGSon{\HEADINGSsingle}}
 
-\def\CHAPPAGodd{
+\def\CHAPPAGodd{%
+\global\let\contentsalignmacro = \chapoddpage
 \global\let\pchapsepmacro=\chapoddpage
 \global\let\pagealignmacro=\chapoddpage
 \global\def\HEADINGSon{\HEADINGSdouble}}
 
 \CHAPPAGon
 
-\def\CHAPFplain{
-\global\let\chapmacro=\chfplain
-\global\let\unnumbchapmacro=\unnchfplain}
-
-\def\chfplain #1#2{%
+% Chapter opening.
+%
+% #1 is the text, #2 is the section type (Ynumbered, Ynothing,
+% Yappendix, Yomitfromtoc), #3 the chapter number.
+%
+% To test against our argument.
+\def\Ynothingkeyword{Ynothing}
+\def\Yomitfromtockeyword{Yomitfromtoc}
+\def\Yappendixkeyword{Yappendix}
+%
+\def\chapmacro#1#2#3{%
   \pchapsepmacro
   {%
-    \chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
-                     \parindent=0pt\raggedright
-                     \rm #2\enspace #1}%
+    \chapfonts \rm
+    %
+    % Have to define \thissection before calling \donoderef, because the
+    % xref code eventually uses it.  On the other hand, it has to be called
+    % after \pchapsepmacro, or the headline will change too soon.
+    \gdef\thissection{#1}%
+    \gdef\thischaptername{#1}%
+    %
+    % Only insert the separating space if we have a chapter/appendix
+    % number, and don't print the unnumbered ``number''.
+    \def\temptype{#2}%
+    \ifx\temptype\Ynothingkeyword
+      \setbox0 = \hbox{}%
+      \def\toctype{unnchap}%
+      \gdef\thischapter{#1}%
+    \else\ifx\temptype\Yomitfromtockeyword
+      \setbox0 = \hbox{}% contents like unnumbered, but no toc entry
+      \def\toctype{omit}%
+      \gdef\thischapter{}%
+    \else\ifx\temptype\Yappendixkeyword
+      \setbox0 = \hbox{\putwordAppendix{} #3\enspace}%
+      \def\toctype{app}%
+      % We don't substitute the actual chapter name into \thischapter
+      % because we don't want its macros evaluated now.  And we don't
+      % use \thissection because that changes with each section.
+      %
+      \xdef\thischapter{\putwordAppendix{} \appendixletter:
+                        \noexpand\thischaptername}%
+    \else
+      \setbox0 = \hbox{#3\enspace}%
+      \def\toctype{numchap}%
+      \xdef\thischapter{\putwordChapter{} \the\chapno:
+                        \noexpand\thischaptername}%
+    \fi\fi\fi
+    %
+    % Write the toc entry for this chapter.  Must come before the
+    % \donoderef, because we include the current node name in the toc
+    % entry, and \donoderef resets it to empty.
+    \writetocentry{\toctype}{#1}{#3}%
+    %
+    % For pdftex, we have to write out the node definition (aka, make
+    % the pdfdest) after any page break, but before the actual text has
+    % been typeset.  If the destination for the pdf outline is after the
+    % text, then jumping from the outline may wind up with the text not
+    % being visible, for instance under high magnification.
+    \donoderef{#2}%
+    %
+    % Typeset the actual heading.
+    \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright
+          \hangindent=\wd0 \centerparametersmaybe
+          \unhbox0 #1\par}%
   }%
-  \bigskip
-  \penalty5000
+  \nobreak\bigskip % no page break after a chapter title
+  \nobreak
 }
 
-\def\unnchfplain #1{%
-\pchapsepmacro %
-{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
-                  \parindent=0pt\raggedright
-                  \rm #1\hfill}}\bigskip \par\penalty 10000 %
+% @centerchap -- centered and unnumbered.
+\let\centerparametersmaybe = \relax
+\def\centerparameters{%
+  \advance\rightskip by 3\rightskip
+  \leftskip = \rightskip
+  \parfillskip = 0pt
 }
-\CHAPFplain % The default
 
+
+% I don't think this chapter style is supported any more, so I'm not
+% updating it with the new noderef stuff.  We'll see.  --karl, 11aug03.
+%
+\def\setchapterstyle #1 {\csname CHAPF#1\endcsname}
+%
 \def\unnchfopen #1{%
 \chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
                        \parindent=0pt\raggedright
-                       \rm #1\hfill}}\bigskip \par\penalty 10000 %
+                       \rm #1\hfill}}\bigskip \par\nobreak
 }
-
 \def\chfopen #1#2{\chapoddpage {\chapfonts
 \vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}%
 \par\penalty 5000 %
 }
+\def\centerchfopen #1{%
+\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+                       \parindent=0pt
+                       \hfill {\rm #1}\hfill}}\bigskip \par\nobreak
+}
+\def\CHAPFopen{%
+  \global\let\chapmacro=\chfopen
+  \global\let\centerchapmacro=\centerchfopen}
 
-\def\CHAPFopen{
-\global\let\chapmacro=\chfopen
-\global\let\unnumbchapmacro=\unnchfopen}
-
-% Parameter controlling skip before section headings.
 
-\newskip \subsecheadingskip  \subsecheadingskip = 17pt plus 8pt minus 4pt
-\def\subsecheadingbreak{\dobreak \subsecheadingskip {-500}}
+% Section titles.  These macros combine the section number parts and
+% call the generic \sectionheading to do the printing.
+%
+\newskip\secheadingskip
+\def\secheadingbreak{\dobreak \secheadingskip{-1000}}
 
-\newskip \secheadingskip  \secheadingskip = 21pt plus 8pt minus 4pt
-\def\secheadingbreak{\dobreak \secheadingskip {-1000}}
+% Subsection titles.
+\newskip\subsecheadingskip
+\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}}
 
-% @paragraphindent  is defined for the Info formatting commands only.
-\let\paragraphindent=\comment
+% Subsubsection titles.
+\def\subsubsecheadingskip{\subsecheadingskip}
+\def\subsubsecheadingbreak{\subsecheadingbreak}
 
-% Section fonts are the base font at magstep2, which produces
-% a size a bit more than 14 points in the default situation.
 
-\def\secheading #1#2#3{\secheadingi {#2.#3\enspace #1}}
-\def\plainsecheading #1{\secheadingi {#1}}
-\def\secheadingi #1{{\advance \secheadingskip by \parskip %
-\secheadingbreak}%
-{\secfonts \vbox{\hyphenpenalty=10000\tolerance=5000
-                 \parindent=0pt\raggedright
-                 \rm #1\hfill}}%
-\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 }
+% Print any size, any type, section title.
+%
+% #1 is the text, #2 is the section level (sec/subsec/subsubsec), #3 is
+% the section type for xrefs (Ynumbered, Ynothing, Yappendix), #4 is the
+% section number.
+%
+\def\sectionheading#1#2#3#4{%
+  {%
+    % Switch to the right set of fonts.
+    \csname #2fonts\endcsname \rm
+    %
+    % Insert space above the heading.
+    \csname #2headingbreak\endcsname
+    %
+    % Only insert the space after the number if we have a section number.
+    \def\sectionlevel{#2}%
+    \def\temptype{#3}%
+    %
+    \ifx\temptype\Ynothingkeyword
+      \setbox0 = \hbox{}%
+      \def\toctype{unn}%
+      \gdef\thissection{#1}%
+    \else\ifx\temptype\Yomitfromtockeyword
+      % for @headings -- no section number, don't include in toc,
+      % and don't redefine \thissection.
+      \setbox0 = \hbox{}%
+      \def\toctype{omit}%
+      \let\sectionlevel=\empty
+    \else\ifx\temptype\Yappendixkeyword
+      \setbox0 = \hbox{#4\enspace}%
+      \def\toctype{app}%
+      \gdef\thissection{#1}%
+    \else
+      \setbox0 = \hbox{#4\enspace}%
+      \def\toctype{num}%
+      \gdef\thissection{#1}%
+    \fi\fi\fi
+    %
+    % Write the toc entry (before \donoderef).  See comments in \chapmacro.
+    \writetocentry{\toctype\sectionlevel}{#1}{#4}%
+    %
+    % Write the node reference (= pdf destination for pdftex).
+    % Again, see comments in \chapmacro.
+    \donoderef{#3}%
+    %
+    % Interline glue will be inserted when the vbox is completed.
+    % That glue will be a valid breakpoint for the page, since it'll be
+    % preceded by a whatsit (usually from the \donoderef, or from the
+    % \writetocentry if there was no node).  We don't want to allow that
+    % break, since then the whatsits could end up on page n while the
+    % section is on page n+1, thus toc/etc. are wrong.  Debian bug 276000.
+    \nobreak
+    %
+    % Output the actual section heading.
+    \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright
+          \hangindent=\wd0  % zero if no section number
+          \unhbox0 #1}%
+  }%
+  % Add extra space after the heading -- half of whatever came above it.
+  % Don't allow stretch, though.
+  \kern .5 \csname #2headingskip\endcsname
+  %
+  % Do not let the kern be a potential breakpoint, as it would be if it
+  % was followed by glue.
+  \nobreak
+  %
+  % We'll almost certainly start a paragraph next, so don't let that
+  % glue accumulate.  (Not a breakpoint because it's preceded by a
+  % discardable item.)
+  \vskip-\parskip
+  % 
+  % This is purely so the last item on the list is a known \penalty >
+  % 10000.  This is so \startdefun can avoid allowing breakpoints after
+  % section headings.  Otherwise, it would insert a valid breakpoint between:
+  % 
+  %   @section sec-whatever
+  %   @deffn def-whatever
+  \penalty 10001
+}
 
 
-% Subsection fonts are the base font at magstep1,
-% which produces a size of 12 points.
+\message{toc,}
+% Table of contents.
+\newwrite\tocfile
 
-\def\subsecheading #1#2#3#4{\subsecheadingi {#2.#3.#4\enspace #1}}
-\def\subsecheadingi #1{{\advance \subsecheadingskip by \parskip %
-\subsecheadingbreak}%
-{\subsecfonts \vbox{\hyphenpenalty=10000\tolerance=5000
-                     \parindent=0pt\raggedright
-                     \rm #1\hfill}}%
-\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 }
+% Write an entry to the toc file, opening it if necessary.
+% Called from @chapter, etc.
+%
+% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno}
+% We append the current node name (if any) and page number as additional
+% arguments for the \{chap,sec,...}entry macros which will eventually
+% read this.  The node name is used in the pdf outlines as the
+% destination to jump to.
+%
+% We open the .toc file for writing here instead of at @setfilename (or
+% any other fixed time) so that @contents can be anywhere in the document.
+% But if #1 is `omit', then we don't do anything.  This is used for the
+% table of contents chapter openings themselves.
+%
+\newif\iftocfileopened
+\def\omitkeyword{omit}%
+%
+\def\writetocentry#1#2#3{%
+  \edef\writetoctype{#1}%
+  \ifx\writetoctype\omitkeyword \else
+    \iftocfileopened\else
+      \immediate\openout\tocfile = \jobname.toc
+      \global\tocfileopenedtrue
+    \fi
+    %
+    \iflinks
+      {\atdummies
+       \edef\temp{%
+         \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}%
+       \temp
+      }%
+    \fi
+  \fi
+  %
+  % Tell \shipout to create a pdf destination on each page, if we're
+  % writing pdf.  These are used in the table of contents.  We can't
+  % just write one on every page because the title pages are numbered
+  % 1 and 2 (the page numbers aren't printed), and so are the first
+  % two pages of the document.  Thus, we'd have two destinations named
+  % `1', and two named `2'.
+  \ifpdf \global\pdfmakepagedesttrue \fi
+}
 
-\def\subsubsecfonts{\subsecfonts} % Maybe this should change:
-                                 % Perhaps make sssec fonts scaled
-                                 % magstep half
-\def\subsubsecheading #1#2#3#4#5{\subsubsecheadingi {#2.#3.#4.#5\enspace #1}}
-\def\subsubsecheadingi #1{{\advance \subsecheadingskip by \parskip %
-\subsecheadingbreak}%
-{\subsubsecfonts \vbox{\hyphenpenalty=10000\tolerance=5000
-                       \parindent=0pt\raggedright
-                       \rm #1\hfill}}%
-\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000}
 
+% These characters do not print properly in the Computer Modern roman
+% fonts, so we must take special care.  This is more or less redundant
+% with the Texinfo input format setup at the end of this file.
+% 
+\def\activecatcodes{%
+  \catcode`\"=\active
+  \catcode`\$=\active
+  \catcode`\<=\active
+  \catcode`\>=\active
+  \catcode`\\=\active
+  \catcode`\^=\active
+  \catcode`\_=\active
+  \catcode`\|=\active
+  \catcode`\~=\active
+}
 
-\message{toc printing,}
 
-% Finish up the main text and prepare to read what we've written
-% to \contentsfile.
+% Read the toc file, which is essentially Texinfo input.
+\def\readtocfile{%
+  \setupdatafile
+  \activecatcodes
+  \input \jobname.toc
+}
 
 \newskip\contentsrightmargin \contentsrightmargin=1in
+\newcount\savepageno
+\newcount\lastnegativepageno \lastnegativepageno = -1
+
+% Prepare to read what we've written to \tocfile.
+%
 \def\startcontents#1{%
-   \pagealignmacro
-   \immediate\closeout \contentsfile
-   \ifnum \pageno>0
-      \pageno = -1             % Request roman numbered pages.
-   \fi
-   % Don't need to put `Contents' or `Short Contents' in the headline.
-   % It is abundantly clear what they are.
-   \unnumbchapmacro{#1}\def\thischapter{}%
-   \begingroup                 % Set up to handle contents files properly.
-      \catcode`\\=0  \catcode`\{=1  \catcode`\}=2  \catcode`\@=11
-      \raggedbottom             % Worry more about breakpoints than the bottom.
-      \advance\hsize by -\contentsrightmargin % Don't use the full line length.
+  % If @setchapternewpage on, and @headings double, the contents should
+  % start on an odd page, unlike chapters.  Thus, we maintain
+  % \contentsalignmacro in parallel with \pagealignmacro.
+  % From: Torbjorn Granlund <tege@matematik.su.se>
+  \contentsalignmacro
+  \immediate\closeout\tocfile
+  %
+  % Don't need to put `Contents' or `Short Contents' in the headline.
+  % It is abundantly clear what they are.
+  \def\thischapter{}%
+  \chapmacro{#1}{Yomitfromtoc}{}%
+  %
+  \savepageno = \pageno
+  \begingroup                  % Set up to handle contents files properly.
+    \raggedbottom              % Worry more about breakpoints than the bottom.
+    \advance\hsize by -\contentsrightmargin % Don't use the full line length.
+    %
+    % Roman numerals for page numbers.
+    \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi
 }
 
 
 % Normal (long) toc.
-\outer\def\contents{%
-   \startcontents{\putwordTableofContents}%
-      \input \jobname.toc
-   \endgroup
-   \vfill \eject
+\def\contents{%
+  \startcontents{\putwordTOC}%
+    \openin 1 \jobname.toc
+    \ifeof 1 \else
+      \readtocfile
+    \fi
+    \vfill \eject
+    \contentsalignmacro % in case @setchapternewpage odd is in effect
+    \ifeof 1 \else
+      \pdfmakeoutlines
+    \fi
+    \closein 1
+  \endgroup
+  \lastnegativepageno = \pageno
+  \global\pageno = \savepageno
 }
 
 % And just the chapters.
-\outer\def\summarycontents{%
-   \startcontents{\putwordShortContents}%
-      %
-      \let\chapentry = \shortchapentry
-      \let\unnumbchapentry = \shortunnumberedentry
-      % We want a true roman here for the page numbers.
-      \secfonts
-      \let\rm=\shortcontrm \let\bf=\shortcontbf \let\sl=\shortcontsl
-      \rm
-      \advance\baselineskip by 1pt % Open it up a little.
-      \def\secentry ##1##2##3##4{}
-      \def\unnumbsecentry ##1##2{}
-      \def\subsecentry ##1##2##3##4##5{}
-      \def\unnumbsubsecentry ##1##2{}
-      \def\subsubsecentry ##1##2##3##4##5##6{}
-      \def\unnumbsubsubsecentry ##1##2{}
-      \input \jobname.toc
-   \endgroup
-   \vfill \eject
+\def\summarycontents{%
+  \startcontents{\putwordShortTOC}%
+    %
+    \let\numchapentry = \shortchapentry
+    \let\appentry = \shortchapentry
+    \let\unnchapentry = \shortunnchapentry
+    % We want a true roman here for the page numbers.
+    \secfonts
+    \let\rm=\shortcontrm \let\bf=\shortcontbf
+    \let\sl=\shortcontsl \let\tt=\shortconttt
+    \rm
+    \hyphenpenalty = 10000
+    \advance\baselineskip by 1pt % Open it up a little.
+    \def\numsecentry##1##2##3##4{}
+    \let\appsecentry = \numsecentry
+    \let\unnsecentry = \numsecentry
+    \let\numsubsecentry = \numsecentry
+    \let\appsubsecentry = \numsecentry
+    \let\unnsubsecentry = \numsecentry
+    \let\numsubsubsecentry = \numsecentry
+    \let\appsubsubsecentry = \numsecentry
+    \let\unnsubsubsecentry = \numsecentry
+    \openin 1 \jobname.toc
+    \ifeof 1 \else
+      \readtocfile
+    \fi
+    \closein 1
+    \vfill \eject
+    \contentsalignmacro % in case @setchapternewpage odd is in effect
+  \endgroup
+  \lastnegativepageno = \pageno
+  \global\pageno = \savepageno
 }
 \let\shortcontents = \summarycontents
 
+% Typeset the label for a chapter or appendix for the short contents.
+% The arg is, e.g., `A' for an appendix, or `3' for a chapter.
+%
+\def\shortchaplabel#1{%
+  % This space should be enough, since a single number is .5em, and the
+  % widest letter (M) is 1em, at least in the Computer Modern fonts.
+  % But use \hss just in case.
+  % (This space doesn't include the extra space that gets added after
+  % the label; that gets put in by \shortchapentry above.)
+  %
+  % We'd like to right-justify chapter numbers, but that looks strange
+  % with appendix letters.  And right-justifying numbers and
+  % left-justifying letters looks strange when there is less than 10
+  % chapters.  Have to read the whole toc once to know how many chapters
+  % there are before deciding ...
+  \hbox to 1em{#1\hss}%
+}
+
 % These macros generate individual entries in the table of contents.
 % The first argument is the chapter or section name.
 % The last argument is the page number.
 % The arguments in between are the chapter number, section number, ...
 
-% Chapter-level things, for both the long and short contents.
-\def\chapentry#1#2#3{\dochapentry{#2\labelspace#1}{#3}}
-
-% See comments in \dochapentry re vbox and related settings
-\def\shortchapentry#1#2#3{%
-  \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno{#3}}%
+% Chapters, in the main contents.
+\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}}
+%
+% Chapters, in the short toc.
+% See comments in \dochapentry re vbox and related settings.
+\def\shortchapentry#1#2#3#4{%
+  \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}%
 }
 
-% Typeset the label for a chapter or appendix for the short contents.
-% The arg is, e.g. `Appendix A' for an appendix, or `3' for a chapter.
-% We could simplify the code here by writing out an \appendixentry
-% command in the toc file for appendices, instead of using \chapentry
-% for both, but it doesn't seem worth it.
-\setbox0 = \hbox{\shortcontrm \putwordAppendix }
-\newdimen\shortappendixwidth \shortappendixwidth = \wd0
-
-\def\shortchaplabel#1{%
-  % We typeset #1 in a box of constant width, regardless of the text of
-  % #1, so the chapter titles will come out aligned.
-  \setbox0 = \hbox{#1}%
-  \dimen0 = \ifdim\wd0 > \shortappendixwidth \shortappendixwidth \else 0pt \fi
-  %
-  % This space should be plenty, since a single number is .5em, and the
-  % widest letter (M) is 1em, at least in the Computer Modern fonts.
-  % (This space doesn't include the extra space that gets added after
-  % the label; that gets put in in \shortchapentry above.)
-  \advance\dimen0 by 1.1em
-  \hbox to \dimen0{#1\hfil}%
-}
+% Appendices, in the main contents.
+% Need the word Appendix, and a fixed-size box.
+%
+\def\appendixbox#1{%
+  % We use M since it's probably the widest letter.
+  \setbox0 = \hbox{\putwordAppendix{} M}%
+  \hbox to \wd0{\putwordAppendix{} #1\hss}}
+%
+\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\labelspace#1}{#4}}
 
-\def\unnumbchapentry#1#2{\dochapentry{#1}{#2}}
-\def\shortunnumberedentry#1#2{\tocentry{#1}{\doshortpageno{#2}}}
+% Unnumbered chapters.
+\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}}
+\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}}
 
 % Sections.
-\def\secentry#1#2#3#4{\dosecentry{#2.#3\labelspace#1}{#4}}
-\def\unnumbsecentry#1#2{\dosecentry{#1}{#2}}
+\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}}
+\let\appsecentry=\numsecentry
+\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}}
 
 % Subsections.
-\def\subsecentry#1#2#3#4#5{\dosubsecentry{#2.#3.#4\labelspace#1}{#5}}
-\def\unnumbsubsecentry#1#2{\dosubsecentry{#1}{#2}}
+\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}}
+\let\appsubsecentry=\numsubsecentry
+\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}}
 
 % And subsubsections.
-\def\subsubsecentry#1#2#3#4#5#6{%
-  \dosubsubsecentry{#2.#3.#4.#5\labelspace#1}{#6}}
-\def\unnumbsubsubsecentry#1#2{\dosubsubsecentry{#1}{#2}}
-
+\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}}
+\let\appsubsubsecentry=\numsubsubsecentry
+\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}}
 
 % This parameter controls the indentation of the various levels.
-\newdimen\tocindent \tocindent = 3pc
+% Same as \defaultparindent.
+\newdimen\tocindent \tocindent = 15pt
 
 % Now for the actual typesetting. In all these, #1 is the text and #2 is the
 % page number.
 %
-% If the toc has to be broken over pages, we would want to be at chapters
+% If the toc has to be broken over pages, we want it to be at chapters
 % if at all possible; hence the \penalty.
 \def\dochapentry#1#2{%
-   \penalty-300 \vskip\baselineskip
+   \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip
    \begingroup
      \chapentryfonts
-     \tocentry{#1}{\dopageno{#2}}%
+     \tocentry{#1}{\dopageno\bgroup#2\egroup}%
    \endgroup
-   \nobreak\vskip .25\baselineskip
+   \nobreak\vskip .25\baselineskip plus.1\baselineskip
 }
 
 \def\dosecentry#1#2{\begingroup
   \secentryfonts \leftskip=\tocindent
-  \tocentry{#1}{\dopageno{#2}}%
+  \tocentry{#1}{\dopageno\bgroup#2\egroup}%
 \endgroup}
 
 \def\dosubsecentry#1#2{\begingroup
   \subsecentryfonts \leftskip=2\tocindent
-  \tocentry{#1}{\dopageno{#2}}%
+  \tocentry{#1}{\dopageno\bgroup#2\egroup}%
 \endgroup}
 
 \def\dosubsubsecentry#1#2{\begingroup
   \subsubsecentryfonts \leftskip=3\tocindent
-  \tocentry{#1}{\dopageno{#2}}%
+  \tocentry{#1}{\dopageno\bgroup#2\egroup}%
 \endgroup}
 
-% Final typesetting of a toc entry; we use the same \entry macro as for
-% the index entries, but we want to suppress hyphenation here.  (We
-% can't do that in the \entry macro, since index entries might consist
-% of hyphenated-identifiers-that-do-not-fit-on-a-line-and-nothing-else.)
-%
-\def\tocentry#1#2{\begingroup
-  \hyphenpenalty = 10000
-  \entry{#1}{#2}%
-\endgroup}
+% We use the same \entry macro as for the index entries.
+\let\tocentry = \entry
 
 % Space between chapter (or whatever) number and the title.
 \def\labelspace{\hskip1em \relax}
@@ -2983,92 +4810,91 @@ July\or August\or September\or October\or November\or December\fi
 
 \def\chapentryfonts{\secfonts \rm}
 \def\secentryfonts{\textfonts}
-\let\subsecentryfonts = \textfonts
-\let\subsubsecentryfonts = \textfonts
+\def\subsecentryfonts{\textfonts}
+\def\subsubsecentryfonts{\textfonts}
 
 
 \message{environments,}
+% @foo ... @end foo.
 
+% @point{}, @result{}, @expansion{}, @print{}, @equiv{}.
+%
 % Since these characters are used in examples, it should be an even number of
 % \tt widths. Each \tt character is 1en, so two makes it 1em.
-% Furthermore, these definitions must come after we define our fonts.
-\newbox\dblarrowbox    \newbox\longdblarrowbox
-\newbox\pushcharbox    \newbox\bullbox
-\newbox\equivbox       \newbox\errorbox
-
-\let\ptexequiv = \equiv
-
-%{\tentt
-%\global\setbox\dblarrowbox = \hbox to 1em{\hfil$\Rightarrow$\hfil}
-%\global\setbox\longdblarrowbox = \hbox to 1em{\hfil$\mapsto$\hfil}
-%\global\setbox\pushcharbox = \hbox to 1em{\hfil$\dashv$\hfil}
-%\global\setbox\equivbox = \hbox to 1em{\hfil$\ptexequiv$\hfil}
-% Adapted from the manmac format (p.420 of TeXbook)
-%\global\setbox\bullbox = \hbox to 1em{\kern.15em\vrule height .75ex width .85ex
-%                                      depth .1ex\hfil}
-%}
-
+%
 \def\point{$\star$}
-
 \def\result{\leavevmode\raise.15ex\hbox to 1em{\hfil$\Rightarrow$\hfil}}
 \def\expansion{\leavevmode\raise.1ex\hbox to 1em{\hfil$\mapsto$\hfil}}
 \def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}}
-
 \def\equiv{\leavevmode\lower.1ex\hbox to 1em{\hfil$\ptexequiv$\hfil}}
 
+% The @error{} command.
 % Adapted from the TeXbook's \boxit.
+%
+\newbox\errorbox
+%
 {\tentt \global\dimen0 = 3em}% Width of the box.
 \dimen2 = .55pt % Thickness of rules
 % The text. (`r' is open on the right, `e' somewhat less so on the left.)
 \setbox0 = \hbox{\kern-.75pt \tensf error\kern-1.5pt}
-
-\global\setbox\errorbox=\hbox to \dimen0{\hfil
+%
+\setbox\errorbox=\hbox to \dimen0{\hfil
    \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right.
    \advance\hsize by -2\dimen2 % Rules.
-   \vbox{
+   \vbox{%
       \hrule height\dimen2
       \hbox{\vrule width\dimen2 \kern3pt          % Space to left of text.
          \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below.
          \kern3pt\vrule width\dimen2}% Space to right.
       \hrule height\dimen2}
     \hfil}
-
-% The @error{} command.
+%
 \def\error{\leavevmode\lower.7ex\copy\errorbox}
 
 % @tex ... @end tex    escapes into raw Tex temporarily.
 % One exception: @ is still an escape character, so that @end tex works.
 % But \@ or @@ will get a plain tex @ character.
 
-\def\tex{\begingroup
-\catcode `\\=0 \catcode `\{=1 \catcode `\}=2
-\catcode `\$=3 \catcode `\&=4 \catcode `\#=6
-\catcode `\^=7 \catcode `\_=8 \catcode `\~=13 \let~=\tie
-\catcode `\%=14
-\catcode 43=12
-\catcode`\"=12
-\catcode`\==12
-\catcode`\|=12
-\catcode`\<=12
-\catcode`\>=12
-\escapechar=`\\
-%
-\let\~=\ptextilde
-\let\{=\ptexlbrace
-\let\}=\ptexrbrace
-\let\.=\ptexdot
-\let\*=\ptexstar
-\let\dots=\ptexdots
-\def\@{@}%
-\let\bullet=\ptexbullet
-\let\b=\ptexb \let\c=\ptexc \let\i=\ptexi \let\t=\ptext \let\l=\ptexl
-\let\L=\ptexL
-%
-\let\Etex=\endgroup}
-
-% Define @lisp ... @endlisp.
-% @lisp does a \begingroup so it can rebind things,
-% including the definition of @endlisp (which normally is erroneous).
+\envdef\tex{%
+  \catcode `\\=0 \catcode `\{=1 \catcode `\}=2
+  \catcode `\$=3 \catcode `\&=4 \catcode `\#=6
+  \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie
+  \catcode `\%=14
+  \catcode `\+=\other
+  \catcode `\"=\other
+  \catcode `\|=\other
+  \catcode `\<=\other
+  \catcode `\>=\other
+  \escapechar=`\\
+  %
+  \let\b=\ptexb
+  \let\bullet=\ptexbullet
+  \let\c=\ptexc
+  \let\,=\ptexcomma
+  \let\.=\ptexdot
+  \let\dots=\ptexdots
+  \let\equiv=\ptexequiv
+  \let\!=\ptexexclam
+  \let\i=\ptexi
+  \let\indent=\ptexindent
+  \let\noindent=\ptexnoindent
+  \let\{=\ptexlbrace
+  \let\+=\tabalign
+  \let\}=\ptexrbrace
+  \let\/=\ptexslash
+  \let\*=\ptexstar
+  \let\t=\ptext
+  \let\frenchspacing=\plainfrenchspacing
+  %
+  \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}%
+  \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}%
+  \def\@{@}%
+}
+% There is no need to define \Etex.
+
+% Define @lisp ... @end lisp.
+% @lisp environment forms a group so it can rebind things,
+% including the definition of @end lisp (which normally is erroneous).
 
 % Amount to narrow the margins by for @lisp.
 \newskip\lispnarrowing \lispnarrowing=0.4in
@@ -3078,38 +4904,38 @@ July\or August\or September\or October\or November\or December\fi
 % have any width.
 \def\lisppar{\null\endgraf}
 
-% Make each space character in the input produce a normal interword
-% space in the output.  Don't allow a line break at this space, as this
-% is used only in environments like @example, where each line of input
-% should produce a line of output anyway.
-%
-{\obeyspaces %
-\gdef\sepspaces{\obeyspaces\let =\tie}}
-
-% Define \obeyedspace to be our active space, whatever it is.  This is
-% for use in \parsearg.
-{\sepspaces%
-\global\let\obeyedspace= }
-
 % This space is always present above and below environments.
 \newskip\envskipamount \envskipamount = 0pt
 
 % Make spacing and below environment symmetrical.  We use \parskip here
 % to help in doing that, since in @example-like environments \parskip
 % is reset to zero; thus the \afterenvbreak inserts no space -- but the
-% start of the next paragraph will insert \parskip
+% start of the next paragraph will insert \parskip.
 %
-\def\aboveenvbreak{{\advance\envskipamount by \parskip
-\endgraf \ifdim\lastskip<\envskipamount
-\removelastskip \penalty-50 \vskip\envskipamount \fi}}
+\def\aboveenvbreak{{%
+  % =10000 instead of <10000 because of a special case in \itemzzz and
+  % \sectionheading, q.v.
+  \ifnum \lastpenalty=10000 \else
+    \advance\envskipamount by \parskip
+    \endgraf
+    \ifdim\lastskip<\envskipamount
+      \removelastskip
+      % it's not a good place to break if the last penalty was \nobreak
+      % or better ...
+      \ifnum\lastpenalty<10000 \penalty-50 \fi
+      \vskip\envskipamount
+    \fi
+  \fi
+}}
 
 \let\afterenvbreak = \aboveenvbreak
 
-% \nonarrowing is a flag.  If "set", @lisp etc don't narrow margins.
+% \nonarrowing is a flag.  If "set", @lisp etc don't narrow margins; it will
+% also clear it, so that its embedded environments do the narrowing again.
 \let\nonarrowing=\relax
 
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% \cartouche: draw rectangle w/rounded corners around argument
+% @cartouche ... @end cartouche: draw rectangle w/rounded corners around
+% environment contents.
 \font\circle=lcircle10
 \newdimen\circthick
 \newdimen\cartouter\newdimen\cartinner
@@ -3121,912 +4947,1442 @@ July\or August\or September\or October\or November\or December\fi
 \def\cbl{{\circle\char'012\hskip -6pt}}
 \def\cbr{{\hskip 6pt\circle\char'011}}
 \def\carttop{\hbox to \cartouter{\hskip\lskip
-       \ctl\leaders\hrule height\circthick\hfil\ctr
-       \hskip\rskip}}
+        \ctl\leaders\hrule height\circthick\hfil\ctr
+        \hskip\rskip}}
 \def\cartbot{\hbox to \cartouter{\hskip\lskip
-       \cbl\leaders\hrule height\circthick\hfil\cbr
-       \hskip\rskip}}
+        \cbl\leaders\hrule height\circthick\hfil\cbr
+        \hskip\rskip}}
 %
 \newskip\lskip\newskip\rskip
 
-\long\def\cartouche{%
-\begingroup
-       \lskip=\leftskip \rskip=\rightskip
-       \leftskip=0pt\rightskip=0pt %we want these *outside*.
-       \cartinner=\hsize \advance\cartinner by-\lskip
-                         \advance\cartinner by-\rskip
-       \cartouter=\hsize
-       \advance\cartouter by 18pt % allow for 3pt kerns on either
-%                                   side, and for 6pt waste from
-%                                   each corner char
-       \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip
-       % Flag to tell @lisp, etc., not to narrow margin.
-       \let\nonarrowing=\comment
-       \vbox\bgroup
-               \baselineskip=0pt\parskip=0pt\lineskip=0pt
-               \carttop
-               \hbox\bgroup
-                       \hskip\lskip
-                       \vrule\kern3pt
-                       \vbox\bgroup
-                               \hsize=\cartinner
-                               \kern3pt
-                               \begingroup
-                                       \baselineskip=\normbskip
-                                       \lineskip=\normlskip
-                                       \parskip=\normpskip
-                                       \vskip -\parskip
+\envdef\cartouche{%
+  \ifhmode\par\fi  % can't be in the midst of a paragraph.
+  \startsavinginserts
+  \lskip=\leftskip \rskip=\rightskip
+  \leftskip=0pt\rightskip=0pt % we want these *outside*.
+  \cartinner=\hsize \advance\cartinner by-\lskip
+  \advance\cartinner by-\rskip
+  \cartouter=\hsize
+  \advance\cartouter by 18.4pt % allow for 3pt kerns on either
+                               % side, and for 6pt waste from
+                               % each corner char, and rule thickness
+  \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip
+  % Flag to tell @lisp, etc., not to narrow margin.
+  \let\nonarrowing = t%
+  \vbox\bgroup
+      \baselineskip=0pt\parskip=0pt\lineskip=0pt
+      \carttop
+      \hbox\bgroup
+         \hskip\lskip
+         \vrule\kern3pt
+         \vbox\bgroup
+             \kern3pt
+             \hsize=\cartinner
+             \baselineskip=\normbskip
+             \lineskip=\normlskip
+             \parskip=\normpskip
+             \vskip -\parskip
+             \comment % For explanation, see the end of \def\group.
+}
 \def\Ecartouche{%
-                               \endgroup
-                               \kern3pt
-                       \egroup
-                       \kern3pt\vrule
-                       \hskip\rskip
-               \egroup
-               \cartbot
-       \egroup
-\endgroup
-}}
+              \ifhmode\par\fi
+             \kern3pt
+         \egroup
+         \kern3pt\vrule
+         \hskip\rskip
+      \egroup
+      \cartbot
+  \egroup
+  \checkinserts
+}
 
 
 % This macro is called at the beginning of all the @example variants,
 % inside a group.
 \def\nonfillstart{%
   \aboveenvbreak
-  \inENV % This group ends at the end of the body
   \hfuzz = 12pt % Don't be fussy
   \sepspaces % Make spaces be word-separators rather than space tokens.
-  \singlespace
   \let\par = \lisppar % don't ignore blank lines
   \obeylines % each line of input is a line of output
   \parskip = 0pt
   \parindent = 0pt
   \emergencystretch = 0pt % don't try to avoid overfull boxes
-  % @cartouche defines \nonarrowing to inhibit narrowing
-  % at next level down.
   \ifx\nonarrowing\relax
     \advance \leftskip by \lispnarrowing
     \exdentamount=\lispnarrowing
-    \let\exdent=\nofillexdent
-    \let\nonarrowing=\relax
+  \else
+    \let\nonarrowing = \relax
   \fi
+  \let\exdent=\nofillexdent
 }
 
-% To ending an @example-like environment, we first end the paragraph
-% (via \afterenvbreak's vertical glue), and then the group.  That way we
-% keep the zero \parskip that the environments set -- \parskip glue
-% will be inserted at the beginning of the next paragraph in the
-% document, after the environment.
+% If you want all examples etc. small: @set dispenvsize small.
+% If you want even small examples the full size: @set dispenvsize nosmall.
+% This affects the following displayed environments:
+%    @example, @display, @format, @lisp
 %
-\def\nonfillfinish{\afterenvbreak\endgroup}%
+\def\smallword{small}
+\def\nosmallword{nosmall}
+\let\SETdispenvsize\relax
+\def\setnormaldispenv{%
+  \ifx\SETdispenvsize\smallword
+    \smallexamplefonts \rm
+  \fi
+}
+\def\setsmalldispenv{%
+  \ifx\SETdispenvsize\nosmallword
+  \else
+    \smallexamplefonts \rm
+  \fi
+}
 
-% This macro is
-\def\lisp{\begingroup
-  \nonfillstart
-  \let\Elisp = \nonfillfinish
-  \tt
-  \rawbackslash % have \ input char produce \ char from current font
-  \gobble
+% We often define two environments, @foo and @smallfoo.
+% Let's do it by one command:
+\def\makedispenv #1#2{
+  \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2}
+  \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2}
+  \expandafter\let\csname E#1\endcsname \afterenvbreak
+  \expandafter\let\csname Esmall#1\endcsname \afterenvbreak
+}
+
+% Define two synonyms:
+\def\maketwodispenvs #1#2#3{
+  \makedispenv{#1}{#3}
+  \makedispenv{#2}{#3}
 }
 
-% Define the \E... control sequence only if we are inside the
-% environment, so the error checking in \end will work.
+% @lisp: indented, narrowed, typewriter font; @example: same as @lisp.
 %
-% We must call \lisp last in the definition, since it reads the
-% return following the @example (or whatever) command.
+% @smallexample and @smalllisp: use smaller fonts.
+% Originally contributed by Pavel@xerox.
 %
-\def\example{\begingroup \def\Eexample{\nonfillfinish\endgroup}\lisp}
-\def\smallexample{\begingroup \def\Esmallexample{\nonfillfinish\endgroup}\lisp}
-\def\smalllisp{\begingroup \def\Esmalllisp{\nonfillfinish\endgroup}\lisp}
+\maketwodispenvs {lisp}{example}{%
+  \nonfillstart
+  \tt
+  \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special.
+  \gobble       % eat return
+}
 
-% @smallexample and @smalllisp.  This is not used unless the @smallbook
-% command is given.  Originally contributed by Pavel@xerox.
+% @display/@smalldisplay: same as @lisp except keep current font.
 %
-\def\smalllispx{\begingroup
+\makedispenv {display}{%
   \nonfillstart
-  \let\Esmalllisp = \nonfillfinish
-  \let\Esmallexample = \nonfillfinish
-  %
-  % Smaller interline space and fonts for small examples.
-  \setleading{10pt}%
-  \indexfonts \tt
-  \rawbackslash % make \ output the \ character from the current font (tt)
   \gobble
 }
 
-% This is @display; same as @lisp except use roman font.
+% @format/@smallformat: same as @display except don't narrow margins.
 %
-\def\display{\begingroup
+\makedispenv{format}{%
+  \let\nonarrowing = t%
   \nonfillstart
-  \let\Edisplay = \nonfillfinish
   \gobble
 }
 
-% This is @format; same as @display except don't narrow margins.
-%
-\def\format{\begingroup
-  \let\nonarrowing = t
+% @flushleft: same as @format, but doesn't obey \SETdispenvsize.
+\envdef\flushleft{%
+  \let\nonarrowing = t%
   \nonfillstart
-  \let\Eformat = \nonfillfinish
   \gobble
 }
+\let\Eflushleft = \afterenvbreak
 
-% @flushleft (same as @format) and @flushright.
+% @flushright.
 %
-\def\flushleft{\begingroup
-  \let\nonarrowing = t
+\envdef\flushright{%
+  \let\nonarrowing = t%
   \nonfillstart
-  \let\Eflushleft = \nonfillfinish
+  \advance\leftskip by 0pt plus 1fill
   \gobble
 }
-\def\flushright{\begingroup
-  \let\nonarrowing = t
-  \nonfillstart
-  \let\Eflushright = \nonfillfinish
-  \advance\leftskip by 0pt plus 1fill
-  \gobble}
+\let\Eflushright = \afterenvbreak
+
 
 % @quotation does normal linebreaking (hence we can't use \nonfillstart)
-% and narrows the margins.
+% and narrows the margins.  We keep \parskip nonzero in general, since
+% we're doing normal filling.  So, when using \aboveenvbreak and
+% \afterenvbreak, temporarily make \parskip 0.
 %
-\def\quotation{%
-  \begingroup\inENV %This group ends at the end of the @quotation body
+\envdef\quotation{%
   {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip
-  \singlespace
   \parindent=0pt
-  % We have retained a nonzero parskip for the environment, since we're
-  % doing normal filling. So to avoid extra space below the environment...
-  \def\Equotation{\parskip = 0pt \nonfillfinish}%
   %
   % @cartouche defines \nonarrowing to inhibit narrowing at next level down.
   \ifx\nonarrowing\relax
     \advance\leftskip by \lispnarrowing
     \advance\rightskip by \lispnarrowing
     \exdentamount = \lispnarrowing
+  \else
     \let\nonarrowing = \relax
   \fi
+  \parsearg\quotationlabel
 }
 
-\message{defuns,}
-% Define formatter for defuns
-% First, allow user to change definition object font (\df) internally
-\def\setdeffont #1 {\csname DEF#1\endcsname}
-
-\newskip\defbodyindent \defbodyindent=.4in
-\newskip\defargsindent \defargsindent=50pt
-\newskip\deftypemargin \deftypemargin=12pt
-\newskip\deflastargmargin \deflastargmargin=18pt
-
-\newcount\parencount
-% define \functionparens, which makes ( and ) and & do special things.
-% \functionparens affects the group it is contained in.
-\def\activeparens{%
-\catcode`\(=\active \catcode`\)=\active \catcode`\&=\active
-\catcode`\[=\active \catcode`\]=\active}
-
-% Make control sequences which act like normal parenthesis chars.
-\let\lparen = ( \let\rparen = )
-
-{\activeparens % Now, smart parens don't turn on until &foo (see \amprm)
+% We have retained a nonzero parskip for the environment, since we're
+% doing normal filling.
+%
+\def\Equotation{%
+  \par
+  \ifx\quotationauthor\undefined\else
+    % indent a bit.
+    \leftline{\kern 2\leftskip \sl ---\quotationauthor}%
+  \fi
+  {\parskip=0pt \afterenvbreak}%
+}
 
-% Be sure that we always have a definition for `(', etc.  For example,
-% if the fn name has parens in it, \boldbrax will not be in effect yet,
-% so TeX would otherwise complain about undefined control sequence.
-\global\let(=\lparen \global\let)=\rparen
-\global\let[=\lbrack \global\let]=\rbrack
+% If we're given an argument, typeset it in bold with a colon after.
+\def\quotationlabel#1{%
+  \def\temp{#1}%
+  \ifx\temp\empty \else
+    {\bf #1: }%
+  \fi
+}
 
-\gdef\functionparens{\boldbrax\let&=\amprm\parencount=0 }
-\gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb}
 
-% Definitions of (, ) and & used in args for functions.
-% This is the definition of ( outside of all parentheses.
-\gdef\oprm#1 {{\rm\char`\(}#1 \bf \let(=\opnested %
-\global\advance\parencount by 1 }
+% LaTeX-like @verbatim...@end verbatim and @verb{<char>...<char>}
+% If we want to allow any <char> as delimiter,
+% we need the curly braces so that makeinfo sees the @verb command, eg:
+% `@verbx...x' would look like the '@verbx' command.  --janneke@gnu.org
 %
-% This is the definition of ( when already inside a level of parens.
-\gdef\opnested{\char`\(\global\advance\parencount by 1 }
+% [Knuth]: Donald Ervin Knuth, 1996.  The TeXbook.
 %
-\gdef\clrm{% Print a paren in roman if it is taking us back to depth of 0.
-% also in that case restore the outer-level definition of (.
-\ifnum \parencount=1 {\rm \char `\)}\sl \let(=\oprm \else \char `\) \fi
-\global\advance \parencount by -1 }
-% If we encounter &foo, then turn on ()-hacking afterwards
-\gdef\amprm#1 {{\rm\&#1}\let(=\oprm \let)=\clrm\ }
-%
-\gdef\normalparens{\boldbrax\let&=\ampnr}
-} % End of definition inside \activeparens
-%% These parens (in \boldbrax) actually are a little bolder than the
-%% contained text.  This is especially needed for [ and ]
-\def\opnr{{\sf\char`\(}} \def\clnr{{\sf\char`\)}} \def\ampnr{\&}
-\def\lbrb{{\bf\char`\[}} \def\rbrb{{\bf\char`\]}}
-
-% First, defname, which formats the header line itself.
-% #1 should be the function name.
-% #2 should be the type of definition, such as "Function".
-
-\def\defname #1#2{%
-% Get the values of \leftskip and \rightskip as they were
-% outside the @def...
-\dimen2=\leftskip
-\advance\dimen2 by -\defbodyindent
-\dimen3=\rightskip
-\advance\dimen3 by -\defbodyindent
-\noindent        %
-\setbox0=\hbox{\hskip \deflastargmargin{\rm #2}\hskip \deftypemargin}%
-\dimen0=\hsize \advance \dimen0 by -\wd0 % compute size for first line
-\dimen1=\hsize \advance \dimen1 by -\defargsindent %size for continuations
-\parshape 2 0in \dimen0 \defargsindent \dimen1     %
-% Now output arg 2 ("Function" or some such)
-% ending at \deftypemargin from the right margin,
-% but stuck inside a box of width 0 so it does not interfere with linebreaking
-{% Adjust \hsize to exclude the ambient margins,
-% so that \rightline will obey them.
-\advance \hsize by -\dimen2 \advance \hsize by -\dimen3
-\rlap{\rightline{{\rm #2}\hskip \deftypemargin}}}%
-% Make all lines underfull and no complaints:
-\tolerance=10000 \hbadness=10000
-\advance\leftskip by -\defbodyindent
-\exdentamount=\defbodyindent
-{\df #1}\enskip        % Generate function name
-}
-
-% Actually process the body of a definition
-% #1 should be the terminating control sequence, such as \Edefun.
-% #2 should be the "another name" control sequence, such as \defunx.
-% #3 should be the control sequence that actually processes the header,
-%    such as \defunheader.
-
-\def\defparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody
-\medbreak %
-% Define the end token that this defining construct specifies
-% so that it will exit this group.
-\def#1{\endgraf\endgroup\medbreak}%
-\def#2{\begingroup\obeylines\activeparens\spacesplit#3}%
-\parindent=0in
-\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
-\exdentamount=\defbodyindent
-\begingroup %
-\catcode 61=\active % 61 is `='
-\obeylines\activeparens\spacesplit#3}
-
-\def\defmethparsebody #1#2#3#4 {\begingroup\inENV %
-\medbreak %
-% Define the end token that this defining construct specifies
-% so that it will exit this group.
-\def#1{\endgraf\endgroup\medbreak}%
-\def#2##1 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}}}%
-\parindent=0in
-\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
-\exdentamount=\defbodyindent
-\begingroup\obeylines\activeparens\spacesplit{#3{#4}}}
-
-\def\defopparsebody #1#2#3#4#5 {\begingroup\inENV %
-\medbreak %
-% Define the end token that this defining construct specifies
-% so that it will exit this group.
-\def#1{\endgraf\endgroup\medbreak}%
-\def#2##1 ##2 {\def#4{##1}%
-\begingroup\obeylines\activeparens\spacesplit{#3{##2}}}%
-\parindent=0in
-\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
-\exdentamount=\defbodyindent
-\begingroup\obeylines\activeparens\spacesplit{#3{#5}}}
-
-% These parsing functions are similar to the preceding ones
-% except that they do not make parens into active characters.
-% These are used for "variables" since they have no arguments.
-
-\def\defvarparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody
-\medbreak %
-% Define the end token that this defining construct specifies
-% so that it will exit this group.
-\def#1{\endgraf\endgroup\medbreak}%
-\def#2{\begingroup\obeylines\spacesplit#3}%
-\parindent=0in
-\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
-\exdentamount=\defbodyindent
-\begingroup %
-\catcode 61=\active %
-\obeylines\spacesplit#3}
-
-% This is used for \def{tp,vr}parsebody.  It could probably be used for
-% some of the others, too, with some judicious conditionals.
-% 
-\def\parsebodycommon#1#2#3{%
-  \begingroup\inENV %
-  \medbreak %
-  % Define the end token that this defining construct specifies
-  % so that it will exit this group.
-  \def#1{\endgraf\endgroup\medbreak}%
-  \def#2##1 {\begingroup\obeylines\spacesplit{#3{##1}}}%
-  \parindent=0in
-  \advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
-  \exdentamount=\defbodyindent
-  \begingroup\obeylines
+% [Knuth] p.344; only we need to do the other characters Texinfo sets
+% active too.  Otherwise, they get lost as the first character on a
+% verbatim line.
+\def\dospecials{%
+  \do\ \do\\\do\{\do\}\do\$\do\&%
+  \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~%
+  \do\<\do\>\do\|\do\@\do+\do\"%
 }
-
-\def\defvrparsebody#1#2#3#4 {%
-  \parsebodycommon{#1}{#2}{#3}%
-  \spacesplit{#3{#4}}%
+%
+% [Knuth] p. 380
+\def\uncatcodespecials{%
+  \def\do##1{\catcode`##1=\other}\dospecials}
+%
+% [Knuth] pp. 380,381,391
+% Disable Spanish ligatures ?` and !` of \tt font
+\begingroup
+  \catcode`\`=\active\gdef`{\relax\lq}
+\endgroup
+%
+% Setup for the @verb command.
+%
+% Eight spaces for a tab
+\begingroup
+  \catcode`\^^I=\active
+  \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }}
+\endgroup
+%
+\def\setupverb{%
+  \tt  % easiest (and conventionally used) font for verbatim
+  \def\par{\leavevmode\endgraf}%
+  \catcode`\`=\active
+  \tabeightspaces
+  % Respect line breaks,
+  % print special symbols as themselves, and
+  % make each space count
+  % must do in this order:
+  \obeylines \uncatcodespecials \sepspaces
 }
 
-% This loses on `@deftp {Data Type} {struct termios}' -- it thinks the
-% type is just `struct', because we lose the braces in `{struct
-% termios}' when \spacesplit reads its undelimited argument.  Sigh.
-% \let\deftpparsebody=\defvrparsebody
+% Setup for the @verbatim environment
 %
-% So, to get around this, we put \empty in with the type name.  That
-% way, TeX won't find exactly `{...}' as an undelimited argument, and
-% won't strip off the braces.
+% Real tab expansion
+\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount
 %
-\def\deftpparsebody #1#2#3#4 {%
-  \parsebodycommon{#1}{#2}{#3}%
-  \spacesplit{\parsetpheaderline{#3{#4}}}\empty
+\def\starttabbox{\setbox0=\hbox\bgroup}
+\begingroup
+  \catcode`\^^I=\active
+  \gdef\tabexpand{%
+    \catcode`\^^I=\active
+    \def^^I{\leavevmode\egroup
+      \dimen0=\wd0 % the width so far, or since the previous tab
+      \divide\dimen0 by\tabw
+      \multiply\dimen0 by\tabw % compute previous multiple of \tabw
+      \advance\dimen0 by\tabw  % advance to next multiple of \tabw
+      \wd0=\dimen0 \box0 \starttabbox
+    }%
+  }
+\endgroup
+\def\setupverbatim{%
+  \let\nonarrowing = t%
+  \nonfillstart
+  % Easiest (and conventionally used) font for verbatim
+  \tt
+  \def\par{\leavevmode\egroup\box0\endgraf}%
+  \catcode`\`=\active
+  \tabexpand
+  % Respect line breaks,
+  % print special symbols as themselves, and
+  % make each space count
+  % must do in this order:
+  \obeylines \uncatcodespecials \sepspaces
+  \everypar{\starttabbox}%
 }
 
-% Fine, but then we have to eventually remove the \empty *and* the
-% braces (if any).  That's what this does, putting the result in \tptemp.
-% 
-\def\removeemptybraces\empty#1\relax{\def\tptemp{#1}}%
-
-% After \spacesplit has done its work, this is called -- #1 is the final
-% thing to call, #2 the type name (which starts with \empty), and #3
-% (which might be empty) the arguments.
-% 
-\def\parsetpheaderline#1#2#3{%
-  \removeemptybraces#2\relax
-  #1{\tptemp}{#3}%
-}%
-
-\def\defopvarparsebody #1#2#3#4#5 {\begingroup\inENV %
-\medbreak %
-% Define the end token that this defining construct specifies
-% so that it will exit this group.
-\def#1{\endgraf\endgroup\medbreak}%
-\def#2##1 ##2 {\def#4{##1}%
-\begingroup\obeylines\spacesplit{#3{##2}}}%
-\parindent=0in
-\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
-\exdentamount=\defbodyindent
-\begingroup\obeylines\spacesplit{#3{#5}}}
+% Do the @verb magic: verbatim text is quoted by unique
+% delimiter characters.  Before first delimiter expect a
+% right brace, after last delimiter expect closing brace:
+%
+%    \def\doverb'{'<char>#1<char>'}'{#1}
+%
+% [Knuth] p. 382; only eat outer {}
+\begingroup
+  \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other
+  \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next]
+\endgroup
+%
+\def\verb{\begingroup\setupverb\doverb}
+%
+%
+% Do the @verbatim magic: define the macro \doverbatim so that
+% the (first) argument ends when '@end verbatim' is reached, ie:
+%
+%     \def\doverbatim#1@end verbatim{#1}
+%
+% For Texinfo it's a lot easier than for LaTeX,
+% because texinfo's \verbatim doesn't stop at '\end{verbatim}':
+% we need not redefine '\', '{' and '}'.
+%
+% Inspired by LaTeX's verbatim command set [latex.ltx]
+%
+\begingroup
+  \catcode`\ =\active
+  \obeylines %
+  % ignore everything up to the first ^^M, that's the newline at the end
+  % of the @verbatim input line itself.  Otherwise we get an extra blank
+  % line in the output.
+  \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}%
+  % We really want {...\end verbatim} in the body of the macro, but
+  % without the active space; thus we have to use \xdef and \gobble.
+\endgroup
+%
+\envdef\verbatim{%
+    \setupverbatim\doverbatim
+}
+\let\Everbatim = \afterenvbreak
 
-% Split up #2 at the first space token.
-% call #1 with two arguments:
-%  the first is all of #2 before the space token,
-%  the second is all of #2 after that space token.
-% If #2 contains no space token, all of it is passed as the first arg
-% and the second is passed as empty.
 
-{\obeylines
-\gdef\spacesplit#1#2^^M{\endgroup\spacesplitfoo{#1}#2 \relax\spacesplitfoo}%
-\long\gdef\spacesplitfoo#1#2 #3#4\spacesplitfoo{%
-\ifx\relax #3%
-#1{#2}{}\else #1{#2}{#3#4}\fi}}
+% @verbatiminclude FILE - insert text of file in verbatim environment.
+%
+\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude}
+%
+\def\doverbatiminclude#1{%
+  {%
+    \makevalueexpandable
+    \setupverbatim
+    \input #1
+    \afterenvbreak
+  }%
+}
 
-% So much for the things common to all kinds of definitions.
+% @copying ... @end copying.
+% Save the text away for @insertcopying later.
+%
+% We save the uninterpreted tokens, rather than creating a box.
+% Saving the text in a box would be much easier, but then all the
+% typesetting commands (@smallbook, font changes, etc.) have to be done
+% beforehand -- and a) we want @copying to be done first in the source
+% file; b) letting users define the frontmatter in as flexible order as
+% possible is very desirable.
+%
+\def\copying{\checkenv{}\begingroup\scanargctxt\docopying}
+\def\docopying#1@end copying{\endgroup\def\copyingtext{#1}}
+%
+\def\insertcopying{%
+  \begingroup
+    \parindent = 0pt  % paragraph indentation looks wrong on title page
+    \scanexp\copyingtext
+  \endgroup
+}
 
-% Define @defun.
+\message{defuns,}
+% @defun etc.
 
-% First, define the processing that is wanted for arguments of \defun
-% Use this to expand the args and terminate the paragraph they make up
+\newskip\defbodyindent \defbodyindent=.4in
+\newskip\defargsindent \defargsindent=50pt
+\newskip\deflastargmargin \deflastargmargin=18pt
 
-\def\defunargs #1{\functionparens \sl
-% Expand, preventing hyphenation at `-' chars.
-% Note that groups don't affect changes in \hyphenchar.
-\hyphenchar\tensl=0
-#1%
-\hyphenchar\tensl=45
-\ifnum\parencount=0 \else \errmessage{unbalanced parens in @def arguments}\fi%
-\interlinepenalty=10000
-\advance\rightskip by 0pt plus 1fil
-\endgraf\penalty 10000\vskip -\parskip\penalty 10000%
+% Start the processing of @deffn:
+\def\startdefun{%
+  \ifnum\lastpenalty<10000
+    \medbreak
+  \else
+    % If there are two @def commands in a row, we'll have a \nobreak,
+    % which is there to keep the function description together with its
+    % header.  But if there's nothing but headers, we need to allow a
+    % break somewhere.  Check specifically for penalty 10002, inserted
+    % by \defargscommonending, instead of 10000, since the sectioning
+    % commands also insert a nobreak penalty, and we don't want to allow
+    % a break between a section heading and a defun.
+    % 
+    \ifnum\lastpenalty=10002 \penalty2000 \fi
+    %
+    % Similarly, after a section heading, do not allow a break.
+    % But do insert the glue.
+    \medskip  % preceded by discardable penalty, so not a breakpoint
+  \fi
+  %
+  \parindent=0in
+  \advance\leftskip by \defbodyindent
+  \exdentamount=\defbodyindent
 }
 
-\def\deftypefunargs #1{%
-% Expand, preventing hyphenation at `-' chars.
-% Note that groups don't affect changes in \hyphenchar.
-\functionparens
-\tclose{#1}% avoid \code because of side effects on active chars
-\interlinepenalty=10000
-\advance\rightskip by 0pt plus 1fil
-\endgraf\penalty 10000\vskip -\parskip\penalty 10000%
+\def\dodefunx#1{%
+  % First, check whether we are in the right environment:
+  \checkenv#1%
+  %
+  % As above, allow line break if we have multiple x headers in a row.
+  % It's not a great place, though.
+  \ifnum\lastpenalty=10002 \penalty3000 \fi
+  %
+  % And now, it's time to reuse the body of the original defun:
+  \expandafter\gobbledefun#1%
 }
+\def\gobbledefun#1\startdefun{}
 
-% Do complete processing of one @defun or @defunx line already parsed.
-
-% @deffn Command forward-char nchars
+% \printdefunline \deffnheader{text}
+%
+\def\printdefunline#1#2{%
+  \begingroup
+    % call \deffnheader:
+    #1#2 \endheader
+    % common ending:
+    \interlinepenalty = 10000
+    \advance\rightskip by 0pt plus 1fil
+    \endgraf
+    \nobreak\vskip -\parskip
+    \penalty 10002  % signal to \startdefun and \dodefunx
+    % Some of the @defun-type tags do not enable magic parentheses,
+    % rendering the following check redundant.  But we don't optimize.
+    \checkparencounts
+  \endgroup
+}
 
-\def\deffn{\defmethparsebody\Edeffn\deffnx\deffnheader}
+\def\Edefun{\endgraf\medbreak}
 
-\def\deffnheader #1#2#3{\doind {fn}{\code{#2}}%
-\begingroup\defname {#2}{#1}\defunargs{#3}\endgroup %
-\catcode 61=\other % Turn off change made in \defparsebody
+% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn;
+% the only thing remainnig is to define \deffnheader.
+%
+\def\makedefun#1{%
+  \expandafter\let\csname E#1\endcsname = \Edefun
+  \edef\temp{\noexpand\domakedefun
+    \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}%
+  \temp
 }
 
-% @defun == @deffn Function
+% \domakedefun \deffn \deffnx \deffnheader
+%
+% Define \deffn and \deffnx, without parameters.
+% \deffnheader has to be defined explicitly.
+%
+\def\domakedefun#1#2#3{%
+  \envdef#1{%
+    \startdefun
+    \parseargusing\activeparens{\printdefunline#3}%
+  }%
+  \def#2{\dodefunx#1}%
+  \def#3%
+}
 
-\def\defun{\defparsebody\Edefun\defunx\defunheader}
+%%% Untyped functions:
 
-\def\defunheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
-\begingroup\defname {#1}{Function}%
-\defunargs {#2}\endgroup %
-\catcode 61=\other % Turn off change made in \defparsebody
-}
+% @deffn category name args
+\makedefun{deffn}{\deffngeneral{}}
 
-% @deftypefun int foobar (int @var{foo}, float @var{bar})
+% @deffn category class name args
+\makedefun{defop}#1 {\defopon{#1\ \putwordon}}
 
-\def\deftypefun{\defparsebody\Edeftypefun\deftypefunx\deftypefunheader}
+% \defopon {category on}class name args
+\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} }
 
-% #1 is the data type.  #2 is the name and args.
-\def\deftypefunheader #1#2{\deftypefunheaderx{#1}#2 \relax}
-% #1 is the data type, #2 the name, #3 the args.
-\def\deftypefunheaderx #1#2 #3\relax{%
-\doind {fn}{\code{#2}}% Make entry in function index
-\begingroup\defname {\defheaderxcond#1\relax$$$#2}{Function}%
-\deftypefunargs {#3}\endgroup %
-\catcode 61=\other % Turn off change made in \defparsebody
+% \deffngeneral {subind}category name args
+%
+\def\deffngeneral#1#2 #3 #4\endheader{%
+  % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}.
+  \dosubind{fn}{\code{#3}}{#1}%
+  \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}%
 }
 
-% @deftypefn {Library Function} int foobar (int @var{foo}, float @var{bar})
+%%% Typed functions:
 
-\def\deftypefn{\defmethparsebody\Edeftypefn\deftypefnx\deftypefnheader}
+% @deftypefn category type name args
+\makedefun{deftypefn}{\deftypefngeneral{}}
 
-% \defheaderxcond#1\relax$$$
-% puts #1 in @code, followed by a space, but does nothing if #1 is null.
-\def\defheaderxcond#1#2$$${\ifx#1\relax\else\code{#1#2} \fi}
+% @deftypeop category class type name args
+\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}}
 
-% #1 is the classification.  #2 is the data type.  #3 is the name and args.
-\def\deftypefnheader #1#2#3{\deftypefnheaderx{#1}{#2}#3 \relax}
-% #1 is the classification, #2 the data type, #3 the name, #4 the args.
-\def\deftypefnheaderx #1#2#3 #4\relax{%
-\doind {fn}{\code{#3}}% Make entry in function index
-\begingroup
-\normalparens % notably, turn off `&' magic, which prevents
-%               at least some C++ text from working
-\defname {\defheaderxcond#2\relax$$$#3}{#1}%
-\deftypefunargs {#4}\endgroup %
-\catcode 61=\other % Turn off change made in \defparsebody
-}
+% \deftypeopon {category on}class type name args
+\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} }
 
-% @defmac == @deffn Macro
+% \deftypefngeneral {subind}category type name args
+%
+\def\deftypefngeneral#1#2 #3 #4 #5\endheader{%
+  \dosubind{fn}{\code{#4}}{#1}%
+  \defname{#2}{#3}{#4}\defunargs{#5\unskip}%
+}
 
-\def\defmac{\defparsebody\Edefmac\defmacx\defmacheader}
+%%% Typed variables:
 
-\def\defmacheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
-\begingroup\defname {#1}{Macro}%
-\defunargs {#2}\endgroup %
-\catcode 61=\other % Turn off change made in \defparsebody
-}
+% @deftypevr category type var args
+\makedefun{deftypevr}{\deftypecvgeneral{}}
 
-% @defspec == @deffn Special Form
+% @deftypecv category class type var args
+\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}}
 
-\def\defspec{\defparsebody\Edefspec\defspecx\defspecheader}
+% \deftypecvof {category of}class type var args
+\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} }
 
-\def\defspecheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
-\begingroup\defname {#1}{Special Form}%
-\defunargs {#2}\endgroup %
-\catcode 61=\other % Turn off change made in \defparsebody
+% \deftypecvgeneral {subind}category type var args
+%
+\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{%
+  \dosubind{vr}{\code{#4}}{#1}%
+  \defname{#2}{#3}{#4}\defunargs{#5\unskip}%
 }
 
-% This definition is run if you use @defunx
-% anywhere other than immediately after a @defun or @defunx.
+%%% Untyped variables:
 
-\def\deffnx #1 {\errmessage{@deffnx in invalid context}}
-\def\defunx #1 {\errmessage{@defunx in invalid context}}
-\def\defmacx #1 {\errmessage{@defmacx in invalid context}}
-\def\defspecx #1 {\errmessage{@defspecx in invalid context}}
-\def\deftypefnx #1 {\errmessage{@deftypefnx in invalid context}}
-\def\deftypeunx #1 {\errmessage{@deftypeunx in invalid context}}
+% @defvr category var args
+\makedefun{defvr}#1 {\deftypevrheader{#1} {} }
 
-% @defmethod, and so on
+% @defcv category class var args
+\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}}
 
-% @defop {Funny Method} foo-class frobnicate argument
+% \defcvof {category of}class var args
+\def\defcvof#1#2 {\deftypecvof{#1}#2 {} }
 
-\def\defop #1 {\def\defoptype{#1}%
-\defopparsebody\Edefop\defopx\defopheader\defoptype}
-
-\def\defopheader #1#2#3{%
-\dosubind {fn}{\code{#2}}{on #1}% Make entry in function index
-\begingroup\defname {#2}{\defoptype{} on #1}%
-\defunargs {#3}\endgroup %
+%%% Type:
+% @deftp category name args
+\makedefun{deftp}#1 #2 #3\endheader{%
+  \doind{tp}{\code{#2}}%
+  \defname{#1}{}{#2}\defunargs{#3\unskip}%
 }
 
-% @defmethod == @defop Method
+% Remaining @defun-like shortcuts:
+\makedefun{defun}{\deffnheader{\putwordDeffunc} }
+\makedefun{defmac}{\deffnheader{\putwordDefmac} }
+\makedefun{defspec}{\deffnheader{\putwordDefspec} }
+\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} }
+\makedefun{defvar}{\defvrheader{\putwordDefvar} }
+\makedefun{defopt}{\defvrheader{\putwordDefopt} }
+\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} }
+\makedefun{defmethod}{\defopon\putwordMethodon}
+\makedefun{deftypemethod}{\deftypeopon\putwordMethodon}
+\makedefun{defivar}{\defcvof\putwordInstanceVariableof}
+\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof}
+
+% \defname, which formats the name of the @def (not the args).
+% #1 is the category, such as "Function".
+% #2 is the return type, if any.
+% #3 is the function name.
+%
+% We are followed by (but not passed) the arguments, if any.
+%
+\def\defname#1#2#3{%
+  % Get the values of \leftskip and \rightskip as they were outside the @def...
+  \advance\leftskip by -\defbodyindent
+  %
+  % How we'll format the type name.  Putting it in brackets helps
+  % distinguish it from the body text that may end up on the next line
+  % just below it.
+  \def\temp{#1}%
+  \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi}
+  %
+  % Figure out line sizes for the paragraph shape.
+  % The first line needs space for \box0; but if \rightskip is nonzero,
+  % we need only space for the part of \box0 which exceeds it:
+  \dimen0=\hsize  \advance\dimen0 by -\wd0  \advance\dimen0 by \rightskip
+  % The continuations:
+  \dimen2=\hsize  \advance\dimen2 by -\defargsindent
+  % (plain.tex says that \dimen1 should be used only as global.)
+  \parshape 2 0in \dimen0 \defargsindent \dimen2
+  %
+  % Put the type name to the right margin.
+  \noindent
+  \hbox to 0pt{%
+    \hfil\box0 \kern-\hsize
+    % \hsize has to be shortened this way:
+    \kern\leftskip
+    % Intentionally do not respect \rightskip, since we need the space.
+  }%
+  %
+  % Allow all lines to be underfull without complaint:
+  \tolerance=10000 \hbadness=10000
+  \exdentamount=\defbodyindent
+  {%
+    % defun fonts. We use typewriter by default (used to be bold) because:
+    % . we're printing identifiers, they should be in tt in principle.
+    % . in languages with many accents, such as Czech or French, it's
+    %   common to leave accents off identifiers.  The result looks ok in
+    %   tt, but exceedingly strange in rm.
+    % . we don't want -- and --- to be treated as ligatures.
+    % . this still does not fix the ?` and !` ligatures, but so far no
+    %   one has made identifiers using them :).
+    \df \tt
+    \def\temp{#2}% return value type
+    \ifx\temp\empty\else \tclose{\temp} \fi
+    #3% output function name
+  }%
+  {\rm\enskip}% hskip 0.5 em of \tenrm
+  %
+  \boldbrax
+  % arguments will be output next, if any.
+}
 
-\def\defmethod{\defmethparsebody\Edefmethod\defmethodx\defmethodheader}
+% Print arguments in slanted roman (not ttsl), inconsistently with using
+% tt for the name.  This is because literal text is sometimes needed in
+% the argument list (groff manual), and ttsl and tt are not very
+% distinguishable.  Prevent hyphenation at `-' chars.
+%
+\def\defunargs#1{%
+  % use sl by default (not ttsl),
+  % tt for the names.
+  \df \sl \hyphenchar\font=0
+  %
+  % On the other hand, if an argument has two dashes (for instance), we
+  % want a way to get ttsl.  Let's try @var for that.
+  \let\var=\ttslanted
+  #1%
+  \sl\hyphenchar\font=45
+}
 
-\def\defmethodheader #1#2#3{%
-\dosubind {fn}{\code{#2}}{on #1}% entry in function index
-\begingroup\defname {#2}{Method on #1}%
-\defunargs {#3}\endgroup %
+% We want ()&[] to print specially on the defun line.
+%
+\def\activeparens{%
+  \catcode`\(=\active \catcode`\)=\active
+  \catcode`\[=\active \catcode`\]=\active
+  \catcode`\&=\active
 }
 
-% @defcv {Class Option} foo-class foo-flag
+% Make control sequences which act like normal parenthesis chars.
+\let\lparen = ( \let\rparen = )
 
-\def\defcv #1 {\def\defcvtype{#1}%
-\defopvarparsebody\Edefcv\defcvx\defcvarheader\defcvtype}
+% Be sure that we always have a definition for `(', etc.  For example,
+% if the fn name has parens in it, \boldbrax will not be in effect yet,
+% so TeX would otherwise complain about undefined control sequence.
+{
+  \activeparens
+  \global\let(=\lparen \global\let)=\rparen
+  \global\let[=\lbrack \global\let]=\rbrack
+  \global\let& = \&
 
-\def\defcvarheader #1#2#3{%
-\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index
-\begingroup\defname {#2}{\defcvtype{} of #1}%
-\defvarargs {#3}\endgroup %
+  \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb}
+  \gdef\magicamp{\let&=\amprm}
 }
 
-% @defivar == @defcv {Instance Variable}
-
-\def\defivar{\defvrparsebody\Edefivar\defivarx\defivarheader}
+\newcount\parencount
 
-\def\defivarheader #1#2#3{%
-\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index
-\begingroup\defname {#2}{Instance Variable of #1}%
-\defvarargs {#3}\endgroup %
+% If we encounter &foo, then turn on ()-hacking afterwards
+\newif\ifampseen
+\def\amprm#1 {\ampseentrue{\bf\&#1 }}
+
+\def\parenfont{%
+  \ifampseen
+    % At the first level, print parens in roman,
+    % otherwise use the default font.
+    \ifnum \parencount=1 \rm \fi
+  \else
+    % The \sf parens (in \boldbrax) actually are a little bolder than
+    % the contained text.  This is especially needed for [ and ] .
+    \sf
+  \fi
 }
+\def\infirstlevel#1{%
+  \ifampseen
+    \ifnum\parencount=1
+      #1%
+    \fi
+  \fi
+}
+\def\bfafterword#1 {#1 \bf}
 
-% These definitions are run if you use @defmethodx, etc.,
-% anywhere other than immediately after a @defmethod, etc.
-
-\def\defopx #1 {\errmessage{@defopx in invalid context}}
-\def\defmethodx #1 {\errmessage{@defmethodx in invalid context}}
-\def\defcvx #1 {\errmessage{@defcvx in invalid context}}
-\def\defivarx #1 {\errmessage{@defivarx in invalid context}}
+\def\opnr{%
+  \global\advance\parencount by 1
+  {\parenfont(}%
+  \infirstlevel \bfafterword
+}
+\def\clnr{%
+  {\parenfont)}%
+  \infirstlevel \sl
+  \global\advance\parencount by -1
+}
 
-% Now @defvar
+\newcount\brackcount
+\def\lbrb{%
+  \global\advance\brackcount by 1
+  {\bf[}%
+}
+\def\rbrb{%
+  {\bf]}%
+  \global\advance\brackcount by -1
+}
 
-% First, define the processing that is wanted for arguments of @defvar.
-% This is actually simple: just print them in roman.
-% This must expand the args and terminate the paragraph they make up
-\def\defvarargs #1{\normalparens #1%
-\interlinepenalty=10000
-\endgraf\penalty 10000\vskip -\parskip\penalty 10000}
+\def\checkparencounts{%
+  \ifnum\parencount=0 \else \badparencount \fi
+  \ifnum\brackcount=0 \else \badbrackcount \fi
+}
+\def\badparencount{%
+  \errmessage{Unbalanced parentheses in @def}%
+  \global\parencount=0
+}
+\def\badbrackcount{%
+  \errmessage{Unbalanced square braces in @def}%
+  \global\brackcount=0
+}
 
-% @defvr Counter foo-count
 
-\def\defvr{\defvrparsebody\Edefvr\defvrx\defvrheader}
+\message{macros,}
+% @macro.
+
+% To do this right we need a feature of e-TeX, \scantokens,
+% which we arrange to emulate with a temporary file in ordinary TeX.
+\ifx\eTeXversion\undefined
+  \newwrite\macscribble
+  \def\scantokens#1{%
+    \toks0={#1}%
+    \immediate\openout\macscribble=\jobname.tmp
+    \immediate\write\macscribble{\the\toks0}%
+    \immediate\closeout\macscribble
+    \input \jobname.tmp
+  }
+\fi
 
-\def\defvrheader #1#2#3{\doind {vr}{\code{#2}}%
-\begingroup\defname {#2}{#1}\defvarargs{#3}\endgroup}
+\def\scanmacro#1{%
+  \begingroup
+    \newlinechar`\^^M
+    \let\xeatspaces\eatspaces
+    % Undo catcode changes of \startcontents and \doprintindex
+    % When called from @insertcopying or (short)caption, we need active
+    % backslash to get it printed correctly.  Previously, we had
+    % \catcode`\\=\other instead.  We'll see whether a problem appears
+    % with macro expansion.                            --kasal, 19aug04
+    \catcode`\@=0 \catcode`\\=\active \escapechar=`\@
+    % ... and \example
+    \spaceisspace
+    %
+    % Append \endinput to make sure that TeX does not see the ending newline.
+    %
+    % I've verified that it is necessary both for e-TeX and for ordinary TeX
+    %                                                  --kasal, 29nov03
+    \scantokens{#1\endinput}%
+  \endgroup
+}
 
-% @defvar == @defvr Variable
+\def\scanexp#1{%
+  \edef\temp{\noexpand\scanmacro{#1}}%
+  \temp
+}
 
-\def\defvar{\defvarparsebody\Edefvar\defvarx\defvarheader}
+\newcount\paramno   % Count of parameters
+\newtoks\macname    % Macro name
+\newif\ifrecursive  % Is it recursive?
+
+% List of all defined macros in the form
+%    \definedummyword\macro1\definedummyword\macro2...
+% Currently is also contains all @aliases; the list can be split
+% if there is a need.
+\def\macrolist{}
+
+% Add the macro to \macrolist
+\def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname}
+\def\addtomacrolistxxx#1{%
+     \toks0 = \expandafter{\macrolist\definedummyword#1}%
+     \xdef\macrolist{\the\toks0}%
+}
 
-\def\defvarheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index
-\begingroup\defname {#1}{Variable}%
-\defvarargs {#2}\endgroup %
+% Utility routines.
+% This does \let #1 = #2, with \csnames; that is,
+%   \let \csname#1\endcsname = \csname#2\endcsname
+% (except of course we have to play expansion games).
+% 
+\def\cslet#1#2{%
+  \expandafter\let
+  \csname#1\expandafter\endcsname
+  \csname#2\endcsname
 }
 
-% @defopt == @defvr {User Option}
-
-\def\defopt{\defvarparsebody\Edefopt\defoptx\defoptheader}
-
-\def\defoptheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index
-\begingroup\defname {#1}{User Option}%
-\defvarargs {#2}\endgroup %
+% Trim leading and trailing spaces off a string.
+% Concepts from aro-bend problem 15 (see CTAN).
+{\catcode`\@=11
+\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }}
+\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@}
+\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @}
+\def\unbrace#1{#1}
+\unbrace{\gdef\trim@@@ #1 } #2@{#1}
 }
 
-% @deftypevar int foobar
-
-\def\deftypevar{\defvarparsebody\Edeftypevar\deftypevarx\deftypevarheader}
-
-% #1 is the data type.  #2 is the name.
-\def\deftypevarheader #1#2{%
-\doind {vr}{\code{#2}}% Make entry in variables index
-\begingroup\defname {\defheaderxcond#1\relax$$$#2}{Variable}%
-\interlinepenalty=10000
-\endgraf\penalty 10000\vskip -\parskip\penalty 10000
-\endgroup}
+% Trim a single trailing ^^M off a string.
+{\catcode`\^^M=\other \catcode`\Q=3%
+\gdef\eatcr #1{\eatcra #1Q^^MQ}%
+\gdef\eatcra#1^^MQ{\eatcrb#1Q}%
+\gdef\eatcrb#1Q#2Q{#1}%
+}
 
-% @deftypevr {Global Flag} int enable
+% Macro bodies are absorbed as an argument in a context where
+% all characters are catcode 10, 11 or 12, except \ which is active
+% (as in normal texinfo). It is necessary to change the definition of \.
+
+% It's necessary to have hard CRs when the macro is executed. This is
+% done by  making ^^M (\endlinechar) catcode 12 when reading the macro
+% body, and then making it the \newlinechar in \scanmacro.
+
+\def\scanctxt{%
+  \catcode`\"=\other
+  \catcode`\+=\other
+  \catcode`\<=\other
+  \catcode`\>=\other
+  \catcode`\@=\other
+  \catcode`\^=\other
+  \catcode`\_=\other
+  \catcode`\|=\other
+  \catcode`\~=\other
+}
 
-\def\deftypevr{\defvrparsebody\Edeftypevr\deftypevrx\deftypevrheader}
+\def\scanargctxt{%
+  \scanctxt
+  \catcode`\\=\other
+  \catcode`\^^M=\other
+}
 
-\def\deftypevrheader #1#2#3{\doind {vr}{\code{#3}}%
-\begingroup\defname {\defheaderxcond#2\relax$$$#3}{#1}
-\interlinepenalty=10000
-\endgraf\penalty 10000\vskip -\parskip\penalty 10000
-\endgroup}
+\def\macrobodyctxt{%
+  \scanctxt
+  \catcode`\{=\other
+  \catcode`\}=\other
+  \catcode`\^^M=\other
+  \usembodybackslash
+}
 
-% This definition is run if you use @defvarx
-% anywhere other than immediately after a @defvar or @defvarx.
+\def\macroargctxt{%
+  \scanctxt
+  \catcode`\\=\other
+}
 
-\def\defvrx #1 {\errmessage{@defvrx in invalid context}}
-\def\defvarx #1 {\errmessage{@defvarx in invalid context}}
-\def\defoptx #1 {\errmessage{@defoptx in invalid context}}
-\def\deftypevarx #1 {\errmessage{@deftypevarx in invalid context}}
-\def\deftypevrx #1 {\errmessage{@deftypevrx in invalid context}}
+% \mbodybackslash is the definition of \ in @macro bodies.
+% It maps \foo\ => \csname macarg.foo\endcsname => #N
+% where N is the macro parameter number.
+% We define \csname macarg.\endcsname to be \realbackslash, so
+% \\ in macro replacement text gets you a backslash.
 
-% Now define @deftp
-% Args are printed in bold, a slight difference from @defvar.
+{\catcode`@=0 @catcode`@\=@active
+ @gdef@usembodybackslash{@let\=@mbodybackslash}
+ @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname}
+}
+\expandafter\def\csname macarg.\endcsname{\realbackslash}
 
-\def\deftpargs #1{\bf \defvarargs{#1}}
+\def\macro{\recursivefalse\parsearg\macroxxx}
+\def\rmacro{\recursivetrue\parsearg\macroxxx}
 
-% @deftp Class window height width ...
+\def\macroxxx#1{%
+  \getargs{#1}%           now \macname is the macname and \argl the arglist
+  \ifx\argl\empty       % no arguments
+     \paramno=0%
+  \else
+     \expandafter\parsemargdef \argl;%
+  \fi
+  \if1\csname ismacro.\the\macname\endcsname
+     \message{Warning: redefining \the\macname}%
+  \else
+     \expandafter\ifx\csname \the\macname\endcsname \relax
+     \else \errmessage{Macro name \the\macname\space already defined}\fi
+     \global\cslet{macsave.\the\macname}{\the\macname}%
+     \global\expandafter\let\csname ismacro.\the\macname\endcsname=1%
+     \addtomacrolist{\the\macname}%
+  \fi
+  \begingroup \macrobodyctxt
+  \ifrecursive \expandafter\parsermacbody
+  \else \expandafter\parsemacbody
+  \fi}
+
+\parseargdef\unmacro{%
+  \if1\csname ismacro.#1\endcsname
+    \global\cslet{#1}{macsave.#1}%
+    \global\expandafter\let \csname ismacro.#1\endcsname=0%
+    % Remove the macro name from \macrolist:
+    \begingroup
+      \expandafter\let\csname#1\endcsname \relax
+      \let\definedummyword\unmacrodo
+      \xdef\macrolist{\macrolist}%
+    \endgroup
+  \else
+    \errmessage{Macro #1 not defined}%
+  \fi
+}
 
-\def\deftp{\deftpparsebody\Edeftp\deftpx\deftpheader}
+% Called by \do from \dounmacro on each macro.  The idea is to omit any
+% macro definitions that have been changed to \relax.
+%
+\def\unmacrodo#1{%
+  \ifx #1\relax
+    % remove this
+  \else
+    \noexpand\definedummyword \noexpand#1%
+  \fi
+}
 
-\def\deftpheader #1#2#3{\doind {tp}{\code{#2}}%
-\begingroup\defname {#2}{#1}\deftpargs{#3}\endgroup}
+% This makes use of the obscure feature that if the last token of a
+% <parameter list> is #, then the preceding argument is delimited by
+% an opening brace, and that opening brace is not consumed.
+\def\getargs#1{\getargsxxx#1{}}
+\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs}
+\def\getmacname #1 #2\relax{\macname={#1}}
+\def\getmacargs#1{\def\argl{#1}}
+
+% Parse the optional {params} list.  Set up \paramno and \paramlist
+% so \defmacro knows what to do.  Define \macarg.blah for each blah
+% in the params list, to be ##N where N is the position in that list.
+% That gets used by \mbodybackslash (above).
+
+% We need to get `macro parameter char #' into several definitions.
+% The technique used is stolen from LaTeX:  let \hash be something
+% unexpandable, insert that wherever you need a #, and then redefine
+% it to # just before using the token list produced.
+%
+% The same technique is used to protect \eatspaces till just before
+% the macro is used.
+
+\def\parsemargdef#1;{\paramno=0\def\paramlist{}%
+        \let\hash\relax\let\xeatspaces\relax\parsemargdefxxx#1,;,}
+\def\parsemargdefxxx#1,{%
+  \if#1;\let\next=\relax
+  \else \let\next=\parsemargdefxxx
+    \advance\paramno by 1%
+    \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname
+        {\xeatspaces{\hash\the\paramno}}%
+    \edef\paramlist{\paramlist\hash\the\paramno,}%
+  \fi\next}
+
+% These two commands read recursive and nonrecursive macro bodies.
+% (They're different since rec and nonrec macros end differently.)
+
+\long\def\parsemacbody#1@end macro%
+{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}%
+\long\def\parsermacbody#1@end rmacro%
+{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}%
+
+% This defines the macro itself. There are six cases: recursive and
+% nonrecursive macros of zero, one, and many arguments.
+% Much magic with \expandafter here.
+% \xdef is used so that macro definitions will survive the file
+% they're defined in; @include reads the file inside a group.
+\def\defmacro{%
+  \let\hash=##% convert placeholders to macro parameter chars
+  \ifrecursive
+    \ifcase\paramno
+    % 0
+      \expandafter\xdef\csname\the\macname\endcsname{%
+        \noexpand\scanmacro{\temp}}%
+    \or % 1
+      \expandafter\xdef\csname\the\macname\endcsname{%
+         \bgroup\noexpand\macroargctxt
+         \noexpand\braceorline
+         \expandafter\noexpand\csname\the\macname xxx\endcsname}%
+      \expandafter\xdef\csname\the\macname xxx\endcsname##1{%
+         \egroup\noexpand\scanmacro{\temp}}%
+    \else % many
+      \expandafter\xdef\csname\the\macname\endcsname{%
+         \bgroup\noexpand\macroargctxt
+         \noexpand\csname\the\macname xx\endcsname}%
+      \expandafter\xdef\csname\the\macname xx\endcsname##1{%
+          \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}%
+      \expandafter\expandafter
+      \expandafter\xdef
+      \expandafter\expandafter
+        \csname\the\macname xxx\endcsname
+          \paramlist{\egroup\noexpand\scanmacro{\temp}}%
+    \fi
+  \else
+    \ifcase\paramno
+    % 0
+      \expandafter\xdef\csname\the\macname\endcsname{%
+        \noexpand\norecurse{\the\macname}%
+        \noexpand\scanmacro{\temp}\egroup}%
+    \or % 1
+      \expandafter\xdef\csname\the\macname\endcsname{%
+         \bgroup\noexpand\macroargctxt
+         \noexpand\braceorline
+         \expandafter\noexpand\csname\the\macname xxx\endcsname}%
+      \expandafter\xdef\csname\the\macname xxx\endcsname##1{%
+        \egroup
+        \noexpand\norecurse{\the\macname}%
+        \noexpand\scanmacro{\temp}\egroup}%
+    \else % many
+      \expandafter\xdef\csname\the\macname\endcsname{%
+         \bgroup\noexpand\macroargctxt
+         \expandafter\noexpand\csname\the\macname xx\endcsname}%
+      \expandafter\xdef\csname\the\macname xx\endcsname##1{%
+          \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}%
+      \expandafter\expandafter
+      \expandafter\xdef
+      \expandafter\expandafter
+      \csname\the\macname xxx\endcsname
+      \paramlist{%
+          \egroup
+          \noexpand\norecurse{\the\macname}%
+          \noexpand\scanmacro{\temp}\egroup}%
+    \fi
+  \fi}
+
+\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}}
+
+% \braceorline decides whether the next nonwhitespace character is a
+% {.  If so it reads up to the closing }, if not, it reads the whole
+% line.  Whatever was read is then fed to the next control sequence
+% as an argument (by \parsebrace or \parsearg)
+\def\braceorline#1{\let\next=#1\futurelet\nchar\braceorlinexxx}
+\def\braceorlinexxx{%
+  \ifx\nchar\bgroup\else
+    \expandafter\parsearg
+  \fi \next}
+
+
+% @alias.
+% We need some trickery to remove the optional spaces around the equal
+% sign.  Just make them active and then expand them all to nothing.
+\def\alias{\parseargusing\obeyspaces\aliasxxx}
+\def\aliasxxx #1{\aliasyyy#1\relax}
+\def\aliasyyy #1=#2\relax{%
+  {%
+    \expandafter\let\obeyedspace=\empty
+    \addtomacrolist{#1}%
+    \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}%
+  }%
+  \next
+}
 
-% This definition is run if you use @deftpx, etc
-% anywhere other than immediately after a @deftp, etc.
 
-\def\deftpx #1 {\errmessage{@deftpx in invalid context}}
+\message{cross references,}
 
-\message{cross reference,}
-% Define cross-reference macros
-\newwrite \auxfile
+\newwrite\auxfile
 
-\newif\ifhavexrefs  % True if xref values are known.
+\newif\ifhavexrefs    % True if xref values are known.
 \newif\ifwarnedxrefs  % True if we warned once that they aren't known.
 
-% \setref{foo} defines a cross-reference point named foo.
+% @inforef is relatively simple.
+\def\inforef #1{\inforefzzz #1,,,,**}
+\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}},
+  node \samp{\ignorespaces#1{}}}
 
-\def\setref#1{%
-\dosetq{#1-title}{Ytitle}%
-\dosetq{#1-pg}{Ypagenumber}%
-\dosetq{#1-snt}{Ysectionnumberandtype}}
+% @node's only job in TeX is to define \lastnode, which is used in
+% cross-references.  The @node line might or might not have commas, and
+% might or might not have spaces before the first comma, like:
+% @node foo , bar , ...
+% We don't want such trailing spaces in the node name.
+%
+\parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse}
+%
+% also remove a trailing comma, in case of something like this:
+% @node Help-Cross,  ,  , Cross-refs
+\def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse}
+\def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}}
 
-\def\unnumbsetref#1{%
-\dosetq{#1-title}{Ytitle}%
-\dosetq{#1-pg}{Ypagenumber}%
-\dosetq{#1-snt}{Ynothing}}
+\let\nwnode=\node
+\let\lastnode=\empty
+
+% Write a cross-reference definition for the current node.  #1 is the
+% type (Ynumbered, Yappendix, Ynothing).
+%
+\def\donoderef#1{%
+  \ifx\lastnode\empty\else
+    \setref{\lastnode}{#1}%
+    \global\let\lastnode=\empty
+  \fi
+}
 
-\def\appendixsetref#1{%
-\dosetq{#1-title}{Ytitle}%
-\dosetq{#1-pg}{Ypagenumber}%
-\dosetq{#1-snt}{Yappendixletterandtype}}
+% @anchor{NAME} -- define xref target at arbitrary point.
+%
+\newcount\savesfregister
+%
+\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi}
+\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi}
+\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces}
+
+% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an
+% anchor), which consists of three parts:
+% 1) NAME-title - the current sectioning name taken from \thissection,
+%                 or the anchor name.
+% 2) NAME-snt   - section number and type, passed as the SNT arg, or
+%                 empty for anchors.
+% 3) NAME-pg    - the page number.
+%
+% This is called from \donoderef, \anchor, and \dofloat.  In the case of
+% floats, there is an additional part, which is not written here:
+% 4) NAME-lof   - the text as it should appear in a @listoffloats.
+%
+\def\setref#1#2{%
+  \pdfmkdest{#1}%
+  \iflinks
+    {%
+      \atdummies  % preserve commands, but don't expand them
+      \edef\writexrdef##1##2{%
+       \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef
+         ##1}{##2}}% these are parameters of \writexrdef
+      }%
+      \toks0 = \expandafter{\thissection}%
+      \immediate \writexrdef{title}{\the\toks0 }%
+      \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc.
+      \writexrdef{pg}{\folio}% will be written later, during \shipout
+    }%
+  \fi
+}
 
-% \xref, \pxref, and \ref generate cross-references to specified points.
-% For \xrefX, #1 is the node name, #2 the name of the Info
-% cross-reference, #3 the printed node name, #4 the name of the Info
-% file, #5 the name of the printed manual.  All but the node name can be
-% omitted.
+% @xref, @pxref, and @ref generate cross-references.  For \xrefX, #1 is
+% the node name, #2 the name of the Info cross-reference, #3 the printed
+% node name, #4 the name of the Info file, #5 the name of the printed
+% manual.  All but the node name can be omitted.
 %
 \def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]}
 \def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]}
 \def\ref#1{\xrefX[#1,,,,,,,]}
 \def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup
+  \unsepspaces
   \def\printedmanual{\ignorespaces #5}%
-  \def\printednodename{\ignorespaces #3}%
-  \setbox1=\hbox{\printedmanual}%
-  \setbox0=\hbox{\printednodename}%
+  \def\printedrefname{\ignorespaces #3}%
+  \setbox1=\hbox{\printedmanual\unskip}%
+  \setbox0=\hbox{\printedrefname\unskip}%
   \ifdim \wd0 = 0pt
     % No printed node name was explicitly given.
-    \ifx\SETxref-automatic-section-title\relax %
+    \expandafter\ifx\csname SETxref-automatic-section-title\endcsname\relax
+      % Use the node name inside the square brackets.
+      \def\printedrefname{\ignorespaces #1}%
+    \else
       % Use the actual chapter/section title appear inside
       % the square brackets.  Use the real section title if we have it.
-      \ifdim \wd1>0pt%
+      \ifdim \wd1 > 0pt
         % It is in another manual, so we don't have it.
-        \def\printednodename{\ignorespaces #1}%
+        \def\printedrefname{\ignorespaces #1}%
       \else
         \ifhavexrefs
           % We know the real title if we have the xref values.
-          \def\printednodename{\refx{#1-title}}%
+          \def\printedrefname{\refx{#1-title}{}}%
         \else
           % Otherwise just copy the Info node name.
-          \def\printednodename{\ignorespaces #1}%
+          \def\printedrefname{\ignorespaces #1}%
         \fi%
       \fi
-      \def\printednodename{#1-title}%
-    \else
-      % Use the node name inside the square brackets.
-      \def\printednodename{\ignorespaces #1}%
     \fi
   \fi
   %
-  % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not
-  % insert empty discretionaries after hyphens, which means that it will
-  % not find a line break at a hyphen in a node names.  Since some manuals
-  % are best written with fairly long node names, containing hyphens, this
-  % is a loss.  Therefore, we give the text of the node name again, so it
-  % is as if TeX is seeing it for the first time.
-  \ifdim \wd1 > 0pt
-    \putwordsection{} ``\printednodename'' in \cite{\printedmanual}%
+  % Make link in pdf output.
+  \ifpdf
+    \leavevmode
+    \getfilename{#4}%
+    {\turnoffactive
+     % See comments at \activebackslashdouble.
+     {\activebackslashdouble \xdef\pdfxrefdest{#1}%
+      \backslashparens\pdfxrefdest}%
+     %
+     \ifnum\filenamelength>0
+       \startlink attr{/Border [0 0 0]}%
+         goto file{\the\filename.pdf} name{\pdfxrefdest}%
+     \else
+       \startlink attr{/Border [0 0 0]}%
+         goto name{\pdfmkpgn{\pdfxrefdest}}%
+     \fi
+    }%
+    \linkcolor
+  \fi
+  %
+  % Float references are printed completely differently: "Figure 1.2"
+  % instead of "[somenode], p.3".  We distinguish them by the
+  % LABEL-title being set to a magic string.
+  {%
+    % Have to otherify everything special to allow the \csname to
+    % include an _ in the xref name, etc.
+    \indexnofonts
+    \turnoffactive
+    \expandafter\global\expandafter\let\expandafter\Xthisreftitle
+      \csname XR#1-title\endcsname
+  }%
+  \iffloat\Xthisreftitle
+    % If the user specified the print name (third arg) to the ref,
+    % print it instead of our usual "Figure 1.2".
+    \ifdim\wd0 = 0pt
+      \refx{#1-snt}{}%
+    \else
+      \printedrefname
+    \fi
+    %
+    % if the user also gave the printed manual name (fifth arg), append
+    % "in MANUALNAME".
+    \ifdim \wd1 > 0pt
+      \space \putwordin{} \cite{\printedmanual}%
+    \fi
   \else
-    % _ (for example) has to be the character _ for the purposes of the
-    % control sequence corresponding to the node, but it has to expand
-    % into the usual \leavevmode...\vrule stuff for purposes of
-    % printing. So we \turnoffactive for the \refx-snt, back on for the
-    % printing, back off for the \refx-pg.
-    {\turnoffactive \refx{#1-snt}{}}%
-    \space [\printednodename],\space
-    \turnoffactive \putwordpage\tie\refx{#1-pg}{}%
+    % node/anchor (non-float) references.
+    %
+    % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not
+    % insert empty discretionaries after hyphens, which means that it will
+    % not find a line break at a hyphen in a node names.  Since some manuals
+    % are best written with fairly long node names, containing hyphens, this
+    % is a loss.  Therefore, we give the text of the node name again, so it
+    % is as if TeX is seeing it for the first time.
+    \ifdim \wd1 > 0pt
+      \putwordsection{} ``\printedrefname'' \putwordin{} \cite{\printedmanual}%
+    \else
+      % _ (for example) has to be the character _ for the purposes of the
+      % control sequence corresponding to the node, but it has to expand
+      % into the usual \leavevmode...\vrule stuff for purposes of
+      % printing. So we \turnoffactive for the \refx-snt, back on for the
+      % printing, back off for the \refx-pg.
+      {\turnoffactive
+       % Only output a following space if the -snt ref is nonempty; for
+       % @unnumbered and @anchor, it won't be.
+       \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}%
+       \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi
+      }%
+      % output the `[mynode]' via a macro so it can be overridden.
+      \xrefprintnodename\printedrefname
+      %
+      % But we always want a comma and a space:
+      ,\space
+      %
+      % output the `page 3'.
+      \turnoffactive \putwordpage\tie\refx{#1-pg}{}%
+    \fi
   \fi
+  \endlink
 \endgroup}
 
-% \dosetq is the interface for calls from other macros
-
-% Use \turnoffactive so that punctuation chars such as underscore
-% work in node names.
-\def\dosetq #1#2{{\let\folio=0 \turnoffactive%
-\edef\next{\write\auxfile{\internalsetq {#1}{#2}}}%
-\next}}
-
-% \internalsetq {foo}{page} expands into
-% CHARACTERS 'xrdef {foo}{...expansion of \Ypage...}
-% When the aux file is read, ' is the escape character
-
-\def\internalsetq #1#2{'xrdef {#1}{\csname #2\endcsname}}
-
-% Things to be expanded by \internalsetq
-
-\def\Ypagenumber{\folio}
-
-\def\Ytitle{\thissection}
-
-\def\Ynothing{}
-
-\def\Ysectionnumberandtype{%
-\ifnum\secno=0 \putwordChapter\xreftie\the\chapno %
-\else \ifnum \subsecno=0 \putwordSection\xreftie\the\chapno.\the\secno %
-\else \ifnum \subsubsecno=0 %
-\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno %
-\else %
-\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno %
-\fi \fi \fi }
-
-\def\Yappendixletterandtype{%
-\ifnum\secno=0 \putwordAppendix\xreftie'char\the\appendixno{}%
-\else \ifnum \subsecno=0 \putwordSection\xreftie'char\the\appendixno.\the\secno %
-\else \ifnum \subsubsecno=0 %
-\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno %
-\else %
-\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno %
-\fi \fi \fi }
-
-\gdef\xreftie{'tie}
+% This macro is called from \xrefX for the `[nodename]' part of xref
+% output.  It's a separate macro only so it can be changed more easily,
+% since square brackets don't work well in some documents.  Particularly
+% one that Bob is working on :).
+%
+\def\xrefprintnodename#1{[#1]}
 
-% Use TeX 3.0's \inputlineno to get the line number, for better error
-% messages, but if we're using an old version of TeX, don't do anything.
+% Things referred to by \setref.
 %
-\ifx\inputlineno\thisisundefined
-  \let\linenumber = \empty % Non-3.0.
-\else
-  \def\linenumber{\the\inputlineno:\space}
-\fi
+\def\Ynothing{}
+\def\Yomitfromtoc{}
+\def\Ynumbered{%
+  \ifnum\secno=0
+    \putwordChapter@tie \the\chapno
+  \else \ifnum\subsecno=0
+    \putwordSection@tie \the\chapno.\the\secno
+  \else \ifnum\subsubsecno=0
+    \putwordSection@tie \the\chapno.\the\secno.\the\subsecno
+  \else
+    \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno
+  \fi\fi\fi
+}
+\def\Yappendix{%
+  \ifnum\secno=0
+     \putwordAppendix@tie @char\the\appendixno{}%
+  \else \ifnum\subsecno=0
+     \putwordSection@tie @char\the\appendixno.\the\secno
+  \else \ifnum\subsubsecno=0
+    \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno
+  \else
+    \putwordSection@tie
+      @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno
+  \fi\fi\fi
+}
 
 % Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME.
 % If its value is nonempty, SUFFIX is output afterward.
-
+%
 \def\refx#1#2{%
-  \expandafter\ifx\csname X#1\endcsname\relax
+  {%
+    \indexnofonts
+    \otherbackslash
+    \expandafter\global\expandafter\let\expandafter\thisrefX
+      \csname XR#1\endcsname
+  }%
+  \ifx\thisrefX\relax
     % If not defined, say something at least.
-    $\langle$un\-de\-fined$\rangle$%
-    \ifhavexrefs
-      \message{\linenumber Undefined cross reference `#1'.}%
-    \else
-      \ifwarnedxrefs\else
-        \global\warnedxrefstrue
-        \message{Cross reference values unknown; you must run TeX again.}%
+    \angleleft un\-de\-fined\angleright
+    \iflinks
+      \ifhavexrefs
+        \message{\linenumber Undefined cross reference `#1'.}%
+      \else
+        \ifwarnedxrefs\else
+          \global\warnedxrefstrue
+          \message{Cross reference values unknown; you must run TeX again.}%
+        \fi
       \fi
     \fi
   \else
     % It's defined, so just use it.
-    \csname X#1\endcsname
+    \thisrefX
   \fi
   #2% Output the suffix in any case.
 }
 
+% This is the macro invoked by entries in the aux file.  Usually it's
+% just a \def (we prepend XR to the control sequence name to avoid
+% collisions).  But if this is a float type, we have more work to do.
+%
+\def\xrdef#1#2{%
+  \expandafter\gdef\csname XR#1\endcsname{#2}% remember this xref value.
+  %
+  % Was that xref control sequence that we just defined for a float?
+  \expandafter\iffloat\csname XR#1\endcsname
+    % it was a float, and we have the (safe) float type in \iffloattype.
+    \expandafter\let\expandafter\floatlist
+      \csname floatlist\iffloattype\endcsname
+    %
+    % Is this the first time we've seen this float type?
+    \expandafter\ifx\floatlist\relax
+      \toks0 = {\do}% yes, so just \do
+    \else
+      % had it before, so preserve previous elements in list.
+      \toks0 = \expandafter{\floatlist\do}%
+    \fi
+    %
+    % Remember this xref in the control sequence \floatlistFLOATTYPE,
+    % for later use in \listoffloats.
+    \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0{#1}}%
+  \fi
+}
+
 % Read the last existing aux file, if any.  No error if none exists.
+%
+\def\tryauxfile{%
+  \openin 1 \jobname.aux
+  \ifeof 1 \else
+    \readdatafile{aux}%
+    \global\havexrefstrue
+  \fi
+  \closein 1
+}
 
-% This is the macro invoked by entries in the aux file.
-\def\xrdef #1#2{
-{\catcode`\'=\other\expandafter \gdef \csname X#1\endcsname {#2}}}
+\def\setupdatafile{%
+  \catcode`\^^@=\other
+  \catcode`\^^A=\other
+  \catcode`\^^B=\other
+  \catcode`\^^C=\other
+  \catcode`\^^D=\other
+  \catcode`\^^E=\other
+  \catcode`\^^F=\other
+  \catcode`\^^G=\other
+  \catcode`\^^H=\other
+  \catcode`\^^K=\other
+  \catcode`\^^L=\other
+  \catcode`\^^N=\other
+  \catcode`\^^P=\other
+  \catcode`\^^Q=\other
+  \catcode`\^^R=\other
+  \catcode`\^^S=\other
+  \catcode`\^^T=\other
+  \catcode`\^^U=\other
+  \catcode`\^^V=\other
+  \catcode`\^^W=\other
+  \catcode`\^^X=\other
+  \catcode`\^^Z=\other
+  \catcode`\^^[=\other
+  \catcode`\^^\=\other
+  \catcode`\^^]=\other
+  \catcode`\^^^=\other
+  \catcode`\^^_=\other
+  % It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc.
+  % in xref tags, i.e., node names.  But since ^^e4 notation isn't
+  % supported in the main text, it doesn't seem desirable.  Furthermore,
+  % that is not enough: for node names that actually contain a ^
+  % character, we would end up writing a line like this: 'xrdef {'hat
+  % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first
+  % argument, and \hat is not an expandable control sequence.  It could
+  % all be worked out, but why?  Either we support ^^ or we don't.
+  %
+  % The other change necessary for this was to define \auxhat:
+  % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter
+  % and then to call \auxhat in \setq.
+  %
+  \catcode`\^=\other
+  %
+  % Special characters.  Should be turned off anyway, but...
+  \catcode`\~=\other
+  \catcode`\[=\other
+  \catcode`\]=\other
+  \catcode`\"=\other
+  \catcode`\_=\other
+  \catcode`\|=\other
+  \catcode`\<=\other
+  \catcode`\>=\other
+  \catcode`\$=\other
+  \catcode`\#=\other
+  \catcode`\&=\other
+  \catcode`\%=\other
+  \catcode`+=\other % avoid \+ for paranoia even though we've turned it off
+  %
+  % This is to support \ in node names and titles, since the \
+  % characters end up in a \csname.  It's easier than
+  % leaving it active and making its active definition an actual \
+  % character.  What I don't understand is why it works in the *value*
+  % of the xrdef.  Seems like it should be a catcode12 \, and that
+  % should not typeset properly.  But it works, so I'm moving on for
+  % now.  --karl, 15jan04.
+  \catcode`\\=\other
+  %
+  % Make the characters 128-255 be printing characters.
+  {%
+    \count1=128
+    \def\loop{%
+      \catcode\count1=\other
+      \advance\count1 by 1
+      \ifnum \count1<256 \loop \fi
+    }%
+  }%
+  %
+  % @ is our escape character in .aux files, and we need braces.
+  \catcode`\{=1
+  \catcode`\}=2
+  \catcode`\@=0
+}
 
-\def\readauxfile{%
+\def\readdatafile#1{%
 \begingroup
-\catcode `\^^@=\other
-\catcode `\\ 1=\other
-\catcode `\\ 2=\other
-\catcode `\^^C=\other
-\catcode `\^^D=\other
-\catcode `\^^E=\other
-\catcode `\^^F=\other
-\catcode `\^^G=\other
-\catcode `\^^H=\other
-\catcode `\\v=\other
-\catcode `\^^L=\other
-\catcode `\\ e=\other
-\catcode `\\ f=\other
-\catcode `\\10=\other
-\catcode `\\11=\other
-\catcode `\\12=\other
-\catcode `\\13=\other
-\catcode `\\14=\other
-\catcode `\\15=\other
-\catcode `\\16=\other
-\catcode `\\17=\other
-\catcode `\\18=\other
-\catcode `\\19=\other
-\catcode 26=\other
-\catcode `\^^[=\other
-\catcode `\^^\=\other
-\catcode `\^^]=\other
-\catcode `\^^^=\other
-\catcode `\^^_=\other
-\catcode `\@=\other
-\catcode `\^=\other
-\catcode `\~=\other
-\catcode `\[=\other
-\catcode `\]=\other
-\catcode`\"=\other
-\catcode`\_=\other
-\catcode`\|=\other
-\catcode`\<=\other
-\catcode`\>=\other
-\catcode `\$=\other
-\catcode `\#=\other
-\catcode `\&=\other
-% `\+ does not work, so use 43.
-\catcode 43=\other
-% Make the characters 128-255 be printing characters
-{%
-  \count 1=128
-  \def\loop{%
-    \catcode\count 1=\other
-    \advance\count 1 by 1
-    \ifnum \count 1<256 \loop \fi
-  }%
-}%
-% the aux file uses ' as the escape.
-% Turn off \ as an escape so we do not lose on
-% entries which were dumped with control sequences in their names.
-% For example, 'xrdef {$\leq $-fun}{page ...} made by @defun ^^
-% Reference to such entries still does not work the way one would wish,
-% but at least they do not bomb out when the aux file is read in.
-\catcode `\{=1 \catcode `\}=2
-\catcode `\%=\other
-\catcode `\'=0
-\catcode `\\=\other
-\openin 1 \jobname.aux
-\ifeof 1 \else \closein 1 \input \jobname.aux \global\havexrefstrue
-\global\warnedobstrue
-\fi
-% Open the new aux file.  Tex will close it automatically at exit.
-\openout \auxfile=\jobname.aux
+  \setupdatafile
+  \input\jobname.#1
 \endgroup}
 
-
-% Footnotes.
+\message{insertions,}
+% including footnotes.
 
 \newcount \footnoteno
 
 % The trailing space in the following definition for supereject is
 % vital for proper filling; pages come out unaligned when you do a
 % pagealignmacro call if that space before the closing brace is
-% removed.
+% removed. (Generally, numeric constants should always be followed by a
+% space to prevent strange expansion errors.)
 \def\supereject{\par\penalty -20000\footnoteno =0 }
 
-% @footnotestyle is meaningful for info output only..
+% @footnotestyle is meaningful for info output only.
 \let\footnotestyle=\comment
 
-\let\ptexfootnote=\footnote
-
 {\catcode `\@=11
 %
 % Auto-number footnotes.  Otherwise like plain.
 \gdef\footnote{%
+  \let\indent=\ptexindent
+  \let\noindent=\ptexnoindent
   \global\advance\footnoteno by \@ne
   \edef\thisfootno{$^{\the\footnoteno}$}%
   %
   % In case the footnote comes at the end of a sentence, preserve the
   % extra spacing after we do the footnote number.
   \let\@sf\empty
-  \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi
+  \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi
   %
   % Remove inadvertent blank space before typesetting the footnote number.
   \unskip
   \thisfootno\@sf
-  \footnotezzz
+  \dofootnote
 }%
 
 % Don't bother with the trickery in plain.tex to not require the
 % footnote text as a parameter.  Our footnotes don't need to be so general.
 %
-\long\gdef\footnotezzz#1{\insert\footins{%
+% Oh yes, they do; otherwise, @ifset (and anything else that uses
+% \parseargline) fails inside footnotes because the tokens are fixed when
+% the footnote is read.  --karl, 16nov96.
+%
+\gdef\dofootnote{%
+  \insert\footins\bgroup
   % We want to typeset this text as a normal paragraph, even if the
   % footnote reference occurs in (for example) a display environment.
   % So reset some parameters.
+  \hsize=\pagewidth
   \interlinepenalty\interfootnotelinepenalty
   \splittopskip\ht\strutbox % top baseline for broken footnotes
   \splitmaxdepth\dp\strutbox
@@ -4037,97 +6393,466 @@ July\or August\or September\or October\or November\or December\fi
   \xspaceskip\z@skip
   \parindent\defaultparindent
   %
-  % Hang the footnote text off the number.
-  \hang
+  \smallfonts \rm
+  %
+  % Because we use hanging indentation in footnotes, a @noindent appears
+  % to exdent this text, so make it be a no-op.  makeinfo does not use
+  % hanging indentation so @noindent can still be needed within footnote
+  % text after an @example or the like (not that this is good style).
+  \let\noindent = \relax
+  %
+  % Hang the footnote text off the number.  Use \everypar in case the
+  % footnote extends for more than one paragraph.
+  \everypar = {\hang}%
   \textindent{\thisfootno}%
   %
   % Don't crash into the line above the footnote text.  Since this
   % expands into a box, it must come within the paragraph, lest it
   % provide a place where TeX can split the footnote.
   \footstrut
-  #1\strut}%
+  \futurelet\next\fo@t
 }
-
 }%end \catcode `\@=11
 
-% Set the baselineskip to #1, and the lineskip and strut size
-% correspondingly.  There is no deep meaning behind these magic numbers
-% used as factors; they just match (closely enough) what Knuth defined.
+% In case a @footnote appears in a vbox, save the footnote text and create
+% the real \insert just after the vbox finished.  Otherwise, the insertion
+% would be lost.
+% Similarily, if a @footnote appears inside an alignment, save the footnote
+% text to a box and make the \insert when a row of the table is finished.
+% And the same can be done for other insert classes.  --kasal, 16nov03.
+
+% Replace the \insert primitive by a cheating macro.
+% Deeper inside, just make sure that the saved insertions are not spilled
+% out prematurely.
 %
-\def\lineskipfactor{.08333}
-\def\strutheightpercent{.70833}
-\def\strutdepthpercent {.29167}
+\def\startsavinginserts{%
+  \ifx \insert\ptexinsert
+    \let\insert\saveinsert
+  \else
+    \let\checkinserts\relax
+  \fi
+}
+
+% This \insert replacement works for both \insert\footins{foo} and
+% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}.
 %
-\def\setleading#1{%
-  \normalbaselineskip = #1\relax
-  \normallineskip = \lineskipfactor\normalbaselineskip
-  \normalbaselines
-  \setbox\strutbox =\hbox{%
-    \vrule width0pt height\strutheightpercent\baselineskip
-                    depth \strutdepthpercent \baselineskip
-  }%
+\def\saveinsert#1{%
+  \edef\next{\noexpand\savetobox \makeSAVEname#1}%
+  \afterassignment\next
+  % swallow the left brace
+  \let\temp =
 }
+\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}}
+\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1}
 
-% @| inserts a changebar to the left of the current line.  It should
-% surround any changed text.  This approach does *not* work if the
-% change spans more than two lines of output.  To handle that, we would
-% have adopt a much more difficult approach (putting marks into the main
-% vertical list for the beginning and end of each change).
+\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi}
+
+\def\placesaveins#1{%
+  \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname
+    {\box#1}%
+}
+
+% eat @SAVE -- beware, all of them have catcode \other:
+{
+  \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials  %  ;-)
+  \gdef\gobblesave @SAVE{}
+}
+
+% initialization:
+\def\newsaveins #1{%
+  \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}%
+  \next
+}
+\def\newsaveinsX #1{%
+  \csname newbox\endcsname #1%
+  \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts
+    \checksaveins #1}%
+}
+
+% initialize:
+\let\checkinserts\empty
+\newsaveins\footins
+\newsaveins\margin
+
+
+% @image.  We use the macros from epsf.tex to support this.
+% If epsf.tex is not installed and @image is used, we complain.
 %
-\def\|{%
-  % \vadjust can only be used in horizontal mode.
-  \leavevmode
+% Check for and read epsf.tex up front.  If we read it only at @image
+% time, we might be inside a group, and then its definitions would get
+% undone and the next image would fail.
+\openin 1 = epsf.tex
+\ifeof 1 \else
+  % Do not bother showing banner with epsf.tex v2.7k (available in
+  % doc/epsf.tex and on ctan).
+  \def\epsfannounce{\toks0 = }%
+  \input epsf.tex
+\fi
+\closein 1
+%
+% We will only complain once about lack of epsf.tex.
+\newif\ifwarnednoepsf
+\newhelp\noepsfhelp{epsf.tex must be installed for images to
+  work.  It is also included in the Texinfo distribution, or you can get
+  it from ftp://tug.org/tex/epsf.tex.}
+%
+\def\image#1{%
+  \ifx\epsfbox\undefined
+    \ifwarnednoepsf \else
+      \errhelp = \noepsfhelp
+      \errmessage{epsf.tex not found, images will be ignored}%
+      \global\warnednoepsftrue
+    \fi
+  \else
+    \imagexxx #1,,,,,\finish
+  \fi
+}
+%
+% Arguments to @image:
+% #1 is (mandatory) image filename; we tack on .eps extension.
+% #2 is (optional) width, #3 is (optional) height.
+% #4 is (ignored optional) html alt text.
+% #5 is (ignored optional) extension.
+% #6 is just the usual extra ignored arg for parsing this stuff.
+\newif\ifimagevmode
+\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup
+  \catcode`\^^M = 5     % in case we're inside an example
+  \normalturnoffactive  % allow _ et al. in names
+  % If the image is by itself, center it.
+  \ifvmode
+    \imagevmodetrue
+    \nobreak\bigskip
+    % Usually we'll have text after the image which will insert
+    % \parskip glue, so insert it here too to equalize the space
+    % above and below.
+    \nobreak\vskip\parskip
+    \nobreak
+    \line\bgroup
+  \fi
   %
-  % Append this vertical mode material after the current line in the output.
-  \vadjust{%
-    % We want to insert a rule with the height and depth of the current
-    % leading; that is exactly what \strutbox is supposed to record.
-    \vskip-\baselineskip
+  % Output the image.
+  \ifpdf
+    \dopdfimage{#1}{#2}{#3}%
+  \else
+    % \epsfbox itself resets \epsf?size at each figure.
+    \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi
+    \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi
+    \epsfbox{#1.eps}%
+  \fi
+  %
+  \ifimagevmode \egroup \bigbreak \fi  % space after the image
+\endgroup}
+
+
+% @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables,
+% etc.  We don't actually implement floating yet, we always include the
+% float "here".  But it seemed the best name for the future.
+%
+\envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish}
+
+% There may be a space before second and/or third parameter; delete it.
+\def\eatcommaspace#1, {#1,}
+
+% #1 is the optional FLOATTYPE, the text label for this float, typically
+% "Figure", "Table", "Example", etc.  Can't contain commas.  If omitted,
+% this float will not be numbered and cannot be referred to.
+%
+% #2 is the optional xref label.  Also must be present for the float to
+% be referable.
+%
+% #3 is the optional positioning argument; for now, it is ignored.  It
+% will somehow specify the positions allowed to float to (here, top, bottom).
+%
+% We keep a separate counter for each FLOATTYPE, which we reset at each
+% chapter-level command.
+\let\resetallfloatnos=\empty
+%
+\def\dofloat#1,#2,#3,#4\finish{%
+  \let\thiscaption=\empty
+  \let\thisshortcaption=\empty
+  %
+  % don't lose footnotes inside @float.
+  %
+  % BEWARE: when the floats start float, we have to issue warning whenever an
+  % insert appears inside a float which could possibly float. --kasal, 26may04
+  %
+  \startsavinginserts
+  %
+  % We can't be used inside a paragraph.
+  \par
+  %
+  \vtop\bgroup
+    \def\floattype{#1}%
+    \def\floatlabel{#2}%
+    \def\floatloc{#3}% we do nothing with this yet.
     %
-    % \vadjust-items are inserted at the left edge of the type.  So
-    % the \llap here moves out into the left-hand margin.
-    \llap{%
+    \ifx\floattype\empty
+      \let\safefloattype=\empty
+    \else
+      {%
+        % the floattype might have accents or other special characters,
+        % but we need to use it in a control sequence name.
+        \indexnofonts
+        \turnoffactive
+        \xdef\safefloattype{\floattype}%
+      }%
+    \fi
+    %
+    % If label is given but no type, we handle that as the empty type.
+    \ifx\floatlabel\empty \else
+      % We want each FLOATTYPE to be numbered separately (Figure 1,
+      % Table 1, Figure 2, ...).  (And if no label, no number.)
       %
-      % For a thicker or thinner bar, change the `1pt'.
-      \vrule height\baselineskip width1pt
+      \expandafter\getfloatno\csname\safefloattype floatno\endcsname
+      \global\advance\floatno by 1
       %
-      % This is the space between the bar and the text.
-      \hskip 12pt
-    }%
-  }%
+      {%
+        % This magic value for \thissection is output by \setref as the
+        % XREFLABEL-title value.  \xrefX uses it to distinguish float
+        % labels (which have a completely different output format) from
+        % node and anchor labels.  And \xrdef uses it to construct the
+        % lists of floats.
+        %
+        \edef\thissection{\floatmagic=\safefloattype}%
+        \setref{\floatlabel}{Yfloat}%
+      }%
+    \fi
+    %
+    % start with \parskip glue, I guess.
+    \vskip\parskip
+    %
+    % Don't suppress indentation if a float happens to start a section.
+    \restorefirstparagraphindent
 }
 
-% For a final copy, take out the rectangles
-% that mark overfull boxes (in case you have decided
-% that the text looks ok even though it passes the margin).
+% we have these possibilities:
+% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap
+% @float Foo,lbl & no caption:    Foo 1.1
+% @float Foo & @caption{Cap}:     Foo: Cap
+% @float Foo & no caption:        Foo
+% @float ,lbl & Caption{Cap}:     1.1: Cap
+% @float ,lbl & no caption:       1.1
+% @float & @caption{Cap}:         Cap
+% @float & no caption:
 %
-\def\finalout{\overfullrule=0pt}
+\def\Efloat{%
+    \let\floatident = \empty
+    %
+    % In all cases, if we have a float type, it comes first.
+    \ifx\floattype\empty \else \def\floatident{\floattype}\fi
+    %
+    % If we have an xref label, the number comes next.
+    \ifx\floatlabel\empty \else
+      \ifx\floattype\empty \else % if also had float type, need tie first.
+        \appendtomacro\floatident{\tie}%
+      \fi
+      % the number.
+      \appendtomacro\floatident{\chaplevelprefix\the\floatno}%
+    \fi
+    %
+    % Start the printed caption with what we've constructed in
+    % \floatident, but keep it separate; we need \floatident again.
+    \let\captionline = \floatident
+    %
+    \ifx\thiscaption\empty \else
+      \ifx\floatident\empty \else
+       \appendtomacro\captionline{: }% had ident, so need a colon between
+      \fi
+      %
+      % caption text.
+      \appendtomacro\captionline{\scanexp\thiscaption}%
+    \fi
+    %
+    % If we have anything to print, print it, with space before.
+    % Eventually this needs to become an \insert.
+    \ifx\captionline\empty \else
+      \vskip.5\parskip
+      \captionline
+      %
+      % Space below caption.
+      \vskip\parskip
+    \fi
+    %
+    % If have an xref label, write the list of floats info.  Do this
+    % after the caption, to avoid chance of it being a breakpoint.
+    \ifx\floatlabel\empty \else
+      % Write the text that goes in the lof to the aux file as
+      % \floatlabel-lof.  Besides \floatident, we include the short
+      % caption if specified, else the full caption if specified, else nothing.
+      {%
+        \atdummies
+        %
+        % since we read the caption text in the macro world, where ^^M
+        % is turned into a normal character, we have to scan it back, so
+        % we don't write the literal three characters "^^M" into the aux file.
+       \scanexp{%
+         \xdef\noexpand\gtemp{%
+           \ifx\thisshortcaption\empty
+             \thiscaption
+           \else
+             \thisshortcaption
+           \fi
+         }%
+       }%
+        \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident
+         \ifx\gtemp\empty \else : \gtemp \fi}}%
+      }%
+    \fi
+  \egroup  % end of \vtop
+  %
+  % place the captured inserts
+  %
+  % BEWARE: when the floats start floating, we have to issue warning
+  % whenever an insert appears inside a float which could possibly
+  % float. --kasal, 26may04
+  %
+  \checkinserts
+}
 
+% Append the tokens #2 to the definition of macro #1, not expanding either.
+%
+\def\appendtomacro#1#2{%
+  \expandafter\def\expandafter#1\expandafter{#1#2}%
+}
 
-% End of control word definitions.
+% @caption, @shortcaption
+%
+\def\caption{\docaption\thiscaption}
+\def\shortcaption{\docaption\thisshortcaption}
+\def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption}
+\def\defcaption#1#2{\egroup \def#1{#2}}
+
+% The parameter is the control sequence identifying the counter we are
+% going to use.  Create it if it doesn't exist and assign it to \floatno.
+\def\getfloatno#1{%
+  \ifx#1\relax
+      % Haven't seen this figure type before.
+      \csname newcount\endcsname #1%
+      %
+      % Remember to reset this floatno at the next chap.
+      \expandafter\gdef\expandafter\resetallfloatnos
+        \expandafter{\resetallfloatnos #1=0 }%
+  \fi
+  \let\floatno#1%
+}
 
-\message{and turning on texinfo input format.}
+% \setref calls this to get the XREFLABEL-snt value.  We want an @xref
+% to the FLOATLABEL to expand to "Figure 3.1".  We call \setref when we
+% first read the @float command.
+%
+\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}%
 
-\def\openindices{%
-   \newindex{cp}%
-   \newcodeindex{fn}%
-   \newcodeindex{vr}%
-   \newcodeindex{tp}%
-   \newcodeindex{ky}%
-   \newcodeindex{pg}%
+% Magic string used for the XREFLABEL-title value, so \xrefX can
+% distinguish floats from other xref types.
+\def\floatmagic{!!float!!}
+
+% #1 is the control sequence we are passed; we expand into a conditional
+% which is true if #1 represents a float ref.  That is, the magic
+% \thissection value which we \setref above.
+%
+\def\iffloat#1{\expandafter\doiffloat#1==\finish}
+%
+% #1 is (maybe) the \floatmagic string.  If so, #2 will be the
+% (safe) float type for this float.  We set \iffloattype to #2.
+%
+\def\doiffloat#1=#2=#3\finish{%
+  \def\temp{#1}%
+  \def\iffloattype{#2}%
+  \ifx\temp\floatmagic
+}
+
+% @listoffloats FLOATTYPE - print a list of floats like a table of contents.
+%
+\parseargdef\listoffloats{%
+  \def\floattype{#1}% floattype
+  {%
+    % the floattype might have accents or other special characters,
+    % but we need to use it in a control sequence name.
+    \indexnofonts
+    \turnoffactive
+    \xdef\safefloattype{\floattype}%
+  }%
+  %
+  % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE.
+  \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax
+    \ifhavexrefs
+      % if the user said @listoffloats foo but never @float foo.
+      \message{\linenumber No `\safefloattype' floats to list.}%
+    \fi
+  \else
+    \begingroup
+      \leftskip=\tocindent  % indent these entries like a toc
+      \let\do=\listoffloatsdo
+      \csname floatlist\safefloattype\endcsname
+    \endgroup
+  \fi
+}
+
+% This is called on each entry in a list of floats.  We're passed the
+% xref label, in the form LABEL-title, which is how we save it in the
+% aux file.  We strip off the -title and look up \XRLABEL-lof, which
+% has the text we're supposed to typeset here.
+%
+% Figures without xref labels will not be included in the list (since
+% they won't appear in the aux file).
+%
+\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish}
+\def\listoffloatsdoentry#1-title\finish{{%
+  % Can't fully expand XR#1-lof because it can contain anything.  Just
+  % pass the control sequence.  On the other hand, XR#1-pg is just the
+  % page number, and we want to fully expand that so we can get a link
+  % in pdf output.
+  \toksA = \expandafter{\csname XR#1-lof\endcsname}%
+  %
+  % use the same \entry macro we use to generate the TOC and index.
+  \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}%
+  \writeentry
+}}
+
+\message{localization,}
+% and i18n.
+
+% @documentlanguage is usually given very early, just after
+% @setfilename.  If done too late, it may not override everything
+% properly.  Single argument is the language abbreviation.
+% It would be nice if we could set up a hyphenation file here.
+%
+\parseargdef\documentlanguage{%
+  \tex % read txi-??.tex file in plain TeX.
+    % Read the file if it exists.
+    \openin 1 txi-#1.tex
+    \ifeof 1
+      \errhelp = \nolanghelp
+      \errmessage{Cannot read language file txi-#1.tex}%
+    \else
+      \input txi-#1.tex
+    \fi
+    \closein 1
+  \endgroup
 }
+\newhelp\nolanghelp{The given language definition file cannot be found or
+is empty.  Maybe you need to install it?  In the current directory
+should work if nowhere else does.}
+
+
+% @documentencoding should change something in TeX eventually, most
+% likely, but for now just recognize it.
+\let\documentencoding = \comment
 
-% Set some numeric style parameters, for 8.5 x 11 format.
 
-%\hsize = 6.5in
+% Page size parameters.
+%
 \newdimen\defaultparindent \defaultparindent = 15pt
-\parindent = \defaultparindent
-\parskip 18pt plus 1pt
-\setleading{15pt}
-\advance\topskip by 1.2cm
+
+\chapheadingskip = 15pt plus 4pt minus 2pt
+\secheadingskip = 12pt plus 3pt minus 2pt
+\subsecheadingskip = 9pt plus 2pt minus 2pt
 
 % Prevent underfull vbox error messages.
-\vbadness=10000
+\vbadness = 10000
+
+% Don't be so finicky about underfull hboxes, either.
+\hbadness = 2000
 
 % Following George Bush, just get rid of widows and orphans.
 \widowpenalty=10000
@@ -4136,85 +6861,199 @@ July\or August\or September\or October\or November\or December\fi
 % Use TeX 3.0's \emergencystretch to help line breaking, but if we're
 % using an old version of TeX, don't do anything.  We want the amount of
 % stretch added to depend on the line length, hence the dependence on
-% \hsize.  This makes it come to about 9pt for the 8.5x11 format.
+% \hsize.  We call this whenever the paper size is set.
 %
-\ifx\emergencystretch\thisisundefined
-  % Allow us to assign to \emergencystretch anyway.
-  \def\emergencystretch{\dimen0}%
-\else
-  \emergencystretch = \hsize
-  \divide\emergencystretch by 45
-\fi
-
-% Use @smallbook to reset parameters for 7x9.5 format  (or else 7x9.25)
-\def\smallbook{
+\def\setemergencystretch{%
+  \ifx\emergencystretch\thisisundefined
+    % Allow us to assign to \emergencystretch anyway.
+    \def\emergencystretch{\dimen0}%
+  \else
+    \emergencystretch = .15\hsize
+  \fi
+}
 
-% These values for secheadingskip and subsecheadingskip are
-% experiments.  RJC 7 Aug 1992
-\global\secheadingskip = 17pt plus 6pt minus 3pt
-\global\subsecheadingskip = 14pt plus 6pt minus 3pt
+% Parameters in order: 1) textheight; 2) textwidth;
+% 3) voffset; 4) hoffset; 5) binding offset; 6) topskip;
+% 7) physical page height; 8) physical page width.
+%
+% We also call \setleading{\textleading}, so the caller should define
+% \textleading.  The caller should also set \parskip.
+%
+\def\internalpagesizes#1#2#3#4#5#6#7#8{%
+  \voffset = #3\relax
+  \topskip = #6\relax
+  \splittopskip = \topskip
+  %
+  \vsize = #1\relax
+  \advance\vsize by \topskip
+  \outervsize = \vsize
+  \advance\outervsize by 2\topandbottommargin
+  \pageheight = \vsize
+  %
+  \hsize = #2\relax
+  \outerhsize = \hsize
+  \advance\outerhsize by 0.5in
+  \pagewidth = \hsize
+  %
+  \normaloffset = #4\relax
+  \bindingoffset = #5\relax
+  %
+  \ifpdf
+    \pdfpageheight #7\relax
+    \pdfpagewidth #8\relax
+  \fi
+  %
+  \setleading{\textleading}
+  %
+  \parindent = \defaultparindent
+  \setemergencystretch
+}
 
-\global\lispnarrowing = 0.3in
-\setleading{12pt}
-\advance\topskip by -1cm
-\global\parskip 3pt plus 1pt
-\global\hsize = 5in
-\global\vsize=7.5in
-\global\tolerance=700
-\global\hfuzz=1pt
-\global\contentsrightmargin=0pt
-\global\deftypemargin=0pt
-\global\defbodyindent=.5cm
+% @letterpaper (the default).
+\def\letterpaper{{\globaldefs = 1
+  \parskip = 3pt plus 2pt minus 1pt
+  \textleading = 13.2pt
+  %
+  % If page is nothing but text, make it come out even.
+  \internalpagesizes{46\baselineskip}{6in}%
+                    {\voffset}{.25in}%
+                    {\bindingoffset}{36pt}%
+                    {11in}{8.5in}%
+}}
 
-\global\pagewidth=\hsize
-\global\pageheight=\vsize
+% Use @smallbook to reset parameters for 7x9.25 trim size.
+\def\smallbook{{\globaldefs = 1
+  \parskip = 2pt plus 1pt
+  \textleading = 12pt
+  %
+  \internalpagesizes{7.5in}{5in}%
+                    {\voffset}{.25in}%
+                    {\bindingoffset}{16pt}%
+                    {9.25in}{7in}%
+  %
+  \lispnarrowing = 0.3in
+  \tolerance = 700
+  \hfuzz = 1pt
+  \contentsrightmargin = 0pt
+  \defbodyindent = .5cm
+}}
 
-\global\let\smalllisp=\smalllispx
-\global\let\smallexample=\smalllispx
-\global\def\Esmallexample{\Esmalllisp}
-}
+% Use @smallerbook to reset parameters for 6x9 trim size.
+% (Just testing, parameters still in flux.)
+\def\smallerbook{{\globaldefs = 1
+  \parskip = 1.5pt plus 1pt
+  \textleading = 12pt
+  %
+  \internalpagesizes{7.4in}{4.8in}%
+                    {-.2in}{-.4in}%
+                    {0pt}{14pt}%
+                    {9in}{6in}%
+  %
+  \lispnarrowing = 0.25in
+  \tolerance = 700
+  \hfuzz = 1pt
+  \contentsrightmargin = 0pt
+  \defbodyindent = .4cm
+}}
 
 % Use @afourpaper to print on European A4 paper.
-\def\afourpaper{
-\global\tolerance=700
-\global\hfuzz=1pt
-\setleading{12pt}
-\global\parskip 15pt plus 1pt
-
-\global\vsize= 53\baselineskip
-\advance\vsize by \topskip
-%\global\hsize=   5.85in     % A4 wide 10pt
-\global\hsize=  6.5in
-\global\outerhsize=\hsize
-\global\advance\outerhsize by 0.5in
-\global\outervsize=\vsize
-\global\advance\outervsize by 0.6in
-
-\global\pagewidth=\hsize
-\global\pageheight=\vsize
-}
-
-% Allow control of the text dimensions.  Parameters in order: textheight;
-% textwidth; \voffset; \hoffset (!); binding offset.  All require a dimension;
-% header is additional; added length extends the bottom of the page.
-
-\def\changepagesizes#1#2#3#4#5
-{\global\vsize= #1
- \advance\vsize by \topskip
- \global\voffset= #3
- \global\hsize= #2
- \global\outerhsize=\hsize
- \global\advance\outerhsize by 0.5in
- \global\outervsize=\vsize
- \global\advance\outervsize by 0.6in
- \global\pagewidth=\hsize
- \global\pageheight=\vsize
- \global\normaloffset= #4
- \global\bindingoffset= #5}
-
-% This layout is compatible with Latex on A4 paper.
-
-\def\afourlatex{\changepagesizes{22cm}{15cm}{7mm}{4.6mm}{5mm}}
+\def\afourpaper{{\globaldefs = 1
+  \parskip = 3pt plus 2pt minus 1pt
+  \textleading = 13.2pt
+  %
+  % Double-side printing via postscript on Laserjet 4050
+  % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm.
+  % To change the settings for a different printer or situation, adjust
+  % \normaloffset until the front-side and back-side texts align.  Then
+  % do the same for \bindingoffset.  You can set these for testing in
+  % your texinfo source file like this:
+  % @tex
+  % \global\normaloffset = -6mm
+  % \global\bindingoffset = 10mm
+  % @end tex
+  \internalpagesizes{51\baselineskip}{160mm}
+                    {\voffset}{\hoffset}%
+                    {\bindingoffset}{44pt}%
+                    {297mm}{210mm}%
+  %
+  \tolerance = 700
+  \hfuzz = 1pt
+  \contentsrightmargin = 0pt
+  \defbodyindent = 5mm
+}}
+
+% Use @afivepaper to print on European A5 paper.
+% From romildo@urano.iceb.ufop.br, 2 July 2000.
+% He also recommends making @example and @lisp be small.
+\def\afivepaper{{\globaldefs = 1
+  \parskip = 2pt plus 1pt minus 0.1pt
+  \textleading = 12.5pt
+  %
+  \internalpagesizes{160mm}{120mm}%
+                    {\voffset}{\hoffset}%
+                    {\bindingoffset}{8pt}%
+                    {210mm}{148mm}%
+  %
+  \lispnarrowing = 0.2in
+  \tolerance = 800
+  \hfuzz = 1.2pt
+  \contentsrightmargin = 0pt
+  \defbodyindent = 2mm
+  \tableindent = 12mm
+}}
+
+% A specific text layout, 24x15cm overall, intended for A4 paper.
+\def\afourlatex{{\globaldefs = 1
+  \afourpaper
+  \internalpagesizes{237mm}{150mm}%
+                    {\voffset}{4.6mm}%
+                    {\bindingoffset}{7mm}%
+                    {297mm}{210mm}%
+  %
+  % Must explicitly reset to 0 because we call \afourpaper.
+  \globaldefs = 0
+}}
+
+% Use @afourwide to print on A4 paper in landscape format.
+\def\afourwide{{\globaldefs = 1
+  \afourpaper
+  \internalpagesizes{241mm}{165mm}%
+                    {\voffset}{-2.95mm}%
+                    {\bindingoffset}{7mm}%
+                    {297mm}{210mm}%
+  \globaldefs = 0
+}}
+
+% @pagesizes TEXTHEIGHT[,TEXTWIDTH]
+% Perhaps we should allow setting the margins, \topskip, \parskip,
+% and/or leading, also. Or perhaps we should compute them somehow.
+%
+\parseargdef\pagesizes{\pagesizesyyy #1,,\finish}
+\def\pagesizesyyy#1,#2,#3\finish{{%
+  \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi
+  \globaldefs = 1
+  %
+  \parskip = 3pt plus 2pt minus 1pt
+  \setleading{\textleading}%
+  %
+  \dimen0 = #1
+  \advance\dimen0 by \voffset
+  %
+  \dimen2 = \hsize
+  \advance\dimen2 by \normaloffset
+  %
+  \internalpagesizes{#1}{\hsize}%
+                    {\voffset}{\normaloffset}%
+                    {\bindingoffset}{44pt}%
+                    {\dimen0}{\dimen2}%
+}}
+
+% Set default to letter.
+%
+\letterpaper
+
+
+\message{and turning on texinfo input format.}
 
 % Define macros to output various characters with catcode for normal text.
 \catcode`\"=\other
@@ -4225,6 +7064,7 @@ July\or August\or September\or October\or November\or December\fi
 \catcode`\<=\other
 \catcode`\>=\other
 \catcode`\+=\other
+\catcode`\$=\other
 \def\normaldoublequote{"}
 \def\normaltilde{~}
 \def\normalcaret{^}
@@ -4233,9 +7073,10 @@ July\or August\or September\or October\or November\or December\fi
 \def\normalless{<}
 \def\normalgreater{>}
 \def\normalplus{+}
+\def\normaldollar{$}%$ font-lock fix
 
-% This macro is used to make a character print one way in ttfont
-% where it can probably just be output, and another way in other fonts,
+% This macro is used to make a character print one way in \tt
+% (where it can probably be output as-is), and another way in other fonts,
 % where something hairier probably needs to be done.
 %
 % #1 is what to print if we are indeed using \tt; #2 is what to print
@@ -4243,7 +7084,13 @@ July\or August\or September\or October\or November\or December\fi
 % interword stretch (and shrink), and it is reasonable to expect all
 % typewriter fonts to have this, we can check that font parameter.
 %
-\def\ifusingtt#1#2{\ifdim \fontdimen3\the\font=0pt #1\else #2\fi}
+\def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi}
+
+% Same as above, but check for italic font.  Actually this also catches
+% non-italic slanted fonts since it is impossible to distinguish them from
+% italic fonts.  But since this is only used by $ and it uses \sl anyway
+% this is not a problem.
+\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi}
 
 % Turn off all special characters except @
 % (and those which the user can use as if they were ordinary).
@@ -4251,28 +7098,22 @@ July\or August\or September\or October\or November\or December\fi
 % use math or other variants that look better in normal text.
 
 \catcode`\"=\active
-\def\activedoublequote{{\tt \char '042}}
+\def\activedoublequote{{\tt\char34}}
 \let"=\activedoublequote
 \catcode`\~=\active
-\def~{{\tt \char '176}}
+\def~{{\tt\char126}}
 \chardef\hat=`\^
 \catcode`\^=\active
 \def^{{\tt \hat}}
 
 \catcode`\_=\active
 \def_{\ifusingtt\normalunderscore\_}
+\let\realunder=_
 % Subroutine for the previous macro.
-\def\_{\lvvmode \kern.06em \vbox{\hrule width.3em height.1ex}}
-
-% \lvvmode is equivalent in function to \leavevmode.
-% Using \leavevmode runs into trouble when written out to
-% an index file due to the expansion of \leavevmode into ``\unhbox
-% \voidb@x'' ---which looks to TeX like ``\unhbox \voidb\x'' due to our
-% magic tricks with @.
-\def\lvvmode{\vbox to 0pt{}}
+\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em }
 
 \catcode`\|=\active
-\def|{{\tt \char '174}}
+\def|{{\tt\char124}}
 \chardef \less=`\<
 \catcode`\<=\active
 \def<{{\tt \less}}
@@ -4281,54 +7122,66 @@ July\or August\or September\or October\or November\or December\fi
 \def>{{\tt \gtr}}
 \catcode`\+=\active
 \def+{{\tt \char 43}}
-%\catcode 27=\active
-%\def^^[{$\diamondsuit$}
-
-% Set up an active definition for =, but don't enable it most of the time.
-{\catcode`\==\active
-\global\def={{\tt \char 61}}}
+\catcode`\$=\active
+\def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix
+
+% If a .fmt file is being used, characters that might appear in a file
+% name cannot be active until we have parsed the command line.
+% So turn them off again, and have \everyjob (or @setfilename) turn them on.
+% \otherifyactive is called near the end of this file.
+\def\otherifyactive{\catcode`+=\other \catcode`\_=\other}
+
+% Used sometimes to turn off (effectively) the active characters even after
+% parsing them.
+\def\turnoffactive{%
+  \normalturnoffactive
+  \otherbackslash
+}
 
 \catcode`\@=0
 
-% \rawbackslashxx output one backslash character in current font
-\global\chardef\rawbackslashxx=`\\
-%{\catcode`\\=\other
-%@gdef@rawbackslashxx{\}}
-
-% \rawbackslash redefines \ as input to do \rawbackslashxx.
-{\catcode`\\=\active
-@gdef@rawbackslash{@let\=@rawbackslashxx }}
-
-% \normalbackslash outputs one backslash in fixed width font.
-\def\normalbackslash{{\tt\rawbackslashxx}}
+% \backslashcurfont outputs one backslash character in current font,
+% as in \char`\\.
+\global\chardef\backslashcurfont=`\\
+\global\let\rawbackslashxx=\backslashcurfont  % let existing .??s files work
 
-% Say @foo, not \foo, in error messages.
-\escapechar=`\@
+% \realbackslash is an actual character `\' with catcode other, and
+% \doublebackslash is two of them (for the pdf outlines).
+{\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}}
 
-% \catcode 17=0   % Define control-q
+% In texinfo, backslash is an active character; it prints the backslash
+% in fixed width font.
 \catcode`\\=\active
+@def@normalbackslash{{@tt@backslashcurfont}}
+% On startup, @fixbackslash assigns:
+%  @let \ = @normalbackslash
+
+% \rawbackslash defines an active \ to do \backslashcurfont.
+% \otherbackslash defines an active \ to be a literal `\' character with
+% catcode other.
+@gdef@rawbackslash{@let\=@backslashcurfont}
+@gdef@otherbackslash{@let\=@realbackslash}
+
+% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of
+% the literal character `\'.
+% 
+@def@normalturnoffactive{%
+  @let\=@normalbackslash
+  @let"=@normaldoublequote
+  @let~=@normaltilde
+  @let^=@normalcaret
+  @let_=@normalunderscore
+  @let|=@normalverticalbar
+  @let<=@normalless
+  @let>=@normalgreater
+  @let+=@normalplus
+  @let$=@normaldollar %$ font-lock fix
+  @unsepspaces
+}
 
-% Used sometimes to turn off (effectively) the active characters
-% even after parsing them.
-@def@turnoffactive{@let"=@normaldoublequote
-@let\=@realbackslash
-@let~=@normaltilde
-@let^=@normalcaret
-@let_=@normalunderscore
-@let|=@normalverticalbar
-@let<=@normalless
-@let>=@normalgreater
-@let+=@normalplus}
-
-@def@normalturnoffactive{@let"=@normaldoublequote
-@let\=@normalbackslash
-@let~=@normaltilde
-@let^=@normalcaret
-@let_=@normalunderscore
-@let|=@normalverticalbar
-@let<=@normalless
-@let>=@normalgreater
-@let+=@normalplus}
+% Make _ and + \other characters, temporarily.
+% This is canceled by @fixbackslash.
+@otherifyactive
 
 % If a .fmt file is being used, we don't want the `\input texinfo' to show up.
 % That is what \eatinput is for; after that, the `\' should revert to printing
@@ -4338,18 +7191,36 @@ July\or August\or September\or October\or November\or December\fi
 @global@let\ = @eatinput
 
 % On the other hand, perhaps the file did not have a `\input texinfo'. Then
-% the first `\{ in the file would cause an error. This macro tries to fix
+% the first `\' in the file would cause an error. This macro tries to fix
 % that, assuming it is called before the first `\' could plausibly occur.
+% Also turn back on active characters that might appear in the input
+% file name, in case not using a pre-dumped format.
 %
-@gdef@fixbackslash{@ifx\@eatinput @let\ = @normalbackslash @fi}
+@gdef@fixbackslash{%
+  @ifx\@eatinput @let\ = @normalbackslash @fi
+  @catcode`+=@active
+  @catcode`@_=@active
+}
+
+% Say @foo, not \foo, in error messages.
+@escapechar = `@@
 
-%% These look ok in all fonts, so just make them not special.  The @rm below
-%% makes sure that the current font starts out as the newly loaded cmr10
-@catcode`@$=@other @catcode`@%=@other @catcode`@&=@other @catcode`@#=@other
+% These look ok in all fonts, so just make them not special.
+@catcode`@& = @other
+@catcode`@# = @other
+@catcode`@% = @other
 
-@textfonts
-@rm
 
 @c Local variables:
+@c eval: (add-hook 'write-file-hooks 'time-stamp)
 @c page-delimiter: "^\\\\message"
+@c time-stamp-start: "def\\\\texinfoversion{"
+@c time-stamp-format: "%:y-%02m-%02d.%02H"
+@c time-stamp-end: "}"
 @c End:
+
+@c vim:sw=2:
+
+@ignore
+   arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115
+@end ignore
index c1fe462358ae42b350bad7cd0ad6267fb0f5c42f..0d0fd68782903cca0644727556c2d972dd7cef1d 100644 (file)
 @parskip 6pt plus 6pt
 @end iftex
 
+@dircategory Kerberos
+@direntry
+* krb5-user: (krb5-user).               Kerberos V5 UNIX User's Guide
+@end direntry
+
 @include definitions.texinfo
 @set EDITION 1.0
 
@@ -932,7 +937,7 @@ Escape character is '^]'.
 [ Kerberos V5 accepts you as ``@value{RANDOMUSER1}@@@value{PRIMARYDOMAIN}'' ]
 [ Kerberos V5 accepted forwarded credentials ]
 What you type is protected by encryption.
-Last login: Tue Jul 30 18:47:44 from @value{RANDOMHOST}.@value{SECONDDOMAIN}
+Last login: Tue Jul 30 18:47:44 from @value{RANDOMHOST1}.@value{SECONDDOMAIN}
 Athena Server (sun4) Version 9.1.11 Tue Jul 30 14:40:08 EDT 2002
 
 shell%}
index a05406a747d884f71f6dcefabc78ae623c11bb22..de4cd342e97f512c626b9ac11f7c2e02fd0eef02 100644 (file)
@@ -34,22 +34,26 @@ When a service request is received, the following protocol is initiated:
 .IP 1)
 Check authentication.
 .IP 2)
-Check authorization via the access-control files \fI.k5login\fP, \fI.klogin\fP 
-and \fI.rhosts\fP in the user's home directory.
+Check authorization via the access-control files \fI.k5login\fP and
+\fI.klogin\fP in the user's home directory.
 .IP 3)
 Prompt for password if any checks fail and the \fI-p\fP option was supplied.
 .PP
 If the authentication succeeds, login the user by calling the accompanying 
 login.krb5 or /bin/login, according to the definition of 
-DO_NOT_USE_K_LOGIN.  
+DO_NOT_USE_K_LOGIN.
 .PP 
 The configuration of \fIklogind\fP is done
 by command line arguments passed by inetd.  The options are:
 .IP \fB\-5\fP 10
 Allow Kerberos V5 authentication with the \fI.k5login\fP access control
 file to be trusted.  If this authentication system is used by the client
-and the authorization check is passed, then the user is allowed to log
-in.
+and the authorization check is passed, then the user is allowed to log in.
+If the user has no \fI.k5login\fP file, the login will be authorized if
+the results of krb5_aname_to_localname conversion matches the account
+name.  Unless special rules are configured, this will be true if and only
+if the Kerberos principal of the connecting user is in the default local
+realm and the principal portion matches the account name.
 
 .IP \fB\-4\fP 
 Allow Kerberos V4 authentication with the \fI.klogin\fP access control
@@ -107,12 +111,6 @@ clients--particularly clients predating the release of Kerberos V5
 Beta5 (May 1995)--present bogus checksums that prevent Kerberos
 authentication from succeeding in the default mode.
 
-
-.PP
-If the
-~/.rhosts check is to be used, then the program verifies that the
-client is connecting from a privileged port, before allowing login.
-
 .PP
 The parent of the login process manipulates the master side of the
 pseduo terminal, operating as an intermediary between the login
index 6e2efdb2b058e8c141aa388839cc250e541498eb..b6d50390a782f3409a76225008d5975c53fc1023 100644 (file)
@@ -37,8 +37,8 @@ When a service request is received, the following protocol is initiated:
 .IP 1) 
 Authentication is checked
 .IP 2)
-Check authorization via the access-control files \fI.k5login\fP, \fI.klogin\fP 
-and \fI.rhosts\fP in the user's home directory.
+Check authorization via the access-control files \fI.k5login\fP and
+\fI.klogin\fP in the user's home directory.
 .IP 3)
 A null byte is returned on the initial socket
 and the command line is passed to the normal login
@@ -53,8 +53,13 @@ by \fIinetd(8)\fP.
 
 .IP \fB\-5\fP 10
 Allow Kerberos5 authentication with the \fI.k5login\fP access control file
-to be trusted.  If this authentication system is used by the client and the
-authorization check is passed, then the user is allowed to log in.
+to be trusted.  If this authentication system is used by the client and
+the authorization check is passed, then the user is allowed to log in.  If
+the user has no \fI.k5login\fP file, the login will be authorized if the
+results of krb5_aname_to_localname conversion matches the account name.
+Unless special rules are configured, this will be true if and only if the
+Kerberos principal of the connecting user is in the default local realm
+and the principal portion matches the account name.
 
 .IP \fB\-4\fP 
 Allow Kerberos4 authentication with the \fI.klogin\fP access control file
@@ -107,9 +112,6 @@ Beta5 (May 1995)--present bogus checksums that prevent Kerberos
 authentication from succeeding in the default mode.
 
 
-.PP
-If the \fB\-r\fP or \fB\-R\fP options are used, the client must
-connect from a privileged port.
 .PP
 \fIKrshd\fP supports six options which may be used for testing:
 
@@ -126,7 +128,7 @@ Don't allocate a reserved port for the stderr connection.
 Use the argument to find the Kerberos binaries.  Normally a compiled
 in argument is used.
 
-.IP \fB\-D\ port \fP
+.IP \fB\-D\ port\fP
 Run in standalone mode, listening on \fBport\fP.  The daemon will exit
 after one connection and will not background itself.
 
index 97b2a8e7d12493153aa6b86490b589b50b44084c..e2597823e086e4efa1802770591c3018e391ff05 100644 (file)
@@ -149,7 +149,7 @@ simple heuristics are used to guess which to try.
 \fB\-4\fP
 Use Kerberos V4 authentication only; don't try Kerberos V5.
 .SH SEE ALSO
-rsh(1), kerberos(3), krb_sendauth(3), krb_realmofhost(3), rlogin(1) [UCB
+rsh(1), kerberos(1), krb_sendauth(3), krb_realmofhost(3), rlogin(1) [UCB
 version], klogind(8)
 .SH FILES
 .TP "\w'~/\&.k5login\ \ 'u"
index 0050893263de95ed07adaa839feefd19795a86b6..1f0444c6574964a141d42c5080c47d7819fd9f6e 100644 (file)
@@ -111,3 +111,4 @@ $(OUTPRE)secure.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(SRCTOP)/include/kerberosIV/des.h $(SRCTOP)/include/kerberosIV/krb.h \
   $(srcdir)/../arpa/ftp.h $(srcdir)/../ftp/secure.c secure.h
 $(OUTPRE)getdtablesize.$(OBJEXT): $(srcdir)/../../bsd/getdtablesize.c
+$(OUTPRE)setenv.$(OBJEXT): $(srcdir)/../../bsd/setenv.c
index cb3390d38c484b0cade3b5f9a33ff31fa6510517..f6c0fcdb447e85f09d0d1bb2152ad1530500c25e 100644 (file)
@@ -2446,7 +2446,7 @@ tn(argc, argv)
     cmd = *argv;
     --argc; ++argv;
     while (argc) {
-       if (isprefix(*argv, "help") || isprefix(*argv, "?"))
+       if (isprefix(*argv, "?"))
            goto usage;
        if (strcmp(*argv, "-l") == 0) {
            --argc; ++argv;
index dccb424f050d8ba00a16c4742b9f15c1110597bb..3d098adff24859dc0657769d66b6a5385d283eb5 100644 (file)
@@ -57,9 +57,8 @@
 #if defined(CRAY) && !defined(NO_BSD_SETJMP)
 #include <bsdsetjmp.h>
 #endif
-#ifndef        HAVE_SYS_FILIO_H
 #include <sys/ioctl.h>
-#else
+#ifdef HAVE_SYS_FILIO_H
 #include <sys/filio.h>
 #endif
 #ifdef CRAY
index 3ceb8653de818d48d23b7796a6d2ef266053b26c..ab653a35f858e835ff622abfc1887db8aec6d1fe 100644 (file)
@@ -89,9 +89,8 @@
 # endif
 #endif
 
-#ifndef        HAVE_SYS_FILIO_H
 #include <sys/ioctl.h>
-#else
+#ifdef HAVE_SYS_FILIO_H
 #include <sys/filio.h>
 #endif
 
index fce16ba93ab30c58c91a546f9a557c5b4ce3f9d3..e7aa47c155218851e9d1df74645edd5c33f4dc3a 100644 (file)
@@ -198,4 +198,4 @@ default location for the local host's
 .B keytab
 file.
 .SH SEE ALSO
-klist(1), kdestroy(1), krb5(3)
+klist(1), kdestroy(1), kerberos(1)
index 7c1a9d1a888e1efbd745764a47ab17aa1e076c4a..452d98cf0babeb573bc19ca21e71b4479978eb78 100644 (file)
@@ -211,7 +211,7 @@ usage(progname)
            USAGE_BREAK_LONG
            "[-p | -P" USAGE_LONG_PROXIABLE "] "
            USAGE_BREAK_LONG
-           "[-A" USAGE_LONG_ADDRESSES "] "
+           "[-a | -A" USAGE_LONG_ADDRESSES "] "
            USAGE_BREAK
            "[-v] [-R] "
            "[-k [-t keytab_file]] "
@@ -258,6 +258,7 @@ fprintf(stderr, USAGE_OPT_FMT, indent, col1)
     ULINE("\t", "-F not forwardable",           OPTTYPE_KRB5);
     ULINE("\t", "-p proxiable",                 OPTTYPE_KRB5);
     ULINE("\t", "-P not proxiable",             OPTTYPE_KRB5);
+    ULINE("\t", "-a include addresses",         OPTTYPE_KRB5);
     ULINE("\t", "-A do not include addresses",  OPTTYPE_KRB5);
     ULINE("\t", "-v validate",                  OPTTYPE_KRB5);
     ULINE("\t", "-R renew",                     OPTTYPE_BOTH);
@@ -295,7 +296,7 @@ parse_options(argc, argv, opts, progname)
     int use_k5 = 0;
     int i;
 
-    while ((i = GETOPT(argc, argv, "r:fpFP54AVl:s:c:kt:RS:v"))
+    while ((i = GETOPT(argc, argv, "r:fpFP54aAVl:s:c:kt:RS:v"))
           != -1) {
        switch (i) {
        case 'V':
@@ -330,7 +331,6 @@ parse_options(argc, argv, opts, progname)
            opts->not_proxiable = 1;
            break;
        case 'a':
-           /* Note: This is supported only with GETOPT_LONG */
            opts->addresses = 1;
            break;
        case 'A':
index 34de8303a8e45689db42edef17f6f395a0d4318a..8f3ec39b43c3a3d659c280b6f2f5d9e104d751d1 100644 (file)
@@ -202,8 +202,8 @@ be in a comma-separated list.
 
 .IP udp_preference_limit
 When sending a message to the KDC, the library will try using TCP
-before UDP if the size of the message is above "udp_preference_list".
-If the message is smaller than "udp_preference_list", then UDP will be
+before UDP if the size of the message is above "udp_preference_limit".
+If the message is smaller than "udp_preference_limit", then UDP will be
 tried before TCP.  Regardless of the size, both protocols will be
 tried if the first attempt fails.
 
index a76340159c4fe0aaaae6fe9fc09e9fa8dc5f7380..1a1caad78b1768ad65b15359be2f51681909c726 100644 (file)
@@ -350,7 +350,7 @@ mips-*-netbsd*)
        SHLIBEXT=.so
        # Linux ld doesn't default to stuffing the SONAME field...
        # Use objdump -x to examine the fields of the library
-       LDCOMBINE='$(CC) -shared -fPIC -Wl,-h,$(LIBPREFIX)$(LIBBASE)$(SHLIBSEXT),--as-needed'
+       LDCOMBINE='$(CC) -shared -fPIC -Wl,-h,$(LIBPREFIX)$(LIBBASE)$(SHLIBSEXT)'
        # 
        LDCOMBINE_TAIL='-Wl,--version-script binutils.versions && $(PERL) -w $(SRCTOP)/util/export-check.pl $(SHLIB_EXPORT_FILE) $@'
        SHLIB_EXPORT_FILE_DEP=binutils.versions
@@ -375,6 +375,19 @@ mips-*-netbsd*)
 
        ;;
 
+*-*-bsdi4*)
+       PICFLAGS=-fpic
+       SHLIBVEXT='.so.$(LIBMAJOR)'
+       SHLIBEXT=.so
+       LDCOMBINE='ld -Bshareable'
+       SHLIB_EXPFLAGS='-R$(SHLIB_RDIRS) $(SHLIB_DIRS) $(SHLIB_EXPLIBS)'
+       CC_LINK_SHARED='$(CC) $(PROG_LIBPATH) -Wl,-rpath,$(PROG_RPATH)'
+       CC_LINK_STATIC='$(CC) $(PROG_LIBPATH)'
+       RUN_ENV='LD_LIBRARY_PATH=`echo $(PROG_LIBPATH) | sed -e "s/-L//g" -e "s/
+/:/g"`; export LD_LIBRARY_PATH;'
+       PROFFLAGS=-pg
+       ;;
+
 *-*-aix5*)
        SHLIBVEXT='.so.$(LIBMAJOR).$(LIBMINOR)'
        SHLIBEXT=.so
index ca5a411fc7c796838fb8f47fd886a56f51155776..b770fab99882958638d360c5e8f614b9744a8833 100644 (file)
@@ -929,6 +929,7 @@ V5_AC_OUTPUT_MAKEFILE(.
        lib/krb5/keytab lib/krb5/krb lib/krb5/rcache lib/krb5/os
 
        lib/gssapi lib/gssapi/generic lib/gssapi/krb5
+       lib/gssapi/mechglue lib/gssapi/spnego
 
        lib/rpc lib/rpc/unit-test
 
index 6bfdfeed1d18cb47f46f2cee867a816fc89c84ed..1bc3ed2d7c8148e63aee96aa5175e9f9ffe2cd4d 100644 (file)
@@ -1047,15 +1047,7 @@ struct _krb5_context {
           absolute limit on the UDP packet size.  */
        int             udp_pref_limit;
 
-       /* This is the tgs_ktypes list as read from the profile, or
-          set to compiled-in defaults.  The application code cannot
-          override it.  This is used for session keys for
-          intermediate ticket-granting tickets used to acquire the
-          requested ticket (the session key of which may be
-          constrained by tgs_ktypes above).  */
-       krb5_enctype    *conf_tgs_ktypes;
-       unsigned int    conf_tgs_ktypes_count;
-       /* Use the _configured version?  */
+       /* Use the config-file ktypes instead of app-specified?  */
        krb5_boolean    use_conf_ktypes;
 
 #ifdef KRB5_DNS_LOOKUP
@@ -1645,12 +1637,14 @@ struct srv_dns_entry {
     unsigned short port;
     char *host;
 };
+#ifdef KRB5_DNS_LOOKUP
 krb5_error_code
 krb5int_make_srv_query_realm(const krb5_data *realm,
                             const char *service,
                             const char *protocol,
                             struct srv_dns_entry **answers);
 void krb5int_free_srv_dns_data(struct srv_dns_entry *);
+#endif
 
 /*
  * Convenience function for structure magic number
index af949dbd402bb5e29ec788f3ced3a8ff5b50171e..876335f94d1a568ce8af5af288dd13d94faa3aa5 100644 (file)
@@ -664,8 +664,9 @@ load_64_le (const unsigned char *p)
 #ifdef HAVE_GETPWNAM_R
 # ifndef GETPWNAM_R_4_ARGS
 /* POSIX */
-#  define k5_getpwnam_r(NAME, REC, BUF, BUFSIZE, OUT) \
-       getpwnam_r(NAME,REC,BUF,BUFSIZE,OUT)
+#  define k5_getpwnam_r(NAME, REC, BUF, BUFSIZE, OUT)  \
+       (getpwnam_r(NAME,REC,BUF,BUFSIZE,OUT) == 0      \
+        ? (*(OUT) == NULL ? -1 : 0) : -1)
 # else
 /* POSIX drafts? */
 #  ifdef GETPWNAM_R_RETURNS_INT
@@ -688,8 +689,9 @@ load_64_le (const unsigned char *p)
 #ifdef HAVE_GETPWUID_R
 # ifndef GETPWUID_R_4_ARGS
 /* POSIX */
-#  define k5_getpwuid_r(UID, REC, BUF, BUFSIZE, OUT) \
-       getpwuid_r(UID,REC,BUF,BUFSIZE,OUT)
+#  define k5_getpwuid_r(UID, REC, BUF, BUFSIZE, OUT)   \
+       (getpwuid_r(UID,REC,BUF,BUFSIZE,OUT) == 0       \
+        ? (*(OUT) == NULL ? -1 : 0) : -1)
 # else
 /* POSIX drafts?  Yes, I mean to test GETPWNAM... here.  Less junk to
    do at configure time.  */
index 08a8612ba2bea96595cab28dcdec14c5cf8a895a..5ea5ea39228113f9c57736dee5ed1af7de54bce3 100644 (file)
@@ -82,6 +82,7 @@ install::
        f=$(FAKEKA); \
        if test -n "$$f" ; then \
                $(INSTALL_PROGRAM) $$f ${DESTDIR}$(SERVER_BINDIR)/$$f; \
+               $(INSTALL_DATA) $(srcdir)/fakeka.M ${DESTDIR}$(SERVER_MANDIR)/fakeka.8; \
        fi
 
 clean::
diff --git a/src/kdc/fakeka.M b/src/kdc/fakeka.M
new file mode 100644 (file)
index 0000000..80ea015
--- /dev/null
@@ -0,0 +1,111 @@
+.\" kdc/fakeka.M
+.\"
+.\" Copyright 2005 by the Massachusetts Institute of Technology.
+.\"
+.\" Export of this software from the United States of America may
+.\"   require a specific license from the United States Government.
+.\"   It is the responsibility of any person or organization contemplating
+.\"   export to obtain such a license before exporting.
+.\"
+.\" WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+.\" distribute this software and its documentation for any purpose and
+.\" without fee is hereby granted, provided that the above copyright
+.\" notice appear in all copies and that both that copyright notice and
+.\" this permission notice appear in supporting documentation, and that
+.\" the name of M.I.T. not be used in advertising or publicity pertaining
+.\" to distribution of the software without specific, written prior
+.\" permission.  Furthermore if you modify this software you must label
+.\" your software as modified software and not distribute it in such a
+.\" fashion that it might be confused with the original M.I.T. software.
+.\" M.I.T. makes no representations about the suitability of
+.\" this software for any purpose.  It is provided "as is" without express
+.\" or implied warranty.
+.\" "
+.TH FAKEKA 8
+.SH NAME
+fakeka \- Fake kaserver for AFS clients
+.SH SYNOPSIS
+\fBfakeka\fP [\fB\-dm\fP] [\fB\-c\fP \fIcell\fP] [\fB\-f\fP \fIforwarder\fP]
+[\fB\-l\fP \fBfacility\fP] [\fB\-p\fP \fBport\fP] [\fB\-r\fP \fIrealm\fP]
+.br
+.SH DESCRIPTION
+.I fakeka
+is a fake kaserver that speaks just enough of the AFS RX protocol to make
+klog work.  It is used in conjunction with a Kerberos V5 KDC to support
+existing AFS clients, and is usually used with ka-forwarder.
+.I fakeka
+must run on the same host as your Kerberos V5 KDC, since it needs access
+to the KDC database.  ka-forwarder should run on each of your AFS database
+servers, pointing to your Kerberos V5 KDCs.
+.I fakeka
+should then be running on each of the KDCs, with the AFS database servers
+listed as arguments to the
+.B -f
+option.
+.PP
+Note that principals you wish to use
+.I fakeka
+with must have either a V4-style key (des:v4) or an AFS-style key
+(des:afs3).  V5 enctypes won't work.
+.SH OPTIONS
+.TP
+\fB\-c\fP \fIcell\fP
+The AFS cell for which
+.I fakeka
+will be handling requests.  If not given, this defaults to the same as the
+Kerberos V5 realm (see
+.B \-r
+below).
+.TP
+.B \-d
+Enables debugging.  When this flag is given,
+.I fakeka
+will run in the foreground and print debugging information to standard
+error.  Overrides
+.BR \-m .
+.TP
+\fB\-f\fP \fIforwarder\fP
+Allows forwarded requests from
+.IR forwarder ,
+which is generally an AFS database server running ka-forwarder.  This
+option can be given multiple times (up to 10).  Each system running
+ka-forwarder should be specified with the
+.B \-f
+flag or forwarded requests from that host will not be answered.  (The
+forwarders append their own address to the packet.
+.TP
+\fB\-l\fP \fIfacility\fP
+Log actions via syslog with the given
+.I facility
+rather than the default of LOG_DAEMON.
+.I facility
+must be one of KERN, USER, MAIL, DAEMON, AUTH, LPR, NEWS, UUCP, CRON,
+LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, or LOCAL7.  This
+option is case-sensitive.  Not all of these facilities may be available,
+depending on what pre-defined syslog facilities your system provides.
+.TP
+.B \-m
+Fork and background when starting.  You will usually always want to give
+this flag.
+.TP
+\fB\-p\fP \fIport\fP
+Listen on the specified port rather than the default of 7004 (which is
+what klog expects).
+.I port
+may be a number or a service name from
+.IR /etc/services .
+.TP
+\fB\-r\fP \fIrealm\fP
+The Kerberos V5 realm to which the requests are being translated.  The
+default is the local default realm.
+.SH EXAMPLES
+Handle requests for a local cell whose name matches the local realm,
+accepting forwarded queries from afs1.example.com and afs2.example.com:
+.IP "" 4
+fakeka -m -f afs1.example.com -f afs2.example.com
+.PP
+If the cell name doesn't match the realm name,
+.B \-c
+would need to be added, specifying the cell name.
+.SH SEE ALSO
+ka-forwarder(8)
index 7bb6dfde16dae90aa0ae180e92d139ac8efd70c4..a11e6225acb2af96e160b5994ab0a60ce9bbb0ca 100644 (file)
@@ -53,6 +53,7 @@
 #include <ctype.h>
 #include <syslog.h>
 #include <string.h>
+#include <errno.h>
 
 /* v4 include files:
  */
@@ -516,8 +517,8 @@ kerb_get_principal(char *name, char *inst, /* could have wild cards */
            return(0);
        }
        compat_decrypt_key(pkey, k, k5key, issrv);
-    memset (k, 0, sizeof k);
-       }
+       memset (k, 0, sizeof k);
+    }
 
 
     /*
index dc9baa465eb4383d9fbcdcec612e89897c350982..cf2be4947de9e0a2ddc9c163d98bf9494d4f922f 100755 (executable)
@@ -166,7 +166,11 @@ if test -n "$do_exec_prefix"; then
 fi
 
 if test -n "$do_cflags"; then
-    echo "-I${includedir}"
+    if test x"$includedir" != x"/usr/include" ; then
+        echo "-I${includedir}"
+    else
+        echo ''
+    fi
 fi
 
 
index 7aee1c7c2288a9355575a40fc22097bd8ae0162b..d1a38915e44f999524708370928443cb704cb16c 100644 (file)
@@ -96,6 +96,8 @@ install-unix::
        $(INSTALL_PROGRAM) krb524d $(DESTDIR)$(SERVER_BINDIR)/krb524d
        $(INSTALL_PROGRAM) k524init $(DESTDIR)$(CLIENT_BINDIR)/krb524init
        $(INSTALL_DATA) $(srcdir)/krb524d.M $(DESTDIR)$(SERVER_MANDIR)/krb524d.8
+       $(INSTALL_DATA) $(srcdir)/k524init.M \
+               $(DESTDIR)$(CLIENT_MANDIR)/krb524init.1
 
 clean-unix::
        $(RM) $(OBJS) core *~ *.bak #*
diff --git a/src/krb524/k524init.M b/src/krb524/k524init.M
new file mode 100644 (file)
index 0000000..f480767
--- /dev/null
@@ -0,0 +1,47 @@
+.\" krb524/k524init.M
+.\"
+.\" Copyright 2005 by the Massachusetts Institute of Technology.
+.\"
+.\" Export of this software from the United States of America may
+.\"   require a specific license from the United States Government.
+.\"   It is the responsibility of any person or organization contemplating
+.\"   export to obtain such a license before exporting.
+.\"
+.\" WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+.\" distribute this software and its documentation for any purpose and
+.\" without fee is hereby granted, provided that the above copyright
+.\" notice appear in all copies and that both that copyright notice and
+.\" this permission notice appear in supporting documentation, and that
+.\" the name of M.I.T. not be used in advertising or publicity pertaining
+.\" to distribution of the software without specific, written prior
+.\" permission.  Furthermore if you modify this software you must label
+.\" your software as modified software and not distribute it in such a
+.\" fashion that it might be confused with the original M.I.T. software.
+.\" M.I.T. makes no representations about the suitability of
+.\" this software for any purpose.  It is provided "as is" without express
+.\" or implied warranty.
+.\" "
+.TH KRB524INIT 1
+.SH NAME
+krb524init \- Obtain Kerberos V4 tickets from Kerberos V5 tickets
+.SH SYNOPSIS
+\fBkrb524init\fP [\fB\-n\fP] [\fB\-p\fP \fIprincipal\fP]
+.SH DESCRIPTION
+.I krb524init
+converts a V5 credential to a V4 credential by querying a remote krb524d
+server and stores it in a V4 ticket cache.  The credential is
+.I principal
+or "krbtgt" at the V5 ticket cache's default principal's realm if not
+specified.
+.SH OPTIONS
+.TP
+.B \-n
+By default, the V4 ticket cache is initialized.  If this option is given,
+the converted credential is instead added to the existing ticket cache.
+.TP
+\fB\-p\fP \fIprincipal\fP
+Convert
+.I principal
+rather than krbtgt.
+.SH SEE ALSO
+kinit(1), krb524d(8)
diff --git a/src/lib/gssapi/LICENSE b/src/lib/gssapi/LICENSE
new file mode 100644 (file)
index 0000000..612f8ad
--- /dev/null
@@ -0,0 +1,91 @@
+[ NOTE: MIT has only incorporated the mechglue and spnego change, and
+not the incremental propagation changes.  The filenames are different
+between the Sun and MIT sources.  The actual MIT filenames appear in
+the top-level README file.  Original text of Sun's LICENSE file
+follows. ]
+
+Subject to the license set forth below, Sun Microsystems, Inc. donates
+the attached files to MIT for the purpose of including these
+modifications and additions in future versions of the Kerberos system.
+
+Many of the files attached are subject to licenses issued by other
+entities, including OpenVision, MIT, and FundsXpress.  See the
+individual files, and/or related Readme files, for these licenses.
+
+In addition Sun requires that the license set forth below be
+incorporated into any future version of the Kerberos system which
+contains portions of the files attached.  The following files must be
+listed, in the top level Readme file, as being provided subject to such
+license:
+
+cmd/krb5/iprop/iprop.x
+cmd/krb5/iprop/iprop_hdr.h
+cmd/krb5/kadmin/server/ipropd_svc.c
+cmd/krb5/kproplog/kproplog.c
+cmd/krb5/slave/kpropd_rpc.c
+lib/gss_mechs/mech_krb5/et/kdb5_err.c
+lib/gss_mechs/mech_spnego/mech/gssapiP_spnego.h
+lib/gss_mechs/mech_spnego/mech/spnego_mech.c
+lib/krb5/kadm5/kadm_host_srv_names.c
+lib/krb5/kdb/kdb_convert.c
+lib/krb5/kdb/kdb_hdr.h
+lib/krb5/kdb/kdb_log.c
+lib/krb5/kdb/kdb_log.h
+lib/libgss/g_accept_sec_context.c
+lib/libgss/g_acquire_cred.c
+lib/libgss/g_canon_name.c
+lib/libgss/g_compare_name.c
+lib/libgss/g_context_time.c
+lib/libgss/g_delete_sec_context.c
+lib/libgss/g_dsp_name.c
+lib/libgss/g_dsp_status.c
+lib/libgss/g_dup_name.c
+lib/libgss/g_exp_sec_context.c
+lib/libgss/g_export_name.c
+lib/libgss/g_glue.c
+lib/libgss/g_imp_name.c
+lib/libgss/g_imp_sec_context.c
+lib/libgss/g_init_sec_context.c
+lib/libgss/g_initialize.c
+lib/libgss/g_inquire_context.c
+lib/libgss/g_inquire_cred.c
+lib/libgss/g_inquire_names.c
+lib/libgss/g_process_context.c
+lib/libgss/g_rel_buffer.c
+lib/libgss/g_rel_cred.c
+lib/libgss/g_rel_name.c
+lib/libgss/g_rel_oid_set.c
+lib/libgss/g_seal.c
+lib/libgss/g_sign.c
+lib/libgss/g_store_cred.c
+lib/libgss/g_unseal.c
+lib/libgss/g_userok.c
+lib/libgss/g_utils.c
+lib/libgss/g_verify.c
+lib/libgss/gssd_pname_to_uid.c
+uts/common/gssapi/include/gssapi_err_generic.h
+uts/common/gssapi/include/mechglueP.h
+
+Sun's License is as follows:
+
+Copyright (c) 2004 Sun Microsystems, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
index daf0818ddc6b1b47da2c530731e123fd61fcea22..03fc695b2fde909cf1c9f1931e70943237d562e2 100644 (file)
@@ -2,7 +2,7 @@ thisconfigdir=../..
 myfulldir=lib/gssapi
 mydir=lib/gssapi
 BUILDTOP=$(REL)..$(S)..
-LOCAL_SUBDIRS= generic krb5
+LOCAL_SUBDIRS= generic mechglue krb5 spnego
 DEFS=
 
 ##DOSLIBNAME=$(OUTPRE)gssapi.lib
@@ -14,7 +14,7 @@ DEFS=
 
 ##DOS##DLL_EXP_TYPE=GSS
 
-LOCALINCLUDES = -Igeneric -I$(srcdir)/generic -Ikrb5 -I$(srcdir)/krb5
+LOCALINCLUDES = -Igeneric -I$(srcdir)/generic -Ikrb5 -I$(srcdir)/krb5 -I$(srcdir)/mechglue
 STLIBOBJS=\
        gss_libinit.o
 
@@ -29,11 +29,11 @@ LIBMAJOR=2
 LIBMINOR=2
 LIBINITFUNC=gssint_lib_init
 LIBFINIFUNC=gssint_lib_fini
-STOBJLISTS=OBJS.ST generic/OBJS.ST krb5/OBJS.ST
-SUBDIROBJLISTS=generic/OBJS.ST krb5/OBJS.ST
+STOBJLISTS=OBJS.ST generic/OBJS.ST mechglue/OBJS.ST krb5/OBJS.ST spnego/OBJS.ST
+SUBDIROBJLISTS=generic/OBJS.ST mechglue/OBJS.ST krb5/OBJS.ST
 SHLIB_EXPDEPS=\
        $(KRB5_DEPLIB) $(CRYPTO_DEPLIB) $(SUPPORT_DEPLIB) $(COM_ERR_DEPLIB)
-SHLIB_EXPLIBS=-lkrb5 -lk5crypto -lcom_err $(SUPPORT_LIB) $(LIBS)
+SHLIB_EXPLIBS=-lkrb5 -lk5crypto -lcom_err $(SUPPORT_LIB) $(DL_LIB) $(LIBS)
 SHLIB_DIRS=-L$(TOPLIBD)
 SHLIB_RDIRS=$(KRB5_LIBDIR)
 RELDIR=gssapi
@@ -132,6 +132,7 @@ gss_libinit.so gss_libinit.po $(OUTPRE)gss_libinit.$(OBJEXT): \
   $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
   $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
   $(srcdir)/generic/gssapiP_generic.h $(srcdir)/generic/gssapi_generic.h \
-  $(srcdir)/krb5/gssapiP_krb5.h generic/gssapi.h generic/gssapi_err_generic.h \
+  $(srcdir)/krb5/gssapiP_krb5.h $(srcdir)/mechglue/mechglue.h \
+  $(srcdir)/mechglue/mglueP.h generic/gssapi.h generic/gssapi_err_generic.h \
   gss_libinit.c gss_libinit.h krb5/gssapi_err_krb5.h \
   krb5/gssapi_krb5.h
index 9dfa68e6fd243f53adfde3e7faa6d0fad3ec9a75..305a32589a7b71643c73f0e9a0e5f43c5f384c3a 100644 (file)
@@ -67,11 +67,9 @@ SRCS = \
        $(srcdir)/disp_com_err_status.c \
        $(srcdir)/disp_major_status.c \
        $(srcdir)/gssapi_generic.c \
-       $(srcdir)/oid_ops.c \
        $(srcdir)/rel_buffer.c \
        $(srcdir)/rel_oid_set.c \
        $(srcdir)/util_buffer.c \
-       $(srcdir)/util_oid.c \
        $(srcdir)/util_ordering.c \
        $(srcdir)/util_set.c \
        $(srcdir)/util_token.c \
@@ -82,11 +80,9 @@ OBJS = \
        $(OUTPRE)disp_com_err_status.$(OBJEXT) \
        $(OUTPRE)disp_major_status.$(OBJEXT) \
        $(OUTPRE)gssapi_generic.$(OBJEXT) \
-       $(OUTPRE)oid_ops.$(OBJEXT) \
        $(OUTPRE)rel_buffer.$(OBJEXT) \
        $(OUTPRE)rel_oid_set.$(OBJEXT) \
        $(OUTPRE)util_buffer.$(OBJEXT) \
-       $(OUTPRE)util_oid.$(OBJEXT) \
        $(OUTPRE)util_ordering.$(OBJEXT) \
        $(OUTPRE)util_set.$(OBJEXT) \
        $(OUTPRE)util_token.$(OBJEXT) \
@@ -97,11 +93,9 @@ STLIBOBJS = \
        disp_com_err_status.o \
        disp_major_status.o \
        gssapi_generic.o \
-       oid_ops.o \
        rel_buffer.o \
        rel_oid_set.o \
        util_buffer.o \
-       util_oid.o \
        util_ordering.o \
        util_set.o \
        util_token.o \
@@ -170,11 +164,6 @@ gssapi_generic.so gssapi_generic.po $(OUTPRE)gssapi_generic.$(OBJEXT): \
   $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
   gssapiP_generic.h gssapi_err_generic.h gssapi_generic.c \
   gssapi_generic.h
-oid_ops.so oid_ops.po $(OUTPRE)oid_ops.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
-  $(BUILDTOP)/include/gssapi/gssapi.h $(COM_ERR_DEPS) \
-  $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
-  gssapiP_generic.h gssapi_err_generic.h gssapi_generic.h \
-  oid_ops.c
 rel_buffer.so rel_buffer.po $(OUTPRE)rel_buffer.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
   $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
@@ -190,11 +179,6 @@ util_buffer.so util_buffer.po $(OUTPRE)util_buffer.$(OBJEXT): \
   $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
   gssapiP_generic.h gssapi_err_generic.h gssapi_generic.h \
   util_buffer.c
-util_oid.so util_oid.po $(OUTPRE)util_oid.$(OBJEXT): \
-  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
-  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
-  gssapiP_generic.h gssapi_err_generic.h gssapi_generic.h \
-  util_oid.c
 util_ordering.so util_ordering.po $(OUTPRE)util_ordering.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
   $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
index 37b79e807f14d1936afae297bc21c87436d9b431..28b5b1123700b27ed6047b78bc39f7e1eba37aa6 100644 (file)
@@ -691,6 +691,13 @@ OM_uint32 KRB5_CALLCONV gss_inquire_names_for_mech
            gss_OID_set *               /* name_types */
           );
 
+/* New for V2 */
+OM_uint32 KRB5_CALLCONV gss_inquire_mechs_for_name(
+    OM_uint32 *,               /* minor_status */
+    const gss_name_t,          /* input_name */
+    gss_OID_set *              /* mech_types */
+);
+
 /*
  * The following routines are obsolete variants of gss_get_mic, gss_wrap,
  * gss_verify_mic and gss_unwrap.  They should be provided by GSSAPI V2
index ca19b18910ff049162934b09ae293f135c6973c4..2752f1dfa1ad8294141e74ea5140037e04e96e8d 100644 (file)
@@ -48,9 +48,9 @@ typedef UINT64_TYPE gssint_uint64;
 
 /** helper macros **/
 
-#define g_OID_equal(o1,o2) \
-   (((o1)->length == (o2)->length) && \
-    (memcmp((o1)->elements,(o2)->elements,(unsigned int) (o1)->length) == 0))
+#define        g_OID_equal(o1, o2) \
+       (((o1)->length == (o2)->length) && \
+       (memcmp((o1)->elements, (o2)->elements, (o1)->length) == 0))
 
 /* this code knows that an int on the wire is 32 bits.  The type of
    num should be at least this big, or the extra shifts may do weird
@@ -122,7 +122,6 @@ typedef UINT64_TYPE gssint_uint64;
 #define        g_delete_ctx_id         gssint_g_delete_ctx_id
 #define        g_delete_lucidctx_id    gssint_g_delete_lucidctx_id
 #define        g_make_string_buffer    gssint_g_make_string_buffer
-#define        g_copy_OID_set          gssint_g_copy_OID_set
 #define        g_token_size            gssint_g_token_size
 #define        g_make_token_header     gssint_g_make_token_header
 #define        g_verify_token_header   gssint_g_verify_token_header
@@ -168,8 +167,6 @@ int g_delete_lucidctx_id (g_set *vdb, void *lctx);
 
 int g_make_string_buffer (const char *str, gss_buffer_t buffer);
 
-int g_copy_OID_set (const gss_OID_set_desc * const in, gss_OID_set *out);
-
 unsigned int g_token_size (const gss_OID_desc * mech, unsigned int body_size);
 
 void g_make_token_header (const gss_OID_desc * mech, unsigned int body_size,
index 99ba45fe3814c163a889ab38f69acef9286facbd..3e976e3dbf5022d9e609d9f47a3fdcd4061c6ccb 100644 (file)
@@ -43,4 +43,7 @@ error_code G_BAD_DIRECTION, "Packet was replayed in wrong direction"
 error_code G_TOK_TRUNC, "Token is missing data"
 error_code G_REFLECT, "Token was reflected"
 error_code G_WRONG_TOKID, "Received token ID does not match expected token ID"
+error_code G_CRED_USAGE_MISMATCH, "The given credential's usage does not match the requested usage"
+error_code G_STORE_ACCEPTOR_CRED_NOSUPP, "Storing of acceptor credentials is not supported by the mechanism"
+error_code G_STORE_NON_DEFAULT_CRED_NOSUPP, "Storing of non-default credentials is not supported by the mechanism"
 end
diff --git a/src/lib/gssapi/generic/oid_ops.c b/src/lib/gssapi/generic/oid_ops.c
deleted file mode 100644 (file)
index 1234f2e..0000000
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
- * lib/gssapi/generic/oid_ops.c
- *
- * Copyright 1995 by the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * Export of this software from the United States of America may
- *   require a specific license from the United States Government.
- *   It is the responsibility of any person or organization contemplating
- *   export to obtain such a license before exporting.
- *
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
- * distribute this software and its documentation for any purpose and
- * without fee is hereby granted, provided that the above copyright
- * notice appear in all copies and that both that copyright notice and
- * this permission notice appear in supporting documentation, and that
- * the name of M.I.T. not be used in advertising or publicity pertaining
- * to distribution of the software without specific, written prior
- * permission.  Furthermore if you modify this software you must label
- * your software as modified software and not distribute it in such a
- * fashion that it might be confused with the original M.I.T. software.
- * M.I.T. makes no representations about the suitability of
- * this software for any purpose.  It is provided "as is" without express
- * or implied warranty.
- *
- */
-
-/*
- * oid_ops.c - GSS-API V2 interfaces to manipulate OIDs
- */
-
-#include "gssapiP_generic.h"
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <ctype.h>
-
-OM_uint32
-generic_gss_release_oid(minor_status, oid)
-    OM_uint32  *minor_status;
-    gss_OID    *oid;
-{
-    *minor_status = 0;
-
-    if (*oid == GSS_C_NO_OID)
-       return(GSS_S_COMPLETE);
-
-    /*
-     * The V2 API says the following!
-     *
-     * gss_release_oid[()] will recognize any of the GSSAPI's own OID values,
-     * and will silently ignore attempts to free these OIDs; for other OIDs
-     * it will call the C free() routine for both the OID data and the
-     * descriptor.  This allows applications to freely mix their own heap-
-     * allocated OID values with OIDs returned by GSS-API.
-     */
-    if ((*oid != gss_nt_user_name) &&
-       (*oid != gss_nt_machine_uid_name) &&
-       (*oid != gss_nt_string_uid_name) &&
-       (*oid != gss_nt_service_name) &&
-       (*oid != gss_nt_exported_name) &&
-       (*oid != gss_nt_service_name_v2)) {
-       free((*oid)->elements);
-       free(*oid);
-    }
-    *oid = GSS_C_NO_OID;
-    return(GSS_S_COMPLETE);
-}
-
-OM_uint32
-generic_gss_copy_oid(minor_status, oid, new_oid)
-       OM_uint32       *minor_status;
-       const gss_OID_desc * const oid;
-       gss_OID         *new_oid;
-{
-       gss_OID         p;
-
-       p = (gss_OID) malloc(sizeof(gss_OID_desc));
-       if (!p) {
-               *minor_status = ENOMEM;
-               return GSS_S_FAILURE;
-       }
-       p->length = oid->length;
-       p->elements = malloc(p->length);
-       if (!p->elements) {
-               free(p);
-               *minor_status = ENOMEM;
-               return GSS_S_FAILURE;
-       }
-       memcpy(p->elements, oid->elements, p->length);
-       *new_oid = p;
-       return(GSS_S_COMPLETE);
-}
-
-
-OM_uint32
-generic_gss_create_empty_oid_set(minor_status, oid_set)
-    OM_uint32  *minor_status;
-    gss_OID_set        *oid_set;
-{
-    if ((*oid_set = (gss_OID_set) malloc(sizeof(gss_OID_set_desc)))) {
-       memset(*oid_set, 0, sizeof(gss_OID_set_desc));
-       *minor_status = 0;
-       return(GSS_S_COMPLETE);
-    }
-    else {
-       *minor_status = ENOMEM;
-       return(GSS_S_FAILURE);
-    }
-}
-
-OM_uint32
-generic_gss_add_oid_set_member(minor_status, member_oid, oid_set)
-    OM_uint32  *minor_status;
-    const gss_OID_desc * const member_oid;
-    gss_OID_set        *oid_set;
-{
-    gss_OID    elist;
-    gss_OID    lastel;
-
-    elist = (*oid_set)->elements;
-    /* Get an enlarged copy of the array */
-    if (((*oid_set)->elements = (gss_OID) malloc(((*oid_set)->count+1) *
-                                                 sizeof(gss_OID_desc)))) {
-       /* Copy in the old junk */
-       if (elist)
-           memcpy((*oid_set)->elements,
-                  elist,
-                  ((*oid_set)->count * sizeof(gss_OID_desc)));
-
-       /* Duplicate the input element */
-       lastel = &(*oid_set)->elements[(*oid_set)->count];
-       if ((lastel->elements =
-            (void *) malloc((size_t) member_oid->length))) {
-           /* Success - copy elements */
-           memcpy(lastel->elements, member_oid->elements,
-                  (size_t) member_oid->length);
-           /* Set length */
-           lastel->length = member_oid->length;
-
-           /* Update count */
-           (*oid_set)->count++;
-           if (elist)
-               free(elist);
-           *minor_status = 0;
-           return(GSS_S_COMPLETE);
-       }
-       else
-           free((*oid_set)->elements);
-    }
-    /* Failure - restore old contents of list */
-    (*oid_set)->elements = elist;
-    *minor_status = ENOMEM;
-    return(GSS_S_FAILURE);
-}
-
-OM_uint32
-generic_gss_test_oid_set_member(minor_status, member, set, present)
-    OM_uint32  *minor_status;
-    const gss_OID_desc * const member;
-    gss_OID_set        set;
-    int                *present;
-{
-    size_t     i;
-    int                result;
-
-    result = 0;
-    for (i=0; i<set->count; i++) {
-       if ((set->elements[i].length == member->length) &&
-           !memcmp(set->elements[i].elements,
-                   member->elements,
-                   (size_t) member->length)) {
-           result = 1;
-           break;
-       }
-    }
-    *present = result;
-    *minor_status = 0;
-    return(GSS_S_COMPLETE);
-}
-
-/*
- * OID<->string routines.  These are uuuuugly.
- */
-OM_uint32
-generic_gss_oid_to_str(minor_status, oid, oid_str)
-    OM_uint32          *minor_status;
-    const gss_OID_desc * const oid;
-    gss_buffer_t       oid_str;
-{
-    char               numstr[128];
-    unsigned long      number;
-    int                        numshift;
-    size_t             string_length;
-    size_t             i;
-    unsigned char      *cp;
-    char               *bp;
-
-    /* Decoded according to krb5/gssapi_krb5.c */
-
-    /* First determine the size of the string */
-    string_length = 0;
-    number = 0;
-    numshift = 0;
-    cp = (unsigned char *) oid->elements;
-    number = (unsigned long) cp[0];
-    sprintf(numstr, "%ld ", number/40);
-    string_length += strlen(numstr);
-    sprintf(numstr, "%ld ", number%40);
-    string_length += strlen(numstr);
-    for (i=1; i<oid->length; i++) {
-       if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
-           number = (number << 7) | (cp[i] & 0x7f);
-           numshift += 7;
-       }
-       else {
-           *minor_status = EINVAL;
-           return(GSS_S_FAILURE);
-       }
-       if ((cp[i] & 0x80) == 0) {
-           sprintf(numstr, "%ld ", number);
-           string_length += strlen(numstr);
-           number = 0;
-           numshift = 0;
-       }
-    }
-    /*
-     * If we get here, we've calculated the length of "n n n ... n ".  Add 4
-     * here for "{ " and "}\0".
-     */
-    string_length += 4;
-    if ((bp = (char *) malloc(string_length))) {
-       strcpy(bp, "{ ");
-       number = (unsigned long) cp[0];
-       sprintf(numstr, "%ld ", number/40);
-       strcat(bp, numstr);
-       sprintf(numstr, "%ld ", number%40);
-       strcat(bp, numstr);
-       number = 0;
-       cp = (unsigned char *) oid->elements;
-       for (i=1; i<oid->length; i++) {
-           number = (number << 7) | (cp[i] & 0x7f);
-           if ((cp[i] & 0x80) == 0) {
-               sprintf(numstr, "%ld ", number);
-               strcat(bp, numstr);
-               number = 0;
-           }
-       }
-       strcat(bp, "}");
-       oid_str->length = strlen(bp)+1;
-       oid_str->value = (void *) bp;
-       *minor_status = 0;
-       return(GSS_S_COMPLETE);
-    }
-    *minor_status = ENOMEM;
-    return(GSS_S_FAILURE);
-}
-
-OM_uint32
-generic_gss_str_to_oid(minor_status, oid_str, oid)
-    OM_uint32          *minor_status;
-    gss_buffer_t       oid_str;
-    gss_OID            *oid;
-{
-    char       *cp, *bp, *startp;
-    int                brace;
-    long       numbuf;
-    long       onumbuf;
-    OM_uint32  nbytes;
-    int                idx;
-    unsigned char *op;
-
-    brace = 0;
-    bp = (char *) oid_str->value;
-    cp = bp;
-    /* Skip over leading space */
-    while ((bp < &cp[oid_str->length]) && isspace((int) *bp))
-       bp++;
-    if (*bp == '{') {
-       brace = 1;
-       bp++;
-    }
-    while ((bp < &cp[oid_str->length]) && isspace((int) *bp))
-       bp++;
-    startp = bp;
-    nbytes = 0;
-
-    /*
-     * The first two numbers are chewed up by the first octet.
-     */
-    if (sscanf(bp, "%ld", &numbuf) != 1) {
-       *minor_status = EINVAL;
-       return(GSS_S_FAILURE);
-    }
-    while ((bp < &cp[oid_str->length]) && isdigit((int) *bp))
-       bp++;
-    while ((bp < &cp[oid_str->length]) && isspace((int) *bp))
-       bp++;
-    if (sscanf(bp, "%ld", &numbuf) != 1) {
-       *minor_status = EINVAL;
-       return(GSS_S_FAILURE);
-    }
-    while ((bp < &cp[oid_str->length]) && isdigit((int) *bp))
-       bp++;
-    while ((bp < &cp[oid_str->length]) && isspace((int) *bp))
-       bp++;
-    nbytes++;
-    while (isdigit((int) *bp)) {
-       if (sscanf(bp, "%ld", &numbuf) != 1) {
-           *minor_status = EINVAL;
-           return(GSS_S_FAILURE);
-       }
-       while (numbuf) {
-           nbytes++;
-           numbuf >>= 7;
-       }
-       while ((bp < &cp[oid_str->length]) && isdigit((int) *bp))
-           bp++;
-       while ((bp < &cp[oid_str->length]) && isspace((int) *bp))
-           bp++;
-    }
-    if (brace && (*bp != '}')) {
-       *minor_status = EINVAL;
-       return(GSS_S_FAILURE);
-    }
-
-    /*
-     * Phew!  We've come this far, so the syntax is good.
-     */
-    if ((*oid = (gss_OID) malloc(sizeof(gss_OID_desc)))) {
-       if (((*oid)->elements = (void *) malloc((size_t) nbytes))) {
-           (*oid)->length = nbytes;
-           op = (unsigned char *) (*oid)->elements;
-           bp = startp;
-           sscanf(bp, "%ld", &numbuf);
-           while (isdigit((int) *bp))
-               bp++;
-           while (isspace((int) *bp))
-               bp++;
-           onumbuf = 40*numbuf;
-           sscanf(bp, "%ld", &numbuf);
-           onumbuf += numbuf;
-           *op = (unsigned char) onumbuf;
-           op++;
-           while (isdigit((int) *bp))
-               bp++;
-           while (isspace((int) *bp))
-               bp++;
-           while (isdigit((int) *bp)) {
-               sscanf(bp, "%ld", &numbuf);
-               nbytes = 0;
-               /* Have to fill in the bytes msb-first */
-               onumbuf = numbuf;
-               while (numbuf) {
-                   nbytes++;
-                   numbuf >>= 7;
-               }
-               numbuf = onumbuf;
-               op += nbytes;
-               idx = -1;
-               while (numbuf) {
-                   op[idx] = (unsigned char) numbuf & 0x7f;
-                   if (idx != -1)
-                       op[idx] |= 0x80;
-                   idx--;
-                   numbuf >>= 7;
-               }
-               while (isdigit((int) *bp))
-                   bp++;
-               while (isspace((int) *bp))
-                   bp++;
-           }
-           *minor_status = 0;
-           return(GSS_S_COMPLETE);
-       }
-       else {
-           free(*oid);
-           *oid = GSS_C_NO_OID;
-       }
-    }
-    *minor_status = ENOMEM;
-    return(GSS_S_FAILURE);
-}
-
diff --git a/src/lib/gssapi/generic/util_oid.c b/src/lib/gssapi/generic/util_oid.c
deleted file mode 100644 (file)
index 60b1e15..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 1993 by OpenVision Technologies, Inc.
- * 
- * Permission to use, copy, modify, distribute, and sell this software
- * and its documentation for any purpose is hereby granted without fee,
- * provided that the above copyright notice appears in all copies and
- * that both that copyright notice and this permission notice appear in
- * supporting documentation, and that the name of OpenVision not be used
- * in advertising or publicity pertaining to distribution of the software
- * without specific, written prior permission. OpenVision makes no
- * representations about the suitability of this software for any
- * purpose.  It is provided "as is" without express or implied warranty.
- * 
- * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
- * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
- * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "gssapiP_generic.h"
-#include "string.h"
-
-/*
- * $Id$
- */
-
-int
-g_copy_OID_set(in, out)
-     const gss_OID_set_desc * const in;
-     gss_OID_set *out;
-{
-   gss_OID_set copy;
-   gss_OID     new_oid;
-   size_t      i;
-   size_t      len;
-
-   *out = NULL;
-
-   if ((copy =
-       (gss_OID_set_desc *) xmalloc(sizeof(gss_OID_set_desc))) == NULL)
-      return(0);
-
-   copy->count = in->count;
-   len = sizeof(gss_OID_desc) * copy->count;
-
-   if ((copy->elements = 
-       (gss_OID_desc *) xmalloc( len )) == NULL) {
-      xfree(copy);
-      return(0);
-   }
-
-   memset( copy->elements, 0, len );
-   
-   for (i=0; i<in->count; i++) {
-      len = in->elements[i].length;
-      new_oid = &(copy->elements[i]);
-      new_oid->elements = xmalloc( len );
-      if ( new_oid->elements == NULL ) {
-         while( i>0 ) {
-            i--;
-            new_oid = &(copy->elements[i]);
-            if ( new_oid->elements!=NULL )
-               xfree( new_oid->elements );
-         }
-         xfree( copy->elements );
-         xfree( copy );
-         return( 0 );
-      }
-      memcpy( new_oid->elements, in->elements[i].elements, len );
-      new_oid->length = len;
-   }
-
-   *out = copy;
-   return(1);
-}
index f16359497a5291222ad3989607290392aadbb245..16031e26efc8b6548abfeb9873e09664bfeb5e47 100644 (file)
@@ -7,6 +7,8 @@
 #include "gss_libinit.h"
 #include "k5-platform.h"
 
+#include "mglueP.h"
+
 /*
  * Initialize the GSSAPI library.
  */
@@ -26,6 +28,9 @@ int gssint_lib_init(void)
     add_error_table(&et_k5g_error_table);
     add_error_table(&et_ggss_error_table);
 #endif
+    err = gssint_mechglue_init();
+    if (err)
+       return err;
     err = k5_mutex_finish_init(&gssint_krb5_keytab_lock);
     if (err)
        return err;
@@ -61,6 +66,7 @@ void gssint_lib_fini(void)
     k5_mutex_destroy(&kg_vdb.mutex);
     k5_mutex_destroy(&kg_kdc_flag_mutex);
     k5_mutex_destroy(&gssint_krb5_keytab_lock);
+    gssint_mechglue_fini();
 }
 
 OM_uint32 gssint_initialize_library (void)
index 7d9e8826ff43bcede5646aae36e029be579067cd..7f6b80034eabd1544e75ea43246ba4599d914632 100644 (file)
@@ -2,7 +2,7 @@ thisconfigdir=../../..
 myfulldir=lib/gssapi/krb5
 mydir=lib/gssapi/krb5
 BUILDTOP=$(REL)..$(S)..$(S)..
-LOCALINCLUDES = -I. -I$(srcdir) -I$(srcdir)/.. -I../generic -I$(srcdir)/../generic
+LOCALINCLUDES = -I. -I$(srcdir) -I$(srcdir)/.. -I../generic -I$(srcdir)/../generic -I../mechglue -I$(srcdir)/../mechglue
 DEFS=
 
 ##DOS##BUILDTOP = ..\..\..
@@ -438,6 +438,7 @@ indicate_mechs.so indicate_mechs.po $(OUTPRE)indicate_mechs.$(OBJEXT): \
   $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
   $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
   $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  $(srcdir)/../mechglue/mechglue.h $(srcdir)/../mechglue/mglueP.h \
   ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \
   gssapi_krb5.h indicate_mechs.c
 init_sec_context.so init_sec_context.po $(OUTPRE)init_sec_context.$(OBJEXT): \
@@ -525,6 +526,7 @@ krb5_gss_glue.so krb5_gss_glue.po $(OUTPRE)krb5_gss_glue.$(OBJEXT): \
   $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
   $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
   $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  $(srcdir)/../mechglue/mechglue.h $(srcdir)/../mechglue/mglueP.h \
   ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \
   gssapi_krb5.h krb5_gss_glue.c
 lucid_context.so lucid_context.po $(OUTPRE)lucid_context.$(OBJEXT): \
index ecda750f593ea7f51048933fce0c77405f6d4e4e..f461e8d501c3a37e72fe390a6dabb596668a07b9 100644 (file)
@@ -347,6 +347,12 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
                                      &ptr, KG_TOK_CTX_AP_REQ,
                                      input_token->length, 1))) {
        mech_used = gss_mech_krb5;
+   } else if ((code == G_WRONG_MECH)
+             &&!(code = g_verify_token_header((gss_OID) gss_mech_krb5_wrong,
+                                            &(ap_req.length), 
+                                            &ptr, KG_TOK_CTX_AP_REQ,
+                                            input_token->length, 1))) {
+       mech_used = gss_mech_krb5_wrong;
    } else if ((code == G_WRONG_MECH) &&
              !(code = g_verify_token_header(gss_mech_krb5_old,
                                             &(ap_req.length), 
index aed67c3527377c85a5b230db661f747619666c3b..0f7c9cd9c0a2bb86a537ac6e2fb1e3ea6d8aad97 100644 (file)
@@ -34,7 +34,8 @@ OM_uint32 krb5_gss_canonicalize_name(OM_uint32  *minor_status,
                                     const gss_OID mech_type,
                                     gss_name_t *output_name)
 {
-    if (!g_OID_equal(gss_mech_krb5, mech_type) &&
+    if ((mech_type != GSS_C_NULL_OID) &&
+       !g_OID_equal(gss_mech_krb5, mech_type) &&
        !g_OID_equal(gss_mech_krb5_old, mech_type)) {
        *minor_status = 0;
        return(GSS_S_BAD_MECH);
index b0cc96fd8f498f4a2d88d9f55bf2e14d3c1e01bc..195be0f84299ded14ce72aaef82fbb85f2a419a1 100644 (file)
@@ -1,7 +1,7 @@
 #include "gssapiP_krb5.h"
 
 OM_uint32 KRB5_CALLCONV 
-gss_krb5_copy_ccache(minor_status, cred_handle, out_ccache)
+gss_krb5int_copy_ccache(minor_status, cred_handle, out_ccache)
      OM_uint32 *minor_status;
      gss_cred_id_t cred_handle;
      krb5_ccache out_ccache;
index 74f1532ae38f2480624d85b8b0e360d4fd1cc332..19841a086d6710c9adfb6ae97136f1246b82bec8 100644 (file)
@@ -27,7 +27,7 @@
  */
 
 OM_uint32 KRB5_CALLCONV 
-gss_krb5_get_tkt_flags(minor_status, context_handle, ticket_flags)
+gss_krb5int_get_tkt_flags(minor_status, context_handle, ticket_flags)
      OM_uint32 *minor_status;
      gss_ctx_id_t context_handle;
      krb5_flags *ticket_flags;
index 3539ac7a91c28c8bc4ebf07554501e62e8f604c3..08e76d77039a36ef87c5c8f96f60529290075776 100644 (file)
 
 /** constants **/
 
+#define GSS_MECH_KRB5_OID_LENGTH 9
+#define GSS_MECH_KRB5_OID "\052\206\110\206\367\022\001\002\002"
+
+#define GSS_MECH_KRB5_OLD_OID_LENGTH 5
+#define GSS_MECH_KRB5_OLD_OID "\053\005\001\005\002"
+
+/* Incorrect krb5 mech OID emitted by MS. */
+#define GSS_MECH_KRB5_WRONG_OID_LENGTH 9
+#define GSS_MECH_KRB5_WRONG_OID "\052\206\110\202\367\022\001\002\002"
+
+
 #define CKSUMTYPE_KG_CB                0x8003
 
 #define KG_TOK_CTX_AP_REQ      0x0100
@@ -580,6 +591,11 @@ OM_uint32 krb5_gss_release_oid
            gss_OID *                   /* oid */
           );
 
+OM_uint32 krb5_gss_internal_release_oid
+(OM_uint32 *,          /* minor_status */
+           gss_OID *                   /* oid */
+          );
+
 OM_uint32 krb5_gss_inquire_names_for_mech
 (OM_uint32 *,          /* minor_status */
            gss_OID,                    /* mechanism */
@@ -631,6 +647,33 @@ OM_uint32 gss_krb5int_unseal_token_v3(krb5_context *contextptr,
                                      int *conf_state, int *qop_state, 
                                      int toktype);
 
+/*
+ * These take unglued krb5-mech-specific contexts.
+ */
+
+OM_uint32 KRB5_CALLCONV gss_krb5int_get_tkt_flags 
+       (OM_uint32 *minor_status,
+                  gss_ctx_id_t context_handle,
+                  krb5_flags *ticket_flags);
+
+OM_uint32 KRB5_CALLCONV gss_krb5int_copy_ccache
+       (OM_uint32 *minor_status,
+                  gss_cred_id_t cred_handle,
+                  krb5_ccache out_ccache);
+
+OM_uint32 KRB5_CALLCONV
+gss_krb5int_set_allowable_enctypes(OM_uint32 *minor_status, 
+                                  gss_cred_id_t cred,
+                                  OM_uint32 num_ktypes,
+                                  krb5_enctype *ktypes);
+
+OM_uint32 KRB5_CALLCONV
+gss_krb5int_export_lucid_sec_context(OM_uint32 *minor_status,
+                                    gss_ctx_id_t *context_handle,
+                                    OM_uint32 version,
+                                    void **kctx);
+
+
 extern k5_mutex_t kg_kdc_flag_mutex;
 krb5_error_code krb5_gss_init_context (krb5_context *ctxp);
 
index 94f11ef0321c5095f9274cb7e3061ff7c4eb9e46..f1c27e487e51dc462e68de3154aaca66b8733032 100644 (file)
 
 const gss_OID_desc krb5_gss_oid_array[] = {
    /* this is the official, rfc-specified OID */
-   {9, "\052\206\110\206\367\022\001\002\002"},
-   /* this is the unofficial, wrong OID */
-   {5, "\053\005\001\005\002"},
+   {GSS_MECH_KRB5_OID_LENGTH, GSS_MECH_KRB5_OID},
+   /* this pre-RFC mech OID */
+   {GSS_MECH_KRB5_OLD_OID_LENGTH, GSS_MECH_KRB5_OLD_OID},
+   /* this is the unofficial, incorrect mech OID emitted by MS */
+   {GSS_MECH_KRB5_WRONG_OID_LENGTH, GSS_MECH_KRB5_WRONG_OID},
    /* this is the v2 assigned OID */
    {9, "\052\206\110\206\367\022\001\002\003"},
    /* these two are name type OID's */
@@ -108,14 +110,15 @@ const gss_OID_desc krb5_gss_oid_array[] = {
 
 const gss_OID_desc * const gss_mech_krb5              = krb5_gss_oid_array+0;
 const gss_OID_desc * const gss_mech_krb5_old          = krb5_gss_oid_array+1;
-const gss_OID_desc * const gss_nt_krb5_name           = krb5_gss_oid_array+3;
-const gss_OID_desc * const gss_nt_krb5_principal      = krb5_gss_oid_array+4;
-const gss_OID_desc * const GSS_KRB5_NT_PRINCIPAL_NAME = krb5_gss_oid_array+3;
+const gss_OID_desc * const gss_mech_krb5_wrong        = krb5_gss_oid_array+2;
+const gss_OID_desc * const gss_nt_krb5_name           = krb5_gss_oid_array+4;
+const gss_OID_desc * const gss_nt_krb5_principal      = krb5_gss_oid_array+5;
+const gss_OID_desc * const GSS_KRB5_NT_PRINCIPAL_NAME = krb5_gss_oid_array+4;
 
 static const gss_OID_set_desc oidsets[] = {
    {1, (gss_OID) krb5_gss_oid_array+0},
    {1, (gss_OID) krb5_gss_oid_array+1},
-   {2, (gss_OID) krb5_gss_oid_array+0},
+   {3, (gss_OID) krb5_gss_oid_array+0},
    {1, (gss_OID) krb5_gss_oid_array+2},
    {3, (gss_OID) krb5_gss_oid_array+0},
 };
index 20002478ece302c27d62b3099d98082af1ea7c08..647d14e3953b28c7287ccb74b74c64515da536d5 100644 (file)
@@ -72,6 +72,7 @@ GSS_DLLIMP extern const gss_OID_desc * const GSS_KRB5_NT_PRINCIPAL_NAME;
 
 GSS_DLLIMP extern const gss_OID_desc * const gss_mech_krb5;
 GSS_DLLIMP extern const gss_OID_desc * const gss_mech_krb5_old;
+GSS_DLLIMP extern const gss_OID_desc * const gss_mech_krb5_wrong;
 GSS_DLLIMP extern const gss_OID_set_desc * const gss_mech_set_krb5;
 GSS_DLLIMP extern const gss_OID_set_desc * const gss_mech_set_krb5_old;
 GSS_DLLIMP extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
index 48baf1a0e58660f97774f7185320ef00ada9e882..9f2a2a1aa00d74de568af717d5b7ac31efdb5a52 100644 (file)
@@ -25,6 +25,7 @@
  */
 
 #include "gssapiP_krb5.h"
+#include "mglueP.h"
 
 OM_uint32
 krb5_gss_indicate_mechs(minor_status, mech_set)
@@ -33,7 +34,7 @@ krb5_gss_indicate_mechs(minor_status, mech_set)
 {
    *minor_status = 0;
 
-   if (! g_copy_OID_set(gss_mech_set_krb5_both, mech_set)) {
+   if (! gssint_copy_oid_set(minor_status, gss_mech_set_krb5_both, mech_set)) {
          *mech_set     = GSS_C_NO_OID_SET;
          *minor_status = ENOMEM;
          return(GSS_S_FAILURE);
index 2ce795bb83ba42b478642002e07a76af151be3b9..6d27fd33cb64bfa59c20007f04bc78d37739c3bf 100644 (file)
@@ -918,6 +918,9 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
    } else if (g_OID_equal(mech_type, gss_mech_krb5_old)) {
        if (!cred->prerfc_mech)
           err = 1;
+   } else if (g_OID_equal(mech_type, gss_mech_krb5_wrong)) {
+       if (!cred->rfc_mech)
+          err = 1;
    } else {
        err = 1;
    }
index 583881d8e443cc5f1377d80f771b02715a127bdc..758cfbcb0f28459520fb9a3841f9e24d06a6b275 100644 (file)
  */
 
 #include "gssapiP_krb5.h"
+#include "mglueP.h"
 
-OM_uint32 KRB5_CALLCONV
-gss_accept_sec_context(minor_status, context_handle, verifier_cred_handle,
+/** mechglue wrappers **/
+
+static OM_uint32 k5glue_acquire_cred
+(void *, OM_uint32*,       /* minor_status */
+            gss_name_t,       /* desired_name */
+            OM_uint32,        /* time_req */
+            gss_OID_set,      /* desired_mechs */
+            gss_cred_usage_t, /* cred_usage */
+            gss_cred_id_t*,   /* output_cred_handle */
+            gss_OID_set*,     /* actual_mechs */
+            OM_uint32*        /* time_rec */
+           );
+
+static OM_uint32 k5glue_release_cred
+(void *, OM_uint32*,       /* minor_status */
+            gss_cred_id_t*    /* cred_handle */
+           );
+
+static OM_uint32 k5glue_init_sec_context
+(void *, OM_uint32*,       /* minor_status */
+            gss_cred_id_t,    /* claimant_cred_handle */
+            gss_ctx_id_t*,    /* context_handle */
+            gss_name_t,       /* target_name */
+            gss_OID,          /* mech_type */
+            OM_uint32,        /* req_flags */
+            OM_uint32,        /* time_req */
+            gss_channel_bindings_t,
+                              /* input_chan_bindings */
+            gss_buffer_t,     /* input_token */
+            gss_OID*,         /* actual_mech_type */
+            gss_buffer_t,     /* output_token */
+            OM_uint32*,       /* ret_flags */
+            OM_uint32*        /* time_rec */
+           );
+
+static OM_uint32 k5glue_accept_sec_context
+(void *, OM_uint32*,       /* minor_status */
+            gss_ctx_id_t*,    /* context_handle */
+            gss_cred_id_t,    /* verifier_cred_handle */
+            gss_buffer_t,     /* input_token_buffer */
+            gss_channel_bindings_t,
+                              /* input_chan_bindings */
+            gss_name_t*,      /* src_name */
+            gss_OID*,         /* mech_type */
+            gss_buffer_t,     /* output_token */
+            OM_uint32*,       /* ret_flags */
+            OM_uint32*,       /* time_rec */
+            gss_cred_id_t*    /* delegated_cred_handle */
+           );
+
+static OM_uint32 k5glue_process_context_token
+(void *, OM_uint32*,       /* minor_status */
+            gss_ctx_id_t,     /* context_handle */
+            gss_buffer_t      /* token_buffer */
+           );
+
+static OM_uint32 k5glue_delete_sec_context
+(void *, OM_uint32*,       /* minor_status */
+            gss_ctx_id_t*,    /* context_handle */
+            gss_buffer_t      /* output_token */
+           );
+
+static OM_uint32 k5glue_context_time
+(void *, OM_uint32*,       /* minor_status */
+            gss_ctx_id_t,     /* context_handle */
+            OM_uint32*        /* time_rec */
+           );
+
+static OM_uint32 k5glue_sign
+(void *, OM_uint32*,       /* minor_status */
+            gss_ctx_id_t,     /* context_handle */
+            int,              /* qop_req */
+            gss_buffer_t,     /* message_buffer */
+            gss_buffer_t      /* message_token */
+           );
+
+static OM_uint32 k5glue_verify
+(void *, OM_uint32*,       /* minor_status */
+            gss_ctx_id_t,     /* context_handle */
+            gss_buffer_t,     /* message_buffer */
+            gss_buffer_t,     /* token_buffer */
+            int*              /* qop_state */
+           );
+
+static OM_uint32 k5glue_seal
+(void *, OM_uint32*,       /* minor_status */
+            gss_ctx_id_t,     /* context_handle */
+            int,              /* conf_req_flag */
+            int,              /* qop_req */
+            gss_buffer_t,     /* input_message_buffer */
+            int*,             /* conf_state */
+            gss_buffer_t      /* output_message_buffer */
+           );
+
+static OM_uint32 k5glue_unseal
+(void *, OM_uint32*,       /* minor_status */
+            gss_ctx_id_t,     /* context_handle */
+            gss_buffer_t,     /* input_message_buffer */
+            gss_buffer_t,     /* output_message_buffer */
+            int*,             /* conf_state */
+            int*              /* qop_state */
+           );
+
+static OM_uint32 k5glue_display_status
+(void *, OM_uint32*,       /* minor_status */
+            OM_uint32,        /* status_value */
+            int,              /* status_type */
+            gss_OID,          /* mech_type */
+            OM_uint32*,       /* message_context */
+            gss_buffer_t      /* status_string */
+           );
+
+static OM_uint32 k5glue_indicate_mechs
+(void *, OM_uint32*,       /* minor_status */
+            gss_OID_set*      /* mech_set */
+           );
+
+static OM_uint32 k5glue_compare_name
+(void *, OM_uint32*,       /* minor_status */
+            gss_name_t,       /* name1 */
+            gss_name_t,       /* name2 */
+            int*              /* name_equal */
+           );
+
+static OM_uint32 k5glue_display_name
+(void *, OM_uint32*,      /* minor_status */
+            gss_name_t,      /* input_name */
+            gss_buffer_t,    /* output_name_buffer */
+            gss_OID*         /* output_name_type */
+           );
+
+static OM_uint32 k5glue_import_name
+(void *, OM_uint32*,       /* minor_status */
+            gss_buffer_t,     /* input_name_buffer */
+            gss_OID,          /* input_name_type */
+            gss_name_t*       /* output_name */
+           );
+
+static OM_uint32 k5glue_release_name
+(void *, OM_uint32*,       /* minor_status */
+            gss_name_t*       /* input_name */
+           );
+
+static OM_uint32 k5glue_inquire_cred
+(void *, OM_uint32 *,      /* minor_status */
+            gss_cred_id_t,    /* cred_handle */
+            gss_name_t *,     /* name */
+            OM_uint32 *,      /* lifetime */
+            gss_cred_usage_t*,/* cred_usage */
+            gss_OID_set *     /* mechanisms */
+           );
+
+static OM_uint32 k5glue_inquire_context
+(void *, OM_uint32*,       /* minor_status */
+           gss_ctx_id_t,     /* context_handle */
+           gss_name_t*,      /* initiator_name */
+           gss_name_t*,      /* acceptor_name */
+           OM_uint32*,       /* lifetime_rec */
+           gss_OID*,         /* mech_type */
+           OM_uint32*,       /* ret_flags */
+           int*,             /* locally_initiated */
+           int*              /* open */
+          );
+
+#if 0
+/* New V2 entry points */
+static OM_uint32 k5glue_get_mic
+(void *, OM_uint32 *,          /* minor_status */
+           gss_ctx_id_t,               /* context_handle */
+           gss_qop_t,                  /* qop_req */
+           gss_buffer_t,               /* message_buffer */
+           gss_buffer_t                /* message_token */
+          );
+
+static OM_uint32 k5glue_verify_mic
+(void *, OM_uint32 *,          /* minor_status */
+           gss_ctx_id_t,               /* context_handle */
+           gss_buffer_t,               /* message_buffer */
+           gss_buffer_t,               /* message_token */
+           gss_qop_t *                 /* qop_state */
+          );
+
+static OM_uint32 k5glue_wrap
+(void *, OM_uint32 *,          /* minor_status */
+           gss_ctx_id_t,               /* context_handle */
+           int,                        /* conf_req_flag */
+           gss_qop_t,                  /* qop_req */
+           gss_buffer_t,               /* input_message_buffer */
+           int *,                      /* conf_state */
+           gss_buffer_t                /* output_message_buffer */
+          );
+
+static OM_uint32 k5glue_unwrap
+(void *, OM_uint32 *,          /* minor_status */
+           gss_ctx_id_t,               /* context_handle */
+           gss_buffer_t,               /* input_message_buffer */
+           gss_buffer_t,               /* output_message_buffer */
+           int *,                      /* conf_state */
+           gss_qop_t *                 /* qop_state */
+          );
+#endif
+
+static OM_uint32 k5glue_wrap_size_limit
+(void *, OM_uint32 *,          /* minor_status */
+           gss_ctx_id_t,               /* context_handle */
+           int,                        /* conf_req_flag */
+           gss_qop_t,                  /* qop_req */
+           OM_uint32,                  /* req_output_size */
+           OM_uint32 *                 /* max_input_size */
+          );
+
+#if 0
+static OM_uint32 k5glue_import_name_object
+(void *, OM_uint32 *,          /* minor_status */
+           void *,                     /* input_name */
+           gss_OID,                    /* input_name_type */
+           gss_name_t *                /* output_name */
+          );
+
+static OM_uint32 k5glue_export_name_object
+(void *, OM_uint32 *,          /* minor_status */
+           gss_name_t,                 /* input_name */
+           gss_OID,                    /* desired_name_type */
+           void * *                    /* output_name */
+          );
+#endif
+
+static OM_uint32 k5glue_add_cred
+(void *, OM_uint32 *,          /* minor_status */
+           gss_cred_id_t,              /* input_cred_handle */
+           gss_name_t,                 /* desired_name */
+           gss_OID,                    /* desired_mech */
+           gss_cred_usage_t,           /* cred_usage */
+           OM_uint32,                  /* initiator_time_req */
+           OM_uint32,                  /* acceptor_time_req */
+           gss_cred_id_t *,            /* output_cred_handle */
+           gss_OID_set *,              /* actual_mechs */
+           OM_uint32 *,                /* initiator_time_rec */
+           OM_uint32 *                 /* acceptor_time_rec */
+          );
+
+static OM_uint32 k5glue_inquire_cred_by_mech
+(void *, OM_uint32  *,         /* minor_status */
+           gss_cred_id_t,              /* cred_handle */
+           gss_OID,                    /* mech_type */
+           gss_name_t *,               /* name */
+           OM_uint32 *,                /* initiator_lifetime */
+           OM_uint32 *,                /* acceptor_lifetime */
+           gss_cred_usage_t *          /* cred_usage */
+          );
+
+static OM_uint32 k5glue_export_sec_context
+(void *, OM_uint32 *,          /* minor_status */
+           gss_ctx_id_t *,             /* context_handle */
+           gss_buffer_t                /* interprocess_token */
+           );
+
+static OM_uint32 k5glue_import_sec_context
+(void *, OM_uint32 *,          /* minor_status */
+           gss_buffer_t,               /* interprocess_token */
+           gss_ctx_id_t *              /* context_handle */
+           );
+
+krb5_error_code k5glue_ser_init(krb5_context);
+
+static OM_uint32 k5glue_internal_release_oid
+(void *, OM_uint32 *,          /* minor_status */
+           gss_OID *                   /* oid */
+          );
+
+static OM_uint32 k5glue_inquire_names_for_mech
+(void *, OM_uint32 *,          /* minor_status */
+           gss_OID,                    /* mechanism */
+           gss_OID_set *               /* name_types */
+          );
+
+#if 0
+static OM_uint32 k5glue_canonicalize_name
+(void *, OM_uint32  *,         /* minor_status */
+           const gss_name_t,           /* input_name */
+           const gss_OID,              /* mech_type */
+           gss_name_t *                /* output_name */
+        );
+#endif
+
+static OM_uint32 k5glue_export_name
+(void *, OM_uint32  *,         /* minor_status */
+           const gss_name_t,           /* input_name */
+           gss_buffer_t                /* exported_name */
+        );
+
+#if 0
+static OM_uint32 k5glue_duplicate_name
+(void *, OM_uint32  *,         /* minor_status */
+           const gss_name_t,           /* input_name */
+           gss_name_t *                /* dest_name */
+        );
+#endif
+
+#if 0
+static OM_uint32 k5glue_validate_cred
+(void *, OM_uint32 *,          /* minor_status */
+           gss_cred_id_t               /* cred */
+         );
+#endif
+
+/*
+ * The krb5 mechanism provides two mech OIDs; use this initializer to
+ * ensure that both dispatch tables contain identical function
+ * pointers.
+ */
+#define KRB5_GSS_CONFIG_INIT                           \
+    NULL,                                              \
+    k5glue_acquire_cred,                               \
+    k5glue_release_cred,                               \
+    k5glue_init_sec_context,                           \
+    k5glue_accept_sec_context,                         \
+    k5glue_process_context_token,                      \
+    k5glue_delete_sec_context,                         \
+    k5glue_context_time,                               \
+    k5glue_sign,                                       \
+    k5glue_verify,                                     \
+    k5glue_seal,                                       \
+    k5glue_unseal,                                     \
+    k5glue_display_status,                             \
+    k5glue_indicate_mechs,                             \
+    k5glue_compare_name,                               \
+    k5glue_display_name,                               \
+    k5glue_import_name,                                        \
+    k5glue_release_name,                               \
+    k5glue_inquire_cred,                               \
+    k5glue_add_cred,                                   \
+    k5glue_export_sec_context,                         \
+    k5glue_import_sec_context,                         \
+    k5glue_inquire_cred_by_mech,                       \
+    k5glue_inquire_names_for_mech,                     \
+    k5glue_inquire_context,                            \
+    k5glue_internal_release_oid,                       \
+    k5glue_wrap_size_limit,                            \
+    NULL,                      /* pname_to_uid */      \
+    NULL,                      /* userok */            \
+    k5glue_export_name,                                        \
+    NULL                       /* store_cred */
+
+static struct gss_config krb5_mechanism = {
+    100, "kerberos_v5",
+    { GSS_MECH_KRB5_OID_LENGTH, GSS_MECH_KRB5_OID },
+    KRB5_GSS_CONFIG_INIT
+};
+
+static struct gss_config krb5_mechanism_old = {
+    200, "kerberos_v5 (pre-RFC OID)",
+    { GSS_MECH_KRB5_OLD_OID_LENGTH, GSS_MECH_KRB5_OLD_OID },
+    KRB5_GSS_CONFIG_INIT
+};
+
+static struct gss_config krb5_mechanism_wrong = {
+    300, "kerberos_v5 (wrong OID)",
+    { GSS_MECH_KRB5_WRONG_OID_LENGTH, GSS_MECH_KRB5_WRONG_OID },
+    KRB5_GSS_CONFIG_INIT
+};
+
+static gss_mechanism krb5_mech_configs[] = {
+    &krb5_mechanism, &krb5_mechanism_old, &krb5_mechanism_wrong, NULL
+};
+
+#ifdef MS_BUG_TEST
+static gss_mechanism krb5_mech_configs_hack[] = {
+    &krb5_mechanism, &krb5_mechanism_old, NULL
+}
+#endif
+
+#if 1
+#define gssint_get_mech_configs krb5_gss_get_mech_configs
+#endif
+
+gss_mechanism *
+gssint_get_mech_configs(void)
+{
+#ifdef MS_BUG_TEST
+    char *envstr = getenv("MS_FORCE_NO_MSOID");
+
+    if (envstr != NULL && strcmp(envstr, "1") == 0) {
+       return krb5_mech_configs_hack;
+    }
+#endif
+    return krb5_mech_configs;
+}
+
+static OM_uint32
+k5glue_accept_sec_context(ctx, minor_status, context_handle, verifier_cred_handle,
                       input_token, input_chan_bindings, src_name, mech_type, 
                       output_token, ret_flags, time_rec, delegated_cred_handle)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_ctx_id_t *context_handle;
      gss_cred_id_t verifier_cred_handle;
@@ -55,9 +446,10 @@ gss_accept_sec_context(minor_status, context_handle, verifier_cred_handle,
                                      delegated_cred_handle));
 }
 
-OM_uint32 KRB5_CALLCONV
-gss_acquire_cred(minor_status, desired_name, time_req, desired_mechs,
+static OM_uint32
+k5glue_acquire_cred(ctx, minor_status, desired_name, time_req, desired_mechs,
                 cred_usage, output_cred_handle, actual_mechs, time_rec)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_name_t desired_name;
      OM_uint32 time_req;
@@ -78,11 +470,12 @@ gss_acquire_cred(minor_status, desired_name, time_req, desired_mechs,
 }
 
 /* V2 */
-OM_uint32 KRB5_CALLCONV
-gss_add_cred(minor_status, input_cred_handle, desired_name, desired_mech,
+static OM_uint32
+k5glue_add_cred(ctx, minor_status, input_cred_handle, desired_name, desired_mech,
             cred_usage, initiator_time_req, acceptor_time_req,
             output_cred_handle, actual_mechs, initiator_time_rec,
             acceptor_time_rec)
+    void *ctx;
     OM_uint32           *minor_status;
     gss_cred_id_t      input_cred_handle;
     gss_name_t         desired_name;
@@ -102,18 +495,22 @@ gss_add_cred(minor_status, input_cred_handle, desired_name, desired_mech,
                             acceptor_time_rec));
 }
 
+#if 0
 /* V2 */
-OM_uint32 KRB5_CALLCONV
-gss_add_oid_set_member(minor_status, member_oid, oid_set)
+static OM_uint32
+k5glue_add_oid_set_member(ctx, minor_status, member_oid, oid_set)
+    void *ctx;
     OM_uint32   *minor_status;
     gss_OID    member_oid;
     gss_OID_set         *oid_set;
 {
     return(generic_gss_add_oid_set_member(minor_status, member_oid, oid_set));
 }
+#endif
 
-OM_uint32 KRB5_CALLCONV
-gss_compare_name(minor_status, name1, name2, name_equal)
+static OM_uint32
+k5glue_compare_name(ctx, minor_status, name1, name2, name_equal)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_name_t name1;
      gss_name_t name2;
@@ -123,8 +520,9 @@ gss_compare_name(minor_status, name1, name2, name_equal)
                                name2, name_equal));
 }
 
-OM_uint32 KRB5_CALLCONV
-gss_context_time(minor_status, context_handle, time_rec)
+static OM_uint32
+k5glue_context_time(ctx, minor_status, context_handle, time_rec)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_ctx_id_t context_handle;
      OM_uint32 *time_rec;
@@ -133,17 +531,21 @@ gss_context_time(minor_status, context_handle, time_rec)
                                time_rec));
 }
 
+#if 0
 /* V2 */
-OM_uint32 KRB5_CALLCONV
-gss_create_empty_oid_set(minor_status, oid_set)
+static OM_uint32
+k5glue_create_empty_oid_set(ctx, minor_status, oid_set)
+    void *ctx;
     OM_uint32   *minor_status;
     gss_OID_set         *oid_set;
 {
     return(generic_gss_create_empty_oid_set(minor_status, oid_set));
 }
+#endif
 
-OM_uint32 KRB5_CALLCONV
-gss_delete_sec_context(minor_status, context_handle, output_token)
+static OM_uint32
+k5glue_delete_sec_context(ctx, minor_status, context_handle, output_token)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_ctx_id_t *context_handle;
      gss_buffer_t output_token;
@@ -152,8 +554,9 @@ gss_delete_sec_context(minor_status, context_handle, output_token)
                                      context_handle, output_token));
 }
 
-OM_uint32 KRB5_CALLCONV
-gss_display_name(minor_status, input_name, output_name_buffer, output_name_type)
+static OM_uint32
+k5glue_display_name(ctx, minor_status, input_name, output_name_buffer, output_name_type)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_name_t input_name;
      gss_buffer_t output_name_buffer;
@@ -163,9 +566,10 @@ gss_display_name(minor_status, input_name, output_name_buffer, output_name_type)
                                output_name_buffer, output_name_type));
 }
 
-OM_uint32 KRB5_CALLCONV
-gss_display_status(minor_status, status_value, status_type,
+static OM_uint32
+k5glue_display_status(ctx, minor_status, status_value, status_type,
                   mech_type, message_context, status_string)
+    void *ctx;
      OM_uint32 *minor_status;
      OM_uint32 status_value;
      int status_type;
@@ -179,8 +583,9 @@ gss_display_status(minor_status, status_value, status_type,
 }
 
 /* V2 */
-OM_uint32 KRB5_CALLCONV
-gss_export_sec_context(minor_status, context_handle, interprocess_token)
+static OM_uint32
+k5glue_export_sec_context(ctx, minor_status, context_handle, interprocess_token)
+    void *ctx;
      OM_uint32          *minor_status;
      gss_ctx_id_t       *context_handle;
      gss_buffer_t      interprocess_token;
@@ -190,10 +595,12 @@ gss_export_sec_context(minor_status, context_handle, interprocess_token)
                                      interprocess_token));
 }
 
+#if 0
 /* V2 */
-OM_uint32 KRB5_CALLCONV
-gss_get_mic(minor_status, context_handle, qop_req,
+static OM_uint32
+k5glue_get_mic(ctx, minor_status, context_handle, qop_req,
            message_buffer, message_token)
+    void *ctx;
      OM_uint32          *minor_status;
      gss_ctx_id_t      context_handle;
      gss_qop_t         qop_req;
@@ -203,27 +610,32 @@ gss_get_mic(minor_status, context_handle, qop_req,
     return(krb5_gss_get_mic(minor_status, context_handle,
                            qop_req, message_buffer, message_token));
 }
+#endif
 
-OM_uint32 KRB5_CALLCONV
-gss_import_name(minor_status, input_name_buffer, input_name_type, output_name)
+static OM_uint32
+k5glue_import_name(ctx, minor_status, input_name_buffer, input_name_type, output_name)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_buffer_t input_name_buffer;
      gss_OID input_name_type;
      gss_name_t *output_name;
 {
+#if 0
     OM_uint32 err;
     err = gssint_initialize_library();
     if (err) {
        *minor_status = err;
        return GSS_S_FAILURE;
     }
+#endif
     return(krb5_gss_import_name(minor_status, input_name_buffer,
                                input_name_type, output_name));
 }
 
 /* V2 */
-OM_uint32 KRB5_CALLCONV
-gss_import_sec_context(minor_status, interprocess_token, context_handle)
+static OM_uint32
+k5glue_import_sec_context(ctx, minor_status, interprocess_token, context_handle)
+    void *ctx;
      OM_uint32          *minor_status;
      gss_buffer_t      interprocess_token;
      gss_ctx_id_t       *context_handle;
@@ -233,19 +645,21 @@ gss_import_sec_context(minor_status, interprocess_token, context_handle)
                                      context_handle));
 }
 
-OM_uint32 KRB5_CALLCONV
-gss_indicate_mechs(minor_status, mech_set)
+static OM_uint32
+k5glue_indicate_mechs(ctx, minor_status, mech_set)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_OID_set *mech_set;
 {
    return(krb5_gss_indicate_mechs(minor_status, mech_set));
 }
 
-OM_uint32 KRB5_CALLCONV
-gss_init_sec_context(minor_status, claimant_cred_handle, context_handle,
+static OM_uint32
+k5glue_init_sec_context(ctx, minor_status, claimant_cred_handle, context_handle,
                     target_name, mech_type, req_flags, time_req,
                     input_chan_bindings, input_token, actual_mech_type,
                     output_token, ret_flags, time_rec)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_cred_id_t claimant_cred_handle;
      gss_ctx_id_t *context_handle;
@@ -268,10 +682,11 @@ gss_init_sec_context(minor_status, claimant_cred_handle, context_handle,
                                    time_rec));
 }
 
-OM_uint32 KRB5_CALLCONV
-gss_inquire_context(minor_status, context_handle, initiator_name, acceptor_name,
+static OM_uint32
+k5glue_inquire_context(ctx, minor_status, context_handle, initiator_name, acceptor_name,
                    lifetime_rec, mech_type, ret_flags,
                    locally_initiated, open)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_ctx_id_t context_handle;
      gss_name_t *initiator_name;
@@ -288,9 +703,10 @@ gss_inquire_context(minor_status, context_handle, initiator_name, acceptor_name,
                                   open));
 }
 
-OM_uint32 KRB5_CALLCONV
-gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret,
+static OM_uint32
+k5glue_inquire_cred(ctx, minor_status, cred_handle, name, lifetime_ret,
                 cred_usage, mechanisms)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_cred_id_t cred_handle;
      gss_name_t *name;
@@ -303,9 +719,10 @@ gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret,
 }
 
 /* V2 */
-OM_uint32 KRB5_CALLCONV
-gss_inquire_cred_by_mech(minor_status, cred_handle, mech_type, name,
+static OM_uint32
+k5glue_inquire_cred_by_mech(ctx, minor_status, cred_handle, mech_type, name,
                         initiator_lifetime, acceptor_lifetime, cred_usage)
+    void *ctx;
      OM_uint32          *minor_status;
      gss_cred_id_t     cred_handle;
      gss_OID           mech_type;
@@ -320,8 +737,9 @@ gss_inquire_cred_by_mech(minor_status, cred_handle, mech_type, name,
 }
 
 /* V2 */
-OM_uint32 KRB5_CALLCONV
-gss_inquire_names_for_mech(minor_status, mechanism, name_types)
+static OM_uint32
+k5glue_inquire_names_for_mech(ctx, minor_status, mechanism, name_types)
+    void *ctx;
     OM_uint32   *minor_status;
     gss_OID    mechanism;
     gss_OID_set         *name_types;
@@ -331,18 +749,22 @@ gss_inquire_names_for_mech(minor_status, mechanism, name_types)
                                           name_types));
 }
 
+#if 0
 /* V2 */
-OM_uint32 KRB5_CALLCONV
-gss_oid_to_str(minor_status, oid, oid_str)
+static OM_uint32
+k5glue_oid_to_str(ctx, minor_status, oid, oid_str)
+    void *ctx;
     OM_uint32           *minor_status;
     gss_OID            oid;
     gss_buffer_t       oid_str;
 {
     return(generic_gss_oid_to_str(minor_status, oid, oid_str));
 }
+#endif
 
-OM_uint32 KRB5_CALLCONV
-gss_process_context_token(minor_status, context_handle, token_buffer)
+static OM_uint32
+k5glue_process_context_token(ctx, minor_status, context_handle, token_buffer)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_ctx_id_t context_handle;
      gss_buffer_t token_buffer;
@@ -351,52 +773,62 @@ gss_process_context_token(minor_status, context_handle, token_buffer)
                                         context_handle, token_buffer));
 }
 
-OM_uint32 KRB5_CALLCONV
-gss_release_cred(minor_status, cred_handle)
+static OM_uint32
+k5glue_release_cred(ctx, minor_status, cred_handle)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_cred_id_t *cred_handle;
 {
    return(krb5_gss_release_cred(minor_status, cred_handle));
 }
 
-OM_uint32 KRB5_CALLCONV
-gss_release_name(minor_status, input_name)
+static OM_uint32
+k5glue_release_name(ctx, minor_status, input_name)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_name_t *input_name;
 {
    return(krb5_gss_release_name(minor_status, input_name));
 }
 
-OM_uint32 KRB5_CALLCONV
-gss_release_buffer(minor_status, buffer)
+#if 0
+static OM_uint32
+k5glue_release_buffer(ctx, minor_status, buffer)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_buffer_t buffer;
 {
    return(generic_gss_release_buffer(minor_status,
                                     buffer));
 }
+#endif
 
 /* V2 */
-OM_uint32 KRB5_CALLCONV
-gss_release_oid(minor_status, oid)
+static OM_uint32
+k5glue_internal_release_oid(ctx, minor_status, oid)
+    void *ctx;
      OM_uint32  *minor_status;
      gss_OID    *oid;
 {
-    return(krb5_gss_release_oid(minor_status, oid));
+    return(krb5_gss_internal_release_oid(minor_status, oid));
 }
 
-OM_uint32 KRB5_CALLCONV
-gss_release_oid_set(minor_status, set)
+#if 0
+static OM_uint32
+k5glue_release_oid_set(ctx, minor_status, set)
+    void *ctx;
      OM_uint32 * minor_status;
      gss_OID_set *set;
 {
    return(generic_gss_release_oid_set(minor_status, set));
 }
+#endif
 
 /* V1 only */
-OM_uint32 KRB5_CALLCONV
-gss_seal(minor_status, context_handle, conf_req_flag, qop_req,
+static OM_uint32
+k5glue_seal(ctx, minor_status, context_handle, conf_req_flag, qop_req,
         input_message_buffer, conf_state, output_message_buffer)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_ctx_id_t context_handle;
      int conf_req_flag;
@@ -410,10 +842,11 @@ gss_seal(minor_status, context_handle, conf_req_flag, qop_req,
                        conf_state, output_message_buffer));
 }
 
-OM_uint32 KRB5_CALLCONV
-gss_sign(minor_status, context_handle,
+static OM_uint32
+k5glue_sign(ctx, minor_status, context_handle,
              qop_req, message_buffer, 
              message_token)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_ctx_id_t context_handle;
      int qop_req;
@@ -424,10 +857,12 @@ gss_sign(minor_status, context_handle,
                        qop_req, message_buffer, message_token));
 }
 
+#if 0
 /* V2 */
-OM_uint32 KRB5_CALLCONV
-gss_verify_mic(minor_status, context_handle,
+static OM_uint32
+k5glue_verify_mic(ctx, minor_status, context_handle,
               message_buffer, token_buffer, qop_state)
+    void *ctx;
      OM_uint32          *minor_status;
      gss_ctx_id_t      context_handle;
      gss_buffer_t      message_buffer;
@@ -439,9 +874,10 @@ gss_verify_mic(minor_status, context_handle,
 }
 
 /* V2 */
-OM_uint32 KRB5_CALLCONV
-gss_wrap(minor_status, context_handle, conf_req_flag, qop_req,
+static OM_uint32
+k5glue_wrap(ctx, minor_status, context_handle, conf_req_flag, qop_req,
         input_message_buffer, conf_state, output_message_buffer)
+    void *ctx;
     OM_uint32           *minor_status;
     gss_ctx_id_t       context_handle;
     int                        conf_req_flag;
@@ -456,8 +892,9 @@ gss_wrap(minor_status, context_handle, conf_req_flag, qop_req,
 }
 
 /* V2 */
-OM_uint32 KRB5_CALLCONV
-gss_str_to_oid(minor_status, oid_str, oid)
+static OM_uint32
+k5glue_str_to_oid(ctx, minor_status, oid_str, oid)
+    void *ctx;
     OM_uint32           *minor_status;
     gss_buffer_t       oid_str;
     gss_OID             *oid;
@@ -466,8 +903,9 @@ gss_str_to_oid(minor_status, oid_str, oid)
 }
 
 /* V2 */
-OM_uint32 KRB5_CALLCONV
-gss_test_oid_set_member(minor_status, member, set, present)
+static OM_uint32
+k5glue_test_oid_set_member(ctx, minor_status, member, set, present)
+    void *ctx;
     OM_uint32   *minor_status;
     gss_OID    member;
     gss_OID_set        set;
@@ -476,11 +914,13 @@ gss_test_oid_set_member(minor_status, member, set, present)
     return(generic_gss_test_oid_set_member(minor_status, member, set,
                                           present));
 }
+#endif
 
 /* V1 only */
-OM_uint32 KRB5_CALLCONV
-gss_unseal(minor_status, context_handle, input_message_buffer,
+static OM_uint32
+k5glue_unseal(ctx, minor_status, context_handle, input_message_buffer,
           output_message_buffer, conf_state, qop_state)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_ctx_id_t context_handle;
      gss_buffer_t input_message_buffer;
@@ -493,10 +933,12 @@ gss_unseal(minor_status, context_handle, input_message_buffer,
                          conf_state, qop_state));
 }
 
+#if 0
 /* V2 */
-OM_uint32 KRB5_CALLCONV
-gss_unwrap(minor_status, context_handle, input_message_buffer, 
+static OM_uint32
+k5glue_unwrap(ctx, minor_status, context_handle, input_message_buffer, 
           output_message_buffer, conf_state, qop_state)
+    void *ctx;
     OM_uint32           *minor_status;
     gss_ctx_id_t       context_handle;
     gss_buffer_t       input_message_buffer;
@@ -507,11 +949,13 @@ gss_unwrap(minor_status, context_handle, input_message_buffer,
     return(krb5_gss_unwrap(minor_status, context_handle, input_message_buffer,
                           output_message_buffer, conf_state, qop_state));
 }
+#endif
 
 /* V1 only */
-OM_uint32 KRB5_CALLCONV
-gss_verify(minor_status, context_handle, message_buffer,
+static OM_uint32
+k5glue_verify(ctx, minor_status, context_handle, message_buffer,
           token_buffer, qop_state)
+    void *ctx;
      OM_uint32 *minor_status;
      gss_ctx_id_t context_handle;
      gss_buffer_t message_buffer;
@@ -526,9 +970,10 @@ gss_verify(minor_status, context_handle, message_buffer,
 }
 
 /* V2 interface */
-OM_uint32 KRB5_CALLCONV
-gss_wrap_size_limit(minor_status, context_handle, conf_req_flag,
+static OM_uint32
+k5glue_wrap_size_limit(ctx, minor_status, context_handle, conf_req_flag,
                    qop_req, req_output_size, max_input_size)
+    void *ctx;
     OM_uint32           *minor_status;
     gss_ctx_id_t       context_handle;
     int                        conf_req_flag;
@@ -541,9 +986,11 @@ gss_wrap_size_limit(minor_status, context_handle, conf_req_flag,
                                   req_output_size, max_input_size));
 }
 
+#if 0
 /* V2 interface */
-OM_uint32 KRB5_CALLCONV
-gss_canonicalize_name(minor_status, input_name, mech_type, output_name)
+static OM_uint32
+k5glue_canonicalize_name(ctx, minor_status, input_name, mech_type, output_name)
+    void *ctx;
        OM_uint32  *minor_status;
        const gss_name_t input_name;
        const gss_OID mech_type;
@@ -552,11 +999,12 @@ gss_canonicalize_name(minor_status, input_name, mech_type, output_name)
        return krb5_gss_canonicalize_name(minor_status, input_name,
                                          mech_type, output_name);
 }
-
+#endif
 
 /* V2 interface */
-OM_uint32 KRB5_CALLCONV
-gss_export_name(minor_status, input_name, exported_name)
+static OM_uint32
+k5glue_export_name(ctx, minor_status, input_name, exported_name)
+    void *ctx;
        OM_uint32  *minor_status;
        const gss_name_t input_name;
        gss_buffer_t exported_name;
@@ -564,15 +1012,96 @@ gss_export_name(minor_status, input_name, exported_name)
        return krb5_gss_export_name(minor_status, input_name, exported_name);
 }
 
+#if 0
 /* V2 interface */
-OM_uint32 KRB5_CALLCONV
-gss_duplicate_name(minor_status, input_name, dest_name)
+static OM_uint32
+k5glue_duplicate_name(ctx, minor_status, input_name, dest_name)
+    void *ctx;
        OM_uint32  *minor_status;
        const gss_name_t input_name;
        gss_name_t *dest_name;
 {
        return krb5_gss_duplicate_name(minor_status, input_name, dest_name);
 }
+#endif
+
+OM_uint32 KRB5_CALLCONV
+gss_krb5_get_tkt_flags(
+    OM_uint32 *minor_status,
+    gss_ctx_id_t context_handle,
+    krb5_flags *ticket_flags)
+{
+    gss_union_ctx_id_t uctx;
+
+    uctx = (gss_union_ctx_id_t)context_handle;
+    if (!g_OID_equal(uctx->mech_type, &krb5_mechanism.mech_type) &&
+       !g_OID_equal(uctx->mech_type, &krb5_mechanism_old.mech_type))
+       return GSS_S_BAD_MECH;
+    return gss_krb5int_get_tkt_flags(minor_status, uctx->internal_ctx_id,
+                                    ticket_flags);
+}
+
+OM_uint32 KRB5_CALLCONV 
+gss_krb5_copy_ccache(
+    OM_uint32 *minor_status,
+    gss_cred_id_t cred_handle,
+    krb5_ccache out_ccache)
+{
+    gss_union_cred_t ucred;
+    gss_cred_id_t mcred;
+
+    ucred = (gss_union_cred_t)cred_handle;
+
+    mcred = gssint_get_mechanism_cred(ucred, &krb5_mechanism.mech_type);
+    if (mcred != GSS_C_NO_CREDENTIAL)
+       return gss_krb5int_copy_ccache(minor_status, mcred, out_ccache);
+
+    mcred = gssint_get_mechanism_cred(ucred, &krb5_mechanism_old.mech_type);
+    if (mcred != GSS_C_NO_CREDENTIAL)
+       return gss_krb5int_copy_ccache(minor_status, mcred, out_ccache);
+
+    return GSS_S_DEFECTIVE_CREDENTIAL;
+}
+
+/* XXX need to delete mechglue ctx too */
+OM_uint32 KRB5_CALLCONV
+gss_krb5_export_lucid_sec_context(
+    OM_uint32 *minor_status,
+    gss_ctx_id_t *context_handle,
+    OM_uint32 version,
+    void **kctx)
+{
+    gss_union_ctx_id_t uctx;
+
+    uctx = (gss_union_ctx_id_t)*context_handle;
+    if (!g_OID_equal(uctx->mech_type, &krb5_mechanism.mech_type) &&
+       !g_OID_equal(uctx->mech_type, &krb5_mechanism_old.mech_type))
+       return GSS_S_BAD_MECH;
+    return gss_krb5int_export_lucid_sec_context(minor_status,
+                                               &uctx->internal_ctx_id,
+                                               version, kctx);
+}
 
+OM_uint32 KRB5_CALLCONV
+gss_krb5_set_allowable_enctypes(
+    OM_uint32 *minor_status, 
+    gss_cred_id_t cred,
+    OM_uint32 num_ktypes,
+    krb5_enctype *ktypes)
+{
+    gss_union_cred_t ucred;
+    gss_cred_id_t mcred;
+
+    ucred = (gss_union_cred_t)cred;
+    mcred = gssint_get_mechanism_cred(ucred, &krb5_mechanism.mech_type);
+    if (mcred != GSS_C_NO_CREDENTIAL)
+       return gss_krb5int_set_allowable_enctypes(minor_status, mcred,
+                                                 num_ktypes, ktypes);
 
+    mcred = gssint_get_mechanism_cred(ucred, &krb5_mechanism_old.mech_type);
+    if (mcred != GSS_C_NO_CREDENTIAL)
+       return gss_krb5int_set_allowable_enctypes(minor_status, mcred,
+                                                 num_ktypes, ktypes);
 
+    return GSS_S_DEFECTIVE_CREDENTIAL;
+}
index ac81fff60391fdb7fd1ce55d6c05e2a6707c1d4c..a1679a93d5600f42ae8935b83dc6d46607ec29ac 100644 (file)
@@ -60,7 +60,7 @@ make_external_lucid_ctx_v1(
  */
 
 OM_uint32 KRB5_CALLCONV
-gss_krb5_export_lucid_sec_context(
+gss_krb5int_export_lucid_sec_context(
     OM_uint32          *minor_status,
     gss_ctx_id_t       *context_handle,
     OM_uint32          version,
index b7db6f061b69bfcb734501a25a184cb9d1935622..e48656f9f3dceac2c5ffc07fc8aa055f5da85646 100644 (file)
@@ -70,6 +70,10 @@ krb5_gss_release_cred(minor_status, cred_handle)
       code3 = 0;
    if (cred->princ)
       krb5_free_principal(context, cred->princ);
+
+   if (cred->req_enctypes)
+       free(cred->req_enctypes);
+
    xfree(cred);
    krb5_free_context(context);
 
index 01921c02f7af7f8b9917b544e5cc32d5dd8a8de2..21408b5d67e84124a7975e6b8f76022486f61575 100644 (file)
@@ -30,8 +30,8 @@
  */
 #include "gssapiP_krb5.h"
 
-static OM_uint32 krb5_gss_internal_release_oid (OM_uint32 *, /* minor_status */
-                                               gss_OID * /* oid */
+OM_uint32 krb5_gss_internal_release_oid (OM_uint32 *, /* minor_status */
+                                        gss_OID * /* oid */
     );
 
 OM_uint32
@@ -59,7 +59,7 @@ krb5_gss_release_oid(minor_status, oid)
     }
 }
 
-static OM_uint32
+OM_uint32
 krb5_gss_internal_release_oid(minor_status, oid)
     OM_uint32  *minor_status;
     gss_OID    *oid;
@@ -71,6 +71,7 @@ krb5_gss_internal_release_oid(minor_status, oid)
    
     if ((*oid != gss_mech_krb5) &&
        (*oid != gss_mech_krb5_old) &&
+       (*oid != gss_mech_krb5_wrong) &&
        (*oid != gss_nt_krb5_name) &&
        (*oid != gss_nt_krb5_principal)) {
        /* We don't know about this OID */
index 88cae714a3a883a918c353945b170cbd62b29559..f573d7dfcc54c1353b15e3a34163a6b8a7cc63b4 100644 (file)
 #include "gssapi_krb5.h"
 
 OM_uint32 KRB5_CALLCONV
-gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status, 
-                                gss_cred_id_t cred_handle,
-                               OM_uint32 num_ktypes,
-                               krb5_enctype *ktypes)
+gss_krb5int_set_allowable_enctypes(OM_uint32 *minor_status, 
+                                  gss_cred_id_t cred_handle,
+                                  OM_uint32 num_ktypes,
+                                  krb5_enctype *ktypes)
 {
     int i;
     krb5_enctype * new_ktypes;
@@ -115,8 +115,10 @@ gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status,
        goto error_out;
     }
     kerr = k5_mutex_lock(&cred->lock);
-    if (kerr)
+    if (kerr) {
+       free(new_ktypes);
        goto error_out;
+    }
     if (cred->req_enctypes)
        free(cred->req_enctypes);
     cred->req_enctypes = new_ktypes;
index f67967137c58ac10fab0b198a1218ee85f277623..eb7dce968d17aee5404d45efa5506b68a09ab4d8 100644 (file)
@@ -28,6 +28,7 @@ gss_init_sec_context
 gss_inquire_context
 gss_inquire_cred
 gss_inquire_cred_by_mech
+gss_inquire_mechs_for_name
 gss_inquire_names_for_mech
 gss_krb5_ccache_name
 gss_krb5_copy_ccache
index afb6ef35f246d03f51f37a2ee1393c196a3b91cf..7d1c6136ce37c4acc5e513b86e18ccde6cf2cb4e 100644 (file)
@@ -1,8 +1,8 @@
-thisconfigdir=..
+thisconfigdir=../../..
 myfulldir=lib/gssapi/mechglue
-mydir=.
+mydir=lib/gssapi/mechglue
 BUILDTOP=$(REL)..$(S)..$(S)..
-LOCALINCLUDES = -I. -I$(srcdir)
+LOCALINCLUDES = -I. -I$(srcdir) -I$(srcdir)/.. -I../generic -I$(srcdir)/../generic
 DEFS=
 
 ##DOSBUILDTOP = ..\..\..
@@ -10,111 +10,86 @@ DEFS=
 
 ##DOS##DLL_EXP_TYPE=GSS
 
-LIBDONE=DONE
-LIB_SUBDIRS=.
-DEPLIBS=
-SHLIB_LDFLAGS= $(LDFLAGS) @SHLIB_RPATH_DIRS@ \
-       $(LD_UNRESOLVED_PREFIX)krb5_gss_initialize
+SRCS = \
+       $(srcdir)/g_accept_sec_context.c \
+       $(srcdir)/g_acquire_cred.c \
+       $(srcdir)/g_canon_name.c \
+       $(srcdir)/g_compare_name.c \
+       $(srcdir)/g_context_time.c \
+       $(srcdir)/g_delete_sec_context.c \
+       $(srcdir)/g_dsp_name.c \
+       $(srcdir)/g_dsp_status.c \
+       $(srcdir)/g_dup_name.c \
+       $(srcdir)/g_exp_sec_context.c \
+       $(srcdir)/g_export_name.c \
+       $(srcdir)/g_glue.c \
+       $(srcdir)/g_imp_name.c \
+       $(srcdir)/g_imp_sec_context.c \
+       $(srcdir)/g_init_sec_context.c \
+       $(srcdir)/g_initialize.c \
+       $(srcdir)/g_inq_context.c \
+       $(srcdir)/g_inq_cred.c \
+       $(srcdir)/g_inq_names.c \
+       $(srcdir)/g_mechname.c \
+       $(srcdir)/g_oid_ops.c \
+       $(srcdir)/g_process_context.c \
+       $(srcdir)/g_rel_buffer.c \
+       $(srcdir)/g_rel_cred.c \
+       $(srcdir)/g_rel_name.c \
+       $(srcdir)/g_rel_oid_set.c \
+       $(srcdir)/g_seal.c \
+       $(srcdir)/g_sign.c \
+       $(srcdir)/g_store_cred.c \
+       $(srcdir)/g_unseal.c \
+       $(srcdir)/g_userok.c \
+       $(srcdir)/g_utils.c \
+       $(srcdir)/g_verify.c \
+       $(srcdir)/gssd_pname_to_uid.c \
+       $(srcdir)/oid_ops.c
 
-
-SHLIB_LIBDIRS= @SHLIB_LIBDIRS@
-
-SRCS   = $(srcdir)/g_acquire_cred.c \
-         $(srcdir)/g_rel_cred.c \
-         $(srcdir)/g_init_sec_context.c \
-         $(srcdir)/g_accept_sec_context.c \
-         $(srcdir)/g_process_context.c \
-         $(srcdir)/g_delete_sec_context.c \
-         $(srcdir)/g_imp_sec_context.c \
-         $(srcdir)/g_exp_sec_context.c \
-         $(srcdir)/g_context_time.c \
-         $(srcdir)/g_sign.c \
-         $(srcdir)/g_verify.c \
-         $(srcdir)/g_seal.c \
-         $(srcdir)/g_unseal.c \
-         $(srcdir)/g_dsp_status.c \
-         $(srcdir)/g_indicate_mechs.c \
-         $(srcdir)/g_compare_name.c \
-         $(srcdir)/g_dsp_name.c \
-         $(srcdir)/g_imp_name.c \
-         $(srcdir)/g_rel_name.c \
-         $(srcdir)/g_rel_buffer.c \
-         $(srcdir)/g_rel_oid_set.c \
-         $(srcdir)/g_oid_ops.c \
-         $(srcdir)/g_inq_cred.c \
-         $(srcdir)/g_inq_context.c \
-         $(srcdir)/g_inq_names.c \
-         $(srcdir)/g_initialize.c \
-         $(srcdir)/g_glue.c \
-         $(srcdir)/gssd_pname_to_uid.c \
-         $(srcdir)/gen_oids.c \
-         $(srcdir)/oid_ops.c \
-         $(srcdir)/g_mechname.c 
-
-OBJS   = $(OUTPRE)g_acquire_cred.$(OBJEXT) \
-         $(OUTPRE)g_rel_cred.$(OBJEXT) \
-         $(OUTPRE)g_init_sec_context.$(OBJEXT) \
-         $(OUTPRE)g_accept_sec_context.$(OBJEXT) \
-         $(OUTPRE)g_process_context.$(OBJEXT) \
-         $(OUTPRE)g_delete_sec_context.$(OBJEXT) \
-         $(OUTPRE)g_imp_sec_context.$(OBJEXT) \
-         $(OUTPRE)g_exp_sec_context.$(OBJEXT) \
-         $(OUTPRE)g_context_time.$(OBJEXT) \
-         $(OUTPRE)g_sign.$(OBJEXT) \
-         $(OUTPRE)g_verify.$(OBJEXT) \
-         $(OUTPRE)g_seal.$(OBJEXT) \
-         $(OUTPRE)g_unseal.$(OBJEXT) \
-         $(OUTPRE)g_dsp_status.$(OBJEXT) \
-         $(OUTPRE)g_indicate_mechs.$(OBJEXT) \
-         $(OUTPRE)g_compare_name.$(OBJEXT) \
-         $(OUTPRE)g_dsp_name.$(OBJEXT) \
-         $(OUTPRE)g_imp_name.$(OBJEXT) \
-         $(OUTPRE)g_rel_name.$(OBJEXT) \
-         $(OUTPRE)g_rel_buffer.$(OBJEXT) \
-         $(OUTPRE)g_rel_oid_set.$(OBJEXT) \
-         $(OUTPRE)g_oid_ops.$(OBJEXT) \
-         $(OUTPRE)g_inq_cred.$(OBJEXT) \
-         $(OUTPRE)g_inq_context.$(OBJEXT) \
-         $(OUTPRE)g_inq_names.$(OBJEXT) \
-         $(OUTPRE)g_initialize.$(OBJEXT) \
-         $(OUTPRE)g_glue.$(OBJEXT) \
-         $(OUTPRE)gssd_pname_to_uid.$(OBJEXT) \
-         $(OUTPRE)gen_oids.$(OBJEXT) \
-         $(OUTPRE)oid_ops.$(OBJEXT) \
-         $(OUTPRE)g_mechname.$(OBJEXT)
+STLIBOBJS = \
+       g_accept_sec_context.o \
+       g_acquire_cred.o \
+       g_canon_name.o \
+       g_compare_name.o \
+       g_context_time.o \
+       g_delete_sec_context.o \
+       g_dsp_name.o \
+       g_dsp_status.o \
+       g_dup_name.o \
+       g_exp_sec_context.o \
+       g_export_name.o \
+       g_glue.o \
+       g_imp_name.o \
+       g_imp_sec_context.o \
+       g_init_sec_context.o \
+       g_initialize.o \
+       g_inq_context.o \
+       g_inq_cred.o \
+       g_inq_names.o \
+       g_mechname.o \
+       g_oid_ops.o \
+       g_process_context.o \
+       g_rel_buffer.o \
+       g_rel_cred.o \
+       g_rel_name.o \
+       g_rel_oid_set.o \
+       g_seal.o \
+       g_sign.o \
+       g_store_cred.o \
+       g_unseal.o \
+       g_userok.o \
+       g_utils.o \
+       g_verify.o \
+       gssd_pname_to_uid.o \
+       oid_ops.o
 
 EHDRDIR= $(BUILDTOP)$(S)include$(S)gssapi
 EXPORTED_HEADERS = mechglue.h
 
-all:: all-$(WHAT) 
-
-all-unix:: shared includes $(OBJS)
-
-all-windows:: includes $(OBJS)
-       if not exist $(EHDRDIR)\nul mkdir $(EHDRDIR)
-       copy mechglue.h $(EHDRDIR)
-
-shared:
-       mkdir shared
+all-unix:: all-libobjs
 
-libgssapi.$(STEXT): $(OBJS)
-       $(RM) $@
-       $(ARADD) $@ $(OBJS)
-       $(RANLIB) $@
-
-
-#libgssapi.$(LIBEXT): $(OBJS)
-#      $(ARCHIVE) $@ $(OBJS)
-#      $(RANLIB) $@
-
-clean:: clean-$(WHAT)
-
-clean-unix::
-       $(RM) shared/*
-
-clean-windows::
-       $(RM) $(EHDRDIR)\gssapi.h $(EHDRDIR)\gssapi_generic.h
-       if exist $(EHDRDIR)\nul rmdir $(EHDRDIR)
+clean-unix:: clean-libobjs
 
 # Krb5InstallHeaders($(EXPORTED_HEADERS), $(KRB5_INCDIR)/krb5)
 install::
@@ -124,3 +99,216 @@ install::
        done
 
 includes::
+
+# @libobj_frag@
+# +++ Dependency line eater +++
+# 
+# Makefile dependencies follow.  This must be the last section in
+# the Makefile.in file
+#
+g_accept_sec_context.so g_accept_sec_context.po $(OUTPRE)g_accept_sec_context.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_accept_sec_context.c \
+  mechglue.h mglueP.h
+g_acquire_cred.so g_acquire_cred.po $(OUTPRE)g_acquire_cred.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_acquire_cred.c mechglue.h \
+  mglueP.h
+g_canon_name.so g_canon_name.po $(OUTPRE)g_canon_name.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_canon_name.c mechglue.h \
+  mglueP.h
+g_compare_name.so g_compare_name.po $(OUTPRE)g_compare_name.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_compare_name.c mechglue.h \
+  mglueP.h
+g_context_time.so g_context_time.po $(OUTPRE)g_context_time.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_context_time.c mechglue.h \
+  mglueP.h
+g_delete_sec_context.so g_delete_sec_context.po $(OUTPRE)g_delete_sec_context.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_delete_sec_context.c \
+  mechglue.h mglueP.h
+g_dsp_name.so g_dsp_name.po $(OUTPRE)g_dsp_name.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_dsp_name.c mechglue.h \
+  mglueP.h
+g_dsp_status.so g_dsp_status.po $(OUTPRE)g_dsp_status.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_dsp_status.c mechglue.h \
+  mglueP.h
+g_dup_name.so g_dup_name.po $(OUTPRE)g_dup_name.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_dup_name.c mechglue.h \
+  mglueP.h
+g_exp_sec_context.so g_exp_sec_context.po $(OUTPRE)g_exp_sec_context.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_exp_sec_context.c \
+  mechglue.h mglueP.h
+g_export_name.so g_export_name.po $(OUTPRE)g_export_name.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_export_name.c mechglue.h \
+  mglueP.h
+g_glue.so g_glue.po $(OUTPRE)g_glue.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(BUILDTOP)/include/gssapi/gssapi.h $(COM_ERR_DEPS) \
+  $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_glue.c mechglue.h \
+  mglueP.h
+g_imp_name.so g_imp_name.po $(OUTPRE)g_imp_name.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_imp_name.c mechglue.h \
+  mglueP.h
+g_imp_sec_context.so g_imp_sec_context.po $(OUTPRE)g_imp_sec_context.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_imp_sec_context.c \
+  mechglue.h mglueP.h
+g_init_sec_context.so g_init_sec_context.po $(OUTPRE)g_init_sec_context.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_init_sec_context.c \
+  mechglue.h mglueP.h
+g_initialize.so g_initialize.po $(OUTPRE)g_initialize.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_initialize.c mechglue.h \
+  mglueP.h
+g_inq_context.so g_inq_context.po $(OUTPRE)g_inq_context.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_inq_context.c mechglue.h \
+  mglueP.h
+g_inq_cred.so g_inq_cred.po $(OUTPRE)g_inq_cred.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_inq_cred.c mechglue.h \
+  mglueP.h
+g_inq_names.so g_inq_names.po $(OUTPRE)g_inq_names.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_inq_names.c mechglue.h \
+  mglueP.h
+g_mechname.so g_mechname.po $(OUTPRE)g_mechname.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_mechname.c mechglue.h \
+  mglueP.h
+g_oid_ops.so g_oid_ops.po $(OUTPRE)g_oid_ops.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_oid_ops.c mechglue.h \
+  mglueP.h
+g_process_context.so g_process_context.po $(OUTPRE)g_process_context.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_process_context.c \
+  mechglue.h mglueP.h
+g_rel_buffer.so g_rel_buffer.po $(OUTPRE)g_rel_buffer.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_rel_buffer.c mechglue.h \
+  mglueP.h
+g_rel_cred.so g_rel_cred.po $(OUTPRE)g_rel_cred.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_rel_cred.c mechglue.h \
+  mglueP.h
+g_rel_name.so g_rel_name.po $(OUTPRE)g_rel_name.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_rel_name.c mechglue.h \
+  mglueP.h
+g_rel_oid_set.so g_rel_oid_set.po $(OUTPRE)g_rel_oid_set.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_rel_oid_set.c mechglue.h \
+  mglueP.h
+g_seal.so g_seal.po $(OUTPRE)g_seal.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(BUILDTOP)/include/gssapi/gssapi.h $(COM_ERR_DEPS) \
+  $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_seal.c mechglue.h \
+  mglueP.h
+g_sign.so g_sign.po $(OUTPRE)g_sign.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(BUILDTOP)/include/gssapi/gssapi.h $(COM_ERR_DEPS) \
+  $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_sign.c mechglue.h \
+  mglueP.h
+g_store_cred.so g_store_cred.po $(OUTPRE)g_store_cred.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_store_cred.c mechglue.h \
+  mglueP.h
+g_unseal.so g_unseal.po $(OUTPRE)g_unseal.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_unseal.c mechglue.h \
+  mglueP.h
+g_userok.so g_userok.po $(OUTPRE)g_userok.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_userok.c mechglue.h \
+  mglueP.h
+g_utils.so g_utils.po $(OUTPRE)g_utils.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+  g_utils.c
+g_verify.so g_verify.po $(OUTPRE)g_verify.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h g_verify.c mechglue.h \
+  mglueP.h
+gssd_pname_to_uid.so gssd_pname_to_uid.po $(OUTPRE)gssd_pname_to_uid.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h gssd_pname_to_uid.c \
+  mechglue.h mglueP.h
+oid_ops.so oid_ops.po $(OUTPRE)oid_ops.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_generic.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+  $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_generic.h \
+  ../generic/gssapi_err_generic.h mechglue.h mglueP.h \
+  oid_ops.c
index 8cc752fe7415060494a3f16404e63b3ec8e5d279..e0be150937ab73145358573bf584cb4cc9ba3fb7 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_accept_sec_context.c 1.19     95/08/07 SMI" */
+/* #pragma ident       "@(#)g_accept_sec_context.c     1.19    04/02/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -44,7 +44,7 @@ gss_accept_sec_context (minor_status,
                         output_token,
                         ret_flags,
                         time_rec,
-                        delegated_cred_handle)
+                        d_cred)
 
 OM_uint32 *            minor_status;
 gss_ctx_id_t *         context_handle;
@@ -56,23 +56,39 @@ gss_OID *           mech_type;
 gss_buffer_t           output_token;
 OM_uint32 *            ret_flags;
 OM_uint32 *            time_rec;
-gss_cred_id_t *                delegated_cred_handle;
+gss_cred_id_t *                d_cred;
 
 {
     OM_uint32          status, temp_status, temp_minor_status;
     gss_union_ctx_id_t union_ctx_id;
     gss_union_cred_t   union_cred;
     gss_cred_id_t      input_cred_handle = GSS_C_NO_CREDENTIAL;
-    gss_name_t         internal_name;
+    gss_cred_id_t      tmp_d_cred = GSS_C_NO_CREDENTIAL;
+    gss_name_t         internal_name = GSS_C_NO_NAME;
+    gss_name_t         tmp_src_name = GSS_C_NO_NAME;
     gss_OID_desc       token_mech_type_desc;
     gss_OID            token_mech_type = &token_mech_type_desc;
     gss_mechanism      mech;
     
-    gss_initialize();
+    /* check parameters first */
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
+    if (context_handle == NULL || output_token == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    /* clear optional fields */
+    output_token->value = NULL;
+    output_token->length = 0;
+    if (src_name)
+       *src_name = NULL;
 
-    if (context_handle == NULL)
-       return GSS_S_NO_CONTEXT;
+    if (mech_type)
+       *mech_type = NULL;
 
+    if (d_cred)
+       *d_cred = NULL;
     /*
      * if context_handle is GSS_C_NO_CONTEXT, allocate a union context
      * descriptor to hold the mech type information as well as the
@@ -82,42 +98,33 @@ gss_cred_id_t *             delegated_cred_handle;
     
     if(*context_handle == GSS_C_NO_CONTEXT) {
        
+       if (GSS_EMPTY_BUFFER(input_token_buffer))
+           return (GSS_S_CALL_INACCESSIBLE_READ);
+
        /* Get the token mech type */
-       status = __gss_get_mech_type(token_mech_type, input_token_buffer);
+       status = gssint_get_mech_type(token_mech_type, input_token_buffer);
        if (status)
            return status;
 
        status = GSS_S_FAILURE;
        union_ctx_id = (gss_union_ctx_id_t)
            malloc(sizeof(gss_union_ctx_id_desc));
-       if (!union_ctx_id) {
-           *minor_status = ENOMEM;
-           goto error_out;
-       }
+       if (!union_ctx_id)
+           return (GSS_S_FAILURE);
 
-       union_ctx_id->mech_type = (gss_OID) malloc(sizeof(gss_OID_desc));
-       if (!union_ctx_id->mech_type) {
-           *minor_status = ENOMEM;
-           goto error_out;
+       union_ctx_id->internal_ctx_id = GSS_C_NO_CONTEXT;
+       status = generic_gss_copy_oid(&temp_minor_status,
+                                     token_mech_type,
+                                     &union_ctx_id->mech_type);
+       if (status != GSS_S_COMPLETE) {
+           free(union_ctx_id);
+           return (status);
        }
-       
-       union_ctx_id->mech_type->elements = (void *)
-           malloc(token_mech_type->length);
-       if (!union_ctx_id->mech_type->elements) {
-           *minor_status = ENOMEM;
-           goto error_out;
-       }
-
-       union_ctx_id->mech_type->length = token_mech_type->length;
-       memcpy(union_ctx_id->mech_type->elements,
-              token_mech_type->elements,
-              token_mech_type->length);
 
-       /* copy the supplied context handle */
-
-       union_ctx_id->internal_ctx_id = *context_handle;
+       /* set the new context handle to caller's data */
+       *context_handle = (gss_ctx_id_t)union_ctx_id;
     } else {
-       union_ctx_id = *context_handle;
+       union_ctx_id = (gss_union_ctx_id_t)*context_handle;
        token_mech_type = union_ctx_id->mech_type;
     }
     
@@ -127,14 +134,14 @@ gss_cred_id_t *           delegated_cred_handle;
      * use the default credential.
      */
     union_cred = (gss_union_cred_t) verifier_cred_handle;
-    input_cred_handle = __gss_get_mechanism_cred(union_cred, token_mech_type);
+    input_cred_handle = gssint_get_mechanism_cred(union_cred, token_mech_type);
     
     /*
      * now select the approprate underlying mechanism routine and
      * call it.
      */
     
-    mech = __gss_get_mechanism (token_mech_type);
+    mech = gssint_get_mechanism (token_mech_type);
     if (mech && mech->gss_accept_sec_context) {
 
            status = mech->gss_accept_sec_context(
@@ -149,7 +156,7 @@ gss_cred_id_t *             delegated_cred_handle;
                                                  output_token,
                                                  ret_flags,
                                                  time_rec,
-                                                 delegated_cred_handle);
+                                       d_cred ? &tmp_d_cred : NULL);
 
            /* If there's more work to do, keep going... */
            if (status == GSS_S_CONTINUE_NEEDED)
@@ -166,36 +173,140 @@ gss_cred_id_t *          delegated_cred_handle;
             * then call gss_import_name() to create
             * the union name struct cast to src_name
             */
-           if (src_name != NULL && status == GSS_S_COMPLETE) {
-               temp_status = __gss_convert_name_to_union_name(
-                      &temp_minor_status, mech, internal_name, src_name);
+           if (internal_name != NULL) {
+               temp_status = gssint_convert_name_to_union_name(
+                      &temp_minor_status, mech,
+                      internal_name, &tmp_src_name);
                if (temp_status != GSS_S_COMPLETE) {
-                   if (minor_status)
-                       *minor_status = temp_minor_status;
-                   gss_release_buffer(&temp_minor_status, output_token);
-                   __gss_release_internal_name(&temp_minor_status,
-                                         &mech->mech_type, &internal_name);
+                   *minor_status = temp_minor_status;
+                   if (output_token->length)
+                       (void) gss_release_buffer(&temp_minor_status,
+                                                 output_token);
+                   if (internal_name != GSS_C_NO_NAME)
+                       mech->gss_release_name(
+                           mech->context,
+                           &temp_minor_status,
+                           &internal_name);
                    return (temp_status);
                }
+               if (src_name != NULL) {
+                   *src_name = tmp_src_name;
+               }
+           } else if (src_name != NULL) {
+               *src_name = GSS_C_NO_NAME;
+           }
+
+           /* Ensure we're returning correct creds format */
+           if ((ret_flags && GSS_C_DELEG_FLAG) &&
+               tmp_d_cred != GSS_C_NO_CREDENTIAL) {
+               gss_union_cred_t d_u_cred = NULL;
+
+               d_u_cred = malloc(sizeof (gss_union_cred_desc));
+               if (d_u_cred == NULL) {
+                   status = GSS_S_FAILURE;
+                   goto error_out;
+               }
+               (void) memset(d_u_cred, 0,
+                             sizeof (gss_union_cred_desc));
+
+               d_u_cred->count = 1;
+
+               status = generic_gss_copy_oid(&temp_minor_status,
+                                             token_mech_type,
+                                             &d_u_cred->mechs_array);
+
+               if (status != GSS_S_COMPLETE) {
+                   free(d_u_cred);
+                   goto error_out;
+               }
+
+               d_u_cred->cred_array = malloc(sizeof (gss_cred_id_t));
+               if (d_u_cred->cred_array != NULL) {
+                   d_u_cred->cred_array[0] = tmp_d_cred;
+               } else {
+                   free(d_u_cred);
+                   status = GSS_S_FAILURE;
+                   goto error_out;
+               }
+
+               if (status != GSS_S_COMPLETE) {
+                   free(d_u_cred->cred_array);
+                   free(d_u_cred);
+                   goto error_out;
+               }
+
+               internal_name = GSS_C_NO_NAME;
+
+               d_u_cred->auxinfo.creation_time = time(0);
+               d_u_cred->auxinfo.time_rec = 0;
+
+               if (mech->gss_inquire_cred) {
+                   status = mech->gss_inquire_cred(mech->context,
+                                                   minor_status,
+                                                   tmp_d_cred,
+                                                   &internal_name,
+                                                   &d_u_cred->auxinfo.time_rec,
+                                                   &d_u_cred->auxinfo.cred_usage,
+                                                   NULL);
+               }
+
+               if (internal_name != NULL) {
+                   temp_status = gssint_convert_name_to_union_name(
+                       &temp_minor_status, mech,
+                       internal_name, &tmp_src_name);
+                   if (temp_status != GSS_S_COMPLETE) {
+                       *minor_status = temp_minor_status;
+                       if (output_token->length)
+                           (void) gss_release_buffer(
+                               &temp_minor_status,
+                               output_token);
+                       free(d_u_cred->cred_array);
+                       free(d_u_cred);
+                       return (temp_status);
+                   }
+               }
+
+               if (tmp_src_name != NULL) {
+                   status = gss_display_name(
+                       &temp_minor_status,
+                       tmp_src_name,
+                       &d_u_cred->auxinfo.name,
+                       &d_u_cred->auxinfo.name_type);
+               }
+
+               *d_cred = (gss_cred_id_t)d_u_cred;
            }
 
-       if(*context_handle == GSS_C_NO_CONTEXT)
-           *context_handle = (gss_ctx_id_t *) union_ctx_id;
+           if (src_name == NULL && tmp_src_name != NULL)
+               (void) gss_release_name(&temp_minor_status,
+                                       &tmp_src_name);
+           return      (status);
+    } else {
 
-       return(status);
+       status = GSS_S_BAD_MECH;
     }
     
-    return(GSS_S_BAD_MECH);
-    
 error_out:
     if (union_ctx_id) {
        if (union_ctx_id->mech_type) {
            if (union_ctx_id->mech_type->elements)
                free(union_ctx_id->mech_type->elements);
            free(union_ctx_id->mech_type);
+           *context_handle = GSS_C_NO_CONTEXT;
        }
        free(union_ctx_id);
     }
+
+    if (output_token->length)
+       (void) gss_release_buffer(&temp_minor_status, output_token);
+
+    if (src_name)
+       *src_name = GSS_C_NO_NAME;
+
+    if (tmp_src_name != GSS_C_NO_NAME)
+       (void) gss_release_buffer(&temp_minor_status,
+                                 (gss_buffer_t)tmp_src_name);
+
     return (status);
 }
 
index 8ecf55f31dbacb6944a2e42b34dc09aee898885c..ca30607911b911e5328527c5d684a6a91f89eb15 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_acquire_cred.c 1.19     95/08/07 SMI" */
+/* #pragma ident       "@(#)g_acquire_cred.c   1.22    04/02/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
 #include <errno.h>
 #include <time.h>
 
-#define g_OID_equal(o1,o2) \
-   (((o1)->length == (o2)->length) && \
-    (memcmp((o1)->elements,(o2)->elements,(int) (o1)->length) == 0))
-
 static gss_OID_set
-create_actual_mechs(creds)
-    gss_union_cred_t   creds;
+create_actual_mechs(mechs_array, count)
+    const gss_OID      mechs_array;
+    int count;
 {
     gss_OID_set        actual_mechs;
     int                        i;
+    OM_uint32          minor;
 
     actual_mechs = (gss_OID_set) malloc(sizeof(gss_OID_set_desc));
     if (!actual_mechs)
        return NULL;
 
     actual_mechs->elements = (gss_OID)
-           malloc(sizeof(gss_OID_desc) * creds->count);
+       malloc(sizeof (gss_OID_desc) * count);
     if (!actual_mechs->elements) {
        free(actual_mechs);
        return NULL;
     }
     
-    actual_mechs->count = creds->count;
+    actual_mechs->count = 0;
 
-    for (i=0; i < creds->count; i++) {
-       actual_mechs->elements[i].length = creds->mechs_array[i].length;
+    for (i = 0; i < count; i++) {
        actual_mechs->elements[i].elements = (void *)
-           malloc(creds->mechs_array[i].length);
-       memcpy(actual_mechs->elements[i].elements,
-              creds->mechs_array[i].elements, creds->mechs_array[i].length);
+           malloc(mechs_array[i].length);
+       if (actual_mechs->elements[i].elements == NULL) {
+           (void) gss_release_oid_set(&minor, &actual_mechs);
+           return (NULL);
+       }
+       g_OID_copy(&actual_mechs->elements[i], &mechs_array[i]);
+       actual_mechs->count++;
     }
 
     return actual_mechs;
@@ -91,291 +92,118 @@ gss_OID_set *             actual_mechs;
 OM_uint32 *            time_rec;
 
 {
-    OM_uint32          status, temp_minor_status, temp_time_rec = ~0;
-    unsigned int       i, j, creds_acquired = 0;
-    int                        k;
-    gss_union_name_t   union_name;
-    gss_name_t         internal_name;
-    gss_union_cred_t   creds;
-    gss_OID_set_desc   default_OID_set;
-    gss_OID_desc       default_OID;
-    gss_OID            specific_mech_type = 0;
-    gss_mechanism      mech;
-    
-    /*
-     * This struct is used to keep track of which mech_types are
-     * actually available and to store the credentials returned
-     * from them by each mechanism specific gss_acquire_cred() call.
-     * The results are used to construct the final union_cred
-     * structure returned by the glue layer gss_acquire_cred() call
-     * and the actual_mechs gss_OID_set returned.
-     */
-    
-    struct creds_returned {
-       unsigned char   available;
-       gss_cred_id_t   cred;
-    } *creds_returned;
+    OM_uint32 major = GSS_S_FAILURE;
+    OM_uint32 initTimeOut, acceptTimeOut, outTime = GSS_C_INDEFINITE;
+    gss_OID_set_desc default_OID_set;
+    gss_OID_set mechs;
+    gss_OID_desc default_OID;
+    gss_mechanism mech;
+    int i;
+    gss_union_cred_t creds;
+
+    /* start by checking parameters */
+    if (!minor_status)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
     
-    gss_initialize();
+    if (!output_cred_handle)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED);
 
-    /* Set this to NULL for now */
+    *output_cred_handle = GSS_C_NO_CREDENTIAL;
 
+    /* Set output parameters to NULL for now */
     if (actual_mechs)
        *actual_mechs = GSS_C_NULL_OID_SET;
 
-    if (minor_status)
-       *minor_status = 0;
-    
-    /* No need to continue if we don't have a place to store the creds */
-    if (output_cred_handle == NULL)
-       return GSS_S_COMPLETE;
-
-    /* get desired_name cast as a union_name type */
-    
-    union_name = (gss_union_name_t) desired_name;
+    if (time_rec)
+       *time_rec = 0;
 
-    if (union_name)
-           specific_mech_type = union_name->mech_type;
-    
     /*
      * if desired_mechs equals GSS_C_NULL_OID_SET, then pick an
-     * appropriate default.
+     * appropriate default.  We use the first mechanism in the
+     * mechansim list as the default. This set is created with
+     * statics thus needs not be freed
      */
     if(desired_mechs == GSS_C_NULL_OID_SET) {
-       /*
-        * If union_name->mech_type is NULL then we get the default
-        * mechanism; otherwise, we get the mechanism for the
-        * mechanism-specific name.
-        */
-       mech = __gss_get_mechanism(specific_mech_type);
+       mech = gssint_get_mechanism(NULL);
        if (mech == NULL)
            return (GSS_S_BAD_MECH);
-
-       desired_mechs = &default_OID_set;
-       default_OID_set.count = 1 ;
+       
+       mechs = &default_OID_set;
+       default_OID_set.count = 1;
        default_OID_set.elements = &default_OID;
        default_OID.length = mech->mech_type.length;
        default_OID.elements = mech->mech_type.elements;
-    }
-    
-    /*
-     * Now allocate the creds returned array. There is one element
-     * for each member of the desired_mechs argument. 
-     */
-    
-    creds_returned = (struct creds_returned *)
-       malloc(sizeof(struct creds_returned) * desired_mechs->count);
-    
-    /*
-     * For each requested mechanism in desired_mechs, determine if it
-     * is supported. If so, mark the corresponding element in
-     * creds_returned->available as 1 and call the mechanism
-     * specific gss_acquire_cred(), placing the returned cred in
-     * creds_returned->cred. If not, mark creds_returned->available as
-     * 0.
-     */
-    status = GSS_S_BAD_MECH;
-    for (j=0; j < desired_mechs->count; j++) {
-       creds_returned[j].available = 0;
-
-       mech = __gss_get_mechanism (&desired_mechs->elements[j]);
-       if (!mech || !mech->gss_acquire_cred)
-           continue;
-       /*
-        * If this is a mechanism-specific name, then only use the
-        * mechanism of the name.
-        */
-       if (specific_mech_type && !g_OID_equal(specific_mech_type,
-                                              &mech->mech_type))
-           continue;
-       /*
-        * If this is not a mechanism-specific name, then we need to
-        * do an import the external name in union_name first.
-        */
-       if (union_name == 0)
-           internal_name = (gss_name_t) 0;
-       else if (!union_name->mech_type) {
-           if (__gss_import_internal_name(&temp_minor_status,
-                                          &mech->mech_type,
-                                          union_name, &internal_name)) {
-               continue;
+    } else
+       mechs = desired_mechs;
+
+    if (mechs->count == 0)
+       return (GSS_S_BAD_MECH);
+
+    /* allocate the output credential structure */
+    creds = (gss_union_cred_t)malloc(sizeof (gss_union_cred_desc));
+    if (creds == NULL)
+       return (GSS_S_FAILURE);
+
+    /* initialize to 0s */
+    (void) memset(creds, 0, sizeof (gss_union_cred_desc));
+
+    /* for each requested mech attempt to obtain a credential */
+    for (i = 0; i < mechs->count; i++) {
+       major = gss_add_cred(minor_status, (gss_cred_id_t)creds,
+                            desired_name,
+                            &mechs->elements[i],
+                            cred_usage, time_req, time_req, NULL,
+                            NULL, &initTimeOut, &acceptTimeOut);
+       if (major == GSS_S_COMPLETE) {
+           /* update the credential's time */
+           if (cred_usage == GSS_C_ACCEPT) {
+               if (outTime > acceptTimeOut)
+                   outTime = acceptTimeOut;
+           } else if (cred_usage == GSS_C_INITIATE) {
+               if (outTime > initTimeOut)
+                   outTime = initTimeOut;
+           } else {
+               /*
+                * time_rec is the lesser of the
+                * init/accept times
+                */
+               if (initTimeOut > acceptTimeOut)
+                   outTime = (outTime > acceptTimeOut) ?
+                       acceptTimeOut : outTime;
+               else
+                   outTime = (outTime > initTimeOut) ?
+                       initTimeOut : outTime;
            }
-       } else
-           internal_name = union_name->mech_name;
-
-       status = mech->gss_acquire_cred(mech->context, minor_status,
-                                       internal_name, time_req,
-                                       desired_mechs, cred_usage,
-                                       &creds_returned[j].cred,
-                                       NULL, &temp_time_rec);
-
-       /* Release the internal name, if allocated above */
-       if (union_name && !union_name->mech_type) {
-           (void) __gss_release_internal_name(&temp_minor_status,
-                                              &mech->mech_type,
-                                              &internal_name);
        }
+    } /* for */
 
-       if (status != GSS_S_COMPLETE)
-           continue;
-
-       /* 
-        * Add this into the creds_returned structure, if we got
-        * a good credential for this mechanism.
-        */
-       if (time_rec) {
-           *time_rec = *time_rec > temp_time_rec ? temp_time_rec : *time_rec;
-           temp_time_rec = *time_rec;
-       }
-
-       creds_returned[j].available = 1;
-       creds_acquired++;
-
-       /*
-        * If union_name is set, then we're done.  Continue, and
-        * declare success.  Otherwise, if do an inquire credentials
-        * from the first mechanism that succeeds and use that as the
-        * union name.
-        */
-       if (union_name)
-           continue;
-
-       status = mech->gss_inquire_cred(mech->context, &temp_minor_status,
-                                       creds_returned[j].cred,
-                                       &internal_name, 0, 0, 0);
-       if (status) {
-           /* Should never happen */
-           creds_returned[j].available = 0;
-           creds_acquired--;
-           if (mech->gss_release_cred)
-               mech->gss_release_cred(mech->context, minor_status,
-                                      &creds_returned[j].cred);
-           continue;
-       }
-
-       status = __gss_convert_name_to_union_name(&temp_minor_status, mech,
-                                                 internal_name,
-                                                 (gss_name_t *) &union_name);
+    /* ensure that we have at least one credential element */
+    if (creds->count < 1) {
+       free(creds);
+       return (major);
     }
-    
-    /*
-     * Now allocate the creds struct, which will be cast as a gss_cred_id_t
-     * and returned in the output_cred_handle argument. If there were
-     * no credentials found, return an error. Also, allocate the
-     * actual_mechs data.
-     */
-    if (creds_acquired == 0) {
-       free (creds_returned);
-       return (status);
-    }
-    
-    creds = (gss_union_cred_t) malloc(sizeof(gss_union_cred_desc));
-    
-    creds->count = creds_acquired;
-    
-    creds->mechs_array = (gss_OID)
-       malloc(sizeof(gss_OID_desc) * creds_acquired);
-    
-    creds->cred_array = (gss_cred_id_t *)
-       malloc(sizeof(gss_cred_id_t) * creds_acquired);
-    
-    if(actual_mechs != NULL) {
-       *actual_mechs = (gss_OID_set) malloc(sizeof(gss_OID_set_desc));
-
-       (*actual_mechs)->count = creds_acquired;
 
-       (*actual_mechs)->elements = (gss_OID)
-           malloc(sizeof(gss_OID_desc) * creds_acquired);
-    }
-    
-    /*
-     * copy the mechanisms found and their allocated credentials into the
-     * creds structure. At the same time, build up the actual_mechs
-     * data.
-     */
-    
-    j = 0;
-    
-    for (i=0; i<desired_mechs->count; i++) {
-       if(creds_returned[i].available) {
-
-           creds->mechs_array[j].length =
-               desired_mechs->elements[i].length;
-           creds->mechs_array[j].elements = (void *)
-               malloc(desired_mechs->elements[i].length);
-           memcpy(creds->mechs_array[j].elements,
-                  desired_mechs->elements[i].elements,
-                  desired_mechs->elements[i].length);
-           creds->cred_array[j] = creds_returned[i].cred;
-           if (actual_mechs) {
-                   (*actual_mechs)->elements[j].length =
-                       desired_mechs->elements[i].length;
-                   (*actual_mechs)->elements[j].elements = (void *)
-                       malloc(desired_mechs->elements[i].length);
-                   memcpy((*actual_mechs)->elements[j].elements,
-                          desired_mechs->elements[i].elements,
-                          desired_mechs->elements[i].length);
-           }
-           j++;
-       }
-    }
-    
-    /* free the creds_returned struct, since we are done with it. */
-    
-    free(creds_returned);
-    
-    /* record the information needed for gss_inquire_cred() */
-    
-    creds->auxinfo.creation_time = time(0);
-    creds->auxinfo.time_rec = temp_time_rec;
-    creds->auxinfo.cred_usage =  cred_usage;
-    
     /*
-     * we can't just record the internal name, desired_name, since
-     * it may be destroyed between now and the time gss_inquire_cred()
-     * is called.  So we must record the printable name in a
-     * gss_buffer_t, calling gss_display_name() to fill it in. When
-     * gss_inquire_name() is called, we must then call gss_import_name()
-     * to get the internal name that is required at that point.
+     * fill in output parameters
+     * setup the actual mechs output parameter
      */
-    if (desired_name) {
-       status = gss_display_name(&temp_minor_status, desired_name,
-                                 &creds->auxinfo.name,
-                                 &creds->auxinfo.name_type);
-       if (status) {
-           status = GSS_S_BAD_NAME;
-           goto error_out;
+    if (actual_mechs != NULL) {
+       if ((*actual_mechs = create_actual_mechs(creds->mechs_array,
+                                                creds->count)) == NULL) {
+           (void) gss_release_cred(minor_status,
+                                   (gss_cred_id_t *)&creds);
+           *minor_status = 0;
+           return (GSS_S_FAILURE);
        }
-    } else {
-       status = gss_display_name(&temp_minor_status, union_name,
-                                 &creds->auxinfo.name,
-                                 &creds->auxinfo.name_type);
-       if (status) {
-           status = GSS_S_BAD_NAME;
-           goto error_out;
-       }
-    }
-    
-    *output_cred_handle = (gss_cred_id_t) creds;
-    return(GSS_S_COMPLETE);
-
-error_out:
-    for (k=0; k < creds->count; k++) {
-       free(creds->mechs_array[k].elements);
-       if (actual_mechs)
-           free((*actual_mechs)->elements[k].elements);
     }
-       
-    if (actual_mechs) {
-       free((*actual_mechs)->elements);
-       free(*actual_mechs);
-       *actual_mechs = GSS_C_NULL_OID_SET;
-    }
-    free(creds->cred_array);
-    free(creds->mechs_array);
-    free(creds);
-       
-    return(status);
+
+    if (time_rec)
+       *time_rec = outTime;
+
+
+    *output_cred_handle = (gss_cred_id_t)creds;
+    return (GSS_S_COMPLETE);
 }
 
 /* V2 KRB5_CALLCONV */
@@ -401,36 +229,73 @@ gss_add_cred(minor_status, input_cred_handle,
     OM_uint32          time_req, time_rec;
     gss_union_name_t   union_name;
     gss_union_cred_t   new_union_cred, union_cred;
-    gss_name_t         internal_name;
+    gss_name_t         internal_name = GSS_C_NO_NAME;
+    gss_name_t         allocated_name = GSS_C_NO_NAME;
     gss_mechanism      mech;
-    gss_cred_id_t      cred;
-    gss_OID            new_mechs_array;
-    gss_cred_id_t *    new_cred_array;
+    gss_cred_id_t      cred = NULL;
+    gss_OID            new_mechs_array = NULL;
+    gss_cred_id_t *    new_cred_array = NULL;
+
+    /* check input parameters */
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
+
+    if (input_cred_handle == GSS_C_NO_CREDENTIAL &&
+       output_cred_handle == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED);
+
+    if (output_cred_handle)
+       *output_cred_handle = GSS_C_NO_CREDENTIAL;
 
-    if (input_cred_handle == GSS_C_NO_CREDENTIAL)
-       return GSS_S_NO_CRED;
+    if (actual_mechs)
+       *actual_mechs = NULL;
+
+    if (acceptor_time_rec)
+       *acceptor_time_rec = 0;
 
-    union_cred = (gss_union_cred_t) input_cred_handle;
+    if (initiator_time_rec)
+       *initiator_time_rec = 0;
 
-    mech = __gss_get_mechanism(desired_mech);
+    mech = gssint_get_mechanism(desired_mech);
     if (!mech)
        return GSS_S_BAD_MECH;
+    else if (!mech->gss_acquire_cred)
+       return (GSS_S_UNAVAILABLE);
 
-    if (__gss_get_mechanism_cred(union_cred, desired_mech) !=
-       GSS_C_NO_CREDENTIAL)
-       return GSS_S_DUPLICATE_ELEMENT;
+    if (input_cred_handle == GSS_C_NO_CREDENTIAL) {
+       union_cred = malloc(sizeof (gss_union_cred_desc));
+       if (union_cred == NULL)
+           return (GSS_S_FAILURE);
 
-    union_name = (gss_union_name_t) desired_name;
-    if (union_name->mech_type) {
-       if (!g_OID_equal(desired_mech, union_name->mech_type))
-           return GSS_S_BAD_NAMETYPE;
-       internal_name = union_name->mech_name;
+       (void) memset(union_cred, 0, sizeof (gss_union_cred_desc));
+
+       /* for default credentials we will use GSS_C_NO_NAME */
+       internal_name = GSS_C_NO_NAME;
     } else {
-       if (__gss_import_internal_name(minor_status, desired_mech,
-                                      union_name, &internal_name))
-           return (GSS_S_BAD_NAME);
+       union_cred = (gss_union_cred_t)input_cred_handle;
+       if (gssint_get_mechanism_cred(union_cred, desired_mech) !=
+           GSS_C_NO_CREDENTIAL)
+           return (GSS_S_DUPLICATE_ELEMENT);
+
+       /* may need to create a mechanism specific name */
+       if (desired_name) {
+           union_name = (gss_union_name_t)desired_name;
+           if (union_name->mech_type &&
+               g_OID_equal(union_name->mech_type,
+                           &mech->mech_type))
+               internal_name = union_name->mech_name;
+           else {
+               if (gssint_import_internal_name(minor_status,
+                                              &mech->mech_type, union_name,
+                                              &allocated_name) != GSS_S_COMPLETE)
+                   return (GSS_S_BAD_NAME);
+               internal_name = allocated_name;
+           }
+       }
     }
 
+
     if (cred_usage == GSS_C_ACCEPT)
        time_req = acceptor_time_req;
     else if (cred_usage == GSS_C_INITIATE)
@@ -443,22 +308,54 @@ gss_add_cred(minor_status, input_cred_handle,
                                    internal_name, time_req,
                                    GSS_C_NULL_OID_SET, cred_usage,
                                    &cred, NULL, &time_rec);
+
     if (status != GSS_S_COMPLETE)
        goto errout;
 
+    /* may need to set credential auxinfo strucutre */
+    if (union_cred->auxinfo.creation_time == 0) {
+       union_cred->auxinfo.creation_time = time(NULL);
+       union_cred->auxinfo.time_rec = time_rec;
+       union_cred->auxinfo.cred_usage = cred_usage;
+
+       /*
+        * we must set the name; if name is not supplied
+        * we must do inquire cred to get it
+        */
+       if (internal_name == NULL) {
+           if (mech->gss_inquire_cred == NULL ||
+               ((status = mech->gss_inquire_cred(
+                     mech->context,
+                     &temp_minor_status, cred,
+                     &allocated_name, NULL, NULL,
+                     NULL)) != GSS_S_COMPLETE))
+               goto errout;
+           internal_name = allocated_name;
+       }
+
+       if (internal_name != GSS_C_NO_NAME) {
+           status = mech->gss_display_name(mech->context,
+                                           &temp_minor_status, internal_name,
+                                           &union_cred->auxinfo.name,
+                                           &union_cred->auxinfo.name_type);
+       
+           if (status != GSS_S_COMPLETE)
+               goto errout;
+       }
+    }
+
+    /* now add the new credential elements */
     new_mechs_array = (gss_OID)
-       malloc(sizeof(gss_OID_desc) * (union_cred->count+1));
-    
+       malloc(sizeof (gss_OID_desc) * (union_cred->count+1));
+
     new_cred_array = (gss_cred_id_t *)
-       malloc(sizeof(gss_cred_id_t) * (union_cred->count+1));
+       malloc(sizeof (gss_cred_id_t) * (union_cred->count+1));
 
     if (!new_mechs_array || !new_cred_array) {
-       *minor_status = ENOMEM;
        status = GSS_S_FAILURE;
        goto errout;
     }
 
-
     if (acceptor_time_rec)
        if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH)
            *acceptor_time_rec = time_rec;
@@ -467,57 +364,77 @@ gss_add_cred(minor_status, input_cred_handle,
            *initiator_time_rec = time_rec;
 
     /*
-     * OK, expand the mechanism array in the union credentials
-     * (Look for the union label...)
+     * OK, expand the mechanism array and the credential array
      */
-    memcpy(new_mechs_array, union_cred->mechs_array,
-          sizeof(gss_OID_desc) * union_cred->count);
-    memcpy(new_cred_array, union_cred->cred_array,
-          sizeof(gss_cred_id_t) * union_cred->count);
-    
+    (void) memcpy(new_mechs_array, union_cred->mechs_array,
+                 sizeof (gss_OID_desc) * union_cred->count);
+    (void) memcpy(new_cred_array, union_cred->cred_array,
+                 sizeof (gss_cred_id_t) * union_cred->count);
+
     new_cred_array[union_cred->count] = cred;
-    new_mechs_array[union_cred->count].length = desired_mech->length;
-    new_mechs_array[union_cred->count].elements = malloc(desired_mech->length);
-    if (!new_mechs_array[union_cred->count].elements) {
-       *minor_status = ENOMEM;
+    if ((new_mechs_array[union_cred->count].elements =
+        malloc(mech->mech_type.length)) == NULL)
        goto errout;
+
+    g_OID_copy(&new_mechs_array[union_cred->count],
+              &mech->mech_type);
+
+    if (actual_mechs) {
+       *actual_mechs = create_actual_mechs(new_mechs_array,
+                                           union_cred->count + 1);
+       if (*actual_mechs == NULL) {
+           free(new_mechs_array[union_cred->count].elements);
+           goto errout;
+       }
     }
-    memcpy(new_mechs_array[union_cred->count].elements, desired_mech->elements,
-          desired_mech->length);
 
     if (output_cred_handle == NULL) {
        free(union_cred->mechs_array);
        free(union_cred->cred_array);
        new_union_cred = union_cred;
     } else {
-       new_union_cred = malloc(sizeof(gss_union_cred_desc));
+       new_union_cred = malloc(sizeof (gss_union_cred_desc));
        if (new_union_cred == NULL) {
-           *minor_status = ENOMEM;
+           free(new_mechs_array[union_cred->count].elements);
            goto errout;
        }
        *new_union_cred = *union_cred;
-       *output_cred_handle = new_union_cred;
+       *output_cred_handle = (gss_cred_id_t)new_union_cred;
     }
+
     new_union_cred->mechs_array = new_mechs_array;
     new_union_cred->cred_array = new_cred_array;
     new_union_cred->count++;
-    new_mechs_array = 0;
-    new_cred_array = 0;
 
-    if (actual_mechs)
-       *actual_mechs = create_actual_mechs(new_union_cred);
-    
-    status = GSS_S_COMPLETE;
-    
+    /* We're done with the internal name. Free it if we allocated it. */
+
+    if (allocated_name)
+       (void) gssint_release_internal_name(&temp_minor_status,
+                                          &mech->mech_type,
+                                          &allocated_name);
+
+    return (GSS_S_COMPLETE);
+
 errout:
     if (new_mechs_array)
        free(new_mechs_array);
     if (new_cred_array)
        free(new_cred_array);
-    if (!union_name->mech_type) {
-       (void) __gss_release_internal_name(&temp_minor_status,
-                                          desired_mech, &internal_name);
+
+    if (cred != NULL && mech->gss_release_cred)
+       mech->gss_release_cred(mech->context,
+                              &temp_minor_status, &cred);
+
+    if (allocated_name)
+       (void) gssint_release_internal_name(&temp_minor_status,
+                                          &mech->mech_type,
+                                          &allocated_name);
+
+    if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred) {
+       if (union_cred->auxinfo.name.value)
+           free(union_cred->auxinfo.name.value);
+       free(union_cred);
     }
 
-    return(status);
+    return (status);
 }
diff --git a/src/lib/gssapi/mechglue/g_canon_name.c b/src/lib/gssapi/mechglue/g_canon_name.c
new file mode 100644 (file)
index 0000000..a726fef
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* #pragma ident       "@(#)g_canon_name.c     1.15    04/02/23 SMI" */
+
+/*
+ * routine gss_canonicalize_name
+ *
+ * This routine is used to produce a mechanism specific
+ * representation of name that has been previously
+ * imported with gss_import_name.  The routine uses the mechanism
+ * specific implementation of gss_import_name to implement this
+ * function.
+ *
+ * We allow a NULL output_name, in which case we modify the
+ * input_name to include the mechanism specific name.
+ */
+
+#include <mglueP.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <errno.h>
+
+OM_uint32 KRB5_CALLCONV
+gss_canonicalize_name(minor_status,
+                               input_name,
+                               mech_type,
+                               output_name)
+OM_uint32 *minor_status;
+const gss_name_t input_name;
+const gss_OID mech_type;
+gss_name_t *output_name;
+{
+       gss_union_name_t in_union, out_union = NULL, dest_union = NULL;
+       OM_uint32 major_status = GSS_S_FAILURE;
+
+       if (minor_status == NULL)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+       *minor_status = 0;
+
+       if (output_name)
+               *output_name = 0;
+
+       /* check the input parameters */
+       if (input_name == NULL || mech_type == GSS_C_NULL_OID)
+               return (GSS_S_CALL_INACCESSIBLE_READ);
+
+       in_union = (gss_union_name_t)input_name;
+       /*
+        * If the caller wants to reuse the name, and the name has already
+        * been converted, then there is nothing for us to do.
+        */
+       if (!output_name && in_union->mech_type &&
+               g_OID_equal(in_union->mech_type, mech_type))
+               return (GSS_S_COMPLETE);
+
+       /* ok, then we need to do something - start by creating data struct */
+       if (output_name) {
+               out_union =
+                       (gss_union_name_t)malloc(sizeof (gss_union_name_desc));
+               if (!out_union)
+                       goto allocation_failure;
+
+               out_union->mech_type = 0;
+               out_union->mech_name = 0;
+               out_union->name_type = 0;
+               out_union->external_name = 0;
+
+               /* Allocate the buffer for the user specified representation */
+               if (gssint_create_copy_buffer(in_union->external_name,
+                               &out_union->external_name, 1))
+                       goto allocation_failure;
+
+               if (in_union->name_type != GSS_C_NULL_OID) {
+                       if ((major_status = generic_gss_copy_oid(minor_status,
+                               in_union->name_type, &out_union->name_type)))
+                       goto allocation_failure;
+               }
+
+       }
+
+       /*
+        * might need to delete any old mechanism names if we are
+        * reusing the buffer.
+        */
+       if (!output_name) {
+               if (in_union->mech_type) {
+                       (void) gssint_release_internal_name(minor_status,
+                                                       in_union->mech_type,
+                                                       &in_union->mech_name);
+                       (void) gss_release_oid(minor_status,
+                                           &in_union->mech_type);
+                       in_union->mech_type = 0;
+               }
+               dest_union = in_union;
+       } else
+               dest_union = out_union;
+
+       /* now let's create the new mech name */
+       if (major_status = generic_gss_copy_oid(minor_status, mech_type,
+                                               &dest_union->mech_type))
+               goto allocation_failure;
+
+       if (major_status =
+               gssint_import_internal_name(minor_status, mech_type,
+                                               dest_union,
+                                               &dest_union->mech_name))
+               goto allocation_failure;
+
+       if (output_name)
+               *output_name = (gss_name_t)dest_union;
+
+       return (GSS_S_COMPLETE);
+
+allocation_failure:
+       /* do not delete the src name external name format */
+       if (output_name) {
+               if (out_union->external_name) {
+                       if (out_union->external_name->value)
+                               free(out_union->external_name->value);
+                       free(out_union->external_name);
+               }
+               if (out_union->name_type)
+                       (void) gss_release_oid(minor_status,
+                                           &out_union->name_type);
+
+               dest_union = out_union;
+       } else
+               dest_union = in_union;
+
+       /*
+        * delete the partially created mech specific name
+        * applies for both src and dest which ever is being used for output
+        */
+
+       if (dest_union->mech_name) {
+               (void) gssint_release_internal_name(minor_status,
+                                               dest_union->mech_type,
+                                               &dest_union->mech_name);
+       }
+
+       if (dest_union->mech_type)
+               (void) gss_release_oid(minor_status, &dest_union->mech_type);
+
+
+       if (output_name)
+               free(out_union);
+
+       return (major_status);
+} /**********  gss_canonicalize_name ********/
index 851be4a85b65c7473d50ab99fac6c03087c05cbe..0a6db1c16bd242dbabbc6dd7c2b30958ee1c46b2 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_compare_name.c 1.13     95/08/02 SMI" */
+/* #pragma ident       "@(#)g_compare_name.c   1.16    04/02/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
 #endif
 #include <string.h>
 
-#define g_OID_equal(o1,o2) \
-   (((o1)->length == (o2)->length) && \
-    (memcmp((o1)->elements,(o2)->elements,(int) (o1)->length) == 0))
-
 OM_uint32 KRB5_CALLCONV
 gss_compare_name (minor_status,
                   name1,
@@ -54,13 +50,15 @@ int *                       name_equal;
     gss_mechanism      mech;
     gss_name_t         internal_name;
     
-    gss_initialize();
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
 
-    if (name1 == 0 || name2 == 0) {
-       if (name_equal)
-           *name_equal = 0;
-       return GSS_S_BAD_NAME;
-    }
+    if (name1 == 0 || name2 == 0)
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME);
+
+    if (name_equal == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
 
     union_name1 = (gss_union_name_t) name1;
     union_name2 = (gss_union_name_t) name2;
@@ -78,16 +76,13 @@ int *                       name_equal;
      * information.
      */
     if (union_name1->mech_type) {
-       mech = __gss_get_mechanism (union_name1->mech_type);
+       mech = gssint_get_mechanism (union_name1->mech_type);
        if (!mech)
            return (GSS_S_BAD_MECH);
        if (!mech->gss_compare_name)
-           return (GSS_S_BAD_BINDINGS);
+                       return (GSS_S_UNAVAILABLE);
     }
        
-    if (name_equal == NULL)
-       return GSS_S_COMPLETE;
-
     *name_equal = 0;           /* Default to *not* equal.... */
 
     /*
@@ -116,8 +111,32 @@ int *                      name_equal;
      * gss_import_name().
      */
     if (!union_name1->mech_type && !union_name2->mech_type) {
-       if (!g_OID_equal(union_name1->name_type, union_name2->name_type))
+               /*
+                * Second case, first sub-case... one name has null
+                * name_type, the other doesn't.
+                *
+                * Not knowing a mech_type we can't import the name with
+                * null name_type so we can't compare.
+                */
+               if ((union_name1->name_type == GSS_C_NULL_OID &&
+                   union_name2->name_type != GSS_C_NULL_OID) ||
+                   (union_name1->name_type != GSS_C_NULL_OID &&
+                   union_name2->name_type == GSS_C_NULL_OID))
+                       return (GSS_S_COMPLETE);
+               /*
+                * Second case, second sub-case... both names have
+                * name_types, but they are different.
+                */
+               if ((union_name1->name_type != GSS_C_NULL_OID &&
+                   union_name2->name_type != GSS_C_NULL_OID) &&
+                   !g_OID_equal(union_name1->name_type,
+                                       union_name2->name_type))
            return (GSS_S_COMPLETE);
+               /*
+                * Second case, third sub-case... both names have equal
+                * name_types (and both have no mech_types) so we just
+                * compare the external_names.
+                */
        if ((union_name1->external_name->length !=
             union_name2->external_name->length) ||
            (memcmp(union_name1->external_name->value,
@@ -141,16 +160,17 @@ int *                     name_equal;
        union_name1 = (gss_union_name_t) name2;
        union_name2 = (gss_union_name_t) name1;
     }
-    major_status = __gss_import_internal_name(minor_status,
+    major_status = gssint_import_internal_name(minor_status,
                                              union_name1->mech_type,
                                              union_name2,
                                              &internal_name);
     if (major_status != GSS_S_COMPLETE)
-       return (GSS_S_COMPLETE);
+       return (GSS_S_COMPLETE); /* return complete, but not equal */
+
     major_status = mech->gss_compare_name(mech->context, minor_status,
                                          union_name1->mech_name,
                                          internal_name, name_equal);
-    __gss_release_internal_name(&temp_minor, union_name1->mech_type,
+    gssint_release_internal_name(&temp_minor, union_name1->mech_type,
                                &internal_name);
     return (major_status);
     
index 6f08c9224eab2653cce3b3a602290a5c78ce0986..5ce6b56d86bf1c0741fcc0b12674640caacb0b04 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_context_time.c 1.8     95/08/07 SMI" */
+/* #pragma ident       "@(#)g_context_time.c   1.12    98/01/22 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -42,18 +42,23 @@ OM_uint32 *         time_rec;
     gss_union_ctx_id_t ctx;
     gss_mechanism      mech;
 
-    gss_initialize();
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
+
+    if (time_rec == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
 
     if (context_handle == GSS_C_NO_CONTEXT)
-       return GSS_S_NO_CONTEXT;
-    
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT);
+
     /*
      * select the approprate underlying mechanism routine and
      * call it.
      */
     
     ctx = (gss_union_ctx_id_t) context_handle;
-    mech = __gss_get_mechanism (ctx->mech_type);
+    mech = gssint_get_mechanism (ctx->mech_type);
     
     if (mech) {
 
@@ -64,10 +69,10 @@ OM_uint32 *         time_rec;
                                            ctx->internal_ctx_id,
                                            time_rec);
        else
-           status = GSS_S_BAD_BINDINGS;
+           status = GSS_S_UNAVAILABLE;
 
        return(status);
     }
     
-    return(GSS_S_NO_CONTEXT);
+    return (GSS_S_BAD_MECH);
 }
index fae0b0ac3397992097ab274d39ec65f6bf11d3f0..de70b8fb79e61a5d898ea1b09a6dc5a333689423 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_delete_sec_context.c 1.10     95/08/07 SMI" */
+/* #pragma ident       "@(#)g_delete_sec_context.c     1.11    97/11/09 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -46,20 +46,25 @@ gss_buffer_t                output_token;
     gss_union_ctx_id_t ctx;
     gss_mechanism      mech;
     
-    gss_initialize();
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+    if (output_token != GSS_C_NO_BUFFER) {
+       output_token->length = 0;
+       output_token->value = NULL;
+    }
 
     /* if the context_handle is Null, return NO_CONTEXT error */
-    
     if(context_handle == NULL || *context_handle == GSS_C_NO_CONTEXT)
-       return(GSS_S_NO_CONTEXT);
-    
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT);
+
     /*
      * select the approprate underlying mechanism routine and
      * call it.
      */
     
     ctx = (gss_union_ctx_id_t) *context_handle;
-    mech = __gss_get_mechanism (ctx->mech_type);
+    mech = gssint_get_mechanism (ctx->mech_type);
     
     if (mech) {
 
@@ -70,10 +75,9 @@ gss_buffer_t         output_token;
                                                  &ctx->internal_ctx_id,
                                                  output_token);
        else
-           status = GSS_S_BAD_BINDINGS;
+           status = GSS_S_UNAVAILABLE;
 
        /* now free up the space for the union context structure */
-       
        free(ctx->mech_type->elements);
        free(ctx->mech_type);
        free(*context_handle);
@@ -81,6 +85,6 @@ gss_buffer_t          output_token;
 
        return(status);
     }
-    
-    return(GSS_S_NO_CONTEXT);
+
+    return (GSS_S_BAD_MECH);
 }
index 8bd0426c3343f4b312be68ddde3136ac6a877aac..161b2707e37d986911496e9e33cae1962a5e4cf1 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)g_dsp_name.c 1.2     96/02/06 SMI" */
+/* #pragma ident       "@(#)g_dsp_name.c       1.13    04/02/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -49,8 +49,18 @@ gss_OID *            output_name_type;
     OM_uint32          major_status;
     gss_union_name_t   union_name;
     
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
+
     if (input_name == 0)
-       return GSS_S_BAD_NAME;
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME);
+
+    if (output_name_buffer == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+    if (output_name_type)
+       *output_name_type = NULL;
 
     union_name = (gss_union_name_t) input_name;
 
@@ -58,7 +68,7 @@ gss_OID *             output_name_type;
        /*
         * OK, we have a mechanism-specific name; let's use it!
         */
-       return (__gss_display_internal_name(minor_status,
+       return (gssint_display_internal_name(minor_status,
                                            union_name->mech_type,
                                            union_name->mech_name,
                                            output_name_buffer,
@@ -70,27 +80,29 @@ gss_OID *           output_name_type;
      * name into the output_name_buffer and point the output_name_type
      * to the name_type component of union_name
      */
-    if (output_name_type != NULL) {
+    if (output_name_type != NULL &&
+       union_name->name_type != GSS_C_NULL_OID) {
        major_status = generic_gss_copy_oid(minor_status,
                                            union_name->name_type,
                                            output_name_type);
-       if (major_status)
+       if (major_status != GSS_S_COMPLETE)
            return (major_status);
     }
-    
-    if (output_name_buffer != NULL) {
-       output_name_buffer->length = union_name->external_name->length;
-
-       output_name_buffer->value =
-           (void *) malloc(output_name_buffer->length);
 
-       memcpy(output_name_buffer->value,
-              union_name->external_name->value,
-              output_name_buffer->length);
+    if ((output_name_buffer->value =
+        malloc(union_name->external_name->length + 1)) == NULL) {
+       if (output_name_type && *output_name_type != GSS_C_NULL_OID) {
+           (void) generic_gss_release_oid(minor_status,
+                                          output_name_type);
+           *output_name_type = NULL;
+       }
+       return (GSS_S_FAILURE);
     }
-    
-    if (minor_status)
-       *minor_status = 0;
+    output_name_buffer->length = union_name->external_name->length;
+    (void) memcpy(output_name_buffer->value,
+                 union_name->external_name->value,
+                 union_name->external_name->length);
+    ((char *)output_name_buffer->value)[output_name_buffer->length] = '\0';
 
     return(GSS_S_COMPLETE);
 }
index 4b58c38f7091e1363538408971f96521994c8184..83583035fcaa07f865204f2d695855bcdf46962b 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_display_status.c 1.8     95/08/07 SMI" */
+/* #pragma ident       "@(#)g_dsp_status.c     1.17    04/02/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
 
 #include "mglueP.h"
 #include <stdio.h>
-#ifdef HAVE_STDLIB_H
 #include <stdlib.h>
-#endif
+#include <string.h>
+
+/* local function */
+static OM_uint32 displayMajor(OM_uint32, OM_uint32 *, gss_buffer_t);
 
 OM_uint32 KRB5_CALLCONV
 gss_display_status (minor_status,
@@ -49,36 +51,275 @@ OM_uint32 *                message_context;
 gss_buffer_t           status_string;
 
 {
-    OM_uint32          status;
     gss_OID            mech_type = (gss_OID) req_mech_type;
     gss_mechanism      mech;
 
-    gss_initialize();
+    /* check the input parameters */
+    if (!minor_status)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+    *minor_status = 0;
+
+    if (!message_context || status_string == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+    status_string->length = 0;
+    status_string->value = NULL;
+
+    /* we handle major status codes, and the mechs do the minor */
+    if (status_type == GSS_C_GSS_CODE)
+       return (displayMajor(status_value, message_context,
+                            status_string));
 
     /*
-     * select the approprate underlying mechanism routine and
+     * must be the minor status - let mechs do the work
+     * select the appropriate underlying mechanism routine and
      * call it.
      */
 
-    mech = __gss_get_mechanism (mech_type);
+    mech = gssint_get_mechanism (mech_type);
+
+    if (mech && mech->gss_display_status) {
+       if (mech_type == GSS_C_NULL_OID)
+           mech_type = &mech->mech_type;
 
-    if (mech == NULL) 
+       return (mech->gss_display_status(mech->context, minor_status,
+                                        status_value, status_type, mech_type,
+                                        message_context, status_string));
+    }
+
+    if (!mech)
        return (GSS_S_BAD_MECH);
 
-    if (mech_type == GSS_C_NULL_OID)
-       mech_type = &mech->mech_type;
-
-    if (mech->gss_display_status)
-       status = mech->gss_display_status(
-                                         mech->context,
-                                         minor_status,
-                                         status_value,
-                                         status_type,
-                                         mech_type,
-                                         message_context,
-                                         status_string);
-    else
-       status = GSS_S_BAD_BINDINGS;
-
-    return(status);
+    return (GSS_S_UNAVAILABLE);
 }
+
+/*
+ * function to map the major error codes
+ * it uses case statements so that the strings could be wrapped by gettext
+ * msgCtxt is interpreted as:
+ *     0 - first call
+ *     1 - routine error
+ *     >= 2 - the supplementary error code bit shifted by 1
+ */
+static OM_uint32
+displayMajor(status, msgCtxt, outStr)
+OM_uint32 status;
+OM_uint32 *msgCtxt;
+gss_buffer_t outStr;
+{
+       OM_uint32 oneVal, mask = 0x1, currErr;
+       char *errStr = NULL;
+       int i, haveErr = 0;
+
+       /* take care of the success value first */
+       if (status == GSS_S_COMPLETE)
+               errStr = "The routine completed successfully";
+       else if (*msgCtxt == 0 && (oneVal = GSS_CALLING_ERROR(status))) {
+               switch (oneVal) {
+               case GSS_S_CALL_INACCESSIBLE_READ:
+                       errStr = "A required input parameter"
+                               " could not be read";
+                       break;
+
+               case GSS_S_CALL_INACCESSIBLE_WRITE:
+                       errStr = "A required output parameter"
+                               " could not be written";
+                       break;
+
+               case GSS_S_CALL_BAD_STRUCTURE:
+                       errStr = "A parameter was malformed";
+                       break;
+
+               default:
+                       errStr = "An invalid status code was supplied";
+                       break;
+               }
+
+               /* we now need to determine new value of msgCtxt */
+               if (GSS_ROUTINE_ERROR(status))
+                       *msgCtxt = 1;
+               else if ((oneVal = GSS_SUPPLEMENTARY_INFO(status)) != 0)
+                       *msgCtxt = (OM_uint32)(oneVal << 1);
+               else
+                       *msgCtxt = 0;
+
+       } else if ((*msgCtxt == 0 || *msgCtxt == 1) &&
+               (oneVal = GSS_ROUTINE_ERROR(status))) {
+               switch (oneVal) {
+               case GSS_S_BAD_MECH:
+                       errStr = "An unsupported mechanism"
+                               " was requested";
+                       break;
+
+               case GSS_S_BAD_NAME:
+                       errStr = "An invalid name was supplied";
+                       break;
+
+               case GSS_S_BAD_NAMETYPE:
+                       errStr = "A supplied name was of an"
+                               " unsupported type";
+                       break;
+
+               case GSS_S_BAD_BINDINGS:
+                       errStr = "Incorrect channel bindings"
+                               " were supplied";
+                       break;
+
+               case GSS_S_BAD_SIG: /* same as GSS_S_BAD_MIC: */
+                       errStr = "A token had an invalid Message"
+                               " Integrity Check (MIC)";
+                       break;
+
+               case GSS_S_NO_CRED:
+                       errStr = "No credentials were supplied, or the"
+                               " credentials were unavailable or"
+                               " inaccessible";
+                       break;
+
+               case GSS_S_NO_CONTEXT:
+                       errStr = "No context has been established";
+                       break;
+
+               case GSS_S_DEFECTIVE_TOKEN:
+                       errStr = "Invalid token was supplied";
+                       break;
+
+               case GSS_S_DEFECTIVE_CREDENTIAL:
+                       errStr = "Invalid credential was supplied";
+                       break;
+
+               case GSS_S_CREDENTIALS_EXPIRED:
+                       errStr = "The referenced credential has"
+                               " expired";
+                       break;
+
+               case GSS_S_CONTEXT_EXPIRED:
+                       errStr = "The referenced context has expired";
+                       break;
+
+               case GSS_S_FAILURE:
+                       errStr = "Unspecified GSS failure.  Minor code"
+                               " may provide more information";
+                       break;
+
+               case GSS_S_BAD_QOP:
+                       errStr = "The quality-of-protection (QOP) "
+                               "requested could not be provided";
+                       break;
+
+               case GSS_S_UNAUTHORIZED:
+                       errStr = "The operation is forbidden by local"
+                               " security policy";
+                       break;
+
+               case GSS_S_UNAVAILABLE:
+                       errStr = "The operation or option is not"
+                               " available or unsupported";
+                       break;
+
+               case GSS_S_DUPLICATE_ELEMENT:
+                       errStr = "The requested credential element"
+                               " already exists";
+                       break;
+
+               case GSS_S_NAME_NOT_MN:
+                       errStr = "The provided name was not mechanism"
+                               " specific (MN)";
+                       break;
+
+               case GSS_S_BAD_STATUS:
+               default:
+                       errStr = "An invalid status code was supplied";
+               }
+
+               /* we must determine if the caller should call us again */
+               if ((oneVal = GSS_SUPPLEMENTARY_INFO(status)) != 0)
+                       *msgCtxt = (OM_uint32)(oneVal << 1);
+               else
+                       *msgCtxt = 0;
+
+       } else if ((*msgCtxt == 0 || *msgCtxt >= 2) &&
+               (oneVal = GSS_SUPPLEMENTARY_INFO(status))) {
+               /*
+                * if msgCtxt is not 0, then it should encode
+                * the supplementary error code we should be printing
+                */
+               if (*msgCtxt >= 2)
+                       oneVal = (OM_uint32) (*msgCtxt) >> 1;
+               else
+                       oneVal = GSS_SUPPLEMENTARY_INFO(status);
+
+               /* we display the errors LSB first */
+               for (i = 0; i < 16; i++) {
+                       if (oneVal & mask) {
+                               haveErr = 1;
+                               break;
+                       }
+                       mask <<= 1;
+               }
+
+               /* isolate the bit or if not found set to illegal value */
+               if (haveErr)
+                       currErr = oneVal & mask;
+               else
+                       currErr = 1 << 17; /* illegal value */
+
+               switch (currErr) {
+               case GSS_S_CONTINUE_NEEDED:
+                       errStr = "The routine must be called again to"
+                               " complete its function";
+                       break;
+
+               case GSS_S_DUPLICATE_TOKEN:
+                       errStr = "The token was a duplicate of an"
+                               " earlier token";
+                       break;
+
+               case GSS_S_OLD_TOKEN:
+                       errStr = "The token's validity period"
+                               " has expired";
+                       break;
+
+               case GSS_S_UNSEQ_TOKEN:
+                       errStr = "A later token has already been"
+                               " processed";
+                       break;
+
+               case GSS_S_GAP_TOKEN:
+                       errStr = "An expected per-message token was"
+                               " not received";
+                       break;
+
+               default:
+                       errStr = "An invalid status code was supplied";
+               }
+
+               /*
+                * we must check if there is any other supplementary errors
+                * if found, then turn off current bit, and store next value
+                * in msgCtxt shifted by 1 bit
+                */
+               if (!haveErr)
+                       *msgCtxt = 0;
+               else if (GSS_SUPPLEMENTARY_INFO(oneVal) ^ mask)
+                       *msgCtxt = (OM_uint32)
+                               ((GSS_SUPPLEMENTARY_INFO(oneVal) ^ mask) << 1);
+               else
+                       *msgCtxt = 0;
+       }
+
+       if (errStr == NULL)
+               errStr = "An invalid status code was supplied";
+
+       /* now copy the status code and return to caller */
+       outStr->length = strlen(errStr);
+       outStr->value = malloc((size_t)outStr->length+1);
+       if (outStr->value == NULL) {
+               outStr->length = 0;
+               return (GSS_S_FAILURE);
+       }
+
+       (void) strcpy((char *)outStr->value, errStr);
+       return (GSS_S_COMPLETE);
+} /* displayMajor */
diff --git a/src/lib/gssapi/mechglue/g_dup_name.c b/src/lib/gssapi/mechglue/g_dup_name.c
new file mode 100644 (file)
index 0000000..1f8815f
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* #pragma ident       "@(#)g_dup_name.c       1.14    04/02/23 SMI" */
+
+/*
+ *  routine gss_duplicate_name
+ *
+ * This routine does not rely on mechanism implementation of this
+ * name, but instead uses mechanism specific gss_import_name routine.
+ */
+
+#include <mglueP.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <errno.h>
+
+OM_uint32 KRB5_CALLCONV
+gss_duplicate_name(minor_status,
+               src_name,
+               dest_name)
+OM_uint32 *minor_status;
+const gss_name_t src_name;
+gss_name_t *dest_name;
+{
+               gss_union_name_t src_union, dest_union;
+               OM_uint32 major_status = GSS_S_FAILURE;
+
+
+       if (!minor_status)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+       *minor_status = 0;
+
+       /* if output_name is NULL, simply return */
+       if (dest_name == NULL)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_BAD_NAME);
+
+       *dest_name = 0;
+
+       if (src_name == NULL)
+               return (GSS_S_CALL_INACCESSIBLE_READ);
+
+       src_union = (gss_union_name_t)src_name;
+
+       /*
+        * First create the union name struct that will hold the external
+        * name and the name type.
+        */
+       dest_union = (gss_union_name_t)malloc(sizeof (gss_union_name_desc));
+       if (!dest_union)
+               goto allocation_failure;
+
+       dest_union->mech_type = 0;
+       dest_union->mech_name = 0;
+       dest_union->name_type = 0;
+       dest_union->external_name = 0;
+
+       /* Now copy the external representaion */
+       if (gssint_create_copy_buffer(src_union->external_name,
+                               &dest_union->external_name, 0))
+               goto allocation_failure;
+
+       if (src_union->name_type != GSS_C_NULL_OID) {
+               major_status = generic_gss_copy_oid(minor_status,
+                                               src_union->name_type,
+                                               &dest_union->name_type);
+               if (major_status != GSS_S_COMPLETE)
+                       goto allocation_failure;
+       }
+
+       /*
+        * See if source name is mechanim specific, if so then need to import it
+        */
+       if (src_union->mech_type) {
+               major_status = generic_gss_copy_oid(minor_status,
+                                                       src_union->mech_type,
+                                                       &dest_union->mech_type);
+               if (major_status != GSS_S_COMPLETE)
+                       goto allocation_failure;
+
+               major_status = gssint_import_internal_name(minor_status,
+                                                       dest_union->mech_type,
+                                                       dest_union,
+                                                       &dest_union->mech_name);
+               if (major_status != GSS_S_COMPLETE)
+                       goto allocation_failure;
+       }
+
+
+       *dest_name = (gss_name_t)dest_union;
+       return (GSS_S_COMPLETE);
+
+allocation_failure:
+       if (dest_union) {
+               if (dest_union->external_name) {
+                       if (dest_union->external_name->value)
+                               free(dest_union->external_name->value);
+                               free(dest_union->external_name);
+               }
+               if (dest_union->name_type)
+                       (void) generic_gss_release_oid(minor_status,
+                                                       &dest_union->name_type);
+               if (dest_union->mech_name)
+                       (void) gssint_release_internal_name(minor_status,
+                                               dest_union->mech_type,
+                                               &dest_union->mech_name);
+               if (dest_union->mech_type)
+                       (void) generic_gss_release_oid(minor_status,
+                                                       &dest_union->mech_type);
+               free(dest_union);
+       }
+       return (major_status);
+} /*   gss_duplicate_name      */
index 958553b49b6e647c44ac1ce7f0cdbc2698d6b994..539920b49b0225d1a392f938f0706dabb9328881 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)g_exp_sec_context.c 1.2     96/01/18 SMI" */
+/* #pragma ident       "@(#)g_exp_sec_context.c        1.14    04/02/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -45,16 +45,21 @@ gss_buffer_t                interprocess_token;
 
 {
     OM_uint32          status;
-    size_t             length;
+    OM_uint32          length;
     gss_union_ctx_id_t ctx;
     gss_mechanism      mech;
     gss_buffer_desc    token;
     char               *buf;
     
-    gss_initialize();
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
 
     if (context_handle == NULL || *context_handle == GSS_C_NO_CONTEXT)
-       return GSS_S_NO_CONTEXT;
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT);
+
+    if (interprocess_token == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_READ);
 
     /*
      * select the approprate underlying mechanism routine and
@@ -62,11 +67,11 @@ gss_buffer_t                interprocess_token;
      */
     
     ctx = (gss_union_ctx_id_t) *context_handle;
-    mech = __gss_get_mechanism (ctx->mech_type);
+    mech = gssint_get_mechanism (ctx->mech_type);
     if (!mech)
        return GSS_S_BAD_MECH;
     if (!mech->gss_export_sec_context)
-       return GSS_S_BAD_BINDINGS;
+       return (GSS_S_UNAVAILABLE);
     
     status = mech->gss_export_sec_context(mech->context, minor_status,
                                          &ctx->internal_ctx_id, &token);
@@ -78,7 +83,6 @@ gss_buffer_t          interprocess_token;
     interprocess_token->value = malloc(length);
     if (interprocess_token->value == 0) {
        (void) gss_release_buffer(minor_status, &token);
-       *minor_status = ENOMEM;
        return (GSS_S_FAILURE);
     }
     buf = interprocess_token->value;
diff --git a/src/lib/gssapi/mechglue/g_export_name.c b/src/lib/gssapi/mechglue/g_export_name.c
new file mode 100644 (file)
index 0000000..a6aab3a
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1996,1997, by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+/* #pragma ident       "@(#)g_export_name.c    1.11    00/07/17 SMI" */
+
+/*
+ * glue routine gss_export_name
+ *
+ * Will either call the mechanism defined gss_export_name, or if one is
+ * not defined will call a generic_gss_export_name routine.
+ */
+
+#include <mglueP.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <errno.h>
+
+OM_uint32 KRB5_CALLCONV
+gss_export_name(minor_status,
+                       input_name,
+                       exported_name)
+OM_uint32 *            minor_status;
+const gss_name_t       input_name;
+gss_buffer_t           exported_name;
+{
+       gss_union_name_t                union_name;
+
+
+       if (minor_status)
+               *minor_status = 0;
+
+       /* check out parameter */
+       if (!exported_name)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+       exported_name->value = NULL;
+       exported_name->length = 0;
+
+       /* check input parameter */
+       if (!input_name)
+               return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME);
+
+       union_name = (gss_union_name_t)input_name;
+
+       /* the name must be in mechanism specific format */
+       if (!union_name->mech_type)
+               return (GSS_S_NAME_NOT_MN);
+
+       return gssint_export_internal_name(minor_status, union_name->mech_type,
+                                       union_name->mech_name, exported_name);
+}
index 6aecab7fb6d9489a13165507725392cbdb73bb9a..030fbfdfa571cdb06422d804f76ea2ba9aefa0e2 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident      "@(#)g_glue.c 1.1     96/02/06 SMI" */
+/* #pragma ident       "@(#)g_glue.c   1.25    04/02/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
 #include <string.h>
 #include <errno.h>
 
-#define g_OID_equal(o1,o2) \
-   (((o1)->length == (o2)->length) && \
-    (memcmp((o1)->elements,(o2)->elements,(int) (o1)->length) == 0))
+#define        MSO_BIT (8*(sizeof (int) - 1))  /* Most significant octet bit */
 
-extern gss_mechanism *__gss_mechs_array;
+extern gss_mechanism *gssint_mechs_array;
 
 /*
  * This file contains the support routines for the glue layer.
  */
 
 /*
- *  given the mechs_array and a mechanism OID, return the 
- *  pointer to the mechanism, or NULL if that mechanism is
- *  not supported.  If the requested OID is NULL, then return
- *  the first mechanism.
+ * get_der_length: Givin a pointer to a buffer that contains a DER encoded
+ * length, decode the length updating the buffer to point to the character
+ * after the DER encoding. The parameter bytes will point to the number of
+ * bytes that made up the DER encoding of the length originally pointed to
+ * by the buffer. Note we return -1 on error.
  */
+int
+gssint_get_der_length(unsigned char **buf, unsigned int buf_len, unsigned int *bytes)
+{
+    /* p points to the beginning of the buffer */
+    unsigned char *p = *buf;
+    int length, new_length;
+    int octets;
+
+    if (buf_len < 1)
+       return (-1);
+
+    /* We should have at least one byte */
+    *bytes = 1;
+
+    /*
+     * If the High order bit is not set then the length is just the value
+     * of *p.
+     */
+    if (*p < 128) {
+       *buf = p+1;     /* Advance the buffer */
+       return (*p);            /* return the length */
+    }
+
+    /*
+     * if the High order bit is set, then the low order bits represent
+     * the number of bytes that contain the DER encoding of the length.
+     */
+
+    octets = *p++ & 0x7f;
+    *bytes += octets;
 
-gss_mechanism __gss_get_mechanism (type)
-     gss_OID type;
+    /* See if the supplied buffer contains enough bytes for the length. */
+    if (octets > buf_len - 1)
+       return (-1);
+
+    /*
+     * Calculate a multibyte length. The length is encoded as an
+     * unsigned integer base 256.
+     */
+    for (length = 0; octets; octets--) {
+       new_length = (length << 8) + *p++;
+       if (new_length < length)  /* overflow */
+           return (-1);
+       length = new_length;
+    }
+
+    *buf = p; /* Advance the buffer */
+
+    return (length);
+}
+
+/*
+ * der_length_size: Return the number of bytes to encode a given length.
+ */
+unsigned int
+gssint_der_length_size(unsigned int len)
 {
-    int        i;
+    int i;
 
-    if (type == GSS_C_NULL_OID)
-       return (__gss_mechs_array[0]);
+    if (len < 128)
+       return (1);
 
-    for (i=0; __gss_mechs_array[i]->mech_type.length != 0; i++) {
-       if ((__gss_mechs_array[i]->mech_type.length == type->length) &&
-           (memcmp (__gss_mechs_array[i]->mech_type.elements, type->elements,
-                    type->length) == 0)) {
+    for (i = 0; len; i++) {
+       len >>= 8;
+    }
+
+    return (i+1);
+}
+
+/*
+ * put_der_length: Encode the supplied length into the buffer pointed to
+ * by buf. max_length represents the maximum length of the buffer pointed
+ * to by buff. We will advance buf to point to the character after the newly
+ * DER encoded length. We return 0 on success or -l it the length cannot
+ * be encoded in max_len characters.
+ */
+int
+gssint_put_der_length(unsigned int length, unsigned char **buf, unsigned int max_len)
+{
+    unsigned char *s = *buf, *p;
+    unsigned int buf_len = 0;
+    int i, first;
+
+    /* Oops */
+    if (buf == 0 || max_len < 1)
+       return (-1);
+
+    /* Single byte is the length */
+    if (length < 128) {
+       *s++ = length;
+       *buf = s;
+       return (0);
+    }
+
+    /* First byte contains the number of octets */
+    p = s + 1;
+
+    /* Running total of the DER encoding length */
+    buf_len = 0;
 
-           return (__gss_mechs_array[i]);
+    /*
+     * Encode MSB first. We do the encoding by setting a shift
+     * factor to MSO_BIT (24 for 32 bit words) and then shifting the length
+     * by the factor. We then encode the resulting low order byte.
+     * We subtract 8 from the shift factor and repeat to ecnode the next
+     * byte. We stop when the shift factor is zero or we've run out of
+     * buffer to encode into.
+     */
+    first = 0;
+    for (i = MSO_BIT; i >= 0 && buf_len <= max_len; i -= 8) {
+       unsigned int v;
+       v = (length >> i) & 0xff;
+       if ((v) || first) {
+           buf_len += 1;
+           *p++ = v;
+           first = 1;
        }
     }
-    return NULL;
+    if (i >= 0)                        /* buffer overflow */
+       return (-1);
+
+    /*
+     * We go back now and set the first byte to be the length with
+     * the high order bit set.
+     */
+    *s = buf_len | 0x80;
+    *buf = p;
+
+    return (0);
 }
 
 
@@ -72,7 +182,7 @@ gss_mechanism __gss_get_mechanism (type)
  *
  */
 
-OM_uint32 __gss_get_mech_type(OID, token)
+OM_uint32 gssint_get_mech_type(OID, token)
     gss_OID            OID;
     gss_buffer_t       token;
 {
@@ -102,7 +212,10 @@ OM_uint32 __gss_get_mech_type(OID, token)
      * The routine fills in the OID value and returns an error as necessary.
      */
     
-    if (token == NULL)
+       if (OID == NULL)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+       if ((token == NULL) || (token->value == NULL))
        return (GSS_S_DEFECTIVE_TOKEN);
     
     /* Skip past the APP/Sequnce byte and the token length */
@@ -112,6 +225,11 @@ OM_uint32 __gss_get_mech_type(OID, token)
     if (*(buffer_ptr++) != 0x60)
        return (GSS_S_DEFECTIVE_TOKEN);
     length = *buffer_ptr++;
+
+       /* check if token length is null */
+       if (length == 0)
+           return (GSS_S_DEFECTIVE_TOKEN);
+
     if (length & 0x80) {
        if ((length & 0x7f) > 4)
            return (GSS_S_DEFECTIVE_TOKEN);
@@ -133,7 +251,7 @@ OM_uint32 __gss_get_mech_type(OID, token)
 
 #include "mglueP.h"
 
-OM_uint32 __gss_import_internal_name (minor_status, mech_type, union_name, 
+OM_uint32 gssint_import_internal_name (minor_status, mech_type, union_name, 
                                internal_name)
 OM_uint32      *minor_status;
 gss_OID                mech_type;
@@ -143,7 +261,7 @@ gss_name_t  *internal_name;
     OM_uint32          status;
     gss_mechanism      mech;
 
-    mech = __gss_get_mechanism (mech_type);
+    mech = gssint_get_mechanism (mech_type);
     if (mech) {
        if (mech->gss_import_name)
            status = mech->gss_import_name (
@@ -153,7 +271,7 @@ gss_name_t  *internal_name;
                                            union_name->name_type,
                                            internal_name);
        else
-           status = GSS_S_BAD_BINDINGS;
+           status = GSS_S_UNAVAILABLE;
 
        return (status);
     }
@@ -161,7 +279,124 @@ gss_name_t        *internal_name;
     return (GSS_S_BAD_MECH);
 }
 
-OM_uint32 __gss_display_internal_name (minor_status, mech_type, internal_name, 
+OM_uint32 gssint_export_internal_name(minor_status, mech_type,
+                                    internal_name, name_buf)
+    OM_uint32          *minor_status;
+    const gss_OID              mech_type;
+    const gss_name_t   internal_name;
+    gss_buffer_t               name_buf;
+{
+    OM_uint32 status;
+    gss_mechanism mech;
+    gss_buffer_desc dispName;
+    gss_OID nameOid;
+    unsigned char *buf = NULL;
+    const unsigned char tokId[] = "\x04\x01";
+    const unsigned int tokIdLen = 2;
+    const int mechOidLenLen = 2, mechOidTagLen = 1, nameLenLen = 4;
+    int mechOidDERLen = 0;
+    int mechOidLen = 0;
+
+    mech = gssint_get_mechanism(mech_type);
+    if (!mech)
+       return (GSS_S_BAD_MECH);
+
+    if (mech->gss_export_name)
+       return (mech->gss_export_name(mech->context,
+                                     minor_status,
+                                     internal_name,
+                                     name_buf));
+
+    /*
+     * if we are here it is because the mechanism does not provide
+     * a gss_export_name so we will use our implementation.  We
+     * do required that the mechanism define a gss_display_name.
+     */
+    if (!mech->gss_display_name)
+       return (GSS_S_UNAVAILABLE);
+
+    /*
+     * NOTE: RFC2743 (section 3.2) governs the format of the outer
+     *  wrapper of exported names; the mechanisms' specs govern
+     *  the format of the inner portion of the exported name
+     *  and, for some (e.g., RFC1964, the Kerberos V mech), a
+     *  generic default as implemented here will do.
+     *
+     * The outer wrapper of an exported MN is: 2-octet tok Id
+     * (0x0401) + 2-octet network-byte order mech OID length + mech
+     * oid (in DER format, including DER tag and DER length) +
+     * 4-octet network-byte order length of inner portion + inner
+     * portion.
+     *
+     * For the Kerberos V mechanism the inner portion of an exported
+     * MN is the display name string and ignores the name type OID
+     * altogether.  And we hope this will be so for any future
+     * mechanisms also, so that factoring name export/import out of
+     * the mech and into libgss pays off.
+     */
+    if ((status = mech->gss_display_name(mech->context,
+                                        minor_status,
+                                        internal_name,
+                                        &dispName,
+                                        &nameOid))
+       != GSS_S_COMPLETE)
+       return (status);
+
+    /* determine the size of the buffer needed */
+    mechOidDERLen = gssint_der_length_size(mech_type->length);
+    name_buf->length = tokIdLen + mechOidLenLen +
+       mechOidTagLen + mechOidDERLen +
+       mech_type->length +
+       nameLenLen + dispName.length;
+    if ((name_buf->value = (void*)malloc(name_buf->length)) ==
+       (void*)NULL) {
+       name_buf->length = 0;
+       (void) gss_release_buffer(&status, &dispName);
+       return (GSS_S_FAILURE);
+    }
+
+    /* now create the name ..... */
+    buf = (unsigned char *)name_buf->value;
+    (void) memset(name_buf->value, 0, name_buf->length);
+    (void) memcpy(buf, tokId, tokIdLen);
+    buf += tokIdLen;
+
+    /* spec allows only 2 bytes for the mech oid length */
+    mechOidLen = mechOidDERLen + mechOidTagLen + mech_type->length;
+    *buf++ = (mechOidLen & 0xFF00) >> 8;
+    *buf++ = (mechOidLen & 0x00FF);
+
+    /*
+     * DER Encoding of mech OID contains OID Tag (0x06), length and
+     * mech OID value
+     */
+    *buf++ = 0x06;
+    if (gssint_put_der_length(mech_type->length, &buf,
+                      (name_buf->length - tokIdLen -2)) != 0) {
+       name_buf->length = 0;
+       free(name_buf->value);
+       (void) gss_release_buffer(&status, &dispName);
+       return (GSS_S_FAILURE);
+    }
+
+    (void) memcpy(buf, mech_type->elements, mech_type->length);
+    buf += mech_type->length;
+
+    /* spec designates the next 4 bytes for the name length */
+    *buf++ = (dispName.length & 0xFF000000) >> 24;
+    *buf++ = (dispName.length & 0x00FF0000) >> 16;
+    *buf++ = (dispName.length & 0x0000FF00) >> 8;
+    *buf++ = (dispName.length & 0X000000FF);
+
+    /* for the final ingredient - add the name from gss_display_name */
+    (void) memcpy(buf, dispName.value, dispName.length);
+
+    /* release the buffer obtained from gss_display_name */
+    (void) gss_release_buffer(minor_status, &dispName);
+    return (GSS_S_COMPLETE);
+} /*  gssint_export_internal_name */
+
+OM_uint32 gssint_display_internal_name (minor_status, mech_type, internal_name, 
                                 external_name, name_type)
 OM_uint32      *minor_status;
 gss_OID                mech_type;
@@ -172,7 +407,7 @@ gss_OID             *name_type;
     OM_uint32          status;
     gss_mechanism      mech;
 
-    mech = __gss_get_mechanism (mech_type);
+    mech = gssint_get_mechanism (mech_type);
     if (mech) {
        if (mech->gss_display_name)
            status = mech->gss_display_name (
@@ -182,7 +417,7 @@ gss_OID             *name_type;
                                             external_name,
                                             name_type);
        else
-           status = GSS_S_BAD_BINDINGS;
+           status = GSS_S_UNAVAILABLE;
 
        return (status);
     }
@@ -190,7 +425,7 @@ gss_OID             *name_type;
     return (GSS_S_BAD_MECH);
 }
 
-OM_uint32 __gss_release_internal_name (minor_status, mech_type, internal_name)
+OM_uint32 gssint_release_internal_name (minor_status, mech_type, internal_name)
 OM_uint32      *minor_status;
 gss_OID                mech_type;
 gss_name_t     *internal_name;
@@ -198,7 +433,7 @@ gss_name_t  *internal_name;
     OM_uint32          status;
     gss_mechanism      mech;
 
-    mech = __gss_get_mechanism (mech_type);
+    mech = gssint_get_mechanism (mech_type);
     if (mech) {
        if (mech->gss_release_name)
            status = mech->gss_release_name (
@@ -206,7 +441,7 @@ gss_name_t  *internal_name;
                                             minor_status,
                                             internal_name);
        else
-           status = GSS_S_BAD_BINDINGS;
+           status = GSS_S_UNAVAILABLE;
 
        return (status);
     }
@@ -220,7 +455,7 @@ gss_name_t  *internal_name;
  * name.  Note that internal_name should be considered "consumed" by
  * this call, whether or not we return an error.
  */
-OM_uint32 __gss_convert_name_to_union_name(minor_status, mech,
+OM_uint32 gssint_convert_name_to_union_name(minor_status, mech,
                                           internal_name, external_name)
     OM_uint32 *minor_status;
     gss_mechanism      mech;
@@ -232,7 +467,6 @@ OM_uint32 __gss_convert_name_to_union_name(minor_status, mech,
 
     union_name = (gss_union_name_t) malloc (sizeof(gss_union_name_desc));
     if (!union_name) {
-           *minor_status = ENOMEM;
            goto allocation_failure;
     }
     union_name->mech_type = 0;
@@ -248,7 +482,6 @@ OM_uint32 __gss_convert_name_to_union_name(minor_status, mech,
     union_name->external_name =
        (gss_buffer_t) malloc(sizeof(gss_buffer_desc));
     if (!union_name->external_name) {
-           *minor_status = ENOMEM;
            goto allocation_failure;
     }
        
@@ -271,13 +504,17 @@ allocation_failure:
        }
        if (union_name->name_type)
            gss_release_oid(&tmp, &union_name->name_type);
-       if (union_name->mech_name)
-           __gss_release_internal_name(minor_status, union_name->mech_type,
-                                       &union_name->mech_name);
        if (union_name->mech_type)
            gss_release_oid(&tmp, &union_name->mech_type);
        free(union_name);
     }
+    /*
+     * do as the top comment says - since we are now owners of
+     * internal_name, we must clean it up
+     */
+    if (internal_name)
+       (void) gssint_release_internal_name(&tmp, &mech->mech_type,
+                                          &internal_name);
     return (major_status);
 }
 
@@ -286,7 +523,7 @@ allocation_failure:
  * external union credential.
  */
 gss_cred_id_t
-__gss_get_mechanism_cred(union_cred, mech_type)
+gssint_get_mechanism_cred(union_cred, mech_type)
     gss_union_cred_t   union_cred;
     gss_OID            mech_type;
 {
@@ -302,4 +539,46 @@ __gss_get_mechanism_cred(union_cred, mech_type)
     return GSS_C_NO_CREDENTIAL;
 }
 
-    
+/*
+ * Routine to create and copy the gss_buffer_desc structure.
+ * Both space for the structure and the data is allocated.
+ */
+OM_uint32
+gssint_create_copy_buffer(srcBuf, destBuf, addNullChar)
+    const gss_buffer_t srcBuf;
+    gss_buffer_t               *destBuf;
+    int                        addNullChar;
+{
+    gss_buffer_t aBuf;
+    unsigned int len;
+
+    if (destBuf == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+    *destBuf = 0;
+
+    aBuf = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
+    if (!aBuf)
+       return (GSS_S_FAILURE);
+
+    if (addNullChar)
+       len = srcBuf->length + 1;
+    else
+       len = srcBuf->length;
+
+    if (!(aBuf->value = (void*)malloc(len))) {
+       free(aBuf);
+       return (GSS_S_FAILURE);
+    }
+
+
+    (void) memcpy(aBuf->value, srcBuf->value, srcBuf->length);
+    aBuf->length = srcBuf->length;
+    *destBuf = aBuf;
+
+    /* optionally add a NULL character */
+    if (addNullChar)
+       ((char *)aBuf->value)[aBuf->length] = '\0';
+
+    return (GSS_S_COMPLETE);
+} /* ****** gssint_create_copy_buffer  ****** */
index e93b4c9acd980465f4d97c68c1049cd790d78274..48815b3615accde9acb860abedb86986ba67aad7 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)g_imp_name.c 1.2     96/02/06 SMI" */
+/* #pragma ident       "@(#)g_imp_name.c       1.26    04/02/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -35,6 +35,9 @@
 #include <string.h>
 #include <errno.h>
 
+/* local function to import GSS_C_EXPORT_NAME names */
+static OM_uint32 importExportName(OM_uint32 *, gss_union_name_t);
+
 OM_uint32 KRB5_CALLCONV
 gss_import_name(minor_status,
                 input_name_buffer,
@@ -49,33 +52,32 @@ gss_name_t *                output_name;
 {
     gss_union_name_t   union_name;
     OM_uint32          tmp, major_status = GSS_S_FAILURE;
-    gss_OID            mech;
-
-    gss_initialize();
 
-    if (minor_status)
-       *minor_status = 0;
+    /* check output parameters */
+    if (!minor_status)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
 
-    /* if output_name is NULL, simply return */
+    *minor_status = 0;
 
-    if(output_name == NULL)
-       return (GSS_S_COMPLETE);
+    if (output_name == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
 
     *output_name = 0;
 
     if (input_name_buffer == GSS_C_NO_BUFFER)
        return (GSS_S_BAD_NAME);
 
+    if (GSS_EMPTY_BUFFER(input_name_buffer))
+       return (GSS_S_BAD_NAME);
+
     /*
      * First create the union name struct that will hold the external
      * name and the name type.
      */
-
     union_name = (gss_union_name_t) malloc (sizeof(gss_union_name_desc));
-    if (!union_name) {
-           *minor_status = ENOMEM;
-           goto allocation_failure;
-    }
+    if (!union_name)
+       return (GSS_S_FAILURE);
+
     union_name->mech_type = 0;
     union_name->mech_name = 0;
     union_name->name_type = 0;
@@ -84,61 +86,43 @@ gss_name_t *                output_name;
     /*
      * All we do here is record the external name and name_type.
      * When the name is actually used, the underlying gss_import_name()
-     * is called for the appropriate mechanism. Note that the name type
-     * is assumed to be constant, so only a pointer to it is stored in
-     * union_name
+     * is called for the appropriate mechanism.  The exception to this
+     * rule is when the name of GSS_C_NT_EXPORT_NAME type.  If that is
+     * the case, then we make it MN in this call.
      */
-    union_name->external_name =
-       (gss_buffer_t) malloc(sizeof(gss_buffer_desc));
-    if (!union_name->external_name) {
-           *minor_status = ENOMEM;
-           goto allocation_failure;
-    }
-    
-    union_name->external_name->length = input_name_buffer->length;
-    /* we malloc length+1 to stick a NULL on the end, just in case */
-    /* Note that this NULL is not included in ->length for a reason! */
-    union_name->external_name->value =
-       (void  *) malloc(input_name_buffer->length+1);
-    if (!union_name->external_name->value) {
-       *minor_status = ENOMEM;
-       goto allocation_failure;
+    major_status = gssint_create_copy_buffer(input_name_buffer,
+                                           &union_name->external_name, 0);
+    if (major_status != GSS_S_COMPLETE) {
+       free(union_name);
+       return (major_status);
     }
-       
-    memcpy(union_name->external_name->value, input_name_buffer->value,
-          input_name_buffer->length);
-
-    /* add NULL to end of external_name->value, just in case... */
-    ((char *)union_name->external_name->value)
-                               [input_name_buffer->length] = '\0';
 
-    major_status = generic_gss_copy_oid(minor_status, input_name_type,
-                                       &union_name->name_type);
-    if (major_status != GSS_S_COMPLETE)
-       goto allocation_failure;
+    if (input_name_type != GSS_C_NULL_OID) {
+       major_status = generic_gss_copy_oid(minor_status,
+                                           input_name_type,
+                                           &union_name->name_type);
+       if (major_status != GSS_S_COMPLETE)
+           goto allocation_failure;
+    }
 
     /*
-     * See if this is a mechanism-specific name.  If so, let's import
-     * it now so we can get any error messages, and to avoid trouble
-     * later...
+     * In MIT Distribution the mechanism is determined from the nametype;
+     * This is not a good idea - first mechanism that supports a given
+     * name type is picked up; later on the caller can request a
+     * different mechanism. So we don't determine the mechanism here. Now
+     * the user level and kernel level import_name routine looks similar
+     * except the kernel routine makes a copy of the nametype structure. We
+     * do however make this an MN for names of GSS_C_NT_EXPORT_NAME type.
      */
-    mech = gss_find_mechanism_from_name_type(input_name_type);
-    if (mech) {
-       major_status = generic_gss_copy_oid(minor_status, mech,
-                                           &union_name->mech_type);
+    if (input_name_type != GSS_C_NULL_OID &&
+       g_OID_equal(input_name_type, GSS_C_NT_EXPORT_NAME)) {
+       major_status = importExportName(minor_status, union_name);
        if (major_status != GSS_S_COMPLETE)
            goto allocation_failure;
-
-       major_status = __gss_import_internal_name(minor_status, mech, 
-                                                 union_name,
-                                                 &union_name->mech_name);
-       if (major_status)
-           goto allocation_failure;
     }
 
-    *output_name = (gss_name_t) union_name;
-
-    return(GSS_S_COMPLETE);
+    *output_name = (gss_name_t)union_name;
+    return (GSS_S_COMPLETE);
 
 allocation_failure:
     if (union_name) {
@@ -150,7 +134,7 @@ allocation_failure:
        if (union_name->name_type)
            generic_gss_release_oid(&tmp, &union_name->name_type);
        if (union_name->mech_name)
-           __gss_release_internal_name(minor_status, union_name->mech_type,
+           gssint_release_internal_name(minor_status, union_name->mech_type,
                                        &union_name->mech_name);
        if (union_name->mech_type)
            generic_gss_release_oid(&tmp, &union_name->mech_type);
@@ -158,3 +142,187 @@ allocation_failure:
     }
     return (major_status);
 }
+
+/*
+ * GSS export name constants
+ */
+static const char *expNameTokId = "\x04\x01";
+static const unsigned int expNameTokIdLen = 2;
+static const unsigned int mechOidLenLen = 2;
+static const unsigned int nameTypeLenLen = 2;
+
+static OM_uint32
+importExportName(minor, unionName)
+    OM_uint32 *minor;
+    gss_union_name_t unionName;
+{
+    gss_OID_desc mechOid;
+    gss_buffer_desc expName;
+    unsigned char *buf;
+    gss_mechanism mech;
+    OM_uint32 major, mechOidLen, nameLen, curLength;
+    unsigned int bytes;
+
+    expName.value = unionName->external_name->value;
+    expName.length = unionName->external_name->length;
+
+    curLength = expNameTokIdLen + mechOidLenLen;
+    if (expName.length < curLength)
+       return (GSS_S_DEFECTIVE_TOKEN);
+
+    buf = (unsigned char *)expName.value;
+    if (memcmp(expNameTokId, buf, expNameTokIdLen) != 0)
+       return (GSS_S_DEFECTIVE_TOKEN);
+
+    buf += expNameTokIdLen;
+
+    /* extract the mechanism oid length */
+    mechOidLen = (*buf++ << 8);
+    mechOidLen |= (*buf++);
+    curLength += mechOidLen;
+    if (expName.length < curLength)
+       return (GSS_S_DEFECTIVE_TOKEN);
+    /*
+     * The mechOid itself is encoded in DER format, OID Tag (0x06)
+     * length and the value of mech_OID
+     */
+    if (*buf++ != 0x06)
+       return (GSS_S_DEFECTIVE_TOKEN);
+
+    /*
+     * mechoid Length is encoded twice; once in 2 bytes as
+     * explained in RFC2743 (under mechanism independent exported
+     * name object format) and once using DER encoding
+     *
+     * We verify both lengths.
+     */
+
+    mechOid.length = gssint_get_der_length(&buf,
+                                   (expName.length - curLength), &bytes);
+    mechOid.elements = (void *)buf;
+
+    /*
+     * 'bytes' is the length of the DER length, '1' is for the DER
+     * tag for OID
+     */
+    if ((bytes + mechOid.length + 1) != mechOidLen)
+       return (GSS_S_DEFECTIVE_TOKEN);
+
+    buf += mechOid.length;
+    if ((mech = gssint_get_mechanism(&mechOid)) == NULL)
+       return (GSS_S_BAD_MECH);
+
+    if (mech->gss_import_name == NULL)
+       return (GSS_S_UNAVAILABLE);
+
+    /*
+     * we must now determine if we should unwrap the name ourselves
+     * or make the mechanism do it - we should only unwrap it
+     * if we create it; so if mech->gss_export_name == NULL, we must
+     * have created it.
+     */
+    if (mech->gss_export_name) {
+       if ((major = mech->gss_import_name(mech->context, minor,
+                                          &expName, (gss_OID)GSS_C_NT_EXPORT_NAME,
+                                          &unionName->mech_name)) != GSS_S_COMPLETE ||
+           (major = generic_gss_copy_oid(minor, &mechOid,
+                                         &unionName->mech_type)) !=
+           GSS_S_COMPLETE) {
+           return (major);
+       }
+       return (major);
+    }
+    /*
+     * we must have exported the name - so we now need to reconstruct it
+     * and call the mechanism to create it
+     *
+     * WARNING:        Older versions of gssint_export_internal_name() did
+     *         not export names correctly, but now it does.  In
+     *         order to stay compatible with existing exported
+     *         names we must support names exported the broken
+     *         way.
+     *
+     * Specifically, gssint_export_internal_name() used to include
+     * the name type OID in the encoding of the exported MN.
+     * Additionally, the Kerberos V mech used to make display names
+     * that included a null terminator which was counted in the
+     * display name gss_buffer_desc.
+     */
+    curLength += 4;            /* 4 bytes for name len */
+    if (expName.length < curLength)
+       return (GSS_S_DEFECTIVE_TOKEN);
+
+    /* next 4 bytes in the name are the name length */
+    nameLen = (*buf++) << 24;
+    nameLen |= (*buf++ << 16);
+    nameLen |= (*buf++ << 8);
+    nameLen |= (*buf++);
+
+    /*
+     * we use < here because bad code in rpcsec_gss rounds up exported
+     * name token lengths and pads with nulls, otherwise != would be
+     * appropriate
+     */
+    curLength += nameLen;   /* this is the total length */
+    if (expName.length < curLength)
+       return (GSS_S_DEFECTIVE_TOKEN);
+
+    /*
+     * We detect broken exported names here: they always start with
+     * a two-octet network-byte order OID length, which is always
+     * less than 256 bytes, so the first octet of the length is
+     * always '\0', which is not allowed in GSS-API display names
+     * (or never occurs in them anyways).  Of course, the OID
+     * shouldn't be there, but it is.  After the OID (sans DER tag
+     * and length) there's the name itself, though null-terminated;
+     * this null terminator should also not be there, but it is.
+     */
+    if (nameLen > 0 && *buf == '\0') {
+       OM_uint32 nameTypeLen;
+       /* next two bytes are the name oid */
+       if (nameLen < nameTypeLenLen)
+           return (GSS_S_DEFECTIVE_TOKEN);
+
+       nameLen -= nameTypeLenLen;
+
+       nameTypeLen = (*buf++) << 8;
+       nameTypeLen |= (*buf++);
+
+       if (nameLen < nameTypeLen)
+           return (GSS_S_DEFECTIVE_TOKEN);
+
+       buf += nameTypeLen;
+       nameLen -= nameTypeLen;
+
+       /*
+        * adjust for expected null terminator that should
+        * really not be there
+        */
+       if (nameLen > 0 && *(buf + nameLen - 1) == '\0')
+           nameLen--;
+    }
+
+    /*
+     * Can a name be null?  Let the mech decide.
+     *
+     * NOTE: We use GSS_C_NULL_OID as the name type when importing
+     *  the unwrapped name.  Presumably the exported name had,
+     *  prior to being exported been obtained in such a way
+     *  that it has been properly perpared ("canonicalized," in
+     *  GSS-API terms) accroding to some name type; we cannot
+     *  tell what that name type was now, but the name should
+     *  need no further preparation other than the lowest
+     *  common denominator afforded by the mech to names
+     *  imported with GSS_C_NULL_OID.  For the Kerberos V mech
+     *  this means doing less busywork too (particularly once
+     *  IDN is thrown in with Kerberos V extensions).
+     */
+    expName.length = nameLen;
+    expName.value = nameLen ? (void *)buf : NULL;
+    major = mech->gss_import_name(mech->context, minor, &expName,
+                                 GSS_C_NULL_OID, &unionName->mech_name);
+    if (major != GSS_S_COMPLETE)
+       return (major);
+
+    return (generic_gss_copy_oid(minor, &mechOid, &unionName->mech_type));
+} /* importExportName */
index eed9bbedaf34ffe6ecfe83936f7b8cdeb7af3ce3..533b0175c0f66a43be9621aa6b1cf268d63c1ca5 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)g_imp_sec_context.c 1.2     96/01/18 SMI" */
+/* #pragma ident       "@(#)g_imp_sec_context.c        1.18    04/02/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -44,48 +44,59 @@ gss_buffer_t                interprocess_token;
 gss_ctx_id_t *         context_handle;
 
 {
-    size_t             length;
+    OM_uint32          length = 0;
     OM_uint32          status;
     char               *p;
     gss_union_ctx_id_t ctx;
     gss_buffer_desc    token;
     gss_mechanism      mech;
     
-    gss_initialize();
-
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
     *minor_status = 0;
     
-    if (interprocess_token->length == 0 || interprocess_token->value == 0)
-       return (GSS_S_DEFECTIVE_TOKEN);
+    if (context_handle == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CONTEXT);
+    *context_handle = GSS_C_NO_CONTEXT;
+
+    if (GSS_EMPTY_BUFFER(interprocess_token))
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_DEFECTIVE_TOKEN);
 
     status = GSS_S_FAILURE;
 
     ctx = (gss_union_ctx_id_t) malloc(sizeof(gss_union_ctx_id_desc));
-    if (!ctx) {
-       *minor_status = ENOMEM;
-       goto error_out;
-    }
+    if (!ctx)
+       return (GSS_S_FAILURE);
+
     ctx->mech_type = (gss_OID) malloc(sizeof(gss_OID_desc));
     if (!ctx->mech_type) {
-       *minor_status = ENOMEM;
-       goto error_out;
+       free(ctx);
+       return (GSS_S_FAILURE);
+    }
+
+    if (interprocess_token->length >= sizeof (OM_uint32)) {
+       p = interprocess_token->value;
+       length = (OM_uint32)*p++;
+       length = (OM_uint32)(length << 8) + *p++;
+       length = (OM_uint32)(length << 8) + *p++;
+       length = (OM_uint32)(length << 8) + *p++;
+    }
+
+    if (length == 0 ||
+       length > (interprocess_token->length - sizeof (OM_uint32))) {
+       free(ctx);
+       return (GSS_S_CALL_BAD_STRUCTURE | GSS_S_DEFECTIVE_TOKEN);
     }
-    p = interprocess_token->value;
-    length = *p++;
-    length = (length << 8) + *p++;
-    length = (length << 8) + *p++;
-    length = (length << 8) + *p++;
 
     ctx->mech_type->length = length;
     ctx->mech_type->elements = malloc(length);
     if (!ctx->mech_type->elements) {
-       *minor_status = ENOMEM;
        goto error_out;
     }
     memcpy(ctx->mech_type->elements, p, length);
     p += length;
 
-    token.length = interprocess_token->length - 4 - length;
+    token.length = interprocess_token->length - sizeof (OM_uint32) - length;
     token.value = p;
 
     /*
@@ -93,13 +104,13 @@ gss_ctx_id_t *             context_handle;
      * call it.
      */
     
-    mech = __gss_get_mechanism (ctx->mech_type);
+    mech = gssint_get_mechanism (ctx->mech_type);
     if (!mech) {
        status = GSS_S_BAD_MECH;
        goto error_out;
     }
     if (!mech->gss_import_sec_context) {
-       status = GSS_S_BAD_BINDINGS;
+       status = GSS_S_UNAVAILABLE;
        goto error_out;
     }
     
index 334f7c1b940acb5b7a5125b7f0e26e53f588db29..e2c8d414fea16668df77755517af99fe86d36018 100644 (file)
@@ -33,7 +33,7 @@
 #endif
 #include <string.h>
 
-extern gss_mechanism *__gss_mechs_array;
+extern gss_mechanism *gssint_mechs_array;
 
 static gss_OID_set_desc        supported_mechs_desc; 
 static gss_OID_set supported_mechs = NULL;
@@ -65,7 +65,7 @@ gss_OID_set *         mech_set;
 
        /* Build the mech_set from the OIDs in mechs_array. */
 
-       for(i=0; __gss_mechs_array[i]->mech_type.length != 0; i++) 
+       for(i=0; gssint_mechs_array[i]->mech_type.length != 0; i++) 
            supported_mechs->count++;
 
        supported_mechs->elements =
@@ -74,12 +74,12 @@ gss_OID_set *               mech_set;
 
        for(i=0; i < supported_mechs->count; i++) {
            supported_mechs->elements[i].length =
-               __gss_mechs_array[i]->mech_type.length;
+               gssint_mechs_array[i]->mech_type.length;
            supported_mechs->elements[i].elements = (void *)
-               malloc(__gss_mechs_array[i]->mech_type.length);
+               malloc(gssint_mechs_array[i]->mech_type.length);
            memcpy(supported_mechs->elements[i].elements,
-                  __gss_mechs_array[i]->mech_type.elements,
-                  __gss_mechs_array[i]->mech_type.length);
+                  gssint_mechs_array[i]->mech_type.elements,
+                  gssint_mechs_array[i]->mech_type.length);
        }
     }
     
index 4ff47f89943da66304af65348642fc0361d2ad9d..53252f798a53090fb74736d7bf9a7e15426b37ac 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_init_sec_context.c 1.20     95/08/07 SMI" */
+/* #pragma ident       "@(#)g_init_sec_context.c       1.20    03/10/24 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
 #endif
 #include <string.h>
 
-#define g_OID_equal(o1,o2) \
-   (((o1)->length == (o2)->length) && \
-    (memcmp((o1)->elements,(o2)->elements,(int) (o1)->length) == 0))
-
 OM_uint32 KRB5_CALLCONV
 gss_init_sec_context (minor_status,
                       claimant_cred_handle,
@@ -67,7 +63,7 @@ OM_uint32 *           ret_flags;
 OM_uint32 *            time_rec;
 
 {
-    OM_uint32          status, temp_status, temp_minor_status;
+    OM_uint32          status, temp_minor_status;
     gss_union_name_t   union_name;
     gss_union_cred_t   union_cred;
     gss_name_t         internal_name;
@@ -76,30 +72,48 @@ OM_uint32 *         time_rec;
     gss_mechanism      mech;
     gss_cred_id_t      input_cred_handle;
 
-    gss_initialize();
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
+    output_token->length = 0;
+    output_token->value = NULL;
+
+    /* clear output values */
+    if (actual_mech_type)
+       *actual_mech_type = NULL;
 
     if (context_handle == NULL)
-       return GSS_S_NO_CONTEXT;
+       return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CONTEXT);
 
     union_name = (gss_union_name_t) target_name;
 
-    /*
-     * If mech_type is NULL, and the target_name is
-     * mechanism-specific, then set it to the mech_type of
-     * target_name.
-     */
-    if ((mech_type == GSS_C_NULL_OID) && union_name->mech_type)
-       mech_type = union_name->mech_type;
+    if (target_name == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME);
+
+    if (output_token == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+    output_token->value = NULL;
+    output_token->length = 0;
+
+
+    if (req_mech_type)
+       mech_type = (gss_OID)req_mech_type;
+
+    union_name = (gss_union_name_t)target_name;
     
     /*
      * obtain the gss mechanism information for the requested
      * mechanism.  If mech_type is NULL, set it to the resultant
      * mechanism
      */
-    mech = __gss_get_mechanism (mech_type);
+    mech = gssint_get_mechanism (mech_type);
     if (mech == NULL)
        return (GSS_S_BAD_MECH);
 
+    if (mech->gss_init_sec_context == NULL)
+       return (GSS_S_UNAVAILABLE);
+
     if (mech_type == GSS_C_NULL_OID)
        mech_type = &mech->mech_type;
 
@@ -108,15 +122,14 @@ OM_uint32 *               time_rec;
      * mech_type that we're about to use.  Otherwise, do an import on
      * the external_name form of the target name.
      */
-    if (union_name->mech_type) {
-       if (!g_OID_equal(union_name->mech_type, mech_type))
-           return (GSS_S_BAD_MECH);
+    if (union_name->mech_type &&
+       g_OID_equal(union_name->mech_type, mech_type)) {
        internal_name = union_name->mech_name;
     } else {
-       if ((temp_status = __gss_import_internal_name(minor_status, mech_type,
-                                                     union_name,
-                                                     &internal_name)))
-           return (GSS_S_BAD_NAME);
+       if ((status = gssint_import_internal_name(minor_status, mech_type,
+                                                union_name,
+                                                &internal_name)) != GSS_S_COMPLETE)
+           return (status);
     }
 
     /*
@@ -127,23 +140,22 @@ OM_uint32 *               time_rec;
      */
     
     if(*context_handle == GSS_C_NO_CONTEXT) {
+       status = GSS_S_FAILURE;
        union_ctx_id = (gss_union_ctx_id_t)
            malloc(sizeof(gss_union_ctx_id_desc));
+       if (union_ctx_id == NULL)
+           goto end;
 
        union_ctx_id->mech_type = (gss_OID)
            malloc(sizeof(gss_OID_desc));
 
-       /* copy in the mech type information */
-
-       union_ctx_id->mech_type->elements = (void *)
-           malloc(mech_type->length);
-
-       union_ctx_id->mech_type->length = mech_type->length;
-       memcpy(union_ctx_id->mech_type->elements, mech_type->elements,
-              mech_type->length);
+       if (generic_gss_copy_oid(&temp_minor_status, mech_type,
+                                &union_ctx_id->mech_type) != GSS_S_COMPLETE) {
+           free(union_ctx_id);
+           goto end;
+       }
 
        /* copy the supplied context handle */
-
        union_ctx_id->internal_ctx_id = *context_handle;
     } else
        union_ctx_id = *context_handle;
@@ -154,37 +166,47 @@ OM_uint32 *               time_rec;
      * use the default credential.
      */
     union_cred = (gss_union_cred_t) claimant_cred_handle;
-    input_cred_handle = __gss_get_mechanism_cred(union_cred, mech_type);
+    input_cred_handle = gssint_get_mechanism_cred(union_cred, mech_type);
     
     /*
      * now call the approprate underlying mechanism routine 
      */
     
-    if (mech->gss_init_sec_context) {
-       status = mech->gss_init_sec_context(
-                                           mech->context,
-                                           minor_status,
-                                           input_cred_handle,
-                                           &union_ctx_id->internal_ctx_id,
-                                           internal_name,
-                                           mech_type,
-                                           req_flags,
-                                           time_req,
-                                           input_chan_bindings,
-                                           input_token,
-                                           actual_mech_type,
-                                           output_token,
-                                           ret_flags,
-                                           time_rec);
-
-       if (*context_handle == GSS_C_NO_CONTEXT)
-           *context_handle = (gss_ctx_id_t) union_ctx_id;
-
-    } else
-       status = GSS_S_BAD_BINDINGS;
-
-    if (!union_name->mech_type) {
-       (void) __gss_release_internal_name(&temp_minor_status,
+    status = mech->gss_init_sec_context(
+       mech->context,
+       minor_status,
+       input_cred_handle,
+       &union_ctx_id->internal_ctx_id,
+       internal_name,
+       mech_type,
+       req_flags,
+       time_req,
+       input_chan_bindings,
+       input_token,
+       actual_mech_type,
+       output_token,
+       ret_flags,
+       time_rec);
+
+    if (status != GSS_S_COMPLETE && status != GSS_S_CONTINUE_NEEDED) {
+       /*
+        * the spec says (the preferred) method is to delete all
+        * context info on the first call to init, and on all
+        * subsequent calls make the caller responsible for
+        * calling gss_delete_sec_context
+        */
+       if (*context_handle == GSS_C_NO_CONTEXT) {
+           free(union_ctx_id->mech_type->elements);
+           free(union_ctx_id->mech_type);
+           free(union_ctx_id);
+       }
+    } else if (*context_handle == GSS_C_NO_CONTEXT)
+       *context_handle = (gss_ctx_id_t)union_ctx_id;
+
+end:
+    if (union_name->mech_name == NULL ||
+       union_name->mech_name != internal_name) {
+       (void) gssint_release_internal_name(&temp_minor_status,
                                           mech_type, &internal_name);
     }
 
index 09e14deed11fbdc9a4fcee5b2ca2de086d00c160..21677562756034eff3df5779d3f18cc991fc7287 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)g_initialize.c 1.2     96/02/06 SMI" */
+/* #pragma ident       "@(#)g_initialize.c     1.36    05/02/02 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
 #include <ctype.h>
 #include <errno.h>
 
-#ifdef USE_SOLARIS_SHARED_LIBRARIES
 #include <dlfcn.h>
 
-#define MECH_CONF "/etc/mech.conf"
+#define        MECH_CONF "/etc/gss/mech"
+
+#define        MECH_LIB_PREFIX1        "/usr/lib/"
+
+#define        MECH_LIB_PREFIX2        ""
+
+#define        MECH_LIB_DIR            "gss/"
+
+#define        MECH_LIB_PREFIX MECH_LIB_PREFIX1 MECH_LIB_PREFIX2 MECH_LIB_DIR
+
 #define MECH_SYM "gss_mech_initialize"
 
-static void solaris_initialize (void);
-#endif /* USE_SOLARIS_SHARED_LIBRARIES */
+#define        M_DEFAULT       "default"
+
+#include <sys/stat.h>
+
+#include "k5-thread.h"
+
+/* Local functions */
+static gss_mech_info searchMechList(const gss_OID);
+static void loadConfigFile(const char *);
+static void updateMechList(void);
+static void register_mech(gss_mechanism, const char *, void *);
+
+static OM_uint32 build_mechSet(void);
+static void init_hardcoded(void);
+
+/*
+ * list of mechanism libraries and their entry points.
+ * the list also maintains state of the mech libraries (loaded or not).
+ */
+static gss_mech_info g_mechList = NULL;
+static gss_mech_info g_mechListTail = NULL;
+static k5_mutex_t g_mechListLock = K5_MUTEX_PARTIAL_INITIALIZER;
+static time_t g_confFileModTime = (time_t)0;
+
+static time_t g_mechSetTime = (time_t)0;
+static gss_OID_set_desc g_mechSet = { 0, NULL };
+static k5_mutex_t g_mechSetLock = K5_MUTEX_PARTIAL_INITIALIZER;
+
+int
+gssint_mechglue_init(void)
+{
+       int err;
 
-#define g_OID_equal(o1,o2) \
-   (((o1)->length == (o2)->length) && \
-    (memcmp((o1)->elements,(o2)->elements,(int) (o1)->length) == 0))
+       err = k5_mutex_finish_init(&g_mechSetLock);
+       return k5_mutex_finish_init(&g_mechListLock);
+}
 
-extern gss_mechanism krb5_gss_initialize();
+void
+gssint_mechglue_fini(void)
+{
+       k5_mutex_destroy(&g_mechSetLock);
+       k5_mutex_destroy(&g_mechListLock);
+}
 
-static int _gss_initialized = 0;
 
-static struct gss_config null_mech = {
-  {0,NULL}};
+/*
+ * function used to reclaim the memory used by a gss_OID structure.
+ * This routine requires direct access to the mechList.
+ */
+OM_uint32 KRB5_CALLCONV
+gss_release_oid(minor_status, oid)
+OM_uint32 *minor_status;
+gss_OID *oid;
+{
+       OM_uint32 major;
+       gss_mech_info aMech;
+
+       if (minor_status == NULL)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+       *minor_status = 0;
+
+       k5_mutex_lock(&g_mechListLock);
+       aMech = g_mechList;
+       while (aMech != NULL) {
+
+               /*
+                * look through the loaded mechanism libraries for
+                * gss_internal_release_oid until one returns success.
+                * gss_internal_release_oid will only return success when
+                * the OID was recognized as an internal mechanism OID. if no
+                * mechanisms recognize the OID, then call the generic version.
+                */
+               if (aMech->mech && aMech->mech->gss_internal_release_oid) {
+                       major = aMech->mech->gss_internal_release_oid(
+                                       aMech->mech->context,
+                                       minor_status, oid);
+                       if (major == GSS_S_COMPLETE) {
+                               k5_mutex_unlock(&g_mechListLock);
+                               return (GSS_S_COMPLETE);
+                       }
+               }
+               aMech = aMech->next;
+       } /* while */
+       k5_mutex_unlock(&g_mechListLock);
+
+       return (generic_gss_release_oid(minor_status, oid));
+} /* gss_release_oid */
 
-gss_mechanism *__gss_mechs_array = NULL;
 
 /*
- * This function will add a new mechanism to the mechs_array
+ * this function will return an oid set indicating available mechanisms.
+ * The set returned is based on configuration file entries and
+ * NOT on the loaded mechanisms.  This function does not check if any
+ * of these can actually be loaded.
+ * This routine needs direct access to the mechanism list.
+ * To avoid reading the configuration file each call, we will save a
+ * a mech oid set, and only update it once the file has changed.
  */
+OM_uint32 KRB5_CALLCONV
+gss_indicate_mechs(minorStatus, mechSet)
+OM_uint32 *minorStatus;
+gss_OID_set *mechSet;
+{
+       char *fileName;
+       struct stat fileInfo;
+       int i, j;
+       gss_OID curItem;
+
+       if (!minorStatus)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
+       if (gssint_initialize_library())
+               return GSS_S_FAILURE;
+
+       *minorStatus = 0;
+
+
+       /* check output parameter */
+       if (mechSet == NULL)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+       fileName = MECH_CONF;
+
+#if 0
+       /*
+        * If we have already computed the mechanisms supported and if it
+        * is still valid; make a copy and return to caller,
+        * otherwise build it first.
+        */
+       if ((stat(fileName, &fileInfo) == 0 &&
+               fileInfo.st_mtime > g_mechSetTime)) {
+       } /* if g_mechSet is out of date or not initialized */
+#endif
+       if (build_mechSet())
+               return GSS_S_FAILURE;
+
+       /*
+        * the mech set is created and it is up to date
+        * so just copy it to caller
+        */
+       if ((*mechSet =
+               (gss_OID_set) malloc(sizeof (gss_OID_set_desc))) == NULL)
+       {
+               return (GSS_S_FAILURE);
+       }
+
+       /*
+        * need to lock the g_mechSet in case someone tries to update it while
+        * I'm copying it.
+        */
+       (void) k5_mutex_lock(&g_mechSetLock);
+
+       /* allocate space for the oid structures */
+       if (((*mechSet)->elements =
+               (void*) calloc(g_mechSet.count, sizeof (gss_OID_desc)))
+               == NULL)
+       {
+               (void) k5_mutex_unlock(&g_mechSetLock);
+               free(*mechSet);
+               *mechSet = NULL;
+               return (GSS_S_FAILURE);
+       }
+
+       /* now copy the oid structures */
+       (void) memcpy((*mechSet)->elements, g_mechSet.elements,
+               g_mechSet.count * sizeof (gss_OID_desc));
+
+       (*mechSet)->count = g_mechSet.count;
+
+       /* still need to copy each of the oid elements arrays */
+       for (i = 0; i < (*mechSet)->count; i++) {
+               curItem = &((*mechSet)->elements[i]);
+               curItem->elements =
+                       (void *) malloc(g_mechSet.elements[i].length);
+               if (curItem->elements == NULL) {
+                       (void) k5_mutex_unlock(&g_mechSetLock);
+                       /*
+                        * must still free the allocated elements for
+                        * each allocated gss_OID_desc
+                        */
+                       for (j = 0; j < i; j++) {
+                               free((*mechSet)->elements[j].elements);
+                       }
+                       free((*mechSet)->elements);
+                       free(mechSet);
+                       *mechSet = NULL;
+                       return (GSS_S_FAILURE);
+               }
+               g_OID_copy(curItem, &g_mechSet.elements[i]);
+       }
+       (void) k5_mutex_unlock(&g_mechSetLock);
+       return (GSS_S_COMPLETE);
+} /* gss_indicate_mechs */
+
 
 static OM_uint32
-add_mechanism (mech, replace)
-     gss_mechanism mech;
-     int replace;
+build_mechSet(void)
 {
-    gss_mechanism *    temp_array;
-    gss_OID_set                mech_names;
-    OM_uint32          minor_status, major_status;
-    unsigned int       i;
+       gss_mech_info mList;
+       int i, count;
+       gss_OID curItem;
+
+       /*
+        * lock the mutex since we will be updating
+        * the mechList structure
+        * we need to keep the lock while we build the mechanism list
+        * since we are accessing parts of the mechList which could be
+        * modified.
+        */
+       (void) k5_mutex_lock(&g_mechListLock);
+
+#if 0
+       /*
+        * this checks for the case when we need to re-construct the
+        * g_mechSet structure, but the mechanism list is upto date
+        * (because it has been read by someone calling
+        * gssint_get_mechanism)
+        */
+       if (fileInfo.st_mtime > g_confFileModTime)
+       {
+               g_confFileModTime = fileInfo.st_mtime;
+               loadConfigFile(fileName);
+       }
+#endif
+
+       updateMechList();
+
+       /*
+        * we need to lock the mech set so that no one else will
+        * try to read it as we are re-creating it
+        */
+       (void) k5_mutex_lock(&g_mechSetLock);
+
+       /* if the oid list already exists we must free it first */
+       if (g_mechSet.count != 0) {
+               for (i = 0; i < g_mechSet.count; i++)
+                       free(g_mechSet.elements[i].elements);
+               free(g_mechSet.elements);
+               g_mechSet.elements = NULL;
+               g_mechSet.count = 0;
+       }
+
+       /* determine how many elements to have in the list */
+       mList = g_mechList;
+       count = 0;
+       while (mList != NULL) {
+               count++;
+               mList = mList->next;
+       }
+
+       /* this should always be true, but.... */
+       if (count > 0) {
+               g_mechSet.elements =
+                       (gss_OID) calloc(count, sizeof (gss_OID_desc));
+               if (g_mechSet.elements == NULL) {
+                       (void) k5_mutex_unlock(&g_mechSetLock);
+                       (void) k5_mutex_unlock(&g_mechListLock);
+                       return (GSS_S_FAILURE);
+               }
+
+               (void) memset(g_mechSet.elements, 0,
+                             count * sizeof (gss_OID_desc));
+
+               /* now copy each oid element */
+               g_mechSet.count = count;
+               count = 0;
+               mList = g_mechList;
+               while (mList != NULL) {
+                       curItem = &(g_mechSet.elements[count]);
+                       curItem->elements = (void*)
+                               malloc(mList->mech_type->length);
+                       if (curItem->elements == NULL) {
+                               /*
+                                * this is nasty - we must delete the
+                                * part of the array already copied
+                                */
+                               for (i = 0; i < count; i++) {
+                                       free(g_mechSet.elements[i].
+                                            elements);
+                               }
+                               free(g_mechSet.elements);
+                               g_mechSet.count = 0;
+                               g_mechSet.elements = NULL;
+                               (void) k5_mutex_unlock(&g_mechSetLock);
+                               (void) k5_mutex_unlock(&g_mechListLock);
+                               return (GSS_S_FAILURE);
+                       }
+                       g_OID_copy(curItem, mList->mech_type);
+                       count++;
+                       mList = mList->next;
+               }
+       }
+
+#if 0
+       g_mechSetTime = fileInfo.st_mtime;
+#endif
+       (void) k5_mutex_unlock(&g_mechSetLock);
+       (void) k5_mutex_unlock(&g_mechListLock);
 
-    if (mech == NULL)
        return GSS_S_COMPLETE;
+}
 
-    /* initialize the mechs_array if it hasn't already been initialized */
-    if (__gss_mechs_array == NULL) {
-       __gss_mechs_array = (gss_mechanism *) malloc (sizeof(gss_mechanism));
 
-       if (__gss_mechs_array == NULL)
-           return ENOMEM;
+/*
+ * this function has been added for use by modules that need to
+ * know what (if any) optional parameters are supplied in the
+ * config file (MECH_CONF).
+ * It will return the option string for a specified mechanism.
+ * caller is responsible for freeing the memory
+ */
+char *
+gssint_get_modOptions(oid)
+const gss_OID oid;
+{
+       gss_mech_info aMech;
+       char *modOptions = NULL;
 
-       __gss_mechs_array[0] = &null_mech;
-    }
+       /* make sure we have fresh data */
+       (void) k5_mutex_lock(&g_mechListLock);
+       updateMechList();
 
-    /* 
-     * Find the length of __gss_mechs_array, and look for an existing
-     * entry for this OID
-     */
-    for (i=0; __gss_mechs_array[i]->mech_type.length != 0; i++) {
-       if (!g_OID_equal(&__gss_mechs_array[i]->mech_type,
-                        &mech->mech_type))
-           continue;
+       if ((aMech = searchMechList(oid)) == NULL ||
+               aMech->optionStr == NULL) {
+               (void) k5_mutex_unlock(&g_mechListLock);
+               return (NULL);
+       }
 
-       /* We found a match.  Replace it? */
-       if (!replace)
-           return GSS_S_FAILURE;
+       if (aMech->optionStr)
+               modOptions = strdup(aMech->optionStr);
+       (void) k5_mutex_unlock(&g_mechListLock);
 
-       __gss_mechs_array[i] = mech;
-       return GSS_S_COMPLETE;
-    }
+       return (modOptions);
+} /* gssint_get_modOptions */
 
-    /* we didn't find it -- add it to the end of the __gss_mechs_array */
-    temp_array = (gss_mechanism *) realloc(__gss_mechs_array,
-                                          (i+2)*sizeof(gss_mechanism));
+/*
+ * given a mechanism string return the mechanism oid
+ */
+OM_uint32
+gssint_mech_to_oid(const char *mechStr, gss_OID* oid)
+{
+       gss_mech_info aMech;
 
-    if (temp_array == NULL)
-       return ENOMEM;
+       if (oid == NULL)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
 
-    temp_array[i++] = mech;
-    temp_array[i] = &null_mech;
+       *oid = GSS_C_NULL_OID;
 
-    __gss_mechs_array = temp_array;
+       if ((mechStr == NULL) || (strlen(mechStr) == 0) ||
+               (strcasecmp(mechStr, M_DEFAULT) == 0))
+               return (GSS_S_COMPLETE);
 
-    /*
-     * OK, now let's register all of the name types this mechanism
-     * knows how to deal with.
-     */
-    major_status = gss_inquire_names_for_mech(&minor_status, &mech->mech_type,
-                                             &mech_names);
-    if (major_status != GSS_S_COMPLETE)
-       return (GSS_S_COMPLETE);
-    for (i=0; i < mech_names->count; i++) {
-       gss_add_mech_name_type(&minor_status, &mech_names->elements[i],
-                              &mech->mech_type);
-    }
-    (void) gss_release_oid_set(&minor_status, &mech_names);
+       /* ensure we have fresh data */
+       (void) k5_mutex_lock(&g_mechListLock);
+       updateMechList();
+       (void) k5_mutex_unlock(&g_mechListLock);
+
+       aMech = g_mechList;
+
+       /* no lock required - only looking at fields that are not updated */
+       while (aMech != NULL) {
+               if ((aMech->mechNameStr) &&
+                       strcmp(aMech->mechNameStr, mechStr) == 0) {
+                       *oid = aMech->mech_type;
+                       return (GSS_S_COMPLETE);
+               }
+               aMech = aMech->next;
+       }
+       return (GSS_S_FAILURE);
+} /* gssint_mech_to_oid */
 
-    return GSS_S_COMPLETE;
-}
 
-void gss_initialize ()
+/*
+ * Given the mechanism oid, return the readable mechanism name
+ * associated with that oid from the mech config file
+ * (/etc/gss/mech).
+ */
+const char *
+gssint_oid_to_mech(const gss_OID oid)
 {
-    gss_mechanism mech;
+       gss_mech_info aMech;
 
-    /* Make sure we've not run already */
-    if (_gss_initialized)
-       return;
-    _gss_initialized = 1;
+       if (oid == GSS_C_NULL_OID)
+               return (M_DEFAULT);
 
-#ifdef USE_SOLARIS_SHARED_LIBRARIES
-    solaris_initialize();
+       /* ensure we have fresh data */
+       (void) k5_mutex_lock(&g_mechListLock);
+       updateMechList();
+       aMech = searchMechList(oid);
+       (void) k5_mutex_unlock(&g_mechListLock);
 
-#else
-    /* 
-     * Use hard-coded in mechanisms...  I need to know what mechanisms
-     * are supported...  As more mechanisms become supported, they
-     * should be added here, unless shared libraries are used.
-     */
+       if (aMech == NULL)
+               return (NULL);
 
-    /* Initialize the krb5 mechanism */
-    mech = (gss_mechanism)krb5_gss_initialize();
-    if (mech)
-       add_mechanism (mech, 1);
+       return (aMech->mechNameStr);
+} /* gssint_oid_to_mech */
 
-#endif /* USE_SOLARIS_SHARED_LIBRARIES */
 
-    if (__gss_mechs_array == NULL) { /* this is very bad! */
-      fprintf(stderr,"gss_initialize fatal error: no mechanisms loaded!\n");
-      exit(-1);
-    }
+/*
+ * return a list of mechanism strings supported
+ * upon return the array is terminated with a NULL entry
+ */
+OM_uint32
+gssint_get_mechanisms(char *mechArray[], int arrayLen)
+{
+       gss_mech_info aMech;
+       int i;
+
+       if (gssint_initialize_library())
+               return GSS_S_FAILURE;
+       if (mechArray == NULL || arrayLen < 1)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+       /* ensure we have fresh data */
+       (void) k5_mutex_lock(&g_mechListLock);
+       updateMechList();
+       (void) k5_mutex_unlock(&g_mechListLock);
+
+       aMech = g_mechList;
+
+       /* no lock required - only looking at fields that are not updated */
+       for (i = 1; i < arrayLen; i++) {
+               if (aMech != NULL) {
+                       *mechArray = aMech->mechNameStr;
+                       mechArray++;
+                       aMech = aMech->next;
+               } else
+                       break;
+       }
+       *mechArray = NULL;
+       return (GSS_S_COMPLETE);
+} /* gss_get_mechanisms */
 
-    return;
+
+/*
+ * determines if the mechList needs to be updated from file
+ * and performs the update.
+ * this functions must be called with a lock of g_mechListLock
+ */
+static void
+updateMechList(void)
+{
+       char *fileName;
+       struct stat fileInfo;
+
+       init_hardcoded();
+       fileName = MECH_CONF;
+
+#if 0
+       /* check if mechList needs updating */
+       if (stat(fileName, &fileInfo) == 0 &&
+               (fileInfo.st_mtime > g_confFileModTime)) {
+               loadConfigFile(fileName);
+               g_confFileModTime = fileInfo.st_mtime;
+       }
+#endif
+} /* updateMechList */
+
+/*
+ * Register a mechanism.  Called with g_mechListLock held.
+ */
+static void
+register_mech(gss_mechanism mech, const char *namestr, void *dl_handle)
+{
+       gss_mech_info cf, new_cf;
+
+       new_cf = malloc(sizeof(*new_cf));
+       if (new_cf == NULL)
+               return;
+
+       memset(new_cf, 0, sizeof(*new_cf));
+       new_cf->kmodName = NULL;
+       new_cf->uLibName = strdup(namestr);
+       new_cf->mechNameStr = strdup(mech->mechNameStr);
+       new_cf->mech_type = &mech->mech_type;
+       new_cf->mech = mech;
+       new_cf->next = NULL;
+
+       if (g_mechList == NULL) {
+               g_mechList = new_cf;
+               g_mechListTail = new_cf;
+               return;
+       } else if (mech->priority < g_mechList->mech->priority) {
+               new_cf->next = g_mechList;
+               g_mechList = new_cf;
+               return;
+       }
+       for (cf = g_mechList; cf != NULL; cf = cf->next) {
+               if (cf->next == NULL ||
+                   mech->priority < cf->next->mech->priority) {
+                       new_cf->next = cf->next;
+                       cf->next = new_cf;
+                       if (g_mechListTail == cf) {
+                               g_mechListTail = new_cf;
+                       }
+                       break;
+               }
+       }
 }
 
-#ifdef USE_SOLARIS_SHARED_LIBRARIES
-/* 
- * read the configuration file to find out what mechanisms to
- * load, load them, and then load the mechanism defitions in
- * and add the mechanisms
+/*
+ * Initialize the hardcoded mechanisms.  This function is called with
+ * g_mechListLock held.
  */
-static void solaris_initialize ()
+static void
+init_hardcoded(void)
 {
-    char buffer[BUFSIZ], *filename, *symname, *endp;
-    FILE *conffile;
-    void *dl;
-    gss_mechanism (*sym)(void), mech;
+       extern gss_mechanism *krb5_gss_get_mech_configs(void);
+       extern gss_mechanism *spnego_gss_get_mech_configs(void);
+       gss_mechanism *cflist;
+       static int inited;
+       gss_mech_info cf;
+
+       if (inited)
+               return;
+
+       cflist = krb5_gss_get_mech_configs();
+       if (cflist == NULL)
+               return;
+       for ( ; *cflist != NULL; cflist++) {
+               register_mech(*cflist, "<builtin krb5>", NULL);
+       }
+       cflist = spnego_gss_get_mech_configs();
+       if (cflist == NULL)
+               return;
+       for ( ; *cflist != NULL; cflist++) {
+               register_mech(*cflist, "<builtin spnego>", NULL);
+       }
+       inited = 1;
+}
 
-    if ((filename = getenv("GSSAPI_MECH_CONF")) == NULL)
-       filename = MECH_CONF;
 
-    if ((conffile = fopen(filename, "r")) == NULL)
-       return;
+/*
+ * given the mechanism type, return the mechanism structure
+ * containing the mechanism library entry points.
+ * will return NULL if mech type is not found
+ * This function will also trigger the loading of the mechanism
+ * module if it has not been already loaded.
+ */
+gss_mechanism
+gssint_get_mechanism(oid)
+const gss_OID oid;
+{
+       gss_mech_info aMech;
+       gss_mechanism (*sym)(const gss_OID);
+       void *dl;
+
+       if (gssint_initialize_library())
+               return NULL;
+
+       (void) k5_mutex_lock(&g_mechListLock);
+       /* check if the mechanism is already loaded */
+       if ((aMech = searchMechList(oid)) != NULL && aMech->mech) {
+               (void) k5_mutex_unlock(&g_mechListLock);
+               return (aMech->mech);
+       }
 
-    while (fgets (buffer, BUFSIZ, conffile) != NULL) {
-       /* ignore lines beginning with # */
-       if (*buffer == '#')
-           continue;
+       /*
+        * might need to re-read the configuration file before loading
+        * the mechanism to ensure we have the latest info.
+        */
+       updateMechList();
 
-       /* find the first white-space character after the filename */
-       for (symname = buffer; *symname && !isspace(*symname); symname++);
+       aMech = searchMechList(oid);
 
-       /* Now find the first non-white-space character */
-       if (*symname) {
-           *symname = '\0';
-           symname++;
-           while (*symname && isspace(*symname))
-               symname++;
+       /* is the mechanism present in the list ? */
+       if (aMech == NULL) {
+               (void) k5_mutex_unlock(&g_mechListLock);
+               return ((gss_mechanism)NULL);
        }
 
-       if (! *symname)
-           symname = MECH_SYM;
-       else {
-         /* Find the end of the symname and make sure it is NULL-terminated */
-         for (endp = symname; *endp && !isspace(*endp); endp++);
-         if (*endp)
-           *endp = '\0';
+       /* has another thread loaded the mech */
+       if (aMech->mech) {
+               (void) k5_mutex_unlock(&g_mechListLock);
+               return (aMech->mech);
        }
 
-       if ((dl = dlopen(buffer, RTLD_NOW)) == NULL) {
-               /* for debugging only */
-               fprintf(stderr,"can't open %s: %s\n",buffer, dlerror());
-               continue;
+       /* we found the mechanism, but it is not loaded */
+       if ((dl = dlopen(aMech->uLibName, RTLD_NOW)) == NULL) {
+#if 0
+               (void) syslog(LOG_INFO, "libgss dlopen(%s): %s\n",
+                               aMech->uLibName, dlerror());
+#endif
+               (void) k5_mutex_unlock(&g_mechListLock);
+               return ((gss_mechanism)NULL);
        }
 
-       if ((sym = (gss_mechanism (*)(void))dlsym(dl, symname)) == NULL) {
-           dlclose(dl);
-           continue;
+       if ((sym = (gss_mechanism (*)(const gss_OID))dlsym(dl, MECH_SYM))
+                       == NULL) {
+               (void) dlclose(dl);
+#if 0
+               (void) syslog(LOG_INFO, "unable to initialize mechanism"
+                               " library [%s]\n", aMech->uLibName);
+#endif
+               (void) k5_mutex_unlock(&g_mechListLock);
+               return ((gss_mechanism)NULL);
        }
 
        /* Call the symbol to get the mechanism table */
-       mech = sym();
+       aMech->mech = (*sym)(aMech->mech_type);
+
+       if (aMech->mech == NULL) {
+               (void) dlclose(dl);
+#if 0
+               (void) syslog(LOG_INFO, "unable to initialize mechanism"
+                               " library [%s]\n", aMech->uLibName);
+#endif
+               (void) k5_mutex_unlock(&g_mechListLock);
+               return ((gss_mechanism)NULL);
+       }
+
+       aMech->dl_handle = dl;
+
+       (void) k5_mutex_unlock(&g_mechListLock);
+       return (aMech->mech);
+} /* gssint_get_mechanism */
 
-       /* And add the mechanism (or close the shared library) */
-       if (mech)
-           add_mechanism (mech, 1);
+gss_mechanism_ext
+gssint_get_mechanism_ext(oid)
+const gss_OID oid;
+{
+       gss_mech_info aMech;
+       gss_mechanism_ext mech_ext;
+
+       /* check if the mechanism is already loaded */
+       if ((aMech = searchMechList(oid)) != NULL && aMech->mech_ext != NULL)
+               return (aMech->mech_ext);
+
+       if (gssint_get_mechanism(oid) == NULL)
+               return (NULL);
+
+       if (aMech->dl_handle == NULL)
+               return (NULL);
+
+       /* Load the gss_config_ext struct for this mech */
+
+       mech_ext = (gss_mechanism_ext)malloc(sizeof (struct gss_config_ext));
+
+       if (mech_ext == NULL)
+               return (NULL);
+
+       /*
+        * dlsym() the mech's 'method' functions for the extended APIs
+        *
+        * NOTE:  Until the void *context argument is removed from the
+        * SPI method functions' signatures it will be necessary to have
+        * different function pointer typedefs and function names for
+        * the SPI methods than for the API.  When this argument is
+        * removed it will be possible to rename gss_*_sfct to gss_*_fct
+        * and and gssspi_* to gss_*.
+        */
+       mech_ext->gss_acquire_cred_with_password =
+               (gss_acquire_cred_with_password_sfct)dlsym(aMech->dl_handle,
+                       "gssspi_acquire_cred_with_password");
+
+       /* Set aMech->mech_ext */
+       (void) k5_mutex_lock(&g_mechListLock);
+
+       if (aMech->mech_ext == NULL)
+               aMech->mech_ext = mech_ext;
        else
-           dlclose(dl);
+               free(mech_ext); /* we raced and lost; don't leak */
 
-    } /* while */
+       (void) k5_mutex_unlock(&g_mechListLock);
 
-    return;
-}
-#endif /* USE_SOLARIS_SHARED_LIBRARIES */
+       return (aMech->mech_ext);
+
+} /* gssint_get_mechanism_ext */
+
+
+/*
+ * this routine is used for searching the list of mechanism data.
+ *
+ * this needs to be called with g_mechListLock held.
+ */
+static gss_mech_info searchMechList(oid)
+const gss_OID oid;
+{
+       gss_mech_info aMech = g_mechList;
+
+       /* if oid is null -> then get default which is the first in the list */
+       if (oid == GSS_C_NULL_OID)
+               return (aMech);
+
+       while (aMech != NULL) {
+               if (g_OID_equal(aMech->mech_type, oid))
+                       return (aMech);
+               aMech = aMech->next;
+       }
+
+       /* none found */
+       return ((gss_mech_info) NULL);
+} /* searchMechList */
+
+
+/*
+ * loads the configuration file
+ * this is called while having a mutex lock on the mechanism list
+ * entries for libraries that have been loaded can't be modified
+ * mechNameStr and mech_type fields are not updated during updates
+ */
+static void loadConfigFile(fileName)
+const char *fileName;
+{
+       char buffer[BUFSIZ], *oidStr, *oid, *sharedLib, *kernMod, *endp;
+       char *modOptions;
+       char sharedPath[sizeof (MECH_LIB_PREFIX) + BUFSIZ];
+       char *tmpStr;
+       FILE *confFile;
+       gss_OID mechOid;
+       gss_mech_info aMech, tmp;
+       OM_uint32 minor;
+       gss_buffer_desc oidBuf;
+
+       if ((confFile = fopen(fileName, "r")) == NULL) {
+               return;
+       }
+
+       (void) memset(buffer, 0, sizeof (buffer));
+       while (fgets(buffer, BUFSIZ, confFile) != NULL) {
+
+               /* ignore lines beginning with # */
+               if (*buffer == '#')
+                       continue;
+
+               /*
+                * find the first white-space character after
+                * the mechanism name
+                */
+               oidStr = buffer;
+               for (oid = buffer; *oid && !isspace(*oid); oid++);
+
+               /* Now find the first non-white-space character */
+               if (*oid) {
+                       *oid = '\0';
+                       oid++;
+                       while (*oid && isspace(*oid))
+                               oid++;
+               }
+
+               /*
+                * If that's all, then this is a corrupt entry. Skip it.
+                */
+               if (! *oid)
+                       continue;
+
+               /* Find the end of the oid and make sure it is NULL-ended */
+               for (endp = oid; *endp && !isspace(*endp); endp++)
+                       ;
+
+               if (*endp) {
+                       *endp = '\0';
+               }
+
+               /*
+                * check if an entry for this oid already exists
+                * if it does, and the library is already loaded then
+                * we can't modify it, so skip it
+                */
+               oidBuf.value = (void *)oid;
+               oidBuf.length = strlen(oid);
+               if (generic_gss_str_to_oid(&minor, &oidBuf, &mechOid)
+                       != GSS_S_COMPLETE) {
+#if 0
+                       (void) syslog(LOG_INFO, "invalid mechanism oid"
+                                       " [%s] in configuration file", oid);
+#endif
+                       continue;
+               }
+
+               k5_mutex_lock(&g_mechListLock);
+               aMech = searchMechList(mechOid);
+               if (aMech && aMech->mech) {
+                       free(mechOid->elements);
+                       free(mechOid);
+                       k5_mutex_unlock(&g_mechListLock);
+                       continue;
+               }
+               k5_mutex_unlock(&g_mechListLock);
+
+               /* Find the start of the shared lib name */
+               for (sharedLib = endp+1; *sharedLib && isspace(*sharedLib);
+                       sharedLib++)
+                       ;
+
+               /*
+                * If that's all, then this is a corrupt entry. Skip it.
+                */
+               if (! *sharedLib) {
+                       free(mechOid->elements);
+                       free(mechOid);
+                       continue;
+               }
+
+               /*
+                * Find the end of the shared lib name and make sure it is
+                *  NULL-terminated.
+                */
+               for (endp = sharedLib; *endp && !isspace(*endp); endp++)
+                       ;
+
+               if (*endp) {
+                       *endp = '\0';
+               }
+
+               /* Find the start of the optional kernel module lib name */
+               for (kernMod = endp+1; *kernMod && isspace(*kernMod);
+                       kernMod++)
+                       ;
+
+               /*
+                * If this item starts with a bracket "[", then
+                * it is not a kernel module, but is a list of
+                * options for the user module to parse later.
+                */
+               if (*kernMod && *kernMod != '[') {
+                       /*
+                        * Find the end of the shared lib name and make sure
+                        * it is NULL-terminated.
+                        */
+                       for (endp = kernMod; *endp && !isspace(*endp); endp++)
+                               ;
+
+                       if (*endp) {
+                               *endp = '\0';
+                       }
+               } else
+                       kernMod = NULL;
+
+               /* Find the start of the optional module options list */
+               for (modOptions = endp+1; *modOptions && isspace(*modOptions);
+                       modOptions++);
+
+               if (*modOptions == '[')  {
+                       /* move past the opening bracket */
+                       for (modOptions = modOptions+1;
+                           *modOptions && isspace(*modOptions);
+                           modOptions++);
+
+                       /* Find the closing bracket */
+                       for (endp = modOptions;
+                               *endp && *endp != ']'; endp++);
+
+                       if (endp)
+                               *endp = '\0';
+
+               } else {
+                       modOptions = NULL;
+               }
+
+               (void) strcpy(sharedPath, MECH_LIB_PREFIX);
+               (void) strcat(sharedPath, sharedLib);
+
+               /*
+                * are we creating a new mechanism entry or
+                * just modifying existing (non loaded) mechanism entry
+                */
+               if (aMech) {
+                       /*
+                        * delete any old values and set new
+                        * mechNameStr and mech_type are not modified
+                        */
+                       if (aMech->kmodName) {
+                               free(aMech->kmodName);
+                               aMech->kmodName = NULL;
+                       }
+
+                       if (aMech->optionStr) {
+                               free(aMech->optionStr);
+                               aMech->optionStr = NULL;
+                       }
+
+                       if ((tmpStr = strdup(sharedPath)) != NULL) {
+                               if (aMech->uLibName)
+                                       free(aMech->uLibName);
+                               aMech->uLibName = tmpStr;
+                       }
+
+                       if (kernMod) /* this is an optional parameter */
+                               aMech->kmodName = strdup(kernMod);
+
+                       if (modOptions) /* optional module options */
+                               aMech->optionStr = strdup(modOptions);
+
+                       /* the oid is already set */
+                       free(mechOid->elements);
+                       free(mechOid);
+                       continue;
+               }
+
+               /* adding a new entry */
+               aMech = malloc(sizeof (struct gss_mech_config));
+               if (aMech == NULL) {
+                       free(mechOid->elements);
+                       free(mechOid);
+                       continue;
+               }
+               (void) memset(aMech, 0, sizeof (struct gss_mech_config));
+               aMech->mech_type = mechOid;
+               aMech->uLibName = strdup(sharedPath);
+               aMech->mechNameStr = strdup(oidStr);
+
+               /* check if any memory allocations failed - bad news */
+               if (aMech->uLibName == NULL || aMech->mechNameStr == NULL) {
+                       if (aMech->uLibName)
+                               free(aMech->uLibName);
+                       if (aMech->mechNameStr)
+                               free(aMech->mechNameStr);
+                       free(mechOid->elements);
+                       free(mechOid);
+                       free(aMech);
+                       continue;
+               }
+               if (kernMod)    /* this is an optional parameter */
+                       aMech->kmodName = strdup(kernMod);
+
+               if (modOptions)
+                       aMech->optionStr = strdup(modOptions);
+               /*
+                * add the new entry to the end of the list - make sure
+                * that only complete entries are added because other
+                * threads might currently be searching the list.
+                */
+               tmp = g_mechListTail;
+               g_mechListTail = aMech;
+
+               if (tmp != NULL)
+                       tmp->next = aMech;
+
+               if (g_mechList == NULL)
+                       g_mechList = aMech;
+       } /* while */
+       (void) fclose(confFile);
+} /* loadConfigFile */
index 3f28c484fca8f49170a0af9102d04c53cd5ff2ea..e717aa3473b5c93fcb0c6469c482b8a9a9992c6f 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident      "@(#)g_inquire_context.c 1.2     96/01/18 SMI" */
+/* #pragma ident       "@(#)g_inquire_context.c        1.15    04/02/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -59,13 +59,26 @@ int *               open;
     gss_union_ctx_id_t ctx;
     gss_mechanism      mech;
     OM_uint32          status, temp_minor;
+    gss_name_t localTargName = NULL, localSourceName = NULL;
     
-    gss_initialize();
+    if (!minor_status)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
 
-    /* if the context_handle is Null, return NO_CONTEXT error */
+    *minor_status = 0;
     
-    if(context_handle == GSS_C_NO_CONTEXT)
-       return(GSS_S_NO_CONTEXT);
+    /* if the context_handle is Null, return NO_CONTEXT error */
+    if (context_handle == GSS_C_NO_CONTEXT)
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT);
+
+    /* set all output value to NULL */
+    if (src_name)
+       *src_name = NULL;
+
+    if (targ_name)
+       *targ_name = NULL;
+
+    if (mech_type)
+       *mech_type = NULL;
     
     /*
      * select the approprate underlying mechanism routine and
@@ -73,21 +86,21 @@ int *               open;
      */
     
     ctx = (gss_union_ctx_id_t) context_handle;
-    mech = __gss_get_mechanism (ctx->mech_type);
+    mech = gssint_get_mechanism (ctx->mech_type);
     
-    if (!mech || !mech->gss_inquire_context || !mech->gss_display_name) {
-       return(GSS_S_NO_CONTEXT);
-
+    if (!mech || !mech->gss_inquire_context || !mech->gss_display_name ||
+       !mech->gss_release_name) {
+       return (GSS_S_UNAVAILABLE);
     }
 
     status = mech->gss_inquire_context(
                        mech->context,
                        minor_status,
                        ctx->internal_ctx_id,
-                       src_name,
-                       targ_name,
+                       (src_name ? &localSourceName : NULL),
+                       (targ_name ? &localTargName : NULL),
                        lifetime_rec,
-                       mech_type,
+                       NULL,
                        ctx_flags,
                        locally_initiated,
                        open);
@@ -99,34 +112,33 @@ int *              open;
     /* need to convert names */
 
     if (src_name) {
-           status = __gss_convert_name_to_union_name(minor_status, mech,
-                                                     *src_name, src_name);
+           status = gssint_convert_name_to_union_name(minor_status, mech,
+                                                     localSourceName, src_name);
 
            if (status != GSS_S_COMPLETE) {
-               (void) mech->gss_release_name(mech->context,
-                                               &temp_minor, src_name);
-               (void) mech->gss_release_name(mech->context,
-                                               &temp_minor, targ_name);
-               if (mech_type) {
-                               gss_release_oid(&temp_minor, mech_type);
-               }
-               return (GSS_S_FAILURE);
+               if (localTargName)
+                   mech->gss_release_name(mech->context,
+                                          &temp_minor, &localTargName);
+               return (status);
            }
 
     }
 
     if (targ_name) {
-           status = __gss_convert_name_to_union_name(minor_status, mech,
-                                                     *targ_name, targ_name);
+           status = gssint_convert_name_to_union_name(minor_status, mech,
+                                                     localTargName, targ_name);
 
            if (status != GSS_S_COMPLETE) {
-               if (mech_type) {
-                       gss_release_oid(&temp_minor, mech_type);
-               }
-               return (GSS_S_FAILURE);
+               if (src_name)
+                   (void) gss_release_name(&temp_minor, src_name);
+
+               return (status);
            }
     }
 
+    /* spec says mech type must point to static storage */
+    if (mech_type)
+       *mech_type = &mech->mech_type;
     return(GSS_S_COMPLETE);
 }
 
index e0a2bc4a83ee1f37652519702dc3f7944aab930f..c4b59114d35d1b7995d05d9dcbd309b73dd63b93 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_inquire_cred.c 1.9     95/08/02 SMI" */
+/* #pragma ident       "@(#)g_inquire_cred.c   1.16    04/02/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -56,7 +56,16 @@ gss_OID_set *                mechanisms;
     gss_name_t         internal_name;
     int                        i;
     
-    gss_initialize();
+    /* check parms and set to defaults */
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
+
+    if (name)
+       *name = NULL;
+
+    if (mechanisms)
+       *mechanisms = NULL;
 
     if (cred_handle == GSS_C_NO_CREDENTIAL) {
        /*
@@ -67,11 +76,11 @@ gss_OID_set *               mechanisms;
         * array, which becomes the default mechanism.
         */
 
-       if ((mech = __gss_get_mechanism(GSS_C_NULL_OID)) == NULL)
-           return(GSS_S_NO_CRED);
+       if ((mech = gssint_get_mechanism(GSS_C_NULL_OID)) == NULL)
+           return (GSS_S_DEFECTIVE_CREDENTIAL);
 
        if (!mech->gss_inquire_cred)
-               return (GSS_S_FAILURE);
+           return (GSS_S_UNAVAILABLE);
        
        status = mech->gss_inquire_cred(mech->context, minor_status,
                                        GSS_C_NO_CREDENTIAL,
@@ -85,14 +94,16 @@ gss_OID_set *               mechanisms;
            /*
             * Convert internal_name into a union_name equivalent.
             */
-           status = __gss_convert_name_to_union_name(&temp_minor_status,
+           status = gssint_convert_name_to_union_name(&temp_minor_status,
                                                      mech, internal_name,
                                                      name);
            if (status != GSS_S_COMPLETE) {
-               if (minor_status)
-                   *minor_status = temp_minor_status;
-               __gss_release_internal_name(&temp_minor_status,
-                                           &mech->mech_type, &internal_name);
+               *minor_status = temp_minor_status;
+               if (mechanisms && *mechanisms) {
+                   (void) gss_release_oid_set(
+                       &temp_minor_status,
+                       mechanisms);
+               }
                return (status);
            }
        }
@@ -124,39 +135,68 @@ gss_OID_set *             mechanisms;
      * caller. If this call fails, return failure to our caller.
      */
     
-    if(name != NULL)
-       if(gss_import_name(&temp_minor_status,
-                          &union_cred->auxinfo.name,
-                          union_cred->auxinfo.name_type,
-                          name) != GSS_S_COMPLETE)
-           return(GSS_S_DEFECTIVE_CREDENTIAL);
-    
+    if(name != NULL) {
+       if ((gss_import_name(&temp_minor_status,
+                            &union_cred->auxinfo.name,
+                            union_cred->auxinfo.name_type,
+                            name) != GSS_S_COMPLETE) ||
+           (gss_canonicalize_name(minor_status, *name,
+                                  &union_cred->mechs_array[0],
+                                  NULL) != GSS_S_COMPLETE)) {
+           status = GSS_S_DEFECTIVE_CREDENTIAL;
+           goto error;
+       }
+    }
+
     /*
      * copy the mechanism set in union_cred into an OID set and return in
      * the mechanisms parameter.
      */
     
     if(mechanisms != NULL) {
-
+       status = GSS_S_FAILURE;
        *mechanisms = (gss_OID_set) malloc(sizeof(gss_OID_set_desc));
+       if (*mechanisms == NULL)
+           goto error;
 
-       (*mechanisms)->count = union_cred->count;
+       (*mechanisms)->count = 0;
        (*mechanisms)->elements =
            (gss_OID) malloc(sizeof(gss_OID_desc) *
                             union_cred->count);
 
+       if ((*mechanisms)->elements == NULL) {
+           free(*mechanisms);
+           *mechanisms = NULL;
+           goto error;
+       }
+
        for(i=0; i < union_cred->count; i++) {
-           (*mechanisms)->elements[i].length =
-               union_cred->mechs_array[i].length;
            (*mechanisms)->elements[i].elements = (void *)
                malloc(union_cred->mechs_array[i].length);
-           memcpy((*mechanisms)->elements[i].elements,
-                  union_cred->mechs_array[i].elements,
-                  union_cred->mechs_array[i].length);
+           if ((*mechanisms)->elements[i].elements == NULL)
+               goto error;
+           g_OID_copy(&(*mechanisms)->elements[i],
+                      &union_cred->mechs_array[i]);
+           (*mechanisms)->count++;
        }
     }
     
     return(GSS_S_COMPLETE);
+
+error:
+    /*
+     * cleanup any allocated memory - we can just call
+     * gss_release_oid_set, because the set is constructed so that
+     * count always references the currently copied number of
+     * elements.
+     */
+    if (mechanisms && *mechanisms != NULL)
+       (void) gss_release_oid_set(&temp_minor_status, mechanisms);
+
+    if (name && *name != NULL)
+       (void) gss_release_name(&temp_minor_status, name);
+
+    return (status);
 }
 
 OM_uint32 KRB5_CALLCONV
@@ -173,19 +213,46 @@ gss_inquire_cred_by_mech(minor_status, cred_handle, mech_type, name,
     gss_union_cred_t   union_cred;
     gss_cred_id_t      mech_cred;
     gss_mechanism      mech;
+    OM_uint32          status, temp_minor_status;
+    gss_name_t         internal_name;
+
 
-    mech = __gss_get_mechanism (mech_type);
+    mech = gssint_get_mechanism (mech_type);
     if (!mech)
        return (GSS_S_BAD_MECH);
     if (!mech->gss_inquire_cred_by_mech)
        return (GSS_S_BAD_BINDINGS);
      
     union_cred = (gss_union_cred_t) cred_handle;
-    mech_cred = __gss_get_mechanism_cred(union_cred, mech_type);
+    mech_cred = gssint_get_mechanism_cred(union_cred, mech_type);
+
+#if 0
+    if (mech_cred == NULL)
+       return (GSS_S_DEFECTIVE_CREDENTIAL);
+#endif
+
+    status = mech->gss_inquire_cred_by_mech(mech->context, minor_status,
+                                           mech_cred, mech_type,
+                                           name ? &internal_name : NULL,
+                                           initiator_lifetime,
+                                           acceptor_lifetime, cred_usage);
+       
+    if (status != GSS_S_COMPLETE)
+       return (status);
+
+    if (name) {
+       /*
+        * Convert internal_name into a union_name equivalent.
+        */
+       status = gssint_convert_name_to_union_name(
+           &temp_minor_status, mech,
+           internal_name, name);
+       if (status != GSS_S_COMPLETE) {
+           *minor_status = temp_minor_status;
+           return (status);
+       }
+    }
 
-    return (mech->gss_inquire_cred_by_mech(mech->context, minor_status,
-                                          mech_cred, mech_type,
-                                          name, initiator_lifetime,
-                                          acceptor_lifetime, cred_usage));
+    return (GSS_S_COMPLETE);
 }
 
index 7c07f44edd0730d9207b465c0ae9db1a8d35e547..4332e3e0ce6ae547e9abd2adf481899f9ebf4ff5 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident      "@(#)g_inquire_names.c 1.1     95/12/19 SMI" */
+/* #pragma ident       "@(#)g_inquire_names.c  1.16    04/02/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -28,6 +28,8 @@
 
 #include "mglueP.h"
 
+#define        MAX_MECH_OID_PAIRS 32
+
 /* Last argument new for V2 */
 OM_uint32 KRB5_CALLCONV
 gss_inquire_names_for_mech(minor_status, mechanism, name_types)
@@ -40,14 +42,19 @@ gss_OID_set *       name_types;
     OM_uint32          status;
     gss_mechanism      mech;
     
-    gss_initialize();
-    
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
+
+    if (name_types == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
     /*
      * select the approprate underlying mechanism routine and
      * call it.
      */
     
-    mech = __gss_get_mechanism (mechanism);
+    mech = gssint_get_mechanism (mechanism);
     
     if (mech) {
 
@@ -58,10 +65,93 @@ gss_OID_set *       name_types;
                                mechanism,
                                name_types);
        else
-           status = GSS_S_BAD_BINDINGS;
+           status = GSS_S_UNAVAILABLE;
 
        return(status);
     }
     
-    return(GSS_S_NO_CONTEXT);
+    return (GSS_S_BAD_MECH);
+}
+OM_uint32 KRB5_CALLCONV
+gss_inquire_mechs_for_name(minor_status, input_name, mech_set)
+
+    OM_uint32 *                minor_status;
+    const gss_name_t   input_name;
+    gss_OID_set *              mech_set;
+
+{
+    OM_uint32          status;
+    static char                *mech_list[MAX_MECH_OID_PAIRS+1];
+    gss_OID_set                mech_name_types;
+    int                        present;
+    char                       *mechanism;
+    gss_OID            mechOid;
+    gss_OID            name_type;
+    gss_buffer_desc            name_buffer;
+    int                        i;
+
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
+
+    if (input_name == NULL)
+       return (GSS_S_BAD_NAME);
+
+    status = gss_create_empty_oid_set(minor_status, mech_set);
+    if (status != GSS_S_COMPLETE)
+       return (status);
+    *mech_list = NULL;
+    status = gssint_get_mechanisms(mech_list, MAX_MECH_OID_PAIRS+1);
+    if (status != GSS_S_COMPLETE)
+       return (status);
+    for (i = 0; i < MAX_MECH_OID_PAIRS && mech_list[i] != NULL; i++) {
+       mechanism = mech_list[i];
+       if (gssint_mech_to_oid(mechanism, &mechOid) == GSS_S_COMPLETE) {
+           status = gss_inquire_names_for_mech(
+               minor_status,
+               mechOid,
+               &mech_name_types);
+           if (status == GSS_S_COMPLETE) {
+               status = gss_display_name(minor_status,
+                                         input_name,
+                                         &name_buffer,
+                                         &name_type);
+
+               (void) gss_release_buffer(NULL, &name_buffer);
+
+               if (status == GSS_S_COMPLETE && name_type) {
+                   status = gss_test_oid_set_member(
+                       minor_status,
+                       name_type,
+                       mech_name_types,
+                       &present);
+                   if (status == GSS_S_COMPLETE &&
+                       present) {
+                       status = gss_add_oid_set_member(
+                           minor_status,
+                           mechOid,
+                           mech_set);
+                       if (status != GSS_S_COMPLETE) {
+                           (void) gss_release_oid_set(
+                               minor_status,
+                               &mech_name_types);
+                           (void) gss_release_oid_set(
+                               minor_status,
+                               mech_set);
+                           return (status);
+                       }
+                   }
+               }
+               (void) gss_release_oid_set(
+                   minor_status,
+                   &mech_name_types);
+           }
+       } else {
+           (void) gss_release_oid_set(
+               minor_status,
+               mech_set);
+           return (GSS_S_FAILURE);
+       }
+    }
+    return (GSS_S_COMPLETE);
 }
index c013bdc155d92981fcf022a3e954825e6f8f2232..0607c38f10b062156237cb5ff3d4455a07900ff2 100644 (file)
 #include <string.h>
 #include <errno.h>
 
-#define g_OID_equal(o1,o2) \
-   (((o1)->length == (o2)->length) && \
-    (memcmp((o1)->elements,(o2)->elements,(int) (o1)->length) == 0))
-
 static gss_mech_spec_name name_list = NULL;
 
 /*
index 4a9d765f27021a0038019953c98ab11c0193c749..86e57972d2a6f810265df9d5e86c41ae9c42c29a 100644 (file)
@@ -1,3 +1,4 @@
+/* #pragma ident       "@(#)g_oid_ops.c        1.11    98/01/22 SMI" */
 /*
  * lib/gssapi/mechglue/g_oid_ops.c
  *
 #include "mglueP.h"
 /* should include to get protos #include "../generic/gssapiP_generic.h" */
 
-extern gss_mechanism *__gss_mechs_array;
+extern gss_mechanism *gssint_mechs_array;
 
-OM_uint32 KRB5_CALLCONV
-gss_release_oid(minor_status, oid)
-    OM_uint32  *minor_status;
-    gss_OID    *oid;
-{
-    int i;
-    OM_uint32   major_status;
-
-    /* first call the gss_internal_release_oid for each mechanism
-     * until one returns success. gss_internal_release_oid will only return
-     * success when the OID was recognized as an internal mechanism OID.
-     * if no mechanisms recognize the OID, then call the generic version.
-     */
-
-    for(i=0; __gss_mechs_array[i]->mech_type.length !=0; i++) {
-        if (__gss_mechs_array[i]->gss_internal_release_oid) {
-           major_status = __gss_mechs_array[i]->gss_internal_release_oid(
-                                           __gss_mechs_array[i]->context,
-                                           minor_status,
-                                           oid);
-           if (major_status == GSS_S_COMPLETE) {
-               return (GSS_S_COMPLETE);
-           }
-       }
-    }
-
-    return generic_gss_release_oid(minor_status, oid);
-}
+/*
+ * gss_release_oid has been moved to g_initialize, becasue it requires access
+ * to the mechanism list.  All functions requiring direct access to the
+ * mechanism list are now in g_initialize.c
+ */
 
 OM_uint32 KRB5_CALLCONV
 gss_create_empty_oid_set(minor_status, oid_set)
index dcb4716bc4dacfbcebd0e69f830c167efd6840ad..beb65a141dc1cdc5b3838530405910b3e67c97de 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_process_context.c 1.9     95/08/07 SMI" */
+/* #pragma ident       "@(#)g_process_context.c        1.12    98/01/22 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -42,10 +42,15 @@ gss_buffer_t                token_buffer;
     gss_union_ctx_id_t ctx;
     gss_mechanism      mech;
     
-    gss_initialize();
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
 
     if (context_handle == GSS_C_NO_CONTEXT)
-       return GSS_S_NO_CONTEXT;
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT);
+
+    if (GSS_EMPTY_BUFFER(token_buffer))
+       return (GSS_S_CALL_INACCESSIBLE_READ);
 
     /*
      * select the approprate underlying mechanism routine and
@@ -53,7 +58,7 @@ gss_buffer_t          token_buffer;
      */
     
     ctx = (gss_union_ctx_id_t) context_handle;
-    mech = __gss_get_mechanism (ctx->mech_type);
+    mech = gssint_get_mechanism (ctx->mech_type);
 
     if (mech) {
 
@@ -64,10 +69,10 @@ gss_buffer_t                token_buffer;
                                                    ctx->internal_ctx_id,
                                                    token_buffer);
        else
-           status = GSS_S_BAD_BINDINGS;
+           status = GSS_S_UNAVAILABLE;
 
        return(status);
     }
     
-    return(GSS_S_NO_CONTEXT);
+    return (GSS_S_BAD_MECH);
 }
index 44d82709e41706f539f20fa12c6294f0e3b91f52..ffcce2d7e1a74144fe8db2e35eded720c2f273df 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_release_cred.c 1.15     95/08/07 SMI" */
+/* #pragma ident       "@(#)g_rel_cred.c       1.14    04/02/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -45,15 +45,13 @@ gss_cred_id_t *             cred_handle;
     gss_union_cred_t   union_cred;
     gss_mechanism      mech;
     
-    gss_initialize();
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
 
-    if (minor_status)
-       *minor_status = 0;
+    *minor_status = 0;
 
-    /* if the cred_handle is null, return a NO_CRED error */
-    
-    if (cred_handle == GSS_C_NO_CREDENTIAL)
-       return(GSS_S_NO_CRED);
+    if (cred_handle == NULL)
+       return (GSS_S_NO_CRED | GSS_S_CALL_INACCESSIBLE_READ);
     
     /*
      * Loop through the union_cred struct, selecting the approprate 
@@ -64,14 +62,14 @@ gss_cred_id_t *             cred_handle;
     union_cred = (gss_union_cred_t) *cred_handle;
     *cred_handle = NULL;
 
-    if (union_cred == NULL)
-       return GSS_S_NO_CRED;
+    if (union_cred == (gss_union_cred_t)GSS_C_NO_CREDENTIAL)
+       return (GSS_S_COMPLETE);
 
     status = GSS_S_COMPLETE;
     
     for(j=0; j < union_cred->count; j++) {
 
-       mech = __gss_get_mechanism (&union_cred->mechs_array[j]);
+       mech = gssint_get_mechanism (&union_cred->mechs_array[j]);
 
        if (union_cred->mechs_array[j].elements)
                free(union_cred->mechs_array[j].elements);
@@ -86,9 +84,9 @@ gss_cred_id_t *               cred_handle;
                status = GSS_S_NO_CRED;
 
            } else
-               status = GSS_S_NO_CRED;
+               status = GSS_S_UNAVAILABLE;
        } else
-           status = GSS_S_NO_CRED;
+           status = GSS_S_DEFECTIVE_CREDENTIAL;
     }
 
     gss_release_buffer(minor_status, &union_cred->auxinfo.name);
index 29c3f98199c0d2a3345b26b3fc393685ce3aba1a..ff3c4a10a60cc8ed9221ce52bd551da5186a7dbd 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_release_name.c 1.2     95/05/09 SMI" */
+/* #pragma ident       "@(#)g_rel_name.c       1.11    04/02/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -43,11 +43,17 @@ gss_name_t *                input_name;
 {
     gss_union_name_t   union_name;
     
-    /* if input_name is NULL, return error */
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
     
+    /* if input_name is NULL, return error */
     if (input_name == 0)
-       return(GSS_S_BAD_NAME);
-    
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME);
+
+    if (*input_name == GSS_C_NO_NAME)
+       return GSS_S_COMPLETE;
+
     /*
      * free up the space for the external_name and then
      * free the union_name descriptor
@@ -56,9 +62,6 @@ gss_name_t *          input_name;
     union_name = (gss_union_name_t) *input_name;
     *input_name = 0;
     *minor_status = 0;
-    
-    if (union_name == NULL)
-       return GSS_S_BAD_NAME;
 
     if (union_name->name_type)
            gss_release_oid(minor_status, &union_name->name_type);
@@ -67,7 +70,7 @@ gss_name_t *          input_name;
     free(union_name->external_name);
 
     if (union_name->mech_type) {
-           __gss_release_internal_name(minor_status, union_name->mech_type,
+           gssint_release_internal_name(minor_status, union_name->mech_type,
                                        &union_name->mech_name);
            gss_release_oid(minor_status, &union_name->mech_type);
     }
index 357c00e67e3d107309a57b1ac3089c3c90a06192..f712a891a13298c0f168eb7d697b4c320564fe4e 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_release_oid_set.c 1.12     95/08/23 SMI" */
+/* #pragma ident       "@(#)g_rel_oid_set.c    1.12    97/11/11 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -39,8 +39,8 @@ gss_release_oid_set (minor_status,
 OM_uint32 *            minor_status;
 gss_OID_set *          set;
 {
-   size_t index;
-   gss_OID oid;
+    OM_uint32 index;
+    gss_OID oid;
     if (minor_status)
        *minor_status = 0;
 
index 7d66e469aee4288c8e85e7fd743a3a2b4a5c3bab..2b31c370f1d128c037cdaf39df044d30c0076f26 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_seal.c 1.10     95/08/07 SMI" */
+/* #pragma ident       "@(#)g_seal.c   1.19    98/04/21 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -44,16 +44,25 @@ int                 qop_req;
 gss_buffer_t           input_message_buffer;
 int *                  conf_state;
 gss_buffer_t           output_message_buffer;
-
 {
+ /* EXPORT DELETE START */
+
     OM_uint32          status;
     gss_union_ctx_id_t ctx;
     gss_mechanism      mech;
 
-    gss_initialize();
-    
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
+
     if (context_handle == GSS_C_NO_CONTEXT)
-       return GSS_S_NO_CONTEXT;
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT);
+
+    if (input_message_buffer == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_READ);
+
+    if (output_message_buffer == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
 
     /*
      * select the approprate underlying mechanism routine and
@@ -61,7 +70,7 @@ gss_buffer_t          output_message_buffer;
      */
     
     ctx = (gss_union_ctx_id_t) context_handle;
-    mech = __gss_get_mechanism (ctx->mech_type);
+    mech = gssint_get_mechanism (ctx->mech_type);
     
     if (mech) {
        if (mech->gss_seal)
@@ -75,12 +84,13 @@ gss_buffer_t                output_message_buffer;
                                    conf_state,
                                    output_message_buffer);
        else
-           status = GSS_S_BAD_BINDINGS;
+           status = GSS_S_UNAVAILABLE;
        
        return(status);
     }
-
-    return(GSS_S_NO_CONTEXT);
+ /* EXPORT DELETE END */
+    return (GSS_S_BAD_MECH);
 }
 
 OM_uint32 KRB5_CALLCONV
@@ -101,9 +111,10 @@ int *                      conf_state;
 gss_buffer_t           output_message_buffer;
 
 {
-       return gss_seal(minor_status, context_handle, conf_req_flag,
-                       (int) qop_req, input_message_buffer, conf_state,
-                       output_message_buffer);
+    return gss_seal(minor_status, (gss_ctx_id_t)context_handle,
+                   conf_req_flag, (int) qop_req,
+                   (gss_buffer_t)input_message_buffer, conf_state,
+                   output_message_buffer);
 }
 
 /*
@@ -119,14 +130,18 @@ gss_wrap_size_limit(minor_status, context_handle, conf_req_flag,
     OM_uint32          req_output_size;
     OM_uint32          *max_input_size;
 {
-    OM_uint32          status;
     gss_union_ctx_id_t ctx;
     gss_mechanism      mech;
 
-    gss_initialize();
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
     
     if (context_handle == GSS_C_NO_CONTEXT)
-       return GSS_S_NO_CONTEXT;
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT);
+
+    if (max_input_size == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
 
     /*
      * select the approprate underlying mechanism routine and
@@ -134,16 +149,15 @@ gss_wrap_size_limit(minor_status, context_handle, conf_req_flag,
      */
     
     ctx = (gss_union_ctx_id_t) context_handle;
-    mech = __gss_get_mechanism (ctx->mech_type);
+    mech = gssint_get_mechanism (ctx->mech_type);
 
     if (!mech)
-       return (GSS_S_NO_CONTEXT);
+       return (GSS_S_BAD_MECH);
 
     if (!mech->gss_wrap_size_limit)
-       return (GSS_S_BAD_BINDINGS);
+       return (GSS_S_UNAVAILABLE);
     
-    status = mech->gss_wrap_size_limit(mech->context, minor_status,
-                                      context_handle, conf_req_flag, qop_req,
-                                      req_output_size, max_input_size);
-    return(status);
+    return (mech->gss_wrap_size_limit(mech->context, minor_status,
+                                     ctx->internal_ctx_id, conf_req_flag, qop_req,
+                                     req_output_size, max_input_size));
 }
index 4dfd3ec7188850cf95f606aea25261c01a4b8067..72e0ce742c77951d7ecf8e7dbd3584d4c83eb920 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_sign.c 1.10     95/08/07 SMI" */
+/* #pragma ident       "@(#)g_sign.c   1.14    98/04/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -46,18 +46,28 @@ gss_buffer_t                msg_token;
     gss_union_ctx_id_t ctx;
     gss_mechanism      mech;
 
-    gss_initialize();
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
 
     if (context_handle == GSS_C_NO_CONTEXT)
-       return GSS_S_NO_CONTEXT;
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT);
 
+    if (message_buffer == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_READ);
+
+    if (msg_token == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+    msg_token->value = NULL;
+    msg_token->length = 0;
     /*
      * select the approprate underlying mechanism routine and
      * call it.
      */
 
     ctx = (gss_union_ctx_id_t) context_handle;
-    mech = __gss_get_mechanism (ctx->mech_type);
+    mech = gssint_get_mechanism (ctx->mech_type);
 
     if (mech) {
        if (mech->gss_sign)
@@ -69,12 +79,12 @@ gss_buffer_t                msg_token;
                                    message_buffer,
                                    msg_token);
        else
-           status = GSS_S_BAD_BINDINGS;
+           status = GSS_S_UNAVAILABLE;
 
        return(status);
     }
 
-    return(GSS_S_NO_CONTEXT);
+    return (GSS_S_BAD_MECH);
 }
 
 OM_uint32 KRB5_CALLCONV
diff --git a/src/lib/gssapi/mechglue/g_store_cred.c b/src/lib/gssapi/mechglue/g_store_cred.c
new file mode 100644 (file)
index 0000000..92581be
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* #pragma ident       "@(#)g_store_cred.c     1.2     04/04/05 SMI" */
+
+/*
+ *  glue routine for gss_store_cred
+ */
+
+#include <mglueP.h>
+
+OM_uint32 gss_store_cred(minor_status,
+                       input_cred_handle,
+                       cred_usage,
+                       desired_mech,
+                       overwrite_cred,
+                       default_cred,
+                       elements_stored,
+                       cred_usage_stored)
+
+OM_uint32              *minor_status;
+const gss_cred_id_t     input_cred_handle;
+gss_cred_usage_t        cred_usage;
+const gss_OID           desired_mech;
+OM_uint32               overwrite_cred;
+OM_uint32               default_cred;
+gss_OID_set            *elements_stored;
+gss_cred_usage_t       *cred_usage_stored;
+
+{
+       OM_uint32               major_status = GSS_S_FAILURE;
+       gss_union_cred_t        union_cred;
+       gss_cred_id_t           mech_cred;
+       gss_mechanism           mech;
+       gss_OID                 dmech;
+       int                     i;
+
+       /* Start by checking parameters */
+       if (minor_status == NULL)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE|GSS_S_NO_CRED);
+       *minor_status = 0;
+
+       if (input_cred_handle == GSS_C_NO_CREDENTIAL)
+               return (GSS_S_CALL_INACCESSIBLE_READ);
+
+       if (elements_stored != NULL)
+               *elements_stored = GSS_C_NULL_OID_SET;
+
+       if (cred_usage_stored != NULL)
+               *cred_usage_stored = GSS_C_BOTH; /* there's no GSS_C_NEITHER */
+
+       union_cred = (gss_union_cred_t)input_cred_handle;
+
+       /* desired_mech != GSS_C_NULL_OID -> store one element */
+       if (desired_mech != GSS_C_NULL_OID) {
+               mech = gssint_get_mechanism(desired_mech);
+               if (mech == NULL)
+                       return (GSS_S_BAD_MECH);
+
+               if (mech->gss_store_cred == NULL)
+                       return (major_status);
+
+               mech_cred = gssint_get_mechanism_cred(union_cred, desired_mech);
+               if (mech_cred == GSS_C_NO_CREDENTIAL)
+                       return (GSS_S_NO_CRED);
+
+               return (mech->gss_store_cred(mech->context,
+                                               minor_status,
+                                               (gss_cred_id_t)mech_cred,
+                                               cred_usage,
+                                               desired_mech,
+                                               overwrite_cred,
+                                               default_cred,
+                                               elements_stored,
+                                               cred_usage_stored));
+       }
+
+       /* desired_mech == GSS_C_NULL_OID -> store all elements */
+
+       *minor_status = 0;
+
+       for (i = 0; i < union_cred->count; i++) {
+               /* Get mech and cred element */
+               dmech = &union_cred->mechs_array[i];
+               mech = gssint_get_mechanism(dmech);
+               if (mech == NULL)
+                       continue;
+
+               if (mech->gss_store_cred == NULL)
+                       continue;
+
+               mech_cred = gssint_get_mechanism_cred(union_cred, dmech);
+               if (mech_cred == GSS_C_NO_CREDENTIAL)
+                       continue; /* can't happen, but safe to ignore */
+
+               major_status = mech->gss_store_cred(mech->context,
+                                               minor_status,
+                                               (gss_cred_id_t)mech_cred,
+                                               cred_usage,
+                                               dmech,
+                                               overwrite_cred,
+                                               default_cred,
+                                               NULL,
+                                               cred_usage_stored);
+               if (major_status != GSS_S_COMPLETE)
+                       continue;
+
+               /* Succeeded for at least one mech */
+
+               if (elements_stored == NULL)
+                       continue;
+
+               if (*elements_stored == GSS_C_NULL_OID_SET) {
+                       major_status = gss_create_empty_oid_set(minor_status,
+                                               elements_stored);
+
+                       if (GSS_ERROR(major_status))
+                               return (major_status);
+               }
+
+               major_status = gss_add_oid_set_member(minor_status, dmech,
+                       elements_stored);
+
+               /* The caller should clean up elements_stored */
+               if (GSS_ERROR(major_status))
+                       return (major_status);
+       }
+
+       /*
+        * Success with some mechs may mask failure with others, but
+        * that's what elements_stored is for.
+        */
+       return (major_status);
+}
index 9ca1c15129cde71fe50c85f81354132b4062a39a..c70c59beb0f14a9ade48ed37be449654e6b9bacd 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_unseal.c 1.10     95/08/07 SMI" */
+/* #pragma ident       "@(#)g_unseal.c 1.13    98/01/22 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -44,14 +44,26 @@ int *                       conf_state;
 int *                  qop_state;
 
 {
+/* EXPORT DELETE START */
     OM_uint32          status;
     gss_union_ctx_id_t ctx;
     gss_mechanism      mech;
 
-    gss_initialize();
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
 
     if (context_handle == GSS_C_NO_CONTEXT)
-       return GSS_S_NO_CONTEXT;
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT);
+
+    if (GSS_EMPTY_BUFFER(input_message_buffer))
+       return (GSS_S_CALL_INACCESSIBLE_READ);
+
+    if (output_message_buffer == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+    output_message_buffer->length = 0;
+    output_message_buffer->value = NULL;
 
     /*
      * select the approprate underlying mechanism routine and
@@ -59,7 +71,7 @@ int *                 qop_state;
      */
 
     ctx = (gss_union_ctx_id_t) context_handle;
-    mech = __gss_get_mechanism (ctx->mech_type);
+    mech = gssint_get_mechanism (ctx->mech_type);
 
     if (mech) {
        if (mech->gss_unseal) 
@@ -72,12 +84,14 @@ int *                       qop_state;
                                      conf_state,
                                      qop_state);
        else
-           status = GSS_S_BAD_BINDINGS;
+           status = GSS_S_UNAVAILABLE;
 
        return(status);
     }
 
-    return(GSS_S_NO_CONTEXT);
+/* EXPORT DELETE END */
+
+    return (GSS_S_BAD_MECH);
 }
 
 OM_uint32 KRB5_CALLCONV
@@ -89,15 +103,14 @@ gss_unwrap (minor_status,
             qop_state)
 
 OM_uint32 *            minor_status;
-gss_ctx_id_t           context_handle;
-gss_buffer_t           input_message_buffer;
+const gss_ctx_id_t     context_handle;
+const gss_buffer_t     input_message_buffer;
 gss_buffer_t           output_message_buffer;
 int *                  conf_state;
 gss_qop_t *            qop_state;
 
 {
-       return (gss_unseal(minor_status, context_handle,
-                          input_message_buffer,
-                          output_message_buffer,
-                          conf_state, (int *) qop_state));
+    return (gss_unseal(minor_status, (gss_ctx_id_t)context_handle,
+                      (gss_buffer_t)input_message_buffer,
+                      output_message_buffer, conf_state, (int *) qop_state));
 }
diff --git a/src/lib/gssapi/mechglue/g_userok.c b/src/lib/gssapi/mechglue/g_userok.c
new file mode 100644 (file)
index 0000000..4657b8e
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* #pragma ident       "@(#)g_userok.c 1.1     04/03/25 SMI" */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <mglueP.h>
+#include <gssapi/gssapi.h>
+
+
+static OM_uint32
+compare_names(OM_uint32 *minor,
+           const gss_OID mech_type,
+           const gss_name_t name,
+           const char *user,
+           int *user_ok)
+{
+
+       OM_uint32 status, tmpMinor;
+       gss_name_t imported_name;
+       gss_name_t canon_name;
+       gss_buffer_desc gss_user;
+       int match = 0;
+
+       *user_ok = 0;
+
+       gss_user.value = (void *)user;
+       if (!gss_user.value || !name || !mech_type)
+               return (GSS_S_BAD_NAME);
+       gss_user.length = strlen(gss_user.value);
+
+       status = gss_import_name(minor,
+                               &gss_user,
+                               GSS_C_NT_USER_NAME,
+                               &imported_name);
+       if (status != GSS_S_COMPLETE) {
+               goto out;
+       }
+
+       status = gss_canonicalize_name(minor,
+                                   imported_name,
+                                   mech_type,
+                                   &canon_name);
+       if (status != GSS_S_COMPLETE) {
+               (void) gss_release_name(&tmpMinor, &imported_name);
+               goto out;
+       }
+
+       status = gss_compare_name(minor,
+                               canon_name,
+                               name,
+                               &match);
+       (void) gss_release_name(&tmpMinor, &canon_name);
+       (void) gss_release_name(&tmpMinor, &imported_name);
+       if (status == GSS_S_COMPLETE) {
+               if (match)
+                       *user_ok = 1; /* remote user is a-ok */
+       }
+
+out:
+       return (status);
+}
+
+
+OM_uint32
+gssint_userok(OM_uint32 *minor,
+           const gss_name_t name,
+           const char *user,
+           int *user_ok)
+
+{
+       gss_mechanism mech;
+       gss_union_name_t intName;
+       gss_name_t mechName = NULL;
+       OM_uint32 major;
+
+       if (minor == NULL || user_ok == NULL)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+       if (name == NULL || user == NULL)
+               return (GSS_S_CALL_INACCESSIBLE_READ);
+
+       *user_ok = 0;
+       *minor = GSS_S_COMPLETE;
+
+       intName = (gss_union_name_t)name;
+
+       mech = gssint_get_mechanism(intName->mech_type);
+       if (mech == NULL)
+               return (GSS_S_UNAVAILABLE);
+
+       /* may need to import the name if this is not MN */
+       if (intName->mech_type == NULL) {
+               return (GSS_S_FAILURE);
+       } else
+               mechName = intName->mech_name;
+
+       if (mech->gssint_userok)
+               major = mech->gssint_userok(mech->context,  minor, mechName,
+                               user, user_ok);
+       else
+               major = compare_names(minor, intName->mech_type,
+                                   name, user, user_ok);
+
+       return (major);
+} /* gss_userok */
diff --git a/src/lib/gssapi/mechglue/g_utils.c b/src/lib/gssapi/mechglue/g_utils.c
new file mode 100644 (file)
index 0000000..82fe70d
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* #pragma ident       "@(#)g_utils.c  1.8     04/02/23 SMI" */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <ctype.h>
+#include <errno.h>
+#include <gssapi/gssapi.h>
+
+#define        Q_DEFAULT               "default"
+#define        BUFLEN                  256
+
+#if 0
+static int qop_num_pair_cnt;
+static const char    QOP_NUM_FILE[] = "/etc/gss/qop";
+static qop_num qop_num_pairs[MAX_QOP_NUM_PAIRS+1];
+static mutex_t qopfile_lock = DEFAULTMUTEX;
+
+static OM_uint32 gssint_read_qop_file(void);
+
+/*
+ * This routine fetches qop and num from "/etc/gss/qop".
+ * There is a memory leak associated with rereading this file,
+ * because we can't free the qop_num_pairs array when we reread
+ * the file (some callers may have been given these pointers).
+ * In general, this memory leak should be a small one, because
+ * we don't expect the qop file to be changed and reread often.
+ */
+static OM_uint32
+gssint_read_qop_file(void)
+{
+       char    buf[BUFLEN];    /* one line from the file */
+       char    *name, *next;
+       char    *qopname, *num_str;
+       char    *line;
+       FILE    *fp;
+       static int last = 0;
+       struct stat stbuf;
+       OM_uint32 major = GSS_S_COMPLETE;
+
+       (void) mutex_lock(&qopfile_lock);
+       if (stat(QOP_NUM_FILE, &stbuf) != 0 || stbuf.st_mtime < last) {
+               if (!qop_num_pairs[0].qop) {
+                       major = GSS_S_FAILURE;
+               }
+               goto done;
+       }
+       last = stbuf.st_mtime;
+
+       fp = fopen(QOP_NUM_FILE, "r");
+       if (fp == (FILE *)0) {
+               major = GSS_S_FAILURE;
+               goto done;
+       }
+
+       /*
+        * For each line in the file parse it appropriately.
+        * File format : qopname        num(int)
+        * Note that we silently ignore corrupt entries.
+        */
+       qop_num_pair_cnt = 0;
+       while (!feof(fp)) {
+               line = fgets(buf, BUFLEN, fp);
+               if (line == NULL)
+                       break;
+
+               /* Skip comments and blank lines */
+               if ((*line == '#') || (*line == '\n'))
+                       continue;
+
+               /* Skip trailing comments */
+               next = strchr(line, '#');
+               if (next)
+                       *next = '\0';
+
+               name = &(buf[0]);
+               while (isspace(*name))
+                       name++;
+               if (*name == '\0')      /* blank line */
+                       continue;
+
+               qopname = name; /* will contain qop name */
+               while (!isspace(*qopname))
+                       qopname++;
+               if (*qopname == '\0') {
+                       continue;
+               }
+               next = qopname+1;
+               *qopname = '\0';        /* null terminate qopname */
+               qop_num_pairs[qop_num_pair_cnt].qop = strdup(name);
+               if (qop_num_pairs[qop_num_pair_cnt].qop == NULL)
+                       continue;
+
+               name = next;
+               while (isspace(*name))
+                       name++;
+               if (*name == '\0') {    /* end of line, no num */
+                       free(qop_num_pairs[qop_num_pair_cnt].qop);
+                       continue;
+               }
+               num_str = name; /* will contain num (n) */
+               while (!isspace(*num_str))
+                       num_str++;
+               next = num_str+1;
+               *num_str++ = '\0';      /* null terminate num_str */
+
+               qop_num_pairs[qop_num_pair_cnt].num = (OM_uint32)atoi(name);
+               name = next;
+               while (isspace(*name))
+                       name++;
+               if (*name == '\0') {    /* end of line, no mechanism */
+                       free(qop_num_pairs[qop_num_pair_cnt].qop);
+                       continue;
+               }
+               num_str = name; /* will contain mech */
+               while (!isspace(*num_str))
+                       num_str++;
+               *num_str = '\0';
+
+               qop_num_pairs[qop_num_pair_cnt].mech = strdup(name);
+               if (qop_num_pairs[qop_num_pair_cnt].mech == NULL) {
+                       free(qop_num_pairs[qop_num_pair_cnt].qop);
+                       continue;
+               }
+
+               if (qop_num_pair_cnt++ >= MAX_QOP_NUM_PAIRS)
+                       break;
+       }
+       (void) fclose(fp);
+done:
+       (void) mutex_unlock(&qopfile_lock);
+       return (major);
+}
+
+OM_uint32
+gssint_qop_to_num(
+       char            *qop,
+       char            *mech,
+       OM_uint32       *num
+)
+{
+       int i;
+       OM_uint32 major = GSS_S_FAILURE;
+
+       if (!num)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+       if (qop == NULL || strlen(qop) == 0 ||
+                       strcasecmp(qop, Q_DEFAULT) == 0) {
+               *num = GSS_C_QOP_DEFAULT;
+               return (GSS_S_COMPLETE);
+       }
+
+       if ((major = gssint_read_qop_file()) != GSS_S_COMPLETE)
+               return (major);
+
+       for (i = 0; i < qop_num_pair_cnt; i++) {
+               if ((strcasecmp(mech, qop_num_pairs[i].mech) == 0) &&
+                   (strcasecmp(qop, qop_num_pairs[i].qop) == 0)) {
+                       *num = qop_num_pairs[i].num;
+                       return (GSS_S_COMPLETE);
+               }
+       }
+
+       return (GSS_S_FAILURE);
+}
+
+OM_uint32
+gssint_num_to_qop(
+       char            *mech,
+       OM_uint32       num,
+       char            **qop
+)
+{
+       int i;
+       OM_uint32 major;
+
+       if (!qop)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
+       *qop = NULL;
+
+       if (num == GSS_C_QOP_DEFAULT) {
+               *qop = Q_DEFAULT;
+               return (GSS_S_COMPLETE);
+       }
+
+       if (mech == NULL)
+               return (GSS_S_CALL_INACCESSIBLE_READ);
+
+       if ((major = gssint_read_qop_file()) != GSS_S_COMPLETE)
+               return (major);
+
+       for (i = 0; i < qop_num_pair_cnt; i++) {
+               if ((strcasecmp(mech, qop_num_pairs[i].mech) == 0) &&
+                   (num == qop_num_pairs[i].num)) {
+                       *qop = qop_num_pairs[i].qop;
+                       return (GSS_S_COMPLETE);
+               }
+       }
+       return (GSS_S_FAILURE);
+}
+
+/*
+ * For a given mechanism pass back qop information about it in a buffer
+ * of size MAX_QOPS_PER_MECH+1.
+ */
+OM_uint32
+gssint_get_mech_info(
+       char            *mech,
+       char            **qops
+)
+{
+       int i, cnt = 0;
+       OM_uint32 major = GSS_S_COMPLETE;
+
+       if (!qops)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
+       *qops = NULL;
+
+       if (!mech)
+               return (GSS_S_CALL_INACCESSIBLE_READ);
+
+       if ((major = gssint_read_qop_file()) != GSS_S_COMPLETE)
+               return (major);
+
+       for (i = 0; i < qop_num_pair_cnt; i++) {
+               if (strcmp(mech, qop_num_pairs[i].mech) == 0) {
+                   if (cnt >= MAX_QOPS_PER_MECH) {
+                       return (GSS_S_FAILURE);
+                   }
+                   qops[cnt++] = qop_num_pairs[i].qop;
+               }
+       }
+       qops[cnt] = NULL;
+       return (GSS_S_COMPLETE);
+}
+
+/*
+ * Copy the qop values and names for the mechanism back in a qop_num
+ * buffer of size MAX_QOPS_PER_MECH provided by the caller.
+ */
+OM_uint32
+gssint_mech_qops(
+       char *mech,
+       qop_num *mechqops,
+       int *numqop
+)
+{
+       int i;
+       OM_uint32 major;
+       int cnt = 0;
+
+       if (!mechqops || !numqop)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
+       *numqop = 0;
+
+       if (!mech)
+               return (GSS_S_CALL_INACCESSIBLE_READ);
+
+       if ((major = gssint_read_qop_file()) != GSS_S_COMPLETE)
+               return (major);
+
+       for (i = 0; i < qop_num_pair_cnt; i++) {
+           if (strcasecmp(mech, qop_num_pairs[i].mech) == 0) {
+               if (cnt >= MAX_QOPS_PER_MECH) {
+                       return (GSS_S_FAILURE);
+               }
+               mechqops[cnt++] = qop_num_pairs[i];
+           }
+       }
+       *numqop = cnt;
+       return (GSS_S_COMPLETE);
+}
+#endif
index 7fc86b448b251d671dad297c8572caeb2ced8849..e6a01282ab4fd707d150b5f38637be27f64f3021 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gss_verify.c 1.9     95/08/07 SMI" */
+/* #pragma ident       "@(#)g_verify.c 1.13    98/04/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -46,10 +46,16 @@ int *                       qop_state;
     gss_union_ctx_id_t ctx;
     gss_mechanism      mech;
 
-    gss_initialize();
+
+    if (minor_status == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    *minor_status = 0;
 
     if (context_handle == GSS_C_NO_CONTEXT)
-       return GSS_S_NO_CONTEXT;
+       return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT);
+
+    if ((message_buffer == NULL) || GSS_EMPTY_BUFFER(token_buffer))
+       return (GSS_S_CALL_INACCESSIBLE_READ);
 
     /*
      * select the approprate underlying mechanism routine and
@@ -57,7 +63,7 @@ int *                 qop_state;
      */
 
     ctx = (gss_union_ctx_id_t) context_handle;
-    mech = __gss_get_mechanism (ctx->mech_type);
+    mech = gssint_get_mechanism (ctx->mech_type);
 
     if (mech) {
        if (mech->gss_verify)
@@ -69,12 +75,12 @@ int *                       qop_state;
                                      token_buffer,
                                      qop_state);
        else
-           status = GSS_S_BAD_BINDINGS;
+           status = GSS_S_UNAVAILABLE;
 
        return(status);
     }
 
-    return(GSS_S_NO_CONTEXT);
+    return (GSS_S_BAD_MECH);
 }
 
 OM_uint32 KRB5_CALLCONV
index eb7c051acdc9825124925cd67f5cb5d690fd06bd..f15b16c5db17cdc797ea31bd796e3bc6e6b63da5 100644 (file)
@@ -1,4 +1,4 @@
-/* #ident  "@(#)gssd_pname_to_uid.c 1.5     95/08/02 SMI" */
+/* #pragma ident       "@(#)gssd_pname_to_uid.c        1.18    04/02/23 SMI" */
 
 /*
  * Copyright 1996 by Sun Microsystems, Inc.
@@ -42,14 +42,12 @@ uid_t * uid;
     int status;
     gss_mechanism      mech;
 
-    gss_initialize();
-
     /*
      * find the appropriate mechanism specific pname_to_uid procedure and
      * call it.
      */
 
-    mech = __gss_get_mechanism (mech_type);
+    mech = gssint_get_mechanism (mech_type);
 
     if (mech) {
        if (mech_type == GSS_C_NULL_OID)
index c9a7d4ee3899b298ca851d56f54d9c32d066129c..bfee2524a8ab3f996e12b049f4a3ade6ccba84c6 100644 (file)
 #ifndef _GSS_MECHGLUEP_H
 #define _GSS_MECHGLUEP_H
 
+#include "autoconf.h"
 #include "mechglue.h"
+#include "gssapiP_generic.h"
+
+#define        g_OID_copy(o1, o2)                                      \
+do {                                                           \
+       memcpy((o1)->elements, (o2)->elements, (o2)->length);   \
+       (o1)->length = (o2)->length;                            \
+} while (0)
+
+#define        GSS_EMPTY_BUFFER(buf)   ((buf) == NULL ||\
+       (buf)->value == NULL || (buf)->length == 0)
 
 /*
  * Array of context IDs typed by mechanism OID
@@ -50,7 +61,7 @@ typedef struct gss_mech_spec_name_t {
 typedef struct gss_union_cred_auxinfo {
        gss_buffer_desc         name;
        gss_OID                 name_type;
-       time_t                  creation_time;
+       OM_uint32               creation_time;
        OM_uint32               time_rec;
        int                     cred_usage;
 } gss_union_cred_auxinfo;
@@ -61,10 +72,23 @@ typedef struct gss_union_cred_auxinfo {
 typedef struct gss_union_cred_t {
        int                     count;
        gss_OID                 mechs_array;
-       gss_cred_id_t *         cred_array;
+       gss_cred_id_t           *cred_array;
        gss_union_cred_auxinfo  auxinfo;
 } gss_union_cred_desc, *gss_union_cred_t;
  
+typedef        OM_uint32           (*gss_acquire_cred_with_password_sfct)(
+                   void *,             /* context */
+                   OM_uint32 *,        /* minor_status */
+                   const gss_name_t,   /* desired_name */
+                   const gss_buffer_t, /* password */
+                   OM_uint32,          /* time_req */
+                   const gss_OID_set,  /* desired_mechs */
+                   int,                /* cred_usage */
+                   gss_cred_id_t *,    /* output_cred_handle */
+                   gss_OID_set *,      /* actual_mechs */
+                   OM_uint32 *         /* time_rec */
+       /* */);
+
 /********************************************************/
 /* The Mechanism Dispatch Table -- a mechanism needs to */
 /* define one of these and provide a function to return */
@@ -82,6 +106,8 @@ typedef struct gss_union_cred_t {
  */
  
 typedef struct gss_config {
+    OM_uint32      priority;
+    char *         mechNameStr;
     gss_OID_desc    mech_type;
     void *         context;
     OM_uint32       (*gss_acquire_cred)
@@ -334,79 +360,165 @@ typedef struct gss_config {
                    gss_OID,            /* mech type */
                    uid_t *             /* uid */
                    );
-
+       OM_uint32               (*gssint_userok)
+       (
+                   void *,             /* context */
+                   OM_uint32 *,        /* minor_status */
+                   const gss_name_t,   /* pname */
+                   const char *,       /* local user */
+                   int *               /* user ok? */
+       /* */);
+       OM_uint32               (*gss_export_name)
+       (
+               void *,                 /* context */
+               OM_uint32 *,            /* minor_status */
+               const gss_name_t,       /* input_name */
+               gss_buffer_t            /* exported_name */
+       /* */);
+       OM_uint32       (*gss_store_cred)
+       (
+               void *,                 /* context */
+               OM_uint32 *,            /* minor_status */
+               const gss_cred_id_t,    /* input_cred */
+               gss_cred_usage_t,       /* cred_usage */
+               const gss_OID,          /* desired_mech */
+               OM_uint32,              /* overwrite_cred */
+               OM_uint32,              /* default_cred */
+               gss_OID_set *,          /* elements_stored */
+               gss_cred_usage_t *      /* cred_usage_stored */
+       /* */);
 } *gss_mechanism;
 
+/* This structure MUST NOT be used by any code outside libgss */
+typedef struct gss_config_ext {
+       gss_acquire_cred_with_password_sfct     gss_acquire_cred_with_password;
+} *gss_mechanism_ext;
+
+/*
+ * In the user space we use a wrapper structure to encompass the
+ * mechanism entry points.  The wrapper contain the mechanism
+ * entry points and other data which is only relevant to the gss-api
+ * layer.  In the kernel we use only the gss_config strucutre because
+ * the kernal does not cantain any of the extra gss-api specific data.
+ */
+typedef struct gss_mech_config {
+       char *kmodName;                 /* kernel module name */
+       char *uLibName;                 /* user library name */
+       char *mechNameStr;              /* mechanism string name */
+       char *optionStr;                /* optional mech parameters */
+       void *dl_handle;                /* RTLD object handle for the mech */
+       gss_OID mech_type;              /* mechanism oid */
+       gss_mechanism mech;             /* mechanism initialization struct */
+       gss_mechanism_ext mech_ext;     /* extensions */
+       struct gss_mech_config *next;   /* next element in the list */
+} *gss_mech_info;
+
 /********************************************************/
 /* Internal mechglue routines */
 
-gss_mechanism __gss_get_mechanism (gss_OID);
-OM_uint32 __gss_get_mech_type (gss_OID, gss_buffer_t);
-OM_uint32 __gss_import_internal_name (OM_uint32 *, gss_OID, gss_union_name_t,
+int gssint_mechglue_init(void);
+void gssint_mechglue_fini(void);
+
+gss_mechanism gssint_get_mechanism (gss_OID);
+gss_mechanism_ext gssint_get_mechanism_ext(const gss_OID);
+OM_uint32 gssint_get_mech_type (gss_OID, gss_buffer_t);
+char *gssint_get_kmodName(const gss_OID);
+char *gssint_get_modOptions(const gss_OID);
+OM_uint32 gssint_import_internal_name (OM_uint32 *, gss_OID, gss_union_name_t,
                                      gss_name_t *);
-OM_uint32 __gss_display_internal_name (OM_uint32 *, gss_OID, gss_name_t,
+OM_uint32 gssint_export_internal_name(OM_uint32 *, const gss_OID,
+       const gss_name_t, gss_buffer_t);
+OM_uint32 gssint_display_internal_name (OM_uint32 *, gss_OID, gss_name_t,
                                       gss_buffer_t, gss_OID *);
-OM_uint32 __gss_release_internal_name (OM_uint32 *, gss_OID, gss_name_t *);
+OM_uint32 gssint_release_internal_name (OM_uint32 *, gss_OID, gss_name_t *);
 
-OM_uint32 __gss_convert_name_to_union_name
+OM_uint32 gssint_convert_name_to_union_name
          (OM_uint32 *,         /* minor_status */
           gss_mechanism,       /* mech */
           gss_name_t,          /* internal_name */
           gss_name_t *         /* external_name */
           );
-gss_cred_id_t __gss_get_mechanism_cred
+gss_cred_id_t gssint_get_mechanism_cred
          (gss_union_cred_t,    /* union_cred */
           gss_OID              /* mech_type */
           );
 
-OM_uint32 generic_gss_release_oid
-          (OM_uint32 *,        /* minor_status */
-           gss_OID *           /* oid */
-          );
+OM_uint32 gssint_create_copy_buffer(
+       const gss_buffer_t,     /* src buffer */
+       gss_buffer_t *,         /* destination buffer */
+       int                     /* NULL terminate buffer ? */
+);
 
-OM_uint32 generic_gss_copy_oid
-          (OM_uint32 *,        /* minor_status */
-           gss_OID,            /* oid */
-           gss_OID *           /* new_oid */
-           );
+OM_uint32 gssint_copy_oid_set(
+       OM_uint32 *,                    /* minor_status */
+       const gss_OID_set_desc *,       /* oid set */
+       gss_OID_set *                   /* new oid set */
+);
 
-OM_uint32 generic_gss_create_empty_oid_set
-          (OM_uint32 *,        /* minor_status */
-           gss_OID_set *       /* oid_set */
-          );
+gss_OID gss_find_mechanism_from_name_type (gss_OID); /* name_type */
 
-OM_uint32 generic_gss_add_oid_set_member
+OM_uint32 gss_add_mech_name_type
           (OM_uint32 *,        /* minor_status */
-           gss_OID,            /* member_oid */
-           gss_OID_set *       /* oid_set */
-          );
+           gss_OID,            /* name_type */
+           gss_OID             /* mech */
+              );
 
-OM_uint32 generic_gss_test_oid_set_member
-          (OM_uint32 *,        /* minor_status */
-           gss_OID,            /* member */
-           gss_OID_set,        /* set */
-           int *               /* present */
-          );
+/*
+ * Sun extensions to GSS-API v2
+ */
 
-OM_uint32 generic_gss_oid_to_str
- (OM_uint32 *, /* minor_status */
-           gss_OID,            /* oid */
-           gss_buffer_t        /* oid_str */
-          );
+OM_uint32
+gssint_mech_to_oid(
+       const char *mech,               /* mechanism string name */
+       gss_OID *oid                    /* mechanism oid */
+);
 
-OM_uint32 generic_gss_str_to_oid
-          (OM_uint32 *,        /* minor_status */
-           gss_buffer_t,       /* oid_str */
-           gss_OID *           /* oid */
-          );
+const char *
+gssint_oid_to_mech(
+       const gss_OID oid               /* mechanism oid */
+);
 
+OM_uint32
+gssint_get_mechanisms(
+       char *mechArray[],              /* array to populate with mechs */
+       int arrayLen                    /* length of passed in array */
+);
 
-gss_OID gss_find_mechanism_from_name_type (gss_OID); /* name_type */
+OM_uint32
+gssint_userok(
+       OM_uint32 *,            /* minor */
+       const gss_name_t,       /* name */
+       const char *,           /* user */
+       int *                   /* user_ok */
+);
 
-OM_uint32 gss_add_mech_name_type
-          (OM_uint32 *,        /* minor_status */
-           gss_OID,            /* name_type */
-           gss_OID             /* mech */
-              );
+OM_uint32
+gss_store_cred(
+       OM_uint32 *,            /* minor_status */
+       const gss_cred_id_t,    /* input_cred_handle */
+       gss_cred_usage_t,       /* cred_usage */
+       const gss_OID,          /* desired_mech */
+       OM_uint32,              /* overwrite_cred */
+       OM_uint32,              /* default_cred */
+       gss_OID_set *,          /* elements_stored */
+       gss_cred_usage_t *      /* cred_usage_stored */
+);
+
+int
+gssint_get_der_length(
+       unsigned char **,       /* buf */
+       unsigned int,           /* buf_len */
+       unsigned int *          /* bytes */
+);
+
+unsigned int
+gssint_der_length_size(unsigned int /* len */);
+
+int
+gssint_put_der_length(
+       unsigned int,           /* length */
+       unsigned char **,       /* buf */
+       unsigned int            /* max_len */
+);
 
 #endif /* _GSS_MECHGLUEP_H */
index 8e9da8852c482d26c4aa4b021302c6c1694eed04..ee981a507337cde3e766706c8dfbf11d3473b6d6 100644 (file)
@@ -1,3 +1,4 @@
+/* #pragma ident       "@(#)oid_ops.c  1.19    04/02/23 SMI" */
 /*
  * lib/gssapi/generic/oid_ops.c
  *
@@ -45,7 +46,8 @@ generic_gss_release_oid(minor_status, oid)
     OM_uint32  *minor_status;
     gss_OID    *oid;
 {
-    *minor_status = 0;
+    if (minor_status)
+       *minor_status = 0;
 
     if (*oid == GSS_C_NO_OID)
        return(GSS_S_COMPLETE);
@@ -59,9 +61,20 @@ generic_gss_release_oid(minor_status, oid)
      * descriptor.  This allows applications to freely mix their own heap-
      * allocated OID values with OIDs returned by GSS-API.
      */
-    if ((*oid != gss_nt_user_name) &&
-       (*oid != gss_nt_machine_uid_name) &&
-       (*oid != gss_nt_string_uid_name) &&
+
+    /*
+     * We use the official OID definitions instead of the unofficial OID
+     * defintions. But we continue to support the unofficial OID
+     * gss_nt_service_name just in case if some gss applications use
+     * the old OID.
+     */
+
+    if ((*oid != GSS_C_NT_USER_NAME) &&
+       (*oid != GSS_C_NT_MACHINE_UID_NAME) &&
+       (*oid != GSS_C_NT_STRING_UID_NAME) &&
+       (*oid != GSS_C_NT_HOSTBASED_SERVICE) &&
+       (*oid != GSS_C_NT_ANONYMOUS) &&
+       (*oid != GSS_C_NT_EXPORT_NAME) &&
        (*oid != gss_nt_service_name)) {
        free((*oid)->elements);
        free(*oid);
@@ -73,10 +86,13 @@ generic_gss_release_oid(minor_status, oid)
 OM_uint32
 generic_gss_copy_oid(minor_status, oid, new_oid)
        OM_uint32       *minor_status;
-       gss_OID         oid, *new_oid;
+       const gss_OID_desc * const oid;
+       gss_OID         *new_oid;
 {
        gss_OID         p;
 
+       *minor_status = 0;
+
        p = (gss_OID) malloc(sizeof(gss_OID_desc));
        if (!p) {
                *minor_status = ENOMEM;
@@ -86,7 +102,6 @@ generic_gss_copy_oid(minor_status, oid, new_oid)
        p->elements = malloc(p->length);
        if (!p->elements) {
                free(p);
-               *minor_status = ENOMEM;
                return GSS_S_FAILURE;
        }
        memcpy(p->elements, oid->elements, p->length);
@@ -100,9 +115,10 @@ generic_gss_create_empty_oid_set(minor_status, oid_set)
     OM_uint32  *minor_status;
     gss_OID_set        *oid_set;
 {
+    *minor_status = 0;
+
     if ((*oid_set = (gss_OID_set) malloc(sizeof(gss_OID_set_desc)))) {
        memset(*oid_set, 0, sizeof(gss_OID_set_desc));
-       *minor_status = 0;
        return(GSS_S_COMPLETE);
     }
     else {
@@ -114,12 +130,18 @@ generic_gss_create_empty_oid_set(minor_status, oid_set)
 OM_uint32
 generic_gss_add_oid_set_member(minor_status, member_oid, oid_set)
     OM_uint32  *minor_status;
-    gss_OID    member_oid;
+    const gss_OID_desc * const member_oid;
     gss_OID_set        *oid_set;
 {
     gss_OID    elist;
     gss_OID    lastel;
 
+    *minor_status = 0;
+
+    if (member_oid == NULL || member_oid->length == 0 ||
+       member_oid->elements == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_READ);
+
     elist = (*oid_set)->elements;
     /* Get an enlarged copy of the array */
     if (((*oid_set)->elements = (gss_OID) malloc(((*oid_set)->count+1) *
@@ -159,13 +181,21 @@ generic_gss_add_oid_set_member(minor_status, member_oid, oid_set)
 OM_uint32
 generic_gss_test_oid_set_member(minor_status, member, set, present)
     OM_uint32  *minor_status;
-    gss_OID    member;
+    const gss_OID_desc * const member;
     gss_OID_set        set;
     int                *present;
 {
-    size_t     i;
+    OM_uint32  i;
     int                result;
 
+    *minor_status = 0;
+
+    if (member == NULL || set == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_READ);
+
+    if (present == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
     result = 0;
     for (i=0; i<set->count; i++) {
        if ((set->elements[i].length == member->length) &&
@@ -177,7 +207,6 @@ generic_gss_test_oid_set_member(minor_status, member, set, present)
        }
     }
     *present = result;
-    *minor_status = 0;
     return(GSS_S_COMPLETE);
 }
 
@@ -187,17 +216,25 @@ generic_gss_test_oid_set_member(minor_status, member, set, present)
 OM_uint32
 generic_gss_oid_to_str(minor_status, oid, oid_str)
     OM_uint32          *minor_status;
-    gss_OID            oid;
+    const gss_OID_desc * const oid;
     gss_buffer_t       oid_str;
 {
     char               numstr[128];
-    unsigned long      number;
+    OM_uint32          number;
     int                        numshift;
-    size_t             string_length;
-    size_t             i;
+    OM_uint32 string_length;
+    OM_uint32 i;
     unsigned char      *cp;
     char               *bp;
 
+    *minor_status = 0;
+
+    if (oid == NULL || oid->length == 0 || oid->elements == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_READ);
+
+    if (oid_str == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
     /* Decoded according to krb5/gssapi_krb5.c */
 
     /* First determine the size of the string */
@@ -206,21 +243,20 @@ generic_gss_oid_to_str(minor_status, oid, oid_str)
     numshift = 0;
     cp = (unsigned char *) oid->elements;
     number = (unsigned long) cp[0];
-    sprintf(numstr, "%ld ", number/40);
+    sprintf(numstr, "%lu ", (unsigned long)number/40);
     string_length += strlen(numstr);
-    sprintf(numstr, "%ld ", number%40);
+    sprintf(numstr, "%lu ", (unsigned long)number%40);
     string_length += strlen(numstr);
     for (i=1; i<oid->length; i++) {
-       if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
+       if ((OM_uint32) (numshift+7) < (sizeof (OM_uint32)*8)) {/* XXX */
            number = (number << 7) | (cp[i] & 0x7f);
            numshift += 7;
        }
        else {
-           *minor_status = EINVAL;
            return(GSS_S_FAILURE);
        }
        if ((cp[i] & 0x80) == 0) {
-           sprintf(numstr, "%ld ", number);
+           sprintf(numstr, "%lu ", (unsigned long)number);
            string_length += strlen(numstr);
            number = 0;
            numshift = 0;
@@ -233,17 +269,17 @@ generic_gss_oid_to_str(minor_status, oid, oid_str)
     string_length += 4;
     if ((bp = (char *) malloc(string_length))) {
        strcpy(bp, "{ ");
-       number = (unsigned long) cp[0];
-       sprintf(numstr, "%ld ", number/40);
+       number = (OM_uint32) cp[0];
+       sprintf(numstr, "%lu ", (unsigned long)number/40);
        strcat(bp, numstr);
-       sprintf(numstr, "%ld ", number%40);
+       sprintf(numstr, "%lu ", (unsigned long)number%40);
        strcat(bp, numstr);
        number = 0;
        cp = (unsigned char *) oid->elements;
        for (i=1; i<oid->length; i++) {
            number = (number << 7) | (cp[i] & 0x7f);
            if ((cp[i] & 0x80) == 0) {
-               sprintf(numstr, "%ld ", number);
+               sprintf(numstr, "%lu ", (unsigned long)number);
                strcat(bp, numstr);
                number = 0;
            }
@@ -251,7 +287,6 @@ generic_gss_oid_to_str(minor_status, oid, oid_str)
        strcat(bp, "}");
        oid_str->length = strlen(bp)+1;
        oid_str->value = (void *) bp;
-       *minor_status = 0;
        return(GSS_S_COMPLETE);
     }
     *minor_status = ENOMEM;
@@ -264,7 +299,7 @@ generic_gss_str_to_oid(minor_status, oid_str, oid)
     gss_buffer_t       oid_str;
     gss_OID            *oid;
 {
-    char       *cp, *bp, *startp;
+    unsigned char      *cp, *bp, *startp;
     int                brace;
     long       numbuf;
     long       onumbuf;
@@ -272,8 +307,16 @@ generic_gss_str_to_oid(minor_status, oid_str, oid)
     int                index;
     unsigned char *op;
 
+    *minor_status = 0;
+
+    if (GSS_EMPTY_BUFFER(oid_str))
+       return (GSS_S_CALL_INACCESSIBLE_READ);
+
+    if (oid == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
     brace = 0;
-    bp = (char *) oid_str->value;
+    bp = oid_str->value;
     cp = bp;
     /* Skip over leading space */
     while ((bp < &cp[oid_str->length]) && isspace(*bp))
@@ -290,7 +333,7 @@ generic_gss_str_to_oid(minor_status, oid_str, oid)
     /*
      * The first two numbers are chewed up by the first octet.
      */
-    if (sscanf(bp, "%ld", &numbuf) != 1) {
+    if (sscanf((char *)bp, "%ld", &numbuf) != 1) {
        *minor_status = EINVAL;
        return(GSS_S_FAILURE);
     }
@@ -298,18 +341,18 @@ generic_gss_str_to_oid(minor_status, oid_str, oid)
        bp++;
     while ((bp < &cp[oid_str->length]) && isspace(*bp))
        bp++;
-    if (sscanf(bp, "%ld", &numbuf) != 1) {
+    if (sscanf((char *)bp, "%ld", &numbuf) != 1) {
        *minor_status = EINVAL;
        return(GSS_S_FAILURE);
     }
     while ((bp < &cp[oid_str->length]) && isdigit(*bp))
        bp++;
-    while ((bp < &cp[oid_str->length]) && isspace(*bp))
+    while ((bp < &cp[oid_str->length]) &&
+          (isspace(*bp) || *bp == '.'))
        bp++;
     nbytes++;
     while (isdigit(*bp)) {
-       if (sscanf(bp, "%ld", &numbuf) != 1) {
-           *minor_status = EINVAL;
+       if (sscanf((char *)bp, "%ld", &numbuf) != 1) {
            return(GSS_S_FAILURE);
        }
        while (numbuf) {
@@ -318,11 +361,11 @@ generic_gss_str_to_oid(minor_status, oid_str, oid)
        }
        while ((bp < &cp[oid_str->length]) && isdigit(*bp))
            bp++;
-       while ((bp < &cp[oid_str->length]) && isspace(*bp))
+       while ((bp < &cp[oid_str->length]) &&
+              (isspace(*bp) || *bp == '.'))
            bp++;
     }
     if (brace && (*bp != '}')) {
-       *minor_status = EINVAL;
        return(GSS_S_FAILURE);
     }
 
@@ -330,26 +373,26 @@ generic_gss_str_to_oid(minor_status, oid_str, oid)
      * Phew!  We've come this far, so the syntax is good.
      */
     if ((*oid = (gss_OID) malloc(sizeof(gss_OID_desc)))) {
-       if (((*oid)->elements = (void *) malloc((size_t) nbytes))) {
+       if (((*oid)->elements = (void *) malloc(nbytes))) {
            (*oid)->length = nbytes;
            op = (unsigned char *) (*oid)->elements;
            bp = startp;
-           sscanf(bp, "%ld", &numbuf);
+           (void) sscanf((char *)bp, "%ld", &numbuf);
            while (isdigit(*bp))
                bp++;
-           while (isspace(*bp))
+           while (isspace(*bp) || *bp == '.')
                bp++;
            onumbuf = 40*numbuf;
-           sscanf(bp, "%ld", &numbuf);
+           (void) sscanf((char *)bp, "%ld", &numbuf);
            onumbuf += numbuf;
            *op = (unsigned char) onumbuf;
            op++;
            while (isdigit(*bp))
                bp++;
-           while (isspace(*bp))
+           while (isspace(*bp) || *bp == '.')
                bp++;
            while (isdigit(*bp)) {
-               sscanf(bp, "%ld", &numbuf);
+               (void) sscanf((char *)bp, "%ld", &numbuf);
                nbytes = 0;
                /* Have to fill in the bytes msb-first */
                onumbuf = numbuf;
@@ -369,10 +412,9 @@ generic_gss_str_to_oid(minor_status, oid_str, oid)
                }
                while (isdigit(*bp))
                    bp++;
-               while (isspace(*bp))
+               while (isspace(*bp) || *bp == '.')
                    bp++;
            }
-           *minor_status = 0;
            return(GSS_S_COMPLETE);
        }
        else {
@@ -380,7 +422,82 @@ generic_gss_str_to_oid(minor_status, oid_str, oid)
            *oid = GSS_C_NO_OID;
        }
     }
-    *minor_status = ENOMEM;
     return(GSS_S_FAILURE);
 }
 
+/*
+ * Copyright 1993 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+OM_uint32
+gssint_copy_oid_set(
+    OM_uint32 *minor_status,
+    const gss_OID_set_desc * const oidset,
+    gss_OID_set *new_oidset
+    )
+{
+    gss_OID_set_desc *copy;
+    OM_uint32 minor = 0;
+    OM_uint32 major = GSS_S_COMPLETE;
+    OM_uint32 index;
+
+    if (minor_status)
+       *minor_status = 0;
+
+    if (oidset == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_READ);
+
+    if (new_oidset == NULL)
+       return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+    *new_oidset = NULL;
+
+    if ((copy = (gss_OID_set_desc *) calloc(1, sizeof (*copy))) == NULL) {
+       major = GSS_S_FAILURE;
+       goto done;
+    }
+
+    if ((copy->elements = (gss_OID_desc *)
+        calloc(oidset->count, sizeof (*copy->elements))) == NULL) {
+       major = GSS_S_FAILURE;
+       goto done;
+    }
+    copy->count = oidset->count;
+
+    for (index = 0; index < copy->count; index++) {
+       gss_OID_desc *out = &copy->elements[index];
+       gss_OID_desc *in = &oidset->elements[index];
+
+       if ((out->elements = (void *) malloc(in->length)) == NULL) {
+           major = GSS_S_FAILURE;
+           goto done;
+       }
+       (void) memcpy(out->elements, in->elements, in->length);
+       out->length = in->length;
+    }
+
+    *new_oidset = copy;
+done:
+    if (major != GSS_S_COMPLETE) {
+       (void) gss_release_oid_set(&minor, &copy);
+    }
+
+    return (major);
+}
diff --git a/src/lib/gssapi/spnego/Makefile.in b/src/lib/gssapi/spnego/Makefile.in
new file mode 100644 (file)
index 0000000..ba6fe9a
--- /dev/null
@@ -0,0 +1,36 @@
+thisconfigdir=../../..
+myfulldir=lib/gssapi/spnego
+mydir=lib/gssapi/spnego
+BUILDTOP=$(REL)..$(S)..$(S)..
+LOCALINCLUDES = -I. -I$(srcdir) -I$(srcdir)/.. -I../generic -I$(srcdir)/../generic -I../mechglue -I$(srcdir)/../mechglue
+
+##DOS##BUILDTOP = ..\..\..
+##DOS##PREFIXDIR=spnego
+##DOS##OBJFILE = ..\$(OUTPRE)spnego.lst
+
+##DOS##DLL_EXP_TYPE=GSS
+
+SRCS = $(srcdir)/spnego_mech.c
+
+OBJS = $(OUTPRE)spnego_mech.$(OBJEXT)
+
+STLIBOBJS = spnego_mech.o
+
+all-unix:: all-libobjs
+
+clean-unix:: clean-libobjs
+
+# @libobj_frag@
+# +++ Dependency line eater +++
+# 
+# Makefile dependencies follow.  This must be the last section in
+# the Makefile.in file
+#
+spnego_mech.so spnego_mech.po $(OUTPRE)spnego_mech.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-platform.h \
+  $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \
+  $(SRCTOP)/include/syslog.h $(srcdir)/../generic/gssapiP_generic.h \
+  $(srcdir)/../generic/gssapi_generic.h $(srcdir)/../mechglue/mechglue.h \
+  $(srcdir)/../mechglue/mglueP.h ../generic/gssapi_err_generic.h \
+  gssapiP_spnego.h spnego_mech.c
diff --git a/src/lib/gssapi/spnego/gssapiP_spnego.h b/src/lib/gssapi/spnego/gssapiP_spnego.h
new file mode 100644 (file)
index 0000000..2d3e5e2
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef        _GSSAPIP_SPNEGO_H_
+#define        _GSSAPIP_SPNEGO_H_
+
+/* #pragma ident       "@(#)gssapiP_spnego.h   1.3     03/09/18 SMI" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <gssapi/gssapi.h>
+#include <syslog.h>
+
+#define        SEC_CONTEXT_TOKEN 1
+#define        SPNEGO_SIZE_OF_INT 4
+
+#define        ACCEPT_COMPLETE 0
+#define        ACCEPT_INCOMPLETE 1
+#define        REJECT 2
+#define REQUEST_MIC 3
+#define        ACCEPT_DEFECTIVE_TOKEN 0xffffffffUL
+
+/*
+ * constants for der encoding/decoding routines.
+ */
+
+#define        MECH_OID                0x06
+#define        OCTET_STRING            0x04
+#define        CONTEXT                 0xa0
+#define        SEQUENCE                0x30
+#define        SEQUENCE_OF             0x30
+#define        BIT_STRING              0x03
+#define        BIT_STRING_LENGTH       0x02
+#define        BIT_STRING_PADDING      0x01
+#define        ENUMERATED              0x0a
+#define        ENUMERATION_LENGTH      1
+#define        HEADER_ID               0x60
+
+/*
+ * SPNEGO specific error codes (minor status codes)
+ */
+#define        ERR_SPNEGO_NO_MECHS_AVAILABLE           0x20000001
+#define        ERR_SPNEGO_NO_CREDS_ACQUIRED            0x20000002
+#define        ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR        0x20000003
+#define        ERR_SPNEGO_NEGOTIATION_FAILED           0x20000004
+#define        ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR       0x20000005
+
+/*
+ * send_token_flag is used to indicate in later steps what type
+ * of token, if any should be sent or processed.
+ * NO_TOKEN_SEND = no token should be sent
+ * INIT_TOKEN_SEND = initial token will be sent
+ * CONT_TOKEN_SEND = continuing tokens to be sent
+ * CHECK_MIC = no token to be sent, but have a MIC to check.
+ * ERROR_TOKEN_SEND = error token from peer needs to be sent.
+ */
+
+typedef        enum {NO_TOKEN_SEND, INIT_TOKEN_SEND, CONT_TOKEN_SEND,
+               CHECK_MIC, ERROR_TOKEN_SEND} send_token_flag;
+
+/*
+ * The Mech OID:
+ * { iso(1) org(3) dod(6) internet(1) security(5)
+ *  mechanism(5) spnego(2) }
+ */
+
+#define        SPNEGO_OID_LENGTH 6
+#define        SPNEGO_OID "\053\006\001\005\005\002"
+
+typedef void *spnego_token_t;
+
+/* spnego name structure for internal representation. */
+typedef struct {
+       gss_OID type;
+       gss_buffer_t buffer;
+       gss_OID mech_type;
+       gss_name_t      mech_name;
+} spnego_name_desc, *spnego_name_t;
+
+/* Structure for context handle */
+typedef struct {
+       OM_uint32       magic_num;
+       gss_buffer_desc DER_mechTypes;
+       gss_OID internal_mech;
+       gss_ctx_id_t ctx_handle;
+       char  *optionStr;
+       gss_cred_id_t default_cred;
+       int mic_reqd;
+       int mic_sent;
+       int mic_rcvd;
+       int firstpass;
+       int mech_complete;
+       int nego_done;
+       OM_uint32 ctx_flags;
+       gss_name_t internal_name;
+       gss_OID actual_mech;
+} spnego_gss_ctx_id_rec, *spnego_gss_ctx_id_t;
+
+/*
+ * The magic number must be less than a standard pagesize
+ * to avoid a possible collision with a real address.
+ */
+#define        SPNEGO_MAGIC_ID  0x00000fed
+
+/* SPNEGO oid structure */
+static const gss_OID_desc spnego_oids[] = {
+       {SPNEGO_OID_LENGTH, SPNEGO_OID},
+};
+
+const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;
+static const gss_OID_set_desc spnego_oidsets[] = {
+       {1, (gss_OID) spnego_oids+0},
+};
+const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
+
+#define        TWRITE_STR(ptr, str, len) \
+       memcpy((ptr), (char *)(str), (len)); \
+       (ptr) += (len);
+
+#ifdef DEBUG
+#define        dsyslog(a) syslog(LOG_DEBUG, a)
+#else
+#define        dsyslog(a)
+#define        SPNEGO_STATIC
+#endif /* DEBUG */
+
+/*
+ * declarations of internal name mechanism functions
+ */
+
+OM_uint32 spnego_gss_acquire_cred
+(
+       void *,                 /* spnego context */
+       OM_uint32 *,            /* minor_status */
+       gss_name_t,             /* desired_name */
+       OM_uint32,              /* time_req */
+       gss_OID_set,            /* desired_mechs */
+       gss_cred_usage_t,       /* cred_usage */
+       gss_cred_id_t *,        /* output_cred_handle */
+       gss_OID_set *,          /* actual_mechs */
+       OM_uint32 *             /* time_rec */
+);
+
+OM_uint32 spnego_gss_release_cred
+(
+       void *,                 /* spnego context */
+       OM_uint32 *,            /* minor_status */
+       /* CSTYLED */
+       gss_cred_id_t   *       /* cred_handle */
+);
+
+OM_uint32 spnego_gss_init_sec_context
+(
+       void *,                 /* spnego context */
+       OM_uint32 *,            /* minor_status */
+       gss_cred_id_t,          /* claimant_cred_handle */
+       gss_ctx_id_t *,         /* context_handle */
+       gss_name_t,             /* target_name */
+       gss_OID,                /* mech_type */
+       OM_uint32,              /* req_flags */
+       OM_uint32,              /* time_req */
+       gss_channel_bindings_t, /* input_chan_bindings */
+       gss_buffer_t,           /* input_token */
+       gss_OID *,              /* actual_mech_type */
+       gss_buffer_t,           /* output_token */
+       OM_uint32 *,            /* ret_flags */
+       OM_uint32 *             /* time_rec */
+);
+
+OM_uint32 spnego_gss_accept_sec_context
+(
+       void *,                 /* spnego context */
+       OM_uint32 *,            /* minor_status */
+       gss_ctx_id_t *,         /* context_handle */
+       gss_cred_id_t,          /* verifier_cred_handle */
+       gss_buffer_t,           /* input_token_buffer */
+       gss_channel_bindings_t, /* input_chan_bindings */
+       gss_name_t *,           /* src_name */
+       gss_OID *,              /* mech_type */
+       gss_buffer_t,           /* output_token */
+       OM_uint32 *,            /* ret_flags */
+       OM_uint32 *,            /* time_rec */
+       /* CSTYLED */
+       gss_cred_id_t *         /* delegated_cred_handle */
+);
+
+OM_uint32 spnego_gss_display_name
+(
+       void *,
+       OM_uint32 *,            /* minor_status */
+       gss_name_t,             /*  input_name */
+       gss_buffer_t,           /*  output_name_buffer */
+       gss_OID *               /* output_name_type */
+);
+
+OM_uint32 spnego_gss_display_status
+(
+       void *,                 /* spnego context */
+       OM_uint32 *,            /* minor_status */
+       OM_uint32,              /* status_value */
+       int,                    /* status_type */
+       gss_OID,                /* mech_type */
+       OM_uint32 *,            /* message_context */
+       gss_buffer_t            /* status_string */
+);
+
+OM_uint32 spnego_gss_import_name
+(
+       void *,                 /* spnego context */
+       OM_uint32 *,            /* minor_status */
+       gss_buffer_t,           /* input_name_buffer */
+       gss_OID,                /* input_name_type */
+       /* CSTYLED */
+       gss_name_t *            /* output_name */
+);
+
+OM_uint32 spnego_gss_release_name
+(
+       void *,                 /* spnego context */
+       OM_uint32 *,            /* minor_status */
+       /* CSTYLED */
+       gss_name_t *            /* input_name */
+);
+
+OM_uint32 spnego_gss_inquire_names_for_mech
+(
+       void *,                 /* spnego context */
+       OM_uint32 *,            /* minor_status */
+       gss_OID,                /* mechanism */
+       gss_OID_set *           /* name_types */
+);
+
+OM_uint32 spnego_gss_unseal
+(
+       void *context,
+       OM_uint32 *minor_status,
+       gss_ctx_id_t context_handle,
+       gss_buffer_t input_message_buffer,
+       gss_buffer_t output_message_buffer,
+       int *conf_state,
+       int *qop_state
+);
+
+OM_uint32 spnego_gss_seal
+(
+       void *context,
+       OM_uint32 *minor_status,
+       gss_ctx_id_t context_handle,
+       int conf_req_flag,
+       int qop_req,
+       gss_buffer_t input_message_buffer,
+       int *conf_state,
+       gss_buffer_t output_message_buffer
+);
+
+OM_uint32 spnego_gss_process_context_token
+(
+       void *context,
+       OM_uint32       *minor_status,
+       const gss_ctx_id_t context_handle,
+       const gss_buffer_t token_buffer
+);
+
+OM_uint32 spnego_gss_delete_sec_context
+(
+       void *context,
+       OM_uint32 *minor_status,
+       gss_ctx_id_t *context_handle,
+       gss_buffer_t output_token
+);
+
+OM_uint32 spnego_gss_context_time
+(
+       void *context,
+       OM_uint32       *minor_status,
+       const gss_ctx_id_t context_handle,
+       OM_uint32       *time_rec
+);
+
+OM_uint32 spnego_gss_export_sec_context
+(
+       void *context,
+       OM_uint32       *minor_status,
+       gss_ctx_id_t    *context_handle,
+       gss_buffer_t    interprocess_token
+);
+
+OM_uint32 spnego_gss_import_sec_context
+(
+       void                    *context,
+       OM_uint32               *minor_status,
+       const gss_buffer_t      interprocess_token,
+       gss_ctx_id_t            *context_handle
+);
+
+OM_uint32 spnego_gss_inquire_context
+(
+       void            *context,
+       OM_uint32       *minor_status,
+       const gss_ctx_id_t context_handle,
+       gss_name_t      *src_name,
+       gss_name_t      *targ_name,
+       OM_uint32       *lifetime_rec,
+       gss_OID         *mech_type,
+       OM_uint32       *ctx_flags,
+       int             *locally_initiated,
+       int             *open
+);
+
+OM_uint32 spnego_gss_wrap_size_limit
+(
+       void            *context,
+       OM_uint32       *minor_status,
+       const gss_ctx_id_t context_handle,
+       int             conf_req_flag,
+       gss_qop_t       qop_req,
+       OM_uint32       req_output_size,
+       OM_uint32       *max_input_size
+);
+
+OM_uint32 spnego_gss_sign
+(
+       void *context,
+       OM_uint32 *minor_status,
+       const gss_ctx_id_t context_handle,
+       int  qop_req,
+       const gss_buffer_t message_buffer,
+       gss_buffer_t message_token
+);
+
+OM_uint32 spnego_gss_verify
+(
+       void *context,
+       OM_uint32 *minor_status,
+       const gss_ctx_id_t context_handle,
+       const gss_buffer_t msg_buffer,
+       const gss_buffer_t token_buffer,
+       int *qop_state
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GSSAPIP_SPNEGO_H_ */
diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c
new file mode 100644 (file)
index 0000000..ec8b608
--- /dev/null
@@ -0,0 +1,2843 @@
+/*
+ * Copyright (C) 2006 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * A module that implements the spnego security mechanism.
+ * It is used to negotiate the security mechanism between
+ * peers using the GSS-API.
+ *
+ */
+
+/* #pragma ident       "@(#)spnego_mech.c      1.7     04/09/28 SMI" */
+
+#include       <assert.h>
+#include       <stdio.h>
+#include       <stdlib.h>
+#include       <string.h>
+#include       <krb5.h>
+#include       "gssapiP_spnego.h"
+#include       <mglueP.h>
+#include       <gssapi_err_generic.h>
+
+#undef g_token_size
+#undef g_verify_token_header
+#undef g_make_token_header
+
+#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
+typedef const gss_OID_desc *gss_OID_const;
+
+/* der routines defined in libgss */
+extern unsigned int gssint_der_length_size(OM_uint32);
+extern int gssint_get_der_length(unsigned char **, OM_uint32, OM_uint32*);
+extern int gssint_put_der_length(OM_uint32, unsigned char **, OM_uint32);
+
+
+/* private routines for spnego_mechanism */
+static spnego_token_t make_spnego_token(char *);
+static gss_buffer_desc make_err_msg(char *);
+static int g_token_size(gss_OID_const, OM_uint32);
+static int g_make_token_header(gss_OID_const, int, unsigned char **, int);
+static int g_verify_token_header(gss_OID_const, int *, unsigned char **,
+                                int, int);
+static int g_verify_neg_token_init(unsigned char **, int);
+static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);
+static gss_buffer_t get_input_token(unsigned char **, int);
+static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, int);
+static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);
+static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
+       gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
+static void release_spnego_ctx(spnego_gss_ctx_id_t *);
+static void check_spnego_options(spnego_gss_ctx_id_t);
+static spnego_gss_ctx_id_t create_spnego_ctx(void);
+static int put_req_flags(unsigned char **, OM_uint32, int);
+static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
+static int put_input_token(unsigned char **, gss_buffer_t, int);
+static int put_mech_oid(unsigned char **, gss_OID_const, int);
+static int put_negResult(unsigned char **, OM_uint32, int);
+
+static OM_uint32
+process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
+           gss_buffer_t *, OM_uint32 *, send_token_flag *);
+static OM_uint32
+handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
+          gss_buffer_t *, OM_uint32 *, send_token_flag *);
+
+static OM_uint32
+init_ctx_new(OM_uint32 *, gss_cred_id_t, gss_ctx_id_t *,
+            gss_OID_set *, send_token_flag *);
+static OM_uint32
+init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
+             gss_buffer_t *, gss_buffer_t *,
+             OM_uint32 *, send_token_flag *);
+static OM_uint32
+init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
+             gss_buffer_t *, gss_buffer_t *,
+             OM_uint32 *, send_token_flag *);
+static OM_uint32
+init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
+                 gss_OID, gss_buffer_t *, gss_buffer_t *,
+                 OM_uint32 *, send_token_flag *);
+static OM_uint32
+init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t,
+                  gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
+                  gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
+                  OM_uint32 *, send_token_flag *);
+
+static OM_uint32
+acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
+           gss_cred_id_t, gss_buffer_t *,
+           gss_buffer_t *, OM_uint32 *, send_token_flag *);
+static OM_uint32
+acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
+            gss_buffer_t *, gss_buffer_t *,
+            OM_uint32 *, send_token_flag *);
+static OM_uint32
+acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
+               OM_uint32 *, send_token_flag *);
+static OM_uint32
+acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t,
+                gss_buffer_t, gss_OID *, gss_buffer_t,
+                OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
+                OM_uint32 *, send_token_flag *);
+
+static gss_OID
+negotiate_mech_type(OM_uint32 *, gss_OID_set, gss_OID_set,
+               OM_uint32 *);
+static int
+g_get_tag_and_length(unsigned char **, int, int, int *);
+
+static int
+make_spnego_tokenInit_msg(spnego_gss_ctx_id_t, gss_buffer_t,
+                       OM_uint32, gss_buffer_t, send_token_flag,
+                       gss_buffer_t);
+static int
+make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
+                       gss_buffer_t, send_token_flag,
+                       gss_buffer_t);
+
+static OM_uint32
+get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
+                gss_OID_set *, OM_uint32 *, gss_buffer_t *,
+                gss_buffer_t *);
+static OM_uint32
+get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
+                OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);
+
+/*
+ * The Mech OID for SPNEGO:
+ * { iso(1) org(3) dod(6) internet(1) security(5)
+ *  mechanism(5) spnego(2) }
+ */
+static struct gss_config spnego_mechanism =
+{
+       400, "spnego",
+       {SPNEGO_OID_LENGTH, SPNEGO_OID},
+       NULL,
+       spnego_gss_acquire_cred,
+       spnego_gss_release_cred,
+       spnego_gss_init_sec_context,
+       spnego_gss_accept_sec_context,
+       NULL,                           /* gss_process_context_token */
+       spnego_gss_delete_sec_context,  /* gss_delete_sec_context */
+       spnego_gss_context_time,        /* gss_context_time */
+       spnego_gss_sign,                /* gss_sign */
+       spnego_gss_verify,              /* gss_verify */
+       spnego_gss_seal,                /* gss_seal */
+       spnego_gss_unseal,              /* gss_unseal */
+       spnego_gss_display_status,
+       NULL,                           /* gss_indicate_mechs */
+       NULL,                           /* gss_compare_name */
+       spnego_gss_display_name,
+       spnego_gss_import_name,
+       spnego_gss_release_name,
+       NULL,                           /* gss_inquire_cred */
+       NULL,                           /* gss_add_cred */
+       spnego_gss_export_sec_context,  /* gss_export_sec_context */
+       spnego_gss_import_sec_context,  /* gss_import_sec_context */
+       NULL,                           /* gss_inquire_cred_by_mech */
+       spnego_gss_inquire_names_for_mech,
+       spnego_gss_inquire_context,     /* gss_inquire_context */
+       NULL,                           /* gss_internal_release_oid */
+       spnego_gss_wrap_size_limit,     /* gss_wrap_size_limit */
+       NULL,                           /* gss_pname_to_uid */
+       NULL,                           /* gssint_userok */
+       NULL,                           /* gss_export_name */
+       NULL,                           /* gss_store_cred */
+};
+
+static gss_mechanism spnego_mech_configs[] = {
+       &spnego_mechanism, NULL
+};
+
+#if 1
+#define gssint_get_mech_configs spnego_gss_get_mech_configs
+#endif
+
+gss_mechanism *
+gssint_get_mech_configs(void)
+{
+       return spnego_mech_configs;
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_acquire_cred(void *ctx,
+                       OM_uint32 *minor_status,
+                       gss_name_t desired_name,
+                       OM_uint32 time_req,
+                       gss_OID_set desired_mechs,
+                       gss_cred_usage_t cred_usage,
+                       gss_cred_id_t *output_cred_handle,
+                       gss_OID_set *actual_mechs,
+                       OM_uint32 *time_rec)
+{
+       OM_uint32 status;
+       gss_OID_set amechs;
+       dsyslog("Entering spnego_gss_acquire_cred\n");
+
+       if (actual_mechs)
+               *actual_mechs = NULL;
+
+       if (time_rec)
+               *time_rec = 0;
+
+       /*
+        * If the user did not specify a list of mechs,
+        * use get_available_mechs to collect a list of
+        * mechs for which creds are available.
+        */
+       if (desired_mechs == GSS_C_NULL_OID_SET) {
+               status = get_available_mechs(minor_status,
+                               desired_name, cred_usage,
+                               output_cred_handle, &amechs);
+       } else {
+               /*
+                * The caller gave a specific list of mechanisms,
+                * so just get whatever creds are available.
+                * gss_acquire_creds will return the subset of mechs for
+                * which the given 'output_cred_handle' is valid.
+                */
+               status = gss_acquire_cred(minor_status,
+                               desired_name, time_req,
+                               desired_mechs, cred_usage,
+                               output_cred_handle, &amechs,
+                               time_rec);
+       }
+
+       if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
+               (void) gssint_copy_oid_set(minor_status, amechs, actual_mechs);
+       }
+       (void) gss_release_oid_set(minor_status, &amechs);
+
+       dsyslog("Leaving spnego_gss_acquire_cred\n");
+       return (status);
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_release_cred(void *ctx,
+                       OM_uint32 *minor_status,
+                       gss_cred_id_t *cred_handle)
+{
+       OM_uint32 status;
+
+       dsyslog("Entering spnego_gss_release_cred\n");
+
+       if (minor_status == NULL || cred_handle == NULL)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+       *minor_status = 0;
+
+       if (*cred_handle == GSS_C_NO_CREDENTIAL)
+               return (GSS_S_COMPLETE);
+
+       status = gss_release_cred(minor_status, cred_handle);
+
+       dsyslog("Leaving spnego_gss_release_cred\n");
+       return (status);
+}
+
+static void
+check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
+{
+       spnego_ctx->optionStr = gssint_get_modOptions(
+               (const gss_OID)&spnego_oids[0]);
+}
+
+static spnego_gss_ctx_id_t
+create_spnego_ctx(void)
+{
+       spnego_gss_ctx_id_t spnego_ctx = NULL;
+       spnego_ctx = (spnego_gss_ctx_id_t)
+               malloc(sizeof (spnego_gss_ctx_id_rec));
+
+       if (spnego_ctx == NULL) {
+               return (NULL);
+       }
+
+       spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
+       spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
+       spnego_ctx->internal_mech = NULL;
+       spnego_ctx->optionStr = NULL;
+       spnego_ctx->DER_mechTypes.length = 0;
+       spnego_ctx->DER_mechTypes.value = NULL;
+       spnego_ctx->default_cred = GSS_C_NO_CREDENTIAL;
+       spnego_ctx->mic_reqd = 0;
+       spnego_ctx->mic_sent = 0;
+       spnego_ctx->mic_rcvd = 0;
+       spnego_ctx->mech_complete = 0;
+       spnego_ctx->nego_done = 0;
+       spnego_ctx->internal_name = GSS_C_NO_NAME;
+       spnego_ctx->actual_mech = GSS_C_NO_OID;
+
+       check_spnego_options(spnego_ctx);
+
+       return (spnego_ctx);
+}
+
+/*
+ * Both initiator and acceptor call here to verify and/or create
+ * mechListMIC, and to consistency-check the MIC state.
+ */
+static OM_uint32
+handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
+          int send_mechtok, spnego_gss_ctx_id_t sc,
+          gss_buffer_t *mic_out,
+          OM_uint32 *negState, send_token_flag *tokflag)
+{
+       OM_uint32 ret;
+
+       ret = GSS_S_FAILURE;
+       *mic_out = GSS_C_NO_BUFFER;
+       if (mic_in != GSS_C_NO_BUFFER) {
+               if (sc->mic_rcvd) {
+                       /* Reject MIC if we've already received a MIC. */
+                       *negState = REJECT;
+                       *tokflag = ERROR_TOKEN_SEND;
+                       return GSS_S_DEFECTIVE_TOKEN;
+               }
+       } else if (sc->mic_reqd && !send_mechtok) {
+               /*
+                * If the peer sends the final mechanism token, it
+                * must send the MIC with that token if the
+                * negotiation requires MICs.
+                */
+               *negState = REJECT;
+               *tokflag = ERROR_TOKEN_SEND;
+               return GSS_S_DEFECTIVE_TOKEN;
+       }
+       ret = process_mic(minor_status, mic_in, sc, mic_out,
+                         negState, tokflag);
+       if (ret != GSS_S_COMPLETE) {
+               return ret;
+       }
+       if (sc->mic_reqd) {
+               assert(sc->mic_sent || sc->mic_rcvd);
+       }
+       if (sc->mic_sent && sc->mic_rcvd) {
+               ret = GSS_S_COMPLETE;
+               *negState = ACCEPT_COMPLETE;
+               if (*mic_out == GSS_C_NO_BUFFER) {
+                       /*
+                        * We sent a MIC on the previous pass; we
+                        * shouldn't be sending a mechanism token.
+                        */
+                       assert(!send_mechtok);
+                       *tokflag = NO_TOKEN_SEND;
+               } else {
+                       *tokflag = CONT_TOKEN_SEND;
+               }
+       } else if (sc->mic_reqd) {
+               *negState = ACCEPT_INCOMPLETE;
+               ret = GSS_S_CONTINUE_NEEDED;
+       } else if (*negState == ACCEPT_COMPLETE) {
+               ret = GSS_S_COMPLETE;
+       } else {
+               ret = GSS_S_CONTINUE_NEEDED;
+       }
+       return ret;
+}
+
+/*
+ * Perform the actual verification and/or generation of mechListMIC.
+ */
+static OM_uint32
+process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
+           spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
+           OM_uint32 *negState, send_token_flag *tokflag)
+{
+       OM_uint32 ret, tmpmin;
+       gss_qop_t qop_state;
+       gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
+
+       ret = GSS_S_FAILURE;
+       if (mic_in != GSS_C_NO_BUFFER) {
+               ret = gss_verify_mic(minor_status, sc->ctx_handle,
+                                    &sc->DER_mechTypes,
+                                    mic_in, &qop_state);
+               if (ret != GSS_S_COMPLETE) {
+                       *negState = REJECT;
+                       *tokflag = ERROR_TOKEN_SEND;
+                       return ret;
+               }
+               /* If we got a MIC, we must send a MIC. */
+               sc->mic_reqd = 1;
+               sc->mic_rcvd = 1;
+       }
+       if (sc->mic_reqd && !sc->mic_sent) {
+               ret = gss_get_mic(minor_status, sc->ctx_handle,
+                                 GSS_C_QOP_DEFAULT,
+                                 &sc->DER_mechTypes,
+                                 &tmpmic);
+               if (ret != GSS_S_COMPLETE) {
+                       gss_release_buffer(&tmpmin, &tmpmic);
+                       *tokflag = NO_TOKEN_SEND;
+                       return ret;
+               }
+               *mic_out = malloc(sizeof(gss_buffer_desc));
+               if (*mic_out == GSS_C_NO_BUFFER) {
+                       gss_release_buffer(&tmpmin, &tmpmic);
+                       *tokflag = NO_TOKEN_SEND;
+                       return GSS_S_FAILURE;
+               }
+               **mic_out = tmpmic;
+               sc->mic_sent = 1;
+       }
+       return GSS_S_COMPLETE;
+}
+
+/*
+ * Initial call to spnego_gss_init_sec_context().
+ */
+static OM_uint32
+init_ctx_new(OM_uint32 *minor_status,
+            gss_cred_id_t cred,
+            gss_ctx_id_t *ctx,
+            gss_OID_set *mechSet,
+            send_token_flag *tokflag)
+{
+       OM_uint32 ret, tmpmin;
+       gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
+       spnego_gss_ctx_id_t sc = NULL;
+
+       /* determine negotiation mech set */
+       if (cred == GSS_C_NO_CREDENTIAL) {
+               ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
+                                         GSS_C_INITIATE, &creds, mechSet);
+               gss_release_cred(&tmpmin, &creds);
+       } else {
+               /*
+                * Use the list of mechs included in the cred that we
+                * were given.
+                */
+               ret = gss_inquire_cred(minor_status, cred,
+                                      NULL, NULL, NULL, mechSet);
+       }
+       if (ret != GSS_S_COMPLETE)
+               return ret;
+
+       sc = create_spnego_ctx();
+       if (sc == NULL)
+               return GSS_S_FAILURE;
+
+       /*
+        * need to pull the first mech from mechSet to do first
+        * gss_init_sec_context()
+        */
+       ret = generic_gss_copy_oid(minor_status, (*mechSet)->elements,
+                                  &sc->internal_mech);
+       if (ret != GSS_S_COMPLETE)
+               goto cleanup;
+
+       if (put_mech_set(*mechSet, &sc->DER_mechTypes) < 0) {
+               generic_gss_release_oid(&tmpmin, &sc->internal_mech);
+               ret = GSS_S_FAILURE;
+               goto cleanup;
+       }
+       /*
+        * The actual context is not yet determined, set the output
+        * context handle to refer to the spnego context itself.
+        */
+       sc->ctx_handle = GSS_C_NO_CONTEXT;
+       *ctx = (gss_ctx_id_t)sc;
+       *tokflag = INIT_TOKEN_SEND;
+       ret = GSS_S_CONTINUE_NEEDED;
+
+cleanup:
+       gss_release_oid_set(&tmpmin, mechSet);
+       return ret;
+}
+
+/*
+ * Called by second and later calls to spnego_gss_init_sec_context()
+ * to decode reply and update state.
+ */
+static OM_uint32
+init_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf,
+             gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
+             OM_uint32 *negState, send_token_flag *tokflag)
+{
+       OM_uint32 ret, tmpmin, acc_negState;
+       unsigned char *ptr;
+       spnego_gss_ctx_id_t sc;
+       gss_OID supportedMech = GSS_C_NO_OID;
+
+       sc = (spnego_gss_ctx_id_t)*ctx;
+       *negState = REJECT;
+       *tokflag = ERROR_TOKEN_SEND;
+
+       ptr = buf->value;
+       ret = get_negTokenResp(minor_status, ptr, buf->length,
+                              &acc_negState, &supportedMech,
+                              responseToken, mechListMIC);
+       if (ret != GSS_S_COMPLETE)
+               goto cleanup;
+       if (acc_negState == ACCEPT_DEFECTIVE_TOKEN &&
+           supportedMech == GSS_C_NO_OID &&
+           *responseToken == GSS_C_NO_BUFFER &&
+           *mechListMIC == GSS_C_NO_BUFFER) {
+               /* Reject "empty" token. */
+               ret = GSS_S_DEFECTIVE_TOKEN;
+       }
+       if (acc_negState == REJECT) {
+               *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
+               *tokflag = NO_TOKEN_SEND;
+               ret = GSS_S_FAILURE;
+               goto cleanup;
+       }
+       /*
+        * nego_done is false for the first call to init_ctx_cont()
+        */
+       if (!sc->nego_done) {
+               ret = init_ctx_nego(minor_status, sc,
+                                   acc_negState,
+                                   supportedMech, responseToken,
+                                   mechListMIC,
+                                   negState, tokflag);
+       } else if (!sc->mech_complete &&
+                  *responseToken == GSS_C_NO_BUFFER) {
+               /*
+                * mech not finished and mech token missing
+                */
+               ret = GSS_S_DEFECTIVE_TOKEN;
+       } else {
+               *negState = ACCEPT_INCOMPLETE;
+               *tokflag = CONT_TOKEN_SEND;
+               ret = GSS_S_CONTINUE_NEEDED;
+       }
+cleanup:
+       if (supportedMech != GSS_C_NO_OID)
+               generic_gss_release_oid(&tmpmin, &supportedMech);
+       return ret;
+}
+
+/*
+ * Consistency checking and mechanism negotiation handling for second
+ * call of spnego_gss_init_sec_context().  Call init_ctx_reselect() to
+ * update internal state if acceptor has counter-proposed.
+ */
+static OM_uint32
+init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
+             OM_uint32 acc_negState, gss_OID supportedMech,
+             gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
+             OM_uint32 *negState, send_token_flag *tokflag)
+{
+       OM_uint32 ret;
+
+       *negState = REJECT;
+       *tokflag = ERROR_TOKEN_SEND;
+       ret = GSS_S_DEFECTIVE_TOKEN;
+       /*
+        * Both supportedMech and negState must be present in first
+        * acceptor token.
+        */
+       if (supportedMech == GSS_C_NO_OID) {
+               *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
+               return GSS_S_DEFECTIVE_TOKEN;
+       }
+       if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
+               *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
+               return GSS_S_DEFECTIVE_TOKEN;
+       }
+       if (!g_OID_equal(supportedMech, sc->internal_mech)) {
+               ret = init_ctx_reselect(minor_status, sc,
+                                       acc_negState, supportedMech,
+                                       responseToken, mechListMIC,
+                                       negState, tokflag);
+
+       } else if (*responseToken == GSS_C_NO_BUFFER) {
+               if (sc->mech_complete) {
+                       /*
+                        * Mech completed on first call to its
+                        * init_sec_context().  Acceptor sends no mech
+                        * token.
+                        */
+                       *negState = ACCEPT_COMPLETE;
+                       *tokflag = NO_TOKEN_SEND;
+                       ret = GSS_S_COMPLETE;
+               } else {
+                       /*
+                        * Reject missing mech token when optimistic
+                        * mech selected.
+                        */
+                       *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+               }
+       } else if (sc->mech_complete) {
+               /* Reject spurious mech token. */
+               ret = GSS_S_DEFECTIVE_TOKEN;
+       } else {
+               *negState = ACCEPT_INCOMPLETE;
+               *tokflag = CONT_TOKEN_SEND;
+               ret = GSS_S_CONTINUE_NEEDED;
+       }
+       sc->nego_done = 1;
+       return ret;
+}
+
+/*
+ * Handle acceptor's counter-proposal of an alternative mechanism.
+ */
+static OM_uint32
+init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
+                 OM_uint32 acc_negState, gss_OID supportedMech,
+                 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
+                 OM_uint32 *negState, send_token_flag *tokflag)
+{
+       OM_uint32 ret, tmpmin;
+
+       generic_gss_release_oid(&tmpmin, &sc->internal_mech);
+       gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
+                              GSS_C_NO_BUFFER);
+
+       ret = generic_gss_copy_oid(minor_status, supportedMech,
+                                  &sc->internal_mech);
+       if (ret != GSS_S_COMPLETE) {
+               sc->internal_mech = GSS_C_NO_OID;
+               *tokflag = NO_TOKEN_SEND;
+               return ret;
+       }
+       if (*responseToken != GSS_C_NO_BUFFER) {
+               /* Reject spurious mech token. */
+               return GSS_S_DEFECTIVE_TOKEN;
+       }
+       /*
+        * Windows 2003 and earlier don't correctly send a
+        * negState of request-mic when counter-proposing a
+        * mechanism.  They probably don't handle mechListMICs
+        * properly either.
+        */
+       if (acc_negState != REQUEST_MIC)
+               return GSS_S_DEFECTIVE_TOKEN;
+
+       sc->mech_complete = 0;
+       sc->mic_reqd = 1;
+       *negState = REQUEST_MIC;
+       *tokflag = CONT_TOKEN_SEND;
+       return GSS_S_CONTINUE_NEEDED;
+}
+
+/*
+ * Wrap call to mechanism gss_init_sec_context() and update state
+ * accordingly.
+ */
+static OM_uint32
+init_ctx_call_init(OM_uint32 *minor_status,
+                  spnego_gss_ctx_id_t sc,
+                  gss_cred_id_t claimant_cred_handle,
+                  gss_name_t target_name,
+                  OM_uint32 req_flags,
+                  OM_uint32 time_req,
+                  gss_buffer_t mechtok_in,
+                  gss_OID *actual_mech,
+                  gss_buffer_t mechtok_out,
+                  OM_uint32 *ret_flags,
+                  OM_uint32 *time_rec,
+                  OM_uint32 *negState,
+                  send_token_flag *send_token)
+{
+       OM_uint32 ret;
+
+       ret = gss_init_sec_context(minor_status,
+                                  claimant_cred_handle,
+                                  &sc->ctx_handle,
+                                  target_name,
+                                  sc->internal_mech,
+                                  (req_flags | GSS_C_INTEG_FLAG),
+                                  time_req,
+                                  GSS_C_NO_CHANNEL_BINDINGS,
+                                  mechtok_in,
+                                  &sc->actual_mech,
+                                  mechtok_out,
+                                  &sc->ctx_flags,
+                                  time_rec);
+       if (ret == GSS_S_COMPLETE) {
+               sc->mech_complete = 1;
+               if (ret_flags != NULL)
+                       *ret_flags = sc->ctx_flags;
+               /*
+                * If this isn't the first time we've been called,
+                * we're done unless a MIC needs to be
+                * generated/handled.
+                */
+               if (*send_token == CONT_TOKEN_SEND &&
+                   (!sc->mic_reqd ||
+                    !(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
+
+                       *negState = ACCEPT_COMPLETE;
+                       ret = GSS_S_COMPLETE;
+                       if (mechtok_out->length == 0) {
+                               *send_token = NO_TOKEN_SEND;
+                       }
+               } else {
+                       *negState = ACCEPT_INCOMPLETE;
+                       ret = GSS_S_CONTINUE_NEEDED;
+               }
+       } else if (ret != GSS_S_CONTINUE_NEEDED) {
+               if (*send_token == INIT_TOKEN_SEND) {
+                       /* Don't output token on error if first call. */
+                       *send_token = NO_TOKEN_SEND;
+               } else {
+                       *send_token = ERROR_TOKEN_SEND;
+               }
+               *negState = REJECT;
+       }
+       return ret;
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_init_sec_context(void *ct,
+                       OM_uint32 *minor_status,
+                       gss_cred_id_t claimant_cred_handle,
+                       gss_ctx_id_t *context_handle,
+                       gss_name_t target_name,
+                       gss_OID mech_type,
+                       OM_uint32 req_flags,
+                       OM_uint32 time_req,
+                       gss_channel_bindings_t input_chan_bindings,
+                       gss_buffer_t input_token,
+                       gss_OID *actual_mech,
+                       gss_buffer_t output_token,
+                       OM_uint32 *ret_flags,
+                       OM_uint32 *time_rec)
+{
+       /*
+        * send_token is used to indicate in later steps
+        * what type of token, if any should be sent or processed.
+        * NO_TOKEN_SEND = no token should be sent
+        * INIT_TOKEN_SEND = initial token will be sent
+        * CONT_TOKEN_SEND = continuing tokens to be sent
+        * CHECK_MIC = no token to be sent, but have a MIC to check.
+        */
+       send_token_flag send_token = NO_TOKEN_SEND;
+       OM_uint32 tmpmin, ret, negState;
+       gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
+       gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
+       gss_OID_set mechSet = GSS_C_NO_OID_SET;
+       spnego_gss_ctx_id_t spnego_ctx = NULL;
+
+       dsyslog("Entering init_sec_context\n");
+
+       mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
+       negState = REJECT;
+
+       if (minor_status != NULL)
+               *minor_status = 0;
+       if (output_token != GSS_C_NO_BUFFER) {
+               output_token->length = 0;
+               output_token->value = NULL;
+       }
+       if (minor_status == NULL ||
+           output_token == GSS_C_NO_BUFFER ||
+           context_handle == NULL)
+               return GSS_S_CALL_INACCESSIBLE_WRITE;
+
+       if (actual_mech != NULL)
+               *actual_mech = GSS_C_NO_OID;
+
+       if (*context_handle == GSS_C_NO_CONTEXT) {
+               ret = init_ctx_new(minor_status, claimant_cred_handle,
+                                  context_handle, &mechSet, &send_token);
+               if (ret != GSS_S_CONTINUE_NEEDED) {
+                       goto cleanup;
+               }
+       } else {
+               ret = init_ctx_cont(minor_status, context_handle,
+                                   input_token, &mechtok_in,
+                                   &mechListMIC_in, &negState, &send_token);
+               if (HARD_ERROR(ret)) {
+                       goto cleanup;
+               }
+       }
+       spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
+       if (!spnego_ctx->mech_complete) {
+               ret = init_ctx_call_init(
+                       minor_status, spnego_ctx,
+                       claimant_cred_handle,
+                       target_name, req_flags,
+                       time_req, mechtok_in,
+                       actual_mech, &mechtok_out,
+                       ret_flags, time_rec,
+                       &negState, &send_token);
+       }
+       /* create mic/check mic */
+       if (!HARD_ERROR(ret) && spnego_ctx->mech_complete &&
+           (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
+
+               ret = handle_mic(minor_status,
+                                mechListMIC_in,
+                                (mechtok_out.length != 0),
+                                spnego_ctx, &mechListMIC_out,
+                                &negState, &send_token);
+       }
+cleanup:
+       if (send_token == INIT_TOKEN_SEND) {
+               if (make_spnego_tokenInit_msg(spnego_ctx,
+                                             mechListMIC_out,
+                                             req_flags,
+                                             &mechtok_out, send_token,
+                                             output_token) < 0) {
+
+                       ret = GSS_S_FAILURE;
+               }
+       } else if (send_token != NO_TOKEN_SEND) {
+               if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
+                                             &mechtok_out, mechListMIC_out,
+                                             send_token,
+                                             output_token) < 0) {
+                       ret = GSS_S_FAILURE;
+               }
+       }
+       if (ret == GSS_S_COMPLETE) {
+               /*
+                * Now, switch the output context to refer to the
+                * negotiated mechanism's context.
+                */
+               *context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle;
+               if (actual_mech != NULL)
+                       *actual_mech = spnego_ctx->actual_mech;
+               release_spnego_ctx(&spnego_ctx);
+       } else if (ret != GSS_S_CONTINUE_NEEDED) {
+               if (spnego_ctx != NULL) {
+                       gss_delete_sec_context(&tmpmin,
+                                              &spnego_ctx->ctx_handle,
+                                              GSS_C_NO_BUFFER);
+                       release_spnego_ctx(&spnego_ctx);
+               }
+               *context_handle = GSS_C_NO_CONTEXT;
+       }
+       if (mechtok_in != GSS_C_NO_BUFFER) {
+               gss_release_buffer(&tmpmin, mechtok_in);
+               free(mechtok_in);
+       }
+       if (mechListMIC_in != GSS_C_NO_BUFFER) {
+               gss_release_buffer(&tmpmin, mechListMIC_in);
+               free(mechListMIC_in);
+       }
+       if (mechListMIC_out != GSS_C_NO_BUFFER) {
+               gss_release_buffer(&tmpmin, mechListMIC_out);
+               free(mechListMIC_out);
+       }
+       if (mechSet != GSS_C_NO_OID_SET) {
+               gss_release_oid_set(&tmpmin, &mechSet);
+       }
+       return ret;
+} /* init_sec_context */
+
+/*
+ * Set negState to REJECT if the token is defective, else
+ * ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's
+ * preferred mechanism is supported.
+ */
+static OM_uint32
+acc_ctx_new(OM_uint32 *minor_status,
+           gss_buffer_t buf,
+           gss_ctx_id_t *ctx,
+           gss_cred_id_t cred,
+           gss_buffer_t *mechToken,
+           gss_buffer_t *mechListMIC,
+           OM_uint32 *negState,
+           send_token_flag *return_token)
+{
+       OM_uint32 tmpmin, ret, req_flags;
+       gss_OID_set supported_mechSet, mechTypes;
+       gss_buffer_desc der_mechTypes;
+       gss_OID mech_wanted;
+       spnego_gss_ctx_id_t sc = NULL;
+
+       *ctx = GSS_C_NO_CONTEXT;
+       ret = GSS_S_DEFECTIVE_TOKEN;
+       der_mechTypes.length = 0;
+       der_mechTypes.value = NULL;
+       *mechToken = *mechListMIC = GSS_C_NO_BUFFER;
+       supported_mechSet = mechTypes = GSS_C_NO_OID_SET;
+       *return_token = ERROR_TOKEN_SEND;
+       *negState = REJECT;
+       *minor_status = 0;
+
+       ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
+                              &mechTypes, &req_flags,
+                              mechToken, mechListMIC);
+       if (ret != GSS_S_COMPLETE) {
+               goto cleanup;
+       }
+       if (cred != GSS_C_NO_CREDENTIAL) {
+               ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
+                                      NULL, &supported_mechSet);
+               if (ret != GSS_S_COMPLETE) {
+                       *return_token = NO_TOKEN_SEND;
+                       goto cleanup;
+               }
+       } else {
+               ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
+                                         GSS_C_ACCEPT, NULL,
+                                         &supported_mechSet);
+               if (ret != GSS_S_COMPLETE) {
+                       *return_token = NO_TOKEN_SEND;
+                       goto cleanup;
+               }
+       }
+       /*
+        * Select the best match between the list of mechs
+        * that the initiator requested and the list that
+        * the acceptor will support.
+        */
+       mech_wanted = negotiate_mech_type(minor_status,
+                                         supported_mechSet,
+                                         mechTypes,
+                                         negState);
+       if (*negState == REJECT) {
+               ret = GSS_S_BAD_MECH;
+               goto cleanup;
+       }
+       sc = create_spnego_ctx();
+       if (sc == NULL) {
+               ret = GSS_S_FAILURE;
+               *return_token = NO_TOKEN_SEND;
+               generic_gss_release_oid(&tmpmin, &mech_wanted);
+               goto cleanup;
+       }
+       sc->internal_mech = mech_wanted;
+       sc->DER_mechTypes = der_mechTypes;
+       der_mechTypes.length = 0;
+       der_mechTypes.value = NULL;
+
+       if (*negState == REQUEST_MIC)
+               sc->mic_reqd = 1;
+
+       *return_token = INIT_TOKEN_SEND;
+       sc->firstpass = 1;
+       *ctx = (gss_ctx_id_t)sc;
+       ret = GSS_S_COMPLETE;
+cleanup:
+       gss_release_oid_set(&tmpmin, &mechTypes);
+       gss_release_oid_set(&tmpmin, &supported_mechSet);
+       if (der_mechTypes.length != 0)
+               gss_release_buffer(&tmpmin, &der_mechTypes);
+
+       return ret;
+}
+
+static OM_uint32
+acc_ctx_cont(OM_uint32 *minstat,
+            gss_buffer_t buf,
+            gss_ctx_id_t *ctx,
+            gss_buffer_t *responseToken,
+            gss_buffer_t *mechListMIC,
+            OM_uint32 *negState,
+            send_token_flag *return_token)
+{
+       OM_uint32 ret, tmpmin;
+       gss_OID supportedMech;
+       spnego_gss_ctx_id_t sc;
+       int len;
+       unsigned char *ptr, *bufstart;
+
+       sc = (spnego_gss_ctx_id_t)*ctx;
+       ret = GSS_S_DEFECTIVE_TOKEN;
+       *negState = REJECT;
+       *minstat = 0;
+       supportedMech = GSS_C_NO_OID;
+       *return_token = ERROR_TOKEN_SEND;
+       *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
+
+       ptr = bufstart = buf->value;
+#define REMAIN (buf->length - (ptr - bufstart))
+       if (REMAIN > INT_MAX)
+               return GSS_S_DEFECTIVE_TOKEN;
+
+       /*
+        * Attempt to work with old Sun SPNEGO.
+        */
+       if (*ptr == HEADER_ID) {
+               ret = g_verify_token_header(gss_mech_spnego,
+                                           &len, &ptr, 0, REMAIN);
+               if (ret) {
+                       *minstat = ret;
+                       return GSS_S_DEFECTIVE_TOKEN;
+               }
+       }
+       if (*ptr != (CONTEXT | 0x01)) {
+               return GSS_S_DEFECTIVE_TOKEN;
+       }
+       ret = get_negTokenResp(minstat, ptr, REMAIN,
+                              negState, &supportedMech,
+                              responseToken, mechListMIC);
+       if (ret != GSS_S_COMPLETE)
+               goto cleanup;
+
+       if (*responseToken == GSS_C_NO_BUFFER &&
+           *mechListMIC == GSS_C_NO_BUFFER) {
+
+               ret = GSS_S_DEFECTIVE_TOKEN;
+               goto cleanup;
+       }
+       if (supportedMech != GSS_C_NO_OID) {
+               ret = GSS_S_DEFECTIVE_TOKEN;
+               goto cleanup;
+       }
+       sc->firstpass = 0;
+       *negState = ACCEPT_INCOMPLETE;
+       *return_token = CONT_TOKEN_SEND;
+cleanup:
+       if (supportedMech != GSS_C_NO_OID) {
+               generic_gss_release_oid(&tmpmin, &supportedMech);
+       }
+       return ret;
+#undef REMAIN
+}
+
+/*
+ * Verify that mech OID is either exactly the same as the negotiated
+ * mech OID, or is a mech OID supported by the negotiated mech.  MS
+ * implementations can list a most preferred mech using an incorrect
+ * krb5 OID while emitting a krb5 initiator mech token having the
+ * correct krb5 mech OID.
+ */
+static OM_uint32
+acc_ctx_vfy_oid(OM_uint32 *minor_status,
+               spnego_gss_ctx_id_t sc, gss_OID mechoid,
+               OM_uint32 *negState, send_token_flag *tokflag)
+{
+       OM_uint32 ret, tmpmin;
+       gss_mechanism mech = NULL;
+       gss_OID_set mech_set = GSS_C_NO_OID_SET;
+       int present = 0;
+
+       if (g_OID_equal(sc->internal_mech, mechoid))
+               return GSS_S_COMPLETE;
+
+       mech = gssint_get_mechanism(sc->internal_mech);
+       if (mech == NULL || mech->gss_indicate_mechs == NULL) {
+               *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
+               *negState = REJECT;
+               *tokflag = ERROR_TOKEN_SEND;
+               return GSS_S_BAD_MECH;
+       }
+       ret = mech->gss_indicate_mechs(NULL, minor_status, &mech_set);
+       if (ret != GSS_S_COMPLETE) {
+               *tokflag = NO_TOKEN_SEND;
+               goto cleanup;
+       }
+       ret = gss_test_oid_set_member(minor_status, mechoid,
+                                     mech_set, &present);
+       if (ret != GSS_S_COMPLETE)
+               goto cleanup;
+       if (!present) {
+               *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
+               *negState = REJECT;
+               *tokflag = ERROR_TOKEN_SEND;
+               ret = GSS_S_BAD_MECH;
+       }
+cleanup:
+       gss_release_oid_set(&tmpmin, &mech_set);
+       return ret;
+}
+
+/*
+ * Wrap call to gss_accept_sec_context() and update state
+ * accordingly.
+ */
+static OM_uint32
+acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
+                gss_cred_id_t cred, gss_buffer_t mechtok_in,
+                gss_OID *mech_type, gss_buffer_t mechtok_out,
+                OM_uint32 *ret_flags, OM_uint32 *time_rec,
+                gss_cred_id_t *delegated_cred_handle,
+                OM_uint32 *negState, send_token_flag *tokflag)
+{
+       OM_uint32 ret;
+       gss_OID_desc mechoid;
+
+       /*
+        * mechoid is an alias; don't free it.
+        */
+       ret = gssint_get_mech_type(&mechoid, mechtok_in);
+       if (ret != GSS_S_COMPLETE) {
+               *tokflag = NO_TOKEN_SEND;
+               return ret;
+       }
+       ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
+                             negState, tokflag);
+       if (ret != GSS_S_COMPLETE)
+               return ret;
+
+       ret = gss_accept_sec_context(minor_status,
+                                    &sc->ctx_handle,
+                                    cred,
+                                    mechtok_in,
+                                    GSS_C_NO_CHANNEL_BINDINGS,
+                                    &sc->internal_name,
+                                    mech_type,
+                                    mechtok_out,
+                                    &sc->ctx_flags,
+                                    time_rec,
+                                    delegated_cred_handle);
+       if (ret == GSS_S_COMPLETE) {
+#ifdef MS_BUG_TEST
+               /*
+                * Force MIC to be not required even if we previously
+                * requested a MIC.
+                */
+               char *envstr = getenv("MS_FORCE_NO_MIC");
+
+               if (envstr != NULL && strcmp(envstr, "1") == 0 &&
+                   !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
+                   sc->mic_reqd) {
+
+                       sc->mic_reqd = 0;
+               }
+#endif
+               sc->mech_complete = 1;
+               if (ret_flags != NULL)
+                       *ret_flags = sc->ctx_flags;
+
+               if (!sc->mic_reqd) {
+                       *negState = ACCEPT_COMPLETE;
+                       ret = GSS_S_COMPLETE;
+               } else {
+                       ret = GSS_S_CONTINUE_NEEDED;
+               }
+       } else if (ret != GSS_S_CONTINUE_NEEDED) {
+               *negState = REJECT;
+               *tokflag = ERROR_TOKEN_SEND;
+       }
+       return ret;
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_accept_sec_context(void *ct,
+                           OM_uint32 *minor_status,
+                           gss_ctx_id_t *context_handle,
+                           gss_cred_id_t verifier_cred_handle,
+                           gss_buffer_t input_token,
+                           gss_channel_bindings_t input_chan_bindings,
+                           gss_name_t *src_name,
+                           gss_OID *mech_type,
+                           gss_buffer_t output_token,
+                           OM_uint32 *ret_flags,
+                           OM_uint32 *time_rec,
+                           gss_cred_id_t *delegated_cred_handle)
+{
+       OM_uint32 ret, tmpret, tmpmin, negState;
+       send_token_flag return_token;
+       gss_buffer_t mechtok_in, mic_in, mic_out;
+       gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
+       spnego_gss_ctx_id_t sc = NULL;
+       OM_uint32 mechstat = GSS_S_FAILURE;
+
+       mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
+
+       if (minor_status != NULL)
+               *minor_status = 0;
+       if (output_token != GSS_C_NO_BUFFER) {
+               output_token->length = 0;
+               output_token->value = NULL;
+       }
+
+       if (minor_status == NULL ||
+           output_token == GSS_C_NO_BUFFER ||
+           context_handle == NULL)
+               return GSS_S_CALL_INACCESSIBLE_WRITE;
+
+       if (input_token == GSS_C_NO_BUFFER)
+               return GSS_S_CALL_INACCESSIBLE_READ;
+
+       if (*context_handle == GSS_C_NO_CONTEXT) {
+               if (src_name != NULL)
+                       *src_name = GSS_C_NO_NAME;
+               if (mech_type != NULL)
+                       *mech_type = GSS_C_NO_OID;
+               if (time_rec != NULL)
+                       *time_rec = 0;
+               if (ret_flags != NULL)
+                       *ret_flags = 0;
+               if (delegated_cred_handle != NULL)
+                       *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
+               /* Can set negState to REQUEST_MIC */
+               ret = acc_ctx_new(minor_status, input_token,
+                                 context_handle, verifier_cred_handle,
+                                 &mechtok_in, &mic_in,
+                                 &negState, &return_token);
+               if (ret != GSS_S_COMPLETE)
+                       goto cleanup;
+               ret = GSS_S_CONTINUE_NEEDED;
+       } else {
+               /* Can set negState to ACCEPT_INCOMPLETE */
+               ret = acc_ctx_cont(minor_status, input_token,
+                                  context_handle, &mechtok_in,
+                                  &mic_in, &negState, &return_token);
+               if (ret != GSS_S_COMPLETE)
+                       goto cleanup;
+               ret = GSS_S_CONTINUE_NEEDED;
+       }
+       sc = (spnego_gss_ctx_id_t)*context_handle;
+       /*
+        * Handle mechtok_in and mic_in only if they are
+        * present in input_token.  If neither is present, whether
+        * this is an error depends on whether this is the first
+        * round-trip.  RET is set to a default value according to
+        * whether it is the first round-trip.
+        */
+       mechstat = GSS_S_FAILURE;
+       if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
+               ret = acc_ctx_call_acc(minor_status, sc,
+                                      verifier_cred_handle, mechtok_in,
+                                      mech_type, &mechtok_out,
+                                      ret_flags, time_rec,
+                                      delegated_cred_handle,
+                                      &negState, &return_token);
+       } else if (negState == REQUEST_MIC) {
+               mechstat = GSS_S_CONTINUE_NEEDED;
+       }
+       if (!HARD_ERROR(ret) && sc->mech_complete &&
+           (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
+
+               ret = handle_mic(minor_status, mic_in,
+                                (mechtok_out.length != 0),
+                                sc, &mic_out,
+                                &negState, &return_token);
+       }
+cleanup:
+       if (return_token != NO_TOKEN_SEND && return_token != CHECK_MIC) {
+               tmpret = make_spnego_tokenTarg_msg(negState, sc->internal_mech,
+                                                  &mechtok_out, mic_out,
+                                                  return_token,
+                                                  output_token);
+               if (tmpret != GSS_S_COMPLETE) {
+                       ret = tmpret;
+               }
+       }
+       if (ret == GSS_S_COMPLETE) {
+               *context_handle = (gss_ctx_id_t)sc->ctx_handle;
+               if (sc->internal_name != GSS_C_NO_NAME &&
+                   src_name != NULL) {
+                       *src_name = sc->internal_name;
+               }
+               release_spnego_ctx(&sc);
+       }
+       gss_release_buffer(&tmpmin, &mechtok_out);
+       if (mechtok_in != GSS_C_NO_BUFFER) {
+               gss_release_buffer(&tmpmin, mechtok_in);
+               free(mechtok_in);
+       }
+       if (mic_in != GSS_C_NO_BUFFER) {
+               gss_release_buffer(&tmpmin, mic_in);
+               free(mic_in);
+       }
+       if (mic_out != GSS_C_NO_BUFFER) {
+               gss_release_buffer(&tmpmin, mic_out);
+               free(mic_out);
+       }
+       return ret;
+}
+
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_display_status(void *ctx,
+               OM_uint32 *minor_status,
+               OM_uint32 status_value,
+               int status_type,
+               gss_OID mech_type,
+               OM_uint32 *message_context,
+               gss_buffer_t status_string)
+{
+       dsyslog("Entering display_status\n");
+
+       *message_context = 0;
+       switch (status_value) {
+           case ERR_SPNEGO_NO_MECHS_AVAILABLE:
+               /* CSTYLED */
+               *status_string = make_err_msg("SPNEGO cannot find mechanisms to negotiate");
+               break;
+           case ERR_SPNEGO_NO_CREDS_ACQUIRED:
+               /* CSTYLED */
+               *status_string = make_err_msg("SPNEGO failed to acquire creds");
+               break;
+           case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
+               /* CSTYLED */
+               *status_string = make_err_msg("SPNEGO acceptor did not select a mechanism");
+               break;
+           case ERR_SPNEGO_NEGOTIATION_FAILED:
+               /* CSTYLED */
+               *status_string = make_err_msg("SPNEGO failed to negotiate a mechanism");
+               break;
+           case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
+               /* CSTYLED */
+               *status_string = make_err_msg("SPNEGO acceptor did not return a valid token");
+               break;
+           default:
+               status_string->length = 0;
+               status_string->value = "";
+               break;
+       }
+
+       dsyslog("Leaving display_status\n");
+       return (GSS_S_COMPLETE);
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_import_name(void *ctx,
+                   OM_uint32 *minor_status,
+                   gss_buffer_t input_name_buffer,
+                   gss_OID input_name_type,
+                   gss_name_t *output_name)
+{
+       OM_uint32 status;
+
+       dsyslog("Entering import_name\n");
+
+       status = gss_import_name(minor_status, input_name_buffer,
+                       input_name_type, output_name);
+
+       dsyslog("Leaving import_name\n");
+       return (status);
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_release_name(void *ctx,
+                       OM_uint32 *minor_status,
+                       gss_name_t *input_name)
+{
+       OM_uint32 status;
+
+       dsyslog("Entering release_name\n");
+
+       status = gss_release_name(minor_status, input_name);
+
+       dsyslog("Leaving release_name\n");
+       return (status);
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_display_name(void *ctx,
+                       OM_uint32 *minor_status,
+                       gss_name_t input_name,
+                       gss_buffer_t output_name_buffer,
+                       gss_OID *output_name_type)
+{
+       OM_uint32 status = GSS_S_COMPLETE;
+       dsyslog("Entering display_name\n");
+
+       status = gss_display_name(minor_status, input_name,
+                       output_name_buffer, output_name_type);
+
+       dsyslog("Leaving display_name\n");
+       return (status);
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_inquire_names_for_mech(void *ctx,
+                               OM_uint32       *minor_status,
+                               gss_OID         mechanism,
+                               gss_OID_set     *name_types)
+{
+       OM_uint32   major, minor;
+
+       dsyslog("Entering inquire_names_for_mech\n");
+       /*
+        * We only know how to handle our own mechanism.
+        */
+       if ((mechanism != GSS_C_NULL_OID) &&
+           !g_OID_equal(gss_mech_spnego, mechanism)) {
+               *minor_status = 0;
+               return (GSS_S_FAILURE);
+       }
+
+       major = gss_create_empty_oid_set(minor_status, name_types);
+       if (major == GSS_S_COMPLETE) {
+               /* Now add our members. */
+               if (((major = gss_add_oid_set_member(minor_status,
+                               (gss_OID) GSS_C_NT_USER_NAME,
+                               name_types)) == GSS_S_COMPLETE) &&
+                   ((major = gss_add_oid_set_member(minor_status,
+                               (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
+                               name_types)) == GSS_S_COMPLETE) &&
+                   ((major = gss_add_oid_set_member(minor_status,
+                               (gss_OID) GSS_C_NT_STRING_UID_NAME,
+                               name_types)) == GSS_S_COMPLETE)) {
+                       major = gss_add_oid_set_member(minor_status,
+                               (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
+                               name_types);
+               }
+
+               if (major != GSS_S_COMPLETE)
+                       (void) gss_release_oid_set(&minor, name_types);
+       }
+
+       dsyslog("Leaving inquire_names_for_mech\n");
+       return (major);
+}
+
+OM_uint32
+spnego_gss_unseal(void *context,
+               OM_uint32 *minor_status,
+               gss_ctx_id_t context_handle,
+               gss_buffer_t input_message_buffer,
+               gss_buffer_t output_message_buffer,
+               int *conf_state,
+               int *qop_state)
+{
+       OM_uint32 ret;
+       ret = gss_unseal(minor_status,
+                       context_handle,
+                       input_message_buffer,
+                       output_message_buffer,
+                       conf_state,
+                       qop_state);
+
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_seal(void *context,
+               OM_uint32 *minor_status,
+               gss_ctx_id_t context_handle,
+               int conf_req_flag,
+               int qop_req,
+               gss_buffer_t input_message_buffer,
+               int *conf_state,
+               gss_buffer_t output_message_buffer)
+{
+       OM_uint32 ret;
+       ret = gss_seal(minor_status,
+                   context_handle,
+                   conf_req_flag,
+                   qop_req,
+                   input_message_buffer,
+                   conf_state,
+                   output_message_buffer);
+
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_process_context_token(void *context,
+                               OM_uint32       *minor_status,
+                               const gss_ctx_id_t context_handle,
+                               const gss_buffer_t token_buffer)
+{
+       OM_uint32 ret;
+       ret = gss_process_context_token(minor_status,
+                                       context_handle,
+                                       token_buffer);
+
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_delete_sec_context(void *context,
+                           OM_uint32 *minor_status,
+                           gss_ctx_id_t *context_handle,
+                           gss_buffer_t output_token)
+{
+       OM_uint32 ret = GSS_S_COMPLETE;
+       spnego_gss_ctx_id_t *ctx =
+                   (spnego_gss_ctx_id_t *)context_handle;
+
+       if (context_handle == NULL)
+               return (GSS_S_FAILURE);
+
+       /*
+        * If this is still an SPNEGO mech, release it locally.
+        */
+       if (*ctx != NULL &&
+           (*ctx)->magic_num == SPNEGO_MAGIC_ID) {
+               (void) release_spnego_ctx(ctx);
+       } else {
+               ret = gss_delete_sec_context(minor_status,
+                                   context_handle,
+                                   output_token);
+       }
+
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_context_time(void *context,
+                       OM_uint32       *minor_status,
+                       const gss_ctx_id_t context_handle,
+                       OM_uint32       *time_rec)
+{
+       OM_uint32 ret;
+       ret = gss_context_time(minor_status,
+                           context_handle,
+                           time_rec);
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_export_sec_context(void *context,
+                           OM_uint32     *minor_status,
+                           gss_ctx_id_t *context_handle,
+                           gss_buffer_t interprocess_token)
+{
+       OM_uint32 ret;
+       ret = gss_export_sec_context(minor_status,
+                                   context_handle,
+                                   interprocess_token);
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_import_sec_context(void *context,
+       OM_uint32               *minor_status,
+       const gss_buffer_t      interprocess_token,
+       gss_ctx_id_t            *context_handle)
+{
+       OM_uint32 ret;
+       ret = gss_import_sec_context(minor_status,
+                                   interprocess_token,
+                                   context_handle);
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_inquire_context(void *context,
+                       OM_uint32       *minor_status,
+                       const gss_ctx_id_t context_handle,
+                       gss_name_t      *src_name,
+                       gss_name_t      *targ_name,
+                       OM_uint32       *lifetime_rec,
+                       gss_OID         *mech_type,
+                       OM_uint32       *ctx_flags,
+                       int             *locally_initiated,
+                       int             *open)
+{
+       OM_uint32 ret = GSS_S_COMPLETE;
+
+       ret = gss_inquire_context(minor_status,
+                               context_handle,
+                               src_name,
+                               targ_name,
+                               lifetime_rec,
+                               mech_type,
+                               ctx_flags,
+                               locally_initiated,
+                               open);
+
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_wrap_size_limit(void *context,
+       OM_uint32       *minor_status,
+       const gss_ctx_id_t context_handle,
+       int             conf_req_flag,
+       gss_qop_t       qop_req,
+       OM_uint32       req_output_size,
+       OM_uint32       *max_input_size)
+{
+       OM_uint32 ret;
+       ret = gss_wrap_size_limit(minor_status,
+                               context_handle,
+                               conf_req_flag,
+                               qop_req,
+                               req_output_size,
+                               max_input_size);
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_sign(void *context,
+               OM_uint32 *minor_status,
+               const gss_ctx_id_t context_handle,
+               int  qop_req,
+               const gss_buffer_t message_buffer,
+               gss_buffer_t message_token)
+{
+       OM_uint32 ret;
+       ret = gss_sign(minor_status,
+                   context_handle,
+                   qop_req,
+                   message_buffer,
+                   message_token);
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_verify(void *context,
+               OM_uint32 *minor_status,
+               const gss_ctx_id_t context_handle,
+               const gss_buffer_t msg_buffer,
+               const gss_buffer_t token_buffer,
+               int *qop_state)
+{
+       OM_uint32 ret;
+       ret = gss_verify_mic(minor_status,
+                           context_handle,
+                           msg_buffer,
+                           token_buffer,
+                           (gss_qop_t *)qop_state); /* XXX */
+       return (ret);
+}
+
+/*
+ * We will release everything but the ctx_handle so that it
+ * can be passed back to init/accept context. This routine should
+ * not be called until after the ctx_handle memory is assigned to
+ * the supplied context handle from init/accept context.
+ */
+static void
+release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
+{
+       spnego_gss_ctx_id_t context;
+       OM_uint32 minor_stat;
+       context = *ctx;
+
+       if (context != NULL) {
+               (void) gss_release_buffer(&minor_stat,
+                                       &context->DER_mechTypes);
+
+               (void) generic_gss_release_oid(&minor_stat,
+                               &context->internal_mech);
+
+               if (context->optionStr != NULL) {
+                       free(context->optionStr);
+                       context->optionStr = NULL;
+               }
+               free(context);
+               *ctx = NULL;
+       }
+}
+
+/*
+ * Can't use gss_indicate_mechs by itself to get available mechs for
+ * SPNEGO because it will also return the SPNEGO mech and we do not
+ * want to consider SPNEGO as an available security mech for
+ * negotiation. For this reason, get_available_mechs will return
+ * all available mechs except SPNEGO.
+ *
+ * If a ptr to a creds list is given, this function will attempt
+ * to acquire creds for the creds given and trim the list of
+ * returned mechanisms to only those for which creds are valid.
+ *
+ */
+static OM_uint32
+get_available_mechs(OM_uint32 *minor_status,
+       gss_name_t name, gss_cred_usage_t usage,
+       gss_cred_id_t *creds, gss_OID_set *rmechs)
+{
+       int             i;
+       int             found = 0;
+       OM_uint32 stat = GSS_S_COMPLETE, tmpmin;
+       gss_OID_set mechs, goodmechs;
+
+       stat = gss_indicate_mechs(minor_status, &mechs);
+
+       if (stat != GSS_S_COMPLETE) {
+               return (stat);
+       }
+
+       stat = gss_create_empty_oid_set(minor_status, rmechs);
+
+       if (stat != GSS_S_COMPLETE) {
+               (void) gss_release_oid_set(minor_status, &mechs);
+               return (stat);
+       }
+
+       for (i = 0; i < mechs->count && stat == GSS_S_COMPLETE; i++) {
+               if ((mechs->elements[i].length
+                   != spnego_mechanism.mech_type.length) ||
+                   memcmp(mechs->elements[i].elements,
+                       spnego_mechanism.mech_type.elements,
+                       spnego_mechanism.mech_type.length)) {
+
+                       stat = gss_add_oid_set_member(minor_status,
+                                           &mechs->elements[i],
+                                           rmechs);
+                       if (stat == GSS_S_COMPLETE)
+                               found++;
+               }
+       }
+
+       /*
+        * If the caller wanted a list of creds returned,
+        * trim the list of mechanisms down to only those
+        * for which the creds are valid.
+        */
+       if (found > 0 && stat == GSS_S_COMPLETE && creds != NULL) {
+               stat = gss_acquire_cred(minor_status,
+                       name, GSS_C_INDEFINITE, *rmechs, usage, creds,
+                       &goodmechs, NULL);
+
+               /*
+                * Drop the old list in favor of the new
+                * "trimmed" list.
+                */
+               (void) gss_release_oid_set(&tmpmin, rmechs);
+               if (stat == GSS_S_COMPLETE) {
+                       (void) gssint_copy_oid_set(&tmpmin,
+                                       goodmechs, rmechs);
+                       (void) gss_release_oid_set(&tmpmin, &goodmechs);
+               }
+       }
+
+       (void) gss_release_oid_set(&tmpmin, &mechs);
+       if (found == 0 || stat != GSS_S_COMPLETE) {
+               *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
+               if (stat == GSS_S_COMPLETE)
+                       stat = GSS_S_FAILURE;
+       }
+
+       return (stat);
+}
+
+/* following are token creation and reading routines */
+
+/*
+ * If buff_in is not pointing to a MECH_OID, then return NULL and do not
+ * advance the buffer, otherwise, decode the mech_oid from the buffer and
+ * place in gss_OID.
+ */
+static gss_OID
+get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
+{
+       OM_uint32       status;
+       gss_OID_desc    toid;
+       gss_OID         mech_out = NULL;
+       unsigned char           *start, *end;
+
+       if (length < 1 || **buff_in != MECH_OID)
+               return (NULL);
+
+       start = *buff_in;
+       end = start + length;
+
+       (*buff_in)++;
+       toid.length = *(*buff_in)++;
+
+       if ((*buff_in + toid.length) > end)
+               return (NULL);
+
+       toid.elements = *buff_in;
+       *buff_in += toid.length;
+
+       status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
+
+       if (status != GSS_S_COMPLETE)
+               mech_out = NULL;
+
+       return (mech_out);
+}
+
+/*
+ * der encode the given mechanism oid into buf_out, advancing the
+ * buffer pointer.
+ */
+
+static int
+put_mech_oid(unsigned char **buf_out, gss_OID_const mech, int buflen)
+{
+       if (buflen < mech->length + 2)
+               return (-1);
+       *(*buf_out)++ = MECH_OID;
+       *(*buf_out)++ = (unsigned char) mech->length;
+       memcpy((void *)(*buf_out), mech->elements, mech->length);
+       *buf_out += mech->length;
+       return (0);
+}
+
+/*
+ * verify that buff_in points to an octet string, if it does not,
+ * return NULL and don't advance the pointer. If it is an octet string
+ * decode buff_in into a gss_buffer_t and return it, advancing the
+ * buffer pointer.
+ */
+static gss_buffer_t
+get_input_token(unsigned char **buff_in, int buff_length)
+{
+       gss_buffer_t input_token;
+       unsigned int bytes;
+
+       if (**buff_in != OCTET_STRING)
+               return (NULL);
+
+       (*buff_in)++;
+       input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
+
+       if (input_token == NULL)
+               return (NULL);
+
+       input_token->length = gssint_get_der_length(buff_in, buff_length, &bytes);
+       if ((int)input_token->length == -1) {
+               free(input_token);
+               return (NULL);
+       }
+       input_token->value = malloc(input_token->length);
+
+       if (input_token->value == NULL) {
+               free(input_token);
+               return (NULL);
+       }
+
+       (void) memcpy(input_token->value, *buff_in, input_token->length);
+       *buff_in += input_token->length;
+       return (input_token);
+}
+
+/*
+ * verify that the input token length is not 0. If it is, just return.
+ * If the token length is greater than 0, der encode as an octet string
+ * and place in buf_out, advancing buf_out.
+ */
+
+static int
+put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
+               int buflen)
+{
+       int ret;
+
+       /* if token length is 0, we do not want to send */
+       if (input_token->length == 0)
+               return (0);
+
+       if (input_token->length > buflen)
+               return (-1);
+
+       *(*buf_out)++ = OCTET_STRING;
+       if ((ret = gssint_put_der_length(input_token->length, buf_out,
+                           input_token->length)))
+               return (ret);
+       TWRITE_STR(*buf_out, input_token->value, ((int)input_token->length));
+       return (0);
+}
+
+/*
+ * verify that buff_in points to a sequence of der encoding. The mech
+ * set is the only sequence of encoded object in the token, so if it is
+ * a sequence of encoding, decode the mechset into a gss_OID_set and
+ * return it, advancing the buffer pointer.
+ */
+static gss_OID_set
+get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in, int buff_length)
+{
+       gss_OID_set returned_mechSet;
+       OM_uint32 major_status;
+       OM_uint32 length;
+       OM_uint32 bytes;
+       OM_uint32 set_length;
+       unsigned char           *start;
+       int i;
+
+       if (**buff_in != SEQUENCE_OF)
+               return (NULL);
+
+       start = *buff_in;
+       (*buff_in)++;
+
+       length = gssint_get_der_length(buff_in, buff_length, &bytes);
+
+       major_status = gss_create_empty_oid_set(minor_status,
+                                               &returned_mechSet);
+       if (major_status != GSS_S_COMPLETE)
+               return (NULL);
+
+       for (set_length = 0, i = 0; set_length < length; i++) {
+               gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
+                       buff_length - (*buff_in - start));
+               if (temp != NULL) {
+                   major_status = gss_add_oid_set_member(minor_status,
+                                       temp, &returned_mechSet);
+                   if (major_status == GSS_S_COMPLETE) {
+                       set_length += returned_mechSet->elements[i].length +2;
+                       generic_gss_release_oid(minor_status, &temp);
+                   }
+               }
+       }
+
+       return (returned_mechSet);
+}
+
+/*
+ * Encode mechSet into buf.
+ */
+static int
+put_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
+{
+       unsigned char *ptr;
+       int i, tlen, ilen;
+
+       tlen = ilen = 0;
+       for (i = 0; i < mechSet->count; i++) {
+               /*
+                * 0x06 [DER LEN] [OID]
+                */
+               ilen += 1 +
+                       gssint_der_length_size(mechSet->elements[i].length) +
+                       mechSet->elements[i].length;
+       }
+       /*
+        * 0x30 [DER LEN]
+        */
+       tlen = 1 + gssint_der_length_size(ilen) + ilen;
+       ptr = malloc(tlen);
+       if (ptr == NULL)
+               return -1;
+
+       buf->value = ptr;
+       buf->length = tlen;
+#define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
+
+       *ptr++ = SEQUENCE_OF;
+       if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
+               return -1;
+       for (i = 0; i < mechSet->count; i++) {
+               if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
+                       return -1;
+               }
+       }
+       return 0;
+#undef REMAIN
+}
+
+/*
+ * Verify that buff_in is pointing to a BIT_STRING with the correct
+ * length and padding for the req_flags. If it is, decode req_flags
+ * and return them, otherwise, return NULL.
+ */
+static OM_uint32
+get_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
+             OM_uint32 *req_flags)
+{
+       int len;
+       unsigned char *start = *buff_in;
+
+       if (**buff_in != (CONTEXT | 0x01))
+               return (0);
+
+       if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
+                               bodysize, &len) < 0)
+               return GSS_S_DEFECTIVE_TOKEN;
+
+       if (*(*buff_in)++ != BIT_STRING)
+               return GSS_S_DEFECTIVE_TOKEN;
+
+       if (*(*buff_in)++ != BIT_STRING_LENGTH)
+               return GSS_S_DEFECTIVE_TOKEN;
+
+       if (*(*buff_in)++ != BIT_STRING_PADDING)
+               return GSS_S_DEFECTIVE_TOKEN;
+
+       *req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
+       return (0);
+}
+
+/*
+ * der encode the passed req_flags into buf_out, advancing
+ * the buffer pointer.
+ */
+
+static int
+put_req_flags(unsigned char **buf_out, OM_uint32 req_flags, int buflen)
+{
+       int ret = 0;
+       if (buflen < 6)
+               return (-1);
+
+       *(*buf_out)++ = CONTEXT | 0x01;
+       if ((ret = gssint_put_der_length(4, buf_out, buflen-1)) != 0)
+               return (ret);
+
+       *(*buf_out)++ = BIT_STRING;
+       *(*buf_out)++ = BIT_STRING_LENGTH;
+       *(*buf_out)++ = BIT_STRING_PADDING;
+       *(*buf_out)++ = (unsigned char) (req_flags << 1);
+       return (ret);
+}
+
+static OM_uint32
+get_negTokenInit(OM_uint32 *minor_status,
+                gss_buffer_t buf,
+                gss_buffer_t der_mechSet,
+                gss_OID_set *mechSet,
+                OM_uint32 *req_flags,
+                gss_buffer_t *mechtok,
+                gss_buffer_t *mechListMIC)
+{
+       OM_uint32 err;
+       unsigned char *ptr, *bufstart;
+       int len;
+       gss_buffer_desc tmpbuf;
+
+       *minor_status = 0;
+       der_mechSet->length = 0;
+       der_mechSet->value = NULL;
+       *mechSet = GSS_C_NO_OID_SET;
+       *req_flags = 0;
+       *mechtok = *mechListMIC = GSS_C_NO_BUFFER;
+
+       ptr = bufstart = buf->value;
+       if ((buf->length - (ptr - bufstart)) > INT_MAX)
+               return GSS_S_FAILURE;
+#define REMAIN ((int)(buf->length - (ptr - bufstart)))
+
+       err = g_verify_token_header(gss_mech_spnego,
+                                   &len, &ptr, 0, REMAIN);
+       if (err) {
+               *minor_status = err;
+               return GSS_S_FAILURE;
+       }
+       *minor_status = g_verify_neg_token_init(&ptr, REMAIN);
+       if (*minor_status)
+               return GSS_S_FAILURE;
+
+       /* alias into input_token */
+       tmpbuf.value = ptr;
+       tmpbuf.length = REMAIN;
+       *mechSet = get_mech_set(minor_status, &ptr, REMAIN);
+       if (*mechSet == NULL)
+               return GSS_S_FAILURE;
+
+       tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
+       der_mechSet->value = malloc(tmpbuf.length);
+       if (der_mechSet->value == NULL)
+               return GSS_S_FAILURE;
+       memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
+       der_mechSet->length = tmpbuf.length;
+
+       err = get_req_flags(&ptr, REMAIN, req_flags);
+       if (err != GSS_S_COMPLETE) {
+               return err;
+       }
+       if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
+                                REMAIN, &len) >= 0) {
+               *mechtok = get_input_token(&ptr, len);
+               if (*mechtok == GSS_C_NO_BUFFER) {
+                       return GSS_S_FAILURE;
+               }
+       }
+       if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
+                                REMAIN, &len) >= 0) {
+               *mechListMIC = get_input_token(&ptr, len);
+               if (*mechListMIC == GSS_C_NO_BUFFER) {
+                       return GSS_S_FAILURE;
+               }
+       }
+       return GSS_S_COMPLETE;
+#undef REMAIN
+}
+
+static OM_uint32
+get_negTokenResp(OM_uint32 *minor_status,
+                unsigned char *buf, unsigned int buflen,
+                OM_uint32 *negState,
+                gss_OID *supportedMech,
+                gss_buffer_t *responseToken,
+                gss_buffer_t *mechListMIC)
+{
+       unsigned char *ptr, *bufstart;
+       int len, bytes;
+       unsigned int tag;
+
+       *negState = ACCEPT_DEFECTIVE_TOKEN;
+       *supportedMech = GSS_C_NO_OID;
+       *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
+       ptr = bufstart = buf;
+#define REMAIN (buflen - (ptr - bufstart))
+
+       if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
+               return GSS_S_DEFECTIVE_TOKEN;
+       if (*ptr++ == SEQUENCE) {
+               len = gssint_get_der_length(&ptr, REMAIN, &bytes);
+               if (len < 0)
+                       return GSS_S_DEFECTIVE_TOKEN;
+       }
+       if (REMAIN < 1)
+               tag = 0;
+       else
+               tag = *ptr++;
+
+       if (tag == CONTEXT) {
+               len = gssint_get_der_length(&ptr, REMAIN, &bytes);
+               if (len < 0)
+                       return GSS_S_DEFECTIVE_TOKEN;
+
+               if (g_get_tag_and_length(&ptr, ENUMERATED,
+                                        REMAIN, &len) < 0)
+                       return GSS_S_DEFECTIVE_TOKEN;
+
+               if (len != ENUMERATION_LENGTH)
+                       return GSS_S_DEFECTIVE_TOKEN;
+
+               if (REMAIN < 1)
+                       return GSS_S_DEFECTIVE_TOKEN;
+               *negState = *ptr++;
+
+               if (REMAIN < 1)
+                       tag = 0;
+               else
+                       tag = *ptr++;
+       }
+       if (tag == (CONTEXT | 0x01)) {
+               len = gssint_get_der_length(&ptr, REMAIN, &bytes);
+               if (len < 0)
+                       return GSS_S_DEFECTIVE_TOKEN;
+
+               *supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
+               if (*supportedMech == GSS_C_NO_OID)
+                       return GSS_S_DEFECTIVE_TOKEN;
+
+               if (REMAIN < 1)
+                       tag = 0;
+               else
+                       tag = *ptr++;
+       }
+       if (tag == (CONTEXT | 0x02)) {
+               len = gssint_get_der_length(&ptr, REMAIN, &bytes);
+               if (len < 0)
+                       return GSS_S_DEFECTIVE_TOKEN;
+
+               *responseToken = get_input_token(&ptr, REMAIN);
+               if (*responseToken == GSS_C_NO_BUFFER)
+                       return GSS_S_DEFECTIVE_TOKEN;
+
+               if (REMAIN < 1)
+                       tag = 0;
+               else
+                       tag = *ptr++;
+       }
+       if (tag == (CONTEXT | 0x03)) {
+               len = gssint_get_der_length(&ptr, REMAIN, &bytes);
+               if (len < 0)
+                       return GSS_S_DEFECTIVE_TOKEN;
+
+               *mechListMIC = get_input_token(&ptr, REMAIN);
+               if (*mechListMIC == GSS_C_NO_BUFFER)
+                       return GSS_S_DEFECTIVE_TOKEN;
+       }
+       return GSS_S_COMPLETE;
+#undef REMAIN
+}
+
+/*
+ * der encode the passed negResults as an ENUMERATED type and
+ * place it in buf_out, advancing the buffer.
+ */
+
+static int
+put_negResult(unsigned char **buf_out, OM_uint32 negResult, int buflen)
+{
+       if (buflen < 3)
+               return (-1);
+       *(*buf_out)++ = ENUMERATED;
+       *(*buf_out)++ = ENUMERATION_LENGTH;
+       *(*buf_out)++ = (unsigned char) negResult;
+       return (0);
+}
+
+/*
+ * This routine compares the recieved mechset to the mechset that
+ * this server can support. It looks sequentially through the mechset
+ * and the first one that matches what the server can support is
+ * chosen as the negotiated mechanism. If one is found, negResult
+ * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
+ * it's not the first mech, otherwise we return NULL and negResult
+ * is set to REJECT.
+ *
+ * NOTE: There is currently no way to specify a preference order of
+ * mechanisms supported by the acceptor.
+ */
+static gss_OID
+negotiate_mech_type(OM_uint32 *minor_status,
+                   gss_OID_set supported_mechSet,
+                   gss_OID_set mechset,
+                   OM_uint32 *negResult)
+{
+       gss_OID returned_mech;
+       OM_uint32 status;
+       int present;
+       int i;
+
+       for (i = 0; i < mechset->count; i++) {
+               gss_test_oid_set_member(minor_status, &mechset->elements[i],
+                                       supported_mechSet, &present);
+               if (!present)
+                       continue;
+
+               if (i == 0)
+                       *negResult = ACCEPT_INCOMPLETE;
+               else
+                       *negResult = REQUEST_MIC;
+
+               status = generic_gss_copy_oid(minor_status,
+                                             &mechset->elements[i],
+                                             &returned_mech);
+               if (status != GSS_S_COMPLETE) {
+                       *negResult = REJECT;
+                       return (NULL);
+               }
+               return (returned_mech);
+       }
+       *negResult = REJECT;
+       return (NULL);
+}
+
+/*
+ * the next two routines make a token buffer suitable for
+ * spnego_gss_display_status. These currently take the string
+ * in name and place it in the token. Eventually, if
+ * spnego_gss_display_status returns valid error messages,
+ * these routines will be changes to return the error string.
+ */
+static spnego_token_t
+make_spnego_token(char *name)
+{
+       spnego_token_t token;
+
+       token = (spnego_token_t)malloc(strlen(name)+1);
+
+       if (token == NULL)
+               return (NULL);
+       strcpy(token, name);
+       return (token);
+}
+
+static gss_buffer_desc
+make_err_msg(char *name)
+{
+       gss_buffer_desc buffer;
+
+       if (name == NULL) {
+               buffer.length = 0;
+               buffer.value = NULL;
+       } else {
+               buffer.length = strlen(name)+1;
+               buffer.value = make_spnego_token(name);
+       }
+
+       return (buffer);
+}
+
+/*
+ * Create the client side spnego token passed back to gss_init_sec_context
+ * and eventually up to the application program and over to the server.
+ *
+ * Use DER rules, definite length method per RFC 2478
+ */
+static int
+make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
+                         gss_buffer_t mechListMIC, OM_uint32 req_flags,
+                         gss_buffer_t data, send_token_flag sendtoken,
+                         gss_buffer_t outbuf)
+{
+       int tlen, dataLen = 0, ret = 0;
+       int negTokenInitSize = 0;
+       int negTokenInitSeqSize = 0;
+       int negTokenInitContSize = 0;
+       int rspTokenSize = 0;
+       int mechListTokenSize = 0;
+       int micTokenSize = 0;
+       int i;
+       unsigned char *t;
+       unsigned char *ptr;
+       unsigned char *MechListPtr = NULL;
+       gss_buffer_desc MICbuff;
+
+       if (outbuf == GSS_C_NO_BUFFER)
+               return (-1);
+
+       outbuf->length = 0;
+       outbuf->value = NULL;
+
+       /* calculate the data length */
+
+       /*
+        * 0xa0 [DER LEN] [mechTypes]
+        */
+       mechListTokenSize = 1 +
+               gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
+               spnego_ctx->DER_mechTypes.length;
+       dataLen += mechListTokenSize;
+       /*
+        * 4 bytes for ret_flags:
+        *   ASN.1 token + ASN.1 Length + Padding + Flags
+        *   0xa1 LENGTH BIT_STRING BIT_STRING_LEN PAD DATA
+        */
+       if (req_flags != 0)
+               dataLen += 6;
+
+       /*
+        * If a token from gss_init_sec_context exists,
+        * add the length of the token + the ASN.1 overhead
+        */
+       if (data != NULL) {
+               /*
+                * Encoded in final output as:
+                * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
+                * -----s--------|--------s2----------
+                */
+               rspTokenSize = 1 +
+                       gssint_der_length_size(data->length) +
+                       data->length;
+               dataLen += 1 + gssint_der_length_size(rspTokenSize) +
+                       rspTokenSize;
+       }
+
+       if (mechListMIC) {
+               /*
+                * Encoded in final output as:
+                * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
+                *      --s--     -----tlen------------
+                */
+               micTokenSize = 1 +
+                       gssint_der_length_size(mechListMIC->length) +
+                       mechListMIC->length;
+               dataLen += 1 +
+                       gssint_der_length_size(micTokenSize) +
+                       micTokenSize;
+       }
+
+       /*
+        * Add size of DER encoding
+        * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
+        *   0x30 [DER_LEN] [data]
+        *
+        */
+       negTokenInitContSize = dataLen;
+       negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
+       dataLen = negTokenInitSeqSize;
+
+       /*
+        * negTokenInitSize indicates the bytes needed to
+        * hold the ASN.1 encoding of the entire NegTokenInit
+        * SEQUENCE.
+        * 0xa0 [DER_LEN] + data
+        *
+        */
+       negTokenInitSize = 1 +
+               gssint_der_length_size(negTokenInitSeqSize) +
+               negTokenInitSeqSize;
+
+       tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
+
+       t = (unsigned char *) malloc(tlen);
+
+       if (t == NULL) {
+               return (-1);
+       }
+
+       ptr = t;
+
+       /* create the message */
+       if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
+                           &ptr, tlen)))
+               goto errout;
+
+       *ptr++ = CONTEXT; /* NegotiationToken identifier */
+       if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
+               goto errout;
+
+       *ptr++ = SEQUENCE;
+       if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
+                                        tlen - (int)(ptr-t))))
+               goto errout;
+
+       *ptr++ = CONTEXT; /* MechTypeList identifier */
+       if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
+                                        &ptr, tlen - (int)(ptr-t))))
+               goto errout;
+
+       /* We already encoded the MechSetList */
+       (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
+                     spnego_ctx->DER_mechTypes.length);
+
+       ptr += spnego_ctx->DER_mechTypes.length;
+
+       if (req_flags != 0) {
+               if ((ret = put_req_flags(&ptr, req_flags,
+                                        tlen - (int)(ptr-t))))
+                       goto errout;
+       }
+
+       if (data != NULL) {
+               *ptr++ = CONTEXT | 0x02;
+               if ((ret = gssint_put_der_length(rspTokenSize,
+                               &ptr, tlen - (int)(ptr - t))))
+                       goto errout;
+
+               if ((ret = put_input_token(&ptr, data,
+                       tlen - (int)(ptr - t))))
+                       goto errout;
+       }
+
+       if (mechListMIC != GSS_C_NO_BUFFER) {
+               *ptr++ = CONTEXT | 0x03;
+               if ((ret = gssint_put_der_length(micTokenSize,
+                               &ptr, tlen - (int)(ptr - t))))
+                       goto errout;
+
+               if ((ret = put_input_token(&ptr, mechListMIC,
+                               tlen - (int)(ptr - t))))
+                       goto errout;
+       }
+
+errout:
+       if (ret != 0) {
+               if (t)
+                       free(t);
+               t = NULL;
+               tlen = 0;
+       }
+       outbuf->length = tlen;
+       outbuf->value = (void *) t;
+
+       return (ret);
+}
+
+/*
+ * create the server side spnego token passed back to
+ * gss_accept_sec_context and eventually up to the application program
+ * and over to the client.
+ */
+static int
+make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
+                         gss_buffer_t data, gss_buffer_t mechListMIC,
+                         send_token_flag sendtoken,
+                         gss_buffer_t outbuf)
+{
+       int tlen;
+       int ret;
+       int NegTokenTargSize;
+       int negresultTokenSize;
+       int NegTokenSize;
+       int rspTokenSize;
+       int micTokenSize;
+       int dataLen = 0;
+       unsigned char *t;
+       unsigned char *ptr;
+
+       if (outbuf == GSS_C_NO_BUFFER)
+               return (GSS_S_DEFECTIVE_TOKEN);
+
+       outbuf->length = 0;
+       outbuf->value = NULL;
+
+       /*
+        * ASN.1 encoding of the negResult
+        * ENUMERATED type is 3 bytes
+        *  ENUMERATED TAG, Length, Value,
+        * Plus 2 bytes for the CONTEXT id and length.
+        */
+       dataLen = 5;
+
+       /*
+        * calculate data length
+        *
+        * If this is the initial token, include length of
+        * mech_type and the negotiation result fields.
+        */
+       if (sendtoken == INIT_TOKEN_SEND) {
+               int mechlistTokenSize;
+               /*
+                * 1 byte for the CONTEXT ID(0xa0),
+                * 1 byte for the OID ID(0x06)
+                * 1 byte for OID Length field
+                * Plus the rest... (OID Length, OID value)
+                */
+               mechlistTokenSize = 3 + mech_wanted->length +
+                       gssint_der_length_size(mech_wanted->length);
+
+               dataLen += mechlistTokenSize;
+       }
+       if (data != NULL && data->length > 0) {
+               /* Length of the inner token */
+               rspTokenSize = 1 + gssint_der_length_size(data->length) +
+                       data->length;
+
+               dataLen += rspTokenSize;
+
+               /* Length of the outer token */
+               dataLen += 1 + gssint_der_length_size(rspTokenSize);
+       }
+       if (mechListMIC != NULL) {
+
+               /* Length of the inner token */
+               micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
+                       mechListMIC->length;
+
+               dataLen += micTokenSize;
+
+               /* Length of the outer token */
+               dataLen += 1 + gssint_der_length_size(micTokenSize);
+       }
+       /*
+        * Add size of DER encoded:
+        * NegTokenTarg [ SEQUENCE ] of
+        *    NegResult[0] ENUMERATED {
+        *      accept_completed(0),
+        *      accept_incomplete(1),
+        *      reject(2) }
+        *    supportedMech [1] MechType OPTIONAL,
+        *    responseToken [2] OCTET STRING OPTIONAL,
+        *    mechListMIC   [3] OCTET STRING OPTIONAL
+        *
+        * size = data->length + MechListMic + SupportedMech len +
+        *      Result Length + ASN.1 overhead
+        */
+       NegTokenTargSize = dataLen;
+       dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
+
+       /*
+        * NegotiationToken [ CHOICE ]{
+        *    negTokenInit  [0]  NegTokenInit,
+        *    negTokenTarg  [1]  NegTokenTarg }
+        */
+       NegTokenSize = dataLen;
+       dataLen += 1 + gssint_der_length_size(NegTokenSize);
+
+       tlen = dataLen;
+       t = (unsigned char *) malloc(tlen);
+
+       if (t == NULL) {
+               ret = GSS_S_DEFECTIVE_TOKEN;
+               goto errout;
+       }
+
+       ptr = t;
+
+       /*
+        * Indicate that we are sending CHOICE 1
+        * (NegTokenTarg)
+        */
+       *ptr++ = CONTEXT | 0x01;
+       if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
+               ret = GSS_S_DEFECTIVE_TOKEN;
+               goto errout;
+       }
+       *ptr++ = SEQUENCE;
+       if (gssint_put_der_length(NegTokenTargSize, &ptr,
+                                 tlen - (int)(ptr-t)) < 0) {
+               ret = GSS_S_DEFECTIVE_TOKEN;
+               goto errout;
+       }
+
+       /*
+        * First field of the NegTokenTarg SEQUENCE
+        * is the ENUMERATED NegResult.
+        */
+       *ptr++ = CONTEXT;
+       if (gssint_put_der_length(3, &ptr,
+                                 tlen - (int)(ptr-t)) < 0) {
+               ret = GSS_S_DEFECTIVE_TOKEN;
+               goto errout;
+       }
+       if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
+               ret = GSS_S_DEFECTIVE_TOKEN;
+               goto errout;
+       }
+       if (sendtoken == INIT_TOKEN_SEND) {
+               /*
+                * Next, is the Supported MechType
+                */
+               *ptr++ = CONTEXT | 0x01;
+               if (gssint_put_der_length(mech_wanted->length + 2,
+                                         &ptr,
+                                         tlen - (int)(ptr - t)) < 0) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       goto errout;
+               }
+               if (put_mech_oid(&ptr, mech_wanted,
+                                tlen - (int)(ptr - t)) < 0) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       goto errout;
+               }
+       }
+       if (data != NULL && data->length > 0) {
+               *ptr++ = CONTEXT | 0x02;
+               if (gssint_put_der_length(rspTokenSize, &ptr,
+                                         tlen - (int)(ptr - t)) < 0) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       goto errout;
+               }
+               if (put_input_token(&ptr, data,
+                                   tlen - (int)(ptr - t)) < 0) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       goto errout;
+               }
+       }
+       if (mechListMIC != NULL) {
+               *ptr++ = CONTEXT | 0x03;
+               if (gssint_put_der_length(micTokenSize, &ptr,
+                                         tlen - (int)(ptr - t)) < 0) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       goto errout;
+               }
+               if (put_input_token(&ptr, mechListMIC,
+                                   tlen - (int)(ptr - t)) < 0) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       goto errout;
+               }
+       }
+       ret = GSS_S_COMPLETE;
+errout:
+       if (ret != GSS_S_COMPLETE) {
+               if (t)
+                       free(t);
+       } else {
+               outbuf->length = ptr - t;
+               outbuf->value = (void *) t;
+       }
+
+       return (ret);
+}
+
+/* determine size of token */
+static int
+g_token_size(gss_OID_const mech, unsigned int body_size)
+{
+       int hdrsize;
+
+       /*
+        * Initialize the header size to the
+        * MECH_OID byte + the bytes needed to indicate the
+        * length of the OID + the OID itself.
+        *
+        * 0x06 [MECHLENFIELD] MECHDATA
+        */
+       hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
+
+       /*
+        * Now add the bytes needed for the initial header
+        * token bytes:
+        * 0x60 + [DER_LEN] + HDRSIZE
+        */
+       hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
+
+       return (hdrsize + body_size);
+}
+
+/*
+ * generate token header.
+ *
+ * Use DER Definite Length method per RFC2478
+ * Use of indefinite length encoding will not be compatible
+ * with Microsoft or others that actually follow the spec.
+ */
+static int
+g_make_token_header(gss_OID_const mech,
+                   int body_size,
+                   unsigned char **buf,
+                   int totallen)
+{
+       int hdrsize, ret = 0;
+       unsigned char *p = *buf;
+
+       hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
+
+       *(*buf)++ = HEADER_ID;
+       if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
+               return (ret);
+
+       *(*buf)++ = MECH_OID;
+       if ((ret = gssint_put_der_length(mech->length, buf,
+                           totallen - (int)(p - *buf))))
+               return (ret);
+       TWRITE_STR(*buf, mech->elements, ((int)mech->length));
+       return (0);
+}
+
+/*
+ * NOTE: This checks that the length returned by
+ * gssint_get_der_length() is not greater than the number of octets
+ * remaining, even though gssint_get_der_length() already checks, in
+ * theory.
+ */
+static int
+g_get_tag_and_length(unsigned char **buf, int tag, int buflen, int *outlen)
+{
+       unsigned char *ptr = *buf;
+       int ret = -1; /* pessimists, assume failure ! */
+       unsigned int encoded_len;
+
+       if (buflen > 1 && *ptr == tag) {
+               ptr++;
+               *outlen = gssint_get_der_length(&ptr, buflen - 1,
+                                               &encoded_len);
+               if (*outlen < 0) {
+                       ret = -1;
+               } else if (*outlen > buflen - (ptr - *buf)) {
+                       ret = -1;
+               } else
+                       ret = 0;
+       }
+
+       *buf = ptr;
+       return (ret);
+}
+
+static int
+g_verify_neg_token_init(unsigned char **buf_in, int cur_size)
+{
+       unsigned char *buf = *buf_in;
+       unsigned char *endptr = buf + cur_size;
+       int seqsize;
+       int ret = 0;
+       unsigned int bytes;
+
+       /*
+        * Verify this is a NegotiationToken type token
+        * - check for a0(context specific identifier)
+        * - get length and verify that enoughd ata exists
+        */
+       if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
+               return (G_BAD_TOK_HEADER);
+
+       cur_size = seqsize; /* should indicate bytes remaining */
+
+       /*
+        * Verify the next piece, it should identify this as
+        * a strucure of type NegTokenInit.
+        */
+       if (*buf++ == SEQUENCE) {
+               if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
+                       return (G_BAD_TOK_HEADER);
+               /*
+                * Make sure we have the entire buffer as described
+                */
+               if (buf + seqsize > endptr)
+                       return (G_BAD_TOK_HEADER);
+       } else {
+               return (G_BAD_TOK_HEADER);
+       }
+
+       cur_size = seqsize; /* should indicate bytes remaining */
+
+       /*
+        * Verify that the first blob is a sequence of mechTypes
+        */
+       if (*buf++ == CONTEXT) {
+               if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
+                       return (G_BAD_TOK_HEADER);
+               /*
+                * Make sure we have the entire buffer as described
+                */
+               if (buf + bytes > endptr)
+                       return (G_BAD_TOK_HEADER);
+       } else {
+               return (G_BAD_TOK_HEADER);
+       }
+
+       /*
+        * At this point, *buf should be at the beginning of the
+        * DER encoded list of mech types that are to be negotiated.
+        */
+       *buf_in = buf;
+
+       return (ret);
+
+}
+
+/* verify token header. */
+static int
+g_verify_token_header(gss_OID_const mech,
+                   int *body_size,
+                   unsigned char **buf_in,
+                   int tok_type,
+                   int toksize)
+{
+       unsigned char *buf = *buf_in;
+       int seqsize;
+       gss_OID_desc toid;
+       int ret = 0;
+       unsigned int bytes;
+
+       if ((toksize -= 1) < 0)
+               return (G_BAD_TOK_HEADER);
+
+       if (*buf++ != HEADER_ID)
+               return (G_BAD_TOK_HEADER);
+
+       if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
+               return (G_BAD_TOK_HEADER);
+
+       if ((seqsize + bytes) != toksize)
+               return (G_BAD_TOK_HEADER);
+
+       if ((toksize -= 1) < 0)
+               return (G_BAD_TOK_HEADER);
+
+
+       if (*buf++ != MECH_OID)
+               return (G_BAD_TOK_HEADER);
+
+       if ((toksize -= 1) < 0)
+               return (G_BAD_TOK_HEADER);
+
+       toid.length = *buf++;
+
+       if ((toksize -= toid.length) < 0)
+               return (G_BAD_TOK_HEADER);
+
+       toid.elements = buf;
+       buf += toid.length;
+
+       if (!g_OID_equal(&toid, mech))
+               ret = G_WRONG_MECH;
+
+       /*
+        * G_WRONG_MECH is not returned immediately because it's more important
+        * to return G_BAD_TOK_HEADER if the token header is in fact bad
+        */
+       if ((toksize -= 2) < 0)
+               return (G_BAD_TOK_HEADER);
+
+       if (!ret) {
+               *buf_in = buf;
+               *body_size = toksize;
+       }
+
+       return (ret);
+}
index 768d8265fc55811fe06a10abb65df7a84d1e8981..2823da9a18adc700f3199f333817c10ae4479367 100644 (file)
 static struct timeval TIMEOUT = { 25, 0 };
 
 generic_ret *
-create_principal_2(argp, clnt)
-       cprinc_arg *argp;
-       CLIENT *clnt;
+create_principal_2(cprinc_arg *argp, CLIENT *clnt)
 {
-       static generic_ret res;
+       static generic_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, CREATE_PRINCIPAL, xdr_cprinc_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, CREATE_PRINCIPAL,
+                     (xdrproc_t) xdr_cprinc_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 generic_ret *
-create_principal3_2(argp, clnt)
-       cprinc3_arg *argp;
-       CLIENT *clnt;
+create_principal3_2(cprinc3_arg *argp, CLIENT *clnt)
 {
-       static generic_ret res;
+       static generic_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, CREATE_PRINCIPAL3, xdr_cprinc3_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, CREATE_PRINCIPAL3,
+                     (xdrproc_t) xdr_cprinc3_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 generic_ret *
-delete_principal_2(argp, clnt)
-       dprinc_arg *argp;
-       CLIENT *clnt;
+delete_principal_2(dprinc_arg *argp, CLIENT *clnt)
 {
-       static generic_ret res;
+       static generic_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, DELETE_PRINCIPAL, xdr_dprinc_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, DELETE_PRINCIPAL,
+                     (xdrproc_t) xdr_dprinc_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 generic_ret *
-modify_principal_2(argp, clnt)
-       mprinc_arg *argp;
-       CLIENT *clnt;
+modify_principal_2(mprinc_arg *argp, CLIENT *clnt)
 {
-       static generic_ret res;
+       static generic_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, MODIFY_PRINCIPAL, xdr_mprinc_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MODIFY_PRINCIPAL,
+                     (xdrproc_t) xdr_mprinc_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 generic_ret *
-rename_principal_2(argp, clnt)
-       rprinc_arg *argp;
-       CLIENT *clnt;
+rename_principal_2(rprinc_arg *argp, CLIENT *clnt)
 {
-       static generic_ret res;
+       static generic_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, RENAME_PRINCIPAL, xdr_rprinc_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, RENAME_PRINCIPAL,
+                     (xdrproc_t) xdr_rprinc_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 gprinc_ret *
-get_principal_2(argp, clnt)
-       gprinc_arg *argp;
-       CLIENT *clnt;
+get_principal_2(gprinc_arg *argp, CLIENT *clnt)
 {
-       static gprinc_ret res;
+       static gprinc_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, GET_PRINCIPAL, xdr_gprinc_arg, argp, xdr_gprinc_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, GET_PRINCIPAL,
+                     (xdrproc_t) xdr_gprinc_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_gprinc_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 gprincs_ret *
-get_princs_2(argp, clnt)
-       gprincs_arg *argp;
-       CLIENT *clnt;
+get_princs_2(gprincs_arg *argp, CLIENT *clnt)
 {
-       static gprincs_ret res;
+       static gprincs_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, GET_PRINCS, xdr_gprincs_arg, argp,
-                     xdr_gprincs_ret, &res, TIMEOUT) != RPC_SUCCESS) { 
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, GET_PRINCS,
+                     (xdrproc_t) xdr_gprincs_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_gprincs_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) { 
             return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 generic_ret *
-chpass_principal_2(argp, clnt)
-       chpass_arg *argp;
-       CLIENT *clnt;
+chpass_principal_2(chpass_arg *argp, CLIENT *clnt)
 {
-       static generic_ret res;
+       static generic_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, CHPASS_PRINCIPAL, xdr_chpass_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, CHPASS_PRINCIPAL,
+                     (xdrproc_t) xdr_chpass_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 generic_ret *
-chpass_principal3_2(argp, clnt)
-       chpass3_arg *argp;
-       CLIENT *clnt;
+chpass_principal3_2(chpass3_arg *argp, CLIENT *clnt)
 {
-       static generic_ret res;
+       static generic_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, CHPASS_PRINCIPAL3, xdr_chpass3_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, CHPASS_PRINCIPAL3,
+                     (xdrproc_t) xdr_chpass3_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 generic_ret *
-setv4key_principal_2(argp, clnt)
-       setv4key_arg *argp;
-       CLIENT *clnt;
+setv4key_principal_2(setv4key_arg *argp, CLIENT *clnt)
 {
-       static generic_ret res;
+       static generic_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, SETV4KEY_PRINCIPAL, xdr_setv4key_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, SETV4KEY_PRINCIPAL,
+                     (xdrproc_t) xdr_setv4key_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 generic_ret *
-setkey_principal_2(argp, clnt)
-       setkey_arg *argp;
-       CLIENT *clnt;
+setkey_principal_2(setkey_arg *argp, CLIENT *clnt)
 {
-       static generic_ret res;
+       static generic_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, SETKEY_PRINCIPAL, xdr_setkey_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, SETKEY_PRINCIPAL,
+                     (xdrproc_t) xdr_setkey_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 generic_ret *
-setkey_principal3_2(argp, clnt)
-       setkey3_arg *argp;
-       CLIENT *clnt;
+setkey_principal3_2(setkey3_arg *argp, CLIENT *clnt)
 {
-       static generic_ret res;
+       static generic_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, SETKEY_PRINCIPAL3, xdr_setkey3_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, SETKEY_PRINCIPAL3,
+                     (xdrproc_t) xdr_setkey3_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 chrand_ret *
-chrand_principal_2(argp, clnt)
-       chrand_arg *argp;
-       CLIENT *clnt;
+chrand_principal_2(chrand_arg *argp, CLIENT *clnt)
 {
-       static chrand_ret res;
+       static chrand_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, CHRAND_PRINCIPAL, xdr_chrand_arg, argp, xdr_chrand_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, CHRAND_PRINCIPAL,
+                     (xdrproc_t) xdr_chrand_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_chrand_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 chrand_ret *
-chrand_principal3_2(argp, clnt)
-       chrand3_arg *argp;
-       CLIENT *clnt;
+chrand_principal3_2(chrand3_arg *argp, CLIENT *clnt)
 {
-       static chrand_ret res;
+       static chrand_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, CHRAND_PRINCIPAL3, xdr_chrand3_arg, argp, xdr_chrand_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, CHRAND_PRINCIPAL3,
+                     (xdrproc_t) xdr_chrand3_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_chrand_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 generic_ret *
-create_policy_2(argp, clnt)
-       cpol_arg *argp;
-       CLIENT *clnt;
+create_policy_2(cpol_arg *argp, CLIENT *clnt)
 {
-       static generic_ret res;
+       static generic_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, CREATE_POLICY, xdr_cpol_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, CREATE_POLICY,
+                     (xdrproc_t) xdr_cpol_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 generic_ret *
-delete_policy_2(argp, clnt)
-       dpol_arg *argp;
-       CLIENT *clnt;
+delete_policy_2(dpol_arg *argp, CLIENT *clnt)
 {
-       static generic_ret res;
+       static generic_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, DELETE_POLICY, xdr_dpol_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, DELETE_POLICY,
+                     (xdrproc_t) xdr_dpol_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 generic_ret *
-modify_policy_2(argp, clnt)
-       mpol_arg *argp;
-       CLIENT *clnt;
+modify_policy_2(mpol_arg *argp, CLIENT *clnt)
 {
-       static generic_ret res;
+       static generic_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, MODIFY_POLICY, xdr_mpol_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MODIFY_POLICY,
+                     (xdrproc_t) xdr_mpol_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 gpol_ret *
-get_policy_2(argp, clnt)
-       gpol_arg *argp;
-       CLIENT *clnt;
+get_policy_2(gpol_arg *argp, CLIENT *clnt)
 {
-       static gpol_ret res;
+       static gpol_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, GET_POLICY, xdr_gpol_arg, argp, xdr_gpol_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, GET_POLICY,
+                     (xdrproc_t) xdr_gpol_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_gpol_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
 gpols_ret *
-get_pols_2(argp, clnt)
-       gpols_arg *argp;
-       CLIENT *clnt;
+get_pols_2(gpols_arg *argp, CLIENT *clnt)
 {
-       static gpols_ret res;
+       static gpols_ret clnt_res;
 
-       memset((char *)&res, 0, sizeof(res));
-       if (clnt_call(clnt, GET_POLS, xdr_gpols_arg, argp,
-                     xdr_gpols_ret, &res, TIMEOUT) != RPC_SUCCESS) { 
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, GET_POLS,
+                     (xdrproc_t) xdr_gpols_arg, (caddr_t) argp,
+                     (xdrproc_t) xdr_gpols_ret, (caddr_t) &clnt_res,
+                     TIMEOUT) != RPC_SUCCESS) { 
             return (NULL);
        }
-       return (&res);
+       return (&clnt_res);
 }
 
-getprivs_ret *get_privs_2(argp, clnt)
-   void *argp;
-   CLIENT *clnt;
+getprivs_ret *
+get_privs_2(void *argp, CLIENT *clnt)
 {
-     static getprivs_ret res;
+     static getprivs_ret clnt_res;
 
-     memset((char *)&res, 0, sizeof(res));
-     if (clnt_call(clnt, GET_PRIVS, xdr_u_int32, argp,
-                  xdr_getprivs_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+     memset((char *)&clnt_res, 0, sizeof(clnt_res));
+     if (clnt_call(clnt, GET_PRIVS,
+                  (xdrproc_t) xdr_u_int32, (caddr_t) argp,
+                  (xdrproc_t) xdr_getprivs_ret, (caddr_t) &clnt_res,
+                  TIMEOUT) != RPC_SUCCESS) {
          return (NULL);
      }
-     return (&res);
+     return (&clnt_res);
 }
 
 generic_ret *
-init_2(argp, clnt)
-   void *argp;
-   CLIENT *clnt;
+init_2(void *argp, CLIENT *clnt)
 {
-     static generic_ret res;
+     static generic_ret clnt_res;
 
-     memset((char *)&res, 0, sizeof(res));
-     if (clnt_call(clnt, INIT, xdr_u_int32, argp,
-                  xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+     memset((char *)&clnt_res, 0, sizeof(clnt_res));
+     if (clnt_call(clnt, INIT,
+                  (xdrproc_t) xdr_u_int32, (caddr_t) argp,
+                  (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res,
+                  TIMEOUT) != RPC_SUCCESS) {
          return (NULL);
      }
-     return (&res);
+     return (&clnt_res);
 }
index 204fd90754f8f2a864440041fd62edffee6979cf..5f7ed4370387055aad03d6f950961c0eb596c11b 100644 (file)
@@ -4,71 +4,6 @@
  * $Id$
  * $Source$
  * 
- * $Log$
- * Revision 1.4  2005/08/20 09:14:59  raeburn
- * Rename all RPC functions from _1 to _2 to match current program version number;
- * likewise _1_svc to _2_svc in the kadmin server.  Delete the RPC functions from
- * the libkadm5clnt export list.
- *
- * Revision 1.3  2005/06/21 01:35:56  raeburn
- * Novell Database Abstraction Layer merge.
- * Will probably break things.
- *
- * Revision 1.2.26.1  2005/06/17 21:11:24  raeburn
- * Initial checkin of Novell Database Abstraction Layer changes.
- * Patches applied to 1.4.1 release code, updated to trunk, makefile dependencies
- * deleted when they caused cvs merge conflicts.
- *
- * Revision 1.2  1998/02/14 02:32:58  tlyu
- *     * client_init.c:
- *     * client_principal.c:
- *     * client_rpc.c:
- *     * clnt_policy.c:
- *     * clnt_privs.c: Update header locations.
- *
- *     * Makefile.in (LIBMAJOR): Bump major version to reflect change in
- *     rpc library.
- *
- * Revision 1.1  1996/07/24 22:22:48  tlyu
- *     * Makefile.in, configure.in: break out client lib into a
- *             subdirectory
- *
- * Revision 1.6  1996/07/22 20:35:57  marc
- * this commit includes all the changes on the OV_9510_INTEGRATION and
- * OV_MERGE branches.  This includes, but is not limited to, the new openvision
- * admin system, and major changes to gssapi to add functionality, and bring
- * the implementation in line with rfc1964.  before committing, the
- * code was built and tested for netbsd and solaris.
- *
- * Revision 1.5.4.1  1996/07/18 03:08:45  marc
- * merged in changes from OV_9510_BP to OV_9510_FINAL1
- *
- * Revision 1.5.2.1  1996/06/20  02:16:53  marc
- * File added to the repository on a branch
- *
- * Revision 1.5  1996/05/17  21:36:50  bjaspan
- * rename to kadm5, begin implementing version 2
- *
- * Revision 1.4  1996/05/16 21:45:51  bjaspan
- * u_int32 -> long, add krb5_context
- *
- * Revision 1.3  1994/09/20 16:25:05  bjaspan
- * [secure-admin/2436: API versioning fixes to various admin files]
- * [secure-releng/2502: audit secure-admin/2436: random API versioning fixes]
- *
- * Sandbox:
- *
- *  Unnecessary variable initialization removed.
- *
- * Revision 1.3  1994/09/12  20:26:39  jik
- * Unnecessary variable initialization removed.
- *
- * Revision 1.2  1994/08/16  18:52:02  jik
- * Versioning changes.
- *
- * Revision 1.1  1993/11/10  23:10:39  bjaspan
- * Initial revision
- *
  */
 
 #if !defined(lint) && !defined(__CODECENTER__)
index 58a91b00df92591227a85bee218449f3773a2b93..cf4ebd15dfcbf979eaaf4d44faddc62d412e0779 100644 (file)
@@ -406,6 +406,8 @@ krb_get_in_tkt_preauth_creds(user, instance, realm, service, sinstance, life,
     CREDENTIALS *creds;
     KRB_UINT32 *laddrp;
 {
+    int ok;
+    char key_string[BUFSIZ];
     KTEXT_ST cip_st;
     KTEXT cip = &cip_st;       /* Returned Ciphertext */
     int kerror;
@@ -420,6 +422,23 @@ krb_get_in_tkt_preauth_creds(user, instance, realm, service, sinstance, life,
                                   cip, &byteorder, &local_addr);
     if (kerror)
        return kerror;
+
+    /* If arg is null, we have to prompt for the password.  decrypt_tkt, by
+       way of the *_passwd_to_key functions, will prompt if the password is
+       NULL, but that means that each separate encryption type will prompt
+       separately.  Obtain the password first so that we can try multiple
+       encryption types without re-prompting.
+
+       Don't, however, prompt on a Windows or Macintosh environment, since
+       that's harder.  Rely on our caller to do it. */
+#if !(defined(_WIN32) || defined(USE_LOGIN_LIBRARY))
+    if (arg == NULL) {
+        ok = des_read_pw_string(key_string, sizeof(key_string), "Password", 0);
+        if (ok != 0)
+            return ok;
+        arg = key_string;
+    }
+#endif
     
     /* Attempt to decrypt the reply.  Loop trying password_to_key algorithms 
        until we succeed or we get an error other than "bad password" */
@@ -443,6 +462,7 @@ krb_get_in_tkt_preauth_creds(user, instance, realm, service, sinstance, life,
     }
 
     /* stomp stomp stomp */
+    memset(key_string, 0, sizeof(key_string));
     memset(cip->dat, 0, (size_t)cip->length);
     return kerror;
 }
index 7125435f9e2ecc328bd94b097691bb9f119a2ec7..15ea14564775d7313cb9ba0f3eb7318b102c5b8f 100644 (file)
@@ -117,3 +117,9 @@ int krb4int_save_credentials_addr(
 
 int krb4int_send_to_kdc_addr(KTEXT, KTEXT, char *,
                             struct sockaddr *, socklen_t *);
+
+/* 
+ * Exported by libdes425 and called by krb_get_in_pw_tkt, but not part of
+ * the standard DES interface and therefore not prototyped in des.h.
+ */
+int KRB5_CALLCONV des_read_pw_string(char *, int, char *, int);
index c4fc49bb005f3f1c56ba3d59feb71b8fc1aea1be..8c35acd45cef0edaa00da510408e336403571a32 100644 (file)
@@ -2285,6 +2285,9 @@ krb5_fcc_interpret(krb5_context context, int errnum)
     case ENXIO:
     default:
        retval = KRB5_CC_IO;            /* XXX */
+       krb5_set_error_message(context, retval,
+                              "Credentials cache I/O operation failed (%s)",
+                              strerror(errnum));
     }
     return retval;
 }
index c0358bfcbb01f97106251b0811e93a8b00a6c452..c31b90f34f13681a8a127389971c45ac68d9302a 100644 (file)
@@ -1092,7 +1092,10 @@ krb5_ktfileint_open(krb5_context context, krb5_keytab id, int mode)
     } else {
        /* gotta verify it instead... */
        if (!xfread(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
-           kerror = errno;
+           if (feof(KTFILEP(id)))
+               kerror = KRB5_KEYTAB_BADVNO;
+           else
+               kerror = errno;
            (void) krb5_unlock_file(context, fileno(KTFILEP(id)));
            (void) fclose(KTFILEP(id));
            return kerror;
index 9b90f71210ae3bb3a7a9e35870ca199e53264f5a..e1e1e755e7d1e404cdd11b4ee675629d17667df3 100644 (file)
@@ -169,13 +169,6 @@ init_common (krb5_context *context, krb5_boolean secure, krb5_boolean kdc)
        if ((retval = krb5_set_default_tgs_ktypes(ctx, NULL)))
                goto cleanup;
 
-       ctx->conf_tgs_ktypes = calloc(ctx->tgs_ktype_count, sizeof(krb5_enctype));
-       if (ctx->conf_tgs_ktypes == NULL && ctx->tgs_ktype_count != 0)
-           goto cleanup;
-       memcpy(ctx->conf_tgs_ktypes, ctx->tgs_ktypes,
-              sizeof(krb5_enctype) * ctx->tgs_ktype_count);
-       ctx->conf_tgs_ktypes_count = ctx->tgs_ktype_count;
-
        if ((retval = krb5_os_init_context(ctx, kdc)))
                goto cleanup;
 
@@ -269,11 +262,6 @@ krb5_free_context(krb5_context ctx)
          ctx->tgs_ktypes = 0;
      }
 
-     if (ctx->conf_tgs_ktypes) {
-        free(ctx->conf_tgs_ktypes);
-        ctx->conf_tgs_ktypes = 0;
-     }
-
      if (ctx->default_realm) {
          free(ctx->default_realm);
          ctx->default_realm = 0;
@@ -462,8 +450,7 @@ krb5_get_tgs_ktypes(krb5_context context, krb5_const_principal princ, krb5_encty
        /* This one is set *only* by reading the config file; it's not
           set by the application.  */
        return(get_profile_etype_list(context, ktypes, "default_tgs_enctypes",
-                                     context->conf_tgs_ktypes_count,
-                                     context->conf_tgs_ktypes));
+                                     0, NULL));
     else
        return(get_profile_etype_list(context, ktypes, "default_tgs_enctypes",
                                      context->tgs_ktype_count,
index 8439e23279306484dd1c7610b2fe3b0ec3c71d63..6de62b7806019ca5821614f7483582d61b3c513d 100644 (file)
@@ -1,4 +1,30 @@
-/* foo */
+/*
+ * lib/krb5/krb/kerrs.c
+ *
+ * Copyright 2006 Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ * error-message functions
+ */
 #include <stdarg.h>
 #include "k5-int.h"
 
index 356b98706f22c02f9c294da57e9e8463f0e7ab0c..1505c82cdf80eba45cc8ec93b1588b3abd0d7a65 100644 (file)
 
 #define MAX_USERNAME 65
 
+#if defined(__APPLE__) && defined(__MACH__)
+#include <hfs/hfs_mount.h>     /* XXX */
+#define FILE_OWNER_OK(UID)  ((UID) == 0 || (UID) == UNKNOWNUID)
+#else
+#define FILE_OWNER_OK(UID)  ((UID) == 0)
+#endif
+
 /*
  * Given a Kerberos principal "principal", and a local username "luser",
  * determine whether user is authorized to login according to the
@@ -112,7 +119,7 @@ krb5_kuserok(krb5_context context, krb5_principal principal, const char *luser)
        free(princname);
        return(FALSE);
     }
-    if ((sbuf.st_uid != pwd->pw_uid) && sbuf.st_uid) {
+    if (sbuf.st_uid != pwd->pw_uid && !FILE_OWNER_OK(sbuf.st_uid)) {
        fclose(fp);
        free(princname);
        return(FALSE);
index e9f9505c5ff0e06f514c4498204ded511225f8f4..ad5c4e93cd13dd09fd9583a7697fd4db9593fe60 100644 (file)
@@ -119,7 +119,6 @@ krb5_rc_default(krb5_context context, krb5_rcache *id)
 
     if ((retval = krb5_rc_resolve_type(context, id, 
                                       krb5_rc_default_type(context)))) {
-       k5_mutex_destroy(&(*id)->lock);
        FREE(*id);
        return retval;
     }
@@ -157,7 +156,6 @@ krb5_error_code krb5_rc_resolve_full(krb5_context context, krb5_rcache *id, char
 
     if ((retval = krb5_rc_resolve_type(context, id,type))) {
        FREE(type);
-       k5_mutex_destroy(&(*id)->lock);
        FREE(*id);
        return retval;
     }
index 52b57e44f32f288001d41856b66fa351f5e14654..8453d18323584c25a89dbfbc6e077d2407ceafc9 100644 (file)
@@ -139,11 +139,17 @@ krb5_rc_io_creat(krb5_context context, krb5_rc_iostuff *d, char **fn)
        case EROFS:
        case EEXIST:
            retval = KRB5_RC_IO_PERM;
+           krb5_set_error_message(context, retval,
+                                  "Cannot create replay cache: %s",
+                                  strerror(errno));
            do_not_unlink = 1;
            goto cleanup;
 
        default:
            retval = KRB5_RC_IO_UNKNOWN;
+           krb5_set_error_message(context, retval,
+                                  "Cannot create replay cache: %s",
+                                  strerror(errno));
            goto cleanup;
        }
     }
@@ -228,10 +234,16 @@ krb5_rc_io_open_internal(krb5_context context, krb5_rc_iostuff *d, char *fn,
        case EACCES:
        case EROFS:
            retval = KRB5_RC_IO_PERM;
+           krb5_set_error_message (context, retval,
+                                   "Cannot open replay cache %s: %s",
+                                   d->fn, strerror(errno));
            goto cleanup;
 
        default:
            retval = KRB5_RC_IO_UNKNOWN;
+           krb5_set_error_message (context, retval,
+                                   "Cannot open replay cache %s: %s",
+                                   d->fn, strerror(errno));
            goto cleanup;
        }
     }
@@ -269,7 +281,7 @@ krb5_error_code
 krb5_rc_io_move(krb5_context context, krb5_rc_iostuff *new1,
                krb5_rc_iostuff *old)
 {
-#if defined(_WIN32)
+#if defined(_WIN32) || defined(__CYGWIN__)
     char *new_fn = NULL;
     char *old_fn = NULL;
     off_t offset = 0;
@@ -348,14 +360,26 @@ krb5_rc_io_write(krb5_context context, krb5_rc_iostuff *d, krb5_pointer buf,
     if (write(d->fd, (char *) buf, num) == -1)
        switch(errno)
        {
-       case EBADF: return KRB5_RC_IO_UNKNOWN;
-       case EFBIG: return KRB5_RC_IO_SPACE;
 #ifdef EDQUOT
-       case EDQUOT: return KRB5_RC_IO_SPACE;
+       case EDQUOT:
 #endif
-       case ENOSPC: return KRB5_RC_IO_SPACE;
-       case EIO: return KRB5_RC_IO_IO;
-       default: return KRB5_RC_IO_UNKNOWN;
+       case EFBIG:
+       case ENOSPC:
+           krb5_set_error_message (context, KRB5_RC_IO_SPACE,
+                                   "Can't write to replay cache: %s",
+                                   strerror(errno));
+           return KRB5_RC_IO_SPACE;
+       case EIO:
+           krb5_set_error_message (context, KRB5_RC_IO_IO,
+                                   "Can't write to replay cache: %s",
+                                   strerror(errno));
+           return KRB5_RC_IO_IO;
+       case EBADF:
+       default:
+           krb5_set_error_message (context, KRB5_RC_IO_UNKNOWN,
+                                   "Can't write to replay cache: %s",
+                                   strerror(errno));
+           return KRB5_RC_IO_UNKNOWN;
        }
     return 0;
 }
@@ -373,7 +397,11 @@ krb5_rc_io_sync(krb5_context context, krb5_rc_iostuff *d)
        {
        case EBADF: return KRB5_RC_IO_UNKNOWN;
        case EIO: return KRB5_RC_IO_IO;
-       default: return KRB5_RC_IO_UNKNOWN;
+       default:
+           krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN,
+                                  "Cannot sync replay cache file: %s",
+                                  strerror(errno));
+           return KRB5_RC_IO_UNKNOWN;
        }
     }
     return 0;
@@ -387,9 +415,13 @@ krb5_rc_io_read(krb5_context context, krb5_rc_iostuff *d, krb5_pointer buf,
     if ((count = read(d->fd, (char *) buf, num)) == -1)
        switch(errno)
        {
-       case EBADF: return KRB5_RC_IO_UNKNOWN;
        case EIO: return KRB5_RC_IO_IO;
-       default: return KRB5_RC_IO_UNKNOWN;
+       case EBADF:
+       default:
+           krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN,
+                                  "Can't read from replay cache: %s",
+                                  strerror(errno));
+           return KRB5_RC_IO_UNKNOWN;
        }
     if (count == 0)
        return KRB5_RC_IO_EOF;
@@ -417,12 +449,24 @@ krb5_rc_io_destroy(krb5_context context, krb5_rc_iostuff *d)
     if (unlink(d->fn) == -1)
        switch(errno)
        {
-       case EBADF: return KRB5_RC_IO_UNKNOWN;
-       case EIO: return KRB5_RC_IO_IO;
-       case EPERM: return KRB5_RC_IO_PERM;
-       case EBUSY: return KRB5_RC_IO_PERM;
-       case EROFS: return KRB5_RC_IO_PERM;
-       default: return KRB5_RC_IO_UNKNOWN;
+       case EIO:
+           krb5_set_error_message(context, KRB5_RC_IO_IO,
+                                  "Can't destroy replay cache: %s",
+                                  strerror(errno));
+           return KRB5_RC_IO_IO;
+       case EPERM:
+       case EBUSY:
+       case EROFS:
+           krb5_set_error_message(context, KRB5_RC_IO_PERM,
+                                  "Can't destroy replay cache: %s",
+                                  strerror(errno));
+           return KRB5_RC_IO_PERM;
+       case EBADF:
+       default:
+           krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN,
+                                  "Can't destroy replay cache: %s",
+                                  strerror(errno));
+           return KRB5_RC_IO_UNKNOWN;
        }
     return 0;
 }
index 7ddefe5e82649e7e0c15862d4dd4ef14d3188940..ec2410331f925845060da2042269b76470c1cf59 100644 (file)
@@ -972,7 +972,6 @@ void svcauth_gssapi_unset_names(void)
                    gss_release_cred(&minor_stat, &server_creds_list[i]);
          free(server_creds_list);
          server_creds_list = NULL;
-         server_creds_count = 0;
      }
 
      if (server_name_list) {
@@ -981,8 +980,8 @@ void svcauth_gssapi_unset_names(void)
                    gss_release_name(&minor_stat, &server_name_list[i]);
          free(server_name_list);
          server_name_list = NULL;
-         server_creds_count = 0;
      }
+     server_creds_count = 0;
 }
 
 
index 5c33ffde52e7a44e65da095648020498a7aad470..f5841011f85954e263298b8f73d24490eeaf731a 100644 (file)
@@ -12,7 +12,7 @@ proc expired {} {
 
     expect {
        -i $server_id
-       -re "rpc_test server: Authen.*failed: .* referenced credentials have expired" { pass "expired" }
+       -re "rpc_test server: Authen.*failed:.*credential.*expired" { pass "expired" }
        timeout { fail "expired: timeout waiting for expired creds error" }
     }
 
index 0e254938e901c82673f73a6100c797558e77b020..b68b1dc9187f30e350422b7aad5281d3ec4a2dd2 100644 (file)
@@ -96,7 +96,7 @@ u_int32_t hash_accesses, hash_collisions, hash_expansions, hash_overflows,
 extern DB *
 __kdb2_hash_open(file, flags, mode, info, dflags)
        const char *file;
-       int32_t flags, mode, dflags;
+       int flags, mode, dflags;
        const HASHINFO *info;   /* Special directives for create */
 {
        struct stat statbuf;
index e730f46461564af9e72db3bd6d027aab9d6c12bd..b2ea2c2b36790aeeb5352e9ad762ea9ce3b744e1 100644 (file)
@@ -346,13 +346,10 @@ open_connection(host, fd, Errmsg, ErrmsgSz)
        if(!port) {
                sp = getservbyname(KPROP_SERVICE, "tcp");
                if (sp == 0) {
-                       (void) strncpy(Errmsg, KPROP_SERVICE, ErrmsgSz - 1);
-                       Errmsg[ErrmsgSz - 1] = '\0';
-                       (void) strncat(Errmsg, "/tcp: unknown service", ErrmsgSz - 1 - strlen(Errmsg));
-                       *fd = -1;
-                       return(0);
+                   my_sin.sin_port = htons(KPROP_PORT);
+               } else {
+                   my_sin.sin_port = sp->s_port;
                }
-               my_sin.sin_port = sp->s_port;
        } else
                my_sin.sin_port = port;
        s = socket(AF_INET, SOCK_STREAM, 0);
index 93e147e515b911af09a22be87c7d3cc62f23bb16..bc601de548db7c88a65e90c10f74ce3cf351513f 100644 (file)
@@ -29,6 +29,7 @@
 #define KPROP_SERVICE_NAME "host"
 #define TGT_SERVICE_NAME "krbtgt"
 #define KPROP_SERVICE "krb5_prop"
+#define KPROP_PORT 754
 
 #define KPROP_PROT_VERSION "kprop5_01"
 
index 6ded72aed2a032b4c10bfa1517fc5187a98885a2..9d87b9ebf5594279b2287fb7581f9acb57815e19 100644 (file)
@@ -193,9 +193,9 @@ void do_standalone()
                sp = getservbyname(KPROP_SERVICE, "tcp");
                if (sp == NULL) {
                        com_err(progname, 0, "%s/tcp: unknown service", KPROP_SERVICE);
-                       exit(1);
+                       my_sin.sin_port = htons(KPROP_PORT);
                }
-               my_sin.sin_port = sp->s_port;
+               else my_sin.sin_port = sp->s_port;
        } else {
                my_sin.sin_port = port;
        }
index e739e9bd98f248724ac6a88f7ae0013e6e62860a..6353dc41f137c2f2730e4ceefceaba20e75b574a 100644 (file)
@@ -249,7 +249,7 @@ proc v4ftp_test { } {
     expect -nocase -re "$localhostname.*ftp server .version \[0-9.\]*. ready."
     expect -re "Using authentication type GSSAPI; ADAT must follow"
     expect "GSSAPI accepted as authentication type"
-    expect "GSSAPI error major: Miscellaneous failure"
+    expect -re "GSSAPI error major: (Unspecified GSS|Miscellaneous) failure"
     expect {
        "GSSAPI error minor: Unsupported credentials cache format version number" {}
        "GSSAPI error minor: No credentials cache found" {}
index d229bbb3ad9a0e30cca7b6f315f289568cc75d9f..c5ef4f8e7bdc9548e6a9740c66eaf57f2e4a02db 100644 (file)
@@ -84,10 +84,10 @@ static errcode_t parse_std_line(char *line, struct parse_state *state)
        
        if (*line == 0)
                return 0;
-       if (line[0] == ';' || line[0] == '#')
-               return 0;
-       strip_line(line);
        cp = skip_over_blanks(line);
+       if (cp[0] == ';' || cp[0] == '#')
+               return 0;
+       strip_line(cp);
        ch = *cp;
        if (ch == 0)
                return 0;