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