1 /*
2 * chap_ms.c - Microsoft MS-CHAP compatible implementation.
3 *
4 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
5 * Use is subject to license terms.
6 *
7 * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
8 * http://www.strataware.com/
9 *
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms are permitted
13 * provided that the above copyright notice and this paragraph are
14 * duplicated in all such forms and that any documentation,
15 * advertising materials, and other materials related to such
16 * distribution and use acknowledge that the software was developed
17 * by Eric Rosenquist. The name of the author may not be used to
18 * endorse or promote products derived from this software without
19 * specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
23 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
24 */
25
26 /*
27 * This module implements MS-CHAPv1 (RFC 2433) and MS-CHAPv2 (RFC 2759).
28 *
29 * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
30 *
31 * Implemented LANManager type password response to MS-CHAP challenges.
32 * Now pppd provides both NT style and LANMan style blocks, and the
33 * prefered is set by option "ms-lanman". Default is to use NT.
34 * The hash text (StdText) was taken from Win95 RASAPI32.DLL.
35 *
36 * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
37 *
38 * Modifications by James Carlson / james.d.carlson@sun.com, June 1st, 2000.
39 *
40 * Added MS-CHAPv2 support.
41 */
42
43 #if defined(CHAPMS) || defined(CHAPMSV2)
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 #ifdef CHAPMSV2
57 #include "sha1.h"
58 #endif
59
60 #ifndef USE_CRYPT
61 #include <des.h>
62 #endif
63
64 #include "pppd.h"
65 #include "chap.h"
66 #include "chap_ms.h"
67 #include "md4.h"
68
69 typedef struct {
70 u_char LANManResp[24];
71 u_char NTResp[24];
72 u_char UseNT; /* If 1, ignore the LANMan response field */
73 } MS_ChapResponse;
74 /* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
75 in case this struct gets padded. */
76
77 typedef struct {
78 u_char PeerChallenge[16];
79 u_char MustBeZero[8];
80 u_char NTResp[24];
81 u_char Flags; /* Should be zero (Win98 sends 04) */
82 } MS_Chapv2Response;
83 /* We use MS_CHAPV2_RESPONSE_LEN, rather than sizeof(MS_Chapv2Response),
84 in case this struct gets padded. */
85
86 static void ChallengeResponse __P((u_char *, u_char *, u_char *));
87 static void DesEncrypt __P((u_char *, u_char *, u_char *));
88 static void MakeKey __P((u_char *, u_char *));
89 static u_char Get7Bits __P((u_char *, int));
90 #ifdef CHAPMS
91 static void ChapMS_NT __P((u_char *, char *, int, MS_ChapResponse *));
92 #ifdef MSLANMAN
93 static void ChapMS_LANMan __P((u_char *, char *, int, MS_ChapResponse *));
94 #endif
95 #endif
96 #ifdef CHAPMSV2
97 static void ChapMSv2_NT __P((char *, u_char *, char *, int,
98 MS_Chapv2Response *));
99 #endif
100
101 #ifdef USE_CRYPT
102 static void Expand __P((u_char *, char *));
103 static void Collapse __P((char *, u_char *));
104 #endif
105
106 #if defined(MSLANMAN) && defined(CHAPMS)
107 bool ms_lanman = 0; /* Use LanMan password instead of NT */
108 /* Has meaning only with MS-CHAP challenges */
109 #endif
110
111 #ifdef CHAPMSV2
112 /* Specially-formatted Microsoft CHAP response message. */
113 static char status_message[256];
114 #endif
115
116 static void
ChallengeResponse(challenge,pwHash,response)117 ChallengeResponse(challenge, pwHash, response)
118 u_char *challenge; /* IN 8 octets */
119 u_char *pwHash; /* IN 16 octets */
120 u_char *response; /* OUT 24 octets */
121 {
122 u_char ZPasswordHash[21];
123
124 BZERO(ZPasswordHash, sizeof(ZPasswordHash));
125 BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
126
127 #if 0
128 dbglog("ChallengeResponse - ZPasswordHash %.*B",
129 sizeof(ZPasswordHash), ZPasswordHash);
130 #endif
131
132 DesEncrypt(challenge, ZPasswordHash + 0, response + 0);
133 DesEncrypt(challenge, ZPasswordHash + 7, response + 8);
134 DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
135
136 #if 0
137 dbglog("ChallengeResponse - response %.24B", response);
138 #endif
139 }
140
141
142 #ifdef USE_CRYPT
143 static void
DesEncrypt(clear,key,cipher)144 DesEncrypt(clear, key, cipher)
145 u_char *clear; /* IN 8 octets */
146 u_char *key; /* IN 7 octets */
147 u_char *cipher; /* OUT 8 octets */
148 {
149 u_char des_key[8];
150 char crypt_key[66];
151 char des_input[66];
152
153 MakeKey(key, des_key);
154
155 Expand(des_key, crypt_key);
156 setkey(crypt_key);
157
158 #if 0
159 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
160 #endif
161
162 Expand(clear, des_input);
163 encrypt(des_input, 0);
164 Collapse(des_input, cipher);
165
166 #if 0
167 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
168 #endif
169 }
170
171 #else /* USE_CRYPT */
172
173 static void
DesEncrypt(clear,key,cipher)174 DesEncrypt(clear, key, cipher)
175 u_char *clear; /* IN 8 octets */
176 u_char *key; /* IN 7 octets */
177 u_char *cipher; /* OUT 8 octets */
178 {
179 des_cblock des_key;
180 des_key_schedule key_schedule;
181
182 MakeKey(key, des_key);
183
184 des_set_key(&des_key, key_schedule);
185
186 #if 0
187 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
188 #endif
189
190 des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
191
192 #if 0
193 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
194 #endif
195 }
196
197 #endif /* USE_CRYPT */
198
199
Get7Bits(input,startBit)200 static u_char Get7Bits(input, startBit)
201 u_char *input;
202 int startBit;
203 {
204 register unsigned int word;
205
206 word = (unsigned)input[startBit / 8] << 8;
207 word |= (unsigned)input[startBit / 8 + 1];
208
209 word >>= 15 - (startBit % 8 + 7);
210
211 return word & 0xFE;
212 }
213
214 #ifdef USE_CRYPT
215
216 /* in == 8-byte string (expanded version of the 56-bit key)
217 * out == 64-byte string where each byte is either 1 or 0
218 * Note that the low-order "bit" is always ignored by by setkey()
219 */
Expand(in,out)220 static void Expand(in, out)
221 u_char *in;
222 char *out;
223 {
224 int j, c;
225 int i;
226
227 for(i = 0; i < 64; in++){
228 c = *in;
229 for(j = 7; j >= 0; j--)
230 *out++ = (c >> j) & 01;
231 i += 8;
232 }
233 }
234
235 /* The inverse of Expand
236 */
Collapse(in,out)237 static void Collapse(in, out)
238 char *in;
239 u_char *out;
240 {
241 int j;
242 int i;
243 unsigned int c;
244
245 for (i = 0; i < 64; i += 8, out++) {
246 c = 0;
247 for (j = 7; j >= 0; j--, in++)
248 c |= *(u_char *)in << j;
249 *out = c & 0xff;
250 }
251 }
252 #endif
253
MakeKey(key,des_key)254 static void MakeKey(key, des_key)
255 u_char *key; /* IN 56 bit DES key missing parity bits */
256 u_char *des_key; /* OUT 64 bit DES key with parity bits added */
257 {
258 des_key[0] = Get7Bits(key, 0);
259 des_key[1] = Get7Bits(key, 7);
260 des_key[2] = Get7Bits(key, 14);
261 des_key[3] = Get7Bits(key, 21);
262 des_key[4] = Get7Bits(key, 28);
263 des_key[5] = Get7Bits(key, 35);
264 des_key[6] = Get7Bits(key, 42);
265 des_key[7] = Get7Bits(key, 49);
266
267 #ifndef USE_CRYPT
268 des_set_odd_parity((des_cblock *)des_key);
269 #endif
270
271 #if 0
272 CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %.7B", key));
273 CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %.8B", des_key));
274 #endif
275 }
276
277 #ifdef CHAPMS
278 static void
ChapMS_NT(rchallenge,secret,secret_len,response)279 ChapMS_NT(rchallenge, secret, secret_len, response)
280 u_char *rchallenge;
281 char *secret;
282 int secret_len;
283 MS_ChapResponse *response;
284 {
285 int i;
286 #if defined(__NetBSD__) || defined(HAVE_LIBMD)
287 /* NetBSD uses the libc md4 routines which take bytes instead of bits */
288 int mdlen = secret_len * 2;
289 #else
290 int mdlen = secret_len * 2 * 8;
291 #endif
292 MD4_CTX md4Context;
293 u_char hash[MD4_SIGNATURE_SIZE];
294 u_char unicodePassword[MAX_NT_PASSWORD * 2];
295
296 /* Initialize the Unicode version of the secret (== password). */
297 /* This implicitly supports 8-bit ISO8859/1 characters. */
298 BZERO(unicodePassword, sizeof(unicodePassword));
299 for (i = 0; i < secret_len; i++)
300 unicodePassword[i * 2] = (u_char)secret[i];
301
302 MD4Init(&md4Context);
303 MD4Update(&md4Context, unicodePassword, mdlen);
304
305 MD4Final(hash, &md4Context); /* Tell MD4 we're done */
306
307 ChallengeResponse(rchallenge, hash, response->NTResp);
308 }
309
310 #ifdef MSLANMAN
311 static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
312
313 static void
ChapMS_LANMan(rchallenge,secret,secret_len,response)314 ChapMS_LANMan(rchallenge, secret, secret_len, response)
315 u_char *rchallenge;
316 char *secret;
317 int secret_len;
318 MS_ChapResponse *response;
319 {
320 int i;
321 u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
322 u_char PasswordHash[MD4_SIGNATURE_SIZE];
323
324 /* LANMan password is case insensitive */
325 BZERO(UcasePassword, sizeof(UcasePassword));
326 for (i = 0; i < secret_len; i++)
327 UcasePassword[i] = (u_char)(
328 islower(secret[i]) ? toupper(secret[i]) : secret[i]);
329 DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
330 DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
331 ChallengeResponse(rchallenge, PasswordHash, response->LANManResp);
332 }
333 #endif
334
335 void
ChapMS(cstate,rchallenge,rchallenge_len,secret,secret_len)336 ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len)
337 chap_state *cstate;
338 u_char *rchallenge;
339 int rchallenge_len;
340 char *secret;
341 int secret_len;
342 {
343 MS_ChapResponse response;
344
345 if (rchallenge_len < 8) {
346 cstate->resp_length = 0;
347 return;
348 }
349
350 #if 0
351 CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret));
352 #endif
353 BZERO(&response, sizeof(response));
354
355 /* Calculate both always */
356 ChapMS_NT(rchallenge, secret, secret_len, &response);
357
358 #ifdef MSLANMAN
359 ChapMS_LANMan(rchallenge, secret, secret_len, &response);
360
361 /* prefered method is set by option */
362 response.UseNT = !ms_lanman;
363 #else
364 response.UseNT = 1;
365 #endif
366
367 BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN);
368 cstate->resp_length = MS_CHAP_RESPONSE_LEN;
369 }
370
371 static int
ChapMSStatus(cstate,flag)372 ChapMSStatus(cstate, flag)
373 chap_state *cstate;
374 int flag;
375 {
376 if (flag != 0) {
377 cstate->stat_message = NULL;
378 cstate->stat_length = 0;
379 } else {
380 cstate->stat_message = "E=691 R=0 M=\"Authentication failed\"";
381 cstate->stat_length = strlen(cstate->stat_message);
382 }
383 return (flag);
384 }
385
386 int
ChapMSValidate(cstate,response,response_len,secret,secret_len)387 ChapMSValidate(cstate, response, response_len, secret, secret_len)
388 chap_state *cstate;
389 u_char *response;
390 int response_len;
391 char *secret;
392 int secret_len;
393 {
394 MS_ChapResponse ckresp;
395
396 if (response_len < MS_CHAP_RESPONSE_LEN || cstate->chal_len < 8)
397 return (0);
398
399 BZERO(&ckresp, sizeof(ckresp));
400
401 if (response[MS_CHAP_RESPONSE_LEN-1]) {
402 ChapMS_NT(cstate->challenge, secret, secret_len, &ckresp);
403 return (ChapMSStatus(cstate, memcmp(ckresp.NTResp, response+24,
404 24) == 0));
405 }
406
407 #ifdef MSLANMAN
408 ChapMS_LANMan(cstate->challenge, secret, secret_len, &ckresp);
409 return (ChapMSStatus(cstate,
410 memcmp(ckresp.LANManResp, response, 24) == 0));
411 #else
412 return (ChapMSStatus(cstate, 0));
413 #endif
414 }
415 #endif /* CHAPMS */
416
417 #ifdef CHAPMSV2
418 static void
ChallengeHash(peerchallenge,authenticatorchallenge,username,challenge)419 ChallengeHash(peerchallenge, authenticatorchallenge, username, challenge)
420 u_char *peerchallenge, *authenticatorchallenge, *challenge;
421 char *username;
422 {
423 uint8_t digest[20];
424 SHA1_CTX sha1Context;
425 char *cp;
426
427 SHA1Init(&sha1Context);
428 SHA1Update(&sha1Context, peerchallenge, 16);
429 SHA1Update(&sha1Context, authenticatorchallenge, 16);
430
431 /*
432 * Only the user name (as presented by the peer and
433 * excluding any prepended domain name)
434 * is used as input to SHAUpdate().
435 */
436 if ((cp = strchr(username,'\\')) != NULL)
437 username = cp;
438
439 SHA1Update(&sha1Context, (uint8_t *)username, strlen(username));
440 SHA1Final(digest, &sha1Context);
441 BCOPY(digest, challenge, 8);
442 }
443
444 static void
ChapMSv2_NT(username,rchallenge,secret,secret_len,response)445 ChapMSv2_NT(username, rchallenge, secret, secret_len, response)
446 char *username;
447 u_char *rchallenge;
448 char *secret;
449 int secret_len;
450 MS_Chapv2Response *response;
451 {
452 int i;
453 #if defined(__NetBSD__) || defined(HAVE_LIBMD)
454 /* NetBSD uses the libc md4 routines that take bytes instead of bits */
455 int mdlen = secret_len * 2;
456 #else
457 int mdlen = secret_len * 2 * 8;
458 #endif
459 MD4_CTX md4Context;
460 u_char hash[MD4_SIGNATURE_SIZE];
461 u_char challenge[8];
462 u_char unicodePassword[MAX_NT_PASSWORD * 2];
463
464 /* Initialize the Unicode version of the secret (== password). */
465 /* This implicitly supports 8-bit ISO8859/1 characters. */
466 BZERO(unicodePassword, sizeof(unicodePassword));
467 for (i = 0; i < secret_len && i < MAX_NT_PASSWORD; i++)
468 if ((unicodePassword[i * 2] = (u_char)secret[i]) == '\0')
469 break;
470
471 ChallengeHash(response->PeerChallenge, rchallenge, username, challenge);
472
473 MD4Init(&md4Context);
474 MD4Update(&md4Context, unicodePassword, mdlen);
475
476 MD4Final(hash, &md4Context); /* Tell MD4 we're done */
477
478 ChallengeResponse(challenge, hash, response->NTResp);
479 }
480
481 void
ChapMSv2(cstate,rchallenge,rchallenge_len,secret,secret_len)482 ChapMSv2(cstate, rchallenge, rchallenge_len, secret, secret_len)
483 chap_state *cstate;
484 u_char *rchallenge;
485 int rchallenge_len;
486 char *secret;
487 int secret_len;
488 {
489 MS_Chapv2Response response;
490 u_char *ptr;
491 int i;
492
493 if (rchallenge_len < 8) {
494 cstate->resp_length = 0;
495 return;
496 }
497
498 BZERO(&response, sizeof(response));
499
500 ptr = response.PeerChallenge;
501 for (i = 0; i < 16; i++)
502 *ptr++ = (u_char) (drand48() * 0xff);
503
504 ChapMSv2_NT(cstate->resp_name, rchallenge, secret, secret_len, &response);
505
506 BCOPY(&response, cstate->response, MS_CHAPV2_RESPONSE_LEN);
507 cstate->resp_length = MS_CHAPV2_RESPONSE_LEN;
508 }
509
510 static void
ChapMSv2Success(cstate,msresp,authchall,rhostname,secret,secret_len)511 ChapMSv2Success(cstate, msresp, authchall, rhostname, secret, secret_len)
512 chap_state *cstate;
513 MS_Chapv2Response *msresp;
514 u_char *authchall;
515 char *rhostname, *secret;
516 int secret_len;
517 {
518 static const u_char Magic1[39] = "Magic server to client signing constant";
519 static const u_char Magic2[41] =
520 "Pad to make it do more than one iteration";
521 #if defined(__NetBSD__) || defined(HAVE_LIBMD)
522 /* NetBSD uses the libc md4 routines that take bytes instead of bits */
523 int mdlen = 1;
524 #else
525 int mdlen = 8;
526 #endif
527 u_char unicodePassword[MAX_NT_PASSWORD * 2];
528 MD4_CTX md4Context;
529 u_char hash[MD4_SIGNATURE_SIZE];
530 u_char hashhash[MD4_SIGNATURE_SIZE];
531 SHA1_CTX sha1Context;
532 uint8_t digest[20];
533 u_char challenge[8];
534 char *cp;
535 static const char hexdig[] = "0123456789ABCDEF";
536 int i;
537
538 /* Initialize the Unicode version of the secret (== password). */
539 /* This implicitly supports 8-bit ISO8859/1 characters. */
540 BZERO(unicodePassword, sizeof(unicodePassword));
541 for (i = 0; i < secret_len && i < MAX_NT_PASSWORD; i++)
542 if ((unicodePassword[i * 2] = (u_char)secret[i]) == '\0')
543 break;
544
545 /* Hash the password with MD4 */
546 MD4Init(&md4Context);
547 MD4Update(&md4Context, unicodePassword, secret_len * 2 * mdlen);
548 MD4Final(hash, &md4Context);
549
550 /* Now hash the hash */
551 MD4Init(&md4Context);
552 MD4Update(&md4Context, hash, MD4_SIGNATURE_SIZE * mdlen);
553 MD4Final(hashhash, &md4Context);
554
555 SHA1Init(&sha1Context);
556 SHA1Update(&sha1Context, hashhash, MD4_SIGNATURE_SIZE);
557 SHA1Update(&sha1Context, msresp->NTResp, sizeof (msresp->NTResp));
558 SHA1Update(&sha1Context, Magic1, 39);
559 SHA1Final(digest, &sha1Context);
560
561 ChallengeHash(msresp->PeerChallenge, authchall, rhostname, challenge);
562
563 SHA1Init(&sha1Context);
564 SHA1Update(&sha1Context, digest, 20);
565 SHA1Update(&sha1Context, challenge, 8);
566 SHA1Update(&sha1Context, Magic2, 41);
567 SHA1Final(digest, &sha1Context);
568
569 cp = status_message;
570 *cp++ = 'S';
571 *cp++ = '=';
572 for (i = 0; i < 20; i++) {
573 *cp++ = hexdig[digest[i]>>4];
574 *cp++ = hexdig[digest[i]&15];
575 }
576 /*
577 * RFC 2759 says that a M=<string> greeting message is possible
578 * here. It lies. Any such greeting causes Windoze-98 to give
579 * error number 742, "Dial-Up Networking was unable to complete
580 * the connection. The computer you're dialing in to does not
581 * support the data encryption requirements specified. Please
582 * check your encryption settings in the properties of the
583 * connection. If this problem persists, contact your network
584 * administrator."
585 */
586 *cp = '\0';
587 #if 0
588 slprintf(cp, sizeof (status_message) - (cp-status_message),
589 "M=\"Welcome to %s.\"", hostname);
590 #endif
591 cstate->stat_message = status_message;
592 cstate->stat_length = strlen(status_message);
593 }
594
595 int
ChapMSv2Validate(cstate,rhostname,response,response_len,secret,secret_len)596 ChapMSv2Validate(cstate, rhostname, response, response_len, secret, secret_len)
597 chap_state *cstate;
598 char *rhostname;
599 u_char *response;
600 int response_len;
601 char *secret;
602 int secret_len;
603 {
604 MS_Chapv2Response ckresp;
605
606 if (response_len < MS_CHAPV2_RESPONSE_LEN ||
607 /* response[MS_CHAPV2_RESPONSE_LEN-1] != 0 || */cstate->chal_len < 8) {
608 cstate->stat_message = NULL;
609 cstate->stat_length = 0;
610 return 0;
611 }
612
613 BZERO(&ckresp, sizeof(ckresp));
614
615 BCOPY(response, ckresp.PeerChallenge, 16);
616
617 ChapMSv2_NT(rhostname, cstate->challenge, secret, secret_len, &ckresp);
618 if (memcmp(ckresp.NTResp, response+24, 24) != 0) {
619 cstate->stat_message = "E=691 R=0 C=11111111111111111111111111111111 V=3 M=\"Authentication failed\"";
620 cstate->stat_length = strlen(cstate->stat_message);
621 return (0);
622 }
623 ChapMSv2Success(cstate, (MS_Chapv2Response *)response, cstate->challenge,
624 rhostname, secret, secret_len);
625 return (1);
626 }
627 #endif /* CHAPMSV2 */
628
629 #endif /* CHAPMS or CHAPMSV2 */
630