]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/eap.c
pppd: Add support for registering ppp interface via Linux rtnetlink API
[ppp.git] / pppd / eap.c
index ede5ff0d3a2f09fd7015763bfadfdbda7abc0902..6cb595f5b7c57b78cdd22ed790928535f4f29777 100644 (file)
@@ -48,9 +48,9 @@
  * Implemented EAP-TLS authentication
  */
 
-/*
- * TODO:
- */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
 
 #include <stdio.h>
 #include <stdlib.h>
 #include "pathnames.h"
 #include "md5.h"
 #include "eap.h"
+#ifdef USE_PEAP
+#include "peap.h"
+#endif /* USE_PEAP */
 
 #ifdef USE_SRP
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
 #include <t_pwd.h>
 #include <t_server.h>
 #include <t_client.h>
 #ifdef USE_EAPTLS
 #include "eap-tls.h"
 #endif /* USE_EAPTLS */
+
 #ifdef CHAPMS
-#include "magic.h"
 #include "chap_ms.h"
 #include "chap-new.h"
+
+extern int chapms_strip_domain;
 #endif /* CHAPMS */
 
 eap_state eap_states[NUM_PPP];         /* EAP state; one for each unit */
@@ -222,6 +230,10 @@ eap_init(int unit)
 #ifdef USE_EAPTLS
        esp->es_client.ea_using_eaptls = 0;
 #endif /* USE_EAPTLS */
