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