chap_ms.c 33.7 KB
Newer Older
H
HuangXiHans 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
/*
 * chap_ms.c - Microsoft MS-CHAP compatible implementation.
 *
 * Copyright (c) 1995 Eric Rosenquist.  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.
 */

/*
 * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
 *
 *   Implemented LANManager type password response to MS-CHAP challenges.
 *   Now pppd provides both NT style and LANMan style blocks, and the
 *   prefered is set by option "ms-lanman". Default is to use NT.
 *   The hash text (StdText) was taken from Win95 RASAPI32.DLL.
 *
 *   You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
 */

/*
 * Modifications by Frank Cusack, frank@google.com, March 2002.
 *
 *   Implemented MS-CHAPv2 functionality, heavily based on sample
 *   implementation in RFC 2759.  Implemented MPPE functionality,
 *   heavily based on sample implementation in RFC 3079.
 *
 * Copyright (c) 2002 Google, Inc.  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 "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT && MSCHAP_SUPPORT  /* don't build if not configured for use in lwipopts.h */

#if 0 /* UNUSED */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#endif /* UNUSED */

#include "netif/ppp/ppp_impl.h"

#include "netif/ppp/chap-new.h"
#include "netif/ppp/chap_ms.h"
#include "netif/ppp/pppcrypt.h"
#include "netif/ppp/magic.h"
#if MPPE_SUPPORT
#include "netif/ppp/mppe.h" /* For mppe_sha1_pad*, mppe_set_key() */
#endif /* MPPE_SUPPORT */

100 101 102
#define SHA1_SIGNATURE_SIZE 20
#define MD4_SIGNATURE_SIZE  16  /* 16 bytes in a MD4 message digest */
#define MAX_NT_PASSWORD     256 /* Max (Unicode) chars in an NT pass */
H
HuangXiHans 已提交
103

104 105 106 107
#define MS_CHAP_RESPONSE_LEN    49  /* Response length for MS-CHAP */
#define MS_CHAP2_RESPONSE_LEN   49  /* Response length for MS-CHAPv2 */
#define MS_AUTH_RESPONSE_LENGTH 40  /* MS-CHAPv2 authenticator response, */
                    /* as ASCII */
H
HuangXiHans 已提交
108 109

/* Error codes for MS-CHAP failure messages. */
110 111 112 113 114 115
#define MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS    646
#define MS_CHAP_ERROR_ACCT_DISABLED     647
#define MS_CHAP_ERROR_PASSWD_EXPIRED        648
#define MS_CHAP_ERROR_NO_DIALIN_PERMISSION  649
#define MS_CHAP_ERROR_AUTHENTICATION_FAILURE    691
#define MS_CHAP_ERROR_CHANGING_PASSWORD     709
H
HuangXiHans 已提交
116 117 118 119

/*
 * Offsets within the response field for MS-CHAP
 */
120 121 122 123 124
#define MS_CHAP_LANMANRESP  0
#define MS_CHAP_LANMANRESP_LEN  24
#define MS_CHAP_NTRESP      24
#define MS_CHAP_NTRESP_LEN  24
#define MS_CHAP_USENT       48
H
HuangXiHans 已提交
125 126 127 128

/*
 * Offsets within the response field for MS-CHAP2
 */
129 130 131 132 133 134
#define MS_CHAP2_PEER_CHALLENGE 0
#define MS_CHAP2_PEER_CHAL_LEN  16
#define MS_CHAP2_RESERVED_LEN   8
#define MS_CHAP2_NTRESP     24
#define MS_CHAP2_NTRESP_LEN 24
#define MS_CHAP2_FLAGS      48
H
HuangXiHans 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152

#if MPPE_SUPPORT
#if 0 /* UNUSED */
/* These values are the RADIUS attribute values--see RFC 2548. */
#define MPPE_ENC_POL_ENC_ALLOWED 1
#define MPPE_ENC_POL_ENC_REQUIRED 2
#define MPPE_ENC_TYPES_RC4_40 2
#define MPPE_ENC_TYPES_RC4_128 4

/* used by plugins (using above values) */
extern void set_mppe_enc_types(int, int);
#endif /* UNUSED */
#endif /* MPPE_SUPPORT */

/* Are we the authenticator or authenticatee?  For MS-CHAPv2 key derivation. */
#define MS_CHAP2_AUTHENTICATEE 0
#define MS_CHAP2_AUTHENTICATOR 1

