2 * chap_ms.c - Microsoft MS-CHAP compatible implementation.
4 * Copyright (c) 1995 Eric Rosenquist. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
18 * 3. The name(s) of the authors of this software must not be used to
19 * endorse or promote products derived from this software without
20 * prior written permission.
22 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
23 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
24 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
25 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
26 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
27 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
28 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32 * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
34 * Implemented LANManager type password response to MS-CHAP challenges.
35 * Now pppd provides both NT style and LANMan style blocks, and the
36 * prefered is set by option "ms-lanman". Default is to use NT.
37 * The hash text (StdText) was taken from Win95 RASAPI32.DLL.
39 * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
43 * Modifications by Frank Cusack, frank@google.com, March 2002.
45 * Implemented MS-CHAPv2 functionality, heavily based on sample
46 * implementation in RFC 2759. Implemented MPPE functionality,
47 * heavily based on sample implementation in RFC 3079.
49 * Copyright (c) 2002 Google, Inc. All rights reserved.
51 * Redistribution and use in source and binary forms, with or without
52 * modification, are permitted provided that the following conditions
55 * 1. Redistributions of source code must retain the above copyright
56 * notice, this list of conditions and the following disclaimer.
58 * 2. Redistributions in binary form must reproduce the above copyright
59 * notice, this list of conditions and the following disclaimer in
60 * the documentation and/or other materials provided with the
63 * 3. The name(s) of the authors of this software must not be used to
64 * endorse or promote products derived from this software without
65 * prior written permission.
67 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
68 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
69 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
70 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
71 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
72 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
73 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
77 #define RCSID "$Id: chap_ms.c,v 1.38 2007/12/01 20:10:51 carlsonj Exp $"
87 #include <sys/types.h>
91 #include <net/ppp-comp.h>
93 #include <linux/ppp-comp.h>
101 #include "ppp-crypto.h"
102 #include "pppcrypt.h"
108 static void ascii2unicode (char[], int, u_char[]);
109 static void NTPasswordHash (u_char *, int, unsigned char *);
110 static int ChallengeResponse (u_char *, u_char *, u_char*);
111 static void ChapMS_NT (u_char *, char *, int, u_char[24]);
112 static void ChapMS2_NT (u_char *, u_char[16], char *, char *, int,
114 static void GenerateAuthenticatorResponsePlain
115 (char*, int, u_char[24], u_char[16], u_char *,
117 #ifdef PPP_WITH_MSLANMAN
118 static void ChapMS_LANMan (u_char *, char *, int, u_char *);
121 #ifdef PPP_WITH_MSLANMAN
122 bool ms_lanman = 0; /* Use LanMan password instead of NT */
123 /* Has meaning only with MS-CHAP challenges */
129 /* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
130 static char *mschap_challenge = NULL;
131 /* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
132 static char *mschap2_peer_challenge = NULL;
135 #include "fsm.h" /* Need to poke MPPE options */
140 * Command-line options.
142 static option_t chapms_option_list[] = {
143 #ifdef PPP_WITH_MSLANMAN
144 { "ms-lanman", o_bool, &ms_lanman,
145 "Use LanMan passwd when using MS-CHAP", 1 },
148 { "mschap-challenge", o_string, &mschap_challenge,
149 "specify CHAP challenge" },
150 { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
151 "specify CHAP peer challenge" },
157 * chapms_generate_challenge - generate a challenge for MS-CHAP.
158 * For MS-CHAP the challenge length is fixed at 8 bytes.
159 * The length goes in challenge[0] and the actual challenge starts
163 chapms_generate_challenge(unsigned char *challenge)
167 if (mschap_challenge && strlen(mschap_challenge) == 8)
168 memcpy(challenge, mschap_challenge, 8);
171 random_bytes(challenge, 8);
175 chapms2_generate_challenge(unsigned char *challenge)
179 if (mschap_challenge && strlen(mschap_challenge) == 16)
180 memcpy(challenge, mschap_challenge, 16);
183 random_bytes(challenge, 16);
187 chapms_verify_response(int id, char *name,
188 unsigned char *secret, int secret_len,
189 unsigned char *challenge, unsigned char *response,
190 char *message, int message_space)
192 unsigned char md[MS_CHAP_RESPONSE_LEN];
194 int challenge_len, response_len;
196 challenge_len = *challenge++; /* skip length, is 8 */
197 response_len = *response++;
198 if (response_len != MS_CHAP_RESPONSE_LEN)
201 #ifndef PPP_WITH_MSLANMAN
202 if (!response[MS_CHAP_USENT]) {
203 /* Should really propagate this into the error packet. */
204 notice("Peer request for LANMAN auth not supported");
209 /* Generate the expected response. */
210 ChapMS(challenge, (char *)secret, secret_len, md);
212 #ifdef PPP_WITH_MSLANMAN
213 /* Determine which part of response to verify against */
214 if (!response[MS_CHAP_USENT])
215 diff = memcmp(&response[MS_CHAP_LANMANRESP],
216 &md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN);
219 diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP],
223 slprintf(message, message_space, "Access granted");
228 /* See comments below for MS-CHAP V2 */
229 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
230 challenge_len, challenge);
235 chapms2_verify_response(int id, char *name,
236 unsigned char *secret, int secret_len,
237 unsigned char *challenge, unsigned char *response,
238 char *message, int message_space)
240 unsigned char md[MS_CHAP2_RESPONSE_LEN];
241 char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
242 int challenge_len, response_len;
244 challenge_len = *challenge++; /* skip length, is 16 */
245 response_len = *response++;
246 if (response_len != MS_CHAP2_RESPONSE_LEN)
247 goto bad; /* not even the right length */
249 /* Generate the expected response and our mutual auth. */
250 ChapMS2(challenge, &response[MS_CHAP2_PEER_CHALLENGE], name,
251 (char *)secret, secret_len, md,
252 (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
254 /* compare MDs and send the appropriate status */
256 * Per RFC 2759, success message must be formatted as
257 * "S=<auth_string> M=<message>"
259 * <auth_string> is the Authenticator Response (mutual auth)
260 * <message> is a text message
262 * However, some versions of Windows (win98 tested) do not know
263 * about the M=<message> part (required per RFC 2759) and flag
264 * it as an error (reported incorrectly as an encryption error
265 * to the user). Since the RFC requires it, and it can be
266 * useful information, we supply it if the peer is a conforming
267 * system. Luckily (?), win98 sets the Flags field to 0x04
268 * (contrary to RFC requirements) so we can use that to
269 * distinguish between conforming and non-conforming systems.
271 * Special thanks to Alex Swiridov <say@real.kharkov.ua> for
272 * help debugging this.
274 if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP],
275 MS_CHAP2_NTRESP_LEN) == 0) {
276 if (response[MS_CHAP2_FLAGS])
277 slprintf(message, message_space, "S=%s", saresponse);
279 slprintf(message, message_space, "S=%s M=%s",
280 saresponse, "Access granted");
286 * Failure message must be formatted as
287 * "E=e R=r C=c V=v M=m"
289 * e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
290 * r = retry (we use 1, ok to retry)
291 * c = challenge to use for next response, we reuse previous
292 * v = Change Password version supported, we use 0
295 * The M=m part is only for MS-CHAPv2. Neither win2k nor
296 * win98 (others untested) display the message to the user anyway.
297 * They also both ignore the E=e code.
299 * Note that it's safe to reuse the same challenge as we don't
300 * actually accept another response based on the error message
301 * (and no clients try to resend a response anyway).
303 * Basically, this whole bit is useless code, even the small
304 * implementation here is only because of overspecification.
306 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
307 challenge_len, challenge, "Access denied");
312 chapms_make_response(unsigned char *response, int id, char *our_name,
313 unsigned char *challenge, char *secret, int secret_len,
314 unsigned char *private)
316 challenge++; /* skip length, should be 8 */
317 *response++ = MS_CHAP_RESPONSE_LEN;
318 ChapMS(challenge, secret, secret_len, response);
321 struct chapms2_response_cache_entry {
323 unsigned char challenge[16];
324 unsigned char response[MS_CHAP2_RESPONSE_LEN];
325 unsigned char auth_response[MS_AUTH_RESPONSE_LENGTH];
328 #define CHAPMS2_MAX_RESPONSE_CACHE_SIZE 10
329 static struct chapms2_response_cache_entry
330 chapms2_response_cache[CHAPMS2_MAX_RESPONSE_CACHE_SIZE];
331 static int chapms2_response_cache_next_index = 0;
332 static int chapms2_response_cache_size = 0;
335 chapms2_add_to_response_cache(int id, unsigned char *challenge,
336 unsigned char *response,
337 unsigned char *auth_response)
339 int i = chapms2_response_cache_next_index;
341 chapms2_response_cache[i].id = id;
342 memcpy(chapms2_response_cache[i].challenge, challenge, 16);
343 memcpy(chapms2_response_cache[i].response, response,
344 MS_CHAP2_RESPONSE_LEN);
345 memcpy(chapms2_response_cache[i].auth_response,
346 auth_response, MS_AUTH_RESPONSE_LENGTH);
347 chapms2_response_cache_next_index =
348 (i + 1) % CHAPMS2_MAX_RESPONSE_CACHE_SIZE;
349 if (chapms2_response_cache_next_index > chapms2_response_cache_size)
350 chapms2_response_cache_size = chapms2_response_cache_next_index;
351 dbglog("added response cache entry %d", i);
354 static struct chapms2_response_cache_entry*
355 chapms2_find_in_response_cache(int id, unsigned char *challenge,
356 unsigned char *auth_response)
360 for (i = 0; i < chapms2_response_cache_size; i++) {
361 if (id == chapms2_response_cache[i].id
364 chapms2_response_cache[i].challenge,
367 || memcmp(auth_response,
368 chapms2_response_cache[i].auth_response,
369 MS_AUTH_RESPONSE_LENGTH) == 0)) {
370 dbglog("response found in cache (entry %d)", i);
371 return &chapms2_response_cache[i];
374 return NULL; /* not found */
378 chapms2_make_response(unsigned char *response, int id, char *our_name,
379 unsigned char *challenge, char *secret, int secret_len,
380 unsigned char *private)
382 const struct chapms2_response_cache_entry *cache_entry;
383 unsigned char auth_response[MS_AUTH_RESPONSE_LENGTH+1];
385 challenge++; /* skip length, should be 16 */
386 *response++ = MS_CHAP2_RESPONSE_LEN;
387 cache_entry = chapms2_find_in_response_cache(id, challenge, NULL);
389 memcpy(response, cache_entry->response, MS_CHAP2_RESPONSE_LEN);
394 mschap2_peer_challenge,
398 our_name, secret, secret_len, response, auth_response,
399 MS_CHAP2_AUTHENTICATEE);
400 chapms2_add_to_response_cache(id, challenge, response, auth_response);
404 chapms2_check_success(int id, unsigned char *msg, int len)
406 if ((len < MS_AUTH_RESPONSE_LENGTH + 2) ||
407 strncmp((char *)msg, "S=", 2) != 0) {
408 /* Packet does not start with "S=" */
409 error("MS-CHAPv2 Success packet is badly formed.");
414 if (len < MS_AUTH_RESPONSE_LENGTH
415 || !chapms2_find_in_response_cache(id, NULL /* challenge */, msg)) {
416 /* Authenticator Response did not match expected. */
417 error("MS-CHAPv2 mutual authentication failed.");
420 /* Authenticator Response matches. */
421 msg += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
422 len -= MS_AUTH_RESPONSE_LENGTH;
423 if ((len >= 3) && !strncmp((char *)msg, " M=", 3)) {
424 msg += 3; /* Eat the delimiter */
425 } else if ((len >= 2) && !strncmp((char *)msg, "M=", 2)) {
426 msg += 2; /* Eat the delimiter */
428 /* Packet has extra text which does not begin " M=" */
429 error("MS-CHAPv2 Success packet is badly formed.");
436 chapms_handle_failure(unsigned char *inp, int len)
441 /* We want a null-terminated string for strxxx(). */
442 msg = malloc(len + 1);
444 notice("Out of memory in chapms_handle_failure");
447 BCOPY(inp, msg, len);
452 * Deal with MS-CHAP formatted failure messages; just print the
453 * M=<message> part (if any). For MS-CHAP we're not really supposed
454 * to use M=<message>, but it shouldn't hurt. See
455 * chapms[2]_verify_response.
457 if (!strncmp(p, "E=", 2))
458 err = strtol(p+2, NULL, 10); /* Remember the error code. */
460 goto print_msg; /* Message is badly formatted. */
462 if (len && ((p = strstr(p, " M=")) != NULL)) {
463 /* M=<message> field found. */
466 /* No M=<message>; use the error code. */
468 case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
469 p = "E=646 Restricted logon hours";
472 case MS_CHAP_ERROR_ACCT_DISABLED:
473 p = "E=647 Account disabled";
476 case MS_CHAP_ERROR_PASSWD_EXPIRED:
477 p = "E=648 Password expired";
480 case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
481 p = "E=649 No dialin permission";
484 case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
485 p = "E=691 Authentication failure";
488 case MS_CHAP_ERROR_CHANGING_PASSWORD:
489 /* Should never see this, we don't support Change Password. */
490 p = "E=709 Error changing password";
495 error("Unknown MS-CHAP authentication failure: %.*v",
502 error("MS-CHAP authentication failed: %v", p);
507 ChallengeResponse(u_char *challenge,
508 u_char *PasswordHash,
511 u_char ZPasswordHash[21];
514 BZERO(ZPasswordHash, sizeof(ZPasswordHash));
515 BCOPY(PasswordHash, ZPasswordHash, MD4_DIGEST_LENGTH);
518 dbglog("ChallengeResponse - ZPasswordHash %.*B",
519 sizeof(ZPasswordHash), ZPasswordHash);
522 if (DesEncrypt(challenge, ZPasswordHash + 0, response + 0) &&
523 DesEncrypt(challenge, ZPasswordHash + 7, response + 8) &&
524 DesEncrypt(challenge, ZPasswordHash + 14, response + 16))
528 dbglog("ChallengeResponse - response %.24B", response);
534 ChallengeHash(u_char PeerChallenge[16], u_char *rchallenge,
535 char *username, u_char Challenge[8])
539 u_char hash[SHA_DIGEST_LENGTH];
543 /* remove domain from "domain\username" */
544 if ((user = strrchr(username, '\\')) != NULL)
549 ctx = PPP_MD_CTX_new();
552 if (PPP_DigestInit(ctx, PPP_sha1())) {
554 if (PPP_DigestUpdate(ctx, PeerChallenge, 16)) {
556 if (PPP_DigestUpdate(ctx, rchallenge, 16)) {
558 if (PPP_DigestUpdate(ctx, user, strlen(user))) {
560 hash_len = SHA_DIGEST_LENGTH;
561 if (PPP_DigestFinal(ctx, hash, &hash_len)) {
563 BCOPY(hash, Challenge, 8);
570 PPP_MD_CTX_free(ctx);
575 * Convert the ASCII version of the password to Unicode.
576 * This implicitly supports 8-bit ISO8859/1 characters.
577 * This gives us the little-endian representation, which
578 * is assumed by all M$ CHAP RFCs. (Unicode byte ordering
579 * is machine-dependent.)
582 ascii2unicode(char ascii[], int ascii_len, u_char unicode[])
586 BZERO(unicode, ascii_len * 2);
587 for (i = 0; i < ascii_len; i++)
588 unicode[i * 2] = (u_char) ascii[i];
592 NTPasswordHash(u_char *secret, int secret_len, unsigned char* hash)
594 PPP_MD_CTX* ctx = PPP_MD_CTX_new();
597 if (PPP_DigestInit(ctx, PPP_md4())) {
599 if (PPP_DigestUpdate(ctx, secret, secret_len)) {
601 int hash_len = MD4_DIGEST_LENGTH;
602 PPP_DigestFinal(ctx, hash, &hash_len);
606 PPP_MD_CTX_free(ctx);
611 ChapMS_NT(u_char *rchallenge, char *secret, int secret_len,
612 u_char NTResponse[24])
614 u_char unicodePassword[MAX_NT_PASSWORD * 2];
615 u_char PasswordHash[MD4_DIGEST_LENGTH];
617 /* Hash the Unicode version of the secret (== password). */
618 ascii2unicode(secret, secret_len, unicodePassword);
619 NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
621 ChallengeResponse(rchallenge, PasswordHash, NTResponse);
625 ChapMS2_NT(u_char *rchallenge, u_char PeerChallenge[16], char *username,
626 char *secret, int secret_len, u_char NTResponse[24])
628 u_char unicodePassword[MAX_NT_PASSWORD * 2];
629 u_char PasswordHash[MD4_DIGEST_LENGTH];
632 ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
634 /* Hash the Unicode version of the secret (== password). */
635 ascii2unicode(secret, secret_len, unicodePassword);
636 NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
638 ChallengeResponse(Challenge, PasswordHash, NTResponse);
641 #ifdef PPP_WITH_MSLANMAN
642 static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
645 ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
646 unsigned char *response)
649 u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
650 u_char PasswordHash[MD4_DIGEST_LENGTH];
652 /* LANMan password is case insensitive */
653 BZERO(UcasePassword, sizeof(UcasePassword));
654 for (i = 0; i < secret_len; i++)
655 UcasePassword[i] = (u_char)toupper(secret[i]);
656 (void) DesSetkey(UcasePassword + 0);
657 DesEncrypt( StdText, PasswordHash + 0 );
658 (void) DesSetkey(UcasePassword + 7);
659 DesEncrypt( StdText, PasswordHash + 8 );
660 ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]);
666 GenerateAuthenticatorResponse(unsigned char* PasswordHashHash,
667 unsigned char *NTResponse, unsigned char *PeerChallenge,
668 unsigned char *rchallenge, char *username,
669 unsigned char *authResponse)
672 * "Magic" constants used in response generation, from RFC 2759.
674 u_char Magic1[39] = /* "Magic server to client signing constant" */
675 { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
676 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
677 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
678 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
679 u_char Magic2[41] = /* "Pad to make it do more than one iteration" */
680 { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
681 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
682 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
683 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
688 u_char Digest[SHA_DIGEST_LENGTH];
692 ctx = PPP_MD_CTX_new();
695 if (PPP_DigestInit(ctx, PPP_sha1())) {
697 if (PPP_DigestUpdate(ctx, PasswordHashHash, MD4_DIGEST_LENGTH)) {
699 if (PPP_DigestUpdate(ctx, NTResponse, 24)) {
701 if (PPP_DigestUpdate(ctx, Magic1, sizeof(Magic1))) {
703 hash_len = sizeof(Digest);
704 PPP_DigestFinal(ctx, Digest, &hash_len);
709 PPP_MD_CTX_free(ctx);
712 ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
714 ctx = PPP_MD_CTX_new();
717 if (PPP_DigestInit(ctx, PPP_sha1())) {
719 if (PPP_DigestUpdate(ctx, Digest, sizeof(Digest))) {
721 if (PPP_DigestUpdate(ctx, Challenge, sizeof(Challenge))) {
723 if (PPP_DigestUpdate(ctx, Magic2, sizeof(Magic2))) {
725 hash_len = sizeof(Digest);
726 PPP_DigestFinal(ctx, Digest, &hash_len);
732 PPP_MD_CTX_free(ctx);
735 /* Convert to ASCII hex string. */
736 for (i = 0; i < MAX((MS_AUTH_RESPONSE_LENGTH / 2), sizeof(Digest)); i++) {
737 sprintf((char *)&authResponse[i * 2], "%02X", Digest[i]);
743 GenerateAuthenticatorResponsePlain
744 (char *secret, int secret_len,
745 u_char NTResponse[24], u_char PeerChallenge[16],
746 u_char *rchallenge, char *username,
747 u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
749 u_char unicodePassword[MAX_NT_PASSWORD * 2];
750 u_char PasswordHash[MD4_DIGEST_LENGTH];
751 u_char PasswordHashHash[MD4_DIGEST_LENGTH];
753 /* Hash (x2) the Unicode version of the secret (== password). */
754 ascii2unicode(secret, secret_len, unicodePassword);
755 NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
756 NTPasswordHash(PasswordHash, sizeof(PasswordHash),
759 GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge,
760 rchallenge, username, authResponse);
767 * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079)
770 Set_Start_Key(u_char *rchallenge, char *secret, int secret_len)
772 u_char unicodePassword[MAX_NT_PASSWORD * 2];
773 u_char PasswordHash[MD4_DIGEST_LENGTH];
774 u_char PasswordHashHash[MD4_DIGEST_LENGTH];
776 /* Hash (x2) the Unicode version of the secret (== password). */
777 ascii2unicode(secret, secret_len, unicodePassword);
778 NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
779 NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
781 mppe_set_chapv1(rchallenge, PasswordHashHash);
785 * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
788 SetMasterKeys(char *secret, int secret_len, u_char NTResponse[24], int IsServer)
790 u_char unicodePassword[MAX_NT_PASSWORD * 2];
791 u_char PasswordHash[MD4_DIGEST_LENGTH];
792 u_char PasswordHashHash[MD4_DIGEST_LENGTH];
793 /* Hash (x2) the Unicode version of the secret (== password). */
794 ascii2unicode(secret, secret_len, unicodePassword);
795 NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
796 NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
797 mppe_set_chapv2(PasswordHashHash, NTResponse, IsServer);
800 #endif /* PPP_WITH_MPPE */
804 ChapMS(u_char *rchallenge, char *secret, int secret_len,
805 unsigned char *response)
807 BZERO(response, MS_CHAP_RESPONSE_LEN);
809 ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]);
811 #ifdef PPP_WITH_MSLANMAN
812 ChapMS_LANMan(rchallenge, secret, secret_len,
813 &response[MS_CHAP_LANMANRESP]);
815 /* preferred method is set by option */
816 response[MS_CHAP_USENT] = !ms_lanman;
818 response[MS_CHAP_USENT] = 1;
822 Set_Start_Key(rchallenge, secret, secret_len);
828 * If PeerChallenge is NULL, one is generated and the PeerChallenge
829 * field of response is filled in. Call this way when generating a response.
830 * If PeerChallenge is supplied, it is copied into the PeerChallenge field.
831 * Call this way when verifying a response (or debugging).
832 * Do not call with PeerChallenge = response.
834 * The PeerChallenge field of response is then used for calculation of the
835 * Authenticator Response.
838 ChapMS2(u_char *rchallenge, u_char *PeerChallenge,
839 char *user, char *secret, int secret_len, unsigned char *response,
840 u_char authResponse[], int authenticator)
843 u_char *p = &response[MS_CHAP2_PEER_CHALLENGE];
846 BZERO(response, MS_CHAP2_RESPONSE_LEN);
848 /* Generate the Peer-Challenge if requested, or copy it if supplied. */
850 for (i = 0; i < MS_CHAP2_PEER_CHAL_LEN; i++)
851 *p++ = (u_char) (drand48() * 0xff);
853 BCOPY(PeerChallenge, &response[MS_CHAP2_PEER_CHALLENGE],
854 MS_CHAP2_PEER_CHAL_LEN);
856 /* Generate the NT-Response */
857 ChapMS2_NT(rchallenge, &response[MS_CHAP2_PEER_CHALLENGE], user,
858 secret, secret_len, &response[MS_CHAP2_NTRESP]);
860 /* Generate the Authenticator Response. */
861 GenerateAuthenticatorResponsePlain(secret, secret_len,
862 &response[MS_CHAP2_NTRESP],
863 &response[MS_CHAP2_PEER_CHALLENGE],
864 rchallenge, user, authResponse);
867 SetMasterKeys(secret, secret_len,
868 &response[MS_CHAP2_NTRESP], authenticator);
873 static struct chap_digest_type chapms_digest = {
874 CHAP_MICROSOFT, /* code */
875 chapms_generate_challenge,
876 chapms_verify_response,
877 chapms_make_response,
878 NULL, /* check_success */
879 chapms_handle_failure,
882 static struct chap_digest_type chapms2_digest = {
883 CHAP_MICROSOFT_V2, /* code */
884 chapms2_generate_challenge,
885 chapms2_verify_response,
886 chapms2_make_response,
887 chapms2_check_success,
888 chapms_handle_failure,
895 chap_register_digest(&chapms_digest);
896 chap_register_digest(&chapms2_digest);
897 add_options(chapms_option_list);
907 void random_bytes(unsigned char *bytes, int len)
912 bytes[i++] = (unsigned char) rand();
917 int test_chap_v1(void) {
918 char *secret = "MyPw";
920 unsigned char challenge[8] = {
921 0x10, 0x2D, 0xB5, 0xDF, 0x08, 0x5D, 0x30, 0x41
923 unsigned char response[MS_CHAP_RESPONSE_LEN] = {
925 unsigned char result[MS_CHAP_RESPONSE_LEN] = {
926 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
927 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
928 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
930 0x4E, 0x9D, 0x3C, 0x8F, 0x9C, 0xFD, 0x38, 0x5D,
931 0x5B, 0xF4, 0xD3, 0x24, 0x67, 0x91, 0x95, 0x6C,
932 0xA4, 0xC3, 0x51, 0xAB, 0x40, 0x9A, 0x3D, 0x61,
937 ChapMS(challenge, secret, strlen(secret), response);
938 return memcmp(response, result, MS_CHAP_RESPONSE_LEN);
941 int test_chap_v2(void) {
942 char *secret = "clientPass";
945 char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
946 char *saresult = "407A5589115FD0D6209F510FE9C04566932CDA56";
948 unsigned char authenticator[16] = {
949 0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
950 0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28
952 unsigned char peerchallenge[16] = {
953 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
954 0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E
956 unsigned char result[MS_CHAP_NTRESP_LEN] = {
957 0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E,
958 0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54,
959 0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF
962 unsigned char response[MS_CHAP2_RESPONSE_LEN] = {
965 ChapMS2(authenticator, peerchallenge, name,
966 secret, strlen(secret), response,
967 (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
969 return memcmp(&response[MS_CHAP2_NTRESP], result, MS_CHAP2_NTRESP_LEN) ||
970 strncmp(saresponse, saresult, MS_AUTH_RESPONSE_LENGTH);
973 int main(int argc, char *argv[]) {
977 if (test_chap_v1()) {
978 printf("CHAPv1 failed\n");
982 if (test_chap_v2()) {
983 printf("CHAPv2 failed\n");
993 #endif /* UNIT_TEST */