]> git.ozlabs.org Git - ppp.git/blob - pppd/chap_ms.c
d10b99a4f1980c6ac2f99fccd51d3bd2c202c9e2
[ppp.git] / pppd / chap_ms.c
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 /*
35  * Modifications by Frank Cusack, frank@google.com, March 2002.
36  *
37  *   Implemented MS-CHAPv2 functionality.  Heavily based on
38  *   sample implementation in RFC 2759.
39  */
40
41 #define RCSID   "$Id: chap_ms.c,v 1.18 2002/03/05 15:14:04 dfs Exp $"
42
43 #ifdef CHAPMS
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <ctype.h>
49 #include <sys/types.h>
50 #include <sys/time.h>
51 #include <unistd.h>
52 #ifdef HAVE_CRYPT_H
53 #include <crypt.h>
54 #endif
55
56 #include "pppd.h"
57 #include "chap.h"
58 #include "chap_ms.h"
59 #include "md4.h"
60 #include "sha1.h"
61
62 #ifndef USE_CRYPT
63 #include <des.h>
64 #endif
65
66 static const char rcsid[] = RCSID;
67
68
69 static void     ChallengeHash __P((u_char[16], u_char *, char *, u_char[8]));
70 static void     ascii2unicode __P((char[], int, u_char[]));
71 static void     NTPasswordHash __P((char *, int, u_char[MD4_SIGNATURE_SIZE]));
72 static void     ChallengeResponse __P((u_char *, u_char *, u_char[24]));
73 static void     DesEncrypt __P((u_char *, u_char *, u_char[8]));
74 static void     MakeKey __P((u_char *, u_char *));
75 static u_char   Get7Bits __P((u_char *, int));
76 static void     ChapMS_NT __P((u_char *, char *, int, u_char[24]));
77 static void     ChapMS2_NT __P((char *, u_char[16], char *, char *, int,
78                                 u_char[24]));
79 static void     GenerateAuthenticatorResponse __P((char*, int, u_char[24],
80                                                    u_char[16], u_char *,
81                                                    char *, u_char[41]));
82 #ifdef MSLANMAN
83 static void     ChapMS_LANMan __P((u_char *, char *, int, MS_ChapResponse *));
84 #endif
85
86 #ifdef USE_CRYPT
87 static void     Expand __P((u_char *, u_char *));
88 static void     Collapse __P((u_char *, u_char *));
89 #endif
90
91 extern double drand48 __P((void));
92
93 #ifdef MSLANMAN
94 bool    ms_lanman = 0;          /* Use LanMan password instead of NT */
95                                 /* Has meaning only with MS-CHAP challenges */
96 #endif
97
98 static void
99 ChallengeResponse(u_char *challenge,
100                   u_char PasswordHash[MD4_SIGNATURE_SIZE],
101                   u_char response[24])
102 {
103     char    ZPasswordHash[21];
104
105     BZERO(ZPasswordHash, sizeof(ZPasswordHash));
106     BCOPY(PasswordHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
107
108 #if 0
109     dbglog("ChallengeResponse - ZPasswordHash %.*B",
110            sizeof(ZPasswordHash), ZPasswordHash);
111 #endif
112
113     DesEncrypt(challenge, ZPasswordHash +  0, &response[0]);
114     DesEncrypt(challenge, ZPasswordHash +  7, &response[8]);
115     DesEncrypt(challenge, ZPasswordHash + 14, &response[16]);
116
117 #if 0
118     dbglog("ChallengeResponse - response %.24B", response);
119 #endif
120 }
121
122
123 #ifdef USE_CRYPT
124 static void
125 DesEncrypt(u_char *clear, u_char *key, u_char cipher[8])
126 {
127     u_char des_key[8];
128     u_char crypt_key[66];
129     u_char des_input[66];
130
131     MakeKey(key, des_key);
132
133     Expand(des_key, crypt_key);
134     setkey(crypt_key);
135
136 #if 0
137     CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
138 #endif
139
140     Expand(clear, des_input);
141     encrypt(des_input, 0);
142     Collapse(des_input, cipher);
143
144 #if 0
145     CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
146 #endif
147 }
148
149 #else /* USE_CRYPT */
150
151 static void
152 DesEncrypt(u_char *clear, u_char *key, u_char cipher[8])
153 {
154     des_cblock          des_key;
155     des_key_schedule    key_schedule;
156
157     MakeKey(key, des_key);
158
159     des_set_key(&des_key, key_schedule);
160
161 #if 0
162     CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
163 #endif
164
165     des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
166
167 #if 0
168     CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
169 #endif
170 }
171
172 #endif /* USE_CRYPT */
173
174
175 static u_char Get7Bits(u_char *input, int startBit)
176 {
177     register unsigned int       word;
178
179     word  = (unsigned)input[startBit / 8] << 8;
180     word |= (unsigned)input[startBit / 8 + 1];
181
182     word >>= 15 - (startBit % 8 + 7);
183
184     return word & 0xFE;
185 }
186
187 #ifdef USE_CRYPT
188
189 /* in == 8-byte string (expanded version of the 56-bit key)
190  * out == 64-byte string where each byte is either 1 or 0
191  * Note that the low-order "bit" is always ignored by by setkey()
192  */
193 static void Expand(u_char *in, u_char *out)
194 {
195         int j, c;
196         int i;
197
198         for(i = 0; i < 64; in++){
199                 c = *in;
200                 for(j = 7; j >= 0; j--)
201                         *out++ = (c >> j) & 01;
202                 i += 8;
203         }
204 }
205
206 /* The inverse of Expand
207  */
208 static void Collapse(u_char *in, u_char *out)
209 {
210         int j;
211         int i;
212         unsigned int c;
213
214         for (i = 0; i < 64; i += 8, out++) {
215             c = 0;
216             for (j = 7; j >= 0; j--, in++)
217                 c |= *in << j;
218             *out = c & 0xff;
219         }
220 }
221 #endif
222
223 static void MakeKey(u_char *key, u_char *des_key)
224 {
225     des_key[0] = Get7Bits(key,  0);
226     des_key[1] = Get7Bits(key,  7);
227     des_key[2] = Get7Bits(key, 14);
228     des_key[3] = Get7Bits(key, 21);
229     des_key[4] = Get7Bits(key, 28);
230     des_key[5] = Get7Bits(key, 35);
231     des_key[6] = Get7Bits(key, 42);
232     des_key[7] = Get7Bits(key, 49);
233
234 #ifndef USE_CRYPT
235     des_set_odd_parity((des_cblock *)des_key);
236 #endif
237
238 #if 0
239     CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %.7B", key));
240     CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %.8B", des_key));
241 #endif
242 }
243
244
245 static void
246 ChallengeHash(u_char PeerChallenge[16], u_char *rchallenge,
247               char *username, u_char Challenge[8])
248     
249 {
250     SHA1_CTX    sha1Context;
251     u_char      sha1Hash[SHA1_SIGNATURE_SIZE];
252
253     SHA1_Init(&sha1Context);
254     SHA1_Update(&sha1Context, PeerChallenge, 16);
255     SHA1_Update(&sha1Context, rchallenge, 16);
256     SHA1_Update(&sha1Context, username, strlen(username));
257     SHA1_Final(sha1Hash, &sha1Context);
258
259     BCOPY(sha1Hash, Challenge, 8);
260 }
261
262 /*
263  * Convert the ASCII version of the password to Unicode.
264  * This implicitly supports 8-bit ISO8859/1 characters.
265  * This gives us the little-endian representation, which
266  * is assumed by all M$ CHAP RFCs.  (Unicode byte ordering
267  * is machine-dependent.)
268  */
269 static void
270 ascii2unicode(char ascii[], int ascii_len, u_char unicode[])
271 {
272     int i;
273
274     BZERO(unicode, ascii_len * 2);
275     for (i = 0; i < ascii_len; i++)
276         unicode[i * 2] = (u_char) ascii[i];
277 }
278
279 static void
280 NTPasswordHash(char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE])
281 {
282 #ifdef __NetBSD__
283     /* NetBSD uses the libc md4 routines which take bytes instead of bits */
284     int                 mdlen = secret_len;
285 #else
286     int                 mdlen = secret_len * 8;
287 #endif
288     MD4_CTX             md4Context;
289
290     MD4Init(&md4Context);
291     MD4Update(&md4Context, secret, mdlen);
292     MD4Final(hash, &md4Context);
293
294 }
295
296 static void
297 ChapMS_NT(u_char *rchallenge, char *secret, int secret_len,
298           u_char NTResponse[24])
299 {
300     u_char      unicodePassword[MAX_NT_PASSWORD * 2];
301     u_char      PasswordHash[MD4_SIGNATURE_SIZE];
302
303     /* Hash the Unicode version of the secret (== password). */
304     ascii2unicode(secret, secret_len, unicodePassword);
305     NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
306
307     ChallengeResponse(rchallenge, PasswordHash, NTResponse);
308 }
309
310 static void
311 ChapMS2_NT(char *rchallenge, u_char PeerChallenge[16], char *username,
312            char *secret, int secret_len, u_char NTResponse[24])
313 {
314     u_char      unicodePassword[MAX_NT_PASSWORD * 2];
315     u_char      PasswordHash[MD4_SIGNATURE_SIZE];
316     u_char      Challenge[8];
317
318     ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
319
320     /* Hash the Unicode version of the secret (== password). */
321     ascii2unicode(secret, secret_len, unicodePassword);
322     NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
323
324     ChallengeResponse(Challenge, PasswordHash, NTResponse);
325 }
326
327 #ifdef MSLANMAN
328 static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
329
330 static void
331 ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
332               u_char LMResponse[24])
333 {
334     int                 i;
335     u_char              UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
336     u_char              PasswordHash[MD4_SIGNATURE_SIZE];
337
338     /* LANMan password is case insensitive */
339     BZERO(UcasePassword, sizeof(UcasePassword));
340     for (i = 0; i < secret_len; i++)
341        UcasePassword[i] = (u_char)toupper(secret[i]);
342     DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
343     DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
344     ChallengeResponse(rchallenge, PasswordHash, LMResponse);
345 }
346 #endif
347
348
349 static void
350 GenerateAuthenticatorResponse(char *secret, int secret_len,
351                               u_char NTResponse[24], u_char PeerChallenge[16],
352                               u_char *rchallenge, char *username,
353                               u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
354 {
355     /*
356      * "Magic" constants used in response generation, from RFC 2759.
357      */
358     u_char Magic1[39] = /* "Magic server to client signing constant" */
359         {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
360          0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
361          0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
362          0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
363     u_char Magic2[41] = /* "Pad to make it do more than one iteration" */
364         {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
365          0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
366          0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
367          0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
368          0x6E};
369
370     int         i;
371     SHA1_CTX    sha1Context;
372     u_char      unicodePassword[MAX_NT_PASSWORD * 2];
373     u_char      PasswordHash[MD4_SIGNATURE_SIZE];
374     u_char      PasswordHashHash[MD4_SIGNATURE_SIZE];
375     u_char      Digest[SHA1_SIGNATURE_SIZE];
376     u_char      Challenge[8];
377
378     /* Hash (x2) the Unicode version of the secret (== password). */
379     ascii2unicode(secret, secret_len, unicodePassword);
380     NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
381     NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
382
383     SHA1_Init(&sha1Context);
384     SHA1_Update(&sha1Context, PasswordHashHash, sizeof(PasswordHashHash));
385     SHA1_Update(&sha1Context, NTResponse, 24);
386     SHA1_Update(&sha1Context, Magic1, sizeof(Magic1));
387     SHA1_Final(Digest, &sha1Context);
388
389     ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
390
391     SHA1_Init(&sha1Context);
392     SHA1_Update(&sha1Context, Digest, sizeof(Digest));
393     SHA1_Update(&sha1Context, Challenge, sizeof(Challenge));
394     SHA1_Update(&sha1Context, Magic2, sizeof(Magic2));
395     SHA1_Final(Digest, &sha1Context);
396
397     /* Convert to ASCII hex string. */
398     for (i = 0; i < MAX((MS_AUTH_RESPONSE_LENGTH / 2), sizeof(Digest)); i++)
399         sprintf(&authResponse[i * 2], "%02X", Digest[i]);
400 }
401
402
403 void
404 ChapMS(chap_state *cstate, u_char *rchallenge, char *secret, int secret_len,
405        MS_ChapResponse *response)
406 {
407 #if 0
408     CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret));
409 #endif
410     BZERO(response, sizeof(response));
411
412     /* Calculate both always */
413     ChapMS_NT(rchallenge, secret, secret_len, response->NTResp);
414
415 #ifdef MSLANMAN
416     ChapMS_LANMan(rchallenge, secret, secret_len, response);
417
418     /* prefered method is set by option  */
419     response->UseNT[0] = !ms_lanman;
420 #else
421     response->UseNT[0] = 1;
422 #endif
423
424 }
425
426
427 /*
428  * If PeerChallenge is NULL, one is generated and response->PeerChallenge
429  * is filled in.  Call this way when generating a response.
430  * If PeerChallenge is supplied, it is copied into response->PeerChallenge.
431  * Call this way when verifying a response.
432  *
433  * response->PeerChallenge is then used for calculation of the
434  * Authenticator Response.
435  */
436 void
437 ChapMS2(chap_state *cstate, u_char *rchallenge, u_char *PeerChallenge,
438         char *user, char *secret, int secret_len, MS_Chap2Response *response,
439         u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
440 {
441     u_char *p = response->PeerChallenge;
442     int i;
443
444     BZERO(response, sizeof(response));
445
446     /* Generate the Peer-Challenge if requested, or copy it if supplied. */
447     if (!PeerChallenge)
448         for (i = 0; i < sizeof(response->PeerChallenge); i++)
449             *p++ = (u_char) (drand48() * 0xff);
450     else
451         BCOPY(PeerChallenge, response->PeerChallenge,
452               sizeof(response->PeerChallenge));
453
454     /* Generate the NT-Response */
455     ChapMS2_NT(rchallenge, response->PeerChallenge, user,
456                secret, secret_len, response->NTResp);
457
458     /* Generate the Authenticator Response. */
459     GenerateAuthenticatorResponse(secret, secret_len, response->NTResp,
460                                   response->PeerChallenge, rchallenge,
461                                   user, authResponse);
462 }
463
464
465 #endif /* CHAPMS */