2 * chap_ms.c - Microsoft MS-CHAP compatible implementation.
4 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
5 * Use is subject to license terms.
7 * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
8 * http://www.strataware.com/
10 * All rights reserved.
12 * Redistribution and use in source and binary forms are permitted
13 * provided that the above copyright notice and this paragraph are
14 * duplicated in all such forms and that any documentation,
15 * advertising materials, and other materials related to such
16 * distribution and use acknowledge that the software was developed
17 * by Eric Rosenquist. The name of the author may not be used to
18 * endorse or promote products derived from this software without
19 * specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
23 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27 * This module implements MS-CHAPv1 (RFC 2433) and MS-CHAPv2 (RFC 2759).
29 * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
31 * Implemented LANManager type password response to MS-CHAP challenges.
32 * Now pppd provides both NT style and LANMan style blocks, and the
33 * prefered is set by option "ms-lanman". Default is to use NT.
34 * The hash text (StdText) was taken from Win95 RASAPI32.DLL.
36 * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
38 * Modifications by James Carlson / james.d.carlson@sun.com, June 1st, 2000.
40 * Added MS-CHAPv2 support.
43 #pragma ident "%Z%%M% %I% %E% SMI"
44 #define RCSID "$Id: chap_ms.c,v 1.15 1999/08/13 06:46:12 paulus Exp $"
46 #if defined(CHAPMS) || defined(CHAPMSV2)
52 #include <sys/types.h>
72 #if !defined(lint) && !defined(_lint)
73 static const char rcsid
[] = RCSID
;
77 u_char LANManResp
[24];
79 u_char UseNT
; /* If 1, ignore the LANMan response field */
81 /* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
82 in case this struct gets padded. */
85 u_char PeerChallenge
[16];
88 u_char Flags
; /* Should be zero (Win98 sends 04) */
90 /* We use MS_CHAPV2_RESPONSE_LEN, rather than sizeof(MS_Chapv2Response),
91 in case this struct gets padded. */
93 static void ChallengeResponse
__P((u_char
*, u_char
*, u_char
*));
94 static void DesEncrypt
__P((u_char
*, u_char
*, u_char
*));
95 static void MakeKey
__P((u_char
*, u_char
*));
96 static u_char Get7Bits
__P((u_char
*, int));
98 static void ChapMS_NT
__P((u_char
*, char *, int, MS_ChapResponse
*));
100 static void ChapMS_LANMan
__P((u_char
*, char *, int, MS_ChapResponse
*));
104 static void ChapMSv2_NT
__P((char *, u_char
*, char *, int,
105 MS_Chapv2Response
*));
109 static void Expand
__P((u_char
*, char *));
110 static void Collapse
__P((char *, u_char
*));
113 #if defined(MSLANMAN) && defined(CHAPMS)
114 bool ms_lanman
= 0; /* Use LanMan password instead of NT */
115 /* Has meaning only with MS-CHAP challenges */
119 /* Specially-formatted Microsoft CHAP response message. */
120 static char status_message
[256];
124 ChallengeResponse(challenge
, pwHash
, response
)
125 u_char
*challenge
; /* IN 8 octets */
126 u_char
*pwHash
; /* IN 16 octets */
127 u_char
*response
; /* OUT 24 octets */
129 u_char ZPasswordHash
[21];
131 BZERO(ZPasswordHash
, sizeof(ZPasswordHash
));
132 BCOPY(pwHash
, ZPasswordHash
, MD4_SIGNATURE_SIZE
);
135 dbglog("ChallengeResponse - ZPasswordHash %.*B",
136 sizeof(ZPasswordHash
), ZPasswordHash
);
139 DesEncrypt(challenge
, ZPasswordHash
+ 0, response
+ 0);
140 DesEncrypt(challenge
, ZPasswordHash
+ 7, response
+ 8);
141 DesEncrypt(challenge
, ZPasswordHash
+ 14, response
+ 16);
144 dbglog("ChallengeResponse - response %.24B", response
);
151 DesEncrypt(clear
, key
, cipher
)
152 u_char
*clear
; /* IN 8 octets */
153 u_char
*key
; /* IN 7 octets */
154 u_char
*cipher
; /* OUT 8 octets */
160 MakeKey(key
, des_key
);
162 Expand(des_key
, crypt_key
);
166 CHAPDEBUG((LOG_INFO
, "DesEncrypt: 8 octet input : %.8B", clear
));
169 Expand(clear
, des_input
);
170 encrypt(des_input
, 0);
171 Collapse(des_input
, cipher
);
174 CHAPDEBUG((LOG_INFO
, "DesEncrypt: 8 octet output: %.8B", cipher
));
178 #else /* USE_CRYPT */
181 DesEncrypt(clear
, key
, cipher
)
182 u_char
*clear
; /* IN 8 octets */
183 u_char
*key
; /* IN 7 octets */
184 u_char
*cipher
; /* OUT 8 octets */
187 des_key_schedule key_schedule
;
189 MakeKey(key
, des_key
);
191 des_set_key(&des_key
, key_schedule
);
194 CHAPDEBUG((LOG_INFO
, "DesEncrypt: 8 octet input : %.8B", clear
));
197 des_ecb_encrypt((des_cblock
*)clear
, (des_cblock
*)cipher
, key_schedule
, 1);
200 CHAPDEBUG((LOG_INFO
, "DesEncrypt: 8 octet output: %.8B", cipher
));
204 #endif /* USE_CRYPT */
207 static u_char
Get7Bits(input
, startBit
)
211 register unsigned int word
;
213 word
= (unsigned)input
[startBit
/ 8] << 8;
214 word
|= (unsigned)input
[startBit
/ 8 + 1];
216 word
>>= 15 - (startBit
% 8 + 7);
223 /* in == 8-byte string (expanded version of the 56-bit key)
224 * out == 64-byte string where each byte is either 1 or 0
225 * Note that the low-order "bit" is always ignored by by setkey()
227 static void Expand(in
, out
)
234 for(i
= 0; i
< 64; in
++){
236 for(j
= 7; j
>= 0; j
--)
237 *out
++ = (c
>> j
) & 01;
242 /* The inverse of Expand
244 static void Collapse(in
, out
)
252 for (i
= 0; i
< 64; i
+= 8, out
++) {
254 for (j
= 7; j
>= 0; j
--, in
++)
255 c
|= *(u_char
*)in
<< j
;
261 static void MakeKey(key
, des_key
)
262 u_char
*key
; /* IN 56 bit DES key missing parity bits */
263 u_char
*des_key
; /* OUT 64 bit DES key with parity bits added */
265 des_key
[0] = Get7Bits(key
, 0);
266 des_key
[1] = Get7Bits(key
, 7);
267 des_key
[2] = Get7Bits(key
, 14);
268 des_key
[3] = Get7Bits(key
, 21);
269 des_key
[4] = Get7Bits(key
, 28);
270 des_key
[5] = Get7Bits(key
, 35);
271 des_key
[6] = Get7Bits(key
, 42);
272 des_key
[7] = Get7Bits(key
, 49);
275 des_set_odd_parity((des_cblock
*)des_key
);
279 CHAPDEBUG((LOG_INFO
, "MakeKey: 56-bit input : %.7B", key
));
280 CHAPDEBUG((LOG_INFO
, "MakeKey: 64-bit output: %.8B", des_key
));
286 ChapMS_NT(rchallenge
, secret
, secret_len
, response
)
290 MS_ChapResponse
*response
;
293 #if defined(__NetBSD__) || defined(HAVE_LIBMD)
294 /* NetBSD uses the libc md4 routines which take bytes instead of bits */
295 int mdlen
= secret_len
* 2;
297 int mdlen
= secret_len
* 2 * 8;
300 u_char hash
[MD4_SIGNATURE_SIZE
];
301 u_char unicodePassword
[MAX_NT_PASSWORD
* 2];
303 /* Initialize the Unicode version of the secret (== password). */
304 /* This implicitly supports 8-bit ISO8859/1 characters. */
305 BZERO(unicodePassword
, sizeof(unicodePassword
));
306 for (i
= 0; i
< secret_len
; i
++)
307 unicodePassword
[i
* 2] = (u_char
)secret
[i
];
309 MD4Init(&md4Context
);
310 MD4Update(&md4Context
, unicodePassword
, mdlen
);
312 MD4Final(hash
, &md4Context
); /* Tell MD4 we're done */
314 ChallengeResponse(rchallenge
, hash
, response
->NTResp
);
318 static u_char
*StdText
= (u_char
*)"KGS!@#$%"; /* key from rasapi32.dll */
321 ChapMS_LANMan(rchallenge
, secret
, secret_len
, response
)
325 MS_ChapResponse
*response
;
328 u_char UcasePassword
[MAX_NT_PASSWORD
]; /* max is actually 14 */
329 u_char PasswordHash
[MD4_SIGNATURE_SIZE
];
331 /* LANMan password is case insensitive */
332 BZERO(UcasePassword
, sizeof(UcasePassword
));
333 for (i
= 0; i
< secret_len
; i
++)
334 UcasePassword
[i
] = (u_char
)(
335 islower(secret
[i
]) ? toupper(secret
[i
]) : secret
[i
]);
336 DesEncrypt( StdText
, UcasePassword
+ 0, PasswordHash
+ 0 );
337 DesEncrypt( StdText
, UcasePassword
+ 7, PasswordHash
+ 8 );
338 ChallengeResponse(rchallenge
, PasswordHash
, response
->LANManResp
);
343 ChapMS(cstate
, rchallenge
, rchallenge_len
, secret
, secret_len
)
350 MS_ChapResponse response
;
352 if (rchallenge_len
< 8) {
353 cstate
->resp_length
= 0;
358 CHAPDEBUG((LOG_INFO
, "ChapMS: secret is '%.*s'", secret_len
, secret
));
360 BZERO(&response
, sizeof(response
));
362 /* Calculate both always */
363 ChapMS_NT(rchallenge
, secret
, secret_len
, &response
);
366 ChapMS_LANMan(rchallenge
, secret
, secret_len
, &response
);
368 /* prefered method is set by option */
369 response
.UseNT
= !ms_lanman
;
374 BCOPY(&response
, cstate
->response
, MS_CHAP_RESPONSE_LEN
);
375 cstate
->resp_length
= MS_CHAP_RESPONSE_LEN
;
379 ChapMSStatus(cstate
, flag
)
384 cstate
->stat_message
= NULL
;
385 cstate
->stat_length
= 0;
387 cstate
->stat_message
= "E=691 R=0 M=\"Authentication failed\"";
388 cstate
->stat_length
= strlen(cstate
->stat_message
);
394 ChapMSValidate(cstate
, response
, response_len
, secret
, secret_len
)
401 MS_ChapResponse ckresp
;
403 if (response_len
< MS_CHAP_RESPONSE_LEN
|| cstate
->chal_len
< 8)
406 BZERO(&ckresp
, sizeof(ckresp
));
408 if (response
[MS_CHAP_RESPONSE_LEN
-1]) {
409 ChapMS_NT(cstate
->challenge
, secret
, secret_len
, &ckresp
);
410 return (ChapMSStatus(cstate
, memcmp(ckresp
.NTResp
, response
+24,
415 ChapMS_LANMan(cstate
->challenge
, secret
, secret_len
, &ckresp
);
416 return (ChapMSStatus(cstate
,
417 memcmp(ckresp
.LANManResp
, response
, 24) == 0));
419 return (ChapMSStatus(cstate
, 0));
426 ChallengeHash(peerchallenge
, authenticatorchallenge
, username
, challenge
)
427 u_char
*peerchallenge
, *authenticatorchallenge
, *challenge
;
431 SHA1_CTX sha1Context
;
434 SHA1Init(&sha1Context
);
435 SHA1Update(&sha1Context
, peerchallenge
, 16);
436 SHA1Update(&sha1Context
, authenticatorchallenge
, 16);
439 * Only the user name (as presented by the peer and
440 * excluding any prepended domain name)
441 * is used as input to SHAUpdate().
443 if ((cp
= strchr(username
,'\\')) != NULL
)
446 SHA1Update(&sha1Context
, (uint8_t *)username
, strlen(username
));
447 SHA1Final(digest
, &sha1Context
);
448 BCOPY(digest
, challenge
, 8);
452 ChapMSv2_NT(username
, rchallenge
, secret
, secret_len
, response
)
457 MS_Chapv2Response
*response
;
460 #if defined(__NetBSD__) || defined(HAVE_LIBMD)
461 /* NetBSD uses the libc md4 routines that take bytes instead of bits */
462 int mdlen
= secret_len
* 2;
464 int mdlen
= secret_len
* 2 * 8;
467 u_char hash
[MD4_SIGNATURE_SIZE
];
469 u_char unicodePassword
[MAX_NT_PASSWORD
* 2];
471 /* Initialize the Unicode version of the secret (== password). */
472 /* This implicitly supports 8-bit ISO8859/1 characters. */
473 BZERO(unicodePassword
, sizeof(unicodePassword
));
474 for (i
= 0; i
< secret_len
&& i
< MAX_NT_PASSWORD
; i
++)
475 if ((unicodePassword
[i
* 2] = (u_char
)secret
[i
]) == '\0')
478 ChallengeHash(response
->PeerChallenge
, rchallenge
, username
, challenge
);
480 MD4Init(&md4Context
);
481 MD4Update(&md4Context
, unicodePassword
, mdlen
);
483 MD4Final(hash
, &md4Context
); /* Tell MD4 we're done */
485 ChallengeResponse(challenge
, hash
, response
->NTResp
);
489 ChapMSv2(cstate
, rchallenge
, rchallenge_len
, secret
, secret_len
)
496 MS_Chapv2Response response
;
500 if (rchallenge_len
< 8) {
501 cstate
->resp_length
= 0;
505 BZERO(&response
, sizeof(response
));
507 ptr
= response
.PeerChallenge
;
508 for (i
= 0; i
< 16; i
++)
509 *ptr
++ = (u_char
) (drand48() * 0xff);
511 ChapMSv2_NT(cstate
->resp_name
, rchallenge
, secret
, secret_len
, &response
);
513 BCOPY(&response
, cstate
->response
, MS_CHAPV2_RESPONSE_LEN
);
514 cstate
->resp_length
= MS_CHAPV2_RESPONSE_LEN
;
518 ChapMSv2Success(cstate
, msresp
, authchall
, rhostname
, secret
, secret_len
)
520 MS_Chapv2Response
*msresp
;
522 char *rhostname
, *secret
;
525 static const u_char Magic1
[39] = "Magic server to client signing constant";
526 static const u_char Magic2
[41] =
527 "Pad to make it do more than one iteration";
528 #if defined(__NetBSD__) || defined(HAVE_LIBMD)
529 /* NetBSD uses the libc md4 routines that take bytes instead of bits */
534 u_char unicodePassword
[MAX_NT_PASSWORD
* 2];
536 u_char hash
[MD4_SIGNATURE_SIZE
];
537 u_char hashhash
[MD4_SIGNATURE_SIZE
];
538 SHA1_CTX sha1Context
;
542 static const char hexdig
[] = "0123456789ABCDEF";
545 /* Initialize the Unicode version of the secret (== password). */
546 /* This implicitly supports 8-bit ISO8859/1 characters. */
547 BZERO(unicodePassword
, sizeof(unicodePassword
));
548 for (i
= 0; i
< secret_len
&& i
< MAX_NT_PASSWORD
; i
++)
549 if ((unicodePassword
[i
* 2] = (u_char
)secret
[i
]) == '\0')
552 /* Hash the password with MD4 */
553 MD4Init(&md4Context
);
554 MD4Update(&md4Context
, unicodePassword
, secret_len
* 2 * mdlen
);
555 MD4Final(hash
, &md4Context
);
557 /* Now hash the hash */
558 MD4Init(&md4Context
);
559 MD4Update(&md4Context
, hash
, MD4_SIGNATURE_SIZE
* mdlen
);
560 MD4Final(hashhash
, &md4Context
);
562 SHA1Init(&sha1Context
);
563 SHA1Update(&sha1Context
, hashhash
, MD4_SIGNATURE_SIZE
);
564 SHA1Update(&sha1Context
, msresp
->NTResp
, sizeof (msresp
->NTResp
));
565 SHA1Update(&sha1Context
, Magic1
, 39);
566 SHA1Final(digest
, &sha1Context
);
568 ChallengeHash(msresp
->PeerChallenge
, authchall
, rhostname
, challenge
);
570 SHA1Init(&sha1Context
);
571 SHA1Update(&sha1Context
, digest
, 20);
572 SHA1Update(&sha1Context
, challenge
, 8);
573 SHA1Update(&sha1Context
, Magic2
, 41);
574 SHA1Final(digest
, &sha1Context
);
579 for (i
= 0; i
< 20; i
++) {
580 *cp
++ = hexdig
[digest
[i
]>>4];
581 *cp
++ = hexdig
[digest
[i
]&15];
584 * RFC 2759 says that a M=<string> greeting message is possible
585 * here. It lies. Any such greeting causes Windoze-98 to give
586 * error number 742, "Dial-Up Networking was unable to complete
587 * the connection. The computer you're dialing in to does not
588 * support the data encryption requirements specified. Please
589 * check your encryption settings in the properties of the
590 * connection. If this problem persists, contact your network
595 slprintf(cp
, sizeof (status_message
) - (cp
-status_message
),
596 "M=\"Welcome to %s.\"", hostname
);
598 cstate
->stat_message
= status_message
;
599 cstate
->stat_length
= strlen(status_message
);
603 ChapMSv2Validate(cstate
, rhostname
, response
, response_len
, secret
, secret_len
)
611 MS_Chapv2Response ckresp
;
613 if (response_len
< MS_CHAPV2_RESPONSE_LEN
||
614 /* response[MS_CHAPV2_RESPONSE_LEN-1] != 0 || */cstate
->chal_len
< 8) {
615 cstate
->stat_message
= NULL
;
616 cstate
->stat_length
= 0;
620 BZERO(&ckresp
, sizeof(ckresp
));
622 BCOPY(response
, ckresp
.PeerChallenge
, 16);
624 ChapMSv2_NT(rhostname
, cstate
->challenge
, secret
, secret_len
, &ckresp
);
625 if (memcmp(ckresp
.NTResp
, response
+24, 24) != 0) {
626 cstate
->stat_message
= "E=691 R=0 C=11111111111111111111111111111111 V=3 M=\"Authentication failed\"";
627 cstate
->stat_length
= strlen(cstate
->stat_message
);
630 ChapMSv2Success(cstate
, (MS_Chapv2Response
*)response
, cstate
->challenge
,
631 rhostname
, secret
, secret_len
);
634 #endif /* CHAPMSV2 */
636 #endif /* CHAPMS or CHAPMSV2 */