Added EAP support with MD5-Challenge and SRP-SHA1 methods. Tested
authorJames Carlson <carlsonj@workingcode.com>
Sat, 2 Nov 2002 19:48:13 +0000 (19:48 +0000)
committerJames Carlson <carlsonj@workingcode.com>
Sat, 2 Nov 2002 19:48:13 +0000 (19:48 +0000)
on Linux (with both methods) and on Solaris (just MD5-Challenge).
Fixed several Makefiles that were missing references to required
modules such as tty.o.

28 files changed:
README.eap-srp [new file with mode: 0644]
configure
include/net/ppp_defs.h
pppd/Makefile.NeXT
pppd/Makefile.aix4
pppd/Makefile.bsd
pppd/Makefile.linux
pppd/Makefile.netbsd-1.2
pppd/Makefile.osf
pppd/Makefile.sol2
pppd/Makefile.sunos4
pppd/Makefile.svr4
pppd/Makefile.ultrix
pppd/auth.c
pppd/chap_ms.c
pppd/eap.c [new file with mode: 0644]
pppd/eap.h [new file with mode: 0644]
pppd/lcp.c
pppd/lcp.h
pppd/main.c
pppd/pathnames.h
pppd/pppcrypt.c [new file with mode: 0644]
pppd/pppcrypt.h [new file with mode: 0644]
pppd/pppd.8
pppd/pppd.h
pppd/sha1.h
pppd/srp-entry.8 [new file with mode: 0644]
pppd/srp-entry.c [new file with mode: 0644]

diff --git a/README.eap-srp b/README.eap-srp
new file mode 100644 (file)
index 0000000..6900b0d
--- /dev/null
@@ -0,0 +1,149 @@
+EAP with MD5-Challenge and SRP-SHA1 support
+by James Carlson, Sun Microsystems
+Version 2, September 22nd, 2002
+
+
+1.  What it does
+
+    The Extensible Authentication Protocol (EAP; RFC 2284) is a
+    security protocol that can be used with PPP.  It provides a means
+    to plug in multiple optional authentication methods.
+
+    This implementation includes the required default MD5-Challenge
+    method, which is similar to CHAP (RFC 1994), as well as the new
+    SRP-SHA1 method.  This latter method relies on an exchange that is
+    not vulnerable to dictionary attacks (as is CHAP), does not
+    require the server to keep a cleartext copy of the secret (as in
+    CHAP), supports identity privacy, and produces a temporary shared
+    key that could be used for data encryption.
+
+    The SRP-SHA1 method is based on draft-ietf-pppext-eap-srp-03.txt,
+    a work in progress.
+
+2.  Required libraries
+
+    Two other packages are required first.  Download and install
+    OpenSSL and Thomas Wu's SRP implementation.
+
+       http://www.openssl.org/ (or ftp://ftp.openssl.org/source/)
+       http://srp.stanford.edu/
+
+    Follow the directions in each package to install the SSL and SRP
+    libraries.  Once SRP is installed, you may run tconf as root to
+    create known fields, if desired.  (This step is not required.)
+
+3.  Installing the patch
+
+    The EAP-SRP patch described here is integrated into this version
+    of pppd.  The following patch may be used with older pppd sources:
+
+       ftp://playground.sun.com/carlsonj/eap/ppp-2.4.1-eap-1.tar.gz
+
+    Configure, compile, and install as root.  You may want to edit
+    pppd/Makefile after configuring to enable or disable optional
+    features.
+
+       % ./configure
+       % make
+       % su
+       # make install
+
+    If you use csh or tcsh, run "rehash" to pick up the new commands.
+
+    If you're using Solaris, and you run into trouble with the
+    pseudonym feature on the server side ("no DES here" shows in the
+    log file), make sure that you have the "domestic" versions of the
+    DES libraries linked.  You should see "crypt_d" in "ldd
+    /usr/local/bin/pppd".  If you see "crypt_i" instead, then make
+    sure that /usr/lib/libcrypt.* links to /usr/lib/libcrypt_d.*.  (If
+    you have the international version of Solaris, then you won't have
+    crypt_d.  You might want to find an alternative DES library.)
+
+4.  Adding the secrets
+
+    On the EAP SRP-SHA1 client side, access to the cleartext secret is
+    required.  This can be done in two ways:
+
+       - Enter the client name, server name, and password in the
+          /etc/ppp/srp-secrets file.  This file has the same format as
+          the existing chap-secrets and pap-secrets files.
+
+         clientname servername "secret here"
+
+       - Use the "password" option in any of the standard
+          configuration files (or the command line) to specify the
+          secret.
+
+         password "secret here"
+
+    On the EAP SRP-SHA1 server side, a secret verifier is required.
+    This is a one-way hash of the client's name and password.  To
+    generate this value, run the srp-entry program (see srp-entry(8)).
+    This program prompts for the client name and the passphrase (the
+    secret).  The output will be an entry, such as the following,
+    suitable for use in the server's srp-secrets file.  Note that if
+    this is transferred by cut-and-paste, the entry must be a single
+    line of text in the file.
+
+pppuser srpserver 0:LFDpwg4HBLi4/kWByzbZpW6pE95/iIWBSt7L.DAkHsvwQphtiq0f6reoUy/1LC1qYqjcrV97lCDmQHQd4KIACGgtkhttLdP3KMowvS0wLXLo25FPJeG2sMAUEWu/HlJPn2/gHyh9aT.ZxUs5MsoQ1E61sJkVBc.2qze1CdZiQGTK3qtWRP6DOpM1bfhKtPoVm.g.MiCcTMWzc54xJUIA0mgKtpthE3JrqCc81cXUt4DYi5yBzeeGTqrI0z2/Gj8Jp7pS4Fkq3GmnYjMxnKfQorFXNwl3m7JSaPa8Gj9/BqnorJOsnSMlIhBe6dy4CYytuTbNb4Wv/nFkmSThK782V:2cIyMp1yKslQgE *
+
+    The "secret" field consists of three entries separated by colons.
+    The first entry is the index of the modulus and generator from
+    SRP's /etc/tpasswd.conf.  If the special value 0 is used, then the
+    well-known modulus/generator value is used (this is recommended,
+    because it is much faster).  The second value is the verifier
+    value.  The third is the password "salt."  These latter two values
+    are encoded in base64 notation.
+
+    For EAP MD5-Challenge, both client and server use the existing
+    /etc/ppp/chap-secrets file.
+
+5.  Configuration options
+
+    There are two main options relating to EAP available for the
+    client.  These are:
+
+       refuse-eap              - refuse to authenticate with EAP
+       srp-use-pseudonym       - use the identity privacy if
+                                 offered by server
+
+    The second option stores a pseudonym, if offered by the EAP
+    SRP-SHA1 server, in the $HOME/.ppp_pseudonym file.  The pseudonym
+    is typically an encrypted version of the client identity.  During
+    EAP start-up, the pseudonym stored in this file is offered to the
+    peer as the identity.  If this is accepted by the peer, then
+    eavesdroppers will be unable to determine the identity of the
+    client.  Each time the client is authenticated, the server will
+    offer a new pseudoname to the client using an obscured (reversibly
+    encrypted) message.  Thus, access across successive sessions
+    cannot be tracked.
+
+    There are two main options for EAP on the server:
+
+       require-eap             - require client to use EAP
+       srp-pn-secret "string"  - set server's pseudoname secret
+
+    The second option sets the long-term secret used on the server to
+    encrypt the user's identity to produce pseudonames.  The
+    pseudoname is constructed by hashing this string with the current
+    date (to the nearest day) with SHA1, then using this hash as the
+    key for a DES encryption of the client's name.  The date is added
+    to the hash for two reasons.  First, this allows the pseudonym to
+    change daily.  Second, it allows the server to decode any previous
+    pseudonym by trying previous dates.
+
+    See the pppd(8) man page for additional options.
+
+6.  Comments welcome!
+
+    This is still an experimental implementation.  It has been tested
+    and reviewed carefully for correctness, but may still be
+    incomplete or have other flaws.  All comments are welcome.  Please
+    address them to the author:
+
+               james.d.carlson@sun.com
+
+    or, for EAP itself or the SRP extensions to EAP, to the IETF PPP
+    Extensions working group:
+
+               ietf-ppp@merit.edu
index 2744f4e5617616b6a5bb7b38bb09a6e592220cd3..04dd908bd2e6f47cfcce82f5db32b82b1369f844 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #!/bin/sh
-# $Id: configure,v 1.29 2002/09/07 05:15:25 carlsonj Exp $
+# $Id: configure,v 1.30 2002/11/02 19:48:12 carlsonj Exp $
 
 #  if [ -d /NextApps ]; then
 #    system="NeXTStep"
@@ -21,8 +21,8 @@ case $system in
 #      [0-3]*) state="ancient";;
 #      4*)     state="known"; ksrc="sunos4"; makext="sunos4";;
       5.[7-9]*|5.[1-9][0-9])   state="known"; ksrc="solaris"; makext="sol2";
-              case $arch in
-               sun4u)  archvariant='-64';;
+              case "`/usr/bin/isainfo -k`" in
+               sparcv9)        archvariant='-64';;
                *)      ;;
              esac;;
       5.[1-6]*)        state="known"; ksrc="solaris"; makext="sol2";;
@@ -33,7 +33,7 @@ case $system in
     elif gcc --version >/dev/null 2>&1; then
       archvariant=gcc$archvariant
       compiletype=.gcc
-      if [ "$arch" = "sun4u" ]; then
+      if [ "$archvariant" = "gcc-64" ]; then
         ( cd /tmp; touch ppp$$.c
          gcc -c -m64 ppp$$.c >/dev/null 2>&1 || (
            echo "gcc is unable to make 64 bit modules, and your $arch system needs them."
@@ -125,10 +125,12 @@ case $state in
     echo "This is a newer release of $system than is supported by"
     echo "this software.  It may or may not work.";;
   unknown)
-    echo "This software has not been ported to this system.  Sorry.";;
+    echo "This software has not been ported to $system.  Sorry.";;
   notincluded)
-    echo "Support for this system has not been included"
+    echo "Support for $system has not been included"
     echo "in this distribution.  Sorry.";;
+  known)
+    echo "Configuring for $system";;
 esac
 
 if [ -d "$ksrc" ]; then
index bda882ea1a285d09a8ebc5b23d12ed18b0a59e7c..85f1fc7fd35293df070c8ae52f97028f36f9fe04 100644 (file)
@@ -1,4 +1,4 @@
-/*     $Id: ppp_defs.h,v 1.15 2002/05/21 17:26:48 dfs Exp $    */
+/*     $Id: ppp_defs.h,v 1.16 2002/11/02 19:48:12 carlsonj Exp $       */
 
 /*
  * ppp_defs.h - PPP definitions.
@@ -85,6 +85,7 @@
 #define PPP_LQR                0xc025  /* Link Quality Report protocol */
 #define PPP_CHAP       0xc223  /* Cryptographic Handshake Auth. Protocol */
 #define PPP_CBCP       0xc029  /* Callback Control Protocol */
+#define PPP_EAP                0xc227  /* Extensible Authentication Protocol */
 
 /*
  * Values for FCS calculations.
index 7b873e2dabd0177f4f3dde38538d4e64ebef2f2d..d35059946c4bea23daba482236071c7914f6d762 100644 (file)
@@ -1,8 +1,8 @@
 #
 # pppd makefile for NeXT
 #
-# $Orignial: Makefile.ultrix,v 1.4 1994/09/01 00:40:40 paulus Exp $
-# $Id: Makefile.NeXT,v 1.7 2002/05/21 17:26:48 dfs Exp $
+# $Original: Makefile.ultrix,v 1.4 1994/09/01 00:40:40 paulus Exp $
+# $Id: Makefile.NeXT,v 1.8 2002/11/02 19:48:12 carlsonj Exp $
 #
 
 ARCHFLAGS = 
@@ -10,8 +10,8 @@ ARCHFLAGS =
 BINDIR = /usr/local/ppp/bin
 MANDIR = /usr/local/ppp/man
 
-OBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
-       ecp.o auth.o options.o demand.o utils.o sys-NeXT.o
+OBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o eap.o md5.o ccp.o \
+       ecp.o auth.o options.o demand.o utils.o sys-NeXT.o tty.o
 
 #
 # For HPPA and SPARC, define FIXSIGS to get around posix bugs in
index e173f6f76dba9321cc0908891cff35d5dc884427..f1bf643ee9e90a824184b49e5ce427e677b473df 100644 (file)
@@ -1,6 +1,6 @@
 #
 # pppd makefile for AIX 4.1
-# $Id: Makefile.aix4,v 1.5 2002/05/21 17:26:48 dfs Exp $
+# $Id: Makefile.aix4,v 1.6 2002/11/02 19:48:12 carlsonj Exp $
 #
 #ifndef BINDIR
 BINDIR = /usr/sbin
@@ -10,12 +10,14 @@ MANDIR = /usr/man
 #ENDIF
 
 PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
-       ecp.c auth.c options.c demand.c utils.c sys-aix4.c \
-       gencode.c grammar.c scanner.c nametoaddr.c optimize.c
+       ecp.c auth.c options.c demand.c utils.c sys-aix4.c eap.c tty.c
 
 PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
-       ecp.o auth.o options.o demand.o utils.o sys-aix4.o \
-       gencode.o grammar.o scanner.o nametoaddr.o optimize.o
+       ecp.o auth.o options.o demand.o utils.o sys-aix4.o eap.o tty.o
+
+# xxx what are these?  They're not here.
+#      gencode.c grammar.c scanner.c nametoaddr.c optimize.c
+#      gencode.o grammar.o scanner.o nametoaddr.o optimize.o
 
 CC = xlc
 DEBUG_FLAGS = -DDEBUGALL
index 83a899f2149fda58190c5172e3eb38dbbe88440e..c1f7738fb9f1337648fcd74ea8912a8a3f2dc72d 100644 (file)
@@ -1,4 +1,4 @@
-#      $Id: Makefile.bsd,v 1.16 2002/05/21 17:26:48 dfs Exp $
+#      $Id: Makefile.bsd,v 1.17 2002/11/02 19:48:12 carlsonj Exp $
 
 BINDIR?= /usr/sbin
 # -D_BITYPES is for FreeBSD, which doesn't define anything to
@@ -8,7 +8,7 @@ CFLAGS+= -g -I../include -DHAVE_PATHS_H -D_BITYPES
 
 PROG=  pppd
 SRCS=  main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
-       ecp.c demand.c auth.c options.c utils.c sys-bsd.c
+       ecp.c demand.c auth.c options.c utils.c sys-bsd.c eap.c tty.c
 MAN=   pppd.cat8
 MAN8=  pppd.8
 BINMODE=4555
index a8d01a4a3cf68b59dc80c20eb29c819278e65863..44f75e7339849c7e8b5787f9a3af9a7201e46e6c 100644 (file)
@@ -1,6 +1,6 @@
 #
 # pppd makefile for Linux
-# $Id: Makefile.linux,v 1.53 2002/10/27 12:56:26 fcusack Exp $
+# $Id: Makefile.linux,v 1.54 2002/11/02 19:48:12 carlsonj Exp $
 #
 
 # Default installation locations
@@ -8,19 +8,19 @@ BINDIR = $(DESTDIR)/usr/sbin
 MANDIR = $(DESTDIR)/usr/man
 INCDIR = $(DESTDIR)/usr/include
 
+TARGETS = pppd
+
 PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c ecp.c \
-          ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c cbcp.c \
-          demand.c utils.c tty.c sha1.c
+          ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c \
+          demand.c utils.c tty.c eap.c
 
-HEADERS = cbcp.h ccp.h chap.h chap_ms.h ecp.h fsm.h ipcp.h \
-       ipxcp.h lcp.h magic.h md4.h md5.h patchlevel.h pathnames.h pppd.h \
-       sha1.h tdb.h upap.h
+HEADERS = ccp.h chap.h ecp.h fsm.h ipcp.h \
+       ipxcp.h lcp.h magic.h md5.h patchlevel.h pathnames.h pppd.h \
+       upap.h eap.h
 
 MANPAGES = pppd.8
 PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o ecp.o \
-          auth.o options.o demand.o utils.o sys-linux.o ipxcp.o tty.o sha1.o
-
-all: pppd
+          auth.o options.o demand.o utils.o sys-linux.o ipxcp.o tty.o eap.o
 
 #
 # include dependencies if present
@@ -33,14 +33,11 @@ endif
 COPTS = -O2 -pipe -Wall -g
 LIBS =
 
-ifneq ($(wildcard /usr/lib/libcrypt.*),)
-LIBS += -lcrypt
-endif
-
 # Uncomment the next 2 lines to include support for Microsoft's
 # MS-CHAP authentication protocol.  Also, edit plugins/radius/Makefile.linux.
 CHAPMS=y
 USE_CRYPT=y
+MSLANMAN=y
 # Uncomment the next line to include support for MPPE.  CHAPMS (above) must
 # also be enabled.  Also, edit plugins/radius/Makefile.linux.
 MPPE=y
@@ -68,10 +65,15 @@ HAS_SHADOW=y
 #USE_PAM=y
 #HAVE_INET6=y
 
+# Enable plugins
 PLUGIN=y
 
+# Enable Microsoft proprietary Callback Control Protocol
 #CBCP=y
 
+# Enable EAP SRP-SHA1 authentication (requires libsrp)
+USE_SRP=y
+
 MAXOCTETS=y
 
 INCLUDE_DIRS= -I../include
@@ -82,15 +84,9 @@ CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS)
 
 ifdef CHAPMS
 CFLAGS   += -DCHAPMS=1
-ifndef USE_CRYPT
-LIBS     := -ldes $(LIBS)
-else
-CFLAGS   += -DUSE_CRYPT=1
-ifneq ($(wildcard /usr/include/crypt.h),)
-CFLAGS   += -DHAVE_CRYPT_H=1
-endif
-endif
+NEEDDES=y
 PPPDOBJS += md4.o chap_ms.o
+HEADERS        += md4.h chap_ms.h
 ifdef MSLANMAN
 CFLAGS   += -DMSLANMAN=1
 endif
@@ -99,15 +95,49 @@ CFLAGS   += -DMPPE=1
 endif
 endif
 
+# EAP SRP-SHA1
+ifdef USE_SRP
+CFLAGS += -DUSE_SRP -DOPENSSL -I/usr/local/ssl/include
+LIBS   += -lsrp -L/usr/local/ssl/lib -lcrypto
+TARGETS        += srp-entry
+EXTRAINSTALL = $(INSTALL) -s -c -m 555 srp-entry $(BINDIR)/srp-entry
+MANPAGES += srp-entry.8
+EXTRACLEAN += srp-entry.o
+NEEDDES=y
+else
+# OpenSSL has an integrated version of SHA-1, and its implementation
+# is incompatible with this local SHA-1 implementation.  We must use
+# one or the other, not both.
+PPPDSRCS += sha1.c
+HEADERS += sha1.h
+PPPDOBJS += sha1.o
+endif
+
 ifdef HAS_SHADOW
 CFLAGS   += -DHAS_SHADOW
-#LIBS     := -lshadow $(LIBS)
+#LIBS     += -lshadow $(LIBS)
+endif
+
+ifdef NEEDDES
+ifndef USE_CRYPT
+LIBS     += -ldes $(LIBS)
+else
+CFLAGS   += -DUSE_CRYPT=1
+ifneq ($(wildcard /usr/include/crypt.h),)
+CFLAGS   += -DHAVE_CRYPT_H=1
+endif
+ifneq ($(wildcard /usr/lib/libcrypt.*),)
+LIBS   += -lcrypt
+endif
+endif
+PPPDOBJS += pppcrypt.o
+HEADERS += pppcrypt.h
 endif
 
 # For "Pluggable Authentication Modules", see ftp.redhat.com:/pub/pam/.
 ifdef USE_PAM
 CFLAGS   += -DUSE_PAM
-LIBS     := -lpam -ldl $(LIBS)
+LIBS     += -lpam -ldl
 endif
 
 # Multi-linnk
@@ -125,11 +155,12 @@ ifdef USE_TDB
        CFLAGS += -DUSE_TDB=1
        PPPDSRCS += tdb.c
        PPPDOBJS += tdb.o
+       HEADERS += tdb.h
 endif
 
 # Lock library binary for Linux is included in 'linux' subdirectory.
 ifdef LOCKLIB
-LIBS     := -llock $(LIBS)
+LIBS     += -llock
 CFLAGS   += -DLOCKLIB=1
 endif
 
@@ -152,8 +183,10 @@ ifdef HAVE_INET6
 endif
 
 ifdef CBCP
+     PPPDSRCS += cbcp.c
      PPPDOBJS += cbcp.o
      CFLAGS += -DCBCP_SUPPORT
+     HEADERS += cbcp.h
 endif
 
 ifdef MAXOCTETS
@@ -162,8 +195,11 @@ endif
 
 INSTALL= install -o root
 
+all: $(TARGETS)
+
 install: pppd
        mkdir -p $(BINDIR) $(MANDIR)
+       $(EXTRAINSTALL)
        $(INSTALL) -s -c -m 555 pppd $(BINDIR)/pppd
        if chgrp pppusers $(BINDIR)/pppd 2>/dev/null; then \
          chmod o-rx,u+s $(BINDIR)/pppd; fi
@@ -172,12 +208,15 @@ install: pppd
 pppd: $(PPPDOBJS)
        $(CC) $(CFLAGS) $(LDFLAGS) -o pppd $(PPPDOBJS) $(LIBS)
 
+srp-entry:     srp-entry.c
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ srp-entry.c $(LIBS)
+
 install-devel:
        mkdir -p $(INCDIR)/pppd
        $(INSTALL) -c -m 644 $(HEADERS) $(INCDIR)/pppd
 
 clean:
-       rm -f $(PPPDOBJS) pppd *~ #* core
+       rm -f $(PPPDOBJS) $(EXTRACLEAN) $(TARGETS) *~ #* core
 
 depend:
        $(CPP) -M $(CFLAGS) $(PPPDSRCS) >.depend
index e50b7f54e2b722ec81c7f64a3cf96b91549a1eeb..4dbe7e9fa0d0e47ca96baf5036a56e789cd1061a 100644 (file)
@@ -4,7 +4,8 @@ PCAPDIR=${.CURDIR}/../../lib/libpcap
 
 PROG=  pppd
 SRCS=  auth.c cbcp.c ccp.c ecp.c chap.c chap_ms.c demand.c fsm.c ipcp.c \
-       ipxcp.c lcp.c magic.c main.c options.c sys-bsd.c upap.c
+       ipxcp.c lcp.c magic.c main.c options.c sys-bsd.c upap.c eap.c tty.c \
+       utils.c
 
 .PATH: ${PCAPDIR} ${.CURDIR}/../../sys/net
 MAN=   pppd.8 pppd.cbcp.8
@@ -12,6 +13,7 @@ SUBDIR=       pppstats chat
 BINMODE=4555
 BINOWN=        root
 
+# pick up md5, md4, and sha1
 LDADD= -lpcap -lcrypt -lutil
 DPADD= ${LIBPCAP} ${LIBCRYPT} ${LIBUTIL}
 CFLAGS+= -I. -I../include -DHAVE_PATHS_H -I${PCAPDIR} -DPPP_FILTER
index e680d2a58c8ada625f47c365081d8fe03bf5c6ca..385e3115875b6e4069b4a3f5d817720bb6c5b93a 100644 (file)
@@ -1,16 +1,18 @@
 #
 # pppd makefile for OSF/1 on DEC Alpha
-# $Id: Makefile.osf,v 1.12 2002/05/21 17:26:48 dfs Exp $
+# $Id: Makefile.osf,v 1.13 2002/11/02 19:48:12 carlsonj Exp $
 #
 
 BINDIR = /usr/local/etc
 MANDIR = /usr/local/man
 
 PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c ecp.c \
-       auth.c options.c demand.c utils.c sys-osf.c md4.c chap_ms.c sha1.c
+       auth.c options.c demand.c utils.c sys-osf.c md4.c chap_ms.c sha1.c \
+       eap.c tty.c
 
 PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o ecp.o \
-       auth.o options.o demand.o utils.o sys-osf.o md4.o chap_ms.o sha1.o
+       auth.o options.o demand.o utils.o sys-osf.o md4.o chap_ms.o sha1.o \
+       eap.o tty.o
 
 CC = cc
 #DEBUG_FLAGS = -DDEBUGALL
index 83b23af80251c4c5c3605df2e2a2cd9cb8d47af6..f15d9474d31f3b0a1ae948a372ed52fa948118cf 100644 (file)
@@ -1,6 +1,6 @@
 #
 # Makefile for pppd under Solaris 2.
-# $Id: Makefile.sol2,v 1.22 2002/09/07 05:15:25 carlsonj Exp $
+# $Id: Makefile.sol2,v 1.23 2002/11/02 19:48:12 carlsonj Exp $
 #
 
 include ../Makedefs.com
@@ -8,8 +8,8 @@ include ../Makedefs.com
 CFLAGS =  -I../include -DSVR4 -DSOL2 $(COPTS)
 LIBS   = -lsocket -lnsl
 
-OBJS   =  main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o tty.o \
-       ccp.o ecp.o auth.o options.o demand.o utils.o sys-solaris.o tdb.o
+OBJS   =  main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o eap.o md5.o tty.o \
+       ccp.o ecp.o auth.o options.o demand.o utils.o sys-solaris.o
 
 #
 # uncomment the following to enable plugins
index 09276d28bc6ddd5a6f06774fd79bb35aa7d81571..7bd5c3ad5c3b51b5e238e18158855037d1b71cd6 100644 (file)
@@ -1,6 +1,6 @@
 #
 # Makefile for pppd under SunOS 4.
-# $Id: Makefile.sunos4,v 1.12 2002/05/21 17:26:48 dfs Exp $
+# $Id: Makefile.sunos4,v 1.13 2002/11/02 19:48:12 carlsonj Exp $
 #
 
 include ../sunos4/Makedefs
@@ -13,7 +13,7 @@ CFLAGS = $(COPTS) -I../include -DSUNOS4 -DGIDSET_TYPE=int \
 all: pppd
 
 OBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o ecp.o \
-       auth.o options.o demand.o utils.o sys-sunos4.o tty.o
+       auth.o options.o demand.o utils.o sys-sunos4.o tty.o eap.o
 
 pppd:  $(OBJS)
        $(CC) -o pppd $(OBJS) $(LIBS)
index ea7e7f890540b35ee459c482f59936b1cb6489ec..f84d75885f1cec00a9dcf8bdf0ab508cdca27e92 100644 (file)
@@ -1,6 +1,6 @@
 #
-# Makefile for pppd under Solaris 2.
-# $Id: Makefile.svr4,v 1.15 2002/05/21 17:26:48 dfs Exp $
+# Makefile for pppd on generic System V release 4
+# $Id: Makefile.svr4,v 1.16 2002/11/02 19:48:12 carlsonj Exp $
 #
 
 include ../svr4/Makedefs
@@ -11,7 +11,7 @@ LIBS = -lsocket -lnsl -lc -L/usr/ucblib -lucb
 all: pppd
 
 OBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o ecp.o \
-       auth.o options.o demand.o utils.o sys-svr4.o
+       auth.o options.o demand.o utils.o sys-svr4.o eap.o tty.o
 
 pppd:  $(OBJS)
        $(CC) -o pppd $(OBJS) $(LIBS)
index 8ffa6248ffa221e27d253acbd6872e65eeca19da..57072c80b10f558e1b77e51762f8e3254491dff9 100644 (file)
@@ -1,16 +1,16 @@
 #
 # pppd makefile for Ultrix
-# $Id: Makefile.ultrix,v 1.12 2002/05/21 17:26:49 dfs Exp $
+# $Id: Makefile.ultrix,v 1.13 2002/11/02 19:48:12 carlsonj Exp $
 #
 
 BINDIR = /usr/local/etc
 MANDIR = /usr/local/man
 
 PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c ecp.c \
-       auth.c options.c demand.c utils.c sys-ultrix.c
+       auth.c options.c demand.c utils.c sys-ultrix.c eap.c tty.c
 
 PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o ecp.o \
-       auth.o options.o demand.o utils.o sys-ultrix.o
+       auth.o options.o demand.o utils.o sys-ultrix.o eap.o tty.o
 
 # CC = gcc
 DEBUG_FLAGS =
index cf042686a33557e14fb1f81ce29b83c7a28c787b..076392f09f42ad02dbd81fde510ac3a3ca22a975 100644 (file)
@@ -32,7 +32,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: auth.c,v 1.88 2002/10/27 12:56:26 fcusack Exp $"
+#define RCSID  "$Id: auth.c,v 1.89 2002/11/02 19:48:12 carlsonj Exp $"
 
 #include <stdio.h>
 #include <stddef.h>
@@ -74,6 +74,7 @@
 #include "ipcp.h"
 #include "upap.h"
 #include "chap.h"
+#include "eap.h"
 #ifdef CBCP_SUPPORT
 #include "cbcp.h"
 #endif
@@ -180,6 +181,7 @@ bool uselogin = 0;          /* Use /etc/passwd for checking PAP */
 bool cryptpap = 0;             /* Passwords in pap-secrets are encrypted */
 bool refuse_pap = 0;           /* Don't wanna auth. ourselves with PAP */
 bool refuse_chap = 0;          /* Don't wanna auth. ourselves with CHAP */
