2 * chap_ms.c - Microsoft MS-CHAP compatible implementation.
4 * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
5 * http://www.strataware.com/
9 * Redistribution and use in source and binary forms are permitted
10 * provided that the above copyright notice and this paragraph are
11 * duplicated in all such forms and that any documentation,
12 * advertising materials, and other materials related to such
13 * distribution and use acknowledge that the software was developed
14 * by Eric Rosenquist. The name of the author may not be used to
15 * endorse or promote products derived from this software without
16 * specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
20 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
24 * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
26 * Implemented LANManager type password response to MS-CHAP challenges.
27 * Now pppd provides both NT style and LANMan style blocks, and the
28 * prefered is set by option "ms-lanman". Default is to use NT.
29 * The hash text (StdText) was taken from Win95 RASAPI32.DLL.
31 * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
34 #define RCSID "$Id: chap_ms.c,v 1.16 2002/03/01 14:39:18 dfs Exp $"
42 #include <sys/types.h>
58 static const char rcsid[] = RCSID;
61 static void ChallengeResponse __P((u_char *, u_char *, u_char *));
62 static void DesEncrypt __P((u_char *, u_char *, u_char *));
63 static void MakeKey __P((u_char *, u_char *));
64 static u_char Get7Bits __P((u_char *, int));
65 static void ChapMS_NT __P((char *, int, char *, int, MS_ChapResponse *));
67 static void ChapMS_LANMan __P((char *, int, char *, int, MS_ChapResponse *));
71 static void Expand __P((u_char *, u_char *));
72 static void Collapse __P((u_char *, u_char *));
76 bool ms_lanman = 0; /* Use LanMan password instead of NT */
77 /* Has meaning only with MS-CHAP challenges */
81 ChallengeResponse(challenge, pwHash, response)
82 u_char *challenge; /* IN 8 octets */
83 u_char *pwHash; /* IN 16 octets */
84 u_char *response; /* OUT 24 octets */
86 char ZPasswordHash[21];
88 BZERO(ZPasswordHash, sizeof(ZPasswordHash));
89 BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
92 dbglog("ChallengeResponse - ZPasswordHash %.*B",
93 sizeof(ZPasswordHash), ZPasswordHash);
96 DesEncrypt(challenge, ZPasswordHash + 0, response + 0);
97 DesEncrypt(challenge, ZPasswordHash + 7, response + 8);
98 DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
101 dbglog("ChallengeResponse - response %.24B", response);
108 DesEncrypt(clear, key, cipher)
109 u_char *clear; /* IN 8 octets */
110 u_char *key; /* IN 7 octets */
111 u_char *cipher; /* OUT 8 octets */
114 u_char crypt_key[66];
115 u_char des_input[66];
117 MakeKey(key, des_key);
119 Expand(des_key, crypt_key);
123 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
126 Expand(clear, des_input);
127 encrypt(des_input, 0);
128 Collapse(des_input, cipher);
131 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
135 #else /* USE_CRYPT */
138 DesEncrypt(clear, key, cipher)
139 u_char *clear; /* IN 8 octets */
140 u_char *key; /* IN 7 octets */
141 u_char *cipher; /* OUT 8 octets */
144 des_key_schedule key_schedule;
146 MakeKey(key, des_key);
148 des_set_key(&des_key, key_schedule);
151 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
154 des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
157 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
161 #endif /* USE_CRYPT */
164 static u_char Get7Bits(input, startBit)
168 register unsigned int word;
170 word = (unsigned)input[startBit / 8] << 8;
171 word |= (unsigned)input[startBit / 8 + 1];
173 word >>= 15 - (startBit % 8 + 7);
180 /* in == 8-byte string (expanded version of the 56-bit key)
181 * out == 64-byte string where each byte is either 1 or 0
182 * Note that the low-order "bit" is always ignored by by setkey()
184 static void Expand(in, out)
191 for(i = 0; i < 64; in++){
193 for(j = 7; j >= 0; j--)
194 *out++ = (c >> j) & 01;
199 /* The inverse of Expand
201 static void Collapse(in, out)
209 for (i = 0; i < 64; i += 8, out++) {
211 for (j = 7; j >= 0; j--, in++)
218 static void MakeKey(key, des_key)
219 u_char *key; /* IN 56 bit DES key missing parity bits */
220 u_char *des_key; /* OUT 64 bit DES key with parity bits added */
222 des_key[0] = Get7Bits(key, 0);
223 des_key[1] = Get7Bits(key, 7);
224 des_key[2] = Get7Bits(key, 14);
225 des_key[3] = Get7Bits(key, 21);
226 des_key[4] = Get7Bits(key, 28);
227 des_key[5] = Get7Bits(key, 35);
228 des_key[6] = Get7Bits(key, 42);
229 des_key[7] = Get7Bits(key, 49);
232 des_set_odd_parity((des_cblock *)des_key);
236 CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %.7B", key));
237 CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %.8B", des_key));
242 ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, response)
247 MS_ChapResponse *response;
251 /* NetBSD uses the libc md4 routines which take bytes instead of bits */
252 int mdlen = secret_len * 2;
254 int mdlen = secret_len * 2 * 8;
257 u_char hash[MD4_SIGNATURE_SIZE];
258 u_char unicodePassword[MAX_NT_PASSWORD * 2];
260 /* Initialize the Unicode version of the secret (== password). */
261 /* This implicitly supports 8-bit ISO8859/1 characters. */
262 BZERO(unicodePassword, sizeof(unicodePassword));
263 for (i = 0; i < secret_len; i++)
264 unicodePassword[i * 2] = (u_char)secret[i];
266 MD4Init(&md4Context);
267 MD4Update(&md4Context, unicodePassword, mdlen);
269 MD4Final(hash, &md4Context); /* Tell MD4 we're done */
271 ChallengeResponse(rchallenge, hash, response->NTResp);
275 static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
278 ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, response)
283 MS_ChapResponse *response;
286 u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
287 u_char PasswordHash[MD4_SIGNATURE_SIZE];
289 /* LANMan password is case insensitive */
290 BZERO(UcasePassword, sizeof(UcasePassword));
291 for (i = 0; i < secret_len; i++)
292 UcasePassword[i] = (u_char)toupper(secret[i]);
293 DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
294 DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
295 ChallengeResponse(rchallenge, PasswordHash, response->LANManResp);
300 ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len)
307 MS_ChapResponse response;
310 CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret));
312 BZERO(&response, sizeof(response));
314 /* Calculate both always */
315 ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response);
318 ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response);
320 /* prefered method is set by option */
321 response.UseNT = !ms_lanman;
326 BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN);
327 cstate->resp_length = MS_CHAP_RESPONSE_LEN;