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#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 $"
45
46#if defined(CHAPMS) || defined(CHAPMSV2)
47
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
58
59#ifdef CHAPMSV2
60#include "sha1.h"
61#endif
62
63#ifndef USE_CRYPT
64#include <des.h>
65#endif
66
67#include "pppd.h"
68#include "chap.h"
69#include "chap_ms.h"
70#include "md4.h"
71
72#if !defined(lint) && !defined(_lint)
73static const char rcsid[] = RCSID;
74#endif
75
76typedef 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. */
83
84typedef 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. */
92
93static void	ChallengeResponse __P((u_char *, u_char *, u_char *));
94static void	DesEncrypt __P((u_char *, u_char *, u_char *));
95static void	MakeKey __P((u_char *, u_char *));
96static u_char	Get7Bits __P((u_char *, int));
97#ifdef CHAPMS
98static void	ChapMS_NT __P((u_char *, char *, int, MS_ChapResponse *));
99#ifdef MSLANMAN
100static void	ChapMS_LANMan __P((u_char *, char *, int, MS_ChapResponse *));
101#endif
102#endif
103#ifdef CHAPMSV2
104static void	ChapMSv2_NT __P((char *, u_char *, char *, int,
105    MS_Chapv2Response *));
106#endif
107
108#ifdef USE_CRYPT
109static void	Expand __P((u_char *, char *));
110static void	Collapse __P((char *, u_char *));
111#endif
112
113#if defined(MSLANMAN) && defined(CHAPMS)
114bool	ms_lanman = 0;    	/* Use LanMan password instead of NT */
115			  	/* Has meaning only with MS-CHAP challenges */
116#endif
117
118#ifdef CHAPMSV2
119/* Specially-formatted Microsoft CHAP response message. */
120static char status_message[256];
121#endif
122
123static void
124ChallengeResponse(challenge, pwHash, response)
125    u_char *challenge;	/* IN   8 octets */
126    u_char *pwHash;	/* IN  16 octets */
127    u_char *response;	/* OUT 24 octets */
128{
129    u_char    ZPasswordHash[21];
130
131    BZERO(ZPasswordHash, sizeof(ZPasswordHash));
132    BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
133
134#if 0
135    dbglog("ChallengeResponse - ZPasswordHash %.*B",
136	   sizeof(ZPasswordHash), ZPasswordHash);
137#endif
138
139    DesEncrypt(challenge, ZPasswordHash +  0, response + 0);
140    DesEncrypt(challenge, ZPasswordHash +  7, response + 8);
141    DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
142
143#if 0
144    dbglog("ChallengeResponse - response %.24B", response);
145#endif
146}
147
148
149#ifdef USE_CRYPT
150static void
151DesEncrypt(clear, key, cipher)
152    u_char *clear;	/* IN  8 octets */
153    u_char *key;	/* IN  7 octets */
154    u_char *cipher;	/* OUT 8 octets */
155{
156    u_char des_key[8];
157    char crypt_key[66];
158    char des_input[66];
159
160    MakeKey(key, des_key);
161
162    Expand(des_key, crypt_key);
163    setkey(crypt_key);
164
165#if 0
166    CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
167#endif
168
169    Expand(clear, des_input);
170    encrypt(des_input, 0);
171    Collapse(des_input, cipher);
172
173#if 0
174    CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
175#endif
176}
177
178#else /* USE_CRYPT */
179
180static void
181DesEncrypt(clear, key, cipher)
182    u_char *clear;	/* IN  8 octets */
183    u_char *key;	/* IN  7 octets */
184    u_char *cipher;	/* OUT 8 octets */
185{
186    des_cblock		des_key;
187    des_key_schedule	key_schedule;
188
189    MakeKey(key, des_key);
190
191    des_set_key(&des_key, key_schedule);
192
193#if 0
194    CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
195#endif
196
197    des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
198
199#if 0
200    CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
201#endif
202}
203
204#endif /* USE_CRYPT */
205
206
207static u_char Get7Bits(input, startBit)
208    u_char *input;
209    int startBit;
210{
211    register unsigned int	word;
212
213    word  = (unsigned)input[startBit / 8] << 8;
214    word |= (unsigned)input[startBit / 8 + 1];
215
216    word >>= 15 - (startBit % 8 + 7);
217
218    return word & 0xFE;
219}
220
221#ifdef USE_CRYPT
222
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()
226 */
227static void Expand(in, out)
228    u_char *in;
229    char *out;
230{
231        int j, c;
232        int i;
233
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;
239        }
240}
241
242/* The inverse of Expand
243 */
244static void Collapse(in, out)
245    char *in;
246    u_char *out;
247{
248        int j;
249        int i;
250	unsigned int c;
251
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;
257	}
258}
259#endif
260
261static 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 */
264{
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);
273
274#ifndef USE_CRYPT
275    des_set_odd_parity((des_cblock *)des_key);
276#endif
277
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
282}
283
284#ifdef CHAPMS
285static void
286ChapMS_NT(rchallenge, secret, secret_len, response)
287    u_char *rchallenge;
288    char *secret;
289    int secret_len;
290    MS_ChapResponse    *response;
291{
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];
302
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];
308
309    MD4Init(&md4Context);
310    MD4Update(&md4Context, unicodePassword, mdlen);
311
312    MD4Final(hash, &md4Context); 	/* Tell MD4 we're done */
313
314    ChallengeResponse(rchallenge, hash, response->NTResp);
315}
316
317#ifdef MSLANMAN
318static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
319
320static void
321ChapMS_LANMan(rchallenge, secret, secret_len, response)
322    u_char *rchallenge;
323    char *secret;
324    int secret_len;
325    MS_ChapResponse	*response;
326{
327    int			i;
328    u_char		UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
329    u_char		PasswordHash[MD4_SIGNATURE_SIZE];
330
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);
339}
340#endif
341
342void
343ChapMS(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;
349{
350    MS_ChapResponse	response;
351
352    if (rchallenge_len < 8) {
353	    cstate->resp_length = 0;
354	    return;
355    }
356
357#if 0
358    CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret));
359#endif
360    BZERO(&response, sizeof(response));
361
362    /* Calculate both always */
363    ChapMS_NT(rchallenge, secret, secret_len, &response);
364
365#ifdef MSLANMAN
366    ChapMS_LANMan(rchallenge, secret, secret_len, &response);
367
368    /* prefered method is set by option  */
369    response.UseNT = !ms_lanman;
370#else
371    response.UseNT = 1;
372#endif
373
374    BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN);
375    cstate->resp_length = MS_CHAP_RESPONSE_LEN;
376}
377
378static int
379ChapMSStatus(cstate, flag)
380    chap_state *cstate;
381    int flag;
382{
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);
389    }
390    return (flag);
391}
392
393int
394ChapMSValidate(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;
400{
401    MS_ChapResponse ckresp;
402
403    if (response_len < MS_CHAP_RESPONSE_LEN || cstate->chal_len < 8)
404	return (0);
405
406    BZERO(&ckresp, sizeof(ckresp));
407
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));
412    }
413
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
421}
422#endif /* CHAPMS */
423
424#ifdef CHAPMSV2
425static void
426ChallengeHash(peerchallenge, authenticatorchallenge, username, challenge)
427u_char *peerchallenge, *authenticatorchallenge, *challenge;
428char *username;
429{
430    uint8_t digest[20];
431    SHA1_CTX sha1Context;
432    char *cp;
433
434    SHA1Init(&sha1Context);
435    SHA1Update(&sha1Context, peerchallenge, 16);
436    SHA1Update(&sha1Context, authenticatorchallenge, 16);
437
438    /*
439     * Only the user name (as presented by the peer and
440     * excluding any prepended domain name)
441     * is used as input to SHAUpdate().
442     */
443    if ((cp = strchr(username,'\\')) != NULL)
444	username = cp;
445
446    SHA1Update(&sha1Context, (uint8_t *)username, strlen(username));
447    SHA1Final(digest, &sha1Context);
448    BCOPY(digest, challenge, 8);
449}
450
451static void
452ChapMSv2_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;
458{
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];
470
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;
477
478    ChallengeHash(response->PeerChallenge, rchallenge, username, challenge);
479
480    MD4Init(&md4Context);
481    MD4Update(&md4Context, unicodePassword, mdlen);
482
483    MD4Final(hash, &md4Context); 	/* Tell MD4 we're done */
484
485    ChallengeResponse(challenge, hash, response->NTResp);
486}
487
488void
489ChapMSv2(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;
495{
496    MS_Chapv2Response	response;
497    u_char *ptr;
498    int i;
499
500    if (rchallenge_len < 8) {
501	cstate->resp_length = 0;
502	return;
503    }
504
505    BZERO(&response, sizeof(response));
506
507    ptr = response.PeerChallenge;
508    for (i = 0; i < 16; i++)
509	*ptr++ = (u_char) (drand48() * 0xff);
510
511    ChapMSv2_NT(cstate->resp_name, rchallenge, secret, secret_len, &response);
512
513    BCOPY(&response, cstate->response, MS_CHAPV2_RESPONSE_LEN);
514    cstate->resp_length = MS_CHAPV2_RESPONSE_LEN;
515}
516
517static void
518ChapMSv2Success(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;
524{
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;
544
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;
551
552    /* Hash the password with MD4 */
553    MD4Init(&md4Context);
554    MD4Update(&md4Context, unicodePassword, secret_len * 2 * mdlen);
555    MD4Final(hash, &md4Context);
556
557    /* Now hash the hash */
558    MD4Init(&md4Context);
559    MD4Update(&md4Context, hash, MD4_SIGNATURE_SIZE * mdlen);
560    MD4Final(hashhash, &md4Context);
561
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);
567
568    ChallengeHash(msresp->PeerChallenge, authchall, rhostname, challenge);
569
570    SHA1Init(&sha1Context);
571    SHA1Update(&sha1Context, digest, 20);
572    SHA1Update(&sha1Context, challenge, 8);
573    SHA1Update(&sha1Context, Magic2, 41);
574    SHA1Final(digest, &sha1Context);
575
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];
582    }
583    /*
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."
592     */
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);
600}
601
602int
603ChapMSv2Validate(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;
610{
611    MS_Chapv2Response ckresp;
612
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;
618    }
619
620    BZERO(&ckresp, sizeof(ckresp));
621
622    BCOPY(response, ckresp.PeerChallenge, 16);
623
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);
629    }
630    ChapMSv2Success(cstate, (MS_Chapv2Response *)response, cstate->challenge,
631	rhostname, secret, secret_len);
632    return (1);
633}
634#endif /* CHAPMSV2 */
635
636#endif /* CHAPMS or CHAPMSV2 */
637