153 154 155 156 157 158 159 160 161 162
static void ascii2unicode (const char[], int, u_char[]);
static void NTPasswordHash (u_char *, int, u_char[MD4_SIGNATURE_SIZE]);
static void ChallengeResponse (const u_char *, const u_char *, u_char[24]);
static void ChallengeHash (const u_char[16], const u_char *, const char *, u_char[8]);
static void ChapMS_NT (const u_char *, const char *, int, u_char[24]);
static void ChapMS2_NT (const u_char *, const u_char[16], const char *, const char *, int,
                u_char[24]);
static void GenerateAuthenticatorResponsePlain
            (const char*, int, u_char[24], const u_char[16], const u_char *,
                 const char *, u_char[41]);
H
HuangXiHans 已提交
163
#ifdef MSLANMAN
164
static void ChapMS_LANMan (u_char *, char *, int, u_char *);
H
HuangXiHans 已提交
165 166 167
#endif

static void GenerateAuthenticatorResponse(const u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
168 169 170
            u_char NTResponse[24], const u_char PeerChallenge[16],
            const u_char *rchallenge, const char *username,
            u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]);
H
HuangXiHans 已提交
171 172

#if MPPE_SUPPORT
173 174
static void Set_Start_Key (ppp_pcb *pcb, const u_char *, const char *, int);
static void SetMasterKeys (ppp_pcb *pcb, const char *, int, u_char[24], int);
H
HuangXiHans 已提交
175 176 177 178
#endif /* MPPE_SUPPORT */

static void ChapMS (ppp_pcb *pcb, const u_char *, const char *, int, u_char *);
static void ChapMS2 (ppp_pcb *pcb, const u_char *, const u_char *, const char *, const char *, int,
179
          u_char *, u_char[MS_AUTH_RESPONSE_LENGTH+1], int);
H
HuangXiHans 已提交
180 181

#ifdef MSLANMAN
182 183
bool    ms_lanman = 0;      /* Use LanMan password instead of NT */
                /* Has meaning only with MS-CHAP challenges */
H
HuangXiHans 已提交
184 185 186 187 188 189 190 191 192 193 194
#endif

#if MPPE_SUPPORT
#ifdef DEBUGMPPEKEY
/* For MPPE debug */
/* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
static char *mschap_challenge = NULL;
/* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
static char *mschap2_peer_challenge = NULL;
#endif

195
#include "netif/ppp/fsm.h"      /* Need to poke MPPE options */
H
HuangXiHans 已提交
196 197 198 199 200 201 202 203 204
#include "netif/ppp/ccp.h"
#endif /* MPPE_SUPPORT */

#if PPP_OPTIONS
/*
 * Command-line options.
 */
static option_t chapms_option_list[] = {
#ifdef MSLANMAN
205 206
    { "ms-lanman", o_bool, &ms_lanman,
      "Use LanMan passwd when using MS-CHAP", 1 },
H
HuangXiHans 已提交
207 208
#endif
#ifdef DEBUGMPPEKEY
209 210 211 212
    { "mschap-challenge", o_string, &mschap_challenge,
      "specify CHAP challenge" },
    { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
      "specify CHAP peer challenge" },
H
HuangXiHans 已提交
213
#endif
214
    { NULL }
H
HuangXiHans 已提交
215 216 217 218 219 220 221 222 223 224 225
};
#endif /* PPP_OPTIONS */

#if PPP_SERVER
/*
 * chapms_generate_challenge - generate a challenge for MS-CHAP.
 * For MS-CHAP the challenge length is fixed at 8 bytes.
 * The length goes in challenge[0] and the actual challenge starts
 * at challenge[1].
 */
static void chapms_generate_challenge(ppp_pcb *pcb, unsigned char *challenge) {
226
    LWIP_UNUSED_ARG(pcb);
H
HuangXiHans 已提交
227

228
    *challenge++ = 8;
H
HuangXiHans 已提交
229
#ifdef DEBUGMPPEKEY
230 231 232
    if (mschap_challenge && strlen(mschap_challenge) == 8)
        memcpy(challenge, mschap_challenge, 8);
    else
H
HuangXiHans 已提交
233
#endif
234
        magic_random_bytes(challenge, 8);
H
HuangXiHans 已提交
235 236 237
}

static void chapms2_generate_challenge(ppp_pcb *pcb, unsigned char *challenge) {
238
    LWIP_UNUSED_ARG(pcb);
H
HuangXiHans 已提交
239

240
    *challenge++ = 16;
H
HuangXiHans 已提交
241
#ifdef DEBUGMPPEKEY
242 243 244
    if (mschap_challenge && strlen(mschap_challenge) == 16)
        memcpy(challenge, mschap_challenge, 16);
    else
H
HuangXiHans 已提交
245
#endif
246
        magic_random_bytes(challenge, 16);
H
HuangXiHans 已提交
247 248 249
}

