1 /*** WARNING - THIS CODE HAS NOT BEEN FINISHED! ***/
2 /*** The original PPPD code is written in a way to require either the UNIX DES
3 encryption functions encrypt(3) and setkey(3) or the DES library libdes.
4 Since both is not included in lwIP, MSCHAP currently does not work! */
5 /*****************************************************************************
6 * chpms.c - Network MicroSoft Challenge Handshake Authentication Protocol program file.
8 * Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
9 * Copyright (c) 1997 by Global Election Systems Inc. All rights reserved.
11 * The authors hereby grant permission to use, copy, modify, distribute,
12 * and license this software and its documentation for any purpose, provided
13 * that existing copyright notices are retained in all copies and that this
14 * notice and the following disclaimer are included verbatim in any
15 * distributions. No written agreement, license, or royalty fee is required
16 * for any of the authorized uses.
18 * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 ******************************************************************************
32 * 03-01-01 Marc Boucher <marc@mbsi.ca>
34 * 97-12-08 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
35 * Original based on BSD chap_ms.c.
36 *****************************************************************************/
38 * chap_ms.c - Microsoft MS-CHAP compatible implementation.
40 * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
41 * http://www.strataware.com/
43 * All rights reserved.
45 * Redistribution and use in source and binary forms are permitted
46 * provided that the above copyright notice and this paragraph are
47 * duplicated in all such forms and that any documentation,
48 * advertising materials, and other materials related to such
49 * distribution and use acknowledge that the software was developed
50 * by Eric Rosenquist. The name of the author may not be used to
51 * endorse or promote products derived from this software without
52 * specific prior written permission.
54 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
55 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
56 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
60 * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
62 * Implemented LANManager type password response to MS-CHAP challenges.
63 * Now pppd provides both NT style and LANMan style blocks, and the
64 * prefered is set by option "ms-lanman". Default is to use NT.
65 * The hash text (StdText) was taken from Win95 RASAPI32.DLL.
67 * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
74 #if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
76 #if MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
89 /*************************/
90 /*** LOCAL DEFINITIONS ***/
91 /*************************/
94 /************************/
95 /*** LOCAL DATA TYPES ***/
96 /************************/
98 u_char LANManResp
[24];
100 u_char UseNT
; /* If 1, ignore the LANMan response field */
102 /* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
103 in case this struct gets padded. */
107 /***********************************/
108 /*** LOCAL FUNCTION DECLARATIONS ***/
109 /***********************************/
111 /* XXX Don't know what to do with these. */
112 extern void setkey(const char *);
113 extern void encrypt(char *, int);
115 static void DesEncrypt (u_char
*, u_char
*, u_char
*);
116 static void MakeKey (u_char
*, u_char
*);
119 static void Expand (u_char
*, u_char
*);
120 static void Collapse (u_char
*, u_char
*);
123 static void ChallengeResponse(
124 u_char
*challenge
, /* IN 8 octets */
125 u_char
*pwHash
, /* IN 16 octets */
126 u_char
*response
/* OUT 24 octets */
128 static void ChapMS_NT(
133 MS_ChapResponse
*response
135 static u_char
Get7Bits(
141 /***********************************/
142 /*** PUBLIC FUNCTION DEFINITIONS ***/
143 /***********************************/
145 ChapMS( chap_state
*cstate
, char *rchallenge
, int rchallenge_len
, char *secret
, int secret_len
)
147 MS_ChapResponse response
;
149 extern int ms_lanman
;
153 CHAPDEBUG((LOG_INFO
, "ChapMS: secret is '%.*s'\n", secret_len
, secret
));
155 BZERO(&response
, sizeof(response
));
157 /* Calculate both always */
158 ChapMS_NT(rchallenge
, rchallenge_len
, secret
, secret_len
, &response
);
161 ChapMS_LANMan(rchallenge
, rchallenge_len
, secret
, secret_len
, &response
);
163 /* prefered method is set by option */
164 response
.UseNT
= !ms_lanman
;
169 BCOPY(&response
, cstate
->response
, MS_CHAP_RESPONSE_LEN
);
170 cstate
->resp_length
= MS_CHAP_RESPONSE_LEN
;
174 /**********************************/
175 /*** LOCAL FUNCTION DEFINITIONS ***/
176 /**********************************/
178 ChallengeResponse( u_char
*challenge
, /* IN 8 octets */
179 u_char
*pwHash
, /* IN 16 octets */
180 u_char
*response
/* OUT 24 octets */)
182 char ZPasswordHash
[21];
184 BZERO(ZPasswordHash
, sizeof(ZPasswordHash
));
185 BCOPY(pwHash
, ZPasswordHash
, 16);
188 log_packet(ZPasswordHash
, sizeof(ZPasswordHash
), "ChallengeResponse - ZPasswordHash", LOG_DEBUG
);
191 DesEncrypt(challenge
, ZPasswordHash
+ 0, response
+ 0);
192 DesEncrypt(challenge
, ZPasswordHash
+ 7, response
+ 8);
193 DesEncrypt(challenge
, ZPasswordHash
+ 14, response
+ 16);
196 log_packet(response
, 24, "ChallengeResponse - response", LOG_DEBUG
);
203 DesEncrypt( u_char
*clear
, /* IN 8 octets */
204 u_char
*key
, /* IN 7 octets */
205 u_char
*cipher
/* OUT 8 octets */)
208 u_char crypt_key
[66];
209 u_char des_input
[66];
211 MakeKey(key
, des_key
);
213 Expand(des_key
, crypt_key
);
217 CHAPDEBUG((LOG_INFO
, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n",
218 clear
[0], clear
[1], clear
[2], clear
[3], clear
[4], clear
[5], clear
[6], clear
[7]));
221 Expand(clear
, des_input
);
222 encrypt(des_input
, 0);
223 Collapse(des_input
, cipher
);
226 CHAPDEBUG((LOG_INFO
, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n",
227 cipher
[0], cipher
[1], cipher
[2], cipher
[3], cipher
[4], cipher
[5], cipher
[6], cipher
[7]));
231 #else /* USE_CRYPT */
234 DesEncrypt( u_char
*clear
, /* IN 8 octets */
235 u_char
*key
, /* IN 7 octets */
236 u_char
*cipher
/* OUT 8 octets */)
239 des_key_schedule key_schedule
;
241 MakeKey(key
, des_key
);
243 des_set_key(&des_key
, key_schedule
);
246 CHAPDEBUG((LOG_INFO
, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n",
247 clear
[0], clear
[1], clear
[2], clear
[3], clear
[4], clear
[5], clear
[6], clear
[7]));
250 des_ecb_encrypt((des_cblock
*)clear
, (des_cblock
*)cipher
, key_schedule
, 1);
253 CHAPDEBUG((LOG_INFO
, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n",
254 cipher
[0], cipher
[1], cipher
[2], cipher
[3], cipher
[4], cipher
[5], cipher
[6], cipher
[7]));
258 #endif /* USE_CRYPT */
262 Get7Bits( u_char
*input
, int startBit
)
264 register unsigned int word
;
266 word
= (unsigned)input
[startBit
/ 8] << 8;
267 word
|= (unsigned)input
[startBit
/ 8 + 1];
269 word
>>= 15 - (startBit
% 8 + 7);
276 /* in == 8-byte string (expanded version of the 56-bit key)
277 * out == 64-byte string where each byte is either 1 or 0
278 * Note that the low-order "bit" is always ignored by by setkey()
281 Expand(u_char
*in
, u_char
*out
)
286 for(i
= 0; i
< 64; in
++){
288 for(j
= 7; j
>= 0; j
--) {
289 *out
++ = (c
>> j
) & 01;
295 /* The inverse of Expand
298 Collapse(u_char
*in
, u_char
*out
)
304 for (i
= 0; i
< 64; i
+= 8, out
++) {
306 for (j
= 7; j
>= 0; j
--, in
++) {
315 MakeKey( u_char
*key
, /* IN 56 bit DES key missing parity bits */
316 u_char
*des_key
/* OUT 64 bit DES key with parity bits added */)
318 des_key
[0] = Get7Bits(key
, 0);
319 des_key
[1] = Get7Bits(key
, 7);
320 des_key
[2] = Get7Bits(key
, 14);
321 des_key
[3] = Get7Bits(key
, 21);
322 des_key
[4] = Get7Bits(key
, 28);
323 des_key
[5] = Get7Bits(key
, 35);
324 des_key
[6] = Get7Bits(key
, 42);
325 des_key
[7] = Get7Bits(key
, 49);
328 des_set_odd_parity((des_cblock
*)des_key
);
332 CHAPDEBUG((LOG_INFO
, "MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X\n",
333 key
[0], key
[1], key
[2], key
[3], key
[4], key
[5], key
[6]));
334 CHAPDEBUG((LOG_INFO
, "MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X\n",
335 des_key
[0], des_key
[1], des_key
[2], des_key
[3], des_key
[4], des_key
[5], des_key
[6], des_key
[7]));
340 ChapMS_NT( char *rchallenge
,
344 MS_ChapResponse
*response
)
348 u_char unicodePassword
[MAX_NT_PASSWORD
* 2];
349 static int low_byte_first
= -1;
351 /* Initialize the Unicode version of the secret (== password). */
352 /* This implicitly supports 8-bit ISO8859/1 characters. */
353 BZERO(unicodePassword
, sizeof(unicodePassword
));
354 for (i
= 0; i
< secret_len
; i
++) {
355 unicodePassword
[i
* 2] = (u_char
)secret
[i
];
357 MDbegin(&md4Context
);
358 MDupdate(&md4Context
, unicodePassword
, secret_len
* 2 * 8); /* Unicode is 2 bytes/char, *8 for bit count */
360 if (low_byte_first
== -1) {
361 low_byte_first
= (htons((unsigned short int)1) != 1);
363 if (low_byte_first
== 0) {
364 MDreverse((u_long
*)&md4Context
); /* sfb 961105 */
367 MDupdate(&md4Context
, NULL
, 0); /* Tell MD4 we're done */
369 ChallengeResponse(rchallenge
, (char *)md4Context
.buffer
, response
->NTResp
);
373 static u_char
*StdText
= (u_char
*)"KGS!@#$%"; /* key from rasapi32.dll */
376 ChapMS_LANMan( char *rchallenge
,
380 MS_ChapResponse
*response
)
383 u_char UcasePassword
[MAX_NT_PASSWORD
]; /* max is actually 14 */
384 u_char PasswordHash
[16];
386 /* LANMan password is case insensitive */
387 BZERO(UcasePassword
, sizeof(UcasePassword
));
388 for (i
= 0; i
< secret_len
; i
++) {
389 UcasePassword
[i
] = (u_char
)toupper(secret
[i
]);
391 DesEncrypt( StdText
, UcasePassword
+ 0, PasswordHash
+ 0 );
392 DesEncrypt( StdText
, UcasePassword
+ 7, PasswordHash
+ 8 );
393 ChallengeResponse(rchallenge
, PasswordHash
, response
->LANManResp
);
397 #endif /* MSCHAP_SUPPORT */
399 #endif /* PPP_SUPPORT */