+#ifdef CHAPMS
+       esp->es_client.digest = chap_find_digest(CHAP_MICROSOFT_V2);
+       esp->es_server.digest = chap_find_digest(CHAP_MICROSOFT_V2);
+#endif
 }
 
 /*
@@ -550,7 +562,7 @@ eap_figure_next_state(eap_state *esp, int status)
                        tpw.pebuf.name = esp->es_server.ea_peer;
                        tpw.pebuf.password.len = t_fromb64((char *)tpw.pwbuf,
                            cp);
-                       tpw.pebuf.password.data = tpw.pwbuf;
+                       tpw.pebuf.password.data = (char*) tpw.pwbuf;
                        tpw.pebuf.salt.len = t_fromb64((char *)tpw.saltbuf,
                            cp2);
                        tpw.pebuf.salt.data = tpw.saltbuf;
@@ -716,92 +728,6 @@ eap_figure_next_state(eap_state *esp, int status)
 }
 
 #if CHAPMS
-static int
-eap_chapms2_verify_response(int id, char *name,
-                           unsigned char *secret, int secret_len,
-                           unsigned char *challenge, unsigned char *response,
-                           char *message, int message_space)
-{
-       unsigned char md[MS_CHAP2_RESPONSE_LEN];
-       char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
-       int challenge_len, response_len;
-
-       challenge_len = *challenge++;   /* skip length, is 16 */
-       response_len = *response++;
-       if (response_len != MS_CHAP2_RESPONSE_LEN)
-               goto bad;       /* not even the right length */
-
-       /* Generate the expected response and our mutual auth. */
-       ChapMS2(challenge, &response[MS_CHAP2_PEER_CHALLENGE], name,
-               (char *)secret, secret_len, md,
-               (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
-
-       /* compare MDs and send the appropriate status */
-       /*
-        * Per RFC 2759, success message must be formatted as
-        *     "S=<auth_string> M=<message>"
-        * where
-        *     <auth_string> is the Authenticator Response (mutual auth)
-        *     <message> is a text message
-        *
-        * However, some versions of Windows (win98 tested) do not know
-        * about the M=<message> part (required per RFC 2759) and flag
-        * it as an error (reported incorrectly as an encryption error
-        * to the user).  Since the RFC requires it, and it can be
-        * useful information, we supply it if the peer is a conforming
-        * system.  Luckily (?), win98 sets the Flags field to 0x04
-        * (contrary to RFC requirements) so we can use that to
-        * distinguish between conforming and non-conforming systems.
-        *
-        * Special thanks to Alex Swiridov <say@real.kharkov.ua> for
-        * help debugging this.
-        */
-       if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP],
-                  MS_CHAP2_NTRESP_LEN) == 0) {
-               if (response[MS_CHAP2_FLAGS])
-                       slprintf(message, message_space, "S=%s", saresponse);
-               else
-                       slprintf(message, message_space, "S=%s M=%s",
-                                saresponse, "Access granted");
-               return 1;
-       }
-
- bad:
-       /*
-        * Failure message must be formatted as
-        *     "E=e R=r C=c V=v M=m"
-        * where
-        *     e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
-        *     r = retry (we use 1, ok to retry)
-        *     c = challenge to use for next response, we reuse previous
-        *     v = Change Password version supported, we use 0
-        *     m = text message
-        *
-        * The M=m part is only for MS-CHAPv2.  Neither win2k nor
-        * win98 (others untested) display the message to the user anyway.
-        * They also both ignore the E=e code.
-        *
-        * Note that it's safe to reuse the same challenge as we don't
-        * actually accept another response based on the error message
-        * (and no clients try to resend a response anyway).
-        *
-        * Basically, this whole bit is useless code, even the small
-        * implementation here is only because of overspecification.
-        */
-       slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
-                challenge_len, challenge, "Access denied");
-       return 0;
-}
-
-static struct chap_digest_type eap_chapms2_digest = {
-       CHAP_MICROSOFT_V2,      /* code */
-       NULL, /* chapms2_generate_challenge, */
-       eap_chapms2_verify_response,
-       NULL, /* chapms2_make_response, */
-       NULL, /* chapms2_check_success, */
-       NULL, /* chapms_handle_failure, */
-};
-
 /*
  * eap_chap_verify_response - check whether the peer's response matches
  * what we think it should be.  Returns 1 if it does (authentication
@@ -958,10 +884,9 @@ eap_send_request(eap_state *esp)
 
 #ifdef CHAPMS
        case eapMSCHAPv2Chall:
-               challen = 0x10;
+               esp->es_server.digest->generate_challenge(esp->es_challenge);
+               challen = esp->es_challenge[0];
                esp->es_challen = challen;
-               esp->es_challenge[0] = challen;
-               random_bytes(&esp->es_challenge[1], challen);
 
                PUTCHAR(EAPT_MSCHAPV2, outp);
                PUTCHAR(CHAP_CHALLENGE, outp);
@@ -1673,6 +1598,39 @@ write_pseudonym(eap_state *esp, u_char *inp, int len, int id)
 }
 #endif /* USE_SRP */
 
+#if CHAPMS
+/*
+ * Format and send an CHAPV2-Challenge EAP Response message.
+ */
+static void
+eap_chapv2_response(eap_state *esp, u_char id, u_char chapid, u_char *response, char *user, int user_len)
+{
+    u_char *outp;
+    int msglen;
+
+    outp = outpacket_buf;
+
+    MAKEHEADER(outp, PPP_EAP);
+
+    PUTCHAR(EAP_RESPONSE, outp);
+    PUTCHAR(id, outp);
+    esp->es_client.ea_id = id;
+    msglen = EAP_HEADERLEN + 6 * sizeof (u_char) + MS_CHAP2_RESPONSE_LEN + user_len;
+    PUTSHORT(msglen, outp);
+    PUTCHAR(EAPT_MSCHAPV2, outp);
+    PUTCHAR(CHAP_RESPONSE, outp);
+    PUTCHAR(chapid, outp);
+    PUTCHAR(0, outp);
+    /* len */
+    PUTCHAR(5 + user_len + MS_CHAP2_RESPONSE_LEN, outp);
+    BCOPY(response, outp, MS_CHAP2_RESPONSE_LEN+1); // VLEN + VALUE
+    INCPTR(MS_CHAP2_RESPONSE_LEN+1, outp);
+    BCOPY(user, outp, user_len);
+
+    output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+#endif
+
 /*
  * eap_request - Receive EAP Request message (client mode).
  */
@@ -1859,7 +1817,7 @@ eap_request(eap_state *esp, u_char *inp, int id, int len)
                                /* Init ssl session */
                                if(!eaptls_init_ssl_client(esp)) {
                                        dbglog("cannot init ssl");
-                                       eap_send_nak(esp, id, EAPT_TLS);
+                                       eap_send_nak(esp, id, EAPT_MSCHAPV2);
                                        esp->es_client.ea_using_eaptls = 0;
                                        break;
                                }
@@ -1871,7 +1829,7 @@ eap_request(eap_state *esp, u_char *inp, int id, int len)
                        }
 
                        /* The server has sent a bad start packet. */