static int chapms_verify_response(ppp_pcb *pcb, int id, const char *name,
250 251 252 253 254 255 256 257 258 259 260 261 262
               const unsigned char *secret, int secret_len,
               const unsigned char *challenge, const unsigned char *response,
               char *message, int message_space) {
    unsigned char md[MS_CHAP_RESPONSE_LEN];
    int diff;
    int challenge_len, response_len;
    LWIP_UNUSED_ARG(id);
    LWIP_UNUSED_ARG(name);

    challenge_len = *challenge++;   /* skip length, is 8 */
    response_len = *response++;
    if (response_len != MS_CHAP_RESPONSE_LEN)
        goto bad;
H
HuangXiHans 已提交
263 264

#ifndef MSLANMAN
265 266 267 268 269
    if (!response[MS_CHAP_USENT]) {
        /* Should really propagate this into the error packet. */
        ppp_notice("Peer request for LANMAN auth not supported");
        goto bad;
    }
H
HuangXiHans 已提交
270 271
#endif

272 273
    /* Generate the expected response. */
    ChapMS(pcb, (const u_char *)challenge, (const char *)secret, secret_len, md);
H
HuangXiHans 已提交
274 275

#ifdef MSLANMAN
276 277 278 279 280
    /* Determine which part of response to verify against */
    if (!response[MS_CHAP_USENT])
        diff = memcmp(&response[MS_CHAP_LANMANRESP],
                  &md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN);
    else
H
HuangXiHans 已提交
281
#endif
282 283
        diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP],
                  MS_CHAP_NTRESP_LEN);
H
HuangXiHans 已提交
284

285 286 287 288
    if (diff == 0) {
        ppp_slprintf(message, message_space, "Access granted");
        return 1;
    }
H
HuangXiHans 已提交
289 290

 bad:
291 292 293 294
    /* See comments below for MS-CHAP V2 */
    ppp_slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
         challenge_len, challenge);
    return 0;
H
HuangXiHans 已提交
295 296 297
}

static int chapms2_verify_response(ppp_pcb *pcb, int id, const char *name,
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
            const unsigned char *secret, int secret_len,
            const unsigned char *challenge, const 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;
    LWIP_UNUSED_ARG(id);

    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(pcb, (const u_char*)challenge, (const u_char*)&response[MS_CHAP2_PEER_CHALLENGE], name,
        (const 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])
            ppp_slprintf(message, message_space, "S=%s", saresponse);
        else
            ppp_slprintf(message, message_space, "S=%s M=%s",
                 saresponse, "Access granted");
        return 1;
    }
H
HuangXiHans 已提交
345 346

 bad:
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
    /*
     * 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.
     */
    ppp_slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
         challenge_len, challenge, "Access denied");
    return 0;
H
HuangXiHans 已提交
371 372 373 374
}
#endif /* PPP_SERVER */

static void chapms_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
375 376 377 378 379 380 381 382
             const unsigned char *challenge, const char *secret, int secret_len,
             unsigned char *private_) {
    LWIP_UNUSED_ARG(id);
    LWIP_UNUSED_ARG(our_name);
    LWIP_UNUSED_ARG(private_);
    challenge++;    /* skip length, should be 8 */
    *response++ = MS_CHAP_RESPONSE_LEN;
    ChapMS(pcb, challenge, secret, secret_len, response);
H
HuangXiHans 已提交
383 384 385
}

static void chapms2_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
386 387 388 389 390 391
              const unsigned char *challenge, const char *secret, int secret_len,
              unsigned char *private_) {
    LWIP_UNUSED_ARG(id);
    challenge++;    /* skip length, should be 16 */
    *response++ = MS_CHAP2_RESPONSE_LEN;
    ChapMS2(pcb, challenge,
H
HuangXiHans 已提交
392
#ifdef DEBUGMPPEKEY
393
        mschap2_peer_challenge,
H
HuangXiHans 已提交
394
#else
395
        NULL,
H
HuangXiHans 已提交
396
#endif
397 398
        our_name, secret, secret_len, response, private_,
        MS_CHAP2_AUTHENTICATEE);
H
HuangXiHans 已提交
399 400 401
}

