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>
104 static void ascii2unicode (char[], int, u_char[]);
105 static void NTPasswordHash (u_char *, int, u_char[MD4_SIGNATURE_SIZE]);
106 static void ChallengeResponse (u_char *, u_char *, u_char[24]);
107 static void ChapMS_NT (u_char *, char *, int, u_char[24]);
108 static void ChapMS2_NT (u_char *, u_char[16], char *, char *, int,
110 static void GenerateAuthenticatorResponsePlain
111 (char*, int, u_char[24], u_char[16], u_char *,
113 #ifdef PPP_WITH_MSLANMAN
114 static void ChapMS_LANMan (u_char *, char *, int, u_char *);
117 #ifdef PPP_WITH_MSLANMAN
118 bool ms_lanman = 0; /* Use LanMan password instead of NT */
119 /* Has meaning only with MS-CHAP challenges */
125 /* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
126 static char *mschap_challenge = NULL;
127 /* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
128 static char *mschap2_peer_challenge = NULL;
131 #include "fsm.h" /* Need to poke MPPE options */
133 #include <net/ppp-comp.h>
137 * Command-line options.
139 static option_t chapms_option_list[] = {
140 #ifdef PPP_WITH_MSLANMAN
141 { "ms-lanman", o_bool, &ms_lanman,
142 "Use LanMan passwd when using MS-CHAP", 1 },
145 { "mschap-challenge", o_string, &mschap_challenge,
146 "specify CHAP challenge" },
147 { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
148 "specify CHAP peer challenge" },
154 * chapms_generate_challenge - generate a challenge for MS-CHAP.
155 * For MS-CHAP the challenge length is fixed at 8 bytes.
156 * The length goes in challenge[0] and the actual challenge starts
160 chapms_generate_challenge(unsigned char *challenge)
164 if (mschap_challenge && strlen(mschap_challenge) == 8)
165 memcpy(challenge, mschap_challenge, 8);
168 random_bytes(challenge, 8);
172 chapms2_generate_challenge(unsigned char *challenge)
176 if (mschap_challenge && strlen(mschap_challenge) == 16)
177 memcpy(challenge, mschap_challenge, 16);
180 random_bytes(challenge, 16);
184 chapms_verify_response(int id, char *name,
185 unsigned char *secret, int secret_len,
186 unsigned char *challenge, unsigned char *response,
187 char *message, int message_space)
189 unsigned char md[MS_CHAP_RESPONSE_LEN];
191 int challenge_len, response_len;
193 challenge_len = *challenge++; /* skip length, is 8 */
194 response_len = *response++;
195 if (response_len != MS_CHAP_RESPONSE_LEN)
198 #ifndef PPP_WITH_MSLANMAN
199 if (!response[MS_CHAP_USENT]) {
200 /* Should really propagate this into the error packet. */
201 notice("Peer request for LANMAN auth not supported");
206 /* Generate the expected response. */
207 ChapMS(challenge, (char *)secret, secret_len, md);
209 #ifdef PPP_WITH_MSLANMAN
210 /* Determine which part of response to verify against */
211 if (!response[MS_CHAP_USENT])
212 diff = memcmp(&response[MS_CHAP_LANMANRESP],
213 &md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN);
216 diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP],
220 slprintf(message, message_space, "Access granted");
225 /* See comments below for MS-CHAP V2 */
226 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
227 challenge_len, challenge);
232 chapms2_verify_response(int id, char *name,
233 unsigned char *secret, int secret_len,
234 unsigned char *challenge, unsigned char *response,
235 char *message, int message_space)
237 unsigned char md[MS_CHAP2_RESPONSE_LEN];
238 char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
239 int challenge_len, response_len;
241 challenge_len = *challenge++; /* skip length, is 16 */
242 response_len = *response++;
243 if (response_len != MS_CHAP2_RESPONSE_LEN)
244 goto bad; /* not even the right length */
246 /* Generate the expected response and our mutual auth. */
247 ChapMS2(challenge, &response[MS_CHAP2_PEER_CHALLENGE], name,
248 (char *)secret, secret_len, md,
249 (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
251 /* compare MDs and send the appropriate status */
253 * Per RFC 2759, success message must be formatted as
254 * "S=<auth_string> M=<message>"
256 * <auth_string> is the Authenticator Response (mutual auth)
257 * <message> is a text message
259 * However, some versions of Windows (win98 tested) do not know
260 * about the M=<message> part (required per RFC 2759) and flag
261 * it as an error (reported incorrectly as an encryption error
262 * to the user). Since the RFC requires it, and it can be
263 * useful information, we supply it if the peer is a conforming
264 * system. Luckily (?), win98 sets the Flags field to 0x04
265 * (contrary to RFC requirements) so we can use that to
266 * distinguish between conforming and non-conforming systems.
268 * Special thanks to Alex Swiridov <say@real.kharkov.ua> for
269 * help debugging this.
271 if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP],
272 MS_CHAP2_NTRESP_LEN) == 0) {
273 if (response[MS_CHAP2_FLAGS])
274 slprintf(message, message_space, "S=%s", saresponse);
276 slprintf(message, message_space, "S=%s M=%s",
277 saresponse, "Access granted");
283 * Failure message must be formatted as
284 * "E=e R=r C=c V=v M=m"
286 * e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
287 * r = retry (we use 1, ok to retry)
288 * c = challenge to use for next response, we reuse previous
289 * v = Change Password version supported, we use 0
292 * The M=m part is only for MS-CHAPv2. Neither win2k nor
293 * win98 (others untested) display the message to the user anyway.
294 * They also both ignore the E=e code.
296 * Note that it's safe to reuse the same challenge as we don't
297 * actually accept another response based on the error message
298 * (and no clients try to resend a response anyway).
300 * Basically, this whole bit is useless code, even the small
301 * implementation here is only because of overspecification.
303 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
304 challenge_len, challenge, "Access denied");
309 chapms_make_response(unsigned char *response, int id, char *our_name,
310 unsigned char *challenge, char *secret, int secret_len,
311 unsigned char *private)
313 challenge++; /* skip length, should be 8 */
314 *response++ = MS_CHAP_RESPONSE_LEN;
315 ChapMS(challenge, secret, secret_len, response);
318 struct chapms2_response_cache_entry {
320 unsigned char challenge[16];
321 unsigned char response[MS_CHAP2_RESPONSE_LEN];
322 unsigned char auth_response[MS_AUTH_RESPONSE_LENGTH];
325 #define CHAPMS2_MAX_RESPONSE_CACHE_SIZE 10
326 static struct chapms2_response_cache_entry
327 chapms2_response_cache[CHAPMS2_MAX_RESPONSE_CACHE_SIZE];
328 static int chapms2_response_cache_next_index = 0;
329 static int chapms2_response_cache_size = 0;
332 chapms2_add_to_response_cache(int id, unsigned char *challenge,
333 unsigned char *response,
334 unsigned char *auth_response)
336 int i = chapms2_response_cache_next_index;
338 chapms2_response_cache[i].id = id;
339 memcpy(chapms2_response_cache[i].challenge, challenge, 16);
340 memcpy(chapms2_response_cache[i].response, response,
341 MS_CHAP2_RESPONSE_LEN);
342 memcpy(chapms2_response_cache[i].auth_response,
343 auth_response, MS_AUTH_RESPONSE_LENGTH);
344 chapms2_response_cache_next_index =
345 (i + 1) % CHAPMS2_MAX_RESPONSE_CACHE_SIZE;
346 if (chapms2_response_cache_next_index > chapms2_response_cache_size)
347 chapms2_response_cache_size = chapms2_response_cache_next_index;
348 dbglog("added response cache entry %d", i);
351 static struct chapms2_response_cache_entry*
352 chapms2_find_in_response_cache(int id, unsigned char *challenge,
353 unsigned char *auth_response)
357 for (i = 0; i < chapms2_response_cache_size; i++) {
358 if (id == chapms2_response_cache[i].id
361 chapms2_response_cache[i].challenge,
364 || memcmp(auth_response,
365 chapms2_response_cache[i].auth_response,
366 MS_AUTH_RESPONSE_LENGTH) == 0)) {
367 dbglog("response found in cache (entry %d)", i);
368 return &chapms2_response_cache[i];
371 return NULL; /* not found */
375 chapms2_make_response(unsigned char *response, int id, char *our_name,
376 unsigned char *challenge, char *secret, int secret_len,
377 unsigned char *private)
379 const struct chapms2_response_cache_entry *cache_entry;
380 unsigned char auth_response[MS_AUTH_RESPONSE_LENGTH+1];
382 challenge++; /* skip length, should be 16 */
383 *response++ = MS_CHAP2_RESPONSE_LEN;
384 cache_entry = chapms2_find_in_response_cache(id, challenge, NULL);
386 memcpy(response, cache_entry->response, MS_CHAP2_RESPONSE_LEN);
391 mschap2_peer_challenge,
395 our_name, secret, secret_len, response, auth_response,
396 MS_CHAP2_AUTHENTICATEE);
397 chapms2_add_to_response_cache(id, challenge, response, auth_response);
401 chapms2_check_success(int id, unsigned char *msg, int len)
403 if ((len < MS_AUTH_RESPONSE_LENGTH + 2) ||
404 strncmp((char *)msg, "S=", 2) != 0) {
405 /* Packet does not start with "S=" */
406 error("MS-CHAPv2 Success packet is badly formed.");
411 if (len < MS_AUTH_RESPONSE_LENGTH
412 || !chapms2_find_in_response_cache(id, NULL /* challenge */, msg)) {
413 /* Authenticator Response did not match expected. */
414 error("MS-CHAPv2 mutual authentication failed.");
417 /* Authenticator Response matches. */
418 msg += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
419 len -= MS_AUTH_RESPONSE_LENGTH;
420 if ((len >= 3) && !strncmp((char *)msg, " M=", 3)) {
421 msg += 3; /* Eat the delimiter */
422 } else if ((len >= 2) && !strncmp((char *)msg, "M=", 2)) {
423 msg += 2; /* Eat the delimiter */
425 /* Packet has extra text which does not begin " M=" */
426 error("MS-CHAPv2 Success packet is badly formed.");
433 chapms_handle_failure(unsigned char *inp, int len)
438 /* We want a null-terminated string for strxxx(). */
439 msg = malloc(len + 1);
441 notice("Out of memory in chapms_handle_failure");
444 BCOPY(inp, msg, len);
449 * Deal with MS-CHAP formatted failure messages; just print the
450 * M=<message> part (if any). For MS-CHAP we're not really supposed
451 * to use M=<message>, but it shouldn't hurt. See
452 * chapms[2]_verify_response.
454 if (!strncmp(p, "E=", 2))
455 err = strtol(p+2, NULL, 10); /* Remember the error code. */
457 goto print_msg; /* Message is badly formatted. */
459 if (len && ((p = strstr(p, " M=")) != NULL)) {
460 /* M=<message> field found. */
463 /* No M=<message>; use the error code. */
465 case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
466 p = "E=646 Restricted logon hours";
469 case MS_CHAP_ERROR_ACCT_DISABLED:
470 p = "E=647 Account disabled";
473 case MS_CHAP_ERROR_PASSWD_EXPIRED:
474 p = "E=648 Password expired";
477 case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
478 p = "E=649 No dialin permission";
481 case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
482 p = "E=691 Authentication failure";
485 case MS_CHAP_ERROR_CHANGING_PASSWORD:
486 /* Should never see this, we don't support Change Password. */
487 p = "E=709 Error changing password";
492 error("Unknown MS-CHAP authentication failure: %.*v",
499 error("MS-CHAP authentication failed: %v", p);
504 ChallengeResponse(u_char *challenge,
505 u_char PasswordHash[MD4_SIGNATURE_SIZE],
508 u_char ZPasswordHash[21];
510 BZERO(ZPasswordHash, sizeof(ZPasswordHash));
511 BCOPY(PasswordHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
514 dbglog("ChallengeResponse - ZPasswordHash %.*B",
515 sizeof(ZPasswordHash), ZPasswordHash);
518 (void) DesSetkey(ZPasswordHash + 0);
519 DesEncrypt(challenge, response + 0);
520 (void) DesSetkey(ZPasswordHash + 7);
521 DesEncrypt(challenge, response + 8);
522 (void) DesSetkey(ZPasswordHash + 14);
523 DesEncrypt(challenge, response + 16);
526 dbglog("ChallengeResponse - response %.24B", response);
531 ChallengeHash(u_char PeerChallenge[16], u_char *rchallenge,
532 char *username, u_char Challenge[8])
535 SHA1_CTX sha1Context;
536 u_char sha1Hash[SHA1_SIGNATURE_SIZE];
539 /* remove domain from "domain\username" */
540 if ((user = strrchr(username, '\\')) != NULL)
545 SHA1_Init(&sha1Context);
546 SHA1_Update(&sha1Context, PeerChallenge, 16);
547 SHA1_Update(&sha1Context, rchallenge, 16);
548 SHA1_Update(&sha1Context, (unsigned char *)user, strlen(user));
549 SHA1_Final(sha1Hash, &sha1Context);
551 BCOPY(sha1Hash, Challenge, 8);
555 * Convert the ASCII version of the password to Unicode.
556 * This implicitly supports 8-bit ISO8859/1 characters.
557 * This gives us the little-endian representation, which
558 * is assumed by all M$ CHAP RFCs. (Unicode byte ordering
559 * is machine-dependent.)
562 ascii2unicode(char ascii[], int ascii_len, u_char unicode[])
566 BZERO(unicode, ascii_len * 2);
567 for (i = 0; i < ascii_len; i++)
568 unicode[i * 2] = (u_char) ascii[i];
572 NTPasswordHash(u_char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE])
574 #if defined(__NetBSD__) || !defined(USE_MD4)
575 /* NetBSD uses the libc md4 routines which take bytes instead of bits */
576 int mdlen = secret_len;
578 int mdlen = secret_len * 8;
582 MD4Init(&md4Context);
583 #if !defined(USE_MD4)
584 /* Internal MD4Update can take at most 64 bytes at a time */
585 while (mdlen > 512) {
586 MD4Update(&md4Context, secret, 512);
591 MD4Update(&md4Context, secret, mdlen);
592 MD4Final(hash, &md4Context);
597 ChapMS_NT(u_char *rchallenge, char *secret, int secret_len,
598 u_char NTResponse[24])
600 u_char unicodePassword[MAX_NT_PASSWORD * 2];
601 u_char PasswordHash[MD4_SIGNATURE_SIZE];
603 /* Hash the Unicode version of the secret (== password). */
604 ascii2unicode(secret, secret_len, unicodePassword);
605 NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
607 ChallengeResponse(rchallenge, PasswordHash, NTResponse);
611 ChapMS2_NT(u_char *rchallenge, u_char PeerChallenge[16], char *username,
612 char *secret, int secret_len, u_char NTResponse[24])
614 u_char unicodePassword[MAX_NT_PASSWORD * 2];
615 u_char PasswordHash[MD4_SIGNATURE_SIZE];
618 ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
620 /* Hash the Unicode version of the secret (== password). */
621 ascii2unicode(secret, secret_len, unicodePassword);
622 NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
624 ChallengeResponse(Challenge, PasswordHash, NTResponse);
627 #ifdef PPP_WITH_MSLANMAN
628 static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
631 ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
632 unsigned char *response)
635 u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
636 u_char PasswordHash[MD4_SIGNATURE_SIZE];
638 /* LANMan password is case insensitive */
639 BZERO(UcasePassword, sizeof(UcasePassword));
640 for (i = 0; i < secret_len; i++)
641 UcasePassword[i] = (u_char)toupper(secret[i]);
642 (void) DesSetkey(UcasePassword + 0);
643 DesEncrypt( StdText, PasswordHash + 0 );
644 (void) DesSetkey(UcasePassword + 7);
645 DesEncrypt( StdText, PasswordHash + 8 );
646 ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]);
652 GenerateAuthenticatorResponse(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
653 u_char NTResponse[24], u_char PeerChallenge[16],
654 u_char *rchallenge, char *username,
655 u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
658 * "Magic" constants used in response generation, from RFC 2759.
660 u_char Magic1[39] = /* "Magic server to client signing constant" */
661 { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
662 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
663 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
664 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
665 u_char Magic2[41] = /* "Pad to make it do more than one iteration" */
666 { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
667 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
668 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
669 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
673 SHA1_CTX sha1Context;
674 u_char Digest[SHA1_SIGNATURE_SIZE];
677 SHA1_Init(&sha1Context);
678 SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
679 SHA1_Update(&sha1Context, NTResponse, 24);
680 SHA1_Update(&sha1Context, Magic1, sizeof(Magic1));
681 SHA1_Final(Digest, &sha1Context);
683 ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
685 SHA1_Init(&sha1Context);
686 SHA1_Update(&sha1Context, Digest, sizeof(Digest));
687 SHA1_Update(&sha1Context, Challenge, sizeof(Challenge));
688 SHA1_Update(&sha1Context, Magic2, sizeof(Magic2));
689 SHA1_Final(Digest, &sha1Context);
691 /* Convert to ASCII hex string. */
692 for (i = 0; i < MAX((MS_AUTH_RESPONSE_LENGTH / 2), sizeof(Digest)); i++)
693 sprintf((char *)&authResponse[i * 2], "%02X", Digest[i]);
698 GenerateAuthenticatorResponsePlain
699 (char *secret, int secret_len,
700 u_char NTResponse[24], u_char PeerChallenge[16],
701 u_char *rchallenge, char *username,
702 u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
704 u_char unicodePassword[MAX_NT_PASSWORD * 2];
705 u_char PasswordHash[MD4_SIGNATURE_SIZE];
706 u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
708 /* Hash (x2) the Unicode version of the secret (== password). */
709 ascii2unicode(secret, secret_len, unicodePassword);
710 NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
711 NTPasswordHash(PasswordHash, sizeof(PasswordHash),
714 GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge,
715 rchallenge, username, authResponse);
722 * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079)
725 Set_Start_Key(u_char *rchallenge, char *secret, int secret_len)
727 u_char unicodePassword[MAX_NT_PASSWORD * 2];
728 u_char PasswordHash[MD4_SIGNATURE_SIZE];
729 u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
731 /* Hash (x2) the Unicode version of the secret (== password). */
732 ascii2unicode(secret, secret_len, unicodePassword);
733 NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
734 NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
736 mppe_set_chapv1(rchallenge, PasswordHashHash);
740 * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
743 SetMasterKeys(char *secret, int secret_len, u_char NTResponse[24], int IsServer)
745 u_char unicodePassword[MAX_NT_PASSWORD * 2];
746 u_char PasswordHash[MD4_SIGNATURE_SIZE];
747 u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
748 /* Hash (x2) the Unicode version of the secret (== password). */
749 ascii2unicode(secret, secret_len, unicodePassword);
750 NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
751 NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
752 mppe_set_chapv2(PasswordHashHash, NTResponse, IsServer);
755 #endif /* PPP_WITH_MPPE */
759 ChapMS(u_char *rchallenge, char *secret, int secret_len,
760 unsigned char *response)
762 BZERO(response, MS_CHAP_RESPONSE_LEN);
764 ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]);
766 #ifdef PPP_WITH_MSLANMAN
767 ChapMS_LANMan(rchallenge, secret, secret_len,
768 &response[MS_CHAP_LANMANRESP]);
770 /* preferred method is set by option */
771 response[MS_CHAP_USENT] = !ms_lanman;
773 response[MS_CHAP_USENT] = 1;
777 Set_Start_Key(rchallenge, secret, secret_len);
783 * If PeerChallenge is NULL, one is generated and the PeerChallenge
784 * field of response is filled in. Call this way when generating a response.
785 * If PeerChallenge is supplied, it is copied into the PeerChallenge field.
786 * Call this way when verifying a response (or debugging).
787 * Do not call with PeerChallenge = response.
789 * The PeerChallenge field of response is then used for calculation of the
790 * Authenticator Response.
793 ChapMS2(u_char *rchallenge, u_char *PeerChallenge,
794 char *user, char *secret, int secret_len, unsigned char *response,
795 u_char authResponse[], int authenticator)
798 u_char *p = &response[MS_CHAP2_PEER_CHALLENGE];
801 BZERO(response, MS_CHAP2_RESPONSE_LEN);
803 /* Generate the Peer-Challenge if requested, or copy it if supplied. */
805 for (i = 0; i < MS_CHAP2_PEER_CHAL_LEN; i++)
806 *p++ = (u_char) (drand48() * 0xff);
808 BCOPY(PeerChallenge, &response[MS_CHAP2_PEER_CHALLENGE],
809 MS_CHAP2_PEER_CHAL_LEN);
811 /* Generate the NT-Response */
812 ChapMS2_NT(rchallenge, &response[MS_CHAP2_PEER_CHALLENGE], user,
813 secret, secret_len, &response[MS_CHAP2_NTRESP]);
815 /* Generate the Authenticator Response. */
816 GenerateAuthenticatorResponsePlain(secret, secret_len,
817 &response[MS_CHAP2_NTRESP],
818 &response[MS_CHAP2_PEER_CHALLENGE],
819 rchallenge, user, authResponse);
822 SetMasterKeys(secret, secret_len,
823 &response[MS_CHAP2_NTRESP], authenticator);
828 static struct chap_digest_type chapms_digest = {
829 CHAP_MICROSOFT, /* code */
830 chapms_generate_challenge,
831 chapms_verify_response,
832 chapms_make_response,
833 NULL, /* check_success */
834 chapms_handle_failure,
837 static struct chap_digest_type chapms2_digest = {
838 CHAP_MICROSOFT_V2, /* code */
839 chapms2_generate_challenge,
840 chapms2_verify_response,
841 chapms2_make_response,
842 chapms2_check_success,
843 chapms_handle_failure,
850 chap_register_digest(&chapms_digest);
851 chap_register_digest(&chapms2_digest);
852 add_options(chapms_option_list);
862 void random_bytes(unsigned char *bytes, int len)
867 bytes[i++] = (unsigned char) rand();
872 int test_chap_v1(void) {
873 char *secret = "TestPassword";
875 unsigned char challenge[8] = {
876 0x6c, 0x8d, 0x4b, 0xa1, 0x2b, 0x5c, 0x13, 0xc3
878 unsigned char response[MS_CHAP_RESPONSE_LEN] = {
880 unsigned char result[MS_CHAP_RESPONSE_LEN] = {
881 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
882 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
883 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
885 0x91, 0x09, 0x61, 0x5a, 0x0c, 0xac, 0xac, 0x55,
886 0x1f, 0x60, 0xe2, 0x9c, 0x00, 0xac, 0x24, 0xda,
887 0x6e, 0xa5, 0x7b, 0xdb, 0x1d, 0x6a, 0x17, 0xc5,
891 ChapMS(challenge, secret, strlen(secret), response);
892 return memcmp(response, result, MS_CHAP_RESPONSE_LEN);
895 int test_chap_v2(void) {
896 char *secret = "clientPass";
899 char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
900 char *saresult = "407A5589115FD0D6209F510FE9C04566932CDA56";
902 unsigned char authenticator[16] = {
903 0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
904 0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28
906 unsigned char peerchallenge[16] = {
907 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
908 0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E
910 unsigned char result[MS_CHAP_NTRESP_LEN] = {
911 0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E,
912 0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54,
913 0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF
916 unsigned char response[MS_CHAP2_RESPONSE_LEN] = {
919 ChapMS2(authenticator, peerchallenge, name,
920 secret, strlen(secret), response,
921 (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
923 return memcmp(&response[MS_CHAP2_NTRESP], result, MS_CHAP2_NTRESP_LEN) ||
924 strncmp(saresponse, saresult, MS_AUTH_RESPONSE_LENGTH);
927 int main(int argc, char *argv[]) {
929 if (test_chap_v1()) {
930 printf("CHAPv1 failed\n");
934 if (test_chap_v2()) {
935 printf("CHAPv2 failed\n");
943 #endif /* UNIT_TEST */