-                       eap_send_nak(esp, id, EAPT_TLS);
+                       eap_send_nak(esp, id, EAPT_MSCHAPV2);
                        break;
 
                case eapTlsRecvAck:
@@ -1915,7 +1873,7 @@ eap_request(eap_state *esp, u_char *inp, int id, int len)
                        break;
 
                default:
-                       eap_send_nak(esp, id, EAPT_TLS);
+                       eap_send_nak(esp, id, EAPT_MSCHAPV2);
                        esp->es_client.ea_using_eaptls = 0;
                        break;
                }
@@ -2154,6 +2112,136 @@ eap_request(eap_state *esp, u_char *inp, int id, int len)
                }
                break;
 #endif /* USE_SRP */
+    
+#ifdef CHAPMS
+        case EAPT_MSCHAPV2:
+           if (len < 4) {
+               error("EAP: received invalid MSCHAPv2 packet, too short");
+               return;
+           }
+           unsigned char opcode;
+           GETCHAR(opcode, inp);
+           unsigned char chapid; /* Chapv2-ID */
+           GETCHAR(chapid, inp);
+           short mssize;
+           GETSHORT(mssize, inp);
+
+           /* Validate the mssize field */
+           if (len != mssize) { 
+               error("EAP: received invalid MSCHAPv2 packet, invalid length");
+               return;
+           }
+           len -= 4;
+
+           /* If MSCHAPv2 digest was not found, NAK the packet */
+           if (!esp->es_client.digest) {
+               error("EAP MSCHAPv2 not supported");
+               eap_send_nak(esp, id, EAPT_SRP);
+               return;
+           }
+
+           switch (opcode) {
+           case CHAP_CHALLENGE: {
+
+               /* make_response() expects: VLEN + VALUE */
+               u_char *challenge = inp;
+
+               unsigned char vsize;
+               GETCHAR(vsize, inp);
+                len -= 1;
+
+               /* Validate the VALUE field */
+                if (vsize != MS_CHAP2_PEER_CHAL_LEN || len < MS_CHAP2_PEER_CHAL_LEN) {
+                    error("EAP: received invalid MSCHAPv2 packet, invalid value-length: %d", vsize);
+                    return;
+                }
+
+               /* Increment past the VALUE field */
+               INCPTR(MS_CHAP2_PEER_CHAL_LEN, inp);
+               len -= MS_CHAP2_PEER_CHAL_LEN;
+
+               /* Extract the hostname */
+               rhostname[0] = '\0';
+               if (len > 0) {
+                   if (len >= sizeof (rhostname)) {
+                       dbglog("EAP: trimming really long peer name down");
+                       len = sizeof(rhostname) - 1;
+                   }
+                   BCOPY(inp, rhostname, len);
+                   rhostname[len] = '\0';
+               }
+
+               /* In case the remote doesn't give us his name. */
+               if (explicit_remote || (remote_name[0] != '\0' && len == 0))
+                   strlcpy(rhostname, remote_name, sizeof(rhostname));
+
+               /* Get the secret for authenticating ourselves with the specified host. */
+               if (!get_secret(esp->es_unit, esp->es_client.ea_name,
+                   rhostname, secret, &secret_len, 0)) {
+                   dbglog("EAP: no CHAP secret for auth to %q", rhostname);
+                   eap_send_nak(esp, id, EAPT_SRP);
+                   break;
+               }
+               esp->es_client.ea_namelen = strlen(esp->es_client.ea_name);
+
+               /* Create the MSCHAPv2 response (and add to cache) */
+               unsigned char response[MS_CHAP2_RESPONSE_LEN+1]; // VLEN + VALUE
+               esp->es_client.digest->make_response(response, chapid, esp->es_client.ea_name,
+                       challenge, secret, secret_len, NULL);
+
+               eap_chapv2_response(esp, id, chapid, response, esp->es_client.ea_name, esp->es_client.ea_namelen);
+               break;
+           }
+           case CHAP_SUCCESS: {
+
+               /* Check response for mutual authentication */
+               u_char status = CHAP_FAILURE;
+               if (esp->es_client.digest->check_success(chapid, inp, len) == 1) {
+                    info("Chap authentication succeeded! %.*v", len, inp);
+                    status = CHAP_SUCCESS;
+               }
+               eap_send_response(esp, id, EAPT_MSCHAPV2, &status, sizeof(status));
+               break;
+           }
+           case CHAP_FAILURE: {
+
+               /* Process the failure string, and log appropriate information */
+               esp->es_client.digest->handle_failure(inp, len);
+
+               u_char status = CHAP_FAILURE;
+               eap_send_response(esp, id, EAPT_MSCHAPV2, &status, sizeof(status));
+               goto client_failure; /* force termination */
+           }
+           default:
+
+                error("EAP: received invalid MSCHAPv2 packet, invalid or unsupported opcode: %d", opcode);
+               eap_send_nak(esp, id, EAPT_SRP);
+           }
+
+           break;
+#endif /* CHAPMS */
+#ifdef USE_PEAP
+       case EAPT_PEAP:
+
+               /* Initialize the PEAP context (if not already initialized) */
+               if (!esp->ea_peap) {
+                       rhostname[0] = '\0';
+                       if (explicit_remote || (remote_name[0] != '\0')) {
+                               strlcpy(rhostname, remote_name, sizeof (rhostname));
+                       }
+                       if (peap_init(&esp->ea_peap, rhostname)) {
+                               eap_send_nak(esp, id, EAPT_TLS);
+                               break;
+                       }
+               }
+
+               /* Process the PEAP packet */
+               if (peap_process(esp, id, inp, len)) {
+                       eap_send_nak(esp, id, EAPT_TLS);
+               }
+
+               break;
+#endif /* USE_PEAP */
 
        default:
                info("EAP: unknown authentication type %d; Naking", typenum);