+bool refuse_eap = 0;           /* Don't wanna auth. ourselves with EAP */
 #ifdef CHAPMS
 bool refuse_mschap = 0;                /* Don't wanna auth. ourselves with MS-CHAP */
 bool refuse_mschap_v2 = 0;     /* Don't wanna auth. ourselves with MS-CHAPv2 */
@@ -208,10 +210,12 @@ static int  null_login __P((int));
 static int  get_pap_passwd __P((char *));
 static int  have_pap_secret __P((int *));
 static int  have_chap_secret __P((char *, char *, int, int *));
+static int  have_srp_secret __P((char *client, char *server, int need_ip,
+    int *lacks_ipp));
 static int  ip_addr_check __P((u_int32_t, struct permitted_ip *));
 static int  scan_authfile __P((FILE *, char *, char *, char *,
                               struct wordlist **, struct wordlist **,
-                              char *));
+                              char *, int));
 static void free_wordlist __P((struct wordlist *));
 static void auth_script __P((char *));
 static void auth_script_done __P((void *));
@@ -301,6 +305,12 @@ option_t auth_options[] = {
       &lcp_allowoptions[0].chap_mdtype },
 #endif
 
+    { "require-eap", o_bool, &lcp_wantoptions[0].neg_eap,
+      "Require EAP authentication from peer", OPT_PRIOSUB | 1,
+      &auth_required },
+    { "refuse-eap", o_bool, &refuse_eap,
+      "Don't agree to authenticate to peer with EAP", 1 },
+
     { "name", o_string, our_name,
       "Set local name for authentication",
       OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXNAMELEN },
@@ -554,7 +564,7 @@ link_established(unit)
            && protp->lowerup != NULL)
            (*protp->lowerup)(unit);
 