static int chapms2_check_success(ppp_pcb *pcb, unsigned char *msg, int len, unsigned char *private_) {
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
    LWIP_UNUSED_ARG(pcb);

    if ((len < MS_AUTH_RESPONSE_LENGTH + 2) ||
        strncmp((char *)msg, "S=", 2) != 0) {
        /* Packet does not start with "S=" */
        ppp_error("MS-CHAPv2 Success packet is badly formed.");
        return 0;
    }
    msg += 2;
    len -= 2;
    if (len < MS_AUTH_RESPONSE_LENGTH
        || memcmp(msg, private_, MS_AUTH_RESPONSE_LENGTH)) {
        /* Authenticator Response did not match expected. */
        ppp_error("MS-CHAPv2 mutual authentication failed.");
        return 0;
    }
    /* Authenticator Response matches. */
    msg += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
    len -= MS_AUTH_RESPONSE_LENGTH;
    if ((len >= 3) && !strncmp((char *)msg, " M=", 3)) {
        msg += 3; /* Eat the delimiter */
    } else if (len) {
        /* Packet has extra text which does not begin " M=" */
        ppp_error("MS-CHAPv2 Success packet is badly formed.");
        return 0;
    }
    return 1;
H
HuangXiHans 已提交
429 430 431
}

static void chapms_handle_failure(ppp_pcb *pcb, unsigned char *inp, int len) {
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
    int err;
    const char *p;
    char msg[64];
    LWIP_UNUSED_ARG(pcb);

    /* We want a null-terminated string for strxxx(). */
    len = LWIP_MIN(len, 63);
    MEMCPY(msg, inp, len);
    msg[len] = 0;
    p = msg;

    /*
     * Deal with MS-CHAP formatted failure messages; just print the
     * M=<message> part (if any).  For MS-CHAP we're not really supposed
     * to use M=<message>, but it shouldn't hurt.  See
     * chapms[2]_verify_response.
     */
    if (!strncmp(p, "E=", 2))
        err = strtol(p+2, NULL, 10); /* Remember the error code. */
    else
        goto print_msg; /* Message is badly formatted. */

    if (len && ((p = strstr(p, " M=")) != NULL)) {
        /* M=<message> field found. */
        p += 3;
    } else {
        /* No M=<message>; use the error code. */
        switch (err) {
        case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
            p = "E=646 Restricted logon hours";
            break;

        case MS_CHAP_ERROR_ACCT_DISABLED:
            p = "E=647 Account disabled";
            break;

        case MS_CHAP_ERROR_PASSWD_EXPIRED:
            p = "E=648 Password expired";
            break;

        case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
            p = "E=649 No dialin permission";
            break;

        case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
            p = "E=691 Authentication failure";
            break;

        case MS_CHAP_ERROR_CHANGING_PASSWORD:
            /* Should never see this, we don't support Change Password. */
            p = "E=709 Error changing password";
            break;

        default:
            ppp_error("Unknown MS-CHAP authentication failure: %.*v",
                  len, inp);
            return;
        }
    }
H
HuangXiHans 已提交
491
print_msg:
492 493
    if (p != NULL)
        ppp_error("MS-CHAP authentication failed: %v", p);
H
HuangXiHans 已提交
494 495 496
}

static void ChallengeResponse(const u_char *challenge,
497 498
          const u_char PasswordHash[MD4_SIGNATURE_SIZE],
          u_char response[24]) {
H
HuangXiHans 已提交
499 500 501 502 503 504 505 506 507
    u_char    ZPasswordHash[21];
    lwip_des_context des;
    u_char des_key[8];

    BZERO(ZPasswordHash, sizeof(ZPasswordHash));
    MEMCPY(ZPasswordHash, PasswordHash, MD4_SIGNATURE_SIZE);

#if 0
    dbglog("ChallengeResponse - ZPasswordHash %.*B",
508
       sizeof(ZPasswordHash), ZPasswordHash);
H
HuangXiHans 已提交
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
#endif

    pppcrypt_56_to_64_bit_key(ZPasswordHash + 0, des_key);
    lwip_des_init(&des);
    lwip_des_setkey_enc(&des, des_key);
    lwip_des_crypt_ecb(&des, challenge, response +0);
    lwip_des_free(&des);

    pppcrypt_56_to_64_bit_key(ZPasswordHash + 7, des_key);
    lwip_des_init(&des);
    lwip_des_setkey_enc(&des, des_key);
    lwip_des_crypt_ecb(&des, challenge, response +8);
    lwip_des_free(&des);

    pppcrypt_56_to_64_bit_key(ZPasswordHash + 14, des_key);
    lwip_des_init(&des);
    lwip_des_setkey_enc(&des, des_key);
    lwip_des_crypt_ecb(&des, challenge, response +16);
    lwip_des_free(&des);

#if 0
    dbglog("ChallengeResponse - response %.24B", response);
#endif
}

