1/*
2 * chap_ms.c - Microsoft MS-CHAP compatible implementation.
3 *
4 * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
5 * http://www.strataware.com/
6 *
7 * All rights reserved.
8 *
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.
17 *
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.
21 */
22
23/*
24 * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
25 *
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.
30 *
31 *   You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
32 */
33
34#define RCSID	"$Id$"
35
36#ifdef CHAPMS
37
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <ctype.h>
42#include <sys/types.h>
43#include <sys/time.h>
44#include <unistd.h>
45#ifdef HAVE_CRYPT_H
46#include <crypt.h>
47#endif
48
49#include "pppd.h"
50#include "chap.h"
51#include "chap_ms.h"
52#include "md4.h"
53
54#ifndef USE_CRYPT
55#include <des.h>
56#endif
57
58static const char rcsid[] = RCSID;
59
60typedef struct {
61    u_char LANManResp[24];
62    u_char NTResp[24];
63    u_char UseNT;		/* If 1, ignore the LANMan response field */
64} MS_ChapResponse;
65/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
66   in case this struct gets padded. */
67
68
69static void	ChallengeResponse __P((u_char *, u_char *, u_char *));
70static void	DesEncrypt __P((u_char *, u_char *, u_char *));
71static void	MakeKey __P((u_char *, u_char *));
72static u_char	Get7Bits __P((u_char *, int));
73static void	ChapMS_NT __P((char *, int, char *, int, MS_ChapResponse *));
74#ifdef MSLANMAN
75static void	ChapMS_LANMan __P((char *, int, char *, int, MS_ChapResponse *));
76#endif
77
78#ifdef USE_CRYPT
79static void	Expand __P((u_char *, u_char *));
80static void	Collapse __P((u_char *, u_char *));
81#endif
82
83#ifdef MSLANMAN
84bool	ms_lanman = 0;    	/* Use LanMan password instead of NT */
85			  	/* Has meaning only with MS-CHAP challenges */
86#endif
87
88static void
89ChallengeResponse(challenge, pwHash, response)
90    u_char *challenge;	/* IN   8 octets */
91    u_char *pwHash;	/* IN  16 octets */
92    u_char *response;	/* OUT 24 octets */
93{
94    char    ZPasswordHash[21];
95
96    BZERO(ZPasswordHash, sizeof(ZPasswordHash));
97    BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
98
99
100    DesEncrypt(challenge, ZPasswordHash +  0, response + 0);
101    DesEncrypt(challenge, ZPasswordHash +  7, response + 8);
102    DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
103
104}
105
106
107#ifdef USE_CRYPT
108static void
109DesEncrypt(clear, key, cipher)
110    u_char *clear;	/* IN  8 octets */
111    u_char *key;	/* IN  7 octets */
112    u_char *cipher;	/* OUT 8 octets */
113{
114    u_char des_key[8];
115    u_char crypt_key[66];
116    u_char des_input[66];
117
118    MakeKey(key, des_key);
119
120    Expand(des_key, crypt_key);
121    setkey(crypt_key);
122
123
124    Expand(clear, des_input);
125    encrypt(des_input, 0);
126    Collapse(des_input, cipher);
127
128}
129
130#else /* USE_CRYPT */
131
132static void
133DesEncrypt(clear, key, cipher)
134    u_char *clear;	/* IN  8 octets */
135    u_char *key;	/* IN  7 octets */
136    u_char *cipher;	/* OUT 8 octets */
137{
138    des_cblock		des_key;
139    des_key_schedule	key_schedule;
140
141    MakeKey(key, des_key);
142
143    des_set_key(&des_key, key_schedule);
144
145
146    des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
147
148}
149
150#endif /* USE_CRYPT */
151
152
153static u_char Get7Bits(input, startBit)
154    u_char *input;
155    int startBit;
156{
157    register unsigned int	word;
158
159    word  = (unsigned)input[startBit / 8] << 8;
160    word |= (unsigned)input[startBit / 8 + 1];
161
162    word >>= 15 - (startBit % 8 + 7);
163
164    return word & 0xFE;
165}
166
167#ifdef USE_CRYPT
168
169/* in == 8-byte string (expanded version of the 56-bit key)
170 * out == 64-byte string where each byte is either 1 or 0
171 * Note that the low-order "bit" is always ignored by by setkey()
172 */
173static void Expand(in, out)
174    u_char *in;
175    u_char *out;
176{
177        int j, c;
178        int i;
179
180        for(i = 0; i < 64; in++){
181		c = *in;
182                for(j = 7; j >= 0; j--)
183                        *out++ = (c >> j) & 01;
184                i += 8;
185        }
186}
187
188/* The inverse of Expand
189 */
190static void Collapse(in, out)
191    u_char *in;
192    u_char *out;
193{
194        int j;
195        int i;
196	unsigned int c;
197
198	for (i = 0; i < 64; i += 8, out++) {
199	    c = 0;
200	    for (j = 7; j >= 0; j--, in++)
201		c |= *in << j;
202	    *out = c & 0xff;
203	}
204}
205#endif
206
207static void MakeKey(key, des_key)
208    u_char *key;	/* IN  56 bit DES key missing parity bits */
209    u_char *des_key;	/* OUT 64 bit DES key with parity bits added */
210{
211    des_key[0] = Get7Bits(key,  0);
212    des_key[1] = Get7Bits(key,  7);
213    des_key[2] = Get7Bits(key, 14);
214    des_key[3] = Get7Bits(key, 21);
215    des_key[4] = Get7Bits(key, 28);
216    des_key[5] = Get7Bits(key, 35);
217    des_key[6] = Get7Bits(key, 42);
218    des_key[7] = Get7Bits(key, 49);
219
220#ifndef USE_CRYPT
221    des_set_odd_parity((des_cblock *)des_key);
222#endif
223
224}
225
226static void
227ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, response)
228    char *rchallenge;
229    int rchallenge_len;
230    char *secret;
231    int secret_len;
232    MS_ChapResponse    *response;
233{
234    int			i;
235#ifdef __NetBSD__
236    /* NetBSD uses the libc md4 routines which take bytes instead of bits */
237    int			mdlen = secret_len * 2;
238#else
239    int			mdlen = secret_len * 2 * 8;
240#endif
241    MD4_CTX		md4Context;
242    u_char		hash[MD4_SIGNATURE_SIZE];
243    u_char		unicodePassword[MAX_NT_PASSWORD * 2];
244
245    /* Initialize the Unicode version of the secret (== password). */
246    /* This implicitly supports 8-bit ISO8859/1 characters. */
247    BZERO(unicodePassword, sizeof(unicodePassword));
248    for (i = 0; i < secret_len; i++)
249	unicodePassword[i * 2] = (u_char)secret[i];
250
251    MD4Init(&md4Context);
252    MD4Update(&md4Context, unicodePassword, mdlen);
253
254    MD4Final(hash, &md4Context); 	/* Tell MD4 we're done */
255
256    ChallengeResponse(rchallenge, hash, response->NTResp);
257}
258
259#ifdef MSLANMAN
260static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
261
262static void
263ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, response)
264    char *rchallenge;
265    int rchallenge_len;
266    char *secret;
267    int secret_len;
268    MS_ChapResponse	*response;
269{
270    int			i;
271    u_char		UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
272    u_char		PasswordHash[MD4_SIGNATURE_SIZE];
273
274    /* LANMan password is case insensitive */
275    BZERO(UcasePassword, sizeof(UcasePassword));
276    for (i = 0; i < secret_len; i++)
277       UcasePassword[i] = (u_char)toupper(secret[i]);
278    DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
279    DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
280    ChallengeResponse(rchallenge, PasswordHash, response->LANManResp);
281}
282#endif
283
284void
285ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len)
286    chap_state *cstate;
287    char *rchallenge;
288    int rchallenge_len;
289    char *secret;
290    int secret_len;
291{
292    MS_ChapResponse	response;
293
294    BZERO(&response, sizeof(response));
295
296    /* Calculate both always */
297    ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response);
298
299#ifdef MSLANMAN
300    ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response);
301
302    /* prefered method is set by option  */
303    response.UseNT = !ms_lanman;
304#else
305    response.UseNT = 1;
306#endif
307
308    BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN);
309    cstate->resp_length = MS_CHAP_RESPONSE_LEN;
310}
311
312#endif /* CHAPMS */
313