diff -Naur ppp-2.4.5/README.eap-tls ppp-2.4.5-eaptls-mppe-0.992/README.eap-tls --- ppp-2.4.5/README.eap-tls 1970-01-01 01:00:00.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/README.eap-tls 2011-12-01 15:14:39.020344767 +0100 @@ -0,0 +1,169 @@ +EAP-TLS authentication support for PPP +====================================== + +1. Intro + + The Extensible Authentication Protocol (EAP; RFC 3748) is a + security protocol that can be used with PPP. It provides a means + to plug in multiple optional authentication methods. + + Transport Level Security (TLS; RFC 2246) provides for mutual + authentication, integrity-protected ciphersuite negotiation and + key exchange between two endpoints. It also provides for optional + MPPE encryption. + + EAP-TLS (RFC 2716) incapsulates the TLS messages in EAP packets, + allowing TLS mutual authentication to be used as a generic EAP + mechanism. It also provides optional encryption using the MPPE + protocol. + + This patch provide EAP-TLS support to pppd. + This authentication method can be used in both client or server + mode. + +2. Building + + To build pppd with EAP-TLS support, OpenSSL (http://www.openssl.org) + is required. Any version from 0.9.7 should work. + + Configure, compile, and install as usual. + +3. Configuration + + On the client side there are two ways to configure EAP-TLS: + + 1. supply the appropriate 'ca', 'cert' and 'key' command-line parameters + + 2. edit the /etc/ppp/eaptls-client file. + Insert a line for each system with which you use EAP-TLS. + The line is composed of this fields separated by tab: + + - Client name + The name used by the client for authentication, can be * + - Server name + The name of the server, can be * + - Client certificate file + The file containing the certificate chain for the + client in PEM format + - Server certificate file + If you want to specify the certificate that the + server is allowed to use, put the certificate file name. + Else put a dash '-'. + - CA certificate file + The file containing the trusted CA certificates in PEM + format. + - Client private key file + The file containing the client private key in PEM format. + + + On the server side edit the /etc/ppp/eaptls-server file. + Insert a line for each system with which you use EAP-TLS. + The line is composed of this fields separated by tab: + + - Client name + The name used by the client for authentication, can be * + - Server name + The name of the server, can be * + - Client certificate file + If you want to specify the certificate that the + client is allowed to use, put the certificate file name. + Else put a dash '-'. + - Server certificate file + The file containing the certificate chain for the + server in PEM format + - CA certificate file + The file containing the trusted CA certificates in PEM + format. + - Client private key file + The file containing the server private key in PEM format. + - addresses + A list of IP addresses the client is allowed to use. + + + OpenSSL engine support is included starting with v0.95 of this patch. + Currently the only engine tested is the 'pkcs11' engine (hardware token + support). To use the 'pksc11' engine: + - Use a special private key fileiname in the /etc/ppp/eaptls-client file: + : + e.g. + pkcs11:123456 + + - The certificate can also be loaded from the 'pkcs11' engine using + a special client certificate filename in the /etc/ppp/eaptls-client file: + : + e.g. + pkcs11:123456 + + - Create an /etc/ppp/openssl.cnf file to load the right OpenSSL engine prior + to starting 'pppd'. A sample openssl.cnf file is + + openssl_conf = openssl_def + + [ openssl_def ] + engines = engine_section + + [ engine_section ] + pkcs11 = pkcs11_section + + [ pkcs11_section ] + engine_id = pkcs11 + dynamic_path = /usr/lib64/openssl/engines/engine_pkcs11.so + MODULE_PATH = /usr/lib64/libeTPkcs11.so + init = 0 + + - There are two ways to specify a password/PIN for the PKCS11 engine: + - inside the openssl.cnf file using + PIN = your-secret-pin + Note The keyword 'PIN' is case sensitive! + - Using the 'password' in the ppp options file. + From v0.97 of the eap-tls patch the password can also be supplied + using the appropriate 'eaptls_passwd_hook' (see plugins/passprompt.c + for an example). + + +4. Options + + These pppd options are available: + + ca + Use the CA public certificate found in in PEM format + cert + Use the client public certificate found in in PEM format + or in engine:engine_id format + key + Use the client private key found in in PEM format + or in engine:engine_id format + crl-dir + Use CRL files from dir. It contains CRL files in PEM + format and each file contains a CRL. The files are looked up + by the issuer name hash value. Use the c_rehash utility + to create necessary links. + need-peer-eap + If the peer doesn't ask us to authenticate or doesn't use eap + to authenticate us, disconnect. + + Note: + password-encrypted certificates can be used as of v0.94 of this + patch. The password for the eap-tls.key file is specified using + the regular + password .... + statement in the ppp options file, or by using the appropriate + plugin which supplies a 'eaptls_passwd_hook' routine. + +5. Connecting + + If you're setting up a pppd server, edit the EAP-TLS configuration file + as written above and then run pppd with the 'auth' option to authenticate + the client. The EAP-TLS method will be used if the other eap methods can't + be used (no secrets). + + If you're setting up a client, edit the configuration file and then run + pppd with 'remotename' option to specify the server name. Add the + 'need-peer-eap' option if you want to be sure the peer ask you to + authenticate (and to use eap) and to disconnect if it doesn't. + +6. Notes + + This is experimental code. + Send suggestions and comments to Jan Just Keijser + diff -Naur ppp-2.4.5/etc.ppp/eaptls-client ppp-2.4.5-eaptls-mppe-0.992/etc.ppp/eaptls-client --- ppp-2.4.5/etc.ppp/eaptls-client 1970-01-01 01:00:00.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/etc.ppp/eaptls-client 2011-12-01 15:14:39.030346052 +0100 @@ -0,0 +1,10 @@ +# Parameters for authentication using EAP-TLS (client) + +# client name (can be *) +# server name (can be *) +# client certificate file (required) +# server certificate file (optional, if unused put '-') +# CA certificate file (required) +# client private key file (required) + +#client server /root/cert/client.crt - /root/cert/ca.crt /root/cert/client.key diff -Naur ppp-2.4.5/etc.ppp/eaptls-server ppp-2.4.5-eaptls-mppe-0.992/etc.ppp/eaptls-server --- ppp-2.4.5/etc.ppp/eaptls-server 1970-01-01 01:00:00.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/etc.ppp/eaptls-server 2011-12-01 15:14:39.038347081 +0100 @@ -0,0 +1,11 @@ +# Parameters for authentication using EAP-TLS (server) + +# client name (can be *) +# server name (can be *) +# client certificate file (optional, if unused put '-') +# server certificate file (required) +# CA certificate file (required) +# server private key file (required) +# allowed addresses (required, can be *) + +#client server - /root/cert/server.crt /root/cert/ca.crt /root/cert/server.key 192.168.1.0/24 diff -Naur ppp-2.4.5/etc.ppp/openssl.cnf ppp-2.4.5-eaptls-mppe-0.992/etc.ppp/openssl.cnf --- ppp-2.4.5/etc.ppp/openssl.cnf 1970-01-01 01:00:00.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/etc.ppp/openssl.cnf 2011-12-01 15:14:39.038347081 +0100 @@ -0,0 +1,14 @@ +openssl_conf = openssl_def + +[ openssl_def ] +engines = engine_section + +[ engine_section ] +pkcs11 = pkcs11_section + +[ pkcs11_section ] +engine_id = pkcs11 +dynamic_path = /usr/lib64/openssl/engines/engine_pkcs11.so +MODULE_PATH = /usr/lib64/libeTPkcs11.so +init = 0 + diff -Naur ppp-2.4.5/linux/Makefile.top ppp-2.4.5-eaptls-mppe-0.992/linux/Makefile.top --- ppp-2.4.5/linux/Makefile.top 2009-11-16 23:26:07.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/linux/Makefile.top 2011-12-01 15:14:39.046348110 +0100 @@ -26,7 +26,7 @@ cd pppdump; $(MAKE) $(MFLAGS) install install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \ - $(ETCDIR)/chap-secrets + $(ETCDIR)/chap-secrets $(ETCDIR)/eaptls-server $(ETCDIR)/eaptls-client install-devel: cd pppd; $(MAKE) $(MFLAGS) install-devel @@ -37,6 +37,10 @@ $(INSTALL) -c -m 600 etc.ppp/pap-secrets $@ $(ETCDIR)/chap-secrets: $(INSTALL) -c -m 600 etc.ppp/chap-secrets $@ +$(ETCDIR)/eaptls-server: + $(INSTALL) -c -m 600 etc.ppp/eaptls-server $@ +$(ETCDIR)/eaptls-client: + $(INSTALL) -c -m 600 etc.ppp/eaptls-client $@ $(BINDIR): $(INSTALL) -d -m 755 $@ diff -Naur ppp-2.4.5/pppd/Makefile.linux ppp-2.4.5-eaptls-mppe-0.992/pppd/Makefile.linux --- ppp-2.4.5/pppd/Makefile.linux 2009-11-16 23:26:07.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/Makefile.linux 2011-12-01 15:14:39.060349910 +0100 @@ -33,7 +33,7 @@ # CC = gcc # COPTS = -O2 -pipe -Wall -g -LIBS = +LIBS = # Uncomment the next 2 lines to include support for Microsoft's # MS-CHAP authentication protocol. Also, edit plugins/radius/Makefile.linux. @@ -73,6 +73,9 @@ # Enable EAP SRP-SHA1 authentication (requires libsrp) #USE_SRP=y +# Enable EAP-TLS authentication (requires libssl and libcrypto) +USE_EAPTLS=y + MAXOCTETS=y INCLUDE_DIRS= -I../include @@ -112,6 +115,15 @@ PPPDOBJS += sha1.o endif +# EAP-TLS +ifdef USE_EAPTLS +CFLAGS += -DUSE_EAPTLS=1 -I/usr/kerberos/include +LIBS += -lssl -lcrypto +PPPDSRC += eap-tls.c +HEADERS += eap-tls.h +PPPDOBJS += eap-tls.o +endif + ifdef HAS_SHADOW CFLAGS += -DHAS_SHADOW #LIBS += -lshadow $(LIBS) diff -Naur ppp-2.4.5/pppd/auth.c ppp-2.4.5-eaptls-mppe-0.992/pppd/auth.c --- ppp-2.4.5/pppd/auth.c 2009-11-16 23:26:07.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/auth.c 2011-12-01 15:14:39.069351068 +0100 @@ -109,6 +109,9 @@ #include "upap.h" #include "chap-new.h" #include "eap.h" +#ifdef USE_EAPTLS +#include "eap-tls.h" +#endif #ifdef CBCP_SUPPORT #include "cbcp.h" #endif @@ -183,6 +186,11 @@ /* Hook for a plugin to get the CHAP password for authenticating us */ int (*chap_passwd_hook) __P((char *user, char *passwd)) = NULL; +#ifdef USE_EAPTLS +/* Hook for a plugin to get the EAP-TLS password for authenticating us */ +int (*eaptls_passwd_hook) __P((char *user, char *passwd)) = NULL; +#endif + /* Hook for a plugin to say whether it is OK if the peer refuses to authenticate. */ int (*null_auth_hook) __P((struct wordlist **paddrs, @@ -238,6 +246,13 @@ bool explicit_user = 0; /* Set if "user" option supplied */ bool explicit_passwd = 0; /* Set if "password" option supplied */ char remote_name[MAXNAMELEN]; /* Peer's name for authentication */ +#ifdef USE_EAPTLS +char *cacert_file = NULL; /* CA certificate file (pem format) */ +char *cert_file = NULL; /* client certificate file (pem format) */ +char *privkey_file = NULL; /* client private key file (pem format) */ +char *crl_dir = NULL; /* directory containing CRL files */ +bool need_peer_eap = 0; /* Require peer to authenticate us */ +#endif static char *uafname; /* name of most recent +ua file */ @@ -254,6 +269,19 @@ 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)); + +#ifdef USE_EAPTLS +static int have_eaptls_secret_server +__P((char *client, char *server, int need_ip, int *lacks_ipp)); +static int have_eaptls_secret_client __P((char *client, char *server)); +static int scan_authfile_eaptls __P((FILE * f, char *client, char *server, + char *cli_cert, char *serv_cert, + char *ca_cert, char *pk, + struct wordlist ** addrs, + struct wordlist ** opts, + char *filename, int flags)); +#endif + 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 **, @@ -401,6 +429,14 @@ "Set telephone number(s) which are allowed to connect", OPT_PRIV | OPT_A2LIST }, +#ifdef USE_EAPTLS + { "ca", o_string, &cacert_file, "EAP-TLS CA certificate in PEM format" }, + { "cert", o_string, &cert_file, "EAP-TLS client certificate in PEM format" }, + { "key", o_string, &privkey_file, "EAP-TLS client private key in PEM format" }, + { "crl-dir", o_string, &crl_dir, "Use CRLs in directory" }, + { "need-peer-eap", o_bool, &need_peer_eap, + "Require the peer to authenticate us", 1 }, +#endif /* USE_EAPTLS */ { NULL } }; @@ -731,6 +767,9 @@ lcp_options *wo = &lcp_wantoptions[unit]; lcp_options *go = &lcp_gotoptions[unit]; lcp_options *ho = &lcp_hisoptions[unit]; +#ifdef USE_EAPTLS + lcp_options *ao = &lcp_allowoptions[unit]; +#endif int i; struct protent *protp; @@ -765,6 +804,22 @@ } } +#ifdef USE_EAPTLS + if (need_peer_eap && !ao->neg_eap) { + warn("eap required to authenticate us but no suitable secrets"); + lcp_close(unit, "couldn't negotiate eap"); + status = EXIT_AUTH_TOPEER_FAILED; + return; + } + + if (need_peer_eap && !ho->neg_eap) { + warn("peer doesn't want to authenticate us with eap"); + lcp_close(unit, "couldn't negotiate eap"); + status = EXIT_PEER_AUTH_FAILED; + return; + } +#endif + new_phase(PHASE_AUTHENTICATE); auth = 0; if (go->neg_eap) { @@ -1278,6 +1333,15 @@ our_name, 1, &lacks_ip); } +#ifdef USE_EAPTLS + if (!can_auth && wo->neg_eap) { + can_auth = + have_eaptls_secret_server((explicit_remote ? remote_name : + NULL), our_name, 1, &lacks_ip); + + } +#endif + if (auth_required && !can_auth && noauth_addrs == NULL) { if (default_auth) { option_error( @@ -1332,7 +1396,11 @@ 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)); + have_srp_secret(user, (explicit_remote? remote_name: NULL), 0, NULL) +#ifdef USE_EAPTLS + || have_eaptls_secret_client(user, (explicit_remote? remote_name: NULL))) +#endif + ; hadchap = -1; if (go->neg_upap && !uselogin && !have_pap_secret(NULL)) @@ -1347,8 +1415,14 @@ !have_chap_secret((explicit_remote? remote_name: NULL), our_name, 1, NULL))) && !have_srp_secret((explicit_remote? remote_name: NULL), our_name, 1, - NULL)) + NULL) +#ifdef USE_EAPTLS + && !have_eaptls_secret_server((explicit_remote? remote_name: NULL), + our_name, 1, NULL) +#endif + ) go->neg_eap = 0; + } @@ -1706,6 +1780,7 @@ } + /* * get_secret - open the CHAP secret file and return the secret * for authenticating the given client on the given server. @@ -2358,3 +2433,335 @@ auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL, 0); } + + +#ifdef USE_EAPTLS +static int +have_eaptls_secret_server(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; + char servcertfile[MAXWORDLEN]; + char clicertfile[MAXWORDLEN]; + char cacertfile[MAXWORDLEN]; + char pkfile[MAXWORDLEN]; + + filename = _PATH_EAPTLSSERVFILE; + 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_eaptls(f, client, server, clicertfile, servcertfile, + cacertfile, pkfile, &addrs, NULL, filename, + 0); + + fclose(f); + +/* + if (ret >= 0 && !eaptls_init_ssl(1, cacertfile, servcertfile, + clicertfile, pkfile)) + ret = -1; +*/ + + 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; +} + + +static int +have_eaptls_secret_client(client, server) + char *client; + char *server; +{ + FILE *f; + int ret; + char *filename; + struct wordlist *addrs = NULL; + char servcertfile[MAXWORDLEN]; + char clicertfile[MAXWORDLEN]; + char cacertfile[MAXWORDLEN]; + char pkfile[MAXWORDLEN]; + + if (client != NULL && client[0] == 0) + client = NULL; + else if (server != NULL && server[0] == 0) + server = NULL; + + if (cacert_file && cert_file && privkey_file) + return 1; + + filename = _PATH_EAPTLSCLIFILE; + f = fopen(filename, "r"); + if (f == NULL) + return 0; + + ret = + scan_authfile_eaptls(f, client, server, clicertfile, servcertfile, + cacertfile, pkfile, &addrs, NULL, filename, + 0); + fclose(f); + +/* + if (ret >= 0 && !eaptls_init_ssl(0, cacertfile, clicertfile, + servcertfile, pkfile)) + ret = -1; +*/ + + if (addrs != 0) + free_wordlist(addrs); + + return ret >= 0; +} + + +static int +scan_authfile_eaptls(f, client, server, cli_cert, serv_cert, ca_cert, pk, + addrs, opts, filename, flags) + FILE *f; + char *client; + char *server; + char *cli_cert; + char *serv_cert; + char *ca_cert; + char *pk; + struct wordlist **addrs; + struct wordlist **opts; + char *filename; + int flags; +{ + int newline; + int got_flag, best_flag; + struct wordlist *ap, *addr_list, *alist, **app; + char word[MAXWORDLEN]; + + if (addrs != NULL) + *addrs = NULL; + if (opts != NULL) + *opts = NULL; + addr_list = NULL; + if (!getword(f, word, &newline, filename)) + return -1; /* file is empty??? */ + newline = 1; + best_flag = -1; + for (;;) { + /* + * Skip until we find a word at the start of a line. + */ + while (!newline && getword(f, word, &newline, filename)); + if (!newline) + break; /* got to end of file */ + + /* + * Got a client - check if it's a match or a wildcard. + */ + got_flag = 0; + if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) { + newline = 0; + continue; + } + if (!ISWILD(word)) + got_flag = NONWILD_CLIENT; + + /* + * Now get a server and check if it matches. + */ + if (!getword(f, word, &newline, filename)) + break; + if (newline) + continue; + if (!ISWILD(word)) { + if (server != NULL && strcmp(word, server) != 0) + continue; + got_flag |= NONWILD_SERVER; + } + + /* + * Got some sort of a match - see if it's better than what + * we have already. + */ + if (got_flag <= best_flag) + continue; + + /* + * Get the cli_cert + */ + if (!getword(f, word, &newline, filename)) + break; + if (newline) + continue; + if (strcmp(word, "-") != 0) { + strlcpy(cli_cert, word, MAXWORDLEN); + } else + cli_cert[0] = 0; + + /* + * Get serv_cert + */ + if (!getword(f, word, &newline, filename)) + break; + if (newline) + continue; + if (strcmp(word, "-") != 0) { + strlcpy(serv_cert, word, MAXWORDLEN); + } else + serv_cert[0] = 0; + + /* + * Get ca_cert + */ + if (!getword(f, word, &newline, filename)) + break; + if (newline) + continue; + strlcpy(ca_cert, word, MAXWORDLEN); + + /* + * Get pk + */ + if (!getword(f, word, &newline, filename)) + break; + if (newline) + continue; + strlcpy(pk, word, MAXWORDLEN); + + + /* + * Now read address authorization info and make a wordlist. + */ + app = &alist; + for (;;) { + if (!getword(f, word, &newline, filename) || newline) + break; + ap = (struct wordlist *) + malloc(sizeof(struct wordlist) + strlen(word) + 1); + if (ap == NULL) + novm("authorized addresses"); + ap->word = (char *) (ap + 1); + strcpy(ap->word, word); + *app = ap; + app = &ap->next; + } + *app = NULL; + /* + * This is the best so far; remember it. + */ + best_flag = got_flag; + if (addr_list) + free_wordlist(addr_list); + addr_list = alist; + + if (!newline) + break; + } + + /* scan for a -- word indicating the start of options */ + for (app = &addr_list; (ap = *app) != NULL; app = &ap->next) + if (strcmp(ap->word, "--") == 0) + break; + /* ap = start of options */ + if (ap != NULL) { + ap = ap->next; /* first option */ + free(*app); /* free the "--" word */ + *app = NULL; /* terminate addr list */ + } + if (opts != NULL) + *opts = ap; + else if (ap != NULL) + free_wordlist(ap); + if (addrs != NULL) + *addrs = addr_list; + else if (addr_list != NULL) + free_wordlist(addr_list); + + return best_flag; +} + + +int +get_eaptls_secret(unit, client, server, clicertfile, servcertfile, + cacertfile, pkfile, am_server) + int unit; + char *client; + char *server; + char *clicertfile; + char *servcertfile; + char *cacertfile; + char *pkfile; + int am_server; +{ + FILE *fp; + int ret; + char *filename = NULL; + struct wordlist *addrs = NULL; + struct wordlist *opts = NULL; + + /* in client mode the ca+cert+privkey can also be specified as options */ + if (!am_server && cacert_file && cert_file && privkey_file ) + { + strlcpy( clicertfile, cert_file, MAXWORDLEN ); + strlcpy( cacertfile, cacert_file, MAXWORDLEN ); + strlcpy( pkfile, privkey_file, MAXWORDLEN ); + servcertfile[0] = '\0'; + } + else + { + filename = (am_server ? _PATH_EAPTLSSERVFILE : _PATH_EAPTLSCLIFILE); + addrs = NULL; + + fp = fopen(filename, "r"); + if (fp == NULL) + { + error("Can't open eap-tls secret file %s: %m", filename); + return 0; + } + + check_access(fp, filename); + + ret = scan_authfile_eaptls(fp, client, server, clicertfile, servcertfile, + cacertfile, pkfile, &addrs, &opts, filename, 0); + + fclose(fp); + + if (ret < 0) return 0; + } + + if (eaptls_passwd_hook) + { + dbglog( "Calling eaptls password hook" ); + if ( (*eaptls_passwd_hook)(pkfile, passwd) < 0) + { + error("Unable to obtain EAP-TLS password for %s (%s) from plugin", + client, pkfile); + 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; +} +#endif + diff -Naur ppp-2.4.5/pppd/ccp.c ppp-2.4.5-eaptls-mppe-0.992/pppd/ccp.c --- ppp-2.4.5/pppd/ccp.c 2009-11-16 23:26:07.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/ccp.c 2011-12-01 15:14:39.078352226 +0100 @@ -540,6 +540,9 @@ if (go->mppe) { ccp_options *ao = &ccp_allowoptions[f->unit]; int auth_mschap_bits = auth_done[f->unit]; +#ifdef USE_EAPTLS + int auth_eap_bits = auth_done[f->unit]; +#endif int numbits; /* @@ -567,8 +570,23 @@ lcp_close(f->unit, "MPPE required but not available"); return; } + +#ifdef USE_EAPTLS + /* + * MPPE is also possible in combination with EAP-TLS. + * It is not possible to detect if we're doing EAP or EAP-TLS + * at this stage, hence we accept all forms of EAP. If TLS is + * not used then the MPPE keys will not be derived anyway. + */ + /* Leave only the eap auth bits set */ + auth_eap_bits &= (EAP_WITHPEER | EAP_PEER ); + + if ((numbits == 0) && (auth_eap_bits == 0)) { + error("MPPE required, but MS-CHAP[v2] nor EAP-TLS auth are performed."); +#else if (!numbits) { - error("MPPE required, but MS-CHAP[v2] auth not performed."); + error("MPPE required, but MS-CHAP[v2] auth not performed."); +#endif lcp_close(f->unit, "MPPE required but not available"); return; } diff -Naur ppp-2.4.5/pppd/chap-md5.c ppp-2.4.5-eaptls-mppe-0.992/pppd/chap-md5.c --- ppp-2.4.5/pppd/chap-md5.c 2009-11-16 23:26:07.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/chap-md5.c 2011-12-01 15:14:39.079352354 +0100 @@ -36,7 +36,11 @@ #include "chap-new.h" #include "chap-md5.h" #include "magic.h" +#ifdef USE_EAPTLS +#include "eap-tls.h" +#else #include "md5.h" +#endif /* USE_EAPTLS */ #define MD5_HASH_SIZE 16 #define MD5_MIN_CHALLENGE 16 diff -Naur ppp-2.4.5/pppd/eap-tls.c ppp-2.4.5-eaptls-mppe-0.992/pppd/eap-tls.c --- ppp-2.4.5/pppd/eap-tls.c 1970-01-01 01:00:00.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/eap-tls.c 2011-12-01 15:14:39.080352482 +0100 @@ -0,0 +1,1174 @@ +/* + * eap-tls.c - EAP-TLS implementation for PPP + * + * Copyright (c) Beniamino Galvani 2005 All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "pppd.h" +#include "eap.h" +#include "eap-tls.h" +#include "fsm.h" +#include "lcp.h" +#include "pathnames.h" + +/* The openssl configuration file and engines can be loaded only once */ +static CONF *ssl_config = NULL; +static ENGINE *cert_engine = NULL; +static ENGINE *pkey_engine = NULL; + +#ifdef MPPE + +/* + * TLS PRF from RFC 2246 + */ +static void P_hash(const EVP_MD *evp_md, + const unsigned char *secret, unsigned int secret_len, + const unsigned char *seed, unsigned int seed_len, + unsigned char *out, unsigned int out_len) +{ + HMAC_CTX ctx_a, ctx_out; + unsigned char a[HMAC_MAX_MD_CBLOCK]; + unsigned int size; + + HMAC_CTX_init(&ctx_a); + HMAC_CTX_init(&ctx_out); + HMAC_Init_ex(&ctx_a, secret, secret_len, evp_md, NULL); + HMAC_Init_ex(&ctx_out, secret, secret_len, evp_md, NULL); + + size = HMAC_size(&ctx_out); + + /* Calculate A(1) */ + HMAC_Update(&ctx_a, seed, seed_len); + HMAC_Final(&ctx_a, a, NULL); + + while (1) { + /* Calculate next part of output */ + HMAC_Update(&ctx_out, a, size); + HMAC_Update(&ctx_out, seed, seed_len); + + /* Check if last part */ + if (out_len < size) { + HMAC_Final(&ctx_out, a, NULL); + memcpy(out, a, out_len); + break; + } + + /* Place digest in output buffer */ + HMAC_Final(&ctx_out, out, NULL); + HMAC_Init_ex(&ctx_out, NULL, 0, NULL, NULL); + out += size; + out_len -= size; + + /* Calculate next A(i) */ + HMAC_Init_ex(&ctx_a, NULL, 0, NULL, NULL); + HMAC_Update(&ctx_a, a, size); + HMAC_Final(&ctx_a, a, NULL); + } + + HMAC_CTX_cleanup(&ctx_a); + HMAC_CTX_cleanup(&ctx_out); + memset(a, 0, sizeof(a)); +} + +static void PRF(const unsigned char *secret, unsigned int secret_len, + const unsigned char *seed, unsigned int seed_len, + unsigned char *out, unsigned char *buf, unsigned int out_len) +{ + unsigned int i; + unsigned int len = (secret_len + 1) / 2; + const unsigned char *s1 = secret; + const unsigned char *s2 = secret + (secret_len - len); + + P_hash(EVP_md5(), s1, len, seed, seed_len, out, out_len); + P_hash(EVP_sha1(), s2, len, seed, seed_len, buf, out_len); + + for (i=0; i < out_len; i++) { + out[i] ^= buf[i]; + } +} + +#define EAPTLS_MPPE_KEY_LEN 32 + +/* + * Generate keys according to RFC 2716 and add to reply + */ +void eaptls_gen_mppe_keys(struct eaptls_session *ets, const char *prf_label, + int client) +{ + unsigned char out[4*EAPTLS_MPPE_KEY_LEN], buf[4*EAPTLS_MPPE_KEY_LEN]; + unsigned char seed[64 + 2*SSL3_RANDOM_SIZE]; + unsigned char *p = seed; + SSL *s = ets->ssl; + size_t prf_size; + + prf_size = strlen(prf_label); + + memcpy(p, prf_label, prf_size); + p += prf_size; + + memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + prf_size += SSL3_RANDOM_SIZE; + + memcpy(p, s->s3->server_random, SSL3_RANDOM_SIZE); + prf_size += SSL3_RANDOM_SIZE; + + PRF(s->session->master_key, s->session->master_key_length, + seed, prf_size, out, buf, sizeof(out)); + + /* + * We now have the master send and receive keys. + * From these, generate the session send and receive keys. + * (see RFC3079 / draft-ietf-pppext-mppe-keys-03.txt for details) + */ + if (client) + { + p = out; + BCOPY( p, mppe_send_key, sizeof(mppe_send_key) ); + p += EAPTLS_MPPE_KEY_LEN; + BCOPY( p, mppe_recv_key, sizeof(mppe_recv_key) ); + } + else + { + p = out; + BCOPY( p, mppe_recv_key, sizeof(mppe_recv_key) ); + p += EAPTLS_MPPE_KEY_LEN; + BCOPY( p, mppe_send_key, sizeof(mppe_send_key) ); + } + + mppe_keys_set = 1; +} + +#endif + +void log_ssl_errors( void ) +{ + unsigned long ssl_err = ERR_get_error(); + + if (ssl_err != 0) + dbglog("EAP-TLS SSL error stack:"); + while (ssl_err != 0) { + dbglog( ERR_error_string( ssl_err, NULL ) ); + ssl_err = ERR_get_error(); + } +} + + +int password_callback (char *buf, int size, int rwflag, void *u) +{ + if (buf) + { + strncpy (buf, passwd, size); + return strlen (buf); + } + return 0; +} + + +CONF *eaptls_ssl_load_config( void ) +{ + CONF *config; + int ret_code; + long error_line = 33; + + config = NCONF_new( NULL ); + dbglog( "Loading OpenSSL config file" ); + ret_code = NCONF_load( config, _PATH_OPENSSLCONFFILE, &error_line ); + if (ret_code == 0) + { + warn( "EAP-TLS: Error in OpenSSL config file %s at line %d", _PATH_OPENSSLCONFFILE, error_line ); + NCONF_free( config ); + config = NULL; + ERR_clear_error(); + } + + dbglog( "Loading OpenSSL built-ins" ); + ENGINE_load_builtin_engines(); + OPENSSL_load_builtin_modules(); + + dbglog( "Loading OpenSSL configured modules" ); + if (CONF_modules_load( config, NULL, 0 ) <= 0 ) + { + warn( "EAP-TLS: Error loading OpenSSL modules" ); + log_ssl_errors(); + config = NULL; + } + + return config; +} + +ENGINE *eaptls_ssl_load_engine( char *engine_name ) +{ + ENGINE *e = NULL; + + dbglog( "Enabling OpenSSL auto engines" ); + ENGINE_register_all_complete(); + + dbglog( "Loading OpenSSL '%s' engine support", engine_name ); + e = ENGINE_by_id( engine_name ); + if (!e) + { + dbglog( "EAP-TLS: Cannot load '%s' engine support, trying 'dynamic'", engine_name ); + e = ENGINE_by_id( "dynamic" ); + if (e) + { + if (!ENGINE_ctrl_cmd_string(e, "SO_PATH", engine_name, 0) + || !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) + { + warn( "EAP-TLS: Error loading dynamic engine '%s'", engine_name ); + log_ssl_errors(); + ENGINE_free(e); + e = NULL; + } + } + else + { + warn( "EAP-TLS: Cannot load dynamic engine support" ); + } + } + + if (e) + { + dbglog( "Initialising engine" ); + if(!ENGINE_set_default(e, ENGINE_METHOD_ALL)) + { + warn( "EAP-TLS: Cannot use that engine" ); + log_ssl_errors(); + ENGINE_free(e); + e = NULL; + } + } + + return e; +} + +/* + * Initialize the SSL stacks and tests if certificates, key and crl + * for client or server use can be loaded. + */ +SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, + char *certfile, char *peer_certfile, char *privkeyfile) +{ + char *cert_engine_name = NULL; + char *cert_identifier = NULL; + char *pkey_engine_name = NULL; + char *pkey_identifier = NULL; + SSL_CTX *ctx; + X509_STORE *certstore; + X509_LOOKUP *lookup; + X509 *tmp; + + /* + * Without these can't continue + */ + if (!cacertfile[0]) + { + error("EAP-TLS: CA certificate missing"); + return NULL; + } + + if (!certfile[0]) + { + error("EAP-TLS: User certificate missing"); + return NULL; + } + + if (!privkeyfile[0]) + { + error("EAP-TLS: User private key missing"); + return NULL; + } + + SSL_library_init(); + SSL_load_error_strings(); + + ctx = SSL_CTX_new(TLSv1_method()); + + if (!ctx) { + error("EAP-TLS: Cannot initialize SSL CTX context"); + goto fail; + } + + /* if the certificate filename is of the form engine:id. e.g. + pkcs11:12345 + then we try to load and use this engine. + If the certificate filename starts with a / or . then we + ALWAYS assume it is a file and not an engine/pkcs11 identifier + */ + if ( index( certfile, '/' ) == NULL && index( certfile, '.') == NULL ) + { + cert_identifier = index( certfile, ':' ); + + if (cert_identifier) + { + cert_engine_name = certfile; + *cert_identifier = '\0'; + cert_identifier++; + + dbglog( "Found certificate engine '%s'", cert_engine_name ); + dbglog( "Found certificate identifier '%s'", cert_identifier ); + } + } + + /* if the privatekey filename is of the form engine:id. e.g. + pkcs11:12345 + then we try to load and use this engine. + If the privatekey filename starts with a / or . then we + ALWAYS assume it is a file and not an engine/pkcs11 identifier + */ + if ( index( privkeyfile, '/' ) == NULL && index( privkeyfile, '.') == NULL ) + { + pkey_identifier = index( privkeyfile, ':' ); + + if (pkey_identifier) + { + pkey_engine_name = privkeyfile; + *pkey_identifier = '\0'; + pkey_identifier++; + + dbglog( "Found privatekey engine '%s'", pkey_engine_name ); + dbglog( "Found privatekey identifier '%s'", pkey_identifier ); + } + } + + if (cert_identifier && pkey_identifier) + { + if (strlen( cert_identifier ) == 0) + { + if (strlen( pkey_identifier ) == 0) + error( "EAP-TLS: both the certificate and privatekey identifiers are missing!" ); + else + { + dbglog( "Substituting privatekey identifier for certificate identifier" ); + cert_identifier = pkey_identifier; + } + } + else + { + if (strlen( pkey_identifier ) == 0) + { + dbglog( "Substituting certificate identifier for privatekey identifier" ); + pkey_identifier = cert_identifier; + } + } + + } + + /* load the openssl config file only once */ + if (!ssl_config) + { + if (cert_engine_name || pkey_engine_name) + ssl_config = eaptls_ssl_load_config(); + + if (ssl_config && cert_engine_name) + cert_engine = eaptls_ssl_load_engine( cert_engine_name ); + + if (ssl_config && pkey_engine_name) + { + /* don't load the same engine twice */ + if ( strcmp( cert_engine_name, pkey_engine_name) == 0 ) + pkey_engine = cert_engine; + else + pkey_engine = eaptls_ssl_load_engine( pkey_engine_name ); + } + } + + SSL_CTX_set_default_passwd_cb (ctx, password_callback); + + if (!SSL_CTX_load_verify_locations(ctx, cacertfile, NULL)) + { + error("EAP-TLS: Cannot load or verify CA file %s", cacertfile); + goto fail; + } + + if (init_server) + SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(cacertfile)); + + if (cert_engine) + { + struct + { + const char *s_slot_cert_id; + X509 *cert; + } cert_info; + + cert_info.s_slot_cert_id = cert_identifier; + cert_info.cert = NULL; + + if (!ENGINE_ctrl_cmd( cert_engine, "LOAD_CERT_CTRL", 0, &cert_info, NULL, 0 ) ) + { + error( "EAP-TLS: Error loading certificate with id '%s' from engine", cert_identifier ); + goto fail; + } + + if (cert_info.cert) + { + dbglog( "Got the certificate, adding it to SSL context" ); + dbglog( "subject = %s", X509_NAME_oneline( X509_get_subject_name( cert_info.cert ), NULL, 0 ) ); + if (SSL_CTX_use_certificate(ctx, cert_info.cert) <= 0) + { + error("EAP-TLS: Cannot use PKCS11 certificate %s", cert_identifier); + goto fail; + } + } + else + { + warn("EAP-TLS: Cannot load PKCS11 key %s", cert_identifier); + log_ssl_errors(); + } + } + else + { + if (!SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM)) + { + error( "EAP-TLS: Cannot use public certificate %s", certfile ); + goto fail; + } + } + + if (pkey_engine) + { + EVP_PKEY *pkey = NULL; + PW_CB_DATA cb_data; + + cb_data.password = passwd; + cb_data.prompt_info = pkey_identifier; + + dbglog( "Loading private key '%s' from engine", pkey_identifier ); + pkey = ENGINE_load_private_key(pkey_engine, pkey_identifier, NULL, &cb_data); + if (pkey) + { + dbglog( "Got the private key, adding it to SSL context" ); + if (SSL_CTX_use_PrivateKey(ctx, pkey) <= 0) + { + error("EAP-TLS: Cannot use PKCS11 key %s", pkey_identifier); + goto fail; + } + } + else + { + warn("EAP-TLS: Cannot load PKCS11 key %s", pkey_identifier); + log_ssl_errors(); + } + } + else + { + if (!SSL_CTX_use_PrivateKey_file(ctx, privkeyfile, SSL_FILETYPE_PEM)) + { + error("EAP-TLS: Cannot use private key %s", privkeyfile); + goto fail; + } + } + + if (SSL_CTX_check_private_key(ctx) != 1) { + error("EAP-TLS: Private key %s fails security check", privkeyfile); + goto fail; + } + + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + SSL_CTX_set_verify_depth(ctx, 5); + SSL_CTX_set_verify(ctx, + SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + &ssl_verify_callback); + + if (crl_dir) { + if (!(certstore = SSL_CTX_get_cert_store(ctx))) { + error("EAP-TLS: Failed to get certificate store"); + goto fail; + } + + if (!(lookup = + X509_STORE_add_lookup(certstore, X509_LOOKUP_hash_dir()))) { + error("EAP-TLS: Store lookup for CRL failed"); + + goto fail; + } + + X509_LOOKUP_add_dir(lookup, crl_dir, X509_FILETYPE_PEM); + X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK); + } + + /* + * If a peer certificate file was specified, it must be valid, else fail + */ + if (peer_certfile[0]) { + if (!(tmp = get_X509_from_file(peer_certfile))) { + error("EAP-TLS: Error loading client certificate from file %s", + peer_certfile); + goto fail; + } + X509_free(tmp); + } + + return ctx; + +fail: + log_ssl_errors(); + SSL_CTX_free(ctx); + return NULL; +} + +/* + * Determine the maximum packet size by looking at the LCP handshake + */ + +int eaptls_get_mtu(int unit) +{ + int mtu, mru; + + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *go = &lcp_gotoptions[unit]; + lcp_options *ho = &lcp_hisoptions[unit]; + lcp_options *ao = &lcp_allowoptions[unit]; + + mtu = ho->neg_mru? ho->mru: PPP_MRU; + mru = go->neg_mru? MAX(wo->mru, go->mru): PPP_MRU; + mtu = MIN(MIN(mtu, mru), ao->mru)- PPP_HDRLEN - 10; + + dbglog("MTU = %d", mtu); + return mtu; +} + + +/* + * Init the ssl handshake (server mode) + */ +int eaptls_init_ssl_server(eap_state * esp) +{ + struct eaptls_session *ets; + char servcertfile[MAXWORDLEN]; + char clicertfile[MAXWORDLEN]; + char cacertfile[MAXWORDLEN]; + char pkfile[MAXWORDLEN]; + /* + * Allocate new eaptls session + */ + esp->es_server.ea_session = malloc(sizeof(struct eaptls_session)); + if (!esp->es_server.ea_session) + fatal("Allocation error"); + ets = esp->es_server.ea_session; + + if (!esp->es_server.ea_peer) { + error("EAP-TLS: Error: client name not set (BUG)"); + return 0; + } + + strncpy(ets->peer, esp->es_server.ea_peer, MAXWORDLEN); + + dbglog( "getting eaptls secret" ); + if (!get_eaptls_secret(esp->es_unit, esp->es_server.ea_peer, + esp->es_server.ea_name, clicertfile, + servcertfile, cacertfile, pkfile, 1)) { + error( "EAP-TLS: Cannot get secret/password for client \"%s\", server \"%s\"", + esp->es_server.ea_peer, esp->es_server.ea_name ); + return 0; + } + + ets->mtu = eaptls_get_mtu(esp->es_unit); + + ets->ctx = eaptls_init_ssl(1, cacertfile, servcertfile, clicertfile, pkfile); + if (!ets->ctx) + goto fail; + + if (!(ets->ssl = SSL_new(ets->ctx))) + goto fail; + + /* + * Set auto-retry to avoid timeouts on BIO_read + */ + SSL_set_mode(ets->ssl, SSL_MODE_AUTO_RETRY); + + /* + * Initialize the BIOs we use to read/write to ssl engine + */ + ets->into_ssl = BIO_new(BIO_s_mem()); + ets->from_ssl = BIO_new(BIO_s_mem()); + SSL_set_bio(ets->ssl, ets->into_ssl, ets->from_ssl); + + SSL_set_msg_callback(ets->ssl, ssl_msg_callback); + SSL_set_msg_callback_arg(ets->ssl, ets); + + /* + * Attach the session struct to the connection, so we can later + * retrieve it when doing certificate verification + */ + SSL_set_ex_data(ets->ssl, 0, ets); + + SSL_set_accept_state(ets->ssl); + + ets->data = NULL; + ets->datalen = 0; + ets->alert_sent = 0; + ets->alert_recv = 0; + + /* + * If we specified the client certificate file, store it in ets->peercertfile, + * so we can check it later in ssl_verify_callback() + */ + if (clicertfile[0]) + strncpy(&ets->peercertfile[0], clicertfile, MAXWORDLEN); + else + ets->peercertfile[0] = 0; + + return 1; + +fail: + SSL_CTX_free(ets->ctx); + return 0; +} + +/* + * Init the ssl handshake (client mode) + */ +int eaptls_init_ssl_client(eap_state * esp) +{ + struct eaptls_session *ets; + char servcertfile[MAXWORDLEN]; + char clicertfile[MAXWORDLEN]; + char cacertfile[MAXWORDLEN]; + char pkfile[MAXWORDLEN]; + + /* + * Allocate new eaptls session + */ + esp->es_client.ea_session = malloc(sizeof(struct eaptls_session)); + if (!esp->es_client.ea_session) + fatal("Allocation error"); + ets = esp->es_client.ea_session; + + /* + * If available, copy server name in ets; it will be used in cert + * verify + */ + if (esp->es_client.ea_peer) + strncpy(ets->peer, esp->es_client.ea_peer, MAXWORDLEN); + else + ets->peer[0] = 0; + + ets->mtu = eaptls_get_mtu(esp->es_unit); + + dbglog( "calling get_eaptls_secret" ); + if (!get_eaptls_secret(esp->es_unit, esp->es_client.ea_name, + esp->es_client.ea_peer, clicertfile, + servcertfile, cacertfile, pkfile, 0)) { + error( "EAP-TLS: Cannot get secret/password for client \"%s\", server \"%s\"", + esp->es_client.ea_name, esp->es_client.ea_peer ); + return 0; + } + + dbglog( "calling eaptls_init_ssl" ); + ets->ctx = eaptls_init_ssl(0, cacertfile, clicertfile, servcertfile, pkfile); + if (!ets->ctx) + goto fail; + + ets->ssl = SSL_new(ets->ctx); + + if (!ets->ssl) + goto fail; + + /* + * Initialize the BIOs we use to read/write to ssl engine + */ + dbglog( "Initializing SSL BIOs" ); + ets->into_ssl = BIO_new(BIO_s_mem()); + ets->from_ssl = BIO_new(BIO_s_mem()); + SSL_set_bio(ets->ssl, ets->into_ssl, ets->from_ssl); + + SSL_set_msg_callback(ets->ssl, ssl_msg_callback); + SSL_set_msg_callback_arg(ets->ssl, ets); + + /* + * Attach the session struct to the connection, so we can later + * retrieve it when doing certificate verification + */ + SSL_set_ex_data(ets->ssl, 0, ets); + + SSL_set_connect_state(ets->ssl); + + ets->data = NULL; + ets->datalen = 0; + ets->alert_sent = 0; + ets->alert_recv = 0; + + /* + * If we specified the server certificate file, store it in + * ets->peercertfile, so we can check it later in + * ssl_verify_callback() + */ + if (servcertfile[0]) + strncpy(ets->peercertfile, servcertfile, MAXWORDLEN); + else + ets->peercertfile[0] = 0; + + return 1; + +fail: + dbglog( "eaptls_init_ssl_client: fail" ); + SSL_CTX_free(ets->ctx); + return 0; + +} + +void eaptls_free_session(struct eaptls_session *ets) +{ + if (ets->ssl) + SSL_free(ets->ssl); + + if (ets->ctx) + SSL_CTX_free(ets->ctx); + + free(ets); +} + +/* + * Handle a received packet, reassembling fragmented messages and + * passing them to the ssl engine + */ +int eaptls_receive(struct eaptls_session *ets, u_char * inp, int len) +{ + u_char flags; + u_int tlslen; + u_char dummy[65536]; + + GETCHAR(flags, inp); + len--; + + if (flags & EAP_TLS_FLAGS_LI && !ets->data) { + + /* + * This is the first packet of a message + */ + + GETLONG(tlslen, inp); + len -= 4; + + if (tlslen > EAP_TLS_MAX_LEN) { + error("Error: tls message length > %d, truncated", + EAP_TLS_MAX_LEN); + tlslen = EAP_TLS_MAX_LEN; + } + + /* + * Allocate memory for the whole message + */ + ets->data = malloc(tlslen); + if (!ets->data) + fatal("EAP TLS: allocation error\n"); + + ets->datalen = 0; + ets->tlslen = tlslen; + + } + else if (flags & EAP_TLS_FLAGS_LI && ets->data) { + /* + * Non first with LI (strange...) + */ + + GETLONG(tlslen, inp); + len -= 4; + + } + else if (!ets->data) { + /* + * A non fragmented message without LI flag + */ + + ets->data = malloc(len); + if (!ets->data) + fatal("EAP TLS: allocation error\n"); + + ets->datalen = 0; + ets->tlslen = len; + } + + if (flags & EAP_TLS_FLAGS_MF) + ets->frag = 1; + else + ets->frag = 0; + + if (len + ets->datalen > ets->tlslen) { + warn("EAP TLS: received data > TLS message length"); + return 1; + } + + BCOPY(inp, ets->data + ets->datalen, len); + ets->datalen += len; + + if (!ets->frag) { + + /* + * If we have the whole message, pass it to ssl + */ + + if (ets->datalen != ets->tlslen) { + warn("EAP TLS: received data != TLS message length"); + return 1; + } + + if (BIO_write(ets->into_ssl, ets->data, ets->datalen) == -1) + log_ssl_errors(); + + SSL_read(ets->ssl, dummy, 65536); + + free(ets->data); + ets->data = NULL; + ets->datalen = 0; + } + + return 0; +} + +/* + * Return an eap-tls packet in outp. + * A TLS message read from the ssl engine is buffered in ets->data. + * At each call we control if there is buffered data and send a + * packet of mtu bytes. + */ +int eaptls_send(struct eaptls_session *ets, u_char ** outp) +{ + bool first = 0; + int size; + u_char fromtls[65536]; + int res; + u_char *start; + + start = *outp; + + if (!ets->data) { + + if(!ets->alert_sent) + SSL_read(ets->ssl, fromtls, 65536); + + /* + * Read from ssl + */ + if ((res = BIO_read(ets->from_ssl, fromtls, 65536)) == -1) + fatal("No data from BIO_read"); + + ets->datalen = res; + + ets->data = malloc(ets->datalen); + BCOPY(fromtls, ets->data, ets->datalen); + + ets->offset = 0; + first = 1; + + } + + size = ets->datalen - ets->offset; + + if (size > ets->mtu) { + size = ets->mtu; + ets->frag = 1; + } else + ets->frag = 0; + + PUTCHAR(EAPT_TLS, *outp); + + /* + * Set right flags and length if necessary + */ + if (ets->frag && first) { + PUTCHAR(EAP_TLS_FLAGS_LI | EAP_TLS_FLAGS_MF, *outp); + PUTLONG(ets->datalen, *outp); + } else if (ets->frag) { + PUTCHAR(EAP_TLS_FLAGS_MF, *outp); + } else + PUTCHAR(0, *outp); + + /* + * Copy the data in outp + */ + BCOPY(ets->data + ets->offset, *outp, size); + INCPTR(size, *outp); + + /* + * Copy the packet in retransmission buffer + */ + BCOPY(start, &ets->rtx[0], *outp - start); + ets->rtx_len = *outp - start; + + ets->offset += size; + + if (ets->offset >= ets->datalen) { + + /* + * The whole message has been sent + */ + + free(ets->data); + ets->data = NULL; + ets->datalen = 0; + ets->offset = 0; + } + + return 0; +} + +/* + * Get the sent packet from the retransmission buffer + */ +void eaptls_retransmit(struct eaptls_session *ets, u_char ** outp) +{ + BCOPY(ets->rtx, *outp, ets->rtx_len); + INCPTR(ets->rtx_len, *outp); +} + +/* + * Verify a certificate. + * Most of the work (signatures and issuer attributes checking) + * is done by ssl; we check the CN in the peer certificate + * against the peer name. + */ +int ssl_verify_callback(int preverify_ok, X509_STORE_CTX * ctx) +{ + char subject[256]; + char cn_str[256]; + X509 *peer_cert; + int err, depth; + int ok = preverify_ok; + SSL *ssl; + struct eaptls_session *ets; + + peer_cert = X509_STORE_CTX_get_current_cert(ctx); + err = X509_STORE_CTX_get_error(ctx); + depth = X509_STORE_CTX_get_error_depth(ctx); + + dbglog("certificate verify depth: %d", depth); + + if (auth_required && !ok) { + X509_NAME_oneline(X509_get_subject_name(peer_cert), + subject, 256); + + X509_NAME_get_text_by_NID(X509_get_subject_name(peer_cert), + NID_commonName, cn_str, 256); + + dbglog("Certificate verification error:\n depth: %d CN: %s" + "\n err: %d (%s)\n", depth, cn_str, err, + X509_verify_cert_error_string(err)); + + return 0; + } + + ssl = X509_STORE_CTX_get_ex_data(ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + + ets = (struct eaptls_session *)SSL_get_ex_data(ssl, 0); + + if (ets == NULL) { + error("Error: SSL_get_ex_data returned NULL"); + return 0; + } + + log_ssl_errors(); + + if (!depth) { /* This is the peer certificate */ + + X509_NAME_oneline(X509_get_subject_name(peer_cert), + subject, 256); + + X509_NAME_get_text_by_NID(X509_get_subject_name(peer_cert), + NID_commonName, cn_str, 256); + + /* + * If acting as client and the name of the server wasn't specified + * explicitely, we can't verify the server authenticity + */ + if (!ets->peer[0]) { + warn("Peer name not specified: no check"); + return 1; + } + + /* + * Check the CN + */ + if (strcmp(cn_str, ets->peer)) { + error + ("Certificate verification error: CN (%s) != peer_name (%s)", + cn_str, ets->peer); + return 0; + } + + warn("Certificate CN: %s , peer name %s", cn_str, ets->peer); + + /* + * If a peer certificate file was specified, here we check it + */ + if (ets->peercertfile[0]) { + if (ssl_cmp_certs(&ets->peercertfile[0], peer_cert) + != 0) { + error + ("Peer certificate doesn't match stored certificate"); + return 0; + } + } + } + + return 1; +} + +/* + * Compare a certificate with the one stored in a file + */ +int ssl_cmp_certs(char *filename, X509 * a) +{ + X509 *b; + int ret; + + if (!(b = get_X509_from_file(filename))) + return 1; + + ret = X509_cmp(a, b); + X509_free(b); + + return ret; + +} + +X509 *get_X509_from_file(char *filename) +{ + FILE *fp; + X509 *ret; + + if (!(fp = fopen(filename, "r"))) + return NULL; + + ret = PEM_read_X509(fp, NULL, NULL, NULL); + + fclose(fp); + + return ret; +} + +/* + * Every sent & received message this callback function is invoked, + * so we know when alert messages have arrived or are sent and + * we can print debug information about TLS handshake. + */ +void +ssl_msg_callback(int write_p, int version, int content_type, + const void *buf, size_t len, SSL * ssl, void *arg) +{ + char string[256]; + struct eaptls_session *ets = (struct eaptls_session *)arg; + unsigned char code; + + if(write_p) + strcpy(string, " -> "); + else + strcpy(string, " <- "); + + + switch(content_type) { + + case SSL3_RT_ALERT: + strcat(string, "Alert: "); + code = ((const unsigned char *)buf)[1]; + + if (write_p) { + ets->alert_sent = 1; + ets->alert_sent_desc = code; + } else { + ets->alert_recv = 1; + ets->alert_recv_desc = code; + } + + strcat(string, SSL_alert_desc_string_long(code)); + break; + + case SSL3_RT_CHANGE_CIPHER_SPEC: + strcat(string, "ChangeCipherSpec"); + break; + + case SSL3_RT_HANDSHAKE: + + strcat(string, "Handshake: "); + code = ((const unsigned char *)buf)[0]; + + switch(code) { + case SSL3_MT_HELLO_REQUEST: + strcat(string,"Hello Request"); + break; + case SSL3_MT_CLIENT_HELLO: + strcat(string,"Client Hello"); + break; + case SSL3_MT_SERVER_HELLO: + strcat(string,"Server Hello"); + break; + case SSL3_MT_CERTIFICATE: + strcat(string,"Certificate"); + break; + case SSL3_MT_SERVER_KEY_EXCHANGE: + strcat(string,"Server Key Exchange"); + break; + case SSL3_MT_CERTIFICATE_REQUEST: + strcat(string,"Certificate Request"); + break; + case SSL3_MT_SERVER_DONE: + strcat(string,"Server Hello Done"); + break; + case SSL3_MT_CERTIFICATE_VERIFY: + strcat(string,"Certificate Verify"); + break; + case SSL3_MT_CLIENT_KEY_EXCHANGE: + strcat(string,"Client Key Exchange"); + break; + case SSL3_MT_FINISHED: + strcat(string,"Finished"); + break; + + default: + sprintf( string, "Handshake: Unknown SSL3 code received: %d", code ); + } + break; + + default: + sprintf( string, "SSL message contains unknown content type: %d", content_type ); + + } + + /* Alert messages must always be displayed */ + if(content_type == SSL3_RT_ALERT) + error("%s", string); + else + dbglog("%s", string); +} + diff -Naur ppp-2.4.5/pppd/eap-tls.h ppp-2.4.5-eaptls-mppe-0.992/pppd/eap-tls.h --- ppp-2.4.5/pppd/eap-tls.h 1970-01-01 01:00:00.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/eap-tls.h 2011-12-01 15:14:39.081352611 +0100 @@ -0,0 +1,107 @@ +/* + * eap-tls.h + * + * Copyright (c) Beniamino Galvani 2005 All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef __EAP_TLS_H__ +#define __EAP_TLS_H__ + +#include "eap.h" + +#include +#include +#include + +#define EAP_TLS_FLAGS_LI 128 /* length included flag */ +#define EAP_TLS_FLAGS_MF 64 /* more fragments flag */ +#define EAP_TLS_FLAGS_START 32 /* start flag */ + +#define EAP_TLS_MAX_LEN 65536 /* max eap tls packet size */ + +struct eaptls_session +{ + u_char *data; /* buffered data */ + int datalen; /* buffered data len */ + int offset; /* from where to send */ + int tlslen; /* total length of tls data */ + bool frag; /* packet is fragmented */ + SSL_CTX *ctx; + SSL *ssl; /* ssl connection */ + BIO *from_ssl; + BIO *into_ssl; + char peer[MAXWORDLEN]; /* peer name */ + char peercertfile[MAXWORDLEN]; + bool alert_sent; + u_char alert_sent_desc; + bool alert_recv; + u_char alert_recv_desc; + char rtx[65536]; /* retransmission buffer */ + int rtx_len; + int mtu; /* unit mtu */ +}; + +typedef struct pw_cb_data +{ + const void *password; + const char *prompt_info; +} PW_CB_DATA; + + +int ssl_verify_callback(int, X509_STORE_CTX *); +void ssl_msg_callback(int write_p, int version, int ct, const void *buf, + size_t len, SSL * ssl, void *arg); + +X509 *get_X509_from_file(char *filename); +int ssl_cmp_certs(char *filename, X509 * a); + +SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, + char *certfile, char *peer_certfile, char *privkeyfile); +int eaptls_init_ssl_server(eap_state * esp); +int eaptls_init_ssl_client(eap_state * esp); +void eaptls_free_session(struct eaptls_session *ets); + +int eaptls_receive(struct eaptls_session *ets, u_char * inp, int len); +int eaptls_send(struct eaptls_session *ets, u_char ** outp); +void eaptls_retransmit(struct eaptls_session *ets, u_char ** outp); + +int get_eaptls_secret(int unit, char *client, char *server, + char *clicertfile, char *servcertfile, char *cacertfile, + char *pkfile, int am_server); + +#ifdef MPPE +#include "mppe.h" /* MPPE_MAX_KEY_LEN */ +extern u_char mppe_send_key[MPPE_MAX_KEY_LEN]; +extern u_char mppe_recv_key[MPPE_MAX_KEY_LEN]; +extern int mppe_keys_set; + +void eaptls_gen_mppe_keys(struct eaptls_session *ets, const char *prf_label, int client); + +#endif + +#endif diff -Naur ppp-2.4.5/pppd/eap.c ppp-2.4.5-eaptls-mppe-0.992/pppd/eap.c --- ppp-2.4.5/pppd/eap.c 2009-11-16 23:26:07.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/eap.c 2011-12-01 15:14:39.083352869 +0100 @@ -43,6 +43,11 @@ * Based on draft-ietf-pppext-eap-srp-03.txt. */ +/* + * Modification by Beniamino Galvani, Mar 2005 + * Implemented EAP-TLS authentication + */ + #define RCSID "$Id: eap.c,v 1.4 2004/11/09 22:39:25 paulus Exp $" /* @@ -62,8 +67,12 @@ #include "pppd.h" #include "pathnames.h" -#include "md5.h" #include "eap.h" +#ifdef USE_EAPTLS +#include "eap-tls.h" +#else +#include "md5.h" +#endif /* USE_EAPTLS */ #ifdef USE_SRP #include @@ -209,6 +218,9 @@ esp->es_server.ea_id = (u_char)(drand48() * 0x100); esp->es_client.ea_timeout = EAP_DEFREQTIME; esp->es_client.ea_maxrequests = EAP_DEFALLOWREQ; +#ifdef USE_EAPTLS + esp->es_client.ea_using_eaptls = 0; +#endif /* USE_EAPTLS */ } /* @@ -436,8 +448,16 @@ u_char vals[2]; struct b64state bs; #endif /* USE_SRP */ +#ifdef USE_EAPTLS + struct eaptls_session *ets; + int secret_len; + char secret[MAXWORDLEN]; +#endif /* USE_EAPTLS */ esp->es_server.ea_timeout = esp->es_savedtime; +#ifdef USE_EAPTLS + esp->es_server.ea_prev_state = esp->es_server.ea_state; +#endif /* USE_EAPTLS */ switch (esp->es_server.ea_state) { case eapBadAuth: return; @@ -562,9 +582,81 @@ break; } #endif /* USE_SRP */ +#ifdef USE_EAPTLS + if (!get_secret(esp->es_unit, esp->es_server.ea_peer, + esp->es_server.ea_name, secret, &secret_len, 1)) { + + esp->es_server.ea_state = eapTlsStart; + break; + } +#endif /* USE_EAPTLS */ + esp->es_server.ea_state = eapMD5Chall; break; +#ifdef USE_EAPTLS + case eapTlsStart: + /* Initialize ssl session */ + if(!eaptls_init_ssl_server(esp)) { + esp->es_server.ea_state = eapBadAuth; + break; + } + + esp->es_server.ea_state = eapTlsRecv; + break; + + case eapTlsRecv: + ets = (struct eaptls_session *) esp->es_server.ea_session; + + if(ets->alert_sent) { + esp->es_server.ea_state = eapTlsSendAlert; + break; + } + + if (status) { + esp->es_server.ea_state = eapBadAuth; + break; + } + ets = (struct eaptls_session *) esp->es_server.ea_session; + + if(ets->frag) + esp->es_server.ea_state = eapTlsSendAck; + else + esp->es_server.ea_state = eapTlsSend; + break; + + case eapTlsSend: + ets = (struct eaptls_session *) esp->es_server.ea_session; + + if(SSL_is_init_finished(ets->ssl)) { + esp->es_server.ea_state = eapTlsRecvClient; + break; + } + + if(ets->frag) + esp->es_server.ea_state = eapTlsRecvAck; + else + esp->es_server.ea_state = eapTlsRecv; + break; + + case eapTlsSendAck: + esp->es_server.ea_state = eapTlsRecv; + break; + + case eapTlsRecvAck: + if (status) { + esp->es_server.ea_state = eapBadAuth; + break; + } + + esp->es_server.ea_state = eapTlsSend; + break; + + case eapTlsSendAlert: + esp->es_server.ea_state = eapTlsRecvAlertAck; + break; +#endif /* USE_EAPTLS */ + case eapSRP1: #ifdef USE_SRP ts = (struct t_server *)esp->es_server.ea_session; @@ -718,6 +810,30 @@ INCPTR(esp->es_server.ea_namelen, outp); break; +#ifdef USE_EAPTLS + case eapTlsStart: + PUTCHAR(EAPT_TLS, outp); + PUTCHAR(EAP_TLS_FLAGS_START, outp); + eap_figure_next_state(esp, 0); + break; + + case eapTlsSend: + eaptls_send(esp->es_server.ea_session, &outp); + eap_figure_next_state(esp, 0); + break; + + case eapTlsSendAck: + PUTCHAR(EAPT_TLS, outp); + PUTCHAR(0, outp); + eap_figure_next_state(esp, 0); + break; + + case eapTlsSendAlert: + eaptls_send(esp->es_server.ea_session, &outp); + eap_figure_next_state(esp, 0); + break; +#endif /* USE_EAPTLS */ + #ifdef USE_SRP case eapSRP1: PUTCHAR(EAPT_SRP, outp); @@ -904,11 +1020,57 @@ eap_server_timeout(arg) void *arg; { +#ifdef USE_EAPTLS + u_char *outp; + u_char *lenloc; + int outlen; +#endif /* USE_EAPTLS */ + eap_state *esp = (eap_state *) arg; if (!eap_server_active(esp)) return; +#ifdef USE_EAPTLS + switch(esp->es_server.ea_prev_state) { + + /* + * In eap-tls the state changes after a request, so we return to + * previous state ... + */ + case(eapTlsStart): + case(eapTlsSendAck): + esp->es_server.ea_state = esp->es_server.ea_prev_state; + break; + + /* + * ... or resend the stored data + */ + case(eapTlsSend): + case(eapTlsSendAlert): + outp = outpacket_buf; + MAKEHEADER(outp, PPP_EAP); + PUTCHAR(EAP_REQUEST, outp); + PUTCHAR(esp->es_server.ea_id, outp); + lenloc = outp; + INCPTR(2, outp); + + eaptls_retransmit(esp->es_server.ea_session, &outp); + + 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); + + return; + default: + break; + } +#endif /* USE_EAPTLS */ + /* EAP ID number must not change on timeout. */ eap_send_request(esp); } @@ -1166,6 +1328,81 @@ } #endif /* USE_SRP */ +#ifdef USE_EAPTLS +/* + * Send an EAP-TLS response message with tls data + */ +static void +eap_tls_response(esp, id) +eap_state *esp; +u_char id; +{ + u_char *outp; + int outlen; + u_char *lenloc; + + outp = outpacket_buf; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_RESPONSE, outp); + PUTCHAR(id, outp); + + lenloc = outp; + INCPTR(2, outp); + + /* + If the id in the request is unchanged, we must retransmit + the old data + */ + if(id == esp->es_client.ea_id) + eaptls_retransmit(esp->es_client.ea_session, &outp); + else + eaptls_send(esp->es_client.ea_session, &outp); + + outlen = (outp - outpacket_buf) - PPP_HDRLEN; + PUTSHORT(outlen, lenloc); + + output(esp->es_unit, outpacket_buf, PPP_HDRLEN + outlen); + + esp->es_client.ea_id = id; + +} + +/* + * Send an EAP-TLS ack + */ +static void +eap_tls_sendack(esp, id) +eap_state *esp; +u_char id; +{ + u_char *outp; + int outlen; + u_char *lenloc; + + outp = outpacket_buf; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_RESPONSE, outp); + PUTCHAR(id, outp); + esp->es_client.ea_id = id; + + lenloc = outp; + INCPTR(2, outp); + + PUTCHAR(EAPT_TLS, outp); + PUTCHAR(0, outp); + + outlen = (outp - outpacket_buf) - PPP_HDRLEN; + PUTSHORT(outlen, lenloc); + + output(esp->es_unit, outpacket_buf, PPP_HDRLEN + outlen); + +} +#endif /* USE_EAPTLS */ + static void eap_send_nak(esp, id, type) eap_state *esp; @@ -1320,6 +1557,11 @@ char rhostname[256]; MD5_CTX mdContext; u_char hash[MD5_SIGNATURE_SIZE]; +#ifdef USE_EAPTLS + u_char flags; + struct eaptls_session *ets = esp->es_client.ea_session; +#endif /* USE_EAPTLS */ + #ifdef USE_SRP struct t_client *tc; struct t_num sval, gval, Nval, *Ap, Bval; @@ -1456,6 +1698,90 @@ esp->es_client.ea_namelen); break; +#ifdef USE_EAPTLS + case EAPT_TLS: + + switch(esp->es_client.ea_state) { + + case eapListen: + + GETCHAR(flags, inp); + if(flags & EAP_TLS_FLAGS_START){ + + esp->es_client.ea_using_eaptls = 1; + + if (explicit_remote){ + esp->es_client.ea_peer = strdup(remote_name); + esp->es_client.ea_peerlen = strlen(remote_name); + } else + esp->es_client.ea_peer = NULL; + + /* Init ssl session */ + if(!eaptls_init_ssl_client(esp)) { + dbglog("cannot init ssl"); + eap_send_nak(esp, id, EAPT_TLS); + esp->es_client.ea_using_eaptls = 0; + break; + } + + ets = esp->es_client.ea_session; + eap_tls_response(esp, id); + esp->es_client.ea_state = (ets->frag ? eapTlsRecvAck : + eapTlsRecv); + break; + } + + /* The server has sent a bad start packet. */ + eap_send_nak(esp, id, EAPT_TLS); + break; + + case eapTlsRecvAck: + eap_tls_response(esp, id); + esp->es_client.ea_state = (ets->frag ? eapTlsRecvAck : + eapTlsRecv); + break; + + case eapTlsRecv: + eaptls_receive(ets, inp, len); + + if(ets->frag) { + eap_tls_sendack(esp, id); + esp->es_client.ea_state = eapTlsRecv; + break; + } + + if(ets->alert_recv) { + eap_tls_sendack(esp, id); + esp->es_client.ea_state = eapTlsRecvFailure; + break; + } + + /* Check if TLS handshake is finished */ + if(SSL_is_init_finished(ets->ssl)){ +#ifdef MPPE + eaptls_gen_mppe_keys( ets, "client EAP encryption", 1 ); +#endif + eaptls_free_session(ets); + eap_tls_sendack(esp, id); + esp->es_client.ea_state = eapTlsRecvSuccess; + break; + } + + eap_tls_response(esp,id); + esp->es_client.ea_state = (ets->frag ? eapTlsRecvAck : + eapTlsRecv); + + break; + + default: + eap_send_nak(esp, id, EAPT_TLS); + esp->es_client.ea_using_eaptls = 0; + break; + } + + break; +#endif /* USE_EAPTLS */ + #ifdef USE_SRP case EAPT_SRP: if (len < 1) { @@ -1737,6 +2063,11 @@ u_char dig[SHA_DIGESTSIZE]; #endif /* USE_SRP */ +#ifdef USE_EAPTLS + struct eaptls_session *ets; + u_char flags; +#endif /* USE_EAPTLS */ + if (esp->es_server.ea_id != id) { dbglog("EAP: discarding Response %d; expected ID %d", id, esp->es_server.ea_id); @@ -1776,6 +2107,60 @@ eap_figure_next_state(esp, 0); break; +#ifdef USE_EAPTLS + case EAPT_TLS: + switch(esp->es_server.ea_state) { + + case eapTlsRecv: + ets = (struct eaptls_session *) esp->es_server.ea_session; + eap_figure_next_state(esp, + eaptls_receive(esp->es_server.ea_session, inp, len)); + + if(ets->alert_recv) { + eap_send_failure(esp); + break; + } + break; + + case eapTlsRecvAck: + if(len > 1) { + dbglog("EAP-TLS ACK with extra data"); + } + eap_figure_next_state(esp, 0); + break; + + case eapTlsRecvClient: + /* Receive authentication response from client */ + + GETCHAR(flags, inp); + + if(len == 1 && !flags) { /* Ack = ok */ +#ifdef MPPE + eaptls_gen_mppe_keys( esp->es_server.ea_session, "client EAP encryption", 0 ); +#endif + eap_send_success(esp); + } + else { /* failure */ + eaptls_receive(esp->es_server.ea_session, inp, len); + warn("Server authentication failed"); + eap_send_failure(esp); + } + + eaptls_free_session(esp->es_server.ea_session); + + break; + + case eapTlsRecvAlertAck: + eap_send_failure(esp); + break; + + default: + eap_figure_next_state(esp, 1); + break; + } + break; +#endif /* USE_EAPTLS */ + case EAPT_NOTIFICATION: dbglog("EAP unexpected Notification; response discarded"); break; @@ -1807,6 +2192,13 @@ esp->es_server.ea_state = eapMD5Chall; break; +#ifdef USE_EAPTLS + /* Send EAP-TLS start packet */ + case EAPT_TLS: + esp->es_server.ea_state = eapTlsStart; + break; +#endif /* USE_EAPTLS */ + default: dbglog("EAP: peer requesting unknown Type %d", vallen); switch (esp->es_server.ea_state) { @@ -2018,13 +2410,27 @@ int id; int len; { - if (esp->es_client.ea_state != eapOpen && !eap_client_active(esp)) { + if (esp->es_client.ea_state != eapOpen && !eap_client_active(esp) +#ifdef USE_EAPTLS + && esp->es_client.ea_state != eapTlsRecvSuccess +#endif /* USE_EAPTLS */ + ) { dbglog("EAP unexpected success message in state %s (%d)", eap_state_name(esp->es_client.ea_state), esp->es_client.ea_state); return; } +#ifdef USE_EAPTLS + if(esp->es_client.ea_using_eaptls && esp->es_client.ea_state != + eapTlsRecvSuccess) { + dbglog("EAP-TLS unexpected success message in state %s (%d)", + eap_state_name(esp->es_client.ea_state), + esp->es_client.ea_state); + return; + } +#endif /* USE_EAPTLS */ + if (esp->es_client.ea_timeout > 0) { UNTIMEOUT(eap_client_timeout, (void *)esp); } @@ -2150,6 +2556,9 @@ int code, id, len, rtype, vallen; u_char *pstart; u_int32_t uval; +#ifdef USE_EAPTLS + u_char flags; +#endif /* USE_EAPTLS */ if (inlen < EAP_HEADERLEN) return (0); @@ -2214,6 +2623,24 @@ } break; +#ifdef USE_EAPTLS + case EAPT_TLS: + if (len < 1) + break; + GETCHAR(flags, inp); + len--; + + if(flags == 0 && len == 0){ + printer(arg, " Ack"); + break; + } + + printer(arg, flags & EAP_TLS_FLAGS_LI ? " L":" -"); + printer(arg, flags & EAP_TLS_FLAGS_MF ? "M":"-"); + printer(arg, flags & EAP_TLS_FLAGS_START ? "S":"- "); + break; +#endif /* USE_EAPTLS */ + case EAPT_SRP: if (len < 3) goto truncated; @@ -2325,6 +2752,25 @@ } break; +#ifdef USE_EAPTLS + case EAPT_TLS: + if (len < 1) + break; + GETCHAR(flags, inp); + len--; + + if(flags == 0 && len == 0){ + printer(arg, " Ack"); + break; + } + + printer(arg, flags & EAP_TLS_FLAGS_LI ? " L":" -"); + printer(arg, flags & EAP_TLS_FLAGS_MF ? "M":"-"); + printer(arg, flags & EAP_TLS_FLAGS_START ? "S":"- "); + + break; +#endif /* USE_EAPTLS */ + case EAPT_NAK: if (len <= 0) { printer(arg, " "); @@ -2426,3 +2872,4 @@ return (inp - pstart); } + diff -Naur ppp-2.4.5/pppd/eap.h ppp-2.4.5-eaptls-mppe-0.992/pppd/eap.h --- ppp-2.4.5/pppd/eap.h 2009-11-16 23:26:07.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/eap.h 2011-12-01 15:14:39.085353125 +0100 @@ -84,6 +84,16 @@ eapClosed, /* Authentication not in use */ eapListen, /* Client ready (and timer running) */ eapIdentify, /* EAP Identify sent */ + eapTlsStart, /* Send EAP-TLS start packet */ + eapTlsRecv, /* Receive EAP-TLS tls data */ + eapTlsSendAck, /* Send EAP-TLS ack */ + eapTlsSend, /* Send EAP-TLS tls data */ + eapTlsRecvAck, /* Receive EAP-TLS ack */ + eapTlsRecvClient, /* Receive EAP-TLS auth response from client*/ + eapTlsSendAlert, /* Send EAP-TLS tls alert (server)*/ + eapTlsRecvAlertAck, /* Receive EAP-TLS ack after sending alert */ + eapTlsRecvSuccess, /* Receive EAP success */ + eapTlsRecvFailure, /* Receive EAP failure */ eapSRP1, /* Sent EAP SRP-SHA1 Subtype 1 */ eapSRP2, /* Sent EAP SRP-SHA1 Subtype 2 */ eapSRP3, /* Sent EAP SRP-SHA1 Subtype 3 */ @@ -95,9 +105,18 @@ #define EAP_STATES \ "Initial", "Pending", "Closed", "Listen", "Identify", \ + "TlsStart", "TlsRecv", "TlsSendAck", "TlsSend", "TlsRecvAck", "TlsRecvClient",\ + "TlsSendAlert", "TlsRecvAlertAck" , "TlsRecvSuccess", "TlsRecvFailure", \ "SRP1", "SRP2", "SRP3", "MD5Chall", "Open", "SRP4", "BadAuth" -#define eap_client_active(esp) ((esp)->es_client.ea_state == eapListen) +#ifdef USE_EAPTLS +#define eap_client_active(esp) ((esp)->es_client.ea_state != eapInitial ||\ + (esp)->es_client.ea_state != eapPending ||\ + (esp)->es_client.ea_state != eapClosed) +#else +#define eap_client_active(esp) ((esp)->es_client.ea_state == eapListen) +#endif /* USE_EAPTLS */ + #define eap_server_active(esp) \ ((esp)->es_server.ea_state >= eapIdentify && \ (esp)->es_server.ea_state <= eapMD5Chall) @@ -112,11 +131,17 @@ u_short ea_namelen; /* Length of our name */ u_short ea_peerlen; /* Length of peer's name */ enum eap_state_code ea_state; +#ifdef USE_EAPTLS + enum eap_state_code ea_prev_state; +#endif 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 */ +#ifdef USE_EAPTLS + bool ea_using_eaptls; +#endif }; /* @@ -139,7 +164,12 @@ * Timeouts. */ #define EAP_DEFTIMEOUT 3 /* Timeout (seconds) for rexmit */ +#ifdef USE_EAPTLS +#define EAP_DEFTRANSMITS 30 /* max # times to transmit */ + /* certificates can be long ... */ +#else #define EAP_DEFTRANSMITS 10 /* max # times to transmit */ +#endif /* USE_EAPTLS */ #define EAP_DEFREQTIME 20 /* Time to wait for peer request */ #define EAP_DEFALLOWREQ 20 /* max # times to accept requests */ diff -Naur ppp-2.4.5/pppd/md5.c ppp-2.4.5-eaptls-mppe-0.992/pppd/md5.c --- ppp-2.4.5/pppd/md5.c 2009-11-16 23:26:07.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/md5.c 2011-12-01 15:14:39.086353254 +0100 @@ -33,6 +33,8 @@ *********************************************************************** */ +#ifndef USE_EAPTLS + #include #include "md5.h" @@ -305,3 +307,5 @@ ** End of md5.c ** ******************************** (cut) ******************************** */ +#endif /* USE_EAPTLS */ + diff -Naur ppp-2.4.5/pppd/md5.h ppp-2.4.5-eaptls-mppe-0.992/pppd/md5.h --- ppp-2.4.5/pppd/md5.h 2009-11-16 23:26:07.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/md5.h 2011-12-01 15:14:39.086353254 +0100 @@ -36,6 +36,7 @@ ** documentation and/or software. ** *********************************************************************** */ +#ifndef USE_EAPTLS #ifndef __MD5_INCLUDE__ @@ -63,3 +64,5 @@ #define __MD5_INCLUDE__ #endif /* __MD5_INCLUDE__ */ + +#endif /* USE_EAPTLS */ diff -Naur ppp-2.4.5/pppd/options.c ppp-2.4.5-eaptls-mppe-0.992/pppd/options.c --- ppp-2.4.5/pppd/options.c 2009-11-16 23:26:07.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/options.c 2011-12-01 15:14:39.087353383 +0100 @@ -119,6 +119,10 @@ bool dryrun; /* print out option values and exit */ char *domain; /* domain name set by domain option */ int child_wait = 5; /* # seconds to wait for children at exit */ +#ifdef USE_EAPTLS +bool only_update_crl_server = 0; /* update server crl and exit */ +bool only_update_crl_client = 0; /* update client crl and exit */ +#endif /* USE_EAPTLS */ #ifdef MAXOCTETS unsigned int maxoctets = 0; /* default - no limit */ @@ -320,6 +324,12 @@ { "mo-timeout", o_int, &maxoctets_timeout, "Check for traffic limit every N seconds", OPT_PRIO | OPT_LLIMIT | 1 }, #endif +#ifdef USE_EAPTLS + { "only-update-crl-server", o_bool, &only_update_crl_server, + "Update server CA CRLs and exit", 1 }, + { "only-update-crl-client", o_bool, &only_update_crl_client, + "Update client CA CRLs and exit", 1 }, +#endif /* USE_EAPTLS */ { NULL } }; diff -Naur ppp-2.4.5/pppd/pathnames.h ppp-2.4.5-eaptls-mppe-0.992/pppd/pathnames.h --- ppp-2.4.5/pppd/pathnames.h 2009-11-16 23:26:07.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/pathnames.h 2011-12-01 15:14:39.088353512 +0100 @@ -21,6 +21,13 @@ #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" + +#ifdef USE_EAPTLS +#define _PATH_EAPTLSCLIFILE _ROOT_PATH "/etc/ppp/eaptls-client" +#define _PATH_EAPTLSSERVFILE _ROOT_PATH "/etc/ppp/eaptls-server" +#define _PATH_OPENSSLCONFFILE _ROOT_PATH "/etc/ppp/openssl.cnf" +#endif /* USE_EAPTLS */ + #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" diff -Naur ppp-2.4.5/pppd/plugins/Makefile.linux ppp-2.4.5-eaptls-mppe-0.992/pppd/plugins/Makefile.linux --- ppp-2.4.5/pppd/plugins/Makefile.linux 2009-11-16 23:26:07.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/plugins/Makefile.linux 2011-12-01 15:14:39.089353640 +0100 @@ -4,6 +4,9 @@ LDFLAGS = -shared INSTALL = install +# EAP-TLS +CFLAGS += -DUSE_EAPTLS=1 + DESTDIR = $(INSTROOT)@DESTDIR@ BINDIR = $(DESTDIR)/sbin MANDIR = $(DESTDIR)/share/man/man8 diff -Naur ppp-2.4.5/pppd/plugins/passprompt.c ppp-2.4.5-eaptls-mppe-0.992/pppd/plugins/passprompt.c --- ppp-2.4.5/pppd/plugins/passprompt.c 2009-11-16 23:26:07.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/plugins/passprompt.c 2011-12-01 15:14:39.090353769 +0100 @@ -107,4 +107,7 @@ { add_options(options); pap_passwd_hook = promptpass; +#ifdef USE_EAPTLS + eaptls_passwd_hook = promptpass; +#endif } diff -Naur ppp-2.4.5/pppd/plugins/passwordfd.c ppp-2.4.5-eaptls-mppe-0.992/pppd/plugins/passwordfd.c --- ppp-2.4.5/pppd/plugins/passwordfd.c 2009-11-16 23:26:07.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/plugins/passwordfd.c 2011-12-01 15:16:39.158794111 +0100 @@ -79,4 +79,8 @@ chap_check_hook = pwfd_check; chap_passwd_hook = pwfd_passwd; + +#ifdef USE_EAPTLS + eaptls_passwd_hook = pwfd_passwd; +#endif } diff -Naur ppp-2.4.5/pppd/pppd.h ppp-2.4.5-eaptls-mppe-0.992/pppd/pppd.h --- ppp-2.4.5/pppd/pppd.h 2009-11-16 23:26:07.000000000 +0100 +++ ppp-2.4.5-eaptls-mppe-0.992/pppd/pppd.h 2011-12-01 15:16:25.296011439 +0100 @@ -320,6 +320,10 @@ extern bool dryrun; /* check everything, print options, exit */ extern int child_wait; /* # seconds to wait for children at end */ +#ifdef USE_EAPTLS +extern char *crl_dir; +#endif /* USE_EAPTLS */ + #ifdef MAXOCTETS extern unsigned int maxoctets; /* Maximum octetes per session (in bytes) */ extern int maxoctets_dir; /* Direction : @@ -717,6 +721,10 @@ extern int (*chap_passwd_hook) __P((char *user, char *passwd)); extern void (*multilink_join_hook) __P((void)); +#ifdef USE_EAPTLS +extern int (*eaptls_passwd_hook) __P((char *user, char *passwd)); +#endif + /* Let a plugin snoop sent and received packets. Useful for L2TP */ extern void (*snoop_recv_hook) __P((unsigned char *p, int len)); extern void (*snoop_send_hook) __P((unsigned char *p, int len));