static void ChallengeHash(const u_char PeerChallenge[16], const u_char *rchallenge,
535 536 537 538
          const char *username, u_char Challenge[8]) {
    lwip_sha1_context   sha1Context;
    u_char  sha1Hash[SHA1_SIGNATURE_SIZE];
    const char  *user;
H
HuangXiHans 已提交
539 540 541

    /* remove domain from "domain\username" */
    if ((user = strrchr(username, '\\')) != NULL)
542
    ++user;
H
HuangXiHans 已提交
543
    else
544
    user = username;
H
HuangXiHans 已提交
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568

    lwip_sha1_init(&sha1Context);
    lwip_sha1_starts(&sha1Context);
    lwip_sha1_update(&sha1Context, PeerChallenge, 16);
    lwip_sha1_update(&sha1Context, rchallenge, 16);
    lwip_sha1_update(&sha1Context, (const unsigned char*)user, strlen(user));
    lwip_sha1_finish(&sha1Context, sha1Hash);
    lwip_sha1_free(&sha1Context);

    MEMCPY(Challenge, sha1Hash, 8);
}

/*
 * Convert the ASCII version of the password to Unicode.
 * This implicitly supports 8-bit ISO8859/1 characters.
 * This gives us the little-endian representation, which
 * is assumed by all M$ CHAP RFCs.  (Unicode byte ordering
 * is machine-dependent.)
 */
static void ascii2unicode(const char ascii[], int ascii_len, u_char unicode[]) {
    int i;

    BZERO(unicode, ascii_len * 2);
    for (i = 0; i < ascii_len; i++)
569
    unicode[i * 2] = (u_char) ascii[i];
H
HuangXiHans 已提交
570 571 572
}

static void NTPasswordHash(u_char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE]) {
573
    lwip_md4_context        md4Context;
H
HuangXiHans 已提交
574 575 576 577 578 579 580 581 582

    lwip_md4_init(&md4Context);
    lwip_md4_starts(&md4Context);
    lwip_md4_update(&md4Context, secret, secret_len);
    lwip_md4_finish(&md4Context, hash);
    lwip_md4_free(&md4Context);
}

static void ChapMS_NT(const u_char *rchallenge, const char *secret, int secret_len,
583 584 585
      u_char NTResponse[24]) {
    u_char  unicodePassword[MAX_NT_PASSWORD * 2];
    u_char  PasswordHash[MD4_SIGNATURE_SIZE];
H
HuangXiHans 已提交
586 587 588 589 590 591 592 593 594

    /* Hash the Unicode version of the secret (== password). */
    ascii2unicode(secret, secret_len, unicodePassword);
    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);

    ChallengeResponse(rchallenge, PasswordHash, NTResponse);
}

static void ChapMS2_NT(const u_char *rchallenge, const u_char PeerChallenge[16], const char *username,
595 596 597 598
       const char *secret, int secret_len, u_char NTResponse[24]) {
    u_char  unicodePassword[MAX_NT_PASSWORD * 2];
    u_char  PasswordHash[MD4_SIGNATURE_SIZE];
    u_char  Challenge[8];
H
HuangXiHans 已提交
599 600 601 602 603 604 605 606 607 608 609 610 611 612

    ChallengeHash(PeerChallenge, rchallenge, username, Challenge);

    /* Hash the Unicode version of the secret (== password). */
    ascii2unicode(secret, secret_len, unicodePassword);
    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);

    ChallengeResponse(Challenge, PasswordHash, NTResponse);
}

#ifdef MSLANMAN
static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */

static void ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
613 614 615 616
          unsigned char *response) {
    int         i;
    u_char      UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
    u_char      PasswordHash[MD4_SIGNATURE_SIZE];
H
HuangXiHans 已提交
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
    lwip_des_context des;
    u_char des_key[8];

    /* LANMan password is case insensitive */
    BZERO(UcasePassword, sizeof(UcasePassword));
    for (i = 0; i < secret_len; i++)
       UcasePassword[i] = (u_char)toupper(secret[i]);

    pppcrypt_56_to_64_bit_key(UcasePassword +0, des_key);
    lwip_des_init(&des);
    lwip_des_setkey_enc(&des, des_key);
    lwip_des_crypt_ecb(&des, StdText, PasswordHash +0);
    lwip_des_free(&des);

    pppcrypt_56_to_64_bit_key(UcasePassword +7, des_key);
    lwip_des_init(&des);
    lwip_des_setkey_enc(&des, des_key);
    lwip_des_crypt_ecb(&des, StdText, PasswordHash +8);
    lwip_des_free(&des);

    ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]);
}
#endif


