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