-    if (auth_required && !(go->neg_upap || go->neg_chap)) {
+    if (auth_required && !(go->neg_upap || go->neg_chap || go->neg_eap)) {
        /*
         * We wanted the peer to authenticate itself, and it refused:
         * if we have some address(es) it can use without auth, fine,
@@ -575,14 +585,20 @@ link_established(unit)
     new_phase(PHASE_AUTHENTICATE);
     used_login = 0;
     auth = 0;
-    if (go->neg_chap) {
+    if (go->neg_eap) {
+       eap_authpeer(unit, our_name);
+       auth |= EAP_PEER;
+    } else if (go->neg_chap) {
        ChapAuthPeer(unit, our_name, CHAP_DIGEST(go->chap_mdtype));
        auth |= CHAP_PEER;
     } else if (go->neg_upap) {
        upap_authpeer(unit);
        auth |= PAP_PEER;
     }
-    if (ho->neg_chap) {
+    if (ho->neg_eap) {
+       eap_authwithpeer(unit, user);
+       auth |= EAP_WITHPEER;
+    } else if (ho->neg_chap) {
        ChapAuthWithPeer(unit, user, CHAP_DIGEST(ho->chap_mdtype));
        auth |= CHAP_WITHPEER;
     } else if (ho->neg_upap) {
@@ -617,7 +633,7 @@ network_phase(unit)
     /*
      * If the peer had to authenticate, run the auth-up script now.
      */
-    if (go->neg_chap || go->neg_upap) {
+    if (go->neg_chap || go->neg_upap || go->neg_eap) {
        notify(auth_up_notifier, 0);
        auth_state = s_up;
        if (auth_script_state == s_down && auth_script_pid == 0) {
@@ -755,6 +771,9 @@ auth_peer_success(unit, protocol, prot_flavor, name, namelen)
     case PPP_PAP:
        bit = PAP_PEER;
        break;
+    case PPP_EAP:
+       bit = EAP_PEER;
+       break;
     default:
        warn("auth_peer_success: unknown protocol %x", protocol);
        return;
@@ -830,6 +849,9 @@ auth_withpeer_success(unit, protocol, prot_flavor)
            BZERO(passwd, MAXSECRETLEN);
        bit = PAP_WITHPEER;
        break;
+    case PPP_EAP:
+       bit = EAP_WITHPEER;
+       break;
     default:
        warn("auth_withpeer_success: unknown protocol %x", protocol);
        bit = 0;
@@ -1032,28 +1054,35 @@ auth_check_options()
     if (wo->chap_mdtype)
        wo->neg_chap = 1;
 
-    /* If authentication is required, ask peer for CHAP or PAP. */
+    /* If authentication is required, ask peer for CHAP, PAP, or EAP. */
     if (auth_required) {
        allow_any_ip = 0;
-       if (!wo->neg_chap && !wo->neg_upap) {
+       if (!wo->neg_chap && !wo->neg_upap && !wo->neg_eap) {
            wo->neg_chap = 1; wo->chap_mdtype = MDTYPE_ALL;
            wo->neg_upap = 1;
+           wo->neg_eap = 1;
        }
     } else {
        wo->neg_chap = 0; wo->chap_mdtype = MDTYPE_NONE;
        wo->neg_upap = 0;
+       wo->neg_eap = 0;
     }
 
     /*
      * Check whether we have appropriate secrets to use
-     * to authenticate the peer.
+     * to authenticate the peer.  Note that EAP can authenticate by way
+     * of a CHAP-like exchanges as well as SRP.
      */
     lacks_ip = 0;
     can_auth = wo->neg_upap && (uselogin || have_pap_secret(&lacks_ip));
-    if (!can_auth && (wo->neg_chap)) {
+    if (!can_auth && (wo->neg_chap || wo->neg_eap)) {
        can_auth = have_chap_secret((explicit_remote? remote_name: NULL),
                                    our_name, 1, &lacks_ip);
     }
+    if (!can_auth && wo->neg_eap) {
+       can_auth = have_srp_secret((explicit_remote? remote_name: NULL),
+                                   our_name, 1, &lacks_ip);
+    }
 
     if (auth_required && !can_auth && noauth_addrs == NULL) {
        if (default_auth) {
@@ -1088,21 +1117,36 @@ auth_reset(unit)
     int unit;
 {
     lcp_options *go = &lcp_gotoptions[unit];
-    lcp_options *ao = &lcp_allowoptions[0];
+    lcp_options *ao = &lcp_allowoptions[unit];
+    int hadchap;
 
+    hadchap = -1;
     ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL));
     ao->neg_chap = (!refuse_chap || !refuse_mschap || !refuse_mschap_v2)
-       && (passwd[0] != 0
-           || have_chap_secret(user, (explicit_remote? remote_name: NULL),
-                               0, NULL));
-
+       && (passwd[0] != 0 ||
+           (hadchap = have_chap_secret(user, (explicit_remote? remote_name:
+                                              NULL), 0, NULL)));
+    ao->neg_eap = !refuse_eap && (
+       passwd[0] != 0 ||
+       (hadchap == 1 || (hadchap == -1 && have_chap_secret(user,
+           (explicit_remote? remote_name: NULL), 0, NULL))) ||
+       have_srp_secret(user, (explicit_remote? remote_name: NULL), 0, NULL));
+
+    hadchap = -1;
     if (go->neg_upap && !uselogin && !have_pap_secret(NULL))
        go->neg_upap = 0;
     if (go->neg_chap) {
-       if (!have_chap_secret((explicit_remote? remote_name: NULL),
-                             our_name, 1, NULL))
+       if (!(hadchap = have_chap_secret((explicit_remote? remote_name: NULL),
+                             our_name, 1, NULL)))
            go->neg_chap = 0;
     }
+    if (go->neg_eap &&
+       (hadchap == 0 || (hadchap == -1 &&
+           !have_chap_secret((explicit_remote? remote_name: NULL), our_name,
+               1, NULL))) &&
+       !have_srp_secret((explicit_remote? remote_name: NULL), our_name, 1,
+           NULL))
+       go->neg_eap = 0;
 }
 
 
@@ -1173,7 +1217,7 @@ check_passwd(unit, auser, userlen, apasswd, passwdlen, msg)
 
     } else {
        check_access(f, filename);
-       if (scan_authfile(f, user, our_name, secret, &addrs, &opts, filename) < 0) {
+       if (scan_authfile(f, user, our_name, secret, &addrs, &opts, filename, 0) < 0) {
            warn("no PAP secret found for %s", user);
        } else {
            /*
@@ -1248,8 +1292,12 @@ static pam_handle_t *pamh = NULL;
  * echo off means password.
  */
 
-static int PAM_conv (int num_msg, const struct pam_message **msg,
-                    struct pam_response **resp, void *appdata_ptr)
+static int PAM_conv (int num_msg,
+#ifndef SOL2
+    const
+#endif
+    struct pam_message **msg,
+    struct pam_response **resp, void *appdata_ptr)
 {
     int replies = 0;
     struct pam_response *reply = NULL;
@@ -1486,7 +1534,7 @@ null_login(unit)
            return 0;
        check_access(f, filename);
 
-       i = scan_authfile(f, "", our_name, secret, &addrs, &opts, filename);
+       i = scan_authfile(f, "", our_name, secret, &addrs, &opts, filename, 0);
        ret = i >= 0 && secret[0] == 0;
        BZERO(secret, sizeof(secret));
        fclose(f);
@@ -1534,7 +1582,7 @@ get_pap_passwd(passwd)
     check_access(f, filename);
     ret = scan_authfile(f, user,
                        (remote_name[0]? remote_name: NULL),
-                       secret, NULL, NULL, filename);
+                       secret, NULL, NULL, filename, 0);
     fclose(f);
     if (ret < 0)
        return 0;
@@ -1571,7 +1619,7 @@ have_pap_secret(lacks_ipp)
        return 0;
 
     ret = scan_authfile(f, (explicit_remote? remote_name: NULL), our_name,
-                       NULL, &addrs, NULL, filename);
+                       NULL, &addrs, NULL, filename, 0);
     fclose(f);
     if (ret >= 0 && !some_ip_ok(addrs)) {
        if (lacks_ipp != 0)
@@ -1620,7 +1668,49 @@ have_chap_secret(client, server, need_ip, lacks_ipp)
     else if (server != NULL && server[0] == 0)
        server = NULL;
 
-    ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename);
+    ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0);
+    fclose(f);
+    if (ret >= 0 && need_ip && !some_ip_ok(addrs)) {
+       if (lacks_ipp != 0)
+           *lacks_ipp = 1;
+       ret = -1;
+    }
+    if (addrs != 0)
+       free_wordlist(addrs);
+
+    return ret >= 0;
+}
+
+
+/*
+ * have_srp_secret - check whether we have a SRP file with a
+ * secret that we could possibly use for authenticating `client'
+ * on `server'.  Either can be the null string, meaning we don't
+ * know the identity yet.
+ */
+static int
+have_srp_secret(client, server, need_ip, lacks_ipp)
+    char *client;
+    char *server;
+    int need_ip;
+    int *lacks_ipp;
+{
+    FILE *f;
+    int ret;
+    char *filename;
+    struct wordlist *addrs;
+
+    filename = _PATH_SRPFILE;
+    f = fopen(filename, "r");
+    if (f == NULL)
+       return 0;
+
+    if (client != NULL && client[0] == 0)
+       client = NULL;
+    else if (server != NULL && server[0] == 0)
+       server = NULL;
+
+    ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0);
     fclose(f);
     if (ret >= 0 && need_ip && !some_ip_ok(addrs)) {
        if (lacks_ipp != 0)
@@ -1674,7 +1764,7 @@ get_secret(unit, client, server, secret, secret_len, am_server)
        }
        check_access(f, filename);
 
-       ret = scan_authfile(f, client, server, secbuf, &addrs, &opts, filename);
+       ret = scan_authfile(f, client, server, secbuf, &addrs, &opts, filename, 0);
        fclose(f);
        if (ret < 0)
            return 0;
@@ -1699,6 +1789,56 @@ get_secret(unit, client, server, secret, secret_len, am_server)
     return 1;
 }
 
+
+/*
+ * get_srp_secret - open the SRP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int
+get_srp_secret(unit, client, server, secret, am_server)
+    int unit;
+    char *client;
+    char *server;
+    char *secret;
+    int am_server;
+{
+    FILE *fp;
+    int ret;
+    char *filename;
+    struct wordlist *addrs, *opts;
+
+    if (!am_server && passwd[0] != '\0') {
+       strlcpy(secret, passwd, MAXWORDLEN);
+    } else {
+       filename = _PATH_SRPFILE;
+       addrs = NULL;
+
+       fp = fopen(filename, "r");
+       if (fp == NULL) {
+           error("Can't open srp secret file %s: %m", filename);
+           return 0;
+       }
+       check_access(fp, filename);
+
+       secret[0] = '\0';
+       ret = scan_authfile(fp, client, server, secret, &addrs, &opts,
+           filename, am_server);
+       fclose(fp);
+       if (ret < 0)
+           return 0;
+
+       if (am_server)
+           set_allowed_addrs(unit, addrs, opts);
+       else if (opts != NULL)
+           free_wordlist(opts);
+       if (addrs != NULL)
+           free_wordlist(addrs);
+    }
+
+    return 1;
+}
+
 /*
  * set_allowed_addrs() - set the list of allowed addresses.
  * Also looks for `--' indicating options to apply for this peer
@@ -1986,9 +2126,11 @@ check_access(f, filename)
  * following words (extra options) are placed in a wordlist and
  * returned in *opts.
  * We assume secret is NULL or points to MAXWORDLEN bytes of space.
+ * Flags are non-zero if we need two colons in the secret in order to
+ * match.
  */
 static int
-scan_authfile(f, client, server, secret, addrs, opts, filename)
+scan_authfile(f, client, server, secret, addrs, opts, filename, flags)
     FILE *f;
     char *client;
     char *server;
@@ -1996,6 +2138,7 @@ scan_authfile(f, client, server, secret, addrs, opts, filename)
     struct wordlist **addrs;
     struct wordlist **opts;
     char *filename;
+    int flags;
 {
     int newline, xxx;
     int got_flag, best_flag;
@@ -2004,6 +2147,7 @@ scan_authfile(f, client, server, secret, addrs, opts, filename)
     char word[MAXWORDLEN];
     char atfile[MAXWORDLEN];
     char lsecret[MAXWORDLEN];
+    char *cp;
 
     if (addrs != NULL)
        *addrs = NULL;
@@ -2062,6 +2206,14 @@ scan_authfile(f, client, server, secret, addrs, opts, filename)
        if (newline)
            continue;
 
+       /*
+        * SRP-SHA1 authenticator should never be reading secrets from
+        * a file.  (Authenticatee may, though.)
+        */
+       if (flags && ((cp = strchr(word, ':')) == NULL ||
+           strchr(cp + 1, ':') == NULL))
+           continue;
+
        if (secret != NULL) {
            /*
             * Special syntax: @/pathname means read secret from file.
index 4e50d5bd24018d39ee5de7cc4f0bfe9663ce2d85..1041e0386759b5f9a26b08e80235dafa32bb6cb6 100644 (file)
@@ -40,7 +40,7 @@
  *   Copyright (c) 2002 Google, Inc.
  */
 
-#define RCSID  "$Id: chap_ms.c,v 1.22 2002/09/06 22:11:12 kad Exp $"
+#define RCSID  "$Id: chap_ms.c,v 1.23 2002/11/02 19:48:12 carlsonj Exp $"
 
 #ifdef CHAPMS
 
 #include <sys/types.h>
 #include <sys/time.h>
 #include <unistd.h>
-#ifdef HAVE_CRYPT_H
-#include <crypt.h>
-#endif
 
 #include "pppd.h"
 #include "chap.h"
 #include "chap_ms.h"
 #include "md4.h"
 #include "sha1.h"
-
-#ifndef USE_CRYPT
-#include <des.h>
-#endif
+#include "pppcrypt.h"
 
 static const char rcsid[] = RCSID;
 
@@ -72,9 +66,6 @@ static void   ChallengeHash __P((u_char[16], u_char *, char *, u_char[8]));
 static void    ascii2unicode __P((char[], int, u_char[]));
 static void    NTPasswordHash __P((char *, int, u_char[MD4_SIGNATURE_SIZE]));
 static void    ChallengeResponse __P((u_char *, u_char *, u_char[24]));
-static void    DesEncrypt __P((u_char *, u_char *, u_char[8]));
-static void    MakeKey __P((u_char *, u_char *));
-static u_char  Get7Bits __P((u_char *, int));
 static void    ChapMS_NT __P((u_char *, char *, int, u_char[24]));
 static void    ChapMS2_NT __P((char *, u_char[16], char *, char *, int,
                                u_char[24]));
@@ -85,11 +76,6 @@ static void  GenerateAuthenticatorResponse __P((char*, int, u_char[24],
 static void    ChapMS_LANMan __P((u_char *, char *, int, MS_ChapResponse *));
 #endif
 
-#ifdef USE_CRYPT
-static void    Expand __P((u_char *, u_char *));
-static void    Collapse __P((u_char *, u_char *));
-#endif
-
 #ifdef MPPE
 static void    Set_Start_Key __P((u_char *, char *, int));
 static void    SetMasterKeys __P((char *, int, u_char[24], int));
@@ -112,7 +98,7 @@ ChallengeResponse(u_char *challenge,
                  u_char PasswordHash[MD4_SIGNATURE_SIZE],
                  u_char response[24])
 {
-    char    ZPasswordHash[21];
+    u_char    ZPasswordHash[21];
 
     BZERO(ZPasswordHash, sizeof(ZPasswordHash));
     BCOPY(PasswordHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
@@ -122,138 +108,18 @@ ChallengeResponse(u_char *challenge,
           sizeof(ZPasswordHash), ZPasswordHash);
 #endif
 
-    DesEncrypt(challenge, ZPasswordHash +  0, &response[0]);
-    DesEncrypt(challenge, ZPasswordHash +  7, &response[8]);
-    DesEncrypt(challenge, ZPasswordHash + 14, &response[16]);
+    (void) DesSetkey(ZPasswordHash + 0);
+    DesEncrypt(challenge, response + 0);
+    (void) DesSetkey(ZPasswordHash + 7);
+    DesEncrypt(challenge, response + 8);
+    (void) DesSetkey(ZPasswordHash + 14);
+    DesEncrypt(challenge, response + 16);
 
 #if 0
     dbglog("ChallengeResponse - response %.24B", response);
 #endif
 }
 
-
-#ifdef USE_CRYPT
-static void
-DesEncrypt(u_char *clear, u_char *key, u_char cipher[8])
-{
-    u_char des_key[8];
-    u_char crypt_key[66];
-    u_char des_input[66];
-
-    MakeKey(key, des_key);
-
-    Expand(des_key, crypt_key);
-    setkey(crypt_key);
-
-#if 0
-    CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
-#endif
-
-    Expand(clear, des_input);
-    encrypt(des_input, 0);
-    Collapse(des_input, cipher);
-
-#if 0
-    CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
-#endif
-}
-
-#else /* USE_CRYPT */
-
-static void
-DesEncrypt(u_char *clear, u_char *key, u_char cipher[8])
-{
-    des_cblock         des_key;
-    des_key_schedule   key_schedule;
-
-    MakeKey(key, des_key);
-
-    des_set_key(&des_key, key_schedule);
-
-#if 0
-    CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
-#endif
-
-    des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
-
-#if 0
-    CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
-#endif
-}
-
-#endif /* USE_CRYPT */
-
-
-static u_char Get7Bits(u_char *input, int startBit)
-{
-    register unsigned int      word;
-
-    word  = (unsigned)input[startBit / 8] << 8;
-    word |= (unsigned)input[startBit / 8 + 1];
-
-    word >>= 15 - (startBit % 8 + 7);
-
-    return word & 0xFE;
-}
-
-#ifdef USE_CRYPT
-
-/* in == 8-byte string (expanded version of the 56-bit key)
- * out == 64-byte string where each byte is either 1 or 0
- * Note that the low-order "bit" is always ignored by by setkey()
- */
-static void Expand(u_char *in, u_char *out)
-{
-        int j, c;
-        int i;
-
-        for(i = 0; i < 64; in++){
-               c = *in;
-                for(j = 7; j >= 0; j--)
-                        *out++ = (c >> j) & 01;
-                i += 8;
-        }
-}
-
-/* The inverse of Expand
- */
-static void Collapse(u_char *in, u_char *out)
-{
-        int j;
-        int i;
-       unsigned int c;
-
-       for (i = 0; i < 64; i += 8, out++) {
-           c = 0;
-           for (j = 7; j >= 0; j--, in++)
-               c |= *in << j;
-           *out = c & 0xff;
-       }
-}
-#endif
-
-static void MakeKey(u_char *key, u_char *des_key)
-{
-    des_key[0] = Get7Bits(key,  0);
-    des_key[1] = Get7Bits(key,  7);
-    des_key[2] = Get7Bits(key, 14);
-    des_key[3] = Get7Bits(key, 21);
-    des_key[4] = Get7Bits(key, 28);
-    des_key[5] = Get7Bits(key, 35);
-    des_key[6] = Get7Bits(key, 42);
-    des_key[7] = Get7Bits(key, 49);
-
-#ifndef USE_CRYPT
-    des_set_odd_parity((des_cblock *)des_key);
-#endif
-
-#if 0
-    CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %.7B", key));
-    CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %.8B", des_key));
-#endif
-}
-
-
 static void
 ChallengeHash(u_char PeerChallenge[16], u_char *rchallenge,
              char *username, u_char Challenge[8])
@@ -348,7 +214,7 @@ static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
 
 static void
 ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
-             u_char LMResponse[24])
+             MS_ChapResponse *LMResponse)
 {
     int                        i;
     u_char             UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
@@ -358,8 +224,10 @@ ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
     BZERO(UcasePassword, sizeof(UcasePassword));
     for (i = 0; i < secret_len; i++)
        UcasePassword[i] = (u_char)toupper(secret[i]);
-    DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
-    DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
+    (void) DesSetkey(UcasePassword + 0);
+    DesEncrypt( StdText, PasswordHash + 0 );
+    (void) DesSetkey(UcasePassword + 7);
+    DesEncrypt( StdText, PasswordHash + 8 );
     ChallengeResponse(rchallenge, PasswordHash, LMResponse);
 }
 #endif
diff --git a/pppd/eap.c b/pppd/eap.c
new file mode 100644 (file)
index 0000000..cc3bc73
--- /dev/null
@@ -0,0 +1,2428 @@
+/*
+ * eap.c - Extensible Authentication Protocol for PPP (RFC 2284)
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Non-exclusive rights to redistribute, modify, translate, and use
+ * this software in source and binary forms, in whole or in part, is
+ * hereby granted, provided that the above copyright notice is
+ * duplicated in any source form, and that neither the name of the
+ * copyright holder nor the author is used to endorse or promote
+ * products derived from this software.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Original version by James Carlson
+ *
+ * This implementation of EAP supports MD5-Challenge and SRP-SHA1
+ * authentication styles.  Note that support of MD5-Challenge is a
+ * requirement of RFC 2284, and that it's essentially just a
+ * reimplementation of regular RFC 1994 CHAP using EAP messages.
+ *
+ * As an authenticator ("server"), there are multiple phases for each
+ * style.  In the first phase of each style, the unauthenticated peer
+ * name is queried using the EAP Identity request type.  If the
+ * "remotename" option is used, then this phase is skipped, because
+ * the peer's name is presumed to be known.
+ *
+ * For MD5-Challenge, there are two phases, and the second phase
+ * consists of sending the challenge itself and handling the
+ * associated response.
+ *
+ * For SRP-SHA1, there are four phases.  The second sends 's', 'N',
+ * and 'g'.  The reply contains 'A'.  The third sends 'B', and the
+ * reply contains 'M1'.  The forth sends the 'M2' value.
+ *
+ * As an authenticatee ("client"), there's just a single phase --
+ * responding to the queries generated by the peer.  EAP is an
+ * authenticator-driven protocol.
+ *
+ * Based on draft-ietf-pppext-eap-srp-03.txt.
+ */
+
+#define RCSID  "$Id: eap.c,v 1.1 2002/11/02 19:48:12 carlsonj Exp $"
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "pppd.h"
+#include "pathnames.h"
+#include "md5.h"
+/* For MD5_SIGNATURE_SIZE and MIN/MAX_CHALLENGE_LENGTH; should fix. */
+#include "chap.h"
+#include "eap.h"
+
+#ifdef USE_SRP
+#include <t_pwd.h>
+#include <t_server.h>
+#include <t_client.h>
+#include "pppcrypt.h"
+#endif /* USE_SRP */
+
+#ifndef SHA_DIGESTSIZE
+#define        SHA_DIGESTSIZE 20
+#endif
+
+static const char rcsid[] = RCSID;
+
+eap_state eap_states[NUM_PPP];         /* EAP state; one for each unit */
+static char *pn_secret = NULL;         /* Pseudonym generating secret */
+
+/*
+ * Command-line options.
+ */
+static option_t eap_option_list[] = {
+    { "eap-restart", o_int, &eap_states[0].es_server.ea_timeout,
+      "Set retransmit timeout for EAP Requests (server)" },
+    { "eap-max-sreq", o_int, &eap_states[0].es_server.ea_maxrequests,
+      "Set max number of EAP Requests sent (server)" },
+    { "eap-timeout", o_int, &eap_states[0].es_client.ea_timeout,
+      "Set time limit for peer EAP authentication" },
+    { "eap-max-rreq", o_int, &eap_states[0].es_client.ea_maxrequests,
+      "Set max number of EAP Requests allows (client)" },
+    { "eap-interval", o_int, &eap_states[0].es_rechallenge,
+      "Set interval for EAP rechallenge" },
+#ifdef USE_SRP
+    { "srp-interval", o_int, &eap_states[0].es_lwrechallenge,
+      "Set interval for SRP lightweight rechallenge" },
+    { "srp-pn-secret", o_string, &pn_secret,
+      "Long term pseudonym generation secret" },
+    { "srp-use-pseudonym", o_bool, &eap_states[0].es_usepseudo,
+      "Use pseudonym if offered one by server", 1 },
+#endif
+    { NULL }
+};
+
+/*
+ * Protocol entry points.
+ */
+static void eap_init __P((int unit));
+static void eap_input __P((int unit, u_char *inp, int inlen));
+static void eap_protrej __P((int unit));
+static void eap_lowerup __P((int unit));
+static void eap_lowerdown __P((int unit));
+static int  eap_printpkt __P((u_char *inp, int inlen,
+    void (*)(void *arg, char *fmt, ...), void *arg));
+
+struct protent eap_protent = {
+       PPP_EAP,                /* protocol number */
+       eap_init,               /* initialization procedure */
+       eap_input,              /* process a received packet */
+       eap_protrej,            /* process a received protocol-reject */
+       eap_lowerup,            /* lower layer has gone up */
+       eap_lowerdown,          /* lower layer has gone down */
+       NULL,                   /* open the protocol */
+       NULL,                   /* close the protocol */
+       eap_printpkt,           /* print a packet in readable form */
+       NULL,                   /* process a received data packet */
+       1,                      /* protocol enabled */
+       "EAP",                  /* text name of protocol */
+       NULL,                   /* text name of corresponding data protocol */
+       eap_option_list,        /* list of command-line options */
+       NULL,                   /* check requested options; assign defaults */
+       NULL,                   /* configure interface for demand-dial */
+       NULL                    /* say whether to bring up link for this pkt */
+};
+
+/*
+ * A well-known 2048 bit modulus.
+ */
+static const u_char wkmodulus[] = {
+       0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B,
+       0xF1, 0x66, 0xDE, 0x5E, 0x13, 0x89, 0x58, 0x2F,
+       0xAF, 0x72, 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07,
+       0xFC, 0x31, 0x92, 0x94, 0x3D, 0xB5, 0x60, 0x50,
+       0xA3, 0x73, 0x29, 0xCB, 0xB4, 0xA0, 0x99, 0xED,
+       0x81, 0x93, 0xE0, 0x75, 0x77, 0x67, 0xA1, 0x3D,
+       0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, 0x31, 0x0D,
+       0xCD, 0x7F, 0x48, 0xA9, 0xDA, 0x04, 0xFD, 0x50,
+       0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0,
+       0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3,
+       0x66, 0x1A, 0x05, 0xFB, 0xD5, 0xFA, 0xAA, 0xE8,
+       0x29, 0x18, 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8,
+       0x55, 0xF9, 0x79, 0x93, 0xEC, 0x97, 0x5E, 0xEA,
+       0xA8, 0x0D, 0x74, 0x0A, 0xDB, 0xF4, 0xFF, 0x74,
+       0x73, 0x59, 0xD0, 0x41, 0xD5, 0xC3, 0x3E, 0xA7,
+       0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, 0x77, 0x3B,
+       0xCA, 0x97, 0xB4, 0x3A, 0x23, 0xFB, 0x80, 0x16,
+       0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81,
+       0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A,
+       0x5B, 0x9D, 0x32, 0xE6, 0x88, 0xF8, 0x77, 0x48,
+       0x54, 0x45, 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D,
+       0x5E, 0xA7, 0x7A, 0x27, 0x75, 0xD2, 0xEC, 0xFA,
+       0x03, 0x2C, 0xFB, 0xDB, 0xF5, 0x2F, 0xB3, 0x78,
+       0x61, 0x60, 0x27, 0x90, 0x04, 0xE5, 0x7A, 0xE6,
+       0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, 0x53, 0x29,
+       0x9C, 0xCC, 0x04, 0x1C, 0x7B, 0xC3, 0x08, 0xD8,
+       0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82,
+       0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6,
+       0x94, 0xB5, 0xC8, 0x03, 0xD8, 0x9F, 0x7A, 0xE4,
+       0x35, 0xDE, 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75,
+       0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2,
+       0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73
+};
+
+/* Local forward declarations. */
+static void eap_server_timeout __P((void *arg));
+
+/*
+ * Convert EAP state code to printable string for debug.
+ */
+static const char *
+eap_state_name(esc)
+enum eap_state_code esc;
+{
+       static const char *state_names[] = { EAP_STATES };
+
+       return (state_names[(int)esc]);
+}
+
+/*
+ * eap_init - Initialize state for an EAP user.  This is currently
+ * called once by main() during start-up.
+ */
+static void
+eap_init(unit)
+int unit;
+{
+       eap_state *esp = &eap_states[unit];
+
+       BZERO(esp, sizeof (*esp));
+       esp->es_unit = unit;
+       esp->es_server.ea_timeout = EAP_DEFTIMEOUT;
+       esp->es_server.ea_maxrequests = EAP_DEFTRANSMITS;
+       esp->es_server.ea_id = (u_char)(drand48() * 0x100);
+       esp->es_client.ea_timeout = EAP_DEFREQTIME;
+       esp->es_client.ea_maxrequests = EAP_DEFALLOWREQ;
+}
+
+/*
+ * eap_client_timeout - Give up waiting for the peer to send any
+ * Request messages.
+ */
+static void
+eap_client_timeout(arg)
+void *arg;
+{
+       eap_state *esp = (eap_state *) arg;
+
+       if (!eap_client_active(esp))
+               return;
+
+       error("EAP: timeout waiting for Request from peer");
+       auth_withpeer_fail(esp->es_unit, PPP_EAP);
+       esp->es_client.ea_state = eapBadAuth;
+}
+
+/*
+ * eap_authwithpeer - Authenticate to our peer (behave as client).
+ *
+ * Start client state and wait for requests.  This is called only
+ * after eap_lowerup.
+ */
+void
+eap_authwithpeer(unit, localname)
+int unit;
+char *localname;
+{
+       eap_state *esp = &eap_states[unit];
+
+       /* Save the peer name we're given */
+       esp->es_client.ea_name = localname;
+       esp->es_client.ea_namelen = strlen(localname);
+
+       esp->es_client.ea_state = eapListen;
+
+       /*
+        * Start a timer so that if the other end just goes
+        * silent, we don't sit here waiting forever.
+        */
+       if (esp->es_client.ea_timeout > 0)
+               TIMEOUT(eap_client_timeout, (void *)esp,
+                   esp->es_client.ea_timeout);
+}
+
+/*
+ * Format a standard EAP Failure message and send it to the peer.
+ * (Server operation)
+ */
+static void
+eap_send_failure(esp)
+eap_state *esp;
+{
+       u_char *outp;
+
+       outp = outpacket_buf;
+    
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_FAILURE, outp);
+       esp->es_server.ea_id++;
+       PUTCHAR(esp->es_server.ea_id, outp);
+       PUTSHORT(EAP_HEADERLEN, outp);
+
+       output(esp->es_unit, outpacket_buf, EAP_HEADERLEN + PPP_HDRLEN);
+
+       esp->es_server.ea_state = eapBadAuth;
+       auth_peer_fail(esp->es_unit, PPP_EAP);
+}
+
+/*
+ * Format a standard EAP Success message and send it to the peer.
+ * (Server operation)
+ */
+static void
+eap_send_success(esp)
+eap_state *esp;
+{
+       u_char *outp;
+
+       outp = outpacket_buf;
+    
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_SUCCESS, outp);
+       esp->es_server.ea_id++;
+       PUTCHAR(esp->es_server.ea_id, outp);
+       PUTSHORT(EAP_HEADERLEN, outp);
+
+       output(esp->es_unit, outpacket_buf, PPP_HDRLEN + EAP_HEADERLEN);
+
+       auth_peer_success(esp->es_unit, PPP_EAP, 0,
+           esp->es_server.ea_peer, esp->es_server.ea_peerlen);
+}
+
+#ifdef USE_SRP
+/*
+ * Set DES key according to pseudonym-generating secret and current
+ * date.
+ */
+static bool
+pncrypt_setkey(int timeoffs)
+{
+       struct tm *tp;
+       char tbuf[9];
+       SHA1_CTX ctxt;
+       u_char dig[SHA_DIGESTSIZE];
+       time_t reftime;
+
+       if (pn_secret == NULL)
+               return (0);
+       reftime = time(NULL) + timeoffs;
+       tp = localtime(&reftime);
+       SHA1Init(&ctxt);
+       SHA1Update(&ctxt, pn_secret, strlen(pn_secret));
+       strftime(tbuf, sizeof (tbuf), "%Y%m%d", tp);
+       SHA1Update(&ctxt, tbuf, strlen(tbuf));
+       SHA1Final(dig, &ctxt);
+       return (DesSetkey(dig));
+}
+
+static char base64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+struct b64state {
+       u_int32_t bs_bits;
+       int bs_offs;
+};
+
+static int
+b64enc(bs, inp, inlen, outp)
+struct b64state *bs;
+u_char *inp;
+int inlen;
+u_char *outp;
+{
+       int outlen = 0;
+
+       while (inlen > 0) {
+               bs->bs_bits = (bs->bs_bits << 8) | *inp++;
+               inlen--;
+               bs->bs_offs += 8;
+               if (bs->bs_offs >= 24) {
+                       *outp++ = base64[(bs->bs_bits >> 18) & 0x3F];
+                       *outp++ = base64[(bs->bs_bits >> 12) & 0x3F];
+                       *outp++ = base64[(bs->bs_bits >> 6) & 0x3F];
+                       *outp++ = base64[bs->bs_bits & 0x3F];
+                       outlen += 4;
+                       bs->bs_offs = 0;
+                       bs->bs_bits = 0;
+               }
+       }
+       return (outlen);
+}
+
+static int
+b64flush(bs, outp)
+struct b64state *bs;
+u_char *outp;
+{
+       int outlen = 0;
+
+       if (bs->bs_offs == 8) {
+               *outp++ = base64[(bs->bs_bits >> 2) & 0x3F];
+               *outp++ = base64[(bs->bs_bits << 4) & 0x3F];
+               outlen = 2;
+       } else if (bs->bs_offs == 16) {
+               *outp++ = base64[(bs->bs_bits >> 10) & 0x3F];
+               *outp++ = base64[(bs->bs_bits >> 4) & 0x3F];
+               *outp++ = base64[(bs->bs_bits << 2) & 0x3F];
+               outlen = 3;
+       }
+       bs->bs_offs = 0;
+       bs->bs_bits = 0;
+       return (outlen);
+}
+
+static int
+b64dec(bs, inp, inlen, outp)
+struct b64state *bs;
+u_char *inp;
+int inlen;
+u_char *outp;
+{
+       int outlen = 0;
+       char *cp;
+
+       while (inlen > 0) {
+               if ((cp = strchr(base64, *inp++)) == NULL)
+                       break;
+               bs->bs_bits = (bs->bs_bits << 6) | (cp - base64);
+               inlen--;
+               bs->bs_offs += 6;
+               if (bs->bs_offs >= 8) {
+                       *outp++ = bs->bs_bits >> (bs->bs_offs - 8);
+                       outlen++;
+                       bs->bs_offs -= 8;
+               }
+       }
+       return (outlen);
+}
+#endif /* USE_SRP */
+
+/*
+ * Assume that current waiting server state is complete and figure
+ * next state to use based on available authentication data.  'status'
+ * indicates if there was an error in handling the last query.  It is
+ * 0 for success and non-zero for failure.
+ */
+static void
+eap_figure_next_state(esp, status)
+eap_state *esp;
+int status;
+{
+#ifdef USE_SRP
+       unsigned char secbuf[MAXWORDLEN], clear[8], *sp, *dp;
+       struct t_pw tpw;
+       struct t_confent *tce, mytce;
+       char *cp, *cp2;
+       struct t_server *ts;
+       int id, i, plen, toffs;
+       u_char vals[2];
+       struct b64state bs;
+#endif /* USE_SRP */
+
+       esp->es_server.ea_timeout = esp->es_savedtime;
+       switch (esp->es_server.ea_state) {
+       case eapBadAuth:
+               return;
+
+       case eapIdentify:
+#ifdef USE_SRP
+               /* Discard any previous session. */
+               ts = (struct t_server *)esp->es_server.ea_session;
+               if (ts != NULL) {
+                       t_serverclose(ts);
+                       esp->es_server.ea_session = NULL;
+                       esp->es_server.ea_skey = NULL;
+               }
+#endif /* USE_SRP */
+               if (status != 0) {
+                       esp->es_server.ea_state = eapBadAuth;
+                       break;
+               }
+#ifdef USE_SRP
+               /* If we've got a pseudonym, try to decode to real name. */
+               if (esp->es_server.ea_peerlen > SRP_PSEUDO_LEN &&
+                   strncmp(esp->es_server.ea_peer, SRP_PSEUDO_ID,
+                       SRP_PSEUDO_LEN) == 0 &&
+                   (esp->es_server.ea_peerlen - SRP_PSEUDO_LEN) * 3 / 4 <
+                   sizeof (secbuf)) {
+                       BZERO(&bs, sizeof (bs));
+                       plen = b64dec(&bs,
+                           esp->es_server.ea_peer + SRP_PSEUDO_LEN,
+                           esp->es_server.ea_peerlen - SRP_PSEUDO_LEN,
+                           secbuf);
+                       toffs = 0;
+                       for (i = 0; i < 5; i++) {
+                               pncrypt_setkey(toffs);
+                               toffs -= 86400;
+                               if (!DesDecrypt(secbuf, clear)) {
+                                       dbglog("no DES here; cannot decode "
+                                           "pseudonym");
+                                       return;
+                               }
+                               id = *(unsigned char *)clear;
+                               if (id + 1 <= plen && id + 9 > plen)
+                                       break;
+                       }
+                       if (plen % 8 == 0 && i < 5) {
+                               /*
+                                * Note that this is always shorter than the
+                                * original stored string, so there's no need
+                                * to realloc.
+                                */
+                               if ((i = plen = *(unsigned char *)clear) > 7)
+                                       i = 7;
+                               esp->es_server.ea_peerlen = plen;
+                               dp = (unsigned char *)esp->es_server.ea_peer;
+                               BCOPY(clear + 1, dp, i);
+                               plen -= i;
+                               dp += i;
+                               sp = secbuf + 8;
+                               while (plen > 0) {
+                                       (void) DesDecrypt(sp, dp);
+                                       sp += 8;
+                                       dp += 8;
+                                       plen -= 8;
+                               }
+                               esp->es_server.ea_peer[
+                                       esp->es_server.ea_peerlen] = '\0';
+                               dbglog("decoded pseudonym to \"%.*q\"",
+                                   esp->es_server.ea_peerlen,
+                                   esp->es_server.ea_peer);
+                       } else {
+                               dbglog("failed to decode real name");
+                               /* Stay in eapIdentfy state; requery */
+                               break;
+                       }
+               }
+               /* Look up user in secrets database. */
+               if (get_srp_secret(esp->es_unit, esp->es_server.ea_peer,
+                   esp->es_server.ea_name, (char *)secbuf, 1) != 0) {
+                       /* Set up default in case SRP entry is bad */
+                       esp->es_server.ea_state = eapMD5Chall;
+                       /* Get t_confent based on index in srp-secrets */
+                       id = strtol((char *)secbuf, &cp, 10);
+                       if (*cp++ != ':' || id < 0)
+                               break;
+                       if (id == 0) {
+                               mytce.index = 0;
+                               mytce.modulus.data = (u_char *)wkmodulus;
+                               mytce.modulus.len = sizeof (wkmodulus);
+                               mytce.generator.data = (u_char *)"\002";
+                               mytce.generator.len = 1;
+                               tce = &mytce;
+                       } else if ((tce = gettcid(id)) != NULL) {
+                               /*
+                                * Client will have to verify this modulus/
+                                * generator combination, and that will take
+                                * a while.  Lengthen the timeout here.
+                                */
+                               if (esp->es_server.ea_timeout > 0 &&
+                                   esp->es_server.ea_timeout < 30)
+                                       esp->es_server.ea_timeout = 30;
+                       } else {
+                               break;
+                       }
+                       if ((cp2 = strchr(cp, ':')) == NULL)
+                               break;
+                       *cp2++ = '\0';
+                       tpw.pebuf.name = esp->es_server.ea_peer;
+                       tpw.pebuf.password.len = t_fromb64((char *)tpw.pwbuf,
+                           cp);
+                       tpw.pebuf.password.data = tpw.pwbuf;
+                       tpw.pebuf.salt.len = t_fromb64((char *)tpw.saltbuf,
+                           cp2);
+                       tpw.pebuf.salt.data = tpw.saltbuf;
+                       if ((ts = t_serveropenraw(&tpw.pebuf, tce)) == NULL)
+                               break;
+                       esp->es_server.ea_session = (void *)ts;
+                       esp->es_server.ea_state = eapSRP1;
+                       vals[0] = esp->es_server.ea_id + 1;
+                       vals[1] = EAPT_SRP;
+                       t_serveraddexdata(ts, vals, 2);
+                       /* Generate B; must call before t_servergetkey() */
+                       t_servergenexp(ts);
+                       break;
+               }
+#endif /* USE_SRP */
+               esp->es_server.ea_state = eapMD5Chall;
+               break;
+
+       case eapSRP1:
+#ifdef USE_SRP
+               ts = (struct t_server *)esp->es_server.ea_session;
+               if (ts != NULL && status != 0) {
+                       t_serverclose(ts);
+                       esp->es_server.ea_session = NULL;
+                       esp->es_server.ea_skey = NULL;
+               }
+#endif /* USE_SRP */
+               if (status == 1) {
+                       esp->es_server.ea_state = eapMD5Chall;
+               } else if (status != 0 || esp->es_server.ea_session == NULL) {
+                       esp->es_server.ea_state = eapBadAuth;
+               } else {
+                       esp->es_server.ea_state = eapSRP2;
+               }
+               break;
+
+       case eapSRP2:
+#ifdef USE_SRP
+               ts = (struct t_server *)esp->es_server.ea_session;
+               if (ts != NULL && status != 0) {
+                       t_serverclose(ts);
+                       esp->es_server.ea_session = NULL;
+                       esp->es_server.ea_skey = NULL;
+               }
+#endif /* USE_SRP */
+               if (status != 0 || esp->es_server.ea_session == NULL) {
+                       esp->es_server.ea_state = eapBadAuth;
+               } else {
+                       esp->es_server.ea_state = eapSRP3;
+               }
+               break;
+
+       case eapSRP3:
+       case eapSRP4:
+#ifdef USE_SRP
+               ts = (struct t_server *)esp->es_server.ea_session;
+               if (ts != NULL && status != 0) {
+                       t_serverclose(ts);
+                       esp->es_server.ea_session = NULL;
+                       esp->es_server.ea_skey = NULL;
+               }
+#endif /* USE_SRP */
+               if (status != 0 || esp->es_server.ea_session == NULL) {
+                       esp->es_server.ea_state = eapBadAuth;
+               } else {
+                       esp->es_server.ea_state = eapOpen;
+               }
+               break;
+
+       case eapMD5Chall:
+               if (status != 0) {
+                       esp->es_server.ea_state = eapBadAuth;
+               } else {
+                       esp->es_server.ea_state = eapOpen;
+               }
+               break;
+
+       default:
+               esp->es_server.ea_state = eapBadAuth;
+               break;
+       }
+       if (esp->es_server.ea_state == eapBadAuth)
+               eap_send_failure(esp);
+}
+
+/*
+ * Format an EAP Request message and send it to the peer.  Message
+ * type depends on current state.  (Server operation)
+ */
+static void
+eap_send_request(esp)
+eap_state *esp;
+{
+       u_char *outp;
+       u_char *lenloc;
+       u_char *ptr;
+       int outlen;
+       int challen;
+       char *str;
+#ifdef USE_SRP
+       struct t_server *ts;
+       u_char clear[8], cipher[8], dig[SHA_DIGESTSIZE], *optr, *cp;
+       int i, j;
+       struct b64state b64;
+       SHA1_CTX ctxt;
+#endif /* USE_SRP */
+
+       /* Handle both initial auth and restart */
+       if (esp->es_server.ea_state < eapIdentify &&
+           esp->es_server.ea_state != eapInitial) {
+               esp->es_server.ea_state = eapIdentify;
+               if (explicit_remote) {
+                       /*
+                        * If we already know the peer's
+                        * unauthenticated name, then there's no
+                        * reason to ask.  Go to next state instead.
+                        */
+                       esp->es_server.ea_peer = remote_name;
+                       esp->es_server.ea_peerlen = strlen(remote_name);
+                       eap_figure_next_state(esp, 0);
+               }
+       }
+
+       if (esp->es_server.ea_maxrequests > 0 &&
+           esp->es_server.ea_requests >= esp->es_server.ea_maxrequests) {
+               if (esp->es_server.ea_responses > 0)
+                       error("EAP: too many Requests sent");
+               else
+                       error("EAP: no response to Requests");
+               eap_send_failure(esp);
+               return;
+       }
+
+       outp = outpacket_buf;
+    
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_REQUEST, outp);
+       PUTCHAR(esp->es_server.ea_id, outp);
+       lenloc = outp;
+       INCPTR(2, outp);
+
+       switch (esp->es_server.ea_state) {
+       case eapIdentify:
+               PUTCHAR(EAPT_IDENTITY, outp);
+               str = "Name";
+               challen = strlen(str);
+               BCOPY(str, outp, challen);
+               INCPTR(challen, outp);
+               break;
+
+       case eapMD5Chall:
+               PUTCHAR(EAPT_MD5CHAP, outp);
+               /*
+                * pick a random challenge length between
+                * MIN_CHALLENGE_LENGTH and MAX_CHALLENGE_LENGTH
+                */
+               challen = (drand48() *
+                   (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
+                           MIN_CHALLENGE_LENGTH;
+               PUTCHAR(challen, outp);
+               esp->es_challen = challen;
+               ptr = esp->es_challenge;
+               while (--challen >= 0)
+                       *ptr++ = (u_char) (drand48() * 0x100);
+               BCOPY(esp->es_challenge, outp, esp->es_challen);
+               INCPTR(esp->es_challen, outp);
+               BCOPY(esp->es_server.ea_name, outp, esp->es_server.ea_namelen);
+               INCPTR(esp->es_server.ea_namelen, outp);
+               break;
+
+#ifdef USE_SRP
+       case eapSRP1:
+               PUTCHAR(EAPT_SRP, outp);
+               PUTCHAR(EAPSRP_CHALLENGE, outp);
+
+               PUTCHAR(esp->es_server.ea_namelen, outp);
+               BCOPY(esp->es_server.ea_name, outp, esp->es_server.ea_namelen);
+               INCPTR(esp->es_server.ea_namelen, outp);
+
+               ts = (struct t_server *)esp->es_server.ea_session;
+               assert(ts != NULL);
+               PUTCHAR(ts->s.len, outp);
+               BCOPY(ts->s.data, outp, ts->s.len);
+               INCPTR(ts->s.len, outp);
+
+               if (ts->g.len == 1 && ts->g.data[0] == 2) {
+                       PUTCHAR(0, outp);
+               } else {
+                       PUTCHAR(ts->g.len, outp);
+                       BCOPY(ts->g.data, outp, ts->g.len);
+                       INCPTR(ts->g.len, outp);
+               }
+
+               if (ts->n.len != sizeof (wkmodulus) ||
+                   BCMP(ts->n.data, wkmodulus, sizeof (wkmodulus)) != 0) {
+                       BCOPY(ts->n.data, outp, ts->n.len);
+                       INCPTR(ts->n.len, outp);
+               }
+               break;
+
+       case eapSRP2:
+               PUTCHAR(EAPT_SRP, outp);
+               PUTCHAR(EAPSRP_SKEY, outp);
+
+               ts = (struct t_server *)esp->es_server.ea_session;
+               assert(ts != NULL);
+               BCOPY(ts->B.data, outp, ts->B.len);
+               INCPTR(ts->B.len, outp);
+               break;
+
+       case eapSRP3:
+               PUTCHAR(EAPT_SRP, outp);
+               PUTCHAR(EAPSRP_SVALIDATOR, outp);
+               PUTLONG(SRPVAL_EBIT, outp);
+               ts = (struct t_server *)esp->es_server.ea_session;
+               assert(ts != NULL);
+               BCOPY(t_serverresponse(ts), outp, SHA_DIGESTSIZE);
+               INCPTR(SHA_DIGESTSIZE, outp);
+
+               if (pncrypt_setkey(0)) {
+                       /* Generate pseudonym */
+                       optr = outp;
+                       cp = (unsigned char *)esp->es_server.ea_peer;
+                       if ((j = i = esp->es_server.ea_peerlen) > 7)
+                               j = 7;
+                       clear[0] = i;
+                       BCOPY(cp, clear + 1, j);
+                       i -= j;
+                       cp += j;
+                       if (!DesEncrypt(clear, cipher)) {
+                               dbglog("no DES here; not generating pseudonym");
+                               break;
+                       }
+                       BZERO(&b64, sizeof (b64));
+                       outp++;         /* space for pseudonym length */
+                       outp += b64enc(&b64, cipher, 8, outp);
+                       while (i >= 8) {
+                               (void) DesEncrypt(cp, cipher);
+                               outp += b64enc(&b64, cipher, 8, outp);
+                               cp += 8;
+                               i -= 8;
+                       }
+                       if (i > 0) {
+                               BCOPY(cp, clear, i);
+                               cp += i;
+                               while (i < 8) {
+                                       *cp++ = drand48() * 0x100;
+                                       i++;
+                               }
+                               (void) DesEncrypt(clear, cipher);
+                               outp += b64enc(&b64, cipher, 8, outp);
+                       }
+                       outp += b64flush(&b64, outp);
+
+                       /* Set length and pad out to next 20 octet boundary */
+                       i = outp - optr - 1;
+                       *optr = i;
+                       i %= SHA_DIGESTSIZE;
+                       if (i != 0) {
+                               while (i < SHA_DIGESTSIZE) {
+                                       *outp++ = drand48() * 0x100;
+                                       i++;
+                               }
+                       }
+
+                       /* Obscure the pseudonym with SHA1 hash */
+                       SHA1Init(&ctxt);
+                       SHA1Update(&ctxt, &esp->es_server.ea_id, 1);
+                       SHA1Update(&ctxt, esp->es_server.ea_skey,
+                           SESSION_KEY_LEN);
+                       SHA1Update(&ctxt, esp->es_server.ea_peer,
+                           esp->es_server.ea_peerlen);
+                       while (optr < outp) {
+                               SHA1Final(dig, &ctxt);
+                               cp = dig;
+                               while (cp < dig + SHA_DIGESTSIZE)
+                                       *optr++ ^= *cp++;
+                               SHA1Init(&ctxt);
+                               SHA1Update(&ctxt, &esp->es_server.ea_id, 1);
+                               SHA1Update(&ctxt, esp->es_server.ea_skey,
+                                   SESSION_KEY_LEN);
+                               SHA1Update(&ctxt, optr - SHA_DIGESTSIZE,
+                                   SHA_DIGESTSIZE);
+                       }
+               }
+               break;
+
+       case eapSRP4:
+               PUTCHAR(EAPT_SRP, outp);
+               PUTCHAR(EAPSRP_LWRECHALLENGE, outp);
+               challen = MIN_CHALLENGE_LENGTH +
+                   ((MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH) * drand48());
+               esp->es_challen = challen;
+               ptr = esp->es_challenge;
+               while (--challen >= 0)
+                       *ptr++ = drand48() * 0x100;
+               BCOPY(esp->es_challenge, outp, esp->es_challen);
+               INCPTR(esp->es_challen, outp);
+               break;
+#endif /* USE_SRP */
+
+       default:
+               return;
+       }
+
+       outlen = (outp - outpacket_buf) - PPP_HDRLEN;
+       PUTSHORT(outlen, lenloc);
+
+       output(esp->es_unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+       esp->es_server.ea_requests++;
+
+       if (esp->es_server.ea_timeout > 0)
+               TIMEOUT(eap_server_timeout, esp, esp->es_server.ea_timeout);
+}
+
+/*
+ * eap_authpeer - Authenticate our peer (behave as server).
+ *
+ * Start server state and send first request.  This is called only
+ * after eap_lowerup.
+ */
+void
+eap_authpeer(unit, localname)
+int unit;
+char *localname;
+{
+       eap_state *esp = &eap_states[unit];
+
+       /* Save the name we're given. */
+       esp->es_server.ea_name = localname;
+       esp->es_server.ea_namelen = strlen(localname);
+
+       esp->es_savedtime = esp->es_server.ea_timeout;
+
+       /* Lower layer up yet? */
+       if (esp->es_server.ea_state == eapInitial ||
+           esp->es_server.ea_state == eapPending) {
+               esp->es_server.ea_state = eapPending;
+               return;
+       }
+
+       esp->es_server.ea_state = eapPending;
+
+       /* ID number not updated here intentionally; hashed into M1 */
+       eap_send_request(esp);
+}
+
+/*
+ * eap_server_timeout - Retransmission timer for sending Requests
+ * expired.
+ */
+static void
+eap_server_timeout(arg)
+void *arg;
+{
+       eap_state *esp = (eap_state *) arg;
+
+       if (!eap_server_active(esp))
+               return;
+
+       /* EAP ID number must not change on timeout. */
+       eap_send_request(esp);
+}
+
+/*
+ * When it's time to send rechallenge the peer, this timeout is
+ * called.  Once the rechallenge is successful, the response handler
+ * will restart the timer.  If it fails, then the link is dropped.
+ */
+static void
+eap_rechallenge(arg)
+void *arg;
+{
+       eap_state *esp = (eap_state *)arg;
+
+       if (esp->es_server.ea_state != eapOpen &&
+           esp->es_server.ea_state != eapSRP4)
+               return;
+
+       esp->es_server.ea_requests = 0;
+       esp->es_server.ea_state = eapIdentify;
+       eap_figure_next_state(esp, 0);
+       esp->es_server.ea_id++;
+       eap_send_request(esp);
+}
+
+static void
+srp_lwrechallenge(arg)
+void *arg;
+{
+       eap_state *esp = (eap_state *)arg;
+
+       if (esp->es_server.ea_state != eapOpen ||
+           esp->es_server.ea_type != EAPT_SRP)
+               return;
+
+       esp->es_server.ea_requests = 0;
+       esp->es_server.ea_state = eapSRP4;
+       esp->es_server.ea_id++;
+       eap_send_request(esp);
+}
+
+/*
+ * eap_lowerup - The lower layer is now up.
+ *
+ * This is called before either eap_authpeer or eap_authwithpeer.  See
+ * link_established() in auth.c.  All that's necessary here is to
+ * return to closed state so that those two routines will do the right
+ * thing.
+ */
+static void
+eap_lowerup(unit)
+int unit;
+{
+       eap_state *esp = &eap_states[unit];
+
+       /* Discard any (possibly authenticated) peer name. */
+       if (esp->es_server.ea_peer != NULL &&
+           esp->es_server.ea_peer != remote_name)
+               free(esp->es_server.ea_peer);
+       esp->es_server.ea_peer = NULL;
+       if (esp->es_client.ea_peer != NULL)
+               free(esp->es_client.ea_peer);
+       esp->es_client.ea_peer = NULL;
+
+       esp->es_client.ea_state = eapClosed;
+       esp->es_server.ea_state = eapClosed;
+}
+
+/*
+ * eap_lowerdown - The lower layer is now down.
+ *
+ * Cancel all timeouts and return to initial state.
+ */
+static void
+eap_lowerdown(unit)
+int unit;
+{
+       eap_state *esp = &eap_states[unit];
+
+       if (eap_client_active(esp) && esp->es_client.ea_timeout > 0) {
+               UNTIMEOUT(eap_client_timeout, (void *)esp);
+       }
+       if (eap_server_active(esp)) {
+               if (esp->es_server.ea_timeout > 0) {
+                       UNTIMEOUT(eap_server_timeout, (void *)esp);
+               }
+       } else {
+               if ((esp->es_server.ea_state == eapOpen ||
+                   esp->es_server.ea_state == eapSRP4) &&
+                   esp->es_rechallenge > 0) {
+                       UNTIMEOUT(eap_rechallenge, (void *)esp);
+               }
+               if (esp->es_server.ea_state == eapOpen &&
+                   esp->es_lwrechallenge > 0) {
+                       UNTIMEOUT(srp_lwrechallenge, (void *)esp);
+               }
+       }
+
+       esp->es_client.ea_state = esp->es_server.ea_state = eapInitial;
+       esp->es_client.ea_requests = esp->es_server.ea_requests = 0;
+}
+
+/*
+ * eap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen.  If it does, it represents authentication
+ * failure.
+ */
+static void
+eap_protrej(unit)
+int unit;
+{
+       eap_state *esp = &eap_states[unit];
+
+       if (eap_client_active(esp)) {
+               error("EAP authentication failed due to Protocol-Reject");
+               auth_withpeer_fail(unit, PPP_EAP);
+       }
+       if (eap_server_active(esp)) {
+               error("EAP authentication of peer failed on Protocol-Reject");
+               auth_peer_fail(unit, PPP_EAP);
+       }
+       eap_lowerdown(unit);
+}
+
+/*
+ * Format and send a regular EAP Response message.
+ */
+static void
+eap_send_response(esp, id, typenum, str, lenstr)
+eap_state *esp;
+u_char id;
+u_char typenum;
+u_char *str;
+int lenstr;
+{
+       u_char *outp;
+       int msglen;
+
+       outp = outpacket_buf;
+
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_RESPONSE, outp);
+       PUTCHAR(id, outp);
+       esp->es_client.ea_id = id;
+       msglen = EAP_HEADERLEN + sizeof (u_char) + lenstr;
+       PUTSHORT(msglen, outp);
+       PUTCHAR(typenum, outp);
+       if (lenstr > 0) {
+               BCOPY(str, outp, lenstr);
+       }
+
+       output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+
+/*
+ * Format and send an MD5-Challenge EAP Response message.
+ */
+static void
+eap_chap_response(esp, id, hash, name, namelen)
+eap_state *esp;
+u_char id;
+u_char *hash;
+char *name;
+int namelen;
+{
+       u_char *outp;
+       int msglen;
+
+       outp = outpacket_buf;
+    
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_RESPONSE, outp);
+       PUTCHAR(id, outp);
+       esp->es_client.ea_id = id;
+       msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + MD5_SIGNATURE_SIZE +
+           namelen;
+       PUTSHORT(msglen, outp);
+       PUTCHAR(EAPT_MD5CHAP, outp);
+       PUTCHAR(MD5_SIGNATURE_SIZE, outp);
+       BCOPY(hash, outp, MD5_SIGNATURE_SIZE);
+       INCPTR(MD5_SIGNATURE_SIZE, outp);
+       if (namelen > 0) {
+               BCOPY(name, outp, namelen);
+       }
+
+       output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+
+#ifdef USE_SRP
+/*
+ * Format and send a SRP EAP Response message.
+ */
+static void
+eap_srp_response(esp, id, subtypenum, str, lenstr)
+eap_state *esp;
+u_char id;
+u_char subtypenum;
+u_char *str;
+int lenstr;
+{
+       u_char *outp;
+       int msglen;
+
+       outp = outpacket_buf;
+    
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_RESPONSE, outp);
+       PUTCHAR(id, outp);
+       esp->es_client.ea_id = id;
+       msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + lenstr;
+       PUTSHORT(msglen, outp);
+       PUTCHAR(EAPT_SRP, outp);
+       PUTCHAR(subtypenum, outp);
+       if (lenstr > 0) {
+               BCOPY(str, outp, lenstr);
+       }
+
+       output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+
+/*
+ * Format and send a SRP EAP Client Validator Response message.
+ */
+static void
+eap_srpval_response(esp, id, flags, str)
+eap_state *esp;
+u_char id;
+u_int32_t flags;
+u_char *str;
+{
+       u_char *outp;
+       int msglen;
+
+       outp = outpacket_buf;
+    
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_RESPONSE, outp);
+       PUTCHAR(id, outp);
+       esp->es_client.ea_id = id;
+       msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + sizeof (u_int32_t) +
+           SHA_DIGESTSIZE;
+       PUTSHORT(msglen, outp);
+       PUTCHAR(EAPT_SRP, outp);
+       PUTCHAR(EAPSRP_CVALIDATOR, outp);
+       PUTLONG(flags, outp);
+       BCOPY(str, outp, SHA_DIGESTSIZE);
+
+       output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+#endif /* USE_SRP */
+
+static void
+eap_send_nak(esp, id, type)
+eap_state *esp;
+u_char id;
+u_char type;
+{
+       u_char *outp;
+       int msglen;
+
+       outp = outpacket_buf;
+
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_RESPONSE, outp);
+       PUTCHAR(id, outp);
+       esp->es_client.ea_id = id;
+       msglen = EAP_HEADERLEN + 2 * sizeof (u_char);
+       PUTSHORT(msglen, outp);
+       PUTCHAR(EAPT_NAK, outp);
+       PUTCHAR(type, outp);
+
+       output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+
+#ifdef USE_SRP
+static char *
+name_of_pn_file()
+{
+       char *user, *path, *file;
+       struct passwd *pw;
+       size_t pl;
+       static bool pnlogged = 0;
+
+       pw = getpwuid(getuid());
+       if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0) {
+               errno = EINVAL;
+               return (NULL);
+       }
+       file = _PATH_PSEUDONYM;
+       pl = strlen(user) + strlen(file) + 2;
+       path = malloc(pl);
+       if (path == NULL)
+               return (NULL);
+       (void) slprintf(path, pl, "%s/%s", user, file);
+       if (!pnlogged) {
+               dbglog("pseudonym file: %s", path);
+               pnlogged = 1;
+       }
+       return (path);
+}
+
+static int
+open_pn_file(modebits)
+mode_t modebits;
+{
+       char *path;
+       int fd, err;
+
+       if ((path = name_of_pn_file()) == NULL)
+               return (-1);
+       fd = open(path, modebits, S_IRUSR | S_IWUSR);
+       err = errno;
+       free(path);
+       errno = err;
+       return (fd);
+}
+
+static void
+remove_pn_file()
+{
+       char *path;
+
+       if ((path = name_of_pn_file()) != NULL) {
+               (void) unlink(path);
+               (void) free(path);
+       }
+}
+
+static void
+write_pseudonym(esp, inp, len, id)
+eap_state *esp;
+u_char *inp;
+int len, id;
+{
+       u_char val;
+       u_char *datp, *digp;
+       SHA1_CTX ctxt;
+       u_char dig[SHA_DIGESTSIZE];
+       int dsize, fd, olen = len;
+
+       /*
+        * Do the decoding by working backwards.  This eliminates the need
+        * to save the decoded output in a separate buffer.
+        */
+       val = id;
+       while (len > 0) {
+               if ((dsize = len % SHA_DIGESTSIZE) == 0)
+                       dsize = SHA_DIGESTSIZE;
+               len -= dsize;
+               datp = inp + len;
+               SHA1Init(&ctxt);
+               SHA1Update(&ctxt, &val, 1);
+               SHA1Update(&ctxt, esp->es_client.ea_skey, SESSION_KEY_LEN);
+               if (len > 0) {
+                       SHA1Update(&ctxt, datp, SHA_DIGESTSIZE);
+               } else {
+                       SHA1Update(&ctxt, esp->es_client.ea_name,
+                           esp->es_client.ea_namelen);
+               }
+               SHA1Final(dig, &ctxt);
+               for (digp = dig; digp < dig + SHA_DIGESTSIZE; digp++)
+                       *datp++ ^= *digp;
+       }
+
+       /* Now check that the result is sane */
+       if (olen <= 0 || *inp + 1 > olen) {
+               dbglog("EAP: decoded pseudonym is unusable <%.*B>", olen, inp);
+               return;
+       }
+
+       /* Save it away */
+       fd = open_pn_file(O_WRONLY | O_CREAT | O_TRUNC);
+       if (fd < 0) {
+               dbglog("EAP: error saving pseudonym: %m");
+               return;
+       }
+       len = write(fd, inp + 1, *inp);
+       if (close(fd) != -1 && len == *inp) {
+               dbglog("EAP: saved pseudonym");
+               esp->es_usedpseudo = 0;
+       } else {
+               dbglog("EAP: failed to save pseudonym");
+               remove_pn_file();
+       }
+}
+#endif /* USE_SRP */
+
+/*
+ * eap_request - Receive EAP Request message (client mode).
+ */
+static void
+eap_request(esp, inp, id, len)
+eap_state *esp;
+u_char *inp;
+int id;
+int len;
+{
+       u_char typenum;
+       u_char vallen;
+       int secret_len;
+       char secret[MAXWORDLEN];
+       char rhostname[256];
+       MD5_CTX mdContext;
+       u_char hash[MD5_SIGNATURE_SIZE];
+#ifdef USE_SRP
+       struct t_client *tc;
+       struct t_num sval, gval, Nval, *Ap, Bval;
+       u_char vals[2];
+       SHA1_CTX ctxt;
+       u_char dig[SHA_DIGESTSIZE];
+       int fd;
+#endif /* USE_SRP */
+
+       /*
+        * Note: we update es_client.ea_id *only if* a Response
+        * message is being generated.  Otherwise, we leave it the
+        * same for duplicate detection purposes.
+        */
+
+       esp->es_client.ea_requests++;
+       if (esp->es_client.ea_maxrequests != 0 &&
+           esp->es_client.ea_requests > esp->es_client.ea_maxrequests) {
+               info("EAP: received too many Request messages");
+               if (esp->es_client.ea_timeout > 0) {
+                       UNTIMEOUT(eap_client_timeout, (void *)esp);
+               }
+               auth_withpeer_fail(esp->es_unit, PPP_EAP);
+               return;
+       }
+
+       if (len <= 0) {
+               error("EAP: empty Request message discarded");
+               return;
+       }
+
+       GETCHAR(typenum, inp);
+       len--;
+
+       switch (typenum) {
+       case EAPT_IDENTITY:
+               if (len > 0)
+                       info("EAP: Identity prompt \"%.*q\"", len, inp);
+#ifdef USE_SRP
+               if (esp->es_usepseudo &&
+                   (esp->es_usedpseudo == 0 ||
+                       (esp->es_usedpseudo == 1 &&
+                           id == esp->es_client.ea_id))) {
+                       esp->es_usedpseudo = 1;
+                       /* Try to get a pseudonym */
+                       if ((fd = open_pn_file(O_RDONLY)) >= 0) {
+                               strcpy(rhostname, SRP_PSEUDO_ID);
+                               len = read(fd, rhostname + SRP_PSEUDO_LEN,
+                                   sizeof (rhostname) - SRP_PSEUDO_LEN);
+                               /* XXX NAI unsupported */
+                               if (len > 0) {
+                                       eap_send_response(esp, id, typenum,
+                                           rhostname, len + SRP_PSEUDO_LEN);
+                               }
+                               (void) close(fd);
+                               if (len > 0)
+                                       break;
+                       }
+               }
+               /* Stop using pseudonym now. */
+               if (esp->es_usepseudo && esp->es_usedpseudo != 2) {
+                       remove_pn_file();
+                       esp->es_usedpseudo = 2;
+               }
+#endif /* USE_SRP */
+               eap_send_response(esp, id, typenum, esp->es_client.ea_name,
+                   esp->es_client.ea_namelen);
+               break;
+
+       case EAPT_NOTIFICATION:
+               if (len > 0)
+                       info("EAP: Notification \"%.*q\"", len, inp);
+               eap_send_response(esp, id, typenum, NULL, 0);
+               break;
+
+       case EAPT_NAK:
+               /*
+                * Avoid the temptation to send Response Nak in reply
+                * to Request Nak here.  It can only lead to trouble.
+                */
+               warn("EAP: unexpected Nak in Request; ignored");
+               /* Return because we're waiting for something real. */
+               return;
+
+       case EAPT_MD5CHAP:
+               if (len < 1) {
+                       error("EAP: received MD5-Challenge with no data");
+                       /* Bogus request; wait for something real. */
+                       return;
+               }
+               GETCHAR(vallen, inp);
+               len--;
+               if (vallen < 8 || vallen > len) {
+                       error("EAP: MD5-Challenge with bad length %d (8..%d)",
+                           vallen, len);
+                       /* Try something better. */
+                       eap_send_nak(esp, id, EAPT_SRP);
+                       break;
+               }
+
+               /* Not so likely to happen. */
+               if (vallen >= len + sizeof (rhostname)) {
+                       dbglog("EAP: trimming really long peer name down");
+                       BCOPY(inp + vallen, rhostname, sizeof (rhostname) - 1);
+                       rhostname[sizeof (rhostname) - 1] = '\0';
+               } else {
+                       BCOPY(inp + vallen, rhostname, len - vallen);
+                       rhostname[len - vallen] = '\0';
+               }
+
+               /* In case the remote doesn't give us his name. */
+               if (explicit_remote ||
+                   (remote_name[0] != '\0' && vallen == len))
+                       strlcpy(rhostname, remote_name, sizeof (rhostname));
+
+               /*
+                * Get the secret for authenticating ourselves with
+                * the specified host.
+                */
+               if (!get_secret(esp->es_unit, esp->es_client.ea_name,
+                   rhostname, secret, &secret_len, 0)) {
+                       dbglog("EAP: no MD5 secret for auth to %q", rhostname);
+                       eap_send_nak(esp, id, EAPT_SRP);
+                       break;
+               }
+               MD5Init(&mdContext);
+               typenum = id;
+               MD5Update(&mdContext, &typenum, 1);
+               MD5Update(&mdContext, secret, secret_len);
+               BZERO(secret, sizeof (secret));
+               MD5Update(&mdContext, inp, vallen);
+               MD5Final(hash, &mdContext);
+               eap_chap_response(esp, id, hash, esp->es_client.ea_name,
+                   esp->es_client.ea_namelen);
+               break;
+
+#ifdef USE_SRP
+       case EAPT_SRP:
+               if (len < 1) {
+                       error("EAP: received empty SRP Request");
+                       /* Bogus request; wait for something real. */
+                       return;
+               }
+
+               /* Get subtype */
+               GETCHAR(vallen, inp);
+               len--;
+               switch (vallen) {
+               case EAPSRP_CHALLENGE:
+                       tc = NULL;
+                       if (esp->es_client.ea_session != NULL) {
+                               tc = (struct t_client *)esp->es_client.
+                                   ea_session;
+                               /*
+                                * If this is a new challenge, then start
+                                * over with a new client session context.
+                                * Otherwise, just resend last response.
+                                */
+                               if (id != esp->es_client.ea_id) {
+                                       t_clientclose(tc);
+                                       esp->es_client.ea_session = NULL;
+                                       tc = NULL;
+                               }
+                       }
+                       /* No session key just yet */
+                       esp->es_client.ea_skey = NULL;
+                       if (tc == NULL) {
+                               GETCHAR(vallen, inp);
+                               len--;
+                               if (vallen >= len) {
+                                       error("EAP: badly-formed SRP Challenge"
+                                           " (name)");
+                                       /* Ignore badly-formed messages */
+                                       return;
+                               }
+                               BCOPY(inp, rhostname, vallen);
+                               rhostname[vallen] = '\0';
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+
+                               /*
+                                * In case the remote doesn't give us his name,
+                                * use configured name.
+                                */
+                               if (explicit_remote ||
+                                   (remote_name[0] != '\0' && vallen == 0)) {
+                                       strlcpy(rhostname, remote_name,
+                                           sizeof (rhostname));
+                               }
+
+                               if (esp->es_client.ea_peer != NULL)
+                                       free(esp->es_client.ea_peer);
+                               esp->es_client.ea_peer = strdup(rhostname);
+                               esp->es_client.ea_peerlen = strlen(rhostname);
+
+                               GETCHAR(vallen, inp);
+                               len--;
+                               if (vallen >= len) {
+                                       error("EAP: badly-formed SRP Challenge"
+                                           " (s)");
+                                       /* Ignore badly-formed messages */
+                                       return;
+                               }
+                               sval.data = inp;
+                               sval.len = vallen;
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+
+                               GETCHAR(vallen, inp);
+                               len--;
+                               if (vallen > len) {
+                                       error("EAP: badly-formed SRP Challenge"
+                                           " (g)");
+                                       /* Ignore badly-formed messages */
+                                       return;
+                               }
+                               /* If no generator present, then use value 2 */
+                               if (vallen == 0) {
+                                       gval.data = (u_char *)"\002";
+                                       gval.len = 1;
+                               } else {
+                                       gval.data = inp;
+                                       gval.len = vallen;
+                               }
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+
+                               /*
+                                * If no modulus present, then use well-known
+                                * value.
+                                */
+                               if (len == 0) {
+                                       Nval.data = (u_char *)wkmodulus;
+                                       Nval.len = sizeof (wkmodulus);
+                               } else {
+                                       Nval.data = inp;
+                                       Nval.len = len;
+                               }
+                               tc = t_clientopen(esp->es_client.ea_name,
+                                   &Nval, &gval, &sval);
+                               if (tc == NULL) {
+                                       eap_send_nak(esp, id, EAPT_MD5CHAP);
+                                       break;
+                               }
+                               esp->es_client.ea_session = (void *)tc;
+
+                               /* Add Challenge ID & type to verifier */
+                               vals[0] = id;
+                               vals[1] = EAPT_SRP;
+                               t_clientaddexdata(tc, vals, 2);
+                       }
+                       Ap = t_clientgenexp(tc);
+                       eap_srp_response(esp, id, EAPSRP_CKEY, Ap->data,
+                           Ap->len);
+                       break;
+
+               case EAPSRP_SKEY:
+                       tc = (struct t_client *)esp->es_client.ea_session;
+                       if (tc == NULL) {
+                               warn("EAP: peer sent Subtype 2 without 1");
+                               eap_send_nak(esp, id, EAPT_MD5CHAP);
+                               break;
+                       }
+                       if (esp->es_client.ea_skey != NULL) {
+                               /*
+                                * ID number should not change here.  Warn
+                                * if it does (but otherwise ignore).
+                                */
+                               if (id != esp->es_client.ea_id) {
+                                       warn("EAP: ID changed from %d to %d "
+                                           "in SRP Subtype 2 rexmit",
+                                           esp->es_client.ea_id, id);
+                               }
+                       } else {
+                               if (get_srp_secret(esp->es_unit,
+                                   esp->es_client.ea_name,
+                                   esp->es_client.ea_peer, secret, 0) == 0) {
+                                       /*
+                                        * Can't work with this peer because
+                                        * the secret is missing.  Just give
+                                        * up.
+                                        */
+                                       eap_send_nak(esp, id, EAPT_MD5CHAP);
+                                       break;
+                               }
+                               Bval.data = inp;
+                               Bval.len = len;
+                               t_clientpasswd(tc, secret);
+                               BZERO(secret, sizeof (secret));
+                               esp->es_client.ea_skey =
+                                   t_clientgetkey(tc, &Bval);
+                               if (esp->es_client.ea_skey == NULL) {
+                                       /* Server is rogue; stop now */
+                                       error("EAP: SRP server is rogue");
+                                       goto client_failure;
+                               }
+                       }
+                       eap_srpval_response(esp, id, SRPVAL_EBIT,
+                           t_clientresponse(tc));
+                       break;
+
+               case EAPSRP_SVALIDATOR:
+                       tc = (struct t_client *)esp->es_client.ea_session;
+                       if (tc == NULL || esp->es_client.ea_skey == NULL) {
+                               warn("EAP: peer sent Subtype 3 without 1/2");
+                               eap_send_nak(esp, id, EAPT_MD5CHAP);
+                               break;
+                       }
+                       /*
+                        * If we're already open, then this ought to be a
+                        * duplicate.  Otherwise, check that the server is
+                        * who we think it is.
+                        */
+                       if (esp->es_client.ea_state == eapOpen) {
+                               if (id != esp->es_client.ea_id) {
+                                       warn("EAP: ID changed from %d to %d "
+                                           "in SRP Subtype 3 rexmit",
+                                           esp->es_client.ea_id, id);
+                               }
+                       } else {
+                               len -= sizeof (u_int32_t) + SHA_DIGESTSIZE;
+                               if (len < 0 || t_clientverify(tc, inp +
+                                       sizeof (u_int32_t)) != 0) {
+                                       error("EAP: SRP server verification "
+                                           "failed");
+                                       goto client_failure;
+                               }
+                               GETLONG(esp->es_client.ea_keyflags, inp);
+                               /* Save pseudonym if user wants it. */
+                               if (len > 0 && esp->es_usepseudo) {
+                                       INCPTR(SHA_DIGESTSIZE, inp);
+                                       write_pseudonym(esp, inp, len, id);
+                               }
+                       }
+                       /*
+                        * We've verified our peer.  We're now mostly done,
+                        * except for waiting on the regular EAP Success
+                        * message.
+                        */
+                       eap_srp_response(esp, id, EAPSRP_ACK, NULL, 0);
+                       break;
+
+               case EAPSRP_LWRECHALLENGE:
+                       if (len < 4) {
+                               warn("EAP: malformed Lightweight rechallenge");
+                               return;
+                       }
+                       SHA1Init(&ctxt);
+                       vals[0] = id;
+                       SHA1Update(&ctxt, vals, 1);
+                       SHA1Update(&ctxt, esp->es_client.ea_skey,
+                           SESSION_KEY_LEN);
+                       SHA1Update(&ctxt, inp, len);
+                       SHA1Update(&ctxt, esp->es_client.ea_name,
+                           esp->es_client.ea_namelen);
+                       SHA1Final(dig, &ctxt);
+                       eap_srp_response(esp, id, EAPSRP_LWRECHALLENGE, dig,
+                           SHA_DIGESTSIZE);
+                       break;
+
+               default:
+                       error("EAP: unknown SRP Subtype %d", vallen);
+                       eap_send_nak(esp, id, EAPT_MD5CHAP);
+                       break;
+               }
+               break;
+#endif /* USE_SRP */
+
+       default:
+               info("EAP: unknown authentication type %d; Naking", typenum);
+               eap_send_nak(esp, id, EAPT_SRP);
+               break;
+       }
+
+       if (esp->es_client.ea_timeout > 0) {
+               UNTIMEOUT(eap_client_timeout, (void *)esp);
+               TIMEOUT(eap_client_timeout, (void *)esp,
+                   esp->es_client.ea_timeout);
+       }
+       return;
+
+#ifdef USE_SRP
+client_failure:
+       esp->es_client.ea_state = eapBadAuth;
+       if (esp->es_client.ea_timeout > 0) {
+               UNTIMEOUT(eap_client_timeout, (void *)esp);
+       }
+       esp->es_client.ea_session = NULL;
+       t_clientclose(tc);
+       auth_withpeer_fail(esp->es_unit, PPP_EAP);
+#endif /* USE_SRP */
+}
+
+/*
+ * eap_response - Receive EAP Response message (server mode).
+ */
+static void
+eap_response(esp, inp, id, len)
+eap_state *esp;
+u_char *inp;
+int id;
+int len;
+{
+       u_char typenum;
+       u_char vallen;
+       int secret_len;
+       char secret[MAXSECRETLEN];
+       char rhostname[256];
+       MD5_CTX mdContext;
+       u_char hash[MD5_SIGNATURE_SIZE];
+#ifdef USE_SRP
+       struct t_server *ts;
+       struct t_num A;
+       SHA1_CTX ctxt;
+       u_char dig[SHA_DIGESTSIZE];
+#endif /* USE_SRP */
+
+       if (esp->es_server.ea_id != id) {
+               dbglog("EAP: discarding Response %d; expected ID %d", id,
+                   esp->es_server.ea_id);
+               return;
+       }
+
+       esp->es_server.ea_responses++;
+
+       if (len <= 0) {
+               error("EAP: empty Response message discarded");
+               return;
+       }
+
+       GETCHAR(typenum, inp);
+       len--;
+
+       switch (typenum) {
+       case EAPT_IDENTITY:
+               if (esp->es_server.ea_state != eapIdentify) {
+                       dbglog("EAP discarding unwanted Identify \"%.q\"", len,
+                           inp);
+                       break;
+               }
+               info("EAP: unauthenticated peer name \"%.*q\"", len, inp);
+               if (esp->es_server.ea_peer != NULL &&
+                   esp->es_server.ea_peer != remote_name)
+                       free(esp->es_server.ea_peer);
+               esp->es_server.ea_peer = malloc(len + 1);
+               if (esp->es_server.ea_peer == NULL) {
+                       esp->es_server.ea_peerlen = 0;
+                       eap_figure_next_state(esp, 1);
+                       break;
+               }
+               BCOPY(inp, esp->es_server.ea_peer, len);
+               esp->es_server.ea_peer[len] = '\0';
+               esp->es_server.ea_peerlen = len;
+               eap_figure_next_state(esp, 0);
+               break;
+
+       case EAPT_NOTIFICATION:
+               dbglog("EAP unexpected Notification; response discarded");
+               break;
+
+       case EAPT_NAK:
+               if (len < 1) {
+                       info("EAP: Nak Response with no suggested protocol");
+                       eap_figure_next_state(esp, 1);
+                       break;
+               }
+
+               GETCHAR(vallen, inp);
+               len--;
+
+               if (!explicit_remote && esp->es_server.ea_state == eapIdentify){
+                       /* Peer cannot Nak Identify Request */
+                       eap_figure_next_state(esp, 1);
+                       break;
+               }
+
+               switch (vallen) {
+               case EAPT_SRP:
+                       /* Run through SRP validator selection again. */
+                       esp->es_server.ea_state = eapIdentify;
+                       eap_figure_next_state(esp, 0);
+                       break;
+
+               case EAPT_MD5CHAP:
+                       esp->es_server.ea_state = eapMD5Chall;
+                       break;
+
+               default:
+                       dbglog("EAP: peer requesting unknown Type %d", vallen);
+                       switch (esp->es_server.ea_state) {
+                       case eapSRP1:
+                       case eapSRP2:
+                       case eapSRP3:
+                               esp->es_server.ea_state = eapMD5Chall;
+                               break;
+                       case eapMD5Chall:
+                       case eapSRP4:
+                               esp->es_server.ea_state = eapIdentify;
+                               eap_figure_next_state(esp, 0);
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               }
+               break;
+
+       case EAPT_MD5CHAP:
+               if (esp->es_server.ea_state != eapMD5Chall) {
+                       error("EAP: unexpected MD5-Response");
+                       eap_figure_next_state(esp, 1);
+                       break;
+               }
+               if (len < 1) {
+                       error("EAP: received MD5-Response with no data");
+                       eap_figure_next_state(esp, 1);
+                       break;
+               }
+               GETCHAR(vallen, inp);
+               len--;
+               if (vallen != 16 || vallen > len) {
+                       error("EAP: MD5-Response with bad length %d", vallen);
+                       eap_figure_next_state(esp, 1);
+                       break;
+               }
+
+               /* Not so likely to happen. */
+               if (vallen >= len + sizeof (rhostname)) {
+                       dbglog("EAP: trimming really long peer name down");
+                       BCOPY(inp + vallen, rhostname, sizeof (rhostname) - 1);
+                       rhostname[sizeof (rhostname) - 1] = '\0';
+               } else {
+                       BCOPY(inp + vallen, rhostname, len - vallen);
+                       rhostname[len - vallen] = '\0';
+               }
+
+               /* In case the remote doesn't give us his name. */
+               if (explicit_remote ||
+                   (remote_name[0] != '\0' && vallen == len))
+                       strlcpy(rhostname, remote_name, sizeof (rhostname));
+
+               /*
+                * Get the secret for authenticating the specified
+                * host.
+                */
+               if (!get_secret(esp->es_unit, rhostname,
+                   esp->es_server.ea_name, secret, &secret_len, 1)) {
+                       dbglog("EAP: no MD5 secret for auth of %q", rhostname);
+                       eap_send_failure(esp);
+                       break;
+               }
+               MD5Init(&mdContext);
+               MD5Update(&mdContext, &esp->es_server.ea_id, 1);
+               MD5Update(&mdContext, secret, secret_len);
+               BZERO(secret, sizeof (secret));
+               MD5Update(&mdContext, esp->es_challenge, esp->es_challen);
+               MD5Final(hash, &mdContext);
+               if (BCMP(hash, inp, MD5_SIGNATURE_SIZE) != 0) {
+                       eap_send_failure(esp);
+                       break;
+               }
+               esp->es_server.ea_type = EAPT_MD5CHAP;
+               eap_send_success(esp);
+               eap_figure_next_state(esp, 0);
+               if (esp->es_rechallenge != 0)
+                       TIMEOUT(eap_rechallenge, esp, esp->es_rechallenge);
+               break;
+
+#ifdef USE_SRP
+       case EAPT_SRP:
+               if (len < 1) {
+                       error("EAP: empty SRP Response");
+                       eap_figure_next_state(esp, 1);
+                       break;
+               }
+               GETCHAR(typenum, inp);
+               len--;
+               switch (typenum) {
+               case EAPSRP_CKEY:
+                       if (esp->es_server.ea_state != eapSRP1) {
+                               error("EAP: unexpected SRP Subtype 1 Response");
+                               eap_figure_next_state(esp, 1);
+                               break;
+                       }
+                       A.data = inp;
+                       A.len = len;
+                       ts = (struct t_server *)esp->es_server.ea_session;
+                       assert(ts != NULL);
+                       esp->es_server.ea_skey = t_servergetkey(ts, &A);
+                       if (esp->es_server.ea_skey == NULL) {
+                               /* Client's A value is bogus; terminate now */
+                               error("EAP: bogus A value from client");
+                               eap_send_failure(esp);
+                       } else {
+                               eap_figure_next_state(esp, 0);
+                       }
+                       break;
+
+               case EAPSRP_CVALIDATOR:
+                       if (esp->es_server.ea_state != eapSRP2) {
+                               error("EAP: unexpected SRP Subtype 2 Response");
+                               eap_figure_next_state(esp, 1);
+                               break;
+                       }
+                       if (len < sizeof (u_int32_t) + SHA_DIGESTSIZE) {
+                               error("EAP: M1 length %d < %d", len,
+                                   sizeof (u_int32_t) + SHA_DIGESTSIZE);
+                               eap_figure_next_state(esp, 1);
+                               break;
+                       }
+                       GETLONG(esp->es_server.ea_keyflags, inp);
+                       ts = (struct t_server *)esp->es_server.ea_session;
+                       assert(ts != NULL);
+                       if (t_serververify(ts, inp)) {
+                               info("EAP: unable to validate client identity");
+                               eap_send_failure(esp);
+                               break;
+                       }
+                       eap_figure_next_state(esp, 0);
+                       break;
+
+               case EAPSRP_ACK:
+                       if (esp->es_server.ea_state != eapSRP3) {
+                               error("EAP: unexpected SRP Subtype 3 Response");
+                               eap_send_failure(esp);
+                               break;
+                       }
+                       esp->es_server.ea_type = EAPT_SRP;
+                       eap_send_success(esp);
+                       eap_figure_next_state(esp, 0);
+                       if (esp->es_rechallenge != 0)
+                               TIMEOUT(eap_rechallenge, esp,
+                                   esp->es_rechallenge);
+                       if (esp->es_lwrechallenge != 0)
+                               TIMEOUT(srp_lwrechallenge, esp,
+                                   esp->es_lwrechallenge);
+                       break;
+
+               case EAPSRP_LWRECHALLENGE:
+                       if (esp->es_server.ea_state != eapSRP4) {
+                               info("EAP: unexpected SRP Subtype 4 Response");
+                               return;
+                       }
+                       if (len != SHA_DIGESTSIZE) {
+                               error("EAP: bad Lightweight rechallenge "
+                                   "response");
+                               return;
+                       }
+                       SHA1Init(&ctxt);
+                       vallen = id;
+                       SHA1Update(&ctxt, &vallen, 1);
+                       SHA1Update(&ctxt, esp->es_server.ea_skey,
+                           SESSION_KEY_LEN);
+                       SHA1Update(&ctxt, esp->es_challenge, esp->es_challen);
+                       SHA1Update(&ctxt, esp->es_server.ea_peer,
+                           esp->es_server.ea_peerlen);
+                       SHA1Final(dig, &ctxt);
+                       if (BCMP(dig, inp, SHA_DIGESTSIZE) != 0) {
+                               error("EAP: failed Lightweight rechallenge");
+                               eap_send_failure(esp);
+                               break;
+                       }
+                       esp->es_server.ea_state = eapOpen;
+                       if (esp->es_lwrechallenge != 0)
+                               TIMEOUT(srp_lwrechallenge, esp,
+                                   esp->es_lwrechallenge);
+                       break;
+               }
+               break;
+#endif /* USE_SRP */
+
+       default:
+               /* This can't happen. */
+               error("EAP: unknown Response type %d; ignored", typenum);
+               return;
+       }
+
+       if (esp->es_server.ea_timeout > 0) {
+               UNTIMEOUT(eap_server_timeout, (void *)esp);
+       }
+
+       if (esp->es_server.ea_state != eapBadAuth &&
+           esp->es_server.ea_state != eapOpen) {
+               esp->es_server.ea_id++;
+               eap_send_request(esp);
+       }
+}
+
+/*
+ * eap_success - Receive EAP Success message (client mode).
+ */
+static void
+eap_success(esp, inp, id, len)
+eap_state *esp;
+u_char *inp;
+int id;
+int len;
+{
+       if (esp->es_client.ea_state != eapOpen && !eap_client_active(esp)) {
+               dbglog("EAP unexpected success message in state %s (%d)",
+                   eap_state_name(esp->es_client.ea_state),
+                   esp->es_client.ea_state);
+               return;
+       }
+
+       if (esp->es_client.ea_timeout > 0) {
+               UNTIMEOUT(eap_client_timeout, (void *)esp);
+       }
+
+       if (len > 0) {
+               /* This is odd.  The spec doesn't allow for this. */
+               PRINTMSG(inp, len);
+       }
+
+       esp->es_client.ea_state = eapOpen;
+       auth_withpeer_success(esp->es_unit, PPP_EAP, 0);
+}
+
+/*
+ * eap_failure - Receive EAP Failure message (client mode).
+ */
+static void
+eap_failure(esp, inp, id, len)
+eap_state *esp;
+u_char *inp;
+int id;
+int len;
+{
+       if (!eap_client_active(esp)) {
+               dbglog("EAP unexpected failure message in state %s (%d)",
+                   eap_state_name(esp->es_client.ea_state),
+                   esp->es_client.ea_state);
+       }
+
+       if (esp->es_client.ea_timeout > 0) {
+               UNTIMEOUT(eap_client_timeout, (void *)esp);
+       }
+
+       if (len > 0) {
+               /* This is odd.  The spec doesn't allow for this. */
+               PRINTMSG(inp, len);
+       }
+
+       esp->es_client.ea_state = eapBadAuth;
+
+       error("EAP: peer reports authentication failure");
+       auth_withpeer_fail(esp->es_unit, PPP_EAP);
+}
+
+/*
+ * eap_input - Handle received EAP message.
+ */
+static void
+eap_input(unit, inp, inlen)
+int unit;
+u_char *inp;
+int inlen;
+{
+       eap_state *esp = &eap_states[unit];
+       u_char code, id;
+       int len;
+
+       /*
+        * Parse header (code, id and length).  If packet too short,
+        * drop it.
+        */
+       if (inlen < EAP_HEADERLEN) {
+               error("EAP: packet too short: %d < %d", inlen, EAP_HEADERLEN);
+               return;
+       }
+       GETCHAR(code, inp);
+       GETCHAR(id, inp);
+       GETSHORT(len, inp);
+       if (len < EAP_HEADERLEN || len > inlen) {
+               error("EAP: packet has illegal length field %d (%d..%d)", len,
+                   EAP_HEADERLEN, inlen);
+               return;
+       }
+       len -= EAP_HEADERLEN;
+
+       /* Dispatch based on message code */
+       switch (code) {
+       case EAP_REQUEST:
+               eap_request(esp, inp, id, len);
+               break;
+
+       case EAP_RESPONSE:
+               eap_response(esp, inp, id, len);
+               break;
+
+       case EAP_SUCCESS:
+               eap_success(esp, inp, id, len);
+               break;
+
+       case EAP_FAILURE:
+               eap_failure(esp, inp, id, len);
+               break;
+
+       default:                                /* XXX Need code reject */
+               /* Note: it's not legal to send EAP Nak here. */
+               warn("EAP: unknown code %d received", code);
+               break;
+       }
+}
+
+/*
+ * eap_printpkt - print the contents of an EAP packet.
+ */
+static char *eap_codenames[] = {
+       "Request", "Response", "Success", "Failure"
+};
+
+static char *eap_typenames[] = {
+       "Identity", "Notification", "Nak", "MD5-Challenge",
+       "OTP", "Generic-Token", NULL, NULL,
+       "RSA", "DSS", "KEA", "KEA-Validate",
+       "TLS", "Defender", "Windows 2000", "Arcot",
+       "Cisco", "Nokia", "SRP"
+};
+
+static int
+eap_printpkt(inp, inlen, printer, arg)
+u_char *inp;
+int inlen;
+void (*printer) __P((void *, char *, ...));
+void *arg;
+{
+       int code, id, len, rtype, vallen;
+       u_char *pstart;
+       u_int32_t uval;
+
+       if (inlen < EAP_HEADERLEN)
+               return (0);
+       pstart = inp;
+       GETCHAR(code, inp);
+       GETCHAR(id, inp);
+       GETSHORT(len, inp);
+       if (len < EAP_HEADERLEN || len > inlen)
+               return (0);
+
+       if (code >= 1 && code <= sizeof(eap_codenames) / sizeof(char *))
+               printer(arg, " %s", eap_codenames[code-1]);
+       else
+               printer(arg, " code=0x%x", code);
+       printer(arg, " id=0x%x", id);
+       len -= EAP_HEADERLEN;
+       switch (code) {
+       case EAP_REQUEST:
+               if (len < 1) {
+                       printer(arg, " <missing type>");
+                       break;
+               }
+               GETCHAR(rtype, inp);
+               len--;
+               if (rtype >= 1 &&
+                   rtype <= sizeof (eap_typenames) / sizeof (char *))
+                       printer(arg, " %s", eap_typenames[rtype-1]);
+               else
+                       printer(arg, " type=0x%x", rtype);
+               switch (rtype) {
+               case EAPT_IDENTITY:
+               case EAPT_NOTIFICATION:
+                       if (len > 0) {
+                               printer(arg, " <Message ");
+                               print_string((char *)inp, len, printer, arg);
+                               printer(arg, ">");
+                               INCPTR(len, inp);
+                               len = 0;
+                       } else {
+                               printer(arg, " <No message>");
+                       }
+                       break;
+
+               case EAPT_MD5CHAP:
+                       if (len <= 0)
+                               break;
+                       GETCHAR(vallen, inp);
+                       len--;
+                       if (vallen > len)
+                               goto truncated;
+                       printer(arg, " <Value%.*B>", vallen, inp);
+                       INCPTR(vallen, inp);
+                       len -= vallen;
+                       if (len > 0) {
+                               printer(arg, " <Name ");
+                               print_string((char *)inp, len, printer, arg);
+                               printer(arg, ">");
+                               INCPTR(len, inp);
+                               len = 0;
+                       } else {
+                               printer(arg, " <No name>");
+                       }
+                       break;
+
+               case EAPT_SRP:
+                       if (len < 3)
+                               goto truncated;
+                       GETCHAR(vallen, inp);
+                       len--;
+                       printer(arg, "-%d", vallen);
+                       switch (vallen) {
+                       case EAPSRP_CHALLENGE:
+                               GETCHAR(vallen, inp);
+                               len--;
+                               if (vallen >= len)
+                                       goto truncated;
+                               if (vallen > 0) {
+                                       printer(arg, " <Name ");
+                                       print_string((char *)inp, vallen, printer,
+                                           arg);
+                                       printer(arg, ">");
+                               } else {
+                                       printer(arg, " <No name>");
+                               }
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+                               GETCHAR(vallen, inp);
+                               len--;
+                               if (vallen >= len)
+                                       goto truncated;
+                               printer(arg, " <s%.*B>", vallen, inp);
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+                               GETCHAR(vallen, inp);
+                               len--;
+                               if (vallen > len)
+                                       goto truncated;
+                               if (vallen == 0) {
+                                       printer(arg, " <Default g=2>");
+                               } else {
+                                       printer(arg, " <g%.*B>", vallen, inp);
+                               }
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+                               if (len == 0) {
+                                       printer(arg, " <Default N>");
+                               } else {
+                                       printer(arg, " <N%.*B>", len, inp);
+                                       INCPTR(len, inp);
+                                       len = 0;
+                               }
+                               break;
+
+                       case EAPSRP_SKEY:
+                               printer(arg, " <B%.*B>", len, inp);
+                               INCPTR(len, inp);
+                               len = 0;
+                               break;
+
+                       case EAPSRP_SVALIDATOR:
+                               if (len < sizeof (u_int32_t))
+                                       break;
+                               GETLONG(uval, inp);
+                               len -= sizeof (u_int32_t);
+                               if (uval & SRPVAL_EBIT) {
+                                       printer(arg, " E");
+                                       uval &= ~SRPVAL_EBIT;
+                               }
+                               if (uval != 0) {
+                                       printer(arg, " f<%X>", uval);
+                               }
+                               if ((vallen = len) > SHA_DIGESTSIZE)
+                                       vallen = SHA_DIGESTSIZE;
+                               printer(arg, " <M2%.*B%s>", len, inp,
+                                   len < SHA_DIGESTSIZE ? "?" : "");
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+                               if (len > 0) {
+                                       printer(arg, " <PN%.*B>", len, inp);
+                                       INCPTR(len, inp);
+                                       len = 0;
+                               }
+                               break;
+
+                       case EAPSRP_LWRECHALLENGE:
+                               printer(arg, " <Challenge%.*B>", len, inp);
+                               INCPTR(len, inp);
+                               len = 0;
+                               break;
+                       }
+                       break;
+               }
+               break;
+
+       case EAP_RESPONSE:
+               if (len < 1)
+                       break;
+               GETCHAR(rtype, inp);
+               len--;
+               if (rtype >= 1 &&
+                   rtype <= sizeof (eap_typenames) / sizeof (char *))
+                       printer(arg, " %s", eap_typenames[rtype-1]);
+               else
+                       printer(arg, " type=0x%x", rtype);
+               switch (rtype) {
+               case EAPT_IDENTITY:
+                       if (len > 0) {
+                               printer(arg, " <Name ");
+                               print_string((char *)inp, len, printer, arg);
+                               printer(arg, ">");
+                               INCPTR(len, inp);
+                               len = 0;
+                       }
+                       break;
+
+               case EAPT_NAK:
+                       if (len <= 0) {
+                               printer(arg, " <missing hint>");
+                               break;
+                       }
+                       GETCHAR(rtype, inp);
+                       len--;
+                       printer(arg, " <Suggested-type %02X", rtype);
+                       if (rtype >= 1 &&
+                           rtype < sizeof (eap_typenames) / sizeof (char *))
+                               printer(arg, " (%s)", eap_typenames[rtype-1]);
+                       printer(arg, ">");
+                       break;
+
+               case EAPT_MD5CHAP:
+                       if (len <= 0) {
+                               printer(arg, " <missing length>");
+                               break;
+                       }
+                       GETCHAR(vallen, inp);
+                       len--;
+                       if (vallen > len)
+                               goto truncated;
+                       printer(arg, " <Value%.*B>", vallen, inp);
+                       INCPTR(vallen, inp);
+                       len -= vallen;
+                       if (len > 0) {
+                               printer(arg, " <Name ");
+                               print_string((char *)inp, len, printer, arg);
+                               printer(arg, ">");
+                               INCPTR(len, inp);
+                               len = 0;
+                       } else {
+                               printer(arg, " <No name>");
+                       }
+                       break;
+
+               case EAPT_SRP:
+                       if (len < 1)
+                               goto truncated;
+                       GETCHAR(vallen, inp);
+                       len--;
+                       printer(arg, "-%d", vallen);
+                       switch (vallen) {
+                       case EAPSRP_CKEY:
+                               printer(arg, " <A%.*B>", len, inp);
+                               INCPTR(len, inp);
+                               len = 0;
+                               break;
+
+                       case EAPSRP_CVALIDATOR:
+                               if (len < sizeof (u_int32_t))
+                                       break;
+                               GETLONG(uval, inp);
+                               len -= sizeof (u_int32_t);
+                               if (uval & SRPVAL_EBIT) {
+                                       printer(arg, " E");
+                                       uval &= ~SRPVAL_EBIT;
+                               }
+                               if (uval != 0) {
+                                       printer(arg, " f<%X>", uval);
+                               }
+                               printer(arg, " <M1%.*B%s>", len, inp,
+                                   len == SHA_DIGESTSIZE ? "" : "?");
+                               INCPTR(len, inp);
+                               len = 0;
+                               break;
+
+                       case EAPSRP_ACK:
+                               break;
+
+                       case EAPSRP_LWRECHALLENGE:
+                               printer(arg, " <Response%.*B%s>", len, inp,
+                                   len == SHA_DIGESTSIZE ? "" : "?");
+                               if ((vallen = len) > SHA_DIGESTSIZE)
+                                       vallen = SHA_DIGESTSIZE;
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+                               break;
+                       }
+                       break;
+               }
+               break;
+
+       case EAP_SUCCESS:       /* No payload expected for these! */
+       case EAP_FAILURE:
+               break;
+
+       truncated:
+               printer(arg, " <truncated>");
+               break;
+       }
+
+       if (len > 8)
+               printer(arg, "%8B...", inp);
+       else if (len > 0)
+               printer(arg, "%.*B", len, inp);
+       INCPTR(len, inp);
+
+       return (inp - pstart);
+}
diff --git a/pppd/eap.h b/pppd/eap.h
new file mode 100644 (file)
index 0000000..cf5adac
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * eap.h - Extensible Authentication Protocol for PPP (RFC 2284)
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Non-exclusive rights to redistribute, modify, translate, and use
+ * this software in source and binary forms, in whole or in part, is
+ * hereby granted, provided that the above copyright notice is
+ * duplicated in any source form, and that neither the name of the
+ * copyright holder nor the author is used to endorse or promote
+ * products derived from this software.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Original version by James Carlson
+ *
+ * $Id: eap.h,v 1.1 2002/11/02 19:48:12 carlsonj Exp $
+ */
+
+#ifndef PPP_EAP_H
+#define        PPP_EAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define        EAP_HEADERLEN   4
+
+
+/* EAP message codes. */
+#define        EAP_REQUEST     1
+#define        EAP_RESPONSE    2
+#define        EAP_SUCCESS     3
+#define        EAP_FAILURE     4
+
+/* EAP types */
+#define        EAPT_IDENTITY           1
+#define        EAPT_NOTIFICATION       2
+#define        EAPT_NAK                3       /* (response only) */
+#define        EAPT_MD5CHAP            4
+#define        EAPT_OTP                5       /* One-Time Password; RFC 1938 */
+#define        EAPT_TOKEN              6       /* Generic Token Card */
+/* 7 and 8 are unassigned. */
+#define        EAPT_RSA                9       /* RSA Public Key Authentication */
+#define        EAPT_DSS                10      /* DSS Unilateral */
+#define        EAPT_KEA                11      /* KEA */
+#define        EAPT_KEA_VALIDATE       12      /* KEA-VALIDATE */
+#define        EAPT_TLS                13      /* EAP-TLS */
+#define        EAPT_DEFENDER           14      /* Defender Token (AXENT) */
+#define        EAPT_W2K                15      /* Windows 2000 EAP */
+#define        EAPT_ARCOT              16      /* Arcot Systems */
+#define        EAPT_CISCOWIRELESS      17      /* Cisco Wireless */
+#define        EAPT_NOKIACARD          18      /* Nokia IP smart card */
+#define        EAPT_SRP                19      /* Secure Remote Password */
+/* 20 is deprecated */
+
+/* EAP SRP-SHA1 Subtypes */
+#define        EAPSRP_CHALLENGE        1       /* Request 1 - Challenge */
+#define        EAPSRP_CKEY             1       /* Response 1 - Client Key */
+#define        EAPSRP_SKEY             2       /* Request 2 - Server Key */
+#define        EAPSRP_CVALIDATOR       2       /* Response 2 - Client Validator */
+#define        EAPSRP_SVALIDATOR       3       /* Request 3 - Server Validator */
+#define        EAPSRP_ACK              3       /* Response 3 - final ack */
+#define        EAPSRP_LWRECHALLENGE    4       /* Req/resp 4 - Lightweight rechal */
+
+#define        SRPVAL_EBIT     0x00000001      /* Use shared key for ECP */
+
+#define        SRP_PSEUDO_ID   "pseudo_"
+#define        SRP_PSEUDO_LEN  7
+
+enum eap_state_code {
+       eapInitial = 0, /* No EAP authentication yet requested */
+       eapPending,     /* Waiting for LCP (no timer) */
+       eapClosed,      /* Authentication not in use */
+       eapListen,      /* Client ready (and timer running) */
+       eapIdentify,    /* EAP Identify sent */
+       eapSRP1,        /* Sent EAP SRP-SHA1 Subtype 1 */
+       eapSRP2,        /* Sent EAP SRP-SHA1 Subtype 2 */
+       eapSRP3,        /* Sent EAP SRP-SHA1 Subtype 3 */
+       eapMD5Chall,    /* Sent MD5-Challenge */
+       eapOpen,        /* Completed authentication */
+       eapSRP4,        /* Sent EAP SRP-SHA1 Subtype 4 */
+       eapBadAuth      /* Failed authentication */
+};
+
+#define        EAP_STATES      \
+       "Initial", "Pending", "Closed", "Listen", "Identify", \
+       "SRP1", "SRP2", "SRP3", "MD5Chall", "Open", "SRP4", "BadAuth"
+
+#define        eap_client_active(esp)  ((esp)->es_client.ea_state == eapListen)
+#define        eap_server_active(esp)  \
+       ((esp)->es_server.ea_state >= eapIdentify && \
+        (esp)->es_server.ea_state <= eapMD5Chall)
+
+struct eap_auth {
+       char *ea_name;          /* Our name */
+       char *ea_peer;          /* Peer's name */
+       void *ea_session;       /* Authentication library linkage */
+       u_char *ea_skey;        /* Shared encryption key */
+       int ea_timeout;         /* Time to wait (for retransmit/fail) */
+       int ea_maxrequests;     /* Max Requests allowed */
+       u_short ea_namelen;     /* Length of our name */
+       u_short ea_peerlen;     /* Length of peer's name */
+       enum eap_state_code ea_state;
+       u_char ea_id;           /* Current id */
+       u_char ea_requests;     /* Number of Requests sent/received */
+       u_char ea_responses;    /* Number of Responses */
+       u_char ea_type;         /* One of EAPT_* */
+       u_int32_t ea_keyflags;  /* SRP shared key usage flags */
+};
+
+/*
+ * Complete EAP state for one PPP session.
+ */
+typedef struct eap_state {
+       int es_unit;                    /* Interface unit number */
+       struct eap_auth es_client;      /* Client (authenticatee) data */
+       struct eap_auth es_server;      /* Server (authenticator) data */
+       int es_savedtime;               /* Saved timeout */
+       int es_rechallenge;             /* EAP rechallenge interval */
+       int es_lwrechallenge;           /* SRP lightweight rechallenge inter */
+       bool es_usepseudo;              /* Use SRP Pseudonym if offered one */
+       int es_usedpseudo;              /* Set if we already sent PN */
+       int es_challen;                 /* Length of challenge string */
+       u_char es_challenge[MAX_CHALLENGE_LENGTH];
+} eap_state;
+
+/*
+ * Timeouts.
+ */
+#define        EAP_DEFTIMEOUT          3       /* Timeout (seconds) for rexmit */
+#define        EAP_DEFTRANSMITS        10      /* max # times to transmit */
+#define        EAP_DEFREQTIME          20      /* Time to wait for peer request */
+#define        EAP_DEFALLOWREQ         20      /* max # times to accept requests */
+
+extern eap_state eap_states[];
+
+void eap_authwithpeer __P((int unit, char *localname));
+void eap_authpeer __P((int unit, char *localname));
+
+extern struct protent eap_protent;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPP_EAP_H */
+
index 1923c6d4cc0cc9b467b76b7642c4804e424c90b7..5d1500e63fe000dff9e176485a80ea8136b7349a 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: lcp.c,v 1.62 2002/09/24 11:35:22 fcusack Exp $"
+#define RCSID  "$Id: lcp.c,v 1.63 2002/11/02 19:48:12 carlsonj Exp $"
 
 /*
  * TODO:
@@ -338,6 +338,7 @@ lcp_init(unit)
     ao->neg_chap = 1;
     ao->chap_mdtype = MDTYPE_ALL;
     ao->neg_upap = 1;
+    ao->neg_eap = 1;
     ao->neg_magicnumber = 1;
     ao->neg_pcompression = 1;
     ao->neg_accompression = 1;
@@ -635,13 +636,15 @@ lcp_cilen(f)
 #define LENCILQR(neg)  ((neg) ? CILEN_LQR: 0)
 #define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0)
     /*
-     * NB: we only ask for one of CHAP and UPAP, even if we will
-     * accept either.
+     * NB: we only ask for one of CHAP, UPAP, or EAP, even if we will
+     * accept more than one.  We prefer EAP first, then CHAP, then
+     * PAP.
      */
     return (LENCISHORT(go->neg_mru && go->mru != DEFMRU) +
            LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) +
-           LENCICHAP(go->neg_chap) +
-           LENCISHORT(!go->neg_chap && go->neg_upap) +
+           LENCISHORT(go->neg_eap) +
+           LENCICHAP(!go->neg_eap && go->neg_chap) +
+           LENCISHORT(!go->neg_eap && !go->neg_chap && go->neg_upap) +
            LENCILQR(go->neg_lqr) +
            LENCICBCP(go->neg_cbcp) +
            LENCILONG(go->neg_magicnumber) +
@@ -715,8 +718,10 @@ lcp_addci(f, ucp, lenp)
     ADDCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru);
     ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
              go->asyncmap);
-    ADDCICHAP(CI_AUTHTYPE, go->neg_chap, go->chap_mdtype);
-    ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+    ADDCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP);
+    ADDCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype);
+    ADDCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap,
+              PPP_PAP);
     ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
     ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
     ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
@@ -862,8 +867,10 @@ lcp_ackci(f, p, len)
     ACKCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru);
     ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
              go->asyncmap);
-    ACKCICHAP(CI_AUTHTYPE, go->neg_chap, go->chap_mdtype);
-    ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+    ACKCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP);
+    ACKCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype);
+    ACKCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap,
+              PPP_PAP);
     ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
     ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
     ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