static void GenerateAuthenticatorResponse(const u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
643 644 645
                  u_char NTResponse[24], const u_char PeerChallenge[16],
                  const u_char *rchallenge, const char *username,
                  u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]) {
H
HuangXiHans 已提交
646 647 648 649
    /*
     * "Magic" constants used in response generation, from RFC 2759.
     */
    static const u_char Magic1[39] = /* "Magic server to client signing constant" */
650 651 652 653
    { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
      0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
      0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
      0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
H
HuangXiHans 已提交
654
    static const u_char Magic2[41] = /* "Pad to make it do more than one iteration" */
655 656 657 658 659
    { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
      0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
      0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
      0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
      0x6E };
H
HuangXiHans 已提交
660

661 662 663 664
    int     i;
    lwip_sha1_context   sha1Context;
    u_char  Digest[SHA1_SIGNATURE_SIZE];
    u_char  Challenge[8];
H
HuangXiHans 已提交
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685

    lwip_sha1_init(&sha1Context);
    lwip_sha1_starts(&sha1Context);
    lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
    lwip_sha1_update(&sha1Context, NTResponse, 24);
    lwip_sha1_update(&sha1Context, Magic1, sizeof(Magic1));
    lwip_sha1_finish(&sha1Context, Digest);
    lwip_sha1_free(&sha1Context);

    ChallengeHash(PeerChallenge, rchallenge, username, Challenge);

    lwip_sha1_init(&sha1Context);
    lwip_sha1_starts(&sha1Context);
    lwip_sha1_update(&sha1Context, Digest, sizeof(Digest));
    lwip_sha1_update(&sha1Context, Challenge, sizeof(Challenge));
    lwip_sha1_update(&sha1Context, Magic2, sizeof(Magic2));
    lwip_sha1_finish(&sha1Context, Digest);
    lwip_sha1_free(&sha1Context);

    /* Convert to ASCII hex string. */
    for (i = 0; i < LWIP_MAX((MS_AUTH_RESPONSE_LENGTH / 2), (int)sizeof(Digest)); i++)
686
    sprintf((char *)&authResponse[i * 2], "%02X", Digest[i]);
H
HuangXiHans 已提交
687 688 689 690
}


static void GenerateAuthenticatorResponsePlain(
691 692 693 694 695 696 697
         const char *secret, int secret_len,
         u_char NTResponse[24], const u_char PeerChallenge[16],
         const u_char *rchallenge, const char *username,
         u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]) {
    u_char  unicodePassword[MAX_NT_PASSWORD * 2];
    u_char  PasswordHash[MD4_SIGNATURE_SIZE];
    u_char  PasswordHashHash[MD4_SIGNATURE_SIZE];
H
HuangXiHans 已提交
698 699 700 701 702

    /* Hash (x2) the Unicode version of the secret (== password). */
    ascii2unicode(secret, secret_len, unicodePassword);
    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
    NTPasswordHash(PasswordHash, sizeof(PasswordHash),
703
           PasswordHashHash);
H
HuangXiHans 已提交
704 705

    GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge,
706
                  rchallenge, username, authResponse);
H
HuangXiHans 已提交
707 708 709 710 711 712 713 714
}


#if MPPE_SUPPORT
/*
 * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079)
 */
static void Set_Start_Key(ppp_pcb *pcb, const u_char *rchallenge, const char *secret, int secret_len) {
715 716 717 718 719
    u_char  unicodePassword[MAX_NT_PASSWORD * 2];
    u_char  PasswordHash[MD4_SIGNATURE_SIZE];
    u_char  PasswordHashHash[MD4_SIGNATURE_SIZE];
    lwip_sha1_context   sha1Context;
    u_char  Digest[SHA1_SIGNATURE_SIZE];    /* >= MPPE_MAX_KEY_LEN */
H
HuangXiHans 已提交
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744

    /* Hash (x2) the Unicode version of the secret (== password). */
    ascii2unicode(secret, secret_len, unicodePassword);
    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
    NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);

    lwip_sha1_init(&sha1Context);
    lwip_sha1_starts(&sha1Context);
    lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
    lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
    lwip_sha1_update(&sha1Context, rchallenge, 8);
    lwip_sha1_finish(&sha1Context, Digest);
    lwip_sha1_free(&sha1Context);

    /* Same key in both directions. */
    mppe_set_key(pcb, &pcb->mppe_comp, Digest);
    mppe_set_key(pcb, &pcb->mppe_decomp, Digest);

    pcb->mppe_keys_set = 1;
}

/*
 * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
 */
