From: James Carlson Date: Sat, 2 Nov 2002 19:48:13 +0000 (+0000) Subject: Added EAP support with MD5-Challenge and SRP-SHA1 methods. Tested X-Git-Tag: ppp-2.4.7~360 X-Git-Url: https://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=d741a3b912f17d84dc8dc87474e0b989c775de50 Added EAP support with MD5-Challenge and SRP-SHA1 methods. Tested 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. --- diff --git a/README.eap-srp b/README.eap-srp new file mode 100644 index 0000000..6900b0d --- /dev/null +++ b/README.eap-srp @@ -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 diff --git a/configure b/configure index 2744f4e..04dd908 100755 --- 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 diff --git a/include/net/ppp_defs.h b/include/net/ppp_defs.h index bda882e..85f1fc7 100644 --- a/include/net/ppp_defs.h +++ b/include/net/ppp_defs.h @@ -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. diff --git a/pppd/Makefile.NeXT b/pppd/Makefile.NeXT index 7b873e2..d350599 100644 --- a/pppd/Makefile.NeXT +++ b/pppd/Makefile.NeXT @@ -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 diff --git a/pppd/Makefile.aix4 b/pppd/Makefile.aix4 index e173f6f..f1bf643 100644 --- a/pppd/Makefile.aix4 +++ b/pppd/Makefile.aix4 @@ -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 diff --git a/pppd/Makefile.bsd b/pppd/Makefile.bsd index 83a899f..c1f7738 100644 --- a/pppd/Makefile.bsd +++ b/pppd/Makefile.bsd @@ -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 diff --git a/pppd/Makefile.linux b/pppd/Makefile.linux index a8d01a4..44f75e7 100644 --- a/pppd/Makefile.linux +++ b/pppd/Makefile.linux @@ -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 diff --git a/pppd/Makefile.netbsd-1.2 b/pppd/Makefile.netbsd-1.2 index e50b7f5..4dbe7e9 100644 --- a/pppd/Makefile.netbsd-1.2 +++ b/pppd/Makefile.netbsd-1.2 @@ -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 diff --git a/pppd/Makefile.osf b/pppd/Makefile.osf index e680d2a..385e311 100644 --- a/pppd/Makefile.osf +++ b/pppd/Makefile.osf @@ -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 diff --git a/pppd/Makefile.sol2 b/pppd/Makefile.sol2 index 83b23af..f15d947 100644 --- a/pppd/Makefile.sol2 +++ b/pppd/Makefile.sol2 @@ -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 diff --git a/pppd/Makefile.sunos4 b/pppd/Makefile.sunos4 index 09276d2..7bd5c3a 100644 --- a/pppd/Makefile.sunos4 +++ b/pppd/Makefile.sunos4 @@ -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) diff --git a/pppd/Makefile.svr4 b/pppd/Makefile.svr4 index ea7e7f8..f84d758 100644 --- a/pppd/Makefile.svr4 +++ b/pppd/Makefile.svr4 @@ -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) diff --git a/pppd/Makefile.ultrix b/pppd/Makefile.ultrix index 8ffa624..57072c8 100644 --- a/pppd/Makefile.ultrix +++ b/pppd/Makefile.ultrix @@ -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 = diff --git a/pppd/auth.c b/pppd/auth.c index cf04268..076392f 100644 --- a/pppd/auth.c +++ b/pppd/auth.c @@ -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 #include @@ -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. diff --git a/pppd/chap_ms.c b/pppd/chap_ms.c index 4e50d5b..1041e03 100644 --- a/pppd/chap_ms.c +++ b/pppd/chap_ms.c @@ -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 @@ -51,19 +51,13 @@ #include #include #include -#ifdef HAVE_CRYPT_H -#include -#endif #include "pppd.h" #include "chap.h" #include "chap_ms.h" #include "md4.h" #include "sha1.h" - -#ifndef USE_CRYPT -#include -#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 index 0000000..cc3bc73 --- /dev/null +++ b/pppd/eap.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#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, " "); + 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, " "); + INCPTR(len, inp); + len = 0; + } else { + printer(arg, " "); + } + break; + + case EAPT_MD5CHAP: + if (len <= 0) + break; + GETCHAR(vallen, inp); + len--; + if (vallen > len) + goto truncated; + printer(arg, " ", vallen, inp); + INCPTR(vallen, inp); + len -= vallen; + if (len > 0) { + printer(arg, " "); + INCPTR(len, inp); + len = 0; + } else { + printer(arg, " "); + } + 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, " "); + } else { + printer(arg, " "); + } + INCPTR(vallen, inp); + len -= vallen; + GETCHAR(vallen, inp); + len--; + if (vallen >= len) + goto truncated; + printer(arg, " ", vallen, inp); + INCPTR(vallen, inp); + len -= vallen; + GETCHAR(vallen, inp); + len--; + if (vallen > len) + goto truncated; + if (vallen == 0) { + printer(arg, " "); + } else { + printer(arg, " ", vallen, inp); + } + INCPTR(vallen, inp); + len -= vallen; + if (len == 0) { + printer(arg, " "); + } else { + printer(arg, " ", len, inp); + INCPTR(len, inp); + len = 0; + } + break; + + case EAPSRP_SKEY: + printer(arg, " ", 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, " ", len, inp, + len < SHA_DIGESTSIZE ? "?" : ""); + INCPTR(vallen, inp); + len -= vallen; + if (len > 0) { + printer(arg, " ", len, inp); + INCPTR(len, inp); + len = 0; + } + break; + + case EAPSRP_LWRECHALLENGE: + printer(arg, " ", 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, " "); + INCPTR(len, inp); + len = 0; + } + break; + + case EAPT_NAK: + if (len <= 0) { + printer(arg, " "); + break; + } + GETCHAR(rtype, inp); + len--; + printer(arg, " = 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, " "); + break; + } + GETCHAR(vallen, inp); + len--; + if (vallen > len) + goto truncated; + printer(arg, " ", vallen, inp); + INCPTR(vallen, inp); + len -= vallen; + if (len > 0) { + printer(arg, " "); + INCPTR(len, inp); + len = 0; + } else { + printer(arg, " "); + } + break; + + case EAPT_SRP: + if (len < 1) + goto truncated; + GETCHAR(vallen, inp); + len--; + printer(arg, "-%d", vallen); + switch (vallen) { + case EAPSRP_CKEY: + printer(arg, " ", 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, " ", len, inp, + len == SHA_DIGESTSIZE ? "" : "?"); + INCPTR(len, inp); + len = 0; + break; + + case EAPSRP_ACK: + break; + + case EAPSRP_LWRECHALLENGE: + printer(arg, " ", 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, " "); + 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 index 0000000..cf5adac --- /dev/null +++ b/pppd/eap.h @@ -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 */ + diff --git a/pppd/lcp.c b/pppd/lcp.c index 1923c6d..5d1500e 100644 --- a/pppd/lcp.c +++ b/pppd/lcp.c @@ -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); } diff --git a/pppd/lcp.h b/pppd/lcp.h index 95260bb..e6b1b9d 100644 --- a/pppd/lcp.h +++ b/pppd/lcp.h @@ -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? */ diff --git a/pppd/main.c b/pppd/main.c index e24e08b..25a0379 100644 --- a/pppd/main.c +++ b/pppd/main.c @@ -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 #include @@ -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; } diff --git a/pppd/pathnames.h b/pppd/pathnames.h index ebac0f0..dccf0fd 100644 --- a/pppd/pathnames.h +++ b/pppd/pathnames.h @@ -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 index 0000000..1c4ce3b --- /dev/null +++ b/pppd/pppcrypt.c @@ -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 +#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 index 0000000..76fa68a --- /dev/null +++ b/pppd/pppcrypt.h @@ -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 +#endif + +#ifndef USE_CRYPT +#include +#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 */ diff --git a/pppd/pppd.8 b/pppd/pppd.8 index 91ea477..8dd3137 100644 --- a/pppd/pppd.8 +++ b/pppd/pppd.8 @@ -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 diff --git a/pppd/pppd.h b/pppd/pppd.h index 35cf85b..d9f3eec 100644 --- a/pppd/pppd.h +++ b/pppd/pppd.h @@ -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); } diff --git a/pppd/sha1.h b/pppd/sha1.h index dff8da8..86f982d 100644 --- a/pppd/sha1.h +++ b/pppd/sha1.h @@ -1,5 +1,11 @@ /* sha1.h */ +/* If OpenSSL is in use, then use that version of SHA-1 */ +#ifdef OPENSSL +#include +#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 index 0000000..ceb516a --- /dev/null +++ b/pppd/srp-entry.8 @@ -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 +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 +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 index 0000000..8c0e297 --- /dev/null +++ b/pppd/srp-entry.c @@ -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 +#include +#include +#include +#include +#include + +#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; +}