@@ -1026,28 +1033,40 @@ lcp_nakci(f, p, len)
      * they are proposing a different protocol, or a different
      * hash algorithm for CHAP.
      */
-    if ((go->neg_chap || go->neg_upap)
+    if ((go->neg_chap || go->neg_upap || go->neg_eap)
        && len >= CILEN_SHORT
        && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) {
        cilen = p[1];
        len -= cilen;
        no.neg_chap = go->neg_chap;
        no.neg_upap = go->neg_upap;
+       no.neg_eap = go->neg_eap;
        INCPTR(2, p);
        GETSHORT(cishort, p);
        if (cishort == PPP_PAP && cilen == CILEN_SHORT) {
+           /* If we were asking for EAP, then we need to stop that. */
+           if (go->neg_eap)
+               try.neg_eap = 0;
+
+           /* If we were asking for CHAP, then we need to stop that. */
+           else if (go->neg_chap)
+               try.neg_chap = 0;
            /*
-            * If we were asking for CHAP, they obviously don't want to do it.
-            * If we weren't asking for CHAP, then we were asking for PAP,
-            * in which case this Nak is bad.
+            * If we weren't asking for CHAP or EAP, then we were asking for
+            * PAP, in which case this Nak is bad.
             */
-           if (!go->neg_chap)
+           else
                goto bad;
-           try.neg_chap = 0;
 
        } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) {
            GETCHAR(cichar, p);
-           if (go->neg_chap) {
+           /* Stop asking for EAP, if we were. */
+           if (go->neg_eap) {
+               try.neg_eap = 0;
+               /* Try to set up to use their suggestion, if possible */
+               if (CHAP_CANDIGEST(go->chap_mdtype, cichar))
+                   go->chap_mdtype = CHAP_MDTYPE_D(cichar);
+           } else if (go->neg_chap) {
                /*
                 * We were asking for our preferred algorithm, they must
                 * want something different.
@@ -1077,11 +1096,21 @@ lcp_nakci(f, p, len)
            }
 
        } else {
+
+           /*
+            * If we were asking for EAP, and they're Conf-Naking EAP,
+            * well, that's just strange.  Nobody should do that.
+            */
+           if (cishort == PPP_EAP && cilen == CILEN_SHORT && go->neg_eap)
+               dbglog("Unexpected Conf-Nak for EAP");
+
            /*
             * We don't recognize what they're suggesting.
             * Stop asking for what we were asking for.
             */
-           if (go->neg_chap)
+           if (go->neg_eap)
+               try.neg_eap = 0;
+           else if (go->neg_chap)
                try.neg_chap = 0;
            else
                try.neg_upap = 0;
@@ -1187,7 +1216,8 @@ lcp_nakci(f, p, len)
                goto bad;
            break;
        case CI_AUTHTYPE:
-           if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap)
+           if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap ||
+               go->neg_eap || no.neg_eap)
                goto bad;
            break;
        case CI_MAGICNUMBER:
@@ -1313,7 +1343,7 @@ lcp_rejci(f, p, len)
        if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \
            goto bad; \
        try.neg = 0; \
-       try.neg_upap = 0; \
+       try.neg_eap = try.neg_upap = 0; \
     }
 #define REJCILONG(opt, neg, val) \
     if (go->neg && \
@@ -1376,9 +1406,12 @@ lcp_rejci(f, p, len)
 
     REJCISHORT(CI_MRU, neg_mru, go->mru);
     REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap);
-    REJCICHAP(CI_AUTHTYPE, neg_chap, go->chap_mdtype);
-    if (!go->neg_chap) {
-       REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP);
+    REJCISHORT(CI_AUTHTYPE, neg_eap, PPP_EAP);
+    if (!go->neg_eap) {
+       REJCICHAP(CI_AUTHTYPE, neg_chap, go->chap_mdtype);
+       if (!go->neg_chap) {
+           REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP);
+       }
     }
     REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period);
     REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT);
@@ -1515,56 +1548,68 @@ lcp_reqci(f, inp, lenp, reject_if_disagree)
 
        case CI_AUTHTYPE:
            if (cilen < CILEN_SHORT ||
-               !(ao->neg_upap || ao->neg_chap)) {
+               !(ao->neg_upap || ao->neg_chap || ao->neg_eap)) {
                /*
                 * Reject the option if we're not willing to authenticate.
                 */
+               dbglog("No auth is possible");
                orc = CONFREJ;
                break;
            }
            GETSHORT(cishort, p);
 
            /*
-            * Authtype must be PAP or CHAP.
+            * Authtype must be PAP, CHAP, or EAP.
             *
-            * Note: if both ao->neg_upap and ao->neg_chap are set,
-            * and the peer sends a Configure-Request with two
-            * authenticate-protocol requests, one for CHAP and one
-            * for UPAP, then we will reject the second request.
-            * Whether we end up doing CHAP or UPAP depends then on
+            * Note: if more than one of ao->neg_upap, ao->neg_chap, and
+            * ao->neg_eap are set, and the peer sends a Configure-Request
+            * with two or more authenticate-protocol requests, then we will
+            * reject the second request.
+            * Whether we end up doing CHAP, UPAP, or EAP depends then on
             * the ordering of the CIs in the peer's Configure-Request.
              */
 
            if (cishort == PPP_PAP) {
-               if (ho->neg_chap ||     /* we've already accepted CHAP */
+               /* we've already accepted CHAP or EAP */
+               if (ho->neg_chap || ho->neg_eap ||
                    cilen != CILEN_SHORT) {
                    LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE PAP, rejecting..."));
                    orc = CONFREJ;
                    break;
                }
                if (!ao->neg_upap) {    /* we don't want to do PAP */
-                   orc = CONFNAK;      /* NAK it and suggest CHAP */
-                   PUTCHAR(CI_AUTHTYPE, nakp);
-                   PUTCHAR(CILEN_CHAP, nakp);
-                   PUTSHORT(PPP_CHAP, nakp);
-                   PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp);
+                   orc = CONFNAK;      /* NAK it and suggest CHAP or EAP */
+                   if (ao->neg_eap) {
+                       PUTCHAR(CILEN_SHORT, nakp);
+                       PUTSHORT(PPP_EAP, nakp);
+                   } else {
+                       PUTCHAR(CI_AUTHTYPE, nakp);
+                       PUTCHAR(CILEN_CHAP, nakp);
+                       PUTSHORT(PPP_CHAP, nakp);
+                       PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp);
+                   }
                    break;
                }
                ho->neg_upap = 1;
                break;
            }
            if (cishort == PPP_CHAP) {
-               if (ho->neg_upap ||     /* we've already accepted PAP */
+               /* we've already accepted PAP or EAP */
+               if (ho->neg_upap || ho->neg_eap ||
                    cilen != CILEN_CHAP) {
                    LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE CHAP, rejecting..."));
                    orc = CONFREJ;
                    break;
                }
                if (!ao->neg_chap) {    /* we don't want to do CHAP */
-                   orc = CONFNAK;      /* NAK it and suggest PAP */
+                   orc = CONFNAK;      /* NAK it and suggest EAP or PAP */
                    PUTCHAR(CI_AUTHTYPE, nakp);
                    PUTCHAR(CILEN_SHORT, nakp);
-                   PUTSHORT(PPP_PAP, nakp);
+                   if (ao->neg_eap) {
+                       PUTSHORT(PPP_EAP, nakp);
+                   } else {
+                       PUTSHORT(PPP_PAP, nakp);
+                   }
                    break;
                }
                GETCHAR(cichar, p);     /* get digest type */
@@ -1584,15 +1629,42 @@ lcp_reqci(f, inp, lenp, reject_if_disagree)
                ho->neg_chap = 1;
                break;
            }
+           if (cishort == PPP_EAP) {
+               /* we've already accepted CHAP or PAP */
+               if (ho->neg_chap || ho->neg_upap || cilen != CILEN_SHORT) {
+                   LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE EAP, rejecting..."));
+                   orc = CONFREJ;
+                   break;
+               }
+               if (!ao->neg_eap) {     /* we don't want to do EAP */
+                   orc = CONFNAK;      /* NAK it and suggest CHAP or PAP */
+                   PUTCHAR(CI_AUTHTYPE, nakp);
+                   if (ao->neg_chap) {
+                       PUTCHAR(CILEN_CHAP, nakp);
+                       PUTSHORT(PPP_CHAP, nakp);
+                       PUTCHAR(ao->chap_mdtype, nakp);
+                   } else {
+                       PUTCHAR(CILEN_SHORT, nakp);
+                       PUTSHORT(PPP_PAP, nakp);
+                   }
+                   break;
+               }
+               ho->neg_eap = 1;
+               break;
+           }
 
            /*
             * We don't recognize the protocol they're asking for.
             * Nak it with something we're willing to do.
-            * (At this point we know ao->neg_upap || ao->neg_chap.)
+            * (At this point we know ao->neg_upap || ao->neg_chap ||
+            * ao->neg_eap.)
             */
            orc = CONFNAK;
            PUTCHAR(CI_AUTHTYPE, nakp);
-           if (ao->neg_chap) {
+           if (ao->neg_eap) {
+               PUTCHAR(CILEN_SHORT, nakp);
+               PUTSHORT(PPP_EAP, nakp);
+           } else if (ao->neg_chap) {
                PUTCHAR(CILEN_CHAP, nakp);
                PUTSHORT(PPP_CHAP, nakp);
                PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp);
@@ -1956,6 +2028,9 @@ lcp_printpkt(p, plen, printer, arg)
                            }
                        }
                        break;
+                   case PPP_EAP:
+                       printer(arg, "eap");
+                       break;
                    default:
                        printer(arg, "0x%x", cishort);
                    }
index 95260bb387d02a9f8cde10b6ac40a96a1e24111f..e6b1b9d6a11ea471c3c5e5f3c1ea0972fe6d8cbc 100644 (file)
@@ -16,7 +16,7 @@
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
- * $Id: lcp.h,v 1.17 2002/03/01 14:39:18 dfs Exp $
+ * $Id: lcp.h,v 1.18 2002/11/02 19:48:12 carlsonj Exp $
  */
 
 /*
@@ -54,6 +54,7 @@ typedef struct lcp_options {
     bool neg_asyncmap;         /* Negotiate the async map? */
     bool neg_upap;             /* Ask for UPAP authentication? */
     bool neg_chap;             /* Ask for CHAP authentication? */
+    bool neg_eap;              /* Ask for EAP authentication? */
     bool neg_magicnumber;      /* Ask for magic number? */
     bool neg_pcompression;     /* HDLC Protocol Field Compression? */
     bool neg_accompression;    /* HDLC Address/Control Field Compression? */
index e24e08bfc806130605cca9f73b5b840ceab0f8d5..25a0379e3cdc71da307160a32de731a86e666e47 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: main.c,v 1.118 2002/10/27 12:19:58 fcusack Exp $"
+#define RCSID  "$Id: main.c,v 1.119 2002/11/02 19:48:12 carlsonj Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -52,6 +52,7 @@
 #endif
 #include "upap.h"
 #include "chap.h"
+#include "eap.h"
 #include "ccp.h"
 #include "ecp.h"
 #include "pathnames.h"
@@ -229,6 +230,7 @@ struct protent *protocols[] = {
 #ifdef AT_CHANGE
     &atcp_protent,
 #endif
+    &eap_protent,
     NULL
 };
 
@@ -990,7 +992,7 @@ get_input()
      * Toss all non-LCP packets unless LCP is OPEN.
      */
     if (protocol != PPP_LCP && lcp_fsm[0].state != OPENED) {
-       MAINDEBUG(("get_input: Received non-LCP packet when LCP not open."));
+       dbglog("Discarded non-LCP packet when LCP not open");
        return;
     }
 
@@ -1000,9 +1002,10 @@ get_input()
      */
     if (phase <= PHASE_AUTHENTICATE
        && !(protocol == PPP_LCP || protocol == PPP_LQR
-            || protocol == PPP_PAP || protocol == PPP_CHAP)) {
-       MAINDEBUG(("get_input: discarding proto 0x%x in phase %d",
-                  protocol, phase));
+            || protocol == PPP_PAP || protocol == PPP_CHAP ||
+               protocol == PPP_EAP)) {
+       dbglog("discarding proto 0x%x in phase %d",
+                  protocol, phase);
        return;
     }
 
index ebac0f02dae82beda57ad52e0b4b6cac75442faa..dccf0fda35720e13136be034038cc441001437e8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * define path names
  *
- * $Id: pathnames.h,v 1.14 2001/03/08 05:15:37 paulus Exp $
+ * $Id: pathnames.h,v 1.15 2002/11/02 19:48:13 carlsonj Exp $
  */
 
 #ifdef HAVE_PATHS_H
@@ -20,6 +20,7 @@
 
 #define _PATH_UPAPFILE          _ROOT_PATH "/etc/ppp/pap-secrets"
 #define _PATH_CHAPFILE          _ROOT_PATH "/etc/ppp/chap-secrets"
+#define _PATH_SRPFILE   _ROOT_PATH "/etc/ppp/srp-secrets"
 #define _PATH_SYSOPTIONS _ROOT_PATH "/etc/ppp/options"
 #define _PATH_IPUP      _ROOT_PATH "/etc/ppp/ip-up"
 #define _PATH_IPDOWN    _ROOT_PATH "/etc/ppp/ip-down"
@@ -31,6 +32,7 @@
 #define _PATH_RESOLV    _ROOT_PATH "/etc/ppp/resolv.conf"
 
 #define _PATH_USEROPT   ".ppprc"
+#define        _PATH_PSEUDONYM  ".ppp_pseudonym"
 
 #ifdef INET6
 #define _PATH_IPV6UP     _ROOT_PATH "/etc/ppp/ipv6-up"
diff --git a/pppd/pppcrypt.c b/pppd/pppcrypt.c
new file mode 100644 (file)
index 0000000..1c4ce3b
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1
+ *
+ * Extracted from chap_ms.c by James Carlson.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist.  The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <errno.h>
+#include "pppd.h"
+#include "pppcrypt.h"
+
+static u_char
+Get7Bits(input, startBit)
+u_char *input;
+int startBit;
+{
+       unsigned int word;
+
+       word  = (unsigned)input[startBit / 8] << 8;
+       word |= (unsigned)input[startBit / 8 + 1];
+
+       word >>= 15 - (startBit % 8 + 7);
+
+       return word & 0xFE;
+}
+
+static void
+MakeKey(key, des_key)
+u_char *key;           /* IN  56 bit DES key missing parity bits */
+u_char *des_key;       /* OUT 64 bit DES key with parity bits added */
+{
+       des_key[0] = Get7Bits(key,  0);
+       des_key[1] = Get7Bits(key,  7);
+       des_key[2] = Get7Bits(key, 14);
+       des_key[3] = Get7Bits(key, 21);
+       des_key[4] = Get7Bits(key, 28);
+       des_key[5] = Get7Bits(key, 35);
+       des_key[6] = Get7Bits(key, 42);
+       des_key[7] = Get7Bits(key, 49);
+
+#ifndef USE_CRYPT
+       des_set_odd_parity((des_cblock *)des_key);
+#endif
+}
+
+#ifdef USE_CRYPT
+/*
+ * in == 8-byte string (expanded version of the 56-bit key)
+ * out == 64-byte string where each byte is either 1 or 0
+ * Note that the low-order "bit" is always ignored by by setkey()
+ */
+static void
+Expand(in, out)
+u_char *in;
+u_char *out;
+{
+        int j, c;
+        int i;
+
+        for (i = 0; i < 64; in++){
+               c = *in;
+                for (j = 7; j >= 0; j--)
+                        *out++ = (c >> j) & 01;
+                i += 8;
+        }
+}
+
+/* The inverse of Expand
+ */
+static void
+Collapse(in, out)
+u_char *in;
+u_char *out;
+{
+        int j;
+        int i;
+       unsigned int c;
+
+       for (i = 0; i < 64; i += 8, out++) {
+           c = 0;
+           for (j = 7; j >= 0; j--, in++)
+               c |= *in << j;
+           *out = c & 0xff;
+       }
+}
+
+bool
+DesSetkey(key)
+u_char *key;
+{
+       u_char des_key[8];
+       u_char crypt_key[66];
+
+       MakeKey(key, des_key);
+       Expand(des_key, crypt_key);
+       errno = 0;
+       setkey((const char *)crypt_key);
+       if (errno != 0)
+               return (0);
+       return (1);
+}
+
+bool
+DesEncrypt(clear, cipher)
+u_char *clear; /* IN  8 octets */
+u_char *cipher;        /* OUT 8 octets */
+{
+       u_char des_input[66];
+
+       Expand(clear, des_input);
+       errno = 0;
+       encrypt((char *)des_input, 0);
+       if (errno != 0)
+               return (0);
+       Collapse(des_input, cipher);
+       return (1);
+}
+
+bool
+DesDecrypt(cipher, clear)
+u_char *cipher;        /* IN  8 octets */
+u_char *clear; /* OUT 8 octets */
+{
+       u_char des_input[66];
+
+       Expand(cipher, des_input);
+       errno = 0;
+       encrypt((char *)des_input, 1);
+       if (errno != 0)
+               return (0);
+       Collapse(des_input, clear);
+       return (1);
+}
+
+#else /* USE_CRYPT */
+static des_key_schedule        key_schedule;
+
+bool
+DesSetkey(key)
+u_char *key;
+{
+       des_cblock des_key;
+       MakeKey(key, des_key);
+       des_set_key(&des_key, key_schedule);
+       return (1);
+}
+
+bool
+DesEncrypt(clear, key, cipher)
+u_char *clear; /* IN  8 octets */
+u_char *cipher;        /* OUT 8 octets */
+{
+       des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher,
+           key_schedule, 1);
+       return (1);
+}
+
+bool
+DesDecrypt(cipher, clear)
+u_char *cipher;        /* IN  8 octets */
+u_char *clear; /* OUT 8 octets */
+{
+       des_ecb_encrypt((des_cblock *)cipher, (des_cblock *)clear,
+           key_schedule, 0);
+       return (1);
+}
+
+#endif /* USE_CRYPT */
diff --git a/pppd/pppcrypt.h b/pppd/pppcrypt.h
new file mode 100644 (file)
index 0000000..76fa68a
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1
+ *
+ * Extracted from chap_ms.c by James Carlson.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist.  The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef PPPCRYPT_H
+#define        PPPCRYPT_H
+
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#ifndef USE_CRYPT
+#include <des.h>
+#endif
+
+extern bool    DesSetkey __P((u_char *));
+extern bool    DesEncrypt __P((u_char *, u_char *));
+extern bool    DesDecrypt __P((u_char *, u_char *));
+
+#endif /* PPPCRYPT_H */
index 91ea477be53821ae787725acf8e3ed587c7ecf40..8dd3137932c7064bfd1994e1f5afe87d4aee1493 100644 (file)
@@ -1,5 +1,5 @@
 .\" manual page [] for pppd 2.4
-.\" $Id: pppd.8,v 1.66 2002/10/10 05:47:34 fcusack Exp $
+.\" $Id: pppd.8,v 1.67 2002/11/02 19:48:13 carlsonj Exp $
 .\" SH section heading
 .\" SS subsection heading
 .\" LP paragraph
@@ -331,6 +331,28 @@ the MAC type, the value may also be the name of an ethernet or similar
 network interface.  This option is currently only available under
 Linux.
 .TP
+.B eap-interval \fIn
+If this option is given and pppd authenticates the peer with EAP
+(i.e., is the server), pppd will restart EAP authentication every
+\fIn\fR seconds.  For EAP SRP-SHA1, see also the \fBsrp-interval\fR
+option, which enables lightweight rechallenge.
+.TP
+.B eap-max-rreq \fIn
+Set the maximum number of EAP Requests to which pppd will respond (as
+a client) without hearing EAP Success or Failure.  (Default is 20.)
+.TP
+.B eap-max-sreq \fIn
+Set the maximum number of EAP Requests that pppd will issue (as a
+server) while attempting authentication.  (Default is 10.)
+.TP
+.B eap-restart \fIn
+Set the retransmit timeout for EAP Requests when acting as a server
+(authenticator).  (Default is 3 seconds.)
+.TP
+.B eap-timeout \fIn
+Set the maximum time to wait for the peer to send an EAP Request when
+acting as a client (authenticatee).  (Default is 20 seconds.)
+.TP
 .B hide-password
 When logging the contents of PAP packets, this option causes pppd to
 exclude the password string from the log.  This is the default.
@@ -885,6 +907,10 @@ peer using MS-CHAP.
 With this option, pppd will not agree to authenticate itself to the
 peer using MS-CHAPv2.
 .TP
+.B refuse-eap
+With this option, pppd will not agree to authenticate itself to the
+peer using EAP.
+.TP
 .B refuse-pap
 With this option, pppd will not agree to authenticate itself to the
 peer using PAP.
@@ -915,6 +941,10 @@ Handshake Authentication Protocol] authentication.
 Require the peer to authenticate itself using MS-CHAPv2 [Microsft Challenge
 Handshake Authentication Protocol, Version 2] authentication.
 .TP
