+#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
+ * succeeded), or 0 if it doesn't.
+ */
+static int
+eap_chap_verify_response(char *name, char *ourname, int id,
+ struct chap_digest_type *digest,
+ unsigned char *challenge, unsigned char *response,
+ char *message, int message_space)
+{
+ int ok;
+ unsigned char secret[MAXSECRETLEN];
+ int secret_len;
+
+ /* Get the secret that the peer is supposed to know */
+ if (!get_secret(0, name, ourname, (char *)secret, &secret_len, 1)) {
+ error("No CHAP secret found for authenticating %q", name);
+ return 0;
+ }
+
+ ok = digest->verify_response(id, name, secret, secret_len, challenge,
+ response, message, message_space);
+ memset(secret, 0, sizeof(secret));
+
+ return ok;
+}
+
+/*
+ * Format and send an CHAPV2-Success/Failure EAP Request message.
+ */
+static void
+eap_chapms2_send_request(eap_state *esp, u_char id,
+ u_char opcode, u_char chapid,
+ char *message, int message_len)
+{
+ u_char *outp;
+ int msglen;
+
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_EAP);
+
+ msglen = EAP_HEADERLEN + 5 * sizeof (u_char);
+ msglen += message_len;
+
+ PUTCHAR(EAP_REQUEST, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(msglen, outp);
+ PUTCHAR(EAPT_MSCHAPV2, outp);
+ PUTCHAR(opcode, outp);
+ PUTCHAR(chapid, outp);
+ /* MS len */
+ PUTSHORT(msglen - 5, outp);
+ BCOPY(message, outp, message_len);
+
+ output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+
+ if (opcode == CHAP_SUCCESS) {
+ auth_peer_success(esp->es_unit, PPP_EAP, 0,
+ esp->es_server.ea_peer, esp->es_server.ea_peerlen);
+ }
+ else {
+ esp->es_server.ea_state = eapBadAuth;
+ auth_peer_fail(esp->es_unit, PPP_EAP);
+ }
+}
+#endif /* CHAPMS */
+