@@ -2168,13 +2256,13 @@ eap_request(eap_state *esp, u_char *inp, int id, int len)
        }
        return;
 
-#ifdef USE_SRP
 client_failure:
        esp->es_client.ea_state = eapBadAuth;
        if (esp->es_client.ea_timeout > 0) {
                UNTIMEOUT(eap_client_timeout, (void *)esp);
        }
        esp->es_client.ea_session = NULL;
+#ifdef USE_SRP
        t_clientclose(tc);
        auth_withpeer_fail(esp->es_unit, PPP_EAP);
 #endif /* USE_SRP */
@@ -2198,8 +2286,6 @@ eap_response(eap_state *esp, u_char *inp, int id, int len)
        struct t_num A;
        SHA1_CTX ctxt;
        u_char dig[SHA_DIGESTSIZE];
-       SHA1_CTX ctxt;
-       u_char dig[SHA_DIGESTSIZE];
 #endif /* USE_SRP */
 
 #ifdef USE_EAPTLS
@@ -2357,6 +2443,12 @@ eap_response(eap_state *esp, u_char *inp, int id, int len)
 #ifdef CHAPMS
                case EAPT_MSCHAPV2:
                        info("EAP: peer proposes MSCHAPv2");
+                       /* If MSCHAPv2 digest was not found, NAK the packet */
+                       if (!esp->es_server.digest) {
+                               error("EAP MSCHAPv2 not supported");
+                               eap_send_nak(esp, id, EAPT_SRP);
+                               break;
+                       }
                        esp->es_server.ea_state = eapMSCHAPv2Chall;
                        break;
 #endif /* CHAPMS */