static void SetMasterKeys(ppp_pcb *pcb, const char *secret, int secret_len, u_char NTResponse[24], int IsServer) {
745 746 747 748 749 750
    u_char  unicodePassword[MAX_NT_PASSWORD * 2];
    u_char  PasswordHash[MD4_SIGNATURE_SIZE];
    u_char  PasswordHashHash[MD4_SIGNATURE_SIZE];
    lwip_sha1_context   sha1Context;
    u_char  MasterKey[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */
    u_char  Digest[SHA1_SIGNATURE_SIZE];    /* >= MPPE_MAX_KEY_LEN */
H
HuangXiHans 已提交
751 752 753 754
    const u_char *s;

    /* "This is the MPPE Master Key" */
    static const u_char Magic1[27] =
755 756 757
    { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
      0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
      0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
H
HuangXiHans 已提交
758 759 760
    /* "On the client side, this is the send key; "
       "on the server side, it is the receive key." */
    static const u_char Magic2[84] =
761 762 763 764 765 766 767 768 769
    { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
      0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
      0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
      0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
      0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
      0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
      0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
      0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
      0x6b, 0x65, 0x79, 0x2e };
H
HuangXiHans 已提交
770 771 772
    /* "On the client side, this is the receive key; "
       "on the server side, it is the send key." */
    static const u_char Magic3[84] =
773 774 775 776 777 778 779 780 781
    { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
      0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
      0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
      0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
      0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
      0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
      0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
      0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
      0x6b, 0x65, 0x79, 0x2e };
H
HuangXiHans 已提交
782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799

    /* Hash (x2) the Unicode version of the secret (== password). */
    ascii2unicode(secret, secret_len, unicodePassword);
    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
    NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);

    lwip_sha1_init(&sha1Context);
    lwip_sha1_starts(&sha1Context);
    lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
    lwip_sha1_update(&sha1Context, NTResponse, 24);
    lwip_sha1_update(&sha1Context, Magic1, sizeof(Magic1));
    lwip_sha1_finish(&sha1Context, MasterKey);
    lwip_sha1_free(&sha1Context);

    /*
     * generate send key
     */
    if (IsServer)
800
    s = Magic3;
H
HuangXiHans 已提交
801
    else
802
    s = Magic2;
H
HuangXiHans 已提交
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
    lwip_sha1_init(&sha1Context);
    lwip_sha1_starts(&sha1Context);
    lwip_sha1_update(&sha1Context, MasterKey, 16);
    lwip_sha1_update(&sha1Context, mppe_sha1_pad1, SHA1_PAD_SIZE);
    lwip_sha1_update(&sha1Context, s, 84);
    lwip_sha1_update(&sha1Context, mppe_sha1_pad2, SHA1_PAD_SIZE);
    lwip_sha1_finish(&sha1Context, Digest);
    lwip_sha1_free(&sha1Context);

    mppe_set_key(pcb, &pcb->mppe_comp, Digest);

    /*
     * generate recv key
     */
    if (IsServer)
818
    s = Magic2;
H
HuangXiHans 已提交
819
    else
820
    s = Magic3;
H
HuangXiHans 已提交
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848
    lwip_sha1_init(&sha1Context);
    lwip_sha1_starts(&sha1Context);
    lwip_sha1_update(&sha1Context, MasterKey, 16);
    lwip_sha1_update(&sha1Context, mppe_sha1_pad1, SHA1_PAD_SIZE);
    lwip_sha1_update(&sha1Context, s, 84);
    lwip_sha1_update(&sha1Context, mppe_sha1_pad2, SHA1_PAD_SIZE);
    lwip_sha1_finish(&sha1Context, Digest);
    lwip_sha1_free(&sha1Context);

    mppe_set_key(pcb, &pcb->mppe_decomp, Digest);

    pcb->mppe_keys_set = 1;
}

#endif /* MPPE_SUPPORT */


static void ChapMS(ppp_pcb *pcb, const u_char *rchallenge, const char *secret, int secret_len,
       unsigned char *response) {
#if !MPPE_SUPPORT
    LWIP_UNUSED_ARG(pcb);
#endif /* !MPPE_SUPPORT */
    BZERO(response, MS_CHAP_RESPONSE_LEN);

    ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]);

#ifdef MSLANMAN
    ChapMS_LANMan(rchallenge, secret, secret_len,
849
          &response[MS_CHAP_LANMANRESP]);
H
HuangXiHans 已提交
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873

    /* preferred method is set by option  */
    response[MS_CHAP_USENT] = !ms_lanman;
#else
    response[MS_CHAP_USENT] = 1;
#endif

#if MPPE_SUPPORT
    Set_Start_Key(pcb, rchallenge, secret, secret_len);
#endif /* MPPE_SUPPORT */
}


/*
 * If PeerChallenge is NULL, one is generated and the PeerChallenge
 * field of response is filled in.  Call this way when generating a response.
 * If PeerChallenge is supplied, it is copied into the PeerChallenge field.
 * Call this way when verifying a response (or debugging).
 * Do not call with PeerChallenge = response.
 *
 * The PeerChallenge field of response is then used for calculation of the
 * Authenticator Response.
 */
