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"
107 static void ascii2unicode (char[], int, u_char[]);
108 static void NTPasswordHash (u_char *, int, u_char[MD4_SIGNATURE_SIZE]);
109 static int ChallengeResponse (u_char *, u_char *, u_char[24]);
110 static void ChapMS_NT (u_char *, char *, int, u_char[24]);
111 static void ChapMS2_NT (u_char *, u_char[16], char *, char *, int,
113 static void GenerateAuthenticatorResponsePlain
114 (char*, int, u_char[24], u_char[16], u_char *,
116 #ifdef PPP_WITH_MSLANMAN
117 static void ChapMS_LANMan (u_char *, char *, int, u_char *);
120 #ifdef PPP_WITH_MSLANMAN
121 bool ms_lanman = 0; /* Use LanMan password instead of NT */
122 /* Has meaning only with MS-CHAP challenges */
128 /* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
129 static char *mschap_challenge = NULL;
130 /* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
131 static char *mschap2_peer_challenge = NULL;
134 #include "fsm.h" /* Need to poke MPPE options */
139 * Command-line options.
141 static option_t chapms_option_list[] = {
142 #ifdef PPP_WITH_MSLANMAN
143 { "ms-lanman", o_bool, &ms_lanman,
144 "Use LanMan passwd when using MS-CHAP", 1 },
147 { "mschap-challenge", o_string, &mschap_challenge,
148 "specify CHAP challenge" },
149 { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
150 "specify CHAP peer challenge" },
156 * chapms_generate_challenge - generate a challenge for MS-CHAP.
157 * For MS-CHAP the challenge length is fixed at 8 bytes.
158 * The length goes in challenge[0] and the actual challenge starts
162 chapms_generate_challenge(unsigned char *challenge)
166 if (mschap_challenge && strlen(mschap_challenge) == 8)
167 memcpy(challenge, mschap_challenge, 8);
170 random_bytes(challenge, 8);
174 chapms2_generate_challenge(unsigned char *challenge)
178 if (mschap_challenge && strlen(mschap_challenge) == 16)
179 memcpy(challenge, mschap_challenge, 16);
182 random_bytes(challenge, 16);
186 chapms_verify_response(int id, char *name,
187 unsigned char *secret, int secret_len,
188 unsigned char *challenge, unsigned char *response,
189 char *message, int message_space)
191 unsigned char md[MS_CHAP_RESPONSE_LEN];
193 int challenge_len, response_len;
195 challenge_len = *challenge++; /* skip length, is 8 */
196 response_len = *response++;
197 if (response_len != MS_CHAP_RESPONSE_LEN)
200 #ifndef PPP_WITH_MSLANMAN
201 if (!response[MS_CHAP_USENT]) {
202 /* Should really propagate this into the error packet. */
203 notice("Peer request for LANMAN auth not supported");
208 /* Generate the expected response. */
209 ChapMS(challenge, (char *)secret, secret_len, md);
211 #ifdef PPP_WITH_MSLANMAN
212 /* Determine which part of response to verify against */
213 if (!response[MS_CHAP_USENT])
214 diff = memcmp(&response[MS_CHAP_LANMANRESP],
215 &md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN);
218 diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP],
222 slprintf(message, message_space, "Access granted");
227 /* See comments below for MS-CHAP V2 */
228 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
229 challenge_len, challenge);
234 chapms2_verify_response(int id, char *name,
235 unsigned char *secret, int secret_len,
236 unsigned char *challenge, unsigned char *response,
237 char *message, int message_space)
239 unsigned char md[MS_CHAP2_RESPONSE_LEN];
240 char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
241 int challenge_len, response_len;
243 challenge_len = *challenge++; /* skip length, is 16 */
244 response_len = *response++;
245 if (response_len != MS_CHAP2_RESPONSE_LEN)
246 goto bad; /* not even the right length */
248 /* Generate the expected response and our mutual auth. */
249 ChapMS2(challenge, &response[MS_CHAP2_PEER_CHALLENGE], name,
250 (char *)secret, secret_len, md,
251 (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
253 /* compare MDs and send the appropriate status */
255 * Per RFC 2759, success message must be formatted as
256 * "S=<auth_string> M=<message>"
258 * <auth_string> is the Authenticator Response (mutual auth)
259 * <message> is a text message
261 * However, some versions of Windows (win98 tested) do not know
262 * about the M=<message> part (required per RFC 2759) and flag
263 * it as an error (reported incorrectly as an encryption error
264 * to the user). Since the RFC requires it, and it can be
265 * useful information, we supply it if the peer is a conforming
266 * system. Luckily (?), win98 sets the Flags field to 0x04
267 * (contrary to RFC requirements) so we can use that to
268 * distinguish between conforming and non-conforming systems.
270 * Special thanks to Alex Swiridov <say@real.kharkov.ua> for
271 * help debugging this.
273 if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP],
274 MS_CHAP2_NTRESP_LEN) == 0) {
275 if (response[MS_CHAP2_FLAGS])
276 slprintf(message, message_space, "S=%s", saresponse);
278 slprintf(message, message_space, "S=%s M=%s",
279 saresponse, "Access granted");
285 * Failure message must be formatted as
286 * "E=e R=r C=c V=v M=m"
288 * e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
289 * r = retry (we use 1, ok to retry)
290 * c = challenge to use for next response, we reuse previous
291 * v = Change Password version supported, we use 0
294 * The M=m part is only for MS-CHAPv2. Neither win2k nor
295 * win98 (others untested) display the message to the user anyway.
296 * They also both ignore the E=e code.
298 * Note that it's safe to reuse the same challenge as we don't
299 * actually accept another response based on the error message
300 * (and no clients try to resend a response anyway).
302 * Basically, this whole bit is useless code, even the small
303 * implementation here is only because of overspecification.
305 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
306 challenge_len, challenge, "Access denied");
311 chapms_make_response(unsigned char *response, int id, char *our_name,
312 unsigned char *challenge, char *secret, int secret_len,
313 unsigned char *private)
315 challenge++; /* skip length, should be 8 */
316 *response++ = MS_CHAP_RESPONSE_LEN;
317 ChapMS(challenge, secret, secret_len, response);
320 struct chapms2_response_cache_entry {
322 unsigned char challenge[16];
323 unsigned char response[MS_CHAP2_RESPONSE_LEN];
324 unsigned char auth_response[MS_AUTH_RESPONSE_LENGTH];
327 #define CHAPMS2_MAX_RESPONSE_CACHE_SIZE 10
328 static struct chapms2_response_cache_entry
329 chapms2_response_cache[CHAPMS2_MAX_RESPONSE_CACHE_SIZE];
330 static int chapms2_response_cache_next_index = 0;
331 static int chapms2_response_cache_size = 0;
334 chapms2_add_to_response_cache(int id, unsigned char *challenge,
335 unsigned char *response,
336 unsigned char *auth_response)
338 int i = chapms2_response_cache_next_index;
340 chapms2_response_cache[i].id = id;
341 memcpy(chapms2_response_cache[i].challenge, challenge, 16);
342 memcpy(chapms2_response_cache[i].response, response,
343 MS_CHAP2_RESPONSE_LEN);
344 memcpy(chapms2_response_cache[i].auth_response,
345 auth_response, MS_AUTH_RESPONSE_LENGTH);
346 chapms2_response_cache_next_index =
347 (i + 1) % CHAPMS2_MAX_RESPONSE_CACHE_SIZE;
348 if (chapms2_response_cache_next_index > chapms2_response_cache_size)
349 chapms2_response_cache_size = chapms2_response_cache_next_index;
350 dbglog("added response cache entry %d", i);
353 static struct chapms2_response_cache_entry*
354 chapms2_find_in_response_cache(int id, unsigned char *challenge,
355 unsigned char *auth_response)
359 for (i = 0; i < chapms2_response_cache_size; i++) {
360 if (id == chapms2_response_cache[i].id
363 chapms2_response_cache[i].challenge,
366 || memcmp(auth_response,
367 chapms2_response_cache[i].auth_response,
368 MS_AUTH_RESPONSE_LENGTH) == 0)) {
369 dbglog("response found in cache (entry %d)", i);
370 return &chapms2_response_cache[i];
373 return NULL; /* not found */
377 chapms2_make_response(unsigned char *response, int id, char *our_name,
378 unsigned char *challenge, char *secret, int secret_len,
379 unsigned char *private)
381 const struct chapms2_response_cache_entry *cache_entry;
382 unsigned char auth_response[MS_AUTH_RESPONSE_LENGTH+1];
384 challenge++; /* skip length, should be 16 */
385 *response++ = MS_CHAP2_RESPONSE_LEN;
386 cache_entry = chapms2_find_in_response_cache(id, challenge, NULL);
388 memcpy(response, cache_entry->response, MS_CHAP2_RESPONSE_LEN);
393 mschap2_peer_challenge,
397 our_name, secret, secret_len, response, auth_response,
398 MS_CHAP2_AUTHENTICATEE);
399 chapms2_add_to_response_cache(id, challenge, response, auth_response);
403 chapms2_check_success(int id, unsigned char *msg, int len)
405 if ((len < MS_AUTH_RESPONSE_LENGTH + 2) ||
406 strncmp((char *)msg, "S=", 2) != 0) {
407 /* Packet does not start with "S=" */
408 error("MS-CHAPv2 Success packet is badly formed.");
413 if (len < MS_AUTH_RESPONSE_LENGTH
414 || !chapms2_find_in_response_cache(id, NULL /* challenge */, msg)) {
415 /* Authenticator Response did not match expected. */
416 error("MS-CHAPv2 mutual authentication failed.");
419 /* Authenticator Response matches. */
420 msg += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
421 len -= MS_AUTH_RESPONSE_LENGTH;
422 if ((len >= 3) && !strncmp((char *)msg, " M=", 3)) {
423 msg += 3; /* Eat the delimiter */
424 } else if ((len >= 2) && !strncmp((char *)msg, "M=", 2)) {
425 msg += 2; /* Eat the delimiter */
427 /* Packet has extra text which does not begin " M=" */
428 error("MS-CHAPv2 Success packet is badly formed.");
435 chapms_handle_failure(unsigned char *inp, int len)
440 /* We want a null-terminated string for strxxx(). */
441 msg = malloc(len + 1);
443 notice("Out of memory in chapms_handle_failure");
446 BCOPY(inp, msg, len);
451 * Deal with MS-CHAP formatted failure messages; just print the
452 * M=<message> part (if any). For MS-CHAP we're not really supposed
453 * to use M=<message>, but it shouldn't hurt. See
454 * chapms[2]_verify_response.
456 if (!strncmp(p, "E=", 2))
457 err = strtol(p+2, NULL, 10); /* Remember the error code. */
459 goto print_msg; /* Message is badly formatted. */
461 if (len && ((p = strstr(p, " M=")) != NULL)) {
462 /* M=<message> field found. */
465 /* No M=<message>; use the error code. */
467 case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
468 p = "E=646 Restricted logon hours";
471 case MS_CHAP_ERROR_ACCT_DISABLED:
472 p = "E=647 Account disabled";
475 case MS_CHAP_ERROR_PASSWD_EXPIRED:
476 p = "E=648 Password expired";
479 case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
480 p = "E=649 No dialin permission";
483 case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
484 p = "E=691 Authentication failure";
487 case MS_CHAP_ERROR_CHANGING_PASSWORD:
488 /* Should never see this, we don't support Change Password. */
489 p = "E=709 Error changing password";
494 error("Unknown MS-CHAP authentication failure: %.*v",
501 error("MS-CHAP authentication failed: %v", p);
505 // TODO: Move this definition somewhere
506 #define NT_RESPONSE_LEN 24
509 ChallengeResponse(u_char *challenge,
510 u_char PasswordHash[MD4_SIGNATURE_SIZE],
511 u_char response[NT_RESPONSE_LEN])
513 u_char ZPasswordHash[21];
515 int outlen = NT_RESPONSE_LEN;
519 BZERO(ZPasswordHash, sizeof(ZPasswordHash));
520 BCOPY(PasswordHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
523 dbglog("ChallengeResponse - ZPasswordHash %.*B",
524 sizeof(ZPasswordHash), ZPasswordHash);
527 ctx = PPP_CIPHER_CTX_new();
530 if (PPP_CipherInit(ctx, PPP_des_ecb(), ZPasswordHash + 0, NULL, 1)) {
532 if (PPP_CipherUpdate(ctx, response + offset, &outlen, challenge, 8)) {
535 PPP_CIPHER_CTX_set_cipher_data(ctx, ZPasswordHash + 7);
536 if (PPP_CipherUpdate(ctx, response + offset, &outlen, challenge, 8)) {
539 PPP_CIPHER_CTX_set_cipher_data(ctx, ZPasswordHash + 14);
540 if (PPP_CipherUpdate(ctx, response + offset, &outlen, challenge, 8)) {
543 if (PPP_CipherFinal(ctx, response + offset, &outlen)) {
552 PPP_CIPHER_CTX_free(ctx);
556 dbglog("ChallengeResponse - response %.24B", response);
562 ChallengeHash(u_char PeerChallenge[16], u_char *rchallenge,
563 char *username, u_char Challenge[8])
567 u_char hash[SHA1_SIGNATURE_SIZE];
571 /* remove domain from "domain\username" */
572 if ((user = strrchr(username, '\\')) != NULL)
577 ctx = PPP_MD_CTX_new();
580 if (PPP_DigestInit(ctx, PPP_sha1())) {
582 if (PPP_DigestUpdate(ctx, PeerChallenge, 16)) {
584 if (PPP_DigestUpdate(ctx, rchallenge, 16)) {
586 if (PPP_DigestUpdate(ctx, user, strlen(user))) {
588 hash_len = SHA1_SIGNATURE_SIZE;
589 if (PPP_DigestFinal(ctx, hash, &hash_len)) {
591 BCOPY(hash, Challenge, 8);
598 PPP_MD_CTX_free(ctx);
603 * Convert the ASCII version of the password to Unicode.
604 * This implicitly supports 8-bit ISO8859/1 characters.
605 * This gives us the little-endian representation, which
606 * is assumed by all M$ CHAP RFCs. (Unicode byte ordering
607 * is machine-dependent.)
610 ascii2unicode(char ascii[], int ascii_len, u_char unicode[])
614 BZERO(unicode, ascii_len * 2);
615 for (i = 0; i < ascii_len; i++)
616 unicode[i * 2] = (u_char) ascii[i];
620 NTPasswordHash(u_char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE])
622 PPP_MD_CTX* ctx = PPP_MD_CTX_new();
625 if (PPP_DigestInit(ctx, PPP_md4())) {
627 if (PPP_DigestUpdate(ctx, secret, secret_len)) {
629 int hash_len = MD4_SIGNATURE_SIZE;
630 PPP_DigestFinal(ctx, hash, &hash_len);
634 PPP_MD_CTX_free(ctx);
639 ChapMS_NT(u_char *rchallenge, char *secret, int secret_len,
640 u_char NTResponse[24])
642 u_char unicodePassword[MAX_NT_PASSWORD * 2];
643 u_char PasswordHash[MD4_SIGNATURE_SIZE];
645 /* Hash the Unicode version of the secret (== password). */
646 ascii2unicode(secret, secret_len, unicodePassword);
647 NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
649 ChallengeResponse(rchallenge, PasswordHash, NTResponse);
653 ChapMS2_NT(u_char *rchallenge, u_char PeerChallenge[16], char *username,
654 char *secret, int secret_len, u_char NTResponse[24])
656 u_char unicodePassword[MAX_NT_PASSWORD * 2];
657 u_char PasswordHash[MD4_SIGNATURE_SIZE];
660 ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
662 /* Hash the Unicode version of the secret (== password). */
663 ascii2unicode(secret, secret_len, unicodePassword);
664 NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
666 ChallengeResponse(Challenge, PasswordHash, NTResponse);
669 #ifdef PPP_WITH_MSLANMAN
670 static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
673 ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
674 unsigned char *response)
677 u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
678 u_char PasswordHash[MD4_SIGNATURE_SIZE];
680 /* LANMan password is case insensitive */
681 BZERO(UcasePassword, sizeof(UcasePassword));
682 for (i = 0; i < secret_len; i++)
683 UcasePassword[i] = (u_char)toupper(secret[i]);
684 (void) DesSetkey(UcasePassword + 0);
685 DesEncrypt( StdText, PasswordHash + 0 );
686 (void) DesSetkey(UcasePassword + 7);
687 DesEncrypt( StdText, PasswordHash + 8 );
688 ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]);
694 GenerateAuthenticatorResponse(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
695 u_char NTResponse[24], u_char PeerChallenge[16],
696 u_char *rchallenge, char *username,
697 u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
700 * "Magic" constants used in response generation, from RFC 2759.
702 u_char Magic1[39] = /* "Magic server to client signing constant" */
703 { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
704 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
705 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
706 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
707 u_char Magic2[41] = /* "Pad to make it do more than one iteration" */
708 { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
709 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
710 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
711 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
716 u_char Digest[SHA1_SIGNATURE_SIZE];
720 ctx = PPP_MD_CTX_new();
723 if (PPP_DigestInit(ctx, PPP_sha1())) {
725 if (PPP_DigestUpdate(ctx, PasswordHashHash, MD4_SIGNATURE_SIZE)) {
727 if (PPP_DigestUpdate(ctx, NTResponse, 24)) {
729 if (PPP_DigestUpdate(ctx, Magic1, sizeof(Magic1))) {
731 hash_len = sizeof(Digest);
732 PPP_DigestFinal(ctx, Digest, &hash_len);
737 PPP_MD_CTX_free(ctx);
740 ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
742 ctx = PPP_MD_CTX_new();
745 if (PPP_DigestInit(ctx, PPP_sha1())) {
747 if (PPP_DigestUpdate(ctx, Digest, sizeof(Digest))) {
749 if (PPP_DigestUpdate(ctx, Challenge, sizeof(Challenge))) {
751 if (PPP_DigestUpdate(ctx, Magic2, sizeof(Magic2))) {
753 hash_len = sizeof(Digest);
754 PPP_DigestFinal(ctx, Digest, &hash_len);
760 PPP_MD_CTX_free(ctx);
763 /* Convert to ASCII hex string. */
764 for (i = 0; i < MAX((MS_AUTH_RESPONSE_LENGTH / 2), sizeof(Digest)); i++) {
765 sprintf((char *)&authResponse[i * 2], "%02X", Digest[i]);
771 GenerateAuthenticatorResponsePlain
772 (char *secret, int secret_len,
773 u_char NTResponse[24], u_char PeerChallenge[16],
774 u_char *rchallenge, char *username,
775 u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
777 u_char unicodePassword[MAX_NT_PASSWORD * 2];
778 u_char PasswordHash[MD4_SIGNATURE_SIZE];
779 u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
781 /* Hash (x2) the Unicode version of the secret (== password). */
782 ascii2unicode(secret, secret_len, unicodePassword);
783 NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
784 NTPasswordHash(PasswordHash, sizeof(PasswordHash),
787 GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge,
788 rchallenge, username, authResponse);
795 * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079)
798 Set_Start_Key(u_char *rchallenge, char *secret, int secret_len)
800 u_char unicodePassword[MAX_NT_PASSWORD * 2];
801 u_char PasswordHash[MD4_SIGNATURE_SIZE];
802 u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
804 /* Hash (x2) the Unicode version of the secret (== password). */
805 ascii2unicode(secret, secret_len, unicodePassword);
806 NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
807 NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
809 mppe_set_chapv1(rchallenge, PasswordHashHash);
813 * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
816 SetMasterKeys(char *secret, int secret_len, u_char NTResponse[24], int IsServer)
818 u_char unicodePassword[MAX_NT_PASSWORD * 2];
819 u_char PasswordHash[MD4_SIGNATURE_SIZE];
820 u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
821 /* Hash (x2) the Unicode version of the secret (== password). */
822 ascii2unicode(secret, secret_len, unicodePassword);
823 NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
824 NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
825 mppe_set_chapv2(PasswordHashHash, NTResponse, IsServer);
828 #endif /* PPP_WITH_MPPE */
832 ChapMS(u_char *rchallenge, char *secret, int secret_len,
833 unsigned char *response)
835 BZERO(response, MS_CHAP_RESPONSE_LEN);
837 ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]);
839 #ifdef PPP_WITH_MSLANMAN
840 ChapMS_LANMan(rchallenge, secret, secret_len,
841 &response[MS_CHAP_LANMANRESP]);
843 /* preferred method is set by option */
844 response[MS_CHAP_USENT] = !ms_lanman;
846 response[MS_CHAP_USENT] = 1;
850 Set_Start_Key(rchallenge, secret, secret_len);
856 * If PeerChallenge is NULL, one is generated and the PeerChallenge
857 * field of response is filled in. Call this way when generating a response.
858 * If PeerChallenge is supplied, it is copied into the PeerChallenge field.
859 * Call this way when verifying a response (or debugging).
860 * Do not call with PeerChallenge = response.
862 * The PeerChallenge field of response is then used for calculation of the
863 * Authenticator Response.
866 ChapMS2(u_char *rchallenge, u_char *PeerChallenge,
867 char *user, char *secret, int secret_len, unsigned char *response,
868 u_char authResponse[], int authenticator)
871 u_char *p = &response[MS_CHAP2_PEER_CHALLENGE];
874 BZERO(response, MS_CHAP2_RESPONSE_LEN);
876 /* Generate the Peer-Challenge if requested, or copy it if supplied. */
878 for (i = 0; i < MS_CHAP2_PEER_CHAL_LEN; i++)
879 *p++ = (u_char) (drand48() * 0xff);
881 BCOPY(PeerChallenge, &response[MS_CHAP2_PEER_CHALLENGE],
882 MS_CHAP2_PEER_CHAL_LEN);
884 /* Generate the NT-Response */
885 ChapMS2_NT(rchallenge, &response[MS_CHAP2_PEER_CHALLENGE], user,
886 secret, secret_len, &response[MS_CHAP2_NTRESP]);
888 /* Generate the Authenticator Response. */
889 GenerateAuthenticatorResponsePlain(secret, secret_len,
890 &response[MS_CHAP2_NTRESP],
891 &response[MS_CHAP2_PEER_CHALLENGE],
892 rchallenge, user, authResponse);
895 SetMasterKeys(secret, secret_len,
896 &response[MS_CHAP2_NTRESP], authenticator);
901 static struct chap_digest_type chapms_digest = {
902 CHAP_MICROSOFT, /* code */
903 chapms_generate_challenge,
904 chapms_verify_response,
905 chapms_make_response,
906 NULL, /* check_success */
907 chapms_handle_failure,
910 static struct chap_digest_type chapms2_digest = {
911 CHAP_MICROSOFT_V2, /* code */
912 chapms2_generate_challenge,
913 chapms2_verify_response,
914 chapms2_make_response,
915 chapms2_check_success,
916 chapms_handle_failure,
923 chap_register_digest(&chapms_digest);
924 chap_register_digest(&chapms2_digest);
925 add_options(chapms_option_list);
935 void random_bytes(unsigned char *bytes, int len)
940 bytes[i++] = (unsigned char) rand();
945 int test_chap_v1(void) {
946 char *secret = "MyPw";
948 unsigned char challenge[8] = {
949 0x10, 0x2D, 0xB5, 0xDF, 0x08, 0x5D, 0x30, 0x41
951 unsigned char response[MS_CHAP_RESPONSE_LEN] = {
953 unsigned char result[MS_CHAP_RESPONSE_LEN] = {
954 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
955 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
956 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
958 0x4E, 0x9D, 0x3C, 0x8F, 0x9C, 0xFD, 0x38, 0x5D,
959 0x5B, 0xF4, 0xD3, 0x24, 0x67, 0x91, 0x95, 0x6C,
960 0xA4, 0xC3, 0x51, 0xAB, 0x40, 0x9A, 0x3D, 0x61,
965 ChapMS(challenge, secret, strlen(secret), response);
966 return memcmp(response, result, MS_CHAP_RESPONSE_LEN);
969 int test_chap_v2(void) {
970 char *secret = "clientPass";
973 char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
974 char *saresult = "407A5589115FD0D6209F510FE9C04566932CDA56";
976 unsigned char authenticator[16] = {
977 0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
978 0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28
980 unsigned char peerchallenge[16] = {
981 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
982 0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E
984 unsigned char result[MS_CHAP_NTRESP_LEN] = {
985 0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E,
986 0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54,
987 0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF
990 unsigned char response[MS_CHAP2_RESPONSE_LEN] = {
993 ChapMS2(authenticator, peerchallenge, name,
994 secret, strlen(secret), response,
995 (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
997 return memcmp(&response[MS_CHAP2_NTRESP], result, MS_CHAP2_NTRESP_LEN) ||
998 strncmp(saresponse, saresult, MS_AUTH_RESPONSE_LENGTH);
1001 int main(int argc, char *argv[]) {
1005 if (test_chap_v1()) {
1006 printf("CHAPv1 failed\n");
1010 if (test_chap_v2()) {
1011 printf("CHAPv2 failed\n");
1015 PPP_crypto_deinit();
1017 printf("Success\n");
1021 #endif /* UNIT_TEST */