+.B require-eap
+Require the peer to authenticate itself using EAP [Extensible
+Authentication Protocol] authentication.
+.TP
 .B require-pap
 Require the peer to authenticate itself using PAP [Password
 Authentication Protocol] authentication.
@@ -928,6 +958,27 @@ With this option, pppd will not transmit LCP packets to initiate a
 connection until a valid LCP packet is received from the peer (as for
 the `passive' option with ancient versions of pppd).
 .TP
+.B srp-interval \fIn
+If this parameter is given and pppd uses EAP SRP-SHA1 to authenticate
+the peer (i.e., is the server), then pppd will use the optional
+lightweight SRP rechallenge mechanism at intervals of \fIn\fR
+seconds.  This option is faster than \fBeap-interval\fR
+reauthentication because it uses a hash-based mechanism and does not
+derive a new session key.
+.TP
+.B srp-pn-secret \fIstring
+Set the long-term pseudonym-generating secret for the server.  This
+value is optional and if set, needs to be known at the server
+(authenticator) side only, and should be different for each server (or
+poll of identical servers).  It is used along with the current date to
+generate a key to encrypt and decrypt the client's identity contained
+in the pseudonym.
+.TP
+.B srp-use-pseudonym
+When operating as an EAP SRP-SHA1 client, attempt to use the pseudonym
+stored in ~/.ppp_psuedonym first as the identity, and save in this
+file any pseudonym offered by the peer during authentication.
+.TP
 .B sync
 Use synchronous HDLC serial encoding instead of asynchronous.
 The device used by pppd with this option must have sync support.
@@ -1044,15 +1095,18 @@ knows that secret.  Very often, the names used for authentication
 correspond to the internet hostnames of the peers, but this is not
 essential.
 .LP
-At present, pppd supports two authentication protocols: the Password
-Authentication Protocol (PAP) and the Challenge Handshake
-Authentication Protocol (CHAP).  PAP involves the client sending its
-name and a cleartext password to the server to authenticate itself.
-In contrast, the server initiates the CHAP authentication exchange by
-sending a challenge to the client (the challenge packet includes the
-server's name).  The client must respond with a response which
-includes its name plus a hash value derived from the shared secret and
-the challenge, in order to prove that it knows the secret.
+At present, pppd supports three authentication protocols: the Password
+Authentication Protocol (PAP), Challenge Handshake Authentication
+Protocol (CHAP), and Extensible Authentication Protocol (EAP).  PAP
+involves the client sending its name and a cleartext password to the
+server to authenticate itself.  In contrast, the server initiates the
+CHAP authentication exchange by sending a challenge to the client (the
+challenge packet includes the server's name).  The client must respond
+with a response which includes its name plus a hash value derived from
+the shared secret and the challenge, in order to prove that it knows
+the secret.  EAP supports CHAP-style authentication, and also includes
+the SRP-SHA1 mechanism, which is resistant to dictionary-based attacks
+and does not require a cleartext password on the server side.
 .LP
 The PPP protocol, being symmetrical, allows both peers to require the
 other to authenticate itself.  In that case, two separate and
@@ -1066,9 +1120,10 @@ pppd will not agree to authenticate itself with a particular protocol
 if it has no secrets which could be used to do so.
 .LP
 Pppd stores secrets for use in authentication in secrets
-files (/etc/ppp/pap-secrets for PAP, /etc/ppp/chap-secrets for
-CHAP/MS-CHAP/MS-CHAPv2).
-Both secrets files have the same format.  The secrets files can
+files (/etc/ppp/pap-secrets for PAP, /etc/ppp/chap-secrets for CHAP,
+MS-CHAP, MS-CHAPv2, and EAP MD5-Challenge, and /etc/ppp/srp-secrets
+for EAP SRP-SHA1).
+All secrets files have the same format.  The secrets files can
 contain secrets for pppd to use in authenticating itself to other
 systems, as well as secrets for pppd to use when authenticating other
 systems to itself.
@@ -1113,7 +1168,9 @@ field and the name of the local system in the second field.  The
 name of the local system defaults to the hostname, with the domain
 name appended if the \fIdomain\fR option is used.  This default can be
 overridden with the \fIname\fR option, except when the
-\fIusehostname\fR option is used.
+\fIusehostname\fR option is used.  (For EAP SRP-SHA1, see the
+srp-entry(8) utility for generating proper validator entries to be
+used in the "secret" field.)
 .LP
 When pppd is choosing a secret to use in authenticating itself to the
 peer, it first determines what name it is going to use to identify
@@ -1122,14 +1179,14 @@ itself to the peer.  This name can be specified by the user with the
 the name of the local system, determined as described in the previous
 paragraph.  Then pppd looks for a secret with this name in the first
 field and the peer's name in the second field.  Pppd will know the
-name of the peer if CHAP authentication is being used, because the
-peer will have sent it in the challenge packet.  However, if PAP is being
-used, pppd will have to determine the peer's name from the options
-specified by the user.  The user can specify the peer's name directly
-with the \fIremotename\fR option.  Otherwise, if the remote IP address
-was specified by a name (rather than in numeric form), that name will
-be used as the peer's name.  Failing that, pppd will use the null
-string as the peer's name.
+name of the peer if CHAP or EAP authentication is being used, because
+the peer will have sent it in the challenge packet.  However, if PAP
+is being used, pppd will have to determine the peer's name from the
+options specified by the user.  The user can specify the peer's name
+directly with the \fIremotename\fR option.  Otherwise, if the remote
+IP address was specified by a name (rather than in numeric form), that
+name will be used as the peer's name.  Failing that, pppd will use the
+null string as the peer's name.
 .LP
 When authenticating the peer with PAP, the supplied password is first
 compared with the secret from the secrets file.  If the password
@@ -1295,16 +1352,18 @@ pppd proxyarp
 .LP
 To allow a user to use the PPP facilities, you need to allocate an IP
 address for that user's machine and create an entry in
-/etc/ppp/pap-secrets or /etc/ppp/chap-secrets (depending on which
-authentication method the PPP implementation on the user's machine
-supports), so that the user's
-machine can authenticate itself.  For example, if Joe has a machine
-called "joespc" which is to be allowed to dial in to the machine
-called "server" and use the IP address joespc.my.net, you would add an
-entry like this to /etc/ppp/pap-secrets or /etc/ppp/chap-secrets:
+/etc/ppp/pap-secrets, /etc/ppp/chap-secrets, or /etc/ppp/srp-secrets
+(depending on which authentication method the PPP implementation on
+the user's machine supports), so that the user's machine can
+authenticate itself.  For example, if Joe has a machine called
+"joespc" that is to be allowed to dial in to the machine called
+"server" and use the IP address joespc.my.net, you would add an entry
+like this to /etc/ppp/pap-secrets or /etc/ppp/chap-secrets:
 .IP
 joespc server  "joe's secret"  joespc.my.net
 .LP
+(See srp-entry(8) for a means to generate the server's entry when
+SRP-SHA1 is in use.)
 Alternatively, you can create a username called (for example) "ppp",
 whose login shell is pppd and whose home directory is /etc/ppp.
 Options to be used when pppd is run this way can be put in
@@ -1329,7 +1388,7 @@ and debug messages, you will need to edit your /etc/syslog.conf file
 to direct the messages to the desired output device or file.
 .LP
 The \fIdebug\fR option causes the contents of all control packets sent
-or received to be logged, that is, all LCP, PAP, CHAP or IPCP packets.
+or received to be logged, that is, all LCP, PAP, CHAP, EAP, or IPCP packets.
 This can be useful if the PPP negotiation does not succeed or if
 authentication fails.
 If debugging is enabled at compile time, the \fIdebug\fR option also
@@ -1561,6 +1620,16 @@ As for /etc/ppp/pap-secrets, this file should be owned by root and not
 readable or writable by any other user.  Pppd will log a warning if
 this is not the case.
 .TP
+.B /etc/ppp/srp-secrets
+Names, secrets, and IP addresses for EAP authentication.  As for
+/etc/ppp/pap-secrets, this file should be owned by root and not
+readable or writable by any other user.  Pppd will log a warning if
+this is not the case.
+.TP
+.B ~/.ppp_pseudonym
+Saved client-side SRP-SHA1 pseudonym.  See the \fIsrp-use-pseudonym\fR
+option for details.
+.TP
 .B /etc/ppp/options
 System default options for pppd, read before user default options or
 command-line options.
@@ -1613,10 +1682,25 @@ Simpson, W.A.
 .I PPP in HDLC-like Framing.
 July 1994.
 .TP
+.B RFC2284
+Blunk, L.; Vollbrecht, J.,
+.I PPP Extensible Authentication Protocol (EAP).
+March 1998.
+.TP
 .B RFC2472
 Haskin, D.
 .I IP Version 6 over PPP
 December 1998.
+.TP
+.B RFC2945
+Wu, T.,
+.I The SRP Authentication and Key Exchange System
+September 2000.
+.TP
+.B draft-ietf-pppext-eap-srp-03.txt
+Carlson, J.; et al.,
+.I EAP SRP-SHA1 Authentication Protocol.
+July 2001.
 .SH NOTES
 The following signals have the specified effect when sent to pppd.
 .TP
index 35cf85bf2ca51ef7fca09689c3a5078de14b2276..d9f3eece3ba5892e3cc5194fa72a5465a673ba76 100644 (file)
@@ -16,7 +16,7 @@
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
- * $Id: pppd.h,v 1.76 2002/10/27 12:30:54 fcusack Exp $
+ * $Id: pppd.h,v 1.77 2002/11/02 19:48:13 carlsonj Exp $
  */
 
 /*
@@ -317,15 +317,18 @@ extern bool       ms_lanman;      /* Use LanMan password instead of NT */
 #define PAP_PEER       0x2
 #define CHAP_WITHPEER  0x4
 #define CHAP_PEER      0x8
+#define EAP_WITHPEER   0x10
+#define EAP_PEER       0x20
+
 /* Values for auth_done only */
-#define CHAP_MD5_WITHPEER      0x10
-#define CHAP_MD5_PEER          0x20
+#define CHAP_MD5_WITHPEER      0x40
+#define CHAP_MD5_PEER          0x80
 #ifdef CHAPMS
-#define CHAP_MS_SHIFT          6       /* LSB position for MS auths */
-#define CHAP_MS_WITHPEER       0x40
-#define CHAP_MS_PEER           0x80
-#define CHAP_MS2_WITHPEER      0x100
-#define CHAP_MS2_PEER          0x200
+#define CHAP_MS_SHIFT          8       /* LSB position for MS auths */
+#define CHAP_MS_WITHPEER       0x100
+#define CHAP_MS_PEER           0x200
+#define CHAP_MS2_WITHPEER      0x400
+#define CHAP_MS2_PEER          0x800
 #endif
 
 extern char *current_option;   /* the name of the option being parsed */
@@ -514,6 +517,8 @@ int  check_passwd __P((int, char *, int, char *, int, char **));
                                /* Check peer-supplied username/password */
 int  get_secret __P((int, char *, char *, char *, int *, int));
                                /* get "secret" for chap */
+int  get_srp_secret __P((int unit, char *client, char *server, char *secret,
+    int am_server));
 int  auth_ip_addr __P((int, u_int32_t));
                                /* check if IP address is authorized */
 int  auth_number __P((void));  /* check if remote number is authorized */
@@ -719,6 +724,7 @@ extern void (*snoop_send_hook) __P((unsigned char *p, int len));
 
 #define BCOPY(s, d, l)         memcpy(d, s, l)
 #define BZERO(s, n)            memset(s, 0, n)
+#define        BCMP(s1, s2, l)         memcmp(s1, s2, l)
 
 #define PRINTMSG(m, l)         { info("Remote message: %0.*v", l, m); }
 
index dff8da88bd91a041f616e9fb34020c9958144099..86f982d13cd3ed544db664227b0ccf919a725589 100644 (file)
@@ -1,5 +1,11 @@
 /* sha1.h */
 
+/* If OpenSSL is in use, then use that version of SHA-1 */
+#ifdef OPENSSL
+#include <t_sha.h>
+#define __SHA1_INCLUDE_
+#endif
+
 #ifndef __SHA1_INCLUDE_
 
 typedef struct {
@@ -8,11 +14,17 @@ typedef struct {
     unsigned char buffer[64];
 } SHA1_CTX;
 
-#define SHA1_SIGNATURE_SIZE 20
-
 extern void SHA1_Init(SHA1_CTX *);
 extern void SHA1_Update(SHA1_CTX *, const unsigned char *, unsigned int);
 extern void SHA1_Final(unsigned char[SHA1_SIGNATURE_SIZE], SHA1_CTX *);
 
 #define __SHA1_INCLUDE_
 #endif /* __SHA1_INCLUDE_ */
+
+#ifndef SHA1_SIGNATURE_SIZE
+#ifdef SHA_DIGESTSIZE
+#define SHA1_SIGNATURE_SIZE SHA_DIGESTSIZE
+#else
+#define SHA1_SIGNATURE_SIZE 20
+#endif
+#endif
diff --git a/pppd/srp-entry.8 b/pppd/srp-entry.8
new file mode 100644 (file)
index 0000000..ceb516a
--- /dev/null
@@ -0,0 +1,83 @@
+.\" manual page [] for srp-entry
+.\" $Id: srp-entry.8,v 1.1 2002/11/02 19:48:13 carlsonj Exp $
+.\" SH section heading
+.\" SS subsection heading
+.\" LP paragraph
+.\" IP indented paragraph
+.\" TP hanging label
+.TH SRP-ENTRY 8
+.SH NAME
+srp-entry \- Generate a SRP-SHA1 Server Entry
+.SH SYNOPSIS
+.B srp-entry
+[
+.I -i index
+] [
+.I clientname
+]
+.SH DESCRIPTION
+.LP
+This utility generates an entry suitable for use in the
+/etc/ppp/srp-secrets file on a PPP EAP SRP-SHA1 authenticator
+("server").  This file has the same basic layout as the other pppd(8)
+authentication files, /etc/ppp/pap-secrets and /etc/ppp/chap-secrets.
+Thus, the entry generated has at least four main fields separated by
+spaces.  The first field is the authenticatee ("client") name.  The
+second is the server name.  The third is the secret.  The fourth is
+the allowed (or assigned) IP address for the client, and defaults to
+"*".  Additional fields can contain additional IP addresses or pppd
+options; see pppd(8) for details.
+.LP
+The third field has three subfields, separated by colons.  The first
+subfield is the index of the modulus and generator from SRP's
+/etc/tpasswd.conf.  The special value 0 is used to represent the
+well-known modulus and generator specified in the EAP SRP-SHA1 draft.
+The second subfield is the password validator.  The third is the
+password salt.  These latter two values are encoded in base64 notation.
+.SH OPTIONS
+.TP
+.I -i <index>
+Specifies the modulus/generator index in /etc/tpasswd.conf.  In order
+to use this option, you will need to run the "tconf" utility from the
+SRP package to generate local entries for this file.  Note that if
+these values are not known to the client, the client will be forced to
+run time-consuming safety tests on the values used.  For this reason,
+using the well-known values is recommended.
+.TP
+.I <clientname>
+Specifies the client name.  The password validator is a hashed
+combination of the client's name and password, and both are required.
+If the client name is not supplied on the command line, srp-entry will
+prompt for the client name first.
+.SH FILES
+.TP
+.B /etc/ppp/srp-secrets
+Usernames, passwords and IP addresses for SRP authentication.  This
+file should be owned by root and not readable or writable by any other
+user.  Pppd will log a warning if this is not the case.  Note that
+srp-entry does not write to this file.  The user is responsible for
+copying the output of srp-entry into this file.
+.TP
+.B /etc/tpasswd.conf
+Indexed copies of tested modulus/generator combinations; part of the
+SRP package.
+.SH SEE ALSO
+.TP
+pppd(8)
+.TP
+.B RFC2284
+Blunk, L., Vollbrecht, J.,
+.I PPP Extensible Authentication Protocol (EAP).
+March 1998.
+.TP
+.B draft-ietf-pppext-eap-srp-03.txt
+Carlson, J., et al.,
+.I EAP SRP-SHA1 Authentication Protocol.
+July 2001.
+.TP
+.B RFC2945
+Wu, T.,
+.I The SRP Authentication and Key Exchange System
+September 2000.
+.SH AUTHOR
+James Carlson (james.d.carlson@sun.com)
diff --git a/pppd/srp-entry.c b/pppd/srp-entry.c
new file mode 100644 (file)
index 0000000..8c0e297
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Utility program for generating entries in /etc/ppp/srp-secrets
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Non-exclusive rights to redistribute, modify, translate, and use
+ * this software in source and binary forms, in whole or in part, is
+ * hereby granted, provided that the above copyright notice is
+ * duplicated in any source form, and that neither the name of the
+ * copyright holder nor the author is used to endorse or promote
+ * products derived from this software.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Original version by James Carlson
+ *
+ * Usage:
+ *     srp-entry [-i index] [clientname]
+ *
+ * Index, if supplied, is the modulus/generator index from
+ * /etc/tpasswd.conf.  If not supplied, then the last (highest
+ * numbered) entry from that file is used.  If the file doesn't exist,
+ * then the default "well known" EAP SRP-SHA1 modulus/generator is
+ * used.
+ *
+ * The default modulus/generator can be requested as index 0.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <t_pwd.h>
+
+#ifndef        SOL2
+#define        getpassphrase   getpass
+#endif
+
+#define        HAS_SPACE       1
+#define        HAS_DQUOTE      2
+#define        HAS_SQUOTE      4
+#define        HAS_BACKSLASH   8
+
+static const u_char wkmodulus[] = {
+       0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B,
+       0xF1, 0x66, 0xDE, 0x5E, 0x13, 0x89, 0x58, 0x2F,
+       0xAF, 0x72, 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07,
+       0xFC, 0x31, 0x92, 0x94, 0x3D, 0xB5, 0x60, 0x50,
+       0xA3, 0x73, 0x29, 0xCB, 0xB4, 0xA0, 0x99, 0xED,
+       0x81, 0x93, 0xE0, 0x75, 0x77, 0x67, 0xA1, 0x3D,
+       0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, 0x31, 0x0D,
+       0xCD, 0x7F, 0x48, 0xA9, 0xDA, 0x04, 0xFD, 0x50,
+       0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0,
+       0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3,
+       0x66, 0x1A, 0x05, 0xFB, 0xD5, 0xFA, 0xAA, 0xE8,
+       0x29, 0x18, 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8,
+       0x55, 0xF9, 0x79, 0x93, 0xEC, 0x97, 0x5E, 0xEA,
+       0xA8, 0x0D, 0x74, 0x0A, 0xDB, 0xF4, 0xFF, 0x74,
+       0x73, 0x59, 0xD0, 0x41, 0xD5, 0xC3, 0x3E, 0xA7,
+       0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, 0x77, 0x3B,
+       0xCA, 0x97, 0xB4, 0x3A, 0x23, 0xFB, 0x80, 0x16,
+       0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81,
+       0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A,
+       0x5B, 0x9D, 0x32, 0xE6, 0x88, 0xF8, 0x77, 0x48,
+       0x54, 0x45, 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D,
+       0x5E, 0xA7, 0x7A, 0x27, 0x75, 0xD2, 0xEC, 0xFA,
+       0x03, 0x2C, 0xFB, 0xDB, 0xF5, 0x2F, 0xB3, 0x78,
+       0x61, 0x60, 0x27, 0x90, 0x04, 0xE5, 0x7A, 0xE6,
+       0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, 0x53, 0x29,
+       0x9C, 0xCC, 0x04, 0x1C, 0x7B, 0xC3, 0x08, 0xD8,
+       0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82,
+       0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6,
+       0x94, 0xB5, 0xC8, 0x03, 0xD8, 0x9F, 0x7A, 0xE4,
+       0x35, 0xDE, 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75,
+       0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2,
+       0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73
+};
+
+static const char *myname;
+
+static void
+usage(void)
+{
+       (void) fprintf(stderr, "Usage:\n\t%s [-i index] [clientname]\n",
+           myname);
+       exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+       struct t_conf *tc;
+       struct t_confent *tcent, mytce;
+       struct t_pw pwval;
+       char *name;
+       char pname[256];
+       char *pass1, *pass2;
+       int flags, idx;
+       char *cp;
+       char delimit;
+       char strbuf[MAXB64PARAMLEN];
+       char saltbuf[MAXB64SALTLEN];
+
+       if ((myname = *argv) == NULL)
+               myname = "srp-entry";
+       else
+               argv++;
+
+       idx = -1;
+       if (*argv != NULL && strcmp(*argv, "-i") == 0) {
+               if (*++argv == NULL)
+                       usage();
+               idx = atoi(*argv++);
+       }
+
+       tcent = NULL;
+       if (idx != 0 && (tc = t_openconf(NULL)) != NULL) {
+               if (idx == -1)
+                       tcent = t_getconflast(tc);
+               else
+                       tcent = t_getconfbyindex(tc, idx);
+       }
+       if (idx <= 0 && tcent == NULL) {
+               mytce.index = 0;
+               mytce.modulus.data = (u_char *)wkmodulus;
+               mytce.modulus.len = sizeof (wkmodulus);
+               mytce.generator.data = (u_char *)"\002";
+               mytce.generator.len = 1;
+               tcent = &mytce;
+       }
+       if (tcent == NULL) {
+               (void) fprintf(stderr, "SRP modulus/generator %d not found\n",
+                   idx);
+               exit(1);
+       }
+
+       if ((name = *argv) == NULL) {
+               (void) printf("Client name: ");
+               if (fgets(pname, sizeof (pname), stdin) == NULL)
+                       exit(1);
+               if ((cp = strchr(pname, '\n')) != NULL)
+                       *cp = '\0';
+               name = pname;
+       }
+
+       for (;;) {
+               if ((pass1 = getpassphrase("Pass phrase: ")) == NULL)
+                       exit(1);
+               pass1 = strdup(pass1);
+               if ((pass2 = getpassphrase("Re-enter phrase: ")) == NULL)
+                       exit(1);
+               if (strcmp(pass1, pass2) == 0)
+                       break;
+               free(pass1);
+               (void) printf("Phrases don't match; try again.\n");
+       }
+
+       memset(&pwval, 0, sizeof (pwval));
+       t_makepwent(&pwval, name, pass1, NULL, tcent);
+       flags = 0;
+       for (cp = name; *cp != '\0'; cp++)
+               if (isspace(*cp))
+                       flags |= HAS_SPACE;
+               else if (*cp == '"')
+                       flags |= HAS_DQUOTE;
+               else if (*cp == '\'')
+                       flags |= HAS_SQUOTE;
+               else if (*cp == '\\')
+                       flags |= HAS_BACKSLASH;
+       delimit = flags == 0 ? '\0' : (flags & HAS_DQUOTE) ? '\'' : '"';
+       if (delimit != '\0')
+               (void) putchar(delimit);
+       for (cp = name; *cp != '\0'; cp++) {
+               if (*cp == delimit || *cp == '\\')
+                       (void) putchar('\\');
+               (void) putchar(*cp);
+       }
+       if (delimit != '\0')
+               (void) putchar(delimit);
+       (void) printf(" * %d:%s:%s *\n",
+           pwval.pebuf.index, t_tob64(strbuf,
+               (char *)pwval.pebuf.password.data, pwval.pebuf.password.len),
+           t_tob64(saltbuf, (char *)pwval.pebuf.salt.data,
+               pwval.pebuf.salt.len));
+       return 0;
+}