xref: /dragonfly/usr.sbin/ppp/chap_ms.c (revision 37de577a)
1 /*-
2  * Copyright (c) 1997        Gabor Kincses <gabor@acm.org>
3  *               1997 - 2001 Brian Somers <brian@Awfulhak.org>
4  *          based on work by Eric Rosenquist
5  *                           Strata Software Limited.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD: src/usr.sbin/ppp/chap_ms.c,v 1.9.2.6 2002/09/01 02:12:23 brian Exp $
30  */
31 
32 #include <ctype.h>
33 #include <sys/types.h>
34 #include <stdlib.h>
35 #if defined(__DragonFly__) || defined(__NetBSD__)
36 #include <openssl/des.h>
37 #else
38 #include <des.h>
39 #endif
40 #include <openssl/sha.h>
41 #include <openssl/md4.h>
42 #include <string.h>
43 
44 #include "chap_ms.h"
45 
46 /*
47  * Documentation & specifications:
48  *
49  * MS-CHAP (CHAP80)	rfc2433
50  * MS-CHAP-V2 (CHAP81)	rfc2759
51  * MPPE key management	draft-ietf-pppext-mppe-keys-02.txt
52  */
53 
54 static char SHA1_Pad1[40] =
55   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
56    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
58    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
59 
60 static char SHA1_Pad2[40] =
61   {0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
62    0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
63    0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
64    0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2};
65 
66 /* unused, for documentation only */
67 /* only NTResp is filled in for FreeBSD */
68 struct MS_ChapResponse {
69     u_char LANManResp[24];
70     u_char NTResp[24];
71     u_char UseNT;	/* If 1, ignore the LANMan response field */
72 };
73 
74 static u_char
75 Get7Bits(u_char *input, int startBit)
76 {
77     unsigned int word;
78 
79     word  = (unsigned)input[startBit / 8] << 8;
80     word |= (unsigned)input[startBit / 8 + 1];
81 
82     word >>= 15 - (startBit % 8 + 7);
83 
84     return word & 0xFE;
85 }
86 
87 /* IN  56 bit DES key missing parity bits
88    OUT 64 bit DES key with parity bits added */
89 static void
90 MakeKey(u_char *key, u_char *des_key)
91 {
92     des_key[0] = Get7Bits(key,  0);
93     des_key[1] = Get7Bits(key,  7);
94     des_key[2] = Get7Bits(key, 14);
95     des_key[3] = Get7Bits(key, 21);
96     des_key[4] = Get7Bits(key, 28);
97     des_key[5] = Get7Bits(key, 35);
98     des_key[6] = Get7Bits(key, 42);
99     des_key[7] = Get7Bits(key, 49);
100 
101     DES_set_odd_parity((DES_cblock *)des_key);
102 }
103 
104 static void /* IN 8 octets IN 7 octest OUT 8 octets */
105 DesEncrypt(u_char *clear, u_char *key, u_char *cipher)
106 {
107     DES_cblock		des_key;
108     DES_key_schedule	key_schedule;
109 
110     MakeKey(key, des_key);
111     DES_set_key(&des_key, &key_schedule);
112     DES_ecb_encrypt((DES_cblock *)clear, (DES_cblock *)cipher, &key_schedule, 1);
113 }
114 
115 static void      /* IN 8 octets      IN 16 octets     OUT 24 octets */
116 ChallengeResponse(u_char *challenge, u_char *pwHash, u_char *response)
117 {
118     char    ZPasswordHash[21];
119 
120     memset(ZPasswordHash, '\0', sizeof ZPasswordHash);
121     memcpy(ZPasswordHash, pwHash, 16);
122 
123     DesEncrypt(challenge, ZPasswordHash +  0, response + 0);
124     DesEncrypt(challenge, ZPasswordHash +  7, response + 8);
125     DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
126 }
127 
128 void
129 NtPasswordHash(char *key, int keylen, char *hash)
130 {
131   MD4_CTX MD4context;
132 
133   MD4_Init(&MD4context);
134   MD4_Update(&MD4context, key, keylen);
135   MD4_Final(hash, &MD4context);
136 }
137 
138 void
139 HashNtPasswordHash(char *hash, char *hashhash)
140 {
141   MD4_CTX MD4context;
142 
143   MD4_Init(&MD4context);
144   MD4_Update(&MD4context, hash, 16);
145   MD4_Final(hashhash, &MD4context);
146 }
147 
148 static void
149 ChallengeHash(char *PeerChallenge, char *AuthenticatorChallenge,
150               char *UserName, char *Challenge)
151 {
152   SHA_CTX Context;
153   char Digest[SHA_DIGEST_LENGTH];
154   char *Name;
155 
156   Name = strrchr(UserName, '\\');
157   if(NULL == Name)
158     Name = UserName;
159   else
160     Name++;
161 
162   SHA1_Init(&Context);
163 
164   SHA1_Update(&Context, PeerChallenge, 16);
165   SHA1_Update(&Context, AuthenticatorChallenge, 16);
166   SHA1_Update(&Context, Name, strlen(Name));
167 
168   SHA1_Final(Digest, &Context);
169   memcpy(Challenge, Digest, 8);
170 }
171 
172 void
173 GenerateNTResponse(char *AuthenticatorChallenge, char *PeerChallenge,
174                    char *UserName, char *Password,
175                    int PasswordLen, char *Response)
176 {
177   char Challenge[8];
178   char PasswordHash[16];
179 
180   ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, Challenge);
181   NtPasswordHash(Password, PasswordLen, PasswordHash);
182   ChallengeResponse(Challenge, PasswordHash, Response);
183 }
184 
185 #define LENGTH 20
186 static char *
187 SHA1_End(SHA_CTX *ctx, char *buf)
188 {
189     int i;
190     unsigned char digest[LENGTH];
191     static const char hex[]="0123456789abcdef";
192 
193     if (!buf)
194         buf = malloc(2*LENGTH + 1);
195     if (!buf)
196         return 0;
197     SHA1_Final(digest, ctx);
198     for (i = 0; i < LENGTH; i++) {
199         buf[i+i] = hex[digest[i] >> 4];
200         buf[i+i+1] = hex[digest[i] & 0x0f];
201     }
202     buf[i+i] = '\0';
203     return buf;
204 }
205 
206 void
207 GenerateAuthenticatorResponse(char *Password, int PasswordLen,
208                               char *NTResponse, char *PeerChallenge,
209                               char *AuthenticatorChallenge, char *UserName,
210                               char *AuthenticatorResponse)
211 {
212   SHA_CTX Context;
213   char PasswordHash[16];
214   char PasswordHashHash[16];
215   char Challenge[8];
216   u_char Digest[SHA_DIGEST_LENGTH];
217   int i;
218 
219       /*
220        * "Magic" constants used in response generation
221        */
222   char Magic1[39] =
223          {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
224           0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
225           0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
226           0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
227 
228 
229   char Magic2[41] =
230          {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
231           0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
232           0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
233           0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
234           0x6E};
235       /*
236        * Hash the password with MD4
237        */
238   NtPasswordHash(Password, PasswordLen, PasswordHash);
239       /*
240        * Now hash the hash
241        */
242   HashNtPasswordHash(PasswordHash, PasswordHashHash);
243 
244   SHA1_Init(&Context);
245   SHA1_Update(&Context, PasswordHashHash, 16);
246   SHA1_Update(&Context, NTResponse, 24);
247   SHA1_Update(&Context, Magic1, 39);
248   SHA1_Final(Digest, &Context);
249   ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, Challenge);
250   SHA1_Init(&Context);
251   SHA1_Update(&Context, Digest, 20);
252   SHA1_Update(&Context, Challenge, 8);
253   SHA1_Update(&Context, Magic2, 41);
254 
255       /*
256        * Encode the value of 'Digest' as "S=" followed by
257        * 40 ASCII hexadecimal digits and return it in
258        * AuthenticatorResponse.
259        * For example,
260        *   "S=0123456789ABCDEF0123456789ABCDEF01234567"
261        */
262   AuthenticatorResponse[0] = 'S';
263   AuthenticatorResponse[1] = '=';
264   SHA1_End(&Context, AuthenticatorResponse + 2);
265   for (i=2; i<42; i++)
266     AuthenticatorResponse[i] = toupper(AuthenticatorResponse[i]);
267 
268 }
269 
270 void
271 GetMasterKey(char *PasswordHashHash, char *NTResponse, char *MasterKey)
272 {
273   char Digest[SHA_DIGEST_LENGTH];
274   SHA_CTX Context;
275   static char Magic1[27] =
276       {0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
277        0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
278        0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79};
279 
280   SHA1_Init(&Context);
281   SHA1_Update(&Context, PasswordHashHash, 16);
282   SHA1_Update(&Context, NTResponse, 24);
283   SHA1_Update(&Context, Magic1, 27);
284   SHA1_Final(Digest, &Context);
285   memcpy(MasterKey, Digest, 16);
286 }
287 
288 void
289 GetAsymetricStartKey(char *MasterKey, char *SessionKey, int SessionKeyLength,
290                      int IsSend, int IsServer)
291 {
292   char Digest[SHA_DIGEST_LENGTH];
293   SHA_CTX Context;
294   char *s;
295 
296   static char Magic2[84] =
297       {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
298        0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
299        0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
300        0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
301        0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
302        0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
303        0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
304        0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
305        0x6b, 0x65, 0x79, 0x2e};
306 
307   static char Magic3[84] =
308       {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
309        0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
310        0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
311        0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
312        0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
313        0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
314        0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
315        0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
316        0x6b, 0x65, 0x79, 0x2e};
317 
318   if (IsSend) {
319      if (IsServer) {
320         s = Magic3;
321      } else {
322         s = Magic2;
323      }
324   } else {
325      if (IsServer) {
326         s = Magic2;
327      } else {
328         s = Magic3;
329      }
330   }
331 
332   SHA1_Init(&Context);
333   SHA1_Update(&Context, MasterKey, 16);
334   SHA1_Update(&Context, SHA1_Pad1, 40);
335   SHA1_Update(&Context, s, 84);
336   SHA1_Update(&Context, SHA1_Pad2, 40);
337   SHA1_Final(Digest, &Context);
338 
339   memcpy(SessionKey, Digest, SessionKeyLength);
340 }
341 
342 void
343 GetNewKeyFromSHA(char *StartKey, char *SessionKey, long SessionKeyLength,
344                  char *InterimKey)
345 {
346   SHA_CTX Context;
347   char Digest[SHA_DIGEST_LENGTH];
348 
349   SHA1_Init(&Context);
350   SHA1_Update(&Context, StartKey, SessionKeyLength);
351   SHA1_Update(&Context, SHA1_Pad1, 40);
352   SHA1_Update(&Context, SessionKey, SessionKeyLength);
353   SHA1_Update(&Context, SHA1_Pad2, 40);
354   SHA1_Final(Digest, &Context);
355 
356   memcpy(InterimKey, Digest, SessionKeyLength);
357 }
358 
359 #if 0
360 static void
361 Get_Key(char *InitialSessionKey, char *CurrentSessionKey,
362         int LengthOfDesiredKey)
363 {
364   SHA_CTX Context;
365   char Digest[SHA_DIGEST_LENGTH];
366 
367   SHA1_Init(&Context);
368   SHA1_Update(&Context, InitialSessionKey, LengthOfDesiredKey);
369   SHA1_Update(&Context, SHA1_Pad1, 40);
370   SHA1_Update(&Context, CurrentSessionKey, LengthOfDesiredKey);
371   SHA1_Update(&Context, SHA1_Pad2, 40);
372   SHA1_Final(Digest, &Context);
373 
374   memcpy(CurrentSessionKey, Digest, LengthOfDesiredKey);
375 }
376 #endif
377 
378 /* passwordHash 16-bytes MD4 hashed password
379    challenge    8-bytes peer CHAP challenge
380    since passwordHash is in a 24-byte buffer, response is written in there */
381 void
382 mschap_NT(char *passwordHash, char *challenge)
383 {
384     u_char response[24];
385 
386     ChallengeResponse(challenge, passwordHash, response);
387     memcpy(passwordHash, response, 24);
388     passwordHash[24] = 1;		/* NT-style response */
389 }
390 
391 void
392 mschap_LANMan(char *digest, char *challenge, char *secret)
393 {
394   static u_char salt[] = "KGS!@#$%";	/* RASAPI32.dll */
395   char SECRET[14], *ptr, *end;
396   u_char hash[16];
397 
398   end = SECRET + sizeof SECRET;
399   for (ptr = SECRET; *secret && ptr < end; ptr++, secret++)
400     *ptr = toupper(*secret);
401   if (ptr < end)
402     memset(ptr, '\0', end - ptr);
403 
404   DesEncrypt(salt, SECRET, hash);
405   DesEncrypt(salt, SECRET + 7, hash + 8);
406 
407   ChallengeResponse(challenge, hash, digest);
408 }
409