static void ChapMS2(ppp_pcb *pcb, const u_char *rchallenge, const u_char *PeerChallenge,
874 875
    const char *user, const char *secret, int secret_len, unsigned char *response,
    u_char authResponse[], int authenticator) {
H
HuangXiHans 已提交
876 877 878 879 880 881 882 883 884 885
    /* ARGSUSED */
    LWIP_UNUSED_ARG(authenticator);
#if !MPPE_SUPPORT
    LWIP_UNUSED_ARG(pcb);
#endif /* !MPPE_SUPPORT */

    BZERO(response, MS_CHAP2_RESPONSE_LEN);

    /* Generate the Peer-Challenge if requested, or copy it if supplied. */
    if (!PeerChallenge)
886
    magic_random_bytes(&response[MS_CHAP2_PEER_CHALLENGE], MS_CHAP2_PEER_CHAL_LEN);
H
HuangXiHans 已提交
887
    else
888 889
    MEMCPY(&response[MS_CHAP2_PEER_CHALLENGE], PeerChallenge,
          MS_CHAP2_PEER_CHAL_LEN);
H
HuangXiHans 已提交
890 891 892

    /* Generate the NT-Response */
    ChapMS2_NT(rchallenge, &response[MS_CHAP2_PEER_CHALLENGE], user,
893
           secret, secret_len, &response[MS_CHAP2_NTRESP]);
H
HuangXiHans 已提交
894 895 896

    /* Generate the Authenticator Response. */
    GenerateAuthenticatorResponsePlain(secret, secret_len,
897 898 899
                       &response[MS_CHAP2_NTRESP],
                       &response[MS_CHAP2_PEER_CHALLENGE],
                       rchallenge, user, authResponse);
H
HuangXiHans 已提交
900 901 902

#if MPPE_SUPPORT
    SetMasterKeys(pcb, secret, secret_len,
903
          &response[MS_CHAP2_NTRESP], authenticator);
H
HuangXiHans 已提交
904 905 906 907 908 909 910 911 912 913 914
#endif /* MPPE_SUPPORT */
}

#if 0 /* UNUSED */
#if MPPE_SUPPORT
/*
 * Set MPPE options from plugins.
 */
void set_mppe_enc_types(int policy, int types) {
    /* Early exit for unknown policies. */
    if (policy != MPPE_ENC_POL_ENC_ALLOWED ||
915 916
    policy != MPPE_ENC_POL_ENC_REQUIRED)
    return;
H
HuangXiHans 已提交
917 918 919

    /* Don't modify MPPE if it's optional and wasn't already configured. */
    if (policy == MPPE_ENC_POL_ENC_ALLOWED && !ccp_wantoptions[0].mppe)
920
    return;
H
HuangXiHans 已提交
921 922 923 924 925 926

    /*
     * Disable undesirable encryption types.  Note that we don't ENABLE
     * any encryption types, to avoid overriding manual configuration.
     */
    switch(types) {
927 928 929 930 931 932 933 934
    case MPPE_ENC_TYPES_RC4_40:
        ccp_wantoptions[0].mppe &= ~MPPE_OPT_128;   /* disable 128-bit */
        break;
    case MPPE_ENC_TYPES_RC4_128:
        ccp_wantoptions[0].mppe &= ~MPPE_OPT_40;    /* disable 40-bit */
        break;
    default:
        break;
H
HuangXiHans 已提交
935 936 937 938 939 940
    }
}
#endif /* MPPE_SUPPORT */
#endif /* UNUSED */

const struct chap_digest_type chapms_digest = {
941
    CHAP_MICROSOFT,     /* code */
H
HuangXiHans 已提交
942
#if PPP_SERVER
943 944
    chapms_generate_challenge,
    chapms_verify_response,
H
HuangXiHans 已提交
945
#endif /* PPP_SERVER */
946 947 948
    chapms_make_response,
    NULL,           /* check_success */
    chapms_handle_failure,
H
HuangXiHans 已提交
949 950 951
};

const struct chap_digest_type chapms2_digest = {
952
    CHAP_MICROSOFT_V2,  /* code */
H
HuangXiHans 已提交
953
#if PPP_SERVER
954 955
    chapms2_generate_challenge,
    chapms2_verify_response,
H
HuangXiHans 已提交
956
#endif /* PPP_SERVER */
957 958 959
    chapms2_make_response,
    chapms2_check_success,
    chapms_handle_failure,
H
HuangXiHans 已提交
960 961 962
};

#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */