diff -Naur ppp-2.4.4/README.eap-tls ppp-2.4.4-eap-tls-mppe/README.eap-tls --- ppp-2.4.4/README.eap-tls 1970-01-01 01:00:00.000000000 +0100 +++ ppp-2.4.4-eap-tls-mppe/README.eap-tls 2006-07-24 14:50:22.862595811 +0200 @@ -0,0 +1,150 @@ +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. + + 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. + + You must also have libcurl installed (used to fetch CRLs). + The version required is 7.9.8 or newer (because that's the one I used, + but an earlier version may work). + + Configure, compile, and install as usual. + +3. Configuration + + On the client side 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. + + +4. Options + + These pppd options are available: + + 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. + + auto-update-crl + On a new TLS handshake, try to fetch updated CRLs for the + trusted CAs. This is done only if the crl-dir option was used. + For each trusted CA, the appropriate CRL is checked and if + it doesn't exist or is too old, the update take place. + + crl-update-time + Time (in hours) after which a CRL must be updated if the option + auto-update-crl or only-update-crl-* were specified. Use 0 as + value if you want to force update. The default value is 72 + + need-peer-eap + If the peer doesn't ask us to authenticate or doesn't use eap + to authenticate us, disconnect. + + only-update-crl-server + Parse EAP-TLS server configuration file (/etc/ppp/eaptls-server) + and find all files containing trusted CAs; then update their CRL + and exit. + The 'crl-dir' option must be used to specify crl directory. + + only-update-crl-client + Similar to only-update-crl-server, but uses client configuration + file. + + +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. Updating CRLs with cron + + With the 'auto-update-crl' option you can do crl updating at connect time. + In some situations this approach can't be used, because the download can + take long time and the peer will have to wait too much, possibly going in + timeout. + In such cases, you can update crls periodically (with cron) using pppd with + the 'only-update-crl-client' or 'only-update-crl-server' options, as + explained above. + Example: + + pppd crl-dir /root/crldir only-update-crl-server crl-update-time 0 + + To show debug information add '/dev/null debug' to the command line. + +7. Notes + + This is experimental code. + Send suggestions and comments to Beniamino Galvani + or to Jan Just Keijser + diff -Naur ppp-2.4.4/etc.ppp/eaptls-client ppp-2.4.4-eap-tls-mppe/etc.ppp/eaptls-client --- ppp-2.4.4/etc.ppp/eaptls-client 1970-01-01 01:00:00.000000000 +0100 +++ ppp-2.4.4-eap-tls-mppe/etc.ppp/eaptls-client 2006-07-20 12:21:51.000000000 +0200 @@ -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.4/etc.ppp/eaptls-server ppp-2.4.4-eap-tls-mppe/etc.ppp/eaptls-server --- ppp-2.4.4/etc.ppp/eaptls-server 1970-01-01 01:00:00.000000000 +0100 +++ ppp-2.4.4-eap-tls-mppe/etc.ppp/eaptls-server 2006-07-20 12:21:51.000000000 +0200 @@ -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.4/linux/Makefile.top ppp-2.4.4-eap-tls-mppe/linux/Makefile.top --- ppp-2.4.4/linux/Makefile.top 2006-06-04 07:04:14.000000000 +0200 +++ ppp-2.4.4-eap-tls-mppe/linux/Makefile.top 2006-07-20 12:21:51.000000000 +0200 @@ -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.4/pppd/Makefile.linux ppp-2.4.4-eap-tls-mppe/pppd/Makefile.linux --- ppp-2.4.4/pppd/Makefile.linux 2006-06-04 07:07:46.000000000 +0200 +++ ppp-2.4.4-eap-tls-mppe/pppd/Makefile.linux 2006-07-24 15:39:35.138053142 +0200 @@ -73,6 +73,9 @@ # Enable EAP SRP-SHA1 authentication (requires libsrp) #USE_SRP=y +# Enable EAP-TLS authentication (requires libssl and libcurl) +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 -lcurl +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.4/pppd/auth.c ppp-2.4.4-eap-tls-mppe/pppd/auth.c --- ppp-2.4.4/pppd/auth.c 2006-06-18 13:26:00.000000000 +0200 +++ ppp-2.4.4-eap-tls-mppe/pppd/auth.c 2006-07-24 15:38:34.070498062 +0200 @@ -112,6 +112,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 @@ -237,6 +240,9 @@ bool allow_any_ip = 0; /* Allow peer to use any IP address */ bool explicit_remote = 0; /* User specified explicit remote name */ char remote_name[MAXNAMELEN]; /* Peer's name for authentication */ +#ifdef USE_EAPTLS +bool need_peer_eap = 0; /* Require peer to authenticate us */ +#endif static char *uafname; /* name of most recent +ua file */ @@ -255,6 +261,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 **, @@ -396,7 +415,10 @@ { "allow-number", o_special, (void *)set_permitted_number, "Set telephone number(s) which are allowed to connect", OPT_PRIV | OPT_A2LIST }, - +#ifdef USE_EAPTLS + { "need-peer-eap", o_bool, &need_peer_eap, + "Require the peer to authenticate us", 1 }, +#endif { NULL } }; @@ -724,6 +746,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; @@ -758,6 +783,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); used_login = 0; auth = 0; @@ -1268,6 +1309,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( @@ -1322,7 +1372,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)) @@ -1337,8 +1391,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; + } @@ -1916,6 +1976,7 @@ } + /* * get_secret - open the CHAP secret file and return the secret * for authenticating the given client on the given server. @@ -2568,3 +2629,307 @@ 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; + char servcertfile[MAXWORDLEN]; + char clicertfile[MAXWORDLEN]; + char cacertfile[MAXWORDLEN]; + char pkfile[MAXWORDLEN]; + + filename = _PATH_EAPTLSCLIFILE; + 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(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; + struct wordlist *addrs, *opts; + + 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 (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.4/pppd/ccp.c ppp-2.4.4-eap-tls-mppe/pppd/ccp.c --- ppp-2.4.4/pppd/ccp.c 2005-07-09 02:23:05.000000000 +0200 +++ ppp-2.4.4-eap-tls-mppe/pppd/ccp.c 2006-07-24 15:41:53.429251910 +0200 @@ -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.4/pppd/eap-tls.c ppp-2.4.4-eap-tls-mppe/pppd/eap-tls.c --- ppp-2.4.4/pppd/eap-tls.c 1970-01-01 01:00:00.000000000 +0100 +++ ppp-2.4.4-eap-tls-mppe/pppd/eap-tls.c 2006-07-24 13:49:08.515871696 +0200 @@ -0,0 +1,1276 @@ +/* + * 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 "pppd.h" +#include "eap.h" +#include "eap-tls.h" +#include "pathnames.h" + +extern char *crl_dir; +extern bool auto_update_crl; +extern int crl_update_time; + +static int search_wordlist(struct wordlist *wl, char *word); +static void add_wordlist(struct wordlist *wl, char *word); +static void destroy_wordlist(struct wordlist *wl); + +#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(); + } +} + +/* + * 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) +{ + SSL_CTX *ctx; + X509_STORE *certstore; + X509_LOOKUP *lookup; + X509 *tmp; + + /* + * Without these can't continue + */ + if (!cacertfile[0] || !privkeyfile[0] || !certfile[0] ) + 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 (!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 (!SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM)) { + error("EAP-TLS: Cannot use public certificate %s", certfile); + goto fail; + } + + 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; +} + +/* + * 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]; + struct wordlist updatedfiles; + + updatedfiles.next = NULL; + updatedfiles.word = NULL; + + /* + * 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("Error: client name not set (BUG)"); + return 0; + } + + strncpy(ets->peer, esp->es_server.ea_peer, MAXWORDLEN); + + if (!get_eaptls_secret(esp->es_unit, esp->es_server.ea_peer, + esp->es_server.ea_name, clicertfile, + servcertfile, cacertfile, pkfile, 1)) { + dbglog("get_eaptls_secret error"); + return 0; + } + + ets->mtu = netif_get_mtu(esp->es_unit) - PPP_HDRLEN - 10; + + ets->ctx = eaptls_init_ssl(1, cacertfile, servcertfile, clicertfile, pkfile); + if (!ets->ctx) + goto fail; + + if (crl_dir) { + if (auto_update_crl) + eaptls_update_crls(crl_dir, cacertfile, &updatedfiles); + + destroy_wordlist(&updatedfiles); + } + + 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]; + struct wordlist updatedfiles; + + updatedfiles.next = NULL; + updatedfiles.word = NULL; + + /* + * 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 = netif_get_mtu(esp->es_unit) - PPP_HDRLEN - 10; + + if (!get_eaptls_secret(esp->es_unit, esp->es_client.ea_name, + esp->es_client.ea_peer, clicertfile, + servcertfile, cacertfile, pkfile, 0)) { + dbglog("get_eaptls_secret error"); + return 0; + } + + ets->ctx = eaptls_init_ssl(0, cacertfile, clicertfile, servcertfile, pkfile); + if (!ets->ctx) + goto fail; + + if (crl_dir) { + if (auto_update_crl) + eaptls_update_crls(crl_dir, cacertfile, &updatedfiles); + + destroy_wordlist(&updatedfiles); + } + + ets->ssl = SSL_new(ets->ctx); + + if (!ets->ssl) + goto fail; + + /* + * 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_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: + SSL_CTX_free(ets->ctx); + return 0; + +} + +/* + * Updates the CA CRL + */ +void eaptls_update_crls(char *crldir, char *cacertfile, struct wordlist *uf) +{ + FILE *file; + X509 *cert; + char *filename; + + if (!(file = fopen(cacertfile, "r"))) + return; + + filename = malloc(strlen(crldir) + 15); + + /* + * for each CA certificate, control if the CRL must be updated + */ + while ((cert = PEM_read_X509(file, NULL, NULL, NULL))) { + + if (eaptls_must_update(cert, crldir, filename, uf) == 1) { + + eaptls_get_crl(cert, filename); + add_wordlist(uf, filename); + } + + } + + free(filename); + fclose(file); +} + +/* + * Control if the CA CRL must be updated + * Return in filename the file to be updated + */ +int eaptls_must_update(X509 * cert, char *crldir, char *filename, + struct wordlist *uf) +{ + X509_NAME *xn; + unsigned long hash; + int found = 0, i, hours; + FILE *file; + X509_CRL *crl; + time_t curr_time, mod_time; + struct stat file_stat; + char tmpname[15]; + char subject[256]; + char cn_str[256]; + + /* + * Get CA subject name and calculate its hash + */ + xn = X509_get_subject_name(cert); + hash = X509_NAME_hash(xn); + + X509_NAME_oneline(xn, subject, 256); + X509_NAME_get_text_by_NID(xn, NID_commonName, cn_str, 256); + dbglog(" -> processing CA: %s", cn_str); + + /* + * Search a CRL for this CA in crldir + */ + for (i = 0; i < 10; i++) { + sprintf(tmpname, "%08lx.r%1d", hash, i); + strcpy(filename, crldir); + strcat(filename, "/"); + strcat(filename, tmpname); + + if (!(file = fopen(filename, "r"))) + break; /* CRL not found */ + + if (!(crl = PEM_read_X509_CRL(file, NULL, NULL, NULL))) { + fclose(file); + continue; + } + fclose(file); + + /* + * test if it's the right CRL + */ + if (X509_NAME_cmp(xn, X509_CRL_get_issuer(crl)) == 0) { + found = 1; + X509_CRL_free(crl); + break; + } + X509_CRL_free(crl); + } + + /* + * If file already in list, we've already updated it + */ + if (search_wordlist(uf, filename) == 1) + return 0; + + /* + * If CRL file for this CA not found, need update + */ + if (!found) + return 1; + + curr_time = time(NULL); + stat(filename, &file_stat); + mod_time = file_stat.st_mtime; + hours = (curr_time - mod_time) / 3600; + + /* + * If file too old, need update + */ + if (hours >= crl_update_time) { + dbglog(" file %s is %d hours old, need update", + filename, hours); + return 1; + } + + return 0; +} + +/* + * Extract CDPs from certificate + */ +void eaptls_get_crl(X509 * cert, char *filename) +{ + STACK_OF(DIST_POINT) * distpoints = NULL; + DIST_POINT *dp; + DIST_POINT_NAME *dpn; + STACK_OF(GENERAL_NAME) * names; + GENERAL_NAME *name; + ASN1_IA5STRING *uri_asn; + char *uri; + int num_dp, i; + int num_name, j; + + distpoints = X509_get_ext_d2i(cert, + NID_crl_distribution_points, NULL, NULL); + num_dp = sk_DIST_POINT_num(distpoints); + + dbglog(" CA certificate contains %d CDP", num_dp); + + for (i = 0; i < num_dp; i++) { + + dp = sk_DIST_POINT_value(distpoints, i); + dpn = dp->distpoint; + names = dpn->name.fullname; + num_name = sk_GENERAL_NAME_num(names); + + for (j = 0; j < num_name; j++) { + + name = sk_GENERAL_NAME_value(names, j); + + if (name->type == GEN_URI) { + uri_asn = name->d.uniformResourceIdentifier; + uri = ASN1_STRING_data(uri_asn); + + if (eaptls_download_crl(uri, filename)) { + dbglog + (" * %s successfully loaded", + uri); + return; + } + + } + + } + } +} + +/* + * Download a CRL from uri into file + */ +int eaptls_download_crl(char *uri, char *filename) +{ + FILE *tmpfile; + FILE *destfile; + int tmpfd; + char tmpname[30]; + CURL *curl; + int res; + X509_CRL *crl; + + strcpy(tmpname, "/tmp/ppp-tmp-XXXXXX"); + + if ((tmpfd = mkstemp(tmpname)) == -1) { + error("error creating temp file /tmp/ppp-tmp-*"); + return 0; + } + + tmpfile = fdopen(tmpfd, "w"); + if (!tmpfile) { + error("error fdopen"); + close(tmpfd); + return 0; + } + + /* + * download in temp file using libcurl + */ + curl_global_init(CURL_GLOBAL_NOTHING); + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, uri); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, tmpfile); + + res = curl_easy_perform(curl); + + curl_easy_cleanup(curl); + curl_global_cleanup(); + + if (res != CURLE_OK) { + dbglog(" * %s download failed", uri); + fclose(tmpfile); + unlink(tmpname); + return 0; + } + + fclose(tmpfile); + tmpfile = fopen(tmpname, "r"); + if(!tmpfile) + return 0; + + /* + * load CRL from temp file + */ + crl = d2i_X509_CRL_fp(tmpfile, NULL); + + fclose(tmpfile); + unlink(tmpname); + + if (!crl) { + warn("cannot load DER CRL"); + return 0; + } + + destfile = fopen(filename, "w"); + if (!destfile) { + X509_CRL_free(crl); + return 0; + } + + /* + * write CRL in PEM format to destination file + */ + PEM_write_X509_CRL(destfile, crl); + + X509_CRL_free(crl); + fclose(destfile); + + return 1; +} + +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 (!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; + } + break; + } + + /* Alert messages must always be displayed */ + if(content_type == SSL3_RT_ALERT) + error("%s", string); + else + dbglog("%s", string); +} + +/* + * Extract all CA files from conf file 'filename' and update crl + */ +void eaptls_do_update(char *filename, struct wordlist *updatedfiles) +{ + int newline; + FILE *f; + char word[MAXWORDLEN]; + + if (!(f = fopen(filename, "r"))) { + fprintf(stderr, "can't open %s", filename); + die(1); + } + + if (!getword(f, word, &newline, filename)) + return; /* file is empty??? */ + + newline = 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 */ + + /* + * server + */ + if (!getword(f, word, &newline, filename)) + break; + if (newline) + continue; + + /* + * client cert + */ + if (!getword(f, word, &newline, filename)) + break; + if (newline) + continue; + + /* + * server cert + */ + if (!getword(f, word, &newline, filename)) + break; + if (newline) + continue; + + /* + * ca cert + */ + if (!getword(f, word, &newline, filename)) + break; + if (newline) + continue; + + printf("Updating CRLs for CA file %s\n", word); + + eaptls_update_crls(crl_dir, word, updatedfiles); + } + + fclose(f); +} + +void eaptls_only_update_crl(int server, int client) +{ + /* + * mantain a list of updated crl files, so if a CA appears more + * times, it is updated once + */ + struct wordlist updatedfiles; + + updatedfiles.next = NULL; + updatedfiles.word = NULL; + + if (server) + eaptls_do_update(_PATH_EAPTLSSERVFILE, &updatedfiles); + + if (client) + eaptls_do_update(_PATH_EAPTLSCLIFILE, &updatedfiles); + + destroy_wordlist(&updatedfiles); +} + +static int search_wordlist(struct wordlist *wl, char *word) +{ + struct wordlist *tmp = wl->next; + + while (tmp) { + if (strcmp(tmp->word, word) == 0) + return 1; + tmp = tmp->next; + } + return 0; +} + +static void add_wordlist(struct wordlist *wl, char *word) +{ + struct wordlist *tmp = wl; + + while (tmp->next) + tmp = tmp->next; + + tmp->next = malloc(sizeof(struct wordlist)); + tmp->next->word = strdup(word); + tmp->next->next = NULL; + +} + +static void destroy_wordlist(struct wordlist *wl) +{ + struct wordlist *tmp = wl->next; + struct wordlist *next; + + while (tmp) { + next = tmp->next; + + if (tmp->word) + free(tmp->word); + + free(tmp); + tmp = next; + } + +} diff -Naur ppp-2.4.4/pppd/eap-tls.h ppp-2.4.4-eap-tls-mppe/pppd/eap-tls.h --- ppp-2.4.4/pppd/eap-tls.h 1970-01-01 01:00:00.000000000 +0100 +++ ppp-2.4.4-eap-tls-mppe/pppd/eap-tls.h 2006-07-24 12:36:35.240447056 +0200 @@ -0,0 +1,106 @@ +/* + * 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 + +#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 */ + +#define CRL_UPDATE_TIME 72 /* update time for crl: 72 hours */ + +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 */ +}; + +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); + +void eaptls_update_crls(char *crldir, char *cacertfile, struct wordlist *wl); +int eaptls_must_update(X509 * cert, char *crldir, char *filename, + struct wordlist *wl); +void eaptls_get_crl(X509 * cert, char *filename); +int eaptls_download_crl(char *uri, char *filename); + +#ifdef MPPE +#include /* 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.4/pppd/eap.c ppp-2.4.4-eap-tls-mppe/pppd/eap.c --- ppp-2.4.4/pppd/eap.c 2004-11-09 23:39:25.000000000 +0100 +++ ppp-2.4.4-eap-tls-mppe/pppd/eap.c 2006-07-24 16:07:52.889280229 +0200 @@ -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 $" /* @@ -73,9 +73,14 @@ #define MD5Update MD5_Update #define MD5Final MD5_Final #else +#ifndef USE_EAPTLS #include "md5.h" -#endif +#endif /* USE_EAPTLS */ +#endif /* USE_OPENSSL */ #include "eap.h" +#ifdef USE_EAPTLS +#include "eap-tls.h" +#endif /* USE_EAPTLS */ #ifdef USE_SRP #include @@ -83,6 +92,12 @@ static char *pn_secret = NULL; /* Pseudonym generating secret */ #endif +#ifdef USE_EAPTLS +char *crl_dir = NULL; +bool auto_update_crl = 0; +int crl_update_time = CRL_UPDATE_TIME; +#endif /* USE_EAPTLS */ + /* * Command-line options. */ @@ -105,6 +120,14 @@ { "srp-use-pseudonym", o_bool, &eap_states[0].es_usepseudo, "Use pseudonym if offered one by server", 1 }, #endif +#ifdef USE_EAPTLS + { "crl-dir", o_string, &crl_dir, + "Use CRLs in directory" }, + { "auto-update-crl", o_bool, &auto_update_crl, + "Automatically download CA CRL if old or non existent", 1 }, + { "crl-update-time", o_int, &crl_update_time, + "Time (in hours) after which reload CA CRL" }, +#endif /* USE_EAPTLS */ { NULL } }; @@ -209,6 +232,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 +462,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 +596,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 +824,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 +1034,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 +1342,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 +1571,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 +1712,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 +2077,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 +2121,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 +2206,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 +2424,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 +2570,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 +2637,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 +2766,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 +2886,4 @@ return (inp - pstart); } + diff -Naur ppp-2.4.4/pppd/eap.h ppp-2.4.4-eap-tls-mppe/pppd/eap.h --- ppp-2.4.4/pppd/eap.h 2003-06-12 01:56:26.000000000 +0200 +++ ppp-2.4.4-eap-tls-mppe/pppd/eap.h 2006-07-24 16:03:38.142650902 +0200 @@ -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.4/pppd/main.c ppp-2.4.4-eap-tls-mppe/pppd/main.c --- ppp-2.4.4/pppd/main.c 2006-06-04 05:52:50.000000000 +0200 +++ ppp-2.4.4-eap-tls-mppe/pppd/main.c 2006-07-24 16:09:38.714310467 +0200 @@ -260,6 +260,10 @@ extern char *getlogin __P((void)); int main __P((int, char *[])); +#ifdef USE_EAPTLS +void eaptls_only_update_crl(int serv, int cli); +#endif + #ifdef ultrix #undef O_NONBLOCK #define O_NONBLOCK O_NDELAY @@ -418,6 +422,19 @@ if (dryrun) die(0); +#ifdef USE_EAPTLS + if(only_update_crl_server || only_update_crl_client) { + if(!crl_dir){ + fprintf(stderr, "You must specify a crl dir\n"); + die(1); + } + + eaptls_only_update_crl(only_update_crl_server, only_update_crl_client); + + die(0); + } +#endif /* USE_EAPTLS */ + /* Make sure fds 0, 1, 2 are open to somewhere. */ fd_devnull = open(_PATH_DEVNULL, O_RDWR); if (fd_devnull < 0) diff -Naur ppp-2.4.4/pppd/options.c ppp-2.4.4-eap-tls-mppe/pppd/options.c --- ppp-2.4.4/pppd/options.c 2006-06-18 13:26:00.000000000 +0200 +++ ppp-2.4.4-eap-tls-mppe/pppd/options.c 2006-07-24 16:10:16.075202878 +0200 @@ -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.4/pppd/pathnames.h ppp-2.4.4-eap-tls-mppe/pppd/pathnames.h --- ppp-2.4.4/pppd/pathnames.h 2005-08-26 01:59:34.000000000 +0200 +++ ppp-2.4.4-eap-tls-mppe/pppd/pathnames.h 2006-07-24 16:10:41.665759273 +0200 @@ -21,6 +21,12 @@ #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" +#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.4/pppd/pppd.h ppp-2.4.4-eap-tls-mppe/pppd/pppd.h --- ppp-2.4.4/pppd/pppd.h 2005-08-26 01:59:34.000000000 +0200 +++ ppp-2.4.4-eap-tls-mppe/pppd/pppd.h 2006-07-24 16:11:03.272540399 +0200 @@ -319,6 +319,12 @@ extern bool dryrun; /* check everything, print options, exit */ extern int child_wait; /* # seconds to wait for children at end */ +#ifdef USE_EAPTLS +extern bool only_update_crl_server; /* update server crls and exit */ +extern bool only_update_crl_client; /* update client crls and exit */ +extern char *crl_dir; +#endif /* USE_EAPTLS */ + #ifdef MAXOCTETS extern unsigned int maxoctets; /* Maximum octetes per session (in bytes) */ extern int maxoctets_dir; /* Direction :