@@ -2486,6 +2578,14 @@ eap_response(eap_state *esp, u_char *inp, int id, int len)
                                        (remote_name[0] != '\0' && vallen == len))
                                strlcpy(rhostname, remote_name, sizeof (rhostname));
 
+                       /* strip the MS domain name */
+                       if (chapms_strip_domain && strrchr(rhostname, '\\')) {
+                               char tmp[MAXNAMELEN+1];
+
+                               strcpy(tmp, strrchr(rhostname, '\\') + 1);
+                               strcpy(rhostname, tmp);
+                       }
+
                        if (chap_verify_hook)
                                chap_verifier = chap_verify_hook;
                        else
@@ -2495,7 +2595,7 @@ eap_response(eap_state *esp, u_char *inp, int id, int len)
                        if ((*chap_verifier)(rhostname,
                                                esp->es_server.ea_name,
                                                id,
-                                               &eap_chapms2_digest,
+                                               esp->es_server.digest,
                                                esp->es_challenge,
                                                inp - 1,
                                                response_message,
@@ -2695,6 +2795,10 @@ eap_success(eap_state *esp, u_char *inp, int id, int len)
                PRINTMSG(inp, len);
        }
 
+#ifdef USE_PEAP
+       peap_finish(&esp->ea_peap);
+#endif
+
        esp->es_client.ea_state = eapOpen;
        auth_withpeer_success(esp->es_unit, PPP_EAP, 0);
 }
@@ -2729,6 +2833,11 @@ eap_failure(eap_state *esp, u_char *inp, int id, int len)
        esp->es_client.ea_state = eapBadAuth;
 
        error("EAP: peer reports authentication failure");
+
+#ifdef USE_PEAP
+       peap_finish(&esp->ea_peap);
+#endif
+
        auth_withpeer_fail(esp->es_unit, PPP_EAP);
 }
 
@@ -2952,6 +3061,7 @@ eap_printpkt(u_char *inp, int inlen,
                        break;
 #endif /* USE_EAPTLS */
 
+#ifdef USE_SRP
                case EAPT_SRP:
                        if (len < 3)
                                goto truncated;
@@ -3039,6 +3149,7 @@ eap_printpkt(u_char *inp, int inlen,
                                break;
                        }
                        break;
+#endif  /* USE_SRP */
                }
                break;
 
@@ -3091,7 +3202,7 @@ eap_printpkt(u_char *inp, int inlen,
                        len--;
                        printer(arg, " <Suggested-type %02X", rtype);
                        if (rtype >= 1 &&
-                           rtype < sizeof (eap_typenames) / sizeof (char *))
+                           rtype <= sizeof (eap_typenames) / sizeof (char *))
                                printer(arg, " (%s)", eap_typenames[rtype-1]);
                        printer(arg, ">");
                        break;
@@ -3164,6 +3275,7 @@ eap_printpkt(u_char *inp, int inlen,
                        break;
 #endif /* CHAPMS */
 
+#ifdef USE_SRP
                case EAPT_SRP:
                        if (len < 1)
                                goto truncated;
@@ -3208,6 +3320,7 @@ eap_printpkt(u_char *inp, int inlen,
                                break;
                        }
                        break;
+#endif  /* USE_SRP */
                }
                break;