8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.bin / pppd / chap_ms.c
blob21cf5ee114999b7109c1af636c8b440359e08695
1 /*
2 * chap_ms.c - Microsoft MS-CHAP compatible implementation.
4 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
5 * Use is subject to license terms.
7 * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
8 * http://www.strataware.com/
10 * All rights reserved.
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.
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.
27 * This module implements MS-CHAPv1 (RFC 2433) and MS-CHAPv2 (RFC 2759).
29 * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
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.
36 * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
38 * Modifications by James Carlson / james.d.carlson@sun.com, June 1st, 2000.
40 * Added MS-CHAPv2 support.
43 #pragma ident "%Z%%M% %I% %E% SMI"
44 #define RCSID "$Id: chap_ms.c,v 1.15 1999/08/13 06:46:12 paulus Exp $"
46 #if defined(CHAPMS) || defined(CHAPMSV2)
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <ctype.h>
52 #include <sys/types.h>
53 #include <sys/time.h>
54 #include <unistd.h>
55 #ifdef HAVE_CRYPT_H
56 #include <crypt.h>
57 #endif
59 #ifdef CHAPMSV2
60 #include "sha1.h"
61 #endif
63 #ifndef USE_CRYPT
64 #include <des.h>
65 #endif
67 #include "pppd.h"
68 #include "chap.h"
69 #include "chap_ms.h"
70 #include "md4.h"
72 #if !defined(lint) && !defined(_lint)
73 static const char rcsid[] = RCSID;
74 #endif
76 typedef struct {
77 u_char LANManResp[24];
78 u_char NTResp[24];
79 u_char UseNT; /* If 1, ignore the LANMan response field */
80 } MS_ChapResponse;
81 /* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
82 in case this struct gets padded. */
84 typedef struct {
85 u_char PeerChallenge[16];
86 u_char MustBeZero[8];
87 u_char NTResp[24];
88 u_char Flags; /* Should be zero (Win98 sends 04) */
89 } MS_Chapv2Response;
90 /* We use MS_CHAPV2_RESPONSE_LEN, rather than sizeof(MS_Chapv2Response),
91 in case this struct gets padded. */
93 static void ChallengeResponse __P((u_char *, u_char *, u_char *));
94 static void DesEncrypt __P((u_char *, u_char *, u_char *));
95 static void MakeKey __P((u_char *, u_char *));
96 static u_char Get7Bits __P((u_char *, int));
97 #ifdef CHAPMS
98 static void ChapMS_NT __P((u_char *, char *, int, MS_ChapResponse *));
99 #ifdef MSLANMAN
100 static void ChapMS_LANMan __P((u_char *, char *, int, MS_ChapResponse *));
101 #endif
102 #endif
103 #ifdef CHAPMSV2
104 static void ChapMSv2_NT __P((char *, u_char *, char *, int,
105 MS_Chapv2Response *));
106 #endif
108 #ifdef USE_CRYPT
109 static void Expand __P((u_char *, char *));
110 static void Collapse __P((char *, u_char *));
111 #endif
113 #if defined(MSLANMAN) && defined(CHAPMS)
114 bool ms_lanman = 0; /* Use LanMan password instead of NT */
115 /* Has meaning only with MS-CHAP challenges */
116 #endif
118 #ifdef CHAPMSV2
119 /* Specially-formatted Microsoft CHAP response message. */
120 static char status_message[256];
121 #endif
123 static void
124 ChallengeResponse(challenge, pwHash, response)
125 u_char *challenge; /* IN 8 octets */
126 u_char *pwHash; /* IN 16 octets */
127 u_char *response; /* OUT 24 octets */
129 u_char ZPasswordHash[21];
131 BZERO(ZPasswordHash, sizeof(ZPasswordHash));
132 BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
134 #if 0
135 dbglog("ChallengeResponse - ZPasswordHash %.*B",
136 sizeof(ZPasswordHash), ZPasswordHash);
137 #endif
139 DesEncrypt(challenge, ZPasswordHash + 0, response + 0);
140 DesEncrypt(challenge, ZPasswordHash + 7, response + 8);
141 DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
143 #if 0
144 dbglog("ChallengeResponse - response %.24B", response);
145 #endif
149 #ifdef USE_CRYPT
150 static void
151 DesEncrypt(clear, key, cipher)
152 u_char *clear; /* IN 8 octets */
153 u_char *key; /* IN 7 octets */
154 u_char *cipher; /* OUT 8 octets */
156 u_char des_key[8];
157 char crypt_key[66];
158 char des_input[66];
160 MakeKey(key, des_key);
162 Expand(des_key, crypt_key);
163 setkey(crypt_key);
165 #if 0
166 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
167 #endif
169 Expand(clear, des_input);
170 encrypt(des_input, 0);
171 Collapse(des_input, cipher);
173 #if 0
174 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
175 #endif
178 #else /* USE_CRYPT */
180 static void
181 DesEncrypt(clear, key, cipher)
182 u_char *clear; /* IN 8 octets */
183 u_char *key; /* IN 7 octets */
184 u_char *cipher; /* OUT 8 octets */
186 des_cblock des_key;
187 des_key_schedule key_schedule;
189 MakeKey(key, des_key);
191 des_set_key(&des_key, key_schedule);
193 #if 0
194 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
195 #endif
197 des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
199 #if 0
200 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
201 #endif
204 #endif /* USE_CRYPT */
207 static u_char Get7Bits(input, startBit)
208 u_char *input;
209 int startBit;
211 register unsigned int word;
213 word = (unsigned)input[startBit / 8] << 8;
214 word |= (unsigned)input[startBit / 8 + 1];
216 word >>= 15 - (startBit % 8 + 7);
218 return word & 0xFE;
221 #ifdef USE_CRYPT
223 /* in == 8-byte string (expanded version of the 56-bit key)
224 * out == 64-byte string where each byte is either 1 or 0
225 * Note that the low-order "bit" is always ignored by by setkey()
227 static void Expand(in, out)
228 u_char *in;
229 char *out;
231 int j, c;
232 int i;
234 for(i = 0; i < 64; in++){
235 c = *in;
236 for(j = 7; j >= 0; j--)
237 *out++ = (c >> j) & 01;
238 i += 8;
242 /* The inverse of Expand
244 static void Collapse(in, out)
245 char *in;
246 u_char *out;
248 int j;
249 int i;
250 unsigned int c;
252 for (i = 0; i < 64; i += 8, out++) {
253 c = 0;
254 for (j = 7; j >= 0; j--, in++)
255 c |= *(u_char *)in << j;
256 *out = c & 0xff;
259 #endif
261 static void MakeKey(key, des_key)
262 u_char *key; /* IN 56 bit DES key missing parity bits */
263 u_char *des_key; /* OUT 64 bit DES key with parity bits added */
265 des_key[0] = Get7Bits(key, 0);
266 des_key[1] = Get7Bits(key, 7);
267 des_key[2] = Get7Bits(key, 14);
268 des_key[3] = Get7Bits(key, 21);
269 des_key[4] = Get7Bits(key, 28);
270 des_key[5] = Get7Bits(key, 35);
271 des_key[6] = Get7Bits(key, 42);
272 des_key[7] = Get7Bits(key, 49);
274 #ifndef USE_CRYPT
275 des_set_odd_parity((des_cblock *)des_key);
276 #endif
278 #if 0
279 CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %.7B", key));
280 CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %.8B", des_key));
281 #endif
284 #ifdef CHAPMS
285 static void
286 ChapMS_NT(rchallenge, secret, secret_len, response)
287 u_char *rchallenge;
288 char *secret;
289 int secret_len;
290 MS_ChapResponse *response;
292 int i;
293 #if defined(__NetBSD__) || defined(HAVE_LIBMD)
294 /* NetBSD uses the libc md4 routines which take bytes instead of bits */
295 int mdlen = secret_len * 2;
296 #else
297 int mdlen = secret_len * 2 * 8;
298 #endif
299 MD4_CTX md4Context;
300 u_char hash[MD4_SIGNATURE_SIZE];
301 u_char unicodePassword[MAX_NT_PASSWORD * 2];
303 /* Initialize the Unicode version of the secret (== password). */
304 /* This implicitly supports 8-bit ISO8859/1 characters. */
305 BZERO(unicodePassword, sizeof(unicodePassword));
306 for (i = 0; i < secret_len; i++)
307 unicodePassword[i * 2] = (u_char)secret[i];
309 MD4Init(&md4Context);
310 MD4Update(&md4Context, unicodePassword, mdlen);
312 MD4Final(hash, &md4Context); /* Tell MD4 we're done */
314 ChallengeResponse(rchallenge, hash, response->NTResp);
317 #ifdef MSLANMAN
318 static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
320 static void
321 ChapMS_LANMan(rchallenge, secret, secret_len, response)
322 u_char *rchallenge;
323 char *secret;
324 int secret_len;
325 MS_ChapResponse *response;
327 int i;
328 u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
329 u_char PasswordHash[MD4_SIGNATURE_SIZE];
331 /* LANMan password is case insensitive */
332 BZERO(UcasePassword, sizeof(UcasePassword));
333 for (i = 0; i < secret_len; i++)
334 UcasePassword[i] = (u_char)(
335 islower(secret[i]) ? toupper(secret[i]) : secret[i]);
336 DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
337 DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
338 ChallengeResponse(rchallenge, PasswordHash, response->LANManResp);
340 #endif
342 void
343 ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len)
344 chap_state *cstate;
345 u_char *rchallenge;
346 int rchallenge_len;
347 char *secret;
348 int secret_len;
350 MS_ChapResponse response;
352 if (rchallenge_len < 8) {
353 cstate->resp_length = 0;
354 return;
357 #if 0
358 CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret));
359 #endif
360 BZERO(&response, sizeof(response));
362 /* Calculate both always */
363 ChapMS_NT(rchallenge, secret, secret_len, &response);
365 #ifdef MSLANMAN
366 ChapMS_LANMan(rchallenge, secret, secret_len, &response);
368 /* prefered method is set by option */
369 response.UseNT = !ms_lanman;
370 #else
371 response.UseNT = 1;
372 #endif
374 BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN);
375 cstate->resp_length = MS_CHAP_RESPONSE_LEN;
378 static int
379 ChapMSStatus(cstate, flag)
380 chap_state *cstate;
381 int flag;
383 if (flag != 0) {
384 cstate->stat_message = NULL;
385 cstate->stat_length = 0;
386 } else {
387 cstate->stat_message = "E=691 R=0 M=\"Authentication failed\"";
388 cstate->stat_length = strlen(cstate->stat_message);
390 return (flag);
394 ChapMSValidate(cstate, response, response_len, secret, secret_len)
395 chap_state *cstate;
396 u_char *response;
397 int response_len;
398 char *secret;
399 int secret_len;
401 MS_ChapResponse ckresp;
403 if (response_len < MS_CHAP_RESPONSE_LEN || cstate->chal_len < 8)
404 return (0);
406 BZERO(&ckresp, sizeof(ckresp));
408 if (response[MS_CHAP_RESPONSE_LEN-1]) {
409 ChapMS_NT(cstate->challenge, secret, secret_len, &ckresp);
410 return (ChapMSStatus(cstate, memcmp(ckresp.NTResp, response+24,
411 24) == 0));
414 #ifdef MSLANMAN
415 ChapMS_LANMan(cstate->challenge, secret, secret_len, &ckresp);
416 return (ChapMSStatus(cstate,
417 memcmp(ckresp.LANManResp, response, 24) == 0));
418 #else
419 return (ChapMSStatus(cstate, 0));
420 #endif
422 #endif /* CHAPMS */
424 #ifdef CHAPMSV2
425 static void
426 ChallengeHash(peerchallenge, authenticatorchallenge, username, challenge)
427 u_char *peerchallenge, *authenticatorchallenge, *challenge;
428 char *username;
430 uint8_t digest[20];
431 SHA1_CTX sha1Context;
432 char *cp;
434 SHA1Init(&sha1Context);
435 SHA1Update(&sha1Context, peerchallenge, 16);
436 SHA1Update(&sha1Context, authenticatorchallenge, 16);
439 * Only the user name (as presented by the peer and
440 * excluding any prepended domain name)
441 * is used as input to SHAUpdate().
443 if ((cp = strchr(username,'\\')) != NULL)
444 username = cp;
446 SHA1Update(&sha1Context, (uint8_t *)username, strlen(username));
447 SHA1Final(digest, &sha1Context);
448 BCOPY(digest, challenge, 8);
451 static void
452 ChapMSv2_NT(username, rchallenge, secret, secret_len, response)
453 char *username;
454 u_char *rchallenge;
455 char *secret;
456 int secret_len;
457 MS_Chapv2Response *response;
459 int i;
460 #if defined(__NetBSD__) || defined(HAVE_LIBMD)
461 /* NetBSD uses the libc md4 routines that take bytes instead of bits */
462 int mdlen = secret_len * 2;
463 #else
464 int mdlen = secret_len * 2 * 8;
465 #endif
466 MD4_CTX md4Context;
467 u_char hash[MD4_SIGNATURE_SIZE];
468 u_char challenge[8];
469 u_char unicodePassword[MAX_NT_PASSWORD * 2];
471 /* Initialize the Unicode version of the secret (== password). */
472 /* This implicitly supports 8-bit ISO8859/1 characters. */
473 BZERO(unicodePassword, sizeof(unicodePassword));
474 for (i = 0; i < secret_len && i < MAX_NT_PASSWORD; i++)
475 if ((unicodePassword[i * 2] = (u_char)secret[i]) == '\0')
476 break;
478 ChallengeHash(response->PeerChallenge, rchallenge, username, challenge);
480 MD4Init(&md4Context);
481 MD4Update(&md4Context, unicodePassword, mdlen);
483 MD4Final(hash, &md4Context); /* Tell MD4 we're done */
485 ChallengeResponse(challenge, hash, response->NTResp);
488 void
489 ChapMSv2(cstate, rchallenge, rchallenge_len, secret, secret_len)
490 chap_state *cstate;
491 u_char *rchallenge;
492 int rchallenge_len;
493 char *secret;
494 int secret_len;
496 MS_Chapv2Response response;
497 u_char *ptr;
498 int i;
500 if (rchallenge_len < 8) {
501 cstate->resp_length = 0;
502 return;
505 BZERO(&response, sizeof(response));
507 ptr = response.PeerChallenge;
508 for (i = 0; i < 16; i++)
509 *ptr++ = (u_char) (drand48() * 0xff);
511 ChapMSv2_NT(cstate->resp_name, rchallenge, secret, secret_len, &response);
513 BCOPY(&response, cstate->response, MS_CHAPV2_RESPONSE_LEN);
514 cstate->resp_length = MS_CHAPV2_RESPONSE_LEN;
517 static void
518 ChapMSv2Success(cstate, msresp, authchall, rhostname, secret, secret_len)
519 chap_state *cstate;
520 MS_Chapv2Response *msresp;
521 u_char *authchall;
522 char *rhostname, *secret;
523 int secret_len;
525 static const u_char Magic1[39] = "Magic server to client signing constant";
526 static const u_char Magic2[41] =
527 "Pad to make it do more than one iteration";
528 #if defined(__NetBSD__) || defined(HAVE_LIBMD)
529 /* NetBSD uses the libc md4 routines that take bytes instead of bits */
530 int mdlen = 1;
531 #else
532 int mdlen = 8;
533 #endif
534 u_char unicodePassword[MAX_NT_PASSWORD * 2];
535 MD4_CTX md4Context;
536 u_char hash[MD4_SIGNATURE_SIZE];
537 u_char hashhash[MD4_SIGNATURE_SIZE];
538 SHA1_CTX sha1Context;
539 uint8_t digest[20];
540 u_char challenge[8];
541 char *cp;
542 static const char hexdig[] = "0123456789ABCDEF";
543 int i;
545 /* Initialize the Unicode version of the secret (== password). */
546 /* This implicitly supports 8-bit ISO8859/1 characters. */
547 BZERO(unicodePassword, sizeof(unicodePassword));
548 for (i = 0; i < secret_len && i < MAX_NT_PASSWORD; i++)
549 if ((unicodePassword[i * 2] = (u_char)secret[i]) == '\0')
550 break;
552 /* Hash the password with MD4 */
553 MD4Init(&md4Context);
554 MD4Update(&md4Context, unicodePassword, secret_len * 2 * mdlen);
555 MD4Final(hash, &md4Context);
557 /* Now hash the hash */
558 MD4Init(&md4Context);
559 MD4Update(&md4Context, hash, MD4_SIGNATURE_SIZE * mdlen);
560 MD4Final(hashhash, &md4Context);
562 SHA1Init(&sha1Context);
563 SHA1Update(&sha1Context, hashhash, MD4_SIGNATURE_SIZE);
564 SHA1Update(&sha1Context, msresp->NTResp, sizeof (msresp->NTResp));
565 SHA1Update(&sha1Context, Magic1, 39);
566 SHA1Final(digest, &sha1Context);
568 ChallengeHash(msresp->PeerChallenge, authchall, rhostname, challenge);
570 SHA1Init(&sha1Context);
571 SHA1Update(&sha1Context, digest, 20);
572 SHA1Update(&sha1Context, challenge, 8);
573 SHA1Update(&sha1Context, Magic2, 41);
574 SHA1Final(digest, &sha1Context);
576 cp = status_message;
577 *cp++ = 'S';
578 *cp++ = '=';
579 for (i = 0; i < 20; i++) {
580 *cp++ = hexdig[digest[i]>>4];
581 *cp++ = hexdig[digest[i]&15];
584 * RFC 2759 says that a M=<string> greeting message is possible
585 * here. It lies. Any such greeting causes Windoze-98 to give
586 * error number 742, "Dial-Up Networking was unable to complete
587 * the connection. The computer you're dialing in to does not
588 * support the data encryption requirements specified. Please
589 * check your encryption settings in the properties of the
590 * connection. If this problem persists, contact your network
591 * administrator."
593 *cp = '\0';
594 #if 0
595 slprintf(cp, sizeof (status_message) - (cp-status_message),
596 "M=\"Welcome to %s.\"", hostname);
597 #endif
598 cstate->stat_message = status_message;
599 cstate->stat_length = strlen(status_message);
603 ChapMSv2Validate(cstate, rhostname, response, response_len, secret, secret_len)
604 chap_state *cstate;
605 char *rhostname;
606 u_char *response;
607 int response_len;
608 char *secret;
609 int secret_len;
611 MS_Chapv2Response ckresp;
613 if (response_len < MS_CHAPV2_RESPONSE_LEN ||
614 /* response[MS_CHAPV2_RESPONSE_LEN-1] != 0 || */cstate->chal_len < 8) {
615 cstate->stat_message = NULL;
616 cstate->stat_length = 0;
617 return 0;
620 BZERO(&ckresp, sizeof(ckresp));
622 BCOPY(response, ckresp.PeerChallenge, 16);
624 ChapMSv2_NT(rhostname, cstate->challenge, secret, secret_len, &ckresp);
625 if (memcmp(ckresp.NTResp, response+24, 24) != 0) {
626 cstate->stat_message = "E=691 R=0 C=11111111111111111111111111111111 V=3 M=\"Authentication failed\"";
627 cstate->stat_length = strlen(cstate->stat_message);
628 return (0);
630 ChapMSv2Success(cstate, (MS_Chapv2Response *)response, cstate->challenge,
631 rhostname, secret, secret_len);
632 return (1);
634 #endif /* CHAPMSV2 */
636 #endif /* CHAPMS or CHAPMSV2 */